authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-09-29 19:07:37-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-10-03 12:12:51-07:00
logc4b0b7a30bf96590f9849f64a3d86fe2ebbc6a26
treed7c32643bbdd61d5ebf1d73e7b78ddac553a3818
parent9d069d98e3e3773d76bfa4fb07cf4bcbf06e2b67

C backend: render anon decls

Introduce the new mechanism needed to render anonymous decls to C code that the frontend is now using. The current strategy is to collect the set of used anonymous decls into one ArrayHashMap for the entire compilation, and then render them during flush(). In the future this may need to be adjusted for incremental compilation purposes, so that removing a Decl from decl_table means that newly unused anonymous decls are no longer rendered. However, let's do one thing at a time. The only goal of this branch is to stop using Module.Decl objects for unnamed constants.

2 files changed, 181 insertions(+), 90 deletions(-)

src/codegen/c.zig+37-19
......@@ -530,7 +530,7 @@ pub const DeclGen = struct {
530530 ctypes: CType.Store,
531531 /// Keeps track of anonymous decls that need to be rendered before this
532532 /// (named) Decl in the output C code.
533 anon_decl_deps: std.AutoArrayHashMapUnmanaged(InternPool.Index, void),
533 anon_decl_deps: std.AutoArrayHashMapUnmanaged(InternPool.Index, C.DeclBlock),
534534
535535 fn fail(dg: *DeclGen, comptime format: []const u8, args: anytype) error{ AnalysisFail, OutOfMemory } {
536536 @setCold(true);
......@@ -586,12 +586,13 @@ pub const DeclGen = struct {
586586 try dg.renderType(writer, ty);
587587 try writer.writeByte(')');
588588 }
589 try writer.print("&__anon_{d}", .{@intFromEnum(decl_val)});
589 try writer.writeByte('&');
590 try renderAnonDeclName(writer, decl_val);
590591 if (need_typecast) try writer.writeByte(')');
591592
592593 // Indicate that the anon decl should be rendered to the output so that
593594 // our reference above is not undefined.
594 try dg.anon_decl_deps.put(dg.gpa, decl_val, {});
595 _ = try dg.anon_decl_deps.getOrPut(dg.gpa, decl_val);
595596 }
596597
597598 fn renderDeclValue(
......@@ -1806,7 +1807,7 @@ pub const DeclGen = struct {
18061807 .none => unreachable,
18071808 .local, .new_local => |i| return w.print("t{d}", .{i}),
18081809 .local_ref => |i| return w.print("&t{d}", .{i}),
1809 .constant => unreachable,
1810 .constant => |val| return renderAnonDeclName(w, val),
18101811 .arg => |i| return w.print("a{d}", .{i}),
18111812 .arg_array => |i| return dg.writeCValueMember(w, .{ .arg = i }, .{ .identifier = "array" }),
18121813 .field => |i| return w.print("f{d}", .{i}),
......@@ -1924,6 +1925,10 @@ pub const DeclGen = struct {
19241925 }
19251926 }
19261927
1928 fn renderAnonDeclName(writer: anytype, anon_decl_val: InternPool.Index) !void {
1929 return writer.print("__anon_{d}", .{@intFromEnum(anon_decl_val)});
1930 }
1931
19271932 fn renderTypeForBuiltinFnName(dg: *DeclGen, writer: anytype, ty: Type) !void {
19281933 try dg.renderCTypeForBuiltinFnName(writer, try dg.typeToCType(ty, .complete));
19291934 }
......@@ -2761,7 +2766,6 @@ pub fn genDecl(o: *Object) !void {
27612766
27622767 const mod = o.dg.module;
27632768 const decl_index = o.dg.decl_index.unwrap().?;
2764 const decl_c_value = .{ .decl = decl_index };
27652769 const decl = mod.declPtr(decl_index);
27662770 const tv: TypedValue = .{ .ty = decl.ty, .val = (try decl.internValue(mod)).toValue() };
27672771
......@@ -2785,6 +2789,7 @@ pub fn genDecl(o: *Object) !void {
27852789 if (variable.is_threadlocal) try w.writeAll("zig_threadlocal ");
27862790 if (mod.intern_pool.stringToSliceUnwrap(decl.@"linksection")) |s|
27872791 try w.print("zig_linksection(\"{s}\", ", .{s});
2792 const decl_c_value = .{ .decl = decl_index };
27882793 try o.dg.renderTypeAndName(w, tv.ty, decl_c_value, .{}, decl.alignment, .complete);
27892794 if (decl.@"linksection" != .none) try w.writeAll(", read, write)");
27902795 try w.writeAll(" = ");
......@@ -2793,22 +2798,35 @@ pub fn genDecl(o: *Object) !void {
27932798 try o.indent_writer.insertNewline();
27942799 } else {
27952800 const is_global = o.dg.module.decl_exports.contains(decl_index);
2796 const fwd_decl_writer = o.dg.fwd_decl.writer();
2801 const decl_c_value = .{ .decl = decl_index };
2802 return genDeclValue(o, tv, is_global, decl_c_value, decl.alignment, decl.@"linksection");
2803 }
2804}
27972805
2798 try fwd_decl_writer.writeAll(if (is_global) "zig_extern " else "static ");
2799 try o.dg.renderTypeAndName(fwd_decl_writer, tv.ty, decl_c_value, Const, decl.alignment, .complete);
2800 try fwd_decl_writer.writeAll(";\n");
2806pub fn genDeclValue(
2807 o: *Object,
2808 tv: TypedValue,
2809 is_global: bool,
2810 decl_c_value: CValue,
2811 alignment: Alignment,
2812 link_section: InternPool.OptionalNullTerminatedString,
2813) !void {
2814 const fwd_decl_writer = o.dg.fwd_decl.writer();
28012815
2802 const w = o.writer();
2803 if (!is_global) try w.writeAll("static ");
2804 if (mod.intern_pool.stringToSliceUnwrap(decl.@"linksection")) |s|
2805 try w.print("zig_linksection(\"{s}\", ", .{s});
2806 try o.dg.renderTypeAndName(w, tv.ty, decl_c_value, Const, decl.alignment, .complete);
2807 if (decl.@"linksection" != .none) try w.writeAll(", read)");
2808 try w.writeAll(" = ");
2809 try o.dg.renderValue(w, tv.ty, tv.val, .StaticInitializer);
2810 try w.writeAll(";\n");
2811 }
2816 try fwd_decl_writer.writeAll(if (is_global) "zig_extern " else "static ");
2817 try o.dg.renderTypeAndName(fwd_decl_writer, tv.ty, decl_c_value, Const, alignment, .complete);
2818 try fwd_decl_writer.writeAll(";\n");
2819
2820 const mod = o.dg.module;
2821 const w = o.writer();
2822 if (!is_global) try w.writeAll("static ");
2823 if (mod.intern_pool.stringToSliceUnwrap(link_section)) |s|
2824 try w.print("zig_linksection(\"{s}\", ", .{s});
2825 try o.dg.renderTypeAndName(w, tv.ty, decl_c_value, Const, alignment, .complete);
2826 if (link_section != .none) try w.writeAll(", read)");
2827 try w.writeAll(" = ");
2828 try o.dg.renderValue(w, tv.ty, tv.val, .StaticInitializer);
2829 try w.writeAll(";\n");
28122830}
28132831
28142832pub fn genHeader(dg: *DeclGen) error{ AnalysisFail, OutOfMemory }!void {
src/link/C.zig+144-71
......@@ -27,6 +27,9 @@ decl_table: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, DeclBlock) = .{},
2727/// While in progress, a separate buffer is used, and then when finished, the
2828/// buffer is copied into this one.
2929string_bytes: std.ArrayListUnmanaged(u8) = .{},
30/// Tracks all the anonymous decls that are used by all the decls so they can
31/// be rendered during flush().
32anon_decls: std.AutoArrayHashMapUnmanaged(InternPool.Index, DeclBlock) = .{},
3033
3134/// Optimization, `updateDecl` reuses this buffer rather than creating a new
3235/// one with every call.
......@@ -34,9 +37,6 @@ fwd_decl_buf: std.ArrayListUnmanaged(u8) = .{},
3437/// Optimization, `updateDecl` reuses this buffer rather than creating a new
3538/// one with every call.
3639code_buf: std.ArrayListUnmanaged(u8) = .{},
37/// Optimization, `updateDecl` reuses this table rather than creating a new one
38/// with every call.
39scratch_anon_decl_deps: std.AutoArrayHashMapUnmanaged(InternPool.Index, void) = .{},
4040/// Optimization, `flush` reuses this buffer rather than creating a new
4141/// one with every call.
4242lazy_fwd_decl_buf: std.ArrayListUnmanaged(u8) = .{},
......@@ -45,7 +45,7 @@ lazy_fwd_decl_buf: std.ArrayListUnmanaged(u8) = .{},
4545lazy_code_buf: std.ArrayListUnmanaged(u8) = .{},
4646
4747/// A reference into `string_bytes`.
48const String = struct {
48const String = extern struct {
4949 start: u32,
5050 len: u32,
5151
......@@ -54,8 +54,9 @@ const String = struct {
5454 .len = 0,
5555 };
5656};
57
5758/// Per-declaration data.
58const DeclBlock = struct {
59pub const DeclBlock = struct {
5960 code: String = String.empty,
6061 fwd_decl: String = String.empty,
6162 /// Each `Decl` stores a set of used `CType`s. In `flush()`, we iterate
......@@ -120,10 +121,14 @@ pub fn deinit(self: *C) void {
120121 }
121122 self.decl_table.deinit(gpa);
122123
124 for (self.anon_decls.values()) |*db| {
125 db.deinit(gpa);
126 }
127 self.anon_decls.deinit(gpa);
128
123129 self.string_bytes.deinit(gpa);
124130 self.fwd_decl_buf.deinit(gpa);
125131 self.code_buf.deinit(gpa);
126 self.scratch_anon_decl_deps.deinit(gpa);
127132}
128133
129134pub fn freeDecl(self: *C, decl_index: Module.Decl.Index) void {
......@@ -158,57 +163,110 @@ pub fn updateFunc(
158163 lazy_fns.clearRetainingCapacity();
159164 fwd_decl.clearRetainingCapacity();
160165 code.clearRetainingCapacity();
161 self.scratch_anon_decl_deps.clearRetainingCapacity();
162166
163 {
164 var function: codegen.Function = .{
165 .value_map = codegen.CValueMap.init(gpa),
166 .air = air,
167 .liveness = liveness,
168 .func_index = func_index,
169 .object = .{
170 .dg = .{
171 .gpa = gpa,
172 .module = module,
173 .error_msg = null,
174 .decl_index = decl_index.toOptional(),
175 .is_naked_fn = decl.ty.fnCallingConvention(module) == .Naked,
176 .fwd_decl = fwd_decl.toManaged(gpa),
177 .ctypes = ctypes.*,
178 .anon_decl_deps = self.scratch_anon_decl_deps,
179 },
180 .code = code.toManaged(gpa),
181 .indent_writer = undefined, // set later so we can get a pointer to object.code
167 var function: codegen.Function = .{
168 .value_map = codegen.CValueMap.init(gpa),
169 .air = air,
170 .liveness = liveness,
171 .func_index = func_index,
172 .object = .{
173 .dg = .{
174 .gpa = gpa,
175 .module = module,
176 .error_msg = null,
177 .decl_index = decl_index.toOptional(),
178 .is_naked_fn = decl.ty.fnCallingConvention(module) == .Naked,
179 .fwd_decl = fwd_decl.toManaged(gpa),
180 .ctypes = ctypes.*,
181 .anon_decl_deps = self.anon_decls,
182182 },
183 .lazy_fns = lazy_fns.*,
184 };
183 .code = code.toManaged(gpa),
184 .indent_writer = undefined, // set later so we can get a pointer to object.code
185 },
186 .lazy_fns = lazy_fns.*,
187 };
185188
186 function.object.indent_writer = .{ .underlying_writer = function.object.code.writer() };
187 defer {
188 self.scratch_anon_decl_deps = function.object.dg.anon_decl_deps;
189 fwd_decl.* = function.object.dg.fwd_decl.moveToUnmanaged();
190 code.* = function.object.code.moveToUnmanaged();
191 function.deinit();
192 }
189 function.object.indent_writer = .{ .underlying_writer = function.object.code.writer() };
190 defer {
191 self.anon_decls = function.object.dg.anon_decl_deps;
192 fwd_decl.* = function.object.dg.fwd_decl.moveToUnmanaged();
193 code.* = function.object.code.moveToUnmanaged();
194 function.deinit();
195 }
193196
194 codegen.genFunc(&function) catch |err| switch (err) {
195 error.AnalysisFail => {
196 try module.failed_decls.put(gpa, decl_index, function.object.dg.error_msg.?);
197 return;
198 },
199 else => |e| return e,
200 };
197 codegen.genFunc(&function) catch |err| switch (err) {
198 error.AnalysisFail => {
199 try module.failed_decls.put(gpa, decl_index, function.object.dg.error_msg.?);
200 return;
201 },
202 else => |e| return e,
203 };
204
205 ctypes.* = function.object.dg.ctypes.move();
206 lazy_fns.* = function.lazy_fns.move();
201207
202 ctypes.* = function.object.dg.ctypes.move();
203 lazy_fns.* = function.lazy_fns.move();
208 // Free excess allocated memory for this Decl.
209 ctypes.shrinkAndFree(gpa, ctypes.count());
210 lazy_fns.shrinkAndFree(gpa, lazy_fns.count());
211
212 gop.value_ptr.code = try self.addString(function.object.code.items);
213 gop.value_ptr.fwd_decl = try self.addString(function.object.dg.fwd_decl.items);
214}
215
216fn updateAnonDecl(self: *C, module: *Module, i: usize) !void {
217 const gpa = self.base.allocator;
218 const anon_decl = self.anon_decls.keys()[i];
204219
205 // Free excess allocated memory for this Decl.
206 ctypes.shrinkAndFree(gpa, ctypes.count());
207 lazy_fns.shrinkAndFree(gpa, lazy_fns.count());
220 const fwd_decl = &self.fwd_decl_buf;
221 const code = &self.code_buf;
222 fwd_decl.clearRetainingCapacity();
223 code.clearRetainingCapacity();
208224
209 gop.value_ptr.code = try self.addString(function.object.code.items);
210 gop.value_ptr.fwd_decl = try self.addString(function.object.dg.fwd_decl.items);
225 var object: codegen.Object = .{
226 .dg = .{
227 .gpa = gpa,
228 .module = module,
229 .error_msg = null,
230 .decl_index = .none,
231 .is_naked_fn = false,
232 .fwd_decl = fwd_decl.toManaged(gpa),
233 .ctypes = .{},
234 .anon_decl_deps = self.anon_decls,
235 },
236 .code = code.toManaged(gpa),
237 .indent_writer = undefined, // set later so we can get a pointer to object.code
238 };
239 object.indent_writer = .{ .underlying_writer = object.code.writer() };
240
241 defer {
242 self.anon_decls = object.dg.anon_decl_deps;
243 object.dg.ctypes.deinit(object.dg.gpa);
244 fwd_decl.* = object.dg.fwd_decl.moveToUnmanaged();
245 code.* = object.code.moveToUnmanaged();
211246 }
247
248 const tv: @import("../TypedValue.zig") = .{
249 .ty = module.intern_pool.typeOf(anon_decl).toType(),
250 .val = anon_decl.toValue(),
251 };
252 const c_value: codegen.CValue = .{ .constant = anon_decl };
253 codegen.genDeclValue(&object, tv, false, c_value, .none, .none) catch |err| switch (err) {
254 error.AnalysisFail => {
255 @panic("TODO: C backend AnalysisFail on anonymous decl");
256 //try module.failed_decls.put(gpa, decl_index, object.dg.error_msg.?);
257 //return;
258 },
259 else => |e| return e,
260 };
261
262 // Free excess allocated memory for this Decl.
263 object.dg.ctypes.shrinkAndFree(gpa, object.dg.ctypes.count());
264
265 object.dg.anon_decl_deps.values()[i] = .{
266 .code = try self.addString(object.code.items),
267 .fwd_decl = try self.addString(object.dg.fwd_decl.items),
268 .ctypes = object.dg.ctypes.move(),
269 };
212270}
213271
214272pub fn updateDecl(self: *C, module: *Module, decl_index: Module.Decl.Index) !void {
......@@ -227,7 +285,6 @@ pub fn updateDecl(self: *C, module: *Module, decl_index: Module.Decl.Index) !voi
227285 ctypes.clearRetainingCapacity(gpa);
228286 fwd_decl.clearRetainingCapacity();
229287 code.clearRetainingCapacity();
230 self.scratch_anon_decl_deps.clearRetainingCapacity();
231288
232289 var object: codegen.Object = .{
233290 .dg = .{
......@@ -238,14 +295,14 @@ pub fn updateDecl(self: *C, module: *Module, decl_index: Module.Decl.Index) !voi
238295 .is_naked_fn = false,
239296 .fwd_decl = fwd_decl.toManaged(gpa),
240297 .ctypes = ctypes.*,
241 .anon_decl_deps = self.scratch_anon_decl_deps,
298 .anon_decl_deps = self.anon_decls,
242299 },
243300 .code = code.toManaged(gpa),
244301 .indent_writer = undefined, // set later so we can get a pointer to object.code
245302 };
246303 object.indent_writer = .{ .underlying_writer = object.code.writer() };
247304 defer {
248 self.scratch_anon_decl_deps = object.dg.anon_decl_deps;
305 self.anon_decls = object.dg.anon_decl_deps;
249306 object.dg.ctypes.deinit(object.dg.gpa);
250307 fwd_decl.* = object.dg.fwd_decl.moveToUnmanaged();
251308 code.* = object.code.moveToUnmanaged();
......@@ -303,6 +360,13 @@ pub fn flushModule(self: *C, _: *Compilation, prog_node: *std.Progress.Node) !vo
303360 const gpa = self.base.allocator;
304361 const module = self.base.options.module.?;
305362
363 {
364 var i: usize = 0;
365 while (i < self.anon_decls.count()) : (i += 1) {
366 try updateAnonDecl(self, module, i);
367 }
368 }
369
306370 // This code path happens exclusively with -ofmt=c. The flush logic for
307371 // emit-h is in `flushEmitH` below.
308372
......@@ -345,10 +409,15 @@ pub fn flushModule(self: *C, _: *Compilation, prog_node: *std.Progress.Node) !vo
345409 for (module.decl_exports.values()) |exports| for (exports.items) |@"export"|
346410 try export_names.put(gpa, @"export".opts.name, {});
347411
348 const decl_keys = self.decl_table.keys();
349 for (decl_keys) |decl_index| {
412 for (self.anon_decls.values()) |*decl_block| {
413 try self.flushDeclBlock(&f, decl_block, export_names, .none);
414 }
415
416 for (self.decl_table.keys(), self.decl_table.values()) |decl_index, *decl_block| {
350417 assert(module.declPtr(decl_index).has_tv);
351 try self.flushDecl(&f, decl_index, export_names);
418 const decl = module.declPtr(decl_index);
419 const extern_symbol_name = if (decl.isExtern(module)) decl.name.toOptional() else .none;
420 try self.flushDeclBlock(&f, decl_block, export_names, extern_symbol_name);
352421 }
353422 }
354423
......@@ -358,8 +427,12 @@ pub fn flushModule(self: *C, _: *Compilation, prog_node: *std.Progress.Node) !vo
358427 assert(f.ctypes.count() == 0);
359428 try self.flushCTypes(&f, .none, f.lazy_ctypes);
360429
361 for (self.decl_table.keys(), self.decl_table.values()) |decl_index, db| {
362 try self.flushCTypes(&f, decl_index.toOptional(), db.ctypes);
430 for (self.anon_decls.values()) |decl_block| {
431 try self.flushCTypes(&f, .none, decl_block.ctypes);
432 }
433
434 for (self.decl_table.keys(), self.decl_table.values()) |decl_index, decl_block| {
435 try self.flushCTypes(&f, decl_index.toOptional(), decl_block.ctypes);
363436 }
364437 }
365438
......@@ -377,10 +450,12 @@ pub fn flushModule(self: *C, _: *Compilation, prog_node: *std.Progress.Node) !vo
377450 f.file_size += lazy_fwd_decl_len;
378451
379452 // Now the code.
453 const anon_decl_values = self.anon_decls.values();
380454 const decl_values = self.decl_table.values();
381 try f.all_buffers.ensureUnusedCapacity(gpa, 1 + decl_values.len);
455 try f.all_buffers.ensureUnusedCapacity(gpa, 1 + anon_decl_values.len + decl_values.len);
382456 f.appendBufAssumeCapacity(self.lazy_code_buf.items);
383 for (decl_values) |decl| f.appendBufAssumeCapacity(self.getString(decl.code));
457 for (anon_decl_values) |db| f.appendBufAssumeCapacity(self.getString(db.code));
458 for (decl_values) |db| f.appendBufAssumeCapacity(self.getString(db.code));
384459
385460 const file = self.base.file.?;
386461 try file.setEndPos(f.file_size);
......@@ -526,16 +601,14 @@ fn flushErrDecls(self: *C, ctypes: *codegen.CType.Store) FlushDeclError!void {
526601 .is_naked_fn = false,
527602 .fwd_decl = fwd_decl.toManaged(gpa),
528603 .ctypes = ctypes.*,
529 .anon_decl_deps = .{},
604 .anon_decl_deps = self.anon_decls,
530605 },
531606 .code = code.toManaged(gpa),
532607 .indent_writer = undefined, // set later so we can get a pointer to object.code
533608 };
534609 object.indent_writer = .{ .underlying_writer = object.code.writer() };
535610 defer {
536 // If this assert trips just handle the anon_decl_deps the same as
537 // `updateFunc()` does.
538 assert(object.dg.anon_decl_deps.count() == 0);
611 self.anon_decls = object.dg.anon_decl_deps;
539612 object.dg.ctypes.deinit(gpa);
540613 fwd_decl.* = object.dg.fwd_decl.moveToUnmanaged();
541614 code.* = object.code.moveToUnmanaged();
......@@ -604,22 +677,22 @@ fn flushLazyFns(self: *C, f: *Flush, lazy_fns: codegen.LazyFnMap) FlushDeclError
604677 }
605678}
606679
607fn flushDecl(
680fn flushDeclBlock(
608681 self: *C,
609682 f: *Flush,
610 decl_index: Module.Decl.Index,
683 decl_block: *DeclBlock,
611684 export_names: std.AutoHashMapUnmanaged(InternPool.NullTerminatedString, void),
685 extern_symbol_name: InternPool.OptionalNullTerminatedString,
612686) FlushDeclError!void {
613687 const gpa = self.base.allocator;
614 const mod = self.base.options.module.?;
615 const decl = mod.declPtr(decl_index);
616
617 const decl_block = self.decl_table.getPtr(decl_index).?;
618
619688 try self.flushLazyFns(f, decl_block.lazy_fns);
620689 try f.all_buffers.ensureUnusedCapacity(gpa, 1);
621 if (!(decl.isExtern(mod) and export_names.contains(decl.name)))
690 fwd_decl: {
691 if (extern_symbol_name.unwrap()) |name| {
692 if (export_names.contains(name)) break :fwd_decl;
693 }
622694 f.appendBufAssumeCapacity(self.getString(decl_block.fwd_decl));
695 }
623696}
624697
625698pub fn flushEmitH(module: *Module) !void {