1414//! int)` and `rec(x=int, y=int, z=int)` will have the same `llvm::Type`.
1515
1616use crate :: back:: write:: {
17- start_async_codegen, submit_post_lto_module_to_llvm , submit_pre_lto_module_to_llvm ,
18- OngoingCodegen ,
17+ start_async_codegen, submit_codegened_module_to_llvm , submit_post_lto_module_to_llvm ,
18+ submit_pre_lto_module_to_llvm , OngoingCodegen ,
1919} ;
2020use crate :: common:: { IntPredicate , RealPredicate , TypeKind } ;
2121use crate :: meth;
@@ -40,6 +40,7 @@ use rustc::ty::{self, Instance, Ty, TyCtxt};
4040use rustc_codegen_utils:: { check_for_rustc_errors_attr, symbol_names_test} ;
4141use rustc_data_structures:: fx:: FxHashMap ;
4242use rustc_data_structures:: profiling:: print_time_passes_entry;
43+ use rustc_data_structures:: sync:: { par_iter, Lock , ParallelIterator } ;
4344use rustc_hir as hir;
4445use rustc_hir:: def_id:: { DefId , LOCAL_CRATE } ;
4546use rustc_index:: vec:: Idx ;
@@ -606,20 +607,83 @@ pub fn codegen_crate<B: ExtraBackendMethods>(
606607 codegen_units
607608 } ;
608609
609- let mut total_codegen_time = Duration :: new ( 0 , 0 ) ;
610+ let total_codegen_time = Lock :: new ( Duration :: new ( 0 , 0 ) ) ;
610611
611- for cgu in codegen_units. into_iter ( ) {
612+ // The non-parallel compiler can only translate codegen units to LLVM IR
613+ // on a single thread, leading to a staircase effect where the N LLVM
614+ // threads have to wait on the single codegen threads to generate work
615+ // for them. The parallel compiler does not have this restriction, so
616+ // we can pre-load the LLVM queue in parallel before handing off
617+ // coordination to the OnGoingCodegen scheduler.
618+ //
619+ // This likely is a temporary measure. Once we don't have to support the
620+ // non-parallel compiler anymore, we can compile CGUs end-to-end in
621+ // parallel and get rid of the complicated scheduling logic.
622+ let pre_compile_cgus = |cgu_reuse : & [ CguReuse ] | {
623+ if cfg ! ( parallel_compiler) {
624+ tcx. sess . time ( "compile_first_CGU_batch" , || {
625+ // Try to find one CGU to compile per thread.
626+ let cgus: Vec < _ > = cgu_reuse
627+ . iter ( )
628+ . enumerate ( )
629+ . filter ( |& ( _, reuse) | reuse == & CguReuse :: No )
630+ . take ( tcx. sess . threads ( ) )
631+ . collect ( ) ;
632+
633+ // Compile the found CGUs in parallel.
634+ par_iter ( cgus)
635+ . map ( |( i, _) | {
636+ let start_time = Instant :: now ( ) ;
637+ let module = backend. compile_codegen_unit ( tcx, codegen_units[ i] . name ( ) ) ;
638+ let mut time = total_codegen_time. lock ( ) ;
639+ * time += start_time. elapsed ( ) ;
640+ ( i, module)
641+ } )
642+ . collect ( )
643+ } )
644+ } else {
645+ FxHashMap :: default ( )
646+ }
647+ } ;
648+
649+ let mut cgu_reuse = Vec :: new ( ) ;
650+ let mut pre_compiled_cgus: Option < FxHashMap < usize , _ > > = None ;
651+
652+ for ( i, cgu) in codegen_units. iter ( ) . enumerate ( ) {
612653 ongoing_codegen. wait_for_signal_to_codegen_item ( ) ;
613654 ongoing_codegen. check_for_errors ( tcx. sess ) ;
614655
615- let cgu_reuse = determine_cgu_reuse ( tcx, & cgu) ;
656+ // Do some setup work in the first iteration
657+ if pre_compiled_cgus. is_none ( ) {
658+ // Calculate the CGU reuse
659+ cgu_reuse = tcx. sess . time ( "find_cgu_reuse" , || {
660+ codegen_units. iter ( ) . map ( |cgu| determine_cgu_reuse ( tcx, & cgu) ) . collect ( )
661+ } ) ;
662+ // Pre compile some CGUs
663+ pre_compiled_cgus = Some ( pre_compile_cgus ( & cgu_reuse) ) ;
664+ }
665+
666+ let cgu_reuse = cgu_reuse[ i] ;
616667 tcx. sess . cgu_reuse_tracker . set_actual_reuse ( & cgu. name ( ) . as_str ( ) , cgu_reuse) ;
617668
618669 match cgu_reuse {
619670 CguReuse :: No => {
620- let start_time = Instant :: now ( ) ;
621- backend. compile_codegen_unit ( tcx, cgu. name ( ) , & ongoing_codegen. coordinator_send ) ;
622- total_codegen_time += start_time. elapsed ( ) ;
671+ let ( module, cost) =
672+ if let Some ( cgu) = pre_compiled_cgus. as_mut ( ) . unwrap ( ) . remove ( & i) {
673+ cgu
674+ } else {
675+ let start_time = Instant :: now ( ) ;
676+ let module = backend. compile_codegen_unit ( tcx, cgu. name ( ) ) ;
677+ let mut time = total_codegen_time. lock ( ) ;
678+ * time += start_time. elapsed ( ) ;
679+ module
680+ } ;
681+ submit_codegened_module_to_llvm (
682+ & backend,
683+ & ongoing_codegen. coordinator_send ,
684+ module,
685+ cost,
686+ ) ;
623687 false
624688 }
625689 CguReuse :: PreLto => {
@@ -652,7 +716,11 @@ pub fn codegen_crate<B: ExtraBackendMethods>(
652716
653717 // Since the main thread is sometimes blocked during codegen, we keep track
654718 // -Ztime-passes output manually.
655- print_time_passes_entry ( tcx. sess . time_passes ( ) , "codegen_to_LLVM_IR" , total_codegen_time) ;
719+ print_time_passes_entry (
720+ tcx. sess . time_passes ( ) ,
721+ "codegen_to_LLVM_IR" ,
722+ total_codegen_time. into_inner ( ) ,
723+ ) ;
656724
657725 :: rustc_incremental:: assert_module_sources:: assert_module_sources ( tcx) ;
658726
0 commit comments