authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-09-29 14:04:52-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-09-29 14:04:52-07:00
logea6706b6f406606a7523e35e34e390fb880b607e
treee3d71639d007cbc528f13d30330fe04c0ac6cbf2
parent1d1f6a04214027da014cbc8eb780ff4c5e55f863

stage2: LLVM backend: implement struct type fwd decls

Makes struct types able to refer to themselves.

6 files changed, 154 insertions(+), 82 deletions(-)

src/Module.zig+1-1
......@@ -813,7 +813,7 @@ pub const Struct = struct {
813813 is_comptime: bool,
814814 };
815815
816 pub fn getFullyQualifiedName(s: *Struct, gpa: *Allocator) ![]u8 {
816 pub fn getFullyQualifiedName(s: *Struct, gpa: *Allocator) ![:0]u8 {
817817 return s.owner_decl.getFullyQualifiedName(gpa);
818818 }
819819
src/codegen/llvm.zig+99-38
......@@ -164,9 +164,25 @@ pub const Object = struct {
164164 /// * it works for functions not all globals.
165165 /// Therefore, this table keeps track of the mapping.
166166 decl_map: std.AutoHashMapUnmanaged(*const Module.Decl, *const llvm.Value),
167 /// Maps Zig types to LLVM types. The table memory itself is backed by the GPA of
168 /// the compiler, but the Type/Value memory here is backed by `type_map_arena`.
169 /// TODO we need to remove entries from this map in response to incremental compilation
170 /// but I think the frontend won't tell us about types that get deleted because
171 /// hasCodeGenBits() is false for types.
172 type_map: TypeMap,
173 /// The backing memory for `type_map`. Periodically garbage collected after flush().
174 /// The code for doing the periodical GC is not yet implemented.
175 type_map_arena: std.heap.ArenaAllocator,
167176 /// Where to put the output object file, relative to bin_file.options.emit directory.
168177 sub_path: []const u8,
169178
179 pub const TypeMap = std.HashMapUnmanaged(
180 Type,
181 *const llvm.Type,
182 Type.HashContext64,
183 std.hash_map.default_max_load_percentage,
184 );
185
170186 pub fn create(gpa: *Allocator, sub_path: []const u8, options: link.Options) !*Object {
171187 const obj = try gpa.create(Object);
172188 errdefer gpa.destroy(obj);
......@@ -253,6 +269,8 @@ pub const Object = struct {
253269 .context = context,
254270 .target_machine = target_machine,
255271 .decl_map = .{},
272 .type_map = .{},
273 .type_map_arena = std.heap.ArenaAllocator.init(gpa),
256274 .sub_path = sub_path,
257275 };
258276 }
......@@ -262,6 +280,8 @@ pub const Object = struct {
262280 self.llvm_module.dispose();
263281 self.context.dispose();
264282 self.decl_map.deinit(gpa);
283 self.type_map.deinit(gpa);
284 self.type_map_arena.deinit();
265285 self.* = undefined;
266286 }
267287
......@@ -725,10 +745,10 @@ pub const DeclGen = struct {
725745 }
726746
727747 fn llvmType(self: *DeclGen, t: Type) error{ OutOfMemory, CodegenFail }!*const llvm.Type {
748 const gpa = self.gpa;
728749 log.debug("llvmType for {}", .{t});
729750 switch (t.zigTypeTag()) {
730 .Void => return self.context.voidType(),
731 .NoReturn => return self.context.voidType(),
751 .Void, .NoReturn => return self.context.voidType(),
732752 .Int => {
733753 const info = t.intInfo(self.module.getTarget());
734754 return self.context.intType(info.bits);
......@@ -799,18 +819,38 @@ pub const DeclGen = struct {
799819 return self.context.intType(16);
800820 },
801821 .Struct => {
822 const gop = try self.object.type_map.getOrPut(gpa, t);
823 if (gop.found_existing) return gop.value_ptr.*;
824
825 // The Type memory is ephemeral; since we want to store a longer-lived
826 // reference, we need to copy it here.
827 gop.key_ptr.* = try t.copy(&self.object.type_map_arena.allocator);
828
802829 const struct_obj = t.castTag(.@"struct").?.data;
803830 assert(struct_obj.haveFieldTypes());
804 const llvm_fields = try self.gpa.alloc(*const llvm.Type, struct_obj.fields.count());
805 defer self.gpa.free(llvm_fields);
806 for (struct_obj.fields.values()) |field, i| {
807 llvm_fields[i] = try self.llvmType(field.ty);
831
832 const name = try struct_obj.getFullyQualifiedName(gpa);
833 defer gpa.free(name);
834
835 const llvm_struct_ty = self.context.structCreateNamed(name);
836 gop.value_ptr.* = llvm_struct_ty; // must be done before any recursive calls
837
838 var llvm_field_types: std.ArrayListUnmanaged(*const llvm.Type) = .{};
839 try llvm_field_types.ensureTotalCapacity(gpa, struct_obj.fields.count());
840 defer llvm_field_types.deinit(gpa);
841
842 for (struct_obj.fields.values()) |field| {
843 if (!field.ty.hasCodeGenBits()) continue;
844 llvm_field_types.appendAssumeCapacity(try self.llvmType(field.ty));
808845 }
809 return self.context.structType(
810 llvm_fields.ptr,
811 @intCast(c_uint, llvm_fields.len),
812 .False,
846
847 llvm_struct_ty.structSetBody(
848 llvm_field_types.items.ptr,
849 @intCast(c_uint, llvm_field_types.items.len),
850 llvm.Bool.fromBool(struct_obj.layout == .Packed),
813851 );
852
853 return llvm_struct_ty;
814854 },
815855 .Union => {
816856 const union_obj = t.castTag(.@"union").?.data;
......@@ -838,8 +878,8 @@ pub const DeclGen = struct {
838878 .Fn => {
839879 const ret_ty = try self.llvmType(t.fnReturnType());
840880 const params_len = t.fnParamLen();
841 const llvm_params = try self.gpa.alloc(*const llvm.Type, params_len);
842 defer self.gpa.free(llvm_params);
881 const llvm_params = try gpa.alloc(*const llvm.Type, params_len);
882 defer gpa.free(llvm_params);
843883 for (llvm_params) |*llvm_param, i| {
844884 llvm_param.* = try self.llvmType(t.fnParamType(i));
845885 }
......@@ -1073,21 +1113,26 @@ pub const DeclGen = struct {
10731113 return self.context.constStruct(&fields, fields.len, .False);
10741114 },
10751115 .Struct => {
1076 const fields_len = tv.ty.structFieldCount();
1116 const llvm_struct_ty = try self.llvmType(tv.ty);
10771117 const field_vals = tv.val.castTag(.@"struct").?.data;
10781118 const gpa = self.gpa;
1079 const llvm_fields = try gpa.alloc(*const llvm.Value, fields_len);
1080 defer gpa.free(llvm_fields);
1081 for (llvm_fields) |*llvm_field, i| {
1082 llvm_field.* = try self.genTypedValue(.{
1083 .ty = tv.ty.structFieldType(i),
1084 .val = field_vals[i],
1085 });
1119
1120 var llvm_fields: std.ArrayListUnmanaged(*const llvm.Value) = .{};
1121 try llvm_fields.ensureTotalCapacity(gpa, field_vals.len);
1122 defer llvm_fields.deinit(gpa);
1123
1124 for (field_vals) |field_val, i| {
1125 const field_ty = tv.ty.structFieldType(i);
1126 if (!field_ty.hasCodeGenBits()) continue;
1127
1128 llvm_fields.appendAssumeCapacity(try self.genTypedValue(.{
1129 .ty = field_ty,
1130 .val = field_val,
1131 }));
10861132 }
1087 return self.context.constStruct(
1088 llvm_fields.ptr,
1089 @intCast(c_uint, llvm_fields.len),
1090 .False,
1133 return llvm_struct_ty.constNamedStruct(
1134 llvm_fields.items.ptr,
1135 @intCast(c_uint, llvm_fields.items.len),
10911136 );
10921137 },
10931138 .ComptimeInt => unreachable,
......@@ -1692,13 +1737,15 @@ pub const FuncGen = struct {
16921737 const struct_field = self.air.extraData(Air.StructField, ty_pl.payload).data;
16931738 const struct_ptr = try self.resolveInst(struct_field.struct_operand);
16941739 const struct_ptr_ty = self.air.typeOf(struct_field.struct_operand);
1695 const field_index = @intCast(c_uint, struct_field.field_index);
1696 return self.fieldPtr(inst, struct_ptr, struct_ptr_ty, field_index);
1740 return self.fieldPtr(inst, struct_ptr, struct_ptr_ty, struct_field.field_index);
16971741 }
16981742
1699 fn airStructFieldPtrIndex(self: *FuncGen, inst: Air.Inst.Index, field_index: c_uint) !?*const llvm.Value {
1700 if (self.liveness.isUnused(inst))
1701 return null;
1743 fn airStructFieldPtrIndex(
1744 self: *FuncGen,
1745 inst: Air.Inst.Index,
1746 field_index: u32,
1747 ) !?*const llvm.Value {
1748 if (self.liveness.isUnused(inst)) return null;
17021749
17031750 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
17041751 const struct_ptr = try self.resolveInst(ty_op.operand);
......@@ -1707,13 +1754,13 @@ pub const FuncGen = struct {
17071754 }
17081755
17091756 fn airStructFieldVal(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
1710 if (self.liveness.isUnused(inst))
1711 return null;
1757 if (self.liveness.isUnused(inst)) return null;
17121758
17131759 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
17141760 const struct_field = self.air.extraData(Air.StructField, ty_pl.payload).data;
1761 const struct_ty = self.air.typeOf(struct_field.struct_operand);
17151762 const struct_byval = try self.resolveInst(struct_field.struct_operand);
1716 const field_index = @intCast(c_uint, struct_field.field_index);
1763 const field_index = llvmFieldIndex(struct_ty, struct_field.field_index);
17171764 return self.builder.buildExtractValue(struct_byval, field_index, "");
17181765 }
17191766
......@@ -2643,8 +2690,7 @@ pub const FuncGen = struct {
26432690 const fill_char = if (val_is_undef) u8_llvm_ty.constInt(0xaa, .False) else value;
26442691 const target = self.dg.module.getTarget();
26452692 const dest_ptr_align = ptr_ty.ptrAlignment(target);
2646 const memset = self.builder.buildMemSet(dest_ptr_u8, fill_char, len, dest_ptr_align);
2647 memset.setVolatile(llvm.Bool.fromBool(ptr_ty.isVolatilePtr()));
2693 _ = self.builder.buildMemSet(dest_ptr_u8, fill_char, len, dest_ptr_align, ptr_ty.isVolatilePtr());
26482694
26492695 if (val_is_undef and self.dg.module.comp.bin_file.options.valgrind) {
26502696 // TODO generate valgrind client request to mark byte range as undefined
......@@ -2667,14 +2713,14 @@ pub const FuncGen = struct {
26672713 const src_ptr_u8 = self.builder.buildBitCast(src_ptr, ptr_u8_llvm_ty, "");
26682714 const is_volatile = src_ptr_ty.isVolatilePtr() or dest_ptr_ty.isVolatilePtr();
26692715 const target = self.dg.module.getTarget();
2670 const memcpy = self.builder.buildMemCpy(
2716 _ = self.builder.buildMemCpy(
26712717 dest_ptr_u8,
26722718 dest_ptr_ty.ptrAlignment(target),
26732719 src_ptr_u8,
26742720 src_ptr_ty.ptrAlignment(target),
26752721 len,
2722 is_volatile,
26762723 );
2677 memcpy.setVolatile(llvm.Bool.fromBool(is_volatile));
26782724 return null;
26792725 }
26802726
......@@ -2741,11 +2787,14 @@ pub const FuncGen = struct {
27412787 inst: Air.Inst.Index,
27422788 struct_ptr: *const llvm.Value,
27432789 struct_ptr_ty: Type,
2744 field_index: c_uint,
2790 field_index: u32,
27452791 ) !?*const llvm.Value {
27462792 const struct_ty = struct_ptr_ty.childType();
27472793 switch (struct_ty.zigTypeTag()) {
2748 .Struct => return self.builder.buildStructGEP(struct_ptr, field_index, ""),
2794 .Struct => {
2795 const llvm_field_index = llvmFieldIndex(struct_ty, field_index);
2796 return self.builder.buildStructGEP(struct_ptr, llvm_field_index, "");
2797 },
27492798 .Union => return self.unionFieldPtr(inst, struct_ptr, struct_ty, field_index),
27502799 else => unreachable,
27512800 }
......@@ -2968,3 +3017,15 @@ fn toLlvmAtomicRmwBinOp(
29683017 .Min => if (is_signed) llvm.AtomicRMWBinOp.Min else return .UMin,
29693018 };
29703019}
3020
3021/// Take into account 0 bit fields.
3022fn llvmFieldIndex(ty: Type, index: u32) c_uint {
3023 const struct_obj = ty.castTag(.@"struct").?.data;
3024 var result: c_uint = 0;
3025 for (struct_obj.fields.values()[0..index]) |field| {
3026 if (field.ty.hasCodeGenBits()) {
3027 result += 1;
3028 }
3029 }
3030 return result;
3031}
src/codegen/llvm/bindings.zig+13-4
......@@ -181,6 +181,13 @@ pub const Type = opaque {
181181 pub const constArray = LLVMConstArray;
182182 extern fn LLVMConstArray(ElementTy: *const Type, ConstantVals: [*]*const Value, Length: c_uint) *const Value;
183183
184 pub const constNamedStruct = LLVMConstNamedStruct;
185 extern fn LLVMConstNamedStruct(
186 StructTy: *const Type,
187 ConstantVals: [*]const *const Value,
188 Count: c_uint,
189 ) *const Value;
190
184191 pub const getUndef = LLVMGetUndef;
185192 extern fn LLVMGetUndef(Ty: *const Type) *const Value;
186193
......@@ -666,23 +673,25 @@ pub const Builder = opaque {
666673 Name: [*:0]const u8,
667674 ) *const Value;
668675
669 pub const buildMemSet = LLVMBuildMemSet;
670 extern fn LLVMBuildMemSet(
676 pub const buildMemSet = ZigLLVMBuildMemSet;
677 extern fn ZigLLVMBuildMemSet(
671678 B: *const Builder,
672679 Ptr: *const Value,
673680 Val: *const Value,
674681 Len: *const Value,
675682 Align: c_uint,
683 is_volatile: bool,
676684 ) *const Value;
677685
678 pub const buildMemCpy = LLVMBuildMemCpy;
679 extern fn LLVMBuildMemCpy(
686 pub const buildMemCpy = ZigLLVMBuildMemCpy;
687 extern fn ZigLLVMBuildMemCpy(
680688 B: *const Builder,
681689 Dst: *const Value,
682690 DstAlign: c_uint,
683691 Src: *const Value,
684692 SrcAlign: c_uint,
685693 Size: *const Value,
694 is_volatile: bool,
686695 ) *const Value;
687696};
688697
src/type.zig+2
......@@ -1785,6 +1785,8 @@ pub const Type = extern union {
17851785 if (is_packed) @panic("TODO packed structs");
17861786 var size: u64 = 0;
17871787 for (s.fields.values()) |field| {
1788 if (!field.ty.hasCodeGenBits()) continue;
1789
17881790 const field_align = a: {
17891791 if (field.abi_align.tag() == .abi_align_default) {
17901792 break :a field.ty.abiAlignment(target);
test/behavior/struct.zig+39
......@@ -90,3 +90,42 @@ test "call member function directly" {
9090 const result = MemberFnTestFoo.member(instance);
9191 try expect(result == 1234);
9292}
93
94test "struct point to self" {
95 var root: Node = undefined;
96 root.val.x = 1;
97
98 var node: Node = undefined;
99 node.next = &root;
100 node.val.x = 2;
101
102 root.next = &node;
103
104 try expect(node.next.next.next.val.x == 1);
105}
106
107test "void struct fields" {
108 const foo = VoidStructFieldsFoo{
109 .a = void{},
110 .b = 1,
111 .c = void{},
112 };
113 try expect(foo.b == 1);
114 try expect(@sizeOf(VoidStructFieldsFoo) == 4);
115}
116const VoidStructFieldsFoo = struct {
117 a: void,
118 b: i32,
119 c: void,
120};
121
122test "member functions" {
123 const r = MemberFnRand{ .seed = 1234 };
124 try expect(r.getSeed() == 1234);
125}
126const MemberFnRand = struct {
127 seed: u32,
128 pub fn getSeed(r: *const MemberFnRand) u32 {
129 return r.seed;
130 }
131};
test/behavior/struct_stage1.zig-39
......@@ -16,21 +16,6 @@ test "top level fields" {
1616 try expectEqual(@as(i32, 1235), instance.top_level_field);
1717}
1818
19test "void struct fields" {
20 const foo = VoidStructFieldsFoo{
21 .a = void{},
22 .b = 1,
23 .c = void{},
24 };
25 try expect(foo.b == 1);
26 try expect(@sizeOf(VoidStructFieldsFoo) == 4);
27}
28const VoidStructFieldsFoo = struct {
29 a: void,
30 b: i32,
31 c: void,
32};
33
3419const StructFoo = struct {
3520 a: i32,
3621 b: bool,
......@@ -46,19 +31,6 @@ const Val = struct {
4631 x: i32,
4732};
4833
49test "struct point to self" {
50 var root: Node = undefined;
51 root.val.x = 1;
52
53 var node: Node = undefined;
54 node.next = &root;
55 node.val.x = 2;
56
57 root.next = &node;
58
59 try expect(node.next.next.next.val.x == 1);
60}
61
6234test "fn call of struct field" {
6335 const Foo = struct {
6436 ptr: fn () i32,
......@@ -89,17 +61,6 @@ test "store member function in variable" {
8961 try expect(result == 1234);
9062}
9163
92test "member functions" {
93 const r = MemberFnRand{ .seed = 1234 };
94 try expect(r.getSeed() == 1234);
95}
96const MemberFnRand = struct {
97 seed: u32,
98 pub fn getSeed(r: *const MemberFnRand) u32 {
99 return r.seed;
100 }
101};
102
10364test "return struct byval from function" {
10465 const bar = makeBar2(1234, 5678);
10566 try expect(bar.y == 5678);