| ... | ... | @@ -597,7 +597,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 597 | 597 | .mul_sat => try self.airMulSat(inst), |
| 598 | 598 | .rem => try self.airMulDivBinOp(inst), |
| 599 | 599 | .mod => try self.airMulDivBinOp(inst), |
| 600 | | .shl, .shl_exact => try self.airShl(inst), |
| 600 | .shl, .shl_exact => try self.airShlShrBinOp(inst), |
| 601 | 601 | .shl_sat => try self.airShlSat(inst), |
| 602 | 602 | .min => try self.airMin(inst), |
| 603 | 603 | .max => try self.airMax(inst), |
| ... | ... | @@ -643,7 +643,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 643 | 643 | .bit_and => try self.airBinOp(inst), |
| 644 | 644 | .bit_or => try self.airBinOp(inst), |
| 645 | 645 | .xor => try self.airBinOp(inst), |
| 646 | | .shr, .shr_exact => try self.airShr(inst), |
| 646 | .shr, .shr_exact => try self.airShlShrBinOp(inst), |
| 647 | 647 | |
| 648 | 648 | .alloc => try self.airAlloc(inst), |
| 649 | 649 | .ret_ptr => try self.airRetPtr(inst), |
| ... | ... | @@ -1652,64 +1652,22 @@ fn genInlineIntDivFloor(self: *Self, ty: Type, lhs: MCValue, rhs: MCValue) !MCVa |
| 1652 | 1652 | return MCValue{ .register = divisor }; |
| 1653 | 1653 | } |
| 1654 | 1654 | |
| 1655 | | fn airShl(self: *Self, inst: Air.Inst.Index) !void { |
| 1655 | fn airShlShrBinOp(self: *Self, inst: Air.Inst.Index) !void { |
| 1656 | 1656 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1657 | |
| 1657 | 1658 | if (self.liveness.isUnused(inst)) { |
| 1658 | 1659 | return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1659 | 1660 | } |
| 1660 | 1661 | |
| 1661 | | const ty = self.air.typeOfIndex(inst); |
| 1662 | | |
| 1663 | 1662 | const tag = self.air.instructions.items(.tag)[inst]; |
| 1664 | | switch (tag) { |
| 1665 | | .shl_exact => return self.fail("TODO implement {} for type {}", .{ tag, ty.fmtDebug() }), |
| 1666 | | .shl => {}, |
| 1667 | | else => unreachable, |
| 1668 | | } |
| 1669 | | |
| 1670 | | if (ty.zigTypeTag() != .Int) { |
| 1671 | | return self.fail("TODO implement .shl for type {}", .{ty.fmtDebug()}); |
| 1672 | | } |
| 1673 | | if (ty.abiSize(self.target.*) > 8) { |
| 1674 | | return self.fail("TODO implement .shl for integers larger than 8 bytes", .{}); |
| 1675 | | } |
| 1676 | | |
| 1677 | | // TODO look into reusing the operands |
| 1678 | | // TODO audit register allocation mechanics |
| 1679 | | const shift = try self.resolveInst(bin_op.rhs); |
| 1680 | | const shift_ty = self.air.typeOf(bin_op.rhs); |
| 1681 | | |
| 1682 | | blk: { |
| 1683 | | switch (shift) { |
| 1684 | | .register => |reg| { |
| 1685 | | if (reg.to64() == .rcx) break :blk; |
| 1686 | | }, |
| 1687 | | else => {}, |
| 1688 | | } |
| 1689 | | try self.register_manager.getReg(.rcx, null); |
| 1690 | | try self.genSetReg(shift_ty, .rcx, shift); |
| 1691 | | } |
| 1692 | | const rcx_lock = self.register_manager.lockRegAssumeUnused(.rcx); |
| 1693 | | defer self.register_manager.unlockReg(rcx_lock); |
| 1694 | | |
| 1695 | | const value = try self.resolveInst(bin_op.lhs); |
| 1696 | | const value_lock: ?RegisterLock = switch (value) { |
| 1697 | | .register => |reg| self.register_manager.lockRegAssumeUnused(reg), |
| 1698 | | else => null, |
| 1699 | | }; |
| 1700 | | defer if (value_lock) |lock| self.register_manager.unlockReg(lock); |
| 1663 | const lhs = try self.resolveInst(bin_op.lhs); |
| 1664 | const rhs = try self.resolveInst(bin_op.rhs); |
| 1665 | const lhs_ty = self.air.typeOf(bin_op.lhs); |
| 1666 | const rhs_ty = self.air.typeOf(bin_op.rhs); |
| 1701 | 1667 | |
| 1702 | | const dst_mcv = try self.copyToRegisterWithInstTracking(inst, ty, value); |
| 1703 | | _ = try self.addInst(.{ |
| 1704 | | .tag = .sal, |
| 1705 | | .ops = (Mir.Ops{ |
| 1706 | | .reg1 = dst_mcv.register, |
| 1707 | | .flags = 0b01, |
| 1708 | | }).encode(), |
| 1709 | | .data = undefined, |
| 1710 | | }); |
| 1668 | const result = try self.genShiftBinOp(tag, inst, lhs, rhs, lhs_ty, rhs_ty); |
| 1711 | 1669 | |
| 1712 | | return self.finishAir(inst, dst_mcv, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1670 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1713 | 1671 | } |
| 1714 | 1672 | |
| 1715 | 1673 | fn airShlSat(self: *Self, inst: Air.Inst.Index) !void { |
| ... | ... | @@ -1721,80 +1679,6 @@ fn airShlSat(self: *Self, inst: Air.Inst.Index) !void { |
| 1721 | 1679 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1722 | 1680 | } |
| 1723 | 1681 | |
| 1724 | | fn airShr(self: *Self, inst: Air.Inst.Index) !void { |
| 1725 | | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1726 | | |
| 1727 | | if (self.liveness.isUnused(inst)) { |
| 1728 | | return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1729 | | } |
| 1730 | | |
| 1731 | | const ty = self.air.typeOfIndex(inst); |
| 1732 | | const tag = self.air.instructions.items(.tag)[inst]; |
| 1733 | | switch (tag) { |
| 1734 | | .shr_exact => return self.fail("TODO implement shr_exact for type {}", .{ty.fmtDebug()}), |
| 1735 | | .shr => {}, |
| 1736 | | else => unreachable, |
| 1737 | | } |
| 1738 | | |
| 1739 | | if (ty.zigTypeTag() != .Int) { |
| 1740 | | return self.fail("TODO implement shr for type {}", .{ty.fmtDebug()}); |
| 1741 | | } |
| 1742 | | if (ty.abiSize(self.target.*) > 8) { |
| 1743 | | return self.fail("TODO implement shr for integers larger than 8 bytes", .{}); |
| 1744 | | } |
| 1745 | | |
| 1746 | | // TODO look into reusing the operands |
| 1747 | | // TODO audit register allocation mechanics |
| 1748 | | const shift = try self.resolveInst(bin_op.rhs); |
| 1749 | | const shift_ty = self.air.typeOf(bin_op.rhs); |
| 1750 | | |
| 1751 | | blk: { |
| 1752 | | switch (shift) { |
| 1753 | | .register => |reg| { |
| 1754 | | if (reg.to64() == .rcx) break :blk; |
| 1755 | | }, |
| 1756 | | else => {}, |
| 1757 | | } |
| 1758 | | try self.register_manager.getReg(.rcx, null); |
| 1759 | | try self.genSetReg(shift_ty, .rcx, shift); |
| 1760 | | } |
| 1761 | | const rcx_lock = self.register_manager.lockRegAssumeUnused(.rcx); |
| 1762 | | defer self.register_manager.unlockReg(rcx_lock); |
| 1763 | | |
| 1764 | | const value = try self.resolveInst(bin_op.lhs); |
| 1765 | | const value_lock: ?RegisterLock = switch (value) { |
| 1766 | | .register => |reg| self.register_manager.lockRegAssumeUnused(reg), |
| 1767 | | else => null, |
| 1768 | | }; |
| 1769 | | defer if (value_lock) |lock| self.register_manager.unlockReg(lock); |
| 1770 | | |
| 1771 | | const dst_mcv = try self.copyToRegisterWithInstTracking(inst, ty, value); |
| 1772 | | switch (ty.intInfo(self.target.*).signedness) { |
| 1773 | | .signed => { |
| 1774 | | _ = try self.addInst(.{ |
| 1775 | | .tag = .sar, |
| 1776 | | .ops = (Mir.Ops{ |
| 1777 | | .reg1 = dst_mcv.register, |
| 1778 | | .flags = 0b01, |
| 1779 | | }).encode(), |
| 1780 | | .data = undefined, |
| 1781 | | }); |
| 1782 | | }, |
| 1783 | | .unsigned => { |
| 1784 | | _ = try self.addInst(.{ |
| 1785 | | .tag = .shr, |
| 1786 | | .ops = (Mir.Ops{ |
| 1787 | | .reg1 = dst_mcv.register, |
| 1788 | | .flags = 0b01, |
| 1789 | | }).encode(), |
| 1790 | | .data = undefined, |
| 1791 | | }); |
| 1792 | | }, |
| 1793 | | } |
| 1794 | | |
| 1795 | | return self.finishAir(inst, dst_mcv, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1796 | | } |
| 1797 | | |
| 1798 | 1682 | fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) !void { |
| 1799 | 1683 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1800 | 1684 | if (self.liveness.isUnused(inst)) { |
| ... | ... | @@ -1822,7 +1706,7 @@ fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) !void { |
| 1822 | 1706 | // TODO reuse the operand |
| 1823 | 1707 | const result = try self.copyToRegisterWithInstTracking(inst, optional_ty, operand); |
| 1824 | 1708 | const shift = @intCast(u8, offset * @sizeOf(usize)); |
| 1825 | | try self.shiftRegister(result.register, @intCast(u8, shift)); |
| 1709 | try self.shiftRegisterRightUnsigned(result.register, @intCast(u8, shift)); |
| 1826 | 1710 | break :result result; |
| 1827 | 1711 | }, |
| 1828 | 1712 | else => return self.fail("TODO implement optional_payload when operand is {}", .{operand}), |
| ... | ... | @@ -1909,7 +1793,7 @@ fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void { |
| 1909 | 1793 | // TODO reuse operand |
| 1910 | 1794 | const shift = @intCast(u6, err_abi_size * @sizeOf(usize)); |
| 1911 | 1795 | const result = try self.copyToRegisterWithInstTracking(inst, err_union_ty, operand); |
| 1912 | | try self.shiftRegister(result.register.to64(), shift); |
| 1796 | try self.shiftRegisterRightUnsigned(result.register.to64(), shift); |
| 1913 | 1797 | break :result MCValue{ |
| 1914 | 1798 | .register = registerAlias(result.register, @intCast(u32, payload_ty.abiSize(self.target.*))), |
| 1915 | 1799 | }; |
| ... | ... | @@ -2421,7 +2305,7 @@ fn airGetUnionTag(self: *Self, inst: Air.Inst.Index) !void { |
| 2421 | 2305 | else |
| 2422 | 2306 | 0; |
| 2423 | 2307 | const result = try self.copyToRegisterWithInstTracking(inst, union_ty, operand); |
| 2424 | | try self.shiftRegister(result.register.to64(), shift); |
| 2308 | try self.shiftRegisterRightUnsigned(result.register.to64(), shift); |
| 2425 | 2309 | break :blk MCValue{ |
| 2426 | 2310 | .register = registerAlias(result.register, @intCast(u32, layout.tag_size)), |
| 2427 | 2311 | }; |
| ... | ... | @@ -3020,7 +2904,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { |
| 3020 | 2904 | |
| 3021 | 2905 | // Shift by struct_field_offset. |
| 3022 | 2906 | const shift = @intCast(u8, struct_field_offset * @sizeOf(usize)); |
| 3023 | | try self.shiftRegister(dst_mcv.register, shift); |
| 2907 | try self.shiftRegisterRightUnsigned(dst_mcv.register, shift); |
| 3024 | 2908 | |
| 3025 | 2909 | // Mask with reg.size() - struct_field_size |
| 3026 | 2910 | const max_reg_bit_width = Register.rax.size(); |
| ... | ... | @@ -3097,7 +2981,111 @@ fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 3097 | 2981 | } |
| 3098 | 2982 | |
| 3099 | 2983 | /// Result is always a register. |
| 3100 | | /// Clobbers .rax and .rdx therefore care needs to be taken to spill operands upfront. |
| 2984 | /// Clobbers .rcx therefore care is needed to spill .rcx upfront. |
| 2985 | /// Asserts .rcx is free. |
| 2986 | fn genShiftBinOp( |
| 2987 | self: *Self, |
| 2988 | tag: Air.Inst.Tag, |
| 2989 | maybe_inst: ?Air.Inst.Index, |
| 2990 | lhs: MCValue, |
| 2991 | rhs: MCValue, |
| 2992 | lhs_ty: Type, |
| 2993 | rhs_ty: Type, |
| 2994 | ) !MCValue { |
| 2995 | if (lhs_ty.zigTypeTag() == .Vector or lhs_ty.zigTypeTag() == .Float) { |
| 2996 | return self.fail("TODO implement genShiftBinOp for {}", .{lhs_ty.fmtDebug()}); |
| 2997 | } |
| 2998 | if (lhs_ty.abiSize(self.target.*) > 8) { |
| 2999 | return self.fail("TODO implement genShiftBinOp for {}", .{lhs_ty.fmtDebug()}); |
| 3000 | } |
| 3001 | |
| 3002 | assert(self.register_manager.isRegFree(.rcx)); |
| 3003 | |
| 3004 | try self.register_manager.getReg(.rcx, null); |
| 3005 | try self.genSetReg(rhs_ty, .rcx, rhs); |
| 3006 | const rcx_lock = self.register_manager.lockRegAssumeUnused(.rcx); |
| 3007 | defer self.register_manager.unlockReg(rcx_lock); |
| 3008 | |
| 3009 | const int_info = lhs_ty.intInfo(self.target.*); |
| 3010 | const signedness = int_info.signedness; |
| 3011 | |
| 3012 | const lhs_lock: ?RegisterLock = switch (lhs) { |
| 3013 | .register => |reg| self.register_manager.lockReg(reg), |
| 3014 | else => null, |
| 3015 | }; |
| 3016 | defer if (lhs_lock) |lock| self.register_manager.unlockReg(lock); |
| 3017 | |
| 3018 | const rhs_lock: ?RegisterLock = switch (rhs) { |
| 3019 | .register => |reg| self.register_manager.lockReg(reg), |
| 3020 | else => null, |
| 3021 | }; |
| 3022 | defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock); |
| 3023 | |
| 3024 | const dst: MCValue = blk: { |
| 3025 | if (maybe_inst) |inst| { |
| 3026 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 3027 | // TODO dst can also be a memory location |
| 3028 | if (self.reuseOperand(inst, bin_op.lhs, 0, lhs) and lhs.isRegister()) { |
| 3029 | break :blk lhs; |
| 3030 | } |
| 3031 | break :blk try self.copyToRegisterWithInstTracking(inst, lhs_ty, lhs); |
| 3032 | } |
| 3033 | break :blk MCValue{ .register = try self.copyToTmpRegister(lhs_ty, lhs) }; |
| 3034 | }; |
| 3035 | |
| 3036 | switch (tag) { |
| 3037 | .shl => switch (signedness) { |
| 3038 | .signed => { |
| 3039 | _ = try self.addInst(.{ |
| 3040 | .tag = .sal, |
| 3041 | .ops = (Mir.Ops{ |
| 3042 | .reg1 = dst.register, |
| 3043 | .flags = 0b01, |
| 3044 | }).encode(), |
| 3045 | .data = undefined, |
| 3046 | }); |
| 3047 | }, |
| 3048 | .unsigned => { |
| 3049 | _ = try self.addInst(.{ |
| 3050 | .tag = .shl, |
| 3051 | .ops = (Mir.Ops{ |
| 3052 | .reg1 = dst.register, |
| 3053 | .flags = 0b01, |
| 3054 | }).encode(), |
| 3055 | .data = undefined, |
| 3056 | }); |
| 3057 | }, |
| 3058 | }, |
| 3059 | .shr => switch (signedness) { |
| 3060 | .signed => { |
| 3061 | _ = try self.addInst(.{ |
| 3062 | .tag = .sar, |
| 3063 | .ops = (Mir.Ops{ |
| 3064 | .reg1 = dst.register, |
| 3065 | .flags = 0b01, |
| 3066 | }).encode(), |
| 3067 | .data = undefined, |
| 3068 | }); |
| 3069 | }, |
| 3070 | .unsigned => { |
| 3071 | _ = try self.addInst(.{ |
| 3072 | .tag = .shr, |
| 3073 | .ops = (Mir.Ops{ |
| 3074 | .reg1 = dst.register, |
| 3075 | .flags = 0b01, |
| 3076 | }).encode(), |
| 3077 | .data = undefined, |
| 3078 | }); |
| 3079 | }, |
| 3080 | }, |
| 3081 | else => unreachable, |
| 3082 | } |
| 3083 | |
| 3084 | return dst; |
| 3085 | } |
| 3086 | |
| 3087 | /// Result is always a register. |
| 3088 | /// Clobbers .rax and .rdx therefore care is needed to spill .rax and .rdx upfront. |
| 3101 | 3089 | /// Asserts .rax and .rdx are free. |
| 3102 | 3090 | fn genMulDivBinOp( |
| 3103 | 3091 | self: *Self, |
| ... | ... | @@ -6790,7 +6778,8 @@ fn registerAlias(reg: Register, size_bytes: u32) Register { |
| 6790 | 6778 | } |
| 6791 | 6779 | } |
| 6792 | 6780 | |
| 6793 | | fn shiftRegister(self: *Self, reg: Register, shift: u8) !void { |
| 6781 | /// Shifts register right without sign-extension. |
| 6782 | fn shiftRegisterRightUnsigned(self: *Self, reg: Register, shift: u8) !void { |
| 6794 | 6783 | if (shift == 0) return; |
| 6795 | 6784 | if (shift == 1) { |
| 6796 | 6785 | _ = try self.addInst(.{ |