@@ -18,7 +18,9 @@ use std::process::Command;
1818
1919use  object:: read:: archive:: ArchiveFile ; 
2020use  object:: BinaryFormat ; 
21+ use  sha2:: Digest ; 
2122
23+ use  crate :: bolt:: { instrument_with_bolt,  optimize_with_bolt} ; 
2224use  crate :: builder:: { Builder ,  Kind ,  RunConfig ,  ShouldRun ,  Step } ; 
2325use  crate :: cache:: { Interned ,  INTERNER } ; 
2426use  crate :: channel; 
@@ -1904,6 +1906,26 @@ fn add_env(builder: &Builder<'_>, cmd: &mut Command, target: TargetSelection) {
19041906    } 
19051907} 
19061908
1909+ fn  install_llvm_file ( builder :  & Builder < ' _ > ,  source :  & Path ,  destination :  & Path )  { 
1910+     if  builder. config . dry_run ( )  { 
1911+         return ; 
1912+     } 
1913+ 
1914+     // After LLVM is built, we modify (instrument or optimize) the libLLVM.so library file. 
1915+     // This is not done in-place so that the built LLVM files are not "tainted" with BOLT. 
1916+     // We perform the instrumentation/optimization here, on the fly, just before they are being 
1917+     // packaged into some destination directory. 
1918+     let  postprocessed = if  builder. config . llvm_bolt_profile_generate  { 
1919+         builder. ensure ( BoltInstrument :: new ( source. to_path_buf ( ) ) ) 
1920+     }  else  if  let  Some ( path)  = & builder. config . llvm_bolt_profile_use  { 
1921+         builder. ensure ( BoltOptimize :: new ( source. to_path_buf ( ) ,  path. into ( ) ) ) 
1922+     }  else  { 
1923+         source. to_path_buf ( ) 
1924+     } ; 
1925+ 
1926+     builder. install ( & postprocessed,  destination,  0o644 ) ; 
1927+ } 
1928+ 
19071929/// Maybe add LLVM object files to the given destination lib-dir. Allows either static or dynamic linking. 
19081930/// 
19091931/// Returns whether the files were actually copied. 
@@ -1955,7 +1977,7 @@ fn maybe_install_llvm(builder: &Builder<'_>, target: TargetSelection, dst_libdir
19551977            }  else  { 
19561978                PathBuf :: from ( file) 
19571979            } ; 
1958-             builder . install ( & file,  dst_libdir,   0o644 ) ; 
1980+             install_llvm_file ( builder ,   & file,  dst_libdir) ; 
19591981        } 
19601982        !builder. config . dry_run ( ) 
19611983    }  else  { 
@@ -1986,6 +2008,117 @@ pub fn maybe_install_llvm_runtime(builder: &Builder<'_>, target: TargetSelection
19862008    } 
19872009} 
19882010
2011+ /// Creates an output path to a BOLT-manipulated artifact for the given `file`. 
2012+ /// The hash of the file is used to make sure that we don't mix BOLT artifacts amongst different 
2013+ /// files with the same name. 
2014+ /// 
2015+ /// We need to keep the file-name the same though, to make sure that copying the manipulated file 
2016+ /// to a directory will not change the final file path. 
2017+ fn  create_bolt_output_path ( builder :  & Builder < ' _ > ,  file :  & Path ,  hash :  & str )  -> PathBuf  { 
2018+     let  directory = builder. out . join ( "bolt" ) . join ( hash) ; 
2019+     t ! ( fs:: create_dir_all( & directory) ) ; 
2020+     directory. join ( file. file_name ( ) . unwrap ( ) ) 
2021+ } 
2022+ 
2023+ /// Instrument the provided file with BOLT. 
2024+ /// Returns a path to the instrumented artifact. 
2025+ #[ derive( Clone ,  Debug ,  Eq ,  Hash ,  PartialEq ) ]  
2026+ pub  struct  BoltInstrument  { 
2027+     file :  PathBuf , 
2028+     hash :  String , 
2029+ } 
2030+ 
2031+ impl  BoltInstrument  { 
2032+     fn  new ( file :  PathBuf )  -> Self  { 
2033+         let  mut  hasher = sha2:: Sha256 :: new ( ) ; 
2034+         hasher. update ( t ! ( fs:: read( & file) ) ) ; 
2035+         let  hash = hex:: encode ( hasher. finalize ( ) . as_slice ( ) ) ; 
2036+ 
2037+         Self  {  file,  hash } 
2038+     } 
2039+ } 
2040+ 
2041+ impl  Step  for  BoltInstrument  { 
2042+     type  Output  = PathBuf ; 
2043+ 
2044+     const  ONLY_HOSTS :  bool  = false ; 
2045+     const  DEFAULT :  bool  = false ; 
2046+ 
2047+     fn  should_run ( run :  ShouldRun < ' _ > )  -> ShouldRun < ' _ >  { 
2048+         run. never ( ) 
2049+     } 
2050+ 
2051+     fn  run ( self ,  builder :  & Builder < ' _ > )  -> PathBuf  { 
2052+         if  builder. build . config . dry_run ( )  { 
2053+             return  self . file . clone ( ) ; 
2054+         } 
2055+ 
2056+         if  builder. build . config . llvm_from_ci  { 
2057+             println ! ( "warning: trying to use BOLT with LLVM from CI, this will probably not work" ) ; 
2058+         } 
2059+ 
2060+         println ! ( "Instrumenting {} with BOLT" ,  self . file. display( ) ) ; 
2061+ 
2062+         let  output_path = create_bolt_output_path ( builder,  & self . file ,  & self . hash ) ; 
2063+         if  !output_path. is_file ( )  { 
2064+             instrument_with_bolt ( & self . file ,  & output_path) ; 
2065+         } 
2066+         output_path
2067+     } 
2068+ } 
2069+ 
2070+ /// Optimize the provided file with BOLT. 
2071+ /// Returns a path to the optimized artifact. 
2072+ /// 
2073+ /// The hash is stored in the step to make sure that we don't optimize the same file 
2074+ /// twice (even under  different file paths). 
2075+ #[ derive( Clone ,  Debug ,  Eq ,  Hash ,  PartialEq ) ]  
2076+ pub  struct  BoltOptimize  { 
2077+     file :  PathBuf , 
2078+     profile :  PathBuf , 
2079+     hash :  String , 
2080+ } 
2081+ 
2082+ impl  BoltOptimize  { 
2083+     fn  new ( file :  PathBuf ,  profile :  PathBuf )  -> Self  { 
2084+         let  mut  hasher = sha2:: Sha256 :: new ( ) ; 
2085+         hasher. update ( t ! ( fs:: read( & file) ) ) ; 
2086+         hasher. update ( t ! ( fs:: read( & profile) ) ) ; 
2087+         let  hash = hex:: encode ( hasher. finalize ( ) . as_slice ( ) ) ; 
2088+ 
2089+         Self  {  file,  profile,  hash } 
2090+     } 
2091+ } 
2092+ 
2093+ impl  Step  for  BoltOptimize  { 
2094+     type  Output  = PathBuf ; 
2095+ 
2096+     const  ONLY_HOSTS :  bool  = false ; 
2097+     const  DEFAULT :  bool  = false ; 
2098+ 
2099+     fn  should_run ( run :  ShouldRun < ' _ > )  -> ShouldRun < ' _ >  { 
2100+         run. never ( ) 
2101+     } 
2102+ 
2103+     fn  run ( self ,  builder :  & Builder < ' _ > )  -> PathBuf  { 
2104+         if  builder. build . config . dry_run ( )  { 
2105+             return  self . file . clone ( ) ; 
2106+         } 
2107+ 
2108+         if  builder. build . config . llvm_from_ci  { 
2109+             println ! ( "warning: trying to use BOLT with LLVM from CI, this will probably not work" ) ; 
2110+         } 
2111+ 
2112+         println ! ( "Optimizing {} with BOLT" ,  self . file. display( ) ) ; 
2113+ 
2114+         let  output_path = create_bolt_output_path ( builder,  & self . file ,  & self . hash ) ; 
2115+         if  !output_path. is_file ( )  { 
2116+             optimize_with_bolt ( & self . file ,  & self . profile ,  & output_path) ; 
2117+         } 
2118+         output_path
2119+     } 
2120+ } 
2121+ 
19892122#[ derive( Clone ,  Debug ,  Eq ,  Hash ,  PartialEq ) ]  
19902123pub  struct  LlvmTools  { 
19912124    pub  target :  TargetSelection , 
0 commit comments