| ... | @@ -597,7 +597,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { | ... | @@ -597,7 +597,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 597 | .mul_sat => try self.airMulSat(inst), | 597 | .mul_sat => try self.airMulSat(inst), |
| 598 | .rem => try self.airMulDivBinOp(inst), | 598 | .rem => try self.airMulDivBinOp(inst), |
| 599 | .mod => try self.airMulDivBinOp(inst), | 599 | .mod => try self.airMulDivBinOp(inst), |
| 600 | .shl, .shl_exact => try self.airShl(inst), | 600 | .shl, .shl_exact => try self.airShlShrBinOp(inst), |
| 601 | .shl_sat => try self.airShlSat(inst), | 601 | .shl_sat => try self.airShlSat(inst), |
| 602 | .min => try self.airMin(inst), | 602 | .min => try self.airMin(inst), |
| 603 | .max => try self.airMax(inst), | 603 | .max => try self.airMax(inst), |
| ... | @@ -643,7 +643,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { | ... | @@ -643,7 +643,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 643 | .bit_and => try self.airBinOp(inst), | 643 | .bit_and => try self.airBinOp(inst), |
| 644 | .bit_or => try self.airBinOp(inst), | 644 | .bit_or => try self.airBinOp(inst), |
| 645 | .xor => try self.airBinOp(inst), | 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 | .alloc => try self.airAlloc(inst), | 648 | .alloc => try self.airAlloc(inst), |
| 649 | .ret_ptr => try self.airRetPtr(inst), | 649 | .ret_ptr => try self.airRetPtr(inst), |
| ... | @@ -1652,64 +1652,22 @@ fn genInlineIntDivFloor(self: *Self, ty: Type, lhs: MCValue, rhs: MCValue) !MCVa | ... | @@ -1652,64 +1652,22 @@ fn genInlineIntDivFloor(self: *Self, ty: Type, lhs: MCValue, rhs: MCValue) !MCVa |
| 1652 | return MCValue{ .register = divisor }; | 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 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | 1656 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| | 1657 | |
| 1657 | if (self.liveness.isUnused(inst)) { | 1658 | if (self.liveness.isUnused(inst)) { |
| 1658 | return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); | 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 | const tag = self.air.instructions.items(.tag)[inst]; | 1662 | const tag = self.air.instructions.items(.tag)[inst]; |
| 1664 | switch (tag) { | 1663 | const lhs = try self.resolveInst(bin_op.lhs); |
| 1665 | .shl_exact => return self.fail("TODO implement {} for type {}", .{ tag, ty.fmtDebug() }), | 1664 | const rhs = try self.resolveInst(bin_op.rhs); |
| 1666 | .shl => {}, | 1665 | const lhs_ty = self.air.typeOf(bin_op.lhs); |
| 1667 | else => unreachable, | 1666 | const rhs_ty = self.air.typeOf(bin_op.rhs); |
| 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); | | |
| 1701 | | 1667 | |
| 1702 | const dst_mcv = try self.copyToRegisterWithInstTracking(inst, ty, value); | 1668 | const result = try self.genShiftBinOp(tag, inst, lhs, rhs, lhs_ty, rhs_ty); |
| 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 | }); | | |
| 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 | fn airShlSat(self: *Self, inst: Air.Inst.Index) !void { | 1673 | fn airShlSat(self: *Self, inst: Air.Inst.Index) !void { |
| ... | @@ -1721,80 +1679,6 @@ fn airShlSat(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1721,80 +1679,6 @@ fn airShlSat(self: *Self, inst: Air.Inst.Index) !void { |
| 1721 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | 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 | fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) !void { | 1682 | fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) !void { |
| 1799 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 1683 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1800 | if (self.liveness.isUnused(inst)) { | 1684 | if (self.liveness.isUnused(inst)) { |
| ... | @@ -1822,7 +1706,7 @@ fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1822,7 +1706,7 @@ fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) !void { |
| 1822 | // TODO reuse the operand | 1706 | // TODO reuse the operand |
| 1823 | const result = try self.copyToRegisterWithInstTracking(inst, optional_ty, operand); | 1707 | const result = try self.copyToRegisterWithInstTracking(inst, optional_ty, operand); |
| 1824 | const shift = @intCast(u8, offset * @sizeOf(usize)); | 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 | break :result result; | 1710 | break :result result; |
| 1827 | }, | 1711 | }, |
| 1828 | else => return self.fail("TODO implement optional_payload when operand is {}", .{operand}), | 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,7 +1793,7 @@ fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void { |
| 1909 | // TODO reuse operand | 1793 | // TODO reuse operand |
| 1910 | const shift = @intCast(u6, err_abi_size * @sizeOf(usize)); | 1794 | const shift = @intCast(u6, err_abi_size * @sizeOf(usize)); |
| 1911 | const result = try self.copyToRegisterWithInstTracking(inst, err_union_ty, operand); | 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 | break :result MCValue{ | 1797 | break :result MCValue{ |
| 1914 | .register = registerAlias(result.register, @intCast(u32, payload_ty.abiSize(self.target.*))), | 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,7 +2305,7 @@ fn airGetUnionTag(self: *Self, inst: Air.Inst.Index) !void { |
| 2421 | else | 2305 | else |
| 2422 | 0; | 2306 | 0; |
| 2423 | const result = try self.copyToRegisterWithInstTracking(inst, union_ty, operand); | 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 | break :blk MCValue{ | 2309 | break :blk MCValue{ |
| 2426 | .register = registerAlias(result.register, @intCast(u32, layout.tag_size)), | 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,7 +2904,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { |
| 3020 | | 2904 | |
| 3021 | // Shift by struct_field_offset. | 2905 | // Shift by struct_field_offset. |
| 3022 | const shift = @intCast(u8, struct_field_offset * @sizeOf(usize)); | 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 | // Mask with reg.size() - struct_field_size | 2909 | // Mask with reg.size() - struct_field_size |
| 3026 | const max_reg_bit_width = Register.rax.size(); | 2910 | const max_reg_bit_width = Register.rax.size(); |
| ... | @@ -3097,7 +2981,111 @@ fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -3097,7 +2981,111 @@ fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 3097 | } | 2981 | } |
| 3098 | | 2982 | |
| 3099 | /// Result is always a register. | 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 | /// Asserts .rax and .rdx are free. | 3089 | /// Asserts .rax and .rdx are free. |
| 3102 | fn genMulDivBinOp( | 3090 | fn genMulDivBinOp( |
| 3103 | self: *Self, | 3091 | self: *Self, |
| ... | @@ -6790,7 +6778,8 @@ fn registerAlias(reg: Register, size_bytes: u32) Register { | ... | @@ -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 | if (shift == 0) return; | 6783 | if (shift == 0) return; |
| 6795 | if (shift == 1) { | 6784 | if (shift == 1) { |
| 6796 | _ = try self.addInst(.{ | 6785 | _ = try self.addInst(.{ |