77//! goes along from the output of the previous stage.
88
99use std:: borrow:: Cow ;
10- use std:: collections:: { HashMap , HashSet } ;
10+ use std:: collections:: { BTreeMap , HashMap , HashSet } ;
1111use std:: ffi:: OsStr ;
1212use std:: io:: BufReader ;
1313use std:: io:: prelude:: * ;
@@ -19,7 +19,7 @@ use serde_derive::Deserialize;
1919#[ cfg( feature = "tracing" ) ]
2020use tracing:: span;
2121
22- use crate :: core:: build_steps:: gcc:: { Gcc , GccOutput , add_cg_gcc_cargo_flags} ;
22+ use crate :: core:: build_steps:: gcc:: { Gcc , GccOutput , GccTargetPair , add_cg_gcc_cargo_flags} ;
2323use crate :: core:: build_steps:: tool:: { RustcPrivateCompilers , SourceType , copy_lld_artifacts} ;
2424use crate :: core:: build_steps:: { dist, llvm} ;
2525use crate :: core:: builder;
@@ -1576,17 +1576,98 @@ impl Step for RustcLink {
15761576 }
15771577}
15781578
1579+ /// Set of `libgccjit` dylibs that can be used by `cg_gcc` to compile code for a set of targets.
1580+ #[ derive( Clone ) ]
1581+ pub struct GccDylibSet {
1582+ dylibs : BTreeMap < GccTargetPair , GccOutput > ,
1583+ host_pair : GccTargetPair ,
1584+ }
1585+
1586+ impl GccDylibSet {
1587+ /// Returns the libgccjit.so dylib that corresponds to a host target on which `cg_gcc` will be
1588+ /// executed, and which will target the host. So e.g. if `cg_gcc` will be executed on
1589+ /// x86_64-unknown-linux-gnu, the host dylib will be for compilation pair
1590+ /// `(x86_64-unknown-linux-gnu, x86_64-unknown-linux-gnu)`.
1591+ fn host_dylib ( & self ) -> & GccOutput {
1592+ self . dylibs . get ( & self . host_pair ) . unwrap_or_else ( || {
1593+ panic ! ( "libgccjit.so was not built for host target {}" , self . host_pair)
1594+ } )
1595+ }
1596+
1597+ /// Install the libgccjit dylibs to the corresponding target directories of the given compiler.
1598+ /// cg_gcc know how to search for the libgccjit dylibs in these directories, according to the
1599+ /// (host, target) pair that is being compiled by rustc and cg_gcc.
1600+ pub fn install_to ( & self , builder : & Builder < ' _ > , compiler : Compiler ) {
1601+ if builder. config . dry_run ( ) {
1602+ return ;
1603+ }
1604+
1605+ // <rustc>/lib/<host-target>/codegen-backends
1606+ let cg_sysroot = builder. sysroot_codegen_backends ( compiler) ;
1607+
1608+ for ( target_pair, libgccjit) in & self . dylibs {
1609+ assert_eq ! (
1610+ target_pair. host( ) ,
1611+ compiler. host,
1612+ "Trying to install libgccjit ({target_pair}) to a compiler with a different host ({})" ,
1613+ compiler. host
1614+ ) ;
1615+ let libgccjit = libgccjit. libgccjit ( ) ;
1616+ let target_filename = libgccjit. file_name ( ) . unwrap ( ) . to_str ( ) . unwrap ( ) ;
1617+
1618+ // If we build libgccjit ourselves, then `libgccjit` can actually be a symlink.
1619+ // In that case, we have to resolve it first, otherwise we'd create a symlink to a
1620+ // symlink, which wouldn't work.
1621+ let actual_libgccjit_path = t ! (
1622+ libgccjit. canonicalize( ) ,
1623+ format!( "Cannot find libgccjit at {}" , libgccjit. display( ) )
1624+ ) ;
1625+
1626+ // <cg-sysroot>/lib/<target>/libgccjit.so
1627+ let dest_dir = cg_sysroot. join ( "lib" ) . join ( target_pair. target ( ) ) ;
1628+ t ! ( fs:: create_dir_all( & dest_dir) ) ;
1629+ let dst = dest_dir. join ( target_filename) ;
1630+ builder. copy_link ( & actual_libgccjit_path, & dst, FileType :: NativeLibrary ) ;
1631+ }
1632+ }
1633+ }
1634+
15791635/// Output of the `compile::GccCodegenBackend` step.
1580- /// It includes the path to the libgccjit library on which this backend depends.
1636+ ///
1637+ /// It contains paths to all built libgccjit libraries on which this backend depends here.
15811638#[ derive( Clone ) ]
15821639pub struct GccCodegenBackendOutput {
15831640 stamp : BuildStamp ,
1584- gcc : GccOutput ,
1641+ dylib_set : GccDylibSet ,
15851642}
15861643
1644+ /// Builds the GCC codegen backend (`cg_gcc`).
1645+ /// The `cg_gcc` backend uses `libgccjit`, which requires a separate build for each
1646+ /// `host -> target` pair. So if you are on linux-x64 and build for linux-aarch64,
1647+ /// you will need at least:
1648+ /// - linux-x64 -> linux-x64 libgccjit (for building host code like proc macros)
1649+ /// - linux-x64 -> linux-aarch64 libgccjit (for the aarch64 target code)
1650+ ///
1651+ /// We model this by having a single cg_gcc for a given host target, which contains one
1652+ /// libgccjit per (host, target) pair.
1653+ /// Note that the host target is taken from `self.compilers.target_compiler.host`.
15871654#[ derive( Debug , Clone , PartialEq , Eq , Hash ) ]
15881655pub struct GccCodegenBackend {
15891656 compilers : RustcPrivateCompilers ,
1657+ targets : Vec < TargetSelection > ,
1658+ }
1659+
1660+ impl GccCodegenBackend {
1661+ /// Build `cg_gcc` that will run on host `H` (`compilers.target_compiler.host`) and will be
1662+ /// able to produce code target pairs (`H`, `T`) for all `T` from `targets`.
1663+ pub fn for_targets (
1664+ compilers : RustcPrivateCompilers ,
1665+ mut targets : Vec < TargetSelection > ,
1666+ ) -> Self {
1667+ // Sort targets to improve step cache hits
1668+ targets. sort ( ) ;
1669+ Self { compilers, targets }
1670+ }
15901671}
15911672
15921673impl Step for GccCodegenBackend {
@@ -1599,23 +1680,34 @@ impl Step for GccCodegenBackend {
15991680 }
16001681
16011682 fn make_run ( run : RunConfig < ' _ > ) {
1602- run. builder . ensure ( GccCodegenBackend {
1603- compilers : RustcPrivateCompilers :: new ( run. builder , run. builder . top_stage , run. target ) ,
1604- } ) ;
1683+ // By default, build cg_gcc that will only be able to compile native code for the given
1684+ // host target.
1685+ let compilers = RustcPrivateCompilers :: new ( run. builder , run. builder . top_stage , run. target ) ;
1686+ run. builder . ensure ( GccCodegenBackend { compilers, targets : vec ! [ run. target] } ) ;
16051687 }
16061688
16071689 fn run ( self , builder : & Builder < ' _ > ) -> Self :: Output {
1608- let target = self . compilers . target ( ) ;
1690+ let host = self . compilers . target ( ) ;
16091691 let build_compiler = self . compilers . build_compiler ( ) ;
16101692
16111693 let stamp = build_stamp:: codegen_backend_stamp (
16121694 builder,
16131695 build_compiler,
1614- target ,
1696+ host ,
16151697 & CodegenBackendKind :: Gcc ,
16161698 ) ;
16171699
1618- let gcc = builder. ensure ( Gcc { target } ) ;
1700+ let dylib_set = GccDylibSet {
1701+ dylibs : self
1702+ . targets
1703+ . iter ( )
1704+ . map ( |& target| {
1705+ let target_pair = GccTargetPair :: for_target_pair ( host, target) ;
1706+ ( target_pair, builder. ensure ( Gcc { target_pair } ) )
1707+ } )
1708+ . collect ( ) ,
1709+ host_pair : GccTargetPair :: for_native_build ( host) ,
1710+ } ;
16191711
16201712 if builder. config . keep_stage . contains ( & build_compiler. stage ) {
16211713 trace ! ( "`keep-stage` requested" ) ;
@@ -1625,29 +1717,29 @@ impl Step for GccCodegenBackend {
16251717 ) ;
16261718 // Codegen backends are linked separately from this step today, so we don't do
16271719 // anything here.
1628- return GccCodegenBackendOutput { stamp, gcc } ;
1720+ return GccCodegenBackendOutput { stamp, dylib_set } ;
16291721 }
16301722
16311723 let mut cargo = builder:: Cargo :: new (
16321724 builder,
16331725 build_compiler,
16341726 Mode :: Codegen ,
16351727 SourceType :: InTree ,
1636- target ,
1728+ host ,
16371729 Kind :: Build ,
16381730 ) ;
16391731 cargo. arg ( "--manifest-path" ) . arg ( builder. src . join ( "compiler/rustc_codegen_gcc/Cargo.toml" ) ) ;
1640- rustc_cargo_env ( builder, & mut cargo, target ) ;
1732+ rustc_cargo_env ( builder, & mut cargo, host ) ;
16411733
1642- add_cg_gcc_cargo_flags ( & mut cargo, & gcc ) ;
1734+ add_cg_gcc_cargo_flags ( & mut cargo, dylib_set . host_dylib ( ) ) ;
16431735
16441736 let _guard =
1645- builder. msg ( Kind :: Build , "codegen backend gcc" , Mode :: Codegen , build_compiler, target ) ;
1737+ builder. msg ( Kind :: Build , "codegen backend gcc" , Mode :: Codegen , build_compiler, host ) ;
16461738 let files = run_cargo ( builder, cargo, vec ! [ ] , & stamp, vec ! [ ] , false , false ) ;
16471739
16481740 GccCodegenBackendOutput {
16491741 stamp : write_codegen_backend_stamp ( stamp, files, builder. config . dry_run ( ) ) ,
1650- gcc ,
1742+ dylib_set ,
16511743 }
16521744 }
16531745
@@ -2324,12 +2416,65 @@ impl Step for Assemble {
23242416 copy_codegen_backends_to_sysroot ( builder, stamp, target_compiler) ;
23252417 }
23262418 CodegenBackendKind :: Gcc => {
2327- let output =
2328- builder. ensure ( GccCodegenBackend { compilers : prepare_compilers ( ) } ) ;
2419+ // We need to build cg_gcc for the host target of the compiler which we
2420+ // build here, which is `target_compiler`.
2421+ // But we also need to build libgccjit for some additional targets, in
2422+ // the most general case.
2423+ // 1. We need to build (target_compiler.host, stdlib target) libgccjit
2424+ // for all stdlibs that we build, so that cg_gcc can be used to build code
2425+ // for all those targets.
2426+ // 2. We need to build (target_compiler.host, target_compiler.host)
2427+ // libgccjit, so that the target compiler can compile host code (e.g. proc
2428+ // macros).
2429+ // 3. We need to build (target_compiler.host, host target) libgccjit
2430+ // for all *host targets* that we build, so that cg_gcc can be used to
2431+ // build a (possibly cross-compiled) stage 2+ rustc.
2432+ //
2433+ // Assume that we are on host T1 and we do a stage2 build of rustc for T2.
2434+ // We want the T2 rustc compiler to be able to use cg_gcc and build code
2435+ // for T2 (host) and T3 (target). We also want to build the stage2 compiler
2436+ // itself using cg_gcc.
2437+ // This could correspond to the following bootstrap invocation:
2438+ // `x build rustc --build T1 --host T2 --target T3 --set codegen-backends=['gcc', 'llvm']`
2439+ //
2440+ // For that, we will need the following GCC target pairs:
2441+ // 1. T1 -> T2 (to cross-compile a T2 rustc using cg_gcc running on T1)
2442+ // 2. T2 -> T2 (to build host code with the stage 2 rustc running on T2)
2443+ // 3. T2 -> T3 (to cross-compile code with the stage 2 rustc running on T2)
2444+ //
2445+ // FIXME: this set of targets is *maximal*, in reality we might need
2446+ // less libgccjits at this current build stage. Try to reduce the set of
2447+ // GCC dylibs built below by taking a look at the current stage and whether
2448+ // cg_gcc is used as the default codegen backend.
2449+
2450+ let compilers = prepare_compilers ( ) ;
2451+
2452+ // The left side of the target pairs below is implied. It has to match the
2453+ // host target on which cg_gcc will run, which is the host target of
2454+ // `target_compiler`. We only pass the right side of the target pairs to
2455+ // the `GccCodegenBackend` constructor.
2456+ let mut targets = HashSet :: new ( ) ;
2457+ // Add all host targets, so that we are able to build host code in this
2458+ // bootstrap invocation using cg_gcc.
2459+ for target in & builder. hosts {
2460+ targets. insert ( * target) ;
2461+ }
2462+ // Add all stdlib targets, so that the built rustc can produce code for them
2463+ for target in & builder. targets {
2464+ targets. insert ( * target) ;
2465+ }
2466+ // Add the host target of the built rustc itself, so that it can build
2467+ // host code (e.g. proc macros) using cg_gcc.
2468+ targets. insert ( compilers. target_compiler ( ) . host ) ;
2469+
2470+ let output = builder. ensure ( GccCodegenBackend :: for_targets (
2471+ compilers,
2472+ targets. into_iter ( ) . collect ( ) ,
2473+ ) ) ;
23292474 copy_codegen_backends_to_sysroot ( builder, output. stamp , target_compiler) ;
2330- // Also copy libgccjit to the library sysroot, so that it is available for
2331- // the codegen backend.
2332- output. gcc . install_to ( builder, & rustc_libdir ) ;
2475+ // Also copy all requires libgccjit dylibs to the corresponding
2476+ // library sysroots, so that they are available for the codegen backend.
2477+ output. dylib_set . install_to ( builder, target_compiler ) ;
23332478 }
23342479 CodegenBackendKind :: Llvm | CodegenBackendKind :: Custom ( _) => continue ,
23352480 }
0 commit comments