| ... | ... | @@ -1690,7 +1690,7 @@ fn airMulDivBinOp(self: *Self, inst: Air.Inst.Index) !void { |
| 1690 | 1690 | .unsigned => .int_unsigned, |
| 1691 | 1691 | } }, .data = switch (tag) { |
| 1692 | 1692 | else => unreachable, |
| 1693 | | .mul, .mulwrap => std.math.max3( |
| 1693 | .mul, .mulwrap => math.max3( |
| 1694 | 1694 | self.activeIntBits(bin_op.lhs), |
| 1695 | 1695 | self.activeIntBits(bin_op.rhs), |
| 1696 | 1696 | dst_info.bits / 2, |
| ... | ... | @@ -1743,7 +1743,7 @@ fn airAddSat(self: *Self, inst: Air.Inst.Index) !void { |
| 1743 | 1743 | break :cc .o; |
| 1744 | 1744 | } else cc: { |
| 1745 | 1745 | try self.genSetReg(ty, limit_reg, .{ |
| 1746 | | .immediate = @as(u64, std.math.maxInt(u64)) >> @intCast(u6, 64 - reg_bits), |
| 1746 | .immediate = @as(u64, math.maxInt(u64)) >> @intCast(u6, 64 - reg_bits), |
| 1747 | 1747 | }); |
| 1748 | 1748 | break :cc .c; |
| 1749 | 1749 | }; |
| ... | ... | @@ -1850,7 +1850,7 @@ fn airMulSat(self: *Self, inst: Air.Inst.Index) !void { |
| 1850 | 1850 | break :cc .o; |
| 1851 | 1851 | } else cc: { |
| 1852 | 1852 | try self.genSetReg(ty, limit_reg, .{ |
| 1853 | | .immediate = @as(u64, std.math.maxInt(u64)) >> @intCast(u6, 64 - reg_bits), |
| 1853 | .immediate = @as(u64, math.maxInt(u64)) >> @intCast(u6, 64 - reg_bits), |
| 1854 | 1854 | }); |
| 1855 | 1855 | break :cc .c; |
| 1856 | 1856 | }; |
| ... | ... | @@ -2067,7 +2067,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 2067 | 2067 | var src_pl = Type.Payload.Bits{ .base = .{ .tag = switch (dst_info.signedness) { |
| 2068 | 2068 | .signed => .int_signed, |
| 2069 | 2069 | .unsigned => .int_unsigned, |
| 2070 | | } }, .data = std.math.max3( |
| 2070 | } }, .data = math.max3( |
| 2071 | 2071 | self.activeIntBits(bin_op.lhs), |
| 2072 | 2072 | self.activeIntBits(bin_op.rhs), |
| 2073 | 2073 | dst_info.bits / 2, |
| ... | ... | @@ -2087,12 +2087,14 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 2087 | 2087 | else => {}, |
| 2088 | 2088 | } |
| 2089 | 2089 | |
| 2090 | // For now, this is the only supported multiply that doesn't fit in a register. |
| 2091 | assert(dst_info.bits == 128 and src_pl.data == 64); |
| 2090 | 2092 | const dst_abi_size = @intCast(i32, dst_ty.abiSize(self.target.*)); |
| 2091 | 2093 | const dst_mcv = try self.allocRegOrMem(inst, false); |
| 2092 | 2094 | try self.genSetStack( |
| 2093 | 2095 | Type.u1, |
| 2094 | 2096 | dst_mcv.stack_offset - dst_abi_size, |
| 2095 | | .{ .eflags = cc }, |
| 2097 | .{ .immediate = 0 }, // 64x64 -> 128 never overflows |
| 2096 | 2098 | .{}, |
| 2097 | 2099 | ); |
| 2098 | 2100 | try self.genSetStack(dst_ty, dst_mcv.stack_offset, partial_mcv, .{}); |
| ... | ... | @@ -3122,7 +3124,7 @@ fn airClz(self: *Self, inst: Air.Inst.Index) !void { |
| 3122 | 3124 | const imm_reg = try self.copyToTmpRegister(dst_ty, .{ |
| 3123 | 3125 | .immediate = src_bits ^ (src_bits - 1), |
| 3124 | 3126 | }); |
| 3125 | | try self.genBinOpMir(.bsf, src_ty, dst_mcv, mat_src_mcv); |
| 3127 | try self.genBinOpMir(.bsr, src_ty, dst_mcv, mat_src_mcv); |
| 3126 | 3128 | |
| 3127 | 3129 | const cmov_abi_size = @max(@intCast(u32, dst_ty.abiSize(self.target.*)), 2); |
| 3128 | 3130 | try self.asmCmovccRegisterRegister( |
| ... | ... | @@ -3136,7 +3138,7 @@ fn airClz(self: *Self, inst: Air.Inst.Index) !void { |
| 3136 | 3138 | const imm_reg = try self.copyToTmpRegister(dst_ty, .{ |
| 3137 | 3139 | .immediate = @as(u64, math.maxInt(u64)) >> @intCast(u6, 64 - self.regBitSize(dst_ty)), |
| 3138 | 3140 | }); |
| 3139 | | try self.genBinOpMir(.bsf, src_ty, dst_mcv, mat_src_mcv); |
| 3141 | try self.genBinOpMir(.bsr, src_ty, dst_mcv, mat_src_mcv); |
| 3140 | 3142 | |
| 3141 | 3143 | const cmov_abi_size = @max(@intCast(u32, dst_ty.abiSize(self.target.*)), 2); |
| 3142 | 3144 | try self.asmCmovccRegisterRegister( |
| ... | ... | @@ -3565,6 +3567,62 @@ fn reuseOperand( |
| 3565 | 3567 | return true; |
| 3566 | 3568 | } |
| 3567 | 3569 | |
| 3570 | fn packedLoad(self: *Self, dst_mcv: MCValue, ptr_mcv: MCValue, ptr_ty: Type) InnerError!void { |
| 3571 | const ptr_info = ptr_ty.ptrInfo().data; |
| 3572 | |
| 3573 | const val_ty = ptr_info.pointee_type; |
| 3574 | const val_abi_size = @intCast(u32, val_ty.abiSize(self.target.*)); |
| 3575 | const limb_abi_size = @min(val_abi_size, 8); |
| 3576 | const limb_abi_bits = limb_abi_size * 8; |
| 3577 | const val_byte_off = @intCast(i32, ptr_info.bit_offset / limb_abi_bits * limb_abi_size); |
| 3578 | const val_bit_off = ptr_info.bit_offset % limb_abi_bits; |
| 3579 | const val_extra_bits = self.regExtraBits(val_ty); |
| 3580 | |
| 3581 | if (val_abi_size > 8) return self.fail("TODO implement packed load of {}", .{ |
| 3582 | val_ty.fmt(self.bin_file.options.module.?), |
| 3583 | }); |
| 3584 | |
| 3585 | const ptr_reg = try self.copyToTmpRegister(ptr_ty, ptr_mcv); |
| 3586 | const ptr_lock = self.register_manager.lockRegAssumeUnused(ptr_reg); |
| 3587 | defer self.register_manager.unlockReg(ptr_lock); |
| 3588 | |
| 3589 | const dst_reg = switch (dst_mcv) { |
| 3590 | .register => |reg| reg, |
| 3591 | else => try self.register_manager.allocReg(null, gp), |
| 3592 | }; |
| 3593 | const dst_lock = self.register_manager.lockReg(dst_reg); |
| 3594 | defer if (dst_lock) |lock| self.register_manager.unlockReg(lock); |
| 3595 | |
| 3596 | const load_abi_size = |
| 3597 | if (val_bit_off < val_extra_bits) val_abi_size else val_abi_size * 2; |
| 3598 | if (load_abi_size <= 8) { |
| 3599 | const load_reg = registerAlias(dst_reg, load_abi_size); |
| 3600 | try self.asmRegisterMemory(.mov, load_reg, Memory.sib( |
| 3601 | Memory.PtrSize.fromSize(load_abi_size), |
| 3602 | .{ .base = ptr_reg, .disp = val_byte_off }, |
| 3603 | )); |
| 3604 | try self.asmRegisterImmediate(.shr, load_reg, Immediate.u(val_bit_off)); |
| 3605 | } else { |
| 3606 | const tmp_reg = registerAlias(try self.register_manager.allocReg(null, gp), val_abi_size); |
| 3607 | const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); |
| 3608 | defer self.register_manager.unlockReg(tmp_lock); |
| 3609 | |
| 3610 | const dst_alias = registerAlias(dst_reg, val_abi_size); |
| 3611 | try self.asmRegisterMemory(.mov, dst_alias, Memory.sib( |
| 3612 | Memory.PtrSize.fromSize(val_abi_size), |
| 3613 | .{ .base = ptr_reg, .disp = val_byte_off }, |
| 3614 | )); |
| 3615 | try self.asmRegisterMemory(.mov, tmp_reg, Memory.sib( |
| 3616 | Memory.PtrSize.fromSize(val_abi_size), |
| 3617 | .{ .base = ptr_reg, .disp = val_byte_off + 1 }, |
| 3618 | )); |
| 3619 | try self.asmRegisterRegisterImmediate(.shrd, dst_alias, tmp_reg, Immediate.u(val_bit_off)); |
| 3620 | } |
| 3621 | |
| 3622 | if (val_extra_bits > 0) try self.truncateRegister(val_ty, dst_reg); |
| 3623 | try self.setRegOrMem(val_ty, dst_mcv, .{ .register = dst_reg }); |
| 3624 | } |
| 3625 | |
| 3568 | 3626 | fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!void { |
| 3569 | 3627 | const elem_ty = ptr_ty.elemType(); |
| 3570 | 3628 | const abi_size = @intCast(u32, elem_ty.abiSize(self.target.*)); |
| ... | ... | @@ -3653,12 +3711,84 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) !void { |
| 3653 | 3711 | ptr |
| 3654 | 3712 | else |
| 3655 | 3713 | try self.allocRegOrMem(inst, true); |
| 3656 | | try self.load(dst_mcv, ptr, self.air.typeOf(ty_op.operand)); |
| 3714 | |
| 3715 | const ptr_ty = self.air.typeOf(ty_op.operand); |
| 3716 | if (ptr_ty.ptrInfo().data.host_size > 0) { |
| 3717 | try self.packedLoad(dst_mcv, ptr, ptr_ty); |
| 3718 | } else { |
| 3719 | try self.load(dst_mcv, ptr, ptr_ty); |
| 3720 | } |
| 3657 | 3721 | break :result dst_mcv; |
| 3658 | 3722 | }; |
| 3659 | 3723 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 3660 | 3724 | } |
| 3661 | 3725 | |
| 3726 | fn packedStore( |
| 3727 | self: *Self, |
| 3728 | ptr_mcv: MCValue, |
| 3729 | val_mcv: MCValue, |
| 3730 | ptr_ty: Type, |
| 3731 | val_ty: Type, |
| 3732 | ) InnerError!void { |
| 3733 | const ptr_info = ptr_ty.ptrInfo().data; |
| 3734 | |
| 3735 | const limb_abi_size = @min(ptr_info.host_size, 8); |
| 3736 | const limb_abi_bits = limb_abi_size * 8; |
| 3737 | |
| 3738 | const val_bit_size = val_ty.bitSize(self.target.*); |
| 3739 | const val_byte_off = @intCast(i32, ptr_info.bit_offset / limb_abi_bits * limb_abi_size); |
| 3740 | const val_bit_off = ptr_info.bit_offset % limb_abi_bits; |
| 3741 | |
| 3742 | const ptr_reg = try self.copyToTmpRegister(ptr_ty, ptr_mcv); |
| 3743 | const ptr_lock = self.register_manager.lockRegAssumeUnused(ptr_reg); |
| 3744 | defer self.register_manager.unlockReg(ptr_lock); |
| 3745 | |
| 3746 | var limb_i: u16 = 0; |
| 3747 | while (limb_i * limb_abi_bits < val_bit_off + val_bit_size) : (limb_i += 1) { |
| 3748 | const part_bit_off = if (limb_i == 0) val_bit_off else 0; |
| 3749 | const part_bit_size = |
| 3750 | @min(val_bit_off + val_bit_size - limb_i * limb_abi_bits, limb_abi_bits) - part_bit_off; |
| 3751 | const limb_mem = Memory.sib( |
| 3752 | Memory.PtrSize.fromSize(limb_abi_size), |
| 3753 | .{ .base = ptr_reg, .disp = val_byte_off + limb_i * limb_abi_bits }, |
| 3754 | ); |
| 3755 | |
| 3756 | const part_mask = (@as(u64, math.maxInt(u64)) >> @intCast(u6, 64 - part_bit_size)) << |
| 3757 | @intCast(u6, part_bit_off); |
| 3758 | const part_mask_not = part_mask ^ |
| 3759 | (@as(u64, math.maxInt(u64)) >> @intCast(u6, 64 - limb_abi_bits)); |
| 3760 | if (limb_abi_size <= 4) { |
| 3761 | try self.asmMemoryImmediate(.@"and", limb_mem, Immediate.u(part_mask_not)); |
| 3762 | } else if (math.cast(i32, @bitCast(i64, part_mask_not))) |small| { |
| 3763 | try self.asmMemoryImmediate(.@"and", limb_mem, Immediate.s(small)); |
| 3764 | } else { |
| 3765 | const part_mask_reg = try self.register_manager.allocReg(null, gp); |
| 3766 | try self.asmRegisterImmediate(.mov, part_mask_reg, Immediate.u(part_mask_not)); |
| 3767 | try self.asmMemoryRegister(.@"and", limb_mem, part_mask_reg); |
| 3768 | } |
| 3769 | |
| 3770 | if (val_bit_size <= 64) { |
| 3771 | const tmp_reg = try self.register_manager.allocReg(null, gp); |
| 3772 | const tmp_mcv = MCValue{ .register = tmp_reg }; |
| 3773 | const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); |
| 3774 | defer self.register_manager.unlockReg(tmp_lock); |
| 3775 | |
| 3776 | try self.genSetReg(val_ty, tmp_reg, val_mcv); |
| 3777 | switch (limb_i) { |
| 3778 | 0 => try self.genShiftBinOpMir(.shl, val_ty, tmp_mcv, .{ .immediate = val_bit_off }), |
| 3779 | 1 => try self.genShiftBinOpMir(.shr, val_ty, tmp_mcv, .{ |
| 3780 | .immediate = limb_abi_bits - val_bit_off, |
| 3781 | }), |
| 3782 | else => unreachable, |
| 3783 | } |
| 3784 | try self.genBinOpMir(.@"and", val_ty, tmp_mcv, .{ .immediate = part_mask }); |
| 3785 | try self.asmMemoryRegister(.@"or", limb_mem, registerAlias(tmp_reg, limb_abi_size)); |
| 3786 | } else return self.fail("TODO: implement packed store of {}", .{ |
| 3787 | val_ty.fmt(self.bin_file.options.module.?), |
| 3788 | }); |
| 3789 | } |
| 3790 | } |
| 3791 | |
| 3662 | 3792 | fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type) InnerError!void { |
| 3663 | 3793 | const abi_size = @intCast(u32, value_ty.abiSize(self.target.*)); |
| 3664 | 3794 | switch (ptr) { |
| ... | ... | @@ -3850,7 +3980,11 @@ fn airStore(self: *Self, inst: Air.Inst.Index) !void { |
| 3850 | 3980 | const value = try self.resolveInst(bin_op.rhs); |
| 3851 | 3981 | const value_ty = self.air.typeOf(bin_op.rhs); |
| 3852 | 3982 | log.debug("airStore(%{d}): {} <- {}", .{ inst, ptr, value }); |
| 3853 | | try self.store(ptr, value, ptr_ty, value_ty); |
| 3983 | if (ptr_ty.ptrInfo().data.host_size > 0) { |
| 3984 | try self.packedStore(ptr, value, ptr_ty, value_ty); |
| 3985 | } else { |
| 3986 | try self.store(ptr, value, ptr_ty, value_ty); |
| 3987 | } |
| 3854 | 3988 | return self.finishAir(inst, .none, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 3855 | 3989 | } |
| 3856 | 3990 | |
| ... | ... | @@ -5214,7 +5348,7 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M |
| 5214 | 5348 | registerAlias(src_reg, abi_size), |
| 5215 | 5349 | ), |
| 5216 | 5350 | .immediate => |imm| { |
| 5217 | | if (std.math.cast(i32, imm)) |small| { |
| 5351 | if (math.cast(i32, imm)) |small| { |
| 5218 | 5352 | try self.asmRegisterRegisterImmediate( |
| 5219 | 5353 | .imul, |
| 5220 | 5354 | dst_alias, |
| ... | ... | @@ -6820,7 +6954,7 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { |
| 6820 | 6954 | } else if (mem.startsWith(u8, op_str, "$")) { |
| 6821 | 6955 | if (std.fmt.parseInt(i32, op_str["$".len..], 0)) |s| { |
| 6822 | 6956 | if (mnem_size) |size| { |
| 6823 | | const max = @as(u64, std.math.maxInt(u64)) >> |
| 6957 | const max = @as(u64, math.maxInt(u64)) >> |
| 6824 | 6958 | @intCast(u6, 64 - (size.bitSize() - 1)); |
| 6825 | 6959 | if ((if (s < 0) ~s else s) > max) |
| 6826 | 6960 | return self.fail("Invalid immediate size: '{s}'", .{op_str}); |
| ... | ... | @@ -6828,7 +6962,7 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { |
| 6828 | 6962 | op.* = .{ .imm = Immediate.s(s) }; |
| 6829 | 6963 | } else |_| if (std.fmt.parseInt(u64, op_str["$".len..], 0)) |u| { |
| 6830 | 6964 | if (mnem_size) |size| { |
| 6831 | | const max = @as(u64, std.math.maxInt(u64)) >> |
| 6965 | const max = @as(u64, math.maxInt(u64)) >> |
| 6832 | 6966 | @intCast(u6, 64 - size.bitSize()); |
| 6833 | 6967 | if (u > max) |
| 6834 | 6968 | return self.fail("Invalid immediate size: '{s}'", .{op_str}); |
| ... | ... | @@ -7167,7 +7301,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl |
| 7167 | 7301 | else => { |
| 7168 | 7302 | // 64 bit write to memory would take two mov's anyways so we |
| 7169 | 7303 | // insted just use two 32 bit writes to avoid register allocation |
| 7170 | | if (std.math.cast(i32, @bitCast(i64, imm))) |small| { |
| 7304 | if (math.cast(i32, @bitCast(i64, imm))) |small| { |
| 7171 | 7305 | try self.asmMemoryImmediate(.mov, Memory.sib( |
| 7172 | 7306 | Memory.PtrSize.fromSize(abi_size), |
| 7173 | 7307 | .{ .base = base_reg, .disp = -stack_offset }, |