Skip to content

Commit 5913140

Browse files
committed
stage2: free Sema's arena after generating machine code
Previously, linker backends or machine code backends were able to hold on to references to inside Sema's temporary arena. However there can be large objects stored there that we want to free after machine code is generated. The primary change in this commit is to use a temporary arena for Sema of function bodies that gets freed after machine code backend finishes handling `updateFunc` (at the same time that Air and Liveness get freed). The other changes in this commit are fixing issues that fell out from the primary change. * The C linker backend is rewritten to handle updateDecl and updateFunc separately. Also, all Decl updates get access to typedefs and fwd_decls, not only functions. * The C linker backend is updated to the new API that does not depend on allocateDeclIndexes and does not have to handle garbage collected decls. * The C linker backend uses an arena for Type/Value objects that `typedefs` references. These can be garbage collected every so often after flush(), however that garbage collection code is not implemented at this time. It will be pretty simple, just allocate a new arena, copy all the Type objects to it, update the keys of the hash map, free the old arena. * Sema: fix a handful of instances of not copying Type/Value objects from the temporary arena into the appropriate Decl arena. * Type: fix some function types not reporting hasCodeGenBits() correctly.
1 parent affd8f8 commit 5913140

File tree

7 files changed

+773
-662
lines changed

7 files changed

+773
-662
lines changed

src/Compilation.zig

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2145,7 +2145,11 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
21452145
const module = self.bin_file.options.module.?;
21462146
const decl = func.owner_decl;
21472147

