Skip to content

Commit ea6706b

Browse files
committed
stage2: LLVM backend: implement struct type fwd decls
Makes struct types able to refer to themselves.
1 parent 1d1f6a0 commit ea6706b

File tree

6 files changed

+154
-82
lines changed

6 files changed

+154
-82
lines changed

src/Module.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -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

Lines changed: 99 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -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.
3022+
fn 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

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,3 +90,42 @@ test "call member function directly" {
9090
const result = MemberFnTestFoo.member(instance);
9191
try expect(result == 1234);
9292
}
93+
94+
test "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+
107+
test "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+
}
116+
const VoidStructFieldsFoo = struct {
117+
a: void,
118+
b: i32,
119+
c: void,
120+
};
121+
122+
test "member functions" {
123+
const r = MemberFnRand{ .seed = 1234 };
124+
try expect(r.getSeed() == 1234);
125+
}
126+
const MemberFnRand = struct {
127+
seed: u32,
128+
pub fn getSeed(r: *const MemberFnRand) u32 {
129+
return r.seed;
130+
}
131+
};

0 commit comments

Comments
 (0)