1/// Unlike other linker implementations, `link.C` does not attempt to incrementally link its output,
2/// because C has many language rules which make that impractical. Instead, we individually generate
3/// each declaration (NAV), and the output is stitched together (alongside types and UAVs) in an
4/// appropriate order in `flush`.
5const C = @This();
6
7const std = @import("std");
8const mem = std.mem;
9const assert = std.debug.assert;
10const Allocator = std.mem.Allocator;
11const fs = std.fs;
12const Path = std.Build.Cache.Path;
13
14const build_options = @import("build_options");
15const Zcu = @import("../Zcu.zig");
16const Module = @import("../Module.zig");
17const InternPool = @import("../InternPool.zig");
18const Alignment = InternPool.Alignment;
19const Compilation = @import("../Compilation.zig");
20const codegen = @import("../codegen/c.zig");
21const link = @import("../link.zig");
22const trace = @import("../tracy.zig").trace;
23const Type = @import("../Type.zig");
24const Value = @import("../Value.zig");
25const AnyMir = @import("../codegen.zig").AnyMir;
26
27base: link.File,
28
29/// All the string bytes of rendered C code, all squished into one array. `String` is used to refer
30/// to specific slices of this array, used for the rendered C code of an individual UAV/NAV/type.
31///
32/// During code generation for functions, a separate buffer is used, and the contents of that buffer
33/// are copied into `string_bytes` when the function is emitted by `updateFunc`.
34string_bytes: std.ArrayList(u8),
35
36/// Like with `string_bytes`, we concatenate all type dependencies into one array, and slice into it
37/// for specific groups of dependencies. These values are indices into `type_pool`, and thus also
38/// into `types`. We store these instead of `InternPool.Index` because it lets us avoid some hash
39/// map lookups in `flush`.
40type_dependencies: std.ArrayList(link.ConstPool.Index),
41/// For storing dependencies on "aligned" versions of types, we must associate each type with a
42/// bitmask of required alignments. As with `type_dependencies`, we concatenate all such masks into
43/// one array.
44align_dependency_masks: std.ArrayList(u64),
45
46/// Emitted at the top of the file. This can be cached since it only depends on the target.
47header: String,
48/// All NAVs, regardless of whether they are functions or simple constants, are put in this map.
49navs: std.array_hash_map.Auto(InternPool.Nav.Index, RenderedDecl),
50/// All UAVs which may be referenced are in this map. The UAV alignment is not included in the
51/// rendered C code stored here, because we don't know the alignment a UAV needs until `flush`.
52uavs: std.array_hash_map.Auto(InternPool.Index, RenderedDecl),
53/// Contains all types which are needed by some other rendered code. Does not contain any constants
54/// other than types.
55type_pool: link.ConstPool,
56/// Indices are `link.ConstPool.Index` from `type_pool`. Contains rendered C code for every type
57/// which may be referenced. Logic in `flush` will perform the appropriate topological sort to emit
58/// these type definitions in an order which C allows.
59types: std.ArrayList(RenderedType),
60
61/// The set of big int types required by *any* generated code so far. These are always safe to emit,
62/// so they do not participate in the dependency graph traversal in `flush`. Therefore, redundant
63/// big-int types may be emitted under incremental compilation.
64bigint_types: std.array_hash_map.Auto(codegen.CType.BigInt, void),
65
66exported_navs: std.array_hash_map.Auto(InternPool.Nav.Index, String),
67exported_uavs: std.array_hash_map.Auto(InternPool.Index, String),
68
69/// A reference into `string_bytes`.
70const String = extern struct {
71 start: u32,
72 len: u32,
73
74 const empty: String = .{
75 .start = 0,
76 .len = 0,
77 };
78
79 fn get(s: String, c: *C) []const u8 {
80 return c.string_bytes.items[s.start..][0..s.len];
81 }
82};
83
84const CTypeDependencies = struct {
85 len: u32,
86 errunion_len: u32,
87 fwd_len: u32,
88 errunion_fwd_len: u32,
89 aligned_fwd_len: u32,
90
91 /// Index into `C.type_dependencies`. Starting at this index are:
92 /// * `len` dependencies on complete types
93 /// * `errunion_len` dependencies on complete error union types
94 /// * `fwd_len` dependencies on forward-declared types
95 /// * `errunion_fwd_len` dependencies on forward-declared error union types
96 /// * `aligned_fwd_len` dependencies on aligned types
97 type_start: u32,
98 /// Index into `C.align_dependency_masks`. Starting at this index are `aligned_type_fwd_len`
99 /// items containing the bitmasks for each aligned type (in `C.type_dependencies`).
100 align_mask_start: u32,
101
102 const Resolved = struct {
103 type: []const link.ConstPool.Index,
104 errunion_type: []const link.ConstPool.Index,
105 type_fwd: []const link.ConstPool.Index,
106 errunion_type_fwd: []const link.ConstPool.Index,
107 aligned_type_fwd: []const link.ConstPool.Index,
108 aligned_type_masks: []const u64,
109 };
110
111 fn get(td: *const CTypeDependencies, c: *const C) Resolved {
112 const types_overlong = c.type_dependencies.items[td.type_start..];
113 return .{
114 .type = types_overlong[0..td.len],
115 .errunion_type = types_overlong[td.len..][0..td.errunion_len],
116 .type_fwd = types_overlong[td.len + td.errunion_len ..][0..td.fwd_len],
117 .errunion_type_fwd = types_overlong[td.len + td.errunion_len + td.fwd_len ..][0..td.errunion_fwd_len],
118 .aligned_type_fwd = types_overlong[td.len + td.errunion_len + td.fwd_len + td.errunion_fwd_len ..][0..td.aligned_fwd_len],
119 .aligned_type_masks = c.align_dependency_masks.items[td.align_mask_start..][0..td.aligned_fwd_len],
120 };
121 }
122
123 const empty: CTypeDependencies = .{
124 .len = 0,
125 .errunion_len = 0,
126 .fwd_len = 0,
127 .errunion_fwd_len = 0,
128 .aligned_fwd_len = 0,
129 .type_start = 0,
130 .align_mask_start = 0,
131 };
132};
133
134const RenderedDecl = struct {
135 fwd_decl: String,
136 code: String,
137 ctype_deps: CTypeDependencies,
138 need_uavs: std.array_hash_map.Auto(InternPool.Index, Alignment),
139 need_tag_name_funcs: std.array_hash_map.Auto(InternPool.Index, void),
140 need_never_tail_funcs: std.array_hash_map.Auto(InternPool.Nav.Index, void),
141 need_never_inline_funcs: std.array_hash_map.Auto(InternPool.Nav.Index, void),
142
143 const init: RenderedDecl = .{
144 .fwd_decl = .empty,
145 .code = .empty,
146 .ctype_deps = .empty,
147 .need_uavs = .empty,
148 .need_tag_name_funcs = .empty,
149 .need_never_tail_funcs = .empty,
150 .need_never_inline_funcs = .empty,
151 };
152
153 fn deinit(rd: *RenderedDecl, gpa: Allocator) void {
154 rd.need_uavs.deinit(gpa);
155 rd.need_tag_name_funcs.deinit(gpa);
156 rd.need_never_tail_funcs.deinit(gpa);
157 rd.need_never_inline_funcs.deinit(gpa);
158 rd.* = undefined;
159 }
160
161 /// We are about to re-render this declaration, but we want to reuse the existing buffers, so
162 /// call `clearRetainCapacity` on the containers. Sets `fwd_decl` and `code` to `undefined`,
163 /// because we shouldn't be using the old values any longer.
164 fn clearRetainingCapacity(rd: *RenderedDecl) void {
165 rd.fwd_decl = undefined;
166 rd.code = undefined;
167 rd.need_uavs.clearRetainingCapacity();
168 rd.need_tag_name_funcs.clearRetainingCapacity();
169 rd.need_never_tail_funcs.clearRetainingCapacity();
170 rd.need_never_inline_funcs.clearRetainingCapacity();
171 }
172};
173
174const RenderedType = struct {
175 /// If this type lowers to an aggregate, this is a forward declaration of its struct/union tag.
176 /// Otherwise, this is `.empty`.
177 ///
178 /// Populated immediately and never changes.
179 fwd_decl: String,
180
181 /// A forward declaration of an error union type with this type as its *payload*.
182 ///
183 /// Populated immediately and never changes.
184 errunion_fwd_decl: String,
185
186 /// If this type lowers to an aggregate, this is the struct/union definition.
187 /// If this type lowers to a typedef, this is that typedef.
188 /// Otherwise, this is `.empty`.
189 definition: String,
190 /// The `struct` definition for an error union type with this type as its *payload*.
191 ///
192 /// This string is empty iff the payload type does not have a resolved layout. If the layout is
193 /// resolved, the error union struct is defined, even if the payload type lacks runtime bits.
194 errunion_definition: String,
195
196 /// Dependencies which must be satisfied before emitting the name of this type. As such, they
197 /// must be satisfied before emitting `errunion_definition` or any aligned typedef.
198 ///
199 /// Populated immediately and never changes.
200 deps: CTypeDependencies,
201
202 /// Dependencies which must be satisfied before emitting `definition`.
203 definition_deps: CTypeDependencies,
204};
205
206/// Only called by `link.ConstPool` due to `c.type_pool`, so `val` is always a type.
207pub fn addConst(
208 c: *C,
209 pt: Zcu.PerThread,
210 pool_index: link.ConstPool.Index,
211 val: InternPool.Index,
212) link.Error!void {
213 const zcu = pt.zcu;
214 const gpa = zcu.comp.gpa;
215 assert(zcu.intern_pool.typeOf(val) == .type_type);
216 assert(@backingInt(pool_index) == c.types.items.len);
217
218 const ty: Type = .fromInterned(val);
219
220 const fwd_decl: String = fwd_decl: {
221 var aw: std.Io.Writer.Allocating = .fromArrayList(gpa, &c.string_bytes);
222 defer c.string_bytes = aw.toArrayList();
223 const start = aw.written().len;
224 codegen.CType.render_defs.fwdDecl(ty, &aw.writer, zcu) catch |err| switch (err) {
225 error.WriteFailed => return error.OutOfMemory,
226 };
227 break :fwd_decl .{
228 .start = @intCast(start),
229 .len = @intCast(aw.written().len - start),
230 };
231 };
232
233 const errunion_fwd_decl: String = errunion_fwd_decl: {
234 var aw: std.Io.Writer.Allocating = .fromArrayList(gpa, &c.string_bytes);
235 defer c.string_bytes = aw.toArrayList();
236 const start = aw.written().len;
237 codegen.CType.render_defs.errunionFwdDecl(ty, &aw.writer, zcu) catch |err| switch (err) {
238 error.WriteFailed => return error.OutOfMemory,
239 };
240 break :errunion_fwd_decl .{
241 .start = @intCast(start),
242 .len = @intCast(aw.written().len - start),
243 };
244 };
245
246 try c.types.append(gpa, .{
247 .fwd_decl = fwd_decl,
248 .errunion_fwd_decl = errunion_fwd_decl,
249 // This field will be populated just below.
250 .deps = undefined,
251 // The remaining fields will be populated later by either `updateConstIncomplete` or
252 // `updateConstComplete` (it is guaranteed that at least one will be called).
253 .definition = undefined,
254 .errunion_definition = undefined,
255 .definition_deps = undefined,
256 });
257
258 {
259 // Find the dependencies required to just render the type `ty`.
260 var arena: std.heap.ArenaAllocator = .init(gpa);
261 defer arena.deinit();
262 var deps: codegen.CType.Dependencies = .empty;
263 defer deps.deinit(gpa);
264 _ = try codegen.CType.lower(ty, &deps, arena.allocator(), zcu);
265 // This call may add more items to `c.types`.
266 const type_deps = try c.addCTypeDependencies(pt, &deps);
267 c.types.items[@backingInt(pool_index)].deps = type_deps;
268 }
269}
270
271/// Only called by `link.ConstPool` due to `c.type_pool`, so `val` is always a type.
272pub fn updateConstIncomplete(
273 c: *C,
274 pt: Zcu.PerThread,
275 index: link.ConstPool.Index,
276 val: InternPool.Index,
277) Allocator.Error!void {
278 const zcu = pt.zcu;
279 const gpa = zcu.comp.gpa;
280
281 assert(zcu.intern_pool.typeOf(val) == .type_type);
282 const ty: Type = .fromInterned(val);
283
284 const rendered: *RenderedType = &c.types.items[@backingInt(index)];
285
286 rendered.errunion_definition = .empty;
287 rendered.definition_deps = .empty;
288 rendered.definition = definition: {
289 if (rendered.fwd_decl.len != 0) {
290 // This is a struct or union type. We will never complete it, but we must forward
291 // declare it to ensure that its first usage does not appear in a different scope.
292 break :definition rendered.fwd_decl;
293 }
294 // Otherwise, we might need to `typedef` to `void`.
295 var aw: std.Io.Writer.Allocating = .fromArrayList(gpa, &c.string_bytes);
296 defer c.string_bytes = aw.toArrayList();
297 const start = aw.written().len;
298 codegen.CType.render_defs.defineIncomplete(ty, &aw.writer, pt) catch |err| switch (err) {
299 error.WriteFailed => return error.OutOfMemory,
300 };
301 break :definition .{
302 .start = @intCast(start),
303 .len = @intCast(aw.written().len - start),
304 };
305 };
306}
307/// Only called by `link.ConstPool` due to `c.type_pool`, so `val` is always a type.
308pub fn updateConst(
309 c: *C,
310 pt: Zcu.PerThread,
311 index: link.ConstPool.Index,
312 val: InternPool.Index,
313) link.Error!void {
314 const zcu = pt.zcu;
315 const gpa = zcu.comp.gpa;
316
317 assert(zcu.intern_pool.typeOf(val) == .type_type);
318 const ty: Type = .fromInterned(val);
319
320 const rendered: *RenderedType = &c.types.items[@backingInt(index)];
321
322 var arena: std.heap.ArenaAllocator = .init(gpa);
323 defer arena.deinit();
324
325 var deps: codegen.CType.Dependencies = .empty;
326 defer deps.deinit(gpa);
327
328 {
329 var aw: std.Io.Writer.Allocating = .fromArrayList(gpa, &c.string_bytes);
330 defer c.string_bytes = aw.toArrayList();
331 const start = aw.written().len;
332 codegen.CType.render_defs.errunionDefineComplete(
333 ty,
334 &deps,
335 arena.allocator(),
336 &aw.writer,
337 pt,
338 ) catch |err| switch (err) {
339 error.WriteFailed => return error.OutOfMemory,
340 error.OutOfMemory => |e| return e,
341 };
342 rendered.errunion_definition = .{
343 .start = @intCast(start),
344 .len = @intCast(aw.written().len - start),
345 };
346 }
347
348 deps.clearRetainingCapacity();
349
350 {
351 var aw: std.Io.Writer.Allocating = .fromArrayList(gpa, &c.string_bytes);
352 defer c.string_bytes = aw.toArrayList();
353 const start = aw.written().len;
354 codegen.CType.render_defs.defineComplete(
355 ty,
356 &deps,
357 arena.allocator(),
358 &aw.writer,
359 pt,
360 ) catch |err| switch (err) {
361 error.WriteFailed => return error.OutOfMemory,
362 error.OutOfMemory => |e| return e,
363 };
364 // Remove dependency on a forward declaration of ourselves; we're defining this type so that
365 // forward declaration obviously exists!
366 _ = deps.type_fwd.swapRemove(ty.toIntern());
367 rendered.definition = .{
368 .start = @intCast(start),
369 .len = @intCast(aw.written().len - start),
370 };
371 }
372
373 {
374 // This call invalidates `rendered`.
375 const definition_deps = try c.addCTypeDependencies(pt, &deps);
376 c.types.items[@backingInt(index)].definition_deps = definition_deps;
377 }
378}
379
380fn addString(c: *C, vec: []const []const u8) Allocator.Error!String {
381 const gpa = c.base.comp.gpa;
382
383 var len: u32 = 0;
384 for (vec) |s| len += @intCast(s.len);
385 try c.string_bytes.ensureUnusedCapacity(gpa, len);
386
387 const start: u32 = @intCast(c.string_bytes.items.len);
388 for (vec) |s| c.string_bytes.appendSliceAssumeCapacity(s);
389 assert(c.string_bytes.items.len == start + len);
390
391 return .{ .start = start, .len = len };
392}
393
394pub fn open(
395 arena: Allocator,
396 comp: *Compilation,
397 emit: Path,
398 options: link.File.OpenOptions,
399) !*C {
400 return createEmpty(arena, comp, emit, options);
401}
402
403pub fn createEmpty(
404 arena: Allocator,
405 comp: *Compilation,
406 emit: Path,
407 options: link.File.OpenOptions,
408) !*C {
409 assert(comp.root_mod.resolved_target.result.ofmt == .c);
410 const io = comp.io;
411 const optimize_mode = comp.root_mod.optimize_mode;
412 const use_lld = build_options.have_llvm and comp.config.use_lld;
413 const use_llvm = comp.config.use_llvm;
414 const output_mode = comp.config.output_mode;
415
416 // These are caught by `Compilation.Config.resolve`.
417 assert(!use_lld);
418 assert(!use_llvm);
419
420 const file = try emit.root_dir.handle.createFile(io, emit.sub_path, .{
421 // Truncation is done on `flush`.
422 .truncate = false,
423 });
424 errdefer file.close(io);
425
426 const c = try arena.create(C);
427 c.* = .{
428 .base = .{
429 .tag = .c,
430 .comp = comp,
431 .emit = emit,
432 .gc_sections = options.gc_sections orelse (optimize_mode != .debug and output_mode != .Obj),
433 .print_gc_sections = options.print_gc_sections,
434 .stack_size = options.stack_size orelse 16777216,
435 .allow_shlib_undefined = options.allow_shlib_undefined orelse false,
436 .file = file,
437 .build_id = options.build_id,
438 },
439 .string_bytes = .empty,
440 .type_dependencies = .empty,
441 .align_dependency_masks = .empty,
442 .header = .empty,
443 .navs = .empty,
444 .uavs = .empty,
445 .type_pool = .empty,
446 .types = .empty,
447 .bigint_types = .empty,
448 .exported_navs = .empty,
449 .exported_uavs = .empty,
450 };
451 return c;
452}
453
454pub fn deinit(c: *C) void {
455 const gpa = c.base.comp.gpa;
456
457 for (c.navs.values()) |*r| r.deinit(gpa);
458 for (c.uavs.values()) |*r| r.deinit(gpa);
459
460 c.string_bytes.deinit(gpa);
461 c.type_dependencies.deinit(gpa);
462 c.align_dependency_masks.deinit(gpa);
463 c.navs.deinit(gpa);
464 c.uavs.deinit(gpa);
465 c.type_pool.deinit(gpa);
466 c.types.deinit(gpa);
467 c.bigint_types.deinit(gpa);
468 c.exported_navs.deinit(gpa);
469 c.exported_uavs.deinit(gpa);
470}
471
472pub fn prelink(c: *C, prog_node: std.Progress.Node) !void {
473 const comp = c.base.comp;
474
475 const sub_prog_node = prog_node.start("Generate Header", 0);
476 defer sub_prog_node.end();
477
478 var header_aw: std.Io.Writer.Allocating = .init(comp.gpa);
479 defer header_aw.deinit();
480 codegen.genHeader(comp.zcu.?, &header_aw.writer) catch |err| switch (err) {
481 error.WriteFailed => return error.OutOfMemory,
482 else => |e| return e,
483 };
484 c.header = try c.addString(&.{header_aw.written()});
485}
486
487pub fn updateContainerType(
488 c: *C,
489 pt: Zcu.PerThread,
490 ty: InternPool.Index,
491 success: bool,
492) link.Error!void {
493 try c.type_pool.updateContainerType(pt, .{ .c = c }, ty, success);
494}
495
496pub fn updateFunc(
497 c: *C,
498 pt: Zcu.PerThread,
499 func_index: InternPool.Index,
500 mir: *AnyMir,
501) link.Error!void {
502 const zcu = pt.zcu;
503 const gpa = zcu.gpa;
504 const nav = zcu.funcInfo(func_index).owner_nav;
505
506 const rendered_decl: *RenderedDecl = rd: {
507 const gop = try c.navs.getOrPut(gpa, nav);
508 if (gop.found_existing) gop.value_ptr.deinit(gpa);
509 break :rd gop.value_ptr;
510 };
511 c.navs.lockPointers();
512 defer c.navs.unlockPointers();
513
514 rendered_decl.* = .{
515 .fwd_decl = try c.addString(&.{mir.c.fwd_decl}),
516 .code = try c.addString(&.{ mir.c.code_header, mir.c.code }),
517 .ctype_deps = try c.addCTypeDependencies(pt, &mir.c.ctype_deps),
518 .need_uavs = mir.c.need_uavs.move(),
519 .need_tag_name_funcs = mir.c.need_tag_name_funcs.move(),
520 .need_never_tail_funcs = mir.c.need_never_tail_funcs.move(),
521 .need_never_inline_funcs = mir.c.need_never_inline_funcs.move(),
522 };
523
524 const old_uavs_len = c.uavs.count();
525 try c.uavs.ensureUnusedCapacity(gpa, rendered_decl.need_uavs.count());
526 for (rendered_decl.need_uavs.keys()) |val| {
527 const gop = c.uavs.getOrPutAssumeCapacity(val);
528 if (gop.found_existing) {
529 assert(gop.index < old_uavs_len);
530 } else {
531 assert(gop.index >= old_uavs_len);
532 }
533 }
534 try c.updateNewUavs(pt, old_uavs_len);
535
536 try c.type_pool.flushPending(pt, .{ .c = c });
537}
538
539pub fn updateNav(c: *C, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) link.Error!void {
540 const tracy = trace(@src());
541 defer tracy.end();
542
543 const gpa = c.base.comp.gpa;
544 const zcu = pt.zcu;
545 const ip = &zcu.intern_pool;
546
547 const nav = ip.getNav(nav_index);
548 switch (ip.indexToKey(nav.resolved.?.value)) {
549 .func => return,
550 .@"extern" => {},
551 else => {
552 const nav_ty: Type = .fromInterned(nav.resolved.?.type);
553 if (!nav_ty.hasRuntimeBits(zcu)) {
554 if (c.navs.fetchSwapRemove(nav_index)) |kv| {
555 var old_rendered = kv.value;
556 old_rendered.deinit(gpa);
557 }
558 return;
559 }
560 },
561 }
562
563 const rendered_decl: *RenderedDecl = rd: {
564 const gop = try c.navs.getOrPut(gpa, nav_index);
565 if (gop.found_existing) {
566 gop.value_ptr.clearRetainingCapacity();
567 } else {
568 gop.value_ptr.* = .init;
569 }
570 break :rd gop.value_ptr;
571 };
572 c.navs.lockPointers();
573 defer c.navs.unlockPointers();
574
575 {
576 var arena: std.heap.ArenaAllocator = .init(gpa);
577 defer arena.deinit();
578
579 var dg: codegen.DeclGen = .{
580 .gpa = gpa,
581 .arena = arena.allocator(),
582 .pt = pt,
583 .mod = zcu.navFileScope(nav_index).mod.?,
584 .owner_nav = nav_index.toOptional(),
585 .is_naked_fn = false,
586 .expected_block = null,
587 .ctype_deps = .empty,
588 .uavs = rendered_decl.need_uavs.move(),
589 };
590
591 defer {
592 rendered_decl.need_uavs = dg.uavs.move();
593 dg.ctype_deps.deinit(gpa);
594 }
595
596 rendered_decl.fwd_decl = fwd_decl: {
597 var aw: std.Io.Writer.Allocating = .fromArrayList(gpa, &c.string_bytes);
598 defer c.string_bytes = aw.toArrayList();
599 const start = aw.written().len;
600 codegen.genDeclFwd(&dg, &aw.writer) catch |err| switch (err) {
601 error.AlreadyReported => return,
602 error.WriteFailed => return error.OutOfMemory,
603 error.Canceled, error.OutOfMemory => |e| return e,
604 };
605 break :fwd_decl .{
606 .start = @intCast(start),
607 .len = @intCast(aw.written().len - start),
608 };
609 };
610
611 rendered_decl.code = code: {
612 var aw: std.Io.Writer.Allocating = .fromArrayList(gpa, &c.string_bytes);
613 defer c.string_bytes = aw.toArrayList();
614 const start = aw.written().len;
615 codegen.genDecl(&dg, &aw.writer) catch |err| switch (err) {
616 error.AlreadyReported => return,
617 error.WriteFailed => return error.OutOfMemory,
618 error.Canceled, error.OutOfMemory => |e| return e,
619 };
620 break :code .{
621 .start = @intCast(start),
622 .len = @intCast(aw.written().len - start),
623 };
624 };
625
626 rendered_decl.ctype_deps = try c.addCTypeDependencies(pt, &dg.ctype_deps);
627 }
628
629 const old_uavs_len = c.uavs.count();
630 try c.uavs.ensureUnusedCapacity(gpa, rendered_decl.need_uavs.count());
631 for (rendered_decl.need_uavs.keys()) |val| {
632 const gop = c.uavs.getOrPutAssumeCapacity(val);
633 if (gop.found_existing) {
634 assert(gop.index < old_uavs_len);
635 } else {
636 assert(gop.index >= old_uavs_len);
637 }
638 }
639 try c.updateNewUavs(pt, old_uavs_len);
640
641 try c.type_pool.flushPending(pt, .{ .c = c });
642}
643
644/// Unlike `updateNav` and `updateFunc`, this does *not* add newly-discovered UAVs to `c.uavs`. The
645/// caller is instead responsible for doing that (by iterating `rendered_decl.need_uavs`). However,
646/// this function *does* still add newly-discovered *types* to `c.type_pool`.
647///
648/// This function does not accept an alignment for the UAV, because the alignment needed on a UAV is
649/// not known until `flush` (since we need to have seen all uses of the UAV first). Instead, `flush`
650/// will prefix the UAV definition with an appropriate alignment annotation if necessary.
651fn updateUav(
652 c: *C,
653 pt: Zcu.PerThread,
654 val: Value,
655 rendered_decl: *RenderedDecl,
656) link.Error!void {
657 const tracy = trace(@src());
658 defer tracy.end();
659
660 const gpa = c.base.comp.gpa;
661
662 var arena: std.heap.ArenaAllocator = .init(gpa);
663 defer arena.deinit();
664
665 var dg: codegen.DeclGen = .{
666 .gpa = gpa,
667 .arena = arena.allocator(),
668 .pt = pt,
669 .mod = pt.zcu.root_mod,
670 .owner_nav = .none,
671 .is_naked_fn = false,
672 .expected_block = null,
673 .ctype_deps = .empty,
674 .uavs = .empty,
675 };
676 defer {
677 rendered_decl.need_uavs = dg.uavs.move();
678 dg.ctype_deps.deinit(gpa);
679 }
680
681 rendered_decl.fwd_decl = fwd_decl: {
682 var aw: std.Io.Writer.Allocating = .fromArrayList(gpa, &c.string_bytes);
683 defer c.string_bytes = aw.toArrayList();
684 const start = aw.written().len;
685 codegen.genDeclValueFwd(&dg, &aw.writer, .{
686 .name = .{ .constant = val },
687 .@"const" = true,
688 .@"threadlocal" = false,
689 .init_val = val,
690 }) catch |err| switch (err) {
691 error.AlreadyReported => return,
692 error.WriteFailed => return error.OutOfMemory,
693 error.Canceled, error.OutOfMemory => |e| return e,
694 };
695 break :fwd_decl .{
696 .start = @intCast(start),
697 .len = @intCast(aw.written().len - start),
698 };
699 };
700
701 rendered_decl.code = code: {
702 var aw: std.Io.Writer.Allocating = .fromArrayList(gpa, &c.string_bytes);
703 defer c.string_bytes = aw.toArrayList();
704 const start = aw.written().len;
705 codegen.genDeclValue(&dg, &aw.writer, .{
706 .name = .{ .constant = val },
707 .@"const" = true,
708 .@"threadlocal" = false,
709 .init_val = val,
710 }) catch |err| switch (err) {
711 error.AlreadyReported => return,
712 error.WriteFailed => return error.OutOfMemory,
713 error.Canceled, error.OutOfMemory => |e| return e,
714 };
715 break :code .{
716 .start = @intCast(start),
717 .len = @intCast(aw.written().len - start),
718 };
719 };
720
721 rendered_decl.ctype_deps = try c.addCTypeDependencies(pt, &dg.ctype_deps);
722}
723
724pub fn updateLineNumber(c: *C, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index, line: u32) error{}!void {
725 // The C backend does not currently emit "#line" directives. Even if it did, it would not be
726 // capable of updating those line numbers without re-generating the entire declaration.
727 _ = c;
728 _ = pt;
729 _ = ti_id;
730 _ = line;
731}
732
733pub fn flush(c: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.Error!void {
734 const tracy = trace(@src());
735 defer tracy.end();
736
737 const sub_prog_node = prog_node.start("Flush Module", 0);
738 defer sub_prog_node.end();
739
740 const comp = c.base.comp;
741 const diags = &comp.link_diags;
742 const gpa = comp.gpa;
743 const io = comp.io;
744 const zcu = c.base.comp.zcu.?;
745 const ip = &zcu.intern_pool;
746 const active = zcu.activate(tid);
747 defer active.deactivate();
748 const pt = active.pt;
749
750 // If it's somehow not made it into the pool, we need to generate the type `[:0]const u8` for
751 // error names.
752 const slice_const_u8_sentinel_0_pool_index = try c.type_pool.get(
753 pt,
754 .{ .c = c },
755 .slice_const_u8_sentinel_0_type,
756 );
757 try c.type_pool.flushPending(pt, .{ .c = c });
758
759 // Find the set of referenced NAVs; these are the ones we'll emit. It is important in this
760 // backend that we only emit referenced NAVs, because other ones may contain code from past
761 // incremental updates which is invalid C (due to e.g. types changing). Machine code backends
762 // don't have this problem because there are, of course, no type checking performed when you
763 // *execute* a binary!
764 var need_navs: std.array_hash_map.Auto(InternPool.Nav.Index, void) = .empty;
765 defer need_navs.deinit(gpa);
766 {
767 const unit_references = try zcu.resolveReferences();
768 for (c.navs.keys()) |nav| {
769 const nav_val = ip.getNav(nav).resolved.?.value;
770 const check_unit: ?InternPool.AnalUnit = switch (ip.indexToKey(nav_val)) {
771 else => .wrap(.{ .nav_val = nav }),
772 .func => .wrap(.{ .func = nav_val }),
773 // TODO: this is a hack to deal with the fact that there's currently no good way to
774 // know which `extern`s are alive. This can and will break in certain patterns of
775 // incremental update. We kind of need to think a bit more about how the frontend
776 // actually represents `extern`, it's a bit awkward right now.
777 .@"extern" => null,
778 };
779 if (check_unit) |u| {
780 if (!unit_references.contains(u)) continue;
781 }
782 try need_navs.putNoClobber(gpa, nav, {});
783 }
784 }
785
786 // Using our knowledge of which NAVs are referenced, we now need to discover the set of UAVs and
787 // C types which are referenced (and hence must be emitted). As above, this is necessary to make
788 // sure we only emit valid C code.
789 //
790 // At the same time, we will discover the set of lazy functions which are referenced.
791
792 var need_uavs: std.array_hash_map.Auto(InternPool.Index, Alignment) = .empty;
793 defer need_uavs.deinit(gpa);
794
795 var need_types: std.array_hash_map.Auto(link.ConstPool.Index, void) = .empty;
796 defer need_types.deinit(gpa);
797 var need_errunion_types: std.array_hash_map.Auto(link.ConstPool.Index, void) = .empty;
798 defer need_errunion_types.deinit(gpa);
799 var need_aligned_types: std.array_hash_map.Auto(link.ConstPool.Index, u64) = .empty;
800 defer need_aligned_types.deinit(gpa);
801
802 var need_tag_name_funcs: std.array_hash_map.Auto(InternPool.Index, void) = .empty;
803 defer need_tag_name_funcs.deinit(gpa);
804
805 var need_never_tail_funcs: std.array_hash_map.Auto(InternPool.Nav.Index, void) = .empty;
806 defer need_never_tail_funcs.deinit(gpa);
807
808 var need_never_inline_funcs: std.array_hash_map.Auto(InternPool.Nav.Index, void) = .empty;
809 defer need_never_inline_funcs.deinit(gpa);
810
811 // As mentioned above, we need this type for error names.
812 try need_types.put(gpa, slice_const_u8_sentinel_0_pool_index, {});
813
814 // Every exported NAV should have been discovered via `zcu.resolveReferences`...
815 for (c.exported_navs.keys()) |nav| assert(need_navs.contains(nav));
816 // ...but we *do* need to add exported UAVs to the set.
817 try need_uavs.ensureUnusedCapacity(gpa, c.exported_uavs.count());
818 for (c.exported_uavs.keys()) |uav| {
819 const gop = need_uavs.getOrPutAssumeCapacity(uav);
820 if (!gop.found_existing) gop.value_ptr.* = .none;
821 }
822
823 // For every referenced NAV, some UAVs, C types, and lazy functions may be referenced.
824 for (need_navs.keys()) |nav| {
825 const rendered = c.navs.getPtr(nav).?;
826 try mergeNeededCTypes(
827 c,
828 &need_types,
829 &need_errunion_types,
830 &need_aligned_types,
831 &rendered.ctype_deps,
832 );
833 try mergeNeededUavs(zcu, &need_uavs, &rendered.need_uavs);
834
835 try need_tag_name_funcs.ensureUnusedCapacity(gpa, rendered.need_tag_name_funcs.count());
836 for (rendered.need_tag_name_funcs.keys()) |enum_type| {
837 need_tag_name_funcs.putAssumeCapacity(enum_type, {});
838 }
839
840 try need_never_tail_funcs.ensureUnusedCapacity(gpa, rendered.need_never_tail_funcs.count());
841 for (rendered.need_never_tail_funcs.keys()) |fn_nav| {
842 need_never_tail_funcs.putAssumeCapacity(fn_nav, {});
843 }
844
845 try need_never_inline_funcs.ensureUnusedCapacity(gpa, rendered.need_never_inline_funcs.count());
846 for (rendered.need_never_inline_funcs.keys()) |fn_nav| {
847 need_never_inline_funcs.putAssumeCapacity(fn_nav, {});
848 }
849 }
850
851 // UAVs may reference other UAVs or C types.
852 {
853 var index: usize = 0;
854 while (need_uavs.count() > index) : (index += 1) {
855 const val = need_uavs.keys()[index];
856 const rendered = c.uavs.getPtr(val).?;
857 try mergeNeededCTypes(
858 c,
859 &need_types,
860 &need_errunion_types,
861 &need_aligned_types,
862 &rendered.ctype_deps,
863 );
864 try mergeNeededUavs(zcu, &need_uavs, &rendered.need_uavs);
865 }
866 }
867
868 // Finally, C types may reference other C types.
869 {
870 var index: usize = 0;
871 var errunion_index: usize = 0;
872 var aligned_index: usize = 0;
873 while (true) {
874 if (index < need_types.count()) {
875 const pool_index = need_types.keys()[index];
876 const rendered = &c.types.items[@backingInt(pool_index)];
877 try mergeNeededCTypes(
878 c,
879 &need_types,
880 &need_errunion_types,
881 &need_aligned_types,
882 &rendered.definition_deps, // we're tasked with emitting the *definition* of this type
883 );
884 index += 1;
885 continue;
886 }
887
888 if (errunion_index < need_errunion_types.count()) {
889 const payload_pool_index = need_errunion_types.keys()[errunion_index];
890 const rendered = &c.types.items[@backingInt(payload_pool_index)];
891 try mergeNeededCTypes(
892 c,
893 &need_types,
894 &need_errunion_types,
895 &need_aligned_types,
896 &rendered.deps, // the error union type requires emitting this type's *name*
897 );
898 errunion_index += 1;
899 continue;
900 }
901
902 if (aligned_index < need_aligned_types.count()) {
903 const pool_index = need_aligned_types.keys()[aligned_index];
904 const rendered = &c.types.items[@backingInt(pool_index)];
905 try mergeNeededCTypes(
906 c,
907 &need_types,
908 &need_errunion_types,
909 &need_aligned_types,
910 &rendered.deps, // an aligned typedef requires emitting this type's *name*
911 );
912 aligned_index += 1;
913 continue;
914 }
915
916 break;
917 }
918 }
919
920 // Now that we know which types are required, generate aligned typedefs. One buffer per aligned
921 // type, with *all* aligned typedefs for that type.
922 const aligned_type_strings = try arena.alloc([]const u8, need_aligned_types.count());
923 {
924 var aw: std.Io.Writer.Allocating = .init(gpa);
925 defer aw.deinit();
926 var unused_deps: codegen.CType.Dependencies = .empty;
927 defer unused_deps.deinit(gpa);
928 for (
929 need_aligned_types.keys(),
930 need_aligned_types.values(),
931 aligned_type_strings,
932 ) |pool_index, align_mask, *str_out| {
933 const ty: Type = .fromInterned(pool_index.val(&c.type_pool));
934 const has_layout = c.types.items[@backingInt(pool_index)].errunion_definition.len > 0;
935 for (0..@bitSizeOf(@TypeOf(align_mask))) |bit_index| {
936 switch (@as(u1, @truncate(align_mask >> @intCast(bit_index)))) {
937 0 => continue,
938 1 => {},
939 }
940 codegen.CType.render_defs.defineAligned(
941 ty,
942 .fromLog2Units(@intCast(bit_index)),
943 has_layout,
944 &unused_deps,
945 arena,
946 &aw.writer,
947 pt,
948 ) catch |err| switch (err) {
949 error.WriteFailed => return error.OutOfMemory,
950 error.OutOfMemory => |e| return e,
951 };
952 }
953 str_out.* = try arena.dupe(u8, aw.written());
954 aw.clearRetainingCapacity();
955 }
956 }
957
958 // We have discovered the full set of NAVs, UAVs, and types we need to emit, and will now begin
959 // to build the output buffer. Our strategy is to emit the C source in this order:
960 //
961 // * Header
962 // * Big-int type definitions
963 // * Other CType definitions (traversing the dependency graph to sort topologically)
964 // * Global assembly
965 // * UAV exports
966 // * NAV exports
967 // * UAV forward declarations
968 // * NAV forward declarations
969 // * Lazy declarations (error names; @tagName functions; never_tail/never_inline wrappers)
970 // * UAV definitions
971 // * NAV definitions
972 //
973 // Most of these sections are order-independent within themselves, with the exception of the
974 // type definitions, which must be ordered to avoid a struct/union from embedding a type which
975 // is currently incomplete.
976 //
977 // When emitting UAV forward declarations, if the UAV requires alignment, we must prefix it with
978 // an alignment annotation. We couldn't emit the alignment into the UAV's `RenderedDecl` because
979 // we couldn't have known the required alignment until now!
980
981 var f: Flush = .{ .all_buffers = .empty, .file_size = 0 };
982 defer f.deinit(gpa);
983
984 // We know exactly what we'll be emitting, so can reserve capacity for all of our buffers!
985
986 try f.all_buffers.ensureUnusedCapacity(gpa, 1 + // Header
987 1 + // Big-int type definitions
988 need_types.count() + // `RenderedType.fwd_decl` (worst-case)
989 need_types.count() + // `RenderedType.definition`
990 need_errunion_types.count() + // `RenderedType.errunion_fwd_decl` (worst-case)
991 need_errunion_types.count() + // `RenderedType.errunion_definition`
992 need_aligned_types.count() + // `aligned_type_strings`
993 1 + // Global assembly
994 c.exported_uavs.count() + // UAV export block
995 c.exported_navs.count() + // NAV export block
996 need_uavs.count() + // UAV forward declarations
997 need_navs.count() + // NAV forward declarations
998 1 + // Lazy declarations
999 need_uavs.count() * 3 + // UAV definitions ("static ", "zig_align(4)", "<definition body>")
1000 need_navs.count() * 2); // NAV definitions ("static ", "<definition body>")
1001
1002 f.appendBufAssumeCapacity(c.header.get(c));
1003
1004 // Big-int type definitions
1005 var bigint_aw: std.Io.Writer.Allocating = .init(gpa);
1006 defer bigint_aw.deinit();
1007 for (c.bigint_types.keys()) |bigint| {
1008 codegen.CType.render_defs.defineBigInt(bigint, &bigint_aw.writer, zcu) catch |err| switch (err) {
1009 error.WriteFailed => return error.OutOfMemory,
1010 };
1011 }
1012 f.appendBufAssumeCapacity(bigint_aw.written());
1013
1014 // CType definitions
1015 {
1016 var ft: FlushTypes = .{
1017 .c = c,
1018 .f = &f,
1019 .aligned_types = &need_aligned_types,
1020 .aligned_type_strings = aligned_type_strings,
1021 .status = .empty,
1022 .errunion_status = .empty,
1023 .aligned_status = .empty,
1024 };
1025 defer {
1026 ft.status.deinit(gpa);
1027 ft.errunion_status.deinit(gpa);
1028 ft.aligned_status.deinit(gpa);
1029 }
1030 try ft.status.ensureUnusedCapacity(gpa, need_types.count());
1031 try ft.errunion_status.ensureUnusedCapacity(gpa, need_errunion_types.count());
1032 try ft.aligned_status.ensureUnusedCapacity(gpa, need_aligned_types.count());
1033
1034 for (need_types.keys()) |pool_index| {
1035 ft.doType(pool_index);
1036 }
1037 for (need_errunion_types.keys()) |pool_index| {
1038 ft.doErrunionType(pool_index);
1039 }
1040 for (need_aligned_types.keys()) |pool_index| {
1041 ft.doAlignedTypeFwd(pool_index);
1042 }
1043 }
1044
1045 // Global assembly
1046 var asm_aw: std.Io.Writer.Allocating = .init(gpa);
1047 defer asm_aw.deinit();
1048 codegen.genGlobalAsm(zcu, &asm_aw.writer) catch |err| switch (err) {
1049 error.WriteFailed => return error.OutOfMemory,
1050 };
1051 f.appendBufAssumeCapacity(asm_aw.written());
1052
1053 var export_names: std.AutoHashMapUnmanaged(InternPool.NullTerminatedString, void) = .empty;
1054 defer export_names.deinit(gpa);
1055 try export_names.ensureTotalCapacity(gpa, @intCast(zcu.single_exports.count()));
1056 for (zcu.single_exports.values()) |export_index| {
1057 export_names.putAssumeCapacity(export_index.ptr(zcu).opts.name, {});
1058 }
1059 for (zcu.multi_exports.values()) |info| {
1060 try export_names.ensureUnusedCapacity(gpa, info.len);
1061 for (zcu.all_exports.items[info.index..][0..info.len]) |@"export"| {
1062 export_names.putAssumeCapacity(@"export".opts.name, {});
1063 }
1064 }
1065
1066 // UAV export block
1067 for (c.exported_uavs.values()) |code| {
1068 f.appendBufAssumeCapacity(code.get(c));
1069 }
1070
1071 // NAV export block
1072 for (c.exported_navs.values()) |code| {
1073 f.appendBufAssumeCapacity(code.get(c));
1074 }
1075
1076 // UAV forward declarations
1077 for (need_uavs.keys()) |val| {
1078 if (c.exported_uavs.contains(val)) continue; // the export was the declaration
1079 const fwd_decl = c.uavs.getPtr(val).?.fwd_decl;
1080 f.appendBufAssumeCapacity(fwd_decl.get(c));
1081 }
1082
1083 // NAV forward declarations
1084 for (need_navs.keys()) |nav| {
1085 if (c.exported_navs.contains(nav)) continue; // the export was the declaration
1086 switch (ip.indexToKey(ip.getNav(nav).resolved.?.value)) {
1087 .@"extern" => |e| if (export_names.contains(e.name)) continue,
1088 else => {},
1089 }
1090 const fwd_decl = c.navs.getPtr(nav).?.fwd_decl;
1091 f.appendBufAssumeCapacity(fwd_decl.get(c));
1092 }
1093
1094 // Lazy declarations
1095 var lazy_decls_aw: std.Io.Writer.Allocating = .init(gpa);
1096 defer lazy_decls_aw.deinit();
1097 {
1098 var lazy_dg: codegen.DeclGen = .{
1099 .gpa = gpa,
1100 .arena = arena,
1101 .pt = pt,
1102 .mod = pt.zcu.root_mod,
1103 .owner_nav = .none,
1104 .is_naked_fn = false,
1105 .expected_block = null,
1106 .ctype_deps = .empty,
1107 .uavs = .empty,
1108 };
1109 defer {
1110 assert(lazy_dg.uavs.count() == 0);
1111 lazy_dg.ctype_deps.deinit(gpa);
1112 }
1113 const slice_const_u8_sentinel_0_cty: codegen.CType = try .lower(
1114 .slice_const_u8_sentinel_0,
1115 &lazy_dg.ctype_deps,
1116 arena,
1117 zcu,
1118 );
1119 const slice_const_u8_sentinel_0_name = try std.fmt.allocPrint(
1120 arena,
1121 "{f}",
1122 .{slice_const_u8_sentinel_0_cty.fmtTypeName(zcu)},
1123 );
1124 codegen.genErrDecls(zcu, &lazy_decls_aw.writer, slice_const_u8_sentinel_0_name) catch |err| switch (err) {
1125 error.WriteFailed => return error.OutOfMemory,
1126 };
1127 for (need_tag_name_funcs.keys()) |enum_ty_ip| {
1128 const enum_ty: Type = .fromInterned(enum_ty_ip);
1129 const enum_cty: codegen.CType = try .lower(
1130 enum_ty,
1131 &lazy_dg.ctype_deps,
1132 arena,
1133 zcu,
1134 );
1135 codegen.genTagNameFn(
1136 zcu,
1137 &lazy_decls_aw.writer,
1138 slice_const_u8_sentinel_0_name,
1139 enum_ty,
1140 try std.fmt.allocPrint(arena, "{f}", .{enum_cty.fmtTypeName(zcu)}),
1141 ) catch |err| switch (err) {
1142 error.WriteFailed => return error.OutOfMemory,
1143 };
1144 }
1145 for (need_never_tail_funcs.keys()) |fn_nav| {
1146 codegen.genLazyCallModifierFn(&lazy_dg, fn_nav, .never_tail, &lazy_decls_aw.writer) catch |err| switch (err) {
1147 error.WriteFailed => return error.OutOfMemory,
1148 error.Canceled, error.OutOfMemory => |e| return e,
1149 error.AlreadyReported => unreachable,
1150 };
1151 }
1152 for (need_never_inline_funcs.keys()) |fn_nav| {
1153 codegen.genLazyCallModifierFn(&lazy_dg, fn_nav, .never_inline, &lazy_decls_aw.writer) catch |err| switch (err) {
1154 error.WriteFailed => return error.OutOfMemory,
1155 error.Canceled, error.OutOfMemory => |e| return e,
1156 error.AlreadyReported => unreachable,
1157 };
1158 }
1159 }
1160 f.appendBufAssumeCapacity(lazy_decls_aw.written());
1161
1162 // UAV definitions
1163 for (need_uavs.keys(), need_uavs.values()) |val, overalign| {
1164 const code = c.uavs.getPtr(val).?.code;
1165 if (code.len == 0) continue;
1166 if (!c.exported_uavs.contains(val)) {
1167 f.appendBufAssumeCapacity("static ");
1168 }
1169 if (overalign != .none) {
1170 // As long as `Alignment` isn't too big, it's reasonable to just generate all possible
1171 // alignment annotations statically into a LUT, which avoids allocating strings on this
1172 // path.
1173 comptime assert(@bitSizeOf(Alignment) < 8);
1174 const table_len = (1 << @bitSizeOf(Alignment)) - 1;
1175 const table: [table_len][]const u8 = comptime table: {
1176 @setEvalBranchQuota(16_000);
1177 var table: [table_len][]const u8 = undefined;
1178 for (&table, 0..) |*str, log2_align| {
1179 const byte_align = Alignment.fromLog2Units(log2_align).toByteUnits().?;
1180 str.* = std.fmt.comptimePrint("zig_align({d}) ", .{byte_align});
1181 }
1182 break :table table;
1183 };
1184 f.appendBufAssumeCapacity(table[overalign.toLog2Units()]);
1185 }
1186 f.appendBufAssumeCapacity(code.get(c));
1187 }
1188
1189 // NAV definitions
1190 for (need_navs.keys()) |nav| {
1191 const code = c.navs.getPtr(nav).?.code;
1192 if (code.len == 0) continue;
1193 if (!c.exported_navs.contains(nav)) {
1194 const is_extern = ip.indexToKey(ip.getNav(nav).resolved.?.value) == .@"extern";
1195 f.appendBufAssumeCapacity(if (is_extern) "zig_extern " else "static ");
1196 }
1197 f.appendBufAssumeCapacity(code.get(c));
1198 }
1199
1200 // We've collected all of our buffers; it's now time to actually write the file!
1201 const file = c.base.file.?;
1202 file.setLength(io, f.file_size) catch |err| return diags.fail("failed to allocate file: {t}", .{err});
1203 var fw = file.writer(io, &.{});
1204 var w = &fw.interface;
1205 w.writeVecAll(f.all_buffers.items) catch |err| switch (err) {
1206 error.WriteFailed => return diags.fail("failed to write to '{f}': {s}", .{
1207 std.fmt.alt(c.base.emit, .formatEscapeChar), @errorName(fw.err.?),
1208 }),
1209 };
1210}
1211
1212const Flush = struct {
1213 /// We collect a list of buffers to write, and write them all at once with pwritev 😎
1214 all_buffers: std.ArrayList([]const u8),
1215 /// Keeps track of the total bytes of `all_buffers`.
1216 file_size: u64,
1217
1218 fn appendBufAssumeCapacity(f: *Flush, buf: []const u8) void {
1219 if (buf.len == 0) return;
1220 f.all_buffers.appendAssumeCapacity(buf);
1221 f.file_size += buf.len;
1222 }
1223
1224 fn deinit(f: *Flush, gpa: Allocator) void {
1225 f.all_buffers.deinit(gpa);
1226 }
1227};
1228
1229pub fn updateExports(
1230 c: *C,
1231 pt: Zcu.PerThread,
1232 export_indices: []const Zcu.Export.Index,
1233) Allocator.Error!void {
1234 const zcu = pt.zcu;
1235 const gpa = zcu.gpa;
1236
1237 c.exported_navs.clearRetainingCapacity();
1238 c.exported_uavs.clearRetainingCapacity();
1239
1240 var arena: std.heap.ArenaAllocator = .init(gpa);
1241 defer arena.deinit();
1242
1243 var by_exported: std.array_hash_map.Auto(Zcu.Exported, std.ArrayList(Zcu.Export.Index)) = .empty;
1244 try by_exported.ensureUnusedCapacity(arena.allocator(), export_indices.len);
1245
1246 for (export_indices) |exp_index| {
1247 const exported = exp_index.ptr(zcu).exported;
1248 const gop = by_exported.getOrPutAssumeCapacity(exported);
1249 if (!gop.found_existing) {
1250 gop.value_ptr.* = .empty;
1251 }
1252 try gop.value_ptr.append(arena.allocator(), exp_index);
1253 }
1254
1255 for (by_exported.keys(), by_exported.values()) |exported, *exports_of_this| {
1256 var dg: codegen.DeclGen = .{
1257 .gpa = gpa,
1258 .arena = arena.allocator(),
1259 .pt = pt,
1260 .mod = zcu.root_mod,
1261 .owner_nav = .none,
1262 .is_naked_fn = false,
1263 .expected_block = null,
1264 .ctype_deps = .empty,
1265 .uavs = .empty,
1266 };
1267 defer {
1268 assert(dg.uavs.count() == 0);
1269 dg.ctype_deps.deinit(gpa);
1270 }
1271 const code: String = code: {
1272 var aw: std.Io.Writer.Allocating = .fromArrayList(gpa, &c.string_bytes);
1273 defer c.string_bytes = aw.toArrayList();
1274 const start = aw.written().len;
1275 codegen.genExports(&dg, &aw.writer, exported, exports_of_this.items) catch |err| switch (err) {
1276 error.WriteFailed => return error.OutOfMemory,
1277 error.OutOfMemory => |e| return e,
1278 };
1279 break :code .{
1280 .start = @intCast(start),
1281 .len = @intCast(aw.written().len - start),
1282 };
1283 };
1284 switch (exported) {
1285 .nav => |nav| try c.exported_navs.put(gpa, nav, code),
1286 .uav => |uav| try c.exported_uavs.put(gpa, uav, code),
1287 }
1288 }
1289}
1290
1291fn mergeNeededCTypes(
1292 c: *C,
1293 need_types: *std.array_hash_map.Auto(link.ConstPool.Index, void),
1294 need_errunion_types: *std.array_hash_map.Auto(link.ConstPool.Index, void),
1295 need_aligned_types: *std.array_hash_map.Auto(link.ConstPool.Index, u64),
1296 deps: *const CTypeDependencies,
1297) Allocator.Error!void {
1298 const gpa = c.base.comp.gpa;
1299
1300 const resolved = deps.get(c);
1301
1302 try need_types.ensureUnusedCapacity(gpa, resolved.type.len + resolved.type_fwd.len);
1303 try need_errunion_types.ensureUnusedCapacity(gpa, resolved.errunion_type.len + resolved.errunion_type_fwd.len);
1304 try need_aligned_types.ensureUnusedCapacity(gpa, resolved.aligned_type_fwd.len);
1305
1306 for (resolved.type) |index| need_types.putAssumeCapacity(index, {});
1307 for (resolved.type_fwd) |index| need_types.putAssumeCapacity(index, {});
1308
1309 for (resolved.errunion_type) |index| need_errunion_types.putAssumeCapacity(index, {});
1310 for (resolved.errunion_type_fwd) |index| need_errunion_types.putAssumeCapacity(index, {});
1311
1312 for (resolved.aligned_type_fwd, resolved.aligned_type_masks) |ty_index, align_mask| {
1313 const gop = need_aligned_types.getOrPutAssumeCapacity(ty_index);
1314 if (!gop.found_existing) gop.value_ptr.* = 0;
1315 gop.value_ptr.* |= align_mask;
1316 }
1317}
1318
1319fn mergeNeededUavs(
1320 zcu: *const Zcu,
1321 global: *std.array_hash_map.Auto(InternPool.Index, Alignment),
1322 new: *const std.array_hash_map.Auto(InternPool.Index, Alignment),
1323) Allocator.Error!void {
1324 const gpa = zcu.comp.gpa;
1325
1326 try global.ensureUnusedCapacity(gpa, new.count());
1327 for (new.keys(), new.values()) |uav_val, need_align| {
1328 const gop = global.getOrPutAssumeCapacity(uav_val);
1329 if (!gop.found_existing) gop.value_ptr.* = .none;
1330
1331 if (need_align != .none) {
1332 const cur_align = switch (gop.value_ptr.*) {
1333 .none => Value.fromInterned(uav_val).typeOf(zcu).abiAlignment(zcu),
1334 else => |a| a,
1335 };
1336 if (need_align.compareStrict(.gt, cur_align)) {
1337 gop.value_ptr.* = need_align;
1338 }
1339 }
1340 }
1341}
1342
1343fn addCTypeDependencies(
1344 c: *C,
1345 pt: Zcu.PerThread,
1346 deps: *const codegen.CType.Dependencies,
1347) link.Error!CTypeDependencies {
1348 const gpa = pt.zcu.comp.gpa;
1349
1350 try c.bigint_types.ensureUnusedCapacity(gpa, deps.bigint.count());
1351 for (deps.bigint.keys()) |bigint| c.bigint_types.putAssumeCapacity(bigint, {});
1352
1353 const type_start = c.type_dependencies.items.len;
1354 const errunion_type_start = type_start + deps.type.count();
1355 const type_fwd_start = errunion_type_start + deps.errunion_type.count();
1356 const errunion_type_fwd_start = type_fwd_start + deps.type_fwd.count();
1357 const aligned_type_fwd_start = errunion_type_fwd_start + deps.errunion_type_fwd.count();
1358 try c.type_dependencies.appendNTimes(gpa, undefined, deps.type.count() +
1359 deps.errunion_type.count() +
1360 deps.type_fwd.count() +
1361 deps.errunion_type_fwd.count() +
1362 deps.aligned_type_fwd.count());
1363
1364 const align_mask_start = c.align_dependency_masks.items.len;
1365 try c.align_dependency_masks.appendSlice(gpa, deps.aligned_type_fwd.values());
1366
1367 for (deps.type.keys(), type_start..) |ty, i| {
1368 const pool_index = try c.type_pool.get(pt, .{ .c = c }, ty);
1369 c.type_dependencies.items[i] = pool_index;
1370 }
1371
1372 for (deps.errunion_type.keys(), errunion_type_start..) |ty, i| {
1373 const pool_index = try c.type_pool.get(pt, .{ .c = c }, ty);
1374 c.type_dependencies.items[i] = pool_index;
1375 }
1376
1377 for (deps.type_fwd.keys(), type_fwd_start..) |ty, i| {
1378 const pool_index = try c.type_pool.get(pt, .{ .c = c }, ty);
1379 c.type_dependencies.items[i] = pool_index;
1380 }
1381
1382 for (deps.errunion_type_fwd.keys(), errunion_type_fwd_start..) |ty, i| {
1383 const pool_index = try c.type_pool.get(pt, .{ .c = c }, ty);
1384 c.type_dependencies.items[i] = pool_index;
1385 }
1386
1387 for (deps.aligned_type_fwd.keys(), aligned_type_fwd_start..) |ty, i| {
1388 const pool_index = try c.type_pool.get(pt, .{ .c = c }, ty);
1389 c.type_dependencies.items[i] = pool_index;
1390 }
1391
1392 return .{
1393 .len = @intCast(deps.type.count()),
1394 .errunion_len = @intCast(deps.errunion_type.count()),
1395 .fwd_len = @intCast(deps.type_fwd.count()),
1396 .errunion_fwd_len = @intCast(deps.errunion_type_fwd.count()),
1397 .aligned_fwd_len = @intCast(deps.aligned_type_fwd.count()),
1398 .type_start = @intCast(type_start),
1399 .align_mask_start = @intCast(align_mask_start),
1400 };
1401}
1402
1403fn updateNewUavs(c: *C, pt: Zcu.PerThread, old_uavs_len: usize) link.Error!void {
1404 const gpa = pt.zcu.comp.gpa;
1405 var index = old_uavs_len;
1406 while (index < c.uavs.count()) : (index += 1) {
1407 // `new_uavs` is UAVs discovered while lowering *this* UAV.
1408 const new_uavs: []const InternPool.Index = new: {
1409 c.uavs.lockPointers();
1410 defer c.uavs.unlockPointers();
1411 const val: Value = .fromInterned(c.uavs.keys()[index]);
1412 const rendered_decl = &c.uavs.values()[index];
1413 rendered_decl.* = .init;
1414 try c.updateUav(pt, val, rendered_decl);
1415 break :new rendered_decl.need_uavs.keys();
1416 };
1417 try c.uavs.ensureUnusedCapacity(gpa, new_uavs.len);
1418 for (new_uavs) |val| {
1419 const gop = c.uavs.getOrPutAssumeCapacity(val);
1420 if (!gop.found_existing) {
1421 assert(gop.index > index);
1422 }
1423 }
1424 }
1425}
1426
1427const FlushTypes = struct {
1428 c: *C,
1429 f: *Flush,
1430
1431 aligned_types: *const std.array_hash_map.Auto(link.ConstPool.Index, u64),
1432 aligned_type_strings: []const []const u8,
1433
1434 status: std.array_hash_map.Auto(link.ConstPool.Index, bool),
1435 errunion_status: std.array_hash_map.Auto(link.ConstPool.Index, bool),
1436 aligned_status: std.array_hash_map.Auto(link.ConstPool.Index, void),
1437
1438 fn processDeps(ft: *FlushTypes, deps: *const CTypeDependencies) void {
1439 const resolved = deps.get(ft.c);
1440 for (resolved.type) |pool_index| ft.doType(pool_index);
1441 for (resolved.type_fwd) |pool_index| ft.doTypeFwd(pool_index);
1442 for (resolved.errunion_type) |pool_index| ft.doErrunionType(pool_index);
1443 for (resolved.errunion_type_fwd) |pool_index| ft.doErrunionTypeFwd(pool_index);
1444 for (resolved.aligned_type_fwd) |pool_index| ft.doAlignedTypeFwd(pool_index);
1445 }
1446 fn processDepsAsFwd(ft: *FlushTypes, deps: *const CTypeDependencies) void {
1447 const resolved = deps.get(ft.c);
1448 for (resolved.type) |pool_index| ft.doTypeFwd(pool_index);
1449 for (resolved.type_fwd) |pool_index| ft.doTypeFwd(pool_index);
1450 for (resolved.errunion_type) |pool_index| ft.doErrunionTypeFwd(pool_index);
1451 for (resolved.errunion_type_fwd) |pool_index| ft.doErrunionTypeFwd(pool_index);
1452 for (resolved.aligned_type_fwd) |pool_index| ft.doAlignedTypeFwd(pool_index);
1453 }
1454
1455 fn doAlignedTypeFwd(ft: *FlushTypes, pool_index: link.ConstPool.Index) void {
1456 const c = ft.c;
1457 if (ft.aligned_status.contains(pool_index)) return;
1458 if (ft.aligned_types.getIndex(pool_index)) |i| {
1459 const rendered = &c.types.items[@backingInt(pool_index)];
1460 ft.processDepsAsFwd(&rendered.deps);
1461 ft.f.appendBufAssumeCapacity(ft.aligned_type_strings[i]);
1462 }
1463 ft.aligned_status.putAssumeCapacity(pool_index, {});
1464 }
1465 fn doTypeFwd(ft: *FlushTypes, pool_index: link.ConstPool.Index) void {
1466 const c = ft.c;
1467 if (ft.status.contains(pool_index)) return;
1468 const rendered = &c.types.items[@backingInt(pool_index)];
1469 if (rendered.fwd_decl.len > 0) {
1470 ft.f.appendBufAssumeCapacity(rendered.fwd_decl.get(c));
1471 ft.status.putAssumeCapacityNoClobber(pool_index, false);
1472 } else {
1473 ft.processDepsAsFwd(&rendered.definition_deps);
1474 const gop = ft.status.getOrPutAssumeCapacity(pool_index);
1475 if (!gop.found_existing) {
1476 gop.value_ptr.* = false;
1477 ft.f.appendBufAssumeCapacity(rendered.definition.get(c));
1478 }
1479 }
1480 }
1481 fn doType(ft: *FlushTypes, pool_index: link.ConstPool.Index) void {
1482 const c = ft.c;
1483 if (ft.status.get(pool_index)) |completed| {
1484 if (completed) return;
1485 }
1486 const rendered = &c.types.items[@backingInt(pool_index)];
1487 ft.processDeps(&rendered.definition_deps);
1488 if (rendered.fwd_decl.len == 0 and ft.status.contains(pool_index)) {
1489 // `doTypeFwd` already rendered the defintion, we just had to complete the type by
1490 // fully resolving its dependencies.
1491 } else if (rendered.definition.len > 0) {
1492 ft.f.appendBufAssumeCapacity(rendered.definition.get(c));
1493 } else if (!ft.status.contains(pool_index)) {
1494 // The type will never be completed, but it must be forward declared to avoid it being
1495 // declared in the wrong scope.
1496 ft.f.appendBufAssumeCapacity(rendered.fwd_decl.get(c));
1497 }
1498 ft.status.putAssumeCapacity(pool_index, true);
1499 }
1500 fn doErrunionTypeFwd(ft: *FlushTypes, pool_index: link.ConstPool.Index) void {
1501 const c = ft.c;
1502 const gop = ft.errunion_status.getOrPutAssumeCapacity(pool_index);
1503 if (gop.found_existing) return;
1504 const rendered = &c.types.items[@backingInt(pool_index)];
1505 ft.f.appendBufAssumeCapacity(rendered.errunion_fwd_decl.get(c));
1506 gop.value_ptr.* = false;
1507 }
1508 fn doErrunionType(ft: *FlushTypes, pool_index: link.ConstPool.Index) void {
1509 const c = ft.c;
1510 if (ft.errunion_status.get(pool_index)) |completed| {
1511 if (completed) return;
1512 }
1513 const rendered = &c.types.items[@backingInt(pool_index)];
1514 ft.processDeps(&rendered.deps);
1515 if (rendered.errunion_definition.len > 0) {
1516 ft.f.appendBufAssumeCapacity(rendered.errunion_definition.get(c));
1517 } else {
1518 // The error union type will never be completed, but forward declare it to avoid the
1519 // type being first declared in a different scope.
1520 ft.f.appendBufAssumeCapacity(rendered.errunion_fwd_decl.get(c));
1521 }
1522 ft.errunion_status.putAssumeCapacity(pool_index, true);
1523 }
1524};