| ... | ... | @@ -37,6 +37,7 @@ const caller_preserved_regs = abi.caller_preserved_regs; |
| 37 | 37 | const c_abi_int_param_regs = abi.c_abi_int_param_regs; |
| 38 | 38 | const c_abi_int_return_regs = abi.c_abi_int_return_regs; |
| 39 | 39 | |
| 40 | const Condition = bits.Condition; |
| 40 | 41 | const RegisterManager = abi.RegisterManager; |
| 41 | 42 | const RegisterLock = RegisterManager.RegisterLock; |
| 42 | 43 | const Register = bits.Register; |
| ... | ... | @@ -65,7 +66,7 @@ arg_index: u32, |
| 65 | 66 | src_loc: Module.SrcLoc, |
| 66 | 67 | stack_align: u32, |
| 67 | 68 | |
| 68 | | compare_flags_inst: ?Air.Inst.Index = null, |
| 69 | eflags_inst: ?Air.Inst.Index = null, |
| 69 | 70 | |
| 70 | 71 | /// MIR Instructions |
| 71 | 72 | mir_instructions: std.MultiArrayList(Mir.Inst) = .{}, |
| ... | ... | @@ -149,12 +150,8 @@ pub const MCValue = union(enum) { |
| 149 | 150 | stack_offset: i32, |
| 150 | 151 | /// The value is a pointer to one of the stack variables (payload is stack offset). |
| 151 | 152 | 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, |
| 153 | /// The value resides in the EFLAGS register. |
| 154 | eflags: Condition, |
| 158 | 155 | |
| 159 | 156 | fn isMemory(mcv: MCValue) bool { |
| 160 | 157 | return switch (mcv) { |
| ... | ... | @@ -183,8 +180,7 @@ pub const MCValue = union(enum) { |
| 183 | 180 | |
| 184 | 181 | .immediate, |
| 185 | 182 | .memory, |
| 186 | | .compare_flags_unsigned, |
| 187 | | .compare_flags_signed, |
| 183 | .eflags, |
| 188 | 184 | .ptr_stack_offset, |
| 189 | 185 | .undef, |
| 190 | 186 | .register_overflow_unsigned, |
| ... | ... | @@ -780,10 +776,10 @@ fn processDeath(self: *Self, inst: Air.Inst.Index) void { |
| 780 | 776 | }, |
| 781 | 777 | .register_overflow_signed, .register_overflow_unsigned => |reg| { |
| 782 | 778 | self.register_manager.freeReg(reg.to64()); |
| 783 | | self.compare_flags_inst = null; |
| 779 | self.eflags_inst = null; |
| 784 | 780 | }, |
| 785 | | .compare_flags_signed, .compare_flags_unsigned => { |
| 786 | | self.compare_flags_inst = null; |
| 781 | .eflags => { |
| 782 | self.eflags_inst = null; |
| 787 | 783 | }, |
| 788 | 784 | else => {}, // TODO process stack allocation death |
| 789 | 785 | } |
| ... | ... | @@ -929,16 +925,14 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void |
| 929 | 925 | try self.genSetStack(self.air.typeOfIndex(inst), stack_mcv.stack_offset, reg_mcv, .{}); |
| 930 | 926 | } |
| 931 | 927 | |
| 932 | | pub fn spillCompareFlagsIfOccupied(self: *Self) !void { |
| 933 | | if (self.compare_flags_inst) |inst_to_save| { |
| 928 | pub fn spillEflagsIfOccupied(self: *Self) !void { |
| 929 | if (self.eflags_inst) |inst_to_save| { |
| 934 | 930 | const mcv = self.getResolvedInstValue(inst_to_save); |
| 935 | 931 | const new_mcv = switch (mcv) { |
| 936 | 932 | .register_overflow_signed, |
| 937 | 933 | .register_overflow_unsigned, |
| 938 | 934 | => try self.allocRegOrMem(inst_to_save, false), |
| 939 | | .compare_flags_signed, |
| 940 | | .compare_flags_unsigned, |
| 941 | | => try self.allocRegOrMem(inst_to_save, true), |
| 935 | .eflags => try self.allocRegOrMem(inst_to_save, true), |
| 942 | 936 | else => unreachable, |
| 943 | 937 | }; |
| 944 | 938 | |
| ... | ... | @@ -948,7 +942,7 @@ pub fn spillCompareFlagsIfOccupied(self: *Self) !void { |
| 948 | 942 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; |
| 949 | 943 | try branch.inst_table.put(self.gpa, inst_to_save, new_mcv); |
| 950 | 944 | |
| 951 | | self.compare_flags_inst = null; |
| 945 | self.eflags_inst = null; |
| 952 | 946 | |
| 953 | 947 | // TODO consolidate with register manager and spillInstruction |
| 954 | 948 | // this call should really belong in the register manager! |
| ... | ... | @@ -1123,31 +1117,8 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void { |
| 1123 | 1117 | switch (operand) { |
| 1124 | 1118 | .dead => unreachable, |
| 1125 | 1119 | .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; |
| 1120 | .eflags => |cc| { |
| 1121 | break :result MCValue{ .eflags = cc.negate() }; |
| 1151 | 1122 | }, |
| 1152 | 1123 | else => {}, |
| 1153 | 1124 | } |
| ... | ... | @@ -1213,13 +1184,17 @@ fn airMin(self: *Self, inst: Air.Inst.Index) !void { |
| 1213 | 1184 | try self.genBinOpMir(.cmp, ty, .{ .register = lhs_reg }, rhs_mcv); |
| 1214 | 1185 | |
| 1215 | 1186 | const dst_mcv = try self.copyToRegisterWithInstTracking(inst, ty, rhs_mcv); |
| 1187 | const cc: Condition = switch (signedness) { |
| 1188 | .unsigned => .b, |
| 1189 | .signed => .l, |
| 1190 | }; |
| 1216 | 1191 | _ = try self.addInst(.{ |
| 1217 | | .tag = if (signedness == .signed) .cond_mov_lt else .cond_mov_below, |
| 1192 | .tag = .cond_mov, |
| 1218 | 1193 | .ops = Mir.Inst.Ops.encode(.{ |
| 1219 | 1194 | .reg1 = dst_mcv.register, |
| 1220 | 1195 | .reg2 = lhs_reg, |
| 1221 | 1196 | }), |
| 1222 | | .data = undefined, |
| 1197 | .data = .{ .cc = cc }, |
| 1223 | 1198 | }); |
| 1224 | 1199 | |
| 1225 | 1200 | break :result dst_mcv; |
| ... | ... | @@ -1341,7 +1316,7 @@ fn airAddSubShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1341 | 1316 | return self.fail("TODO implement add/sub/shl with overflow for Ints larger than 64bits", .{}); |
| 1342 | 1317 | } |
| 1343 | 1318 | |
| 1344 | | try self.spillCompareFlagsIfOccupied(); |
| 1319 | try self.spillEflagsIfOccupied(); |
| 1345 | 1320 | |
| 1346 | 1321 | if (tag == .shl_with_overflow) { |
| 1347 | 1322 | try self.spillRegisters(1, .{.rcx}); |
| ... | ... | @@ -1362,7 +1337,7 @@ fn airAddSubShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1362 | 1337 | const int_info = ty.intInfo(self.target.*); |
| 1363 | 1338 | |
| 1364 | 1339 | if (math.isPowerOfTwo(int_info.bits) and int_info.bits >= 8) { |
| 1365 | | self.compare_flags_inst = inst; |
| 1340 | self.eflags_inst = inst; |
| 1366 | 1341 | |
| 1367 | 1342 | const result: MCValue = switch (int_info.signedness) { |
| 1368 | 1343 | .signed => .{ .register_overflow_signed = partial.register }, |
| ... | ... | @@ -1371,7 +1346,7 @@ fn airAddSubShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1371 | 1346 | break :result result; |
| 1372 | 1347 | } |
| 1373 | 1348 | |
| 1374 | | self.compare_flags_inst = null; |
| 1349 | self.eflags_inst = null; |
| 1375 | 1350 | |
| 1376 | 1351 | const tuple_ty = self.air.typeOfIndex(inst); |
| 1377 | 1352 | const tuple_size = @intCast(u32, tuple_ty.abiSize(self.target.*)); |
| ... | ... | @@ -1413,17 +1388,16 @@ fn genSetStackTruncatedOverflowCompare( |
| 1413 | 1388 | }; |
| 1414 | 1389 | |
| 1415 | 1390 | const overflow_reg = temp_regs[0]; |
| 1416 | | const flags: u2 = switch (int_info.signedness) { |
| 1417 | | .signed => 0b00, |
| 1418 | | .unsigned => 0b10, |
| 1391 | const cc: Condition = switch (int_info.signedness) { |
| 1392 | .signed => .o, |
| 1393 | .unsigned => .c, |
| 1419 | 1394 | }; |
| 1420 | 1395 | _ = try self.addInst(.{ |
| 1421 | | .tag = .cond_set_byte_overflow, |
| 1396 | .tag = .cond_set_byte, |
| 1422 | 1397 | .ops = Mir.Inst.Ops.encode(.{ |
| 1423 | 1398 | .reg1 = overflow_reg.to8(), |
| 1424 | | .flags = flags, |
| 1425 | 1399 | }), |
| 1426 | | .data = undefined, |
| 1400 | .data = .{ .cc = cc }, |
| 1427 | 1401 | }); |
| 1428 | 1402 | |
| 1429 | 1403 | const scratch_reg = temp_regs[1]; |
| ... | ... | @@ -1438,9 +1412,9 @@ fn genSetStackTruncatedOverflowCompare( |
| 1438 | 1412 | |
| 1439 | 1413 | const eq_reg = temp_regs[2]; |
| 1440 | 1414 | _ = try self.addInst(.{ |
| 1441 | | .tag = .cond_set_byte_eq_ne, |
| 1415 | .tag = .cond_set_byte, |
| 1442 | 1416 | .ops = Mir.Inst.Ops.encode(.{ .reg1 = eq_reg.to8() }), |
| 1443 | | .data = undefined, |
| 1417 | .data = .{ .cc = .ne }, |
| 1444 | 1418 | }); |
| 1445 | 1419 | |
| 1446 | 1420 | try self.genBinOpMir( |
| ... | ... | @@ -1477,8 +1451,8 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1477 | 1451 | const int_info = ty.intInfo(self.target.*); |
| 1478 | 1452 | |
| 1479 | 1453 | if (math.isPowerOfTwo(int_info.bits) and int_info.bits >= 8) { |
| 1480 | | try self.spillCompareFlagsIfOccupied(); |
| 1481 | | self.compare_flags_inst = inst; |
| 1454 | try self.spillEflagsIfOccupied(); |
| 1455 | self.eflags_inst = inst; |
| 1482 | 1456 | |
| 1483 | 1457 | try self.spillRegisters(2, .{ .rax, .rdx }); |
| 1484 | 1458 | |
| ... | ... | @@ -1492,8 +1466,8 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1492 | 1466 | }; |
| 1493 | 1467 | } |
| 1494 | 1468 | |
| 1495 | | try self.spillCompareFlagsIfOccupied(); |
| 1496 | | self.compare_flags_inst = null; |
| 1469 | try self.spillEflagsIfOccupied(); |
| 1470 | self.eflags_inst = null; |
| 1497 | 1471 | |
| 1498 | 1472 | const dst_reg: Register = dst_reg: { |
| 1499 | 1473 | switch (int_info.signedness) { |
| ... | ... | @@ -1686,12 +1660,12 @@ fn genInlineIntDivFloor(self: *Self, ty: Type, lhs: MCValue, rhs: MCValue) !MCVa |
| 1686 | 1660 | .data = undefined, |
| 1687 | 1661 | }); |
| 1688 | 1662 | _ = try self.addInst(.{ |
| 1689 | | .tag = .cond_mov_eq, |
| 1663 | .tag = .cond_mov, |
| 1690 | 1664 | .ops = Mir.Inst.Ops.encode(.{ |
| 1691 | 1665 | .reg1 = divisor.to64(), |
| 1692 | 1666 | .reg2 = .rdx, |
| 1693 | 1667 | }), |
| 1694 | | .data = undefined, |
| 1668 | .data = .{ .cc = .e }, |
| 1695 | 1669 | }); |
| 1696 | 1670 | try self.genBinOpMir(.add, Type.isize, .{ .register = divisor }, .{ .register = .rax }); |
| 1697 | 1671 | return MCValue{ .register = divisor }; |
| ... | ... | @@ -2511,8 +2485,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo |
| 2511 | 2485 | .undef => unreachable, |
| 2512 | 2486 | .unreach => unreachable, |
| 2513 | 2487 | .dead => unreachable, |
| 2514 | | .compare_flags_unsigned => unreachable, |
| 2515 | | .compare_flags_signed => unreachable, |
| 2488 | .eflags => unreachable, |
| 2516 | 2489 | .register_overflow_unsigned => unreachable, |
| 2517 | 2490 | .register_overflow_signed => unreachable, |
| 2518 | 2491 | .immediate => |imm| { |
| ... | ... | @@ -2532,8 +2505,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo |
| 2532 | 2505 | switch (dst_mcv) { |
| 2533 | 2506 | .dead => unreachable, |
| 2534 | 2507 | .undef => unreachable, |
| 2535 | | .compare_flags_unsigned => unreachable, |
| 2536 | | .compare_flags_signed => unreachable, |
| 2508 | .eflags => unreachable, |
| 2537 | 2509 | .register => |dst_reg| { |
| 2538 | 2510 | // mov dst_reg, [reg] |
| 2539 | 2511 | _ = try self.addInst(.{ |
| ... | ... | @@ -2637,8 +2609,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type |
| 2637 | 2609 | .undef => unreachable, |
| 2638 | 2610 | .unreach => unreachable, |
| 2639 | 2611 | .dead => unreachable, |
| 2640 | | .compare_flags_unsigned => unreachable, |
| 2641 | | .compare_flags_signed => unreachable, |
| 2612 | .eflags => unreachable, |
| 2642 | 2613 | .register_overflow_unsigned => unreachable, |
| 2643 | 2614 | .register_overflow_signed => unreachable, |
| 2644 | 2615 | .immediate => |imm| { |
| ... | ... | @@ -2660,8 +2631,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type |
| 2660 | 2631 | .undef => unreachable, |
| 2661 | 2632 | .dead => unreachable, |
| 2662 | 2633 | .unreach => unreachable, |
| 2663 | | .compare_flags_unsigned => unreachable, |
| 2664 | | .compare_flags_signed => unreachable, |
| 2634 | .eflags => unreachable, |
| 2665 | 2635 | .immediate => |imm| { |
| 2666 | 2636 | switch (abi_size) { |
| 2667 | 2637 | 1, 2, 4 => { |
| ... | ... | @@ -3041,18 +3011,17 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { |
| 3041 | 3011 | defer self.register_manager.unlockReg(reg_lock); |
| 3042 | 3012 | |
| 3043 | 3013 | 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, |
| 3014 | const cc: Condition = switch (mcv) { |
| 3015 | .register_overflow_unsigned => .c, |
| 3016 | .register_overflow_signed => .o, |
| 3047 | 3017 | else => unreachable, |
| 3048 | 3018 | }; |
| 3049 | 3019 | _ = try self.addInst(.{ |
| 3050 | | .tag = .cond_set_byte_overflow, |
| 3020 | .tag = .cond_set_byte, |
| 3051 | 3021 | .ops = Mir.Inst.Ops.encode(.{ |
| 3052 | 3022 | .reg1 = dst_reg.to8(), |
| 3053 | | .flags = flags, |
| 3054 | 3023 | }), |
| 3055 | | .data = undefined, |
| 3024 | .data = .{ .cc = cc }, |
| 3056 | 3025 | }); |
| 3057 | 3026 | break :result MCValue{ .register = dst_reg.to8() }; |
| 3058 | 3027 | }, |
| ... | ... | @@ -3479,8 +3448,7 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu |
| 3479 | 3448 | .none => unreachable, |
| 3480 | 3449 | .undef => unreachable, |
| 3481 | 3450 | .dead, .unreach, .immediate => unreachable, |
| 3482 | | .compare_flags_unsigned => unreachable, |
| 3483 | | .compare_flags_signed => unreachable, |
| 3451 | .eflags => unreachable, |
| 3484 | 3452 | .register_overflow_unsigned => unreachable, |
| 3485 | 3453 | .register_overflow_signed => unreachable, |
| 3486 | 3454 | .register => |dst_reg| { |
| ... | ... | @@ -3559,8 +3527,7 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu |
| 3559 | 3527 | .memory, |
| 3560 | 3528 | .got_load, |
| 3561 | 3529 | .direct_load, |
| 3562 | | .compare_flags_signed, |
| 3563 | | .compare_flags_unsigned, |
| 3530 | .eflags, |
| 3564 | 3531 | => { |
| 3565 | 3532 | assert(abi_size <= 8); |
| 3566 | 3533 | const dst_reg_lock = self.register_manager.lockReg(dst_reg); |
| ... | ... | @@ -3649,11 +3616,8 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu |
| 3649 | 3616 | .got_load, .direct_load => { |
| 3650 | 3617 | return self.fail("TODO implement x86 ADD/SUB/CMP source symbol at index in linker", .{}); |
| 3651 | 3618 | }, |
| 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)", .{}); |
| 3619 | .eflags => { |
| 3620 | return self.fail("TODO implement x86 ADD/SUB/CMP source eflags", .{}); |
| 3657 | 3621 | }, |
| 3658 | 3622 | } |
| 3659 | 3623 | }, |
| ... | ... | @@ -3674,8 +3638,7 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M |
| 3674 | 3638 | .none => unreachable, |
| 3675 | 3639 | .undef => unreachable, |
| 3676 | 3640 | .dead, .unreach, .immediate => unreachable, |
| 3677 | | .compare_flags_unsigned => unreachable, |
| 3678 | | .compare_flags_signed => unreachable, |
| 3641 | .eflags => unreachable, |
| 3679 | 3642 | .ptr_stack_offset => unreachable, |
| 3680 | 3643 | .register_overflow_unsigned => unreachable, |
| 3681 | 3644 | .register_overflow_signed => unreachable, |
| ... | ... | @@ -3734,11 +3697,8 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M |
| 3734 | 3697 | .got_load, .direct_load => { |
| 3735 | 3698 | return self.fail("TODO implement x86 multiply source symbol at index in linker", .{}); |
| 3736 | 3699 | }, |
| 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)", .{}); |
| 3700 | .eflags => { |
| 3701 | return self.fail("TODO implement x86 multiply source eflags", .{}); |
| 3742 | 3702 | }, |
| 3743 | 3703 | } |
| 3744 | 3704 | }, |
| ... | ... | @@ -3782,11 +3742,8 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M |
| 3782 | 3742 | .got_load, .direct_load => { |
| 3783 | 3743 | return self.fail("TODO implement x86 multiply source symbol at index in linker", .{}); |
| 3784 | 3744 | }, |
| 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)", .{}); |
| 3745 | .eflags => { |
| 3746 | return self.fail("TODO implement x86 multiply source eflags", .{}); |
| 3790 | 3747 | }, |
| 3791 | 3748 | } |
| 3792 | 3749 | }, |
| ... | ... | @@ -3905,7 +3862,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. |
| 3905 | 3862 | var info = try self.resolveCallingConventionValues(fn_ty); |
| 3906 | 3863 | defer info.deinit(self); |
| 3907 | 3864 | |
| 3908 | | try self.spillCompareFlagsIfOccupied(); |
| 3865 | try self.spillEflagsIfOccupied(); |
| 3909 | 3866 | |
| 3910 | 3867 | for (caller_preserved_regs) |reg| { |
| 3911 | 3868 | try self.register_manager.getReg(reg, null); |
| ... | ... | @@ -3957,8 +3914,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. |
| 3957 | 3914 | .memory => unreachable, |
| 3958 | 3915 | .got_load => unreachable, |
| 3959 | 3916 | .direct_load => unreachable, |
| 3960 | | .compare_flags_signed => unreachable, |
| 3961 | | .compare_flags_unsigned => unreachable, |
| 3917 | .eflags => unreachable, |
| 3962 | 3918 | .register_overflow_signed => unreachable, |
| 3963 | 3919 | .register_overflow_unsigned => unreachable, |
| 3964 | 3920 | } |
| ... | ... | @@ -4220,8 +4176,8 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { |
| 4220 | 4176 | break :blk ty.intInfo(self.target.*).signedness; |
| 4221 | 4177 | }; |
| 4222 | 4178 | |
| 4223 | | try self.spillCompareFlagsIfOccupied(); |
| 4224 | | self.compare_flags_inst = inst; |
| 4179 | try self.spillEflagsIfOccupied(); |
| 4180 | self.eflags_inst = inst; |
| 4225 | 4181 | |
| 4226 | 4182 | const result: MCValue = result: { |
| 4227 | 4183 | // There are 2 operands, destination and source. |
| ... | ... | @@ -4265,9 +4221,10 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { |
| 4265 | 4221 | defer if (src_lock) |lock| self.register_manager.unlockReg(lock); |
| 4266 | 4222 | |
| 4267 | 4223 | try self.genBinOpMir(.cmp, ty, dst_mcv, src_mcv); |
| 4224 | |
| 4268 | 4225 | break :result switch (signedness) { |
| 4269 | | .signed => MCValue{ .compare_flags_signed = op }, |
| 4270 | | .unsigned => MCValue{ .compare_flags_unsigned = op }, |
| 4226 | .signed => MCValue{ .eflags = Condition.fromCompareOperatorSigned(op) }, |
| 4227 | .unsigned => MCValue{ .eflags = Condition.fromCompareOperatorUnsigned(op) }, |
| 4271 | 4228 | }; |
| 4272 | 4229 | }; |
| 4273 | 4230 | |
| ... | ... | @@ -4440,47 +4397,39 @@ fn addDbgInfoTypeReloc(self: *Self, ty: Type) !void { |
| 4440 | 4397 | fn genCondBrMir(self: *Self, ty: Type, mcv: MCValue) !u32 { |
| 4441 | 4398 | const abi_size = ty.abiSize(self.target.*); |
| 4442 | 4399 | 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; |
| 4400 | .eflags => |cc| { |
| 4461 | 4401 | return self.addInst(.{ |
| 4462 | | .tag = tag, |
| 4463 | | .ops = Mir.Inst.Ops.encode(.{ .flags = flags }), |
| 4464 | | .data = .{ .inst = undefined }, |
| 4402 | .tag = .cond_jmp, |
| 4403 | .ops = Mir.Inst.Ops.encode(.{}), |
| 4404 | .data = .{ |
| 4405 | .inst_cc = .{ |
| 4406 | .inst = undefined, |
| 4407 | // Here we map the opposites since the jump is to the false branch. |
| 4408 | .cc = cc.negate(), |
| 4409 | }, |
| 4410 | }, |
| 4465 | 4411 | }); |
| 4466 | 4412 | }, |
| 4467 | 4413 | .register => |reg| { |
| 4468 | | try self.spillCompareFlagsIfOccupied(); |
| 4414 | try self.spillEflagsIfOccupied(); |
| 4469 | 4415 | _ = try self.addInst(.{ |
| 4470 | 4416 | .tag = .@"test", |
| 4471 | 4417 | .ops = Mir.Inst.Ops.encode(.{ .reg1 = reg }), |
| 4472 | 4418 | .data = .{ .imm = 1 }, |
| 4473 | 4419 | }); |
| 4474 | 4420 | return self.addInst(.{ |
| 4475 | | .tag = .cond_jmp_eq_ne, |
| 4476 | | .ops = Mir.Inst.Ops.encode(.{ .flags = 0b01 }), |
| 4477 | | .data = .{ .inst = undefined }, |
| 4421 | .tag = .cond_jmp, |
| 4422 | .ops = Mir.Inst.Ops.encode(.{}), |
| 4423 | .data = .{ .inst_cc = .{ |
| 4424 | .inst = undefined, |
| 4425 | .cc = .e, |
| 4426 | } }, |
| 4478 | 4427 | }); |
| 4479 | 4428 | }, |
| 4480 | 4429 | .immediate, |
| 4481 | 4430 | .stack_offset, |
| 4482 | 4431 | => { |
| 4483 | | try self.spillCompareFlagsIfOccupied(); |
| 4432 | try self.spillEflagsIfOccupied(); |
| 4484 | 4433 | if (abi_size <= 8) { |
| 4485 | 4434 | const reg = try self.copyToTmpRegister(ty, mcv); |
| 4486 | 4435 | return self.genCondBrMir(ty, .{ .register = reg }); |
| ... | ... | @@ -4516,7 +4465,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 4516 | 4465 | // Capture the state of register and stack allocation state so that we can revert to it. |
| 4517 | 4466 | const parent_next_stack_offset = self.next_stack_offset; |
| 4518 | 4467 | const parent_free_registers = self.register_manager.free_registers; |
| 4519 | | const parent_compare_flags_inst = self.compare_flags_inst; |
| 4468 | const parent_eflags_inst = self.eflags_inst; |
| 4520 | 4469 | var parent_stack = try self.stack.clone(self.gpa); |
| 4521 | 4470 | defer parent_stack.deinit(self.gpa); |
| 4522 | 4471 | const parent_registers = self.register_manager.registers; |
| ... | ... | @@ -4538,7 +4487,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 4538 | 4487 | defer saved_then_branch.deinit(self.gpa); |
| 4539 | 4488 | |
| 4540 | 4489 | self.register_manager.registers = parent_registers; |
| 4541 | | self.compare_flags_inst = parent_compare_flags_inst; |
| 4490 | self.eflags_inst = parent_eflags_inst; |
| 4542 | 4491 | |
| 4543 | 4492 | self.stack.deinit(self.gpa); |
| 4544 | 4493 | self.stack = parent_stack; |
| ... | ... | @@ -4640,8 +4589,8 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 4640 | 4589 | } |
| 4641 | 4590 | |
| 4642 | 4591 | fn isNull(self: *Self, inst: Air.Inst.Index, ty: Type, operand: MCValue) !MCValue { |
| 4643 | | try self.spillCompareFlagsIfOccupied(); |
| 4644 | | self.compare_flags_inst = inst; |
| 4592 | try self.spillEflagsIfOccupied(); |
| 4593 | self.eflags_inst = inst; |
| 4645 | 4594 | |
| 4646 | 4595 | const cmp_ty: Type = if (!ty.isPtrLikeOptional()) blk: { |
| 4647 | 4596 | var buf: Type.Payload.ElemType = undefined; |
| ... | ... | @@ -4651,13 +4600,13 @@ fn isNull(self: *Self, inst: Air.Inst.Index, ty: Type, operand: MCValue) !MCValu |
| 4651 | 4600 | |
| 4652 | 4601 | try self.genBinOpMir(.cmp, cmp_ty, operand, MCValue{ .immediate = 0 }); |
| 4653 | 4602 | |
| 4654 | | return MCValue{ .compare_flags_unsigned = .eq }; |
| 4603 | return MCValue{ .eflags = .e }; |
| 4655 | 4604 | } |
| 4656 | 4605 | |
| 4657 | 4606 | fn isNonNull(self: *Self, inst: Air.Inst.Index, ty: Type, operand: MCValue) !MCValue { |
| 4658 | 4607 | 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 }; |
| 4608 | assert(is_null_res.eflags == .e); |
| 4609 | return MCValue{ .eflags = is_null_res.eflags.negate() }; |
| 4661 | 4610 | } |
| 4662 | 4611 | |
| 4663 | 4612 | fn isErr(self: *Self, inst: Air.Inst.Index, ty: Type, operand: MCValue) !MCValue { |
| ... | ... | @@ -4667,8 +4616,8 @@ fn isErr(self: *Self, inst: Air.Inst.Index, ty: Type, operand: MCValue) !MCValue |
| 4667 | 4616 | return MCValue{ .immediate = 0 }; // always false |
| 4668 | 4617 | } |
| 4669 | 4618 | |
| 4670 | | try self.spillCompareFlagsIfOccupied(); |
| 4671 | | self.compare_flags_inst = inst; |
| 4619 | try self.spillEflagsIfOccupied(); |
| 4620 | self.eflags_inst = inst; |
| 4672 | 4621 | |
| 4673 | 4622 | const err_off = errUnionErrorOffset(ty.errorUnionPayload(), self.target.*); |
| 4674 | 4623 | switch (operand) { |
| ... | ... | @@ -4691,15 +4640,15 @@ fn isErr(self: *Self, inst: Air.Inst.Index, ty: Type, operand: MCValue) !MCValue |
| 4691 | 4640 | else => return self.fail("TODO implement isErr for {}", .{operand}), |
| 4692 | 4641 | } |
| 4693 | 4642 | |
| 4694 | | return MCValue{ .compare_flags_unsigned = .gt }; |
| 4643 | return MCValue{ .eflags = .a }; |
| 4695 | 4644 | } |
| 4696 | 4645 | |
| 4697 | 4646 | fn isNonErr(self: *Self, inst: Air.Inst.Index, ty: Type, operand: MCValue) !MCValue { |
| 4698 | 4647 | const is_err_res = try self.isErr(inst, ty, operand); |
| 4699 | 4648 | switch (is_err_res) { |
| 4700 | | .compare_flags_unsigned => |op| { |
| 4701 | | assert(op == .gt); |
| 4702 | | return MCValue{ .compare_flags_unsigned = .lte }; |
| 4649 | .eflags => |cc| { |
| 4650 | assert(cc == .a); |
| 4651 | return MCValue{ .eflags = cc.negate() }; |
| 4703 | 4652 | }, |
| 4704 | 4653 | .immediate => |imm| { |
| 4705 | 4654 | assert(imm == 0); |
| ... | ... | @@ -4914,10 +4863,9 @@ fn genCondSwitchMir(self: *Self, ty: Type, condition: MCValue, case: MCValue) !u |
| 4914 | 4863 | .none => unreachable, |
| 4915 | 4864 | .undef => unreachable, |
| 4916 | 4865 | .dead, .unreach => unreachable, |
| 4917 | | .compare_flags_signed => unreachable, |
| 4918 | | .compare_flags_unsigned => unreachable, |
| 4866 | .eflags => unreachable, |
| 4919 | 4867 | .register => |cond_reg| { |
| 4920 | | try self.spillCompareFlagsIfOccupied(); |
| 4868 | try self.spillEflagsIfOccupied(); |
| 4921 | 4869 | |
| 4922 | 4870 | const cond_reg_lock = self.register_manager.lockReg(cond_reg); |
| 4923 | 4871 | defer if (cond_reg_lock) |lock| self.register_manager.unlockReg(lock); |
| ... | ... | @@ -4965,13 +4913,16 @@ fn genCondSwitchMir(self: *Self, ty: Type, condition: MCValue, case: MCValue) !u |
| 4965 | 4913 | .data = undefined, |
| 4966 | 4914 | }); |
| 4967 | 4915 | return self.addInst(.{ |
| 4968 | | .tag = .cond_jmp_eq_ne, |
| 4916 | .tag = .cond_jmp, |
| 4969 | 4917 | .ops = Mir.Inst.Ops.encode(.{}), |
| 4970 | | .data = .{ .inst = undefined }, |
| 4918 | .data = .{ .inst_cc = .{ |
| 4919 | .inst = undefined, |
| 4920 | .cc = .ne, |
| 4921 | } }, |
| 4971 | 4922 | }); |
| 4972 | 4923 | }, |
| 4973 | 4924 | .stack_offset => { |
| 4974 | | try self.spillCompareFlagsIfOccupied(); |
| 4925 | try self.spillEflagsIfOccupied(); |
| 4975 | 4926 | |
| 4976 | 4927 | if (abi_size <= 8) { |
| 4977 | 4928 | const reg = try self.copyToTmpRegister(ty, condition); |
| ... | ... | @@ -5030,7 +4981,7 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { |
| 5030 | 4981 | // Capture the state of register and stack allocation state so that we can revert to it. |
| 5031 | 4982 | const parent_next_stack_offset = self.next_stack_offset; |
| 5032 | 4983 | const parent_free_registers = self.register_manager.free_registers; |
| 5033 | | const parent_compare_flags_inst = self.compare_flags_inst; |
| 4984 | const parent_eflags_inst = self.eflags_inst; |
| 5034 | 4985 | var parent_stack = try self.stack.clone(self.gpa); |
| 5035 | 4986 | defer parent_stack.deinit(self.gpa); |
| 5036 | 4987 | const parent_registers = self.register_manager.registers; |
| ... | ... | @@ -5052,7 +5003,7 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { |
| 5052 | 5003 | defer saved_case_branch.deinit(self.gpa); |
| 5053 | 5004 | |
| 5054 | 5005 | self.register_manager.registers = parent_registers; |
| 5055 | | self.compare_flags_inst = parent_compare_flags_inst; |
| 5006 | self.eflags_inst = parent_eflags_inst; |
| 5056 | 5007 | self.stack.deinit(self.gpa); |
| 5057 | 5008 | self.stack = parent_stack; |
| 5058 | 5009 | parent_stack = .{}; |
| ... | ... | @@ -5092,7 +5043,15 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { |
| 5092 | 5043 | |
| 5093 | 5044 | fn performReloc(self: *Self, reloc: Mir.Inst.Index) !void { |
| 5094 | 5045 | const next_inst = @intCast(u32, self.mir_instructions.len); |
| 5095 | | self.mir_instructions.items(.data)[reloc].inst = next_inst; |
| 5046 | switch (self.mir_instructions.items(.tag)[reloc]) { |
| 5047 | .cond_jmp => { |
| 5048 | self.mir_instructions.items(.data)[reloc].inst_cc.inst = next_inst; |
| 5049 | }, |
| 5050 | .jmp => { |
| 5051 | self.mir_instructions.items(.data)[reloc].inst = next_inst; |
| 5052 | }, |
| 5053 | else => unreachable, |
| 5054 | } |
| 5096 | 5055 | } |
| 5097 | 5056 | |
| 5098 | 5057 | fn airBr(self: *Self, inst: Air.Inst.Index) !void { |
| ... | ... | @@ -5111,7 +5070,7 @@ fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Ref) !void { |
| 5111 | 5070 | block_data.mcv = switch (operand_mcv) { |
| 5112 | 5071 | .none, .dead, .unreach => unreachable, |
| 5113 | 5072 | .register, .stack_offset, .memory => operand_mcv, |
| 5114 | | .compare_flags_signed, .compare_flags_unsigned, .immediate => blk: { |
| 5073 | .eflags, .immediate => blk: { |
| 5115 | 5074 | const new_mcv = try self.allocRegOrMem(block, true); |
| 5116 | 5075 | try self.setRegOrMem(self.air.typeOfIndex(block), new_mcv, operand_mcv); |
| 5117 | 5076 | break :blk new_mcv; |
| ... | ... | @@ -5335,9 +5294,7 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE |
| 5335 | 5294 | .register_overflow_unsigned, |
| 5336 | 5295 | .register_overflow_signed, |
| 5337 | 5296 | => return self.fail("TODO genSetStackArg for register with overflow bit", .{}), |
| 5338 | | .compare_flags_unsigned, |
| 5339 | | .compare_flags_signed, |
| 5340 | | => { |
| 5297 | .eflags => { |
| 5341 | 5298 | const reg = try self.copyToTmpRegister(ty, mcv); |
| 5342 | 5299 | return self.genSetStackArg(ty, stack_offset, .{ .register = reg }); |
| 5343 | 5300 | }, |
| ... | ... | @@ -5484,18 +5441,17 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl |
| 5484 | 5441 | const overflow_bit_ty = ty.structFieldType(1); |
| 5485 | 5442 | const overflow_bit_offset = ty.structFieldOffset(1, self.target.*); |
| 5486 | 5443 | 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, |
| 5444 | const cc: Condition = switch (mcv) { |
| 5445 | .register_overflow_unsigned => .c, |
| 5446 | .register_overflow_signed => .o, |
| 5490 | 5447 | else => unreachable, |
| 5491 | 5448 | }; |
| 5492 | 5449 | _ = try self.addInst(.{ |
| 5493 | | .tag = .cond_set_byte_overflow, |
| 5450 | .tag = .cond_set_byte, |
| 5494 | 5451 | .ops = Mir.Inst.Ops.encode(.{ |
| 5495 | 5452 | .reg1 = tmp_reg.to8(), |
| 5496 | | .flags = flags, |
| 5497 | 5453 | }), |
| 5498 | | .data = undefined, |
| 5454 | .data = .{ .cc = cc }, |
| 5499 | 5455 | }); |
| 5500 | 5456 | |
| 5501 | 5457 | return self.genSetStack( |
| ... | ... | @@ -5505,9 +5461,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl |
| 5505 | 5461 | .{}, |
| 5506 | 5462 | ); |
| 5507 | 5463 | }, |
| 5508 | | .compare_flags_unsigned, |
| 5509 | | .compare_flags_signed, |
| 5510 | | => { |
| 5464 | .eflags => { |
| 5511 | 5465 | const reg = try self.copyToTmpRegister(ty, mcv); |
| 5512 | 5466 | return self.genSetStack(ty, stack_offset, .{ .register = reg }, opts); |
| 5513 | 5467 | }, |
| ... | ... | @@ -5832,9 +5786,12 @@ fn genInlineMemcpy( |
| 5832 | 5786 | |
| 5833 | 5787 | // je end |
| 5834 | 5788 | const loop_reloc = try self.addInst(.{ |
| 5835 | | .tag = .cond_jmp_eq_ne, |
| 5836 | | .ops = Mir.Inst.Ops.encode(.{ .flags = 0b01 }), |
| 5837 | | .data = .{ .inst = undefined }, |
| 5789 | .tag = .cond_jmp, |
| 5790 | .ops = Mir.Inst.Ops.encode(.{}), |
| 5791 | .data = .{ .inst_cc = .{ |
| 5792 | .inst = undefined, |
| 5793 | .cc = .e, |
| 5794 | } }, |
| 5838 | 5795 | }); |
| 5839 | 5796 | |
| 5840 | 5797 | // mov tmp, [addr + rcx] |
| ... | ... | @@ -5950,9 +5907,12 @@ fn genInlineMemset( |
| 5950 | 5907 | |
| 5951 | 5908 | // je end |
| 5952 | 5909 | const loop_reloc = try self.addInst(.{ |
| 5953 | | .tag = .cond_jmp_eq_ne, |
| 5954 | | .ops = Mir.Inst.Ops.encode(.{ .flags = 0b01 }), |
| 5955 | | .data = .{ .inst = undefined }, |
| 5910 | .tag = .cond_jmp, |
| 5911 | .ops = Mir.Inst.Ops.encode(.{}), |
| 5912 | .data = .{ .inst_cc = .{ |
| 5913 | .inst = undefined, |
| 5914 | .cc = .e, |
| 5915 | } }, |
| 5956 | 5916 | }); |
| 5957 | 5917 | |
| 5958 | 5918 | switch (value) { |
| ... | ... | @@ -6025,31 +5985,13 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 6025 | 5985 | else => unreachable, |
| 6026 | 5986 | } |
| 6027 | 5987 | }, |
| 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 | | }; |
| 5988 | .eflags => |cc| { |
| 6046 | 5989 | _ = try self.addInst(.{ |
| 6047 | | .tag = tag, |
| 5990 | .tag = .cond_set_byte, |
| 6048 | 5991 | .ops = Mir.Inst.Ops.encode(.{ |
| 6049 | 5992 | .reg1 = reg.to8(), |
| 6050 | | .flags = flags, |
| 6051 | 5993 | }), |
| 6052 | | .data = undefined, |
| 5994 | .data = .{ .cc = cc }, |
| 6053 | 5995 | }); |
| 6054 | 5996 | }, |
| 6055 | 5997 | .immediate => |x| { |