diff --git a/compiler/rustc_llvm/llvm-wrapper/offload/CMakeLists.txt b/compiler/rustc_llvm/llvm-wrapper/offload/CMakeLists.txt index 37c747a902d87..8c78b41c5c578 100644 --- a/compiler/rustc_llvm/llvm-wrapper/offload/CMakeLists.txt +++ b/compiler/rustc_llvm/llvm-wrapper/offload/CMakeLists.txt @@ -1,7 +1,8 @@ cmake_minimum_required(VERSION 3.20) project(RustOffload LANGUAGES CXX) -find_package(LLVM CONFIG REQUIRED) +# If we don't prohibit the default path, CMake will find an incompatible system LLVM installation instead of the one we built. +find_package(LLVM CONFIG REQUIRED NO_DEFAULT_PATH PATHS "${LLVM_DIR}") add_library(RustOffload-${LLVM_VERSION_MAJOR} SHARED OffloadWrapper.cpp diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs index 323cce1da51ab..714a064f588db 100644 --- a/src/bootstrap/src/core/build_steps/llvm.rs +++ b/src/bootstrap/src/core/build_steps/llvm.rs @@ -1298,17 +1298,64 @@ impl CommandLineStep for OmpOffload { builder.config.update_submodule("src/llvm-project"); - // OpenMP/Offload builds currently (LLVM-22) still depend on Clang, although there are - // intentions to loosen this requirement over time. FIXME(offload): re-evaluate on LLVM 23 - let clang_dir = if !builder.config.llvm_clang { + let offload_clang_dir = if !builder.config.llvm_clang { // We must have an external clang to use. - assert!(&builder.build.config.llvm_clang_dir.is_some()); - builder.build.config.llvm_clang_dir.clone() + builder.build.config.offload_clang_dir.clone() } else { // No need to specify it, since we use the in-tree clang None }; + // We currently build libompdevice by accident. It includes bitcode for our amd/nvptx + // targets, and only the latest clang compiler can build those. We could stop building those + // to fix this requirement, but we plan on instead building libc-for-gpu very soon, which + // will have the same clang requirement, so we wouldn't save much. There are two ways in + // which we can find a suitable clang. Either a user enabled the llvm.clang, in which case + // we built our own clang based on the llvm submodule first, this always works. The + // alternative is that the user sets the offload_clang_dir path, in which case they hopefully point + // to a suitable clang, otherwise the build will fail. + let clang_bin_dir = if builder.config.llvm_clang { + llvm_output.host_llvm_config.parent().map(Path::to_path_buf) + } else { + // We expect the following (default) structure of the offload_clang_dir: + // /lib/cmake/clang, with a ClangConfig.cmake inside. + // The clang binary is located in /bin, so we go up three levels to find it. + // This hardcodes the ClangConfig.cmake logic, which isn't great, so we filter for the + // binary and error if we can't find it (presumably because LLVM build layout changed?). + offload_clang_dir + .as_deref() + .and_then(|dir| dir.ancestors().nth(3)) + .map(|prefix| prefix.join("bin")) + } + .filter(|dir| dir.join(exe("clang", target)).exists()); + + let Some(clang_bin_dir) = clang_bin_dir else { + eprintln!( + "Building Offload requires a clang binary. Please either set `llvm.offload-clang-dir` or enable `llvm.clang` to build it." + ); + helpers::exit_process(1); + }; + let clang = clang_bin_dir.join(exe("clang", target)); + let clangxx = clang_bin_dir.join(exe("clang++", target)); + + // This was encountered when using gcc 13 to build the llvm submodule on a server, where no + // clang was available. We first built clang along with llvm, and then switched over to use + // the newly built clang to build the offload runtimes. Since we switched compiler, we have + // to make sure that we're still using the same libstdc++ we used before. Without this + // change, clang picked up a system libstdc++ from a different gcc and failed. + let cxx_lib_dir = builder.cxx(target).ok().and_then(|cxx| { + let stdout = command(&cxx) + .arg("-print-file-name=libstdc++.so") + .cached() + .run_capture_stdout(builder) + .stdout(); + let libstdcxx = PathBuf::from(stdout.trim()); + if !libstdcxx.is_absolute() { + return None; + } + libstdcxx.parent().map(Path::to_path_buf) + }); + // In the context of OpenMP offload, some libraries must be compiled for the gpu target, // some for the host, and others for both. We do not perform a full cross-compilation, since // we don't want to run rustc on a GPU. @@ -1320,7 +1367,6 @@ impl CommandLineStep for OmpOffload { // come with it's own set of default include directories, which are based on a potentially older // LLVM. This can cause issues, so we overwrite it to include headers based on our // `src/llvm-project` submodule instead. - // FIXME(offload): With LLVM-22 we hopefully won't need an external clang anymore. let mut cflags = CcFlags::default(); if !builder.config.llvm_clang { let base = builder.llvm_out(target).join("include"); @@ -1335,9 +1381,18 @@ impl CommandLineStep for OmpOffload { if builder.config.llvm_thin_lto && !target.contains("apple") { ldflags.push_all("-fuse-ld=lld"); } + if *omp_target == *target.triple + && let Some(dir) = &cxx_lib_dir + { + ldflags.push_all(format!("-L{}", dir.display())); + } configure_cmake(builder, target, &mut cfg, true, ldflags, cflags, &[]); + cfg.define("CMAKE_C_COMPILER", &clang) + .define("CMAKE_CXX_COMPILER", &clangxx) + .define("CMAKE_ASM_COMPILER", &clang); + // Re-use the same flags as llvm to control the level of debug information // generated for offload. let profile = get_llvm_profile(&builder.config); @@ -1354,7 +1409,7 @@ impl CommandLineStep for OmpOffload { .define("LLVM_ROOT", builder.llvm_out(target).join("build")) .define("LLVM_DIR", llvm_output.cmake_dir()) .define("LLVM_DEFAULT_TARGET_TRIPLE", omp_target); - if let Some(p) = clang_dir.clone() { + if let Some(p) = offload_clang_dir.clone() { cfg.define("Clang_DIR", p); } diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 8ca1c74b929e3..215197314bdf5 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -172,7 +172,7 @@ pub(crate) struct Config { pub llvm_link_jobs: Option, pub llvm_version_suffix: Option, pub llvm_use_linker: Option, - pub llvm_clang_dir: Option, + pub offload_clang_dir: Option, pub llvm_allow_old_toolchain: bool, pub llvm_polly: bool, pub llvm_clang: bool, @@ -643,7 +643,7 @@ impl Config { use_linker: llvm_use_linker, allow_old_toolchain: llvm_allow_old_toolchain, offload: llvm_offload, - offload_clang_dir: llvm_clang_dir, + offload_clang_dir, polly: llvm_polly, clang: llvm_clang, enable_warnings: llvm_enable_warnings, @@ -1489,7 +1489,6 @@ NOTE: Please add `--stage 2` to your command line, or if you're sure you want to llvm_ci_mode, llvm_clang: llvm_clang.unwrap_or(false), llvm_clang_cl, - llvm_clang_dir: llvm_clang_dir.map(PathBuf::from), llvm_cxxflags, llvm_enable_warnings: llvm_enable_warnings.unwrap_or(false), llvm_enzyme: llvm_enzyme.unwrap_or(false), @@ -1522,6 +1521,7 @@ NOTE: Please add `--stage 2` to your command line, or if you're sure you want to musl_root: rust_musl_root.map(PathBuf::from), ninja_in_file: llvm_ninja.unwrap_or(true), nodejs: build_nodejs.map(PathBuf::from), + offload_clang_dir: offload_clang_dir.map(PathBuf::from), omit_git_hash, on_fail: flags_on_fail, optimized_compiler_builtins,