Commit ac68d72d24

Jacob Young <jacobly0@users.noreply.github.com>
2023-04-01 02:31:39
x86_64: implement aggregate init of a packed struct
1 parent 43a6384
Changed files (3)
src
arch
test
src/arch/x86_64/CodeGen.zig
@@ -7853,19 +7853,85 @@ fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void {
         if (self.liveness.isUnused(inst)) break :res MCValue.dead;
         switch (result_ty.zigTypeTag()) {
             .Struct => {
-                if (result_ty.containerLayout() == .Packed) {
-                    return self.fail("TODO airAggregateInit implement packed structs", .{});
-                }
                 const stack_offset = @intCast(i32, try self.allocMem(inst, abi_size, abi_align));
-                for (elements, 0..) |elem, elem_i| {
-                    if (result_ty.structFieldValueComptime(elem_i) != null) continue; // comptime elem
+                const dst_mcv = MCValue{ .stack_offset = stack_offset };
+                if (result_ty.containerLayout() == .Packed) {
+                    const struct_obj = result_ty.castTag(.@"struct").?.data;
+                    try self.genInlineMemset(
+                        dst_mcv,
+                        .{ .immediate = 0 },
+                        .{ .immediate = abi_size },
+                        .{},
+                    );
+                    for (elements, 0..) |elem, elem_i| {
+                        if (result_ty.structFieldValueComptime(elem_i) != null) continue;
+
+                        const elem_ty = result_ty.structFieldType(elem_i);
+                        const elem_bit_size = @intCast(u32, elem_ty.bitSize(self.target.*));
+                        if (elem_bit_size > 64) {
+                            return self.fail("TODO airAggregateInit implement packed structs with large fields", .{});
+                        }
+                        const elem_abi_size = @intCast(u32, elem_ty.abiSize(self.target.*));
+                        const elem_abi_bits = elem_abi_size * 8;
+                        const elem_off = struct_obj.packedFieldBitOffset(self.target.*, elem_i);
+                        const elem_byte_off = @intCast(i32, elem_off / elem_abi_bits * elem_abi_size);
+                        const elem_bit_off = elem_off % elem_abi_bits;
+                        const elem_mcv = try self.resolveInst(elem);
+                        const elem_lock = switch (elem_mcv) {
+                            .register => |reg| self.register_manager.lockReg(reg),
+                            .immediate => |imm| lock: {
+                                if (imm == 0) continue;
+                                break :lock null;
+                            },
+                            else => null,
+                        };
+                        defer if (elem_lock) |lock| self.register_manager.unlockReg(lock);
+                        const elem_reg = try self.copyToTmpRegister(elem_ty, elem_mcv);
+                        const elem_extra_bits = self.regExtraBits(elem_ty);
+                        if (elem_bit_off < elem_extra_bits) {
+                            try self.truncateRegister(elem_ty, registerAlias(elem_reg, elem_abi_size));
+                        }
+                        if (elem_bit_off > 0) try self.genShiftBinOpMir(
+                            .sal,
+                            elem_ty,
+                            .{ .register = elem_reg },
+                            .{ .immediate = elem_bit_off },
+                        );
+                        try self.genBinOpMir(
+                            .@"or",
+                            elem_ty,
+                            .{ .stack_offset = stack_offset - elem_byte_off },
+                            .{ .register = elem_reg },
+                        );
+                        if (elem_bit_off > elem_extra_bits) {
+                            const reg = try self.copyToTmpRegister(elem_ty, elem_mcv);
+                            if (elem_extra_bits > 0) {
+                                try self.truncateRegister(elem_ty, registerAlias(reg, elem_abi_size));
+                            }
+                            try self.genShiftBinOpMir(
+                                .sar,
+                                elem_ty,
+                                .{ .register = reg },
+                                .{ .immediate = elem_abi_bits - elem_bit_off },
+                            );
+                            try self.genBinOpMir(
+                                .@"or",
+                                elem_ty,
+                                .{ .stack_offset = stack_offset - elem_byte_off -
+                                    @intCast(i32, elem_abi_size) },
+                                .{ .register = reg },
+                            );
+                        }
+                    }
+                } else for (elements, 0..) |elem, elem_i| {
+                    if (result_ty.structFieldValueComptime(elem_i) != null) continue;
 
                     const elem_ty = result_ty.structFieldType(elem_i);
-                    const elem_off = result_ty.structFieldOffset(elem_i, self.target.*);
+                    const elem_off = @intCast(i32, result_ty.structFieldOffset(elem_i, self.target.*));
                     const elem_mcv = try self.resolveInst(elem);
-                    try self.genSetStack(elem_ty, stack_offset - @intCast(i32, elem_off), elem_mcv, .{});
+                    try self.genSetStack(elem_ty, stack_offset - elem_off, elem_mcv, .{});
                 }
-                break :res MCValue{ .stack_offset = stack_offset };
+                break :res dst_mcv;
             },
             .Array => {
                 const stack_offset = @intCast(i32, try self.allocMem(inst, abi_size, abi_align));
test/behavior/packed-struct.zig
@@ -352,7 +352,6 @@ test "byte-aligned field pointer offsets" {
 }
 
 test "load pointer from packed struct" {
-    if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
     if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
     if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
     if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
@@ -585,7 +584,6 @@ test "overaligned pointer to packed struct" {
 test "packed struct initialized in bitcast" {
     if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
     if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
-    if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
 
     const T = packed struct { val: u8 };
     var val: u8 = 123;
test/behavior/struct.zig
@@ -1244,7 +1244,6 @@ test "loading a struct pointer perfoms a copy" {
 }
 
 test "packed struct aggregate init" {
-    if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
     if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
     if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
     if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO