| ... | ... | @@ -507,8 +507,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 507 | 507 | .mul => @panic("TODO try self.airMul(inst)"), |
| 508 | 508 | .mulwrap => @panic("TODO try self.airMulWrap(inst)"), |
| 509 | 509 | .mul_sat => @panic("TODO try self.airMulSat(inst)"), |
| 510 | | .rem => @panic("TODO try self.airRem(inst)"), |
| 511 | | .mod => @panic("TODO try self.airMod(inst)"), |
| 510 | .rem => try self.airRem(inst), |
| 511 | .mod => try self.airMod(inst), |
| 512 | 512 | .shl, .shl_exact => @panic("TODO try self.airShl(inst)"), |
| 513 | 513 | .shl_sat => @panic("TODO try self.airShlSat(inst)"), |
| 514 | 514 | .min => @panic("TODO try self.airMin(inst)"), |
| ... | ... | @@ -1602,6 +1602,144 @@ fn airMemset(self: *Self, inst: Air.Inst.Index) !void { |
| 1602 | 1602 | return self.fail("TODO implement airMemset for {}", .{self.target.cpu.arch}); |
| 1603 | 1603 | } |
| 1604 | 1604 | |
| 1605 | fn airMod(self: *Self, inst: Air.Inst.Index) !void { |
| 1606 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1607 | const lhs = try self.resolveInst(bin_op.lhs); |
| 1608 | const rhs = try self.resolveInst(bin_op.rhs); |
| 1609 | const lhs_ty = self.air.typeOf(bin_op.lhs); |
| 1610 | const rhs_ty = self.air.typeOf(bin_op.rhs); |
| 1611 | assert(lhs_ty.eql(rhs_ty, self.bin_file.options.module.?)); |
| 1612 | |
| 1613 | if (self.liveness.isUnused(inst)) |
| 1614 | return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1615 | |
| 1616 | // TODO add safety check |
| 1617 | |
| 1618 | // We use manual assembly emission to generate faster code |
| 1619 | // First, ensure lhs, rhs, rem, and added are in registers |
| 1620 | |
| 1621 | const lhs_is_register = lhs == .register; |
| 1622 | const rhs_is_register = rhs == .register; |
| 1623 | |
| 1624 | const lhs_reg = if (lhs_is_register) |
| 1625 | lhs.register |
| 1626 | else |
| 1627 | try self.register_manager.allocReg(null, gp); |
| 1628 | |
| 1629 | const lhs_lock = self.register_manager.lockReg(lhs_reg); |
| 1630 | defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg); |
| 1631 | |
| 1632 | const rhs_reg = if (rhs_is_register) |
| 1633 | rhs.register |
| 1634 | else |
| 1635 | try self.register_manager.allocReg(null, gp); |
| 1636 | const rhs_lock = self.register_manager.lockReg(rhs_reg); |
| 1637 | defer if (rhs_lock) |reg| self.register_manager.unlockReg(reg); |
| 1638 | |
| 1639 | if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); |
| 1640 | if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs); |
| 1641 | |
| 1642 | const regs = try self.register_manager.allocRegs(2, .{ null, null }, gp); |
| 1643 | const regs_locks = self.register_manager.lockRegsAssumeUnused(2, regs); |
| 1644 | defer for (regs_locks) |reg| { |
| 1645 | self.register_manager.unlockReg(reg); |
| 1646 | }; |
| 1647 | |
| 1648 | const add_reg = regs[0]; |
| 1649 | const mod_reg = regs[1]; |
| 1650 | |
| 1651 | // mod_reg = @rem(lhs_reg, rhs_reg) |
| 1652 | _ = try self.addInst(.{ |
| 1653 | .tag = .sdivx, |
| 1654 | .data = .{ |
| 1655 | .arithmetic_3op = .{ |
| 1656 | .is_imm = false, |
| 1657 | .rd = mod_reg, |
| 1658 | .rs1 = lhs_reg, |
| 1659 | .rs2_or_imm = .{ .rs2 = rhs_reg }, |
| 1660 | }, |
| 1661 | }, |
| 1662 | }); |
| 1663 | |
| 1664 | _ = try self.addInst(.{ |
| 1665 | .tag = .mulx, |
| 1666 | .data = .{ |
| 1667 | .arithmetic_3op = .{ |
| 1668 | .is_imm = false, |
| 1669 | .rd = mod_reg, |
| 1670 | .rs1 = mod_reg, |
| 1671 | .rs2_or_imm = .{ .rs2 = rhs_reg }, |
| 1672 | }, |
| 1673 | }, |
| 1674 | }); |
| 1675 | |
| 1676 | _ = try self.addInst(.{ |
| 1677 | .tag = .sub, |
| 1678 | .data = .{ |
| 1679 | .arithmetic_3op = .{ |
| 1680 | .is_imm = false, |
| 1681 | .rd = mod_reg, |
| 1682 | .rs1 = lhs_reg, |
| 1683 | .rs2_or_imm = .{ .rs2 = mod_reg }, |
| 1684 | }, |
| 1685 | }, |
| 1686 | }); |
| 1687 | |
| 1688 | // add_reg = mod_reg + rhs_reg |
| 1689 | _ = try self.addInst(.{ |
| 1690 | .tag = .add, |
| 1691 | .data = .{ |
| 1692 | .arithmetic_3op = .{ |
| 1693 | .is_imm = false, |
| 1694 | .rd = add_reg, |
| 1695 | .rs1 = mod_reg, |
| 1696 | .rs2_or_imm = .{ .rs2 = rhs_reg }, |
| 1697 | }, |
| 1698 | }, |
| 1699 | }); |
| 1700 | |
| 1701 | // if (add_reg == rhs_reg) add_reg = 0 |
| 1702 | _ = try self.addInst(.{ |
| 1703 | .tag = .cmp, |
| 1704 | .data = .{ |
| 1705 | .arithmetic_2op = .{ |
| 1706 | .is_imm = false, |
| 1707 | .rs1 = add_reg, |
| 1708 | .rs2_or_imm = .{ .rs2 = rhs_reg }, |
| 1709 | }, |
| 1710 | }, |
| 1711 | }); |
| 1712 | |
| 1713 | _ = try self.addInst(.{ |
| 1714 | .tag = .movcc, |
| 1715 | .data = .{ |
| 1716 | .conditional_move_int = .{ |
| 1717 | .is_imm = true, |
| 1718 | .ccr = .xcc, |
| 1719 | .cond = .{ .icond = .eq }, |
| 1720 | .rd = add_reg, |
| 1721 | .rs2_or_imm = .{ .imm = 0 }, |
| 1722 | }, |
| 1723 | }, |
| 1724 | }); |
| 1725 | |
| 1726 | // if (lhs_reg < 0) mod_reg = add_reg |
| 1727 | _ = try self.addInst(.{ |
| 1728 | .tag = .movr, |
| 1729 | .data = .{ |
| 1730 | .conditional_move_reg = .{ |
| 1731 | .is_imm = false, |
| 1732 | .cond = .lt_zero, |
| 1733 | .rd = mod_reg, |
| 1734 | .rs1 = lhs_reg, |
| 1735 | .rs2_or_imm = .{ .rs2 = add_reg }, |
| 1736 | }, |
| 1737 | }, |
| 1738 | }); |
| 1739 | |
| 1740 | return self.finishAir(inst, .{ .register = mod_reg }, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1741 | } |
| 1742 | |
| 1605 | 1743 | fn airNot(self: *Self, inst: Air.Inst.Index) !void { |
| 1606 | 1744 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1607 | 1745 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| ... | ... | @@ -1704,6 +1842,27 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 1704 | 1842 | return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); |
| 1705 | 1843 | } |
| 1706 | 1844 | |
| 1845 | fn airRem(self: *Self, inst: Air.Inst.Index) !void { |
| 1846 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1847 | const lhs = try self.resolveInst(bin_op.lhs); |
| 1848 | const rhs = try self.resolveInst(bin_op.rhs); |
| 1849 | const lhs_ty = self.air.typeOf(bin_op.lhs); |
| 1850 | const rhs_ty = self.air.typeOf(bin_op.rhs); |
| 1851 | |
| 1852 | // TODO add safety check |
| 1853 | |
| 1854 | // result = lhs - @divTrunc(lhs, rhs) * rhs |
| 1855 | const result: MCValue = if (self.liveness.isUnused(inst)) blk: { |
| 1856 | break :blk .dead; |
| 1857 | } else blk: { |
| 1858 | const tmp0 = try self.binOp(.div_trunc, lhs, rhs, lhs_ty, rhs_ty, null); |
| 1859 | const tmp1 = try self.binOp(.mul, tmp0, rhs, lhs_ty, rhs_ty, null); |
| 1860 | break :blk try self.binOp(.sub, lhs, tmp1, lhs_ty, rhs_ty, null); |
| 1861 | }; |
| 1862 | |
| 1863 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1864 | } |
| 1865 | |
| 1707 | 1866 | fn airRet(self: *Self, inst: Air.Inst.Index) !void { |
| 1708 | 1867 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 1709 | 1868 | const operand = try self.resolveInst(un_op); |
| ... | ... | @@ -2077,7 +2236,10 @@ fn binOp( |
| 2077 | 2236 | ) InnerError!MCValue { |
| 2078 | 2237 | const mod = self.bin_file.options.module.?; |
| 2079 | 2238 | switch (tag) { |
| 2080 | | .add, .cmp_eq => { |
| 2239 | .add, |
| 2240 | .sub, |
| 2241 | .cmp_eq, |
| 2242 | => { |
| 2081 | 2243 | switch (lhs_ty.zigTypeTag()) { |
| 2082 | 2244 | .Float => return self.fail("TODO binary operations on floats", .{}), |
| 2083 | 2245 | .Vector => return self.fail("TODO binary operations on vectors", .{}), |
| ... | ... | @@ -2103,6 +2265,7 @@ fn binOp( |
| 2103 | 2265 | |
| 2104 | 2266 | const mir_tag: Mir.Inst.Tag = switch (tag) { |
| 2105 | 2267 | .add => .add, |
| 2268 | .sub => .sub, |
| 2106 | 2269 | .cmp_eq => .cmp, |
| 2107 | 2270 | else => unreachable, |
| 2108 | 2271 | }; |
| ... | ... | @@ -2124,6 +2287,39 @@ fn binOp( |
| 2124 | 2287 | } |
| 2125 | 2288 | }, |
| 2126 | 2289 | |
| 2290 | .div_trunc => { |
| 2291 | switch (lhs_ty.zigTypeTag()) { |
| 2292 | .Vector => return self.fail("TODO binary operations on vectors", .{}), |
| 2293 | .Int => { |
| 2294 | assert(lhs_ty.eql(rhs_ty, mod)); |
| 2295 | const int_info = lhs_ty.intInfo(self.target.*); |
| 2296 | if (int_info.bits <= 64) { |
| 2297 | const rhs_immediate_ok = switch (tag) { |
| 2298 | .div_trunc => rhs == .immediate and rhs.immediate <= std.math.maxInt(u12), |
| 2299 | else => unreachable, |
| 2300 | }; |
| 2301 | |
| 2302 | const mir_tag: Mir.Inst.Tag = switch (tag) { |
| 2303 | .div_trunc => switch (int_info.signedness) { |
| 2304 | .signed => Mir.Inst.Tag.sdivx, |
| 2305 | .unsigned => Mir.Inst.Tag.udivx, |
| 2306 | }, |
| 2307 | else => unreachable, |
| 2308 | }; |
| 2309 | |
| 2310 | if (rhs_immediate_ok) { |
| 2311 | return try self.binOpImmediate(mir_tag, lhs, rhs, lhs_ty, true, metadata); |
| 2312 | } else { |
| 2313 | return try self.binOpRegister(mir_tag, lhs, rhs, lhs_ty, rhs_ty, metadata); |
| 2314 | } |
| 2315 | } else { |
| 2316 | return self.fail("TODO binary operations on int with bits > 64", .{}); |
| 2317 | } |
| 2318 | }, |
| 2319 | else => unreachable, |
| 2320 | } |
| 2321 | }, |
| 2322 | |
| 2127 | 2323 | .mul => { |
| 2128 | 2324 | switch (lhs_ty.zigTypeTag()) { |
| 2129 | 2325 | .Vector => return self.fail("TODO binary operations on vectors", .{}), |
| ... | ... | @@ -2382,6 +2578,9 @@ fn binOpImmediate( |
| 2382 | 2578 | .xor, |
| 2383 | 2579 | .xnor, |
| 2384 | 2580 | .mulx, |
| 2581 | .sdivx, |
| 2582 | .udivx, |
| 2583 | .sub, |
| 2385 | 2584 | .subcc, |
| 2386 | 2585 | => .{ |
| 2387 | 2586 | .arithmetic_3op = .{ |
| ... | ... | @@ -2503,6 +2702,9 @@ fn binOpRegister( |
| 2503 | 2702 | .xor, |
| 2504 | 2703 | .xnor, |
| 2505 | 2704 | .mulx, |
| 2705 | .sdivx, |
| 2706 | .udivx, |
| 2707 | .sub, |
| 2506 | 2708 | .subcc, |
| 2507 | 2709 | => .{ |
| 2508 | 2710 | .arithmetic_3op = .{ |