authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-05-31 08:43:48+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-06-04 09:57:29+01:00
log41a617990513a0b6a0ad1f10ca8b78f9ea69cb28
treecafa0c94af067ffa4e95f22d4629b33c748a4037
parent72f0ae7ec46c24d59aef2fae793275bede59ac8c
signaturelock-open Commit is signed but in an unrecognized format.

compiler: linker-agnostic handling of LLVM object

This started as a small diff to `Elf2` to make it support the LLVM backend, but as I was writing it, I felt that I was just writing logic shared by every linker implementation. The object file emitted by the LLVM backend is a totally normal link input as far as the linker implementations are concerned---so, why not treat it as one? The `zcu_object_basename` field is removed from `link.File`, instead moved to `llvm.Object`. Logic in `link.Queue` avoids calling `prelink` when using the LLVM backend, because it knows we will receive another link input later. In `Compilation.flush`, after LLVM emits the ZCU object, we call `link.runPrelinkTask` to process that input, and then perform the deferred `prelink` call. This means that linker implementations which integrate with prelink don't need to be aware of LLVM whatsoever! (Aside from perhaps checking `use_llvm` to know if they are going to receive any ZCU tasks.) The `MachO` and `Lld` linker implementations still have specific handling for the ZCU object file from LLVM, because they do not currently integrate with `prelink`---but the `Coff`, `Elf`, `Wasm`, and `Elf2` implementations no longer need to handle this case specially. Note that the self-hosted linkers currently do not support incremental compilation with the LLVM backend---this is an existing issue, but I thought I'd mention it here because I wasn't previously aware of this bug. That is tracked by https://codeberg.org/ziglang/zig/issues/32053.

10 files changed, 111 insertions(+), 135 deletions(-)

