Skip to content

Commit 65ced4a

Browse files
committed
Compilation: put supported codegen backends on a separate thread
(There are no supported backends.)
1 parent c36e2bb commit 65ced4a

File tree

7 files changed

+173
-65
lines changed

7 files changed

+173
-65
lines changed

lib/std/Progress.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,7 @@ pub const Node = struct {
282282
}
283283

284284
fn init(free_index: Index, parent: Parent, name: []const u8, estimated_total_items: usize) Node {
285-
assert(parent != .unused);
285+
assert(parent == .none or @intFromEnum(parent) < node_storage_buffer_len);
286286

287287
const storage = storageByIndex(free_index);
288288
storage.* = .{

lib/std/Thread/Pool.zig

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,11 @@ const Runnable = struct {
2121
runFn: RunProto,
2222
};
2323

24-
const RunProto = *const fn (*Runnable, id: ?u32) void;
24+
const RunProto = *const fn (*Runnable, id: ?usize) void;
2525

2626
pub const Options = struct {
2727
allocator: std.mem.Allocator,
28-
n_jobs: ?u32 = null,
28+
n_jobs: ?usize = null,
2929
track_ids: bool = false,
3030
};
3131

@@ -109,7 +109,7 @@ pub fn spawnWg(pool: *Pool, wait_group: *WaitGroup, comptime func: anytype, args
109109
run_node: RunQueue.Node = .{ .data = .{ .runFn = runFn } },
110110
wait_group: *WaitGroup,
111111

112-
fn runFn(runnable: *Runnable, _: ?u32) void {
112+
fn runFn(runnable: *Runnable, _: ?usize) void {
113113
const run_node: *RunQueue.Node = @fieldParentPtr("data", runnable);
114114
const closure: *@This() = @alignCast(@fieldParentPtr("run_node", run_node));
115115
@call(.auto, func, closure.arguments);
@@ -150,7 +150,7 @@ pub fn spawnWg(pool: *Pool, wait_group: *WaitGroup, comptime func: anytype, args
150150
/// Runs `func` in the thread pool, calling `WaitGroup.start` beforehand, and
151151
/// `WaitGroup.finish` after it returns.
152152
///
153-
/// The first argument passed to `func` is a dense `u32` thread id, the rest
153+
/// The first argument passed to `func` is a dense `usize` thread id, the rest
154154
/// of the arguments are passed from `args`. Requires the pool to have been
155155
/// initialized with `.track_ids = true`.
156156
///
@@ -172,7 +172,7 @@ pub fn spawnWgId(pool: *Pool, wait_group: *WaitGroup, comptime func: anytype, ar
172172
run_node: RunQueue.Node = .{ .data = .{ .runFn = runFn } },
173173
wait_group: *WaitGroup,
174174

175-
fn runFn(runnable: *Runnable, id: ?u32) void {
175+
fn runFn(runnable: *Runnable, id: ?usize) void {
176176
const run_node: *RunQueue.Node = @fieldParentPtr("data", runnable);
177177
const closure: *@This() = @alignCast(@fieldParentPtr("run_node", run_node));
178178
@call(.auto, func, .{id.?} ++ closure.arguments);
@@ -191,7 +191,7 @@ pub fn spawnWgId(pool: *Pool, wait_group: *WaitGroup, comptime func: anytype, ar
191191
pool.mutex.lock();
192192

193193
const closure = pool.allocator.create(Closure) catch {
194-
const id = pool.ids.getIndex(std.Thread.getCurrentId());
194+
const id: ?usize = pool.ids.getIndex(std.Thread.getCurrentId());
195195
pool.mutex.unlock();
196196
@call(.auto, func, .{id.?} ++ args);
197197
wait_group.finish();
@@ -258,7 +258,7 @@ fn worker(pool: *Pool) void {
258258
pool.mutex.lock();
259259
defer pool.mutex.unlock();
260260

261-
const id: ?u32 = if (pool.ids.count() > 0) @intCast(pool.ids.count()) else null;
261+
const id: ?usize = if (pool.ids.count() > 0) @intCast(pool.ids.count()) else null;
262262
if (id) |_| pool.ids.putAssumeCapacityNoClobber(std.Thread.getCurrentId(), {});
263263

264264
while (true) {
@@ -280,15 +280,12 @@ fn worker(pool: *Pool) void {
280280
}
281281

282282
pub fn waitAndWork(pool: *Pool, wait_group: *WaitGroup) void {
283-
var id: ?u32 = null;
283+
var id: ?usize = null;
284284

285285
while (!wait_group.isDone()) {
286286
pool.mutex.lock();
287287
if (pool.run_queue.popFirst()) |run_node| {
288-
id = id orelse if (pool.ids.getIndex(std.Thread.getCurrentId())) |index|
289-
@intCast(index)
290-
else
291-
null;
288+
id = id orelse pool.ids.getIndex(std.Thread.getCurrentId());
292289
pool.mutex.unlock();
293290
run_node.data.runFn(&run_node.data, id);
294291
continue;
@@ -300,6 +297,6 @@ pub fn waitAndWork(pool: *Pool, wait_group: *WaitGroup) void {
300297
}
301298
}
302299

303-
pub fn getIdCount(pool: *Pool) u32 {
300+
pub fn getIdCount(pool: *Pool) usize {
304301
return @intCast(1 + pool.threads.len);
305302
}

src/Compilation.zig

Lines changed: 115 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,14 @@ lld_errors: std.ArrayListUnmanaged(LldError) = .{},
103103

104104
work_queue: std.fifo.LinearFifo(Job, .Dynamic),
105105

106+
codegen_work: if (InternPool.single_threaded) void else struct {
107+
mutex: std.Thread.Mutex,
108+
cond: std.Thread.Condition,
109+
queue: std.fifo.LinearFifo(CodegenJob, .Dynamic),
110+
job_error: ?JobError,
111+
done: bool,
112+
},
113+
106114
/// These jobs are to invoke the Clang compiler to create an object file, which
107115
/// gets linked with the Compilation.
108116
c_object_work_queue: std.fifo.LinearFifo(*CObject, .Dynamic),
@@ -362,6 +370,16 @@ const Job = union(enum) {
362370
windows_import_lib: usize,
363371
};
364372

373+
const CodegenJob = union(enum) {
374+
decl: InternPool.DeclIndex,
375+
func: struct {
376+
func: InternPool.Index,
377+
/// This `Air` is owned by the `Job` and allocated with `gpa`.
378+
/// It must be deinited when the job is processed.
379+
air: Air,
380+
},
381+
};
382+
365383
pub const CObject = struct {
366384
/// Relative to cwd. Owned by arena.
367385
src: CSourceFile,
@@ -1429,6 +1447,13 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
14291447
.emit_llvm_ir = options.emit_llvm_ir,
14301448
.emit_llvm_bc = options.emit_llvm_bc,
14311449
.work_queue = std.fifo.LinearFifo(Job, .Dynamic).init(gpa),
1450+
.codegen_work = if (InternPool.single_threaded) {} else .{
1451+
.mutex = .{},
1452+
.cond = .{},
1453+
.queue = std.fifo.LinearFifo(CodegenJob, .Dynamic).init(gpa),
1454+
.job_error = null,
1455+
.done = false,
1456+
},
14321457
.c_object_work_queue = std.fifo.LinearFifo(*CObject, .Dynamic).init(gpa),
14331458
.win32_resource_work_queue = if (build_options.only_core_functionality) {} else std.fifo.LinearFifo(*Win32Resource, .Dynamic).init(gpa),
14341459
.astgen_work_queue = std.fifo.LinearFifo(Zcu.File.Index, .Dynamic).init(gpa),
@@ -3310,7 +3335,21 @@ pub fn addZirErrorMessages(eb: *ErrorBundle.Wip, file: *Zcu.File) !void {
33103335
pub fn performAllTheWork(
33113336
comp: *Compilation,
33123337
main_progress_node: std.Progress.Node,
3313-
) error{ TimerUnsupported, OutOfMemory }!void {
3338+
) JobError!void {
3339+
defer if (comp.module) |mod| {
3340+
mod.sema_prog_node.end();
3341+
mod.sema_prog_node = std.Progress.Node.none;
3342+
mod.codegen_prog_node.end();
3343+
mod.codegen_prog_node = std.Progress.Node.none;
3344+
};
3345+
try comp.performAllTheWorkInner(main_progress_node);
3346+
if (!InternPool.single_threaded) if (comp.codegen_work.job_error) |job_error| return job_error;
3347+
}
3348+
3349+
fn performAllTheWorkInner(
3350+
comp: *Compilation,
3351+
main_progress_node: std.Progress.Node,
3352+
) JobError!void {
33143353
// Here we queue up all the AstGen tasks first, followed by C object compilation.
33153354
// We wait until the AstGen tasks are all completed before proceeding to the
33163355
// (at least for now) single-threaded main work queue. However, C object compilation
@@ -3410,16 +3449,20 @@ pub fn performAllTheWork(
34103449
mod.sema_prog_node = main_progress_node.start("Semantic Analysis", 0);
34113450
mod.codegen_prog_node = main_progress_node.start("Code Generation", 0);
34123451
}
3413-
defer if (comp.module) |mod| {
3414-
mod.sema_prog_node.end();
3415-
mod.sema_prog_node = undefined;
3416-
mod.codegen_prog_node.end();
3417-
mod.codegen_prog_node = undefined;
3452+
3453+
if (!InternPool.single_threaded) comp.thread_pool.spawnWgId(&comp.work_queue_wait_group, codegenThread, .{comp});
3454+
defer if (!InternPool.single_threaded) {
3455+
{
3456+
comp.codegen_work.mutex.lock();
3457+
defer comp.codegen_work.mutex.unlock();
3458+
comp.codegen_work.done = true;
3459+
}
3460+
comp.codegen_work.cond.signal();
34183461
};
34193462

34203463
while (true) {
34213464
if (comp.work_queue.readItem()) |work_item| {
3422-
try processOneJob(0, comp, work_item, main_progress_node);
3465+
try processOneJob(@intFromEnum(Zcu.PerThread.Id.main), comp, work_item, main_progress_node);
34233466
continue;
34243467
}
34253468
if (comp.module) |zcu| {
@@ -3447,11 +3490,12 @@ pub fn performAllTheWork(
34473490
}
34483491
}
34493492

3450-
fn processOneJob(tid: usize, comp: *Compilation, job: Job, prog_node: std.Progress.Node) !void {
3493+
const JobError = Allocator.Error;
3494+
3495+
fn processOneJob(tid: usize, comp: *Compilation, job: Job, prog_node: std.Progress.Node) JobError!void {
34513496
switch (job) {
34523497
.codegen_decl => |decl_index| {
3453-
const pt: Zcu.PerThread = .{ .zcu = comp.module.?, .tid = @enumFromInt(tid) };
3454-
const decl = pt.zcu.declPtr(decl_index);
3498+
const decl = comp.module.?.declPtr(decl_index);
34553499

34563500
switch (decl.analysis) {
34573501
.unreferenced => unreachable,
@@ -3461,26 +3505,20 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job, prog_node: std.Progre
34613505
.sema_failure,
34623506
.codegen_failure,
34633507
.dependency_failure,
3464-
=> return,
3508+
=> {},
34653509

34663510
.complete => {
3467-
const named_frame = tracy.namedFrame("codegen_decl");
3468-
defer named_frame.end();
3469-
34703511
assert(decl.has_tv);
3471-
3472-
try pt.linkerUpdateDecl(decl_index);
3473-
return;
3512+
try comp.queueCodegenJob(tid, .{ .decl = decl_index });
34743513
},
34753514
}
34763515
},
34773516
.codegen_func => |func| {
3478-
const named_frame = tracy.namedFrame("codegen_func");
3479-
defer named_frame.end();
3480-
3481-
const pt: Zcu.PerThread = .{ .zcu = comp.module.?, .tid = @enumFromInt(tid) };
34823517
// This call takes ownership of `func.air`.
3483-
try pt.linkerUpdateFunc(func.func, func.air);
3518+
try comp.queueCodegenJob(tid, .{ .func = .{
3519+
.func = func.func,
3520+
.air = func.air,
3521+
} });
34843522
},
34853523
.analyze_func => |func| {
34863524
const named_frame = tracy.namedFrame("analyze_func");
@@ -3772,6 +3810,61 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job, prog_node: std.Progre
37723810
}
37733811
}
37743812

3813+
fn queueCodegenJob(comp: *Compilation, tid: usize, codegen_job: CodegenJob) !void {
3814+
if (InternPool.single_threaded or
3815+
!comp.module.?.backendSupportsFeature(.separate_thread))
3816+
return processOneCodegenJob(tid, comp, codegen_job);
3817+
3818+
{
3819+
comp.codegen_work.mutex.lock();
3820+
defer comp.codegen_work.mutex.unlock();
3821+
try comp.codegen_work.queue.writeItem(codegen_job);
3822+
}
3823+
comp.codegen_work.cond.signal();
3824+
}
3825+
3826+
fn codegenThread(tid: usize, comp: *Compilation) void {
3827+
comp.codegen_work.mutex.lock();
3828+
defer comp.codegen_work.mutex.unlock();
3829+
3830+
while (true) {
3831+
if (comp.codegen_work.queue.readItem()) |codegen_job| {
3832+
comp.codegen_work.mutex.unlock();
3833+
defer comp.codegen_work.mutex.lock();
3834+
3835+
processOneCodegenJob(tid, comp, codegen_job) catch |job_error| {
3836+
comp.codegen_work.job_error = job_error;
3837+
break;
3838+
};
3839+
continue;
3840+
}
3841+
3842+
if (comp.codegen_work.done) break;
3843+
3844+
comp.codegen_work.cond.wait(&comp.codegen_work.mutex);
3845+
}
3846+
}
3847+
3848+
fn processOneCodegenJob(tid: usize, comp: *Compilation, codegen_job: CodegenJob) JobError!void {
3849+
switch (codegen_job) {
3850+
.decl => |decl_index| {
3851+
const named_frame = tracy.namedFrame("codegen_decl");
3852+
defer named_frame.end();
3853+
3854+
const pt: Zcu.PerThread = .{ .zcu = comp.module.?, .tid = @enumFromInt(tid) };
3855+
try pt.linkerUpdateDecl(decl_index);
3856+
},
3857+
.func => |func| {
3858+
const named_frame = tracy.namedFrame("codegen_func");
3859+
defer named_frame.end();
3860+
3861+
const pt: Zcu.PerThread = .{ .zcu = comp.module.?, .tid = @enumFromInt(tid) };
3862+
// This call takes ownership of `func.air`.
3863+
try pt.linkerUpdateFunc(func.func, func.air);
3864+
},
3865+
}
3866+
}
3867+
37753868
fn workerDocsCopy(comp: *Compilation) void {
37763869
docsCopyFallible(comp) catch |err| {
37773870
return comp.lockAndSetMiscFailure(

src/Compilation/Config.zig

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -440,12 +440,8 @@ pub fn resolve(options: Options) ResolveError!Config {
440440
};
441441
};
442442

443-
const backend_supports_error_tracing = target_util.backendSupportsFeature(
444-
target.cpu.arch,
445-
target.ofmt,
446-
use_llvm,
447-
.error_return_trace,
448-
);
443+
const backend = target_util.zigBackend(target, use_llvm);
444+
const backend_supports_error_tracing = target_util.backendSupportsFeature(backend, .error_return_trace);
449445

450446
const root_error_tracing = b: {
451447
if (options.root_error_tracing) |x| break :b x;

src/Zcu.zig

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,8 @@ root_mod: *Package.Module,
6464
/// `root_mod` is the test runner, and `main_mod` is the user's source file which has the tests.
6565
main_mod: *Package.Module,
6666
std_mod: *Package.Module,
67-
sema_prog_node: std.Progress.Node = undefined,
68-
codegen_prog_node: std.Progress.Node = undefined,
67+
sema_prog_node: std.Progress.Node = std.Progress.Node.none,
68+
codegen_prog_node: std.Progress.Node = std.Progress.Node.none,
6969

7070
/// Used by AstGen worker to load and store ZIR cache.
7171
global_zir_cache: Compilation.Directory,
@@ -3557,13 +3557,13 @@ pub const Feature = enum {
35573557
/// to generate better machine code in the backends. All backends should migrate to
35583558
/// enabling this feature.
35593559
safety_checked_instructions,
3560+
/// If the backend supports running from another thread.
3561+
separate_thread,
35603562
};
35613563

3562-
pub fn backendSupportsFeature(zcu: Module, feature: Feature) bool {
3563-
const cpu_arch = zcu.root_mod.resolved_target.result.cpu.arch;
3564-
const ofmt = zcu.root_mod.resolved_target.result.ofmt;
3565-
const use_llvm = zcu.comp.config.use_llvm;
3566-
return target_util.backendSupportsFeature(cpu_arch, ofmt, use_llvm, feature);
3564+
pub fn backendSupportsFeature(zcu: Module, comptime feature: Feature) bool {
3565+
const backend = target_util.zigBackend(zcu.root_mod.resolved_target.result, zcu.comp.config.use_llvm);
3566+
return target_util.backendSupportsFeature(backend, feature);
35673567
}
35683568

35693569
pub const AtomicPtrAlignmentError = error{

src/Zcu/PerThread.zig

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2129,7 +2129,7 @@ pub fn populateTestFunctions(
21292129
zcu.sema_prog_node = main_progress_node.start("Semantic Analysis", 0);
21302130
defer {
21312131
zcu.sema_prog_node.end();
2132-
zcu.sema_prog_node = undefined;
2132+
zcu.sema_prog_node = std.Progress.Node.none;
21332133
}
21342134
try pt.ensureDeclAnalyzed(decl_index);
21352135
}
@@ -2238,7 +2238,7 @@ pub fn populateTestFunctions(
22382238
zcu.codegen_prog_node = main_progress_node.start("Code Generation", 0);
22392239
defer {
22402240
zcu.codegen_prog_node.end();
2241-
zcu.codegen_prog_node = undefined;
2241+
zcu.codegen_prog_node = std.Progress.Node.none;
22422242
}
22432243

22442244
try pt.linkerUpdateDecl(decl_index);

0 commit comments

Comments
 (0)