Commit 5ab307cf47

mlugg <mlugg@mlugg.co.uk>
2025-06-01 23:57:59
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.
1 parent 9eb400e
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 {
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 } {
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 {
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 } {
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;
 }
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) {
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");
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(
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 } {
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| {
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");
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;
 
     {
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;
 
     {
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;
 
     {
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;
 
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);
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
 }
 
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 {
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 = .{
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.
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
 }
 
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;
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;
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,
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);
             },