@@ -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 } ;
@@ -74,6 +75,25 @@ struct LdFlags {
7475 module : OsString ,
7576}
7677
78+ /// Allows each step to add C/Cxx flags which are only used for a specific cmake invocation.
79+ #[ derive( Debug , Clone , Default ) ]
80+ struct CcFlags {
81+ /// Additional values for CMAKE_CC_FLAGS, to be added before all other values.
82+ cflags : OsString ,
83+ /// Additional values for CMAKE_CXX_FLAGS, to be added before all other values.
84+ cxxflags : OsString ,
85+ }
86+
87+ impl CcFlags {
88+ fn push_all ( & mut self , s : impl AsRef < OsStr > ) {
89+ let s = s. as_ref ( ) ;
90+ self . cflags . push ( " " ) ;
91+ self . cflags . push ( s) ;
92+ self . cxxflags . push ( " " ) ;
93+ self . cxxflags . push ( s) ;
94+ }
95+ }
96+
7797impl LdFlags {
7898 fn push_all ( & mut self , s : impl AsRef < OsStr > ) {
7999 let s = s. as_ref ( ) ;
@@ -454,16 +474,6 @@ impl Step for Llvm {
454474 enabled_llvm_runtimes. push ( "compiler-rt" ) ;
455475 }
456476
457- // This is an experimental flag, which likely builds more than necessary.
458- // We will optimize it when we get closer to releasing it on nightly.
459- if builder. config . llvm_offload {
460- enabled_llvm_runtimes. push ( "offload" ) ;
461- //FIXME(ZuseZ4): LLVM intends to drop the offload dependency on openmp.
462- //Remove this line once they achieved it.
463- enabled_llvm_runtimes. push ( "openmp" ) ;
464- enabled_llvm_projects. push ( "compiler-rt" ) ;
465- }
466-
467477 if !enabled_llvm_projects. is_empty ( ) {
468478 enabled_llvm_projects. sort ( ) ;
469479 enabled_llvm_projects. dedup ( ) ;
@@ -527,7 +537,7 @@ impl Step for Llvm {
527537 cfg. define ( "LLVM_VERSION_SUFFIX" , suffix) ;
528538 }
529539
530- configure_cmake ( builder, target, & mut cfg, true , ldflags, & [ ] ) ;
540+ configure_cmake ( builder, target, & mut cfg, true , ldflags, CcFlags :: default ( ) , & [ ] ) ;
531541 configure_llvm ( builder, target, & mut cfg) ;
532542
533543 for ( key, val) in & builder. config . llvm_build_config {
@@ -633,6 +643,7 @@ fn configure_cmake(
633643 cfg : & mut cmake:: Config ,
634644 use_compiler_launcher : bool ,
635645 mut ldflags : LdFlags ,
646+ ccflags : CcFlags ,
636647 suppressed_compiler_flag_prefixes : & [ & str ] ,
637648) {
638649 // Do not print installation messages for up-to-date files.
@@ -778,6 +789,11 @@ fn configure_cmake(
778789 . collect :: < Vec < String > > ( )
779790 . join ( " " )
780791 . into ( ) ;
792+ let mut tmp = ccflags. cflags . clone ( ) ;
793+ tmp. push ( " " ) ;
794+ tmp. push ( cflags) ;
795+ cflags = tmp;
796+
781797 if let Some ( ref s) = builder. config . llvm_cflags {
782798 cflags. push ( " " ) ;
783799 cflags. push ( s) ;
@@ -789,6 +805,7 @@ fn configure_cmake(
789805 cflags. push ( format ! ( " --target={target}" ) ) ;
790806 }
791807 cfg. define ( "CMAKE_C_FLAGS" , cflags) ;
808+
792809 let mut cxxflags: OsString = builder
793810 . cc_handled_clags ( target, CLang :: Cxx )
794811 . into_iter ( )
@@ -801,6 +818,10 @@ fn configure_cmake(
801818 . collect :: < Vec < String > > ( )
802819 . join ( " " )
803820 . into ( ) ;
821+ let mut tmp = ccflags. cxxflags . clone ( ) ;
822+ tmp. push ( " " ) ;
823+ tmp. push ( cxxflags) ;
824+ cxxflags = tmp;
804825 if let Some ( ref s) = builder. config . llvm_cxxflags {
805826 cxxflags. push ( " " ) ;
806827 cxxflags. push ( s) ;
@@ -896,6 +917,175 @@ fn get_var(var_base: &str, host: &str, target: &str) -> Option<OsString> {
896917 . or_else ( || env:: var_os ( var_base) )
897918}
898919
920+ #[ derive( Clone ) ]
921+ pub struct BuiltOmpOffload {
922+ /// Path to the omp and offload dylibs.
923+ offload : Vec < PathBuf > ,
924+ }
925+
926+ impl BuiltOmpOffload {
927+ pub fn offload_paths ( & self ) -> Vec < PathBuf > {
928+ self . offload . clone ( )
929+ }
930+ }
931+
932+ // FIXME(offload): In an ideal world, we would just enable the offload runtime in our previous LLVM
933+ // build step. For now, we still depend on the openmp runtime since we use some of it's API, so we
934+ // build both. However, when building those runtimes as part of the LLVM step, then LLVM's cmake
935+ // implicitly assumes that Clang has also been build and will try to use it. In the Rust CI, we
936+ // don't always build clang (due to compile times), but instead use a slightly older external clang.
937+ // LLVM tries to remove this build dependency of offload/openmp on Clang for LLVM-22, so in the
938+ // future we might be able to integrate this step into the LLVM step. For now, we instead introduce
939+ // a Clang_DIR bootstrap option, which allows us tell CMake to use an external clang for these two
940+ // runtimes. This external clang will try to use it's own (older) include dirs when building our
941+ // in-tree LLVM submodule, which will cause build failures. To prevent those, we now also
942+ // explicitly set our include dirs.
943+ #[ derive( Debug , Copy , Clone , Hash , PartialEq , Eq ) ]
944+ pub struct OmpOffload {
945+ pub target : TargetSelection ,
946+ }
947+
948+ impl Step for OmpOffload {
949+ type Output = BuiltOmpOffload ;
950+ const IS_HOST : bool = true ;
951+
952+ fn should_run ( run : ShouldRun < ' _ > ) -> ShouldRun < ' _ > {
953+ run. path ( "src/llvm-project/offload" )
954+ }
955+
956+ fn make_run ( run : RunConfig < ' _ > ) {
957+ run. builder . ensure ( OmpOffload { target : run. target } ) ;
958+ }
959+
960+ /// Compile OpenMP offload runtimes for `target`.
961+ #[ allow( unused) ]
962+ fn run ( self , builder : & Builder < ' _ > ) -> Self :: Output {
963+ if builder. config . dry_run ( ) {
964+ return BuiltOmpOffload {
965+ offload : vec ! [ builder. config. tempdir( ) . join( "llvm-offload-dry-run" ) ] ,
966+ } ;
967+ }
968+ let target = self . target ;
969+
970+ let LlvmResult { host_llvm_config, llvm_cmake_dir } =
971+ builder. ensure ( Llvm { target : self . target } ) ;
972+
973+ // Running cmake twice in the same folder is known to cause issues, like deleting existing
974+ // binaries. We therefore write our offload artifacts into it's own folder, instead of
975+ // using the llvm build dir.
976+ let out_dir = builder. offload_out ( target) ;
977+
978+ let mut files = vec ! [ ] ;
979+ let lib_ext = std:: env:: consts:: DLL_EXTENSION ;
980+ files. push ( out_dir. join ( "lib" ) . join ( "libLLVMOffload" ) . with_extension ( lib_ext) ) ;
981+ files. push ( out_dir. join ( "lib" ) . join ( "libomp" ) . with_extension ( lib_ext) ) ;
982+ files. push ( out_dir. join ( "lib" ) . join ( "libomptarget" ) . with_extension ( lib_ext) ) ;
983+
984+ // Offload/OpenMP are just subfolders of LLVM, so we can use the LLVM sha.
985+ static STAMP_HASH_MEMO : OnceLock < String > = OnceLock :: new ( ) ;
986+ let smart_stamp_hash = STAMP_HASH_MEMO . get_or_init ( || {
987+ generate_smart_stamp_hash (
988+ builder,
989+ & builder. config . src . join ( "src/llvm-project/offload" ) ,
990+ builder. in_tree_llvm_info . sha ( ) . unwrap_or_default ( ) ,
991+ )
992+ } ) ;
993+ let stamp = BuildStamp :: new ( & out_dir) . with_prefix ( "offload" ) . add_stamp ( smart_stamp_hash) ;
994+
995+ trace ! ( "checking build stamp to see if we need to rebuild offload/openmp artifacts" ) ;
996+ if stamp. is_up_to_date ( ) {
997+ trace ! ( ?out_dir, "offload/openmp build artifacts are up to date" ) ;
998+ if stamp. stamp ( ) . is_empty ( ) {
999+ builder. info (
1000+ "Could not determine the Offload submodule commit hash. \
1001+ Assuming that an Offload rebuild is not necessary.",
1002+ ) ;
1003+ builder. info ( & format ! (
1004+ "To force Offload/OpenMP to rebuild, remove the file `{}`" ,
1005+ stamp. path( ) . display( )
1006+ ) ) ;
1007+ }
1008+ return BuiltOmpOffload { offload : files } ;
1009+ }
1010+
1011+ trace ! ( ?target, "(re)building offload/openmp artifacts" ) ;
1012+ builder. info ( & format ! ( "Building OpenMP/Offload for {target}" ) ) ;
1013+ t ! ( stamp. remove( ) ) ;
1014+ let _time = helpers:: timeit ( builder) ;
1015+ t ! ( fs:: create_dir_all( & out_dir) ) ;
1016+
1017+ builder. config . update_submodule ( "src/llvm-project" ) ;
1018+ let mut cfg = cmake:: Config :: new ( builder. src . join ( "src/llvm-project/runtimes/" ) ) ;
1019+
1020+ // If we use an external clang as opposed to building our own llvm_clang, than that clang will
1021+ // come with it's own set of default include directories, which are based on a potentially older
1022+ // LLVM. This can cause issues, so we overwrite it to include headers based on our
1023+ // `src/llvm-project` submodule instead.
1024+ // FIXME(offload): With LLVM-22 we hopefully won't need an external clang anymore.
1025+ let mut cflags = CcFlags :: default ( ) ;
1026+ if !builder. config . llvm_clang {
1027+ let base = builder. llvm_out ( target) . join ( "include" ) ;
1028+ let inc_dir = base. display ( ) ;
1029+ cflags. push_all ( format ! ( " -I {inc_dir}" ) ) ;
1030+ }
1031+
1032+ configure_cmake ( builder, target, & mut cfg, true , LdFlags :: default ( ) , cflags, & [ ] ) ;
1033+
1034+ // Re-use the same flags as llvm to control the level of debug information
1035+ // generated for offload.
1036+ let profile = match ( builder. config . llvm_optimize , builder. config . llvm_release_debuginfo ) {
1037+ ( false , _) => "Debug" ,
1038+ ( true , false ) => "Release" ,
1039+ ( true , true ) => "RelWithDebInfo" ,
1040+ } ;
1041+ trace ! ( ?profile) ;
1042+
1043+ // OpenMP/Offload builds currently (LLVM-21) still depend on Clang, although there are
1044+ // intentions to loosen this requirement for LLVM-22. If we were to
1045+ let clang_dir = if !builder. config . llvm_clang {
1046+ // We must have an external clang to use.
1047+ assert ! ( & builder. build. config. llvm_clang_dir. is_some( ) ) ;
1048+ builder. build . config . llvm_clang_dir . clone ( )
1049+ } else {
1050+ // No need to specify it, since we use the in-tree clang
1051+ None
1052+ } ;
1053+
1054+ // FIXME(offload): Once we move from OMP to Offload (Ol) APIs, we should drop the openmp
1055+ // runtime to simplify our build. We should also re-evaluate the LLVM_Root and try to get
1056+ // rid of the Clang_DIR, once we upgrade to LLVM-22.
1057+ cfg. out_dir ( & out_dir)
1058+ . profile ( profile)
1059+ . env ( "LLVM_CONFIG_REAL" , & host_llvm_config)
1060+ . define ( "LLVM_ENABLE_ASSERTIONS" , "ON" )
1061+ . define ( "LLVM_ENABLE_RUNTIMES" , "openmp;offload" )
1062+ . define ( "LLVM_INCLUDE_TESTS" , "OFF" )
1063+ . define ( "OFFLOAD_INCLUDE_TESTS" , "OFF" )
1064+ . define ( "OPENMP_STANDALONE_BUILD" , "ON" )
1065+ . define ( "LLVM_ROOT" , builder. llvm_out ( target) . join ( "build" ) )
1066+ . define ( "LLVM_DIR" , llvm_cmake_dir) ;
1067+ if let Some ( p) = clang_dir {
1068+ cfg. define ( "Clang_DIR" , p) ;
1069+ }
1070+ cfg. build ( ) ;
1071+
1072+ t ! ( stamp. write( ) ) ;
1073+
1074+ for p in & files {
1075+ // At this point, `out_dir` should contain the built <offload-filename>.<dylib-ext>
1076+ // files.
1077+ if !p. exists ( ) {
1078+ eprintln ! (
1079+ "`{p:?}` not found in `{}`. Either the build has failed or Offload was built with a wrong version of LLVM" ,
1080+ out_dir. display( )
1081+ ) ;
1082+ exit ! ( 1 ) ;
1083+ }
1084+ }
1085+ BuiltOmpOffload { offload : files }
1086+ }
1087+ }
1088+
8991089#[ derive( Debug , Copy , Clone , Hash , PartialEq , Eq ) ]
9001090pub struct Enzyme {
9011091 pub target : TargetSelection ,
@@ -970,7 +1160,9 @@ impl Step for Enzyme {
9701160
9711161 builder. config . update_submodule ( "src/tools/enzyme" ) ;
9721162 let mut cfg = cmake:: Config :: new ( builder. src . join ( "src/tools/enzyme/enzyme/" ) ) ;
973- configure_cmake ( builder, target, & mut cfg, true , LdFlags :: default ( ) , & [ ] ) ;
1163+ let mut cflags = CcFlags :: default ( ) ;
1164+ cflags. push_all ( "-Wno-deprecated" ) ;
1165+ configure_cmake ( builder, target, & mut cfg, true , LdFlags :: default ( ) , cflags, & [ ] ) ;
9741166
9751167 // Re-use the same flags as llvm to control the level of debug information
9761168 // generated by Enzyme.
@@ -1090,7 +1282,7 @@ impl Step for Lld {
10901282 ldflags. push_all ( "-Wl,-rpath,'$ORIGIN/../../../'" ) ;
10911283 }
10921284
1093- configure_cmake ( builder, target, & mut cfg, true , ldflags, & [ ] ) ;
1285+ configure_cmake ( builder, target, & mut cfg, true , ldflags, CcFlags :: default ( ) , & [ ] ) ;
10941286 configure_llvm ( builder, target, & mut cfg) ;
10951287
10961288 // Re-use the same flags as llvm to control the level of debug information
@@ -1213,6 +1405,7 @@ impl Step for Sanitizers {
12131405 & mut cfg,
12141406 use_compiler_launcher,
12151407 LdFlags :: default ( ) ,
1408+ CcFlags :: default ( ) ,
12161409 suppressed_compiler_flag_prefixes,
12171410 ) ;
12181411
0 commit comments