14
14
//! int)` and `rec(x=int, y=int, z=int)` will have the same `llvm::Type`.
15
15
16
16
use 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 ,
19
19
} ;
20
20
use crate :: common:: { IntPredicate , RealPredicate , TypeKind } ;
21
21
use crate :: meth;
@@ -40,6 +40,7 @@ use rustc::ty::{self, Instance, Ty, TyCtxt};
40
40
use rustc_codegen_utils:: { check_for_rustc_errors_attr, symbol_names_test} ;
41
41
use rustc_data_structures:: fx:: FxHashMap ;
42
42
use rustc_data_structures:: profiling:: print_time_passes_entry;
43
+ use rustc_data_structures:: sync:: { par_iter, Lock , ParallelIterator } ;
43
44
use rustc_hir as hir;
44
45
use rustc_hir:: def_id:: { DefId , LOCAL_CRATE } ;
45
46
use rustc_index:: vec:: Idx ;
@@ -606,20 +607,83 @@ pub fn codegen_crate<B: ExtraBackendMethods>(
606
607
codegen_units
607
608
} ;
608
609
609
- let mut total_codegen_time = Duration :: new ( 0 , 0 ) ;
610
+ let total_codegen_time = Lock :: new ( Duration :: new ( 0 , 0 ) ) ;
610
611
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 ( ) {
612
653
ongoing_codegen. wait_for_signal_to_codegen_item ( ) ;
613
654
ongoing_codegen. check_for_errors ( tcx. sess ) ;
614
655
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] ;
616
667
tcx. sess . cgu_reuse_tracker . set_actual_reuse ( & cgu. name ( ) . as_str ( ) , cgu_reuse) ;
617
668
618
669
match cgu_reuse {
619
670
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
+ ) ;
623
687
false
624
688
}
625
689
CguReuse :: PreLto => {
@@ -652,7 +716,11 @@ pub fn codegen_crate<B: ExtraBackendMethods>(
652
716
653
717
// Since the main thread is sometimes blocked during codegen, we keep track
654
718
// -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
+ ) ;
656
724
657
725
:: rustc_incremental:: assert_module_sources:: assert_module_sources ( tcx) ;
658
726
0 commit comments