2148-
var air = module.analyzeFnBody(decl, func) catch |err| switch (err) {
2148+
var tmp_arena = std.heap.ArenaAllocator.init(gpa);
2149+
defer tmp_arena.deinit();
2150+
const sema_arena = &tmp_arena.allocator;
2151+
2152+
var air = module.analyzeFnBody(decl, func, sema_arena) catch |err| switch (err) {
21492153
error.AnalysisFail => {
21502154
assert(func.state != .in_progress);
21512155
continue;
@@ -2207,16 +2211,20 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
22072211
const decl_emit_h = decl.getEmitH(module);
22082212
const fwd_decl = &decl_emit_h.fwd_decl;
22092213
fwd_decl.shrinkRetainingCapacity(0);
2214+
var typedefs_arena = std.heap.ArenaAllocator.init(gpa);
2215+
defer typedefs_arena.deinit();
22102216

22112217
var dg: c_codegen.DeclGen = .{
2218+
.gpa = gpa,
22122219
.module = module,
22132220
.error_msg = null,
22142221
.decl = decl,
22152222
.fwd_decl = fwd_decl.toManaged(gpa),
2216-
// we don't want to emit optionals and error unions to headers since they have no ABI
2217-
.typedefs = undefined,
2223+
.typedefs = c_codegen.TypedefMap.init(gpa),
2224+
.typedefs_arena = &typedefs_arena.allocator,
22182225
};
22192226
defer dg.fwd_decl.deinit();
2227+
defer dg.typedefs.deinit();
22202228

22212229
c_codegen.genHeader(&dg) catch |err| switch (err) {
22222230
error.AnalysisFail => {

src/Module.zig

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -610,7 +610,7 @@ pub const Decl = struct {
610610

611611
/// If the Decl has a value and it is a function, return it,
612612
/// otherwise null.
613-
pub fn getFunction(decl: *Decl) ?*Fn {
613+
pub fn getFunction(decl: *const Decl) ?*Fn {
614614
if (!decl.owns_tv) return null;
615615
const func = (decl.val.castTag(.function) orelse return null).data;
616616
assert(func.owner_decl == decl);
@@ -3789,7 +3789,7 @@ pub fn clearDecl(
37893789
.elf => .{ .elf = link.File.Elf.TextBlock.empty },
37903790
.macho => .{ .macho = link.File.MachO.TextBlock.empty },
37913791
.plan9 => .{ .plan9 = link.File.Plan9.DeclBlock.empty },
3792-
.c => .{ .c = link.File.C.DeclBlock.empty },
3792+
.c => .{ .c = {} },
37933793
.wasm => .{ .wasm = link.File.Wasm.DeclBlock.empty },
37943794
.spirv => .{ .spirv = {} },
37953795
};
@@ -3798,7 +3798,7 @@ pub fn clearDecl(
37983798
.elf => .{ .elf = link.File.Elf.SrcFn.empty },
37993799
.macho => .{ .macho = link.File.MachO.SrcFn.empty },
38003800
.plan9 => .{ .plan9 = {} },
3801-
.c => .{ .c = link.File.C.FnBlock.empty },
3801+
.c => .{ .c = {} },
38023802
.wasm => .{ .wasm = link.File.Wasm.FnData.empty },
38033803
.spirv => .{ .spirv = .{} },
38043804
};
@@ -3828,10 +3828,13 @@ pub fn deleteUnusedDecl(mod: *Module, decl: *Decl) void {
38283828
// about the Decl in the first place.
38293829
// Until then, we did call `allocateDeclIndexes` on this anonymous Decl and so we
38303830
// must call `freeDecl` in the linker backend now.
3831-
if (decl.has_tv) {
3832-
if (decl.ty.hasCodeGenBits()) {
3833-
mod.comp.bin_file.freeDecl(decl);
3834-
}
3831+
switch (mod.comp.bin_file.tag) {
3832+
.c => {}, // this linker backend has already migrated to the new API
3833+
else => if (decl.has_tv) {
3834+
if (decl.ty.hasCodeGenBits()) {
3835+
mod.comp.bin_file.freeDecl(decl);
3836+
}
3837+
},
38353838
}
38363839

38373840
const dependants = decl.dependants.keys();
@@ -3893,22 +3896,16 @@ fn deleteDeclExports(mod: *Module, decl: *Decl) void {
38933896
mod.gpa.free(kv.value);
38943897
}
38953898

3896-
pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) SemaError!Air {
3899+
pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn, arena: *Allocator) SemaError!Air {
38973900
const tracy = trace(@src());
38983901
defer tracy.end();
38993902

39003903
const gpa = mod.gpa;
39013904

3902-
// Use the Decl's arena for function memory.
3903-
var arena = decl.value_arena.?.promote(gpa);
3904-
defer decl.value_arena.?.* = arena.state;
3905-
3906-
const fn_ty = decl.ty;
3907-
39083905
var sema: Sema = .{
39093906
.mod = mod,
39103907
.gpa = gpa,
3911-
.arena = &arena.allocator,
3908+
.arena = arena,
39123909
.code = decl.namespace.file_scope.zir,
39133910
.owner_decl = decl,
39143911
.namespace = decl.namespace,
@@ -3942,6 +3939,7 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) SemaError!Air {
39423939
// This could be a generic function instantiation, however, in which case we need to
39433940
// map the comptime parameters to constant values and only emit arg AIR instructions
39443941
// for the runtime ones.
3942+
const fn_ty = decl.ty;
39453943
const runtime_params_len = @intCast(u32, fn_ty.fnParamLen());
39463944
try inner_block.instructions.ensureTotalCapacity(gpa, runtime_params_len);
39473945
try sema.air_instructions.ensureUnusedCapacity(gpa, fn_info.total_params_len * 2); // * 2 for the `addType`
@@ -4072,7 +4070,7 @@ pub fn allocateNewDecl(mod: *Module, namespace: *Scope.Namespace, src_node: Ast.
40724070
.elf => .{ .elf = link.File.Elf.TextBlock.empty },
40734071
.macho => .{ .macho = link.File.MachO.TextBlock.empty },
40744072
.plan9 => .{ .plan9 = link.File.Plan9.DeclBlock.empty },
4075-
.c => .{ .c = link.File.C.DeclBlock.empty },
4073+
.c => .{ .c = {} },
40764074
.wasm => .{ .wasm = link.File.Wasm.DeclBlock.empty },
40774075
.spirv => .{ .spirv = {} },
40784076
},
@@ -4081,7 +4079,7 @@ pub fn allocateNewDecl(mod: *Module, namespace: *Scope.Namespace, src_node: Ast.
40814079
.elf => .{ .elf = link.File.Elf.SrcFn.empty },
40824080
.macho => .{ .macho = link.File.MachO.SrcFn.empty },
40834081
.plan9 => .{ .plan9 = {} },
4084-
.c => .{ .c = link.File.C.FnBlock.empty },
4082+
.c => .{ .c = {} },
40854083
.wasm => .{ .wasm = link.File.Wasm.FnData.empty },
40864084
.spirv => .{ .spirv = .{} },
40874085
},

src/Sema.zig

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2999,6 +2999,8 @@ fn analyzeCall(
29992999

30003000
// TODO: check whether any external comptime memory was mutated by the
30013001
// comptime function call. If so, then do not memoize the call here.
3002+
// TODO: re-evaluate whether memoized_calls needs its own arena. I think
3003+
// it should be fine to use the Decl arena for the function.
30023004
{
30033005
var arena_allocator = std.heap.ArenaAllocator.init(gpa);
30043006
errdefer arena_allocator.deinit();
@@ -3009,7 +3011,7 @@ fn analyzeCall(
30093011
}
30103012

30113013
try mod.memoized_calls.put(gpa, memoized_call_key, .{
3012-
.val = result_val,
3014+
.val = try result_val.copy(arena),
30133015
.arena = arena_allocator.state,
30143016
});
30153017
delete_memoized_call_key = false;
@@ -5876,10 +5878,7 @@ fn zirArrayCat(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr
58765878
else
58775879
try Type.Tag.array.create(anon_decl.arena(), .{ .len = final_len, .elem_type = lhs_info.elem_type });
58785880
const val = try Value.Tag.array.create(anon_decl.arena(), buf);
5879-
return sema.analyzeDeclRef(try anon_decl.finish(
5880-
ty,
5881-
val,
5882-
));
5881+
return sema.analyzeDeclRef(try anon_decl.finish(ty, val));
58835882
}
58845883
return sema.mod.fail(&block.base, lhs_src, "TODO array_cat more types of Values", .{});
58855884
} else {
@@ -5941,10 +5940,7 @@ fn zirArrayMul(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr
59415940
}
59425941
}
59435942
const val = try Value.Tag.array.create(anon_decl.arena(), buf);
5944-
return sema.analyzeDeclRef(try anon_decl.finish(
5945-
final_ty,
5946-
val,
5947-
));
5943+
return sema.analyzeDeclRef(try anon_decl.finish(final_ty, val));
59485944
}
59495945
return sema.mod.fail(&block.base, lhs_src, "TODO array_mul more types of Values", .{});
59505946
}
@@ -9979,7 +9975,7 @@ fn analyzeRef(
99799975
var anon_decl = try block.startAnonDecl();
99809976
defer anon_decl.deinit();
99819977
return sema.analyzeDeclRef(try anon_decl.finish(
9982-
operand_ty,
9978+
try operand_ty.copy(anon_decl.arena()),
99839979
try val.copy(anon_decl.arena()),
99849980
));
99859981
}

0 commit comments

Comments
 (0)