@@ -454,16 +454,6 @@ impl Step for Llvm {
454454 enabled_llvm_runtimes. push ( "compiler-rt" ) ;
455455 }
456456
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-
467457 if !enabled_llvm_projects. is_empty ( ) {
468458 enabled_llvm_projects. sort ( ) ;
469459 enabled_llvm_projects. dedup ( ) ;
@@ -778,6 +768,17 @@ fn configure_cmake(
778768 . collect :: < Vec < String > > ( )
779769 . join ( " " )
780770 . into ( ) ;
771+
772+ // If we use an external clang as opposed to building our own llvm_clang, than that clang will
773+ // come with it's own set of default include directories, which are based on a potentially older
774+ // LLVM. This can cause issues, so we overwrite it to include headers based on our
775+ // `src/llvm-project` submodule instead.
776+ // FIXME(offload): With LLVM-22 we hopefully won't need an external clang anymore.
777+ let base = builder. llvm_out ( target) . join ( "include" ) ;
778+ let inc_dir = base. display ( ) ;
779+ if builder. config . llvm_offload && !builder. config . llvm_clang {
780+ cflags. push ( format ! ( " -I {inc_dir}" ) ) ;
781+ }
781782 if let Some ( ref s) = builder. config . llvm_cflags {
782783 cflags. push ( " " ) ;
783784 cflags. push ( s) ;
@@ -789,6 +790,7 @@ fn configure_cmake(
789790 cflags. push ( format ! ( " --target={target}" ) ) ;
790791 }
791792 cfg. define ( "CMAKE_C_FLAGS" , cflags) ;
793+
792794 let mut cxxflags: OsString = builder
793795 . cc_handled_clags ( target, CLang :: Cxx )
794796 . into_iter ( )
@@ -801,6 +803,9 @@ fn configure_cmake(
801803 . collect :: < Vec < String > > ( )
802804 . join ( " " )
803805 . into ( ) ;
806+ if builder. config . llvm_offload && !builder. config . llvm_clang {
807+ cxxflags. push ( format ! ( " -I {inc_dir}" ) ) ;
808+ }
804809 if let Some ( ref s) = builder. config . llvm_cxxflags {
805810 cxxflags. push ( " " ) ;
806811 cxxflags. push ( s) ;
@@ -896,6 +901,134 @@ fn get_var(var_base: &str, host: &str, target: &str) -> Option<OsString> {
896901 . or_else ( || env:: var_os ( var_base) )
897902}
898903
904+ // FIXME(offload): In an ideal world, we would just enable the offload runtime in our previous LLVM
905+ // build step. For now, we still depend on the openmp runtime since we use some of it's API, so we
906+ // build both. However, when building those runtimes as part of the LLVM step, then LLVM's cmake
907+ // implicitly assumes that Clang has also been build and will try to use it. In the Rust CI, we
908+ // don't always build clang (due to compile times), but instead use a slightly older external clang.
909+ // LLVM tries to remove this build dependency of offload/openmp on Clang for LLVM-22, so in the
910+ // future we might be able to integrate this step into the LLVM step. For now, we instead introduce
911+ // a Clang_DIR bootstrap option, which allows us tell CMake to use an external clang for these two
912+ // runtimes. This external clang will try to use it's own (older) include dirs when building our
913+ // in-tree LLVM submodule, which will cause build failures. To prevent those, we now also
914+ // explicitly set our include dirs.
915+ #[ derive( Debug , Copy , Clone , Hash , PartialEq , Eq ) ]
916+ pub struct OmpOffload {
917+ pub target : TargetSelection ,
918+ }
919+
920+ impl Step for OmpOffload {
921+ type Output = PathBuf ;
922+ const IS_HOST : bool = true ;
923+
924+ fn should_run ( run : ShouldRun < ' _ > ) -> ShouldRun < ' _ > {
925+ run. path ( "src/llvm-project/offload" )
926+ }
927+
928+ fn make_run ( run : RunConfig < ' _ > ) {
929+ run. builder . ensure ( OmpOffload { target : run. target } ) ;
930+ }
931+
932+ /// Compile OpenMP offload runtimes for `target`.
933+ #[ allow( unused) ]
934+ fn run ( self , builder : & Builder < ' _ > ) -> PathBuf {
935+ if builder. config . dry_run ( ) {
936+ return builder. config . tempdir ( ) . join ( "llvm-offload-dry-run" ) ;
937+ }
938+ let target = self . target ;
939+
940+ let LlvmResult { host_llvm_config, .. } = builder. ensure ( Llvm { target : self . target } ) ;
941+
942+ // Running cmake twice in the same folder is known to cause issues, like deleting existing
943+ // binaries. We therefore write our offload artifacts into it's own subfolder. We use a
944+ // subfolder, so that all the logic that processes our build artifacts (hopefully) also
945+ // automatically manages our artifacts in the subfolder.
946+ let out_dir = builder. llvm_out ( target) . join ( "offload-outdir" ) ;
947+ if std:: fs:: exists ( & out_dir) . is_ok_and ( |x| x == false ) {
948+ std:: fs:: DirBuilder :: new ( ) . create ( & out_dir) . unwrap ( ) ;
949+ dbg ! ( "Created out subdir!" ) ;
950+ }
951+
952+ // Offload/OpenMP are just subfolders of LLVM, so we can use the LLVM sha.
953+ static STAMP_HASH_MEMO : OnceLock < String > = OnceLock :: new ( ) ;
954+ let smart_stamp_hash = STAMP_HASH_MEMO . get_or_init ( || {
955+ generate_smart_stamp_hash (
956+ builder,
957+ & builder. config . src . join ( "src/llvm-project/offload" ) ,
958+ builder. in_tree_llvm_info . sha ( ) . unwrap_or_default ( ) ,
959+ )
960+ } ) ;
961+ let stamp = BuildStamp :: new ( & out_dir) . with_prefix ( "offload" ) . add_stamp ( smart_stamp_hash) ;
962+
963+ trace ! ( "checking build stamp to see if we need to rebuild offload/openmp artifacts" ) ;
964+ if stamp. is_up_to_date ( ) {
965+ trace ! ( ?out_dir, "offload/openmp build artifacts are up to date" ) ;
966+ if stamp. stamp ( ) . is_empty ( ) {
967+ builder. info (
968+ "Could not determine the Offload submodule commit hash. \
969+ Assuming that an Offload rebuild is not necessary.",
970+ ) ;
971+ builder. info ( & format ! (
972+ "To force Offload/OpenMP to rebuild, remove the file `{}`" ,
973+ stamp. path( ) . display( )
974+ ) ) ;
975+ }
976+ return out_dir;
977+ }
978+
979+ trace ! ( ?target, "(re)building offload/openmp artifacts" ) ;
980+ builder. info ( & format ! ( "Building OpenMP/Offload for {target}" ) ) ;
981+ t ! ( stamp. remove( ) ) ;
982+ let _time = helpers:: timeit ( builder) ;
983+ t ! ( fs:: create_dir_all( & out_dir) ) ;
984+
985+ builder. config . update_submodule ( "src/llvm-project" ) ;
986+ let mut cfg = cmake:: Config :: new ( builder. src . join ( "src/llvm-project/runtimes/" ) ) ;
987+ configure_cmake ( builder, target, & mut cfg, true , LdFlags :: default ( ) , & [ ] ) ;
988+
989+ // Re-use the same flags as llvm to control the level of debug information
990+ // generated for offload.
991+ let profile = match ( builder. config . llvm_optimize , builder. config . llvm_release_debuginfo ) {
992+ ( false , _) => "Debug" ,
993+ ( true , false ) => "Release" ,
994+ ( true , true ) => "RelWithDebInfo" ,
995+ } ;
996+ trace ! ( ?profile) ;
997+
998+ // OpenMP/Offload builds currently (LLVM-21) still depend on Clang, although there are
999+ // intentions to loosen this requirement for LLVM-22. If we were to
1000+ let clang_dir = if !builder. config . llvm_clang {
1001+ // We must have an external clang to use.
1002+ assert ! ( & builder. build. config. llvm_clang_dir. is_some( ) ) ;
1003+ builder. build . config . llvm_clang_dir . clone ( )
1004+ } else {
1005+ // No need to specify it, since we use the in-tree clang
1006+ None
1007+ } ;
1008+
1009+ // FIXME(offload): Once we move from OMP to Offload (Ol) APIs, we should drop the openmp
1010+ // runtime to simplify our build. We should also re-evaluate the LLVM_Root and try to get
1011+ // rid of the Clang_DIR, once we upgrade to LLVM-22.
1012+ cfg. out_dir ( & out_dir)
1013+ . profile ( profile)
1014+ . env ( "LLVM_CONFIG_REAL" , & host_llvm_config)
1015+ . define ( "LLVM_ENABLE_ASSERTIONS" , "ON" )
1016+ . define ( "LLVM_ENABLE_RUNTIMES" , "openmp;offload" )
1017+ . define ( "LLVM_INCLUDE_TESTS" , "OFF" )
1018+ . define ( "OFFLOAD_INCLUDE_TESTS" , "OFF" )
1019+ . define ( "OPENMP_STANDALONE_BUILD" , "ON" )
1020+ . define ( "LLVM_ROOT" , builder. llvm_out ( target) . join ( "build" ) )
1021+ . define ( "LLVM_DIR" , builder. llvm_out ( target) . join ( "lib" ) . join ( "cmake" ) . join ( "llvm" ) ) ;
1022+ if let Some ( p) = clang_dir {
1023+ cfg. define ( "Clang_DIR" , p) ;
1024+ }
1025+ cfg. build ( ) ;
1026+
1027+ t ! ( stamp. write( ) ) ;
1028+ out_dir
1029+ }
1030+ }
1031+
8991032#[ derive( Debug , Copy , Clone , Hash , PartialEq , Eq ) ]
9001033pub struct Enzyme {
9011034 pub target : TargetSelection ,
0 commit comments