aboutsummaryrefslogtreecommitdiff
path: root/src/Zcu/PerThread.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/Zcu/PerThread.zig')
-rw-r--r--src/Zcu/PerThread.zig405
1 files changed, 189 insertions, 216 deletions
diff --git a/src/Zcu/PerThread.zig b/src/Zcu/PerThread.zig
index 8b35d8d799..4d90878420 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;
@@ -795,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 => {
@@ -975,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 .{
@@ -1320,7 +1321,8 @@ 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 });
+ zcu.comp.link_prog_node.increaseEstimatedTotalItems(1);
+ try zcu.comp.queueJob(.{ .link_nav = nav_id });
}
switch (old_nav.status) {
@@ -1395,8 +1397,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 .{
@@ -1616,8 +1618,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 }
@@ -1716,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,
@@ -1724,87 +1728,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();
-
- 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);
- }
-
- 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 (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.
- },
- };
- } else if (zcu.llvm_object) |llvm_object| {
- llvm_object.updateFunc(pt, func_index, air.*, liveness) catch |err| switch (err) {
- error.OutOfMemory => return error.OutOfMemory,
- };
- }
-}
-
pub fn semaMod(pt: Zcu.PerThread, mod: *Module) !void {
dev.check(.sema);
const file_index = pt.zcu.module_roots.get(mod).?.unwrap().?;
@@ -1867,7 +1790,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) {
@@ -1877,10 +1800,10 @@ 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(.{ .codegen_type = wip_ty.index });
+ zcu.comp.link_prog_node.increaseEstimatedTotalItems(1);
+ 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);
@@ -2574,7 +2497,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
@@ -2974,17 +2897,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;
@@ -3004,10 +2920,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),
} },
});
}
@@ -3189,7 +3102,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;
}
@@ -3213,8 +3128,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;
@@ -3309,46 +3226,49 @@ 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));
}
}
-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);
@@ -3450,81 +3370,8 @@ pub fn populateTestFunctions(
} });
ip.mutateVarInit(test_fns_val.toIntern(), new_init);
}
- {
- 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;
- }
-
- 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").linkTestFunctionsNav(pt, nav_index);
}
/// Stores an error in `pt.zcu.failed_files` for this file, and sets the file
@@ -3984,7 +3831,8 @@ 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 });
+ 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);
}
return result.index;
@@ -4122,17 +3970,17 @@ 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.
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(.{ .codegen_type = wip_ty.index });
+ zcu.comp.link_prog_node.increaseEstimatedTotalItems(1);
+ try zcu.comp.queueJob(.{ .link_type = wip_ty.index });
}
if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index);
@@ -4215,17 +4063,17 @@ 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.
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(.{ .codegen_type = wip_ty.index });
+ zcu.comp.link_prog_node.increaseEstimatedTotalItems(1);
+ try zcu.comp.queueJob(.{ .link_type = wip_ty.index });
}
if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index);
@@ -4325,7 +4173,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.
@@ -4518,3 +4366,128 @@ 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 {
+ 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 => {
+ zcu.comp.setAllocFailure();
+ out.status.store(.failed, .monotonic);
+ },
+ error.CodegenFail => {
+ zcu.assertCodegenFailed(zcu.funcInfo(func_index).owner_nav);
+ out.status.store(.failed, .monotonic);
+ },
+ error.NoLinkFile => {
+ 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_spirv64,
+ .stage2_llvm,
+ => {},
+ }
+ out.status.store(.failed, .monotonic);
+ },
+ }
+ 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,
+ CodegenFail,
+ NoLinkFile,
+ BackendDoesNotProduceMir,
+}!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);
+
+ if (build_options.enable_debug_extensions and comp.verbose_air) {
+ 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) {
+ 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| {
+ 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;
+
+ // 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,
+ => |e| return e,
+ error.Overflow,
+ error.RelocationNotByteAligned,
+ => return zcu.codegenFail(nav, "unable to codegen: {s}", .{@errorName(err)}),
+ };
+}