From 3743c3e39c6bb645db7403fd446953d43ac7c7dc Mon Sep 17 00:00:00 2001 From: mlugg Date: Wed, 28 May 2025 06:36:47 +0100 Subject: [PATCH] compiler: slightly untangle LLVM from the linkers The main goal of this commit is to make it easier to decouple codegen from the linkers by being able to do LLVM codegen without going through the `link.File`; however, this ended up being a nice refactor anyway. Previously, every linker stored an optional `llvm.Object`, which was populated when using LLVM for the ZCU *and* linking an output binary; and `Zcu` also stored an optional `llvm.Object`, which was used only when we needed LLVM for the ZCU (e.g. for `-femit-llvm-bc`) but were not emitting a binary. This situation was incredibly silly. It meant there were N+1 places the LLVM object might be instead of just 1, and it meant that every linker had to start a bunch of methods by checking for an LLVM object, and just dispatching to the corresponding method on *it* instead if it was not `null`. Instead, we now always store the LLVM object on the `Zcu` -- which makes sense, because it corresponds to the object emitted by, well, the Zig Compilation Unit! The linkers now mostly don't make reference to LLVM. `Compilation` makes sure to emit the LLVM object if necessary before calling `flush`, so it is ready for the linker. Also, all of the `link.File` methods which act on the ZCU -- like `updateNav` -- now check for the LLVM object in `link.zig` instead of in every single individual linker implementation. Notably, the change to LLVM emit improves this rather ludicrous call chain in the `-fllvm -flld` case: * Compilation.flush * link.File.flush * link.Elf.flush * link.Elf.linkWithLLD * link.Elf.flushModule * link.emitLlvmObject * Compilation.emitLlvmObject * llvm.Object.emit Replacing it with this one: * Compilation.flush * llvm.Object.emit ...although we do currently still end up in `link.Elf.linkWithLLD` to do the actual linking. The logic for invoking LLD should probably also be unified at least somewhat; I haven't done that in this commit. --- src/Compilation.zig | 72 +++++++++----------- src/Zcu.zig | 15 +---- src/Zcu/PerThread.zig | 21 +++--- src/codegen/llvm.zig | 18 +++++ src/codegen/spirv/Section.zig | 2 - src/link.zig | 85 +++++++++++++----------- src/link/C.zig | 4 +- src/link/Coff.zig | 114 +++++++++----------------------- src/link/Dwarf.zig | 2 +- src/link/Elf.zig | 40 +++-------- src/link/Elf/ZigObject.zig | 8 +-- src/link/Goff.zig | 46 ++++++------- src/link/MachO.zig | 29 ++------ src/link/MachO/DebugSymbols.zig | 2 +- src/link/MachO/ZigObject.zig | 8 +-- src/link/Plan9.zig | 10 +-- src/link/SpirV.zig | 8 +-- src/link/Wasm.zig | 32 +++------ src/link/Xcoff.zig | 46 ++++++------- src/target.zig | 2 +- 20 files changed, 227 insertions(+), 337 deletions(-) diff --git a/src/Compilation.zig b/src/Compilation.zig index 69cb2c4d6f0167901b64dd120d95e01a2fd83953..61201f39f48d2efd374c8125d73eb072dc21a7cb 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -2188,14 +2188,10 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil }, } - // Handle the case of e.g. -fno-emit-bin -femit-llvm-ir. - if (options.emit_bin == null and (comp.verbose_llvm_ir != null or - comp.verbose_llvm_bc != null or - (use_llvm and comp.emit_asm != null) or - comp.emit_llvm_ir != null or - comp.emit_llvm_bc != null)) - { - if (opt_zcu) |zcu| zcu.llvm_object = try LlvmObject.create(arena, comp); + if (use_llvm) { + if (opt_zcu) |zcu| { + zcu.llvm_object = try LlvmObject.create(arena, comp); + } } break :comp comp; @@ -2945,6 +2941,33 @@ fn flush( tid: Zcu.PerThread.Id, prog_node: std.Progress.Node, ) !void { + if (comp.zcu) |zcu| { + if (zcu.llvm_object) |llvm_object| { + // Emit the ZCU object from LLVM now; it's required to flush the output file. + // If there's an output file, it wants to decide where the LLVM object goes! + const zcu_obj_emit_loc: ?EmitLoc = if (comp.bin_file) |lf| .{ + .directory = null, + .basename = lf.zcu_object_sub_path.?, + } else null; + const sub_prog_node = prog_node.start("LLVM Emit Object", 0); + defer sub_prog_node.end(); + try llvm_object.emit(.{ + .pre_ir_path = comp.verbose_llvm_ir, + .pre_bc_path = comp.verbose_llvm_bc, + .bin_path = try resolveEmitLoc(arena, default_artifact_directory, zcu_obj_emit_loc), + .asm_path = try resolveEmitLoc(arena, default_artifact_directory, comp.emit_asm), + .post_ir_path = try resolveEmitLoc(arena, default_artifact_directory, comp.emit_llvm_ir), + .post_bc_path = try resolveEmitLoc(arena, default_artifact_directory, comp.emit_llvm_bc), + + .is_debug = comp.root_mod.optimize_mode == .Debug, + .is_small = comp.root_mod.optimize_mode == .ReleaseSmall, + .time_report = comp.time_report, + .sanitize_thread = comp.config.any_sanitize_thread, + .fuzz = comp.config.any_fuzz, + .lto = comp.config.lto, + }); + } + } if (comp.bin_file) |lf| { // This is needed before reading the error flags. lf.flush(arena, tid, prog_node) catch |err| switch (err) { @@ -2952,13 +2975,8 @@ fn flush( error.OutOfMemory => return error.OutOfMemory, }; } - if (comp.zcu) |zcu| { try link.File.C.flushEmitH(zcu); - - if (zcu.llvm_object) |llvm_object| { - try emitLlvmObject(comp, arena, default_artifact_directory, null, llvm_object, prog_node); - } } } @@ -3233,34 +3251,6 @@ fn emitOthers(comp: *Compilation) void { } } -pub fn emitLlvmObject( - comp: *Compilation, - arena: Allocator, - default_artifact_directory: Cache.Path, - bin_emit_loc: ?EmitLoc, - llvm_object: LlvmObject.Ptr, - prog_node: std.Progress.Node, -) !void { - const sub_prog_node = prog_node.start("LLVM Emit Object", 0); - defer sub_prog_node.end(); - - try llvm_object.emit(.{ - .pre_ir_path = comp.verbose_llvm_ir, - .pre_bc_path = comp.verbose_llvm_bc, - .bin_path = try resolveEmitLoc(arena, default_artifact_directory, bin_emit_loc), - .asm_path = try resolveEmitLoc(arena, default_artifact_directory, comp.emit_asm), - .post_ir_path = try resolveEmitLoc(arena, default_artifact_directory, comp.emit_llvm_ir), - .post_bc_path = try resolveEmitLoc(arena, default_artifact_directory, comp.emit_llvm_bc), - - .is_debug = comp.root_mod.optimize_mode == .Debug, - .is_small = comp.root_mod.optimize_mode == .ReleaseSmall, - .time_report = comp.time_report, - .sanitize_thread = comp.config.any_sanitize_thread, - .fuzz = comp.config.any_fuzz, - .lto = comp.config.lto, - }); -} - fn resolveEmitLoc( arena: Allocator, default_artifact_directory: Cache.Path, diff --git a/src/Zcu.zig b/src/Zcu.zig index 7223e5a55e04c6356e9b135802ae896fb2a37685..6a6a74e260e1dfbdadfe4b4a8619472078469f59 100644 --- a/src/Zcu.zig +++ b/src/Zcu.zig @@ -56,9 +56,8 @@ comptime { /// General-purpose allocator. Used for both temporary and long-term storage. gpa: Allocator, comp: *Compilation, -/// Usually, the LlvmObject is managed by linker code, however, in the case -/// that -fno-emit-bin is specified, the linker code never executes, so we -/// store the LlvmObject here. +/// If the ZCU is emitting an LLVM object (i.e. we are using the LLVM backend), then this is the +/// `LlvmObject` we are emitting to. llvm_object: ?LlvmObject.Ptr, /// Pointer to externally managed resource. @@ -267,16 +266,6 @@ resolved_references: ?std.AutoHashMapUnmanaged(AnalUnit, ?ResolvedReference) = n /// Reset to `false` at the start of each update in `Compilation.update`. skip_analysis_this_update: bool = false, -stage1_flags: packed struct { - have_winmain: bool = false, - have_wwinmain: bool = false, - have_winmain_crt_startup: bool = false, - have_wwinmain_crt_startup: bool = false, - have_dllmain_crt_startup: bool = false, - have_c_main: bool = false, - reserved: u2 = 0, -} = .{}, - test_functions: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, void) = .empty, global_assembly: std.AutoArrayHashMapUnmanaged(AnalUnit, []u8) = .empty, diff --git a/src/Zcu/PerThread.zig b/src/Zcu/PerThread.zig index 4b4ae98cb4b41348e93841a05cc6f46e09dcc3c8..b10e6d7c4195f4ca315e1f93f4a9751595747946 100644 --- a/src/Zcu/PerThread.zig +++ b/src/Zcu/PerThread.zig @@ -1784,8 +1784,12 @@ pub fn linkerUpdateFunc(pt: Zcu.PerThread, func_index: InternPool.Index, air: *A }; } - if (comp.bin_file) |lf| { - lf.updateFunc(pt, func_index, air.*, liveness) catch |err| switch (err) { + if (zcu.llvm_object) |llvm_object| { + llvm_object.updateFunc(pt, func_index, air.*, liveness) catch |err| switch (err) { + error.OutOfMemory => return error.OutOfMemory, + }; + } else if (comp.bin_file) |lf| { + lf.updateFunc(pt, func_index, air, liveness) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.CodegenFail => assert(zcu.failed_codegen.contains(nav_index)), error.Overflow, error.RelocationNotByteAligned => { @@ -1798,10 +1802,6 @@ pub fn linkerUpdateFunc(pt: Zcu.PerThread, func_index: InternPool.Index, air: *A // Not a retryable failure. }, }; - } else if (zcu.llvm_object) |llvm_object| { - llvm_object.updateFunc(pt, func_index, air.*, liveness) catch |err| switch (err) { - error.OutOfMemory => return error.OutOfMemory, - }; } } @@ -1877,7 +1877,6 @@ fn createFileRootStruct( try pt.scanNamespace(namespace_index, decls); try zcu.comp.queueJob(.{ .resolve_type_fully = wip_ty.index }); codegen_type: { - if (zcu.comp.config.use_llvm) break :codegen_type; if (file.mod.?.strip) break :codegen_type; // This job depends on any resolve_type_fully jobs queued up before it. try zcu.comp.queueJob(.{ .link_type = wip_ty.index }); @@ -3309,10 +3308,10 @@ fn processExportsInner( .uav => {}, } - if (zcu.comp.bin_file) |lf| { - try zcu.handleUpdateExports(export_indices, lf.updateExports(pt, exported, export_indices)); - } else if (zcu.llvm_object) |llvm_object| { + if (zcu.llvm_object) |llvm_object| { try zcu.handleUpdateExports(export_indices, llvm_object.updateExports(pt, exported, export_indices)); + } else if (zcu.comp.bin_file) |lf| { + try zcu.handleUpdateExports(export_indices, lf.updateExports(pt, exported, export_indices)); } } @@ -4064,7 +4063,6 @@ fn recreateStructType( try zcu.comp.queueJob(.{ .resolve_type_fully = wip_ty.index }); codegen_type: { - if (zcu.comp.config.use_llvm) break :codegen_type; if (file.mod.?.strip) break :codegen_type; // This job depends on any resolve_type_fully jobs queued up before it. try zcu.comp.queueJob(.{ .link_type = wip_ty.index }); @@ -4157,7 +4155,6 @@ fn recreateUnionType( try zcu.comp.queueJob(.{ .resolve_type_fully = wip_ty.index }); codegen_type: { - if (zcu.comp.config.use_llvm) break :codegen_type; if (file.mod.?.strip) break :codegen_type; // This job depends on any resolve_type_fully jobs queued up before it. try zcu.comp.queueJob(.{ .link_type = wip_ty.index }); diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 3fc6250d3f321a720c75b34a3da06ad9db243626..2b39396c38ac5c2074ed60c6c1db033e50e97f3b 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -1586,6 +1586,24 @@ pub const Object = struct { const global_index = self.nav_map.get(nav_index).?; const comp = zcu.comp; + // If we're on COFF and linking with LLD, the linker cares about our exports to determine the subsystem in use. + if (comp.bin_file != null and + comp.bin_file.?.tag == .coff and + zcu.comp.config.use_lld and + ip.isFunctionType(ip.getNav(nav_index).typeOf(ip))) + { + const flags = &comp.bin_file.?.cast(.coff).?.lld_export_flags; + for (export_indices) |export_index| { + const name = export_index.ptr(zcu).opts.name; + if (name.eqlSlice("main", ip)) flags.c_main = true; + if (name.eqlSlice("WinMain", ip)) flags.winmain = true; + if (name.eqlSlice("wWinMain", ip)) flags.wwinmain = true; + if (name.eqlSlice("WinMainCRTStartup", ip)) flags.winmain_crt_startup = true; + if (name.eqlSlice("wWinMainCRTStartup", ip)) flags.wwinmain_crt_startup = true; + if (name.eqlSlice("DllMainCRTStartup", ip)) flags.dllmain_crt_startup = true; + } + } + if (export_indices.len != 0) { return updateExportedGlobal(self, zcu, global_index, export_indices); } else { diff --git a/src/codegen/spirv/Section.zig b/src/codegen/spirv/Section.zig index 4fe12f999f82bb055efc8a687a64888b38d90ea9..5c2a5fde62d012858fb34565800ec1af79d7107c 100644 --- a/src/codegen/spirv/Section.zig +++ b/src/codegen/spirv/Section.zig @@ -386,8 +386,6 @@ test "SPIR-V Section emit() - string" { } test "SPIR-V Section emit() - extended mask" { - if (@import("builtin").zig_backend == .stage1) return error.SkipZigTest; - var section = Section{}; defer section.deinit(std.testing.allocator); diff --git a/src/link.zig b/src/link.zig index 7673b44e47ba317ab5538794a090be88b31b89f3..3270d10c876554034f1b6412ff6aba53df82bdf5 100644 --- a/src/link.zig +++ b/src/link.zig @@ -19,7 +19,6 @@ const Zcu = @import("Zcu.zig"); const InternPool = @import("InternPool.zig"); const Type = @import("Type.zig"); const Value = @import("Value.zig"); -const LlvmObject = @import("codegen/llvm.zig").Object; const lldMain = @import("main.zig").lldMain; const Package = @import("Package.zig"); const dev = @import("dev.zig"); @@ -704,7 +703,9 @@ pub const File = struct { } /// May be called before or after updateExports for any given Nav. + /// Asserts that the ZCU is not using the LLVM backend. fn updateNav(base: *File, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) UpdateNavError!void { + assert(base.comp.zcu.?.llvm_object == null); const nav = pt.zcu.intern_pool.getNav(nav_index); assert(nav.status == .fully_resolved); switch (base.tag) { @@ -721,7 +722,9 @@ pub const File = struct { TypeFailureReported, }; + /// Never called when LLVM is codegenning the ZCU. fn updateContainerType(base: *File, pt: Zcu.PerThread, ty: InternPool.Index) UpdateContainerTypeError!void { + assert(base.comp.zcu.?.llvm_object == null); switch (base.tag) { else => {}, inline .elf => |tag| { @@ -733,6 +736,7 @@ pub const File = struct { /// May be called before or after updateExports for any given Decl. /// TODO: currently `pub` because `Zcu.PerThread` is calling this. + /// Never called when LLVM is codegenning the ZCU. pub fn updateFunc( base: *File, pt: Zcu.PerThread, @@ -740,6 +744,7 @@ pub const File = struct { air: Air, liveness: Air.Liveness, ) UpdateNavError!void { + assert(base.comp.zcu.?.llvm_object == null); switch (base.tag) { inline else => |tag| { dev.check(tag.devFeature()); @@ -756,7 +761,9 @@ pub const File = struct { /// On an incremental update, fixup the line number of all `Nav`s at the given `TrackedInst`, because /// its line number has changed. The ZIR instruction `ti_id` has tag `.declaration`. + /// Never called when LLVM is codegenning the ZCU. fn updateLineNumber(base: *File, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) UpdateLineNumberError!void { + assert(base.comp.zcu.?.llvm_object == null); { const ti = ti_id.resolveFull(&pt.zcu.intern_pool).?; const file = pt.zcu.fileByIndex(ti.file); @@ -846,11 +853,13 @@ pub const File = struct { /// Commit pending changes and write headers. Works based on `effectiveOutputMode` /// rather than final output mode. - pub fn flushModule(base: *File, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) FlushError!void { + /// Never called when LLVM is codegenning the ZCU. + fn flushZcu(base: *File, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) FlushError!void { + assert(base.comp.zcu.?.llvm_object == null); switch (base.tag) { inline else => |tag| { dev.check(tag.devFeature()); - return @as(*tag.Type(), @fieldParentPtr("base", base)).flushModule(arena, tid, prog_node); + return @as(*tag.Type(), @fieldParentPtr("base", base)).flushZcu(arena, tid, prog_node); }, } } @@ -864,12 +873,14 @@ pub const File = struct { /// a list of size 1, meaning that `exported` is exported once. However, it is possible /// to export the same thing with multiple different symbol names (aliases). /// May be called before or after updateDecl for any given Decl. + /// Never called when LLVM is codegenning the ZCU. pub fn updateExports( base: *File, pt: Zcu.PerThread, exported: Zcu.Exported, export_indices: []const Zcu.Export.Index, ) UpdateExportsError!void { + assert(base.comp.zcu.?.llvm_object == null); switch (base.tag) { inline else => |tag| { dev.check(tag.devFeature()); @@ -896,7 +907,9 @@ pub const File = struct { /// `Nav`'s address was not yet resolved, or the containing atom gets moved in virtual memory. /// May be called before or after updateFunc/updateNav therefore it is up to the linker to allocate /// the block/atom. + /// Never called when LLVM is codegenning the ZCU. pub fn getNavVAddr(base: *File, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index, reloc_info: RelocInfo) !u64 { + assert(base.comp.zcu.?.llvm_object == null); switch (base.tag) { .c => unreachable, .spirv => unreachable, @@ -909,6 +922,7 @@ pub const File = struct { } } + /// Never called when LLVM is codegenning the ZCU. pub fn lowerUav( base: *File, pt: Zcu.PerThread, @@ -916,6 +930,7 @@ pub const File = struct { decl_align: InternPool.Alignment, src_loc: Zcu.LazySrcLoc, ) !codegen.GenResult { + assert(base.comp.zcu.?.llvm_object == null); switch (base.tag) { .c => unreachable, .spirv => unreachable, @@ -928,7 +943,9 @@ pub const File = struct { } } + /// Never called when LLVM is codegenning the ZCU. pub fn getUavVAddr(base: *File, decl_val: InternPool.Index, reloc_info: RelocInfo) !u64 { + assert(base.comp.zcu.?.llvm_object == null); switch (base.tag) { .c => unreachable, .spirv => unreachable, @@ -941,11 +958,13 @@ pub const File = struct { } } + /// Never called when LLVM is codegenning the ZCU. pub fn deleteExport( base: *File, exported: Zcu.Exported, name: InternPool.NullTerminatedString, ) void { + assert(base.comp.zcu.?.llvm_object == null); switch (base.tag) { .plan9, .spirv, @@ -1077,7 +1096,7 @@ pub const File = struct { } } - pub fn linkAsArchive(base: *File, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) FlushError!void { + fn linkAsArchive(base: *File, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) FlushError!void { dev.check(.lld_linker); const tracy = trace(@src()); @@ -1103,9 +1122,12 @@ pub const File = struct { // If there is no Zig code to compile, then we should skip flushing the output file // because it will not be part of the linker line anyway. - const zcu_obj_path: ?[]const u8 = if (opt_zcu != null) blk: { - try base.flushModule(arena, tid, prog_node); - + const zcu_obj_path: ?[]const u8 = if (opt_zcu) |zcu| blk: { + if (zcu.llvm_object == null) { + try base.flushZcu(arena, tid, prog_node); + } else { + // `Compilation.flush` has already made LLVM emit this object file for us. + } const dirname = fs.path.dirname(full_out_path_z) orelse "."; break :blk try fs.path.join(arena, &.{ dirname, base.zcu_object_sub_path.? }); } else null; @@ -1346,21 +1368,6 @@ pub const File = struct { return output_mode == .Lib and !self.isStatic(); } - pub fn emitLlvmObject( - base: File, - arena: Allocator, - llvm_object: LlvmObject.Ptr, - prog_node: std.Progress.Node, - ) !void { - return base.comp.emitLlvmObject(arena, .{ - .root_dir = base.emit.root_dir, - .sub_path = std.fs.path.dirname(base.emit.sub_path) orelse "", - }, .{ - .directory = null, - .basename = base.zcu_object_sub_path.?, - }, llvm_object, prog_node); - } - pub fn cgFail( base: *File, nav_index: InternPool.Nav.Index, @@ -1600,7 +1607,11 @@ pub fn doTask(comp: *Compilation, tid: usize, task: Task) void { // on the failed type, so when it is changed the `Nav` will be updated. return; } - if (comp.bin_file) |lf| { + if (zcu.llvm_object) |llvm_object| { + llvm_object.updateNav(pt, nav_index) catch |err| switch (err) { + error.OutOfMemory => diags.setAllocFailure(), + }; + } else if (comp.bin_file) |lf| { lf.updateNav(pt, nav_index) catch |err| switch (err) { error.OutOfMemory => diags.setAllocFailure(), error.CodegenFail => assert(zcu.failed_codegen.contains(nav_index)), @@ -1616,10 +1627,6 @@ pub fn doTask(comp: *Compilation, tid: usize, task: Task) void { // Not a retryable failure. }, }; - } else if (zcu.llvm_object) |llvm_object| { - llvm_object.updateNav(pt, nav_index) catch |err| switch (err) { - error.OutOfMemory => diags.setAllocFailure(), - }; } }, .link_func => |func| { @@ -1650,11 +1657,13 @@ pub fn doTask(comp: *Compilation, tid: usize, task: Task) void { // on the failed type, so when that is changed, this type will be updated. return; } - if (comp.bin_file) |lf| { - lf.updateContainerType(pt, ty) catch |err| switch (err) { - error.OutOfMemory => diags.setAllocFailure(), - error.TypeFailureReported => assert(zcu.failed_types.contains(ty)), - }; + if (zcu.llvm_object == null) { + if (comp.bin_file) |lf| { + lf.updateContainerType(pt, ty) catch |err| switch (err) { + error.OutOfMemory => diags.setAllocFailure(), + error.TypeFailureReported => assert(zcu.failed_types.contains(ty)), + }; + } } }, .update_line_number => |ti| { @@ -1664,11 +1673,13 @@ pub fn doTask(comp: *Compilation, tid: usize, task: Task) void { } const pt: Zcu.PerThread = .activate(comp.zcu.?, @enumFromInt(tid)); defer pt.deactivate(); - if (comp.bin_file) |lf| { - lf.updateLineNumber(pt, ti) catch |err| switch (err) { - error.OutOfMemory => diags.setAllocFailure(), - else => |e| log.err("update line number failed: {s}", .{@errorName(e)}), - }; + if (pt.zcu.llvm_object == null) { + if (comp.bin_file) |lf| { + lf.updateLineNumber(pt, ti) catch |err| switch (err) { + error.OutOfMemory => diags.setAllocFailure(), + else => |e| log.err("update line number failed: {s}", .{@errorName(e)}), + }; + } } }, } diff --git a/src/link/C.zig b/src/link/C.zig index c32d8ba80ba07308512c188384636348c29128d2..15004a26b7e55038aac6c7431ad60aa812009066 100644 --- a/src/link/C.zig +++ b/src/link/C.zig @@ -382,7 +382,7 @@ pub fn updateLineNumber(self: *C, pt: Zcu.PerThread, ti_id: InternPool.TrackedIn } pub fn flush(self: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void { - return self.flushModule(arena, tid, prog_node); + return self.flushZcu(arena, tid, prog_node); } fn abiDefines(self: *C, target: std.Target) !std.ArrayList(u8) { @@ -400,7 +400,7 @@ fn abiDefines(self: *C, target: std.Target) !std.ArrayList(u8) { return defines; } -pub fn flushModule(self: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void { +pub fn flushZcu(self: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void { _ = arena; // Has the same lifetime as the call to Compilation.update. const tracy = trace(@src()); diff --git a/src/link/Coff.zig b/src/link/Coff.zig index 5100406030bd8ea995465119f44397a2e241e308..12a9dc975328e510bde660932a417bbac6fa0054 100644 --- a/src/link/Coff.zig +++ b/src/link/Coff.zig @@ -3,9 +3,6 @@ //! LLD for traditional linking (linking relocatable object files). //! LLD is also the default linker for LLVM. -/// If this is not null, an object file is created by LLVM and emitted to zcu_object_sub_path. -llvm_object: ?LlvmObject.Ptr = null, - base: link.File, image_base: u64, subsystem: ?std.Target.SubSystem, @@ -87,6 +84,16 @@ base_relocs: BaseRelocationTable = .{}, /// Hot-code swapping state. hot_state: if (is_hot_update_compatible) HotUpdateState else struct {} = .{}, +/// When linking with LLD, these flags are used to determine the subsystem to pass on the LLD command line. +lld_export_flags: struct { + c_main: bool = false, + winmain: bool = false, + wwinmain: bool = false, + winmain_crt_startup: bool = false, + wwinmain_crt_startup: bool = false, + dllmain_crt_startup: bool = false, +} = .{}, + const is_hot_update_compatible = switch (builtin.target.os.tag) { .windows => true, else => false, @@ -302,9 +309,6 @@ pub fn createEmpty( .pdb_out_path = options.pdb_out_path, .repro = options.repro, }; - if (use_llvm and comp.config.have_zcu) { - coff.llvm_object = try LlvmObject.create(arena, comp); - } errdefer coff.base.destroy(); if (use_lld and (use_llvm or !comp.config.have_zcu)) { @@ -322,7 +326,6 @@ pub fn createEmpty( .mode = link.File.determineMode(use_lld, output_mode, link_mode), }); - assert(coff.llvm_object == null); const gpa = comp.gpa; try coff.strtab.buffer.ensureUnusedCapacity(gpa, @sizeOf(u32)); @@ -428,8 +431,6 @@ pub fn open( pub fn deinit(coff: *Coff) void { const gpa = coff.base.comp.gpa; - if (coff.llvm_object) |llvm_object| llvm_object.deinit(); - for (coff.sections.items(.free_list)) |*free_list| { free_list.deinit(gpa); } @@ -1103,9 +1104,6 @@ pub fn updateFunc( if (build_options.skip_non_native and builtin.object_format != .coff) { @panic("Attempted to compile for object format that was disabled by build configuration"); } - if (coff.llvm_object) |llvm_object| { - return llvm_object.updateFunc(pt, func_index, air, liveness); - } const tracy = trace(@src()); defer tracy.end(); @@ -1205,7 +1203,6 @@ pub fn updateNav( if (build_options.skip_non_native and builtin.object_format != .coff) { @panic("Attempted to compile for object format that was disabled by build configuration"); } - if (coff.llvm_object) |llvm_object| return llvm_object.updateNav(pt, nav_index); const tracy = trace(@src()); defer tracy.end(); @@ -1330,7 +1327,7 @@ pub fn getOrCreateAtomForLazySymbol( } state_ptr.* = .pending_flush; const atom = atom_ptr.*; - // anyerror needs to be deferred until flushModule + // anyerror needs to be deferred until flushZcu if (lazy_sym.ty != .anyerror_type) try coff.updateLazySymbolAtom(pt, lazy_sym, atom, switch (lazy_sym.kind) { .code => coff.text_section_index.?, .const_data => coff.rdata_section_index.?, @@ -1463,8 +1460,6 @@ fn updateNavCode( } pub fn freeNav(coff: *Coff, nav_index: InternPool.NavIndex) void { - if (coff.llvm_object) |llvm_object| return llvm_object.freeNav(nav_index); - const gpa = coff.base.comp.gpa; if (coff.decls.fetchOrderedRemove(nav_index)) |const_kv| { @@ -1485,50 +1480,7 @@ pub fn updateExports( } const zcu = pt.zcu; - const ip = &zcu.intern_pool; - const comp = coff.base.comp; - const target = comp.root_mod.resolved_target.result; - - if (comp.config.use_llvm) { - // Even in the case of LLVM, we need to notice certain exported symbols in order to - // detect the default subsystem. - for (export_indices) |export_idx| { - const exp = export_idx.ptr(zcu); - const exported_nav_index = switch (exp.exported) { - .nav => |nav| nav, - .uav => continue, - }; - const exported_nav = ip.getNav(exported_nav_index); - const exported_ty = exported_nav.typeOf(ip); - if (!ip.isFunctionType(exported_ty)) continue; - const c_cc = target.cCallingConvention().?; - const winapi_cc: std.builtin.CallingConvention = switch (target.cpu.arch) { - .x86 => .{ .x86_stdcall = .{} }, - else => c_cc, - }; - const exported_cc = Type.fromInterned(exported_ty).fnCallingConvention(zcu); - const CcTag = std.builtin.CallingConvention.Tag; - if (@as(CcTag, exported_cc) == @as(CcTag, c_cc) and exp.opts.name.eqlSlice("main", ip) and comp.config.link_libc) { - zcu.stage1_flags.have_c_main = true; - } else if (@as(CcTag, exported_cc) == @as(CcTag, winapi_cc) and target.os.tag == .windows) { - if (exp.opts.name.eqlSlice("WinMain", ip)) { - zcu.stage1_flags.have_winmain = true; - } else if (exp.opts.name.eqlSlice("wWinMain", ip)) { - zcu.stage1_flags.have_wwinmain = true; - } else if (exp.opts.name.eqlSlice("WinMainCRTStartup", ip)) { - zcu.stage1_flags.have_winmain_crt_startup = true; - } else if (exp.opts.name.eqlSlice("wWinMainCRTStartup", ip)) { - zcu.stage1_flags.have_wwinmain_crt_startup = true; - } else if (exp.opts.name.eqlSlice("DllMainCRTStartup", ip)) { - zcu.stage1_flags.have_dllmain_crt_startup = true; - } - } - } - } - - if (coff.llvm_object) |llvm_object| return llvm_object.updateExports(pt, exported, export_indices); - - const gpa = comp.gpa; + const gpa = zcu.gpa; const metadata = switch (exported) { .nav => |nav| blk: { @@ -1621,7 +1573,6 @@ pub fn deleteExport( exported: Zcu.Exported, name: InternPool.NullTerminatedString, ) void { - if (coff.llvm_object) |_| return; const metadata = switch (exported) { .nav => |nav| coff.navs.getPtr(nav), .uav => |uav| coff.uavs.getPtr(uav), @@ -1692,7 +1643,7 @@ pub fn flush(coff: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: st }; } switch (comp.config.output_mode) { - .Exe, .Obj => return coff.flushModule(arena, tid, prog_node), + .Exe, .Obj => return coff.flushZcu(arena, tid, prog_node), .Lib => return diags.fail("writing lib files not yet implemented for COFF", .{}), } } @@ -1711,8 +1662,12 @@ fn linkWithLLD(coff: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: // If there is no Zig code to compile, then we should skip flushing the output file because it // will not be part of the linker line anyway. - const module_obj_path: ?[]const u8 = if (comp.zcu != null) blk: { - try coff.flushModule(arena, tid, prog_node); + const module_obj_path: ?[]const u8 = if (comp.zcu) |zcu| blk: { + if (zcu.llvm_object == null) { + try coff.flushZcu(arena, tid, prog_node); + } else { + // `Compilation.flush` has already made LLVM emit this object file for us. + } if (fs.path.dirname(full_out_path)) |dirname| { break :blk try fs.path.join(arena, &.{ dirname, coff.base.zcu_object_sub_path.? }); @@ -1998,16 +1953,16 @@ fn linkWithLLD(coff: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: if (coff.subsystem) |explicit| break :blk explicit; switch (target.os.tag) { .windows => { - if (comp.zcu) |module| { - if (module.stage1_flags.have_dllmain_crt_startup or is_dyn_lib) + if (comp.zcu != null) { + if (coff.lld_export_flags.dllmain_crt_startup or is_dyn_lib) break :blk null; - if (module.stage1_flags.have_c_main or comp.config.is_test or - module.stage1_flags.have_winmain_crt_startup or - module.stage1_flags.have_wwinmain_crt_startup) + if (coff.lld_export_flags.c_main or comp.config.is_test or + coff.lld_export_flags.winmain_crt_startup or + coff.lld_export_flags.wwinmain_crt_startup) { break :blk .Console; } - if (module.stage1_flags.have_winmain or module.stage1_flags.have_wwinmain) + if (coff.lld_export_flags.winmain or coff.lld_export_flags.wwinmain) break :blk .Windows; } }, @@ -2136,8 +2091,8 @@ fn linkWithLLD(coff: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: } else { try argv.append("-NODEFAULTLIB"); if (!is_lib and entry_name == null) { - if (comp.zcu) |module| { - if (module.stage1_flags.have_winmain_crt_startup) { + if (comp.zcu != null) { + if (coff.lld_export_flags.winmain_crt_startup) { try argv.append("-ENTRY:WinMainCRTStartup"); } else { try argv.append("-ENTRY:wWinMainCRTStartup"); @@ -2244,7 +2199,7 @@ fn findLib(arena: Allocator, name: []const u8, lib_directories: []const Director return null; } -pub fn flushModule( +pub fn flushZcu( coff: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, @@ -2256,22 +2211,17 @@ pub fn flushModule( const comp = coff.base.comp; const diags = &comp.link_diags; - if (coff.llvm_object) |llvm_object| { - try coff.base.emitLlvmObject(arena, llvm_object, prog_node); - return; - } - const sub_prog_node = prog_node.start("COFF Flush", 0); defer sub_prog_node.end(); - return flushModuleInner(coff, arena, tid) catch |err| switch (err) { + return flushZcuInner(coff, arena, tid) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.LinkFailure => return error.LinkFailure, else => |e| return diags.fail("COFF flush failed: {s}", .{@errorName(e)}), }; } -fn flushModuleInner(coff: *Coff, arena: Allocator, tid: Zcu.PerThread.Id) !void { +fn flushZcuInner(coff: *Coff, arena: Allocator, tid: Zcu.PerThread.Id) !void { _ = arena; const comp = coff.base.comp; @@ -2397,7 +2347,6 @@ pub fn getNavVAddr( nav_index: InternPool.Nav.Index, reloc_info: link.File.RelocInfo, ) !u64 { - assert(coff.llvm_object == null); const zcu = pt.zcu; const ip = &zcu.intern_pool; const nav = ip.getNav(nav_index); @@ -2483,8 +2432,6 @@ pub fn getUavVAddr( uav: InternPool.Index, reloc_info: link.File.RelocInfo, ) !u64 { - assert(coff.llvm_object == null); - const this_atom_index = coff.uavs.get(uav).?.atom; const sym_index = coff.getAtom(this_atom_index).getSymbolIndex().?; const atom_index = coff.getAtomIndexForSymbol(.{ @@ -3798,7 +3745,6 @@ const trace = @import("../tracy.zig").trace; const Air = @import("../Air.zig"); const Compilation = @import("../Compilation.zig"); -const LlvmObject = @import("../codegen/llvm.zig").Object; const Zcu = @import("../Zcu.zig"); const InternPool = @import("../InternPool.zig"); const TableSection = @import("table_section.zig").TableSection; diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index e2b8229736ec854dd17cbd5fc45ae3945586826b..c0d1281df2e4dd37bc5f28b3b43b1b7ae574896b 100644 --- a/src/link/Dwarf.zig +++ b/src/link/Dwarf.zig @@ -4391,7 +4391,7 @@ fn refAbbrevCode(dwarf: *Dwarf, abbrev_code: AbbrevCode) UpdateError!@typeInfo(A return @intFromEnum(abbrev_code); } -pub fn flushModule(dwarf: *Dwarf, pt: Zcu.PerThread) FlushError!void { +pub fn flushZcu(dwarf: *Dwarf, pt: Zcu.PerThread) FlushError!void { const zcu = pt.zcu; const ip = &zcu.intern_pool; diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 1516993c748990e1ea991b0ce8b4fea17e8a60d7..b18fc7ce33505409863a800db3e45371e35fc091 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -32,9 +32,6 @@ entry_name: ?[]const u8, ptr_width: PtrWidth, -/// If this is not null, an object file is created by LLVM and emitted to zcu_object_sub_path. -llvm_object: ?LlvmObject.Ptr = null, - /// A list of all input files. /// First index is a special "null file". Order is otherwise not observed. files: std.MultiArrayList(File.Entry) = .{}, @@ -344,9 +341,6 @@ pub fn createEmpty( .print_map = options.print_map, .dump_argv_list = .empty, }; - if (use_llvm and comp.config.have_zcu) { - self.llvm_object = try LlvmObject.create(arena, comp); - } errdefer self.base.destroy(); if (use_lld and (use_llvm or !comp.config.have_zcu)) { @@ -457,8 +451,6 @@ pub fn open( pub fn deinit(self: *Elf) void { const gpa = self.base.comp.gpa; - if (self.llvm_object) |llvm_object| llvm_object.deinit(); - for (self.file_handles.items) |fh| { fh.close(); } @@ -515,7 +507,6 @@ pub fn deinit(self: *Elf) void { } pub fn getNavVAddr(self: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index, reloc_info: link.File.RelocInfo) !u64 { - assert(self.llvm_object == null); return self.zigObjectPtr().?.getNavVAddr(self, pt, nav_index, reloc_info); } @@ -530,7 +521,6 @@ pub fn lowerUav( } pub fn getUavVAddr(self: *Elf, uav: InternPool.Index, reloc_info: link.File.RelocInfo) !u64 { - assert(self.llvm_object == null); return self.zigObjectPtr().?.getUavVAddr(self, uav, reloc_info); } @@ -805,35 +795,29 @@ pub fn flush(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std else => |e| return diags.fail("failed to link with LLD: {s}", .{@errorName(e)}), }; } - try self.flushModule(arena, tid, prog_node); + try self.flushZcu(arena, tid, prog_node); } -pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void { +pub fn flushZcu(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void { const tracy = trace(@src()); defer tracy.end(); const comp = self.base.comp; const diags = &comp.link_diags; - if (self.llvm_object) |llvm_object| { - try self.base.emitLlvmObject(arena, llvm_object, prog_node); - const use_lld = build_options.have_llvm and comp.config.use_lld; - if (use_lld) return; - } - if (comp.verbose_link) Compilation.dump_argv(self.dump_argv_list.items); const sub_prog_node = prog_node.start("ELF Flush", 0); defer sub_prog_node.end(); - return flushModuleInner(self, arena, tid) catch |err| switch (err) { + return flushZcuInner(self, arena, tid) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.LinkFailure => return error.LinkFailure, else => |e| return diags.fail("ELF flush failed: {s}", .{@errorName(e)}), }; } -fn flushModuleInner(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id) !void { +fn flushZcuInner(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id) !void { const comp = self.base.comp; const gpa = comp.gpa; const diags = &comp.link_diags; @@ -1523,8 +1507,12 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s // If there is no Zig code to compile, then we should skip flushing the output file because it // will not be part of the linker line anyway. - const module_obj_path: ?[]const u8 = if (comp.zcu != null) blk: { - try self.flushModule(arena, tid, prog_node); + const module_obj_path: ?[]const u8 = if (comp.zcu) |zcu| blk: { + if (zcu.llvm_object == null) { + try self.flushZcu(arena, tid, prog_node); + } else { + // `Compilation.flush` has already made LLVM emit this object file for us. + } if (fs.path.dirname(full_out_path)) |dirname| { break :blk try fs.path.join(arena, &.{ dirname, self.base.zcu_object_sub_path.? }); @@ -2385,7 +2373,6 @@ pub fn writeElfHeader(self: *Elf) !void { } pub fn freeNav(self: *Elf, nav: InternPool.Nav.Index) void { - if (self.llvm_object) |llvm_object| return llvm_object.freeNav(nav); return self.zigObjectPtr().?.freeNav(self, nav); } @@ -2399,7 +2386,6 @@ pub fn updateFunc( if (build_options.skip_non_native and builtin.object_format != .elf) { @panic("Attempted to compile for object format that was disabled by build configuration"); } - if (self.llvm_object) |llvm_object| return llvm_object.updateFunc(pt, func_index, air, liveness); return self.zigObjectPtr().?.updateFunc(self, pt, func_index, air, liveness); } @@ -2411,7 +2397,6 @@ pub fn updateNav( if (build_options.skip_non_native and builtin.object_format != .elf) { @panic("Attempted to compile for object format that was disabled by build configuration"); } - if (self.llvm_object) |llvm_object| return llvm_object.updateNav(pt, nav); return self.zigObjectPtr().?.updateNav(self, pt, nav); } @@ -2423,7 +2408,6 @@ pub fn updateContainerType( if (build_options.skip_non_native and builtin.object_format != .elf) { @panic("Attempted to compile for object format that was disabled by build configuration"); } - if (self.llvm_object) |_| return; const zcu = pt.zcu; const gpa = zcu.gpa; return self.zigObjectPtr().?.updateContainerType(pt, ty) catch |err| switch (err) { @@ -2449,12 +2433,10 @@ pub fn updateExports( if (build_options.skip_non_native and builtin.object_format != .elf) { @panic("Attempted to compile for object format that was disabled by build configuration"); } - if (self.llvm_object) |llvm_object| return llvm_object.updateExports(pt, exported, export_indices); return self.zigObjectPtr().?.updateExports(self, pt, exported, export_indices); } pub fn updateLineNumber(self: *Elf, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) !void { - if (self.llvm_object) |_| return; return self.zigObjectPtr().?.updateLineNumber(pt, ti_id); } @@ -2463,7 +2445,6 @@ pub fn deleteExport( exported: Zcu.Exported, name: InternPool.NullTerminatedString, ) void { - if (self.llvm_object) |_| return; return self.zigObjectPtr().?.deleteExport(self, exported, name); } @@ -5332,7 +5313,6 @@ const GotSection = synthetic_sections.GotSection; const GotPltSection = synthetic_sections.GotPltSection; const HashSection = synthetic_sections.HashSection; const LinkerDefined = @import("Elf/LinkerDefined.zig"); -const LlvmObject = @import("../codegen/llvm.zig").Object; const Zcu = @import("../Zcu.zig"); const Object = @import("Elf/Object.zig"); const InternPool = @import("../InternPool.zig"); diff --git a/src/link/Elf/ZigObject.zig b/src/link/Elf/ZigObject.zig index 13816940fe1550e2dfc8dacd988e333caf002856..49921089f7eba6978597bcaee28718322f054435 100644 --- a/src/link/Elf/ZigObject.zig +++ b/src/link/Elf/ZigObject.zig @@ -310,7 +310,7 @@ pub fn flush(self: *ZigObject, elf_file: *Elf, tid: Zcu.PerThread.Id) !void { if (self.dwarf) |*dwarf| { const pt: Zcu.PerThread = .activate(elf_file.base.comp.zcu.?, tid); defer pt.deactivate(); - try dwarf.flushModule(pt); + try dwarf.flushZcu(pt); const gpa = elf_file.base.comp.gpa; const cpu_arch = elf_file.getTarget().cpu.arch; @@ -481,7 +481,7 @@ pub fn flush(self: *ZigObject, elf_file: *Elf, tid: Zcu.PerThread.Id) !void { self.debug_str_section_dirty = false; } - // The point of flushModule() is to commit changes, so in theory, nothing should + // The point of flushZcu() is to commit changes, so in theory, nothing should // be dirty after this. However, it is possible for some things to remain // dirty because they fail to be written in the event of compile errors, // such as debug_line_header_dirty and debug_info_header_dirty. @@ -661,7 +661,7 @@ pub fn scanRelocs(self: *ZigObject, elf_file: *Elf, undefs: anytype) !void { if (shdr.sh_type == elf.SHT_NOBITS) continue; if (atom_ptr.scanRelocsRequiresCode(elf_file)) { // TODO ideally we don't have to fetch the code here. - // Perhaps it would make sense to save the code until flushModule where we + // Perhaps it would make sense to save the code until flushZcu where we // would free all of generated code? const code = try self.codeAlloc(elf_file, atom_index); defer gpa.free(code); @@ -1075,7 +1075,7 @@ pub fn getOrCreateMetadataForLazySymbol( } state_ptr.* = .pending_flush; const symbol_index = symbol_index_ptr.*; - // anyerror needs to be deferred until flushModule + // anyerror needs to be deferred until flushZcu if (lazy_sym.ty != .anyerror_type) try self.updateLazySymbol(elf_file, pt, lazy_sym, symbol_index); return symbol_index; } diff --git a/src/link/Goff.zig b/src/link/Goff.zig index 6ed360be258bc79437ea002bc3e0836aa8f66f98..35821289cd036b69753856ea521bb4fa12019ea0 100644 --- a/src/link/Goff.zig +++ b/src/link/Goff.zig @@ -17,10 +17,8 @@ const link = @import("../link.zig"); const trace = @import("../tracy.zig").trace; const build_options = @import("build_options"); const Air = @import("../Air.zig"); -const LlvmObject = @import("../codegen/llvm.zig").Object; base: link.File, -llvm_object: LlvmObject.Ptr, pub fn createEmpty( arena: Allocator, @@ -36,7 +34,6 @@ pub fn createEmpty( assert(!use_lld); // Caught by Compilation.Config.resolve. assert(target.os.tag == .zos); // Caught by Compilation.Config.resolve. - const llvm_object = try LlvmObject.create(arena, comp); const goff = try arena.create(Goff); goff.* = .{ .base = .{ @@ -52,7 +49,6 @@ pub fn createEmpty( .disable_lld_caching = options.disable_lld_caching, .build_id = options.build_id, }, - .llvm_object = llvm_object, }; return goff; @@ -70,7 +66,7 @@ pub fn open( } pub fn deinit(self: *Goff) void { - self.llvm_object.deinit(); + _ = self; } pub fn updateFunc( @@ -80,17 +76,19 @@ pub fn updateFunc( air: Air, liveness: Air.Liveness, ) link.File.UpdateNavError!void { - if (build_options.skip_non_native and builtin.object_format != .goff) - @panic("Attempted to compile for object format that was disabled by build configuration"); - - try self.llvm_object.updateFunc(pt, func_index, air, liveness); + _ = self; + _ = pt; + _ = func_index; + _ = air; + _ = liveness; + unreachable; // we always use llvm } pub fn updateNav(self: *Goff, pt: Zcu.PerThread, nav: InternPool.Nav.Index) link.File.UpdateNavError!void { - if (build_options.skip_non_native and builtin.object_format != .goff) - @panic("Attempted to compile for object format that was disabled by build configuration"); - - return self.llvm_object.updateNav(pt, nav); + _ = self; + _ = pt; + _ = nav; + unreachable; // we always use llvm } pub fn updateExports( @@ -99,21 +97,21 @@ pub fn updateExports( exported: Zcu.Exported, export_indices: []const Zcu.Export.Index, ) !void { - if (build_options.skip_non_native and builtin.object_format != .goff) - @panic("Attempted to compile for object format that was disabled by build configuration"); - - return self.llvm_object.updateExports(pt, exported, export_indices); + _ = self; + _ = pt; + _ = exported; + _ = export_indices; + unreachable; // we always use llvm } pub fn flush(self: *Goff, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void { - return self.flushModule(arena, tid, prog_node); + return self.flushZcu(arena, tid, prog_node); } -pub fn flushModule(self: *Goff, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void { - if (build_options.skip_non_native and builtin.object_format != .goff) - @panic("Attempted to compile for object format that was disabled by build configuration"); - +pub fn flushZcu(self: *Goff, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void { + _ = self; + _ = arena; _ = tid; - - try self.base.emitLlvmObject(arena, self.llvm_object, prog_node); + _ = prog_node; + unreachable; // we always use llvm } diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 3ddc12a5b0aad6c14441cb5679ee0cfa93831a16..6667ed6a635f5e03e5f6026e0358f2240fe44172 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -6,9 +6,6 @@ base: link.File, rpath_list: []const []const u8, -/// If this is not null, an object file is created by LLVM and emitted to zcu_object_sub_path. -llvm_object: ?LlvmObject.Ptr = null, - /// Debug symbols bundle (or dSym). d_sym: ?DebugSymbols = null, @@ -225,9 +222,6 @@ pub fn createEmpty( .force_load_objc = options.force_load_objc, .discard_local_symbols = options.discard_local_symbols, }; - if (use_llvm and comp.config.have_zcu) { - self.llvm_object = try LlvmObject.create(arena, comp); - } errdefer self.base.destroy(); self.base.file = try emit.root_dir.handle.createFile(emit.sub_path, .{ @@ -280,8 +274,6 @@ pub fn open( pub fn deinit(self: *MachO) void { const gpa = self.base.comp.gpa; - if (self.llvm_object) |llvm_object| llvm_object.deinit(); - if (self.d_sym) |*d_sym| { d_sym.deinit(); } @@ -350,10 +342,10 @@ pub fn flush( tid: Zcu.PerThread.Id, prog_node: std.Progress.Node, ) link.File.FlushError!void { - try self.flushModule(arena, tid, prog_node); + try self.flushZcu(arena, tid, prog_node); } -pub fn flushModule( +pub fn flushZcu( self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, @@ -366,10 +358,6 @@ pub fn flushModule( const gpa = comp.gpa; const diags = &self.base.comp.link_diags; - if (self.llvm_object) |llvm_object| { - try self.base.emitLlvmObject(arena, llvm_object, prog_node); - } - const sub_prog_node = prog_node.start("MachO Flush", 0); defer sub_prog_node.end(); @@ -385,7 +373,7 @@ pub fn flushModule( // --verbose-link if (comp.verbose_link) try self.dumpArgv(comp); - if (self.getZigObject()) |zo| try zo.flushModule(self, tid); + if (self.getZigObject()) |zo| try zo.flushZcu(self, tid); if (self.base.isStaticLib()) return relocatable.flushStaticLib(self, comp, module_obj_path); if (self.base.isObject()) return relocatable.flushObject(self, comp, module_obj_path); @@ -629,7 +617,7 @@ pub fn flushModule( error.LinkFailure => return error.LinkFailure, else => |e| return diags.fail("failed to calculate and write uuid: {s}", .{@errorName(e)}), }; - if (self.getDebugSymbols()) |dsym| dsym.flushModule(self) catch |err| switch (err) { + if (self.getDebugSymbols()) |dsym| dsym.flushZcu(self) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, else => |e| return diags.fail("failed to get debug symbols: {s}", .{@errorName(e)}), }; @@ -3079,7 +3067,6 @@ pub fn updateFunc( if (build_options.skip_non_native and builtin.object_format != .macho) { @panic("Attempted to compile for object format that was disabled by build configuration"); } - if (self.llvm_object) |llvm_object| return llvm_object.updateFunc(pt, func_index, air, liveness); return self.getZigObject().?.updateFunc(self, pt, func_index, air, liveness); } @@ -3087,12 +3074,10 @@ pub fn updateNav(self: *MachO, pt: Zcu.PerThread, nav: InternPool.Nav.Index) lin if (build_options.skip_non_native and builtin.object_format != .macho) { @panic("Attempted to compile for object format that was disabled by build configuration"); } - if (self.llvm_object) |llvm_object| return llvm_object.updateNav(pt, nav); return self.getZigObject().?.updateNav(self, pt, nav); } pub fn updateLineNumber(self: *MachO, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) !void { - if (self.llvm_object) |_| return; return self.getZigObject().?.updateLineNumber(pt, ti_id); } @@ -3105,7 +3090,6 @@ pub fn updateExports( if (build_options.skip_non_native and builtin.object_format != .macho) { @panic("Attempted to compile for object format that was disabled by build configuration"); } - if (self.llvm_object) |llvm_object| return llvm_object.updateExports(pt, exported, export_indices); return self.getZigObject().?.updateExports(self, pt, exported, export_indices); } @@ -3114,17 +3098,14 @@ pub fn deleteExport( exported: Zcu.Exported, name: InternPool.NullTerminatedString, ) void { - if (self.llvm_object) |_| return; return self.getZigObject().?.deleteExport(self, exported, name); } pub fn freeNav(self: *MachO, nav: InternPool.Nav.Index) void { - if (self.llvm_object) |llvm_object| return llvm_object.freeNav(nav); return self.getZigObject().?.freeNav(nav); } pub fn getNavVAddr(self: *MachO, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index, reloc_info: link.File.RelocInfo) !u64 { - assert(self.llvm_object == null); return self.getZigObject().?.getNavVAddr(self, pt, nav_index, reloc_info); } @@ -3139,7 +3120,6 @@ pub fn lowerUav( } pub fn getUavVAddr(self: *MachO, uav: InternPool.Index, reloc_info: link.File.RelocInfo) !u64 { - assert(self.llvm_object == null); return self.getZigObject().?.getUavVAddr(self, uav, reloc_info); } @@ -5496,7 +5476,6 @@ const ObjcStubsSection = synthetic.ObjcStubsSection; const Object = @import("MachO/Object.zig"); const LazyBind = bind.LazyBind; const LaSymbolPtrSection = synthetic.LaSymbolPtrSection; -const LlvmObject = @import("../codegen/llvm.zig").Object; const Md5 = std.crypto.hash.Md5; const Zcu = @import("../Zcu.zig"); const InternPool = @import("../InternPool.zig"); diff --git a/src/link/MachO/DebugSymbols.zig b/src/link/MachO/DebugSymbols.zig index 04b2fe92b06e82d432273929d49f2cc66a2dec7d..8579863d0343ab8105aae93a00cea38c6a94f110 100644 --- a/src/link/MachO/DebugSymbols.zig +++ b/src/link/MachO/DebugSymbols.zig @@ -178,7 +178,7 @@ fn findFreeSpace(self: *DebugSymbols, object_size: u64, min_alignment: u64) !u64 return offset; } -pub fn flushModule(self: *DebugSymbols, macho_file: *MachO) !void { +pub fn flushZcu(self: *DebugSymbols, macho_file: *MachO) !void { const zo = macho_file.getZigObject().?; for (self.relocs.items) |*reloc| { const sym = zo.symbols.items[reloc.target]; diff --git a/src/link/MachO/ZigObject.zig b/src/link/MachO/ZigObject.zig index a0de86654421f473de50c75a613ba41fa754cce7..4d99afc61a9d698a0e739ee9cb9478946b29961c 100644 --- a/src/link/MachO/ZigObject.zig +++ b/src/link/MachO/ZigObject.zig @@ -550,7 +550,7 @@ pub fn getInputSection(self: ZigObject, atom: Atom, macho_file: *MachO) macho.se return sect; } -pub fn flushModule(self: *ZigObject, macho_file: *MachO, tid: Zcu.PerThread.Id) link.File.FlushError!void { +pub fn flushZcu(self: *ZigObject, macho_file: *MachO, tid: Zcu.PerThread.Id) link.File.FlushError!void { const diags = &macho_file.base.comp.link_diags; // Handle any lazy symbols that were emitted by incremental compilation. @@ -589,7 +589,7 @@ pub fn flushModule(self: *ZigObject, macho_file: *MachO, tid: Zcu.PerThread.Id) if (self.dwarf) |*dwarf| { const pt: Zcu.PerThread = .activate(macho_file.base.comp.zcu.?, tid); defer pt.deactivate(); - dwarf.flushModule(pt) catch |err| switch (err) { + dwarf.flushZcu(pt) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, else => |e| return diags.fail("failed to flush dwarf module: {s}", .{@errorName(e)}), }; @@ -599,7 +599,7 @@ pub fn flushModule(self: *ZigObject, macho_file: *MachO, tid: Zcu.PerThread.Id) self.debug_strtab_dirty = false; } - // The point of flushModule() is to commit changes, so in theory, nothing should + // The point of flushZcu() is to commit changes, so in theory, nothing should // be dirty after this. However, it is possible for some things to remain // dirty because they fail to be written in the event of compile errors, // such as debug_line_header_dirty and debug_info_header_dirty. @@ -1537,7 +1537,7 @@ pub fn getOrCreateMetadataForLazySymbol( } state_ptr.* = .pending_flush; const symbol_index = symbol_index_ptr.*; - // anyerror needs to be deferred until flushModule + // anyerror needs to be deferred until flushZcu if (lazy_sym.ty != .anyerror_type) try self.updateLazySymbol(macho_file, pt, lazy_sym, symbol_index); return symbol_index; } diff --git a/src/link/Plan9.zig b/src/link/Plan9.zig index 5cbb9287d7b171957390389a19e75917d8490a24..0a940cb0b3455d25851196af1dfa442440dd6a1d 100644 --- a/src/link/Plan9.zig +++ b/src/link/Plan9.zig @@ -494,7 +494,7 @@ fn updateFinish(self: *Plan9, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index // write the symbol // we already have the got index const sym: aout.Sym = .{ - .value = undefined, // the value of stuff gets filled in in flushModule + .value = undefined, // the value of stuff gets filled in in flushZcu .type = atom.type, .name = try gpa.dupe(u8, nav.name.toSlice(ip)), }; @@ -543,7 +543,7 @@ pub fn flush( .Obj => return diags.fail("writing plan9 object files unimplemented", .{}), .Lib => return diags.fail("writing plan9 lib files unimplemented", .{}), } - return self.flushModule(arena, tid, prog_node); + return self.flushZcu(arena, tid, prog_node); } pub fn changeLine(l: *std.ArrayList(u8), delta_line: i32) !void { @@ -586,7 +586,7 @@ fn atomCount(self: *Plan9) usize { return data_nav_count + fn_nav_count + lazy_atom_count + extern_atom_count + uav_atom_count; } -pub fn flushModule( +pub fn flushZcu( self: *Plan9, arena: Allocator, /// TODO: stop using this @@ -610,7 +610,7 @@ pub fn flushModule( const sub_prog_node = prog_node.start("Flush Module", 0); defer sub_prog_node.end(); - log.debug("flushModule", .{}); + log.debug("flushZcu", .{}); defer assert(self.hdr.entry != 0x0); @@ -1039,7 +1039,7 @@ pub fn getOrCreateAtomForLazySymbol(self: *Plan9, pt: Zcu.PerThread, lazy_sym: F const atom = atom_ptr.*; _ = try self.getAtomPtr(atom).getOrCreateSymbolTableEntry(self); _ = self.getAtomPtr(atom).getOrCreateOffsetTableEntry(self); - // anyerror needs to be deferred until flushModule + // anyerror needs to be deferred until flushZcu if (lazy_sym.ty != .anyerror_type) try self.updateLazySymbolAtom(pt, lazy_sym, atom); return atom; } diff --git a/src/link/SpirV.zig b/src/link/SpirV.zig index a49771c3e2f24ae6a3843ffc706253f2f2543ea4..c6e86895f5601280c3b0ef0bfbcd5061f4c8d9a7 100644 --- a/src/link/SpirV.zig +++ b/src/link/SpirV.zig @@ -17,7 +17,7 @@ //! All regular functions. // Because SPIR-V requires re-compilation anyway, and so hot swapping will not work -// anyway, we simply generate all the code in flushModule. This keeps +// anyway, we simply generate all the code in flushZcu. This keeps // things considerably simpler. const SpirV = @This(); @@ -194,17 +194,17 @@ pub fn updateExports( } pub fn flush(self: *SpirV, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void { - return self.flushModule(arena, tid, prog_node); + return self.flushZcu(arena, tid, prog_node); } -pub fn flushModule( +pub fn flushZcu( self: *SpirV, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node, ) link.File.FlushError!void { // The goal is to never use this because it's only needed if we need to - // write to InternPool, but flushModule is too late to be writing to the + // write to InternPool, but flushZcu is too late to be writing to the // InternPool. _ = tid; diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 69684724a5b411894721c6a521615e6077385556..e92be32b7d49c3d3f617d8825a02731e6842a941 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -36,7 +36,6 @@ const abi = @import("../arch/wasm/abi.zig"); const Compilation = @import("../Compilation.zig"); const Dwarf = @import("Dwarf.zig"); const InternPool = @import("../InternPool.zig"); -const LlvmObject = @import("../codegen/llvm.zig").Object; const Zcu = @import("../Zcu.zig"); const codegen = @import("../codegen.zig"); const dev = @import("../dev.zig"); @@ -81,8 +80,6 @@ import_table: bool, export_table: bool, /// Output name of the file name: []const u8, -/// If this is not null, an object file is created by LLVM and linked with LLD afterwards. -llvm_object: ?LlvmObject.Ptr = null, /// List of relocatable files to be linked into the final binary. objects: std.ArrayListUnmanaged(Object) = .{}, @@ -2992,9 +2989,6 @@ pub fn createEmpty( .object_host_name = .none, .preloaded_strings = undefined, }; - if (use_llvm and comp.config.have_zcu) { - wasm.llvm_object = try LlvmObject.create(arena, comp); - } errdefer wasm.base.destroy(); if (options.object_host_name) |name| wasm.object_host_name = (try wasm.internString(name)).toOptional(); @@ -3116,7 +3110,6 @@ fn parseArchive(wasm: *Wasm, obj: link.Input.Object) !void { pub fn deinit(wasm: *Wasm) void { const gpa = wasm.base.comp.gpa; - if (wasm.llvm_object) |llvm_object| llvm_object.deinit(); wasm.navs_exe.deinit(gpa); wasm.navs_obj.deinit(gpa); @@ -3196,7 +3189,6 @@ pub fn updateFunc(wasm: *Wasm, pt: Zcu.PerThread, func_index: InternPool.Index, if (build_options.skip_non_native and builtin.object_format != .wasm) { @panic("Attempted to compile for object format that was disabled by build configuration"); } - if (wasm.llvm_object) |llvm_object| return llvm_object.updateFunc(pt, func_index, air, liveness); dev.check(.wasm_backend); @@ -3228,7 +3220,6 @@ pub fn updateNav(wasm: *Wasm, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index if (build_options.skip_non_native and builtin.object_format != .wasm) { @panic("Attempted to compile for object format that was disabled by build configuration"); } - if (wasm.llvm_object) |llvm_object| return llvm_object.updateNav(pt, nav_index); const zcu = pt.zcu; const ip = &zcu.intern_pool; const nav = ip.getNav(nav_index); @@ -3308,8 +3299,6 @@ pub fn deleteExport( exported: Zcu.Exported, name: InternPool.NullTerminatedString, ) void { - if (wasm.llvm_object != null) return; - const zcu = wasm.base.comp.zcu.?; const ip = &zcu.intern_pool; const name_slice = name.toSlice(ip); @@ -3332,7 +3321,6 @@ pub fn updateExports( if (build_options.skip_non_native and builtin.object_format != .wasm) { @panic("Attempted to compile for object format that was disabled by build configuration"); } - if (wasm.llvm_object) |llvm_object| return llvm_object.updateExports(pt, exported, export_indices); const zcu = pt.zcu; const gpa = zcu.gpa; @@ -3391,7 +3379,7 @@ pub fn flush(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: st else => |e| return diags.fail("failed to link with LLD: {s}", .{@errorName(e)}), }; } - return wasm.flushModule(arena, tid, prog_node); + return wasm.flushZcu(arena, tid, prog_node); } pub fn prelink(wasm: *Wasm, prog_node: std.Progress.Node) link.File.FlushError!void { @@ -3785,26 +3773,20 @@ fn markTable(wasm: *Wasm, i: ObjectTableIndex) link.File.FlushError!void { try wasm.tables.put(wasm.base.comp.gpa, .fromObjectTable(i), {}); } -pub fn flushModule( +pub fn flushZcu( wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node, ) link.File.FlushError!void { // The goal is to never use this because it's only needed if we need to - // write to InternPool, but flushModule is too late to be writing to the + // write to InternPool, but flushZcu is too late to be writing to the // InternPool. _ = tid; const comp = wasm.base.comp; - const use_lld = build_options.have_llvm and comp.config.use_lld; const diags = &comp.link_diags; const gpa = comp.gpa; - if (wasm.llvm_object) |llvm_object| { - try wasm.base.emitLlvmObject(arena, llvm_object, prog_node); - if (use_lld) return; - } - if (comp.verbose_link) Compilation.dump_argv(wasm.dump_argv_list.items); if (wasm.base.zcu_object_sub_path) |path| { @@ -3870,8 +3852,12 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: // If there is no Zig code to compile, then we should skip flushing the output file because it // will not be part of the linker line anyway. - const module_obj_path: ?[]const u8 = if (comp.zcu != null) blk: { - try wasm.flushModule(arena, tid, prog_node); + const module_obj_path: ?[]const u8 = if (comp.zcu) |zcu| blk: { + if (zcu.llvm_object == null) { + try wasm.flushZcu(arena, tid, prog_node); + } else { + // `Compilation.flush` has already made LLVM emit this object file for us. + } if (fs.path.dirname(full_out_path)) |dirname| { break :blk try fs.path.join(arena, &.{ dirname, wasm.base.zcu_object_sub_path.? }); diff --git a/src/link/Xcoff.zig b/src/link/Xcoff.zig index 525d99d39165ba6de963c40302b5d57ac896d4f5..e2f81e015e016e3688c7576779f76d2a7c171bf3 100644 --- a/src/link/Xcoff.zig +++ b/src/link/Xcoff.zig @@ -17,10 +17,8 @@ const link = @import("../link.zig"); const trace = @import("../tracy.zig").trace; const build_options = @import("build_options"); const Air = @import("../Air.zig"); -const LlvmObject = @import("../codegen/llvm.zig").Object; base: link.File, -llvm_object: LlvmObject.Ptr, pub fn createEmpty( arena: Allocator, @@ -36,7 +34,6 @@ pub fn createEmpty( assert(!use_lld); // Caught by Compilation.Config.resolve. assert(target.os.tag == .aix); // Caught by Compilation.Config.resolve. - const llvm_object = try LlvmObject.create(arena, comp); const xcoff = try arena.create(Xcoff); xcoff.* = .{ .base = .{ @@ -52,7 +49,6 @@ pub fn createEmpty( .disable_lld_caching = options.disable_lld_caching, .build_id = options.build_id, }, - .llvm_object = llvm_object, }; return xcoff; @@ -70,7 +66,7 @@ pub fn open( } pub fn deinit(self: *Xcoff) void { - self.llvm_object.deinit(); + _ = self; } pub fn updateFunc( @@ -80,17 +76,19 @@ pub fn updateFunc( air: Air, liveness: Air.Liveness, ) link.File.UpdateNavError!void { - if (build_options.skip_non_native and builtin.object_format != .xcoff) - @panic("Attempted to compile for object format that was disabled by build configuration"); - - try self.llvm_object.updateFunc(pt, func_index, air, liveness); + _ = self; + _ = pt; + _ = func_index; + _ = air; + _ = liveness; + unreachable; // we always use llvm } pub fn updateNav(self: *Xcoff, pt: Zcu.PerThread, nav: InternPool.Nav.Index) link.File.UpdateNavError!void { - if (build_options.skip_non_native and builtin.object_format != .xcoff) - @panic("Attempted to compile for object format that was disabled by build configuration"); - - return self.llvm_object.updateNav(pt, nav); + _ = self; + _ = pt; + _ = nav; + unreachable; // we always use llvm } pub fn updateExports( @@ -99,21 +97,21 @@ pub fn updateExports( exported: Zcu.Exported, export_indices: []const Zcu.Export.Index, ) !void { - if (build_options.skip_non_native and builtin.object_format != .xcoff) - @panic("Attempted to compile for object format that was disabled by build configuration"); - - return self.llvm_object.updateExports(pt, exported, export_indices); + _ = self; + _ = pt; + _ = exported; + _ = export_indices; + unreachable; // we always use llvm } pub fn flush(self: *Xcoff, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void { - return self.flushModule(arena, tid, prog_node); + return self.flushZcu(arena, tid, prog_node); } -pub fn flushModule(self: *Xcoff, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void { - if (build_options.skip_non_native and builtin.object_format != .xcoff) - @panic("Attempted to compile for object format that was disabled by build configuration"); - +pub fn flushZcu(self: *Xcoff, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void { + _ = self; + _ = arena; _ = tid; - - try self.base.emitLlvmObject(arena, self.llvm_object, prog_node); + _ = prog_node; + unreachable; // we always use llvm } diff --git a/src/target.zig b/src/target.zig index 247b783439fbe451518476e4733934195f89ca53..6172b5e7e9e84b6ebb2f7ae0ebe8cf8e345d35db 100644 --- a/src/target.zig +++ b/src/target.zig @@ -739,7 +739,7 @@ pub fn functionPointerMask(target: std.Target) ?u64 { pub fn supportsTailCall(target: std.Target, backend: std.builtin.CompilerBackend) bool { switch (backend) { - .stage1, .stage2_llvm => return @import("codegen/llvm.zig").supportsTailCall(target), + .stage2_llvm => return @import("codegen/llvm.zig").supportsTailCall(target), .stage2_c => return true, else => return false, } -- 2.54.0