authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-09-26 18:36:57-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-09-27 04:09:22-07:00
log1606717b5fed83ee64ba1a91e55248e07a51afa6
treea7a1de9fe9ff8ddac0c8b920c6fabd0598434eaf
parent70563aeac3e9efee7e8fb24744bc97197fc0ca9b

C backend: flatten out some of the long-lived state

When the compiler's state lives through multiple Compilation.update() calls, the C backend stores the rendered C source code for each decl code body and forward declarations. With this commit, the state is still stored, but it is managed in one big array list in link/C.zig rather than many array lists, one for each decl. This means simpler serialization and deserialization.

2 files changed, 95 insertions(+), 53 deletions(-)

src/codegen/c.zig+2-2
......@@ -489,9 +489,7 @@ pub const Function = struct {
489489 f.blocks.deinit(gpa);
490490 f.value_map.deinit();
491491 f.lazy_fns.deinit(gpa);
492 f.object.code.deinit();
493492 f.object.dg.ctypes.deinit(gpa);
494 f.object.dg.fwd_decl.deinit();
495493 }
496494
497495 fn typeOf(f: *Function, inst: Air.Inst.Ref) Type {
......@@ -509,6 +507,7 @@ pub const Function = struct {
509507/// It is not available when generating .h file.
510508pub const Object = struct {
511509 dg: DeclGen,
510 /// This is a borrowed reference from `link.C`.
512511 code: std.ArrayList(u8),
513512 /// Goes before code. Initialized and deinitialized in `genFunc`.
514513 code_header: std.ArrayList(u8) = undefined,
......@@ -525,6 +524,7 @@ pub const DeclGen = struct {
525524 module: *Module,
526525 decl: ?*Decl,
527526 decl_index: Decl.OptionalIndex,
527 /// This is a borrowed reference from `link.C`.
528528 fwd_decl: std.ArrayList(u8),
529529 error_msg: ?*Module.ErrorMsg,
530530 ctypes: CType.Store,
src/link/C.zig+93-51
......@@ -23,11 +23,39 @@ base: link.File,
2323/// Instead, it tracks all declarations in this table, and iterates over it
2424/// in the flush function, stitching pre-rendered pieces of C code together.
2525decl_table: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, DeclBlock) = .{},
26/// All the string bytes of rendered C code, all squished into one array.
27/// While in progress, a separate buffer is used, and then when finished, the
28/// buffer is copied into this one.
29string_bytes: std.ArrayListUnmanaged(u8) = .{},
30
31/// Optimization, `updateDecl` reuses this buffer rather than creating a new
32/// one with every call.
33fwd_decl_buf: std.ArrayListUnmanaged(u8) = .{},
34/// Optimization, `updateDecl` reuses this buffer rather than creating a new
35/// one with every call.
36code_buf: std.ArrayListUnmanaged(u8) = .{},
37/// Optimization, `flush` reuses this buffer rather than creating a new
38/// one with every call.
39lazy_fwd_decl_buf: std.ArrayListUnmanaged(u8) = .{},
40/// Optimization, `flush` reuses this buffer rather than creating a new
41/// one with every call.
42lazy_code_buf: std.ArrayListUnmanaged(u8) = .{},
43
44/// A reference into `string_bytes`.
45const String = struct {
46 start: u32,
47 len: u32,
48
49 const empty: String = .{
50 .start = 0,
51 .len = 0,
52 };
53};
2654
2755/// Per-declaration data.
2856const DeclBlock = struct {
29 code: std.ArrayListUnmanaged(u8) = .{},
30 fwd_decl: std.ArrayListUnmanaged(u8) = .{},
57 code: String = String.empty,
58 fwd_decl: String = String.empty,
3159 /// Each `Decl` stores a set of used `CType`s. In `flush()`, we iterate
3260 /// over each `Decl` and generate the definition for each used `CType` once.
3361 ctypes: codegen.CType.Store = .{},
......@@ -37,12 +65,23 @@ const DeclBlock = struct {
3765 fn deinit(db: *DeclBlock, gpa: Allocator) void {
3866 db.lazy_fns.deinit(gpa);
3967 db.ctypes.deinit(gpa);
40 db.fwd_decl.deinit(gpa);
41 db.code.deinit(gpa);
4268 db.* = undefined;
4369 }
4470};
4571
72pub fn getString(this: C, s: String) []const u8 {
73 return this.string_bytes.items[s.start..][0..s.len];
74}
75
76pub fn addString(this: *C, s: []const u8) Allocator.Error!String {
77 const gpa = this.base.allocator;
78 try this.string_bytes.appendSlice(gpa, s);
79 return .{
80 .start = @intCast(this.string_bytes.items.len - s.len),
81 .len = @intCast(s.len),
82 };
83}
84
4685pub fn openPath(gpa: Allocator, sub_path: []const u8, options: link.Options) !*C {
4786 assert(options.target.ofmt == .c);
4887
......@@ -78,6 +117,10 @@ pub fn deinit(self: *C) void {
78117 db.deinit(gpa);
79118 }
80119 self.decl_table.deinit(gpa);
120
121 self.string_bytes.deinit(gpa);
122 self.fwd_decl_buf.deinit(gpa);
123 self.code_buf.deinit(gpa);
81124}
82125
83126pub fn freeDecl(self: *C, decl_index: Module.Decl.Index) void {
......@@ -102,12 +145,12 @@ pub fn updateFunc(self: *C, module: *Module, func_index: InternPool.Index, air:
102145 }
103146 const ctypes = &gop.value_ptr.ctypes;
104147 const lazy_fns = &gop.value_ptr.lazy_fns;
105 const fwd_decl = &gop.value_ptr.fwd_decl;
106 const code = &gop.value_ptr.code;
148 const fwd_decl = &self.fwd_decl_buf;
149 const code = &self.code_buf;
107150 ctypes.clearRetainingCapacity(gpa);
108151 lazy_fns.clearRetainingCapacity();
109 fwd_decl.shrinkRetainingCapacity(0);
110 code.shrinkRetainingCapacity(0);
152 fwd_decl.clearRetainingCapacity();
153 code.clearRetainingCapacity();
111154
112155 var function: codegen.Function = .{
113156 .value_map = codegen.CValueMap.init(gpa),
......@@ -131,7 +174,11 @@ pub fn updateFunc(self: *C, module: *Module, func_index: InternPool.Index, air:
131174 };
132175
133176 function.object.indent_writer = .{ .underlying_writer = function.object.code.writer() };
134 defer function.deinit();
177 defer {
178 fwd_decl.* = function.object.dg.fwd_decl.moveToUnmanaged();
179 code.* = function.object.code.moveToUnmanaged();
180 function.deinit();
181 }
135182
136183 codegen.genFunc(&function) catch |err| switch (err) {
137184 error.AnalysisFail => {
......@@ -143,14 +190,13 @@ pub fn updateFunc(self: *C, module: *Module, func_index: InternPool.Index, air:
143190
144191 ctypes.* = function.object.dg.ctypes.move();
145192 lazy_fns.* = function.lazy_fns.move();
146 fwd_decl.* = function.object.dg.fwd_decl.moveToUnmanaged();
147 code.* = function.object.code.moveToUnmanaged();
148193
149194 // Free excess allocated memory for this Decl.
150195 ctypes.shrinkAndFree(gpa, ctypes.count());
151196 lazy_fns.shrinkAndFree(gpa, lazy_fns.count());
152 fwd_decl.shrinkAndFree(gpa, fwd_decl.items.len);
153 code.shrinkAndFree(gpa, code.items.len);
197
198 gop.value_ptr.code = try self.addString(function.object.code.items);
199 gop.value_ptr.fwd_decl = try self.addString(function.object.dg.fwd_decl.items);
154200}
155201
156202pub fn updateDecl(self: *C, module: *Module, decl_index: Module.Decl.Index) !void {
......@@ -164,11 +210,11 @@ pub fn updateDecl(self: *C, module: *Module, decl_index: Module.Decl.Index) !voi
164210 gop.value_ptr.* = .{};
165211 }
166212 const ctypes = &gop.value_ptr.ctypes;
167 const fwd_decl = &gop.value_ptr.fwd_decl;
168 const code = &gop.value_ptr.code;
213 const fwd_decl = &self.fwd_decl_buf;
214 const code = &self.code_buf;
169215 ctypes.clearRetainingCapacity(gpa);
170 fwd_decl.shrinkRetainingCapacity(0);
171 code.shrinkRetainingCapacity(0);
216 fwd_decl.clearRetainingCapacity();
217 code.clearRetainingCapacity();
172218
173219 const decl = module.declPtr(decl_index);
174220
......@@ -187,9 +233,9 @@ pub fn updateDecl(self: *C, module: *Module, decl_index: Module.Decl.Index) !voi
187233 };
188234 object.indent_writer = .{ .underlying_writer = object.code.writer() };
189235 defer {
190 object.code.deinit();
191236 object.dg.ctypes.deinit(object.dg.gpa);
192 object.dg.fwd_decl.deinit();
237 fwd_decl.* = object.dg.fwd_decl.moveToUnmanaged();
238 code.* = object.code.moveToUnmanaged();
193239 }
194240
195241 codegen.genDecl(&object) catch |err| switch (err) {
......@@ -201,13 +247,12 @@ pub fn updateDecl(self: *C, module: *Module, decl_index: Module.Decl.Index) !voi
201247 };
202248
203249 ctypes.* = object.dg.ctypes.move();
204 fwd_decl.* = object.dg.fwd_decl.moveToUnmanaged();
205 code.* = object.code.moveToUnmanaged();
206250
207251 // Free excess allocated memory for this Decl.
208252 ctypes.shrinkAndFree(gpa, ctypes.count());
209 fwd_decl.shrinkAndFree(gpa, fwd_decl.items.len);
210 code.shrinkAndFree(gpa, code.items.len);
253
254 gop.value_ptr.code = try self.addString(object.code.items);
255 gop.value_ptr.fwd_decl = try self.addString(object.dg.fwd_decl.items);
211256}
212257
213258pub fn updateDeclLineNumber(self: *C, module: *Module, decl_index: Module.Decl.Index) !void {
......@@ -273,7 +318,9 @@ pub fn flushModule(self: *C, _: *Compilation, prog_node: *std.Progress.Node) !vo
273318 const lazy_index = f.all_buffers.items.len;
274319 f.all_buffers.items.len += 1;
275320
276 try self.flushErrDecls(&f.lazy_db);
321 self.lazy_fwd_decl_buf.clearRetainingCapacity();
322 self.lazy_code_buf.clearRetainingCapacity();
323 try self.flushErrDecls(&f.lazy_ctypes);
277324
278325 // `CType`s, forward decls, and non-functions first.
279326 // Unlike other backends, the .c code we are emitting is order-dependent. Therefore
......@@ -306,7 +353,7 @@ pub fn flushModule(self: *C, _: *Compilation, prog_node: *std.Progress.Node) !vo
306353 // We need to flush lazy ctypes after flushing all decls but before flushing any decl ctypes.
307354 // This ensures that every lazy CType.Index exactly matches the global CType.Index.
308355 assert(f.ctypes.count() == 0);
309 try self.flushCTypes(&f, .none, f.lazy_db.ctypes);
356 try self.flushCTypes(&f, .none, f.lazy_ctypes);
310357
311358 var it = self.decl_table.iterator();
312359 while (it.next()) |entry|
......@@ -319,16 +366,17 @@ pub fn flushModule(self: *C, _: *Compilation, prog_node: *std.Progress.Node) !vo
319366 };
320367 f.file_size += f.ctypes_buf.items.len;
321368
369 const lazy_fwd_decl_len = self.lazy_fwd_decl_buf.items.len;
322370 f.all_buffers.items[lazy_index] = .{
323 .iov_base = if (f.lazy_db.fwd_decl.items.len > 0) f.lazy_db.fwd_decl.items.ptr else "",
324 .iov_len = f.lazy_db.fwd_decl.items.len,
371 .iov_base = if (lazy_fwd_decl_len > 0) self.lazy_fwd_decl_buf.items.ptr else "",
372 .iov_len = lazy_fwd_decl_len,
325373 };
326 f.file_size += f.lazy_db.fwd_decl.items.len;
374 f.file_size += lazy_fwd_decl_len;
327375
328376 // Now the code.
329377 try f.all_buffers.ensureUnusedCapacity(gpa, 1 + decl_values.len);
330 f.appendBufAssumeCapacity(f.lazy_db.code.items);
331 for (decl_values) |decl| f.appendBufAssumeCapacity(decl.code.items);
378 f.appendBufAssumeCapacity(self.lazy_code_buf.items);
379 for (decl_values) |decl| f.appendBufAssumeCapacity(self.getString(decl.code));
332380
333381 const file = self.base.file.?;
334382 try file.setEndPos(f.file_size);
......@@ -342,7 +390,7 @@ const Flush = struct {
342390 ctypes_map: std.ArrayListUnmanaged(codegen.CType.Index) = .{},
343391 ctypes_buf: std.ArrayListUnmanaged(u8) = .{},
344392
345 lazy_db: DeclBlock = .{},
393 lazy_ctypes: codegen.CType.Store = .{},
346394 lazy_fns: LazyFns = .{},
347395
348396 asm_buf: std.ArrayListUnmanaged(u8) = .{},
......@@ -364,7 +412,7 @@ const Flush = struct {
364412 f.all_buffers.deinit(gpa);
365413 f.asm_buf.deinit(gpa);
366414 f.lazy_fns.deinit(gpa);
367 f.lazy_db.deinit(gpa);
415 f.lazy_ctypes.deinit(gpa);
368416 f.ctypes_buf.deinit(gpa);
369417 f.ctypes_map.deinit(gpa);
370418 f.ctypes.deinit(gpa);
......@@ -462,12 +510,11 @@ fn flushCTypes(
462510 }
463511}
464512
465fn flushErrDecls(self: *C, db: *DeclBlock) FlushDeclError!void {
513fn flushErrDecls(self: *C, ctypes: *codegen.CType.Store) FlushDeclError!void {
466514 const gpa = self.base.allocator;
467515
468 const fwd_decl = &db.fwd_decl;
469 const ctypes = &db.ctypes;
470 const code = &db.code;
516 const fwd_decl = &self.lazy_fwd_decl_buf;
517 const code = &self.lazy_code_buf;
471518
472519 var object = codegen.Object{
473520 .dg = .{
......@@ -484,9 +531,9 @@ fn flushErrDecls(self: *C, db: *DeclBlock) FlushDeclError!void {
484531 };
485532 object.indent_writer = .{ .underlying_writer = object.code.writer() };
486533 defer {
487 object.code.deinit();
488534 object.dg.ctypes.deinit(gpa);
489 object.dg.fwd_decl.deinit();
535 fwd_decl.* = object.dg.fwd_decl.moveToUnmanaged();
536 code.* = object.code.moveToUnmanaged();
490537 }
491538
492539 codegen.genErrDecls(&object) catch |err| switch (err) {
......@@ -494,17 +541,14 @@ fn flushErrDecls(self: *C, db: *DeclBlock) FlushDeclError!void {
494541 else => |e| return e,
495542 };
496543
497 fwd_decl.* = object.dg.fwd_decl.moveToUnmanaged();
498544 ctypes.* = object.dg.ctypes.move();
499 code.* = object.code.moveToUnmanaged();
500545}
501546
502fn flushLazyFn(self: *C, db: *DeclBlock, lazy_fn: codegen.LazyFnMap.Entry) FlushDeclError!void {
547fn flushLazyFn(self: *C, ctypes: *codegen.CType.Store, lazy_fn: codegen.LazyFnMap.Entry) FlushDeclError!void {
503548 const gpa = self.base.allocator;
504549
505 const fwd_decl = &db.fwd_decl;
506 const ctypes = &db.ctypes;
507 const code = &db.code;
550 const fwd_decl = &self.lazy_fwd_decl_buf;
551 const code = &self.lazy_code_buf;
508552
509553 var object = codegen.Object{
510554 .dg = .{
......@@ -521,9 +565,9 @@ fn flushLazyFn(self: *C, db: *DeclBlock, lazy_fn: codegen.LazyFnMap.Entry) Flush
521565 };
522566 object.indent_writer = .{ .underlying_writer = object.code.writer() };
523567 defer {
524 object.code.deinit();
525568 object.dg.ctypes.deinit(gpa);
526 object.dg.fwd_decl.deinit();
569 fwd_decl.* = object.dg.fwd_decl.moveToUnmanaged();
570 code.* = object.code.moveToUnmanaged();
527571 }
528572
529573 codegen.genLazyFn(&object, lazy_fn) catch |err| switch (err) {
......@@ -531,21 +575,19 @@ fn flushLazyFn(self: *C, db: *DeclBlock, lazy_fn: codegen.LazyFnMap.Entry) Flush
531575 else => |e| return e,
532576 };
533577
534 fwd_decl.* = object.dg.fwd_decl.moveToUnmanaged();
535578 ctypes.* = object.dg.ctypes.move();
536 code.* = object.code.moveToUnmanaged();
537579}
538580
539581fn flushLazyFns(self: *C, f: *Flush, lazy_fns: codegen.LazyFnMap) FlushDeclError!void {
540582 const gpa = self.base.allocator;
541 try f.lazy_fns.ensureUnusedCapacity(gpa, @as(Flush.LazyFns.Size, @intCast(lazy_fns.count())));
583 try f.lazy_fns.ensureUnusedCapacity(gpa, @intCast(lazy_fns.count()));
542584
543585 var it = lazy_fns.iterator();
544586 while (it.next()) |entry| {
545587 const gop = f.lazy_fns.getOrPutAssumeCapacity(entry.key_ptr.*);
546588 if (gop.found_existing) continue;
547589 gop.value_ptr.* = {};
548 try self.flushLazyFn(&f.lazy_db, entry);
590 try self.flushLazyFn(&f.lazy_ctypes, entry);
549591 }
550592}
551593
......@@ -573,7 +615,7 @@ fn flushDecl(
573615 try self.flushLazyFns(f, decl_block.lazy_fns);
574616 try f.all_buffers.ensureUnusedCapacity(gpa, 1);
575617 if (!(decl.isExtern(mod) and export_names.contains(decl.name)))
576 f.appendBufAssumeCapacity(decl_block.fwd_decl.items);
618 f.appendBufAssumeCapacity(self.getString(decl_block.fwd_decl));
577619}
578620
579621pub fn flushEmitH(module: *Module) !void {