@@ -15,8 +15,8 @@ use rustc_data_structures::profiling::{SelfProfilerRef, VerboseTimingGuard};
1515use rustc_errors:: emitter:: Emitter ;
1616use rustc_errors:: translation:: Translator ;
1717use rustc_errors:: {
18- Diag , DiagArgMap , DiagCtxt , DiagMessage , ErrCode , FatalErrorMarker , Level , MultiSpan , Style ,
19- Suggestions ,
18+ Diag , DiagArgMap , DiagCtxt , DiagMessage , ErrCode , FatalError , FatalErrorMarker , Level ,
19+ MultiSpan , Style , Suggestions ,
2020} ;
2121use rustc_fs_util:: link_or_copy;
2222use rustc_incremental:: {
@@ -992,6 +992,155 @@ fn do_fat_lto<B: ExtraBackendMethods>(
992992 B :: codegen ( cgcx, module, & cgcx. module_config )
993993}
994994
995+ fn do_thin_lto < ' a , B : ExtraBackendMethods > (
996+ cgcx : & ' a CodegenContext < B > ,
997+ llvm_start_time : & mut Option < VerboseTimingGuard < ' a > > ,
998+ exported_symbols_for_lto : Arc < Vec < String > > ,
999+ each_linked_rlib_for_lto : Vec < PathBuf > ,
1000+ needs_thin_lto : Vec < ( String , <B as WriteBackendMethods >:: ThinBuffer ) > ,
1001+ lto_import_only_modules : Vec < (
1002+ SerializedModule < <B as WriteBackendMethods >:: ModuleBuffer > ,
1003+ WorkProduct ,
1004+ ) > ,
1005+ ) -> Vec < CompiledModule > {
1006+ check_lto_allowed ( & cgcx) ;
1007+
1008+ let ( coordinator_send, coordinator_receive) = channel ( ) ;
1009+
1010+ // First up, convert our jobserver into a helper thread so we can use normal
1011+ // mpsc channels to manage our messages and such.
1012+ // After we've requested tokens then we'll, when we can,
1013+ // get tokens on `coordinator_receive` which will
1014+ // get managed in the main loop below.
1015+ let coordinator_send2 = coordinator_send. clone ( ) ;
1016+ let helper = jobserver:: client ( )
1017+ . into_helper_thread ( move |token| {
1018+ drop ( coordinator_send2. send ( Message :: Token :: < B > ( token) ) ) ;
1019+ } )
1020+ . expect ( "failed to spawn helper thread" ) ;
1021+
1022+ let mut work_items = vec ! [ ] ;
1023+
1024+ // We have LTO work to do. Perform the serial work here of
1025+ // figuring out what we're going to LTO and then push a
1026+ // bunch of work items onto our queue to do LTO. This all
1027+ // happens on the coordinator thread but it's very quick so
1028+ // we don't worry about tokens.
1029+ for ( work, cost) in generate_thin_lto_work (
1030+ cgcx,
1031+ & exported_symbols_for_lto,
1032+ & each_linked_rlib_for_lto,
1033+ needs_thin_lto,
1034+ lto_import_only_modules,
1035+ ) {
1036+ let insertion_index =
1037+ work_items. binary_search_by_key ( & cost, |& ( _, cost) | cost) . unwrap_or_else ( |e| e) ;
1038+ work_items. insert ( insertion_index, ( work, cost) ) ;
1039+ if cgcx. parallel {
1040+ helper. request_token ( ) ;
1041+ }
1042+ }
1043+
1044+ let mut codegen_aborted = None ;
1045+
1046+ // These are the Jobserver Tokens we currently hold. Does not include
1047+ // the implicit Token the compiler process owns no matter what.
1048+ let mut tokens = vec ! [ ] ;
1049+
1050+ // Amount of tokens that are used (including the implicit token).
1051+ let mut used_token_count = 0 ;
1052+
1053+ let mut compiled_modules = vec ! [ ] ;
1054+
1055+ // Run the message loop while there's still anything that needs message
1056+ // processing. Note that as soon as codegen is aborted we simply want to
1057+ // wait for all existing work to finish, so many of the conditions here
1058+ // only apply if codegen hasn't been aborted as they represent pending
1059+ // work to be done.
1060+ loop {
1061+ if codegen_aborted. is_none ( ) {
1062+ if used_token_count == 0 && work_items. is_empty ( ) {
1063+ // All codegen work is done.
1064+ break ;
1065+ }
1066+
1067+ // Spin up what work we can, only doing this while we've got available
1068+ // parallelism slots and work left to spawn.
1069+ while used_token_count < tokens. len ( ) + 1
1070+ && let Some ( ( item, _) ) = work_items. pop ( )
1071+ {
1072+ spawn_work ( & cgcx, coordinator_send. clone ( ) , llvm_start_time, item) ;
1073+ used_token_count += 1 ;
1074+ }
1075+ } else {
1076+ // Don't queue up any more work if codegen was aborted, we're
1077+ // just waiting for our existing children to finish.
1078+ if used_token_count == 0 {
1079+ break ;
1080+ }
1081+ }
1082+
1083+ // Relinquish accidentally acquired extra tokens. Subtract 1 for the implicit token.
1084+ tokens. truncate ( used_token_count. saturating_sub ( 1 ) ) ;
1085+
1086+ match coordinator_receive. recv ( ) . unwrap ( ) {
1087+ // Save the token locally and the next turn of the loop will use
1088+ // this to spawn a new unit of work, or it may get dropped
1089+ // immediately if we have no more work to spawn.
1090+ Message :: Token ( token) => match token {
1091+ Ok ( token) => {
1092+ tokens. push ( token) ;
1093+ }
1094+ Err ( e) => {
1095+ let msg = & format ! ( "failed to acquire jobserver token: {e}" ) ;
1096+ cgcx. diag_emitter . fatal ( msg) ;
1097+ codegen_aborted = Some ( FatalError ) ;
1098+ }
1099+ } ,
1100+
1101+ Message :: CodegenDone { .. }
1102+ | Message :: CodegenComplete
1103+ | Message :: CodegenAborted
1104+ | Message :: AddImportOnlyModule { .. } => {
1105+ unreachable ! ( )
1106+ }
1107+
1108+ Message :: WorkItem { result } => {
1109+ // If a thread exits successfully then we drop a token associated
1110+ // with that worker and update our `used_token_count` count.
1111+ // We may later re-acquire a token to continue running more work.
1112+ // We may also not actually drop a token here if the worker was
1113+ // running with an "ephemeral token".
1114+ used_token_count -= 1 ;
1115+
1116+ match result {
1117+ Ok ( WorkItemResult :: Finished ( compiled_module) ) => {
1118+ compiled_modules. push ( compiled_module) ;
1119+ }
1120+ Ok ( WorkItemResult :: NeedsFatLto ( _) ) | Ok ( WorkItemResult :: NeedsThinLto ( _, _) ) => {
1121+ unreachable ! ( )
1122+ }
1123+ Err ( Some ( WorkerFatalError ) ) => {
1124+ // Like `CodegenAborted`, wait for remaining work to finish.
1125+ codegen_aborted = Some ( FatalError ) ;
1126+ }
1127+ Err ( None ) => {
1128+ // If the thread failed that means it panicked, so
1129+ // we abort immediately.
1130+ bug ! ( "worker thread panicked" ) ;
1131+ }
1132+ }
1133+ }
1134+ }
1135+ }
1136+
1137+ if let Some ( codegen_aborted) = codegen_aborted {
1138+ codegen_aborted. raise ( ) ;
1139+ }
1140+
1141+ compiled_modules
1142+ }
1143+
9951144fn execute_thin_lto_work_item < B : ExtraBackendMethods > (
9961145 cgcx : & CodegenContext < B > ,
9971146 module : lto:: ThinModule < B > ,
@@ -1085,9 +1234,8 @@ fn start_executing_work<B: ExtraBackendMethods>(
10851234 regular_config : Arc < ModuleConfig > ,
10861235 allocator_config : Arc < ModuleConfig > ,
10871236 allocator_module : Option < ModuleCodegen < B :: Module > > ,
1088- tx_to_llvm_workers : Sender < Message < B > > ,
1237+ coordinator_send : Sender < Message < B > > ,
10891238) -> thread:: JoinHandle < Result < CompiledModules , ( ) > > {
1090- let coordinator_send = tx_to_llvm_workers;
10911239 let sess = tcx. sess ;
10921240
10931241 let mut each_linked_rlib_for_lto = Vec :: new ( ) ;
@@ -1307,7 +1455,6 @@ fn start_executing_work<B: ExtraBackendMethods>(
13071455 let mut needs_fat_lto = Vec :: new ( ) ;
13081456 let mut needs_thin_lto = Vec :: new ( ) ;
13091457 let mut lto_import_only_modules = Vec :: new ( ) ;
1310- let mut started_lto = false ;
13111458
13121459 /// Possible state transitions:
13131460 /// - Ongoing -> Completed
@@ -1397,48 +1544,8 @@ fn start_executing_work<B: ExtraBackendMethods>(
13971544 if running_with_any_token ( main_thread_state, running_with_own_token) == 0
13981545 && work_items. is_empty ( )
13991546 {
1400- // All codegen work is done. Do we have LTO work to do?
1401- if needs_fat_lto. is_empty ( )
1402- && needs_thin_lto. is_empty ( )
1403- && lto_import_only_modules. is_empty ( )
1404- {
1405- // Nothing more to do!
1406- break ;
1407- }
1408-
1409- // We have LTO work to do. Perform the serial work here of
1410- // figuring out what we're going to LTO and then push a
1411- // bunch of work items onto our queue to do LTO. This all
1412- // happens on the coordinator thread but it's very quick so
1413- // we don't worry about tokens.
1414- assert ! ( !started_lto) ;
1415- started_lto = true ;
1416-
1417- if !needs_fat_lto. is_empty ( ) {
1418- // We're doing fat LTO outside of the main loop.
1419- break ;
1420- }
1421-
1422- check_lto_allowed ( & cgcx) ;
1423-
1424- let needs_thin_lto = mem:: take ( & mut needs_thin_lto) ;
1425- let import_only_modules = mem:: take ( & mut lto_import_only_modules) ;
1426-
1427- for ( work, cost) in generate_thin_lto_work (
1428- & cgcx,
1429- & exported_symbols_for_lto,
1430- & each_linked_rlib_file_for_lto,
1431- needs_thin_lto,
1432- import_only_modules,
1433- ) {
1434- let insertion_index = work_items
1435- . binary_search_by_key ( & cost, |& ( _, cost) | cost)
1436- . unwrap_or_else ( |e| e) ;
1437- work_items. insert ( insertion_index, ( work, cost) ) ;
1438- if cgcx. parallel {
1439- helper. request_token ( ) ;
1440- }
1441- }
1547+ // All codegen work is done.
1548+ break ;
14421549 }
14431550
14441551 // In this branch, we know that everything has been codegened,
@@ -1576,12 +1683,10 @@ fn start_executing_work<B: ExtraBackendMethods>(
15761683 compiled_modules. push ( compiled_module) ;
15771684 }
15781685 Ok ( WorkItemResult :: NeedsFatLto ( fat_lto_input) ) => {
1579- assert ! ( !started_lto) ;
15801686 assert ! ( needs_thin_lto. is_empty( ) ) ;
15811687 needs_fat_lto. push ( fat_lto_input) ;
15821688 }
15831689 Ok ( WorkItemResult :: NeedsThinLto ( name, thin_buffer) ) => {
1584- assert ! ( !started_lto) ;
15851690 assert ! ( needs_fat_lto. is_empty( ) ) ;
15861691 needs_thin_lto. push ( ( name, thin_buffer) ) ;
15871692 }
@@ -1598,7 +1703,6 @@ fn start_executing_work<B: ExtraBackendMethods>(
15981703 }
15991704
16001705 Message :: AddImportOnlyModule { module_data, work_product } => {
1601- assert ! ( !started_lto) ;
16021706 assert_eq ! ( codegen_state, Ongoing ) ;
16031707 assert_eq ! ( main_thread_state, MainThreadState :: Codegenning ) ;
16041708 lto_import_only_modules. push ( ( module_data, work_product) ) ;
@@ -1614,6 +1718,7 @@ fn start_executing_work<B: ExtraBackendMethods>(
16141718 drop ( codegen_state) ;
16151719 drop ( tokens) ;
16161720 drop ( helper) ;
1721+ assert ! ( work_items. is_empty( ) ) ;
16171722
16181723 if !needs_fat_lto. is_empty ( ) {
16191724 assert ! ( compiled_modules. is_empty( ) ) ;
@@ -1628,6 +1733,18 @@ fn start_executing_work<B: ExtraBackendMethods>(
16281733 lto_import_only_modules,
16291734 ) ;
16301735 compiled_modules. push ( module) ;
1736+ } else if !needs_thin_lto. is_empty ( ) || !lto_import_only_modules. is_empty ( ) {
1737+ assert ! ( compiled_modules. is_empty( ) ) ;
1738+ assert ! ( needs_fat_lto. is_empty( ) ) ;
1739+
1740+ compiled_modules. extend ( do_thin_lto (
1741+ & cgcx,
1742+ & mut llvm_start_time,
1743+ exported_symbols_for_lto,
1744+ each_linked_rlib_file_for_lto,
1745+ needs_thin_lto,
1746+ lto_import_only_modules,
1747+ ) ) ;
16311748 }
16321749
16331750 // Drop to print timings
0 commit comments