@@ -14,6 +14,7 @@ use std::path::{Path, PathBuf};
1414use std:: sync:: OnceLock ;
1515use std:: { env, fs} ;
1616
17+ use build_helper:: exit;
1718use build_helper:: git:: PathFreshness ;
1819
1920use crate :: core:: builder:: { Builder , RunConfig , ShouldRun , Step , StepMetadata } ;
@@ -93,6 +94,25 @@ struct LdFlags {
9394 module : OsString ,
9495}
9596
97+ /// Allows each step to add C/Cxx flags which are only used for a specific cmake invocation.
98+ #[ derive( Debug , Clone , Default ) ]
99+ struct CcFlags {
100+ /// Additional values for CMAKE_CC_FLAGS, to be added before all other values.
101+ cflags : OsString ,
102+ /// Additional values for CMAKE_CXX_FLAGS, to be added before all other values.
103+ cxxflags : OsString ,
104+ }
105+
106+ impl CcFlags {
107+ fn push_all ( & mut self , s : impl AsRef < OsStr > ) {
108+ let s = s. as_ref ( ) ;
109+ self . cflags . push ( " " ) ;
110+ self . cflags . push ( s) ;
111+ self . cxxflags . push ( " " ) ;
112+ self . cxxflags . push ( s) ;
113+ }
114+ }
115+
96116impl LdFlags {
97117 fn push_all ( & mut self , s : impl AsRef < OsStr > ) {
98118 let s = s. as_ref ( ) ;
@@ -473,16 +493,6 @@ impl Step for Llvm {
473493 enabled_llvm_runtimes. push ( "compiler-rt" ) ;
474494 }
475495
476- // This is an experimental flag, which likely builds more than necessary.
477- // We will optimize it when we get closer to releasing it on nightly.
478- if builder. config . llvm_offload {
479- enabled_llvm_runtimes. push ( "offload" ) ;
480- //FIXME(ZuseZ4): LLVM intends to drop the offload dependency on openmp.
481- //Remove this line once they achieved it.
482- enabled_llvm_runtimes. push ( "openmp" ) ;
483- enabled_llvm_projects. push ( "compiler-rt" ) ;
484- }
485-
486496 if !enabled_llvm_projects. is_empty ( ) {
487497 enabled_llvm_projects. sort ( ) ;
488498 enabled_llvm_projects. dedup ( ) ;
@@ -917,6 +927,175 @@ fn get_var(var_base: &str, host: &str, target: &str) -> Option<OsString> {
917927 . or_else ( || env:: var_os ( var_base) )
918928}
919929
930+ #[ derive( Clone ) ]
931+ pub struct BuiltOmpOffload {
932+ /// Path to the omp and offload dylibs.
933+ offload : Vec < PathBuf > ,
934+ }
935+
936+ impl BuiltOmpOffload {
937+ pub fn offload_paths ( & self ) -> Vec < PathBuf > {
938+ self . offload . clone ( )
939+ }
940+ }
941+
942+ // FIXME(offload): In an ideal world, we would just enable the offload runtime in our previous LLVM
943+ // build step. For now, we still depend on the openmp runtime since we use some of it's API, so we
944+ // build both. However, when building those runtimes as part of the LLVM step, then LLVM's cmake
945+ // implicitly assumes that Clang has also been build and will try to use it. In the Rust CI, we
946+ // don't always build clang (due to compile times), but instead use a slightly older external clang.
947+ // LLVM tries to remove this build dependency of offload/openmp on Clang for LLVM-22, so in the
948+ // future we might be able to integrate this step into the LLVM step. For now, we instead introduce
949+ // a Clang_DIR bootstrap option, which allows us tell CMake to use an external clang for these two
950+ // runtimes. This external clang will try to use it's own (older) include dirs when building our
951+ // in-tree LLVM submodule, which will cause build failures. To prevent those, we now also
952+ // explicitly set our include dirs.
953+ #[ derive( Debug , Copy , Clone , Hash , PartialEq , Eq ) ]
954+ pub struct OmpOffload {
955+ pub target : TargetSelection ,
956+ }
957+
958+ impl Step for OmpOffload {
959+ type Output = BuiltOmpOffload ;
960+ const IS_HOST : bool = true ;
961+
962+ fn should_run ( run : ShouldRun < ' _ > ) -> ShouldRun < ' _ > {
963+ run. path ( "src/llvm-project/offload" )
964+ }
965+
966+ fn make_run ( run : RunConfig < ' _ > ) {
967+ run. builder . ensure ( OmpOffload { target : run. target } ) ;
968+ }
969+
970+ /// Compile OpenMP offload runtimes for `target`.
971+ #[ allow( unused) ]
972+ fn run ( self , builder : & Builder < ' _ > ) -> Self :: Output {
973+ if builder. config . dry_run ( ) {
974+ return BuiltOmpOffload {
975+ offload : vec ! [ builder. config. tempdir( ) . join( "llvm-offload-dry-run" ) ] ,
976+ } ;
977+ }
978+ let target = self . target ;
979+
980+ let LlvmResult { host_llvm_config, llvm_cmake_dir } =
981+ builder. ensure ( Llvm { target : self . target } ) ;
982+
983+ // Running cmake twice in the same folder is known to cause issues, like deleting existing
984+ // binaries. We therefore write our offload artifacts into it's own folder, instead of
985+ // using the llvm build dir.
986+ let out_dir = builder. offload_out ( target) ;
987+
988+ let mut files = vec ! [ ] ;
989+ let lib_ext = std:: env:: consts:: DLL_EXTENSION ;
990+ files. push ( out_dir. join ( "lib" ) . join ( "libLLVMOffload" ) . with_extension ( lib_ext) ) ;
991+ files. push ( out_dir. join ( "lib" ) . join ( "libomp" ) . with_extension ( lib_ext) ) ;
992+ files. push ( out_dir. join ( "lib" ) . join ( "libomptarget" ) . with_extension ( lib_ext) ) ;
993+
994+ // Offload/OpenMP are just subfolders of LLVM, so we can use the LLVM sha.
995+ static STAMP_HASH_MEMO : OnceLock < String > = OnceLock :: new ( ) ;
996+ let smart_stamp_hash = STAMP_HASH_MEMO . get_or_init ( || {
997+ generate_smart_stamp_hash (
998+ builder,
999+ & builder. config . src . join ( "src/llvm-project/offload" ) ,
1000+ builder. in_tree_llvm_info . sha ( ) . unwrap_or_default ( ) ,
1001+ )
1002+ } ) ;
1003+ let stamp = BuildStamp :: new ( & out_dir) . with_prefix ( "offload" ) . add_stamp ( smart_stamp_hash) ;
1004+
1005+ trace ! ( "checking build stamp to see if we need to rebuild offload/openmp artifacts" ) ;
1006+ if stamp. is_up_to_date ( ) {
1007+ trace ! ( ?out_dir, "offload/openmp build artifacts are up to date" ) ;
1008+ if stamp. stamp ( ) . is_empty ( ) {
1009+ builder. info (
1010+ "Could not determine the Offload submodule commit hash. \
1011+ Assuming that an Offload rebuild is not necessary.",
1012+ ) ;
1013+ builder. info ( & format ! (
1014+ "To force Offload/OpenMP to rebuild, remove the file `{}`" ,
1015+ stamp. path( ) . display( )
1016+ ) ) ;
1017+ }
1018+ return BuiltOmpOffload { offload : files } ;
1019+ }
1020+
1021+ trace ! ( ?target, "(re)building offload/openmp artifacts" ) ;
1022+ builder. info ( & format ! ( "Building OpenMP/Offload for {target}" ) ) ;
1023+ t ! ( stamp. remove( ) ) ;
1024+ let _time = helpers:: timeit ( builder) ;
1025+ t ! ( fs:: create_dir_all( & out_dir) ) ;
1026+
1027+ builder. config . update_submodule ( "src/llvm-project" ) ;
1028+ let mut cfg = cmake:: Config :: new ( builder. src . join ( "src/llvm-project/runtimes/" ) ) ;
1029+
1030+ // If we use an external clang as opposed to building our own llvm_clang, than that clang will
1031+ // come with it's own set of default include directories, which are based on a potentially older
1032+ // LLVM. This can cause issues, so we overwrite it to include headers based on our
1033+ // `src/llvm-project` submodule instead.
1034+ // FIXME(offload): With LLVM-22 we hopefully won't need an external clang anymore.
1035+ let mut cflags = CcFlags :: default ( ) ;
1036+ if !builder. config . llvm_clang {
1037+ let base = builder. llvm_out ( target) . join ( "include" ) ;
1038+ let inc_dir = base. display ( ) ;
1039+ cflags. push_all ( format ! ( " -I {inc_dir}" ) ) ;
1040+ }
1041+
1042+ configure_cmake ( builder, target, & mut cfg, true , LdFlags :: default ( ) , cflags, & [ ] ) ;
1043+
1044+ // Re-use the same flags as llvm to control the level of debug information
1045+ // generated for offload.
1046+ let profile = match ( builder. config . llvm_optimize , builder. config . llvm_release_debuginfo ) {
1047+ ( false , _) => "Debug" ,
1048+ ( true , false ) => "Release" ,
1049+ ( true , true ) => "RelWithDebInfo" ,
1050+ } ;
1051+ trace ! ( ?profile) ;
1052+
1053+ // OpenMP/Offload builds currently (LLVM-21) still depend on Clang, although there are
1054+ // intentions to loosen this requirement for LLVM-22. If we were to
1055+ let clang_dir = if !builder. config . llvm_clang {
1056+ // We must have an external clang to use.
1057+ assert ! ( & builder. build. config. llvm_clang_dir. is_some( ) ) ;
1058+ builder. build . config . llvm_clang_dir . clone ( )
1059+ } else {
1060+ // No need to specify it, since we use the in-tree clang
1061+ None
1062+ } ;
1063+
1064+ // FIXME(offload): Once we move from OMP to Offload (Ol) APIs, we should drop the openmp
1065+ // runtime to simplify our build. We should also re-evaluate the LLVM_Root and try to get
1066+ // rid of the Clang_DIR, once we upgrade to LLVM-22.
1067+ cfg. out_dir ( & out_dir)
1068+ . profile ( profile)
1069+ . env ( "LLVM_CONFIG_REAL" , & host_llvm_config)
1070+ . define ( "LLVM_ENABLE_ASSERTIONS" , "ON" )
1071+ . define ( "LLVM_ENABLE_RUNTIMES" , "openmp;offload" )
1072+ . define ( "LLVM_INCLUDE_TESTS" , "OFF" )
1073+ . define ( "OFFLOAD_INCLUDE_TESTS" , "OFF" )
1074+ . define ( "OPENMP_STANDALONE_BUILD" , "ON" )
1075+ . define ( "LLVM_ROOT" , builder. llvm_out ( target) . join ( "build" ) )
1076+ . define ( "LLVM_DIR" , llvm_cmake_dir) ;
1077+ if let Some ( p) = clang_dir {
1078+ cfg. define ( "Clang_DIR" , p) ;
1079+ }
1080+ cfg. build ( ) ;
1081+
1082+ t ! ( stamp. write( ) ) ;
1083+
1084+ for p in & files {
1085+ // At this point, `out_dir` should contain the built <offload-filename>.<dylib-ext>
1086+ // files.
1087+ if !p. exists ( ) {
1088+ eprintln ! (
1089+ "`{p:?}` not found in `{}`. Either the build has failed or Offload was built with a wrong version of LLVM" ,
1090+ out_dir. display( )
1091+ ) ;
1092+ exit ! ( 1 ) ;
1093+ }
1094+ }
1095+ BuiltOmpOffload { offload : files }
1096+ }
1097+ }
1098+
9201099#[ derive( Debug , Copy , Clone , Hash , PartialEq , Eq ) ]
9211100pub struct Enzyme {
9221101 pub target : TargetSelection ,
@@ -991,11 +1170,10 @@ impl Step for Enzyme {
9911170
9921171 builder. config . update_submodule ( "src/tools/enzyme" ) ;
9931172 let mut cfg = cmake:: Config :: new ( builder. src . join ( "src/tools/enzyme/enzyme/" ) ) ;
994-
995- let mut cflags = CcFlags :: default ( ) ;
9961173 // Enzyme devs maintain upstream compability, but only fix deprecations when they are about
9971174 // to turn into a hard error. As such, Enzyme generates various warnings which could make it
9981175 // hard to spot more relevant issues.
1176+ let mut cflags = CcFlags :: default ( ) ;
9991177 cflags. push_all ( "-Wno-deprecated" ) ;
10001178 configure_cmake ( builder, target, & mut cfg, true , LdFlags :: default ( ) , cflags, & [ ] ) ;
10011179
0 commit comments