authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-05-25 20:33:43+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-05-25 20:33:43+02:00
log39ebfedd2bea7ebb8630f8593ae59538969bfea7
treeeb11df8c0125d4afd99fd4ecf43ac3f1ce13e9d6
parente0be22b6c06530d56308853723f982c492e768fd
parent7000395a7d002a6d242d717de60614516f4e29af
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #11715 from ziglang/stage2-x64-cond-codes


4 files changed, 552 insertions(+), 369 deletions(-)

src/arch/x86_64/CodeGen.zig+181-266
......@@ -37,6 +37,7 @@ const caller_preserved_regs = abi.caller_preserved_regs;
3737const c_abi_int_param_regs = abi.c_abi_int_param_regs;
3838const c_abi_int_return_regs = abi.c_abi_int_return_regs;
3939
40const Condition = bits.Condition;
4041const RegisterManager = abi.RegisterManager;
4142const RegisterLock = RegisterManager.RegisterLock;
4243const Register = bits.Register;
......@@ -65,7 +66,7 @@ arg_index: u32,
6566src_loc: Module.SrcLoc,
6667stack_align: u32,
6768
68compare_flags_inst: ?Air.Inst.Index = null,
69eflags_inst: ?Air.Inst.Index = null,
6970
7071/// MIR Instructions
7172mir_instructions: std.MultiArrayList(Mir.Inst) = .{},
......@@ -127,12 +128,8 @@ pub const MCValue = union(enum) {
127128 immediate: u64,
128129 /// The value is in a GP register.
129130 register: Register,
130 /// The value is a tuple { wrapped, overflow } where wrapped value is stored in the GP register,
131 /// and the operation is an unsigned operation.
132 register_overflow_unsigned: Register,
133 /// The value is a tuple { wrapped, overflow } where wrapped value is stored in the GP register,
134 /// and the operation is a signed operation.
135 register_overflow_signed: Register,
131 /// The value is a tuple { wrapped, overflow } where wrapped value is stored in the GP register.
132 register_overflow: struct { reg: Register, eflags: Condition },
136133 /// The value is in memory at a hard-coded address.
137134 /// If the type is a pointer, it means the pointer address is at this memory location.
138135 memory: u64,
......@@ -149,12 +146,8 @@ pub const MCValue = union(enum) {
149146 stack_offset: i32,
150147 /// The value is a pointer to one of the stack variables (payload is stack offset).
151148 ptr_stack_offset: i32,
152 /// The value is in the compare flags assuming an unsigned operation,
153 /// with this operator applied on top of it.
154 compare_flags_unsigned: math.CompareOperator,
155 /// The value is in the compare flags assuming a signed operation,
156 /// with this operator applied on top of it.
157 compare_flags_signed: math.CompareOperator,
149 /// The value resides in the EFLAGS register.
150 eflags: Condition,
158151
159152 fn isMemory(mcv: MCValue) bool {
160153 return switch (mcv) {
......@@ -183,12 +176,10 @@ pub const MCValue = union(enum) {
183176
184177 .immediate,
185178 .memory,
186 .compare_flags_unsigned,
187 .compare_flags_signed,
179 .eflags,
188180 .ptr_stack_offset,
189181 .undef,
190 .register_overflow_unsigned,
191 .register_overflow_signed,
182 .register_overflow,
192183 => false,
193184
194185 .register,
......@@ -778,12 +769,12 @@ fn processDeath(self: *Self, inst: Air.Inst.Index) void {
778769 .register => |reg| {
779770 self.register_manager.freeReg(reg.to64());
780771 },
781 .register_overflow_signed, .register_overflow_unsigned => |reg| {
782 self.register_manager.freeReg(reg.to64());
783 self.compare_flags_inst = null;
772 .register_overflow => |ro| {
773 self.register_manager.freeReg(ro.reg.to64());
774 self.eflags_inst = null;
784775 },
785 .compare_flags_signed, .compare_flags_unsigned => {
786 self.compare_flags_inst = null;
776 .eflags => {
777 self.eflags_inst = null;
787778 },
788779 else => {}, // TODO process stack allocation death
789780 }
......@@ -813,20 +804,22 @@ fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Live
813804 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
814805 branch.inst_table.putAssumeCapacityNoClobber(inst, result);
815806
807 // In some cases (such as bitcast), an operand
808 // may be the same MCValue as the result. If
809 // that operand died and was a register, it
810 // was freed by processDeath. We have to
811 // "re-allocate" the register.
816812 switch (result) {
817 .register,
818 .register_overflow_signed,
819 .register_overflow_unsigned,
820 => |reg| {
821 // In some cases (such as bitcast), an operand
822 // may be the same MCValue as the result. If
823 // that operand died and was a register, it
824 // was freed by processDeath. We have to
825 // "re-allocate" the register.
813 .register => |reg| {
826814 if (self.register_manager.isRegFree(reg)) {
827815 self.register_manager.getRegAssumeFree(reg, inst);
828816 }
829817 },
818 .register_overflow => |ro| {
819 if (self.register_manager.isRegFree(ro.reg)) {
820 self.register_manager.getRegAssumeFree(ro.reg, inst);
821 }
822 },
830823 else => {},
831824 }
832825 }
......@@ -916,12 +909,12 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void
916909 log.debug("spilling {d} to stack mcv {any}", .{ inst, stack_mcv });
917910 const reg_mcv = self.getResolvedInstValue(inst);
918911 switch (reg_mcv) {
919 .register,
920 .register_overflow_unsigned,
921 .register_overflow_signed,
922 => |other| {
912 .register => |other| {
923913 assert(reg.to64() == other.to64());
924914 },
915 .register_overflow => |ro| {
916 assert(reg.to64() == ro.reg.to64());
917 },
925918 else => {},
926919 }
927920 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
......@@ -929,16 +922,12 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void
929922 try self.genSetStack(self.air.typeOfIndex(inst), stack_mcv.stack_offset, reg_mcv, .{});
930923}
931924
932pub fn spillCompareFlagsIfOccupied(self: *Self) !void {
933 if (self.compare_flags_inst) |inst_to_save| {
925pub fn spillEflagsIfOccupied(self: *Self) !void {
926 if (self.eflags_inst) |inst_to_save| {
934927 const mcv = self.getResolvedInstValue(inst_to_save);
935928 const new_mcv = switch (mcv) {
936 .register_overflow_signed,
937 .register_overflow_unsigned,
938 => try self.allocRegOrMem(inst_to_save, false),
939 .compare_flags_signed,
940 .compare_flags_unsigned,
941 => try self.allocRegOrMem(inst_to_save, true),
929 .register_overflow => try self.allocRegOrMem(inst_to_save, false),
930 .eflags => try self.allocRegOrMem(inst_to_save, true),
942931 else => unreachable,
943932 };
944933
......@@ -948,14 +937,12 @@ pub fn spillCompareFlagsIfOccupied(self: *Self) !void {
948937 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
949938 try branch.inst_table.put(self.gpa, inst_to_save, new_mcv);
950939
951 self.compare_flags_inst = null;
940 self.eflags_inst = null;
952941
953942 // TODO consolidate with register manager and spillInstruction
954943 // this call should really belong in the register manager!
955944 switch (mcv) {
956 .register_overflow_signed,
957 .register_overflow_unsigned,
958 => |reg| self.register_manager.freeReg(reg),
945 .register_overflow => |ro| self.register_manager.freeReg(ro.reg),
959946 else => {},
960947 }
961948 }
......@@ -1123,31 +1110,8 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void {
11231110 switch (operand) {
11241111 .dead => unreachable,
11251112 .unreach => unreachable,
1126 .compare_flags_unsigned => |op| {
1127 const r = MCValue{
1128 .compare_flags_unsigned = switch (op) {
1129 .gte => .lt,
1130 .gt => .lte,
1131 .neq => .eq,
1132 .lt => .gte,
1133 .lte => .gt,
1134 .eq => .neq,
1135 },
1136 };
1137 break :result r;
1138 },
1139 .compare_flags_signed => |op| {
1140 const r = MCValue{
1141 .compare_flags_signed = switch (op) {
1142 .gte => .lt,
1143 .gt => .lte,
1144 .neq => .eq,
1145 .lt => .gte,
1146 .lte => .gt,
1147 .eq => .neq,
1148 },
1149 };
1150 break :result r;
1113 .eflags => |cc| {
1114 break :result MCValue{ .eflags = cc.negate() };
11511115 },
11521116 else => {},
11531117 }
......@@ -1213,13 +1177,17 @@ fn airMin(self: *Self, inst: Air.Inst.Index) !void {
12131177 try self.genBinOpMir(.cmp, ty, .{ .register = lhs_reg }, rhs_mcv);
12141178
12151179 const dst_mcv = try self.copyToRegisterWithInstTracking(inst, ty, rhs_mcv);
1180 const cc: Condition = switch (signedness) {
1181 .unsigned => .b,
1182 .signed => .l,
1183 };
12161184 _ = try self.addInst(.{
1217 .tag = if (signedness == .signed) .cond_mov_lt else .cond_mov_below,
1185 .tag = .cond_mov,
12181186 .ops = Mir.Inst.Ops.encode(.{
12191187 .reg1 = dst_mcv.register,
12201188 .reg2 = lhs_reg,
12211189 }),
1222 .data = undefined,
1190 .data = .{ .cc = cc },
12231191 });
12241192
12251193 break :result dst_mcv;
......@@ -1341,7 +1309,7 @@ fn airAddSubShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
13411309 return self.fail("TODO implement add/sub/shl with overflow for Ints larger than 64bits", .{});
13421310 }
13431311
1344 try self.spillCompareFlagsIfOccupied();
1312 try self.spillEflagsIfOccupied();
13451313
13461314 if (tag == .shl_with_overflow) {
13471315 try self.spillRegisters(1, .{.rcx});
......@@ -1362,16 +1330,19 @@ fn airAddSubShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
13621330 const int_info = ty.intInfo(self.target.*);
13631331
13641332 if (math.isPowerOfTwo(int_info.bits) and int_info.bits >= 8) {
1365 self.compare_flags_inst = inst;
1333 self.eflags_inst = inst;
13661334
1367 const result: MCValue = switch (int_info.signedness) {
1368 .signed => .{ .register_overflow_signed = partial.register },
1369 .unsigned => .{ .register_overflow_unsigned = partial.register },
1335 const cc: Condition = switch (int_info.signedness) {
1336 .unsigned => .c,
1337 .signed => .o,
13701338 };
1371 break :result result;
1339 break :result MCValue{ .register_overflow = .{
1340 .reg = partial.register,
1341 .eflags = cc,
1342 } };
13721343 }
13731344
1374 self.compare_flags_inst = null;
1345 self.eflags_inst = null;
13751346
13761347 const tuple_ty = self.air.typeOfIndex(inst);
13771348 const tuple_size = @intCast(u32, tuple_ty.abiSize(self.target.*));
......@@ -1413,17 +1384,16 @@ fn genSetStackTruncatedOverflowCompare(
14131384 };
14141385
14151386 const overflow_reg = temp_regs[0];
1416 const flags: u2 = switch (int_info.signedness) {
1417 .signed => 0b00,
1418 .unsigned => 0b10,
1387 const cc: Condition = switch (int_info.signedness) {
1388 .signed => .o,
1389 .unsigned => .c,
14191390 };
14201391 _ = try self.addInst(.{
1421 .tag = .cond_set_byte_overflow,
1392 .tag = .cond_set_byte,
14221393 .ops = Mir.Inst.Ops.encode(.{
14231394 .reg1 = overflow_reg.to8(),
1424 .flags = flags,
14251395 }),
1426 .data = undefined,
1396 .data = .{ .cc = cc },
14271397 });
14281398
14291399 const scratch_reg = temp_regs[1];
......@@ -1438,9 +1408,9 @@ fn genSetStackTruncatedOverflowCompare(
14381408
14391409 const eq_reg = temp_regs[2];
14401410 _ = try self.addInst(.{
1441 .tag = .cond_set_byte_eq_ne,
1411 .tag = .cond_set_byte,
14421412 .ops = Mir.Inst.Ops.encode(.{ .reg1 = eq_reg.to8() }),
1443 .data = undefined,
1413 .data = .{ .cc = .ne },
14441414 });
14451415
14461416 try self.genBinOpMir(
......@@ -1477,8 +1447,8 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
14771447 const int_info = ty.intInfo(self.target.*);
14781448
14791449 if (math.isPowerOfTwo(int_info.bits) and int_info.bits >= 8) {
1480 try self.spillCompareFlagsIfOccupied();
1481 self.compare_flags_inst = inst;
1450 try self.spillEflagsIfOccupied();
1451 self.eflags_inst = inst;
14821452
14831453 try self.spillRegisters(2, .{ .rax, .rdx });
14841454
......@@ -1486,14 +1456,18 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
14861456 const rhs = try self.resolveInst(bin_op.rhs);
14871457
14881458 const partial = try self.genMulDivBinOp(.mul, null, ty, lhs, rhs);
1489 break :result switch (int_info.signedness) {
1490 .signed => MCValue{ .register_overflow_signed = partial.register },
1491 .unsigned => MCValue{ .register_overflow_unsigned = partial.register },
1459 const cc: Condition = switch (int_info.signedness) {
1460 .unsigned => .c,
1461 .signed => .o,
14921462 };
1463 break :result MCValue{ .register_overflow = .{
1464 .reg = partial.register,
1465 .eflags = cc,
1466 } };
14931467 }
14941468
1495 try self.spillCompareFlagsIfOccupied();
1496 self.compare_flags_inst = null;
1469 try self.spillEflagsIfOccupied();
1470 self.eflags_inst = null;
14971471
14981472 const dst_reg: Register = dst_reg: {
14991473 switch (int_info.signedness) {
......@@ -1686,12 +1660,12 @@ fn genInlineIntDivFloor(self: *Self, ty: Type, lhs: MCValue, rhs: MCValue) !MCVa
16861660 .data = undefined,
16871661 });
16881662 _ = try self.addInst(.{
1689 .tag = .cond_mov_eq,
1663 .tag = .cond_mov,
16901664 .ops = Mir.Inst.Ops.encode(.{
16911665 .reg1 = divisor.to64(),
16921666 .reg2 = .rdx,
16931667 }),
1694 .data = undefined,
1668 .data = .{ .cc = .e },
16951669 });
16961670 try self.genBinOpMir(.add, Type.isize, .{ .register = divisor }, .{ .register = .rax });
16971671 return MCValue{ .register = divisor };
......@@ -2511,10 +2485,8 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo
25112485 .undef => unreachable,
25122486 .unreach => unreachable,
25132487 .dead => unreachable,
2514 .compare_flags_unsigned => unreachable,
2515 .compare_flags_signed => unreachable,
2516 .register_overflow_unsigned => unreachable,
2517 .register_overflow_signed => unreachable,
2488 .eflags => unreachable,
2489 .register_overflow => unreachable,
25182490 .immediate => |imm| {
25192491 try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm });
25202492 },
......@@ -2532,8 +2504,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo
25322504 switch (dst_mcv) {
25332505 .dead => unreachable,
25342506 .undef => unreachable,
2535 .compare_flags_unsigned => unreachable,
2536 .compare_flags_signed => unreachable,
2507 .eflags => unreachable,
25372508 .register => |dst_reg| {
25382509 // mov dst_reg, [reg]
25392510 _ = try self.addInst(.{
......@@ -2637,10 +2608,8 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
26372608 .undef => unreachable,
26382609 .unreach => unreachable,
26392610 .dead => unreachable,
2640 .compare_flags_unsigned => unreachable,
2641 .compare_flags_signed => unreachable,
2642 .register_overflow_unsigned => unreachable,
2643 .register_overflow_signed => unreachable,
2611 .eflags => unreachable,
2612 .register_overflow => unreachable,
26442613 .immediate => |imm| {
26452614 try self.setRegOrMem(value_ty, .{ .memory = imm }, value);
26462615 },
......@@ -2660,8 +2629,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
26602629 .undef => unreachable,
26612630 .dead => unreachable,
26622631 .unreach => unreachable,
2663 .compare_flags_unsigned => unreachable,
2664 .compare_flags_signed => unreachable,
2632 .eflags => unreachable,
26652633 .immediate => |imm| {
26662634 switch (abi_size) {
26672635 1, 2, 4 => {
......@@ -3027,32 +2995,24 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
30272995
30282996 break :result dst_mcv;
30292997 },
3030 .register_overflow_unsigned,
3031 .register_overflow_signed,
3032 => |reg| {
2998 .register_overflow => |ro| {
30332999 switch (index) {
30343000 0 => {
30353001 // Get wrapped value for overflow operation.
3036 break :result MCValue{ .register = reg };
3002 break :result MCValue{ .register = ro.reg };
30373003 },
30383004 1 => {
30393005 // Get overflow bit.
3040 const reg_lock = self.register_manager.lockRegAssumeUnused(reg);
3006 const reg_lock = self.register_manager.lockRegAssumeUnused(ro.reg);
30413007 defer self.register_manager.unlockReg(reg_lock);
30423008
30433009 const dst_reg = try self.register_manager.allocReg(inst, gp);
3044 const flags: u2 = switch (mcv) {
3045 .register_overflow_unsigned => 0b10,
3046 .register_overflow_signed => 0b00,
3047 else => unreachable,
3048 };
30493010 _ = try self.addInst(.{
3050 .tag = .cond_set_byte_overflow,
3011 .tag = .cond_set_byte,
30513012 .ops = Mir.Inst.Ops.encode(.{
30523013 .reg1 = dst_reg.to8(),
3053 .flags = flags,
30543014 }),
3055 .data = undefined,
3015 .data = .{ .cc = ro.eflags },
30563016 });
30573017 break :result MCValue{ .register = dst_reg.to8() };
30583018 },
......@@ -3479,17 +3439,14 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu
34793439 .none => unreachable,
34803440 .undef => unreachable,
34813441 .dead, .unreach, .immediate => unreachable,
3482 .compare_flags_unsigned => unreachable,
3483 .compare_flags_signed => unreachable,
3484 .register_overflow_unsigned => unreachable,
3485 .register_overflow_signed => unreachable,
3442 .eflags => unreachable,
3443 .register_overflow => unreachable,
34863444 .register => |dst_reg| {
34873445 switch (src_mcv) {
34883446 .none => unreachable,
34893447 .undef => unreachable,
34903448 .dead, .unreach => unreachable,
3491 .register_overflow_unsigned => unreachable,
3492 .register_overflow_signed => unreachable,
3449 .register_overflow => unreachable,
34933450 .ptr_stack_offset => {
34943451 const dst_reg_lock = self.register_manager.lockReg(dst_reg);
34953452 defer if (dst_reg_lock) |lock| self.register_manager.unlockReg(lock);
......@@ -3559,8 +3516,7 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu
35593516 .memory,
35603517 .got_load,
35613518 .direct_load,
3562 .compare_flags_signed,
3563 .compare_flags_unsigned,
3519 .eflags,
35643520 => {
35653521 assert(abi_size <= 8);
35663522 const dst_reg_lock = self.register_manager.lockReg(dst_reg);
......@@ -3597,8 +3553,7 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu
35973553 .none => unreachable,
35983554 .undef => unreachable,
35993555 .dead, .unreach => unreachable,
3600 .register_overflow_unsigned => unreachable,
3601 .register_overflow_signed => unreachable,
3556 .register_overflow => unreachable,
36023557 .register => |src_reg| {
36033558 _ = try self.addInst(.{
36043559 .tag = mir_tag,
......@@ -3649,11 +3604,8 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu
36493604 .got_load, .direct_load => {
36503605 return self.fail("TODO implement x86 ADD/SUB/CMP source symbol at index in linker", .{});
36513606 },
3652 .compare_flags_unsigned => {
3653 return self.fail("TODO implement x86 ADD/SUB/CMP source compare flag (unsigned)", .{});
3654 },
3655 .compare_flags_signed => {
3656 return self.fail("TODO implement x86 ADD/SUB/CMP source compare flag (signed)", .{});
3607 .eflags => {
3608 return self.fail("TODO implement x86 ADD/SUB/CMP source eflags", .{});
36573609 },
36583610 }
36593611 },
......@@ -3674,19 +3626,16 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M
36743626 .none => unreachable,
36753627 .undef => unreachable,
36763628 .dead, .unreach, .immediate => unreachable,
3677 .compare_flags_unsigned => unreachable,
3678 .compare_flags_signed => unreachable,
3629 .eflags => unreachable,
36793630 .ptr_stack_offset => unreachable,
3680 .register_overflow_unsigned => unreachable,
3681 .register_overflow_signed => unreachable,
3631 .register_overflow => unreachable,
36823632 .register => |dst_reg| {
36833633 switch (src_mcv) {
36843634 .none => unreachable,
36853635 .undef => try self.genSetReg(dst_ty, dst_reg, .undef),
36863636 .dead, .unreach => unreachable,
36873637 .ptr_stack_offset => unreachable,
3688 .register_overflow_unsigned => unreachable,
3689 .register_overflow_signed => unreachable,
3638 .register_overflow => unreachable,
36903639 .register => |src_reg| {
36913640 // register, register
36923641 _ = try self.addInst(.{
......@@ -3734,11 +3683,8 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M
37343683 .got_load, .direct_load => {
37353684 return self.fail("TODO implement x86 multiply source symbol at index in linker", .{});
37363685 },
3737 .compare_flags_unsigned => {
3738 return self.fail("TODO implement x86 multiply source compare flag (unsigned)", .{});
3739 },
3740 .compare_flags_signed => {
3741 return self.fail("TODO implement x86 multiply source compare flag (signed)", .{});
3686 .eflags => {
3687 return self.fail("TODO implement x86 multiply source eflags", .{});
37423688 },
37433689 }
37443690 },
......@@ -3748,8 +3694,7 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M
37483694 .undef => return self.genSetStack(dst_ty, off, .undef, .{}),
37493695 .dead, .unreach => unreachable,
37503696 .ptr_stack_offset => unreachable,
3751 .register_overflow_unsigned => unreachable,
3752 .register_overflow_signed => unreachable,
3697 .register_overflow => unreachable,
37533698 .register => |src_reg| {
37543699 // copy dst to a register
37553700 const dst_reg = try self.copyToTmpRegister(dst_ty, dst_mcv);
......@@ -3782,11 +3727,8 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M
37823727 .got_load, .direct_load => {
37833728 return self.fail("TODO implement x86 multiply source symbol at index in linker", .{});
37843729 },
3785 .compare_flags_unsigned => {
3786 return self.fail("TODO implement x86 multiply source compare flag (unsigned)", .{});
3787 },
3788 .compare_flags_signed => {
3789 return self.fail("TODO implement x86 multiply source compare flag (signed)", .{});
3730 .eflags => {
3731 return self.fail("TODO implement x86 multiply source eflags", .{});
37903732 },
37913733 }
37923734 },
......@@ -3905,7 +3847,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
39053847 var info = try self.resolveCallingConventionValues(fn_ty);
39063848 defer info.deinit(self);
39073849
3908 try self.spillCompareFlagsIfOccupied();
3850 try self.spillEflagsIfOccupied();
39093851
39103852 for (caller_preserved_regs) |reg| {
39113853 try self.register_manager.getReg(reg, null);
......@@ -3957,10 +3899,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
39573899 .memory => unreachable,
39583900 .got_load => unreachable,
39593901 .direct_load => unreachable,
3960 .compare_flags_signed => unreachable,
3961 .compare_flags_unsigned => unreachable,
3962 .register_overflow_signed => unreachable,
3963 .register_overflow_unsigned => unreachable,
3902 .eflags => unreachable,
3903 .register_overflow => unreachable,
39643904 }
39653905 }
39663906
......@@ -4220,8 +4160,8 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
42204160 break :blk ty.intInfo(self.target.*).signedness;
42214161 };
42224162
4223 try self.spillCompareFlagsIfOccupied();
4224 self.compare_flags_inst = inst;
4163 try self.spillEflagsIfOccupied();
4164 self.eflags_inst = inst;
42254165
42264166 const result: MCValue = result: {
42274167 // There are 2 operands, destination and source.
......@@ -4265,9 +4205,10 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
42654205 defer if (src_lock) |lock| self.register_manager.unlockReg(lock);
42664206
42674207 try self.genBinOpMir(.cmp, ty, dst_mcv, src_mcv);
4208
42684209 break :result switch (signedness) {
4269 .signed => MCValue{ .compare_flags_signed = op },
4270 .unsigned => MCValue{ .compare_flags_unsigned = op },
4210 .signed => MCValue{ .eflags = Condition.fromCompareOperatorSigned(op) },
4211 .unsigned => MCValue{ .eflags = Condition.fromCompareOperatorUnsigned(op) },
42714212 };
42724213 };
42734214
......@@ -4440,47 +4381,39 @@ fn addDbgInfoTypeReloc(self: *Self, ty: Type) !void {
44404381fn genCondBrMir(self: *Self, ty: Type, mcv: MCValue) !u32 {
44414382 const abi_size = ty.abiSize(self.target.*);
44424383 switch (mcv) {
4443 .compare_flags_unsigned,
4444 .compare_flags_signed,
4445 => |cmp_op| {
4446 // Here we map the opposites since the jump is to the false branch.
4447 const flags: u2 = switch (cmp_op) {
4448 .gte => 0b10,
4449 .gt => 0b11,
4450 .neq => 0b01,
4451 .lt => 0b00,
4452 .lte => 0b01,
4453 .eq => 0b00,
4454 };
4455 const tag: Mir.Inst.Tag = if (cmp_op == .neq or cmp_op == .eq)
4456 .cond_jmp_eq_ne
4457 else if (mcv == .compare_flags_unsigned)
4458 Mir.Inst.Tag.cond_jmp_above_below
4459 else
4460 Mir.Inst.Tag.cond_jmp_greater_less;
4384 .eflags => |cc| {
44614385 return self.addInst(.{
4462 .tag = tag,
4463 .ops = Mir.Inst.Ops.encode(.{ .flags = flags }),
4464 .data = .{ .inst = undefined },
4386 .tag = .cond_jmp,
4387 .ops = Mir.Inst.Ops.encode(.{}),
4388 .data = .{
4389 .inst_cc = .{
4390 .inst = undefined,
4391 // Here we map the opposites since the jump is to the false branch.
4392 .cc = cc.negate(),
4393 },
4394 },
44654395 });
44664396 },
44674397 .register => |reg| {
4468 try self.spillCompareFlagsIfOccupied();
4398 try self.spillEflagsIfOccupied();
44694399 _ = try self.addInst(.{
44704400 .tag = .@"test",
44714401 .ops = Mir.Inst.Ops.encode(.{ .reg1 = reg }),
44724402 .data = .{ .imm = 1 },
44734403 });
44744404 return self.addInst(.{
4475 .tag = .cond_jmp_eq_ne,
4476 .ops = Mir.Inst.Ops.encode(.{ .flags = 0b01 }),
4477 .data = .{ .inst = undefined },
4405 .tag = .cond_jmp,
4406 .ops = Mir.Inst.Ops.encode(.{}),
4407 .data = .{ .inst_cc = .{
4408 .inst = undefined,
4409 .cc = .e,
4410 } },
44784411 });
44794412 },
44804413 .immediate,
44814414 .stack_offset,
44824415 => {
4483 try self.spillCompareFlagsIfOccupied();
4416 try self.spillEflagsIfOccupied();
44844417 if (abi_size <= 8) {
44854418 const reg = try self.copyToTmpRegister(ty, mcv);
44864419 return self.genCondBrMir(ty, .{ .register = reg });
......@@ -4516,7 +4449,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
45164449 // Capture the state of register and stack allocation state so that we can revert to it.
45174450 const parent_next_stack_offset = self.next_stack_offset;
45184451 const parent_free_registers = self.register_manager.free_registers;
4519 const parent_compare_flags_inst = self.compare_flags_inst;
4452 const parent_eflags_inst = self.eflags_inst;
45204453 var parent_stack = try self.stack.clone(self.gpa);
45214454 defer parent_stack.deinit(self.gpa);
45224455 const parent_registers = self.register_manager.registers;
......@@ -4538,7 +4471,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
45384471 defer saved_then_branch.deinit(self.gpa);
45394472
45404473 self.register_manager.registers = parent_registers;
4541 self.compare_flags_inst = parent_compare_flags_inst;
4474 self.eflags_inst = parent_eflags_inst;
45424475
45434476 self.stack.deinit(self.gpa);
45444477 self.stack = parent_stack;
......@@ -4640,8 +4573,8 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
46404573}
46414574
46424575fn isNull(self: *Self, inst: Air.Inst.Index, ty: Type, operand: MCValue) !MCValue {
4643 try self.spillCompareFlagsIfOccupied();
4644 self.compare_flags_inst = inst;
4576 try self.spillEflagsIfOccupied();
4577 self.eflags_inst = inst;
46454578
46464579 const cmp_ty: Type = if (!ty.isPtrLikeOptional()) blk: {
46474580 var buf: Type.Payload.ElemType = undefined;
......@@ -4651,13 +4584,13 @@ fn isNull(self: *Self, inst: Air.Inst.Index, ty: Type, operand: MCValue) !MCValu
46514584
46524585 try self.genBinOpMir(.cmp, cmp_ty, operand, MCValue{ .immediate = 0 });
46534586
4654 return MCValue{ .compare_flags_unsigned = .eq };
4587 return MCValue{ .eflags = .e };
46554588}
46564589
46574590fn isNonNull(self: *Self, inst: Air.Inst.Index, ty: Type, operand: MCValue) !MCValue {
46584591 const is_null_res = try self.isNull(inst, ty, operand);
4659 assert(is_null_res.compare_flags_unsigned == .eq);
4660 return MCValue{ .compare_flags_unsigned = .neq };
4592 assert(is_null_res.eflags == .e);
4593 return MCValue{ .eflags = is_null_res.eflags.negate() };
46614594}
46624595
46634596fn isErr(self: *Self, inst: Air.Inst.Index, ty: Type, operand: MCValue) !MCValue {
......@@ -4667,8 +4600,8 @@ fn isErr(self: *Self, inst: Air.Inst.Index, ty: Type, operand: MCValue) !MCValue
46674600 return MCValue{ .immediate = 0 }; // always false
46684601 }
46694602
4670 try self.spillCompareFlagsIfOccupied();
4671 self.compare_flags_inst = inst;
4603 try self.spillEflagsIfOccupied();
4604 self.eflags_inst = inst;
46724605
46734606 const err_off = errUnionErrorOffset(ty.errorUnionPayload(), self.target.*);
46744607 switch (operand) {
......@@ -4691,15 +4624,15 @@ fn isErr(self: *Self, inst: Air.Inst.Index, ty: Type, operand: MCValue) !MCValue
46914624 else => return self.fail("TODO implement isErr for {}", .{operand}),
46924625 }
46934626
4694 return MCValue{ .compare_flags_unsigned = .gt };
4627 return MCValue{ .eflags = .a };
46954628}
46964629
46974630fn isNonErr(self: *Self, inst: Air.Inst.Index, ty: Type, operand: MCValue) !MCValue {
46984631 const is_err_res = try self.isErr(inst, ty, operand);
46994632 switch (is_err_res) {
4700 .compare_flags_unsigned => |op| {
4701 assert(op == .gt);
4702 return MCValue{ .compare_flags_unsigned = .lte };
4633 .eflags => |cc| {
4634 assert(cc == .a);
4635 return MCValue{ .eflags = cc.negate() };
47034636 },
47044637 .immediate => |imm| {
47054638 assert(imm == 0);
......@@ -4914,10 +4847,9 @@ fn genCondSwitchMir(self: *Self, ty: Type, condition: MCValue, case: MCValue) !u
49144847 .none => unreachable,
49154848 .undef => unreachable,
49164849 .dead, .unreach => unreachable,
4917 .compare_flags_signed => unreachable,
4918 .compare_flags_unsigned => unreachable,
4850 .eflags => unreachable,
49194851 .register => |cond_reg| {
4920 try self.spillCompareFlagsIfOccupied();
4852 try self.spillEflagsIfOccupied();
49214853
49224854 const cond_reg_lock = self.register_manager.lockReg(cond_reg);
49234855 defer if (cond_reg_lock) |lock| self.register_manager.unlockReg(lock);
......@@ -4965,13 +4897,16 @@ fn genCondSwitchMir(self: *Self, ty: Type, condition: MCValue, case: MCValue) !u
49654897 .data = undefined,
49664898 });
49674899 return self.addInst(.{
4968 .tag = .cond_jmp_eq_ne,
4900 .tag = .cond_jmp,
49694901 .ops = Mir.Inst.Ops.encode(.{}),
4970 .data = .{ .inst = undefined },
4902 .data = .{ .inst_cc = .{
4903 .inst = undefined,
4904 .cc = .ne,
4905 } },
49714906 });
49724907 },
49734908 .stack_offset => {
4974 try self.spillCompareFlagsIfOccupied();
4909 try self.spillEflagsIfOccupied();
49754910
49764911 if (abi_size <= 8) {
49774912 const reg = try self.copyToTmpRegister(ty, condition);
......@@ -5030,7 +4965,7 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
50304965 // Capture the state of register and stack allocation state so that we can revert to it.
50314966 const parent_next_stack_offset = self.next_stack_offset;
50324967 const parent_free_registers = self.register_manager.free_registers;
5033 const parent_compare_flags_inst = self.compare_flags_inst;
4968 const parent_eflags_inst = self.eflags_inst;
50344969 var parent_stack = try self.stack.clone(self.gpa);
50354970 defer parent_stack.deinit(self.gpa);
50364971 const parent_registers = self.register_manager.registers;
......@@ -5052,7 +4987,7 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
50524987 defer saved_case_branch.deinit(self.gpa);
50534988
50544989 self.register_manager.registers = parent_registers;
5055 self.compare_flags_inst = parent_compare_flags_inst;
4990 self.eflags_inst = parent_eflags_inst;
50564991 self.stack.deinit(self.gpa);
50574992 self.stack = parent_stack;
50584993 parent_stack = .{};
......@@ -5092,7 +5027,15 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
50925027
50935028fn performReloc(self: *Self, reloc: Mir.Inst.Index) !void {
50945029 const next_inst = @intCast(u32, self.mir_instructions.len);
5095 self.mir_instructions.items(.data)[reloc].inst = next_inst;
5030 switch (self.mir_instructions.items(.tag)[reloc]) {
5031 .cond_jmp => {
5032 self.mir_instructions.items(.data)[reloc].inst_cc.inst = next_inst;
5033 },
5034 .jmp => {
5035 self.mir_instructions.items(.data)[reloc].inst = next_inst;
5036 },
5037 else => unreachable,
5038 }
50965039}
50975040
50985041fn airBr(self: *Self, inst: Air.Inst.Index) !void {
......@@ -5111,7 +5054,7 @@ fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Ref) !void {
51115054 block_data.mcv = switch (operand_mcv) {
51125055 .none, .dead, .unreach => unreachable,
51135056 .register, .stack_offset, .memory => operand_mcv,
5114 .compare_flags_signed, .compare_flags_unsigned, .immediate => blk: {
5057 .eflags, .immediate => blk: {
51155058 const new_mcv = try self.allocRegOrMem(block, true);
51165059 try self.setRegOrMem(self.air.typeOfIndex(block), new_mcv, operand_mcv);
51175060 break :blk new_mcv;
......@@ -5332,12 +5275,8 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE
53325275 .{ .dest_stack_base = .rsp },
53335276 );
53345277 },
5335 .register_overflow_unsigned,
5336 .register_overflow_signed,
5337 => return self.fail("TODO genSetStackArg for register with overflow bit", .{}),
5338 .compare_flags_unsigned,
5339 .compare_flags_signed,
5340 => {
5278 .register_overflow => return self.fail("TODO genSetStackArg for register with overflow bit", .{}),
5279 .eflags => {
53415280 const reg = try self.copyToTmpRegister(ty, mcv);
53425281 return self.genSetStackArg(ty, stack_offset, .{ .register = reg });
53435282 },
......@@ -5472,30 +5411,22 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl
54725411 ),
54735412 }
54745413 },
5475 .register_overflow_unsigned,
5476 .register_overflow_signed,
5477 => |reg| {
5478 const reg_lock = self.register_manager.lockReg(reg);
5414 .register_overflow => |ro| {
5415 const reg_lock = self.register_manager.lockReg(ro.reg);
54795416 defer if (reg_lock) |lock| self.register_manager.unlockReg(lock);
54805417
54815418 const wrapped_ty = ty.structFieldType(0);
5482 try self.genSetStack(wrapped_ty, stack_offset, .{ .register = reg }, .{});
5419 try self.genSetStack(wrapped_ty, stack_offset, .{ .register = ro.reg }, .{});
54835420
54845421 const overflow_bit_ty = ty.structFieldType(1);
54855422 const overflow_bit_offset = ty.structFieldOffset(1, self.target.*);
54865423 const tmp_reg = try self.register_manager.allocReg(null, gp);
5487 const flags: u2 = switch (mcv) {
5488 .register_overflow_unsigned => 0b10,
5489 .register_overflow_signed => 0b00,
5490 else => unreachable,
5491 };
54925424 _ = try self.addInst(.{
5493 .tag = .cond_set_byte_overflow,
5425 .tag = .cond_set_byte,
54945426 .ops = Mir.Inst.Ops.encode(.{
54955427 .reg1 = tmp_reg.to8(),
5496 .flags = flags,
54975428 }),
5498 .data = undefined,
5429 .data = .{ .cc = ro.eflags },
54995430 });
55005431
55015432 return self.genSetStack(
......@@ -5505,9 +5436,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl
55055436 .{},
55065437 );
55075438 },
5508 .compare_flags_unsigned,
5509 .compare_flags_signed,
5510 => {
5439 .eflags => {
55115440 const reg = try self.copyToTmpRegister(ty, mcv);
55125441 return self.genSetStack(ty, stack_offset, .{ .register = reg }, opts);
55135442 },
......@@ -5832,9 +5761,12 @@ fn genInlineMemcpy(
58325761
58335762 // je end
58345763 const loop_reloc = try self.addInst(.{
5835 .tag = .cond_jmp_eq_ne,
5836 .ops = Mir.Inst.Ops.encode(.{ .flags = 0b01 }),
5837 .data = .{ .inst = undefined },
5764 .tag = .cond_jmp,
5765 .ops = Mir.Inst.Ops.encode(.{}),
5766 .data = .{ .inst_cc = .{
5767 .inst = undefined,
5768 .cc = .e,
5769 } },
58385770 });
58395771
58405772 // mov tmp, [addr + rcx]
......@@ -5950,9 +5882,12 @@ fn genInlineMemset(
59505882
59515883 // je end
59525884 const loop_reloc = try self.addInst(.{
5953 .tag = .cond_jmp_eq_ne,
5954 .ops = Mir.Inst.Ops.encode(.{ .flags = 0b01 }),
5955 .data = .{ .inst = undefined },
5885 .tag = .cond_jmp,
5886 .ops = Mir.Inst.Ops.encode(.{}),
5887 .data = .{ .inst_cc = .{
5888 .inst = undefined,
5889 .cc = .e,
5890 } },
59565891 });
59575892
59585893 switch (value) {
......@@ -5996,9 +5931,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
59965931 const abi_size = @intCast(u32, ty.abiSize(self.target.*));
59975932 switch (mcv) {
59985933 .dead => unreachable,
5999 .register_overflow_unsigned,
6000 .register_overflow_signed,
6001 => unreachable,
5934 .register_overflow => unreachable,
60025935 .ptr_stack_offset => |off| {
60035936 if (off < std.math.minInt(i32) or off > std.math.maxInt(i32)) {
60045937 return self.fail("stack offset too large", .{});
......@@ -6025,31 +5958,13 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
60255958 else => unreachable,
60265959 }
60275960 },
6028 .compare_flags_unsigned,
6029 .compare_flags_signed,
6030 => |op| {
6031 const tag: Mir.Inst.Tag = switch (op) {
6032 .gte, .gt, .lt, .lte => if (mcv == .compare_flags_unsigned)
6033 Mir.Inst.Tag.cond_set_byte_above_below
6034 else
6035 Mir.Inst.Tag.cond_set_byte_greater_less,
6036 .eq, .neq => .cond_set_byte_eq_ne,
6037 };
6038 const flags: u2 = switch (op) {
6039 .gte => 0b00,
6040 .gt => 0b01,
6041 .lt => 0b10,
6042 .lte => 0b11,
6043 .eq => 0b01,
6044 .neq => 0b00,
6045 };
5961 .eflags => |cc| {
60465962 _ = try self.addInst(.{
6047 .tag = tag,
5963 .tag = .cond_set_byte,
60485964 .ops = Mir.Inst.Ops.encode(.{
60495965 .reg1 = reg.to8(),
6050 .flags = flags,
60515966 }),
6052 .data = undefined,
5967 .data = .{ .cc = cc },
60535968 });
60545969 },
60555970 .immediate => |x| {
src/arch/x86_64/Emit.zig+221-75
......@@ -158,20 +158,9 @@ pub fn lowerMir(emit: *Emit) InnerError!void {
158158 .jmp => try emit.mirJmpCall(.jmp_near, inst),
159159 .call => try emit.mirJmpCall(.call_near, inst),
160160
161 .cond_jmp_greater_less,
162 .cond_jmp_above_below,
163 .cond_jmp_eq_ne,
164 => try emit.mirCondJmp(tag, inst),
165
166 .cond_set_byte_greater_less,
167 .cond_set_byte_above_below,
168 .cond_set_byte_eq_ne,
169 .cond_set_byte_overflow,
170 => try emit.mirCondSetByte(tag, inst),
171
172 .cond_mov_eq => try emit.mirCondMov(.cmove, inst),
173 .cond_mov_lt => try emit.mirCondMov(.cmovl, inst),
174 .cond_mov_below => try emit.mirCondMov(.cmovb, inst),
161 .cond_jmp => try emit.mirCondJmp(inst),
162 .cond_set_byte => try emit.mirCondSetByte(inst),
163 .cond_mov => try emit.mirCondMov(inst),
175164
176165 .ret => try emit.mirRet(inst),
177166
......@@ -356,70 +345,130 @@ fn mirJmpCall(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
356345 }
357346}
358347
359fn mirCondJmp(emit: *Emit, mir_tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerError!void {
360 const ops = emit.mir.instructions.items(.ops)[inst].decode();
361 const target = emit.mir.instructions.items(.data)[inst].inst;
362 const tag = switch (mir_tag) {
363 .cond_jmp_greater_less => switch (ops.flags) {
364 0b00 => Tag.jge,
365 0b01 => Tag.jg,
366 0b10 => Tag.jl,
367 0b11 => Tag.jle,
368 },
369 .cond_jmp_above_below => switch (ops.flags) {
370 0b00 => Tag.jae,
371 0b01 => Tag.ja,
372 0b10 => Tag.jb,
373 0b11 => Tag.jbe,
374 },
375 .cond_jmp_eq_ne => switch (@truncate(u1, ops.flags)) {
376 0b0 => Tag.jne,
377 0b1 => Tag.je,
378 },
379 else => unreachable,
348fn mirCondJmp(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
349 const mir_tag = emit.mir.instructions.items(.tag)[inst];
350 assert(mir_tag == .cond_jmp);
351 const inst_cc = emit.mir.instructions.items(.data)[inst].inst_cc;
352 const tag: Tag = switch (inst_cc.cc) {
353 .a => .ja,
354 .ae => .jae,
355 .b => .jb,
356 .be => .jbe,
357 .c => .jc,
358 .e => .je,
359 .g => .jg,
360 .ge => .jge,
361 .l => .jl,
362 .le => .jle,
363 .na => .jna,
364 .nae => .jnae,
365 .nb => .jnb,
366 .nbe => .jnbe,
367 .nc => .jnc,
368 .ne => .jne,
369 .ng => .jng,
370 .nge => .jnge,
371 .nl => .jnl,
372 .nle => .jnle,
373 .no => .jno,
374 .np => .jnp,
375 .ns => .jns,
376 .nz => .jnz,
377 .o => .jo,
378 .p => .jp,
379 .pe => .jpe,
380 .po => .jpo,
381 .s => .js,
382 .z => .jz,
380383 };
381384 const source = emit.code.items.len;
382385 try lowerToDEnc(tag, 0, emit.code);
383386 try emit.relocs.append(emit.bin_file.allocator, .{
384387 .source = source,
385 .target = target,
388 .target = inst_cc.inst,
386389 .offset = emit.code.items.len - 4,
387390 .length = 6,
388391 });
389392}
390393
391fn mirCondSetByte(emit: *Emit, mir_tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerError!void {
394fn mirCondSetByte(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
395 const mir_tag = emit.mir.instructions.items(.tag)[inst];
396 assert(mir_tag == .cond_set_byte);
392397 const ops = emit.mir.instructions.items(.ops)[inst].decode();
393 const tag = switch (mir_tag) {
394 .cond_set_byte_greater_less => switch (ops.flags) {
395 0b00 => Tag.setge,
396 0b01 => Tag.setg,
397 0b10 => Tag.setl,
398 0b11 => Tag.setle,
399 },
400 .cond_set_byte_above_below => switch (ops.flags) {
401 0b00 => Tag.setae,
402 0b01 => Tag.seta,
403 0b10 => Tag.setb,
404 0b11 => Tag.setbe,
405 },
406 .cond_set_byte_eq_ne => switch (@truncate(u1, ops.flags)) {
407 0b0 => Tag.setne,
408 0b1 => Tag.sete,
409 },
410 .cond_set_byte_overflow => switch (ops.flags) {
411 0b00 => Tag.seto,
412 0b01 => Tag.setno,
413 0b10 => Tag.setc,
414 0b11 => Tag.setnc,
415 },
416 else => unreachable,
398 const cc = emit.mir.instructions.items(.data)[inst].cc;
399 const tag: Tag = switch (cc) {
400 .a => .seta,
401 .ae => .setae,
402 .b => .setb,
403 .be => .setbe,
404 .c => .setc,
405 .e => .sete,
406 .g => .setg,
407 .ge => .setge,
408 .l => .setl,
409 .le => .setle,
410 .na => .setna,
411 .nae => .setnae,
412 .nb => .setnb,
413 .nbe => .setnbe,
414 .nc => .setnc,
415 .ne => .setne,
416 .ng => .setng,
417 .nge => .setnge,
418 .nl => .setnl,
419 .nle => .setnle,
420 .no => .setno,
421 .np => .setnp,
422 .ns => .setns,
423 .nz => .setnz,
424 .o => .seto,
425 .p => .setp,
426 .pe => .setpe,
427 .po => .setpo,
428 .s => .sets,
429 .z => .setz,
417430 };
418431 return lowerToMEnc(tag, RegisterOrMemory.reg(ops.reg1.to8()), emit.code);
419432}
420433
421fn mirCondMov(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
434fn mirCondMov(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
435 const mir_tag = emit.mir.instructions.items(.tag)[inst];
436 assert(mir_tag == .cond_mov);
422437 const ops = emit.mir.instructions.items(.ops)[inst].decode();
438 const cc = emit.mir.instructions.items(.data)[inst].cc;
439 const tag: Tag = switch (cc) {
440 .a => .cmova,
441 .ae => .cmovae,
442 .b => .cmovb,
443 .be => .cmovbe,
444 .c => .cmovc,
445 .e => .cmove,
446 .g => .cmovg,
447 .ge => .cmovge,
448 .l => .cmovl,
449 .le => .cmovle,
450 .na => .cmovna,
451 .nae => .cmovnae,
452 .nb => .cmovnb,
453 .nbe => .cmovnbe,
454 .nc => .cmovnc,
455 .ne => .cmovne,
456 .ng => .cmovng,
457 .nge => .cmovnge,
458 .nl => .cmovnl,
459 .nle => .cmovnle,
460 .no => .cmovno,
461 .np => .cmovnp,
462 .ns => .cmovns,
463 .nz => .cmovnz,
464 .o => .cmovo,
465 .p => .cmovp,
466 .pe => .cmovpe,
467 .po => .cmovpo,
468 .s => .cmovs,
469 .z => .cmovz,
470 };
471
423472 if (ops.flags == 0b00) {
424473 return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code);
425474 }
......@@ -1277,7 +1326,7 @@ const Tag = enum {
12771326 setp,
12781327 setpe,
12791328 setnp,
1280 setop,
1329 setpo,
12811330 setl,
12821331 setnge,
12831332 setnl,
......@@ -1286,6 +1335,36 @@ const Tag = enum {
12861335 setng,
12871336 setnle,
12881337 setg,
1338 cmovo,
1339 cmovno,
1340 cmovb,
1341 cmovc,
1342 cmovnae,
1343 cmovnb,
1344 cmovnc,
1345 cmovae,
1346 cmove,
1347 cmovz,
1348 cmovne,
1349 cmovnz,
1350 cmovbe,
1351 cmovna,
1352 cmova,
1353 cmovnbe,
1354 cmovs,
1355 cmovns,
1356 cmovp,
1357 cmovpe,
1358 cmovnp,
1359 cmovpo,
1360 cmovl,
1361 cmovnge,
1362 cmovnl,
1363 cmovge,
1364 cmovle,
1365 cmovng,
1366 cmovnle,
1367 cmovg,
12891368 shl,
12901369 sal,
12911370 shr,
......@@ -1294,12 +1373,6 @@ const Tag = enum {
12941373 cwd,
12951374 cdq,
12961375 cqo,
1297 cmove,
1298 cmovz,
1299 cmovl,
1300 cmovng,
1301 cmovb,
1302 cmovnae,
13031376 movsd,
13041377 movss,
13051378 addsd,
......@@ -1372,7 +1445,7 @@ const Tag = enum {
13721445 .setp,
13731446 .setpe,
13741447 .setnp,
1375 .setop,
1448 .setpo,
13761449 .setl,
13771450 .setnge,
13781451 .setnl,
......@@ -1492,77 +1565,110 @@ inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) OpCode {
14921565 .d => return switch (tag) {
14931566 .jmp_near => OpCode.init(&.{0xe9}),
14941567 .call_near => OpCode.init(&.{0xe8}),
1568
14951569 .jo => if (is_one_byte) OpCode.init(&.{0x70}) else OpCode.init(&.{0x0f,0x80}),
1570
14961571 .jno => if (is_one_byte) OpCode.init(&.{0x71}) else OpCode.init(&.{0x0f,0x81}),
1572
14971573 .jb,
14981574 .jc,
14991575 .jnae => if (is_one_byte) OpCode.init(&.{0x72}) else OpCode.init(&.{0x0f,0x82}),
1576
15001577 .jnb,
15011578 .jnc,
15021579 .jae => if (is_one_byte) OpCode.init(&.{0x73}) else OpCode.init(&.{0x0f,0x83}),
1580
15031581 .je,
15041582 .jz => if (is_one_byte) OpCode.init(&.{0x74}) else OpCode.init(&.{0x0f,0x84}),
1583
15051584 .jne,
15061585 .jnz => if (is_one_byte) OpCode.init(&.{0x75}) else OpCode.init(&.{0x0f,0x85}),
1586
15071587 .jna,
15081588 .jbe => if (is_one_byte) OpCode.init(&.{0x76}) else OpCode.init(&.{0x0f,0x86}),
1589
15091590 .jnbe,
15101591 .ja => if (is_one_byte) OpCode.init(&.{0x77}) else OpCode.init(&.{0x0f,0x87}),
1592
15111593 .js => if (is_one_byte) OpCode.init(&.{0x78}) else OpCode.init(&.{0x0f,0x88}),
1594
15121595 .jns => if (is_one_byte) OpCode.init(&.{0x79}) else OpCode.init(&.{0x0f,0x89}),
1596
15131597 .jpe,
15141598 .jp => if (is_one_byte) OpCode.init(&.{0x7a}) else OpCode.init(&.{0x0f,0x8a}),
1599
15151600 .jpo,
15161601 .jnp => if (is_one_byte) OpCode.init(&.{0x7b}) else OpCode.init(&.{0x0f,0x8b}),
1602
15171603 .jnge,
15181604 .jl => if (is_one_byte) OpCode.init(&.{0x7c}) else OpCode.init(&.{0x0f,0x8c}),
1605
15191606 .jge,
15201607 .jnl => if (is_one_byte) OpCode.init(&.{0x7d}) else OpCode.init(&.{0x0f,0x8d}),
1608
15211609 .jle,
15221610 .jng => if (is_one_byte) OpCode.init(&.{0x7e}) else OpCode.init(&.{0x0f,0x8e}),
1611
15231612 .jg,
15241613 .jnle => if (is_one_byte) OpCode.init(&.{0x7f}) else OpCode.init(&.{0x0f,0x8f}),
1614
15251615 else => unreachable,
15261616 },
15271617 .m => return switch (tag) {
15281618 .jmp_near,
15291619 .call_near,
15301620 .push => OpCode.init(&.{0xff}),
1621
15311622 .pop => OpCode.init(&.{0x8f}),
15321623 .seto => OpCode.init(&.{0x0f,0x90}),
15331624 .setno => OpCode.init(&.{0x0f,0x91}),
1625
15341626 .setb,
15351627 .setc,
15361628 .setnae => OpCode.init(&.{0x0f,0x92}),
1629
15371630 .setnb,
15381631 .setnc,
15391632 .setae => OpCode.init(&.{0x0f,0x93}),
1633
15401634 .sete,
15411635 .setz => OpCode.init(&.{0x0f,0x94}),
1636
15421637 .setne,
15431638 .setnz => OpCode.init(&.{0x0f,0x95}),
1639
15441640 .setbe,
15451641 .setna => OpCode.init(&.{0x0f,0x96}),
1642
15461643 .seta,
15471644 .setnbe => OpCode.init(&.{0x0f,0x97}),
1645
15481646 .sets => OpCode.init(&.{0x0f,0x98}),
15491647 .setns => OpCode.init(&.{0x0f,0x99}),
1648
15501649 .setp,
15511650 .setpe => OpCode.init(&.{0x0f,0x9a}),
1651
15521652 .setnp,
1553 .setop => OpCode.init(&.{0x0f,0x9b}),
1653 .setpo => OpCode.init(&.{0x0f,0x9b}),
1654
15541655 .setl,
15551656 .setnge => OpCode.init(&.{0x0f,0x9c}),
1657
15561658 .setnl,
15571659 .setge => OpCode.init(&.{0x0f,0x9d}),
1660
15581661 .setle,
15591662 .setng => OpCode.init(&.{0x0f,0x9e}),
1663
15601664 .setnle,
15611665 .setg => OpCode.init(&.{0x0f,0x9f}),
1666
15621667 .idiv,
15631668 .div,
15641669 .imul,
15651670 .mul => if (is_one_byte) OpCode.init(&.{0xf6}) else OpCode.init(&.{0xf7}),
1671
15661672 .fisttp16 => OpCode.init(&.{0xdf}),
15671673 .fisttp32 => OpCode.init(&.{0xdb}),
15681674 .fisttp64 => OpCode.init(&.{0xdd}),
......@@ -1640,12 +1746,52 @@ inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) OpCode {
16401746 .movzx => if (is_one_byte) OpCode.init(&.{0x0f,0xb6}) else OpCode.init(&.{0x0f,0xb7}),
16411747 .lea => if (is_one_byte) OpCode.init(&.{0x8c}) else OpCode.init(&.{0x8d}),
16421748 .imul => OpCode.init(&.{0x0f,0xaf}),
1643 .cmove,
1644 .cmovz => OpCode.init(&.{0x0f,0x44}),
1749
1750 .cmova,
1751 .cmovnbe, => OpCode.init(&.{0x0f,0x47}),
1752
1753 .cmovae,
1754 .cmovnb, => OpCode.init(&.{0x0f,0x43}),
1755
16451756 .cmovb,
1757 .cmovc,
16461758 .cmovnae => OpCode.init(&.{0x0f,0x42}),
1759
1760 .cmovbe,
1761 .cmovna, => OpCode.init(&.{0x0f,0x46}),
1762
1763 .cmove,
1764 .cmovz, => OpCode.init(&.{0x0f,0x44}),
1765
1766 .cmovg,
1767 .cmovnle, => OpCode.init(&.{0x0f,0x4f}),
1768
1769 .cmovge,
1770 .cmovnl, => OpCode.init(&.{0x0f,0x4d}),
1771
16471772 .cmovl,
1648 .cmovng => OpCode.init(&.{0x0f,0x4c}),
1773 .cmovnge, => OpCode.init(&.{0x0f,0x4c}),
1774
1775 .cmovle,
1776 .cmovng, => OpCode.init(&.{0x0f,0x4e}),
1777
1778 .cmovne,
1779 .cmovnz, => OpCode.init(&.{0x0f,0x45}),
1780
1781 .cmovno => OpCode.init(&.{0x0f,0x41}),
1782
1783 .cmovnp,
1784 .cmovpo, => OpCode.init(&.{0x0f,0x4b}),
1785
1786 .cmovns => OpCode.init(&.{0x0f,0x49}),
1787
1788 .cmovo => OpCode.init(&.{0x0f,0x40}),
1789
1790 .cmovp,
1791 .cmovpe, => OpCode.init(&.{0x0f,0x4a}),
1792
1793 .cmovs => OpCode.init(&.{0x0f,0x48}),
1794
16491795 .movsd => OpCode.init(&.{0xf2,0x0f,0x10}),
16501796 .movss => OpCode.init(&.{0xf3,0x0f,0x10}),
16511797 .addsd => OpCode.init(&.{0xf2,0x0f,0x58}),
......@@ -1735,7 +1881,7 @@ inline fn getModRmExt(tag: Tag) u3 {
17351881 .setp,
17361882 .setpe,
17371883 .setnp,
1738 .setop,
1884 .setpo,
17391885 .setl,
17401886 .setnge,
17411887 .setnl,
src/arch/x86_64/Mir.zig+21-28
......@@ -275,42 +275,25 @@ pub const Inst = struct {
275275 call,
276276
277277 /// ops flags:
278 /// 0b00 gte
279 /// 0b01 gt
280 /// 0b10 lt
281 /// 0b11 lte
282 cond_jmp_greater_less,
283 cond_set_byte_greater_less,
284
285 /// ops flags:
286 /// 0b00 above or equal
287 /// 0b01 above
288 /// 0b10 below
289 /// 0b11 below or equal
290 cond_jmp_above_below,
291 cond_set_byte_above_below,
278 /// unused
279 /// Notes:
280 /// * uses `inst_cc` in Data.
281 cond_jmp,
292282
293283 /// ops flags:
294 /// 0bX0 ne
295 /// 0bX1 eq
296 cond_jmp_eq_ne,
297 cond_set_byte_eq_ne,
284 /// 0b00 reg1
285 /// Notes:
286 /// * uses condition code (CC) stored as part of data
287 cond_set_byte,
298288
299289 /// ops flags:
300290 /// 0b00 reg1, reg2,
301291 /// 0b01 reg1, word ptr [reg2 + imm]
302292 /// 0b10 reg1, dword ptr [reg2 + imm]
303293 /// 0b11 reg1, qword ptr [reg2 + imm]
304 cond_mov_eq,
305 cond_mov_lt,
306 cond_mov_below,
307
308 /// ops flags:
309 /// 0b00 reg1 if OF = 1
310 /// 0b01 reg1 if OF = 0
311 /// 0b10 reg1 if CF = 1
312 /// 0b11 reg1 if CF = 0
313 cond_set_byte_overflow,
294 /// Notes:
295 /// * uses condition code (CC) stored as part of data
296 cond_mov,
314297
315298 /// ops flags: form:
316299 /// 0b00 reg1
......@@ -451,6 +434,16 @@ pub const Inst = struct {
451434 inst: Index,
452435 /// A 32-bit immediate value.
453436 imm: u32,
437 /// A condition code for use with EFLAGS register.
438 cc: bits.Condition,
439 /// Another instruction with condition code.
440 /// Used by `cond_jmp`.
441 inst_cc: struct {
442 /// Another instruction.
443 inst: Index,
444 /// A condition code for use with EFLAGS register.
445 cc: bits.Condition,
446 },
454447 /// An extern function.
455448 extern_fn: struct {
456449 /// Index of the containing atom.
src/arch/x86_64/bits.zig+129
......@@ -6,6 +6,135 @@ const ArrayList = std.ArrayList;
66const Allocator = std.mem.Allocator;
77const DW = std.dwarf;
88
9/// EFLAGS condition codes
10pub const Condition = enum(u5) {
11 /// above
12 a,
13 /// above or equal
14 ae,
15 /// below
16 b,
17 /// below or equal
18 be,
19 /// carry
20 c,
21 /// equal
22 e,
23 /// greater
24 g,
25 /// greater or equal
26 ge,
27 /// less
28 l,
29 /// less or equal
30 le,
31 /// not above
32 na,
33 /// not above or equal
34 nae,
35 /// not below
36 nb,
37 /// not below or equal
38 nbe,
39 /// not carry
40 nc,
41 /// not equal
42 ne,
43 /// not greater
44 ng,
45 /// not greater or equal
46 nge,
47 /// not less
48 nl,
49 /// not less or equal
50 nle,
51 /// not overflow
52 no,
53 /// not parity
54 np,
55 /// not sign
56 ns,
57 /// not zero
58 nz,
59 /// overflow
60 o,
61 /// parity
62 p,
63 /// parity even
64 pe,
65 /// parity odd
66 po,
67 /// sign
68 s,
69 /// zero
70 z,
71
72 /// Converts a std.math.CompareOperator into a condition flag,
73 /// i.e. returns the condition that is true iff the result of the
74 /// comparison is true. Assumes signed comparison
75 pub fn fromCompareOperatorSigned(op: std.math.CompareOperator) Condition {
76 return switch (op) {
77 .gte => .ge,
78 .gt => .g,
79 .neq => .ne,
80 .lt => .l,
81 .lte => .le,
82 .eq => .e,
83 };
84 }
85
86 /// Converts a std.math.CompareOperator into a condition flag,
87 /// i.e. returns the condition that is true iff the result of the
88 /// comparison is true. Assumes unsigned comparison
89 pub fn fromCompareOperatorUnsigned(op: std.math.CompareOperator) Condition {
90 return switch (op) {
91 .gte => .ae,
92 .gt => .a,
93 .neq => .ne,
94 .lt => .b,
95 .lte => .be,
96 .eq => .e,
97 };
98 }
99
100 /// Returns the condition which is true iff the given condition is
101 /// false (if such a condition exists)
102 pub fn negate(cond: Condition) Condition {
103 return switch (cond) {
104 .a => .na,
105 .ae => .nae,
106 .b => .nb,
107 .be => .nbe,
108 .c => .nc,
109 .e => .ne,
110 .g => .ng,
111 .ge => .nge,
112 .l => .nl,
113 .le => .nle,
114 .na => .a,
115 .nae => .ae,
116 .nb => .b,
117 .nbe => .be,
118 .nc => .c,
119 .ne => .e,
120 .ng => .g,
121 .nge => .ge,
122 .nl => .l,
123 .nle => .le,
124 .no => .o,
125 .np => .p,
126 .ns => .s,
127 .nz => .z,
128 .o => .no,
129 .p => .np,
130 .pe => unreachable,
131 .po => unreachable,
132 .s => .ns,
133 .z => .nz,
134 };
135 }
136};
137
9138// zig fmt: off
10139
11140/// Definitions of all of the general purpose x64 registers. The order is semantically meaningful.