src/Compilation.zig+17-6
......@@ -3344,15 +3344,15 @@ fn flush(comp: *Compilation, arena: Allocator, tid: Zcu.PerThread.Id) (Io.Cancel
33443344 comp.time_report.?.stats.real_ns_llvm_emit = ns;
33453345 };
33463346
3347 const zcu_obj_path: ?Cache.Path = if (comp.bin_file != null) p: {
3348 break :p try comp.resolveEmitPathFlush(arena, .temp, llvm_object.out_bin_basename);
3349 } else null;
3350
33473351 llvm_object.emit(pt, .{
33483352 .pre_ir_path = comp.verbose_llvm_ir,
33493353 .pre_bc_path = comp.verbose_llvm_bc,
33503354
3351 .bin_path = p: {
3352 const lf = comp.bin_file orelse break :p null;
3353 const p = try comp.resolveEmitPathFlush(arena, .temp, lf.zcu_object_basename.?);
3354 break :p try p.toStringZ(arena);
3355 },
3355 .bin_path = if (zcu_obj_path) |p| try p.toStringZ(arena) else null,
33563356 .asm_path = p: {
33573357 const raw = comp.emit_asm orelse break :p null;
33583358 const p = try comp.resolveEmitPathFlush(arena, .artifact, raw);
......@@ -3379,6 +3379,17 @@ fn flush(comp: *Compilation, arena: Allocator, tid: Zcu.PerThread.Id) (Io.Cancel
33793379 error.AlreadyReported => {},
33803380 error.OutOfMemory => |e| return e,
33813381 };
3382
3383 if (zcu_obj_path) |path| {
3384 // Tell the linker backend about the ZCU object emitted by LLVM.
3385 link.doPrelinkTask(comp, .{ .load_object = path });
3386 // `link.Queue` has not called `prelink` because it knew we would want to send that
3387 // final link input. It is *our* responsibility to call `prelink` now we're done.
3388 comp.bin_file.?.prelink() catch |err| switch (err) {
3389 error.AlreadyReported => return,
3390 else => |e| return e,
3391 };
3392 }
33823393 }
33833394 }
33843395 if (comp.bin_file) |lf| {
......@@ -3390,7 +3401,7 @@ fn flush(comp: *Compilation, arena: Allocator, tid: Zcu.PerThread.Id) (Io.Cancel
33903401 };
33913402 // This is needed before reading the error flags.
33923403 lf.flush(arena, tid, comp.link_prog_node) catch |err| switch (err) {
3393 error.AlreadyReported => {},
3404 error.AlreadyReported => return,
33943405 error.OutOfMemory, error.Canceled => |e| return e,
33953406 };
33963407 }
src/codegen/llvm.zig+14
......@@ -518,6 +518,12 @@ pub const Object = struct {
518518 gpa: Allocator,
519519 builder: Builder,
520520
521 /// The basename of the object file which will emitted by LLVM for the ZCU. Once it it emitted,
522 /// this object file is passed to the active linker implementation as an ordinary link input.
523 ///
524 /// For the full path, use `Compilation.resolveEmitPath` with `kind == .temp`.
525 out_bin_basename: []const u8,
526
521527 /// This pool contains only types (and not `@as(type, undefined)`). It has two purposes:
522528 ///
523529 /// * Lazily tracking ABI alignment of types, so that `@"align"` attributes can be set to a
......@@ -657,6 +663,14 @@ pub const Object = struct {
657663 obj.* = .{
658664 .gpa = gpa,
659665 .builder = builder,
666 .out_bin_basename = try std.zig.binNameAlloc(arena, .{
667 .root_name = try std.fmt.allocPrint(arena, "{s}_zcu", .{comp.root_name}),
668 .cpu_arch = target.cpu.arch,
669 .os_tag = target.os.tag,
670 .ofmt = target.ofmt,
671 .abi = target.abi,
672 .output_mode = .Obj,
673 }),
660674 .type_pool = .empty,
661675 .lazy_abi_aligns = .empty,
662676 .debug_compile_unit = debug_compile_unit,
src/link.zig+15-13
......@@ -402,11 +402,6 @@ pub const File = struct {
402402 emit: Path,
403403
404404 file: ?Io.File,
405 /// When using the LLVM backend, the emitted object is written to a file with this name. This
406 /// object file then becomes a normal link input to LLD or a self-hosted linker.
407 ///
408 /// To convert this to an actual path, see `Compilation.resolveEmitPath` (with `kind == .temp`).
409 zcu_object_basename: ?[]const u8 = null,
410405 gc_sections: bool,
411406 print_gc_sections: bool,
412407 build_id: std.zig.BuildId,
......@@ -1196,20 +1191,22 @@ pub const File = struct {
11961191 /// Called when all linker inputs have been sent via `loadInput`. After
11971192 /// this, `loadInput` will not be called anymore.
11981193 pub fn prelink(base: *File) Error!void {
1199 assert(!base.post_prelink);
1200
1201 // In this case, an object file is created by the LLVM backend, so
1202 // there is no prelink phase. The Zig code is linked as a standard
1203 // object along with the others.
1204 if (base.zcu_object_basename != null) return;
1194 // The guard on this assertion is a temporary hack to make the LLVM backend with LLD work with
1195 // `-fincremental`. This works only because `File.Lld` does nothing in prelink.
1196 // Related: https://codeberg.org/ziglang/zig/issues/32081
1197 if (base.tag != .lld) {
1198 assert(!base.post_prelink);
1199 }
12051200
12061201 switch (base.tag) {
12071202 inline .elf2, .coff2, .wasm => |tag| {
12081203 dev.check(tag.devFeature());
1209 return @as(*tag.Type(), @fieldParentPtr("base", base)).prelink(base.comp.link_prog_node);
1204 try @as(*tag.Type(), @fieldParentPtr("base", base)).prelink(base.comp.link_prog_node);
12101205 },
12111206 else => {},
12121207 }
1208
1209 base.post_prelink = true;
12131210 }
12141211
12151212 /// Legacy function for old linker code
......@@ -1407,7 +1404,12 @@ pub fn doPrelinkTask(comp: *Compilation, task: PrelinkTask) void {
14071404 return;
14081405 };
14091406
1410 assert(!base.post_prelink);
1407 // The guard on this assertion is a temporary hack to make the LLVM backend with LLD work with
1408 // `-fincremental`. This works only because `File.Lld` does nothing in prelink.
1409 // Related: https://codeberg.org/ziglang/zig/issues/32081
1410 if (base.tag != .lld) {
1411 assert(!base.post_prelink);
1412 }
14111413
14121414 var timer = comp.startTimer();
14131415 defer if (timer.finish(io)) |ns| {
src/link/Coff.zig+1-1
......@@ -1524,7 +1524,7 @@ pub fn addReloc(
15241524 target.target_relocs = ri;
15251525}
15261526
1527pub fn prelink(coff: *Coff, prog_node: std.Progress.Node) void {
1527pub fn prelink(coff: *Coff, prog_node: std.Progress.Node) link.Error!void {
15281528 _ = coff;
15291529 _ = prog_node;
15301530}
src/link/Elf.zig+2-31
......@@ -260,10 +260,6 @@ pub fn createEmpty(
260260 .tag = .elf,
261261 .comp = comp,
262262 .emit = emit,
263 .zcu_object_basename = if (use_llvm)
264 try std.fmt.allocPrint(arena, "{s}_zcu.o", .{fs.path.stem(emit.sub_path)})
265 else
266 null,
267263 .gc_sections = options.gc_sections orelse (optimize_mode != .Debug and output_mode != .Obj),
268264 .print_gc_sections = options.print_gc_sections,
269265 .stack_size = options.stack_size orelse 16777216,
......@@ -762,18 +758,14 @@ pub fn flush(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std
762758}
763759
764760fn flushInner(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id) !void {
761 _ = arena;
762
765763 const comp = self.base.comp;
766764 const gpa = comp.gpa;
767765 const diags = &comp.link_diags;
768766
769 const zcu_obj_path: ?Path = if (self.base.zcu_object_basename) |raw| p: {
770 break :p try comp.resolveEmitPathFlush(arena, .temp, raw);
771 } else null;
772
773767 if (self.zigObjectPtr()) |zig_object| try zig_object.flush(self, tid);
774768
775 if (zcu_obj_path) |path| openParseObjectReportingFailure(self, path);
776
777769 switch (comp.config.output_mode) {
778770 .Obj => return relocatable.flushObject(self, comp),
779771 .Lib => switch (comp.config.link_mode) {
......@@ -1046,27 +1038,6 @@ fn dumpArgvInit(self: *Elf, arena: Allocator) !void {
10461038 }
10471039}
10481040
1049pub fn openParseObjectReportingFailure(self: *Elf, path: Path) void {
1050 const comp = self.base.comp;
1051 const io = comp.io;
1052 const diags = &comp.link_diags;
1053 const obj = link.openObject(io, path, false, false) catch |err| {
1054 switch (diags.failParse(path, "failed to open object: {t}", .{err})) {
1055 error.AlreadyReported => return,
1056 }
1057 };
1058 self.parseObjectReportingFailure(obj);
1059}
1060
1061fn parseObjectReportingFailure(self: *Elf, obj: link.Input.Object) void {
1062 const comp = self.base.comp;
1063 const diags = &comp.link_diags;
1064 self.parseObject(obj) catch |err| switch (err) {
1065 error.AlreadyReported => return, // already reported
1066 else => |e| diags.addParseError(obj.path, "failed to parse object: {t}", .{e}),
1067 };
1068}
1069
10701041fn parseObject(self: *Elf, obj: link.Input.Object) !void {
10711042 const tracy = trace(@src());
10721043 defer tracy.end();
src/link/Elf2.zig+24-20
......@@ -4825,25 +4825,29 @@ pub fn prelink(elf: *Elf, prog_node: std.Progress.Node) link.Error!void {
48254825fn prelinkInner(elf: *Elf) Error!void {
48264826 const comp = elf.base.comp;
48274827 const gpa = comp.gpa;
4828 try elf.ensureUnusedSymbolCapacity(1, .all_local);
4829 try elf.inputs.ensureUnusedCapacity(gpa, 1);
4830 const zcu_name = try std.fmt.allocPrint(gpa, "{s}_zcu", .{
4831 std.fs.path.stem(elf.base.emit.sub_path),
4832 });
4833 defer gpa.free(zcu_name);
4834 const zcu_file_symbol = elf.addLocalSymbolAssumeCapacity(.{
4835 .node = .none,
4836 .name = try elf.string(.strtab, zcu_name),
4837 .value = 0,
4838 .size = 0,
4839 .type = .FILE,
4840 .shndx = .ABS,
4841 });
4842 elf.inputs.addOneAssumeCapacity().* = .{
4843 .path = elf.base.emit,
4844 .member = null,
4845 .file_symbol = zcu_file_symbol,
4846 };
4828
4829 if (comp.zcu != null and !comp.config.use_llvm) {
4830 // We're use self-hosted codegen---add an input representing the Zig "object".
4831 try elf.ensureUnusedSymbolCapacity(1, .all_local);
4832 try elf.inputs.ensureUnusedCapacity(gpa, 1);
4833 const zcu_name = try std.fmt.allocPrint(gpa, "{s}_zcu", .{
4834 std.fs.path.stem(elf.base.emit.sub_path),
4835 });
4836 defer gpa.free(zcu_name);
4837 const zcu_file_symbol = elf.addLocalSymbolAssumeCapacity(.{
4838 .node = .none,
4839 .name = try elf.string(.strtab, zcu_name),
4840 .value = 0,
4841 .size = 0,
4842 .type = .FILE,
4843 .shndx = .ABS,
4844 });
4845 elf.inputs.addOneAssumeCapacity().* = .{
4846 .path = elf.base.emit,
4847 .member = null,
4848 .file_symbol = zcu_file_symbol,
4849 };
4850 }
48474851
48484852 if (elf.shndx.dynamic != .UNDEF) switch (elf.identClass()) {
48494853 .NONE, _ => unreachable,
......@@ -5859,8 +5863,8 @@ pub fn flush(
58595863) link.Error!void {
58605864 const comp = elf.base.comp;
58615865 const diags = &comp.link_diags;
5862 _ = arena;
58635866 _ = prog_node;
5867 _ = arena;
58645868
58655869 if (comp.config.output_mode == .Exe) {
58665870 var any_undef = false;
src/link/Lld.zig+8-14
......@@ -207,11 +207,6 @@ pub fn createEmpty(
207207 const output_mode = comp.config.output_mode;
208208 const optimize_mode = comp.root_mod.optimize_mode;
209209
210 const obj_file_ext: []const u8 = switch (target.ofmt) {
211 .coff => "obj",
212 .elf, .wasm => "o",
213 else => unreachable,
214 };
215210 const gc_sections: bool = options.gc_sections orelse switch (target.ofmt) {
216211 .coff => optimize_mode != .Debug,
217212 .elf => optimize_mode != .Debug and output_mode != .Obj,
......@@ -230,7 +225,6 @@ pub fn createEmpty(
230225 .tag = .lld,
231226 .comp = comp,
232227 .emit = emit,
233 .zcu_object_basename = try allocPrint(arena, "{s}_zcu.{s}", .{ fs.path.stem(emit.sub_path), obj_file_ext }),
234228 .gc_sections = gc_sections,
235229 .print_gc_sections = options.print_gc_sections,
236230 .stack_size = stack_size,
......@@ -290,8 +284,8 @@ fn linkAsArchive(lld: *Lld, arena: Allocator) !void {
290284 const full_out_path_z = try arena.dupeSentinel(u8, full_out_path, 0);
291285 const opt_zcu = comp.zcu;
292286
293 const zcu_obj_path: ?Cache.Path = if (opt_zcu != null) p: {
294 break :p try comp.resolveEmitPathFlush(arena, .temp, base.zcu_object_basename.?);
287 const zcu_obj_path: ?Cache.Path = if (opt_zcu) |zcu| p: {
288 break :p try comp.resolveEmitPathFlush(arena, .temp, zcu.llvm_object.?.out_bin_basename);
295289 } else null;
296290
297291 log.debug("zcu_obj_path={?f}", .{zcu_obj_path});
......@@ -376,8 +370,8 @@ fn coffLink(lld: *Lld, arena: Allocator) !void {
376370 const directory = base.emit.root_dir; // Just an alias to make it shorter to type.
377371 const full_out_path = try directory.join(arena, &[_][]const u8{base.emit.sub_path});
378372
379 const zcu_obj_path: ?Cache.Path = if (comp.zcu != null) p: {
380 break :p try comp.resolveEmitPathFlush(arena, .temp, base.zcu_object_basename.?);
373 const zcu_obj_path: ?Cache.Path = if (comp.zcu) |zcu| p: {
374 break :p try comp.resolveEmitPathFlush(arena, .temp, zcu.llvm_object.?.out_bin_basename);
381375 } else null;
382376
383377 const is_lib = comp.config.output_mode == .Lib;
......@@ -766,8 +760,8 @@ fn elfLink(lld: *Lld, arena: Allocator) !void {
766760 const directory = base.emit.root_dir; // Just an alias to make it shorter to type.
767761 const full_out_path = try directory.join(arena, &[_][]const u8{base.emit.sub_path});
768762
769 const zcu_obj_path: ?Cache.Path = if (comp.zcu != null) p: {
770 break :p try comp.resolveEmitPathFlush(arena, .temp, base.zcu_object_basename.?);
763 const zcu_obj_path: ?Cache.Path = if (comp.zcu) |zcu| p: {
764 break :p try comp.resolveEmitPathFlush(arena, .temp, zcu.llvm_object.?.out_bin_basename);
771765 } else null;
772766
773767 const output_mode = comp.config.output_mode;
......@@ -1364,8 +1358,8 @@ fn wasmLink(lld: *Lld, arena: Allocator) !void {
13641358 const directory = base.emit.root_dir; // Just an alias to make it shorter to type.
13651359 const full_out_path = try directory.join(arena, &[_][]const u8{base.emit.sub_path});
13661360
1367 const zcu_obj_path: ?Cache.Path = if (comp.zcu != null) p: {
1368 break :p try comp.resolveEmitPathFlush(arena, .temp, base.zcu_object_basename.?);
1361 const zcu_obj_path: ?Cache.Path = if (comp.zcu) |zcu| p: {
1362 break :p try comp.resolveEmitPathFlush(arena, .temp, zcu.llvm_object.?.out_bin_basename);
13691363 } else null;
13701364
13711365 const is_obj = comp.config.output_mode == .Obj;
src/link/MachO.zig+10-10
......@@ -181,10 +181,6 @@ pub fn createEmpty(
181181 .tag = .macho,
182182 .comp = comp,
183183 .emit = emit,
184 .zcu_object_basename = if (use_llvm)
185 try std.fmt.allocPrint(arena, "{s}_zcu.o", .{fs.path.stem(emit.sub_path)})
186 else
187 null,
188184 .gc_sections = options.gc_sections orelse (optimize_mode != .Debug),
189185 .print_gc_sections = options.print_gc_sections,
190186 .stack_size = options.stack_size orelse 16777216,
......@@ -353,9 +349,11 @@ pub fn flush(
353349 const sub_prog_node = prog_node.start("MachO Flush", 0);
354350 defer sub_prog_node.end();
355351
356 const zcu_obj_path: ?Path = if (self.base.zcu_object_basename) |raw| p: {
357 break :p try comp.resolveEmitPathFlush(arena, .temp, raw);
358 } else null;
352 const zcu_obj_path: ?Path = p: {
353 const zcu = comp.zcu orelse break :p null;
354 const llvm_object = zcu.llvm_object orelse break :p null;
355 break :p try comp.resolveEmitPathFlush(arena, .temp, llvm_object.out_bin_basename);
356 };
359357
360358 // --verbose-link
361359 if (comp.verbose_link) try self.dumpArgv(comp);
......@@ -630,10 +628,12 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {
630628
631629 const directory = self.base.emit.root_dir;
632630 const full_out_path = try directory.join(arena, &[_][]const u8{self.base.emit.sub_path});
633 const zcu_obj_path: ?[]const u8 = if (self.base.zcu_object_basename) |raw| p: {
634 const p = try comp.resolveEmitPathFlush(arena, .temp, raw);
631 const zcu_obj_path: ?[]const u8 = p: {
632 const zcu = comp.zcu orelse break :p null;
633 const llvm_object = zcu.llvm_object orelse break :p null;
634 const p = try comp.resolveEmitPathFlush(arena, .temp, llvm_object.out_bin_basename);
635635 break :p try p.toString(arena);
636 } else null;
636 };
637637
638638 var argv = std.array_list.Managed([]const u8).init(arena);
639639
src/link/Queue.zig+19-13
......@@ -130,14 +130,17 @@ pub fn finishPrelinkQueue(q: *Queue, comp: *Compilation) Io.Cancelable!void {
130130 prelink: {
131131 const lf = comp.bin_file orelse break :prelink;
132132 if (lf.post_prelink) break :prelink;
133 if (comp.zcu != null and comp.zcu.?.llvm_object != null) {
134 // Don't call `prelink` just yet. It will be the frontend's responsibility instead,
135 // after it sends the ZCU object emitted by LLVM as the final link input.
136 break :prelink;
137 }
133138
134 if (lf.prelink()) |_| {
135 lf.post_prelink = true;
136 } else |err| switch (err) {
139 lf.prelink() catch |err| switch (err) {
137140 error.OutOfMemory => comp.link_diags.setAllocFailure(),
138141 error.AlreadyReported => {},
139142 error.Canceled => |e| return e,
140 }
143 };
141144 }
142145}
143146
......@@ -171,16 +174,19 @@ fn runLinkTasks(q: *Queue, comp: *Compilation) void {
171174 }
172175
173176 // We've finished the prelink tasks, so run prelink if necessary.
174 if (comp.bin_file) |lf| {
175 if (!lf.post_prelink) {
176 if (lf.prelink()) |_| {
177 lf.post_prelink = true;
178 } else |err| switch (err) {
179 error.OutOfMemory => comp.link_diags.setAllocFailure(),
180 error.Canceled => @panic("TODO"),
181 error.AlreadyReported => {},
182 }
177 prelink: {
178 const lf = comp.bin_file orelse break :prelink;
179 if (lf.post_prelink) break :prelink;
180 if (comp.zcu != null and comp.zcu.?.llvm_object != null) {
181 // Don't call `prelink` just yet. It will be the frontend's responsibility instead,
182 // after it sends the ZCU object emitted by LLVM as the final link input.
183 break :prelink;
183184 }
185 lf.prelink() catch |err| switch (err) {
186 error.OutOfMemory => comp.link_diags.setAllocFailure(),
187 error.Canceled => @panic("TODO"),
188 error.AlreadyReported => {},
189 };
184190 }
185191
186192 zcu_tasks: while (true) {
src/link/Wasm.zig+1-27
......@@ -2944,7 +2944,6 @@ pub fn createEmpty(
29442944 const target = &comp.root_mod.resolved_target.result;
29452945 assert(target.ofmt == .wasm);
29462946
2947 const use_llvm = comp.config.use_llvm;
29482947 const output_mode = comp.config.output_mode;
29492948 const wasi_exec_model = comp.config.wasi_exec_model;
29502949
......@@ -2954,10 +2953,6 @@ pub fn createEmpty(
29542953 .tag = .wasm,
29552954 .comp = comp,
29562955 .emit = emit,
2957 .zcu_object_basename = if (use_llvm)
2958 try std.fmt.allocPrint(arena, "{s}_zcu.o", .{fs.path.stem(emit.sub_path)})
2959 else
2960 null,
29612956 // Garbage collection is so crucial to WebAssembly that we design
29622957 // the linker around the assumption that it will be on in the vast
29632958 // majority of cases, and therefore express "no garbage collection"
......@@ -3021,22 +3016,6 @@ pub fn createEmpty(
30213016 return wasm;
30223017}
30233018
3024fn openParseObjectReportingFailure(wasm: *Wasm, path: Path) void {
3025 const comp = wasm.base.comp;
3026 const io = comp.io;
3027 const diags = &comp.link_diags;
3028 const obj = link.openObject(io, path, false, false) catch |err| {
3029 switch (diags.failParse(path, "failed to open object: {t}", .{err})) {
3030 error.AlreadyReported => return,
3031 }
3032 };
3033 wasm.parseObject(obj) catch |err| {
3034 switch (diags.failParse(path, "failed to parse object: {t}", .{err})) {
3035 error.AlreadyReported => return,
3036 }
3037 };
3038}
3039
30403019fn parseObject(wasm: *Wasm, obj: link.Input.Object) !void {
30413020 log.debug("parseObject {f}", .{obj.path});
30423021 const gpa = wasm.base.comp.gpa;
......@@ -3822,6 +3801,7 @@ pub fn flush(
38223801 tid: Zcu.PerThread.Id,
38233802 prog_node: std.Progress.Node,
38243803) link.Error!void {
3804 _ = arena;
38253805 // The goal is to never use this because it's only needed if we need to
38263806 // write to InternPool, but flush is too late to be writing to the
38273807 // InternPool.
......@@ -3833,12 +3813,6 @@ pub fn flush(
38333813
38343814 if (comp.verbose_link) try Compilation.dumpArgv(io, wasm.dump_argv_list.items);
38353815
3836 if (wasm.base.zcu_object_basename) |raw| {
3837 const zcu_obj_path: Path = try comp.resolveEmitPathFlush(arena, .temp, raw);
3838 openParseObjectReportingFailure(wasm, zcu_obj_path);
3839 try prelink(wasm, prog_node);
3840 }
3841
38423816 const tracy = trace(@src());
38433817 defer tracy.end();
38443818