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) Allocator.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) Allocator.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) Allocator.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(
540 c: *C,
541 pt: Zcu.PerThread,
542 nav_index: InternPool.Nav.Index,
543) Allocator.Error!void {
544 const tracy = trace(@src());
545 defer tracy.end();
546
547 const gpa = c.base.comp.gpa;
548 const zcu = pt.zcu;
549 const ip = &zcu.intern_pool;
550
551 const nav = ip.getNav(nav_index);
552 switch (ip.indexToKey(nav.resolved.?.value)) {
553 .func => return,
554 .@"extern" => {},
555 else => {
556 const nav_ty: Type = .fromInterned(nav.resolved.?.type);
557 if (!nav_ty.hasRuntimeBits(zcu)) {
558 if (c.navs.fetchSwapRemove(nav_index)) |kv| {
559 var old_rendered = kv.value;
560 old_rendered.deinit(gpa);
561 }
562 return;
563 }
564 },
565 }
566
567 const rendered_decl: *RenderedDecl = rd: {
568 const gop = try c.navs.getOrPut(gpa, nav_index);
569 if (gop.found_existing) {
570 gop.value_ptr.clearRetainingCapacity();
571 } else {
572 gop.value_ptr.* = .init;
573 }
574 break :rd gop.value_ptr;
575 };
576 c.navs.lockPointers();
577 defer c.navs.unlockPointers();
578
579 {
580 var arena: std.heap.ArenaAllocator = .init(gpa);
581 defer arena.deinit();
582
583 var dg: codegen.DeclGen = .{
584 .gpa = gpa,
585 .arena = arena.allocator(),
586 .pt = pt,
587 .mod = zcu.navFileScope(nav_index).mod.?,
588 .owner_nav = nav_index.toOptional(),
589 .is_naked_fn = false,
590 .expected_block = null,
591 .ctype_deps = .empty,
592 .uavs = rendered_decl.need_uavs.move(),
593 };
594
595 defer {
596 rendered_decl.need_uavs = dg.uavs.move();
597 dg.ctype_deps.deinit(gpa);
598 }
599
600 rendered_decl.fwd_decl = fwd_decl: {
601 var aw: std.Io.Writer.Allocating = .fromArrayList(gpa, &c.string_bytes);
602 defer c.string_bytes = aw.toArrayList();
603 const start = aw.written().len;
604 codegen.genDeclFwd(&dg, &aw.writer) catch |err| switch (err) {
605 error.AlreadyReported => return,
606 error.WriteFailed, error.OutOfMemory => return error.OutOfMemory,
607 };
608 break :fwd_decl .{
609 .start = @intCast(start),
610 .len = @intCast(aw.written().len - start),
611 };
612 };
613
614 rendered_decl.code = code: {
615 var aw: std.Io.Writer.Allocating = .fromArrayList(gpa, &c.string_bytes);
616 defer c.string_bytes = aw.toArrayList();
617 const start = aw.written().len;
618 codegen.genDecl(&dg, &aw.writer) catch |err| switch (err) {
619 error.AlreadyReported => return,
620 error.WriteFailed, error.OutOfMemory => return error.OutOfMemory,
621 };
622 break :code .{
623 .start = @intCast(start),
624 .len = @intCast(aw.written().len - start),
625 };
626 };
627
628 rendered_decl.ctype_deps = try c.addCTypeDependencies(pt, &dg.ctype_deps);
629 }
630
631 const old_uavs_len = c.uavs.count();
632 try c.uavs.ensureUnusedCapacity(gpa, rendered_decl.need_uavs.count());
633 for (rendered_decl.need_uavs.keys()) |val| {
634 const gop = c.uavs.getOrPutAssumeCapacity(val);
635 if (gop.found_existing) {
636 assert(gop.index < old_uavs_len);
637 } else {
638 assert(gop.index >= old_uavs_len);
639 }
640 }
641 try c.updateNewUavs(pt, old_uavs_len);
642
643 try c.type_pool.flushPending(pt, .{ .c = c });
644}
645
646/// Unlike `updateNav` and `updateFunc`, this does *not* add newly-discovered UAVs to `c.uavs`. The
647/// caller is instead responsible for doing that (by iterating `rendered_decl.need_uavs`). However,
648/// this function *does* still add newly-discovered *types* to `c.type_pool`.
649///
650/// This function does not accept an alignment for the UAV, because the alignment needed on a UAV is
651/// not known until `flush` (since we need to have seen all uses of the UAV first). Instead, `flush`
652/// will prefix the UAV definition with an appropriate alignment annotation if necessary.
653fn updateUav(
654 c: *C,
655 pt: Zcu.PerThread,
656 val: Value,
657 rendered_decl: *RenderedDecl,
658) Allocator.Error!void {
659 const tracy = trace(@src());
660 defer tracy.end();
661
662 const gpa = c.base.comp.gpa;
663
664 var arena: std.heap.ArenaAllocator = .init(gpa);
665 defer arena.deinit();
666
667 var dg: codegen.DeclGen = .{
668 .gpa = gpa,
669 .arena = arena.allocator(),
670 .pt = pt,
671 .mod = pt.zcu.root_mod,
672 .owner_nav = .none,
673 .is_naked_fn = false,
674 .expected_block = null,
675 .ctype_deps = .empty,
676 .uavs = .empty,
677 };
678 defer {
679 rendered_decl.need_uavs = dg.uavs.move();
680 dg.ctype_deps.deinit(gpa);
681 }
682
683 rendered_decl.fwd_decl = fwd_decl: {
684 var aw: std.Io.Writer.Allocating = .fromArrayList(gpa, &c.string_bytes);
685 defer c.string_bytes = aw.toArrayList();
686 const start = aw.written().len;
687 codegen.genDeclValueFwd(&dg, &aw.writer, .{
688 .name = .{ .constant = val },
689 .@"const" = true,
690 .@"threadlocal" = false,
691 .init_val = val,
692 }) catch |err| switch (err) {
693 error.AlreadyReported => return,
694 error.WriteFailed, error.OutOfMemory => return error.OutOfMemory,
695 };
696 break :fwd_decl .{
697 .start = @intCast(start),
698 .len = @intCast(aw.written().len - start),
699 };
700 };
701
702 rendered_decl.code = code: {
703 var aw: std.Io.Writer.Allocating = .fromArrayList(gpa, &c.string_bytes);
704 defer c.string_bytes = aw.toArrayList();
705 const start = aw.written().len;
706 codegen.genDeclValue(&dg, &aw.writer, .{
707 .name = .{ .constant = val },
708 .@"const" = true,
709 .@"threadlocal" = false,
710 .init_val = val,
711 }) catch |err| switch (err) {
712 error.AlreadyReported => return,
713 error.WriteFailed, error.OutOfMemory => return error.OutOfMemory,
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) 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}
731
732pub fn flush(c: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.Error!void {
733 const tracy = trace(@src());
734 defer tracy.end();
735
736 const sub_prog_node = prog_node.start("Flush Module", 0);
737 defer sub_prog_node.end();
738
739 const comp = c.base.comp;
740 const diags = &comp.link_diags;
741 const gpa = comp.gpa;
742 const io = comp.io;
743 const zcu = c.base.comp.zcu.?;
744 const ip = &zcu.intern_pool;
745 const active = zcu.activate(tid);
746 defer active.deactivate();
747 const pt = active.pt;
748
749 // If it's somehow not made it into the pool, we need to generate the type `[:0]const u8` for
750 // error names.
751 const slice_const_u8_sentinel_0_pool_index = try c.type_pool.get(
752 pt,
753 .{ .c = c },
754 .slice_const_u8_sentinel_0_type,
755 );
756 try c.type_pool.flushPending(pt, .{ .c = c });
757
758 // Find the set of referenced NAVs; these are the ones we'll emit. It is important in this
759 // backend that we only emit referenced NAVs, because other ones may contain code from past
760 // incremental updates which is invalid C (due to e.g. types changing). Machine code backends
761 // don't have this problem because there are, of course, no type checking performed when you
762 // *execute* a binary!
763 var need_navs: std.array_hash_map.Auto(InternPool.Nav.Index, void) = .empty;
764 defer need_navs.deinit(gpa);
765 {
766 const unit_references = try zcu.resolveReferences();
767 for (c.navs.keys()) |nav| {
768 const nav_val = ip.getNav(nav).resolved.?.value;
769 const check_unit: ?InternPool.AnalUnit = switch (ip.indexToKey(nav_val)) {
770 else => .wrap(.{ .nav_val = nav }),
771 .func => .wrap(.{ .func = nav_val }),
772 // TODO: this is a hack to deal with the fact that there's currently no good way to
773 // know which `extern`s are alive. This can and will break in certain patterns of
774 // incremental update. We kind of need to think a bit more about how the frontend
775 // actually represents `extern`, it's a bit awkward right now.
776 .@"extern" => null,
777 };
778 if (check_unit) |u| {
779 if (!unit_references.contains(u)) continue;
780 }
781 try need_navs.putNoClobber(gpa, nav, {});
782 }
783 }
784
785 // Using our knowledge of which NAVs are referenced, we now need to discover the set of UAVs and
786 // C types which are referenced (and hence must be emitted). As above, this is necessary to make
787 // sure we only emit valid C code.
788 //
789 // At the same time, we will discover the set of lazy functions which are referenced.
790
791 var need_uavs: std.array_hash_map.Auto(InternPool.Index, Alignment) = .empty;
792 defer need_uavs.deinit(gpa);
793
794 var need_types: std.array_hash_map.Auto(link.ConstPool.Index, void) = .empty;
795 defer need_types.deinit(gpa);
796 var need_errunion_types: std.array_hash_map.Auto(link.ConstPool.Index, void) = .empty;
797 defer need_errunion_types.deinit(gpa);
798 var need_aligned_types: std.array_hash_map.Auto(link.ConstPool.Index, u64) = .empty;
799 defer need_aligned_types.deinit(gpa);
800
801 var need_tag_name_funcs: std.array_hash_map.Auto(InternPool.Index, void) = .empty;
802 defer need_tag_name_funcs.deinit(gpa);
803
804 var need_never_tail_funcs: std.array_hash_map.Auto(InternPool.Nav.Index, void) = .empty;
805 defer need_never_tail_funcs.deinit(gpa);
806
807 var need_never_inline_funcs: std.array_hash_map.Auto(InternPool.Nav.Index, void) = .empty;
808 defer need_never_inline_funcs.deinit(gpa);
809
810 // As mentioned above, we need this type for error names.
811 try need_types.put(gpa, slice_const_u8_sentinel_0_pool_index, {});
812
813 // Every exported NAV should have been discovered via `zcu.resolveReferences`...
814 for (c.exported_navs.keys()) |nav| assert(need_navs.contains(nav));
815 // ...but we *do* need to add exported UAVs to the set.
816 try need_uavs.ensureUnusedCapacity(gpa, c.exported_uavs.count());
817 for (c.exported_uavs.keys()) |uav| {
818 const gop = need_uavs.getOrPutAssumeCapacity(uav);
819 if (!gop.found_existing) gop.value_ptr.* = .none;
820 }
821
822 // For every referenced NAV, some UAVs, C types, and lazy functions may be referenced.
823 for (need_navs.keys()) |nav| {
824 const rendered = c.navs.getPtr(nav).?;
825 try mergeNeededCTypes(
826 c,
827 &need_types,
828 &need_errunion_types,
829 &need_aligned_types,
830 &rendered.ctype_deps,
831 );
832 try mergeNeededUavs(zcu, &need_uavs, &rendered.need_uavs);
833
834 try need_tag_name_funcs.ensureUnusedCapacity(gpa, rendered.need_tag_name_funcs.count());
835 for (rendered.need_tag_name_funcs.keys()) |enum_type| {
836 need_tag_name_funcs.putAssumeCapacity(enum_type, {});
837 }
838
839 try need_never_tail_funcs.ensureUnusedCapacity(gpa, rendered.need_never_tail_funcs.count());
840 for (rendered.need_never_tail_funcs.keys()) |fn_nav| {
841 need_never_tail_funcs.putAssumeCapacity(fn_nav, {});
842 }
843
844 try need_never_inline_funcs.ensureUnusedCapacity(gpa, rendered.need_never_inline_funcs.count());
845 for (rendered.need_never_inline_funcs.keys()) |fn_nav| {
846 need_never_inline_funcs.putAssumeCapacity(fn_nav, {});
847 }
848 }
849
850 // UAVs may reference other UAVs or C types.
851 {
852 var index: usize = 0;
853 while (need_uavs.count() > index) : (index += 1) {
854 const val = need_uavs.keys()[index];
855 const rendered = c.uavs.getPtr(val).?;
856 try mergeNeededCTypes(
857 c,
858 &need_types,
859 &need_errunion_types,
860 &need_aligned_types,
861 &rendered.ctype_deps,
862 );
863 try mergeNeededUavs(zcu, &need_uavs, &rendered.need_uavs);
864 }
865 }
866
867 // Finally, C types may reference other C types.
868 {
869 var index: usize = 0;
870 var errunion_index: usize = 0;
871 var aligned_index: usize = 0;
872 while (true) {
873 if (index < need_types.count()) {
874 const pool_index = need_types.keys()[index];
875 const rendered = &c.types.items[@backingInt(pool_index)];
876 try mergeNeededCTypes(
877 c,
878 &need_types,
879 &need_errunion_types,
880 &need_aligned_types,
881 &rendered.definition_deps, // we're tasked with emitting the *definition* of this type
882 );
883 index += 1;
884 continue;
885 }
886
887 if (errunion_index < need_errunion_types.count()) {
888 const payload_pool_index = need_errunion_types.keys()[errunion_index];
889 const rendered = &c.types.items[@backingInt(payload_pool_index)];
890 try mergeNeededCTypes(
891 c,
892 &need_types,
893 &need_errunion_types,
894 &need_aligned_types,
895 &rendered.deps, // the error union type requires emitting this type's *name*
896 );
897 errunion_index += 1;
898 continue;
899 }
900
901 if (aligned_index < need_aligned_types.count()) {
902 const pool_index = need_aligned_types.keys()[aligned_index];
903 const rendered = &c.types.items[@backingInt(pool_index)];
904 try mergeNeededCTypes(
905 c,
906 &need_types,
907 &need_errunion_types,
908 &need_aligned_types,
909 &rendered.deps, // an aligned typedef requires emitting this type's *name*
910 );
911 aligned_index += 1;
912 continue;
913 }
914
915 break;
916 }
917 }
918
919 // Now that we know which types are required, generate aligned typedefs. One buffer per aligned
920 // type, with *all* aligned typedefs for that type.
921 const aligned_type_strings = try arena.alloc([]const u8, need_aligned_types.count());
922 {
923 var aw: std.Io.Writer.Allocating = .init(gpa);
924 defer aw.deinit();
925 var unused_deps: codegen.CType.Dependencies = .empty;
926 defer unused_deps.deinit(gpa);
927 for (
928 need_aligned_types.keys(),
929 need_aligned_types.values(),
930 aligned_type_strings,
931 ) |pool_index, align_mask, *str_out| {
932 const ty: Type = .fromInterned(pool_index.val(&c.type_pool));
933 const has_layout = c.types.items[@backingInt(pool_index)].errunion_definition.len > 0;
934 for (0..@bitSizeOf(@TypeOf(align_mask))) |bit_index| {
935 switch (@as(u1, @truncate(align_mask >> @intCast(bit_index)))) {
936 0 => continue,
937 1 => {},
938 }
939 codegen.CType.render_defs.defineAligned(
940 ty,
941 .fromLog2Units(@intCast(bit_index)),
942 has_layout,
943 &unused_deps,
944 arena,
945 &aw.writer,
946 pt,
947 ) catch |err| switch (err) {
948 error.WriteFailed => return error.OutOfMemory,
949 error.OutOfMemory => |e| return e,
950 };
951 }
952 str_out.* = try arena.dupe(u8, aw.written());
953 aw.clearRetainingCapacity();
954 }
955 }
956
957 // We have discovered the full set of NAVs, UAVs, and types we need to emit, and will now begin
958 // to build the output buffer. Our strategy is to emit the C source in this order:
959 //
960 // * Header
961 // * Big-int type definitions
962 // * Other CType definitions (traversing the dependency graph to sort topologically)
963 // * Global assembly
964 // * UAV exports
965 // * NAV exports
966 // * UAV forward declarations
967 // * NAV forward declarations
968 // * Lazy declarations (error names; @tagName functions; never_tail/never_inline wrappers)
969 // * UAV definitions
970 // * NAV definitions
971 //
972 // Most of these sections are order-independent within themselves, with the exception of the
973 // type definitions, which must be ordered to avoid a struct/union from embedding a type which
974 // is currently incomplete.
975 //
976 // When emitting UAV forward declarations, if the UAV requires alignment, we must prefix it with
977 // an alignment annotation. We couldn't emit the alignment into the UAV's `RenderedDecl` because
978 // we couldn't have known the required alignment until now!
979
980 var f: Flush = .{ .all_buffers = .empty, .file_size = 0 };
981 defer f.deinit(gpa);
982
983 // We know exactly what we'll be emitting, so can reserve capacity for all of our buffers!
984
985 try f.all_buffers.ensureUnusedCapacity(gpa, 1 + // Header
986 1 + // Big-int type definitions
987 need_types.count() + // `RenderedType.fwd_decl` (worst-case)
988 need_types.count() + // `RenderedType.definition`
989 need_errunion_types.count() + // `RenderedType.errunion_fwd_decl` (worst-case)
990 need_errunion_types.count() + // `RenderedType.errunion_definition`
991 need_aligned_types.count() + // `aligned_type_strings`
992 1 + // Global assembly
993 c.exported_uavs.count() + // UAV export block
994 c.exported_navs.count() + // NAV export block
995 need_uavs.count() + // UAV forward declarations
996 need_navs.count() + // NAV forward declarations
997 1 + // Lazy declarations
998 need_uavs.count() * 3 + // UAV definitions ("static ", "zig_align(4)", "<definition body>")
999 need_navs.count() * 2); // NAV definitions ("static ", "<definition body>")
1000
1001 f.appendBufAssumeCapacity(c.header.get(c));
1002
1003 // Big-int type definitions
1004 var bigint_aw: std.Io.Writer.Allocating = .init(gpa);
1005 defer bigint_aw.deinit();
1006 for (c.bigint_types.keys()) |bigint| {
1007 codegen.CType.render_defs.defineBigInt(bigint, &bigint_aw.writer, zcu) catch |err| switch (err) {
1008 error.WriteFailed => return error.OutOfMemory,
1009 };
1010 }
1011 f.appendBufAssumeCapacity(bigint_aw.written());
1012
1013 // CType definitions
1014 {
1015 var ft: FlushTypes = .{
1016 .c = c,
1017 .f = &f,
1018 .aligned_types = &need_aligned_types,
1019 .aligned_type_strings = aligned_type_strings,
1020 .status = .empty,
1021 .errunion_status = .empty,
1022 .aligned_status = .empty,
1023 };
1024 defer {
1025 ft.status.deinit(gpa);
1026 ft.errunion_status.deinit(gpa);
1027 ft.aligned_status.deinit(gpa);
1028 }
1029 try ft.status.ensureUnusedCapacity(gpa, need_types.count());
1030 try ft.errunion_status.ensureUnusedCapacity(gpa, need_errunion_types.count());
1031 try ft.aligned_status.ensureUnusedCapacity(gpa, need_aligned_types.count());
1032
1033 for (need_types.keys()) |pool_index| {
1034 ft.doType(pool_index);
1035 }
1036 for (need_errunion_types.keys()) |pool_index| {
1037 ft.doErrunionType(pool_index);
1038 }
1039 for (need_aligned_types.keys()) |pool_index| {
1040 ft.doAlignedTypeFwd(pool_index);
1041 }
1042 }
1043
1044 // Global assembly
1045 var asm_aw: std.Io.Writer.Allocating = .init(gpa);
1046 defer asm_aw.deinit();
1047 codegen.genGlobalAsm(zcu, &asm_aw.writer) catch |err| switch (err) {
1048 error.WriteFailed => return error.OutOfMemory,
1049 };
1050 f.appendBufAssumeCapacity(asm_aw.written());
1051
1052 var export_names: std.AutoHashMapUnmanaged(InternPool.NullTerminatedString, void) = .empty;
1053 defer export_names.deinit(gpa);
1054 try export_names.ensureTotalCapacity(gpa, @intCast(zcu.single_exports.count()));
1055 for (zcu.single_exports.values()) |export_index| {
1056 export_names.putAssumeCapacity(export_index.ptr(zcu).opts.name, {});
1057 }
1058 for (zcu.multi_exports.values()) |info| {
1059 try export_names.ensureUnusedCapacity(gpa, info.len);
1060 for (zcu.all_exports.items[info.index..][0..info.len]) |@"export"| {
1061 export_names.putAssumeCapacity(@"export".opts.name, {});
1062 }
1063 }
1064
1065 // UAV export block
1066 for (c.exported_uavs.values()) |code| {
1067 f.appendBufAssumeCapacity(code.get(c));
1068 }
1069
1070 // NAV export block
1071 for (c.exported_navs.values()) |code| {
1072 f.appendBufAssumeCapacity(code.get(c));
1073 }
1074
1075 // UAV forward declarations
1076 for (need_uavs.keys()) |val| {
1077 if (c.exported_uavs.contains(val)) continue; // the export was the declaration
1078 const fwd_decl = c.uavs.getPtr(val).?.fwd_decl;
1079 f.appendBufAssumeCapacity(fwd_decl.get(c));
1080 }
1081
1082 // NAV forward declarations
1083 for (need_navs.keys()) |nav| {
1084 if (c.exported_navs.contains(nav)) continue; // the export was the declaration
1085 switch (ip.indexToKey(ip.getNav(nav).resolved.?.value)) {
1086 .@"extern" => |e| if (export_names.contains(e.name)) continue,
1087 else => {},
1088 }
1089 const fwd_decl = c.navs.getPtr(nav).?.fwd_decl;
1090 f.appendBufAssumeCapacity(fwd_decl.get(c));
1091 }
1092
1093 // Lazy declarations
1094 var lazy_decls_aw: std.Io.Writer.Allocating = .init(gpa);
1095 defer lazy_decls_aw.deinit();
1096 {
1097 var lazy_dg: codegen.DeclGen = .{
1098 .gpa = gpa,
1099 .arena = arena,
1100 .pt = pt,
1101 .mod = pt.zcu.root_mod,
1102 .owner_nav = .none,
1103 .is_naked_fn = false,
1104 .expected_block = null,
1105 .ctype_deps = .empty,
1106 .uavs = .empty,
1107 };
1108 defer {
1109 assert(lazy_dg.uavs.count() == 0);
1110 lazy_dg.ctype_deps.deinit(gpa);
1111 }
1112 const slice_const_u8_sentinel_0_cty: codegen.CType = try .lower(
1113 .slice_const_u8_sentinel_0,
1114 &lazy_dg.ctype_deps,
1115 arena,
1116 zcu,
1117 );
1118 const slice_const_u8_sentinel_0_name = try std.fmt.allocPrint(
1119 arena,
1120 "{f}",
1121 .{slice_const_u8_sentinel_0_cty.fmtTypeName(zcu)},
1122 );
1123 codegen.genErrDecls(zcu, &lazy_decls_aw.writer, slice_const_u8_sentinel_0_name) catch |err| switch (err) {
1124 error.WriteFailed => return error.OutOfMemory,
1125 };
1126 for (need_tag_name_funcs.keys()) |enum_ty_ip| {
1127 const enum_ty: Type = .fromInterned(enum_ty_ip);
1128 const enum_cty: codegen.CType = try .lower(
1129 enum_ty,
1130 &lazy_dg.ctype_deps,
1131 arena,
1132 zcu,
1133 );
1134 codegen.genTagNameFn(
1135 zcu,
1136 &lazy_decls_aw.writer,
1137 slice_const_u8_sentinel_0_name,
1138 enum_ty,
1139 try std.fmt.allocPrint(arena, "{f}", .{enum_cty.fmtTypeName(zcu)}),
1140 ) catch |err| switch (err) {
1141 error.WriteFailed => return error.OutOfMemory,
1142 };
1143 }
1144 for (need_never_tail_funcs.keys()) |fn_nav| {
1145 codegen.genLazyCallModifierFn(&lazy_dg, fn_nav, .never_tail, &lazy_decls_aw.writer) catch |err| switch (err) {
1146 error.WriteFailed => return error.OutOfMemory,
1147 error.OutOfMemory => |e| return e,
1148 error.AlreadyReported => unreachable,
1149 };
1150 }
1151 for (need_never_inline_funcs.keys()) |fn_nav| {
1152 codegen.genLazyCallModifierFn(&lazy_dg, fn_nav, .never_inline, &lazy_decls_aw.writer) catch |err| switch (err) {
1153 error.WriteFailed => return error.OutOfMemory,
1154 error.OutOfMemory => |e| return e,
1155 error.AlreadyReported => unreachable,
1156 };
1157 }
1158 }
1159 f.appendBufAssumeCapacity(lazy_decls_aw.written());
1160
1161 // UAV definitions
1162 for (need_uavs.keys(), need_uavs.values()) |val, overalign| {
1163 const code = c.uavs.getPtr(val).?.code;
1164 if (code.len == 0) continue;
1165 if (!c.exported_uavs.contains(val)) {
1166 f.appendBufAssumeCapacity("static ");
1167 }
1168 if (overalign != .none) {
1169 // As long as `Alignment` isn't too big, it's reasonable to just generate all possible
1170 // alignment annotations statically into a LUT, which avoids allocating strings on this
1171 // path.
1172 comptime assert(@bitSizeOf(Alignment) < 8);
1173 const table_len = (1 << @bitSizeOf(Alignment)) - 1;
1174 const table: [table_len][]const u8 = comptime table: {
1175 @setEvalBranchQuota(16_000);
1176 var table: [table_len][]const u8 = undefined;
1177 for (&table, 0..) |*str, log2_align| {
1178 const byte_align = Alignment.fromLog2Units(log2_align).toByteUnits().?;
1179 str.* = std.fmt.comptimePrint("zig_align({d}) ", .{byte_align});
1180 }
1181 break :table table;
1182 };
1183 f.appendBufAssumeCapacity(table[overalign.toLog2Units()]);
1184 }
1185 f.appendBufAssumeCapacity(code.get(c));
1186 }
1187
1188 // NAV definitions
1189 for (need_navs.keys()) |nav| {
1190 const code = c.navs.getPtr(nav).?.code;
1191 if (code.len == 0) continue;
1192 if (!c.exported_navs.contains(nav)) {
1193 const is_extern = ip.indexToKey(ip.getNav(nav).resolved.?.value) == .@"extern";
1194 f.appendBufAssumeCapacity(if (is_extern) "zig_extern " else "static ");
1195 }
1196 f.appendBufAssumeCapacity(code.get(c));
1197 }
1198
1199 // We've collected all of our buffers; it's now time to actually write the file!
1200 const file = c.base.file.?;
1201 file.setLength(io, f.file_size) catch |err| return diags.fail("failed to allocate file: {t}", .{err});
1202 var fw = file.writer(io, &.{});
1203 var w = &fw.interface;
1204 w.writeVecAll(f.all_buffers.items) catch |err| switch (err) {
1205 error.WriteFailed => return diags.fail("failed to write to '{f}': {s}", .{
1206 std.fmt.alt(c.base.emit, .formatEscapeChar), @errorName(fw.err.?),
1207 }),
1208 };
1209}
1210
1211const Flush = struct {
1212 /// We collect a list of buffers to write, and write them all at once with pwritev 😎
1213 all_buffers: std.ArrayList([]const u8),
1214 /// Keeps track of the total bytes of `all_buffers`.
1215 file_size: u64,
1216
1217 fn appendBufAssumeCapacity(f: *Flush, buf: []const u8) void {
1218 if (buf.len == 0) return;
1219 f.all_buffers.appendAssumeCapacity(buf);
1220 f.file_size += buf.len;
1221 }
1222
1223 fn deinit(f: *Flush, gpa: Allocator) void {
1224 f.all_buffers.deinit(gpa);
1225 }
1226};
1227
1228pub fn updateExports(
1229 c: *C,
1230 pt: Zcu.PerThread,
1231 export_indices: []const Zcu.Export.Index,
1232) Allocator.Error!void {
1233 const zcu = pt.zcu;
1234 const gpa = zcu.gpa;
1235
1236 c.exported_navs.clearRetainingCapacity();
1237 c.exported_uavs.clearRetainingCapacity();
1238
1239 var arena: std.heap.ArenaAllocator = .init(gpa);
1240 defer arena.deinit();
1241
1242 var by_exported: std.array_hash_map.Auto(Zcu.Exported, std.ArrayList(Zcu.Export.Index)) = .empty;
1243 try by_exported.ensureUnusedCapacity(arena.allocator(), export_indices.len);
1244
1245 for (export_indices) |exp_index| {
1246 const exported = exp_index.ptr(zcu).exported;
1247 const gop = by_exported.getOrPutAssumeCapacity(exported);
1248 if (!gop.found_existing) {
1249 gop.value_ptr.* = .empty;
1250 }
1251 try gop.value_ptr.append(arena.allocator(), exp_index);
1252 }
1253
1254 for (by_exported.keys(), by_exported.values()) |exported, *exports_of_this| {
1255 var dg: codegen.DeclGen = .{
1256 .gpa = gpa,
1257 .arena = arena.allocator(),
1258 .pt = pt,
1259 .mod = zcu.root_mod,
1260 .owner_nav = .none,
1261 .is_naked_fn = false,
1262 .expected_block = null,
1263 .ctype_deps = .empty,
1264 .uavs = .empty,
1265 };
1266 defer {
1267 assert(dg.uavs.count() == 0);
1268 dg.ctype_deps.deinit(gpa);
1269 }
1270 const code: String = code: {
1271 var aw: std.Io.Writer.Allocating = .fromArrayList(gpa, &c.string_bytes);
1272 defer c.string_bytes = aw.toArrayList();
1273 const start = aw.written().len;
1274 codegen.genExports(&dg, &aw.writer, exported, exports_of_this.items) catch |err| switch (err) {
1275 error.WriteFailed => return error.OutOfMemory,
1276 error.OutOfMemory => |e| return e,
1277 };
1278 break :code .{
1279 .start = @intCast(start),
1280 .len = @intCast(aw.written().len - start),
1281 };
1282 };
1283 switch (exported) {
1284 .nav => |nav| try c.exported_navs.put(gpa, nav, code),
1285 .uav => |uav| try c.exported_uavs.put(gpa, uav, code),
1286 }
1287 }
1288}
1289
1290fn mergeNeededCTypes(
1291 c: *C,
1292 need_types: *std.array_hash_map.Auto(link.ConstPool.Index, void),
1293 need_errunion_types: *std.array_hash_map.Auto(link.ConstPool.Index, void),
1294 need_aligned_types: *std.array_hash_map.Auto(link.ConstPool.Index, u64),
1295 deps: *const CTypeDependencies,
1296) Allocator.Error!void {
1297 const gpa = c.base.comp.gpa;
1298
1299 const resolved = deps.get(c);
1300
1301 try need_types.ensureUnusedCapacity(gpa, resolved.type.len + resolved.type_fwd.len);
1302 try need_errunion_types.ensureUnusedCapacity(gpa, resolved.errunion_type.len + resolved.errunion_type_fwd.len);
1303 try need_aligned_types.ensureUnusedCapacity(gpa, resolved.aligned_type_fwd.len);
1304
1305 for (resolved.type) |index| need_types.putAssumeCapacity(index, {});
1306 for (resolved.type_fwd) |index| need_types.putAssumeCapacity(index, {});
1307
1308 for (resolved.errunion_type) |index| need_errunion_types.putAssumeCapacity(index, {});
1309 for (resolved.errunion_type_fwd) |index| need_errunion_types.putAssumeCapacity(index, {});
1310
1311 for (resolved.aligned_type_fwd, resolved.aligned_type_masks) |ty_index, align_mask| {
1312 const gop = need_aligned_types.getOrPutAssumeCapacity(ty_index);
1313 if (!gop.found_existing) gop.value_ptr.* = 0;
1314 gop.value_ptr.* |= align_mask;
1315 }
1316}
1317
1318fn mergeNeededUavs(
1319 zcu: *const Zcu,
1320 global: *std.array_hash_map.Auto(InternPool.Index, Alignment),
1321 new: *const std.array_hash_map.Auto(InternPool.Index, Alignment),
1322) Allocator.Error!void {
1323 const gpa = zcu.comp.gpa;
1324
1325 try global.ensureUnusedCapacity(gpa, new.count());
1326 for (new.keys(), new.values()) |uav_val, need_align| {
1327 const gop = global.getOrPutAssumeCapacity(uav_val);
1328 if (!gop.found_existing) gop.value_ptr.* = .none;
1329
1330 if (need_align != .none) {
1331 const cur_align = switch (gop.value_ptr.*) {
1332 .none => Value.fromInterned(uav_val).typeOf(zcu).abiAlignment(zcu),
1333 else => |a| a,
1334 };
1335 if (need_align.compareStrict(.gt, cur_align)) {
1336 gop.value_ptr.* = need_align;
1337 }
1338 }
1339 }
1340}
1341
1342fn addCTypeDependencies(
1343 c: *C,
1344 pt: Zcu.PerThread,
1345 deps: *const codegen.CType.Dependencies,
1346) Allocator.Error!CTypeDependencies {
1347 const gpa = pt.zcu.comp.gpa;
1348
1349 try c.bigint_types.ensureUnusedCapacity(gpa, deps.bigint.count());
1350 for (deps.bigint.keys()) |bigint| c.bigint_types.putAssumeCapacity(bigint, {});
1351
1352 const type_start = c.type_dependencies.items.len;
1353 const errunion_type_start = type_start + deps.type.count();
1354 const type_fwd_start = errunion_type_start + deps.errunion_type.count();
1355 const errunion_type_fwd_start = type_fwd_start + deps.type_fwd.count();
1356 const aligned_type_fwd_start = errunion_type_fwd_start + deps.errunion_type_fwd.count();
1357 try c.type_dependencies.appendNTimes(gpa, undefined, deps.type.count() +
1358 deps.errunion_type.count() +
1359 deps.type_fwd.count() +
1360 deps.errunion_type_fwd.count() +
1361 deps.aligned_type_fwd.count());
1362
1363 const align_mask_start = c.align_dependency_masks.items.len;
1364 try c.align_dependency_masks.appendSlice(gpa, deps.aligned_type_fwd.values());
1365
1366 for (deps.type.keys(), type_start..) |ty, i| {
1367 const pool_index = try c.type_pool.get(pt, .{ .c = c }, ty);
1368 c.type_dependencies.items[i] = pool_index;
1369 }
1370
1371 for (deps.errunion_type.keys(), errunion_type_start..) |ty, i| {
1372 const pool_index = try c.type_pool.get(pt, .{ .c = c }, ty);
1373 c.type_dependencies.items[i] = pool_index;
1374 }
1375
1376 for (deps.type_fwd.keys(), type_fwd_start..) |ty, i| {
1377 const pool_index = try c.type_pool.get(pt, .{ .c = c }, ty);
1378 c.type_dependencies.items[i] = pool_index;
1379 }
1380
1381 for (deps.errunion_type_fwd.keys(), errunion_type_fwd_start..) |ty, i| {
1382 const pool_index = try c.type_pool.get(pt, .{ .c = c }, ty);
1383 c.type_dependencies.items[i] = pool_index;
1384 }
1385
1386 for (deps.aligned_type_fwd.keys(), aligned_type_fwd_start..) |ty, i| {
1387 const pool_index = try c.type_pool.get(pt, .{ .c = c }, ty);
1388 c.type_dependencies.items[i] = pool_index;
1389 }
1390
1391 return .{
1392 .len = @intCast(deps.type.count()),
1393 .errunion_len = @intCast(deps.errunion_type.count()),
1394 .fwd_len = @intCast(deps.type_fwd.count()),
1395 .errunion_fwd_len = @intCast(deps.errunion_type_fwd.count()),
1396 .aligned_fwd_len = @intCast(deps.aligned_type_fwd.count()),
1397 .type_start = @intCast(type_start),
1398 .align_mask_start = @intCast(align_mask_start),
1399 };
1400}
1401
1402fn updateNewUavs(c: *C, pt: Zcu.PerThread, old_uavs_len: usize) Allocator.Error!void {
1403 const gpa = pt.zcu.comp.gpa;
1404 var index = old_uavs_len;
1405 while (index < c.uavs.count()) : (index += 1) {
1406 // `new_uavs` is UAVs discovered while lowering *this* UAV.
1407 const new_uavs: []const InternPool.Index = new: {
1408 c.uavs.lockPointers();
1409 defer c.uavs.unlockPointers();
1410 const val: Value = .fromInterned(c.uavs.keys()[index]);
1411 const rendered_decl = &c.uavs.values()[index];
1412 rendered_decl.* = .init;
1413 try c.updateUav(pt, val, rendered_decl);
1414 break :new rendered_decl.need_uavs.keys();
1415 };
1416 try c.uavs.ensureUnusedCapacity(gpa, new_uavs.len);
1417 for (new_uavs) |val| {
1418 const gop = c.uavs.getOrPutAssumeCapacity(val);
1419 if (!gop.found_existing) {
1420 assert(gop.index > index);
1421 }
1422 }
1423 }
1424}
1425
1426const FlushTypes = struct {
1427 c: *C,
1428 f: *Flush,
1429
1430 aligned_types: *const std.array_hash_map.Auto(link.ConstPool.Index, u64),
1431 aligned_type_strings: []const []const u8,
1432
1433 status: std.array_hash_map.Auto(link.ConstPool.Index, bool),
1434 errunion_status: std.array_hash_map.Auto(link.ConstPool.Index, bool),
1435 aligned_status: std.array_hash_map.Auto(link.ConstPool.Index, void),
1436
1437 fn processDeps(ft: *FlushTypes, deps: *const CTypeDependencies) void {
1438 const resolved = deps.get(ft.c);
1439 for (resolved.type) |pool_index| ft.doType(pool_index);
1440 for (resolved.type_fwd) |pool_index| ft.doTypeFwd(pool_index);
1441 for (resolved.errunion_type) |pool_index| ft.doErrunionType(pool_index);
1442 for (resolved.errunion_type_fwd) |pool_index| ft.doErrunionTypeFwd(pool_index);
1443 for (resolved.aligned_type_fwd) |pool_index| ft.doAlignedTypeFwd(pool_index);
1444 }
1445 fn processDepsAsFwd(ft: *FlushTypes, deps: *const CTypeDependencies) void {
1446 const resolved = deps.get(ft.c);
1447 for (resolved.type) |pool_index| ft.doTypeFwd(pool_index);
1448 for (resolved.type_fwd) |pool_index| ft.doTypeFwd(pool_index);
1449 for (resolved.errunion_type) |pool_index| ft.doErrunionTypeFwd(pool_index);
1450 for (resolved.errunion_type_fwd) |pool_index| ft.doErrunionTypeFwd(pool_index);
1451 for (resolved.aligned_type_fwd) |pool_index| ft.doAlignedTypeFwd(pool_index);
1452 }
1453
1454 fn doAlignedTypeFwd(ft: *FlushTypes, pool_index: link.ConstPool.Index) void {
1455 const c = ft.c;
1456 if (ft.aligned_status.contains(pool_index)) return;
1457 if (ft.aligned_types.getIndex(pool_index)) |i| {
1458 const rendered = &c.types.items[@backingInt(pool_index)];
1459 ft.processDepsAsFwd(&rendered.deps);
1460 ft.f.appendBufAssumeCapacity(ft.aligned_type_strings[i]);
1461 }
1462 ft.aligned_status.putAssumeCapacity(pool_index, {});
1463 }
1464 fn doTypeFwd(ft: *FlushTypes, pool_index: link.ConstPool.Index) void {
1465 const c = ft.c;
1466 if (ft.status.contains(pool_index)) return;
1467 const rendered = &c.types.items[@backingInt(pool_index)];
1468 if (rendered.fwd_decl.len > 0) {
1469 ft.f.appendBufAssumeCapacity(rendered.fwd_decl.get(c));
1470 ft.status.putAssumeCapacityNoClobber(pool_index, false);
1471 } else {
1472 ft.processDepsAsFwd(&rendered.definition_deps);
1473 const gop = ft.status.getOrPutAssumeCapacity(pool_index);
1474 if (!gop.found_existing) {
1475 gop.value_ptr.* = false;
1476 ft.f.appendBufAssumeCapacity(rendered.definition.get(c));
1477 }
1478 }
1479 }
1480 fn doType(ft: *FlushTypes, pool_index: link.ConstPool.Index) void {
1481 const c = ft.c;
1482 if (ft.status.get(pool_index)) |completed| {
1483 if (completed) return;
1484 }
1485 const rendered = &c.types.items[@backingInt(pool_index)];
1486 ft.processDeps(&rendered.definition_deps);
1487 if (rendered.fwd_decl.len == 0 and ft.status.contains(pool_index)) {
1488 // `doTypeFwd` already rendered the defintion, we just had to complete the type by
1489 // fully resolving its dependencies.
1490 } else if (rendered.definition.len > 0) {
1491 ft.f.appendBufAssumeCapacity(rendered.definition.get(c));
1492 } else if (!ft.status.contains(pool_index)) {
1493 // The type will never be completed, but it must be forward declared to avoid it being
1494 // declared in the wrong scope.
1495 ft.f.appendBufAssumeCapacity(rendered.fwd_decl.get(c));
1496 }
1497 ft.status.putAssumeCapacity(pool_index, true);
1498 }
1499 fn doErrunionTypeFwd(ft: *FlushTypes, pool_index: link.ConstPool.Index) void {
1500 const c = ft.c;
1501 const gop = ft.errunion_status.getOrPutAssumeCapacity(pool_index);
1502 if (gop.found_existing) return;
1503 const rendered = &c.types.items[@backingInt(pool_index)];
1504 ft.f.appendBufAssumeCapacity(rendered.errunion_fwd_decl.get(c));
1505 gop.value_ptr.* = false;
1506 }
1507 fn doErrunionType(ft: *FlushTypes, pool_index: link.ConstPool.Index) void {
1508 const c = ft.c;
1509 if (ft.errunion_status.get(pool_index)) |completed| {
1510 if (completed) return;
1511 }
1512 const rendered = &c.types.items[@backingInt(pool_index)];
1513 ft.processDeps(&rendered.deps);
1514 if (rendered.errunion_definition.len > 0) {
1515 ft.f.appendBufAssumeCapacity(rendered.errunion_definition.get(c));
1516 } else {
1517 // The error union type will never be completed, but forward declare it to avoid the
1518 // type being first declared in a different scope.
1519 ft.f.appendBufAssumeCapacity(rendered.errunion_fwd_decl.get(c));
1520 }
1521 ft.errunion_status.putAssumeCapacity(pool_index, true);
1522 }
1523};