compiler: replace thread pool with std.Io

Eliminate the `std.Thread.Pool` used in the compiler for concurrency and
asynchrony, in favour of the new `std.Io.async` and `std.Io.concurrent`
primitives.

This removes the last usage of `std.Thread.Pool` in the Zig repository.
This commit is contained in:
Matthew Lugg
2025-12-06 15:27:57 +00:00
parent 9ae4e38ca2
commit 18bc7e802f
33 changed files with 2255 additions and 1722 deletions
+438 -483
View File
@@ -10,8 +10,6 @@ const Allocator = std.mem.Allocator;
const assert = std.debug.assert;
const log = std.log.scoped(.compilation);
const Target = std.Target;
const ThreadPool = std.Thread.Pool;
const WaitGroup = std.Thread.WaitGroup;
const ErrorBundle = std.zig.ErrorBundle;
const fatal = std.process.fatal;
@@ -56,6 +54,7 @@ gpa: Allocator,
/// threads at once.
arena: Allocator,
io: Io,
thread_limit: usize,
/// Not every Compilation compiles .zig code! For example you could do `zig build-exe foo.o`.
zcu: ?*Zcu,
/// Contains different state depending on the `CacheMode` used by this `Compilation`.
@@ -110,7 +109,14 @@ win32_resource_table: if (dev.env.supports(.win32_resource)) std.AutoArrayHashMa
} = .{},
link_diags: link.Diags,
link_task_queue: link.Queue = .empty,
link_queue: link.Queue = .empty,
/// This is populated during `Compilation.create` with a set of prelink tasks which need to be
/// queued on the first update. In `update`, we will send these tasks to the linker, and clear
/// them from this list.
///
/// Allocated into `gpa`.
oneshot_prelink_tasks: std.ArrayList(link.PrelinkTask),
/// Set of work that can be represented by only flags to determine whether the
/// work is queued or not.
@@ -198,7 +204,6 @@ libc_include_dir_list: []const []const u8,
libc_framework_dir_list: []const []const u8,
rc_includes: std.zig.RcIncludes,
mingw_unicode_entry_point: bool,
thread_pool: *ThreadPool,
/// Populated when we build the libc++ static library. A Job to build this is placed in the queue
/// and resolved before calling linker.flush().
@@ -248,16 +253,10 @@ crt_files: std.StringHashMapUnmanaged(CrtFile) = .empty,
reference_trace: ?u32 = null,
/// This mutex guards all `Compilation` mutable state.
/// Disabled in single-threaded mode because the thread pool spawns in the same thread.
mutex: if (builtin.single_threaded) struct {
pub inline fn tryLock(_: @This()) void {}
pub inline fn lock(_: @This()) void {}
pub inline fn unlock(_: @This()) void {}
} else std.Thread.Mutex = .{},
mutex: std.Io.Mutex = .init,
test_filters: []const []const u8,
link_task_wait_group: WaitGroup = .{},
link_prog_node: std.Progress.Node = .none,
llvm_opt_bisect_limit: c_int,
@@ -1568,7 +1567,7 @@ pub const CacheMode = enum {
pub const ParentWholeCache = struct {
manifest: *Cache.Manifest,
mutex: *std.Thread.Mutex,
mutex: *std.Io.Mutex,
prefix_map: [4]u8,
};
@@ -1596,7 +1595,7 @@ const CacheUse = union(CacheMode) {
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,
cache_manifest_mutex: std.Io.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`.
@@ -1636,7 +1635,7 @@ const CacheUse = union(CacheMode) {
pub const CreateOptions = struct {
dirs: Directories,
thread_pool: *ThreadPool,
thread_limit: usize,
self_exe_path: ?[]const u8 = null,
/// Options that have been resolved by calling `resolveDefaults`.
@@ -2211,8 +2210,9 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic,
.llvm_object = null,
.analysis_roots_buffer = undefined,
.analysis_roots_len = 0,
.codegen_task_pool = try .init(arena),
};
try zcu.init(options.thread_pool.getIdCount());
try zcu.init(gpa, io, options.thread_limit);
break :blk zcu;
} else blk: {
if (options.emit_h != .no) return diag.fail(.emit_h_without_zcu);
@@ -2224,6 +2224,7 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic,
.gpa = gpa,
.arena = arena,
.io = io,
.thread_limit = options.thread_limit,
.zcu = opt_zcu,
.cache_use = undefined, // populated below
.bin_file = null, // populated below if necessary
@@ -2241,7 +2242,6 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic,
.libc_framework_dir_list = libc_dirs.libc_framework_dir_list,
.rc_includes = options.rc_includes,
.mingw_unicode_entry_point = options.mingw_unicode_entry_point,
.thread_pool = options.thread_pool,
.clang_passthrough_mode = options.clang_passthrough_mode,
.clang_preprocessor_mode = options.clang_preprocessor_mode,
.verbose_cc = options.verbose_cc,
@@ -2282,7 +2282,8 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic,
.global_cc_argv = options.global_cc_argv,
.file_system_inputs = options.file_system_inputs,
.parent_whole_cache = options.parent_whole_cache,
.link_diags = .init(gpa),
.link_diags = .init(gpa, io),
.oneshot_prelink_tasks = .empty,
.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),
@@ -2468,7 +2469,7 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic,
whole.* = .{
.lf_open_opts = lf_open_opts,
.cache_manifest = null,
.cache_manifest_mutex = .{},
.cache_manifest_mutex = .init,
.tmp_artifact_directory = null,
.lock = null,
};
@@ -2553,14 +2554,14 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic,
};
const fields = @typeInfo(@TypeOf(paths)).@"struct".fields;
try comp.link_task_queue.queued_prelink.ensureUnusedCapacity(gpa, fields.len + 1);
try comp.oneshot_prelink_tasks.ensureUnusedCapacity(gpa, fields.len + 1);
inline for (fields) |field| {
if (@field(paths, field.name)) |path| {
comp.link_task_queue.queued_prelink.appendAssumeCapacity(.{ .load_object = path });
comp.oneshot_prelink_tasks.appendAssumeCapacity(.{ .load_object = path });
}
}
// Loads the libraries provided by `target_util.libcFullLinkFlags(target)`.
comp.link_task_queue.queued_prelink.appendAssumeCapacity(.load_host_libc);
comp.oneshot_prelink_tasks.appendAssumeCapacity(.load_host_libc);
} else if (target.isMuslLibC()) {
if (!std.zig.target.canBuildLibC(target)) return diag.fail(.cross_libc_unavailable);
@@ -2629,10 +2630,9 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic,
for (0..count) |i| {
try comp.queueJob(.{ .windows_import_lib = i });
}
// when integrating coff linker with prelink, the above
// queueJob will need to change into something else since those
// jobs are dispatched *after* the link_task_wait_group.wait()
// that happens when separateCodegenThreadOk() is false.
// when integrating coff linker with prelink, the above `queueJob` will need to move
// to something in `dispatchPrelinkWork`, which must queue all prelink link tasks
// *before* we begin working on the main job queue.
}
if (comp.wantBuildLibUnwindFromSource()) {
comp.queued_jobs.libunwind = true;
@@ -2681,19 +2681,15 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic,
}
}
try comp.link_task_queue.queued_prelink.append(gpa, .load_explicitly_provided);
try comp.oneshot_prelink_tasks.append(gpa, .load_explicitly_provided);
}
log.debug("queued prelink tasks: {d}", .{comp.link_task_queue.queued_prelink.items.len});
log.debug("queued oneshot prelink tasks: {d}", .{comp.oneshot_prelink_tasks.items.len});
return comp;
}
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();
@@ -2760,6 +2756,7 @@ pub fn destroy(comp: *Compilation) void {
if (comp.time_report) |*tr| tr.deinit(gpa);
comp.link_diags.deinit();
comp.oneshot_prelink_tasks.deinit(gpa);
comp.clearMiscFailures();
@@ -2865,8 +2862,10 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) UpdateE
const tracy_trace = trace(@src());
defer tracy_trace.end();
// This arena is scoped to this one update.
const gpa = comp.gpa;
const io = comp.io;
// This arena is scoped to this one update.
var arena_allocator = std.heap.ArenaAllocator.init(gpa);
defer arena_allocator.deinit();
const arena = arena_allocator.allocator();
@@ -2946,8 +2945,8 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) UpdateE
// In this case the cache hit contains the full set of file system inputs. Nice!
if (comp.file_system_inputs) |buf| try man.populateFileSystemInputs(buf);
if (comp.parent_whole_cache) |pwc| {
pwc.mutex.lock();
defer pwc.mutex.unlock();
try pwc.mutex.lock(io);
defer pwc.mutex.unlock(io);
try man.populateOtherManifest(pwc.manifest, pwc.prefix_map);
}
@@ -3066,7 +3065,7 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) UpdateE
comp.link_prog_node = .none;
};
try comp.performAllTheWork(main_progress_node);
try comp.performAllTheWork(main_progress_node, arena);
if (comp.zcu) |zcu| {
const pt: Zcu.PerThread = .activate(zcu, .main);
@@ -3132,8 +3131,8 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) UpdateE
.whole => |whole| {
if (comp.file_system_inputs) |buf| try man.populateFileSystemInputs(buf);
if (comp.parent_whole_cache) |pwc| {
pwc.mutex.lock();
defer pwc.mutex.unlock();
try pwc.mutex.lock(io);
defer pwc.mutex.unlock(io);
try man.populateOtherManifest(pwc.manifest, pwc.prefix_map);
}
@@ -3234,6 +3233,7 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) UpdateE
/// Thread-safe. Assumes that `comp.mutex` is *not* already held by the caller.
pub fn appendFileSystemInput(comp: *Compilation, path: Compilation.Path) Allocator.Error!void {
const gpa = comp.gpa;
const io = comp.io;
const fsi = comp.file_system_inputs orelse return;
const prefixes = comp.cache_parent.prefixes();
@@ -3253,8 +3253,8 @@ pub fn appendFileSystemInput(comp: *Compilation, path: Compilation.Path) Allocat
);
// There may be concurrent calls to this function from C object workers and/or the main thread.
comp.mutex.lock();
defer comp.mutex.unlock();
comp.mutex.lockUncancelable(io);
defer comp.mutex.unlock(io);
try fsi.ensureUnusedCapacity(gpa, path.sub_path.len + 3);
if (fsi.items.len > 0) fsi.appendAssumeCapacity(0);
@@ -3305,6 +3305,7 @@ fn flush(
arena: Allocator,
tid: Zcu.PerThread.Id,
) Allocator.Error!void {
const io = comp.io;
if (comp.zcu) |zcu| {
if (zcu.llvm_object) |llvm_object| {
const pt: Zcu.PerThread = .activate(zcu, tid);
@@ -3317,8 +3318,8 @@ fn flush(
var timer = comp.startTimer();
defer if (timer.finish()) |ns| {
comp.mutex.lock();
defer comp.mutex.unlock();
comp.mutex.lockUncancelable(io);
defer comp.mutex.unlock(io);
comp.time_report.?.stats.real_ns_llvm_emit = ns;
};
@@ -3362,8 +3363,8 @@ fn flush(
if (comp.bin_file) |lf| {
var timer = comp.startTimer();
defer if (timer.finish()) |ns| {
comp.mutex.lock();
defer comp.mutex.unlock();
comp.mutex.lockUncancelable(io);
defer comp.mutex.unlock(io);
comp.time_report.?.stats.real_ns_link_flush = ns;
};
// This is needed before reading the error flags.
@@ -4575,243 +4576,47 @@ pub fn unableToLoadZcuFile(
fn performAllTheWork(
comp: *Compilation,
main_progress_node: std.Progress.Node,
update_arena: Allocator,
) JobError!void {
// Regardless of errors, `comp.zcu` needs to update its generation number.
defer if (comp.zcu) |zcu| {
zcu.codegen_task_pool.cancel(zcu);
// Regardless of errors, `comp.zcu` needs to update its generation number.
zcu.generation += 1;
};
const io = comp.io;
// This is awkward: we don't want to start the timer until later, but we won't want to stop it
// until the wait groups finish. That means we need do do this.
var decl_work_timer: ?Timer = null;
defer commit_timer: {
const t = &(decl_work_timer orelse break :commit_timer);
const ns = t.finish() orelse break :commit_timer;
comp.mutex.lock();
defer comp.mutex.unlock();
comp.mutex.lockUncancelable(io);
defer comp.mutex.unlock(io);
comp.time_report.?.stats.real_ns_decls = ns;
}
// 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
// only needs to be finished by the end of this function.
var misc_group: Io.Group = .init;
defer misc_group.cancel(io);
var work_queue_wait_group: WaitGroup = .{};
defer work_queue_wait_group.wait();
try comp.link_queue.start(comp, update_arena);
defer comp.link_queue.cancel(io);
comp.link_task_wait_group.reset();
defer comp.link_task_wait_group.wait();
// Already-queued prelink tasks
comp.link_prog_node.increaseEstimatedTotalItems(comp.link_task_queue.queued_prelink.items.len);
comp.link_task_queue.start(comp);
misc_group.concurrent(io, dispatchPrelinkWork, .{ comp, main_progress_node }) catch |err| switch (err) {
error.ConcurrencyUnavailable => {
// Do it immediately so that the link queue isn't blocked
dispatchPrelinkWork(comp, main_progress_node);
},
};
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 });
misc_group.async(io, workerDocsCopy, .{comp});
misc_group.async(io, workerDocsWasm, .{ comp, main_progress_node });
}
// In case it failed last time, try again. `clearMiscFailures` was already
// called at the start of `update`.
if (comp.queued_jobs.compiler_rt_lib and comp.compiler_rt_lib == null) {
// LLVM disables LTO for its compiler-rt and we've had various issues with LTO of our
// compiler-rt due to LLD bugs as well, e.g.:
//
// https://github.com/llvm/llvm-project/issues/43698#issuecomment-2542660611
comp.link_task_queue.startPrelinkItem();
comp.link_task_wait_group.spawnManager(buildRt, .{
comp,
"compiler_rt.zig",
"compiler_rt",
.Lib,
.static,
.compiler_rt,
main_progress_node,
RtOptions{
.checks_valgrind = true,
.allow_lto = false,
},
&comp.compiler_rt_lib,
});
}
if (comp.queued_jobs.compiler_rt_obj and comp.compiler_rt_obj == null) {
comp.link_task_queue.startPrelinkItem();
comp.link_task_wait_group.spawnManager(buildRt, .{
comp,
"compiler_rt.zig",
"compiler_rt",
.Obj,
.static,
.compiler_rt,
main_progress_node,
RtOptions{
.checks_valgrind = true,
.allow_lto = false,
},
&comp.compiler_rt_obj,
});
}
// hack for stage2_x86_64 + coff
if (comp.queued_jobs.compiler_rt_dyn_lib and comp.compiler_rt_dyn_lib == null) {
comp.link_task_queue.startPrelinkItem();
comp.link_task_wait_group.spawnManager(buildRt, .{
comp,
"compiler_rt.zig",
"compiler_rt",
.Lib,
.dynamic,
.compiler_rt,
main_progress_node,
RtOptions{
.checks_valgrind = true,
.allow_lto = false,
},
&comp.compiler_rt_dyn_lib,
});
}
if (comp.queued_jobs.fuzzer_lib and comp.fuzzer_lib == null) {
comp.link_task_queue.startPrelinkItem();
comp.link_task_wait_group.spawnManager(buildRt, .{
comp,
"fuzzer.zig",
"fuzzer",
.Lib,
.static,
.libfuzzer,
main_progress_node,
RtOptions{},
&comp.fuzzer_lib,
});
}
if (comp.queued_jobs.ubsan_rt_lib and comp.ubsan_rt_lib == null) {
comp.link_task_queue.startPrelinkItem();
comp.link_task_wait_group.spawnManager(buildRt, .{
comp,
"ubsan_rt.zig",
"ubsan_rt",
.Lib,
.static,
.libubsan,
main_progress_node,
RtOptions{
.allow_lto = false,
},
&comp.ubsan_rt_lib,
});
}
if (comp.queued_jobs.ubsan_rt_obj and comp.ubsan_rt_obj == null) {
comp.link_task_queue.startPrelinkItem();
comp.link_task_wait_group.spawnManager(buildRt, .{
comp,
"ubsan_rt.zig",
"ubsan_rt",
.Obj,
.static,
.libubsan,
main_progress_node,
RtOptions{
.allow_lto = false,
},
&comp.ubsan_rt_obj,
});
}
if (comp.queued_jobs.glibc_shared_objects) {
comp.link_task_queue.startPrelinkItem();
comp.link_task_wait_group.spawnManager(buildGlibcSharedObjects, .{ comp, main_progress_node });
}
if (comp.queued_jobs.freebsd_shared_objects) {
comp.link_task_queue.startPrelinkItem();
comp.link_task_wait_group.spawnManager(buildFreeBSDSharedObjects, .{ comp, main_progress_node });
}
if (comp.queued_jobs.netbsd_shared_objects) {
comp.link_task_queue.startPrelinkItem();
comp.link_task_wait_group.spawnManager(buildNetBSDSharedObjects, .{ comp, main_progress_node });
}
if (comp.queued_jobs.libunwind) {
comp.link_task_queue.startPrelinkItem();
comp.link_task_wait_group.spawnManager(buildLibUnwind, .{ comp, main_progress_node });
}
if (comp.queued_jobs.libcxx) {
comp.link_task_queue.startPrelinkItem();
comp.link_task_wait_group.spawnManager(buildLibCxx, .{ comp, main_progress_node });
}
if (comp.queued_jobs.libcxxabi) {
comp.link_task_queue.startPrelinkItem();
comp.link_task_wait_group.spawnManager(buildLibCxxAbi, .{ comp, main_progress_node });
}
if (comp.queued_jobs.libtsan) {
comp.link_task_queue.startPrelinkItem();
comp.link_task_wait_group.spawnManager(buildLibTsan, .{ comp, main_progress_node });
}
if (comp.queued_jobs.zigc_lib and comp.zigc_static_lib == null) {
comp.link_task_queue.startPrelinkItem();
comp.link_task_wait_group.spawnManager(buildLibZigC, .{ comp, main_progress_node });
}
for (0..@typeInfo(musl.CrtFile).@"enum".fields.len) |i| {
if (comp.queued_jobs.musl_crt_file[i]) {
const tag: musl.CrtFile = @enumFromInt(i);
comp.link_task_queue.startPrelinkItem();
comp.link_task_wait_group.spawnManager(buildMuslCrtFile, .{ comp, tag, main_progress_node });
}
}
for (0..@typeInfo(glibc.CrtFile).@"enum".fields.len) |i| {
if (comp.queued_jobs.glibc_crt_file[i]) {
const tag: glibc.CrtFile = @enumFromInt(i);
comp.link_task_queue.startPrelinkItem();
comp.link_task_wait_group.spawnManager(buildGlibcCrtFile, .{ comp, tag, main_progress_node });
}
}
for (0..@typeInfo(freebsd.CrtFile).@"enum".fields.len) |i| {
if (comp.queued_jobs.freebsd_crt_file[i]) {
const tag: freebsd.CrtFile = @enumFromInt(i);
comp.link_task_queue.startPrelinkItem();
comp.link_task_wait_group.spawnManager(buildFreeBSDCrtFile, .{ comp, tag, main_progress_node });
}
}
for (0..@typeInfo(netbsd.CrtFile).@"enum".fields.len) |i| {
if (comp.queued_jobs.netbsd_crt_file[i]) {
const tag: netbsd.CrtFile = @enumFromInt(i);
comp.link_task_queue.startPrelinkItem();
comp.link_task_wait_group.spawnManager(buildNetBSDCrtFile, .{ comp, tag, main_progress_node });
}
}
for (0..@typeInfo(wasi_libc.CrtFile).@"enum".fields.len) |i| {
if (comp.queued_jobs.wasi_libc_crt_file[i]) {
const tag: wasi_libc.CrtFile = @enumFromInt(i);
comp.link_task_queue.startPrelinkItem();
comp.link_task_wait_group.spawnManager(buildWasiLibcCrtFile, .{ comp, tag, main_progress_node });
}
}
for (0..@typeInfo(mingw.CrtFile).@"enum".fields.len) |i| {
if (comp.queued_jobs.mingw_crt_file[i]) {
const tag: mingw.CrtFile = @enumFromInt(i);
comp.link_task_queue.startPrelinkItem();
comp.link_task_wait_group.spawnManager(buildMingwCrtFile, .{ comp, tag, main_progress_node });
}
}
{
if (comp.zcu) |zcu| {
const astgen_frame = tracy.namedFrame("astgen");
defer astgen_frame.end();
@@ -4820,76 +4625,62 @@ fn performAllTheWork(
var timer = comp.startTimer();
defer if (timer.finish()) |ns| {
comp.mutex.lock();
defer comp.mutex.unlock();
comp.mutex.lockUncancelable(io);
defer comp.mutex.unlock(io);
comp.time_report.?.stats.real_ns_files = ns;
};
var astgen_wait_group: WaitGroup = .{};
defer astgen_wait_group.wait();
const gpa = comp.gpa;
if (comp.zcu) |zcu| {
const gpa = zcu.gpa;
var astgen_group: Io.Group = .init;
defer astgen_group.cancel(io);
// We cannot reference `zcu.import_table` after we spawn any `workerUpdateFile` jobs,
// because on single-threaded targets the worker will be run eagerly, meaning the
// `import_table` could be mutated, and not even holding `comp.mutex` will save us. So,
// build up a list of the files to update *before* we spawn any jobs.
var astgen_work_items: std.MultiArrayList(struct {
file_index: Zcu.File.Index,
file: *Zcu.File,
}) = .empty;
defer astgen_work_items.deinit(gpa);
// Not every item in `import_table` will need updating, because some are builtin.zig
// files. However, most will, so let's just reserve sufficient capacity upfront.
try astgen_work_items.ensureTotalCapacity(gpa, zcu.import_table.count());
for (zcu.import_table.keys()) |file_index| {
const file = zcu.fileByIndex(file_index);
if (file.is_builtin) {
// This is a `builtin.zig`, so updating is redundant. However, we want to make
// sure the file contents are still correct on disk, since it can improve the
// debugging experience better. That job only needs `file`, so we can kick it
// off right now.
comp.thread_pool.spawnWg(&astgen_wait_group, workerUpdateBuiltinFile, .{ comp, file });
continue;
}
astgen_work_items.appendAssumeCapacity(.{
.file_index = file_index,
.file = file,
});
// We cannot reference `zcu.import_table` after we spawn any `workerUpdateFile` jobs,
// because on single-threaded targets the worker will be run eagerly, meaning the
// `import_table` could be mutated, and not even holding `comp.mutex` will save us. So,
// build up a list of the files to update *before* we spawn any jobs.
var astgen_work_items: std.MultiArrayList(struct {
file_index: Zcu.File.Index,
file: *Zcu.File,
}) = .empty;
defer astgen_work_items.deinit(gpa);
// Not every item in `import_table` will need updating, because some are builtin.zig
// files. However, most will, so let's just reserve sufficient capacity upfront.
try astgen_work_items.ensureTotalCapacity(gpa, zcu.import_table.count());
for (zcu.import_table.keys()) |file_index| {
const file = zcu.fileByIndex(file_index);
if (file.is_builtin) {
// This is a `builtin.zig`, so updating is redundant. However, we want to make
// sure the file contents are still correct on disk, since it can improve the
// debugging experience better. That job only needs `file`, so we can kick it
// off right now.
astgen_group.async(io, workerUpdateBuiltinFile, .{ comp, file });
continue;
}
// Now that we're not going to touch `zcu.import_table` again, we can spawn `workerUpdateFile` jobs.
for (astgen_work_items.items(.file_index), astgen_work_items.items(.file)) |file_index, file| {
comp.thread_pool.spawnWgId(&astgen_wait_group, workerUpdateFile, .{
comp, file, file_index, zir_prog_node, &astgen_wait_group,
});
}
// On the other hand, it's fine to directly iterate `zcu.embed_table.keys()` here
// because `workerUpdateEmbedFile` can't invalidate it. The different here is that one
// `@embedFile` can't trigger analysis of a new `@embedFile`!
for (0.., zcu.embed_table.keys()) |ef_index_usize, ef| {
const ef_index: Zcu.EmbedFile.Index = @enumFromInt(ef_index_usize);
comp.thread_pool.spawnWgId(&astgen_wait_group, workerUpdateEmbedFile, .{
comp, ef_index, ef,
});
}
}
while (comp.c_object_work_queue.popFront()) |c_object| {
comp.link_task_queue.startPrelinkItem();
comp.thread_pool.spawnWg(&comp.link_task_wait_group, workerUpdateCObject, .{
comp, c_object, main_progress_node,
astgen_work_items.appendAssumeCapacity(.{
.file_index = file_index,
.file = file,
});
}
while (comp.win32_resource_work_queue.popFront()) |win32_resource| {
comp.link_task_queue.startPrelinkItem();
comp.thread_pool.spawnWg(&comp.link_task_wait_group, workerUpdateWin32Resource, .{
comp, win32_resource, main_progress_node,
// Now that we're not going to touch `zcu.import_table` again, we can spawn `workerUpdateFile` jobs.
for (astgen_work_items.items(.file_index), astgen_work_items.items(.file)) |file_index, file| {
astgen_group.async(io, workerUpdateFile, .{
comp, file, file_index, zir_prog_node, &astgen_group,
});
}
// On the other hand, it's fine to directly iterate `zcu.embed_table.keys()` here
// because `workerUpdateEmbedFile` can't invalidate it. The different here is that one
// `@embedFile` can't trigger analysis of a new `@embedFile`!
for (0.., zcu.embed_table.keys()) |ef_index_usize, ef| {
const ef_index: Zcu.EmbedFile.Index = @enumFromInt(ef_index_usize);
astgen_group.async(io, workerUpdateEmbedFile, .{
comp, ef_index, ef,
});
}
astgen_group.wait(io);
}
if (comp.zcu) |zcu| {
@@ -4920,8 +4711,8 @@ fn performAllTheWork(
defer gpa.free(path);
const result = res: {
whole.cache_manifest_mutex.lock();
defer whole.cache_manifest_mutex.unlock();
try whole.cache_manifest_mutex.lock(io);
defer whole.cache_manifest_mutex.unlock(io);
if (file.source) |source| {
break :res man.addFilePostContents(path, source, file.stat);
} else {
@@ -4949,6 +4740,11 @@ fn performAllTheWork(
// us from invalidating lots of incremental dependencies due to files with e.g. parse errors.
// However, this means our analysis data is invalid, so we want to omit all analysis errors.
zcu.skip_analysis_this_update = true;
// Since we're skipping analysis, there are no ZCU link tasks.
comp.link_queue.finishZcuQueue(comp);
// Let other compilation work finish to collect as many errors as possible.
misc_group.wait(io);
comp.link_queue.wait(io);
return;
}
@@ -4981,21 +4777,21 @@ fn performAllTheWork(
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) {
if (zcu.pending_codegen_jobs.fetchSub(1, .monotonic) == 1) {
// Decremented to 0, so all done.
zcu.codegen_prog_node.end();
zcu.codegen_prog_node = .none;
}
};
// We aren't going to queue any more prelink tasks.
comp.link_task_queue.finishPrelinkItem(comp);
if (!comp.separateCodegenThreadOk()) {
// Waits until all input files have been parsed.
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.zcu) |zcu| {
if (!zcu.backendSupportsFeature(.separate_thread)) {
// Close the ZCU task queue. Prelink may still be running, but the closed
// queue will cause the linker task to exit once prelink finishes. The
// closed queue also communicates to `enqueueZcu` that it should wait for
// the linker task to finish and then run ZCU tasks serially.
comp.link_queue.finishZcuQueue(comp);
}
}
if (comp.zcu != null) {
@@ -5005,7 +4801,11 @@ fn performAllTheWork(
work: while (true) {
for (&comp.work_queues) |*work_queue| if (work_queue.popFront()) |job| {
try processOneJob(@intFromEnum(Zcu.PerThread.Id.main), comp, job);
try processOneJob(
@intFromEnum(Zcu.PerThread.Id.main),
comp,
job,
);
continue :work;
};
if (comp.zcu) |zcu| {
@@ -5028,6 +4828,217 @@ fn performAllTheWork(
}
break;
}
comp.link_queue.finishZcuQueue(comp);
// Main thread work is all done, now just wait for all async work.
misc_group.wait(io);
comp.link_queue.wait(io);
}
fn dispatchPrelinkWork(comp: *Compilation, main_progress_node: std.Progress.Node) void {
const io = comp.io;
var prelink_group: Io.Group = .init;
defer prelink_group.cancel(io);
comp.queuePrelinkTasks(comp.oneshot_prelink_tasks.items) catch |err| switch (err) {
error.Canceled => return,
};
comp.oneshot_prelink_tasks.clearRetainingCapacity();
// In case it failed last time, try again. `clearMiscFailures` was already
// called at the start of `update`.
if (comp.queued_jobs.compiler_rt_lib and comp.compiler_rt_lib == null) {
// LLVM disables LTO for its compiler-rt and we've had various issues with LTO of our
// compiler-rt due to LLD bugs as well, e.g.:
//
// https://github.com/llvm/llvm-project/issues/43698#issuecomment-2542660611
prelink_group.async(io, buildRt, .{
comp,
"compiler_rt.zig",
"compiler_rt",
.Lib,
.static,
.compiler_rt,
main_progress_node,
RtOptions{
.checks_valgrind = true,
.allow_lto = false,
},
&comp.compiler_rt_lib,
});
}
if (comp.queued_jobs.compiler_rt_obj and comp.compiler_rt_obj == null) {
prelink_group.async(io, buildRt, .{
comp,
"compiler_rt.zig",
"compiler_rt",
.Obj,
.static,
.compiler_rt,
main_progress_node,
RtOptions{
.checks_valgrind = true,
.allow_lto = false,
},
&comp.compiler_rt_obj,
});
}
// hack for stage2_x86_64 + coff
if (comp.queued_jobs.compiler_rt_dyn_lib and comp.compiler_rt_dyn_lib == null) {
prelink_group.async(io, buildRt, .{
comp,
"compiler_rt.zig",
"compiler_rt",
.Lib,
.dynamic,
.compiler_rt,
main_progress_node,
RtOptions{
.checks_valgrind = true,
.allow_lto = false,
},
&comp.compiler_rt_dyn_lib,
});
}
if (comp.queued_jobs.fuzzer_lib and comp.fuzzer_lib == null) {
prelink_group.async(io, buildRt, .{
comp,
"fuzzer.zig",
"fuzzer",
.Lib,
.static,
.libfuzzer,
main_progress_node,
RtOptions{},
&comp.fuzzer_lib,
});
}
if (comp.queued_jobs.ubsan_rt_lib and comp.ubsan_rt_lib == null) {
prelink_group.async(io, buildRt, .{
comp,
"ubsan_rt.zig",
"ubsan_rt",
.Lib,
.static,
.libubsan,
main_progress_node,
RtOptions{
.allow_lto = false,
},
&comp.ubsan_rt_lib,
});
}
if (comp.queued_jobs.ubsan_rt_obj and comp.ubsan_rt_obj == null) {
prelink_group.async(io, buildRt, .{
comp,
"ubsan_rt.zig",
"ubsan_rt",
.Obj,
.static,
.libubsan,
main_progress_node,
RtOptions{
.allow_lto = false,
},
&comp.ubsan_rt_obj,
});
}
if (comp.queued_jobs.glibc_shared_objects) {
prelink_group.async(io, buildGlibcSharedObjects, .{ comp, main_progress_node });
}
if (comp.queued_jobs.freebsd_shared_objects) {
prelink_group.async(io, buildFreeBSDSharedObjects, .{ comp, main_progress_node });
}
if (comp.queued_jobs.netbsd_shared_objects) {
prelink_group.async(io, buildNetBSDSharedObjects, .{ comp, main_progress_node });
}
if (comp.queued_jobs.libunwind) {
prelink_group.async(io, buildLibUnwind, .{ comp, main_progress_node });
}
if (comp.queued_jobs.libcxx) {
prelink_group.async(io, buildLibCxx, .{ comp, main_progress_node });
}
if (comp.queued_jobs.libcxxabi) {
prelink_group.async(io, buildLibCxxAbi, .{ comp, main_progress_node });
}
if (comp.queued_jobs.libtsan) {
prelink_group.async(io, buildLibTsan, .{ comp, main_progress_node });
}
if (comp.queued_jobs.zigc_lib and comp.zigc_static_lib == null) {
prelink_group.async(io, buildLibZigC, .{ comp, main_progress_node });
}
for (0..@typeInfo(musl.CrtFile).@"enum".fields.len) |i| {
if (comp.queued_jobs.musl_crt_file[i]) {
const tag: musl.CrtFile = @enumFromInt(i);
prelink_group.async(io, buildMuslCrtFile, .{ comp, tag, main_progress_node });
}
}
for (0..@typeInfo(glibc.CrtFile).@"enum".fields.len) |i| {
if (comp.queued_jobs.glibc_crt_file[i]) {
const tag: glibc.CrtFile = @enumFromInt(i);
prelink_group.async(io, buildGlibcCrtFile, .{ comp, tag, main_progress_node });
}
}
for (0..@typeInfo(freebsd.CrtFile).@"enum".fields.len) |i| {
if (comp.queued_jobs.freebsd_crt_file[i]) {
const tag: freebsd.CrtFile = @enumFromInt(i);
prelink_group.async(io, buildFreeBSDCrtFile, .{ comp, tag, main_progress_node });
}
}
for (0..@typeInfo(netbsd.CrtFile).@"enum".fields.len) |i| {
if (comp.queued_jobs.netbsd_crt_file[i]) {
const tag: netbsd.CrtFile = @enumFromInt(i);
prelink_group.async(io, buildNetBSDCrtFile, .{ comp, tag, main_progress_node });
}
}
for (0..@typeInfo(wasi_libc.CrtFile).@"enum".fields.len) |i| {
if (comp.queued_jobs.wasi_libc_crt_file[i]) {
const tag: wasi_libc.CrtFile = @enumFromInt(i);
prelink_group.async(io, buildWasiLibcCrtFile, .{ comp, tag, main_progress_node });
}
}
for (0..@typeInfo(mingw.CrtFile).@"enum".fields.len) |i| {
if (comp.queued_jobs.mingw_crt_file[i]) {
const tag: mingw.CrtFile = @enumFromInt(i);
prelink_group.async(io, buildMingwCrtFile, .{ comp, tag, main_progress_node });
}
}
while (comp.c_object_work_queue.popFront()) |c_object| {
prelink_group.async(io, workerUpdateCObject, .{
comp, c_object, main_progress_node,
});
}
while (comp.win32_resource_work_queue.popFront()) |win32_resource| {
prelink_group.async(io, workerUpdateWin32Resource, .{
comp, win32_resource, main_progress_node,
});
}
prelink_group.wait(io);
comp.link_queue.finishPrelinkQueue(comp);
}
const JobError = Allocator.Error || Io.Cancelable;
@@ -5040,58 +5051,38 @@ pub fn queueJobs(comp: *Compilation, jobs: []const Job) !void {
for (jobs) |job| try comp.queueJob(job);
}
fn processOneJob(tid: usize, comp: *Compilation, job: Job) JobError!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 {
zcu.codegen_prog_node.completeOne();
comp.link_prog_node.completeOne();
air.deinit(gpa);
}
if (!air.typesFullyResolved(zcu)) {
var owned_air: ?Air = func.air;
defer if (owned_air) |*air| air.deinit(gpa);
if (!owned_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;
}
const shared_mir = try gpa.create(link.ZcuTask.LinkFunc.SharedMir);
shared_mir.* = .{
.status = .init(.pending),
.value = undefined,
};
assert(zcu.pending_codegen_jobs.rmw(.Add, 1, .monotonic) > 0); // the "Code Generation" node hasn't been ended
// 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 {
{
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,
.mir = shared_mir,
.air_bytes = air_bytes,
} });
air.deinit(gpa);
}
// Some linkers need to refer to the AIR. In that case, the linker is not running
// concurrently, so we'll just keep ownership of the AIR for ourselves instead of
// letting the codegen job destroy it.
const disown_air = zcu.backendSupportsFeature(.separate_thread);
// Begin the codegen task. If the codegen/link queue is backed up, this might
// block until the linker is able to process some tasks.
const codegen_task = try zcu.codegen_task_pool.start(zcu, func.func, &owned_air.?, disown_air);
if (disown_air) owned_air = null;
try comp.link_queue.enqueueZcu(comp, tid, .{ .link_func = codegen_task });
},
.link_nav => |nav_index| {
const zcu = comp.zcu.?;
@@ -5111,7 +5102,7 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job) JobError!void {
comp.link_prog_node.completeOne();
return;
}
comp.dispatchZcuLinkTask(tid, .{ .link_nav = nav_index });
try comp.link_queue.enqueueZcu(comp, tid, .{ .link_nav = nav_index });
},
.link_type => |ty| {
const zcu = comp.zcu.?;
@@ -5123,10 +5114,10 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job) JobError!void {
comp.link_prog_node.completeOne();
return;
}
comp.dispatchZcuLinkTask(tid, .{ .link_type = ty });
try comp.link_queue.enqueueZcu(comp, tid, .{ .link_type = ty });
},
.update_line_number => |ti| {
comp.dispatchZcuLinkTask(tid, .{ .update_line_number = ti });
.update_line_number => |tracked_inst| {
try comp.link_queue.enqueueZcu(comp, tid, .{ .update_line_number = tracked_inst });
},
.analyze_func => |func| {
const named_frame = tracy.namedFrame("analyze_func");
@@ -5220,12 +5211,6 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job) JobError!void {
}
}
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);
}
fn createDepFile(
comp: *Compilation,
depfile: []const u8,
@@ -5480,6 +5465,7 @@ fn workerDocsWasmFallible(comp: *Compilation, prog_node: std.Progress.Node) SubU
var sub_create_diag: CreateDiagnostic = undefined;
const sub_compilation = Compilation.create(gpa, arena, io, &sub_create_diag, .{
.thread_limit = comp.thread_limit,
.dirs = dirs,
.self_exe_path = comp.self_exe_path,
.config = config,
@@ -5487,7 +5473,6 @@ fn workerDocsWasmFallible(comp: *Compilation, prog_node: std.Progress.Node) SubU
.entry = .disabled,
.cache_mode = .whole,
.root_name = root_name,
.thread_pool = comp.thread_pool,
.libc_installation = comp.libc_installation,
.emit_bin = .yes_cache,
.verbose_cc = comp.verbose_cc,
@@ -5541,13 +5526,15 @@ fn workerDocsWasmFallible(comp: *Compilation, prog_node: std.Progress.Node) SubU
}
fn workerUpdateFile(
tid: usize,
comp: *Compilation,
file: *Zcu.File,
file_index: Zcu.File.Index,
prog_node: std.Progress.Node,
wg: *WaitGroup,
group: *Io.Group,
) void {
const tid = Compilation.getTid();
const io = comp.io;
const child_prog_node = prog_node.start(fs.path.basename(file.path.sub_path), 0);
defer child_prog_node.end();
@@ -5556,8 +5543,8 @@ fn workerUpdateFile(
pt.updateFile(file_index, file) catch |err| {
pt.reportRetryableFileError(file_index, "unable to load '{s}': {s}", .{ fs.path.basename(file.path.sub_path), @errorName(err) }) catch |oom| switch (oom) {
error.OutOfMemory => {
comp.mutex.lock();
defer comp.mutex.unlock();
comp.mutex.lockUncancelable(io);
defer comp.mutex.unlock(io);
comp.setAllocFailure();
},
};
@@ -5587,14 +5574,14 @@ fn workerUpdateFile(
if (pt.discoverImport(file.path, import_path)) |res| switch (res) {
.module, .existing_file => {},
.new_file => |new| {
comp.thread_pool.spawnWgId(wg, workerUpdateFile, .{
comp, new.file, new.index, prog_node, wg,
group.async(io, workerUpdateFile, .{
comp, new.file, new.index, prog_node, group,
});
},
} else |err| switch (err) {
error.OutOfMemory => {
comp.mutex.lock();
defer comp.mutex.unlock();
comp.mutex.lockUncancelable(io);
defer comp.mutex.unlock(io);
comp.setAllocFailure();
},
}
@@ -5610,17 +5597,20 @@ fn workerUpdateBuiltinFile(comp: *Compilation, file: *Zcu.File) void {
);
}
fn workerUpdateEmbedFile(tid: usize, comp: *Compilation, ef_index: Zcu.EmbedFile.Index, ef: *Zcu.EmbedFile) void {
fn workerUpdateEmbedFile(comp: *Compilation, ef_index: Zcu.EmbedFile.Index, ef: *Zcu.EmbedFile) void {
const tid = Compilation.getTid();
const io = comp.io;
comp.detectEmbedFileUpdate(@enumFromInt(tid), ef_index, ef) catch |err| switch (err) {
error.OutOfMemory => {
comp.mutex.lock();
defer comp.mutex.unlock();
comp.mutex.lockUncancelable(io);
defer comp.mutex.unlock(io);
comp.setAllocFailure();
},
};
}
fn detectEmbedFileUpdate(comp: *Compilation, tid: Zcu.PerThread.Id, ef_index: Zcu.EmbedFile.Index, ef: *Zcu.EmbedFile) !void {
const io = comp.io;
const zcu = comp.zcu.?;
const pt: Zcu.PerThread = .activate(zcu, tid);
defer pt.deactivate();
@@ -5633,8 +5623,8 @@ fn detectEmbedFileUpdate(comp: *Compilation, tid: Zcu.PerThread.Id, ef_index: Zc
if (ef.val != .none and ef.val == old_val) return; // success, value unchanged
if (ef.val == .none and old_val == .none and ef.err == old_err) return; // failure, error unchanged
comp.mutex.lock();
defer comp.mutex.unlock();
comp.mutex.lockUncancelable(io);
defer comp.mutex.unlock(io);
try zcu.markDependeeOutdated(.not_marked_po, .{ .embed_file = ef_index });
}
@@ -5777,8 +5767,8 @@ pub fn translateC(
switch (comp.cache_use) {
.whole => |whole| if (whole.cache_manifest) |whole_cache_manifest| {
whole.cache_manifest_mutex.lock();
defer whole.cache_manifest_mutex.unlock();
try whole.cache_manifest_mutex.lock(io);
defer whole.cache_manifest_mutex.unlock(io);
try whole_cache_manifest.addDepFilePost(cache_tmp_dir, dep_basename);
},
.incremental, .none => {},
@@ -5879,7 +5869,6 @@ fn workerUpdateCObject(
c_object: *CObject,
progress_node: std.Progress.Node,
) void {
defer comp.link_task_queue.finishPrelinkItem(comp);
comp.updateCObject(c_object, progress_node) catch |err| switch (err) {
error.AnalysisFail => return,
else => {
@@ -5897,7 +5886,6 @@ fn workerUpdateWin32Resource(
win32_resource: *Win32Resource,
progress_node: std.Progress.Node,
) void {
defer comp.link_task_queue.finishPrelinkItem(comp);
comp.updateWin32Resource(win32_resource, progress_node) catch |err| switch (err) {
error.AnalysisFail => return,
else => {
@@ -5915,21 +5903,6 @@ 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,
@@ -5941,7 +5914,6 @@ fn buildRt(
options: RtOptions,
out: *?CrtFile,
) void {
defer comp.link_task_queue.finishPrelinkItem(comp);
comp.buildOutputFromZig(
root_source_name,
root_name,
@@ -5960,7 +5932,6 @@ fn buildRt(
}
fn buildMuslCrtFile(comp: *Compilation, crt_file: musl.CrtFile, prog_node: std.Progress.Node) void {
defer comp.link_task_queue.finishPrelinkItem(comp);
if (musl.buildCrtFile(comp, crt_file, prog_node)) |_| {
comp.queued_jobs.musl_crt_file[@intFromEnum(crt_file)] = false;
} else |err| switch (err) {
@@ -5972,7 +5943,6 @@ fn buildMuslCrtFile(comp: *Compilation, crt_file: musl.CrtFile, prog_node: std.P
}
fn buildGlibcCrtFile(comp: *Compilation, crt_file: glibc.CrtFile, prog_node: std.Progress.Node) void {
defer comp.link_task_queue.finishPrelinkItem(comp);
if (glibc.buildCrtFile(comp, crt_file, prog_node)) |_| {
comp.queued_jobs.glibc_crt_file[@intFromEnum(crt_file)] = false;
} else |err| switch (err) {
@@ -5984,7 +5954,6 @@ fn buildGlibcCrtFile(comp: *Compilation, crt_file: glibc.CrtFile, prog_node: std
}
fn buildGlibcSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) void {
defer comp.link_task_queue.finishPrelinkItem(comp);
if (glibc.buildSharedObjects(comp, prog_node)) |_| {
// The job should no longer be queued up since it succeeded.
comp.queued_jobs.glibc_shared_objects = false;
@@ -5995,7 +5964,6 @@ fn buildGlibcSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) voi
}
fn buildFreeBSDCrtFile(comp: *Compilation, crt_file: freebsd.CrtFile, prog_node: std.Progress.Node) void {
defer comp.link_task_queue.finishPrelinkItem(comp);
if (freebsd.buildCrtFile(comp, crt_file, prog_node)) |_| {
comp.queued_jobs.freebsd_crt_file[@intFromEnum(crt_file)] = false;
} else |err| switch (err) {
@@ -6007,7 +5975,6 @@ fn buildFreeBSDCrtFile(comp: *Compilation, crt_file: freebsd.CrtFile, prog_node:
}
fn buildFreeBSDSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) void {
defer comp.link_task_queue.finishPrelinkItem(comp);
if (freebsd.buildSharedObjects(comp, prog_node)) |_| {
// The job should no longer be queued up since it succeeded.
comp.queued_jobs.freebsd_shared_objects = false;
@@ -6020,7 +5987,6 @@ fn buildFreeBSDSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) v
}
fn buildNetBSDCrtFile(comp: *Compilation, crt_file: netbsd.CrtFile, prog_node: std.Progress.Node) void {
defer comp.link_task_queue.finishPrelinkItem(comp);
if (netbsd.buildCrtFile(comp, crt_file, prog_node)) |_| {
comp.queued_jobs.netbsd_crt_file[@intFromEnum(crt_file)] = false;
} else |err| switch (err) {
@@ -6032,7 +5998,6 @@ fn buildNetBSDCrtFile(comp: *Compilation, crt_file: netbsd.CrtFile, prog_node: s
}
fn buildNetBSDSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) void {
defer comp.link_task_queue.finishPrelinkItem(comp);
if (netbsd.buildSharedObjects(comp, prog_node)) |_| {
// The job should no longer be queued up since it succeeded.
comp.queued_jobs.netbsd_shared_objects = false;
@@ -6045,7 +6010,6 @@ fn buildNetBSDSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) vo
}
fn buildMingwCrtFile(comp: *Compilation, crt_file: mingw.CrtFile, prog_node: std.Progress.Node) void {
defer comp.link_task_queue.finishPrelinkItem(comp);
if (mingw.buildCrtFile(comp, crt_file, prog_node)) |_| {
comp.queued_jobs.mingw_crt_file[@intFromEnum(crt_file)] = false;
} else |err| switch (err) {
@@ -6057,7 +6021,6 @@ fn buildMingwCrtFile(comp: *Compilation, crt_file: mingw.CrtFile, prog_node: std
}
fn buildWasiLibcCrtFile(comp: *Compilation, crt_file: wasi_libc.CrtFile, prog_node: std.Progress.Node) void {
defer comp.link_task_queue.finishPrelinkItem(comp);
if (wasi_libc.buildCrtFile(comp, crt_file, prog_node)) |_| {
comp.queued_jobs.wasi_libc_crt_file[@intFromEnum(crt_file)] = false;
} else |err| switch (err) {
@@ -6069,7 +6032,6 @@ fn buildWasiLibcCrtFile(comp: *Compilation, crt_file: wasi_libc.CrtFile, prog_no
}
fn buildLibUnwind(comp: *Compilation, prog_node: std.Progress.Node) void {
defer comp.link_task_queue.finishPrelinkItem(comp);
if (libunwind.buildStaticLib(comp, prog_node)) |_| {
comp.queued_jobs.libunwind = false;
} else |err| switch (err) {
@@ -6079,7 +6041,6 @@ fn buildLibUnwind(comp: *Compilation, prog_node: std.Progress.Node) void {
}
fn buildLibCxx(comp: *Compilation, prog_node: std.Progress.Node) void {
defer comp.link_task_queue.finishPrelinkItem(comp);
if (libcxx.buildLibCxx(comp, prog_node)) |_| {
comp.queued_jobs.libcxx = false;
} else |err| switch (err) {
@@ -6089,7 +6050,6 @@ fn buildLibCxx(comp: *Compilation, prog_node: std.Progress.Node) void {
}
fn buildLibCxxAbi(comp: *Compilation, prog_node: std.Progress.Node) void {
defer comp.link_task_queue.finishPrelinkItem(comp);
if (libcxx.buildLibCxxAbi(comp, prog_node)) |_| {
comp.queued_jobs.libcxxabi = false;
} else |err| switch (err) {
@@ -6099,7 +6059,6 @@ fn buildLibCxxAbi(comp: *Compilation, prog_node: std.Progress.Node) void {
}
fn buildLibTsan(comp: *Compilation, prog_node: std.Progress.Node) void {
defer comp.link_task_queue.finishPrelinkItem(comp);
if (libtsan.buildTsan(comp, prog_node)) |_| {
comp.queued_jobs.libtsan = false;
} else |err| switch (err) {
@@ -6109,7 +6068,6 @@ fn buildLibTsan(comp: *Compilation, prog_node: std.Progress.Node) void {
}
fn buildLibZigC(comp: *Compilation, prog_node: std.Progress.Node) void {
defer comp.link_task_queue.finishPrelinkItem(comp);
comp.buildOutputFromZig(
"c.zig",
"zigc",
@@ -6139,6 +6097,8 @@ fn reportRetryableWin32ResourceError(
win32_resource: *Win32Resource,
err: anyerror,
) error{OutOfMemory}!void {
const io = comp.io;
win32_resource.status = .failure_retryable;
var bundle: ErrorBundle.Wip = undefined;
@@ -6160,8 +6120,8 @@ fn reportRetryableWin32ResourceError(
});
const finished_bundle = try bundle.toOwnedBundle("");
{
comp.mutex.lock();
defer comp.mutex.unlock();
comp.mutex.lockUncancelable(io);
defer comp.mutex.unlock(io);
try comp.failed_win32_resources.putNoClobber(comp.gpa, win32_resource, finished_bundle);
}
}
@@ -6186,8 +6146,8 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr
if (c_object.clearStatus(gpa)) {
// There was previous failure.
comp.mutex.lock();
defer comp.mutex.unlock();
comp.mutex.lockUncancelable(io);
defer comp.mutex.unlock(io);
// If the failure was OOM, there will not be an entry here, so we do
// not assert discard.
_ = comp.failed_c_objects.swapRemove(c_object);
@@ -6457,8 +6417,8 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr
switch (comp.cache_use) {
.whole => |whole| {
if (whole.cache_manifest) |whole_cache_manifest| {
whole.cache_manifest_mutex.lock();
defer whole.cache_manifest_mutex.unlock();
try whole.cache_manifest_mutex.lock(io);
defer whole.cache_manifest_mutex.unlock(io);
try whole_cache_manifest.addDepFilePost(zig_cache_tmp_dir, dep_basename);
}
},
@@ -6503,7 +6463,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr
},
};
comp.queuePrelinkTasks(&.{.{ .load_object = c_object.status.success.object_path }});
try 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 {
@@ -6517,6 +6477,8 @@ fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32
const tracy_trace = trace(@src());
defer tracy_trace.end();
const io = comp.io;
const src_path = switch (win32_resource.src) {
.rc => |rc_src| rc_src.src_path,
.manifest => |src_path| src_path,
@@ -6531,8 +6493,8 @@ fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32
if (win32_resource.clearStatus(comp.gpa)) {
// There was previous failure.
comp.mutex.lock();
defer comp.mutex.unlock();
comp.mutex.lockUncancelable(io);
defer comp.mutex.unlock(io);
// If the failure was OOM, there will not be an entry here, so we do
// not assert discard.
_ = comp.failed_win32_resources.swapRemove(win32_resource);
@@ -6706,8 +6668,8 @@ fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32
try man.addFilePost(dep_file_path);
switch (comp.cache_use) {
.whole => |whole| if (whole.cache_manifest) |whole_cache_manifest| {
whole.cache_manifest_mutex.lock();
defer whole.cache_manifest_mutex.unlock();
try whole.cache_manifest_mutex.lock(io);
defer whole.cache_manifest_mutex.unlock(io);
try whole_cache_manifest.addFilePost(dep_file_path);
},
.incremental, .none => {},
@@ -7428,8 +7390,9 @@ fn failCObjWithOwnedDiagBundle(
@branchHint(.cold);
assert(diag_bundle.diags.len > 0);
{
comp.mutex.lock();
defer comp.mutex.unlock();
const io = comp.io;
comp.mutex.lockUncancelable(io);
defer comp.mutex.unlock(io);
{
errdefer diag_bundle.destroy(comp.gpa);
try comp.failed_c_objects.ensureUnusedCapacity(comp.gpa, 1);
@@ -7470,8 +7433,9 @@ fn failWin32ResourceWithOwnedBundle(
) error{ OutOfMemory, AnalysisFail } {
@branchHint(.cold);
{
comp.mutex.lock();
defer comp.mutex.unlock();
const io = comp.io;
comp.mutex.lockUncancelable(io);
defer comp.mutex.unlock(io);
try comp.failed_win32_resources.putNoClobber(comp.gpa, win32_resource, err_bundle);
}
win32_resource.status = .failure;
@@ -7795,9 +7759,9 @@ pub fn lockAndSetMiscFailure(
comptime format: []const u8,
args: anytype,
) void {
comp.mutex.lock();
defer comp.mutex.unlock();
const io = comp.io;
comp.mutex.lockUncancelable(io);
defer comp.mutex.unlock(io);
return setMiscFailure(comp, tag, format, args);
}
@@ -7840,8 +7804,8 @@ pub fn updateSubCompilation(
defer errors.deinit(gpa);
if (errors.errorMessageCount() > 0) {
parent_comp.mutex.lock();
defer parent_comp.mutex.unlock();
parent_comp.mutex.lockUncancelable(parent_comp.io);
defer parent_comp.mutex.unlock(parent_comp.io);
try parent_comp.misc_failures.ensureUnusedCapacity(gpa, 1);
parent_comp.misc_failures.putAssumeCapacityNoClobber(misc_task, .{
.msg = try std.fmt.allocPrint(gpa, "sub-compilation of {t} failed", .{misc_task}),
@@ -7942,6 +7906,7 @@ fn buildOutputFromZig(
var sub_create_diag: CreateDiagnostic = undefined;
const sub_compilation = Compilation.create(gpa, arena, io, &sub_create_diag, .{
.thread_limit = comp.thread_limit,
.dirs = comp.dirs.withoutLocalCache(),
.cache_mode = .whole,
.parent_whole_cache = parent_whole_cache,
@@ -7949,7 +7914,6 @@ fn buildOutputFromZig(
.config = config,
.root_mod = root_mod,
.root_name = root_name,
.thread_pool = comp.thread_pool,
.libc_installation = comp.libc_installation,
.emit_bin = .yes_cache,
.function_sections = true,
@@ -7980,7 +7944,7 @@ fn buildOutputFromZig(
assert(out.* == null);
out.* = crt_file;
comp.queuePrelinkTaskMode(crt_file.full_object_path, &config);
try comp.queuePrelinkTaskMode(crt_file.full_object_path, &config);
}
pub const CrtFileOptions = struct {
@@ -8079,13 +8043,13 @@ pub fn build_crt_file(
var sub_create_diag: CreateDiagnostic = undefined;
const sub_compilation = Compilation.create(gpa, arena, io, &sub_create_diag, .{
.thread_limit = comp.thread_limit,
.dirs = comp.dirs.withoutLocalCache(),
.self_exe_path = comp.self_exe_path,
.cache_mode = .whole,
.config = config,
.root_mod = root_mod,
.root_name = root_name,
.thread_pool = comp.thread_pool,
.libc_installation = comp.libc_installation,
.emit_bin = .yes_cache,
.function_sections = options.function_sections orelse false,
@@ -8114,18 +8078,18 @@ pub fn build_crt_file(
try comp.updateSubCompilation(sub_compilation, misc_task_tag, prog_node);
const crt_file = try sub_compilation.toCrtFile();
comp.queuePrelinkTaskMode(crt_file.full_object_path, &config);
try comp.queuePrelinkTaskMode(crt_file.full_object_path, &config);
{
comp.mutex.lock();
defer comp.mutex.unlock();
comp.mutex.lockUncancelable(io);
defer comp.mutex.unlock(io);
try comp.crt_files.ensureUnusedCapacity(gpa, 1);
comp.crt_files.putAssumeCapacityNoClobber(basename, crt_file);
}
}
pub fn queuePrelinkTaskMode(comp: *Compilation, path: Cache.Path, config: *const Compilation.Config) void {
comp.queuePrelinkTasks(switch (config.output_mode) {
pub fn queuePrelinkTaskMode(comp: *Compilation, path: Cache.Path, config: *const Compilation.Config) Io.Cancelable!void {
try comp.queuePrelinkTasks(switch (config.output_mode) {
.Exe => unreachable,
.Obj => &.{.{ .load_object = path }},
.Lib => &.{switch (config.link_mode) {
@@ -8135,33 +8099,10 @@ pub fn queuePrelinkTaskMode(comp: *Compilation, path: Cache.Path, config: *const
});
}
/// Only valid to call during `update`. Automatically handles queuing up a
/// linker worker task if there is not already one.
pub fn queuePrelinkTasks(comp: *Compilation, tasks: []const link.PrelinkTask) void {
/// Only valid to call during `update`.
pub fn queuePrelinkTasks(comp: *Compilation, tasks: []const link.PrelinkTask) Io.Cancelable!void {
comp.link_prog_node.increaseEstimatedTotalItems(tasks.len);
comp.link_task_queue.enqueuePrelink(comp, tasks) catch |err| switch (err) {
error.OutOfMemory => return comp.setAllocFailure(),
};
}
/// 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();
},
};
try comp.link_queue.enqueuePrelink(comp, tasks);
}
pub fn toCrtFile(comp: *Compilation) Allocator.Error!CrtFile {
@@ -8251,3 +8192,17 @@ pub fn compilerRtOptMode(comp: Compilation) std.builtin.OptimizeMode {
pub fn compilerRtStrip(comp: Compilation) bool {
return comp.root_mod.strip;
}
/// This is a temporary workaround put in place to migrate from `std.Thread.Pool`
/// to `std.Io.Threaded` for asynchronous/concurrent work. The eventual solution
/// will likely involve significant changes to the `InternPool` implementation.
pub fn getTid() usize {
if (my_tid == null) my_tid = next_tid.fetchAdd(1, .monotonic);
return my_tid.?;
}
pub fn setMainThread() void {
my_tid = 0;
}
/// TID 0 is reserved for the main thread.
var next_tid: std.atomic.Value(usize) = .init(1);
threadlocal var my_tid: ?usize = null;
+109 -39
View File
@@ -14,57 +14,122 @@ comptime {
}
zcu: *Zcu,
thread: ?std.Thread,
running: std.atomic.Value(bool),
future: ?Io.Future(void),
/// Held by our owner when an update is in-progress, and held by us when responding to a command.
/// So, essentially guards all access to `Compilation`, including `Zcu`.
mutex: std.Thread.Mutex,
mutex: std.Io.Mutex,
pub fn init(zcu: *Zcu) IncrementalDebugServer {
return .{
.zcu = zcu,
.thread = null,
.running = .init(true),
.mutex = .{},
.future = null,
.mutex = .init,
};
}
pub fn deinit(ids: *IncrementalDebugServer) void {
if (ids.thread) |t| {
ids.running.store(false, .monotonic);
t.join();
}
const io = ids.zcu.comp.io;
if (ids.future) |*f| f.cancel(io);
}
const port = 7623;
pub fn spawn(ids: *IncrementalDebugServer) void {
const io = ids.zcu.comp.io;
std.debug.print("spawning incremental debug server on port {d}\n", .{port});
ids.thread = std.Thread.spawn(.{ .allocator = ids.zcu.comp.arena }, runThread, .{ids}) catch |err|
std.process.fatal("failed to spawn incremental debug server: {s}", .{@errorName(err)});
ids.future = io.concurrent(runServer, .{ids}) catch |err|
std.process.fatal("failed to start incremental debug server: {s}", .{@errorName(err)});
}
fn runThread(ids: *IncrementalDebugServer) void {
fn runServer(ids: *IncrementalDebugServer) void {
const io = ids.zcu.comp.io;
const addr: Io.net.IpAddress = .{ .ip6 = .loopback(port) };
var server = addr.listen(io, .{}) catch |err| switch (err) {
error.Canceled => return,
else => |e| {
log.err("listen failed ({t}); closing server", .{e});
return;
},
};
defer server.deinit(io);
while (true) {
var stream = server.accept(io) catch |err| switch (err) {
error.Canceled => return,
error.ConnectionAborted => {
log.warn("client disconnected during accept", .{});
continue;
},
else => |e| {
log.err("accept failed ({t})", .{e});
return;
},
};
defer stream.close(io);
log.info("client '{f}' connected", .{stream.socket.address});
var cmd_buf: [1024]u8 = undefined;
var reader = stream.reader(io, &cmd_buf);
var writer = stream.writer(io, &.{});
ids.serveStream(&reader.interface, &writer.interface) catch |orig_err| {
const actual_err = switch (orig_err) {
error.Canceled,
error.OutOfMemory,
error.EndOfStream,
error.StreamTooLong,
=> |e| e,
error.ReadFailed => reader.err.?,
error.WriteFailed => writer.err.?,
};
switch (actual_err) {
error.Canceled => return,
error.OutOfMemory,
error.Unexpected,
error.SystemResources,
error.Timeout,
error.NetworkDown,
error.NetworkUnreachable,
error.HostUnreachable,
error.FastOpenAlreadyInProgress,
error.ConnectionRefused,
error.StreamTooLong,
=> |e| log.err("failed to serve '{f}' ({t})", .{ stream.socket.address, e }),
error.EndOfStream,
error.ConnectionResetByPeer,
=> log.info("client '{f}' disconnected", .{stream.socket.address}),
error.AddressFamilyUnsupported,
error.SocketUnconnected,
error.SocketNotBound,
error.AccessDenied,
=> unreachable,
}
};
}
}
fn serveStream(
ids: *IncrementalDebugServer,
stream_reader: *Io.Reader,
stream_writer: *Io.Writer,
) error{
Canceled,
OutOfMemory,
EndOfStream,
StreamTooLong,
ReadFailed,
WriteFailed,
}!noreturn {
const gpa = ids.zcu.gpa;
const io = ids.zcu.comp.io;
var cmd_buf: [1024]u8 = undefined;
var text_out: std.ArrayList(u8) = .empty;
defer text_out.deinit(gpa);
const addr: std.Io.net.IpAddress = .{ .ip6 = .loopback(port) };
var server = addr.listen(io, .{}) catch @panic("IncrementalDebugServer: failed to listen");
defer server.deinit(io);
var stream = server.accept(io) catch @panic("IncrementalDebugServer: failed to accept");
defer stream.close(io);
var stream_reader = stream.reader(io, &cmd_buf);
var stream_writer = stream.writer(io, &.{});
while (ids.running.load(.monotonic)) {
stream_writer.interface.writeAll("zig> ") catch @panic("IncrementalDebugServer: failed to write");
const untrimmed = stream_reader.interface.takeSentinel('\n') catch |err| switch (err) {
error.EndOfStream => break,
else => @panic("IncrementalDebugServer: failed to read command"),
};
while (true) {
try stream_writer.writeAll("zig> ");
const untrimmed = try stream_reader.takeSentinel('\n');
const cmd_and_arg = std.mem.trim(u8, untrimmed, " \t\r\n");
const cmd: []const u8, const arg: []const u8 = if (std.mem.indexOfScalar(u8, cmd_and_arg, ' ')) |i|
.{ cmd_and_arg[0..i], cmd_and_arg[i + 1 ..] }
@@ -74,18 +139,21 @@ fn runThread(ids: *IncrementalDebugServer) void {
text_out.clearRetainingCapacity();
{
if (!ids.mutex.tryLock()) {
stream_writer.interface.writeAll("waiting for in-progress update to finish...\n") catch @panic("IncrementalDebugServer: failed to write");
ids.mutex.lock();
try stream_writer.writeAll("waiting for in-progress update to finish...\n");
try ids.mutex.lock(io);
}
defer ids.mutex.unlock();
var allocating: std.Io.Writer.Allocating = .fromArrayList(gpa, &text_out);
defer ids.mutex.unlock(io);
var allocating: Io.Writer.Allocating = .fromArrayList(gpa, &text_out);
defer text_out = allocating.toArrayList();
handleCommand(ids.zcu, &allocating.writer, cmd, arg) catch @panic("IncrementalDebugServer: out of memory");
handleCommand(ids.zcu, &allocating.writer, cmd, arg) catch |err| switch (err) {
error.OutOfMemory,
error.WriteFailed,
=> return error.OutOfMemory,
};
}
text_out.append(gpa, '\n') catch @panic("IncrementalDebugServer: out of memory");
stream_writer.interface.writeAll(text_out.items) catch @panic("IncrementalDebugServer: failed to write");
try text_out.append(gpa, '\n');
try stream_writer.writeAll(text_out.items);
}
std.debug.print("closing incremental debug server\n", .{});
}
const help_str: []const u8 =
@@ -123,7 +191,7 @@ const help_str: []const u8 =
\\
;
fn handleCommand(zcu: *Zcu, w: *std.Io.Writer, cmd_str: []const u8, arg_str: []const u8) error{ WriteFailed, OutOfMemory }!void {
fn handleCommand(zcu: *Zcu, w: *Io.Writer, cmd_str: []const u8, arg_str: []const u8) error{ WriteFailed, OutOfMemory }!void {
const ip = &zcu.intern_pool;
if (std.mem.eql(u8, cmd_str, "help")) {
try w.writeAll(help_str);
@@ -328,7 +396,8 @@ fn printAnalUnit(unit: AnalUnit, buf: *[32]u8) []const u8 {
};
return std.fmt.bufPrint(buf, "{s} {d}", .{ @tagName(unit.unwrap()), idx }) catch unreachable;
}
fn printType(ty: Type, zcu: *const Zcu, w: anytype) !void {
fn printType(ty: Type, zcu: *const Zcu, w: *Io.Writer) Io.Writer.Error!void {
const ip = &zcu.intern_pool;
switch (ip.indexToKey(ty.toIntern())) {
.int_type => |int| try w.print("{c}{d}", .{
@@ -377,6 +446,7 @@ fn printType(ty: Type, zcu: *const Zcu, w: anytype) !void {
const std = @import("std");
const Io = std.Io;
const Allocator = std.mem.Allocator;
const log = std.log.scoped(.incremental_debug_server);
const Compilation = @import("Compilation.zig");
const Zcu = @import("Zcu.zig");
+441 -331
View File
@@ -8,6 +8,7 @@ const assert = std.debug.assert;
const BigIntConst = std.math.big.int.Const;
const BigIntMutable = std.math.big.int.Mutable;
const Cache = std.Build.Cache;
const Io = std.Io;
const Limb = std.math.big.Limb;
const Hash = std.hash.Wyhash;
@@ -214,6 +215,7 @@ pub const TrackedInst = extern struct {
pub fn trackZir(
ip: *InternPool,
gpa: Allocator,
io: Io,
tid: Zcu.PerThread.Id,
key: TrackedInst,
) Allocator.Error!TrackedInst.Index {
@@ -235,8 +237,8 @@ pub fn trackZir(
if (entry.hash != hash) continue;
if (std.meta.eql(index.resolveFull(ip) orelse continue, key)) return index;
}
shard.mutate.tracked_inst_map.mutex.lock();
defer shard.mutate.tracked_inst_map.mutex.unlock();
shard.mutate.tracked_inst_map.mutex.lock(io, tid);
defer shard.mutate.tracked_inst_map.mutex.unlock(io);
if (map.entries != shard.shared.tracked_inst_map.entries) {
map = shard.shared.tracked_inst_map;
map_mask = map.header().mask();
@@ -251,7 +253,7 @@ pub fn trackZir(
}
defer shard.mutate.tracked_inst_map.len += 1;
const local = ip.getLocal(tid);
const list = local.getMutableTrackedInsts(gpa);
const list = local.getMutableTrackedInsts(gpa, io);
try list.ensureUnusedCapacity(1);
const map_header = map.header().*;
if (shard.mutate.tracked_inst_map.len < map_header.capacity * 3 / 5) {
@@ -317,6 +319,7 @@ pub fn trackZir(
pub fn rehashTrackedInsts(
ip: *InternPool,
gpa: Allocator,
io: Io,
tid: Zcu.PerThread.Id,
) Allocator.Error!void {
assert(tid == .main); // we shouldn't have any other threads active right now
@@ -333,7 +336,7 @@ pub fn rehashTrackedInsts(
for (ip.locals) |*local| {
// `getMutableTrackedInsts` is okay only because no other thread is currently active.
// We need the `mutate` for the len.
for (local.getMutableTrackedInsts(gpa).viewAllowEmpty().items(.@"0")) |tracked_inst| {
for (local.getMutableTrackedInsts(gpa, io).viewAllowEmpty().items(.@"0")) |tracked_inst| {
if (tracked_inst.inst == .lost) continue; // we can ignore this one!
const full_hash = Hash.hash(0, std.mem.asBytes(&tracked_inst));
const shard = &ip.shards[@intCast(full_hash & (ip.shards.len - 1))];
@@ -379,7 +382,7 @@ pub fn rehashTrackedInsts(
for (ip.locals, 0..) |*local, local_tid| {
// `getMutableTrackedInsts` is okay only because no other thread is currently active.
// We need the `mutate` for the len.
for (local.getMutableTrackedInsts(gpa).viewAllowEmpty().items(.@"0"), 0..) |tracked_inst, local_inst_index| {
for (local.getMutableTrackedInsts(gpa, io).viewAllowEmpty().items(.@"0"), 0..) |tracked_inst, local_inst_index| {
if (tracked_inst.inst == .lost) continue; // we can ignore this one!
const full_hash = Hash.hash(0, std.mem.asBytes(&tracked_inst));
const hash: u32 = @truncate(full_hash >> 32);
@@ -1113,11 +1116,11 @@ const Local = struct {
const Namespaces = List(struct { *[1 << namespaces_bucket_width]Zcu.Namespace });
const ListMutate = struct {
mutex: std.Thread.Mutex,
mutex: Io.Mutex,
len: u32,
const empty: ListMutate = .{
.mutex = .{},
.mutex = .init,
.len = 0,
};
};
@@ -1144,6 +1147,7 @@ const Local = struct {
const ListSelf = @This();
const Mutable = struct {
gpa: Allocator,
io: Io,
arena: *std.heap.ArenaAllocator.State,
mutate: *ListMutate,
list: *ListSelf,
@@ -1296,6 +1300,7 @@ const Local = struct {
}
fn setCapacity(mutable: Mutable, capacity: u32) Allocator.Error!void {
const io = mutable.io;
var arena = mutable.arena.promote(mutable.gpa);
defer mutable.arena.* = arena.state;
const buf = try arena.allocator().alignedAlloc(
@@ -1313,8 +1318,8 @@ const Local = struct {
const new_slice = new_list.view().slice();
inline for (fields) |field| @memcpy(new_slice.items(field)[0..len], old_slice.items(field)[0..len]);
}
mutable.mutate.mutex.lock();
defer mutable.mutate.mutex.unlock();
mutable.mutate.mutex.lockUncancelable(io);
defer mutable.mutate.mutex.unlock(io);
mutable.list.release(new_list);
}
@@ -1375,18 +1380,20 @@ const Local = struct {
};
}
pub fn getMutableItems(local: *Local, gpa: Allocator) List(Item).Mutable {
pub fn getMutableItems(local: *Local, gpa: Allocator, io: Io) List(Item).Mutable {
return .{
.gpa = gpa,
.io = io,
.arena = &local.mutate.arena,
.mutate = &local.mutate.items,
.list = &local.shared.items,
};
}
pub fn getMutableExtra(local: *Local, gpa: Allocator) Extra.Mutable {
pub fn getMutableExtra(local: *Local, gpa: Allocator, io: Io) Extra.Mutable {
return .{
.gpa = gpa,
.io = io,
.arena = &local.mutate.arena,
.mutate = &local.mutate.extra,
.list = &local.shared.extra,
@@ -1397,11 +1404,12 @@ const Local = struct {
/// On 64-bit systems, this array is used for big integers and associated metadata.
/// Use the helper methods instead of accessing this directly in order to not
/// violate the above mechanism.
pub fn getMutableLimbs(local: *Local, gpa: Allocator) Limbs.Mutable {
pub fn getMutableLimbs(local: *Local, gpa: Allocator, io: Io) Limbs.Mutable {
return switch (@sizeOf(Limb)) {
@sizeOf(u32) => local.getMutableExtra(gpa),
@sizeOf(u32) => local.getMutableExtra(gpa, io),
@sizeOf(u64) => .{
.gpa = gpa,
.io = io,
.arena = &local.mutate.arena,
.mutate = &local.mutate.limbs,
.list = &local.shared.limbs,
@@ -1411,9 +1419,10 @@ const Local = struct {
}
/// A list of offsets into `string_bytes` for each string.
pub fn getMutableStrings(local: *Local, gpa: Allocator) Strings.Mutable {
pub fn getMutableStrings(local: *Local, gpa: Allocator, io: Io) Strings.Mutable {
return .{
.gpa = gpa,
.io = io,
.arena = &local.mutate.arena,
.mutate = &local.mutate.strings,
.list = &local.shared.strings,
@@ -1425,9 +1434,10 @@ const Local = struct {
/// is referencing the data here whether they want to store both index and length,
/// thus allowing null bytes, or store only index, and use null-termination. The
/// `strings_bytes` array is agnostic to either usage.
pub fn getMutableStringBytes(local: *Local, gpa: Allocator) StringBytes.Mutable {
pub fn getMutableStringBytes(local: *Local, gpa: Allocator, io: Io) StringBytes.Mutable {
return .{
.gpa = gpa,
.io = io,
.arena = &local.mutate.arena,
.mutate = &local.mutate.string_bytes,
.list = &local.shared.string_bytes,
@@ -1436,9 +1446,10 @@ const Local = struct {
/// An index into `tracked_insts` gives a reference to a single ZIR instruction which
/// persists across incremental updates.
pub fn getMutableTrackedInsts(local: *Local, gpa: Allocator) TrackedInsts.Mutable {
pub fn getMutableTrackedInsts(local: *Local, gpa: Allocator, io: Io) TrackedInsts.Mutable {
return .{
.gpa = gpa,
.io = io,
.arena = &local.mutate.arena,
.mutate = &local.mutate.tracked_insts,
.list = &local.shared.tracked_insts,
@@ -1452,9 +1463,10 @@ const Local = struct {
///
/// Key is the hash of the path to this file, used to store
/// `InternPool.TrackedInst`.
pub fn getMutableFiles(local: *Local, gpa: Allocator) List(File).Mutable {
pub fn getMutableFiles(local: *Local, gpa: Allocator, io: Io) List(File).Mutable {
return .{
.gpa = gpa,
.io = io,
.arena = &local.mutate.arena,
.mutate = &local.mutate.files,
.list = &local.shared.files,
@@ -1466,27 +1478,30 @@ const Local = struct {
/// field names and values directly, relying on one of these maps, stored separately,
/// to provide lookup.
/// These are not serialized; it is computed upon deserialization.
pub fn getMutableMaps(local: *Local, gpa: Allocator) Maps.Mutable {
pub fn getMutableMaps(local: *Local, gpa: Allocator, io: Io) Maps.Mutable {
return .{
.gpa = gpa,
.io = io,
.arena = &local.mutate.arena,
.mutate = &local.mutate.maps,
.list = &local.shared.maps,
};
}
pub fn getMutableNavs(local: *Local, gpa: Allocator) Navs.Mutable {
pub fn getMutableNavs(local: *Local, gpa: Allocator, io: Io) Navs.Mutable {
return .{
.gpa = gpa,
.io = io,
.arena = &local.mutate.arena,
.mutate = &local.mutate.navs,
.list = &local.shared.navs,
};
}
pub fn getMutableComptimeUnits(local: *Local, gpa: Allocator) ComptimeUnits.Mutable {
pub fn getMutableComptimeUnits(local: *Local, gpa: Allocator, io: Io) ComptimeUnits.Mutable {
return .{
.gpa = gpa,
.io = io,
.arena = &local.mutate.arena,
.mutate = &local.mutate.comptime_units,
.list = &local.shared.comptime_units,
@@ -1503,9 +1518,10 @@ const Local = struct {
/// serialization trivial.
/// * It provides a unique integer to be used for anonymous symbol names, avoiding
/// multi-threaded contention on an atomic counter.
pub fn getMutableNamespaces(local: *Local, gpa: Allocator) Namespaces.Mutable {
pub fn getMutableNamespaces(local: *Local, gpa: Allocator, io: Io) Namespaces.Mutable {
return .{
.gpa = gpa,
.io = io,
.arena = &local.mutate.arena,
.mutate = &local.mutate.namespaces.buckets_list,
.list = &local.shared.namespaces,
@@ -1535,11 +1551,63 @@ const Shard = struct {
},
const Mutate = struct {
mutex: std.Thread.Mutex.Recursive,
/// This mutex needs to be recursive because `getFuncDeclIes` interns multiple things at
/// once (the function, its IES, the corresponding error union, and the resulting function
/// type), so calls `getOrPutKeyEnsuringAdditionalCapacity` multiple times. Each of these
/// calls acquires a lock which will only be released when the whole operation is finalized,
/// and these different items could be in the same shard, in which case that shard's lock
/// will be acquired multiple times.
mutex: RecursiveMutex,
len: u32,
const RecursiveMutex = struct {
const OptionalTid = if (single_threaded) enum(u8) {
null,
main,
fn unwrap(ot: OptionalTid) ?Zcu.PerThread.Id {
return switch (ot) {
.null => null,
.main => .main,
};
}
fn wrap(tid: Zcu.PerThread.Id) OptionalTid {
comptime assert(tid == .main);
return .main;
}
} else packed struct(u8) {
non_null: bool,
value: Zcu.PerThread.Id,
const @"null": OptionalTid = .{ .non_null = false, .value = .main };
fn unwrap(ot: OptionalTid) ?Zcu.PerThread.Id {
return if (ot.non_null) ot.value else null;
}
fn wrap(tid: Zcu.PerThread.Id) OptionalTid {
return .{ .non_null = true, .value = tid };
}
};
mutex: Io.Mutex,
tid: std.atomic.Value(OptionalTid),
lock_count: u32,
const init: RecursiveMutex = .{ .mutex = .init, .tid = .init(.null), .lock_count = 0 };
fn lock(r: *RecursiveMutex, io: Io, tid: Zcu.PerThread.Id) void {
if (r.tid.load(.monotonic) != OptionalTid.wrap(tid)) {
r.mutex.lockUncancelable(io);
assert(r.lock_count == 0);
r.tid.store(.wrap(tid), .monotonic);
}
r.lock_count += 1;
}
fn unlock(r: *RecursiveMutex, io: Io) void {
r.lock_count -= 1;
if (r.lock_count == 0) {
r.tid.store(.null, .monotonic);
r.mutex.unlock(io);
}
}
};
const empty: Mutate = .{
.mutex = std.Thread.Mutex.Recursive.init,
.mutex = .init,
.len = 0,
};
};
@@ -1896,7 +1964,7 @@ pub const NullTerminatedString = enum(u32) {
ip: *const InternPool,
id: bool,
};
fn format(data: FormatData, writer: *std.Io.Writer) std.Io.Writer.Error!void {
fn format(data: FormatData, writer: *Io.Writer) Io.Writer.Error!void {
const slice = data.string.toSlice(data.ip);
if (!data.id) {
try writer.writeAll(slice);
@@ -2323,10 +2391,10 @@ pub const Key = union(enum) {
return @atomicLoad(FuncAnalysis, func.analysisPtr(ip), .unordered);
}
pub fn setBranchHint(func: Func, ip: *InternPool, hint: std.builtin.BranchHint) void {
pub fn setBranchHint(func: Func, ip: *InternPool, io: Io, hint: std.builtin.BranchHint) void {
const extra_mutex = &ip.getLocal(func.tid).mutate.extra.mutex;
extra_mutex.lock();
defer extra_mutex.unlock();
extra_mutex.lockUncancelable(io);
defer extra_mutex.unlock(io);
const analysis_ptr = func.analysisPtr(ip);
var analysis = analysis_ptr.*;
@@ -2334,10 +2402,10 @@ pub const Key = union(enum) {
@atomicStore(FuncAnalysis, analysis_ptr, analysis, .release);
}
pub fn setAnalyzed(func: Func, ip: *InternPool) void {
pub fn setAnalyzed(func: Func, ip: *InternPool, io: Io) void {
const extra_mutex = &ip.getLocal(func.tid).mutate.extra.mutex;
extra_mutex.lock();
defer extra_mutex.unlock();
extra_mutex.lockUncancelable(io);
defer extra_mutex.unlock(io);
const analysis_ptr = func.analysisPtr(ip);
var analysis = analysis_ptr.*;
@@ -2365,10 +2433,10 @@ pub const Key = union(enum) {
return @atomicLoad(u32, func.branchQuotaPtr(ip), .unordered);
}
pub fn maxBranchQuota(func: Func, ip: *InternPool, new_branch_quota: u32) void {
pub fn maxBranchQuota(func: Func, ip: *InternPool, io: Io, new_branch_quota: u32) void {
const extra_mutex = &ip.getLocal(func.tid).mutate.extra.mutex;
extra_mutex.lock();
defer extra_mutex.unlock();
extra_mutex.lockUncancelable(io);
defer extra_mutex.unlock(io);
const branch_quota_ptr = func.branchQuotaPtr(ip);
@atomicStore(u32, branch_quota_ptr, @max(branch_quota_ptr.*, new_branch_quota), .release);
@@ -2385,10 +2453,10 @@ pub const Key = union(enum) {
return @atomicLoad(Index, func.resolvedErrorSetPtr(ip), .unordered);
}
pub fn setResolvedErrorSet(func: Func, ip: *InternPool, ies: Index) void {
pub fn setResolvedErrorSet(func: Func, ip: *InternPool, io: Io, ies: Index) void {
const extra_mutex = &ip.getLocal(func.tid).mutate.extra.mutex;
extra_mutex.lock();
defer extra_mutex.unlock();
extra_mutex.lockUncancelable(io);
defer extra_mutex.unlock(io);
@atomicStore(Index, func.resolvedErrorSetPtr(ip), ies, .release);
}
@@ -3349,10 +3417,10 @@ pub const LoadedUnionType = struct {
return @atomicLoad(Index, u.tagTypePtr(ip), .unordered);
}
pub fn setTagType(u: LoadedUnionType, ip: *InternPool, tag_type: Index) void {
pub fn setTagType(u: LoadedUnionType, ip: *InternPool, io: Io, tag_type: Index) void {
const extra_mutex = &ip.getLocal(u.tid).mutate.extra.mutex;
extra_mutex.lock();
defer extra_mutex.unlock();
extra_mutex.lockUncancelable(io);
defer extra_mutex.unlock(io);
@atomicStore(Index, u.tagTypePtr(ip), tag_type, .release);
}
@@ -3368,10 +3436,10 @@ pub const LoadedUnionType = struct {
return @atomicLoad(Tag.TypeUnion.Flags, u.flagsPtr(ip), .unordered);
}
pub fn setStatus(u: LoadedUnionType, ip: *InternPool, status: Status) void {
pub fn setStatus(u: LoadedUnionType, ip: *InternPool, io: Io, status: Status) void {
const extra_mutex = &ip.getLocal(u.tid).mutate.extra.mutex;
extra_mutex.lock();
defer extra_mutex.unlock();
extra_mutex.lockUncancelable(io);
defer extra_mutex.unlock(io);
const flags_ptr = u.flagsPtr(ip);
var flags = flags_ptr.*;
@@ -3379,10 +3447,10 @@ pub const LoadedUnionType = struct {
@atomicStore(Tag.TypeUnion.Flags, flags_ptr, flags, .release);
}
pub fn setStatusIfLayoutWip(u: LoadedUnionType, ip: *InternPool, status: Status) void {
pub fn setStatusIfLayoutWip(u: LoadedUnionType, ip: *InternPool, io: Io, status: Status) void {
const extra_mutex = &ip.getLocal(u.tid).mutate.extra.mutex;
extra_mutex.lock();
defer extra_mutex.unlock();
extra_mutex.lockUncancelable(io);
defer extra_mutex.unlock(io);
const flags_ptr = u.flagsPtr(ip);
var flags = flags_ptr.*;
@@ -3390,10 +3458,10 @@ pub const LoadedUnionType = struct {
@atomicStore(Tag.TypeUnion.Flags, flags_ptr, flags, .release);
}
pub fn setAlignment(u: LoadedUnionType, ip: *InternPool, alignment: Alignment) void {
pub fn setAlignment(u: LoadedUnionType, ip: *InternPool, io: Io, alignment: Alignment) void {
const extra_mutex = &ip.getLocal(u.tid).mutate.extra.mutex;
extra_mutex.lock();
defer extra_mutex.unlock();
extra_mutex.lockUncancelable(io);
defer extra_mutex.unlock(io);
const flags_ptr = u.flagsPtr(ip);
var flags = flags_ptr.*;
@@ -3401,10 +3469,10 @@ pub const LoadedUnionType = struct {
@atomicStore(Tag.TypeUnion.Flags, flags_ptr, flags, .release);
}
pub fn assumeRuntimeBitsIfFieldTypesWip(u: LoadedUnionType, ip: *InternPool) bool {
pub fn assumeRuntimeBitsIfFieldTypesWip(u: LoadedUnionType, ip: *InternPool, io: Io) bool {
const extra_mutex = &ip.getLocal(u.tid).mutate.extra.mutex;
extra_mutex.lock();
defer extra_mutex.unlock();
extra_mutex.lockUncancelable(io);
defer extra_mutex.unlock(io);
const flags_ptr = u.flagsPtr(ip);
var flags = flags_ptr.*;
@@ -3419,10 +3487,10 @@ pub const LoadedUnionType = struct {
return u.flagsUnordered(ip).requires_comptime;
}
pub fn setRequiresComptimeWip(u: LoadedUnionType, ip: *InternPool) RequiresComptime {
pub fn setRequiresComptimeWip(u: LoadedUnionType, ip: *InternPool, io: Io) RequiresComptime {
const extra_mutex = &ip.getLocal(u.tid).mutate.extra.mutex;
extra_mutex.lock();
defer extra_mutex.unlock();
extra_mutex.lockUncancelable(io);
defer extra_mutex.unlock(io);
const flags_ptr = u.flagsPtr(ip);
var flags = flags_ptr.*;
@@ -3433,12 +3501,12 @@ pub const LoadedUnionType = struct {
return flags.requires_comptime;
}
pub fn setRequiresComptime(u: LoadedUnionType, ip: *InternPool, requires_comptime: RequiresComptime) void {
pub fn setRequiresComptime(u: LoadedUnionType, ip: *InternPool, io: Io, requires_comptime: RequiresComptime) void {
assert(requires_comptime != .wip); // see setRequiresComptimeWip
const extra_mutex = &ip.getLocal(u.tid).mutate.extra.mutex;
extra_mutex.lock();
defer extra_mutex.unlock();
extra_mutex.lockUncancelable(io);
defer extra_mutex.unlock(io);
const flags_ptr = u.flagsPtr(ip);
var flags = flags_ptr.*;
@@ -3446,10 +3514,10 @@ pub const LoadedUnionType = struct {
@atomicStore(Tag.TypeUnion.Flags, flags_ptr, flags, .release);
}
pub fn assumePointerAlignedIfFieldTypesWip(u: LoadedUnionType, ip: *InternPool, ptr_align: Alignment) bool {
pub fn assumePointerAlignedIfFieldTypesWip(u: LoadedUnionType, ip: *InternPool, io: Io, ptr_align: Alignment) bool {
const extra_mutex = &ip.getLocal(u.tid).mutate.extra.mutex;
extra_mutex.lock();
defer extra_mutex.unlock();
extra_mutex.lockUncancelable(io);
defer extra_mutex.unlock(io);
const flags_ptr = u.flagsPtr(ip);
var flags = flags_ptr.*;
@@ -3495,10 +3563,10 @@ pub const LoadedUnionType = struct {
return self.flagsUnordered(ip).status.haveLayout();
}
pub fn setHaveLayout(u: LoadedUnionType, ip: *InternPool, size: u32, padding: u32, alignment: Alignment) void {
pub fn setHaveLayout(u: LoadedUnionType, ip: *InternPool, io: Io, size: u32, padding: u32, alignment: Alignment) void {
const extra_mutex = &ip.getLocal(u.tid).mutate.extra.mutex;
extra_mutex.lock();
defer extra_mutex.unlock();
extra_mutex.lockUncancelable(io);
defer extra_mutex.unlock(io);
@atomicStore(u32, u.sizePtr(ip), size, .unordered);
@atomicStore(u32, u.paddingPtr(ip), padding, .unordered);
@@ -3767,10 +3835,10 @@ pub const LoadedStructType = struct {
return s.flagsUnordered(ip).requires_comptime;
}
pub fn setRequiresComptimeWip(s: LoadedStructType, ip: *InternPool) RequiresComptime {
pub fn setRequiresComptimeWip(s: LoadedStructType, ip: *InternPool, io: Io) RequiresComptime {
const extra_mutex = &ip.getLocal(s.tid).mutate.extra.mutex;
extra_mutex.lock();
defer extra_mutex.unlock();
extra_mutex.lockUncancelable(io);
defer extra_mutex.unlock(io);
const flags_ptr = s.flagsPtr(ip);
var flags = flags_ptr.*;
@@ -3781,12 +3849,12 @@ pub const LoadedStructType = struct {
return flags.requires_comptime;
}
pub fn setRequiresComptime(s: LoadedStructType, ip: *InternPool, requires_comptime: RequiresComptime) void {
pub fn setRequiresComptime(s: LoadedStructType, ip: *InternPool, io: Io, requires_comptime: RequiresComptime) void {
assert(requires_comptime != .wip); // see setRequiresComptimeWip
const extra_mutex = &ip.getLocal(s.tid).mutate.extra.mutex;
extra_mutex.lock();
defer extra_mutex.unlock();
extra_mutex.lockUncancelable(io);
defer extra_mutex.unlock(io);
const flags_ptr = s.flagsPtr(ip);
var flags = flags_ptr.*;
@@ -3794,12 +3862,12 @@ pub const LoadedStructType = struct {
@atomicStore(Tag.TypeStruct.Flags, flags_ptr, flags, .release);
}
pub fn assumeRuntimeBitsIfFieldTypesWip(s: LoadedStructType, ip: *InternPool) bool {
pub fn assumeRuntimeBitsIfFieldTypesWip(s: LoadedStructType, ip: *InternPool, io: Io) bool {
if (s.layout == .@"packed") return false;
const extra_mutex = &ip.getLocal(s.tid).mutate.extra.mutex;
extra_mutex.lock();
defer extra_mutex.unlock();
extra_mutex.lockUncancelable(io);
defer extra_mutex.unlock(io);
const flags_ptr = s.flagsPtr(ip);
var flags = flags_ptr.*;
@@ -3810,12 +3878,12 @@ pub const LoadedStructType = struct {
return flags.field_types_wip;
}
pub fn setFieldTypesWip(s: LoadedStructType, ip: *InternPool) bool {
pub fn setFieldTypesWip(s: LoadedStructType, ip: *InternPool, io: Io) bool {
if (s.layout == .@"packed") return false;
const extra_mutex = &ip.getLocal(s.tid).mutate.extra.mutex;
extra_mutex.lock();
defer extra_mutex.unlock();
extra_mutex.lockUncancelable(io);
defer extra_mutex.unlock(io);
const flags_ptr = s.flagsPtr(ip);
var flags = flags_ptr.*;
@@ -3826,12 +3894,12 @@ pub const LoadedStructType = struct {
return flags.field_types_wip;
}
pub fn clearFieldTypesWip(s: LoadedStructType, ip: *InternPool) void {
pub fn clearFieldTypesWip(s: LoadedStructType, ip: *InternPool, io: Io) void {
if (s.layout == .@"packed") return;
const extra_mutex = &ip.getLocal(s.tid).mutate.extra.mutex;
extra_mutex.lock();
defer extra_mutex.unlock();
extra_mutex.lockUncancelable(io);
defer extra_mutex.unlock(io);
const flags_ptr = s.flagsPtr(ip);
var flags = flags_ptr.*;
@@ -3839,12 +3907,12 @@ pub const LoadedStructType = struct {
@atomicStore(Tag.TypeStruct.Flags, flags_ptr, flags, .release);
}
pub fn setLayoutWip(s: LoadedStructType, ip: *InternPool) bool {
pub fn setLayoutWip(s: LoadedStructType, ip: *InternPool, io: Io) bool {
if (s.layout == .@"packed") return false;
const extra_mutex = &ip.getLocal(s.tid).mutate.extra.mutex;
extra_mutex.lock();
defer extra_mutex.unlock();
extra_mutex.lockUncancelable(io);
defer extra_mutex.unlock(io);
const flags_ptr = s.flagsPtr(ip);
var flags = flags_ptr.*;
@@ -3855,12 +3923,12 @@ pub const LoadedStructType = struct {
return flags.layout_wip;
}
pub fn clearLayoutWip(s: LoadedStructType, ip: *InternPool) void {
pub fn clearLayoutWip(s: LoadedStructType, ip: *InternPool, io: Io) void {
if (s.layout == .@"packed") return;
const extra_mutex = &ip.getLocal(s.tid).mutate.extra.mutex;
extra_mutex.lock();
defer extra_mutex.unlock();
extra_mutex.lockUncancelable(io);
defer extra_mutex.unlock(io);
const flags_ptr = s.flagsPtr(ip);
var flags = flags_ptr.*;
@@ -3868,10 +3936,10 @@ pub const LoadedStructType = struct {
@atomicStore(Tag.TypeStruct.Flags, flags_ptr, flags, .release);
}
pub fn setAlignment(s: LoadedStructType, ip: *InternPool, alignment: Alignment) void {
pub fn setAlignment(s: LoadedStructType, ip: *InternPool, io: Io, alignment: Alignment) void {
const extra_mutex = &ip.getLocal(s.tid).mutate.extra.mutex;
extra_mutex.lock();
defer extra_mutex.unlock();
extra_mutex.lockUncancelable(io);
defer extra_mutex.unlock(io);
const flags_ptr = s.flagsPtr(ip);
var flags = flags_ptr.*;
@@ -3879,10 +3947,10 @@ pub const LoadedStructType = struct {
@atomicStore(Tag.TypeStruct.Flags, flags_ptr, flags, .release);
}
pub fn assumePointerAlignedIfFieldTypesWip(s: LoadedStructType, ip: *InternPool, ptr_align: Alignment) bool {
pub fn assumePointerAlignedIfFieldTypesWip(s: LoadedStructType, ip: *InternPool, io: Io, ptr_align: Alignment) bool {
const extra_mutex = &ip.getLocal(s.tid).mutate.extra.mutex;
extra_mutex.lock();
defer extra_mutex.unlock();
extra_mutex.lockUncancelable(io);
defer extra_mutex.unlock(io);
const flags_ptr = s.flagsPtr(ip);
var flags = flags_ptr.*;
@@ -3894,10 +3962,10 @@ pub const LoadedStructType = struct {
return flags.field_types_wip;
}
pub fn assumePointerAlignedIfWip(s: LoadedStructType, ip: *InternPool, ptr_align: Alignment) bool {
pub fn assumePointerAlignedIfWip(s: LoadedStructType, ip: *InternPool, io: Io, ptr_align: Alignment) bool {
const extra_mutex = &ip.getLocal(s.tid).mutate.extra.mutex;
extra_mutex.lock();
defer extra_mutex.unlock();
extra_mutex.lockUncancelable(io);
defer extra_mutex.unlock(io);
const flags_ptr = s.flagsPtr(ip);
var flags = flags_ptr.*;
@@ -3911,12 +3979,12 @@ pub const LoadedStructType = struct {
return flags.alignment_wip;
}
pub fn clearAlignmentWip(s: LoadedStructType, ip: *InternPool) void {
pub fn clearAlignmentWip(s: LoadedStructType, ip: *InternPool, io: Io) void {
if (s.layout == .@"packed") return;
const extra_mutex = &ip.getLocal(s.tid).mutate.extra.mutex;
extra_mutex.lock();
defer extra_mutex.unlock();
extra_mutex.lockUncancelable(io);
defer extra_mutex.unlock(io);
const flags_ptr = s.flagsPtr(ip);
var flags = flags_ptr.*;
@@ -3924,10 +3992,10 @@ pub const LoadedStructType = struct {
@atomicStore(Tag.TypeStruct.Flags, flags_ptr, flags, .release);
}
pub fn setInitsWip(s: LoadedStructType, ip: *InternPool) bool {
pub fn setInitsWip(s: LoadedStructType, ip: *InternPool, io: Io) bool {
const extra_mutex = &ip.getLocal(s.tid).mutate.extra.mutex;
extra_mutex.lock();
defer extra_mutex.unlock();
extra_mutex.lockUncancelable(io);
defer extra_mutex.unlock(io);
switch (s.layout) {
.@"packed" => {
@@ -3951,10 +4019,10 @@ pub const LoadedStructType = struct {
}
}
pub fn clearInitsWip(s: LoadedStructType, ip: *InternPool) void {
pub fn clearInitsWip(s: LoadedStructType, ip: *InternPool, io: Io) void {
const extra_mutex = &ip.getLocal(s.tid).mutate.extra.mutex;
extra_mutex.lock();
defer extra_mutex.unlock();
extra_mutex.lockUncancelable(io);
defer extra_mutex.unlock(io);
switch (s.layout) {
.@"packed" => {
@@ -3972,12 +4040,12 @@ pub const LoadedStructType = struct {
}
}
pub fn setFullyResolved(s: LoadedStructType, ip: *InternPool) bool {
pub fn setFullyResolved(s: LoadedStructType, ip: *InternPool, io: Io) bool {
if (s.layout == .@"packed") return true;
const extra_mutex = &ip.getLocal(s.tid).mutate.extra.mutex;
extra_mutex.lock();
defer extra_mutex.unlock();
extra_mutex.lockUncancelable(io);
defer extra_mutex.unlock(io);
const flags_ptr = s.flagsPtr(ip);
var flags = flags_ptr.*;
@@ -3988,10 +4056,10 @@ pub const LoadedStructType = struct {
return flags.fully_resolved;
}
pub fn clearFullyResolved(s: LoadedStructType, ip: *InternPool) void {
pub fn clearFullyResolved(s: LoadedStructType, ip: *InternPool, io: Io) void {
const extra_mutex = &ip.getLocal(s.tid).mutate.extra.mutex;
extra_mutex.lock();
defer extra_mutex.unlock();
extra_mutex.lockUncancelable(io);
defer extra_mutex.unlock(io);
const flags_ptr = s.flagsPtr(ip);
var flags = flags_ptr.*;
@@ -4027,10 +4095,10 @@ pub const LoadedStructType = struct {
return @atomicLoad(Index, s.backingIntTypePtr(ip), .unordered);
}
pub fn setBackingIntType(s: LoadedStructType, ip: *InternPool, backing_int_ty: Index) void {
pub fn setBackingIntType(s: LoadedStructType, ip: *InternPool, io: Io, backing_int_ty: Index) void {
const extra_mutex = &ip.getLocal(s.tid).mutate.extra.mutex;
extra_mutex.lock();
defer extra_mutex.unlock();
extra_mutex.lockUncancelable(io);
defer extra_mutex.unlock(io);
@atomicStore(Index, s.backingIntTypePtr(ip), backing_int_ty, .release);
}
@@ -4054,10 +4122,10 @@ pub const LoadedStructType = struct {
};
}
pub fn setHaveFieldInits(s: LoadedStructType, ip: *InternPool) void {
pub fn setHaveFieldInits(s: LoadedStructType, ip: *InternPool, io: Io) void {
const extra_mutex = &ip.getLocal(s.tid).mutate.extra.mutex;
extra_mutex.lock();
defer extra_mutex.unlock();
extra_mutex.lockUncancelable(io);
defer extra_mutex.unlock(io);
switch (s.layout) {
.@"packed" => {
@@ -4082,10 +4150,10 @@ pub const LoadedStructType = struct {
};
}
pub fn setLayoutResolved(s: LoadedStructType, ip: *InternPool, size: u32, alignment: Alignment) void {
pub fn setLayoutResolved(s: LoadedStructType, ip: *InternPool, io: Io, size: u32, alignment: Alignment) void {
const extra_mutex = &ip.getLocal(s.tid).mutate.extra.mutex;
extra_mutex.lock();
defer extra_mutex.unlock();
extra_mutex.lockUncancelable(io);
defer extra_mutex.unlock(io);
@atomicStore(u32, s.sizePtr(ip), size, .unordered);
const flags_ptr = s.flagsPtr(ip);
@@ -6826,8 +6894,8 @@ pub const MemoizedCall = struct {
branch_count: u32,
};
pub fn init(ip: *InternPool, gpa: Allocator, available_threads: usize) !void {
errdefer ip.deinit(gpa);
pub fn init(ip: *InternPool, gpa: Allocator, io: Io, available_threads: usize) !void {
errdefer ip.deinit(gpa, io);
assert(ip.locals.len == 0 and ip.shards.len == 0);
assert(available_threads > 0 and available_threads <= std.math.maxInt(u8));
@@ -6865,7 +6933,7 @@ pub fn init(ip: *InternPool, gpa: Allocator, available_threads: usize) !void {
.namespaces = .empty,
},
});
for (ip.locals) |*local| try local.getMutableStrings(gpa).append(.{0});
for (ip.locals) |*local| try local.getMutableStrings(gpa, io).append(.{0});
ip.tid_width = @intCast(std.math.log2_int_ceil(usize, used_threads));
ip.tid_shift_30 = if (single_threaded) 0 else 30 - ip.tid_width;
@@ -6874,28 +6942,28 @@ pub fn init(ip: *InternPool, gpa: Allocator, available_threads: usize) !void {
ip.shards = try gpa.alloc(Shard, @as(usize, 1) << ip.tid_width);
@memset(ip.shards, .{
.shared = .{
.map = Shard.Map(Index).empty,
.string_map = Shard.Map(OptionalNullTerminatedString).empty,
.tracked_inst_map = Shard.Map(TrackedInst.Index.Optional).empty,
.map = .empty,
.string_map = .empty,
.tracked_inst_map = .empty,
},
.mutate = .{
.map = Shard.Mutate.empty,
.string_map = Shard.Mutate.empty,
.tracked_inst_map = Shard.Mutate.empty,
.map = .empty,
.string_map = .empty,
.tracked_inst_map = .empty,
},
});
// Reserve string index 0 for an empty string.
assert((try ip.getOrPutString(gpa, .main, "", .no_embedded_nulls)) == .empty);
assert((try ip.getOrPutString(gpa, io, .main, "", .no_embedded_nulls)) == .empty);
// This inserts all the statically-known values into the intern pool in the
// order expected.
for (&static_keys, 0..) |key, key_index| switch (@as(Index, @enumFromInt(key_index))) {
.empty_tuple_type => assert(try ip.getTupleType(gpa, .main, .{
.empty_tuple_type => assert(try ip.getTupleType(gpa, io, .main, .{
.types = &.{},
.values = &.{},
}) == .empty_tuple_type),
else => |expected_index| assert(try ip.get(gpa, .main, key) == expected_index),
else => |expected_index| assert(try ip.get(gpa, io, .main, key) == expected_index),
};
if (std.debug.runtime_safety) {
@@ -6905,7 +6973,7 @@ pub fn init(ip: *InternPool, gpa: Allocator, available_threads: usize) !void {
}
}
pub fn deinit(ip: *InternPool, gpa: Allocator) void {
pub fn deinit(ip: *InternPool, gpa: Allocator, io: Io) void {
if (debug_state.enable_checks) std.debug.assert(debug_state.intern_pool == null);
ip.src_hash_deps.deinit(gpa);
@@ -6940,7 +7008,7 @@ pub fn deinit(ip: *InternPool, gpa: Allocator) void {
namespace.test_decls.deinit(gpa);
}
};
const maps = local.getMutableMaps(gpa);
const maps = local.getMutableMaps(gpa, io);
if (maps.mutate.len > 0) for (maps.view().items(.@"0")) |*map| map.deinit(gpa);
local.mutate.arena.promote(gpa).deinit();
}
@@ -7645,6 +7713,7 @@ const GetOrPutKey = union(enum) {
new: struct {
ip: *InternPool,
tid: Zcu.PerThread.Id,
io: Io,
shard: *Shard,
map_index: u32,
},
@@ -7679,7 +7748,7 @@ const GetOrPutKey = union(enum) {
.new => |info| {
assert(info.shard.shared.map.entries[info.map_index].value == index);
info.shard.mutate.map.len += 1;
info.shard.mutate.map.mutex.unlock();
info.shard.mutate.map.mutex.unlock(info.io);
gop.* = .{ .existing = index };
},
}
@@ -7688,7 +7757,7 @@ const GetOrPutKey = union(enum) {
fn cancel(gop: *GetOrPutKey) void {
switch (gop.*) {
.existing => {},
.new => |info| info.shard.mutate.map.mutex.unlock(),
.new => |info| info.shard.mutate.map.mutex.unlock(info.io),
}
gop.* = .{ .existing = undefined };
}
@@ -7705,14 +7774,16 @@ const GetOrPutKey = union(enum) {
fn getOrPutKey(
ip: *InternPool,
gpa: Allocator,
io: Io,
tid: Zcu.PerThread.Id,
key: Key,
) Allocator.Error!GetOrPutKey {
return ip.getOrPutKeyEnsuringAdditionalCapacity(gpa, tid, key, 0);
return ip.getOrPutKeyEnsuringAdditionalCapacity(gpa, io, tid, key, 0);
}
fn getOrPutKeyEnsuringAdditionalCapacity(
ip: *InternPool,
gpa: Allocator,
io: Io,
tid: Zcu.PerThread.Id,
key: Key,
additional_capacity: u32,
@@ -7733,8 +7804,8 @@ fn getOrPutKeyEnsuringAdditionalCapacity(
if (index.unwrap(ip).getTag(ip) == .removed) continue;
if (ip.indexToKey(index).eql(key, ip)) return .{ .existing = index };
}
shard.mutate.map.mutex.lock();
errdefer shard.mutate.map.mutex.unlock();
shard.mutate.map.mutex.lock(io, tid);
errdefer shard.mutate.map.mutex.unlock(io);
if (map.entries != shard.shared.map.entries) {
map = shard.shared.map;
map_mask = map.header().mask();
@@ -7747,7 +7818,7 @@ fn getOrPutKeyEnsuringAdditionalCapacity(
if (index == .none) break;
if (entry.hash != hash) continue;
if (ip.indexToKey(index).eql(key, ip)) {
defer shard.mutate.map.mutex.unlock();
defer shard.mutate.map.mutex.unlock(io);
return .{ .existing = index };
}
}
@@ -7801,6 +7872,7 @@ fn getOrPutKeyEnsuringAdditionalCapacity(
return .{ .new = .{
.ip = ip,
.tid = tid,
.io = io,
.shard = shard,
.map_index = map_index,
} };
@@ -7815,14 +7887,15 @@ fn getOrPutKeyEnsuringAdditionalCapacity(
/// will be cleaned up when the `Zcu` undergoes garbage collection.
fn putKeyReplace(
ip: *InternPool,
io: Io,
tid: Zcu.PerThread.Id,
key: Key,
) GetOrPutKey {
const full_hash = key.hash64(ip);
const hash: u32 = @truncate(full_hash >> 32);
const shard = &ip.shards[@intCast(full_hash & (ip.shards.len - 1))];
shard.mutate.map.mutex.lock();
errdefer shard.mutate.map.mutex.unlock();
shard.mutate.map.mutex.lock(io, tid);
errdefer shard.mutate.map.mutex.unlock(io);
const map = shard.shared.map;
const map_mask = map.header().mask();
var map_index = hash;
@@ -7838,18 +7911,19 @@ fn putKeyReplace(
return .{ .new = .{
.ip = ip,
.tid = tid,
.io = io,
.shard = shard,
.map_index = map_index,
} };
}
pub fn get(ip: *InternPool, gpa: Allocator, tid: Zcu.PerThread.Id, key: Key) Allocator.Error!Index {
var gop = try ip.getOrPutKey(gpa, tid, key);
pub fn get(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.PerThread.Id, key: Key) Allocator.Error!Index {
var gop = try ip.getOrPutKey(gpa, io, tid, key);
defer gop.deinit();
if (gop == .existing) return gop.existing;
const local = ip.getLocal(tid);
const items = local.getMutableItems(gpa);
const extra = local.getMutableExtra(gpa);
const items = local.getMutableItems(gpa, io);
const extra = local.getMutableExtra(gpa, io);
try items.ensureUnusedCapacity(1);
switch (key) {
.int_type => |int_type| {
@@ -7870,8 +7944,8 @@ pub fn get(ip: *InternPool, gpa: Allocator, tid: Zcu.PerThread.Id, key: Key) All
gop.cancel();
var new_key = key;
new_key.ptr_type.flags.size = .many;
const ptr_type_index = try ip.get(gpa, tid, new_key);
gop = try ip.getOrPutKey(gpa, tid, key);
const ptr_type_index = try ip.get(gpa, io, tid, new_key);
gop = try ip.getOrPutKey(gpa, io, tid, key);
try items.ensureUnusedCapacity(1);
items.appendAssumeCapacity(.{
@@ -7953,7 +8027,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, tid: Zcu.PerThread.Id, key: Key) All
assert(error_set_type.names_map == .none);
assert(std.sort.isSorted(NullTerminatedString, error_set_type.names.get(ip), {}, NullTerminatedString.indexLessThan));
const names = error_set_type.names.get(ip);
const names_map = try ip.addMap(gpa, tid, names.len);
const names_map = try ip.addMap(gpa, io, tid, names.len);
ip.addStringsToMap(names_map, names);
const names_len = error_set_type.names.len;
try extra.ensureUnusedCapacity(@typeInfo(Tag.ErrorSet).@"struct".fields.len + names_len);
@@ -8051,7 +8125,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, tid: Zcu.PerThread.Id, key: Key) All
gop.cancel();
var new_key = key;
new_key.ptr.base_addr.uav.orig_ty = ptr.ty;
gop = try ip.getOrPutKey(gpa, tid, new_key);
gop = try ip.getOrPutKey(gpa, io, tid, new_key);
if (gop == .existing) return gop.existing;
}
break :item .{
@@ -8123,11 +8197,11 @@ pub fn get(ip: *InternPool, gpa: Allocator, tid: Zcu.PerThread.Id, key: Key) All
else => unreachable,
}
gop.cancel();
const index_index = try ip.get(gpa, tid, .{ .int = .{
const index_index = try ip.get(gpa, io, tid, .{ .int = .{
.ty = .usize_type,
.storage = .{ .u64 = base_index.index },
} });
gop = try ip.getOrPutKey(gpa, tid, key);
gop = try ip.getOrPutKey(gpa, io, tid, key);
try items.ensureUnusedCapacity(1);
items.appendAssumeCapacity(.{
.tag = switch (ptr.base_addr) {
@@ -8318,7 +8392,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, tid: Zcu.PerThread.Id, key: Key) All
} else |_| {}
const tag: Tag = if (big_int.positive) .int_positive else .int_negative;
try addInt(ip, gpa, tid, int.ty, tag, big_int.limbs);
try addInt(ip, gpa, io, tid, int.ty, tag, big_int.limbs);
},
inline .u64, .i64 => |x| {
if (std.math.cast(u32, x)) |casted| {
@@ -8335,7 +8409,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, tid: Zcu.PerThread.Id, key: Key) All
var buf: [2]Limb = undefined;
const big_int = BigIntMutable.init(&buf, x).toConst();
const tag: Tag = if (big_int.positive) .int_positive else .int_negative;
try addInt(ip, gpa, tid, int.ty, tag, big_int.limbs);
try addInt(ip, gpa, io, tid, int.ty, tag, big_int.limbs);
},
.lazy_align, .lazy_size => unreachable,
}
@@ -8546,11 +8620,11 @@ pub fn get(ip: *InternPool, gpa: Allocator, tid: Zcu.PerThread.Id, key: Key) All
const elem = switch (aggregate.storage) {
.bytes => |bytes| elem: {
gop.cancel();
const elem = try ip.get(gpa, tid, .{ .int = .{
const elem = try ip.get(gpa, io, tid, .{ .int = .{
.ty = .u8_type,
.storage = .{ .u64 = bytes.at(0, ip) },
} });
gop = try ip.getOrPutKey(gpa, tid, key);
gop = try ip.getOrPutKey(gpa, io, tid, key);
try items.ensureUnusedCapacity(1);
break :elem elem;
},
@@ -8570,7 +8644,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, tid: Zcu.PerThread.Id, key: Key) All
}
if (child == .u8_type) bytes: {
const string_bytes = ip.getLocal(tid).getMutableStringBytes(gpa);
const string_bytes = ip.getLocal(tid).getMutableStringBytes(gpa, io);
const start = string_bytes.mutate.len;
try string_bytes.ensureUnusedCapacity(@intCast(len_including_sentinel + 1));
try extra.ensureUnusedCapacity(@typeInfo(Bytes).@"struct".fields.len);
@@ -8598,6 +8672,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, tid: Zcu.PerThread.Id, key: Key) All
});
const string = try ip.getOrPutTrailingString(
gpa,
io,
tid,
@intCast(len_including_sentinel),
.maybe_embedded_nulls,
@@ -8647,15 +8722,16 @@ pub fn get(ip: *InternPool, gpa: Allocator, tid: Zcu.PerThread.Id, key: Key) All
pub fn getUnion(
ip: *InternPool,
gpa: Allocator,
io: Io,
tid: Zcu.PerThread.Id,
un: Key.Union,
) Allocator.Error!Index {
var gop = try ip.getOrPutKey(gpa, tid, .{ .un = un });
var gop = try ip.getOrPutKey(gpa, io, tid, .{ .un = un });
defer gop.deinit();
if (gop == .existing) return gop.existing;
const local = ip.getLocal(tid);
const items = local.getMutableItems(gpa);
const extra = local.getMutableExtra(gpa);
const items = local.getMutableItems(gpa, io);
const extra = local.getMutableExtra(gpa, io);
try items.ensureUnusedCapacity(1);
assert(un.ty != .none);
@@ -8706,6 +8782,7 @@ pub const UnionTypeInit = struct {
pub fn getUnionType(
ip: *InternPool,
gpa: Allocator,
io: Io,
tid: Zcu.PerThread.Id,
ini: UnionTypeInit,
/// If it is known that there is an existing type with this key which is outdated,
@@ -8727,16 +8804,16 @@ pub fn getUnionType(
} },
} };
var gop = if (replace_existing)
ip.putKeyReplace(tid, key)
ip.putKeyReplace(io, tid, key)
else
try ip.getOrPutKey(gpa, tid, key);
try ip.getOrPutKey(gpa, io, tid, key);
defer gop.deinit();
if (gop == .existing) return .{ .existing = gop.existing };
const local = ip.getLocal(tid);
const items = local.getMutableItems(gpa);
const items = local.getMutableItems(gpa, io);
try items.ensureUnusedCapacity(1);
const extra = local.getMutableExtra(gpa);
const extra = local.getMutableExtra(gpa, io);
const align_elements_len = if (ini.flags.any_aligned_fields) (ini.fields_len + 3) / 4 else 0;
const align_element: u32 = @bitCast([1]u8{@intFromEnum(Alignment.none)} ** 4);
@@ -8903,6 +8980,7 @@ pub const StructTypeInit = struct {
pub fn getStructType(
ip: *InternPool,
gpa: Allocator,
io: Io,
tid: Zcu.PerThread.Id,
ini: StructTypeInit,
/// If it is known that there is an existing type with this key which is outdated,
@@ -8924,17 +9002,17 @@ pub fn getStructType(
} },
} };
var gop = if (replace_existing)
ip.putKeyReplace(tid, key)
ip.putKeyReplace(io, tid, key)
else
try ip.getOrPutKey(gpa, tid, key);
try ip.getOrPutKey(gpa, io, tid, key);
defer gop.deinit();
if (gop == .existing) return .{ .existing = gop.existing };
const local = ip.getLocal(tid);
const items = local.getMutableItems(gpa);
const extra = local.getMutableExtra(gpa);
const items = local.getMutableItems(gpa, io);
const extra = local.getMutableExtra(gpa, io);
const names_map = try ip.addMap(gpa, tid, ini.fields_len);
const names_map = try ip.addMap(gpa, io, tid, ini.fields_len);
errdefer local.mutate.maps.len -= 1;
const zir_index = switch (ini.key) {
@@ -9109,6 +9187,7 @@ pub const TupleTypeInit = struct {
pub fn getTupleType(
ip: *InternPool,
gpa: Allocator,
io: Io,
tid: Zcu.PerThread.Id,
ini: TupleTypeInit,
) Allocator.Error!Index {
@@ -9116,8 +9195,8 @@ pub fn getTupleType(
for (ini.types) |elem| assert(elem != .none);
const local = ip.getLocal(tid);
const items = local.getMutableItems(gpa);
const extra = local.getMutableExtra(gpa);
const items = local.getMutableItems(gpa, io);
const extra = local.getMutableExtra(gpa, io);
const prev_extra_len = extra.mutate.len;
const fields_len: u32 = @intCast(ini.types.len);
@@ -9134,7 +9213,7 @@ pub fn getTupleType(
extra.appendSliceAssumeCapacity(.{@ptrCast(ini.values)});
errdefer extra.mutate.len = prev_extra_len;
var gop = try ip.getOrPutKey(gpa, tid, .{ .tuple_type = extraTypeTuple(tid, extra.list.*, extra_index) });
var gop = try ip.getOrPutKey(gpa, io, tid, .{ .tuple_type = extraTypeTuple(tid, extra.list.*, extra_index) });
defer gop.deinit();
if (gop == .existing) {
extra.mutate.len = prev_extra_len;
@@ -9166,6 +9245,7 @@ pub const GetFuncTypeKey = struct {
pub fn getFuncType(
ip: *InternPool,
gpa: Allocator,
io: Io,
tid: Zcu.PerThread.Id,
key: GetFuncTypeKey,
) Allocator.Error!Index {
@@ -9174,9 +9254,9 @@ pub fn getFuncType(
for (key.param_types) |param_type| assert(param_type != .none);
const local = ip.getLocal(tid);
const items = local.getMutableItems(gpa);
const items = local.getMutableItems(gpa, io);
try items.ensureUnusedCapacity(1);
const extra = local.getMutableExtra(gpa);
const extra = local.getMutableExtra(gpa, io);
// The strategy here is to add the function type unconditionally, then to
// ask if it already exists, and if so, revert the lengths of the mutated
@@ -9207,7 +9287,7 @@ pub fn getFuncType(
extra.appendSliceAssumeCapacity(.{@ptrCast(key.param_types)});
errdefer extra.mutate.len = prev_extra_len;
var gop = try ip.getOrPutKey(gpa, tid, .{
var gop = try ip.getOrPutKey(gpa, io, tid, .{
.func_type = extraFuncType(tid, extra.list.*, func_type_extra_index),
});
defer gop.deinit();
@@ -9228,6 +9308,7 @@ pub fn getFuncType(
pub fn getExtern(
ip: *InternPool,
gpa: Allocator,
io: Io,
tid: Zcu.PerThread.Id,
/// `key.owner_nav` is ignored.
key: Key.Extern,
@@ -9236,7 +9317,7 @@ pub fn getExtern(
/// Only set if the `Nav` was newly created.
new_nav: Nav.Index.Optional,
} {
var gop = try ip.getOrPutKey(gpa, tid, .{ .@"extern" = key });
var gop = try ip.getOrPutKey(gpa, io, tid, .{ .@"extern" = key });
defer gop.deinit();
if (gop == .existing) return .{
.index = gop.existing,
@@ -9244,18 +9325,18 @@ pub fn getExtern(
};
const local = ip.getLocal(tid);
const items = local.getMutableItems(gpa);
const extra = local.getMutableExtra(gpa);
const items = local.getMutableItems(gpa, io);
const extra = local.getMutableExtra(gpa, io);
try items.ensureUnusedCapacity(1);
try extra.ensureUnusedCapacity(@typeInfo(Tag.Extern).@"struct".fields.len);
try local.getMutableNavs(gpa).ensureUnusedCapacity(1);
try local.getMutableNavs(gpa, io).ensureUnusedCapacity(1);
// Predict the index the `@"extern" will live at, so we can construct the owner `Nav` before releasing the shard's mutex.
const extern_index = Index.Unwrapped.wrap(.{
.tid = tid,
.index = items.mutate.len,
}, ip);
const owner_nav = ip.createNav(gpa, tid, .{
const owner_nav = ip.createNav(gpa, io, tid, .{
.name = key.name,
.fqn = key.name,
.val = extern_index,
@@ -9305,13 +9386,14 @@ pub const GetFuncDeclKey = struct {
pub fn getFuncDecl(
ip: *InternPool,
gpa: Allocator,
io: Io,
tid: Zcu.PerThread.Id,
key: GetFuncDeclKey,
) Allocator.Error!Index {
const local = ip.getLocal(tid);
const items = local.getMutableItems(gpa);
const items = local.getMutableItems(gpa, io);
try items.ensureUnusedCapacity(1);
const extra = local.getMutableExtra(gpa);
const extra = local.getMutableExtra(gpa, io);
// The strategy here is to add the function type unconditionally, then to
// ask if it already exists, and if so, revert the lengths of the mutated
@@ -9340,7 +9422,7 @@ pub fn getFuncDecl(
});
errdefer extra.mutate.len = prev_extra_len;
var gop = try ip.getOrPutKey(gpa, tid, .{
var gop = try ip.getOrPutKey(gpa, io, tid, .{
.func = extraFuncDecl(tid, extra.list.*, func_decl_extra_index),
});
defer gop.deinit();
@@ -9387,6 +9469,7 @@ pub const GetFuncDeclIesKey = struct {
pub fn getFuncDeclIes(
ip: *InternPool,
gpa: Allocator,
io: Io,
tid: Zcu.PerThread.Id,
key: GetFuncDeclIesKey,
) Allocator.Error!Index {
@@ -9395,9 +9478,9 @@ pub fn getFuncDeclIes(
for (key.param_types) |param_type| assert(param_type != .none);
const local = ip.getLocal(tid);
const items = local.getMutableItems(gpa);
const items = local.getMutableItems(gpa, io);
try items.ensureUnusedCapacity(4);
const extra = local.getMutableExtra(gpa);
const extra = local.getMutableExtra(gpa, io);
// The strategy here is to add the function decl unconditionally, then to
// ask if it already exists, and if so, revert the lengths of the mutated
@@ -9488,7 +9571,7 @@ pub fn getFuncDeclIes(
extra.mutate.len = prev_extra_len;
}
var func_gop = try ip.getOrPutKeyEnsuringAdditionalCapacity(gpa, tid, .{
var func_gop = try ip.getOrPutKeyEnsuringAdditionalCapacity(gpa, io, tid, .{
.func = extraFuncDecl(tid, extra.list.*, func_decl_extra_index),
}, 3);
defer func_gop.deinit();
@@ -9509,18 +9592,18 @@ pub fn getFuncDeclIes(
return func_gop.existing;
}
func_gop.putTentative(func_index);
var error_union_type_gop = try ip.getOrPutKeyEnsuringAdditionalCapacity(gpa, tid, .{ .error_union_type = .{
var error_union_type_gop = try ip.getOrPutKeyEnsuringAdditionalCapacity(gpa, io, tid, .{ .error_union_type = .{
.error_set_type = error_set_type,
.payload_type = key.bare_return_type,
} }, 2);
defer error_union_type_gop.deinit();
error_union_type_gop.putTentative(error_union_type);
var error_set_type_gop = try ip.getOrPutKeyEnsuringAdditionalCapacity(gpa, tid, .{
var error_set_type_gop = try ip.getOrPutKeyEnsuringAdditionalCapacity(gpa, io, tid, .{
.inferred_error_set_type = func_index,
}, 1);
defer error_set_type_gop.deinit();
error_set_type_gop.putTentative(error_set_type);
var func_ty_gop = try ip.getOrPutKey(gpa, tid, .{
var func_ty_gop = try ip.getOrPutKey(gpa, io, tid, .{
.func_type = extraFuncType(tid, extra.list.*, func_type_extra_index),
});
defer func_ty_gop.deinit();
@@ -9536,17 +9619,18 @@ pub fn getFuncDeclIes(
pub fn getErrorSetType(
ip: *InternPool,
gpa: Allocator,
io: Io,
tid: Zcu.PerThread.Id,
names: []const NullTerminatedString,
) Allocator.Error!Index {
assert(std.sort.isSorted(NullTerminatedString, names, {}, NullTerminatedString.indexLessThan));
const local = ip.getLocal(tid);
const items = local.getMutableItems(gpa);
const extra = local.getMutableExtra(gpa);
const items = local.getMutableItems(gpa, io);
const extra = local.getMutableExtra(gpa, io);
try extra.ensureUnusedCapacity(@typeInfo(Tag.ErrorSet).@"struct".fields.len + names.len);
const names_map = try ip.addMap(gpa, tid, names.len);
const names_map = try ip.addMap(gpa, io, tid, names.len);
errdefer local.mutate.maps.len -= 1;
// The strategy here is to add the type unconditionally, then to ask if it
@@ -9562,7 +9646,7 @@ pub fn getErrorSetType(
extra.appendSliceAssumeCapacity(.{@ptrCast(names)});
errdefer extra.mutate.len = prev_extra_len;
var gop = try ip.getOrPutKey(gpa, tid, .{
var gop = try ip.getOrPutKey(gpa, io, tid, .{
.error_set_type = extraErrorSet(tid, extra.list.*, error_set_extra_index),
});
defer gop.deinit();
@@ -9599,16 +9683,17 @@ pub const GetFuncInstanceKey = struct {
pub fn getFuncInstance(
ip: *InternPool,
gpa: Allocator,
io: Io,
tid: Zcu.PerThread.Id,
arg: GetFuncInstanceKey,
) Allocator.Error!Index {
if (arg.inferred_error_set)
return getFuncInstanceIes(ip, gpa, tid, arg);
return getFuncInstanceIes(ip, gpa, io, tid, arg);
const generic_owner = unwrapCoercedFunc(ip, arg.generic_owner);
const generic_owner_ty = ip.indexToKey(ip.funcDeclInfo(generic_owner).ty).func_type;
const func_ty = try ip.getFuncType(gpa, tid, .{
const func_ty = try ip.getFuncType(gpa, io, tid, .{
.param_types = arg.param_types,
.return_type = arg.bare_return_type,
.noalias_bits = arg.noalias_bits,
@@ -9617,8 +9702,8 @@ pub fn getFuncInstance(
});
const local = ip.getLocal(tid);
const items = local.getMutableItems(gpa);
const extra = local.getMutableExtra(gpa);
const items = local.getMutableItems(gpa, io);
const extra = local.getMutableExtra(gpa, io);
try extra.ensureUnusedCapacity(@typeInfo(Tag.FuncInstance).@"struct".fields.len +
arg.comptime_args.len);
@@ -9646,7 +9731,7 @@ pub fn getFuncInstance(
});
extra.appendSliceAssumeCapacity(.{@ptrCast(arg.comptime_args)});
var gop = try ip.getOrPutKey(gpa, tid, .{
var gop = try ip.getOrPutKey(gpa, io, tid, .{
.func = ip.extraFuncInstance(tid, extra.list.*, func_extra_index),
});
defer gop.deinit();
@@ -9664,6 +9749,7 @@ pub fn getFuncInstance(
try finishFuncInstance(
ip,
gpa,
io,
tid,
extra,
generic_owner,
@@ -9676,9 +9762,10 @@ pub fn getFuncInstance(
/// This function exists separately than `getFuncInstance` because it needs to
/// create 4 new items in the InternPool atomically before it can look for an
/// existing item in the map.
pub fn getFuncInstanceIes(
fn getFuncInstanceIes(
ip: *InternPool,
gpa: Allocator,
io: Io,
tid: Zcu.PerThread.Id,
arg: GetFuncInstanceKey,
) Allocator.Error!Index {
@@ -9688,8 +9775,8 @@ pub fn getFuncInstanceIes(
for (arg.param_types) |param_type| assert(param_type != .none);
const local = ip.getLocal(tid);
const items = local.getMutableItems(gpa);
const extra = local.getMutableExtra(gpa);
const items = local.getMutableItems(gpa, io);
const extra = local.getMutableExtra(gpa, io);
try items.ensureUnusedCapacity(4);
const generic_owner = unwrapCoercedFunc(ip, arg.generic_owner);
@@ -9784,7 +9871,7 @@ pub fn getFuncInstanceIes(
extra.mutate.len = prev_extra_len;
}
var func_gop = try ip.getOrPutKeyEnsuringAdditionalCapacity(gpa, tid, .{
var func_gop = try ip.getOrPutKeyEnsuringAdditionalCapacity(gpa, io, tid, .{
.func = ip.extraFuncInstance(tid, extra.list.*, func_extra_index),
}, 3);
defer func_gop.deinit();
@@ -9795,18 +9882,18 @@ pub fn getFuncInstanceIes(
return func_gop.existing;
}
func_gop.putTentative(func_index);
var error_union_type_gop = try ip.getOrPutKeyEnsuringAdditionalCapacity(gpa, tid, .{ .error_union_type = .{
var error_union_type_gop = try ip.getOrPutKeyEnsuringAdditionalCapacity(gpa, io, tid, .{ .error_union_type = .{
.error_set_type = error_set_type,
.payload_type = arg.bare_return_type,
} }, 2);
defer error_union_type_gop.deinit();
error_union_type_gop.putTentative(error_union_type);
var error_set_type_gop = try ip.getOrPutKeyEnsuringAdditionalCapacity(gpa, tid, .{
var error_set_type_gop = try ip.getOrPutKeyEnsuringAdditionalCapacity(gpa, io, tid, .{
.inferred_error_set_type = func_index,
}, 1);
defer error_set_type_gop.deinit();
error_set_type_gop.putTentative(error_set_type);
var func_ty_gop = try ip.getOrPutKey(gpa, tid, .{
var func_ty_gop = try ip.getOrPutKey(gpa, io, tid, .{
.func_type = extraFuncType(tid, extra.list.*, func_type_extra_index),
});
defer func_ty_gop.deinit();
@@ -9814,6 +9901,7 @@ pub fn getFuncInstanceIes(
try finishFuncInstance(
ip,
gpa,
io,
tid,
extra,
generic_owner,
@@ -9831,6 +9919,7 @@ pub fn getFuncInstanceIes(
fn finishFuncInstance(
ip: *InternPool,
gpa: Allocator,
io: Io,
tid: Zcu.PerThread.Id,
extra: Local.Extra.Mutable,
generic_owner: Index,
@@ -9841,12 +9930,12 @@ fn finishFuncInstance(
const fn_namespace = fn_owner_nav.analysis.?.namespace;
// TODO: improve this name
const nav_name = try ip.getOrPutStringFmt(gpa, tid, "{f}__anon_{d}", .{
const nav_name = try ip.getOrPutStringFmt(gpa, io, tid, "{f}__anon_{d}", .{
fn_owner_nav.name.fmt(ip), @intFromEnum(func_index),
}, .no_embedded_nulls);
const nav_index = try ip.createNav(gpa, tid, .{
const nav_index = try ip.createNav(gpa, io, tid, .{
.name = nav_name,
.fqn = try ip.namespacePtr(fn_namespace).internFullyQualifiedName(ip, gpa, tid, nav_name),
.fqn = try ip.namespacePtr(fn_namespace).internFullyQualifiedName(ip, gpa, io, tid, nav_name),
.val = func_index,
.is_const = fn_owner_nav.status.fully_resolved.is_const,
.alignment = fn_owner_nav.status.fully_resolved.alignment,
@@ -9967,6 +10056,7 @@ pub const WipEnumType = struct {
pub fn getEnumType(
ip: *InternPool,
gpa: Allocator,
io: Io,
tid: Zcu.PerThread.Id,
ini: EnumTypeInit,
/// If it is known that there is an existing type with this key which is outdated,
@@ -9988,18 +10078,18 @@ pub fn getEnumType(
} },
} };
var gop = if (replace_existing)
ip.putKeyReplace(tid, key)
ip.putKeyReplace(io, tid, key)
else
try ip.getOrPutKey(gpa, tid, key);
try ip.getOrPutKey(gpa, io, tid, key);
defer gop.deinit();
if (gop == .existing) return .{ .existing = gop.existing };
const local = ip.getLocal(tid);
const items = local.getMutableItems(gpa);
const items = local.getMutableItems(gpa, io);
try items.ensureUnusedCapacity(1);
const extra = local.getMutableExtra(gpa);
const extra = local.getMutableExtra(gpa, io);
const names_map = try ip.addMap(gpa, tid, ini.fields_len);
const names_map = try ip.addMap(gpa, io, tid, ini.fields_len);
errdefer local.mutate.maps.len -= 1;
switch (ini.tag_mode) {
@@ -10056,7 +10146,7 @@ pub fn getEnumType(
},
.explicit, .nonexhaustive => {
const values_map: OptionalMapIndex = if (!ini.has_values) .none else m: {
const values_map = try ip.addMap(gpa, tid, ini.fields_len);
const values_map = try ip.addMap(gpa, io, tid, ini.fields_len);
break :m values_map.toOptional();
};
errdefer if (ini.has_values) {
@@ -10141,6 +10231,7 @@ const GeneratedTagEnumTypeInit = struct {
pub fn getGeneratedTagEnumType(
ip: *InternPool,
gpa: Allocator,
io: Io,
tid: Zcu.PerThread.Id,
ini: GeneratedTagEnumTypeInit,
) Allocator.Error!Index {
@@ -10149,11 +10240,11 @@ pub fn getGeneratedTagEnumType(
for (ini.values) |val| assert(ip.typeOf(val) == ini.tag_ty);
const local = ip.getLocal(tid);
const items = local.getMutableItems(gpa);
const items = local.getMutableItems(gpa, io);
try items.ensureUnusedCapacity(1);
const extra = local.getMutableExtra(gpa);
const extra = local.getMutableExtra(gpa, io);
const names_map = try ip.addMap(gpa, tid, ini.names.len);
const names_map = try ip.addMap(gpa, io, tid, ini.names.len);
errdefer local.mutate.maps.len -= 1;
ip.addStringsToMap(names_map, ini.names);
@@ -10165,7 +10256,7 @@ pub fn getGeneratedTagEnumType(
.index = items.mutate.len,
}, ip);
const parent_namespace = ip.namespacePtr(ini.parent_namespace);
const namespace = try ip.createNamespace(gpa, tid, .{
const namespace = try ip.createNamespace(gpa, io, tid, .{
.parent = ini.parent_namespace.toOptional(),
.owner_type = enum_index,
.file_scope = parent_namespace.file_scope,
@@ -10202,7 +10293,7 @@ pub fn getGeneratedTagEnumType(
ini.values.len); // field values
const values_map: OptionalMapIndex = if (ini.values.len != 0) m: {
const map = try ip.addMap(gpa, tid, ini.values.len);
const map = try ip.addMap(gpa, io, tid, ini.values.len);
ip.addIndexesToMap(map, ini.values);
break :m map.toOptional();
} else .none;
@@ -10240,7 +10331,7 @@ pub fn getGeneratedTagEnumType(
},
};
var gop = try ip.getOrPutKey(gpa, tid, .{ .enum_type = .{
var gop = try ip.getOrPutKey(gpa, io, tid, .{ .enum_type = .{
.generated_tag = .{ .union_type = ini.owner_union_ty },
} });
defer gop.deinit();
@@ -10256,10 +10347,11 @@ pub const OpaqueTypeInit = struct {
pub fn getOpaqueType(
ip: *InternPool,
gpa: Allocator,
io: Io,
tid: Zcu.PerThread.Id,
ini: OpaqueTypeInit,
) Allocator.Error!WipNamespaceType.Result {
var gop = try ip.getOrPutKey(gpa, tid, .{ .opaque_type = .{ .declared = .{
var gop = try ip.getOrPutKey(gpa, io, tid, .{ .opaque_type = .{ .declared = .{
.zir_index = ini.zir_index,
.captures = .{ .external = ini.captures },
} } });
@@ -10267,8 +10359,8 @@ pub fn getOpaqueType(
if (gop == .existing) return .{ .existing = gop.existing };
const local = ip.getLocal(tid);
const items = local.getMutableItems(gpa);
const extra = local.getMutableExtra(gpa);
const items = local.getMutableItems(gpa, io);
const extra = local.getMutableExtra(gpa, io);
try items.ensureUnusedCapacity(1);
try extra.ensureUnusedCapacity(@typeInfo(Tag.TypeOpaque).@"struct".fields.len + ini.captures.len);
@@ -10338,8 +10430,8 @@ fn addIndexesToMap(
}
}
fn addMap(ip: *InternPool, gpa: Allocator, tid: Zcu.PerThread.Id, cap: usize) Allocator.Error!MapIndex {
const maps = ip.getLocal(tid).getMutableMaps(gpa);
fn addMap(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.PerThread.Id, cap: usize) Allocator.Error!MapIndex {
const maps = ip.getLocal(tid).getMutableMaps(gpa, io);
const unwrapped: MapIndex.Unwrapped = .{ .tid = tid, .index = maps.mutate.len };
const ptr = try maps.addOne();
errdefer maps.mutate.len = unwrapped.index;
@@ -10373,14 +10465,15 @@ pub fn remove(ip: *InternPool, tid: Zcu.PerThread.Id, index: Index) void {
fn addInt(
ip: *InternPool,
gpa: Allocator,
io: Io,
tid: Zcu.PerThread.Id,
ty: Index,
tag: Tag,
limbs: []const Limb,
) !void {
const local = ip.getLocal(tid);
const items_list = local.getMutableItems(gpa);
const limbs_list = local.getMutableLimbs(gpa);
const items_list = local.getMutableItems(gpa, io);
const limbs_list = local.getMutableLimbs(gpa, io);
const limbs_len: u32 = @intCast(limbs.len);
try limbs_list.ensureUnusedCapacity(Int.limbs_items_len + limbs_len);
items_list.appendAssumeCapacity(.{
@@ -10510,28 +10603,29 @@ fn extraData(extra: Local.Extra, comptime T: type, index: u32) T {
test "basic usage" {
const gpa = std.testing.allocator;
const io = std.testing.io;
var ip: InternPool = .empty;
try ip.init(gpa, 1);
defer ip.deinit(gpa);
try ip.init(gpa, io, 1);
defer ip.deinit(gpa, io);
const i32_type = try ip.get(gpa, .main, .{ .int_type = .{
const i32_type = try ip.get(gpa, io, .main, .{ .int_type = .{
.signedness = .signed,
.bits = 32,
} });
const array_i32 = try ip.get(gpa, .main, .{ .array_type = .{
const array_i32 = try ip.get(gpa, io, .main, .{ .array_type = .{
.len = 10,
.child = i32_type,
.sentinel = .none,
} });
const another_i32_type = try ip.get(gpa, .main, .{ .int_type = .{
const another_i32_type = try ip.get(gpa, io, .main, .{ .int_type = .{
.signedness = .signed,
.bits = 32,
} });
try std.testing.expect(another_i32_type == i32_type);
const another_array_i32 = try ip.get(gpa, .main, .{ .array_type = .{
const another_array_i32 = try ip.get(gpa, io, .main, .{ .array_type = .{
.len = 10,
.child = i32_type,
.sentinel = .none,
@@ -10608,6 +10702,7 @@ pub fn sliceLen(ip: *const InternPool, index: Index) Index {
pub fn getCoerced(
ip: *InternPool,
gpa: Allocator,
io: Io,
tid: Zcu.PerThread.Id,
val: Index,
new_ty: Index,
@@ -10616,22 +10711,22 @@ pub fn getCoerced(
if (old_ty == new_ty) return val;
switch (val) {
.undef => return ip.get(gpa, tid, .{ .undef = new_ty }),
.undef => return ip.get(gpa, io, tid, .{ .undef = new_ty }),
.null_value => {
if (ip.isOptionalType(new_ty)) return ip.get(gpa, tid, .{ .opt = .{
if (ip.isOptionalType(new_ty)) return ip.get(gpa, io, tid, .{ .opt = .{
.ty = new_ty,
.val = .none,
} });
if (ip.isPointerType(new_ty)) switch (ip.indexToKey(new_ty).ptr_type.flags.size) {
.one, .many, .c => return ip.get(gpa, tid, .{ .ptr = .{
.one, .many, .c => return ip.get(gpa, io, tid, .{ .ptr = .{
.ty = new_ty,
.base_addr = .int,
.byte_offset = 0,
} }),
.slice => return ip.get(gpa, tid, .{ .slice = .{
.slice => return ip.get(gpa, io, tid, .{ .slice = .{
.ty = new_ty,
.ptr = try ip.get(gpa, tid, .{ .ptr = .{
.ptr = try ip.get(gpa, io, tid, .{ .ptr = .{
.ty = ip.slicePtrType(new_ty),
.base_addr = .int,
.byte_offset = 0,
@@ -10644,15 +10739,15 @@ pub fn getCoerced(
const unwrapped_val = val.unwrap(ip);
const val_item = unwrapped_val.getItem(ip);
switch (val_item.tag) {
.func_decl => return getCoercedFuncDecl(ip, gpa, tid, val, new_ty),
.func_instance => return getCoercedFuncInstance(ip, gpa, tid, val, new_ty),
.func_decl => return getCoercedFuncDecl(ip, gpa, io, tid, val, new_ty),
.func_instance => return getCoercedFuncInstance(ip, gpa, io, tid, val, new_ty),
.func_coerced => {
const func: Index = @enumFromInt(unwrapped_val.getExtra(ip).view().items(.@"0")[
val_item.data + std.meta.fieldIndex(Tag.FuncCoerced, "func").?
]);
switch (func.unwrap(ip).getTag(ip)) {
.func_decl => return getCoercedFuncDecl(ip, gpa, tid, val, new_ty),
.func_instance => return getCoercedFuncInstance(ip, gpa, tid, val, new_ty),
.func_decl => return getCoercedFuncDecl(ip, gpa, io, tid, val, new_ty),
.func_instance => return getCoercedFuncInstance(ip, gpa, io, tid, val, new_ty),
else => unreachable,
}
},
@@ -10662,16 +10757,16 @@ pub fn getCoerced(
}
switch (ip.indexToKey(val)) {
.undef => return ip.get(gpa, tid, .{ .undef = new_ty }),
.undef => return ip.get(gpa, io, tid, .{ .undef = new_ty }),
.func => unreachable,
.int => |int| switch (ip.indexToKey(new_ty)) {
.enum_type => return ip.get(gpa, tid, .{ .enum_tag = .{
.enum_type => return ip.get(gpa, io, tid, .{ .enum_tag = .{
.ty = new_ty,
.int = try ip.getCoerced(gpa, tid, val, ip.loadEnumType(new_ty).tag_ty),
.int = try ip.getCoerced(gpa, io, tid, val, ip.loadEnumType(new_ty).tag_ty),
} }),
.ptr_type => switch (int.storage) {
inline .u64, .i64 => |int_val| return ip.get(gpa, tid, .{ .ptr = .{
inline .u64, .i64 => |int_val| return ip.get(gpa, io, tid, .{ .ptr = .{
.ty = new_ty,
.base_addr = .int,
.byte_offset = @intCast(int_val),
@@ -10680,7 +10775,7 @@ pub fn getCoerced(
.lazy_align, .lazy_size => {},
},
else => if (ip.isIntegerType(new_ty))
return ip.getCoercedInts(gpa, tid, int, new_ty),
return ip.getCoercedInts(gpa, io, tid, int, new_ty),
},
.float => |float| switch (ip.indexToKey(new_ty)) {
.simple_type => |simple| switch (simple) {
@@ -10691,7 +10786,7 @@ pub fn getCoerced(
.f128,
.c_longdouble,
.comptime_float,
=> return ip.get(gpa, tid, .{ .float = .{
=> return ip.get(gpa, io, tid, .{ .float = .{
.ty = new_ty,
.storage = float.storage,
} }),
@@ -10700,17 +10795,17 @@ pub fn getCoerced(
else => {},
},
.enum_tag => |enum_tag| if (ip.isIntegerType(new_ty))
return ip.getCoercedInts(gpa, tid, ip.indexToKey(enum_tag.int).int, new_ty),
return ip.getCoercedInts(gpa, io, tid, ip.indexToKey(enum_tag.int).int, new_ty),
.enum_literal => |enum_literal| switch (ip.indexToKey(new_ty)) {
.enum_type => {
const enum_type = ip.loadEnumType(new_ty);
const index = enum_type.nameIndex(ip, enum_literal).?;
return ip.get(gpa, tid, .{ .enum_tag = .{
return ip.get(gpa, io, tid, .{ .enum_tag = .{
.ty = new_ty,
.int = if (enum_type.values.len != 0)
enum_type.values.get(ip)[index]
else
try ip.get(gpa, tid, .{ .int = .{
try ip.get(gpa, io, tid, .{ .int = .{
.ty = enum_type.tag_ty,
.storage = .{ .u64 = index },
} }),
@@ -10719,22 +10814,22 @@ pub fn getCoerced(
else => {},
},
.slice => |slice| if (ip.isPointerType(new_ty) and ip.indexToKey(new_ty).ptr_type.flags.size == .slice)
return ip.get(gpa, tid, .{ .slice = .{
return ip.get(gpa, io, tid, .{ .slice = .{
.ty = new_ty,
.ptr = try ip.getCoerced(gpa, tid, slice.ptr, ip.slicePtrType(new_ty)),
.ptr = try ip.getCoerced(gpa, io, tid, slice.ptr, ip.slicePtrType(new_ty)),
.len = slice.len,
} })
else if (ip.isIntegerType(new_ty))
return ip.getCoerced(gpa, tid, slice.ptr, new_ty),
return ip.getCoerced(gpa, io, tid, slice.ptr, new_ty),
.ptr => |ptr| if (ip.isPointerType(new_ty) and ip.indexToKey(new_ty).ptr_type.flags.size != .slice)
return ip.get(gpa, tid, .{ .ptr = .{
return ip.get(gpa, io, tid, .{ .ptr = .{
.ty = new_ty,
.base_addr = ptr.base_addr,
.byte_offset = ptr.byte_offset,
} })
else if (ip.isIntegerType(new_ty))
switch (ptr.base_addr) {
.int => return ip.get(gpa, tid, .{ .int = .{
.int => return ip.get(gpa, io, tid, .{ .int = .{
.ty = .usize_type,
.storage = .{ .u64 = @intCast(ptr.byte_offset) },
} }),
@@ -10743,14 +10838,14 @@ pub fn getCoerced(
.opt => |opt| switch (ip.indexToKey(new_ty)) {
.ptr_type => |ptr_type| return switch (opt.val) {
.none => switch (ptr_type.flags.size) {
.one, .many, .c => try ip.get(gpa, tid, .{ .ptr = .{
.one, .many, .c => try ip.get(gpa, io, tid, .{ .ptr = .{
.ty = new_ty,
.base_addr = .int,
.byte_offset = 0,
} }),
.slice => try ip.get(gpa, tid, .{ .slice = .{
.slice => try ip.get(gpa, io, tid, .{ .slice = .{
.ty = new_ty,
.ptr = try ip.get(gpa, tid, .{ .ptr = .{
.ptr = try ip.get(gpa, io, tid, .{ .ptr = .{
.ty = ip.slicePtrType(new_ty),
.base_addr = .int,
.byte_offset = 0,
@@ -10758,29 +10853,29 @@ pub fn getCoerced(
.len = .undef_usize,
} }),
},
else => |payload| try ip.getCoerced(gpa, tid, payload, new_ty),
else => |payload| try ip.getCoerced(gpa, io, tid, payload, new_ty),
},
.opt_type => |child_type| return try ip.get(gpa, tid, .{ .opt = .{
.opt_type => |child_type| return try ip.get(gpa, io, tid, .{ .opt = .{
.ty = new_ty,
.val = switch (opt.val) {
.none => .none,
else => try ip.getCoerced(gpa, tid, opt.val, child_type),
else => try ip.getCoerced(gpa, io, tid, opt.val, child_type),
},
} }),
else => {},
},
.err => |err| if (ip.isErrorSetType(new_ty))
return ip.get(gpa, tid, .{ .err = .{
return ip.get(gpa, io, tid, .{ .err = .{
.ty = new_ty,
.name = err.name,
} })
else if (ip.isErrorUnionType(new_ty))
return ip.get(gpa, tid, .{ .error_union = .{
return ip.get(gpa, io, tid, .{ .error_union = .{
.ty = new_ty,
.val = .{ .err_name = err.name },
} }),
.error_union => |error_union| if (ip.isErrorUnionType(new_ty))
return ip.get(gpa, tid, .{ .error_union = .{
return ip.get(gpa, io, tid, .{ .error_union = .{
.ty = new_ty,
.val = error_union.val,
} }),
@@ -10799,20 +10894,20 @@ pub fn getCoerced(
};
if (old_ty_child != new_ty_child) break :direct;
switch (aggregate.storage) {
.bytes => |bytes| return ip.get(gpa, tid, .{ .aggregate = .{
.bytes => |bytes| return ip.get(gpa, io, tid, .{ .aggregate = .{
.ty = new_ty,
.storage = .{ .bytes = bytes },
} }),
.elems => |elems| {
const elems_copy = try gpa.dupe(Index, elems[0..new_len]);
defer gpa.free(elems_copy);
return ip.get(gpa, tid, .{ .aggregate = .{
return ip.get(gpa, io, tid, .{ .aggregate = .{
.ty = new_ty,
.storage = .{ .elems = elems_copy },
} });
},
.repeated_elem => |elem| {
return ip.get(gpa, tid, .{ .aggregate = .{
return ip.get(gpa, io, tid, .{ .aggregate = .{
.ty = new_ty,
.storage = .{ .repeated_elem = elem },
} });
@@ -10830,7 +10925,7 @@ pub fn getCoerced(
// We have to intern each value here, so unfortunately we can't easily avoid
// the repeated indexToKey calls.
for (agg_elems, 0..) |*elem, index| {
elem.* = try ip.get(gpa, tid, .{ .int = .{
elem.* = try ip.get(gpa, io, tid, .{ .int = .{
.ty = .u8_type,
.storage = .{ .u64 = bytes.at(index, ip) },
} });
@@ -10847,27 +10942,27 @@ pub fn getCoerced(
.struct_type => ip.loadStructType(new_ty).field_types.get(ip)[i],
else => unreachable,
};
elem.* = try ip.getCoerced(gpa, tid, elem.*, new_elem_ty);
elem.* = try ip.getCoerced(gpa, io, tid, elem.*, new_elem_ty);
}
return ip.get(gpa, tid, .{ .aggregate = .{ .ty = new_ty, .storage = .{ .elems = agg_elems } } });
return ip.get(gpa, io, tid, .{ .aggregate = .{ .ty = new_ty, .storage = .{ .elems = agg_elems } } });
},
else => {},
}
switch (ip.indexToKey(new_ty)) {
.opt_type => |child_type| switch (val) {
.null_value => return ip.get(gpa, tid, .{ .opt = .{
.null_value => return ip.get(gpa, io, tid, .{ .opt = .{
.ty = new_ty,
.val = .none,
} }),
else => return ip.get(gpa, tid, .{ .opt = .{
else => return ip.get(gpa, io, tid, .{ .opt = .{
.ty = new_ty,
.val = try ip.getCoerced(gpa, tid, val, child_type),
.val = try ip.getCoerced(gpa, io, tid, val, child_type),
} }),
},
.error_union_type => |error_union_type| return ip.get(gpa, tid, .{ .error_union = .{
.error_union_type => |error_union_type| return ip.get(gpa, io, tid, .{ .error_union = .{
.ty = new_ty,
.val = .{ .payload = try ip.getCoerced(gpa, tid, val, error_union_type.payload_type) },
.val = .{ .payload = try ip.getCoerced(gpa, io, tid, val, error_union_type.payload_type) },
} }),
else => {},
}
@@ -10884,6 +10979,7 @@ pub fn getCoerced(
fn getCoercedFuncDecl(
ip: *InternPool,
gpa: Allocator,
io: Io,
tid: Zcu.PerThread.Id,
val: Index,
new_ty: Index,
@@ -10893,12 +10989,13 @@ fn getCoercedFuncDecl(
unwrapped_val.getData(ip) + std.meta.fieldIndex(Tag.FuncDecl, "ty").?
]);
if (new_ty == prev_ty) return val;
return getCoercedFunc(ip, gpa, tid, val, new_ty);
return getCoercedFunc(ip, gpa, io, tid, val, new_ty);
}
fn getCoercedFuncInstance(
ip: *InternPool,
gpa: Allocator,
io: Io,
tid: Zcu.PerThread.Id,
val: Index,
new_ty: Index,
@@ -10908,20 +11005,21 @@ fn getCoercedFuncInstance(
unwrapped_val.getData(ip) + std.meta.fieldIndex(Tag.FuncInstance, "ty").?
]);
if (new_ty == prev_ty) return val;
return getCoercedFunc(ip, gpa, tid, val, new_ty);
return getCoercedFunc(ip, gpa, io, tid, val, new_ty);
}
fn getCoercedFunc(
ip: *InternPool,
gpa: Allocator,
io: Io,
tid: Zcu.PerThread.Id,
func: Index,
ty: Index,
) Allocator.Error!Index {
const local = ip.getLocal(tid);
const items = local.getMutableItems(gpa);
const items = local.getMutableItems(gpa, io);
try items.ensureUnusedCapacity(1);
const extra = local.getMutableExtra(gpa);
const extra = local.getMutableExtra(gpa, io);
const prev_extra_len = extra.mutate.len;
try extra.ensureUnusedCapacity(@typeInfo(Tag.FuncCoerced).@"struct".fields.len);
@@ -10932,7 +11030,7 @@ fn getCoercedFunc(
});
errdefer extra.mutate.len = prev_extra_len;
var gop = try ip.getOrPutKey(gpa, tid, .{
var gop = try ip.getOrPutKey(gpa, io, tid, .{
.func = ip.extraFuncCoerced(extra.list.*, extra_index),
});
defer gop.deinit();
@@ -10950,8 +11048,15 @@ fn getCoercedFunc(
/// Asserts `val` has an integer type.
/// Assumes `new_ty` is an integer type.
pub fn getCoercedInts(ip: *InternPool, gpa: Allocator, tid: Zcu.PerThread.Id, int: Key.Int, new_ty: Index) Allocator.Error!Index {
return ip.get(gpa, tid, .{ .int = .{
pub fn getCoercedInts(
ip: *InternPool,
gpa: Allocator,
io: Io,
tid: Zcu.PerThread.Id,
int: Key.Int,
new_ty: Index,
) Allocator.Error!Index {
return ip.get(gpa, io, tid, .{ .int = .{
.ty = new_ty,
.storage = int.storage,
} });
@@ -11047,12 +11152,12 @@ pub fn errorUnionPayload(ip: *const InternPool, ty: Index) Index {
}
/// The is only legal because the initializer is not part of the hash.
pub fn mutateVarInit(ip: *InternPool, index: Index, init_index: Index) void {
pub fn mutateVarInit(ip: *InternPool, io: Io, index: Index, init_index: Index) void {
const unwrapped_index = index.unwrap(ip);
const local = ip.getLocal(unwrapped_index.tid);
local.mutate.extra.mutex.lock();
defer local.mutate.extra.mutex.unlock();
local.mutate.extra.mutex.lockUncancelable(io);
defer local.mutate.extra.mutex.unlock(io);
const extra_items = local.shared.extra.view().items(.@"0");
const item = unwrapped_index.getItem(ip);
@@ -11508,11 +11613,12 @@ pub fn namespacePtr(ip: *InternPool, namespace_index: NamespaceIndex) *Zcu.Names
pub fn createComptimeUnit(
ip: *InternPool,
gpa: Allocator,
io: Io,
tid: Zcu.PerThread.Id,
zir_index: TrackedInst.Index,
namespace: NamespaceIndex,
) Allocator.Error!ComptimeUnit.Id {
const comptime_units = ip.getLocal(tid).getMutableComptimeUnits(gpa);
const comptime_units = ip.getLocal(tid).getMutableComptimeUnits(gpa, io);
const id_unwrapped: ComptimeUnit.Id.Unwrapped = .{
.tid = tid,
.index = comptime_units.mutate.len,
@@ -11532,9 +11638,10 @@ pub fn getComptimeUnit(ip: *const InternPool, id: ComptimeUnit.Id) ComptimeUnit
/// Create a `Nav` which does not undergo semantic analysis.
/// Since it is never analyzed, the `Nav`'s value must be known at creation time.
pub fn createNav(
fn createNav(
ip: *InternPool,
gpa: Allocator,
io: Io,
tid: Zcu.PerThread.Id,
opts: struct {
name: NullTerminatedString,
@@ -11546,7 +11653,7 @@ pub fn createNav(
@"addrspace": std.builtin.AddressSpace,
},
) Allocator.Error!Nav.Index {
const navs = ip.getLocal(tid).getMutableNavs(gpa);
const navs = ip.getLocal(tid).getMutableNavs(gpa, io);
const index_unwrapped: Nav.Index.Unwrapped = .{
.tid = tid,
.index = navs.mutate.len,
@@ -11571,13 +11678,14 @@ pub fn createNav(
pub fn createDeclNav(
ip: *InternPool,
gpa: Allocator,
io: Io,
tid: Zcu.PerThread.Id,
name: NullTerminatedString,
fqn: NullTerminatedString,
zir_index: TrackedInst.Index,
namespace: NamespaceIndex,
) Allocator.Error!Nav.Index {
const navs = ip.getLocal(tid).getMutableNavs(gpa);
const navs = ip.getLocal(tid).getMutableNavs(gpa, io);
try navs.ensureUnusedCapacity(1);
@@ -11603,6 +11711,7 @@ pub fn createDeclNav(
/// If its status is already `resolved`, the old value is discarded.
pub fn resolveNavType(
ip: *InternPool,
io: Io,
nav: Nav.Index,
resolved: struct {
type: InternPool.Index,
@@ -11617,8 +11726,8 @@ pub fn resolveNavType(
const unwrapped = nav.unwrap(ip);
const local = ip.getLocal(unwrapped.tid);
local.mutate.extra.mutex.lock();
defer local.mutate.extra.mutex.unlock();
local.mutate.extra.mutex.lockUncancelable(io);
defer local.mutate.extra.mutex.unlock(io);
const navs = local.shared.navs.view();
@@ -11647,6 +11756,7 @@ pub fn resolveNavType(
/// If its status is already `resolved`, the old value is discarded.
pub fn resolveNavValue(
ip: *InternPool,
io: Io,
nav: Nav.Index,
resolved: struct {
val: InternPool.Index,
@@ -11659,8 +11769,8 @@ pub fn resolveNavValue(
const unwrapped = nav.unwrap(ip);
const local = ip.getLocal(unwrapped.tid);
local.mutate.extra.mutex.lock();
defer local.mutate.extra.mutex.unlock();
local.mutate.extra.mutex.lockUncancelable(io);
defer local.mutate.extra.mutex.unlock(io);
const navs = local.shared.navs.view();
@@ -11687,6 +11797,7 @@ pub fn resolveNavValue(
pub fn createNamespace(
ip: *InternPool,
gpa: Allocator,
io: Io,
tid: Zcu.PerThread.Id,
initialization: Zcu.Namespace,
) Allocator.Error!NamespaceIndex {
@@ -11700,7 +11811,7 @@ pub fn createNamespace(
reused_namespace.* = initialization;
return reused_namespace_index;
}
const namespaces = local.getMutableNamespaces(gpa);
const namespaces = local.getMutableNamespaces(gpa, io);
const last_bucket_len = local.mutate.namespaces.last_bucket_len & Local.namespaces_bucket_mask;
if (last_bucket_len == 0) {
try namespaces.ensureUnusedCapacity(1);
@@ -11748,10 +11859,11 @@ pub fn filePtr(ip: *const InternPool, file_index: FileIndex) *Zcu.File {
pub fn createFile(
ip: *InternPool,
gpa: Allocator,
io: Io,
tid: Zcu.PerThread.Id,
file: File,
) Allocator.Error!FileIndex {
const files = ip.getLocal(tid).getMutableFiles(gpa);
const files = ip.getLocal(tid).getMutableFiles(gpa, io);
const file_index_unwrapped: FileIndex.Unwrapped = .{
.tid = tid,
.index = files.mutate.len,
@@ -11782,20 +11894,22 @@ const EmbeddedNulls = enum {
pub fn getOrPutString(
ip: *InternPool,
gpa: Allocator,
io: Io,
tid: Zcu.PerThread.Id,
slice: []const u8,
comptime embedded_nulls: EmbeddedNulls,
) Allocator.Error!embedded_nulls.StringType() {
const string_bytes = ip.getLocal(tid).getMutableStringBytes(gpa);
const string_bytes = ip.getLocal(tid).getMutableStringBytes(gpa, io);
try string_bytes.ensureUnusedCapacity(slice.len + 1);
string_bytes.appendSliceAssumeCapacity(.{slice});
string_bytes.appendAssumeCapacity(.{0});
return ip.getOrPutTrailingString(gpa, tid, @intCast(slice.len + 1), embedded_nulls);
return ip.getOrPutTrailingString(gpa, io, tid, @intCast(slice.len + 1), embedded_nulls);
}
pub fn getOrPutStringFmt(
ip: *InternPool,
gpa: Allocator,
io: Io,
tid: Zcu.PerThread.Id,
comptime format: []const u8,
args: anytype,
@@ -11804,20 +11918,21 @@ pub fn getOrPutStringFmt(
// ensure that references to strings in args do not get invalidated
const format_z = format ++ .{0};
const len: u32 = @intCast(std.fmt.count(format_z, args));
const string_bytes = ip.getLocal(tid).getMutableStringBytes(gpa);
const string_bytes = ip.getLocal(tid).getMutableStringBytes(gpa, io);
const slice = try string_bytes.addManyAsSlice(len);
assert((std.fmt.bufPrint(slice[0], format_z, args) catch unreachable).len == len);
return ip.getOrPutTrailingString(gpa, tid, len, embedded_nulls);
return ip.getOrPutTrailingString(gpa, io, tid, len, embedded_nulls);
}
pub fn getOrPutStringOpt(
ip: *InternPool,
gpa: Allocator,
io: Io,
tid: Zcu.PerThread.Id,
slice: ?[]const u8,
comptime embedded_nulls: EmbeddedNulls,
) Allocator.Error!embedded_nulls.OptionalStringType() {
const string = try getOrPutString(ip, gpa, tid, slice orelse return .none, embedded_nulls);
const string = try getOrPutString(ip, gpa, io, tid, slice orelse return .none, embedded_nulls);
return string.toOptional();
}
@@ -11825,14 +11940,15 @@ pub fn getOrPutStringOpt(
pub fn getOrPutTrailingString(
ip: *InternPool,
gpa: Allocator,
io: Io,
tid: Zcu.PerThread.Id,
len: u32,
comptime embedded_nulls: EmbeddedNulls,
) Allocator.Error!embedded_nulls.StringType() {
const local = ip.getLocal(tid);
const strings = local.getMutableStrings(gpa);
const strings = local.getMutableStrings(gpa, io);
try strings.ensureUnusedCapacity(1);
const string_bytes = local.getMutableStringBytes(gpa);
const string_bytes = local.getMutableStringBytes(gpa, io);
const start: u32 = @intCast(string_bytes.mutate.len - len);
if (len > 0 and string_bytes.view().items(.@"0")[string_bytes.mutate.len - 1] == 0) {
string_bytes.mutate.len -= 1;
@@ -11870,8 +11986,8 @@ pub fn getOrPutTrailingString(
string_bytes.shrinkRetainingCapacity(start);
return @enumFromInt(@intFromEnum(index));
}
shard.mutate.string_map.mutex.lock();
defer shard.mutate.string_map.mutex.unlock();
shard.mutate.string_map.mutex.lock(io, tid);
defer shard.mutate.string_map.mutex.unlock(io);
if (map.entries != shard.shared.string_map.entries) {
map = shard.shared.string_map;
map_mask = map.header().mask();
@@ -12590,11 +12706,11 @@ pub fn funcAnalysisUnordered(ip: *const InternPool, func: Index) FuncAnalysis {
return @atomicLoad(FuncAnalysis, ip.funcAnalysisPtr(func), .unordered);
}
pub fn funcSetHasErrorTrace(ip: *InternPool, func: Index, has_error_trace: bool) void {
pub fn funcSetHasErrorTrace(ip: *InternPool, io: Io, func: Index, has_error_trace: bool) void {
const unwrapped_func = func.unwrap(ip);
const extra_mutex = &ip.getLocal(unwrapped_func.tid).mutate.extra.mutex;
extra_mutex.lock();
defer extra_mutex.unlock();
extra_mutex.lockUncancelable(io);
defer extra_mutex.unlock(io);
const analysis_ptr = ip.funcAnalysisPtr(func);
var analysis = analysis_ptr.*;
@@ -12602,11 +12718,11 @@ pub fn funcSetHasErrorTrace(ip: *InternPool, func: Index, has_error_trace: bool)
@atomicStore(FuncAnalysis, analysis_ptr, analysis, .release);
}
pub fn funcSetDisableInstrumentation(ip: *InternPool, func: Index) void {
pub fn funcSetDisableInstrumentation(ip: *InternPool, io: Io, func: Index) void {
const unwrapped_func = func.unwrap(ip);
const extra_mutex = &ip.getLocal(unwrapped_func.tid).mutate.extra.mutex;
extra_mutex.lock();
defer extra_mutex.unlock();
extra_mutex.lockUncancelable(io);
defer extra_mutex.unlock(io);
const analysis_ptr = ip.funcAnalysisPtr(func);
var analysis = analysis_ptr.*;
@@ -12614,11 +12730,11 @@ pub fn funcSetDisableInstrumentation(ip: *InternPool, func: Index) void {
@atomicStore(FuncAnalysis, analysis_ptr, analysis, .release);
}
pub fn funcSetDisableIntrinsics(ip: *InternPool, func: Index) void {
pub fn funcSetDisableIntrinsics(ip: *InternPool, io: Io, func: Index) void {
const unwrapped_func = func.unwrap(ip);
const extra_mutex = &ip.getLocal(unwrapped_func.tid).mutate.extra.mutex;
extra_mutex.lock();
defer extra_mutex.unlock();
extra_mutex.lockUncancelable(io);
defer extra_mutex.unlock(io);
const analysis_ptr = ip.funcAnalysisPtr(func);
var analysis = analysis_ptr.*;
@@ -12663,15 +12779,6 @@ pub fn iesFuncIndex(ip: *const InternPool, ies_index: Index) Index {
return func_index;
}
/// Returns a mutable pointer to the resolved error set type of an inferred
/// error set function. The returned pointer is invalidated when anything is
/// added to `ip`.
fn iesResolvedPtr(ip: *InternPool, ies_index: Index) *Index {
const ies_item = ies_index.getItem(ip);
assert(ies_item.tag == .type_inferred_error_set);
return ip.funcIesResolvedPtr(ies_item.data);
}
/// Returns a mutable pointer to the resolved error set type of an inferred
/// error set function. The returned pointer is invalidated when anything is
/// added to `ip`.
@@ -12706,11 +12813,11 @@ pub fn funcIesResolvedUnordered(ip: *const InternPool, index: Index) Index {
return @atomicLoad(Index, ip.funcIesResolvedPtr(index), .unordered);
}
pub fn funcSetIesResolved(ip: *InternPool, index: Index, ies: Index) void {
pub fn funcSetIesResolved(ip: *InternPool, io: Io, index: Index, ies: Index) void {
const unwrapped_func = index.unwrap(ip);
const extra_mutex = &ip.getLocal(unwrapped_func.tid).mutate.extra.mutex;
extra_mutex.lock();
defer extra_mutex.unlock();
extra_mutex.lockUncancelable(io);
defer extra_mutex.unlock(io);
@atomicStore(Index, ip.funcIesResolvedPtr(index), ies, .release);
}
@@ -12777,19 +12884,19 @@ const GlobalErrorSet = struct {
} align(std.atomic.cache_line),
mutate: struct {
names: Local.ListMutate,
map: struct { mutex: std.Thread.Mutex },
map: struct { mutex: Io.Mutex },
} align(std.atomic.cache_line),
const Names = Local.List(struct { NullTerminatedString });
const empty: GlobalErrorSet = .{
.shared = .{
.names = Names.empty,
.map = Shard.Map(GlobalErrorSet.Index).empty,
.names = .empty,
.map = .empty,
},
.mutate = .{
.names = Local.ListMutate.empty,
.map = .{ .mutex = .{} },
.names = .empty,
.map = .{ .mutex = .init },
},
};
@@ -12807,6 +12914,7 @@ const GlobalErrorSet = struct {
fn getErrorValue(
ges: *GlobalErrorSet,
gpa: Allocator,
io: Io,
arena_state: *std.heap.ArenaAllocator.State,
name: NullTerminatedString,
) Allocator.Error!GlobalErrorSet.Index {
@@ -12825,8 +12933,8 @@ const GlobalErrorSet = struct {
if (entry.hash != hash) continue;
if (names.view().items(.@"0")[@intFromEnum(index) - 1] == name) return index;
}
ges.mutate.map.mutex.lock();
defer ges.mutate.map.mutex.unlock();
ges.mutate.map.mutex.lockUncancelable(io);
defer ges.mutate.map.mutex.unlock(io);
if (map.entries != ges.shared.map.entries) {
map = ges.shared.map;
map_mask = map.header().mask();
@@ -12842,6 +12950,7 @@ const GlobalErrorSet = struct {
}
const mutable_names: Names.Mutable = .{
.gpa = gpa,
.io = io,
.arena = arena_state,
.mutate = &ges.mutate.names,
.list = &ges.shared.names,
@@ -12923,10 +13032,11 @@ const GlobalErrorSet = struct {
pub fn getErrorValue(
ip: *InternPool,
gpa: Allocator,
io: Io,
tid: Zcu.PerThread.Id,
name: NullTerminatedString,
) Allocator.Error!Zcu.ErrorInt {
return @intFromEnum(try ip.global_error_set.getErrorValue(gpa, &ip.getLocal(tid).mutate.arena, name));
return @intFromEnum(try ip.global_error_set.getErrorValue(gpa, io, &ip.getLocal(tid).mutate.arena, name));
}
pub fn getErrorValueIfExists(ip: *const InternPool, name: NullTerminatedString) ?Zcu.ErrorInt {
+461 -202
View File
@@ -853,8 +853,9 @@ pub const Block = struct {
fn trackZir(block: *Block, inst: Zir.Inst.Index) Allocator.Error!InternPool.TrackedInst.Index {
const pt = block.sema.pt;
const comp = pt.zcu.comp;
block.sema.code.assertTrackable(inst);
return pt.zcu.intern_pool.trackZir(pt.zcu.gpa, pt.tid, .{
return pt.zcu.intern_pool.trackZir(comp.gpa, comp.io, pt.tid, .{
.file = block.getFileScopeIndex(pt.zcu),
.inst = inst,
});
@@ -2205,10 +2206,11 @@ fn analyzeAsType(
pub fn setupErrorReturnTrace(sema: *Sema, block: *Block, last_arg_index: usize) !void {
const pt = sema.pt;
const zcu = pt.zcu;
const comp = zcu.comp;
const gpa = sema.gpa;
const ip = &zcu.intern_pool;
const comp = pt.zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const ip = &pt.zcu.intern_pool;
if (!comp.config.any_error_tracing) return;
assert(!block.isComptime());
@@ -2231,12 +2233,12 @@ pub fn setupErrorReturnTrace(sema: *Sema, block: *Block, last_arg_index: usize)
const st_ptr = try err_trace_block.addTy(.alloc, try pt.singleMutPtrType(stack_trace_ty));
// st.instruction_addresses = &addrs;
const instruction_addresses_field_name = try ip.getOrPutString(gpa, pt.tid, "instruction_addresses", .no_embedded_nulls);
const instruction_addresses_field_name = try ip.getOrPutString(gpa, io, pt.tid, "instruction_addresses", .no_embedded_nulls);
const addr_field_ptr = try sema.fieldPtr(&err_trace_block, src, st_ptr, instruction_addresses_field_name, src, true);
try sema.storePtr2(&err_trace_block, src, addr_field_ptr, src, addrs_ptr, src, .store);
// st.index = 0;
const index_field_name = try ip.getOrPutString(gpa, pt.tid, "index", .no_embedded_nulls);
const index_field_name = try ip.getOrPutString(gpa, io, pt.tid, "index", .no_embedded_nulls);
const index_field_ptr = try sema.fieldPtr(&err_trace_block, src, st_ptr, index_field_name, src, true);
try sema.storePtr2(&err_trace_block, src, index_field_ptr, src, .zero_usize, src, .store);
@@ -2828,9 +2830,12 @@ fn zirTupleDecl(
block: *Block,
extended: Zir.Inst.Extended.InstData,
) CompileError!Air.Inst.Ref {
const gpa = sema.gpa;
const pt = sema.pt;
const zcu = pt.zcu;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const fields_len = extended.small;
const extra = sema.code.extraData(Zir.Inst.TupleDecl, extended.operand);
var extra_index = extra.end;
@@ -2863,7 +2868,7 @@ fn zirTupleDecl(
const coerced_field_init = try sema.coerce(block, field_type, uncoerced_field_init, init_src);
const field_init_val = try sema.resolveConstDefinedValue(block, init_src, coerced_field_init, .{ .simple = .tuple_field_default_value });
if (field_init_val.canMutateComptimeVarState(zcu)) {
const field_name = try zcu.intern_pool.getOrPutStringFmt(gpa, pt.tid, "{d}", .{field_index}, .no_embedded_nulls);
const field_name = try zcu.intern_pool.getOrPutStringFmt(gpa, io, pt.tid, "{d}", .{field_index}, .no_embedded_nulls);
return sema.failWithContainsReferenceToComptimeVar(block, init_src, field_name, "field default value", field_init_val);
}
break :init field_init_val.toIntern();
@@ -2872,7 +2877,7 @@ fn zirTupleDecl(
};
}
return Air.internedToRef(try zcu.intern_pool.getTupleType(gpa, pt.tid, .{
return Air.internedToRef(try zcu.intern_pool.getTupleType(gpa, io, pt.tid, .{
.types = types,
.values = inits,
}));
@@ -2911,7 +2916,11 @@ fn validateTupleFieldType(
fn getCaptures(sema: *Sema, block: *Block, type_src: LazySrcLoc, extra_index: usize, captures_len: u32) ![]InternPool.CaptureValue {
const pt = sema.pt;
const zcu = pt.zcu;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const ip = &zcu.intern_pool;
const parent_ty: Type = .fromInterned(zcu.namespacePtr(block.namespace).owner_type);
const parent_captures: InternPool.CaptureValue.Slice = parent_ty.getCaptures(zcu);
@@ -2934,7 +2943,7 @@ fn getCaptures(sema: *Sema, block: *Block, type_src: LazySrcLoc, extra_index: us
};
const loaded_val = try sema.resolveLazyValue(unresolved_loaded_val);
if (loaded_val.canMutateComptimeVarState(zcu)) {
const field_name = try ip.getOrPutString(zcu.gpa, pt.tid, zir_name_slice, .no_embedded_nulls);
const field_name = try ip.getOrPutString(gpa, io, pt.tid, zir_name_slice, .no_embedded_nulls);
return sema.failWithContainsReferenceToComptimeVar(block, type_src, field_name, "captured value", loaded_val);
}
break :capture .{ .@"comptime" = loaded_val.toIntern() };
@@ -2943,7 +2952,7 @@ fn getCaptures(sema: *Sema, block: *Block, type_src: LazySrcLoc, extra_index: us
const air_ref = try sema.resolveInst(inst.toRef());
if (try sema.resolveValueResolveLazy(air_ref)) |val| {
if (val.canMutateComptimeVarState(zcu)) {
const field_name = try ip.getOrPutString(zcu.gpa, pt.tid, zir_name_slice, .no_embedded_nulls);
const field_name = try ip.getOrPutString(gpa, io, pt.tid, zir_name_slice, .no_embedded_nulls);
return sema.failWithContainsReferenceToComptimeVar(block, type_src, field_name, "captured value", val);
}
break :capture .{ .@"comptime" = val.toIntern() };
@@ -2952,7 +2961,8 @@ fn getCaptures(sema: *Sema, block: *Block, type_src: LazySrcLoc, extra_index: us
}),
.decl_val => |str| capture: {
const decl_name = try ip.getOrPutString(
sema.gpa,
gpa,
io,
pt.tid,
sema.code.nullTerminatedString(str),
.no_embedded_nulls,
@@ -2962,7 +2972,8 @@ fn getCaptures(sema: *Sema, block: *Block, type_src: LazySrcLoc, extra_index: us
},
.decl_ref => |str| capture: {
const decl_name = try ip.getOrPutString(
sema.gpa,
gpa,
io,
pt.tid,
sema.code.nullTerminatedString(str),
.no_embedded_nulls,
@@ -2984,8 +2995,11 @@ fn zirStructDecl(
) CompileError!Air.Inst.Ref {
const pt = sema.pt;
const zcu = pt.zcu;
const gpa = sema.gpa;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const ip = &zcu.intern_pool;
const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small);
const extra = sema.code.extraData(Zir.Inst.StructDecl, extended.operand);
@@ -3040,7 +3054,7 @@ fn zirStructDecl(
.captures = captures,
} },
};
const wip_ty = switch (try ip.getStructType(gpa, pt.tid, struct_init, false)) {
const wip_ty = switch (try ip.getStructType(gpa, io, pt.tid, struct_init, false)) {
.existing => |ty| {
const new_ty = try pt.ensureTypeUpToDate(ty);
@@ -3108,7 +3122,9 @@ pub fn createTypeName(
} {
const pt = sema.pt;
const zcu = pt.zcu;
const gpa = zcu.gpa;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const ip = &zcu.intern_pool;
switch (name_strategy) {
@@ -3158,7 +3174,7 @@ pub fn createTypeName(
w.writeByte(')') catch return error.OutOfMemory;
return .{
.name = try ip.getOrPutString(gpa, pt.tid, aw.written(), .no_embedded_nulls),
.name = try ip.getOrPutString(gpa, io, pt.tid, aw.written(), .no_embedded_nulls),
.nav = .none,
};
},
@@ -3170,7 +3186,7 @@ pub fn createTypeName(
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 .{
.name = try ip.getOrPutStringFmt(gpa, pt.tid, "{f}.{s}", .{
.name = try ip.getOrPutStringFmt(gpa, io, pt.tid, "{f}.{s}", .{
block.type_name_ctx.fmt(ip), zir_data[i].str_op.getStr(sema.code),
}, .no_embedded_nulls),
.nav = .none,
@@ -3193,7 +3209,7 @@ pub fn createTypeName(
// that builtin from the language, we can consider this.
return .{
.name = try ip.getOrPutStringFmt(gpa, pt.tid, "{f}__{s}_{d}", .{
.name = try ip.getOrPutStringFmt(gpa, io, pt.tid, "{f}__{s}_{d}", .{
block.type_name_ctx.fmt(ip), anon_prefix, @intFromEnum(type_index),
}, .no_embedded_nulls),
.nav = .none,
@@ -3211,8 +3227,11 @@ fn zirEnumDecl(
const pt = sema.pt;
const zcu = pt.zcu;
const gpa = sema.gpa;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const ip = &zcu.intern_pool;
const small: Zir.Inst.EnumDecl.Small = @bitCast(extended.small);
const extra = sema.code.extraData(Zir.Inst.EnumDecl, extended.operand);
var extra_index: usize = extra.end;
@@ -3281,7 +3300,7 @@ fn zirEnumDecl(
.captures = captures,
} },
};
const wip_ty = switch (try ip.getEnumType(gpa, pt.tid, enum_init, false)) {
const wip_ty = switch (try ip.getEnumType(gpa, io, pt.tid, enum_init, false)) {
.existing => |ty| {
const new_ty = try pt.ensureTypeUpToDate(ty);
@@ -3380,8 +3399,11 @@ fn zirUnionDecl(
const pt = sema.pt;
const zcu = pt.zcu;
const gpa = sema.gpa;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const ip = &zcu.intern_pool;
const small: Zir.Inst.UnionDecl.Small = @bitCast(extended.small);
const extra = sema.code.extraData(Zir.Inst.UnionDecl, extended.operand);
var extra_index: usize = extra.end;
@@ -3438,7 +3460,7 @@ fn zirUnionDecl(
.captures = captures,
} },
};
const wip_ty = switch (try ip.getUnionType(gpa, pt.tid, union_init, false)) {
const wip_ty = switch (try ip.getUnionType(gpa, io, pt.tid, union_init, false)) {
.existing => |ty| {
const new_ty = try pt.ensureTypeUpToDate(ty);
@@ -3503,7 +3525,9 @@ fn zirOpaqueDecl(
const pt = sema.pt;
const zcu = pt.zcu;
const gpa = sema.gpa;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const ip = &zcu.intern_pool;
const small: Zir.Inst.OpaqueDecl.Small = @bitCast(extended.small);
@@ -3532,7 +3556,7 @@ fn zirOpaqueDecl(
.zir_index = tracked_inst,
.captures = captures,
};
const wip_ty = switch (try ip.getOpaqueType(gpa, pt.tid, opaque_init)) {
const wip_ty = switch (try ip.getOpaqueType(gpa, io, pt.tid, opaque_init)) {
.existing => |ty| {
// Make sure we update the namespace if the declaration is re-analyzed, to pick
// up on e.g. changed comptime decls.
@@ -3587,7 +3611,10 @@ fn zirErrorSetDecl(
const pt = sema.pt;
const zcu = pt.zcu;
const gpa = sema.gpa;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
const extra = sema.code.extraData(Zir.Inst.ErrorSetDecl, inst_data.payload_index);
@@ -3599,7 +3626,7 @@ fn zirErrorSetDecl(
while (extra_index < extra_index_end) : (extra_index += 1) {
const name_index: Zir.NullTerminatedString = @enumFromInt(sema.code.extra[extra_index]);
const name = sema.code.nullTerminatedString(name_index);
const name_ip = try zcu.intern_pool.getOrPutString(gpa, pt.tid, name, .no_embedded_nulls);
const name_ip = try zcu.intern_pool.getOrPutString(gpa, io, pt.tid, name, .no_embedded_nulls);
_ = try pt.getErrorValue(name_ip);
const result = names.getOrPutAssumeCapacity(name_ip);
assert(!result.found_existing); // verified in AstGen
@@ -3761,11 +3788,14 @@ fn indexablePtrLen(
) CompileError!Air.Inst.Ref {
const pt = sema.pt;
const zcu = pt.zcu;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const object_ty = sema.typeOf(object);
const is_pointer_to = object_ty.isSinglePointer(zcu);
const indexable_ty = if (is_pointer_to) object_ty.childType(zcu) else object_ty;
try sema.checkIndexable(block, src, indexable_ty);
const field_name = try zcu.intern_pool.getOrPutString(sema.gpa, pt.tid, "len", .no_embedded_nulls);
const field_name = try zcu.intern_pool.getOrPutString(gpa, io, pt.tid, "len", .no_embedded_nulls);
return sema.fieldVal(block, src, object, field_name, src);
}
@@ -3777,13 +3807,16 @@ fn indexablePtrLenOrNone(
) CompileError!Air.Inst.Ref {
const pt = sema.pt;
const zcu = pt.zcu;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const operand_ty = sema.typeOf(operand);
try checkMemOperand(sema, block, src, operand_ty);
switch (operand_ty.ptrSize(zcu)) {
.many, .c => return .none,
.one, .slice => {},
}
const field_name = try zcu.intern_pool.getOrPutString(sema.gpa, pt.tid, "len", .no_embedded_nulls);
const field_name = try zcu.intern_pool.getOrPutString(gpa, io, pt.tid, "len", .no_embedded_nulls);
return sema.fieldVal(block, src, operand, field_name, src);
}
@@ -3961,6 +3994,9 @@ fn zirMakePtrConst(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro
fn resolveComptimeKnownAllocPtr(sema: *Sema, block: *Block, alloc: Air.Inst.Ref, resolved_alloc_ty: ?Type) CompileError!?InternPool.Index {
const pt = sema.pt;
const zcu = pt.zcu;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const alloc_ty = resolved_alloc_ty orelse sema.typeOf(alloc);
const ptr_info = alloc_ty.ptrInfo(zcu);
@@ -4108,7 +4144,7 @@ fn resolveComptimeKnownAllocPtr(sema: *Sema, block: *Block, alloc: Air.Inst.Ref,
};
const new_ptr_ty = tmp_air.typeOfIndex(air_ptr, &zcu.intern_pool).toIntern();
const new_ptr = switch (method) {
.same_addr => try zcu.intern_pool.getCoerced(sema.gpa, pt.tid, decl_parent_ptr, new_ptr_ty),
.same_addr => try zcu.intern_pool.getCoerced(gpa, io, pt.tid, decl_parent_ptr, new_ptr_ty),
.opt_payload => ptr: {
// Set the optional to non-null at comptime.
// If the payload is OPV, we must use that value instead of undef.
@@ -4523,8 +4559,11 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com
fn zirForLen(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
const pt = sema.pt;
const zcu = pt.zcu;
const gpa = sema.gpa;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const ip = &zcu.intern_pool;
const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
const extra = sema.code.extraData(Zir.Inst.MultiOp, inst_data.payload_index);
const all_args = sema.code.refSlice(extra.end, extra.data.operands_len);
@@ -4570,7 +4609,7 @@ fn zirForLen(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
return sema.failWithOwnedErrorMsg(block, msg);
}
if (!object_ty.indexableHasLen(zcu)) continue;
break :l try sema.fieldVal(block, arg_src, object, try ip.getOrPutString(gpa, pt.tid, "len", .no_embedded_nulls), arg_src);
break :l try sema.fieldVal(block, arg_src, object, try ip.getOrPutString(gpa, io, pt.tid, "len", .no_embedded_nulls), arg_src);
} else l: {
// This argument is a range.
const range_start = try sema.resolveInst(zir_arg_pair[0]);
@@ -4733,6 +4772,10 @@ fn zirCoercePtrElemTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
fn zirTryOperandTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is_ref: bool) CompileError!Air.Inst.Ref {
const pt = sema.pt;
const zcu = pt.zcu;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const un_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
const src = block.nodeOffset(un_node.src_node);
@@ -4758,7 +4801,7 @@ fn zirTryOperandTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is_ref: boo
// This function cannot return an error.
// `try` is still valid if the error case is impossible, i.e. no error is returned.
// So, the result type has an error set of `error{}`.
break :err_set .fromInterned(try zcu.intern_pool.getErrorSetType(zcu.gpa, pt.tid, &.{}));
break :err_set .fromInterned(try zcu.intern_pool.getErrorSetType(gpa, io, pt.tid, &.{}));
},
}
}
@@ -5003,7 +5046,9 @@ fn validateStructInit(
) CompileError!void {
const pt = sema.pt;
const zcu = pt.zcu;
const gpa = sema.gpa;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const ip = &zcu.intern_pool;
// Tracks whether each field was explicitly initialized.
@@ -5017,6 +5062,7 @@ fn validateStructInit(
const field_ptr_extra = sema.code.extraData(Zir.Inst.Field, field_ptr_data.payload_index).data;
const field_name = try ip.getOrPutString(
gpa,
io,
pt.tid,
sema.code.nullTerminatedString(field_ptr_extra.field_name_start),
.no_embedded_nulls,
@@ -5461,9 +5507,15 @@ fn zirStoreNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!v
}
fn zirStr(sema: *Sema, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
const pt = sema.pt;
const zcu = pt.zcu;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const ip = &zcu.intern_pool;
const bytes = sema.code.instructions.items(.data)[@intFromEnum(inst)].str.get(sema.code);
return sema.addStrLit(
try sema.pt.zcu.intern_pool.getOrPutString(sema.gpa, sema.pt.tid, bytes, .maybe_embedded_nulls),
try ip.getOrPutString(gpa, io, pt.tid, bytes, .maybe_embedded_nulls),
bytes.len,
);
}
@@ -5555,7 +5607,9 @@ fn zirCompileLog(
) CompileError!Air.Inst.Ref {
const pt = sema.pt;
const zcu = pt.zcu;
const gpa = zcu.gpa;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
var aw: std.Io.Writer.Allocating = .init(gpa);
defer aw.deinit();
@@ -5579,7 +5633,7 @@ fn zirCompileLog(
}
}
const line_data = try zcu.intern_pool.getOrPutString(gpa, pt.tid, aw.written(), .no_embedded_nulls);
const line_data = try zcu.intern_pool.getOrPutString(gpa, io, pt.tid, aw.written(), .no_embedded_nulls);
const line_idx: Zcu.CompileLogLine.Index = if (zcu.free_compile_log_lines.pop()) |idx| idx: {
zcu.compile_log_lines.items[@intFromEnum(idx)] = .{
@@ -5757,7 +5811,9 @@ fn zirCImport(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileEr
const pt = sema.pt;
const zcu = pt.zcu;
const comp = zcu.comp;
const gpa = sema.gpa;
const gpa = comp.gpa;
const io = comp.io;
const pl_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
const src = parent_block.nodeOffset(pl_node.src_node);
const extra = sema.code.extraData(Zir.Inst.Block, pl_node.payload_index);
@@ -5846,7 +5902,7 @@ fn zirCImport(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileEr
errdefer c_import_file_path.deinit(gpa);
const c_import_file = try gpa.create(Zcu.File);
errdefer gpa.destroy(c_import_file);
const c_import_file_index = try zcu.intern_pool.createFile(gpa, pt.tid, .{
const c_import_file_index = try zcu.intern_pool.createFile(gpa, io, pt.tid, .{
.bin_digest = c_import_file_path.digest(),
.file = c_import_file,
.root_type = .none,
@@ -6350,6 +6406,7 @@ pub fn analyzeExport(
fn zirDisableInstrumentation(sema: *Sema) CompileError!void {
const pt = sema.pt;
const zcu = pt.zcu;
const io = zcu.comp.io;
const ip = &zcu.intern_pool;
const func = switch (sema.owner.unwrap()) {
.func => |func| func,
@@ -6360,13 +6417,14 @@ fn zirDisableInstrumentation(sema: *Sema) CompileError!void {
.memoized_state,
=> return, // does nothing outside a function
};
ip.funcSetDisableInstrumentation(func);
ip.funcSetDisableInstrumentation(io, func);
sema.allow_memoize = false;
}
fn zirDisableIntrinsics(sema: *Sema) CompileError!void {
const pt = sema.pt;
const zcu = pt.zcu;
const io = zcu.comp.io;
const ip = &zcu.intern_pool;
const func = switch (sema.owner.unwrap()) {
.func => |func| func,
@@ -6377,7 +6435,7 @@ fn zirDisableIntrinsics(sema: *Sema) CompileError!void {
.memoized_state,
=> return, // does nothing outside a function
};
ip.funcSetDisableIntrinsics(func);
ip.funcSetDisableIntrinsics(io, func);
sema.allow_memoize = false;
}
@@ -6576,10 +6634,15 @@ pub fn appendAirString(sema: *Sema, str: []const u8) Allocator.Error!Air.NullTer
fn zirDeclRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
const pt = sema.pt;
const zcu = pt.zcu;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].str_tok;
const src = block.tokenOffset(inst_data.src_tok);
const decl_name = try zcu.intern_pool.getOrPutString(
sema.gpa,
gpa,
io,
pt.tid,
inst_data.get(sema.code),
.no_embedded_nulls,
@@ -6591,10 +6654,15 @@ fn zirDeclRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
fn zirDeclVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
const pt = sema.pt;
const zcu = pt.zcu;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].str_tok;
const src = block.tokenOffset(inst_data.src_tok);
const decl_name = try zcu.intern_pool.getOrPutString(
sema.gpa,
gpa,
io,
pt.tid,
inst_data.get(sema.code),
.no_embedded_nulls,
@@ -6683,7 +6751,9 @@ fn funcDeclSrcInst(sema: *Sema, func_inst: Air.Inst.Ref) !?InternPool.TrackedIns
pub fn analyzeSaveErrRetIndex(sema: *Sema, block: *Block) SemaError!Air.Inst.Ref {
const pt = sema.pt;
const zcu = pt.zcu;
const gpa = sema.gpa;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
if (block.isComptime() or block.is_typeof) {
const index_val = try pt.intValue_u64(.usize, sema.comptime_err_ret_trace.items.len);
@@ -6694,7 +6764,7 @@ pub fn analyzeSaveErrRetIndex(sema: *Sema, block: *Block) SemaError!Air.Inst.Ref
const stack_trace_ty = try sema.getBuiltinType(block.nodeOffset(.zero), .StackTrace);
try stack_trace_ty.resolveFields(pt);
const field_name = try zcu.intern_pool.getOrPutString(gpa, pt.tid, "index", .no_embedded_nulls);
const field_name = try zcu.intern_pool.getOrPutString(gpa, io, pt.tid, "index", .no_embedded_nulls);
const field_index = sema.structFieldIndex(block, stack_trace_ty, field_name, LazySrcLoc.unneeded) catch |err| switch (err) {
error.AnalysisFail => @panic("std.builtin.StackTrace is corrupt"),
error.ComptimeReturn, error.ComptimeBreak => unreachable,
@@ -6721,7 +6791,9 @@ fn popErrorReturnTrace(
) CompileError!void {
const pt = sema.pt;
const zcu = pt.zcu;
const gpa = sema.gpa;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
var is_non_error: ?bool = null;
var is_non_error_inst: Air.Inst.Ref = undefined;
if (operand != .none) {
@@ -6738,7 +6810,7 @@ fn popErrorReturnTrace(
try stack_trace_ty.resolveFields(pt);
const ptr_stack_trace_ty = try pt.singleMutPtrType(stack_trace_ty);
const err_return_trace = try block.addTy(.err_return_trace, ptr_stack_trace_ty);
const field_name = try zcu.intern_pool.getOrPutString(gpa, pt.tid, "index", .no_embedded_nulls);
const field_name = try zcu.intern_pool.getOrPutString(gpa, io, pt.tid, "index", .no_embedded_nulls);
const field_ptr = try sema.structFieldPtr(block, src, err_return_trace, field_name, src, stack_trace_ty, true);
try sema.storePtr2(block, src, field_ptr, src, saved_error_trace_index, src, .store);
} else if (is_non_error == null) {
@@ -6764,7 +6836,7 @@ fn popErrorReturnTrace(
try stack_trace_ty.resolveFields(pt);
const ptr_stack_trace_ty = try pt.singleMutPtrType(stack_trace_ty);
const err_return_trace = try then_block.addTy(.err_return_trace, ptr_stack_trace_ty);
const field_name = try zcu.intern_pool.getOrPutString(gpa, pt.tid, "index", .no_embedded_nulls);
const field_name = try zcu.intern_pool.getOrPutString(gpa, io, pt.tid, "index", .no_embedded_nulls);
const field_ptr = try sema.structFieldPtr(&then_block, src, err_return_trace, field_name, src, stack_trace_ty, true);
try sema.storePtr2(&then_block, src, field_ptr, src, saved_error_trace_index, src, .store);
_ = try then_block.addBr(cond_block_inst, .void_value);
@@ -6818,6 +6890,10 @@ fn zirCall(
const pt = sema.pt;
const zcu = pt.zcu;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
const callee_src = block.src(.{ .node_offset_call_func = inst_data.src_node });
const call_src = block.nodeOffset(inst_data.src_node);
@@ -6837,7 +6913,8 @@ fn zirCall(
.field => blk: {
const object_ptr = try sema.resolveInst(extra.data.obj_ptr);
const field_name = try zcu.intern_pool.getOrPutString(
sema.gpa,
gpa,
io,
pt.tid,
sema.code.nullTerminatedString(extra.data.field_name_start),
.no_embedded_nulls,
@@ -6897,7 +6974,7 @@ fn zirCall(
if (input_is_error or (pop_error_return_trace and return_ty.isError(zcu))) {
const stack_trace_ty = try sema.getBuiltinType(call_src, .StackTrace);
try stack_trace_ty.resolveFields(pt);
const field_name = try zcu.intern_pool.getOrPutString(sema.gpa, pt.tid, "index", .no_embedded_nulls);
const field_name = try zcu.intern_pool.getOrPutString(gpa, io, pt.tid, "index", .no_embedded_nulls);
const field_index = try sema.structFieldIndex(block, stack_trace_ty, field_name, call_src);
// Insert a save instruction before the arg resolution + call instructions we just generated
@@ -7232,7 +7309,9 @@ fn analyzeCall(
) CompileError!Air.Inst.Ref {
const pt = sema.pt;
const zcu = pt.zcu;
const gpa = zcu.gpa;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const ip = &zcu.intern_pool;
const arena = sema.arena;
@@ -7544,7 +7623,7 @@ fn analyzeCall(
if (func_ty_info.cc == .auto) {
switch (sema.owner.unwrap()) {
.@"comptime", .nav_ty, .nav_val, .type, .memoized_state => {},
.func => |owner_func| ip.funcSetHasErrorTrace(owner_func, true),
.func => |owner_func| ip.funcSetHasErrorTrace(io, owner_func, true),
}
}
for (args, 0..) |arg, arg_idx| {
@@ -7596,7 +7675,7 @@ fn analyzeCall(
} else resolved_ret_ty;
// We now need to actually create the function instance.
const func_instance = try ip.getFuncInstance(gpa, pt.tid, .{
const func_instance = try ip.getFuncInstance(gpa, io, pt.tid, .{
.param_types = runtime_param_tys.items,
.noalias_bits = noalias_bits,
.bare_return_type = bare_ret_ty.toIntern(),
@@ -7614,7 +7693,7 @@ fn analyzeCall(
// This call is problematic as it breaks guarantees about order-independency of semantic analysis.
// These guarantees are necessary for incremental compilation and parallel semantic analysis.
// See: #22410
zcu.funcInfo(func_instance).maxBranchQuota(ip, sema.branch_quota);
zcu.funcInfo(func_instance).maxBranchQuota(ip, io, sema.branch_quota);
break :func .{ Air.internedToRef(func_instance), runtime_args.items };
};
@@ -8102,6 +8181,9 @@ fn zirArrayTypeSentinel(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Compil
const pt = sema.pt;
const zcu = pt.zcu;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const ip = &zcu.intern_pool;
const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
@@ -8116,7 +8198,7 @@ fn zirArrayTypeSentinel(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Compil
const sentinel = try sema.coerce(block, elem_type, uncasted_sentinel, sentinel_src);
const sentinel_val = try sema.resolveConstDefinedValue(block, sentinel_src, sentinel, .{ .simple = .array_sentinel });
if (sentinel_val.canMutateComptimeVarState(zcu)) {
const sentinel_name = try ip.getOrPutString(sema.gpa, pt.tid, "sentinel", .no_embedded_nulls);
const sentinel_name = try ip.getOrPutString(gpa, io, pt.tid, "sentinel", .no_embedded_nulls);
return sema.failWithContainsReferenceToComptimeVar(block, sentinel_src, sentinel_name, "sentinel", sentinel_val);
}
const array_ty = try pt.arrayType(.{
@@ -8194,10 +8276,17 @@ fn validateErrorUnionPayloadType(sema: *Sema, block: *Block, payload_ty: Type, p
fn zirErrorValue(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
_ = block;
const pt = sema.pt;
const zcu = pt.zcu;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].str_tok;
const name = try pt.zcu.intern_pool.getOrPutString(
sema.gpa,
gpa,
io,
pt.tid,
inst_data.get(sema.code),
.no_embedded_nulls,
@@ -8259,7 +8348,9 @@ fn zirErrorFromInt(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstD
const pt = sema.pt;
const zcu = pt.zcu;
const io = zcu.comp.io;
const ip = &zcu.intern_pool;
const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
const src = block.nodeOffset(extra.node);
const operand_src = block.builtinCallArgSrc(extra.node, 0);
@@ -8271,8 +8362,8 @@ fn zirErrorFromInt(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstD
const int = try sema.usizeCast(block, operand_src, try value.toUnsignedIntSema(pt));
if (int > len: {
const mutate = &ip.global_error_set.mutate;
mutate.map.mutex.lock();
defer mutate.map.mutex.unlock();
mutate.map.mutex.lockUncancelable(io);
defer mutate.map.mutex.unlock(io);
break :len mutate.names.len;
} or int == 0)
return sema.fail(block, operand_src, "integer value '{d}' represents no error", .{int});
@@ -8361,10 +8452,14 @@ fn zirEnumLiteral(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
const pt = sema.pt;
const zcu = pt.zcu;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].str_tok;
const name = inst_data.get(sema.code);
return Air.internedToRef((try pt.intern(.{
.enum_literal = try zcu.intern_pool.getOrPutString(sema.gpa, pt.tid, name, .no_embedded_nulls),
.enum_literal = try zcu.intern_pool.getOrPutString(gpa, io, pt.tid, name, .no_embedded_nulls),
})));
}
@@ -8374,11 +8469,16 @@ fn zirDeclLiteral(sema: *Sema, block: *Block, inst: Zir.Inst.Index, do_coerce: b
const pt = sema.pt;
const zcu = pt.zcu;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
const src = block.nodeOffset(inst_data.src_node);
const extra = sema.code.extraData(Zir.Inst.Field, inst_data.payload_index).data;
const name = try zcu.intern_pool.getOrPutString(
sema.gpa,
gpa,
io,
pt.tid,
sema.code.nullTerminatedString(extra.field_name_start),
.no_embedded_nulls,
@@ -8915,7 +9015,11 @@ fn zirFunc(
) CompileError!Air.Inst.Ref {
const pt = sema.pt;
const zcu = pt.zcu;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const ip = &zcu.intern_pool;
const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
const extra = sema.code.extraData(Zir.Inst.Func, inst_data.payload_index);
const target = zcu.getTarget();
@@ -8970,7 +9074,7 @@ fn zirFunc(
block,
LazySrcLoc.unneeded,
cc_type.getNamespaceIndex(zcu),
try ip.getOrPutString(sema.gpa, pt.tid, "c", .no_embedded_nulls),
try ip.getOrPutString(gpa, io, pt.tid, "c", .no_embedded_nulls),
);
// The above should have errored.
@panic("std.builtin is corrupt");
@@ -9443,8 +9547,11 @@ fn funcCommon(
) CompileError!Air.Inst.Ref {
const pt = sema.pt;
const zcu = pt.zcu;
const gpa = sema.gpa;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const ip = &zcu.intern_pool;
const ret_ty_src = block.src(.{ .node_offset_fn_type_ret_ty = src_node_offset });
const cc_src = block.src(.{ .node_offset_fn_type_cc = src_node_offset });
const func_src = block.nodeOffset(src_node_offset);
@@ -9563,7 +9670,7 @@ fn funcCommon(
if (inferred_error_set) {
assert(has_body);
return .fromIntern(try ip.getFuncDeclIes(gpa, pt.tid, .{
return .fromIntern(try ip.getFuncDeclIes(gpa, io, pt.tid, .{
.owner_nav = sema.owner.unwrap().nav_val,
.param_types = param_types,
@@ -9583,7 +9690,7 @@ fn funcCommon(
}));
}
const func_ty = try ip.getFuncType(gpa, pt.tid, .{
const func_ty = try ip.getFuncType(gpa, io, pt.tid, .{
.param_types = param_types,
.noalias_bits = noalias_bits,
.comptime_bits = comptime_bits,
@@ -9595,7 +9702,7 @@ fn funcCommon(
});
if (has_body) {
return .fromIntern(try ip.getFuncDecl(gpa, pt.tid, .{
return .fromIntern(try ip.getFuncDecl(gpa, io, pt.tid, .{
.owner_nav = sema.owner.unwrap().nav_val,
.ty = func_ty,
.cc = cc,
@@ -9778,12 +9885,17 @@ fn zirFieldPtrLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro
const pt = sema.pt;
const zcu = pt.zcu;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
const src = block.nodeOffset(inst_data.src_node);
const field_name_src = block.src(.{ .node_offset_field_name = inst_data.src_node });
const extra = sema.code.extraData(Zir.Inst.Field, inst_data.payload_index).data;
const field_name = try zcu.intern_pool.getOrPutString(
sema.gpa,
gpa,
io,
pt.tid,
sema.code.nullTerminatedString(extra.field_name_start),
.no_embedded_nulls,
@@ -9798,12 +9910,17 @@ fn zirFieldPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
const pt = sema.pt;
const zcu = pt.zcu;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
const src = block.nodeOffset(inst_data.src_node);
const field_name_src = block.src(.{ .node_offset_field_name = inst_data.src_node });
const extra = sema.code.extraData(Zir.Inst.Field, inst_data.payload_index).data;
const field_name = try zcu.intern_pool.getOrPutString(
sema.gpa,
gpa,
io,
pt.tid,
sema.code.nullTerminatedString(extra.field_name_start),
.no_embedded_nulls,
@@ -9818,12 +9935,17 @@ fn zirStructInitFieldPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Compi
const pt = sema.pt;
const zcu = pt.zcu;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
const src = block.nodeOffset(inst_data.src_node);
const field_name_src = block.src(.{ .node_offset_field_name_init = inst_data.src_node });
const extra = sema.code.extraData(Zir.Inst.Field, inst_data.payload_index).data;
const field_name = try zcu.intern_pool.getOrPutString(
sema.gpa,
gpa,
io,
pt.tid,
sema.code.nullTerminatedString(extra.field_name_start),
.no_embedded_nulls,
@@ -13941,9 +14063,14 @@ fn zirEmbedFile(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
fn zirRetErrValueCode(sema: *Sema, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
const pt = sema.pt;
const zcu = pt.zcu;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].str_tok;
const name = try zcu.intern_pool.getOrPutString(
sema.gpa,
gpa,
io,
pt.tid,
inst_data.get(sema.code),
.no_embedded_nulls,
@@ -14379,6 +14506,10 @@ fn analyzeTupleCat(
) CompileError!Air.Inst.Ref {
const pt = sema.pt;
const zcu = pt.zcu;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const lhs_ty = sema.typeOf(lhs);
const rhs_ty = sema.typeOf(rhs);
const src = block.nodeOffset(src_node);
@@ -14434,7 +14565,7 @@ fn analyzeTupleCat(
break :rs runtime_src;
};
const tuple_ty: Type = .fromInterned(try zcu.intern_pool.getTupleType(zcu.gpa, pt.tid, .{
const tuple_ty: Type = .fromInterned(try zcu.intern_pool.getTupleType(gpa, io, pt.tid, .{
.types = types,
.values = values,
}));
@@ -14821,6 +14952,10 @@ fn analyzeTupleMul(
) CompileError!Air.Inst.Ref {
const pt = sema.pt;
const zcu = pt.zcu;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const operand_ty = sema.typeOf(operand);
const src = block.nodeOffset(src_node);
const len_src = block.src(.{ .node_offset_bin_rhs = src_node });
@@ -14856,7 +14991,7 @@ fn analyzeTupleMul(
break :rs runtime_src;
};
const tuple_ty: Type = .fromInterned(try zcu.intern_pool.getTupleType(zcu.gpa, pt.tid, .{
const tuple_ty: Type = .fromInterned(try zcu.intern_pool.getTupleType(gpa, io, pt.tid, .{
.types = types,
.values = values,
}));
@@ -16388,7 +16523,11 @@ fn zirAsm(
const pt = sema.pt;
const zcu = pt.zcu;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const ip = &zcu.intern_pool;
const extra = sema.code.extraData(Zir.Inst.Asm, extended.operand);
const src = block.nodeOffset(extra.data.src_node);
const ret_ty_src = block.src(.{ .node_offset_asm_ret_ty = extra.data.src_node });
@@ -16445,7 +16584,7 @@ fn zirAsm(
} else {
const inst = try sema.resolveInst(output.data.operand);
if (!sema.checkRuntimeValue(inst)) {
const output_name = try ip.getOrPutString(sema.gpa, pt.tid, name, .no_embedded_nulls);
const output_name = try ip.getOrPutString(gpa, io, pt.tid, name, .no_embedded_nulls);
return sema.failWithContainsReferenceToComptimeVar(block, output_src, output_name, "assembly output", .fromInterned(inst.toInterned().?));
}
arg.* = inst;
@@ -16476,7 +16615,7 @@ fn zirAsm(
const uncasted_arg = try sema.resolveInst(input.data.operand);
const name = sema.code.nullTerminatedString(input.data.name);
if (!sema.checkRuntimeValue(uncasted_arg)) {
const input_name = try ip.getOrPutString(sema.gpa, pt.tid, name, .no_embedded_nulls);
const input_name = try ip.getOrPutString(gpa, io, pt.tid, name, .no_embedded_nulls);
return sema.failWithContainsReferenceToComptimeVar(block, input_src, input_name, "assembly input", .fromInterned(uncasted_arg.toInterned().?));
}
const uncasted_arg_ty = sema.typeOf(uncasted_arg);
@@ -16500,7 +16639,6 @@ fn zirAsm(
const clobbers_val = try sema.resolveConstDefinedValue(block, src, clobbers, .{ .simple = .clobber });
needed_capacity += asm_source.len / 4 + 1;
const gpa = sema.gpa;
try sema.air_extra.ensureUnusedCapacity(gpa, needed_capacity);
const asm_air = try block.addInst(.{
.tag = .assembly,
@@ -17060,10 +17198,13 @@ fn zirBuiltinSrc(
const pt = sema.pt;
const zcu = pt.zcu;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const ip = &zcu.intern_pool;
const extra = sema.code.extraData(Zir.Inst.Src, extended.operand).data;
const fn_name = ip.getNav(zcu.funcInfo(sema.func_index).owner_nav).name;
const gpa = sema.gpa;
const file_scope = block.getFileScope(zcu);
const func_name_val = v: {
@@ -17106,7 +17247,7 @@ fn zirBuiltinSrc(
.val = try pt.intern(.{ .aggregate = .{
.ty = array_ty,
.storage = .{
.bytes = try ip.getOrPutString(gpa, pt.tid, module_name, .maybe_embedded_nulls),
.bytes = try ip.getOrPutString(gpa, io, pt.tid, module_name, .maybe_embedded_nulls),
},
} }),
} },
@@ -17132,7 +17273,7 @@ fn zirBuiltinSrc(
.val = try pt.intern(.{ .aggregate = .{
.ty = array_ty,
.storage = .{
.bytes = try ip.getOrPutString(gpa, pt.tid, file_name, .maybe_embedded_nulls),
.bytes = try ip.getOrPutString(gpa, io, pt.tid, file_name, .maybe_embedded_nulls),
},
} }),
} },
@@ -17161,8 +17302,11 @@ fn zirBuiltinSrc(
fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
const pt = sema.pt;
const zcu = pt.zcu;
const gpa = sema.gpa;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const ip = &zcu.intern_pool;
const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
const src = block.nodeOffset(inst_data.src_node);
const ty = try sema.resolveType(block, src, inst_data.operand);
@@ -17511,7 +17655,8 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
const enum_type = ip.loadEnumType(ty.toIntern());
const value_val = if (enum_type.values.len > 0)
try ip.getCoercedInts(
zcu.gpa,
gpa,
io,
pt.tid,
ip.indexToKey(enum_type.values.get(ip)[tag_index]).int,
.comptime_int_type,
@@ -17729,7 +17874,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
const field_ty = tuple_type.types.get(ip)[field_index];
const field_val = tuple_type.values.get(ip)[field_index];
const name_val = v: {
const field_name = try ip.getOrPutStringFmt(gpa, pt.tid, "{d}", .{field_index}, .no_embedded_nulls);
const field_name = try ip.getOrPutStringFmt(gpa, io, pt.tid, "{d}", .{field_index}, .no_embedded_nulls);
const field_name_len = field_name.length(ip);
const new_decl_ty = try pt.arrayType(.{
.len = field_name_len,
@@ -18752,10 +18897,15 @@ fn zirRetErrValue(
) CompileError!void {
const pt = sema.pt;
const zcu = pt.zcu;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].str_tok;
const src = block.tokenOffset(inst_data.src_tok);
const err_name = try zcu.intern_pool.getOrPutString(
sema.gpa,
gpa,
io,
pt.tid,
inst_data.get(sema.code),
.no_embedded_nulls,
@@ -19121,6 +19271,9 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
const pt = sema.pt;
const zcu = pt.zcu;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const ip = &zcu.intern_pool;
const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].ptr_type;
@@ -19158,7 +19311,7 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
const val = try sema.resolveConstDefinedValue(block, sentinel_src, coerced, .{ .simple = .pointer_sentinel });
try checkSentinelType(sema, block, sentinel_src, elem_ty);
if (val.canMutateComptimeVarState(zcu)) {
const sentinel_name = try ip.getOrPutString(sema.gpa, pt.tid, "sentinel", .no_embedded_nulls);
const sentinel_name = try ip.getOrPutString(gpa, io, pt.tid, "sentinel", .no_embedded_nulls);
return sema.failWithContainsReferenceToComptimeVar(block, sentinel_src, sentinel_name, "sentinel", val);
}
break :blk val.toIntern();
@@ -19463,15 +19616,18 @@ fn zirStructInit(
inst: Zir.Inst.Index,
is_ref: bool,
) CompileError!Air.Inst.Ref {
const gpa = sema.gpa;
const pt = sema.pt;
const zcu = pt.zcu;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const ip = &zcu.intern_pool;
const zir_datas = sema.code.instructions.items(.data);
const inst_data = zir_datas[@intFromEnum(inst)].pl_node;
const extra = sema.code.extraData(Zir.Inst.StructInit, inst_data.payload_index);
const src = block.nodeOffset(inst_data.src_node);
const pt = sema.pt;
const zcu = pt.zcu;
const ip = &zcu.intern_pool;
const first_item = sema.code.extraData(Zir.Inst.StructInit.Item, extra.end).data;
const first_field_type_data = zir_datas[@intFromEnum(first_item.field_type)].pl_node;
const first_field_type_extra = sema.code.extraData(Zir.Inst.FieldType, first_field_type_data.payload_index).data;
@@ -19513,6 +19669,7 @@ fn zirStructInit(
const field_type_extra = sema.code.extraData(Zir.Inst.FieldType, field_type_data.payload_index).data;
const field_name = try ip.getOrPutString(
gpa,
io,
pt.tid,
sema.code.nullTerminatedString(field_type_extra.name_start),
.no_embedded_nulls,
@@ -19554,6 +19711,7 @@ fn zirStructInit(
const field_type_extra = sema.code.extraData(Zir.Inst.FieldType, field_type_data.payload_index).data;
const field_name = try ip.getOrPutString(
gpa,
io,
pt.tid,
sema.code.nullTerminatedString(field_type_extra.name_start),
.no_embedded_nulls,
@@ -19797,8 +19955,11 @@ fn structInitAnon(
) CompileError!Air.Inst.Ref {
const pt = sema.pt;
const zcu = pt.zcu;
const gpa = sema.gpa;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const ip = &zcu.intern_pool;
const zir_datas = sema.code.instructions.items(.data);
const types = try sema.arena.alloc(InternPool.Index, extra_data.fields_len);
@@ -19828,7 +19989,7 @@ fn structInitAnon(
},
};
field_name.* = try zcu.intern_pool.getOrPutString(gpa, pt.tid, name, .no_embedded_nulls);
field_name.* = try zcu.intern_pool.getOrPutString(gpa, io, pt.tid, name, .no_embedded_nulls);
const init = try sema.resolveInst(item.data.init);
field_ty.* = sema.typeOf(init).toIntern();
@@ -19871,7 +20032,7 @@ fn structInitAnon(
break :hash hasher.final();
};
const tracked_inst = try block.trackZir(inst);
const struct_ty = switch (try ip.getStructType(gpa, pt.tid, .{
const struct_ty = switch (try ip.getStructType(gpa, io, pt.tid, .{
.layout = .auto,
.fields_len = extra_data.fields_len,
.known_non_opv = false,
@@ -20131,7 +20292,9 @@ fn arrayInitAnon(
) CompileError!Air.Inst.Ref {
const pt = sema.pt;
const zcu = pt.zcu;
const gpa = sema.gpa;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const ip = &zcu.intern_pool;
const types = try sema.arena.alloc(InternPool.Index, operands.len);
@@ -20180,7 +20343,7 @@ fn arrayInitAnon(
break :blk new_values;
};
const tuple_ty: Type = .fromInterned(try ip.getTupleType(gpa, pt.tid, .{
const tuple_ty: Type = .fromInterned(try ip.getTupleType(gpa, io, pt.tid, .{
.types = types,
.values = values_no_comptime,
}));
@@ -20247,7 +20410,11 @@ fn zirFieldTypeRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro
fn zirStructInitFieldType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
const pt = sema.pt;
const zcu = pt.zcu;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const ip = &zcu.intern_pool;
const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
const extra = sema.code.extraData(Zir.Inst.FieldType, inst_data.payload_index).data;
const ty_src = block.nodeOffset(inst_data.src_node);
@@ -20255,7 +20422,7 @@ fn zirStructInitFieldType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Comp
const wrapped_aggregate_ty = try sema.resolveTypeOrPoison(block, ty_src, extra.container_type) orelse return .generic_poison_type;
const aggregate_ty = wrapped_aggregate_ty.optEuBaseType(zcu);
const zir_field_name = sema.code.nullTerminatedString(extra.name_start);
const field_name = try ip.getOrPutString(sema.gpa, pt.tid, zir_field_name, .no_embedded_nulls);
const field_name = try ip.getOrPutString(gpa, io, pt.tid, zir_field_name, .no_embedded_nulls);
return sema.fieldType(block, aggregate_ty, field_name, field_name_src, ty_src);
}
@@ -20669,6 +20836,9 @@ fn zirReifyTuple(
) CompileError!Air.Inst.Ref {
const pt = sema.pt;
const zcu = pt.zcu;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
const operand_src = block.builtinCallArgSrc(extra.node, 0);
@@ -20691,7 +20861,7 @@ fn zirReifyTuple(
const field_values = try sema.arena.alloc(InternPool.Index, fields_len);
@memset(field_values, .none);
return .fromIntern(try zcu.intern_pool.getTupleType(zcu.gpa, pt.tid, .{
return .fromIntern(try zcu.intern_pool.getTupleType(gpa, io, pt.tid, .{
.types = field_types,
.values = field_values,
}));
@@ -20704,7 +20874,9 @@ fn zirReifyPointer(
) CompileError!Air.Inst.Ref {
const pt = sema.pt;
const zcu = pt.zcu;
const gpa = zcu.gpa;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const ip = &zcu.intern_pool;
const extra = sema.code.extraData(Zir.Inst.ReifyPointer, extended.operand).data;
@@ -20772,7 +20944,7 @@ fn zirReifyPointer(
}
try checkSentinelType(sema, block, sentinel_src, elem_ty);
if (sentinel.canMutateComptimeVarState(zcu)) {
const sentinel_name = try ip.getOrPutString(gpa, pt.tid, "sentinel", .no_embedded_nulls);
const sentinel_name = try ip.getOrPutString(gpa, io, pt.tid, "sentinel", .no_embedded_nulls);
return sema.failWithContainsReferenceToComptimeVar(block, sentinel_src, sentinel_name, "sentinel", sentinel);
}
}
@@ -20801,7 +20973,9 @@ fn zirReifyFn(
) CompileError!Air.Inst.Ref {
const pt = sema.pt;
const zcu = pt.zcu;
const gpa = zcu.gpa;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const ip = &zcu.intern_pool;
const extra = sema.code.extraData(Zir.Inst.ReifyFn, extended.operand).data;
@@ -20884,7 +21058,7 @@ fn zirReifyFn(
return sema.fail(block, param_attrs_src, "cannot reify function type with comptime-only return type '{f}'", .{ret_ty.fmt(pt)});
}
return .fromIntern(try ip.getFuncType(gpa, pt.tid, .{
return .fromIntern(try ip.getFuncType(gpa, io, pt.tid, .{
.param_types = param_types_ip,
.noalias_bits = noalias_bits,
.comptime_bits = 0,
@@ -20904,7 +21078,9 @@ fn zirReifyStruct(
) CompileError!Air.Inst.Ref {
const pt = sema.pt;
const zcu = pt.zcu;
const gpa = sema.gpa;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const ip = &zcu.intern_pool;
const name_strategy: Zir.Inst.NameStrategy = @enumFromInt(extended.small);
@@ -21079,7 +21255,7 @@ fn zirReifyStruct(
return sema.fail(block, field_attrs_src, "{t} struct fields cannot be marked comptime", .{layout});
}
const wip_ty = switch (try ip.getStructType(gpa, pt.tid, .{
const wip_ty = switch (try ip.getStructType(gpa, io, pt.tid, .{
.layout = layout,
.fields_len = @intCast(fields_len),
.known_non_opv = false,
@@ -21223,10 +21399,10 @@ fn zirReifyStruct(
}
if (backing_int_ty) |ty| {
try sema.checkBackingIntType(block, src, ty, fields_bit_sum);
wip_struct_type.setBackingIntType(ip, ty.toIntern());
wip_struct_type.setBackingIntType(ip, io, ty.toIntern());
} else {
const ty = try pt.intType(.unsigned, @intCast(fields_bit_sum));
wip_struct_type.setBackingIntType(ip, ty.toIntern());
wip_struct_type.setBackingIntType(ip, io, ty.toIntern());
}
}
@@ -21259,7 +21435,9 @@ fn zirReifyUnion(
) CompileError!Air.Inst.Ref {
const pt = sema.pt;
const zcu = pt.zcu;
const gpa = sema.gpa;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const ip = &zcu.intern_pool;
const name_strategy: Zir.Inst.NameStrategy = @enumFromInt(extended.small);
@@ -21400,7 +21578,7 @@ fn zirReifyUnion(
return sema.fail(block, field_attrs_src, "packed union fields cannot be aligned", .{});
}
const wip_ty = switch (try ip.getUnionType(gpa, pt.tid, .{
const wip_ty = switch (try ip.getUnionType(gpa, io, pt.tid, .{
.flags = .{
.layout = layout,
.status = .none,
@@ -21558,8 +21736,8 @@ fn zirReifyUnion(
}
}
loaded_union.setTagType(ip, enum_tag_ty);
loaded_union.setStatus(ip, .have_field_types);
loaded_union.setTagType(ip, io, enum_tag_ty);
loaded_union.setStatus(ip, io, .have_field_types);
const new_namespace_index = try pt.createNamespace(.{
.parent = block.namespace.toOptional(),
@@ -21590,7 +21768,9 @@ fn zirReifyEnum(
) CompileError!Air.Inst.Ref {
const pt = sema.pt;
const zcu = pt.zcu;
const gpa = sema.gpa;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const ip = &zcu.intern_pool;
const name_strategy: Zir.Inst.NameStrategy = @enumFromInt(extended.small);
@@ -21688,7 +21868,7 @@ fn zirReifyEnum(
std.hash.autoHash(&hasher, field_name);
}
const wip_ty = switch (try ip.getEnumType(gpa, pt.tid, .{
const wip_ty = switch (try ip.getEnumType(gpa, io, pt.tid, .{
.has_values = true,
.tag_mode = if (nonexhaustive) .nonexhaustive else .explicit,
.fields_len = @intCast(fields_len),
@@ -21844,13 +22024,16 @@ fn zirCVaStart(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData)
fn zirTypeName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
const pt = sema.pt;
const zcu = pt.zcu;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const ip = &zcu.intern_pool;
const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
const ty_src = block.builtinCallArgSrc(inst_data.src_node, 0);
const ty = try sema.resolveType(block, ty_src, inst_data.operand);
const type_name = try ip.getOrPutStringFmt(sema.gpa, pt.tid, "{f}", .{ty.fmt(pt)}, .no_embedded_nulls);
const type_name = try ip.getOrPutStringFmt(gpa, io, pt.tid, "{f}", .{ty.fmt(pt)}, .no_embedded_nulls);
return sema.addNullTerminatedStrLit(type_name);
}
@@ -22281,6 +22464,10 @@ fn ptrCastFull(
) CompileError!Air.Inst.Ref {
const pt = sema.pt;
const zcu = pt.zcu;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const operand_ty = sema.typeOf(operand);
try sema.checkPtrType(block, src, dest_ty, true);
@@ -22452,14 +22639,14 @@ fn ptrCastFull(
if (dest_info.sentinel == .none) break :check_sent;
if (src_info.flags.size == .c) break :check_sent;
if (src_info.sentinel != .none) {
const coerced_sent = try zcu.intern_pool.getCoerced(sema.gpa, pt.tid, src_info.sentinel, dest_info.child);
const coerced_sent = try zcu.intern_pool.getCoerced(gpa, io, pt.tid, src_info.sentinel, dest_info.child);
if (dest_info.sentinel == coerced_sent) break :check_sent;
}
if (is_array_ptr_to_slice) {
// [*]nT -> []T
const arr_ty: Type = .fromInterned(src_info.child);
if (arr_ty.sentinel(zcu)) |src_sentinel| {
const coerced_sent = try zcu.intern_pool.getCoerced(sema.gpa, pt.tid, src_sentinel.toIntern(), dest_info.child);
const coerced_sent = try zcu.intern_pool.getCoerced(gpa, io, pt.tid, src_sentinel.toIntern(), dest_info.child);
if (dest_info.sentinel == coerced_sent) break :check_sent;
}
}
@@ -23577,8 +23764,11 @@ fn resolveExportOptions(
) CompileError!Zcu.Export.Options {
const pt = sema.pt;
const zcu = pt.zcu;
const gpa = sema.gpa;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const ip = &zcu.intern_pool;
const export_options_ty = try sema.getBuiltinType(src, .ExportOptions);
const air_ref = try sema.resolveInst(zir_ref);
const options = try sema.coerce(block, export_options_ty, air_ref, src);
@@ -23588,21 +23778,21 @@ fn resolveExportOptions(
const section_src = block.src(.{ .init_field_section = src.offset.node_offset_builtin_call_arg.builtin_call_node });
const visibility_src = block.src(.{ .init_field_visibility = src.offset.node_offset_builtin_call_arg.builtin_call_node });
const name_operand = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "name", .no_embedded_nulls), name_src);
const name_operand = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, io, pt.tid, "name", .no_embedded_nulls), name_src);
const name = try sema.toConstString(block, name_src, name_operand, .{ .simple = .export_options });
const linkage_operand = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "linkage", .no_embedded_nulls), linkage_src);
const linkage_operand = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, io, pt.tid, "linkage", .no_embedded_nulls), linkage_src);
const linkage_val = try sema.resolveConstDefinedValue(block, linkage_src, linkage_operand, .{ .simple = .export_options });
const linkage = try sema.interpretBuiltinType(block, linkage_src, linkage_val, std.builtin.GlobalLinkage);
const section_operand = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "section", .no_embedded_nulls), section_src);
const section_operand = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, io, pt.tid, "section", .no_embedded_nulls), section_src);
const section_opt_val = try sema.resolveConstDefinedValue(block, section_src, section_operand, .{ .simple = .export_options });
const section = if (section_opt_val.optionalValue(zcu)) |section_val|
try sema.toConstString(block, section_src, Air.internedToRef(section_val.toIntern()), .{ .simple = .export_options })
else
null;
const visibility_operand = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "visibility", .no_embedded_nulls), visibility_src);
const visibility_operand = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, io, pt.tid, "visibility", .no_embedded_nulls), visibility_src);
const visibility_val = try sema.resolveConstDefinedValue(block, visibility_src, visibility_operand, .{ .simple = .export_options });
const visibility = try sema.interpretBuiltinType(block, visibility_src, visibility_val, std.builtin.SymbolVisibility);
@@ -23617,9 +23807,9 @@ fn resolveExportOptions(
}
return .{
.name = try ip.getOrPutString(gpa, pt.tid, name, .no_embedded_nulls),
.name = try ip.getOrPutString(gpa, io, pt.tid, name, .no_embedded_nulls),
.linkage = linkage,
.section = try ip.getOrPutStringOpt(gpa, pt.tid, section, .no_embedded_nulls),
.section = try ip.getOrPutStringOpt(gpa, io, pt.tid, section, .no_embedded_nulls),
.visibility = visibility,
};
}
@@ -25345,8 +25535,11 @@ fn zirMemcpy(
fn zirMemset(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
const pt = sema.pt;
const zcu = pt.zcu;
const gpa = sema.gpa;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const ip = &zcu.intern_pool;
const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
const src = block.nodeOffset(inst_data.src_node);
@@ -25385,7 +25578,7 @@ fn zirMemset(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void
const elem = try sema.coerce(block, dest_elem_ty, uncoerced_elem, value_src);
const runtime_src = rs: {
const len_air_ref = try sema.fieldVal(block, src, dest_ptr, try ip.getOrPutString(gpa, pt.tid, "len", .no_embedded_nulls), dest_src);
const len_air_ref = try sema.fieldVal(block, src, dest_ptr, try ip.getOrPutString(gpa, io, pt.tid, "len", .no_embedded_nulls), dest_src);
const len_val = (try sema.resolveDefinedValue(block, dest_src, len_air_ref)) orelse break :rs dest_src;
const len_u64 = try len_val.toUnsignedIntSema(pt);
const len = try sema.usizeCast(block, dest_src, len_u64);
@@ -25438,7 +25631,11 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
const pt = sema.pt;
const zcu = pt.zcu;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const ip = &zcu.intern_pool;
const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
const extra = sema.code.extraData(Zir.Inst.FuncFancy, inst_data.payload_index);
const target = zcu.getTarget();
@@ -25482,7 +25679,7 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
block,
LazySrcLoc.unneeded,
cc_type.getNamespaceIndex(zcu),
try ip.getOrPutString(sema.gpa, pt.tid, "c", .no_embedded_nulls),
try ip.getOrPutString(gpa, io, pt.tid, "c", .no_embedded_nulls),
);
// The above should have errored.
@panic("std.builtin is corrupt");
@@ -25648,8 +25845,11 @@ fn resolvePrefetchOptions(
) CompileError!std.builtin.PrefetchOptions {
const pt = sema.pt;
const zcu = pt.zcu;
const gpa = sema.gpa;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const ip = &zcu.intern_pool;
const options_ty = try sema.getBuiltinType(src, .PrefetchOptions);
const options = try sema.coerce(block, options_ty, try sema.resolveInst(zir_ref), src);
@@ -25657,13 +25857,13 @@ fn resolvePrefetchOptions(
const locality_src = block.src(.{ .init_field_locality = src.offset.node_offset_builtin_call_arg.builtin_call_node });
const cache_src = block.src(.{ .init_field_cache = src.offset.node_offset_builtin_call_arg.builtin_call_node });
const rw = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "rw", .no_embedded_nulls), rw_src);
const rw = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, io, pt.tid, "rw", .no_embedded_nulls), rw_src);
const rw_val = try sema.resolveConstDefinedValue(block, rw_src, rw, .{ .simple = .prefetch_options });
const locality = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "locality", .no_embedded_nulls), locality_src);
const locality = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, io, pt.tid, "locality", .no_embedded_nulls), locality_src);
const locality_val = try sema.resolveConstDefinedValue(block, locality_src, locality, .{ .simple = .prefetch_options });
const cache = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "cache", .no_embedded_nulls), cache_src);
const cache = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, io, pt.tid, "cache", .no_embedded_nulls), cache_src);
const cache_val = try sema.resolveConstDefinedValue(block, cache_src, cache, .{ .simple = .prefetch_options });
return std.builtin.PrefetchOptions{
@@ -25717,8 +25917,11 @@ fn resolveExternOptions(
} {
const pt = sema.pt;
const zcu = pt.zcu;
const gpa = sema.gpa;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const ip = &zcu.intern_pool;
const options_inst = try sema.resolveInst(zir_ref);
const extern_options_ty = try sema.getBuiltinType(src, .ExternOptions);
const options = try sema.coerce(block, extern_options_ty, options_inst, src);
@@ -25731,21 +25934,21 @@ fn resolveExternOptions(
const dll_import_src = block.src(.{ .init_field_dll_import = src.offset.node_offset_builtin_call_arg.builtin_call_node });
const relocation_src = block.src(.{ .init_field_relocation = src.offset.node_offset_builtin_call_arg.builtin_call_node });
const name_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "name", .no_embedded_nulls), name_src);
const name_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, io, pt.tid, "name", .no_embedded_nulls), name_src);
const name = try sema.toConstString(block, name_src, name_ref, .{ .simple = .extern_options });
const library_name_inst = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "library_name", .no_embedded_nulls), library_src);
const library_name_inst = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, io, pt.tid, "library_name", .no_embedded_nulls), library_src);
const library_name_val = try sema.resolveConstDefinedValue(block, library_src, library_name_inst, .{ .simple = .extern_options });
const linkage_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "linkage", .no_embedded_nulls), linkage_src);
const linkage_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, io, pt.tid, "linkage", .no_embedded_nulls), linkage_src);
const linkage_val = try sema.resolveConstDefinedValue(block, linkage_src, linkage_ref, .{ .simple = .extern_options });
const linkage = try sema.interpretBuiltinType(block, linkage_src, linkage_val, std.builtin.GlobalLinkage);
const visibility_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "visibility", .no_embedded_nulls), visibility_src);
const visibility_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, io, pt.tid, "visibility", .no_embedded_nulls), visibility_src);
const visibility_val = try sema.resolveConstDefinedValue(block, visibility_src, visibility_ref, .{ .simple = .extern_options });
const visibility = try sema.interpretBuiltinType(block, visibility_src, visibility_val, std.builtin.SymbolVisibility);
const is_thread_local = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "is_thread_local", .no_embedded_nulls), thread_local_src);
const is_thread_local = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, io, pt.tid, "is_thread_local", .no_embedded_nulls), thread_local_src);
const is_thread_local_val = try sema.resolveConstDefinedValue(block, thread_local_src, is_thread_local, .{ .simple = .extern_options });
const library_name = if (library_name_val.optionalValue(zcu)) |library_name_payload| library_name: {
@@ -25757,10 +25960,10 @@ fn resolveExternOptions(
break :library_name library_name;
} else null;
const is_dll_import_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "is_dll_import", .no_embedded_nulls), dll_import_src);
const is_dll_import_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, io, pt.tid, "is_dll_import", .no_embedded_nulls), dll_import_src);
const is_dll_import_val = try sema.resolveConstDefinedValue(block, dll_import_src, is_dll_import_ref, .{ .simple = .extern_options });
const relocation_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "relocation", .no_embedded_nulls), relocation_src);
const relocation_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, io, pt.tid, "relocation", .no_embedded_nulls), relocation_src);
const relocation_val = try sema.resolveConstDefinedValue(block, relocation_src, relocation_ref, .{ .simple = .extern_options });
const relocation = try sema.interpretBuiltinType(block, relocation_src, relocation_val, std.builtin.ExternOptions.Relocation);
@@ -25773,8 +25976,8 @@ fn resolveExternOptions(
}
return .{
.name = try ip.getOrPutString(gpa, pt.tid, name, .no_embedded_nulls),
.library_name = try ip.getOrPutStringOpt(gpa, pt.tid, library_name, .no_embedded_nulls),
.name = try ip.getOrPutString(gpa, io, pt.tid, name, .no_embedded_nulls),
.library_name = try ip.getOrPutStringOpt(gpa, io, pt.tid, library_name, .no_embedded_nulls),
.linkage = linkage,
.visibility = visibility,
.is_thread_local = is_thread_local_val.toBool(),
@@ -25919,7 +26122,9 @@ fn zirInComptime(
fn zirBuiltinValue(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
const pt = sema.pt;
const zcu = pt.zcu;
const gpa = zcu.gpa;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const ip = &zcu.intern_pool;
const src_node: std.zig.Ast.Node.Offset = @enumFromInt(@as(i32, @bitCast(extended.operand)));
@@ -25955,7 +26160,7 @@ fn zirBuiltinValue(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstD
block,
src,
callconv_ty.getNamespaceIndex(zcu),
try ip.getOrPutString(gpa, pt.tid, "c", .no_embedded_nulls),
try ip.getOrPutString(gpa, io, pt.tid, "c", .no_embedded_nulls),
) orelse @panic("std.builtin is corrupt");
},
.calling_convention_inline => {
@@ -26492,11 +26697,12 @@ fn preparePanicId(sema: *Sema, src: LazySrcLoc, panic_id: Zcu.SimplePanicId) !vo
fn getPanicIdFunc(sema: *Sema, src: LazySrcLoc, panic_id: Zcu.SimplePanicId) !InternPool.Index {
const zcu = sema.pt.zcu;
const io = zcu.comp.io;
try sema.ensureMemoizedStateResolved(src, .panic);
const panic_fn_index = zcu.builtin_decl_values.get(panic_id.toBuiltin());
switch (sema.owner.unwrap()) {
.@"comptime", .nav_ty, .nav_val, .type, .memoized_state => {},
.func => |owner_func| zcu.intern_pool.funcSetHasErrorTrace(owner_func, true),
.func => |owner_func| zcu.intern_pool.funcSetHasErrorTrace(io, owner_func, true),
}
return panic_fn_index;
}
@@ -28539,10 +28745,15 @@ fn coerceExtra(
inst_src: LazySrcLoc,
opts: CoerceOpts,
) CoersionError!Air.Inst.Ref {
if (dest_ty.isGenericPoison()) return inst;
const pt = sema.pt;
const zcu = pt.zcu;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const ip = &zcu.intern_pool;
if (dest_ty.isGenericPoison()) return inst;
const dest_ty_src = inst_src; // TODO better source location
try dest_ty.resolveFields(pt);
const inst_ty = sema.typeOf(inst);
@@ -28904,7 +29115,7 @@ fn coerceExtra(
return switch (zcu.intern_pool.indexToKey(val.toIntern())) {
.undef => try pt.undefRef(dest_ty),
.int => |int| Air.internedToRef(
try zcu.intern_pool.getCoercedInts(zcu.gpa, pt.tid, int, dest_ty.toIntern()),
try zcu.intern_pool.getCoercedInts(gpa, io, pt.tid, int, dest_ty.toIntern()),
),
else => unreachable,
};
@@ -30070,6 +30281,10 @@ fn coerceInMemoryAllowedPtrs(
) !InMemoryCoercionResult {
const pt = sema.pt;
const zcu = pt.zcu;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const dest_info = dest_ptr_ty.ptrInfo(zcu);
const src_info = src_ptr_ty.ptrInfo(zcu);
@@ -30175,7 +30390,7 @@ fn coerceInMemoryAllowedPtrs(
const ds = dest_info.sentinel;
if (ss == .none and ds == .none) break :ok true;
if (ss != .none and ds != .none) {
if (ds == try zcu.intern_pool.getCoerced(sema.gpa, pt.tid, ss, dest_info.child)) break :ok true;
if (ds == try zcu.intern_pool.getCoerced(gpa, io, pt.tid, ss, dest_info.child)) break :ok true;
}
if (src_info.flags.size == .c) break :ok true;
if (!dest_is_mut and dest_info.sentinel == .none) break :ok true;
@@ -33086,6 +33301,9 @@ fn resolvePeerTypesInner(
) !PeerResolveResult {
const pt = sema.pt;
const zcu = pt.zcu;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const ip = &zcu.intern_pool;
var strat_reason: usize = 0;
@@ -33412,8 +33630,8 @@ fn resolvePeerTypesInner(
}).toIntern();
if (ptr_info.sentinel != .none and peer_info.sentinel != .none) {
const peer_sent = try ip.getCoerced(sema.gpa, pt.tid, ptr_info.sentinel, ptr_info.child);
const ptr_sent = try ip.getCoerced(sema.gpa, pt.tid, peer_info.sentinel, ptr_info.child);
const peer_sent = try ip.getCoerced(gpa, io, pt.tid, ptr_info.sentinel, ptr_info.child);
const ptr_sent = try ip.getCoerced(gpa, io, pt.tid, peer_info.sentinel, ptr_info.child);
if (ptr_sent == peer_sent) {
ptr_info.sentinel = ptr_sent;
} else {
@@ -33715,8 +33933,8 @@ fn resolvePeerTypesInner(
no_sentinel: {
if (peer_sentinel == .none) break :no_sentinel;
if (cur_sentinel == .none) break :no_sentinel;
const peer_sent_coerced = try ip.getCoerced(sema.gpa, pt.tid, peer_sentinel, sentinel_ty);
const cur_sent_coerced = try ip.getCoerced(sema.gpa, pt.tid, cur_sentinel, sentinel_ty);
const peer_sent_coerced = try ip.getCoerced(gpa, io, pt.tid, peer_sentinel, sentinel_ty);
const cur_sent_coerced = try ip.getCoerced(gpa, io, pt.tid, cur_sentinel, sentinel_ty);
if (peer_sent_coerced != cur_sent_coerced) break :no_sentinel;
// Sentinels match
if (ptr_info.flags.size == .one) switch (ip.indexToKey(ptr_info.child)) {
@@ -34081,7 +34299,7 @@ fn resolvePeerTypesInner(
else => |result| {
const result_buf = try sema.arena.create(PeerResolveResult);
result_buf.* = result;
const field_name = try ip.getOrPutStringFmt(sema.gpa, pt.tid, "{d}", .{field_index}, .no_embedded_nulls);
const field_name = try ip.getOrPutStringFmt(gpa, io, pt.tid, "{d}", .{field_index}, .no_embedded_nulls);
// The error info needs the field types, but we can't reuse sub_peer_tys
// since the recursive call may have clobbered it.
@@ -34136,7 +34354,7 @@ fn resolvePeerTypesInner(
field_val.* = if (comptime_val) |v| v.toIntern() else .none;
}
const final_ty = try ip.getTupleType(zcu.gpa, pt.tid, .{
const final_ty = try ip.getTupleType(gpa, io, pt.tid, .{
.types = field_types,
.values = field_vals,
});
@@ -34274,6 +34492,7 @@ pub fn resolveStructAlignment(
) SemaError!void {
const pt = sema.pt;
const zcu = pt.zcu;
const io = zcu.comp.io;
const ip = &zcu.intern_pool;
const target = zcu.getTarget();
@@ -34287,15 +34506,15 @@ pub fn resolveStructAlignment(
// We'll guess "pointer-aligned", if the struct has an
// underaligned pointer field then some allocations
// might require explicit alignment.
if (struct_type.assumePointerAlignedIfFieldTypesWip(ip, ptr_align)) return;
if (struct_type.assumePointerAlignedIfFieldTypesWip(ip, io, ptr_align)) return;
try sema.resolveStructFieldTypes(ty, struct_type);
// We'll guess "pointer-aligned", if the struct has an
// underaligned pointer field then some allocations
// might require explicit alignment.
if (struct_type.assumePointerAlignedIfWip(ip, ptr_align)) return;
defer struct_type.clearAlignmentWip(ip);
if (struct_type.assumePointerAlignedIfWip(ip, io, ptr_align)) return;
defer struct_type.clearAlignmentWip(ip, io);
// No `zcu.trackUnitSema` calls, since this phase isn't really doing any semantic analysis.
// It's just triggering *other* analysis, alongside a simple loop over already-resolved info.
@@ -34314,13 +34533,14 @@ pub fn resolveStructAlignment(
alignment = alignment.maxStrict(field_align);
}
struct_type.setAlignment(ip, alignment);
struct_type.setAlignment(ip, io, alignment);
}
pub fn resolveStructLayout(sema: *Sema, ty: Type) SemaError!void {
const pt = sema.pt;
const zcu = pt.zcu;
const ip = &zcu.intern_pool;
const io = zcu.comp.io;
const struct_type = zcu.typeToStruct(ty) orelse return;
assert(sema.owner.unwrap().type == ty.toIntern());
@@ -34341,7 +34561,7 @@ pub fn resolveStructLayout(sema: *Sema, ty: Type) SemaError!void {
return;
}
if (struct_type.setLayoutWip(ip)) {
if (struct_type.setLayoutWip(ip, io)) {
const msg = try sema.errMsg(
ty.srcLoc(zcu),
"struct '{f}' depends on itself",
@@ -34349,7 +34569,7 @@ pub fn resolveStructLayout(sema: *Sema, ty: Type) SemaError!void {
);
return sema.failWithOwnedErrorMsg(null, msg);
}
defer struct_type.clearLayoutWip(ip);
defer struct_type.clearLayoutWip(ip, io);
const aligns = try sema.arena.alloc(Alignment, struct_type.field_types.len);
const sizes = try sema.arena.alloc(u64, struct_type.field_types.len);
@@ -34468,7 +34688,7 @@ pub fn resolveStructLayout(sema: *Sema, ty: Type) SemaError!void {
);
return sema.failWithOwnedErrorMsg(null, msg);
};
struct_type.setLayoutResolved(ip, size, big_align);
struct_type.setLayoutResolved(ip, io, size, big_align);
_ = try ty.comptimeOnlySema(pt);
}
@@ -34478,7 +34698,9 @@ fn backingIntType(
) CompileError!void {
const pt = sema.pt;
const zcu = pt.zcu;
const gpa = zcu.gpa;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const ip = &zcu.intern_pool;
var analysis_arena = std.heap.ArenaAllocator.init(gpa);
@@ -34546,13 +34768,13 @@ fn backingIntType(
};
try sema.checkBackingIntType(&block, backing_int_src, backing_int_ty, fields_bit_sum);
struct_type.setBackingIntType(ip, backing_int_ty.toIntern());
struct_type.setBackingIntType(ip, io, backing_int_ty.toIntern());
} else {
if (fields_bit_sum > std.math.maxInt(u16)) {
return sema.fail(&block, block.nodeOffset(.zero), "size of packed struct '{d}' exceeds maximum bit width of 65535", .{fields_bit_sum});
}
const backing_int_ty = try pt.intType(.unsigned, @intCast(fields_bit_sum));
struct_type.setBackingIntType(ip, backing_int_ty.toIntern());
struct_type.setBackingIntType(ip, io, backing_int_ty.toIntern());
}
try sema.flushExports();
@@ -34620,6 +34842,7 @@ pub fn resolveUnionAlignment(
) SemaError!void {
const pt = sema.pt;
const zcu = pt.zcu;
const io = zcu.comp.io;
const ip = &zcu.intern_pool;
const target = zcu.getTarget();
@@ -34632,7 +34855,7 @@ pub fn resolveUnionAlignment(
// We'll guess "pointer-aligned", if the union has an
// underaligned pointer field then some allocations
// might require explicit alignment.
if (union_type.assumePointerAlignedIfFieldTypesWip(ip, ptr_align)) return;
if (union_type.assumePointerAlignedIfFieldTypesWip(ip, io, ptr_align)) return;
try sema.resolveUnionFieldTypes(ty, union_type);
@@ -34653,12 +34876,13 @@ pub fn resolveUnionAlignment(
max_align = max_align.max(field_align);
}
union_type.setAlignment(ip, max_align);
union_type.setAlignment(ip, io, max_align);
}
/// This logic must be kept in sync with `Type.getUnionLayout`.
pub fn resolveUnionLayout(sema: *Sema, ty: Type) SemaError!void {
const pt = sema.pt;
const io = pt.zcu.comp.io;
const ip = &pt.zcu.intern_pool;
try sema.resolveUnionFieldTypes(ty, ip.loadUnionType(ty.ip_index));
@@ -34682,9 +34906,9 @@ pub fn resolveUnionLayout(sema: *Sema, ty: Type) SemaError!void {
.have_layout, .fully_resolved_wip, .fully_resolved => return,
}
errdefer union_type.setStatusIfLayoutWip(ip, old_flags.status);
errdefer union_type.setStatusIfLayoutWip(ip, io, old_flags.status);
union_type.setStatus(ip, .layout_wip);
union_type.setStatus(ip, io, .layout_wip);
// No `zcu.trackUnitSema` calls, since this phase isn't really doing any semantic analysis.
// It's just triggering *other* analysis, alongside a simple loop over already-resolved info.
@@ -34765,7 +34989,7 @@ pub fn resolveUnionLayout(sema: *Sema, ty: Type) SemaError!void {
);
return sema.failWithOwnedErrorMsg(null, msg);
};
union_type.setHaveLayout(ip, casted_size, padding, alignment);
union_type.setHaveLayout(ip, io, casted_size, padding, alignment);
if (union_type.flagsUnordered(ip).assumed_runtime_bits and !(try ty.hasRuntimeBitsSema(pt))) {
const msg = try sema.errMsg(
@@ -34797,13 +35021,14 @@ pub fn resolveStructFully(sema: *Sema, ty: Type) SemaError!void {
const pt = sema.pt;
const zcu = pt.zcu;
const io = zcu.comp.io;
const ip = &zcu.intern_pool;
const struct_type = zcu.typeToStruct(ty).?;
assert(sema.owner.unwrap().type == ty.toIntern());
if (struct_type.setFullyResolved(ip)) return;
errdefer struct_type.clearFullyResolved(ip);
if (struct_type.setFullyResolved(ip, io)) return;
errdefer struct_type.clearFullyResolved(ip, io);
// No `zcu.trackUnitSema` calls, since this phase isn't really doing any semantic analysis.
// It's just triggering *other* analysis, alongside a simple loop over already-resolved info.
@@ -34823,6 +35048,7 @@ pub fn resolveUnionFully(sema: *Sema, ty: Type) SemaError!void {
const pt = sema.pt;
const zcu = pt.zcu;
const io = zcu.comp.io;
const ip = &zcu.intern_pool;
const union_obj = zcu.typeToUnion(ty).?;
@@ -34841,14 +35067,14 @@ pub fn resolveUnionFully(sema: *Sema, ty: Type) SemaError!void {
// make sure pointer fields get their child types resolved as well.
// See also similar code for structs.
const prev_status = union_obj.flagsUnordered(ip).status;
errdefer union_obj.setStatus(ip, prev_status);
errdefer union_obj.setStatus(ip, io, prev_status);
union_obj.setStatus(ip, .fully_resolved_wip);
union_obj.setStatus(ip, io, .fully_resolved_wip);
for (0..union_obj.field_types.len) |field_index| {
const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_index]);
try field_ty.resolveFully(pt);
}
union_obj.setStatus(ip, .fully_resolved);
union_obj.setStatus(ip, io, .fully_resolved);
}
// And let's not forget comptime-only status.
@@ -34862,13 +35088,14 @@ pub fn resolveStructFieldTypes(
) SemaError!void {
const pt = sema.pt;
const zcu = pt.zcu;
const io = zcu.comp.io;
const ip = &zcu.intern_pool;
assert(sema.owner.unwrap().type == ty);
if (struct_type.haveFieldTypes(ip)) return;
if (struct_type.setFieldTypesWip(ip)) {
if (struct_type.setFieldTypesWip(ip, io)) {
const msg = try sema.errMsg(
Type.fromInterned(ty).srcLoc(zcu),
"struct '{f}' depends on itself",
@@ -34876,7 +35103,7 @@ pub fn resolveStructFieldTypes(
);
return sema.failWithOwnedErrorMsg(null, msg);
}
defer struct_type.clearFieldTypesWip(ip);
defer struct_type.clearFieldTypesWip(ip, io);
// can't happen earlier than this because we only want the progress node if not already resolved
const tracked_unit = zcu.trackUnitSema(struct_type.name.toSlice(ip), null);
@@ -34891,6 +35118,7 @@ pub fn resolveStructFieldTypes(
pub fn resolveStructFieldInits(sema: *Sema, ty: Type) SemaError!void {
const pt = sema.pt;
const zcu = pt.zcu;
const io = zcu.comp.io;
const ip = &zcu.intern_pool;
const struct_type = zcu.typeToStruct(ty) orelse return;
@@ -34901,7 +35129,7 @@ pub fn resolveStructFieldInits(sema: *Sema, ty: Type) SemaError!void {
try sema.resolveStructLayout(ty);
if (struct_type.setInitsWip(ip)) {
if (struct_type.setInitsWip(ip, io)) {
const msg = try sema.errMsg(
ty.srcLoc(zcu),
"struct '{f}' depends on itself",
@@ -34909,7 +35137,7 @@ pub fn resolveStructFieldInits(sema: *Sema, ty: Type) SemaError!void {
);
return sema.failWithOwnedErrorMsg(null, msg);
}
defer struct_type.clearInitsWip(ip);
defer struct_type.clearInitsWip(ip, io);
// can't happen earlier than this because we only want the progress node if not already resolved
const tracked_unit = zcu.trackUnitSema(struct_type.name.toSlice(ip), null);
@@ -34919,12 +35147,13 @@ pub fn resolveStructFieldInits(sema: *Sema, ty: Type) SemaError!void {
error.AnalysisFail, error.OutOfMemory, error.Canceled => |e| return e,
error.ComptimeBreak, error.ComptimeReturn => unreachable,
};
struct_type.setHaveFieldInits(ip);
struct_type.setHaveFieldInits(ip, io);
}
pub fn resolveUnionFieldTypes(sema: *Sema, ty: Type, union_type: InternPool.LoadedUnionType) SemaError!void {
const pt = sema.pt;
const zcu = pt.zcu;
const io = zcu.comp.io;
const ip = &zcu.intern_pool;
assert(sema.owner.unwrap().type == ty.toIntern());
@@ -34947,13 +35176,13 @@ pub fn resolveUnionFieldTypes(sema: *Sema, ty: Type, union_type: InternPool.Load
const tracked_unit = zcu.trackUnitSema(union_type.name.toSlice(ip), null);
defer tracked_unit.end(zcu);
union_type.setStatus(ip, .field_types_wip);
errdefer union_type.setStatus(ip, .none);
union_type.setStatus(ip, io, .field_types_wip);
errdefer union_type.setStatus(ip, io, .none);
sema.unionFields(ty.toIntern(), union_type) catch |err| switch (err) {
error.AnalysisFail, error.OutOfMemory, error.Canceled => |e| return e,
error.ComptimeBreak, error.ComptimeReturn => unreachable,
};
union_type.setStatus(ip, .have_field_types);
union_type.setStatus(ip, io, .have_field_types);
}
/// Returns a normal error set corresponding to the fully populated inferred
@@ -35055,11 +35284,14 @@ fn resolveAdHocInferredErrorSet(
) CompileError!InternPool.Index {
const pt = sema.pt;
const zcu = pt.zcu;
const gpa = sema.gpa;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const ip = &zcu.intern_pool;
const new_ty = try resolveAdHocInferredErrorSetTy(sema, block, src, ip.typeOf(value));
if (new_ty == .none) return value;
return ip.getCoerced(gpa, pt.tid, value, new_ty);
return ip.getCoerced(gpa, io, pt.tid, value, new_ty);
}
fn resolveAdHocInferredErrorSetTy(
@@ -35159,8 +35391,11 @@ fn structFields(
) CompileError!void {
const pt = sema.pt;
const zcu = pt.zcu;
const gpa = zcu.gpa;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const ip = &zcu.intern_pool;
const namespace_index = struct_type.namespace;
const zir = zcu.namespacePtr(namespace_index).fileScope(zcu).zir.?;
const zir_index = struct_type.zir_index.resolve(ip) orelse return error.AnalysisFail;
@@ -35173,7 +35408,7 @@ fn structFields(
return;
},
.auto, .@"extern" => {
struct_type.setLayoutResolved(ip, 0, .none);
struct_type.setLayoutResolved(ip, io, 0, .none);
return;
},
};
@@ -35245,7 +35480,7 @@ fn structFields(
extra_index += 1;
// This string needs to outlive the ZIR code.
const field_name = try ip.getOrPutString(gpa, pt.tid, field_name_zir, .no_embedded_nulls);
const field_name = try ip.getOrPutString(gpa, io, pt.tid, field_name_zir, .no_embedded_nulls);
assert(struct_type.addFieldName(ip, field_name) == null);
if (has_align) {
@@ -35345,8 +35580,8 @@ fn structFields(
extra_index += zir_field.init_body_len;
}
struct_type.clearFieldTypesWip(ip);
if (!any_inits) struct_type.setHaveFieldInits(ip);
struct_type.clearFieldTypesWip(ip, io);
if (!any_inits) struct_type.setHaveFieldInits(ip, io);
try sema.flushExports();
}
@@ -35485,8 +35720,11 @@ fn unionFields(
const pt = sema.pt;
const zcu = pt.zcu;
const gpa = zcu.gpa;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const ip = &zcu.intern_pool;
const zir = zcu.namespacePtr(union_type.namespace).fileScope(zcu).zir.?;
const zir_index = union_type.zir_index.resolve(ip) orelse return error.AnalysisFail;
const extended = zir.instructions.items(.data)[@intFromEnum(zir_index)].extended;
@@ -35595,7 +35833,7 @@ fn unionFields(
.enum_type => ip.loadEnumType(provided_ty.toIntern()),
else => return sema.fail(&block_scope, tag_ty_src, "expected enum tag type, found '{f}'", .{provided_ty.fmt(pt)}),
};
union_type.setTagType(ip, provided_ty.toIntern());
union_type.setTagType(ip, io, provided_ty.toIntern());
// The fields of the union must match the enum exactly.
// A flag per field is used to check for missing and extraneous fields.
explicit_tags_seen = try sema.arena.alloc(bool, enum_type.names.len);
@@ -35727,7 +35965,7 @@ fn unionFields(
}
// This string needs to outlive the ZIR code.
const field_name = try ip.getOrPutString(gpa, pt.tid, field_name_zir, .no_embedded_nulls);
const field_name = try ip.getOrPutString(gpa, io, pt.tid, field_name_zir, .no_embedded_nulls);
if (enum_field_names.len != 0) {
enum_field_names[field_i] = field_name;
}
@@ -35871,10 +36109,10 @@ fn unionFields(
}
} else if (enum_field_vals.count() > 0) {
const enum_ty = try sema.generateUnionTagTypeNumbered(&block_scope, enum_field_names, enum_field_vals.keys(), union_ty, union_type.name);
union_type.setTagType(ip, enum_ty);
union_type.setTagType(ip, io, enum_ty);
} else {
const enum_ty = try sema.generateUnionTagTypeSimple(&block_scope, enum_field_names, union_ty, union_type.name);
union_type.setTagType(ip, enum_ty);
union_type.setTagType(ip, io, enum_ty);
}
try sema.flushExports();
@@ -35890,18 +36128,21 @@ fn generateUnionTagTypeNumbered(
) !InternPool.Index {
const pt = sema.pt;
const zcu = pt.zcu;
const gpa = sema.gpa;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const ip = &zcu.intern_pool;
const name = try ip.getOrPutStringFmt(
gpa,
io,
pt.tid,
"@typeInfo({f}).@\"union\".tag_type.?",
.{union_name.fmt(ip)},
.no_embedded_nulls,
);
const enum_ty = try ip.getGeneratedTagEnumType(gpa, pt.tid, .{
const enum_ty = try ip.getGeneratedTagEnumType(gpa, io, pt.tid, .{
.name = name,
.owner_union_ty = union_type,
.tag_ty = if (enum_field_vals.len == 0)
@@ -35926,18 +36167,21 @@ fn generateUnionTagTypeSimple(
) !InternPool.Index {
const pt = sema.pt;
const zcu = pt.zcu;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const ip = &zcu.intern_pool;
const gpa = sema.gpa;
const name = try ip.getOrPutStringFmt(
gpa,
io,
pt.tid,
"@typeInfo({f}).@\"union\".tag_type.?",
.{union_name.fmt(ip)},
.no_embedded_nulls,
);
const enum_ty = try ip.getGeneratedTagEnumType(gpa, pt.tid, .{
const enum_ty = try ip.getGeneratedTagEnumType(gpa, io, pt.tid, .{
.name = name,
.owner_union_ty = union_type,
.tag_ty = (try pt.smallestUnsignedInt(enum_field_names.len -| 1)).toIntern(),
@@ -35958,7 +36202,11 @@ fn generateUnionTagTypeSimple(
pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value {
const pt = sema.pt;
const zcu = pt.zcu;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const ip = &zcu.intern_pool;
return switch (ty.toIntern()) {
.u0_type,
.i0_type,
@@ -36302,7 +36550,8 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value {
(try pt.intValue(.fromInterned(enum_type.tag_ty), 0)).toIntern()
else
try ip.getCoercedInts(
zcu.gpa,
gpa,
io,
pt.tid,
ip.indexToKey(enum_type.values.get(ip)[0]).int,
enum_type.tag_ty,
@@ -36936,12 +37185,16 @@ fn checkRuntimeValue(sema: *Sema, ptr: Air.Inst.Ref) bool {
fn validateRuntimeValue(sema: *Sema, block: *Block, val_src: LazySrcLoc, val: Air.Inst.Ref) CompileError!void {
if (sema.checkRuntimeValue(val)) return;
return sema.failWithOwnedErrorMsg(block, msg: {
const msg = try sema.errMsg(val_src, "runtime value contains reference to comptime var", .{});
errdefer msg.destroy(sema.gpa);
try sema.errNote(val_src, msg, "comptime var pointers are not available at runtime", .{});
const pt = sema.pt;
const zcu = pt.zcu;
const val_str = try zcu.intern_pool.getOrPutString(zcu.gpa, pt.tid, "runtime_value", .no_embedded_nulls);
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const msg = try sema.errMsg(val_src, "runtime value contains reference to comptime var", .{});
errdefer msg.destroy(gpa);
try sema.errNote(val_src, msg, "comptime var pointers are not available at runtime", .{});
const val_str = try pt.zcu.intern_pool.getOrPutString(gpa, io, pt.tid, "runtime_value", .no_embedded_nulls);
try sema.explainWhyValueContainsReferenceToComptimeVar(msg, val_src, val_str, .fromInterned(val.toInterned().?));
break :msg msg;
});
@@ -37385,7 +37638,9 @@ fn resolveDeclaredEnumInner(
) Zcu.CompileError!void {
const pt = sema.pt;
const zcu = pt.zcu;
const gpa = zcu.gpa;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const ip = &zcu.intern_pool;
const bit_bags_count = std.math.divCeil(usize, fields_len, 32) catch unreachable;
@@ -37430,7 +37685,7 @@ fn resolveDeclaredEnumInner(
const field_name_zir = zir.nullTerminatedString(field_name_index);
extra_index += 1; // field name
const field_name = try ip.getOrPutString(gpa, pt.tid, field_name_zir, .no_embedded_nulls);
const field_name = try ip.getOrPutString(gpa, io, pt.tid, field_name_zir, .no_embedded_nulls);
const value_src: LazySrcLoc = .{
.base_node_inst = tracked_inst,
@@ -37541,7 +37796,9 @@ pub fn resolveNavPtrModifiers(
) CompileError!NavPtrModifiers {
const pt = sema.pt;
const zcu = pt.zcu;
const gpa = zcu.gpa;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const ip = &zcu.intern_pool;
const align_src = block.src(.{ .node_offset_var_decl_align = .zero });
@@ -37563,7 +37820,7 @@ pub fn resolveNavPtrModifiers(
} else if (bytes.len == 0) {
return sema.fail(block, section_src, "linksection cannot be empty", .{});
}
break :ls try ip.getOrPutStringOpt(gpa, pt.tid, bytes, .no_embedded_nulls);
break :ls try ip.getOrPutStringOpt(gpa, io, pt.tid, bytes, .no_embedded_nulls);
};
const @"addrspace": std.builtin.AddressSpace = as: {
@@ -37595,8 +37852,10 @@ pub fn resolveNavPtrModifiers(
pub fn analyzeMemoizedState(sema: *Sema, block: *Block, simple_src: LazySrcLoc, builtin_namespace: InternPool.NamespaceIndex, stage: InternPool.MemoizedStateStage) CompileError!bool {
const pt = sema.pt;
const zcu = pt.zcu;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const ip = &zcu.intern_pool;
const gpa = zcu.gpa;
var any_changed = false;
@@ -37613,7 +37872,7 @@ pub fn analyzeMemoizedState(sema: *Sema, block: *Block, simple_src: LazySrcLoc,
},
};
const name_nts = try ip.getOrPutString(gpa, pt.tid, name, .no_embedded_nulls);
const name_nts = try ip.getOrPutString(gpa, io, pt.tid, name, .no_embedded_nulls);
const nav = try sema.namespaceLookup(block, simple_src, parent_ns, name_nts) orelse
return sema.fail(block, simple_src, "{s} missing {s}", .{ parent_name, name });
+51 -24
View File
@@ -38,8 +38,11 @@ pub fn run(
block: *Sema.Block,
) CompileError!InternPool.Index {
const pt = sema.pt;
const comp = pt.zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const tracked_inst = try pt.zcu.intern_pool.trackZir(pt.zcu.gpa, pt.tid, .{
const tracked_inst = try pt.zcu.intern_pool.trackZir(gpa, io, pt.tid, .{
.file = file_index,
.inst = .main_struct_inst, // this is the only trackable instruction in a ZON file
});
@@ -63,8 +66,10 @@ pub fn run(
}
fn lowerExprAnonResTy(self: *LowerZon, node: Zoir.Node.Index) CompileError!InternPool.Index {
const gpa = self.sema.gpa;
const pt = self.sema.pt;
const comp = pt.zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const ip = &pt.zcu.intern_pool;
switch (node.get(self.file.zoir.?)) {
.true => return .bool_true,
@@ -94,13 +99,14 @@ fn lowerExprAnonResTy(self: *LowerZon, node: Zoir.Node.Index) CompileError!Inter
.enum_literal => |val| return pt.intern(.{
.enum_literal = try ip.getOrPutString(
gpa,
io,
pt.tid,
val.get(self.file.zoir.?),
.no_embedded_nulls,
),
}),
.string_literal => |val| {
const ip_str = try ip.getOrPutString(gpa, pt.tid, val, .maybe_embedded_nulls);
const ip_str = try ip.getOrPutString(gpa, io, pt.tid, val, .maybe_embedded_nulls);
const result = try self.sema.addStrLit(ip_str, val.len);
return result.toInterned().?;
},
@@ -112,14 +118,10 @@ fn lowerExprAnonResTy(self: *LowerZon, node: Zoir.Node.Index) CompileError!Inter
values[i] = try self.lowerExprAnonResTy(nodes.at(@intCast(i)));
types[i] = Value.fromInterned(values[i]).typeOf(pt.zcu).toIntern();
}
const ty = try ip.getTupleType(
gpa,
pt.tid,
.{
.types = types,
.values = values,
},
);
const ty = try ip.getTupleType(gpa, io, pt.tid, .{
.types = types,
.values = values,
});
return (try pt.aggregateValue(.fromInterned(ty), values)).toIntern();
},
.struct_literal => |init| {
@@ -129,6 +131,7 @@ fn lowerExprAnonResTy(self: *LowerZon, node: Zoir.Node.Index) CompileError!Inter
}
const struct_ty = switch (try ip.getStructType(
gpa,
io,
pt.tid,
.{
.layout = .auto,
@@ -168,6 +171,7 @@ fn lowerExprAnonResTy(self: *LowerZon, node: Zoir.Node.Index) CompileError!Inter
for (init.names, 0..) |name, field_idx| {
const name_interned = try ip.getOrPutString(
gpa,
io,
pt.tid,
name.get(self.file.zoir.?),
.no_embedded_nulls,
@@ -636,11 +640,16 @@ fn lowerArray(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool.
}
fn lowerEnum(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool.Index {
const ip = &self.sema.pt.zcu.intern_pool;
const pt = self.sema.pt;
const comp = pt.zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const ip = &pt.zcu.intern_pool;
switch (node.get(self.file.zoir.?)) {
.enum_literal => |field_name| {
const field_name_interned = try ip.getOrPutString(
self.sema.gpa,
gpa,
io,
self.sema.pt.tid,
field_name.get(self.file.zoir.?),
.no_embedded_nulls,
@@ -665,11 +674,16 @@ fn lowerEnum(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool.I
}
fn lowerEnumLiteral(self: *LowerZon, node: Zoir.Node.Index) !InternPool.Index {
const ip = &self.sema.pt.zcu.intern_pool;
const pt = self.sema.pt;
const comp = pt.zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const ip = &pt.zcu.intern_pool;
switch (node.get(self.file.zoir.?)) {
.enum_literal => |field_name| {
const field_name_interned = try ip.getOrPutString(
self.sema.gpa,
gpa,
io,
self.sema.pt.tid,
field_name.get(self.file.zoir.?),
.no_embedded_nulls,
@@ -747,8 +761,11 @@ fn lowerTuple(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool.
}
fn lowerStruct(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool.Index {
const ip = &self.sema.pt.zcu.intern_pool;
const gpa = self.sema.gpa;
const pt = self.sema.pt;
const comp = pt.zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const ip = &pt.zcu.intern_pool;
try res_ty.resolveFields(self.sema.pt);
try res_ty.resolveStructFieldInits(self.sema.pt);
@@ -772,6 +789,7 @@ fn lowerStruct(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool
for (0..fields.names.len) |i| {
const field_name = try ip.getOrPutString(
gpa,
io,
self.sema.pt.tid,
fields.names[i].get(self.file.zoir.?),
.no_embedded_nulls,
@@ -807,8 +825,11 @@ fn lowerStruct(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool
}
fn lowerSlice(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool.Index {
const ip = &self.sema.pt.zcu.intern_pool;
const gpa = self.sema.gpa;
const pt = self.sema.pt;
const comp = pt.zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const ip = &pt.zcu.intern_pool;
const ptr_info = res_ty.ptrInfo(self.sema.pt.zcu);
@@ -820,7 +841,7 @@ fn lowerSlice(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool.
if (string_alignment and ptr_info.child == .u8_type and string_sentinel) {
switch (node.get(self.file.zoir.?)) {
.string_literal => |val| {
const ip_str = try ip.getOrPutString(gpa, self.sema.pt.tid, val, .maybe_embedded_nulls);
const ip_str = try ip.getOrPutString(gpa, io, self.sema.pt.tid, val, .maybe_embedded_nulls);
const str_ref = try self.sema.addStrLit(ip_str, val.len);
return (try self.sema.coerce(
self.block,
@@ -892,7 +913,11 @@ fn lowerSlice(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool.
}
fn lowerUnion(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool.Index {
const ip = &self.sema.pt.zcu.intern_pool;
const pt = self.sema.pt;
const comp = pt.zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const ip = &pt.zcu.intern_pool;
try res_ty.resolveFields(self.sema.pt);
const union_info = self.sema.pt.zcu.typeToUnion(res_ty).?;
const enum_tag_info = union_info.loadTagType(ip);
@@ -900,7 +925,8 @@ fn lowerUnion(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool.
const field_name, const maybe_field_node = switch (node.get(self.file.zoir.?)) {
.enum_literal => |name| b: {
const field_name = try ip.getOrPutString(
self.sema.gpa,
gpa,
io,
self.sema.pt.tid,
name.get(self.file.zoir.?),
.no_embedded_nulls,
@@ -916,7 +942,8 @@ fn lowerUnion(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool.
return error.WrongType;
}
const field_name = try ip.getOrPutString(
self.sema.gpa,
gpa,
io,
self.sema.pt.tid,
fields.names[0].get(self.file.zoir.?),
.no_embedded_nulls,
@@ -942,7 +969,7 @@ fn lowerUnion(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool.
}
break :b .void_value;
};
return ip.getUnion(self.sema.pt.zcu.gpa, self.sema.pt.tid, .{
return ip.getUnion(gpa, io, self.sema.pt.tid, .{
.ty = res_ty.toIntern(),
.tag = tag.toIntern(),
.val = val,
+20 -14
View File
@@ -486,6 +486,7 @@ pub fn hasRuntimeBitsInner(
tid: strat.Tid(),
) RuntimeBitsError!bool {
const ip = &zcu.intern_pool;
const io = zcu.comp.io;
return switch (ty.toIntern()) {
.empty_tuple_type => false,
else => switch (ip.indexToKey(ty.toIntern())) {
@@ -571,7 +572,7 @@ pub fn hasRuntimeBitsInner(
},
.struct_type => {
const struct_type = ip.loadStructType(ty.toIntern());
if (strat != .eager and struct_type.assumeRuntimeBitsIfFieldTypesWip(ip)) {
if (strat != .eager and struct_type.assumeRuntimeBitsIfFieldTypesWip(ip, io)) {
// In this case, we guess that hasRuntimeBits() for this type is true,
// and then later if our guess was incorrect, we emit a compile error.
return true;
@@ -610,7 +611,7 @@ pub fn hasRuntimeBitsInner(
.none => if (strat != .eager) {
// In this case, we guess that hasRuntimeBits() for this type is true,
// and then later if our guess was incorrect, we emit a compile error.
if (union_type.assumeRuntimeBitsIfFieldTypesWip(ip)) return true;
if (union_type.assumeRuntimeBitsIfFieldTypesWip(ip, io)) return true;
},
.safety, .tagged => {},
}
@@ -2491,8 +2492,11 @@ pub fn isNumeric(ty: Type, zcu: *const Zcu) bool {
/// resolves field types rather than asserting they are already resolved.
pub fn onePossibleValue(starting_type: Type, pt: Zcu.PerThread) !?Value {
const zcu = pt.zcu;
var ty = starting_type;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const ip = &zcu.intern_pool;
var ty = starting_type;
while (true) switch (ty.toIntern()) {
.empty_tuple_type => return Value.empty_tuple,
@@ -2664,7 +2668,8 @@ pub fn onePossibleValue(starting_type: Type, pt: Zcu.PerThread) !?Value {
(try pt.intValue(.fromInterned(enum_type.tag_ty), 0)).toIntern()
else
try ip.getCoercedInts(
zcu.gpa,
gpa,
io,
pt.tid,
ip.indexToKey(enum_type.values.get(ip)[0]).int,
enum_type.tag_ty,
@@ -2720,6 +2725,7 @@ pub fn comptimeOnlyInner(
tid: strat.Tid(),
) SemaError!bool {
const ip = &zcu.intern_pool;
const io = zcu.comp.io;
return switch (ty.toIntern()) {
.empty_tuple_type => false,
@@ -2798,16 +2804,16 @@ pub fn comptimeOnlyInner(
.yes => true,
.unknown => unreachable,
},
.sema => switch (struct_type.setRequiresComptimeWip(ip)) {
.sema => switch (struct_type.setRequiresComptimeWip(ip, io)) {
.no, .wip => false,
.yes => true,
.unknown => {
if (struct_type.flagsUnordered(ip).field_types_wip) {
struct_type.setRequiresComptime(ip, .unknown);
struct_type.setRequiresComptime(ip, io, .unknown);
return false;
}
errdefer struct_type.setRequiresComptime(ip, .unknown);
errdefer struct_type.setRequiresComptime(ip, io, .unknown);
const pt = strat.pt(zcu, tid);
try ty.resolveFields(pt);
@@ -2821,12 +2827,12 @@ pub fn comptimeOnlyInner(
// be considered resolved. Comptime-only types
// still maintain a layout of their
// runtime-known fields.
struct_type.setRequiresComptime(ip, .yes);
struct_type.setRequiresComptime(ip, io, .yes);
return true;
}
}
struct_type.setRequiresComptime(ip, .no);
struct_type.setRequiresComptime(ip, io, .no);
return false;
},
},
@@ -2850,16 +2856,16 @@ pub fn comptimeOnlyInner(
.yes => true,
.unknown => unreachable,
},
.sema => switch (union_type.setRequiresComptimeWip(ip)) {
.sema => switch (union_type.setRequiresComptimeWip(ip, io)) {
.no, .wip => return false,
.yes => return true,
.unknown => {
if (union_type.flagsUnordered(ip).status == .field_types_wip) {
union_type.setRequiresComptime(ip, .unknown);
union_type.setRequiresComptime(ip, io, .unknown);
return false;
}
errdefer union_type.setRequiresComptime(ip, .unknown);
errdefer union_type.setRequiresComptime(ip, io, .unknown);
const pt = strat.pt(zcu, tid);
try ty.resolveFields(pt);
@@ -2867,12 +2873,12 @@ pub fn comptimeOnlyInner(
for (0..union_type.field_types.len) |field_idx| {
const field_ty = union_type.field_types.get(ip)[field_idx];
if (try Type.fromInterned(field_ty).comptimeOnlyInner(strat, zcu, tid)) {
union_type.setRequiresComptime(ip, .yes);
union_type.setRequiresComptime(ip, io, .yes);
return true;
}
}
union_type.setRequiresComptime(ip, .no);
union_type.setRequiresComptime(ip, io, .no);
return false;
},
},
+18 -9
View File
@@ -60,18 +60,21 @@ pub fn fmtValueSemaFull(ctx: print_value.FormatContext) std.fmt.Alt(print_value.
/// Asserts `val` is an array of `u8`
pub fn toIpString(val: Value, ty: Type, pt: Zcu.PerThread) !InternPool.NullTerminatedString {
const zcu = pt.zcu;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const ip = &zcu.intern_pool;
assert(ty.zigTypeTag(zcu) == .array);
assert(ty.childType(zcu).toIntern() == .u8_type);
const ip = &zcu.intern_pool;
switch (zcu.intern_pool.indexToKey(val.toIntern()).aggregate.storage) {
.bytes => |bytes| return bytes.toNullTerminatedString(ty.arrayLen(zcu), ip),
.elems => return arrayToIpString(val, ty.arrayLen(zcu), pt),
.repeated_elem => |elem| {
const byte: u8 = @intCast(Value.fromInterned(elem).toUnsignedInt(zcu));
const len: u32 = @intCast(ty.arrayLen(zcu));
const string_bytes = ip.getLocal(pt.tid).getMutableStringBytes(zcu.gpa);
const string_bytes = ip.getLocal(pt.tid).getMutableStringBytes(gpa, io);
try string_bytes.appendNTimes(.{byte}, len);
return ip.getOrPutTrailingString(zcu.gpa, pt.tid, len, .no_embedded_nulls);
return ip.getOrPutTrailingString(gpa, io, pt.tid, len, .no_embedded_nulls);
},
}
}
@@ -109,10 +112,12 @@ fn arrayToAllocatedBytes(val: Value, len: u64, allocator: Allocator, pt: Zcu.Per
fn arrayToIpString(val: Value, len_u64: u64, pt: Zcu.PerThread) !InternPool.NullTerminatedString {
const zcu = pt.zcu;
const gpa = zcu.gpa;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const ip = &zcu.intern_pool;
const len: u32 = @intCast(len_u64);
const string_bytes = ip.getLocal(pt.tid).getMutableStringBytes(gpa);
const string_bytes = ip.getLocal(pt.tid).getMutableStringBytes(gpa, io);
try string_bytes.ensureUnusedCapacity(len);
for (0..len) |i| {
// I don't think elemValue has the possibility to affect ip.string_bytes. Let's
@@ -123,7 +128,7 @@ fn arrayToIpString(val: Value, len_u64: u64, pt: Zcu.PerThread) !InternPool.Null
const byte: u8 = @intCast(elem_val.toUnsignedInt(zcu));
string_bytes.appendAssumeCapacity(.{byte});
}
return ip.getOrPutTrailingString(gpa, pt.tid, len, .no_embedded_nulls);
return ip.getOrPutTrailingString(gpa, io, pt.tid, len, .no_embedded_nulls);
}
pub fn fromInterned(i: InternPool.Index) Value {
@@ -1141,6 +1146,7 @@ pub fn sliceArray(
) error{OutOfMemory}!Value {
const pt = sema.pt;
const ip = &pt.zcu.intern_pool;
const io = pt.zcu.comp.io;
return Value.fromInterned(try pt.intern(.{
.aggregate = .{
.ty = switch (pt.zcu.intern_pool.indexToKey(pt.zcu.intern_pool.typeOf(val.toIntern()))) {
@@ -1160,6 +1166,7 @@ pub fn sliceArray(
try ip.string_bytes.ensureUnusedCapacity(sema.gpa, end - start + 1);
break :storage .{ .bytes = try ip.getOrPutString(
sema.gpa,
io,
bytes.toSlice(end, ip)[start..],
.maybe_embedded_nulls,
) };
@@ -2874,6 +2881,7 @@ const interpret_mode: InterpretMode = @field(InterpretMode, @tagName(build_optio
/// `val` must be fully resolved.
pub fn interpret(val: Value, comptime T: type, pt: Zcu.PerThread) error{ OutOfMemory, UndefinedValue, TypeMismatch }!T {
const zcu = pt.zcu;
const io = zcu.comp.io;
const ip = &zcu.intern_pool;
const ty = val.typeOf(zcu);
if (ty.zigTypeTag(zcu) != @typeInfo(T)) return error.TypeMismatch;
@@ -2960,7 +2968,7 @@ pub fn interpret(val: Value, comptime T: type, pt: Zcu.PerThread) error{ OutOfMe
const struct_obj = zcu.typeToStruct(ty) orelse return error.TypeMismatch;
var result: T = undefined;
inline for (@"struct".fields) |field| {
const field_name_ip = try ip.getOrPutString(zcu.gpa, pt.tid, field.name, .no_embedded_nulls);
const field_name_ip = try ip.getOrPutString(zcu.gpa, io, pt.tid, field.name, .no_embedded_nulls);
@field(result, field.name) = if (struct_obj.nameIndex(ip, field_name_ip)) |field_idx| f: {
const field_val = try val.fieldValue(pt, field_idx);
break :f try field_val.interpret(field.type, pt);
@@ -2979,6 +2987,7 @@ pub fn uninterpret(val: anytype, ty: Type, pt: Zcu.PerThread) error{ OutOfMemory
const T = @TypeOf(val);
const zcu = pt.zcu;
const io = zcu.comp.io;
const ip = &zcu.intern_pool;
if (ty.zigTypeTag(zcu) != @typeInfo(T)) return error.TypeMismatch;
@@ -3022,7 +3031,7 @@ pub fn uninterpret(val: anytype, ty: Type, pt: Zcu.PerThread) error{ OutOfMemory
.@"enum" => switch (interpret_mode) {
.direct => try pt.enumValue(ty, (try uninterpret(@intFromEnum(val), ty.intTagType(zcu), pt)).toIntern()),
.by_name => {
const field_name_ip = try ip.getOrPutString(zcu.gpa, pt.tid, @tagName(val), .no_embedded_nulls);
const field_name_ip = try ip.getOrPutString(zcu.gpa, io, pt.tid, @tagName(val), .no_embedded_nulls);
const field_idx = ty.enumFieldIndex(field_name_ip, zcu) orelse return error.TypeMismatch;
return pt.enumValueFieldIndex(ty, field_idx);
},
@@ -3059,7 +3068,7 @@ pub fn uninterpret(val: anytype, ty: Type, pt: Zcu.PerThread) error{ OutOfMemory
defer zcu.gpa.free(field_vals);
@memset(field_vals, .none);
inline for (@"struct".fields) |field| {
const field_name_ip = try ip.getOrPutString(zcu.gpa, pt.tid, field.name, .no_embedded_nulls);
const field_name_ip = try ip.getOrPutString(zcu.gpa, io, pt.tid, field.name, .no_embedded_nulls);
if (struct_obj.nameIndex(ip, field_name_ip)) |field_idx| {
const field_ty = ty.fieldType(field_idx, zcu);
field_vals[field_idx] = (try uninterpret(@field(val, field.name), field_ty, pt)).toIntern();
+196 -17
View File
@@ -37,6 +37,7 @@ const InternPool = @import("InternPool.zig");
const Alignment = InternPool.Alignment;
const AnalUnit = InternPool.AnalUnit;
const BuiltinFn = std.zig.BuiltinFn;
const codegen = @import("codegen.zig");
const LlvmObject = @import("codegen/llvm.zig").Object;
const dev = @import("dev.zig");
const Zoir = std.zig.Zoir;
@@ -317,6 +318,8 @@ incremental_debug_state: if (build_options.enable_debug_extensions) IncrementalD
/// this timer must be temporarily paused and resumed later.
cur_analysis_timer: ?Compilation.Timer = null,
codegen_task_pool: CodegenTaskPool,
generation: u32 = 0,
pub const IncrementalDebugState = struct {
@@ -895,12 +898,13 @@ pub const Namespace = struct {
ns: Namespace,
ip: *InternPool,
gpa: Allocator,
io: Io,
tid: Zcu.PerThread.Id,
name: InternPool.NullTerminatedString,
) !InternPool.NullTerminatedString {
const ns_name = Type.fromInterned(ns.owner_type).containerTypeName(ip);
if (name == .empty) return ns_name;
return ip.getOrPutStringFmt(gpa, tid, "{f}.{f}", .{ ns_name.fmt(ip), name.fmt(ip) }, .no_embedded_nulls);
return ip.getOrPutStringFmt(gpa, io, tid, "{f}.{f}", .{ ns_name.fmt(ip), name.fmt(ip) }, .no_embedded_nulls);
}
};
@@ -1139,13 +1143,15 @@ pub const File = struct {
}
pub fn internFullyQualifiedName(file: File, pt: Zcu.PerThread) !InternPool.NullTerminatedString {
const gpa = pt.zcu.gpa;
const ip = &pt.zcu.intern_pool;
const string_bytes = ip.getLocal(pt.tid).getMutableStringBytes(gpa);
const comp = pt.zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const string_bytes = ip.getLocal(pt.tid).getMutableStringBytes(gpa, io);
var w: Writer = .fixed((try string_bytes.addManyAsSlice(file.fullyQualifiedNameLen()))[0]);
file.renderFullyQualifiedName(&w) catch unreachable;
assert(w.end == w.buffer.len);
return ip.getOrPutTrailingString(gpa, pt.tid, @intCast(w.end), .no_embedded_nulls);
return ip.getOrPutTrailingString(gpa, io, pt.tid, @intCast(w.end), .no_embedded_nulls);
}
pub const Index = InternPool.FileIndex;
@@ -2801,13 +2807,14 @@ pub const CompileError = error{
ComptimeBreak,
};
pub fn init(zcu: *Zcu, thread_count: usize) !void {
const gpa = zcu.gpa;
try zcu.intern_pool.init(gpa, thread_count);
pub fn init(zcu: *Zcu, gpa: Allocator, io: Io, thread_count: usize) !void {
try zcu.intern_pool.init(gpa, io, thread_count);
}
pub fn deinit(zcu: *Zcu) void {
const gpa = zcu.gpa;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
{
const pt: Zcu.PerThread = .activate(zcu, .main);
defer pt.deactivate();
@@ -2897,7 +2904,7 @@ pub fn deinit(zcu: *Zcu) void {
zcu.incremental_debug_state.deinit(gpa);
}
}
zcu.intern_pool.deinit(gpa);
zcu.intern_pool.deinit(gpa, io);
}
pub fn namespacePtr(zcu: *Zcu, index: Namespace.Index) *Namespace {
@@ -4442,7 +4449,7 @@ pub fn maybeUnresolveIes(zcu: *Zcu, func_index: InternPool.Index) !void {
try zcu.outdated_ready.put(gpa, unit, {});
}
}
zcu.intern_pool.funcSetIesResolved(func_index, .none);
zcu.intern_pool.funcSetIesResolved(zcu.comp.io, func_index, .none);
}
}
@@ -4620,10 +4627,12 @@ pub fn codegenFail(
/// Takes ownership of `msg`, even on OOM.
pub fn codegenFailMsg(zcu: *Zcu, nav_index: InternPool.Nav.Index, msg: *ErrorMsg) CodegenFailError {
const gpa = zcu.gpa;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
{
zcu.comp.mutex.lock();
defer zcu.comp.mutex.unlock();
comp.mutex.lockUncancelable(io);
defer comp.mutex.unlock(io);
errdefer msg.deinit(gpa);
try zcu.failed_codegen.putNoClobber(gpa, nav_index, msg);
}
@@ -4632,8 +4641,10 @@ pub fn codegenFailMsg(zcu: *Zcu, nav_index: InternPool.Nav.Index, msg: *ErrorMsg
/// 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();
const comp = zcu.comp;
const io = comp.io;
comp.mutex.lockUncancelable(io);
defer comp.mutex.unlock(io);
assert(zcu.failed_codegen.contains(nav));
}
@@ -4794,8 +4805,9 @@ const TrackedUnitSema = struct {
report_time: {
const sema_ns = zcu.cur_analysis_timer.?.finish() orelse break :report_time;
const zir_decl = tus.analysis_timer_decl orelse break :report_time;
comp.mutex.lock();
defer comp.mutex.unlock();
const io = comp.io;
comp.mutex.lockUncancelable(io);
defer comp.mutex.unlock(io);
comp.time_report.?.stats.cpu_ns_sema += sema_ns;
const gop = comp.time_report.?.decl_sema_info.getOrPut(comp.gpa, zir_decl) catch |err| switch (err) {
error.OutOfMemory => {
@@ -4830,3 +4842,170 @@ pub fn trackUnitSema(zcu: *Zcu, name: []const u8, zir_inst: ?InternPool.TrackedI
.analysis_timer_decl = zir_inst,
};
}
pub const CodegenTaskPool = struct {
const CodegenResult = PerThread.RunCodegenError!codegen.AnyMir;
/// 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;
const max_funcs_in_flight = @import("link.zig").Queue.buffer_size;
available_air_bytes: u32,
/// Locks the freelist and `available_air_bytes`.
mutex: Io.Mutex,
/// Signaled when an item is added to the freelist.
free_cond: Io.Condition,
/// Pre-allocated with enough capacity for all indices.
free: std.ArrayList(Index),
/// `.none` means this task is in the freelist. The `task_air_bytes` and
/// `task_futures` entries are `undefined`.
task_funcs: []InternPool.Index,
task_air_bytes: []u32,
task_futures: []Io.Future(CodegenResult),
pub fn init(arena: Allocator) Allocator.Error!CodegenTaskPool {
const task_funcs = try arena.alloc(InternPool.Index, max_funcs_in_flight);
const task_air_bytes = try arena.alloc(u32, max_funcs_in_flight);
const task_futures = try arena.alloc(Io.Future(CodegenResult), max_funcs_in_flight);
@memset(task_funcs, .none);
var free: std.ArrayList(Index) = try .initCapacity(arena, max_funcs_in_flight);
for (0..max_funcs_in_flight) |index| free.appendAssumeCapacity(@enumFromInt(index));
return .{
.available_air_bytes = max_air_bytes_in_flight,
.mutex = .init,
.free_cond = .init,
.free = free,
.task_funcs = task_funcs,
.task_air_bytes = task_air_bytes,
.task_futures = task_futures,
};
}
pub fn cancel(pool: *CodegenTaskPool, zcu: *const Zcu) void {
const io = zcu.comp.io;
for (
pool.task_funcs,
pool.task_air_bytes,
pool.task_futures,
) |func, effective_air_bytes, *future| {
if (func == .none) continue;
pool.available_air_bytes += effective_air_bytes;
var mir = future.cancel(io) catch continue;
mir.deinit(zcu);
}
assert(pool.available_air_bytes == max_air_bytes_in_flight);
}
pub fn start(
pool: *CodegenTaskPool,
zcu: *Zcu,
func_index: InternPool.Index,
air: *Air,
/// If `true`, this function will take ownership of `air`, freeing it after codegen
/// completes; it is not assumed that `air` will outlive this function. If `false`,
/// codegen will operate on `air` via the given pointer, which it is assumed will
/// outline the codegen task.
move_air: bool,
) Io.Cancelable!Index {
const io = zcu.comp.io;
// To avoid consuming an excessive amount of memory, there is a limit on the total number of AIR
// bytes which can be in the codegen/link pipeline at one time. If we exceed this limit, we must
// wait for codegen/link to finish some WIP functions so they catch up with us.
const actual_air_bytes: u32 = @intCast(air.instructions.len * 5 + air.extra.items.len * 4);
// We need to let all AIR through eventually, even if one function exceeds `max_air_bytes_in_flight`.
const effective_air_bytes: u32 = @min(actual_air_bytes, max_air_bytes_in_flight);
assert(effective_air_bytes > 0);
const index: Index = index: {
try pool.mutex.lock(io);
defer pool.mutex.unlock(io);
while (pool.free.items.len == 0 or pool.available_air_bytes < effective_air_bytes) {
// The linker thread needs to catch up!
try pool.free_cond.wait(io, &pool.mutex);
}
pool.available_air_bytes -= effective_air_bytes;
break :index pool.free.pop().?;
};
// No turning back now: we're incrementing `pending_codegen_jobs` and starting the worker.
errdefer comptime unreachable;
assert(zcu.pending_codegen_jobs.fetchAdd(1, .monotonic) > 0); // the "Code Generation" node is still active
assert(pool.task_funcs[@intFromEnum(index)] == .none);
pool.task_funcs[@intFromEnum(index)] = func_index;
pool.task_air_bytes[@intFromEnum(index)] = actual_air_bytes;
pool.task_futures[@intFromEnum(index)] = if (move_air) io.async(
workerCodegenOwnedAir,
.{ zcu, func_index, air.* },
) else io.async(
workerCodegenExternalAir,
.{ zcu, func_index, air },
);
return index;
}
pub const Index = enum(u32) {
_,
/// Blocks until codegen has completed, successfully or otherwise.
/// The returned MIR is owned by the caller.
pub fn wait(
index: Index,
pool: *CodegenTaskPool,
io: Io,
) PerThread.RunCodegenError!struct { InternPool.Index, codegen.AnyMir } {
const func = pool.task_funcs[@intFromEnum(index)];
assert(func != .none);
const effective_air_bytes = pool.task_air_bytes[@intFromEnum(index)];
const result = pool.task_futures[@intFromEnum(index)].await(io);
pool.task_funcs[@intFromEnum(index)] = .none;
pool.task_air_bytes[@intFromEnum(index)] = undefined;
pool.task_futures[@intFromEnum(index)] = undefined;
{
pool.mutex.lockUncancelable(io);
defer pool.mutex.unlock(io);
pool.available_air_bytes += effective_air_bytes;
pool.free.appendAssumeCapacity(index);
pool.free_cond.signal(io);
}
return .{ func, try result };
}
};
fn workerCodegenOwnedAir(
zcu: *Zcu,
func_index: InternPool.Index,
orig_air: Air,
) CodegenResult {
// We own `air` now, so we are responsbile for freeing it.
var air = orig_air;
defer air.deinit(zcu.comp.gpa);
const tid = Compilation.getTid();
const pt: Zcu.PerThread = .activate(zcu, @enumFromInt(tid));
defer pt.deactivate();
return pt.runCodegen(func_index, &air);
}
fn workerCodegenExternalAir(
zcu: *Zcu,
func_index: InternPool.Index,
air: *Air,
) CodegenResult {
const tid = Compilation.getTid();
const pt: Zcu.PerThread = .activate(zcu, @enumFromInt(tid));
defer pt.deactivate();
return pt.runCodegen(func_index, air);
}
};
+177 -121
View File
@@ -269,8 +269,8 @@ pub fn updateFile(
// Any potential AST errors are converted to ZIR errors when we run AstGen/ZonGen.
file.tree = try Ast.parse(gpa, source, file.getMode());
if (timer.finish()) |ns_parse| {
comp.mutex.lock();
defer comp.mutex.unlock();
comp.mutex.lockUncancelable(io);
defer comp.mutex.unlock(io);
comp.time_report.?.stats.cpu_ns_parse += ns_parse;
}
@@ -295,8 +295,8 @@ pub fn updateFile(
},
}
if (timer.finish()) |ns_astgen| {
comp.mutex.lock();
defer comp.mutex.unlock();
comp.mutex.lockUncancelable(io);
defer comp.mutex.unlock(io);
comp.time_report.?.stats.cpu_ns_astgen += ns_astgen;
}
@@ -315,8 +315,8 @@ pub fn updateFile(
switch (file.getMode()) {
.zig => {
if (file.zir.?.hasCompileErrors()) {
comp.mutex.lock();
defer comp.mutex.unlock();
comp.mutex.lockUncancelable(io);
defer comp.mutex.unlock(io);
try zcu.failed_files.putNoClobber(gpa, file_index, null);
}
if (file.zir.?.loweringFailed()) {
@@ -328,8 +328,8 @@ pub fn updateFile(
.zon => {
if (file.zoir.?.hasCompileErrors()) {
file.status = .astgen_failure;
comp.mutex.lock();
defer comp.mutex.unlock();
comp.mutex.lockUncancelable(io);
defer comp.mutex.unlock(io);
try zcu.failed_files.putNoClobber(gpa, file_index, null);
} else {
file.status = .success;
@@ -415,7 +415,8 @@ pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void {
const zcu = pt.zcu;
const comp = zcu.comp;
const ip = &zcu.intern_pool;
const gpa = zcu.gpa;
const gpa = comp.gpa;
const io = comp.io;
// We need to visit every updated File for every TrackedInst in InternPool.
// This only includes Zig files; ZON files are omitted.
@@ -459,7 +460,7 @@ pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void {
return;
for (ip.locals, 0..) |*local, tid| {
const tracked_insts_list = local.getMutableTrackedInsts(gpa);
const tracked_insts_list = local.getMutableTrackedInsts(gpa, io);
for (tracked_insts_list.viewAllowEmpty().items(.@"0"), 0..) |*tracked_inst, tracked_inst_unwrapped_index| {
const file_index = tracked_inst.file;
const updated_file = updated_files.get(file_index) orelse continue;
@@ -530,6 +531,7 @@ pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void {
if (old_decl.name == .empty) continue;
const name_ip = try zcu.intern_pool.getOrPutString(
zcu.gpa,
io,
pt.tid,
old_zir.nullTerminatedString(old_decl.name),
.no_embedded_nulls,
@@ -545,6 +547,7 @@ pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void {
if (new_decl.name == .empty) continue;
const name_ip = try zcu.intern_pool.getOrPutString(
zcu.gpa,
io,
pt.tid,
new_zir.nullTerminatedString(new_decl.name),
.no_embedded_nulls,
@@ -575,7 +578,7 @@ pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void {
}
}
try ip.rehashTrackedInsts(gpa, pt.tid);
try ip.rehashTrackedInsts(gpa, io, pt.tid);
for (updated_files.keys(), updated_files.values()) |file_index, updated_file| {
const file = updated_file.file;
@@ -700,7 +703,9 @@ pub fn ensureMemoizedStateUpToDate(pt: Zcu.PerThread, stage: InternPool.Memoized
fn analyzeMemoizedState(pt: Zcu.PerThread, stage: InternPool.MemoizedStateStage) Zcu.CompileError!bool {
const zcu = pt.zcu;
const ip = &zcu.intern_pool;
const gpa = zcu.gpa;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const unit: AnalUnit = .wrap(.{ .memoized_state = stage });
@@ -716,7 +721,7 @@ fn analyzeMemoizedState(pt: Zcu.PerThread, stage: InternPool.MemoizedStateStage)
const std_type: Type = .fromInterned(zcu.fileRootType(std_file_index));
const std_namespace = std_type.getNamespaceIndex(zcu);
try pt.ensureNamespaceUpToDate(std_namespace);
const builtin_str = try ip.getOrPutString(gpa, pt.tid, "builtin", .no_embedded_nulls);
const builtin_str = try ip.getOrPutString(gpa, io, pt.tid, "builtin", .no_embedded_nulls);
const builtin_nav = zcu.namespacePtr(std_namespace).pub_decls.getKeyAdapted(builtin_str, Zcu.Namespace.NameAdapter{ .zcu = zcu }) orelse
@panic("lib/std.zig is corrupt and missing 'builtin'");
try pt.ensureNavValUpToDate(builtin_nav);
@@ -857,8 +862,10 @@ pub fn ensureComptimeUnitUpToDate(pt: Zcu.PerThread, cu_id: InternPool.ComptimeU
/// to `transitive_failed_analysis` if necessary.
fn analyzeComptimeUnit(pt: Zcu.PerThread, cu_id: InternPool.ComptimeUnit.Id) Zcu.CompileError!void {
const zcu = pt.zcu;
const gpa = zcu.gpa;
const ip = &zcu.intern_pool;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const anal_unit: AnalUnit = .wrap(.{ .@"comptime" = cu_id });
const comptime_unit = ip.getComptimeUnit(cu_id);
@@ -909,7 +916,7 @@ fn analyzeComptimeUnit(pt: Zcu.PerThread, cu_id: InternPool.ComptimeUnit.Id) Zcu
.r = .{ .simple = .comptime_keyword },
} },
.src_base_inst = comptime_unit.zir_index,
.type_name_ctx = try ip.getOrPutStringFmt(gpa, pt.tid, "{f}.comptime", .{
.type_name_ctx = try ip.getOrPutStringFmt(gpa, io, pt.tid, "{f}.comptime", .{
Type.fromInterned(zcu.namespacePtr(comptime_unit.namespace).owner_type).containerTypeName(ip).fmt(ip),
}, .no_embedded_nulls),
};
@@ -1087,8 +1094,10 @@ pub fn ensureNavValUpToDate(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu
fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileError!struct { val_changed: bool } {
const zcu = pt.zcu;
const gpa = zcu.gpa;
const ip = &zcu.intern_pool;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const anal_unit: AnalUnit = .wrap(.{ .nav_val = nav_id });
const old_nav = ip.getNav(nav_id);
@@ -1253,7 +1262,7 @@ fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileErr
break :val .fromInterned(try pt.getExtern(.{
.name = old_nav.name,
.ty = nav_ty.toIntern(),
.lib_name = try ip.getOrPutStringOpt(gpa, pt.tid, lib_name, .no_embedded_nulls),
.lib_name = try ip.getOrPutStringOpt(gpa, io, pt.tid, lib_name, .no_embedded_nulls),
.is_threadlocal = zir_decl.is_threadlocal,
.linkage = .strong,
.visibility = .default,
@@ -1310,7 +1319,7 @@ fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileErr
}
}
ip.resolveNavValue(nav_id, .{
ip.resolveNavValue(io, nav_id, .{
.val = nav_val.toIntern(),
.is_const = is_const,
.alignment = modifiers.alignment,
@@ -1327,7 +1336,7 @@ fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileErr
if (zir_decl.linkage == .@"export") {
const export_src = block.src(.{ .token_offset = @enumFromInt(@intFromBool(zir_decl.is_pub)) });
const name_slice = zir.nullTerminatedString(zir_decl.name);
const name_ip = try ip.getOrPutString(gpa, pt.tid, name_slice, .no_embedded_nulls);
const name_ip = try ip.getOrPutString(gpa, io, pt.tid, name_slice, .no_embedded_nulls);
try sema.analyzeExport(&block, export_src, .{ .name = name_ip }, nav_id);
}
@@ -1472,7 +1481,9 @@ pub fn ensureNavTypeUpToDate(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zc
fn analyzeNavType(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileError!struct { type_changed: bool } {
const zcu = pt.zcu;
const gpa = zcu.gpa;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const ip = &zcu.intern_pool;
const anal_unit: AnalUnit = .wrap(.{ .nav_ty = nav_id });
@@ -1579,7 +1590,7 @@ fn analyzeNavType(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileEr
if (!changed) return .{ .type_changed = false };
ip.resolveNavType(nav_id, .{
ip.resolveNavType(io, nav_id, .{
.type = resolved_ty.toIntern(),
.is_const = is_const,
.alignment = modifiers.alignment,
@@ -1775,6 +1786,7 @@ fn createFileRootStruct(
) Allocator.Error!InternPool.Index {
const zcu = pt.zcu;
const gpa = zcu.gpa;
const io = zcu.comp.io;
const ip = &zcu.intern_pool;
const file = zcu.fileByIndex(file_index);
const extended = file.zir.?.instructions.items(.data)[@intFromEnum(Zir.Inst.Index.main_struct_inst)].extended;
@@ -1797,11 +1809,11 @@ fn createFileRootStruct(
const decls = file.zir.?.bodySlice(extra_index, decls_len);
extra_index += decls_len;
const tracked_inst = try ip.trackZir(gpa, pt.tid, .{
const tracked_inst = try ip.trackZir(gpa, io, pt.tid, .{
.file = file_index,
.inst = .main_struct_inst,
});
const wip_ty = switch (try ip.getStructType(gpa, pt.tid, .{
const wip_ty = switch (try ip.getStructType(gpa, io, pt.tid, .{
.layout = .auto,
.fields_len = fields_len,
.known_non_opv = small.known_non_opv,
@@ -1916,7 +1928,9 @@ pub fn discoverImport(
},
} {
const zcu = pt.zcu;
const gpa = zcu.gpa;
const comp = zcu.comp;
const io = comp.io;
const gpa = comp.gpa;
if (!mem.endsWith(u8, import_string, ".zig") and !mem.endsWith(u8, import_string, ".zon")) {
return .module;
@@ -1926,8 +1940,8 @@ pub fn discoverImport(
errdefer new_path.deinit(gpa);
// We're about to do a GOP on `import_table`, so we need the mutex.
zcu.comp.mutex.lock();
defer zcu.comp.mutex.unlock();
comp.mutex.lockUncancelable(io);
defer comp.mutex.unlock(io);
const gop = try zcu.import_table.getOrPutAdapted(gpa, new_path, Zcu.ImportTableAdapter{ .zcu = zcu });
errdefer _ = zcu.import_table.pop();
@@ -1942,7 +1956,7 @@ pub fn discoverImport(
const new_file = try gpa.create(Zcu.File);
errdefer gpa.destroy(new_file);
const new_file_index = try zcu.intern_pool.createFile(gpa, pt.tid, .{
const new_file_index = try zcu.intern_pool.createFile(gpa, io, pt.tid, .{
.bin_digest = new_path.digest(),
.file = new_file,
.root_type = .none,
@@ -2027,7 +2041,9 @@ pub fn populateModuleRootTable(pt: Zcu.PerThread) error{
IllegalZigImport,
}!void {
const zcu = pt.zcu;
const gpa = zcu.gpa;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
// We'll initially add [mod, undefined] pairs, and when we reach the pair while
// iterating, rewrite the undefined value.
@@ -2085,7 +2101,7 @@ pub fn populateModuleRootTable(pt: Zcu.PerThread) error{
const new_file = try gpa.create(Zcu.File);
errdefer gpa.destroy(new_file);
const new_file_index = try zcu.intern_pool.createFile(gpa, pt.tid, .{
const new_file_index = try zcu.intern_pool.createFile(gpa, io, pt.tid, .{
.bin_digest = path.digest(),
.file = new_file,
.root_type = .none,
@@ -2291,7 +2307,8 @@ pub fn computeAliveFiles(pt: Zcu.PerThread) Allocator.Error!bool {
pub fn updateBuiltinModule(pt: Zcu.PerThread, opts: Builtin) Allocator.Error!void {
const zcu = pt.zcu;
const comp = zcu.comp;
const gpa = zcu.gpa;
const gpa = comp.gpa;
const io = comp.io;
const gop = try zcu.builtin_modules.getOrPut(gpa, opts.hash());
if (gop.found_existing) return; // the `File` is up-to-date
@@ -2330,7 +2347,7 @@ pub fn updateBuiltinModule(pt: Zcu.PerThread, opts: Builtin) Allocator.Error!voi
.zoir_invalidated = false,
};
const file_index = try zcu.intern_pool.createFile(gpa, pt.tid, .{
const file_index = try zcu.intern_pool.createFile(gpa, io, pt.tid, .{
.bin_digest = path.digest(),
.file = file,
.root_type = .none,
@@ -2469,7 +2486,7 @@ fn updateEmbedFileInner(
// The loaded bytes of the file, including a sentinel 0 byte.
const ip_str: InternPool.String = str: {
const string_bytes = ip.getLocal(tid).getMutableStringBytes(gpa);
const string_bytes = ip.getLocal(tid).getMutableStringBytes(gpa, io);
const old_len = string_bytes.mutate.len;
errdefer string_bytes.shrinkRetainingCapacity(old_len);
const bytes = (try string_bytes.addManyAsSlice(size_plus_one))[0];
@@ -2480,7 +2497,7 @@ fn updateEmbedFileInner(
error.EndOfStream => return error.UnexpectedEof,
};
bytes[size] = 0;
break :str try ip.getOrPutTrailingString(gpa, tid, @intCast(bytes.len), .maybe_embedded_nulls);
break :str try ip.getOrPutTrailingString(gpa, io, tid, @intCast(bytes.len), .maybe_embedded_nulls);
};
if (ip_str_out) |p| p.* = ip_str;
@@ -2516,7 +2533,8 @@ fn newEmbedFile(
) !*Zcu.EmbedFile {
const zcu = pt.zcu;
const comp = zcu.comp;
const gpa = zcu.gpa;
const io = comp.io;
const gpa = comp.gpa;
const ip = &zcu.intern_pool;
const new_file = try gpa.create(Zcu.EmbedFile);
@@ -2549,8 +2567,8 @@ fn newEmbedFile(
const path_str = try path.toAbsolute(comp.dirs, gpa);
defer gpa.free(path_str);
whole.cache_manifest_mutex.lock();
defer whole.cache_manifest_mutex.unlock();
try whole.cache_manifest_mutex.lock(io);
defer whole.cache_manifest_mutex.unlock(io);
man.addFilePostContents(path_str, contents, new_file.stat) catch |err| switch (err) {
error.Unexpected => unreachable,
@@ -2647,13 +2665,15 @@ const ScanDeclIter = struct {
fn avoidNameConflict(iter: *ScanDeclIter, comptime fmt: []const u8, args: anytype) !InternPool.NullTerminatedString {
const pt = iter.pt;
const gpa = pt.zcu.gpa;
const ip = &pt.zcu.intern_pool;
var name = try ip.getOrPutStringFmt(gpa, pt.tid, fmt, args, .no_embedded_nulls);
const comp = pt.zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
var name = try ip.getOrPutStringFmt(gpa, io, pt.tid, fmt, args, .no_embedded_nulls);
var gop = try iter.seen_decls.getOrPut(gpa, name);
var next_suffix: u32 = 0;
while (gop.found_existing) {
name = try ip.getOrPutStringFmt(gpa, pt.tid, "{f}_{d}", .{ name.fmt(ip), next_suffix }, .no_embedded_nulls);
name = try ip.getOrPutStringFmt(gpa, io, pt.tid, "{f}_{d}", .{ name.fmt(ip), next_suffix }, .no_embedded_nulls);
gop = try iter.seen_decls.getOrPut(gpa, name);
next_suffix += 1;
}
@@ -2669,7 +2689,8 @@ const ScanDeclIter = struct {
const comp = zcu.comp;
const namespace_index = iter.namespace_index;
const namespace = zcu.namespacePtr(namespace_index);
const gpa = zcu.gpa;
const gpa = comp.gpa;
const io = comp.io;
const file = namespace.fileScope(zcu);
const zir = file.zir.?;
const ip = &zcu.intern_pool;
@@ -2697,6 +2718,7 @@ const ScanDeclIter = struct {
if (iter.pass != .named) return;
const name = try ip.getOrPutString(
gpa,
io,
pt.tid,
zir.nullTerminatedString(decl.name),
.no_embedded_nulls,
@@ -2706,7 +2728,7 @@ const ScanDeclIter = struct {
},
};
const tracked_inst = try ip.trackZir(gpa, pt.tid, .{
const tracked_inst = try ip.trackZir(gpa, io, pt.tid, .{
.file = namespace.file_scope,
.inst = decl_inst,
});
@@ -2718,7 +2740,7 @@ const ScanDeclIter = struct {
const cu = if (existing_unit) |eu|
eu.unwrap().@"comptime"
else
try ip.createComptimeUnit(gpa, pt.tid, tracked_inst, namespace_index);
try ip.createComptimeUnit(gpa, io, pt.tid, tracked_inst, namespace_index);
const unit: AnalUnit = .wrap(.{ .@"comptime" = cu });
@@ -2737,9 +2759,9 @@ const ScanDeclIter = struct {
},
else => unit: {
const name = maybe_name.unwrap().?;
const fqn = try namespace.internFullyQualifiedName(ip, gpa, pt.tid, name);
const fqn = try namespace.internFullyQualifiedName(ip, gpa, io, pt.tid, name);
const nav = if (existing_unit) |eu| eu.unwrap().nav_val else nav: {
const nav = try ip.createDeclNav(gpa, pt.tid, name, fqn, tracked_inst, namespace_index);
const nav = try ip.createDeclNav(gpa, io, pt.tid, name, fqn, tracked_inst, namespace_index);
if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newNav(zcu, nav);
break :nav nav;
};
@@ -2798,7 +2820,9 @@ fn analyzeFnBodyInner(pt: Zcu.PerThread, func_index: InternPool.Index) Zcu.SemaE
defer tracy.end();
const zcu = pt.zcu;
const gpa = zcu.gpa;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const ip = &zcu.intern_pool;
const anal_unit = AnalUnit.wrap(.{ .func = func_index });
@@ -2810,9 +2834,9 @@ fn analyzeFnBodyInner(pt: Zcu.PerThread, func_index: InternPool.Index) Zcu.SemaE
try zcu.analysis_in_progress.putNoClobber(gpa, anal_unit, {});
errdefer _ = zcu.analysis_in_progress.swapRemove(anal_unit);
func.setAnalyzed(ip);
func.setAnalyzed(ip, io);
if (func.analysisUnordered(ip).inferred_error_set) {
func.setResolvedErrorSet(ip, .none);
func.setResolvedErrorSet(ip, io, .none);
}
if (zcu.comp.time_report) |*tr| {
@@ -2872,7 +2896,7 @@ fn analyzeFnBodyInner(pt: Zcu.PerThread, func_index: InternPool.Index) Zcu.SemaE
}
// reset in case calls to errorable functions are removed.
ip.funcSetHasErrorTrace(func_index, fn_ty_info.cc == .auto);
ip.funcSetHasErrorTrace(io, func_index, fn_ty_info.cc == .auto);
// First few indexes of extra are reserved and set at the end.
const reserved_count = @typeInfo(Air.ExtraIndex).@"enum".fields.len;
@@ -2971,7 +2995,7 @@ fn analyzeFnBodyInner(pt: Zcu.PerThread, func_index: InternPool.Index) Zcu.SemaE
});
}
func.setBranchHint(ip, sema.branch_hint orelse .none);
func.setBranchHint(ip, io, sema.branch_hint orelse .none);
if (zcu.comp.config.any_error_tracing and func.analysisUnordered(ip).has_error_trace and fn_ty_info.cc != .auto) {
// We're using an error trace, but didn't start out with one from the caller.
@@ -3005,7 +3029,7 @@ fn analyzeFnBodyInner(pt: Zcu.PerThread, func_index: InternPool.Index) Zcu.SemaE
else => |e| return e,
};
assert(ies.resolved != .none);
func.setResolvedErrorSet(ip, ies.resolved);
func.setResolvedErrorSet(ip, io, ies.resolved);
}
assert(zcu.analysis_in_progress.swapRemove(anal_unit));
@@ -3036,7 +3060,8 @@ fn analyzeFnBodyInner(pt: Zcu.PerThread, func_index: InternPool.Index) Zcu.SemaE
}
pub fn createNamespace(pt: Zcu.PerThread, initialization: Zcu.Namespace) !Zcu.Namespace.Index {
return pt.zcu.intern_pool.createNamespace(pt.zcu.gpa, pt.tid, initialization);
const comp = pt.zcu.comp;
return pt.zcu.intern_pool.createNamespace(comp.gpa, comp.io, pt.tid, initialization);
}
pub fn destroyNamespace(pt: Zcu.PerThread, namespace_index: Zcu.Namespace.Index) void {
@@ -3047,11 +3072,15 @@ pub fn getErrorValue(
pt: Zcu.PerThread,
name: InternPool.NullTerminatedString,
) Allocator.Error!Zcu.ErrorInt {
return pt.zcu.intern_pool.getErrorValue(pt.zcu.gpa, pt.tid, name);
const comp = pt.zcu.comp;
return pt.zcu.intern_pool.getErrorValue(comp.gpa, comp.io, pt.tid, name);
}
pub fn getErrorValueFromSlice(pt: Zcu.PerThread, name: []const u8) Allocator.Error!Zcu.ErrorInt {
return pt.getErrorValue(try pt.zcu.intern_pool.getOrPutString(pt.zcu.gpa, name));
const comp = pt.zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
return pt.getErrorValue(try pt.zcu.intern_pool.getOrPutString(gpa, io, name));
}
/// Removes any entry from `Zcu.failed_files` associated with `file`. Acquires `Compilation.mutex` as needed.
@@ -3078,8 +3107,10 @@ fn lockAndClearFileCompileError(pt: Zcu.PerThread, file_index: Zcu.File.Index, f
return;
}
pt.zcu.comp.mutex.lock();
defer pt.zcu.comp.mutex.unlock();
const comp = pt.zcu.comp;
const io = comp.io;
comp.mutex.lockUncancelable(io);
defer comp.mutex.unlock(io);
if (pt.zcu.failed_files.fetchSwapRemove(file_index)) |kv| {
assert(maybe_has_error); // the runtime safety case above
if (kv.value) |msg| pt.zcu.gpa.free(msg); // delete previous error message
@@ -3266,7 +3297,9 @@ fn processExportsInner(
pub fn populateTestFunctions(pt: Zcu.PerThread) Allocator.Error!void {
const zcu = pt.zcu;
const gpa = zcu.gpa;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const ip = &zcu.intern_pool;
// Our job is to correctly set the value of the `test_functions` declaration if it has been
@@ -3284,7 +3317,7 @@ pub fn populateTestFunctions(pt: Zcu.PerThread) Allocator.Error!void {
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),
try ip.getOrPutString(gpa, io, pt.tid, "test_functions", .no_embedded_nulls),
Zcu.Namespace.NameAdapter{ .zcu = zcu },
).?;
// ...but it might not be populated, so let's check that!
@@ -3392,7 +3425,7 @@ pub fn populateTestFunctions(pt: Zcu.PerThread) Allocator.Error!void {
} }),
.len = (try pt.intValue(Type.usize, zcu.test_functions.count())).toIntern(),
} });
ip.mutateVarInit(test_fns_val.toIntern(), new_init);
ip.mutateVarInit(io, test_fns_val.toIntern(), new_init);
}
// The linker thread is not running, so we actually need to dispatch this task directly.
@import("../link.zig").linkTestFunctionsNav(pt, nav_index);
@@ -3407,7 +3440,9 @@ pub fn reportRetryableFileError(
args: anytype,
) error{OutOfMemory}!void {
const zcu = pt.zcu;
const gpa = zcu.gpa;
const comp = zcu.comp;
const io = comp.io;
const gpa = comp.gpa;
const file = zcu.fileByIndex(file_index);
@@ -3417,8 +3452,8 @@ pub fn reportRetryableFileError(
errdefer gpa.free(msg);
const old_msg: ?[]u8 = old_msg: {
zcu.comp.mutex.lock();
defer zcu.comp.mutex.unlock();
comp.mutex.lockUncancelable(io);
defer comp.mutex.unlock(io);
const gop = try zcu.failed_files.getOrPut(gpa, file_index);
const old: ?[]u8 = if (gop.found_existing) old: {
@@ -3433,12 +3468,8 @@ pub fn reportRetryableFileError(
/// Shortcut for calling `intern_pool.get`.
pub fn intern(pt: Zcu.PerThread, key: InternPool.Key) Allocator.Error!InternPool.Index {
return pt.zcu.intern_pool.get(pt.zcu.gpa, pt.tid, key);
}
/// Shortcut for calling `intern_pool.getUnion`.
pub fn internUnion(pt: Zcu.PerThread, un: InternPool.Key.Union) Allocator.Error!InternPool.Index {
return pt.zcu.intern_pool.getUnion(pt.zcu.gpa, pt.tid, un);
const comp = pt.zcu.comp;
return pt.zcu.intern_pool.get(comp.gpa, comp.io, pt.tid, key);
}
/// Essentially a shortcut for calling `intern_pool.getCoerced`.
@@ -3446,6 +3477,9 @@ pub fn internUnion(pt: Zcu.PerThread, un: InternPool.Key.Union) Allocator.Error!
/// this because it requires potentially pushing to the job queue.
pub fn getCoerced(pt: Zcu.PerThread, val: Value, new_ty: Type) Allocator.Error!Value {
const ip = &pt.zcu.intern_pool;
const comp = pt.zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
switch (ip.indexToKey(val.toIntern())) {
.@"extern" => |e| {
const coerced = try pt.getExtern(.{
@@ -3468,7 +3502,7 @@ pub fn getCoerced(pt: Zcu.PerThread, val: Value, new_ty: Type) Allocator.Error!V
},
else => {},
}
return Value.fromInterned(try ip.getCoerced(pt.zcu.gpa, pt.tid, val.toIntern(), new_ty.toIntern()));
return Value.fromInterned(try ip.getCoerced(gpa, io, pt.tid, val.toIntern(), new_ty.toIntern()));
}
pub fn intType(pt: Zcu.PerThread, signedness: std.builtin.Signedness, bits: u16) Allocator.Error!Type {
@@ -3566,7 +3600,8 @@ pub fn adjustPtrTypeChild(pt: Zcu.PerThread, ptr_ty: Type, new_child: Type) Allo
}
pub fn funcType(pt: Zcu.PerThread, key: InternPool.GetFuncTypeKey) Allocator.Error!Type {
return Type.fromInterned(try pt.zcu.intern_pool.getFuncType(pt.zcu.gpa, pt.tid, key));
const comp = pt.zcu.comp;
return .fromInterned(try pt.zcu.intern_pool.getFuncType(comp.gpa, comp.io, pt.tid, key));
}
/// Use this for `anyframe->T` only.
@@ -3584,7 +3619,8 @@ pub fn errorUnionType(pt: Zcu.PerThread, error_set_ty: Type, payload_ty: Type) A
pub fn singleErrorSetType(pt: Zcu.PerThread, name: InternPool.NullTerminatedString) Allocator.Error!Type {
const names: *const [1]InternPool.NullTerminatedString = &name;
return Type.fromInterned(try pt.zcu.intern_pool.getErrorSetType(pt.zcu.gpa, pt.tid, names));
const comp = pt.zcu.comp;
return Type.fromInterned(try pt.zcu.intern_pool.getErrorSetType(comp.gpa, comp.io, pt.tid, names));
}
/// Sorts `names` in place.
@@ -3598,7 +3634,8 @@ pub fn errorSetFromUnsortedNames(
{},
InternPool.NullTerminatedString.indexLessThan,
);
const new_ty = try pt.zcu.intern_pool.getErrorSetType(pt.zcu.gpa, pt.tid, names);
const comp = pt.zcu.comp;
const new_ty = try pt.zcu.intern_pool.getErrorSetType(comp.gpa, comp.io, pt.tid, names);
return Type.fromInterned(new_ty);
}
@@ -3709,9 +3746,17 @@ pub fn intValue_i64(pt: Zcu.PerThread, ty: Type, x: i64) Allocator.Error!Value {
} }));
}
/// Shortcut for calling `intern_pool.getUnion`.
/// TODO: remove either this or `unionValue`.
pub fn internUnion(pt: Zcu.PerThread, un: InternPool.Key.Union) Allocator.Error!InternPool.Index {
const comp = pt.zcu.comp;
return pt.zcu.intern_pool.getUnion(comp.gpa, comp.io, pt.tid, un);
}
/// TODO: remove either this or `internUnion`.
pub fn unionValue(pt: Zcu.PerThread, union_ty: Type, tag: Value, val: Value) Allocator.Error!Value {
const zcu = pt.zcu;
return Value.fromInterned(try zcu.intern_pool.getUnion(zcu.gpa, pt.tid, .{
const comp = pt.zcu.comp;
return Value.fromInterned(try pt.zcu.intern_pool.getUnion(comp.gpa, comp.io, pt.tid, .{
.ty = union_ty.toIntern(),
.tag = tag.toIntern(),
.val = val.toIntern(),
@@ -3771,12 +3816,12 @@ pub fn nullValue(pt: Zcu.PerThread, opt_ty: Type) Allocator.Error!Value {
/// `ty` is an integer or a vector of integers.
pub fn overflowArithmeticTupleType(pt: Zcu.PerThread, ty: Type) !Type {
const zcu = pt.zcu;
const ip = &zcu.intern_pool;
const comp = zcu.comp;
const ov_ty: Type = if (ty.zigTypeTag(zcu) == .vector) try pt.vectorType(.{
.len = ty.vectorLen(zcu),
.child = .u1_type,
}) else .u1;
const tuple_ty = try ip.getTupleType(zcu.gpa, pt.tid, .{
const tuple_ty = try zcu.intern_pool.getTupleType(comp.gpa, comp.io, pt.tid, .{
.types = &.{ ty.toIntern(), ov_ty.toIntern() },
.values = &.{ .none, .none },
});
@@ -3872,12 +3917,14 @@ pub fn navPtrType(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Allocator.Err
/// If necessary, the new `Nav` is queued for codegen.
/// `key.owner_nav` is ignored and may be `undefined`.
pub fn getExtern(pt: Zcu.PerThread, key: InternPool.Key.Extern) Allocator.Error!InternPool.Index {
const result = try pt.zcu.intern_pool.getExtern(pt.zcu.gpa, pt.tid, key);
const zcu = pt.zcu;
const comp = zcu.comp;
const result = try zcu.intern_pool.getExtern(comp.gpa, comp.io, 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);
comp.link_prog_node.increaseEstimatedTotalItems(1);
try comp.queueJob(.{ .link_nav = nav });
if (comp.debugIncremental()) try zcu.incremental_debug_state.newNav(zcu, nav);
}
return result.index;
}
@@ -3966,7 +4013,9 @@ fn recreateStructType(
key: InternPool.Key.NamespaceType.Declared,
) Allocator.Error!InternPool.Index {
const zcu = pt.zcu;
const gpa = zcu.gpa;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const ip = &zcu.intern_pool;
const inst_info = key.zir_index.resolveFull(ip).?;
@@ -3995,7 +4044,7 @@ fn recreateStructType(
const struct_obj = ip.loadStructType(old_ty);
const wip_ty = switch (try ip.getStructType(gpa, pt.tid, .{
const wip_ty = switch (try ip.getStructType(gpa, io, pt.tid, .{
.layout = small.layout,
.fields_len = fields_len,
.known_non_opv = small.known_non_opv,
@@ -4042,7 +4091,9 @@ fn recreateUnionType(
key: InternPool.Key.NamespaceType.Declared,
) Allocator.Error!InternPool.Index {
const zcu = pt.zcu;
const gpa = zcu.gpa;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const ip = &zcu.intern_pool;
const inst_info = key.zir_index.resolveFull(ip).?;
@@ -4075,7 +4126,7 @@ fn recreateUnionType(
const namespace_index = union_obj.namespace;
const wip_ty = switch (try ip.getUnionType(gpa, pt.tid, .{
const wip_ty = switch (try ip.getUnionType(gpa, io, pt.tid, .{
.flags = .{
.layout = small.layout,
.status = .none,
@@ -4133,7 +4184,9 @@ fn recreateEnumType(
key: InternPool.Key.NamespaceType.Declared,
) (Allocator.Error || Io.Cancelable)!InternPool.Index {
const zcu = pt.zcu;
const gpa = zcu.gpa;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const ip = &zcu.intern_pool;
const inst_info = key.zir_index.resolveFull(ip).?;
@@ -4197,7 +4250,7 @@ fn recreateEnumType(
const namespace_index = enum_obj.namespace;
const wip_ty = switch (try ip.getEnumType(gpa, pt.tid, .{
const wip_ty = switch (try ip.getEnumType(gpa, io, pt.tid, .{
.has_values = any_values,
.tag_mode = if (small.nonexhaustive)
.nonexhaustive
@@ -4404,7 +4457,7 @@ pub fn refValue(pt: Zcu.PerThread, val: InternPool.Index) Zcu.SemaError!InternPo
pub fn addDependency(pt: Zcu.PerThread, unit: AnalUnit, dependee: InternPool.Dependee) Allocator.Error!void {
const zcu = pt.zcu;
const gpa = zcu.gpa;
const gpa = zcu.comp.gpa;
try zcu.intern_pool.addDependency(gpa, unit, dependee);
if (zcu.comp.debugIncremental()) {
const info = try zcu.incremental_debug_state.getUnitInfo(gpa, unit);
@@ -4412,50 +4465,38 @@ pub fn addDependency(pt: Zcu.PerThread, unit: AnalUnit, dependee: InternPool.Dep
}
}
/// 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 {
pub const RunCodegenError = Io.Cancelable || error{AlreadyReported};
/// Performs code generation, which comes after `Sema` but before `link` in the pipeline. This part
/// of the pipeline is self-contained and can usually be run concurrently with other components.
///
/// This function is called asynchronously by `Zcu.CodegenTaskPool.start` and awaited by the linker.
/// However, if the codegen backend does not support `Zcu.Feature.separate_thread`, then
/// `Compilation.processOneJob` will immediately await the result of the linker task, meaning the
/// pipeline becomes effectively single-threaded.
pub fn runCodegen(pt: Zcu.PerThread, func_index: InternPool.Index, air: *Air) RunCodegenError!codegen.AnyMir {
const zcu = pt.zcu;
const comp = zcu.comp;
const io = comp.io;
crash_report.CodegenFunc.start(zcu, func_index);
defer crash_report.CodegenFunc.stop(func_index);
var timer = zcu.comp.startTimer();
var timer = comp.startTimer();
const success: bool = if (runCodegenInner(pt, func_index, air)) |mir| success: {
out.value = mir;
break :success true;
} else |err| success: {
switch (err) {
error.OutOfMemory => zcu.comp.setAllocFailure(),
error.CodegenFail => zcu.assertCodegenFailed(zcu.funcInfo(func_index).owner_nav),
error.NoLinkFile => assert(zcu.comp.bin_file == null),
error.BackendDoesNotProduceMir => switch (target_util.zigBackend(
&zcu.root_mod.resolved_target.result,
zcu.comp.config.use_llvm,
)) {
else => unreachable, // assertion failure
.stage2_spirv,
.stage2_llvm,
=> {},
},
}
break :success false;
};
const codegen_result = runCodegenInner(pt, func_index, air);
if (timer.finish()) |ns_codegen| report_time: {
const ip = &zcu.intern_pool;
const nav = ip.indexToKey(func_index).func.owner_nav;
const zir_decl = ip.getNav(nav).srcInst(ip);
zcu.comp.mutex.lock();
defer zcu.comp.mutex.unlock();
comp.mutex.lockUncancelable(io);
defer comp.mutex.unlock(io);
const tr = &zcu.comp.time_report.?;
tr.stats.cpu_ns_codegen += ns_codegen;
const gop = tr.decl_codegen_ns.getOrPut(zcu.gpa, zir_decl) catch |err| switch (err) {
const gop = tr.decl_codegen_ns.getOrPut(comp.gpa, zir_decl) catch |err| switch (err) {
error.OutOfMemory => {
zcu.comp.setAllocFailure();
comp.setAllocFailure();
break :report_time;
},
};
@@ -4463,14 +4504,29 @@ pub fn runCodegen(pt: Zcu.PerThread, func_index: InternPool.Index, air: *Air, ou
gop.value_ptr.* += ns_codegen;
}
// release `out.value` with this store; synchronizes with acquire loads in `link`
out.status.store(if (success) .ready else .failed, .release);
zcu.comp.link_task_queue.mirReady(zcu.comp, func_index, 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;
}
return codegen_result catch |err| {
switch (err) {
error.OutOfMemory => comp.setAllocFailure(),
error.CodegenFail => zcu.assertCodegenFailed(zcu.funcInfo(func_index).owner_nav),
error.NoLinkFile => assert(comp.bin_file == null),
error.BackendDoesNotProduceMir => switch (target_util.zigBackend(
&zcu.root_mod.resolved_target.result,
comp.config.use_llvm,
)) {
else => unreachable, // assertion failure
.stage2_spirv,
.stage2_llvm,
=> {},
},
}
return error.AlreadyReported;
};
}
fn runCodegenInner(pt: Zcu.PerThread, func_index: InternPool.Index, air: *Air) error{
OutOfMemory,
@@ -4527,7 +4583,7 @@ 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| {
assert(pt.tid == .main); // LLVM has a lot of shared state
assert(zcu.pending_codegen_jobs.load(.monotonic) == 2); // only one codegen at a time (but the value is 2 because 1 is the base)
try llvm_object.updateFunc(pt, func_index, air, &liveness);
return error.BackendDoesNotProduceMir;
}
@@ -4536,7 +4592,7 @@ fn runCodegenInner(pt: Zcu.PerThread, func_index: InternPool.Index, air: *Air) e
// Just like LLVM, the SPIR-V backend can't multi-threaded due to SPIR-V design limitations.
if (lf.cast(.spirv)) |spirv_file| {
assert(pt.tid == .main); // SPIR-V has a lot of shared state
assert(zcu.pending_codegen_jobs.load(.monotonic) == 2); // only one codegen at a time (but the value is 2 because 1 is the base)
spirv_file.updateFunc(pt, func_index, air, &liveness) catch |err| {
switch (err) {
error.OutOfMemory => comp.link_diags.setAllocFailure(),
+9 -6
View File
@@ -2270,6 +2270,9 @@ fn buildWideMul(
) !struct { Temporary, Temporary } {
const pt = cg.pt;
const zcu = cg.module.zcu;
const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const target = cg.module.zcu.getTarget();
const ip = &zcu.intern_pool;
@@ -2297,14 +2300,14 @@ fn buildWideMul(
};
for (0..ops) |i| {
try cg.body.emit(cg.module.gpa, .OpIMul, .{
try cg.body.emit(gpa, .OpIMul, .{
.id_result_type = arith_op_ty_id,
.id_result = value_results.at(i),
.operand_1 = lhs_op.at(i),
.operand_2 = rhs_op.at(i),
});
try cg.body.emit(cg.module.gpa, .OpExtInst, .{
try cg.body.emit(gpa, .OpExtInst, .{
.id_result_type = arith_op_ty_id,
.id_result = overflow_results.at(i),
.set = set,
@@ -2316,7 +2319,7 @@ fn buildWideMul(
.vulkan, .opengl => {
// Operations return a struct{T, T}
// where T is maybe vectorized.
const op_result_ty: Type = .fromInterned(try ip.getTupleType(zcu.gpa, pt.tid, .{
const op_result_ty: Type = .fromInterned(try ip.getTupleType(gpa, io, pt.tid, .{
.types = &.{ arith_op_ty.toIntern(), arith_op_ty.toIntern() },
.values = &.{ .none, .none },
}));
@@ -2330,7 +2333,7 @@ fn buildWideMul(
for (0..ops) |i| {
const op_result = cg.module.allocId();
try cg.body.emitRaw(cg.module.gpa, opcode, 4);
try cg.body.emitRaw(gpa, opcode, 4);
cg.body.writeOperand(Id, op_result_ty_id);
cg.body.writeOperand(Id, op_result);
cg.body.writeOperand(Id, lhs_op.at(i));
@@ -2340,14 +2343,14 @@ fn buildWideMul(
// Temporary to deal with the fact that these are structs eventually,
// but for now, take the struct apart and return two separate vectors.
try cg.body.emit(cg.module.gpa, .OpCompositeExtract, .{
try cg.body.emit(gpa, .OpCompositeExtract, .{
.id_result_type = arith_op_ty_id,
.id_result = value_results.at(i),
.composite = op_result,
.indexes = &.{0},
});
try cg.body.emit(cg.module.gpa, .OpCompositeExtract, .{
try cg.body.emit(gpa, .OpCompositeExtract, .{
.id_result_type = arith_op_ty_id,
.id_result = overflow_results.at(i),
.composite = op_result,
+10 -8
View File
@@ -180204,6 +180204,7 @@ fn airSplat(self: *CodeGen, inst: Air.Inst.Index) !void {
fn airSelect(self: *CodeGen, inst: Air.Inst.Index) !void {
const pt = self.pt;
const zcu = pt.zcu;
const io = zcu.comp.io;
const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
const extra = self.air.extraData(Air.Bin, pl_op.payload).data;
const ty = self.typeOfIndex(inst);
@@ -180477,7 +180478,7 @@ fn airSelect(self: *CodeGen, inst: Air.Inst.Index) !void {
for (mask_elems, 0..) |*elem, bit| elem.* = @intCast(bit / elem_bits);
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) },
.storage = .{ .bytes = try zcu.intern_pool.getOrPutString(zcu.gpa, io, pt.tid, mask_elems, .maybe_embedded_nulls) },
} })));
const mask_mem: Memory = .{
.base = .{ .reg = try self.copyToTmpRegister(.usize, mask_mcv.address()) },
@@ -188476,6 +188477,7 @@ const Select = struct {
fn create(spec: TempSpec, s: *const Select) InnerError!struct { Temp, bool } {
const cg = s.cg;
const pt = cg.pt;
const io = pt.zcu.comp.io;
return switch (spec.kind) {
.unused => .{ undefined, false },
.any => .{ try cg.tempAlloc(spec.type), true },
@@ -188693,7 +188695,7 @@ const Select = struct {
};
return .{ try cg.tempMemFromValue(.fromInterned(try pt.intern(.{ .aggregate = .{
.ty = spec.type.toIntern(),
.storage = .{ .bytes = try zcu.intern_pool.getOrPutString(zcu.gpa, pt.tid, elems, .maybe_embedded_nulls) },
.storage = .{ .bytes = try zcu.intern_pool.getOrPutString(zcu.gpa, io, pt.tid, elems, .maybe_embedded_nulls) },
} }))), true };
},
.pshufb_trunc_mem => |trunc_spec| {
@@ -188720,7 +188722,7 @@ const Select = struct {
};
return .{ try cg.tempMemFromValue(.fromInterned(try pt.intern(.{ .aggregate = .{
.ty = spec.type.toIntern(),
.storage = .{ .bytes = try zcu.intern_pool.getOrPutString(zcu.gpa, pt.tid, elems, .maybe_embedded_nulls) },
.storage = .{ .bytes = try zcu.intern_pool.getOrPutString(zcu.gpa, io, pt.tid, elems, .maybe_embedded_nulls) },
} }))), true };
},
.pand_trunc_mem => |trunc_spec| {
@@ -188734,7 +188736,7 @@ const Select = struct {
while (index < elems.len) : (index += from_bytes) @memset(elems[index..][0..to_bytes], std.math.maxInt(u8));
return .{ try cg.tempMemFromValue(.fromInterned(try pt.intern(.{ .aggregate = .{
.ty = spec.type.toIntern(),
.storage = .{ .bytes = try zcu.intern_pool.getOrPutString(zcu.gpa, pt.tid, elems, .maybe_embedded_nulls) },
.storage = .{ .bytes = try zcu.intern_pool.getOrPutString(zcu.gpa, io, pt.tid, elems, .maybe_embedded_nulls) },
} }))), true };
},
.pand_mask_mem => |mask_spec| {
@@ -188753,7 +188755,7 @@ const Select = struct {
@memset(elems[mask_len..], invert_mask);
return .{ try cg.tempMemFromValue(.fromInterned(try pt.intern(.{ .aggregate = .{
.ty = spec.type.toIntern(),
.storage = .{ .bytes = try zcu.intern_pool.getOrPutString(zcu.gpa, pt.tid, elems, .maybe_embedded_nulls) },
.storage = .{ .bytes = try zcu.intern_pool.getOrPutString(zcu.gpa, io, pt.tid, elems, .maybe_embedded_nulls) },
} }))), true };
},
.ptest_mask_mem => |mask_ref| {
@@ -188778,7 +188780,7 @@ const Select = struct {
}
return .{ try cg.tempMemFromValue(.fromInterned(try pt.intern(.{ .aggregate = .{
.ty = spec.type.toIntern(),
.storage = .{ .bytes = try zcu.intern_pool.getOrPutString(zcu.gpa, pt.tid, elems, .maybe_embedded_nulls) },
.storage = .{ .bytes = try zcu.intern_pool.getOrPutString(zcu.gpa, io, pt.tid, elems, .maybe_embedded_nulls) },
} }))), true };
},
.pshufb_bswap_mem => |bswap_spec| {
@@ -188794,7 +188796,7 @@ const Select = struct {
};
return .{ try cg.tempMemFromValue(.fromInterned(try pt.intern(.{ .aggregate = .{
.ty = spec.type.toIntern(),
.storage = .{ .bytes = try zcu.intern_pool.getOrPutString(zcu.gpa, pt.tid, elems, .maybe_embedded_nulls) },
.storage = .{ .bytes = try zcu.intern_pool.getOrPutString(zcu.gpa, io, pt.tid, elems, .maybe_embedded_nulls) },
} }))), true };
},
.bits_mem => |direction| {
@@ -188808,7 +188810,7 @@ const Select = struct {
};
return .{ try cg.tempMemFromValue(.fromInterned(try pt.intern(.{ .aggregate = .{
.ty = spec.type.toIntern(),
.storage = .{ .bytes = try zcu.intern_pool.getOrPutString(zcu.gpa, pt.tid, elems, .maybe_embedded_nulls) },
.storage = .{ .bytes = try zcu.intern_pool.getOrPutString(zcu.gpa, io, pt.tid, elems, .maybe_embedded_nulls) },
} }))), true };
},
.splat_int_mem => |splat_spec| {
+6 -5
View File
@@ -991,7 +991,8 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) anye
});
}
fn queueSharedObjects(comp: *Compilation, so_files: BuiltSharedObjects) void {
fn queueSharedObjects(comp: *Compilation, so_files: BuiltSharedObjects) std.Io.Cancelable!void {
const io = comp.io;
const target = comp.getTarget();
const target_os_version = target.os.version_range.semver.min;
@@ -1002,8 +1003,8 @@ fn queueSharedObjects(comp: *Compilation, so_files: BuiltSharedObjects) void {
var task_buffer_i: usize = 0;
{
comp.mutex.lock(); // protect comp.arena
defer comp.mutex.unlock();
comp.mutex.lockUncancelable(io); // protect comp.arena
defer comp.mutex.unlock(io);
for (libs) |lib| {
if (lib.added_in) |add_in| {
@@ -1021,7 +1022,7 @@ fn queueSharedObjects(comp: *Compilation, so_files: BuiltSharedObjects) void {
}
}
comp.queuePrelinkTasks(task_buffer[0..task_buffer_i]);
try comp.queuePrelinkTasks(task_buffer[0..task_buffer_i]);
}
fn buildSharedLib(
@@ -1094,8 +1095,8 @@ fn buildSharedLib(
var sub_create_diag: Compilation.CreateDiagnostic = undefined;
const sub_compilation = Compilation.create(comp.gpa, arena, io, &sub_create_diag, .{
.thread_limit = comp.thread_limit,
.dirs = comp.dirs.withoutLocalCache(),
.thread_pool = comp.thread_pool,
.self_exe_path = comp.self_exe_path,
// 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.
+6 -5
View File
@@ -1135,7 +1135,8 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) anye
});
}
fn queueSharedObjects(comp: *Compilation, so_files: BuiltSharedObjects) void {
fn queueSharedObjects(comp: *Compilation, so_files: BuiltSharedObjects) std.Io.Cancelable!void {
const io = comp.io;
const target_version = comp.getTarget().os.versionRange().gnuLibCVersion().?;
assert(comp.glibc_so_files == null);
@@ -1145,8 +1146,8 @@ fn queueSharedObjects(comp: *Compilation, so_files: BuiltSharedObjects) void {
var task_buffer_i: usize = 0;
{
comp.mutex.lock(); // protect comp.arena
defer comp.mutex.unlock();
comp.mutex.lockUncancelable(io); // protect comp.arena
defer comp.mutex.unlock(io);
for (libs) |lib| {
if (lib.removed_in) |rem_in| {
@@ -1163,7 +1164,7 @@ fn queueSharedObjects(comp: *Compilation, so_files: BuiltSharedObjects) void {
}
}
comp.queuePrelinkTasks(task_buffer[0..task_buffer_i]);
try comp.queuePrelinkTasks(task_buffer[0..task_buffer_i]);
}
fn buildSharedLib(
@@ -1233,8 +1234,8 @@ fn buildSharedLib(
var sub_create_diag: Compilation.CreateDiagnostic = undefined;
const sub_compilation = Compilation.create(comp.gpa, arena, io, &sub_create_diag, .{
.thread_limit = comp.thread_limit,
.dirs = comp.dirs.withoutLocalCache(),
.thread_pool = comp.thread_pool,
.self_exe_path = comp.self_exe_path,
// 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.
+5 -5
View File
@@ -106,7 +106,7 @@ pub const BuildError = error{
OutOfMemory,
AlreadyReported,
ZigCompilerNotBuiltWithLLVMExtensions,
};
} || std.Io.Cancelable;
pub fn buildLibCxx(comp: *Compilation, prog_node: std.Progress.Node) BuildError!void {
if (!build_options.have_llvm) {
@@ -256,13 +256,13 @@ pub fn buildLibCxx(comp: *Compilation, prog_node: std.Progress.Node) BuildError!
var sub_create_diag: Compilation.CreateDiagnostic = undefined;
const sub_compilation = Compilation.create(comp.gpa, arena, io, &sub_create_diag, .{
.thread_limit = comp.thread_limit,
.dirs = comp.dirs.withoutLocalCache(),
.self_exe_path = comp.self_exe_path,
.cache_mode = .whole,
.config = config,
.root_mod = root_mod,
.root_name = root_name,
.thread_pool = comp.thread_pool,
.libc_installation = comp.libc_installation,
.emit_bin = .yes_cache,
.c_source_files = c_source_files.items,
@@ -295,7 +295,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.queuePrelinkTaskMode(crt_file.full_object_path, &config);
try comp.queuePrelinkTaskMode(crt_file.full_object_path, &config);
}
pub fn buildLibCxxAbi(comp: *Compilation, prog_node: std.Progress.Node) BuildError!void {
@@ -449,13 +449,13 @@ pub fn buildLibCxxAbi(comp: *Compilation, prog_node: std.Progress.Node) BuildErr
var sub_create_diag: Compilation.CreateDiagnostic = undefined;
const sub_compilation = Compilation.create(comp.gpa, arena, io, &sub_create_diag, .{
.thread_limit = comp.thread_limit,
.dirs = comp.dirs.withoutLocalCache(),
.self_exe_path = comp.self_exe_path,
.cache_mode = .whole,
.config = config,
.root_mod = root_mod,
.root_name = root_name,
.thread_pool = comp.thread_pool,
.libc_installation = comp.libc_installation,
.emit_bin = .yes_cache,
.c_source_files = c_source_files.items,
@@ -492,7 +492,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.queuePrelinkTaskMode(crt_file.full_object_path, &config);
try comp.queuePrelinkTaskMode(crt_file.full_object_path, &config);
}
pub fn addCxxArgs(
+3 -3
View File
@@ -11,7 +11,7 @@ pub const BuildError = error{
AlreadyReported,
ZigCompilerNotBuiltWithLLVMExtensions,
TSANUnsupportedCPUArchitecture,
};
} || std.Io.Cancelable;
pub fn buildTsan(comp: *Compilation, prog_node: std.Progress.Node) BuildError!void {
if (!build_options.have_llvm) {
@@ -279,8 +279,8 @@ pub fn buildTsan(comp: *Compilation, prog_node: std.Progress.Node) BuildError!vo
var sub_create_diag: Compilation.CreateDiagnostic = undefined;
const sub_compilation = Compilation.create(comp.gpa, arena, io, &sub_create_diag, .{
.thread_limit = comp.thread_limit,
.dirs = comp.dirs.withoutLocalCache(),
.thread_pool = comp.thread_pool,
.self_exe_path = comp.self_exe_path,
.cache_mode = .whole,
.config = config,
@@ -319,7 +319,7 @@ pub fn buildTsan(comp: *Compilation, prog_node: std.Progress.Node) BuildError!vo
};
const crt_file = try sub_compilation.toCrtFile();
comp.queuePrelinkTaskMode(crt_file.full_object_path, &config);
try comp.queuePrelinkTaskMode(crt_file.full_object_path, &config);
assert(comp.tsan_lib == null);
comp.tsan_lib = crt_file;
}
+3 -3
View File
@@ -12,7 +12,7 @@ pub const BuildError = error{
OutOfMemory,
AlreadyReported,
ZigCompilerNotBuiltWithLLVMExtensions,
};
} || std.Io.Cancelable;
pub fn buildStaticLib(comp: *Compilation, prog_node: std.Progress.Node) BuildError!void {
if (!build_options.have_llvm) {
@@ -145,6 +145,7 @@ pub fn buildStaticLib(comp: *Compilation, prog_node: std.Progress.Node) BuildErr
var sub_create_diag: Compilation.CreateDiagnostic = undefined;
const sub_compilation = Compilation.create(comp.gpa, arena, io, &sub_create_diag, .{
.thread_limit = comp.thread_limit,
.dirs = comp.dirs.withoutLocalCache(),
.self_exe_path = comp.self_exe_path,
.config = config,
@@ -152,7 +153,6 @@ pub fn buildStaticLib(comp: *Compilation, prog_node: std.Progress.Node) BuildErr
.cache_mode = .whole,
.root_name = root_name,
.main_mod = null,
.thread_pool = comp.thread_pool,
.libc_installation = comp.libc_installation,
.emit_bin = .yes_cache,
.function_sections = comp.function_sections,
@@ -184,7 +184,7 @@ pub fn buildStaticLib(comp: *Compilation, prog_node: std.Progress.Node) BuildErr
};
const crt_file = try sub_compilation.toCrtFile();
comp.queuePrelinkTaskMode(crt_file.full_object_path, &config);
try comp.queuePrelinkTaskMode(crt_file.full_object_path, &config);
assert(comp.libunwind_static_lib == null);
comp.libunwind_static_lib = crt_file;
}
+4 -4
View File
@@ -281,8 +281,8 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void {
const sub_path = try std.fs.path.join(gpa, &.{ "o", &digest, final_lib_basename });
errdefer gpa.free(sub_path);
comp.mutex.lock();
defer comp.mutex.unlock();
comp.mutex.lockUncancelable(io);
defer comp.mutex.unlock(io);
try comp.crt_files.ensureUnusedCapacity(gpa, 1);
comp.crt_files.putAssumeCapacityNoClobber(final_lib_basename, .{
.full_object_path = .{
@@ -388,8 +388,8 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void {
log.warn("failed to write cache manifest for DLL import {s}.lib: {s}", .{ lib_name, @errorName(err) });
};
comp.mutex.lock();
defer comp.mutex.unlock();
comp.mutex.lockUncancelable(io);
defer comp.mutex.unlock(io);
try comp.crt_files.putNoClobber(gpa, final_lib_basename, .{
.full_object_path = .{
.root_dir = comp.dirs.global_cache,
+4 -4
View File
@@ -248,12 +248,12 @@ pub fn buildCrtFile(comp: *Compilation, in_crt_file: CrtFile, prog_node: std.Pro
var sub_create_diag: Compilation.CreateDiagnostic = undefined;
const sub_compilation = Compilation.create(comp.gpa, arena, io, &sub_create_diag, .{
.thread_limit = comp.thread_limit,
.dirs = comp.dirs.withoutLocalCache(),
.self_exe_path = comp.self_exe_path,
.cache_mode = .whole,
.config = config,
.root_mod = root_mod,
.thread_pool = comp.thread_pool,
.root_name = "c",
.libc_installation = comp.libc_installation,
.emit_bin = .yes_cache,
@@ -287,10 +287,10 @@ 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.queuePrelinkTaskMode(crt_file.full_object_path, &config);
try comp.queuePrelinkTaskMode(crt_file.full_object_path, &config);
{
comp.mutex.lock();
defer comp.mutex.unlock();
comp.mutex.lockUncancelable(io);
defer comp.mutex.unlock(io);
try comp.crt_files.ensureUnusedCapacity(comp.gpa, 1);
comp.crt_files.putAssumeCapacityNoClobber(basename, crt_file);
}
+6 -5
View File
@@ -645,7 +645,8 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) anye
});
}
fn queueSharedObjects(comp: *Compilation, so_files: BuiltSharedObjects) void {
fn queueSharedObjects(comp: *Compilation, so_files: BuiltSharedObjects) std.Io.Cancelable!void {
const io = comp.io;
assert(comp.netbsd_so_files == null);
comp.netbsd_so_files = so_files;
@@ -653,8 +654,8 @@ fn queueSharedObjects(comp: *Compilation, so_files: BuiltSharedObjects) void {
var task_buffer_i: usize = 0;
{
comp.mutex.lock(); // protect comp.arena
defer comp.mutex.unlock();
comp.mutex.lockUncancelable(io); // protect comp.arena
defer comp.mutex.unlock(io);
for (libs) |lib| {
const so_path: Path = .{
@@ -668,7 +669,7 @@ fn queueSharedObjects(comp: *Compilation, so_files: BuiltSharedObjects) void {
}
}
comp.queuePrelinkTasks(task_buffer[0..task_buffer_i]);
try comp.queuePrelinkTasks(task_buffer[0..task_buffer_i]);
}
fn buildSharedLib(
@@ -737,8 +738,8 @@ fn buildSharedLib(
var sub_create_diag: Compilation.CreateDiagnostic = undefined;
const sub_compilation = Compilation.create(comp.gpa, arena, io, &sub_create_diag, .{
.thread_limit = comp.thread_limit,
.dirs = comp.dirs.withoutLocalCache(),
.thread_pool = comp.thread_pool,
.self_exe_path = comp.self_exe_path,
// 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.
+64 -93
View File
@@ -34,7 +34,8 @@ pub const Diags = struct {
/// Stored here so that function definitions can distinguish between
/// needing an allocator for things besides error reporting.
gpa: Allocator,
mutex: std.Thread.Mutex,
io: Io,
mutex: Io.Mutex,
msgs: std.ArrayList(Msg),
flags: Flags,
lld: std.ArrayList(Lld),
@@ -126,10 +127,11 @@ pub const Diags = struct {
}
};
pub fn init(gpa: Allocator) Diags {
pub fn init(gpa: Allocator, io: Io) Diags {
return .{
.gpa = gpa,
.mutex = .{},
.io = io,
.mutex = .init,
.msgs = .empty,
.flags = .{},
.lld = .empty,
@@ -153,8 +155,10 @@ pub const Diags = struct {
}
pub fn lockAndParseLldStderr(diags: *Diags, prefix: []const u8, stderr: []const u8) void {
diags.mutex.lock();
defer diags.mutex.unlock();
const io = diags.io;
diags.mutex.lockUncancelable(io);
defer diags.mutex.unlock(io);
diags.parseLldStderr(prefix, stderr) catch diags.setAllocFailure();
}
@@ -226,9 +230,10 @@ pub const Diags = struct {
pub fn addErrorSourceLocation(diags: *Diags, sl: SourceLocation, comptime format: []const u8, args: anytype) void {
@branchHint(.cold);
const gpa = diags.gpa;
const io = diags.io;
const eu_main_msg = std.fmt.allocPrint(gpa, format, args);
diags.mutex.lock();
defer diags.mutex.unlock();
diags.mutex.lockUncancelable(io);
defer diags.mutex.unlock(io);
addErrorLockedFallible(diags, sl, eu_main_msg) catch |err| switch (err) {
error.OutOfMemory => diags.setAllocFailureLocked(),
};
@@ -247,8 +252,9 @@ pub const Diags = struct {
pub fn addErrorWithNotes(diags: *Diags, note_count: usize) error{OutOfMemory}!ErrorWithNotes {
@branchHint(.cold);
const gpa = diags.gpa;
diags.mutex.lock();
defer diags.mutex.unlock();
const io = diags.io;
diags.mutex.lockUncancelable(io);
defer diags.mutex.unlock(io);
try diags.msgs.ensureUnusedCapacity(gpa, 1);
return addErrorWithNotesAssumeCapacity(diags, note_count);
}
@@ -276,9 +282,10 @@ pub const Diags = struct {
) void {
@branchHint(.cold);
const gpa = diags.gpa;
const io = diags.io;
const eu_main_msg = std.fmt.allocPrint(gpa, format, args);
diags.mutex.lock();
defer diags.mutex.unlock();
diags.mutex.lockUncancelable(io);
defer diags.mutex.unlock(io);
addMissingLibraryErrorLockedFallible(diags, checked_paths, eu_main_msg) catch |err| switch (err) {
error.OutOfMemory => diags.setAllocFailureLocked(),
};
@@ -312,9 +319,10 @@ pub const Diags = struct {
) void {
@branchHint(.cold);
const gpa = diags.gpa;
const io = diags.io;
const eu_main_msg = std.fmt.allocPrint(gpa, format, args);
diags.mutex.lock();
defer diags.mutex.unlock();
diags.mutex.lockUncancelable(io);
defer diags.mutex.unlock(io);
addParseErrorLockedFallible(diags, path, eu_main_msg) catch |err| switch (err) {
error.OutOfMemory => diags.setAllocFailureLocked(),
};
@@ -349,8 +357,9 @@ pub const Diags = struct {
pub fn setAllocFailure(diags: *Diags) void {
@branchHint(.cold);
diags.mutex.lock();
defer diags.mutex.unlock();
const io = diags.io;
diags.mutex.lockUncancelable(io);
defer diags.mutex.unlock(io);
setAllocFailureLocked(diags);
}
@@ -1101,6 +1110,7 @@ pub const File = struct {
const comp = base.comp;
const diags = &comp.link_diags;
const gpa = comp.gpa;
const io = comp.io;
const stat = try file.stat();
const size = std.math.cast(u32, stat.size) orelse return error.FileTooBig;
const buf = try gpa.alloc(u8, size);
@@ -1123,8 +1133,8 @@ pub const File = struct {
} else {
if (fs.path.isAbsolute(arg.path)) {
const new_path = Path.initCwd(path: {
comp.mutex.lock();
defer comp.mutex.unlock();
comp.mutex.lockUncancelable(io);
defer comp.mutex.unlock(io);
break :path try comp.arena.dupe(u8, arg.path);
});
switch (Compilation.classifyFileExt(arg.path)) {
@@ -1309,61 +1319,13 @@ 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: LinkFunc,
link_func: Zcu.CodegenTaskPool.Index,
link_type: InternPool.Index,
update_line_number: InternPool.TrackedInst.Index,
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(.acquire)) {
.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 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 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
/// 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 doPrelinkTask(comp: *Compilation, task: PrelinkTask) void {
const io = comp.io;
const diags = &comp.link_diags;
const base = comp.bin_file orelse {
comp.link_prog_node.completeOne();
@@ -1372,8 +1334,8 @@ pub fn doPrelinkTask(comp: *Compilation, task: PrelinkTask) void {
var timer = comp.startTimer();
defer if (timer.finish()) |ns| {
comp.mutex.lock();
defer comp.mutex.unlock();
comp.mutex.lockUncancelable(io);
defer comp.mutex.unlock(io);
comp.time_report.?.stats.cpu_ns_link += ns;
};
@@ -1484,6 +1446,7 @@ pub fn doPrelinkTask(comp: *Compilation, task: PrelinkTask) void {
}
}
pub fn doZcuTask(comp: *Compilation, tid: usize, task: ZcuTask) void {
const io = comp.io;
const diags = &comp.link_diags;
const zcu = comp.zcu.?;
const ip = &zcu.intern_pool;
@@ -1492,8 +1455,8 @@ pub fn doZcuTask(comp: *Compilation, tid: usize, task: ZcuTask) void {
var timer = comp.startTimer();
switch (task) {
.link_nav => |nav_index| {
const maybe_nav: ?InternPool.Nav.Index = switch (task) {
.link_nav => |nav_index| nav: {
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();
@@ -1514,21 +1477,25 @@ pub fn doZcuTask(comp: *Compilation, tid: usize, task: ZcuTask) void {
},
};
}
break :nav nav_index;
},
.link_func => |func| {
const nav = zcu.funcInfo(func.func).owner_nav;
.link_func => |codegen_task| nav: {
timer.pause();
const func, var mir = codegen_task.wait(&zcu.codegen_task_pool, io) catch |err| switch (err) {
error.Canceled, error.AlreadyReported => return,
};
defer mir.deinit(zcu);
timer.@"resume"();
const nav = zcu.funcInfo(func).owner_nav;
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(.acquire)) {
.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) catch |err| switch (err) {
lf.updateFunc(pt, func, &mir) catch |err| switch (err) {
error.OutOfMemory => return diags.setAllocFailure(),
error.CodegenFail => return zcu.assertCodegenFailed(nav),
error.Overflow, error.RelocationNotByteAligned => {
@@ -1539,8 +1506,9 @@ pub fn doZcuTask(comp: *Compilation, tid: usize, task: ZcuTask) void {
},
};
}
break :nav ip.indexToKey(func).func.owner_nav;
},
.link_type => |ty| {
.link_type => |ty| nav: {
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();
@@ -1552,8 +1520,9 @@ pub fn doZcuTask(comp: *Compilation, tid: usize, task: ZcuTask) void {
};
}
}
break :nav null;
},
.update_line_number => |ti| {
.update_line_number => |ti| nav: {
const nav_prog_node = comp.link_prog_node.start("Update line number", 0);
defer nav_prog_node.end();
if (pt.zcu.llvm_object == null) {
@@ -1564,21 +1533,18 @@ pub fn doZcuTask(comp: *Compilation, tid: usize, task: ZcuTask) void {
};
}
}
break :nav null;
},
}
};
if (timer.finish()) |ns_link| report_time: {
const zir_decl: ?InternPool.TrackedInst.Index = switch (task) {
.link_type, .update_line_number => null,
.link_nav => |nav| ip.getNav(nav).srcInst(ip),
.link_func => |f| ip.getNav(ip.indexToKey(f.func).func.owner_nav).srcInst(ip),
};
comp.mutex.lock();
defer comp.mutex.unlock();
comp.mutex.lockUncancelable(io);
defer comp.mutex.unlock(io);
const tr = &zcu.comp.time_report.?;
tr.stats.cpu_ns_link += ns_link;
if (zir_decl) |inst| {
const gop = tr.decl_link_ns.getOrPut(zcu.gpa, inst) catch |err| switch (err) {
if (maybe_nav) |nav| {
const zir_decl = ip.getNav(nav).srcInst(ip);
const gop = tr.decl_link_ns.getOrPut(zcu.gpa, zir_decl) catch |err| switch (err) {
error.OutOfMemory => {
zcu.comp.setAllocFailure();
break :report_time;
@@ -2208,8 +2174,13 @@ fn resolvePathInputLib(
const n2 = file.preadAll(buf2, n) catch |err|
fatal("failed to read {f}: {s}", .{ test_path, @errorName(err) });
if (n2 != buf2.len) fatal("failed to read {f}: unexpected end of file", .{test_path});
var diags = Diags.init(gpa);
// This `Io` is only used for a mutex, and we know we aren't doing anything async/concurrent.
var threaded: Io.Threaded = .init_single_threaded;
defer threaded.deinit();
var diags: Diags = .init(gpa, threaded.io());
defer diags.deinit();
const ld_script_result = LdScript.parse(gpa, &diags, test_path, ld_script_bytes.items);
if (diags.hasErrors()) {
var wip_errors: std.zig.ErrorBundle.Wip = undefined;
+3 -2
View File
@@ -713,6 +713,7 @@ pub fn allocateChunk(self: *Elf, args: struct {
pub fn loadInput(self: *Elf, input: link.Input) !void {
const comp = self.base.comp;
const gpa = comp.gpa;
const io = comp.io;
const diags = &comp.link_diags;
const target = self.getTarget();
const debug_fmt_strip = comp.config.debug_format == .strip;
@@ -720,8 +721,8 @@ pub fn loadInput(self: *Elf, input: link.Input) !void {
const is_static_lib = self.base.isStaticLib();
if (comp.verbose_link) {
comp.mutex.lock(); // protect comp.arena
defer comp.mutex.unlock();
comp.mutex.lockUncancelable(io); // protect comp.arena
defer comp.mutex.unlock(io);
const argv = &self.dump_argv_list;
switch (input) {
+2 -2
View File
@@ -29,9 +29,9 @@ resolver: SymbolResolver = .{},
/// This table will be populated after `scanRelocs` has run.
/// Key is symbol index.
undefs: std.AutoArrayHashMapUnmanaged(SymbolResolver.Index, UndefRefs) = .empty,
undefs_mutex: std.Thread.Mutex = .{},
undefs_mutex: std.Io.Mutex = .init,
dupes: std.AutoArrayHashMapUnmanaged(SymbolResolver.Index, std.ArrayList(File.Index)) = .empty,
dupes_mutex: std.Thread.Mutex = .{},
dupes_mutex: std.Io.Mutex = .init,
dyld_info_cmd: macho.dyld_info_command = .{},
symtab_cmd: macho.symtab_command = .{},
+3 -2
View File
@@ -555,9 +555,10 @@ fn reportUndefSymbol(self: Atom, rel: Relocation, macho_file: *MachO) !bool {
const file = self.getFile(macho_file);
const ref = file.getSymbolRef(rel.target, macho_file);
if (ref.getFile(macho_file) == null) {
macho_file.undefs_mutex.lock();
defer macho_file.undefs_mutex.unlock();
const gpa = macho_file.base.comp.gpa;
const io = macho_file.base.comp.io;
macho_file.undefs_mutex.lockUncancelable(io);
defer macho_file.undefs_mutex.unlock(io);
const gop = try macho_file.undefs.getOrPut(gpa, file.getGlobals()[rel.target]);
if (!gop.found_existing) {
gop.value_ptr.* = .{ .refs = .{} };
+1 -1
View File
@@ -289,7 +289,7 @@ pub fn writeAdhocSignature(
self.code_directory.inner.nCodeSlots = total_pages;
// Calculate hash for each page (in file) and write it to the buffer
var hasher = Hasher(Sha256){ .allocator = allocator, .thread_pool = macho_file.base.comp.thread_pool };
var hasher = Hasher(Sha256){ .allocator = allocator, .io = macho_file.base.comp.io };
try hasher.hash(opts.file, self.code_directory.code_slots.items, .{
.chunk_size = self.page_size,
.max_file_size = opts.file_size,
+3 -2
View File
@@ -512,8 +512,9 @@ pub fn checkUndefs(self: InternalObject, macho_file: *MachO) !void {
const addUndef = struct {
fn addUndef(mf: *MachO, index: MachO.SymbolResolver.Index, tag: anytype) !void {
const gpa = mf.base.comp.gpa;
mf.undefs_mutex.lock();
defer mf.undefs_mutex.unlock();
const io = mf.base.comp.io;
mf.undefs_mutex.lockUncancelable(io);
defer mf.undefs_mutex.unlock(io);
const gop = try mf.undefs.getOrPut(gpa, index);
if (!gop.found_existing) {
gop.value_ptr.* = tag;
+3 -2
View File
@@ -242,6 +242,7 @@ pub const File = union(enum) {
const tracy = trace(@src());
defer tracy.end();
const io = macho_file.base.comp.io;
const gpa = macho_file.base.comp.gpa;
for (file.getSymbols(), file.getNlists(), 0..) |sym, nlist, i| {
@@ -252,8 +253,8 @@ pub const File = union(enum) {
const ref_file = ref.getFile(macho_file) orelse continue;
if (ref_file.getIndex() == file.getIndex()) continue;
macho_file.dupes_mutex.lock();
defer macho_file.dupes_mutex.unlock();
macho_file.dupes_mutex.lockUncancelable(io);
defer macho_file.dupes_mutex.unlock(io);
const gop = try macho_file.dupes.getOrPut(gpa, file.getGlobals()[i]);
if (!gop.found_existing) {
+7 -7
View File
@@ -3,7 +3,7 @@ pub fn ParallelHasher(comptime Hasher: type) type {
return struct {
allocator: Allocator,
thread_pool: *ThreadPool,
io: std.Io,
pub fn hash(self: Self, file: fs.File, out: [][hash_size]u8, opts: struct {
chunk_size: u64 = 0x4000,
@@ -12,7 +12,7 @@ pub fn ParallelHasher(comptime Hasher: type) type {
const tracy = trace(@src());
defer tracy.end();
var wg: WaitGroup = .{};
const io = self.io;
const file_size = blk: {
const file_size = opts.max_file_size orelse try file.getEndPos();
@@ -27,8 +27,8 @@ pub fn ParallelHasher(comptime Hasher: type) type {
defer self.allocator.free(results);
{
wg.reset();
defer wg.wait();
var group: std.Io.Group = .init;
errdefer group.cancel(io);
for (out, results, 0..) |*out_buf, *result, i| {
const fstart = i * chunk_size;
@@ -36,7 +36,7 @@ pub fn ParallelHasher(comptime Hasher: type) type {
file_size - fstart
else
chunk_size;
self.thread_pool.spawnWg(&wg, worker, .{
group.async(io, worker, .{
file,
fstart,
buffer[fstart..][0..fsize],
@@ -44,6 +44,8 @@ pub fn ParallelHasher(comptime Hasher: type) type {
&(result.*),
});
}
group.wait(io);
}
for (results) |result| _ = try result;
}
@@ -72,5 +74,3 @@ const std = @import("std");
const trace = @import("../../tracy.zig").trace;
const Allocator = mem.Allocator;
const ThreadPool = std.Thread.Pool;
const WaitGroup = std.Thread.WaitGroup;
-1
View File
@@ -773,7 +773,6 @@ fn writeHeader(macho_file: *MachO, ncmds: usize, sizeofcmds: usize) !void {
const std = @import("std");
const Path = std.Build.Cache.Path;
const WaitGroup = std.Thread.WaitGroup;
const assert = std.debug.assert;
const log = std.log.scoped(.link);
const macho = std.macho;
+1 -2
View File
@@ -15,7 +15,7 @@ pub fn calcUuid(comp: *const Compilation, file: fs.File, file_size: u64, out: *[
const hashes = try comp.gpa.alloc([Md5.digest_length]u8, actual_num_chunks);
defer comp.gpa.free(hashes);
var hasher = Hasher(Md5){ .allocator = comp.gpa, .thread_pool = comp.thread_pool };
var hasher = Hasher(Md5){ .allocator = comp.gpa, .io = comp.io };
try hasher.hash(file, hashes, .{
.chunk_size = chunk_size,
.max_file_size = file_size,
@@ -46,4 +46,3 @@ const trace = @import("../../tracy.zig").trace;
const Compilation = @import("../../Compilation.zig");
const Md5 = std.crypto.hash.Md5;
const Hasher = @import("hasher.zig").ParallelHasher;
const ThreadPool = std.Thread.Pool;
+158 -283
View File
@@ -1,254 +1,171 @@
//! 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.
//! There are two `std.Io.Queue`s, for prelink and ZCU tasks respectively. The compiler writes tasks
//! to these queues, and a single concurrent linker task receives and processes them. `Compilation`
//! is responsible for calling `finishPrelinkQueue` and `finishZcuQueue` once all relevant tasks
//! have been queued. All prelink tasks must be queued and completed before any ZCU tasks can be
//! processed.
//!
//! 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`.
//! If concurrency is unavailable, the `enqueuePrelink` and `enqueueZcu` functions will instead run
//! the given tasks immediately---the queues are unused.
//!
//! 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.
//! If the codegen backend does not permit concurrency, then `Compilation` will call `finishZcuQueue`
//! early so that the concurrent linker task exists after prelink and ZCU tasks will run
//! non-concurrently in `enqueueZcu`.
mutex: std.Thread.Mutex,
/// Validates that only one `flushTaskQueue` thread is running at a time.
flush_safety: std.debug.SafetyLock,
/// This is the concurrent call to `runLinkTasks`. It may be set to non-`null` in `start`, and is
/// set to `null` by the main thread after it is canceled. It is not otherwise modified; as such, it
/// may be checked non-atomically. If a task is being queued and this is `null`, tasks must be run
/// eagerly.
future: ?std.Io.Future(void),
/// This value is positive while there are still prelink tasks yet to be queued. Once they are
/// all queued, this value becomes 0, and ZCU tasks can be run. Guarded by `mutex`.
prelink_wait_count: u32,
/// This is only used if `future == null` during prelink. In that case, it is used to ensure that
/// only one prelink task is run at a time.
prelink_mutex: std.Io.Mutex,
/// Prelink tasks which have been enqueued and are not yet owned by the worker thread.
/// Allocated into `gpa`, guarded by `mutex`.
queued_prelink: std.ArrayList(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.ArrayList(PrelinkTask),
/// Only valid if `future != null`.
prelink_queue: std.Io.Queue(PrelinkTask),
/// Only valid if `future != null`.
zcu_queue: std.Io.Queue(ZcuTask),
/// Like `queued_prelink`, but for ZCU tasks.
/// Allocated into `gpa`, guarded by `mutex`.
queued_zcu: std.ArrayList(ZcuTask),
/// Like `wip_prelink`, but for ZCU tasks.
/// Allocated into `gpa`, accessed only by the worker thread.
wip_zcu: std.ArrayList(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,
/// 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.
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 `prelink_wait_count` 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: InternPool.Index,
},
/// 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 capacity of the task queue buffers.
pub const buffer_size = 512;
/// The initial `Queue` state, containing no tasks, expecting no prelink tasks, and with no running worker thread.
/// The `queued_prelink` field may be appended to before calling `start`.
pub const empty: Queue = .{
.mutex = .{},
.flush_safety = .{},
.prelink_wait_count = undefined, // set in `start`
.queued_prelink = .empty,
.wip_prelink = .empty,
.queued_zcu = .empty,
.wip_zcu = .empty,
.wip_zcu_idx = 0,
.state = .finished,
.air_bytes_in_flight = 0,
.air_bytes_waiting = 0,
.air_bytes_cond = .{},
.future = null,
.prelink_mutex = .init,
.prelink_queue = undefined, // set in `start` if needed
.zcu_queue = undefined, // set in `start` if needed
};
/// `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);
pub fn cancel(q: *Queue, io: Io) void {
if (q.future) |*f| {
f.cancel(io);
q.future = null;
}
}
pub fn wait(q: *Queue, io: Io) void {
if (q.future) |*f| {
f.await(io);
q.future = null;
}
}
/// This is expected to be called exactly once, after which the caller must not directly access
/// `queued_prelink` 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);
// Reset this to 1. We can't init it to 1 in `empty`, because it would fall to 0 on successive
// incremental updates, but we still need the initial 1.
q.prelink_wait_count = 1;
if (q.queued_prelink.items.len != 0) {
q.state = .running;
comp.thread_pool.spawnWgId(&comp.link_task_wait_group, flushTaskQueue, .{ q, comp });
pub fn start(
q: *Queue,
comp: *Compilation,
arena: Allocator,
) Allocator.Error!void {
assert(q.future == null);
q.prelink_queue = .init(try arena.alloc(PrelinkTask, buffer_size));
q.zcu_queue = .init(try arena.alloc(ZcuTask, buffer_size));
if (comp.io.concurrent(runLinkTasks, .{ q, comp })) |future| {
// We will run link tasks concurrently.
q.future = future;
} else |err| switch (err) {
error.ConcurrencyUnavailable => {
// We will run link tasks on the main thread.
q.prelink_queue = undefined;
q.zcu_queue = undefined;
},
}
}
/// Every call to this must be paired with a call to `finishPrelinkItem`.
pub fn startPrelinkItem(q: *Queue) void {
q.mutex.lock();
defer q.mutex.unlock();
assert(q.prelink_wait_count > 0); // must not have finished everything already
q.prelink_wait_count += 1;
}
/// This function must be called exactly one more time than `startPrelinkItem` is. The final call
/// indicates that we have finished calling `startPrelinkItem`, so once all pending items finish,
/// we are ready to move on to ZCU tasks.
pub fn finishPrelinkItem(q: *Queue, comp: *Compilation) void {
{
q.mutex.lock();
defer q.mutex.unlock();
q.prelink_wait_count -= 1;
if (q.prelink_wait_count != 0) return;
// The prelink task count dropped to 0; restart the linker thread if necessary.
switch (q.state) {
.wait_for_mir => unreachable, // we've not started zcu tasks yet
.running => return,
.finished => {},
}
assert(q.queued_prelink.items.len == 0);
// Even if there are no ZCU tasks, we must restart the linker thread to make sure
// that `link.File.prelink()` is called.
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, func_index: InternPool.Index, 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, .running => return,
.wait_for_mir => |wait_for| if (wait_for != func_index) return,
}
// We were waiting for `mir`, so we will restart the linker thread.
q.state = .running;
}
assert(mir.status.load(.acquire) != .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
/// `prelink_wait_count` is not yet 0. 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();
assert(q.prelink_wait_count > 0);
try q.queued_prelink.appendSlice(comp.gpa, tasks);
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;
/// the queue is not yet closed. Also asserts that `tasks.len` is not 0.
pub fn enqueuePrelink(q: *Queue, comp: *Compilation, tasks: []const PrelinkTask) Io.Cancelable!void {
const io = comp.io;
if (q.future != null) {
q.prelink_queue.putAll(io, tasks) catch |err| switch (err) {
error.Canceled => |e| return e,
error.Closed => unreachable,
};
} else {
try q.prelink_mutex.lock(io);
defer q.prelink_mutex.unlock(io);
for (tasks) |task| link.doPrelinkTask(comp, task);
}
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();
// 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,
.finished => if (q.prelink_wait_count > 0) return,
}
// Restart the linker thread, unless it would immediately be blocked
if (task == .link_func and task.link_func.mir.status.load(.acquire) == .pending) {
q.state = .{ .wait_for_mir = task.link_func.func };
pub fn enqueueZcu(
q: *Queue,
comp: *Compilation,
tid: usize,
task: ZcuTask,
) Io.Cancelable!void {
const io = comp.io;
assert(tid == 0);
if (q.future != null) {
if (q.zcu_queue.putOne(io, task)) |_| {
return;
} else |err| switch (err) {
error.Canceled => |e| return e,
error.Closed => {
// The linker is still processing prelink tasks. Wait for those
// to finish, after which the linker task will exist, and ZCU
// tasks will be run non-concurrently. This logic exists for
// backends which do not support `Zcu.Feature.separate_thread`.
q.wait(io);
},
}
q.state = .running;
}
comp.thread_pool.spawnWgId(&comp.link_task_wait_group, flushTaskQueue, .{ q, comp });
link.doZcuTask(comp, tid, task);
}
fn flushTaskQueue(tid: usize, q: *Queue, comp: *Compilation) void {
q.flush_safety.lock(); // every `return` site should unlock this before unlocking `q.mutex`
if (std.debug.runtime_safety) {
q.mutex.lock();
defer q.mutex.unlock();
assert(q.state == .running);
pub fn finishPrelinkQueue(q: *Queue, comp: *Compilation) void {
if (q.future != null) {
q.prelink_queue.close(comp.io);
return;
}
// If linking non-concurrently, we must run prelink.
prelink: {
const lf = comp.bin_file orelse break :prelink;
if (lf.post_prelink) break :prelink;
if (lf.prelink()) |_| {
lf.post_prelink = true;
} else |err| switch (err) {
error.OutOfMemory => comp.link_diags.setAllocFailure(),
error.LinkFailure => {},
}
}
}
pub fn finishZcuQueue(q: *Queue, comp: *Compilation) void {
if (q.future != null) {
q.zcu_queue.close(comp.io);
}
}
fn runLinkTasks(q: *Queue, comp: *Compilation) void {
const tid = Compilation.getTid();
const io = comp.io;
var have_idle_tasks = true;
prelink: while (true) {
assert(q.wip_prelink.items.len == 0);
swap_queues: while (true) {
{
q.mutex.lock();
defer q.mutex.unlock();
std.mem.swap(std.ArrayList(PrelinkTask), &q.queued_prelink, &q.wip_prelink);
if (q.wip_prelink.items.len > 0) break :swap_queues;
if (q.prelink_wait_count == 0) break :prelink; // prelink is done
if (!have_idle_tasks) {
// We're expecting more prelink tasks so can't move on to ZCU tasks.
q.state = .finished;
q.flush_safety.unlock();
return;
}
}
have_idle_tasks = link.doIdleTask(comp, tid) catch |err| switch (err) {
error.OutOfMemory => have_idle_tasks: {
comp.link_diags.setAllocFailure();
break :have_idle_tasks false;
},
error.LinkFailure => false,
};
}
for (q.wip_prelink.items) |task| {
prelink_tasks: while (true) {
var task_buf: [128]PrelinkTask = undefined;
const limit: usize = if (have_idle_tasks) 0 else 1;
const n = q.prelink_queue.get(io, &task_buf, limit) catch |err| switch (err) {
error.Canceled => return,
error.Closed => break :prelink_tasks,
};
if (n == 0) {
assert(have_idle_tasks);
have_idle_tasks = runIdleTask(comp, tid);
} else for (task_buf[0..n]) |task| {
link.doPrelinkTask(comp, task);
have_idle_tasks = true;
}
have_idle_tasks = true;
q.wip_prelink.clearRetainingCapacity();
}
// We've finished the prelink tasks, so run prelink if necessary.
@@ -263,79 +180,37 @@ fn flushTaskQueue(tid: usize, q: *Queue, comp: *Compilation) void {
}
}
// Now we can run ZCU tasks.
while (true) {
if (q.wip_zcu.items.len == q.wip_zcu_idx) swap_queues: {
q.wip_zcu.clearRetainingCapacity();
q.wip_zcu_idx = 0;
while (true) {
{
q.mutex.lock();
defer q.mutex.unlock();
std.mem.swap(std.ArrayList(ZcuTask), &q.queued_zcu, &q.wip_zcu);
if (q.wip_zcu.items.len > 0) break :swap_queues;
if (!have_idle_tasks) {
// We've exhausted all available tasks.
q.state = .finished;
q.flush_safety.unlock();
return;
}
}
have_idle_tasks = link.doIdleTask(comp, tid) catch |err| switch (err) {
error.OutOfMemory => have_idle_tasks: {
comp.link_diags.setAllocFailure();
break :have_idle_tasks false;
},
error.LinkFailure => false,
};
}
zcu_tasks: while (true) {
var task_buf: [128]ZcuTask = undefined;
const limit: usize = if (have_idle_tasks) 0 else 1;
const n = q.zcu_queue.get(io, &task_buf, limit) catch |err| switch (err) {
error.Canceled => return,
error.Closed => break :zcu_tasks,
};
if (n == 0) {
assert(have_idle_tasks);
have_idle_tasks = runIdleTask(comp, tid);
} else for (task_buf[0..n]) |task| {
link.doZcuTask(comp, tid, task);
have_idle_tasks = true;
}
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;
while (true) {
// First check without the mutex to optimize for the common case where MIR is ready.
if (status_ptr.load(.acquire) != .pending) break :pending;
if (have_idle_tasks) have_idle_tasks = link.doIdleTask(comp, tid) catch |err| switch (err) {
error.OutOfMemory => have_idle_tasks: {
comp.link_diags.setAllocFailure();
break :have_idle_tasks false;
},
error.LinkFailure => false,
};
if (!have_idle_tasks) break;
}
q.mutex.lock();
defer q.mutex.unlock();
if (status_ptr.load(.acquire) != .pending) break :pending;
// We will stop for now, and get restarted once this MIR is ready.
q.state = .{ .wait_for_mir = task.link_func.func };
q.flush_safety.unlock();
return;
}
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;
have_idle_tasks = true;
}
}
fn runIdleTask(comp: *Compilation, tid: usize) bool {
return link.doIdleTask(comp, tid) catch |err| switch (err) {
error.OutOfMemory => have_more: {
comp.link_diags.setAllocFailure();
break :have_more false;
},
error.LinkFailure => false,
};
}
const std = @import("std");
const assert = std.debug.assert;
const Allocator = std.mem.Allocator;
const Io = std.Io;
const Compilation = @import("../Compilation.zig");
const InternPool = @import("../InternPool.zig");
const link = @import("../link.zig");
+3 -2
View File
@@ -3393,10 +3393,11 @@ pub fn updateExports(
pub fn loadInput(wasm: *Wasm, input: link.Input) !void {
const comp = wasm.base.comp;
const gpa = comp.gpa;
const io = comp.io;
if (comp.verbose_link) {
comp.mutex.lock(); // protect comp.arena
defer comp.mutex.unlock();
comp.mutex.lockUncancelable(io); // protect comp.arena
defer comp.mutex.unlock(io);
const argv = &wasm.dump_argv_list;
switch (input) {
+36 -34
View File
@@ -11,7 +11,6 @@ const Allocator = mem.Allocator;
const Ast = std.zig.Ast;
const Color = std.zig.Color;
const warn = std.log.warn;
const ThreadPool = std.Thread.Pool;
const cleanExit = std.process.cleanExit;
const Cache = std.Build.Cache;
const Path = std.Build.Cache.Path;
@@ -200,6 +199,8 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
const tr = tracy.trace(@src());
defer tr.end();
Compilation.setMainThread();
if (args.len <= 1) {
std.log.info("{s}", .{usage});
fatal("expected command argument", .{});
@@ -239,6 +240,8 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
var threaded: Io.Threaded = .init(gpa);
defer threaded.deinit();
threaded_impl_ptr = &threaded;
threaded.stack_size = thread_stack_size;
const io = threaded.io();
const cmd = args[1];
@@ -3361,14 +3364,11 @@ fn buildOutputType(
},
};
var thread_pool: ThreadPool = undefined;
try thread_pool.init(.{
.allocator = gpa,
.n_jobs = @min(@max(n_jobs orelse std.Thread.getCpuCount() catch 1, 1), std.math.maxInt(Zcu.PerThread.IdBacking)),
.track_ids = true,
.stack_size = thread_stack_size,
});
defer thread_pool.deinit();
const thread_limit = @min(
@max(n_jobs orelse std.Thread.getCpuCount() catch 1, 1),
std.math.maxInt(Zcu.PerThread.IdBacking),
);
setThreadLimit(thread_limit);
for (create_module.c_source_files.items) |*src| {
dev.check(.c_compiler);
@@ -3461,7 +3461,7 @@ fn buildOutputType(
var create_diag: Compilation.CreateDiagnostic = undefined;
const comp = Compilation.create(gpa, arena, io, &create_diag, .{
.dirs = dirs,
.thread_pool = &thread_pool,
.thread_limit = thread_limit,
.self_exe_path = switch (native_os) {
.wasi => null,
else => self_exe_path,
@@ -4150,6 +4150,7 @@ fn serve(
runtime_args_start: ?usize,
) !void {
const gpa = comp.gpa;
const io = comp.io;
var server = try Server.init(.{
.in = in,
@@ -4178,8 +4179,8 @@ fn serve(
const hdr = try server.receiveMessage();
// Lock the debug server while handling the message.
if (comp.debugIncremental()) ids.mutex.lock();
defer if (comp.debugIncremental()) ids.mutex.unlock();
if (comp.debugIncremental()) try ids.mutex.lock(io);
defer if (comp.debugIncremental()) ids.mutex.unlock(io);
switch (hdr.tag) {
.exit => return cleanExit(),
@@ -5140,14 +5141,11 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8)
child_argv.items[argv_index_global_cache_dir] = dirs.global_cache.path orelse cwd_path;
child_argv.items[argv_index_cache_dir] = dirs.local_cache.path orelse cwd_path;
var thread_pool: ThreadPool = undefined;
try thread_pool.init(.{
.allocator = gpa,
.n_jobs = @min(@max(n_jobs orelse std.Thread.getCpuCount() catch 1, 1), std.math.maxInt(Zcu.PerThread.IdBacking)),
.track_ids = true,
.stack_size = thread_stack_size,
});
defer thread_pool.deinit();
const thread_limit = @min(
@max(n_jobs orelse std.Thread.getCpuCount() catch 1, 1),
std.math.maxInt(Zcu.PerThread.IdBacking),
);
setThreadLimit(thread_limit);
// Dummy http client that is not actually used when fetch_command is unsupported.
// Prevents bootstrap from depending on a bunch of unnecessary stuff.
@@ -5376,7 +5374,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8)
.main_mod = build_mod,
.emit_bin = .yes_cache,
.self_exe_path = self_exe_path,
.thread_pool = &thread_pool,
.thread_limit = thread_limit,
.verbose_cc = verbose_cc,
.verbose_link = verbose_link,
.verbose_air = verbose_air,
@@ -5548,14 +5546,11 @@ fn jitCmd(
);
defer dirs.deinit();
var thread_pool: ThreadPool = undefined;
try thread_pool.init(.{
.allocator = gpa,
.n_jobs = @min(@max(std.Thread.getCpuCount() catch 1, 1), std.math.maxInt(Zcu.PerThread.IdBacking)),
.track_ids = true,
.stack_size = thread_stack_size,
});
defer thread_pool.deinit();
const thread_limit = @min(
@max(std.Thread.getCpuCount() catch 1, 1),
std.math.maxInt(Zcu.PerThread.IdBacking),
);
setThreadLimit(thread_limit);
var child_argv: std.ArrayList([]const u8) = .empty;
try child_argv.ensureUnusedCapacity(arena, args.len + 4);
@@ -5619,7 +5614,7 @@ fn jitCmd(
.main_mod = root_mod,
.emit_bin = .yes_cache,
.self_exe_path = self_exe_path,
.thread_pool = &thread_pool,
.thread_limit = thread_limit,
.cache_mode = .whole,
}) catch |err| switch (err) {
error.CreateFail => fatal("failed to create compilation: {f}", .{create_diag}),
@@ -6946,10 +6941,6 @@ fn cmdFetch(
const path_or_url = opt_path_or_url orelse fatal("missing url or path parameter", .{});
var thread_pool: ThreadPool = undefined;
try thread_pool.init(.{ .allocator = gpa });
defer thread_pool.deinit();
var http_client: std.http.Client = .{ .allocator = gpa, .io = io };
defer http_client.deinit();
@@ -7601,3 +7592,14 @@ fn addLibDirectoryWarn2(
.path = path,
});
}
var threaded_impl_ptr: *Io.Threaded = undefined;
fn setThreadLimit(n: usize) void {
// We want a maximum of n total threads to keep the InternPool happy, but
// the main thread doesn't count towards the limits, so use n-1. Also, the
// linker can run concurrently, so we need to set both the async *and* the
// concurrency limit.
const limit: Io.Limit = .limited(n - 1);
threaded_impl_ptr.setAsyncLimit(limit);
threaded_impl_ptr.concurrent_limit = limit;
}
+4 -1
View File
@@ -55,6 +55,9 @@ pub const MutableValue = union(enum) {
};
pub fn intern(mv: MutableValue, pt: Zcu.PerThread, arena: Allocator) Allocator.Error!Value {
const zcu = pt.zcu;
const comp = zcu.comp;
const io = comp.io;
return Value.fromInterned(switch (mv) {
.interned => |ip_index| ip_index,
.eu_payload => |sv| try pt.intern(.{ .error_union = .{
@@ -68,7 +71,7 @@ pub const MutableValue = union(enum) {
.repeated => |sv| return pt.aggregateSplatValue(.fromInterned(sv.ty), try sv.child.intern(pt, arena)),
.bytes => |b| try pt.intern(.{ .aggregate = .{
.ty = b.ty,
.storage = .{ .bytes = try pt.zcu.intern_pool.getOrPutString(pt.zcu.gpa, pt.tid, b.data, .maybe_embedded_nulls) },
.storage = .{ .bytes = try zcu.intern_pool.getOrPutString(comp.gpa, io, pt.tid, b.data, .maybe_embedded_nulls) },
} }),
.aggregate => |a| {
const elems = try arena.alloc(InternPool.Index, a.elems.len);