@@ -101,7 +101,15 @@ link_error_flags: link.File.ErrorFlags = .{},
101
101
link_errors : std .ArrayListUnmanaged (link .File .ErrorMsg ) = .{},
102
102
lld_errors : std .ArrayListUnmanaged (LldError ) = .{},
103
103
104
- work_queue : std .fifo .LinearFifo (Job , .Dynamic ),
104
+ work_queues : [
105
+ len : {
106
+ var len : usize = 0 ;
107
+ for (std .enums .values (Job .Tag )) | tag | {
108
+ len = @max (Job .stage (tag ) + 1 , len );
109
+ }
110
+ break :len len ;
111
+ }
112
+ ]std .fifo .LinearFifo (Job , .Dynamic ),
105
113
106
114
codegen_work : if (InternPool .single_threaded ) void else struct {
107
115
mutex : std.Thread.Mutex ,
@@ -370,6 +378,20 @@ const Job = union(enum) {
370
378
371
379
/// The value is the index into `system_libs`.
372
380
windows_import_lib : usize ,
381
+
382
+ const Tag = @typeInfo (Job ).Union .tag_type .? ;
383
+ fn stage (tag : Tag ) usize {
384
+ return switch (tag ) {
385
+ // Prioritize functions so that codegen can get to work on them on a
386
+ // separate thread, while Sema goes back to its own work.
387
+ .resolve_type_fully , .analyze_func , .codegen_func = > 0 ,
388
+ else = > 1 ,
389
+ };
390
+ }
391
+ comptime {
392
+ // Job dependencies
393
+ assert (stage (.resolve_type_fully ) <= stage (.codegen_func ));
394
+ }
373
395
};
374
396
375
397
const CodegenJob = union (enum ) {
@@ -1452,7 +1474,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
1452
1474
.emit_asm = options .emit_asm ,
1453
1475
.emit_llvm_ir = options .emit_llvm_ir ,
1454
1476
.emit_llvm_bc = options .emit_llvm_bc ,
1455
- .work_queue = std .fifo .LinearFifo (Job , .Dynamic ).init (gpa ),
1477
+ .work_queues = .{ std .fifo .LinearFifo (Job , .Dynamic ).init (gpa )} ** @typeInfo ( std . meta . FieldType ( Compilation , .work_queues )). Array . len ,
1456
1478
.codegen_work = if (InternPool .single_threaded ) {} else .{
1457
1479
.mutex = .{},
1458
1480
.cond = .{},
@@ -1760,12 +1782,12 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
1760
1782
if (! std .zig .target .canBuildLibC (target )) return error .LibCUnavailable ;
1761
1783
1762
1784
if (glibc .needsCrtiCrtn (target )) {
1763
- try comp .work_queue . write (&[_ ]Job {
1785
+ try comp .queueJobs (&[_ ]Job {
1764
1786
.{ .glibc_crt_file = .crti_o },
1765
1787
.{ .glibc_crt_file = .crtn_o },
1766
1788
});
1767
1789
}
1768
- try comp .work_queue . write (&[_ ]Job {
1790
+ try comp .queueJobs (&[_ ]Job {
1769
1791
.{ .glibc_crt_file = .scrt1_o },
1770
1792
.{ .glibc_crt_file = .libc_nonshared_a },
1771
1793
.{ .glibc_shared_objects = {} },
@@ -1774,14 +1796,13 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
1774
1796
if (comp .wantBuildMuslFromSource ()) {
1775
1797
if (! std .zig .target .canBuildLibC (target )) return error .LibCUnavailable ;
1776
1798
1777
- try comp .work_queue .ensureUnusedCapacity (6 );
1778
1799
if (musl .needsCrtiCrtn (target )) {
1779
- comp .work_queue . writeAssumeCapacity (&[_ ]Job {
1800
+ try comp .queueJobs (&[_ ]Job {
1780
1801
.{ .musl_crt_file = .crti_o },
1781
1802
.{ .musl_crt_file = .crtn_o },
1782
1803
});
1783
1804
}
1784
- comp .work_queue . writeAssumeCapacity (&[_ ]Job {
1805
+ try comp .queueJobs (&[_ ]Job {
1785
1806
.{ .musl_crt_file = .crt1_o },
1786
1807
.{ .musl_crt_file = .scrt1_o },
1787
1808
.{ .musl_crt_file = .rcrt1_o },
@@ -1795,15 +1816,12 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
1795
1816
if (comp .wantBuildWasiLibcFromSource ()) {
1796
1817
if (! std .zig .target .canBuildLibC (target )) return error .LibCUnavailable ;
1797
1818
1798
- // worst-case we need all components
1799
- try comp .work_queue .ensureUnusedCapacity (comp .wasi_emulated_libs .len + 2 );
1800
-
1801
1819
for (comp .wasi_emulated_libs ) | crt_file | {
1802
- comp .work_queue . writeItemAssumeCapacity (.{
1820
+ try comp .queueJob (.{
1803
1821
.wasi_libc_crt_file = crt_file ,
1804
1822
});
1805
1823
}
1806
- comp .work_queue . writeAssumeCapacity (&[_ ]Job {
1824
+ try comp .queueJobs (&[_ ]Job {
1807
1825
.{ .wasi_libc_crt_file = wasi_libc .execModelCrtFile (comp .config .wasi_exec_model ) },
1808
1826
.{ .wasi_libc_crt_file = .libc_a },
1809
1827
});
@@ -1813,9 +1831,10 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
1813
1831
if (! std .zig .target .canBuildLibC (target )) return error .LibCUnavailable ;
1814
1832
1815
1833
const crt_job : Job = .{ .mingw_crt_file = if (is_dyn_lib ) .dllcrt2_o else .crt2_o };
1816
- try comp .work_queue .ensureUnusedCapacity (2 );
1817
- comp .work_queue .writeItemAssumeCapacity (.{ .mingw_crt_file = .mingw32_lib });
1818
- comp .work_queue .writeItemAssumeCapacity (crt_job );
1834
+ try comp .queueJobs (&.{
1835
+ .{ .mingw_crt_file = .mingw32_lib },
1836
+ crt_job ,
1837
+ });
1819
1838
1820
1839
// When linking mingw-w64 there are some import libs we always need.
1821
1840
for (mingw .always_link_libs ) | name | {
@@ -1829,20 +1848,19 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
1829
1848
// Generate Windows import libs.
1830
1849
if (target .os .tag == .windows ) {
1831
1850
const count = comp .system_libs .count ();
1832
- try comp .work_queue .ensureUnusedCapacity (count );
1833
1851
for (0.. count ) | i | {
1834
- comp .work_queue . writeItemAssumeCapacity (.{ .windows_import_lib = i });
1852
+ try comp .queueJob (.{ .windows_import_lib = i });
1835
1853
}
1836
1854
}
1837
1855
if (comp .wantBuildLibUnwindFromSource ()) {
1838
- try comp .work_queue . writeItem (.{ .libunwind = {} });
1856
+ try comp .queueJob (.{ .libunwind = {} });
1839
1857
}
1840
1858
if (build_options .have_llvm and is_exe_or_dyn_lib and comp .config .link_libcpp ) {
1841
- try comp .work_queue . writeItem (.libcxx );
1842
- try comp .work_queue . writeItem (.libcxxabi );
1859
+ try comp .queueJob (.libcxx );
1860
+ try comp .queueJob (.libcxxabi );
1843
1861
}
1844
1862
if (build_options .have_llvm and comp .config .any_sanitize_thread ) {
1845
- try comp .work_queue . writeItem (.libtsan );
1863
+ try comp .queueJob (.libtsan );
1846
1864
}
1847
1865
1848
1866
if (target .isMinGW () and comp .config .any_non_single_threaded ) {
@@ -1872,7 +1890,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
1872
1890
if (! comp .skip_linker_dependencies and is_exe_or_dyn_lib and
1873
1891
! comp .config .link_libc and capable_of_building_zig_libc )
1874
1892
{
1875
- try comp .work_queue . writeItem (.{ .zig_libc = {} });
1893
+ try comp .queueJob (.{ .zig_libc = {} });
1876
1894
}
1877
1895
}
1878
1896
@@ -1883,7 +1901,7 @@ pub fn destroy(comp: *Compilation) void {
1883
1901
if (comp .bin_file ) | lf | lf .destroy ();
1884
1902
if (comp .module ) | zcu | zcu .deinit ();
1885
1903
comp .cache_use .deinit ();
1886
- comp .work_queue .deinit ();
1904
+ for ( comp .work_queues ) | work_queue | work_queue .deinit ();
1887
1905
if (! InternPool .single_threaded ) comp .codegen_work .queue .deinit ();
1888
1906
comp .c_object_work_queue .deinit ();
1889
1907
if (! build_options .only_core_functionality ) {
@@ -2199,13 +2217,13 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void {
2199
2217
}
2200
2218
}
2201
2219
2202
- try comp .work_queue . writeItem (.{ .analyze_mod = std_mod });
2220
+ try comp .queueJob (.{ .analyze_mod = std_mod });
2203
2221
if (comp .config .is_test ) {
2204
- try comp .work_queue . writeItem (.{ .analyze_mod = zcu .main_mod });
2222
+ try comp .queueJob (.{ .analyze_mod = zcu .main_mod });
2205
2223
}
2206
2224
2207
2225
if (zcu .root_mod .deps .get ("compiler_rt" )) | compiler_rt_mod | {
2208
- try comp .work_queue . writeItem (.{ .analyze_mod = compiler_rt_mod });
2226
+ try comp .queueJob (.{ .analyze_mod = compiler_rt_mod });
2209
2227
}
2210
2228
}
2211
2229
@@ -3095,6 +3113,39 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {
3095
3113
for (zcu .failed_embed_files .values ()) | error_msg | {
3096
3114
try addModuleErrorMsg (zcu , & bundle , error_msg .* , & all_references );
3097
3115
}
3116
+ {
3117
+ const SortOrder = struct {
3118
+ zcu : * Zcu ,
3119
+ err : * ? Error ,
3120
+
3121
+ const Error = @typeInfo (
3122
+ @typeInfo (@TypeOf (Zcu .SrcLoc .span )).Fn .return_type .? ,
3123
+ ).ErrorUnion .error_set ;
3124
+
3125
+ pub fn lessThan (ctx : @This (), lhs_index : usize , rhs_index : usize ) bool {
3126
+ if (ctx .err .* ) | _ | return lhs_index < rhs_index ;
3127
+ const errors = ctx .zcu .failed_analysis .values ();
3128
+ const lhs_src_loc = errors [lhs_index ].src_loc .upgrade (ctx .zcu );
3129
+ const rhs_src_loc = errors [rhs_index ].src_loc .upgrade (ctx .zcu );
3130
+ return if (lhs_src_loc .file_scope != rhs_src_loc .file_scope ) std .mem .order (
3131
+ u8 ,
3132
+ lhs_src_loc .file_scope .sub_file_path ,
3133
+ rhs_src_loc .file_scope .sub_file_path ,
3134
+ ).compare (.lt ) else (lhs_src_loc .span (ctx .zcu .gpa ) catch | e | {
3135
+ ctx .err .* = e ;
3136
+ return lhs_index < rhs_index ;
3137
+ }).main < (rhs_src_loc .span (ctx .zcu .gpa ) catch | e | {
3138
+ ctx .err .* = e ;
3139
+ return lhs_index < rhs_index ;
3140
+ }).main ;
3141
+ }
3142
+ };
3143
+ var err : ? SortOrder.Error = null ;
3144
+ // This leaves `zcu.failed_analysis` an invalid state, but we do not
3145
+ // need lookups anymore anyway.
3146
+ zcu .failed_analysis .entries .sort (SortOrder { .zcu = zcu , .err = & err });
3147
+ if (err ) | e | return e ;
3148
+ }
3098
3149
for (zcu .failed_analysis .keys (), zcu .failed_analysis .values ()) | anal_unit , error_msg | {
3099
3150
const decl_index = switch (anal_unit .unwrap ()) {
3100
3151
.decl = > | d | d ,
@@ -3543,18 +3594,18 @@ fn performAllTheWorkInner(
3543
3594
comp .codegen_work .cond .signal ();
3544
3595
};
3545
3596
3546
- while (true ) {
3547
- if ( comp .work_queue .readItem ()) | work_item | {
3548
- try processOneJob (@intFromEnum (Zcu .PerThread .Id .main ), comp , work_item , main_progress_node );
3549
- continue ;
3550
- }
3597
+ work : while (true ) {
3598
+ for ( & comp .work_queues ) | * work_queue | if ( work_queue .readItem ()) | job | {
3599
+ try processOneJob (@intFromEnum (Zcu .PerThread .Id .main ), comp , job , main_progress_node );
3600
+ continue : work ;
3601
+ };
3551
3602
if (comp .module ) | zcu | {
3552
3603
// If there's no work queued, check if there's anything outdated
3553
3604
// which we need to work on, and queue it if so.
3554
3605
if (try zcu .findOutdatedToAnalyze ()) | outdated | {
3555
3606
switch (outdated .unwrap ()) {
3556
- .decl = > | decl | try comp .work_queue . writeItem (.{ .analyze_decl = decl }),
3557
- .func = > | func | try comp .work_queue . writeItem (.{ .analyze_func = func }),
3607
+ .decl = > | decl | try comp .queueJob (.{ .analyze_decl = decl }),
3608
+ .func = > | func | try comp .queueJob (.{ .analyze_func = func }),
3558
3609
}
3559
3610
continue ;
3560
3611
}
@@ -3575,6 +3626,14 @@ fn performAllTheWorkInner(
3575
3626
3576
3627
const JobError = Allocator .Error ;
3577
3628
3629
+ pub fn queueJob (comp : * Compilation , job : Job ) ! void {
3630
+ try comp .work_queues [Job .stage (job )].writeItem (job );
3631
+ }
3632
+
3633
+ pub fn queueJobs (comp : * Compilation , jobs : []const Job ) ! void {
3634
+ for (jobs ) | job | try comp .queueJob (job );
3635
+ }
3636
+
3578
3637
fn processOneJob (tid : usize , comp : * Compilation , job : Job , prog_node : std.Progress.Node ) JobError ! void {
3579
3638
switch (job ) {
3580
3639
.codegen_decl = > | decl_index | {
@@ -6478,7 +6537,7 @@ pub fn addLinkLib(comp: *Compilation, lib_name: []const u8) !void {
6478
6537
};
6479
6538
const target = comp .root_mod .resolved_target .result ;
6480
6539
if (target .os .tag == .windows and target .ofmt != .c ) {
6481
- try comp .work_queue . writeItem (.{
6540
+ try comp .queueJob (.{
6482
6541
.windows_import_lib = comp .system_libs .count () - 1 ,
6483
6542
});
6484
6543
}
0 commit comments