Commit 6d2ec7a4e3

Andrew Kelley <andrew@ziglang.org>
2022-03-27 23:21:32
LLVM: handle aggregate_init for packed structs
1 parent 052079c
Changed files (3)
src
test
behavior
src/codegen/llvm/bindings.zig
@@ -448,6 +448,14 @@ pub const Builder = opaque {
         Name: [*:0]const u8,
     ) *const Value;
 
+    pub const buildZExtOrBitCast = LLVMBuildZExtOrBitCast;
+    extern fn LLVMBuildZExtOrBitCast(
+        *const Builder,
+        Val: *const Value,
+        DestTy: *const Type,
+        Name: [*:0]const u8,
+    ) *const Value;
+
     pub const buildSExt = LLVMBuildSExt;
     extern fn LLVMBuildSExt(
         *const Builder,
src/codegen/llvm.zig
@@ -6459,6 +6459,34 @@ pub const FuncGen = struct {
                 return vector;
             },
             .Struct => {
+                if (result_ty.containerLayout() == .Packed) {
+                    const struct_obj = result_ty.castTag(.@"struct").?.data;
+                    const big_bits = struct_obj.packedIntegerBits(target);
+                    const int_llvm_ty = self.dg.context.intType(big_bits);
+                    const fields = struct_obj.fields.values();
+                    comptime assert(Type.packed_struct_layout_version == 2);
+                    var running_int: *const llvm.Value = int_llvm_ty.constNull();
+                    var running_bits: u16 = 0;
+                    for (elements) |elem, i| {
+                        const field = fields[i];
+                        if (!field.ty.hasRuntimeBitsIgnoreComptime()) continue;
+
+                        const non_int_val = try self.resolveInst(elem);
+                        const ty_bit_size = @intCast(u16, field.ty.bitSize(target));
+                        const small_int_ty = self.dg.context.intType(ty_bit_size);
+                        const small_int_val = self.builder.buildBitCast(non_int_val, small_int_ty, "");
+                        const shift_rhs = int_llvm_ty.constInt(running_bits, .False);
+                        // If the field is as large as the entire packed struct, this
+                        // zext would go from, e.g. i16 to i16. This is legal with
+                        // constZExtOrBitCast but not legal with constZExt.
+                        const extended_int_val = self.builder.buildZExtOrBitCast(small_int_val, int_llvm_ty, "");
+                        const shifted = self.builder.buildShl(extended_int_val, shift_rhs, "");
+                        running_int = self.builder.buildOr(running_int, shifted, "");
+                        running_bits += ty_bit_size;
+                    }
+                    return running_int;
+                }
+
                 var ptr_ty_buf: Type.Payload.Pointer = undefined;
 
                 if (isByRef(result_ty)) {
test/behavior/struct.zig
@@ -1294,3 +1294,24 @@ test "loading a struct pointer perfoms a copy" {
     try expect(s2.b == 2);
     try expect(s2.c == 3);
 }
+
+test "packed struct aggregate init" {
+    if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
+
+    const S = struct {
+        fn foo(a: i2, b: i6) u8 {
+            return @bitCast(u8, P{ .a = a, .b = b });
+        }
+
+        const P = packed struct {
+            a: i2,
+            b: i6,
+        };
+    };
+    const result = @bitCast(u8, S.foo(1, 2));
+    try expect(result == 9);
+}