| ... | ... | @@ -595,8 +595,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 595 | 595 | .mul => try self.airBinOp(inst), |
| 596 | 596 | .mulwrap => try self.airBinOp(inst), |
| 597 | 597 | .mul_sat => try self.airMulSat(inst), |
| 598 | | .rem => try self.airRem(inst), |
| 599 | | .mod => try self.airMod(inst), |
| 598 | .rem => try self.airBinOp(inst), |
| 599 | .mod => try self.airBinOp(inst), |
| 600 | 600 | .shl, .shl_exact => try self.airShl(inst), |
| 601 | 601 | .shl_sat => try self.airShlSat(inst), |
| 602 | 602 | .min => try self.airMin(inst), |
| ... | ... | @@ -1539,6 +1539,7 @@ fn genIntMulDivOpMir( |
| 1539 | 1539 | } |
| 1540 | 1540 | } |
| 1541 | 1541 | |
| 1542 | /// Always returns a register. |
| 1542 | 1543 | /// Clobbers .rax and .rdx registers. |
| 1543 | 1544 | fn genInlineIntDivFloor(self: *Self, ty: Type, lhs: MCValue, rhs: MCValue) !MCValue { |
| 1544 | 1545 | const signedness = ty.intInfo(self.target.*).signedness; |
| ... | ... | @@ -1687,86 +1688,6 @@ fn airDiv(self: *Self, inst: Air.Inst.Index) !void { |
| 1687 | 1688 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1688 | 1689 | } |
| 1689 | 1690 | |
| 1690 | | fn airRem(self: *Self, inst: Air.Inst.Index) !void { |
| 1691 | | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1692 | | |
| 1693 | | if (self.liveness.isUnused(inst)) { |
| 1694 | | return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1695 | | } |
| 1696 | | const ty = self.air.typeOfIndex(inst); |
| 1697 | | if (ty.zigTypeTag() != .Int) { |
| 1698 | | return self.fail("TODO implement .rem for operands of dst type {}", .{ty.zigTypeTag()}); |
| 1699 | | } |
| 1700 | | // Spill .rax and .rdx upfront to ensure we don't spill the operands too late. |
| 1701 | | try self.register_manager.getReg(.rax, null); |
| 1702 | | try self.register_manager.getReg(.rdx, inst); |
| 1703 | | const reg_locks = self.register_manager.lockRegsAssumeUnused(2, .{ .rax, .rdx }); |
| 1704 | | defer for (reg_locks) |reg| { |
| 1705 | | self.register_manager.unlockReg(reg); |
| 1706 | | }; |
| 1707 | | |
| 1708 | | const lhs = try self.resolveInst(bin_op.lhs); |
| 1709 | | const rhs = try self.resolveInst(bin_op.rhs); |
| 1710 | | |
| 1711 | | const signedness = ty.intInfo(self.target.*).signedness; |
| 1712 | | try self.genIntMulDivOpMir(switch (signedness) { |
| 1713 | | .signed => .idiv, |
| 1714 | | .unsigned => .div, |
| 1715 | | }, ty, signedness, lhs, rhs); |
| 1716 | | |
| 1717 | | const result: MCValue = .{ .register = .rdx }; |
| 1718 | | |
| 1719 | | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1720 | | } |
| 1721 | | |
| 1722 | | fn airMod(self: *Self, inst: Air.Inst.Index) !void { |
| 1723 | | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1724 | | |
| 1725 | | if (self.liveness.isUnused(inst)) { |
| 1726 | | return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1727 | | } |
| 1728 | | |
| 1729 | | const ty = self.air.typeOfIndex(inst); |
| 1730 | | if (ty.zigTypeTag() != .Int) { |
| 1731 | | return self.fail("TODO implement .mod for operands of dst type {}", .{ty.zigTypeTag()}); |
| 1732 | | } |
| 1733 | | const signedness = ty.intInfo(self.target.*).signedness; |
| 1734 | | |
| 1735 | | // Spill .rax and .rdx upfront to ensure we don't spill the operands too late. |
| 1736 | | try self.register_manager.getReg(.rax, null); |
| 1737 | | try self.register_manager.getReg(.rdx, if (signedness == .unsigned) inst else null); |
| 1738 | | const reg_locks = self.register_manager.lockRegsAssumeUnused(2, .{ .rax, .rdx }); |
| 1739 | | defer for (reg_locks) |reg| { |
| 1740 | | self.register_manager.unlockReg(reg); |
| 1741 | | }; |
| 1742 | | |
| 1743 | | const lhs = try self.resolveInst(bin_op.lhs); |
| 1744 | | const rhs = try self.resolveInst(bin_op.rhs); |
| 1745 | | |
| 1746 | | const result: MCValue = result: { |
| 1747 | | switch (signedness) { |
| 1748 | | .unsigned => { |
| 1749 | | try self.genIntMulDivOpMir(switch (signedness) { |
| 1750 | | .signed => .idiv, |
| 1751 | | .unsigned => .div, |
| 1752 | | }, ty, signedness, lhs, rhs); |
| 1753 | | break :result MCValue{ .register = .rdx }; |
| 1754 | | }, |
| 1755 | | .signed => { |
| 1756 | | const div_floor = try self.genInlineIntDivFloor(ty, lhs, rhs); |
| 1757 | | try self.genIntMulComplexOpMir(ty, div_floor, rhs); |
| 1758 | | |
| 1759 | | const result = try self.copyToRegisterWithInstTracking(inst, ty, lhs); |
| 1760 | | try self.genBinOpMir(.sub, ty, result, div_floor); |
| 1761 | | |
| 1762 | | break :result result; |
| 1763 | | }, |
| 1764 | | } |
| 1765 | | }; |
| 1766 | | |
| 1767 | | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1768 | | } |
| 1769 | | |
| 1770 | 1691 | fn airShl(self: *Self, inst: Air.Inst.Index) !void { |
| 1771 | 1692 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1772 | 1693 | if (self.liveness.isUnused(inst)) { |
| ... | ... | @@ -3234,12 +3155,18 @@ fn genBinOp( |
| 3234 | 3155 | .subwrap, |
| 3235 | 3156 | .mul, |
| 3236 | 3157 | .mulwrap, |
| 3158 | .div_exact, |
| 3159 | .div_trunc, |
| 3160 | .rem, |
| 3161 | .mod, |
| 3237 | 3162 | .shl, |
| 3238 | 3163 | .shr, |
| 3239 | 3164 | .ptr_add, |
| 3240 | 3165 | .ptr_sub, |
| 3241 | 3166 | => false, |
| 3242 | 3167 | |
| 3168 | .div_float => return self.fail("TODO implement genBinOp for {}", .{tag}), |
| 3169 | |
| 3243 | 3170 | else => unreachable, |
| 3244 | 3171 | }; |
| 3245 | 3172 | const dst_ty = self.air.typeOf(op_lhs); |
| ... | ... | @@ -3278,6 +3205,57 @@ fn genBinOp( |
| 3278 | 3205 | .unsigned => MCValue{ .register = registerAlias(.rax, @intCast(u32, dst_ty.abiSize(self.target.*))) }, |
| 3279 | 3206 | }; |
| 3280 | 3207 | }, |
| 3208 | .mod, |
| 3209 | .rem, |
| 3210 | => { |
| 3211 | const int_info = dst_ty.intInfo(self.target.*); |
| 3212 | const track_inst_rdx: ?Air.Inst.Index = switch (tag) { |
| 3213 | .mod => if (int_info.signedness == .unsigned) maybe_inst else null, |
| 3214 | .rem => maybe_inst, |
| 3215 | else => unreachable, |
| 3216 | }; |
| 3217 | |
| 3218 | // Spill .rax and .rdx upfront to ensure we don't spill the operands too late. |
| 3219 | try self.register_manager.getReg(.rax, null); |
| 3220 | try self.register_manager.getReg(.rdx, track_inst_rdx); |
| 3221 | const reg_locks = self.register_manager.lockRegsAssumeUnused(2, .{ .rax, .rdx }); |
| 3222 | defer for (reg_locks) |reg| { |
| 3223 | self.register_manager.unlockReg(reg); |
| 3224 | }; |
| 3225 | |
| 3226 | const lhs = try self.resolveInst(op_lhs); |
| 3227 | const rhs = try self.resolveInst(op_rhs); |
| 3228 | |
| 3229 | switch (int_info.signedness) { |
| 3230 | .signed => { |
| 3231 | switch (tag) { |
| 3232 | .rem => { |
| 3233 | try self.genIntMulDivOpMir(.idiv, dst_ty, .signed, lhs, rhs); |
| 3234 | return MCValue{ .register = .rdx }; |
| 3235 | }, |
| 3236 | .mod => { |
| 3237 | const div_floor = try self.genInlineIntDivFloor(dst_ty, lhs, rhs); |
| 3238 | try self.genIntMulComplexOpMir(dst_ty, div_floor, rhs); |
| 3239 | const div_floor_lock = self.register_manager.lockReg(div_floor.register); |
| 3240 | defer if (div_floor_lock) |lock| self.register_manager.unlockReg(lock); |
| 3241 | |
| 3242 | const result: MCValue = if (maybe_inst) |inst| |
| 3243 | try self.copyToRegisterWithInstTracking(inst, dst_ty, lhs) |
| 3244 | else |
| 3245 | MCValue{ .register = try self.copyToTmpRegister(dst_ty, lhs) }; |
| 3246 | try self.genBinOpMir(.sub, dst_ty, result, div_floor); |
| 3247 | |
| 3248 | return result; |
| 3249 | }, |
| 3250 | else => unreachable, |
| 3251 | } |
| 3252 | }, |
| 3253 | .unsigned => { |
| 3254 | try self.genIntMulDivOpMir(.div, dst_ty, .unsigned, lhs, rhs); |
| 3255 | return MCValue{ .register = .rdx }; |
| 3256 | }, |
| 3257 | } |
| 3258 | }, |
| 3281 | 3259 | else => {}, |
| 3282 | 3260 | } |
| 3283 | 3261 | |