Commit 220708e7c3

Andrew Kelley <andrew@ziglang.org>
2022-03-02 21:35:15
LLVM: aggregate_init supports structs
in addition to tuples
1 parent 446324a
Changed files (3)
src/codegen/llvm.zig
@@ -2106,7 +2106,7 @@ pub const DeclGen = struct {
     fn llvmFieldIndex(
         dg: *DeclGen,
         ty: Type,
-        field_index: u32,
+        field_index: usize,
         ptr_pl_buf: *Type.Payload.Pointer,
     ) ?c_uint {
         const target = dg.module.getTarget();
@@ -4921,38 +4921,37 @@ pub const FuncGen = struct {
                 return vector;
             },
             .Struct => {
-                const tuple = result_ty.castTag(.tuple).?.data;
+                var ptr_ty_buf: Type.Payload.Pointer = undefined;
 
                 if (isByRef(result_ty)) {
                     const llvm_u32 = self.context.intType(32);
                     const alloca_inst = self.buildAlloca(llvm_result_ty);
+                    // TODO in debug builds init to undef so that the padding will be 0xaa
+                    // even if we fully populate the fields.
                     const target = self.dg.module.getTarget();
                     alloca_inst.setAlignment(result_ty.abiAlignment(target));
 
                     var indices: [2]*const llvm.Value = .{ llvm_u32.constNull(), undefined };
-                    var llvm_i: u32 = 0;
-
                     for (elements) |elem, i| {
-                        if (tuple.values[i].tag() != .unreachable_value) continue;
-                        const field_ty = tuple.types[i];
+                        if (result_ty.structFieldValueComptime(i) != null) continue;
+
                         const llvm_elem = try self.resolveInst(elem);
+                        const llvm_i = self.dg.llvmFieldIndex(result_ty, i, &ptr_ty_buf).?;
                         indices[1] = llvm_u32.constInt(llvm_i, .False);
-                        llvm_i += 1;
                         const field_ptr = self.builder.buildInBoundsGEP(alloca_inst, &indices, indices.len, "");
                         const store_inst = self.builder.buildStore(llvm_elem, field_ptr);
-                        store_inst.setAlignment(field_ty.abiAlignment(target));
+                        store_inst.setAlignment(result_ty.structFieldAlign(i, target));
                     }
 
                     return alloca_inst;
                 } else {
                     var result = llvm_result_ty.getUndef();
-                    var llvm_i: u32 = 0;
                     for (elements) |elem, i| {
-                        if (tuple.values[i].tag() != .unreachable_value) continue;
+                        if (result_ty.structFieldValueComptime(i) != null) continue;
 
                         const llvm_elem = try self.resolveInst(elem);
+                        const llvm_i = self.dg.llvmFieldIndex(result_ty, i, &ptr_ty_buf).?;
                         result = self.builder.buildInsertValue(result, llvm_elem, llvm_i, "");
-                        llvm_i += 1;
                     }
                     return result;
                 }
src/Sema.zig
@@ -394,11 +394,11 @@ pub const Block = struct {
 
     fn addAggregateInit(
         block: *Block,
-        vector_ty: Type,
+        aggregate_ty: Type,
         elements: []const Air.Inst.Ref,
     ) !Air.Inst.Ref {
         const sema = block.sema;
-        const ty_ref = try sema.addType(vector_ty);
+        const ty_ref = try sema.addType(aggregate_ty);
         try sema.air_extra.ensureUnusedCapacity(sema.gpa, elements.len);
         const extra_index = @intCast(u32, sema.air_extra.items.len);
         sema.appendRefsAssumeCapacity(elements);
src/type.zig
@@ -4479,6 +4479,55 @@ pub const Type = extern union {
         }
     }
 
+    pub fn structFieldAlign(ty: Type, index: usize, target: Target) u32 {
+        switch (ty.tag()) {
+            .@"struct" => {
+                const struct_obj = ty.castTag(.@"struct").?.data;
+                assert(struct_obj.layout != .Packed);
+                return struct_obj.fields.values()[index].normalAlignment(target);
+            },
+            .@"union", .union_tagged => {
+                const union_obj = ty.cast(Payload.Union).?.data;
+                return union_obj.fields.values()[index].normalAlignment(target);
+            },
+            .tuple => return ty.castTag(.tuple).?.data.types[index].abiAlignment(target),
+            .anon_struct => return ty.castTag(.anon_struct).?.data.types[index].abiAlignment(target),
+            else => unreachable,
+        }
+    }
+
+    pub fn structFieldValueComptime(ty: Type, index: usize) ?Value {
+        switch (ty.tag()) {
+            .@"struct" => {
+                const struct_obj = ty.castTag(.@"struct").?.data;
+                assert(struct_obj.layout != .Packed);
+                const field = struct_obj.fields.values()[index];
+                if (field.is_comptime) {
+                    return field.default_val;
+                } else {
+                    return null;
+                }
+            },
+            .tuple => {
+                const val = ty.castTag(.tuple).?.data.values[index];
+                if (val.tag() == .unreachable_value) {
+                    return null;
+                } else {
+                    return val;
+                }
+            },
+            .anon_struct => {
+                const val = ty.castTag(.anon_struct).?.data.values[index];
+                if (val.tag() == .unreachable_value) {
+                    return null;
+                } else {
+                    return val;
+                }
+            },
+            else => unreachable,
+        }
+    }
+
     pub const FieldOffset = struct {
         field: usize,
         offset: u64,