| ... | @@ -507,8 +507,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { | ... | @@ -507,8 +507,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 507 | .mul => @panic("TODO try self.airMul(inst)"), | 507 | .mul => @panic("TODO try self.airMul(inst)"), |
| 508 | .mulwrap => @panic("TODO try self.airMulWrap(inst)"), | 508 | .mulwrap => @panic("TODO try self.airMulWrap(inst)"), |
| 509 | .mul_sat => @panic("TODO try self.airMulSat(inst)"), | 509 | .mul_sat => @panic("TODO try self.airMulSat(inst)"), |
| 510 | .rem => @panic("TODO try self.airRem(inst)"), | 510 | .rem => try self.airRem(inst), |
| 511 | .mod => @panic("TODO try self.airMod(inst)"), | 511 | .mod => try self.airMod(inst), |
| 512 | .shl, .shl_exact => @panic("TODO try self.airShl(inst)"), | 512 | .shl, .shl_exact => @panic("TODO try self.airShl(inst)"), |
| 513 | .shl_sat => @panic("TODO try self.airShlSat(inst)"), | 513 | .shl_sat => @panic("TODO try self.airShlSat(inst)"), |
| 514 | .min => @panic("TODO try self.airMin(inst)"), | 514 | .min => @panic("TODO try self.airMin(inst)"), |
| ... | @@ -1602,6 +1602,144 @@ fn airMemset(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1602,6 +1602,144 @@ fn airMemset(self: *Self, inst: Air.Inst.Index) !void { |
| 1602 | return self.fail("TODO implement airMemset for {}", .{self.target.cpu.arch}); | 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 | fn airNot(self: *Self, inst: Air.Inst.Index) !void { | 1743 | fn airNot(self: *Self, inst: Air.Inst.Index) !void { |
| 1606 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 1744 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1607 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { | 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,6 +1842,27 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 1704 | return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); | 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 | fn airRet(self: *Self, inst: Air.Inst.Index) !void { | 1866 | fn airRet(self: *Self, inst: Air.Inst.Index) !void { |
| 1708 | const un_op = self.air.instructions.items(.data)[inst].un_op; | 1867 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 1709 | const operand = try self.resolveInst(un_op); | 1868 | const operand = try self.resolveInst(un_op); |
| ... | @@ -2077,7 +2236,10 @@ fn binOp( | ... | @@ -2077,7 +2236,10 @@ fn binOp( |
| 2077 | ) InnerError!MCValue { | 2236 | ) InnerError!MCValue { |
| 2078 | const mod = self.bin_file.options.module.?; | 2237 | const mod = self.bin_file.options.module.?; |
| 2079 | switch (tag) { | 2238 | switch (tag) { |
| 2080 | .add, .cmp_eq => { | 2239 | .add, |
| | 2240 | .sub, |
| | 2241 | .cmp_eq, |
| | 2242 | => { |
| 2081 | switch (lhs_ty.zigTypeTag()) { | 2243 | switch (lhs_ty.zigTypeTag()) { |
| 2082 | .Float => return self.fail("TODO binary operations on floats", .{}), | 2244 | .Float => return self.fail("TODO binary operations on floats", .{}), |
| 2083 | .Vector => return self.fail("TODO binary operations on vectors", .{}), | 2245 | .Vector => return self.fail("TODO binary operations on vectors", .{}), |
| ... | @@ -2103,6 +2265,7 @@ fn binOp( | ... | @@ -2103,6 +2265,7 @@ fn binOp( |
| 2103 | | 2265 | |
| 2104 | const mir_tag: Mir.Inst.Tag = switch (tag) { | 2266 | const mir_tag: Mir.Inst.Tag = switch (tag) { |
| 2105 | .add => .add, | 2267 | .add => .add, |
| | 2268 | .sub => .sub, |
| 2106 | .cmp_eq => .cmp, | 2269 | .cmp_eq => .cmp, |
| 2107 | else => unreachable, | 2270 | else => unreachable, |
| 2108 | }; | 2271 | }; |
| ... | @@ -2124,6 +2287,39 @@ fn binOp( | ... | @@ -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 | .mul => { | 2323 | .mul => { |
| 2128 | switch (lhs_ty.zigTypeTag()) { | 2324 | switch (lhs_ty.zigTypeTag()) { |
| 2129 | .Vector => return self.fail("TODO binary operations on vectors", .{}), | 2325 | .Vector => return self.fail("TODO binary operations on vectors", .{}), |
| ... | @@ -2382,6 +2578,9 @@ fn binOpImmediate( | ... | @@ -2382,6 +2578,9 @@ fn binOpImmediate( |
| 2382 | .xor, | 2578 | .xor, |
| 2383 | .xnor, | 2579 | .xnor, |
| 2384 | .mulx, | 2580 | .mulx, |
| | 2581 | .sdivx, |
| | 2582 | .udivx, |
| | 2583 | .sub, |
| 2385 | .subcc, | 2584 | .subcc, |
| 2386 | => .{ | 2585 | => .{ |
| 2387 | .arithmetic_3op = .{ | 2586 | .arithmetic_3op = .{ |
| ... | @@ -2503,6 +2702,9 @@ fn binOpRegister( | ... | @@ -2503,6 +2702,9 @@ fn binOpRegister( |
| 2503 | .xor, | 2702 | .xor, |
| 2504 | .xnor, | 2703 | .xnor, |
| 2505 | .mulx, | 2704 | .mulx, |
| | 2705 | .sdivx, |
| | 2706 | .udivx, |
| | 2707 | .sub, |
| 2506 | .subcc, | 2708 | .subcc, |
| 2507 | => .{ | 2709 | => .{ |
| 2508 | .arithmetic_3op = .{ | 2710 | .arithmetic_3op = .{ |