authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2023-04-15 01:59:43-04:00
committergravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2023-04-16 17:05:35-04:00
logc1a2da6b78d27d572cee38bb8cde2e46c842ffc0
treec92d5a12e09a89bf972370c8f7bfdba5bbc3d290
parentc03771e1738dae25eccf7726dcc952ad5936ca15

x86_64: implement packed load and store


4 files changed, 142 insertions(+), 16 deletions(-)

src/arch/x86_64/CodeGen.zig+142-10
...@@ -1692,7 +1692,7 @@ fn airMulDivBinOp(self: *Self, inst: Air.Inst.Index) !void {...@@ -1692,7 +1692,7 @@ fn airMulDivBinOp(self: *Self, inst: Air.Inst.Index) !void {
1692 .unsigned => .int_unsigned,1692 .unsigned => .int_unsigned,
1693 } }, .data = switch (tag) {1693 } }, .data = switch (tag) {
1694 else => unreachable,1694 else => unreachable,
1695 .mul, .mulwrap => std.math.max3(1695 .mul, .mulwrap => math.max3(
1696 self.activeIntBits(bin_op.lhs),1696 self.activeIntBits(bin_op.lhs),
1697 self.activeIntBits(bin_op.rhs),1697 self.activeIntBits(bin_op.rhs),
1698 dst_info.bits / 2,1698 dst_info.bits / 2,
...@@ -1745,7 +1745,7 @@ fn airAddSat(self: *Self, inst: Air.Inst.Index) !void {...@@ -1745,7 +1745,7 @@ fn airAddSat(self: *Self, inst: Air.Inst.Index) !void {
1745 break :cc .o;1745 break :cc .o;
1746 } else cc: {1746 } else cc: {
1747 try self.genSetReg(ty, limit_reg, .{1747 try self.genSetReg(ty, limit_reg, .{
1748 .immediate = @as(u64, std.math.maxInt(u64)) >> @intCast(u6, 64 - reg_bits),1748 .immediate = @as(u64, math.maxInt(u64)) >> @intCast(u6, 64 - reg_bits),
1749 });1749 });
1750 break :cc .c;1750 break :cc .c;
1751 };1751 };
...@@ -1852,7 +1852,7 @@ fn airMulSat(self: *Self, inst: Air.Inst.Index) !void {...@@ -1852,7 +1852,7 @@ fn airMulSat(self: *Self, inst: Air.Inst.Index) !void {
1852 break :cc .o;1852 break :cc .o;
1853 } else cc: {1853 } else cc: {
1854 try self.genSetReg(ty, limit_reg, .{1854 try self.genSetReg(ty, limit_reg, .{
1855 .immediate = @as(u64, std.math.maxInt(u64)) >> @intCast(u6, 64 - reg_bits),1855 .immediate = @as(u64, math.maxInt(u64)) >> @intCast(u6, 64 - reg_bits),
1856 });1856 });
1857 break :cc .c;1857 break :cc .c;
1858 };1858 };
...@@ -2069,7 +2069,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {...@@ -2069,7 +2069,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
2069 var src_pl = Type.Payload.Bits{ .base = .{ .tag = switch (dst_info.signedness) {2069 var src_pl = Type.Payload.Bits{ .base = .{ .tag = switch (dst_info.signedness) {
2070 .signed => .int_signed,2070 .signed => .int_signed,
2071 .unsigned => .int_unsigned,2071 .unsigned => .int_unsigned,
2072 } }, .data = std.math.max3(2072 } }, .data = math.max3(
2073 self.activeIntBits(bin_op.lhs),2073 self.activeIntBits(bin_op.lhs),
2074 self.activeIntBits(bin_op.rhs),2074 self.activeIntBits(bin_op.rhs),
2075 dst_info.bits / 2,2075 dst_info.bits / 2,
...@@ -3569,6 +3569,62 @@ fn reuseOperand(...@@ -3569,6 +3569,62 @@ fn reuseOperand(
3569 return true;3569 return true;
3570}3570}
35713571
3572fn packedLoad(self: *Self, dst_mcv: MCValue, ptr_mcv: MCValue, ptr_ty: Type) InnerError!void {
3573 const ptr_info = ptr_ty.ptrInfo().data;
3574
3575 const val_ty = ptr_info.pointee_type;
3576 const val_abi_size = @intCast(u32, val_ty.abiSize(self.target.*));
3577 const limb_abi_size = @min(val_abi_size, 8);
3578 const limb_abi_bits = limb_abi_size * 8;
3579 const val_byte_off = @intCast(i32, ptr_info.bit_offset / limb_abi_bits * limb_abi_size);
3580 const val_bit_off = ptr_info.bit_offset % limb_abi_bits;
3581 const val_extra_bits = self.regExtraBits(val_ty);
3582
3583 if (val_abi_size > 8) return self.fail("TODO implement packed load of {}", .{
3584 val_ty.fmt(self.bin_file.options.module.?),
3585 });
3586
3587 const ptr_reg = try self.copyToTmpRegister(ptr_ty, ptr_mcv);
3588 const ptr_lock = self.register_manager.lockRegAssumeUnused(ptr_reg);
3589 defer self.register_manager.unlockReg(ptr_lock);
3590
3591 const dst_reg = switch (dst_mcv) {
3592 .register => |reg| reg,
3593 else => try self.register_manager.allocReg(null, gp),
3594 };
3595 const dst_lock = self.register_manager.lockReg(dst_reg);
3596 defer if (dst_lock) |lock| self.register_manager.unlockReg(lock);
3597
3598 const load_abi_size =
3599 if (val_bit_off < val_extra_bits) val_abi_size else val_abi_size * 2;
3600 if (load_abi_size <= 8) {
3601 const load_reg = registerAlias(dst_reg, load_abi_size);
3602 try self.asmRegisterMemory(.mov, load_reg, Memory.sib(
3603 Memory.PtrSize.fromSize(load_abi_size),
3604 .{ .base = ptr_reg, .disp = val_byte_off },
3605 ));
3606 try self.asmRegisterImmediate(.shr, load_reg, Immediate.u(val_bit_off));
3607 } else {
3608 const tmp_reg = registerAlias(try self.register_manager.allocReg(null, gp), val_abi_size);
3609 const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg);
3610 defer self.register_manager.unlockReg(tmp_lock);
3611
3612 const dst_alias = registerAlias(dst_reg, val_abi_size);
3613 try self.asmRegisterMemory(.mov, dst_alias, Memory.sib(
3614 Memory.PtrSize.fromSize(val_abi_size),
3615 .{ .base = ptr_reg, .disp = val_byte_off },
3616 ));
3617 try self.asmRegisterMemory(.mov, tmp_reg, Memory.sib(
3618 Memory.PtrSize.fromSize(val_abi_size),
3619 .{ .base = ptr_reg, .disp = val_byte_off + 1 },
3620 ));
3621 try self.asmRegisterRegisterImmediate(.shrd, dst_alias, tmp_reg, Immediate.u(val_bit_off));
3622 }
3623
3624 if (val_extra_bits > 0) try self.truncateRegister(val_ty, dst_reg);
3625 try self.setRegOrMem(val_ty, dst_mcv, .{ .register = dst_reg });
3626}
3627
3572fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!void {3628fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!void {
3573 const elem_ty = ptr_ty.elemType();3629 const elem_ty = ptr_ty.elemType();
3574 const abi_size = @intCast(u32, elem_ty.abiSize(self.target.*));3630 const abi_size = @intCast(u32, elem_ty.abiSize(self.target.*));
...@@ -3657,12 +3713,84 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) !void {...@@ -3657,12 +3713,84 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) !void {
3657 ptr3713 ptr
3658 else3714 else
3659 try self.allocRegOrMem(inst, true);3715 try self.allocRegOrMem(inst, true);
3660 try self.load(dst_mcv, ptr, self.air.typeOf(ty_op.operand));3716
3717 const ptr_ty = self.air.typeOf(ty_op.operand);
3718 if (ptr_ty.ptrInfo().data.host_size > 0) {
3719 try self.packedLoad(dst_mcv, ptr, ptr_ty);
3720 } else {
3721 try self.load(dst_mcv, ptr, ptr_ty);
3722 }
3661 break :result dst_mcv;3723 break :result dst_mcv;
3662 };3724 };
3663 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });3725 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
3664}3726}
36653727
3728fn packedStore(
3729 self: *Self,
3730 ptr_mcv: MCValue,
3731 val_mcv: MCValue,
3732 ptr_ty: Type,
3733 val_ty: Type,
3734) InnerError!void {
3735 const ptr_info = ptr_ty.ptrInfo().data;
3736
3737 const limb_abi_size = @min(ptr_info.host_size, 8);
3738 const limb_abi_bits = limb_abi_size * 8;
3739
3740 const val_bit_size = val_ty.bitSize(self.target.*);
3741 const val_byte_off = @intCast(i32, ptr_info.bit_offset / limb_abi_bits * limb_abi_size);
3742 const val_bit_off = ptr_info.bit_offset % limb_abi_bits;
3743
3744 const ptr_reg = try self.copyToTmpRegister(ptr_ty, ptr_mcv);
3745 const ptr_lock = self.register_manager.lockRegAssumeUnused(ptr_reg);
3746 defer self.register_manager.unlockReg(ptr_lock);
3747
3748 var limb_i: u16 = 0;
3749 while (limb_i * limb_abi_bits < val_bit_off + val_bit_size) : (limb_i += 1) {
3750 const part_bit_off = if (limb_i == 0) val_bit_off else 0;
3751 const part_bit_size =
3752 @min(val_bit_off + val_bit_size - limb_i * limb_abi_bits, limb_abi_bits) - part_bit_off;
3753 const limb_mem = Memory.sib(
3754 Memory.PtrSize.fromSize(limb_abi_size),
3755 .{ .base = ptr_reg, .disp = val_byte_off + limb_i * limb_abi_bits },
3756 );
3757
3758 const part_mask = (@as(u64, math.maxInt(u64)) >> @intCast(u6, 64 - part_bit_size)) <<
3759 @intCast(u6, part_bit_off);
3760 const part_mask_not = part_mask ^
3761 (@as(u64, math.maxInt(u64)) >> @intCast(u6, 64 - limb_abi_bits));
3762 if (limb_abi_size <= 4) {
3763 try self.asmMemoryImmediate(.@"and", limb_mem, Immediate.u(part_mask_not));
3764 } else if (math.cast(i32, @bitCast(i64, part_mask_not))) |small| {
3765 try self.asmMemoryImmediate(.@"and", limb_mem, Immediate.s(small));
3766 } else {
3767 const part_mask_reg = try self.register_manager.allocReg(null, gp);
3768 try self.asmRegisterImmediate(.mov, part_mask_reg, Immediate.u(part_mask_not));
3769 try self.asmMemoryRegister(.@"and", limb_mem, part_mask_reg);
3770 }
3771
3772 if (val_bit_size <= 64) {
3773 const tmp_reg = try self.register_manager.allocReg(null, gp);
3774 const tmp_mcv = MCValue{ .register = tmp_reg };
3775 const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg);
3776 defer self.register_manager.unlockReg(tmp_lock);
3777
3778 try self.genSetReg(val_ty, tmp_reg, val_mcv);
3779 switch (limb_i) {
3780 0 => try self.genShiftBinOpMir(.shl, val_ty, tmp_mcv, .{ .immediate = val_bit_off }),
3781 1 => try self.genShiftBinOpMir(.shr, val_ty, tmp_mcv, .{
3782 .immediate = limb_abi_bits - val_bit_off,
3783 }),
3784 else => unreachable,
3785 }
3786 try self.genBinOpMir(.@"and", val_ty, tmp_mcv, .{ .immediate = part_mask });
3787 try self.asmMemoryRegister(.@"or", limb_mem, registerAlias(tmp_reg, limb_abi_size));
3788 } else return self.fail("TODO: implement packed store of {}", .{
3789 val_ty.fmt(self.bin_file.options.module.?),
3790 });
3791 }
3792}
3793
3666fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type) InnerError!void {3794fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type) InnerError!void {
3667 const abi_size = @intCast(u32, value_ty.abiSize(self.target.*));3795 const abi_size = @intCast(u32, value_ty.abiSize(self.target.*));
3668 switch (ptr) {3796 switch (ptr) {
...@@ -3854,7 +3982,11 @@ fn airStore(self: *Self, inst: Air.Inst.Index) !void {...@@ -3854,7 +3982,11 @@ fn airStore(self: *Self, inst: Air.Inst.Index) !void {
3854 const value = try self.resolveInst(bin_op.rhs);3982 const value = try self.resolveInst(bin_op.rhs);
3855 const value_ty = self.air.typeOf(bin_op.rhs);3983 const value_ty = self.air.typeOf(bin_op.rhs);
3856 log.debug("airStore(%{d}): {} <- {}", .{ inst, ptr, value });3984 log.debug("airStore(%{d}): {} <- {}", .{ inst, ptr, value });
3857 try self.store(ptr, value, ptr_ty, value_ty);3985 if (ptr_ty.ptrInfo().data.host_size > 0) {
3986 try self.packedStore(ptr, value, ptr_ty, value_ty);
3987 } else {
3988 try self.store(ptr, value, ptr_ty, value_ty);
3989 }
3858 return self.finishAir(inst, .none, .{ bin_op.lhs, bin_op.rhs, .none });3990 return self.finishAir(inst, .none, .{ bin_op.lhs, bin_op.rhs, .none });
3859}3991}
38603992
...@@ -5218,7 +5350,7 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M...@@ -5218,7 +5350,7 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M
5218 registerAlias(src_reg, abi_size),5350 registerAlias(src_reg, abi_size),
5219 ),5351 ),
5220 .immediate => |imm| {5352 .immediate => |imm| {
5221 if (std.math.cast(i32, imm)) |small| {5353 if (math.cast(i32, imm)) |small| {
5222 try self.asmRegisterRegisterImmediate(5354 try self.asmRegisterRegisterImmediate(
5223 .imul,5355 .imul,
5224 dst_alias,5356 dst_alias,
...@@ -6824,7 +6956,7 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {...@@ -6824,7 +6956,7 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
6824 } else if (mem.startsWith(u8, op_str, "$")) {6956 } else if (mem.startsWith(u8, op_str, "$")) {
6825 if (std.fmt.parseInt(i32, op_str["$".len..], 0)) |s| {6957 if (std.fmt.parseInt(i32, op_str["$".len..], 0)) |s| {
6826 if (mnem_size) |size| {6958 if (mnem_size) |size| {
6827 const max = @as(u64, std.math.maxInt(u64)) >>6959 const max = @as(u64, math.maxInt(u64)) >>
6828 @intCast(u6, 64 - (size.bitSize() - 1));6960 @intCast(u6, 64 - (size.bitSize() - 1));
6829 if ((if (s < 0) ~s else s) > max)6961 if ((if (s < 0) ~s else s) > max)
6830 return self.fail("Invalid immediate size: '{s}'", .{op_str});6962 return self.fail("Invalid immediate size: '{s}'", .{op_str});
...@@ -6832,7 +6964,7 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {...@@ -6832,7 +6964,7 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
6832 op.* = .{ .imm = Immediate.s(s) };6964 op.* = .{ .imm = Immediate.s(s) };
6833 } else |_| if (std.fmt.parseInt(u64, op_str["$".len..], 0)) |u| {6965 } else |_| if (std.fmt.parseInt(u64, op_str["$".len..], 0)) |u| {
6834 if (mnem_size) |size| {6966 if (mnem_size) |size| {
6835 const max = @as(u64, std.math.maxInt(u64)) >>6967 const max = @as(u64, math.maxInt(u64)) >>
6836 @intCast(u6, 64 - size.bitSize());6968 @intCast(u6, 64 - size.bitSize());
6837 if (u > max)6969 if (u > max)
6838 return self.fail("Invalid immediate size: '{s}'", .{op_str});6970 return self.fail("Invalid immediate size: '{s}'", .{op_str});
...@@ -7171,7 +7303,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl...@@ -7171,7 +7303,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl
7171 else => {7303 else => {
7172 // 64 bit write to memory would take two mov's anyways so we7304 // 64 bit write to memory would take two mov's anyways so we
7173 // insted just use two 32 bit writes to avoid register allocation7305 // insted just use two 32 bit writes to avoid register allocation
7174 if (std.math.cast(i32, @bitCast(i64, imm))) |small| {7306 if (math.cast(i32, @bitCast(i64, imm))) |small| {
7175 try self.asmMemoryImmediate(.mov, Memory.sib(7307 try self.asmMemoryImmediate(.mov, Memory.sib(
7176 Memory.PtrSize.fromSize(abi_size),7308 Memory.PtrSize.fromSize(abi_size),
7177 .{ .base = base_reg, .disp = -stack_offset },7309 .{ .base = base_reg, .disp = -stack_offset },
test/behavior/bugs/1851.zig-1
...@@ -4,7 +4,6 @@ const expect = std.testing.expect;...@@ -4,7 +4,6 @@ const expect = std.testing.expect;
44
5test "allocation and looping over 3-byte integer" {5test "allocation and looping over 3-byte integer" {
6 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO6 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
7 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
8 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO7 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
9 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO8 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
10 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO9 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
test/behavior/enum.zig-1
...@@ -1071,7 +1071,6 @@ const bit_field_1 = BitFieldOfEnums{...@@ -1071,7 +1071,6 @@ const bit_field_1 = BitFieldOfEnums{
10711071
1072test "bit field access with enum fields" {1072test "bit field access with enum fields" {
1073 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO1073 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1074 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
1075 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;1074 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
1076 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;1075 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
1077 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO1076 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
test/behavior/struct.zig-4
...@@ -456,7 +456,6 @@ test "packed struct 24bits" {...@@ -456,7 +456,6 @@ test "packed struct 24bits" {
456test "runtime struct initialization of bitfield" {456test "runtime struct initialization of bitfield" {
457 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;457 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
458 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO458 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
459 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
460 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO459 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
461460
462 const s1 = Nibbles{461 const s1 = Nibbles{
...@@ -577,7 +576,6 @@ const bit_field_1 = BitField1{...@@ -577,7 +576,6 @@ const bit_field_1 = BitField1{
577test "bit field access" {576test "bit field access" {
578 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;577 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
579 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO578 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
580 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
581 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO579 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
582580
583 var data = bit_field_1;581 var data = bit_field_1;
...@@ -696,7 +694,6 @@ const FooArrayOfAligned = packed struct {...@@ -696,7 +694,6 @@ const FooArrayOfAligned = packed struct {
696};694};
697695
698test "pointer to packed struct member in a stack variable" {696test "pointer to packed struct member in a stack variable" {
699 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
700 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO697 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
701 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO698 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
702 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO699 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
...@@ -1259,7 +1256,6 @@ test "packed struct aggregate init" {...@@ -1259,7 +1256,6 @@ test "packed struct aggregate init" {
1259}1256}
12601257
1261test "packed struct field access via pointer" {1258test "packed struct field access via pointer" {
1262 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1263 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO1259 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1264 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO1260 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1265 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO1261 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO