| ... | ... | @@ -128,12 +128,8 @@ pub const MCValue = union(enum) { |
| 128 | 128 | immediate: u64, |
| 129 | 129 | /// The value is in a GP register. |
| 130 | 130 | register: Register, |
| 131 | | /// The value is a tuple { wrapped, overflow } where wrapped value is stored in the GP register, |
| 132 | | /// and the operation is an unsigned operation. |
| 133 | | register_overflow_unsigned: Register, |
| 134 | | /// The value is a tuple { wrapped, overflow } where wrapped value is stored in the GP register, |
| 135 | | /// and the operation is a signed operation. |
| 136 | | 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 }, |
| 137 | 133 | /// The value is in memory at a hard-coded address. |
| 138 | 134 | /// If the type is a pointer, it means the pointer address is at this memory location. |
| 139 | 135 | memory: u64, |
| ... | ... | @@ -183,8 +179,7 @@ pub const MCValue = union(enum) { |
| 183 | 179 | .eflags, |
| 184 | 180 | .ptr_stack_offset, |
| 185 | 181 | .undef, |
| 186 | | .register_overflow_unsigned, |
| 187 | | .register_overflow_signed, |
| 182 | .register_overflow, |
| 188 | 183 | => false, |
| 189 | 184 | |
| 190 | 185 | .register, |
| ... | ... | @@ -774,8 +769,8 @@ fn processDeath(self: *Self, inst: Air.Inst.Index) void { |
| 774 | 769 | .register => |reg| { |
| 775 | 770 | self.register_manager.freeReg(reg.to64()); |
| 776 | 771 | }, |
| 777 | | .register_overflow_signed, .register_overflow_unsigned => |reg| { |
| 778 | | self.register_manager.freeReg(reg.to64()); |
| 772 | .register_overflow => |ro| { |
| 773 | self.register_manager.freeReg(ro.reg.to64()); |
| 779 | 774 | self.eflags_inst = null; |
| 780 | 775 | }, |
| 781 | 776 | .eflags => { |
| ... | ... | @@ -809,20 +804,22 @@ fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Live |
| 809 | 804 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; |
| 810 | 805 | branch.inst_table.putAssumeCapacityNoClobber(inst, result); |
| 811 | 806 | |
| 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. |
| 812 | 812 | switch (result) { |
| 813 | | .register, |
| 814 | | .register_overflow_signed, |
| 815 | | .register_overflow_unsigned, |
| 816 | | => |reg| { |
| 817 | | // In some cases (such as bitcast), an operand |
| 818 | | // may be the same MCValue as the result. If |
| 819 | | // that operand died and was a register, it |
| 820 | | // was freed by processDeath. We have to |
| 821 | | // "re-allocate" the register. |
| 813 | .register => |reg| { |
| 822 | 814 | if (self.register_manager.isRegFree(reg)) { |
| 823 | 815 | self.register_manager.getRegAssumeFree(reg, inst); |
| 824 | 816 | } |
| 825 | 817 | }, |
| 818 | .register_overflow => |ro| { |
| 819 | if (self.register_manager.isRegFree(ro.reg)) { |
| 820 | self.register_manager.getRegAssumeFree(ro.reg, inst); |
| 821 | } |
| 822 | }, |
| 826 | 823 | else => {}, |
| 827 | 824 | } |
| 828 | 825 | } |
| ... | ... | @@ -912,12 +909,12 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void |
| 912 | 909 | log.debug("spilling {d} to stack mcv {any}", .{ inst, stack_mcv }); |
| 913 | 910 | const reg_mcv = self.getResolvedInstValue(inst); |
| 914 | 911 | switch (reg_mcv) { |
| 915 | | .register, |
| 916 | | .register_overflow_unsigned, |
| 917 | | .register_overflow_signed, |
| 918 | | => |other| { |
| 912 | .register => |other| { |
| 919 | 913 | assert(reg.to64() == other.to64()); |
| 920 | 914 | }, |
| 915 | .register_overflow => |ro| { |
| 916 | assert(reg.to64() == ro.reg.to64()); |
| 917 | }, |
| 921 | 918 | else => {}, |
| 922 | 919 | } |
| 923 | 920 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; |
| ... | ... | @@ -929,9 +926,7 @@ pub fn spillEflagsIfOccupied(self: *Self) !void { |
| 929 | 926 | if (self.eflags_inst) |inst_to_save| { |
| 930 | 927 | const mcv = self.getResolvedInstValue(inst_to_save); |
| 931 | 928 | const new_mcv = switch (mcv) { |
| 932 | | .register_overflow_signed, |
| 933 | | .register_overflow_unsigned, |
| 934 | | => try self.allocRegOrMem(inst_to_save, false), |
| 929 | .register_overflow => try self.allocRegOrMem(inst_to_save, false), |
| 935 | 930 | .eflags => try self.allocRegOrMem(inst_to_save, true), |
| 936 | 931 | else => unreachable, |
| 937 | 932 | }; |
| ... | ... | @@ -947,9 +942,7 @@ pub fn spillEflagsIfOccupied(self: *Self) !void { |
| 947 | 942 | // TODO consolidate with register manager and spillInstruction |
| 948 | 943 | // this call should really belong in the register manager! |
| 949 | 944 | switch (mcv) { |
| 950 | | .register_overflow_signed, |
| 951 | | .register_overflow_unsigned, |
| 952 | | => |reg| self.register_manager.freeReg(reg), |
| 945 | .register_overflow => |ro| self.register_manager.freeReg(ro.reg), |
| 953 | 946 | else => {}, |
| 954 | 947 | } |
| 955 | 948 | } |
| ... | ... | @@ -1339,11 +1332,14 @@ fn airAddSubShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1339 | 1332 | if (math.isPowerOfTwo(int_info.bits) and int_info.bits >= 8) { |
| 1340 | 1333 | self.eflags_inst = inst; |
| 1341 | 1334 | |
| 1342 | | const result: MCValue = switch (int_info.signedness) { |
| 1343 | | .signed => .{ .register_overflow_signed = partial.register }, |
| 1344 | | .unsigned => .{ .register_overflow_unsigned = partial.register }, |
| 1335 | const cc: Condition = switch (int_info.signedness) { |
| 1336 | .unsigned => .c, |
| 1337 | .signed => .o, |
| 1345 | 1338 | }; |
| 1346 | | break :result result; |
| 1339 | break :result MCValue{ .register_overflow = .{ |
| 1340 | .reg = partial.register, |
| 1341 | .eflags = cc, |
| 1342 | } }; |
| 1347 | 1343 | } |
| 1348 | 1344 | |
| 1349 | 1345 | self.eflags_inst = null; |
| ... | ... | @@ -1460,10 +1456,14 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1460 | 1456 | const rhs = try self.resolveInst(bin_op.rhs); |
| 1461 | 1457 | |
| 1462 | 1458 | const partial = try self.genMulDivBinOp(.mul, null, ty, lhs, rhs); |
| 1463 | | break :result switch (int_info.signedness) { |
| 1464 | | .signed => MCValue{ .register_overflow_signed = partial.register }, |
| 1465 | | .unsigned => MCValue{ .register_overflow_unsigned = partial.register }, |
| 1459 | const cc: Condition = switch (int_info.signedness) { |
| 1460 | .unsigned => .c, |
| 1461 | .signed => .o, |
| 1466 | 1462 | }; |
| 1463 | break :result MCValue{ .register_overflow = .{ |
| 1464 | .reg = partial.register, |
| 1465 | .eflags = cc, |
| 1466 | } }; |
| 1467 | 1467 | } |
| 1468 | 1468 | |
| 1469 | 1469 | try self.spillEflagsIfOccupied(); |
| ... | ... | @@ -2486,8 +2486,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo |
| 2486 | 2486 | .unreach => unreachable, |
| 2487 | 2487 | .dead => unreachable, |
| 2488 | 2488 | .eflags => unreachable, |
| 2489 | | .register_overflow_unsigned => unreachable, |
| 2490 | | .register_overflow_signed => unreachable, |
| 2489 | .register_overflow => unreachable, |
| 2491 | 2490 | .immediate => |imm| { |
| 2492 | 2491 | try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm }); |
| 2493 | 2492 | }, |
| ... | ... | @@ -2610,8 +2609,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type |
| 2610 | 2609 | .unreach => unreachable, |
| 2611 | 2610 | .dead => unreachable, |
| 2612 | 2611 | .eflags => unreachable, |
| 2613 | | .register_overflow_unsigned => unreachable, |
| 2614 | | .register_overflow_signed => unreachable, |
| 2612 | .register_overflow => unreachable, |
| 2615 | 2613 | .immediate => |imm| { |
| 2616 | 2614 | try self.setRegOrMem(value_ty, .{ .memory = imm }, value); |
| 2617 | 2615 | }, |
| ... | ... | @@ -2997,31 +2995,24 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { |
| 2997 | 2995 | |
| 2998 | 2996 | break :result dst_mcv; |
| 2999 | 2997 | }, |
| 3000 | | .register_overflow_unsigned, |
| 3001 | | .register_overflow_signed, |
| 3002 | | => |reg| { |
| 2998 | .register_overflow => |ro| { |
| 3003 | 2999 | switch (index) { |
| 3004 | 3000 | 0 => { |
| 3005 | 3001 | // Get wrapped value for overflow operation. |
| 3006 | | break :result MCValue{ .register = reg }; |
| 3002 | break :result MCValue{ .register = ro.reg }; |
| 3007 | 3003 | }, |
| 3008 | 3004 | 1 => { |
| 3009 | 3005 | // Get overflow bit. |
| 3010 | | const reg_lock = self.register_manager.lockRegAssumeUnused(reg); |
| 3006 | const reg_lock = self.register_manager.lockRegAssumeUnused(ro.reg); |
| 3011 | 3007 | defer self.register_manager.unlockReg(reg_lock); |
| 3012 | 3008 | |
| 3013 | 3009 | const dst_reg = try self.register_manager.allocReg(inst, gp); |
| 3014 | | const cc: Condition = switch (mcv) { |
| 3015 | | .register_overflow_unsigned => .c, |
| 3016 | | .register_overflow_signed => .o, |
| 3017 | | else => unreachable, |
| 3018 | | }; |
| 3019 | 3010 | _ = try self.addInst(.{ |
| 3020 | 3011 | .tag = .cond_set_byte, |
| 3021 | 3012 | .ops = Mir.Inst.Ops.encode(.{ |
| 3022 | 3013 | .reg1 = dst_reg.to8(), |
| 3023 | 3014 | }), |
| 3024 | | .data = .{ .cc = cc }, |
| 3015 | .data = .{ .cc = ro.eflags }, |
| 3025 | 3016 | }); |
| 3026 | 3017 | break :result MCValue{ .register = dst_reg.to8() }; |
| 3027 | 3018 | }, |
| ... | ... | @@ -3449,15 +3440,13 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu |
| 3449 | 3440 | .undef => unreachable, |
| 3450 | 3441 | .dead, .unreach, .immediate => unreachable, |
| 3451 | 3442 | .eflags => unreachable, |
| 3452 | | .register_overflow_unsigned => unreachable, |
| 3453 | | .register_overflow_signed => unreachable, |
| 3443 | .register_overflow => unreachable, |
| 3454 | 3444 | .register => |dst_reg| { |
| 3455 | 3445 | switch (src_mcv) { |
| 3456 | 3446 | .none => unreachable, |
| 3457 | 3447 | .undef => unreachable, |
| 3458 | 3448 | .dead, .unreach => unreachable, |
| 3459 | | .register_overflow_unsigned => unreachable, |
| 3460 | | .register_overflow_signed => unreachable, |
| 3449 | .register_overflow => unreachable, |
| 3461 | 3450 | .ptr_stack_offset => { |
| 3462 | 3451 | const dst_reg_lock = self.register_manager.lockReg(dst_reg); |
| 3463 | 3452 | defer if (dst_reg_lock) |lock| self.register_manager.unlockReg(lock); |
| ... | ... | @@ -3564,8 +3553,7 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu |
| 3564 | 3553 | .none => unreachable, |
| 3565 | 3554 | .undef => unreachable, |
| 3566 | 3555 | .dead, .unreach => unreachable, |
| 3567 | | .register_overflow_unsigned => unreachable, |
| 3568 | | .register_overflow_signed => unreachable, |
| 3556 | .register_overflow => unreachable, |
| 3569 | 3557 | .register => |src_reg| { |
| 3570 | 3558 | _ = try self.addInst(.{ |
| 3571 | 3559 | .tag = mir_tag, |
| ... | ... | @@ -3640,16 +3628,14 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M |
| 3640 | 3628 | .dead, .unreach, .immediate => unreachable, |
| 3641 | 3629 | .eflags => unreachable, |
| 3642 | 3630 | .ptr_stack_offset => unreachable, |
| 3643 | | .register_overflow_unsigned => unreachable, |
| 3644 | | .register_overflow_signed => unreachable, |
| 3631 | .register_overflow => unreachable, |
| 3645 | 3632 | .register => |dst_reg| { |
| 3646 | 3633 | switch (src_mcv) { |
| 3647 | 3634 | .none => unreachable, |
| 3648 | 3635 | .undef => try self.genSetReg(dst_ty, dst_reg, .undef), |
| 3649 | 3636 | .dead, .unreach => unreachable, |
| 3650 | 3637 | .ptr_stack_offset => unreachable, |
| 3651 | | .register_overflow_unsigned => unreachable, |
| 3652 | | .register_overflow_signed => unreachable, |
| 3638 | .register_overflow => unreachable, |
| 3653 | 3639 | .register => |src_reg| { |
| 3654 | 3640 | // register, register |
| 3655 | 3641 | _ = try self.addInst(.{ |
| ... | ... | @@ -3708,8 +3694,7 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M |
| 3708 | 3694 | .undef => return self.genSetStack(dst_ty, off, .undef, .{}), |
| 3709 | 3695 | .dead, .unreach => unreachable, |
| 3710 | 3696 | .ptr_stack_offset => unreachable, |
| 3711 | | .register_overflow_unsigned => unreachable, |
| 3712 | | .register_overflow_signed => unreachable, |
| 3697 | .register_overflow => unreachable, |
| 3713 | 3698 | .register => |src_reg| { |
| 3714 | 3699 | // copy dst to a register |
| 3715 | 3700 | const dst_reg = try self.copyToTmpRegister(dst_ty, dst_mcv); |
| ... | ... | @@ -3915,8 +3900,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. |
| 3915 | 3900 | .got_load => unreachable, |
| 3916 | 3901 | .direct_load => unreachable, |
| 3917 | 3902 | .eflags => unreachable, |
| 3918 | | .register_overflow_signed => unreachable, |
| 3919 | | .register_overflow_unsigned => unreachable, |
| 3903 | .register_overflow => unreachable, |
| 3920 | 3904 | } |
| 3921 | 3905 | } |
| 3922 | 3906 | |
| ... | ... | @@ -5291,9 +5275,7 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE |
| 5291 | 5275 | .{ .dest_stack_base = .rsp }, |
| 5292 | 5276 | ); |
| 5293 | 5277 | }, |
| 5294 | | .register_overflow_unsigned, |
| 5295 | | .register_overflow_signed, |
| 5296 | | => return self.fail("TODO genSetStackArg for register with overflow bit", .{}), |
| 5278 | .register_overflow => return self.fail("TODO genSetStackArg for register with overflow bit", .{}), |
| 5297 | 5279 | .eflags => { |
| 5298 | 5280 | const reg = try self.copyToTmpRegister(ty, mcv); |
| 5299 | 5281 | return self.genSetStackArg(ty, stack_offset, .{ .register = reg }); |
| ... | ... | @@ -5429,29 +5411,22 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl |
| 5429 | 5411 | ), |
| 5430 | 5412 | } |
| 5431 | 5413 | }, |
| 5432 | | .register_overflow_unsigned, |
| 5433 | | .register_overflow_signed, |
| 5434 | | => |reg| { |
| 5435 | | const reg_lock = self.register_manager.lockReg(reg); |
| 5414 | .register_overflow => |ro| { |
| 5415 | const reg_lock = self.register_manager.lockReg(ro.reg); |
| 5436 | 5416 | defer if (reg_lock) |lock| self.register_manager.unlockReg(lock); |
| 5437 | 5417 | |
| 5438 | 5418 | const wrapped_ty = ty.structFieldType(0); |
| 5439 | | try self.genSetStack(wrapped_ty, stack_offset, .{ .register = reg }, .{}); |
| 5419 | try self.genSetStack(wrapped_ty, stack_offset, .{ .register = ro.reg }, .{}); |
| 5440 | 5420 | |
| 5441 | 5421 | const overflow_bit_ty = ty.structFieldType(1); |
| 5442 | 5422 | const overflow_bit_offset = ty.structFieldOffset(1, self.target.*); |
| 5443 | 5423 | const tmp_reg = try self.register_manager.allocReg(null, gp); |
| 5444 | | const cc: Condition = switch (mcv) { |
| 5445 | | .register_overflow_unsigned => .c, |
| 5446 | | .register_overflow_signed => .o, |
| 5447 | | else => unreachable, |
| 5448 | | }; |
| 5449 | 5424 | _ = try self.addInst(.{ |
| 5450 | 5425 | .tag = .cond_set_byte, |
| 5451 | 5426 | .ops = Mir.Inst.Ops.encode(.{ |
| 5452 | 5427 | .reg1 = tmp_reg.to8(), |
| 5453 | 5428 | }), |
| 5454 | | .data = .{ .cc = cc }, |
| 5429 | .data = .{ .cc = ro.eflags }, |
| 5455 | 5430 | }); |
| 5456 | 5431 | |
| 5457 | 5432 | return self.genSetStack( |
| ... | ... | @@ -5956,9 +5931,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 5956 | 5931 | const abi_size = @intCast(u32, ty.abiSize(self.target.*)); |
| 5957 | 5932 | switch (mcv) { |
| 5958 | 5933 | .dead => unreachable, |
| 5959 | | .register_overflow_unsigned, |
| 5960 | | .register_overflow_signed, |
| 5961 | | => unreachable, |
| 5934 | .register_overflow => unreachable, |
| 5962 | 5935 | .ptr_stack_offset => |off| { |
| 5963 | 5936 | if (off < std.math.minInt(i32) or off > std.math.maxInt(i32)) { |
| 5964 | 5937 | return self.fail("stack offset too large", .{}); |