| author | |
| committer | |
| log | 41a617990513a0b6a0ad1f10ca8b78f9ea69cb28 |
| tree | cafa0c94af067ffa4e95f22d4629b33c748a4037 |
| parent | 72f0ae7ec46c24d59aef2fae793275bede59ac8c |
| signature |
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 | ... | @@ -3344,15 +3344,15 @@ fn flush(comp: *Compilation, arena: Allocator, tid: Zcu.PerThread.Id) (Io.Cancel |
| 3344 | comp.time_report.?.stats.real_ns_llvm_emit = ns; | 3344 | comp.time_report.?.stats.real_ns_llvm_emit = ns; |
| 3345 | }; | 3345 | }; |
| 3346 | 3346 | ||
| 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 | |||
| 3347 | llvm_object.emit(pt, .{ | 3351 | llvm_object.emit(pt, .{ |
| 3348 | .pre_ir_path = comp.verbose_llvm_ir, | 3352 | .pre_ir_path = comp.verbose_llvm_ir, |
| 3349 | .pre_bc_path = comp.verbose_llvm_bc, | 3353 | .pre_bc_path = comp.verbose_llvm_bc, |
| 3350 | 3354 | ||
| 3351 | .bin_path = p: { | 3355 | .bin_path = if (zcu_obj_path) |p| try p.toStringZ(arena) else null, |
| 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 | }, | ||
| 3356 | .asm_path = p: { | 3356 | .asm_path = p: { |
| 3357 | const raw = comp.emit_asm orelse break :p null; | 3357 | const raw = comp.emit_asm orelse break :p null; |
| 3358 | const p = try comp.resolveEmitPathFlush(arena, .artifact, raw); | 3358 | const p = try comp.resolveEmitPathFlush(arena, .artifact, raw); |
| ... | @@ -3379,6 +3379,17 @@ fn flush(comp: *Compilation, arena: Allocator, tid: Zcu.PerThread.Id) (Io.Cancel | ... | @@ -3379,6 +3379,17 @@ fn flush(comp: *Compilation, arena: Allocator, tid: Zcu.PerThread.Id) (Io.Cancel |
| 3379 | error.AlreadyReported => {}, | 3379 | error.AlreadyReported => {}, |
| 3380 | error.OutOfMemory => |e| return e, | 3380 | error.OutOfMemory => |e| return e, |
| 3381 | }; | 3381 | }; |
| 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 | } | ||
| 3382 | } | 3393 | } |
| 3383 | } | 3394 | } |
| 3384 | if (comp.bin_file) |lf| { | 3395 | if (comp.bin_file) |lf| { |
| ... | @@ -3390,7 +3401,7 @@ fn flush(comp: *Compilation, arena: Allocator, tid: Zcu.PerThread.Id) (Io.Cancel | ... | @@ -3390,7 +3401,7 @@ fn flush(comp: *Compilation, arena: Allocator, tid: Zcu.PerThread.Id) (Io.Cancel |
| 3390 | }; | 3401 | }; |
| 3391 | // This is needed before reading the error flags. | 3402 | // This is needed before reading the error flags. |
| 3392 | lf.flush(arena, tid, comp.link_prog_node) catch |err| switch (err) { | 3403 | lf.flush(arena, tid, comp.link_prog_node) catch |err| switch (err) { |
| 3393 | error.AlreadyReported => {}, | 3404 | error.AlreadyReported => return, |
| 3394 | error.OutOfMemory, error.Canceled => |e| return e, | 3405 | error.OutOfMemory, error.Canceled => |e| return e, |
| 3395 | }; | 3406 | }; |
| 3396 | } | 3407 | } |
src/codegen/llvm.zig+14| ... | @@ -518,6 +518,12 @@ pub const Object = struct { | ... | @@ -518,6 +518,12 @@ pub const Object = struct { |
| 518 | gpa: Allocator, | 518 | gpa: Allocator, |
| 519 | builder: Builder, | 519 | builder: Builder, |
| 520 | 520 | ||
| 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 | |||
| 521 | /// This pool contains only types (and not `@as(type, undefined)`). It has two purposes: | 527 | /// This pool contains only types (and not `@as(type, undefined)`). It has two purposes: |
| 522 | /// | 528 | /// |
| 523 | /// * Lazily tracking ABI alignment of types, so that `@"align"` attributes can be set to a | 529 | /// * Lazily tracking ABI alignment of types, so that `@"align"` attributes can be set to a |
| ... | @@ -657,6 +663,14 @@ pub const Object = struct { | ... | @@ -657,6 +663,14 @@ pub const Object = struct { |
| 657 | obj.* = .{ | 663 | obj.* = .{ |
| 658 | .gpa = gpa, | 664 | .gpa = gpa, |
| 659 | .builder = builder, | 665 | .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 | }), | ||
| 660 | .type_pool = .empty, | 674 | .type_pool = .empty, |
| 661 | .lazy_abi_aligns = .empty, | 675 | .lazy_abi_aligns = .empty, |
| 662 | .debug_compile_unit = debug_compile_unit, | 676 | .debug_compile_unit = debug_compile_unit, |
src/link.zig+15-13| ... | @@ -402,11 +402,6 @@ pub const File = struct { | ... | @@ -402,11 +402,6 @@ pub const File = struct { |
| 402 | emit: Path, | 402 | emit: Path, |
| 403 | 403 | ||
| 404 | file: ?Io.File, | 404 | 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, | ||
| 410 | gc_sections: bool, | 405 | gc_sections: bool, |
| 411 | print_gc_sections: bool, | 406 | print_gc_sections: bool, |
| 412 | build_id: std.zig.BuildId, | 407 | build_id: std.zig.BuildId, |
| ... | @@ -1196,20 +1191,22 @@ pub const File = struct { | ... | @@ -1196,20 +1191,22 @@ pub const File = struct { |
| 1196 | /// Called when all linker inputs have been sent via `loadInput`. After | 1191 | /// Called when all linker inputs have been sent via `loadInput`. After |
| 1197 | /// this, `loadInput` will not be called anymore. | 1192 | /// this, `loadInput` will not be called anymore. |
| 1198 | pub fn prelink(base: *File) Error!void { | 1193 | pub fn prelink(base: *File) Error!void { |
| 1199 | assert(!base.post_prelink); | 1194 | // The guard on this assertion is a temporary hack to make the LLVM backend with LLD work with |
| 1200 | 1195 | // `-fincremental`. This works only because `File.Lld` does nothing in prelink. | |
| 1201 | // In this case, an object file is created by the LLVM backend, so | 1196 | // Related: https://codeberg.org/ziglang/zig/issues/32081 |
| 1202 | // there is no prelink phase. The Zig code is linked as a standard | 1197 | if (base.tag != .lld) { |
| 1203 | // object along with the others. | 1198 | assert(!base.post_prelink); |
| 1204 | if (base.zcu_object_basename != null) return; | 1199 | } |
| 1205 | 1200 | ||
| 1206 | switch (base.tag) { | 1201 | switch (base.tag) { |
| 1207 | inline .elf2, .coff2, .wasm => |tag| { | 1202 | inline .elf2, .coff2, .wasm => |tag| { |
| 1208 | dev.check(tag.devFeature()); | 1203 | 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); |
| 1210 | }, | 1205 | }, |
| 1211 | else => {}, | 1206 | else => {}, |
| 1212 | } | 1207 | } |
| 1208 | |||
| 1209 | base.post_prelink = true; | ||
| 1213 | } | 1210 | } |
| 1214 | 1211 | ||
| 1215 | /// Legacy function for old linker code | 1212 | /// Legacy function for old linker code |
| ... | @@ -1407,7 +1404,12 @@ pub fn doPrelinkTask(comp: *Compilation, task: PrelinkTask) void { | ... | @@ -1407,7 +1404,12 @@ pub fn doPrelinkTask(comp: *Compilation, task: PrelinkTask) void { |
| 1407 | return; | 1404 | return; |
| 1408 | }; | 1405 | }; |
| 1409 | 1406 | ||
| 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 | } | ||
| 1411 | 1413 | ||
| 1412 | var timer = comp.startTimer(); | 1414 | var timer = comp.startTimer(); |
| 1413 | defer if (timer.finish(io)) |ns| { | 1415 | defer if (timer.finish(io)) |ns| { |
src/link/Coff.zig+1-1| ... | @@ -1524,7 +1524,7 @@ pub fn addReloc( | ... | @@ -1524,7 +1524,7 @@ pub fn addReloc( |
| 1524 | target.target_relocs = ri; | 1524 | target.target_relocs = ri; |
| 1525 | } | 1525 | } |
| 1526 | 1526 | ||
| 1527 | pub fn prelink(coff: *Coff, prog_node: std.Progress.Node) void { | 1527 | pub fn prelink(coff: *Coff, prog_node: std.Progress.Node) link.Error!void { |
| 1528 | _ = coff; | 1528 | _ = coff; |
| 1529 | _ = prog_node; | 1529 | _ = prog_node; |
| 1530 | } | 1530 | } |
src/link/Elf.zig+2-31| ... | @@ -260,10 +260,6 @@ pub fn createEmpty( | ... | @@ -260,10 +260,6 @@ pub fn createEmpty( |
| 260 | .tag = .elf, | 260 | .tag = .elf, |
| 261 | .comp = comp, | 261 | .comp = comp, |
| 262 | .emit = emit, | 262 | .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, | ||
| 267 | .gc_sections = options.gc_sections orelse (optimize_mode != .Debug and output_mode != .Obj), | 263 | .gc_sections = options.gc_sections orelse (optimize_mode != .Debug and output_mode != .Obj), |
| 268 | .print_gc_sections = options.print_gc_sections, | 264 | .print_gc_sections = options.print_gc_sections, |
| 269 | .stack_size = options.stack_size orelse 16777216, | 265 | .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 | ... | @@ -762,18 +758,14 @@ pub fn flush(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std |
| 762 | } | 758 | } |
| 763 | 759 | ||
| 764 | fn flushInner(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id) !void { | 760 | fn flushInner(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id) !void { |
| 761 | _ = arena; | ||
| 762 | |||
| 765 | const comp = self.base.comp; | 763 | const comp = self.base.comp; |
| 766 | const gpa = comp.gpa; | 764 | const gpa = comp.gpa; |
| 767 | const diags = &comp.link_diags; | 765 | const diags = &comp.link_diags; |
| 768 | 766 | ||
| 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 | |||
| 773 | if (self.zigObjectPtr()) |zig_object| try zig_object.flush(self, tid); | 767 | if (self.zigObjectPtr()) |zig_object| try zig_object.flush(self, tid); |
| 774 | 768 | ||
| 775 | if (zcu_obj_path) |path| openParseObjectReportingFailure(self, path); | ||
| 776 | |||
| 777 | switch (comp.config.output_mode) { | 769 | switch (comp.config.output_mode) { |
| 778 | .Obj => return relocatable.flushObject(self, comp), | 770 | .Obj => return relocatable.flushObject(self, comp), |
| 779 | .Lib => switch (comp.config.link_mode) { | 771 | .Lib => switch (comp.config.link_mode) { |
| ... | @@ -1046,27 +1038,6 @@ fn dumpArgvInit(self: *Elf, arena: Allocator) !void { | ... | @@ -1046,27 +1038,6 @@ fn dumpArgvInit(self: *Elf, arena: Allocator) !void { |
| 1046 | } | 1038 | } |
| 1047 | } | 1039 | } |
| 1048 | 1040 | ||
| 1049 | pub 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 | |||
| 1061 | fn 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 | |||
| 1070 | fn parseObject(self: *Elf, obj: link.Input.Object) !void { | 1041 | fn parseObject(self: *Elf, obj: link.Input.Object) !void { |
| 1071 | const tracy = trace(@src()); | 1042 | const tracy = trace(@src()); |
| 1072 | defer tracy.end(); | 1043 | 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 { | ... | @@ -4825,25 +4825,29 @@ pub fn prelink(elf: *Elf, prog_node: std.Progress.Node) link.Error!void { |
| 4825 | fn prelinkInner(elf: *Elf) Error!void { | 4825 | fn prelinkInner(elf: *Elf) Error!void { |
| 4826 | const comp = elf.base.comp; | 4826 | const comp = elf.base.comp; |
| 4827 | const gpa = comp.gpa; | 4827 | const gpa = comp.gpa; |
| 4828 | try elf.ensureUnusedSymbolCapacity(1, .all_local); | 4828 | |
| 4829 | try elf.inputs.ensureUnusedCapacity(gpa, 1); | 4829 | if (comp.zcu != null and !comp.config.use_llvm) { |
| 4830 | const zcu_name = try std.fmt.allocPrint(gpa, "{s}_zcu", .{ | 4830 | // We're use self-hosted codegen---add an input representing the Zig "object". |
| 4831 | std.fs.path.stem(elf.base.emit.sub_path), | 4831 | try elf.ensureUnusedSymbolCapacity(1, .all_local); |
| 4832 | }); | 4832 | try elf.inputs.ensureUnusedCapacity(gpa, 1); |
| 4833 | defer gpa.free(zcu_name); | 4833 | const zcu_name = try std.fmt.allocPrint(gpa, "{s}_zcu", .{ |
| 4834 | const zcu_file_symbol = elf.addLocalSymbolAssumeCapacity(.{ | 4834 | std.fs.path.stem(elf.base.emit.sub_path), |
| 4835 | .node = .none, | 4835 | }); |
| 4836 | .name = try elf.string(.strtab, zcu_name), | 4836 | defer gpa.free(zcu_name); |
| 4837 | .value = 0, | 4837 | const zcu_file_symbol = elf.addLocalSymbolAssumeCapacity(.{ |
| 4838 | .size = 0, | 4838 | .node = .none, |
| 4839 | .type = .FILE, | 4839 | .name = try elf.string(.strtab, zcu_name), |
| 4840 | .shndx = .ABS, | 4840 | .value = 0, |
| 4841 | }); | 4841 | .size = 0, |
| 4842 | elf.inputs.addOneAssumeCapacity().* = .{ | 4842 | .type = .FILE, |
| 4843 | .path = elf.base.emit, | 4843 | .shndx = .ABS, |
| 4844 | .member = null, | 4844 | }); |
| 4845 | .file_symbol = zcu_file_symbol, | 4845 | elf.inputs.addOneAssumeCapacity().* = .{ |
| 4846 | }; | 4846 | .path = elf.base.emit, |
| 4847 | .member = null, | ||
| 4848 | .file_symbol = zcu_file_symbol, | ||
| 4849 | }; | ||
| 4850 | } | ||
| 4847 | 4851 | ||
| 4848 | if (elf.shndx.dynamic != .UNDEF) switch (elf.identClass()) { | 4852 | if (elf.shndx.dynamic != .UNDEF) switch (elf.identClass()) { |
| 4849 | .NONE, _ => unreachable, | 4853 | .NONE, _ => unreachable, |
| ... | @@ -5859,8 +5863,8 @@ pub fn flush( | ... | @@ -5859,8 +5863,8 @@ pub fn flush( |
| 5859 | ) link.Error!void { | 5863 | ) link.Error!void { |
| 5860 | const comp = elf.base.comp; | 5864 | const comp = elf.base.comp; |
| 5861 | const diags = &comp.link_diags; | 5865 | const diags = &comp.link_diags; |
| 5862 | _ = arena; | ||
| 5863 | _ = prog_node; | 5866 | _ = prog_node; |
| 5867 | _ = arena; | ||
| 5864 | 5868 | ||
| 5865 | if (comp.config.output_mode == .Exe) { | 5869 | if (comp.config.output_mode == .Exe) { |
| 5866 | var any_undef = false; | 5870 | var any_undef = false; |
src/link/Lld.zig+8-14| ... | @@ -207,11 +207,6 @@ pub fn createEmpty( | ... | @@ -207,11 +207,6 @@ pub fn createEmpty( |
| 207 | const output_mode = comp.config.output_mode; | 207 | const output_mode = comp.config.output_mode; |
| 208 | const optimize_mode = comp.root_mod.optimize_mode; | 208 | const optimize_mode = comp.root_mod.optimize_mode; |
| 209 | 209 | ||
| 210 | const obj_file_ext: []const u8 = switch (target.ofmt) { | ||
| 211 | .coff => "obj", | ||
| 212 | .elf, .wasm => "o", | ||
| 213 | else => unreachable, | ||
| 214 | }; | ||
| 215 | const gc_sections: bool = options.gc_sections orelse switch (target.ofmt) { | 210 | const gc_sections: bool = options.gc_sections orelse switch (target.ofmt) { |
| 216 | .coff => optimize_mode != .Debug, | 211 | .coff => optimize_mode != .Debug, |
| 217 | .elf => optimize_mode != .Debug and output_mode != .Obj, | 212 | .elf => optimize_mode != .Debug and output_mode != .Obj, |
| ... | @@ -230,7 +225,6 @@ pub fn createEmpty( | ... | @@ -230,7 +225,6 @@ pub fn createEmpty( |
| 230 | .tag = .lld, | 225 | .tag = .lld, |
| 231 | .comp = comp, | 226 | .comp = comp, |
| 232 | .emit = emit, | 227 | .emit = emit, |
| 233 | .zcu_object_basename = try allocPrint(arena, "{s}_zcu.{s}", .{ fs.path.stem(emit.sub_path), obj_file_ext }), | ||
| 234 | .gc_sections = gc_sections, | 228 | .gc_sections = gc_sections, |
| 235 | .print_gc_sections = options.print_gc_sections, | 229 | .print_gc_sections = options.print_gc_sections, |
| 236 | .stack_size = stack_size, | 230 | .stack_size = stack_size, |
| ... | @@ -290,8 +284,8 @@ fn linkAsArchive(lld: *Lld, arena: Allocator) !void { | ... | @@ -290,8 +284,8 @@ fn linkAsArchive(lld: *Lld, arena: Allocator) !void { |
| 290 | const full_out_path_z = try arena.dupeSentinel(u8, full_out_path, 0); | 284 | const full_out_path_z = try arena.dupeSentinel(u8, full_out_path, 0); |
| 291 | const opt_zcu = comp.zcu; | 285 | const opt_zcu = comp.zcu; |
| 292 | 286 | ||
| 293 | const zcu_obj_path: ?Cache.Path = if (opt_zcu != null) p: { | 287 | const zcu_obj_path: ?Cache.Path = if (opt_zcu) |zcu| p: { |
| 294 | break :p try comp.resolveEmitPathFlush(arena, .temp, base.zcu_object_basename.?); | 288 | break :p try comp.resolveEmitPathFlush(arena, .temp, zcu.llvm_object.?.out_bin_basename); |
| 295 | } else null; | 289 | } else null; |
| 296 | 290 | ||
| 297 | log.debug("zcu_obj_path={?f}", .{zcu_obj_path}); | 291 | log.debug("zcu_obj_path={?f}", .{zcu_obj_path}); |
| ... | @@ -376,8 +370,8 @@ fn coffLink(lld: *Lld, arena: Allocator) !void { | ... | @@ -376,8 +370,8 @@ fn coffLink(lld: *Lld, arena: Allocator) !void { |
| 376 | const directory = base.emit.root_dir; // Just an alias to make it shorter to type. | 370 | const directory = base.emit.root_dir; // Just an alias to make it shorter to type. |
| 377 | const full_out_path = try directory.join(arena, &[_][]const u8{base.emit.sub_path}); | 371 | const full_out_path = try directory.join(arena, &[_][]const u8{base.emit.sub_path}); |
| 378 | 372 | ||
| 379 | const zcu_obj_path: ?Cache.Path = if (comp.zcu != null) p: { | 373 | const zcu_obj_path: ?Cache.Path = if (comp.zcu) |zcu| p: { |
| 380 | break :p try comp.resolveEmitPathFlush(arena, .temp, base.zcu_object_basename.?); | 374 | break :p try comp.resolveEmitPathFlush(arena, .temp, zcu.llvm_object.?.out_bin_basename); |
| 381 | } else null; | 375 | } else null; |
| 382 | 376 | ||
| 383 | const is_lib = comp.config.output_mode == .Lib; | 377 | const is_lib = comp.config.output_mode == .Lib; |
| ... | @@ -766,8 +760,8 @@ fn elfLink(lld: *Lld, arena: Allocator) !void { | ... | @@ -766,8 +760,8 @@ fn elfLink(lld: *Lld, arena: Allocator) !void { |
| 766 | const directory = base.emit.root_dir; // Just an alias to make it shorter to type. | 760 | const directory = base.emit.root_dir; // Just an alias to make it shorter to type. |
| 767 | const full_out_path = try directory.join(arena, &[_][]const u8{base.emit.sub_path}); | 761 | const full_out_path = try directory.join(arena, &[_][]const u8{base.emit.sub_path}); |
| 768 | 762 | ||
| 769 | const zcu_obj_path: ?Cache.Path = if (comp.zcu != null) p: { | 763 | const zcu_obj_path: ?Cache.Path = if (comp.zcu) |zcu| p: { |
| 770 | break :p try comp.resolveEmitPathFlush(arena, .temp, base.zcu_object_basename.?); | 764 | break :p try comp.resolveEmitPathFlush(arena, .temp, zcu.llvm_object.?.out_bin_basename); |
| 771 | } else null; | 765 | } else null; |
| 772 | 766 | ||
| 773 | const output_mode = comp.config.output_mode; | 767 | const output_mode = comp.config.output_mode; |
| ... | @@ -1364,8 +1358,8 @@ fn wasmLink(lld: *Lld, arena: Allocator) !void { | ... | @@ -1364,8 +1358,8 @@ fn wasmLink(lld: *Lld, arena: Allocator) !void { |
| 1364 | const directory = base.emit.root_dir; // Just an alias to make it shorter to type. | 1358 | const directory = base.emit.root_dir; // Just an alias to make it shorter to type. |
| 1365 | const full_out_path = try directory.join(arena, &[_][]const u8{base.emit.sub_path}); | 1359 | const full_out_path = try directory.join(arena, &[_][]const u8{base.emit.sub_path}); |
| 1366 | 1360 | ||
| 1367 | const zcu_obj_path: ?Cache.Path = if (comp.zcu != null) p: { | 1361 | const zcu_obj_path: ?Cache.Path = if (comp.zcu) |zcu| p: { |
| 1368 | break :p try comp.resolveEmitPathFlush(arena, .temp, base.zcu_object_basename.?); | 1362 | break :p try comp.resolveEmitPathFlush(arena, .temp, zcu.llvm_object.?.out_bin_basename); |
| 1369 | } else null; | 1363 | } else null; |
| 1370 | 1364 | ||
| 1371 | const is_obj = comp.config.output_mode == .Obj; | 1365 | const is_obj = comp.config.output_mode == .Obj; |
src/link/MachO.zig+10-10| ... | @@ -181,10 +181,6 @@ pub fn createEmpty( | ... | @@ -181,10 +181,6 @@ pub fn createEmpty( |
| 181 | .tag = .macho, | 181 | .tag = .macho, |
| 182 | .comp = comp, | 182 | .comp = comp, |
| 183 | .emit = emit, | 183 | .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, | ||
| 188 | .gc_sections = options.gc_sections orelse (optimize_mode != .Debug), | 184 | .gc_sections = options.gc_sections orelse (optimize_mode != .Debug), |
| 189 | .print_gc_sections = options.print_gc_sections, | 185 | .print_gc_sections = options.print_gc_sections, |
| 190 | .stack_size = options.stack_size orelse 16777216, | 186 | .stack_size = options.stack_size orelse 16777216, |
| ... | @@ -353,9 +349,11 @@ pub fn flush( | ... | @@ -353,9 +349,11 @@ pub fn flush( |
| 353 | const sub_prog_node = prog_node.start("MachO Flush", 0); | 349 | const sub_prog_node = prog_node.start("MachO Flush", 0); |
| 354 | defer sub_prog_node.end(); | 350 | defer sub_prog_node.end(); |
| 355 | 351 | ||
| 356 | const zcu_obj_path: ?Path = if (self.base.zcu_object_basename) |raw| p: { | 352 | const zcu_obj_path: ?Path = p: { |
| 357 | break :p try comp.resolveEmitPathFlush(arena, .temp, raw); | 353 | const zcu = comp.zcu orelse break :p null; |
| 358 | } else 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 | }; | ||
| 359 | 357 | ||
| 360 | // --verbose-link | 358 | // --verbose-link |
| 361 | if (comp.verbose_link) try self.dumpArgv(comp); | 359 | if (comp.verbose_link) try self.dumpArgv(comp); |
| ... | @@ -630,10 +628,12 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void { | ... | @@ -630,10 +628,12 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void { |
| 630 | 628 | ||
| 631 | const directory = self.base.emit.root_dir; | 629 | const directory = self.base.emit.root_dir; |
| 632 | const full_out_path = try directory.join(arena, &[_][]const u8{self.base.emit.sub_path}); | 630 | 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: { | 631 | const zcu_obj_path: ?[]const u8 = p: { |
| 634 | const p = try comp.resolveEmitPathFlush(arena, .temp, raw); | 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); | ||
| 635 | break :p try p.toString(arena); | 635 | break :p try p.toString(arena); |
| 636 | } else null; | 636 | }; |
| 637 | 637 | ||
| 638 | var argv = std.array_list.Managed([]const u8).init(arena); | 638 | var argv = std.array_list.Managed([]const u8).init(arena); |
| 639 | 639 |
src/link/Queue.zig+19-13| ... | @@ -130,14 +130,17 @@ pub fn finishPrelinkQueue(q: *Queue, comp: *Compilation) Io.Cancelable!void { | ... | @@ -130,14 +130,17 @@ pub fn finishPrelinkQueue(q: *Queue, comp: *Compilation) Io.Cancelable!void { |
| 130 | prelink: { | 130 | prelink: { |
| 131 | const lf = comp.bin_file orelse break :prelink; | 131 | const lf = comp.bin_file orelse break :prelink; |
| 132 | if (lf.post_prelink) break :prelink; | 132 | 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 | } | ||
| 133 | 138 | ||
| 134 | if (lf.prelink()) |_| { | 139 | lf.prelink() catch |err| switch (err) { |
| 135 | lf.post_prelink = true; | ||
| 136 | } else |err| switch (err) { | ||
| 137 | error.OutOfMemory => comp.link_diags.setAllocFailure(), | 140 | error.OutOfMemory => comp.link_diags.setAllocFailure(), |
| 138 | error.AlreadyReported => {}, | 141 | error.AlreadyReported => {}, |
| 139 | error.Canceled => |e| return e, | 142 | error.Canceled => |e| return e, |
| 140 | } | 143 | }; |
| 141 | } | 144 | } |
| 142 | } | 145 | } |
| 143 | 146 | ||
| ... | @@ -171,16 +174,19 @@ fn runLinkTasks(q: *Queue, comp: *Compilation) void { | ... | @@ -171,16 +174,19 @@ fn runLinkTasks(q: *Queue, comp: *Compilation) void { |
| 171 | } | 174 | } |
| 172 | 175 | ||
| 173 | // We've finished the prelink tasks, so run prelink if necessary. | 176 | // We've finished the prelink tasks, so run prelink if necessary. |
| 174 | if (comp.bin_file) |lf| { | 177 | prelink: { |
| 175 | if (!lf.post_prelink) { | 178 | const lf = comp.bin_file orelse break :prelink; |
| 176 | if (lf.prelink()) |_| { | 179 | if (lf.post_prelink) break :prelink; |
| 177 | lf.post_prelink = true; | 180 | if (comp.zcu != null and comp.zcu.?.llvm_object != null) { |
| 178 | } else |err| switch (err) { | 181 | // Don't call `prelink` just yet. It will be the frontend's responsibility instead, |
| 179 | error.OutOfMemory => comp.link_diags.setAllocFailure(), | 182 | // after it sends the ZCU object emitted by LLVM as the final link input. |
| 180 | error.Canceled => @panic("TODO"), | 183 | break :prelink; |
| 181 | error.AlreadyReported => {}, | ||
| 182 | } | ||
| 183 | } | 184 | } |
| 185 | lf.prelink() catch |err| switch (err) { | ||
| 186 | error.OutOfMemory => comp.link_diags.setAllocFailure(), | ||
| 187 | error.Canceled => @panic("TODO"), | ||
| 188 | error.AlreadyReported => {}, | ||
| 189 | }; | ||
| 184 | } | 190 | } |
| 185 | 191 | ||
| 186 | zcu_tasks: while (true) { | 192 | zcu_tasks: while (true) { |
src/link/Wasm.zig+1-27| ... | @@ -2944,7 +2944,6 @@ pub fn createEmpty( | ... | @@ -2944,7 +2944,6 @@ pub fn createEmpty( |
| 2944 | const target = &comp.root_mod.resolved_target.result; | 2944 | const target = &comp.root_mod.resolved_target.result; |
| 2945 | assert(target.ofmt == .wasm); | 2945 | assert(target.ofmt == .wasm); |
| 2946 | 2946 | ||
| 2947 | const use_llvm = comp.config.use_llvm; | ||
| 2948 | const output_mode = comp.config.output_mode; | 2947 | const output_mode = comp.config.output_mode; |
| 2949 | const wasi_exec_model = comp.config.wasi_exec_model; | 2948 | const wasi_exec_model = comp.config.wasi_exec_model; |
| 2950 | 2949 | ||
| ... | @@ -2954,10 +2953,6 @@ pub fn createEmpty( | ... | @@ -2954,10 +2953,6 @@ pub fn createEmpty( |
| 2954 | .tag = .wasm, | 2953 | .tag = .wasm, |
| 2955 | .comp = comp, | 2954 | .comp = comp, |
| 2956 | .emit = emit, | 2955 | .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, | ||
| 2961 | // Garbage collection is so crucial to WebAssembly that we design | 2956 | // Garbage collection is so crucial to WebAssembly that we design |
| 2962 | // the linker around the assumption that it will be on in the vast | 2957 | // the linker around the assumption that it will be on in the vast |
| 2963 | // majority of cases, and therefore express "no garbage collection" | 2958 | // majority of cases, and therefore express "no garbage collection" |
| ... | @@ -3021,22 +3016,6 @@ pub fn createEmpty( | ... | @@ -3021,22 +3016,6 @@ pub fn createEmpty( |
| 3021 | return wasm; | 3016 | return wasm; |
| 3022 | } | 3017 | } |
| 3023 | 3018 | ||
| 3024 | fn 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 | |||
| 3040 | fn parseObject(wasm: *Wasm, obj: link.Input.Object) !void { | 3019 | fn parseObject(wasm: *Wasm, obj: link.Input.Object) !void { |
| 3041 | log.debug("parseObject {f}", .{obj.path}); | 3020 | log.debug("parseObject {f}", .{obj.path}); |
| 3042 | const gpa = wasm.base.comp.gpa; | 3021 | const gpa = wasm.base.comp.gpa; |
| ... | @@ -3822,6 +3801,7 @@ pub fn flush( | ... | @@ -3822,6 +3801,7 @@ pub fn flush( |
| 3822 | tid: Zcu.PerThread.Id, | 3801 | tid: Zcu.PerThread.Id, |
| 3823 | prog_node: std.Progress.Node, | 3802 | prog_node: std.Progress.Node, |
| 3824 | ) link.Error!void { | 3803 | ) link.Error!void { |
| 3804 | _ = arena; | ||
| 3825 | // The goal is to never use this because it's only needed if we need to | 3805 | // The goal is to never use this because it's only needed if we need to |
| 3826 | // write to InternPool, but flush is too late to be writing to the | 3806 | // write to InternPool, but flush is too late to be writing to the |
| 3827 | // InternPool. | 3807 | // InternPool. |
| ... | @@ -3833,12 +3813,6 @@ pub fn flush( | ... | @@ -3833,12 +3813,6 @@ pub fn flush( |
| 3833 | 3813 | ||
| 3834 | if (comp.verbose_link) try Compilation.dumpArgv(io, wasm.dump_argv_list.items); | 3814 | if (comp.verbose_link) try Compilation.dumpArgv(io, wasm.dump_argv_list.items); |
| 3835 | 3815 | ||
| 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 | |||
| 3842 | const tracy = trace(@src()); | 3816 | const tracy = trace(@src()); |
| 3843 | defer tracy.end(); | 3817 | defer tracy.end(); |
| 3844 | 3818 |