From 424e6ac54b0f8bbfb43f24e28c71ac72169f3719 Mon Sep 17 00:00:00 2001 From: mlugg Date: Wed, 28 May 2025 00:31:16 +0100 Subject: [PATCH 01/35] compiler: minor refactors to ZCU linking * The `codegen_nav`, `codegen_func`, `codegen_type` tasks are renamed to `link_nav`, `link_func`, and `link_type`, to more accurately reflect their purpose of sending data to the *linker*. Currently, `link_func` remains responsible for codegen; this will change in an upcoming commit. * Don't go on a pointless detour through `PerThread` when linking ZCU functions/`Nav`s; so, the `linkerUpdateNav` etc logic now lives in `link.zig`. Currently, `linkerUpdateFunc` is an exception, because it has broader responsibilities including codegen, but this will be solved in an upcoming commit. --- src/Compilation.zig | 29 +++++------ src/Sema.zig | 16 +++--- src/Sema/LowerZon.zig | 2 +- src/Zcu/PerThread.zig | 81 +++--------------------------- src/link.zig | 114 +++++++++++++++++++++++++++++------------- 5 files changed, 111 insertions(+), 131 deletions(-) diff --git a/src/Compilation.zig b/src/Compilation.zig index 81d150b03fea06996f2d306bb87162f287754715..69cb2c4d6f0167901b64dd120d95e01a2fd83953 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -848,17 +848,18 @@ pub const RcIncludes = enum { const Job = union(enum) { /// Corresponds to the task in `link.Task`. /// Only needed for backends that haven't yet been updated to not race against Sema. - codegen_nav: InternPool.Nav.Index, + link_nav: InternPool.Nav.Index, /// Corresponds to the task in `link.Task`. + /// TODO: this is currently also responsible for performing codegen. /// Only needed for backends that haven't yet been updated to not race against Sema. - codegen_func: link.Task.CodegenFunc, + link_func: link.Task.CodegenFunc, /// Corresponds to the task in `link.Task`. /// Only needed for backends that haven't yet been updated to not race against Sema. - codegen_type: InternPool.Index, + link_type: InternPool.Index, update_line_number: InternPool.TrackedInst.Index, /// The `AnalUnit`, which is *not* a `func`, must be semantically analyzed. /// This may be its first time being analyzed, or it may be outdated. - /// If the unit is a function, a `codegen_func` job will then be queued. + /// If the unit is a test function, an `analyze_func` job will then be queued. analyze_comptime_unit: InternPool.AnalUnit, /// This function must be semantically analyzed. /// This may be its first time being analyzed, or it may be outdated. @@ -879,13 +880,13 @@ const Job = union(enum) { return switch (tag) { // Prioritize functions so that codegen can get to work on them on a // separate thread, while Sema goes back to its own work. - .resolve_type_fully, .analyze_func, .codegen_func => 0, + .resolve_type_fully, .analyze_func, .link_func => 0, else => 1, }; } comptime { // Job dependencies - assert(stage(.resolve_type_fully) <= stage(.codegen_func)); + assert(stage(.resolve_type_fully) <= stage(.link_func)); } }; @@ -4552,7 +4553,7 @@ pub fn queueJobs(comp: *Compilation, jobs: []const Job) !void { fn processOneJob(tid: usize, comp: *Compilation, job: Job) JobError!void { switch (job) { - .codegen_nav => |nav_index| { + .link_nav => |nav_index| { const zcu = comp.zcu.?; const nav = zcu.intern_pool.getNav(nav_index); if (nav.analysis != null) { @@ -4562,16 +4563,16 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job) JobError!void { } } assert(nav.status == .fully_resolved); - comp.dispatchCodegenTask(tid, .{ .codegen_nav = nav_index }); + comp.dispatchLinkTask(tid, .{ .link_nav = nav_index }); }, - .codegen_func => |func| { - comp.dispatchCodegenTask(tid, .{ .codegen_func = func }); + .link_func => |func| { + comp.dispatchLinkTask(tid, .{ .link_func = func }); }, - .codegen_type => |ty| { - comp.dispatchCodegenTask(tid, .{ .codegen_type = ty }); + .link_type => |ty| { + comp.dispatchLinkTask(tid, .{ .link_type = ty }); }, .update_line_number => |ti| { - comp.dispatchCodegenTask(tid, .{ .update_line_number = ti }); + comp.dispatchLinkTask(tid, .{ .update_line_number = ti }); }, .analyze_func => |func| { const named_frame = tracy.namedFrame("analyze_func"); @@ -4665,7 +4666,7 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job) JobError!void { /// The reason for the double-queue here is that the first queue ensures any /// resolve_type_fully tasks are complete before this dispatch function is called. -fn dispatchCodegenTask(comp: *Compilation, tid: usize, link_task: link.Task) void { +fn dispatchLinkTask(comp: *Compilation, tid: usize, link_task: link.Task) void { if (comp.separateCodegenThreadOk()) { comp.queueLinkTasks(&.{link_task}); } else { diff --git a/src/Sema.zig b/src/Sema.zig index e20fb17f2626ff050641dd670ccda730abc3db15..c9f307e6244561dc003312ceb2fb522eb8b6af6d 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -2991,7 +2991,7 @@ fn zirStructDecl( if (zcu.comp.config.use_llvm) break :codegen_type; if (block.ownerModule().strip) break :codegen_type; // This job depends on any resolve_type_fully jobs queued up before it. - try zcu.comp.queueJob(.{ .codegen_type = wip_ty.index }); + try zcu.comp.queueJob(.{ .link_type = wip_ty.index }); } try sema.declareDependency(.{ .interned = wip_ty.index }); try sema.addTypeReferenceEntry(src, wip_ty.index); @@ -3250,7 +3250,7 @@ fn zirEnumDecl( if (zcu.comp.config.use_llvm) break :codegen_type; if (block.ownerModule().strip) break :codegen_type; // This job depends on any resolve_type_fully jobs queued up before it. - try zcu.comp.queueJob(.{ .codegen_type = wip_ty.index }); + try zcu.comp.queueJob(.{ .link_type = wip_ty.index }); } return Air.internedToRef(wip_ty.index); } @@ -3368,7 +3368,7 @@ fn zirUnionDecl( if (zcu.comp.config.use_llvm) break :codegen_type; if (block.ownerModule().strip) break :codegen_type; // This job depends on any resolve_type_fully jobs queued up before it. - try zcu.comp.queueJob(.{ .codegen_type = wip_ty.index }); + try zcu.comp.queueJob(.{ .link_type = wip_ty.index }); } try sema.declareDependency(.{ .interned = wip_ty.index }); try sema.addTypeReferenceEntry(src, wip_ty.index); @@ -3455,7 +3455,7 @@ fn zirOpaqueDecl( if (zcu.comp.config.use_llvm) break :codegen_type; if (block.ownerModule().strip) break :codegen_type; // This job depends on any resolve_type_fully jobs queued up before it. - try zcu.comp.queueJob(.{ .codegen_type = wip_ty.index }); + try zcu.comp.queueJob(.{ .link_type = wip_ty.index }); } try sema.addTypeReferenceEntry(src, wip_ty.index); if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index); @@ -20086,7 +20086,7 @@ fn structInitAnon( codegen_type: { if (zcu.comp.config.use_llvm) break :codegen_type; if (block.ownerModule().strip) break :codegen_type; - try zcu.comp.queueJob(.{ .codegen_type = wip.index }); + try zcu.comp.queueJob(.{ .link_type = wip.index }); } if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip.index); break :ty wip.finish(ip, new_namespace_index); @@ -21396,7 +21396,7 @@ fn reifyEnum( if (zcu.comp.config.use_llvm) break :codegen_type; if (block.ownerModule().strip) break :codegen_type; // This job depends on any resolve_type_fully jobs queued up before it. - try zcu.comp.queueJob(.{ .codegen_type = wip_ty.index }); + try zcu.comp.queueJob(.{ .link_type = wip_ty.index }); } return Air.internedToRef(wip_ty.index); } @@ -21650,7 +21650,7 @@ fn reifyUnion( if (zcu.comp.config.use_llvm) break :codegen_type; if (block.ownerModule().strip) break :codegen_type; // This job depends on any resolve_type_fully jobs queued up before it. - try zcu.comp.queueJob(.{ .codegen_type = wip_ty.index }); + try zcu.comp.queueJob(.{ .link_type = wip_ty.index }); } try sema.declareDependency(.{ .interned = wip_ty.index }); try sema.addTypeReferenceEntry(src, wip_ty.index); @@ -22004,7 +22004,7 @@ fn reifyStruct( if (zcu.comp.config.use_llvm) break :codegen_type; if (block.ownerModule().strip) break :codegen_type; // This job depends on any resolve_type_fully jobs queued up before it. - try zcu.comp.queueJob(.{ .codegen_type = wip_ty.index }); + try zcu.comp.queueJob(.{ .link_type = wip_ty.index }); } try sema.declareDependency(.{ .interned = wip_ty.index }); try sema.addTypeReferenceEntry(src, wip_ty.index); diff --git a/src/Sema/LowerZon.zig b/src/Sema/LowerZon.zig index 77065a07e8ec1a0dc62c20bc3dad2fc6f389426d..192c2e2d564d2b8ade6604656a184db433a5afc2 100644 --- a/src/Sema/LowerZon.zig +++ b/src/Sema/LowerZon.zig @@ -194,7 +194,7 @@ fn lowerExprAnonResTy(self: *LowerZon, node: Zoir.Node.Index) CompileError!Inter codegen_type: { if (pt.zcu.comp.config.use_llvm) break :codegen_type; if (self.block.ownerModule().strip) break :codegen_type; - try pt.zcu.comp.queueJob(.{ .codegen_type = wip.index }); + try pt.zcu.comp.queueJob(.{ .link_type = wip.index }); } break :ty wip.finish(ip, new_namespace_index); }, diff --git a/src/Zcu/PerThread.zig b/src/Zcu/PerThread.zig index 8b35d8d7999f82ef810aa4fd9a153ea1c9225e50..4b4ae98cb4b41348e93841a05cc6f46e09dcc3c8 100644 --- a/src/Zcu/PerThread.zig +++ b/src/Zcu/PerThread.zig @@ -1320,7 +1320,7 @@ fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileErr } // This job depends on any resolve_type_fully jobs queued up before it. - try zcu.comp.queueJob(.{ .codegen_nav = nav_id }); + try zcu.comp.queueJob(.{ .link_nav = nav_id }); } switch (old_nav.status) { @@ -1716,7 +1716,7 @@ fn analyzeFuncBody( } // This job depends on any resolve_type_fully jobs queued up before it. - try comp.queueJob(.{ .codegen_func = .{ + try comp.queueJob(.{ .link_func = .{ .func = func_index, .air = air, } }); @@ -1880,7 +1880,7 @@ fn createFileRootStruct( 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(.{ .codegen_type = wip_ty.index }); + try zcu.comp.queueJob(.{ .link_type = wip_ty.index }); } zcu.setFileRootType(file_index, wip_ty.index); if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index); @@ -3457,73 +3457,8 @@ pub fn populateTestFunctions( zcu.codegen_prog_node = std.Progress.Node.none; } - try pt.linkerUpdateNav(nav_index); - } -} - -pub fn linkerUpdateNav(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) error{OutOfMemory}!void { - const zcu = pt.zcu; - const comp = zcu.comp; - const gpa = zcu.gpa; - const ip = &zcu.intern_pool; - - const nav = zcu.intern_pool.getNav(nav_index); - const codegen_prog_node = zcu.codegen_prog_node.start(nav.fqn.toSlice(ip), 0); - defer codegen_prog_node.end(); - - if (!Air.valFullyResolved(zcu.navValue(nav_index), zcu)) { - // The value of this nav failed to resolve. This is a transitive failure. - // TODO: do we need to mark this failure anywhere? I don't think so, since compilation - // will fail due to the type error anyway. - } else if (comp.bin_file) |lf| { - lf.updateNav(pt, nav_index) catch |err| switch (err) { - error.OutOfMemory => return error.OutOfMemory, - error.CodegenFail => assert(zcu.failed_codegen.contains(nav_index)), - error.Overflow, error.RelocationNotByteAligned => { - try zcu.failed_codegen.putNoClobber(gpa, nav_index, try Zcu.ErrorMsg.create( - gpa, - zcu.navSrcLoc(nav_index), - "unable to codegen: {s}", - .{@errorName(err)}, - )); - // Not a retryable failure. - }, - }; - } else if (zcu.llvm_object) |llvm_object| { - llvm_object.updateNav(pt, nav_index) catch |err| switch (err) { - error.OutOfMemory => return error.OutOfMemory, - }; - } -} - -pub fn linkerUpdateContainerType(pt: Zcu.PerThread, ty: InternPool.Index) error{OutOfMemory}!void { - const zcu = pt.zcu; - const gpa = zcu.gpa; - const comp = zcu.comp; - const ip = &zcu.intern_pool; - - const codegen_prog_node = zcu.codegen_prog_node.start(Type.fromInterned(ty).containerTypeName(ip).toSlice(ip), 0); - defer codegen_prog_node.end(); - - if (zcu.failed_types.fetchSwapRemove(ty)) |*entry| entry.value.deinit(gpa); - - if (!Air.typeFullyResolved(Type.fromInterned(ty), zcu)) { - // This type failed to resolve. This is a transitive failure. - return; - } - - if (comp.bin_file) |lf| lf.updateContainerType(pt, ty) catch |err| switch (err) { - error.OutOfMemory => return error.OutOfMemory, - error.TypeFailureReported => assert(zcu.failed_types.contains(ty)), - }; -} - -pub fn linkerUpdateLineNumber(pt: Zcu.PerThread, ti: InternPool.TrackedInst.Index) !void { - if (pt.zcu.comp.bin_file) |lf| { - lf.updateLineNumber(pt, ti) catch |err| switch (err) { - error.OutOfMemory => return error.OutOfMemory, - else => |e| log.err("update line number failed: {s}", .{@errorName(e)}), - }; + // The linker thread is not running, so we actually need to dispatch this task directly. + @import("../link.zig").doTask(zcu.comp, @intFromEnum(pt.tid), .{ .link_nav = nav_index }); } } @@ -3984,7 +3919,7 @@ pub fn getExtern(pt: Zcu.PerThread, key: InternPool.Key.Extern) Allocator.Error! const result = try pt.zcu.intern_pool.getExtern(pt.zcu.gpa, pt.tid, key); if (result.new_nav.unwrap()) |nav| { // This job depends on any resolve_type_fully jobs queued up before it. - try pt.zcu.comp.queueJob(.{ .codegen_nav = nav }); + try pt.zcu.comp.queueJob(.{ .link_nav = nav }); if (pt.zcu.comp.debugIncremental()) try pt.zcu.incremental_debug_state.newNav(pt.zcu, nav); } return result.index; @@ -4132,7 +4067,7 @@ fn recreateStructType( 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(.{ .codegen_type = wip_ty.index }); + try zcu.comp.queueJob(.{ .link_type = wip_ty.index }); } if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index); @@ -4225,7 +4160,7 @@ fn recreateUnionType( 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(.{ .codegen_type = wip_ty.index }); + try zcu.comp.queueJob(.{ .link_type = wip_ty.index }); } if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index); diff --git a/src/link.zig b/src/link.zig index 688210c3556ddbaae0c5b489df87aa0abcb7f6ef..7673b44e47ba317ab5538794a090be88b31b89f3 100644 --- a/src/link.zig +++ b/src/link.zig @@ -704,7 +704,7 @@ pub const File = struct { } /// May be called before or after updateExports for any given Nav. - pub fn updateNav(base: *File, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) UpdateNavError!void { + fn updateNav(base: *File, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) UpdateNavError!void { const nav = pt.zcu.intern_pool.getNav(nav_index); assert(nav.status == .fully_resolved); switch (base.tag) { @@ -721,7 +721,7 @@ pub const File = struct { TypeFailureReported, }; - pub fn updateContainerType(base: *File, pt: Zcu.PerThread, ty: InternPool.Index) UpdateContainerTypeError!void { + fn updateContainerType(base: *File, pt: Zcu.PerThread, ty: InternPool.Index) UpdateContainerTypeError!void { switch (base.tag) { else => {}, inline .elf => |tag| { @@ -732,6 +732,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. pub fn updateFunc( base: *File, pt: Zcu.PerThread, @@ -755,7 +756,7 @@ 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`. - pub fn updateLineNumber(base: *File, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) UpdateLineNumberError!void { + fn updateLineNumber(base: *File, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) UpdateLineNumberError!void { { const ti = ti_id.resolveFull(&pt.zcu.intern_pool).?; const file = pt.zcu.fileByIndex(ti.file); @@ -1435,10 +1436,10 @@ pub const Task = union(enum) { load_input: Input, /// Write the constant value for a Decl to the output file. - codegen_nav: InternPool.Nav.Index, + link_nav: InternPool.Nav.Index, /// Write the machine code for a function to the output file. - codegen_func: CodegenFunc, - codegen_type: InternPool.Index, + link_func: CodegenFunc, + link_type: InternPool.Index, update_line_number: InternPool.TrackedInst.Index, @@ -1585,48 +1586,91 @@ pub fn doTask(comp: *Compilation, tid: usize, task: Task) void { }, }; }, - .codegen_nav => |nav_index| { - if (comp.remaining_prelink_tasks == 0) { - const pt: Zcu.PerThread = .activate(comp.zcu.?, @enumFromInt(tid)); - defer pt.deactivate(); - pt.linkerUpdateNav(nav_index) catch |err| switch (err) { - error.OutOfMemory => diags.setAllocFailure(), - }; - } else { + .link_nav => |nav_index| { + if (comp.remaining_prelink_tasks != 0) { comp.link_task_queue_postponed.appendAssumeCapacity(task); + return; } - }, - .codegen_func => |func| { - if (comp.remaining_prelink_tasks == 0) { - const pt: Zcu.PerThread = .activate(comp.zcu.?, @enumFromInt(tid)); - defer pt.deactivate(); - var air = func.air; - defer air.deinit(comp.gpa); - pt.linkerUpdateFunc(func.func, &air) catch |err| switch (err) { + const zcu = comp.zcu.?; + const pt: Zcu.PerThread = .activate(zcu, @enumFromInt(tid)); + defer pt.deactivate(); + if (!Air.valFullyResolved(zcu.navValue(nav_index), zcu)) { + // Type resolution failed in a way which affects this `Nav`. This is a transitive + // failure, but it doesn't need recording, because this `Nav` semantically depends + // on the failed type, so when it is changed the `Nav` will be updated. + return; + } + 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)), + error.Overflow, error.RelocationNotByteAligned => { + zcu.failed_codegen.ensureUnusedCapacity(zcu.gpa, 1) catch return diags.setAllocFailure(); + const msg = Zcu.ErrorMsg.create( + zcu.gpa, + zcu.navSrcLoc(nav_index), + "unable to codegen: {s}", + .{@errorName(err)}, + ) catch return diags.setAllocFailure(); + zcu.failed_codegen.putAssumeCapacityNoClobber(nav_index, msg); + // Not a retryable failure. + }, }; - } else { - comp.link_task_queue_postponed.appendAssumeCapacity(task); - } - }, - .codegen_type => |ty| { - if (comp.remaining_prelink_tasks == 0) { - const pt: Zcu.PerThread = .activate(comp.zcu.?, @enumFromInt(tid)); - defer pt.deactivate(); - pt.linkerUpdateContainerType(ty) catch |err| switch (err) { + } else if (zcu.llvm_object) |llvm_object| { + llvm_object.updateNav(pt, nav_index) catch |err| switch (err) { error.OutOfMemory => diags.setAllocFailure(), }; - } else { - comp.link_task_queue_postponed.appendAssumeCapacity(task); } }, - .update_line_number => |ti| { + .link_func => |func| { + if (comp.remaining_prelink_tasks != 0) { + comp.link_task_queue_postponed.appendAssumeCapacity(task); + return; + } const pt: Zcu.PerThread = .activate(comp.zcu.?, @enumFromInt(tid)); defer pt.deactivate(); - pt.linkerUpdateLineNumber(ti) catch |err| switch (err) { + var air = func.air; + defer air.deinit(comp.gpa); + pt.linkerUpdateFunc(func.func, &air) catch |err| switch (err) { error.OutOfMemory => diags.setAllocFailure(), }; }, + .link_type => |ty| { + if (comp.remaining_prelink_tasks != 0) { + comp.link_task_queue_postponed.appendAssumeCapacity(task); + return; + } + const zcu = comp.zcu.?; + const pt: Zcu.PerThread = .activate(zcu, @enumFromInt(tid)); + defer pt.deactivate(); + if (zcu.failed_types.fetchSwapRemove(ty)) |*entry| entry.value.deinit(zcu.gpa); + if (!Air.typeFullyResolved(.fromInterned(ty), zcu)) { + // Type resolution failed in a way which affects this type. This is a transitive + // failure, but it doesn't need recording, because this type semantically depends + // 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)), + }; + } + }, + .update_line_number => |ti| { + if (comp.remaining_prelink_tasks != 0) { + comp.link_task_queue_postponed.appendAssumeCapacity(task); + return; + } + 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)}), + }; + } + }, } } -- 2.54.0 From 3743c3e39c6bb645db7403fd446953d43ac7c7dc Mon Sep 17 00:00:00 2001 From: mlugg Date: Wed, 28 May 2025 06:36:47 +0100 Subject: [PATCH 02/35] 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 From 2fb6f5c1adcd764372ad28ed4014fdaf558da778 Mon Sep 17 00:00:00 2001 From: mlugg Date: Wed, 28 May 2025 09:30:31 +0100 Subject: [PATCH 03/35] link: divorce LLD from the self-hosted linkers Similar to the previous commit, this commit untangles LLD integration from the self-hosted linkers. Despite the big network of functions which were involved, it turns out what was going on here is quite simple. The LLD linking logic is actually very self-contained; it requires a few flags from the `link.File.OpenOptions`, but that's really about it. We don't need any of the mutable state on `Elf`/`Coff`/`Wasm`, for instance. There was some legacy code trying to handle support for using self-hosted codegen with LLD, but that's not a supported use case, so I've just stripped it out. For now, I've just pasted the logic for linking the 3 targets we currently support using LLD for into this new linker implementation, `link.Lld`; however, it's almost certainly possible to combine some of the logic and simplify this file a bit. But to be honest, it's not actually that bad right now. This commit ends up eliminating the distinction between `flush` and `flushZcu` (formerly `flushModule`) in linkers, where the latter previously meant something along the lines of "flush, but if you're going to be linking with LLD, just flush the ZCU object file, don't actually link"?. The distinction here doesn't seem like it was properly defined, and most linkers seem to treat them as essentially identical anyway. Regardless, all calls to `flushZcu` are gone now, so it's deleted -- one `flush` to rule them all! The end result of this commit and the preceding one is that LLVM and LLD fit into the pipeline much more sanely: * If we're using LLVM for the ZCU, that state is on `zcu.llvm_object` * If we're using LLD to link, then the `link.File` is a `link.Lld` * Calls to "ZCU link functions" (e.g. `updateNav`) lower to calls to the LLVM object if it's available, or otherwise to the `link.File` if it's available (neither is available under `-fno-emit-bin`) * After everything is done, linking is finalized by calling `flush` on the `link.File`; for `link.Lld` this invokes LLD, for other linkers it flushes self-hosted linker state There's one messy thing remaining, and that's how self-hosted function codegen in a ZCU works; right now, we process AIR with a call sequence something like this: * `link.doTask` * `Zcu.PerThread.linkerUpdateFunc` * `link.File.updateFunc` * `link.Elf.updateFunc` * `link.Elf.ZigObject.updateFunc` * `codegen.generateFunction` * `arch.x86_64.CodeGen.generate` So, we start in the linker, take a scenic detour through `Zcu`, go back to the linker, into its implementation, and then... right back out, into code which is generic over the linker implementation, and then dispatch on the *backend* instead! Of course, within `arch.x86_64.CodeGen`, there are some more places which switch on the `link` implementation being used. This is all pretty silly... so it shall be my next target. --- src/Compilation.zig | 6 +- src/codegen/llvm.zig | 15 +- src/link.zig | 371 +----- src/link/C.zig | 7 +- src/link/Coff.zig | 630 +-------- src/link/Dwarf.zig | 2 +- src/link/Elf.zig | 781 +---------- src/link/Elf/ZigObject.zig | 8 +- src/link/Goff.zig | 5 - src/link/Lld.zig | 2148 +++++++++++++++++++++++++++++++ src/link/MachO.zig | 16 +- src/link/MachO/DebugSymbols.zig | 2 +- src/link/MachO/ZigObject.zig | 8 +- src/link/Plan9.zig | 40 +- src/link/SpirV.zig | 11 +- src/link/Wasm.zig | 473 +------ src/link/Xcoff.zig | 5 - src/main.zig | 18 +- 18 files changed, 2262 insertions(+), 2284 deletions(-) create mode 100644 src/link/Lld.zig diff --git a/src/Compilation.zig b/src/Compilation.zig index 61201f39f48d2efd374c8125d73eb072dc21a7cb..f51020c0ff8cc5bd77c23b5a99797efe0d4cae70 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -1592,9 +1592,9 @@ pub const CreateOptions = struct { linker_tsaware: bool = false, linker_nxcompat: bool = false, linker_dynamicbase: bool = true, - linker_compress_debug_sections: ?link.File.Elf.CompressDebugSections = null, + linker_compress_debug_sections: ?link.File.Lld.Elf.CompressDebugSections = null, linker_module_definition_file: ?[]const u8 = null, - linker_sort_section: ?link.File.Elf.SortSection = null, + linker_sort_section: ?link.File.Lld.Elf.SortSection = null, major_subsystem_version: ?u16 = null, minor_subsystem_version: ?u16 = null, clang_passthrough_mode: bool = false, @@ -1616,7 +1616,7 @@ pub const CreateOptions = struct { /// building such dependencies themselves, this flag must be set to avoid /// infinite recursion. skip_linker_dependencies: bool = false, - hash_style: link.File.Elf.HashStyle = .both, + hash_style: link.File.Lld.Elf.HashStyle = .both, entry: Entry = .default, force_undefined_symbols: std.StringArrayHashMapUnmanaged(void) = .empty, stack_size: ?u64 = null, diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 2b39396c38ac5c2074ed60c6c1db033e50e97f3b..37c13c721109a6b119b743cd3c0323917526a2c6 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -1587,12 +1587,15 @@ pub const Object = struct { 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; + coff_export_flags: { + const lf = comp.bin_file orelse break :coff_export_flags; + const lld = lf.cast(.lld) orelse break :coff_export_flags; + const coff = switch (lld.ofmt) { + .elf, .wasm => break :coff_export_flags, + .coff => |*coff| coff, + }; + if (!ip.isFunctionType(ip.getNav(nav_index).typeOf(ip))) break :coff_export_flags; + const flags = &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; diff --git a/src/link.zig b/src/link.zig index 3270d10c876554034f1b6412ff6aba53df82bdf5..68ea533eedb1d537b5138a048127340382b201e5 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 lldMain = @import("main.zig").lldMain; const Package = @import("Package.zig"); const dev = @import("dev.zig"); const ThreadSafeQueue = @import("ThreadSafeQueue.zig").ThreadSafeQueue; @@ -388,7 +387,6 @@ pub const File = struct { /// When linking with LLD, this linker code will output an object file only at /// this location, and then this path can be placed on the LLD linker line. zcu_object_sub_path: ?[]const u8 = null, - disable_lld_caching: bool, gc_sections: bool, print_gc_sections: bool, build_id: std.zig.BuildId, @@ -424,7 +422,7 @@ pub const File = struct { tsaware: bool, nxcompat: bool, dynamicbase: bool, - compress_debug_sections: Elf.CompressDebugSections, + compress_debug_sections: Lld.Elf.CompressDebugSections, bind_global_refs_locally: bool, import_symbols: bool, import_table: bool, @@ -436,8 +434,8 @@ pub const File = struct { global_base: ?u64, build_id: std.zig.BuildId, disable_lld_caching: bool, - hash_style: Elf.HashStyle, - sort_section: ?Elf.SortSection, + hash_style: Lld.Elf.HashStyle, + sort_section: ?Lld.Elf.SortSection, major_subsystem_version: ?u16, minor_subsystem_version: ?u16, gc_sections: ?bool, @@ -521,12 +519,20 @@ pub const File = struct { emit: Path, options: OpenOptions, ) !*File { + if (comp.config.use_lld) { + dev.check(.lld_linker); + assert(comp.zcu == null or comp.config.use_llvm); + // LLD does not support incremental linking. + const lld: *Lld = try .createEmpty(arena, comp, emit, options); + return &lld.base; + } switch (Tag.fromObjectFormat(comp.root_mod.resolved_target.result.ofmt)) { inline else => |tag| { dev.check(tag.devFeature()); const ptr = try tag.Type().open(arena, comp, emit, options); return &ptr.base; }, + .lld => unreachable, // not known from ofmt } } @@ -536,12 +542,19 @@ pub const File = struct { emit: Path, options: OpenOptions, ) !*File { + if (comp.config.use_lld) { + dev.check(.lld_linker); + assert(comp.zcu == null or comp.config.use_llvm); + const lld: *Lld = try .createEmpty(arena, comp, emit, options); + return &lld.base; + } switch (Tag.fromObjectFormat(comp.root_mod.resolved_target.result.ofmt)) { inline else => |tag| { dev.check(tag.devFeature()); const ptr = try tag.Type().createEmpty(arena, comp, emit, options); return &ptr.base; }, + .lld => unreachable, // not known from ofmt } } @@ -554,6 +567,7 @@ pub const File = struct { const comp = base.comp; const gpa = comp.gpa; switch (base.tag) { + .lld => assert(base.file == null), .coff, .elf, .macho, .plan9, .wasm, .goff, .xcoff => { if (base.file != null) return; dev.checkAny(&.{ .coff_linker, .elf_linker, .macho_linker, .plan9_linker, .wasm_linker, .goff_linker, .xcoff_linker }); @@ -586,13 +600,12 @@ pub const File = struct { } } } - const use_lld = build_options.have_llvm and comp.config.use_lld; const output_mode = comp.config.output_mode; const link_mode = comp.config.link_mode; base.file = try emit.root_dir.handle.createFile(emit.sub_path, .{ .truncate = false, .read = true, - .mode = determineMode(use_lld, output_mode, link_mode), + .mode = determineMode(output_mode, link_mode), }); }, .c, .spirv => dev.checkAny(&.{ .c_linker, .spirv_linker }), @@ -618,7 +631,6 @@ pub const File = struct { const comp = base.comp; const output_mode = comp.config.output_mode; const link_mode = comp.config.link_mode; - const use_lld = build_options.have_llvm and comp.config.use_lld; switch (output_mode) { .Obj => return, @@ -629,13 +641,9 @@ pub const File = struct { .Exe => {}, } switch (base.tag) { + .lld => assert(base.file == null), .elf => if (base.file) |f| { dev.check(.elf_linker); - if (base.zcu_object_sub_path != null and use_lld) { - // The file we have open is not the final file that we want to - // make executable, so we don't have to close it. - return; - } f.close(); base.file = null; @@ -650,11 +658,6 @@ pub const File = struct { }, .coff, .macho, .plan9, .wasm, .goff, .xcoff => if (base.file) |f| { dev.checkAny(&.{ .coff_linker, .macho_linker, .plan9_linker, .wasm_linker, .goff_linker, .xcoff_linker }); - if (base.zcu_object_sub_path != null) { - // The file we have open is not the final file that we want to - // make executable, so we don't have to close it. - return; - } f.close(); base.file = null; @@ -692,6 +695,7 @@ pub const File = struct { pub fn getGlobalSymbol(base: *File, name: []const u8, lib_name: ?[]const u8) UpdateNavError!u32 { log.debug("getGlobalSymbol '{s}' (expected in '{?s}')", .{ name, lib_name }); switch (base.tag) { + .lld => unreachable, .plan9 => unreachable, .spirv => unreachable, .c => unreachable, @@ -709,6 +713,7 @@ pub const File = struct { const nav = pt.zcu.intern_pool.getNav(nav_index); assert(nav.status == .fully_resolved); switch (base.tag) { + .lld => unreachable, inline else => |tag| { dev.check(tag.devFeature()); return @as(*tag.Type(), @fieldParentPtr("base", base)).updateNav(pt, nav_index); @@ -726,6 +731,7 @@ pub const File = struct { fn updateContainerType(base: *File, pt: Zcu.PerThread, ty: InternPool.Index) UpdateContainerTypeError!void { assert(base.comp.zcu.?.llvm_object == null); switch (base.tag) { + .lld => unreachable, else => {}, inline .elf => |tag| { dev.check(tag.devFeature()); @@ -746,6 +752,7 @@ pub const File = struct { ) UpdateNavError!void { assert(base.comp.zcu.?.llvm_object == null); switch (base.tag) { + .lld => unreachable, inline else => |tag| { dev.check(tag.devFeature()); return @as(*tag.Type(), @fieldParentPtr("base", base)).updateFunc(pt, func_index, air, liveness); @@ -772,6 +779,7 @@ pub const File = struct { } switch (base.tag) { + .lld => unreachable, .spirv => {}, .goff, .xcoff => {}, inline else => |tag| { @@ -811,8 +819,7 @@ pub const File = struct { OutOfMemory, }; - /// Commit pending changes and write headers. Takes into account final output mode - /// and `use_lld`, not only `effectiveOutputMode`. + /// Commit pending changes and write headers. Takes into account final output mode. /// `arena` has the lifetime of the call to `Compilation.update`. pub fn flush(base: *File, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) FlushError!void { const comp = base.comp; @@ -834,15 +841,7 @@ pub const File = struct { }; return; } - assert(base.post_prelink); - - const use_lld = build_options.have_llvm and comp.config.use_lld; - const output_mode = comp.config.output_mode; - const link_mode = comp.config.link_mode; - if (use_lld and output_mode == .Lib and link_mode == .static) { - return base.linkAsArchive(arena, tid, prog_node); - } switch (base.tag) { inline else => |tag| { dev.check(tag.devFeature()); @@ -851,19 +850,6 @@ pub const File = struct { } } - /// Commit pending changes and write headers. Works based on `effectiveOutputMode` - /// rather than final output mode. - /// 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)).flushZcu(arena, tid, prog_node); - }, - } - } - pub const UpdateExportsError = error{ OutOfMemory, AnalysisFail, @@ -882,6 +868,7 @@ pub const File = struct { ) UpdateExportsError!void { assert(base.comp.zcu.?.llvm_object == null); switch (base.tag) { + .lld => unreachable, inline else => |tag| { dev.check(tag.devFeature()); return @as(*tag.Type(), @fieldParentPtr("base", base)).updateExports(pt, exported, export_indices); @@ -911,6 +898,7 @@ pub const File = struct { 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) { + .lld => unreachable, .c => unreachable, .spirv => unreachable, .wasm => unreachable, @@ -932,6 +920,7 @@ pub const File = struct { ) !codegen.GenResult { assert(base.comp.zcu.?.llvm_object == null); switch (base.tag) { + .lld => unreachable, .c => unreachable, .spirv => unreachable, .wasm => unreachable, @@ -947,6 +936,7 @@ pub const File = struct { pub fn getUavVAddr(base: *File, decl_val: InternPool.Index, reloc_info: RelocInfo) !u64 { assert(base.comp.zcu.?.llvm_object == null); switch (base.tag) { + .lld => unreachable, .c => unreachable, .spirv => unreachable, .wasm => unreachable, @@ -966,6 +956,8 @@ pub const File = struct { ) void { assert(base.comp.zcu.?.llvm_object == null); switch (base.tag) { + .lld => unreachable, + .plan9, .spirv, .goff, @@ -981,6 +973,7 @@ pub const File = struct { /// Opens a path as an object file and parses it into the linker. fn openLoadObject(base: *File, path: Path) anyerror!void { + if (base.tag == .lld) return; const diags = &base.comp.link_diags; const input = try openObjectInput(diags, path); errdefer input.object.file.close(); @@ -990,6 +983,7 @@ pub const File = struct { /// Opens a path as a static library and parses it into the linker. /// If `query` is non-null, allows GNU ld scripts. fn openLoadArchive(base: *File, path: Path, opt_query: ?UnresolvedInput.Query) anyerror!void { + if (base.tag == .lld) return; if (opt_query) |query| { const archive = try openObject(path, query.must_link, query.hidden); errdefer archive.file.close(); @@ -1012,6 +1006,7 @@ pub const File = struct { /// Opens a path as a shared library and parses it into the linker. /// Handles GNU ld scripts. fn openLoadDso(base: *File, path: Path, query: UnresolvedInput.Query) anyerror!void { + if (base.tag == .lld) return; const dso = try openDso(path, query.needed, query.weak, query.reexport); errdefer dso.file.close(); loadInput(base, .{ .dso = dso }) catch |err| switch (err) { @@ -1064,8 +1059,7 @@ pub const File = struct { } pub fn loadInput(base: *File, input: Input) anyerror!void { - const use_lld = build_options.have_llvm and base.comp.config.use_lld; - if (use_lld) return; + if (base.tag == .lld) return; switch (base.tag) { inline .elf, .wasm => |tag| { dev.check(tag.devFeature()); @@ -1079,8 +1073,6 @@ pub const File = struct { /// this, `loadInput` will not be called anymore. pub fn prelink(base: *File, prog_node: std.Progress.Node) FlushError!void { assert(!base.post_prelink); - const use_lld = build_options.have_llvm and base.comp.config.use_lld; - if (use_lld) return; // In this case, an object file is created by the LLVM backend, so // there is no prelink phase. The Zig code is linked as a standard @@ -1096,170 +1088,6 @@ pub const File = struct { } } - 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()); - defer tracy.end(); - - const comp = base.comp; - const diags = &comp.link_diags; - - return linkAsArchiveInner(base, arena, tid, prog_node) catch |err| switch (err) { - error.OutOfMemory => return error.OutOfMemory, - error.LinkFailure => return error.LinkFailure, - else => |e| return diags.fail("failed to link as archive: {s}", .{@errorName(e)}), - }; - } - - fn linkAsArchiveInner(base: *File, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) !void { - const comp = base.comp; - - const directory = base.emit.root_dir; // Just an alias to make it shorter to type. - const full_out_path = try directory.join(arena, &[_][]const u8{base.emit.sub_path}); - const full_out_path_z = try arena.dupeZ(u8, full_out_path); - const opt_zcu = comp.zcu; - - // 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) |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; - - log.debug("zcu_obj_path={s}", .{if (zcu_obj_path) |s| s else "(null)"}); - - const compiler_rt_path: ?Path = if (comp.compiler_rt_strat == .obj) - comp.compiler_rt_obj.?.full_object_path - else - null; - - const ubsan_rt_path: ?Path = if (comp.ubsan_rt_strat == .obj) - comp.ubsan_rt_obj.?.full_object_path - else - null; - - // This function follows the same pattern as link.Elf.linkWithLLD so if you want some - // insight as to what's going on here you can read that function body which is more - // well-commented. - - const id_symlink_basename = "llvm-ar.id"; - - var man: Cache.Manifest = undefined; - defer if (!base.disable_lld_caching) man.deinit(); - - const link_inputs = comp.link_inputs; - - var digest: [Cache.hex_digest_len]u8 = undefined; - - if (!base.disable_lld_caching) { - man = comp.cache_parent.obtain(); - - // We are about to obtain this lock, so here we give other processes a chance first. - base.releaseLock(); - - try hashInputs(&man, link_inputs); - - for (comp.c_object_table.keys()) |key| { - _ = try man.addFilePath(key.status.success.object_path, null); - } - for (comp.win32_resource_table.keys()) |key| { - _ = try man.addFile(key.status.success.res_path, null); - } - try man.addOptionalFile(zcu_obj_path); - try man.addOptionalFilePath(compiler_rt_path); - try man.addOptionalFilePath(ubsan_rt_path); - - // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock. - _ = try man.hit(); - digest = man.final(); - - var prev_digest_buf: [digest.len]u8 = undefined; - const prev_digest: []u8 = Cache.readSmallFile( - directory.handle, - id_symlink_basename, - &prev_digest_buf, - ) catch |err| b: { - log.debug("archive new_digest={s} readFile error: {s}", .{ std.fmt.fmtSliceHexLower(&digest), @errorName(err) }); - break :b prev_digest_buf[0..0]; - }; - if (mem.eql(u8, prev_digest, &digest)) { - log.debug("archive digest={s} match - skipping invocation", .{std.fmt.fmtSliceHexLower(&digest)}); - base.lock = man.toOwnedLock(); - return; - } - - // We are about to change the output file to be different, so we invalidate the build hash now. - directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) { - error.FileNotFound => {}, - else => |e| return e, - }; - } - - var object_files: std.ArrayListUnmanaged([*:0]const u8) = .empty; - - try object_files.ensureUnusedCapacity(arena, link_inputs.len); - for (link_inputs) |input| { - object_files.appendAssumeCapacity(try input.path().?.toStringZ(arena)); - } - - try object_files.ensureUnusedCapacity(arena, comp.c_object_table.count() + - comp.win32_resource_table.count() + 2); - - for (comp.c_object_table.keys()) |key| { - object_files.appendAssumeCapacity(try key.status.success.object_path.toStringZ(arena)); - } - for (comp.win32_resource_table.keys()) |key| { - object_files.appendAssumeCapacity(try arena.dupeZ(u8, key.status.success.res_path)); - } - if (zcu_obj_path) |p| object_files.appendAssumeCapacity(try arena.dupeZ(u8, p)); - if (compiler_rt_path) |p| object_files.appendAssumeCapacity(try p.toStringZ(arena)); - if (ubsan_rt_path) |p| object_files.appendAssumeCapacity(try p.toStringZ(arena)); - - if (comp.verbose_link) { - std.debug.print("ar rcs {s}", .{full_out_path_z}); - for (object_files.items) |arg| { - std.debug.print(" {s}", .{arg}); - } - std.debug.print("\n", .{}); - } - - const llvm_bindings = @import("codegen/llvm/bindings.zig"); - const llvm = @import("codegen/llvm.zig"); - const target = comp.root_mod.resolved_target.result; - llvm.initializeLLVMTarget(target.cpu.arch); - const bad = llvm_bindings.WriteArchive( - full_out_path_z, - object_files.items.ptr, - object_files.items.len, - switch (target.os.tag) { - .aix => .AIXBIG, - .windows => .COFF, - else => if (target.os.tag.isDarwin()) .DARWIN else .GNU, - }, - ); - if (bad) return error.UnableToWriteArchive; - - if (!base.disable_lld_caching) { - Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| { - log.warn("failed to save archive hash digest file: {s}", .{@errorName(err)}); - }; - - if (man.have_exclusive_lock) { - man.writeManifest() catch |err| { - log.warn("failed to write cache manifest when archiving: {s}", .{@errorName(err)}); - }; - } - - base.lock = man.toOwnedLock(); - } - } - pub const Tag = enum { coff, elf, @@ -1270,6 +1098,7 @@ pub const File = struct { plan9, goff, xcoff, + lld, pub fn Type(comptime tag: Tag) type { return switch (tag) { @@ -1282,10 +1111,11 @@ pub const File = struct { .plan9 => Plan9, .goff => Goff, .xcoff => Xcoff, + .lld => Lld, }; } - pub fn fromObjectFormat(ofmt: std.Target.ObjectFormat) Tag { + fn fromObjectFormat(ofmt: std.Target.ObjectFormat) Tag { return switch (ofmt) { .coff => .coff, .elf => .elf, @@ -1313,15 +1143,7 @@ pub const File = struct { ty: InternPool.Index, }; - pub fn effectiveOutputMode( - use_lld: bool, - output_mode: std.builtin.OutputMode, - ) std.builtin.OutputMode { - return if (use_lld) .Obj else output_mode; - } - pub fn determineMode( - use_lld: bool, output_mode: std.builtin.OutputMode, link_mode: std.builtin.LinkMode, ) fs.File.Mode { @@ -1330,7 +1152,7 @@ pub const File = struct { // more leniently. As another data point, C's fopen seems to open files with the // 666 mode. const executable_mode = if (builtin.target.os.tag == .windows) 0 else 0o777; - switch (effectiveOutputMode(use_lld, output_mode)) { + switch (output_mode) { .Lib => return switch (link_mode) { .dynamic => executable_mode, .static => fs.File.default_mode, @@ -1378,6 +1200,7 @@ pub const File = struct { return base.comp.zcu.?.codegenFail(nav_index, format, args); } + pub const Lld = @import("link/Lld.zig"); pub const C = @import("link/C.zig"); pub const Coff = @import("link/Coff.zig"); pub const Plan9 = @import("link/Plan9.zig"); @@ -1685,116 +1508,6 @@ pub fn doTask(comp: *Compilation, tid: usize, task: Task) void { } } -pub fn spawnLld( - comp: *Compilation, - arena: Allocator, - argv: []const []const u8, -) !void { - if (comp.verbose_link) { - // Skip over our own name so that the LLD linker name is the first argv item. - Compilation.dump_argv(argv[1..]); - } - - // If possible, we run LLD as a child process because it does not always - // behave properly as a library, unfortunately. - // https://github.com/ziglang/zig/issues/3825 - if (!std.process.can_spawn) { - const exit_code = try lldMain(arena, argv, false); - if (exit_code == 0) return; - if (comp.clang_passthrough_mode) std.process.exit(exit_code); - return error.LinkFailure; - } - - var stderr: []u8 = &.{}; - defer comp.gpa.free(stderr); - - var child = std.process.Child.init(argv, arena); - const term = (if (comp.clang_passthrough_mode) term: { - child.stdin_behavior = .Inherit; - child.stdout_behavior = .Inherit; - child.stderr_behavior = .Inherit; - - break :term child.spawnAndWait(); - } else term: { - child.stdin_behavior = .Ignore; - child.stdout_behavior = .Ignore; - child.stderr_behavior = .Pipe; - - child.spawn() catch |err| break :term err; - stderr = try child.stderr.?.reader().readAllAlloc(comp.gpa, std.math.maxInt(usize)); - break :term child.wait(); - }) catch |first_err| term: { - const err = switch (first_err) { - error.NameTooLong => err: { - const s = fs.path.sep_str; - const rand_int = std.crypto.random.int(u64); - const rsp_path = "tmp" ++ s ++ std.fmt.hex(rand_int) ++ ".rsp"; - - const rsp_file = try comp.dirs.local_cache.handle.createFileZ(rsp_path, .{}); - defer comp.dirs.local_cache.handle.deleteFileZ(rsp_path) catch |err| - log.warn("failed to delete response file {s}: {s}", .{ rsp_path, @errorName(err) }); - { - defer rsp_file.close(); - var rsp_buf = std.io.bufferedWriter(rsp_file.writer()); - const rsp_writer = rsp_buf.writer(); - for (argv[2..]) |arg| { - try rsp_writer.writeByte('"'); - for (arg) |c| { - switch (c) { - '\"', '\\' => try rsp_writer.writeByte('\\'), - else => {}, - } - try rsp_writer.writeByte(c); - } - try rsp_writer.writeByte('"'); - try rsp_writer.writeByte('\n'); - } - try rsp_buf.flush(); - } - - var rsp_child = std.process.Child.init(&.{ argv[0], argv[1], try std.fmt.allocPrint( - arena, - "@{s}", - .{try comp.dirs.local_cache.join(arena, &.{rsp_path})}, - ) }, arena); - if (comp.clang_passthrough_mode) { - rsp_child.stdin_behavior = .Inherit; - rsp_child.stdout_behavior = .Inherit; - rsp_child.stderr_behavior = .Inherit; - - break :term rsp_child.spawnAndWait() catch |err| break :err err; - } else { - rsp_child.stdin_behavior = .Ignore; - rsp_child.stdout_behavior = .Ignore; - rsp_child.stderr_behavior = .Pipe; - - rsp_child.spawn() catch |err| break :err err; - stderr = try rsp_child.stderr.?.reader().readAllAlloc(comp.gpa, std.math.maxInt(usize)); - break :term rsp_child.wait() catch |err| break :err err; - } - }, - else => first_err, - }; - log.err("unable to spawn LLD {s}: {s}", .{ argv[0], @errorName(err) }); - return error.UnableToSpawnSelf; - }; - - const diags = &comp.link_diags; - switch (term) { - .Exited => |code| if (code != 0) { - if (comp.clang_passthrough_mode) std.process.exit(code); - diags.lockAndParseLldStderr(argv[1], stderr); - return error.LinkFailure; - }, - else => { - if (comp.clang_passthrough_mode) std.process.abort(); - return diags.fail("{s} terminated with stderr:\n{s}", .{ argv[0], stderr }); - }, - } - - if (stderr.len > 0) log.warn("unexpected LLD stderr:\n{s}", .{stderr}); -} - /// Provided by the CLI, processed into `LinkInput` instances at the start of /// the compilation pipeline. pub const UnresolvedInput = union(enum) { diff --git a/src/link/C.zig b/src/link/C.zig index 15004a26b7e55038aac6c7431ad60aa812009066..34fc1d3775503000bfa2875486444131ee7c6aba 100644 --- a/src/link/C.zig +++ b/src/link/C.zig @@ -145,7 +145,6 @@ pub fn createEmpty( .stack_size = options.stack_size orelse 16777216, .allow_shlib_undefined = options.allow_shlib_undefined orelse false, .file = file, - .disable_lld_caching = options.disable_lld_caching, .build_id = options.build_id, }, }; @@ -381,10 +380,6 @@ pub fn updateLineNumber(self: *C, pt: Zcu.PerThread, ti_id: InternPool.TrackedIn _ = ti_id; } -pub fn flush(self: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void { - return self.flushZcu(arena, tid, prog_node); -} - fn abiDefines(self: *C, target: std.Target) !std.ArrayList(u8) { const gpa = self.base.comp.gpa; var defines = std.ArrayList(u8).init(gpa); @@ -400,7 +395,7 @@ fn abiDefines(self: *C, target: std.Target) !std.ArrayList(u8) { return defines; } -pub fn flushZcu(self: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void { +pub fn flush(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 12a9dc975328e510bde660932a417bbac6fa0054..e7dcbcdf2a68d4b0f3a51d98dcb158c94cdebb8c 100644 --- a/src/link/Coff.zig +++ b/src/link/Coff.zig @@ -1,23 +1,14 @@ -//! The main driver of the COFF linker. -//! Currently uses our own implementation for the incremental linker, and falls back to -//! LLD for traditional linking (linking relocatable object files). -//! LLD is also the default linker for LLVM. +//! The main driver of the self-hosted COFF linker. base: link.File, image_base: u64, -subsystem: ?std.Target.SubSystem, -tsaware: bool, -nxcompat: bool, -dynamicbase: bool, /// TODO this and minor_subsystem_version should be combined into one property and left as /// default or populated together. They should not be separate fields. major_subsystem_version: u16, minor_subsystem_version: u16, -lib_directories: []const Directory, entry: link.File.OpenOptions.Entry, entry_addr: ?u32, module_definition_file: ?[]const u8, -pdb_out_path: ?[]const u8, repro: bool, ptr_width: PtrWidth, @@ -84,16 +75,6 @@ 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, @@ -233,7 +214,6 @@ pub fn createEmpty( const output_mode = comp.config.output_mode; const link_mode = comp.config.link_mode; const use_llvm = comp.config.use_llvm; - const use_lld = build_options.have_llvm and comp.config.use_lld; const ptr_width: PtrWidth = switch (target.ptrBitWidth()) { 0...32 => .p32, @@ -244,12 +224,10 @@ pub fn createEmpty( else => 0x1000, }; - // If using LLD to link, this code should produce an object file so that it - // can be passed to LLD. // If using LLVM to generate the object file for the zig compilation unit, // we need a place to put the object file so that it can be subsequently // handled. - const zcu_object_sub_path = if (!use_lld and !use_llvm) + const zcu_object_sub_path = if (!use_llvm) null else try allocPrint(arena, "{s}.obj", .{emit.sub_path}); @@ -266,7 +244,6 @@ pub fn createEmpty( .print_gc_sections = options.print_gc_sections, .allow_shlib_undefined = options.allow_shlib_undefined orelse false, .file = null, - .disable_lld_caching = options.disable_lld_caching, .build_id = options.build_id, }, .ptr_width = ptr_width, @@ -291,39 +268,21 @@ pub fn createEmpty( .Obj => 0, }, - // Subsystem depends on the set of public symbol names from linked objects. - // See LinkerDriver::inferSubsystem from the LLD project for the flow chart. - .subsystem = options.subsystem, - .entry = options.entry, - .tsaware = options.tsaware, - .nxcompat = options.nxcompat, - .dynamicbase = options.dynamicbase, .major_subsystem_version = options.major_subsystem_version orelse 6, .minor_subsystem_version = options.minor_subsystem_version orelse 0, - .lib_directories = options.lib_directories, .entry_addr = math.cast(u32, options.entry_addr orelse 0) orelse return error.EntryAddressTooBig, .module_definition_file = options.module_definition_file, - .pdb_out_path = options.pdb_out_path, .repro = options.repro, }; errdefer coff.base.destroy(); - if (use_lld and (use_llvm or !comp.config.have_zcu)) { - // LLVM emits the object file (if any); LLD links it into the final product. - return coff; - } - - // What path should this COFF linker code output to? - // If using LLD to link, this code should produce an object file so that it - // can be passed to LLD. - const sub_path = if (use_lld) zcu_object_sub_path.? else emit.sub_path; - coff.base.file = try emit.root_dir.handle.createFile(sub_path, .{ + coff.base.file = try emit.root_dir.handle.createFile(emit.sub_path, .{ .truncate = true, .read = true, - .mode = link.File.determineMode(use_lld, output_mode, link_mode), + .mode = link.File.determineMode(output_mode, link_mode), }); const gpa = comp.gpa; @@ -1327,7 +1286,7 @@ pub fn getOrCreateAtomForLazySymbol( } state_ptr.* = .pending_flush; const atom = atom_ptr.*; - // anyerror needs to be deferred until flushZcu + // anyerror needs to be deferred until flush 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.?, @@ -1631,575 +1590,7 @@ fn resolveGlobalSymbol(coff: *Coff, current: SymbolWithLoc) !void { gop.value_ptr.* = current; } -pub fn flush(coff: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void { - const comp = coff.base.comp; - const use_lld = build_options.have_llvm and comp.config.use_lld; - const diags = &comp.link_diags; - if (use_lld) { - return coff.linkWithLLD(arena, tid, prog_node) catch |err| switch (err) { - error.OutOfMemory => return error.OutOfMemory, - error.LinkFailure => return error.LinkFailure, - else => |e| return diags.fail("failed to link with LLD: {s}", .{@errorName(e)}), - }; - } - switch (comp.config.output_mode) { - .Exe, .Obj => return coff.flushZcu(arena, tid, prog_node), - .Lib => return diags.fail("writing lib files not yet implemented for COFF", .{}), - } -} - -fn linkWithLLD(coff: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) !void { - dev.check(.lld_linker); - - const tracy = trace(@src()); - defer tracy.end(); - - const comp = coff.base.comp; - const gpa = comp.gpa; - - const directory = coff.base.emit.root_dir; // Just an alias to make it shorter to type. - const full_out_path = try directory.join(arena, &[_][]const u8{coff.base.emit.sub_path}); - - // 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) |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.? }); - } else { - break :blk coff.base.zcu_object_sub_path.?; - } - } else null; - - const sub_prog_node = prog_node.start("LLD Link", 0); - defer sub_prog_node.end(); - - const is_lib = comp.config.output_mode == .Lib; - const is_dyn_lib = comp.config.link_mode == .dynamic and is_lib; - const is_exe_or_dyn_lib = is_dyn_lib or comp.config.output_mode == .Exe; - const link_in_crt = comp.config.link_libc and is_exe_or_dyn_lib; - const target = comp.root_mod.resolved_target.result; - const optimize_mode = comp.root_mod.optimize_mode; - const entry_name: ?[]const u8 = switch (coff.entry) { - // This logic isn't quite right for disabled or enabled. No point in fixing it - // when the goal is to eliminate dependency on LLD anyway. - // https://github.com/ziglang/zig/issues/17751 - .disabled, .default, .enabled => null, - .named => |name| name, - }; - - // See link/Elf.zig for comments on how this mechanism works. - const id_symlink_basename = "lld.id"; - - var man: Cache.Manifest = undefined; - defer if (!coff.base.disable_lld_caching) man.deinit(); - - var digest: [Cache.hex_digest_len]u8 = undefined; - - if (!coff.base.disable_lld_caching) { - man = comp.cache_parent.obtain(); - coff.base.releaseLock(); - - comptime assert(Compilation.link_hash_implementation_version == 14); - - try link.hashInputs(&man, comp.link_inputs); - for (comp.c_object_table.keys()) |key| { - _ = try man.addFilePath(key.status.success.object_path, null); - } - for (comp.win32_resource_table.keys()) |key| { - _ = try man.addFile(key.status.success.res_path, null); - } - try man.addOptionalFile(module_obj_path); - man.hash.addOptionalBytes(entry_name); - man.hash.add(coff.base.stack_size); - man.hash.add(coff.image_base); - man.hash.add(coff.base.build_id); - { - // TODO remove this, libraries must instead be resolved by the frontend. - for (coff.lib_directories) |lib_directory| man.hash.addOptionalBytes(lib_directory.path); - } - man.hash.add(comp.skip_linker_dependencies); - if (comp.config.link_libc) { - man.hash.add(comp.libc_installation != null); - if (comp.libc_installation) |libc_installation| { - man.hash.addBytes(libc_installation.crt_dir.?); - if (target.abi == .msvc or target.abi == .itanium) { - man.hash.addBytes(libc_installation.msvc_lib_dir.?); - man.hash.addBytes(libc_installation.kernel32_lib_dir.?); - } - } - } - man.hash.addListOfBytes(comp.windows_libs.keys()); - man.hash.addListOfBytes(comp.force_undefined_symbols.keys()); - man.hash.addOptional(coff.subsystem); - man.hash.add(comp.config.is_test); - man.hash.add(coff.tsaware); - man.hash.add(coff.nxcompat); - man.hash.add(coff.dynamicbase); - man.hash.add(coff.base.allow_shlib_undefined); - // strip does not need to go into the linker hash because it is part of the hash namespace - man.hash.add(coff.major_subsystem_version); - man.hash.add(coff.minor_subsystem_version); - man.hash.add(coff.repro); - man.hash.addOptional(comp.version); - try man.addOptionalFile(coff.module_definition_file); - - // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock. - _ = try man.hit(); - digest = man.final(); - var prev_digest_buf: [digest.len]u8 = undefined; - const prev_digest: []u8 = Cache.readSmallFile( - directory.handle, - id_symlink_basename, - &prev_digest_buf, - ) catch |err| blk: { - log.debug("COFF LLD new_digest={s} error: {s}", .{ std.fmt.fmtSliceHexLower(&digest), @errorName(err) }); - // Handle this as a cache miss. - break :blk prev_digest_buf[0..0]; - }; - if (mem.eql(u8, prev_digest, &digest)) { - log.debug("COFF LLD digest={s} match - skipping invocation", .{std.fmt.fmtSliceHexLower(&digest)}); - // Hot diggity dog! The output binary is already there. - coff.base.lock = man.toOwnedLock(); - return; - } - log.debug("COFF LLD prev_digest={s} new_digest={s}", .{ std.fmt.fmtSliceHexLower(prev_digest), std.fmt.fmtSliceHexLower(&digest) }); - - // We are about to change the output file to be different, so we invalidate the build hash now. - directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) { - error.FileNotFound => {}, - else => |e| return e, - }; - } - - if (comp.config.output_mode == .Obj) { - // LLD's COFF driver does not support the equivalent of `-r` so we do a simple file copy - // here. TODO: think carefully about how we can avoid this redundant operation when doing - // build-obj. See also the corresponding TODO in linkAsArchive. - const the_object_path = blk: { - if (link.firstObjectInput(comp.link_inputs)) |obj| break :blk obj.path; - - if (comp.c_object_table.count() != 0) - break :blk comp.c_object_table.keys()[0].status.success.object_path; - - if (module_obj_path) |p| - break :blk Path.initCwd(p); - - // TODO I think this is unreachable. Audit this situation when solving the above TODO - // regarding eliding redundant object -> object transformations. - return error.NoObjectsToLink; - }; - try std.fs.Dir.copyFile( - the_object_path.root_dir.handle, - the_object_path.sub_path, - directory.handle, - coff.base.emit.sub_path, - .{}, - ); - } else { - // Create an LLD command line and invoke it. - var argv = std.ArrayList([]const u8).init(gpa); - defer argv.deinit(); - // We will invoke ourselves as a child process to gain access to LLD. - // This is necessary because LLD does not behave properly as a library - - // it calls exit() and does not reset all global data between invocations. - const linker_command = "lld-link"; - try argv.appendSlice(&[_][]const u8{ comp.self_exe_path.?, linker_command }); - - if (target.isMinGW()) { - try argv.append("-lldmingw"); - } - - try argv.append("-ERRORLIMIT:0"); - try argv.append("-NOLOGO"); - if (comp.config.debug_format != .strip) { - try argv.append("-DEBUG"); - - const out_ext = std.fs.path.extension(full_out_path); - const out_pdb = coff.pdb_out_path orelse try allocPrint(arena, "{s}.pdb", .{ - full_out_path[0 .. full_out_path.len - out_ext.len], - }); - const out_pdb_basename = std.fs.path.basename(out_pdb); - - try argv.append(try allocPrint(arena, "-PDB:{s}", .{out_pdb})); - try argv.append(try allocPrint(arena, "-PDBALTPATH:{s}", .{out_pdb_basename})); - } - if (comp.version) |version| { - try argv.append(try allocPrint(arena, "-VERSION:{}.{}", .{ version.major, version.minor })); - } - - if (target_util.llvmMachineAbi(target)) |mabi| { - try argv.append(try allocPrint(arena, "-MLLVM:-target-abi={s}", .{mabi})); - } - - try argv.append(try allocPrint(arena, "-MLLVM:-float-abi={s}", .{if (target.abi.float() == .hard) "hard" else "soft"})); - - if (comp.config.lto != .none) { - switch (optimize_mode) { - .Debug => {}, - .ReleaseSmall => try argv.append("-OPT:lldlto=2"), - .ReleaseFast, .ReleaseSafe => try argv.append("-OPT:lldlto=3"), - } - } - if (comp.config.output_mode == .Exe) { - try argv.append(try allocPrint(arena, "-STACK:{d}", .{coff.base.stack_size})); - } - try argv.append(try allocPrint(arena, "-BASE:{d}", .{coff.image_base})); - - switch (coff.base.build_id) { - .none => try argv.append("-BUILD-ID:NO"), - .fast => try argv.append("-BUILD-ID"), - .uuid, .sha1, .md5, .hexstring => {}, - } - - if (target.cpu.arch == .x86) { - try argv.append("-MACHINE:X86"); - } else if (target.cpu.arch == .x86_64) { - try argv.append("-MACHINE:X64"); - } else if (target.cpu.arch == .thumb) { - try argv.append("-MACHINE:ARM"); - } else if (target.cpu.arch == .aarch64) { - try argv.append("-MACHINE:ARM64"); - } - - for (comp.force_undefined_symbols.keys()) |symbol| { - try argv.append(try allocPrint(arena, "-INCLUDE:{s}", .{symbol})); - } - - if (is_dyn_lib) { - try argv.append("-DLL"); - } - - if (entry_name) |name| { - try argv.append(try allocPrint(arena, "-ENTRY:{s}", .{name})); - } - - if (coff.repro) { - try argv.append("-BREPRO"); - } - - if (coff.tsaware) { - try argv.append("-tsaware"); - } - if (coff.nxcompat) { - try argv.append("-nxcompat"); - } - if (!coff.dynamicbase) { - try argv.append("-dynamicbase:NO"); - } - if (coff.base.allow_shlib_undefined) { - try argv.append("-FORCE:UNRESOLVED"); - } - - try argv.append(try allocPrint(arena, "-OUT:{s}", .{full_out_path})); - - if (comp.implib_emit) |emit| { - const implib_out_path = try emit.root_dir.join(arena, &[_][]const u8{emit.sub_path}); - try argv.append(try allocPrint(arena, "-IMPLIB:{s}", .{implib_out_path})); - } - - if (comp.config.link_libc) { - if (comp.libc_installation) |libc_installation| { - try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{libc_installation.crt_dir.?})); - - if (target.abi == .msvc or target.abi == .itanium) { - try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{libc_installation.msvc_lib_dir.?})); - try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{libc_installation.kernel32_lib_dir.?})); - } - } - } - - for (coff.lib_directories) |lib_directory| { - try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{lib_directory.path orelse "."})); - } - - try argv.ensureUnusedCapacity(comp.link_inputs.len); - for (comp.link_inputs) |link_input| switch (link_input) { - .dso_exact => unreachable, // not applicable to PE/COFF - inline .dso, .res => |x| { - argv.appendAssumeCapacity(try x.path.toString(arena)); - }, - .object, .archive => |obj| { - if (obj.must_link) { - argv.appendAssumeCapacity(try allocPrint(arena, "-WHOLEARCHIVE:{}", .{@as(Path, obj.path)})); - } else { - argv.appendAssumeCapacity(try obj.path.toString(arena)); - } - }, - }; - - for (comp.c_object_table.keys()) |key| { - try argv.append(try key.status.success.object_path.toString(arena)); - } - - for (comp.win32_resource_table.keys()) |key| { - try argv.append(key.status.success.res_path); - } - - if (module_obj_path) |p| { - try argv.append(p); - } - - if (coff.module_definition_file) |def| { - try argv.append(try allocPrint(arena, "-DEF:{s}", .{def})); - } - - const resolved_subsystem: ?std.Target.SubSystem = blk: { - if (coff.subsystem) |explicit| break :blk explicit; - switch (target.os.tag) { - .windows => { - if (comp.zcu != null) { - if (coff.lld_export_flags.dllmain_crt_startup or is_dyn_lib) - break :blk null; - 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 (coff.lld_export_flags.winmain or coff.lld_export_flags.wwinmain) - break :blk .Windows; - } - }, - .uefi => break :blk .EfiApplication, - else => {}, - } - break :blk null; - }; - - const Mode = enum { uefi, win32 }; - const mode: Mode = mode: { - if (resolved_subsystem) |subsystem| { - const subsystem_suffix = try allocPrint(arena, ",{d}.{d}", .{ - coff.major_subsystem_version, coff.minor_subsystem_version, - }); - - switch (subsystem) { - .Console => { - try argv.append(try allocPrint(arena, "-SUBSYSTEM:console{s}", .{ - subsystem_suffix, - })); - break :mode .win32; - }, - .EfiApplication => { - try argv.append(try allocPrint(arena, "-SUBSYSTEM:efi_application{s}", .{ - subsystem_suffix, - })); - break :mode .uefi; - }, - .EfiBootServiceDriver => { - try argv.append(try allocPrint(arena, "-SUBSYSTEM:efi_boot_service_driver{s}", .{ - subsystem_suffix, - })); - break :mode .uefi; - }, - .EfiRom => { - try argv.append(try allocPrint(arena, "-SUBSYSTEM:efi_rom{s}", .{ - subsystem_suffix, - })); - break :mode .uefi; - }, - .EfiRuntimeDriver => { - try argv.append(try allocPrint(arena, "-SUBSYSTEM:efi_runtime_driver{s}", .{ - subsystem_suffix, - })); - break :mode .uefi; - }, - .Native => { - try argv.append(try allocPrint(arena, "-SUBSYSTEM:native{s}", .{ - subsystem_suffix, - })); - break :mode .win32; - }, - .Posix => { - try argv.append(try allocPrint(arena, "-SUBSYSTEM:posix{s}", .{ - subsystem_suffix, - })); - break :mode .win32; - }, - .Windows => { - try argv.append(try allocPrint(arena, "-SUBSYSTEM:windows{s}", .{ - subsystem_suffix, - })); - break :mode .win32; - }, - } - } else if (target.os.tag == .uefi) { - break :mode .uefi; - } else { - break :mode .win32; - } - }; - - switch (mode) { - .uefi => try argv.appendSlice(&[_][]const u8{ - "-BASE:0", - "-ENTRY:EfiMain", - "-OPT:REF", - "-SAFESEH:NO", - "-MERGE:.rdata=.data", - "-NODEFAULTLIB", - "-SECTION:.xdata,D", - }), - .win32 => { - if (link_in_crt) { - if (target.abi.isGnu()) { - if (target.cpu.arch == .x86) { - try argv.append("-ALTERNATENAME:__image_base__=___ImageBase"); - } else { - try argv.append("-ALTERNATENAME:__image_base__=__ImageBase"); - } - - if (is_dyn_lib) { - try argv.append(try comp.crtFileAsString(arena, "dllcrt2.obj")); - if (target.cpu.arch == .x86) { - try argv.append("-ALTERNATENAME:__DllMainCRTStartup@12=_DllMainCRTStartup@12"); - } else { - try argv.append("-ALTERNATENAME:_DllMainCRTStartup=DllMainCRTStartup"); - } - } else { - try argv.append(try comp.crtFileAsString(arena, "crt2.obj")); - } - - try argv.append(try comp.crtFileAsString(arena, "libmingw32.lib")); - } else { - try argv.append(switch (comp.config.link_mode) { - .static => "libcmt.lib", - .dynamic => "msvcrt.lib", - }); - - const lib_str = switch (comp.config.link_mode) { - .static => "lib", - .dynamic => "", - }; - try argv.append(try allocPrint(arena, "{s}vcruntime.lib", .{lib_str})); - try argv.append(try allocPrint(arena, "{s}ucrt.lib", .{lib_str})); - - //Visual C++ 2015 Conformance Changes - //https://msdn.microsoft.com/en-us/library/bb531344.aspx - try argv.append("legacy_stdio_definitions.lib"); - - // msvcrt depends on kernel32 and ntdll - try argv.append("kernel32.lib"); - try argv.append("ntdll.lib"); - } - } else { - try argv.append("-NODEFAULTLIB"); - if (!is_lib and entry_name == null) { - if (comp.zcu != null) { - if (coff.lld_export_flags.winmain_crt_startup) { - try argv.append("-ENTRY:WinMainCRTStartup"); - } else { - try argv.append("-ENTRY:wWinMainCRTStartup"); - } - } else { - try argv.append("-ENTRY:wWinMainCRTStartup"); - } - } - } - }, - } - - if (comp.config.link_libc and link_in_crt) { - if (comp.zigc_static_lib) |zigc| { - try argv.append(try zigc.full_object_path.toString(arena)); - } - } - - // libc++ dep - if (comp.config.link_libcpp) { - try argv.append(try comp.libcxxabi_static_lib.?.full_object_path.toString(arena)); - try argv.append(try comp.libcxx_static_lib.?.full_object_path.toString(arena)); - } - - // libunwind dep - if (comp.config.link_libunwind) { - try argv.append(try comp.libunwind_static_lib.?.full_object_path.toString(arena)); - } - - if (comp.config.any_fuzz) { - try argv.append(try comp.fuzzer_lib.?.full_object_path.toString(arena)); - } - - const ubsan_rt_path: ?Path = blk: { - if (comp.ubsan_rt_lib) |x| break :blk x.full_object_path; - if (comp.ubsan_rt_obj) |x| break :blk x.full_object_path; - break :blk null; - }; - if (ubsan_rt_path) |path| { - try argv.append(try path.toString(arena)); - } - - if (is_exe_or_dyn_lib and !comp.skip_linker_dependencies) { - // MSVC compiler_rt is missing some stuff, so we build it unconditionally but - // and rely on weak linkage to allow MSVC compiler_rt functions to override ours. - if (comp.compiler_rt_obj) |obj| try argv.append(try obj.full_object_path.toString(arena)); - if (comp.compiler_rt_lib) |lib| try argv.append(try lib.full_object_path.toString(arena)); - } - - try argv.ensureUnusedCapacity(comp.windows_libs.count()); - for (comp.windows_libs.keys()) |key| { - const lib_basename = try allocPrint(arena, "{s}.lib", .{key}); - if (comp.crt_files.get(lib_basename)) |crt_file| { - argv.appendAssumeCapacity(try crt_file.full_object_path.toString(arena)); - continue; - } - if (try findLib(arena, lib_basename, coff.lib_directories)) |full_path| { - argv.appendAssumeCapacity(full_path); - continue; - } - if (target.abi.isGnu()) { - const fallback_name = try allocPrint(arena, "lib{s}.dll.a", .{key}); - if (try findLib(arena, fallback_name, coff.lib_directories)) |full_path| { - argv.appendAssumeCapacity(full_path); - continue; - } - } - if (target.abi == .msvc or target.abi == .itanium) { - argv.appendAssumeCapacity(lib_basename); - continue; - } - - log.err("DLL import library for -l{s} not found", .{key}); - return error.DllImportLibraryNotFound; - } - - try link.spawnLld(comp, arena, argv.items); - } - - if (!coff.base.disable_lld_caching) { - // Update the file with the digest. If it fails we can continue; it only - // means that the next invocation will have an unnecessary cache miss. - Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| { - log.warn("failed to save linking hash digest file: {s}", .{@errorName(err)}); - }; - // Again failure here only means an unnecessary cache miss. - man.writeManifest() catch |err| { - log.warn("failed to write cache manifest when linking: {s}", .{@errorName(err)}); - }; - // We hang on to this lock so that the output file path can be used without - // other processes clobbering it. - coff.base.lock = man.toOwnedLock(); - } -} - -fn findLib(arena: Allocator, name: []const u8, lib_directories: []const Directory) !?[]const u8 { - for (lib_directories) |lib_directory| { - lib_directory.handle.access(name, .{}) catch |err| switch (err) { - error.FileNotFound => continue, - else => |e| return e, - }; - return try lib_directory.join(arena, &.{name}); - } - return null; -} - -pub fn flushZcu( +pub fn flush( coff: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, @@ -2211,17 +1602,22 @@ pub fn flushZcu( const comp = coff.base.comp; const diags = &comp.link_diags; + switch (coff.base.comp.config.output_mode) { + .Exe, .Obj => {}, + .Lib => return diags.fail("writing lib files not yet implemented for COFF", .{}), + } + const sub_prog_node = prog_node.start("COFF Flush", 0); defer sub_prog_node.end(); - return flushZcuInner(coff, arena, tid) catch |err| switch (err) { + return flushInner(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 flushZcuInner(coff: *Coff, arena: Allocator, tid: Zcu.PerThread.Id) !void { +fn flushInner(coff: *Coff, arena: Allocator, tid: Zcu.PerThread.Id) !void { _ = arena; const comp = coff.base.comp; diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index c0d1281df2e4dd37bc5f28b3b43b1b7ae574896b..393cd53774919ad5ba552d4692f19df88ae06b79 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 flushZcu(dwarf: *Dwarf, pt: Zcu.PerThread) FlushError!void { +pub fn flush(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 b18fc7ce33505409863a800db3e45371e35fc091..1702ef200cb7cb89a9e6f6d3892094d8916c626e 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -4,7 +4,6 @@ base: link.File, zig_object: ?*ZigObject, rpath_table: std.StringArrayHashMapUnmanaged(void), image_base: u64, -emit_relocs: bool, z_nodelete: bool, z_notext: bool, z_defs: bool, @@ -16,18 +15,7 @@ z_relro: bool, z_common_page_size: ?u64, /// TODO make this non optional and resolve the default in open() z_max_page_size: ?u64, -hash_style: HashStyle, -compress_debug_sections: CompressDebugSections, -symbol_wrap_set: std.StringArrayHashMapUnmanaged(void), -sort_section: ?SortSection, soname: ?[]const u8, -bind_global_refs_locally: bool, -linker_script: ?[]const u8, -version_script: ?[]const u8, -allow_undefined_version: bool, -enable_new_dtags: ?bool, -print_icf_sections: bool, -print_map: bool, entry_name: ?[]const u8, ptr_width: PtrWidth, @@ -201,9 +189,6 @@ const minimum_atom_size = 64; pub const min_text_capacity = padToIdeal(minimum_atom_size); pub const PtrWidth = enum { p32, p64 }; -pub const HashStyle = enum { sysv, gnu, both }; -pub const CompressDebugSections = enum { none, zlib, zstd }; -pub const SortSection = enum { name, alignment }; pub fn createEmpty( arena: Allocator, @@ -214,7 +199,6 @@ pub fn createEmpty( const target = comp.root_mod.resolved_target.result; assert(target.ofmt == .elf); - const use_lld = build_options.have_llvm and comp.config.use_lld; const use_llvm = comp.config.use_llvm; const opt_zcu = comp.zcu; const output_mode = comp.config.output_mode; @@ -265,12 +249,10 @@ pub fn createEmpty( const is_dyn_lib = output_mode == .Lib and link_mode == .dynamic; const default_sym_version: elf.Versym = if (is_dyn_lib or comp.config.rdynamic) .GLOBAL else .LOCAL; - // If using LLD to link, this code should produce an object file so that it - // can be passed to LLD. // If using LLVM to generate the object file for the zig compilation unit, // we need a place to put the object file so that it can be subsequently // handled. - const zcu_object_sub_path = if (!use_lld and !use_llvm) + const zcu_object_sub_path = if (!use_llvm) null else try std.fmt.allocPrint(arena, "{s}.o", .{emit.sub_path}); @@ -292,7 +274,6 @@ pub fn createEmpty( .stack_size = options.stack_size orelse 16777216, .allow_shlib_undefined = options.allow_shlib_undefined orelse !is_native_os, .file = null, - .disable_lld_caching = options.disable_lld_caching, .build_id = options.build_id, }, .zig_object = null, @@ -317,7 +298,6 @@ pub fn createEmpty( }; }, - .emit_relocs = options.emit_relocs, .z_nodelete = options.z_nodelete, .z_notext = options.z_notext, .z_defs = options.z_defs, @@ -327,27 +307,11 @@ pub fn createEmpty( .z_relro = options.z_relro, .z_common_page_size = options.z_common_page_size, .z_max_page_size = options.z_max_page_size, - .hash_style = options.hash_style, - .compress_debug_sections = options.compress_debug_sections, - .symbol_wrap_set = options.symbol_wrap_set, - .sort_section = options.sort_section, .soname = options.soname, - .bind_global_refs_locally = options.bind_global_refs_locally, - .linker_script = options.linker_script, - .version_script = options.version_script, - .allow_undefined_version = options.allow_undefined_version, - .enable_new_dtags = options.enable_new_dtags, - .print_icf_sections = options.print_icf_sections, - .print_map = options.print_map, .dump_argv_list = .empty, }; errdefer self.base.destroy(); - if (use_lld and (use_llvm or !comp.config.have_zcu)) { - // LLVM emits the object file (if any); LLD links it into the final product. - return self; - } - // --verbose-link if (comp.verbose_link) try dumpArgvInit(self, arena); @@ -355,13 +319,11 @@ pub fn createEmpty( const is_obj_or_ar = is_obj or (output_mode == .Lib and link_mode == .static); // What path should this ELF linker code output to? - // If using LLD to link, this code should produce an object file so that it - // can be passed to LLD. - const sub_path = if (use_lld) zcu_object_sub_path.? else emit.sub_path; + const sub_path = emit.sub_path; self.base.file = try emit.root_dir.handle.createFile(sub_path, .{ .truncate = true, .read = true, - .mode = link.File.determineMode(use_lld, output_mode, link_mode), + .mode = link.File.determineMode(output_mode, link_mode), }); const gpa = comp.gpa; @@ -785,20 +747,6 @@ pub fn loadInput(self: *Elf, input: link.Input) !void { } pub fn flush(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void { - const comp = self.base.comp; - const use_lld = build_options.have_llvm and comp.config.use_lld; - const diags = &comp.link_diags; - if (use_lld) { - return self.linkWithLLD(arena, tid, prog_node) catch |err| switch (err) { - error.OutOfMemory => return error.OutOfMemory, - error.LinkFailure => return error.LinkFailure, - else => |e| return diags.fail("failed to link with LLD: {s}", .{@errorName(e)}), - }; - } - try self.flushZcu(arena, tid, prog_node); -} - -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(); @@ -810,14 +758,14 @@ pub fn flushZcu(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: const sub_prog_node = prog_node.start("ELF Flush", 0); defer sub_prog_node.end(); - return flushZcuInner(self, arena, tid) catch |err| switch (err) { + return flushInner(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 flushZcuInner(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id) !void { +fn flushInner(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id) !void { const comp = self.base.comp; const gpa = comp.gpa; const diags = &comp.link_diags; @@ -1492,643 +1440,6 @@ pub fn initOutputSection(self: *Elf, args: struct { return out_shndx; } -fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) !void { - dev.check(.lld_linker); - - const tracy = trace(@src()); - defer tracy.end(); - - const comp = self.base.comp; - const gpa = comp.gpa; - const diags = &comp.link_diags; - - const directory = self.base.emit.root_dir; // Just an alias to make it shorter to type. - const full_out_path = try directory.join(arena, &[_][]const u8{self.base.emit.sub_path}); - - // 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) |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.? }); - } else { - break :blk self.base.zcu_object_sub_path.?; - } - } else null; - - const sub_prog_node = prog_node.start("LLD Link", 0); - defer sub_prog_node.end(); - - const output_mode = comp.config.output_mode; - const is_obj = output_mode == .Obj; - const is_lib = output_mode == .Lib; - const link_mode = comp.config.link_mode; - const is_dyn_lib = link_mode == .dynamic and is_lib; - const is_exe_or_dyn_lib = is_dyn_lib or output_mode == .Exe; - const have_dynamic_linker = link_mode == .dynamic and is_exe_or_dyn_lib; - const target = self.getTarget(); - const compiler_rt_path: ?Path = blk: { - if (comp.compiler_rt_lib) |x| break :blk x.full_object_path; - if (comp.compiler_rt_obj) |x| break :blk x.full_object_path; - break :blk null; - }; - const ubsan_rt_path: ?Path = blk: { - if (comp.ubsan_rt_lib) |x| break :blk x.full_object_path; - if (comp.ubsan_rt_obj) |x| break :blk x.full_object_path; - break :blk null; - }; - - // Here we want to determine whether we can save time by not invoking LLD when the - // output is unchanged. None of the linker options or the object files that are being - // linked are in the hash that namespaces the directory we are outputting to. Therefore, - // we must hash those now, and the resulting digest will form the "id" of the linking - // job we are about to perform. - // After a successful link, we store the id in the metadata of a symlink named "lld.id" in - // the artifact directory. So, now, we check if this symlink exists, and if it matches - // our digest. If so, we can skip linking. Otherwise, we proceed with invoking LLD. - const id_symlink_basename = "lld.id"; - - var man: std.Build.Cache.Manifest = undefined; - defer if (!self.base.disable_lld_caching) man.deinit(); - - var digest: [std.Build.Cache.hex_digest_len]u8 = undefined; - - if (!self.base.disable_lld_caching) { - man = comp.cache_parent.obtain(); - - // We are about to obtain this lock, so here we give other processes a chance first. - self.base.releaseLock(); - - comptime assert(Compilation.link_hash_implementation_version == 14); - - try man.addOptionalFile(self.linker_script); - try man.addOptionalFile(self.version_script); - man.hash.add(self.allow_undefined_version); - man.hash.addOptional(self.enable_new_dtags); - try link.hashInputs(&man, comp.link_inputs); - for (comp.c_object_table.keys()) |key| { - _ = try man.addFilePath(key.status.success.object_path, null); - } - try man.addOptionalFile(module_obj_path); - try man.addOptionalFilePath(compiler_rt_path); - try man.addOptionalFilePath(ubsan_rt_path); - try man.addOptionalFilePath(if (comp.tsan_lib) |l| l.full_object_path else null); - try man.addOptionalFilePath(if (comp.fuzzer_lib) |l| l.full_object_path else null); - - // We can skip hashing libc and libc++ components that we are in charge of building from Zig - // installation sources because they are always a product of the compiler version + target information. - man.hash.addOptionalBytes(self.entry_name); - man.hash.add(self.image_base); - man.hash.add(self.base.gc_sections); - man.hash.addOptional(self.sort_section); - man.hash.add(comp.link_eh_frame_hdr); - man.hash.add(self.emit_relocs); - man.hash.add(comp.config.rdynamic); - man.hash.addListOfBytes(self.rpath_table.keys()); - if (output_mode == .Exe) { - man.hash.add(self.base.stack_size); - } - man.hash.add(self.base.build_id); - man.hash.addListOfBytes(self.symbol_wrap_set.keys()); - man.hash.add(comp.skip_linker_dependencies); - man.hash.add(self.z_nodelete); - man.hash.add(self.z_notext); - man.hash.add(self.z_defs); - man.hash.add(self.z_origin); - man.hash.add(self.z_nocopyreloc); - man.hash.add(self.z_now); - man.hash.add(self.z_relro); - man.hash.add(self.z_common_page_size orelse 0); - man.hash.add(self.z_max_page_size orelse 0); - man.hash.add(self.hash_style); - // strip does not need to go into the linker hash because it is part of the hash namespace - if (comp.config.link_libc) { - man.hash.add(comp.libc_installation != null); - if (comp.libc_installation) |libc_installation| { - man.hash.addBytes(libc_installation.crt_dir.?); - } - } - if (have_dynamic_linker) { - man.hash.addOptionalBytes(target.dynamic_linker.get()); - } - man.hash.addOptionalBytes(self.soname); - man.hash.addOptional(comp.version); - man.hash.addListOfBytes(comp.force_undefined_symbols.keys()); - man.hash.add(self.base.allow_shlib_undefined); - man.hash.add(self.bind_global_refs_locally); - man.hash.add(self.compress_debug_sections); - man.hash.add(comp.config.any_sanitize_thread); - man.hash.add(comp.config.any_fuzz); - man.hash.addOptionalBytes(comp.sysroot); - - // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock. - _ = try man.hit(); - digest = man.final(); - - var prev_digest_buf: [digest.len]u8 = undefined; - const prev_digest: []u8 = std.Build.Cache.readSmallFile( - directory.handle, - id_symlink_basename, - &prev_digest_buf, - ) catch |err| blk: { - log.debug("ELF LLD new_digest={s} error: {s}", .{ std.fmt.fmtSliceHexLower(&digest), @errorName(err) }); - // Handle this as a cache miss. - break :blk prev_digest_buf[0..0]; - }; - if (mem.eql(u8, prev_digest, &digest)) { - log.debug("ELF LLD digest={s} match - skipping invocation", .{std.fmt.fmtSliceHexLower(&digest)}); - // Hot diggity dog! The output binary is already there. - self.base.lock = man.toOwnedLock(); - return; - } - log.debug("ELF LLD prev_digest={s} new_digest={s}", .{ std.fmt.fmtSliceHexLower(prev_digest), std.fmt.fmtSliceHexLower(&digest) }); - - // We are about to change the output file to be different, so we invalidate the build hash now. - directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) { - error.FileNotFound => {}, - else => |e| return e, - }; - } - - // Due to a deficiency in LLD, we need to special-case BPF to a simple file - // copy when generating relocatables. Normally, we would expect `lld -r` to work. - // However, because LLD wants to resolve BPF relocations which it shouldn't, it fails - // before even generating the relocatable. - // - // For m68k, we go through this path because LLD doesn't support it yet, but LLVM can - // produce usable object files. - if (output_mode == .Obj and - (comp.config.lto != .none or - target.cpu.arch.isBpf() or - target.cpu.arch == .lanai or - target.cpu.arch == .m68k or - target.cpu.arch.isSPARC() or - target.cpu.arch == .ve or - target.cpu.arch == .xcore)) - { - // In this case we must do a simple file copy - // here. TODO: think carefully about how we can avoid this redundant operation when doing - // build-obj. See also the corresponding TODO in linkAsArchive. - const the_object_path = blk: { - if (link.firstObjectInput(comp.link_inputs)) |obj| break :blk obj.path; - - if (comp.c_object_table.count() != 0) - break :blk comp.c_object_table.keys()[0].status.success.object_path; - - if (module_obj_path) |p| - break :blk Path.initCwd(p); - - // TODO I think this is unreachable. Audit this situation when solving the above TODO - // regarding eliding redundant object -> object transformations. - return error.NoObjectsToLink; - }; - try std.fs.Dir.copyFile( - the_object_path.root_dir.handle, - the_object_path.sub_path, - directory.handle, - self.base.emit.sub_path, - .{}, - ); - } else { - // Create an LLD command line and invoke it. - var argv = std.ArrayList([]const u8).init(gpa); - defer argv.deinit(); - // We will invoke ourselves as a child process to gain access to LLD. - // This is necessary because LLD does not behave properly as a library - - // it calls exit() and does not reset all global data between invocations. - const linker_command = "ld.lld"; - try argv.appendSlice(&[_][]const u8{ comp.self_exe_path.?, linker_command }); - if (is_obj) { - try argv.append("-r"); - } - - try argv.append("--error-limit=0"); - - if (comp.sysroot) |sysroot| { - try argv.append(try std.fmt.allocPrint(arena, "--sysroot={s}", .{sysroot})); - } - - if (target_util.llvmMachineAbi(target)) |mabi| { - try argv.appendSlice(&.{ - "-mllvm", - try std.fmt.allocPrint(arena, "-target-abi={s}", .{mabi}), - }); - } - - try argv.appendSlice(&.{ - "-mllvm", - try std.fmt.allocPrint(arena, "-float-abi={s}", .{if (target.abi.float() == .hard) "hard" else "soft"}), - }); - - if (comp.config.lto != .none) { - switch (comp.root_mod.optimize_mode) { - .Debug => {}, - .ReleaseSmall => try argv.append("--lto-O2"), - .ReleaseFast, .ReleaseSafe => try argv.append("--lto-O3"), - } - } - switch (comp.root_mod.optimize_mode) { - .Debug => {}, - .ReleaseSmall => try argv.append("-O2"), - .ReleaseFast, .ReleaseSafe => try argv.append("-O3"), - } - - if (self.entry_name) |name| { - try argv.appendSlice(&.{ "--entry", name }); - } - - for (comp.force_undefined_symbols.keys()) |sym| { - try argv.append("-u"); - try argv.append(sym); - } - - switch (self.hash_style) { - .gnu => try argv.append("--hash-style=gnu"), - .sysv => try argv.append("--hash-style=sysv"), - .both => {}, // this is the default - } - - if (output_mode == .Exe) { - try argv.appendSlice(&.{ - "-z", - try std.fmt.allocPrint(arena, "stack-size={d}", .{self.base.stack_size}), - }); - } - - switch (self.base.build_id) { - .none => try argv.append("--build-id=none"), - .fast, .uuid, .sha1, .md5 => try argv.append(try std.fmt.allocPrint(arena, "--build-id={s}", .{ - @tagName(self.base.build_id), - })), - .hexstring => |hs| try argv.append(try std.fmt.allocPrint(arena, "--build-id=0x{s}", .{ - std.fmt.fmtSliceHexLower(hs.toSlice()), - })), - } - - try argv.append(try std.fmt.allocPrint(arena, "--image-base={d}", .{self.image_base})); - - if (self.linker_script) |linker_script| { - try argv.append("-T"); - try argv.append(linker_script); - } - - if (self.sort_section) |how| { - const arg = try std.fmt.allocPrint(arena, "--sort-section={s}", .{@tagName(how)}); - try argv.append(arg); - } - - if (self.base.gc_sections) { - try argv.append("--gc-sections"); - } - - if (self.base.print_gc_sections) { - try argv.append("--print-gc-sections"); - } - - if (self.print_icf_sections) { - try argv.append("--print-icf-sections"); - } - - if (self.print_map) { - try argv.append("--print-map"); - } - - if (comp.link_eh_frame_hdr) { - try argv.append("--eh-frame-hdr"); - } - - if (self.emit_relocs) { - try argv.append("--emit-relocs"); - } - - if (comp.config.rdynamic) { - try argv.append("--export-dynamic"); - } - - if (comp.config.debug_format == .strip) { - try argv.append("-s"); - } - - if (self.z_nodelete) { - try argv.append("-z"); - try argv.append("nodelete"); - } - if (self.z_notext) { - try argv.append("-z"); - try argv.append("notext"); - } - if (self.z_defs) { - try argv.append("-z"); - try argv.append("defs"); - } - if (self.z_origin) { - try argv.append("-z"); - try argv.append("origin"); - } - if (self.z_nocopyreloc) { - try argv.append("-z"); - try argv.append("nocopyreloc"); - } - if (self.z_now) { - // LLD defaults to -zlazy - try argv.append("-znow"); - } - if (!self.z_relro) { - // LLD defaults to -zrelro - try argv.append("-znorelro"); - } - if (self.z_common_page_size) |size| { - try argv.append("-z"); - try argv.append(try std.fmt.allocPrint(arena, "common-page-size={d}", .{size})); - } - if (self.z_max_page_size) |size| { - try argv.append("-z"); - try argv.append(try std.fmt.allocPrint(arena, "max-page-size={d}", .{size})); - } - - if (getLDMOption(target)) |ldm| { - try argv.append("-m"); - try argv.append(ldm); - } - - if (link_mode == .static) { - if (target.cpu.arch.isArm()) { - try argv.append("-Bstatic"); - } else { - try argv.append("-static"); - } - } else if (switch (target.os.tag) { - else => is_dyn_lib, - .haiku => is_exe_or_dyn_lib, - }) { - try argv.append("-shared"); - } - - if (comp.config.pie and output_mode == .Exe) { - try argv.append("-pie"); - } - - if (is_exe_or_dyn_lib and target.os.tag == .netbsd) { - // Add options to produce shared objects with only 2 PT_LOAD segments. - // NetBSD expects 2 PT_LOAD segments in a shared object, otherwise - // ld.elf_so fails loading dynamic libraries with "not found" error. - // See https://github.com/ziglang/zig/issues/9109 . - try argv.append("--no-rosegment"); - try argv.append("-znorelro"); - } - - try argv.append("-o"); - try argv.append(full_out_path); - - // csu prelude - const csu = try comp.getCrtPaths(arena); - if (csu.crt0) |p| try argv.append(try p.toString(arena)); - if (csu.crti) |p| try argv.append(try p.toString(arena)); - if (csu.crtbegin) |p| try argv.append(try p.toString(arena)); - - for (self.rpath_table.keys()) |rpath| { - try argv.appendSlice(&.{ "-rpath", rpath }); - } - - for (self.symbol_wrap_set.keys()) |symbol_name| { - try argv.appendSlice(&.{ "-wrap", symbol_name }); - } - - if (comp.config.link_libc) { - if (comp.libc_installation) |libc_installation| { - try argv.append("-L"); - try argv.append(libc_installation.crt_dir.?); - } - } - - if (have_dynamic_linker and - (comp.config.link_libc or comp.root_mod.resolved_target.is_explicit_dynamic_linker)) - { - if (target.dynamic_linker.get()) |dynamic_linker| { - try argv.append("-dynamic-linker"); - try argv.append(dynamic_linker); - } - } - - if (is_dyn_lib) { - if (self.soname) |soname| { - try argv.append("-soname"); - try argv.append(soname); - } - if (self.version_script) |version_script| { - try argv.append("-version-script"); - try argv.append(version_script); - } - if (self.allow_undefined_version) { - try argv.append("--undefined-version"); - } else { - try argv.append("--no-undefined-version"); - } - if (self.enable_new_dtags) |enable_new_dtags| { - if (enable_new_dtags) { - try argv.append("--enable-new-dtags"); - } else { - try argv.append("--disable-new-dtags"); - } - } - } - - // Positional arguments to the linker such as object files. - var whole_archive = false; - - for (self.base.comp.link_inputs) |link_input| switch (link_input) { - .res => unreachable, // Windows-only - .dso => continue, - .object, .archive => |obj| { - if (obj.must_link and !whole_archive) { - try argv.append("-whole-archive"); - whole_archive = true; - } else if (!obj.must_link and whole_archive) { - try argv.append("-no-whole-archive"); - whole_archive = false; - } - try argv.append(try obj.path.toString(arena)); - }, - .dso_exact => |dso_exact| { - assert(dso_exact.name[0] == ':'); - try argv.appendSlice(&.{ "-l", dso_exact.name }); - }, - }; - - if (whole_archive) { - try argv.append("-no-whole-archive"); - whole_archive = false; - } - - for (comp.c_object_table.keys()) |key| { - try argv.append(try key.status.success.object_path.toString(arena)); - } - - if (module_obj_path) |p| { - try argv.append(p); - } - - if (comp.tsan_lib) |lib| { - assert(comp.config.any_sanitize_thread); - try argv.append(try lib.full_object_path.toString(arena)); - } - - if (comp.fuzzer_lib) |lib| { - assert(comp.config.any_fuzz); - try argv.append(try lib.full_object_path.toString(arena)); - } - - if (ubsan_rt_path) |p| { - try argv.append(try p.toString(arena)); - } - - // Shared libraries. - if (is_exe_or_dyn_lib) { - // Worst-case, we need an --as-needed argument for every lib, as well - // as one before and one after. - try argv.ensureUnusedCapacity(2 * self.base.comp.link_inputs.len + 2); - argv.appendAssumeCapacity("--as-needed"); - var as_needed = true; - - for (self.base.comp.link_inputs) |link_input| switch (link_input) { - .res => unreachable, // Windows-only - .object, .archive, .dso_exact => continue, - .dso => |dso| { - const lib_as_needed = !dso.needed; - switch ((@as(u2, @intFromBool(lib_as_needed)) << 1) | @intFromBool(as_needed)) { - 0b00, 0b11 => {}, - 0b01 => { - argv.appendAssumeCapacity("--no-as-needed"); - as_needed = false; - }, - 0b10 => { - argv.appendAssumeCapacity("--as-needed"); - as_needed = true; - }, - } - - // By this time, we depend on these libs being dynamically linked - // libraries and not static libraries (the check for that needs to be earlier), - // but they could be full paths to .so files, in which case we - // want to avoid prepending "-l". - argv.appendAssumeCapacity(try dso.path.toString(arena)); - }, - }; - - if (!as_needed) { - argv.appendAssumeCapacity("--as-needed"); - as_needed = true; - } - - // libc++ dep - if (comp.config.link_libcpp) { - try argv.append(try comp.libcxxabi_static_lib.?.full_object_path.toString(arena)); - try argv.append(try comp.libcxx_static_lib.?.full_object_path.toString(arena)); - } - - // libunwind dep - if (comp.config.link_libunwind) { - try argv.append(try comp.libunwind_static_lib.?.full_object_path.toString(arena)); - } - - // libc dep - diags.flags.missing_libc = false; - if (comp.config.link_libc) { - if (comp.libc_installation != null) { - const needs_grouping = link_mode == .static; - if (needs_grouping) try argv.append("--start-group"); - try argv.appendSlice(target_util.libcFullLinkFlags(target)); - if (needs_grouping) try argv.append("--end-group"); - } else if (target.isGnuLibC()) { - for (glibc.libs) |lib| { - if (lib.removed_in) |rem_in| { - if (target.os.versionRange().gnuLibCVersion().?.order(rem_in) != .lt) continue; - } - - const lib_path = try std.fmt.allocPrint(arena, "{}{c}lib{s}.so.{d}", .{ - comp.glibc_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover, - }); - try argv.append(lib_path); - } - try argv.append(try comp.crtFileAsString(arena, "libc_nonshared.a")); - } else if (target.isMuslLibC()) { - try argv.append(try comp.crtFileAsString(arena, switch (link_mode) { - .static => "libc.a", - .dynamic => "libc.so", - })); - } else if (target.isFreeBSDLibC()) { - for (freebsd.libs) |lib| { - const lib_path = try std.fmt.allocPrint(arena, "{}{c}lib{s}.so.{d}", .{ - comp.freebsd_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover, - }); - try argv.append(lib_path); - } - } else if (target.isNetBSDLibC()) { - for (netbsd.libs) |lib| { - const lib_path = try std.fmt.allocPrint(arena, "{}{c}lib{s}.so.{d}", .{ - comp.netbsd_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover, - }); - try argv.append(lib_path); - } - } else { - diags.flags.missing_libc = true; - } - - if (comp.zigc_static_lib) |zigc| { - try argv.append(try zigc.full_object_path.toString(arena)); - } - } - } - - // compiler-rt. Since compiler_rt exports symbols like `memset`, it needs - // to be after the shared libraries, so they are picked up from the shared - // libraries, not libcompiler_rt. - if (compiler_rt_path) |p| { - try argv.append(try p.toString(arena)); - } - - // crt postlude - if (csu.crtend) |p| try argv.append(try p.toString(arena)); - if (csu.crtn) |p| try argv.append(try p.toString(arena)); - - if (self.base.allow_shlib_undefined) { - try argv.append("--allow-shlib-undefined"); - } - - switch (self.compress_debug_sections) { - .none => {}, - .zlib => try argv.append("--compress-debug-sections=zlib"), - .zstd => try argv.append("--compress-debug-sections=zstd"), - } - - if (self.bind_global_refs_locally) { - try argv.append("-Bsymbolic"); - } - - try link.spawnLld(comp, arena, argv.items); - } - - if (!self.base.disable_lld_caching) { - // Update the file with the digest. If it fails we can continue; it only - // means that the next invocation will have an unnecessary cache miss. - std.Build.Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| { - log.warn("failed to save linking hash digest file: {s}", .{@errorName(err)}); - }; - // Again failure here only means an unnecessary cache miss. - man.writeManifest() catch |err| { - log.warn("failed to write cache manifest when linking: {s}", .{@errorName(err)}); - }; - // We hang on to this lock so that the output file path can be used without - // other processes clobbering it. - self.base.lock = man.toOwnedLock(); - } -} - pub fn writeShdrTable(self: *Elf) !void { const gpa = self.base.comp.gpa; const target_endian = self.getTarget().cpu.arch.endian(); @@ -4121,85 +3432,6 @@ fn shdrTo32(shdr: elf.Elf64_Shdr) elf.Elf32_Shdr { }; } -fn getLDMOption(target: std.Target) ?[]const u8 { - // This should only return emulations understood by LLD's parseEmulation(). - return switch (target.cpu.arch) { - .aarch64 => switch (target.os.tag) { - .linux => "aarch64linux", - else => "aarch64elf", - }, - .aarch64_be => switch (target.os.tag) { - .linux => "aarch64linuxb", - else => "aarch64elfb", - }, - .amdgcn => "elf64_amdgpu", - .arm, .thumb => switch (target.os.tag) { - .linux => "armelf_linux_eabi", - else => "armelf", - }, - .armeb, .thumbeb => switch (target.os.tag) { - .linux => "armelfb_linux_eabi", - else => "armelfb", - }, - .hexagon => "hexagonelf", - .loongarch32 => "elf32loongarch", - .loongarch64 => "elf64loongarch", - .mips => switch (target.os.tag) { - .freebsd => "elf32btsmip_fbsd", - else => "elf32btsmip", - }, - .mipsel => switch (target.os.tag) { - .freebsd => "elf32ltsmip_fbsd", - else => "elf32ltsmip", - }, - .mips64 => switch (target.os.tag) { - .freebsd => switch (target.abi) { - .gnuabin32, .muslabin32 => "elf32btsmipn32_fbsd", - else => "elf64btsmip_fbsd", - }, - else => switch (target.abi) { - .gnuabin32, .muslabin32 => "elf32btsmipn32", - else => "elf64btsmip", - }, - }, - .mips64el => switch (target.os.tag) { - .freebsd => switch (target.abi) { - .gnuabin32, .muslabin32 => "elf32ltsmipn32_fbsd", - else => "elf64ltsmip_fbsd", - }, - else => switch (target.abi) { - .gnuabin32, .muslabin32 => "elf32ltsmipn32", - else => "elf64ltsmip", - }, - }, - .msp430 => "msp430elf", - .powerpc => switch (target.os.tag) { - .freebsd => "elf32ppc_fbsd", - .linux => "elf32ppclinux", - else => "elf32ppc", - }, - .powerpcle => switch (target.os.tag) { - .linux => "elf32lppclinux", - else => "elf32lppc", - }, - .powerpc64 => "elf64ppc", - .powerpc64le => "elf64lppc", - .riscv32 => "elf32lriscv", - .riscv64 => "elf64lriscv", - .s390x => "elf64_s390", - .sparc64 => "elf64_sparc", - .x86 => switch (target.os.tag) { - .freebsd => "elf_i386_fbsd", - else => "elf_i386", - }, - .x86_64 => switch (target.abi) { - .gnux32, .muslx32 => "elf32_x86_64", - else => "elf_x86_64", - }, - else => null, - }; -} - pub fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) { return actual_size +| (actual_size / ideal_factor); } @@ -5284,10 +4516,7 @@ const codegen = @import("../codegen.zig"); const dev = @import("../dev.zig"); const eh_frame = @import("Elf/eh_frame.zig"); const gc = @import("Elf/gc.zig"); -const glibc = @import("../libs/glibc.zig"); const musl = @import("../libs/musl.zig"); -const freebsd = @import("../libs/freebsd.zig"); -const netbsd = @import("../libs/netbsd.zig"); const link = @import("../link.zig"); const relocatable = @import("Elf/relocatable.zig"); const relocation = @import("Elf/relocation.zig"); diff --git a/src/link/Elf/ZigObject.zig b/src/link/Elf/ZigObject.zig index 49921089f7eba6978597bcaee28718322f054435..e377f3a9afdf3f239b74a9e3843867854d7ff1e2 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.flushZcu(pt); + try dwarf.flush(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 flushZcu() is to commit changes, so in theory, nothing should + // The point of flush() 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 flushZcu where we + // Perhaps it would make sense to save the code until flush 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 flushZcu + // anyerror needs to be deferred until flush 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 35821289cd036b69753856ea521bb4fa12019ea0..28da184495c1e918c51dc2880f8f132392331d26 100644 --- a/src/link/Goff.zig +++ b/src/link/Goff.zig @@ -46,7 +46,6 @@ pub fn createEmpty( .stack_size = options.stack_size orelse 0, .allow_shlib_undefined = options.allow_shlib_undefined orelse false, .file = null, - .disable_lld_caching = options.disable_lld_caching, .build_id = options.build_id, }, }; @@ -105,10 +104,6 @@ pub fn updateExports( } pub fn flush(self: *Goff, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void { - return self.flushZcu(arena, tid, prog_node); -} - -pub fn flushZcu(self: *Goff, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void { _ = self; _ = arena; _ = tid; diff --git a/src/link/Lld.zig b/src/link/Lld.zig new file mode 100644 index 0000000000000000000000000000000000000000..ba52d0c8d41f6ef7506abbde3c37769917ca7047 --- /dev/null +++ b/src/link/Lld.zig @@ -0,0 +1,2148 @@ +base: link.File, +disable_caching: bool, +ofmt: union(enum) { + elf: Elf, + coff: Coff, + wasm: Wasm, +}, + +const Coff = struct { + image_base: u64, + entry: link.File.OpenOptions.Entry, + pdb_out_path: ?[]const u8, + repro: bool, + tsaware: bool, + nxcompat: bool, + dynamicbase: bool, + /// TODO this and minor_subsystem_version should be combined into one property and left as + /// default or populated together. They should not be separate fields. + major_subsystem_version: u16, + minor_subsystem_version: u16, + lib_directories: []const Cache.Directory, + module_definition_file: ?[]const u8, + subsystem: ?std.Target.SubSystem, + /// These flags are populated by `codegen.llvm.updateExports` to allow us to guess the subsystem. + lld_export_flags: struct { + c_main: bool, + winmain: bool, + wwinmain: bool, + winmain_crt_startup: bool, + wwinmain_crt_startup: bool, + dllmain_crt_startup: bool, + }, + fn init(comp: *Compilation, options: link.File.OpenOptions) !Coff { + const target = comp.root_mod.resolved_target.result; + const output_mode = comp.config.output_mode; + return .{ + .image_base = options.image_base orelse switch (output_mode) { + .Exe => switch (target.cpu.arch) { + .aarch64, .x86_64 => 0x140000000, + .thumb, .x86 => 0x400000, + else => unreachable, + }, + .Lib => switch (target.cpu.arch) { + .aarch64, .x86_64 => 0x180000000, + .thumb, .x86 => 0x10000000, + else => unreachable, + }, + .Obj => 0, + }, + .entry = options.entry, + .pdb_out_path = options.pdb_out_path, + .repro = options.repro, + .tsaware = options.tsaware, + .nxcompat = options.nxcompat, + .dynamicbase = options.dynamicbase, + .major_subsystem_version = options.major_subsystem_version orelse 6, + .minor_subsystem_version = options.minor_subsystem_version orelse 0, + .lib_directories = options.lib_directories, + .module_definition_file = options.module_definition_file, + // Subsystem depends on the set of public symbol names from linked objects. + // See LinkerDriver::inferSubsystem from the LLD project for the flow chart. + .subsystem = options.subsystem, + // These flags are initially all `false`; the LLVM backend populates them when it learns about exports. + .lld_export_flags = .{ + .c_main = false, + .winmain = false, + .wwinmain = false, + .winmain_crt_startup = false, + .wwinmain_crt_startup = false, + .dllmain_crt_startup = false, + }, + }; + } +}; +pub const Elf = struct { + entry_name: ?[]const u8, + hash_style: HashStyle, + image_base: u64, + linker_script: ?[]const u8, + version_script: ?[]const u8, + sort_section: ?SortSection, + print_icf_sections: bool, + print_map: bool, + emit_relocs: bool, + z_nodelete: bool, + z_notext: bool, + z_defs: bool, + z_origin: bool, + z_nocopyreloc: bool, + z_now: bool, + z_relro: bool, + z_common_page_size: ?u64, + z_max_page_size: ?u64, + rpath_list: []const []const u8, + symbol_wrap_set: []const []const u8, + soname: ?[]const u8, + allow_undefined_version: bool, + enable_new_dtags: ?bool, + compress_debug_sections: CompressDebugSections, + bind_global_refs_locally: bool, + pub const HashStyle = enum { sysv, gnu, both }; + pub const SortSection = enum { name, alignment }; + pub const CompressDebugSections = enum { none, zlib, zstd }; + + fn init(comp: *Compilation, options: link.File.OpenOptions) !Elf { + const PtrWidth = enum { p32, p64 }; + const target = comp.root_mod.resolved_target.result; + const output_mode = comp.config.output_mode; + const is_dyn_lib = output_mode == .Lib and comp.config.link_mode == .dynamic; + const ptr_width: PtrWidth = switch (target.ptrBitWidth()) { + 0...32 => .p32, + 33...64 => .p64, + else => return error.UnsupportedElfArchitecture, + }; + const default_entry_name: []const u8 = switch (target.cpu.arch) { + .mips, .mipsel, .mips64, .mips64el => "__start", + else => "_start", + }; + return .{ + .entry_name = switch (options.entry) { + .disabled => null, + .default => if (output_mode != .Exe) null else default_entry_name, + .enabled => default_entry_name, + .named => |name| name, + }, + .hash_style = options.hash_style, + .image_base = b: { + if (is_dyn_lib) break :b 0; + if (output_mode == .Exe and comp.config.pie) break :b 0; + break :b options.image_base orelse switch (ptr_width) { + .p32 => 0x10000, + .p64 => 0x1000000, + }; + }, + .linker_script = options.linker_script, + .version_script = options.version_script, + .sort_section = options.sort_section, + .print_icf_sections = options.print_icf_sections, + .print_map = options.print_map, + .emit_relocs = options.emit_relocs, + .z_nodelete = options.z_nodelete, + .z_notext = options.z_notext, + .z_defs = options.z_defs, + .z_origin = options.z_origin, + .z_nocopyreloc = options.z_nocopyreloc, + .z_now = options.z_now, + .z_relro = options.z_relro, + .z_common_page_size = options.z_common_page_size, + .z_max_page_size = options.z_max_page_size, + .rpath_list = options.rpath_list, + .symbol_wrap_set = options.symbol_wrap_set.keys(), + .soname = options.soname, + .allow_undefined_version = options.allow_undefined_version, + .enable_new_dtags = options.enable_new_dtags, + .compress_debug_sections = options.compress_debug_sections, + .bind_global_refs_locally = options.bind_global_refs_locally, + }; + } +}; +const Wasm = struct { + /// Symbol name of the entry function to export + entry_name: ?[]const u8, + /// When true, will import the function table from the host environment. + import_table: bool, + /// When true, will export the function table to the host environment. + export_table: bool, + /// When defined, sets the initial memory size of the memory. + initial_memory: ?u64, + /// When defined, sets the maximum memory size of the memory. + max_memory: ?u64, + /// When defined, sets the start of the data section. + global_base: ?u64, + /// Set of *global* symbol names to export to the host environment. + export_symbol_names: []const []const u8, + /// When true, will allow undefined symbols + import_symbols: bool, + fn init(comp: *Compilation, options: link.File.OpenOptions) !Wasm { + const default_entry_name: []const u8 = switch (comp.config.wasi_exec_model) { + .reactor => "_initialize", + .command => "_start", + }; + return .{ + .entry_name = switch (options.entry) { + .disabled => null, + .default => if (comp.config.output_mode != .Exe) null else default_entry_name, + .enabled => default_entry_name, + .named => |name| name, + }, + .import_table = options.import_table, + .export_table = options.export_table, + .initial_memory = options.initial_memory, + .max_memory = options.max_memory, + .global_base = options.global_base, + .export_symbol_names = options.export_symbol_names, + .import_symbols = options.import_symbols, + }; + } +}; + +pub fn createEmpty( + arena: Allocator, + comp: *Compilation, + emit: Cache.Path, + options: link.File.OpenOptions, +) !*Lld { + const target = comp.root_mod.resolved_target.result; + const output_mode = comp.config.output_mode; + const optimize_mode = comp.root_mod.optimize_mode; + const is_native_os = comp.root_mod.resolved_target.is_native_os; + + const obj_file_ext: []const u8 = switch (target.ofmt) { + .coff => "obj", + .elf, .wasm => "o", + else => unreachable, + }; + const gc_sections: bool = options.gc_sections orelse switch (target.ofmt) { + .coff => optimize_mode != .Debug, + .elf => optimize_mode != .Debug and output_mode != .Obj, + .wasm => output_mode != .Obj, + else => unreachable, + }; + const stack_size: u64 = options.stack_size orelse default: { + if (target.ofmt == .wasm and target.os.tag == .freestanding) + break :default 1 * 1024 * 1024; // 1 MiB + break :default 16 * 1024 * 1024; // 16 MiB + }; + + const lld = try arena.create(Lld); + lld.* = .{ + .base = .{ + .tag = .lld, + .comp = comp, + .emit = emit, + .zcu_object_sub_path = try allocPrint(arena, "{s}.{s}", .{ emit.sub_path, obj_file_ext }), + .gc_sections = gc_sections, + .print_gc_sections = options.print_gc_sections, + .stack_size = stack_size, + .allow_shlib_undefined = options.allow_shlib_undefined orelse !is_native_os, + .file = null, + .build_id = options.build_id, + }, + .disable_caching = options.disable_lld_caching, + .ofmt = switch (target.ofmt) { + .coff => .{ .coff = try .init(comp, options) }, + .elf => .{ .elf = try .init(comp, options) }, + .wasm => .{ .wasm = try .init(comp, options) }, + else => unreachable, + }, + }; + return lld; +} +pub fn deinit(lld: *Lld) void { + _ = lld; +} +pub fn flush( + lld: *Lld, + arena: Allocator, + tid: Zcu.PerThread.Id, + prog_node: std.Progress.Node, +) link.File.FlushError!void { + dev.check(.lld_linker); + _ = tid; + + const tracy = trace(@src()); + defer tracy.end(); + + const sub_prog_node = prog_node.start("LLD Link", 0); + defer sub_prog_node.end(); + + const comp = lld.base.comp; + const result = if (comp.config.output_mode == .Lib and comp.config.link_mode == .static) r: { + break :r linkAsArchive(lld, arena); + } else switch (lld.ofmt) { + .coff => coffLink(lld, arena), + .elf => elfLink(lld, arena), + .wasm => wasmLink(lld, arena), + }; + result catch |err| switch (err) { + error.OutOfMemory, error.LinkFailure => |e| return e, + else => |e| return lld.base.comp.link_diags.fail("failed to link with LLD: {s}", .{@errorName(e)}), + }; +} + +fn linkAsArchive(lld: *Lld, arena: Allocator) !void { + const base = &lld.base; + const comp = base.comp; + const directory = base.emit.root_dir; // Just an alias to make it shorter to type. + const full_out_path = try directory.join(arena, &[_][]const u8{base.emit.sub_path}); + const full_out_path_z = try arena.dupeZ(u8, full_out_path); + const opt_zcu = comp.zcu; + + // 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: { + 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; + + log.debug("zcu_obj_path={s}", .{if (zcu_obj_path) |s| s else "(null)"}); + + const compiler_rt_path: ?Cache.Path = if (comp.compiler_rt_strat == .obj) + comp.compiler_rt_obj.?.full_object_path + else + null; + + const ubsan_rt_path: ?Cache.Path = if (comp.ubsan_rt_strat == .obj) + comp.ubsan_rt_obj.?.full_object_path + else + null; + + // This function follows the same pattern as link.Elf.linkWithLLD so if you want some + // insight as to what's going on here you can read that function body which is more + // well-commented. + + const id_symlink_basename = "llvm-ar.id"; + + var man: Cache.Manifest = undefined; + defer if (!lld.disable_caching) man.deinit(); + + const link_inputs = comp.link_inputs; + + var digest: [Cache.hex_digest_len]u8 = undefined; + + if (!lld.disable_caching) { + man = comp.cache_parent.obtain(); + + // We are about to obtain this lock, so here we give other processes a chance first. + base.releaseLock(); + + try link.hashInputs(&man, link_inputs); + + for (comp.c_object_table.keys()) |key| { + _ = try man.addFilePath(key.status.success.object_path, null); + } + for (comp.win32_resource_table.keys()) |key| { + _ = try man.addFile(key.status.success.res_path, null); + } + try man.addOptionalFile(zcu_obj_path); + try man.addOptionalFilePath(compiler_rt_path); + try man.addOptionalFilePath(ubsan_rt_path); + + // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock. + _ = try man.hit(); + digest = man.final(); + + var prev_digest_buf: [digest.len]u8 = undefined; + const prev_digest: []u8 = Cache.readSmallFile( + directory.handle, + id_symlink_basename, + &prev_digest_buf, + ) catch |err| b: { + log.debug("archive new_digest={s} readFile error: {s}", .{ std.fmt.fmtSliceHexLower(&digest), @errorName(err) }); + break :b prev_digest_buf[0..0]; + }; + if (mem.eql(u8, prev_digest, &digest)) { + log.debug("archive digest={s} match - skipping invocation", .{std.fmt.fmtSliceHexLower(&digest)}); + base.lock = man.toOwnedLock(); + return; + } + + // We are about to change the output file to be different, so we invalidate the build hash now. + directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) { + error.FileNotFound => {}, + else => |e| return e, + }; + } + + var object_files: std.ArrayListUnmanaged([*:0]const u8) = .empty; + + try object_files.ensureUnusedCapacity(arena, link_inputs.len); + for (link_inputs) |input| { + object_files.appendAssumeCapacity(try input.path().?.toStringZ(arena)); + } + + try object_files.ensureUnusedCapacity(arena, comp.c_object_table.count() + + comp.win32_resource_table.count() + 2); + + for (comp.c_object_table.keys()) |key| { + object_files.appendAssumeCapacity(try key.status.success.object_path.toStringZ(arena)); + } + for (comp.win32_resource_table.keys()) |key| { + object_files.appendAssumeCapacity(try arena.dupeZ(u8, key.status.success.res_path)); + } + if (zcu_obj_path) |p| object_files.appendAssumeCapacity(try arena.dupeZ(u8, p)); + if (compiler_rt_path) |p| object_files.appendAssumeCapacity(try p.toStringZ(arena)); + if (ubsan_rt_path) |p| object_files.appendAssumeCapacity(try p.toStringZ(arena)); + + if (comp.verbose_link) { + std.debug.print("ar rcs {s}", .{full_out_path_z}); + for (object_files.items) |arg| { + std.debug.print(" {s}", .{arg}); + } + std.debug.print("\n", .{}); + } + + const llvm_bindings = @import("../codegen/llvm/bindings.zig"); + const llvm = @import("../codegen/llvm.zig"); + const target = comp.root_mod.resolved_target.result; + llvm.initializeLLVMTarget(target.cpu.arch); + const bad = llvm_bindings.WriteArchive( + full_out_path_z, + object_files.items.ptr, + object_files.items.len, + switch (target.os.tag) { + .aix => .AIXBIG, + .windows => .COFF, + else => if (target.os.tag.isDarwin()) .DARWIN else .GNU, + }, + ); + if (bad) return error.UnableToWriteArchive; + + if (!lld.disable_caching) { + Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| { + log.warn("failed to save archive hash digest file: {s}", .{@errorName(err)}); + }; + + if (man.have_exclusive_lock) { + man.writeManifest() catch |err| { + log.warn("failed to write cache manifest when archiving: {s}", .{@errorName(err)}); + }; + } + + base.lock = man.toOwnedLock(); + } +} + +fn coffLink(lld: *Lld, arena: Allocator) !void { + const comp = lld.base.comp; + const gpa = comp.gpa; + const base = &lld.base; + const coff = &lld.ofmt.coff; + + const directory = base.emit.root_dir; // Just an alias to make it shorter to type. + const full_out_path = try directory.join(arena, &[_][]const u8{base.emit.sub_path}); + + // 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) p: { + if (fs.path.dirname(full_out_path)) |dirname| { + break :p try fs.path.join(arena, &.{ dirname, base.zcu_object_sub_path.? }); + } else { + break :p base.zcu_object_sub_path.?; + } + } else null; + + const is_lib = comp.config.output_mode == .Lib; + const is_dyn_lib = comp.config.link_mode == .dynamic and is_lib; + const is_exe_or_dyn_lib = is_dyn_lib or comp.config.output_mode == .Exe; + const link_in_crt = comp.config.link_libc and is_exe_or_dyn_lib; + const target = comp.root_mod.resolved_target.result; + const optimize_mode = comp.root_mod.optimize_mode; + const entry_name: ?[]const u8 = switch (coff.entry) { + // This logic isn't quite right for disabled or enabled. No point in fixing it + // when the goal is to eliminate dependency on LLD anyway. + // https://github.com/ziglang/zig/issues/17751 + .disabled, .default, .enabled => null, + .named => |name| name, + }; + + // See link/Elf.zig for comments on how this mechanism works. + const id_symlink_basename = "lld.id"; + + var man: Cache.Manifest = undefined; + defer if (!lld.disable_caching) man.deinit(); + + var digest: [Cache.hex_digest_len]u8 = undefined; + + if (!lld.disable_caching) { + man = comp.cache_parent.obtain(); + base.releaseLock(); + + comptime assert(Compilation.link_hash_implementation_version == 14); + + try link.hashInputs(&man, comp.link_inputs); + for (comp.c_object_table.keys()) |key| { + _ = try man.addFilePath(key.status.success.object_path, null); + } + for (comp.win32_resource_table.keys()) |key| { + _ = try man.addFile(key.status.success.res_path, null); + } + try man.addOptionalFile(module_obj_path); + man.hash.addOptionalBytes(entry_name); + man.hash.add(base.stack_size); + man.hash.add(coff.image_base); + man.hash.add(base.build_id); + { + // TODO remove this, libraries must instead be resolved by the frontend. + for (coff.lib_directories) |lib_directory| man.hash.addOptionalBytes(lib_directory.path); + } + man.hash.add(comp.skip_linker_dependencies); + if (comp.config.link_libc) { + man.hash.add(comp.libc_installation != null); + if (comp.libc_installation) |libc_installation| { + man.hash.addBytes(libc_installation.crt_dir.?); + if (target.abi == .msvc or target.abi == .itanium) { + man.hash.addBytes(libc_installation.msvc_lib_dir.?); + man.hash.addBytes(libc_installation.kernel32_lib_dir.?); + } + } + } + man.hash.addListOfBytes(comp.windows_libs.keys()); + man.hash.addListOfBytes(comp.force_undefined_symbols.keys()); + man.hash.addOptional(coff.subsystem); + man.hash.add(comp.config.is_test); + man.hash.add(coff.tsaware); + man.hash.add(coff.nxcompat); + man.hash.add(coff.dynamicbase); + man.hash.add(base.allow_shlib_undefined); + // strip does not need to go into the linker hash because it is part of the hash namespace + man.hash.add(coff.major_subsystem_version); + man.hash.add(coff.minor_subsystem_version); + man.hash.add(coff.repro); + man.hash.addOptional(comp.version); + try man.addOptionalFile(coff.module_definition_file); + + // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock. + _ = try man.hit(); + digest = man.final(); + var prev_digest_buf: [digest.len]u8 = undefined; + const prev_digest: []u8 = Cache.readSmallFile( + directory.handle, + id_symlink_basename, + &prev_digest_buf, + ) catch |err| blk: { + log.debug("COFF LLD new_digest={s} error: {s}", .{ std.fmt.fmtSliceHexLower(&digest), @errorName(err) }); + // Handle this as a cache miss. + break :blk prev_digest_buf[0..0]; + }; + if (mem.eql(u8, prev_digest, &digest)) { + log.debug("COFF LLD digest={s} match - skipping invocation", .{std.fmt.fmtSliceHexLower(&digest)}); + // Hot diggity dog! The output binary is already there. + base.lock = man.toOwnedLock(); + return; + } + log.debug("COFF LLD prev_digest={s} new_digest={s}", .{ std.fmt.fmtSliceHexLower(prev_digest), std.fmt.fmtSliceHexLower(&digest) }); + + // We are about to change the output file to be different, so we invalidate the build hash now. + directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) { + error.FileNotFound => {}, + else => |e| return e, + }; + } + + if (comp.config.output_mode == .Obj) { + // LLD's COFF driver does not support the equivalent of `-r` so we do a simple file copy + // here. TODO: think carefully about how we can avoid this redundant operation when doing + // build-obj. See also the corresponding TODO in linkAsArchive. + const the_object_path = blk: { + if (link.firstObjectInput(comp.link_inputs)) |obj| break :blk obj.path; + + if (comp.c_object_table.count() != 0) + break :blk comp.c_object_table.keys()[0].status.success.object_path; + + if (module_obj_path) |p| + break :blk Cache.Path.initCwd(p); + + // TODO I think this is unreachable. Audit this situation when solving the above TODO + // regarding eliding redundant object -> object transformations. + return error.NoObjectsToLink; + }; + try std.fs.Dir.copyFile( + the_object_path.root_dir.handle, + the_object_path.sub_path, + directory.handle, + base.emit.sub_path, + .{}, + ); + } else { + // Create an LLD command line and invoke it. + var argv = std.ArrayList([]const u8).init(gpa); + defer argv.deinit(); + // We will invoke ourselves as a child process to gain access to LLD. + // This is necessary because LLD does not behave properly as a library - + // it calls exit() and does not reset all global data between invocations. + const linker_command = "lld-link"; + try argv.appendSlice(&[_][]const u8{ comp.self_exe_path.?, linker_command }); + + if (target.isMinGW()) { + try argv.append("-lldmingw"); + } + + try argv.append("-ERRORLIMIT:0"); + try argv.append("-NOLOGO"); + if (comp.config.debug_format != .strip) { + try argv.append("-DEBUG"); + + const out_ext = std.fs.path.extension(full_out_path); + const out_pdb = coff.pdb_out_path orelse try allocPrint(arena, "{s}.pdb", .{ + full_out_path[0 .. full_out_path.len - out_ext.len], + }); + const out_pdb_basename = std.fs.path.basename(out_pdb); + + try argv.append(try allocPrint(arena, "-PDB:{s}", .{out_pdb})); + try argv.append(try allocPrint(arena, "-PDBALTPATH:{s}", .{out_pdb_basename})); + } + if (comp.version) |version| { + try argv.append(try allocPrint(arena, "-VERSION:{}.{}", .{ version.major, version.minor })); + } + + if (target_util.llvmMachineAbi(target)) |mabi| { + try argv.append(try allocPrint(arena, "-MLLVM:-target-abi={s}", .{mabi})); + } + + try argv.append(try allocPrint(arena, "-MLLVM:-float-abi={s}", .{if (target.abi.float() == .hard) "hard" else "soft"})); + + if (comp.config.lto != .none) { + switch (optimize_mode) { + .Debug => {}, + .ReleaseSmall => try argv.append("-OPT:lldlto=2"), + .ReleaseFast, .ReleaseSafe => try argv.append("-OPT:lldlto=3"), + } + } + if (comp.config.output_mode == .Exe) { + try argv.append(try allocPrint(arena, "-STACK:{d}", .{base.stack_size})); + } + try argv.append(try allocPrint(arena, "-BASE:{d}", .{coff.image_base})); + + switch (base.build_id) { + .none => try argv.append("-BUILD-ID:NO"), + .fast => try argv.append("-BUILD-ID"), + .uuid, .sha1, .md5, .hexstring => {}, + } + + if (target.cpu.arch == .x86) { + try argv.append("-MACHINE:X86"); + } else if (target.cpu.arch == .x86_64) { + try argv.append("-MACHINE:X64"); + } else if (target.cpu.arch == .thumb) { + try argv.append("-MACHINE:ARM"); + } else if (target.cpu.arch == .aarch64) { + try argv.append("-MACHINE:ARM64"); + } + + for (comp.force_undefined_symbols.keys()) |symbol| { + try argv.append(try allocPrint(arena, "-INCLUDE:{s}", .{symbol})); + } + + if (is_dyn_lib) { + try argv.append("-DLL"); + } + + if (entry_name) |name| { + try argv.append(try allocPrint(arena, "-ENTRY:{s}", .{name})); + } + + if (coff.repro) { + try argv.append("-BREPRO"); + } + + if (coff.tsaware) { + try argv.append("-tsaware"); + } + if (coff.nxcompat) { + try argv.append("-nxcompat"); + } + if (!coff.dynamicbase) { + try argv.append("-dynamicbase:NO"); + } + if (base.allow_shlib_undefined) { + try argv.append("-FORCE:UNRESOLVED"); + } + + try argv.append(try allocPrint(arena, "-OUT:{s}", .{full_out_path})); + + if (comp.implib_emit) |emit| { + const implib_out_path = try emit.root_dir.join(arena, &[_][]const u8{emit.sub_path}); + try argv.append(try allocPrint(arena, "-IMPLIB:{s}", .{implib_out_path})); + } + + if (comp.config.link_libc) { + if (comp.libc_installation) |libc_installation| { + try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{libc_installation.crt_dir.?})); + + if (target.abi == .msvc or target.abi == .itanium) { + try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{libc_installation.msvc_lib_dir.?})); + try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{libc_installation.kernel32_lib_dir.?})); + } + } + } + + for (coff.lib_directories) |lib_directory| { + try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{lib_directory.path orelse "."})); + } + + try argv.ensureUnusedCapacity(comp.link_inputs.len); + for (comp.link_inputs) |link_input| switch (link_input) { + .dso_exact => unreachable, // not applicable to PE/COFF + inline .dso, .res => |x| { + argv.appendAssumeCapacity(try x.path.toString(arena)); + }, + .object, .archive => |obj| { + if (obj.must_link) { + argv.appendAssumeCapacity(try allocPrint(arena, "-WHOLEARCHIVE:{}", .{@as(Cache.Path, obj.path)})); + } else { + argv.appendAssumeCapacity(try obj.path.toString(arena)); + } + }, + }; + + for (comp.c_object_table.keys()) |key| { + try argv.append(try key.status.success.object_path.toString(arena)); + } + + for (comp.win32_resource_table.keys()) |key| { + try argv.append(key.status.success.res_path); + } + + if (module_obj_path) |p| { + try argv.append(p); + } + + if (coff.module_definition_file) |def| { + try argv.append(try allocPrint(arena, "-DEF:{s}", .{def})); + } + + const resolved_subsystem: ?std.Target.SubSystem = blk: { + if (coff.subsystem) |explicit| break :blk explicit; + switch (target.os.tag) { + .windows => { + if (comp.zcu != null) { + if (coff.lld_export_flags.dllmain_crt_startup or is_dyn_lib) + break :blk null; + 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 (coff.lld_export_flags.winmain or coff.lld_export_flags.wwinmain) + break :blk .Windows; + } + }, + .uefi => break :blk .EfiApplication, + else => {}, + } + break :blk null; + }; + + const Mode = enum { uefi, win32 }; + const mode: Mode = mode: { + if (resolved_subsystem) |subsystem| { + const subsystem_suffix = try allocPrint(arena, ",{d}.{d}", .{ + coff.major_subsystem_version, coff.minor_subsystem_version, + }); + + switch (subsystem) { + .Console => { + try argv.append(try allocPrint(arena, "-SUBSYSTEM:console{s}", .{ + subsystem_suffix, + })); + break :mode .win32; + }, + .EfiApplication => { + try argv.append(try allocPrint(arena, "-SUBSYSTEM:efi_application{s}", .{ + subsystem_suffix, + })); + break :mode .uefi; + }, + .EfiBootServiceDriver => { + try argv.append(try allocPrint(arena, "-SUBSYSTEM:efi_boot_service_driver{s}", .{ + subsystem_suffix, + })); + break :mode .uefi; + }, + .EfiRom => { + try argv.append(try allocPrint(arena, "-SUBSYSTEM:efi_rom{s}", .{ + subsystem_suffix, + })); + break :mode .uefi; + }, + .EfiRuntimeDriver => { + try argv.append(try allocPrint(arena, "-SUBSYSTEM:efi_runtime_driver{s}", .{ + subsystem_suffix, + })); + break :mode .uefi; + }, + .Native => { + try argv.append(try allocPrint(arena, "-SUBSYSTEM:native{s}", .{ + subsystem_suffix, + })); + break :mode .win32; + }, + .Posix => { + try argv.append(try allocPrint(arena, "-SUBSYSTEM:posix{s}", .{ + subsystem_suffix, + })); + break :mode .win32; + }, + .Windows => { + try argv.append(try allocPrint(arena, "-SUBSYSTEM:windows{s}", .{ + subsystem_suffix, + })); + break :mode .win32; + }, + } + } else if (target.os.tag == .uefi) { + break :mode .uefi; + } else { + break :mode .win32; + } + }; + + switch (mode) { + .uefi => try argv.appendSlice(&[_][]const u8{ + "-BASE:0", + "-ENTRY:EfiMain", + "-OPT:REF", + "-SAFESEH:NO", + "-MERGE:.rdata=.data", + "-NODEFAULTLIB", + "-SECTION:.xdata,D", + }), + .win32 => { + if (link_in_crt) { + if (target.abi.isGnu()) { + if (target.cpu.arch == .x86) { + try argv.append("-ALTERNATENAME:__image_base__=___ImageBase"); + } else { + try argv.append("-ALTERNATENAME:__image_base__=__ImageBase"); + } + + if (is_dyn_lib) { + try argv.append(try comp.crtFileAsString(arena, "dllcrt2.obj")); + if (target.cpu.arch == .x86) { + try argv.append("-ALTERNATENAME:__DllMainCRTStartup@12=_DllMainCRTStartup@12"); + } else { + try argv.append("-ALTERNATENAME:_DllMainCRTStartup=DllMainCRTStartup"); + } + } else { + try argv.append(try comp.crtFileAsString(arena, "crt2.obj")); + } + + try argv.append(try comp.crtFileAsString(arena, "libmingw32.lib")); + } else { + try argv.append(switch (comp.config.link_mode) { + .static => "libcmt.lib", + .dynamic => "msvcrt.lib", + }); + + const lib_str = switch (comp.config.link_mode) { + .static => "lib", + .dynamic => "", + }; + try argv.append(try allocPrint(arena, "{s}vcruntime.lib", .{lib_str})); + try argv.append(try allocPrint(arena, "{s}ucrt.lib", .{lib_str})); + + //Visual C++ 2015 Conformance Changes + //https://msdn.microsoft.com/en-us/library/bb531344.aspx + try argv.append("legacy_stdio_definitions.lib"); + + // msvcrt depends on kernel32 and ntdll + try argv.append("kernel32.lib"); + try argv.append("ntdll.lib"); + } + } else { + try argv.append("-NODEFAULTLIB"); + if (!is_lib and entry_name == null) { + if (comp.zcu != null) { + if (coff.lld_export_flags.winmain_crt_startup) { + try argv.append("-ENTRY:WinMainCRTStartup"); + } else { + try argv.append("-ENTRY:wWinMainCRTStartup"); + } + } else { + try argv.append("-ENTRY:wWinMainCRTStartup"); + } + } + } + }, + } + + if (comp.config.link_libc and link_in_crt) { + if (comp.zigc_static_lib) |zigc| { + try argv.append(try zigc.full_object_path.toString(arena)); + } + } + + // libc++ dep + if (comp.config.link_libcpp) { + try argv.append(try comp.libcxxabi_static_lib.?.full_object_path.toString(arena)); + try argv.append(try comp.libcxx_static_lib.?.full_object_path.toString(arena)); + } + + // libunwind dep + if (comp.config.link_libunwind) { + try argv.append(try comp.libunwind_static_lib.?.full_object_path.toString(arena)); + } + + if (comp.config.any_fuzz) { + try argv.append(try comp.fuzzer_lib.?.full_object_path.toString(arena)); + } + + const ubsan_rt_path: ?Cache.Path = blk: { + if (comp.ubsan_rt_lib) |x| break :blk x.full_object_path; + if (comp.ubsan_rt_obj) |x| break :blk x.full_object_path; + break :blk null; + }; + if (ubsan_rt_path) |path| { + try argv.append(try path.toString(arena)); + } + + if (is_exe_or_dyn_lib and !comp.skip_linker_dependencies) { + // MSVC compiler_rt is missing some stuff, so we build it unconditionally but + // and rely on weak linkage to allow MSVC compiler_rt functions to override ours. + if (comp.compiler_rt_obj) |obj| try argv.append(try obj.full_object_path.toString(arena)); + if (comp.compiler_rt_lib) |lib| try argv.append(try lib.full_object_path.toString(arena)); + } + + try argv.ensureUnusedCapacity(comp.windows_libs.count()); + for (comp.windows_libs.keys()) |key| { + const lib_basename = try allocPrint(arena, "{s}.lib", .{key}); + if (comp.crt_files.get(lib_basename)) |crt_file| { + argv.appendAssumeCapacity(try crt_file.full_object_path.toString(arena)); + continue; + } + if (try findLib(arena, lib_basename, coff.lib_directories)) |full_path| { + argv.appendAssumeCapacity(full_path); + continue; + } + if (target.abi.isGnu()) { + const fallback_name = try allocPrint(arena, "lib{s}.dll.a", .{key}); + if (try findLib(arena, fallback_name, coff.lib_directories)) |full_path| { + argv.appendAssumeCapacity(full_path); + continue; + } + } + if (target.abi == .msvc or target.abi == .itanium) { + argv.appendAssumeCapacity(lib_basename); + continue; + } + + log.err("DLL import library for -l{s} not found", .{key}); + return error.DllImportLibraryNotFound; + } + + try spawnLld(comp, arena, argv.items); + } + + if (!lld.disable_caching) { + // Update the file with the digest. If it fails we can continue; it only + // means that the next invocation will have an unnecessary cache miss. + Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| { + log.warn("failed to save linking hash digest file: {s}", .{@errorName(err)}); + }; + // Again failure here only means an unnecessary cache miss. + man.writeManifest() catch |err| { + log.warn("failed to write cache manifest when linking: {s}", .{@errorName(err)}); + }; + // We hang on to this lock so that the output file path can be used without + // other processes clobbering it. + base.lock = man.toOwnedLock(); + } +} +fn findLib(arena: Allocator, name: []const u8, lib_directories: []const Cache.Directory) !?[]const u8 { + for (lib_directories) |lib_directory| { + lib_directory.handle.access(name, .{}) catch |err| switch (err) { + error.FileNotFound => continue, + else => |e| return e, + }; + return try lib_directory.join(arena, &.{name}); + } + return null; +} + +fn elfLink(lld: *Lld, arena: Allocator) !void { + const comp = lld.base.comp; + const gpa = comp.gpa; + const diags = &comp.link_diags; + const base = &lld.base; + const elf = &lld.ofmt.elf; + + const directory = base.emit.root_dir; // Just an alias to make it shorter to type. + const full_out_path = try directory.join(arena, &[_][]const u8{base.emit.sub_path}); + + // 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) p: { + if (fs.path.dirname(full_out_path)) |dirname| { + break :p try fs.path.join(arena, &.{ dirname, base.zcu_object_sub_path.? }); + } else { + break :p base.zcu_object_sub_path.?; + } + } else null; + + const output_mode = comp.config.output_mode; + const is_obj = output_mode == .Obj; + const is_lib = output_mode == .Lib; + const link_mode = comp.config.link_mode; + const is_dyn_lib = link_mode == .dynamic and is_lib; + const is_exe_or_dyn_lib = is_dyn_lib or output_mode == .Exe; + const have_dynamic_linker = link_mode == .dynamic and is_exe_or_dyn_lib; + const target = comp.root_mod.resolved_target.result; + const compiler_rt_path: ?Cache.Path = blk: { + if (comp.compiler_rt_lib) |x| break :blk x.full_object_path; + if (comp.compiler_rt_obj) |x| break :blk x.full_object_path; + break :blk null; + }; + const ubsan_rt_path: ?Cache.Path = blk: { + if (comp.ubsan_rt_lib) |x| break :blk x.full_object_path; + if (comp.ubsan_rt_obj) |x| break :blk x.full_object_path; + break :blk null; + }; + + // Here we want to determine whether we can save time by not invoking LLD when the + // output is unchanged. None of the linker options or the object files that are being + // linked are in the hash that namespaces the directory we are outputting to. Therefore, + // we must hash those now, and the resulting digest will form the "id" of the linking + // job we are about to perform. + // After a successful link, we store the id in the metadata of a symlink named "lld.id" in + // the artifact directory. So, now, we check if this symlink exists, and if it matches + // our digest. If so, we can skip linking. Otherwise, we proceed with invoking LLD. + const id_symlink_basename = "lld.id"; + + var man: std.Build.Cache.Manifest = undefined; + defer if (!lld.disable_caching) man.deinit(); + + var digest: [std.Build.Cache.hex_digest_len]u8 = undefined; + + if (!lld.disable_caching) { + man = comp.cache_parent.obtain(); + + // We are about to obtain this lock, so here we give other processes a chance first. + base.releaseLock(); + + comptime assert(Compilation.link_hash_implementation_version == 14); + + try man.addOptionalFile(elf.linker_script); + try man.addOptionalFile(elf.version_script); + man.hash.add(elf.allow_undefined_version); + man.hash.addOptional(elf.enable_new_dtags); + try link.hashInputs(&man, comp.link_inputs); + for (comp.c_object_table.keys()) |key| { + _ = try man.addFilePath(key.status.success.object_path, null); + } + try man.addOptionalFile(module_obj_path); + try man.addOptionalFilePath(compiler_rt_path); + try man.addOptionalFilePath(ubsan_rt_path); + try man.addOptionalFilePath(if (comp.tsan_lib) |l| l.full_object_path else null); + try man.addOptionalFilePath(if (comp.fuzzer_lib) |l| l.full_object_path else null); + + // We can skip hashing libc and libc++ components that we are in charge of building from Zig + // installation sources because they are always a product of the compiler version + target information. + man.hash.addOptionalBytes(elf.entry_name); + man.hash.add(elf.image_base); + man.hash.add(base.gc_sections); + man.hash.addOptional(elf.sort_section); + man.hash.add(comp.link_eh_frame_hdr); + man.hash.add(elf.emit_relocs); + man.hash.add(comp.config.rdynamic); + man.hash.addListOfBytes(elf.rpath_list); + if (output_mode == .Exe) { + man.hash.add(base.stack_size); + } + man.hash.add(base.build_id); + man.hash.addListOfBytes(elf.symbol_wrap_set); + man.hash.add(comp.skip_linker_dependencies); + man.hash.add(elf.z_nodelete); + man.hash.add(elf.z_notext); + man.hash.add(elf.z_defs); + man.hash.add(elf.z_origin); + man.hash.add(elf.z_nocopyreloc); + man.hash.add(elf.z_now); + man.hash.add(elf.z_relro); + man.hash.add(elf.z_common_page_size orelse 0); + man.hash.add(elf.z_max_page_size orelse 0); + man.hash.add(elf.hash_style); + // strip does not need to go into the linker hash because it is part of the hash namespace + if (comp.config.link_libc) { + man.hash.add(comp.libc_installation != null); + if (comp.libc_installation) |libc_installation| { + man.hash.addBytes(libc_installation.crt_dir.?); + } + } + if (have_dynamic_linker) { + man.hash.addOptionalBytes(target.dynamic_linker.get()); + } + man.hash.addOptionalBytes(elf.soname); + man.hash.addOptional(comp.version); + man.hash.addListOfBytes(comp.force_undefined_symbols.keys()); + man.hash.add(base.allow_shlib_undefined); + man.hash.add(elf.bind_global_refs_locally); + man.hash.add(elf.compress_debug_sections); + man.hash.add(comp.config.any_sanitize_thread); + man.hash.add(comp.config.any_fuzz); + man.hash.addOptionalBytes(comp.sysroot); + + // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock. + _ = try man.hit(); + digest = man.final(); + + var prev_digest_buf: [digest.len]u8 = undefined; + const prev_digest: []u8 = std.Build.Cache.readSmallFile( + directory.handle, + id_symlink_basename, + &prev_digest_buf, + ) catch |err| blk: { + log.debug("ELF LLD new_digest={s} error: {s}", .{ std.fmt.fmtSliceHexLower(&digest), @errorName(err) }); + // Handle this as a cache miss. + break :blk prev_digest_buf[0..0]; + }; + if (mem.eql(u8, prev_digest, &digest)) { + log.debug("ELF LLD digest={s} match - skipping invocation", .{std.fmt.fmtSliceHexLower(&digest)}); + // Hot diggity dog! The output binary is already there. + base.lock = man.toOwnedLock(); + return; + } + log.debug("ELF LLD prev_digest={s} new_digest={s}", .{ std.fmt.fmtSliceHexLower(prev_digest), std.fmt.fmtSliceHexLower(&digest) }); + + // We are about to change the output file to be different, so we invalidate the build hash now. + directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) { + error.FileNotFound => {}, + else => |e| return e, + }; + } + + // Due to a deficiency in LLD, we need to special-case BPF to a simple file + // copy when generating relocatables. Normally, we would expect `lld -r` to work. + // However, because LLD wants to resolve BPF relocations which it shouldn't, it fails + // before even generating the relocatable. + // + // For m68k, we go through this path because LLD doesn't support it yet, but LLVM can + // produce usable object files. + if (output_mode == .Obj and + (comp.config.lto != .none or + target.cpu.arch.isBpf() or + target.cpu.arch == .lanai or + target.cpu.arch == .m68k or + target.cpu.arch.isSPARC() or + target.cpu.arch == .ve or + target.cpu.arch == .xcore)) + { + // In this case we must do a simple file copy + // here. TODO: think carefully about how we can avoid this redundant operation when doing + // build-obj. See also the corresponding TODO in linkAsArchive. + const the_object_path = blk: { + if (link.firstObjectInput(comp.link_inputs)) |obj| break :blk obj.path; + + if (comp.c_object_table.count() != 0) + break :blk comp.c_object_table.keys()[0].status.success.object_path; + + if (module_obj_path) |p| + break :blk Cache.Path.initCwd(p); + + // TODO I think this is unreachable. Audit this situation when solving the above TODO + // regarding eliding redundant object -> object transformations. + return error.NoObjectsToLink; + }; + try std.fs.Dir.copyFile( + the_object_path.root_dir.handle, + the_object_path.sub_path, + directory.handle, + base.emit.sub_path, + .{}, + ); + } else { + // Create an LLD command line and invoke it. + var argv = std.ArrayList([]const u8).init(gpa); + defer argv.deinit(); + // We will invoke ourselves as a child process to gain access to LLD. + // This is necessary because LLD does not behave properly as a library - + // it calls exit() and does not reset all global data between invocations. + const linker_command = "ld.lld"; + try argv.appendSlice(&[_][]const u8{ comp.self_exe_path.?, linker_command }); + if (is_obj) { + try argv.append("-r"); + } + + try argv.append("--error-limit=0"); + + if (comp.sysroot) |sysroot| { + try argv.append(try std.fmt.allocPrint(arena, "--sysroot={s}", .{sysroot})); + } + + if (target_util.llvmMachineAbi(target)) |mabi| { + try argv.appendSlice(&.{ + "-mllvm", + try std.fmt.allocPrint(arena, "-target-abi={s}", .{mabi}), + }); + } + + try argv.appendSlice(&.{ + "-mllvm", + try std.fmt.allocPrint(arena, "-float-abi={s}", .{if (target.abi.float() == .hard) "hard" else "soft"}), + }); + + if (comp.config.lto != .none) { + switch (comp.root_mod.optimize_mode) { + .Debug => {}, + .ReleaseSmall => try argv.append("--lto-O2"), + .ReleaseFast, .ReleaseSafe => try argv.append("--lto-O3"), + } + } + switch (comp.root_mod.optimize_mode) { + .Debug => {}, + .ReleaseSmall => try argv.append("-O2"), + .ReleaseFast, .ReleaseSafe => try argv.append("-O3"), + } + + if (elf.entry_name) |name| { + try argv.appendSlice(&.{ "--entry", name }); + } + + for (comp.force_undefined_symbols.keys()) |sym| { + try argv.append("-u"); + try argv.append(sym); + } + + switch (elf.hash_style) { + .gnu => try argv.append("--hash-style=gnu"), + .sysv => try argv.append("--hash-style=sysv"), + .both => {}, // this is the default + } + + if (output_mode == .Exe) { + try argv.appendSlice(&.{ + "-z", + try std.fmt.allocPrint(arena, "stack-size={d}", .{base.stack_size}), + }); + } + + switch (base.build_id) { + .none => try argv.append("--build-id=none"), + .fast, .uuid, .sha1, .md5 => try argv.append(try std.fmt.allocPrint(arena, "--build-id={s}", .{ + @tagName(base.build_id), + })), + .hexstring => |hs| try argv.append(try std.fmt.allocPrint(arena, "--build-id=0x{s}", .{ + std.fmt.fmtSliceHexLower(hs.toSlice()), + })), + } + + try argv.append(try std.fmt.allocPrint(arena, "--image-base={d}", .{elf.image_base})); + + if (elf.linker_script) |linker_script| { + try argv.append("-T"); + try argv.append(linker_script); + } + + if (elf.sort_section) |how| { + const arg = try std.fmt.allocPrint(arena, "--sort-section={s}", .{@tagName(how)}); + try argv.append(arg); + } + + if (base.gc_sections) { + try argv.append("--gc-sections"); + } + + if (base.print_gc_sections) { + try argv.append("--print-gc-sections"); + } + + if (elf.print_icf_sections) { + try argv.append("--print-icf-sections"); + } + + if (elf.print_map) { + try argv.append("--print-map"); + } + + if (comp.link_eh_frame_hdr) { + try argv.append("--eh-frame-hdr"); + } + + if (elf.emit_relocs) { + try argv.append("--emit-relocs"); + } + + if (comp.config.rdynamic) { + try argv.append("--export-dynamic"); + } + + if (comp.config.debug_format == .strip) { + try argv.append("-s"); + } + + if (elf.z_nodelete) { + try argv.append("-z"); + try argv.append("nodelete"); + } + if (elf.z_notext) { + try argv.append("-z"); + try argv.append("notext"); + } + if (elf.z_defs) { + try argv.append("-z"); + try argv.append("defs"); + } + if (elf.z_origin) { + try argv.append("-z"); + try argv.append("origin"); + } + if (elf.z_nocopyreloc) { + try argv.append("-z"); + try argv.append("nocopyreloc"); + } + if (elf.z_now) { + // LLD defaults to -zlazy + try argv.append("-znow"); + } + if (!elf.z_relro) { + // LLD defaults to -zrelro + try argv.append("-znorelro"); + } + if (elf.z_common_page_size) |size| { + try argv.append("-z"); + try argv.append(try std.fmt.allocPrint(arena, "common-page-size={d}", .{size})); + } + if (elf.z_max_page_size) |size| { + try argv.append("-z"); + try argv.append(try std.fmt.allocPrint(arena, "max-page-size={d}", .{size})); + } + + if (getLDMOption(target)) |ldm| { + try argv.append("-m"); + try argv.append(ldm); + } + + if (link_mode == .static) { + if (target.cpu.arch.isArm()) { + try argv.append("-Bstatic"); + } else { + try argv.append("-static"); + } + } else if (switch (target.os.tag) { + else => is_dyn_lib, + .haiku => is_exe_or_dyn_lib, + }) { + try argv.append("-shared"); + } + + if (comp.config.pie and output_mode == .Exe) { + try argv.append("-pie"); + } + + if (is_exe_or_dyn_lib and target.os.tag == .netbsd) { + // Add options to produce shared objects with only 2 PT_LOAD segments. + // NetBSD expects 2 PT_LOAD segments in a shared object, otherwise + // ld.elf_so fails loading dynamic libraries with "not found" error. + // See https://github.com/ziglang/zig/issues/9109 . + try argv.append("--no-rosegment"); + try argv.append("-znorelro"); + } + + try argv.append("-o"); + try argv.append(full_out_path); + + // csu prelude + const csu = try comp.getCrtPaths(arena); + if (csu.crt0) |p| try argv.append(try p.toString(arena)); + if (csu.crti) |p| try argv.append(try p.toString(arena)); + if (csu.crtbegin) |p| try argv.append(try p.toString(arena)); + + for (elf.rpath_list) |rpath| { + try argv.appendSlice(&.{ "-rpath", rpath }); + } + + for (elf.symbol_wrap_set) |symbol_name| { + try argv.appendSlice(&.{ "-wrap", symbol_name }); + } + + if (comp.config.link_libc) { + if (comp.libc_installation) |libc_installation| { + try argv.append("-L"); + try argv.append(libc_installation.crt_dir.?); + } + } + + if (have_dynamic_linker and + (comp.config.link_libc or comp.root_mod.resolved_target.is_explicit_dynamic_linker)) + { + if (target.dynamic_linker.get()) |dynamic_linker| { + try argv.append("-dynamic-linker"); + try argv.append(dynamic_linker); + } + } + + if (is_dyn_lib) { + if (elf.soname) |soname| { + try argv.append("-soname"); + try argv.append(soname); + } + if (elf.version_script) |version_script| { + try argv.append("-version-script"); + try argv.append(version_script); + } + if (elf.allow_undefined_version) { + try argv.append("--undefined-version"); + } else { + try argv.append("--no-undefined-version"); + } + if (elf.enable_new_dtags) |enable_new_dtags| { + if (enable_new_dtags) { + try argv.append("--enable-new-dtags"); + } else { + try argv.append("--disable-new-dtags"); + } + } + } + + // Positional arguments to the linker such as object files. + var whole_archive = false; + + for (base.comp.link_inputs) |link_input| switch (link_input) { + .res => unreachable, // Windows-only + .dso => continue, + .object, .archive => |obj| { + if (obj.must_link and !whole_archive) { + try argv.append("-whole-archive"); + whole_archive = true; + } else if (!obj.must_link and whole_archive) { + try argv.append("-no-whole-archive"); + whole_archive = false; + } + try argv.append(try obj.path.toString(arena)); + }, + .dso_exact => |dso_exact| { + assert(dso_exact.name[0] == ':'); + try argv.appendSlice(&.{ "-l", dso_exact.name }); + }, + }; + + if (whole_archive) { + try argv.append("-no-whole-archive"); + whole_archive = false; + } + + for (comp.c_object_table.keys()) |key| { + try argv.append(try key.status.success.object_path.toString(arena)); + } + + if (module_obj_path) |p| { + try argv.append(p); + } + + if (comp.tsan_lib) |lib| { + assert(comp.config.any_sanitize_thread); + try argv.append(try lib.full_object_path.toString(arena)); + } + + if (comp.fuzzer_lib) |lib| { + assert(comp.config.any_fuzz); + try argv.append(try lib.full_object_path.toString(arena)); + } + + if (ubsan_rt_path) |p| { + try argv.append(try p.toString(arena)); + } + + // Shared libraries. + if (is_exe_or_dyn_lib) { + // Worst-case, we need an --as-needed argument for every lib, as well + // as one before and one after. + try argv.ensureUnusedCapacity(2 * base.comp.link_inputs.len + 2); + argv.appendAssumeCapacity("--as-needed"); + var as_needed = true; + + for (base.comp.link_inputs) |link_input| switch (link_input) { + .res => unreachable, // Windows-only + .object, .archive, .dso_exact => continue, + .dso => |dso| { + const lib_as_needed = !dso.needed; + switch ((@as(u2, @intFromBool(lib_as_needed)) << 1) | @intFromBool(as_needed)) { + 0b00, 0b11 => {}, + 0b01 => { + argv.appendAssumeCapacity("--no-as-needed"); + as_needed = false; + }, + 0b10 => { + argv.appendAssumeCapacity("--as-needed"); + as_needed = true; + }, + } + + // By this time, we depend on these libs being dynamically linked + // libraries and not static libraries (the check for that needs to be earlier), + // but they could be full paths to .so files, in which case we + // want to avoid prepending "-l". + argv.appendAssumeCapacity(try dso.path.toString(arena)); + }, + }; + + if (!as_needed) { + argv.appendAssumeCapacity("--as-needed"); + as_needed = true; + } + + // libc++ dep + if (comp.config.link_libcpp) { + try argv.append(try comp.libcxxabi_static_lib.?.full_object_path.toString(arena)); + try argv.append(try comp.libcxx_static_lib.?.full_object_path.toString(arena)); + } + + // libunwind dep + if (comp.config.link_libunwind) { + try argv.append(try comp.libunwind_static_lib.?.full_object_path.toString(arena)); + } + + // libc dep + diags.flags.missing_libc = false; + if (comp.config.link_libc) { + if (comp.libc_installation != null) { + const needs_grouping = link_mode == .static; + if (needs_grouping) try argv.append("--start-group"); + try argv.appendSlice(target_util.libcFullLinkFlags(target)); + if (needs_grouping) try argv.append("--end-group"); + } else if (target.isGnuLibC()) { + for (glibc.libs) |lib| { + if (lib.removed_in) |rem_in| { + if (target.os.versionRange().gnuLibCVersion().?.order(rem_in) != .lt) continue; + } + + const lib_path = try std.fmt.allocPrint(arena, "{}{c}lib{s}.so.{d}", .{ + comp.glibc_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover, + }); + try argv.append(lib_path); + } + try argv.append(try comp.crtFileAsString(arena, "libc_nonshared.a")); + } else if (target.isMuslLibC()) { + try argv.append(try comp.crtFileAsString(arena, switch (link_mode) { + .static => "libc.a", + .dynamic => "libc.so", + })); + } else if (target.isFreeBSDLibC()) { + for (freebsd.libs) |lib| { + const lib_path = try std.fmt.allocPrint(arena, "{}{c}lib{s}.so.{d}", .{ + comp.freebsd_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover, + }); + try argv.append(lib_path); + } + } else if (target.isNetBSDLibC()) { + for (netbsd.libs) |lib| { + const lib_path = try std.fmt.allocPrint(arena, "{}{c}lib{s}.so.{d}", .{ + comp.netbsd_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover, + }); + try argv.append(lib_path); + } + } else { + diags.flags.missing_libc = true; + } + + if (comp.zigc_static_lib) |zigc| { + try argv.append(try zigc.full_object_path.toString(arena)); + } + } + } + + // compiler-rt. Since compiler_rt exports symbols like `memset`, it needs + // to be after the shared libraries, so they are picked up from the shared + // libraries, not libcompiler_rt. + if (compiler_rt_path) |p| { + try argv.append(try p.toString(arena)); + } + + // crt postlude + if (csu.crtend) |p| try argv.append(try p.toString(arena)); + if (csu.crtn) |p| try argv.append(try p.toString(arena)); + + if (base.allow_shlib_undefined) { + try argv.append("--allow-shlib-undefined"); + } + + switch (elf.compress_debug_sections) { + .none => {}, + .zlib => try argv.append("--compress-debug-sections=zlib"), + .zstd => try argv.append("--compress-debug-sections=zstd"), + } + + if (elf.bind_global_refs_locally) { + try argv.append("-Bsymbolic"); + } + + try spawnLld(comp, arena, argv.items); + } + + if (!lld.disable_caching) { + // Update the file with the digest. If it fails we can continue; it only + // means that the next invocation will have an unnecessary cache miss. + std.Build.Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| { + log.warn("failed to save linking hash digest file: {s}", .{@errorName(err)}); + }; + // Again failure here only means an unnecessary cache miss. + man.writeManifest() catch |err| { + log.warn("failed to write cache manifest when linking: {s}", .{@errorName(err)}); + }; + // We hang on to this lock so that the output file path can be used without + // other processes clobbering it. + base.lock = man.toOwnedLock(); + } +} +fn getLDMOption(target: std.Target) ?[]const u8 { + // This should only return emulations understood by LLD's parseEmulation(). + return switch (target.cpu.arch) { + .aarch64 => switch (target.os.tag) { + .linux => "aarch64linux", + else => "aarch64elf", + }, + .aarch64_be => switch (target.os.tag) { + .linux => "aarch64linuxb", + else => "aarch64elfb", + }, + .amdgcn => "elf64_amdgpu", + .arm, .thumb => switch (target.os.tag) { + .linux => "armelf_linux_eabi", + else => "armelf", + }, + .armeb, .thumbeb => switch (target.os.tag) { + .linux => "armelfb_linux_eabi", + else => "armelfb", + }, + .hexagon => "hexagonelf", + .loongarch32 => "elf32loongarch", + .loongarch64 => "elf64loongarch", + .mips => switch (target.os.tag) { + .freebsd => "elf32btsmip_fbsd", + else => "elf32btsmip", + }, + .mipsel => switch (target.os.tag) { + .freebsd => "elf32ltsmip_fbsd", + else => "elf32ltsmip", + }, + .mips64 => switch (target.os.tag) { + .freebsd => switch (target.abi) { + .gnuabin32, .muslabin32 => "elf32btsmipn32_fbsd", + else => "elf64btsmip_fbsd", + }, + else => switch (target.abi) { + .gnuabin32, .muslabin32 => "elf32btsmipn32", + else => "elf64btsmip", + }, + }, + .mips64el => switch (target.os.tag) { + .freebsd => switch (target.abi) { + .gnuabin32, .muslabin32 => "elf32ltsmipn32_fbsd", + else => "elf64ltsmip_fbsd", + }, + else => switch (target.abi) { + .gnuabin32, .muslabin32 => "elf32ltsmipn32", + else => "elf64ltsmip", + }, + }, + .msp430 => "msp430elf", + .powerpc => switch (target.os.tag) { + .freebsd => "elf32ppc_fbsd", + .linux => "elf32ppclinux", + else => "elf32ppc", + }, + .powerpcle => switch (target.os.tag) { + .linux => "elf32lppclinux", + else => "elf32lppc", + }, + .powerpc64 => "elf64ppc", + .powerpc64le => "elf64lppc", + .riscv32 => "elf32lriscv", + .riscv64 => "elf64lriscv", + .s390x => "elf64_s390", + .sparc64 => "elf64_sparc", + .x86 => switch (target.os.tag) { + .freebsd => "elf_i386_fbsd", + else => "elf_i386", + }, + .x86_64 => switch (target.abi) { + .gnux32, .muslx32 => "elf32_x86_64", + else => "elf_x86_64", + }, + else => null, + }; +} +fn wasmLink(lld: *Lld, arena: Allocator) !void { + const comp = lld.base.comp; + const shared_memory = comp.config.shared_memory; + const export_memory = comp.config.export_memory; + const import_memory = comp.config.import_memory; + const target = comp.root_mod.resolved_target.result; + const base = &lld.base; + const wasm = &lld.ofmt.wasm; + + const gpa = comp.gpa; + + const directory = base.emit.root_dir; // Just an alias to make it shorter to type. + const full_out_path = try directory.join(arena, &[_][]const u8{base.emit.sub_path}); + + // 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) p: { + if (fs.path.dirname(full_out_path)) |dirname| { + break :p try fs.path.join(arena, &.{ dirname, base.zcu_object_sub_path.? }); + } else { + break :p base.zcu_object_sub_path.?; + } + } else null; + + const is_obj = comp.config.output_mode == .Obj; + const compiler_rt_path: ?Cache.Path = blk: { + if (comp.compiler_rt_lib) |lib| break :blk lib.full_object_path; + if (comp.compiler_rt_obj) |obj| break :blk obj.full_object_path; + break :blk null; + }; + const ubsan_rt_path: ?Cache.Path = blk: { + if (comp.ubsan_rt_lib) |lib| break :blk lib.full_object_path; + if (comp.ubsan_rt_obj) |obj| break :blk obj.full_object_path; + break :blk null; + }; + + const id_symlink_basename = "lld.id"; + + var man: Cache.Manifest = undefined; + defer if (!lld.disable_caching) man.deinit(); + + var digest: [Cache.hex_digest_len]u8 = undefined; + + if (!lld.disable_caching) { + man = comp.cache_parent.obtain(); + + // We are about to obtain this lock, so here we give other processes a chance first. + base.releaseLock(); + + comptime assert(Compilation.link_hash_implementation_version == 14); + + try link.hashInputs(&man, comp.link_inputs); + for (comp.c_object_table.keys()) |key| { + _ = try man.addFilePath(key.status.success.object_path, null); + } + try man.addOptionalFile(module_obj_path); + try man.addOptionalFilePath(compiler_rt_path); + try man.addOptionalFilePath(ubsan_rt_path); + man.hash.addOptionalBytes(wasm.entry_name); + man.hash.add(base.stack_size); + man.hash.add(base.build_id); + man.hash.add(import_memory); + man.hash.add(export_memory); + man.hash.add(wasm.import_table); + man.hash.add(wasm.export_table); + man.hash.addOptional(wasm.initial_memory); + man.hash.addOptional(wasm.max_memory); + man.hash.add(shared_memory); + man.hash.addOptional(wasm.global_base); + man.hash.addListOfBytes(wasm.export_symbol_names); + // strip does not need to go into the linker hash because it is part of the hash namespace + + // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock. + _ = try man.hit(); + digest = man.final(); + + var prev_digest_buf: [digest.len]u8 = undefined; + const prev_digest: []u8 = Cache.readSmallFile( + directory.handle, + id_symlink_basename, + &prev_digest_buf, + ) catch |err| blk: { + log.debug("WASM LLD new_digest={s} error: {s}", .{ std.fmt.fmtSliceHexLower(&digest), @errorName(err) }); + // Handle this as a cache miss. + break :blk prev_digest_buf[0..0]; + }; + if (mem.eql(u8, prev_digest, &digest)) { + log.debug("WASM LLD digest={s} match - skipping invocation", .{std.fmt.fmtSliceHexLower(&digest)}); + // Hot diggity dog! The output binary is already there. + base.lock = man.toOwnedLock(); + return; + } + log.debug("WASM LLD prev_digest={s} new_digest={s}", .{ std.fmt.fmtSliceHexLower(prev_digest), std.fmt.fmtSliceHexLower(&digest) }); + + // We are about to change the output file to be different, so we invalidate the build hash now. + directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) { + error.FileNotFound => {}, + else => |e| return e, + }; + } + + if (is_obj) { + // LLD's WASM driver does not support the equivalent of `-r` so we do a simple file copy + // here. TODO: think carefully about how we can avoid this redundant operation when doing + // build-obj. See also the corresponding TODO in linkAsArchive. + const the_object_path = blk: { + if (link.firstObjectInput(comp.link_inputs)) |obj| break :blk obj.path; + + if (comp.c_object_table.count() != 0) + break :blk comp.c_object_table.keys()[0].status.success.object_path; + + if (module_obj_path) |p| + break :blk Cache.Path.initCwd(p); + + // TODO I think this is unreachable. Audit this situation when solving the above TODO + // regarding eliding redundant object -> object transformations. + return error.NoObjectsToLink; + }; + try fs.Dir.copyFile( + the_object_path.root_dir.handle, + the_object_path.sub_path, + directory.handle, + base.emit.sub_path, + .{}, + ); + } else { + // Create an LLD command line and invoke it. + var argv = std.ArrayList([]const u8).init(gpa); + defer argv.deinit(); + // We will invoke ourselves as a child process to gain access to LLD. + // This is necessary because LLD does not behave properly as a library - + // it calls exit() and does not reset all global data between invocations. + const linker_command = "wasm-ld"; + try argv.appendSlice(&[_][]const u8{ comp.self_exe_path.?, linker_command }); + try argv.append("--error-limit=0"); + + if (comp.config.lto != .none) { + switch (comp.root_mod.optimize_mode) { + .Debug => {}, + .ReleaseSmall => try argv.append("-O2"), + .ReleaseFast, .ReleaseSafe => try argv.append("-O3"), + } + } + + if (import_memory) { + try argv.append("--import-memory"); + } + + if (export_memory) { + try argv.append("--export-memory"); + } + + if (wasm.import_table) { + assert(!wasm.export_table); + try argv.append("--import-table"); + } + + if (wasm.export_table) { + assert(!wasm.import_table); + try argv.append("--export-table"); + } + + // For wasm-ld we only need to specify '--no-gc-sections' when the user explicitly + // specified it as garbage collection is enabled by default. + if (!base.gc_sections) { + try argv.append("--no-gc-sections"); + } + + if (comp.config.debug_format == .strip) { + try argv.append("-s"); + } + + if (wasm.initial_memory) |initial_memory| { + const arg = try std.fmt.allocPrint(arena, "--initial-memory={d}", .{initial_memory}); + try argv.append(arg); + } + + if (wasm.max_memory) |max_memory| { + const arg = try std.fmt.allocPrint(arena, "--max-memory={d}", .{max_memory}); + try argv.append(arg); + } + + if (shared_memory) { + try argv.append("--shared-memory"); + } + + if (wasm.global_base) |global_base| { + const arg = try std.fmt.allocPrint(arena, "--global-base={d}", .{global_base}); + try argv.append(arg); + } else { + // We prepend it by default, so when a stack overflow happens the runtime will trap correctly, + // rather than silently overwrite all global declarations. See https://github.com/ziglang/zig/issues/4496 + // + // The user can overwrite this behavior by setting the global-base + try argv.append("--stack-first"); + } + + // Users are allowed to specify which symbols they want to export to the wasm host. + for (wasm.export_symbol_names) |symbol_name| { + const arg = try std.fmt.allocPrint(arena, "--export={s}", .{symbol_name}); + try argv.append(arg); + } + + if (comp.config.rdynamic) { + try argv.append("--export-dynamic"); + } + + if (wasm.entry_name) |entry_name| { + try argv.appendSlice(&.{ "--entry", entry_name }); + } else { + try argv.append("--no-entry"); + } + + try argv.appendSlice(&.{ + "-z", + try std.fmt.allocPrint(arena, "stack-size={d}", .{base.stack_size}), + }); + + switch (base.build_id) { + .none => try argv.append("--build-id=none"), + .fast, .uuid, .sha1 => try argv.append(try std.fmt.allocPrint(arena, "--build-id={s}", .{ + @tagName(base.build_id), + })), + .hexstring => |hs| try argv.append(try std.fmt.allocPrint(arena, "--build-id=0x{s}", .{ + std.fmt.fmtSliceHexLower(hs.toSlice()), + })), + .md5 => {}, + } + + if (wasm.import_symbols) { + try argv.append("--allow-undefined"); + } + + if (comp.config.output_mode == .Lib and comp.config.link_mode == .dynamic) { + try argv.append("--shared"); + } + if (comp.config.pie) { + try argv.append("--pie"); + } + + try argv.appendSlice(&.{ "-o", full_out_path }); + + if (target.cpu.arch == .wasm64) { + try argv.append("-mwasm64"); + } + + const is_exe_or_dyn_lib = comp.config.output_mode == .Exe or + (comp.config.output_mode == .Lib and comp.config.link_mode == .dynamic); + + if (comp.config.link_libc and is_exe_or_dyn_lib) { + if (target.os.tag == .wasi) { + for (comp.wasi_emulated_libs) |crt_file| { + try argv.append(try comp.crtFileAsString( + arena, + wasi_libc.emulatedLibCRFileLibName(crt_file), + )); + } + + try argv.append(try comp.crtFileAsString( + arena, + wasi_libc.execModelCrtFileFullName(comp.config.wasi_exec_model), + )); + try argv.append(try comp.crtFileAsString(arena, "libc.a")); + } + + if (comp.zigc_static_lib) |zigc| { + try argv.append(try zigc.full_object_path.toString(arena)); + } + + if (comp.config.link_libcpp) { + try argv.append(try comp.libcxx_static_lib.?.full_object_path.toString(arena)); + try argv.append(try comp.libcxxabi_static_lib.?.full_object_path.toString(arena)); + } + } + + // Positional arguments to the linker such as object files. + var whole_archive = false; + for (comp.link_inputs) |link_input| switch (link_input) { + .object, .archive => |obj| { + if (obj.must_link and !whole_archive) { + try argv.append("-whole-archive"); + whole_archive = true; + } else if (!obj.must_link and whole_archive) { + try argv.append("-no-whole-archive"); + whole_archive = false; + } + try argv.append(try obj.path.toString(arena)); + }, + .dso => |dso| { + try argv.append(try dso.path.toString(arena)); + }, + .dso_exact => unreachable, + .res => unreachable, + }; + if (whole_archive) { + try argv.append("-no-whole-archive"); + whole_archive = false; + } + + for (comp.c_object_table.keys()) |key| { + try argv.append(try key.status.success.object_path.toString(arena)); + } + if (module_obj_path) |p| { + try argv.append(p); + } + + if (compiler_rt_path) |p| { + try argv.append(try p.toString(arena)); + } + + if (ubsan_rt_path) |p| { + try argv.append(try p.toStringZ(arena)); + } + + try spawnLld(comp, arena, argv.items); + + // Give +x to the .wasm file if it is an executable and the OS is WASI. + // Some systems may be configured to execute such binaries directly. Even if that + // is not the case, it means we will get "exec format error" when trying to run + // it, and then can react to that in the same way as trying to run an ELF file + // from a foreign CPU architecture. + if (fs.has_executable_bit and target.os.tag == .wasi and + comp.config.output_mode == .Exe) + { + // TODO: what's our strategy for reporting linker errors from this function? + // report a nice error here with the file path if it fails instead of + // just returning the error code. + // chmod does not interact with umask, so we use a conservative -rwxr--r-- here. + std.posix.fchmodat(fs.cwd().fd, full_out_path, 0o744, 0) catch |err| switch (err) { + error.OperationNotSupported => unreachable, // Not a symlink. + else => |e| return e, + }; + } + } + + if (!lld.disable_caching) { + // Update the file with the digest. If it fails we can continue; it only + // means that the next invocation will have an unnecessary cache miss. + Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| { + log.warn("failed to save linking hash digest symlink: {s}", .{@errorName(err)}); + }; + // Again failure here only means an unnecessary cache miss. + man.writeManifest() catch |err| { + log.warn("failed to write cache manifest when linking: {s}", .{@errorName(err)}); + }; + // We hang on to this lock so that the output file path can be used without + // other processes clobbering it. + base.lock = man.toOwnedLock(); + } +} + +fn spawnLld( + comp: *Compilation, + arena: Allocator, + argv: []const []const u8, +) !void { + if (comp.verbose_link) { + // Skip over our own name so that the LLD linker name is the first argv item. + Compilation.dump_argv(argv[1..]); + } + + // If possible, we run LLD as a child process because it does not always + // behave properly as a library, unfortunately. + // https://github.com/ziglang/zig/issues/3825 + if (!std.process.can_spawn) { + const exit_code = try lldMain(arena, argv, false); + if (exit_code == 0) return; + if (comp.clang_passthrough_mode) std.process.exit(exit_code); + return error.LinkFailure; + } + + var stderr: []u8 = &.{}; + defer comp.gpa.free(stderr); + + var child = std.process.Child.init(argv, arena); + const term = (if (comp.clang_passthrough_mode) term: { + child.stdin_behavior = .Inherit; + child.stdout_behavior = .Inherit; + child.stderr_behavior = .Inherit; + + break :term child.spawnAndWait(); + } else term: { + child.stdin_behavior = .Ignore; + child.stdout_behavior = .Ignore; + child.stderr_behavior = .Pipe; + + child.spawn() catch |err| break :term err; + stderr = try child.stderr.?.reader().readAllAlloc(comp.gpa, std.math.maxInt(usize)); + break :term child.wait(); + }) catch |first_err| term: { + const err = switch (first_err) { + error.NameTooLong => err: { + const s = fs.path.sep_str; + const rand_int = std.crypto.random.int(u64); + const rsp_path = "tmp" ++ s ++ std.fmt.hex(rand_int) ++ ".rsp"; + + const rsp_file = try comp.dirs.local_cache.handle.createFileZ(rsp_path, .{}); + defer comp.dirs.local_cache.handle.deleteFileZ(rsp_path) catch |err| + log.warn("failed to delete response file {s}: {s}", .{ rsp_path, @errorName(err) }); + { + defer rsp_file.close(); + var rsp_buf = std.io.bufferedWriter(rsp_file.writer()); + const rsp_writer = rsp_buf.writer(); + for (argv[2..]) |arg| { + try rsp_writer.writeByte('"'); + for (arg) |c| { + switch (c) { + '\"', '\\' => try rsp_writer.writeByte('\\'), + else => {}, + } + try rsp_writer.writeByte(c); + } + try rsp_writer.writeByte('"'); + try rsp_writer.writeByte('\n'); + } + try rsp_buf.flush(); + } + + var rsp_child = std.process.Child.init(&.{ argv[0], argv[1], try std.fmt.allocPrint( + arena, + "@{s}", + .{try comp.dirs.local_cache.join(arena, &.{rsp_path})}, + ) }, arena); + if (comp.clang_passthrough_mode) { + rsp_child.stdin_behavior = .Inherit; + rsp_child.stdout_behavior = .Inherit; + rsp_child.stderr_behavior = .Inherit; + + break :term rsp_child.spawnAndWait() catch |err| break :err err; + } else { + rsp_child.stdin_behavior = .Ignore; + rsp_child.stdout_behavior = .Ignore; + rsp_child.stderr_behavior = .Pipe; + + rsp_child.spawn() catch |err| break :err err; + stderr = try rsp_child.stderr.?.reader().readAllAlloc(comp.gpa, std.math.maxInt(usize)); + break :term rsp_child.wait() catch |err| break :err err; + } + }, + else => first_err, + }; + log.err("unable to spawn LLD {s}: {s}", .{ argv[0], @errorName(err) }); + return error.UnableToSpawnSelf; + }; + + const diags = &comp.link_diags; + switch (term) { + .Exited => |code| if (code != 0) { + if (comp.clang_passthrough_mode) std.process.exit(code); + diags.lockAndParseLldStderr(argv[1], stderr); + return error.LinkFailure; + }, + else => { + if (comp.clang_passthrough_mode) std.process.abort(); + return diags.fail("{s} terminated with stderr:\n{s}", .{ argv[0], stderr }); + }, + } + + if (stderr.len > 0) log.warn("unexpected LLD stderr:\n{s}", .{stderr}); +} + +const std = @import("std"); +const Allocator = std.mem.Allocator; +const Cache = std.Build.Cache; +const allocPrint = std.fmt.allocPrint; +const assert = std.debug.assert; +const fs = std.fs; +const log = std.log.scoped(.link); +const mem = std.mem; + +const Compilation = @import("../Compilation.zig"); +const Zcu = @import("../Zcu.zig"); +const dev = @import("../dev.zig"); +const freebsd = @import("../libs/freebsd.zig"); +const glibc = @import("../libs/glibc.zig"); +const netbsd = @import("../libs/netbsd.zig"); +const wasi_libc = @import("../libs/wasi_libc.zig"); +const link = @import("../link.zig"); +const lldMain = @import("../main.zig").lldMain; +const target_util = @import("../target.zig"); +const trace = @import("../tracy.zig").trace; +const Lld = @This(); diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 6667ed6a635f5e03e5f6026e0358f2240fe44172..2c30b34215c516fb547f90c4be37cef4f7ce46f7 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -194,7 +194,6 @@ pub fn createEmpty( .stack_size = options.stack_size orelse 16777216, .allow_shlib_undefined = allow_shlib_undefined, .file = null, - .disable_lld_caching = options.disable_lld_caching, .build_id = options.build_id, }, .rpath_list = options.rpath_list, @@ -227,7 +226,7 @@ pub fn createEmpty( self.base.file = try emit.root_dir.handle.createFile(emit.sub_path, .{ .truncate = true, .read = true, - .mode = link.File.determineMode(false, output_mode, link_mode), + .mode = link.File.determineMode(output_mode, link_mode), }); // Append null file @@ -341,15 +340,6 @@ pub fn flush( arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node, -) link.File.FlushError!void { - try self.flushZcu(arena, tid, prog_node); -} - -pub fn flushZcu( - self: *MachO, - arena: Allocator, - tid: Zcu.PerThread.Id, - prog_node: std.Progress.Node, ) link.File.FlushError!void { const tracy = trace(@src()); defer tracy.end(); @@ -373,7 +363,7 @@ pub fn flushZcu( // --verbose-link if (comp.verbose_link) try self.dumpArgv(comp); - if (self.getZigObject()) |zo| try zo.flushZcu(self, tid); + if (self.getZigObject()) |zo| try zo.flush(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); @@ -617,7 +607,7 @@ pub fn flushZcu( error.LinkFailure => return error.LinkFailure, else => |e| return diags.fail("failed to calculate and write uuid: {s}", .{@errorName(e)}), }; - if (self.getDebugSymbols()) |dsym| dsym.flushZcu(self) catch |err| switch (err) { + if (self.getDebugSymbols()) |dsym| dsym.flush(self) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, else => |e| return diags.fail("failed to get debug symbols: {s}", .{@errorName(e)}), }; diff --git a/src/link/MachO/DebugSymbols.zig b/src/link/MachO/DebugSymbols.zig index 8579863d0343ab8105aae93a00cea38c6a94f110..eef3492b485e35e655ca10b464847525a6cf8b8d 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 flushZcu(self: *DebugSymbols, macho_file: *MachO) !void { +pub fn flush(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 4d99afc61a9d698a0e739ee9cb9478946b29961c..13ebb40cf99d3b6d1660485ad5d5fde72ad71a56 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 flushZcu(self: *ZigObject, macho_file: *MachO, tid: Zcu.PerThread.Id) link.File.FlushError!void { +pub fn flush(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 flushZcu(self: *ZigObject, macho_file: *MachO, tid: Zcu.PerThread.Id) lin if (self.dwarf) |*dwarf| { const pt: Zcu.PerThread = .activate(macho_file.base.comp.zcu.?, tid); defer pt.deactivate(); - dwarf.flushZcu(pt) catch |err| switch (err) { + dwarf.flush(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 flushZcu(self: *ZigObject, macho_file: *MachO, tid: Zcu.PerThread.Id) lin self.debug_strtab_dirty = false; } - // The point of flushZcu() is to commit changes, so in theory, nothing should + // The point of flush() 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 flushZcu + // anyerror needs to be deferred until flush 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 0a940cb0b3455d25851196af1dfa442440dd6a1d..c487169b3f16a2c4294f386821895cff7783bed7 100644 --- a/src/link/Plan9.zig +++ b/src/link/Plan9.zig @@ -301,7 +301,6 @@ pub fn createEmpty( .stack_size = options.stack_size orelse 16777216, .allow_shlib_undefined = options.allow_shlib_undefined orelse false, .file = null, - .disable_lld_caching = options.disable_lld_caching, .build_id = options.build_id, }, .sixtyfour_bit = sixtyfour_bit, @@ -494,7 +493,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 flushZcu + .value = undefined, // the value of stuff gets filled in in flush .type = atom.type, .name = try gpa.dupe(u8, nav.name.toSlice(ip)), }; @@ -527,25 +526,6 @@ fn allocateGotIndex(self: *Plan9) usize { } } -pub fn flush( - self: *Plan9, - arena: Allocator, - tid: Zcu.PerThread.Id, - prog_node: std.Progress.Node, -) link.File.FlushError!void { - const comp = self.base.comp; - const diags = &comp.link_diags; - const use_lld = build_options.have_llvm and comp.config.use_lld; - assert(!use_lld); - - switch (link.File.effectiveOutputMode(use_lld, comp.config.output_mode)) { - .Exe => {}, - .Obj => return diags.fail("writing plan9 object files unimplemented", .{}), - .Lib => return diags.fail("writing plan9 lib files unimplemented", .{}), - } - return self.flushZcu(arena, tid, prog_node); -} - pub fn changeLine(l: *std.ArrayList(u8), delta_line: i32) !void { if (delta_line > 0 and delta_line < 65) { const toappend = @as(u8, @intCast(delta_line)); @@ -586,7 +566,7 @@ fn atomCount(self: *Plan9) usize { return data_nav_count + fn_nav_count + lazy_atom_count + extern_atom_count + uav_atom_count; } -pub fn flushZcu( +pub fn flush( self: *Plan9, arena: Allocator, /// TODO: stop using this @@ -607,10 +587,16 @@ pub fn flushZcu( const gpa = comp.gpa; const target = comp.root_mod.resolved_target.result; + switch (comp.config.output_mode) { + .Exe => {}, + .Obj => return diags.fail("writing plan9 object files unimplemented", .{}), + .Lib => return diags.fail("writing plan9 lib files unimplemented", .{}), + } + const sub_prog_node = prog_node.start("Flush Module", 0); defer sub_prog_node.end(); - log.debug("flushZcu", .{}); + log.debug("flush", .{}); defer assert(self.hdr.entry != 0x0); @@ -1039,7 +1025,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 flushZcu + // anyerror needs to be deferred until flush if (lazy_sym.ty != .anyerror_type) try self.updateLazySymbolAtom(pt, lazy_sym, atom); return atom; } @@ -1182,11 +1168,7 @@ pub fn open( const file = try emit.root_dir.handle.createFile(emit.sub_path, .{ .read = true, - .mode = link.File.determineMode( - use_lld, - comp.config.output_mode, - comp.config.link_mode, - ), + .mode = link.File.determineMode(comp.config.output_mode, comp.config.link_mode), }); errdefer file.close(); self.base.file = file; diff --git a/src/link/SpirV.zig b/src/link/SpirV.zig index c6e86895f5601280c3b0ef0bfbcd5061f4c8d9a7..8b6b99525f03e0d1e24dcbcca3ed01e011dd8880 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 flushZcu. This keeps +// anyway, we simply generate all the code in flush. This keeps // things considerably simpler. const SpirV = @This(); @@ -83,7 +83,6 @@ pub fn createEmpty( .stack_size = options.stack_size orelse 0, .allow_shlib_undefined = options.allow_shlib_undefined orelse false, .file = null, - .disable_lld_caching = options.disable_lld_caching, .build_id = options.build_id, }, .object = codegen.Object.init(gpa, comp.getTarget()), @@ -193,18 +192,14 @@ pub fn updateExports( // TODO: Export regular functions, variables, etc using Linkage attributes. } -pub fn flush(self: *SpirV, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void { - return self.flushZcu(arena, tid, prog_node); -} - -pub fn flushZcu( +pub fn flush( 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 flushZcu is too late to be writing to the + // write to InternPool, but flush is too late to be writing to the // InternPool. _ = tid; diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index e92be32b7d49c3d3f617d8825a02731e6842a941..5c804ed21f909d07329eacccd2b0f30ad4e50b65 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -40,7 +40,6 @@ const Zcu = @import("../Zcu.zig"); const codegen = @import("../codegen.zig"); const dev = @import("../dev.zig"); const link = @import("../link.zig"); -const lldMain = @import("../main.zig").lldMain; const trace = @import("../tracy.zig").trace; const wasi_libc = @import("../libs/wasi_libc.zig"); const Value = @import("../Value.zig"); @@ -74,8 +73,6 @@ global_base: ?u64, initial_memory: ?u64, /// When defined, sets the maximum memory size of the memory. max_memory: ?u64, -/// When true, will import the function table from the host environment. -import_table: bool, /// When true, will export the function table to the host environment. export_table: bool, /// Output name of the file @@ -2935,17 +2932,14 @@ pub fn createEmpty( const target = comp.root_mod.resolved_target.result; assert(target.ofmt == .wasm); - const use_lld = build_options.have_llvm and comp.config.use_lld; const use_llvm = comp.config.use_llvm; const output_mode = comp.config.output_mode; const wasi_exec_model = comp.config.wasi_exec_model; - // If using LLD to link, this code should produce an object file so that it - // can be passed to LLD. // If using LLVM to generate the object file for the zig compilation unit, // we need a place to put the object file so that it can be subsequently // handled. - const zcu_object_sub_path = if (!use_lld and !use_llvm) + const zcu_object_sub_path = if (!use_llvm) null else try std.fmt.allocPrint(arena, "{s}.o", .{emit.sub_path}); @@ -2970,13 +2964,11 @@ pub fn createEmpty( }, .allow_shlib_undefined = options.allow_shlib_undefined orelse false, .file = null, - .disable_lld_caching = options.disable_lld_caching, .build_id = options.build_id, }, .name = undefined, .string_table = .empty, .string_bytes = .empty, - .import_table = options.import_table, .export_table = options.export_table, .import_symbols = options.import_symbols, .export_symbol_names = options.export_symbol_names, @@ -3004,17 +2996,7 @@ pub fn createEmpty( .named => |name| (try wasm.internString(name)).toOptional(), }; - if (use_lld and (use_llvm or !comp.config.have_zcu)) { - // LLVM emits the object file (if any); LLD links it into the final product. - return wasm; - } - - // What path should this Wasm linker code output to? - // If using LLD to link, this code should produce an object file so that it - // can be passed to LLD. - const sub_path = if (use_lld) zcu_object_sub_path.? else emit.sub_path; - - wasm.base.file = try emit.root_dir.handle.createFile(sub_path, .{ + wasm.base.file = try emit.root_dir.handle.createFile(emit.sub_path, .{ .truncate = true, .read = true, .mode = if (fs.has_executable_bit) @@ -3025,7 +3007,7 @@ pub fn createEmpty( else 0, }); - wasm.name = sub_path; + wasm.name = emit.sub_path; return wasm; } @@ -3367,21 +3349,6 @@ pub fn loadInput(wasm: *Wasm, input: link.Input) !void { } } -pub fn flush(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void { - const comp = wasm.base.comp; - const use_lld = build_options.have_llvm and comp.config.use_lld; - const diags = &comp.link_diags; - - if (use_lld) { - return wasm.linkWithLLD(arena, tid, prog_node) catch |err| switch (err) { - error.OutOfMemory => return error.OutOfMemory, - error.LinkFailure => return error.LinkFailure, - else => |e| return diags.fail("failed to link with LLD: {s}", .{@errorName(e)}), - }; - } - return wasm.flushZcu(arena, tid, prog_node); -} - pub fn prelink(wasm: *Wasm, prog_node: std.Progress.Node) link.File.FlushError!void { const tracy = trace(@src()); defer tracy.end(); @@ -3773,14 +3740,14 @@ fn markTable(wasm: *Wasm, i: ObjectTableIndex) link.File.FlushError!void { try wasm.tables.put(wasm.base.comp.gpa, .fromObjectTable(i), {}); } -pub fn flushZcu( +pub fn flush( 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 flushZcu is too late to be writing to the + // write to InternPool, but flush is too late to be writing to the // InternPool. _ = tid; const comp = wasm.base.comp; @@ -3832,436 +3799,6 @@ pub fn flushZcu( }; } -fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) !void { - dev.check(.lld_linker); - - const tracy = trace(@src()); - defer tracy.end(); - - const comp = wasm.base.comp; - const diags = &comp.link_diags; - const shared_memory = comp.config.shared_memory; - const export_memory = comp.config.export_memory; - const import_memory = comp.config.import_memory; - const target = comp.root_mod.resolved_target.result; - - const gpa = comp.gpa; - - const directory = wasm.base.emit.root_dir; // Just an alias to make it shorter to type. - const full_out_path = try directory.join(arena, &[_][]const u8{wasm.base.emit.sub_path}); - - // 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) |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.? }); - } else { - break :blk wasm.base.zcu_object_sub_path.?; - } - } else null; - - const sub_prog_node = prog_node.start("LLD Link", 0); - defer sub_prog_node.end(); - - const is_obj = comp.config.output_mode == .Obj; - const compiler_rt_path: ?Path = blk: { - if (comp.compiler_rt_lib) |lib| break :blk lib.full_object_path; - if (comp.compiler_rt_obj) |obj| break :blk obj.full_object_path; - break :blk null; - }; - const ubsan_rt_path: ?Path = blk: { - if (comp.ubsan_rt_lib) |lib| break :blk lib.full_object_path; - if (comp.ubsan_rt_obj) |obj| break :blk obj.full_object_path; - break :blk null; - }; - - const id_symlink_basename = "lld.id"; - - var man: Cache.Manifest = undefined; - defer if (!wasm.base.disable_lld_caching) man.deinit(); - - var digest: [Cache.hex_digest_len]u8 = undefined; - - if (!wasm.base.disable_lld_caching) { - man = comp.cache_parent.obtain(); - - // We are about to obtain this lock, so here we give other processes a chance first. - wasm.base.releaseLock(); - - comptime assert(Compilation.link_hash_implementation_version == 14); - - try link.hashInputs(&man, comp.link_inputs); - for (comp.c_object_table.keys()) |key| { - _ = try man.addFilePath(key.status.success.object_path, null); - } - try man.addOptionalFile(module_obj_path); - try man.addOptionalFilePath(compiler_rt_path); - try man.addOptionalFilePath(ubsan_rt_path); - man.hash.addOptionalBytes(wasm.entry_name.slice(wasm)); - man.hash.add(wasm.base.stack_size); - man.hash.add(wasm.base.build_id); - man.hash.add(import_memory); - man.hash.add(export_memory); - man.hash.add(wasm.import_table); - man.hash.add(wasm.export_table); - man.hash.addOptional(wasm.initial_memory); - man.hash.addOptional(wasm.max_memory); - man.hash.add(shared_memory); - man.hash.addOptional(wasm.global_base); - man.hash.addListOfBytes(wasm.export_symbol_names); - // strip does not need to go into the linker hash because it is part of the hash namespace - - // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock. - _ = try man.hit(); - digest = man.final(); - - var prev_digest_buf: [digest.len]u8 = undefined; - const prev_digest: []u8 = Cache.readSmallFile( - directory.handle, - id_symlink_basename, - &prev_digest_buf, - ) catch |err| blk: { - log.debug("WASM LLD new_digest={s} error: {s}", .{ std.fmt.fmtSliceHexLower(&digest), @errorName(err) }); - // Handle this as a cache miss. - break :blk prev_digest_buf[0..0]; - }; - if (mem.eql(u8, prev_digest, &digest)) { - log.debug("WASM LLD digest={s} match - skipping invocation", .{std.fmt.fmtSliceHexLower(&digest)}); - // Hot diggity dog! The output binary is already there. - wasm.base.lock = man.toOwnedLock(); - return; - } - log.debug("WASM LLD prev_digest={s} new_digest={s}", .{ std.fmt.fmtSliceHexLower(prev_digest), std.fmt.fmtSliceHexLower(&digest) }); - - // We are about to change the output file to be different, so we invalidate the build hash now. - directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) { - error.FileNotFound => {}, - else => |e| return e, - }; - } - - if (is_obj) { - // LLD's WASM driver does not support the equivalent of `-r` so we do a simple file copy - // here. TODO: think carefully about how we can avoid this redundant operation when doing - // build-obj. See also the corresponding TODO in linkAsArchive. - const the_object_path = blk: { - if (link.firstObjectInput(comp.link_inputs)) |obj| break :blk obj.path; - - if (comp.c_object_table.count() != 0) - break :blk comp.c_object_table.keys()[0].status.success.object_path; - - if (module_obj_path) |p| - break :blk Path.initCwd(p); - - // TODO I think this is unreachable. Audit this situation when solving the above TODO - // regarding eliding redundant object -> object transformations. - return error.NoObjectsToLink; - }; - try fs.Dir.copyFile( - the_object_path.root_dir.handle, - the_object_path.sub_path, - directory.handle, - wasm.base.emit.sub_path, - .{}, - ); - } else { - // Create an LLD command line and invoke it. - var argv = std.ArrayList([]const u8).init(gpa); - defer argv.deinit(); - // We will invoke ourselves as a child process to gain access to LLD. - // This is necessary because LLD does not behave properly as a library - - // it calls exit() and does not reset all global data between invocations. - const linker_command = "wasm-ld"; - try argv.appendSlice(&[_][]const u8{ comp.self_exe_path.?, linker_command }); - try argv.append("--error-limit=0"); - - if (comp.config.lto != .none) { - switch (comp.root_mod.optimize_mode) { - .Debug => {}, - .ReleaseSmall => try argv.append("-O2"), - .ReleaseFast, .ReleaseSafe => try argv.append("-O3"), - } - } - - if (import_memory) { - try argv.append("--import-memory"); - } - - if (export_memory) { - try argv.append("--export-memory"); - } - - if (wasm.import_table) { - assert(!wasm.export_table); - try argv.append("--import-table"); - } - - if (wasm.export_table) { - assert(!wasm.import_table); - try argv.append("--export-table"); - } - - // For wasm-ld we only need to specify '--no-gc-sections' when the user explicitly - // specified it as garbage collection is enabled by default. - if (!wasm.base.gc_sections) { - try argv.append("--no-gc-sections"); - } - - if (comp.config.debug_format == .strip) { - try argv.append("-s"); - } - - if (wasm.initial_memory) |initial_memory| { - const arg = try std.fmt.allocPrint(arena, "--initial-memory={d}", .{initial_memory}); - try argv.append(arg); - } - - if (wasm.max_memory) |max_memory| { - const arg = try std.fmt.allocPrint(arena, "--max-memory={d}", .{max_memory}); - try argv.append(arg); - } - - if (shared_memory) { - try argv.append("--shared-memory"); - } - - if (wasm.global_base) |global_base| { - const arg = try std.fmt.allocPrint(arena, "--global-base={d}", .{global_base}); - try argv.append(arg); - } else { - // We prepend it by default, so when a stack overflow happens the runtime will trap correctly, - // rather than silently overwrite all global declarations. See https://github.com/ziglang/zig/issues/4496 - // - // The user can overwrite this behavior by setting the global-base - try argv.append("--stack-first"); - } - - // Users are allowed to specify which symbols they want to export to the wasm host. - for (wasm.export_symbol_names) |symbol_name| { - const arg = try std.fmt.allocPrint(arena, "--export={s}", .{symbol_name}); - try argv.append(arg); - } - - if (comp.config.rdynamic) { - try argv.append("--export-dynamic"); - } - - if (wasm.entry_name.slice(wasm)) |entry_name| { - try argv.appendSlice(&.{ "--entry", entry_name }); - } else { - try argv.append("--no-entry"); - } - - try argv.appendSlice(&.{ - "-z", - try std.fmt.allocPrint(arena, "stack-size={d}", .{wasm.base.stack_size}), - }); - - switch (wasm.base.build_id) { - .none => try argv.append("--build-id=none"), - .fast, .uuid, .sha1 => try argv.append(try std.fmt.allocPrint(arena, "--build-id={s}", .{ - @tagName(wasm.base.build_id), - })), - .hexstring => |hs| try argv.append(try std.fmt.allocPrint(arena, "--build-id=0x{s}", .{ - std.fmt.fmtSliceHexLower(hs.toSlice()), - })), - .md5 => {}, - } - - if (wasm.import_symbols) { - try argv.append("--allow-undefined"); - } - - if (comp.config.output_mode == .Lib and comp.config.link_mode == .dynamic) { - try argv.append("--shared"); - } - if (comp.config.pie) { - try argv.append("--pie"); - } - - try argv.appendSlice(&.{ "-o", full_out_path }); - - if (target.cpu.arch == .wasm64) { - try argv.append("-mwasm64"); - } - - const is_exe_or_dyn_lib = comp.config.output_mode == .Exe or - (comp.config.output_mode == .Lib and comp.config.link_mode == .dynamic); - - if (comp.config.link_libc and is_exe_or_dyn_lib) { - if (target.os.tag == .wasi) { - for (comp.wasi_emulated_libs) |crt_file| { - try argv.append(try comp.crtFileAsString( - arena, - wasi_libc.emulatedLibCRFileLibName(crt_file), - )); - } - - try argv.append(try comp.crtFileAsString( - arena, - wasi_libc.execModelCrtFileFullName(comp.config.wasi_exec_model), - )); - try argv.append(try comp.crtFileAsString(arena, "libc.a")); - } - - if (comp.zigc_static_lib) |zigc| { - try argv.append(try zigc.full_object_path.toString(arena)); - } - - if (comp.config.link_libcpp) { - try argv.append(try comp.libcxx_static_lib.?.full_object_path.toString(arena)); - try argv.append(try comp.libcxxabi_static_lib.?.full_object_path.toString(arena)); - } - } - - // Positional arguments to the linker such as object files. - var whole_archive = false; - for (comp.link_inputs) |link_input| switch (link_input) { - .object, .archive => |obj| { - if (obj.must_link and !whole_archive) { - try argv.append("-whole-archive"); - whole_archive = true; - } else if (!obj.must_link and whole_archive) { - try argv.append("-no-whole-archive"); - whole_archive = false; - } - try argv.append(try obj.path.toString(arena)); - }, - .dso => |dso| { - try argv.append(try dso.path.toString(arena)); - }, - .dso_exact => unreachable, - .res => unreachable, - }; - if (whole_archive) { - try argv.append("-no-whole-archive"); - whole_archive = false; - } - - for (comp.c_object_table.keys()) |key| { - try argv.append(try key.status.success.object_path.toString(arena)); - } - if (module_obj_path) |p| { - try argv.append(p); - } - - if (compiler_rt_path) |p| { - try argv.append(try p.toString(arena)); - } - - if (ubsan_rt_path) |p| { - try argv.append(try p.toStringZ(arena)); - } - - if (comp.verbose_link) { - // Skip over our own name so that the LLD linker name is the first argv item. - Compilation.dump_argv(argv.items[1..]); - } - - if (std.process.can_spawn) { - // If possible, we run LLD as a child process because it does not always - // behave properly as a library, unfortunately. - // https://github.com/ziglang/zig/issues/3825 - var child = std.process.Child.init(argv.items, arena); - if (comp.clang_passthrough_mode) { - child.stdin_behavior = .Inherit; - child.stdout_behavior = .Inherit; - child.stderr_behavior = .Inherit; - - const term = child.spawnAndWait() catch |err| { - log.err("failed to spawn (passthrough mode) LLD {s}: {s}", .{ argv.items[0], @errorName(err) }); - return error.UnableToSpawnWasm; - }; - switch (term) { - .Exited => |code| { - if (code != 0) { - std.process.exit(code); - } - }, - else => std.process.abort(), - } - } else { - child.stdin_behavior = .Ignore; - child.stdout_behavior = .Ignore; - child.stderr_behavior = .Pipe; - - try child.spawn(); - - const stderr = try child.stderr.?.reader().readAllAlloc(arena, std.math.maxInt(usize)); - - const term = child.wait() catch |err| { - log.err("failed to spawn LLD {s}: {s}", .{ argv.items[0], @errorName(err) }); - return error.UnableToSpawnWasm; - }; - - switch (term) { - .Exited => |code| { - if (code != 0) { - diags.lockAndParseLldStderr(linker_command, stderr); - return error.LinkFailure; - } - }, - else => { - return diags.fail("{s} terminated with stderr:\n{s}", .{ argv.items[0], stderr }); - }, - } - - if (stderr.len != 0) { - log.warn("unexpected LLD stderr:\n{s}", .{stderr}); - } - } - } else { - const exit_code = try lldMain(arena, argv.items, false); - if (exit_code != 0) { - if (comp.clang_passthrough_mode) { - std.process.exit(exit_code); - } else { - return diags.fail("{s} returned exit code {d}:\n{s}", .{ argv.items[0], exit_code }); - } - } - } - - // Give +x to the .wasm file if it is an executable and the OS is WASI. - // Some systems may be configured to execute such binaries directly. Even if that - // is not the case, it means we will get "exec format error" when trying to run - // it, and then can react to that in the same way as trying to run an ELF file - // from a foreign CPU architecture. - if (fs.has_executable_bit and target.os.tag == .wasi and - comp.config.output_mode == .Exe) - { - // TODO: what's our strategy for reporting linker errors from this function? - // report a nice error here with the file path if it fails instead of - // just returning the error code. - // chmod does not interact with umask, so we use a conservative -rwxr--r-- here. - std.posix.fchmodat(fs.cwd().fd, full_out_path, 0o744, 0) catch |err| switch (err) { - error.OperationNotSupported => unreachable, // Not a symlink. - else => |e| return e, - }; - } - } - - if (!wasm.base.disable_lld_caching) { - // Update the file with the digest. If it fails we can continue; it only - // means that the next invocation will have an unnecessary cache miss. - Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| { - log.warn("failed to save linking hash digest symlink: {s}", .{@errorName(err)}); - }; - // Again failure here only means an unnecessary cache miss. - man.writeManifest() catch |err| { - log.warn("failed to write cache manifest when linking: {s}", .{@errorName(err)}); - }; - // We hang on to this lock so that the output file path can be used without - // other processes clobbering it. - wasm.base.lock = man.toOwnedLock(); - } -} - fn defaultEntrySymbolName( preloaded_strings: *const PreloadedStrings, wasi_exec_model: std.builtin.WasiExecModel, diff --git a/src/link/Xcoff.zig b/src/link/Xcoff.zig index e2f81e015e016e3688c7576779f76d2a7c171bf3..7fe714ce6e38e4cef69178719708a6282674c0bf 100644 --- a/src/link/Xcoff.zig +++ b/src/link/Xcoff.zig @@ -46,7 +46,6 @@ pub fn createEmpty( .stack_size = options.stack_size orelse 0, .allow_shlib_undefined = options.allow_shlib_undefined orelse false, .file = null, - .disable_lld_caching = options.disable_lld_caching, .build_id = options.build_id, }, }; @@ -105,10 +104,6 @@ pub fn updateExports( } pub fn flush(self: *Xcoff, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void { - return self.flushZcu(arena, tid, prog_node); -} - -pub fn flushZcu(self: *Xcoff, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void { _ = self; _ = arena; _ = tid; diff --git a/src/main.zig b/src/main.zig index 20ccf4b7ec1cafe4a4b135346138ffa0d893a3a2..f7ad35d7cdc328e2418a6665441c6a165fe33d5f 100644 --- a/src/main.zig +++ b/src/main.zig @@ -867,9 +867,9 @@ fn buildOutputType( var linker_allow_undefined_version: bool = false; var linker_enable_new_dtags: ?bool = null; var disable_c_depfile = false; - var linker_sort_section: ?link.File.Elf.SortSection = null; + var linker_sort_section: ?link.File.Lld.Elf.SortSection = null; var linker_gc_sections: ?bool = null; - var linker_compress_debug_sections: ?link.File.Elf.CompressDebugSections = null; + var linker_compress_debug_sections: ?link.File.Lld.Elf.CompressDebugSections = null; var linker_allow_shlib_undefined: ?bool = null; var allow_so_scripts: bool = false; var linker_bind_global_refs_locally: ?bool = null; @@ -921,7 +921,7 @@ fn buildOutputType( var debug_compiler_runtime_libs = false; var opt_incremental: ?bool = null; var install_name: ?[]const u8 = null; - var hash_style: link.File.Elf.HashStyle = .both; + var hash_style: link.File.Lld.Elf.HashStyle = .both; var entitlements: ?[]const u8 = null; var pagezero_size: ?u64 = null; var lib_search_strategy: link.UnresolvedInput.SearchStrategy = .paths_first; @@ -1196,11 +1196,11 @@ fn buildOutputType( install_name = args_iter.nextOrFatal(); } else if (mem.startsWith(u8, arg, "--compress-debug-sections=")) { const param = arg["--compress-debug-sections=".len..]; - linker_compress_debug_sections = std.meta.stringToEnum(link.File.Elf.CompressDebugSections, param) orelse { + linker_compress_debug_sections = std.meta.stringToEnum(link.File.Lld.Elf.CompressDebugSections, param) orelse { fatal("expected --compress-debug-sections=[none|zlib|zstd], found '{s}'", .{param}); }; } else if (mem.eql(u8, arg, "--compress-debug-sections")) { - linker_compress_debug_sections = link.File.Elf.CompressDebugSections.zlib; + linker_compress_debug_sections = link.File.Lld.Elf.CompressDebugSections.zlib; } else if (mem.eql(u8, arg, "-pagezero_size")) { const next_arg = args_iter.nextOrFatal(); pagezero_size = std.fmt.parseUnsigned(u64, eatIntPrefix(next_arg, 16), 16) catch |err| { @@ -2368,7 +2368,7 @@ fn buildOutputType( if (it.only_arg.len == 0) { linker_compress_debug_sections = .zlib; } else { - linker_compress_debug_sections = std.meta.stringToEnum(link.File.Elf.CompressDebugSections, it.only_arg) orelse { + linker_compress_debug_sections = std.meta.stringToEnum(link.File.Lld.Elf.CompressDebugSections, it.only_arg) orelse { fatal("expected [none|zlib|zstd] after --compress-debug-sections, found '{s}'", .{it.only_arg}); }; } @@ -2505,7 +2505,7 @@ fn buildOutputType( linker_print_map = true; } else if (mem.eql(u8, arg, "--sort-section")) { const arg1 = linker_args_it.nextOrFatal(); - linker_sort_section = std.meta.stringToEnum(link.File.Elf.SortSection, arg1) orelse { + linker_sort_section = std.meta.stringToEnum(link.File.Lld.Elf.SortSection, arg1) orelse { fatal("expected [name|alignment] after --sort-section, found '{s}'", .{arg1}); }; } else if (mem.eql(u8, arg, "--allow-shlib-undefined") or @@ -2551,7 +2551,7 @@ fn buildOutputType( try linker_export_symbol_names.append(arena, linker_args_it.nextOrFatal()); } else if (mem.eql(u8, arg, "--compress-debug-sections")) { const arg1 = linker_args_it.nextOrFatal(); - linker_compress_debug_sections = std.meta.stringToEnum(link.File.Elf.CompressDebugSections, arg1) orelse { + linker_compress_debug_sections = std.meta.stringToEnum(link.File.Lld.Elf.CompressDebugSections, arg1) orelse { fatal("expected [none|zlib|zstd] after --compress-debug-sections, found '{s}'", .{arg1}); }; } else if (mem.startsWith(u8, arg, "-z")) { @@ -2764,7 +2764,7 @@ fn buildOutputType( mem.eql(u8, arg, "--hash-style")) { const next_arg = linker_args_it.nextOrFatal(); - hash_style = std.meta.stringToEnum(link.File.Elf.HashStyle, next_arg) orelse { + hash_style = std.meta.stringToEnum(link.File.Lld.Elf.HashStyle, next_arg) orelse { fatal("expected [sysv|gnu|both] after --hash-style, found '{s}'", .{ next_arg, }); -- 2.54.0 From 66d15d9d0974e1b493b717cf02deb435ebd13858 Mon Sep 17 00:00:00 2001 From: mlugg Date: Thu, 29 May 2025 01:27:37 +0100 Subject: [PATCH 04/35] link: make checking for failed types the responsibility of Compilation --- src/Compilation.zig | 21 +++++++++++++++++++++ src/Zcu/PerThread.zig | 8 -------- src/link.zig | 13 ------------- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/Compilation.zig b/src/Compilation.zig index f51020c0ff8cc5bd77c23b5a99797efe0d4cae70..e51b3de1ad8bd42a5d734b8b5c62dedf47d54b96 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -4553,12 +4553,33 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job) JobError!void { } } assert(nav.status == .fully_resolved); + if (!Air.valFullyResolved(zcu.navValue(nav_index), zcu)) { + // Type resolution failed in a way which affects this `Nav`. This is a transitive + // failure, but it doesn't need recording, because this `Nav` semantically depends + // on the failed type, so when it is changed the `Nav` will be updated. + return; + } comp.dispatchLinkTask(tid, .{ .link_nav = nav_index }); }, .link_func => |func| { + const zcu = comp.zcu.?; + if (!func.air.typesFullyResolved(zcu)) { + // Type resolution failed in a way which affects this function. This is a transitive + // failure, but it doesn't need recording, because this function semantically depends + // on the failed type, so when it is changed the function is updated. + return; + } comp.dispatchLinkTask(tid, .{ .link_func = func }); }, .link_type => |ty| { + const zcu = comp.zcu.?; + if (zcu.failed_types.fetchSwapRemove(ty)) |*entry| entry.value.deinit(zcu.gpa); + if (!Air.typeFullyResolved(.fromInterned(ty), zcu)) { + // Type resolution failed in a way which affects this type. This is a transitive + // failure, but it doesn't need recording, because this type semantically depends + // on the failed type, so when that is changed, this type will be updated. + return; + } comp.dispatchLinkTask(tid, .{ .link_type = ty }); }, .update_line_number => |ti| { diff --git a/src/Zcu/PerThread.zig b/src/Zcu/PerThread.zig index b10e6d7c4195f4ca315e1f93f4a9751595747946..137d93b82a3ad6a0b5d5d86d331542facd0f7fd1 100644 --- a/src/Zcu/PerThread.zig +++ b/src/Zcu/PerThread.zig @@ -1739,14 +1739,6 @@ pub fn linkerUpdateFunc(pt: Zcu.PerThread, func_index: InternPool.Index, air: *A const codegen_prog_node = zcu.codegen_prog_node.start(nav.fqn.toSlice(ip), 0); defer codegen_prog_node.end(); - if (!air.typesFullyResolved(zcu)) { - // A type we depend on failed to resolve. This is a transitive failure. - // Correcting this failure will involve changing a type this function - // depends on, hence triggering re-analysis of this function, so this - // interacts correctly with incremental compilation. - return; - } - legalize: { try air.legalize(pt, @import("../codegen.zig").legalizeFeatures(pt, nav_index) orelse break :legalize); } diff --git a/src/link.zig b/src/link.zig index 68ea533eedb1d537b5138a048127340382b201e5..4b4c3c611b9468aa104af95550e0df3d9c1b5719 100644 --- a/src/link.zig +++ b/src/link.zig @@ -1424,12 +1424,6 @@ pub fn doTask(comp: *Compilation, tid: usize, task: Task) void { const zcu = comp.zcu.?; const pt: Zcu.PerThread = .activate(zcu, @enumFromInt(tid)); defer pt.deactivate(); - if (!Air.valFullyResolved(zcu.navValue(nav_index), zcu)) { - // Type resolution failed in a way which affects this `Nav`. This is a transitive - // failure, but it doesn't need recording, because this `Nav` semantically depends - // on the failed type, so when it is changed the `Nav` will be updated. - return; - } if (zcu.llvm_object) |llvm_object| { llvm_object.updateNav(pt, nav_index) catch |err| switch (err) { error.OutOfMemory => diags.setAllocFailure(), @@ -1473,13 +1467,6 @@ pub fn doTask(comp: *Compilation, tid: usize, task: Task) void { const zcu = comp.zcu.?; const pt: Zcu.PerThread = .activate(zcu, @enumFromInt(tid)); defer pt.deactivate(); - if (zcu.failed_types.fetchSwapRemove(ty)) |*entry| entry.value.deinit(zcu.gpa); - if (!Air.typeFullyResolved(.fromInterned(ty), zcu)) { - // Type resolution failed in a way which affects this type. This is a transitive - // failure, but it doesn't need recording, because this type semantically depends - // on the failed type, so when that is changed, this type will be updated. - return; - } if (zcu.llvm_object == null) { if (comp.bin_file) |lf| { lf.updateContainerType(pt, ty) catch |err| switch (err) { -- 2.54.0 From 9eb400ef19391261a3b61129d8665602c89959c5 Mon Sep 17 00:00:00 2001 From: mlugg Date: Thu, 29 May 2025 05:38:55 +0100 Subject: [PATCH 05/35] compiler: rework backend pipeline to separate codegen and link The idea here is that instead of the linker calling into codegen, instead codegen should run before we touch the linker, and after MIR is produced, it is sent to the linker. Aside from simplifying the call graph (by preventing N linkers from each calling into M codegen backends!), this has the huge benefit that it is possible to parallellize codegen separately from linking. The threading model can look like this: * 1 semantic analysis thread, which generates AIR * N codegen threads, which process AIR into MIR * 1 linker thread, which emits MIR to the binary The codegen threads are also responsible for `Air.Legalize` and `Air.Liveness`; it's more efficient to do this work here instead of blocking the main thread for this trivially parallel task. I have repurposed the `Zcu.Feature.separate_thread` backend feature to indicate support for this 1:N:1 threading pattern. This commit makes the C backend support this feature, since it was relatively easy to divorce from `link.C`: it just required eliminating some shared buffers. Other backends don't currently support this feature. In fact, they don't even compile -- the next few commits will fix them back up. --- src/Compilation.zig | 234 ++++++++++++++++++++++--------------- src/ThreadSafeQueue.zig | 72 ------------ src/Zcu.zig | 50 +++++++- src/Zcu/PerThread.zig | 162 +++++++++++++------------ src/codegen.zig | 97 ++++++++++++++- src/codegen/c.zig | 146 +++++++++++++++++++---- src/codegen/llvm.zig | 22 ++-- src/codegen/spirv.zig | 5 +- src/dev.zig | 9 ++ src/libs/freebsd.zig | 2 +- src/libs/glibc.zig | 2 +- src/libs/libcxx.zig | 4 +- src/libs/libtsan.zig | 2 +- src/libs/libunwind.zig | 2 +- src/libs/musl.zig | 2 +- src/libs/netbsd.zig | 2 +- src/link.zig | 189 +++++++++++++++--------------- src/link/C.zig | 143 +++++++++-------------- src/link/Coff.zig | 17 +-- src/link/Elf.zig | 6 +- src/link/Elf/ZigObject.zig | 12 +- src/link/Queue.zig | 234 +++++++++++++++++++++++++++++++++++++ src/target.zig | 4 +- 23 files changed, 918 insertions(+), 500 deletions(-) delete mode 100644 src/ThreadSafeQueue.zig create mode 100644 src/link/Queue.zig diff --git a/src/Compilation.zig b/src/Compilation.zig index e51b3de1ad8bd42a5d734b8b5c62dedf47d54b96..64ec1ab0a88154c9a87768c423563659b47267ef 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -43,7 +43,6 @@ const Air = @import("Air.zig"); const Builtin = @import("Builtin.zig"); const LlvmObject = @import("codegen/llvm.zig").Object; const dev = @import("dev.zig"); -const ThreadSafeQueue = @import("ThreadSafeQueue.zig").ThreadSafeQueue; pub const Config = @import("Compilation/Config.zig"); @@ -113,17 +112,7 @@ win32_resource_table: if (dev.env.supports(.win32_resource)) std.AutoArrayHashMa } = .{}, link_diags: link.Diags, -link_task_queue: ThreadSafeQueue(link.Task) = .empty, -/// Ensure only 1 simultaneous call to `flushTaskQueue`. -link_task_queue_safety: std.debug.SafetyLock = .{}, -/// If any tasks are queued up that depend on prelink being finished, they are moved -/// here until prelink finishes. -link_task_queue_postponed: std.ArrayListUnmanaged(link.Task) = .empty, -/// Initialized with how many link input tasks are expected. After this reaches zero -/// the linker will begin the prelink phase. -/// Initialized in the Compilation main thread before the pipeline; modified only in -/// the linker task thread. -remaining_prelink_tasks: u32, +link_task_queue: link.Queue = .empty, /// Set of work that can be represented by only flags to determine whether the /// work is queued or not. @@ -846,15 +835,24 @@ pub const RcIncludes = enum { }; const Job = union(enum) { - /// Corresponds to the task in `link.Task`. - /// Only needed for backends that haven't yet been updated to not race against Sema. + /// Given the generated AIR for a function, put it onto the code generation queue. + /// This `Job` exists (instead of the `link.ZcuTask` being directly queued) to ensure that + /// all types are resolved before the linker task is queued. + /// If the backend does not support `Zcu.Feature.separate_thread`, codegen and linking happen immediately. + codegen_func: struct { + func: InternPool.Index, + /// The AIR emitted from analyzing `func`; owned by this `Job` in `gpa`. + air: Air, + }, + /// Queue a `link.ZcuTask` to emit this non-function `Nav` into the output binary. + /// This `Job` exists (instead of the `link.ZcuTask` being directly queued) to ensure that + /// all types are resolved before the linker task is queued. + /// If the backend does not support `Zcu.Feature.separate_thread`, the task is run immediately. link_nav: InternPool.Nav.Index, - /// Corresponds to the task in `link.Task`. - /// TODO: this is currently also responsible for performing codegen. - /// Only needed for backends that haven't yet been updated to not race against Sema. - link_func: link.Task.CodegenFunc, - /// Corresponds to the task in `link.Task`. - /// Only needed for backends that haven't yet been updated to not race against Sema. + /// Queue a `link.ZcuTask` to emit debug information for this container type. + /// This `Job` exists (instead of the `link.ZcuTask` being directly queued) to ensure that + /// all types are resolved before the linker task is queued. + /// If the backend does not support `Zcu.Feature.separate_thread`, the task is run immediately. link_type: InternPool.Index, update_line_number: InternPool.TrackedInst.Index, /// The `AnalUnit`, which is *not* a `func`, must be semantically analyzed. @@ -880,13 +878,13 @@ const Job = union(enum) { return switch (tag) { // Prioritize functions so that codegen can get to work on them on a // separate thread, while Sema goes back to its own work. - .resolve_type_fully, .analyze_func, .link_func => 0, + .resolve_type_fully, .analyze_func, .codegen_func => 0, else => 1, }; } comptime { // Job dependencies - assert(stage(.resolve_type_fully) <= stage(.link_func)); + assert(stage(.resolve_type_fully) <= stage(.codegen_func)); } }; @@ -2004,7 +2002,6 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil .file_system_inputs = options.file_system_inputs, .parent_whole_cache = options.parent_whole_cache, .link_diags = .init(gpa), - .remaining_prelink_tasks = 0, }; // Prevent some footguns by making the "any" fields of config reflect @@ -2213,7 +2210,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil }; comp.c_object_table.putAssumeCapacityNoClobber(c_object, {}); } - comp.remaining_prelink_tasks += @intCast(comp.c_object_table.count()); + comp.link_task_queue.pending_prelink_tasks += @intCast(comp.c_object_table.count()); // Add a `Win32Resource` for each `rc_source_files` and one for `manifest_file`. const win32_resource_count = @@ -2224,7 +2221,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil // Add this after adding logic to updateWin32Resource to pass the // result into link.loadInput. loadInput integration is not implemented // for Windows linking logic yet. - //comp.remaining_prelink_tasks += @intCast(win32_resource_count); + //comp.link_task_queue.pending_prelink_tasks += @intCast(win32_resource_count); for (options.rc_source_files) |rc_source_file| { const win32_resource = try gpa.create(Win32Resource); errdefer gpa.destroy(win32_resource); @@ -2275,78 +2272,76 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil const paths = try lci.resolveCrtPaths(arena, basenames, target); const fields = @typeInfo(@TypeOf(paths)).@"struct".fields; - try comp.link_task_queue.shared.ensureUnusedCapacity(gpa, fields.len + 1); + try comp.link_task_queue.queued_prelink.ensureUnusedCapacity(gpa, fields.len + 1); inline for (fields) |field| { if (@field(paths, field.name)) |path| { - comp.link_task_queue.shared.appendAssumeCapacity(.{ .load_object = path }); - comp.remaining_prelink_tasks += 1; + comp.link_task_queue.queued_prelink.appendAssumeCapacity(.{ .load_object = path }); } } // Loads the libraries provided by `target_util.libcFullLinkFlags(target)`. - comp.link_task_queue.shared.appendAssumeCapacity(.load_host_libc); - comp.remaining_prelink_tasks += 1; + comp.link_task_queue.queued_prelink.appendAssumeCapacity(.load_host_libc); } else if (target.isMuslLibC()) { if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable; if (musl.needsCrt0(comp.config.output_mode, comp.config.link_mode, comp.config.pie)) |f| { comp.queued_jobs.musl_crt_file[@intFromEnum(f)] = true; - comp.remaining_prelink_tasks += 1; + comp.link_task_queue.pending_prelink_tasks += 1; } switch (comp.config.link_mode) { .static => comp.queued_jobs.musl_crt_file[@intFromEnum(musl.CrtFile.libc_a)] = true, .dynamic => comp.queued_jobs.musl_crt_file[@intFromEnum(musl.CrtFile.libc_so)] = true, } - comp.remaining_prelink_tasks += 1; + comp.link_task_queue.pending_prelink_tasks += 1; } else if (target.isGnuLibC()) { if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable; if (glibc.needsCrt0(comp.config.output_mode)) |f| { comp.queued_jobs.glibc_crt_file[@intFromEnum(f)] = true; - comp.remaining_prelink_tasks += 1; + comp.link_task_queue.pending_prelink_tasks += 1; } comp.queued_jobs.glibc_shared_objects = true; - comp.remaining_prelink_tasks += glibc.sharedObjectsCount(&target); + comp.link_task_queue.pending_prelink_tasks += glibc.sharedObjectsCount(&target); comp.queued_jobs.glibc_crt_file[@intFromEnum(glibc.CrtFile.libc_nonshared_a)] = true; - comp.remaining_prelink_tasks += 1; + comp.link_task_queue.pending_prelink_tasks += 1; } else if (target.isFreeBSDLibC()) { if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable; if (freebsd.needsCrt0(comp.config.output_mode)) |f| { comp.queued_jobs.freebsd_crt_file[@intFromEnum(f)] = true; - comp.remaining_prelink_tasks += 1; + comp.link_task_queue.pending_prelink_tasks += 1; } comp.queued_jobs.freebsd_shared_objects = true; - comp.remaining_prelink_tasks += freebsd.sharedObjectsCount(); + comp.link_task_queue.pending_prelink_tasks += freebsd.sharedObjectsCount(); } else if (target.isNetBSDLibC()) { if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable; if (netbsd.needsCrt0(comp.config.output_mode)) |f| { comp.queued_jobs.netbsd_crt_file[@intFromEnum(f)] = true; - comp.remaining_prelink_tasks += 1; + comp.link_task_queue.pending_prelink_tasks += 1; } comp.queued_jobs.netbsd_shared_objects = true; - comp.remaining_prelink_tasks += netbsd.sharedObjectsCount(); + comp.link_task_queue.pending_prelink_tasks += netbsd.sharedObjectsCount(); } else if (target.isWasiLibC()) { if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable; for (comp.wasi_emulated_libs) |crt_file| { comp.queued_jobs.wasi_libc_crt_file[@intFromEnum(crt_file)] = true; } - comp.remaining_prelink_tasks += @intCast(comp.wasi_emulated_libs.len); + comp.link_task_queue.pending_prelink_tasks += @intCast(comp.wasi_emulated_libs.len); comp.queued_jobs.wasi_libc_crt_file[@intFromEnum(wasi_libc.execModelCrtFile(comp.config.wasi_exec_model))] = true; comp.queued_jobs.wasi_libc_crt_file[@intFromEnum(wasi_libc.CrtFile.libc_a)] = true; - comp.remaining_prelink_tasks += 2; + comp.link_task_queue.pending_prelink_tasks += 2; } else if (target.isMinGW()) { if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable; const main_crt_file: mingw.CrtFile = if (is_dyn_lib) .dllcrt2_o else .crt2_o; comp.queued_jobs.mingw_crt_file[@intFromEnum(main_crt_file)] = true; comp.queued_jobs.mingw_crt_file[@intFromEnum(mingw.CrtFile.libmingw32_lib)] = true; - comp.remaining_prelink_tasks += 2; + comp.link_task_queue.pending_prelink_tasks += 2; // When linking mingw-w64 there are some import libs we always need. try comp.windows_libs.ensureUnusedCapacity(gpa, mingw.always_link_libs.len); @@ -2360,7 +2355,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil target.isMinGW()) { comp.queued_jobs.zigc_lib = true; - comp.remaining_prelink_tasks += 1; + comp.link_task_queue.pending_prelink_tasks += 1; } } @@ -2377,53 +2372,53 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil } if (comp.wantBuildLibUnwindFromSource()) { comp.queued_jobs.libunwind = true; - comp.remaining_prelink_tasks += 1; + comp.link_task_queue.pending_prelink_tasks += 1; } if (build_options.have_llvm and is_exe_or_dyn_lib and comp.config.link_libcpp) { comp.queued_jobs.libcxx = true; comp.queued_jobs.libcxxabi = true; - comp.remaining_prelink_tasks += 2; + comp.link_task_queue.pending_prelink_tasks += 2; } if (build_options.have_llvm and is_exe_or_dyn_lib and comp.config.any_sanitize_thread) { comp.queued_jobs.libtsan = true; - comp.remaining_prelink_tasks += 1; + comp.link_task_queue.pending_prelink_tasks += 1; } if (can_build_compiler_rt) { if (comp.compiler_rt_strat == .lib) { log.debug("queuing a job to build compiler_rt_lib", .{}); comp.queued_jobs.compiler_rt_lib = true; - comp.remaining_prelink_tasks += 1; + comp.link_task_queue.pending_prelink_tasks += 1; } else if (comp.compiler_rt_strat == .obj) { log.debug("queuing a job to build compiler_rt_obj", .{}); // In this case we are making a static library, so we ask // for a compiler-rt object to put in it. comp.queued_jobs.compiler_rt_obj = true; - comp.remaining_prelink_tasks += 1; + comp.link_task_queue.pending_prelink_tasks += 1; } if (comp.ubsan_rt_strat == .lib) { log.debug("queuing a job to build ubsan_rt_lib", .{}); comp.queued_jobs.ubsan_rt_lib = true; - comp.remaining_prelink_tasks += 1; + comp.link_task_queue.pending_prelink_tasks += 1; } else if (comp.ubsan_rt_strat == .obj) { log.debug("queuing a job to build ubsan_rt_obj", .{}); comp.queued_jobs.ubsan_rt_obj = true; - comp.remaining_prelink_tasks += 1; + comp.link_task_queue.pending_prelink_tasks += 1; } if (is_exe_or_dyn_lib and comp.config.any_fuzz) { log.debug("queuing a job to build libfuzzer", .{}); comp.queued_jobs.fuzzer_lib = true; - comp.remaining_prelink_tasks += 1; + comp.link_task_queue.pending_prelink_tasks += 1; } } } - try comp.link_task_queue.shared.append(gpa, .load_explicitly_provided); - comp.remaining_prelink_tasks += 1; + try comp.link_task_queue.queued_prelink.append(gpa, .load_explicitly_provided); } - log.debug("total prelink tasks: {d}", .{comp.remaining_prelink_tasks}); + log.debug("queued prelink tasks: {d}", .{comp.link_task_queue.queued_prelink.items.len}); + log.debug("pending prelink tasks: {d}", .{comp.link_task_queue.pending_prelink_tasks}); return comp; } @@ -2431,6 +2426,10 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil pub fn destroy(comp: *Compilation) void { const gpa = comp.gpa; + // This needs to be destroyed first, because it might contain MIR which we only know + // how to interpret (which kind of MIR it is) from `comp.bin_file`. + comp.link_task_queue.deinit(comp); + if (comp.bin_file) |lf| lf.destroy(); if (comp.zcu) |zcu| zcu.deinit(); comp.cache_use.deinit(); @@ -2512,8 +2511,6 @@ pub fn destroy(comp: *Compilation) void { comp.failed_win32_resources.deinit(gpa); comp.link_diags.deinit(); - comp.link_task_queue.deinit(gpa); - comp.link_task_queue_postponed.deinit(gpa); comp.clearMiscFailures(); @@ -4180,9 +4177,7 @@ fn performAllTheWorkInner( comp.link_task_wait_group.reset(); defer comp.link_task_wait_group.wait(); - if (comp.link_task_queue.start()) { - comp.thread_pool.spawnWgId(&comp.link_task_wait_group, link.flushTaskQueue, .{comp}); - } + comp.link_task_queue.start(comp); if (comp.docs_emit != null) { dev.check(.docs_emit); @@ -4498,7 +4493,7 @@ fn performAllTheWorkInner( comp.link_task_wait_group.wait(); comp.link_task_wait_group.reset(); std.log.scoped(.link).debug("finished waiting for link_task_wait_group", .{}); - if (comp.remaining_prelink_tasks > 0) { + if (comp.link_task_queue.pending_prelink_tasks > 0) { // Indicates an error occurred preventing prelink phase from completing. return; } @@ -4543,6 +4538,45 @@ pub fn queueJobs(comp: *Compilation, jobs: []const Job) !void { fn processOneJob(tid: usize, comp: *Compilation, job: Job) JobError!void { switch (job) { + .codegen_func => |func| { + const zcu = comp.zcu.?; + const gpa = zcu.gpa; + var air = func.air; + errdefer air.deinit(gpa); + if (!air.typesFullyResolved(zcu)) { + // Type resolution failed in a way which affects this function. This is a transitive + // failure, but it doesn't need recording, because this function semantically depends + // on the failed type, so when it is changed the function is updated. + air.deinit(gpa); + return; + } + const pt: Zcu.PerThread = .activate(comp.zcu.?, @enumFromInt(tid)); + defer pt.deactivate(); + const shared_mir = try gpa.create(link.ZcuTask.LinkFunc.SharedMir); + shared_mir.* = .{ + .status = .init(.pending), + .value = undefined, + }; + if (comp.separateCodegenThreadOk()) { + // `workerZcuCodegen` takes ownership of `air`. + comp.thread_pool.spawnWgId(&comp.link_task_wait_group, workerZcuCodegen, .{ comp, func.func, air, shared_mir }); + comp.dispatchZcuLinkTask(tid, .{ .link_func = .{ + .func = func.func, + .mir = shared_mir, + .air = undefined, + } }); + } else { + const emit_needs_air = !zcu.backendSupportsFeature(.separate_thread); + pt.runCodegen(func.func, &air, shared_mir); + assert(shared_mir.status.load(.monotonic) != .pending); + comp.dispatchZcuLinkTask(tid, .{ .link_func = .{ + .func = func.func, + .mir = shared_mir, + .air = if (emit_needs_air) &air else undefined, + } }); + air.deinit(gpa); + } + }, .link_nav => |nav_index| { const zcu = comp.zcu.?; const nav = zcu.intern_pool.getNav(nav_index); @@ -4559,17 +4593,7 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job) JobError!void { // on the failed type, so when it is changed the `Nav` will be updated. return; } - comp.dispatchLinkTask(tid, .{ .link_nav = nav_index }); - }, - .link_func => |func| { - const zcu = comp.zcu.?; - if (!func.air.typesFullyResolved(zcu)) { - // Type resolution failed in a way which affects this function. This is a transitive - // failure, but it doesn't need recording, because this function semantically depends - // on the failed type, so when it is changed the function is updated. - return; - } - comp.dispatchLinkTask(tid, .{ .link_func = func }); + comp.dispatchZcuLinkTask(tid, .{ .link_nav = nav_index }); }, .link_type => |ty| { const zcu = comp.zcu.?; @@ -4580,10 +4604,10 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job) JobError!void { // on the failed type, so when that is changed, this type will be updated. return; } - comp.dispatchLinkTask(tid, .{ .link_type = ty }); + comp.dispatchZcuLinkTask(tid, .{ .link_type = ty }); }, .update_line_number => |ti| { - comp.dispatchLinkTask(tid, .{ .update_line_number = ti }); + comp.dispatchZcuLinkTask(tid, .{ .update_line_number = ti }); }, .analyze_func => |func| { const named_frame = tracy.namedFrame("analyze_func"); @@ -4675,18 +4699,7 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job) JobError!void { } } -/// The reason for the double-queue here is that the first queue ensures any -/// resolve_type_fully tasks are complete before this dispatch function is called. -fn dispatchLinkTask(comp: *Compilation, tid: usize, link_task: link.Task) void { - if (comp.separateCodegenThreadOk()) { - comp.queueLinkTasks(&.{link_task}); - } else { - assert(comp.remaining_prelink_tasks == 0); - link.doTask(comp, tid, link_task); - } -} - -fn separateCodegenThreadOk(comp: *const Compilation) bool { +pub fn separateCodegenThreadOk(comp: *const Compilation) bool { if (InternPool.single_threaded) return false; const zcu = comp.zcu orelse return true; return zcu.backendSupportsFeature(.separate_thread); @@ -5273,6 +5286,21 @@ pub const RtOptions = struct { allow_lto: bool = true, }; +fn workerZcuCodegen( + tid: usize, + comp: *Compilation, + func_index: InternPool.Index, + orig_air: Air, + out: *link.ZcuTask.LinkFunc.SharedMir, +) void { + var air = orig_air; + // We own `air` now, so we are responsbile for freeing it. + defer air.deinit(comp.gpa); + const pt: Zcu.PerThread = .activate(comp.zcu.?, @enumFromInt(tid)); + defer pt.deactivate(); + pt.runCodegen(func_index, &air, out); +} + fn buildRt( comp: *Compilation, root_source_name: []const u8, @@ -5804,7 +5832,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr }, }; - comp.queueLinkTasks(&.{.{ .load_object = c_object.status.success.object_path }}); + comp.queuePrelinkTasks(&.{.{ .load_object = c_object.status.success.object_path }}); } fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32_resource_prog_node: std.Progress.Node) !void { @@ -7237,7 +7265,7 @@ fn buildOutputFromZig( assert(out.* == null); out.* = crt_file; - comp.queueLinkTaskMode(crt_file.full_object_path, &config); + comp.queuePrelinkTaskMode(crt_file.full_object_path, &config); } pub const CrtFileOptions = struct { @@ -7361,7 +7389,7 @@ pub fn build_crt_file( try comp.updateSubCompilation(sub_compilation, misc_task_tag, prog_node); const crt_file = try sub_compilation.toCrtFile(); - comp.queueLinkTaskMode(crt_file.full_object_path, &config); + comp.queuePrelinkTaskMode(crt_file.full_object_path, &config); { comp.mutex.lock(); @@ -7371,8 +7399,8 @@ pub fn build_crt_file( } } -pub fn queueLinkTaskMode(comp: *Compilation, path: Cache.Path, config: *const Compilation.Config) void { - comp.queueLinkTasks(switch (config.output_mode) { +pub fn queuePrelinkTaskMode(comp: *Compilation, path: Cache.Path, config: *const Compilation.Config) void { + comp.queuePrelinkTasks(switch (config.output_mode) { .Exe => unreachable, .Obj => &.{.{ .load_object = path }}, .Lib => &.{switch (config.link_mode) { @@ -7384,12 +7412,30 @@ pub fn queueLinkTaskMode(comp: *Compilation, path: Cache.Path, config: *const Co /// Only valid to call during `update`. Automatically handles queuing up a /// linker worker task if there is not already one. -pub fn queueLinkTasks(comp: *Compilation, tasks: []const link.Task) void { - if (comp.link_task_queue.enqueue(comp.gpa, tasks) catch |err| switch (err) { +pub fn queuePrelinkTasks(comp: *Compilation, tasks: []const link.PrelinkTask) void { + comp.link_task_queue.enqueuePrelink(comp, tasks) catch |err| switch (err) { error.OutOfMemory => return comp.setAllocFailure(), - }) { - comp.thread_pool.spawnWgId(&comp.link_task_wait_group, link.flushTaskQueue, .{comp}); + }; +} + +/// The reason for the double-queue here is that the first queue ensures any +/// resolve_type_fully tasks are complete before this dispatch function is called. +fn dispatchZcuLinkTask(comp: *Compilation, tid: usize, task: link.ZcuTask) void { + if (!comp.separateCodegenThreadOk()) { + assert(tid == 0); + if (task == .link_func) { + assert(task.link_func.mir.status.load(.monotonic) != .pending); + } + link.doZcuTask(comp, tid, task); + task.deinit(comp.zcu.?); + return; } + comp.link_task_queue.enqueueZcu(comp, task) catch |err| switch (err) { + error.OutOfMemory => { + task.deinit(comp.zcu.?); + comp.setAllocFailure(); + }, + }; } pub fn toCrtFile(comp: *Compilation) Allocator.Error!CrtFile { diff --git a/src/ThreadSafeQueue.zig b/src/ThreadSafeQueue.zig deleted file mode 100644 index 74bbdc418f4326fb8e7992c9b1f39846f5715ba6..0000000000000000000000000000000000000000 --- a/src/ThreadSafeQueue.zig +++ /dev/null @@ -1,72 +0,0 @@ -const std = @import("std"); -const assert = std.debug.assert; -const Allocator = std.mem.Allocator; - -pub fn ThreadSafeQueue(comptime T: type) type { - return struct { - worker_owned: std.ArrayListUnmanaged(T), - /// Protected by `mutex`. - shared: std.ArrayListUnmanaged(T), - mutex: std.Thread.Mutex, - state: State, - - const Self = @This(); - - pub const State = enum { wait, run }; - - pub const empty: Self = .{ - .worker_owned = .empty, - .shared = .empty, - .mutex = .{}, - .state = .wait, - }; - - pub fn deinit(self: *Self, gpa: Allocator) void { - self.worker_owned.deinit(gpa); - self.shared.deinit(gpa); - self.* = undefined; - } - - /// Must be called from the worker thread. - pub fn check(self: *Self) ?[]T { - assert(self.worker_owned.items.len == 0); - { - self.mutex.lock(); - defer self.mutex.unlock(); - assert(self.state == .run); - if (self.shared.items.len == 0) { - self.state = .wait; - return null; - } - std.mem.swap(std.ArrayListUnmanaged(T), &self.worker_owned, &self.shared); - } - const result = self.worker_owned.items; - self.worker_owned.clearRetainingCapacity(); - return result; - } - - /// Adds items to the queue, returning true if and only if the worker - /// thread is waiting. Thread-safe. - /// Not safe to call from the worker thread. - pub fn enqueue(self: *Self, gpa: Allocator, items: []const T) error{OutOfMemory}!bool { - self.mutex.lock(); - defer self.mutex.unlock(); - try self.shared.appendSlice(gpa, items); - return switch (self.state) { - .run => false, - .wait => { - self.state = .run; - return true; - }, - }; - } - - /// Safe only to call exactly once when initially starting the worker. - pub fn start(self: *Self) bool { - assert(self.state == .wait); - if (self.shared.items.len == 0) return false; - self.state = .run; - return true; - } - }; -} diff --git a/src/Zcu.zig b/src/Zcu.zig index 6a6a74e260e1dfbdadfe4b4a8619472078469f59..91d2c0ffff4ea54e62b1ff61ebd4ba17643b269a 100644 --- a/src/Zcu.zig +++ b/src/Zcu.zig @@ -171,6 +171,8 @@ transitive_failed_analysis: std.AutoArrayHashMapUnmanaged(AnalUnit, void) = .emp /// This `Nav` succeeded analysis, but failed codegen. /// This may be a simple "value" `Nav`, or it may be a function. /// The ErrorMsg memory is owned by the `AnalUnit`, using Module's general purpose allocator. +/// While multiple threads are active (most of the time!), this is guarded by `zcu.comp.mutex`, as +/// codegen and linking run on a separate thread. failed_codegen: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, *ErrorMsg) = .empty, failed_types: std.AutoArrayHashMapUnmanaged(InternPool.Index, *ErrorMsg) = .empty, /// Keep track of `@compileLog`s per `AnalUnit`. @@ -3817,7 +3819,36 @@ pub const Feature = enum { is_named_enum_value, error_set_has_value, field_reordering, - /// If the backend supports running from another thread. + /// In theory, backends are supposed to work like this: + /// + /// * The AIR emitted by `Sema` is converted into MIR by `codegen.generateFunction`. This pass + /// is "pure", in that it does not depend on or modify any external mutable state. + /// + /// * That MIR is sent to the linker, which calls `codegen.emitFunction` to convert the MIR to + /// finalized machine code. This process is permitted to query and modify linker state. + /// + /// * The linker stores the resulting machine code in the binary as needed. + /// + /// The first stage described above can run in parallel to the rest of the compiler, and even to + /// other code generation work; we can run as many codegen threads as we want in parallel because + /// of the fact that this pass is pure. Emit and link must be single-threaded, but are generally + /// very fast, so that isn't a problem. + /// + /// Unfortunately, some code generation implementations currently query and/or mutate linker state + /// or even (in the case of the LLVM backend) semantic analysis state. Such backends cannot be run + /// in parallel with each other, with linking, or (potentially) with semantic analysis. + /// + /// Additionally, some backends continue to need the AIR in the "emit" stage, despite this pass + /// operating on MIR. This complicates memory management under the threading model above. + /// + /// These are both **bugs** in backend implementations, left over from legacy code. However, they + /// are difficult to fix. So, this `Feature` currently guards correct threading of code generation: + /// + /// * With this feature enabled, the backend is threaded as described above. The "emit" stage does + /// not have access to AIR (it will be `undefined`; see `codegen.emitFunction`). + /// + /// * With this feature disabled, semantic analysis, code generation, and linking all occur on the + /// same thread, and the "emit" stage has access to AIR. separate_thread, }; @@ -4566,22 +4597,29 @@ pub fn codegenFail( comptime format: []const u8, args: anytype, ) CodegenFailError { - const gpa = zcu.gpa; - try zcu.failed_codegen.ensureUnusedCapacity(gpa, 1); - const msg = try Zcu.ErrorMsg.create(gpa, zcu.navSrcLoc(nav_index), format, args); - zcu.failed_codegen.putAssumeCapacityNoClobber(nav_index, msg); - return error.CodegenFail; + const msg = try Zcu.ErrorMsg.create(zcu.gpa, zcu.navSrcLoc(nav_index), format, args); + return zcu.codegenFailMsg(nav_index, msg); } +/// Takes ownership of `msg`, even on OOM. pub fn codegenFailMsg(zcu: *Zcu, nav_index: InternPool.Nav.Index, msg: *ErrorMsg) CodegenFailError { const gpa = zcu.gpa; { + zcu.comp.mutex.lock(); + defer zcu.comp.mutex.unlock(); errdefer msg.deinit(gpa); try zcu.failed_codegen.putNoClobber(gpa, nav_index, msg); } return error.CodegenFail; } +/// Asserts that `zcu.failed_codegen` contains the key `nav`, with the necessary lock held. +pub fn assertCodegenFailed(zcu: *Zcu, nav: InternPool.Nav.Index) void { + zcu.comp.mutex.lock(); + defer zcu.comp.mutex.unlock(); + assert(zcu.failed_codegen.contains(nav)); +} + pub fn codegenFailType( zcu: *Zcu, ty_index: InternPool.Index, diff --git a/src/Zcu/PerThread.zig b/src/Zcu/PerThread.zig index 137d93b82a3ad6a0b5d5d86d331542facd0f7fd1..92f1adbf2abb3bdd06d45ce3990ae464eca5d02d 100644 --- a/src/Zcu/PerThread.zig +++ b/src/Zcu/PerThread.zig @@ -27,6 +27,7 @@ const Type = @import("../Type.zig"); const Value = @import("../Value.zig"); const Zcu = @import("../Zcu.zig"); const Compilation = @import("../Compilation.zig"); +const codegen = @import("../codegen.zig"); const Zir = std.zig.Zir; const Zoir = std.zig.Zoir; const ZonGen = std.zig.ZonGen; @@ -1716,7 +1717,7 @@ fn analyzeFuncBody( } // This job depends on any resolve_type_fully jobs queued up before it. - try comp.queueJob(.{ .link_func = .{ + try comp.queueJob(.{ .codegen_func = .{ .func = func_index, .air = air, } }); @@ -1724,79 +1725,6 @@ fn analyzeFuncBody( return .{ .ies_outdated = ies_outdated }; } -/// Takes ownership of `air`, even on error. -/// If any types referenced by `air` are unresolved, marks the codegen as failed. -pub fn linkerUpdateFunc(pt: Zcu.PerThread, func_index: InternPool.Index, air: *Air) Allocator.Error!void { - const zcu = pt.zcu; - const gpa = zcu.gpa; - const ip = &zcu.intern_pool; - const comp = zcu.comp; - - const func = zcu.funcInfo(func_index); - const nav_index = func.owner_nav; - const nav = ip.getNav(nav_index); - - const codegen_prog_node = zcu.codegen_prog_node.start(nav.fqn.toSlice(ip), 0); - defer codegen_prog_node.end(); - - legalize: { - try air.legalize(pt, @import("../codegen.zig").legalizeFeatures(pt, nav_index) orelse break :legalize); - } - - var liveness = try Air.Liveness.analyze(zcu, air.*, ip); - defer liveness.deinit(gpa); - - if (build_options.enable_debug_extensions and comp.verbose_air) { - std.debug.print("# Begin Function AIR: {}:\n", .{nav.fqn.fmt(ip)}); - air.dump(pt, liveness); - std.debug.print("# End Function AIR: {}\n\n", .{nav.fqn.fmt(ip)}); - } - - if (std.debug.runtime_safety) { - var verify: Air.Liveness.Verify = .{ - .gpa = gpa, - .zcu = zcu, - .air = air.*, - .liveness = liveness, - .intern_pool = ip, - }; - defer verify.deinit(); - - verify.verify() catch |err| switch (err) { - error.OutOfMemory => return error.OutOfMemory, - else => { - try zcu.failed_codegen.putNoClobber(gpa, nav_index, try Zcu.ErrorMsg.create( - gpa, - zcu.navSrcLoc(nav_index), - "invalid liveness: {s}", - .{@errorName(err)}, - )); - return; - }, - }; - } - - 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 => { - try zcu.failed_codegen.putNoClobber(gpa, nav_index, try Zcu.ErrorMsg.create( - gpa, - zcu.navSrcLoc(nav_index), - "unable to codegen: {s}", - .{@errorName(err)}, - )); - // Not a retryable failure. - }, - }; - } -} - pub fn semaMod(pt: Zcu.PerThread, mod: *Module) !void { dev.check(.sema); const file_index = pt.zcu.module_roots.get(mod).?.unwrap().?; @@ -3449,7 +3377,7 @@ pub fn populateTestFunctions( } // The linker thread is not running, so we actually need to dispatch this task directly. - @import("../link.zig").doTask(zcu.comp, @intFromEnum(pt.tid), .{ .link_nav = nav_index }); + @import("../link.zig").doZcuTask(zcu.comp, @intFromEnum(pt.tid), .{ .link_nav = nav_index }); } } @@ -4442,3 +4370,87 @@ pub fn addDependency(pt: Zcu.PerThread, unit: AnalUnit, dependee: InternPool.Dep try info.deps.append(gpa, dependee); } } + +/// Performs code generation, which comes after `Sema` but before `link` in the pipeline. +/// This part of the pipeline is self-contained/"pure", so can be run in parallel with most +/// other code. This function is currently run either on the main thread, or on a separate +/// codegen thread, depending on whether the backend supports `Zcu.Feature.separate_thread`. +pub fn runCodegen(pt: Zcu.PerThread, func_index: InternPool.Index, air: *Air, out: *@import("../link.zig").ZcuTask.LinkFunc.SharedMir) void { + if (runCodegenInner(pt, func_index, air)) |mir| { + out.value = mir; + out.status.store(.ready, .release); + } else |err| switch (err) { + error.OutOfMemory => { + pt.zcu.comp.setAllocFailure(); + out.status.store(.failed, .monotonic); + }, + error.CodegenFail => { + pt.zcu.assertCodegenFailed(pt.zcu.funcInfo(func_index).owner_nav); + out.status.store(.failed, .monotonic); + }, + error.NoLinkFile => { + assert(pt.zcu.comp.bin_file == null); + out.status.store(.failed, .monotonic); + }, + } + pt.zcu.comp.link_task_queue.mirReady(pt.zcu.comp, out); +} +fn runCodegenInner(pt: Zcu.PerThread, func_index: InternPool.Index, air: *Air) error{ OutOfMemory, CodegenFail, NoLinkFile }!codegen.AnyMir { + const zcu = pt.zcu; + const gpa = zcu.gpa; + const ip = &zcu.intern_pool; + const comp = zcu.comp; + + const nav = zcu.funcInfo(func_index).owner_nav; + const fqn = ip.getNav(nav).fqn; + + const codegen_prog_node = zcu.codegen_prog_node.start(fqn.toSlice(ip), 0); + defer codegen_prog_node.end(); + + if (codegen.legalizeFeatures(pt, nav)) |features| { + try air.legalize(pt, features); + } + + var liveness: Air.Liveness = try .analyze(zcu, air.*, ip); + defer liveness.deinit(gpa); + + // TODO: surely writing to stderr from n threads simultaneously will work flawlessly + if (build_options.enable_debug_extensions and comp.verbose_air) { + std.debug.print("# Begin Function AIR: {}:\n", .{fqn.fmt(ip)}); + air.dump(pt, liveness); + std.debug.print("# End Function AIR: {}\n\n", .{fqn.fmt(ip)}); + } + + if (std.debug.runtime_safety) { + var verify: Air.Liveness.Verify = .{ + .gpa = gpa, + .zcu = zcu, + .air = air.*, + .liveness = liveness, + .intern_pool = ip, + }; + defer verify.deinit(); + + verify.verify() catch |err| switch (err) { + error.OutOfMemory => return error.OutOfMemory, + else => return zcu.codegenFail(nav, "invalid liveness: {s}", .{@errorName(err)}), + }; + } + + // The LLVM backend is special, because we only need to do codegen. There is no equivalent to the + // "emit" step because LLVM does not support incremental linking. Our linker (LLD or self-hosted) + // will just see the ZCU object file which LLVM ultimately emits. + if (zcu.llvm_object) |llvm_object| { + return llvm_object.updateFunc(pt, func_index, air, &liveness); + } + + const lf = comp.bin_file orelse return error.NoLinkFile; + return codegen.generateFunction(lf, pt, zcu.navSrcLoc(nav), func_index, air, &liveness) catch |err| switch (err) { + error.OutOfMemory, + error.CodegenFail, + => |e| return e, + error.Overflow, + error.RelocationNotByteAligned, + => return zcu.codegenFail(nav, "unable to codegen: {s}", .{@errorName(err)}), + }; +} diff --git a/src/codegen.zig b/src/codegen.zig index a2de3e2d01ded56aacdd3ae0f5732bf8f445866b..2c2524257c45a177377a629b98e2a9345647327f 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -85,16 +85,104 @@ pub fn legalizeFeatures(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) ?*co } } +/// Every code generation backend has a different MIR representation. However, we want to pass +/// MIR from codegen to the linker *regardless* of which backend is in use. So, we use this: a +/// union of all MIR types. The active tag is known from the backend in use; see `AnyMir.tag`. +pub const AnyMir = union { + aarch64: @import("arch/aarch64/Mir.zig"), + arm: @import("arch/arm/Mir.zig"), + powerpc: noreturn, //@import("arch/powerpc/Mir.zig"), + riscv64: @import("arch/riscv64/Mir.zig"), + sparc64: @import("arch/sparc64/Mir.zig"), + x86_64: @import("arch/x86_64/Mir.zig"), + wasm: @import("arch/wasm/Mir.zig"), + c: @import("codegen/c.zig").Mir, + + pub inline fn tag(comptime backend: std.builtin.CompilerBackend) []const u8 { + return switch (backend) { + .stage2_aarch64 => "aarch64", + .stage2_arm => "arm", + .stage2_powerpc => "powerpc", + .stage2_riscv64 => "riscv64", + .stage2_sparc64 => "sparc64", + .stage2_x86_64 => "x86_64", + .stage2_wasm => "wasm", + .stage2_c => "c", + else => unreachable, + }; + } + + pub fn deinit(mir: *AnyMir, zcu: *const Zcu) void { + const gpa = zcu.gpa; + const backend = target_util.zigBackend(zcu.root_mod.resolved_target.result, zcu.comp.config.use_llvm); + switch (backend) { + else => unreachable, + inline .stage2_aarch64, + .stage2_arm, + .stage2_powerpc, + .stage2_riscv64, + .stage2_sparc64, + .stage2_x86_64, + .stage2_c, + => |backend_ct| @field(mir, tag(backend_ct)).deinit(gpa), + } + } +}; + +/// Runs code generation for a function. This process converts the `Air` emitted by `Sema`, +/// alongside annotated `Liveness` data, to machine code in the form of MIR (see `AnyMir`). +/// +/// This is supposed to be a "pure" process, but some backends are currently buggy; see +/// `Zcu.Feature.separate_thread` for details. pub fn generateFunction( lf: *link.File, pt: Zcu.PerThread, src_loc: Zcu.LazySrcLoc, func_index: InternPool.Index, - air: Air, - liveness: Air.Liveness, + air: *const Air, + liveness: *const Air.Liveness, +) CodeGenError!AnyMir { + const zcu = pt.zcu; + const func = zcu.funcInfo(func_index); + const target = zcu.navFileScope(func.owner_nav).mod.?.resolved_target.result; + switch (target_util.zigBackend(target, false)) { + else => unreachable, + inline .stage2_aarch64, + .stage2_arm, + .stage2_powerpc, + .stage2_riscv64, + .stage2_sparc64, + .stage2_x86_64, + .stage2_c, + => |backend| { + dev.check(devFeatureForBackend(backend)); + const CodeGen = importBackend(backend); + const mir = try CodeGen.generate(lf, pt, src_loc, func_index, air, liveness); + return @unionInit(AnyMir, AnyMir.tag(backend), mir); + }, + } +} + +/// Converts the MIR returned by `generateFunction` to finalized machine code to be placed in +/// the output binary. This is called from linker implementations, and may query linker state. +/// +/// This function is not called for the C backend, as `link.C` directly understands its MIR. +/// +/// The `air` parameter is not supposed to exist, but some backends are currently buggy; see +/// `Zcu.Feature.separate_thread` for details. +pub fn emitFunction( + lf: *link.File, + pt: Zcu.PerThread, + src_loc: Zcu.LazySrcLoc, + func_index: InternPool.Index, + any_mir: *const AnyMir, code: *std.ArrayListUnmanaged(u8), debug_output: link.File.DebugInfoOutput, -) CodeGenError!void { + /// TODO: this parameter needs to be removed. We should not still hold AIR this late + /// in the pipeline. Any information needed to call emit must be stored in MIR. + /// This is `undefined` if the backend supports the `separate_thread` feature. + air: *const Air, +) Allocator.Error!void { const zcu = pt.zcu; const func = zcu.funcInfo(func_index); const target = zcu.navFileScope(func.owner_nav).mod.?.resolved_target.result; @@ -108,7 +196,8 @@ pub fn generateFunction( .stage2_x86_64, => |backend| { dev.check(devFeatureForBackend(backend)); - return importBackend(backend).generate(lf, pt, src_loc, func_index, air, liveness, code, debug_output); + const mir = &@field(any_mir, AnyMir.tag(backend)); + return mir.emit(lf, pt, src_loc, func_index, code, debug_output, air); }, } } diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 3b8ab5298287c05d4cd2842ea30f8531faeaad95..f4952d4a580787c2a50620eb2a93396507d22f38 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -3,6 +3,7 @@ const builtin = @import("builtin"); const assert = std.debug.assert; const mem = std.mem; const log = std.log.scoped(.c); +const Allocator = mem.Allocator; const dev = @import("../dev.zig"); const link = @import("../link.zig"); @@ -30,6 +31,35 @@ pub fn legalizeFeatures(_: *const std.Target) ?*const Air.Legalize.Features { }) else null; // we don't currently ask zig1 to use safe optimization modes } +/// For most backends, MIR is basically a sequence of machine code instructions, perhaps with some +/// "pseudo instructions" thrown in. For the C backend, it is instead the generated C code for a +/// single function. We also need to track some information to get merged into the global `link.C` +/// state, including: +/// * The UAVs used, so declarations can be emitted in `flush` +/// * The types used, so declarations can be emitted in `flush` +/// * The lazy functions used, so definitions can be emitted in `flush` +pub const Mir = struct { + /// This map contains all the UAVs we saw generating this function. + /// `link.C` will merge them into its `uavs`/`aligned_uavs` fields. + /// Key is the value of the UAV; value is the UAV's alignment, or + /// `.none` for natural alignment. The specified alignment is never + /// less than the natural alignment. + uavs: std.AutoArrayHashMapUnmanaged(InternPool.Index, Alignment), + // These remaining fields are essentially just an owned version of `link.C.AvBlock`. + code: []u8, + fwd_decl: []u8, + ctype_pool: CType.Pool, + lazy_fns: LazyFnMap, + + pub fn deinit(mir: *Mir, gpa: Allocator) void { + mir.uavs.deinit(gpa); + gpa.free(mir.code); + gpa.free(mir.fwd_decl); + mir.ctype_pool.deinit(gpa); + mir.lazy_fns.deinit(gpa); + } +}; + pub const CType = @import("c/Type.zig"); pub const CValue = union(enum) { @@ -671,7 +701,7 @@ pub const Object = struct { /// This data is available both when outputting .c code and when outputting an .h file. pub const DeclGen = struct { - gpa: mem.Allocator, + gpa: Allocator, pt: Zcu.PerThread, mod: *Module, pass: Pass, @@ -682,10 +712,12 @@ pub const DeclGen = struct { error_msg: ?*Zcu.ErrorMsg, ctype_pool: CType.Pool, scratch: std.ArrayListUnmanaged(u32), - /// Keeps track of anonymous decls that need to be rendered before this - /// (named) Decl in the output C code. - uav_deps: std.AutoArrayHashMapUnmanaged(InternPool.Index, C.AvBlock), - aligned_uavs: std.AutoArrayHashMapUnmanaged(InternPool.Index, Alignment), + /// This map contains all the UAVs we saw generating this function. + /// `link.C` will merge them into its `uavs`/`aligned_uavs` fields. + /// Key is the value of the UAV; value is the UAV's alignment, or + /// `.none` for natural alignment. The specified alignment is never + /// less than the natural alignment. + uavs: std.AutoArrayHashMapUnmanaged(InternPool.Index, Alignment), pub const Pass = union(enum) { nav: InternPool.Nav.Index, @@ -753,21 +785,17 @@ pub const DeclGen = struct { // Indicate that the anon decl should be rendered to the output so that // our reference above is not undefined. const ptr_type = ip.indexToKey(uav.orig_ty).ptr_type; - const gop = try dg.uav_deps.getOrPut(dg.gpa, uav.val); - if (!gop.found_existing) gop.value_ptr.* = .{}; - - // Only insert an alignment entry if the alignment is greater than ABI - // alignment. If there is already an entry, keep the greater alignment. - const explicit_alignment = ptr_type.flags.alignment; - if (explicit_alignment != .none) { - const abi_alignment = Type.fromInterned(ptr_type.child).abiAlignment(zcu); - if (explicit_alignment.order(abi_alignment).compare(.gt)) { - const aligned_gop = try dg.aligned_uavs.getOrPut(dg.gpa, uav.val); - aligned_gop.value_ptr.* = if (aligned_gop.found_existing) - aligned_gop.value_ptr.maxStrict(explicit_alignment) - else - explicit_alignment; - } + const gop = try dg.uavs.getOrPut(dg.gpa, uav.val); + if (!gop.found_existing) gop.value_ptr.* = .none; + // If there is an explicit alignment, greater than the current one, use it. + // Note that we intentionally start at `.none`, so `gop.value_ptr.*` is never + // underaligned, so we don't need to worry about the `.none` case here. + if (ptr_type.flags.alignment != .none) { + // Resolve the current alignment so we can choose the bigger one. + const cur_alignment: Alignment = if (gop.value_ptr.* == .none) abi: { + break :abi Type.fromInterned(ptr_type.child).abiAlignment(zcu); + } else gop.value_ptr.*; + gop.value_ptr.* = cur_alignment.maxStrict(ptr_type.flags.alignment); } } @@ -2895,7 +2923,79 @@ pub fn genLazyFn(o: *Object, lazy_ctype_pool: *const CType.Pool, lazy_fn: LazyFn } } -pub fn genFunc(f: *Function) !void { +pub fn generate( + lf: *link.File, + pt: Zcu.PerThread, + src_loc: Zcu.LazySrcLoc, + func_index: InternPool.Index, + air: *const Air, + liveness: *const Air.Liveness, +) @import("../codegen.zig").CodeGenError!Mir { + const zcu = pt.zcu; + const gpa = zcu.gpa; + + _ = src_loc; + assert(lf.tag == .c); + + const func = zcu.funcInfo(func_index); + + var function: Function = .{ + .value_map = .init(gpa), + .air = air.*, + .liveness = liveness.*, + .func_index = func_index, + .object = .{ + .dg = .{ + .gpa = gpa, + .pt = pt, + .mod = zcu.navFileScope(func.owner_nav).mod.?, + .error_msg = null, + .pass = .{ .nav = func.owner_nav }, + .is_naked_fn = Type.fromInterned(func.ty).fnCallingConvention(zcu) == .naked, + .expected_block = null, + .fwd_decl = .init(gpa), + .ctype_pool = .empty, + .scratch = .empty, + .uavs = .empty, + }, + .code = .init(gpa), + .indent_writer = undefined, // set later so we can get a pointer to object.code + }, + .lazy_fns = .empty, + }; + defer { + function.object.code.deinit(); + function.object.dg.fwd_decl.deinit(); + function.object.dg.ctype_pool.deinit(gpa); + function.object.dg.scratch.deinit(gpa); + function.object.dg.uavs.deinit(gpa); + function.deinit(); + } + try function.object.dg.ctype_pool.init(gpa); + function.object.indent_writer = .{ .underlying_writer = function.object.code.writer() }; + + genFunc(&function) catch |err| switch (err) { + error.AnalysisFail => return zcu.codegenFailMsg(func.owner_nav, function.object.dg.error_msg.?), + error.OutOfMemory => |e| return e, + }; + + var mir: Mir = .{ + .uavs = .empty, + .code = &.{}, + .fwd_decl = &.{}, + .ctype_pool = .empty, + .lazy_fns = .empty, + }; + errdefer mir.deinit(gpa); + mir.uavs = function.object.dg.uavs.move(); + mir.code = try function.object.code.toOwnedSlice(); + mir.fwd_decl = try function.object.dg.fwd_decl.toOwnedSlice(); + mir.ctype_pool = function.object.dg.ctype_pool.move(); + mir.lazy_fns = function.lazy_fns.move(); + return mir; +} + +fn genFunc(f: *Function) !void { const tracy = trace(@src()); defer tracy.end(); @@ -8482,7 +8582,7 @@ fn iterateBigTomb(f: *Function, inst: Air.Inst.Index) BigTomb { /// A naive clone of this map would create copies of the ArrayList which is /// stored in the values. This function additionally clones the values. -fn cloneFreeLocalsMap(gpa: mem.Allocator, map: *LocalsMap) !LocalsMap { +fn cloneFreeLocalsMap(gpa: Allocator, map: *LocalsMap) !LocalsMap { var cloned = try map.clone(gpa); const values = cloned.values(); var i: usize = 0; @@ -8499,7 +8599,7 @@ fn cloneFreeLocalsMap(gpa: mem.Allocator, map: *LocalsMap) !LocalsMap { return cloned; } -fn deinitFreeLocalsMap(gpa: mem.Allocator, map: *LocalsMap) void { +fn deinitFreeLocalsMap(gpa: Allocator, map: *LocalsMap) void { for (map.values()) |*value| { value.deinit(gpa); } diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 37c13c721109a6b119b743cd3c0323917526a2c6..e30e8f70a3a3283c2d6e7982a7adfdf3f781e81f 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -1121,8 +1121,8 @@ pub const Object = struct { o: *Object, pt: Zcu.PerThread, func_index: InternPool.Index, - air: Air, - liveness: Air.Liveness, + air: *const Air, + liveness: *const Air.Liveness, ) !void { assert(std.meta.eql(pt, o.pt)); const zcu = pt.zcu; @@ -1479,8 +1479,8 @@ pub const Object = struct { var fg: FuncGen = .{ .gpa = gpa, - .air = air, - .liveness = liveness, + .air = air.*, + .liveness = liveness.*, .ng = &ng, .wip = wip, .is_naked = fn_info.cc == .naked, @@ -1506,10 +1506,9 @@ pub const Object = struct { deinit_wip = false; fg.genBody(air.getMainBody(), .poi) catch |err| switch (err) { - error.CodegenFail => { - try zcu.failed_codegen.put(gpa, func.owner_nav, ng.err_msg.?); - ng.err_msg = null; - return; + error.CodegenFail => switch (zcu.codegenFailMsg(func.owner_nav, ng.err_msg.?)) { + error.CodegenFail => return, + error.OutOfMemory => |e| return e, }, else => |e| return e, }; @@ -1561,10 +1560,9 @@ pub const Object = struct { .err_msg = null, }; ng.genDecl() catch |err| switch (err) { - error.CodegenFail => { - try pt.zcu.failed_codegen.put(pt.zcu.gpa, nav_index, ng.err_msg.?); - ng.err_msg = null; - return; + error.CodegenFail => switch (pt.zcu.codegenFailMsg(nav_index, ng.err_msg.?)) { + error.CodegenFail => return, + error.OutOfMemory => |e| return e, }, else => |e| return e, }; diff --git a/src/codegen/spirv.zig b/src/codegen/spirv.zig index f83c6979ffd3019f42aa281446e071a2c1e995f6..e6c06d9f20fe167358098c3ce4f297940ebf111d 100644 --- a/src/codegen/spirv.zig +++ b/src/codegen/spirv.zig @@ -230,8 +230,9 @@ pub const Object = struct { defer nav_gen.deinit(); nav_gen.genNav(do_codegen) catch |err| switch (err) { - error.CodegenFail => { - try zcu.failed_codegen.put(gpa, nav_index, nav_gen.error_msg.?); + error.CodegenFail => switch (zcu.codegenFailMsg(nav_index, nav_gen.error_msg.?)) { + error.CodegenFail => {}, + error.OutOfMemory => |e| return e, }, else => |other| { // There might be an error that happened *after* self.error_msg diff --git a/src/dev.zig b/src/dev.zig index 1dc8264ebc2c74ad670a016da0087a8ccc77e2a5..2438ae6df72d4c8afc429d987f7a5f4d5de647b1 100644 --- a/src/dev.zig +++ b/src/dev.zig @@ -25,6 +25,9 @@ pub const Env = enum { /// - `zig build-* -fno-emit-bin` sema, + /// - `zig build-* -ofmt=c` + cbe, + /// - sema /// - `zig build-* -fincremental -fno-llvm -fno-lld -target x86_64-linux --listen=-` @"x86_64-linux", @@ -144,6 +147,12 @@ pub const Env = enum { => true, else => Env.ast_gen.supports(feature), }, + .cbe => switch (feature) { + .c_backend, + .c_linker, + => true, + else => Env.sema.supports(feature), + }, .@"x86_64-linux" => switch (feature) { .build_command, .stdio_listen, diff --git a/src/libs/freebsd.zig b/src/libs/freebsd.zig index 47fef32773e4c46aae2ccd0645a044198ae30c7b..98d4a42f91375b41ec1fcc806ff4f3f093d5dafa 100644 --- a/src/libs/freebsd.zig +++ b/src/libs/freebsd.zig @@ -1004,7 +1004,7 @@ fn queueSharedObjects(comp: *Compilation, so_files: BuiltSharedObjects) void { } } - comp.queueLinkTasks(task_buffer[0..task_buffer_i]); + comp.queuePrelinkTasks(task_buffer[0..task_buffer_i]); } fn buildSharedLib( diff --git a/src/libs/glibc.zig b/src/libs/glibc.zig index cc781c547201708b42c21b0a38d4cca8dd7a4e59..c1146d933dd95650ecedb6636233b5a9e563bc57 100644 --- a/src/libs/glibc.zig +++ b/src/libs/glibc.zig @@ -1170,7 +1170,7 @@ fn queueSharedObjects(comp: *Compilation, so_files: BuiltSharedObjects) void { } } - comp.queueLinkTasks(task_buffer[0..task_buffer_i]); + comp.queuePrelinkTasks(task_buffer[0..task_buffer_i]); } fn buildSharedLib( diff --git a/src/libs/libcxx.zig b/src/libs/libcxx.zig index 17a7d3d29ea6d6156cb23b38a6d494502a1e68c3..eb9f5df8558fb179f6230d5cbff90fe8e049249d 100644 --- a/src/libs/libcxx.zig +++ b/src/libs/libcxx.zig @@ -308,7 +308,7 @@ pub fn buildLibCxx(comp: *Compilation, prog_node: std.Progress.Node) BuildError! assert(comp.libcxx_static_lib == null); const crt_file = try sub_compilation.toCrtFile(); comp.libcxx_static_lib = crt_file; - comp.queueLinkTaskMode(crt_file.full_object_path, &config); + comp.queuePrelinkTaskMode(crt_file.full_object_path, &config); } pub fn buildLibCxxAbi(comp: *Compilation, prog_node: std.Progress.Node) BuildError!void { @@ -504,7 +504,7 @@ pub fn buildLibCxxAbi(comp: *Compilation, prog_node: std.Progress.Node) BuildErr assert(comp.libcxxabi_static_lib == null); const crt_file = try sub_compilation.toCrtFile(); comp.libcxxabi_static_lib = crt_file; - comp.queueLinkTaskMode(crt_file.full_object_path, &config); + comp.queuePrelinkTaskMode(crt_file.full_object_path, &config); } pub fn addCxxArgs( diff --git a/src/libs/libtsan.zig b/src/libs/libtsan.zig index 8a5ffd2eab5be8b3b84eeade7710fdf96d89e8f3..0c59d85bc5cc0a7ad09c7f9ad16a9c4ac6de8be6 100644 --- a/src/libs/libtsan.zig +++ b/src/libs/libtsan.zig @@ -325,7 +325,7 @@ pub fn buildTsan(comp: *Compilation, prog_node: std.Progress.Node) BuildError!vo }; const crt_file = try sub_compilation.toCrtFile(); - comp.queueLinkTaskMode(crt_file.full_object_path, &config); + comp.queuePrelinkTaskMode(crt_file.full_object_path, &config); assert(comp.tsan_lib == null); comp.tsan_lib = crt_file; } diff --git a/src/libs/libunwind.zig b/src/libs/libunwind.zig index 945689ebab168ef3bc8654d0a3ff6b7523a0b876..ccea649c173a2e8f1ecae35bb80ced8cae8b1d60 100644 --- a/src/libs/libunwind.zig +++ b/src/libs/libunwind.zig @@ -195,7 +195,7 @@ pub fn buildStaticLib(comp: *Compilation, prog_node: std.Progress.Node) BuildErr }; const crt_file = try sub_compilation.toCrtFile(); - comp.queueLinkTaskMode(crt_file.full_object_path, &config); + comp.queuePrelinkTaskMode(crt_file.full_object_path, &config); assert(comp.libunwind_static_lib == null); comp.libunwind_static_lib = crt_file; } diff --git a/src/libs/musl.zig b/src/libs/musl.zig index d208b098274701d1d5f1d9ffed8f899e9daebd05..21aeee98b5d28417973d45da972f2d94473e5d82 100644 --- a/src/libs/musl.zig +++ b/src/libs/musl.zig @@ -278,7 +278,7 @@ pub fn buildCrtFile(comp: *Compilation, in_crt_file: CrtFile, prog_node: std.Pro errdefer comp.gpa.free(basename); const crt_file = try sub_compilation.toCrtFile(); - comp.queueLinkTaskMode(crt_file.full_object_path, &config); + comp.queuePrelinkTaskMode(crt_file.full_object_path, &config); { comp.mutex.lock(); defer comp.mutex.unlock(); diff --git a/src/libs/netbsd.zig b/src/libs/netbsd.zig index 718861bf5cef18b7e8175bd6a7a1922bc9a36358..aab75cce49ed5acf6d58e253f537c2df07009781 100644 --- a/src/libs/netbsd.zig +++ b/src/libs/netbsd.zig @@ -669,7 +669,7 @@ fn queueSharedObjects(comp: *Compilation, so_files: BuiltSharedObjects) void { } } - comp.queueLinkTasks(task_buffer[0..task_buffer_i]); + comp.queuePrelinkTasks(task_buffer[0..task_buffer_i]); } fn buildSharedLib( diff --git a/src/link.zig b/src/link.zig index 4b4c3c611b9468aa104af95550e0df3d9c1b5719..31fd0a4a4e78ab7d0e213fbac93c70251e5b5eb4 100644 --- a/src/link.zig +++ b/src/link.zig @@ -21,11 +21,11 @@ const Type = @import("Type.zig"); const Value = @import("Value.zig"); const Package = @import("Package.zig"); const dev = @import("dev.zig"); -const ThreadSafeQueue = @import("ThreadSafeQueue.zig").ThreadSafeQueue; const target_util = @import("target.zig"); const codegen = @import("codegen.zig"); pub const LdScript = @import("link/LdScript.zig"); +pub const Queue = @import("link/Queue.zig"); pub const Diags = struct { /// Stored here so that function definitions can distinguish between @@ -741,21 +741,26 @@ pub const File = struct { } /// May be called before or after updateExports for any given Decl. - /// TODO: currently `pub` because `Zcu.PerThread` is calling this. + /// The active tag of `mir` is determined by the backend used for the module this function is in. /// Never called when LLVM is codegenning the ZCU. - pub fn updateFunc( + fn updateFunc( base: *File, pt: Zcu.PerThread, func_index: InternPool.Index, - air: Air, - liveness: Air.Liveness, + /// This is owned by the caller, but the callee is permitted to mutate it provided + /// that `mir.deinit` remains legal for the caller. For instance, the callee can + /// take ownership of an embedded slice and replace it with `&.{}` in `mir`. + mir: *codegen.AnyMir, + /// This may be `undefined`; only pass it to `emitFunction`. + /// This parameter will eventually be removed. + maybe_undef_air: *const Air, ) UpdateNavError!void { assert(base.comp.zcu.?.llvm_object == null); switch (base.tag) { .lld => unreachable, inline else => |tag| { dev.check(tag.devFeature()); - return @as(*tag.Type(), @fieldParentPtr("base", base)).updateFunc(pt, func_index, air, liveness); + return @as(*tag.Type(), @fieldParentPtr("base", base)).updateFunc(pt, func_index, mir, maybe_undef_air); }, } } @@ -1213,40 +1218,7 @@ pub const File = struct { pub const Dwarf = @import("link/Dwarf.zig"); }; -/// Does all the tasks in the queue. Runs in exactly one separate thread -/// from the rest of compilation. All tasks performed here are -/// single-threaded with respect to one another. -pub fn flushTaskQueue(tid: usize, comp: *Compilation) void { - const diags = &comp.link_diags; - // As soon as check() is called, another `flushTaskQueue` call could occur, - // so the safety lock must go after the check. - while (comp.link_task_queue.check()) |tasks| { - comp.link_task_queue_safety.lock(); - defer comp.link_task_queue_safety.unlock(); - - if (comp.remaining_prelink_tasks > 0) { - comp.link_task_queue_postponed.ensureUnusedCapacity(comp.gpa, tasks.len) catch |err| switch (err) { - error.OutOfMemory => return diags.setAllocFailure(), - }; - } - - for (tasks) |task| doTask(comp, tid, task); - - if (comp.remaining_prelink_tasks == 0) { - if (comp.bin_file) |base| if (!base.post_prelink) { - base.prelink(comp.work_queue_progress_node) catch |err| switch (err) { - error.OutOfMemory => diags.setAllocFailure(), - error.LinkFailure => continue, - }; - base.post_prelink = true; - for (comp.link_task_queue_postponed.items) |task| doTask(comp, tid, task); - comp.link_task_queue_postponed.clearRetainingCapacity(); - }; - } - } -} - -pub const Task = union(enum) { +pub const PrelinkTask = union(enum) { /// Loads the objects, shared objects, and archives that are already /// known from the command line. load_explicitly_provided, @@ -1264,31 +1236,70 @@ pub const Task = union(enum) { /// Tells the linker to load an input which could be an object file, /// archive, or shared library. load_input: Input, - +}; +pub const ZcuTask = union(enum) { /// Write the constant value for a Decl to the output file. link_nav: InternPool.Nav.Index, /// Write the machine code for a function to the output file. - link_func: CodegenFunc, + link_func: LinkFunc, link_type: InternPool.Index, - update_line_number: InternPool.TrackedInst.Index, - - pub const CodegenFunc = struct { + pub fn deinit(task: ZcuTask, zcu: *const Zcu) void { + switch (task) { + .link_nav, + .link_type, + .update_line_number, + => {}, + .link_func => |link_func| { + switch (link_func.mir.status.load(.monotonic)) { + .pending => unreachable, // cannot deinit until MIR done + .failed => {}, // MIR not populated so doesn't need freeing + .ready => link_func.mir.value.deinit(zcu), + } + zcu.gpa.destroy(link_func.mir); + }, + } + } + pub const LinkFunc = struct { /// This will either be a non-generic `func_decl` or a `func_instance`. func: InternPool.Index, - /// This `Air` is owned by the `Job` and allocated with `gpa`. - /// It must be deinited when the job is processed. - air: Air, + /// This pointer is allocated into `gpa` and must be freed when the `ZcuTask` is processed. + /// The pointer is shared with the codegen worker, which will populate the MIR inside once + /// it has been generated. It's important that the `link_func` is queued at the same time as + /// the codegen job to ensure that the linker receives functions in a deterministic order, + /// allowing reproducible builds. + mir: *SharedMir, + /// This field exists only due to deficiencies in some codegen implementations; it should + /// be removed when the corresponding parameter of `CodeGen.emitFunction` can be removed. + /// This is `undefined` if `Zcu.Feature.separate_thread` is supported. + /// If this is defined, its memory is owned externally; do not `deinit` this `air`. + air: *const Air, + + pub const SharedMir = struct { + /// This is initially `.pending`. When `value` is populated, the codegen thread will set + /// this to `.ready`, and alert the queue if needed. It could also end up `.failed`. + /// The action of storing a value (other than `.pending`) to this atomic transfers + /// ownership of memory assoicated with `value` to this `ZcuTask`. + status: std.atomic.Value(enum(u8) { + /// We are waiting on codegen to generate MIR (or die trying). + pending, + /// `value` is not populated and will not be populated. Just drop the task from the queue and move on. + failed, + /// `value` is populated with the MIR from the backend in use, which is not LLVM. + ready, + }), + /// This is `undefined` until `ready` is set to `true`. Once populated, this MIR belongs + /// to the `ZcuTask`, and must be `deinit`ed when it is processed. Allocated into `gpa`. + value: codegen.AnyMir, + }; }; }; -pub fn doTask(comp: *Compilation, tid: usize, task: Task) void { +pub fn doPrelinkTask(comp: *Compilation, task: PrelinkTask) void { const diags = &comp.link_diags; + const base = comp.bin_file orelse return; switch (task) { .load_explicitly_provided => { - comp.remaining_prelink_tasks -= 1; - const base = comp.bin_file orelse return; - const prog_node = comp.work_queue_progress_node.start("Parse Linker Inputs", comp.link_inputs.len); defer prog_node.end(); for (comp.link_inputs) |input| { @@ -1306,9 +1317,6 @@ pub fn doTask(comp: *Compilation, tid: usize, task: Task) void { } }, .load_host_libc => { - comp.remaining_prelink_tasks -= 1; - const base = comp.bin_file orelse return; - const prog_node = comp.work_queue_progress_node.start("Linker Parse Host libc", 0); defer prog_node.end(); @@ -1368,8 +1376,6 @@ pub fn doTask(comp: *Compilation, tid: usize, task: Task) void { } }, .load_object => |path| { - comp.remaining_prelink_tasks -= 1; - const base = comp.bin_file orelse return; const prog_node = comp.work_queue_progress_node.start("Linker Parse Object", 0); defer prog_node.end(); base.openLoadObject(path) catch |err| switch (err) { @@ -1378,8 +1384,6 @@ pub fn doTask(comp: *Compilation, tid: usize, task: Task) void { }; }, .load_archive => |path| { - comp.remaining_prelink_tasks -= 1; - const base = comp.bin_file orelse return; const prog_node = comp.work_queue_progress_node.start("Linker Parse Archive", 0); defer prog_node.end(); base.openLoadArchive(path, null) catch |err| switch (err) { @@ -1388,8 +1392,6 @@ pub fn doTask(comp: *Compilation, tid: usize, task: Task) void { }; }, .load_dso => |path| { - comp.remaining_prelink_tasks -= 1; - const base = comp.bin_file orelse return; const prog_node = comp.work_queue_progress_node.start("Linker Parse Shared Library", 0); defer prog_node.end(); base.openLoadDso(path, .{ @@ -1401,8 +1403,6 @@ pub fn doTask(comp: *Compilation, tid: usize, task: Task) void { }; }, .load_input => |input| { - comp.remaining_prelink_tasks -= 1; - const base = comp.bin_file orelse return; const prog_node = comp.work_queue_progress_node.start("Linker Parse Input", 0); defer prog_node.end(); base.loadInput(input) catch |err| switch (err) { @@ -1416,11 +1416,12 @@ pub fn doTask(comp: *Compilation, tid: usize, task: Task) void { }, }; }, + } +} +pub fn doZcuTask(comp: *Compilation, tid: usize, task: ZcuTask) void { + const diags = &comp.link_diags; + switch (task) { .link_nav => |nav_index| { - if (comp.remaining_prelink_tasks != 0) { - comp.link_task_queue_postponed.appendAssumeCapacity(task); - return; - } const zcu = comp.zcu.?; const pt: Zcu.PerThread = .activate(zcu, @enumFromInt(tid)); defer pt.deactivate(); @@ -1431,39 +1432,43 @@ pub fn doTask(comp: *Compilation, tid: usize, task: Task) void { } 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)), + error.CodegenFail => zcu.assertCodegenFailed(nav_index), error.Overflow, error.RelocationNotByteAligned => { - zcu.failed_codegen.ensureUnusedCapacity(zcu.gpa, 1) catch return diags.setAllocFailure(); - const msg = Zcu.ErrorMsg.create( - zcu.gpa, - zcu.navSrcLoc(nav_index), - "unable to codegen: {s}", - .{@errorName(err)}, - ) catch return diags.setAllocFailure(); - zcu.failed_codegen.putAssumeCapacityNoClobber(nav_index, msg); + switch (zcu.codegenFail(nav_index, "unable to codegen: {s}", .{@errorName(err)})) { + error.CodegenFail => return, + error.OutOfMemory => return diags.setAllocFailure(), + } // Not a retryable failure. }, }; } }, .link_func => |func| { - if (comp.remaining_prelink_tasks != 0) { - comp.link_task_queue_postponed.appendAssumeCapacity(task); - return; - } - const pt: Zcu.PerThread = .activate(comp.zcu.?, @enumFromInt(tid)); + const zcu = comp.zcu.?; + const nav = zcu.funcInfo(func.func).owner_nav; + const pt: Zcu.PerThread = .activate(zcu, @enumFromInt(tid)); defer pt.deactivate(); - var air = func.air; - defer air.deinit(comp.gpa); - pt.linkerUpdateFunc(func.func, &air) catch |err| switch (err) { - error.OutOfMemory => diags.setAllocFailure(), - }; + assert(zcu.llvm_object == null); // LLVM codegen doesn't produce MIR + switch (func.mir.status.load(.monotonic)) { + .pending => unreachable, + .ready => {}, + .failed => return, + } + const mir = &func.mir.value; + if (comp.bin_file) |lf| { + lf.updateFunc(pt, func.func, mir, func.air) catch |err| switch (err) { + error.OutOfMemory => return diags.setAllocFailure(), + error.CodegenFail => return zcu.assertCodegenFailed(nav), + error.Overflow, error.RelocationNotByteAligned => { + switch (zcu.codegenFail(nav, "unable to codegen: {s}", .{@errorName(err)})) { + error.OutOfMemory => return diags.setAllocFailure(), + error.CodegenFail => return, + } + }, + }; + } }, .link_type => |ty| { - if (comp.remaining_prelink_tasks != 0) { - comp.link_task_queue_postponed.appendAssumeCapacity(task); - return; - } const zcu = comp.zcu.?; const pt: Zcu.PerThread = .activate(zcu, @enumFromInt(tid)); defer pt.deactivate(); @@ -1477,10 +1482,6 @@ pub fn doTask(comp: *Compilation, tid: usize, task: Task) void { } }, .update_line_number => |ti| { - if (comp.remaining_prelink_tasks != 0) { - comp.link_task_queue_postponed.appendAssumeCapacity(task); - return; - } const pt: Zcu.PerThread = .activate(comp.zcu.?, @enumFromInt(tid)); defer pt.deactivate(); if (pt.zcu.llvm_object == null) { diff --git a/src/link/C.zig b/src/link/C.zig index 34fc1d3775503000bfa2875486444131ee7c6aba..417ebcdee6951ecbb277a3920e5ae8f8dfc0ef92 100644 --- a/src/link/C.zig +++ b/src/link/C.zig @@ -18,6 +18,7 @@ const trace = @import("../tracy.zig").trace; const Type = @import("../Type.zig"); const Value = @import("../Value.zig"); const Air = @import("../Air.zig"); +const AnyMir = @import("../codegen.zig").AnyMir; pub const zig_h = "#include \"zig.h\"\n"; @@ -166,6 +167,9 @@ pub fn deinit(self: *C) void { self.uavs.deinit(gpa); self.aligned_uavs.deinit(gpa); + self.exported_navs.deinit(gpa); + self.exported_uavs.deinit(gpa); + self.string_bytes.deinit(gpa); self.fwd_decl_buf.deinit(gpa); self.code_buf.deinit(gpa); @@ -177,73 +181,28 @@ pub fn updateFunc( self: *C, pt: Zcu.PerThread, func_index: InternPool.Index, - air: Air, - liveness: Air.Liveness, + mir: *AnyMir, + /// This may be `undefined`; only pass it to `emitFunction`. + /// This parameter will eventually be removed. + maybe_undef_air: *const Air, ) link.File.UpdateNavError!void { + _ = maybe_undef_air; // It would be a bug to use this argument. + const zcu = pt.zcu; const gpa = zcu.gpa; const func = zcu.funcInfo(func_index); + const gop = try self.navs.getOrPut(gpa, func.owner_nav); - if (!gop.found_existing) gop.value_ptr.* = .{}; - const ctype_pool = &gop.value_ptr.ctype_pool; - const lazy_fns = &gop.value_ptr.lazy_fns; - const fwd_decl = &self.fwd_decl_buf; - const code = &self.code_buf; - try ctype_pool.init(gpa); - ctype_pool.clearRetainingCapacity(); - lazy_fns.clearRetainingCapacity(); - fwd_decl.clearRetainingCapacity(); - code.clearRetainingCapacity(); - - var function: codegen.Function = .{ - .value_map = codegen.CValueMap.init(gpa), - .air = air, - .liveness = liveness, - .func_index = func_index, - .object = .{ - .dg = .{ - .gpa = gpa, - .pt = pt, - .mod = zcu.navFileScope(func.owner_nav).mod.?, - .error_msg = null, - .pass = .{ .nav = func.owner_nav }, - .is_naked_fn = Type.fromInterned(func.ty).fnCallingConvention(zcu) == .naked, - .expected_block = null, - .fwd_decl = fwd_decl.toManaged(gpa), - .ctype_pool = ctype_pool.*, - .scratch = .{}, - .uav_deps = self.uavs, - .aligned_uavs = self.aligned_uavs, - }, - .code = code.toManaged(gpa), - .indent_writer = undefined, // set later so we can get a pointer to object.code - }, - .lazy_fns = lazy_fns.*, + if (gop.found_existing) gop.value_ptr.deinit(gpa); + gop.value_ptr.* = .{ + .code = .empty, + .fwd_decl = .empty, + .ctype_pool = mir.c.ctype_pool.move(), + .lazy_fns = mir.c.lazy_fns.move(), }; - function.object.indent_writer = .{ .underlying_writer = function.object.code.writer() }; - defer { - self.uavs = function.object.dg.uav_deps; - self.aligned_uavs = function.object.dg.aligned_uavs; - fwd_decl.* = function.object.dg.fwd_decl.moveToUnmanaged(); - ctype_pool.* = function.object.dg.ctype_pool.move(); - ctype_pool.freeUnusedCapacity(gpa); - function.object.dg.scratch.deinit(gpa); - lazy_fns.* = function.lazy_fns.move(); - lazy_fns.shrinkAndFree(gpa, lazy_fns.count()); - code.* = function.object.code.moveToUnmanaged(); - function.deinit(); - } - - try zcu.failed_codegen.ensureUnusedCapacity(gpa, 1); - codegen.genFunc(&function) catch |err| switch (err) { - error.AnalysisFail => { - zcu.failed_codegen.putAssumeCapacityNoClobber(func.owner_nav, function.object.dg.error_msg.?); - return; - }, - else => |e| return e, - }; - gop.value_ptr.fwd_decl = try self.addString(function.object.dg.fwd_decl.items); - gop.value_ptr.code = try self.addString(function.object.code.items); + gop.value_ptr.code = try self.addString(mir.c.code); + gop.value_ptr.fwd_decl = try self.addString(mir.c.fwd_decl); + try self.addUavsFromCodegen(&mir.c.uavs); } fn updateUav(self: *C, pt: Zcu.PerThread, i: usize) !void { @@ -267,16 +226,14 @@ fn updateUav(self: *C, pt: Zcu.PerThread, i: usize) !void { .fwd_decl = fwd_decl.toManaged(gpa), .ctype_pool = codegen.CType.Pool.empty, .scratch = .{}, - .uav_deps = self.uavs, - .aligned_uavs = self.aligned_uavs, + .uavs = .empty, }, .code = code.toManaged(gpa), .indent_writer = undefined, // set later so we can get a pointer to object.code }; object.indent_writer = .{ .underlying_writer = object.code.writer() }; defer { - self.uavs = object.dg.uav_deps; - self.aligned_uavs = object.dg.aligned_uavs; + object.dg.uavs.deinit(gpa); fwd_decl.* = object.dg.fwd_decl.moveToUnmanaged(); object.dg.ctype_pool.deinit(object.dg.gpa); object.dg.scratch.deinit(gpa); @@ -295,8 +252,10 @@ fn updateUav(self: *C, pt: Zcu.PerThread, i: usize) !void { else => |e| return e, }; + try self.addUavsFromCodegen(&object.dg.uavs); + object.dg.ctype_pool.freeUnusedCapacity(gpa); - object.dg.uav_deps.values()[i] = .{ + self.uavs.values()[i] = .{ .code = try self.addString(object.code.items), .fwd_decl = try self.addString(object.dg.fwd_decl.items), .ctype_pool = object.dg.ctype_pool.move(), @@ -343,16 +302,14 @@ pub fn updateNav(self: *C, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) l .fwd_decl = fwd_decl.toManaged(gpa), .ctype_pool = ctype_pool.*, .scratch = .{}, - .uav_deps = self.uavs, - .aligned_uavs = self.aligned_uavs, + .uavs = .empty, }, .code = code.toManaged(gpa), .indent_writer = undefined, // set later so we can get a pointer to object.code }; object.indent_writer = .{ .underlying_writer = object.code.writer() }; defer { - self.uavs = object.dg.uav_deps; - self.aligned_uavs = object.dg.aligned_uavs; + object.dg.uavs.deinit(gpa); fwd_decl.* = object.dg.fwd_decl.moveToUnmanaged(); ctype_pool.* = object.dg.ctype_pool.move(); ctype_pool.freeUnusedCapacity(gpa); @@ -360,16 +317,16 @@ pub fn updateNav(self: *C, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) l code.* = object.code.moveToUnmanaged(); } - try zcu.failed_codegen.ensureUnusedCapacity(gpa, 1); codegen.genDecl(&object) catch |err| switch (err) { - error.AnalysisFail => { - zcu.failed_codegen.putAssumeCapacityNoClobber(nav_index, object.dg.error_msg.?); - return; + error.AnalysisFail => switch (zcu.codegenFailMsg(nav_index, object.dg.error_msg.?)) { + error.CodegenFail => return, + error.OutOfMemory => |e| return e, }, else => |e| return e, }; gop.value_ptr.code = try self.addString(object.code.items); gop.value_ptr.fwd_decl = try self.addString(object.dg.fwd_decl.items); + try self.addUavsFromCodegen(&object.dg.uavs); } pub fn updateLineNumber(self: *C, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) !void { @@ -671,16 +628,14 @@ fn flushErrDecls(self: *C, pt: Zcu.PerThread, ctype_pool: *codegen.CType.Pool) F .fwd_decl = fwd_decl.toManaged(gpa), .ctype_pool = ctype_pool.*, .scratch = .{}, - .uav_deps = self.uavs, - .aligned_uavs = self.aligned_uavs, + .uavs = .empty, }, .code = code.toManaged(gpa), .indent_writer = undefined, // set later so we can get a pointer to object.code }; object.indent_writer = .{ .underlying_writer = object.code.writer() }; defer { - self.uavs = object.dg.uav_deps; - self.aligned_uavs = object.dg.aligned_uavs; + object.dg.uavs.deinit(gpa); fwd_decl.* = object.dg.fwd_decl.moveToUnmanaged(); ctype_pool.* = object.dg.ctype_pool.move(); ctype_pool.freeUnusedCapacity(gpa); @@ -692,6 +647,8 @@ fn flushErrDecls(self: *C, pt: Zcu.PerThread, ctype_pool: *codegen.CType.Pool) F error.AnalysisFail => unreachable, else => |e| return e, }; + + try self.addUavsFromCodegen(&object.dg.uavs); } fn flushLazyFn( @@ -719,8 +676,7 @@ fn flushLazyFn( .fwd_decl = fwd_decl.toManaged(gpa), .ctype_pool = ctype_pool.*, .scratch = .{}, - .uav_deps = .{}, - .aligned_uavs = .{}, + .uavs = .empty, }, .code = code.toManaged(gpa), .indent_writer = undefined, // set later so we can get a pointer to object.code @@ -729,8 +685,7 @@ fn flushLazyFn( defer { // If this assert trips just handle the anon_decl_deps the same as // `updateFunc()` does. - assert(object.dg.uav_deps.count() == 0); - assert(object.dg.aligned_uavs.count() == 0); + assert(object.dg.uavs.count() == 0); fwd_decl.* = object.dg.fwd_decl.moveToUnmanaged(); ctype_pool.* = object.dg.ctype_pool.move(); ctype_pool.freeUnusedCapacity(gpa); @@ -866,12 +821,10 @@ pub fn updateExports( .fwd_decl = fwd_decl.toManaged(gpa), .ctype_pool = decl_block.ctype_pool, .scratch = .{}, - .uav_deps = .{}, - .aligned_uavs = .{}, + .uavs = .empty, }; defer { - assert(dg.uav_deps.count() == 0); - assert(dg.aligned_uavs.count() == 0); + assert(dg.uavs.count() == 0); fwd_decl.* = dg.fwd_decl.moveToUnmanaged(); ctype_pool.* = dg.ctype_pool.move(); ctype_pool.freeUnusedCapacity(gpa); @@ -891,3 +844,21 @@ pub fn deleteExport( .uav => |uav| _ = self.exported_uavs.swapRemove(uav), } } + +fn addUavsFromCodegen(c: *C, uavs: *const std.AutoArrayHashMapUnmanaged(InternPool.Index, Alignment)) Allocator.Error!void { + const gpa = c.base.comp.gpa; + try c.uavs.ensureUnusedCapacity(gpa, uavs.count()); + try c.aligned_uavs.ensureUnusedCapacity(gpa, uavs.count()); + for (uavs.keys(), uavs.values()) |uav_val, uav_align| { + { + const gop = c.uavs.getOrPutAssumeCapacity(uav_val); + if (!gop.found_existing) gop.value_ptr.* = .{}; + } + if (uav_align != .none) { + const gop = c.aligned_uavs.getOrPutAssumeCapacity(uav_val); + gop.value_ptr.* = if (gop.found_existing) max: { + break :max gop.value_ptr.*.maxStrict(uav_align); + } else uav_align; + } + } +} diff --git a/src/link/Coff.zig b/src/link/Coff.zig index e7dcbcdf2a68d4b0f3a51d98dcb158c94cdebb8c..9a040754ef63f376f4e65bff0f3b2d073f64c6c7 100644 --- a/src/link/Coff.zig +++ b/src/link/Coff.zig @@ -1079,7 +1079,7 @@ pub fn updateFunc( var code_buffer: std.ArrayListUnmanaged(u8) = .empty; defer code_buffer.deinit(gpa); - codegen.generateFunction( + try codegen.generateFunction( &coff.base, pt, zcu.navSrcLoc(nav_index), @@ -1088,20 +1088,7 @@ pub fn updateFunc( liveness, &code_buffer, .none, - ) catch |err| switch (err) { - error.CodegenFail => return error.CodegenFail, - error.OutOfMemory => return error.OutOfMemory, - error.Overflow, error.RelocationNotByteAligned => |e| { - try zcu.failed_codegen.putNoClobber(gpa, nav_index, try Zcu.ErrorMsg.create( - gpa, - zcu.navSrcLoc(nav_index), - "unable to codegen: {s}", - .{@errorName(e)}, - )); - try zcu.retryable_failures.append(zcu.gpa, AnalUnit.wrap(.{ .func = func_index })); - return error.CodegenFail; - }, - }; + ); try coff.updateNavCode(pt, nav_index, code_buffer.items, .FUNCTION); diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 1702ef200cb7cb89a9e6f6d3892094d8916c626e..34e04ad557124b99887b6a3a3b7edb7a6e801b91 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1691,13 +1691,13 @@ pub fn updateFunc( self: *Elf, pt: Zcu.PerThread, func_index: InternPool.Index, - air: Air, - liveness: Air.Liveness, + mir: *const codegen.AnyMir, + maybe_undef_air: *const Air, ) link.File.UpdateNavError!void { if (build_options.skip_non_native and builtin.object_format != .elf) { @panic("Attempted to compile for object format that was disabled by build configuration"); } - return self.zigObjectPtr().?.updateFunc(self, pt, func_index, air, liveness); + return self.zigObjectPtr().?.updateFunc(self, pt, func_index, mir, maybe_undef_air); } pub fn updateNav( diff --git a/src/link/Elf/ZigObject.zig b/src/link/Elf/ZigObject.zig index e377f3a9afdf3f239b74a9e3843867854d7ff1e2..1a5ef4b40821f38da207807b546991482172f5e7 100644 --- a/src/link/Elf/ZigObject.zig +++ b/src/link/Elf/ZigObject.zig @@ -1416,8 +1416,10 @@ pub fn updateFunc( elf_file: *Elf, pt: Zcu.PerThread, func_index: InternPool.Index, - air: Air, - liveness: Air.Liveness, + mir: *const codegen.AnyMir, + /// This may be `undefined`; only pass it to `emitFunction`. + /// This parameter will eventually be removed. + maybe_undef_air: *const Air, ) link.File.UpdateNavError!void { const tracy = trace(@src()); defer tracy.end(); @@ -1438,15 +1440,15 @@ pub fn updateFunc( var debug_wip_nav = if (self.dwarf) |*dwarf| try dwarf.initWipNav(pt, func.owner_nav, sym_index) else null; defer if (debug_wip_nav) |*wip_nav| wip_nav.deinit(); - try codegen.generateFunction( + try codegen.emitFunction( &elf_file.base, pt, zcu.navSrcLoc(func.owner_nav), func_index, - air, - liveness, + mir, &code_buffer, if (debug_wip_nav) |*dn| .{ .dwarf = dn } else .none, + maybe_undef_air, ); const code = code_buffer.items; diff --git a/src/link/Queue.zig b/src/link/Queue.zig new file mode 100644 index 0000000000000000000000000000000000000000..c73a0e96845adac891f64df40e589f1272188a89 --- /dev/null +++ b/src/link/Queue.zig @@ -0,0 +1,234 @@ +//! Stores and manages the queue of link tasks. Each task is either a `PrelinkTask` or a `ZcuTask`. +//! +//! There must be at most one link thread (the thread processing these tasks) active at a time. If +//! `!comp.separateCodegenThreadOk()`, then ZCU tasks will be run on the main thread, bypassing this +//! queue entirely. +//! +//! All prelink tasks must be processed before any ZCU tasks are processed. After all prelink tasks +//! are run, but before any ZCU tasks are run, `prelink` must be called on the `link.File`. +//! +//! There will sometimes be a `ZcuTask` in the queue which is not yet ready because it depends on +//! MIR which has not yet been generated by any codegen thread. In this case, we must pause +//! processing of linker tasks until the MIR is ready. It would be incorrect to run any other link +//! tasks first, since this would make builds unreproducible. + +mutex: std.Thread.Mutex, +/// Validates that only one `flushTaskQueue` thread is running at a time. +flush_safety: std.debug.SafetyLock, + +/// This is the number of prelink tasks which are expected but have not yet been enqueued. +/// Guarded by `mutex`. +pending_prelink_tasks: u32, + +/// Prelink tasks which have been enqueued and are not yet owned by the worker thread. +/// Allocated into `gpa`, guarded by `mutex`. +queued_prelink: std.ArrayListUnmanaged(PrelinkTask), +/// The worker thread moves items from `queued_prelink` into this array in order to process them. +/// Allocated into `gpa`, accessed only by the worker thread. +wip_prelink: std.ArrayListUnmanaged(PrelinkTask), + +/// Like `queued_prelink`, but for ZCU tasks. +/// Allocated into `gpa`, guarded by `mutex`. +queued_zcu: std.ArrayListUnmanaged(ZcuTask), +/// Like `wip_prelink`, but for ZCU tasks. +/// Allocated into `gpa`, accessed only by the worker thread. +wip_zcu: std.ArrayListUnmanaged(ZcuTask), + +/// When processing ZCU link tasks, we might have to block due to unpopulated MIR. When this +/// happens, some tasks in `wip_zcu` have been run, and some are still pending. This is the +/// index into `wip_zcu` which we have reached. +wip_zcu_idx: usize, + +/// Guarded by `mutex`. +state: union(enum) { + /// The link thread is currently running or queued to run. + running, + /// The link thread is not running or queued, because it has exhausted all immediately available + /// tasks. It should be spawned when more tasks are enqueued. If `pending_prelink_tasks` is not + /// zero, we are specifically waiting for prelink tasks. + finished, + /// The link thread is not running or queued, because it is waiting for this MIR to be populated. + /// Once codegen completes, it must call `mirReady` which will restart the link thread. + wait_for_mir: *ZcuTask.LinkFunc.SharedMir, +}, + +/// The initial `Queue` state, containing no tasks, expecting no prelink tasks, and with no running worker thread. +/// The `pending_prelink_tasks` and `queued_prelink` fields may be modified as needed before calling `start`. +pub const empty: Queue = .{ + .mutex = .{}, + .flush_safety = .{}, + .pending_prelink_tasks = 0, + .queued_prelink = .empty, + .wip_prelink = .empty, + .queued_zcu = .empty, + .wip_zcu = .empty, + .wip_zcu_idx = 0, + .state = .finished, +}; +/// `lf` is needed to correctly deinit any pending `ZcuTask`s. +pub fn deinit(q: *Queue, comp: *Compilation) void { + const gpa = comp.gpa; + for (q.queued_zcu.items) |t| t.deinit(comp.zcu.?); + for (q.wip_zcu.items[q.wip_zcu_idx..]) |t| t.deinit(comp.zcu.?); + q.queued_prelink.deinit(gpa); + q.wip_prelink.deinit(gpa); + q.queued_zcu.deinit(gpa); + q.wip_zcu.deinit(gpa); +} + +/// This is expected to be called exactly once, after which the caller must not directly access +/// `queued_prelink` or `pending_prelink_tasks` any longer. This will spawn the link thread if +/// necessary. +pub fn start(q: *Queue, comp: *Compilation) void { + assert(q.state == .finished); + assert(q.queued_zcu.items.len == 0); + if (q.queued_prelink.items.len != 0) { + q.state = .running; + comp.thread_pool.spawnWgId(&comp.link_task_wait_group, flushTaskQueue, .{ q, comp }); + } +} + +/// Called by codegen workers after they have populated a `ZcuTask.LinkFunc.SharedMir`. If the link +/// thread was waiting for this MIR, it can resume. +pub fn mirReady(q: *Queue, comp: *Compilation, mir: *ZcuTask.LinkFunc.SharedMir) void { + // We would like to assert that `mir` is not pending, but that would race with a worker thread + // potentially freeing it. + { + q.mutex.lock(); + defer q.mutex.unlock(); + switch (q.state) { + .finished => unreachable, // there's definitely a task queued + .running => return, + .wait_for_mir => |wait_for| if (wait_for != mir) return, + } + // We were waiting for `mir`, so we will restart the linker thread. + q.state = .running; + } + assert(mir.status.load(.monotonic) != .pending); + comp.thread_pool.spawnWgId(&comp.link_task_wait_group, flushTaskQueue, .{ q, comp }); +} + +/// Enqueues all prelink tasks in `tasks`. Asserts that they were expected, i.e. that `tasks.len` is +/// less than or equal to `q.pending_prelink_tasks`. Also asserts that `tasks.len` is not 0. +pub fn enqueuePrelink(q: *Queue, comp: *Compilation, tasks: []const PrelinkTask) Allocator.Error!void { + { + q.mutex.lock(); + defer q.mutex.unlock(); + try q.queued_prelink.appendSlice(comp.gpa, tasks); + q.pending_prelink_tasks -= @intCast(tasks.len); + switch (q.state) { + .wait_for_mir => unreachable, // we've not started zcu tasks yet + .running => return, + .finished => {}, + } + // Restart the linker thread, because it was waiting for a task + q.state = .running; + } + comp.thread_pool.spawnWgId(&comp.link_task_wait_group, flushTaskQueue, .{ q, comp }); +} + +pub fn enqueueZcu(q: *Queue, comp: *Compilation, task: ZcuTask) Allocator.Error!void { + assert(comp.separateCodegenThreadOk()); + { + q.mutex.lock(); + defer q.mutex.unlock(); + try q.queued_zcu.append(comp.gpa, task); + switch (q.state) { + .running, .wait_for_mir => return, + .finished => if (q.pending_prelink_tasks != 0) return, + } + // Restart the linker thread, unless it would immediately be blocked + if (task == .link_func and task.link_func.mir.status.load(.monotonic) == .pending) { + q.state = .{ .wait_for_mir = task.link_func.mir }; + return; + } + q.state = .running; + } + comp.thread_pool.spawnWgId(&comp.link_task_wait_group, flushTaskQueue, .{ q, comp }); +} + +fn flushTaskQueue(tid: usize, q: *Queue, comp: *Compilation) void { + q.flush_safety.lock(); + defer q.flush_safety.unlock(); + + if (std.debug.runtime_safety) { + q.mutex.lock(); + defer q.mutex.unlock(); + assert(q.state == .running); + } + prelink: while (true) { + assert(q.wip_prelink.items.len == 0); + { + q.mutex.lock(); + defer q.mutex.unlock(); + std.mem.swap(std.ArrayListUnmanaged(PrelinkTask), &q.queued_prelink, &q.wip_prelink); + if (q.wip_prelink.items.len == 0) { + if (q.pending_prelink_tasks == 0) { + break :prelink; // prelink is done + } else { + // We're expecting more prelink tasks so can't move on to ZCU tasks. + q.state = .finished; + return; + } + } + } + for (q.wip_prelink.items) |task| { + link.doPrelinkTask(comp, task); + } + q.wip_prelink.clearRetainingCapacity(); + } + + // We've finished the prelink tasks, so run prelink if necessary. + if (comp.bin_file) |lf| { + if (!lf.post_prelink) { + if (lf.prelink(comp.work_queue_progress_node)) |_| { + lf.post_prelink = true; + } else |err| switch (err) { + error.OutOfMemory => comp.link_diags.setAllocFailure(), + error.LinkFailure => {}, + } + } + } + + // Now we can run ZCU tasks. + while (true) { + if (q.wip_zcu.items.len == q.wip_zcu_idx) { + q.wip_zcu.clearRetainingCapacity(); + q.wip_zcu_idx = 0; + q.mutex.lock(); + defer q.mutex.unlock(); + std.mem.swap(std.ArrayListUnmanaged(ZcuTask), &q.queued_zcu, &q.wip_zcu); + if (q.wip_zcu.items.len == 0) { + // We've exhausted all available tasks. + q.state = .finished; + return; + } + } + const task = q.wip_zcu.items[q.wip_zcu_idx]; + // If the task is a `link_func`, we might have to stop until its MIR is populated. + pending: { + if (task != .link_func) break :pending; + const status_ptr = &task.link_func.mir.status; + // First check without the mutex to optimize for the common case where MIR is ready. + if (status_ptr.load(.monotonic) != .pending) break :pending; + q.mutex.lock(); + defer q.mutex.unlock(); + if (status_ptr.load(.monotonic) != .pending) break :pending; + // We will stop for now, and get restarted once this MIR is ready. + q.state = .{ .wait_for_mir = task.link_func.mir }; + return; + } + link.doZcuTask(comp, tid, task); + task.deinit(comp.zcu.?); + q.wip_zcu_idx += 1; + } +} + +const std = @import("std"); +const assert = std.debug.assert; +const Allocator = std.mem.Allocator; +const Compilation = @import("../Compilation.zig"); +const link = @import("../link.zig"); +const PrelinkTask = link.PrelinkTask; +const ZcuTask = link.ZcuTask; +const Queue = @This(); diff --git a/src/target.zig b/src/target.zig index 6172b5e7e9e84b6ebb2f7ae0ebe8cf8e345d35db..01c6a6cbf01f6ac3b3911efb636a2c3f67f88c06 100644 --- a/src/target.zig +++ b/src/target.zig @@ -850,7 +850,9 @@ pub inline fn backendSupportsFeature(backend: std.builtin.CompilerBackend, compt }, .separate_thread => switch (backend) { .stage2_llvm => false, - else => true, + // MLUGG TODO + .stage2_c => true, + else => false, }, }; } -- 2.54.0 From 5ab307cf47b1f0418d9ed4ab56df6fb798305c20 Mon Sep 17 00:00:00 2001 From: mlugg Date: Sun, 1 Jun 2025 22:57:59 +0100 Subject: [PATCH 06/35] compiler: get most backends compiling again As of this commit, every backend other than self-hosted Wasm and self-hosted SPIR-V compiles and (at least somewhat) functions again. Those two backends are currently disabled with panics. Note that `Zcu.Feature.separate_thread` is *not* enabled for the fixed backends. Avoiding linker references from codegen is a non-trivial task, and can be done after this branch. --- src/Compilation.zig | 8 ++- src/Zcu/PerThread.zig | 28 ++++++++-- src/arch/aarch64/CodeGen.zig | 46 ++++++---------- src/arch/aarch64/Mir.zig | 43 +++++++++++++++ src/arch/arm/CodeGen.zig | 48 ++++++---------- src/arch/arm/Mir.zig | 43 +++++++++++++++ src/arch/powerpc/CodeGen.zig | 10 +--- src/arch/riscv64/CodeGen.zig | 51 +++++------------ src/arch/riscv64/Mir.zig | 50 +++++++++++++++++ src/arch/sparc64/CodeGen.zig | 45 +++++---------- src/arch/sparc64/Mir.zig | 39 ++++++++++++- src/arch/x86_64/CodeGen.zig | 104 +++++++++++------------------------ src/arch/x86_64/Mir.zig | 65 ++++++++++++++++++++++ src/codegen.zig | 2 +- src/libs/freebsd.zig | 2 +- src/libs/glibc.zig | 2 +- src/libs/netbsd.zig | 2 +- src/link.zig | 2 + src/link/Coff.zig | 12 ++-- src/link/Goff.zig | 9 +-- src/link/MachO.zig | 6 +- src/link/MachO/ZigObject.zig | 12 ++-- src/link/Plan9.zig | 12 ++-- src/link/Queue.zig | 3 +- src/link/Xcoff.zig | 9 +-- 25 files changed, 402 insertions(+), 251 deletions(-) diff --git a/src/Compilation.zig b/src/Compilation.zig index 64ec1ab0a88154c9a87768c423563659b47267ef..e96793553957157577fe9cfe73126ec7da343fc4 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -4550,8 +4550,6 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job) JobError!void { air.deinit(gpa); return; } - const pt: Zcu.PerThread = .activate(comp.zcu.?, @enumFromInt(tid)); - defer pt.deactivate(); const shared_mir = try gpa.create(link.ZcuTask.LinkFunc.SharedMir); shared_mir.* = .{ .status = .init(.pending), @@ -4567,7 +4565,11 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job) JobError!void { } }); } else { const emit_needs_air = !zcu.backendSupportsFeature(.separate_thread); - pt.runCodegen(func.func, &air, shared_mir); + { + const pt: Zcu.PerThread = .activate(comp.zcu.?, @enumFromInt(tid)); + defer pt.deactivate(); + pt.runCodegen(func.func, &air, shared_mir); + } assert(shared_mir.status.load(.monotonic) != .pending); comp.dispatchZcuLinkTask(tid, .{ .link_func = .{ .func = func.func, diff --git a/src/Zcu/PerThread.zig b/src/Zcu/PerThread.zig index 92f1adbf2abb3bdd06d45ce3990ae464eca5d02d..6475649a681d51665f44cf824acd4dbb129ee1b1 100644 --- a/src/Zcu/PerThread.zig +++ b/src/Zcu/PerThread.zig @@ -4376,26 +4376,40 @@ pub fn addDependency(pt: Zcu.PerThread, unit: AnalUnit, dependee: InternPool.Dep /// other code. This function is currently run either on the main thread, or on a separate /// codegen thread, depending on whether the backend supports `Zcu.Feature.separate_thread`. pub fn runCodegen(pt: Zcu.PerThread, func_index: InternPool.Index, air: *Air, out: *@import("../link.zig").ZcuTask.LinkFunc.SharedMir) void { + const zcu = pt.zcu; if (runCodegenInner(pt, func_index, air)) |mir| { out.value = mir; out.status.store(.ready, .release); } else |err| switch (err) { error.OutOfMemory => { - pt.zcu.comp.setAllocFailure(); + zcu.comp.setAllocFailure(); out.status.store(.failed, .monotonic); }, error.CodegenFail => { - pt.zcu.assertCodegenFailed(pt.zcu.funcInfo(func_index).owner_nav); + zcu.assertCodegenFailed(zcu.funcInfo(func_index).owner_nav); out.status.store(.failed, .monotonic); }, error.NoLinkFile => { - assert(pt.zcu.comp.bin_file == null); + assert(zcu.comp.bin_file == null); + out.status.store(.failed, .monotonic); + }, + error.BackendDoesNotProduceMir => { + const backend = target_util.zigBackend(zcu.root_mod.resolved_target.result, zcu.comp.config.use_llvm); + switch (backend) { + else => unreachable, // assertion failure + .stage2_llvm => {}, + } out.status.store(.failed, .monotonic); }, } - pt.zcu.comp.link_task_queue.mirReady(pt.zcu.comp, out); + zcu.comp.link_task_queue.mirReady(zcu.comp, out); } -fn runCodegenInner(pt: Zcu.PerThread, func_index: InternPool.Index, air: *Air) error{ OutOfMemory, CodegenFail, NoLinkFile }!codegen.AnyMir { +fn runCodegenInner(pt: Zcu.PerThread, func_index: InternPool.Index, air: *Air) error{ + OutOfMemory, + CodegenFail, + NoLinkFile, + BackendDoesNotProduceMir, +}!codegen.AnyMir { const zcu = pt.zcu; const gpa = zcu.gpa; const ip = &zcu.intern_pool; @@ -4441,7 +4455,9 @@ fn runCodegenInner(pt: Zcu.PerThread, func_index: InternPool.Index, air: *Air) e // "emit" step because LLVM does not support incremental linking. Our linker (LLD or self-hosted) // will just see the ZCU object file which LLVM ultimately emits. if (zcu.llvm_object) |llvm_object| { - return llvm_object.updateFunc(pt, func_index, air, &liveness); + assert(pt.tid == .main); // LLVM has a lot of shared state + try llvm_object.updateFunc(pt, func_index, air, &liveness); + return error.BackendDoesNotProduceMir; } const lf = comp.bin_file orelse return error.NoLinkFile; diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 00cceb0c677552137f75e6b15df1681d3530f47d..0c29fd96e2b466c99099fe8d49ebabe281b36dbf 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -49,7 +49,6 @@ pt: Zcu.PerThread, air: Air, liveness: Air.Liveness, bin_file: *link.File, -debug_output: link.File.DebugInfoOutput, target: *const std.Target, func_index: InternPool.Index, owner_nav: InternPool.Nav.Index, @@ -185,6 +184,9 @@ const DbgInfoReloc = struct { } fn genArgDbgInfo(reloc: DbgInfoReloc, function: Self) !void { + // TODO: Add a pseudo-instruction or something to defer this work until Emit. + // We aren't allowed to interact with linker state here. + if (true) return; switch (function.debug_output) { .dwarf => |dw| { const loc: link.File.Dwarf.Loc = switch (reloc.mcv) { @@ -213,6 +215,9 @@ const DbgInfoReloc = struct { } fn genVarDbgInfo(reloc: DbgInfoReloc, function: Self) !void { + // TODO: Add a pseudo-instruction or something to defer this work until Emit. + // We aren't allowed to interact with linker state here. + if (true) return; switch (function.debug_output) { .dwarf => |dwarf| { const loc: link.File.Dwarf.Loc = switch (reloc.mcv) { @@ -326,11 +331,9 @@ pub fn generate( pt: Zcu.PerThread, src_loc: Zcu.LazySrcLoc, func_index: InternPool.Index, - air: Air, - liveness: Air.Liveness, - code: *std.ArrayListUnmanaged(u8), - debug_output: link.File.DebugInfoOutput, -) CodeGenError!void { + air: *const Air, + liveness: *const Air.Liveness, +) CodeGenError!Mir { const zcu = pt.zcu; const gpa = zcu.gpa; const func = zcu.funcInfo(func_index); @@ -349,9 +352,8 @@ pub fn generate( var function: Self = .{ .gpa = gpa, .pt = pt, - .air = air, - .liveness = liveness, - .debug_output = debug_output, + .air = air.*, + .liveness = liveness.*, .target = target, .bin_file = lf, .func_index = func_index, @@ -395,29 +397,13 @@ pub fn generate( var mir: Mir = .{ .instructions = function.mir_instructions.toOwnedSlice(), - .extra = try function.mir_extra.toOwnedSlice(gpa), - }; - defer mir.deinit(gpa); - - var emit: Emit = .{ - .mir = mir, - .bin_file = lf, - .debug_output = debug_output, - .target = target, - .src_loc = src_loc, - .code = code, - .prev_di_pc = 0, - .prev_di_line = func.lbrace_line, - .prev_di_column = func.lbrace_column, - .stack_size = function.max_end_stack, + .extra = &.{}, // fallible, so assign after errdefer + .max_end_stack = function.max_end_stack, .saved_regs_stack_space = function.saved_regs_stack_space, }; - defer emit.deinit(); - - emit.emitMir() catch |err| switch (err) { - error.EmitFail => return function.failMsg(emit.err_msg.?), - else => |e| return e, - }; + errdefer mir.deinit(gpa); + mir.extra = try function.mir_extra.toOwnedSlice(gpa); + return mir; } fn addInst(self: *Self, inst: Mir.Inst) error{OutOfMemory}!Mir.Inst.Index { diff --git a/src/arch/aarch64/Mir.zig b/src/arch/aarch64/Mir.zig index edf05f625e10738ec1972bc4c1b45c9add702006..34fcc64c7ea6c844f0d5cfa5b800ee191857849a 100644 --- a/src/arch/aarch64/Mir.zig +++ b/src/arch/aarch64/Mir.zig @@ -13,6 +13,14 @@ const assert = std.debug.assert; const bits = @import("bits.zig"); const Register = bits.Register; +const InternPool = @import("../../InternPool.zig"); +const Emit = @import("Emit.zig"); +const codegen = @import("../../codegen.zig"); +const link = @import("../../link.zig"); +const Zcu = @import("../../Zcu.zig"); + +max_end_stack: u32, +saved_regs_stack_space: u32, instructions: std.MultiArrayList(Inst).Slice, /// The meaning of this data is determined by `Inst.Tag` value. @@ -498,6 +506,41 @@ pub fn deinit(mir: *Mir, gpa: std.mem.Allocator) void { mir.* = undefined; } +pub fn emit( + mir: Mir, + lf: *link.File, + pt: Zcu.PerThread, + src_loc: Zcu.LazySrcLoc, + func_index: InternPool.Index, + code: *std.ArrayListUnmanaged(u8), + debug_output: link.File.DebugInfoOutput, + air: *const @import("../../Air.zig"), +) codegen.CodeGenError!void { + _ = air; // using this would be a bug + const zcu = pt.zcu; + const func = zcu.funcInfo(func_index); + const nav = func.owner_nav; + const mod = zcu.navFileScope(nav).mod.?; + var e: Emit = .{ + .mir = mir, + .bin_file = lf, + .debug_output = debug_output, + .target = &mod.resolved_target.result, + .src_loc = src_loc, + .code = code, + .prev_di_pc = 0, + .prev_di_line = func.lbrace_line, + .prev_di_column = func.lbrace_column, + .stack_size = mir.max_end_stack, + .saved_regs_stack_space = mir.saved_regs_stack_space, + }; + defer e.deinit(); + e.emitMir() catch |err| switch (err) { + error.EmitFail => return zcu.codegenFailMsg(nav, e.err_msg.?), + else => |e1| return e1, + }; +} + /// Returns the requested data, as well as the new index which is at the start of the /// trailers for the object. pub fn extraData(mir: Mir, comptime T: type, index: usize) struct { data: T, end: usize } { diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index 421ba7d75338776cb626c19b84ade812b54673ce..3868011557b35b8d8d27cd39399ca46e0ccab260 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -50,7 +50,6 @@ pt: Zcu.PerThread, air: Air, liveness: Air.Liveness, bin_file: *link.File, -debug_output: link.File.DebugInfoOutput, target: *const std.Target, func_index: InternPool.Index, err_msg: ?*ErrorMsg, @@ -264,6 +263,9 @@ const DbgInfoReloc = struct { } fn genArgDbgInfo(reloc: DbgInfoReloc, function: Self) !void { + // TODO: Add a pseudo-instruction or something to defer this work until Emit. + // We aren't allowed to interact with linker state here. + if (true) return; switch (function.debug_output) { .dwarf => |dw| { const loc: link.File.Dwarf.Loc = switch (reloc.mcv) { @@ -292,6 +294,9 @@ const DbgInfoReloc = struct { } fn genVarDbgInfo(reloc: DbgInfoReloc, function: Self) !void { + // TODO: Add a pseudo-instruction or something to defer this work until Emit. + // We aren't allowed to interact with linker state here. + if (true) return; switch (function.debug_output) { .dwarf => |dw| { const loc: link.File.Dwarf.Loc = switch (reloc.mcv) { @@ -335,11 +340,9 @@ pub fn generate( pt: Zcu.PerThread, src_loc: Zcu.LazySrcLoc, func_index: InternPool.Index, - air: Air, - liveness: Air.Liveness, - code: *std.ArrayListUnmanaged(u8), - debug_output: link.File.DebugInfoOutput, -) CodeGenError!void { + air: *const Air, + liveness: *const Air.Liveness, +) CodeGenError!Mir { const zcu = pt.zcu; const gpa = zcu.gpa; const func = zcu.funcInfo(func_index); @@ -358,11 +361,10 @@ pub fn generate( var function: Self = .{ .gpa = gpa, .pt = pt, - .air = air, - .liveness = liveness, + .air = air.*, + .liveness = liveness.*, .target = target, .bin_file = lf, - .debug_output = debug_output, .func_index = func_index, .err_msg = null, .args = undefined, // populated after `resolveCallingConventionValues` @@ -402,31 +404,15 @@ pub fn generate( return function.fail("failed to generate debug info: {s}", .{@errorName(err)}); } - var mir = Mir{ + var mir: Mir = .{ .instructions = function.mir_instructions.toOwnedSlice(), - .extra = try function.mir_extra.toOwnedSlice(gpa), - }; - defer mir.deinit(gpa); - - var emit = Emit{ - .mir = mir, - .bin_file = lf, - .debug_output = debug_output, - .target = target, - .src_loc = src_loc, - .code = code, - .prev_di_pc = 0, - .prev_di_line = func.lbrace_line, - .prev_di_column = func.lbrace_column, - .stack_size = function.max_end_stack, + .extra = &.{}, // fallible, so assign after errdefer + .max_end_stack = function.max_end_stack, .saved_regs_stack_space = function.saved_regs_stack_space, }; - defer emit.deinit(); - - emit.emitMir() catch |err| switch (err) { - error.EmitFail => return function.failMsg(emit.err_msg.?), - else => |e| return e, - }; + errdefer mir.deinit(gpa); + mir.extra = try function.mir_extra.toOwnedSlice(gpa); + return mir; } fn addInst(self: *Self, inst: Mir.Inst) error{OutOfMemory}!Mir.Inst.Index { diff --git a/src/arch/arm/Mir.zig b/src/arch/arm/Mir.zig index 5e651b7939146f3b716bd49c4aa925a3c0e52876..0366663eae818ba91000dcffbfb48b532d75ba4e 100644 --- a/src/arch/arm/Mir.zig +++ b/src/arch/arm/Mir.zig @@ -13,6 +13,14 @@ const assert = std.debug.assert; const bits = @import("bits.zig"); const Register = bits.Register; +const InternPool = @import("../../InternPool.zig"); +const Emit = @import("Emit.zig"); +const codegen = @import("../../codegen.zig"); +const link = @import("../../link.zig"); +const Zcu = @import("../../Zcu.zig"); + +max_end_stack: u32, +saved_regs_stack_space: u32, instructions: std.MultiArrayList(Inst).Slice, /// The meaning of this data is determined by `Inst.Tag` value. @@ -278,6 +286,41 @@ pub fn deinit(mir: *Mir, gpa: std.mem.Allocator) void { mir.* = undefined; } +pub fn emit( + mir: Mir, + lf: *link.File, + pt: Zcu.PerThread, + src_loc: Zcu.LazySrcLoc, + func_index: InternPool.Index, + code: *std.ArrayListUnmanaged(u8), + debug_output: link.File.DebugInfoOutput, + air: *const @import("../../Air.zig"), +) codegen.CodeGenError!void { + _ = air; // using this would be a bug + const zcu = pt.zcu; + const func = zcu.funcInfo(func_index); + const nav = func.owner_nav; + const mod = zcu.navFileScope(nav).mod.?; + var e: Emit = .{ + .mir = mir, + .bin_file = lf, + .debug_output = debug_output, + .target = &mod.resolved_target.result, + .src_loc = src_loc, + .code = code, + .prev_di_pc = 0, + .prev_di_line = func.lbrace_line, + .prev_di_column = func.lbrace_column, + .stack_size = mir.max_end_stack, + .saved_regs_stack_space = mir.saved_regs_stack_space, + }; + defer e.deinit(); + e.emitMir() catch |err| switch (err) { + error.EmitFail => return zcu.codegenFailMsg(nav, e.err_msg.?), + else => |e1| return e1, + }; +} + /// Returns the requested data, as well as the new index which is at the start of the /// trailers for the object. pub fn extraData(mir: Mir, comptime T: type, index: usize) struct { data: T, end: usize } { diff --git a/src/arch/powerpc/CodeGen.zig b/src/arch/powerpc/CodeGen.zig index 0cfee67ebdbd63697cc47aa81f6332cd819fbc81..4964fe19f484fc73bef027abcd4bffba99140846 100644 --- a/src/arch/powerpc/CodeGen.zig +++ b/src/arch/powerpc/CodeGen.zig @@ -19,19 +19,15 @@ pub fn generate( pt: Zcu.PerThread, src_loc: Zcu.LazySrcLoc, func_index: InternPool.Index, - air: Air, - liveness: Air.Liveness, - code: *std.ArrayListUnmanaged(u8), - debug_output: link.File.DebugInfoOutput, -) codegen.CodeGenError!void { + air: *const Air, + liveness: *const Air.Liveness, +) codegen.CodeGenError!noreturn { _ = bin_file; _ = pt; _ = src_loc; _ = func_index; _ = air; _ = liveness; - _ = code; - _ = debug_output; unreachable; } diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index 9fc51bd2d3d223ea9571a39dd9ad1ef9cdd89c8e..9b5e0ed69b98de0fe51b7a20256a9151decee43a 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -68,7 +68,6 @@ gpa: Allocator, mod: *Package.Module, target: *const std.Target, -debug_output: link.File.DebugInfoOutput, args: []MCValue, ret_mcv: InstTracking, fn_type: Type, @@ -746,13 +745,10 @@ pub fn generate( pt: Zcu.PerThread, src_loc: Zcu.LazySrcLoc, func_index: InternPool.Index, - air: Air, - liveness: Air.Liveness, - code: *std.ArrayListUnmanaged(u8), - debug_output: link.File.DebugInfoOutput, -) CodeGenError!void { + air: *const Air, + liveness: *const Air.Liveness, +) CodeGenError!Mir { const zcu = pt.zcu; - const comp = zcu.comp; const gpa = zcu.gpa; const ip = &zcu.intern_pool; const func = zcu.funcInfo(func_index); @@ -769,13 +765,12 @@ pub fn generate( var function: Func = .{ .gpa = gpa, - .air = air, + .air = air.*, .pt = pt, .mod = mod, .bin_file = bin_file, - .liveness = liveness, + .liveness = liveness.*, .target = &mod.resolved_target.result, - .debug_output = debug_output, .owner = .{ .nav_index = func.owner_nav }, .args = undefined, // populated after `resolveCallingConventionValues` .ret_mcv = undefined, // populated after `resolveCallingConventionValues` @@ -855,33 +850,8 @@ pub fn generate( .instructions = function.mir_instructions.toOwnedSlice(), .frame_locs = function.frame_locs.toOwnedSlice(), }; - defer mir.deinit(gpa); - - var emit: Emit = .{ - .lower = .{ - .pt = pt, - .allocator = gpa, - .mir = mir, - .cc = fn_info.cc, - .src_loc = src_loc, - .output_mode = comp.config.output_mode, - .link_mode = comp.config.link_mode, - .pic = mod.pic, - }, - .bin_file = bin_file, - .debug_output = debug_output, - .code = code, - .prev_di_pc = 0, - .prev_di_line = func.lbrace_line, - .prev_di_column = func.lbrace_column, - }; - defer emit.deinit(); - - emit.emitMir() catch |err| switch (err) { - error.LowerFail, error.EmitFail => return function.failMsg(emit.lower.err_msg.?), - error.InvalidInstruction => |e| return function.fail("emit MIR failed: {s} (Zig compiler bug)", .{@errorName(e)}), - else => |e| return e, - }; + errdefer mir.deinit(gpa); + return mir; } pub fn generateLazy( @@ -904,7 +874,6 @@ pub fn generateLazy( .bin_file = bin_file, .liveness = undefined, .target = &mod.resolved_target.result, - .debug_output = debug_output, .owner = .{ .lazy_sym = lazy_sym }, .args = undefined, // populated after `resolveCallingConventionValues` .ret_mcv = undefined, // populated after `resolveCallingConventionValues` @@ -4760,6 +4729,9 @@ fn genArgDbgInfo(func: *const Func, inst: Air.Inst.Index, mcv: MCValue) InnerErr const ty = arg.ty.toType(); if (arg.name == .none) return; + // TODO: Add a pseudo-instruction or something to defer this work until Emit. + // We aren't allowed to interact with linker state here. + if (true) return; switch (func.debug_output) { .dwarf => |dw| switch (mcv) { .register => |reg| dw.genLocalDebugInfo( @@ -5273,6 +5245,9 @@ fn genVarDbgInfo( mcv: MCValue, name: []const u8, ) !void { + // TODO: Add a pseudo-instruction or something to defer this work until Emit. + // We aren't allowed to interact with linker state here. + if (true) return; switch (func.debug_output) { .dwarf => |dwarf| { const loc: link.File.Dwarf.Loc = switch (mcv) { diff --git a/src/arch/riscv64/Mir.zig b/src/arch/riscv64/Mir.zig index 2ae62fd9b2e2d9ce95ee7b21ca67e01673de04f7..eef3fe75116738bc851b9765a63b65792aee6c85 100644 --- a/src/arch/riscv64/Mir.zig +++ b/src/arch/riscv64/Mir.zig @@ -109,6 +109,50 @@ pub fn deinit(mir: *Mir, gpa: std.mem.Allocator) void { mir.* = undefined; } +pub fn emit( + mir: Mir, + lf: *link.File, + pt: Zcu.PerThread, + src_loc: Zcu.LazySrcLoc, + func_index: InternPool.Index, + code: *std.ArrayListUnmanaged(u8), + debug_output: link.File.DebugInfoOutput, + air: *const @import("../../Air.zig"), +) codegen.CodeGenError!void { + _ = air; // using this would be a bug + const zcu = pt.zcu; + const comp = zcu.comp; + const gpa = comp.gpa; + const func = zcu.funcInfo(func_index); + const fn_info = zcu.typeToFunc(.fromInterned(func.ty)).?; + const nav = func.owner_nav; + const mod = zcu.navFileScope(nav).mod.?; + var e: Emit = .{ + .lower = .{ + .pt = pt, + .allocator = gpa, + .mir = mir, + .cc = fn_info.cc, + .src_loc = src_loc, + .output_mode = comp.config.output_mode, + .link_mode = comp.config.link_mode, + .pic = mod.pic, + }, + .bin_file = lf, + .debug_output = debug_output, + .code = code, + .prev_di_pc = 0, + .prev_di_line = func.lbrace_line, + .prev_di_column = func.lbrace_column, + }; + defer e.deinit(); + e.emitMir() catch |err| switch (err) { + error.LowerFail, error.EmitFail => return zcu.codegenFailMsg(nav, e.lower.err_msg.?), + error.InvalidInstruction => return zcu.codegenFail(nav, "emit MIR failed: {s} (Zig compiler bug)", .{@errorName(err)}), + else => |err1| return err1, + }; +} + pub const FrameLoc = struct { base: Register, disp: i32, @@ -202,3 +246,9 @@ const FrameIndex = bits.FrameIndex; const FrameAddr = @import("CodeGen.zig").FrameAddr; const IntegerBitSet = std.bit_set.IntegerBitSet; const Mnemonic = @import("mnem.zig").Mnemonic; + +const InternPool = @import("../../InternPool.zig"); +const Emit = @import("Emit.zig"); +const codegen = @import("../../codegen.zig"); +const link = @import("../../link.zig"); +const Zcu = @import("../../Zcu.zig"); diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index ad9884dcdb85f31c9f2dfedeabfe38b7e4ebcbca..180aaedd3cbb9d23d023f2b62a90b92d223399cc 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -57,8 +57,6 @@ liveness: Air.Liveness, bin_file: *link.File, target: *const std.Target, func_index: InternPool.Index, -code: *std.ArrayListUnmanaged(u8), -debug_output: link.File.DebugInfoOutput, err_msg: ?*ErrorMsg, args: []MCValue, ret_mcv: MCValue, @@ -268,11 +266,9 @@ pub fn generate( pt: Zcu.PerThread, src_loc: Zcu.LazySrcLoc, func_index: InternPool.Index, - air: Air, - liveness: Air.Liveness, - code: *std.ArrayListUnmanaged(u8), - debug_output: link.File.DebugInfoOutput, -) CodeGenError!void { + air: *const Air, + liveness: *const Air.Liveness, +) CodeGenError!Mir { const zcu = pt.zcu; const gpa = zcu.gpa; const func = zcu.funcInfo(func_index); @@ -291,13 +287,11 @@ pub fn generate( var function: Self = .{ .gpa = gpa, .pt = pt, - .air = air, - .liveness = liveness, + .air = air.*, + .liveness = liveness.*, .target = target, .bin_file = lf, .func_index = func_index, - .code = code, - .debug_output = debug_output, .err_msg = null, .args = undefined, // populated after `resolveCallingConventionValues` .ret_mcv = undefined, // populated after `resolveCallingConventionValues` @@ -330,29 +324,13 @@ pub fn generate( else => |e| return e, }; - var mir = Mir{ + var mir: Mir = .{ .instructions = function.mir_instructions.toOwnedSlice(), - .extra = try function.mir_extra.toOwnedSlice(gpa), - }; - defer mir.deinit(gpa); - - var emit: Emit = .{ - .mir = mir, - .bin_file = lf, - .debug_output = debug_output, - .target = target, - .src_loc = src_loc, - .code = code, - .prev_di_pc = 0, - .prev_di_line = func.lbrace_line, - .prev_di_column = func.lbrace_column, - }; - defer emit.deinit(); - - emit.emitMir() catch |err| switch (err) { - error.EmitFail => return function.failMsg(emit.err_msg.?), - else => |e| return e, + .extra = &.{}, // fallible, so populated after errdefer }; + errdefer mir.deinit(gpa); + mir.extra = try function.mir_extra.toOwnedSlice(gpa); + return mir; } fn gen(self: *Self) !void { @@ -3566,6 +3544,9 @@ fn genArgDbgInfo(self: Self, inst: Air.Inst.Index, mcv: MCValue) !void { const ty = arg.ty.toType(); if (arg.name == .none) return; + // TODO: Add a pseudo-instruction or something to defer this work until Emit. + // We aren't allowed to interact with linker state here. + if (true) return; switch (self.debug_output) { .dwarf => |dw| switch (mcv) { .register => |reg| try dw.genLocalDebugInfo( diff --git a/src/arch/sparc64/Mir.zig b/src/arch/sparc64/Mir.zig index e9086db7a54fdfe2502bd9b7910b8ccc7a39e57c..26c5c3267b272317919fd517f607c1b9b7d47b40 100644 --- a/src/arch/sparc64/Mir.zig +++ b/src/arch/sparc64/Mir.zig @@ -12,7 +12,11 @@ const assert = std.debug.assert; const Mir = @This(); const bits = @import("bits.zig"); -const Air = @import("../../Air.zig"); +const InternPool = @import("../../InternPool.zig"); +const Emit = @import("Emit.zig"); +const codegen = @import("../../codegen.zig"); +const link = @import("../../link.zig"); +const Zcu = @import("../../Zcu.zig"); const Instruction = bits.Instruction; const ASI = bits.Instruction.ASI; @@ -370,6 +374,39 @@ pub fn deinit(mir: *Mir, gpa: std.mem.Allocator) void { mir.* = undefined; } +pub fn emit( + mir: Mir, + lf: *link.File, + pt: Zcu.PerThread, + src_loc: Zcu.LazySrcLoc, + func_index: InternPool.Index, + code: *std.ArrayListUnmanaged(u8), + debug_output: link.File.DebugInfoOutput, + air: *const @import("../../Air.zig"), +) codegen.CodeGenError!void { + _ = air; // using this would be a bug + const zcu = pt.zcu; + const func = zcu.funcInfo(func_index); + const nav = func.owner_nav; + const mod = zcu.navFileScope(nav).mod.?; + var e: Emit = .{ + .mir = mir, + .bin_file = lf, + .debug_output = debug_output, + .target = &mod.resolved_target.result, + .src_loc = src_loc, + .code = code, + .prev_di_pc = 0, + .prev_di_line = func.lbrace_line, + .prev_di_column = func.lbrace_column, + }; + defer e.deinit(); + e.emitMir() catch |err| switch (err) { + error.EmitFail => return zcu.codegenFailMsg(nav, e.err_msg.?), + else => |err1| return err1, + }; +} + /// Returns the requested data, as well as the new index which is at the start of the /// trailers for the object. pub fn extraData(mir: Mir, comptime T: type, index: usize) struct { data: T, end: usize } { diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index b38492d500d332b194cdba2b2e01e9045cb8de81..1d95c8db77589c0704e1bbe6c6798a2ab2615931 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -125,7 +125,6 @@ pt: Zcu.PerThread, air: Air, liveness: Air.Liveness, bin_file: *link.File, -debug_output: link.File.DebugInfoOutput, target: *const std.Target, owner: Owner, inline_func: InternPool.Index, @@ -972,13 +971,10 @@ pub fn generate( pt: Zcu.PerThread, src_loc: Zcu.LazySrcLoc, func_index: InternPool.Index, - air: Air, - liveness: Air.Liveness, - code: *std.ArrayListUnmanaged(u8), - debug_output: link.File.DebugInfoOutput, -) codegen.CodeGenError!void { + air: *const Air, + liveness: *const Air.Liveness, +) codegen.CodeGenError!Mir { const zcu = pt.zcu; - const comp = zcu.comp; const gpa = zcu.gpa; const ip = &zcu.intern_pool; const func = zcu.funcInfo(func_index); @@ -988,12 +984,11 @@ pub fn generate( var function: CodeGen = .{ .gpa = gpa, .pt = pt, - .air = air, - .liveness = liveness, + .air = air.*, + .liveness = liveness.*, .target = &mod.resolved_target.result, .mod = mod, .bin_file = bin_file, - .debug_output = debug_output, .owner = .{ .nav_index = func.owner_nav }, .inline_func = func_index, .arg_index = undefined, @@ -1090,7 +1085,7 @@ pub fn generate( }; // Drop them off at the rbrace. - if (debug_output != .none) _ = try function.addInst(.{ + if (!mod.strip) _ = try function.addInst(.{ .tag = .pseudo, .ops = .pseudo_dbg_line_line_column, .data = .{ .line_column = .{ @@ -1100,49 +1095,17 @@ pub fn generate( }); var mir: Mir = .{ - .instructions = function.mir_instructions.toOwnedSlice(), - .extra = try function.mir_extra.toOwnedSlice(gpa), - .table = try function.mir_table.toOwnedSlice(gpa), - .frame_locs = function.frame_locs.toOwnedSlice(), - }; - defer mir.deinit(gpa); - - var emit: Emit = .{ - .air = function.air, - .lower = .{ - .bin_file = bin_file, - .target = function.target, - .allocator = gpa, - .mir = mir, - .cc = fn_info.cc, - .src_loc = src_loc, - .output_mode = comp.config.output_mode, - .link_mode = comp.config.link_mode, - .pic = mod.pic, - }, - .atom_index = function.owner.getSymbolIndex(&function) catch |err| switch (err) { - error.CodegenFail => return error.CodegenFail, - else => |e| return e, - }, - .debug_output = debug_output, - .code = code, - .prev_di_loc = .{ - .line = func.lbrace_line, - .column = func.lbrace_column, - .is_stmt = switch (debug_output) { - .dwarf => |dwarf| dwarf.dwarf.debug_line.header.default_is_stmt, - .plan9 => undefined, - .none => undefined, - }, - }, - .prev_di_pc = 0, - }; - emit.emitMir() catch |err| switch (err) { - error.LowerFail, error.EmitFail => return function.failMsg(emit.lower.err_msg.?), - - error.InvalidInstruction, error.CannotEncode => |e| return function.fail("emit MIR failed: {s} (Zig compiler bug)", .{@errorName(e)}), - else => |e| return function.fail("emit MIR failed: {s}", .{@errorName(e)}), + .instructions = .empty, + .extra = &.{}, + .table = &.{}, + .frame_locs = .empty, }; + errdefer mir.deinit(gpa); + mir.instructions = function.mir_instructions.toOwnedSlice(); + mir.extra = try function.mir_extra.toOwnedSlice(gpa); + mir.table = try function.mir_table.toOwnedSlice(gpa); + mir.frame_locs = function.frame_locs.toOwnedSlice(); + return mir; } pub fn generateLazy( @@ -1165,7 +1128,6 @@ pub fn generateLazy( .target = &mod.resolved_target.result, .mod = mod, .bin_file = bin_file, - .debug_output = debug_output, .owner = .{ .lazy_sym = lazy_sym }, .inline_func = undefined, .arg_index = undefined, @@ -2339,7 +2301,7 @@ fn gen(self: *CodeGen) InnerError!void { else => |cc| return self.fail("{s} does not support var args", .{@tagName(cc)}), }; - if (self.debug_output != .none) try self.asmPseudo(.pseudo_dbg_prologue_end_none); + if (!self.mod.strip) try self.asmPseudo(.pseudo_dbg_prologue_end_none); try self.genBody(self.air.getMainBody()); @@ -2356,7 +2318,7 @@ fn gen(self: *CodeGen) InnerError!void { } for (self.epilogue_relocs.items) |epilogue_reloc| self.performReloc(epilogue_reloc); - if (self.debug_output != .none) try self.asmPseudo(.pseudo_dbg_epilogue_begin_none); + if (!self.mod.strip) try self.asmPseudo(.pseudo_dbg_epilogue_begin_none); const backpatch_stack_dealloc = try self.asmPlaceholder(); const backpatch_pop_callee_preserved_regs = try self.asmPlaceholder(); try self.asmRegister(.{ ._, .pop }, .rbp); @@ -2475,9 +2437,9 @@ fn gen(self: *CodeGen) InnerError!void { }); } } else { - if (self.debug_output != .none) try self.asmPseudo(.pseudo_dbg_prologue_end_none); + if (!self.mod.strip) try self.asmPseudo(.pseudo_dbg_prologue_end_none); try self.genBody(self.air.getMainBody()); - if (self.debug_output != .none) try self.asmPseudo(.pseudo_dbg_epilogue_begin_none); + if (!self.mod.strip) try self.asmPseudo(.pseudo_dbg_epilogue_begin_none); } } @@ -2498,9 +2460,9 @@ fn checkInvariantsAfterAirInst(self: *CodeGen) void { } fn genBodyBlock(self: *CodeGen, body: []const Air.Inst.Index) InnerError!void { - if (self.debug_output != .none) try self.asmPseudo(.pseudo_dbg_enter_block_none); + if (!self.mod.strip) try self.asmPseudo(.pseudo_dbg_enter_block_none); try self.genBody(body); - if (self.debug_output != .none) try self.asmPseudo(.pseudo_dbg_leave_block_none); + if (!self.mod.strip) try self.asmPseudo(.pseudo_dbg_leave_block_none); } fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { @@ -2544,7 +2506,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .shuffle_one, .shuffle_two => @panic("x86_64 TODO: shuffle_one/shuffle_two"), // zig fmt: on - .arg => if (cg.debug_output != .none) { + .arg => if (!cg.mod.strip) { // skip zero-bit arguments as they don't have a corresponding arg instruction var arg_index = cg.arg_index; while (cg.args[arg_index] == .none) arg_index += 1; @@ -64179,9 +64141,9 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .block => { const ty_pl = air_datas[@intFromEnum(inst)].ty_pl; const block = cg.air.extraData(Air.Block, ty_pl.payload); - if (cg.debug_output != .none) try cg.asmPseudo(.pseudo_dbg_enter_block_none); + if (!cg.mod.strip) try cg.asmPseudo(.pseudo_dbg_enter_block_none); try cg.lowerBlock(inst, @ptrCast(cg.air.extra.items[block.end..][0..block.data.body_len])); - if (cg.debug_output != .none) try cg.asmPseudo(.pseudo_dbg_leave_block_none); + if (!cg.mod.strip) try cg.asmPseudo(.pseudo_dbg_leave_block_none); }, .loop => if (use_old) try cg.airLoop(inst) else { const ty_pl = air_datas[@intFromEnum(inst)].ty_pl; @@ -85191,7 +85153,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .switch_dispatch => try cg.airSwitchDispatch(inst), .@"try", .try_cold => try cg.airTry(inst), .try_ptr, .try_ptr_cold => try cg.airTryPtr(inst), - .dbg_stmt => if (cg.debug_output != .none) { + .dbg_stmt => if (!cg.mod.strip) { const dbg_stmt = air_datas[@intFromEnum(inst)].dbg_stmt; _ = try cg.addInst(.{ .tag = .pseudo, @@ -85202,7 +85164,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { } }, }); }, - .dbg_empty_stmt => if (cg.debug_output != .none) { + .dbg_empty_stmt => if (!cg.mod.strip) { if (cg.mir_instructions.len > 0) { const prev_mir_op = &cg.mir_instructions.items(.ops)[cg.mir_instructions.len - 1]; if (prev_mir_op.* == .pseudo_dbg_line_line_column) @@ -85216,13 +85178,13 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { const old_inline_func = cg.inline_func; defer cg.inline_func = old_inline_func; cg.inline_func = dbg_inline_block.data.func; - if (cg.debug_output != .none) _ = try cg.addInst(.{ + if (!cg.mod.strip) _ = try cg.addInst(.{ .tag = .pseudo, .ops = .pseudo_dbg_enter_inline_func, .data = .{ .func = dbg_inline_block.data.func }, }); try cg.lowerBlock(inst, @ptrCast(cg.air.extra.items[dbg_inline_block.end..][0..dbg_inline_block.data.body_len])); - if (cg.debug_output != .none) _ = try cg.addInst(.{ + if (!cg.mod.strip) _ = try cg.addInst(.{ .tag = .pseudo, .ops = .pseudo_dbg_leave_inline_func, .data = .{ .func = old_inline_func }, @@ -85231,7 +85193,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .dbg_var_ptr, .dbg_var_val, .dbg_arg_inline, - => if (use_old) try cg.airDbgVar(inst) else if (cg.debug_output != .none) { + => if (use_old) try cg.airDbgVar(inst) else if (!cg.mod.strip) { const pl_op = air_datas[@intFromEnum(inst)].pl_op; var ops = try cg.tempsFromOperands(inst, .{pl_op.operand}); var mcv = ops[0].tracking(cg).short; @@ -173366,7 +173328,7 @@ fn airArg(self: *CodeGen, inst: Air.Inst.Index) !void { while (self.args[arg_index] == .none) arg_index += 1; self.arg_index = arg_index + 1; - const result: MCValue = if (self.debug_output == .none and self.liveness.isUnused(inst)) .unreach else result: { + const result: MCValue = if (self.mod.strip and self.liveness.isUnused(inst)) .unreach else result: { const arg_ty = self.typeOfIndex(inst); const src_mcv = self.args[arg_index]; switch (src_mcv) { @@ -173468,7 +173430,7 @@ fn airArg(self: *CodeGen, inst: Air.Inst.Index) !void { } fn airDbgVarArgs(self: *CodeGen) !void { - if (self.debug_output == .none) return; + if (self.mod.strip) return; if (!self.pt.zcu.typeToFunc(self.fn_type).?.is_var_args) return; try self.asmPseudo(.pseudo_dbg_var_args_none); } @@ -173478,7 +173440,7 @@ fn genLocalDebugInfo( inst: Air.Inst.Index, mcv: MCValue, ) !void { - if (self.debug_output == .none) return; + if (self.mod.strip) return; switch (self.air.instructions.items(.tag)[@intFromEnum(inst)]) { else => unreachable, .arg, .dbg_arg_inline, .dbg_var_val => |tag| { diff --git a/src/arch/x86_64/Mir.zig b/src/arch/x86_64/Mir.zig index 8d202e6baeb3eed73a99923436fb3fdf007ac0f2..14468677afd7c40d900b176b48d84b54686eeac7 100644 --- a/src/arch/x86_64/Mir.zig +++ b/src/arch/x86_64/Mir.zig @@ -1929,6 +1929,67 @@ pub fn deinit(mir: *Mir, gpa: std.mem.Allocator) void { mir.* = undefined; } +pub fn emit( + mir: Mir, + lf: *link.File, + pt: Zcu.PerThread, + src_loc: Zcu.LazySrcLoc, + func_index: InternPool.Index, + code: *std.ArrayListUnmanaged(u8), + debug_output: link.File.DebugInfoOutput, + /// TODO: remove dependency on this argument. This blocks enabling `Zcu.Feature.separate_thread`. + air: *const Air, +) codegen.CodeGenError!void { + const zcu = pt.zcu; + const comp = zcu.comp; + const gpa = comp.gpa; + const func = zcu.funcInfo(func_index); + const fn_info = zcu.typeToFunc(.fromInterned(func.ty)).?; + const nav = func.owner_nav; + const mod = zcu.navFileScope(nav).mod.?; + var e: Emit = .{ + .air = air.*, + .lower = .{ + .bin_file = lf, + .target = &mod.resolved_target.result, + .allocator = gpa, + .mir = mir, + .cc = fn_info.cc, + .src_loc = src_loc, + .output_mode = comp.config.output_mode, + .link_mode = comp.config.link_mode, + .pic = mod.pic, + }, + .atom_index = sym: { + if (lf.cast(.elf)) |ef| break :sym try ef.zigObjectPtr().?.getOrCreateMetadataForNav(zcu, nav); + if (lf.cast(.macho)) |mf| break :sym try mf.getZigObject().?.getOrCreateMetadataForNav(mf, nav); + if (lf.cast(.coff)) |cf| { + const atom = try cf.getOrCreateAtomForNav(nav); + break :sym cf.getAtom(atom).getSymbolIndex().?; + } + if (lf.cast(.plan9)) |p9f| break :sym try p9f.seeNav(pt, nav); + unreachable; + }, + .debug_output = debug_output, + .code = code, + .prev_di_loc = .{ + .line = func.lbrace_line, + .column = func.lbrace_column, + .is_stmt = switch (debug_output) { + .dwarf => |dwarf| dwarf.dwarf.debug_line.header.default_is_stmt, + .plan9 => undefined, + .none => undefined, + }, + }, + .prev_di_pc = 0, + }; + e.emitMir() catch |err| switch (err) { + error.LowerFail, error.EmitFail => return zcu.codegenFailMsg(nav, e.lower.err_msg.?), + error.InvalidInstruction, error.CannotEncode => return zcu.codegenFail(nav, "emit MIR failed: {s} (Zig compiler bug)", .{@errorName(err)}), + else => return zcu.codegenFail(nav, "emit MIR failed: {s}", .{@errorName(err)}), + }; +} + pub fn extraData(mir: Mir, comptime T: type, index: u32) struct { data: T, end: u32 } { const fields = std.meta.fields(T); var i: u32 = index; @@ -1987,3 +2048,7 @@ const IntegerBitSet = std.bit_set.IntegerBitSet; const InternPool = @import("../../InternPool.zig"); const Mir = @This(); const Register = bits.Register; +const Emit = @import("Emit.zig"); +const codegen = @import("../../codegen.zig"); +const link = @import("../../link.zig"); +const Zcu = @import("../../Zcu.zig"); diff --git a/src/codegen.zig b/src/codegen.zig index 2c2524257c45a177377a629b98e2a9345647327f..ea57aaf89c0e5238871c54163c0a814659f97122 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -182,7 +182,7 @@ pub fn emitFunction( /// in the pipeline. Any information needed to call emit must be stored in MIR. /// This is `undefined` if the backend supports the `separate_thread` feature. air: *const Air, -) Allocator.Error!void { +) CodeGenError!void { const zcu = pt.zcu; const func = zcu.funcInfo(func_index); const target = zcu.navFileScope(func.owner_nav).mod.?.resolved_target.result; diff --git a/src/libs/freebsd.zig b/src/libs/freebsd.zig index 98d4a42f91375b41ec1fcc806ff4f3f093d5dafa..d90ba974fce10d536ef66d61ca411a1e57cfd735 100644 --- a/src/libs/freebsd.zig +++ b/src/libs/freebsd.zig @@ -985,7 +985,7 @@ fn queueSharedObjects(comp: *Compilation, so_files: BuiltSharedObjects) void { assert(comp.freebsd_so_files == null); comp.freebsd_so_files = so_files; - var task_buffer: [libs.len]link.Task = undefined; + var task_buffer: [libs.len]link.PrelinkTask = undefined; var task_buffer_i: usize = 0; { diff --git a/src/libs/glibc.zig b/src/libs/glibc.zig index c1146d933dd95650ecedb6636233b5a9e563bc57..ed5eae377f2d6f9b5ce1f9d245388f262b345a84 100644 --- a/src/libs/glibc.zig +++ b/src/libs/glibc.zig @@ -1148,7 +1148,7 @@ fn queueSharedObjects(comp: *Compilation, so_files: BuiltSharedObjects) void { assert(comp.glibc_so_files == null); comp.glibc_so_files = so_files; - var task_buffer: [libs.len]link.Task = undefined; + var task_buffer: [libs.len]link.PrelinkTask = undefined; var task_buffer_i: usize = 0; { diff --git a/src/libs/netbsd.zig b/src/libs/netbsd.zig index aab75cce49ed5acf6d58e253f537c2df07009781..7121c308f5785fae4b5380d76f65630b6ea0b9ee 100644 --- a/src/libs/netbsd.zig +++ b/src/libs/netbsd.zig @@ -650,7 +650,7 @@ fn queueSharedObjects(comp: *Compilation, so_files: BuiltSharedObjects) void { assert(comp.netbsd_so_files == null); comp.netbsd_so_files = so_files; - var task_buffer: [libs.len]link.Task = undefined; + var task_buffer: [libs.len]link.PrelinkTask = undefined; var task_buffer_i: usize = 0; { diff --git a/src/link.zig b/src/link.zig index 31fd0a4a4e78ab7d0e213fbac93c70251e5b5eb4..838654775d359d8c64ad1e7ec206ef36e6dee83b 100644 --- a/src/link.zig +++ b/src/link.zig @@ -759,6 +759,8 @@ pub const File = struct { switch (base.tag) { .lld => unreachable, inline else => |tag| { + if (tag == .wasm) @panic("MLUGG TODO"); + if (tag == .spirv) @panic("MLUGG TODO"); dev.check(tag.devFeature()); return @as(*tag.Type(), @fieldParentPtr("base", base)).updateFunc(pt, func_index, mir, maybe_undef_air); }, diff --git a/src/link/Coff.zig b/src/link/Coff.zig index 9a040754ef63f376f4e65bff0f3b2d073f64c6c7..bb8faf583d0a66800bdf6bbf4da76c1d360d1307 100644 --- a/src/link/Coff.zig +++ b/src/link/Coff.zig @@ -1057,8 +1057,10 @@ pub fn updateFunc( coff: *Coff, pt: Zcu.PerThread, func_index: InternPool.Index, - air: Air, - liveness: Air.Liveness, + mir: *const codegen.AnyMir, + /// This may be `undefined`; only pass it to `emitFunction`. + /// This parameter will eventually be removed. + maybe_undef_air: *const Air, ) link.File.UpdateNavError!void { if (build_options.skip_non_native and builtin.object_format != .coff) { @panic("Attempted to compile for object format that was disabled by build configuration"); @@ -1079,15 +1081,15 @@ pub fn updateFunc( var code_buffer: std.ArrayListUnmanaged(u8) = .empty; defer code_buffer.deinit(gpa); - try codegen.generateFunction( + try codegen.emitFunction( &coff.base, pt, zcu.navSrcLoc(nav_index), func_index, - air, - liveness, + mir, &code_buffer, .none, + maybe_undef_air, ); try coff.updateNavCode(pt, nav_index, code_buffer.items, .FUNCTION); diff --git a/src/link/Goff.zig b/src/link/Goff.zig index 28da184495c1e918c51dc2880f8f132392331d26..d0c2b8e80b6368429f38b42f9fa0706a34936f40 100644 --- a/src/link/Goff.zig +++ b/src/link/Goff.zig @@ -13,6 +13,7 @@ const Path = std.Build.Cache.Path; const Zcu = @import("../Zcu.zig"); const InternPool = @import("../InternPool.zig"); const Compilation = @import("../Compilation.zig"); +const codegen = @import("../codegen.zig"); const link = @import("../link.zig"); const trace = @import("../tracy.zig").trace; const build_options = @import("build_options"); @@ -72,14 +73,14 @@ pub fn updateFunc( self: *Goff, pt: Zcu.PerThread, func_index: InternPool.Index, - air: Air, - liveness: Air.Liveness, + mir: *const codegen.AnyMir, + maybe_undef_air: *const Air, ) link.File.UpdateNavError!void { _ = self; _ = pt; _ = func_index; - _ = air; - _ = liveness; + _ = mir; + _ = maybe_undef_air; unreachable; // we always use llvm } diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 2c30b34215c516fb547f90c4be37cef4f7ce46f7..8fd85df0a3bec9c98e5a74aaa92bd3396a69ffa2 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -3051,13 +3051,13 @@ pub fn updateFunc( self: *MachO, pt: Zcu.PerThread, func_index: InternPool.Index, - air: Air, - liveness: Air.Liveness, + mir: *const codegen.AnyMir, + maybe_undef_air: *const Air, ) link.File.UpdateNavError!void { if (build_options.skip_non_native and builtin.object_format != .macho) { @panic("Attempted to compile for object format that was disabled by build configuration"); } - return self.getZigObject().?.updateFunc(self, pt, func_index, air, liveness); + return self.getZigObject().?.updateFunc(self, pt, func_index, mir, maybe_undef_air); } pub fn updateNav(self: *MachO, pt: Zcu.PerThread, nav: InternPool.Nav.Index) link.File.UpdateNavError!void { diff --git a/src/link/MachO/ZigObject.zig b/src/link/MachO/ZigObject.zig index 13ebb40cf99d3b6d1660485ad5d5fde72ad71a56..f378a9c4106e7d8fe1ac9bd1bf01b9b233b78f4a 100644 --- a/src/link/MachO/ZigObject.zig +++ b/src/link/MachO/ZigObject.zig @@ -777,8 +777,10 @@ pub fn updateFunc( macho_file: *MachO, pt: Zcu.PerThread, func_index: InternPool.Index, - air: Air, - liveness: Air.Liveness, + mir: *const codegen.AnyMir, + /// This may be `undefined`; only pass it to `emitFunction`. + /// This parameter will eventually be removed. + maybe_undef_air: *const Air, ) link.File.UpdateNavError!void { const tracy = trace(@src()); defer tracy.end(); @@ -796,15 +798,15 @@ pub fn updateFunc( var debug_wip_nav = if (self.dwarf) |*dwarf| try dwarf.initWipNav(pt, func.owner_nav, sym_index) else null; defer if (debug_wip_nav) |*wip_nav| wip_nav.deinit(); - try codegen.generateFunction( + try codegen.emitFunction( &macho_file.base, pt, zcu.navSrcLoc(func.owner_nav), func_index, - air, - liveness, + mir, &code_buffer, if (debug_wip_nav) |*wip_nav| .{ .dwarf = wip_nav } else .none, + maybe_undef_air, ); const code = code_buffer.items; diff --git a/src/link/Plan9.zig b/src/link/Plan9.zig index c487169b3f16a2c4294f386821895cff7783bed7..0d0699f0f056984d854e9f506ca1570d2a37fc10 100644 --- a/src/link/Plan9.zig +++ b/src/link/Plan9.zig @@ -386,8 +386,10 @@ pub fn updateFunc( self: *Plan9, pt: Zcu.PerThread, func_index: InternPool.Index, - air: Air, - liveness: Air.Liveness, + mir: *const codegen.AnyMir, + /// This may be `undefined`; only pass it to `emitFunction`. + /// This parameter will eventually be removed. + maybe_undef_air: *const Air, ) link.File.UpdateNavError!void { if (build_options.skip_non_native and builtin.object_format != .plan9) { @panic("Attempted to compile for object format that was disabled by build configuration"); @@ -412,15 +414,15 @@ pub fn updateFunc( }; defer dbg_info_output.dbg_line.deinit(); - try codegen.generateFunction( + try codegen.emitFunction( &self.base, pt, zcu.navSrcLoc(func.owner_nav), func_index, - air, - liveness, + mir, &code_buffer, .{ .plan9 = &dbg_info_output }, + maybe_undef_air, ); const code = try code_buffer.toOwnedSlice(gpa); self.getAtomPtr(atom_idx).code = .{ diff --git a/src/link/Queue.zig b/src/link/Queue.zig index c73a0e96845adac891f64df40e589f1272188a89..3436be592169707123d9d17cc920985fbfe6be66 100644 --- a/src/link/Queue.zig +++ b/src/link/Queue.zig @@ -97,8 +97,7 @@ pub fn mirReady(q: *Queue, comp: *Compilation, mir: *ZcuTask.LinkFunc.SharedMir) q.mutex.lock(); defer q.mutex.unlock(); switch (q.state) { - .finished => unreachable, // there's definitely a task queued - .running => return, + .finished, .running => return, .wait_for_mir => |wait_for| if (wait_for != mir) return, } // We were waiting for `mir`, so we will restart the linker thread. diff --git a/src/link/Xcoff.zig b/src/link/Xcoff.zig index 7fe714ce6e38e4cef69178719708a6282674c0bf..97ea300ed2a33b9c5c038f9c49517ba43442e07b 100644 --- a/src/link/Xcoff.zig +++ b/src/link/Xcoff.zig @@ -13,6 +13,7 @@ const Path = std.Build.Cache.Path; const Zcu = @import("../Zcu.zig"); const InternPool = @import("../InternPool.zig"); const Compilation = @import("../Compilation.zig"); +const codegen = @import("../codegen.zig"); const link = @import("../link.zig"); const trace = @import("../tracy.zig").trace; const build_options = @import("build_options"); @@ -72,14 +73,14 @@ pub fn updateFunc( self: *Xcoff, pt: Zcu.PerThread, func_index: InternPool.Index, - air: Air, - liveness: Air.Liveness, + mir: *const codegen.AnyMir, + maybe_undef_air: *const Air, ) link.File.UpdateNavError!void { _ = self; _ = pt; _ = func_index; - _ = air; - _ = liveness; + _ = mir; + _ = maybe_undef_air; unreachable; // we always use llvm } -- 2.54.0 From c0df70706695a67089d4e691d3d3a0f77b90298f Mon Sep 17 00:00:00 2001 From: mlugg Date: Tue, 3 Jun 2025 16:25:16 +0100 Subject: [PATCH 07/35] wasm: get self-hosted compiling, and supporting `separate_thread` My original goal here was just to get the self-hosted Wasm backend compiling again after the pipeline change, but it turned out that from there it was pretty simple to entirely eliminate the shared state between `codegen.wasm` and `link.Wasm`. As such, this commit not only fixes the backend, but makes it the second backend (after CBE) to support the new 1:N:1 threading model. --- lib/std/multi_array_list.zig | 16 ++ src/Compilation.zig | 2 +- src/arch/wasm/CodeGen.zig | 301 ++++++++++++----------------------- src/arch/wasm/Emit.zig | 39 +++-- src/arch/wasm/Mir.zig | 123 +++++++++++--- src/codegen.zig | 19 +-- src/link.zig | 3 +- src/link/Wasm.zig | 189 +++++++++++++++------- src/link/Wasm/Flush.zig | 17 +- src/target.zig | 2 +- 10 files changed, 402 insertions(+), 309 deletions(-) diff --git a/lib/std/multi_array_list.zig b/lib/std/multi_array_list.zig index 341ca6931efc750faf5e9fd09e2b277ca9840029..279a15079981865deb2555f22d899354ea39d68a 100644 --- a/lib/std/multi_array_list.zig +++ b/lib/std/multi_array_list.zig @@ -135,6 +135,22 @@ pub fn MultiArrayList(comptime T: type) type { self.* = undefined; } + /// Returns a `Slice` representing a range of elements in `s`, analagous to `arr[off..len]`. + /// It is illegal to call `deinit` or `toMultiArrayList` on the returned `Slice`. + /// Asserts that `off + len <= s.len`. + pub fn subslice(s: Slice, off: usize, len: usize) Slice { + assert(off + len <= s.len); + var ptrs: [fields.len][*]u8 = undefined; + inline for (s.ptrs, &ptrs, fields) |in, *out, field| { + out.* = in + (off * @sizeOf(field.type)); + } + return .{ + .ptrs = ptrs, + .len = len, + .capacity = len, + }; + } + /// This function is used in the debugger pretty formatters in tools/ to fetch the /// child field order and entry type to facilitate fancy debug printing for this type. fn dbHelper(self: *Slice, child: *Elem, field: *Field, entry: *Entry) void { diff --git a/src/Compilation.zig b/src/Compilation.zig index e96793553957157577fe9cfe73126ec7da343fc4..0342566e27dc61bab505a3263d359417f5062476 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -3500,7 +3500,7 @@ pub fn saveState(comp: *Compilation) !void { // TODO handle the union safety field //addBuf(&bufs, mem.sliceAsBytes(wasm.mir_instructions.items(.data))); addBuf(&bufs, mem.sliceAsBytes(wasm.mir_extra.items)); - addBuf(&bufs, mem.sliceAsBytes(wasm.all_zcu_locals.items)); + addBuf(&bufs, mem.sliceAsBytes(wasm.mir_locals.items)); addBuf(&bufs, mem.sliceAsBytes(wasm.tag_name_bytes.items)); addBuf(&bufs, mem.sliceAsBytes(wasm.tag_name_offs.items)); diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 264b1e732d9eff3c034399d74d14c2cf6247956b..29939235895b22078fd84b2fe87956f8fc7986e2 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -3,7 +3,6 @@ const builtin = @import("builtin"); const Allocator = std.mem.Allocator; const assert = std.debug.assert; const testing = std.testing; -const leb = std.leb; const mem = std.mem; const log = std.log.scoped(.codegen); @@ -18,12 +17,10 @@ const Compilation = @import("../../Compilation.zig"); const link = @import("../../link.zig"); const Air = @import("../../Air.zig"); const Mir = @import("Mir.zig"); -const Emit = @import("Emit.zig"); const abi = @import("abi.zig"); const Alignment = InternPool.Alignment; const errUnionPayloadOffset = codegen.errUnionPayloadOffset; const errUnionErrorOffset = codegen.errUnionErrorOffset; -const Wasm = link.File.Wasm; const target_util = @import("../../target.zig"); const libcFloatPrefix = target_util.libcFloatPrefix; @@ -78,17 +75,24 @@ simd_immediates: std.ArrayListUnmanaged([16]u8) = .empty, /// The Target we're emitting (used to call intInfo) target: *const std.Target, ptr_size: enum { wasm32, wasm64 }, -wasm: *link.File.Wasm, pt: Zcu.PerThread, /// List of MIR Instructions -mir_instructions: *std.MultiArrayList(Mir.Inst), +mir_instructions: std.MultiArrayList(Mir.Inst), /// Contains extra data for MIR -mir_extra: *std.ArrayListUnmanaged(u32), -start_mir_extra_off: u32, -start_locals_off: u32, +mir_extra: std.ArrayListUnmanaged(u32), /// List of all locals' types generated throughout this declaration /// used to emit locals count at start of 'code' section. -locals: *std.ArrayListUnmanaged(std.wasm.Valtype), +mir_locals: std.ArrayListUnmanaged(std.wasm.Valtype), +/// Set of all UAVs referenced by this function. Key is the UAV value, value is the alignment. +/// `.none` means naturally aligned. An explicit alignment is never less than the natural alignment. +mir_uavs: std.AutoArrayHashMapUnmanaged(InternPool.Index, Alignment), +/// Set of all functions whose address this function has taken and which therefore might be called +/// via a `call_indirect` function. +mir_indirect_function_set: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, void), +/// Set of all function types used by this function. These must be interned by the linker. +mir_func_tys: std.AutoArrayHashMapUnmanaged(InternPool.Index, void), +/// The number of `error_name_table_ref` instructions emitted. +error_name_table_ref_count: u32, /// When a function is executing, we store the the current stack pointer's value within this local. /// This value is then used to restore the stack pointer to the original value at the return of the function. initial_stack_value: WValue = .none, @@ -219,7 +223,7 @@ const WValue = union(enum) { if (local_value < reserved + 2) return; // reserved locals may never be re-used. Also accounts for 2 stack locals. const index = local_value - reserved; - const valtype = gen.locals.items[gen.start_locals_off + index]; + const valtype = gen.mir_locals.items[index]; switch (valtype) { .i32 => gen.free_locals_i32.append(gen.gpa, local_value) catch return, // It's ok to fail any of those, a new local can be allocated instead .i64 => gen.free_locals_i64.append(gen.gpa, local_value) catch return, @@ -716,6 +720,12 @@ pub fn deinit(cg: *CodeGen) void { cg.free_locals_f32.deinit(gpa); cg.free_locals_f64.deinit(gpa); cg.free_locals_v128.deinit(gpa); + cg.mir_instructions.deinit(gpa); + cg.mir_extra.deinit(gpa); + cg.mir_locals.deinit(gpa); + cg.mir_uavs.deinit(gpa); + cg.mir_indirect_function_set.deinit(gpa); + cg.mir_func_tys.deinit(gpa); cg.* = undefined; } @@ -876,7 +886,7 @@ fn addTag(cg: *CodeGen, tag: Mir.Inst.Tag) error{OutOfMemory}!void { } fn addExtended(cg: *CodeGen, opcode: std.wasm.MiscOpcode) error{OutOfMemory}!void { - const extra_index = cg.extraLen(); + const extra_index: u32 = @intCast(cg.mir_extra.items.len); try cg.mir_extra.append(cg.gpa, @intFromEnum(opcode)); try cg.addInst(.{ .tag = .misc_prefix, .data = .{ .payload = extra_index } }); } @@ -889,10 +899,6 @@ fn addLocal(cg: *CodeGen, tag: Mir.Inst.Tag, local: u32) error{OutOfMemory}!void try cg.addInst(.{ .tag = tag, .data = .{ .local = local } }); } -fn addFuncTy(cg: *CodeGen, tag: Mir.Inst.Tag, i: Wasm.FunctionType.Index) error{OutOfMemory}!void { - try cg.addInst(.{ .tag = tag, .data = .{ .func_ty = i } }); -} - /// Accepts an unsigned 32bit integer rather than a signed integer to /// prevent us from having to bitcast multiple times as most values /// within codegen are represented as unsigned rather than signed. @@ -911,7 +917,7 @@ fn addImm64(cg: *CodeGen, imm: u64) error{OutOfMemory}!void { /// Accepts the index into the list of 128bit-immediates fn addImm128(cg: *CodeGen, index: u32) error{OutOfMemory}!void { const simd_values = cg.simd_immediates.items[index]; - const extra_index = cg.extraLen(); + const extra_index: u32 = @intCast(cg.mir_extra.items.len); // tag + 128bit value try cg.mir_extra.ensureUnusedCapacity(cg.gpa, 5); cg.mir_extra.appendAssumeCapacity(@intFromEnum(std.wasm.SimdOpcode.v128_const)); @@ -956,15 +962,13 @@ fn addExtra(cg: *CodeGen, extra: anytype) error{OutOfMemory}!u32 { /// Returns the index into `mir_extra` fn addExtraAssumeCapacity(cg: *CodeGen, extra: anytype) error{OutOfMemory}!u32 { const fields = std.meta.fields(@TypeOf(extra)); - const result = cg.extraLen(); + const result: u32 = @intCast(cg.mir_extra.items.len); inline for (fields) |field| { cg.mir_extra.appendAssumeCapacity(switch (field.type) { u32 => @field(extra, field.name), i32 => @bitCast(@field(extra, field.name)), InternPool.Index, InternPool.Nav.Index, - Wasm.UavsObjIndex, - Wasm.UavsExeIndex, => @intFromEnum(@field(extra, field.name)), else => |field_type| @compileError("Unsupported field type " ++ @typeName(field_type)), }); @@ -1034,18 +1038,12 @@ fn emitWValue(cg: *CodeGen, value: WValue) InnerError!void { .float32 => |val| try cg.addInst(.{ .tag = .f32_const, .data = .{ .float32 = val } }), .float64 => |val| try cg.addFloat64(val), .nav_ref => |nav_ref| { - const wasm = cg.wasm; - const comp = wasm.base.comp; - const zcu = comp.zcu.?; + const zcu = cg.pt.zcu; const ip = &zcu.intern_pool; if (ip.getNav(nav_ref.nav_index).isFn(ip)) { assert(nav_ref.offset == 0); - const gop = try wasm.zcu_indirect_function_set.getOrPut(comp.gpa, nav_ref.nav_index); - if (!gop.found_existing) gop.value_ptr.* = {}; - try cg.addInst(.{ - .tag = .func_ref, - .data = .{ .indirect_function_table_index = @enumFromInt(gop.index) }, - }); + try cg.mir_indirect_function_set.put(cg.gpa, nav_ref.nav_index, {}); + try cg.addInst(.{ .tag = .func_ref, .data = .{ .nav_index = nav_ref.nav_index } }); } else if (nav_ref.offset == 0) { try cg.addInst(.{ .tag = .nav_ref, .data = .{ .nav_index = nav_ref.nav_index } }); } else { @@ -1061,41 +1059,37 @@ fn emitWValue(cg: *CodeGen, value: WValue) InnerError!void { } }, .uav_ref => |uav| { - const wasm = cg.wasm; - const comp = wasm.base.comp; - const is_obj = comp.config.output_mode == .Obj; - const zcu = comp.zcu.?; + const zcu = cg.pt.zcu; const ip = &zcu.intern_pool; - if (ip.isFunctionType(ip.typeOf(uav.ip_index))) { - assert(uav.offset == 0); - const owner_nav = ip.toFunc(uav.ip_index).owner_nav; - const gop = try wasm.zcu_indirect_function_set.getOrPut(comp.gpa, owner_nav); - if (!gop.found_existing) gop.value_ptr.* = {}; - try cg.addInst(.{ - .tag = .func_ref, - .data = .{ .indirect_function_table_index = @enumFromInt(gop.index) }, - }); - } else if (uav.offset == 0) { + assert(!ip.isFunctionType(ip.typeOf(uav.ip_index))); + const gop = try cg.mir_uavs.getOrPut(cg.gpa, uav.ip_index); + const this_align: Alignment = a: { + if (uav.orig_ptr_ty == .none) break :a .none; + const ptr_type = ip.indexToKey(uav.orig_ptr_ty).ptr_type; + const this_align = ptr_type.flags.alignment; + if (this_align == .none) break :a .none; + const abi_align = Type.fromInterned(ptr_type.child).abiAlignment(zcu); + if (this_align.compare(.lte, abi_align)) break :a .none; + break :a this_align; + }; + if (!gop.found_existing or + gop.value_ptr.* == .none or + (this_align != .none and this_align.compare(.gt, gop.value_ptr.*))) + { + gop.value_ptr.* = this_align; + } + if (uav.offset == 0) { try cg.addInst(.{ .tag = .uav_ref, - .data = if (is_obj) .{ - .uav_obj = try wasm.refUavObj(uav.ip_index, uav.orig_ptr_ty), - } else .{ - .uav_exe = try wasm.refUavExe(uav.ip_index, uav.orig_ptr_ty), - }, + .data = .{ .ip_index = uav.ip_index }, }); } else { try cg.addInst(.{ .tag = .uav_ref_off, - .data = .{ - .payload = if (is_obj) try cg.addExtra(Mir.UavRefOffObj{ - .uav_obj = try wasm.refUavObj(uav.ip_index, uav.orig_ptr_ty), - .offset = uav.offset, - }) else try cg.addExtra(Mir.UavRefOffExe{ - .uav_exe = try wasm.refUavExe(uav.ip_index, uav.orig_ptr_ty), - .offset = uav.offset, - }), - }, + .data = .{ .payload = try cg.addExtra(@as(Mir.UavRefOff, .{ + .value = uav.ip_index, + .offset = uav.offset, + })) }, }); } }, @@ -1157,106 +1151,12 @@ fn allocLocal(cg: *CodeGen, ty: Type) InnerError!WValue { /// to use a zero-initialized local. fn ensureAllocLocal(cg: *CodeGen, ty: Type) InnerError!WValue { const zcu = cg.pt.zcu; - try cg.locals.append(cg.gpa, typeToValtype(ty, zcu, cg.target)); + try cg.mir_locals.append(cg.gpa, typeToValtype(ty, zcu, cg.target)); const initial_index = cg.local_index; cg.local_index += 1; return .{ .local = .{ .value = initial_index, .references = 1 } }; } -pub const Function = extern struct { - /// Index into `Wasm.mir_instructions`. - mir_off: u32, - /// This is unused except for as a safety slice bound and could be removed. - mir_len: u32, - /// Index into `Wasm.mir_extra`. - mir_extra_off: u32, - /// This is unused except for as a safety slice bound and could be removed. - mir_extra_len: u32, - locals_off: u32, - locals_len: u32, - prologue: Prologue, - - pub const Prologue = extern struct { - flags: Flags, - sp_local: u32, - stack_size: u32, - bottom_stack_local: u32, - - pub const Flags = packed struct(u32) { - stack_alignment: Alignment, - padding: u26 = 0, - }; - - pub const none: Prologue = .{ - .sp_local = 0, - .flags = .{ .stack_alignment = .none }, - .stack_size = 0, - .bottom_stack_local = 0, - }; - - pub fn isNone(p: *const Prologue) bool { - return p.flags.stack_alignment != .none; - } - }; - - pub fn lower(f: *Function, wasm: *Wasm, code: *std.ArrayListUnmanaged(u8)) Allocator.Error!void { - const gpa = wasm.base.comp.gpa; - - // Write the locals in the prologue of the function body. - const locals = wasm.all_zcu_locals.items[f.locals_off..][0..f.locals_len]; - try code.ensureUnusedCapacity(gpa, 5 + locals.len * 6 + 38); - - std.leb.writeUleb128(code.fixedWriter(), @as(u32, @intCast(locals.len))) catch unreachable; - for (locals) |local| { - std.leb.writeUleb128(code.fixedWriter(), @as(u32, 1)) catch unreachable; - code.appendAssumeCapacity(@intFromEnum(local)); - } - - // Stack management section of function prologue. - const stack_alignment = f.prologue.flags.stack_alignment; - if (stack_alignment.toByteUnits()) |align_bytes| { - const sp_global: Wasm.GlobalIndex = .stack_pointer; - // load stack pointer - code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.global_get)); - std.leb.writeULEB128(code.fixedWriter(), @intFromEnum(sp_global)) catch unreachable; - // store stack pointer so we can restore it when we return from the function - code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.local_tee)); - leb.writeUleb128(code.fixedWriter(), f.prologue.sp_local) catch unreachable; - // get the total stack size - const aligned_stack: i32 = @intCast(stack_alignment.forward(f.prologue.stack_size)); - code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_const)); - leb.writeIleb128(code.fixedWriter(), aligned_stack) catch unreachable; - // subtract it from the current stack pointer - code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_sub)); - // Get negative stack alignment - const neg_stack_align = @as(i32, @intCast(align_bytes)) * -1; - code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_const)); - leb.writeIleb128(code.fixedWriter(), neg_stack_align) catch unreachable; - // Bitwise-and the value to get the new stack pointer to ensure the - // pointers are aligned with the abi alignment. - code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_and)); - // The bottom will be used to calculate all stack pointer offsets. - code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.local_tee)); - leb.writeUleb128(code.fixedWriter(), f.prologue.bottom_stack_local) catch unreachable; - // Store the current stack pointer value into the global stack pointer so other function calls will - // start from this value instead and not overwrite the current stack. - code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.global_set)); - std.leb.writeULEB128(code.fixedWriter(), @intFromEnum(sp_global)) catch unreachable; - } - - var emit: Emit = .{ - .mir = .{ - .instruction_tags = wasm.mir_instructions.items(.tag)[f.mir_off..][0..f.mir_len], - .instruction_datas = wasm.mir_instructions.items(.data)[f.mir_off..][0..f.mir_len], - .extra = wasm.mir_extra.items[f.mir_extra_off..][0..f.mir_extra_len], - }, - .wasm = wasm, - .code = code, - }; - try emit.lowerToCode(); - } -}; - pub const Error = error{ OutOfMemory, /// Compiler was asked to operate on a number larger than supported. @@ -1265,13 +1165,16 @@ pub const Error = error{ CodegenFail, }; -pub fn function( - wasm: *Wasm, +pub fn generate( + bin_file: *link.File, pt: Zcu.PerThread, + src_loc: Zcu.LazySrcLoc, func_index: InternPool.Index, - air: Air, - liveness: Air.Liveness, -) Error!Function { + air: *const Air, + liveness: *const Air.Liveness, +) Error!Mir { + _ = src_loc; + _ = bin_file; const zcu = pt.zcu; const gpa = zcu.gpa; const cg = zcu.funcInfo(func_index); @@ -1279,10 +1182,8 @@ pub fn function( const target = &file_scope.mod.?.resolved_target.result; const fn_ty = zcu.navValue(cg.owner_nav).typeOf(zcu); const fn_info = zcu.typeToFunc(fn_ty).?; - const ip = &zcu.intern_pool; - const fn_ty_index = try wasm.internFunctionType(fn_info.cc, fn_info.param_types.get(ip), .fromInterned(fn_info.return_type), target); - const returns = fn_ty_index.ptr(wasm).returns.slice(wasm); - const any_returns = returns.len != 0; + const ret_ty: Type = .fromInterned(fn_info.return_type); + const any_returns = !firstParamSRet(fn_info.cc, ret_ty, zcu, target) and ret_ty.hasRuntimeBitsIgnoreComptime(zcu); var cc_result = try resolveCallingConventionValues(zcu, fn_ty, target); defer cc_result.deinit(gpa); @@ -1290,8 +1191,8 @@ pub fn function( var code_gen: CodeGen = .{ .gpa = gpa, .pt = pt, - .air = air, - .liveness = liveness, + .air = air.*, + .liveness = liveness.*, .owner_nav = cg.owner_nav, .target = target, .ptr_size = switch (target.cpu.arch) { @@ -1299,31 +1200,33 @@ pub fn function( .wasm64 => .wasm64, else => unreachable, }, - .wasm = wasm, .func_index = func_index, .args = cc_result.args, .return_value = cc_result.return_value, .local_index = cc_result.local_index, - .mir_instructions = &wasm.mir_instructions, - .mir_extra = &wasm.mir_extra, - .locals = &wasm.all_zcu_locals, - .start_mir_extra_off = @intCast(wasm.mir_extra.items.len), - .start_locals_off = @intCast(wasm.all_zcu_locals.items.len), + .mir_instructions = .empty, + .mir_extra = .empty, + .mir_locals = .empty, + .mir_uavs = .empty, + .mir_indirect_function_set = .empty, + .mir_func_tys = .empty, + .error_name_table_ref_count = 0, }; defer code_gen.deinit(); - return functionInner(&code_gen, any_returns) catch |err| switch (err) { - error.CodegenFail => return error.CodegenFail, + try code_gen.mir_func_tys.putNoClobber(gpa, fn_ty.toIntern(), {}); + + return generateInner(&code_gen, any_returns) catch |err| switch (err) { + error.CodegenFail, + error.OutOfMemory, + error.Overflow, + => |e| return e, else => |e| return code_gen.fail("failed to generate function: {s}", .{@errorName(e)}), }; } -fn functionInner(cg: *CodeGen, any_returns: bool) InnerError!Function { - const wasm = cg.wasm; +fn generateInner(cg: *CodeGen, any_returns: bool) InnerError!Mir { const zcu = cg.pt.zcu; - - const start_mir_off: u32 = @intCast(wasm.mir_instructions.len); - try cg.branches.append(cg.gpa, .{}); // clean up outer branch defer { @@ -1347,20 +1250,25 @@ fn functionInner(cg: *CodeGen, any_returns: bool) InnerError!Function { try cg.addTag(.end); try cg.addTag(.dbg_epilogue_begin); - return .{ - .mir_off = start_mir_off, - .mir_len = @intCast(wasm.mir_instructions.len - start_mir_off), - .mir_extra_off = cg.start_mir_extra_off, - .mir_extra_len = cg.extraLen(), - .locals_off = cg.start_locals_off, - .locals_len = @intCast(wasm.all_zcu_locals.items.len - cg.start_locals_off), + var mir: Mir = .{ + .instructions = cg.mir_instructions.toOwnedSlice(), + .extra = &.{}, // fallible so assigned after errdefer + .locals = &.{}, // fallible so assigned after errdefer .prologue = if (cg.initial_stack_value == .none) .none else .{ .sp_local = cg.initial_stack_value.local.value, .flags = .{ .stack_alignment = cg.stack_alignment }, .stack_size = cg.stack_size, .bottom_stack_local = cg.bottom_stack_value.local.value, }, + .uavs = cg.mir_uavs.move(), + .indirect_function_set = cg.mir_indirect_function_set.move(), + .func_tys = cg.mir_func_tys.move(), + .error_name_table_ref_count = cg.error_name_table_ref_count, }; + errdefer mir.deinit(cg.gpa); + mir.extra = try cg.mir_extra.toOwnedSlice(cg.gpa); + mir.locals = try cg.mir_locals.toOwnedSlice(cg.gpa); + return mir; } const CallWValues = struct { @@ -2220,7 +2128,6 @@ fn airRetLoad(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { } fn airCall(cg: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) InnerError!void { - const wasm = cg.wasm; if (modifier == .always_tail) return cg.fail("TODO implement tail calls for wasm", .{}); const pl_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; const extra = cg.air.extraData(Air.Call, pl_op.payload); @@ -2277,8 +2184,11 @@ fn airCall(cg: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModifie const operand = try cg.resolveInst(pl_op.operand); try cg.emitWValue(operand); - const fn_type_index = try wasm.internFunctionType(fn_info.cc, fn_info.param_types.get(ip), .fromInterned(fn_info.return_type), cg.target); - try cg.addFuncTy(.call_indirect, fn_type_index); + try cg.mir_func_tys.put(cg.gpa, fn_ty.toIntern(), {}); + try cg.addInst(.{ + .tag = .call_indirect, + .data = .{ .ip_index = fn_ty.toIntern() }, + }); } const result_value = result_value: { @@ -2449,7 +2359,7 @@ fn store(cg: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErr try cg.emitWValue(lhs); try cg.lowerToStack(rhs); // TODO: Add helper functions for simd opcodes - const extra_index = cg.extraLen(); + const extra_index: u32 = @intCast(cg.mir_extra.items.len); // stores as := opcode, offset, alignment (opcode::memarg) try cg.mir_extra.appendSlice(cg.gpa, &[_]u32{ @intFromEnum(std.wasm.SimdOpcode.v128_store), @@ -2574,7 +2484,7 @@ fn load(cg: *CodeGen, operand: WValue, ty: Type, offset: u32) InnerError!WValue if (ty.zigTypeTag(zcu) == .vector) { // TODO: Add helper functions for simd opcodes - const extra_index = cg.extraLen(); + const extra_index: u32 = @intCast(cg.mir_extra.items.len); // stores as := opcode, offset, alignment (opcode::memarg) try cg.mir_extra.appendSlice(cg.gpa, &[_]u32{ @intFromEnum(std.wasm.SimdOpcode.v128_load), @@ -4971,7 +4881,7 @@ fn airArrayElemVal(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { try cg.emitWValue(array); - const extra_index = cg.extraLen(); + const extra_index: u32 = @intCast(cg.mir_extra.items.len); try cg.mir_extra.appendSlice(cg.gpa, &operands); try cg.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } }); @@ -5123,7 +5033,7 @@ fn airSplat(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { else => break :blk, // Cannot make use of simd-instructions }; try cg.emitWValue(operand); - const extra_index: u32 = cg.extraLen(); + const extra_index: u32 = @intCast(cg.mir_extra.items.len); // stores as := opcode, offset, alignment (opcode::memarg) try cg.mir_extra.appendSlice(cg.gpa, &[_]u32{ opcode, @@ -5142,7 +5052,7 @@ fn airSplat(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { else => break :blk, // Cannot make use of simd-instructions }; try cg.emitWValue(operand); - const extra_index = cg.extraLen(); + const extra_index: u32 = @intCast(cg.mir_extra.items.len); try cg.mir_extra.append(cg.gpa, opcode); try cg.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } }); return cg.finishAir(inst, .stack, &.{ty_op.operand}); @@ -5246,7 +5156,7 @@ fn airShuffleTwo(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { } try cg.emitWValue(operand_a); try cg.emitWValue(operand_b); - const extra_index = cg.extraLen(); + const extra_index: u32 = @intCast(cg.mir_extra.items.len); try cg.mir_extra.appendSlice(cg.gpa, &.{ @intFromEnum(std.wasm.SimdOpcode.i8x16_shuffle), @bitCast(lane_map[0..4].*), @@ -6016,9 +5926,8 @@ fn airErrorName(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { const name_ty = Type.slice_const_u8_sentinel_0; const abi_size = name_ty.abiSize(pt.zcu); - cg.wasm.error_name_table_ref_count += 1; - // Lowers to a i32.const or i64.const with the error table memory address. + cg.error_name_table_ref_count += 1; try cg.addTag(.error_name_table_ref); try cg.emitWValue(operand); switch (cg.ptr_size) { @@ -6046,7 +5955,7 @@ fn airPtrSliceFieldPtr(cg: *CodeGen, inst: Air.Inst.Index, offset: u32) InnerErr /// NOTE: Allocates place for result on virtual stack, when integer size > 64 bits fn intZeroValue(cg: *CodeGen, ty: Type) InnerError!WValue { - const zcu = cg.wasm.base.comp.zcu.?; + const zcu = cg.pt.zcu; const int_info = ty.intInfo(zcu); const wasm_bits = toWasmBits(int_info.bits) orelse { return cg.fail("TODO: Implement intZeroValue for integer bitsize: {d}", .{int_info.bits}); @@ -7673,7 +7582,3 @@ fn floatCmpIntrinsic(op: std.math.CompareOperator, bits: u16) Mir.Intrinsic { }, }; } - -fn extraLen(cg: *const CodeGen) u32 { - return @intCast(cg.mir_extra.items.len - cg.start_mir_extra_off); -} diff --git a/src/arch/wasm/Emit.zig b/src/arch/wasm/Emit.zig index 28159f33361ef205bdd21ceeca61e10743d08296..8024f2db9e1f75433b0a2ad74f566d00aa174c4d 100644 --- a/src/arch/wasm/Emit.zig +++ b/src/arch/wasm/Emit.zig @@ -31,8 +31,8 @@ pub fn lowerToCode(emit: *Emit) Error!void { const target = &comp.root_mod.resolved_target.result; const is_wasm32 = target.cpu.arch == .wasm32; - const tags = mir.instruction_tags; - const datas = mir.instruction_datas; + const tags = mir.instructions.items(.tag); + const datas = mir.instructions.items(.data); var inst: u32 = 0; loop: switch (tags[inst]) { @@ -50,18 +50,19 @@ pub fn lowerToCode(emit: *Emit) Error!void { }, .uav_ref => { if (is_obj) { - try uavRefOffObj(wasm, code, .{ .uav_obj = datas[inst].uav_obj, .offset = 0 }, is_wasm32); + try uavRefObj(wasm, code, datas[inst].ip_index, 0, is_wasm32); } else { - try uavRefOffExe(wasm, code, .{ .uav_exe = datas[inst].uav_exe, .offset = 0 }, is_wasm32); + try uavRefExe(wasm, code, datas[inst].ip_index, 0, is_wasm32); } inst += 1; continue :loop tags[inst]; }, .uav_ref_off => { + const extra = mir.extraData(Mir.UavRefOff, datas[inst].payload).data; if (is_obj) { - try uavRefOffObj(wasm, code, mir.extraData(Mir.UavRefOffObj, datas[inst].payload).data, is_wasm32); + try uavRefObj(wasm, code, extra.value, extra.offset, is_wasm32); } else { - try uavRefOffExe(wasm, code, mir.extraData(Mir.UavRefOffExe, datas[inst].payload).data, is_wasm32); + try uavRefExe(wasm, code, extra.value, extra.offset, is_wasm32); } inst += 1; continue :loop tags[inst]; @@ -77,11 +78,14 @@ pub fn lowerToCode(emit: *Emit) Error!void { continue :loop tags[inst]; }, .func_ref => { + const indirect_func_idx: Wasm.ZcuIndirectFunctionSetIndex = @enumFromInt( + wasm.zcu_indirect_function_set.getIndex(datas[inst].nav_index).?, + ); code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_const)); if (is_obj) { @panic("TODO"); } else { - leb.writeUleb128(code.fixedWriter(), 1 + @intFromEnum(datas[inst].indirect_function_table_index)) catch unreachable; + leb.writeUleb128(code.fixedWriter(), 1 + @intFromEnum(indirect_func_idx)) catch unreachable; } inst += 1; continue :loop tags[inst]; @@ -101,6 +105,7 @@ pub fn lowerToCode(emit: *Emit) Error!void { continue :loop tags[inst]; }, .error_name_table_ref => { + wasm.error_name_table_ref_count += 1; try code.ensureUnusedCapacity(gpa, 11); const opcode: std.wasm.Opcode = if (is_wasm32) .i32_const else .i64_const; code.appendAssumeCapacity(@intFromEnum(opcode)); @@ -176,7 +181,13 @@ pub fn lowerToCode(emit: *Emit) Error!void { .call_indirect => { try code.ensureUnusedCapacity(gpa, 11); - const func_ty_index = datas[inst].func_ty; + const fn_info = comp.zcu.?.typeToFunc(.fromInterned(datas[inst].ip_index)).?; + const func_ty_index = wasm.getExistingFunctionType( + fn_info.cc, + fn_info.param_types.get(&comp.zcu.?.intern_pool), + .fromInterned(fn_info.return_type), + target, + ).?; code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.call_indirect)); if (is_obj) { try wasm.out_relocs.append(gpa, .{ @@ -912,7 +923,7 @@ fn encodeMemArg(code: *std.ArrayListUnmanaged(u8), mem_arg: Mir.MemArg) void { leb.writeUleb128(code.fixedWriter(), mem_arg.offset) catch unreachable; } -fn uavRefOffObj(wasm: *Wasm, code: *std.ArrayListUnmanaged(u8), data: Mir.UavRefOffObj, is_wasm32: bool) !void { +fn uavRefObj(wasm: *Wasm, code: *std.ArrayListUnmanaged(u8), value: InternPool.Index, offset: i32, is_wasm32: bool) !void { const comp = wasm.base.comp; const gpa = comp.gpa; const opcode: std.wasm.Opcode = if (is_wasm32) .i32_const else .i64_const; @@ -922,14 +933,14 @@ fn uavRefOffObj(wasm: *Wasm, code: *std.ArrayListUnmanaged(u8), data: Mir.UavRef try wasm.out_relocs.append(gpa, .{ .offset = @intCast(code.items.len), - .pointee = .{ .symbol_index = try wasm.uavSymbolIndex(data.uav_obj.key(wasm).*) }, + .pointee = .{ .symbol_index = try wasm.uavSymbolIndex(value) }, .tag = if (is_wasm32) .memory_addr_leb else .memory_addr_leb64, - .addend = data.offset, + .addend = offset, }); code.appendNTimesAssumeCapacity(0, if (is_wasm32) 5 else 10); } -fn uavRefOffExe(wasm: *Wasm, code: *std.ArrayListUnmanaged(u8), data: Mir.UavRefOffExe, is_wasm32: bool) !void { +fn uavRefExe(wasm: *Wasm, code: *std.ArrayListUnmanaged(u8), value: InternPool.Index, offset: i32, is_wasm32: bool) !void { const comp = wasm.base.comp; const gpa = comp.gpa; const opcode: std.wasm.Opcode = if (is_wasm32) .i32_const else .i64_const; @@ -937,8 +948,8 @@ fn uavRefOffExe(wasm: *Wasm, code: *std.ArrayListUnmanaged(u8), data: Mir.UavRef try code.ensureUnusedCapacity(gpa, 11); code.appendAssumeCapacity(@intFromEnum(opcode)); - const addr = wasm.uavAddr(data.uav_exe); - leb.writeUleb128(code.fixedWriter(), @as(u32, @intCast(@as(i64, addr) + data.offset))) catch unreachable; + const addr = wasm.uavAddr(value); + leb.writeUleb128(code.fixedWriter(), @as(u32, @intCast(@as(i64, addr) + offset))) catch unreachable; } fn navRefOff(wasm: *Wasm, code: *std.ArrayListUnmanaged(u8), data: Mir.NavRefOff, is_wasm32: bool) !void { diff --git a/src/arch/wasm/Mir.zig b/src/arch/wasm/Mir.zig index 5c8c558926bebed231e3e021d79255121775e523..3aee13acd703838b5839626c6e858b4271593d42 100644 --- a/src/arch/wasm/Mir.zig +++ b/src/arch/wasm/Mir.zig @@ -9,16 +9,53 @@ const Mir = @This(); const InternPool = @import("../../InternPool.zig"); const Wasm = @import("../../link/Wasm.zig"); +const Emit = @import("Emit.zig"); +const Alignment = InternPool.Alignment; const builtin = @import("builtin"); const std = @import("std"); const assert = std.debug.assert; +const leb = std.leb; -instruction_tags: []const Inst.Tag, -instruction_datas: []const Inst.Data, +instructions: std.MultiArrayList(Inst).Slice, /// A slice of indexes where the meaning of the data is determined by the /// `Inst.Tag` value. extra: []const u32, +locals: []const std.wasm.Valtype, +prologue: Prologue, + +/// Not directly used by `Emit`, but the linker needs this to merge it with a global set. +/// Value is the explicit alignment if greater than natural alignment, `.none` otherwise. +uavs: std.AutoArrayHashMapUnmanaged(InternPool.Index, Alignment), +/// Not directly used by `Emit`, but the linker needs this to merge it with a global set. +indirect_function_set: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, void), +/// Not directly used by `Emit`, but the linker needs this to ensure these types are interned. +func_tys: std.AutoArrayHashMapUnmanaged(InternPool.Index, void), +/// Not directly used by `Emit`, but the linker needs this to add it to its own refcount. +error_name_table_ref_count: u32, + +pub const Prologue = extern struct { + flags: Flags, + sp_local: u32, + stack_size: u32, + bottom_stack_local: u32, + + pub const Flags = packed struct(u32) { + stack_alignment: Alignment, + padding: u26 = 0, + }; + + pub const none: Prologue = .{ + .sp_local = 0, + .flags = .{ .stack_alignment = .none }, + .stack_size = 0, + .bottom_stack_local = 0, + }; + + pub fn isNone(p: *const Prologue) bool { + return p.flags.stack_alignment != .none; + } +}; pub const Inst = struct { /// The opcode that represents this instruction @@ -80,7 +117,7 @@ pub const Inst = struct { /// Lowers to an i32_const which is the index of the function in the /// table section. /// - /// Uses `indirect_function_table_index`. + /// Uses `nav_index`. func_ref, /// Inserts debug information about the current line and column /// of the source code @@ -123,7 +160,7 @@ pub const Inst = struct { /// Calls a function pointer by its function signature /// and index into the function table. /// - /// Uses `func_ty` + /// Uses `ip_index`; the `InternPool.Index` is the function type. call_indirect, /// Calls a function by its index. /// @@ -611,11 +648,7 @@ pub const Inst = struct { ip_index: InternPool.Index, nav_index: InternPool.Nav.Index, - func_ty: Wasm.FunctionType.Index, intrinsic: Intrinsic, - uav_obj: Wasm.UavsObjIndex, - uav_exe: Wasm.UavsExeIndex, - indirect_function_table_index: Wasm.ZcuIndirectFunctionSetIndex, comptime { switch (builtin.mode) { @@ -626,10 +659,66 @@ pub const Inst = struct { }; }; -pub fn deinit(self: *Mir, gpa: std.mem.Allocator) void { - self.instructions.deinit(gpa); - gpa.free(self.extra); - self.* = undefined; +pub fn deinit(mir: *Mir, gpa: std.mem.Allocator) void { + mir.instructions.deinit(gpa); + gpa.free(mir.extra); + gpa.free(mir.locals); + mir.uavs.deinit(gpa); + mir.indirect_function_set.deinit(gpa); + mir.func_tys.deinit(gpa); + mir.* = undefined; +} + +pub fn lower(mir: *const Mir, wasm: *Wasm, code: *std.ArrayListUnmanaged(u8)) std.mem.Allocator.Error!void { + const gpa = wasm.base.comp.gpa; + + // Write the locals in the prologue of the function body. + try code.ensureUnusedCapacity(gpa, 5 + mir.locals.len * 6 + 38); + + std.leb.writeUleb128(code.fixedWriter(), @as(u32, @intCast(mir.locals.len))) catch unreachable; + for (mir.locals) |local| { + std.leb.writeUleb128(code.fixedWriter(), @as(u32, 1)) catch unreachable; + code.appendAssumeCapacity(@intFromEnum(local)); + } + + // Stack management section of function prologue. + const stack_alignment = mir.prologue.flags.stack_alignment; + if (stack_alignment.toByteUnits()) |align_bytes| { + const sp_global: Wasm.GlobalIndex = .stack_pointer; + // load stack pointer + code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.global_get)); + std.leb.writeULEB128(code.fixedWriter(), @intFromEnum(sp_global)) catch unreachable; + // store stack pointer so we can restore it when we return from the function + code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.local_tee)); + leb.writeUleb128(code.fixedWriter(), mir.prologue.sp_local) catch unreachable; + // get the total stack size + const aligned_stack: i32 = @intCast(stack_alignment.forward(mir.prologue.stack_size)); + code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_const)); + leb.writeIleb128(code.fixedWriter(), aligned_stack) catch unreachable; + // subtract it from the current stack pointer + code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_sub)); + // Get negative stack alignment + const neg_stack_align = @as(i32, @intCast(align_bytes)) * -1; + code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_const)); + leb.writeIleb128(code.fixedWriter(), neg_stack_align) catch unreachable; + // Bitwise-and the value to get the new stack pointer to ensure the + // pointers are aligned with the abi alignment. + code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_and)); + // The bottom will be used to calculate all stack pointer offsets. + code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.local_tee)); + leb.writeUleb128(code.fixedWriter(), mir.prologue.bottom_stack_local) catch unreachable; + // Store the current stack pointer value into the global stack pointer so other function calls will + // start from this value instead and not overwrite the current stack. + code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.global_set)); + std.leb.writeULEB128(code.fixedWriter(), @intFromEnum(sp_global)) catch unreachable; + } + + var emit: Emit = .{ + .mir = mir.*, + .wasm = wasm, + .code = code, + }; + try emit.lowerToCode(); } pub fn extraData(self: *const Mir, comptime T: type, index: usize) struct { data: T, end: usize } { @@ -643,6 +732,7 @@ pub fn extraData(self: *const Mir, comptime T: type, index: usize) struct { data Wasm.UavsObjIndex, Wasm.UavsExeIndex, InternPool.Nav.Index, + InternPool.Index, => @enumFromInt(self.extra[i]), else => |field_type| @compileError("Unsupported field type " ++ @typeName(field_type)), }; @@ -695,13 +785,8 @@ pub const MemArg = struct { alignment: u32, }; -pub const UavRefOffObj = struct { - uav_obj: Wasm.UavsObjIndex, - offset: i32, -}; - -pub const UavRefOffExe = struct { - uav_exe: Wasm.UavsExeIndex, +pub const UavRefOff = struct { + value: InternPool.Index, offset: i32, }; diff --git a/src/codegen.zig b/src/codegen.zig index ea57aaf89c0e5238871c54163c0a814659f97122..5a8f17735a2ab7c34316bea68917f2fe0ee16a83 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -123,6 +123,7 @@ pub const AnyMir = union { .stage2_riscv64, .stage2_sparc64, .stage2_x86_64, + .stage2_wasm, .stage2_c, => |backend_ct| @field(mir, tag(backend_ct)).deinit(gpa), } @@ -153,6 +154,7 @@ pub fn generateFunction( .stage2_riscv64, .stage2_sparc64, .stage2_x86_64, + .stage2_wasm, .stage2_c, => |backend| { dev.check(devFeatureForBackend(backend)); @@ -784,7 +786,6 @@ fn lowerUavRef( const comp = lf.comp; const target = &comp.root_mod.resolved_target.result; const ptr_width_bytes = @divExact(target.ptrBitWidth(), 8); - const is_obj = comp.config.output_mode == .Obj; const uav_val = uav.val; const uav_ty = Type.fromInterned(ip.typeOf(uav_val)); const is_fn_body = uav_ty.zigTypeTag(zcu) == .@"fn"; @@ -804,21 +805,7 @@ fn lowerUavRef( dev.check(link.File.Tag.wasm.devFeature()); const wasm = lf.cast(.wasm).?; assert(reloc_parent == .none); - if (is_obj) { - try wasm.out_relocs.append(gpa, .{ - .offset = @intCast(code.items.len), - .pointee = .{ .symbol_index = try wasm.uavSymbolIndex(uav.val) }, - .tag = if (ptr_width_bytes == 4) .memory_addr_i32 else .memory_addr_i64, - .addend = @intCast(offset), - }); - } else { - try wasm.uav_fixups.ensureUnusedCapacity(gpa, 1); - wasm.uav_fixups.appendAssumeCapacity(.{ - .uavs_exe_index = try wasm.refUavExe(uav.val, uav.orig_ty), - .offset = @intCast(code.items.len), - .addend = @intCast(offset), - }); - } + try wasm.addUavReloc(code.items.len, uav.val, uav.orig_ty, @intCast(offset)); code.appendNTimesAssumeCapacity(0, ptr_width_bytes); return; }, diff --git a/src/link.zig b/src/link.zig index 838654775d359d8c64ad1e7ec206ef36e6dee83b..f49acbf3d6114643863a934b3e08f27d35c49bc1 100644 --- a/src/link.zig +++ b/src/link.zig @@ -759,7 +759,6 @@ pub const File = struct { switch (base.tag) { .lld => unreachable, inline else => |tag| { - if (tag == .wasm) @panic("MLUGG TODO"); if (tag == .spirv) @panic("MLUGG TODO"); dev.check(tag.devFeature()); return @as(*tag.Type(), @fieldParentPtr("base", base)).updateFunc(pt, func_index, mir, maybe_undef_air); @@ -1450,12 +1449,12 @@ pub fn doZcuTask(comp: *Compilation, tid: usize, task: ZcuTask) void { const nav = zcu.funcInfo(func.func).owner_nav; const pt: Zcu.PerThread = .activate(zcu, @enumFromInt(tid)); defer pt.deactivate(); - assert(zcu.llvm_object == null); // LLVM codegen doesn't produce MIR switch (func.mir.status.load(.monotonic)) { .pending => unreachable, .ready => {}, .failed => return, } + assert(zcu.llvm_object == null); // LLVM codegen doesn't produce MIR const mir = &func.mir.value; if (comp.bin_file) |lf| { lf.updateFunc(pt, func.func, mir, func.air) catch |err| switch (err) { diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 5c804ed21f909d07329eacccd2b0f30ad4e50b65..67e530b5ccf93784866d068f8b39c4d99f547a30 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -282,7 +282,7 @@ mir_instructions: std.MultiArrayList(Mir.Inst) = .{}, /// Corresponds to `mir_instructions`. mir_extra: std.ArrayListUnmanaged(u32) = .empty, /// All local types for all Zcu functions. -all_zcu_locals: std.ArrayListUnmanaged(std.wasm.Valtype) = .empty, +mir_locals: std.ArrayListUnmanaged(std.wasm.Valtype) = .empty, params_scratch: std.ArrayListUnmanaged(std.wasm.Valtype) = .empty, returns_scratch: std.ArrayListUnmanaged(std.wasm.Valtype) = .empty, @@ -866,9 +866,24 @@ const ZcuDataStarts = struct { }; pub const ZcuFunc = union { - function: CodeGen.Function, + function: Function, tag_name: TagName, + pub const Function = extern struct { + /// Index into `Wasm.mir_instructions`. + instructions_off: u32, + /// This is unused except for as a safety slice bound and could be removed. + instructions_len: u32, + /// Index into `Wasm.mir_extra`. + extra_off: u32, + /// This is unused except for as a safety slice bound and could be removed. + extra_len: u32, + /// Index into `Wasm.mir_locals`. + locals_off: u32, + locals_len: u32, + prologue: Mir.Prologue, + }; + pub const TagName = extern struct { symbol_name: String, type_index: FunctionType.Index, @@ -3107,7 +3122,7 @@ pub fn deinit(wasm: *Wasm) void { wasm.mir_instructions.deinit(gpa); wasm.mir_extra.deinit(gpa); - wasm.all_zcu_locals.deinit(gpa); + wasm.mir_locals.deinit(gpa); if (wasm.dwarf) |*dwarf| dwarf.deinit(); @@ -3167,33 +3182,96 @@ pub fn deinit(wasm: *Wasm) void { wasm.missing_exports.deinit(gpa); } -pub fn updateFunc(wasm: *Wasm, pt: Zcu.PerThread, func_index: InternPool.Index, air: Air, liveness: Air.Liveness) !void { +pub fn updateFunc( + wasm: *Wasm, + pt: Zcu.PerThread, + func_index: InternPool.Index, + any_mir: *const codegen.AnyMir, + maybe_undef_air: *const Air, +) !void { if (build_options.skip_non_native and builtin.object_format != .wasm) { @panic("Attempted to compile for object format that was disabled by build configuration"); } dev.check(.wasm_backend); + _ = maybe_undef_air; // we (correctly) do not need this + // This linker implementation only works with codegen backend `.stage2_wasm`. + const mir = &any_mir.wasm; const zcu = pt.zcu; const gpa = zcu.gpa; - try wasm.functions.ensureUnusedCapacity(gpa, 1); - try wasm.zcu_funcs.ensureUnusedCapacity(gpa, 1); - const ip = &zcu.intern_pool; + const is_obj = zcu.comp.config.output_mode == .Obj; + const target = &zcu.comp.root_mod.resolved_target.result; const owner_nav = zcu.funcInfo(func_index).owner_nav; log.debug("updateFunc {}", .{ip.getNav(owner_nav).fqn.fmt(ip)}); + // For Wasm, we do not lower the MIR to code just yet. That lowering happens during `flush`, + // after garbage collection, which can affect function and global indexes, which affects the + // LEB integer encoding, which affects the output binary size. + + // However, we do move the MIR into a more efficient in-memory representation, where the arrays + // for all functions are packed together rather than keeping them each in their own `Mir`. + const mir_instructions_off: u32 = @intCast(wasm.mir_instructions.len); + const mir_extra_off: u32 = @intCast(wasm.mir_extra.items.len); + const mir_locals_off: u32 = @intCast(wasm.mir_locals.items.len); + { + // Copying MultiArrayList data is a little non-trivial. Resize, then memcpy both slices. + const old_len = wasm.mir_instructions.len; + try wasm.mir_instructions.resize(gpa, old_len + mir.instructions.len); + const dest_slice = wasm.mir_instructions.slice().subslice(old_len, mir.instructions.len); + const src_slice = mir.instructions; + @memcpy(dest_slice.items(.tag), src_slice.items(.tag)); + @memcpy(dest_slice.items(.data), src_slice.items(.data)); + } + try wasm.mir_extra.appendSlice(gpa, mir.extra); + try wasm.mir_locals.appendSlice(gpa, mir.locals); + + // We also need to populate some global state from `mir`. + try wasm.zcu_indirect_function_set.ensureUnusedCapacity(gpa, mir.indirect_function_set.count()); + for (mir.indirect_function_set.keys()) |nav| wasm.zcu_indirect_function_set.putAssumeCapacity(nav, {}); + for (mir.func_tys.keys()) |func_ty| { + const fn_info = zcu.typeToFunc(.fromInterned(func_ty)).?; + _ = try wasm.internFunctionType(fn_info.cc, fn_info.param_types.get(ip), .fromInterned(fn_info.return_type), target); + } + wasm.error_name_table_ref_count += mir.error_name_table_ref_count; + // We need to populate UAV data. In theory, we can lower the UAV values while we fill `mir.uavs`. + // However, lowering the data might cause *more* UAVs to be created, and mixing them up would be + // a headache. So instead, just write `undefined` placeholder code and use the `ZcuDataStarts`. const zds: ZcuDataStarts = .init(wasm); + for (mir.uavs.keys(), mir.uavs.values()) |uav_val, uav_align| { + if (uav_align != .none) { + const gop = try wasm.overaligned_uavs.getOrPut(gpa, uav_val); + gop.value_ptr.* = if (gop.found_existing) gop.value_ptr.maxStrict(uav_align) else uav_align; + } + if (is_obj) { + const gop = try wasm.uavs_obj.getOrPut(gpa, uav_val); + if (!gop.found_existing) gop.value_ptr.* = undefined; // `zds` handles lowering + } else { + const gop = try wasm.uavs_exe.getOrPut(gpa, uav_val); + if (!gop.found_existing) gop.value_ptr.* = .{ + .code = undefined, // `zds` handles lowering + .count = 0, + }; + gop.value_ptr.count += 1; + } + } + try zds.finish(wasm, pt); // actually generates the UAVs + + try wasm.functions.ensureUnusedCapacity(gpa, 1); + try wasm.zcu_funcs.ensureUnusedCapacity(gpa, 1); // This converts AIR to MIR but does not yet lower to wasm code. - // That lowering happens during `flush`, after garbage collection, which - // can affect function and global indexes, which affects the LEB integer - // encoding, which affects the output binary size. - const function = try CodeGen.function(wasm, pt, func_index, air, liveness); - wasm.zcu_funcs.putAssumeCapacity(func_index, .{ .function = function }); + wasm.zcu_funcs.putAssumeCapacity(func_index, .{ .function = .{ + .instructions_off = mir_instructions_off, + .instructions_len = @intCast(mir.instructions.len), + .extra_off = mir_extra_off, + .extra_len = @intCast(mir.extra.len), + .locals_off = mir_locals_off, + .locals_len = @intCast(mir.locals.len), + .prologue = mir.prologue, + } }); wasm.functions.putAssumeCapacity(.pack(wasm, .{ .zcu_func = @enumFromInt(wasm.zcu_funcs.entries.len - 1) }), {}); - - try zds.finish(wasm, pt); } // Generate code for the "Nav", storing it in memory to be later written to @@ -3988,58 +4066,54 @@ pub fn symbolNameIndex(wasm: *Wasm, name: String) Allocator.Error!SymbolTableInd return @enumFromInt(gop.index); } -pub fn refUavObj(wasm: *Wasm, ip_index: InternPool.Index, orig_ptr_ty: InternPool.Index) !UavsObjIndex { +pub fn addUavReloc( + wasm: *Wasm, + reloc_offset: usize, + uav_val: InternPool.Index, + orig_ptr_ty: InternPool.Index, + addend: u32, +) !void { const comp = wasm.base.comp; const zcu = comp.zcu.?; const ip = &zcu.intern_pool; const gpa = comp.gpa; - assert(comp.config.output_mode == .Obj); - if (orig_ptr_ty != .none) { - const abi_alignment = Zcu.Type.fromInterned(ip.typeOf(ip_index)).abiAlignment(zcu); - const explicit_alignment = ip.indexToKey(orig_ptr_ty).ptr_type.flags.alignment; - if (explicit_alignment.compare(.gt, abi_alignment)) { - const gop = try wasm.overaligned_uavs.getOrPut(gpa, ip_index); - gop.value_ptr.* = if (gop.found_existing) gop.value_ptr.maxStrict(explicit_alignment) else explicit_alignment; - } + @"align": { + const ptr_type = ip.indexToKey(orig_ptr_ty).ptr_type; + const this_align = ptr_type.flags.alignment; + if (this_align == .none) break :@"align"; + const abi_align = Zcu.Type.fromInterned(ptr_type.child).abiAlignment(zcu); + if (this_align.compare(.lte, abi_align)) break :@"align"; + const gop = try wasm.overaligned_uavs.getOrPut(gpa, uav_val); + gop.value_ptr.* = if (gop.found_existing) gop.value_ptr.maxStrict(this_align) else this_align; } - const gop = try wasm.uavs_obj.getOrPut(gpa, ip_index); - if (!gop.found_existing) gop.value_ptr.* = .{ - // Lowering the value is delayed to avoid recursion. - .code = undefined, - .relocs = undefined, - }; - return @enumFromInt(gop.index); -} - -pub fn refUavExe(wasm: *Wasm, ip_index: InternPool.Index, orig_ptr_ty: InternPool.Index) !UavsExeIndex { - const comp = wasm.base.comp; - const zcu = comp.zcu.?; - const ip = &zcu.intern_pool; - const gpa = comp.gpa; - assert(comp.config.output_mode != .Obj); - - if (orig_ptr_ty != .none) { - const abi_alignment = Zcu.Type.fromInterned(ip.typeOf(ip_index)).abiAlignment(zcu); - const explicit_alignment = ip.indexToKey(orig_ptr_ty).ptr_type.flags.alignment; - if (explicit_alignment.compare(.gt, abi_alignment)) { - const gop = try wasm.overaligned_uavs.getOrPut(gpa, ip_index); - gop.value_ptr.* = if (gop.found_existing) gop.value_ptr.maxStrict(explicit_alignment) else explicit_alignment; - } - } - - const gop = try wasm.uavs_exe.getOrPut(gpa, ip_index); - if (gop.found_existing) { - gop.value_ptr.count += 1; + if (comp.config.output_mode == .Obj) { + const gop = try wasm.uavs_obj.getOrPut(gpa, uav_val); + if (!gop.found_existing) gop.value_ptr.* = undefined; // to avoid recursion, `ZcuDataStarts` will lower the value later + try wasm.out_relocs.append(gpa, .{ + .offset = @intCast(reloc_offset), + .pointee = .{ .symbol_index = try wasm.uavSymbolIndex(uav_val) }, + .tag = switch (wasm.pointerSize()) { + 32 => .memory_addr_i32, + 64 => .memory_addr_i64, + else => unreachable, + }, + .addend = @intCast(addend), + }); } else { - gop.value_ptr.* = .{ - // Lowering the value is delayed to avoid recursion. - .code = undefined, - .count = 1, + const gop = try wasm.uavs_exe.getOrPut(gpa, uav_val); + if (!gop.found_existing) gop.value_ptr.* = .{ + .code = undefined, // to avoid recursion, `ZcuDataStarts` will lower the value later + .count = 0, }; + gop.value_ptr.count += 1; + try wasm.uav_fixups.append(gpa, .{ + .uavs_exe_index = @enumFromInt(gop.index), + .offset = @intCast(reloc_offset), + .addend = addend, + }); } - return @enumFromInt(gop.index); } pub fn refNavObj(wasm: *Wasm, nav_index: InternPool.Nav.Index) !NavsObjIndex { @@ -4073,10 +4147,11 @@ pub fn refNavExe(wasm: *Wasm, nav_index: InternPool.Nav.Index) !NavsExeIndex { } /// Asserts it is called after `Flush.data_segments` is fully populated and sorted. -pub fn uavAddr(wasm: *Wasm, uav_index: UavsExeIndex) u32 { +pub fn uavAddr(wasm: *Wasm, ip_index: InternPool.Index) u32 { assert(wasm.flush_buffer.memory_layout_finished); const comp = wasm.base.comp; assert(comp.config.output_mode != .Obj); + const uav_index: UavsExeIndex = @enumFromInt(wasm.uavs_exe.getIndex(ip_index).?); const ds_id: DataSegmentId = .pack(wasm, .{ .uav_exe = uav_index }); return wasm.flush_buffer.data_segments.get(ds_id).?; } diff --git a/src/link/Wasm/Flush.zig b/src/link/Wasm/Flush.zig index 7ed72e851807d279daa6fa37e735b256ff0063dd..60f5971e40afcb39fe0ea219636efbaa10fe32a0 100644 --- a/src/link/Wasm/Flush.zig +++ b/src/link/Wasm/Flush.zig @@ -9,6 +9,7 @@ const Alignment = Wasm.Alignment; const String = Wasm.String; const Relocation = Wasm.Relocation; const InternPool = @import("../../InternPool.zig"); +const Mir = @import("../../arch/wasm/Mir.zig"); const build_options = @import("build_options"); @@ -868,7 +869,21 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { .enum_type => { try emitTagNameFunction(wasm, binary_bytes, f.data_segments.get(.__zig_tag_name_table).?, i.value(wasm).tag_name.table_index, ip_index); }, - else => try i.value(wasm).function.lower(wasm, binary_bytes), + else => { + const func = i.value(wasm).function; + const mir: Mir = .{ + .instructions = wasm.mir_instructions.slice().subslice(func.instructions_off, func.instructions_len), + .extra = wasm.mir_extra.items[func.extra_off..][0..func.extra_len], + .locals = wasm.mir_locals.items[func.locals_off..][0..func.locals_len], + .prologue = func.prologue, + // These fields are unused by `lower`. + .uavs = undefined, + .indirect_function_set = undefined, + .func_tys = undefined, + .error_name_table_ref_count = undefined, + }; + try mir.lower(wasm, binary_bytes); + }, } }, }; diff --git a/src/target.zig b/src/target.zig index 01c6a6cbf01f6ac3b3911efb636a2c3f67f88c06..a408c82c14228ac57510732c8f1547dbc9cec2d0 100644 --- a/src/target.zig +++ b/src/target.zig @@ -851,7 +851,7 @@ pub inline fn backendSupportsFeature(backend: std.builtin.CompilerBackend, compt .separate_thread => switch (backend) { .stage2_llvm => false, // MLUGG TODO - .stage2_c => true, + .stage2_c, .stage2_wasm => true, else => false, }, }; -- 2.54.0 From 89ba8859704486d526a75434f18d5a25ff89d57b Mon Sep 17 00:00:00 2001 From: mlugg Date: Tue, 3 Jun 2025 22:42:10 +0100 Subject: [PATCH 08/35] spirv: make the backend compile again Unfortunately, the self-hosted SPIR-V backend is quite tightly coupled with the self-hosted SPIR-V linker through its `Object` concept (which is much like `llvm.Object`). Reworking this would be too much work for this branch. So, for now, I have introduced a special case (similar to the LLVM backend's special case) to the codegen logic when using this backend. We will want to delete this special case at some point, but it need not block this work. --- src/Zcu/PerThread.zig | 16 ++++++++++++++++ src/codegen/spirv.zig | 6 +++--- src/link.zig | 2 +- src/link/SpirV.zig | 18 ------------------ src/target.zig | 2 +- 5 files changed, 21 insertions(+), 23 deletions(-) diff --git a/src/Zcu/PerThread.zig b/src/Zcu/PerThread.zig index 6475649a681d51665f44cf824acd4dbb129ee1b1..ffc103310b04a713ec39c8f3c832dd23422b4d77 100644 --- a/src/Zcu/PerThread.zig +++ b/src/Zcu/PerThread.zig @@ -4461,6 +4461,22 @@ fn runCodegenInner(pt: Zcu.PerThread, func_index: InternPool.Index, air: *Air) e } const lf = comp.bin_file orelse return error.NoLinkFile; + + // TODO: self-hosted codegen should always have a type of MIR; codegen should produce that MIR, + // and the linker should consume it. However, our SPIR-V backend is currently tightly coupled + // with our SPIR-V linker, so needs to work more like the LLVM backend. This should be fixed to + // unblock threaded codegen for SPIR-V. + if (lf.cast(.spirv)) |spirv_file| { + assert(pt.tid == .main); // SPIR-V has a lot of shared state + spirv_file.object.updateFunc(pt, func_index, air, &liveness) catch |err| { + switch (err) { + error.OutOfMemory => comp.link_diags.setAllocFailure(), + } + return error.CodegenFail; + }; + return error.BackendDoesNotProduceMir; + } + return codegen.generateFunction(lf, pt, zcu.navSrcLoc(nav), func_index, air, &liveness) catch |err| switch (err) { error.OutOfMemory, error.CodegenFail, diff --git a/src/codegen/spirv.zig b/src/codegen/spirv.zig index e6c06d9f20fe167358098c3ce4f297940ebf111d..b9eb56dd23761a37343fd2f9eae71a4d36c1aef5 100644 --- a/src/codegen/spirv.zig +++ b/src/codegen/spirv.zig @@ -250,12 +250,12 @@ pub const Object = struct { self: *Object, pt: Zcu.PerThread, func_index: InternPool.Index, - air: Air, - liveness: Air.Liveness, + air: *const Air, + liveness: *const Air.Liveness, ) !void { const nav = pt.zcu.funcInfo(func_index).owner_nav; // TODO: Separate types for generating decls and functions? - try self.genNav(pt, nav, air, liveness, true); + try self.genNav(pt, nav, air.*, liveness.*, true); } pub fn updateNav( diff --git a/src/link.zig b/src/link.zig index f49acbf3d6114643863a934b3e08f27d35c49bc1..577d7ba82c33bc157a629c32912fcf615ad56389 100644 --- a/src/link.zig +++ b/src/link.zig @@ -758,8 +758,8 @@ pub const File = struct { assert(base.comp.zcu.?.llvm_object == null); switch (base.tag) { .lld => unreachable, + .spirv => unreachable, // see corresponding special case in `Zcu.PerThread.runCodegenInner` inline else => |tag| { - if (tag == .spirv) @panic("MLUGG TODO"); dev.check(tag.devFeature()); return @as(*tag.Type(), @fieldParentPtr("base", base)).updateFunc(pt, func_index, mir, maybe_undef_air); }, diff --git a/src/link/SpirV.zig b/src/link/SpirV.zig index 8b6b99525f03e0d1e24dcbcca3ed01e011dd8880..bafefccfc0e4a5eb78c889399adc8ced64e9ddce 100644 --- a/src/link/SpirV.zig +++ b/src/link/SpirV.zig @@ -111,24 +111,6 @@ pub fn deinit(self: *SpirV) void { self.object.deinit(); } -pub fn updateFunc( - self: *SpirV, - pt: Zcu.PerThread, - func_index: InternPool.Index, - air: Air, - liveness: Air.Liveness, -) link.File.UpdateNavError!void { - if (build_options.skip_non_native) { - @panic("Attempted to compile for architecture that was disabled by build configuration"); - } - - const ip = &pt.zcu.intern_pool; - const func = pt.zcu.funcInfo(func_index); - log.debug("lowering function {}", .{ip.getNav(func.owner_nav).name.fmt(ip)}); - - try self.object.updateFunc(pt, func_index, air, liveness); -} - pub fn updateNav(self: *SpirV, pt: Zcu.PerThread, nav: InternPool.Nav.Index) link.File.UpdateNavError!void { if (build_options.skip_non_native) { @panic("Attempted to compile for architecture that was disabled by build configuration"); diff --git a/src/target.zig b/src/target.zig index a408c82c14228ac57510732c8f1547dbc9cec2d0..02e64670d0b933c7b50069c3e35cae83c995f040 100644 --- a/src/target.zig +++ b/src/target.zig @@ -850,8 +850,8 @@ pub inline fn backendSupportsFeature(backend: std.builtin.CompilerBackend, compt }, .separate_thread => switch (backend) { .stage2_llvm => false, - // MLUGG TODO .stage2_c, .stage2_wasm => true, + // TODO: most self-hosted backends should be able to support this without too much work. else => false, }, }; -- 2.54.0 From 808c15dd397f995d9bdf43664ee5644b39c9c863 Mon Sep 17 00:00:00 2001 From: mlugg Date: Fri, 6 Jun 2025 20:20:06 +0100 Subject: [PATCH 09/35] link.Lld: remove dead caching logic It turns out that LLD caching hasn't been in use for a while. On master, it is currently only enabled when you compile via the build system, passing `-fincremental`, using LLD (and so LLVM if there's a ZCU). That case never happens, because `-fincremental` is only useful when you're using a backend *other* than the LLVM backend. My previous commits accidentally re-enabled this logic in some cases, exposing bugs; that ultimately led to this realisation. So, let's just delete that logic -- less LLVM-related cruft to maintain. --- src/link/Lld.zig | 371 ----------------------------------------------- 1 file changed, 371 deletions(-) diff --git a/src/link/Lld.zig b/src/link/Lld.zig index ba52d0c8d41f6ef7506abbde3c37769917ca7047..3b7b2b6740d31df82514f9aaeda21ea5f808d40e 100644 --- a/src/link/Lld.zig +++ b/src/link/Lld.zig @@ -312,59 +312,8 @@ fn linkAsArchive(lld: *Lld, arena: Allocator) !void { // insight as to what's going on here you can read that function body which is more // well-commented. - const id_symlink_basename = "llvm-ar.id"; - - var man: Cache.Manifest = undefined; - defer if (!lld.disable_caching) man.deinit(); - const link_inputs = comp.link_inputs; - var digest: [Cache.hex_digest_len]u8 = undefined; - - if (!lld.disable_caching) { - man = comp.cache_parent.obtain(); - - // We are about to obtain this lock, so here we give other processes a chance first. - base.releaseLock(); - - try link.hashInputs(&man, link_inputs); - - for (comp.c_object_table.keys()) |key| { - _ = try man.addFilePath(key.status.success.object_path, null); - } - for (comp.win32_resource_table.keys()) |key| { - _ = try man.addFile(key.status.success.res_path, null); - } - try man.addOptionalFile(zcu_obj_path); - try man.addOptionalFilePath(compiler_rt_path); - try man.addOptionalFilePath(ubsan_rt_path); - - // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock. - _ = try man.hit(); - digest = man.final(); - - var prev_digest_buf: [digest.len]u8 = undefined; - const prev_digest: []u8 = Cache.readSmallFile( - directory.handle, - id_symlink_basename, - &prev_digest_buf, - ) catch |err| b: { - log.debug("archive new_digest={s} readFile error: {s}", .{ std.fmt.fmtSliceHexLower(&digest), @errorName(err) }); - break :b prev_digest_buf[0..0]; - }; - if (mem.eql(u8, prev_digest, &digest)) { - log.debug("archive digest={s} match - skipping invocation", .{std.fmt.fmtSliceHexLower(&digest)}); - base.lock = man.toOwnedLock(); - return; - } - - // We are about to change the output file to be different, so we invalidate the build hash now. - directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) { - error.FileNotFound => {}, - else => |e| return e, - }; - } - var object_files: std.ArrayListUnmanaged([*:0]const u8) = .empty; try object_files.ensureUnusedCapacity(arena, link_inputs.len); @@ -408,20 +357,6 @@ fn linkAsArchive(lld: *Lld, arena: Allocator) !void { }, ); if (bad) return error.UnableToWriteArchive; - - if (!lld.disable_caching) { - Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| { - log.warn("failed to save archive hash digest file: {s}", .{@errorName(err)}); - }; - - if (man.have_exclusive_lock) { - man.writeManifest() catch |err| { - log.warn("failed to write cache manifest when archiving: {s}", .{@errorName(err)}); - }; - } - - base.lock = man.toOwnedLock(); - } } fn coffLink(lld: *Lld, arena: Allocator) !void { @@ -457,90 +392,6 @@ fn coffLink(lld: *Lld, arena: Allocator) !void { .named => |name| name, }; - // See link/Elf.zig for comments on how this mechanism works. - const id_symlink_basename = "lld.id"; - - var man: Cache.Manifest = undefined; - defer if (!lld.disable_caching) man.deinit(); - - var digest: [Cache.hex_digest_len]u8 = undefined; - - if (!lld.disable_caching) { - man = comp.cache_parent.obtain(); - base.releaseLock(); - - comptime assert(Compilation.link_hash_implementation_version == 14); - - try link.hashInputs(&man, comp.link_inputs); - for (comp.c_object_table.keys()) |key| { - _ = try man.addFilePath(key.status.success.object_path, null); - } - for (comp.win32_resource_table.keys()) |key| { - _ = try man.addFile(key.status.success.res_path, null); - } - try man.addOptionalFile(module_obj_path); - man.hash.addOptionalBytes(entry_name); - man.hash.add(base.stack_size); - man.hash.add(coff.image_base); - man.hash.add(base.build_id); - { - // TODO remove this, libraries must instead be resolved by the frontend. - for (coff.lib_directories) |lib_directory| man.hash.addOptionalBytes(lib_directory.path); - } - man.hash.add(comp.skip_linker_dependencies); - if (comp.config.link_libc) { - man.hash.add(comp.libc_installation != null); - if (comp.libc_installation) |libc_installation| { - man.hash.addBytes(libc_installation.crt_dir.?); - if (target.abi == .msvc or target.abi == .itanium) { - man.hash.addBytes(libc_installation.msvc_lib_dir.?); - man.hash.addBytes(libc_installation.kernel32_lib_dir.?); - } - } - } - man.hash.addListOfBytes(comp.windows_libs.keys()); - man.hash.addListOfBytes(comp.force_undefined_symbols.keys()); - man.hash.addOptional(coff.subsystem); - man.hash.add(comp.config.is_test); - man.hash.add(coff.tsaware); - man.hash.add(coff.nxcompat); - man.hash.add(coff.dynamicbase); - man.hash.add(base.allow_shlib_undefined); - // strip does not need to go into the linker hash because it is part of the hash namespace - man.hash.add(coff.major_subsystem_version); - man.hash.add(coff.minor_subsystem_version); - man.hash.add(coff.repro); - man.hash.addOptional(comp.version); - try man.addOptionalFile(coff.module_definition_file); - - // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock. - _ = try man.hit(); - digest = man.final(); - var prev_digest_buf: [digest.len]u8 = undefined; - const prev_digest: []u8 = Cache.readSmallFile( - directory.handle, - id_symlink_basename, - &prev_digest_buf, - ) catch |err| blk: { - log.debug("COFF LLD new_digest={s} error: {s}", .{ std.fmt.fmtSliceHexLower(&digest), @errorName(err) }); - // Handle this as a cache miss. - break :blk prev_digest_buf[0..0]; - }; - if (mem.eql(u8, prev_digest, &digest)) { - log.debug("COFF LLD digest={s} match - skipping invocation", .{std.fmt.fmtSliceHexLower(&digest)}); - // Hot diggity dog! The output binary is already there. - base.lock = man.toOwnedLock(); - return; - } - log.debug("COFF LLD prev_digest={s} new_digest={s}", .{ std.fmt.fmtSliceHexLower(prev_digest), std.fmt.fmtSliceHexLower(&digest) }); - - // We are about to change the output file to be different, so we invalidate the build hash now. - directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) { - error.FileNotFound => {}, - else => |e| return e, - }; - } - if (comp.config.output_mode == .Obj) { // LLD's COFF driver does not support the equivalent of `-r` so we do a simple file copy // here. TODO: think carefully about how we can avoid this redundant operation when doing @@ -935,21 +786,6 @@ fn coffLink(lld: *Lld, arena: Allocator) !void { try spawnLld(comp, arena, argv.items); } - - if (!lld.disable_caching) { - // Update the file with the digest. If it fails we can continue; it only - // means that the next invocation will have an unnecessary cache miss. - Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| { - log.warn("failed to save linking hash digest file: {s}", .{@errorName(err)}); - }; - // Again failure here only means an unnecessary cache miss. - man.writeManifest() catch |err| { - log.warn("failed to write cache manifest when linking: {s}", .{@errorName(err)}); - }; - // We hang on to this lock so that the output file path can be used without - // other processes clobbering it. - base.lock = man.toOwnedLock(); - } } fn findLib(arena: Allocator, name: []const u8, lib_directories: []const Cache.Directory) !?[]const u8 { for (lib_directories) |lib_directory| { @@ -1001,118 +837,6 @@ fn elfLink(lld: *Lld, arena: Allocator) !void { break :blk null; }; - // Here we want to determine whether we can save time by not invoking LLD when the - // output is unchanged. None of the linker options or the object files that are being - // linked are in the hash that namespaces the directory we are outputting to. Therefore, - // we must hash those now, and the resulting digest will form the "id" of the linking - // job we are about to perform. - // After a successful link, we store the id in the metadata of a symlink named "lld.id" in - // the artifact directory. So, now, we check if this symlink exists, and if it matches - // our digest. If so, we can skip linking. Otherwise, we proceed with invoking LLD. - const id_symlink_basename = "lld.id"; - - var man: std.Build.Cache.Manifest = undefined; - defer if (!lld.disable_caching) man.deinit(); - - var digest: [std.Build.Cache.hex_digest_len]u8 = undefined; - - if (!lld.disable_caching) { - man = comp.cache_parent.obtain(); - - // We are about to obtain this lock, so here we give other processes a chance first. - base.releaseLock(); - - comptime assert(Compilation.link_hash_implementation_version == 14); - - try man.addOptionalFile(elf.linker_script); - try man.addOptionalFile(elf.version_script); - man.hash.add(elf.allow_undefined_version); - man.hash.addOptional(elf.enable_new_dtags); - try link.hashInputs(&man, comp.link_inputs); - for (comp.c_object_table.keys()) |key| { - _ = try man.addFilePath(key.status.success.object_path, null); - } - try man.addOptionalFile(module_obj_path); - try man.addOptionalFilePath(compiler_rt_path); - try man.addOptionalFilePath(ubsan_rt_path); - try man.addOptionalFilePath(if (comp.tsan_lib) |l| l.full_object_path else null); - try man.addOptionalFilePath(if (comp.fuzzer_lib) |l| l.full_object_path else null); - - // We can skip hashing libc and libc++ components that we are in charge of building from Zig - // installation sources because they are always a product of the compiler version + target information. - man.hash.addOptionalBytes(elf.entry_name); - man.hash.add(elf.image_base); - man.hash.add(base.gc_sections); - man.hash.addOptional(elf.sort_section); - man.hash.add(comp.link_eh_frame_hdr); - man.hash.add(elf.emit_relocs); - man.hash.add(comp.config.rdynamic); - man.hash.addListOfBytes(elf.rpath_list); - if (output_mode == .Exe) { - man.hash.add(base.stack_size); - } - man.hash.add(base.build_id); - man.hash.addListOfBytes(elf.symbol_wrap_set); - man.hash.add(comp.skip_linker_dependencies); - man.hash.add(elf.z_nodelete); - man.hash.add(elf.z_notext); - man.hash.add(elf.z_defs); - man.hash.add(elf.z_origin); - man.hash.add(elf.z_nocopyreloc); - man.hash.add(elf.z_now); - man.hash.add(elf.z_relro); - man.hash.add(elf.z_common_page_size orelse 0); - man.hash.add(elf.z_max_page_size orelse 0); - man.hash.add(elf.hash_style); - // strip does not need to go into the linker hash because it is part of the hash namespace - if (comp.config.link_libc) { - man.hash.add(comp.libc_installation != null); - if (comp.libc_installation) |libc_installation| { - man.hash.addBytes(libc_installation.crt_dir.?); - } - } - if (have_dynamic_linker) { - man.hash.addOptionalBytes(target.dynamic_linker.get()); - } - man.hash.addOptionalBytes(elf.soname); - man.hash.addOptional(comp.version); - man.hash.addListOfBytes(comp.force_undefined_symbols.keys()); - man.hash.add(base.allow_shlib_undefined); - man.hash.add(elf.bind_global_refs_locally); - man.hash.add(elf.compress_debug_sections); - man.hash.add(comp.config.any_sanitize_thread); - man.hash.add(comp.config.any_fuzz); - man.hash.addOptionalBytes(comp.sysroot); - - // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock. - _ = try man.hit(); - digest = man.final(); - - var prev_digest_buf: [digest.len]u8 = undefined; - const prev_digest: []u8 = std.Build.Cache.readSmallFile( - directory.handle, - id_symlink_basename, - &prev_digest_buf, - ) catch |err| blk: { - log.debug("ELF LLD new_digest={s} error: {s}", .{ std.fmt.fmtSliceHexLower(&digest), @errorName(err) }); - // Handle this as a cache miss. - break :blk prev_digest_buf[0..0]; - }; - if (mem.eql(u8, prev_digest, &digest)) { - log.debug("ELF LLD digest={s} match - skipping invocation", .{std.fmt.fmtSliceHexLower(&digest)}); - // Hot diggity dog! The output binary is already there. - base.lock = man.toOwnedLock(); - return; - } - log.debug("ELF LLD prev_digest={s} new_digest={s}", .{ std.fmt.fmtSliceHexLower(prev_digest), std.fmt.fmtSliceHexLower(&digest) }); - - // We are about to change the output file to be different, so we invalidate the build hash now. - directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) { - error.FileNotFound => {}, - else => |e| return e, - }; - } - // Due to a deficiency in LLD, we need to special-case BPF to a simple file // copy when generating relocatables. Normally, we would expect `lld -r` to work. // However, because LLD wants to resolve BPF relocations which it shouldn't, it fails @@ -1570,21 +1294,6 @@ fn elfLink(lld: *Lld, arena: Allocator) !void { try spawnLld(comp, arena, argv.items); } - - if (!lld.disable_caching) { - // Update the file with the digest. If it fails we can continue; it only - // means that the next invocation will have an unnecessary cache miss. - std.Build.Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| { - log.warn("failed to save linking hash digest file: {s}", .{@errorName(err)}); - }; - // Again failure here only means an unnecessary cache miss. - man.writeManifest() catch |err| { - log.warn("failed to write cache manifest when linking: {s}", .{@errorName(err)}); - }; - // We hang on to this lock so that the output file path can be used without - // other processes clobbering it. - base.lock = man.toOwnedLock(); - } } fn getLDMOption(target: std.Target) ?[]const u8 { // This should only return emulations understood by LLD's parseEmulation(). @@ -1700,71 +1409,6 @@ fn wasmLink(lld: *Lld, arena: Allocator) !void { break :blk null; }; - const id_symlink_basename = "lld.id"; - - var man: Cache.Manifest = undefined; - defer if (!lld.disable_caching) man.deinit(); - - var digest: [Cache.hex_digest_len]u8 = undefined; - - if (!lld.disable_caching) { - man = comp.cache_parent.obtain(); - - // We are about to obtain this lock, so here we give other processes a chance first. - base.releaseLock(); - - comptime assert(Compilation.link_hash_implementation_version == 14); - - try link.hashInputs(&man, comp.link_inputs); - for (comp.c_object_table.keys()) |key| { - _ = try man.addFilePath(key.status.success.object_path, null); - } - try man.addOptionalFile(module_obj_path); - try man.addOptionalFilePath(compiler_rt_path); - try man.addOptionalFilePath(ubsan_rt_path); - man.hash.addOptionalBytes(wasm.entry_name); - man.hash.add(base.stack_size); - man.hash.add(base.build_id); - man.hash.add(import_memory); - man.hash.add(export_memory); - man.hash.add(wasm.import_table); - man.hash.add(wasm.export_table); - man.hash.addOptional(wasm.initial_memory); - man.hash.addOptional(wasm.max_memory); - man.hash.add(shared_memory); - man.hash.addOptional(wasm.global_base); - man.hash.addListOfBytes(wasm.export_symbol_names); - // strip does not need to go into the linker hash because it is part of the hash namespace - - // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock. - _ = try man.hit(); - digest = man.final(); - - var prev_digest_buf: [digest.len]u8 = undefined; - const prev_digest: []u8 = Cache.readSmallFile( - directory.handle, - id_symlink_basename, - &prev_digest_buf, - ) catch |err| blk: { - log.debug("WASM LLD new_digest={s} error: {s}", .{ std.fmt.fmtSliceHexLower(&digest), @errorName(err) }); - // Handle this as a cache miss. - break :blk prev_digest_buf[0..0]; - }; - if (mem.eql(u8, prev_digest, &digest)) { - log.debug("WASM LLD digest={s} match - skipping invocation", .{std.fmt.fmtSliceHexLower(&digest)}); - // Hot diggity dog! The output binary is already there. - base.lock = man.toOwnedLock(); - return; - } - log.debug("WASM LLD prev_digest={s} new_digest={s}", .{ std.fmt.fmtSliceHexLower(prev_digest), std.fmt.fmtSliceHexLower(&digest) }); - - // We are about to change the output file to be different, so we invalidate the build hash now. - directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) { - error.FileNotFound => {}, - else => |e| return e, - }; - } - if (is_obj) { // LLD's WASM driver does not support the equivalent of `-r` so we do a simple file copy // here. TODO: think carefully about how we can avoid this redundant operation when doing @@ -1998,21 +1642,6 @@ fn wasmLink(lld: *Lld, arena: Allocator) !void { }; } } - - if (!lld.disable_caching) { - // Update the file with the digest. If it fails we can continue; it only - // means that the next invocation will have an unnecessary cache miss. - Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| { - log.warn("failed to save linking hash digest symlink: {s}", .{@errorName(err)}); - }; - // Again failure here only means an unnecessary cache miss. - man.writeManifest() catch |err| { - log.warn("failed to write cache manifest when linking: {s}", .{@errorName(err)}); - }; - // We hang on to this lock so that the output file path can be used without - // other processes clobbering it. - base.lock = man.toOwnedLock(); - } } fn spawnLld( -- 2.54.0 From b5f73f8a7b90c5144b79692f142b5d91025dbe01 Mon Sep 17 00:00:00 2001 From: mlugg Date: Fri, 6 Jun 2025 20:16:26 +0100 Subject: [PATCH 10/35] compiler: rework emit paths and cache modes Previously, various doc comments heavily disagreed with the implementation on both what lives where on the filesystem at what time, and how that was represented in code. Notably, the combination of emit paths outside the cache and `disable_lld_caching` created a kind of ad-hoc "cache disable" mechanism -- which didn't actually *work* very well, 'most everything still ended up in this cache. There was also a long-standing issue where building using the LLVM backend would put a random object file in your cwd. This commit reworks how emit paths are specified in `Compilation.CreateOptions`, how they are represented internally, and how the cache usage is specified. There are now 3 options for `Compilation.CacheMode`: * `.none`: do not use the cache. The paths we have to emit to are relative to the compiler cwd (they're either user-specified, or defaults inferred from the root name). If we create any temporary files (e.g. the ZCU object when using the LLVM backend) they are emitted to a directory in `local_cache/tmp/`, which is deleted once the update finishes. * `.whole`: cache the compilation based on all inputs, including file contents. All emit paths are computed by the compiler (and will be stored as relative to the local cache directory); it is a CLI error to specify an explicit emit path. Artifacts (including temporary files) are written to a directory under `local_cache/tmp/`, which is later renamed to an appropriate `local_cache/o/`. The caller (who is using `--listen`; e.g. the build system) learns the name of this directory, and can get the artifacts from it. * `.incremental`: similar to `.whole`, but Zig source file contents, and anything else which incremental compilation can handle changes for, is not included in the cache manifest. We don't need to do the dance where the output directory is initially in `tmp/`, because our digest is computed entirely from CLI inputs. To be clear, the difference between `CacheMode.whole` and `CacheMode.incremental` is unchanged. `CacheMode.none` is new (previously it was sort of poorly imitated with `CacheMode.whole`). The defined behavior for temporary/intermediate files is new. `.none` is used for direct CLI invocations like `zig build-exe foo.zig`. The other cache modes are reserved for `--listen`, and the cache mode in use is currently just based on the presence of the `-fincremental` flag. There are two cases in which `CacheMode.whole` is used despite there being no `--listen` flag: `zig test` and `zig run`. Unless an explicit `-femit-bin=xxx` argument is passed on the CLI, these subcommands will use `CacheMode.whole`, so that they can put the output somewhere without polluting the cwd (plus, caching is potentially more useful for direct usage of these subcommands). Users of `--listen` (such as the build system) can now use `std.zig.EmitArtifact.cacheName` to find out what an output will be named. This avoids having to synchronize logic between the compiler and all users of `--listen`. --- lib/std/Build/Step/Compile.zig | 66 ++- lib/std/zig.zig | 29 ++ src/Compilation.zig | 720 +++++++++++++++++---------------- src/Zcu/PerThread.zig | 4 +- src/libs/freebsd.zig | 7 +- src/libs/glibc.zig | 7 +- src/libs/libcxx.zig | 28 +- src/libs/libtsan.zig | 8 +- src/libs/libunwind.zig | 15 +- src/libs/musl.zig | 3 +- src/libs/netbsd.zig | 7 +- src/link.zig | 36 +- src/link/Coff.zig | 13 +- src/link/Elf.zig | 23 +- src/link/Goff.zig | 2 +- src/link/Lld.zig | 75 ++-- src/link/MachO.zig | 40 +- src/link/Wasm.zig | 25 +- src/link/Xcoff.zig | 2 +- src/main.zig | 355 +++++----------- tools/incr-check.zig | 2 +- 21 files changed, 625 insertions(+), 842 deletions(-) diff --git a/lib/std/Build/Step/Compile.zig b/lib/std/Build/Step/Compile.zig index dd8c4158ce1700a38fa0077474220cde7966d8d9..924dc18f91bed585d891c220541a15c59a31c11c 100644 --- a/lib/std/Build/Step/Compile.zig +++ b/lib/std/Build/Step/Compile.zig @@ -1834,47 +1834,16 @@ fn make(step: *Step, options: Step.MakeOptions) !void { lp.path = b.fmt("{}", .{output_dir}); } - // -femit-bin[=path] (default) Output machine code - if (compile.generated_bin) |bin| { - bin.path = output_dir.joinString(b.allocator, compile.out_filename) catch @panic("OOM"); - } - - const sep = std.fs.path.sep_str; - - // output PDB if someone requested it - if (compile.generated_pdb) |pdb| { - pdb.path = b.fmt("{}" ++ sep ++ "{s}.pdb", .{ output_dir, compile.name }); - } - - // -femit-implib[=path] (default) Produce an import .lib when building a Windows DLL - if (compile.generated_implib) |implib| { - implib.path = b.fmt("{}" ++ sep ++ "{s}.lib", .{ output_dir, compile.name }); - } - - // -femit-h[=path] Generate a C header file (.h) - if (compile.generated_h) |lp| { - lp.path = b.fmt("{}" ++ sep ++ "{s}.h", .{ output_dir, compile.name }); - } - - // -femit-docs[=path] Create a docs/ dir with html documentation - if (compile.generated_docs) |generated_docs| { - generated_docs.path = output_dir.joinString(b.allocator, "docs") catch @panic("OOM"); - } - - // -femit-asm[=path] Output .s (assembly code) - if (compile.generated_asm) |lp| { - lp.path = b.fmt("{}" ++ sep ++ "{s}.s", .{ output_dir, compile.name }); - } - - // -femit-llvm-ir[=path] Produce a .ll file with optimized LLVM IR (requires LLVM extensions) - if (compile.generated_llvm_ir) |lp| { - lp.path = b.fmt("{}" ++ sep ++ "{s}.ll", .{ output_dir, compile.name }); - } - - // -femit-llvm-bc[=path] Produce an optimized LLVM module as a .bc file (requires LLVM extensions) - if (compile.generated_llvm_bc) |lp| { - lp.path = b.fmt("{}" ++ sep ++ "{s}.bc", .{ output_dir, compile.name }); - } + // zig fmt: off + if (compile.generated_bin) |lp| lp.path = compile.outputPath(output_dir, .bin); + if (compile.generated_pdb) |lp| lp.path = compile.outputPath(output_dir, .pdb); + if (compile.generated_implib) |lp| lp.path = compile.outputPath(output_dir, .implib); + if (compile.generated_h) |lp| lp.path = compile.outputPath(output_dir, .h); + if (compile.generated_docs) |lp| lp.path = compile.outputPath(output_dir, .docs); + if (compile.generated_asm) |lp| lp.path = compile.outputPath(output_dir, .@"asm"); + if (compile.generated_llvm_ir) |lp| lp.path = compile.outputPath(output_dir, .llvm_ir); + if (compile.generated_llvm_bc) |lp| lp.path = compile.outputPath(output_dir, .llvm_bc); + // zig fmt: on } if (compile.kind == .lib and compile.linkage != null and compile.linkage.? == .dynamic and @@ -1888,6 +1857,21 @@ fn make(step: *Step, options: Step.MakeOptions) !void { ); } } +fn outputPath(c: *Compile, out_dir: std.Build.Cache.Path, ea: std.zig.EmitArtifact) []const u8 { + const arena = c.step.owner.graph.arena; + const name = ea.cacheName(arena, .{ + .root_name = c.name, + .target = c.root_module.resolved_target.?.result, + .output_mode = switch (c.kind) { + .lib => .Lib, + .obj, .test_obj => .Obj, + .exe, .@"test" => .Exe, + }, + .link_mode = c.linkage, + .version = c.version, + }) catch @panic("OOM"); + return out_dir.joinString(arena, name) catch @panic("OOM"); +} pub fn rebuildInFuzzMode(c: *Compile, progress_node: std.Progress.Node) !Path { const gpa = c.step.owner.allocator; diff --git a/lib/std/zig.zig b/lib/std/zig.zig index a946fe5e8bc7f506f65589f5ae01287c3d2b08a3..21f129676623ab61e3299456df1ecdfa3a67f20c 100644 --- a/lib/std/zig.zig +++ b/lib/std/zig.zig @@ -884,6 +884,35 @@ pub const SimpleComptimeReason = enum(u32) { } }; +/// Every kind of artifact which the compiler can emit. +pub const EmitArtifact = enum { + bin, + @"asm", + implib, + llvm_ir, + llvm_bc, + docs, + pdb, + h, + + /// If using `Server` to communicate with the compiler, it will place requested artifacts in + /// paths under the output directory, where those paths are named according to this function. + /// Returned string is allocated with `gpa` and owned by the caller. + pub fn cacheName(ea: EmitArtifact, gpa: Allocator, opts: BinNameOptions) Allocator.Error![]const u8 { + const suffix: []const u8 = switch (ea) { + .bin => return binNameAlloc(gpa, opts), + .@"asm" => ".s", + .implib => ".lib", + .llvm_ir => ".ll", + .llvm_bc => ".bc", + .docs => "-docs", + .pdb => ".pdb", + .h => ".h", + }; + return std.fmt.allocPrint(gpa, "{s}{s}", .{ opts.root_name, suffix }); + } +}; + test { _ = Ast; _ = AstRlAnnotate; diff --git a/src/Compilation.zig b/src/Compilation.zig index 0342566e27dc61bab505a3263d359417f5062476..b9b51222eb4892514d686706151c5baea9b3505c 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -55,8 +55,7 @@ gpa: Allocator, arena: Allocator, /// Not every Compilation compiles .zig code! For example you could do `zig build-exe foo.o`. zcu: ?*Zcu, -/// Contains different state depending on whether the Compilation uses -/// incremental or whole cache mode. +/// Contains different state depending on the `CacheMode` used by this `Compilation`. cache_use: CacheUse, /// All compilations have a root module because this is where some important /// settings are stored, such as target and optimization mode. This module @@ -67,17 +66,13 @@ root_mod: *Package.Module, config: Config, /// The main output file. -/// In whole cache mode, this is null except for during the body of the update -/// function. In incremental cache mode, this is a long-lived object. -/// In both cases, this is `null` when `-fno-emit-bin` is used. +/// In `CacheMode.whole`, this is null except for during the body of `update`. +/// In `CacheMode.none` and `CacheMode.incremental`, this is long-lived. +/// Regardless of cache mode, this is `null` when `-fno-emit-bin` is used. bin_file: ?*link.File, /// The root path for the dynamic linker and system libraries (as well as frameworks on Darwin) sysroot: ?[]const u8, -/// This is `null` when not building a Windows DLL, or when `-fno-emit-implib` is used. -implib_emit: ?Cache.Path, -/// This is non-null when `-femit-docs` is provided. -docs_emit: ?Cache.Path, root_name: [:0]const u8, compiler_rt_strat: RtStrat, ubsan_rt_strat: RtStrat, @@ -259,10 +254,6 @@ mutex: if (builtin.single_threaded) struct { test_filters: []const []const u8, test_name_prefix: ?[]const u8, -emit_asm: ?EmitLoc, -emit_llvm_ir: ?EmitLoc, -emit_llvm_bc: ?EmitLoc, - link_task_wait_group: WaitGroup = .{}, work_queue_progress_node: std.Progress.Node = .none, @@ -274,6 +265,31 @@ file_system_inputs: ?*std.ArrayListUnmanaged(u8), /// This digest will be known after update() is called. digest: ?[Cache.bin_digest_len]u8 = null, +/// Non-`null` iff we are emitting a binary. +/// Does not change for the lifetime of this `Compilation`. +/// Cwd-relative if `cache_use == .none`. Otherwise, relative to our subdirectory in the cache. +emit_bin: ?[]const u8, +/// Non-`null` iff we are emitting assembly. +/// Does not change for the lifetime of this `Compilation`. +/// Cwd-relative if `cache_use == .none`. Otherwise, relative to our subdirectory in the cache. +emit_asm: ?[]const u8, +/// Non-`null` iff we are emitting an implib. +/// Does not change for the lifetime of this `Compilation`. +/// Cwd-relative if `cache_use == .none`. Otherwise, relative to our subdirectory in the cache. +emit_implib: ?[]const u8, +/// Non-`null` iff we are emitting LLVM IR. +/// Does not change for the lifetime of this `Compilation`. +/// Cwd-relative if `cache_use == .none`. Otherwise, relative to our subdirectory in the cache. +emit_llvm_ir: ?[]const u8, +/// Non-`null` iff we are emitting LLVM bitcode. +/// Does not change for the lifetime of this `Compilation`. +/// Cwd-relative if `cache_use == .none`. Otherwise, relative to our subdirectory in the cache. +emit_llvm_bc: ?[]const u8, +/// Non-`null` iff we are emitting documentation. +/// Does not change for the lifetime of this `Compilation`. +/// Cwd-relative if `cache_use == .none`. Otherwise, relative to our subdirectory in the cache. +emit_docs: ?[]const u8, + const QueuedJobs = struct { compiler_rt_lib: bool = false, compiler_rt_obj: bool = false, @@ -774,13 +790,6 @@ pub const CrtFile = struct { lock: Cache.Lock, full_object_path: Cache.Path, - pub fn isObject(cf: CrtFile) bool { - return switch (classifyFileExt(cf.full_object_path.sub_path)) { - .object => true, - else => false, - }; - } - pub fn deinit(self: *CrtFile, gpa: Allocator) void { self.lock.release(); gpa.free(self.full_object_path.sub_path); @@ -1321,14 +1330,6 @@ pub const MiscError = struct { } }; -pub const EmitLoc = struct { - /// If this is `null` it means the file will be output to the cache directory. - /// When provided, both the open file handle and the path name must outlive the `Compilation`. - directory: ?Cache.Directory, - /// This may not have sub-directories in it. - basename: []const u8, -}; - pub const cache_helpers = struct { pub fn addModule(hh: *Cache.HashHelper, mod: *const Package.Module) void { addResolvedTarget(hh, mod.resolved_target); @@ -1368,15 +1369,6 @@ pub const cache_helpers = struct { hh.add(resolved_target.is_explicit_dynamic_linker); } - pub fn addEmitLoc(hh: *Cache.HashHelper, emit_loc: EmitLoc) void { - hh.addBytes(emit_loc.basename); - } - - pub fn addOptionalEmitLoc(hh: *Cache.HashHelper, optional_emit_loc: ?EmitLoc) void { - hh.add(optional_emit_loc != null); - addEmitLoc(hh, optional_emit_loc orelse return); - } - pub fn addOptionalDebugFormat(hh: *Cache.HashHelper, x: ?Config.DebugFormat) void { hh.add(x != null); addDebugFormat(hh, x orelse return); @@ -1423,7 +1415,38 @@ pub const ClangPreprocessorMode = enum { pub const Framework = link.File.MachO.Framework; pub const SystemLib = link.SystemLib; -pub const CacheMode = enum { incremental, whole }; +pub const CacheMode = enum { + /// The results of this compilation are not cached. The compilation is always performed, and the + /// results are emitted directly to their output locations. Temporary files will be placed in a + /// temporary directory in the cache, but deleted after the compilation is done. + /// + /// This mode is typically used for direct CLI invocations like `zig build-exe`, because such + /// processes are typically low-level usages which would not make efficient use of the cache. + none, + /// The compilation is cached based only on the options given when creating the `Compilation`. + /// In particular, Zig source file contents are not included in the cache manifest. This mode + /// allows incremental compilation, because the old cached compilation state can be restored + /// and the old binary patched up with the changes. All files, including temporary files, are + /// stored in the cache directory like '/o//'. Temporary files are not deleted. + /// + /// At the time of writing, incremental compilation is only supported with the `-fincremental` + /// command line flag, so this mode is rarely used. However, it is required in order to use + /// incremental compilation. + incremental, + /// The compilation is cached based on the `Compilation` options and every input, including Zig + /// source files, linker inputs, and `@embedFile` targets. If any of them change, we will see a + /// cache miss, and the entire compilation will be re-run. On a cache miss, we initially write + /// all output files to a directory under '/tmp/', because we don't know the final + /// manifest digest until the update is almost done. Once we can compute the final digest, this + /// directory is moved to '/o//'. Temporary files are not deleted. + /// + /// At the time of writing, this is the most commonly used cache mode: it is used by the build + /// system (and any other parent using `--listen`) unless incremental compilation is enabled. + /// Once incremental compilation is more mature, it will be replaced by `incremental` in many + /// cases, but still has use cases, such as for release binaries, particularly globally cached + /// artifacts like compiler_rt. + whole, +}; pub const ParentWholeCache = struct { manifest: *Cache.Manifest, @@ -1432,22 +1455,33 @@ pub const ParentWholeCache = struct { }; const CacheUse = union(CacheMode) { + none: *None, incremental: *Incremental, whole: *Whole, + const None = struct { + /// User-requested artifacts are written directly to their output path in this cache mode. + /// However, if we need to emit any temporary files, they are placed in this directory. + /// We will recursively delete this directory at the end of this update. This field is + /// non-`null` only inside `update`. + tmp_artifact_directory: ?Cache.Directory, + }; + + const Incremental = struct { + /// All output files, including artifacts and incremental compilation metadata, are placed + /// in this directory, which is some 'o/' in a cache directory. + artifact_directory: Cache.Directory, + }; + const Whole = struct { - /// This is a pointer to a local variable inside `update()`. - cache_manifest: ?*Cache.Manifest = null, - cache_manifest_mutex: std.Thread.Mutex = .{}, - /// null means -fno-emit-bin. - /// This is mutable memory allocated into the Compilation-lifetime arena (`arena`) - /// of exactly the correct size for "o/[digest]/[basename]". - /// The basename is of the outputted binary file in case we don't know the directory yet. - bin_sub_path: ?[]u8, - /// Same as `bin_sub_path` but for implibs. - implib_sub_path: ?[]u8, - docs_sub_path: ?[]u8, + /// Since we don't open the output file until `update`, we must save these options for then. lf_open_opts: link.File.OpenOptions, + /// This is a pointer to a local variable inside `update`. + cache_manifest: ?*Cache.Manifest, + cache_manifest_mutex: std.Thread.Mutex, + /// This is non-`null` for most of the body of `update`. It is the temporary directory which + /// we initially emit our artifacts to. After the main part of the update is done, it will + /// be closed and moved to its final location, and this field set to `null`. tmp_artifact_directory: ?Cache.Directory, /// Prevents other processes from clobbering files in the output directory. lock: ?Cache.Lock, @@ -1466,17 +1500,16 @@ const CacheUse = union(CacheMode) { } }; - const Incremental = struct { - /// Where build artifacts and incremental compilation metadata serialization go. - artifact_directory: Cache.Directory, - }; - fn deinit(cu: CacheUse) void { switch (cu) { + .none => |none| { + assert(none.tmp_artifact_directory == null); + }, .incremental => |incremental| { incremental.artifact_directory.handle.close(); }, .whole => |whole| { + assert(whole.tmp_artifact_directory == null); whole.releaseLock(); }, } @@ -1503,28 +1536,14 @@ pub const CreateOptions = struct { std_mod: ?*Package.Module = null, root_name: []const u8, sysroot: ?[]const u8 = null, - /// `null` means to not emit a binary file. - emit_bin: ?EmitLoc, - /// `null` means to not emit a C header file. - emit_h: ?EmitLoc = null, - /// `null` means to not emit assembly. - emit_asm: ?EmitLoc = null, - /// `null` means to not emit LLVM IR. - emit_llvm_ir: ?EmitLoc = null, - /// `null` means to not emit LLVM module bitcode. - emit_llvm_bc: ?EmitLoc = null, - /// `null` means to not emit docs. - emit_docs: ?EmitLoc = null, - /// `null` means to not emit an import lib. - emit_implib: ?EmitLoc = null, - /// Normally when using LLD to link, Zig uses a file named "lld.id" in the - /// same directory as the output binary which contains the hash of the link - /// operation, allowing Zig to skip linking when the hash would be unchanged. - /// In the case that the output binary is being emitted into a directory which - /// is externally modified - essentially anything other than zig-cache - then - /// this flag would be set to disable this machinery to avoid false positives. - disable_lld_caching: bool = false, - cache_mode: CacheMode = .incremental, + cache_mode: CacheMode, + emit_h: Emit = .no, + emit_bin: Emit, + emit_asm: Emit = .no, + emit_implib: Emit = .no, + emit_llvm_ir: Emit = .no, + emit_llvm_bc: Emit = .no, + emit_docs: Emit = .no, /// This field is intended to be removed. /// The ELF implementation no longer uses this data, however the MachO and COFF /// implementations still do. @@ -1662,6 +1681,38 @@ pub const CreateOptions = struct { parent_whole_cache: ?ParentWholeCache = null, pub const Entry = link.File.OpenOptions.Entry; + + /// Which fields are valid depends on the `cache_mode` given. + pub const Emit = union(enum) { + /// Do not emit this file. Always valid. + no, + /// Emit this file into its default name in the cache directory. + /// Requires `cache_mode` to not be `.none`. + yes_cache, + /// Emit this file to the given path (absolute or cwd-relative). + /// Requires `cache_mode` to be `.none`. + yes_path: []const u8, + + fn resolve(emit: Emit, arena: Allocator, opts: *const CreateOptions, ea: std.zig.EmitArtifact) Allocator.Error!?[]const u8 { + switch (emit) { + .no => return null, + .yes_cache => { + assert(opts.cache_mode != .none); + return try ea.cacheName(arena, .{ + .root_name = opts.root_name, + .target = opts.root_mod.resolved_target.result, + .output_mode = opts.config.output_mode, + .link_mode = opts.config.link_mode, + .version = opts.version, + }); + }, + .yes_path => |path| { + assert(opts.cache_mode == .none); + return try arena.dupe(u8, path); + }, + } + } + }; }; fn addModuleTableToCacheHash( @@ -1869,13 +1920,18 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil cache.hash.add(options.config.link_libunwind); cache.hash.add(output_mode); cache_helpers.addDebugFormat(&cache.hash, options.config.debug_format); - cache_helpers.addOptionalEmitLoc(&cache.hash, options.emit_bin); - cache_helpers.addOptionalEmitLoc(&cache.hash, options.emit_implib); - cache_helpers.addOptionalEmitLoc(&cache.hash, options.emit_docs); cache.hash.addBytes(options.root_name); cache.hash.add(options.config.wasi_exec_model); cache.hash.add(options.config.san_cov_trace_pc_guard); cache.hash.add(options.debug_compiler_runtime_libs); + // The actual emit paths don't matter. They're only user-specified if we aren't using the + // cache! However, it does matter whether the files are emitted at all. + cache.hash.add(options.emit_bin != .no); + cache.hash.add(options.emit_asm != .no); + cache.hash.add(options.emit_implib != .no); + cache.hash.add(options.emit_llvm_ir != .no); + cache.hash.add(options.emit_llvm_bc != .no); + cache.hash.add(options.emit_docs != .no); // TODO audit this and make sure everything is in it const main_mod = options.main_mod orelse options.root_mod; @@ -1925,7 +1981,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil try zcu.init(options.thread_pool.getIdCount()); break :blk zcu; } else blk: { - if (options.emit_h != null) return error.NoZigModuleForCHeader; + if (options.emit_h != .no) return error.NoZigModuleForCHeader; break :blk null; }; errdefer if (opt_zcu) |zcu| zcu.deinit(); @@ -1938,18 +1994,13 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil .arena = arena, .zcu = opt_zcu, .cache_use = undefined, // populated below - .bin_file = null, // populated below - .implib_emit = null, // handled below - .docs_emit = null, // handled below + .bin_file = null, // populated below if necessary .root_mod = options.root_mod, .config = options.config, .dirs = options.dirs, - .emit_asm = options.emit_asm, - .emit_llvm_ir = options.emit_llvm_ir, - .emit_llvm_bc = options.emit_llvm_bc, .work_queues = @splat(.init(gpa)), - .c_object_work_queue = std.fifo.LinearFifo(*CObject, .Dynamic).init(gpa), - .win32_resource_work_queue = if (dev.env.supports(.win32_resource)) std.fifo.LinearFifo(*Win32Resource, .Dynamic).init(gpa) else .{}, + .c_object_work_queue = .init(gpa), + .win32_resource_work_queue = if (dev.env.supports(.win32_resource)) .init(gpa) else .{}, .c_source_files = options.c_source_files, .rc_source_files = options.rc_source_files, .cache_parent = cache, @@ -2002,6 +2053,12 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil .file_system_inputs = options.file_system_inputs, .parent_whole_cache = options.parent_whole_cache, .link_diags = .init(gpa), + .emit_bin = try options.emit_bin.resolve(arena, &options, .bin), + .emit_asm = try options.emit_asm.resolve(arena, &options, .@"asm"), + .emit_implib = try options.emit_implib.resolve(arena, &options, .implib), + .emit_llvm_ir = try options.emit_llvm_ir.resolve(arena, &options, .llvm_ir), + .emit_llvm_bc = try options.emit_llvm_bc.resolve(arena, &options, .llvm_bc), + .emit_docs = try options.emit_docs.resolve(arena, &options, .docs), }; // Prevent some footguns by making the "any" fields of config reflect @@ -2068,7 +2125,6 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil .soname = options.soname, .compatibility_version = options.compatibility_version, .build_id = build_id, - .disable_lld_caching = options.disable_lld_caching or options.cache_mode == .whole, .subsystem = options.subsystem, .hash_style = options.hash_style, .enable_link_snapshots = options.enable_link_snapshots, @@ -2087,6 +2143,17 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil }; switch (options.cache_mode) { + .none => { + const none = try arena.create(CacheUse.None); + none.* = .{ .tmp_artifact_directory = null }; + comp.cache_use = .{ .none = none }; + if (comp.emit_bin) |path| { + comp.bin_file = try link.File.open(arena, comp, .{ + .root_dir = .cwd(), + .sub_path = path, + }, lf_open_opts); + } + }, .incremental => { // Options that are specific to zig source files, that cannot be // modified between incremental updates. @@ -2100,7 +2167,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil hash.addListOfBytes(options.test_filters); hash.addOptionalBytes(options.test_name_prefix); hash.add(options.skip_linker_dependencies); - hash.add(options.emit_h != null); + hash.add(options.emit_h != .no); hash.add(error_limit); // Here we put the root source file path name, but *not* with addFile. @@ -2135,49 +2202,26 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil }; comp.cache_use = .{ .incremental = incremental }; - if (options.emit_bin) |emit_bin| { + if (comp.emit_bin) |cache_rel_path| { const emit: Cache.Path = .{ - .root_dir = emit_bin.directory orelse artifact_directory, - .sub_path = emit_bin.basename, + .root_dir = artifact_directory, + .sub_path = cache_rel_path, }; comp.bin_file = try link.File.open(arena, comp, emit, lf_open_opts); } - - if (options.emit_implib) |emit_implib| { - comp.implib_emit = .{ - .root_dir = emit_implib.directory orelse artifact_directory, - .sub_path = emit_implib.basename, - }; - } - - if (options.emit_docs) |emit_docs| { - comp.docs_emit = .{ - .root_dir = emit_docs.directory orelse artifact_directory, - .sub_path = emit_docs.basename, - }; - } }, .whole => { - // For whole cache mode, we don't know where to put outputs from - // the linker until the final cache hash, which is available after - // the compilation is complete. + // For whole cache mode, we don't know where to put outputs from the linker until + // the final cache hash, which is available after the compilation is complete. // - // Therefore, bin_file is left null until the beginning of update(), - // where it may find a cache hit, or use a temporary directory to - // hold output artifacts. + // Therefore, `comp.bin_file` is left `null` (already done) until `update`, where + // it may find a cache hit, or else will use a temporary directory to hold output + // artifacts. const whole = try arena.create(CacheUse.Whole); whole.* = .{ - // This is kept here so that link.File.open can be called later. .lf_open_opts = lf_open_opts, - // This is so that when doing `CacheMode.whole`, the mechanism in update() - // can use it for communicating the result directory via `bin_file.emit`. - // This is used to distinguish between -fno-emit-bin and -femit-bin - // for `CacheMode.whole`. - // This memory will be overwritten with the real digest in update() but - // the basename will be preserved. - .bin_sub_path = try prepareWholeEmitSubPath(arena, options.emit_bin), - .implib_sub_path = try prepareWholeEmitSubPath(arena, options.emit_implib), - .docs_sub_path = try prepareWholeEmitSubPath(arena, options.emit_docs), + .cache_manifest = null, + .cache_manifest_mutex = .{}, .tmp_artifact_directory = null, .lock = null, }; @@ -2245,12 +2289,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil } } - const have_bin_emit = switch (comp.cache_use) { - .whole => |whole| whole.bin_sub_path != null, - .incremental => comp.bin_file != null, - }; - - if (have_bin_emit and target.ofmt != .c) { + if (comp.emit_bin != null and target.ofmt != .c) { if (!comp.skip_linker_dependencies) { // If we need to build libc for the target, add work items for it. // We go through the work queue so that building can be done in parallel. @@ -2544,8 +2583,23 @@ pub fn hotCodeSwap( try lf.makeExecutable(); } -fn cleanupAfterUpdate(comp: *Compilation) void { +fn cleanupAfterUpdate(comp: *Compilation, tmp_dir_rand_int: u64) void { switch (comp.cache_use) { + .none => |none| { + if (none.tmp_artifact_directory) |*tmp_dir| { + tmp_dir.handle.close(); + none.tmp_artifact_directory = null; + const tmp_dir_sub_path = "tmp" ++ std.fs.path.sep_str ++ std.fmt.hex(tmp_dir_rand_int); + comp.dirs.local_cache.handle.deleteTree(tmp_dir_sub_path) catch |err| { + log.warn("failed to delete temporary directory '{s}{c}{s}': {s}", .{ + comp.dirs.local_cache.path orelse ".", + std.fs.path.sep, + tmp_dir_sub_path, + @errorName(err), + }); + }; + } + }, .incremental => return, .whole => |whole| { if (whole.cache_manifest) |man| { @@ -2556,10 +2610,18 @@ fn cleanupAfterUpdate(comp: *Compilation) void { lf.destroy(); comp.bin_file = null; } - if (whole.tmp_artifact_directory) |*directory| { - directory.handle.close(); - if (directory.path) |p| comp.gpa.free(p); + if (whole.tmp_artifact_directory) |*tmp_dir| { + tmp_dir.handle.close(); whole.tmp_artifact_directory = null; + const tmp_dir_sub_path = "tmp" ++ std.fs.path.sep_str ++ std.fmt.hex(tmp_dir_rand_int); + comp.dirs.local_cache.handle.deleteTree(tmp_dir_sub_path) catch |err| { + log.warn("failed to delete temporary directory '{s}{c}{s}': {s}", .{ + comp.dirs.local_cache.path orelse ".", + std.fs.path.sep, + tmp_dir_sub_path, + @errorName(err), + }); + }; } }, } @@ -2579,14 +2641,27 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void { comp.clearMiscFailures(); comp.last_update_was_cache_hit = false; - var man: Cache.Manifest = undefined; - defer cleanupAfterUpdate(comp); - var tmp_dir_rand_int: u64 = undefined; + var man: Cache.Manifest = undefined; + defer cleanupAfterUpdate(comp, tmp_dir_rand_int); // If using the whole caching strategy, we check for *everything* up front, including // C source files. + log.debug("Compilation.update for {s}, CacheMode.{s}", .{ comp.root_name, @tagName(comp.cache_use) }); switch (comp.cache_use) { + .none => |none| { + assert(none.tmp_artifact_directory == null); + none.tmp_artifact_directory = d: { + tmp_dir_rand_int = std.crypto.random.int(u64); + const tmp_dir_sub_path = "tmp" ++ std.fs.path.sep_str ++ std.fmt.hex(tmp_dir_rand_int); + const path = try comp.dirs.local_cache.join(arena, &.{tmp_dir_sub_path}); + break :d .{ + .path = path, + .handle = try comp.dirs.local_cache.handle.makeOpenPath(tmp_dir_sub_path, .{}), + }; + }; + }, + .incremental => {}, .whole => |whole| { assert(comp.bin_file == null); // We are about to obtain this lock, so here we give other processes a chance first. @@ -2633,10 +2708,8 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void { comp.last_update_was_cache_hit = true; log.debug("CacheMode.whole cache hit for {s}", .{comp.root_name}); const bin_digest = man.finalBin(); - const hex_digest = Cache.binToHex(bin_digest); comp.digest = bin_digest; - comp.wholeCacheModeSetBinFilePath(whole, &hex_digest); assert(whole.lock == null); whole.lock = man.toOwnedLock(); @@ -2645,52 +2718,23 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void { log.debug("CacheMode.whole cache miss for {s}", .{comp.root_name}); // Compile the artifacts to a temporary directory. - const tmp_artifact_directory: Cache.Directory = d: { - const s = std.fs.path.sep_str; + whole.tmp_artifact_directory = d: { tmp_dir_rand_int = std.crypto.random.int(u64); - const tmp_dir_sub_path = "tmp" ++ s ++ std.fmt.hex(tmp_dir_rand_int); - - const path = try comp.dirs.local_cache.join(gpa, &.{tmp_dir_sub_path}); - errdefer gpa.free(path); - - const handle = try comp.dirs.local_cache.handle.makeOpenPath(tmp_dir_sub_path, .{}); - errdefer handle.close(); - + const tmp_dir_sub_path = "tmp" ++ std.fs.path.sep_str ++ std.fmt.hex(tmp_dir_rand_int); + const path = try comp.dirs.local_cache.join(arena, &.{tmp_dir_sub_path}); break :d .{ .path = path, - .handle = handle, + .handle = try comp.dirs.local_cache.handle.makeOpenPath(tmp_dir_sub_path, .{}), }; }; - whole.tmp_artifact_directory = tmp_artifact_directory; - - // Now that the directory is known, it is time to create the Emit - // objects and call link.File.open. - - if (whole.implib_sub_path) |sub_path| { - comp.implib_emit = .{ - .root_dir = tmp_artifact_directory, - .sub_path = std.fs.path.basename(sub_path), - }; - } - - if (whole.docs_sub_path) |sub_path| { - comp.docs_emit = .{ - .root_dir = tmp_artifact_directory, - .sub_path = std.fs.path.basename(sub_path), - }; - } - - if (whole.bin_sub_path) |sub_path| { + if (comp.emit_bin) |sub_path| { const emit: Cache.Path = .{ - .root_dir = tmp_artifact_directory, - .sub_path = std.fs.path.basename(sub_path), + .root_dir = whole.tmp_artifact_directory.?, + .sub_path = sub_path, }; comp.bin_file = try link.File.createEmpty(arena, comp, emit, whole.lf_open_opts); } }, - .incremental => { - log.debug("Compilation.update for {s}, CacheMode.incremental", .{comp.root_name}); - }, } // From this point we add a preliminary set of file system inputs that @@ -2789,11 +2833,18 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void { return; } - // Flush below handles -femit-bin but there is still -femit-llvm-ir, - // -femit-llvm-bc, and -femit-asm, in the case of C objects. - comp.emitOthers(); + if (comp.zcu == null and comp.config.output_mode == .Obj and comp.c_object_table.count() == 1) { + // This is `zig build-obj foo.c`. We can emit asm and LLVM IR/bitcode. + const c_obj_path = comp.c_object_table.keys()[0].status.success.object_path; + if (comp.emit_asm) |path| try comp.emitFromCObject(arena, c_obj_path, ".s", path); + if (comp.emit_llvm_ir) |path| try comp.emitFromCObject(arena, c_obj_path, ".ll", path); + if (comp.emit_llvm_bc) |path| try comp.emitFromCObject(arena, c_obj_path, ".bc", path); + } switch (comp.cache_use) { + .none, .incremental => { + try flush(comp, arena, .main, main_progress_node); + }, .whole => |whole| { if (comp.file_system_inputs) |buf| try man.populateFileSystemInputs(buf); if (comp.parent_whole_cache) |pwc| { @@ -2805,18 +2856,6 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void { const bin_digest = man.finalBin(); const hex_digest = Cache.binToHex(bin_digest); - // Rename the temporary directory into place. - // Close tmp dir and link.File to avoid open handle during rename. - if (whole.tmp_artifact_directory) |*tmp_directory| { - tmp_directory.handle.close(); - if (tmp_directory.path) |p| gpa.free(p); - whole.tmp_artifact_directory = null; - } else unreachable; - - const s = std.fs.path.sep_str; - const tmp_dir_sub_path = "tmp" ++ s ++ std.fmt.hex(tmp_dir_rand_int); - const o_sub_path = "o" ++ s ++ hex_digest; - // Work around windows `AccessDenied` if any files within this // directory are open by closing and reopening the file handles. const need_writable_dance: enum { no, lf_only, lf_and_debug } = w: { @@ -2841,6 +2880,13 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void { break :w .no; }; + // Rename the temporary directory into place. + // Close tmp dir and link.File to avoid open handle during rename. + whole.tmp_artifact_directory.?.handle.close(); + whole.tmp_artifact_directory = null; + const s = std.fs.path.sep_str; + const tmp_dir_sub_path = "tmp" ++ s ++ std.fmt.hex(tmp_dir_rand_int); + const o_sub_path = "o" ++ s ++ hex_digest; renameTmpIntoCache(comp.dirs.local_cache, tmp_dir_sub_path, o_sub_path) catch |err| { return comp.setMiscFailure( .rename_results, @@ -2853,7 +2899,6 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void { ); }; comp.digest = bin_digest; - comp.wholeCacheModeSetBinFilePath(whole, &hex_digest); // The linker flush functions need to know the final output path // for debug info purposes because executable debug info contains @@ -2861,10 +2906,9 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void { if (comp.bin_file) |lf| { lf.emit = .{ .root_dir = comp.dirs.local_cache, - .sub_path = whole.bin_sub_path.?, + .sub_path = try std.fs.path.join(arena, &.{ o_sub_path, comp.emit_bin.? }), }; - // Has to be after the `wholeCacheModeSetBinFilePath` above. switch (need_writable_dance) { .no => {}, .lf_only => try lf.makeWritable(), @@ -2875,10 +2919,7 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void { } } - try flush(comp, arena, .{ - .root_dir = comp.dirs.local_cache, - .sub_path = o_sub_path, - }, .main, main_progress_node); + try flush(comp, arena, .main, main_progress_node); // Calling `flush` may have produced errors, in which case the // cache manifest must not be written. @@ -2897,11 +2938,6 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void { assert(whole.lock == null); whole.lock = man.toOwnedLock(); }, - .incremental => |incremental| { - try flush(comp, arena, .{ - .root_dir = incremental.artifact_directory, - }, .main, main_progress_node); - }, } } @@ -2931,10 +2967,47 @@ pub fn appendFileSystemInput(comp: *Compilation, path: Compilation.Path) Allocat fsi.appendSliceAssumeCapacity(path.sub_path); } +fn resolveEmitPath(comp: *Compilation, path: []const u8) Cache.Path { + return .{ + .root_dir = switch (comp.cache_use) { + .none => .cwd(), + .incremental => |i| i.artifact_directory, + .whole => |w| w.tmp_artifact_directory.?, + }, + .sub_path = path, + }; +} +/// Like `resolveEmitPath`, but for calling during `flush`. The returned `Cache.Path` may reference +/// memory from `arena`, and may reference `path` itself. +/// If `kind == .temp`, then the returned path will be in a temporary or cache directory. This is +/// useful for intermediate files, such as the ZCU object file emitted by the LLVM backend. +pub fn resolveEmitPathFlush( + comp: *Compilation, + arena: Allocator, + kind: enum { temp, artifact }, + path: []const u8, +) Allocator.Error!Cache.Path { + switch (comp.cache_use) { + .none => |none| return .{ + .root_dir = switch (kind) { + .temp => none.tmp_artifact_directory.?, + .artifact => .cwd(), + }, + .sub_path = path, + }, + .incremental, .whole => return .{ + .root_dir = comp.dirs.local_cache, + .sub_path = try fs.path.join(arena, &.{ + "o", + &Cache.binToHex(comp.digest.?), + path, + }), + }, + } +} fn flush( comp: *Compilation, arena: Allocator, - default_artifact_directory: Cache.Path, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node, ) !void { @@ -2942,19 +3015,32 @@ fn flush( 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), + + .bin_path = p: { + const lf = comp.bin_file orelse break :p null; + const p = try comp.resolveEmitPathFlush(arena, .temp, lf.zcu_object_basename.?); + break :p try p.toStringZ(arena); + }, + .asm_path = p: { + const raw = comp.emit_asm orelse break :p null; + const p = try comp.resolveEmitPathFlush(arena, .artifact, raw); + break :p try p.toStringZ(arena); + }, + .post_ir_path = p: { + const raw = comp.emit_llvm_ir orelse break :p null; + const p = try comp.resolveEmitPathFlush(arena, .artifact, raw); + break :p try p.toStringZ(arena); + }, + .post_bc_path = p: { + const raw = comp.emit_llvm_bc orelse break :p null; + const p = try comp.resolveEmitPathFlush(arena, .artifact, raw); + break :p try p.toStringZ(arena); + }, .is_debug = comp.root_mod.optimize_mode == .Debug, .is_small = comp.root_mod.optimize_mode == .ReleaseSmall, @@ -3025,45 +3111,6 @@ fn renameTmpIntoCache( } } -/// Communicate the output binary location to parent Compilations. -fn wholeCacheModeSetBinFilePath( - comp: *Compilation, - whole: *CacheUse.Whole, - digest: *const [Cache.hex_digest_len]u8, -) void { - const digest_start = 2; // "o/[digest]/[basename]" - - if (whole.bin_sub_path) |sub_path| { - @memcpy(sub_path[digest_start..][0..digest.len], digest); - } - - if (whole.implib_sub_path) |sub_path| { - @memcpy(sub_path[digest_start..][0..digest.len], digest); - - comp.implib_emit = .{ - .root_dir = comp.dirs.local_cache, - .sub_path = sub_path, - }; - } - - if (whole.docs_sub_path) |sub_path| { - @memcpy(sub_path[digest_start..][0..digest.len], digest); - - comp.docs_emit = .{ - .root_dir = comp.dirs.local_cache, - .sub_path = sub_path, - }; - } -} - -fn prepareWholeEmitSubPath(arena: Allocator, opt_emit: ?EmitLoc) error{OutOfMemory}!?[]u8 { - const emit = opt_emit orelse return null; - if (emit.directory != null) return null; - const s = std.fs.path.sep_str; - const format = "o" ++ s ++ ("x" ** Cache.hex_digest_len) ++ s ++ "{s}"; - return try std.fmt.allocPrint(arena, format, .{emit.basename}); -} - /// This is only observed at compile-time and used to emit a compile error /// to remind the programmer to update multiple related pieces of code that /// are in different locations. Bump this number when adding or deleting @@ -3084,7 +3131,7 @@ fn addNonIncrementalStuffToCacheManifest( man.hash.addListOfBytes(comp.test_filters); man.hash.addOptionalBytes(comp.test_name_prefix); man.hash.add(comp.skip_linker_dependencies); - //man.hash.add(zcu.emit_h != null); + //man.hash.add(zcu.emit_h != .no); man.hash.add(zcu.error_limit); } else { cache_helpers.addModule(&man.hash, comp.root_mod); @@ -3130,10 +3177,6 @@ fn addNonIncrementalStuffToCacheManifest( man.hash.addListOfBytes(comp.framework_dirs); man.hash.addListOfBytes(comp.windows_libs.keys()); - cache_helpers.addOptionalEmitLoc(&man.hash, comp.emit_asm); - cache_helpers.addOptionalEmitLoc(&man.hash, comp.emit_llvm_ir); - cache_helpers.addOptionalEmitLoc(&man.hash, comp.emit_llvm_bc); - man.hash.addListOfBytes(comp.global_cc_argv); const opts = comp.cache_use.whole.lf_open_opts; @@ -3211,54 +3254,39 @@ fn addNonIncrementalStuffToCacheManifest( man.hash.addOptional(opts.minor_subsystem_version); } -fn emitOthers(comp: *Compilation) void { - if (comp.config.output_mode != .Obj or comp.zcu != null or - comp.c_object_table.count() == 0) - { - return; - } - const obj_path = comp.c_object_table.keys()[0].status.success.object_path; - const ext = std.fs.path.extension(obj_path.sub_path); - const dirname = obj_path.sub_path[0 .. obj_path.sub_path.len - ext.len]; - // This obj path always ends with the object file extension, but if we change the - // extension to .ll, .bc, or .s, then it will be the path to those things. - const outs = [_]struct { - emit: ?EmitLoc, - ext: []const u8, - }{ - .{ .emit = comp.emit_asm, .ext = ".s" }, - .{ .emit = comp.emit_llvm_ir, .ext = ".ll" }, - .{ .emit = comp.emit_llvm_bc, .ext = ".bc" }, - }; - for (outs) |out| { - if (out.emit) |loc| { - if (loc.directory) |directory| { - const src_path = std.fmt.allocPrint(comp.gpa, "{s}{s}", .{ - dirname, out.ext, - }) catch |err| { - log.err("unable to copy {s}{s}: {s}", .{ dirname, out.ext, @errorName(err) }); - continue; - }; - defer comp.gpa.free(src_path); - obj_path.root_dir.handle.copyFile(src_path, directory.handle, loc.basename, .{}) catch |err| { - log.err("unable to copy {s}: {s}", .{ src_path, @errorName(err) }); - }; - } - } - } -} - -fn resolveEmitLoc( +fn emitFromCObject( + comp: *Compilation, arena: Allocator, - default_artifact_directory: Cache.Path, - opt_loc: ?EmitLoc, -) Allocator.Error!?[*:0]const u8 { - const loc = opt_loc orelse return null; - const slice = if (loc.directory) |directory| - try directory.joinZ(arena, &.{loc.basename}) - else - try default_artifact_directory.joinStringZ(arena, loc.basename); - return slice.ptr; + c_obj_path: Cache.Path, + new_ext: []const u8, + unresolved_emit_path: []const u8, +) Allocator.Error!void { + // The dirname and stem (i.e. everything but the extension), of the sub path of the C object. + // We'll append `new_ext` to it to get the path to the right thing (asm, LLVM IR, etc). + const c_obj_dir_and_stem: []const u8 = p: { + const p = c_obj_path.sub_path; + const ext_len = fs.path.extension(p).len; + break :p p[0 .. p.len - ext_len]; + }; + const src_path: Cache.Path = .{ + .root_dir = c_obj_path.root_dir, + .sub_path = try std.fmt.allocPrint(arena, "{s}{s}", .{ + c_obj_dir_and_stem, + new_ext, + }), + }; + const emit_path = comp.resolveEmitPath(unresolved_emit_path); + + src_path.root_dir.handle.copyFile( + src_path.sub_path, + emit_path.root_dir.handle, + emit_path.sub_path, + .{}, + ) catch |err| log.err("unable to copy '{}' to '{}': {s}", .{ + src_path, + emit_path, + @errorName(err), + }); } /// Having the file open for writing is problematic as far as executing the @@ -4179,7 +4207,7 @@ fn performAllTheWorkInner( comp.link_task_queue.start(comp); - if (comp.docs_emit != null) { + if (comp.emit_docs != null) { dev.check(.docs_emit); comp.thread_pool.spawnWg(&work_queue_wait_group, workerDocsCopy, .{comp}); work_queue_wait_group.spawnManager(workerDocsWasm, .{ comp, main_progress_node }); @@ -4457,7 +4485,7 @@ fn performAllTheWorkInner( }; } }, - .incremental => {}, + .none, .incremental => {}, } if (any_fatal_files or @@ -4721,12 +4749,12 @@ fn docsCopyFallible(comp: *Compilation) anyerror!void { const zcu = comp.zcu orelse return comp.lockAndSetMiscFailure(.docs_copy, "no Zig code to document", .{}); - const emit = comp.docs_emit.?; - var out_dir = emit.root_dir.handle.makeOpenPath(emit.sub_path, .{}) catch |err| { + const docs_path = comp.resolveEmitPath(comp.emit_docs.?); + var out_dir = docs_path.root_dir.handle.makeOpenPath(docs_path.sub_path, .{}) catch |err| { return comp.lockAndSetMiscFailure( .docs_copy, - "unable to create output directory '{}{s}': {s}", - .{ emit.root_dir, emit.sub_path, @errorName(err) }, + "unable to create output directory '{}': {s}", + .{ docs_path, @errorName(err) }, ); }; defer out_dir.close(); @@ -4745,8 +4773,8 @@ fn docsCopyFallible(comp: *Compilation) anyerror!void { var tar_file = out_dir.createFile("sources.tar", .{}) catch |err| { return comp.lockAndSetMiscFailure( .docs_copy, - "unable to create '{}{s}/sources.tar': {s}", - .{ emit.root_dir, emit.sub_path, @errorName(err) }, + "unable to create '{}/sources.tar': {s}", + .{ docs_path, @errorName(err) }, ); }; defer tar_file.close(); @@ -4896,11 +4924,6 @@ fn workerDocsWasmFallible(comp: *Compilation, prog_node: std.Progress.Node) anye .parent = root_mod, }); try root_mod.deps.put(arena, "Walk", walk_mod); - const bin_basename = try std.zig.binNameAlloc(arena, .{ - .root_name = root_name, - .target = resolved_target.result, - .output_mode = output_mode, - }); const sub_compilation = try Compilation.create(gpa, arena, .{ .dirs = dirs, @@ -4912,10 +4935,7 @@ fn workerDocsWasmFallible(comp: *Compilation, prog_node: std.Progress.Node) anye .root_name = root_name, .thread_pool = comp.thread_pool, .libc_installation = comp.libc_installation, - .emit_bin = .{ - .directory = null, // Put it in the cache directory. - .basename = bin_basename, - }, + .emit_bin = .yes_cache, .verbose_cc = comp.verbose_cc, .verbose_link = comp.verbose_link, .verbose_air = comp.verbose_air, @@ -4930,27 +4950,31 @@ fn workerDocsWasmFallible(comp: *Compilation, prog_node: std.Progress.Node) anye try comp.updateSubCompilation(sub_compilation, .docs_wasm, prog_node); - const emit = comp.docs_emit.?; - var out_dir = emit.root_dir.handle.makeOpenPath(emit.sub_path, .{}) catch |err| { + var crt_file = try sub_compilation.toCrtFile(); + defer crt_file.deinit(gpa); + + const docs_bin_file = crt_file.full_object_path; + assert(docs_bin_file.sub_path.len > 0); // emitted binary is not a directory + + const docs_path = comp.resolveEmitPath(comp.emit_docs.?); + var out_dir = docs_path.root_dir.handle.makeOpenPath(docs_path.sub_path, .{}) catch |err| { return comp.lockAndSetMiscFailure( .docs_copy, - "unable to create output directory '{}{s}': {s}", - .{ emit.root_dir, emit.sub_path, @errorName(err) }, + "unable to create output directory '{}': {s}", + .{ docs_path, @errorName(err) }, ); }; defer out_dir.close(); - sub_compilation.dirs.local_cache.handle.copyFile( - sub_compilation.cache_use.whole.bin_sub_path.?, + crt_file.full_object_path.root_dir.handle.copyFile( + crt_file.full_object_path.sub_path, out_dir, "main.wasm", .{}, ) catch |err| { - return comp.lockAndSetMiscFailure(.docs_copy, "unable to copy '{}{s}' to '{}{s}': {s}", .{ - sub_compilation.dirs.local_cache, - sub_compilation.cache_use.whole.bin_sub_path.?, - emit.root_dir, - emit.sub_path, + return comp.lockAndSetMiscFailure(.docs_copy, "unable to copy '{}' to '{}': {s}", .{ + crt_file.full_object_path, + docs_path, @errorName(err), }); }; @@ -5212,7 +5236,7 @@ pub fn cImport(comp: *Compilation, c_src: []const u8, owner_mod: *Package.Module defer whole.cache_manifest_mutex.unlock(); try whole_cache_manifest.addDepFilePost(zig_cache_tmp_dir, dep_basename); }, - .incremental => {}, + .incremental, .none => {}, } const bin_digest = man.finalBin(); @@ -5557,9 +5581,9 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr defer man.deinit(); man.hash.add(comp.clang_preprocessor_mode); - cache_helpers.addOptionalEmitLoc(&man.hash, comp.emit_asm); - cache_helpers.addOptionalEmitLoc(&man.hash, comp.emit_llvm_ir); - cache_helpers.addOptionalEmitLoc(&man.hash, comp.emit_llvm_bc); + man.hash.addOptionalBytes(comp.emit_asm); + man.hash.addOptionalBytes(comp.emit_llvm_ir); + man.hash.addOptionalBytes(comp.emit_llvm_bc); try cache_helpers.hashCSource(&man, c_object.src); @@ -5793,7 +5817,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr try whole_cache_manifest.addDepFilePost(zig_cache_tmp_dir, dep_basename); } }, - .incremental => {}, + .incremental, .none => {}, } } @@ -6037,7 +6061,7 @@ fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32 defer whole.cache_manifest_mutex.unlock(); try whole_cache_manifest.addFilePost(dep_file_path); }, - .incremental => {}, + .incremental, .none => {}, } } } @@ -7209,12 +7233,6 @@ fn buildOutputFromZig( .cc_argv = &.{}, .parent = null, }); - const target = comp.getTarget(); - const bin_basename = try std.zig.binNameAlloc(arena, .{ - .root_name = root_name, - .target = target, - .output_mode = output_mode, - }); const parent_whole_cache: ?ParentWholeCache = switch (comp.cache_use) { .whole => |whole| .{ @@ -7227,7 +7245,7 @@ fn buildOutputFromZig( 3, // global cache is the same }, }, - .incremental => null, + .incremental, .none => null, }; const sub_compilation = try Compilation.create(gpa, arena, .{ @@ -7240,13 +7258,9 @@ fn buildOutputFromZig( .root_name = root_name, .thread_pool = comp.thread_pool, .libc_installation = comp.libc_installation, - .emit_bin = .{ - .directory = null, // Put it in the cache directory. - .basename = bin_basename, - }, + .emit_bin = .yes_cache, .function_sections = true, .data_sections = true, - .emit_h = null, .verbose_cc = comp.verbose_cc, .verbose_link = comp.verbose_link, .verbose_air = comp.verbose_air, @@ -7366,13 +7380,9 @@ pub fn build_crt_file( .root_name = root_name, .thread_pool = comp.thread_pool, .libc_installation = comp.libc_installation, - .emit_bin = .{ - .directory = null, // Put it in the cache directory. - .basename = basename, - }, + .emit_bin = .yes_cache, .function_sections = options.function_sections orelse false, .data_sections = options.data_sections orelse false, - .emit_h = null, .c_source_files = c_source_files, .verbose_cc = comp.verbose_cc, .verbose_link = comp.verbose_link, @@ -7444,7 +7454,11 @@ pub fn toCrtFile(comp: *Compilation) Allocator.Error!CrtFile { return .{ .full_object_path = .{ .root_dir = comp.dirs.local_cache, - .sub_path = try comp.gpa.dupe(u8, comp.cache_use.whole.bin_sub_path.?), + .sub_path = try std.fs.path.join(comp.gpa, &.{ + "o", + &Cache.binToHex(comp.digest.?), + comp.emit_bin.?, + }), }, .lock = comp.cache_use.whole.moveLock(), }; diff --git a/src/Zcu/PerThread.zig b/src/Zcu/PerThread.zig index ffc103310b04a713ec39c8f3c832dd23422b4d77..5215f787ef5d92750dee4dbc097704547e892762 100644 --- a/src/Zcu/PerThread.zig +++ b/src/Zcu/PerThread.zig @@ -2493,7 +2493,7 @@ fn newEmbedFile( cache: { const whole = switch (zcu.comp.cache_use) { .whole => |whole| whole, - .incremental => break :cache, + .incremental, .none => break :cache, }; const man = whole.cache_manifest orelse break :cache; const ip_str = opt_ip_str orelse break :cache; // this will be a compile error @@ -3377,7 +3377,7 @@ pub fn populateTestFunctions( } // The linker thread is not running, so we actually need to dispatch this task directly. - @import("../link.zig").doZcuTask(zcu.comp, @intFromEnum(pt.tid), .{ .link_nav = nav_index }); + @import("../link.zig").linkTestFunctionsNav(pt, nav_index); } } diff --git a/src/libs/freebsd.zig b/src/libs/freebsd.zig index d90ba974fce10d536ef66d61ca411a1e57cfd735..0d14b6fb4776465a6fc5ed2060f9456d0b4002ef 100644 --- a/src/libs/freebsd.zig +++ b/src/libs/freebsd.zig @@ -1019,10 +1019,6 @@ fn buildSharedLib( defer tracy.end(); const basename = try std.fmt.allocPrint(arena, "lib{s}.so.{d}", .{ lib.name, lib.sover }); - const emit_bin = Compilation.EmitLoc{ - .directory = bin_directory, - .basename = basename, - }; const version: Version = .{ .major = lib.sover, .minor = 0, .patch = 0 }; const ld_basename = path.basename(comp.getTarget().standardDynamicLinkerPath().get().?); const soname = if (mem.eql(u8, lib.name, "ld")) ld_basename else basename; @@ -1082,8 +1078,7 @@ fn buildSharedLib( .root_mod = root_mod, .root_name = lib.name, .libc_installation = comp.libc_installation, - .emit_bin = emit_bin, - .emit_h = null, + .emit_bin = .yes_cache, .verbose_cc = comp.verbose_cc, .verbose_link = comp.verbose_link, .verbose_air = comp.verbose_air, diff --git a/src/libs/glibc.zig b/src/libs/glibc.zig index ed5eae377f2d6f9b5ce1f9d245388f262b345a84..cb8dd4b46099495c7bfd64d71989fdb02e5332bf 100644 --- a/src/libs/glibc.zig +++ b/src/libs/glibc.zig @@ -1185,10 +1185,6 @@ fn buildSharedLib( defer tracy.end(); const basename = try std.fmt.allocPrint(arena, "lib{s}.so.{d}", .{ lib.name, lib.sover }); - const emit_bin = Compilation.EmitLoc{ - .directory = bin_directory, - .basename = basename, - }; const version: Version = .{ .major = lib.sover, .minor = 0, .patch = 0 }; const ld_basename = path.basename(comp.getTarget().standardDynamicLinkerPath().get().?); const soname = if (mem.eql(u8, lib.name, "ld")) ld_basename else basename; @@ -1248,8 +1244,7 @@ fn buildSharedLib( .root_mod = root_mod, .root_name = lib.name, .libc_installation = comp.libc_installation, - .emit_bin = emit_bin, - .emit_h = null, + .emit_bin = .yes_cache, .verbose_cc = comp.verbose_cc, .verbose_link = comp.verbose_link, .verbose_air = comp.verbose_air, diff --git a/src/libs/libcxx.zig b/src/libs/libcxx.zig index eb9f5df8558fb179f6230d5cbff90fe8e049249d..0009bfe120c8bdbd3ae7ecadf10c9cab32adc025 100644 --- a/src/libs/libcxx.zig +++ b/src/libs/libcxx.zig @@ -122,17 +122,6 @@ pub fn buildLibCxx(comp: *Compilation, prog_node: std.Progress.Node) BuildError! const output_mode = .Lib; const link_mode = .static; const target = comp.root_mod.resolved_target.result; - const basename = try std.zig.binNameAlloc(arena, .{ - .root_name = root_name, - .target = target, - .output_mode = output_mode, - .link_mode = link_mode, - }); - - const emit_bin = Compilation.EmitLoc{ - .directory = null, // Put it in the cache directory. - .basename = basename, - }; const cxxabi_include_path = try comp.dirs.zig_lib.join(arena, &.{ "libcxxabi", "include" }); const cxx_include_path = try comp.dirs.zig_lib.join(arena, &.{ "libcxx", "include" }); @@ -271,8 +260,7 @@ pub fn buildLibCxx(comp: *Compilation, prog_node: std.Progress.Node) BuildError! .root_name = root_name, .thread_pool = comp.thread_pool, .libc_installation = comp.libc_installation, - .emit_bin = emit_bin, - .emit_h = null, + .emit_bin = .yes_cache, .c_source_files = c_source_files.items, .verbose_cc = comp.verbose_cc, .verbose_link = comp.verbose_link, @@ -327,17 +315,6 @@ pub fn buildLibCxxAbi(comp: *Compilation, prog_node: std.Progress.Node) BuildErr const output_mode = .Lib; const link_mode = .static; const target = comp.root_mod.resolved_target.result; - const basename = try std.zig.binNameAlloc(arena, .{ - .root_name = root_name, - .target = target, - .output_mode = output_mode, - .link_mode = link_mode, - }); - - const emit_bin = Compilation.EmitLoc{ - .directory = null, // Put it in the cache directory. - .basename = basename, - }; const cxxabi_include_path = try comp.dirs.zig_lib.join(arena, &.{ "libcxxabi", "include" }); const cxx_include_path = try comp.dirs.zig_lib.join(arena, &.{ "libcxx", "include" }); @@ -467,8 +444,7 @@ pub fn buildLibCxxAbi(comp: *Compilation, prog_node: std.Progress.Node) BuildErr .root_name = root_name, .thread_pool = comp.thread_pool, .libc_installation = comp.libc_installation, - .emit_bin = emit_bin, - .emit_h = null, + .emit_bin = .yes_cache, .c_source_files = c_source_files.items, .verbose_cc = comp.verbose_cc, .verbose_link = comp.verbose_link, diff --git a/src/libs/libtsan.zig b/src/libs/libtsan.zig index 0c59d85bc5cc0a7ad09c7f9ad16a9c4ac6de8be6..f2cd6831f7d71f08688d8d1ec506d00d837d7dbe 100644 --- a/src/libs/libtsan.zig +++ b/src/libs/libtsan.zig @@ -45,11 +45,6 @@ pub fn buildTsan(comp: *Compilation, prog_node: std.Progress.Node) BuildError!vo .link_mode = link_mode, }); - const emit_bin = Compilation.EmitLoc{ - .directory = null, // Put it in the cache directory. - .basename = basename, - }; - const optimize_mode = comp.compilerRtOptMode(); const strip = comp.compilerRtStrip(); const unwind_tables: std.builtin.UnwindTables = @@ -287,8 +282,7 @@ pub fn buildTsan(comp: *Compilation, prog_node: std.Progress.Node) BuildError!vo .root_mod = root_mod, .root_name = root_name, .libc_installation = comp.libc_installation, - .emit_bin = emit_bin, - .emit_h = null, + .emit_bin = .yes_cache, .c_source_files = c_source_files.items, .verbose_cc = comp.verbose_cc, .verbose_link = comp.verbose_link, diff --git a/src/libs/libunwind.zig b/src/libs/libunwind.zig index ccea649c173a2e8f1ecae35bb80ced8cae8b1d60..711d63ebbcb0d5c7ea7566c6446e23fa4bc7d4b0 100644 --- a/src/libs/libunwind.zig +++ b/src/libs/libunwind.zig @@ -31,7 +31,7 @@ pub fn buildStaticLib(comp: *Compilation, prog_node: std.Progress.Node) BuildErr const unwind_tables: std.builtin.UnwindTables = if (target.cpu.arch == .x86 and target.os.tag == .windows) .none else .@"async"; const config = Compilation.Config.resolve(.{ - .output_mode = .Lib, + .output_mode = output_mode, .resolved_target = comp.root_mod.resolved_target, .is_test = false, .have_zcu = false, @@ -85,17 +85,6 @@ pub fn buildStaticLib(comp: *Compilation, prog_node: std.Progress.Node) BuildErr }; const root_name = "unwind"; - const link_mode = .static; - const basename = try std.zig.binNameAlloc(arena, .{ - .root_name = root_name, - .target = target, - .output_mode = output_mode, - .link_mode = link_mode, - }); - const emit_bin = Compilation.EmitLoc{ - .directory = null, // Put it in the cache directory. - .basename = basename, - }; var c_source_files: [unwind_src_list.len]Compilation.CSourceFile = undefined; for (unwind_src_list, 0..) |unwind_src, i| { var cflags = std.ArrayList([]const u8).init(arena); @@ -160,7 +149,7 @@ pub fn buildStaticLib(comp: *Compilation, prog_node: std.Progress.Node) BuildErr .main_mod = null, .thread_pool = comp.thread_pool, .libc_installation = comp.libc_installation, - .emit_bin = emit_bin, + .emit_bin = .yes_cache, .function_sections = comp.function_sections, .c_source_files = &c_source_files, .verbose_cc = comp.verbose_cc, diff --git a/src/libs/musl.zig b/src/libs/musl.zig index 21aeee98b5d28417973d45da972f2d94473e5d82..7c4e71c9744fefae97eed2dced5737afe65eade5 100644 --- a/src/libs/musl.zig +++ b/src/libs/musl.zig @@ -252,8 +252,7 @@ pub fn buildCrtFile(comp: *Compilation, in_crt_file: CrtFile, prog_node: std.Pro .thread_pool = comp.thread_pool, .root_name = "c", .libc_installation = comp.libc_installation, - .emit_bin = .{ .directory = null, .basename = "libc.so" }, - .emit_h = null, + .emit_bin = .yes_cache, .verbose_cc = comp.verbose_cc, .verbose_link = comp.verbose_link, .verbose_air = comp.verbose_air, diff --git a/src/libs/netbsd.zig b/src/libs/netbsd.zig index 7121c308f5785fae4b5380d76f65630b6ea0b9ee..f19c528d5d1b73748582793a7e92e570a186c852 100644 --- a/src/libs/netbsd.zig +++ b/src/libs/netbsd.zig @@ -684,10 +684,6 @@ fn buildSharedLib( defer tracy.end(); const basename = try std.fmt.allocPrint(arena, "lib{s}.so.{d}", .{ lib.name, lib.sover }); - const emit_bin = Compilation.EmitLoc{ - .directory = bin_directory, - .basename = basename, - }; const version: Version = .{ .major = lib.sover, .minor = 0, .patch = 0 }; const ld_basename = path.basename(comp.getTarget().standardDynamicLinkerPath().get().?); const soname = if (mem.eql(u8, lib.name, "ld")) ld_basename else basename; @@ -746,8 +742,7 @@ fn buildSharedLib( .root_mod = root_mod, .root_name = lib.name, .libc_installation = comp.libc_installation, - .emit_bin = emit_bin, - .emit_h = null, + .emit_bin = .yes_cache, .verbose_cc = comp.verbose_cc, .verbose_link = comp.verbose_link, .verbose_air = comp.verbose_air, diff --git a/src/link.zig b/src/link.zig index 577d7ba82c33bc157a629c32912fcf615ad56389..bbd0163d23c25e8b7fce317542a7ad61984d393a 100644 --- a/src/link.zig +++ b/src/link.zig @@ -384,9 +384,11 @@ pub const File = struct { emit: Path, file: ?fs.File, - /// When linking with LLD, this linker code will output an object file only at - /// this location, and then this path can be placed on the LLD linker line. - zcu_object_sub_path: ?[]const u8 = null, + /// When using the LLVM backend, the emitted object is written to a file with this name. This + /// object file then becomes a normal link input to LLD or a self-hosted linker. + /// + /// To convert this to an actual path, see `Compilation.resolveEmitPath` (with `kind == .temp`). + zcu_object_basename: ?[]const u8 = null, gc_sections: bool, print_gc_sections: bool, build_id: std.zig.BuildId, @@ -433,7 +435,6 @@ pub const File = struct { export_symbol_names: []const []const u8, global_base: ?u64, build_id: std.zig.BuildId, - disable_lld_caching: bool, hash_style: Lld.Elf.HashStyle, sort_section: ?Lld.Elf.SortSection, major_subsystem_version: ?u16, @@ -1083,7 +1084,7 @@ pub const File = struct { // In this case, an object file is created by the LLVM backend, so // there is no prelink phase. The Zig code is linked as a standard // object along with the others. - if (base.zcu_object_sub_path != null) return; + if (base.zcu_object_basename != null) return; switch (base.tag) { inline .wasm => |tag| { @@ -1496,6 +1497,31 @@ pub fn doZcuTask(comp: *Compilation, tid: usize, task: ZcuTask) void { }, } } +/// After the main pipeline is done, but before flush, the compilation may need to link one final +/// `Nav` into the binary: the `builtin.test_functions` value. Since the link thread isn't running +/// by then, we expose this function which can be called directly. +pub fn linkTestFunctionsNav(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) void { + const zcu = pt.zcu; + const comp = zcu.comp; + const diags = &comp.link_diags; + 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 => zcu.assertCodegenFailed(nav_index), + error.Overflow, error.RelocationNotByteAligned => { + switch (zcu.codegenFail(nav_index, "unable to codegen: {s}", .{@errorName(err)})) { + error.CodegenFail => return, + error.OutOfMemory => return diags.setAllocFailure(), + } + // Not a retryable failure. + }, + }; + } +} /// Provided by the CLI, processed into `LinkInput` instances at the start of /// the compilation pipeline. diff --git a/src/link/Coff.zig b/src/link/Coff.zig index bb8faf583d0a66800bdf6bbf4da76c1d360d1307..81376c45d8f6505418befc6e376772ece603ffa5 100644 --- a/src/link/Coff.zig +++ b/src/link/Coff.zig @@ -224,21 +224,16 @@ pub fn createEmpty( else => 0x1000, }; - // If using LLVM to generate the object file for the zig compilation unit, - // we need a place to put the object file so that it can be subsequently - // handled. - const zcu_object_sub_path = if (!use_llvm) - null - else - try allocPrint(arena, "{s}.obj", .{emit.sub_path}); - const coff = try arena.create(Coff); coff.* = .{ .base = .{ .tag = .coff, .comp = comp, .emit = emit, - .zcu_object_sub_path = zcu_object_sub_path, + .zcu_object_basename = if (use_llvm) + try std.fmt.allocPrint(arena, "{s}_zcu.obj", .{fs.path.stem(emit.sub_path)}) + else + null, .stack_size = options.stack_size orelse 16777216, .gc_sections = options.gc_sections orelse (optimize_mode != .Debug), .print_gc_sections = options.print_gc_sections, diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 34e04ad557124b99887b6a3a3b7edb7a6e801b91..498bc734c3d57961962b0460ca69fe9c0e02116f 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -249,14 +249,6 @@ pub fn createEmpty( const is_dyn_lib = output_mode == .Lib and link_mode == .dynamic; const default_sym_version: elf.Versym = if (is_dyn_lib or comp.config.rdynamic) .GLOBAL else .LOCAL; - // If using LLVM to generate the object file for the zig compilation unit, - // we need a place to put the object file so that it can be subsequently - // handled. - const zcu_object_sub_path = if (!use_llvm) - null - else - try std.fmt.allocPrint(arena, "{s}.o", .{emit.sub_path}); - var rpath_table: std.StringArrayHashMapUnmanaged(void) = .empty; try rpath_table.entries.resize(arena, options.rpath_list.len); @memcpy(rpath_table.entries.items(.key), options.rpath_list); @@ -268,7 +260,10 @@ pub fn createEmpty( .tag = .elf, .comp = comp, .emit = emit, - .zcu_object_sub_path = zcu_object_sub_path, + .zcu_object_basename = if (use_llvm) + try std.fmt.allocPrint(arena, "{s}_zcu.o", .{fs.path.stem(emit.sub_path)}) + else + null, .gc_sections = options.gc_sections orelse (optimize_mode != .Debug and output_mode != .Obj), .print_gc_sections = options.print_gc_sections, .stack_size = options.stack_size orelse 16777216, @@ -770,17 +765,13 @@ fn flushInner(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id) !void { const gpa = comp.gpa; const diags = &comp.link_diags; - const module_obj_path: ?Path = if (self.base.zcu_object_sub_path) |path| .{ - .root_dir = self.base.emit.root_dir, - .sub_path = if (fs.path.dirname(self.base.emit.sub_path)) |dirname| - try fs.path.join(arena, &.{ dirname, path }) - else - path, + const zcu_obj_path: ?Path = if (self.base.zcu_object_basename) |raw| p: { + break :p try comp.resolveEmitPathFlush(arena, .temp, raw); } else null; if (self.zigObjectPtr()) |zig_object| try zig_object.flush(self, tid); - if (module_obj_path) |path| openParseObjectReportingFailure(self, path); + if (zcu_obj_path) |path| openParseObjectReportingFailure(self, path); switch (comp.config.output_mode) { .Obj => return relocatable.flushObject(self, comp), diff --git a/src/link/Goff.zig b/src/link/Goff.zig index d0c2b8e80b6368429f38b42f9fa0706a34936f40..c222ae029f30954d830f49553b79393b6f1514f3 100644 --- a/src/link/Goff.zig +++ b/src/link/Goff.zig @@ -41,7 +41,7 @@ pub fn createEmpty( .tag = .goff, .comp = comp, .emit = emit, - .zcu_object_sub_path = emit.sub_path, + .zcu_object_basename = emit.sub_path, .gc_sections = options.gc_sections orelse false, .print_gc_sections = options.print_gc_sections, .stack_size = options.stack_size orelse 0, diff --git a/src/link/Lld.zig b/src/link/Lld.zig index 3b7b2b6740d31df82514f9aaeda21ea5f808d40e..dd50bd2a2f457356403050b47f0580fc3bbe20f6 100644 --- a/src/link/Lld.zig +++ b/src/link/Lld.zig @@ -1,5 +1,4 @@ base: link.File, -disable_caching: bool, ofmt: union(enum) { elf: Elf, coff: Coff, @@ -231,7 +230,7 @@ pub fn createEmpty( .tag = .lld, .comp = comp, .emit = emit, - .zcu_object_sub_path = try allocPrint(arena, "{s}.{s}", .{ emit.sub_path, obj_file_ext }), + .zcu_object_basename = try allocPrint(arena, "{s}_zcu.{s}", .{ fs.path.stem(emit.sub_path), obj_file_ext }), .gc_sections = gc_sections, .print_gc_sections = options.print_gc_sections, .stack_size = stack_size, @@ -239,7 +238,6 @@ pub fn createEmpty( .file = null, .build_id = options.build_id, }, - .disable_caching = options.disable_lld_caching, .ofmt = switch (target.ofmt) { .coff => .{ .coff = try .init(comp, options) }, .elf => .{ .elf = try .init(comp, options) }, @@ -289,14 +287,11 @@ fn linkAsArchive(lld: *Lld, arena: Allocator) !void { const full_out_path_z = try arena.dupeZ(u8, full_out_path); const opt_zcu = comp.zcu; - // 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: { - const dirname = fs.path.dirname(full_out_path_z) orelse "."; - break :blk try fs.path.join(arena, &.{ dirname, base.zcu_object_sub_path.? }); + const zcu_obj_path: ?Cache.Path = if (opt_zcu != null) p: { + break :p try comp.resolveEmitPathFlush(arena, .temp, base.zcu_object_basename.?); } else null; - log.debug("zcu_obj_path={s}", .{if (zcu_obj_path) |s| s else "(null)"}); + log.debug("zcu_obj_path={?}", .{zcu_obj_path}); const compiler_rt_path: ?Cache.Path = if (comp.compiler_rt_strat == .obj) comp.compiler_rt_obj.?.full_object_path @@ -330,7 +325,7 @@ fn linkAsArchive(lld: *Lld, arena: Allocator) !void { for (comp.win32_resource_table.keys()) |key| { object_files.appendAssumeCapacity(try arena.dupeZ(u8, key.status.success.res_path)); } - if (zcu_obj_path) |p| object_files.appendAssumeCapacity(try arena.dupeZ(u8, p)); + if (zcu_obj_path) |p| object_files.appendAssumeCapacity(try p.toStringZ(arena)); if (compiler_rt_path) |p| object_files.appendAssumeCapacity(try p.toStringZ(arena)); if (ubsan_rt_path) |p| object_files.appendAssumeCapacity(try p.toStringZ(arena)); @@ -368,14 +363,8 @@ fn coffLink(lld: *Lld, arena: Allocator) !void { const directory = base.emit.root_dir; // Just an alias to make it shorter to type. const full_out_path = try directory.join(arena, &[_][]const u8{base.emit.sub_path}); - // 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) p: { - if (fs.path.dirname(full_out_path)) |dirname| { - break :p try fs.path.join(arena, &.{ dirname, base.zcu_object_sub_path.? }); - } else { - break :p base.zcu_object_sub_path.?; - } + const zcu_obj_path: ?Cache.Path = if (comp.zcu != null) p: { + break :p try comp.resolveEmitPathFlush(arena, .temp, base.zcu_object_basename.?); } else null; const is_lib = comp.config.output_mode == .Lib; @@ -402,8 +391,8 @@ fn coffLink(lld: *Lld, arena: Allocator) !void { if (comp.c_object_table.count() != 0) break :blk comp.c_object_table.keys()[0].status.success.object_path; - if (module_obj_path) |p| - break :blk Cache.Path.initCwd(p); + if (zcu_obj_path) |p| + break :blk p; // TODO I think this is unreachable. Audit this situation when solving the above TODO // regarding eliding redundant object -> object transformations. @@ -513,9 +502,9 @@ fn coffLink(lld: *Lld, arena: Allocator) !void { try argv.append(try allocPrint(arena, "-OUT:{s}", .{full_out_path})); - if (comp.implib_emit) |emit| { - const implib_out_path = try emit.root_dir.join(arena, &[_][]const u8{emit.sub_path}); - try argv.append(try allocPrint(arena, "-IMPLIB:{s}", .{implib_out_path})); + if (comp.emit_implib) |raw_emit_path| { + const path = try comp.resolveEmitPathFlush(arena, .temp, raw_emit_path); + try argv.append(try allocPrint(arena, "-IMPLIB:{}", .{path})); } if (comp.config.link_libc) { @@ -556,8 +545,8 @@ fn coffLink(lld: *Lld, arena: Allocator) !void { try argv.append(key.status.success.res_path); } - if (module_obj_path) |p| { - try argv.append(p); + if (zcu_obj_path) |p| { + try argv.append(try p.toString(arena)); } if (coff.module_definition_file) |def| { @@ -808,14 +797,8 @@ fn elfLink(lld: *Lld, arena: Allocator) !void { const directory = base.emit.root_dir; // Just an alias to make it shorter to type. const full_out_path = try directory.join(arena, &[_][]const u8{base.emit.sub_path}); - // 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) p: { - if (fs.path.dirname(full_out_path)) |dirname| { - break :p try fs.path.join(arena, &.{ dirname, base.zcu_object_sub_path.? }); - } else { - break :p base.zcu_object_sub_path.?; - } + const zcu_obj_path: ?Cache.Path = if (comp.zcu != null) p: { + break :p try comp.resolveEmitPathFlush(arena, .temp, base.zcu_object_basename.?); } else null; const output_mode = comp.config.output_mode; @@ -862,8 +845,8 @@ fn elfLink(lld: *Lld, arena: Allocator) !void { if (comp.c_object_table.count() != 0) break :blk comp.c_object_table.keys()[0].status.success.object_path; - if (module_obj_path) |p| - break :blk Cache.Path.initCwd(p); + if (zcu_obj_path) |p| + break :blk p; // TODO I think this is unreachable. Audit this situation when solving the above TODO // regarding eliding redundant object -> object transformations. @@ -1151,8 +1134,8 @@ fn elfLink(lld: *Lld, arena: Allocator) !void { try argv.append(try key.status.success.object_path.toString(arena)); } - if (module_obj_path) |p| { - try argv.append(p); + if (zcu_obj_path) |p| { + try argv.append(try p.toString(arena)); } if (comp.tsan_lib) |lib| { @@ -1387,14 +1370,8 @@ fn wasmLink(lld: *Lld, arena: Allocator) !void { const directory = base.emit.root_dir; // Just an alias to make it shorter to type. const full_out_path = try directory.join(arena, &[_][]const u8{base.emit.sub_path}); - // 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) p: { - if (fs.path.dirname(full_out_path)) |dirname| { - break :p try fs.path.join(arena, &.{ dirname, base.zcu_object_sub_path.? }); - } else { - break :p base.zcu_object_sub_path.?; - } + const zcu_obj_path: ?Cache.Path = if (comp.zcu != null) p: { + break :p try comp.resolveEmitPathFlush(arena, .temp, base.zcu_object_basename.?); } else null; const is_obj = comp.config.output_mode == .Obj; @@ -1419,8 +1396,8 @@ fn wasmLink(lld: *Lld, arena: Allocator) !void { if (comp.c_object_table.count() != 0) break :blk comp.c_object_table.keys()[0].status.success.object_path; - if (module_obj_path) |p| - break :blk Cache.Path.initCwd(p); + if (zcu_obj_path) |p| + break :blk p; // TODO I think this is unreachable. Audit this situation when solving the above TODO // regarding eliding redundant object -> object transformations. @@ -1610,8 +1587,8 @@ fn wasmLink(lld: *Lld, arena: Allocator) !void { for (comp.c_object_table.keys()) |key| { try argv.append(try key.status.success.object_path.toString(arena)); } - if (module_obj_path) |p| { - try argv.append(p); + if (zcu_obj_path) |p| { + try argv.append(try p.toString(arena)); } if (compiler_rt_path) |p| { diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 8fd85df0a3bec9c98e5a74aaa92bd3396a69ffa2..6c081653ea3e8c2e8a68cdf0da441fe7f1451bca 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -173,13 +173,6 @@ pub fn createEmpty( const output_mode = comp.config.output_mode; const link_mode = comp.config.link_mode; - // If using LLVM to generate the object file for the zig compilation unit, - // we need a place to put the object file so that it can be subsequently - // handled. - const zcu_object_sub_path = if (!use_llvm) - null - else - try std.fmt.allocPrint(arena, "{s}.o", .{emit.sub_path}); const allow_shlib_undefined = options.allow_shlib_undefined orelse false; const self = try arena.create(MachO); @@ -188,7 +181,10 @@ pub fn createEmpty( .tag = .macho, .comp = comp, .emit = emit, - .zcu_object_sub_path = zcu_object_sub_path, + .zcu_object_basename = if (use_llvm) + try std.fmt.allocPrint(arena, "{s}_zcu.o", .{fs.path.stem(emit.sub_path)}) + else + null, .gc_sections = options.gc_sections orelse (optimize_mode != .Debug), .print_gc_sections = options.print_gc_sections, .stack_size = options.stack_size orelse 16777216, @@ -351,21 +347,16 @@ pub fn flush( const sub_prog_node = prog_node.start("MachO Flush", 0); defer sub_prog_node.end(); - const directory = self.base.emit.root_dir; - const module_obj_path: ?Path = if (self.base.zcu_object_sub_path) |path| .{ - .root_dir = directory, - .sub_path = if (fs.path.dirname(self.base.emit.sub_path)) |dirname| - try fs.path.join(arena, &.{ dirname, path }) - else - path, + const zcu_obj_path: ?Path = if (self.base.zcu_object_basename) |raw| p: { + break :p try comp.resolveEmitPathFlush(arena, .temp, raw); } else null; // --verbose-link if (comp.verbose_link) try self.dumpArgv(comp); if (self.getZigObject()) |zo| try zo.flush(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); + if (self.base.isStaticLib()) return relocatable.flushStaticLib(self, comp, zcu_obj_path); + if (self.base.isObject()) return relocatable.flushObject(self, comp, zcu_obj_path); var positionals = std.ArrayList(link.Input).init(gpa); defer positionals.deinit(); @@ -387,7 +378,7 @@ pub fn flush( positionals.appendAssumeCapacity(try link.openObjectInput(diags, key.status.success.object_path)); } - if (module_obj_path) |path| try positionals.append(try link.openObjectInput(diags, path)); + if (zcu_obj_path) |path| try positionals.append(try link.openObjectInput(diags, path)); if (comp.config.any_sanitize_thread) { try positionals.append(try link.openObjectInput(diags, comp.tsan_lib.?.full_object_path)); @@ -636,12 +627,9 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void { const directory = self.base.emit.root_dir; const full_out_path = try directory.join(arena, &[_][]const u8{self.base.emit.sub_path}); - const module_obj_path: ?[]const u8 = if (self.base.zcu_object_sub_path) |path| blk: { - if (fs.path.dirname(full_out_path)) |dirname| { - break :blk try fs.path.join(arena, &.{ dirname, path }); - } else { - break :blk path; - } + const zcu_obj_path: ?[]const u8 = if (self.base.zcu_object_basename) |raw| p: { + const p = try comp.resolveEmitPathFlush(arena, .temp, raw); + break :p try p.toString(arena); } else null; var argv = std.ArrayList([]const u8).init(arena); @@ -670,7 +658,7 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void { try argv.append(try key.status.success.object_path.toString(arena)); } - if (module_obj_path) |p| { + if (zcu_obj_path) |p| { try argv.append(p); } } else { @@ -762,7 +750,7 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void { try argv.append(try key.status.success.object_path.toString(arena)); } - if (module_obj_path) |p| { + if (zcu_obj_path) |p| { try argv.append(p); } diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 67e530b5ccf93784866d068f8b39c4d99f547a30..82293b9c4541b78c0c249d50cdf65b862fe67e19 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -2951,21 +2951,16 @@ pub fn createEmpty( const output_mode = comp.config.output_mode; const wasi_exec_model = comp.config.wasi_exec_model; - // If using LLVM to generate the object file for the zig compilation unit, - // we need a place to put the object file so that it can be subsequently - // handled. - const zcu_object_sub_path = if (!use_llvm) - null - else - try std.fmt.allocPrint(arena, "{s}.o", .{emit.sub_path}); - const wasm = try arena.create(Wasm); wasm.* = .{ .base = .{ .tag = .wasm, .comp = comp, .emit = emit, - .zcu_object_sub_path = zcu_object_sub_path, + .zcu_object_basename = if (use_llvm) + try std.fmt.allocPrint(arena, "{s}_zcu.o", .{fs.path.stem(emit.sub_path)}) + else + null, // Garbage collection is so crucial to WebAssembly that we design // the linker around the assumption that it will be on in the vast // majority of cases, and therefore express "no garbage collection" @@ -3834,15 +3829,9 @@ pub fn flush( if (comp.verbose_link) Compilation.dump_argv(wasm.dump_argv_list.items); - if (wasm.base.zcu_object_sub_path) |path| { - const module_obj_path: Path = .{ - .root_dir = wasm.base.emit.root_dir, - .sub_path = if (fs.path.dirname(wasm.base.emit.sub_path)) |dirname| - try fs.path.join(arena, &.{ dirname, path }) - else - path, - }; - openParseObjectReportingFailure(wasm, module_obj_path); + if (wasm.base.zcu_object_basename) |raw| { + const zcu_obj_path: Path = try comp.resolveEmitPathFlush(arena, .temp, raw); + openParseObjectReportingFailure(wasm, zcu_obj_path); try prelink(wasm, prog_node); } diff --git a/src/link/Xcoff.zig b/src/link/Xcoff.zig index 97ea300ed2a33b9c5c038f9c49517ba43442e07b..93fda27f3f69482f8dc53f907f9abdac00257487 100644 --- a/src/link/Xcoff.zig +++ b/src/link/Xcoff.zig @@ -41,7 +41,7 @@ pub fn createEmpty( .tag = .xcoff, .comp = comp, .emit = emit, - .zcu_object_sub_path = emit.sub_path, + .zcu_object_basename = emit.sub_path, .gc_sections = options.gc_sections orelse false, .print_gc_sections = options.print_gc_sections, .stack_size = options.stack_size orelse 0, diff --git a/src/main.zig b/src/main.zig index f7ad35d7cdc328e2418a6665441c6a165fe33d5f..dc1d66381b8def8c83f35c52dcb59f9d8b2b3e7b 100644 --- a/src/main.zig +++ b/src/main.zig @@ -699,55 +699,21 @@ const Emit = union(enum) { yes_default_path, yes: []const u8, - const Resolved = struct { - data: ?Compilation.EmitLoc, - dir: ?fs.Dir, - - fn deinit(self: *Resolved) void { - if (self.dir) |*dir| { - dir.close(); - } - } - }; - - fn resolve(emit: Emit, default_basename: []const u8, output_to_cache: bool) !Resolved { - var resolved: Resolved = .{ .data = null, .dir = null }; - errdefer resolved.deinit(); - - switch (emit) { - .no => {}, - .yes_default_path => { - resolved.data = Compilation.EmitLoc{ - .directory = if (output_to_cache) null else .{ - .path = null, - .handle = fs.cwd(), - }, - .basename = default_basename, - }; - }, - .yes => |full_path| { - const basename = fs.path.basename(full_path); - if (fs.path.dirname(full_path)) |dirname| { - const handle = try fs.cwd().openDir(dirname, .{}); - resolved = .{ - .dir = handle, - .data = Compilation.EmitLoc{ - .basename = basename, - .directory = .{ - .path = dirname, - .handle = handle, - }, - }, - }; - } else { - resolved.data = Compilation.EmitLoc{ - .basename = basename, - .directory = .{ .path = null, .handle = fs.cwd() }, - }; + const OutputToCacheReason = enum { listen, @"zig run", @"zig test" }; + fn resolve(emit: Emit, default_basename: []const u8, output_to_cache: ?OutputToCacheReason) Compilation.CreateOptions.Emit { + return switch (emit) { + .no => .no, + .yes_default_path => if (output_to_cache != null) .yes_cache else .{ .yes_path = default_basename }, + .yes => |path| if (output_to_cache) |reason| { + switch (reason) { + .listen => fatal("--listen incompatible with explicit output path '{s}'", .{path}), + .@"zig run", .@"zig test" => fatal( + "'{s}' with explicit output path '{s}' requires explicit '-femit-bin=path' or '-fno-emit-bin'", + .{ @tagName(reason), path }, + ), } - }, - } - return resolved; + } else .{ .yes_path = path }, + }; } }; @@ -2830,7 +2796,7 @@ fn buildOutputType( .link => { create_module.opts.output_mode = if (is_shared_lib) .Lib else .Exe; if (emit_bin != .no) { - emit_bin = if (out_path) |p| .{ .yes = p } else EmitBin.yes_a_out; + emit_bin = if (out_path) |p| .{ .yes = p } else .yes_a_out; } if (emit_llvm) { fatal("-emit-llvm cannot be used when linking", .{}); @@ -3208,7 +3174,17 @@ fn buildOutputType( var cleanup_emit_bin_dir: ?fs.Dir = null; defer if (cleanup_emit_bin_dir) |*dir| dir.close(); - const output_to_cache = listen != .none; + // For `zig run` and `zig test`, we don't want to put the binary in the cwd by default. So, if + // the binary is requested with no explicit path (as is the default), we emit to the cache. + const output_to_cache: ?Emit.OutputToCacheReason = switch (listen) { + .stdio, .ip4 => .listen, + .none => if (arg_mode == .run and emit_bin == .yes_default_path) + .@"zig run" + else if (arg_mode == .zig_test and emit_bin == .yes_default_path) + .@"zig test" + else + null, + }; const optional_version = if (have_version) version else null; const root_name = if (provided_name) |n| n else main_mod.fully_qualified_name; @@ -3225,150 +3201,48 @@ fn buildOutputType( }, }; - const a_out_basename = switch (target.ofmt) { - .coff => "a.exe", - else => "a.out", - }; - - const emit_bin_loc: ?Compilation.EmitLoc = switch (emit_bin) { - .no => null, - .yes_default_path => Compilation.EmitLoc{ - .directory = blk: { - switch (arg_mode) { - .run, .zig_test => break :blk null, - .build, .cc, .cpp, .translate_c, .zig_test_obj => { - if (output_to_cache) { - break :blk null; - } else { - break :blk .{ .path = null, .handle = fs.cwd() }; - } - }, - } - }, - .basename = if (clang_preprocessor_mode == .pch) - try std.fmt.allocPrint(arena, "{s}.pch", .{root_name}) - else - try std.zig.binNameAlloc(arena, .{ + const emit_bin_resolved: Compilation.CreateOptions.Emit = switch (emit_bin) { + .no => .no, + .yes_default_path => emit: { + if (output_to_cache != null) break :emit .yes_cache; + const name = switch (clang_preprocessor_mode) { + .pch => try std.fmt.allocPrint(arena, "{s}.pch", .{root_name}), + else => try std.zig.binNameAlloc(arena, .{ .root_name = root_name, .target = target, .output_mode = create_module.resolved_options.output_mode, .link_mode = create_module.resolved_options.link_mode, .version = optional_version, }), + }; + break :emit .{ .yes_path = name }; }, - .yes => |full_path| b: { - const basename = fs.path.basename(full_path); - if (fs.path.dirname(full_path)) |dirname| { - const handle = fs.cwd().openDir(dirname, .{}) catch |err| { - fatal("unable to open output directory '{s}': {s}", .{ dirname, @errorName(err) }); - }; - cleanup_emit_bin_dir = handle; - break :b Compilation.EmitLoc{ - .basename = basename, - .directory = .{ - .path = dirname, - .handle = handle, - }, - }; - } else { - break :b Compilation.EmitLoc{ - .basename = basename, - .directory = .{ .path = null, .handle = fs.cwd() }, - }; - } - }, - .yes_a_out => Compilation.EmitLoc{ - .directory = .{ .path = null, .handle = fs.cwd() }, - .basename = a_out_basename, + .yes => |path| if (output_to_cache != null) { + assert(output_to_cache == .listen); // there was an explicit bin path + fatal("--listen incompatible with explicit output path '{s}'", .{path}); + } else .{ .yes_path = path }, + .yes_a_out => emit: { + assert(output_to_cache == null); + break :emit .{ .yes_path = switch (target.ofmt) { + .coff => "a.exe", + else => "a.out", + } }; }, }; const default_h_basename = try std.fmt.allocPrint(arena, "{s}.h", .{root_name}); - var emit_h_resolved = emit_h.resolve(default_h_basename, output_to_cache) catch |err| { - switch (emit_h) { - .yes => |p| { - fatal("unable to open directory from argument '-femit-h', '{s}': {s}", .{ - p, @errorName(err), - }); - }, - .yes_default_path => { - fatal("unable to open directory from arguments '--name' or '-fsoname', '{s}': {s}", .{ - default_h_basename, @errorName(err), - }); - }, - .no => unreachable, - } - }; - defer emit_h_resolved.deinit(); + const emit_h_resolved = emit_h.resolve(default_h_basename, output_to_cache); const default_asm_basename = try std.fmt.allocPrint(arena, "{s}.s", .{root_name}); - var emit_asm_resolved = emit_asm.resolve(default_asm_basename, output_to_cache) catch |err| { - switch (emit_asm) { - .yes => |p| { - fatal("unable to open directory from argument '-femit-asm', '{s}': {s}", .{ - p, @errorName(err), - }); - }, - .yes_default_path => { - fatal("unable to open directory from arguments '--name' or '-fsoname', '{s}': {s}", .{ - default_asm_basename, @errorName(err), - }); - }, - .no => unreachable, - } - }; - defer emit_asm_resolved.deinit(); + const emit_asm_resolved = emit_asm.resolve(default_asm_basename, output_to_cache); const default_llvm_ir_basename = try std.fmt.allocPrint(arena, "{s}.ll", .{root_name}); - var emit_llvm_ir_resolved = emit_llvm_ir.resolve(default_llvm_ir_basename, output_to_cache) catch |err| { - switch (emit_llvm_ir) { - .yes => |p| { - fatal("unable to open directory from argument '-femit-llvm-ir', '{s}': {s}", .{ - p, @errorName(err), - }); - }, - .yes_default_path => { - fatal("unable to open directory from arguments '--name' or '-fsoname', '{s}': {s}", .{ - default_llvm_ir_basename, @errorName(err), - }); - }, - .no => unreachable, - } - }; - defer emit_llvm_ir_resolved.deinit(); + const emit_llvm_ir_resolved = emit_llvm_ir.resolve(default_llvm_ir_basename, output_to_cache); const default_llvm_bc_basename = try std.fmt.allocPrint(arena, "{s}.bc", .{root_name}); - var emit_llvm_bc_resolved = emit_llvm_bc.resolve(default_llvm_bc_basename, output_to_cache) catch |err| { - switch (emit_llvm_bc) { - .yes => |p| { - fatal("unable to open directory from argument '-femit-llvm-bc', '{s}': {s}", .{ - p, @errorName(err), - }); - }, - .yes_default_path => { - fatal("unable to open directory from arguments '--name' or '-fsoname', '{s}': {s}", .{ - default_llvm_bc_basename, @errorName(err), - }); - }, - .no => unreachable, - } - }; - defer emit_llvm_bc_resolved.deinit(); + const emit_llvm_bc_resolved = emit_llvm_bc.resolve(default_llvm_bc_basename, output_to_cache); - var emit_docs_resolved = emit_docs.resolve("docs", output_to_cache) catch |err| { - switch (emit_docs) { - .yes => |p| { - fatal("unable to open directory from argument '-femit-docs', '{s}': {s}", .{ - p, @errorName(err), - }); - }, - .yes_default_path => { - fatal("unable to open directory 'docs': {s}", .{@errorName(err)}); - }, - .no => unreachable, - } - }; - defer emit_docs_resolved.deinit(); + const emit_docs_resolved = emit_docs.resolve("docs", output_to_cache); const is_exe_or_dyn_lib = switch (create_module.resolved_options.output_mode) { .Obj => false, @@ -3378,7 +3252,7 @@ fn buildOutputType( // Note that cmake when targeting Windows will try to execute // zig cc to make an executable and output an implib too. const implib_eligible = is_exe_or_dyn_lib and - emit_bin_loc != null and target.os.tag == .windows; + emit_bin_resolved != .no and target.os.tag == .windows; if (!implib_eligible) { if (!emit_implib_arg_provided) { emit_implib = .no; @@ -3387,22 +3261,18 @@ fn buildOutputType( } } const default_implib_basename = try std.fmt.allocPrint(arena, "{s}.lib", .{root_name}); - var emit_implib_resolved = switch (emit_implib) { - .no => Emit.Resolved{ .data = null, .dir = null }, - .yes => |p| emit_implib.resolve(default_implib_basename, output_to_cache) catch |err| { - fatal("unable to open directory from argument '-femit-implib', '{s}': {s}", .{ - p, @errorName(err), + const emit_implib_resolved: Compilation.CreateOptions.Emit = switch (emit_implib) { + .no => .no, + .yes => emit_implib.resolve(default_implib_basename, output_to_cache), + .yes_default_path => emit: { + if (output_to_cache != null) break :emit .yes_cache; + const p = try fs.path.join(arena, &.{ + fs.path.dirname(emit_bin_resolved.yes_path) orelse ".", + default_implib_basename, }); - }, - .yes_default_path => Emit.Resolved{ - .data = Compilation.EmitLoc{ - .directory = emit_bin_loc.?.directory, - .basename = default_implib_basename, - }, - .dir = null, + break :emit .{ .yes_path = p }; }, }; - defer emit_implib_resolved.deinit(); var thread_pool: ThreadPool = undefined; try thread_pool.init(.{ @@ -3456,7 +3326,7 @@ fn buildOutputType( src.src_path = try dirs.local_cache.join(arena, &.{sub_path}); } - if (build_options.have_llvm and emit_asm != .no) { + if (build_options.have_llvm and emit_asm_resolved != .no) { // LLVM has no way to set this non-globally. const argv = [_][*:0]const u8{ "zig (LLVM option parsing)", "--x86-asm-syntax=intel" }; @import("codegen/llvm/bindings.zig").ParseCommandLineOptions(argv.len, &argv); @@ -3472,23 +3342,11 @@ fn buildOutputType( fatal("--debug-incremental requires -fincremental", .{}); } - const disable_lld_caching = !output_to_cache; - const cache_mode: Compilation.CacheMode = b: { + // Once incremental compilation is the default, we'll want some smarter logic here, + // considering things like the backend in use and whether there's a ZCU. + if (output_to_cache == null) break :b .none; if (incremental) break :b .incremental; - if (disable_lld_caching) break :b .incremental; - if (!create_module.resolved_options.have_zcu) break :b .whole; - - // TODO: once we support incremental compilation for the LLVM backend - // via saving the LLVM module into a bitcode file and restoring it, - // along with compiler state, this clause can be removed so that - // incremental cache mode is used for LLVM backend too. - if (create_module.resolved_options.use_llvm) break :b .whole; - - // Eventually, this default should be `.incremental`. However, since incremental - // compilation is currently an opt-in feature, it makes a strictly worse default cache mode - // than `.whole`. - // https://github.com/ziglang/zig/issues/21165 break :b .whole; }; @@ -3510,13 +3368,13 @@ fn buildOutputType( .main_mod = main_mod, .root_mod = root_mod, .std_mod = std_mod, - .emit_bin = emit_bin_loc, - .emit_h = emit_h_resolved.data, - .emit_asm = emit_asm_resolved.data, - .emit_llvm_ir = emit_llvm_ir_resolved.data, - .emit_llvm_bc = emit_llvm_bc_resolved.data, - .emit_docs = emit_docs_resolved.data, - .emit_implib = emit_implib_resolved.data, + .emit_bin = emit_bin_resolved, + .emit_h = emit_h_resolved, + .emit_asm = emit_asm_resolved, + .emit_llvm_ir = emit_llvm_ir_resolved, + .emit_llvm_bc = emit_llvm_bc_resolved, + .emit_docs = emit_docs_resolved, + .emit_implib = emit_implib_resolved, .lib_directories = create_module.lib_directories.items, .rpath_list = create_module.rpath_list.items, .symbol_wrap_set = symbol_wrap_set, @@ -3599,7 +3457,6 @@ fn buildOutputType( .test_filters = test_filters.items, .test_name_prefix = test_name_prefix, .test_runner_path = test_runner_path, - .disable_lld_caching = disable_lld_caching, .cache_mode = cache_mode, .subsystem = subsystem, .debug_compile_errors = debug_compile_errors, @@ -3744,13 +3601,8 @@ fn buildOutputType( }) { dev.checkAny(&.{ .run_command, .test_command }); - if (test_exec_args.items.len == 0 and target.ofmt == .c) default_exec_args: { + if (test_exec_args.items.len == 0 and target.ofmt == .c and emit_bin_resolved != .no) { // Default to using `zig run` to execute the produced .c code from `zig test`. - const c_code_loc = emit_bin_loc orelse break :default_exec_args; - const c_code_directory = c_code_loc.directory orelse comp.bin_file.?.emit.root_dir; - const c_code_path = try fs.path.join(arena, &[_][]const u8{ - c_code_directory.path orelse ".", c_code_loc.basename, - }); try test_exec_args.appendSlice(arena, &.{ self_exe_path, "run" }); if (dirs.zig_lib.path) |p| { try test_exec_args.appendSlice(arena, &.{ "-I", p }); @@ -3775,7 +3627,7 @@ fn buildOutputType( if (create_module.dynamic_linker) |dl| { try test_exec_args.appendSlice(arena, &.{ "--dynamic-linker", dl }); } - try test_exec_args.append(arena, c_code_path); + try test_exec_args.append(arena, null); // placeholder for the path of the emitted C source file } try runOrTest( @@ -4354,12 +4206,22 @@ fn runOrTest( runtime_args_start: ?usize, link_libc: bool, ) !void { - const lf = comp.bin_file orelse return; - // A naive `directory.join` here will indeed get the correct path to the binary, - // however, in the case of cwd, we actually want `./foo` so that the path can be executed. - const exe_path = try fs.path.join(arena, &[_][]const u8{ - lf.emit.root_dir.path orelse ".", lf.emit.sub_path, - }); + const raw_emit_bin = comp.emit_bin orelse return; + const exe_path = switch (comp.cache_use) { + .none => p: { + if (fs.path.isAbsolute(raw_emit_bin)) break :p raw_emit_bin; + // Use `fs.path.join` to make a file in the cwd is still executed properly. + break :p try fs.path.join(arena, &.{ + ".", + raw_emit_bin, + }); + }, + .whole, .incremental => try comp.dirs.local_cache.join(arena, &.{ + "o", + &Cache.binToHex(comp.digest.?), + raw_emit_bin, + }), + }; var argv = std.ArrayList([]const u8).init(gpa); defer argv.deinit(); @@ -5087,16 +4949,6 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { }; }; - const exe_basename = try std.zig.binNameAlloc(arena, .{ - .root_name = "build", - .target = resolved_target.result, - .output_mode = .Exe, - }); - const emit_bin: Compilation.EmitLoc = .{ - .directory = null, // Use the local zig-cache. - .basename = exe_basename, - }; - process.raiseFileDescriptorLimit(); const cwd_path = try introspect.getResolvedCwd(arena); @@ -5357,8 +5209,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { .config = config, .root_mod = root_mod, .main_mod = build_mod, - .emit_bin = emit_bin, - .emit_h = null, + .emit_bin = .yes_cache, .self_exe_path = self_exe_path, .thread_pool = &thread_pool, .verbose_cc = verbose_cc, @@ -5386,8 +5237,11 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { // Since incremental compilation isn't done yet, we use cache_mode = whole // above, and thus the output file is already closed. //try comp.makeBinFileExecutable(); - child_argv.items[argv_index_exe] = - try dirs.local_cache.join(arena, &.{comp.cache_use.whole.bin_sub_path.?}); + child_argv.items[argv_index_exe] = try dirs.local_cache.join(arena, &.{ + "o", + &Cache.binToHex(comp.digest.?), + comp.emit_bin.?, + }); } if (process.can_spawn) { @@ -5504,16 +5358,6 @@ fn jitCmd( .is_explicit_dynamic_linker = false, }; - const exe_basename = try std.zig.binNameAlloc(arena, .{ - .root_name = options.cmd_name, - .target = resolved_target.result, - .output_mode = .Exe, - }); - const emit_bin: Compilation.EmitLoc = .{ - .directory = null, // Use the global zig-cache. - .basename = exe_basename, - }; - const self_exe_path = fs.selfExePathAlloc(arena) catch |err| { fatal("unable to find self exe path: {s}", .{@errorName(err)}); }; @@ -5605,8 +5449,7 @@ fn jitCmd( .config = config, .root_mod = root_mod, .main_mod = root_mod, - .emit_bin = emit_bin, - .emit_h = null, + .emit_bin = .yes_cache, .self_exe_path = self_exe_path, .thread_pool = &thread_pool, .cache_mode = .whole, @@ -5637,7 +5480,11 @@ fn jitCmd( }; } - const exe_path = try dirs.global_cache.join(arena, &.{comp.cache_use.whole.bin_sub_path.?}); + const exe_path = try dirs.global_cache.join(arena, &.{ + "o", + &Cache.binToHex(comp.digest.?), + comp.emit_bin.?, + }); child_argv.appendAssumeCapacity(exe_path); } diff --git a/tools/incr-check.zig b/tools/incr-check.zig index 6e69b93b96c0dccb798838dd8e78696ed826bf97..6c048f7a8709e1a6971b2f1a438e532b3d56c70b 100644 --- a/tools/incr-check.zig +++ b/tools/incr-check.zig @@ -314,7 +314,7 @@ const Eval = struct { const digest = body[@sizeOf(EbpHdr)..][0..Cache.bin_digest_len]; const result_dir = ".local-cache" ++ std.fs.path.sep_str ++ "o" ++ std.fs.path.sep_str ++ Cache.binToHex(digest.*); - const bin_name = try std.zig.binNameAlloc(arena, .{ + const bin_name = try std.zig.EmitArtifact.bin.cacheName(arena, .{ .root_name = "root", // corresponds to the module name "root" .target = eval.target.resolved, .output_mode = .Exe, -- 2.54.0 From d24af297422415346dc8fcbecabd2334571c1b5b Mon Sep 17 00:00:00 2001 From: mlugg Date: Fri, 6 Jun 2025 21:29:55 +0100 Subject: [PATCH 11/35] CMakeLists: update file list --- CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0ead51a35e71280891de4e46aee1dc61982b06a9..6078cad58636f4d72761809761a39d9dd903eb83 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -535,7 +535,6 @@ set(ZIG_STAGE2_SOURCES src/Sema.zig src/Sema/bitcast.zig src/Sema/comptime_ptr_access.zig - src/ThreadSafeQueue.zig src/Type.zig src/Value.zig src/Zcu.zig @@ -624,6 +623,7 @@ set(ZIG_STAGE2_SOURCES src/link/Elf/synthetic_sections.zig src/link/Goff.zig src/link/LdScript.zig + src/link/Lld.zig src/link/MachO.zig src/link/MachO/Archive.zig src/link/MachO/Atom.zig @@ -652,6 +652,7 @@ set(ZIG_STAGE2_SOURCES src/link/MachO/uuid.zig src/link/Plan9.zig src/link/Plan9/aout.zig + src/link/Queue.zig src/link/SpirV.zig src/link/SpirV/BinaryModule.zig src/link/SpirV/deduplicate.zig -- 2.54.0 From 580d622b0dfec3ae9f1e3262673d8d4daa6b5b60 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Fri, 6 Jun 2025 20:09:24 -0400 Subject: [PATCH 12/35] Zcu: fix verbose air --- src/Zcu/PerThread.zig | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Zcu/PerThread.zig b/src/Zcu/PerThread.zig index 5215f787ef5d92750dee4dbc097704547e892762..b5eaca039766709cb3384487896b65d4c705212b 100644 --- a/src/Zcu/PerThread.zig +++ b/src/Zcu/PerThread.zig @@ -4428,11 +4428,13 @@ fn runCodegenInner(pt: Zcu.PerThread, func_index: InternPool.Index, air: *Air) e var liveness: Air.Liveness = try .analyze(zcu, air.*, ip); defer liveness.deinit(gpa); - // TODO: surely writing to stderr from n threads simultaneously will work flawlessly if (build_options.enable_debug_extensions and comp.verbose_air) { - std.debug.print("# Begin Function AIR: {}:\n", .{fqn.fmt(ip)}); - air.dump(pt, liveness); - std.debug.print("# End Function AIR: {}\n\n", .{fqn.fmt(ip)}); + std.debug.lockStdErr(); + defer std.debug.unlockStdErr(); + const stderr = std.io.getStdErr().writer(); + stderr.print("# Begin Function AIR: {}:\n", .{fqn.fmt(ip)}) catch {}; + air.write(stderr, pt, liveness); + stderr.print("# End Function AIR: {}\n\n", .{fqn.fmt(ip)}) catch {}; } if (std.debug.runtime_safety) { -- 2.54.0 From c4ec382fc806e0cb4484c1cb2edcc5fc40c3c9d8 Mon Sep 17 00:00:00 2001 From: mlugg Date: Sat, 7 Jun 2025 14:47:59 +0100 Subject: [PATCH 13/35] InternPool: store the Nav types are named after When the name strategy is `.parent`, the DWARF info really wants to know what `Nav` we were named after to emit a more optimal hierarchy. --- src/InternPool.zig | 51 +++++++++++++++++++++++++++++ src/Sema.zig | 74 ++++++++++++++++++++++++++++--------------- src/Sema/LowerZon.zig | 5 +-- src/Zcu/PerThread.zig | 8 ++--- 4 files changed, 106 insertions(+), 32 deletions(-) diff --git a/src/InternPool.zig b/src/InternPool.zig index de1a434c029eab00e5744d91445519b8b7bc9f66..7c3e52a8cf0baf351e6bcb7d8897c7af20537353 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -3249,6 +3249,9 @@ pub const LoadedUnionType = struct { name: NullTerminatedString, /// Represents the declarations inside this union. namespace: NamespaceIndex, + /// If this is a declared type with the `.parent` name strategy, this is the `Nav` it was named after. + /// Otherwise, this is `.none`. + name_nav: Nav.Index.Optional, /// The enum tag type. enum_tag_ty: Index, /// List of field types in declaration order. @@ -3567,6 +3570,7 @@ pub fn loadUnionType(ip: *const InternPool, index: Index) LoadedUnionType { .tid = unwrapped_index.tid, .extra_index = data, .name = type_union.data.name, + .name_nav = type_union.data.name_nav, .namespace = type_union.data.namespace, .enum_tag_ty = type_union.data.tag_ty, .field_types = field_types, @@ -3584,6 +3588,9 @@ pub const LoadedStructType = struct { /// The name of this struct type. name: NullTerminatedString, namespace: NamespaceIndex, + /// If this is a declared type with the `.parent` name strategy, this is the `Nav` it was named after. + /// Otherwise, or if this is a file's root struct type, this is `.none`. + name_nav: Nav.Index.Optional, /// Index of the `struct_decl` or `reify` ZIR instruction. zir_index: TrackedInst.Index, layout: std.builtin.Type.ContainerLayout, @@ -4173,6 +4180,7 @@ pub fn loadStructType(ip: *const InternPool, index: Index) LoadedStructType { switch (item.tag) { .type_struct => { const name: NullTerminatedString = @enumFromInt(extra_items[item.data + std.meta.fieldIndex(Tag.TypeStruct, "name").?]); + const name_nav: Nav.Index.Optional = @enumFromInt(extra_items[item.data + std.meta.fieldIndex(Tag.TypeStruct, "name_nav").?]); const namespace: NamespaceIndex = @enumFromInt(extra_items[item.data + std.meta.fieldIndex(Tag.TypeStruct, "namespace").?]); const zir_index: TrackedInst.Index = @enumFromInt(extra_items[item.data + std.meta.fieldIndex(Tag.TypeStruct, "zir_index").?]); const fields_len = extra_items[item.data + std.meta.fieldIndex(Tag.TypeStruct, "fields_len").?]; @@ -4259,6 +4267,7 @@ pub fn loadStructType(ip: *const InternPool, index: Index) LoadedStructType { .tid = unwrapped_index.tid, .extra_index = item.data, .name = name, + .name_nav = name_nav, .namespace = namespace, .zir_index = zir_index, .layout = if (flags.is_extern) .@"extern" else .auto, @@ -4275,6 +4284,7 @@ pub fn loadStructType(ip: *const InternPool, index: Index) LoadedStructType { }, .type_struct_packed, .type_struct_packed_inits => { const name: NullTerminatedString = @enumFromInt(extra_items[item.data + std.meta.fieldIndex(Tag.TypeStructPacked, "name").?]); + const name_nav: Nav.Index.Optional = @enumFromInt(extra_items[item.data + std.meta.fieldIndex(Tag.TypeStructPacked, "name_nav").?]); const zir_index: TrackedInst.Index = @enumFromInt(extra_items[item.data + std.meta.fieldIndex(Tag.TypeStructPacked, "zir_index").?]); const fields_len = extra_items[item.data + std.meta.fieldIndex(Tag.TypeStructPacked, "fields_len").?]; const namespace: NamespaceIndex = @enumFromInt(extra_items[item.data + std.meta.fieldIndex(Tag.TypeStructPacked, "namespace").?]); @@ -4321,6 +4331,7 @@ pub fn loadStructType(ip: *const InternPool, index: Index) LoadedStructType { .tid = unwrapped_index.tid, .extra_index = item.data, .name = name, + .name_nav = name_nav, .namespace = namespace, .zir_index = zir_index, .layout = .@"packed", @@ -4345,6 +4356,9 @@ pub const LoadedEnumType = struct { name: NullTerminatedString, /// Represents the declarations inside this enum. namespace: NamespaceIndex, + /// If this is a declared type with the `.parent` name strategy, this is the `Nav` it was named after. + /// Otherwise, this is `.none`. + name_nav: Nav.Index.Optional, /// An integer type which is used for the numerical value of the enum. /// This field is present regardless of whether the enum has an /// explicitly provided tag type or auto-numbered. @@ -4428,6 +4442,7 @@ pub fn loadEnumType(ip: *const InternPool, index: Index) LoadedEnumType { } else extra.data.captures_len; return .{ .name = extra.data.name, + .name_nav = extra.data.name_nav, .namespace = extra.data.namespace, .tag_ty = extra.data.int_tag_type, .names = .{ @@ -4462,6 +4477,7 @@ pub fn loadEnumType(ip: *const InternPool, index: Index) LoadedEnumType { } else extra.data.captures_len; return .{ .name = extra.data.name, + .name_nav = extra.data.name_nav, .namespace = extra.data.namespace, .tag_ty = extra.data.int_tag_type, .names = .{ @@ -4493,6 +4509,9 @@ pub const LoadedOpaqueType = struct { // TODO: the non-fqn will be needed by the new dwarf structure /// The name of this opaque type. name: NullTerminatedString, + /// If this is a declared type with the `.parent` name strategy, this is the `Nav` it was named after. + /// Otherwise, this is `.none`. + name_nav: Nav.Index.Optional, /// Index of the `opaque_decl` or `reify` instruction. zir_index: TrackedInst.Index, captures: CaptureValue.Slice, @@ -4509,6 +4528,7 @@ pub fn loadOpaqueType(ip: *const InternPool, index: Index) LoadedOpaqueType { extra.data.captures_len; return .{ .name = extra.data.name, + .name_nav = extra.data.name_nav, .namespace = extra.data.namespace, .zir_index = extra.data.zir_index, .captures = .{ @@ -6022,6 +6042,7 @@ pub const Tag = enum(u8) { /// 4. field align: Alignment for each field; declaration order pub const TypeUnion = struct { name: NullTerminatedString, + name_nav: Nav.Index.Optional, flags: Flags, /// This could be provided through the tag type, but it is more convenient /// to store it directly. This is also necessary for `dumpStatsFallible` to @@ -6061,6 +6082,7 @@ pub const Tag = enum(u8) { /// 5. init: Index for each fields_len // if tag is type_struct_packed_inits pub const TypeStructPacked = struct { name: NullTerminatedString, + name_nav: Nav.Index.Optional, zir_index: TrackedInst.Index, fields_len: u32, namespace: NamespaceIndex, @@ -6108,6 +6130,7 @@ pub const Tag = enum(u8) { /// 8. field_offset: u32 // for each field in declared order, undef until layout_resolved pub const TypeStruct = struct { name: NullTerminatedString, + name_nav: Nav.Index.Optional, zir_index: TrackedInst.Index, namespace: NamespaceIndex, fields_len: u32, @@ -6151,6 +6174,7 @@ pub const Tag = enum(u8) { /// 0. capture: CaptureValue // for each `captures_len` pub const TypeOpaque = struct { name: NullTerminatedString, + name_nav: Nav.Index.Optional, /// Contains the declarations inside this opaque. namespace: NamespaceIndex, /// The index of the `opaque_decl` instruction. @@ -6429,6 +6453,7 @@ pub const Array = struct { /// 4. tag value: Index for each fields_len; declaration order pub const EnumExplicit = struct { name: NullTerminatedString, + name_nav: Nav.Index.Optional, /// `std.math.maxInt(u32)` indicates this type is reified. captures_len: u32, namespace: NamespaceIndex, @@ -6454,6 +6479,7 @@ pub const EnumExplicit = struct { /// 3. field name: NullTerminatedString for each fields_len; declaration order pub const EnumAuto = struct { name: NullTerminatedString, + name_nav: Nav.Index.Optional, /// `std.math.maxInt(u32)` indicates this type is reified. captures_len: u32, namespace: NamespaceIndex, @@ -8666,6 +8692,7 @@ pub fn getUnionType( .size = std.math.maxInt(u32), .padding = std.math.maxInt(u32), .name = undefined, // set by `finish` + .name_nav = undefined, // set by `finish` .namespace = undefined, // set by `finish` .tag_ty = ini.enum_tag_ty, .zir_index = switch (ini.key) { @@ -8717,6 +8744,7 @@ pub fn getUnionType( .tid = tid, .index = gop.put(), .type_name_extra_index = extra_index + std.meta.fieldIndex(Tag.TypeUnion, "name").?, + .name_nav_extra_index = extra_index + std.meta.fieldIndex(Tag.TypeUnion, "name_nav").?, .namespace_extra_index = extra_index + std.meta.fieldIndex(Tag.TypeUnion, "namespace").?, } }; } @@ -8726,15 +8754,20 @@ pub const WipNamespaceType = struct { index: Index, type_name_extra_index: u32, namespace_extra_index: u32, + name_nav_extra_index: u32, pub fn setName( wip: WipNamespaceType, ip: *InternPool, type_name: NullTerminatedString, + /// This should be the `Nav` we are named after if we use the `.parent` name strategy; `.none` otherwise. + /// This is also `.none` if we use `.parent` because we are the root struct type for a file. + name_nav: Nav.Index.Optional, ) void { const extra = ip.getLocalShared(wip.tid).extra.acquire(); const extra_items = extra.view().items(.@"0"); extra_items[wip.type_name_extra_index] = @intFromEnum(type_name); + extra_items[wip.name_nav_extra_index] = @intFromEnum(name_nav); } pub fn finish( @@ -8843,6 +8876,7 @@ pub fn getStructType( ini.fields_len); // inits const extra_index = addExtraAssumeCapacity(extra, Tag.TypeStructPacked{ .name = undefined, // set by `finish` + .name_nav = undefined, // set by `finish` .zir_index = zir_index, .fields_len = ini.fields_len, .namespace = undefined, // set by `finish` @@ -8887,6 +8921,7 @@ pub fn getStructType( .tid = tid, .index = gop.put(), .type_name_extra_index = extra_index + std.meta.fieldIndex(Tag.TypeStructPacked, "name").?, + .name_nav_extra_index = extra_index + std.meta.fieldIndex(Tag.TypeStructPacked, "name_nav").?, .namespace_extra_index = extra_index + std.meta.fieldIndex(Tag.TypeStructPacked, "namespace").?, } }; }, @@ -8909,6 +8944,7 @@ pub fn getStructType( 1); // names_map const extra_index = addExtraAssumeCapacity(extra, Tag.TypeStruct{ .name = undefined, // set by `finish` + .name_nav = undefined, // set by `finish` .zir_index = zir_index, .namespace = undefined, // set by `finish` .fields_len = ini.fields_len, @@ -8977,6 +9013,7 @@ pub fn getStructType( .tid = tid, .index = gop.put(), .type_name_extra_index = extra_index + std.meta.fieldIndex(Tag.TypeStruct, "name").?, + .name_nav_extra_index = extra_index + std.meta.fieldIndex(Tag.TypeStruct, "name_nav").?, .namespace_extra_index = extra_index + std.meta.fieldIndex(Tag.TypeStruct, "namespace").?, } }; } @@ -9766,6 +9803,7 @@ pub const WipEnumType = struct { tag_ty_index: u32, type_name_extra_index: u32, namespace_extra_index: u32, + name_nav_extra_index: u32, names_map: MapIndex, names_start: u32, values_map: OptionalMapIndex, @@ -9775,10 +9813,13 @@ pub const WipEnumType = struct { wip: WipEnumType, ip: *InternPool, type_name: NullTerminatedString, + /// This should be the `Nav` we are named after if we use the `.parent` name strategy; `.none` otherwise. + name_nav: Nav.Index.Optional, ) void { const extra = ip.getLocalShared(wip.tid).extra.acquire(); const extra_items = extra.view().items(.@"0"); extra_items[wip.type_name_extra_index] = @intFromEnum(type_name); + extra_items[wip.name_nav_extra_index] = @intFromEnum(name_nav); } pub fn prepare( @@ -9893,6 +9934,7 @@ pub fn getEnumType( const extra_index = addExtraAssumeCapacity(extra, EnumAuto{ .name = undefined, // set by `prepare` + .name_nav = undefined, // set by `prepare` .captures_len = switch (ini.key) { inline .declared, .declared_owned_captures => |d| @intCast(d.captures.len), .reified => std.math.maxInt(u32), @@ -9921,6 +9963,7 @@ pub fn getEnumType( .index = gop.put(), .tag_ty_index = extra_index + std.meta.fieldIndex(EnumAuto, "int_tag_type").?, .type_name_extra_index = extra_index + std.meta.fieldIndex(EnumAuto, "name").?, + .name_nav_extra_index = extra_index + std.meta.fieldIndex(EnumAuto, "name_nav").?, .namespace_extra_index = extra_index + std.meta.fieldIndex(EnumAuto, "namespace").?, .names_map = names_map, .names_start = @intCast(names_start), @@ -9950,6 +9993,7 @@ pub fn getEnumType( const extra_index = addExtraAssumeCapacity(extra, EnumExplicit{ .name = undefined, // set by `prepare` + .name_nav = undefined, // set by `prepare` .captures_len = switch (ini.key) { inline .declared, .declared_owned_captures => |d| @intCast(d.captures.len), .reified => std.math.maxInt(u32), @@ -9987,6 +10031,7 @@ pub fn getEnumType( .index = gop.put(), .tag_ty_index = extra_index + std.meta.fieldIndex(EnumExplicit, "int_tag_type").?, .type_name_extra_index = extra_index + std.meta.fieldIndex(EnumExplicit, "name").?, + .name_nav_extra_index = extra_index + std.meta.fieldIndex(EnumExplicit, "name_nav").?, .namespace_extra_index = extra_index + std.meta.fieldIndex(EnumExplicit, "namespace").?, .names_map = names_map, .names_start = @intCast(names_start), @@ -10055,6 +10100,7 @@ pub fn getGeneratedTagEnumType( .tag = .type_enum_auto, .data = addExtraAssumeCapacity(extra, EnumAuto{ .name = ini.name, + .name_nav = .none, .captures_len = 0, .namespace = namespace, .int_tag_type = ini.tag_ty, @@ -10088,6 +10134,7 @@ pub fn getGeneratedTagEnumType( }, .data = addExtraAssumeCapacity(extra, EnumExplicit{ .name = ini.name, + .name_nav = .none, .captures_len = 0, .namespace = namespace, .int_tag_type = ini.tag_ty, @@ -10161,6 +10208,7 @@ pub fn getOpaqueType( }); const extra_index = addExtraAssumeCapacity(extra, Tag.TypeOpaque{ .name = undefined, // set by `finish` + .name_nav = undefined, // set by `finish` .namespace = undefined, // set by `finish` .zir_index = switch (ini.key) { inline else => |x| x.zir_index, @@ -10183,6 +10231,7 @@ pub fn getOpaqueType( .tid = tid, .index = gop.put(), .type_name_extra_index = extra_index + std.meta.fieldIndex(Tag.TypeOpaque, "name").?, + .name_nav_extra_index = extra_index + std.meta.fieldIndex(Tag.TypeOpaque, "name_nav").?, .namespace_extra_index = extra_index + std.meta.fieldIndex(Tag.TypeOpaque, "namespace").?, }, }; @@ -10299,6 +10348,7 @@ fn addExtraAssumeCapacity(extra: Local.Extra.Mutable, item: anytype) u32 { extra.appendAssumeCapacity(.{switch (field.type) { Index, Nav.Index, + Nav.Index.Optional, NamespaceIndex, OptionalNamespaceIndex, MapIndex, @@ -10361,6 +10411,7 @@ fn extraDataTrail(extra: Local.Extra, comptime T: type, index: u32) struct { dat @field(result, field.name) = switch (field.type) { Index, Nav.Index, + Nav.Index.Optional, NamespaceIndex, OptionalNamespaceIndex, MapIndex, diff --git a/src/Sema.zig b/src/Sema.zig index c9f307e6244561dc003312ceb2fb522eb8b6af6d..87837d96d57158eeb6cf3a11d1e19abe4cfdea78 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -2963,13 +2963,14 @@ fn zirStructDecl( }; errdefer wip_ty.cancel(ip, pt.tid); - wip_ty.setName(ip, try sema.createTypeName( + const type_name = try sema.createTypeName( block, small.name_strategy, "struct", inst, wip_ty.index, - )); + ); + wip_ty.setName(ip, type_name.name, type_name.nav); const new_namespace_index: InternPool.NamespaceIndex = try pt.createNamespace(.{ .parent = block.namespace.toOptional(), @@ -3007,7 +3008,10 @@ pub fn createTypeName( inst: ?Zir.Inst.Index, /// This is used purely to give the type a unique name in the `anon` case. type_index: InternPool.Index, -) !InternPool.NullTerminatedString { +) !struct { + name: InternPool.NullTerminatedString, + nav: InternPool.Nav.Index.Optional, +} { const pt = sema.pt; const zcu = pt.zcu; const gpa = zcu.gpa; @@ -3015,7 +3019,10 @@ pub fn createTypeName( switch (name_strategy) { .anon => {}, // handled after switch - .parent => return block.type_name_ctx, + .parent => return .{ + .name = block.type_name_ctx, + .nav = sema.owner.unwrap().nav_val.toOptional(), + }, .func => func_strat: { const fn_info = sema.code.getFnInfo(ip.funcZirBodyInst(sema.func_index).resolve(ip) orelse return error.AnalysisFail); const zir_tags = sema.code.instructions.items(.tag); @@ -3057,7 +3064,10 @@ pub fn createTypeName( }; try writer.writeByte(')'); - return ip.getOrPutString(gpa, pt.tid, buf.items, .no_embedded_nulls); + return .{ + .name = try ip.getOrPutString(gpa, pt.tid, buf.items, .no_embedded_nulls), + .nav = .none, + }; }, .dbg_var => { // TODO: this logic is questionable. We ideally should be traversing the `Block` rather than relying on the order of AstGen instructions. @@ -3066,9 +3076,12 @@ pub fn createTypeName( const zir_data = sema.code.instructions.items(.data); for (@intFromEnum(inst.?)..zir_tags.len) |i| switch (zir_tags[i]) { .dbg_var_ptr, .dbg_var_val => if (zir_data[i].str_op.operand == ref) { - return ip.getOrPutStringFmt(gpa, pt.tid, "{}.{s}", .{ - block.type_name_ctx.fmt(ip), zir_data[i].str_op.getStr(sema.code), - }, .no_embedded_nulls); + return .{ + .name = try ip.getOrPutStringFmt(gpa, pt.tid, "{}.{s}", .{ + block.type_name_ctx.fmt(ip), zir_data[i].str_op.getStr(sema.code), + }, .no_embedded_nulls), + .nav = .none, + }; }, else => {}, }; @@ -3086,9 +3099,12 @@ pub fn createTypeName( // types appropriately. However, `@typeName` becomes a problem then. If we remove // that builtin from the language, we can consider this. - return ip.getOrPutStringFmt(gpa, pt.tid, "{}__{s}_{d}", .{ - block.type_name_ctx.fmt(ip), anon_prefix, @intFromEnum(type_index), - }, .no_embedded_nulls); + return .{ + .name = try ip.getOrPutStringFmt(gpa, pt.tid, "{}__{s}_{d}", .{ + block.type_name_ctx.fmt(ip), anon_prefix, @intFromEnum(type_index), + }, .no_embedded_nulls), + .nav = .none, + }; } fn zirEnumDecl( @@ -3209,7 +3225,7 @@ fn zirEnumDecl( inst, wip_ty.index, ); - wip_ty.setName(ip, type_name); + wip_ty.setName(ip, type_name.name, type_name.nav); const new_namespace_index: InternPool.NamespaceIndex = try pt.createNamespace(.{ .parent = block.namespace.toOptional(), @@ -3236,7 +3252,7 @@ fn zirEnumDecl( inst, tracked_inst, new_namespace_index, - type_name, + type_name.name, small, body, tag_type_ref, @@ -3340,13 +3356,14 @@ fn zirUnionDecl( }; errdefer wip_ty.cancel(ip, pt.tid); - wip_ty.setName(ip, try sema.createTypeName( + const type_name = try sema.createTypeName( block, small.name_strategy, "union", inst, wip_ty.index, - )); + ); + wip_ty.setName(ip, type_name.name, type_name.nav); const new_namespace_index: InternPool.NamespaceIndex = try pt.createNamespace(.{ .parent = block.namespace.toOptional(), @@ -3432,13 +3449,14 @@ fn zirOpaqueDecl( }; errdefer wip_ty.cancel(ip, pt.tid); - wip_ty.setName(ip, try sema.createTypeName( + const type_name = try sema.createTypeName( block, small.name_strategy, "opaque", inst, wip_ty.index, - )); + ); + wip_ty.setName(ip, type_name.name, type_name.nav); const new_namespace_index: InternPool.NamespaceIndex = try pt.createNamespace(.{ .parent = block.namespace.toOptional(), @@ -20062,7 +20080,8 @@ fn structInitAnon( }, false)) { .wip => |wip| ty: { errdefer wip.cancel(ip, pt.tid); - wip.setName(ip, try sema.createTypeName(block, .anon, "struct", inst, wip.index)); + const type_name = try sema.createTypeName(block, .anon, "struct", inst, wip.index); + wip.setName(ip, type_name.name, type_name.nav); const struct_type = ip.loadStructType(wip.index); @@ -21122,13 +21141,14 @@ fn zirReify( }; errdefer wip_ty.cancel(ip, pt.tid); - wip_ty.setName(ip, try sema.createTypeName( + const type_name = try sema.createTypeName( block, name_strategy, "opaque", inst, wip_ty.index, - )); + ); + wip_ty.setName(ip, type_name.name, type_name.nav); const new_namespace_index = try pt.createNamespace(.{ .parent = block.namespace.toOptional(), @@ -21327,13 +21347,14 @@ fn reifyEnum( return sema.fail(block, src, "Type.Enum.tag_type must be an integer type", .{}); } - wip_ty.setName(ip, try sema.createTypeName( + const type_name = try sema.createTypeName( block, name_strategy, "enum", inst, wip_ty.index, - )); + ); + wip_ty.setName(ip, type_name.name, type_name.nav); const new_namespace_index = try pt.createNamespace(.{ .parent = block.namespace.toOptional(), @@ -21498,7 +21519,7 @@ fn reifyUnion( inst, wip_ty.index, ); - wip_ty.setName(ip, type_name); + wip_ty.setName(ip, type_name.name, type_name.nav); const field_types = try sema.arena.alloc(InternPool.Index, fields_len); const field_aligns = if (any_aligns) try sema.arena.alloc(InternPool.Alignment, fields_len) else undefined; @@ -21591,7 +21612,7 @@ fn reifyUnion( } } - const enum_tag_ty = try sema.generateUnionTagTypeSimple(block, field_names.keys(), wip_ty.index, type_name); + const enum_tag_ty = try sema.generateUnionTagTypeSimple(block, field_names.keys(), wip_ty.index, type_name.name); break :tag_ty .{ enum_tag_ty, false }; }; errdefer if (!has_explicit_tag) ip.remove(pt.tid, enum_tag_ty); // remove generated tag type on error @@ -21853,13 +21874,14 @@ fn reifyStruct( }; errdefer wip_ty.cancel(ip, pt.tid); - wip_ty.setName(ip, try sema.createTypeName( + const type_name = try sema.createTypeName( block, name_strategy, "struct", inst, wip_ty.index, - )); + ); + wip_ty.setName(ip, type_name.name, type_name.nav); const struct_type = ip.loadStructType(wip_ty.index); diff --git a/src/Sema/LowerZon.zig b/src/Sema/LowerZon.zig index 192c2e2d564d2b8ade6604656a184db433a5afc2..b8064cefbf3fcf41e75c3090726375036d19c3f6 100644 --- a/src/Sema/LowerZon.zig +++ b/src/Sema/LowerZon.zig @@ -157,13 +157,14 @@ fn lowerExprAnonResTy(self: *LowerZon, node: Zoir.Node.Index) CompileError!Inter )) { .wip => |wip| ty: { errdefer wip.cancel(ip, pt.tid); - wip.setName(ip, try self.sema.createTypeName( + const type_name = try self.sema.createTypeName( self.block, .anon, "struct", self.base_node_inst.resolve(ip), wip.index, - )); + ); + wip.setName(ip, type_name.name, type_name.nav); const struct_type = ip.loadStructType(wip.index); diff --git a/src/Zcu/PerThread.zig b/src/Zcu/PerThread.zig index b5eaca039766709cb3384487896b65d4c705212b..6f0eeba8645bd99bc87e803fb4ea6b340cdda053 100644 --- a/src/Zcu/PerThread.zig +++ b/src/Zcu/PerThread.zig @@ -1787,7 +1787,7 @@ fn createFileRootStruct( }; errdefer wip_ty.cancel(ip, pt.tid); - wip_ty.setName(ip, try file.internFullyQualifiedName(pt)); + wip_ty.setName(ip, try file.internFullyQualifiedName(pt), .none); ip.namespacePtr(namespace_index).owner_type = wip_ty.index; if (zcu.comp.incremental) { @@ -3976,7 +3976,7 @@ fn recreateStructType( }; errdefer wip_ty.cancel(ip, pt.tid); - wip_ty.setName(ip, struct_obj.name); + wip_ty.setName(ip, struct_obj.name, struct_obj.name_nav); try pt.addDependency(.wrap(.{ .type = wip_ty.index }), .{ .src_hash = key.zir_index }); zcu.namespacePtr(struct_obj.namespace).owner_type = wip_ty.index; // No need to re-scan the namespace -- `zirStructDecl` will ultimately do that if the type is still alive. @@ -4068,7 +4068,7 @@ fn recreateUnionType( }; errdefer wip_ty.cancel(ip, pt.tid); - wip_ty.setName(ip, union_obj.name); + wip_ty.setName(ip, union_obj.name, union_obj.name_nav); try pt.addDependency(.wrap(.{ .type = wip_ty.index }), .{ .src_hash = key.zir_index }); zcu.namespacePtr(namespace_index).owner_type = wip_ty.index; // No need to re-scan the namespace -- `zirUnionDecl` will ultimately do that if the type is still alive. @@ -4177,7 +4177,7 @@ fn recreateEnumType( var done = true; errdefer if (!done) wip_ty.cancel(ip, pt.tid); - wip_ty.setName(ip, enum_obj.name); + wip_ty.setName(ip, enum_obj.name, enum_obj.name_nav); zcu.namespacePtr(namespace_index).owner_type = wip_ty.index; // No need to re-scan the namespace -- `zirEnumDecl` will ultimately do that if the type is still alive. -- 2.54.0 From c95b1bf2d3c3ae7595e15ad521952b77d5063801 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Sat, 7 Jun 2025 04:51:26 -0400 Subject: [PATCH 14/35] x86_64: remove air references from mir --- lib/std/zig/Zir.zig | 9 + src/Air.zig | 4 +- src/Air/print.zig | 5 +- src/Compilation.zig | 3 - src/Sema.zig | 22 +- src/Type.zig | 24 +- src/Zcu/PerThread.zig | 14 +- src/arch/aarch64/CodeGen.zig | 25 +- src/arch/aarch64/Mir.zig | 2 - src/arch/arm/CodeGen.zig | 26 +- src/arch/arm/Mir.zig | 2 - src/arch/riscv64/CodeGen.zig | 22 +- src/arch/riscv64/Mir.zig | 2 - src/arch/sparc64/CodeGen.zig | 30 +- src/arch/sparc64/Mir.zig | 2 - src/arch/wasm/CodeGen.zig | 4 +- src/arch/x86_64/CodeGen.zig | 564 +++++++++++++++++------------------ src/arch/x86_64/Emit.zig | 192 ++++++------ src/arch/x86_64/Lower.zig | 38 ++- src/arch/x86_64/Mir.zig | 119 ++++---- src/codegen.zig | 6 +- src/codegen/llvm.zig | 15 +- src/link.zig | 13 +- src/link/C.zig | 6 - src/link/Coff.zig | 5 - src/link/Dwarf.zig | 177 +++++++++-- src/link/Elf.zig | 4 +- src/link/Elf/ZigObject.zig | 5 - src/link/Goff.zig | 3 - src/link/MachO.zig | 4 +- src/link/MachO/ZigObject.zig | 5 - src/link/Plan9.zig | 4 - src/link/Wasm.zig | 3 - src/link/Xcoff.zig | 3 - 34 files changed, 745 insertions(+), 617 deletions(-) diff --git a/lib/std/zig/Zir.zig b/lib/std/zig/Zir.zig index 440b4df9fc54325bb162dcbb24a54d01ec0f2511..17643e6f1e7c45b3b7247ce2946748f4e1c9681b 100644 --- a/lib/std/zig/Zir.zig +++ b/lib/std/zig/Zir.zig @@ -4861,6 +4861,15 @@ pub fn getParamBody(zir: Zir, fn_inst: Inst.Index) []const Zir.Inst.Index { } } +pub fn getParamName(zir: Zir, param_inst: Inst.Index) ?NullTerminatedString { + const inst = zir.instructions.get(@intFromEnum(param_inst)); + return switch (inst.tag) { + .param, .param_comptime => zir.extraData(Inst.Param, inst.data.pl_tok.payload_index).data.name, + .param_anytype, .param_anytype_comptime => inst.data.str_tok.start, + else => null, + }; +} + pub fn getFnInfo(zir: Zir, fn_inst: Inst.Index) FnInfo { const tags = zir.instructions.items(.tag); const datas = zir.instructions.items(.data); diff --git a/src/Air.zig b/src/Air.zig index 4810766e710d0dfd9d90bf0b9426fcac60bfccb2..74f4c5742486ce99a47f56b1ee55e57197748973 100644 --- a/src/Air.zig +++ b/src/Air.zig @@ -1153,9 +1153,7 @@ pub const Inst = struct { ty: Type, arg: struct { ty: Ref, - /// Index into `extra` of a null-terminated string representing the parameter name. - /// This is `.none` if debug info is stripped. - name: NullTerminatedString, + zir_param_index: u32, }, ty_op: struct { ty: Ref, diff --git a/src/Air/print.zig b/src/Air/print.zig index 343c640a6312ef2c06b344c4f53d24cbb59ea273..7f5f396cae148cdbf2c7e4588f541efaf07d919c 100644 --- a/src/Air/print.zig +++ b/src/Air/print.zig @@ -363,10 +363,7 @@ const Writer = struct { fn writeArg(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { const arg = w.air.instructions.items(.data)[@intFromEnum(inst)].arg; try w.writeType(s, arg.ty.toType()); - switch (arg.name) { - .none => {}, - _ => try s.print(", \"{}\"", .{std.zig.fmtEscapes(arg.name.toSlice(w.air))}), - } + try s.print(", {d}", .{arg.zir_param_index}); } fn writeTyOp(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { diff --git a/src/Compilation.zig b/src/Compilation.zig index b9b51222eb4892514d686706151c5baea9b3505c..fe4671848d672ca266a85b01891ac504c5a41dd9 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -4589,10 +4589,8 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job) JobError!void { comp.dispatchZcuLinkTask(tid, .{ .link_func = .{ .func = func.func, .mir = shared_mir, - .air = undefined, } }); } else { - const emit_needs_air = !zcu.backendSupportsFeature(.separate_thread); { const pt: Zcu.PerThread = .activate(comp.zcu.?, @enumFromInt(tid)); defer pt.deactivate(); @@ -4602,7 +4600,6 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job) JobError!void { comp.dispatchZcuLinkTask(tid, .{ .link_func = .{ .func = func.func, .mir = shared_mir, - .air = if (emit_needs_air) &air else undefined, } }); air.deinit(gpa); } diff --git a/src/Sema.zig b/src/Sema.zig index 87837d96d57158eeb6cf3a11d1e19abe4cfdea78..97c9217a5e18bd26253c997e3cee2ac8265fa212 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -35088,24 +35088,24 @@ pub fn resolveUnionLayout(sema: *Sema, ty: Type) SemaError!void { var max_align: Alignment = .@"1"; for (0..union_type.field_types.len) |field_index| { const field_ty: Type = .fromInterned(union_type.field_types.get(ip)[field_index]); + if (field_ty.isNoReturn(pt.zcu)) continue; - if (try field_ty.comptimeOnlySema(pt) or field_ty.zigTypeTag(pt.zcu) == .noreturn) continue; // TODO: should this affect alignment? - - max_size = @max(max_size, field_ty.abiSizeSema(pt) catch |err| switch (err) { - error.AnalysisFail => { - const msg = sema.err orelse return err; - try sema.addFieldErrNote(ty, field_index, msg, "while checking this field", .{}); - return err; - }, - else => return err, - }); + if (try field_ty.hasRuntimeBitsSema(pt)) { + max_size = @max(max_size, field_ty.abiSizeSema(pt) catch |err| switch (err) { + error.AnalysisFail => { + const msg = sema.err orelse return err; + try sema.addFieldErrNote(ty, field_index, msg, "while checking this field", .{}); + return err; + }, + else => return err, + }); + } const explicit_align = union_type.fieldAlign(ip, field_index); const field_align = if (explicit_align != .none) explicit_align else try field_ty.abiAlignmentSema(pt); - max_align = max_align.max(field_align); } diff --git a/src/Type.zig b/src/Type.zig index 00f1c701297b1bfc8173d5ec6547df40c62ddba4..64b389cf5f1d4e0de795085f6fc0b26356b79909 100644 --- a/src/Type.zig +++ b/src/Type.zig @@ -177,6 +177,7 @@ pub fn print(ty: Type, writer: anytype, pt: Zcu.PerThread) @TypeOf(writer).Error const zcu = pt.zcu; const ip = &zcu.intern_pool; switch (ip.indexToKey(ty.toIntern())) { + .undef => return writer.writeAll("@as(type, undefined)"), .int_type => |int_type| { const sign_char: u8 = switch (int_type.signedness) { .signed => 'i', @@ -398,7 +399,6 @@ pub fn print(ty: Type, writer: anytype, pt: Zcu.PerThread) @TypeOf(writer).Error }, // values, not types - .undef, .simple_value, .variable, .@"extern", @@ -3921,23 +3921,25 @@ pub fn getUnionLayout(loaded_union: InternPool.LoadedUnionType, zcu: *const Zcu) var payload_size: u64 = 0; var payload_align: InternPool.Alignment = .@"1"; for (loaded_union.field_types.get(ip), 0..) |field_ty, field_index| { - if (!Type.fromInterned(field_ty).hasRuntimeBitsIgnoreComptime(zcu)) continue; + if (Type.fromInterned(field_ty).isNoReturn(zcu)) continue; const explicit_align = loaded_union.fieldAlign(ip, field_index); const field_align = if (explicit_align != .none) explicit_align else Type.fromInterned(field_ty).abiAlignment(zcu); - const field_size = Type.fromInterned(field_ty).abiSize(zcu); - if (field_size > payload_size) { - payload_size = field_size; - biggest_field = @intCast(field_index); - } - if (field_align.compare(.gte, payload_align)) { - payload_align = field_align; - most_aligned_field = @intCast(field_index); - most_aligned_field_size = field_size; + if (Type.fromInterned(field_ty).hasRuntimeBits(zcu)) { + const field_size = Type.fromInterned(field_ty).abiSize(zcu); + if (field_size > payload_size) { + payload_size = field_size; + biggest_field = @intCast(field_index); + } + if (field_align.compare(.gte, payload_align)) { + most_aligned_field = @intCast(field_index); + most_aligned_field_size = field_size; + } } + payload_align = payload_align.max(field_align); } const have_tag = loaded_union.flagsUnordered(ip).runtime_tag.hasTag(); if (!have_tag or !Type.fromInterned(loaded_union.enum_tag_ty).hasRuntimeBits(zcu)) { diff --git a/src/Zcu/PerThread.zig b/src/Zcu/PerThread.zig index 6f0eeba8645bd99bc87e803fb4ea6b340cdda053..f8efa40dc008e17bd27674129a9d6f0792a24b91 100644 --- a/src/Zcu/PerThread.zig +++ b/src/Zcu/PerThread.zig @@ -2893,17 +2893,10 @@ fn analyzeFnBodyInner(pt: Zcu.PerThread, func_index: InternPool.Index) Zcu.SemaE runtime_params_len; var runtime_param_index: usize = 0; - for (fn_info.param_body[0..src_params_len]) |inst| { + for (fn_info.param_body[0..src_params_len], 0..) |inst, zir_param_index| { const gop = sema.inst_map.getOrPutAssumeCapacity(inst); if (gop.found_existing) continue; // provided above by comptime arg - const param_inst_info = sema.code.instructions.get(@intFromEnum(inst)); - const param_name: Zir.NullTerminatedString = switch (param_inst_info.tag) { - .param_anytype => param_inst_info.data.str_tok.start, - .param => sema.code.extraData(Zir.Inst.Param, param_inst_info.data.pl_tok.payload_index).data.name, - else => unreachable, - }; - const param_ty = fn_ty_info.param_types.get(ip)[runtime_param_index]; runtime_param_index += 1; @@ -2923,10 +2916,7 @@ fn analyzeFnBodyInner(pt: Zcu.PerThread, func_index: InternPool.Index) Zcu.SemaE .tag = .arg, .data = .{ .arg = .{ .ty = Air.internedToRef(param_ty), - .name = if (inner_block.ownerModule().strip) - .none - else - try sema.appendAirString(sema.code.nullTerminatedString(param_name)), + .zir_param_index = @intCast(zir_param_index), } }, }); } diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 0c29fd96e2b466c99099fe8d49ebabe281b36dbf..4aaf6bf85c32c7040235ad63f4ad6d20652b5df7 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -4208,15 +4208,22 @@ fn airArg(self: *Self, inst: Air.Inst.Index) InnerError!void { while (self.args[arg_index] == .none) arg_index += 1; self.arg_index = arg_index + 1; - const ty = self.typeOfIndex(inst); - const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)]; - const name = self.air.instructions.items(.data)[@intFromEnum(inst)].arg.name; - if (name != .none) try self.dbg_info_relocs.append(self.gpa, .{ - .tag = tag, - .ty = ty, - .name = name.toSlice(self.air), - .mcv = self.args[arg_index], - }); + const zcu = self.pt.zcu; + const func_zir = zcu.funcInfo(self.func_index).zir_body_inst.resolveFull(&zcu.intern_pool).?; + const file = zcu.fileByIndex(func_zir.file); + if (!file.mod.?.strip) { + const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)]; + const arg = self.air.instructions.items(.data)[@intFromEnum(inst)].arg; + const ty = self.typeOfIndex(inst); + const zir = &file.zir.?; + const name = zir.nullTerminatedString(zir.getParamName(zir.getParamBody(func_zir.inst)[arg.zir_param_index]).?); + try self.dbg_info_relocs.append(self.gpa, .{ + .tag = tag, + .ty = ty, + .name = name, + .mcv = self.args[arg_index], + }); + } const result: MCValue = if (self.liveness.isUnused(inst)) .dead else self.args[arg_index]; return self.finishAir(inst, result, .{ .none, .none, .none }); diff --git a/src/arch/aarch64/Mir.zig b/src/arch/aarch64/Mir.zig index 34fcc64c7ea6c844f0d5cfa5b800ee191857849a..88089c84883bf4ebe1cfae5510a92c387da89913 100644 --- a/src/arch/aarch64/Mir.zig +++ b/src/arch/aarch64/Mir.zig @@ -514,9 +514,7 @@ pub fn emit( func_index: InternPool.Index, code: *std.ArrayListUnmanaged(u8), debug_output: link.File.DebugInfoOutput, - air: *const @import("../../Air.zig"), ) codegen.CodeGenError!void { - _ = air; // using this would be a bug const zcu = pt.zcu; const func = zcu.funcInfo(func_index); const nav = func.owner_nav; diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index 3868011557b35b8d8d27cd39399ca46e0ccab260..09304bf1def99dc837ca98faff48c85ea4b5fce4 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -4191,16 +4191,22 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { while (self.args[arg_index] == .none) arg_index += 1; self.arg_index = arg_index + 1; - const ty = self.typeOfIndex(inst); - const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)]; - - const name = self.air.instructions.items(.data)[@intFromEnum(inst)].arg.name; - if (name != .none) try self.dbg_info_relocs.append(self.gpa, .{ - .tag = tag, - .ty = ty, - .name = name.toSlice(self.air), - .mcv = self.args[arg_index], - }); + const zcu = self.pt.zcu; + const func_zir = zcu.funcInfo(self.func_index).zir_body_inst.resolveFull(&zcu.intern_pool).?; + const file = zcu.fileByIndex(func_zir.file); + if (!file.mod.?.strip) { + const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)]; + const arg = self.air.instructions.items(.data)[@intFromEnum(inst)].arg; + const ty = self.typeOfIndex(inst); + const zir = &file.zir.?; + const name = zir.nullTerminatedString(zir.getParamName(zir.getParamBody(func_zir.inst)[arg.zir_param_index]).?); + try self.dbg_info_relocs.append(self.gpa, .{ + .tag = tag, + .ty = ty, + .name = name, + .mcv = self.args[arg_index], + }); + } const result: MCValue = if (self.liveness.isUnused(inst)) .dead else self.args[arg_index]; return self.finishAir(inst, result, .{ .none, .none, .none }); diff --git a/src/arch/arm/Mir.zig b/src/arch/arm/Mir.zig index 0366663eae818ba91000dcffbfb48b532d75ba4e..5b7585a2ca9cd9294e1ef3f6afbfb72f1b7cfea2 100644 --- a/src/arch/arm/Mir.zig +++ b/src/arch/arm/Mir.zig @@ -294,9 +294,7 @@ pub fn emit( func_index: InternPool.Index, code: *std.ArrayListUnmanaged(u8), debug_output: link.File.DebugInfoOutput, - air: *const @import("../../Air.zig"), ) codegen.CodeGenError!void { - _ = air; // using this would be a bug const zcu = pt.zcu; const func = zcu.funcInfo(func_index); const nav = func.owner_nav; diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index 9b5e0ed69b98de0fe51b7a20256a9151decee43a..080760bbabc75d0ac972bdac5010ebc36e9e5c74 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -70,6 +70,7 @@ mod: *Package.Module, target: *const std.Target, args: []MCValue, ret_mcv: InstTracking, +func_index: InternPool.Index, fn_type: Type, arg_index: usize, src_loc: Zcu.LazySrcLoc, @@ -774,6 +775,7 @@ pub fn generate( .owner = .{ .nav_index = func.owner_nav }, .args = undefined, // populated after `resolveCallingConventionValues` .ret_mcv = undefined, // populated after `resolveCallingConventionValues` + .func_index = func_index, .fn_type = fn_type, .arg_index = 0, .branch_stack = &branch_stack, @@ -877,6 +879,7 @@ pub fn generateLazy( .owner = .{ .lazy_sym = lazy_sym }, .args = undefined, // populated after `resolveCallingConventionValues` .ret_mcv = undefined, // populated after `resolveCallingConventionValues` + .func_index = undefined, .fn_type = undefined, .arg_index = 0, .branch_stack = undefined, @@ -4724,10 +4727,8 @@ fn airFieldParentPtr(func: *Func, inst: Air.Inst.Index) !void { return func.fail("TODO implement codegen airFieldParentPtr", .{}); } -fn genArgDbgInfo(func: *const Func, inst: Air.Inst.Index, mcv: MCValue) InnerError!void { - const arg = func.air.instructions.items(.data)[@intFromEnum(inst)].arg; - const ty = arg.ty.toType(); - if (arg.name == .none) return; +fn genArgDbgInfo(func: *const Func, name: []const u8, ty: Type, mcv: MCValue) InnerError!void { + assert(!func.mod.strip); // TODO: Add a pseudo-instruction or something to defer this work until Emit. // We aren't allowed to interact with linker state here. @@ -4736,7 +4737,7 @@ fn genArgDbgInfo(func: *const Func, inst: Air.Inst.Index, mcv: MCValue) InnerErr .dwarf => |dw| switch (mcv) { .register => |reg| dw.genLocalDebugInfo( .local_arg, - arg.name.toSlice(func.air), + name, ty, .{ .reg = reg.dwarfNum() }, ) catch |err| return func.fail("failed to generate debug info: {s}", .{@errorName(err)}), @@ -4749,6 +4750,8 @@ fn genArgDbgInfo(func: *const Func, inst: Air.Inst.Index, mcv: MCValue) InnerErr } fn airArg(func: *Func, inst: Air.Inst.Index) InnerError!void { + const zcu = func.pt.zcu; + var arg_index = func.arg_index; // we skip over args that have no bits @@ -4765,7 +4768,14 @@ fn airArg(func: *Func, inst: Air.Inst.Index) InnerError!void { try func.genCopy(arg_ty, dst_mcv, src_mcv); - try func.genArgDbgInfo(inst, src_mcv); + const arg = func.air.instructions.items(.data)[@intFromEnum(inst)].arg; + // can delete `func.func_index` if this logic is moved to emit + const func_zir = zcu.funcInfo(func.func_index).zir_body_inst.resolveFull(&zcu.intern_pool).?; + const file = zcu.fileByIndex(func_zir.file); + const zir = &file.zir.?; + const name = zir.nullTerminatedString(zir.getParamName(zir.getParamBody(func_zir.inst)[arg.zir_param_index]).?); + + try func.genArgDbgInfo(name, arg_ty, src_mcv); break :result dst_mcv; }; diff --git a/src/arch/riscv64/Mir.zig b/src/arch/riscv64/Mir.zig index eef3fe75116738bc851b9765a63b65792aee6c85..2ad75e4677990fba1b1abb8c321d50450c22bdcc 100644 --- a/src/arch/riscv64/Mir.zig +++ b/src/arch/riscv64/Mir.zig @@ -117,9 +117,7 @@ pub fn emit( func_index: InternPool.Index, code: *std.ArrayListUnmanaged(u8), debug_output: link.File.DebugInfoOutput, - air: *const @import("../../Air.zig"), ) codegen.CodeGenError!void { - _ = air; // using this would be a bug const zcu = pt.zcu; const comp = zcu.comp; const gpa = comp.gpa; diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index 180aaedd3cbb9d23d023f2b62a90b92d223399cc..b35f45dd6456702b876935cedcf06956b0573d06 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -995,23 +995,29 @@ fn airArg(self: *Self, inst: Air.Inst.Index) InnerError!void { self.arg_index += 1; const ty = self.typeOfIndex(inst); - - const arg = self.args[arg_index]; - const mcv = blk: { - switch (arg) { + const mcv: MCValue = blk: { + switch (self.args[arg_index]) { .stack_offset => |off| { const abi_size = math.cast(u32, ty.abiSize(zcu)) orelse { return self.fail("type '{}' too big to fit into stack frame", .{ty.fmt(pt)}); }; const offset = off + abi_size; - break :blk MCValue{ .stack_offset = offset }; + break :blk .{ .stack_offset = offset }; }, - else => break :blk arg, + else => |mcv| break :blk mcv, } }; - self.genArgDbgInfo(inst, mcv) catch |err| - return self.fail("failed to generate debug info for parameter: {s}", .{@errorName(err)}); + const func_zir = zcu.funcInfo(self.func_index).zir_body_inst.resolveFull(&zcu.intern_pool).?; + const file = zcu.fileByIndex(func_zir.file); + if (!file.mod.?.strip) { + const arg = self.air.instructions.items(.data)[@intFromEnum(inst)].arg; + const zir = &file.zir.?; + const name = zir.nullTerminatedString(zir.getParamName(zir.getParamBody(func_zir.inst)[arg.zir_param_index]).?); + + self.genArgDbgInfo(name, ty, mcv) catch |err| + return self.fail("failed to generate debug info for parameter: {s}", .{@errorName(err)}); + } if (self.liveness.isUnused(inst)) return self.finishAirBookkeeping(); @@ -3539,11 +3545,7 @@ fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Air. self.finishAirBookkeeping(); } -fn genArgDbgInfo(self: Self, inst: Air.Inst.Index, mcv: MCValue) !void { - const arg = self.air.instructions.items(.data)[@intFromEnum(inst)].arg; - const ty = arg.ty.toType(); - if (arg.name == .none) return; - +fn genArgDbgInfo(self: Self, name: []const u8, ty: Type, mcv: MCValue) !void { // TODO: Add a pseudo-instruction or something to defer this work until Emit. // We aren't allowed to interact with linker state here. if (true) return; @@ -3551,7 +3553,7 @@ fn genArgDbgInfo(self: Self, inst: Air.Inst.Index, mcv: MCValue) !void { .dwarf => |dw| switch (mcv) { .register => |reg| try dw.genLocalDebugInfo( .local_arg, - arg.name.toSlice(self.air), + name, ty, .{ .reg = reg.dwarfNum() }, ), diff --git a/src/arch/sparc64/Mir.zig b/src/arch/sparc64/Mir.zig index 26c5c3267b272317919fd517f607c1b9b7d47b40..842ac10fed1af8fbaeadb7b2c0404a20f806abc0 100644 --- a/src/arch/sparc64/Mir.zig +++ b/src/arch/sparc64/Mir.zig @@ -382,9 +382,7 @@ pub fn emit( func_index: InternPool.Index, code: *std.ArrayListUnmanaged(u8), debug_output: link.File.DebugInfoOutput, - air: *const @import("../../Air.zig"), ) codegen.CodeGenError!void { - _ = air; // using this would be a bug const zcu = pt.zcu; const func = zcu.funcInfo(func_index); const nav = func.owner_nav; diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 29939235895b22078fd84b2fe87956f8fc7986e2..2936025a4997f26e78dda315426e6563f31a8e55 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -1877,7 +1877,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { .dbg_inline_block => cg.airDbgInlineBlock(inst), .dbg_var_ptr => cg.airDbgVar(inst, .local_var, true), .dbg_var_val => cg.airDbgVar(inst, .local_var, false), - .dbg_arg_inline => cg.airDbgVar(inst, .local_arg, false), + .dbg_arg_inline => cg.airDbgVar(inst, .arg, false), .call => cg.airCall(inst, .auto), .call_always_tail => cg.airCall(inst, .always_tail), @@ -6427,7 +6427,7 @@ fn airDbgInlineBlock(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { fn airDbgVar( cg: *CodeGen, inst: Air.Inst.Index, - local_tag: link.File.Dwarf.WipNav.LocalTag, + local_tag: link.File.Dwarf.WipNav.LocalVarTag, is_ptr: bool, ) InnerError!void { _ = is_ptr; diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 1d95c8db77589c0704e1bbe6c6798a2ab2615931..7d88307ba5a7b01628bcb6059b018823bd057a83 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -129,7 +129,6 @@ target: *const std.Target, owner: Owner, inline_func: InternPool.Index, mod: *Module, -arg_index: u32, args: []MCValue, va_info: union { sysv: struct { @@ -151,6 +150,8 @@ eflags_inst: ?Air.Inst.Index = null, mir_instructions: std.MultiArrayList(Mir.Inst) = .empty, /// MIR extra data mir_extra: std.ArrayListUnmanaged(u32) = .empty, +mir_local_name_bytes: std.ArrayListUnmanaged(u8) = .empty, +mir_local_types: std.ArrayListUnmanaged(InternPool.Index) = .empty, mir_table: std.ArrayListUnmanaged(Mir.Inst.Index) = .empty, /// The value is an offset into the `Function` `code` from the beginning. @@ -978,8 +979,10 @@ pub fn generate( const gpa = zcu.gpa; const ip = &zcu.intern_pool; const func = zcu.funcInfo(func_index); + const func_zir = func.zir_body_inst.resolveFull(ip).?; + const file = zcu.fileByIndex(func_zir.file); const fn_type: Type = .fromInterned(func.ty); - const mod = zcu.navFileScope(func.owner_nav).mod.?; + const mod = file.mod.?; var function: CodeGen = .{ .gpa = gpa, @@ -991,7 +994,6 @@ pub fn generate( .bin_file = bin_file, .owner = .{ .nav_index = func.owner_nav }, .inline_func = func_index, - .arg_index = undefined, .args = undefined, // populated after `resolveCallingConventionValues` .va_info = undefined, // populated after `resolveCallingConventionValues` .ret_mcv = undefined, // populated after `resolveCallingConventionValues` @@ -1011,6 +1013,8 @@ pub fn generate( function.inst_tracking.deinit(gpa); function.epilogue_relocs.deinit(gpa); function.mir_instructions.deinit(gpa); + function.mir_local_name_bytes.deinit(gpa); + function.mir_local_types.deinit(gpa); function.mir_extra.deinit(gpa); function.mir_table.deinit(gpa); } @@ -1078,7 +1082,7 @@ pub fn generate( ); } - function.gen() catch |err| switch (err) { + function.gen(&file.zir.?, func_zir.inst, func.comptime_args, call_info.air_arg_count) catch |err| switch (err) { error.CodegenFail => return error.CodegenFail, error.OutOfRegisters => return function.fail("ran out of registers (Zig compiler bug)", .{}), else => |e| return e, @@ -1097,17 +1101,32 @@ pub fn generate( var mir: Mir = .{ .instructions = .empty, .extra = &.{}, + .local_name_bytes = &.{}, + .local_types = &.{}, .table = &.{}, .frame_locs = .empty, }; errdefer mir.deinit(gpa); mir.instructions = function.mir_instructions.toOwnedSlice(); mir.extra = try function.mir_extra.toOwnedSlice(gpa); + mir.local_name_bytes = try function.mir_local_name_bytes.toOwnedSlice(gpa); + mir.local_types = try function.mir_local_types.toOwnedSlice(gpa); mir.table = try function.mir_table.toOwnedSlice(gpa); mir.frame_locs = function.frame_locs.toOwnedSlice(); return mir; } +pub fn toTmpMir(cg: *CodeGen) Mir { + return .{ + .instructions = cg.mir_instructions.slice(), + .extra = cg.mir_extra.items, + .local_name_bytes = cg.mir_local_name_bytes.items, + .local_types = cg.mir_local_types.items, + .table = cg.mir_table.items, + .frame_locs = cg.frame_locs.slice(), + }; +} + pub fn generateLazy( bin_file: *link.File, pt: Zcu.PerThread, @@ -1130,7 +1149,6 @@ pub fn generateLazy( .bin_file = bin_file, .owner = .{ .lazy_sym = lazy_sym }, .inline_func = undefined, - .arg_index = undefined, .args = undefined, .va_info = undefined, .ret_mcv = undefined, @@ -1141,6 +1159,8 @@ pub fn generateLazy( defer { function.inst_tracking.deinit(gpa); function.mir_instructions.deinit(gpa); + function.mir_local_name_bytes.deinit(gpa); + function.mir_local_types.deinit(gpa); function.mir_extra.deinit(gpa); function.mir_table.deinit(gpa); } @@ -1156,21 +1176,12 @@ pub fn generateLazy( else => |e| return e, }; - var mir: Mir = .{ - .instructions = function.mir_instructions.toOwnedSlice(), - .extra = try function.mir_extra.toOwnedSlice(gpa), - .table = try function.mir_table.toOwnedSlice(gpa), - .frame_locs = function.frame_locs.toOwnedSlice(), - }; - defer mir.deinit(gpa); - var emit: Emit = .{ - .air = function.air, .lower = .{ .bin_file = bin_file, .target = function.target, .allocator = gpa, - .mir = mir, + .mir = function.toTmpMir(), .cc = .auto, .src_loc = src_loc, .output_mode = comp.config.output_mode, @@ -1240,22 +1251,16 @@ fn formatWipMir( writer: anytype, ) @TypeOf(writer).Error!void { const comp = data.self.bin_file.comp; - const mod = comp.root_mod; var lower: Lower = .{ .bin_file = data.self.bin_file, .target = data.self.target, .allocator = data.self.gpa, - .mir = .{ - .instructions = data.self.mir_instructions.slice(), - .extra = data.self.mir_extra.items, - .table = data.self.mir_table.items, - .frame_locs = (std.MultiArrayList(Mir.FrameLoc){}).slice(), - }, + .mir = data.self.toTmpMir(), .cc = .auto, .src_loc = data.self.src_loc, .output_mode = comp.config.output_mode, .link_mode = comp.config.link_mode, - .pic = mod.pic, + .pic = data.self.mod.pic, }; var first = true; for ((lower.lowerMir(data.inst) catch |err| switch (err) { @@ -1291,7 +1296,9 @@ fn formatWipMir( .pseudo_dbg_epilogue_begin_none, .pseudo_dbg_enter_block_none, .pseudo_dbg_leave_block_none, + .pseudo_dbg_arg_none, .pseudo_dbg_var_args_none, + .pseudo_dbg_var_none, .pseudo_dead_none, => {}, .pseudo_dbg_line_stmt_line_column, .pseudo_dbg_line_line_column => try writer.print( @@ -1299,57 +1306,47 @@ fn formatWipMir( mir_inst.data.line_column, ), .pseudo_dbg_enter_inline_func, .pseudo_dbg_leave_inline_func => try writer.print(" {}", .{ - ip.getNav(ip.indexToKey(mir_inst.data.func).func.owner_nav).name.fmt(ip), + ip.getNav(ip.indexToKey(mir_inst.data.ip_index).func.owner_nav).name.fmt(ip), }), - .pseudo_dbg_local_a => try writer.print(" {}", .{mir_inst.data.a.air_inst}), - .pseudo_dbg_local_ai_s => try writer.print(" {}, {d}", .{ - mir_inst.data.ai.air_inst, - @as(i32, @bitCast(mir_inst.data.ai.i)), + .pseudo_dbg_arg_i_s, .pseudo_dbg_var_i_s => try writer.print(" {d}", .{ + @as(i32, @bitCast(mir_inst.data.i.i)), }), - .pseudo_dbg_local_ai_u => try writer.print(" {}, {d}", .{ - mir_inst.data.ai.air_inst, - mir_inst.data.ai.i, + .pseudo_dbg_arg_i_u, .pseudo_dbg_var_i_u => try writer.print(" {d}", .{ + mir_inst.data.i.i, }), - .pseudo_dbg_local_ai_64 => try writer.print(" {}, {d}", .{ - mir_inst.data.ai.air_inst, - lower.mir.extraData(Mir.Imm64, mir_inst.data.ai.i).data.decode(), + .pseudo_dbg_arg_i_64, .pseudo_dbg_var_i_64 => try writer.print(" {d}", .{ + mir_inst.data.i64, }), - .pseudo_dbg_local_as => { + .pseudo_dbg_arg_reloc, .pseudo_dbg_var_reloc => { const mem_op: encoder.Instruction.Operand = .{ .mem = .initSib(.qword, .{ - .base = .{ .reloc = mir_inst.data.as.sym_index }, + .base = .{ .reloc = mir_inst.data.reloc.sym_index }, + .disp = mir_inst.data.reloc.off, }) }; - try writer.print(" {}, {}", .{ mir_inst.data.as.air_inst, mem_op.fmt(.m) }); + try writer.print(" {}", .{mem_op.fmt(.m)}); }, - .pseudo_dbg_local_aso => { - const sym_off = lower.mir.extraData(bits.SymbolOffset, mir_inst.data.ax.payload).data; + .pseudo_dbg_arg_ro, .pseudo_dbg_var_ro => { const mem_op: encoder.Instruction.Operand = .{ .mem = .initSib(.qword, .{ - .base = .{ .reloc = sym_off.sym_index }, - .disp = sym_off.off, + .base = .{ .reg = mir_inst.data.ro.reg }, + .disp = mir_inst.data.ro.off, }) }; - try writer.print(" {}, {}", .{ mir_inst.data.ax.air_inst, mem_op.fmt(.m) }); + try writer.print(" {}", .{mem_op.fmt(.m)}); }, - .pseudo_dbg_local_aro => { - const air_off = lower.mir.extraData(Mir.AirOffset, mir_inst.data.rx.payload).data; + .pseudo_dbg_arg_fa, .pseudo_dbg_var_fa => { const mem_op: encoder.Instruction.Operand = .{ .mem = .initSib(.qword, .{ - .base = .{ .reg = mir_inst.data.rx.r1 }, - .disp = air_off.off, + .base = .{ .frame = mir_inst.data.fa.index }, + .disp = mir_inst.data.fa.off, }) }; - try writer.print(" {}, {}", .{ air_off.air_inst, mem_op.fmt(.m) }); + try writer.print(" {}", .{mem_op.fmt(.m)}); }, - .pseudo_dbg_local_af => { - const frame_addr = lower.mir.extraData(bits.FrameAddr, mir_inst.data.ax.payload).data; - const mem_op: encoder.Instruction.Operand = .{ .mem = .initSib(.qword, .{ - .base = .{ .frame = frame_addr.index }, - .disp = frame_addr.off, - }) }; - try writer.print(" {}, {}", .{ mir_inst.data.ax.air_inst, mem_op.fmt(.m) }); - }, - .pseudo_dbg_local_am => { + .pseudo_dbg_arg_m, .pseudo_dbg_var_m => { const mem_op: encoder.Instruction.Operand = .{ - .mem = lower.mir.extraData(Mir.Memory, mir_inst.data.ax.payload).data.decode(), + .mem = lower.mir.extraData(Mir.Memory, mir_inst.data.x.payload).data.decode(), }; - try writer.print(" {}, {}", .{ mir_inst.data.ax.air_inst, mem_op.fmt(.m) }); + try writer.print(" {}", .{mem_op.fmt(.m)}); }, + .pseudo_dbg_arg_val, .pseudo_dbg_var_val => try writer.print(" {}", .{ + Value.fromInterned(mir_inst.data.ip_index).fmtValue(data.self.pt), + }), } } } @@ -1640,124 +1637,6 @@ fn asmPlaceholder(self: *CodeGen) !Mir.Inst.Index { }); } -const MirTagAir = enum { dbg_local }; - -fn asmAir(self: *CodeGen, tag: MirTagAir, inst: Air.Inst.Index) !void { - _ = try self.addInst(.{ - .tag = .pseudo, - .ops = switch (tag) { - .dbg_local => .pseudo_dbg_local_a, - }, - .data = .{ .a = .{ .air_inst = inst } }, - }); -} - -fn asmAirImmediate(self: *CodeGen, tag: MirTagAir, inst: Air.Inst.Index, imm: Immediate) !void { - switch (imm) { - .signed => |s| _ = try self.addInst(.{ - .tag = .pseudo, - .ops = switch (tag) { - .dbg_local => .pseudo_dbg_local_ai_s, - }, - .data = .{ .ai = .{ - .air_inst = inst, - .i = @bitCast(s), - } }, - }), - .unsigned => |u| _ = if (std.math.cast(u32, u)) |small| try self.addInst(.{ - .tag = .pseudo, - .ops = switch (tag) { - .dbg_local => .pseudo_dbg_local_ai_u, - }, - .data = .{ .ai = .{ - .air_inst = inst, - .i = small, - } }, - }) else try self.addInst(.{ - .tag = .pseudo, - .ops = switch (tag) { - .dbg_local => .pseudo_dbg_local_ai_64, - }, - .data = .{ .ai = .{ - .air_inst = inst, - .i = try self.addExtra(Mir.Imm64.encode(u)), - } }, - }), - .reloc => |sym_off| _ = if (sym_off.off == 0) try self.addInst(.{ - .tag = .pseudo, - .ops = switch (tag) { - .dbg_local => .pseudo_dbg_local_as, - }, - .data = .{ .as = .{ - .air_inst = inst, - .sym_index = sym_off.sym_index, - } }, - }) else try self.addInst(.{ - .tag = .pseudo, - .ops = switch (tag) { - .dbg_local => .pseudo_dbg_local_aso, - }, - .data = .{ .ax = .{ - .air_inst = inst, - .payload = try self.addExtra(sym_off), - } }, - }), - } -} - -fn asmAirRegisterImmediate( - self: *CodeGen, - tag: MirTagAir, - inst: Air.Inst.Index, - reg: Register, - imm: Immediate, -) !void { - _ = try self.addInst(.{ - .tag = .pseudo, - .ops = switch (tag) { - .dbg_local => .pseudo_dbg_local_aro, - }, - .data = .{ .rx = .{ - .r1 = reg, - .payload = try self.addExtra(Mir.AirOffset{ - .air_inst = inst, - .off = imm.signed, - }), - } }, - }); -} - -fn asmAirFrameAddress( - self: *CodeGen, - tag: MirTagAir, - inst: Air.Inst.Index, - frame_addr: bits.FrameAddr, -) !void { - _ = try self.addInst(.{ - .tag = .pseudo, - .ops = switch (tag) { - .dbg_local => .pseudo_dbg_local_af, - }, - .data = .{ .ax = .{ - .air_inst = inst, - .payload = try self.addExtra(frame_addr), - } }, - }); -} - -fn asmAirMemory(self: *CodeGen, tag: MirTagAir, inst: Air.Inst.Index, m: Memory) !void { - _ = try self.addInst(.{ - .tag = .pseudo, - .ops = switch (tag) { - .dbg_local => .pseudo_dbg_local_am, - }, - .data = .{ .ax = .{ - .air_inst = inst, - .payload = try self.addExtra(Mir.Memory.encode(m)), - } }, - }); -} - fn asmOpOnly(self: *CodeGen, tag: Mir.Inst.FixedTag) !void { _ = try self.addInst(.{ .tag = tag[1], @@ -2233,7 +2112,13 @@ fn asmMemoryRegisterImmediate( }); } -fn gen(self: *CodeGen) InnerError!void { +fn gen( + self: *CodeGen, + zir: *const std.zig.Zir, + func_zir_inst: std.zig.Zir.Inst.Index, + comptime_args: InternPool.Index.Slice, + air_arg_count: u32, +) InnerError!void { const pt = self.pt; const zcu = pt.zcu; const fn_info = zcu.typeToFunc(self.fn_type).?; @@ -2303,7 +2188,7 @@ fn gen(self: *CodeGen) InnerError!void { if (!self.mod.strip) try self.asmPseudo(.pseudo_dbg_prologue_end_none); - try self.genBody(self.air.getMainBody()); + try self.genMainBody(zir, func_zir_inst, comptime_args, air_arg_count); const epilogue = if (self.epilogue_relocs.items.len > 0) epilogue: { var last_inst: Mir.Inst.Index = @intCast(self.mir_instructions.len - 1); @@ -2438,20 +2323,81 @@ fn gen(self: *CodeGen) InnerError!void { } } else { if (!self.mod.strip) try self.asmPseudo(.pseudo_dbg_prologue_end_none); - try self.genBody(self.air.getMainBody()); + try self.genMainBody(zir, func_zir_inst, comptime_args, air_arg_count); if (!self.mod.strip) try self.asmPseudo(.pseudo_dbg_epilogue_begin_none); } } -fn checkInvariantsAfterAirInst(self: *CodeGen) void { - assert(!self.register_manager.lockedRegsExist()); +fn genMainBody( + cg: *CodeGen, + zir: *const std.zig.Zir, + func_zir_inst: std.zig.Zir.Inst.Index, + comptime_args: InternPool.Index.Slice, + air_arg_count: u32, +) InnerError!void { + const pt = cg.pt; + const zcu = pt.zcu; + const ip = &zcu.intern_pool; + + const main_body = cg.air.getMainBody(); + const air_args_body = main_body[0..air_arg_count]; + try cg.genBody(air_args_body); + + if (!cg.mod.strip) { + var air_arg_index: usize = 0; + const fn_info = zcu.typeToFunc(cg.fn_type).?; + var fn_param_index: usize = 0; + try cg.mir_local_types.ensureTotalCapacity(cg.gpa, fn_info.param_types.len); + var zir_param_index: usize = 0; + for (zir.getParamBody(func_zir_inst)) |zir_param_inst| { + const name = zir.nullTerminatedString(zir.getParamName(zir_param_inst) orelse continue); + defer zir_param_index += 1; + try cg.mir_local_name_bytes.appendSlice(cg.gpa, name[0 .. name.len + 1]); + + if (comptime_args.len > 0) switch (comptime_args.get(ip)[zir_param_index]) { + .none => {}, + else => |comptime_arg| { + _ = try cg.addInst(.{ + .tag = .pseudo, + .ops = .pseudo_dbg_arg_val, + .data = .{ .ip_index = comptime_arg }, + }); + continue; + }, + }; + + const arg_ty: Type = .fromInterned(fn_info.param_types.get(ip)[fn_param_index]); + fn_param_index += 1; + cg.mir_local_types.appendAssumeCapacity(arg_ty.toIntern()); + + if (air_arg_index == air_args_body.len) { + try cg.asmPseudo(.pseudo_dbg_arg_none); + continue; + } + const air_arg_inst = air_args_body[air_arg_index]; + const air_arg_data = cg.air.instructions.items(.data)[air_arg_index].arg; + if (air_arg_data.zir_param_index != zir_param_index) { + try cg.asmPseudo(.pseudo_dbg_arg_none); + continue; + } + air_arg_index += 1; + try cg.genLocalDebugInfo(.arg, arg_ty, cg.getResolvedInstValue(air_arg_inst).short); + } + if (fn_info.is_var_args) try cg.asmPseudo(.pseudo_dbg_var_args_none); + } + + try cg.genBody(main_body[air_arg_count..]); +} + +fn checkInvariantsAfterAirInst(cg: *CodeGen) void { + assert(!cg.register_manager.lockedRegsExist()); if (std.debug.runtime_safety) { // check consistency of tracked registers - var it = self.register_manager.free_registers.iterator(.{ .kind = .unset }); + var it = cg.register_manager.free_registers.iterator(.{ .kind = .unset }); while (it.next()) |index| { - const tracked_inst = self.register_manager.registers[index]; - const tracking = self.getResolvedInstValue(tracked_inst); + const tracked_inst = cg.register_manager.registers[index]; + const tracking = cg.getResolvedInstValue(tracked_inst); for (tracking.getRegs()) |reg| { if (RegisterManager.indexOfRegIntoTracked(reg).? == index) break; } else unreachable; // tracked register not in use @@ -2459,10 +2405,10 @@ fn checkInvariantsAfterAirInst(self: *CodeGen) void { } } -fn genBodyBlock(self: *CodeGen, body: []const Air.Inst.Index) InnerError!void { - if (!self.mod.strip) try self.asmPseudo(.pseudo_dbg_enter_block_none); - try self.genBody(body); - if (!self.mod.strip) try self.asmPseudo(.pseudo_dbg_leave_block_none); +fn genBodyBlock(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { + if (!cg.mod.strip) try cg.asmPseudo(.pseudo_dbg_enter_block_none); + try cg.genBody(body); + if (!cg.mod.strip) try cg.asmPseudo(.pseudo_dbg_leave_block_none); } fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { @@ -2474,25 +2420,6 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { const air_datas = cg.air.instructions.items(.data); const use_old = cg.target.ofmt == .coff; - cg.arg_index = 0; - for (body) |inst| switch (air_tags[@intFromEnum(inst)]) { - .arg => { - wip_mir_log.debug("{}", .{cg.fmtAir(inst)}); - verbose_tracking_log.debug("{}", .{cg.fmtTracking()}); - - cg.reused_operands = .initEmpty(); - try cg.inst_tracking.ensureUnusedCapacity(cg.gpa, 1); - - try cg.airArg(inst); - - try cg.resetTemps(@enumFromInt(0)); - cg.checkInvariantsAfterAirInst(); - }, - else => break, - }; - - if (cg.arg_index == 0) try cg.airDbgVarArgs(); - cg.arg_index = 0; for (body) |inst| { if (cg.liveness.isUnused(inst) and !cg.air.mustLower(inst, ip)) continue; wip_mir_log.debug("{}", .{cg.fmtAir(inst)}); @@ -2506,20 +2433,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .shuffle_one, .shuffle_two => @panic("x86_64 TODO: shuffle_one/shuffle_two"), // zig fmt: on - .arg => if (!cg.mod.strip) { - // skip zero-bit arguments as they don't have a corresponding arg instruction - var arg_index = cg.arg_index; - while (cg.args[arg_index] == .none) arg_index += 1; - cg.arg_index = arg_index + 1; - - const name = air_datas[@intFromEnum(inst)].arg.name; - if (name != .none) try cg.genLocalDebugInfo(inst, cg.getResolvedInstValue(inst).short); - if (cg.liveness.isUnused(inst)) try cg.processDeath(inst); - - for (cg.args[arg_index + 1 ..]) |arg| { - if (arg != .none) break; - } else try cg.airDbgVarArgs(); - }, + .arg => try cg.airArg(inst), .add, .add_optimized, .add_wrap => |air_tag| if (use_old) try cg.airBinOp(inst, switch (air_tag) { else => unreachable, .add, .add_optimized => .add, @@ -85181,19 +85095,19 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { if (!cg.mod.strip) _ = try cg.addInst(.{ .tag = .pseudo, .ops = .pseudo_dbg_enter_inline_func, - .data = .{ .func = dbg_inline_block.data.func }, + .data = .{ .ip_index = dbg_inline_block.data.func }, }); try cg.lowerBlock(inst, @ptrCast(cg.air.extra.items[dbg_inline_block.end..][0..dbg_inline_block.data.body_len])); if (!cg.mod.strip) _ = try cg.addInst(.{ .tag = .pseudo, .ops = .pseudo_dbg_leave_inline_func, - .data = .{ .func = old_inline_func }, + .data = .{ .ip_index = old_inline_func }, }); }, .dbg_var_ptr, .dbg_var_val, .dbg_arg_inline, - => if (use_old) try cg.airDbgVar(inst) else if (!cg.mod.strip) { + => |air_tag| if (use_old) try cg.airDbgVar(inst) else if (!cg.mod.strip) { const pl_op = air_datas[@intFromEnum(inst)].pl_op; var ops = try cg.tempsFromOperands(inst, .{pl_op.operand}); var mcv = ops[0].tracking(cg).short; @@ -85209,7 +85123,16 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, }, } - try cg.genLocalDebugInfo(inst, ops[0].tracking(cg).short); + + const name_nts: Air.NullTerminatedString = @enumFromInt(pl_op.payload); + assert(name_nts != .none); + const name = name_nts.toSlice(cg.air); + try cg.mir_local_name_bytes.appendSlice(cg.gpa, name[0 .. name.len + 1]); + + const ty = cg.typeOf(pl_op.operand); + try cg.mir_local_types.append(cg.gpa, ty.toIntern()); + + try cg.genLocalDebugInfo(air_tag, ty, ops[0].tracking(cg).short); try ops[0].die(cg); }, .is_null => if (use_old) try cg.airIsNull(inst) else { @@ -173321,16 +173244,14 @@ fn genIntMulComplexOpMir(self: *CodeGen, dst_ty: Type, dst_mcv: MCValue, src_mcv } fn airArg(self: *CodeGen, inst: Air.Inst.Index) !void { - const pt = self.pt; - const zcu = pt.zcu; - // skip zero-bit arguments as they don't have a corresponding arg instruction - var arg_index = self.arg_index; - while (self.args[arg_index] == .none) arg_index += 1; - self.arg_index = arg_index + 1; - + const zcu = self.pt.zcu; + const arg_index = for (self.args, 0..) |arg, arg_index| { + if (arg != .none) break arg_index; + } else unreachable; + const src_mcv = self.args[arg_index]; + self.args = self.args[arg_index + 1 ..]; const result: MCValue = if (self.mod.strip and self.liveness.isUnused(inst)) .unreach else result: { const arg_ty = self.typeOfIndex(inst); - const src_mcv = self.args[arg_index]; switch (src_mcv) { .register, .register_pair, .load_frame => { for (src_mcv.getRegs()) |reg| self.register_manager.getRegAssumeFree(reg, inst); @@ -173429,68 +173350,108 @@ fn airArg(self: *CodeGen, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ .none, .none, .none }); } -fn airDbgVarArgs(self: *CodeGen) !void { - if (self.mod.strip) return; - if (!self.pt.zcu.typeToFunc(self.fn_type).?.is_var_args) return; - try self.asmPseudo(.pseudo_dbg_var_args_none); -} - -fn genLocalDebugInfo( - self: *CodeGen, - inst: Air.Inst.Index, - mcv: MCValue, -) !void { - if (self.mod.strip) return; - switch (self.air.instructions.items(.tag)[@intFromEnum(inst)]) { +fn genLocalDebugInfo(cg: *CodeGen, air_tag: Air.Inst.Tag, ty: Type, mcv: MCValue) !void { + assert(!cg.mod.strip); + _ = switch (air_tag) { else => unreachable, - .arg, .dbg_arg_inline, .dbg_var_val => |tag| { - switch (mcv) { - .none => try self.asmAir(.dbg_local, inst), - .unreach, .dead, .elementwise_args, .reserved_frame, .air_ref => unreachable, - .immediate => |imm| try self.asmAirImmediate(.dbg_local, inst, .u(imm)), - .lea_frame => |frame_addr| try self.asmAirFrameAddress(.dbg_local, inst, frame_addr), - .lea_symbol => |sym_off| try self.asmAirImmediate(.dbg_local, inst, .rel(sym_off)), - else => { - const ty = switch (tag) { + .arg, .dbg_var_val, .dbg_arg_inline => switch (mcv) { + .none, .unreach, .dead, .elementwise_args, .reserved_frame, .air_ref => unreachable, + .immediate => |imm| if (std.math.cast(u32, imm)) |small| try cg.addInst(.{ + .tag = .pseudo, + .ops = switch (air_tag) { + else => unreachable, + .arg, .dbg_arg_inline => .pseudo_dbg_arg_i_u, + .dbg_var_val => .pseudo_dbg_var_i_u, + }, + .data = .{ .i = .{ .i = small } }, + }) else try cg.addInst(.{ + .tag = .pseudo, + .ops = switch (air_tag) { + else => unreachable, + .arg, .dbg_arg_inline => .pseudo_dbg_arg_i_64, + .dbg_var_val => .pseudo_dbg_var_i_64, + }, + .data = .{ .i64 = imm }, + }), + .lea_frame => |frame_addr| try cg.addInst(.{ + .tag = .pseudo, + .ops = switch (air_tag) { + else => unreachable, + .arg, .dbg_arg_inline => .pseudo_dbg_arg_fa, + .dbg_var_val => .pseudo_dbg_var_fa, + }, + .data = .{ .fa = frame_addr }, + }), + .lea_symbol => |sym_off| try cg.addInst(.{ + .tag = .pseudo, + .ops = switch (air_tag) { + else => unreachable, + .arg, .dbg_arg_inline => .pseudo_dbg_arg_reloc, + .dbg_var_val => .pseudo_dbg_var_reloc, + }, + .data = .{ .reloc = sym_off }, + }), + else => { + const frame_index = try cg.allocFrameIndex(.initSpill(ty, cg.pt.zcu)); + try cg.genSetMem(.{ .frame = frame_index }, 0, ty, mcv, .{}); + _ = try cg.addInst(.{ + .tag = .pseudo, + .ops = switch (air_tag) { else => unreachable, - .arg => self.typeOfIndex(inst), - .dbg_arg_inline, .dbg_var_val => self.typeOf( - self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op.operand, - ), - }; - const frame_index = try self.allocFrameIndex(.initSpill(ty, self.pt.zcu)); - try self.genSetMem(.{ .frame = frame_index }, 0, ty, mcv, .{}); - try self.asmAirMemory(.dbg_local, inst, .{ - .base = .{ .frame = frame_index }, - .mod = .{ .rm = .{ .size = .qword } }, - }); - }, - } + .arg, .dbg_arg_inline => .pseudo_dbg_arg_m, + .dbg_var_val => .pseudo_dbg_var_m, + }, + .data = .{ .x = .{ + .payload = try cg.addExtra(Mir.Memory.encode(.{ + .base = .{ .frame = frame_index }, + .mod = .{ .rm = .{ .size = .qword } }, + })), + } }, + }); + }, }, .dbg_var_ptr => switch (mcv) { else => unreachable, - .unreach, .dead, .elementwise_args, .reserved_frame, .air_ref => unreachable, - .lea_frame => |frame_addr| try self.asmAirMemory(.dbg_local, inst, .{ - .base = .{ .frame = frame_addr.index }, - .mod = .{ .rm = .{ - .size = .qword, - .disp = frame_addr.off, + .none, .unreach, .dead, .elementwise_args, .reserved_frame, .air_ref => unreachable, + .lea_frame => |frame_addr| try cg.addInst(.{ + .tag = .pseudo, + .ops = .pseudo_dbg_var_m, + .data = .{ .x = .{ + .payload = try cg.addExtra(Mir.Memory.encode(.{ + .base = .{ .frame = frame_addr.index }, + .mod = .{ .rm = .{ + .size = .qword, + .disp = frame_addr.off, + } }, + })), } }, }), // debug info should explicitly ignore pcrel requirements - .lea_symbol, .lea_pcrel => |sym_off| try self.asmAirMemory(.dbg_local, inst, .{ - .base = .{ .reloc = sym_off.sym_index }, - .mod = .{ .rm = .{ - .size = .qword, - .disp = sym_off.off, + .lea_symbol, .lea_pcrel => |sym_off| try cg.addInst(.{ + .tag = .pseudo, + .ops = .pseudo_dbg_var_m, + .data = .{ .x = .{ + .payload = try cg.addExtra(Mir.Memory.encode(.{ + .base = .{ .reloc = sym_off.sym_index }, + .mod = .{ .rm = .{ + .size = .qword, + .disp = sym_off.off, + } }, + })), } }, }), - .lea_direct, .lea_got => |sym_index| try self.asmAirMemory(.dbg_local, inst, .{ - .base = .{ .reloc = sym_index }, - .mod = .{ .rm = .{ .size = .qword } }, + .lea_direct, .lea_got => |sym_index| try cg.addInst(.{ + .tag = .pseudo, + .ops = .pseudo_dbg_var_m, + .data = .{ .x = .{ + .payload = try cg.addExtra(Mir.Memory.encode(.{ + .base = .{ .reloc = sym_index }, + .mod = .{ .rm = .{ .size = .qword } }, + })), + } }, }), }, - } + }; } fn airRetAddr(self: *CodeGen, inst: Air.Inst.Index) !void { @@ -173514,8 +173475,8 @@ fn airCall(self: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModif @ptrCast(self.air.extra.items[extra.end..][0..extra.data.args_len]); const ExpectedContents = extern struct { - tys: [16][@sizeOf(Type)]u8 align(@alignOf(Type)), - vals: [16][@sizeOf(MCValue)]u8 align(@alignOf(MCValue)), + tys: [32][@sizeOf(Type)]u8 align(@alignOf(Type)), + vals: [32][@sizeOf(MCValue)]u8 align(@alignOf(MCValue)), }; var stack align(@max(@alignOf(ExpectedContents), @alignOf(std.heap.StackFallbackAllocator(0)))) = std.heap.stackFallback(@sizeOf(ExpectedContents), self.gpa); @@ -173570,9 +173531,9 @@ fn genCall(self: *CodeGen, info: union(enum) { const fn_info = zcu.typeToFunc(fn_ty).?; const ExpectedContents = extern struct { - var_args: [16][@sizeOf(Type)]u8 align(@alignOf(Type)), - frame_indices: [16]FrameIndex, - reg_locks: [16][@sizeOf(?RegisterLock)]u8 align(@alignOf(?RegisterLock)), + var_args: [32][@sizeOf(Type)]u8 align(@alignOf(Type)), + frame_indices: [32]FrameIndex, + reg_locks: [32][@sizeOf(?RegisterLock)]u8 align(@alignOf(?RegisterLock)), }; var stack align(@max(@alignOf(ExpectedContents), @alignOf(std.heap.StackFallbackAllocator(0)))) = std.heap.stackFallback(@sizeOf(ExpectedContents), self.gpa); @@ -174488,10 +174449,21 @@ fn genTry( return result; } -fn airDbgVar(self: *CodeGen, inst: Air.Inst.Index) !void { - const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; - try self.genLocalDebugInfo(inst, try self.resolveInst(pl_op.operand)); - return self.finishAir(inst, .unreach, .{ pl_op.operand, .none, .none }); +fn airDbgVar(cg: *CodeGen, inst: Air.Inst.Index) !void { + if (cg.mod.strip) return; + const air_tag = cg.air.instructions.items(.tag)[@intFromEnum(inst)]; + const pl_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; + + const name_nts: Air.NullTerminatedString = @enumFromInt(pl_op.payload); + assert(name_nts != .none); + const name = name_nts.toSlice(cg.air); + try cg.mir_local_name_bytes.appendSlice(cg.gpa, name[0 .. name.len + 1]); + + const ty = cg.typeOf(pl_op.operand); + try cg.mir_local_types.append(cg.gpa, ty.toIntern()); + + try cg.genLocalDebugInfo(air_tag, ty, try cg.resolveInst(pl_op.operand)); + return cg.finishAir(inst, .unreach, .{ pl_op.operand, .none, .none }); } fn genCondBrMir(self: *CodeGen, ty: Type, mcv: MCValue) !Mir.Inst.Index { @@ -181477,6 +181449,7 @@ fn lowerUav(self: *CodeGen, val: Value, alignment: InternPool.Alignment) InnerEr const CallMCValues = struct { args: []MCValue, + air_arg_count: u32, return_value: InstTracking, stack_byte_count: u31, stack_align: InternPool.Alignment, @@ -181512,13 +181485,14 @@ fn resolveCallingConventionValues( const param_types = try allocator.alloc(Type, fn_info.param_types.len + var_args.len); defer allocator.free(param_types); - for (param_types[0..fn_info.param_types.len], fn_info.param_types.get(ip)) |*dest, src| - dest.* = .fromInterned(src); + for (param_types[0..fn_info.param_types.len], fn_info.param_types.get(ip)) |*param_ty, arg_ty| + param_ty.* = .fromInterned(arg_ty); for (param_types[fn_info.param_types.len..], var_args) |*param_ty, arg_ty| param_ty.* = self.promoteVarArg(arg_ty); var result: CallMCValues = .{ .args = try self.gpa.alloc(MCValue, param_types.len), + .air_arg_count = 0, // These undefined values must be populated before returning from this function. .return_value = undefined, .stack_byte_count = 0, @@ -181640,6 +181614,7 @@ fn resolveCallingConventionValues( // Input params for (param_types, result.args) |ty, *arg| { assert(ty.hasRuntimeBitsIgnoreComptime(zcu)); + result.air_arg_count += 1; switch (cc) { .x86_64_sysv => {}, .x86_64_win => { @@ -181812,6 +181787,7 @@ fn resolveCallingConventionValues( arg.* = .none; continue; } + result.air_arg_count += 1; const param_size: u31 = @intCast(param_ty.abiSize(zcu)); if (abi.zigcc.params_in_regs) switch (self.regClassForType(param_ty)) { .general_purpose, .gphi => if (param_gpr.len >= 1 and param_size <= @as(u4, switch (self.target.cpu.arch) { diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index d4116974cf77b4bf8ea8e00d1c263ce83e3fe716..cbbfdab20257a9a4c84e61471663269ec5a990ff 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -1,6 +1,5 @@ //! This file contains the functionality for emitting x86_64 MIR as machine code -air: Air, lower: Lower, atom_index: u32, debug_output: link.File.DebugInfoOutput, @@ -22,6 +21,8 @@ pub fn emitMir(emit: *Emit) Error!void { defer relocs.deinit(emit.lower.allocator); var table_relocs: std.ArrayListUnmanaged(TableReloc) = .empty; defer table_relocs.deinit(emit.lower.allocator); + var local_name_index: usize = 0; + var local_index: usize = 0; for (0..emit.lower.mir.instructions.len) |mir_i| { const mir_index: Mir.Inst.Index = @intCast(mir_i); code_offset_mapping[mir_index] = @intCast(emit.code.items.len); @@ -338,7 +339,7 @@ pub fn emitMir(emit: *Emit) Error!void { log.debug("mirDbgEnterInline (line={d}, col={d})", .{ emit.prev_di_loc.line, emit.prev_di_loc.column, }); - try dwarf.enterInlineFunc(mir_inst.data.func, emit.code.items.len, emit.prev_di_loc.line, emit.prev_di_loc.column); + try dwarf.enterInlineFunc(mir_inst.data.ip_index, emit.code.items.len, emit.prev_di_loc.line, emit.prev_di_loc.column); }, .plan9 => {}, .none => {}, @@ -348,77 +349,61 @@ pub fn emitMir(emit: *Emit) Error!void { log.debug("mirDbgLeaveInline (line={d}, col={d})", .{ emit.prev_di_loc.line, emit.prev_di_loc.column, }); - try dwarf.leaveInlineFunc(mir_inst.data.func, emit.code.items.len); + try dwarf.leaveInlineFunc(mir_inst.data.ip_index, emit.code.items.len); }, .plan9 => {}, .none => {}, }, - .pseudo_dbg_local_a, - .pseudo_dbg_local_ai_s, - .pseudo_dbg_local_ai_u, - .pseudo_dbg_local_ai_64, - .pseudo_dbg_local_as, - .pseudo_dbg_local_aso, - .pseudo_dbg_local_aro, - .pseudo_dbg_local_af, - .pseudo_dbg_local_am, + .pseudo_dbg_arg_none, + .pseudo_dbg_arg_i_s, + .pseudo_dbg_arg_i_u, + .pseudo_dbg_arg_i_64, + .pseudo_dbg_arg_reloc, + .pseudo_dbg_arg_ro, + .pseudo_dbg_arg_fa, + .pseudo_dbg_arg_m, + .pseudo_dbg_var_none, + .pseudo_dbg_var_i_s, + .pseudo_dbg_var_i_u, + .pseudo_dbg_var_i_64, + .pseudo_dbg_var_reloc, + .pseudo_dbg_var_ro, + .pseudo_dbg_var_fa, + .pseudo_dbg_var_m, => switch (emit.debug_output) { .dwarf => |dwarf| { var loc_buf: [2]link.File.Dwarf.Loc = undefined; - const air_inst_index, const loc: link.File.Dwarf.Loc = switch (mir_inst.ops) { + const loc: link.File.Dwarf.Loc = loc: switch (mir_inst.ops) { else => unreachable, - .pseudo_dbg_local_a => .{ mir_inst.data.a.air_inst, .empty }, - .pseudo_dbg_local_ai_s, - .pseudo_dbg_local_ai_u, - .pseudo_dbg_local_ai_64, - => .{ mir_inst.data.ai.air_inst, .{ .stack_value = stack_value: { - loc_buf[0] = switch (emit.lower.imm(mir_inst.ops, mir_inst.data.ai.i)) { + .pseudo_dbg_arg_none, .pseudo_dbg_var_none => .empty, + .pseudo_dbg_arg_i_s, + .pseudo_dbg_arg_i_u, + .pseudo_dbg_var_i_s, + .pseudo_dbg_var_i_u, + => .{ .stack_value = stack_value: { + loc_buf[0] = switch (emit.lower.imm(mir_inst.ops, mir_inst.data.i.i)) { .signed => |s| .{ .consts = s }, .unsigned => |u| .{ .constu = u }, }; break :stack_value &loc_buf[0]; - } } }, - .pseudo_dbg_local_as => .{ mir_inst.data.as.air_inst, .{ - .addr_reloc = mir_inst.data.as.sym_index, } }, - .pseudo_dbg_local_aso => loc: { - const sym_off = emit.lower.mir.extraData( - bits.SymbolOffset, - mir_inst.data.ax.payload, - ).data; - break :loc .{ mir_inst.data.ax.air_inst, .{ .plus = .{ - sym: { - loc_buf[0] = .{ .addr_reloc = sym_off.sym_index }; - break :sym &loc_buf[0]; - }, - off: { - loc_buf[1] = .{ .consts = sym_off.off }; - break :off &loc_buf[1]; - }, - } } }; - }, - .pseudo_dbg_local_aro => loc: { - const air_off = emit.lower.mir.extraData( - Mir.AirOffset, - mir_inst.data.rx.payload, - ).data; - break :loc .{ air_off.air_inst, .{ .plus = .{ - reg: { - loc_buf[0] = .{ .breg = mir_inst.data.rx.r1.dwarfNum() }; - break :reg &loc_buf[0]; - }, - off: { - loc_buf[1] = .{ .consts = air_off.off }; - break :off &loc_buf[1]; - }, - } } }; - }, - .pseudo_dbg_local_af => loc: { - const reg_off = emit.lower.mir.resolveFrameAddr(emit.lower.mir.extraData( - bits.FrameAddr, - mir_inst.data.ax.payload, - ).data); - break :loc .{ mir_inst.data.ax.air_inst, .{ .plus = .{ + .pseudo_dbg_arg_i_64, .pseudo_dbg_var_i_64 => .{ .stack_value = stack_value: { + loc_buf[0] = .{ .constu = mir_inst.data.i64 }; + break :stack_value &loc_buf[0]; + } }, + .pseudo_dbg_arg_reloc, .pseudo_dbg_var_reloc => .{ .plus = .{ + sym: { + loc_buf[0] = .{ .addr_reloc = mir_inst.data.reloc.sym_index }; + break :sym &loc_buf[0]; + }, + off: { + loc_buf[1] = .{ .consts = mir_inst.data.reloc.off }; + break :off &loc_buf[1]; + }, + } }, + .pseudo_dbg_arg_fa, .pseudo_dbg_var_fa => { + const reg_off = emit.lower.mir.resolveFrameAddr(mir_inst.data.fa); + break :loc .{ .plus = .{ reg: { loc_buf[0] = .{ .breg = reg_off.reg.dwarfNum() }; break :reg &loc_buf[0]; @@ -427,11 +412,11 @@ pub fn emitMir(emit: *Emit) Error!void { loc_buf[1] = .{ .consts = reg_off.off }; break :off &loc_buf[1]; }, - } } }; + } }; }, - .pseudo_dbg_local_am => loc: { - const mem = emit.lower.mem(undefined, mir_inst.data.ax.payload); - break :loc .{ mir_inst.data.ax.air_inst, .{ .plus = .{ + .pseudo_dbg_arg_m, .pseudo_dbg_var_m => { + const mem = emit.lower.mem(undefined, mir_inst.data.x.payload); + break :loc .{ .plus = .{ base: { loc_buf[0] = switch (mem.base()) { .none => .{ .constu = 0 }, @@ -449,35 +434,69 @@ pub fn emitMir(emit: *Emit) Error!void { }; break :disp &loc_buf[1]; }, - } } }; + } }; }, }; - const ip = &emit.lower.bin_file.comp.zcu.?.intern_pool; - const air_inst = emit.air.instructions.get(@intFromEnum(air_inst_index)); - const name: Air.NullTerminatedString = switch (air_inst.tag) { - else => unreachable, - .arg => air_inst.data.arg.name, - .dbg_var_ptr, .dbg_var_val, .dbg_arg_inline => @enumFromInt(air_inst.data.pl_op.payload), - }; - try dwarf.genLocalDebugInfo( - switch (air_inst.tag) { - else => unreachable, - .arg, .dbg_arg_inline => .local_arg, - .dbg_var_ptr, .dbg_var_val => .local_var, - }, - name.toSlice(emit.air), - switch (air_inst.tag) { + + const local_name_bytes = emit.lower.mir.local_name_bytes[local_name_index..]; + const local_name = local_name_bytes[0..std.mem.indexOfScalar(u8, local_name_bytes, 0).? :0]; + local_name_index += local_name.len + 1; + + const local_type = emit.lower.mir.local_types[local_index]; + local_index += 1; + + try dwarf.genLocalVarDebugInfo( + switch (mir_inst.ops) { else => unreachable, - .arg => emit.air.typeOfIndex(air_inst_index, ip), - .dbg_var_ptr => emit.air.typeOf(air_inst.data.pl_op.operand, ip).childTypeIp(ip), - .dbg_var_val, .dbg_arg_inline => emit.air.typeOf(air_inst.data.pl_op.operand, ip), + .pseudo_dbg_arg_none, + .pseudo_dbg_arg_i_s, + .pseudo_dbg_arg_i_u, + .pseudo_dbg_arg_i_64, + .pseudo_dbg_arg_reloc, + .pseudo_dbg_arg_ro, + .pseudo_dbg_arg_fa, + .pseudo_dbg_arg_m, + .pseudo_dbg_arg_val, + => .arg, + .pseudo_dbg_var_none, + .pseudo_dbg_var_i_s, + .pseudo_dbg_var_i_u, + .pseudo_dbg_var_i_64, + .pseudo_dbg_var_reloc, + .pseudo_dbg_var_ro, + .pseudo_dbg_var_fa, + .pseudo_dbg_var_m, + .pseudo_dbg_var_val, + => .local_var, }, + local_name, + .fromInterned(local_type), loc, ); }, .plan9 => {}, .none => {}, }, + .pseudo_dbg_arg_val, .pseudo_dbg_var_val => switch (emit.debug_output) { + .dwarf => |dwarf| { + const local_name_bytes = emit.lower.mir.local_name_bytes[local_name_index..]; + const local_name = local_name_bytes[0..std.mem.indexOfScalar(u8, local_name_bytes, 0).? :0]; + local_name_index += local_name.len + 1; + + try dwarf.genLocalConstDebugInfo( + emit.lower.src_loc, + switch (mir_inst.ops) { + else => unreachable, + .pseudo_dbg_arg_val => .comptime_arg, + .pseudo_dbg_var_val => .local_const, + }, + local_name, + .fromInterned(mir_inst.data.ip_index), + ); + }, + .plan9 => {}, + .none => {}, + }, .pseudo_dbg_var_args_none => switch (emit.debug_output) { .dwarf => |dwarf| try dwarf.genVarArgsDebugInfo(), .plan9 => {}, @@ -611,11 +630,10 @@ fn dbgAdvancePCAndLine(emit: *Emit, loc: Loc) Error!void { } const bits = @import("bits.zig"); +const Emit = @This(); +const InternPool = @import("../../InternPool.zig"); const link = @import("../../link.zig"); const log = std.log.scoped(.emit); -const std = @import("std"); - -const Air = @import("../../Air.zig"); -const Emit = @This(); const Lower = @import("Lower.zig"); const Mir = @import("Mir.zig"); +const std = @import("std"); diff --git a/src/arch/x86_64/Lower.zig b/src/arch/x86_64/Lower.zig index 838f155d10db268cd979732ece314056d9d16cad..54b419103ffc539556a5d716c7974476170bd312 100644 --- a/src/arch/x86_64/Lower.zig +++ b/src/arch/x86_64/Lower.zig @@ -327,16 +327,25 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index) Error!struct { .pseudo_dbg_leave_block_none, .pseudo_dbg_enter_inline_func, .pseudo_dbg_leave_inline_func, - .pseudo_dbg_local_a, - .pseudo_dbg_local_ai_s, - .pseudo_dbg_local_ai_u, - .pseudo_dbg_local_ai_64, - .pseudo_dbg_local_as, - .pseudo_dbg_local_aso, - .pseudo_dbg_local_aro, - .pseudo_dbg_local_af, - .pseudo_dbg_local_am, + .pseudo_dbg_arg_none, + .pseudo_dbg_arg_i_s, + .pseudo_dbg_arg_i_u, + .pseudo_dbg_arg_i_64, + .pseudo_dbg_arg_reloc, + .pseudo_dbg_arg_ro, + .pseudo_dbg_arg_fa, + .pseudo_dbg_arg_m, + .pseudo_dbg_arg_val, .pseudo_dbg_var_args_none, + .pseudo_dbg_var_none, + .pseudo_dbg_var_i_s, + .pseudo_dbg_var_i_u, + .pseudo_dbg_var_i_64, + .pseudo_dbg_var_reloc, + .pseudo_dbg_var_ro, + .pseudo_dbg_var_fa, + .pseudo_dbg_var_m, + .pseudo_dbg_var_val, .pseudo_dead_none, => {}, @@ -364,7 +373,8 @@ pub fn imm(lower: *const Lower, ops: Mir.Inst.Ops, i: u32) Immediate { .i_s, .mi_s, .rmi_s, - .pseudo_dbg_local_ai_s, + .pseudo_dbg_arg_i_s, + .pseudo_dbg_var_i_s, => .s(@bitCast(i)), .ii, @@ -379,13 +389,17 @@ pub fn imm(lower: *const Lower, ops: Mir.Inst.Ops, i: u32) Immediate { .mri, .rrm, .rrmi, - .pseudo_dbg_local_ai_u, + .pseudo_dbg_arg_i_u, + .pseudo_dbg_var_i_u, => .u(i), .ri_64, - .pseudo_dbg_local_ai_64, => .u(lower.mir.extraData(Mir.Imm64, i).data.decode()), + .pseudo_dbg_arg_i_64, + .pseudo_dbg_var_i_64, + => unreachable, + else => unreachable, }; } diff --git a/src/arch/x86_64/Mir.zig b/src/arch/x86_64/Mir.zig index 14468677afd7c40d900b176b48d84b54686eeac7..24d5c6a3ed8a80991be12f4603b791b80b5b5657 100644 --- a/src/arch/x86_64/Mir.zig +++ b/src/arch/x86_64/Mir.zig @@ -9,6 +9,8 @@ instructions: std.MultiArrayList(Inst).Slice, /// The meaning of this data is determined by `Inst.Tag` value. extra: []const u32, +local_name_bytes: []const u8, +local_types: []const InternPool.Index, table: []const Inst.Index, frame_locs: std.MultiArrayList(FrameLoc).Slice, @@ -1522,6 +1524,7 @@ pub const Inst = struct { pseudo_cfi_escape_bytes, /// End of prologue + /// Uses `none` payload. pseudo_dbg_prologue_end_none, /// Update debug line with is_stmt register set /// Uses `line_column` payload. @@ -1530,44 +1533,76 @@ pub const Inst = struct { /// Uses `line_column` payload. pseudo_dbg_line_line_column, /// Start of epilogue + /// Uses `none` payload. pseudo_dbg_epilogue_begin_none, /// Start of lexical block + /// Uses `none` payload. pseudo_dbg_enter_block_none, /// End of lexical block + /// Uses `none` payload. pseudo_dbg_leave_block_none, /// Start of inline function + /// Uses `ip_index` payload. pseudo_dbg_enter_inline_func, /// End of inline function + /// Uses `ip_index` payload. pseudo_dbg_leave_inline_func, - /// Local argument or variable. - /// Uses `a` payload. - pseudo_dbg_local_a, - /// Local argument or variable. - /// Uses `ai` payload. - pseudo_dbg_local_ai_s, - /// Local argument or variable. - /// Uses `ai` payload. - pseudo_dbg_local_ai_u, - /// Local argument or variable. - /// Uses `ai` payload with extra data of type `Imm64`. - pseudo_dbg_local_ai_64, - /// Local argument or variable. - /// Uses `as` payload. - pseudo_dbg_local_as, - /// Local argument or variable. - /// Uses `ax` payload with extra data of type `bits.SymbolOffset`. - pseudo_dbg_local_aso, - /// Local argument or variable. - /// Uses `rx` payload with extra data of type `AirOffset`. - pseudo_dbg_local_aro, - /// Local argument or variable. - /// Uses `ax` payload with extra data of type `bits.FrameAddr`. - pseudo_dbg_local_af, - /// Local argument or variable. - /// Uses `ax` payload with extra data of type `Memory`. - pseudo_dbg_local_am, + /// Local argument. + /// Uses `none` payload. + pseudo_dbg_arg_none, + /// Local argument. + /// Uses `i` payload. + pseudo_dbg_arg_i_s, + /// Local argument. + /// Uses `i` payload. + pseudo_dbg_arg_i_u, + /// Local argument. + /// Uses `i64` payload. + pseudo_dbg_arg_i_64, + /// Local argument. + /// Uses `reloc` payload. + pseudo_dbg_arg_reloc, + /// Local argument. + /// Uses `ro` payload. + pseudo_dbg_arg_ro, + /// Local argument. + /// Uses `fa` payload. + pseudo_dbg_arg_fa, + /// Local argument. + /// Uses `x` payload with extra data of type `Memory`. + pseudo_dbg_arg_m, + /// Local argument. + /// Uses `ip_index` payload. + pseudo_dbg_arg_val, /// Remaining arguments are varargs. pseudo_dbg_var_args_none, + /// Local variable. + /// Uses `none` payload. + pseudo_dbg_var_none, + /// Local variable. + /// Uses `i` payload. + pseudo_dbg_var_i_s, + /// Local variable. + /// Uses `i` payload. + pseudo_dbg_var_i_u, + /// Local variable. + /// Uses `i64` payload. + pseudo_dbg_var_i_64, + /// Local variable. + /// Uses `reloc` payload. + pseudo_dbg_var_reloc, + /// Local variable. + /// Uses `ro` payload. + pseudo_dbg_var_ro, + /// Local variable. + /// Uses `fa` payload. + pseudo_dbg_var_fa, + /// Local variable. + /// Uses `x` payload with extra data of type `Memory`. + pseudo_dbg_var_m, + /// Local variable. + /// Uses `ip_index` payload. + pseudo_dbg_var_val, /// Tombstone /// Emitter should skip this instruction. @@ -1584,6 +1619,7 @@ pub const Inst = struct { inst: Index, }, /// A 32-bit immediate value. + i64: u64, i: struct { fixes: Fixes = ._, i: u32, @@ -1683,31 +1719,18 @@ pub const Inst = struct { return std.mem.sliceAsBytes(mir.extra[bytes.payload..])[0..bytes.len]; } }, - a: struct { - air_inst: Air.Inst.Index, - }, - ai: struct { - air_inst: Air.Inst.Index, - i: u32, - }, - as: struct { - air_inst: Air.Inst.Index, - sym_index: u32, - }, - ax: struct { - air_inst: Air.Inst.Index, - payload: u32, - }, /// Relocation for the linker where: /// * `sym_index` is the index of the target /// * `off` is the offset from the target reloc: bits.SymbolOffset, + fa: bits.FrameAddr, + ro: bits.RegisterOffset, /// Debug line and column position line_column: struct { line: u32, column: u32, }, - func: InternPool.Index, + ip_index: InternPool.Index, /// Register list reg_list: RegisterList, }; @@ -1760,8 +1783,6 @@ pub const Inst = struct { } }; -pub const AirOffset = struct { air_inst: Air.Inst.Index, off: i32 }; - /// Used in conjunction with payload to transfer a list of used registers in a compact manner. pub const RegisterList = struct { bitset: BitSet, @@ -1924,6 +1945,8 @@ pub const Memory = struct { pub fn deinit(mir: *Mir, gpa: std.mem.Allocator) void { mir.instructions.deinit(gpa); gpa.free(mir.extra); + gpa.free(mir.local_name_bytes); + gpa.free(mir.local_types); gpa.free(mir.table); mir.frame_locs.deinit(gpa); mir.* = undefined; @@ -1937,8 +1960,6 @@ pub fn emit( func_index: InternPool.Index, code: *std.ArrayListUnmanaged(u8), debug_output: link.File.DebugInfoOutput, - /// TODO: remove dependency on this argument. This blocks enabling `Zcu.Feature.separate_thread`. - air: *const Air, ) codegen.CodeGenError!void { const zcu = pt.zcu; const comp = zcu.comp; @@ -1948,7 +1969,6 @@ pub fn emit( const nav = func.owner_nav; const mod = zcu.navFileScope(nav).mod.?; var e: Emit = .{ - .air = air.*, .lower = .{ .bin_file = lf, .target = &mod.resolved_target.result, @@ -1998,7 +2018,7 @@ pub fn extraData(mir: Mir, comptime T: type, index: u32) struct { data: T, end: @field(result, field.name) = switch (field.type) { u32 => mir.extra[i], i32, Memory.Info => @bitCast(mir.extra[i]), - bits.FrameIndex, Air.Inst.Index => @enumFromInt(mir.extra[i]), + bits.FrameIndex => @enumFromInt(mir.extra[i]), else => @compileError("bad field type: " ++ field.name ++ ": " ++ @typeName(field.type)), }; i += 1; @@ -2043,7 +2063,6 @@ const builtin = @import("builtin"); const encoder = @import("encoder.zig"); const std = @import("std"); -const Air = @import("../../Air.zig"); const IntegerBitSet = std.bit_set.IntegerBitSet; const InternPool = @import("../../InternPool.zig"); const Mir = @This(); diff --git a/src/codegen.zig b/src/codegen.zig index 5a8f17735a2ab7c34316bea68917f2fe0ee16a83..9199c27dc2aec52c59d771d6d7900b25807945e1 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -180,10 +180,6 @@ pub fn emitFunction( any_mir: *const AnyMir, code: *std.ArrayListUnmanaged(u8), debug_output: link.File.DebugInfoOutput, - /// TODO: this parameter needs to be removed. We should not still hold AIR this late - /// in the pipeline. Any information needed to call emit must be stored in MIR. - /// This is `undefined` if the backend supports the `separate_thread` feature. - air: *const Air, ) CodeGenError!void { const zcu = pt.zcu; const func = zcu.funcInfo(func_index); @@ -199,7 +195,7 @@ pub fn emitFunction( => |backend| { dev.check(devFeatureForBackend(backend)); const mir = &@field(any_mir, AnyMir.tag(backend)); - return mir.emit(lf, pt, src_loc, func_index, code, debug_output, air); + return mir.emit(lf, pt, src_loc, func_index, code, debug_output); }, } } diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index e30e8f70a3a3283c2d6e7982a7adfdf3f781e81f..658764ba3cf7b81ba2132ac873671660262cff38 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -9509,15 +9509,21 @@ pub const FuncGen = struct { const inst_ty = self.typeOfIndex(inst); - const name = self.air.instructions.items(.data)[@intFromEnum(inst)].arg.name; - if (name == .none) return arg_val; - const func = zcu.funcInfo(zcu.navValue(self.ng.nav_index).toIntern()); + const func_zir = func.zir_body_inst.resolveFull(&zcu.intern_pool).?; + const file = zcu.fileByIndex(func_zir.file); + + const mod = file.mod.?; + if (mod.strip) return arg_val; + const arg = self.air.instructions.items(.data)[@intFromEnum(inst)].arg; + const zir = &file.zir.?; + const name = zir.nullTerminatedString(zir.getParamName(zir.getParamBody(func_zir.inst)[arg.zir_param_index]).?); + const lbrace_line = zcu.navSrcLine(func.owner_nav) + func.lbrace_line + 1; const lbrace_col = func.lbrace_column + 1; const debug_parameter = try o.builder.debugParameter( - try o.builder.metadataString(name.toSlice(self.air)), + try o.builder.metadataString(name), self.file, self.scope, lbrace_line, @@ -9535,7 +9541,6 @@ pub const FuncGen = struct { }, }; - const mod = self.ng.ownerModule(); if (isByRef(inst_ty, zcu)) { _ = try self.wip.callIntrinsic( .normal, diff --git a/src/link.zig b/src/link.zig index bbd0163d23c25e8b7fce317542a7ad61984d393a..844ea7a85cdc0fb167cf721cba1060c34f45ef3d 100644 --- a/src/link.zig +++ b/src/link.zig @@ -8,7 +8,6 @@ const log = std.log.scoped(.link); const trace = @import("tracy.zig").trace; const wasi_libc = @import("libs/wasi_libc.zig"); -const Air = @import("Air.zig"); const Allocator = std.mem.Allocator; const Cache = std.Build.Cache; const Path = std.Build.Cache.Path; @@ -752,9 +751,6 @@ pub const File = struct { /// that `mir.deinit` remains legal for the caller. For instance, the callee can /// take ownership of an embedded slice and replace it with `&.{}` in `mir`. mir: *codegen.AnyMir, - /// This may be `undefined`; only pass it to `emitFunction`. - /// This parameter will eventually be removed. - maybe_undef_air: *const Air, ) UpdateNavError!void { assert(base.comp.zcu.?.llvm_object == null); switch (base.tag) { @@ -762,7 +758,7 @@ pub const File = struct { .spirv => unreachable, // see corresponding special case in `Zcu.PerThread.runCodegenInner` inline else => |tag| { dev.check(tag.devFeature()); - return @as(*tag.Type(), @fieldParentPtr("base", base)).updateFunc(pt, func_index, mir, maybe_undef_air); + return @as(*tag.Type(), @fieldParentPtr("base", base)).updateFunc(pt, func_index, mir); }, } } @@ -1271,11 +1267,6 @@ pub const ZcuTask = union(enum) { /// the codegen job to ensure that the linker receives functions in a deterministic order, /// allowing reproducible builds. mir: *SharedMir, - /// This field exists only due to deficiencies in some codegen implementations; it should - /// be removed when the corresponding parameter of `CodeGen.emitFunction` can be removed. - /// This is `undefined` if `Zcu.Feature.separate_thread` is supported. - /// If this is defined, its memory is owned externally; do not `deinit` this `air`. - air: *const Air, pub const SharedMir = struct { /// This is initially `.pending`. When `value` is populated, the codegen thread will set @@ -1458,7 +1449,7 @@ pub fn doZcuTask(comp: *Compilation, tid: usize, task: ZcuTask) void { assert(zcu.llvm_object == null); // LLVM codegen doesn't produce MIR const mir = &func.mir.value; if (comp.bin_file) |lf| { - lf.updateFunc(pt, func.func, mir, func.air) catch |err| switch (err) { + lf.updateFunc(pt, func.func, mir) catch |err| switch (err) { error.OutOfMemory => return diags.setAllocFailure(), error.CodegenFail => return zcu.assertCodegenFailed(nav), error.Overflow, error.RelocationNotByteAligned => { diff --git a/src/link/C.zig b/src/link/C.zig index 417ebcdee6951ecbb277a3920e5ae8f8dfc0ef92..f3465055b8620bdfa8bab67fa032bb7540e02105 100644 --- a/src/link/C.zig +++ b/src/link/C.zig @@ -17,7 +17,6 @@ const link = @import("../link.zig"); const trace = @import("../tracy.zig").trace; const Type = @import("../Type.zig"); const Value = @import("../Value.zig"); -const Air = @import("../Air.zig"); const AnyMir = @import("../codegen.zig").AnyMir; pub const zig_h = "#include \"zig.h\"\n"; @@ -182,12 +181,7 @@ pub fn updateFunc( pt: Zcu.PerThread, func_index: InternPool.Index, mir: *AnyMir, - /// This may be `undefined`; only pass it to `emitFunction`. - /// This parameter will eventually be removed. - maybe_undef_air: *const Air, ) link.File.UpdateNavError!void { - _ = maybe_undef_air; // It would be a bug to use this argument. - const zcu = pt.zcu; const gpa = zcu.gpa; const func = zcu.funcInfo(func_index); diff --git a/src/link/Coff.zig b/src/link/Coff.zig index 81376c45d8f6505418befc6e376772ece603ffa5..c9234b335db15b1d89c2413b71a3c3da2b0ce7f2 100644 --- a/src/link/Coff.zig +++ b/src/link/Coff.zig @@ -1053,9 +1053,6 @@ pub fn updateFunc( pt: Zcu.PerThread, func_index: InternPool.Index, mir: *const codegen.AnyMir, - /// This may be `undefined`; only pass it to `emitFunction`. - /// This parameter will eventually be removed. - maybe_undef_air: *const Air, ) link.File.UpdateNavError!void { if (build_options.skip_non_native and builtin.object_format != .coff) { @panic("Attempted to compile for object format that was disabled by build configuration"); @@ -1084,7 +1081,6 @@ pub fn updateFunc( mir, &code_buffer, .none, - maybe_undef_air, ); try coff.updateNavCode(pt, nav_index, code_buffer.items, .FUNCTION); @@ -3123,7 +3119,6 @@ const link = @import("../link.zig"); const target_util = @import("../target.zig"); const trace = @import("../tracy.zig").trace; -const Air = @import("../Air.zig"); const Compilation = @import("../Compilation.zig"); const Zcu = @import("../Zcu.zig"); const InternPool = @import("../InternPool.zig"); diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index 393cd53774919ad5ba552d4692f19df88ae06b79..42d0d74ec5ebd51c0d986ef466c4117f7faa476e 100644 --- a/src/link/Dwarf.zig +++ b/src/link/Dwarf.zig @@ -1474,17 +1474,18 @@ pub const WipNav = struct { try cfa.write(wip_nav); } - pub const LocalTag = enum { local_arg, local_var }; - pub fn genLocalDebugInfo( + pub const LocalVarTag = enum { arg, local_var }; + pub fn genLocalVarDebugInfo( wip_nav: *WipNav, - tag: LocalTag, + tag: LocalVarTag, name: []const u8, ty: Type, loc: Loc, ) UpdateError!void { assert(wip_nav.func != .none); try wip_nav.abbrevCode(switch (tag) { - inline else => |ct_tag| @field(AbbrevCode, @tagName(ct_tag)), + .arg => .arg, + .local_var => .local_var, }); try wip_nav.strp(name); try wip_nav.refType(ty); @@ -1492,6 +1493,40 @@ pub const WipNav = struct { wip_nav.any_children = true; } + pub const LocalConstTag = enum { comptime_arg, local_const }; + pub fn genLocalConstDebugInfo( + wip_nav: *WipNav, + src_loc: Zcu.LazySrcLoc, + tag: LocalConstTag, + name: []const u8, + val: Value, + ) UpdateError!void { + assert(wip_nav.func != .none); + const pt = wip_nav.pt; + const zcu = pt.zcu; + const ty = val.typeOf(zcu); + const has_runtime_bits = ty.hasRuntimeBits(zcu); + const has_comptime_state = ty.comptimeOnly(zcu) and try ty.onePossibleValue(pt) == null; + try wip_nav.abbrevCode(if (has_runtime_bits and has_comptime_state) switch (tag) { + .comptime_arg => .comptime_arg_runtime_bits_comptime_state, + .local_const => .local_const_runtime_bits_comptime_state, + } else if (has_comptime_state) switch (tag) { + .comptime_arg => .comptime_arg_comptime_state, + .local_const => .local_const_comptime_state, + } else if (has_runtime_bits) switch (tag) { + .comptime_arg => .comptime_arg_runtime_bits, + .local_const => .local_const_runtime_bits, + } else switch (tag) { + .comptime_arg => .comptime_arg, + .local_const => .local_const, + }); + try wip_nav.strp(name); + try wip_nav.refType(ty); + if (has_runtime_bits) try wip_nav.blockValue(src_loc, val); + if (has_comptime_state) try wip_nav.refValue(val); + wip_nav.any_children = true; + } + pub fn genVarArgsDebugInfo(wip_nav: *WipNav) UpdateError!void { assert(wip_nav.func != .none); try wip_nav.abbrevCode(.is_var_args); @@ -1825,7 +1860,8 @@ pub const WipNav = struct { fn getNavEntry(wip_nav: *WipNav, nav_index: InternPool.Nav.Index) UpdateError!struct { Unit.Index, Entry.Index } { const zcu = wip_nav.pt.zcu; const ip = &zcu.intern_pool; - const unit = try wip_nav.dwarf.getUnit(zcu.fileByIndex(ip.getNav(nav_index).srcInst(ip).resolveFile(ip)).mod.?); + const nav = ip.getNav(nav_index); + const unit = try wip_nav.dwarf.getUnit(zcu.fileByIndex(nav.srcInst(ip).resolveFile(ip)).mod.?); const gop = try wip_nav.dwarf.navs.getOrPut(wip_nav.dwarf.gpa, nav_index); if (gop.found_existing) return .{ unit, gop.value_ptr.* }; const entry = try wip_nav.dwarf.addCommonEntry(unit); @@ -1842,10 +1878,16 @@ pub const WipNav = struct { const zcu = wip_nav.pt.zcu; const ip = &zcu.intern_pool; const maybe_inst_index = ty.typeDeclInst(zcu); - const unit = if (maybe_inst_index) |inst_index| - try wip_nav.dwarf.getUnit(zcu.fileByIndex(inst_index.resolveFile(ip)).mod.?) - else - .main; + const unit = if (maybe_inst_index) |inst_index| switch (switch (ip.indexToKey(ty.toIntern())) { + else => unreachable, + .struct_type => ip.loadStructType(ty.toIntern()).name_nav, + .union_type => ip.loadUnionType(ty.toIntern()).name_nav, + .enum_type => ip.loadEnumType(ty.toIntern()).name_nav, + .opaque_type => ip.loadOpaqueType(ty.toIntern()).name_nav, + }) { + .none => try wip_nav.dwarf.getUnit(zcu.fileByIndex(inst_index.resolveFile(ip)).mod.?), + else => |name_nav| return wip_nav.getNavEntry(name_nav.unwrap().?), + } else .main; const gop = try wip_nav.dwarf.types.getOrPut(wip_nav.dwarf.gpa, ty.toIntern()); if (gop.found_existing) return .{ unit, gop.value_ptr.* }; const entry = try wip_nav.dwarf.addCommonEntry(unit); @@ -1864,10 +1906,8 @@ pub const WipNav = struct { const ip = &zcu.intern_pool; const ty = value.typeOf(zcu); if (std.debug.runtime_safety) assert(ty.comptimeOnly(zcu) and try ty.onePossibleValue(wip_nav.pt) == null); - if (!value.isUndef(zcu)) { - if (ty.toIntern() == .type_type) return wip_nav.getTypeEntry(value.toType()); - if (ip.isFunctionType(ty.toIntern())) return wip_nav.getNavEntry(zcu.funcInfo(value.toIntern()).owner_nav); - } + if (ty.toIntern() == .type_type) return wip_nav.getTypeEntry(value.toType()); + if (ip.isFunctionType(ty.toIntern()) and !value.isUndef(zcu)) return wip_nav.getNavEntry(zcu.funcInfo(value.toIntern()).owner_nav); const gop = try wip_nav.dwarf.values.getOrPut(wip_nav.dwarf.gpa, value.toIntern()); const unit: Unit.Index = .main; if (gop.found_existing) return .{ unit, gop.value_ptr.* }; @@ -1916,7 +1956,10 @@ pub const WipNav = struct { &wip_nav.debug_info, .{ .debug_output = .{ .dwarf = wip_nav } }, ); - assert(old_len + bytes == wip_nav.debug_info.items.len); + if (old_len + bytes != wip_nav.debug_info.items.len) { + std.debug.print("{} [{}]: {} != {}\n", .{ ty.fmt(wip_nav.pt), ty.toIntern(), bytes, wip_nav.debug_info.items.len - old_len }); + unreachable; + } } const AbbrevCodeForForm = struct { @@ -2788,6 +2831,7 @@ fn updateComptimeNavInner(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPoo const type_gop = try dwarf.types.getOrPut(dwarf.gpa, nav_val.toIntern()); if (type_gop.found_existing) { if (dwarf.debug_info.section.getUnit(wip_nav.unit).getEntry(type_gop.value_ptr.*).len > 0) break :tag .decl_alias; + assert(!nav_gop.found_existing); nav_gop.value_ptr.* = type_gop.value_ptr.*; } else { if (nav_gop.found_existing) @@ -2890,6 +2934,7 @@ fn updateComptimeNavInner(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPoo const type_gop = try dwarf.types.getOrPut(dwarf.gpa, nav_val.toIntern()); if (type_gop.found_existing) { if (dwarf.debug_info.section.getUnit(wip_nav.unit).getEntry(type_gop.value_ptr.*).len > 0) break :tag .decl_alias; + assert(!nav_gop.found_existing); nav_gop.value_ptr.* = type_gop.value_ptr.*; } else { if (nav_gop.found_existing) @@ -2928,6 +2973,7 @@ fn updateComptimeNavInner(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPoo const type_gop = try dwarf.types.getOrPut(dwarf.gpa, nav_val.toIntern()); if (type_gop.found_existing) { if (dwarf.debug_info.section.getUnit(wip_nav.unit).getEntry(type_gop.value_ptr.*).len > 0) break :tag .decl_alias; + assert(!nav_gop.found_existing); nav_gop.value_ptr.* = type_gop.value_ptr.*; } else { if (nav_gop.found_existing) @@ -2998,6 +3044,7 @@ fn updateComptimeNavInner(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPoo const type_gop = try dwarf.types.getOrPut(dwarf.gpa, nav_val.toIntern()); if (type_gop.found_existing) { if (dwarf.debug_info.section.getUnit(wip_nav.unit).getEntry(type_gop.value_ptr.*).len > 0) break :tag .decl_alias; + assert(!nav_gop.found_existing); nav_gop.value_ptr.* = type_gop.value_ptr.*; } else { if (nav_gop.found_existing) @@ -3164,6 +3211,7 @@ fn updateLazyType( ) UpdateError!void { const zcu = pt.zcu; const ip = &zcu.intern_pool; + assert(ip.typeOf(type_index) == .type_type); const ty: Type = .fromInterned(type_index); switch (type_index) { .generic_poison_type => log.debug("updateLazyType({s})", .{"anytype"}), @@ -3200,6 +3248,10 @@ fn updateLazyType( defer dwarf.gpa.free(name); switch (ip.indexToKey(type_index)) { + .undef => { + try wip_nav.abbrevCode(.undefined_comptime_value); + try wip_nav.refType(.type); + }, .int_type => |int_type| { try wip_nav.abbrevCode(.numeric_type); try wip_nav.strp(name); @@ -3633,7 +3685,6 @@ fn updateLazyType( }, // values, not types - .undef, .simple_value, .variable, .@"extern", @@ -3666,7 +3717,11 @@ fn updateLazyValue( ) UpdateError!void { const zcu = pt.zcu; const ip = &zcu.intern_pool; - log.debug("updateLazyValue({})", .{Value.fromInterned(value_index).fmtValue(pt)}); + assert(ip.typeOf(value_index) != .type_type); + log.debug("updateLazyValue(@as({}, {}))", .{ + Value.fromInterned(value_index).typeOf(zcu).fmt(pt), + Value.fromInterned(value_index).fmtValue(pt), + }); var wip_nav: WipNav = .{ .dwarf = dwarf, .pt = pt, @@ -3710,9 +3765,8 @@ fn updateLazyValue( .inferred_error_set_type, => unreachable, // already handled .undef => |ty| { - try wip_nav.abbrevCode(.aggregate_comptime_value); + try wip_nav.abbrevCode(.undefined_comptime_value); try wip_nav.refType(.fromInterned(ty)); - try uleb128(diw, @intFromEnum(AbbrevCode.null)); }, .simple_value => unreachable, // opv state .variable, .@"extern" => unreachable, // not a value @@ -4890,8 +4944,17 @@ const AbbrevCode = enum { block, empty_inlined_func, inlined_func, - local_arg, + arg, + comptime_arg, + comptime_arg_runtime_bits, + comptime_arg_comptime_state, + comptime_arg_runtime_bits_comptime_state, local_var, + local_const, + local_const_runtime_bits, + local_const_comptime_state, + local_const_runtime_bits_comptime_state, + undefined_comptime_value, data2_comptime_value, data4_comptime_value, data8_comptime_value, @@ -5663,7 +5726,7 @@ const AbbrevCode = enum { .{ .high_pc, .data4 }, }, }, - .local_arg = .{ + .arg = .{ .tag = .formal_parameter, .attrs = &.{ .{ .name, .strp }, @@ -5671,6 +5734,42 @@ const AbbrevCode = enum { .{ .location, .exprloc }, }, }, + .comptime_arg = .{ + .tag = .formal_parameter, + .attrs = &.{ + .{ .const_expr, .flag_present }, + .{ .name, .strp }, + .{ .type, .ref_addr }, + }, + }, + .comptime_arg_runtime_bits = .{ + .tag = .formal_parameter, + .attrs = &.{ + .{ .const_expr, .flag_present }, + .{ .name, .strp }, + .{ .type, .ref_addr }, + .{ .const_value, .block }, + }, + }, + .comptime_arg_comptime_state = .{ + .tag = .formal_parameter, + .attrs = &.{ + .{ .const_expr, .flag_present }, + .{ .name, .strp }, + .{ .type, .ref_addr }, + .{ .ZIG_comptime_value, .ref_addr }, + }, + }, + .comptime_arg_runtime_bits_comptime_state = .{ + .tag = .formal_parameter, + .attrs = &.{ + .{ .const_expr, .flag_present }, + .{ .name, .strp }, + .{ .type, .ref_addr }, + .{ .const_value, .block }, + .{ .ZIG_comptime_value, .ref_addr }, + }, + }, .local_var = .{ .tag = .variable, .attrs = &.{ @@ -5679,6 +5778,44 @@ const AbbrevCode = enum { .{ .location, .exprloc }, }, }, + .local_const = .{ + .tag = .constant, + .attrs = &.{ + .{ .name, .strp }, + .{ .type, .ref_addr }, + }, + }, + .local_const_runtime_bits = .{ + .tag = .constant, + .attrs = &.{ + .{ .name, .strp }, + .{ .type, .ref_addr }, + .{ .const_value, .block }, + }, + }, + .local_const_comptime_state = .{ + .tag = .constant, + .attrs = &.{ + .{ .name, .strp }, + .{ .type, .ref_addr }, + .{ .ZIG_comptime_value, .ref_addr }, + }, + }, + .local_const_runtime_bits_comptime_state = .{ + .tag = .constant, + .attrs = &.{ + .{ .name, .strp }, + .{ .type, .ref_addr }, + .{ .const_value, .block }, + .{ .ZIG_comptime_value, .ref_addr }, + }, + }, + .undefined_comptime_value = .{ + .tag = .ZIG_comptime_value, + .attrs = &.{ + .{ .type, .ref_addr }, + }, + }, .data2_comptime_value = .{ .tag = .ZIG_comptime_value, .attrs = &.{ diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 498bc734c3d57961962b0460ca69fe9c0e02116f..0beea0d9e7d561f4abceaa2cefbc84c86cc78718 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1683,12 +1683,11 @@ pub fn updateFunc( pt: Zcu.PerThread, func_index: InternPool.Index, mir: *const codegen.AnyMir, - maybe_undef_air: *const Air, ) link.File.UpdateNavError!void { if (build_options.skip_non_native and builtin.object_format != .elf) { @panic("Attempted to compile for object format that was disabled by build configuration"); } - return self.zigObjectPtr().?.updateFunc(self, pt, func_index, mir, maybe_undef_air); + return self.zigObjectPtr().?.updateFunc(self, pt, func_index, mir); } pub fn updateNav( @@ -4516,7 +4515,6 @@ const trace = @import("../tracy.zig").trace; const synthetic_sections = @import("Elf/synthetic_sections.zig"); const Merge = @import("Elf/Merge.zig"); -const Air = @import("../Air.zig"); const Archive = @import("Elf/Archive.zig"); const AtomList = @import("Elf/AtomList.zig"); const Compilation = @import("../Compilation.zig"); diff --git a/src/link/Elf/ZigObject.zig b/src/link/Elf/ZigObject.zig index 1a5ef4b40821f38da207807b546991482172f5e7..8478aad8c3daf5f6bbacc5f84e91f7426e22dbd6 100644 --- a/src/link/Elf/ZigObject.zig +++ b/src/link/Elf/ZigObject.zig @@ -1417,9 +1417,6 @@ pub fn updateFunc( pt: Zcu.PerThread, func_index: InternPool.Index, mir: *const codegen.AnyMir, - /// This may be `undefined`; only pass it to `emitFunction`. - /// This parameter will eventually be removed. - maybe_undef_air: *const Air, ) link.File.UpdateNavError!void { const tracy = trace(@src()); defer tracy.end(); @@ -1448,7 +1445,6 @@ pub fn updateFunc( mir, &code_buffer, if (debug_wip_nav) |*dn| .{ .dwarf = dn } else .none, - maybe_undef_air, ); const code = code_buffer.items; @@ -2363,7 +2359,6 @@ const trace = @import("../../tracy.zig").trace; const std = @import("std"); const Allocator = std.mem.Allocator; -const Air = @import("../../Air.zig"); const Archive = @import("Archive.zig"); const Atom = @import("Atom.zig"); const Dwarf = @import("../Dwarf.zig"); diff --git a/src/link/Goff.zig b/src/link/Goff.zig index c222ae029f30954d830f49553b79393b6f1514f3..ec4cb1252b18b1d1937d16603f3111059234df6a 100644 --- a/src/link/Goff.zig +++ b/src/link/Goff.zig @@ -17,7 +17,6 @@ const codegen = @import("../codegen.zig"); const link = @import("../link.zig"); const trace = @import("../tracy.zig").trace; const build_options = @import("build_options"); -const Air = @import("../Air.zig"); base: link.File, @@ -74,13 +73,11 @@ pub fn updateFunc( pt: Zcu.PerThread, func_index: InternPool.Index, mir: *const codegen.AnyMir, - maybe_undef_air: *const Air, ) link.File.UpdateNavError!void { _ = self; _ = pt; _ = func_index; _ = mir; - _ = maybe_undef_air; unreachable; // we always use llvm } diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 6c081653ea3e8c2e8a68cdf0da441fe7f1451bca..3f3a94bee71a9964891152322c745e84969cdcd1 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -3040,12 +3040,11 @@ pub fn updateFunc( pt: Zcu.PerThread, func_index: InternPool.Index, mir: *const codegen.AnyMir, - maybe_undef_air: *const Air, ) link.File.UpdateNavError!void { if (build_options.skip_non_native and builtin.object_format != .macho) { @panic("Attempted to compile for object format that was disabled by build configuration"); } - return self.getZigObject().?.updateFunc(self, pt, func_index, mir, maybe_undef_air); + return self.getZigObject().?.updateFunc(self, pt, func_index, mir); } pub fn updateNav(self: *MachO, pt: Zcu.PerThread, nav: InternPool.Nav.Index) link.File.UpdateNavError!void { @@ -5431,7 +5430,6 @@ const target_util = @import("../target.zig"); const trace = @import("../tracy.zig").trace; const synthetic = @import("MachO/synthetic.zig"); -const Air = @import("../Air.zig"); const Alignment = Atom.Alignment; const Allocator = mem.Allocator; const Archive = @import("MachO/Archive.zig"); diff --git a/src/link/MachO/ZigObject.zig b/src/link/MachO/ZigObject.zig index f378a9c4106e7d8fe1ac9bd1bf01b9b233b78f4a..bd54be6caab7e7aaf9f64b120ed168eac1b44649 100644 --- a/src/link/MachO/ZigObject.zig +++ b/src/link/MachO/ZigObject.zig @@ -778,9 +778,6 @@ pub fn updateFunc( pt: Zcu.PerThread, func_index: InternPool.Index, mir: *const codegen.AnyMir, - /// This may be `undefined`; only pass it to `emitFunction`. - /// This parameter will eventually be removed. - maybe_undef_air: *const Air, ) link.File.UpdateNavError!void { const tracy = trace(@src()); defer tracy.end(); @@ -806,7 +803,6 @@ pub fn updateFunc( mir, &code_buffer, if (debug_wip_nav) |*wip_nav| .{ .dwarf = wip_nav } else .none, - maybe_undef_air, ); const code = code_buffer.items; @@ -1815,7 +1811,6 @@ const target_util = @import("../../target.zig"); const trace = @import("../../tracy.zig").trace; const std = @import("std"); -const Air = @import("../../Air.zig"); const Allocator = std.mem.Allocator; const Archive = @import("Archive.zig"); const Atom = @import("Atom.zig"); diff --git a/src/link/Plan9.zig b/src/link/Plan9.zig index 0d0699f0f056984d854e9f506ca1570d2a37fc10..c99ebb81bb78d33e3d64a130197a65cc48586f4c 100644 --- a/src/link/Plan9.zig +++ b/src/link/Plan9.zig @@ -387,9 +387,6 @@ pub fn updateFunc( pt: Zcu.PerThread, func_index: InternPool.Index, mir: *const codegen.AnyMir, - /// This may be `undefined`; only pass it to `emitFunction`. - /// This parameter will eventually be removed. - maybe_undef_air: *const Air, ) link.File.UpdateNavError!void { if (build_options.skip_non_native and builtin.object_format != .plan9) { @panic("Attempted to compile for object format that was disabled by build configuration"); @@ -422,7 +419,6 @@ pub fn updateFunc( mir, &code_buffer, .{ .plan9 = &dbg_info_output }, - maybe_undef_air, ); const code = try code_buffer.toOwnedSlice(gpa); self.getAtomPtr(atom_idx).code = .{ diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 82293b9c4541b78c0c249d50cdf65b862fe67e19..eda7552986706dd530ca521fcd90c48ea5171453 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -29,7 +29,6 @@ const leb = std.leb; const log = std.log.scoped(.link); const mem = std.mem; -const Air = @import("../Air.zig"); const Mir = @import("../arch/wasm/Mir.zig"); const CodeGen = @import("../arch/wasm/CodeGen.zig"); const abi = @import("../arch/wasm/abi.zig"); @@ -3182,14 +3181,12 @@ pub fn updateFunc( pt: Zcu.PerThread, func_index: InternPool.Index, any_mir: *const codegen.AnyMir, - maybe_undef_air: *const Air, ) !void { if (build_options.skip_non_native and builtin.object_format != .wasm) { @panic("Attempted to compile for object format that was disabled by build configuration"); } dev.check(.wasm_backend); - _ = maybe_undef_air; // we (correctly) do not need this // This linker implementation only works with codegen backend `.stage2_wasm`. const mir = &any_mir.wasm; diff --git a/src/link/Xcoff.zig b/src/link/Xcoff.zig index 93fda27f3f69482f8dc53f907f9abdac00257487..bbd8a3fea4324884536d7ecaad954bea601296da 100644 --- a/src/link/Xcoff.zig +++ b/src/link/Xcoff.zig @@ -17,7 +17,6 @@ const codegen = @import("../codegen.zig"); const link = @import("../link.zig"); const trace = @import("../tracy.zig").trace; const build_options = @import("build_options"); -const Air = @import("../Air.zig"); base: link.File, @@ -74,13 +73,11 @@ pub fn updateFunc( pt: Zcu.PerThread, func_index: InternPool.Index, mir: *const codegen.AnyMir, - maybe_undef_air: *const Air, ) link.File.UpdateNavError!void { _ = self; _ = pt; _ = func_index; _ = mir; - _ = maybe_undef_air; unreachable; // we always use llvm } -- 2.54.0 From ba53b140288b4518de38a8174ab7ad402607b8d4 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Sat, 7 Jun 2025 23:30:17 -0400 Subject: [PATCH 15/35] x86_64: remove linker references from codegen --- lib/std/heap/debug_allocator.zig | 4 +- src/arch/riscv64/CodeGen.zig | 4 +- src/arch/riscv64/Emit.zig | 4 +- src/arch/x86_64/CodeGen.zig | 3673 ++++++++++++++---------------- src/arch/x86_64/Emit.zig | 781 +++++-- src/arch/x86_64/Lower.zig | 346 +-- src/arch/x86_64/Mir.zig | 174 +- src/arch/x86_64/bits.zig | 26 +- src/arch/x86_64/encoder.zig | 17 +- src/codegen.zig | 158 +- src/link/Dwarf.zig | 72 +- src/link/Elf/Symbol.zig | 3 - src/link/Elf/ZigObject.zig | 6 +- src/link/MachO/Symbol.zig | 3 - src/link/MachO/ZigObject.zig | 6 +- src/target.zig | 2 +- 16 files changed, 2681 insertions(+), 2598 deletions(-) diff --git a/lib/std/heap/debug_allocator.zig b/lib/std/heap/debug_allocator.zig index 3243f1b1bd5f13215f5cea875745afed41f327ba..e8778fc9c1c7f4a86468dda65895c5bb08669b7a 100644 --- a/lib/std/heap/debug_allocator.zig +++ b/lib/std/heap/debug_allocator.zig @@ -212,8 +212,8 @@ pub fn DebugAllocator(comptime config: Config) type { DummyMutex{}; const DummyMutex = struct { - inline fn lock(_: *DummyMutex) void {} - inline fn unlock(_: *DummyMutex) void {} + inline fn lock(_: DummyMutex) void {} + inline fn unlock(_: DummyMutex) void {} }; const stack_n = config.stack_trace_frames; diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index 080760bbabc75d0ac972bdac5010ebc36e9e5c74..c82061a5ddc877519c3be792013dbf2bdb06bc41 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -3603,9 +3603,7 @@ fn airRuntimeNavPtr(func: *Func, inst: Air.Inst.Index) !void { const tlv_sym_index = if (func.bin_file.cast(.elf)) |elf_file| sym: { const zo = elf_file.zigObjectPtr().?; if (nav.getExtern(ip)) |e| { - const sym = try elf_file.getGlobalSymbol(nav.name.toSlice(ip), e.lib_name.toSlice(ip)); - zo.symbol(sym).flags.is_extern_ptr = true; - break :sym sym; + break :sym try elf_file.getGlobalSymbol(nav.name.toSlice(ip), e.lib_name.toSlice(ip)); } break :sym try zo.getOrCreateMetadataForNav(zcu, ty_nav.nav); } else return func.fail("TODO runtime_nav_ptr on {}", .{func.bin_file.tag}); diff --git a/src/arch/riscv64/Emit.zig b/src/arch/riscv64/Emit.zig index 095cfc278b4042ff5c8da1dbe7388a2677d55544..0561eb2019f861a68e1abd88787f1a9bdb0dfeaf 100644 --- a/src/arch/riscv64/Emit.zig +++ b/src/arch/riscv64/Emit.zig @@ -50,8 +50,8 @@ pub fn emitMir(emit: *Emit) Error!void { const atom_ptr = zo.symbol(symbol.atom_index).atom(elf_file).?; const sym = zo.symbol(symbol.sym_index); - if (sym.flags.is_extern_ptr and emit.lower.pic) { - return emit.fail("emit GOT relocation for symbol '{s}'", .{sym.name(elf_file)}); + if (emit.lower.pic) { + return emit.fail("know when to emit GOT relocation for symbol '{s}'", .{sym.name(elf_file)}); } const hi_r_type: u32 = @intFromEnum(std.elf.R_RISCV.HI20); diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 7d88307ba5a7b01628bcb6059b018823bd057a83..84b263a93e5b142d7b67086603f0c6a0671afd18 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -124,9 +124,11 @@ gpa: Allocator, pt: Zcu.PerThread, air: Air, liveness: Air.Liveness, -bin_file: *link.File, target: *const std.Target, -owner: Owner, +owner: union(enum) { + nav_index: InternPool.Nav.Index, + lazy_sym: link.File.LazySymbol, +}, inline_func: InternPool.Index, mod: *Module, args: []MCValue, @@ -150,8 +152,14 @@ eflags_inst: ?Air.Inst.Index = null, mir_instructions: std.MultiArrayList(Mir.Inst) = .empty, /// MIR extra data mir_extra: std.ArrayListUnmanaged(u32) = .empty, -mir_local_name_bytes: std.ArrayListUnmanaged(u8) = .empty, -mir_local_types: std.ArrayListUnmanaged(InternPool.Index) = .empty, +mir_string_bytes: std.ArrayListUnmanaged(u8) = .empty, +mir_strings: std.HashMapUnmanaged( + u32, + void, + std.hash_map.StringIndexContext, + std.hash_map.default_max_load_percentage, +) = .empty, +mir_locals: std.ArrayListUnmanaged(Mir.Local) = .empty, mir_table: std.ArrayListUnmanaged(Mir.Inst.Index) = .empty, /// The value is an offset into the `Function` `code` from the beginning. @@ -194,41 +202,6 @@ loop_switches: std.AutoHashMapUnmanaged(Air.Inst.Index, struct { next_temp_index: Temp.Index = @enumFromInt(0), temp_type: [Temp.Index.max]Type = undefined, -const Owner = union(enum) { - nav_index: InternPool.Nav.Index, - lazy_sym: link.File.LazySymbol, - - fn getSymbolIndex(owner: Owner, ctx: *CodeGen) !u32 { - const pt = ctx.pt; - switch (owner) { - .nav_index => |nav_index| if (ctx.bin_file.cast(.elf)) |elf_file| { - return elf_file.zigObjectPtr().?.getOrCreateMetadataForNav(pt.zcu, nav_index); - } else if (ctx.bin_file.cast(.macho)) |macho_file| { - return macho_file.getZigObject().?.getOrCreateMetadataForNav(macho_file, nav_index); - } else if (ctx.bin_file.cast(.coff)) |coff_file| { - const atom = try coff_file.getOrCreateAtomForNav(nav_index); - return coff_file.getAtom(atom).getSymbolIndex().?; - } else if (ctx.bin_file.cast(.plan9)) |p9_file| { - return p9_file.seeNav(pt, nav_index); - } else unreachable, - .lazy_sym => |lazy_sym| if (ctx.bin_file.cast(.elf)) |elf_file| { - return elf_file.zigObjectPtr().?.getOrCreateMetadataForLazySymbol(elf_file, pt, lazy_sym) catch |err| - ctx.fail("{s} creating lazy symbol", .{@errorName(err)}); - } else if (ctx.bin_file.cast(.macho)) |macho_file| { - return macho_file.getZigObject().?.getOrCreateMetadataForLazySymbol(macho_file, pt, lazy_sym) catch |err| - ctx.fail("{s} creating lazy symbol", .{@errorName(err)}); - } else if (ctx.bin_file.cast(.coff)) |coff_file| { - const atom = coff_file.getOrCreateAtomForLazySymbol(pt, lazy_sym) catch |err| - return ctx.fail("{s} creating lazy symbol", .{@errorName(err)}); - return coff_file.getAtom(atom).getSymbolIndex().?; - } else if (ctx.bin_file.cast(.plan9)) |p9_file| { - return p9_file.getOrCreateAtomForLazySymbol(pt, lazy_sym) catch |err| - return ctx.fail("{s} creating lazy symbol", .{@errorName(err)}); - } else unreachable, - } - } -}; - const MaskInfo = packed struct { kind: enum(u1) { sign, all }, inverted: bool = false, @@ -269,37 +242,22 @@ pub const MCValue = union(enum) { /// The value is in memory at a hard-coded address. /// If the type is a pointer, it means the pointer address is stored at this memory location. memory: u64, - /// The value is in memory at an address not-yet-allocated by the linker. - /// This traditionally corresponds to a relocation emitted in a relocatable object file. - load_symbol: bits.SymbolOffset, - /// The address of the memory location not-yet-allocated by the linker. - lea_symbol: bits.SymbolOffset, - /// The value is in memory at an address not-yet-allocated by the linker. - /// This must use a non-got pc-relative relocation. - load_pcrel: bits.SymbolOffset, - /// The address of the memory location not-yet-allocated by the linker. - /// This must use a non-got pc-relative relocation. - lea_pcrel: bits.SymbolOffset, /// The value is in memory at a constant offset from the address in a register. indirect: bits.RegisterOffset, - /// The value is in memory. - /// Payload is a symbol index. - load_direct: u32, - /// The value is a pointer to a value in memory. - /// Payload is a symbol index. - lea_direct: u32, - /// The value is in memory referenced indirectly via GOT. - /// Payload is a symbol index. - load_got: u32, - /// The value is a pointer to a value referenced indirectly via GOT. - /// Payload is a symbol index. - lea_got: u32, /// The value stored at an offset from a frame index /// Payload is a frame address. load_frame: bits.FrameAddr, /// The address of an offset from a frame index /// Payload is a frame address. lea_frame: bits.FrameAddr, + load_nav: InternPool.Nav.Index, + lea_nav: InternPool.Nav.Index, + load_uav: InternPool.Key.Ptr.BaseAddr.Uav, + lea_uav: InternPool.Key.Ptr.BaseAddr.Uav, + load_lazy_sym: link.File.LazySymbol, + lea_lazy_sym: link.File.LazySymbol, + load_extern_func: Mir.NullTerminatedString, + lea_extern_func: Mir.NullTerminatedString, /// Supports integer_per_element abi elementwise_args: packed struct { regs: u3, frame_off: i29, frame_index: FrameIndex }, /// This indicates that we have already allocated a frame index for this instruction, @@ -319,11 +277,14 @@ pub const MCValue = union(enum) { .register_mask, .eflags, .register_overflow, - .lea_symbol, - .lea_pcrel, - .lea_direct, - .lea_got, .lea_frame, + .lea_nav, + .load_uav, + .lea_uav, + .load_lazy_sym, + .lea_lazy_sym, + .lea_extern_func, + .load_extern_func, .elementwise_args, .reserved_frame, .air_ref, @@ -333,11 +294,8 @@ pub const MCValue = union(enum) { .register_triple, .register_quadruple, .memory, - .load_symbol, - .load_pcrel, - .load_got, - .load_direct, .indirect, + .load_nav, => true, .load_frame => |frame_addr| !frame_addr.index.isNamed(), }; @@ -353,7 +311,14 @@ pub const MCValue = union(enum) { fn isMemory(mcv: MCValue) bool { return switch (mcv) { - .memory, .indirect, .load_frame, .load_symbol => true, + .memory, + .indirect, + .load_frame, + .load_nav, + .load_uav, + .load_lazy_sym, + .load_extern_func, + => true, else => false, }; } @@ -423,7 +388,7 @@ pub const MCValue = union(enum) { fn address(mcv: MCValue) MCValue { return switch (mcv) { - .none, + .none => .none, .unreach, .dead, .undef, @@ -436,11 +401,11 @@ pub const MCValue = union(enum) { .register_offset, .register_overflow, .register_mask, - .lea_symbol, - .lea_pcrel, - .lea_direct, - .lea_got, .lea_frame, + .lea_nav, + .lea_uav, + .lea_lazy_sym, + .lea_extern_func, .elementwise_args, .reserved_frame, .air_ref, @@ -450,17 +415,17 @@ pub const MCValue = union(enum) { 0 => .{ .register = reg_off.reg }, else => .{ .register_offset = reg_off }, }, - .load_direct => |sym_index| .{ .lea_direct = sym_index }, - .load_got => |sym_index| .{ .lea_got = sym_index }, .load_frame => |frame_addr| .{ .lea_frame = frame_addr }, - .load_symbol => |sym_off| .{ .lea_symbol = sym_off }, - .load_pcrel => |sym_off| .{ .lea_pcrel = sym_off }, + .load_nav => |nav| .{ .lea_nav = nav }, + .load_uav => |uav| .{ .lea_uav = uav }, + .load_lazy_sym => |lazy_sym| .{ .lea_lazy_sym = lazy_sym }, + .load_extern_func => |extern_func| .{ .lea_extern_func = extern_func }, }; } fn deref(mcv: MCValue) MCValue { return switch (mcv) { - .none, + .none => .none, .unreach, .dead, .undef, @@ -472,11 +437,11 @@ pub const MCValue = union(enum) { .register_mask, .memory, .indirect, - .load_direct, - .load_got, .load_frame, - .load_symbol, - .load_pcrel, + .load_nav, + .load_uav, + .load_lazy_sym, + .load_extern_func, .elementwise_args, .reserved_frame, .air_ref, @@ -484,17 +449,17 @@ pub const MCValue = union(enum) { .immediate => |addr| .{ .memory = addr }, .register => |reg| .{ .indirect = .{ .reg = reg } }, .register_offset => |reg_off| .{ .indirect = reg_off }, - .lea_direct => |sym_index| .{ .load_direct = sym_index }, - .lea_got => |sym_index| .{ .load_got = sym_index }, .lea_frame => |frame_addr| .{ .load_frame = frame_addr }, - .lea_symbol => |sym_index| .{ .load_symbol = sym_index }, - .lea_pcrel => |sym_index| .{ .load_pcrel = sym_index }, + .lea_nav => |nav| .{ .load_nav = nav }, + .lea_uav => |uav| .{ .load_uav = uav }, + .lea_lazy_sym => |lazy_sym| .{ .load_lazy_sym = lazy_sym }, + .lea_extern_func => |extern_func| .{ .load_extern_func = extern_func }, }; } fn offset(mcv: MCValue, off: i32) MCValue { return switch (mcv) { - .none, + .none => .none, .unreach, .dead, .undef, @@ -510,15 +475,15 @@ pub const MCValue = union(enum) { .register_mask, .memory, .indirect, - .load_direct, - .lea_direct, - .load_got, - .lea_got, .load_frame, - .load_symbol, - .lea_symbol, - .load_pcrel, - .lea_pcrel, + .load_nav, + .lea_nav, + .load_uav, + .lea_uav, + .load_lazy_sym, + .lea_lazy_sym, + .load_extern_func, + .lea_extern_func, => switch (off) { 0 => mcv, else => unreachable, // not offsettable @@ -536,7 +501,7 @@ pub const MCValue = union(enum) { fn mem(mcv: MCValue, function: *CodeGen, mod_rm: Memory.Mod.Rm) !Memory { return switch (mcv) { - .none, + .none => .{ .mod = .{ .rm = mod_rm } }, .unreach, .dead, .undef, @@ -549,15 +514,13 @@ pub const MCValue = union(enum) { .register_offset, .register_overflow, .register_mask, - .load_direct, - .lea_direct, - .load_got, - .lea_got, .lea_frame, .elementwise_args, .reserved_frame, - .lea_symbol, - .lea_pcrel, + .lea_nav, + .lea_uav, + .lea_lazy_sym, + .lea_extern_func, => unreachable, .memory => |addr| if (std.math.cast(i32, @as(i64, @bitCast(addr)))) |small_addr| .{ .base = .{ .reg = .ds }, @@ -586,30 +549,10 @@ pub const MCValue = union(enum) { .disp = frame_addr.off + mod_rm.disp, } }, }, - .load_symbol => |sym_off| { - assert(sym_off.off == 0); - return .{ - .base = .{ .reloc = sym_off.sym_index }, - .mod = .{ .rm = .{ - .size = mod_rm.size, - .index = mod_rm.index, - .scale = mod_rm.scale, - .disp = sym_off.off + mod_rm.disp, - } }, - }; - }, - .load_pcrel => |sym_off| { - assert(sym_off.off == 0); - return .{ - .base = .{ .pcrel = sym_off.sym_index }, - .mod = .{ .rm = .{ - .size = mod_rm.size, - .index = mod_rm.index, - .scale = mod_rm.scale, - .disp = sym_off.off + mod_rm.disp, - } }, - }; - }, + .load_nav => |nav| .{ .base = .{ .nav = nav }, .mod = .{ .rm = mod_rm } }, + .load_uav => |uav| .{ .base = .{ .uav = uav }, .mod = .{ .rm = mod_rm } }, + .load_lazy_sym => |lazy_sym| .{ .base = .{ .lazy_sym = lazy_sym }, .mod = .{ .rm = mod_rm } }, + .load_extern_func => |extern_func| .{ .base = .{ .extern_func = extern_func }, .mod = .{ .rm = mod_rm } }, .air_ref => |ref| (try function.resolveInst(ref)).mem(function, mod_rm), }; } @@ -643,20 +586,20 @@ pub const MCValue = union(enum) { @as(u8, if (pl.info.inverted) '!' else ' '), @tagName(pl.reg), }), - .load_symbol => |pl| try writer.print("[sym:{} + 0x{x}]", .{ pl.sym_index, pl.off }), - .lea_symbol => |pl| try writer.print("sym:{} + 0x{x}", .{ pl.sym_index, pl.off }), - .load_pcrel => |pl| try writer.print("[sym@pcrel:{} + 0x{x}]", .{ pl.sym_index, pl.off }), - .lea_pcrel => |pl| try writer.print("sym@pcrel:{} + 0x{x}", .{ pl.sym_index, pl.off }), .indirect => |pl| try writer.print("[{s} + 0x{x}]", .{ @tagName(pl.reg), pl.off }), - .load_direct => |pl| try writer.print("[direct:{d}]", .{pl}), - .lea_direct => |pl| try writer.print("direct:{d}", .{pl}), - .load_got => |pl| try writer.print("[got:{d}]", .{pl}), - .lea_got => |pl| try writer.print("got:{d}", .{pl}), .load_frame => |pl| try writer.print("[{} + 0x{x}]", .{ pl.index, pl.off }), + .lea_frame => |pl| try writer.print("{} + 0x{x}", .{ pl.index, pl.off }), + .load_nav => |pl| try writer.print("[nav:{d}]", .{@intFromEnum(pl)}), + .lea_nav => |pl| try writer.print("nav:{d}", .{@intFromEnum(pl)}), + .load_uav => |pl| try writer.print("[uav:{d}]", .{@intFromEnum(pl.val)}), + .lea_uav => |pl| try writer.print("uav:{d}", .{@intFromEnum(pl.val)}), + .load_lazy_sym => |pl| try writer.print("[lazy:{s}:{d}]", .{ @tagName(pl.kind), @intFromEnum(pl.ty) }), + .lea_lazy_sym => |pl| try writer.print("lazy:{s}:{d}", .{ @tagName(pl.kind), @intFromEnum(pl.ty) }), + .load_extern_func => |pl| try writer.print("[extern:{d}]", .{@intFromEnum(pl)}), + .lea_extern_func => |pl| try writer.print("extern:{d}", .{@intFromEnum(pl)}), .elementwise_args => |pl| try writer.print("elementwise:{d}:[{} + 0x{x}]", .{ pl.regs, pl.frame_index, pl.frame_off, }), - .lea_frame => |pl| try writer.print("{} + 0x{x}", .{ pl.index, pl.off }), .reserved_frame => |pl| try writer.print("(dead:{})", .{pl}), .air_ref => |pl| try writer.print("(air:0x{x})", .{@intFromEnum(pl)}), } @@ -676,16 +619,16 @@ const InstTracking = struct { .undef, .immediate, .memory, - .load_direct, - .lea_direct, - .load_got, - .lea_got, .load_frame, .lea_frame, - .load_symbol, - .lea_symbol, - .load_pcrel, - .lea_pcrel, + .load_nav, + .lea_nav, + .load_uav, + .lea_uav, + .load_lazy_sym, + .lea_lazy_sym, + .load_extern_func, + .lea_extern_func, => result, .dead, .elementwise_args, @@ -779,15 +722,15 @@ const InstTracking = struct { .undef, .immediate, .memory, - .load_direct, - .lea_direct, - .load_got, - .lea_got, .lea_frame, - .load_symbol, - .lea_symbol, - .load_pcrel, - .lea_pcrel, + .load_nav, + .lea_nav, + .load_uav, + .lea_uav, + .load_lazy_sym, + .lea_lazy_sym, + .load_extern_func, + .lea_extern_func, => assert(std.meta.eql(self.long, target.long)), .dead, .eflags, @@ -975,6 +918,7 @@ pub fn generate( air: *const Air, liveness: *const Air.Liveness, ) codegen.CodeGenError!Mir { + _ = bin_file; const zcu = pt.zcu; const gpa = zcu.gpa; const ip = &zcu.intern_pool; @@ -991,7 +935,6 @@ pub fn generate( .liveness = liveness.*, .target = &mod.resolved_target.result, .mod = mod, - .bin_file = bin_file, .owner = .{ .nav_index = func.owner_nav }, .inline_func = func_index, .args = undefined, // populated after `resolveCallingConventionValues` @@ -1013,8 +956,9 @@ pub fn generate( function.inst_tracking.deinit(gpa); function.epilogue_relocs.deinit(gpa); function.mir_instructions.deinit(gpa); - function.mir_local_name_bytes.deinit(gpa); - function.mir_local_types.deinit(gpa); + function.mir_string_bytes.deinit(gpa); + function.mir_strings.deinit(gpa); + function.mir_locals.deinit(gpa); function.mir_extra.deinit(gpa); function.mir_table.deinit(gpa); } @@ -1101,27 +1045,27 @@ pub fn generate( var mir: Mir = .{ .instructions = .empty, .extra = &.{}, - .local_name_bytes = &.{}, - .local_types = &.{}, + .string_bytes = &.{}, + .locals = &.{}, .table = &.{}, .frame_locs = .empty, }; errdefer mir.deinit(gpa); mir.instructions = function.mir_instructions.toOwnedSlice(); mir.extra = try function.mir_extra.toOwnedSlice(gpa); - mir.local_name_bytes = try function.mir_local_name_bytes.toOwnedSlice(gpa); - mir.local_types = try function.mir_local_types.toOwnedSlice(gpa); + mir.string_bytes = try function.mir_string_bytes.toOwnedSlice(gpa); + mir.locals = try function.mir_locals.toOwnedSlice(gpa); mir.table = try function.mir_table.toOwnedSlice(gpa); mir.frame_locs = function.frame_locs.toOwnedSlice(); return mir; } -pub fn toTmpMir(cg: *CodeGen) Mir { +pub fn getTmpMir(cg: *CodeGen) Mir { return .{ .instructions = cg.mir_instructions.slice(), .extra = cg.mir_extra.items, - .local_name_bytes = cg.mir_local_name_bytes.items, - .local_types = cg.mir_local_types.items, + .string_bytes = cg.mir_string_bytes.items, + .locals = cg.mir_locals.items, .table = cg.mir_table.items, .frame_locs = cg.frame_locs.slice(), }; @@ -1135,10 +1079,9 @@ pub fn generateLazy( code: *std.ArrayListUnmanaged(u8), debug_output: link.File.DebugInfoOutput, ) codegen.CodeGenError!void { - const comp = bin_file.comp; - const gpa = comp.gpa; + const gpa = pt.zcu.gpa; // This function is for generating global code, so we use the root module. - const mod = comp.root_mod; + const mod = pt.zcu.comp.root_mod; var function: CodeGen = .{ .gpa = gpa, .pt = pt, @@ -1146,7 +1089,6 @@ pub fn generateLazy( .liveness = undefined, .target = &mod.resolved_target.result, .mod = mod, - .bin_file = bin_file, .owner = .{ .lazy_sym = lazy_sym }, .inline_func = undefined, .args = undefined, @@ -1159,8 +1101,9 @@ pub fn generateLazy( defer { function.inst_tracking.deinit(gpa); function.mir_instructions.deinit(gpa); - function.mir_local_name_bytes.deinit(gpa); - function.mir_local_types.deinit(gpa); + function.mir_string_bytes.deinit(gpa); + function.mir_strings.deinit(gpa); + function.mir_locals.deinit(gpa); function.mir_extra.deinit(gpa); function.mir_table.deinit(gpa); } @@ -1176,33 +1119,7 @@ pub fn generateLazy( else => |e| return e, }; - var emit: Emit = .{ - .lower = .{ - .bin_file = bin_file, - .target = function.target, - .allocator = gpa, - .mir = function.toTmpMir(), - .cc = .auto, - .src_loc = src_loc, - .output_mode = comp.config.output_mode, - .link_mode = comp.config.link_mode, - .pic = mod.pic, - }, - .atom_index = function.owner.getSymbolIndex(&function) catch |err| switch (err) { - error.CodegenFail => return error.CodegenFail, - else => |e| return e, - }, - .debug_output = debug_output, - .code = code, - .prev_di_loc = undefined, // no debug info yet - .prev_di_pc = undefined, // no debug info yet - }; - emit.emitMir() catch |err| switch (err) { - error.LowerFail, error.EmitFail => return function.failMsg(emit.lower.err_msg.?), - error.InvalidInstruction => return function.fail("failed to find a viable x86 instruction (Zig compiler bug)", .{}), - error.CannotEncode => return function.fail("failed to encode x86 instruction (Zig compiler bug)", .{}), - else => |e| return function.fail("failed to emit MIR: {s}", .{@errorName(e)}), - }; + try function.getTmpMir().emitLazy(bin_file, pt, src_loc, lazy_sym, code, debug_output); } const FormatNavData = struct { @@ -1250,17 +1167,12 @@ fn formatWipMir( _: std.fmt.FormatOptions, writer: anytype, ) @TypeOf(writer).Error!void { - const comp = data.self.bin_file.comp; var lower: Lower = .{ - .bin_file = data.self.bin_file, .target = data.self.target, .allocator = data.self.gpa, - .mir = data.self.toTmpMir(), + .mir = data.self.getTmpMir(), .cc = .auto, .src_loc = data.self.src_loc, - .output_mode = comp.config.output_mode, - .link_mode = comp.config.link_mode, - .pic = data.self.mod.pic, }; var first = true; for ((lower.lowerMir(data.inst) catch |err| switch (err) { @@ -1317,13 +1229,6 @@ fn formatWipMir( .pseudo_dbg_arg_i_64, .pseudo_dbg_var_i_64 => try writer.print(" {d}", .{ mir_inst.data.i64, }), - .pseudo_dbg_arg_reloc, .pseudo_dbg_var_reloc => { - const mem_op: encoder.Instruction.Operand = .{ .mem = .initSib(.qword, .{ - .base = .{ .reloc = mir_inst.data.reloc.sym_index }, - .disp = mir_inst.data.reloc.off, - }) }; - try writer.print(" {}", .{mem_op.fmt(.m)}); - }, .pseudo_dbg_arg_ro, .pseudo_dbg_var_ro => { const mem_op: encoder.Instruction.Operand = .{ .mem = .initSib(.qword, .{ .base = .{ .reg = mir_inst.data.ro.reg }, @@ -1399,6 +1304,22 @@ fn addExtraAssumeCapacity(self: *CodeGen, extra: anytype) u32 { return result; } +fn addString(cg: *CodeGen, string: []const u8) Allocator.Error!Mir.NullTerminatedString { + try cg.mir_string_bytes.ensureUnusedCapacity(cg.gpa, string.len + 1); + try cg.mir_strings.ensureUnusedCapacityContext(cg.gpa, 1, .{ .bytes = &cg.mir_string_bytes }); + + const mir_string_gop = cg.mir_strings.getOrPutAssumeCapacityAdapted( + string, + std.hash_map.StringIndexAdapter{ .bytes = &cg.mir_string_bytes }, + ); + if (!mir_string_gop.found_existing) { + mir_string_gop.key_ptr.* = @intCast(cg.mir_string_bytes.items.len); + cg.mir_string_bytes.appendSliceAssumeCapacity(string); + cg.mir_string_bytes.appendAssumeCapacity(0); + } + return @enumFromInt(mir_string_gop.key_ptr.*); +} + fn asmOps(self: *CodeGen, tag: Mir.Inst.FixedTag, ops: [4]Operand) !void { return switch (ops[0]) { .none => self.asmOpOnly(tag), @@ -1714,21 +1635,36 @@ fn asmImmediate(self: *CodeGen, tag: Mir.Inst.FixedTag, imm: Immediate) !void { .ops = switch (imm) { .signed => .i_s, .unsigned => .i_u, - .reloc => .rel, + .nav => .nav, + .uav => .uav, + .lazy_sym => .lazy_sym, + .extern_func => .extern_func, }, .data = switch (imm) { - .reloc => |sym_off| reloc: { - assert(tag[0] == ._); - break :reloc .{ .reloc = sym_off }; - }, .signed, .unsigned => .{ .i = .{ .fixes = tag[0], .i = switch (imm) { .signed => |s| @bitCast(s), .unsigned => |u| @intCast(u), - .reloc => unreachable, + .nav, .uav, .lazy_sym, .extern_func => unreachable, }, } }, + .nav => |nav| switch (tag[0]) { + ._ => .{ .nav = nav }, + else => unreachable, + }, + .uav => |uav| switch (tag[0]) { + ._ => .{ .uav = uav }, + else => unreachable, + }, + .lazy_sym => |lazy_sym| switch (tag[0]) { + ._ => .{ .lazy_sym = lazy_sym }, + else => unreachable, + }, + .extern_func => |extern_func| switch (tag[0]) { + ._ => .{ .extern_func = extern_func }, + else => unreachable, + }, }, }); } @@ -1743,7 +1679,7 @@ fn asmImmediateRegister(self: *CodeGen, tag: Mir.Inst.FixedTag, imm: Immediate, .i = @as(u8, switch (imm) { .signed => |s| @bitCast(@as(i8, @intCast(s))), .unsigned => |u| @intCast(u), - .reloc => unreachable, + .nav, .uav, .lazy_sym, .extern_func => unreachable, }), } }, }); @@ -1758,12 +1694,12 @@ fn asmImmediateImmediate(self: *CodeGen, tag: Mir.Inst.FixedTag, imm1: Immediate .i1 = switch (imm1) { .signed => |s| @bitCast(@as(i16, @intCast(s))), .unsigned => |u| @intCast(u), - .reloc => unreachable, + .nav, .uav, .lazy_sym, .extern_func => unreachable, }, .i2 = switch (imm2) { .signed => |s| @bitCast(@as(i8, @intCast(s))), .unsigned => |u| @intCast(u), - .reloc => unreachable, + .nav, .uav, .lazy_sym, .extern_func => unreachable, }, } }, }); @@ -1788,7 +1724,7 @@ fn asmRegisterImmediate(self: *CodeGen, tag: Mir.Inst.FixedTag, reg: Register, i .{ .ri_u, small } else .{ .ri_64, try self.addExtra(Mir.Imm64.encode(imm.unsigned)) }, - .reloc => unreachable, + .nav, .uav, .lazy_sym, .extern_func => unreachable, }; _ = try self.addInst(.{ .tag = tag[1], @@ -1860,7 +1796,7 @@ fn asmRegisterRegisterRegisterImmediate( .i = switch (imm) { .signed => |s| @bitCast(@as(i8, @intCast(s))), .unsigned => |u| @intCast(u), - .reloc => unreachable, + .nav, .uav, .lazy_sym, .extern_func => unreachable, }, } }, }); @@ -1878,7 +1814,7 @@ fn asmRegisterRegisterImmediate( .ops = switch (imm) { .signed => .rri_s, .unsigned => .rri_u, - .reloc => unreachable, + .nav, .uav, .lazy_sym, .extern_func => unreachable, }, .data = .{ .rri = .{ .fixes = tag[0], @@ -1887,7 +1823,7 @@ fn asmRegisterRegisterImmediate( .i = switch (imm) { .signed => |s| @bitCast(s), .unsigned => |u| @intCast(u), - .reloc => unreachable, + .nav, .uav, .lazy_sym, .extern_func => unreachable, }, } }, }); @@ -1985,7 +1921,7 @@ fn asmRegisterMemoryImmediate( if (switch (imm) { .signed => |s| if (std.math.cast(i16, s)) |x| @as(u16, @bitCast(x)) else null, .unsigned => |u| std.math.cast(u16, u), - .reloc => unreachable, + .nav, .uav, .lazy_sym, .extern_func => unreachable, }) |small_imm| { _ = try self.addInst(.{ .tag = tag[1], @@ -2001,7 +1937,7 @@ fn asmRegisterMemoryImmediate( const payload = try self.addExtra(Mir.Imm32{ .imm = switch (imm) { .signed => |s| @bitCast(s), .unsigned => |u| @as(u32, @intCast(u)), - .reloc => unreachable, + .nav, .uav, .lazy_sym, .extern_func => unreachable, } }); assert(payload + 1 == try self.addExtra(Mir.Memory.encode(m))); _ = try self.addInst(.{ @@ -2009,7 +1945,7 @@ fn asmRegisterMemoryImmediate( .ops = switch (imm) { .signed => .rmi_s, .unsigned => .rmi_u, - .reloc => unreachable, + .nav, .uav, .lazy_sym, .extern_func => unreachable, }, .data = .{ .rx = .{ .fixes = tag[0], @@ -2057,7 +1993,7 @@ fn asmMemoryImmediate(self: *CodeGen, tag: Mir.Inst.FixedTag, m: Memory, imm: Im const payload = try self.addExtra(Mir.Imm32{ .imm = switch (imm) { .signed => |s| @bitCast(s), .unsigned => |u| @intCast(u), - .reloc => unreachable, + .nav, .uav, .lazy_sym, .extern_func => unreachable, } }); assert(payload + 1 == try self.addExtra(Mir.Memory.encode(m))); _ = try self.addInst(.{ @@ -2065,7 +2001,7 @@ fn asmMemoryImmediate(self: *CodeGen, tag: Mir.Inst.FixedTag, m: Memory, imm: Im .ops = switch (imm) { .signed => .mi_s, .unsigned => .mi_u, - .reloc => unreachable, + .nav, .uav, .lazy_sym, .extern_func => unreachable, }, .data = .{ .x = .{ .fixes = tag[0], @@ -2347,16 +2283,18 @@ fn genMainBody( var air_arg_index: usize = 0; const fn_info = zcu.typeToFunc(cg.fn_type).?; var fn_param_index: usize = 0; - try cg.mir_local_types.ensureTotalCapacity(cg.gpa, fn_info.param_types.len); var zir_param_index: usize = 0; for (zir.getParamBody(func_zir_inst)) |zir_param_inst| { - const name = zir.nullTerminatedString(zir.getParamName(zir_param_inst) orelse continue); + const name = switch (zir.getParamName(zir_param_inst) orelse break) { + .empty => .none, + else => |zir_name| try cg.addString(zir.nullTerminatedString(zir_name)), + }; defer zir_param_index += 1; - try cg.mir_local_name_bytes.appendSlice(cg.gpa, name[0 .. name.len + 1]); if (comptime_args.len > 0) switch (comptime_args.get(ip)[zir_param_index]) { .none => {}, else => |comptime_arg| { + try cg.mir_locals.append(cg.gpa, .{ .name = name, .type = ip.typeOf(comptime_arg) }); _ = try cg.addInst(.{ .tag = .pseudo, .ops = .pseudo_dbg_arg_val, @@ -2366,9 +2304,9 @@ fn genMainBody( }, }; - const arg_ty: Type = .fromInterned(fn_info.param_types.get(ip)[fn_param_index]); + const arg_ty = fn_info.param_types.get(ip)[fn_param_index]; + try cg.mir_locals.append(cg.gpa, .{ .name = name, .type = arg_ty }); fn_param_index += 1; - cg.mir_local_types.appendAssumeCapacity(arg_ty.toIntern()); if (air_arg_index == air_args_body.len) { try cg.asmPseudo(.pseudo_dbg_arg_none); @@ -2381,7 +2319,11 @@ fn genMainBody( continue; } air_arg_index += 1; - try cg.genLocalDebugInfo(.arg, arg_ty, cg.getResolvedInstValue(air_arg_inst).short); + try cg.genLocalDebugInfo( + .arg, + .fromInterned(arg_ty), + cg.getResolvedInstValue(air_arg_inst).short, + ); } if (fn_info.is_var_args) try cg.asmPseudo(.pseudo_dbg_var_args_none); } @@ -3453,7 +3395,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__addhf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__addhf3" } }, .unused, .unused, .unused, @@ -3585,7 +3527,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__addhf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__addhf3" } }, .unused, .unused, .unused, @@ -3621,7 +3563,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__addhf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__addhf3" } }, .unused, .unused, .unused, @@ -3658,7 +3600,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__addhf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__addhf3" } }, .{ .type = .f16, .kind = .{ .reg = .ax } }, .unused, .unused, @@ -3698,7 +3640,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .f32, .kind = .mem }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__addhf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__addhf3" } }, .unused, .unused, .unused, @@ -4183,7 +4125,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__addtf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__addtf3" } }, .unused, .unused, .unused, @@ -4215,7 +4157,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__addtf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__addtf3" } }, .unused, .unused, .unused, @@ -4250,7 +4192,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__addtf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__addtf3" } }, .unused, .unused, .unused, @@ -4285,7 +4227,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__addtf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__addtf3" } }, .unused, .unused, .unused, @@ -13885,7 +13827,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__subhf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__subhf3" } }, .unused, .unused, .unused, @@ -14017,7 +13959,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__subhf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__subhf3" } }, .unused, .unused, .unused, @@ -14053,7 +13995,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__subhf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__subhf3" } }, .unused, .unused, .unused, @@ -14090,7 +14032,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__subhf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__subhf3" } }, .{ .type = .f16, .kind = .{ .reg = .ax } }, .unused, .unused, @@ -14130,7 +14072,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .f32, .kind = .mem }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__subhf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__subhf3" } }, .unused, .unused, .unused, @@ -14632,7 +14574,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__subtf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__subtf3" } }, .unused, .unused, .unused, @@ -14664,7 +14606,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__subtf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__subtf3" } }, .unused, .unused, .unused, @@ -14699,7 +14641,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__subtf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__subtf3" } }, .unused, .unused, .unused, @@ -14734,7 +14676,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__subtf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__subtf3" } }, .unused, .unused, .unused, @@ -23415,7 +23357,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__mulhf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__mulhf3" } }, .unused, .unused, .unused, @@ -23547,7 +23489,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__mulhf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__mulhf3" } }, .unused, .unused, .unused, @@ -23583,7 +23525,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__mulhf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__mulhf3" } }, .unused, .unused, .unused, @@ -23620,7 +23562,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__mulhf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__mulhf3" } }, .{ .type = .f16, .kind = .{ .reg = .ax } }, .unused, .unused, @@ -23660,7 +23602,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .f32, .kind = .mem }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__mulhf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__mulhf3" } }, .unused, .unused, .unused, @@ -24145,7 +24087,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__multf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__multf3" } }, .unused, .unused, .unused, @@ -24177,7 +24119,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__multf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__multf3" } }, .unused, .unused, .unused, @@ -24212,7 +24154,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__multf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__multf3" } }, .unused, .unused, .unused, @@ -24247,7 +24189,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__multf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__multf3" } }, .unused, .unused, .unused, @@ -26107,7 +26049,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__mulhf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__mulhf3" } }, .unused, .unused, .unused, @@ -26239,7 +26181,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__mulhf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__mulhf3" } }, .unused, .unused, .unused, @@ -26275,7 +26217,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__mulhf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__mulhf3" } }, .unused, .unused, .unused, @@ -26312,7 +26254,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__mulhf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__mulhf3" } }, .{ .type = .f16, .kind = .{ .reg = .ax } }, .unused, .unused, @@ -26352,7 +26294,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .f32, .kind = .mem }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__mulhf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__mulhf3" } }, .unused, .unused, .unused, @@ -26837,7 +26779,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__multf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__multf3" } }, .unused, .unused, .unused, @@ -26869,7 +26811,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__multf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__multf3" } }, .unused, .unused, .unused, @@ -26904,7 +26846,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__multf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__multf3" } }, .unused, .unused, .unused, @@ -26939,7 +26881,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__multf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__multf3" } }, .unused, .unused, .unused, @@ -32247,7 +32189,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__divhf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__divhf3" } }, .unused, .unused, .unused, @@ -32379,7 +32321,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__divhf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__divhf3" } }, .unused, .unused, .unused, @@ -32415,7 +32357,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__divhf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__divhf3" } }, .unused, .unused, .unused, @@ -32452,7 +32394,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__divhf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__divhf3" } }, .{ .type = .f16, .kind = .{ .reg = .ax } }, .unused, .unused, @@ -32492,7 +32434,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .f32, .kind = .mem }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__divhf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__divhf3" } }, .unused, .unused, .unused, @@ -32995,7 +32937,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__divtf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__divtf3" } }, .unused, .unused, .unused, @@ -33027,7 +32969,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__divtf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__divtf3" } }, .unused, .unused, .unused, @@ -33062,7 +33004,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__divtf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__divtf3" } }, .unused, .unused, .unused, @@ -33097,7 +33039,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__divtf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__divtf3" } }, .unused, .unused, .unused, @@ -33181,8 +33123,8 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__divhf3" } } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__trunch" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__divhf3" } }, + .{ .type = .usize, .kind = .{ .extern_func = "__trunch" } }, .unused, .unused, .unused, @@ -33323,8 +33265,8 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__divhf3" } } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__trunch" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__divhf3" } }, + .{ .type = .usize, .kind = .{ .extern_func = "__trunch" } }, .unused, .unused, .unused, @@ -33360,8 +33302,8 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__divhf3" } } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__trunch" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__divhf3" } }, + .{ .type = .usize, .kind = .{ .extern_func = "__trunch" } }, .unused, .unused, .unused, @@ -33398,8 +33340,8 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__divhf3" } } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__trunch" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__divhf3" } }, + .{ .type = .usize, .kind = .{ .extern_func = "__trunch" } }, .{ .type = .f16, .kind = .{ .reg = .ax } }, .unused, .unused, @@ -33439,8 +33381,8 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .f32, .kind = .mem }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__divhf3" } } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__trunch" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__divhf3" } }, + .{ .type = .usize, .kind = .{ .extern_func = "__trunch" } }, .unused, .unused, .unused, @@ -33508,7 +33450,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "truncf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "truncf" } }, .unused, .unused, .unused, @@ -33572,7 +33514,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f32, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "truncf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "truncf" } }, .unused, .unused, .unused, @@ -33721,7 +33663,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "trunc" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "trunc" } }, .unused, .unused, .unused, @@ -33751,8 +33693,8 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__divdf3" } } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "trunc" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__divdf3" } }, + .{ .type = .usize, .kind = .{ .extern_func = "trunc" } }, .unused, .unused, .unused, @@ -33899,7 +33841,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "trunc" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "trunc" } }, .unused, .unused, .unused, @@ -33935,8 +33877,8 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, .{ .type = .f64, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__divdf3" } } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "trunc" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__divdf3" } }, + .{ .type = .usize, .kind = .{ .extern_func = "trunc" } }, .unused, .unused, .unused, @@ -33973,7 +33915,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .f80, .kind = .{ .reg = .st6 } }, .{ .type = .f80, .kind = .{ .reg = .st7 } }, .{ .type = .f80, .kind = .{ .frame = .call_frame } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__truncx" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__truncx" } }, .unused, .unused, .unused, @@ -34007,7 +33949,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .f80, .kind = .{ .reg = .st6 } }, .{ .type = .f80, .kind = .{ .reg = .st7 } }, .{ .type = .f80, .kind = .{ .frame = .call_frame } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__truncx" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__truncx" } }, .unused, .unused, .unused, @@ -34041,8 +33983,8 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__divtf3" } } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "truncq" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__divtf3" } }, + .{ .type = .usize, .kind = .{ .extern_func = "truncq" } }, .unused, .unused, .unused, @@ -34074,8 +34016,8 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__divtf3" } } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "truncq" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__divtf3" } }, + .{ .type = .usize, .kind = .{ .extern_func = "truncq" } }, .unused, .unused, .unused, @@ -34110,8 +34052,8 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__divtf3" } } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "truncq" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__divtf3" } }, + .{ .type = .usize, .kind = .{ .extern_func = "truncq" } }, .unused, .unused, .unused, @@ -34146,8 +34088,8 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__divtf3" } } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "truncq" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__divtf3" } }, + .{ .type = .usize, .kind = .{ .extern_func = "truncq" } }, .unused, .unused, .unused, @@ -34237,12 +34179,12 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__divhf3" } } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = switch (direction) { + .{ .type = .usize, .kind = .{ .extern_func = "__divhf3" } }, + .{ .type = .usize, .kind = .{ .extern_func = switch (direction) { else => unreachable, .zero => "__trunch", .down => "__floorh", - } } } }, + } } }, .unused, .unused, .unused, @@ -34377,12 +34319,12 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__divhf3" } } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = switch (direction) { + .{ .type = .usize, .kind = .{ .extern_func = "__divhf3" } }, + .{ .type = .usize, .kind = .{ .extern_func = switch (direction) { else => unreachable, .zero => "__trunch", .down => "__floorh", - } } } }, + } } }, .unused, .unused, .unused, @@ -34418,12 +34360,12 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__divhf3" } } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = switch (direction) { + .{ .type = .usize, .kind = .{ .extern_func = "__divhf3" } }, + .{ .type = .usize, .kind = .{ .extern_func = switch (direction) { else => unreachable, .zero => "__trunch", .down => "__floorh", - } } } }, + } } }, .unused, .unused, .unused, @@ -34460,12 +34402,12 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__divhf3" } } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = switch (direction) { + .{ .type = .usize, .kind = .{ .extern_func = "__divhf3" } }, + .{ .type = .usize, .kind = .{ .extern_func = switch (direction) { else => unreachable, .zero => "__trunch", .down => "__floorh", - } } } }, + } } }, .{ .type = .f16, .kind = .{ .reg = .ax } }, .unused, .unused, @@ -34505,12 +34447,12 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .f32, .kind = .mem }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__divhf3" } } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = switch (direction) { + .{ .type = .usize, .kind = .{ .extern_func = "__divhf3" } }, + .{ .type = .usize, .kind = .{ .extern_func = switch (direction) { else => unreachable, .zero => "__trunch", .down => "__floorh", - } } } }, + } } }, .unused, .unused, .unused, @@ -34578,11 +34520,11 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = switch (direction) { + .{ .type = .usize, .kind = .{ .extern_func = switch (direction) { else => unreachable, .zero => "truncf", .down => "floorf", - } } } }, + } } }, .unused, .unused, .unused, @@ -34646,11 +34588,11 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f32, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = switch (direction) { + .{ .type = .usize, .kind = .{ .extern_func = switch (direction) { else => unreachable, .zero => "truncf", .down => "floorf", - } } } }, + } } }, .unused, .unused, .unused, @@ -34799,11 +34741,11 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = switch (direction) { + .{ .type = .usize, .kind = .{ .extern_func = switch (direction) { else => unreachable, .zero => "trunc", .down => "floor", - } } } }, + } } }, .unused, .unused, .unused, @@ -34833,12 +34775,12 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__divdf3" } } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = switch (direction) { + .{ .type = .usize, .kind = .{ .extern_func = "__divdf3" } }, + .{ .type = .usize, .kind = .{ .extern_func = switch (direction) { else => unreachable, .zero => "trunc", .down => "floor", - } } } }, + } } }, .unused, .unused, .unused, @@ -34985,11 +34927,11 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = switch (direction) { + .{ .type = .usize, .kind = .{ .extern_func = switch (direction) { else => unreachable, .zero => "trunc", .down => "floor", - } } } }, + } } }, .unused, .unused, .unused, @@ -35025,12 +34967,12 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, .{ .type = .f64, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__divdf3" } } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = switch (direction) { + .{ .type = .usize, .kind = .{ .extern_func = "__divdf3" } }, + .{ .type = .usize, .kind = .{ .extern_func = switch (direction) { else => unreachable, .zero => "trunc", .down => "floor", - } } } }, + } } }, .unused, .unused, .unused, @@ -35067,11 +35009,11 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .f80, .kind = .{ .reg = .st6 } }, .{ .type = .f80, .kind = .{ .reg = .st7 } }, .{ .type = .f80, .kind = .{ .frame = .call_frame } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = switch (direction) { + .{ .type = .usize, .kind = .{ .extern_func = switch (direction) { else => unreachable, .zero => "__truncx", .down => "__floorx", - } } } }, + } } }, .unused, .unused, .unused, @@ -35103,11 +35045,11 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .f80, .kind = .{ .reg = .st7 } }, .{ .type = .f80, .kind = .{ .frame = .call_frame } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = switch (direction) { + .{ .type = .usize, .kind = .{ .extern_func = switch (direction) { else => unreachable, .zero => "__truncx", .down => "__floorx", - } } } }, + } } }, .unused, .unused, .unused, @@ -35140,11 +35082,11 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .f80, .kind = .{ .reg = .st7 } }, .{ .type = .f80, .kind = .{ .frame = .call_frame } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = switch (direction) { + .{ .type = .usize, .kind = .{ .extern_func = switch (direction) { else => unreachable, .zero => "__truncx", .down => "__floorx", - } } } }, + } } }, .unused, .unused, .unused, @@ -35178,11 +35120,11 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .f80, .kind = .{ .reg = .st6 } }, .{ .type = .f80, .kind = .{ .reg = .st7 } }, .{ .type = .f80, .kind = .{ .frame = .call_frame } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = switch (direction) { + .{ .type = .usize, .kind = .{ .extern_func = switch (direction) { else => unreachable, .zero => "__truncx", .down => "__floorx", - } } } }, + } } }, .unused, .unused, .unused, @@ -35216,12 +35158,12 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__divtf3" } } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = switch (direction) { + .{ .type = .usize, .kind = .{ .extern_func = "__divtf3" } }, + .{ .type = .usize, .kind = .{ .extern_func = switch (direction) { else => unreachable, .zero => "truncq", .down => "floorq", - } } } }, + } } }, .unused, .unused, .unused, @@ -35253,12 +35195,12 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__divtf3" } } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = switch (direction) { + .{ .type = .usize, .kind = .{ .extern_func = "__divtf3" } }, + .{ .type = .usize, .kind = .{ .extern_func = switch (direction) { else => unreachable, .zero => "truncq", .down => "floorq", - } } } }, + } } }, .unused, .unused, .unused, @@ -35293,12 +35235,12 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__divtf3" } } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = switch (direction) { + .{ .type = .usize, .kind = .{ .extern_func = "__divtf3" } }, + .{ .type = .usize, .kind = .{ .extern_func = switch (direction) { else => unreachable, .zero => "truncq", .down => "floorq", - } } } }, + } } }, .unused, .unused, .unused, @@ -35333,12 +35275,12 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__divtf3" } } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = switch (direction) { + .{ .type = .usize, .kind = .{ .extern_func = "__divtf3" } }, + .{ .type = .usize, .kind = .{ .extern_func = switch (direction) { else => unreachable, .zero => "truncq", .down => "floorq", - } } } }, + } } }, .unused, .unused, .unused, @@ -35525,9 +35467,9 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .i128, .kind = .{ .param_gpr_pair = .{ .cc = .ccc, .at = 0 } } }, .{ .type = .i64, .kind = .{ .param_gpr = .{ .cc = .ccc, .at = 3 } } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__modti3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__modti3" } }, .{ .type = .i64, .kind = .{ .rc = .general_purpose } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__divti3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__divti3" } }, .unused, .unused, .unused, @@ -35576,9 +35518,9 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .usize, .kind = .{ .param_gpr = .{ .cc = .ccc, .at = 2 } } }, .{ .type = .usize, .kind = .{ .param_gpr = .{ .cc = .ccc, .at = 3 } } }, .{ .type = .i32, .kind = .{ .rc = .general_purpose } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__divei4" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__divei4" } }, .{ .kind = .{ .mem_of_type = .dst0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__modei4" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__modei4" } }, .unused, .unused, .unused, @@ -35657,8 +35599,8 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__divhf3" } } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floorh" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__divhf3" } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floorh" } }, .unused, .unused, .unused, @@ -35799,8 +35741,8 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__divhf3" } } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floorh" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__divhf3" } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floorh" } }, .unused, .unused, .unused, @@ -35836,8 +35778,8 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__divhf3" } } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floorh" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__divhf3" } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floorh" } }, .unused, .unused, .unused, @@ -35874,8 +35816,8 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__divhf3" } } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floorh" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__divhf3" } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floorh" } }, .{ .type = .f16, .kind = .{ .reg = .ax } }, .unused, .unused, @@ -35915,8 +35857,8 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .f32, .kind = .mem }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__divhf3" } } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floorh" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__divhf3" } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floorh" } }, .unused, .unused, .unused, @@ -35984,7 +35926,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "floorf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "floorf" } }, .unused, .unused, .unused, @@ -36048,7 +35990,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f32, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "floorf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "floorf" } }, .unused, .unused, .unused, @@ -36197,7 +36139,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "floor" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "floor" } }, .unused, .unused, .unused, @@ -36227,8 +36169,8 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__divdf3" } } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "floor" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__divdf3" } }, + .{ .type = .usize, .kind = .{ .extern_func = "floor" } }, .unused, .unused, .unused, @@ -36375,7 +36317,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "floor" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "floor" } }, .unused, .unused, .unused, @@ -36411,8 +36353,8 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, .{ .type = .f64, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__divdf3" } } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "floor" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__divdf3" } }, + .{ .type = .usize, .kind = .{ .extern_func = "floor" } }, .unused, .unused, .unused, @@ -36449,7 +36391,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .f80, .kind = .{ .reg = .st6 } }, .{ .type = .f80, .kind = .{ .reg = .st7 } }, .{ .type = .f80, .kind = .{ .frame = .call_frame } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floorx" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floorx" } }, .unused, .unused, .unused, @@ -36483,7 +36425,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .f80, .kind = .{ .reg = .st6 } }, .{ .type = .f80, .kind = .{ .reg = .st7 } }, .{ .type = .f80, .kind = .{ .frame = .call_frame } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floorx" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floorx" } }, .unused, .unused, .unused, @@ -36517,8 +36459,8 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__divtf3" } } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "floorq" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__divtf3" } }, + .{ .type = .usize, .kind = .{ .extern_func = "floorq" } }, .unused, .unused, .unused, @@ -36550,8 +36492,8 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__divtf3" } } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "floorq" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__divtf3" } }, + .{ .type = .usize, .kind = .{ .extern_func = "floorq" } }, .unused, .unused, .unused, @@ -36586,8 +36528,8 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__divtf3" } } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "floorq" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__divtf3" } }, + .{ .type = .usize, .kind = .{ .extern_func = "floorq" } }, .unused, .unused, .unused, @@ -36622,8 +36564,8 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__divtf3" } } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "floorq" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__divtf3" } }, + .{ .type = .usize, .kind = .{ .extern_func = "floorq" } }, .unused, .unused, .unused, @@ -36779,7 +36721,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__modti3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__modti3" } }, .unused, .unused, .unused, @@ -36808,7 +36750,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__umodti3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__umodti3" } }, .unused, .unused, .unused, @@ -36841,7 +36783,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .usize, .kind = .{ .param_gpr = .{ .cc = .ccc, .at = 1 } } }, .{ .type = .usize, .kind = .{ .param_gpr = .{ .cc = .ccc, .at = 2 } } }, .{ .type = .usize, .kind = .{ .param_gpr = .{ .cc = .ccc, .at = 3 } } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__modei4" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__modei4" } }, .unused, .unused, .unused, @@ -36874,7 +36816,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .usize, .kind = .{ .param_gpr = .{ .cc = .ccc, .at = 1 } } }, .{ .type = .usize, .kind = .{ .param_gpr = .{ .cc = .ccc, .at = 2 } } }, .{ .type = .usize, .kind = .{ .param_gpr = .{ .cc = .ccc, .at = 3 } } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__umodei4" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__umodei4" } }, .unused, .unused, .unused, @@ -37238,7 +37180,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .i64, .kind = .{ .param_gpr = .{ .cc = .ccc, .at = 1 } } }, .{ .type = .u64, .kind = .{ .param_gpr = .{ .cc = .ccc, .at = 2 } } }, .{ .type = .i64, .kind = .{ .param_gpr = .{ .cc = .ccc, .at = 3 } } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__modti3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__modti3" } }, .{ .type = .u64, .kind = .{ .ret_gpr = .{ .cc = .ccc, .at = 0 } } }, .unused, .unused, @@ -37276,7 +37218,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .u64, .kind = .{ .param_gpr = .{ .cc = .ccc, .at = 1 } } }, .{ .type = .u64, .kind = .{ .param_gpr = .{ .cc = .ccc, .at = 2 } } }, .{ .type = .u64, .kind = .{ .param_gpr = .{ .cc = .ccc, .at = 3 } } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__umodti3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__umodti3" } }, .{ .type = .u64, .kind = .{ .ret_gpr = .{ .cc = .ccc, .at = 0 } } }, .unused, .unused, @@ -37314,7 +37256,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .usize, .kind = .{ .param_gpr = .{ .cc = .ccc, .at = 1 } } }, .{ .type = .usize, .kind = .{ .param_gpr = .{ .cc = .ccc, .at = 2 } } }, .{ .type = .usize, .kind = .{ .param_gpr = .{ .cc = .ccc, .at = 3 } } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__modei4" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__modei4" } }, .unused, .unused, .unused, @@ -37350,7 +37292,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .usize, .kind = .{ .param_gpr = .{ .cc = .ccc, .at = 1 } } }, .{ .type = .usize, .kind = .{ .param_gpr = .{ .cc = .ccc, .at = 2 } } }, .{ .type = .usize, .kind = .{ .param_gpr = .{ .cc = .ccc, .at = 3 } } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__umodei4" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__umodei4" } }, .unused, .unused, .unused, @@ -37381,7 +37323,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fmodh" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fmodh" } }, .unused, .unused, .unused, @@ -37413,7 +37355,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fmodh" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fmodh" } }, .unused, .unused, .unused, @@ -37449,7 +37391,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fmodh" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fmodh" } }, .unused, .unused, .unused, @@ -37486,7 +37428,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fmodh" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fmodh" } }, .{ .type = .f16, .kind = .{ .reg = .ax } }, .unused, .unused, @@ -37526,7 +37468,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .f32, .kind = .mem }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fmodh" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fmodh" } }, .unused, .unused, .unused, @@ -37562,7 +37504,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "fmodf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "fmodf" } }, .unused, .unused, .unused, @@ -37594,7 +37536,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f32, .kind = .{ .reg = .xmm0 } }, .{ .type = .f32, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "fmodf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "fmodf" } }, .unused, .unused, .unused, @@ -37629,7 +37571,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f32, .kind = .{ .reg = .xmm0 } }, .{ .type = .f32, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "fmodf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "fmodf" } }, .unused, .unused, .unused, @@ -37661,7 +37603,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "fmod" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "fmod" } }, .unused, .unused, .unused, @@ -37693,7 +37635,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, .{ .type = .f64, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "fmod" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "fmod" } }, .unused, .unused, .unused, @@ -37728,7 +37670,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, .{ .type = .f64, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "fmod" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "fmod" } }, .unused, .unused, .unused, @@ -37763,7 +37705,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, .{ .type = .f64, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "fmod" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "fmod" } }, .unused, .unused, .unused, @@ -37799,7 +37741,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .f80, .kind = .{ .reg = .xmm0 } }, .{ .type = .f80, .kind = .{ .frame = .call_frame } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fmodx" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fmodx" } }, .unused, .unused, .unused, @@ -37832,7 +37774,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .f80, .kind = .{ .reg = .xmm0 } }, .{ .type = .f80, .kind = .{ .frame = .call_frame } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fmodx" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fmodx" } }, .unused, .unused, .unused, @@ -37865,7 +37807,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .f80, .kind = .{ .reg = .xmm0 } }, .{ .type = .f80, .kind = .{ .frame = .call_frame } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fmodx" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fmodx" } }, .unused, .unused, .unused, @@ -37899,7 +37841,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f80, .kind = .{ .reg = .xmm0 } }, .{ .type = .f80, .kind = .{ .frame = .call_frame } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fmodx" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fmodx" } }, .unused, .unused, .unused, @@ -37937,7 +37879,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f80, .kind = .{ .reg = .xmm0 } }, .{ .type = .f80, .kind = .{ .frame = .call_frame } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fmodx" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fmodx" } }, .unused, .unused, .unused, @@ -37975,7 +37917,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f80, .kind = .{ .reg = .xmm0 } }, .{ .type = .f80, .kind = .{ .frame = .call_frame } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fmodx" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fmodx" } }, .unused, .unused, .unused, @@ -38010,7 +37952,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "fmodq" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "fmodq" } }, .unused, .unused, .unused, @@ -38042,7 +37984,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "fmodq" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "fmodq" } }, .unused, .unused, .unused, @@ -38077,7 +38019,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "fmodq" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "fmodq" } }, .unused, .unused, .unused, @@ -38112,7 +38054,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "fmodq" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "fmodq" } }, .unused, .unused, .unused, @@ -38447,7 +38389,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ .{ .type = .i64, .kind = .{ .param_gpr = .{ .cc = .ccc, .at = 3 } } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__modti3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__modti3" } }, .{ .type = .i64, .kind = .{ .rc = .general_purpose } }, .unused, .unused, @@ -38486,7 +38428,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ .{ .type = .i64, .kind = .{ .param_gpr = .{ .cc = .ccc, .at = 3 } } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__modti3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__modti3" } }, .unused, .unused, .unused, @@ -38526,7 +38468,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__umodti3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__umodti3" } }, .unused, .unused, .unused, @@ -38560,7 +38502,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .usize, .kind = .{ .param_gpr = .{ .cc = .ccc, .at = 2 } } }, .{ .type = .usize, .kind = .{ .param_gpr = .{ .cc = .ccc, .at = 3 } } }, .{ .type = .i64, .kind = .{ .rc = .general_purpose } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__modei4" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__modei4" } }, .unused, .unused, .unused, @@ -38617,7 +38559,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .usize, .kind = .{ .param_gpr = .{ .cc = .ccc, .at = 1 } } }, .{ .type = .usize, .kind = .{ .param_gpr = .{ .cc = .ccc, .at = 2 } } }, .{ .type = .usize, .kind = .{ .param_gpr = .{ .cc = .ccc, .at = 3 } } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__umodei4" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__umodei4" } }, .unused, .unused, .unused, @@ -38647,7 +38589,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ .{ .type = .f16, .kind = .{ .rc = .general_purpose } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fmodh" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fmodh" } }, .{ .type = .f16, .kind = .{ .reg = .dx } }, .{ .type = .f16, .kind = .{ .reg = .ax } }, .unused, @@ -38688,7 +38630,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ .{ .type = .f16, .kind = .{ .rc = .general_purpose } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fmodh" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fmodh" } }, .{ .type = .f16, .kind = .{ .reg = .dx } }, .{ .type = .f16, .kind = .{ .reg = .ax } }, .unused, @@ -38729,10 +38671,10 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ .{ .type = .f16, .kind = .{ .rc = .general_purpose } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fmodh" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fmodh" } }, .{ .type = .f16, .kind = .{ .reg = .dx } }, .{ .type = .f16, .kind = .{ .reg = .ax } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__addhf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__addhf3" } }, .unused, .unused, .unused, @@ -38767,10 +38709,10 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ .{ .type = .f16, .kind = .{ .rc = .general_purpose } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fmodh" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fmodh" } }, .{ .type = .f16, .kind = .{ .reg = .dx } }, .{ .type = .f16, .kind = .{ .reg = .ax } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__addhf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__addhf3" } }, .unused, .unused, .unused, @@ -38805,10 +38747,10 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ .{ .type = .f16, .kind = .{ .rc = .general_purpose } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fmodh" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fmodh" } }, .{ .type = .f16, .kind = .{ .reg = .dx } }, .{ .type = .f16, .kind = .{ .reg = .ax } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__addhf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__addhf3" } }, .unused, .unused, .unused, @@ -38843,10 +38785,10 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ .{ .type = .f16, .kind = .{ .rc = .general_purpose } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fmodh" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fmodh" } }, .{ .type = .f16, .kind = .{ .reg = .dx } }, .{ .type = .f16, .kind = .{ .reg = .ax } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__addhf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__addhf3" } }, .unused, .unused, .unused, @@ -38881,10 +38823,10 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ .{ .type = .f32, .kind = .mem }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fmodh" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fmodh" } }, .{ .type = .f32, .kind = .mem }, .{ .type = .f16, .kind = .{ .reg = .ax } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__addhf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__addhf3" } }, .unused, .unused, .unused, @@ -38919,10 +38861,10 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ .{ .type = .f32, .kind = .mem }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fmodh" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fmodh" } }, .{ .type = .f32, .kind = .mem }, .{ .type = .f16, .kind = .{ .reg = .ax } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__addhf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__addhf3" } }, .unused, .unused, .unused, @@ -38960,7 +38902,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fmodh" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fmodh" } }, .{ .type = .f16, .kind = .{ .reg = .dx } }, .{ .type = .f16, .kind = .{ .reg = .ax } }, .unused, @@ -39008,7 +38950,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fmodh" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fmodh" } }, .{ .type = .f16, .kind = .{ .reg = .dx } }, .{ .type = .f16, .kind = .{ .reg = .ax } }, .unused, @@ -39056,10 +38998,10 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fmodh" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fmodh" } }, .{ .type = .f16, .kind = .{ .reg = .dx } }, .{ .type = .f16, .kind = .{ .reg = .ax } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__addhf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__addhf3" } }, .unused, .unused, .unused, @@ -39101,10 +39043,10 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fmodh" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fmodh" } }, .{ .type = .f16, .kind = .{ .reg = .dx } }, .{ .type = .f16, .kind = .{ .reg = .ax } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__addhf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__addhf3" } }, .unused, .unused, .unused, @@ -39146,10 +39088,10 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fmodh" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fmodh" } }, .{ .type = .f16, .kind = .{ .reg = .dx } }, .{ .type = .f16, .kind = .{ .reg = .ax } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__addhf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__addhf3" } }, .unused, .unused, .unused, @@ -39191,10 +39133,10 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fmodh" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fmodh" } }, .{ .type = .f16, .kind = .{ .reg = .dx } }, .{ .type = .f16, .kind = .{ .reg = .ax } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__addhf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__addhf3" } }, .unused, .unused, .unused, @@ -39236,10 +39178,10 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fmodh" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fmodh" } }, .{ .type = .f16, .kind = .{ .reg = .dx } }, .{ .type = .f16, .kind = .{ .reg = .ax } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__addhf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__addhf3" } }, .unused, .unused, .unused, @@ -39282,10 +39224,10 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fmodh" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fmodh" } }, .{ .type = .f16, .kind = .{ .reg = .dx } }, .{ .type = .f16, .kind = .{ .reg = .ax } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__addhf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__addhf3" } }, .unused, .unused, .unused, @@ -39329,9 +39271,9 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .f32, .kind = .mem }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fmodh" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fmodh" } }, .{ .type = .f32, .kind = .mem }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__addhf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__addhf3" } }, .unused, .unused, .unused, @@ -39378,9 +39320,9 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .f32, .kind = .mem }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fmodh" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fmodh" } }, .{ .type = .f32, .kind = .mem }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__addhf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__addhf3" } }, .unused, .unused, .unused, @@ -39423,7 +39365,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ .{ .type = .f32, .kind = .{ .rc = .general_purpose } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "fmodf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "fmodf" } }, .{ .type = .f32, .kind = .{ .reg = .edx } }, .{ .type = .f32, .kind = .{ .reg = .eax } }, .unused, @@ -39461,7 +39403,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ .{ .type = .f32, .kind = .{ .rc = .general_purpose } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "fmodf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "fmodf" } }, .{ .type = .f32, .kind = .{ .reg = .edx } }, .{ .type = .f32, .kind = .{ .reg = .eax } }, .unused, @@ -39499,7 +39441,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ .{ .type = .f32, .kind = .mem }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "fmodf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "fmodf" } }, .{ .type = .f32, .kind = .mem }, .{ .type = .f32, .kind = .{ .reg = .eax } }, .unused, @@ -39539,7 +39481,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .f32, .kind = .{ .reg = .xmm0 } }, .{ .type = .f32, .kind = .{ .reg = .xmm1 } }, .{ .type = .f32, .kind = .{ .rc = .general_purpose } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "fmodf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "fmodf" } }, .{ .type = .f32, .kind = .{ .reg = .edx } }, .{ .type = .f32, .kind = .{ .reg = .eax } }, .unused, @@ -39583,7 +39525,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .f32, .kind = .{ .reg = .xmm0 } }, .{ .type = .f32, .kind = .{ .reg = .xmm1 } }, .{ .type = .f32, .kind = .{ .rc = .general_purpose } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "fmodf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "fmodf" } }, .{ .type = .f32, .kind = .{ .reg = .edx } }, .{ .type = .f32, .kind = .{ .reg = .eax } }, .unused, @@ -39626,7 +39568,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f32, .kind = .{ .reg = .xmm0 } }, .{ .type = .f32, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "fmodf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "fmodf" } }, .{ .type = .f32, .kind = .mem }, .{ .type = .f32, .kind = .{ .reg = .eax } }, .unused, @@ -39666,7 +39608,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ .{ .type = .f64, .kind = .{ .rc = .general_purpose } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "fmod" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "fmod" } }, .{ .type = .f64, .kind = .{ .reg = .rcx } }, .{ .type = .f64, .kind = .{ .reg = .rdx } }, .{ .type = .f64, .kind = .{ .reg = .rax } }, @@ -39705,7 +39647,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ .{ .type = .f64, .kind = .{ .rc = .general_purpose } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "fmod" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "fmod" } }, .{ .type = .f64, .kind = .{ .reg = .rcx } }, .{ .type = .f64, .kind = .{ .reg = .rdx } }, .{ .type = .f64, .kind = .{ .reg = .rax } }, @@ -39744,7 +39686,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ .{ .type = .f64, .kind = .mem }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "fmod" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "fmod" } }, .{ .type = .f64, .kind = .mem }, .{ .type = .f64, .kind = .{ .reg = .rdx } }, .{ .type = .f64, .kind = .{ .reg = .rax } }, @@ -39789,7 +39731,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, .{ .type = .f64, .kind = .{ .reg = .xmm1 } }, .{ .type = .f64, .kind = .{ .rc = .general_purpose } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "fmod" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "fmod" } }, .{ .type = .f64, .kind = .{ .reg = .rcx } }, .{ .type = .f64, .kind = .{ .reg = .rdx } }, .{ .type = .f64, .kind = .{ .reg = .rax } }, @@ -39834,7 +39776,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, .{ .type = .f64, .kind = .{ .reg = .xmm1 } }, .{ .type = .f64, .kind = .{ .rc = .general_purpose } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "fmod" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "fmod" } }, .{ .type = .f64, .kind = .{ .reg = .rcx } }, .{ .type = .f64, .kind = .{ .reg = .rdx } }, .{ .type = .f64, .kind = .{ .reg = .rax } }, @@ -39878,7 +39820,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, .{ .type = .f64, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "fmod" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "fmod" } }, .{ .type = .f64, .kind = .{ .reg = .rdx } }, .{ .type = .f64, .kind = .mem }, .{ .type = .f64, .kind = .{ .reg = .rax } }, @@ -39926,7 +39868,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .f80, .kind = .{ .reg = .xmm0 } }, .{ .type = .f80, .kind = .{ .frame = .call_frame } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fmodx" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fmodx" } }, .{ .type = .f80, .kind = .{ .reg = .st7 } }, .{ .type = .f80, .kind = .{ .reg = .rax } }, .unused, @@ -39969,7 +39911,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .f80, .kind = .{ .reg = .xmm0 } }, .{ .type = .f80, .kind = .{ .frame = .call_frame } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fmodx" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fmodx" } }, .{ .type = .f80, .kind = .{ .reg = .st7 } }, .{ .type = .f80, .kind = .{ .reg = .rax } }, .unused, @@ -40012,7 +39954,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .f80, .kind = .{ .reg = .xmm0 } }, .{ .type = .f80, .kind = .{ .frame = .call_frame } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fmodx" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fmodx" } }, .{ .type = .f80, .kind = .{ .reg = .st7 } }, .{ .type = .f80, .kind = .{ .reg = .rax } }, .unused, @@ -40055,7 +39997,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .f80, .kind = .{ .reg = .xmm0 } }, .{ .type = .f80, .kind = .{ .frame = .call_frame } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fmodx" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fmodx" } }, .{ .type = .f80, .kind = .{ .reg = .st7 } }, .{ .type = .f80, .kind = .{ .reg = .rax } }, .unused, @@ -40098,7 +40040,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .f80, .kind = .{ .reg = .xmm0 } }, .{ .type = .f80, .kind = .{ .frame = .call_frame } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fmodx" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fmodx" } }, .{ .type = .f80, .kind = .{ .reg = .st7 } }, .{ .type = .f80, .kind = .{ .reg = .rax } }, .unused, @@ -40141,7 +40083,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .f80, .kind = .{ .reg = .xmm0 } }, .{ .type = .f80, .kind = .{ .frame = .call_frame } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fmodx" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fmodx" } }, .{ .type = .f80, .kind = .{ .reg = .st7 } }, .{ .type = .f80, .kind = .{ .reg = .rax } }, .unused, @@ -40185,7 +40127,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f80, .kind = .{ .reg = .xmm0 } }, .{ .type = .f80, .kind = .{ .frame = .call_frame } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fmodx" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fmodx" } }, .{ .type = .f80, .kind = .{ .reg = .st7 } }, .{ .type = .f80, .kind = .{ .reg = .rax } }, .unused, @@ -40233,7 +40175,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f80, .kind = .{ .reg = .xmm0 } }, .{ .type = .f80, .kind = .{ .frame = .call_frame } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fmodx" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fmodx" } }, .{ .type = .f80, .kind = .{ .reg = .st7 } }, .{ .type = .f80, .kind = .{ .reg = .rax } }, .unused, @@ -40281,7 +40223,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f80, .kind = .{ .reg = .xmm0 } }, .{ .type = .f80, .kind = .{ .frame = .call_frame } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fmodx" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fmodx" } }, .{ .type = .f80, .kind = .{ .reg = .st7 } }, .{ .type = .f80, .kind = .{ .reg = .rax } }, .unused, @@ -40329,7 +40271,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f80, .kind = .{ .reg = .xmm0 } }, .{ .type = .f80, .kind = .{ .frame = .call_frame } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fmodx" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fmodx" } }, .{ .type = .f80, .kind = .{ .reg = .st7 } }, .{ .type = .f80, .kind = .{ .reg = .rax } }, .unused, @@ -40377,7 +40319,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f80, .kind = .{ .reg = .xmm0 } }, .{ .type = .f80, .kind = .{ .frame = .call_frame } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fmodx" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fmodx" } }, .{ .type = .f80, .kind = .{ .reg = .st7 } }, .{ .type = .f80, .kind = .{ .reg = .rax } }, .unused, @@ -40425,7 +40367,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f80, .kind = .{ .reg = .xmm0 } }, .{ .type = .f80, .kind = .{ .frame = .call_frame } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fmodx" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fmodx" } }, .{ .type = .f80, .kind = .{ .reg = .st7 } }, .{ .type = .f80, .kind = .{ .reg = .rax } }, .unused, @@ -40471,11 +40413,11 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ .{ .type = .f128, .kind = .mem }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "fmodq" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "fmodq" } }, .{ .type = .f128, .kind = .{ .reg = .rcx } }, .{ .type = .f128, .kind = .{ .reg = .rdx } }, .{ .type = .f128, .kind = .{ .reg = .rax } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__addtf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__addtf3" } }, .unused, .unused, .unused, @@ -40512,11 +40454,11 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ .{ .type = .f128, .kind = .mem }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "fmodq" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "fmodq" } }, .{ .type = .f128, .kind = .{ .reg = .rcx } }, .{ .type = .f128, .kind = .{ .reg = .rdx } }, .{ .type = .f128, .kind = .{ .reg = .rax } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__addtf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__addtf3" } }, .unused, .unused, .unused, @@ -40553,11 +40495,11 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ .{ .type = .f128, .kind = .mem }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "fmodq" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "fmodq" } }, .{ .type = .f128, .kind = .{ .reg = .rcx } }, .{ .type = .f128, .kind = .{ .reg = .rdx } }, .{ .type = .f128, .kind = .{ .reg = .rax } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__addtf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__addtf3" } }, .unused, .unused, .unused, @@ -40595,11 +40537,11 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ .{ .type = .f128, .kind = .mem }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "fmodq" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "fmodq" } }, .{ .type = .f128, .kind = .{ .reg = .rdx } }, .{ .type = .f128, .kind = .mem }, .{ .type = .f128, .kind = .{ .reg = .rax } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__addtf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__addtf3" } }, .unused, .unused, .unused, @@ -40637,11 +40579,11 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "fmodq" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "fmodq" } }, .{ .type = .f128, .kind = .{ .reg = .rcx } }, .{ .type = .f128, .kind = .{ .reg = .rdx } }, .{ .type = .f128, .kind = .{ .reg = .rax } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__addtf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__addtf3" } }, .unused, .unused, .unused, @@ -40683,11 +40625,11 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "fmodq" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "fmodq" } }, .{ .type = .f128, .kind = .{ .reg = .rcx } }, .{ .type = .f128, .kind = .{ .reg = .rdx } }, .{ .type = .f128, .kind = .{ .reg = .rax } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__addtf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__addtf3" } }, .unused, .unused, .unused, @@ -40729,11 +40671,11 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "fmodq" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "fmodq" } }, .{ .type = .f128, .kind = .{ .reg = .rcx } }, .{ .type = .f128, .kind = .{ .reg = .rdx } }, .{ .type = .f128, .kind = .{ .reg = .rax } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__addtf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__addtf3" } }, .unused, .unused, .unused, @@ -40776,11 +40718,11 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "fmodq" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "fmodq" } }, .{ .type = .f128, .kind = .{ .reg = .rdx } }, .{ .type = .f128, .kind = .mem }, .{ .type = .f128, .kind = .{ .reg = .rax } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__addtf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__addtf3" } }, .unused, .unused, .unused, @@ -43870,7 +43812,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fmaxh" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fmaxh" } }, .unused, .unused, .unused, @@ -44008,7 +43950,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fmaxh" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fmaxh" } }, .unused, .unused, .unused, @@ -44044,7 +43986,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fmaxh" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fmaxh" } }, .unused, .unused, .unused, @@ -44081,7 +44023,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fmaxh" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fmaxh" } }, .{ .type = .f16, .kind = .{ .reg = .ax } }, .unused, .unused, @@ -44121,7 +44063,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .f32, .kind = .mem }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fmaxh" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fmaxh" } }, .unused, .unused, .unused, @@ -44536,7 +44478,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "fmax" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "fmax" } }, .unused, .unused, .unused, @@ -44791,7 +44733,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, .{ .type = .f64, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "fmax" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "fmax" } }, .unused, .unused, .unused, @@ -45080,7 +45022,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "fmaxq" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "fmaxq" } }, .unused, .unused, .unused, @@ -45112,7 +45054,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "fmaxq" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "fmaxq" } }, .unused, .unused, .unused, @@ -45147,7 +45089,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "fmaxq" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "fmaxq" } }, .unused, .unused, .unused, @@ -45182,7 +45124,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "fmaxq" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "fmaxq" } }, .unused, .unused, .unused, @@ -48029,7 +47971,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fminh" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fminh" } }, .unused, .unused, .unused, @@ -48167,7 +48109,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fminh" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fminh" } }, .unused, .unused, .unused, @@ -48203,7 +48145,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fminh" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fminh" } }, .unused, .unused, .unused, @@ -48240,7 +48182,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fminh" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fminh" } }, .{ .type = .f16, .kind = .{ .reg = .ax } }, .unused, .unused, @@ -48280,7 +48222,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .f32, .kind = .mem }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fminh" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fminh" } }, .unused, .unused, .unused, @@ -48695,7 +48637,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "fmin" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "fmin" } }, .unused, .unused, .unused, @@ -48950,7 +48892,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, .{ .type = .f64, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "fmin" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "fmin" } }, .unused, .unused, .unused, @@ -49227,7 +49169,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "fminq" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "fminq" } }, .unused, .unused, .unused, @@ -49259,7 +49201,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "fminq" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "fminq" } }, .unused, .unused, .unused, @@ -49294,7 +49236,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "fminq" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "fminq" } }, .unused, .unused, .unused, @@ -49329,7 +49271,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "fminq" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "fminq" } }, .unused, .unused, .unused, @@ -72269,7 +72211,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__sqrth" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__sqrth" } }, .unused, .unused, .unused, @@ -72351,7 +72293,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__sqrth" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__sqrth" } }, .unused, .unused, .unused, @@ -72382,7 +72324,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__sqrth" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__sqrth" } }, .unused, .unused, .unused, @@ -72413,7 +72355,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__sqrth" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__sqrth" } }, .{ .type = .f16, .kind = .{ .reg = .ax } }, .unused, .unused, @@ -72447,7 +72389,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .f16, .kind = .{ .reg = .ax } }, .{ .type = .f32, .kind = .mem }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__sqrth" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__sqrth" } }, .unused, .unused, .unused, @@ -72519,7 +72461,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "sqrtf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "sqrtf" } }, .unused, .unused, .unused, @@ -72635,7 +72577,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f32, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "sqrtf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "sqrtf" } }, .unused, .unused, .unused, @@ -72665,7 +72607,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f32, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "sqrtf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "sqrtf" } }, .unused, .unused, .unused, @@ -72735,7 +72677,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "sqrt" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "sqrt" } }, .unused, .unused, .unused, @@ -72851,7 +72793,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "sqrt" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "sqrt" } }, .unused, .unused, .unused, @@ -72881,7 +72823,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "sqrt" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "sqrt" } }, .unused, .unused, .unused, @@ -72911,7 +72853,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "sqrt" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "sqrt" } }, .unused, .unused, .unused, @@ -72995,7 +72937,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "sqrtq" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "sqrtq" } }, .unused, .unused, .unused, @@ -73022,7 +72964,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "sqrtq" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "sqrtq" } }, .unused, .unused, .unused, @@ -73052,7 +72994,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "sqrtq" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "sqrtq" } }, .unused, .unused, .unused, @@ -73082,7 +73024,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "sqrtq" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "sqrtq" } }, .unused, .unused, .unused, @@ -73126,7 +73068,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__" ++ @tagName(name) ++ "h" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__" ++ @tagName(name) ++ "h" } }, .unused, .unused, .unused, @@ -73153,7 +73095,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__" ++ @tagName(name) ++ "h" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__" ++ @tagName(name) ++ "h" } }, .unused, .unused, .unused, @@ -73184,7 +73126,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__" ++ @tagName(name) ++ "h" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__" ++ @tagName(name) ++ "h" } }, .unused, .unused, .unused, @@ -73215,7 +73157,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__" ++ @tagName(name) ++ "h" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__" ++ @tagName(name) ++ "h" } }, .{ .type = .f16, .kind = .{ .reg = .ax } }, .unused, .unused, @@ -73249,7 +73191,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .f16, .kind = .{ .reg = .ax } }, .{ .type = .f32, .kind = .mem }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__" ++ @tagName(name) ++ "h" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__" ++ @tagName(name) ++ "h" } }, .unused, .unused, .unused, @@ -73279,7 +73221,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = @tagName(name) ++ "f" } } }, + .{ .type = .usize, .kind = .{ .extern_func = @tagName(name) ++ "f" } }, .unused, .unused, .unused, @@ -73306,7 +73248,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f32, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = @tagName(name) ++ "f" } } }, + .{ .type = .usize, .kind = .{ .extern_func = @tagName(name) ++ "f" } }, .unused, .unused, .unused, @@ -73336,7 +73278,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f32, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = @tagName(name) ++ "f" } } }, + .{ .type = .usize, .kind = .{ .extern_func = @tagName(name) ++ "f" } }, .unused, .unused, .unused, @@ -73364,7 +73306,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = @tagName(name) } } }, + .{ .type = .usize, .kind = .{ .extern_func = @tagName(name) } }, .unused, .unused, .unused, @@ -73391,7 +73333,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = @tagName(name) } } }, + .{ .type = .usize, .kind = .{ .extern_func = @tagName(name) } }, .unused, .unused, .unused, @@ -73421,7 +73363,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = @tagName(name) } } }, + .{ .type = .usize, .kind = .{ .extern_func = @tagName(name) } }, .unused, .unused, .unused, @@ -73451,7 +73393,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = @tagName(name) } } }, + .{ .type = .usize, .kind = .{ .extern_func = @tagName(name) } }, .unused, .unused, .unused, @@ -73482,7 +73424,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .f80, .kind = .{ .reg = .xmm0 } }, .{ .type = .f80, .kind = .{ .frame = .call_frame } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__" ++ @tagName(name) ++ "x" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__" ++ @tagName(name) ++ "x" } }, .unused, .unused, .unused, @@ -73509,7 +73451,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .f80, .kind = .{ .reg = .xmm0 } }, .{ .type = .f80, .kind = .{ .frame = .call_frame } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__" ++ @tagName(name) ++ "x" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__" ++ @tagName(name) ++ "x" } }, .unused, .unused, .unused, @@ -73536,7 +73478,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .f80, .kind = .{ .reg = .xmm0 } }, .{ .type = .f80, .kind = .{ .frame = .call_frame } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__" ++ @tagName(name) ++ "x" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__" ++ @tagName(name) ++ "x" } }, .unused, .unused, .unused, @@ -73564,7 +73506,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f80, .kind = .{ .reg = .xmm0 } }, .{ .type = .f80, .kind = .{ .frame = .call_frame } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__" ++ @tagName(name) ++ "x" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__" ++ @tagName(name) ++ "x" } }, .unused, .unused, .unused, @@ -73596,7 +73538,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f80, .kind = .{ .reg = .xmm0 } }, .{ .type = .f80, .kind = .{ .frame = .call_frame } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__" ++ @tagName(name) ++ "x" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__" ++ @tagName(name) ++ "x" } }, .unused, .unused, .unused, @@ -73628,7 +73570,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f80, .kind = .{ .reg = .xmm0 } }, .{ .type = .f80, .kind = .{ .frame = .call_frame } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__" ++ @tagName(name) ++ "x" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__" ++ @tagName(name) ++ "x" } }, .unused, .unused, .unused, @@ -73657,7 +73599,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = @tagName(name) ++ "q" } } }, + .{ .type = .usize, .kind = .{ .extern_func = @tagName(name) ++ "q" } }, .unused, .unused, .unused, @@ -73684,7 +73626,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = @tagName(name) ++ "q" } } }, + .{ .type = .usize, .kind = .{ .extern_func = @tagName(name) ++ "q" } }, .unused, .unused, .unused, @@ -73714,7 +73656,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = @tagName(name) ++ "q" } } }, + .{ .type = .usize, .kind = .{ .extern_func = @tagName(name) ++ "q" } }, .unused, .unused, .unused, @@ -73744,7 +73686,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = @tagName(name) ++ "q" } } }, + .{ .type = .usize, .kind = .{ .extern_func = @tagName(name) ++ "q" } }, .unused, .unused, .unused, @@ -75362,12 +75304,12 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = switch (direction) { + .{ .type = .usize, .kind = .{ .extern_func = switch (direction) { else => unreachable, .down => "__floorh", .up => "__ceilh", .zero => "__trunch", - } } } }, + } } }, .unused, .unused, .unused, @@ -75449,12 +75391,12 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = switch (direction) { + .{ .type = .usize, .kind = .{ .extern_func = switch (direction) { else => unreachable, .down => "__floorh", .up => "__ceilh", .zero => "__trunch", - } } } }, + } } }, .unused, .unused, .unused, @@ -75485,12 +75427,12 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = switch (direction) { + .{ .type = .usize, .kind = .{ .extern_func = switch (direction) { else => unreachable, .down => "__floorh", .up => "__ceilh", .zero => "__trunch", - } } } }, + } } }, .unused, .unused, .unused, @@ -75521,12 +75463,12 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = switch (direction) { + .{ .type = .usize, .kind = .{ .extern_func = switch (direction) { else => unreachable, .down => "__floorh", .up => "__ceilh", .zero => "__trunch", - } } } }, + } } }, .{ .type = .f16, .kind = .{ .reg = .ax } }, .unused, .unused, @@ -75560,12 +75502,12 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .f16, .kind = .{ .reg = .ax } }, .{ .type = .f32, .kind = .mem }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = switch (direction) { + .{ .type = .usize, .kind = .{ .extern_func = switch (direction) { else => unreachable, .down => "__floorh", .up => "__ceilh", .zero => "__trunch", - } } } }, + } } }, .unused, .unused, .unused, @@ -75637,12 +75579,12 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = switch (direction) { + .{ .type = .usize, .kind = .{ .extern_func = switch (direction) { else => unreachable, .down => "floorf", .up => "ceilf", .zero => "truncf", - } } } }, + } } }, .unused, .unused, .unused, @@ -75764,12 +75706,12 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f32, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = switch (direction) { + .{ .type = .usize, .kind = .{ .extern_func = switch (direction) { else => unreachable, .down => "floorf", .up => "ceilf", .zero => "truncf", - } } } }, + } } }, .unused, .unused, .unused, @@ -75799,12 +75741,12 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f32, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = switch (direction) { + .{ .type = .usize, .kind = .{ .extern_func = switch (direction) { else => unreachable, .down => "floorf", .up => "ceilf", .zero => "truncf", - } } } }, + } } }, .unused, .unused, .unused, @@ -75874,12 +75816,12 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = switch (direction) { + .{ .type = .usize, .kind = .{ .extern_func = switch (direction) { else => unreachable, .down => "floor", .up => "ceil", .zero => "trunc", - } } } }, + } } }, .unused, .unused, .unused, @@ -76001,12 +75943,12 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = switch (direction) { + .{ .type = .usize, .kind = .{ .extern_func = switch (direction) { else => unreachable, .down => "floor", .up => "ceil", .zero => "trunc", - } } } }, + } } }, .unused, .unused, .unused, @@ -76036,12 +75978,12 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = switch (direction) { + .{ .type = .usize, .kind = .{ .extern_func = switch (direction) { else => unreachable, .down => "floor", .up => "ceil", .zero => "trunc", - } } } }, + } } }, .unused, .unused, .unused, @@ -76071,12 +76013,12 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = switch (direction) { + .{ .type = .usize, .kind = .{ .extern_func = switch (direction) { else => unreachable, .down => "floor", .up => "ceil", .zero => "trunc", - } } } }, + } } }, .unused, .unused, .unused, @@ -76107,12 +76049,12 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .f80, .kind = .{ .reg = .xmm0 } }, .{ .type = .f80, .kind = .{ .frame = .call_frame } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = switch (direction) { + .{ .type = .usize, .kind = .{ .extern_func = switch (direction) { else => unreachable, .down => "__floorx", .up => "__ceilx", .zero => "__truncx", - } } } }, + } } }, .unused, .unused, .unused, @@ -76139,12 +76081,12 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .f80, .kind = .{ .reg = .xmm0 } }, .{ .type = .f80, .kind = .{ .frame = .call_frame } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = switch (direction) { + .{ .type = .usize, .kind = .{ .extern_func = switch (direction) { else => unreachable, .down => "__floorx", .up => "__ceilx", .zero => "__truncx", - } } } }, + } } }, .unused, .unused, .unused, @@ -76171,12 +76113,12 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .f80, .kind = .{ .reg = .xmm0 } }, .{ .type = .f80, .kind = .{ .frame = .call_frame } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = switch (direction) { + .{ .type = .usize, .kind = .{ .extern_func = switch (direction) { else => unreachable, .down => "__floorx", .up => "__ceilx", .zero => "__truncx", - } } } }, + } } }, .unused, .unused, .unused, @@ -76204,12 +76146,12 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f80, .kind = .{ .reg = .xmm0 } }, .{ .type = .f80, .kind = .{ .frame = .call_frame } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = switch (direction) { + .{ .type = .usize, .kind = .{ .extern_func = switch (direction) { else => unreachable, .down => "__floorx", .up => "__ceilx", .zero => "__truncx", - } } } }, + } } }, .unused, .unused, .unused, @@ -76241,12 +76183,12 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f80, .kind = .{ .reg = .xmm0 } }, .{ .type = .f80, .kind = .{ .frame = .call_frame } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = switch (direction) { + .{ .type = .usize, .kind = .{ .extern_func = switch (direction) { else => unreachable, .down => "__floorx", .up => "__ceilx", .zero => "__truncx", - } } } }, + } } }, .unused, .unused, .unused, @@ -76278,12 +76220,12 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f80, .kind = .{ .reg = .xmm0 } }, .{ .type = .f80, .kind = .{ .frame = .call_frame } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = switch (direction) { + .{ .type = .usize, .kind = .{ .extern_func = switch (direction) { else => unreachable, .down => "__floorx", .up => "__ceilx", .zero => "__truncx", - } } } }, + } } }, .unused, .unused, .unused, @@ -76312,12 +76254,12 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = switch (direction) { + .{ .type = .usize, .kind = .{ .extern_func = switch (direction) { else => unreachable, .down => "floorq", .up => "ceilq", .zero => "truncq", - } } } }, + } } }, .unused, .unused, .unused, @@ -76344,12 +76286,12 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = switch (direction) { + .{ .type = .usize, .kind = .{ .extern_func = switch (direction) { else => unreachable, .down => "floorq", .up => "ceilq", .zero => "truncq", - } } } }, + } } }, .unused, .unused, .unused, @@ -76379,12 +76321,12 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = switch (direction) { + .{ .type = .usize, .kind = .{ .extern_func = switch (direction) { else => unreachable, .down => "floorq", .up => "ceilq", .zero => "truncq", - } } } }, + } } }, .unused, .unused, .unused, @@ -76414,12 +76356,12 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = switch (direction) { + .{ .type = .usize, .kind = .{ .extern_func = switch (direction) { else => unreachable, .down => "floorq", .up => "ceilq", .zero => "truncq", - } } } }, + } } }, .unused, .unused, .unused, @@ -77051,7 +76993,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__cmphf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__cmphf2" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .unused, .unused, @@ -77394,7 +77336,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__cmptf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__cmptf2" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .unused, .unused, @@ -77555,7 +77497,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__cmphf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__cmphf2" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .unused, .unused, @@ -77922,7 +77864,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__cmptf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__cmptf2" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .unused, .unused, @@ -78591,7 +78533,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__cmphf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__cmphf2" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .{ .type = .u8, .kind = .{ .reg = .cl } }, .{ .type = .u32, .kind = .{ .reg = .edx } }, @@ -78635,7 +78577,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__cmphf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__cmphf2" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .{ .type = .u8, .kind = .{ .reg = .cl } }, .{ .type = .u32, .kind = .{ .reg = .edx } }, @@ -78679,7 +78621,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__cmphf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__cmphf2" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .{ .type = .u8, .kind = .{ .reg = .cl } }, .{ .type = .u32, .kind = .{ .reg = .edx } }, @@ -78724,7 +78666,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__cmphf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__cmphf2" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .{ .type = .u8, .kind = .{ .reg = .cl } }, .{ .type = .u32, .kind = .{ .reg = .edx } }, @@ -78769,7 +78711,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__cmphf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__cmphf2" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .{ .type = .u8, .kind = .{ .reg = .cl } }, .{ .type = .u32, .kind = .{ .reg = .edx } }, @@ -78816,7 +78758,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__cmphf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__cmphf2" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .{ .type = .u8, .kind = .{ .reg = .cl } }, .{ .type = .u32, .kind = .{ .reg = .edx } }, @@ -78863,7 +78805,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__cmphf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__cmphf2" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .{ .type = .u8, .kind = .{ .reg = .cl } }, .{ .type = .u64, .kind = .{ .reg = .rdx } }, @@ -78916,7 +78858,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__cmphf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__cmphf2" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .{ .type = .u8, .kind = .{ .reg = .cl } }, .{ .type = .u64, .kind = .{ .reg = .rdx } }, @@ -78969,7 +78911,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__cmphf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__cmphf2" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .{ .type = .u8, .kind = .{ .reg = .cl } }, .{ .type = .u64, .kind = .{ .reg = .rdx } }, @@ -79023,7 +78965,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__cmphf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__cmphf2" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .{ .type = .u8, .kind = .{ .reg = .cl } }, .{ .type = .u64, .kind = .{ .reg = .rdx } }, @@ -79077,7 +79019,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__cmphf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__cmphf2" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .{ .type = .u8, .kind = .{ .reg = .cl } }, .{ .type = .u64, .kind = .{ .reg = .rdx } }, @@ -79133,7 +79075,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__cmphf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__cmphf2" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .{ .type = .u8, .kind = .{ .reg = .cl } }, .{ .type = .u64, .kind = .{ .reg = .rdx } }, @@ -79960,7 +79902,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__cmptf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__cmptf2" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .{ .type = .u8, .kind = .{ .reg = .cl } }, .{ .type = .u32, .kind = .{ .reg = .edx } }, @@ -80004,7 +79946,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__cmptf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__cmptf2" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .{ .type = .u8, .kind = .{ .reg = .cl } }, .{ .type = .u32, .kind = .{ .reg = .edx } }, @@ -80048,7 +79990,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__cmptf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__cmptf2" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .{ .type = .u8, .kind = .{ .reg = .cl } }, .{ .type = .u32, .kind = .{ .reg = .edx } }, @@ -80092,7 +80034,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__cmptf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__cmptf2" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .{ .type = .u8, .kind = .{ .reg = .cl } }, .{ .type = .u32, .kind = .{ .reg = .edx } }, @@ -80136,7 +80078,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__cmptf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__cmptf2" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .{ .type = .u8, .kind = .{ .reg = .cl } }, .{ .type = .u32, .kind = .{ .reg = .edx } }, @@ -80180,7 +80122,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__cmptf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__cmptf2" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .{ .type = .u8, .kind = .{ .reg = .cl } }, .{ .type = .u32, .kind = .{ .reg = .edx } }, @@ -80224,7 +80166,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__cmptf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__cmptf2" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .{ .type = .u8, .kind = .{ .reg = .cl } }, .{ .type = .u64, .kind = .{ .reg = .rdx } }, @@ -80277,7 +80219,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__cmptf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__cmptf2" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .{ .type = .u8, .kind = .{ .reg = .cl } }, .{ .type = .u64, .kind = .{ .reg = .rdx } }, @@ -80330,7 +80272,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__cmptf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__cmptf2" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .{ .type = .u8, .kind = .{ .reg = .cl } }, .{ .type = .u64, .kind = .{ .reg = .rdx } }, @@ -80383,7 +80325,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__cmptf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__cmptf2" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .{ .type = .u8, .kind = .{ .reg = .cl } }, .{ .type = .u64, .kind = .{ .reg = .rdx } }, @@ -80436,7 +80378,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__cmptf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__cmptf2" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .{ .type = .u8, .kind = .{ .reg = .cl } }, .{ .type = .u64, .kind = .{ .reg = .rdx } }, @@ -80489,7 +80431,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__cmptf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__cmptf2" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .{ .type = .u8, .kind = .{ .reg = .cl } }, .{ .type = .u64, .kind = .{ .reg = .rdx } }, @@ -83099,7 +83041,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__cmphf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__cmphf2" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .{ .type = .u8, .kind = .{ .reg = .cl } }, .{ .type = .u32, .kind = .{ .reg = .edx } }, @@ -83143,7 +83085,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__cmphf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__cmphf2" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .{ .type = .u8, .kind = .{ .reg = .cl } }, .{ .type = .u32, .kind = .{ .reg = .edx } }, @@ -83187,7 +83129,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__cmphf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__cmphf2" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .{ .type = .u8, .kind = .{ .reg = .cl } }, .{ .type = .u32, .kind = .{ .reg = .edx } }, @@ -83232,7 +83174,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__cmphf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__cmphf2" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .{ .type = .u8, .kind = .{ .reg = .cl } }, .{ .type = .u32, .kind = .{ .reg = .edx } }, @@ -83277,7 +83219,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__cmphf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__cmphf2" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .{ .type = .u8, .kind = .{ .reg = .cl } }, .{ .type = .u32, .kind = .{ .reg = .edx } }, @@ -83324,7 +83266,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__cmphf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__cmphf2" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .{ .type = .u8, .kind = .{ .reg = .cl } }, .{ .type = .u32, .kind = .{ .reg = .edx } }, @@ -83371,7 +83313,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__cmphf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__cmphf2" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .{ .type = .u8, .kind = .{ .reg = .cl } }, .{ .type = .u64, .kind = .{ .reg = .rdx } }, @@ -83424,7 +83366,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__cmphf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__cmphf2" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .{ .type = .u8, .kind = .{ .reg = .cl } }, .{ .type = .u64, .kind = .{ .reg = .rdx } }, @@ -83477,7 +83419,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__cmphf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__cmphf2" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .{ .type = .u8, .kind = .{ .reg = .cl } }, .{ .type = .u64, .kind = .{ .reg = .rdx } }, @@ -83531,7 +83473,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__cmphf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__cmphf2" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .{ .type = .u8, .kind = .{ .reg = .cl } }, .{ .type = .u64, .kind = .{ .reg = .rdx } }, @@ -83585,7 +83527,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__cmphf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__cmphf2" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .{ .type = .u8, .kind = .{ .reg = .cl } }, .{ .type = .u64, .kind = .{ .reg = .rdx } }, @@ -83641,7 +83583,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__cmphf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__cmphf2" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .{ .type = .u8, .kind = .{ .reg = .cl } }, .{ .type = .u64, .kind = .{ .reg = .rdx } }, @@ -84482,7 +84424,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__cmptf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__cmptf2" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .{ .type = .u8, .kind = .{ .reg = .cl } }, .{ .type = .u32, .kind = .{ .reg = .edx } }, @@ -84526,7 +84468,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__cmptf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__cmptf2" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .{ .type = .u8, .kind = .{ .reg = .cl } }, .{ .type = .u32, .kind = .{ .reg = .edx } }, @@ -84570,7 +84512,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__cmptf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__cmptf2" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .{ .type = .u8, .kind = .{ .reg = .cl } }, .{ .type = .u32, .kind = .{ .reg = .edx } }, @@ -84614,7 +84556,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__cmptf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__cmptf2" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .{ .type = .u8, .kind = .{ .reg = .cl } }, .{ .type = .u32, .kind = .{ .reg = .edx } }, @@ -84658,7 +84600,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__cmptf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__cmptf2" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .{ .type = .u8, .kind = .{ .reg = .cl } }, .{ .type = .u32, .kind = .{ .reg = .edx } }, @@ -84702,7 +84644,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__cmptf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__cmptf2" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .{ .type = .u8, .kind = .{ .reg = .cl } }, .{ .type = .u32, .kind = .{ .reg = .edx } }, @@ -84746,7 +84688,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__cmptf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__cmptf2" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .{ .type = .u8, .kind = .{ .reg = .cl } }, .{ .type = .u64, .kind = .{ .reg = .rdx } }, @@ -84799,7 +84741,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__cmptf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__cmptf2" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .{ .type = .u8, .kind = .{ .reg = .cl } }, .{ .type = .u64, .kind = .{ .reg = .rdx } }, @@ -84852,7 +84794,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__cmptf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__cmptf2" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .{ .type = .u8, .kind = .{ .reg = .cl } }, .{ .type = .u64, .kind = .{ .reg = .rdx } }, @@ -84905,7 +84847,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__cmptf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__cmptf2" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .{ .type = .u8, .kind = .{ .reg = .cl } }, .{ .type = .u64, .kind = .{ .reg = .rdx } }, @@ -84958,7 +84900,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__cmptf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__cmptf2" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .{ .type = .u8, .kind = .{ .reg = .cl } }, .{ .type = .u64, .kind = .{ .reg = .rdx } }, @@ -85011,7 +84953,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__cmptf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__cmptf2" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .{ .type = .u8, .kind = .{ .reg = .cl } }, .{ .type = .u64, .kind = .{ .reg = .rdx } }, @@ -85109,6 +85051,8 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .dbg_arg_inline, => |air_tag| if (use_old) try cg.airDbgVar(inst) else if (!cg.mod.strip) { const pl_op = air_datas[@intFromEnum(inst)].pl_op; + const air_name: Air.NullTerminatedString = @enumFromInt(pl_op.payload); + const ty = cg.typeOf(pl_op.operand); var ops = try cg.tempsFromOperands(inst, .{pl_op.operand}); var mcv = ops[0].tracking(cg).short; switch (mcv) { @@ -85124,13 +85068,16 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, } - const name_nts: Air.NullTerminatedString = @enumFromInt(pl_op.payload); - assert(name_nts != .none); - const name = name_nts.toSlice(cg.air); - try cg.mir_local_name_bytes.appendSlice(cg.gpa, name[0 .. name.len + 1]); - - const ty = cg.typeOf(pl_op.operand); - try cg.mir_local_types.append(cg.gpa, ty.toIntern()); + try cg.mir_locals.append(cg.gpa, .{ + .name = switch (air_name) { + .none => switch (air_tag) { + else => unreachable, + .dbg_arg_inline => .none, + }, + else => try cg.addString(air_name.toSlice(cg.air)), + }, + .type = ty.toIntern(), + }); try cg.genLocalDebugInfo(air_tag, ty, ops[0].tracking(cg).short); try ops[0].die(cg); @@ -85398,7 +85345,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__truncsfhf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__truncsfhf2" } }, .unused, .unused, .unused, @@ -85455,7 +85402,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f32, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__truncsfhf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__truncsfhf2" } }, .unused, .unused, .unused, @@ -85486,7 +85433,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f32, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__truncsfhf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__truncsfhf2" } }, .unused, .unused, .unused, @@ -85517,7 +85464,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f32, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__truncsfhf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__truncsfhf2" } }, .{ .type = .f16, .kind = .{ .reg = .ax } }, .unused, .unused, @@ -85549,7 +85496,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__truncsfhf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__truncsfhf2" } }, .{ .type = .f32, .kind = .mem }, .{ .type = .f16, .kind = .{ .reg = .ax } }, .unused, @@ -85580,7 +85527,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__truncdfhf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__truncdfhf2" } }, .unused, .unused, .unused, @@ -85608,7 +85555,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__truncdfhf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__truncdfhf2" } }, .unused, .unused, .unused, @@ -85639,7 +85586,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__truncdfhf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__truncdfhf2" } }, .unused, .unused, .unused, @@ -85670,7 +85617,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__truncdfhf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__truncdfhf2" } }, .{ .type = .f16, .kind = .{ .reg = .ax } }, .unused, .unused, @@ -85702,7 +85649,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__truncdfhf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__truncdfhf2" } }, .{ .type = .f32, .kind = .mem }, .{ .type = .f16, .kind = .{ .reg = .ax } }, .unused, @@ -85919,7 +85866,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .call_frame = .{ .size = 16, .alignment = .@"16" }, .extra_temps = .{ .{ .type = .f80, .kind = .{ .frame = .call_frame } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__truncxfhf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__truncxfhf2" } }, .unused, .unused, .unused, @@ -85947,7 +85894,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .call_frame = .{ .size = 16, .alignment = .@"16" }, .extra_temps = .{ .{ .type = .f80, .kind = .{ .frame = .call_frame } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__truncxfhf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__truncxfhf2" } }, .unused, .unused, .unused, @@ -85975,7 +85922,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .call_frame = .{ .size = 16, .alignment = .@"16" }, .extra_temps = .{ .{ .type = .f80, .kind = .{ .frame = .call_frame } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__truncxfhf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__truncxfhf2" } }, .unused, .unused, .unused, @@ -86005,7 +85952,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f80, .kind = .{ .reg = .xmm0 } }, .{ .type = .f80, .kind = .{ .frame = .call_frame } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__truncxfhf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__truncxfhf2" } }, .unused, .unused, .unused, @@ -86037,7 +85984,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f80, .kind = .{ .reg = .xmm0 } }, .{ .type = .f80, .kind = .{ .frame = .call_frame } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__truncxfhf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__truncxfhf2" } }, .unused, .unused, .unused, @@ -86069,7 +86016,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f80, .kind = .{ .reg = .xmm0 } }, .{ .type = .f80, .kind = .{ .frame = .call_frame } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__truncxfhf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__truncxfhf2" } }, .{ .type = .f16, .kind = .{ .reg = .ax } }, .unused, .unused, @@ -86102,7 +86049,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f80, .kind = .{ .reg = .xmm0 } }, .{ .type = .f80, .kind = .{ .frame = .call_frame } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__truncxfhf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__truncxfhf2" } }, .{ .type = .f16, .kind = .{ .reg = .ax } }, .unused, .unused, @@ -86243,7 +86190,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__trunctfhf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__trunctfhf2" } }, .unused, .unused, .unused, @@ -86271,7 +86218,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__trunctfhf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__trunctfhf2" } }, .unused, .unused, .unused, @@ -86302,7 +86249,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__trunctfhf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__trunctfhf2" } }, .unused, .unused, .unused, @@ -86333,7 +86280,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__trunctfhf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__trunctfhf2" } }, .{ .type = .f16, .kind = .{ .reg = .ax } }, .unused, .unused, @@ -86365,7 +86312,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__trunctfhf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__trunctfhf2" } }, .{ .type = .f32, .kind = .mem }, .{ .type = .f16, .kind = .{ .reg = .ax } }, .unused, @@ -86396,7 +86343,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__trunctfsf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__trunctfsf2" } }, .unused, .unused, .unused, @@ -86424,7 +86371,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__trunctfsf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__trunctfsf2" } }, .unused, .unused, .unused, @@ -86455,7 +86402,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__trunctfsf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__trunctfsf2" } }, .unused, .unused, .unused, @@ -86486,7 +86433,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__trunctfsf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__trunctfsf2" } }, .unused, .unused, .unused, @@ -86515,7 +86462,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__trunctfdf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__trunctfdf2" } }, .unused, .unused, .unused, @@ -86543,7 +86490,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__trunctfdf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__trunctfdf2" } }, .unused, .unused, .unused, @@ -86574,7 +86521,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__trunctfdf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__trunctfdf2" } }, .unused, .unused, .unused, @@ -86605,7 +86552,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__trunctfdf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__trunctfdf2" } }, .unused, .unused, .unused, @@ -86634,7 +86581,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__trunctfxf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__trunctfxf2" } }, .unused, .unused, .unused, @@ -86662,7 +86609,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__trunctfxf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__trunctfxf2" } }, .unused, .unused, .unused, @@ -86694,7 +86641,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__trunctfxf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__trunctfxf2" } }, .unused, .unused, .unused, @@ -86726,7 +86673,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__trunctfxf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__trunctfxf2" } }, .unused, .unused, .unused, @@ -86806,7 +86753,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__extendhfsf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__extendhfsf2" } }, .unused, .unused, .unused, @@ -86863,7 +86810,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__extendhfsf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__extendhfsf2" } }, .unused, .unused, .unused, @@ -86895,7 +86842,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__extendhfsf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__extendhfsf2" } }, .unused, .unused, .unused, @@ -86929,7 +86876,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .f16, .kind = .{ .reg = .ax } }, .{ .type = .f32, .kind = .mem }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__extendhfsf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__extendhfsf2" } }, .unused, .unused, .unused, @@ -87026,7 +86973,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__extendhfdf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__extendhfdf2" } }, .unused, .unused, .unused, @@ -87087,7 +87034,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__extendhfdf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__extendhfdf2" } }, .unused, .unused, .unused, @@ -87119,7 +87066,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__extendhfdf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__extendhfdf2" } }, .unused, .unused, .unused, @@ -87153,7 +87100,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .f16, .kind = .{ .reg = .ax } }, .{ .type = .f32, .kind = .mem }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__extendhfdf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__extendhfdf2" } }, .unused, .unused, .unused, @@ -87209,7 +87156,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__extendhfxf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__extendhfxf2" } }, .unused, .unused, .unused, @@ -87237,7 +87184,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__extendhfxf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__extendhfxf2" } }, .unused, .unused, .unused, @@ -87270,7 +87217,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__extendhfxf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__extendhfxf2" } }, .unused, .unused, .unused, @@ -87305,7 +87252,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .f16, .kind = .{ .reg = .ax } }, .{ .type = .f32, .kind = .mem }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__extendhfxf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__extendhfxf2" } }, .unused, .unused, .unused, @@ -87335,7 +87282,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__extendhftf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__extendhftf2" } }, .unused, .unused, .unused, @@ -87363,7 +87310,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__extendhftf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__extendhftf2" } }, .unused, .unused, .unused, @@ -87395,7 +87342,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__extendhftf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__extendhftf2" } }, .unused, .unused, .unused, @@ -87429,7 +87376,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .f16, .kind = .{ .reg = .ax } }, .{ .type = .f32, .kind = .mem }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__extendhftf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__extendhftf2" } }, .unused, .unused, .unused, @@ -87696,7 +87643,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__extendsftf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__extendsftf2" } }, .unused, .unused, .unused, @@ -87724,7 +87671,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f32, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__extendsftf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__extendsftf2" } }, .unused, .unused, .unused, @@ -87755,7 +87702,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f32, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__extendsftf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__extendsftf2" } }, .unused, .unused, .unused, @@ -87786,7 +87733,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f32, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__extendsftf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__extendsftf2" } }, .unused, .unused, .unused, @@ -87869,7 +87816,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__extenddftf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__extenddftf2" } }, .unused, .unused, .unused, @@ -87897,7 +87844,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__extenddftf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__extenddftf2" } }, .unused, .unused, .unused, @@ -87928,7 +87875,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__extenddftf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__extenddftf2" } }, .unused, .unused, .unused, @@ -87959,7 +87906,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__extenddftf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__extenddftf2" } }, .unused, .unused, .unused, @@ -87990,7 +87937,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .call_frame = .{ .size = 16, .alignment = .@"16" }, .extra_temps = .{ .{ .type = .f80, .kind = .{ .frame = .call_frame } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__extendxftf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__extendxftf2" } }, .unused, .unused, .unused, @@ -88018,7 +87965,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .call_frame = .{ .size = 16, .alignment = .@"16" }, .extra_temps = .{ .{ .type = .f80, .kind = .{ .frame = .call_frame } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__extendxftf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__extendxftf2" } }, .unused, .unused, .unused, @@ -88046,7 +87993,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .call_frame = .{ .size = 16, .alignment = .@"16" }, .extra_temps = .{ .{ .type = .f80, .kind = .{ .frame = .call_frame } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__extendxftf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__extendxftf2" } }, .unused, .unused, .unused, @@ -88076,7 +88023,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f80, .kind = .{ .reg = .xmm0 } }, .{ .type = .f80, .kind = .{ .frame = .call_frame } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__extendxftf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__extendxftf2" } }, .unused, .unused, .unused, @@ -88108,7 +88055,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f80, .kind = .{ .reg = .xmm0 } }, .{ .type = .f80, .kind = .{ .frame = .call_frame } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__extendxftf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__extendxftf2" } }, .unused, .unused, .unused, @@ -88140,7 +88087,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f80, .kind = .{ .reg = .xmm0 } }, .{ .type = .f80, .kind = .{ .frame = .call_frame } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__extendxftf2" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__extendxftf2" } }, .unused, .unused, .unused, @@ -98891,9 +98838,10 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { } }) catch |err| switch (err) { error.SelectFailed => { const elem_size = res_ty.abiSize(zcu); - const base = try cg.tempAllocReg(.usize, abi.RegisterClass.gp); + var base = try cg.tempAllocReg(.usize, abi.RegisterClass.gp); while (try ops[0].toBase(false, cg) or - try ops[1].toRegClass(true, .general_purpose, cg)) + try ops[1].toRegClass(true, .general_purpose, cg) or + try base.toRegClass(true, .general_purpose, cg)) {} const base_reg = base.tracking(cg).short.register.to64(); const rhs_reg = ops[1].tracking(cg).short.register.to64(); @@ -99334,7 +99282,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixhfsi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixhfsi" } }, .unused, .unused, .unused, @@ -99360,7 +99308,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixunshfsi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixunshfsi" } }, .unused, .unused, .unused, @@ -99386,7 +99334,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixhfdi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixhfdi" } }, .unused, .unused, .unused, @@ -99412,7 +99360,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixunshfdi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixunshfdi" } }, .unused, .unused, .unused, @@ -99438,7 +99386,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixhfti" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixhfti" } }, .unused, .unused, .unused, @@ -99464,7 +99412,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixunshfti" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixunshfti" } }, .unused, .unused, .unused, @@ -99490,7 +99438,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixhfdi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixhfdi" } }, .{ .type = .i64, .kind = .{ .reg = .rax } }, .{ .type = .usize, .kind = .{ .reg = .rdi } }, .{ .type = .u32, .kind = .{ .reg = .ecx } }, @@ -99521,7 +99469,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixunshfdi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixunshfdi" } }, .{ .type = .i64, .kind = .{ .reg = .rax } }, .{ .type = .usize, .kind = .{ .reg = .rdi } }, .{ .type = .u32, .kind = .{ .reg = .ecx } }, @@ -99615,7 +99563,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixhfsi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixhfsi" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .unused, .unused, @@ -99647,7 +99595,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixhfsi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixhfsi" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .unused, .unused, @@ -99679,7 +99627,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixhfsi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixhfsi" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .unused, .unused, @@ -99711,7 +99659,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixhfsi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixhfsi" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .unused, .unused, @@ -99745,7 +99693,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .i32, .kind = .{ .reg = .eax } }, .{ .type = .f32, .kind = .mem }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixhfsi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixhfsi" } }, .unused, .unused, .unused, @@ -99778,7 +99726,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .i32, .kind = .{ .reg = .eax } }, .{ .type = .f32, .kind = .mem }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixhfsi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixhfsi" } }, .unused, .unused, .unused, @@ -99899,7 +99847,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixhfsi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixhfsi" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .unused, .unused, @@ -99931,7 +99879,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixhfsi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixhfsi" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .unused, .unused, @@ -99965,7 +99913,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .i32, .kind = .{ .reg = .eax } }, .{ .type = .f32, .kind = .mem }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixhfsi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixhfsi" } }, .unused, .unused, .unused, @@ -100058,7 +100006,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixhfsi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixhfsi" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .unused, .unused, @@ -100090,7 +100038,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixunshfsi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixunshfsi" } }, .{ .type = .u32, .kind = .{ .reg = .eax } }, .unused, .unused, @@ -100122,7 +100070,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixhfsi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixhfsi" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .unused, .unused, @@ -100154,7 +100102,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixunshfsi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixunshfsi" } }, .{ .type = .u32, .kind = .{ .reg = .eax } }, .unused, .unused, @@ -100188,7 +100136,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .i32, .kind = .{ .reg = .eax } }, .{ .type = .f32, .kind = .mem }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixhfsi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixhfsi" } }, .unused, .unused, .unused, @@ -100221,7 +100169,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .u32, .kind = .{ .reg = .eax } }, .{ .type = .f32, .kind = .mem }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixunshfsi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixunshfsi" } }, .unused, .unused, .unused, @@ -100284,7 +100232,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixhfdi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixhfdi" } }, .{ .type = .i64, .kind = .{ .reg = .rax } }, .unused, .unused, @@ -100316,7 +100264,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixunshfdi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixunshfdi" } }, .{ .type = .u64, .kind = .{ .reg = .rax } }, .unused, .unused, @@ -100348,7 +100296,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixhfdi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixhfdi" } }, .{ .type = .i64, .kind = .{ .reg = .rax } }, .unused, .unused, @@ -100380,7 +100328,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixunshfdi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixunshfdi" } }, .{ .type = .u64, .kind = .{ .reg = .rax } }, .unused, .unused, @@ -100414,7 +100362,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .i64, .kind = .{ .reg = .rax } }, .{ .type = .f32, .kind = .mem }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixhfdi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixhfdi" } }, .unused, .unused, .unused, @@ -100447,7 +100395,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .u64, .kind = .{ .reg = .rax } }, .{ .type = .f32, .kind = .mem }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixunshfdi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixunshfdi" } }, .unused, .unused, .unused, @@ -100478,7 +100426,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixhfti" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixhfti" } }, .{ .type = .u64, .kind = .{ .reg = .rax } }, .{ .type = .i64, .kind = .{ .reg = .rdx } }, .unused, @@ -100511,7 +100459,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixunshfti" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixunshfti" } }, .{ .type = .u64, .kind = .{ .reg = .rax } }, .{ .type = .u64, .kind = .{ .reg = .rdx } }, .unused, @@ -100544,7 +100492,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixhfti" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixhfti" } }, .{ .type = .u64, .kind = .{ .reg = .rax } }, .{ .type = .i64, .kind = .{ .reg = .rdx } }, .unused, @@ -100577,7 +100525,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixunshfti" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixunshfti" } }, .{ .type = .u64, .kind = .{ .reg = .rax } }, .{ .type = .u64, .kind = .{ .reg = .rdx } }, .unused, @@ -100612,7 +100560,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .u64, .kind = .{ .reg = .rax } }, .{ .type = .f32, .kind = .mem }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixhfti" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixhfti" } }, .{ .type = .i64, .kind = .{ .reg = .rdx } }, .unused, .unused, @@ -100646,7 +100594,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .u64, .kind = .{ .reg = .rax } }, .{ .type = .f32, .kind = .mem }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixunshfti" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixunshfti" } }, .{ .type = .u64, .kind = .{ .reg = .rdx } }, .unused, .unused, @@ -100681,7 +100629,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .usize, .kind = .{ .reg = .rdi } }, .{ .type = .usize, .kind = .{ .reg = .rsi } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixhfei" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixhfei" } }, .unused, .unused, .unused, @@ -100716,7 +100664,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .usize, .kind = .{ .reg = .rdi } }, .{ .type = .usize, .kind = .{ .reg = .rsi } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixunshfei" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixunshfei" } }, .unused, .unused, .unused, @@ -100751,7 +100699,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .usize, .kind = .{ .reg = .rdi } }, .{ .type = .usize, .kind = .{ .reg = .rsi } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixhfei" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixhfei" } }, .unused, .unused, .unused, @@ -100786,7 +100734,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .usize, .kind = .{ .reg = .rdi } }, .{ .type = .usize, .kind = .{ .reg = .rsi } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixunshfei" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixunshfei" } }, .unused, .unused, .unused, @@ -100822,7 +100770,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .usize, .kind = .{ .reg = .rsi } }, .{ .type = .f32, .kind = .mem }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixhfei" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixhfei" } }, .unused, .unused, .unused, @@ -100858,7 +100806,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .usize, .kind = .{ .reg = .rsi } }, .{ .type = .f32, .kind = .mem }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixunshfei" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixunshfei" } }, .unused, .unused, .unused, @@ -101000,7 +100948,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixsfti" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixsfti" } }, .unused, .unused, .unused, @@ -101026,7 +100974,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixunssfti" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixunssfti" } }, .unused, .unused, .unused, @@ -101055,7 +101003,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .i64, .kind = .{ .rc = .general_purpose } }, .{ .type = .i64, .kind = .{ .reg = .rax } }, .{ .type = .vector_4_f32, .kind = .{ .smax_mem = .{} } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixunssfti" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixunssfti" } }, .{ .type = .i64, .kind = .{ .reg = .rdx } }, .{ .type = .usize, .kind = .{ .reg = .rdi } }, .{ .type = .u32, .kind = .{ .reg = .ecx } }, @@ -101096,7 +101044,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .i64, .kind = .{ .rc = .general_purpose } }, .{ .type = .i64, .kind = .{ .reg = .rax } }, .{ .type = .vector_4_f32, .kind = .{ .smax_mem = .{} } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixunssfti" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixunssfti" } }, .{ .type = .i64, .kind = .{ .reg = .rdx } }, .{ .type = .usize, .kind = .{ .reg = .rdi } }, .{ .type = .u32, .kind = .{ .reg = .ecx } }, @@ -101137,7 +101085,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .f32, .kind = .{ .reg = .xmm0 } }, .{ .type = .i64, .kind = .{ .reg = .rax } }, .{ .type = .vector_4_f32, .kind = .{ .smax_mem = .{} } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixunssfti" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixunssfti" } }, .{ .type = .u32, .kind = .{ .reg = .ecx } }, .{ .type = .i64, .kind = .{ .reg = .rdx } }, .{ .type = .usize, .kind = .{ .reg = .rdi } }, @@ -101175,7 +101123,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixunssfti" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixunssfti" } }, .{ .type = .i64, .kind = .{ .reg = .rax } }, .{ .type = .i64, .kind = .{ .reg = .rdx } }, .{ .type = .usize, .kind = .{ .reg = .rdi } }, @@ -101385,7 +101333,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f32, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixsfsi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixsfsi" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .unused, .unused, @@ -101416,7 +101364,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f32, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixsfsi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixsfsi" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .unused, .unused, @@ -101447,7 +101395,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f32, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixsfsi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixsfsi" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .unused, .unused, @@ -101478,7 +101426,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f32, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixsfsi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixsfsi" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .unused, .unused, @@ -101681,7 +101629,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f32, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixsfsi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixsfsi" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .unused, .unused, @@ -101712,7 +101660,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f32, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixsfsi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixsfsi" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .unused, .unused, @@ -101825,7 +101773,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f32, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixsfsi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixsfsi" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .unused, .unused, @@ -101856,7 +101804,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f32, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixunssfsi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixunssfsi" } }, .{ .type = .u32, .kind = .{ .reg = .eax } }, .unused, .unused, @@ -101887,7 +101835,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f32, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixsfsi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixsfsi" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .unused, .unused, @@ -101918,7 +101866,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f32, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixunssfsi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixunssfsi" } }, .{ .type = .u32, .kind = .{ .reg = .eax } }, .unused, .unused, @@ -102007,7 +101955,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f32, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixsfdi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixsfdi" } }, .{ .type = .i64, .kind = .{ .reg = .rax } }, .unused, .unused, @@ -102038,7 +101986,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f32, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixunssfdi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixunssfdi" } }, .{ .type = .u64, .kind = .{ .reg = .rax } }, .unused, .unused, @@ -102069,7 +102017,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f32, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixsfdi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixsfdi" } }, .{ .type = .i64, .kind = .{ .reg = .rax } }, .unused, .unused, @@ -102100,7 +102048,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f32, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixunssfdi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixunssfdi" } }, .{ .type = .u64, .kind = .{ .reg = .rax } }, .unused, .unused, @@ -102131,7 +102079,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f32, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixsfti" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixsfti" } }, .{ .type = .u64, .kind = .{ .reg = .rax } }, .{ .type = .i64, .kind = .{ .reg = .rdx } }, .unused, @@ -102163,7 +102111,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f32, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixunssfti" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixunssfti" } }, .{ .type = .u64, .kind = .{ .reg = .rax } }, .{ .type = .u64, .kind = .{ .reg = .rdx } }, .unused, @@ -102195,7 +102143,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f32, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixsfti" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixsfti" } }, .{ .type = .u64, .kind = .{ .reg = .rax } }, .{ .type = .i64, .kind = .{ .reg = .rdx } }, .unused, @@ -102227,7 +102175,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f32, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixunssfti" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixunssfti" } }, .{ .type = .u64, .kind = .{ .reg = .rax } }, .{ .type = .u64, .kind = .{ .reg = .rdx } }, .unused, @@ -102262,7 +102210,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .usize, .kind = .{ .reg = .rdi } }, .{ .type = .usize, .kind = .{ .reg = .rsi } }, .{ .type = .f32, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixsfei" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixsfei" } }, .unused, .unused, .unused, @@ -102296,7 +102244,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .usize, .kind = .{ .reg = .rdi } }, .{ .type = .usize, .kind = .{ .reg = .rsi } }, .{ .type = .f32, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixunssfei" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixunssfei" } }, .unused, .unused, .unused, @@ -102330,7 +102278,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .usize, .kind = .{ .reg = .rdi } }, .{ .type = .usize, .kind = .{ .reg = .rsi } }, .{ .type = .f32, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixsfei" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixsfei" } }, .unused, .unused, .unused, @@ -102364,7 +102312,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .usize, .kind = .{ .reg = .rdi } }, .{ .type = .usize, .kind = .{ .reg = .rsi } }, .{ .type = .f32, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixunssfei" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixunssfei" } }, .unused, .unused, .unused, @@ -102703,7 +102651,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixdfti" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixdfti" } }, .unused, .unused, .unused, @@ -102729,7 +102677,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixunsdfti" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixunsdfti" } }, .unused, .unused, .unused, @@ -102757,7 +102705,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .usize, .kind = .{ .reg = .rdi } }, .{ .type = .usize, .kind = .{ .reg = .rsi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixdfei" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixdfei" } }, .unused, .unused, .unused, @@ -102785,7 +102733,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .usize, .kind = .{ .reg = .rdi } }, .{ .type = .usize, .kind = .{ .reg = .rsi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixunsdfei" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixunsdfei" } }, .unused, .unused, .unused, @@ -103090,7 +103038,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixdfsi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixdfsi" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .unused, .unused, @@ -103121,7 +103069,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixdfsi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixdfsi" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .unused, .unused, @@ -103152,7 +103100,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixdfsi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixdfsi" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .unused, .unused, @@ -103183,7 +103131,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixsfsi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixsfsi" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .unused, .unused, @@ -103214,7 +103162,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixdfsi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixdfsi" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .unused, .unused, @@ -103246,7 +103194,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixsfsi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixsfsi" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .unused, .unused, @@ -103577,7 +103525,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixdfsi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixdfsi" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .unused, .unused, @@ -103608,7 +103556,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixdfsi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixdfsi" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .unused, .unused, @@ -103639,7 +103587,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixdfsi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixdfsi" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .unused, .unused, @@ -103837,7 +103785,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixdfsi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixdfsi" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .unused, .unused, @@ -103868,7 +103816,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixunsdfsi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixunsdfsi" } }, .{ .type = .u32, .kind = .{ .reg = .eax } }, .unused, .unused, @@ -103899,7 +103847,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixdfsi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixdfsi" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .unused, .unused, @@ -103930,7 +103878,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixunsdfsi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixunsdfsi" } }, .{ .type = .u32, .kind = .{ .reg = .eax } }, .unused, .unused, @@ -103961,7 +103909,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixdfsi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixdfsi" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .unused, .unused, @@ -103993,7 +103941,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixunsdfsi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixunsdfsi" } }, .{ .type = .u32, .kind = .{ .reg = .eax } }, .unused, .unused, @@ -104118,7 +104066,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixdfdi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixdfdi" } }, .{ .type = .i64, .kind = .{ .reg = .rax } }, .unused, .unused, @@ -104149,7 +104097,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixunsdfdi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixunsdfdi" } }, .{ .type = .u64, .kind = .{ .reg = .rax } }, .unused, .unused, @@ -104180,7 +104128,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixdfdi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixdfdi" } }, .{ .type = .i64, .kind = .{ .reg = .rax } }, .unused, .unused, @@ -104211,7 +104159,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixunsdfdi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixunsdfdi" } }, .{ .type = .u64, .kind = .{ .reg = .rax } }, .unused, .unused, @@ -104242,7 +104190,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixdfdi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixdfdi" } }, .{ .type = .i64, .kind = .{ .reg = .rax } }, .unused, .unused, @@ -104274,7 +104222,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixunsdfdi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixunsdfdi" } }, .{ .type = .u64, .kind = .{ .reg = .rax } }, .unused, .unused, @@ -104306,7 +104254,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixdfti" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixdfti" } }, .{ .type = .u64, .kind = .{ .reg = .rax } }, .{ .type = .i64, .kind = .{ .reg = .rdx } }, .unused, @@ -104338,7 +104286,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixunsdfti" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixunsdfti" } }, .{ .type = .u64, .kind = .{ .reg = .rax } }, .{ .type = .u64, .kind = .{ .reg = .rdx } }, .unused, @@ -104370,7 +104318,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixdfti" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixdfti" } }, .{ .type = .u64, .kind = .{ .reg = .rax } }, .{ .type = .i64, .kind = .{ .reg = .rdx } }, .unused, @@ -104402,7 +104350,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixunsdfti" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixunsdfti" } }, .{ .type = .u64, .kind = .{ .reg = .rax } }, .{ .type = .u64, .kind = .{ .reg = .rdx } }, .unused, @@ -104434,7 +104382,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixdfti" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixdfti" } }, .{ .type = .u64, .kind = .{ .reg = .rax } }, .{ .type = .i64, .kind = .{ .reg = .rdx } }, .unused, @@ -104467,7 +104415,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixunsdfti" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixunsdfti" } }, .{ .type = .u64, .kind = .{ .reg = .rax } }, .{ .type = .u64, .kind = .{ .reg = .rdx } }, .unused, @@ -104503,7 +104451,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .usize, .kind = .{ .reg = .rdi } }, .{ .type = .usize, .kind = .{ .reg = .rsi } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixdfei" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixdfei" } }, .unused, .unused, .unused, @@ -104537,7 +104485,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .usize, .kind = .{ .reg = .rdi } }, .{ .type = .usize, .kind = .{ .reg = .rsi } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixunsdfei" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixunsdfei" } }, .unused, .unused, .unused, @@ -104571,7 +104519,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .usize, .kind = .{ .reg = .rdi } }, .{ .type = .usize, .kind = .{ .reg = .rsi } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixdfei" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixdfei" } }, .unused, .unused, .unused, @@ -104605,7 +104553,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .usize, .kind = .{ .reg = .rdi } }, .{ .type = .usize, .kind = .{ .reg = .rsi } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixunsdfei" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixunsdfei" } }, .unused, .unused, .unused, @@ -104639,7 +104587,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .usize, .kind = .{ .reg = .rdi } }, .{ .type = .usize, .kind = .{ .reg = .rsi } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixdfei" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixdfei" } }, .unused, .unused, .unused, @@ -104674,7 +104622,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .usize, .kind = .{ .reg = .rdi } }, .{ .type = .usize, .kind = .{ .reg = .rsi } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixunsdfei" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixunsdfei" } }, .unused, .unused, .unused, @@ -105695,7 +105643,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f80, .kind = .{ .reg = .xmm0 } }, .{ .type = .f80, .kind = .{ .frame = .call_frame } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixunsxfti" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixunsxfti" } }, .{ .type = .u64, .kind = .{ .reg = .rax } }, .unused, .unused, @@ -105727,7 +105675,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f80, .kind = .{ .reg = .xmm0 } }, .{ .type = .f80, .kind = .{ .frame = .call_frame } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixunsxfti" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixunsxfti" } }, .{ .type = .u64, .kind = .{ .reg = .rax } }, .unused, .unused, @@ -105759,7 +105707,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f80, .kind = .{ .reg = .xmm0 } }, .{ .type = .f80, .kind = .{ .frame = .call_frame } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixunsxfti" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixunsxfti" } }, .{ .type = .u64, .kind = .{ .reg = .rax } }, .unused, .unused, @@ -105790,7 +105738,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .f80, .kind = .{ .reg = .xmm0 } }, .{ .type = .f80, .kind = .{ .frame = .call_frame } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixxfti" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixxfti" } }, .unused, .unused, .unused, @@ -105818,7 +105766,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .f80, .kind = .{ .reg = .xmm0 } }, .{ .type = .f80, .kind = .{ .frame = .call_frame } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixunsxfti" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixunsxfti" } }, .unused, .unused, .unused, @@ -105846,7 +105794,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .f80, .kind = .{ .reg = .xmm0 } }, .{ .type = .f80, .kind = .{ .frame = .call_frame } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixxfti" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixxfti" } }, .unused, .unused, .unused, @@ -105874,7 +105822,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .f80, .kind = .{ .reg = .xmm0 } }, .{ .type = .f80, .kind = .{ .frame = .call_frame } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixunsxfti" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixunsxfti" } }, .unused, .unused, .unused, @@ -105902,7 +105850,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .f80, .kind = .{ .reg = .xmm0 } }, .{ .type = .f80, .kind = .{ .frame = .call_frame } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixxfti" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixxfti" } }, .unused, .unused, .unused, @@ -105930,7 +105878,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .f80, .kind = .{ .reg = .xmm0 } }, .{ .type = .f80, .kind = .{ .frame = .call_frame } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixunsxfti" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixunsxfti" } }, .unused, .unused, .unused, @@ -105959,7 +105907,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f80, .kind = .{ .reg = .xmm0 } }, .{ .type = .f80, .kind = .{ .frame = .call_frame } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixxfti" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixxfti" } }, .{ .type = .u64, .kind = .{ .reg = .rax } }, .{ .type = .i64, .kind = .{ .reg = .rdx } }, .unused, @@ -105992,7 +105940,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f80, .kind = .{ .reg = .xmm0 } }, .{ .type = .f80, .kind = .{ .frame = .call_frame } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixunsxfti" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixunsxfti" } }, .{ .type = .u64, .kind = .{ .reg = .rax } }, .{ .type = .u64, .kind = .{ .reg = .rdx } }, .unused, @@ -106025,7 +105973,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f80, .kind = .{ .reg = .xmm0 } }, .{ .type = .f80, .kind = .{ .frame = .call_frame } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixxfti" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixxfti" } }, .{ .type = .u64, .kind = .{ .reg = .rax } }, .{ .type = .i64, .kind = .{ .reg = .rdx } }, .unused, @@ -106058,7 +106006,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f80, .kind = .{ .reg = .xmm0 } }, .{ .type = .f80, .kind = .{ .frame = .call_frame } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixunsxfti" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixunsxfti" } }, .{ .type = .u64, .kind = .{ .reg = .rax } }, .{ .type = .u64, .kind = .{ .reg = .rdx } }, .unused, @@ -106091,7 +106039,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f80, .kind = .{ .reg = .xmm0 } }, .{ .type = .f80, .kind = .{ .frame = .call_frame } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixxfti" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixxfti" } }, .{ .type = .u64, .kind = .{ .reg = .rax } }, .{ .type = .i64, .kind = .{ .reg = .rdx } }, .unused, @@ -106124,7 +106072,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f80, .kind = .{ .reg = .xmm0 } }, .{ .type = .f80, .kind = .{ .frame = .call_frame } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixunsxfti" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixunsxfti" } }, .{ .type = .u64, .kind = .{ .reg = .rax } }, .{ .type = .u64, .kind = .{ .reg = .rdx } }, .unused, @@ -106158,7 +106106,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .usize, .kind = .{ .reg = .rsi } }, .{ .type = .f80, .kind = .{ .reg = .xmm0 } }, .{ .type = .f80, .kind = .{ .frame = .call_frame } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixxfei" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixxfei" } }, .unused, .unused, .unused, @@ -106188,7 +106136,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .usize, .kind = .{ .reg = .rsi } }, .{ .type = .f80, .kind = .{ .reg = .xmm0 } }, .{ .type = .f80, .kind = .{ .frame = .call_frame } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixunsxfei" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixunsxfei" } }, .unused, .unused, .unused, @@ -106218,7 +106166,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .usize, .kind = .{ .reg = .rsi } }, .{ .type = .f80, .kind = .{ .reg = .xmm0 } }, .{ .type = .f80, .kind = .{ .frame = .call_frame } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixxfei" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixxfei" } }, .unused, .unused, .unused, @@ -106248,7 +106196,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .usize, .kind = .{ .reg = .rsi } }, .{ .type = .f80, .kind = .{ .reg = .xmm0 } }, .{ .type = .f80, .kind = .{ .frame = .call_frame } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixunsxfei" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixunsxfei" } }, .unused, .unused, .unused, @@ -106278,7 +106226,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .usize, .kind = .{ .reg = .rsi } }, .{ .type = .f80, .kind = .{ .reg = .xmm0 } }, .{ .type = .f80, .kind = .{ .frame = .call_frame } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixxfei" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixxfei" } }, .unused, .unused, .unused, @@ -106308,7 +106256,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .usize, .kind = .{ .reg = .rsi } }, .{ .type = .f80, .kind = .{ .reg = .xmm0 } }, .{ .type = .f80, .kind = .{ .frame = .call_frame } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixunsxfei" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixunsxfei" } }, .unused, .unused, .unused, @@ -106340,7 +106288,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .usize, .kind = .{ .reg = .rsi } }, .{ .type = .f80, .kind = .{ .reg = .xmm0 } }, .{ .type = .f80, .kind = .{ .frame = .call_frame } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixxfei" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixxfei" } }, .unused, .unused, .unused, @@ -106375,7 +106323,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .usize, .kind = .{ .reg = .rsi } }, .{ .type = .f80, .kind = .{ .reg = .xmm0 } }, .{ .type = .f80, .kind = .{ .frame = .call_frame } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixunsxfei" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixunsxfei" } }, .unused, .unused, .unused, @@ -106410,7 +106358,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .usize, .kind = .{ .reg = .rsi } }, .{ .type = .f80, .kind = .{ .reg = .xmm0 } }, .{ .type = .f80, .kind = .{ .frame = .call_frame } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixxfei" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixxfei" } }, .unused, .unused, .unused, @@ -106445,7 +106393,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .usize, .kind = .{ .reg = .rsi } }, .{ .type = .f80, .kind = .{ .reg = .xmm0 } }, .{ .type = .f80, .kind = .{ .frame = .call_frame } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixunsxfei" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixunsxfei" } }, .unused, .unused, .unused, @@ -106480,7 +106428,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .usize, .kind = .{ .reg = .rsi } }, .{ .type = .f80, .kind = .{ .reg = .xmm0 } }, .{ .type = .f80, .kind = .{ .frame = .call_frame } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixxfei" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixxfei" } }, .unused, .unused, .unused, @@ -106515,7 +106463,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .usize, .kind = .{ .reg = .rsi } }, .{ .type = .f80, .kind = .{ .reg = .xmm0 } }, .{ .type = .f80, .kind = .{ .frame = .call_frame } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixunsxfei" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixunsxfei" } }, .unused, .unused, .unused, @@ -106547,7 +106495,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixtfsi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixtfsi" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .unused, .unused, @@ -106580,7 +106528,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixtfsi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixtfsi" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .unused, .unused, @@ -106613,7 +106561,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixtfsi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixtfsi" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .unused, .unused, @@ -106646,7 +106594,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixtfsi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixtfsi" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .unused, .unused, @@ -106679,7 +106627,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixtfsi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixtfsi" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .unused, .unused, @@ -106712,7 +106660,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixtfsi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixtfsi" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .unused, .unused, @@ -106744,7 +106692,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixtfsi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixtfsi" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .unused, .unused, @@ -106775,7 +106723,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixtfsi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixtfsi" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .unused, .unused, @@ -106806,7 +106754,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixtfsi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixtfsi" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .unused, .unused, @@ -106835,7 +106783,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixtfsi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixtfsi" } }, .unused, .unused, .unused, @@ -106861,7 +106809,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixunstfsi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixunstfsi" } }, .unused, .unused, .unused, @@ -106889,7 +106837,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixtfsi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixtfsi" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .unused, .unused, @@ -106920,7 +106868,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixunstfsi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixunstfsi" } }, .{ .type = .u32, .kind = .{ .reg = .eax } }, .unused, .unused, @@ -106951,7 +106899,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixtfsi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixtfsi" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .unused, .unused, @@ -106982,7 +106930,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixunstfsi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixunstfsi" } }, .{ .type = .u32, .kind = .{ .reg = .eax } }, .unused, .unused, @@ -107013,7 +106961,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixtfsi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixtfsi" } }, .{ .type = .i32, .kind = .{ .reg = .eax } }, .unused, .unused, @@ -107044,7 +106992,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixunstfsi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixunstfsi" } }, .{ .type = .u32, .kind = .{ .reg = .eax } }, .unused, .unused, @@ -107073,7 +107021,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixtfdi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixtfdi" } }, .unused, .unused, .unused, @@ -107099,7 +107047,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixunstfdi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixunstfdi" } }, .unused, .unused, .unused, @@ -107127,7 +107075,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixtfdi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixtfdi" } }, .{ .type = .i64, .kind = .{ .reg = .rax } }, .unused, .unused, @@ -107158,7 +107106,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixunstfdi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixunstfdi" } }, .{ .type = .u64, .kind = .{ .reg = .rax } }, .unused, .unused, @@ -107189,7 +107137,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixtfdi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixtfdi" } }, .{ .type = .i64, .kind = .{ .reg = .rax } }, .unused, .unused, @@ -107220,7 +107168,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixunstfdi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixunstfdi" } }, .{ .type = .u64, .kind = .{ .reg = .rax } }, .unused, .unused, @@ -107251,7 +107199,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixtfdi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixtfdi" } }, .{ .type = .i64, .kind = .{ .reg = .rax } }, .unused, .unused, @@ -107282,7 +107230,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixunstfdi" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixunstfdi" } }, .{ .type = .u64, .kind = .{ .reg = .rax } }, .unused, .unused, @@ -107311,7 +107259,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixtfti" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixtfti" } }, .unused, .unused, .unused, @@ -107337,7 +107285,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixunstfti" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixunstfti" } }, .unused, .unused, .unused, @@ -107365,7 +107313,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixtfti" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixtfti" } }, .{ .type = .u64, .kind = .{ .reg = .rax } }, .{ .type = .i64, .kind = .{ .reg = .rdx } }, .unused, @@ -107397,7 +107345,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixunstfti" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixunstfti" } }, .{ .type = .u64, .kind = .{ .reg = .rax } }, .{ .type = .u64, .kind = .{ .reg = .rdx } }, .unused, @@ -107429,7 +107377,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixtfti" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixtfti" } }, .{ .type = .u64, .kind = .{ .reg = .rax } }, .{ .type = .i64, .kind = .{ .reg = .rdx } }, .unused, @@ -107461,7 +107409,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixunstfti" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixunstfti" } }, .{ .type = .u64, .kind = .{ .reg = .rax } }, .{ .type = .u64, .kind = .{ .reg = .rdx } }, .unused, @@ -107493,7 +107441,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixtfti" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixtfti" } }, .{ .type = .u64, .kind = .{ .reg = .rax } }, .{ .type = .i64, .kind = .{ .reg = .rdx } }, .unused, @@ -107525,7 +107473,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixunstfti" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixunstfti" } }, .{ .type = .u64, .kind = .{ .reg = .rax } }, .{ .type = .u64, .kind = .{ .reg = .rdx } }, .unused, @@ -107557,7 +107505,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .usize, .kind = .{ .reg = .rdi } }, .{ .type = .usize, .kind = .{ .reg = .rsi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixtfei" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixtfei" } }, .unused, .unused, .unused, @@ -107585,7 +107533,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .usize, .kind = .{ .reg = .rdi } }, .{ .type = .usize, .kind = .{ .reg = .rsi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixunstfei" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixunstfei" } }, .unused, .unused, .unused, @@ -107616,7 +107564,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .usize, .kind = .{ .reg = .rdi } }, .{ .type = .usize, .kind = .{ .reg = .rsi } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixtfei" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixtfei" } }, .unused, .unused, .unused, @@ -107650,7 +107598,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .usize, .kind = .{ .reg = .rdi } }, .{ .type = .usize, .kind = .{ .reg = .rsi } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixunstfei" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixunstfei" } }, .unused, .unused, .unused, @@ -107684,7 +107632,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .usize, .kind = .{ .reg = .rdi } }, .{ .type = .usize, .kind = .{ .reg = .rsi } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixtfei" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixtfei" } }, .unused, .unused, .unused, @@ -107718,7 +107666,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .usize, .kind = .{ .reg = .rdi } }, .{ .type = .usize, .kind = .{ .reg = .rsi } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixunstfei" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixunstfei" } }, .unused, .unused, .unused, @@ -107752,7 +107700,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .usize, .kind = .{ .reg = .rdi } }, .{ .type = .usize, .kind = .{ .reg = .rsi } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixtfei" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixtfei" } }, .unused, .unused, .unused, @@ -107786,7 +107734,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .usize, .kind = .{ .reg = .rdi } }, .{ .type = .usize, .kind = .{ .reg = .rsi } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fixunstfei" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fixunstfei" } }, .unused, .unused, .unused, @@ -108034,7 +107982,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatsihf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatsihf" } }, .unused, .unused, .unused, @@ -108061,7 +108009,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatunsihf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatunsihf" } }, .unused, .unused, .unused, @@ -108088,7 +108036,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatsihf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatsihf" } }, .unused, .unused, .unused, @@ -108115,7 +108063,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatunsihf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatunsihf" } }, .unused, .unused, .unused, @@ -108142,7 +108090,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatsihf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatsihf" } }, .unused, .unused, .unused, @@ -108168,7 +108116,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatunsihf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatunsihf" } }, .unused, .unused, .unused, @@ -108194,7 +108142,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatdihf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatdihf" } }, .unused, .unused, .unused, @@ -108220,7 +108168,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatundihf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatundihf" } }, .unused, .unused, .unused, @@ -108246,7 +108194,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floattihf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floattihf" } }, .unused, .unused, .unused, @@ -108272,7 +108220,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatuntihf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatuntihf" } }, .unused, .unused, .unused, @@ -108300,7 +108248,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .usize, .kind = .{ .reg = .rdi } }, .{ .type = .usize, .kind = .{ .reg = .rsi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floateihf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floateihf" } }, .unused, .unused, .unused, @@ -108328,7 +108276,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .usize, .kind = .{ .reg = .rdi } }, .{ .type = .usize, .kind = .{ .reg = .rsi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatuneihf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatuneihf" } }, .unused, .unused, .unused, @@ -108532,7 +108480,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .i32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatsihf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatsihf" } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -108563,7 +108511,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .i32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatsihf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatsihf" } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -108594,7 +108542,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .i32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatsihf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatsihf" } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -108625,7 +108573,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .i32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatsihf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatsihf" } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -108656,7 +108604,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .i32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatsihf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatsihf" } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -108688,7 +108636,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .i32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatsihf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatsihf" } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -108720,7 +108668,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .i32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatsihf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatsihf" } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f32, .kind = .mem }, .unused, @@ -108753,7 +108701,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .i32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatsihf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatsihf" } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f32, .kind = .mem }, .unused, @@ -108786,7 +108734,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatunsihf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatunsihf" } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -108817,7 +108765,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatunsihf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatunsihf" } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -108848,7 +108796,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatunsihf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatunsihf" } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -108879,7 +108827,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatunsihf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatunsihf" } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -108910,7 +108858,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatunsihf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatunsihf" } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -108942,7 +108890,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatunsihf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatunsihf" } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -108974,7 +108922,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatunsihf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatunsihf" } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f32, .kind = .mem }, .unused, @@ -109007,7 +108955,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatunsihf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatunsihf" } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f32, .kind = .mem }, .unused, @@ -109216,7 +109164,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .i32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatsihf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatsihf" } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -109247,7 +109195,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .i32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatsihf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatsihf" } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -109278,7 +109226,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .i32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatsihf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatsihf" } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -109310,7 +109258,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .i32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatsihf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatsihf" } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f32, .kind = .mem }, .unused, @@ -109343,7 +109291,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatunsihf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatunsihf" } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -109374,7 +109322,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatunsihf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatunsihf" } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -109405,7 +109353,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatunsihf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatunsihf" } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -109437,7 +109385,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatunsihf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatunsihf" } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f32, .kind = .mem }, .unused, @@ -109554,7 +109502,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .i32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatsihf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatsihf" } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -109585,7 +109533,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .i32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatsihf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatsihf" } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -109616,7 +109564,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .i32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatsihf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatsihf" } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -109648,7 +109596,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .i32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatsihf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatsihf" } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f32, .kind = .mem }, .unused, @@ -109681,7 +109629,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatunsihf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatunsihf" } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -109712,7 +109660,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatunsihf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatunsihf" } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -109743,7 +109691,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatunsihf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatunsihf" } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -109775,7 +109723,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatunsihf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatunsihf" } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f32, .kind = .mem }, .unused, @@ -109839,7 +109787,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .i64, .kind = .{ .reg = .rdi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatdihf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatdihf" } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -109870,7 +109818,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .i64, .kind = .{ .reg = .rdi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatdihf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatdihf" } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -109901,7 +109849,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .i64, .kind = .{ .reg = .rdi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatdihf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatdihf" } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -109933,7 +109881,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .i64, .kind = .{ .reg = .rdi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatdihf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatdihf" } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f32, .kind = .mem }, .unused, @@ -109966,7 +109914,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u64, .kind = .{ .reg = .rdi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatundihf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatundihf" } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -109997,7 +109945,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u64, .kind = .{ .reg = .rdi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatundihf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatundihf" } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -110028,7 +109976,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u64, .kind = .{ .reg = .rdi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatundihf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatundihf" } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -110060,7 +110008,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u64, .kind = .{ .reg = .rdi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatundihf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatundihf" } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f32, .kind = .mem }, .unused, @@ -110094,7 +110042,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u64, .kind = .{ .reg = .rdi } }, .{ .type = .i64, .kind = .{ .reg = .rsi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floattihf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floattihf" } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -110126,7 +110074,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u64, .kind = .{ .reg = .rdi } }, .{ .type = .i64, .kind = .{ .reg = .rsi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floattihf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floattihf" } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -110158,7 +110106,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u64, .kind = .{ .reg = .rdi } }, .{ .type = .i64, .kind = .{ .reg = .rsi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floattihf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floattihf" } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -110191,7 +110139,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u64, .kind = .{ .reg = .rdi } }, .{ .type = .i64, .kind = .{ .reg = .rsi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floattihf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floattihf" } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f32, .kind = .mem }, .unused, @@ -110225,7 +110173,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u64, .kind = .{ .reg = .rdi } }, .{ .type = .u64, .kind = .{ .reg = .rsi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatuntihf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatuntihf" } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -110257,7 +110205,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u64, .kind = .{ .reg = .rdi } }, .{ .type = .u64, .kind = .{ .reg = .rsi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatuntihf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatuntihf" } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -110289,7 +110237,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u64, .kind = .{ .reg = .rdi } }, .{ .type = .u64, .kind = .{ .reg = .rsi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatuntihf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatuntihf" } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -110322,7 +110270,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u64, .kind = .{ .reg = .rdi } }, .{ .type = .u64, .kind = .{ .reg = .rsi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatuntihf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatuntihf" } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f32, .kind = .mem }, .unused, @@ -110357,7 +110305,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .usize, .kind = .{ .reg = .rdi } }, .{ .type = .usize, .kind = .{ .reg = .rsi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floateihf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floateihf" } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -110391,7 +110339,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .usize, .kind = .{ .reg = .rdi } }, .{ .type = .usize, .kind = .{ .reg = .rsi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floateihf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floateihf" } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -110425,7 +110373,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .usize, .kind = .{ .reg = .rdi } }, .{ .type = .usize, .kind = .{ .reg = .rsi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floateihf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floateihf" } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -110460,7 +110408,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .usize, .kind = .{ .reg = .rdi } }, .{ .type = .usize, .kind = .{ .reg = .rsi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floateihf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floateihf" } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f32, .kind = .mem }, .unused, @@ -110496,7 +110444,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .usize, .kind = .{ .reg = .rdi } }, .{ .type = .usize, .kind = .{ .reg = .rsi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatuneihf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatuneihf" } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -110530,7 +110478,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .usize, .kind = .{ .reg = .rdi } }, .{ .type = .usize, .kind = .{ .reg = .rsi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatuneihf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatuneihf" } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -110564,7 +110512,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .usize, .kind = .{ .reg = .rdi } }, .{ .type = .usize, .kind = .{ .reg = .rsi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatuneihf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatuneihf" } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -110599,7 +110547,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .usize, .kind = .{ .reg = .rdi } }, .{ .type = .usize, .kind = .{ .reg = .rsi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatuneihf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatuneihf" } }, .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f32, .kind = .mem }, .unused, @@ -111023,7 +110971,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floattisf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floattisf" } }, .unused, .unused, .unused, @@ -111049,7 +110997,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatuntisf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatuntisf" } }, .unused, .unused, .unused, @@ -111077,7 +111025,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .usize, .kind = .{ .reg = .rdi } }, .{ .type = .usize, .kind = .{ .reg = .rsi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floateisf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floateisf" } }, .unused, .unused, .unused, @@ -111105,7 +111053,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .usize, .kind = .{ .reg = .rdi } }, .{ .type = .usize, .kind = .{ .reg = .rsi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatuneisf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatuneisf" } }, .unused, .unused, .unused, @@ -111391,7 +111339,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .i32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatsisf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatsisf" } }, .{ .type = .f32, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -111422,7 +111370,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .i32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatsisf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatsisf" } }, .{ .type = .f32, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -111453,7 +111401,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .i32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatsisf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatsisf" } }, .{ .type = .f32, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -111484,7 +111432,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .i32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatsisf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatsisf" } }, .{ .type = .f32, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -111515,7 +111463,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatunsisf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatunsisf" } }, .{ .type = .f32, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -111546,7 +111494,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatunsisf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatunsisf" } }, .{ .type = .f32, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -111577,7 +111525,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatunsisf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatunsisf" } }, .{ .type = .f32, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -111608,7 +111556,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatunsisf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatunsisf" } }, .{ .type = .f32, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -111897,7 +111845,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .i32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatsisf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatsisf" } }, .{ .type = .f32, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -111928,7 +111876,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .i32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatsisf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatsisf" } }, .{ .type = .f32, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -111959,7 +111907,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatunsisf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatunsisf" } }, .{ .type = .f32, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -111990,7 +111938,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatunsisf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatunsisf" } }, .{ .type = .f32, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -112173,7 +112121,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .i32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatsisf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatsisf" } }, .{ .type = .f32, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -112204,7 +112152,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .i32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatsisf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatsisf" } }, .{ .type = .f32, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -112235,7 +112183,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatunsisf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatunsisf" } }, .{ .type = .f32, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -112266,7 +112214,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatunsisf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatunsisf" } }, .{ .type = .f32, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -112357,7 +112305,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .i64, .kind = .{ .reg = .rdi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatdisf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatdisf" } }, .{ .type = .f32, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -112388,7 +112336,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .i64, .kind = .{ .reg = .rdi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatdisf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatdisf" } }, .{ .type = .f32, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -112419,7 +112367,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u64, .kind = .{ .reg = .rdi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatundisf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatundisf" } }, .{ .type = .f32, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -112450,7 +112398,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u64, .kind = .{ .reg = .rdi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatundisf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatundisf" } }, .{ .type = .f32, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -112482,7 +112430,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u64, .kind = .{ .reg = .rdi } }, .{ .type = .i64, .kind = .{ .reg = .rsi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floattisf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floattisf" } }, .{ .type = .f32, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -112514,7 +112462,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u64, .kind = .{ .reg = .rdi } }, .{ .type = .i64, .kind = .{ .reg = .rsi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floattisf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floattisf" } }, .{ .type = .f32, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -112546,7 +112494,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u64, .kind = .{ .reg = .rdi } }, .{ .type = .u64, .kind = .{ .reg = .rsi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatuntisf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatuntisf" } }, .{ .type = .f32, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -112578,7 +112526,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u64, .kind = .{ .reg = .rdi } }, .{ .type = .u64, .kind = .{ .reg = .rsi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatuntisf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatuntisf" } }, .{ .type = .f32, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -112611,7 +112559,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .usize, .kind = .{ .reg = .rdi } }, .{ .type = .usize, .kind = .{ .reg = .rsi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floateisf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floateisf" } }, .{ .type = .f32, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -112645,7 +112593,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .usize, .kind = .{ .reg = .rdi } }, .{ .type = .usize, .kind = .{ .reg = .rsi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floateisf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floateisf" } }, .{ .type = .f32, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -112679,7 +112627,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .usize, .kind = .{ .reg = .rdi } }, .{ .type = .usize, .kind = .{ .reg = .rsi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatuneisf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatuneisf" } }, .{ .type = .f32, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -112713,7 +112661,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .usize, .kind = .{ .reg = .rdi } }, .{ .type = .usize, .kind = .{ .reg = .rsi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatuneisf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatuneisf" } }, .{ .type = .f32, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -113353,7 +113301,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floattidf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floattidf" } }, .unused, .unused, .unused, @@ -113379,7 +113327,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatuntidf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatuntidf" } }, .unused, .unused, .unused, @@ -113407,7 +113355,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .usize, .kind = .{ .reg = .rdi } }, .{ .type = .usize, .kind = .{ .reg = .rsi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floateidf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floateidf" } }, .unused, .unused, .unused, @@ -113435,7 +113383,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .usize, .kind = .{ .reg = .rdi } }, .{ .type = .usize, .kind = .{ .reg = .rsi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatuneidf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatuneidf" } }, .unused, .unused, .unused, @@ -113729,7 +113677,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .i32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatsidf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatsidf" } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -113760,7 +113708,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .i32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatsidf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatsidf" } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -113791,7 +113739,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .i32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatsidf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatsidf" } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -113822,7 +113770,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .i32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatsidf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatsidf" } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -113853,7 +113801,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .i32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatsidf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatsidf" } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -113884,7 +113832,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .i32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatsidf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatsidf" } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -113915,7 +113863,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatunsidf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatunsidf" } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -113946,7 +113894,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatunsidf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatunsidf" } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -113977,7 +113925,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatunsidf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatunsidf" } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -114008,7 +113956,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatunsidf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatunsidf" } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -114039,7 +113987,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatunsidf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatunsidf" } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -114070,7 +114018,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatunsidf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatunsidf" } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -114363,7 +114311,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .i32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatsidf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatsidf" } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -114394,7 +114342,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .i32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatsidf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatsidf" } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -114425,7 +114373,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .i32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatsidf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatsidf" } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -114456,7 +114404,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatunsidf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatunsidf" } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -114487,7 +114435,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatunsidf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatunsidf" } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -114518,7 +114466,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatunsidf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatunsidf" } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -114672,7 +114620,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .i32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatsidf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatsidf" } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -114703,7 +114651,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .i32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatsidf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatsidf" } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -114734,7 +114682,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .i32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatsidf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatsidf" } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -114765,7 +114713,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatunsidf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatunsidf" } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -114796,7 +114744,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatunsidf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatunsidf" } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -114827,7 +114775,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatunsidf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatunsidf" } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -114918,7 +114866,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .i64, .kind = .{ .reg = .rdi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatdidf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatdidf" } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -114949,7 +114897,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .i64, .kind = .{ .reg = .rdi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatdidf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatdidf" } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -114980,7 +114928,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .i64, .kind = .{ .reg = .rdi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatdidf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatdidf" } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -115011,7 +114959,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u64, .kind = .{ .reg = .rdi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatundidf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatundidf" } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -115042,7 +114990,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u64, .kind = .{ .reg = .rdi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatundidf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatundidf" } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -115073,7 +115021,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u64, .kind = .{ .reg = .rdi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatundidf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatundidf" } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -115105,7 +115053,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u64, .kind = .{ .reg = .rdi } }, .{ .type = .i64, .kind = .{ .reg = .rsi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floattidf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floattidf" } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -115137,7 +115085,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u64, .kind = .{ .reg = .rdi } }, .{ .type = .i64, .kind = .{ .reg = .rsi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floattidf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floattidf" } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -115169,7 +115117,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u64, .kind = .{ .reg = .rdi } }, .{ .type = .i64, .kind = .{ .reg = .rsi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floattidf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floattidf" } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -115201,7 +115149,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u64, .kind = .{ .reg = .rdi } }, .{ .type = .u64, .kind = .{ .reg = .rsi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatuntidf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatuntidf" } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -115233,7 +115181,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u64, .kind = .{ .reg = .rdi } }, .{ .type = .u64, .kind = .{ .reg = .rsi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatuntidf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatuntidf" } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -115265,7 +115213,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u64, .kind = .{ .reg = .rdi } }, .{ .type = .u64, .kind = .{ .reg = .rsi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatuntidf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatuntidf" } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -115298,7 +115246,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .usize, .kind = .{ .reg = .rdi } }, .{ .type = .usize, .kind = .{ .reg = .rsi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floateidf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floateidf" } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -115332,7 +115280,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .usize, .kind = .{ .reg = .rdi } }, .{ .type = .usize, .kind = .{ .reg = .rsi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floateidf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floateidf" } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -115366,7 +115314,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .usize, .kind = .{ .reg = .rdi } }, .{ .type = .usize, .kind = .{ .reg = .rsi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floateidf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floateidf" } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -115400,7 +115348,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .usize, .kind = .{ .reg = .rdi } }, .{ .type = .usize, .kind = .{ .reg = .rsi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatuneidf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatuneidf" } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -115434,7 +115382,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .usize, .kind = .{ .reg = .rdi } }, .{ .type = .usize, .kind = .{ .reg = .rsi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatuneidf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatuneidf" } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -115468,7 +115416,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .usize, .kind = .{ .reg = .rdi } }, .{ .type = .usize, .kind = .{ .reg = .rsi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatuneidf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatuneidf" } }, .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -115715,7 +115663,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floattixf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floattixf" } }, .unused, .unused, .unused, @@ -115741,7 +115689,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatuntixf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatuntixf" } }, .unused, .unused, .unused, @@ -115769,7 +115717,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .usize, .kind = .{ .reg = .rdi } }, .{ .type = .usize, .kind = .{ .reg = .rsi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floateixf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floateixf" } }, .unused, .unused, .unused, @@ -115797,7 +115745,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .usize, .kind = .{ .reg = .rdi } }, .{ .type = .usize, .kind = .{ .reg = .rsi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatuneixf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatuneixf" } }, .unused, .unused, .unused, @@ -115958,7 +115906,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, .{ .type = .i32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatsixf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatsixf" } }, .{ .type = .f80, .kind = .{ .reg = .st7 } }, .unused, .unused, @@ -115992,7 +115940,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, .{ .type = .i32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatsixf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatsixf" } }, .{ .type = .f80, .kind = .{ .reg = .st7 } }, .unused, .unused, @@ -116026,7 +115974,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatunsixf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatunsixf" } }, .{ .type = .f80, .kind = .{ .reg = .st7 } }, .unused, .unused, @@ -116060,7 +116008,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatunsixf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatunsixf" } }, .{ .type = .f80, .kind = .{ .reg = .st7 } }, .unused, .unused, @@ -116122,7 +116070,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .i32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatsixf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatsixf" } }, .{ .type = .f80, .kind = .{ .reg = .st7 } }, .unused, .unused, @@ -116154,7 +116102,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatunsixf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatunsixf" } }, .{ .type = .f80, .kind = .{ .reg = .st7 } }, .unused, .unused, @@ -116215,7 +116163,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .i32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatsixf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatsixf" } }, .{ .type = .f80, .kind = .{ .reg = .st7 } }, .unused, .unused, @@ -116247,7 +116195,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatunsixf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatunsixf" } }, .{ .type = .f80, .kind = .{ .reg = .st7 } }, .unused, .unused, @@ -116308,7 +116256,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .i64, .kind = .{ .reg = .rdi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatdixf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatdixf" } }, .{ .type = .f80, .kind = .{ .reg = .st7 } }, .unused, .unused, @@ -116340,7 +116288,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u64, .kind = .{ .reg = .rdi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatundixf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatundixf" } }, .{ .type = .f80, .kind = .{ .reg = .st7 } }, .unused, .unused, @@ -116373,7 +116321,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u64, .kind = .{ .reg = .rdi } }, .{ .type = .i64, .kind = .{ .reg = .rsi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floattixf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floattixf" } }, .{ .type = .f80, .kind = .{ .reg = .st7 } }, .unused, .unused, @@ -116406,7 +116354,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u64, .kind = .{ .reg = .rdi } }, .{ .type = .u64, .kind = .{ .reg = .rsi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatuntixf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatuntixf" } }, .{ .type = .f80, .kind = .{ .reg = .st7 } }, .unused, .unused, @@ -116440,7 +116388,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .usize, .kind = .{ .reg = .rdi } }, .{ .type = .usize, .kind = .{ .reg = .rsi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floateixf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floateixf" } }, .{ .type = .f80, .kind = .{ .reg = .st7 } }, .unused, .unused, @@ -116475,7 +116423,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .usize, .kind = .{ .reg = .rdi } }, .{ .type = .usize, .kind = .{ .reg = .rsi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatuneixf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatuneixf" } }, .{ .type = .f80, .kind = .{ .reg = .st7 } }, .unused, .unused, @@ -116508,7 +116456,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ .{ .type = .i32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatsitf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatsitf" } }, .unused, .unused, .unused, @@ -116536,7 +116484,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ .{ .type = .u32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatunsitf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatunsitf" } }, .unused, .unused, .unused, @@ -116564,7 +116512,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ .{ .type = .i32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatsitf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatsitf" } }, .unused, .unused, .unused, @@ -116592,7 +116540,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ .{ .type = .u32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatunsitf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatunsitf" } }, .unused, .unused, .unused, @@ -116618,7 +116566,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatsitf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatsitf" } }, .unused, .unused, .unused, @@ -116644,7 +116592,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatunsitf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatunsitf" } }, .unused, .unused, .unused, @@ -116670,7 +116618,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatditf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatditf" } }, .unused, .unused, .unused, @@ -116696,7 +116644,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatunditf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatunditf" } }, .unused, .unused, .unused, @@ -116722,7 +116670,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floattitf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floattitf" } }, .unused, .unused, .unused, @@ -116748,7 +116696,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatuntitf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatuntitf" } }, .unused, .unused, .unused, @@ -116776,7 +116724,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .usize, .kind = .{ .reg = .rdi } }, .{ .type = .usize, .kind = .{ .reg = .rsi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floateitf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floateitf" } }, .unused, .unused, .unused, @@ -116804,7 +116752,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .usize, .kind = .{ .reg = .rdi } }, .{ .type = .usize, .kind = .{ .reg = .rsi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatuneitf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatuneitf" } }, .unused, .unused, .unused, @@ -116833,7 +116781,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, .{ .type = .i32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatsitf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatsitf" } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -116866,7 +116814,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, .{ .type = .i32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatsitf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatsitf" } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -116899,7 +116847,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, .{ .type = .i32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatsitf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatsitf" } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -116932,7 +116880,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, .{ .type = .i32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatsitf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatsitf" } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -116965,7 +116913,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, .{ .type = .i32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatsitf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatsitf" } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -116998,7 +116946,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, .{ .type = .i32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatsitf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatsitf" } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -117031,7 +116979,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatunsitf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatunsitf" } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -117064,7 +117012,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatunsitf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatunsitf" } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -117097,7 +117045,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatunsitf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatunsitf" } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -117130,7 +117078,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatunsitf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatunsitf" } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -117163,7 +117111,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatunsitf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatunsitf" } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -117196,7 +117144,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatunsitf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatunsitf" } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -117228,7 +117176,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .i32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatsitf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatsitf" } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -117259,7 +117207,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .i32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatsitf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatsitf" } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -117290,7 +117238,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .i32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatsitf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatsitf" } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -117321,7 +117269,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatunsitf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatunsitf" } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -117352,7 +117300,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatunsitf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatunsitf" } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -117383,7 +117331,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatunsitf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatunsitf" } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -117414,7 +117362,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .i32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatsitf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatsitf" } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -117445,7 +117393,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .i32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatsitf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatsitf" } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -117476,7 +117424,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .i32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatsitf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatsitf" } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -117507,7 +117455,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatunsitf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatunsitf" } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -117538,7 +117486,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatunsitf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatunsitf" } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -117569,7 +117517,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u32, .kind = .{ .reg = .edi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatunsitf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatunsitf" } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -117600,7 +117548,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .i64, .kind = .{ .reg = .rdi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatditf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatditf" } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -117631,7 +117579,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .i64, .kind = .{ .reg = .rdi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatditf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatditf" } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -117662,7 +117610,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .i64, .kind = .{ .reg = .rdi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatditf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatditf" } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -117693,7 +117641,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u64, .kind = .{ .reg = .rdi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatunditf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatunditf" } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -117724,7 +117672,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u64, .kind = .{ .reg = .rdi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatunditf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatunditf" } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -117755,7 +117703,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u64, .kind = .{ .reg = .rdi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatunditf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatunditf" } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -117787,7 +117735,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u64, .kind = .{ .reg = .rdi } }, .{ .type = .i64, .kind = .{ .reg = .rsi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floattitf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floattitf" } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -117819,7 +117767,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u64, .kind = .{ .reg = .rdi } }, .{ .type = .i64, .kind = .{ .reg = .rsi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floattitf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floattitf" } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -117851,7 +117799,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u64, .kind = .{ .reg = .rdi } }, .{ .type = .i64, .kind = .{ .reg = .rsi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floattitf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floattitf" } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -117883,7 +117831,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u64, .kind = .{ .reg = .rdi } }, .{ .type = .u64, .kind = .{ .reg = .rsi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatuntitf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatuntitf" } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -117915,7 +117863,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u64, .kind = .{ .reg = .rdi } }, .{ .type = .u64, .kind = .{ .reg = .rsi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatuntitf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatuntitf" } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -117947,7 +117895,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .u64, .kind = .{ .reg = .rdi } }, .{ .type = .u64, .kind = .{ .reg = .rsi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatuntitf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatuntitf" } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -117980,7 +117928,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .usize, .kind = .{ .reg = .rdi } }, .{ .type = .usize, .kind = .{ .reg = .rsi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floateitf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floateitf" } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -118014,7 +117962,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .usize, .kind = .{ .reg = .rdi } }, .{ .type = .usize, .kind = .{ .reg = .rsi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floateitf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floateitf" } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -118048,7 +117996,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .usize, .kind = .{ .reg = .rdi } }, .{ .type = .usize, .kind = .{ .reg = .rsi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floateitf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floateitf" } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -118082,7 +118030,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .usize, .kind = .{ .reg = .rdi } }, .{ .type = .usize, .kind = .{ .reg = .rsi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatuneitf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatuneitf" } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -118116,7 +118064,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .usize, .kind = .{ .reg = .rdi } }, .{ .type = .usize, .kind = .{ .reg = .rsi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatuneitf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatuneitf" } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -118150,7 +118098,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .usize, .kind = .{ .reg = .rdi } }, .{ .type = .usize, .kind = .{ .reg = .rsi } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__floatuneitf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__floatuneitf" } }, .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .unused, .unused, @@ -131865,7 +131813,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fminh" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fminh" } }, .unused, .unused, .unused, @@ -131898,7 +131846,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fminh" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fminh" } }, .unused, .unused, .unused, @@ -131933,7 +131881,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .f16, .kind = .{ .reg = .ax } }, .{ .type = .f32, .kind = .mem }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fminh" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fminh" } }, .unused, .unused, .unused, @@ -133533,7 +133481,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "fminq" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "fminq" } }, .unused, .unused, .unused, @@ -133564,7 +133512,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "fminq" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "fminq" } }, .unused, .unused, .unused, @@ -133595,7 +133543,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "fminq" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "fminq" } }, .unused, .unused, .unused, @@ -141985,7 +141933,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fmaxh" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fmaxh" } }, .unused, .unused, .unused, @@ -142018,7 +141966,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fmaxh" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fmaxh" } }, .unused, .unused, .unused, @@ -142053,7 +142001,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .f16, .kind = .{ .reg = .ax } }, .{ .type = .f32, .kind = .mem }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fmaxh" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fmaxh" } }, .unused, .unused, .unused, @@ -143661,7 +143609,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "fmaxq" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "fmaxq" } }, .unused, .unused, .unused, @@ -143692,7 +143640,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "fmaxq" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "fmaxq" } }, .unused, .unused, .unused, @@ -143723,7 +143671,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "fmaxq" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "fmaxq" } }, .unused, .unused, .unused, @@ -147656,7 +147604,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__addhf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__addhf3" } }, .unused, .unused, .unused, @@ -147689,7 +147637,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__addhf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__addhf3" } }, .unused, .unused, .unused, @@ -147724,7 +147672,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .f16, .kind = .{ .reg = .ax } }, .{ .type = .f32, .kind = .mem }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__addhf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__addhf3" } }, .unused, .unused, .unused, @@ -148241,7 +148189,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__addtf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__addtf3" } }, .unused, .unused, .unused, @@ -148272,7 +148220,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__addtf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__addtf3" } }, .unused, .unused, .unused, @@ -148303,7 +148251,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__addtf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__addtf3" } }, .unused, .unused, .unused, @@ -151315,7 +151263,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__mulhf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__mulhf3" } }, .unused, .unused, .unused, @@ -151348,7 +151296,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__mulhf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__mulhf3" } }, .unused, .unused, .unused, @@ -151383,7 +151331,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .f16, .kind = .{ .reg = .ax } }, .{ .type = .f32, .kind = .mem }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__mulhf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__mulhf3" } }, .unused, .unused, .unused, @@ -151780,7 +151728,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__multf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__multf3" } }, .unused, .unused, .unused, @@ -151811,7 +151759,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__multf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__multf3" } }, .unused, .unused, .unused, @@ -151842,7 +151790,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__multf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__multf3" } }, .unused, .unused, .unused, @@ -152354,7 +152302,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fminh" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fminh" } }, .unused, .unused, .unused, @@ -152387,7 +152335,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fminh" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fminh" } }, .unused, .unused, .unused, @@ -152422,7 +152370,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .f16, .kind = .{ .reg = .ax } }, .{ .type = .f32, .kind = .mem }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fminh" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fminh" } }, .unused, .unused, .unused, @@ -153502,7 +153450,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "fminq" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "fminq" } }, .unused, .unused, .unused, @@ -153533,7 +153481,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "fminq" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "fminq" } }, .unused, .unused, .unused, @@ -153564,7 +153512,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "fminq" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "fminq" } }, .unused, .unused, .unused, @@ -154046,7 +153994,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fmaxh" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fmaxh" } }, .unused, .unused, .unused, @@ -154079,7 +154027,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fmaxh" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fmaxh" } }, .unused, .unused, .unused, @@ -154114,7 +154062,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .f16, .kind = .{ .reg = .ax } }, .{ .type = .f32, .kind = .mem }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fmaxh" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fmaxh" } }, .unused, .unused, .unused, @@ -155194,7 +155142,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "fmaxq" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "fmaxq" } }, .unused, .unused, .unused, @@ -155225,7 +155173,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "fmaxq" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "fmaxq" } }, .unused, .unused, .unused, @@ -155256,7 +155204,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "fmaxq" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "fmaxq" } }, .unused, .unused, .unused, @@ -155987,7 +155935,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__addhf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__addhf3" } }, .unused, .unused, .unused, @@ -156020,7 +155968,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__addhf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__addhf3" } }, .unused, .unused, .unused, @@ -156055,7 +156003,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .f16, .kind = .{ .reg = .ax } }, .{ .type = .f32, .kind = .mem }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__addhf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__addhf3" } }, .unused, .unused, .unused, @@ -157453,7 +157401,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__addtf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__addtf3" } }, .unused, .unused, .unused, @@ -157484,7 +157432,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__addtf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__addtf3" } }, .unused, .unused, .unused, @@ -157515,7 +157463,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__addtf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__addtf3" } }, .unused, .unused, .unused, @@ -157997,7 +157945,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__mulhf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__mulhf3" } }, .unused, .unused, .unused, @@ -158030,7 +157978,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__mulhf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__mulhf3" } }, .unused, .unused, .unused, @@ -158065,7 +158013,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .f16, .kind = .{ .reg = .ax } }, .{ .type = .f32, .kind = .mem }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__mulhf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__mulhf3" } }, .unused, .unused, .unused, @@ -158996,7 +158944,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__multf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__multf3" } }, .unused, .unused, .unused, @@ -159027,7 +158975,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__multf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__multf3" } }, .unused, .unused, .unused, @@ -159058,7 +159006,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__multf3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__multf3" } }, .unused, .unused, .unused, @@ -160939,7 +160887,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = @tagName(symbol) } } }, + .{ .type = .usize, .kind = .{ .extern_func = @tagName(symbol) } }, .unused, .unused, .unused, @@ -160988,7 +160936,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"32" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .lazy_symbol = .{ .kind = .code, .ref = .src0 } } }, + .{ .type = .usize, .kind = .{ .lazy_sym = .{ .kind = .code, .ref = .src0 } } }, .unused, .unused, .unused, @@ -161014,7 +160962,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .lazy_symbol = .{ .kind = .code, .ref = .src0 } } }, + .{ .type = .usize, .kind = .{ .lazy_sym = .{ .kind = .code, .ref = .src0 } } }, .unused, .unused, .unused, @@ -161039,7 +160987,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"8" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .lazy_symbol = .{ .kind = .code, .ref = .src0 } } }, + .{ .type = .usize, .kind = .{ .lazy_sym = .{ .kind = .code, .ref = .src0 } } }, .unused, .unused, .unused, @@ -161079,7 +161027,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"32" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .lazy_symbol = .{ .kind = .code, .ref = .src0 } } }, + .{ .type = .usize, .kind = .{ .lazy_sym = .{ .kind = .code, .ref = .src0 } } }, .unused, .unused, .unused, @@ -161104,7 +161052,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .lazy_symbol = .{ .kind = .code, .ref = .src0 } } }, + .{ .type = .usize, .kind = .{ .lazy_sym = .{ .kind = .code, .ref = .src0 } } }, .unused, .unused, .unused, @@ -161128,7 +161076,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"8" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .lazy_symbol = .{ .kind = .code, .ref = .src0 } } }, + .{ .type = .usize, .kind = .{ .lazy_sym = .{ .kind = .code, .ref = .src0 } } }, .unused, .unused, .unused, @@ -161167,7 +161115,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .src = .{ .to_gpr, .none, .none } }, }, .extra_temps = .{ - .{ .type = .anyerror, .kind = .{ .lazy_symbol = .{ .kind = .const_data } } }, + .{ .type = .anyerror, .kind = .{ .lazy_sym = .{ .kind = .const_data } } }, .{ .type = .u32, .kind = .{ .mut_rc = .{ .ref = .src0, .rc = .general_purpose } } }, .unused, .unused, @@ -161196,7 +161144,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .src = .{ .to_gpr, .none, .none } }, }, .extra_temps = .{ - .{ .type = .anyerror, .kind = .{ .lazy_symbol = .{ .kind = .const_data } } }, + .{ .type = .anyerror, .kind = .{ .lazy_sym = .{ .kind = .const_data } } }, .{ .type = .u32, .kind = .{ .mut_rc = .{ .ref = .src0, .rc = .general_purpose } } }, .unused, .unused, @@ -161225,7 +161173,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .src = .{ .to_gpr, .none, .none } }, }, .extra_temps = .{ - .{ .type = .anyerror, .kind = .{ .lazy_symbol = .{ .kind = .const_data } } }, + .{ .type = .anyerror, .kind = .{ .lazy_sym = .{ .kind = .const_data } } }, .{ .type = .u32, .kind = .{ .mut_rc = .{ .ref = .src0, .rc = .general_purpose } } }, .unused, .unused, @@ -161276,7 +161224,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"32" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .lazy_symbol = .{ .kind = .code, .ref = .src1 } } }, + .{ .type = .usize, .kind = .{ .lazy_sym = .{ .kind = .code, .ref = .src1 } } }, .unused, .unused, .unused, @@ -161302,7 +161250,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .lazy_symbol = .{ .kind = .code, .ref = .src1 } } }, + .{ .type = .usize, .kind = .{ .lazy_sym = .{ .kind = .code, .ref = .src1 } } }, .unused, .unused, .unused, @@ -161327,7 +161275,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"8" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .lazy_symbol = .{ .kind = .code, .ref = .src1 } } }, + .{ .type = .usize, .kind = .{ .lazy_sym = .{ .kind = .code, .ref = .src1 } } }, .unused, .unused, .unused, @@ -161523,7 +161471,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fmah" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fmah" } }, .unused, .unused, .unused, @@ -161666,7 +161614,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, .{ .type = .f16, .kind = .{ .reg = .xmm2 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fmah" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fmah" } }, .unused, .unused, .unused, @@ -161703,7 +161651,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, .{ .type = .f16, .kind = .{ .reg = .xmm2 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fmah" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fmah" } }, .unused, .unused, .unused, @@ -161742,7 +161690,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, .{ .type = .f16, .kind = .{ .reg = .xmm2 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fmah" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fmah" } }, .{ .type = .f16, .kind = .{ .reg = .ax } }, .unused, .unused, @@ -161784,7 +161732,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, .{ .type = .f16, .kind = .{ .reg = .xmm2 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fmah" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fmah" } }, .unused, .unused, .unused, @@ -161872,7 +161820,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "fmaf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "fmaf" } }, .unused, .unused, .unused, @@ -162038,7 +161986,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .f32, .kind = .{ .reg = .xmm0 } }, .{ .type = .f32, .kind = .{ .reg = .xmm1 } }, .{ .type = .f32, .kind = .{ .reg = .xmm2 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "fmaf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "fmaf" } }, .unused, .unused, .unused, @@ -162074,7 +162022,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .f32, .kind = .{ .reg = .xmm0 } }, .{ .type = .f32, .kind = .{ .reg = .xmm1 } }, .{ .type = .f32, .kind = .{ .reg = .xmm2 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "fmaf" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "fmaf" } }, .unused, .unused, .unused, @@ -162156,7 +162104,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "fma" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "fma" } }, .unused, .unused, .unused, @@ -162322,7 +162270,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, .{ .type = .f64, .kind = .{ .reg = .xmm1 } }, .{ .type = .f64, .kind = .{ .reg = .xmm2 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "fma" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "fma" } }, .unused, .unused, .unused, @@ -162358,7 +162306,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, .{ .type = .f64, .kind = .{ .reg = .xmm1 } }, .{ .type = .f64, .kind = .{ .reg = .xmm2 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "fma" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "fma" } }, .unused, .unused, .unused, @@ -162394,7 +162342,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, .{ .type = .f64, .kind = .{ .reg = .xmm1 } }, .{ .type = .f64, .kind = .{ .reg = .xmm2 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "fma" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "fma" } }, .unused, .unused, .unused, @@ -162431,7 +162379,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .extra_temps = .{ .{ .type = .f80, .kind = .{ .reg = .xmm0 } }, .{ .type = .f80, .kind = .{ .frame = .call_frame } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fmax" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fmax" } }, .unused, .unused, .unused, @@ -162467,7 +162415,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .f80, .kind = .{ .reg = .xmm0 } }, .{ .type = .f80, .kind = .{ .frame = .call_frame } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fmax" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__fmax" } }, .unused, .unused, .unused, @@ -162504,7 +162452,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "fmaq" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "fmaq" } }, .unused, .unused, .unused, @@ -162537,7 +162485,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, .{ .type = .f128, .kind = .{ .reg = .xmm2 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "fmaq" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "fmaq" } }, .unused, .unused, .unused, @@ -162573,7 +162521,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, .{ .type = .f128, .kind = .{ .reg = .xmm2 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "fmaq" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "fmaq" } }, .unused, .unused, .unused, @@ -162609,7 +162557,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, .{ .type = .f128, .kind = .{ .reg = .xmm2 } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "fmaq" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "fmaq" } }, .unused, .unused, .unused, @@ -162663,7 +162611,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .src = .{ .to_gpr, .none, .none } }, }, .extra_temps = .{ - .{ .type = .anyerror, .kind = .{ .lazy_symbol = .{ .kind = .const_data } } }, + .{ .type = .anyerror, .kind = .{ .lazy_sym = .{ .kind = .const_data } } }, .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, .unused, .unused, @@ -162687,7 +162635,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .src = .{ .to_gpr, .none, .none } }, }, .extra_temps = .{ - .{ .type = .anyerror, .kind = .{ .lazy_symbol = .{ .kind = .const_data } } }, + .{ .type = .anyerror, .kind = .{ .lazy_sym = .{ .kind = .const_data } } }, .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, .unused, .unused, @@ -162711,7 +162659,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .src = .{ .to_gpr, .none, .none } }, }, .extra_temps = .{ - .{ .type = .anyerror, .kind = .{ .lazy_symbol = .{ .kind = .const_data } } }, + .{ .type = .anyerror, .kind = .{ .lazy_sym = .{ .kind = .const_data } } }, .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, .unused, .unused, @@ -163402,65 +163350,17 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }; for (ops) |op| try op.die(cg); }, - .runtime_nav_ptr => switch (cg.bin_file.tag) { - .elf, .macho => { - const ty_nav = air_datas[@intFromEnum(inst)].ty_nav; - - const nav = ip.getNav(ty_nav.nav); - const sym_index, const relocation = sym: { - if (cg.bin_file.cast(.elf)) |elf_file| { - const zo = elf_file.zigObjectPtr().?; - if (nav.getExtern(ip)) |e| { - const sym = try elf_file.getGlobalSymbol(nav.name.toSlice(ip), e.lib_name.toSlice(ip)); - linkage: switch (e.linkage) { - .internal => {}, - .strong => switch (e.visibility) { - .default => zo.symbol(sym).flags.is_extern_ptr = true, - .hidden, .protected => {}, - }, - .weak => { - zo.symbol(sym).flags.weak = true; - continue :linkage .strong; - }, - .link_once => unreachable, - } - break :sym .{ sym, e.relocation }; - } else break :sym .{ try zo.getOrCreateMetadataForNav(zcu, ty_nav.nav), .any }; - } else if (cg.bin_file.cast(.macho)) |macho_file| { - const zo = macho_file.getZigObject().?; - if (nav.getExtern(ip)) |e| { - const sym = try macho_file.getGlobalSymbol(nav.name.toSlice(ip), e.lib_name.toSlice(ip)); - linkage: switch (e.linkage) { - .internal => {}, - .strong => switch (e.visibility) { - .default => zo.symbols.items[sym].flags.is_extern_ptr = true, - .hidden, .protected => {}, - }, - .weak => { - zo.symbols.items[sym].flags.weak = true; - continue :linkage .strong; - }, - .link_once => unreachable, - } - break :sym .{ sym, e.relocation }; - } else break :sym .{ try zo.getOrCreateMetadataForNav(macho_file, ty_nav.nav), .any }; - } else unreachable; - }; - - if (cg.mod.pic) { - try cg.spillRegisters(&.{ .rdi, .rax }); - } else { - try cg.spillRegisters(&.{.rax}); - } - - var slot = try cg.tempInit(.usize, switch (relocation) { - .any => .{ .lea_symbol = .{ .sym_index = sym_index } }, - .pcrel => .{ .lea_pcrel = .{ .sym_index = sym_index } }, - }); - while (try slot.toRegClass(true, .general_purpose, cg)) {} - try slot.finish(inst, &.{}, &.{}, cg); - }, - else => return cg.fail("TODO implement runtime_nav_ptr on {}", .{cg.bin_file.tag}), + .runtime_nav_ptr => { + const ty_nav = air_datas[@intFromEnum(inst)].ty_nav; + const is_threadlocal = ip.getNav(ty_nav.nav).isThreadlocal(ip); + if (is_threadlocal) if (cg.mod.pic) { + try cg.spillRegisters(&.{ .rdi, .rax }); + } else { + try cg.spillRegisters(&.{.rax}); + }; + var res = try cg.tempInit(.fromInterned(ty_nav.ty), .{ .lea_nav = ty_nav.nav }); + if (is_threadlocal) while (try res.toRegClass(true, .general_purpose, cg)) {}; + try res.finish(inst, &.{}, &.{}, cg); }, .c_va_arg => try cg.airVaArg(inst), .c_va_copy => try cg.airVaCopy(inst), @@ -164116,11 +164016,11 @@ fn airFptrunc(self: *CodeGen, inst: Air.Inst.Index) !void { }, else => unreachable, }) { - var callee_buf: ["__trunc?f?f2".len]u8 = undefined; - break :result try self.genCall(.{ .lib = .{ + var sym_buf: ["__trunc?f?f2".len]u8 = undefined; + break :result try self.genCall(.{ .extern_func = .{ .return_type = self.floatCompilerRtAbiType(dst_ty, src_ty).toIntern(), .param_types = &.{self.floatCompilerRtAbiType(src_ty, dst_ty).toIntern()}, - .callee = std.fmt.bufPrint(&callee_buf, "__trunc{c}f{c}f2", .{ + .sym = std.fmt.bufPrint(&sym_buf, "__trunc{c}f{c}f2", .{ floatCompilerRtAbiName(src_bits), floatCompilerRtAbiName(dst_bits), }) catch unreachable, @@ -164220,11 +164120,11 @@ fn airFpext(self: *CodeGen, inst: Air.Inst.Index) !void { else => unreachable, }) { if (dst_ty.isVector(zcu)) break :result null; - var callee_buf: ["__extend?f?f2".len]u8 = undefined; - break :result try self.genCall(.{ .lib = .{ + var sym_buf: ["__extend?f?f2".len]u8 = undefined; + break :result try self.genCall(.{ .extern_func = .{ .return_type = self.floatCompilerRtAbiType(dst_scalar_ty, src_scalar_ty).toIntern(), .param_types = &.{self.floatCompilerRtAbiType(src_scalar_ty, dst_scalar_ty).toIntern()}, - .callee = std.fmt.bufPrint(&callee_buf, "__extend{c}f{c}f2", .{ + .sym = std.fmt.bufPrint(&sym_buf, "__extend{c}f{c}f2", .{ floatCompilerRtAbiName(src_bits), floatCompilerRtAbiName(dst_bits), }) catch unreachable, @@ -164661,7 +164561,7 @@ fn airTrunc(self: *CodeGen, inst: Air.Inst.Index) !void { .storage = .{ .repeated_elem = mask_val.ip_index }, } }); - const splat_mcv = try self.genTypedValue(.fromInterned(splat_val)); + const splat_mcv = try self.lowerValue(.fromInterned(splat_val)); const splat_addr_mcv: MCValue = switch (splat_mcv) { .memory, .indirect, .load_frame => splat_mcv.address(), else => .{ .register = try self.copyToTmpRegister(.usize, splat_mcv.address()) }, @@ -164860,7 +164760,7 @@ fn airMulDivBinOp(self: *CodeGen, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void .mul, .mul_wrap => {}, .div_trunc, .div_floor, .div_exact, .rem, .mod => { const signed = dst_ty.isSignedInt(zcu); - var callee_buf: ["__udiv?i3".len]u8 = undefined; + var sym_buf: ["__udiv?i3".len]u8 = undefined; const signed_div_floor_state: struct { frame_index: FrameIndex, state: State, @@ -164879,7 +164779,7 @@ fn airMulDivBinOp(self: *CodeGen, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void const lhs_mcv = try self.resolveInst(bin_op.lhs); const mat_lhs_mcv = switch (lhs_mcv) { - .load_symbol => mat_lhs_mcv: { + .load_nav, .load_uav, .load_lazy_sym => mat_lhs_mcv: { // TODO clean this up! const addr_reg = try self.copyToTmpRegister(.usize, lhs_mcv.address()); break :mat_lhs_mcv MCValue{ .indirect = .{ .reg = addr_reg } }; @@ -164903,7 +164803,7 @@ fn airMulDivBinOp(self: *CodeGen, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void const rhs_mcv = try self.resolveInst(bin_op.rhs); const mat_rhs_mcv = switch (rhs_mcv) { - .load_symbol => mat_rhs_mcv: { + .load_nav, .load_uav, .load_lazy_sym => mat_rhs_mcv: { // TODO clean this up! const addr_reg = try self.copyToTmpRegister(.usize, rhs_mcv.address()); break :mat_rhs_mcv MCValue{ .indirect = .{ .reg = addr_reg } }; @@ -164930,10 +164830,10 @@ fn airMulDivBinOp(self: *CodeGen, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void break :state .{ .frame_index = frame_index, .state = state, .reloc = reloc }; } else undefined; const call_mcv = try self.genCall( - .{ .lib = .{ + .{ .extern_func = .{ .return_type = dst_ty.toIntern(), .param_types = &.{ src_ty.toIntern(), src_ty.toIntern() }, - .callee = std.fmt.bufPrint(&callee_buf, "__{s}{s}{c}i3", .{ + .sym = std.fmt.bufPrint(&sym_buf, "__{s}{s}{c}i3", .{ if (signed) "" else "u", switch (tag) { .div_trunc, .div_exact => "div", @@ -164967,10 +164867,10 @@ fn airMulDivBinOp(self: *CodeGen, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void }); self.performReloc(signed_div_floor_state.reloc); const dst_mcv = try self.genCall( - .{ .lib = .{ + .{ .extern_func = .{ .return_type = dst_ty.toIntern(), .param_types = &.{ src_ty.toIntern(), src_ty.toIntern() }, - .callee = std.fmt.bufPrint(&callee_buf, "__div{c}i3", .{ + .sym = std.fmt.bufPrint(&sym_buf, "__div{c}i3", .{ intCompilerRtAbiName(@intCast(dst_ty.bitSize(zcu))), }) catch unreachable, } }, @@ -165004,7 +164904,7 @@ fn airMulDivBinOp(self: *CodeGen, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void const rhs_mcv = try self.resolveInst(bin_op.rhs); const mat_rhs_mcv = switch (rhs_mcv) { - .load_symbol => mat_rhs_mcv: { + .load_nav, .load_uav, .load_lazy_sym => mat_rhs_mcv: { // TODO clean this up! const addr_reg = try self.copyToTmpRegister(.usize, rhs_mcv.address()); break :mat_rhs_mcv MCValue{ .indirect = .{ .reg = addr_reg } }; @@ -165218,10 +165118,10 @@ fn airMulSat(self: *CodeGen, inst: Air.Inst.Index) !void { const ptr_c_int = try pt.singleMutPtrType(.c_int); const overflow = try self.allocTempRegOrMem(.c_int, false); - const dst_mcv = try self.genCall(.{ .lib = .{ + const dst_mcv = try self.genCall(.{ .extern_func = .{ .return_type = .i128_type, .param_types = &.{ .i128_type, .i128_type, ptr_c_int.toIntern() }, - .callee = "__muloti4", + .sym = "__muloti4", } }, &.{ .i128, .i128, ptr_c_int }, &.{ .{ .air_ref = bin_op.lhs }, .{ .air_ref = bin_op.rhs }, @@ -165236,7 +165136,7 @@ fn airMulSat(self: *CodeGen, inst: Air.Inst.Index) !void { const lhs_mcv = try self.resolveInst(bin_op.lhs); const mat_lhs_mcv = switch (lhs_mcv) { - .load_symbol => mat_lhs_mcv: { + .load_nav, .load_uav, .load_lazy_sym => mat_lhs_mcv: { // TODO clean this up! const addr_reg = try self.copyToTmpRegister(.usize, lhs_mcv.address()); break :mat_lhs_mcv MCValue{ .indirect = .{ .reg = addr_reg } }; @@ -165260,7 +165160,7 @@ fn airMulSat(self: *CodeGen, inst: Air.Inst.Index) !void { const rhs_mcv = try self.resolveInst(bin_op.rhs); const mat_rhs_mcv = switch (rhs_mcv) { - .load_symbol => mat_rhs_mcv: { + .load_nav, .load_uav, .load_lazy_sym => mat_rhs_mcv: { // TODO clean this up! const addr_reg = try self.copyToTmpRegister(.usize, rhs_mcv.address()); break :mat_rhs_mcv MCValue{ .indirect = .{ .reg = addr_reg } }; @@ -165734,10 +165634,10 @@ fn airMulWithOverflow(self: *CodeGen, inst: Air.Inst.Index) !void { .signed => { const ptr_c_int = try pt.singleMutPtrType(.c_int); const overflow = try self.allocTempRegOrMem(.c_int, false); - const result = try self.genCall(.{ .lib = .{ + const result = try self.genCall(.{ .extern_func = .{ .return_type = .i128_type, .param_types = &.{ .i128_type, .i128_type, ptr_c_int.toIntern() }, - .callee = "__muloti4", + .sym = "__muloti4", } }, &.{ .i128, .i128, ptr_c_int }, &.{ .{ .air_ref = bin_op.lhs }, .{ .air_ref = bin_op.rhs }, @@ -165791,7 +165691,7 @@ fn airMulWithOverflow(self: *CodeGen, inst: Air.Inst.Index) !void { break :mat_lhs_mcv mat_lhs_mcv; }, }, - .load_symbol => { + .load_nav, .load_uav, .load_lazy_sym => { // TODO clean this up! const addr_reg = try self.copyToTmpRegister(.usize, lhs_mcv.address()); break :mat_lhs_mcv MCValue{ .indirect = .{ .reg = addr_reg } }; @@ -165815,7 +165715,7 @@ fn airMulWithOverflow(self: *CodeGen, inst: Air.Inst.Index) !void { break :mat_rhs_mcv mat_rhs_mcv; }, }, - .load_symbol => { + .load_nav, .load_uav, .load_lazy_sym => { // TODO clean this up! const addr_reg = try self.copyToTmpRegister(.usize, rhs_mcv.address()); break :mat_rhs_mcv MCValue{ .indirect = .{ .reg = addr_reg } }; @@ -166291,7 +166191,7 @@ fn airShlShrBinOp(self: *CodeGen, inst: Air.Inst.Index) !void { defer self.register_manager.unlockReg(shift_lock); const mask_ty = try pt.vectorType(.{ .len = 16, .child = .u8_type }); - const mask_mcv = try self.genTypedValue(.fromInterned(try pt.intern(.{ .aggregate = .{ + const mask_mcv = try self.lowerValue(.fromInterned(try pt.intern(.{ .aggregate = .{ .ty = mask_ty.toIntern(), .storage = .{ .elems = &([1]InternPool.Index{ (try rhs_ty.childType(zcu).maxIntScalar(pt, .u8)).toIntern(), @@ -166432,7 +166332,7 @@ fn airShlSat(self: *CodeGen, inst: Air.Inst.Index) !void { // if lhs is negative, it is min switch (lhs_ty.intInfo(zcu).signedness) { .unsigned => { - const bound_mcv = try self.genTypedValue(try lhs_ty.maxIntScalar(self.pt, lhs_ty)); + const bound_mcv = try self.lowerValue(try lhs_ty.maxIntScalar(self.pt, lhs_ty)); try self.genCopy(lhs_ty, dst_mcv, bound_mcv, .{}); }, .signed => { @@ -166441,7 +166341,7 @@ fn airShlSat(self: *CodeGen, inst: Air.Inst.Index) !void { // we only need the highest bit so shifting the highest part of lhs_mcv // is enough to check the signedness. other parts can be skipped here. var lhs_temp2 = try self.tempInit(lhs_ty, lhs_mcv); - var zero_temp = try self.tempInit(lhs_ty, try self.genTypedValue(try self.pt.intValue(lhs_ty, 0))); + var zero_temp = try self.tempInit(lhs_ty, try self.lowerValue(try self.pt.intValue(lhs_ty, 0))); const sign_cc_temp = lhs_temp2.cmpInts(.lt, &zero_temp, self) catch |err| switch (err) { error.SelectFailed => unreachable, else => |e| return e, @@ -166452,13 +166352,13 @@ fn airShlSat(self: *CodeGen, inst: Air.Inst.Index) !void { try sign_cc_temp.die(self); // if it is negative - const min_mcv = try self.genTypedValue(try lhs_ty.minIntScalar(self.pt, lhs_ty)); + const min_mcv = try self.lowerValue(try lhs_ty.minIntScalar(self.pt, lhs_ty)); try self.genCopy(lhs_ty, dst_mcv, min_mcv, .{}); const sign_reloc_br = try self.asmJmpReloc(undefined); self.performReloc(sign_reloc_condbr); // if it is positive - const max_mcv = try self.genTypedValue(try lhs_ty.maxIntScalar(self.pt, lhs_ty)); + const max_mcv = try self.lowerValue(try lhs_ty.maxIntScalar(self.pt, lhs_ty)); try self.genCopy(lhs_ty, dst_mcv, max_mcv, .{}); self.performReloc(sign_reloc_br); }, @@ -167179,7 +167079,12 @@ fn airArrayElemVal(self: *CodeGen, inst: Air.Inst.Index) !void { }.to64(), ), }, - .memory, .load_symbol, .load_direct, .load_got => switch (index_mcv) { + .memory, + .load_nav, + .load_uav, + .load_lazy_sym, + .load_extern_func, + => switch (index_mcv) { .immediate => |index_imm| try self.asmMemoryImmediate( .{ ._, .bt }, .{ @@ -167241,11 +167146,15 @@ fn airArrayElemVal(self: *CodeGen, inst: Air.Inst.Index) !void { }, ), .memory, - .load_symbol, - .load_direct, - .load_got, + .load_nav, + .lea_nav, + .load_uav, + .lea_uav, + .load_lazy_sym, + .lea_lazy_sym, + .load_extern_func, + .lea_extern_func, => try self.genSetReg(addr_reg, .usize, array_mcv.address(), .{}), - .lea_symbol, .lea_direct => unreachable, else => return self.fail("TODO airArrayElemVal_val for {s} of {}", .{ @tagName(array_mcv), array_ty.fmt(pt), }), @@ -168346,7 +168255,7 @@ fn floatSign(self: *CodeGen, inst: Air.Inst.Index, tag: Air.Inst.Tag, operand: A .child = (try pt.intType(.signed, scalar_bits)).ip_index, }); - const sign_mcv = try self.genTypedValue(switch (tag) { + const sign_mcv = try self.lowerValue(switch (tag) { .neg => try vec_ty.minInt(pt, vec_ty), .abs => try vec_ty.maxInt(pt, vec_ty), else => unreachable, @@ -168488,11 +168397,11 @@ fn genRoundLibcall(self: *CodeGen, ty: Type, src_mcv: MCValue, mode: bits.RoundM if (ty.zigTypeTag(zcu) != .float) return self.fail("TODO implement genRound for {}", .{ty.fmt(pt)}); - var callee_buf: ["__trunc?".len]u8 = undefined; - return try self.genCall(.{ .lib = .{ + var sym_buf: ["__trunc?".len]u8 = undefined; + return try self.genCall(.{ .extern_func = .{ .return_type = ty.toIntern(), .param_types = &.{ty.toIntern()}, - .callee = std.fmt.bufPrint(&callee_buf, "{s}{s}{s}", .{ + .sym = std.fmt.bufPrint(&sym_buf, "{s}{s}{s}", .{ floatLibcAbiPrefix(ty), switch (mode.direction) { .down => "floor", @@ -168765,11 +168674,11 @@ fn airSqrt(self: *CodeGen, inst: Air.Inst.Index) !void { 80, 128 => true, else => unreachable, }) { - var callee_buf: ["__sqrt?".len]u8 = undefined; - break :result try self.genCall(.{ .lib = .{ + var sym_buf: ["__sqrt?".len]u8 = undefined; + break :result try self.genCall(.{ .extern_func = .{ .return_type = ty.toIntern(), .param_types = &.{ty.toIntern()}, - .callee = std.fmt.bufPrint(&callee_buf, "{s}sqrt{s}", .{ + .sym = std.fmt.bufPrint(&sym_buf, "{s}sqrt{s}", .{ floatLibcAbiPrefix(ty), floatLibcAbiSuffix(ty), }) catch unreachable, @@ -168918,11 +168827,11 @@ fn airSqrt(self: *CodeGen, inst: Air.Inst.Index) !void { fn airUnaryMath(self: *CodeGen, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void { const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; const ty = self.typeOf(un_op); - var callee_buf: ["__round?".len]u8 = undefined; - const result = try self.genCall(.{ .lib = .{ + var sym_buf: ["__round?".len]u8 = undefined; + const result = try self.genCall(.{ .extern_func = .{ .return_type = ty.toIntern(), .param_types = &.{ty.toIntern()}, - .callee = std.fmt.bufPrint(&callee_buf, "{s}{s}{s}", .{ + .sym = std.fmt.bufPrint(&sym_buf, "{s}{s}{s}", .{ floatLibcAbiPrefix(ty), switch (tag) { .sin, @@ -169122,19 +169031,19 @@ fn load(self: *CodeGen, dst_mcv: MCValue, ptr_ty: Type, ptr_mcv: MCValue) InnerE .immediate, .register, .register_offset, - .lea_symbol, - .lea_pcrel, - .lea_direct, - .lea_got, .lea_frame, + .lea_nav, + .lea_uav, + .lea_lazy_sym, + .lea_extern_func, => try self.genCopy(dst_ty, dst_mcv, ptr_mcv.deref(), .{}), .memory, .indirect, - .load_symbol, - .load_pcrel, - .load_direct, - .load_got, .load_frame, + .load_nav, + .load_uav, + .load_lazy_sym, + .load_extern_func, => { const addr_reg = try self.copyToTmpRegister(ptr_ty, ptr_mcv); const addr_lock = self.register_manager.lockRegAssumeUnused(addr_reg); @@ -169342,19 +169251,19 @@ fn store( .immediate, .register, .register_offset, - .lea_symbol, - .lea_pcrel, - .lea_direct, - .lea_got, .lea_frame, + .lea_nav, + .lea_uav, + .lea_lazy_sym, + .lea_extern_func, => try self.genCopy(src_ty, ptr_mcv.deref(), src_mcv, opts), .memory, .indirect, - .load_symbol, - .load_pcrel, - .load_direct, - .load_got, .load_frame, + .load_nav, + .load_uav, + .load_lazy_sym, + .load_extern_func, => { const addr_reg = try self.copyToTmpRegister(ptr_ty, ptr_mcv); const addr_lock = self.register_manager.lockRegAssumeUnused(addr_reg); @@ -169820,18 +169729,18 @@ fn genUnOpMir(self: *CodeGen, mir_tag: Mir.Inst.FixedTag, dst_ty: Type, dst_mcv: .eflags, .register_overflow, .register_mask, - .lea_symbol, - .lea_pcrel, - .lea_direct, - .lea_got, .lea_frame, + .lea_nav, + .lea_uav, + .lea_lazy_sym, + .lea_extern_func, .elementwise_args, .reserved_frame, .air_ref, => unreachable, // unmodifiable destination .register => |dst_reg| try self.asmRegister(mir_tag, registerAlias(dst_reg, abi_size)), .register_pair, .register_triple, .register_quadruple => unreachable, // unimplemented - .memory, .load_symbol, .load_pcrel, .load_got, .load_direct => { + .memory, .load_nav, .load_uav, .load_lazy_sym, .load_extern_func => { const addr_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp); const addr_reg_lock = self.register_manager.lockRegAssumeUnused(addr_reg); defer self.register_manager.unlockReg(addr_reg_lock); @@ -170591,7 +170500,7 @@ fn genMulDivBinOp( defer for (reg_locks) |reg_lock| if (reg_lock) |lock| self.register_manager.unlockReg(lock); const mat_lhs_mcv = switch (lhs_mcv) { - .load_symbol => mat_lhs_mcv: { + .load_nav, .load_uav, .load_lazy_sym => mat_lhs_mcv: { // TODO clean this up! const addr_reg = try self.copyToTmpRegister(.usize, lhs_mcv.address()); break :mat_lhs_mcv MCValue{ .indirect = .{ .reg = addr_reg } }; @@ -170604,7 +170513,7 @@ fn genMulDivBinOp( }; defer if (mat_lhs_lock) |lock| self.register_manager.unlockReg(lock); const mat_rhs_mcv = switch (rhs_mcv) { - .load_symbol => mat_rhs_mcv: { + .load_nav, .load_uav, .load_lazy_sym => mat_rhs_mcv: { // TODO clean this up! const addr_reg = try self.copyToTmpRegister(.usize, rhs_mcv.address()); break :mat_rhs_mcv MCValue{ .indirect = .{ .reg = addr_reg } }; @@ -170772,7 +170681,7 @@ fn genMulDivBinOp( .is_const = true, }, }); - _ = try self.genCall(.{ .lib = .{ + _ = try self.genCall(.{ .extern_func = .{ .return_type = .void_type, .param_types = &.{ manyptr_u32_ty.toIntern(), @@ -170780,7 +170689,7 @@ fn genMulDivBinOp( manyptr_const_u32_ty.toIntern(), .usize_type, }, - .callee = switch (tag) { + .sym = switch (tag) { .div_trunc, .div_floor, .div_exact, @@ -171003,8 +170912,8 @@ fn genBinOp( .rem, .mod => {}, else => if (!type_needs_libcall) break :libcall, } - var callee_buf: ["__mod?f3".len]u8 = undefined; - const callee = switch (air_tag) { + var sym_buf: ["__mod?f3".len]u8 = undefined; + const sym = switch (air_tag) { .add, .sub, .mul, @@ -171012,11 +170921,11 @@ fn genBinOp( .div_trunc, .div_floor, .div_exact, - => std.fmt.bufPrint(&callee_buf, "__{s}{c}f3", .{ + => std.fmt.bufPrint(&sym_buf, "__{s}{c}f3", .{ @tagName(air_tag)[0..3], floatCompilerRtAbiName(float_bits), }), - .rem, .mod, .min, .max => std.fmt.bufPrint(&callee_buf, "{s}f{s}{s}", .{ + .rem, .mod, .min, .max => std.fmt.bufPrint(&sym_buf, "{s}f{s}{s}", .{ floatLibcAbiPrefix(lhs_ty), switch (air_tag) { .rem, .mod => "mod", @@ -171030,22 +170939,22 @@ fn genBinOp( @tagName(air_tag), lhs_ty.fmt(pt), }), } catch unreachable; - const result = try self.genCall(.{ .lib = .{ + const result = try self.genCall(.{ .extern_func = .{ .return_type = lhs_ty.toIntern(), .param_types = &.{ lhs_ty.toIntern(), rhs_ty.toIntern() }, - .callee = callee, + .sym = sym, } }, &.{ lhs_ty, rhs_ty }, &.{ .{ .air_ref = lhs_air }, .{ .air_ref = rhs_air } }, .{}); return switch (air_tag) { .mod => result: { const adjusted: MCValue = if (type_needs_libcall) adjusted: { - var add_callee_buf: ["__add?f3".len]u8 = undefined; - break :adjusted try self.genCall(.{ .lib = .{ + var add_sym_buf: ["__add?f3".len]u8 = undefined; + break :adjusted try self.genCall(.{ .extern_func = .{ .return_type = lhs_ty.toIntern(), .param_types = &.{ lhs_ty.toIntern(), rhs_ty.toIntern(), }, - .callee = std.fmt.bufPrint(&add_callee_buf, "__add{c}f3", .{ + .sym = std.fmt.bufPrint(&add_sym_buf, "__add{c}f3", .{ floatCompilerRtAbiName(float_bits), }) catch unreachable, } }, &.{ lhs_ty, rhs_ty }, &.{ result, .{ .air_ref = rhs_air } }, .{}); @@ -171144,10 +171053,10 @@ fn genBinOp( }), else => unreachable, }; - break :result try self.genCall(.{ .lib = .{ + break :result try self.genCall(.{ .extern_func = .{ .return_type = lhs_ty.toIntern(), .param_types = &.{ lhs_ty.toIntern(), rhs_ty.toIntern() }, - .callee = callee, + .sym = sym, } }, &.{ lhs_ty, rhs_ty }, &.{ adjusted, .{ .air_ref = rhs_air } }, .{}); }, .div_trunc, .div_floor => try self.genRoundLibcall(lhs_ty, result, .{ @@ -171430,13 +171339,15 @@ fn genBinOp( .immediate, .eflags, .register_offset, - .load_symbol, - .lea_symbol, - .load_direct, - .lea_direct, - .load_got, - .lea_got, .lea_frame, + .load_nav, + .lea_nav, + .load_uav, + .lea_uav, + .load_lazy_sym, + .lea_lazy_sym, + .load_extern_func, + .lea_extern_func, => true, .memory => |addr| std.math.cast(i32, @as(i64, @bitCast(addr))) == null, else => false, @@ -171489,15 +171400,15 @@ fn genBinOp( .register_offset, .register_overflow, .register_mask, - .load_symbol, - .lea_symbol, - .load_pcrel, - .lea_pcrel, - .load_direct, - .lea_direct, - .load_got, - .lea_got, .lea_frame, + .load_nav, + .lea_nav, + .load_uav, + .lea_uav, + .load_lazy_sym, + .lea_lazy_sym, + .load_extern_func, + .lea_extern_func, .elementwise_args, .reserved_frame, .air_ref, @@ -172595,7 +172506,7 @@ fn genBinOp( .cmp_neq, => { const unsigned_ty = try lhs_ty.toUnsigned(pt); - const not_mcv = try self.genTypedValue(try unsigned_ty.maxInt(pt, unsigned_ty)); + const not_mcv = try self.lowerValue(try unsigned_ty.maxInt(pt, unsigned_ty)); const not_mem: Memory = if (not_mcv.isBase()) try not_mcv.mem(self, .{ .size = .fromSize(abi_size) }) else @@ -172677,11 +172588,11 @@ fn genBinOpMir( .eflags, .register_overflow, .register_mask, - .lea_direct, - .lea_got, .lea_frame, - .lea_symbol, - .lea_pcrel, + .lea_nav, + .lea_uav, + .lea_lazy_sym, + .lea_extern_func, .elementwise_args, .reserved_frame, .air_ref, @@ -172771,16 +172682,16 @@ fn genBinOpMir( .register_offset, .memory, .indirect, - .load_symbol, - .lea_symbol, - .load_pcrel, - .lea_pcrel, - .load_direct, - .lea_direct, - .load_got, - .lea_got, .load_frame, .lea_frame, + .load_nav, + .lea_nav, + .load_uav, + .lea_uav, + .load_lazy_sym, + .lea_lazy_sym, + .load_extern_func, + .lea_extern_func, => { direct: { try self.asmRegisterMemory(mir_limb_tag, dst_alias, switch (src_mcv) { @@ -172813,10 +172724,11 @@ fn genBinOpMir( switch (src_mcv) { .eflags, .register_offset, - .lea_symbol, - .lea_direct, - .lea_got, .lea_frame, + .lea_nav, + .lea_uav, + .lea_lazy_sym, + .lea_extern_func, => { assert(off == 0); const reg = try self.copyToTmpRegister(ty, src_mcv); @@ -172828,9 +172740,10 @@ fn genBinOpMir( ); }, .memory, - .load_symbol, - .load_direct, - .load_got, + .load_nav, + .load_uav, + .load_lazy_sym, + .load_extern_func, => { const ptr_ty = try pt.singleConstPtrType(ty); const addr_reg = try self.copyToTmpRegister(ptr_ty, src_mcv.address()); @@ -172850,13 +172763,20 @@ fn genBinOpMir( } } }, - .memory, .indirect, .load_symbol, .load_pcrel, .load_got, .load_direct, .load_frame => { + .memory, + .indirect, + .load_frame, + .load_nav, + .load_uav, + .load_lazy_sym, + .load_extern_func, + => { const OpInfo = ?struct { addr_reg: Register, addr_lock: RegisterLock }; const limb_abi_size: u32 = @min(abi_size, 8); const dst_info: OpInfo = switch (dst_mcv) { else => unreachable, - .memory, .load_symbol, .load_got, .load_direct => dst: { + .memory, .load_nav, .load_uav, .load_lazy_sym, .load_extern_func => dst: { const dst_addr_reg = (try self.register_manager.allocReg(null, abi.RegisterClass.gp)).to64(); const dst_addr_lock = self.register_manager.lockRegAssumeUnused(dst_addr_reg); @@ -172892,19 +172812,24 @@ fn genBinOpMir( .register_quadruple, .register_offset, .indirect, - .lea_direct, - .lea_got, .load_frame, .lea_frame, - .lea_symbol, - .lea_pcrel, + .lea_nav, + .lea_uav, + .lea_lazy_sym, + .lea_extern_func, => null, - .memory, .load_symbol, .load_pcrel, .load_got, .load_direct => src: { + .memory, + .load_nav, + .load_uav, + .load_lazy_sym, + .load_extern_func, + => src: { switch (resolved_src_mcv) { .memory => |addr| if (std.math.cast(i32, @as(i64, @bitCast(addr))) != null and std.math.cast(i32, @as(i64, @bitCast(addr)) + abi_size - limb_abi_size) != null) break :src null, - .load_symbol, .load_got, .load_direct => {}, + .load_nav, .load_uav, .load_lazy_sym, .load_extern_func => {}, else => unreachable, } @@ -172944,9 +172869,10 @@ fn genBinOpMir( }; const dst_limb_mem: Memory = switch (dst_mcv) { .memory, - .load_symbol, - .load_got, - .load_direct, + .load_nav, + .load_uav, + .load_lazy_sym, + .load_extern_func, => .{ .base = .{ .reg = dst_info.?.addr_reg }, .mod = .{ .rm = .{ @@ -173036,16 +172962,16 @@ fn genBinOpMir( .eflags, .memory, .indirect, - .load_symbol, - .lea_symbol, - .load_pcrel, - .lea_pcrel, - .load_direct, - .lea_direct, - .load_got, - .lea_got, .load_frame, .lea_frame, + .load_nav, + .lea_nav, + .load_uav, + .lea_uav, + .load_lazy_sym, + .lea_lazy_sym, + .load_extern_func, + .lea_extern_func, => { const src_limb_mcv: MCValue = if (src_info) |info| .{ .indirect = .{ .reg = info.addr_reg, .off = off }, @@ -173055,10 +172981,11 @@ fn genBinOpMir( }, .eflags, .register_offset, - .lea_symbol, - .lea_direct, - .lea_got, .lea_frame, + .lea_nav, + .lea_uav, + .lea_lazy_sym, + .lea_extern_func, => switch (limb_i) { 0 => resolved_src_mcv, else => .{ .immediate = 0 }, @@ -173106,11 +173033,11 @@ fn genIntMulComplexOpMir(self: *CodeGen, dst_ty: Type, dst_mcv: MCValue, src_mcv .register_offset, .register_overflow, .register_mask, - .lea_symbol, - .lea_pcrel, - .lea_direct, - .lea_got, .lea_frame, + .lea_nav, + .lea_uav, + .lea_lazy_sym, + .lea_extern_func, .elementwise_args, .reserved_frame, .air_ref, @@ -173168,15 +173095,15 @@ fn genIntMulComplexOpMir(self: *CodeGen, dst_ty: Type, dst_mcv: MCValue, src_mcv }, .register_offset, .eflags, - .load_symbol, - .lea_symbol, - .load_pcrel, - .lea_pcrel, - .load_direct, - .lea_direct, - .load_got, - .lea_got, .lea_frame, + .load_nav, + .lea_nav, + .load_uav, + .lea_uav, + .load_lazy_sym, + .lea_lazy_sym, + .load_extern_func, + .lea_extern_func, => { const src_reg = try self.copyToTmpRegister(dst_ty, resolved_src_mcv); switch (abi_size) { @@ -173231,7 +173158,14 @@ fn genIntMulComplexOpMir(self: *CodeGen, dst_ty: Type, dst_mcv: MCValue, src_mcv } }, .register_pair, .register_triple, .register_quadruple => unreachable, // unimplemented - .memory, .indirect, .load_symbol, .load_pcrel, .load_direct, .load_got, .load_frame => { + .memory, + .indirect, + .load_frame, + .load_nav, + .load_uav, + .load_lazy_sym, + .load_extern_func, + => { const tmp_reg = try self.copyToTmpRegister(dst_ty, dst_mcv); const tmp_mcv = MCValue{ .register = tmp_reg }; const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); @@ -173382,15 +173316,6 @@ fn genLocalDebugInfo(cg: *CodeGen, air_tag: Air.Inst.Tag, ty: Type, mcv: MCValue }, .data = .{ .fa = frame_addr }, }), - .lea_symbol => |sym_off| try cg.addInst(.{ - .tag = .pseudo, - .ops = switch (air_tag) { - else => unreachable, - .arg, .dbg_arg_inline => .pseudo_dbg_arg_reloc, - .dbg_var_val => .pseudo_dbg_var_reloc, - }, - .data = .{ .reloc = sym_off }, - }), else => { const frame_index = try cg.allocFrameIndex(.initSpill(ty, cg.pt.zcu)); try cg.genSetMem(.{ .frame = frame_index }, 0, ty, mcv, .{}); @@ -173426,26 +173351,42 @@ fn genLocalDebugInfo(cg: *CodeGen, air_tag: Air.Inst.Tag, ty: Type, mcv: MCValue })), } }, }), - // debug info should explicitly ignore pcrel requirements - .lea_symbol, .lea_pcrel => |sym_off| try cg.addInst(.{ + .lea_nav => |nav| try cg.addInst(.{ .tag = .pseudo, .ops = .pseudo_dbg_var_m, .data = .{ .x = .{ .payload = try cg.addExtra(Mir.Memory.encode(.{ - .base = .{ .reloc = sym_off.sym_index }, - .mod = .{ .rm = .{ - .size = .qword, - .disp = sym_off.off, - } }, + .base = .{ .nav = nav }, + .mod = .{ .rm = .{ .size = .qword } }, })), } }, }), - .lea_direct, .lea_got => |sym_index| try cg.addInst(.{ + .lea_uav => |uav| try cg.addInst(.{ .tag = .pseudo, .ops = .pseudo_dbg_var_m, .data = .{ .x = .{ .payload = try cg.addExtra(Mir.Memory.encode(.{ - .base = .{ .reloc = sym_index }, + .base = .{ .uav = uav }, + .mod = .{ .rm = .{ .size = .qword } }, + })), + } }, + }), + .lea_lazy_sym => |lazy_sym| try cg.addInst(.{ + .tag = .pseudo, + .ops = .pseudo_dbg_var_m, + .data = .{ .x = .{ + .payload = try cg.addExtra(Mir.Memory.encode(.{ + .base = .{ .lazy_sym = lazy_sym }, + .mod = .{ .rm = .{ .size = .qword } }, + })), + } }, + }), + .lea_extern_func => |extern_func| try cg.addInst(.{ + .tag = .pseudo, + .ops = .pseudo_dbg_var_m, + .data = .{ .x = .{ + .payload = try cg.addExtra(Mir.Memory.encode(.{ + .base = .{ .extern_func = extern_func }, .mod = .{ .rm = .{ .size = .qword } }, })), } }, @@ -173502,11 +173443,10 @@ fn airCall(self: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModif fn genCall(self: *CodeGen, info: union(enum) { air: Air.Inst.Ref, - lib: struct { + extern_func: struct { return_type: InternPool.Index, param_types: []const InternPool.Index, - lib: ?[]const u8 = null, - callee: []const u8, + sym: []const u8, }, }, arg_types: []const Type, args: []const MCValue, opts: CopyOptions) !MCValue { const pt = self.pt; @@ -173522,9 +173462,9 @@ fn genCall(self: *CodeGen, info: union(enum) { else => unreachable, }; }, - .lib => |lib| try pt.funcType(.{ - .param_types = lib.param_types, - .return_type = lib.return_type, + .extern_func => |extern_func| try pt.funcType(.{ + .param_types = extern_func.param_types, + .return_type = extern_func.return_type, .cc = self.target.cCallingConvention().?, }), }; @@ -173753,52 +173693,9 @@ fn genCall(self: *CodeGen, info: union(enum) { else => func_key, } else func_key, }) { - .func => |func| { - if (self.bin_file.cast(.elf)) |elf_file| { - const zo = elf_file.zigObjectPtr().?; - const sym_index = try zo.getOrCreateMetadataForNav(zcu, func.owner_nav); - try self.asmImmediate(.{ ._, .call }, .rel(.{ .sym_index = sym_index })); - } else if (self.bin_file.cast(.coff)) |coff_file| { - const atom = try coff_file.getOrCreateAtomForNav(func.owner_nav); - const sym_index = coff_file.getAtom(atom).getSymbolIndex().?; - const scratch_reg = abi.getCAbiLinkerScratchReg(fn_info.cc); - try self.genSetReg(scratch_reg, .usize, .{ .lea_got = sym_index }, .{}); - try self.asmRegister(.{ ._, .call }, scratch_reg); - } else if (self.bin_file.cast(.macho)) |macho_file| { - const zo = macho_file.getZigObject().?; - const sym_index = try zo.getOrCreateMetadataForNav(macho_file, func.owner_nav); - const sym = zo.symbols.items[sym_index]; - try self.asmImmediate(.{ ._, .call }, .rel(.{ .sym_index = sym.nlist_idx })); - } else if (self.bin_file.cast(.plan9)) |p9| { - const atom_index = try p9.seeNav(pt, func.owner_nav); - const atom = p9.getAtom(atom_index); - try self.asmMemory(.{ ._, .call }, .{ - .base = .{ .reg = .ds }, - .mod = .{ .rm = .{ - .size = .qword, - .disp = @intCast(atom.getOffsetTableAddress(p9)), - } }, - }); - } else unreachable; - }, - .@"extern" => |@"extern"| if (self.bin_file.cast(.elf)) |elf_file| { - const target_sym_index = try elf_file.getGlobalSymbol( - @"extern".name.toSlice(ip), - @"extern".lib_name.toSlice(ip), - ); - try self.asmImmediate(.{ ._, .call }, .rel(.{ .sym_index = target_sym_index })); - } else if (self.bin_file.cast(.macho)) |macho_file| { - const target_sym_index = try macho_file.getGlobalSymbol( - @"extern".name.toSlice(ip), - @"extern".lib_name.toSlice(ip), - ); - try self.asmImmediate(.{ ._, .call }, .rel(.{ .sym_index = target_sym_index })); - } else try self.genExternSymbolRef( - .call, - @"extern".lib_name.toSlice(ip), - @"extern".name.toSlice(ip), - ), - else => return self.fail("TODO implement calling bitcasted functions", .{}), + else => unreachable, + .func => |func| try self.asmImmediate(.{ ._, .call }, .{ .nav = .{ .index = func.owner_nav } }), + .@"extern" => |@"extern"| try self.asmImmediate(.{ ._, .call }, .{ .nav = .{ .index = @"extern".owner_nav } }), } } else { assert(self.typeOf(callee).zigTypeTag(zcu) == .pointer); @@ -173806,13 +173703,7 @@ fn genCall(self: *CodeGen, info: union(enum) { try self.genSetReg(scratch_reg, .usize, .{ .air_ref = callee }, .{}); try self.asmRegister(.{ ._, .call }, scratch_reg); }, - .lib => |lib| if (self.bin_file.cast(.elf)) |elf_file| { - const target_sym_index = try elf_file.getGlobalSymbol(lib.callee, lib.lib); - try self.asmImmediate(.{ ._, .call }, .rel(.{ .sym_index = target_sym_index })); - } else if (self.bin_file.cast(.macho)) |macho_file| { - const target_sym_index = try macho_file.getGlobalSymbol(lib.callee, lib.lib); - try self.asmImmediate(.{ ._, .call }, .rel(.{ .sym_index = target_sym_index })); - } else try self.genExternSymbolRef(.call, lib.lib, lib.callee), + .extern_func => |extern_func| try self.asmImmediate(.{ ._, .call }, .{ .extern_func = try self.addString(extern_func.sym) }), } return call_info.return_value.short; } @@ -173946,11 +173837,11 @@ fn airCmp(self: *CodeGen, inst: Air.Inst.Index, op: std.math.CompareOperator) !v 80, 128 => false, else => unreachable, }) { - var callee_buf: ["__???f2".len]u8 = undefined; - const ret = try self.genCall(.{ .lib = .{ + var sym_buf: ["__???f2".len]u8 = undefined; + const ret = try self.genCall(.{ .extern_func = .{ .return_type = .i32_type, .param_types = &.{ ty.toIntern(), ty.toIntern() }, - .callee = std.fmt.bufPrint(&callee_buf, "__{s}{c}f2", .{ + .sym = std.fmt.bufPrint(&sym_buf, "__{s}{c}f2", .{ switch (op) { .eq => "eq", .neq => "ne", @@ -174093,17 +173984,27 @@ fn airCmp(self: *CodeGen, inst: Air.Inst.Index, op: std.math.CompareOperator) !v .register_overflow, .register_mask, .indirect, - .lea_direct, - .lea_got, .lea_frame, - .lea_symbol, - .lea_pcrel, + .lea_nav, + .lea_uav, + .lea_lazy_sym, + .lea_extern_func, .elementwise_args, .reserved_frame, .air_ref, => unreachable, - .register, .register_pair, .register_triple, .register_quadruple, .load_frame => null, - .memory, .load_symbol, .load_pcrel, .load_got, .load_direct => dst: { + .register, + .register_pair, + .register_triple, + .register_quadruple, + .load_frame, + => null, + .memory, + .load_nav, + .load_uav, + .load_lazy_sym, + .load_extern_func, + => dst: { switch (resolved_dst_mcv) { .memory => |addr| if (std.math.cast( i32, @@ -174112,7 +174013,7 @@ fn airCmp(self: *CodeGen, inst: Air.Inst.Index, op: std.math.CompareOperator) !v i32, @as(i64, @bitCast(addr)) + abi_size - 8, ) != null) break :dst null, - .load_symbol, .load_pcrel, .load_got, .load_direct => {}, + .load_nav, .load_uav, .load_lazy_sym, .load_extern_func => {}, else => unreachable, } @@ -174149,17 +174050,26 @@ fn airCmp(self: *CodeGen, inst: Air.Inst.Index, op: std.math.CompareOperator) !v .register_overflow, .register_mask, .indirect, - .lea_symbol, - .lea_pcrel, - .lea_direct, - .lea_got, .lea_frame, + .lea_nav, + .lea_uav, + .lea_lazy_sym, + .lea_extern_func, .elementwise_args, .reserved_frame, .air_ref, => unreachable, - .register_pair, .register_triple, .register_quadruple, .load_frame => null, - .memory, .load_symbol, .load_pcrel, .load_got, .load_direct => src: { + .register_pair, + .register_triple, + .register_quadruple, + .load_frame, + => null, + .memory, + .load_nav, + .load_uav, + .load_lazy_sym, + .load_extern_func, + => src: { switch (resolved_src_mcv) { .memory => |addr| if (std.math.cast( i32, @@ -174168,7 +174078,7 @@ fn airCmp(self: *CodeGen, inst: Air.Inst.Index, op: std.math.CompareOperator) !v i32, @as(i64, @bitCast(addr)) + abi_size - 8, ) != null) break :src null, - .load_symbol, .load_pcrel, .load_got, .load_direct => {}, + .load_nav, .load_uav, .load_lazy_sym, .load_extern_func => {}, else => unreachable, } @@ -174453,14 +174363,19 @@ fn airDbgVar(cg: *CodeGen, inst: Air.Inst.Index) !void { if (cg.mod.strip) return; const air_tag = cg.air.instructions.items(.tag)[@intFromEnum(inst)]; const pl_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; - - const name_nts: Air.NullTerminatedString = @enumFromInt(pl_op.payload); - assert(name_nts != .none); - const name = name_nts.toSlice(cg.air); - try cg.mir_local_name_bytes.appendSlice(cg.gpa, name[0 .. name.len + 1]); - + const air_name: Air.NullTerminatedString = @enumFromInt(pl_op.payload); const ty = cg.typeOf(pl_op.operand); - try cg.mir_local_types.append(cg.gpa, ty.toIntern()); + + try cg.mir_locals.append(cg.gpa, .{ + .name = switch (air_name) { + .none => switch (air_tag) { + else => unreachable, + .dbg_arg_inline => .none, + }, + else => try cg.addString(air_name.toSlice(cg.air)), + }, + .type = ty.toIntern(), + }); try cg.genLocalDebugInfo(air_tag, ty, try cg.resolveInst(pl_op.operand)); return cg.finishAir(inst, .unreach, .{ pl_op.operand, .none, .none }); @@ -174567,10 +174482,10 @@ fn isNull(self: *CodeGen, inst: Air.Inst.Index, opt_ty: Type, opt_mcv: MCValue) .register_offset, .register_overflow, .register_mask, - .lea_direct, - .lea_got, - .lea_symbol, - .lea_pcrel, + .lea_nav, + .lea_uav, + .lea_lazy_sym, + .lea_extern_func, .elementwise_args, .reserved_frame, .air_ref, @@ -174618,10 +174533,10 @@ fn isNull(self: *CodeGen, inst: Air.Inst.Index, opt_ty: Type, opt_mcv: MCValue) }, .memory, - .load_symbol, - .load_pcrel, - .load_got, - .load_direct, + .load_nav, + .load_uav, + .load_lazy_sym, + .load_extern_func, => { const addr_reg = (try self.register_manager.allocReg(null, abi.RegisterClass.gp)).to64(); const addr_reg_lock = self.register_manager.lockRegAssumeUnused(addr_reg); @@ -175655,7 +175570,7 @@ fn airAsm(self: *CodeGen, inst: Air.Inst.Index) !void { .memory => |addr| if (std.math.cast(i32, @as(i64, @bitCast(addr)))) |_| break :arg input_mcv, .indirect, .load_frame => break :arg input_mcv, - .load_symbol, .load_direct, .load_got => {}, + .load_nav, .load_uav, .load_lazy_sym, .load_extern_func => {}, else => { const temp_mcv = try self.allocTempRegOrMem(ty, false); try self.genCopy(ty, temp_mcv, input_mcv, .{}); @@ -175934,12 +175849,20 @@ fn airAsm(self: *CodeGen, inst: Air.Inst.Index) !void { } } else return self.fail("invalid modifier: '{s}'", .{modifier}), - .lea_got => |sym_index| if (std.mem.eql(u8, modifier, "P")) - .{ .reg = try self.copyToTmpRegister(.usize, .{ .lea_got = sym_index }) } + .lea_nav => |nav| if (std.mem.eql(u8, modifier, "P")) + .{ .reg = try self.copyToTmpRegister(.usize, .{ .lea_nav = nav }) } else return self.fail("invalid modifier: '{s}'", .{modifier}), - .lea_symbol => |sym_off| if (std.mem.eql(u8, modifier, "P")) - .{ .reg = try self.copyToTmpRegister(.usize, .{ .lea_symbol = sym_off }) } + .lea_uav => |uav| if (std.mem.eql(u8, modifier, "P")) + .{ .reg = try self.copyToTmpRegister(.usize, .{ .lea_uav = uav }) } + else + return self.fail("invalid modifier: '{s}'", .{modifier}), + .lea_lazy_sym => |lazy_sym| if (std.mem.eql(u8, modifier, "P")) + .{ .reg = try self.copyToTmpRegister(.usize, .{ .lea_lazy_sym = lazy_sym }) } + else + return self.fail("invalid modifier: '{s}'", .{modifier}), + .lea_extern_func => |extern_func| if (std.mem.eql(u8, modifier, "P")) + .{ .reg = try self.copyToTmpRegister(.usize, .{ .lea_extern_func = extern_func }) } else return self.fail("invalid modifier: '{s}'", .{modifier}), else => return self.fail("invalid constraint: '{s}'", .{op_str}), @@ -176623,11 +176546,11 @@ fn genCopy(self: *CodeGen, ty: Type, dst_mcv: MCValue, src_mcv: MCValue, opts: C .eflags, .register_overflow, .register_mask, - .lea_direct, - .lea_got, .lea_frame, - .lea_symbol, - .lea_pcrel, + .lea_nav, + .lea_uav, + .lea_lazy_sym, + .lea_extern_func, .elementwise_args, .reserved_frame, .air_ref, @@ -176722,7 +176645,7 @@ fn genCopy(self: *CodeGen, ty: Type, dst_mcv: MCValue, src_mcv: MCValue, opts: C } return; }, - .load_symbol, .load_pcrel, .load_direct, .load_got => { + .load_nav, .load_uav, .load_lazy_sym, .load_extern_func => { const src_addr_reg = (try self.register_manager.allocReg(null, abi.RegisterClass.gp)).to64(); const src_addr_lock = self.register_manager.lockRegAssumeUnused(src_addr_reg); @@ -176755,7 +176678,11 @@ fn genCopy(self: *CodeGen, ty: Type, dst_mcv: MCValue, src_mcv: MCValue, opts: C .undef => if (opts.safety and part_i > 0) .{ .register = dst_regs[0] } else .undef, dst_tag => |src_regs| .{ .register = src_regs[part_i] }, .memory, .indirect, .load_frame => src_mcv.address().offset(part_disp).deref(), - .load_symbol, .load_pcrel, .load_direct, .load_got => .{ .indirect = .{ + .load_nav, + .load_uav, + .load_lazy_sym, + .load_extern_func, + => .{ .indirect = .{ .reg = src_info.?.addr_reg, .off = part_disp, } }, @@ -176776,11 +176703,11 @@ fn genCopy(self: *CodeGen, ty: Type, dst_mcv: MCValue, src_mcv: MCValue, opts: C src_mcv, opts, ), - .memory, .load_symbol, .load_pcrel, .load_direct, .load_got => { + .memory => { switch (dst_mcv) { .memory => |addr| if (std.math.cast(i32, @as(i64, @bitCast(addr)))) |small_addr| return self.genSetMem(.{ .reg = .ds }, small_addr, ty, src_mcv, opts), - .load_symbol, .load_pcrel, .load_direct, .load_got => {}, + .load_nav, .load_uav, .load_lazy_sym, .load_extern_func => {}, else => unreachable, } @@ -176797,6 +176724,10 @@ fn genCopy(self: *CodeGen, ty: Type, dst_mcv: MCValue, src_mcv: MCValue, opts: C src_mcv, opts, ), + .load_nav => |nav| try self.genSetMem(.{ .nav = nav }, 0, ty, src_mcv, opts), + .load_uav => |uav| try self.genSetMem(.{ .uav = uav }, 0, ty, src_mcv, opts), + .load_lazy_sym => |lazy_sym| try self.genSetMem(.{ .lazy_sym = lazy_sym }, 0, ty, src_mcv, opts), + .load_extern_func => |extern_func| try self.genSetMem(.{ .extern_func = extern_func }, 0, ty, src_mcv, opts), } } @@ -176841,14 +176772,14 @@ fn genSetReg( .len = self.vectorSize(.float), .child = .u8_type, }); - try self.genSetReg(dst_reg, full_ty, try self.genTypedValue( + try self.genSetReg(dst_reg, full_ty, try self.lowerValue( .fromInterned(try pt.intern(.{ .aggregate = .{ .ty = full_ty.toIntern(), .storage = .{ .repeated_elem = (try pt.intValue(.u8, 0xaa)).toIntern() }, } })), ), opts); }, - .x87 => try self.genSetReg(dst_reg, .f80, try self.genTypedValue( + .x87 => try self.genSetReg(dst_reg, .f80, try self.lowerValue( try pt.floatValue(.f80, @as(f80, @bitCast(@as(u80, 0xaaaaaaaaaaaaaaaaaaaa)))), ), opts), .ip, .cr, .dr => unreachable, @@ -176878,12 +176809,24 @@ fn genSetReg( } }, .register => |src_reg| if (dst_reg.id() != src_reg.id()) switch (dst_reg.class()) { - .general_purpose, .gphi => switch (src_reg.class()) { - .general_purpose, .gphi => try self.asmRegisterRegister( + .general_purpose => switch (src_reg.class()) { + .general_purpose => try self.asmRegisterRegister( .{ ._, .mov }, dst_alias, registerAlias(src_reg, abi_size), ), + .gphi => if (dst_reg.isClass(.gphi)) try self.asmRegisterRegister( + .{ ._, .mov }, + dst_alias, + registerAlias(src_reg, abi_size), + ) else { + const src_lock = self.register_manager.lockReg(src_reg); + defer if (src_lock) |lock| self.register_manager.unlockReg(lock); + const tmp_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gphi); + + try self.asmRegisterRegister(.{ ._, .mov }, tmp_reg.to8(), src_reg); + try self.asmRegisterRegister(.{ ._, .mov }, dst_alias, tmp_reg.to8()); + }, .segment => try self.asmRegisterRegister( .{ ._, .mov }, dst_alias, @@ -176919,6 +176862,26 @@ fn genSetReg( }); }, }, + .gphi => switch (src_reg.class()) { + .general_purpose => if (src_reg.isClass(.gphi)) try self.asmRegisterRegister( + .{ ._, .mov }, + dst_alias, + registerAlias(src_reg, abi_size), + ) else { + const dst_lock = self.register_manager.lockReg(dst_reg); + defer if (dst_lock) |lock| self.register_manager.unlockReg(lock); + const tmp_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gphi); + + try self.asmRegisterRegister(.{ ._, .mov }, tmp_reg.to8(), src_reg.to8()); + try self.asmRegisterRegister(.{ ._, .mov }, dst_reg, tmp_reg.to8()); + }, + .gphi => try self.asmRegisterRegister( + .{ ._, .mov }, + dst_alias, + registerAlias(src_reg, abi_size), + ), + .segment, .x87, .mmx, .ip, .cr, .dr, .sse => unreachable, + }, .segment => try self.asmRegisterRegister( .{ ._, .mov }, dst_reg, @@ -177237,7 +177200,7 @@ fn genSetReg( if (src_reg_mask.info.inverted) try self.asmRegister(.{ ._, .not }, registerAlias(bits_reg, abi_size)); try self.genSetReg(dst_reg, ty, .{ .register = bits_reg }, .{}); }, - .memory, .load_symbol, .load_pcrel, .load_direct, .load_got => { + .memory, .load_nav, .load_uav, .load_lazy_sym, .load_extern_func => { switch (src_mcv) { .memory => |addr| if (std.math.cast(i32, @as(i64, @bitCast(addr)))) |small_addr| return (try self.moveStrategy( @@ -177251,52 +177214,50 @@ fn genSetReg( .disp = small_addr, } }, }), - .load_symbol => |sym_off| switch (dst_reg.class()) { + .load_nav => |nav| switch (dst_reg.class()) { .general_purpose, .gphi => { - assert(sym_off.off == 0); try self.asmRegisterMemory(.{ ._, .mov }, dst_alias, .{ - .base = .{ .reloc = sym_off.sym_index }, - .mod = .{ .rm = .{ - .size = self.memSize(ty), - .disp = sym_off.off, - } }, + .base = .{ .nav = nav }, + .mod = .{ .rm = .{ .size = self.memSize(ty) } }, }); return; }, .segment, .mmx, .ip, .cr, .dr => unreachable, .x87, .sse => {}, }, - .load_pcrel => |sym_off| switch (dst_reg.class()) { + .load_uav => |uav| switch (dst_reg.class()) { .general_purpose, .gphi => { - assert(sym_off.off == 0); try self.asmRegisterMemory(.{ ._, .mov }, dst_alias, .{ - .base = .{ .pcrel = sym_off.sym_index }, - .mod = .{ .rm = .{ - .size = self.memSize(ty), - .disp = sym_off.off, - } }, + .base = .{ .uav = uav }, + .mod = .{ .rm = .{ .size = self.memSize(ty) } }, }); return; }, .segment, .mmx, .ip, .cr, .dr => unreachable, .x87, .sse => {}, }, - .load_direct => |sym_index| switch (dst_reg.class()) { + .load_lazy_sym => |lazy_sym| switch (dst_reg.class()) { .general_purpose, .gphi => { - _ = try self.addInst(.{ - .tag = .mov, - .ops = .direct_reloc, - .data = .{ .rx = .{ - .r1 = dst_alias, - .payload = try self.addExtra(bits.SymbolOffset{ .sym_index = sym_index }), - } }, + try self.asmRegisterMemory(.{ ._, .mov }, dst_alias, .{ + .base = .{ .lazy_sym = lazy_sym }, + .mod = .{ .rm = .{ .size = self.memSize(ty) } }, + }); + return; + }, + .segment, .mmx, .ip, .cr, .dr => unreachable, + .x87, .sse => {}, + }, + .load_extern_func => |extern_func| switch (dst_reg.class()) { + .general_purpose, .gphi => { + try self.asmRegisterMemory(.{ ._, .mov }, dst_alias, .{ + .base = .{ .extern_func = extern_func }, + .mod = .{ .rm = .{ .size = self.memSize(ty) } }, }); return; }, .segment, .mmx, .ip, .cr, .dr => unreachable, .x87, .sse => {}, }, - .load_got => {}, else => unreachable, } @@ -177309,65 +177270,17 @@ fn genSetReg( .mod = .{ .rm = .{ .size = self.memSize(ty) } }, }); }, - .lea_symbol => |sym_off| switch (self.bin_file.tag) { - .elf, .macho => { - try self.asmRegisterMemory( - .{ ._, .lea }, - dst_reg.to64(), - .{ - .base = .{ .reloc = sym_off.sym_index }, - }, - ); - if (sym_off.off != 0) try self.asmRegisterMemory( - .{ ._, .lea }, - dst_reg.to64(), - .{ - .base = .{ .reg = dst_reg.to64() }, - .mod = .{ .rm = .{ .disp = sym_off.off } }, - }, - ); - }, - else => return self.fail("TODO emit symbol sequence on {s}", .{ - @tagName(self.bin_file.tag), - }), - }, - .lea_pcrel => |sym_off| switch (self.bin_file.tag) { - .elf, .macho => { - try self.asmRegisterMemory( - .{ ._, .lea }, - dst_reg.to64(), - .{ - .base = .{ .pcrel = sym_off.sym_index }, - }, - ); - if (sym_off.off != 0) try self.asmRegisterMemory( - .{ ._, .lea }, - dst_reg.to64(), - .{ - .base = .{ .reg = dst_reg.to64() }, - .mod = .{ .rm = .{ .disp = sym_off.off } }, - }, - ); - }, - else => return self.fail("TODO emit symbol sequence on {s}", .{ - @tagName(self.bin_file.tag), - }), - }, - .lea_direct, .lea_got => |sym_index| _ = try self.addInst(.{ - .tag = switch (src_mcv) { - .lea_direct => .lea, - .lea_got => .mov, - else => unreachable, - }, - .ops = switch (src_mcv) { - .lea_direct => .direct_reloc, - .lea_got => .got_reloc, - else => unreachable, - }, - .data = .{ .rx = .{ - .r1 = dst_reg.to64(), - .payload = try self.addExtra(bits.SymbolOffset{ .sym_index = sym_index }), - } }, + .lea_nav => |nav| try self.asmRegisterMemory(.{ ._, .lea }, dst_reg.to64(), .{ + .base = .{ .nav = nav }, + }), + .lea_uav => |uav| try self.asmRegisterMemory(.{ ._, .lea }, dst_reg.to64(), .{ + .base = .{ .uav = uav }, + }), + .lea_lazy_sym => |lazy_sym| try self.asmRegisterMemory(.{ ._, .lea }, dst_reg.to64(), .{ + .base = .{ .lazy_sym = lazy_sym }, + }), + .lea_extern_func => |lazy_sym| try self.asmRegisterMemory(.{ ._, .lea }, dst_reg.to64(), .{ + .base = .{ .extern_func = lazy_sym }, }), .air_ref => |src_ref| try self.genSetReg(dst_reg, ty, try self.resolveInst(src_ref), opts), } @@ -177388,9 +177301,10 @@ fn genSetMem( .none => .{ .immediate = @bitCast(@as(i64, disp)) }, .reg => |base_reg| .{ .register_offset = .{ .reg = base_reg, .off = disp } }, .frame => |base_frame_index| .{ .lea_frame = .{ .index = base_frame_index, .off = disp } }, - .table, .rip_inst => unreachable, - .reloc => |sym_index| .{ .lea_symbol = .{ .sym_index = sym_index, .off = disp } }, - .pcrel => |sym_index| .{ .lea_pcrel = .{ .sym_index = sym_index, .off = disp } }, + .table, .rip_inst, .lazy_sym => unreachable, + .nav => |nav| .{ .lea_nav = nav }, + .uav => |uav| .{ .lea_uav = uav }, + .extern_func => |extern_func| .{ .lea_extern_func = extern_func }, }; switch (src_mcv) { .none, @@ -177453,6 +177367,7 @@ fn genSetMem( .rm = .{ .size = .byte, .disp = disp }, } }), .register => |src_reg| { + const ip = &zcu.intern_pool; const mem_size = switch (base) { .frame => |base_fi| mem_size: { assert(disp >= 0); @@ -177506,8 +177421,9 @@ fn genSetMem( .index = frame_index, .off = disp, }).compare(.gte, src_align), - .table, .rip_inst => unreachable, - .reloc, .pcrel => false, + .table, .rip_inst, .lazy_sym, .extern_func => unreachable, + .nav => |nav| ip.getNav(nav).getAlignment().compare(.gte, src_align), + .uav => |uav| Type.fromInterned(uav.orig_ty).ptrAlignment(zcu).compare(.gte, src_align), })).write( self, .{ .base = base, .mod = .{ .rm = .{ @@ -177590,16 +177506,16 @@ fn genSetMem( }, .memory, .indirect, - .load_direct, - .lea_direct, - .load_got, - .lea_got, .load_frame, .lea_frame, - .load_symbol, - .lea_symbol, - .load_pcrel, - .lea_pcrel, + .load_nav, + .lea_nav, + .load_uav, + .lea_uav, + .load_lazy_sym, + .lea_lazy_sym, + .load_extern_func, + .lea_extern_func, => switch (abi_size) { 0 => {}, 1, 2, 4, 8 => { @@ -177693,119 +177609,19 @@ fn genInlineMemset( try self.asmOpOnly(.{ .@"rep _sb", .sto }); } -fn genExternSymbolRef( - self: *CodeGen, - comptime tag: Mir.Inst.Tag, - lib: ?[]const u8, - callee: []const u8, -) InnerError!void { - if (self.bin_file.cast(.coff)) |coff_file| { - const global_index = try coff_file.getGlobalSymbol(callee, lib); - const scratch_reg = abi.getCAbiLinkerScratchReg(self.target.cCallingConvention().?); - _ = try self.addInst(.{ - .tag = .mov, - .ops = .import_reloc, - .data = .{ .rx = .{ - .r1 = scratch_reg, - .payload = try self.addExtra(bits.SymbolOffset{ - .sym_index = link.File.Coff.global_symbol_bit | global_index, - }), - } }, - }); - switch (tag) { - .mov => {}, - .call => try self.asmRegister(.{ ._, .call }, scratch_reg), - else => unreachable, - } - } else return self.fail("TODO implement calling extern functions", .{}); -} - fn genLazySymbolRef( self: *CodeGen, comptime tag: Mir.Inst.Tag, reg: Register, lazy_sym: link.File.LazySymbol, ) InnerError!void { - const pt = self.pt; - if (self.bin_file.cast(.elf)) |elf_file| { - const zo = elf_file.zigObjectPtr().?; - const sym_index = zo.getOrCreateMetadataForLazySymbol(elf_file, pt, lazy_sym) catch |err| - return self.fail("{s} creating lazy symbol", .{@errorName(err)}); - if (self.mod.pic) { - switch (tag) { - .lea, .call => try self.genSetReg(reg, .usize, .{ - .lea_symbol = .{ .sym_index = sym_index }, - }, .{}), - .mov => try self.genSetReg(reg, .usize, .{ - .load_symbol = .{ .sym_index = sym_index }, - }, .{}), - else => unreachable, - } - switch (tag) { - .lea, .mov => {}, - .call => try self.asmRegister(.{ ._, .call }, reg), - else => unreachable, - } - } else switch (tag) { - .lea, .mov => try self.asmRegisterMemory(.{ ._, tag }, reg.to64(), .{ - .base = .{ .reloc = sym_index }, - .mod = .{ .rm = .{ .size = .qword } }, - }), - .call => try self.asmImmediate(.{ ._, .call }, .rel(.{ .sym_index = sym_index })), - else => unreachable, - } - } else if (self.bin_file.cast(.plan9)) |p9_file| { - const atom_index = p9_file.getOrCreateAtomForLazySymbol(pt, lazy_sym) catch |err| - return self.fail("{s} creating lazy symbol", .{@errorName(err)}); - var atom = p9_file.getAtom(atom_index); - _ = atom.getOrCreateOffsetTableEntry(p9_file); - const got_addr = atom.getOffsetTableAddress(p9_file); - const got_mem: Memory = .{ - .base = .{ .reg = .ds }, - .mod = .{ .rm = .{ - .size = .qword, - .disp = @intCast(got_addr), - } }, - }; - switch (tag) { - .lea, .mov => try self.asmRegisterMemory(.{ ._, .mov }, reg.to64(), got_mem), - .call => try self.asmMemory(.{ ._, .call }, got_mem), - else => unreachable, - } - switch (tag) { - .lea, .call => {}, - .mov => try self.asmRegisterMemory( - .{ ._, tag }, - reg.to64(), - .initSib(.qword, .{ .base = .{ .reg = reg.to64() } }), - ), - else => unreachable, - } - } else if (self.bin_file.cast(.coff)) |coff_file| { - const atom_index = coff_file.getOrCreateAtomForLazySymbol(pt, lazy_sym) catch |err| - return self.fail("{s} creating lazy symbol", .{@errorName(err)}); - const sym_index = coff_file.getAtom(atom_index).getSymbolIndex().?; - switch (tag) { - .lea, .call => try self.genSetReg(reg, .usize, .{ .lea_got = sym_index }, .{}), - .mov => try self.genSetReg(reg, .usize, .{ .load_got = sym_index }, .{}), - else => unreachable, - } - switch (tag) { - .lea, .mov => {}, - .call => try self.asmRegister(.{ ._, .call }, reg), - else => unreachable, - } - } else if (self.bin_file.cast(.macho)) |macho_file| { - const zo = macho_file.getZigObject().?; - const sym_index = zo.getOrCreateMetadataForLazySymbol(macho_file, pt, lazy_sym) catch |err| - return self.fail("{s} creating lazy symbol", .{@errorName(err)}); - const sym = zo.symbols.items[sym_index]; + if (self.mod.pic) { switch (tag) { .lea, .call => try self.genSetReg(reg, .usize, .{ - .lea_symbol = .{ .sym_index = sym.nlist_idx }, + .lea_lazy_sym = lazy_sym, }, .{}), .mov => try self.genSetReg(reg, .usize, .{ - .load_symbol = .{ .sym_index = sym.nlist_idx }, + .lea_lazy_sym = lazy_sym, }, .{}), else => unreachable, } @@ -177814,8 +177630,13 @@ fn genLazySymbolRef( .call => try self.asmRegister(.{ ._, .call }, reg), else => unreachable, } - } else { - return self.fail("TODO implement genLazySymbol for x86_64 {s}", .{@tagName(self.bin_file.tag)}); + } else switch (tag) { + .lea, .mov => try self.asmRegisterMemory(.{ ._, tag }, reg.to64(), .{ + .base = .{ .lazy_sym = lazy_sym }, + .mod = .{ .rm = .{ .size = .qword } }, + }), + .call => try self.asmImmediate(.{ ._, .call }, .{ .lazy_sym = lazy_sym }), + else => unreachable, } } @@ -177968,11 +177789,11 @@ fn airFloatFromInt(self: *CodeGen, inst: Air.Inst.Index) !void { src_ty.fmt(pt), dst_ty.fmt(pt), }); - var callee_buf: ["__floatun?i?f".len]u8 = undefined; - break :result try self.genCall(.{ .lib = .{ + var sym_buf: ["__floatun?i?f".len]u8 = undefined; + break :result try self.genCall(.{ .extern_func = .{ .return_type = dst_ty.toIntern(), .param_types = &.{src_ty.toIntern()}, - .callee = std.fmt.bufPrint(&callee_buf, "__float{s}{c}i{c}f", .{ + .sym = std.fmt.bufPrint(&sym_buf, "__float{s}{c}i{c}f", .{ switch (src_signedness) { .signed => "", .unsigned => "un", @@ -178048,11 +177869,11 @@ fn airIntFromFloat(self: *CodeGen, inst: Air.Inst.Index) !void { src_ty.fmt(pt), dst_ty.fmt(pt), }); - var callee_buf: ["__fixuns?f?i".len]u8 = undefined; - break :result try self.genCall(.{ .lib = .{ + var sym_buf: ["__fixuns?f?i".len]u8 = undefined; + break :result try self.genCall(.{ .extern_func = .{ .return_type = dst_ty.toIntern(), .param_types = &.{src_ty.toIntern()}, - .callee = std.fmt.bufPrint(&callee_buf, "__fix{s}{c}f{c}i", .{ + .sym = std.fmt.bufPrint(&sym_buf, "__fix{s}{c}f{c}i", .{ switch (dst_signedness) { .signed => "", .unsigned => "uns", @@ -178153,9 +177974,9 @@ fn airCmpxchg(self: *CodeGen, inst: Air.Inst.Index) !void { .off => return self.fail("TODO airCmpxchg with {s}", .{@tagName(ptr_mcv)}), } const ptr_lock = switch (ptr_mem.base) { - .none, .frame, .reloc, .pcrel => null, + .none, .frame, .nav, .uav => null, .reg => |reg| self.register_manager.lockReg(reg), - .table, .rip_inst => unreachable, + .table, .rip_inst, .lazy_sym, .extern_func => unreachable, }; defer if (ptr_lock) |lock| self.register_manager.unlockReg(lock); @@ -178236,9 +178057,9 @@ fn atomicOp( .off => return self.fail("TODO airCmpxchg with {s}", .{@tagName(ptr_mcv)}), } const mem_lock = switch (ptr_mem.base) { - .none, .frame, .reloc, .pcrel => null, + .none, .frame, .nav, .uav => null, .reg => |reg| self.register_manager.lockReg(reg), - .table, .rip_inst => unreachable, + .table, .rip_inst, .lazy_sym, .extern_func => unreachable, }; defer if (mem_lock) |lock| self.register_manager.unlockReg(lock); @@ -179627,7 +179448,7 @@ fn airSelect(self: *CodeGen, inst: Air.Inst.Index) !void { var mask_elems_buf: [32]u8 = undefined; const mask_elems = mask_elems_buf[0..mask_len]; for (mask_elems, 0..) |*elem, bit| elem.* = @intCast(bit / elem_bits); - const mask_mcv = try self.genTypedValue(.fromInterned(try pt.intern(.{ .aggregate = .{ + const mask_mcv = try self.lowerValue(.fromInterned(try pt.intern(.{ .aggregate = .{ .ty = mask_ty.toIntern(), .storage = .{ .bytes = try zcu.intern_pool.getOrPutString(zcu.gpa, pt.tid, mask_elems, .maybe_embedded_nulls) }, } }))); @@ -179655,7 +179476,7 @@ fn airSelect(self: *CodeGen, inst: Air.Inst.Index) !void { mask_elem_ty, @as(u8, 1) << @truncate(bit), )).toIntern(); - const mask_mcv = try self.genTypedValue(.fromInterned(try pt.intern(.{ .aggregate = .{ + const mask_mcv = try self.lowerValue(.fromInterned(try pt.intern(.{ .aggregate = .{ .ty = mask_ty.toIntern(), .storage = .{ .elems = mask_elems }, } }))); @@ -180386,7 +180207,7 @@ fn airShuffle(self: *CodeGen, inst: Air.Inst.Index) !void { else try select_mask_elem_ty.minIntScalar(pt, select_mask_elem_ty)).toIntern(); } - const select_mask_mcv = try self.genTypedValue(.fromInterned(try pt.intern(.{ .aggregate = .{ + const select_mask_mcv = try self.lowerValue(.fromInterned(try pt.intern(.{ .aggregate = .{ .ty = select_mask_ty.toIntern(), .storage = .{ .elems = select_mask_elems[0..mask_elems.len] }, } }))); @@ -180531,7 +180352,7 @@ fn airShuffle(self: *CodeGen, inst: Air.Inst.Index) !void { })).toIntern(); } const lhs_mask_ty = try pt.vectorType(.{ .len = max_abi_size, .child = .u8_type }); - const lhs_mask_mcv = try self.genTypedValue(.fromInterned(try pt.intern(.{ .aggregate = .{ + const lhs_mask_mcv = try self.lowerValue(.fromInterned(try pt.intern(.{ .aggregate = .{ .ty = lhs_mask_ty.toIntern(), .storage = .{ .elems = lhs_mask_elems[0..max_abi_size] }, } }))); @@ -180562,7 +180383,7 @@ fn airShuffle(self: *CodeGen, inst: Air.Inst.Index) !void { })).toIntern(); } const rhs_mask_ty = try pt.vectorType(.{ .len = max_abi_size, .child = .u8_type }); - const rhs_mask_mcv = try self.genTypedValue(.fromInterned(try pt.intern(.{ .aggregate = .{ + const rhs_mask_mcv = try self.lowerValue(.fromInterned(try pt.intern(.{ .aggregate = .{ .ty = rhs_mask_ty.toIntern(), .storage = .{ .elems = rhs_mask_elems[0..max_abi_size] }, } }))); @@ -180896,7 +180717,7 @@ fn airAggregateInit(self: *CodeGen, inst: Air.Inst.Index) !void { .{ .frame = frame_index }, @intCast(elem_size * elements.len), elem_ty, - try self.genTypedValue(sentinel), + try self.lowerValue(sentinel), .{}, ); break :result .{ .load_frame = .{ .index = frame_index } }; @@ -180980,11 +180801,11 @@ fn airMulAdd(self: *CodeGen, inst: Air.Inst.Index) !void { ty.fmt(pt), }); - var callee_buf: ["__fma?".len]u8 = undefined; - break :result try self.genCall(.{ .lib = .{ + var sym_buf: ["__fma?".len]u8 = undefined; + break :result try self.genCall(.{ .extern_func = .{ .return_type = ty.toIntern(), .param_types = &.{ ty.toIntern(), ty.toIntern(), ty.toIntern() }, - .callee = std.fmt.bufPrint(&callee_buf, "{s}fma{s}", .{ + .sym = std.fmt.bufPrint(&sym_buf, "{s}fma{s}", .{ floatLibcAbiPrefix(ty), floatLibcAbiSuffix(ty), }) catch unreachable, @@ -181384,7 +181205,7 @@ fn resolveInst(self: *CodeGen, ref: Air.Inst.Ref) InnerError!MCValue { const mcv: MCValue = if (ref.toIndex()) |inst| mcv: { break :mcv self.inst_tracking.getPtr(inst).?.short; } else mcv: { - break :mcv try self.genTypedValue(.fromInterned(ref.toInterned().?)); + break :mcv try self.lowerValue(.fromInterned(ref.toInterned().?)); }; switch (mcv) { @@ -181422,31 +181243,17 @@ fn limitImmediateType(self: *CodeGen, operand: Air.Inst.Ref, comptime T: type) ! return mcv; } -fn genResult(self: *CodeGen, res: codegen.GenResult) InnerError!MCValue { - return switch (res) { - .mcv => |mcv| switch (mcv) { - .none => .none, - .undef => .undef, - .immediate => |imm| .{ .immediate = imm }, - .memory => |addr| .{ .memory = addr }, - .load_symbol => |sym_index| .{ .load_symbol = .{ .sym_index = sym_index } }, - .lea_symbol => |sym_index| .{ .lea_symbol = .{ .sym_index = sym_index } }, - .load_direct => |sym_index| .{ .load_direct = sym_index }, - .lea_direct => |sym_index| .{ .lea_direct = sym_index }, - .load_got => |sym_index| .{ .lea_got = sym_index }, - }, - .fail => |msg| return self.failMsg(msg), +fn lowerValue(cg: *CodeGen, val: Value) Allocator.Error!MCValue { + return switch (try codegen.lowerValue(cg.pt, val, cg.target)) { + .none => .none, + .undef => .undef, + .immediate => |imm| .{ .immediate = imm }, + .lea_nav => |nav| .{ .lea_nav = nav }, + .lea_uav => |uav| .{ .lea_uav = uav }, + .load_uav => |uav| .{ .load_uav = uav }, }; } -fn genTypedValue(self: *CodeGen, val: Value) InnerError!MCValue { - return self.genResult(try codegen.genTypedValue(self.bin_file, self.pt, self.src_loc, val, self.target.*)); -} - -fn lowerUav(self: *CodeGen, val: Value, alignment: InternPool.Alignment) InnerError!MCValue { - return self.genResult(try self.bin_file.lowerUav(self.pt, val.toIntern(), alignment, self.src_loc)); -} - const CallMCValues = struct { args: []MCValue, air_arg_count: u32, @@ -182311,16 +182118,16 @@ const Temp = struct { .register_offset, .register_mask, .memory, - .load_symbol, - .lea_symbol, - .load_pcrel, - .lea_pcrel, .indirect, - .load_direct, - .lea_direct, - .load_got, - .lea_got, .lea_frame, + .load_nav, + .lea_nav, + .load_uav, + .lea_uav, + .load_lazy_sym, + .lea_lazy_sym, + .lea_extern_func, + .load_extern_func, .elementwise_args, .reserved_frame, .air_ref, @@ -182363,15 +182170,11 @@ const Temp = struct { .mod = .{ .rm = .{ .disp = reg_off.off + off } }, }); }, - .load_symbol, .load_frame => { + .load_frame, .load_nav, .lea_nav, .load_uav, .lea_uav, .load_lazy_sym, .lea_lazy_sym => { const new_reg = try cg.register_manager.allocReg(new_temp_index.toIndex(), abi.RegisterClass.gp); new_temp_index.tracking(cg).* = .init(.{ .register_offset = .{ .reg = new_reg, .off = off } }); try cg.genSetReg(new_reg, .usize, mcv, .{}); }, - .lea_symbol => |sym_off| new_temp_index.tracking(cg).* = .init(.{ .lea_symbol = .{ - .sym_index = sym_off.sym_index, - .off = sym_off.off + off, - } }), .lea_frame => |frame_addr| new_temp_index.tracking(cg).* = .init(.{ .lea_frame = .{ .index = frame_addr.index, .off = frame_addr.off + off, @@ -182404,14 +182207,6 @@ const Temp = struct { } }); return; }, - .lea_symbol => |sym_off| { - assert(std.meta.eql(temp_tracking.long.lea_symbol, sym_off)); - temp_tracking.* = .init(.{ .lea_symbol = .{ - .sym_index = sym_off.sym_index, - .off = sym_off.off + off, - } }); - return; - }, .lea_frame => |frame_addr| { assert(std.meta.eql(temp_tracking.long.lea_frame, frame_addr)); temp_tracking.* = .init(.{ .lea_frame = .{ @@ -182460,38 +182255,6 @@ const Temp = struct { .mod = .{ .rm = .{ .disp = reg_off.off + @as(u31, limb_index) * 8 } }, }); }, - .load_symbol => |sym_off| { - const new_reg = - try cg.register_manager.allocReg(new_temp_index.toIndex(), abi.RegisterClass.gp); - new_temp_index.tracking(cg).* = .init(.{ .register = new_reg }); - try cg.asmRegisterMemory(.{ ._, .mov }, new_reg.to64(), .{ - .base = .{ .reloc = sym_off.sym_index }, - .mod = .{ .rm = .{ - .size = .qword, - .disp = sym_off.off + @as(u31, limb_index) * 8, - } }, - }); - }, - .lea_symbol => |sym_off| { - assert(limb_index == 0); - new_temp_index.tracking(cg).* = .init(.{ .lea_symbol = sym_off }); - }, - .load_pcrel => |sym_off| { - const new_reg = - try cg.register_manager.allocReg(new_temp_index.toIndex(), abi.RegisterClass.gp); - new_temp_index.tracking(cg).* = .init(.{ .register = new_reg }); - try cg.asmRegisterMemory(.{ ._, .mov }, new_reg.to64(), .{ - .base = .{ .pcrel = sym_off.sym_index }, - .mod = .{ .rm = .{ - .size = .qword, - .disp = sym_off.off + @as(u31, limb_index) * 8, - } }, - }); - }, - .lea_pcrel => |sym_off| { - assert(limb_index == 0); - new_temp_index.tracking(cg).* = .init(.{ .lea_pcrel = sym_off }); - }, .load_frame => |frame_addr| { const new_reg = try cg.register_manager.allocReg(new_temp_index.toIndex(), abi.RegisterClass.gp); @@ -182508,6 +182271,70 @@ const Temp = struct { assert(limb_index == 0); new_temp_index.tracking(cg).* = .init(.{ .lea_frame = frame_addr }); }, + .load_nav => |nav| { + const new_reg = + try cg.register_manager.allocReg(new_temp_index.toIndex(), abi.RegisterClass.gp); + new_temp_index.tracking(cg).* = .init(.{ .register = new_reg }); + try cg.asmRegisterMemory(.{ ._, .mov }, new_reg.to64(), .{ + .base = .{ .nav = nav }, + .mod = .{ .rm = .{ + .size = .qword, + .disp = @as(u31, limb_index) * 8, + } }, + }); + }, + .lea_nav => |nav| { + assert(limb_index == 0); + new_temp_index.tracking(cg).* = .init(.{ .lea_nav = nav }); + }, + .load_uav => |uav| { + const new_reg = + try cg.register_manager.allocReg(new_temp_index.toIndex(), abi.RegisterClass.gp); + new_temp_index.tracking(cg).* = .init(.{ .register = new_reg }); + try cg.asmRegisterMemory(.{ ._, .mov }, new_reg.to64(), .{ + .base = .{ .uav = uav }, + .mod = .{ .rm = .{ + .size = .qword, + .disp = @as(u31, limb_index) * 8, + } }, + }); + }, + .lea_uav => |uav| { + assert(limb_index == 0); + new_temp_index.tracking(cg).* = .init(.{ .lea_uav = uav }); + }, + .load_lazy_sym => |lazy_sym| { + const new_reg = + try cg.register_manager.allocReg(new_temp_index.toIndex(), abi.RegisterClass.gp); + new_temp_index.tracking(cg).* = .init(.{ .register = new_reg }); + try cg.asmRegisterMemory(.{ ._, .mov }, new_reg.to64(), .{ + .base = .{ .lazy_sym = lazy_sym }, + .mod = .{ .rm = .{ + .size = .qword, + .disp = @as(u31, limb_index) * 8, + } }, + }); + }, + .lea_lazy_sym => |lazy_sym| { + assert(limb_index == 0); + new_temp_index.tracking(cg).* = .init(.{ .lea_lazy_sym = lazy_sym }); + }, + .load_extern_func => |extern_func| { + const new_reg = + try cg.register_manager.allocReg(new_temp_index.toIndex(), abi.RegisterClass.gp); + new_temp_index.tracking(cg).* = .init(.{ .register = new_reg }); + try cg.asmRegisterMemory(.{ ._, .mov }, new_reg.to64(), .{ + .base = .{ .extern_func = extern_func }, + .mod = .{ .rm = .{ + .size = .qword, + .disp = @as(u31, limb_index) * 8, + } }, + }); + }, + .lea_extern_func => |extern_func| { + assert(limb_index == 0); + new_temp_index.tracking(cg).* = .init(.{ .lea_extern_func = extern_func }); + }, } cg.next_temp_index = @enumFromInt(@intFromEnum(new_temp_index) + 1); return .{ .index = new_temp_index.toIndex() }; @@ -182563,7 +182390,7 @@ const Temp = struct { const temp_tracking = temp_index.tracking(cg); switch (temp_tracking.short) { else => {}, - .register, .lea_symbol, .lea_frame => { + .register, .lea_frame, .lea_nav, .lea_uav, .lea_lazy_sym => { assert(limb_index == 0); cg.temp_type[@intFromEnum(temp_index)] = limb_ty; return; @@ -182580,15 +182407,6 @@ const Temp = struct { cg.temp_type[@intFromEnum(temp_index)] = limb_ty; return; }, - .load_symbol => |sym_off| { - assert(std.meta.eql(temp_tracking.long.load_symbol, sym_off)); - temp_tracking.* = .init(.{ .load_symbol = .{ - .sym_index = sym_off.sym_index, - .off = sym_off.off + @as(u31, limb_index) * 8, - } }); - cg.temp_type[@intFromEnum(temp_index)] = limb_ty; - return; - }, .load_frame => |frame_addr| if (!frame_addr.index.isNamed()) { assert(std.meta.eql(temp_tracking.long.load_frame, frame_addr)); temp_tracking.* = .init(.{ .load_frame = .{ @@ -182779,27 +182597,20 @@ const Temp = struct { .immediate, .register, .register_offset, - .lea_direct, - .lea_got, .lea_frame, => return false, .memory, .indirect, - .load_symbol, - .load_pcrel, - .load_direct, - .load_got, .load_frame, + .load_nav, + .lea_nav, + .load_uav, + .lea_uav, + .load_lazy_sym, + .lea_lazy_sym, + .load_extern_func, + .lea_extern_func, => return temp.toRegClass(true, .general_purpose, cg), - .lea_symbol, .lea_pcrel => |sym_off| { - const off = sym_off.off; - // hack around linker relocation bugs - if (false and off == 0) return false; - try temp.toOffset(-off, cg); - while (try temp.toRegClass(true, .general_purpose, cg)) {} - try temp.toOffset(off, cg); - return true; - }, } } @@ -182866,7 +182677,7 @@ const Temp = struct { ), cg), else => unreachable, }, - .memory, .indirect, .load_frame, .load_symbol => { + .memory, .indirect, .load_frame, .load_nav, .load_uav, .load_lazy_sym => { var val_ptr = try cg.tempInit(.usize, val_mcv.address()); var len = try cg.tempInit(.usize, .{ .immediate = val_ty.abiSize(cg.pt.zcu) }); try val_ptr.memcpy(ptr, &len, cg); @@ -182904,7 +182715,11 @@ const Temp = struct { // hack around linker relocation bugs switch (ptr.tracking(cg).short) { else => {}, - .lea_symbol => while (try ptr.toRegClass(false, .general_purpose, cg)) {}, + .lea_nav, + .lea_uav, + .lea_lazy_sym, + .lea_extern_func, + => while (try ptr.toRegClass(false, .general_purpose, cg)) {}, } try cg.asmMemoryImmediate( .{ ._, .mov }, @@ -182918,7 +182733,11 @@ const Temp = struct { // hack around linker relocation bugs switch (ptr.tracking(cg).short) { else => {}, - .lea_symbol => while (try ptr.toRegClass(false, .general_purpose, cg)) {}, + .lea_nav, + .lea_uav, + .lea_lazy_sym, + .lea_extern_func, + => while (try ptr.toRegClass(false, .general_purpose, cg)) {}, } try cg.asmSetccMemory( cc, @@ -182962,8 +182781,8 @@ const Temp = struct { try ptr.tracking(cg).short.deref().mem(cg, .{ .size = .byte }), ); }, - .lea_frame, .lea_symbol => continue :val_to_gpr, - .memory, .indirect, .load_frame, .load_symbol => { + .lea_frame, .lea_nav, .lea_uav, .lea_lazy_sym => continue :val_to_gpr, + .memory, .indirect, .load_frame, .load_nav, .load_uav, .load_lazy_sym => { var val_ptr = try cg.tempInit(.usize, val_mcv.address()); var len = try cg.tempInit(.usize, .{ .immediate = val_ty.abiSize(cg.pt.zcu) }); try ptr.memcpy(&val_ptr, &len, cg); @@ -183003,7 +182822,7 @@ const Temp = struct { ), cg), else => unreachable, }, - .memory, .indirect, .load_frame, .load_symbol => { + .memory, .indirect, .load_frame, .load_nav, .load_uav, .load_lazy_sym => { var val_ptr = try cg.tempInit(.usize, val_mcv.address()); var src_ptr = try cg.tempInit(.usize, src.tracking(cg).short.address().offset(opts.disp)); @@ -183097,8 +182916,8 @@ const Temp = struct { }), ); }, - .lea_frame, .lea_symbol => continue :val_to_gpr, - .memory, .indirect, .load_frame, .load_symbol => { + .lea_frame, .lea_nav, .lea_uav, .lea_lazy_sym => continue :val_to_gpr, + .memory, .indirect, .load_frame, .load_nav, .load_uav, .load_lazy_sym => { var dst_ptr = try cg.tempInit(.usize, dst.tracking(cg).short.address().offset(opts.disp)); var val_ptr = try cg.tempInit(.usize, val_mcv.address()); @@ -183119,7 +182938,7 @@ const Temp = struct { // hack around linker relocation bugs switch (ptr.tracking(cg).short) { else => {}, - .lea_symbol => |sym_off| if (dst_rc != .general_purpose or sym_off.off != 0) + .lea_nav, .lea_uav, .lea_lazy_sym => if (dst_rc != .general_purpose) while (try ptr.toRegClass(false, .general_purpose, cg)) {}, } try strat.read(cg, dst_reg, try ptr.tracking(cg).short.deref().mem(cg, .{ @@ -183139,7 +182958,7 @@ const Temp = struct { // hack around linker relocation bugs switch (ptr.tracking(cg).short) { else => {}, - .lea_symbol => while (try ptr.toRegClass(false, .general_purpose, cg)) {}, + .lea_nav, .lea_uav, .lea_lazy_sym => while (try ptr.toRegClass(false, .general_purpose, cg)) {}, } const strat = try cg.moveStrategy(src_ty, src_rc, false); try strat.write(cg, try ptr.tracking(cg).short.deref().mem(cg, .{ @@ -186902,7 +186721,7 @@ const Temp = struct { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__divti3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__divti3" } }, .unused, .unused, .unused, @@ -186931,7 +186750,7 @@ const Temp = struct { }, .call_frame = .{ .alignment = .@"16" }, .extra_temps = .{ - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__udivti3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__udivti3" } }, .unused, .unused, .unused, @@ -186964,7 +186783,7 @@ const Temp = struct { .{ .type = .usize, .kind = .{ .param_gpr = .{ .cc = .ccc, .at = 1 } } }, .{ .type = .usize, .kind = .{ .param_gpr = .{ .cc = .ccc, .at = 2 } } }, .{ .type = .usize, .kind = .{ .param_gpr = .{ .cc = .ccc, .at = 3 } } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__divei4" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__divei4" } }, .unused, .unused, .unused, @@ -186997,7 +186816,7 @@ const Temp = struct { .{ .type = .usize, .kind = .{ .param_gpr = .{ .cc = .ccc, .at = 1 } } }, .{ .type = .usize, .kind = .{ .param_gpr = .{ .cc = .ccc, .at = 2 } } }, .{ .type = .usize, .kind = .{ .param_gpr = .{ .cc = .ccc, .at = 3 } } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__udivei4" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__udivei4" } }, .unused, .unused, .unused, @@ -187361,7 +187180,7 @@ const Temp = struct { .{ .type = .i64, .kind = .{ .param_gpr = .{ .cc = .ccc, .at = 1 } } }, .{ .type = .u64, .kind = .{ .param_gpr = .{ .cc = .ccc, .at = 2 } } }, .{ .type = .i64, .kind = .{ .param_gpr = .{ .cc = .ccc, .at = 3 } } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__divti3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__divti3" } }, .{ .type = .u64, .kind = .{ .ret_gpr = .{ .cc = .ccc, .at = 0 } } }, .unused, .unused, @@ -187399,7 +187218,7 @@ const Temp = struct { .{ .type = .u64, .kind = .{ .param_gpr = .{ .cc = .ccc, .at = 1 } } }, .{ .type = .u64, .kind = .{ .param_gpr = .{ .cc = .ccc, .at = 2 } } }, .{ .type = .u64, .kind = .{ .param_gpr = .{ .cc = .ccc, .at = 3 } } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__udivti3" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__udivti3" } }, .{ .type = .u64, .kind = .{ .ret_gpr = .{ .cc = .ccc, .at = 0 } } }, .unused, .unused, @@ -187437,7 +187256,7 @@ const Temp = struct { .{ .type = .usize, .kind = .{ .param_gpr = .{ .cc = .ccc, .at = 1 } } }, .{ .type = .usize, .kind = .{ .param_gpr = .{ .cc = .ccc, .at = 2 } } }, .{ .type = .usize, .kind = .{ .param_gpr = .{ .cc = .ccc, .at = 3 } } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__divei4" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__divei4" } }, .unused, .unused, .unused, @@ -187473,7 +187292,7 @@ const Temp = struct { .{ .type = .usize, .kind = .{ .param_gpr = .{ .cc = .ccc, .at = 1 } } }, .{ .type = .usize, .kind = .{ .param_gpr = .{ .cc = .ccc, .at = 2 } } }, .{ .type = .usize, .kind = .{ .param_gpr = .{ .cc = .ccc, .at = 3 } } }, - .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__udivei4" } } }, + .{ .type = .usize, .kind = .{ .extern_func = "__udivei4" } }, .unused, .unused, .unused, @@ -187528,16 +187347,16 @@ const Temp = struct { .register_overflow, .register_mask, .memory, - .load_symbol, - .lea_symbol, - .load_pcrel, - .lea_pcrel, .indirect, - .load_direct, - .lea_direct, - .load_got, - .lea_got, .load_frame, + .load_nav, + .lea_nav, + .load_uav, + .lea_uav, + .load_lazy_sym, + .lea_lazy_sym, + .load_extern_func, + .lea_extern_func, => { const result = try cg.allocRegOrMem(inst, true); try cg.genCopy(cg.typeOfIndex(inst), result, temp_mcv, .{}); @@ -187714,7 +187533,7 @@ fn tempInit(cg: *CodeGen, ty: Type, value: MCValue) InnerError!Temp { } fn tempFromValue(cg: *CodeGen, value: Value) InnerError!Temp { - return cg.tempInit(value.typeOf(cg.pt.zcu), try cg.genTypedValue(value)); + return cg.tempInit(value.typeOf(cg.pt.zcu), try cg.lowerValue(value)); } fn tempMemFromValue(cg: *CodeGen, value: Value) InnerError!Temp { @@ -187722,13 +187541,20 @@ fn tempMemFromValue(cg: *CodeGen, value: Value) InnerError!Temp { } fn tempMemFromAlignedValue(cg: *CodeGen, alignment: InternPool.Alignment, value: Value) InnerError!Temp { - return cg.tempInit(value.typeOf(cg.pt.zcu), try cg.lowerUav(value, alignment)); + const ty = value.typeOf(cg.pt.zcu); + return cg.tempInit(ty, .{ .load_uav = .{ + .val = value.toIntern(), + .orig_ty = (try cg.pt.ptrType(.{ + .child = ty.toIntern(), + .flags = .{ + .is_const = true, + .alignment = alignment, + }, + })).toIntern(), + } }); } fn tempFromOperand(cg: *CodeGen, op_ref: Air.Inst.Ref, op_dies: bool) InnerError!Temp { - const zcu = cg.pt.zcu; - const ip = &zcu.intern_pool; - if (op_dies) { const temp_index = cg.next_temp_index; const temp: Temp = .{ .index = temp_index.toIndex() }; @@ -187742,8 +187568,7 @@ fn tempFromOperand(cg: *CodeGen, op_ref: Air.Inst.Ref, op_dies: bool) InnerError } if (op_ref.toIndex()) |op_inst| return .{ .index = op_inst }; - const val = op_ref.toInterned().?; - return cg.tempInit(.fromInterned(ip.typeOf(val)), try cg.genTypedValue(.fromInterned(val))); + return cg.tempFromValue(.fromInterned(op_ref.toInterned().?)); } fn tempsFromOperandsInner( @@ -188578,8 +188403,8 @@ const Select = struct { splat_int_mem: struct { ref: Select.Operand.Ref, inside: enum { umin, smin, smax } = .umin, outside: enum { smin, smax } }, splat_float_mem: struct { ref: Select.Operand.Ref, inside: enum { zero } = .zero, outside: f16 }, frame: FrameIndex, - lazy_symbol: struct { kind: link.File.LazySymbol.Kind, ref: Select.Operand.Ref = .none }, - symbol: *const struct { lib: ?[]const u8 = null, name: []const u8 }, + lazy_sym: struct { kind: link.File.LazySymbol.Kind, ref: Select.Operand.Ref = .none }, + extern_func: [*:0]const u8, const ConstSpec = struct { ref: Select.Operand.Ref = .none, @@ -189010,43 +188835,21 @@ const Select = struct { } }))), true }; }, .frame => |frame_index| .{ try cg.tempInit(spec.type, .{ .load_frame = .{ .index = frame_index } }), true }, - .lazy_symbol => |lazy_symbol_spec| { + .lazy_sym => |lazy_symbol_spec| { const ip = &pt.zcu.intern_pool; const ty = if (lazy_symbol_spec.ref == .none) spec.type else lazy_symbol_spec.ref.typeOf(s); - const lazy_symbol: link.File.LazySymbol = .{ + return .{ try cg.tempInit(.usize, .{ .lea_lazy_sym = .{ .kind = lazy_symbol_spec.kind, .ty = switch (ip.indexToKey(ty.toIntern())) { .inferred_error_set_type => |func_index| switch (ip.funcIesResolvedUnordered(func_index)) { - .none => unreachable, // unresolved inferred error set + .none => unreachable, else => |ty_index| ty_index, }, else => ty.toIntern(), }, - }; - return .{ try cg.tempInit(.usize, .{ .lea_symbol = .{ - .sym_index = if (cg.bin_file.cast(.elf)) |elf_file| - elf_file.zigObjectPtr().?.getOrCreateMetadataForLazySymbol(elf_file, pt, lazy_symbol) catch |err| - return cg.fail("{s} creating lazy symbol", .{@errorName(err)}) - else if (cg.bin_file.cast(.macho)) |macho_file| - macho_file.getZigObject().?.getOrCreateMetadataForLazySymbol(macho_file, pt, lazy_symbol) catch |err| - return cg.fail("{s} creating lazy symbol", .{@errorName(err)}) - else if (cg.bin_file.cast(.coff)) |coff_file| - coff_file.getAtom(coff_file.getOrCreateAtomForLazySymbol(pt, lazy_symbol) catch |err| - return cg.fail("{s} creating lazy symbol", .{@errorName(err)})).getSymbolIndex().? - else - return cg.fail("external symbols unimplemented for {s}", .{@tagName(cg.bin_file.tag)}), } }), true }; }, - .symbol => |symbol_spec| .{ try cg.tempInit(spec.type, .{ .lea_symbol = .{ - .sym_index = if (cg.bin_file.cast(.elf)) |elf_file| - try elf_file.getGlobalSymbol(symbol_spec.name, symbol_spec.lib) - else if (cg.bin_file.cast(.macho)) |macho_file| - try macho_file.getGlobalSymbol(symbol_spec.name, symbol_spec.lib) - else if (cg.bin_file.cast(.coff)) |coff_file| - link.File.Coff.global_symbol_bit | try coff_file.getGlobalSymbol(symbol_spec.name, symbol_spec.lib) - else - return cg.fail("external symbols unimplemented for {s}", .{@tagName(cg.bin_file.tag)}), - } }), true }, + .extern_func => |extern_func_spec| .{ try cg.tempInit(spec.type, .{ .lea_extern_func = try cg.addString(std.mem.span(extern_func_spec)) }), true }, }; } @@ -190089,9 +189892,12 @@ const Select = struct { .register => |reg| .{ .reg = s.lowerReg(reg.toSize(op.flags.base.size, s.cg.target)) }, .register_pair, .register_triple, .register_quadruple, .register_offset, .register_overflow => unreachable, .register_mask => |reg_mask| .{ .reg = s.lowerReg(reg_mask.reg.toSize(op.flags.base.size, s.cg.target)) }, + .lea_nav => |nav| .{ .imm = .{ .nav = .{ .index = nav } } }, + .lea_uav => |uav| .{ .imm = .{ .uav = uav } }, + .lea_lazy_sym => |lazy_sym| .{ .imm = .{ .lazy_sym = lazy_sym } }, + .lea_extern_func => |extern_func| .{ .imm = .{ .extern_func = extern_func } }, else => |mcv| .{ .mem = try mcv.mem(s.cg, .{ .size = op.flags.base.size }) }, - .lea_symbol => |sym_off| .{ .imm = .rel(sym_off) }, - .load_direct, .lea_direct, .load_got, .lea_got, .lea_frame, .elementwise_args, .reserved_frame, .air_ref => unreachable, + .lea_frame, .elementwise_args, .reserved_frame, .air_ref => unreachable, }, 1...2 => |imm| switch (op.flags.base.ref.valueOf(s)) { inline .register_pair, .register_triple, .register_quadruple => |regs| .{ @@ -190105,37 +189911,20 @@ const Select = struct { }, .simm => .{ .imm = .s(op.adjustedImm(i32, s)) }, .uimm => .{ .imm = .u(@bitCast(op.adjustedImm(i64, s))) }, - .lea => .{ .mem = .{ - .base = switch (op.flags.base.ref.valueOf(s)) { + .lea => .{ .mem = try op.flags.base.ref.valueOf(s).deref().mem(s.cg, .{ + .size = op.flags.base.size, + .index = switch (op.flags.index.ref.valueOf(s)) { else => unreachable, .none => .none, - .register => |base_reg| .{ .reg = base_reg.toSize(.ptr, s.cg.target) }, - .register_offset => |base_reg_off| .{ .reg = base_reg_off.reg.toSize(.ptr, s.cg.target) }, - .lea_symbol => |base_sym_off| .{ .reloc = base_sym_off.sym_index }, - .lea_pcrel => |base_sym_off| .{ .pcrel = base_sym_off.sym_index }, + .register => |index_reg| index_reg.toSize(.ptr, s.cg.target), }, - .mod = .{ .rm = .{ - .size = op.flags.base.size, - .index = switch (op.flags.index.ref.valueOf(s)) { - else => unreachable, - .none => .none, - .register => |index_reg| index_reg.toSize(.ptr, s.cg.target), - .register_offset => |index_reg_off| index_reg_off.reg.toSize(.ptr, s.cg.target), - }, - .scale = op.flags.index.scale, - .disp = op.adjustedImm(i32, s) + switch (op.flags.base.ref.valueOf(s)) { - else => unreachable, - .none, .register => 0, - .register_offset => |base_reg_off| base_reg_off.off, - .lea_symbol => |base_sym_off| base_sym_off.off, - } + switch (op.flags.index.ref.valueOf(s)) { - else => unreachable, - .none, .register => 0, - .register_offset => |base_reg_off| base_reg_off.off, - .lea_symbol => |base_sym_off| base_sym_off.off, - }, - } }, - } }, + .scale = op.flags.index.scale, + .disp = op.adjustedImm(i32, s) + switch (op.flags.index.ref.valueOf(s)) { + else => unreachable, + .none, .register, .lea_nav, .lea_uav, .lea_lazy_sym, .lea_extern_func => 0, + .register_offset => |base_reg_off| base_reg_off.off, + }, + }) }, .mem => .{ .mem = try op.flags.base.ref.valueOf(s).mem(s.cg, .{ .size = op.flags.base.size, .index = switch (op.flags.index.ref.valueOf(s)) { diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index cbbfdab20257a9a4c84e61471663269ec5a990ff..ff6bf85ef3b061d8342e1d021837990a818ffd6e 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -1,6 +1,9 @@ //! This file contains the functionality for emitting x86_64 MIR as machine code lower: Lower, +bin_file: *link.File, +pt: Zcu.PerThread, +pic: bool, atom_index: u32, debug_output: link.File.DebugInfoOutput, code: *std.ArrayListUnmanaged(u8), @@ -9,28 +12,28 @@ prev_di_loc: Loc, /// Relative to the beginning of `code`. prev_di_pc: usize, +code_offset_mapping: std.ArrayListUnmanaged(u32), +relocs: std.ArrayListUnmanaged(Reloc), +table_relocs: std.ArrayListUnmanaged(TableReloc), + pub const Error = Lower.Error || error{ EmitFail, } || link.File.UpdateDebugInfoError; pub fn emitMir(emit: *Emit) Error!void { - const gpa = emit.lower.bin_file.comp.gpa; - const code_offset_mapping = try emit.lower.allocator.alloc(u32, emit.lower.mir.instructions.len); - defer emit.lower.allocator.free(code_offset_mapping); - var relocs: std.ArrayListUnmanaged(Reloc) = .empty; - defer relocs.deinit(emit.lower.allocator); - var table_relocs: std.ArrayListUnmanaged(TableReloc) = .empty; - defer table_relocs.deinit(emit.lower.allocator); - var local_name_index: usize = 0; + const gpa = emit.bin_file.comp.gpa; + try emit.code_offset_mapping.resize(gpa, emit.lower.mir.instructions.len); + emit.relocs.clearRetainingCapacity(); + emit.table_relocs.clearRetainingCapacity(); var local_index: usize = 0; for (0..emit.lower.mir.instructions.len) |mir_i| { const mir_index: Mir.Inst.Index = @intCast(mir_i); - code_offset_mapping[mir_index] = @intCast(emit.code.items.len); + emit.code_offset_mapping.items[mir_index] = @intCast(emit.code.items.len); const lowered = try emit.lower.lowerMir(mir_index); var lowered_relocs = lowered.relocs; - for (lowered.insts, 0..) |lowered_inst, lowered_index| { - const start_offset: u32 = @intCast(emit.code.items.len); + lowered_inst: for (lowered.insts, 0..) |lowered_inst, lowered_index| { if (lowered_inst.prefix == .directive) { + const start_offset: u32 = @intCast(emit.code.items.len); switch (emit.debug_output) { .dwarf => |dwarf| switch (lowered_inst.encoding.mnemonic) { .@".cfi_def_cfa" => try dwarf.genDebugFrame(start_offset, .{ .def_cfa = .{ @@ -83,204 +86,305 @@ pub fn emitMir(emit: *Emit) Error!void { } continue; } - try lowered_inst.encode(emit.code.writer(gpa), .{}); - const end_offset: u32 = @intCast(emit.code.items.len); + var reloc_info_buf: [2]RelocInfo = undefined; + var reloc_info_index: usize = 0; while (lowered_relocs.len > 0 and lowered_relocs[0].lowered_inst_index == lowered_index) : ({ lowered_relocs = lowered_relocs[1..]; - }) switch (lowered_relocs[0].target) { - .inst => |target| { - const inst_length: u4 = @intCast(end_offset - start_offset); - const reloc_offset, const reloc_length = reloc_offset_length: { - var reloc_offset = inst_length; - var op_index: usize = lowered_inst.ops.len; - while (true) { - op_index -= 1; - const op = lowered_inst.encoding.data.ops[op_index]; - if (op == .none) continue; - const is_mem = op.isMemory(); - const enc_length: u4 = if (is_mem) switch (lowered_inst.ops[op_index].mem.sib.base) { - .rip_inst => 4, - else => unreachable, - } else @intCast(std.math.divCeil(u7, @intCast(op.immBitSize()), 8) catch unreachable); - reloc_offset -= enc_length; - if (op_index == lowered_relocs[0].op_index) break :reloc_offset_length .{ reloc_offset, enc_length }; - std.debug.assert(!is_mem); - } - }; - try relocs.append(emit.lower.allocator, .{ - .inst_offset = start_offset, - .inst_length = inst_length, - .source_offset = reloc_offset, - .source_length = reloc_length, - .target = target, - .target_offset = lowered_relocs[0].off, - }); - }, - .table => try table_relocs.append(emit.lower.allocator, .{ - .source_offset = end_offset - 4, - .target_offset = lowered_relocs[0].off, - }), - .linker_extern_fn => |sym_index| if (emit.lower.bin_file.cast(.elf)) |elf_file| { - // Add relocation to the decl. - const zo = elf_file.zigObjectPtr().?; - const atom_ptr = zo.symbol(emit.atom_index).atom(elf_file).?; - const r_type = @intFromEnum(std.elf.R_X86_64.PLT32); - try atom_ptr.addReloc(gpa, .{ - .r_offset = end_offset - 4, - .r_info = @as(u64, sym_index) << 32 | r_type, - .r_addend = lowered_relocs[0].off - 4, - }, zo); - } else if (emit.lower.bin_file.cast(.macho)) |macho_file| { - // Add relocation to the decl. - const zo = macho_file.getZigObject().?; - const atom = zo.symbols.items[emit.atom_index].getAtom(macho_file).?; - try atom.addReloc(macho_file, .{ - .tag = .@"extern", - .offset = end_offset - 4, - .target = sym_index, - .addend = lowered_relocs[0].off, - .type = .branch, - .meta = .{ - .pcrel = true, - .has_subtractor = false, - .length = 2, - .symbolnum = @intCast(sym_index), + reloc_info_index += 1; + }) reloc_info_buf[reloc_info_index] = .{ + .op_index = lowered_relocs[0].op_index, + .off = lowered_relocs[0].off, + .target = target: switch (lowered_relocs[0].target) { + .inst => |inst| .{ .index = inst, .is_extern = false, .type = .inst }, + .table => .{ .index = undefined, .is_extern = false, .type = .table }, + .nav => |nav| { + const ip = &emit.pt.zcu.intern_pool; + const sym_index = switch (try codegen.genNavRef( + emit.bin_file, + emit.pt, + emit.lower.src_loc, + .fromInterned(ip.getNav(nav).typeOf(ip)), + nav, + emit.lower.target.*, + )) { + .mcv => |mcv| switch (mcv) { + else => std.debug.panic("{s}: {}\n", .{ @src().fn_name, mcv }), + .lea_symbol => |sym_index| sym_index, + }, + .fail => |em| { + assert(emit.lower.err_msg == null); + emit.lower.err_msg = em; + return error.EmitFail; + }, + }; + break :target switch (ip.getNav(nav).status) { + .unresolved => unreachable, + .type_resolved => |type_resolved| .{ + .index = sym_index, + .is_extern = false, + .type = if (type_resolved.is_threadlocal) .tlv else .symbol, + }, + .fully_resolved => |fully_resolved| switch (ip.indexToKey(fully_resolved.val)) { + .@"extern" => |@"extern"| .{ + .index = sym_index, + .is_extern = switch (@"extern".visibility) { + .default => true, + .hidden, .protected => false, + }, + .type = if (@"extern".is_threadlocal) .tlv else .symbol, + .force_pcrel_direct = switch (@"extern".relocation) { + .any => false, + .pcrel => true, + }, + }, + .variable => |variable| .{ + .index = sym_index, + .is_extern = false, + .type = if (variable.is_threadlocal) .tlv else .symbol, + }, + else => .{ .index = sym_index, .is_extern = false, .type = .symbol }, + }, + }; + }, + .uav => |uav| .{ + .index = switch (try emit.bin_file.lowerUav( + emit.pt, + uav.val, + Type.fromInterned(uav.orig_ty).ptrAlignment(emit.pt.zcu), + emit.lower.src_loc, + )) { + .mcv => |mcv| switch (mcv) { + else => std.debug.panic("{s}: {}\n", .{ @src().fn_name, mcv }), + .load_direct, .load_symbol => |sym_index| sym_index, + }, + .fail => |em| { + assert(emit.lower.err_msg == null); + emit.lower.err_msg = em; + return error.EmitFail; + }, }, - }); - } else if (emit.lower.bin_file.cast(.coff)) |coff_file| { - // Add relocation to the decl. - const atom_index = coff_file.getAtomIndexForSymbol( - .{ .sym_index = emit.atom_index, .file = null }, - ).?; - const target = if (link.File.Coff.global_symbol_bit & sym_index != 0) - coff_file.getGlobalByIndex(link.File.Coff.global_symbol_mask & sym_index) - else - link.File.Coff.SymbolWithLoc{ .sym_index = sym_index, .file = null }; - try coff_file.addRelocation(atom_index, .{ - .type = .direct, - .target = target, - .offset = end_offset - 4, - .addend = @intCast(lowered_relocs[0].off), - .pcrel = true, - .length = 2, - }); - } else return emit.fail("TODO implement extern reloc for {s}", .{ - @tagName(emit.lower.bin_file.tag), - }), - .linker_tlsld => |sym_index| { - const elf_file = emit.lower.bin_file.cast(.elf).?; - const zo = elf_file.zigObjectPtr().?; - const atom = zo.symbol(emit.atom_index).atom(elf_file).?; - const r_type = @intFromEnum(std.elf.R_X86_64.TLSLD); - try atom.addReloc(gpa, .{ - .r_offset = end_offset - 4, - .r_info = @as(u64, sym_index) << 32 | r_type, - .r_addend = lowered_relocs[0].off - 4, - }, zo); - }, - .linker_dtpoff => |sym_index| { - const elf_file = emit.lower.bin_file.cast(.elf).?; - const zo = elf_file.zigObjectPtr().?; - const atom = zo.symbol(emit.atom_index).atom(elf_file).?; - const r_type = @intFromEnum(std.elf.R_X86_64.DTPOFF32); - try atom.addReloc(gpa, .{ - .r_offset = end_offset - 4, - .r_info = @as(u64, sym_index) << 32 | r_type, - .r_addend = lowered_relocs[0].off, - }, zo); - }, - .linker_reloc, .linker_pcrel => |sym_index| if (emit.lower.bin_file.cast(.elf)) |elf_file| { - const zo = elf_file.zigObjectPtr().?; - const atom = zo.symbol(emit.atom_index).atom(elf_file).?; - const sym = zo.symbol(sym_index); - if (emit.lower.pic) { - const r_type: u32 = if (sym.flags.is_extern_ptr and lowered_relocs[0].target != .linker_pcrel) - @intFromEnum(std.elf.R_X86_64.GOTPCREL) + .is_extern = false, + .type = .symbol, + }, + .lazy_sym => |lazy_sym| .{ + .index = if (emit.bin_file.cast(.elf)) |elf_file| + elf_file.zigObjectPtr().?.getOrCreateMetadataForLazySymbol(elf_file, emit.pt, lazy_sym) catch |err| + return emit.fail("{s} creating lazy symbol", .{@errorName(err)}) + else if (emit.bin_file.cast(.macho)) |macho_file| + macho_file.getZigObject().?.getOrCreateMetadataForLazySymbol(macho_file, emit.pt, lazy_sym) catch |err| + return emit.fail("{s} creating lazy symbol", .{@errorName(err)}) + else if (emit.bin_file.cast(.coff)) |coff_file| sym_index: { + const atom = coff_file.getOrCreateAtomForLazySymbol(emit.pt, lazy_sym) catch |err| + return emit.fail("{s} creating lazy symbol", .{@errorName(err)}); + break :sym_index coff_file.getAtom(atom).getSymbolIndex().?; + } else if (emit.bin_file.cast(.plan9)) |p9_file| + p9_file.getOrCreateAtomForLazySymbol(emit.pt, lazy_sym) catch |err| + return emit.fail("{s} creating lazy symbol", .{@errorName(err)}) else - @intFromEnum(std.elf.R_X86_64.PC32); - try atom.addReloc(gpa, .{ - .r_offset = end_offset - 4, - .r_info = @as(u64, sym_index) << 32 | r_type, - .r_addend = lowered_relocs[0].off - 4, - }, zo); - } else { - const r_type: u32 = if (sym.flags.is_tls) - @intFromEnum(std.elf.R_X86_64.TPOFF32) + return emit.fail("lazy symbols unimplemented for {s}", .{@tagName(emit.bin_file.tag)}), + .is_extern = false, + .type = .symbol, + }, + .extern_func => |extern_func| .{ + .index = if (emit.bin_file.cast(.elf)) |elf_file| + try elf_file.getGlobalSymbol(extern_func.toSlice(&emit.lower.mir).?, null) + else if (emit.bin_file.cast(.macho)) |macho_file| + try macho_file.getGlobalSymbol(extern_func.toSlice(&emit.lower.mir).?, null) + else if (emit.bin_file.cast(.coff)) |coff_file| + link.File.Coff.global_symbol_bit | try coff_file.getGlobalSymbol(extern_func.toSlice(&emit.lower.mir).?, null) else - @intFromEnum(std.elf.R_X86_64.@"32"); - try atom.addReloc(gpa, .{ - .r_offset = end_offset - 4, - .r_info = @as(u64, sym_index) << 32 | r_type, - .r_addend = lowered_relocs[0].off, - }, zo); + return emit.fail("external symbols unimplemented for {s}", .{@tagName(emit.bin_file.tag)}), + .is_extern = true, + .type = .symbol, + }, + }, + }; + const reloc_info = reloc_info_buf[0..reloc_info_index]; + for (reloc_info) |*reloc| switch (reloc.target.type) { + .inst, .table => {}, + .symbol => { + switch (lowered_inst.encoding.mnemonic) { + .call => { + reloc.target.type = .branch; + try emit.encodeInst(lowered_inst, reloc_info); + continue :lowered_inst; + }, + else => {}, } - } else if (emit.lower.bin_file.cast(.macho)) |macho_file| { - const zo = macho_file.getZigObject().?; - const atom = zo.symbols.items[emit.atom_index].getAtom(macho_file).?; - const sym = &zo.symbols.items[sym_index]; - const @"type": link.File.MachO.Relocation.Type = if (sym.flags.is_extern_ptr and lowered_relocs[0].target != .linker_pcrel) - .got_load - else if (sym.flags.tlv) - .tlv - else - .signed; - try atom.addReloc(macho_file, .{ - .tag = .@"extern", - .offset = @intCast(end_offset - 4), - .target = sym_index, - .addend = lowered_relocs[0].off, - .type = @"type", - .meta = .{ - .pcrel = true, - .has_subtractor = false, - .length = 2, - .symbolnum = @intCast(sym_index), - }, + if (emit.bin_file.cast(.elf)) |_| { + if (!emit.pic) switch (lowered_inst.encoding.mnemonic) { + .lea => try emit.encodeInst(try .new(.none, .mov, &.{ + lowered_inst.ops[0], + .{ .imm = .s(0) }, + }, emit.lower.target), reloc_info), + .mov => try emit.encodeInst(try .new(.none, .mov, &.{ + lowered_inst.ops[0], + .{ .mem = .initSib(lowered_inst.ops[reloc.op_index].mem.sib.ptr_size, .{ + .base = .{ .reg = .ds }, + }) }, + }, emit.lower.target), reloc_info), + else => unreachable, + } else if (reloc.target.is_extern) switch (lowered_inst.encoding.mnemonic) { + .lea => try emit.encodeInst(try .new(.none, .mov, &.{ + lowered_inst.ops[0], + .{ .mem = .initRip(.ptr, 0) }, + }, emit.lower.target), reloc_info), + .mov => { + try emit.encodeInst(try .new(.none, .mov, &.{ + lowered_inst.ops[0], + .{ .mem = .initRip(.ptr, 0) }, + }, emit.lower.target), reloc_info); + try emit.encodeInst(try .new(.none, .mov, &.{ + lowered_inst.ops[0], + .{ .mem = .initSib(lowered_inst.ops[reloc.op_index].mem.sib.ptr_size, .{ .base = .{ + .reg = lowered_inst.ops[0].reg.to64(), + } }) }, + }, emit.lower.target), &.{}); + }, + else => unreachable, + } else switch (lowered_inst.encoding.mnemonic) { + .lea => try emit.encodeInst(try .new(.none, .lea, &.{ + lowered_inst.ops[0], + .{ .mem = .initRip(.none, 0) }, + }, emit.lower.target), reloc_info), + .mov => try emit.encodeInst(try .new(.none, .mov, &.{ + lowered_inst.ops[0], + .{ .mem = .initRip(lowered_inst.ops[reloc.op_index].mem.sib.ptr_size, 0) }, + }, emit.lower.target), reloc_info), + else => unreachable, + } + } else if (emit.bin_file.cast(.macho)) |_| { + if (reloc.target.is_extern) switch (lowered_inst.encoding.mnemonic) { + .lea => try emit.encodeInst(try .new(.none, .mov, &.{ + lowered_inst.ops[0], + .{ .mem = .initRip(.ptr, 0) }, + }, emit.lower.target), reloc_info), + .mov => { + try emit.encodeInst(try .new(.none, .mov, &.{ + lowered_inst.ops[0], + .{ .mem = .initRip(.ptr, 0) }, + }, emit.lower.target), reloc_info); + try emit.encodeInst(try .new(.none, .mov, &.{ + lowered_inst.ops[0], + .{ .mem = .initSib(lowered_inst.ops[reloc.op_index].mem.sib.ptr_size, .{ .base = .{ + .reg = lowered_inst.ops[0].reg.to64(), + } }) }, + }, emit.lower.target), &.{}); + }, + else => unreachable, + } else switch (lowered_inst.encoding.mnemonic) { + .lea => try emit.encodeInst(try .new(.none, .lea, &.{ + lowered_inst.ops[0], + .{ .mem = .initRip(.none, 0) }, + }, emit.lower.target), reloc_info), + .mov => try emit.encodeInst(try .new(.none, .mov, &.{ + lowered_inst.ops[0], + .{ .mem = .initRip(lowered_inst.ops[reloc.op_index].mem.sib.ptr_size, 0) }, + }, emit.lower.target), reloc_info), + else => unreachable, + } + } else return emit.fail("TODO implement relocs for {s}", .{ + @tagName(emit.bin_file.tag), }); - } else unreachable, - .linker_got, - .linker_direct, - .linker_import, - => |sym_index| if (emit.lower.bin_file.cast(.elf)) |_| { - unreachable; - } else if (emit.lower.bin_file.cast(.macho)) |_| { - unreachable; - } else if (emit.lower.bin_file.cast(.coff)) |coff_file| { - const atom_index = coff_file.getAtomIndexForSymbol(.{ - .sym_index = emit.atom_index, - .file = null, - }).?; - const target = if (link.File.Coff.global_symbol_bit & sym_index != 0) - coff_file.getGlobalByIndex(link.File.Coff.global_symbol_mask & sym_index) - else - link.File.Coff.SymbolWithLoc{ .sym_index = sym_index, .file = null }; - try coff_file.addRelocation(atom_index, .{ - .type = switch (lowered_relocs[0].target) { - .linker_got => .got, - .linker_direct => .direct, - .linker_import => .import, + continue :lowered_inst; + }, + .branch, .tls => unreachable, + .tlv => { + if (emit.bin_file.cast(.elf)) |elf_file| { + if (reloc.target.is_extern) { + // TODO handle extern TLS vars, i.e., emit GD model + return emit.fail("TODO implement extern {s} reloc for {s}", .{ + @tagName(reloc.target.type), @tagName(emit.bin_file.tag), + }); + } else if (emit.pic) switch (lowered_inst.encoding.mnemonic) { + .lea, .mov => { + // Here, we currently assume local dynamic TLS vars, and so + // we emit LD model. + try emit.encodeInst(try .new(.none, .lea, &.{ + .{ .reg = .rdi }, + .{ .mem = .initRip(.none, 0) }, + }, emit.lower.target), &.{.{ + .op_index = 1, + .target = .{ + .index = reloc.target.index, + .is_extern = false, + .type = .tls, + }, + }}); + try emit.encodeInst(try .new(.none, .call, &.{ + .{ .imm = .s(0) }, + }, emit.lower.target), &.{.{ + .op_index = 0, + .target = .{ + .index = try elf_file.getGlobalSymbol("__tls_get_addr", null), + .is_extern = true, + .type = .branch, + }, + }}); + try emit.encodeInst(try .new(.none, lowered_inst.encoding.mnemonic, &.{ + lowered_inst.ops[0], + .{ .mem = .initSib(.none, .{ + .base = .{ .reg = .rax }, + .disp = std.math.minInt(i32), + }) }, + }, emit.lower.target), reloc_info); + }, + else => unreachable, + } else switch (lowered_inst.encoding.mnemonic) { + .lea, .mov => { + // Since we are linking statically, we emit LE model directly. + try emit.encodeInst(try .new(.none, .mov, &.{ + .{ .reg = .rax }, + .{ .mem = .initSib(.qword, .{ .base = .{ .reg = .fs } }) }, + }, emit.lower.target), &.{}); + try emit.encodeInst(try .new(.none, lowered_inst.encoding.mnemonic, &.{ + lowered_inst.ops[0], + .{ .mem = .initSib(.none, .{ + .base = .{ .reg = .rax }, + .disp = std.math.minInt(i32), + }) }, + }, emit.lower.target), reloc_info); + }, else => unreachable, + } + } else if (emit.bin_file.cast(.macho)) |_| switch (lowered_inst.encoding.mnemonic) { + .lea => { + try emit.encodeInst(try .new(.none, .mov, &.{ + .{ .reg = .rdi }, + .{ .mem = .initRip(.ptr, 0) }, + }, emit.lower.target), reloc_info); + try emit.encodeInst(try .new(.none, .call, &.{ + .{ .mem = .initSib(.qword, .{ .base = .{ .reg = .rdi } }) }, + }, emit.lower.target), &.{}); + try emit.encodeInst(try .new(.none, .mov, &.{ + lowered_inst.ops[0], + .{ .reg = .rax }, + }, emit.lower.target), &.{}); }, - .target = target, - .offset = @intCast(end_offset - 4), - .addend = @intCast(lowered_relocs[0].off), - .pcrel = true, - .length = 2, - }); - } else if (emit.lower.bin_file.cast(.plan9)) |p9_file| { - try p9_file.addReloc(emit.atom_index, .{ // TODO we may need to add a .type field to the relocs if they are .linker_got instead of just .linker_direct - .target = sym_index, // we set sym_index to just be the atom index - .offset = @intCast(end_offset - 4), - .addend = @intCast(lowered_relocs[0].off), - .type = .pcrel, + .mov => { + try emit.encodeInst(try .new(.none, .mov, &.{ + .{ .reg = .rdi }, + .{ .mem = .initRip(.ptr, 0) }, + }, emit.lower.target), reloc_info); + try emit.encodeInst(try .new(.none, .call, &.{ + .{ .mem = .initSib(.qword, .{ .base = .{ .reg = .rdi } }) }, + }, emit.lower.target), &.{}); + try emit.encodeInst(try .new(.none, .mov, &.{ + lowered_inst.ops[0], + .{ .mem = .initSib(.qword, .{ .base = .{ .reg = .rax } }) }, + }, emit.lower.target), &.{}); + }, + else => unreachable, + } else return emit.fail("TODO implement relocs for {s}", .{ + @tagName(emit.bin_file.tag), }); - } else return emit.fail("TODO implement linker reloc for {s}", .{ - @tagName(emit.lower.bin_file.tag), - }), + continue :lowered_inst; + }, }; + try emit.encodeInst(lowered_inst, reloc_info); } - std.debug.assert(lowered_relocs.len == 0); + assert(lowered_relocs.len == 0); if (lowered.insts.len == 0) { const mir_inst = emit.lower.mir.instructions.get(mir_index); @@ -358,7 +462,6 @@ pub fn emitMir(emit: *Emit) Error!void { .pseudo_dbg_arg_i_s, .pseudo_dbg_arg_i_u, .pseudo_dbg_arg_i_64, - .pseudo_dbg_arg_reloc, .pseudo_dbg_arg_ro, .pseudo_dbg_arg_fa, .pseudo_dbg_arg_m, @@ -366,7 +469,6 @@ pub fn emitMir(emit: *Emit) Error!void { .pseudo_dbg_var_i_s, .pseudo_dbg_var_i_u, .pseudo_dbg_var_i_64, - .pseudo_dbg_var_reloc, .pseudo_dbg_var_ro, .pseudo_dbg_var_fa, .pseudo_dbg_var_m, @@ -391,16 +493,6 @@ pub fn emitMir(emit: *Emit) Error!void { loc_buf[0] = .{ .constu = mir_inst.data.i64 }; break :stack_value &loc_buf[0]; } }, - .pseudo_dbg_arg_reloc, .pseudo_dbg_var_reloc => .{ .plus = .{ - sym: { - loc_buf[0] = .{ .addr_reloc = mir_inst.data.reloc.sym_index }; - break :sym &loc_buf[0]; - }, - off: { - loc_buf[1] = .{ .consts = mir_inst.data.reloc.off }; - break :off &loc_buf[1]; - }, - } }, .pseudo_dbg_arg_fa, .pseudo_dbg_var_fa => { const reg_off = emit.lower.mir.resolveFrameAddr(mir_inst.data.fa); break :loc .{ .plus = .{ @@ -415,15 +507,53 @@ pub fn emitMir(emit: *Emit) Error!void { } }; }, .pseudo_dbg_arg_m, .pseudo_dbg_var_m => { - const mem = emit.lower.mem(undefined, mir_inst.data.x.payload); + const ip = &emit.pt.zcu.intern_pool; + const mem = emit.lower.mir.resolveMemoryExtra(mir_inst.data.x.payload).decode(); break :loc .{ .plus = .{ base: { loc_buf[0] = switch (mem.base()) { .none => .{ .constu = 0 }, .reg => |reg| .{ .breg = reg.dwarfNum() }, .frame, .table, .rip_inst => unreachable, - .reloc => |sym_index| .{ .addr_reloc = sym_index }, - .pcrel => unreachable, + .nav => |nav| .{ .addr_reloc = switch (codegen.genNavRef( + emit.bin_file, + emit.pt, + emit.lower.src_loc, + .fromInterned(ip.getNav(nav).typeOf(ip)), + nav, + emit.lower.target.*, + ) catch |err| switch (err) { + error.CodegenFail, + => return emit.fail("unable to codegen: {s}", .{@errorName(err)}), + else => |e| return e, + }) { + .mcv => |mcv| switch (mcv) { + else => unreachable, + .load_direct, .load_symbol => |sym_index| sym_index, + }, + .fail => |em| { + assert(emit.lower.err_msg == null); + emit.lower.err_msg = em; + return error.EmitFail; + }, + } }, + .uav => |uav| .{ .addr_reloc = switch (try emit.bin_file.lowerUav( + emit.pt, + uav.val, + Type.fromInterned(uav.orig_ty).ptrAlignment(emit.pt.zcu), + emit.lower.src_loc, + )) { + .mcv => |mcv| switch (mcv) { + else => unreachable, + .load_direct, .load_symbol => |sym_index| sym_index, + }, + .fail => |em| { + assert(emit.lower.err_msg == null); + emit.lower.err_msg = em; + return error.EmitFail; + }, + } }, + .lazy_sym, .extern_func => unreachable, }; break :base &loc_buf[0]; }, @@ -438,13 +568,8 @@ pub fn emitMir(emit: *Emit) Error!void { }, }; - const local_name_bytes = emit.lower.mir.local_name_bytes[local_name_index..]; - const local_name = local_name_bytes[0..std.mem.indexOfScalar(u8, local_name_bytes, 0).? :0]; - local_name_index += local_name.len + 1; - - const local_type = emit.lower.mir.local_types[local_index]; + const local = &emit.lower.mir.locals[local_index]; local_index += 1; - try dwarf.genLocalVarDebugInfo( switch (mir_inst.ops) { else => unreachable, @@ -452,7 +577,6 @@ pub fn emitMir(emit: *Emit) Error!void { .pseudo_dbg_arg_i_s, .pseudo_dbg_arg_i_u, .pseudo_dbg_arg_i_64, - .pseudo_dbg_arg_reloc, .pseudo_dbg_arg_ro, .pseudo_dbg_arg_fa, .pseudo_dbg_arg_m, @@ -462,27 +586,23 @@ pub fn emitMir(emit: *Emit) Error!void { .pseudo_dbg_var_i_s, .pseudo_dbg_var_i_u, .pseudo_dbg_var_i_64, - .pseudo_dbg_var_reloc, .pseudo_dbg_var_ro, .pseudo_dbg_var_fa, .pseudo_dbg_var_m, .pseudo_dbg_var_val, => .local_var, }, - local_name, - .fromInterned(local_type), + local.name.toSlice(&emit.lower.mir), + .fromInterned(local.type), loc, ); }, - .plan9 => {}, - .none => {}, + .plan9, .none => local_index += 1, }, .pseudo_dbg_arg_val, .pseudo_dbg_var_val => switch (emit.debug_output) { .dwarf => |dwarf| { - const local_name_bytes = emit.lower.mir.local_name_bytes[local_name_index..]; - const local_name = local_name_bytes[0..std.mem.indexOfScalar(u8, local_name_bytes, 0).? :0]; - local_name_index += local_name.len + 1; - + const local = &emit.lower.mir.locals[local_index]; + local_index += 1; try dwarf.genLocalConstDebugInfo( emit.lower.src_loc, switch (mir_inst.ops) { @@ -490,12 +610,11 @@ pub fn emitMir(emit: *Emit) Error!void { .pseudo_dbg_arg_val => .comptime_arg, .pseudo_dbg_var_val => .local_const, }, - local_name, + local.name.toSlice(&emit.lower.mir), .fromInterned(mir_inst.data.ip_index), ); }, - .plan9 => {}, - .none => {}, + .plan9, .none => local_index += 1, }, .pseudo_dbg_var_args_none => switch (emit.debug_output) { .dwarf => |dwarf| try dwarf.genVarArgsDebugInfo(), @@ -507,8 +626,8 @@ pub fn emitMir(emit: *Emit) Error!void { } } } - for (relocs.items) |reloc| { - const target = code_offset_mapping[reloc.target]; + for (emit.relocs.items) |reloc| { + const target = emit.code_offset_mapping.items[reloc.target]; const disp = @as(i64, @intCast(target)) - @as(i64, @intCast(reloc.inst_offset + reloc.inst_length)) + reloc.target_offset; const inst_bytes = emit.code.items[reloc.inst_offset..][0..reloc.inst_length]; switch (reloc.source_length) { @@ -522,13 +641,13 @@ pub fn emitMir(emit: *Emit) Error!void { } } if (emit.lower.mir.table.len > 0) { - if (emit.lower.bin_file.cast(.elf)) |elf_file| { + if (emit.bin_file.cast(.elf)) |elf_file| { const zo = elf_file.zigObjectPtr().?; const atom = zo.symbol(emit.atom_index).atom(elf_file).?; const ptr_size = @divExact(emit.lower.target.ptrBitWidth(), 8); var table_offset = std.mem.alignForward(u32, @intCast(emit.code.items.len), ptr_size); - for (table_relocs.items) |table_reloc| try atom.addReloc(gpa, .{ + for (emit.table_relocs.items) |table_reloc| try atom.addReloc(gpa, .{ .r_offset = table_reloc.source_offset, .r_info = @as(u64, emit.atom_index) << 32 | @intFromEnum(std.elf.R_X86_64.@"32"), .r_addend = @as(i64, table_offset) + table_reloc.target_offset, @@ -537,7 +656,7 @@ pub fn emitMir(emit: *Emit) Error!void { try atom.addReloc(gpa, .{ .r_offset = table_offset, .r_info = @as(u64, emit.atom_index) << 32 | @intFromEnum(std.elf.R_X86_64.@"64"), - .r_addend = code_offset_mapping[entry], + .r_addend = emit.code_offset_mapping.items[entry], }, zo); table_offset += ptr_size; } @@ -546,6 +665,192 @@ pub fn emitMir(emit: *Emit) Error!void { } } +pub fn deinit(emit: *Emit) void { + const gpa = emit.bin_file.comp.gpa; + emit.code_offset_mapping.deinit(gpa); + emit.relocs.deinit(gpa); + emit.table_relocs.deinit(gpa); + emit.* = undefined; +} + +const RelocInfo = struct { + op_index: Lower.InstOpIndex, + off: i32 = 0, + target: Target, + + const Target = struct { + index: u32, + is_extern: bool, + type: Target.Type, + force_pcrel_direct: bool = false, + + const Type = enum { inst, table, symbol, branch, tls, tlv }; + }; +}; + +fn encodeInst(emit: *Emit, lowered_inst: Instruction, reloc_info: []const RelocInfo) Error!void { + const comp = emit.bin_file.comp; + const gpa = comp.gpa; + const start_offset: u32 = @intCast(emit.code.items.len); + try lowered_inst.encode(emit.code.writer(gpa), .{}); + const end_offset: u32 = @intCast(emit.code.items.len); + for (reloc_info) |reloc| switch (reloc.target.type) { + .inst => { + const inst_length: u4 = @intCast(end_offset - start_offset); + const reloc_offset, const reloc_length = reloc_offset_length: { + var reloc_offset = inst_length; + var op_index: usize = lowered_inst.ops.len; + while (true) { + op_index -= 1; + const op = lowered_inst.encoding.data.ops[op_index]; + if (op == .none) continue; + const is_mem = op.isMemory(); + const enc_length: u4 = if (is_mem) switch (lowered_inst.ops[op_index].mem.sib.base) { + .rip_inst => 4, + else => unreachable, + } else @intCast(std.math.divCeil(u7, @intCast(op.immBitSize()), 8) catch unreachable); + reloc_offset -= enc_length; + if (op_index == reloc.op_index) break :reloc_offset_length .{ reloc_offset, enc_length }; + assert(!is_mem); + } + }; + try emit.relocs.append(emit.lower.allocator, .{ + .inst_offset = start_offset, + .inst_length = inst_length, + .source_offset = reloc_offset, + .source_length = reloc_length, + .target = reloc.target.index, + .target_offset = reloc.off, + }); + }, + .table => try emit.table_relocs.append(emit.lower.allocator, .{ + .source_offset = end_offset - 4, + .target_offset = reloc.off, + }), + .symbol => if (emit.bin_file.cast(.elf)) |elf_file| { + const zo = elf_file.zigObjectPtr().?; + const atom = zo.symbol(emit.atom_index).atom(elf_file).?; + const r_type: std.elf.R_X86_64 = if (!emit.pic) + .@"32" + else if (reloc.target.is_extern and !reloc.target.force_pcrel_direct) + .GOTPCREL + else + .PC32; + try atom.addReloc(gpa, .{ + .r_offset = end_offset - 4, + .r_info = @as(u64, reloc.target.index) << 32 | @intFromEnum(r_type), + .r_addend = if (emit.pic) reloc.off - 4 else reloc.off, + }, zo); + } else if (emit.bin_file.cast(.macho)) |macho_file| { + const zo = macho_file.getZigObject().?; + const atom = zo.symbols.items[emit.atom_index].getAtom(macho_file).?; + try atom.addReloc(macho_file, .{ + .tag = .@"extern", + .offset = end_offset - 4, + .target = reloc.target.index, + .addend = reloc.off, + .type = if (reloc.target.is_extern and !reloc.target.force_pcrel_direct) .got_load else .signed, + .meta = .{ + .pcrel = true, + .has_subtractor = false, + .length = 2, + .symbolnum = @intCast(reloc.target.index), + }, + }); + } else unreachable, + .branch => if (emit.bin_file.cast(.elf)) |elf_file| { + const zo = elf_file.zigObjectPtr().?; + const atom = zo.symbol(emit.atom_index).atom(elf_file).?; + const r_type: std.elf.R_X86_64 = .PLT32; + try atom.addReloc(gpa, .{ + .r_offset = end_offset - 4, + .r_info = @as(u64, reloc.target.index) << 32 | @intFromEnum(r_type), + .r_addend = reloc.off - 4, + }, zo); + } else if (emit.bin_file.cast(.macho)) |macho_file| { + const zo = macho_file.getZigObject().?; + const atom = zo.symbols.items[emit.atom_index].getAtom(macho_file).?; + try atom.addReloc(macho_file, .{ + .tag = .@"extern", + .offset = end_offset - 4, + .target = reloc.target.index, + .addend = reloc.off, + .type = .branch, + .meta = .{ + .pcrel = true, + .has_subtractor = false, + .length = 2, + .symbolnum = @intCast(reloc.target.index), + }, + }); + } else if (emit.bin_file.cast(.coff)) |coff_file| { + const atom_index = coff_file.getAtomIndexForSymbol( + .{ .sym_index = emit.atom_index, .file = null }, + ).?; + const target: link.File.Coff.SymbolWithLoc = if (link.File.Coff.global_symbol_bit & reloc.target.index != 0) + coff_file.getGlobalByIndex(link.File.Coff.global_symbol_mask & reloc.target.index) + else + .{ .sym_index = reloc.target.index, .file = null }; + try coff_file.addRelocation(atom_index, .{ + .type = .direct, + .target = target, + .offset = end_offset - 4, + .addend = @intCast(reloc.off), + .pcrel = true, + .length = 2, + }); + } else return emit.fail("TODO implement {s} reloc for {s}", .{ + @tagName(reloc.target.type), @tagName(emit.bin_file.tag), + }), + .tls => if (emit.bin_file.cast(.elf)) |elf_file| { + if (reloc.target.is_extern) return emit.fail("TODO implement extern {s} reloc for {s}", .{ + @tagName(reloc.target.type), @tagName(emit.bin_file.tag), + }); + const zo = elf_file.zigObjectPtr().?; + const atom = zo.symbol(emit.atom_index).atom(elf_file).?; + const r_type: std.elf.R_X86_64 = if (emit.pic) .TLSLD else unreachable; + try atom.addReloc(gpa, .{ + .r_offset = end_offset - 4, + .r_info = @as(u64, reloc.target.index) << 32 | @intFromEnum(r_type), + .r_addend = reloc.off - 4, + }, zo); + } else return emit.fail("TODO implement {s} reloc for {s}", .{ + @tagName(reloc.target.type), @tagName(emit.bin_file.tag), + }), + .tlv => if (emit.bin_file.cast(.elf)) |elf_file| { + if (reloc.target.is_extern) return emit.fail("TODO implement extern {s} reloc for {s}", .{ + @tagName(reloc.target.type), @tagName(emit.bin_file.tag), + }); + const zo = elf_file.zigObjectPtr().?; + const atom = zo.symbol(emit.atom_index).atom(elf_file).?; + const r_type: std.elf.R_X86_64 = if (emit.pic) .DTPOFF32 else .TPOFF32; + try atom.addReloc(gpa, .{ + .r_offset = end_offset - 4, + .r_info = @as(u64, reloc.target.index) << 32 | @intFromEnum(r_type), + .r_addend = reloc.off, + }, zo); + } else if (emit.bin_file.cast(.macho)) |macho_file| { + const zo = macho_file.getZigObject().?; + const atom = zo.symbols.items[emit.atom_index].getAtom(macho_file).?; + try atom.addReloc(macho_file, .{ + .tag = .@"extern", + .offset = end_offset - 4, + .target = reloc.target.index, + .addend = reloc.off, + .type = .tlv, + .meta = .{ + .pcrel = true, + .has_subtractor = false, + .length = 2, + .symbolnum = @intCast(reloc.target.index), + }, + }); + } else return emit.fail("TODO implement {s} reloc for {s}", .{ + @tagName(reloc.target.type), @tagName(emit.bin_file.tag), + }), + }; +} + fn fail(emit: *Emit, comptime format: []const u8, args: anytype) Error { return switch (emit.lower.fail(format, args)) { error.LowerFail => error.EmitFail, @@ -629,11 +934,17 @@ fn dbgAdvancePCAndLine(emit: *Emit, loc: Loc) Error!void { } } +const assert = std.debug.assert; const bits = @import("bits.zig"); +const codegen = @import("../../codegen.zig"); const Emit = @This(); +const encoder = @import("encoder.zig"); +const Instruction = encoder.Instruction; const InternPool = @import("../../InternPool.zig"); const link = @import("../../link.zig"); const log = std.log.scoped(.emit); const Lower = @import("Lower.zig"); const Mir = @import("Mir.zig"); const std = @import("std"); +const Type = @import("../../Type.zig"); +const Zcu = @import("../../Zcu.zig"); diff --git a/src/arch/x86_64/Lower.zig b/src/arch/x86_64/Lower.zig index 54b419103ffc539556a5d716c7974476170bd312..c476fd2eda97adaa4bb17616ba2c87a996fcab7b 100644 --- a/src/arch/x86_64/Lower.zig +++ b/src/arch/x86_64/Lower.zig @@ -1,10 +1,6 @@ //! This file contains the functionality for lowering x86_64 MIR to Instructions -bin_file: *link.File, target: *const std.Target, -output_mode: std.builtin.OutputMode, -link_mode: std.builtin.LinkMode, -pic: bool, allocator: std.mem.Allocator, mir: Mir, cc: std.builtin.CallingConvention, @@ -17,7 +13,6 @@ result_relocs: [max_result_relocs]Reloc = undefined, const max_result_insts = @max( 1, // non-pseudo instructions - 3, // (ELF only) TLS local dynamic (LD) sequence in PIC mode 2, // cmovcc: cmovcc \ cmovcc 3, // setcc: setcc \ setcc \ logicop 2, // jcc: jcc \ jcc @@ -25,6 +20,7 @@ const max_result_insts = @max( pseudo_probe_adjust_unrolled_max_insts, pseudo_probe_adjust_setup_insts, pseudo_probe_adjust_loop_insts, + abi.zigcc.callee_preserved_regs.len * 2, // push_regs/pop_regs abi.Win64.callee_preserved_regs.len * 2, // push_regs/pop_regs abi.SysV.callee_preserved_regs.len * 2, // push_regs/pop_regs ); @@ -33,14 +29,13 @@ const max_result_relocs = @max( 2, // jcc: jcc \ jcc 2, // test \ jcc \ probe \ sub \ jmp 1, // probe \ sub \ jcc - 3, // (ELF only) TLS local dynamic (LD) sequence in PIC mode ); -const ResultInstIndex = std.math.IntFittingRange(0, max_result_insts - 1); -const ResultRelocIndex = std.math.IntFittingRange(0, max_result_relocs - 1); -const InstOpIndex = std.math.IntFittingRange( +const ResultInstIndex = std.math.IntFittingRange(0, max_result_insts); +const ResultRelocIndex = std.math.IntFittingRange(0, max_result_relocs); +pub const InstOpIndex = std.math.IntFittingRange( 0, - @typeInfo(@FieldType(Instruction, "ops")).array.len - 1, + @typeInfo(@FieldType(Instruction, "ops")).array.len, ); pub const pseudo_probe_align_insts = 5; // test \ jcc \ probe \ sub \ jmp @@ -54,7 +49,8 @@ pub const Error = error{ LowerFail, InvalidInstruction, CannotEncode, -}; + CodegenFail, +} || codegen.GenerateSymbolError; pub const Reloc = struct { lowered_inst_index: ResultInstIndex, @@ -65,14 +61,10 @@ pub const Reloc = struct { const Target = union(enum) { inst: Mir.Inst.Index, table, - linker_reloc: u32, - linker_pcrel: u32, - linker_tlsld: u32, - linker_dtpoff: u32, - linker_extern_fn: u32, - linker_got: u32, - linker_direct: u32, - linker_import: u32, + nav: InternPool.Nav.Index, + uav: InternPool.Key.Ptr.BaseAddr.Uav, + lazy_sym: link.File.LazySymbol, + extern_func: Mir.NullTerminatedString, }; }; @@ -80,7 +72,7 @@ const Options = struct { allow_frame_locs: bool }; /// The returned slice is overwritten by the next call to lowerMir. pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index) Error!struct { - insts: []const Instruction, + insts: []Instruction, relocs: []const Reloc, } { lower.result_insts = undefined; @@ -98,130 +90,130 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index) Error!struct { .pseudo => switch (inst.ops) { .pseudo_cmov_z_and_np_rr => { assert(inst.data.rr.fixes == ._); - try lower.emit(.none, .cmovnz, &.{ + try lower.encode(.none, .cmovnz, &.{ .{ .reg = inst.data.rr.r2 }, .{ .reg = inst.data.rr.r1 }, }); - try lower.emit(.none, .cmovnp, &.{ + try lower.encode(.none, .cmovnp, &.{ .{ .reg = inst.data.rr.r1 }, .{ .reg = inst.data.rr.r2 }, }); }, .pseudo_cmov_nz_or_p_rr => { assert(inst.data.rr.fixes == ._); - try lower.emit(.none, .cmovnz, &.{ + try lower.encode(.none, .cmovnz, &.{ .{ .reg = inst.data.rr.r1 }, .{ .reg = inst.data.rr.r2 }, }); - try lower.emit(.none, .cmovp, &.{ + try lower.encode(.none, .cmovp, &.{ .{ .reg = inst.data.rr.r1 }, .{ .reg = inst.data.rr.r2 }, }); }, .pseudo_cmov_nz_or_p_rm => { assert(inst.data.rx.fixes == ._); - try lower.emit(.none, .cmovnz, &.{ + try lower.encode(.none, .cmovnz, &.{ .{ .reg = inst.data.rx.r1 }, .{ .mem = lower.mem(1, inst.data.rx.payload) }, }); - try lower.emit(.none, .cmovp, &.{ + try lower.encode(.none, .cmovp, &.{ .{ .reg = inst.data.rx.r1 }, .{ .mem = lower.mem(1, inst.data.rx.payload) }, }); }, .pseudo_set_z_and_np_r => { assert(inst.data.rr.fixes == ._); - try lower.emit(.none, .setz, &.{ + try lower.encode(.none, .setz, &.{ .{ .reg = inst.data.rr.r1 }, }); - try lower.emit(.none, .setnp, &.{ + try lower.encode(.none, .setnp, &.{ .{ .reg = inst.data.rr.r2 }, }); - try lower.emit(.none, .@"and", &.{ + try lower.encode(.none, .@"and", &.{ .{ .reg = inst.data.rr.r1 }, .{ .reg = inst.data.rr.r2 }, }); }, .pseudo_set_z_and_np_m => { assert(inst.data.rx.fixes == ._); - try lower.emit(.none, .setz, &.{ + try lower.encode(.none, .setz, &.{ .{ .mem = lower.mem(0, inst.data.rx.payload) }, }); - try lower.emit(.none, .setnp, &.{ + try lower.encode(.none, .setnp, &.{ .{ .reg = inst.data.rx.r1 }, }); - try lower.emit(.none, .@"and", &.{ + try lower.encode(.none, .@"and", &.{ .{ .mem = lower.mem(0, inst.data.rx.payload) }, .{ .reg = inst.data.rx.r1 }, }); }, .pseudo_set_nz_or_p_r => { assert(inst.data.rr.fixes == ._); - try lower.emit(.none, .setnz, &.{ + try lower.encode(.none, .setnz, &.{ .{ .reg = inst.data.rr.r1 }, }); - try lower.emit(.none, .setp, &.{ + try lower.encode(.none, .setp, &.{ .{ .reg = inst.data.rr.r2 }, }); - try lower.emit(.none, .@"or", &.{ + try lower.encode(.none, .@"or", &.{ .{ .reg = inst.data.rr.r1 }, .{ .reg = inst.data.rr.r2 }, }); }, .pseudo_set_nz_or_p_m => { assert(inst.data.rx.fixes == ._); - try lower.emit(.none, .setnz, &.{ + try lower.encode(.none, .setnz, &.{ .{ .mem = lower.mem(0, inst.data.rx.payload) }, }); - try lower.emit(.none, .setp, &.{ + try lower.encode(.none, .setp, &.{ .{ .reg = inst.data.rx.r1 }, }); - try lower.emit(.none, .@"or", &.{ + try lower.encode(.none, .@"or", &.{ .{ .mem = lower.mem(0, inst.data.rx.payload) }, .{ .reg = inst.data.rx.r1 }, }); }, .pseudo_j_z_and_np_inst => { assert(inst.data.inst.fixes == ._); - try lower.emit(.none, .jnz, &.{ + try lower.encode(.none, .jnz, &.{ .{ .imm = lower.reloc(0, .{ .inst = index + 1 }, 0) }, }); - try lower.emit(.none, .jnp, &.{ + try lower.encode(.none, .jnp, &.{ .{ .imm = lower.reloc(0, .{ .inst = inst.data.inst.inst }, 0) }, }); }, .pseudo_j_nz_or_p_inst => { assert(inst.data.inst.fixes == ._); - try lower.emit(.none, .jnz, &.{ + try lower.encode(.none, .jnz, &.{ .{ .imm = lower.reloc(0, .{ .inst = inst.data.inst.inst }, 0) }, }); - try lower.emit(.none, .jp, &.{ + try lower.encode(.none, .jp, &.{ .{ .imm = lower.reloc(0, .{ .inst = inst.data.inst.inst }, 0) }, }); }, .pseudo_probe_align_ri_s => { - try lower.emit(.none, .@"test", &.{ + try lower.encode(.none, .@"test", &.{ .{ .reg = inst.data.ri.r1 }, .{ .imm = .s(@bitCast(inst.data.ri.i)) }, }); - try lower.emit(.none, .jz, &.{ + try lower.encode(.none, .jz, &.{ .{ .imm = lower.reloc(0, .{ .inst = index + 1 }, 0) }, }); - try lower.emit(.none, .lea, &.{ + try lower.encode(.none, .lea, &.{ .{ .reg = inst.data.ri.r1 }, .{ .mem = Memory.initSib(.qword, .{ .base = .{ .reg = inst.data.ri.r1 }, .disp = -page_size, }) }, }); - try lower.emit(.none, .@"test", &.{ + try lower.encode(.none, .@"test", &.{ .{ .mem = Memory.initSib(.dword, .{ .base = .{ .reg = inst.data.ri.r1 }, }) }, .{ .reg = inst.data.ri.r1.to32() }, }); - try lower.emit(.none, .jmp, &.{ + try lower.encode(.none, .jmp, &.{ .{ .imm = lower.reloc(0, .{ .inst = index }, 0) }, }); assert(lower.result_insts_len == pseudo_probe_align_insts); @@ -229,7 +221,7 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index) Error!struct { .pseudo_probe_adjust_unrolled_ri_s => { var offset = page_size; while (offset < @as(i32, @bitCast(inst.data.ri.i))) : (offset += page_size) { - try lower.emit(.none, .@"test", &.{ + try lower.encode(.none, .@"test", &.{ .{ .mem = Memory.initSib(.dword, .{ .base = .{ .reg = inst.data.ri.r1 }, .disp = -offset, @@ -237,25 +229,25 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index) Error!struct { .{ .reg = inst.data.ri.r1.to32() }, }); } - try lower.emit(.none, .sub, &.{ + try lower.encode(.none, .sub, &.{ .{ .reg = inst.data.ri.r1 }, .{ .imm = .s(@bitCast(inst.data.ri.i)) }, }); assert(lower.result_insts_len <= pseudo_probe_adjust_unrolled_max_insts); }, .pseudo_probe_adjust_setup_rri_s => { - try lower.emit(.none, .mov, &.{ + try lower.encode(.none, .mov, &.{ .{ .reg = inst.data.rri.r2.to32() }, .{ .imm = .s(@bitCast(inst.data.rri.i)) }, }); - try lower.emit(.none, .sub, &.{ + try lower.encode(.none, .sub, &.{ .{ .reg = inst.data.rri.r1 }, .{ .reg = inst.data.rri.r2 }, }); assert(lower.result_insts_len == pseudo_probe_adjust_setup_insts); }, .pseudo_probe_adjust_loop_rr => { - try lower.emit(.none, .@"test", &.{ + try lower.encode(.none, .@"test", &.{ .{ .mem = Memory.initSib(.dword, .{ .base = .{ .reg = inst.data.rr.r1 }, .scale_index = .{ .scale = 1, .index = inst.data.rr.r2 }, @@ -263,11 +255,11 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index) Error!struct { }) }, .{ .reg = inst.data.rr.r1.to32() }, }); - try lower.emit(.none, .sub, &.{ + try lower.encode(.none, .sub, &.{ .{ .reg = inst.data.rr.r2 }, .{ .imm = .s(page_size) }, }); - try lower.emit(.none, .jae, &.{ + try lower.encode(.none, .jae, &.{ .{ .imm = lower.reloc(0, .{ .inst = index }, 0) }, }); assert(lower.result_insts_len == pseudo_probe_adjust_loop_insts); @@ -275,47 +267,47 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index) Error!struct { .pseudo_push_reg_list => try lower.pushPopRegList(.push, inst), .pseudo_pop_reg_list => try lower.pushPopRegList(.pop, inst), - .pseudo_cfi_def_cfa_ri_s => try lower.emit(.directive, .@".cfi_def_cfa", &.{ + .pseudo_cfi_def_cfa_ri_s => try lower.encode(.directive, .@".cfi_def_cfa", &.{ .{ .reg = inst.data.ri.r1 }, .{ .imm = lower.imm(.ri_s, inst.data.ri.i) }, }), - .pseudo_cfi_def_cfa_register_r => try lower.emit(.directive, .@".cfi_def_cfa_register", &.{ + .pseudo_cfi_def_cfa_register_r => try lower.encode(.directive, .@".cfi_def_cfa_register", &.{ .{ .reg = inst.data.r.r1 }, }), - .pseudo_cfi_def_cfa_offset_i_s => try lower.emit(.directive, .@".cfi_def_cfa_offset", &.{ + .pseudo_cfi_def_cfa_offset_i_s => try lower.encode(.directive, .@".cfi_def_cfa_offset", &.{ .{ .imm = lower.imm(.i_s, inst.data.i.i) }, }), - .pseudo_cfi_adjust_cfa_offset_i_s => try lower.emit(.directive, .@".cfi_adjust_cfa_offset", &.{ + .pseudo_cfi_adjust_cfa_offset_i_s => try lower.encode(.directive, .@".cfi_adjust_cfa_offset", &.{ .{ .imm = lower.imm(.i_s, inst.data.i.i) }, }), - .pseudo_cfi_offset_ri_s => try lower.emit(.directive, .@".cfi_offset", &.{ + .pseudo_cfi_offset_ri_s => try lower.encode(.directive, .@".cfi_offset", &.{ .{ .reg = inst.data.ri.r1 }, .{ .imm = lower.imm(.ri_s, inst.data.ri.i) }, }), - .pseudo_cfi_val_offset_ri_s => try lower.emit(.directive, .@".cfi_val_offset", &.{ + .pseudo_cfi_val_offset_ri_s => try lower.encode(.directive, .@".cfi_val_offset", &.{ .{ .reg = inst.data.ri.r1 }, .{ .imm = lower.imm(.ri_s, inst.data.ri.i) }, }), - .pseudo_cfi_rel_offset_ri_s => try lower.emit(.directive, .@".cfi_rel_offset", &.{ + .pseudo_cfi_rel_offset_ri_s => try lower.encode(.directive, .@".cfi_rel_offset", &.{ .{ .reg = inst.data.ri.r1 }, .{ .imm = lower.imm(.ri_s, inst.data.ri.i) }, }), - .pseudo_cfi_register_rr => try lower.emit(.directive, .@".cfi_register", &.{ + .pseudo_cfi_register_rr => try lower.encode(.directive, .@".cfi_register", &.{ .{ .reg = inst.data.rr.r1 }, .{ .reg = inst.data.rr.r2 }, }), - .pseudo_cfi_restore_r => try lower.emit(.directive, .@".cfi_restore", &.{ + .pseudo_cfi_restore_r => try lower.encode(.directive, .@".cfi_restore", &.{ .{ .reg = inst.data.r.r1 }, }), - .pseudo_cfi_undefined_r => try lower.emit(.directive, .@".cfi_undefined", &.{ + .pseudo_cfi_undefined_r => try lower.encode(.directive, .@".cfi_undefined", &.{ .{ .reg = inst.data.r.r1 }, }), - .pseudo_cfi_same_value_r => try lower.emit(.directive, .@".cfi_same_value", &.{ + .pseudo_cfi_same_value_r => try lower.encode(.directive, .@".cfi_same_value", &.{ .{ .reg = inst.data.r.r1 }, }), - .pseudo_cfi_remember_state_none => try lower.emit(.directive, .@".cfi_remember_state", &.{}), - .pseudo_cfi_restore_state_none => try lower.emit(.directive, .@".cfi_restore_state", &.{}), - .pseudo_cfi_escape_bytes => try lower.emit(.directive, .@".cfi_escape", &.{ + .pseudo_cfi_remember_state_none => try lower.encode(.directive, .@".cfi_remember_state", &.{}), + .pseudo_cfi_restore_state_none => try lower.encode(.directive, .@".cfi_restore_state", &.{}), + .pseudo_cfi_escape_bytes => try lower.encode(.directive, .@".cfi_escape", &.{ .{ .bytes = inst.data.bytes.get(lower.mir) }, }), @@ -331,7 +323,6 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index) Error!struct { .pseudo_dbg_arg_i_s, .pseudo_dbg_arg_i_u, .pseudo_dbg_arg_i_64, - .pseudo_dbg_arg_reloc, .pseudo_dbg_arg_ro, .pseudo_dbg_arg_fa, .pseudo_dbg_arg_m, @@ -341,7 +332,6 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index) Error!struct { .pseudo_dbg_var_i_s, .pseudo_dbg_var_i_u, .pseudo_dbg_var_i_64, - .pseudo_dbg_var_reloc, .pseudo_dbg_var_ro, .pseudo_dbg_var_fa, .pseudo_dbg_var_m, @@ -362,7 +352,7 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index) Error!struct { pub fn fail(lower: *Lower, comptime format: []const u8, args: anytype) Error { @branchHint(.cold); assert(lower.err_msg == null); - lower.err_msg = try Zcu.ErrorMsg.create(lower.allocator, lower.src_loc, format, args); + lower.err_msg = try .create(lower.allocator, lower.src_loc, format, args); return error.LowerFail; } @@ -404,13 +394,17 @@ pub fn imm(lower: *const Lower, ops: Mir.Inst.Ops, i: u32) Immediate { }; } -pub fn mem(lower: *Lower, op_index: InstOpIndex, payload: u32) Memory { - var m = lower.mir.resolveFrameLoc(lower.mir.extraData(Mir.Memory, payload).data).decode(); +fn mem(lower: *Lower, op_index: InstOpIndex, payload: u32) Memory { + var m = lower.mir.resolveMemoryExtra(payload).decode(); switch (m) { .sib => |*sib| switch (sib.base) { - else => {}, + .none, .reg, .frame => {}, .table => sib.disp = lower.reloc(op_index, .table, sib.disp).signed, .rip_inst => |inst_index| sib.disp = lower.reloc(op_index, .{ .inst = inst_index }, sib.disp).signed, + .nav => |nav| sib.disp = lower.reloc(op_index, .{ .nav = nav }, sib.disp).signed, + .uav => |uav| sib.disp = lower.reloc(op_index, .{ .uav = uav }, sib.disp).signed, + .lazy_sym => |lazy_sym| sib.disp = lower.reloc(op_index, .{ .lazy_sym = lazy_sym }, sib.disp).signed, + .extern_func => |extern_func| sib.disp = lower.reloc(op_index, .{ .extern_func = extern_func }, sib.disp).signed, }, else => {}, } @@ -428,172 +422,8 @@ fn reloc(lower: *Lower, op_index: InstOpIndex, target: Reloc.Target, off: i32) I return .s(0); } -fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand) Error!void { - const emit_prefix = prefix; - var emit_mnemonic = mnemonic; - var emit_ops_storage: [4]Operand = undefined; - const emit_ops = emit_ops_storage[0..ops.len]; - for (emit_ops, ops, 0..) |*emit_op, op, op_index| { - emit_op.* = switch (op) { - else => op, - .mem => |mem_op| op: switch (mem_op.base()) { - else => op, - .reloc => |sym_index| { - assert(prefix == .none); - assert(mem_op.sib.disp == 0); - assert(mem_op.sib.scale_index.scale == 0); - - if (lower.bin_file.cast(.elf)) |elf_file| { - const zo = elf_file.zigObjectPtr().?; - const elf_sym = zo.symbol(sym_index); - - if (elf_sym.flags.is_tls) { - // TODO handle extern TLS vars, i.e., emit GD model - if (lower.pic) { - // Here, we currently assume local dynamic TLS vars, and so - // we emit LD model. - _ = lower.reloc(1, .{ .linker_tlsld = sym_index }, 0); - lower.result_insts[lower.result_insts_len] = try .new(.none, .lea, &.{ - .{ .reg = .rdi }, - .{ .mem = Memory.initRip(.none, 0) }, - }, lower.target); - lower.result_insts_len += 1; - _ = lower.reloc(0, .{ - .linker_extern_fn = try elf_file.getGlobalSymbol("__tls_get_addr", null), - }, 0); - lower.result_insts[lower.result_insts_len] = try .new(.none, .call, &.{ - .{ .imm = .s(0) }, - }, lower.target); - lower.result_insts_len += 1; - _ = lower.reloc(@intCast(op_index), .{ .linker_dtpoff = sym_index }, 0); - emit_mnemonic = .lea; - break :op .{ .mem = Memory.initSib(.none, .{ - .base = .{ .reg = .rax }, - .disp = std.math.minInt(i32), - }) }; - } else { - // Since we are linking statically, we emit LE model directly. - lower.result_insts[lower.result_insts_len] = try .new(.none, .mov, &.{ - .{ .reg = .rax }, - .{ .mem = Memory.initSib(.qword, .{ .base = .{ .reg = .fs } }) }, - }, lower.target); - lower.result_insts_len += 1; - _ = lower.reloc(@intCast(op_index), .{ .linker_reloc = sym_index }, 0); - emit_mnemonic = .lea; - break :op .{ .mem = Memory.initSib(.none, .{ - .base = .{ .reg = .rax }, - .disp = std.math.minInt(i32), - }) }; - } - } - - if (lower.pic) switch (mnemonic) { - .lea => { - _ = lower.reloc(@intCast(op_index), .{ .linker_reloc = sym_index }, 0); - if (!elf_sym.flags.is_extern_ptr) break :op .{ .mem = Memory.initRip(.none, 0) }; - emit_mnemonic = .mov; - break :op .{ .mem = Memory.initRip(.ptr, 0) }; - }, - .mov => { - if (elf_sym.flags.is_extern_ptr) { - const reg = ops[0].reg; - _ = lower.reloc(1, .{ .linker_reloc = sym_index }, 0); - lower.result_insts[lower.result_insts_len] = try .new(.none, .mov, &.{ - .{ .reg = reg.to64() }, - .{ .mem = Memory.initRip(.qword, 0) }, - }, lower.target); - lower.result_insts_len += 1; - break :op .{ .mem = Memory.initSib(mem_op.sib.ptr_size, .{ .base = .{ - .reg = reg.to64(), - } }) }; - } - _ = lower.reloc(@intCast(op_index), .{ .linker_reloc = sym_index }, 0); - break :op .{ .mem = Memory.initRip(mem_op.sib.ptr_size, 0) }; - }, - else => unreachable, - }; - _ = lower.reloc(@intCast(op_index), .{ .linker_reloc = sym_index }, 0); - switch (mnemonic) { - .call => break :op .{ .mem = Memory.initSib(mem_op.sib.ptr_size, .{ - .base = .{ .reg = .ds }, - }) }, - .lea => { - emit_mnemonic = .mov; - break :op .{ .imm = .s(0) }; - }, - .mov => break :op .{ .mem = Memory.initSib(mem_op.sib.ptr_size, .{ - .base = .{ .reg = .ds }, - }) }, - else => unreachable, - } - } else if (lower.bin_file.cast(.macho)) |macho_file| { - const zo = macho_file.getZigObject().?; - const macho_sym = zo.symbols.items[sym_index]; - - if (macho_sym.flags.tlv) { - _ = lower.reloc(1, .{ .linker_reloc = sym_index }, 0); - lower.result_insts[lower.result_insts_len] = try .new(.none, .mov, &.{ - .{ .reg = .rdi }, - .{ .mem = Memory.initRip(.ptr, 0) }, - }, lower.target); - lower.result_insts_len += 1; - lower.result_insts[lower.result_insts_len] = try .new(.none, .call, &.{ - .{ .mem = Memory.initSib(.qword, .{ .base = .{ .reg = .rdi } }) }, - }, lower.target); - lower.result_insts_len += 1; - emit_mnemonic = .mov; - break :op .{ .reg = .rax }; - } - - break :op switch (mnemonic) { - .lea => { - _ = lower.reloc(@intCast(op_index), .{ .linker_reloc = sym_index }, 0); - if (!macho_sym.flags.is_extern_ptr) break :op .{ .mem = Memory.initRip(.none, 0) }; - emit_mnemonic = .mov; - break :op .{ .mem = Memory.initRip(.ptr, 0) }; - }, - .mov => { - if (macho_sym.flags.is_extern_ptr) { - const reg = ops[0].reg; - _ = lower.reloc(1, .{ .linker_reloc = sym_index }, 0); - lower.result_insts[lower.result_insts_len] = try .new(.none, .mov, &.{ - .{ .reg = reg.to64() }, - .{ .mem = Memory.initRip(.qword, 0) }, - }, lower.target); - lower.result_insts_len += 1; - break :op .{ .mem = Memory.initSib(mem_op.sib.ptr_size, .{ .base = .{ - .reg = reg.to64(), - } }) }; - } - _ = lower.reloc(@intCast(op_index), .{ .linker_reloc = sym_index }, 0); - break :op .{ .mem = Memory.initRip(mem_op.sib.ptr_size, 0) }; - }, - else => unreachable, - }; - } else { - return lower.fail("TODO: bin format '{s}'", .{@tagName(lower.bin_file.tag)}); - } - }, - .pcrel => |sym_index| { - assert(prefix == .none); - assert(mem_op.sib.disp == 0); - assert(mem_op.sib.scale_index.scale == 0); - - _ = lower.reloc(@intCast(op_index), .{ .linker_pcrel = sym_index }, 0); - break :op switch (lower.bin_file.tag) { - .elf => op, - .macho => switch (mnemonic) { - .lea => .{ .mem = Memory.initRip(.none, 0) }, - .mov => .{ .mem = Memory.initRip(mem_op.sib.ptr_size, 0) }, - else => unreachable, - }, - else => |tag| return lower.fail("TODO: bin format '{s}'", .{@tagName(tag)}), - }; - }, - }, - }; - } - lower.result_insts[lower.result_insts_len] = try .new(emit_prefix, emit_mnemonic, emit_ops, lower.target); +fn encode(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand) Error!void { + lower.result_insts[lower.result_insts_len] = try .new(prefix, mnemonic, ops, lower.target); lower.result_insts_len += 1; } @@ -618,10 +448,10 @@ fn generic(lower: *Lower, inst: Mir.Inst) Error!void { .rrmi => inst.data.rrix.fixes, .mi_u, .mi_s => inst.data.x.fixes, .m => inst.data.x.fixes, - .extern_fn_reloc, .got_reloc, .direct_reloc, .import_reloc, .tlv_reloc, .rel => ._, + .nav, .uav, .lazy_sym, .extern_func => ._, else => return lower.fail("TODO lower .{s}", .{@tagName(inst.ops)}), }; - try lower.emit(switch (fixes) { + try lower.encode(switch (fixes) { inline else => |tag| comptime if (std.mem.indexOfScalar(u8, @tagName(tag), ' ')) |space| @field(Prefix, @tagName(tag)[0..space]) else @@ -752,22 +582,17 @@ fn generic(lower: *Lower, inst: Mir.Inst) Error!void { .{ .mem = lower.mem(2, inst.data.rrix.payload) }, .{ .imm = lower.imm(inst.ops, inst.data.rrix.i) }, }, - .extern_fn_reloc, .rel => &.{ - .{ .imm = lower.reloc(0, .{ .linker_extern_fn = inst.data.reloc.sym_index }, inst.data.reloc.off) }, + .nav => &.{ + .{ .imm = lower.reloc(0, .{ .nav = inst.data.nav.index }, inst.data.nav.off) }, }, - .got_reloc, .direct_reloc, .import_reloc => ops: { - const reg = inst.data.rx.r1; - const extra = lower.mir.extraData(bits.SymbolOffset, inst.data.rx.payload).data; - _ = lower.reloc(1, switch (inst.ops) { - .got_reloc => .{ .linker_got = extra.sym_index }, - .direct_reloc => .{ .linker_direct = extra.sym_index }, - .import_reloc => .{ .linker_import = extra.sym_index }, - else => unreachable, - }, extra.off); - break :ops &.{ - .{ .reg = reg }, - .{ .mem = Memory.initRip(Memory.PtrSize.fromBitSize(reg.bitSize()), 0) }, - }; + .uav => &.{ + .{ .imm = lower.reloc(0, .{ .uav = inst.data.uav }, 0) }, + }, + .lazy_sym => &.{ + .{ .imm = lower.reloc(0, .{ .lazy_sym = inst.data.lazy_sym }, 0) }, + }, + .extern_func => &.{ + .{ .imm = lower.reloc(0, .{ .extern_func = inst.data.extern_func }, 0) }, }, else => return lower.fail("TODO lower {s} {s}", .{ @tagName(inst.tag), @tagName(inst.ops) }), }); @@ -787,7 +612,7 @@ fn pushPopRegList(lower: *Lower, comptime mnemonic: Mnemonic, inst: Mir.Inst) Er else => unreachable, } }); while (it.next()) |i| { - try lower.emit(.none, mnemonic, &.{.{ + try lower.encode(.none, mnemonic, &.{.{ .reg = callee_preserved_regs[i], }}); switch (mnemonic) { @@ -801,7 +626,7 @@ fn pushPopRegList(lower: *Lower, comptime mnemonic: Mnemonic, inst: Mir.Inst) Er .push => { var it = inst.data.reg_list.iterator(.{}); while (it.next()) |i| { - try lower.emit(.directive, .@".cfi_rel_offset", &.{ + try lower.encode(.directive, .@".cfi_rel_offset", &.{ .{ .reg = callee_preserved_regs[i] }, .{ .imm = .s(off) }, }); @@ -819,12 +644,14 @@ const page_size: i32 = 1 << 12; const abi = @import("abi.zig"); const assert = std.debug.assert; const bits = @import("bits.zig"); +const codegen = @import("../../codegen.zig"); const encoder = @import("encoder.zig"); const link = @import("../../link.zig"); const std = @import("std"); const Immediate = Instruction.Immediate; const Instruction = encoder.Instruction; +const InternPool = @import("../../InternPool.zig"); const Lower = @This(); const Memory = Instruction.Memory; const Mir = @import("Mir.zig"); @@ -833,3 +660,4 @@ const Zcu = @import("../../Zcu.zig"); const Operand = Instruction.Operand; const Prefix = Instruction.Prefix; const Register = bits.Register; +const Type = @import("../../Type.zig"); diff --git a/src/arch/x86_64/Mir.zig b/src/arch/x86_64/Mir.zig index 24d5c6a3ed8a80991be12f4603b791b80b5b5657..70f809068552187f0054f355b7c8513dc4016670 100644 --- a/src/arch/x86_64/Mir.zig +++ b/src/arch/x86_64/Mir.zig @@ -9,8 +9,8 @@ instructions: std.MultiArrayList(Inst).Slice, /// The meaning of this data is determined by `Inst.Tag` value. extra: []const u32, -local_name_bytes: []const u8, -local_types: []const InternPool.Index, +string_bytes: []const u8, +locals: []const Local, table: []const Inst.Index, frame_locs: std.MultiArrayList(FrameLoc).Slice, @@ -1363,9 +1363,6 @@ pub const Inst = struct { /// Immediate (byte), register operands. /// Uses `ri` payload. ir, - /// Relative displacement operand. - /// Uses `reloc` payload. - rel, /// Register, memory operands. /// Uses `rx` payload with extra data of type `Memory`. rm, @@ -1411,21 +1408,18 @@ pub const Inst = struct { /// References another Mir instruction directly. /// Uses `inst` payload. inst, - /// Linker relocation - external function. - /// Uses `reloc` payload. - extern_fn_reloc, - /// Linker relocation - GOT indirection. - /// Uses `rx` payload with extra data of type `bits.SymbolOffset`. - got_reloc, - /// Linker relocation - direct reference. - /// Uses `rx` payload with extra data of type `bits.SymbolOffset`. - direct_reloc, - /// Linker relocation - imports table indirection (binding). - /// Uses `rx` payload with extra data of type `bits.SymbolOffset`. - import_reloc, - /// Linker relocation - threadlocal variable via GOT indirection. - /// Uses `rx` payload with extra data of type `bits.SymbolOffset`. - tlv_reloc, + /// References a nav. + /// Uses `nav` payload. + nav, + /// References an uav. + /// Uses `uav` payload. + uav, + /// References a lazy symbol. + /// Uses `lazy_sym` payload. + lazy_sym, + /// References an external symbol. + /// Uses `extern_func` payload. + extern_func, // Pseudo instructions: @@ -1560,9 +1554,6 @@ pub const Inst = struct { /// Uses `i64` payload. pseudo_dbg_arg_i_64, /// Local argument. - /// Uses `reloc` payload. - pseudo_dbg_arg_reloc, - /// Local argument. /// Uses `ro` payload. pseudo_dbg_arg_ro, /// Local argument. @@ -1589,9 +1580,6 @@ pub const Inst = struct { /// Uses `i64` payload. pseudo_dbg_var_i_64, /// Local variable. - /// Uses `reloc` payload. - pseudo_dbg_var_reloc, - /// Local variable. /// Uses `ro` payload. pseudo_dbg_var_ro, /// Local variable. @@ -1719,12 +1707,12 @@ pub const Inst = struct { return std.mem.sliceAsBytes(mir.extra[bytes.payload..])[0..bytes.len]; } }, - /// Relocation for the linker where: - /// * `sym_index` is the index of the target - /// * `off` is the offset from the target - reloc: bits.SymbolOffset, fa: bits.FrameAddr, ro: bits.RegisterOffset, + nav: bits.NavOffset, + uav: InternPool.Key.Ptr.BaseAddr.Uav, + lazy_sym: link.File.LazySymbol, + extern_func: Mir.NullTerminatedString, /// Debug line and column position line_column: struct { line: u32, @@ -1787,7 +1775,7 @@ pub const Inst = struct { pub const RegisterList = struct { bitset: BitSet, - const BitSet = IntegerBitSet(32); + const BitSet = std.bit_set.IntegerBitSet(32); const Self = @This(); pub const empty: RegisterList = .{ .bitset = .initEmpty() }; @@ -1826,6 +1814,22 @@ pub const RegisterList = struct { } }; +pub const NullTerminatedString = enum(u32) { + none = std.math.maxInt(u32), + _, + + pub fn toSlice(nts: NullTerminatedString, mir: *const Mir) ?[:0]const u8 { + if (nts == .none) return null; + const string_bytes = mir.string_bytes[@intFromEnum(nts)..]; + return string_bytes[0..std.mem.indexOfScalar(u8, string_bytes, 0).? :0]; + } +}; + +pub const Local = struct { + name: NullTerminatedString, + type: InternPool.Index, +}; + pub const Imm32 = struct { imm: u32, }; @@ -1861,11 +1865,10 @@ pub const Memory = struct { size: bits.Memory.Size, index: Register, scale: bits.Memory.Scale, - _: u14 = undefined, + _: u13 = undefined, }; pub fn encode(mem: bits.Memory) Memory { - assert(mem.base != .reloc or mem.mod != .off); return .{ .info = .{ .base = mem.base, @@ -1887,17 +1890,27 @@ pub const Memory = struct { .none, .table => undefined, .reg => |reg| @intFromEnum(reg), .frame => |frame_index| @intFromEnum(frame_index), - .reloc, .pcrel => |sym_index| sym_index, .rip_inst => |inst_index| inst_index, + .nav => |nav| @intFromEnum(nav), + .uav => |uav| @intFromEnum(uav.val), + .lazy_sym => |lazy_sym| @intFromEnum(lazy_sym.ty), + .extern_func => |extern_func| @intFromEnum(extern_func), }, .off = switch (mem.mod) { .rm => |rm| @bitCast(rm.disp), .off => |off| @truncate(off), }, - .extra = if (mem.mod == .off) - @intCast(mem.mod.off >> 32) - else - undefined, + .extra = switch (mem.mod) { + .rm => switch (mem.base) { + else => undefined, + .uav => |uav| @intFromEnum(uav.orig_ty), + .lazy_sym => |lazy_sym| @intFromEnum(lazy_sym.kind), + }, + .off => switch (mem.base) { + .reg => @intCast(mem.mod.off >> 32), + else => unreachable, + }, + }, }; } @@ -1915,9 +1928,11 @@ pub const Memory = struct { .reg => .{ .reg = @enumFromInt(mem.base) }, .frame => .{ .frame = @enumFromInt(mem.base) }, .table => .table, - .reloc => .{ .reloc = mem.base }, - .pcrel => .{ .pcrel = mem.base }, .rip_inst => .{ .rip_inst = mem.base }, + .nav => .{ .nav = @enumFromInt(mem.base) }, + .uav => .{ .uav = .{ .val = @enumFromInt(mem.base), .orig_ty = @enumFromInt(mem.extra) } }, + .lazy_sym => .{ .lazy_sym = .{ .kind = @enumFromInt(mem.extra), .ty = @enumFromInt(mem.base) } }, + .extern_func => .{ .extern_func = @enumFromInt(mem.base) }, }, .scale_index = switch (mem.info.index) { .none => null, @@ -1945,8 +1960,8 @@ pub const Memory = struct { pub fn deinit(mir: *Mir, gpa: std.mem.Allocator) void { mir.instructions.deinit(gpa); gpa.free(mir.extra); - gpa.free(mir.local_name_bytes); - gpa.free(mir.local_types); + gpa.free(mir.string_bytes); + gpa.free(mir.locals); gpa.free(mir.table); mir.frame_locs.deinit(gpa); mir.* = undefined; @@ -1970,16 +1985,15 @@ pub fn emit( const mod = zcu.navFileScope(nav).mod.?; var e: Emit = .{ .lower = .{ - .bin_file = lf, .target = &mod.resolved_target.result, .allocator = gpa, .mir = mir, .cc = fn_info.cc, .src_loc = src_loc, - .output_mode = comp.config.output_mode, - .link_mode = comp.config.link_mode, - .pic = mod.pic, }, + .bin_file = lf, + .pt = pt, + .pic = mod.pic, .atom_index = sym: { if (lf.cast(.elf)) |ef| break :sym try ef.zigObjectPtr().?.getOrCreateMetadataForNav(zcu, nav); if (lf.cast(.macho)) |mf| break :sym try mf.getZigObject().?.getOrCreateMetadataForNav(mf, nav); @@ -1992,6 +2006,7 @@ pub fn emit( }, .debug_output = debug_output, .code = code, + .prev_di_loc = .{ .line = func.lbrace_line, .column = func.lbrace_column, @@ -2002,7 +2017,12 @@ pub fn emit( }, }, .prev_di_pc = 0, + + .code_offset_mapping = .empty, + .relocs = .empty, + .table_relocs = .empty, }; + defer e.deinit(); e.emitMir() catch |err| switch (err) { error.LowerFail, error.EmitFail => return zcu.codegenFailMsg(nav, e.lower.err_msg.?), error.InvalidInstruction, error.CannotEncode => return zcu.codegenFail(nav, "emit MIR failed: {s} (Zig compiler bug)", .{@errorName(err)}), @@ -2010,6 +2030,62 @@ pub fn emit( }; } +pub fn emitLazy( + mir: Mir, + lf: *link.File, + pt: Zcu.PerThread, + src_loc: Zcu.LazySrcLoc, + lazy_sym: link.File.LazySymbol, + code: *std.ArrayListUnmanaged(u8), + debug_output: link.File.DebugInfoOutput, +) codegen.CodeGenError!void { + const zcu = pt.zcu; + const comp = zcu.comp; + const gpa = comp.gpa; + const mod = comp.root_mod; + var e: Emit = .{ + .lower = .{ + .target = &mod.resolved_target.result, + .allocator = gpa, + .mir = mir, + .cc = .auto, + .src_loc = src_loc, + }, + .bin_file = lf, + .pt = pt, + .pic = mod.pic, + .atom_index = sym: { + if (lf.cast(.elf)) |ef| break :sym ef.zigObjectPtr().?.getOrCreateMetadataForLazySymbol(ef, pt, lazy_sym) catch |err| + return zcu.codegenFailType(lazy_sym.ty, "{s} creating lazy symbol", .{@errorName(err)}); + if (lf.cast(.macho)) |mf| break :sym mf.getZigObject().?.getOrCreateMetadataForLazySymbol(mf, pt, lazy_sym) catch |err| + return zcu.codegenFailType(lazy_sym.ty, "{s} creating lazy symbol", .{@errorName(err)}); + if (lf.cast(.coff)) |cf| { + const atom = cf.getOrCreateAtomForLazySymbol(pt, lazy_sym) catch |err| + return zcu.codegenFailType(lazy_sym.ty, "{s} creating lazy symbol", .{@errorName(err)}); + break :sym cf.getAtom(atom).getSymbolIndex().?; + } + if (lf.cast(.plan9)) |p9f| break :sym p9f.getOrCreateAtomForLazySymbol(pt, lazy_sym) catch |err| + return zcu.codegenFailType(lazy_sym.ty, "{s} creating lazy symbol", .{@errorName(err)}); + unreachable; + }, + .debug_output = debug_output, + .code = code, + + .prev_di_loc = undefined, + .prev_di_pc = undefined, + + .code_offset_mapping = .empty, + .relocs = .empty, + .table_relocs = .empty, + }; + defer e.deinit(); + e.emitMir() catch |err| switch (err) { + error.LowerFail, error.EmitFail => return zcu.codegenFailTypeMsg(lazy_sym.ty, e.lower.err_msg.?), + error.InvalidInstruction, error.CannotEncode => return zcu.codegenFailType(lazy_sym.ty, "emit MIR failed: {s} (Zig compiler bug)", .{@errorName(err)}), + else => return zcu.codegenFailType(lazy_sym.ty, "emit MIR failed: {s}", .{@errorName(err)}), + }; +} + pub fn extraData(mir: Mir, comptime T: type, index: u32) struct { data: T, end: u32 } { const fields = std.meta.fields(T); var i: u32 = index; @@ -2039,9 +2115,10 @@ pub fn resolveFrameAddr(mir: Mir, frame_addr: bits.FrameAddr) bits.RegisterOffse return .{ .reg = frame_loc.base, .off = frame_loc.disp + frame_addr.off }; } -pub fn resolveFrameLoc(mir: Mir, mem: Memory) Memory { +pub fn resolveMemoryExtra(mir: Mir, payload: u32) Memory { + const mem = mir.extraData(Mir.Memory, payload).data; return switch (mem.info.base) { - .none, .reg, .table, .reloc, .pcrel, .rip_inst => mem, + .none, .reg, .table, .rip_inst, .nav, .uav, .lazy_sym, .extern_func => mem, .frame => if (mir.frame_locs.len > 0) .{ .info = .{ .base = .reg, @@ -2063,7 +2140,6 @@ const builtin = @import("builtin"); const encoder = @import("encoder.zig"); const std = @import("std"); -const IntegerBitSet = std.bit_set.IntegerBitSet; const InternPool = @import("../../InternPool.zig"); const Mir = @This(); const Register = bits.Register; diff --git a/src/arch/x86_64/bits.zig b/src/arch/x86_64/bits.zig index 63b5d4b238c296829edd5986ecf26542fd7d6142..53080598e58dc128d8641d86f010fb879b2ec38f 100644 --- a/src/arch/x86_64/bits.zig +++ b/src/arch/x86_64/bits.zig @@ -4,6 +4,8 @@ const expect = std.testing.expect; const Allocator = std.mem.Allocator; const ArrayList = std.ArrayList; +const InternPool = @import("../../InternPool.zig"); +const link = @import("../../link.zig"); const Mir = @import("Mir.zig"); /// EFLAGS condition codes @@ -750,20 +752,22 @@ pub const FrameAddr = struct { index: FrameIndex, off: i32 = 0 }; pub const RegisterOffset = struct { reg: Register, off: i32 = 0 }; -pub const SymbolOffset = struct { sym_index: u32, off: i32 = 0 }; +pub const NavOffset = struct { index: InternPool.Nav.Index, off: i32 = 0 }; pub const Memory = struct { base: Base = .none, mod: Mod = .{ .rm = .{} }, - pub const Base = union(enum(u3)) { + pub const Base = union(enum(u4)) { none, reg: Register, frame: FrameIndex, table, - reloc: u32, - pcrel: u32, rip_inst: Mir.Inst.Index, + nav: InternPool.Nav.Index, + uav: InternPool.Key.Ptr.BaseAddr.Uav, + lazy_sym: link.File.LazySymbol, + extern_func: Mir.NullTerminatedString, pub const Tag = @typeInfo(Base).@"union".tag_type.?; }; @@ -899,7 +903,10 @@ pub const Memory = struct { pub const Immediate = union(enum) { signed: i32, unsigned: u64, - reloc: SymbolOffset, + nav: NavOffset, + uav: InternPool.Key.Ptr.BaseAddr.Uav, + lazy_sym: link.File.LazySymbol, + extern_func: Mir.NullTerminatedString, pub fn u(x: u64) Immediate { return .{ .unsigned = x }; @@ -909,10 +916,6 @@ pub const Immediate = union(enum) { return .{ .signed = x }; } - pub fn rel(sym_off: SymbolOffset) Immediate { - return .{ .reloc = sym_off }; - } - pub fn format( imm: Immediate, comptime _: []const u8, @@ -921,7 +924,10 @@ pub const Immediate = union(enum) { ) @TypeOf(writer).Error!void { switch (imm) { inline else => |int| try writer.print("{d}", .{int}), - .reloc => |sym_off| try writer.print("Symbol({[sym_index]d}) + {[off]d}", sym_off), + .nav => |nav_off| try writer.print("Nav({d}) + {d}", .{ @intFromEnum(nav_off.nav), nav_off.off }), + .uav => |uav| try writer.print("Uav({d})", .{@intFromEnum(uav.val)}), + .lazy_sym => |lazy_sym| try writer.print("LazySym({s}, {d})", .{ @tagName(lazy_sym.kind), @intFromEnum(lazy_sym.ty) }), + .extern_func => |extern_func| try writer.print("ExternFunc({d})", .{@intFromEnum(extern_func)}), } } }; diff --git a/src/arch/x86_64/encoder.zig b/src/arch/x86_64/encoder.zig index cb1272fba0a4fcad84c8b67b60d198e407de97d3..8d07dce83a8b66ac71c71a9b5988846905ee9d30 100644 --- a/src/arch/x86_64/encoder.zig +++ b/src/arch/x86_64/encoder.zig @@ -138,7 +138,7 @@ pub const Instruction = struct { .moffs => true, .rip => false, .sib => |s| switch (s.base) { - .none, .frame, .table, .reloc, .pcrel, .rip_inst => false, + .none, .frame, .table, .rip_inst, .nav, .uav, .lazy_sym, .extern_func => false, .reg => |reg| reg.isClass(.segment), }, }; @@ -211,7 +211,7 @@ pub const Instruction = struct { .none, .imm => 0b00, .reg => |reg| @truncate(reg.enc() >> 3), .mem => |mem| switch (mem.base()) { - .none, .frame, .table, .reloc, .pcrel, .rip_inst => 0b00, // rsp, rbp, and rip are not extended + .none, .frame, .table, .rip_inst, .nav, .uav, .lazy_sym, .extern_func => 0b00, // rsp, rbp, and rip are not extended .reg => |reg| @truncate(reg.enc() >> 3), }, .bytes => unreachable, @@ -281,9 +281,14 @@ pub const Instruction = struct { .reg => |reg| try writer.print("{s}", .{@tagName(reg)}), .frame => |frame_index| try writer.print("{}", .{frame_index}), .table => try writer.print("Table", .{}), - .reloc => |sym_index| try writer.print("Symbol({d})", .{sym_index}), - .pcrel => |sym_index| try writer.print("PcRelSymbol({d})", .{sym_index}), .rip_inst => |inst_index| try writer.print("RipInst({d})", .{inst_index}), + .nav => |nav| try writer.print("Nav({d})", .{@intFromEnum(nav)}), + .uav => |uav| try writer.print("Uav({d})", .{@intFromEnum(uav.val)}), + .lazy_sym => |lazy_sym| try writer.print("LazySym({s}, {d})", .{ + @tagName(lazy_sym.kind), + @intFromEnum(lazy_sym.ty), + }), + .extern_func => |extern_func| try writer.print("ExternFunc({d})", .{@intFromEnum(extern_func)}), } if (mem.scaleIndex()) |si| { if (any) try writer.writeAll(" + "); @@ -718,11 +723,11 @@ pub const Instruction = struct { try encoder.modRm_indirectDisp32(operand_enc, 0); try encoder.disp32(undefined); } else return error.CannotEncode, - .reloc => if (@TypeOf(encoder).options.allow_symbols) { + .nav, .uav, .lazy_sym, .extern_func => if (@TypeOf(encoder).options.allow_symbols) { try encoder.modRm_indirectDisp32(operand_enc, 0); try encoder.disp32(undefined); } else return error.CannotEncode, - .pcrel, .rip_inst => { + .rip_inst => { try encoder.modRm_RIPDisp32(operand_enc); try encoder.disp32(sib.disp); }, diff --git a/src/codegen.zig b/src/codegen.zig index 9199c27dc2aec52c59d771d6d7900b25807945e1..a977d3003fae7034dace76763b600f456d87a906 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -951,18 +951,17 @@ pub const GenResult = union(enum) { }; }; -fn genNavRef( +pub fn genNavRef( lf: *link.File, pt: Zcu.PerThread, src_loc: Zcu.LazySrcLoc, - val: Value, + ty: Type, nav_index: InternPool.Nav.Index, target: std.Target, ) CodeGenError!GenResult { const zcu = pt.zcu; const ip = &zcu.intern_pool; - const ty = val.typeOf(zcu); - log.debug("genNavRef: val = {}", .{val.fmtValue(pt)}); + log.debug("genNavRef: ty = {}", .{ty.fmt(pt)}); if (!ty.isFnOrHasRuntimeBitsIgnoreComptime(zcu)) { const imm: u64 = switch (@divExact(target.ptrBitWidth(), 8)) { @@ -991,12 +990,10 @@ fn genNavRef( } const nav = ip.getNav(nav_index); - assert(!nav.isThreadlocal(ip)); - - const lib_name, const linkage, const visibility = if (nav.getExtern(ip)) |e| - .{ e.lib_name, e.linkage, e.visibility } + const lib_name, const linkage, const is_threadlocal = if (nav.getExtern(ip)) |e| + .{ e.lib_name, e.linkage, e.is_threadlocal and !zcu.navFileScope(nav_index).mod.?.single_threaded } else - .{ .none, .internal, .default }; + .{ .none, .internal, false }; const name = nav.name; if (lf.cast(.elf)) |elf_file| { @@ -1004,6 +1001,7 @@ fn genNavRef( switch (linkage) { .internal => { const sym_index = try zo.getOrCreateMetadataForNav(zcu, nav_index); + if (is_threadlocal) zo.symbol(sym_index).flags.is_tls = true; return .{ .mcv = .{ .lea_symbol = sym_index } }; }, .strong, .weak => { @@ -1014,10 +1012,7 @@ fn genNavRef( .weak => zo.symbol(sym_index).flags.weak = true, .link_once => unreachable, } - switch (visibility) { - .default => zo.symbol(sym_index).flags.is_extern_ptr = true, - .hidden, .protected => {}, - } + if (is_threadlocal) zo.symbol(sym_index).flags.is_tls = true; return .{ .mcv = .{ .lea_symbol = sym_index } }; }, .link_once => unreachable, @@ -1027,8 +1022,8 @@ fn genNavRef( switch (linkage) { .internal => { const sym_index = try zo.getOrCreateMetadataForNav(macho_file, nav_index); - const sym = zo.symbols.items[sym_index]; - return .{ .mcv = .{ .lea_symbol = sym.nlist_idx } }; + if (is_threadlocal) zo.symbols.items[sym_index].flags.tlv = true; + return .{ .mcv = .{ .lea_symbol = sym_index } }; }, .strong, .weak => { const sym_index = try macho_file.getGlobalSymbol(name.toSlice(ip), lib_name.toSlice(ip)); @@ -1038,10 +1033,7 @@ fn genNavRef( .weak => zo.symbols.items[sym_index].flags.weak = true, .link_once => unreachable, } - switch (visibility) { - .default => zo.symbols.items[sym_index].flags.is_extern_ptr = true, - .hidden, .protected => {}, - } + if (is_threadlocal) zo.symbols.items[sym_index].flags.tlv = true; return .{ .mcv = .{ .lea_symbol = sym_index } }; }, .link_once => unreachable, @@ -1071,6 +1063,7 @@ fn genNavRef( } } +/// deprecated legacy code path pub fn genTypedValue( lf: *link.File, pt: Zcu.PerThread, @@ -1078,45 +1071,97 @@ pub fn genTypedValue( val: Value, target: std.Target, ) CodeGenError!GenResult { + const ip = &pt.zcu.intern_pool; + return switch (try lowerValue(pt, val, &target)) { + .none => .{ .mcv = .none }, + .undef => .{ .mcv = .undef }, + .immediate => |imm| .{ .mcv = .{ .immediate = imm } }, + .lea_nav => |nav| genNavRef(lf, pt, src_loc, .fromInterned(ip.getNav(nav).typeOf(ip)), nav, target), + .lea_uav => |uav| switch (try lf.lowerUav( + pt, + uav.val, + Type.fromInterned(uav.orig_ty).ptrAlignment(pt.zcu), + src_loc, + )) { + .mcv => |mcv| .{ .mcv = switch (mcv) { + else => unreachable, + .load_direct => |sym_index| .{ .lea_direct = sym_index }, + .load_symbol => |sym_index| .{ .lea_symbol = sym_index }, + } }, + .fail => |em| .{ .fail = em }, + }, + .load_uav => |uav| lf.lowerUav( + pt, + uav.val, + Type.fromInterned(uav.orig_ty).ptrAlignment(pt.zcu), + src_loc, + ), + }; +} + +const LowerResult = union(enum) { + none, + undef, + /// The bit-width of the immediate may be smaller than `u64`. For example, on 32-bit targets + /// such as ARM, the immediate will never exceed 32-bits. + immediate: u64, + lea_nav: InternPool.Nav.Index, + lea_uav: InternPool.Key.Ptr.BaseAddr.Uav, + load_uav: InternPool.Key.Ptr.BaseAddr.Uav, +}; + +pub fn lowerValue(pt: Zcu.PerThread, val: Value, target: *const std.Target) Allocator.Error!LowerResult { const zcu = pt.zcu; const ip = &zcu.intern_pool; const ty = val.typeOf(zcu); - log.debug("genTypedValue: val = {}", .{val.fmtValue(pt)}); + log.debug("lowerValue(@as({}, {}))", .{ ty.fmt(pt), val.fmtValue(pt) }); - if (val.isUndef(zcu)) return .{ .mcv = .undef }; + if (val.isUndef(zcu)) return .undef; switch (ty.zigTypeTag(zcu)) { - .void => return .{ .mcv = .none }, + .void => return .none, .pointer => switch (ty.ptrSize(zcu)) { .slice => {}, else => switch (val.toIntern()) { .null_value => { - return .{ .mcv = .{ .immediate = 0 } }; + return .{ .immediate = 0 }; }, else => switch (ip.indexToKey(val.toIntern())) { .int => { - return .{ .mcv = .{ .immediate = val.toUnsignedInt(zcu) } }; + return .{ .immediate = val.toUnsignedInt(zcu) }; }, .ptr => |ptr| if (ptr.byte_offset == 0) switch (ptr.base_addr) { - .nav => |nav| return genNavRef(lf, pt, src_loc, val, nav, target), + .nav => |nav| { + if (!ty.isFnOrHasRuntimeBitsIgnoreComptime(zcu)) { + const imm: u64 = switch (@divExact(target.ptrBitWidth(), 8)) { + 1 => 0xaa, + 2 => 0xaaaa, + 4 => 0xaaaaaaaa, + 8 => 0xaaaaaaaaaaaaaaaa, + else => unreachable, + }; + return .{ .immediate = imm }; + } + + if (ty.castPtrToFn(zcu)) |fn_ty| { + if (zcu.typeToFunc(fn_ty).?.is_generic) { + return .{ .immediate = fn_ty.abiAlignment(zcu).toByteUnits().? }; + } + } else if (ty.zigTypeTag(zcu) == .pointer) { + const elem_ty = ty.elemType2(zcu); + if (!elem_ty.hasRuntimeBits(zcu)) { + return .{ .immediate = elem_ty.abiAlignment(zcu).toByteUnits().? }; + } + } + + return .{ .lea_nav = nav }; + }, .uav => |uav| if (Value.fromInterned(uav.val).typeOf(zcu).hasRuntimeBits(zcu)) - return switch (try lf.lowerUav( - pt, - uav.val, - Type.fromInterned(uav.orig_ty).ptrAlignment(zcu), - src_loc, - )) { - .mcv => |mcv| return .{ .mcv = switch (mcv) { - .load_direct => |sym_index| .{ .lea_direct = sym_index }, - .load_symbol => |sym_index| .{ .lea_symbol = sym_index }, - else => unreachable, - } }, - .fail => |em| return .{ .fail = em }, - } + return .{ .lea_uav = uav } else - return .{ .mcv = .{ .immediate = Type.fromInterned(uav.orig_ty).ptrAlignment(zcu) - .forward(@intCast((@as(u66, 1) << @intCast(target.ptrBitWidth() | 1)) / 3)) } }, + return .{ .immediate = Type.fromInterned(uav.orig_ty).ptrAlignment(zcu) + .forward(@intCast((@as(u66, 1) << @intCast(target.ptrBitWidth() | 1)) / 3)) }, else => {}, }, else => {}, @@ -1130,39 +1175,35 @@ pub fn genTypedValue( .signed => @bitCast(val.toSignedInt(zcu)), .unsigned => val.toUnsignedInt(zcu), }; - return .{ .mcv = .{ .immediate = unsigned } }; + return .{ .immediate = unsigned }; } }, .bool => { - return .{ .mcv = .{ .immediate = @intFromBool(val.toBool()) } }; + return .{ .immediate = @intFromBool(val.toBool()) }; }, .optional => { if (ty.isPtrLikeOptional(zcu)) { - return genTypedValue( - lf, + return lowerValue( pt, - src_loc, - val.optionalValue(zcu) orelse return .{ .mcv = .{ .immediate = 0 } }, + val.optionalValue(zcu) orelse return .{ .immediate = 0 }, target, ); } else if (ty.abiSize(zcu) == 1) { - return .{ .mcv = .{ .immediate = @intFromBool(!val.isNull(zcu)) } }; + return .{ .immediate = @intFromBool(!val.isNull(zcu)) }; } }, .@"enum" => { const enum_tag = ip.indexToKey(val.toIntern()).enum_tag; - return genTypedValue( - lf, + return lowerValue( pt, - src_loc, Value.fromInterned(enum_tag.int), target, ); }, .error_set => { const err_name = ip.indexToKey(val.toIntern()).err.name; - const error_index = try pt.getErrorValue(err_name); - return .{ .mcv = .{ .immediate = error_index } }; + const error_index = ip.getErrorValueIfExists(err_name).?; + return .{ .immediate = error_index }; }, .error_union => { const err_type = ty.errorUnionSet(zcu); @@ -1171,20 +1212,16 @@ pub fn genTypedValue( // We use the error type directly as the type. const err_int_ty = try pt.errorIntType(); switch (ip.indexToKey(val.toIntern()).error_union.val) { - .err_name => |err_name| return genTypedValue( - lf, + .err_name => |err_name| return lowerValue( pt, - src_loc, Value.fromInterned(try pt.intern(.{ .err = .{ .ty = err_type.toIntern(), .name = err_name, } })), target, ), - .payload => return genTypedValue( - lf, + .payload => return lowerValue( pt, - src_loc, try pt.intValue(err_int_ty, 0), target, ), @@ -1204,7 +1241,10 @@ pub fn genTypedValue( else => {}, } - return lf.lowerUav(pt, val.toIntern(), .none, src_loc); + return .{ .load_uav = .{ + .val = val.toIntern(), + .orig_ty = (try pt.singleConstPtrType(ty)).toIntern(), + } }; } pub fn errUnionPayloadOffset(payload_ty: Type, zcu: *Zcu) u64 { diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index 42d0d74ec5ebd51c0d986ef466c4117f7faa476e..0afe10ef03dba7d2f4d101e1f779f19034a7008d 100644 --- a/src/link/Dwarf.zig +++ b/src/link/Dwarf.zig @@ -1478,16 +1478,16 @@ pub const WipNav = struct { pub fn genLocalVarDebugInfo( wip_nav: *WipNav, tag: LocalVarTag, - name: []const u8, + opt_name: ?[]const u8, ty: Type, loc: Loc, ) UpdateError!void { assert(wip_nav.func != .none); try wip_nav.abbrevCode(switch (tag) { - .arg => .arg, - .local_var => .local_var, + .arg => if (opt_name) |_| .arg else .unnamed_arg, + .local_var => if (opt_name) |_| .local_var else unreachable, }); - try wip_nav.strp(name); + if (opt_name) |name| try wip_nav.strp(name); try wip_nav.refType(ty); try wip_nav.infoExprLoc(loc); wip_nav.any_children = true; @@ -1498,7 +1498,7 @@ pub const WipNav = struct { wip_nav: *WipNav, src_loc: Zcu.LazySrcLoc, tag: LocalConstTag, - name: []const u8, + opt_name: ?[]const u8, val: Value, ) UpdateError!void { assert(wip_nav.func != .none); @@ -1508,19 +1508,19 @@ pub const WipNav = struct { const has_runtime_bits = ty.hasRuntimeBits(zcu); const has_comptime_state = ty.comptimeOnly(zcu) and try ty.onePossibleValue(pt) == null; try wip_nav.abbrevCode(if (has_runtime_bits and has_comptime_state) switch (tag) { - .comptime_arg => .comptime_arg_runtime_bits_comptime_state, - .local_const => .local_const_runtime_bits_comptime_state, + .comptime_arg => if (opt_name) |_| .comptime_arg_runtime_bits_comptime_state else .unnamed_comptime_arg_runtime_bits_comptime_state, + .local_const => if (opt_name) |_| .local_const_runtime_bits_comptime_state else unreachable, } else if (has_comptime_state) switch (tag) { - .comptime_arg => .comptime_arg_comptime_state, - .local_const => .local_const_comptime_state, + .comptime_arg => if (opt_name) |_| .comptime_arg_comptime_state else .unnamed_comptime_arg_comptime_state, + .local_const => if (opt_name) |_| .local_const_comptime_state else unreachable, } else if (has_runtime_bits) switch (tag) { - .comptime_arg => .comptime_arg_runtime_bits, - .local_const => .local_const_runtime_bits, + .comptime_arg => if (opt_name) |_| .comptime_arg_runtime_bits else .unnamed_comptime_arg_runtime_bits, + .local_const => if (opt_name) |_| .local_const_runtime_bits else unreachable, } else switch (tag) { - .comptime_arg => .comptime_arg, - .local_const => .local_const, + .comptime_arg => if (opt_name) |_| .comptime_arg else .unnamed_comptime_arg, + .local_const => if (opt_name) |_| .local_const else unreachable, }); - try wip_nav.strp(name); + if (opt_name) |name| try wip_nav.strp(name); try wip_nav.refType(ty); if (has_runtime_bits) try wip_nav.blockValue(src_loc, val); if (has_comptime_state) try wip_nav.refValue(val); @@ -4945,10 +4945,15 @@ const AbbrevCode = enum { empty_inlined_func, inlined_func, arg, + unnamed_arg, comptime_arg, + unnamed_comptime_arg, comptime_arg_runtime_bits, + unnamed_comptime_arg_runtime_bits, comptime_arg_comptime_state, + unnamed_comptime_arg_comptime_state, comptime_arg_runtime_bits_comptime_state, + unnamed_comptime_arg_runtime_bits_comptime_state, local_var, local_const, local_const_runtime_bits, @@ -5734,6 +5739,13 @@ const AbbrevCode = enum { .{ .location, .exprloc }, }, }, + .unnamed_arg = .{ + .tag = .formal_parameter, + .attrs = &.{ + .{ .type, .ref_addr }, + .{ .location, .exprloc }, + }, + }, .comptime_arg = .{ .tag = .formal_parameter, .attrs = &.{ @@ -5742,6 +5754,13 @@ const AbbrevCode = enum { .{ .type, .ref_addr }, }, }, + .unnamed_comptime_arg = .{ + .tag = .formal_parameter, + .attrs = &.{ + .{ .const_expr, .flag_present }, + .{ .type, .ref_addr }, + }, + }, .comptime_arg_runtime_bits = .{ .tag = .formal_parameter, .attrs = &.{ @@ -5751,6 +5770,14 @@ const AbbrevCode = enum { .{ .const_value, .block }, }, }, + .unnamed_comptime_arg_runtime_bits = .{ + .tag = .formal_parameter, + .attrs = &.{ + .{ .const_expr, .flag_present }, + .{ .type, .ref_addr }, + .{ .const_value, .block }, + }, + }, .comptime_arg_comptime_state = .{ .tag = .formal_parameter, .attrs = &.{ @@ -5760,6 +5787,14 @@ const AbbrevCode = enum { .{ .ZIG_comptime_value, .ref_addr }, }, }, + .unnamed_comptime_arg_comptime_state = .{ + .tag = .formal_parameter, + .attrs = &.{ + .{ .const_expr, .flag_present }, + .{ .type, .ref_addr }, + .{ .ZIG_comptime_value, .ref_addr }, + }, + }, .comptime_arg_runtime_bits_comptime_state = .{ .tag = .formal_parameter, .attrs = &.{ @@ -5770,6 +5805,15 @@ const AbbrevCode = enum { .{ .ZIG_comptime_value, .ref_addr }, }, }, + .unnamed_comptime_arg_runtime_bits_comptime_state = .{ + .tag = .formal_parameter, + .attrs = &.{ + .{ .const_expr, .flag_present }, + .{ .type, .ref_addr }, + .{ .const_value, .block }, + .{ .ZIG_comptime_value, .ref_addr }, + }, + }, .local_var = .{ .tag = .variable, .attrs = &.{ diff --git a/src/link/Elf/Symbol.zig b/src/link/Elf/Symbol.zig index 31584ca406ce2416ce1f2113063041dad628e290..843c23dca494d1d163f63b2e5a7e48cad676aca5 100644 --- a/src/link/Elf/Symbol.zig +++ b/src/link/Elf/Symbol.zig @@ -462,9 +462,6 @@ pub const Flags = packed struct { /// Whether the symbol is a TLS variable. is_tls: bool = false, - - /// Whether the symbol is an extern pointer (as opposed to function). - is_extern_ptr: bool = false, }; pub const Extra = struct { diff --git a/src/link/Elf/ZigObject.zig b/src/link/Elf/ZigObject.zig index 8478aad8c3daf5f6bbacc5f84e91f7426e22dbd6..9d70caa6323931d27eb21e0306fdd85571e7b676 100644 --- a/src/link/Elf/ZigObject.zig +++ b/src/link/Elf/ZigObject.zig @@ -1542,11 +1542,7 @@ pub fn updateNav( nav.name.toSlice(ip), @"extern".lib_name.toSlice(ip), ); - if (!ip.isFunctionType(@"extern".ty)) { - const sym = self.symbol(sym_index); - sym.flags.is_extern_ptr = true; - if (@"extern".is_threadlocal) sym.flags.is_tls = true; - } + if (@"extern".is_threadlocal) self.symbol(sym_index).flags.is_tls = true; if (self.dwarf) |*dwarf| dwarf: { var debug_wip_nav = try dwarf.initWipNav(pt, nav_index, sym_index) orelse break :dwarf; defer debug_wip_nav.deinit(); diff --git a/src/link/MachO/Symbol.zig b/src/link/MachO/Symbol.zig index 7493d3ceab5adf92cf96e31254c1f2f564c716f3..be126b09638b717de1eea9a07e2c2dc5defae806 100644 --- a/src/link/MachO/Symbol.zig +++ b/src/link/MachO/Symbol.zig @@ -389,9 +389,6 @@ pub const Flags = packed struct { /// ZigObject specific flags /// Whether the symbol has a trampoline trampoline: bool = false, - - /// Whether the symbol is an extern pointer (as opposed to function). - is_extern_ptr: bool = false, }; pub const SectionFlags = packed struct(u8) { diff --git a/src/link/MachO/ZigObject.zig b/src/link/MachO/ZigObject.zig index bd54be6caab7e7aaf9f64b120ed168eac1b44649..9b32bcde6528ec00d7455ab21eb7e3eca37d686b 100644 --- a/src/link/MachO/ZigObject.zig +++ b/src/link/MachO/ZigObject.zig @@ -881,11 +881,7 @@ pub fn updateNav( const name = @"extern".name.toSlice(ip); const lib_name = @"extern".lib_name.toSlice(ip); const sym_index = try self.getGlobalSymbol(macho_file, name, lib_name); - if (!ip.isFunctionType(@"extern".ty)) { - const sym = &self.symbols.items[sym_index]; - sym.flags.is_extern_ptr = true; - if (@"extern".is_threadlocal) sym.flags.tlv = true; - } + if (@"extern".is_threadlocal) self.symbols.items[sym_index].flags.tlv = true; if (self.dwarf) |*dwarf| dwarf: { var debug_wip_nav = try dwarf.initWipNav(pt, nav_index, sym_index) orelse break :dwarf; defer debug_wip_nav.deinit(); diff --git a/src/target.zig b/src/target.zig index 02e64670d0b933c7b50069c3e35cae83c995f040..0cecc168f5746bed364081352e41865924a95a8b 100644 --- a/src/target.zig +++ b/src/target.zig @@ -850,7 +850,7 @@ pub inline fn backendSupportsFeature(backend: std.builtin.CompilerBackend, compt }, .separate_thread => switch (backend) { .stage2_llvm => false, - .stage2_c, .stage2_wasm => true, + .stage2_c, .stage2_wasm, .stage2_x86_64 => true, // TODO: most self-hosted backends should be able to support this without too much work. else => false, }, -- 2.54.0 From db5d85b8c89b755bd8865def3bd7114d5d9d4867 Mon Sep 17 00:00:00 2001 From: mlugg Date: Sun, 8 Jun 2025 21:47:29 +0100 Subject: [PATCH 16/35] compiler: improve progress output * "Flush" nodes ("LLVM Emit Object", "ELF Flush") appear under "Linking" * "Code Generation" disappears when all analysis and codegen is done * We only show one node under "Semantic Analysis" to accurately convey that analysis isn't happening in parallel, but rather that we're pausing one task to do another --- lib/std/Progress.zig | 22 ++++++++++++++++ src/Compilation.zig | 58 +++++++++++++++++++++++++++---------------- src/Zcu.zig | 38 ++++++++++++++++++++++++++-- src/Zcu/PerThread.zig | 22 ++++++++++------ src/link.zig | 22 ++++++++++------ src/link/Lld.zig | 3 +++ src/link/Queue.zig | 2 +- 7 files changed, 126 insertions(+), 41 deletions(-) diff --git a/lib/std/Progress.zig b/lib/std/Progress.zig index d9ff03a3fe1bd7c5bd1a5a52dc3be60d611df5df..030f3f0a28b702f74c034d26a3c91da0f603aa92 100644 --- a/lib/std/Progress.zig +++ b/lib/std/Progress.zig @@ -234,6 +234,28 @@ pub const Node = struct { _ = @atomicRmw(u32, &storage.completed_count, .Add, 1, .monotonic); } + /// Thread-safe. Bytes after '0' in `new_name` are ignored. + pub fn setName(n: Node, new_name: []const u8) void { + const index = n.index.unwrap() orelse return; + const storage = storageByIndex(index); + + const name_len = @min(max_name_len, std.mem.indexOfScalar(u8, new_name, 0) orelse new_name.len); + + copyAtomicStore(storage.name[0..name_len], new_name[0..name_len]); + if (name_len < storage.name.len) + @atomicStore(u8, &storage.name[name_len], 0, .monotonic); + } + + /// Gets the name of this `Node`. + /// A pointer to this array can later be passed to `setName` to restore the name. + pub fn getName(n: Node) [max_name_len]u8 { + var dest: [max_name_len]u8 align(@alignOf(usize)) = undefined; + if (n.index.unwrap()) |index| { + copyAtomicLoad(&dest, &storageByIndex(index).name); + } + return dest; + } + /// Thread-safe. pub fn setCompletedItems(n: Node, completed_items: usize) void { const index = n.index.unwrap() orelse return; diff --git a/src/Compilation.zig b/src/Compilation.zig index fe4671848d672ca266a85b01891ac504c5a41dd9..74f841723e13b6b5663b755bc6bff17f120b7921 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -255,7 +255,7 @@ test_filters: []const []const u8, test_name_prefix: ?[]const u8, link_task_wait_group: WaitGroup = .{}, -work_queue_progress_node: std.Progress.Node = .none, +link_prog_node: std.Progress.Node = std.Progress.Node.none, llvm_opt_bisect_limit: c_int, @@ -2795,6 +2795,17 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void { } } + // The linker progress node is set up here instead of in `performAllTheWork`, because + // we also want it around during `flush`. + const have_link_node = comp.bin_file != null; + if (have_link_node) { + comp.link_prog_node = main_progress_node.start("Linking", 0); + } + defer if (have_link_node) { + comp.link_prog_node.end(); + comp.link_prog_node = .none; + }; + try comp.performAllTheWork(main_progress_node); if (comp.zcu) |zcu| { @@ -2843,7 +2854,7 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void { switch (comp.cache_use) { .none, .incremental => { - try flush(comp, arena, .main, main_progress_node); + try flush(comp, arena, .main); }, .whole => |whole| { if (comp.file_system_inputs) |buf| try man.populateFileSystemInputs(buf); @@ -2919,7 +2930,7 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void { } } - try flush(comp, arena, .main, main_progress_node); + try flush(comp, arena, .main); // Calling `flush` may have produced errors, in which case the // cache manifest must not be written. @@ -3009,13 +3020,12 @@ fn flush( comp: *Compilation, arena: Allocator, 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 sub_prog_node = prog_node.start("LLVM Emit Object", 0); + const sub_prog_node = comp.link_prog_node.start("LLVM Emit Object", 0); defer sub_prog_node.end(); try llvm_object.emit(.{ .pre_ir_path = comp.verbose_llvm_ir, @@ -3053,7 +3063,7 @@ fn flush( } if (comp.bin_file) |lf| { // This is needed before reading the error flags. - lf.flush(arena, tid, prog_node) catch |err| switch (err) { + lf.flush(arena, tid, comp.link_prog_node) catch |err| switch (err) { error.LinkFailure => {}, // Already reported. error.OutOfMemory => return error.OutOfMemory, }; @@ -4172,28 +4182,15 @@ pub fn addWholeFileError( } } -pub fn performAllTheWork( +fn performAllTheWork( comp: *Compilation, main_progress_node: std.Progress.Node, ) JobError!void { - comp.work_queue_progress_node = main_progress_node; - defer comp.work_queue_progress_node = .none; - + // Regardless of errors, `comp.zcu` needs to update its generation number. defer if (comp.zcu) |zcu| { - zcu.sema_prog_node.end(); - zcu.sema_prog_node = .none; - zcu.codegen_prog_node.end(); - zcu.codegen_prog_node = .none; - zcu.generation += 1; }; - try comp.performAllTheWorkInner(main_progress_node); -} -fn performAllTheWorkInner( - comp: *Compilation, - main_progress_node: std.Progress.Node, -) JobError!void { // Here we queue up all the AstGen tasks first, followed by C object compilation. // We wait until the AstGen tasks are all completed before proceeding to the // (at least for now) single-threaded main work queue. However, C object compilation @@ -4513,8 +4510,24 @@ fn performAllTheWorkInner( } zcu.sema_prog_node = main_progress_node.start("Semantic Analysis", 0); - zcu.codegen_prog_node = if (comp.bin_file != null) main_progress_node.start("Code Generation", 0) else .none; + if (comp.bin_file != null) { + zcu.codegen_prog_node = main_progress_node.start("Code Generation", 0); + } + // We increment `pending_codegen_jobs` so that it doesn't reach 0 until after analysis finishes. + // That prevents the "Code Generation" node from constantly disappearing and reappearing when + // we're probably going to analyze more functions at some point. + assert(zcu.pending_codegen_jobs.swap(1, .monotonic) == 0); // don't let this become 0 until analysis finishes } + // When analysis ends, delete the progress nodes for "Semantic Analysis" and possibly "Code Generation". + defer if (comp.zcu) |zcu| { + zcu.sema_prog_node.end(); + zcu.sema_prog_node = .none; + if (zcu.pending_codegen_jobs.rmw(.Sub, 1, .monotonic) == 1) { + // Decremented to 0, so all done. + zcu.codegen_prog_node.end(); + zcu.codegen_prog_node = .none; + } + }; if (!comp.separateCodegenThreadOk()) { // Waits until all input files have been parsed. @@ -4583,6 +4596,7 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job) JobError!void { .status = .init(.pending), .value = undefined, }; + assert(zcu.pending_codegen_jobs.rmw(.Add, 1, .monotonic) > 0); // the "Code Generation" node hasn't been ended if (comp.separateCodegenThreadOk()) { // `workerZcuCodegen` takes ownership of `air`. comp.thread_pool.spawnWgId(&comp.link_task_wait_group, workerZcuCodegen, .{ comp, func.func, air, shared_mir }); diff --git a/src/Zcu.zig b/src/Zcu.zig index 91d2c0ffff4ea54e62b1ff61ebd4ba17643b269a..513492e8187ea34676d59ee007db1544d05d3ada 100644 --- a/src/Zcu.zig +++ b/src/Zcu.zig @@ -66,8 +66,18 @@ root_mod: *Package.Module, /// `root_mod` is the test runner, and `main_mod` is the user's source file which has the tests. main_mod: *Package.Module, std_mod: *Package.Module, -sema_prog_node: std.Progress.Node = std.Progress.Node.none, -codegen_prog_node: std.Progress.Node = std.Progress.Node.none, +sema_prog_node: std.Progress.Node = .none, +codegen_prog_node: std.Progress.Node = .none, +/// The number of codegen jobs which are pending or in-progress. Whichever thread drops this value +/// to 0 is responsible for ending `codegen_prog_node`. While semantic analysis is happening, this +/// value bottoms out at 1 instead of 0, to ensure that it can only drop to 0 after analysis is +/// completed (since semantic analysis could trigger more codegen work). +pending_codegen_jobs: std.atomic.Value(u32) = .init(0), + +/// This is the progress node *under* `sema_prog_node` which is currently running. +/// When we have to pause to analyze something else, we just temporarily rename this node. +/// Eventually, when we thread semantic analysis, we will want one of these per thread. +cur_sema_prog_node: std.Progress.Node = .none, /// Used by AstGen worker to load and store ZIR cache. global_zir_cache: Cache.Directory, @@ -4753,3 +4763,27 @@ fn explainWhyFileIsInModule( import = importer_ref.import; } } + +const SemaProgNode = struct { + /// `null` means we created the node, so should end it. + old_name: ?[std.Progress.Node.max_name_len]u8, + pub fn end(spn: SemaProgNode, zcu: *Zcu) void { + if (spn.old_name) |old_name| { + zcu.sema_prog_node.completeOne(); // we're just renaming, but it's effectively completion + zcu.cur_sema_prog_node.setName(&old_name); + } else { + zcu.cur_sema_prog_node.end(); + zcu.cur_sema_prog_node = .none; + } + } +}; +pub fn startSemaProgNode(zcu: *Zcu, name: []const u8) SemaProgNode { + if (zcu.cur_sema_prog_node.index != .none) { + const old_name = zcu.cur_sema_prog_node.getName(); + zcu.cur_sema_prog_node.setName(name); + return .{ .old_name = old_name }; + } else { + zcu.cur_sema_prog_node = zcu.sema_prog_node.start(name, 0); + return .{ .old_name = null }; + } +} diff --git a/src/Zcu/PerThread.zig b/src/Zcu/PerThread.zig index f8efa40dc008e17bd27674129a9d6f0792a24b91..8bc723f2e83fc5978eeb7b38e7b91125391738b1 100644 --- a/src/Zcu/PerThread.zig +++ b/src/Zcu/PerThread.zig @@ -796,8 +796,8 @@ pub fn ensureComptimeUnitUpToDate(pt: Zcu.PerThread, cu_id: InternPool.ComptimeU info.deps.clearRetainingCapacity(); } - const unit_prog_node = zcu.sema_prog_node.start("comptime", 0); - defer unit_prog_node.end(); + const unit_prog_node = zcu.startSemaProgNode("comptime"); + defer unit_prog_node.end(zcu); return pt.analyzeComptimeUnit(cu_id) catch |err| switch (err) { error.AnalysisFail => { @@ -976,8 +976,8 @@ pub fn ensureNavValUpToDate(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu info.deps.clearRetainingCapacity(); } - const unit_prog_node = zcu.sema_prog_node.start(nav.fqn.toSlice(ip), 0); - defer unit_prog_node.end(); + const unit_prog_node = zcu.startSemaProgNode(nav.fqn.toSlice(ip)); + defer unit_prog_node.end(zcu); const invalidate_value: bool, const new_failed: bool = if (pt.analyzeNavVal(nav_id)) |result| res: { break :res .{ @@ -1396,8 +1396,8 @@ pub fn ensureNavTypeUpToDate(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zc info.deps.clearRetainingCapacity(); } - const unit_prog_node = zcu.sema_prog_node.start(nav.fqn.toSlice(ip), 0); - defer unit_prog_node.end(); + const unit_prog_node = zcu.startSemaProgNode(nav.fqn.toSlice(ip)); + defer unit_prog_node.end(zcu); const invalidate_type: bool, const new_failed: bool = if (pt.analyzeNavType(nav_id)) |result| res: { break :res .{ @@ -1617,8 +1617,8 @@ pub fn ensureFuncBodyUpToDate(pt: Zcu.PerThread, maybe_coerced_func_index: Inter info.deps.clearRetainingCapacity(); } - const func_prog_node = zcu.sema_prog_node.start(ip.getNav(func.owner_nav).fqn.toSlice(ip), 0); - defer func_prog_node.end(); + const func_prog_node = zcu.startSemaProgNode(ip.getNav(func.owner_nav).fqn.toSlice(ip)); + defer func_prog_node.end(zcu); const ies_outdated, const new_failed = if (pt.analyzeFuncBody(func_index)) |result| .{ prev_failed or result.ies_outdated, false } @@ -3360,6 +3360,7 @@ pub fn populateTestFunctions( ip.mutateVarInit(test_fns_val.toIntern(), new_init); } { + assert(zcu.codegen_prog_node.index == .none); zcu.codegen_prog_node = main_progress_node.start("Code Generation", 0); defer { zcu.codegen_prog_node.end(); @@ -4393,6 +4394,11 @@ pub fn runCodegen(pt: Zcu.PerThread, func_index: InternPool.Index, air: *Air, ou }, } zcu.comp.link_task_queue.mirReady(zcu.comp, out); + if (zcu.pending_codegen_jobs.rmw(.Sub, 1, .monotonic) == 1) { + // Decremented to 0, so all done. + zcu.codegen_prog_node.end(); + zcu.codegen_prog_node = .none; + } } fn runCodegenInner(pt: Zcu.PerThread, func_index: InternPool.Index, air: *Air) error{ OutOfMemory, diff --git a/src/link.zig b/src/link.zig index 844ea7a85cdc0fb167cf721cba1060c34f45ef3d..7d522b94d3fd3b69f526f54a5c4e485e85fe6340 100644 --- a/src/link.zig +++ b/src/link.zig @@ -1074,7 +1074,7 @@ pub const File = struct { /// Called when all linker inputs have been sent via `loadInput`. After /// this, `loadInput` will not be called anymore. - pub fn prelink(base: *File, prog_node: std.Progress.Node) FlushError!void { + pub fn prelink(base: *File) FlushError!void { assert(!base.post_prelink); // In this case, an object file is created by the LLVM backend, so @@ -1085,7 +1085,7 @@ pub const File = struct { switch (base.tag) { inline .wasm => |tag| { dev.check(tag.devFeature()); - return @as(*tag.Type(), @fieldParentPtr("base", base)).prelink(prog_node); + return @as(*tag.Type(), @fieldParentPtr("base", base)).prelink(base.comp.link_prog_node); }, else => {}, } @@ -1293,7 +1293,7 @@ pub fn doPrelinkTask(comp: *Compilation, task: PrelinkTask) void { const base = comp.bin_file orelse return; switch (task) { .load_explicitly_provided => { - const prog_node = comp.work_queue_progress_node.start("Parse Linker Inputs", comp.link_inputs.len); + const prog_node = comp.link_prog_node.start("Parse Inputs", comp.link_inputs.len); defer prog_node.end(); for (comp.link_inputs) |input| { base.loadInput(input) catch |err| switch (err) { @@ -1310,7 +1310,7 @@ pub fn doPrelinkTask(comp: *Compilation, task: PrelinkTask) void { } }, .load_host_libc => { - const prog_node = comp.work_queue_progress_node.start("Linker Parse Host libc", 0); + const prog_node = comp.link_prog_node.start("Parse Host libc", 0); defer prog_node.end(); const target = comp.root_mod.resolved_target.result; @@ -1369,7 +1369,7 @@ pub fn doPrelinkTask(comp: *Compilation, task: PrelinkTask) void { } }, .load_object => |path| { - const prog_node = comp.work_queue_progress_node.start("Linker Parse Object", 0); + const prog_node = comp.link_prog_node.start("Parse Object", 0); defer prog_node.end(); base.openLoadObject(path) catch |err| switch (err) { error.LinkFailure => return, // error reported via diags @@ -1377,7 +1377,7 @@ pub fn doPrelinkTask(comp: *Compilation, task: PrelinkTask) void { }; }, .load_archive => |path| { - const prog_node = comp.work_queue_progress_node.start("Linker Parse Archive", 0); + const prog_node = comp.link_prog_node.start("Parse Archive", 0); defer prog_node.end(); base.openLoadArchive(path, null) catch |err| switch (err) { error.LinkFailure => return, // error reported via link_diags @@ -1385,7 +1385,7 @@ pub fn doPrelinkTask(comp: *Compilation, task: PrelinkTask) void { }; }, .load_dso => |path| { - const prog_node = comp.work_queue_progress_node.start("Linker Parse Shared Library", 0); + const prog_node = comp.link_prog_node.start("Parse Shared Library", 0); defer prog_node.end(); base.openLoadDso(path, .{ .preferred_mode = .dynamic, @@ -1396,7 +1396,7 @@ pub fn doPrelinkTask(comp: *Compilation, task: PrelinkTask) void { }; }, .load_input => |input| { - const prog_node = comp.work_queue_progress_node.start("Linker Parse Input", 0); + const prog_node = comp.link_prog_node.start("Parse Input", 0); defer prog_node.end(); base.loadInput(input) catch |err| switch (err) { error.LinkFailure => return, // error reported via link_diags @@ -1418,6 +1418,9 @@ pub fn doZcuTask(comp: *Compilation, tid: usize, task: ZcuTask) void { const zcu = comp.zcu.?; const pt: Zcu.PerThread = .activate(zcu, @enumFromInt(tid)); defer pt.deactivate(); + const fqn_slice = zcu.intern_pool.getNav(nav_index).fqn.toSlice(&zcu.intern_pool); + const nav_prog_node = comp.link_prog_node.start(fqn_slice, 0); + defer nav_prog_node.end(); if (zcu.llvm_object) |llvm_object| { llvm_object.updateNav(pt, nav_index) catch |err| switch (err) { error.OutOfMemory => diags.setAllocFailure(), @@ -1441,6 +1444,9 @@ pub fn doZcuTask(comp: *Compilation, tid: usize, task: ZcuTask) void { const nav = zcu.funcInfo(func.func).owner_nav; const pt: Zcu.PerThread = .activate(zcu, @enumFromInt(tid)); defer pt.deactivate(); + const fqn_slice = zcu.intern_pool.getNav(nav).fqn.toSlice(&zcu.intern_pool); + const nav_prog_node = comp.link_prog_node.start(fqn_slice, 0); + defer nav_prog_node.end(); switch (func.mir.status.load(.monotonic)) { .pending => unreachable, .ready => {}, diff --git a/src/link/Lld.zig b/src/link/Lld.zig index dd50bd2a2f457356403050b47f0580fc3bbe20f6..4ea809428e5879631a24adcce688662ab0408de5 100644 --- a/src/link/Lld.zig +++ b/src/link/Lld.zig @@ -267,6 +267,9 @@ pub fn flush( const comp = lld.base.comp; const result = if (comp.config.output_mode == .Lib and comp.config.link_mode == .static) r: { + if (!@import("build_options").have_llvm or !comp.config.use_lib_llvm) { + return lld.base.comp.link_diags.fail("using lld without libllvm not implemented", .{}); + } break :r linkAsArchive(lld, arena); } else switch (lld.ofmt) { .coff => coffLink(lld, arena), diff --git a/src/link/Queue.zig b/src/link/Queue.zig index 3436be592169707123d9d17cc920985fbfe6be66..ab5fd89699a32b807f3abdf6ebfc2b222583db7d 100644 --- a/src/link/Queue.zig +++ b/src/link/Queue.zig @@ -180,7 +180,7 @@ fn flushTaskQueue(tid: usize, q: *Queue, comp: *Compilation) void { // We've finished the prelink tasks, so run prelink if necessary. if (comp.bin_file) |lf| { if (!lf.post_prelink) { - if (lf.prelink(comp.work_queue_progress_node)) |_| { + if (lf.prelink()) |_| { lf.post_prelink = true; } else |err| switch (err) { error.OutOfMemory => comp.link_diags.setAllocFailure(), -- 2.54.0 From ac745edbbd6687c5898bb3a50bf9d31d86e57b9e Mon Sep 17 00:00:00 2001 From: mlugg Date: Sun, 8 Jun 2025 16:25:28 +0100 Subject: [PATCH 17/35] compiler: estimate totals for "Code Generation" and "Linking" progress nodes --- src/Compilation.zig | 6 ++++++ src/link.zig | 29 +++++++++++++++-------------- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/src/Compilation.zig b/src/Compilation.zig index 74f841723e13b6b5663b755bc6bff17f120b7921..04cd03c3d8393e6d2df205bf925f4f67fd4a0041 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -4202,6 +4202,10 @@ fn performAllTheWork( comp.link_task_wait_group.reset(); defer comp.link_task_wait_group.wait(); + comp.link_prog_node.increaseEstimatedTotalItems( + comp.link_task_queue.queued_prelink.items.len + // already queued prelink tasks + comp.link_task_queue.pending_prelink_tasks, // prelink tasks which will be queued + ); comp.link_task_queue.start(comp); if (comp.emit_docs != null) { @@ -4597,6 +4601,7 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job) JobError!void { .value = undefined, }; assert(zcu.pending_codegen_jobs.rmw(.Add, 1, .monotonic) > 0); // the "Code Generation" node hasn't been ended + zcu.codegen_prog_node.increaseEstimatedTotalItems(1); if (comp.separateCodegenThreadOk()) { // `workerZcuCodegen` takes ownership of `air`. comp.thread_pool.spawnWgId(&comp.link_task_wait_group, workerZcuCodegen, .{ comp, func.func, air, shared_mir }); @@ -7444,6 +7449,7 @@ pub fn queuePrelinkTasks(comp: *Compilation, tasks: []const link.PrelinkTask) vo /// The reason for the double-queue here is that the first queue ensures any /// resolve_type_fully tasks are complete before this dispatch function is called. fn dispatchZcuLinkTask(comp: *Compilation, tid: usize, task: link.ZcuTask) void { + comp.link_prog_node.increaseEstimatedTotalItems(1); if (!comp.separateCodegenThreadOk()) { assert(tid == 0); if (task == .link_func) { diff --git a/src/link.zig b/src/link.zig index 7d522b94d3fd3b69f526f54a5c4e485e85fe6340..ce98ac89298cadc42a8d082a0fe7edb69e8e0043 100644 --- a/src/link.zig +++ b/src/link.zig @@ -1290,7 +1290,10 @@ pub const ZcuTask = union(enum) { pub fn doPrelinkTask(comp: *Compilation, task: PrelinkTask) void { const diags = &comp.link_diags; - const base = comp.bin_file orelse return; + const base = comp.bin_file orelse { + comp.link_prog_node.completeOne(); + return; + }; switch (task) { .load_explicitly_provided => { const prog_node = comp.link_prog_node.start("Parse Inputs", comp.link_inputs.len); @@ -1413,12 +1416,13 @@ pub fn doPrelinkTask(comp: *Compilation, task: PrelinkTask) void { } pub fn doZcuTask(comp: *Compilation, tid: usize, task: ZcuTask) void { const diags = &comp.link_diags; + const zcu = comp.zcu.?; + const ip = &zcu.intern_pool; + const pt: Zcu.PerThread = .activate(zcu, @enumFromInt(tid)); + defer pt.deactivate(); switch (task) { .link_nav => |nav_index| { - const zcu = comp.zcu.?; - const pt: Zcu.PerThread = .activate(zcu, @enumFromInt(tid)); - defer pt.deactivate(); - const fqn_slice = zcu.intern_pool.getNav(nav_index).fqn.toSlice(&zcu.intern_pool); + const fqn_slice = ip.getNav(nav_index).fqn.toSlice(ip); const nav_prog_node = comp.link_prog_node.start(fqn_slice, 0); defer nav_prog_node.end(); if (zcu.llvm_object) |llvm_object| { @@ -1440,11 +1444,8 @@ pub fn doZcuTask(comp: *Compilation, tid: usize, task: ZcuTask) void { } }, .link_func => |func| { - const zcu = comp.zcu.?; const nav = zcu.funcInfo(func.func).owner_nav; - const pt: Zcu.PerThread = .activate(zcu, @enumFromInt(tid)); - defer pt.deactivate(); - const fqn_slice = zcu.intern_pool.getNav(nav).fqn.toSlice(&zcu.intern_pool); + const fqn_slice = ip.getNav(nav).fqn.toSlice(ip); const nav_prog_node = comp.link_prog_node.start(fqn_slice, 0); defer nav_prog_node.end(); switch (func.mir.status.load(.monotonic)) { @@ -1468,9 +1469,9 @@ pub fn doZcuTask(comp: *Compilation, tid: usize, task: ZcuTask) void { } }, .link_type => |ty| { - const zcu = comp.zcu.?; - const pt: Zcu.PerThread = .activate(zcu, @enumFromInt(tid)); - defer pt.deactivate(); + const name = Type.fromInterned(ty).containerTypeName(ip).toSlice(ip); + const nav_prog_node = comp.link_prog_node.start(name, 0); + defer nav_prog_node.end(); if (zcu.llvm_object == null) { if (comp.bin_file) |lf| { lf.updateContainerType(pt, ty) catch |err| switch (err) { @@ -1481,8 +1482,8 @@ pub fn doZcuTask(comp: *Compilation, tid: usize, task: ZcuTask) void { } }, .update_line_number => |ti| { - const pt: Zcu.PerThread = .activate(comp.zcu.?, @enumFromInt(tid)); - defer pt.deactivate(); + const nav_prog_node = comp.link_prog_node.start("Update line number", 0); + defer nav_prog_node.end(); if (pt.zcu.llvm_object == null) { if (comp.bin_file) |lf| { lf.updateLineNumber(pt, ti) catch |err| switch (err) { -- 2.54.0 From e28b699cbfa4390366d68784b896ee2662af411d Mon Sep 17 00:00:00 2001 From: mlugg Date: Sun, 8 Jun 2025 22:57:37 +0100 Subject: [PATCH 18/35] libs: fix caching behavior glibc, freebsd, and netbsd all do caching manually, because of the fact that they emit multiple files which they want to cache as a block. Therefore, the individual sub-compilation on a cache miss should be using `CacheMode.none` so that we can specify the output paths for each sub-compilation as being in the shared output directory. --- src/libs/freebsd.zig | 6 ++++-- src/libs/glibc.zig | 6 ++++-- src/libs/netbsd.zig | 6 ++++-- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/libs/freebsd.zig b/src/libs/freebsd.zig index 0d14b6fb4776465a6fc5ed2060f9456d0b4002ef..f6195ffa914ee5cb036a0e604bc4a384461d24dd 100644 --- a/src/libs/freebsd.zig +++ b/src/libs/freebsd.zig @@ -1073,12 +1073,14 @@ fn buildSharedLib( .dirs = comp.dirs.withoutLocalCache(), .thread_pool = comp.thread_pool, .self_exe_path = comp.self_exe_path, - .cache_mode = .incremental, + // Because we manually cache the whole set of objects, we don't cache the individual objects + // within it. In fact, we *can't* do that, because we need `emit_bin` to specify the path. + .cache_mode = .none, .config = config, .root_mod = root_mod, .root_name = lib.name, .libc_installation = comp.libc_installation, - .emit_bin = .yes_cache, + .emit_bin = .{ .yes_path = try bin_directory.join(arena, &.{basename}) }, .verbose_cc = comp.verbose_cc, .verbose_link = comp.verbose_link, .verbose_air = comp.verbose_air, diff --git a/src/libs/glibc.zig b/src/libs/glibc.zig index cb8dd4b46099495c7bfd64d71989fdb02e5332bf..8031827a9d29c8bbd5577e268eaf690fa7d1ffab 100644 --- a/src/libs/glibc.zig +++ b/src/libs/glibc.zig @@ -1239,12 +1239,14 @@ fn buildSharedLib( .dirs = comp.dirs.withoutLocalCache(), .thread_pool = comp.thread_pool, .self_exe_path = comp.self_exe_path, - .cache_mode = .incremental, + // Because we manually cache the whole set of objects, we don't cache the individual objects + // within it. In fact, we *can't* do that, because we need `emit_bin` to specify the path. + .cache_mode = .none, .config = config, .root_mod = root_mod, .root_name = lib.name, .libc_installation = comp.libc_installation, - .emit_bin = .yes_cache, + .emit_bin = .{ .yes_path = try bin_directory.join(arena, &.{basename}) }, .verbose_cc = comp.verbose_cc, .verbose_link = comp.verbose_link, .verbose_air = comp.verbose_air, diff --git a/src/libs/netbsd.zig b/src/libs/netbsd.zig index f19c528d5d1b73748582793a7e92e570a186c852..fdab27f217f3557589e99e06579eebbc5ae21ced 100644 --- a/src/libs/netbsd.zig +++ b/src/libs/netbsd.zig @@ -737,12 +737,14 @@ fn buildSharedLib( .dirs = comp.dirs.withoutLocalCache(), .thread_pool = comp.thread_pool, .self_exe_path = comp.self_exe_path, - .cache_mode = .incremental, + // Because we manually cache the whole set of objects, we don't cache the individual objects + // within it. In fact, we *can't* do that, because we need `emit_bin` to specify the path. + .cache_mode = .none, .config = config, .root_mod = root_mod, .root_name = lib.name, .libc_installation = comp.libc_installation, - .emit_bin = .yes_cache, + .emit_bin = .{ .yes_path = try bin_directory.join(arena, &.{basename}) }, .verbose_cc = comp.verbose_cc, .verbose_link = comp.verbose_link, .verbose_air = comp.verbose_air, -- 2.54.0 From 89a6c732e5dcf82fb0cdd18f268b8d0c908b12e8 Mon Sep 17 00:00:00 2001 From: mlugg Date: Sun, 8 Jun 2025 23:04:00 +0100 Subject: [PATCH 19/35] Zcu: fix `deleteExport` crash with LLVM backend --- src/Zcu/PerThread.zig | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Zcu/PerThread.zig b/src/Zcu/PerThread.zig index 8bc723f2e83fc5978eeb7b38e7b91125391738b1..9b6a43b496f7dab90b8cbdab9f57e8653e23c926 100644 --- a/src/Zcu/PerThread.zig +++ b/src/Zcu/PerThread.zig @@ -3098,7 +3098,9 @@ pub fn processExports(pt: Zcu.PerThread) !void { // This export might already have been sent to the linker on a previous update, in which case we need to delete it. // The linker export API should be modified to eliminate this call. #23616 if (zcu.comp.bin_file) |lf| { - lf.deleteExport(exp.exported, exp.opts.name); + if (zcu.llvm_object == null) { + lf.deleteExport(exp.exported, exp.opts.name); + } } continue; } @@ -3122,8 +3124,10 @@ pub fn processExports(pt: Zcu.PerThread) !void { // This export might already have been sent to the linker on a previous update, in which case we need to delete it. // The linker export API should be modified to eliminate this loop. #23616 if (zcu.comp.bin_file) |lf| { - for (exports) |exp| { - lf.deleteExport(exp.exported, exp.opts.name); + if (zcu.llvm_object == null) { + for (exports) |exp| { + lf.deleteExport(exp.exported, exp.opts.name); + } } } continue; -- 2.54.0 From a3abaaee0c1fa9a9d9d2ee47459e9b6ebb1938fa Mon Sep 17 00:00:00 2001 From: mlugg Date: Sun, 8 Jun 2025 23:14:42 +0100 Subject: [PATCH 20/35] test-link: correct expected object file name The name of the ZCU object file emitted by the LLVM backend has been changed in this branch from e.g. `foo.o` to `foo_zcu.o`. This is to avoid name clashes. This commit just updates a link test which started failing because the object name in a linker error changed. --- test/link/macho.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/link/macho.zig b/test/link/macho.zig index a98245926e0a853a98ced95fdc5763299167a656..ed83000c08d64981885eee412d422c18463b76b9 100644 --- a/test/link/macho.zig +++ b/test/link/macho.zig @@ -211,7 +211,7 @@ fn testDuplicateDefinitions(b: *Build, opts: Options) *Step { expectLinkErrors(exe, test_step, .{ .exact = &.{ "error: duplicate symbol definition: _strong", "note: defined by /?/a.o", - "note: defined by /?/main.o", + "note: defined by /?/main_zcu.o", } }); return test_step; @@ -2648,7 +2648,7 @@ fn testUnresolvedError(b: *Build, opts: Options) *Step { expectLinkErrors(exe, test_step, .{ .exact = &.{ "error: undefined symbol: _foo", "note: referenced by /?/a.o:_bar", - "note: referenced by /?/main.o:_main.main", + "note: referenced by /?/main_zcu.o:_main.main", } }); } else { expectLinkErrors(exe, test_step, .{ .exact = &.{ -- 2.54.0 From 56119699bf5fbb44d844d4ad181d51629f754be8 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Mon, 9 Jun 2025 02:35:45 -0400 Subject: [PATCH 21/35] x86_64: fix `dbg_var_ptr` types in debug info --- src/arch/x86_64/CodeGen.zig | 27 +++++++++++++++++---------- src/arch/x86_64/bits.zig | 2 -- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 84b263a93e5b142d7b67086603f0c6a0671afd18..5d2dd08de7f5a13cfcd1316fb91ebb2823c718d6 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -85046,13 +85046,15 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .data = .{ .ip_index = old_inline_func }, }); }, - .dbg_var_ptr, - .dbg_var_val, - .dbg_arg_inline, - => |air_tag| if (use_old) try cg.airDbgVar(inst) else if (!cg.mod.strip) { + .dbg_var_ptr, .dbg_var_val, .dbg_arg_inline => |air_tag| if (use_old) try cg.airDbgVar(inst) else if (!cg.mod.strip) { const pl_op = air_datas[@intFromEnum(inst)].pl_op; const air_name: Air.NullTerminatedString = @enumFromInt(pl_op.payload); - const ty = cg.typeOf(pl_op.operand); + const op_ty = cg.typeOf(pl_op.operand); + const local_ty = switch (air_tag) { + else => unreachable, + .dbg_var_ptr => op_ty.childType(zcu), + .dbg_var_val, .dbg_arg_inline => op_ty, + }; var ops = try cg.tempsFromOperands(inst, .{pl_op.operand}); var mcv = ops[0].tracking(cg).short; switch (mcv) { @@ -85076,10 +85078,10 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, else => try cg.addString(air_name.toSlice(cg.air)), }, - .type = ty.toIntern(), + .type = local_ty.toIntern(), }); - try cg.genLocalDebugInfo(air_tag, ty, ops[0].tracking(cg).short); + try cg.genLocalDebugInfo(air_tag, local_ty, ops[0].tracking(cg).short); try ops[0].die(cg); }, .is_null => if (use_old) try cg.airIsNull(inst) else { @@ -174364,7 +174366,12 @@ fn airDbgVar(cg: *CodeGen, inst: Air.Inst.Index) !void { const air_tag = cg.air.instructions.items(.tag)[@intFromEnum(inst)]; const pl_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; const air_name: Air.NullTerminatedString = @enumFromInt(pl_op.payload); - const ty = cg.typeOf(pl_op.operand); + const op_ty = cg.typeOf(pl_op.operand); + const local_ty = switch (air_tag) { + else => unreachable, + .dbg_var_ptr => op_ty.childType(cg.pt.zcu), + .dbg_var_val, .dbg_arg_inline => op_ty, + }; try cg.mir_locals.append(cg.gpa, .{ .name = switch (air_name) { @@ -174374,10 +174381,10 @@ fn airDbgVar(cg: *CodeGen, inst: Air.Inst.Index) !void { }, else => try cg.addString(air_name.toSlice(cg.air)), }, - .type = ty.toIntern(), + .type = local_ty.toIntern(), }); - try cg.genLocalDebugInfo(air_tag, ty, try cg.resolveInst(pl_op.operand)); + try cg.genLocalDebugInfo(air_tag, local_ty, try cg.resolveInst(pl_op.operand)); return cg.finishAir(inst, .unreach, .{ pl_op.operand, .none, .none }); } diff --git a/src/arch/x86_64/bits.zig b/src/arch/x86_64/bits.zig index 53080598e58dc128d8641d86f010fb879b2ec38f..c854af91d2fb4c8de7f4bb74b830831675d1a31c 100644 --- a/src/arch/x86_64/bits.zig +++ b/src/arch/x86_64/bits.zig @@ -686,8 +686,6 @@ test "Register id - different classes" { try expect(Register.xmm0.id() == Register.ymm0.id()); try expect(Register.xmm0.id() != Register.mm0.id()); try expect(Register.mm0.id() != Register.st0.id()); - - try expect(Register.es.id() == 0b110000); } test "Register enc - different classes" { -- 2.54.0 From d312dfc1f21a9194dd06c1d45f653bdaf823d2f6 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Mon, 9 Jun 2025 02:36:32 -0400 Subject: [PATCH 22/35] codegen: make threadlocal logic consistent --- src/arch/x86_64/CodeGen.zig | 3 ++- src/arch/x86_64/Emit.zig | 28 ++++++--------------- src/codegen.zig | 48 +++++++----------------------------- src/link/Elf/ZigObject.zig | 5 ++-- src/link/MachO/ZigObject.zig | 5 ++-- 5 files changed, 23 insertions(+), 66 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 5d2dd08de7f5a13cfcd1316fb91ebb2823c718d6..4a0113fe366f625a561fc8fbfbc3dfd58a155fa6 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -163354,7 +163354,8 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .runtime_nav_ptr => { const ty_nav = air_datas[@intFromEnum(inst)].ty_nav; - const is_threadlocal = ip.getNav(ty_nav.nav).isThreadlocal(ip); + const nav = ip.getNav(ty_nav.nav); + const is_threadlocal = zcu.comp.config.any_non_single_threaded and nav.isThreadlocal(ip); if (is_threadlocal) if (cg.mod.pic) { try cg.spillRegisters(&.{ .rdi, .rax }); } else { diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index ff6bf85ef3b061d8342e1d021837990a818ffd6e..8a9609dafc3abceb8050ee80feca879c4fd9d213 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -21,7 +21,8 @@ pub const Error = Lower.Error || error{ } || link.File.UpdateDebugInfoError; pub fn emitMir(emit: *Emit) Error!void { - const gpa = emit.bin_file.comp.gpa; + const comp = emit.bin_file.comp; + const gpa = comp.gpa; try emit.code_offset_mapping.resize(gpa, emit.lower.mir.instructions.len); emit.relocs.clearRetainingCapacity(); emit.table_relocs.clearRetainingCapacity(); @@ -99,12 +100,10 @@ pub fn emitMir(emit: *Emit) Error!void { .inst => |inst| .{ .index = inst, .is_extern = false, .type = .inst }, .table => .{ .index = undefined, .is_extern = false, .type = .table }, .nav => |nav| { - const ip = &emit.pt.zcu.intern_pool; const sym_index = switch (try codegen.genNavRef( emit.bin_file, emit.pt, emit.lower.src_loc, - .fromInterned(ip.getNav(nav).typeOf(ip)), nav, emit.lower.target.*, )) { @@ -118,12 +117,13 @@ pub fn emitMir(emit: *Emit) Error!void { return error.EmitFail; }, }; + const ip = &emit.pt.zcu.intern_pool; break :target switch (ip.getNav(nav).status) { .unresolved => unreachable, .type_resolved => |type_resolved| .{ .index = sym_index, .is_extern = false, - .type = if (type_resolved.is_threadlocal) .tlv else .symbol, + .type = if (type_resolved.is_threadlocal and comp.config.any_non_single_threaded) .tlv else .symbol, }, .fully_resolved => |fully_resolved| switch (ip.indexToKey(fully_resolved.val)) { .@"extern" => |@"extern"| .{ @@ -132,7 +132,7 @@ pub fn emitMir(emit: *Emit) Error!void { .default => true, .hidden, .protected => false, }, - .type = if (@"extern".is_threadlocal) .tlv else .symbol, + .type = if (@"extern".is_threadlocal and comp.config.any_non_single_threaded) .tlv else .symbol, .force_pcrel_direct = switch (@"extern".relocation) { .any => false, .pcrel => true, @@ -141,7 +141,7 @@ pub fn emitMir(emit: *Emit) Error!void { .variable => |variable| .{ .index = sym_index, .is_extern = false, - .type = if (variable.is_threadlocal) .tlv else .symbol, + .type = if (variable.is_threadlocal and comp.config.any_non_single_threaded) .tlv else .symbol, }, else => .{ .index = sym_index, .is_extern = false, .type = .symbol }, }, @@ -292,12 +292,8 @@ pub fn emitMir(emit: *Emit) Error!void { .branch, .tls => unreachable, .tlv => { if (emit.bin_file.cast(.elf)) |elf_file| { - if (reloc.target.is_extern) { - // TODO handle extern TLS vars, i.e., emit GD model - return emit.fail("TODO implement extern {s} reloc for {s}", .{ - @tagName(reloc.target.type), @tagName(emit.bin_file.tag), - }); - } else if (emit.pic) switch (lowered_inst.encoding.mnemonic) { + // TODO handle extern TLS vars, i.e., emit GD model + if (emit.pic) switch (lowered_inst.encoding.mnemonic) { .lea, .mov => { // Here, we currently assume local dynamic TLS vars, and so // we emit LD model. @@ -507,7 +503,6 @@ pub fn emitMir(emit: *Emit) Error!void { } }; }, .pseudo_dbg_arg_m, .pseudo_dbg_var_m => { - const ip = &emit.pt.zcu.intern_pool; const mem = emit.lower.mir.resolveMemoryExtra(mir_inst.data.x.payload).decode(); break :loc .{ .plus = .{ base: { @@ -519,7 +514,6 @@ pub fn emitMir(emit: *Emit) Error!void { emit.bin_file, emit.pt, emit.lower.src_loc, - .fromInterned(ip.getNav(nav).typeOf(ip)), nav, emit.lower.target.*, ) catch |err| switch (err) { @@ -803,9 +797,6 @@ fn encodeInst(emit: *Emit, lowered_inst: Instruction, reloc_info: []const RelocI @tagName(reloc.target.type), @tagName(emit.bin_file.tag), }), .tls => if (emit.bin_file.cast(.elf)) |elf_file| { - if (reloc.target.is_extern) return emit.fail("TODO implement extern {s} reloc for {s}", .{ - @tagName(reloc.target.type), @tagName(emit.bin_file.tag), - }); const zo = elf_file.zigObjectPtr().?; const atom = zo.symbol(emit.atom_index).atom(elf_file).?; const r_type: std.elf.R_X86_64 = if (emit.pic) .TLSLD else unreachable; @@ -818,9 +809,6 @@ fn encodeInst(emit: *Emit, lowered_inst: Instruction, reloc_info: []const RelocI @tagName(reloc.target.type), @tagName(emit.bin_file.tag), }), .tlv => if (emit.bin_file.cast(.elf)) |elf_file| { - if (reloc.target.is_extern) return emit.fail("TODO implement extern {s} reloc for {s}", .{ - @tagName(reloc.target.type), @tagName(emit.bin_file.tag), - }); const zo = elf_file.zigObjectPtr().?; const atom = zo.symbol(emit.atom_index).atom(elf_file).?; const r_type: std.elf.R_X86_64 = if (emit.pic) .DTPOFF32 else .TPOFF32; diff --git a/src/codegen.zig b/src/codegen.zig index a977d3003fae7034dace76763b600f456d87a906..df9c0b44641160a2ee1f85e0175474b79fbbd9a5 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -955,47 +955,18 @@ pub fn genNavRef( lf: *link.File, pt: Zcu.PerThread, src_loc: Zcu.LazySrcLoc, - ty: Type, nav_index: InternPool.Nav.Index, target: std.Target, ) CodeGenError!GenResult { const zcu = pt.zcu; const ip = &zcu.intern_pool; - log.debug("genNavRef: ty = {}", .{ty.fmt(pt)}); - - if (!ty.isFnOrHasRuntimeBitsIgnoreComptime(zcu)) { - const imm: u64 = switch (@divExact(target.ptrBitWidth(), 8)) { - 1 => 0xaa, - 2 => 0xaaaa, - 4 => 0xaaaaaaaa, - 8 => 0xaaaaaaaaaaaaaaaa, - else => unreachable, - }; - return .{ .mcv = .{ .immediate = imm } }; - } - - const comp = lf.comp; - const gpa = comp.gpa; - - // TODO this feels clunky. Perhaps we should check for it in `genTypedValue`? - if (ty.castPtrToFn(zcu)) |fn_ty| { - if (zcu.typeToFunc(fn_ty).?.is_generic) { - return .{ .mcv = .{ .immediate = fn_ty.abiAlignment(zcu).toByteUnits().? } }; - } - } else if (ty.zigTypeTag(zcu) == .pointer) { - const elem_ty = ty.elemType2(zcu); - if (!elem_ty.hasRuntimeBits(zcu)) { - return .{ .mcv = .{ .immediate = elem_ty.abiAlignment(zcu).toByteUnits().? } }; - } - } - const nav = ip.getNav(nav_index); + log.debug("genNavRef({})", .{nav.fqn.fmt(ip)}); + const lib_name, const linkage, const is_threadlocal = if (nav.getExtern(ip)) |e| - .{ e.lib_name, e.linkage, e.is_threadlocal and !zcu.navFileScope(nav_index).mod.?.single_threaded } + .{ e.lib_name, e.linkage, e.is_threadlocal and zcu.comp.config.any_non_single_threaded } else .{ .none, .internal, false }; - - const name = nav.name; if (lf.cast(.elf)) |elf_file| { const zo = elf_file.zigObjectPtr().?; switch (linkage) { @@ -1005,7 +976,7 @@ pub fn genNavRef( return .{ .mcv = .{ .lea_symbol = sym_index } }; }, .strong, .weak => { - const sym_index = try elf_file.getGlobalSymbol(name.toSlice(ip), lib_name.toSlice(ip)); + const sym_index = try elf_file.getGlobalSymbol(nav.name.toSlice(ip), lib_name.toSlice(ip)); switch (linkage) { .internal => unreachable, .strong => {}, @@ -1026,7 +997,7 @@ pub fn genNavRef( return .{ .mcv = .{ .lea_symbol = sym_index } }; }, .strong, .weak => { - const sym_index = try macho_file.getGlobalSymbol(name.toSlice(ip), lib_name.toSlice(ip)); + const sym_index = try macho_file.getGlobalSymbol(nav.name.toSlice(ip), lib_name.toSlice(ip)); switch (linkage) { .internal => unreachable, .strong => {}, @@ -1047,8 +1018,8 @@ pub fn genNavRef( return .{ .mcv = .{ .load_got = sym_index } }; }, .strong, .weak => { - const global_index = try coff_file.getGlobalSymbol(name.toSlice(ip), lib_name.toSlice(ip)); - try coff_file.need_got_table.put(gpa, global_index, {}); // needs GOT + const global_index = try coff_file.getGlobalSymbol(nav.name.toSlice(ip), lib_name.toSlice(ip)); + try coff_file.need_got_table.put(zcu.gpa, global_index, {}); // needs GOT return .{ .mcv = .{ .load_got = link.File.Coff.global_symbol_bit | global_index } }; }, .link_once => unreachable, @@ -1058,7 +1029,7 @@ pub fn genNavRef( const atom = p9.getAtom(atom_index); return .{ .mcv = .{ .memory = atom.getOffsetTableAddress(p9) } }; } else { - const msg = try ErrorMsg.create(gpa, src_loc, "TODO genNavRef for target {}", .{target}); + const msg = try ErrorMsg.create(zcu.gpa, src_loc, "TODO genNavRef for target {}", .{target}); return .{ .fail = msg }; } } @@ -1071,12 +1042,11 @@ pub fn genTypedValue( val: Value, target: std.Target, ) CodeGenError!GenResult { - const ip = &pt.zcu.intern_pool; return switch (try lowerValue(pt, val, &target)) { .none => .{ .mcv = .none }, .undef => .{ .mcv = .undef }, .immediate => |imm| .{ .mcv = .{ .immediate = imm } }, - .lea_nav => |nav| genNavRef(lf, pt, src_loc, .fromInterned(ip.getNav(nav).typeOf(ip)), nav, target), + .lea_nav => |nav| genNavRef(lf, pt, src_loc, nav, target), .lea_uav => |uav| switch (try lf.lowerUav( pt, uav.val, diff --git a/src/link/Elf/ZigObject.zig b/src/link/Elf/ZigObject.zig index 9d70caa6323931d27eb21e0306fdd85571e7b676..71b42819e290747720d797dde8fce0a0d9252d0a 100644 --- a/src/link/Elf/ZigObject.zig +++ b/src/link/Elf/ZigObject.zig @@ -1142,7 +1142,6 @@ fn getNavShdrIndex( const gpa = elf_file.base.comp.gpa; const ptr_size = elf_file.ptrWidthBytes(); const ip = &zcu.intern_pool; - const any_non_single_threaded = elf_file.base.comp.config.any_non_single_threaded; const nav_val = zcu.navValue(nav_index); if (ip.isFunctionType(nav_val.typeOf(zcu).toIntern())) { if (self.text_index) |symbol_index| @@ -1162,7 +1161,7 @@ fn getNavShdrIndex( else => .{ true, false, nav_val.toIntern() }, }; const has_relocs = self.symbol(sym_index).atom(elf_file).?.relocs(elf_file).len > 0; - if (any_non_single_threaded and is_threadlocal) { + if (is_threadlocal and elf_file.base.comp.config.any_non_single_threaded) { const is_bss = !has_relocs and for (code) |byte| { if (byte != 0) break false; } else true; @@ -1542,7 +1541,7 @@ pub fn updateNav( nav.name.toSlice(ip), @"extern".lib_name.toSlice(ip), ); - if (@"extern".is_threadlocal) self.symbol(sym_index).flags.is_tls = true; + if (@"extern".is_threadlocal and elf_file.base.comp.config.any_non_single_threaded) self.symbol(sym_index).flags.is_tls = true; if (self.dwarf) |*dwarf| dwarf: { var debug_wip_nav = try dwarf.initWipNav(pt, nav_index, sym_index) orelse break :dwarf; defer debug_wip_nav.deinit(); diff --git a/src/link/MachO/ZigObject.zig b/src/link/MachO/ZigObject.zig index 9b32bcde6528ec00d7455ab21eb7e3eca37d686b..f9ecdc6fb50d772743a31cf1d4cef24e82f28b18 100644 --- a/src/link/MachO/ZigObject.zig +++ b/src/link/MachO/ZigObject.zig @@ -881,7 +881,7 @@ pub fn updateNav( const name = @"extern".name.toSlice(ip); const lib_name = @"extern".lib_name.toSlice(ip); const sym_index = try self.getGlobalSymbol(macho_file, name, lib_name); - if (@"extern".is_threadlocal) self.symbols.items[sym_index].flags.tlv = true; + if (@"extern".is_threadlocal and macho_file.base.comp.config.any_non_single_threaded) self.symbols.items[sym_index].flags.tlv = true; if (self.dwarf) |*dwarf| dwarf: { var debug_wip_nav = try dwarf.initWipNav(pt, nav_index, sym_index) orelse break :dwarf; defer debug_wip_nav.deinit(); @@ -1154,7 +1154,6 @@ fn getNavOutputSection( ) error{OutOfMemory}!u8 { _ = self; const ip = &zcu.intern_pool; - const any_non_single_threaded = macho_file.base.comp.config.any_non_single_threaded; const nav_val = zcu.navValue(nav_index); if (ip.isFunctionType(nav_val.typeOf(zcu).toIntern())) return macho_file.zig_text_sect_index.?; const is_const, const is_threadlocal, const nav_init = switch (ip.indexToKey(nav_val.toIntern())) { @@ -1162,7 +1161,7 @@ fn getNavOutputSection( .@"extern" => |@"extern"| .{ @"extern".is_const, @"extern".is_threadlocal, .none }, else => .{ true, false, nav_val.toIntern() }, }; - if (any_non_single_threaded and is_threadlocal) { + if (is_threadlocal and macho_file.base.comp.config.any_non_single_threaded) { for (code) |byte| { if (byte != 0) break; } else return macho_file.getSectionByName("__DATA", "__thread_bss") orelse try macho_file.addSection( -- 2.54.0 From 746137034e244d863a022b821ae0a1952d9d93c1 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Mon, 9 Jun 2025 02:34:30 -0400 Subject: [PATCH 23/35] Sema: fix union layout logic to match struct layout logic --- src/Sema.zig | 10 +++++++++- src/Type.zig | 33 +++++++++++++++++---------------- 2 files changed, 26 insertions(+), 17 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 97c9217a5e18bd26253c997e3cee2ac8265fa212..310058a4214d3312f4431ce9c2f0d8c19a572d58 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -35054,7 +35054,7 @@ pub fn resolveUnionAlignment( union_type.setAlignment(ip, max_align); } -/// This logic must be kept in sync with `Zcu.getUnionLayout`. +/// This logic must be kept in sync with `Type.getUnionLayout`. pub fn resolveUnionLayout(sema: *Sema, ty: Type) SemaError!void { const pt = sema.pt; const ip = &pt.zcu.intern_pool; @@ -35090,6 +35090,14 @@ pub fn resolveUnionLayout(sema: *Sema, ty: Type) SemaError!void { const field_ty: Type = .fromInterned(union_type.field_types.get(ip)[field_index]); if (field_ty.isNoReturn(pt.zcu)) continue; + // We need to call `hasRuntimeBits` before calling `abiSize` to prevent reachable `unreachable`s, + // but `hasRuntimeBits` only resolves field types and so may infinite recurse on a layout wip type, + // so we must resolve the layout manually first, instead of waiting for `abiSize` to do it for us. + // This is arguably just hacking around bugs in both `abiSize` for not allowing arbitrary types to + // be queried, enabling failures to be handled with the emission of a compile error, and also in + // `hasRuntimeBits` for ever being able to infinite recurse in the first place. + try field_ty.resolveLayout(pt); + if (try field_ty.hasRuntimeBitsSema(pt)) { max_size = @max(max_size, field_ty.abiSizeSema(pt) catch |err| switch (err) { error.AnalysisFail => { diff --git a/src/Type.zig b/src/Type.zig index 64b389cf5f1d4e0de795085f6fc0b26356b79909..eafdd65531c4f15a981511e7eae4002aea1c128a 100644 --- a/src/Type.zig +++ b/src/Type.zig @@ -3915,29 +3915,30 @@ fn resolveUnionInner( pub fn getUnionLayout(loaded_union: InternPool.LoadedUnionType, zcu: *const Zcu) Zcu.UnionLayout { const ip = &zcu.intern_pool; assert(loaded_union.haveLayout(ip)); - var most_aligned_field: u32 = undefined; - var most_aligned_field_size: u64 = undefined; - var biggest_field: u32 = undefined; + var most_aligned_field: u32 = 0; + var most_aligned_field_align: InternPool.Alignment = .@"1"; + var most_aligned_field_size: u64 = 0; + var biggest_field: u32 = 0; var payload_size: u64 = 0; var payload_align: InternPool.Alignment = .@"1"; - for (loaded_union.field_types.get(ip), 0..) |field_ty, field_index| { - if (Type.fromInterned(field_ty).isNoReturn(zcu)) continue; + for (loaded_union.field_types.get(ip), 0..) |field_ty_ip_index, field_index| { + const field_ty: Type = .fromInterned(field_ty_ip_index); + if (field_ty.isNoReturn(zcu)) continue; const explicit_align = loaded_union.fieldAlign(ip, field_index); const field_align = if (explicit_align != .none) explicit_align else - Type.fromInterned(field_ty).abiAlignment(zcu); - if (Type.fromInterned(field_ty).hasRuntimeBits(zcu)) { - const field_size = Type.fromInterned(field_ty).abiSize(zcu); - if (field_size > payload_size) { - payload_size = field_size; - biggest_field = @intCast(field_index); - } - if (field_align.compare(.gte, payload_align)) { - most_aligned_field = @intCast(field_index); - most_aligned_field_size = field_size; - } + field_ty.abiAlignment(zcu); + const field_size = field_ty.abiSize(zcu); + if (field_size > payload_size) { + payload_size = field_size; + biggest_field = @intCast(field_index); + } + if (field_size > 0 and field_align.compare(.gte, most_aligned_field_align)) { + most_aligned_field = @intCast(field_index); + most_aligned_field_align = field_align; + most_aligned_field_size = field_size; } payload_align = payload_align.max(field_align); } -- 2.54.0 From afa07f723f956d78a4bd4c4be10ef04e86e50521 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Mon, 9 Jun 2025 09:05:58 -0400 Subject: [PATCH 24/35] x86_64: implement coff relocations --- src/arch/x86_64/CodeGen.zig | 6 +--- src/arch/x86_64/Emit.zig | 71 +++++++++++++++++++++++++++++-------- src/codegen.zig | 4 +-- src/link/Coff.zig | 4 +-- 4 files changed, 61 insertions(+), 24 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 4a0113fe366f625a561fc8fbfbc3dfd58a155fa6..c652d48f3e12782087d1eed0c1461193da133670 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -85269,11 +85269,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .ret => try cg.airRet(inst, false), .ret_safe => try cg.airRet(inst, true), .ret_load => try cg.airRetLoad(inst), - .store, .store_safe => |air_tag| if (use_old) try cg.airStore(inst, switch (air_tag) { - else => unreachable, - .store => false, - .store_safe => true, - }) else fallback: { + .store, .store_safe => |air_tag| fallback: { const bin_op = air_datas[@intFromEnum(inst)].bin_op; const ptr_ty = cg.typeOf(bin_op.lhs); const ptr_info = ptr_ty.ptrInfo(zcu); diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index 8a9609dafc3abceb8050ee80feca879c4fd9d213..e6b4ac26bbfa5346a32c6514e4f33c4144b6d3e2 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -107,10 +107,7 @@ pub fn emitMir(emit: *Emit) Error!void { nav, emit.lower.target.*, )) { - .mcv => |mcv| switch (mcv) { - else => std.debug.panic("{s}: {}\n", .{ @src().fn_name, mcv }), - .lea_symbol => |sym_index| sym_index, - }, + .mcv => |mcv| mcv.lea_symbol, .fail => |em| { assert(emit.lower.err_msg == null); emit.lower.err_msg = em; @@ -154,10 +151,7 @@ pub fn emitMir(emit: *Emit) Error!void { Type.fromInterned(uav.orig_ty).ptrAlignment(emit.pt.zcu), emit.lower.src_loc, )) { - .mcv => |mcv| switch (mcv) { - else => std.debug.panic("{s}: {}\n", .{ @src().fn_name, mcv }), - .load_direct, .load_symbol => |sym_index| sym_index, - }, + .mcv => |mcv| mcv.load_symbol, .fail => |em| { assert(emit.lower.err_msg == null); emit.lower.err_msg = em; @@ -207,7 +201,9 @@ pub fn emitMir(emit: *Emit) Error!void { switch (lowered_inst.encoding.mnemonic) { .call => { reloc.target.type = .branch; - try emit.encodeInst(lowered_inst, reloc_info); + if (emit.bin_file.cast(.coff)) |_| try emit.encodeInst(try .new(.none, .call, &.{ + .{ .mem = .initRip(.ptr, 0) }, + }, emit.lower.target), reloc_info) else try emit.encodeInst(lowered_inst, reloc_info); continue :lowered_inst; }, else => {}, @@ -284,6 +280,37 @@ pub fn emitMir(emit: *Emit) Error!void { }, emit.lower.target), reloc_info), else => unreachable, } + } else if (emit.bin_file.cast(.coff)) |_| { + if (reloc.target.is_extern) switch (lowered_inst.encoding.mnemonic) { + .lea => try emit.encodeInst(try .new(.none, .mov, &.{ + lowered_inst.ops[0], + .{ .mem = .initRip(.ptr, 0) }, + }, emit.lower.target), reloc_info), + .mov => { + const dst_reg = lowered_inst.ops[0].reg.to64(); + try emit.encodeInst(try .new(.none, .mov, &.{ + .{ .reg = dst_reg }, + .{ .mem = .initRip(.ptr, 0) }, + }, emit.lower.target), reloc_info); + try emit.encodeInst(try .new(.none, .mov, &.{ + lowered_inst.ops[0], + .{ .mem = .initSib(lowered_inst.ops[reloc.op_index].mem.sib.ptr_size, .{ .base = .{ + .reg = dst_reg, + } }) }, + }, emit.lower.target), &.{}); + }, + else => unreachable, + } else switch (lowered_inst.encoding.mnemonic) { + .lea => try emit.encodeInst(try .new(.none, .lea, &.{ + lowered_inst.ops[0], + .{ .mem = .initRip(.none, 0) }, + }, emit.lower.target), reloc_info), + .mov => try emit.encodeInst(try .new(.none, .mov, &.{ + lowered_inst.ops[0], + .{ .mem = .initRip(lowered_inst.ops[reloc.op_index].mem.sib.ptr_size, 0) }, + }, emit.lower.target), reloc_info), + else => unreachable, + } } else return emit.fail("TODO implement relocs for {s}", .{ @tagName(emit.bin_file.tag), }); @@ -751,6 +778,21 @@ fn encodeInst(emit: *Emit, lowered_inst: Instruction, reloc_info: []const RelocI .symbolnum = @intCast(reloc.target.index), }, }); + } else if (emit.bin_file.cast(.coff)) |coff_file| { + const atom_index = coff_file.getAtomIndexForSymbol( + .{ .sym_index = emit.atom_index, .file = null }, + ).?; + try coff_file.addRelocation(atom_index, .{ + .type = if (reloc.target.is_extern) .got else .direct, + .target = if (reloc.target.is_extern) + coff_file.getGlobalByIndex(reloc.target.index) + else + .{ .sym_index = reloc.target.index, .file = null }, + .offset = end_offset - 4, + .addend = @intCast(reloc.off), + .pcrel = true, + .length = 2, + }); } else unreachable, .branch => if (emit.bin_file.cast(.elf)) |elf_file| { const zo = elf_file.zigObjectPtr().?; @@ -781,13 +823,12 @@ fn encodeInst(emit: *Emit, lowered_inst: Instruction, reloc_info: []const RelocI const atom_index = coff_file.getAtomIndexForSymbol( .{ .sym_index = emit.atom_index, .file = null }, ).?; - const target: link.File.Coff.SymbolWithLoc = if (link.File.Coff.global_symbol_bit & reloc.target.index != 0) - coff_file.getGlobalByIndex(link.File.Coff.global_symbol_mask & reloc.target.index) - else - .{ .sym_index = reloc.target.index, .file = null }; try coff_file.addRelocation(atom_index, .{ - .type = .direct, - .target = target, + .type = if (reloc.target.is_extern) .import else .got, + .target = if (reloc.target.is_extern) + coff_file.getGlobalByIndex(reloc.target.index) + else + .{ .sym_index = reloc.target.index, .file = null }, .offset = end_offset - 4, .addend = @intCast(reloc.off), .pcrel = true, diff --git a/src/codegen.zig b/src/codegen.zig index df9c0b44641160a2ee1f85e0175474b79fbbd9a5..9cc27b55ba6a60a44f20b23cceeab86ebee51a86 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -1015,12 +1015,12 @@ pub fn genNavRef( .internal => { const atom_index = try coff_file.getOrCreateAtomForNav(nav_index); const sym_index = coff_file.getAtom(atom_index).getSymbolIndex().?; - return .{ .mcv = .{ .load_got = sym_index } }; + return .{ .mcv = .{ .lea_symbol = sym_index } }; }, .strong, .weak => { const global_index = try coff_file.getGlobalSymbol(nav.name.toSlice(ip), lib_name.toSlice(ip)); try coff_file.need_got_table.put(zcu.gpa, global_index, {}); // needs GOT - return .{ .mcv = .{ .load_got = link.File.Coff.global_symbol_bit | global_index } }; + return .{ .mcv = .{ .lea_symbol = global_index } }; }, .link_once => unreachable, } diff --git a/src/link/Coff.zig b/src/link/Coff.zig index c9234b335db15b1d89c2413b71a3c3da2b0ce7f2..0e00229b78e070bdf5f3825544d5795a9719889a 100644 --- a/src/link/Coff.zig +++ b/src/link/Coff.zig @@ -1767,7 +1767,7 @@ pub fn lowerUav( const atom = coff.getAtom(metadata.atom); const existing_addr = atom.getSymbol(coff).value; if (uav_alignment.check(existing_addr)) - return .{ .mcv = .{ .load_direct = atom.getSymbolIndex().? } }; + return .{ .mcv = .{ .load_symbol = atom.getSymbolIndex().? } }; } var name_buf: [32]u8 = undefined; @@ -1799,7 +1799,7 @@ pub fn lowerUav( .section = coff.rdata_section_index.?, }); return .{ .mcv = .{ - .load_direct = coff.getAtom(atom_index).getSymbolIndex().?, + .load_symbol = coff.getAtom(atom_index).getSymbolIndex().?, } }; } -- 2.54.0 From 22e961070d2299d6189b0ad82e04763e504a0208 Mon Sep 17 00:00:00 2001 From: mlugg Date: Wed, 11 Jun 2025 02:09:09 +0100 Subject: [PATCH 25/35] link: fix goff and xcoff flush --- src/link/Goff.zig | 4 +++- src/link/Xcoff.zig | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/link/Goff.zig b/src/link/Goff.zig index ec4cb1252b18b1d1937d16603f3111059234df6a..1f4a7a4d30953cb9b7d4f70906fc8885e74fc567 100644 --- a/src/link/Goff.zig +++ b/src/link/Goff.zig @@ -102,9 +102,11 @@ pub fn updateExports( } pub fn flush(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"); + _ = self; _ = arena; _ = tid; _ = prog_node; - unreachable; // we always use llvm } diff --git a/src/link/Xcoff.zig b/src/link/Xcoff.zig index bbd8a3fea4324884536d7ecaad954bea601296da..fd143713ffcf4b06d9cc6192efeb9f99b5aa2af9 100644 --- a/src/link/Xcoff.zig +++ b/src/link/Xcoff.zig @@ -102,9 +102,11 @@ pub fn updateExports( } pub fn flush(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"); + _ = self; _ = arena; _ = tid; _ = prog_node; - unreachable; // we always use llvm } -- 2.54.0 From 7f2f107a1ed451c94da0db6ff559bfee3ce512a5 Mon Sep 17 00:00:00 2001 From: mlugg Date: Wed, 11 Jun 2025 02:12:04 +0100 Subject: [PATCH 26/35] Zcu: SPIR-V also doesn't generate MIR (yet) --- src/Zcu/PerThread.zig | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Zcu/PerThread.zig b/src/Zcu/PerThread.zig index 9b6a43b496f7dab90b8cbdab9f57e8653e23c926..7416ebbaabb079a519f2e99e190097cf9f9e0c26 100644 --- a/src/Zcu/PerThread.zig +++ b/src/Zcu/PerThread.zig @@ -4392,7 +4392,9 @@ pub fn runCodegen(pt: Zcu.PerThread, func_index: InternPool.Index, air: *Air, ou const backend = target_util.zigBackend(zcu.root_mod.resolved_target.result, zcu.comp.config.use_llvm); switch (backend) { else => unreachable, // assertion failure - .stage2_llvm => {}, + .stage2_spirv64, + .stage2_llvm, + => {}, } out.status.store(.failed, .monotonic); }, -- 2.54.0 From 1b27369acbb2009935d49c6906363e8fa425313a Mon Sep 17 00:00:00 2001 From: mlugg Date: Wed, 11 Jun 2025 02:25:33 +0100 Subject: [PATCH 27/35] cli: correctly error for missing output directories --- src/main.zig | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/main.zig b/src/main.zig index dc1d66381b8def8c83f35c52dcb59f9d8b2b3e7b..3821fafb80e1ba3099b66df995bee22ad34c9b33 100644 --- a/src/main.zig +++ b/src/main.zig @@ -712,7 +712,16 @@ const Emit = union(enum) { .{ @tagName(reason), path }, ), } - } else .{ .yes_path = path }, + } else e: { + // If there's a dirname, check that dir exists. This will give a more descriptive error than `Compilation` otherwise would. + if (fs.path.dirname(path)) |dir_path| { + var dir = fs.cwd().openDir(dir_path, .{}) catch |err| { + fatal("unable to open output directory '{s}': {s}", .{ dir_path, @errorName(err) }); + }; + dir.close(); + } + break :e .{ .yes_path = path }; + }, }; } }; @@ -3220,7 +3229,16 @@ fn buildOutputType( .yes => |path| if (output_to_cache != null) { assert(output_to_cache == .listen); // there was an explicit bin path fatal("--listen incompatible with explicit output path '{s}'", .{path}); - } else .{ .yes_path = path }, + } else emit: { + // If there's a dirname, check that dir exists. This will give a more descriptive error than `Compilation` otherwise would. + if (fs.path.dirname(path)) |dir_path| { + var dir = fs.cwd().openDir(dir_path, .{}) catch |err| { + fatal("unable to open output directory '{s}': {s}", .{ dir_path, @errorName(err) }); + }; + dir.close(); + } + break :emit .{ .yes_path = path }; + }, .yes_a_out => emit: { assert(output_to_cache == null); break :emit .{ .yes_path = switch (target.ofmt) { -- 2.54.0 From ff89a98c50dbf826564657e7f98cc56194add163 Mon Sep 17 00:00:00 2001 From: mlugg Date: Wed, 11 Jun 2025 02:25:55 +0100 Subject: [PATCH 28/35] link.Queue: release safety lock before releasing mutex after stopping --- src/link/Queue.zig | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/link/Queue.zig b/src/link/Queue.zig index ab5fd89699a32b807f3abdf6ebfc2b222583db7d..16ee701771bf3810ef0017dee4bf54189e3c1be4 100644 --- a/src/link/Queue.zig +++ b/src/link/Queue.zig @@ -147,8 +147,7 @@ pub fn enqueueZcu(q: *Queue, comp: *Compilation, task: ZcuTask) Allocator.Error! } fn flushTaskQueue(tid: usize, q: *Queue, comp: *Compilation) void { - q.flush_safety.lock(); - defer q.flush_safety.unlock(); + q.flush_safety.lock(); // every `return` site should unlock this before unlocking `q.mutex` if (std.debug.runtime_safety) { q.mutex.lock(); @@ -167,6 +166,7 @@ fn flushTaskQueue(tid: usize, q: *Queue, comp: *Compilation) void { } else { // We're expecting more prelink tasks so can't move on to ZCU tasks. q.state = .finished; + q.flush_safety.unlock(); return; } } @@ -200,6 +200,7 @@ fn flushTaskQueue(tid: usize, q: *Queue, comp: *Compilation) void { if (q.wip_zcu.items.len == 0) { // We've exhausted all available tasks. q.state = .finished; + q.flush_safety.unlock(); return; } } @@ -215,6 +216,7 @@ fn flushTaskQueue(tid: usize, q: *Queue, comp: *Compilation) void { if (status_ptr.load(.monotonic) != .pending) break :pending; // We will stop for now, and get restarted once this MIR is ready. q.state = .{ .wait_for_mir = task.link_func.mir }; + q.flush_safety.unlock(); return; } link.doZcuTask(comp, tid, task); -- 2.54.0 From 4d2b216121e5fbdb019ab8e727df654e7b08e984 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Tue, 10 Jun 2025 21:57:46 -0400 Subject: [PATCH 29/35] test-stack-traces: correct expected object file name The name of the ZCU object file emitted by the LLVM backend has been changed in this branch from e.g. `foo.obj` to `foo_zcu.obj`. This is to avoid name clashes. This commit just updates the stack trace tests which started failing on windows because of the object name change. --- test/src/check-stack-trace.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/src/check-stack-trace.zig b/test/src/check-stack-trace.zig index fccbbe36090fc77afdee5fd1b3a64df2b320e8e3..43800086afd324eb99c6c6dc03e1ff8d7ac94282 100644 --- a/test/src/check-stack-trace.zig +++ b/test/src/check-stack-trace.zig @@ -65,7 +65,7 @@ pub fn main() !void { // This actually violates the DWARF specification (DWARF5 ยง 3.1.1, lines 24-27). // The self-hosted backend uses the root Zig source file of the module (in compilance with the spec). if (std.mem.eql(u8, file_name, "test") or - std.mem.eql(u8, file_name, "test.exe.obj") or + std.mem.eql(u8, file_name, "test_zcu.obj") or std.mem.endsWith(u8, file_name, ".zig")) { try buf.appendSlice("[main_file]"); -- 2.54.0 From d7afd797ccdeeab74946f047c3e755f33b5ea9b9 Mon Sep 17 00:00:00 2001 From: mlugg Date: Wed, 11 Jun 2025 14:27:21 +0100 Subject: [PATCH 30/35] Zcu: handle unreferenced `test_functions` correctly Previously, `PerThread.populateTestFunctions` was analyzing the `test_functions` declaration if it hadn't already been analyzed, so that it could then populate it. However, the logic for doing this wasn't actually correct, because it didn't trigger the necessary type resolution. I could have tried to fix this, but there's actually a simpler solution! If the `test_functions` declaration isn't referenced or has a compile error, then we simply don't need to update it; either it's unreferenced so its value doesn't matter, or we're going to get a compile error anyway. Either way, we can just give up early. This avoids doing semantic analysis after `performAllTheWork` finishes. Also, get rid of the "Code Generation" progress node while updating the test decl: this is a linking task. --- src/Compilation.zig | 2 +- src/Zcu/PerThread.zig | 58 +++++++++++++++++++------------------------ 2 files changed, 27 insertions(+), 33 deletions(-) diff --git a/src/Compilation.zig b/src/Compilation.zig index 04cd03c3d8393e6d2df205bf925f4f67fd4a0041..49b2a6b8b626f44842c598e0b931d3dc60653602 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -2817,7 +2817,7 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void { // The `test_functions` decl has been intentionally postponed until now, // at which point we must populate it with the list of test functions that // have been discovered and not filtered out. - try pt.populateTestFunctions(main_progress_node); + try pt.populateTestFunctions(); } try pt.processExports(); diff --git a/src/Zcu/PerThread.zig b/src/Zcu/PerThread.zig index 7416ebbaabb079a519f2e99e190097cf9f9e0c26..4ec6eebc462dba3d016e541302447fa7aaf57d9d 100644 --- a/src/Zcu/PerThread.zig +++ b/src/Zcu/PerThread.zig @@ -3229,39 +3229,42 @@ fn processExportsInner( } } -pub fn populateTestFunctions( - pt: Zcu.PerThread, - main_progress_node: std.Progress.Node, -) Allocator.Error!void { +pub fn populateTestFunctions(pt: Zcu.PerThread) Allocator.Error!void { const zcu = pt.zcu; const gpa = zcu.gpa; const ip = &zcu.intern_pool; + + // Our job is to correctly set the value of the `test_functions` declaration if it has been + // analyzed and sent to codegen, It usually will have been, because the test runner will + // reference it, and `std.builtin` shouldn't have type errors. However, if it hasn't been + // analyzed, we will just terminate early, since clearly the test runner hasn't referenced + // `test_functions` so there's no point populating it. More to the the point, we potentially + // *can't* populate it without doing some type resolution, and... let's try to leave Sema in + // the past here. + const builtin_mod = zcu.builtin_modules.get(zcu.root_mod.getBuiltinOptions(zcu.comp.config).hash()).?; const builtin_file_index = zcu.module_roots.get(builtin_mod).?.unwrap().?; - pt.ensureFileAnalyzed(builtin_file_index) catch |err| switch (err) { - error.AnalysisFail => unreachable, // builtin module is generated so cannot be corrupt - error.OutOfMemory => |e| return e, - }; - const builtin_root_type = Type.fromInterned(zcu.fileRootType(builtin_file_index)); - const builtin_namespace = builtin_root_type.getNamespace(zcu).unwrap().?; + const builtin_root_type = zcu.fileRootType(builtin_file_index); + if (builtin_root_type == .none) return; // `@import("builtin")` never analyzed + const builtin_namespace = Type.fromInterned(builtin_root_type).getNamespace(zcu).unwrap().?; + // We know that the namespace has a `test_functions`... const nav_index = zcu.namespacePtr(builtin_namespace).pub_decls.getKeyAdapted( try ip.getOrPutString(gpa, pt.tid, "test_functions", .no_embedded_nulls), Zcu.Namespace.NameAdapter{ .zcu = zcu }, ).?; + // ...but it might not be populated, so let's check that! + if (zcu.failed_analysis.contains(.wrap(.{ .nav_val = nav_index })) or + zcu.transitive_failed_analysis.contains(.wrap(.{ .nav_val = nav_index })) or + ip.getNav(nav_index).status != .fully_resolved) { - // We have to call `ensureNavValUpToDate` here in case `builtin.test_functions` - // was not referenced by start code. - zcu.sema_prog_node = main_progress_node.start("Semantic Analysis", 0); - defer { - zcu.sema_prog_node.end(); - zcu.sema_prog_node = std.Progress.Node.none; - } - pt.ensureNavValUpToDate(nav_index) catch |err| switch (err) { - error.AnalysisFail => return, - error.OutOfMemory => return error.OutOfMemory, - }; + // The value of `builtin.test_functions` was either never referenced, or failed analysis. + // Either way, we don't need to do anything. + return; } + // Okay, `builtin.test_functions` is (potentially) referenced and valid. Our job now is to swap + // its placeholder `&.{}` value for the actual list of all test functions. + const test_fns_val = zcu.navValue(nav_index); const test_fn_ty = test_fns_val.typeOf(zcu).slicePtrFieldType(zcu).childType(zcu); @@ -3363,17 +3366,8 @@ pub fn populateTestFunctions( } }); ip.mutateVarInit(test_fns_val.toIntern(), new_init); } - { - assert(zcu.codegen_prog_node.index == .none); - zcu.codegen_prog_node = main_progress_node.start("Code Generation", 0); - defer { - zcu.codegen_prog_node.end(); - zcu.codegen_prog_node = std.Progress.Node.none; - } - - // The linker thread is not running, so we actually need to dispatch this task directly. - @import("../link.zig").linkTestFunctionsNav(pt, nav_index); - } + // The linker thread is not running, so we actually need to dispatch this task directly. + @import("../link.zig").linkTestFunctionsNav(pt, nav_index); } /// Stores an error in `pt.zcu.failed_files` for this file, and sets the file -- 2.54.0 From de69d6317516af95e09ca7a48484fa175b171d11 Mon Sep 17 00:00:00 2001 From: mlugg Date: Wed, 11 Jun 2025 21:36:16 +0100 Subject: [PATCH 31/35] stage1: elaborate on "unimplemented" in wasi.c --- stage1/wasi.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/stage1/wasi.c b/stage1/wasi.c index 0c9ca18c57bae17786d8e0df92ab3730374831e2..ef2183dae849d56d86dd6c7a2eff7272f91aaf0b 100644 --- a/stage1/wasi.c +++ b/stage1/wasi.c @@ -517,7 +517,7 @@ uint32_t wasi_snapshot_preview1_fd_read(uint32_t fd, uint32_t iovs, uint32_t iov case wasi_filetype_character_device: break; case wasi_filetype_regular_file: break; case wasi_filetype_directory: return wasi_errno_inval; - default: panic("unimplemented"); + default: panic("unimplemented: fd_read special file"); } size_t size = 0; @@ -629,7 +629,7 @@ uint32_t wasi_snapshot_preview1_fd_pwrite(uint32_t fd, uint32_t iovs, uint32_t i case wasi_filetype_character_device: break; case wasi_filetype_regular_file: break; case wasi_filetype_directory: return wasi_errno_inval; - default: panic("unimplemented"); + default: panic("unimplemented: fd_pwrite special file"); } fpos_t pos; @@ -679,7 +679,7 @@ uint32_t wasi_snapshot_preview1_fd_filestat_set_times(uint32_t fd, uint64_t atim fprintf(stderr, "wasi_snapshot_preview1_fd_filestat_set_times(%u, %llu, %llu, 0x%X)\n", fd, (unsigned long long)atim, (unsigned long long)mtim, fst_flags); #endif - panic("unimplemented"); + panic("unimplemented: fd_filestat_set_times"); return wasi_errno_success; } @@ -703,7 +703,7 @@ uint32_t wasi_snapshot_preview1_environ_get(uint32_t environ, uint32_t environ_b fprintf(stderr, "wasi_snapshot_preview1_environ_get()\n"); #endif - panic("unimplemented"); + panic("unimplemented: environ_get"); return wasi_errno_success; } @@ -757,7 +757,7 @@ uint32_t wasi_snapshot_preview1_fd_readdir(uint32_t fd, uint32_t buf, uint32_t b fprintf(stderr, "wasi_snapshot_preview1_fd_readdir(%u, 0x%X, %u, %llu)\n", fd, buf, buf_len, (unsigned long long)cookie); #endif - panic("unimplemented"); + panic("unimplemented: fd_readdir"); return wasi_errno_success; } @@ -774,7 +774,7 @@ uint32_t wasi_snapshot_preview1_fd_write(uint32_t fd, uint32_t iovs, uint32_t io case wasi_filetype_character_device: break; case wasi_filetype_regular_file: break; case wasi_filetype_directory: return wasi_errno_inval; - default: panic("unimplemented"); + default: panic("unimplemented: fd_write special file"); } size_t size = 0; @@ -825,7 +825,7 @@ uint32_t wasi_snapshot_preview1_path_open(uint32_t fd, uint32_t dirflags, uint32 fds[fd_len].fdflags = fdflags; switch (des[de].filetype) { case wasi_filetype_directory: fds[fd_len].stream = NULL; break; - default: panic("unimplemented"); + default: panic("unimplemented: path_open non-directory DirEntry"); } fds[fd_len].fs_rights_inheriting = fs_rights_inheriting; @@ -943,7 +943,7 @@ uint32_t wasi_snapshot_preview1_path_unlink_file(uint32_t fd, uint32_t path, uin enum wasi_errno lookup_errno = DirEntry_lookup(fd, 0, path_ptr, path_len, &de); if (lookup_errno != wasi_errno_success) return lookup_errno; if (des[de].filetype == wasi_filetype_directory) return wasi_errno_isdir; - if (des[de].filetype != wasi_filetype_regular_file) panic("unimplemented"); + if (des[de].filetype != wasi_filetype_regular_file) panic("unimplemented: path_unlink_file special file"); DirEntry_unlink(de); return wasi_errno_success; } @@ -961,7 +961,7 @@ uint32_t wasi_snapshot_preview1_fd_pread(uint32_t fd, uint32_t iovs, uint32_t io case wasi_filetype_character_device: break; case wasi_filetype_regular_file: break; case wasi_filetype_directory: return wasi_errno_inval; - default: panic("unimplemented"); + default: panic("unimplemented: fd_pread special file"); } fpos_t pos; @@ -975,7 +975,7 @@ uint32_t wasi_snapshot_preview1_fd_pread(uint32_t fd, uint32_t iovs, uint32_t io if (fds[fd].stream != NULL) read_size = fread(&m[load32_align2(&iovs_ptr[i].ptr)], 1, len, fds[fd].stream); else - panic("unimplemented"); + panic("unimplemented: fd_pread stream=NULL"); size += read_size; if (read_size < len) break; } @@ -1000,7 +1000,7 @@ uint32_t wasi_snapshot_preview1_fd_seek(uint32_t fd, uint64_t in_offset, uint32_ case wasi_filetype_character_device: break; case wasi_filetype_regular_file: break; case wasi_filetype_directory: return wasi_errno_inval; - default: panic("unimplemented"); + default: panic("unimplemented: fd_seek special file"); } if (fds[fd].stream == NULL) return wasi_errno_success; @@ -1035,7 +1035,7 @@ uint32_t wasi_snapshot_preview1_poll_oneoff(uint32_t in, uint32_t out, uint32_t fprintf(stderr, "wasi_snapshot_preview1_poll_oneoff(%u)\n", nsubscriptions); #endif - panic("unimplemented"); + panic("unimplemented: poll_oneoff"); return wasi_errno_success; } -- 2.54.0 From f9a670d46de3c62be16202f186eacfee6ec096d4 Mon Sep 17 00:00:00 2001 From: mlugg Date: Wed, 11 Jun 2025 22:00:41 +0100 Subject: [PATCH 32/35] Compilation: prevent zig1 depending on fd_readdir This isn't really coherent to model as a `Feature`; this makes sense because of zig1's specific environment. As such, I opted to check `dev.env` directly. --- src/Compilation.zig | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Compilation.zig b/src/Compilation.zig index 49b2a6b8b626f44842c598e0b931d3dc60653602..9f851cf135763d20bb9b8ebbe77e47294cccbe64 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -2589,6 +2589,11 @@ fn cleanupAfterUpdate(comp: *Compilation, tmp_dir_rand_int: u64) void { if (none.tmp_artifact_directory) |*tmp_dir| { tmp_dir.handle.close(); none.tmp_artifact_directory = null; + if (dev.env == .bootstrap) { + // zig1 uses `CacheMode.none`, but it doesn't need to know how to delete + // temporary directories; it doesn't have a real cache directory anyway. + return; + } const tmp_dir_sub_path = "tmp" ++ std.fs.path.sep_str ++ std.fmt.hex(tmp_dir_rand_int); comp.dirs.local_cache.handle.deleteTree(tmp_dir_sub_path) catch |err| { log.warn("failed to delete temporary directory '{s}{c}{s}': {s}", .{ -- 2.54.0 From 5bb5aaf932b8ed30aebfbb0036e1532abfc6af46 Mon Sep 17 00:00:00 2001 From: mlugg Date: Thu, 12 Jun 2025 09:56:37 +0100 Subject: [PATCH 33/35] compiler: don't queue too much AIR/MIR Without this cap, unlucky scheduling and/or details of what pipeline stages perform best on the host machine could cause many gigabytes of MIR to be stuck in the queue. At a certain point, pause the main thread until some of the functions in flight have been processed. --- src/Compilation.zig | 6 ++++++ src/link.zig | 5 +++++ src/link/Queue.zig | 44 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+) diff --git a/src/Compilation.zig b/src/Compilation.zig index 9f851cf135763d20bb9b8ebbe77e47294cccbe64..ad184b2bc94de6f5d5e9e66e740d4b4e60b7439c 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -4607,12 +4607,17 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job) JobError!void { }; assert(zcu.pending_codegen_jobs.rmw(.Add, 1, .monotonic) > 0); // the "Code Generation" node hasn't been ended zcu.codegen_prog_node.increaseEstimatedTotalItems(1); + // This value is used as a heuristic to avoid queueing too much AIR/MIR at once (hence + // using a lot of memory). If this would cause too many AIR bytes to be in-flight, we + // will block on the `dispatchZcuLinkTask` call below. + const air_bytes: u32 = @intCast(air.instructions.len * 5 + air.extra.items.len * 4); if (comp.separateCodegenThreadOk()) { // `workerZcuCodegen` takes ownership of `air`. comp.thread_pool.spawnWgId(&comp.link_task_wait_group, workerZcuCodegen, .{ comp, func.func, air, shared_mir }); comp.dispatchZcuLinkTask(tid, .{ .link_func = .{ .func = func.func, .mir = shared_mir, + .air_bytes = air_bytes, } }); } else { { @@ -4624,6 +4629,7 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job) JobError!void { comp.dispatchZcuLinkTask(tid, .{ .link_func = .{ .func = func.func, .mir = shared_mir, + .air_bytes = air_bytes, } }); air.deinit(gpa); } diff --git a/src/link.zig b/src/link.zig index ce98ac89298cadc42a8d082a0fe7edb69e8e0043..9bed6b41314d082d30ac1b21e493f8a383ca7ac2 100644 --- a/src/link.zig +++ b/src/link.zig @@ -1267,6 +1267,11 @@ pub const ZcuTask = union(enum) { /// the codegen job to ensure that the linker receives functions in a deterministic order, /// allowing reproducible builds. mir: *SharedMir, + /// This is not actually used by `doZcuTask`. Instead, `Queue` uses this value as a heuristic + /// to avoid queueing too much AIR/MIR for codegen/link at a time. Essentially, we cap the + /// total number of AIR bytes which are being processed at once, preventing unbounded memory + /// usage when AIR is produced faster than it is processed. + air_bytes: u32, pub const SharedMir = struct { /// This is initially `.pending`. When `value` is populated, the codegen thread will set diff --git a/src/link/Queue.zig b/src/link/Queue.zig index 16ee701771bf3810ef0017dee4bf54189e3c1be4..d197edab0233c6b6240307af734556ba9fa63b46 100644 --- a/src/link/Queue.zig +++ b/src/link/Queue.zig @@ -39,6 +39,21 @@ wip_zcu: std.ArrayListUnmanaged(ZcuTask), /// index into `wip_zcu` which we have reached. wip_zcu_idx: usize, +/// The sum of all `air_bytes` for all currently-queued `ZcuTask.link_func` tasks. Because +/// MIR bytes are approximately proportional to AIR bytes, this acts to limit the amount of +/// AIR and MIR which is queued for codegen and link respectively, to prevent excessive +/// memory usage if analysis produces AIR faster than it can be processed by codegen/link. +/// The cap is `max_air_bytes_in_flight`. +/// Guarded by `mutex`. +air_bytes_in_flight: u32, +/// If nonzero, then a call to `enqueueZcu` is blocked waiting to add a `link_func` task, but +/// cannot until `air_bytes_in_flight` is no greater than this value. +/// Guarded by `mutex`. +air_bytes_waiting: u32, +/// After setting `air_bytes_waiting`, `enqueueZcu` will wait on this condition (with `mutex`). +/// When `air_bytes_waiting` many bytes can be queued, this condition should be signaled. +air_bytes_cond: std.Thread.Condition, + /// Guarded by `mutex`. state: union(enum) { /// The link thread is currently running or queued to run. @@ -52,6 +67,11 @@ state: union(enum) { wait_for_mir: *ZcuTask.LinkFunc.SharedMir, }, +/// In the worst observed case, MIR is around 50 times as large as AIR. More typically, the ratio is +/// around 20. Going by that 50x multiplier, and assuming we want to consume no more than 500 MiB of +/// memory on AIR/MIR, we see a limit of around 10 MiB of AIR in-flight. +const max_air_bytes_in_flight = 10 * 1024 * 1024; + /// The initial `Queue` state, containing no tasks, expecting no prelink tasks, and with no running worker thread. /// The `pending_prelink_tasks` and `queued_prelink` fields may be modified as needed before calling `start`. pub const empty: Queue = .{ @@ -64,6 +84,9 @@ pub const empty: Queue = .{ .wip_zcu = .empty, .wip_zcu_idx = 0, .state = .finished, + .air_bytes_in_flight = 0, + .air_bytes_waiting = 0, + .air_bytes_cond = .{}, }; /// `lf` is needed to correctly deinit any pending `ZcuTask`s. pub fn deinit(q: *Queue, comp: *Compilation) void { @@ -131,6 +154,16 @@ pub fn enqueueZcu(q: *Queue, comp: *Compilation, task: ZcuTask) Allocator.Error! { q.mutex.lock(); defer q.mutex.unlock(); + // If this is a `link_func` task, we might need to wait for `air_bytes_in_flight` to fall. + if (task == .link_func) { + const max_in_flight = max_air_bytes_in_flight -| task.link_func.air_bytes; + while (q.air_bytes_in_flight > max_in_flight) { + q.air_bytes_waiting = task.link_func.air_bytes; + q.air_bytes_cond.wait(&q.mutex); + q.air_bytes_waiting = 0; + } + q.air_bytes_in_flight += task.link_func.air_bytes; + } try q.queued_zcu.append(comp.gpa, task); switch (q.state) { .running, .wait_for_mir => return, @@ -221,6 +254,17 @@ fn flushTaskQueue(tid: usize, q: *Queue, comp: *Compilation) void { } link.doZcuTask(comp, tid, task); task.deinit(comp.zcu.?); + if (task == .link_func) { + // Decrease `air_bytes_in_flight`, since we've finished processing this MIR. + q.mutex.lock(); + defer q.mutex.unlock(); + q.air_bytes_in_flight -= task.link_func.air_bytes; + if (q.air_bytes_waiting != 0 and + q.air_bytes_in_flight <= max_air_bytes_in_flight -| q.air_bytes_waiting) + { + q.air_bytes_cond.signal(); + } + } q.wip_zcu_idx += 1; } } -- 2.54.0 From 71baa5e769b3b82468736a60e0725a94da9be4e9 Mon Sep 17 00:00:00 2001 From: mlugg Date: Thu, 12 Jun 2025 13:53:41 +0100 Subject: [PATCH 34/35] compiler: improve progress output Update the estimated total items for the codegen and link progress nodes earlier. Rather than waiting for the main thread to dispatch the tasks, we can add the item to the estimated total as soon as we queue the main task. The only difference is we need to complete it even in error cases. --- src/Compilation.zig | 18 +++++++++++++++--- src/Sema.zig | 8 ++++++++ src/Sema/LowerZon.zig | 1 + src/Zcu/PerThread.zig | 7 +++++++ 4 files changed, 31 insertions(+), 3 deletions(-) diff --git a/src/Compilation.zig b/src/Compilation.zig index ad184b2bc94de6f5d5e9e66e740d4b4e60b7439c..065b717931cdece3d9d67e948aefc5f9a161f19f 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -848,6 +848,8 @@ const Job = union(enum) { /// This `Job` exists (instead of the `link.ZcuTask` being directly queued) to ensure that /// all types are resolved before the linker task is queued. /// If the backend does not support `Zcu.Feature.separate_thread`, codegen and linking happen immediately. + /// Before queueing this `Job`, increase the estimated total item count for both + /// `comp.zcu.?.codegen_prog_node` and `comp.link_prog_node`. codegen_func: struct { func: InternPool.Index, /// The AIR emitted from analyzing `func`; owned by this `Job` in `gpa`. @@ -857,12 +859,15 @@ const Job = union(enum) { /// This `Job` exists (instead of the `link.ZcuTask` being directly queued) to ensure that /// all types are resolved before the linker task is queued. /// If the backend does not support `Zcu.Feature.separate_thread`, the task is run immediately. + /// Before queueing this `Job`, increase the estimated total item count for `comp.link_prog_node`. link_nav: InternPool.Nav.Index, /// Queue a `link.ZcuTask` to emit debug information for this container type. /// This `Job` exists (instead of the `link.ZcuTask` being directly queued) to ensure that /// all types are resolved before the linker task is queued. /// If the backend does not support `Zcu.Feature.separate_thread`, the task is run immediately. + /// Before queueing this `Job`, increase the estimated total item count for `comp.link_prog_node`. link_type: InternPool.Index, + /// Before queueing this `Job`, increase the estimated total item count for `comp.link_prog_node`. update_line_number: InternPool.TrackedInst.Index, /// The `AnalUnit`, which is *not* a `func`, must be semantically analyzed. /// This may be its first time being analyzed, or it may be outdated. @@ -4592,11 +4597,17 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job) JobError!void { const zcu = comp.zcu.?; const gpa = zcu.gpa; var air = func.air; - errdefer air.deinit(gpa); + errdefer { + zcu.codegen_prog_node.completeOne(); + comp.link_prog_node.completeOne(); + air.deinit(gpa); + } if (!air.typesFullyResolved(zcu)) { // Type resolution failed in a way which affects this function. This is a transitive // failure, but it doesn't need recording, because this function semantically depends // on the failed type, so when it is changed the function is updated. + zcu.codegen_prog_node.completeOne(); + comp.link_prog_node.completeOne(); air.deinit(gpa); return; } @@ -4606,7 +4617,6 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job) JobError!void { .value = undefined, }; assert(zcu.pending_codegen_jobs.rmw(.Add, 1, .monotonic) > 0); // the "Code Generation" node hasn't been ended - zcu.codegen_prog_node.increaseEstimatedTotalItems(1); // This value is used as a heuristic to avoid queueing too much AIR/MIR at once (hence // using a lot of memory). If this would cause too many AIR bytes to be in-flight, we // will block on the `dispatchZcuLinkTask` call below. @@ -4640,6 +4650,7 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job) JobError!void { if (nav.analysis != null) { const unit: InternPool.AnalUnit = .wrap(.{ .nav_val = nav_index }); if (zcu.failed_analysis.contains(unit) or zcu.transitive_failed_analysis.contains(unit)) { + comp.link_prog_node.completeOne(); return; } } @@ -4648,6 +4659,7 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job) JobError!void { // Type resolution failed in a way which affects this `Nav`. This is a transitive // failure, but it doesn't need recording, because this `Nav` semantically depends // on the failed type, so when it is changed the `Nav` will be updated. + comp.link_prog_node.completeOne(); return; } comp.dispatchZcuLinkTask(tid, .{ .link_nav = nav_index }); @@ -4659,6 +4671,7 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job) JobError!void { // Type resolution failed in a way which affects this type. This is a transitive // failure, but it doesn't need recording, because this type semantically depends // on the failed type, so when that is changed, this type will be updated. + comp.link_prog_node.completeOne(); return; } comp.dispatchZcuLinkTask(tid, .{ .link_type = ty }); @@ -7460,7 +7473,6 @@ pub fn queuePrelinkTasks(comp: *Compilation, tasks: []const link.PrelinkTask) vo /// The reason for the double-queue here is that the first queue ensures any /// resolve_type_fully tasks are complete before this dispatch function is called. fn dispatchZcuLinkTask(comp: *Compilation, tid: usize, task: link.ZcuTask) void { - comp.link_prog_node.increaseEstimatedTotalItems(1); if (!comp.separateCodegenThreadOk()) { assert(tid == 0); if (task == .link_func) { diff --git a/src/Sema.zig b/src/Sema.zig index 310058a4214d3312f4431ce9c2f0d8c19a572d58..5b3e6419a64d7615cb567f79f4d6681d7c9448dd 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -2992,6 +2992,7 @@ fn zirStructDecl( if (zcu.comp.config.use_llvm) break :codegen_type; if (block.ownerModule().strip) break :codegen_type; // This job depends on any resolve_type_fully jobs queued up before it. + zcu.comp.link_prog_node.increaseEstimatedTotalItems(1); try zcu.comp.queueJob(.{ .link_type = wip_ty.index }); } try sema.declareDependency(.{ .interned = wip_ty.index }); @@ -3266,6 +3267,7 @@ fn zirEnumDecl( if (zcu.comp.config.use_llvm) break :codegen_type; if (block.ownerModule().strip) break :codegen_type; // This job depends on any resolve_type_fully jobs queued up before it. + zcu.comp.link_prog_node.increaseEstimatedTotalItems(1); try zcu.comp.queueJob(.{ .link_type = wip_ty.index }); } return Air.internedToRef(wip_ty.index); @@ -3385,6 +3387,7 @@ fn zirUnionDecl( if (zcu.comp.config.use_llvm) break :codegen_type; if (block.ownerModule().strip) break :codegen_type; // This job depends on any resolve_type_fully jobs queued up before it. + zcu.comp.link_prog_node.increaseEstimatedTotalItems(1); try zcu.comp.queueJob(.{ .link_type = wip_ty.index }); } try sema.declareDependency(.{ .interned = wip_ty.index }); @@ -3473,6 +3476,7 @@ fn zirOpaqueDecl( if (zcu.comp.config.use_llvm) break :codegen_type; if (block.ownerModule().strip) break :codegen_type; // This job depends on any resolve_type_fully jobs queued up before it. + zcu.comp.link_prog_node.increaseEstimatedTotalItems(1); try zcu.comp.queueJob(.{ .link_type = wip_ty.index }); } try sema.addTypeReferenceEntry(src, wip_ty.index); @@ -20105,6 +20109,7 @@ fn structInitAnon( codegen_type: { if (zcu.comp.config.use_llvm) break :codegen_type; if (block.ownerModule().strip) break :codegen_type; + zcu.comp.link_prog_node.increaseEstimatedTotalItems(1); try zcu.comp.queueJob(.{ .link_type = wip.index }); } if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip.index); @@ -21417,6 +21422,7 @@ fn reifyEnum( if (zcu.comp.config.use_llvm) break :codegen_type; if (block.ownerModule().strip) break :codegen_type; // This job depends on any resolve_type_fully jobs queued up before it. + zcu.comp.link_prog_node.increaseEstimatedTotalItems(1); try zcu.comp.queueJob(.{ .link_type = wip_ty.index }); } return Air.internedToRef(wip_ty.index); @@ -21671,6 +21677,7 @@ fn reifyUnion( if (zcu.comp.config.use_llvm) break :codegen_type; if (block.ownerModule().strip) break :codegen_type; // This job depends on any resolve_type_fully jobs queued up before it. + zcu.comp.link_prog_node.increaseEstimatedTotalItems(1); try zcu.comp.queueJob(.{ .link_type = wip_ty.index }); } try sema.declareDependency(.{ .interned = wip_ty.index }); @@ -22026,6 +22033,7 @@ fn reifyStruct( if (zcu.comp.config.use_llvm) break :codegen_type; if (block.ownerModule().strip) break :codegen_type; // This job depends on any resolve_type_fully jobs queued up before it. + zcu.comp.link_prog_node.increaseEstimatedTotalItems(1); try zcu.comp.queueJob(.{ .link_type = wip_ty.index }); } try sema.declareDependency(.{ .interned = wip_ty.index }); diff --git a/src/Sema/LowerZon.zig b/src/Sema/LowerZon.zig index b8064cefbf3fcf41e75c3090726375036d19c3f6..8dfb710ac0a19995521836119fcabcd772085104 100644 --- a/src/Sema/LowerZon.zig +++ b/src/Sema/LowerZon.zig @@ -195,6 +195,7 @@ fn lowerExprAnonResTy(self: *LowerZon, node: Zoir.Node.Index) CompileError!Inter codegen_type: { if (pt.zcu.comp.config.use_llvm) break :codegen_type; if (self.block.ownerModule().strip) break :codegen_type; + pt.zcu.comp.link_prog_node.increaseEstimatedTotalItems(1); try pt.zcu.comp.queueJob(.{ .link_type = wip.index }); } break :ty wip.finish(ip, new_namespace_index); diff --git a/src/Zcu/PerThread.zig b/src/Zcu/PerThread.zig index 4ec6eebc462dba3d016e541302447fa7aaf57d9d..4d90878420071eeea07dfde95fb10b75f85b4cda 100644 --- a/src/Zcu/PerThread.zig +++ b/src/Zcu/PerThread.zig @@ -1321,6 +1321,7 @@ fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileErr } // This job depends on any resolve_type_fully jobs queued up before it. + zcu.comp.link_prog_node.increaseEstimatedTotalItems(1); try zcu.comp.queueJob(.{ .link_nav = nav_id }); } @@ -1717,6 +1718,8 @@ fn analyzeFuncBody( } // This job depends on any resolve_type_fully jobs queued up before it. + zcu.codegen_prog_node.increaseEstimatedTotalItems(1); + comp.link_prog_node.increaseEstimatedTotalItems(1); try comp.queueJob(.{ .codegen_func = .{ .func = func_index, .air = air, @@ -1799,6 +1802,7 @@ fn createFileRootStruct( codegen_type: { if (file.mod.?.strip) break :codegen_type; // This job depends on any resolve_type_fully jobs queued up before it. + zcu.comp.link_prog_node.increaseEstimatedTotalItems(1); try zcu.comp.queueJob(.{ .link_type = wip_ty.index }); } zcu.setFileRootType(file_index, wip_ty.index); @@ -3827,6 +3831,7 @@ pub fn getExtern(pt: Zcu.PerThread, key: InternPool.Key.Extern) Allocator.Error! const result = try pt.zcu.intern_pool.getExtern(pt.zcu.gpa, pt.tid, key); if (result.new_nav.unwrap()) |nav| { // This job depends on any resolve_type_fully jobs queued up before it. + pt.zcu.comp.link_prog_node.increaseEstimatedTotalItems(1); try pt.zcu.comp.queueJob(.{ .link_nav = nav }); if (pt.zcu.comp.debugIncremental()) try pt.zcu.incremental_debug_state.newNav(pt.zcu, nav); } @@ -3974,6 +3979,7 @@ fn recreateStructType( codegen_type: { if (file.mod.?.strip) break :codegen_type; // This job depends on any resolve_type_fully jobs queued up before it. + zcu.comp.link_prog_node.increaseEstimatedTotalItems(1); try zcu.comp.queueJob(.{ .link_type = wip_ty.index }); } @@ -4066,6 +4072,7 @@ fn recreateUnionType( codegen_type: { if (file.mod.?.strip) break :codegen_type; // This job depends on any resolve_type_fully jobs queued up before it. + zcu.comp.link_prog_node.increaseEstimatedTotalItems(1); try zcu.comp.queueJob(.{ .link_type = wip_ty.index }); } -- 2.54.0 From 43d01ff69f6c6c46bef81dd4de2c78fb0a942b65 Mon Sep 17 00:00:00 2001 From: mlugg Date: Sun, 8 Jun 2025 17:58:46 +0100 Subject: [PATCH 35/35] x86_64.Lower: replace slow stringToEnum call Looking at a compilation of 'test/behavior/x86_64/unary.zig' in callgrind showed that a full 30% of the compiler runtime was spent in this `stringToEnum` call, so optimizing it was low-hanging fruit. We tried replacing it with nested `switch` statements using `inline else`, but that generated too much code; it didn't emit huge binaries or anything, but LLVM used a *ridiculous* amount of memory compiling it in some cases. The core problem here is that only a small subset of the cases are actually used (the rest fell through to an "error" path), but that subset is computed at comptime, so we must rely on the optimizer to eliminate the thousands of redundant cases. This would be solved by #21507. Instead, we pre-compute a lookup table at comptime. This table is pretty big (I guess a couple hundred k?), but only the "valid" subset of entries will be accessed in practice (unless a bug in the backend is hit), so it's not too awful on the cache; and it performs much better than the old `std.meta.stringToEnum` call. --- src/arch/x86_64/Lower.zig | 48 ++++++++++++++++++++++++++++++--------- 1 file changed, 37 insertions(+), 11 deletions(-) diff --git a/src/arch/x86_64/Lower.zig b/src/arch/x86_64/Lower.zig index c476fd2eda97adaa4bb17616ba2c87a996fcab7b..ca1fb1e428b672731bc57477bfe553e0b8ea2450 100644 --- a/src/arch/x86_64/Lower.zig +++ b/src/arch/x86_64/Lower.zig @@ -427,8 +427,35 @@ fn encode(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operan lower.result_insts_len += 1; } +const inst_tags_len = @typeInfo(Mir.Inst.Tag).@"enum".fields.len; +const inst_fixes_len = @typeInfo(Mir.Inst.Fixes).@"enum".fields.len; +/// Lookup table, indexed by `@intFromEnum(inst.tag) * inst_fixes_len + @intFromEnum(fixes)`. +/// The value is the resulting `Mnemonic`, or `null` if the combination is not valid. +const mnemonic_table: [inst_tags_len * inst_fixes_len]?Mnemonic = table: { + @setEvalBranchQuota(80_000); + var table: [inst_tags_len * inst_fixes_len]?Mnemonic = undefined; + for (0..inst_fixes_len) |fixes_i| { + const fixes: Mir.Inst.Fixes = @enumFromInt(fixes_i); + const prefix, const suffix = affix: { + const pattern = if (std.mem.indexOfScalar(u8, @tagName(fixes), ' ')) |i| + @tagName(fixes)[i + 1 ..] + else + @tagName(fixes); + const wildcard_idx = std.mem.indexOfScalar(u8, pattern, '_').?; + break :affix .{ pattern[0..wildcard_idx], pattern[wildcard_idx + 1 ..] }; + }; + for (0..inst_tags_len) |inst_tag_i| { + const inst_tag: Mir.Inst.Tag = @enumFromInt(inst_tag_i); + const name = prefix ++ @tagName(inst_tag) ++ suffix; + const idx = inst_tag_i * inst_fixes_len + fixes_i; + table[idx] = if (@hasField(Mnemonic, name)) @field(Mnemonic, name) else null; + } + } + break :table table; +}; + fn generic(lower: *Lower, inst: Mir.Inst) Error!void { - @setEvalBranchQuota(2_800); + @setEvalBranchQuota(2_000); const fixes = switch (inst.ops) { .none => inst.data.none.fixes, .inst => inst.data.inst.fixes, @@ -457,19 +484,18 @@ fn generic(lower: *Lower, inst: Mir.Inst) Error!void { else .none, }, mnemonic: { - comptime var max_len = 0; - inline for (@typeInfo(Mnemonic).@"enum".fields) |field| max_len = @max(field.name.len, max_len); - var buf: [max_len]u8 = undefined; - + if (mnemonic_table[@intFromEnum(inst.tag) * inst_fixes_len + @intFromEnum(fixes)]) |mnemonic| { + break :mnemonic mnemonic; + } + // This combination is invalid; make the theoretical mnemonic name and emit an error with it. const fixes_name = @tagName(fixes); const pattern = fixes_name[if (std.mem.indexOfScalar(u8, fixes_name, ' ')) |i| i + " ".len else 0..]; const wildcard_index = std.mem.indexOfScalar(u8, pattern, '_').?; - const parts = .{ pattern[0..wildcard_index], @tagName(inst.tag), pattern[wildcard_index + "_".len ..] }; - const err_msg = "unsupported mnemonic: "; - const mnemonic = std.fmt.bufPrint(&buf, "{s}{s}{s}", parts) catch - return lower.fail(err_msg ++ "'{s}{s}{s}'", parts); - break :mnemonic std.meta.stringToEnum(Mnemonic, mnemonic) orelse - return lower.fail(err_msg ++ "'{s}'", .{mnemonic}); + return lower.fail("unsupported mnemonic: '{s}{s}{s}'", .{ + pattern[0..wildcard_index], + @tagName(inst.tag), + pattern[wildcard_index + "_".len ..], + }); }, switch (inst.ops) { .none => &.{}, .inst => &.{ -- 2.54.0