using namespace clang;
using namespace llvm::opt;
+// Look for sub-directory starts with PackageName under ROCm candidate path.
+// If there is one and only one matching sub-directory found, append the
+// sub-directory to Path. If there is no matching sub-directory or there are
+// more than one matching sub-directories, diagnose them. Returns the full
+// path of the package if there is only one matching sub-directory, otherwise
+// returns an empty string.
+llvm::SmallString<0>
+RocmInstallationDetector::findSPACKPackage(const Candidate &Cand,
+ StringRef PackageName) {
+ if (!Cand.isSPACK())
+ return {};
+ std::error_code EC;
+ std::string Prefix = Twine(PackageName + "-" + Cand.SPACKReleaseStr).str();
+ llvm::SmallVector<llvm::SmallString<0>> SubDirs;
+ for (llvm::vfs::directory_iterator File = D.getVFS().dir_begin(Cand.Path, EC),
+ FileEnd;
+ File != FileEnd && !EC; File.increment(EC)) {
+ llvm::StringRef FileName = llvm::sys::path::filename(File->path());
+ if (FileName.startswith(Prefix)) {
+ SubDirs.push_back(FileName);
+ if (SubDirs.size() > 1)
+ break;
+ }
+ }
+ if (SubDirs.size() == 1) {
+ auto PackagePath = Cand.Path;
+ llvm::sys::path::append(PackagePath, SubDirs[0]);
+ return PackagePath;
+ }
+ if (SubDirs.size() == 0) {
+ unsigned DiagID = D.getDiags().getCustomDiagID(
+ DiagnosticsEngine::Error,
+ "Expecting SPACK package %0 at %1 but not found");
+ D.Diag(DiagID) << Prefix << Cand.Path;
+ return {};
+ }
+
+ assert(SubDirs.size() > 1);
+ unsigned DiagID = D.getDiags().getCustomDiagID(
+ DiagnosticsEngine::Error,
+ "Expecting one SPACK package %0 at %1 but found more");
+ D.Diag(DiagID) << Prefix << Cand.Path;
+ return {};
+}
+
void RocmInstallationDetector::scanLibDevicePath(llvm::StringRef Path) {
assert(!Path.empty());
return false;
}
-// For candidate specified by --rocm-path we do not do strict check.
-SmallVector<RocmInstallationDetector::Candidate, 4>
+/// \returns a list of candidate directories for ROCm installation, which is
+/// cached and populated only once.
+const SmallVectorImpl<RocmInstallationDetector::Candidate> &
RocmInstallationDetector::getInstallationPathCandidates() {
- SmallVector<Candidate, 4> Candidates;
+
+ // Return the cached candidate list if it has already been populated.
+ if (!ROCmSearchDirs.empty())
+ return ROCmSearchDirs;
+
+ auto DoPrintROCmSearchDirs = [&]() {
+ if (PrintROCmSearchDirs)
+ for (auto Cand : ROCmSearchDirs) {
+ llvm::errs() << "ROCm installation search path";
+ if (Cand.isSPACK())
+ llvm::errs() << " (Spack " << Cand.SPACKReleaseStr << ")";
+ llvm::errs() << ": " << Cand.Path << '\n';
+ }
+ };
+
+ // For candidate specified by --rocm-path we do not do strict check, i.e.,
+ // checking existence of HIP version file and device library files.
if (!RocmPathArg.empty()) {
- Candidates.emplace_back(RocmPathArg.str());
- return Candidates;
+ ROCmSearchDirs.emplace_back(RocmPathArg.str());
+ DoPrintROCmSearchDirs();
+ return ROCmSearchDirs;
}
// Try to find relative to the compiler binary.
// Check both a normal Unix prefix position of the clang binary, as well as
// the Windows-esque layout the ROCm packages use with the host architecture
// subdirectory of bin.
+ auto DeduceROCmPath = [](StringRef ClangPath) {
+ // Strip off directory (usually bin)
+ StringRef ParentDir = llvm::sys::path::parent_path(ClangPath);
+ StringRef ParentName = llvm::sys::path::filename(ParentDir);
+
+ // Some builds use bin/{host arch}, so go up again.
+ if (ParentName == "bin") {
+ ParentDir = llvm::sys::path::parent_path(ParentDir);
+ ParentName = llvm::sys::path::filename(ParentDir);
+ }
- // Strip off directory (usually bin)
- StringRef ParentDir = llvm::sys::path::parent_path(InstallDir);
- StringRef ParentName = llvm::sys::path::filename(ParentDir);
+ // Detect ROCm packages built with SPACK.
+ // clang is installed at
+ // <rocm_root>/llvm-amdgpu-<rocm_release_string>-<hash>/bin directory.
+ // We only consider the parent directory of llvm-amdgpu package as ROCm
+ // installation candidate for SPACK.
+ if (ParentName.startswith("llvm-amdgpu-")) {
+ auto SPACKPostfix =
+ ParentName.drop_front(strlen("llvm-amdgpu-")).split('-');
+ auto SPACKReleaseStr = SPACKPostfix.first;
+ if (!SPACKReleaseStr.empty()) {
+ ParentDir = llvm::sys::path::parent_path(ParentDir);
+ return Candidate(ParentDir.str(), /*StrictChecking=*/true,
+ SPACKReleaseStr);
+ }
+ }
- // Some builds use bin/{host arch}, so go up again.
- if (ParentName == "bin") {
- ParentDir = llvm::sys::path::parent_path(ParentDir);
- ParentName = llvm::sys::path::filename(ParentDir);
- }
+ // Some versions of the rocm llvm package install to /opt/rocm/llvm/bin
+ // Some versions of the aomp package install to /opt/rocm/aomp/bin
+ if (ParentName == "llvm" || ParentName.startswith("aomp"))
+ ParentDir = llvm::sys::path::parent_path(ParentDir);
+
+ return Candidate(ParentDir.str(), /*StrictChecking=*/true);
+ };
- // Some versions of the rocm llvm package install to /opt/rocm/llvm/bin
- if (ParentName == "llvm")
- ParentDir = llvm::sys::path::parent_path(ParentDir);
+ // Deduce ROCm path by the path used to invoke clang. Do not resolve symbolic
+ // link of clang itself.
+ ROCmSearchDirs.emplace_back(DeduceROCmPath(InstallDir));
- Candidates.emplace_back(ParentDir.str(), /*StrictChecking=*/true);
+ // Deduce ROCm path by the real path of the invoked clang, resolving symbolic
+ // link of clang itself.
+ llvm::SmallString<256> RealClangPath;
+ llvm::sys::fs::real_path(D.getClangProgramPath(), RealClangPath);
+ auto ParentPath = llvm::sys::path::parent_path(RealClangPath);
+ if (ParentPath != InstallDir)
+ ROCmSearchDirs.emplace_back(DeduceROCmPath(ParentPath));
// Device library may be installed in clang resource directory.
- Candidates.emplace_back(D.ResourceDir, /*StrictChecking=*/true);
+ ROCmSearchDirs.emplace_back(D.ResourceDir,
+ /*StrictChecking=*/true);
- Candidates.emplace_back(D.SysRoot + "/opt/rocm", /*StrictChecking=*/true);
- return Candidates;
+ ROCmSearchDirs.emplace_back(D.SysRoot + "/opt/rocm",
+ /*StrictChecking=*/true);
+ DoPrintROCmSearchDirs();
+ return ROCmSearchDirs;
}
RocmInstallationDetector::RocmInstallationDetector(
const llvm::opt::ArgList &Args, bool DetectHIPRuntime, bool DetectDeviceLib)
: D(D) {
RocmPathArg = Args.getLastArgValue(clang::driver::options::OPT_rocm_path_EQ);
+ PrintROCmSearchDirs =
+ Args.hasArg(clang::driver::options::OPT_print_rocm_search_dirs);
RocmDeviceLibPathArg =
Args.getAllArgValues(clang::driver::options::OPT_rocm_device_lib_path_EQ);
+ HIPPathArg = Args.getLastArgValue(clang::driver::options::OPT_hip_path_EQ);
if (auto *A = Args.getLastArg(clang::driver::options::OPT_hip_version_EQ)) {
HIPVersionArg = A->getValue();
unsigned Major = 0;
// exist for each frontend project, and differ depending on which build
// system produced the packages. Standalone OpenCL builds also have a
// different directory structure from the ROCm OpenCL package.
- auto Candidates = getInstallationPathCandidates();
- for (const auto &Candidate : Candidates) {
+ auto &ROCmDirs = getInstallationPathCandidates();
+ for (const auto &Candidate : ROCmDirs) {
auto CandidatePath = Candidate.Path;
// Check device library exists at the given path.
// Make a path by appending sub-directories to InstallPath.
auto MakePath = [&](const llvm::ArrayRef<const char *> &SubDirs) {
- auto Path = CandidatePath;
+ // Device library built by SPACK is installed to
+ // <rocm_root>/rocm-device-libs-<rocm_release_string>-<hash> directory.
+ auto SPACKPath = findSPACKPackage(Candidate, "rocm-device-libs");
+ auto Path = SPACKPath.empty() ? CandidatePath : SPACKPath;
for (auto SubDir : SubDirs)
llvm::sys::path::append(Path, SubDir);
return Path;
}
void RocmInstallationDetector::detectHIPRuntime() {
- auto Candidates = getInstallationPathCandidates();
+ SmallVector<Candidate, 4> HIPSearchDirs;
+ if (!HIPPathArg.empty())
+ HIPSearchDirs.emplace_back(HIPPathArg.str(), /*StrictChecking=*/true);
+ else
+ HIPSearchDirs.append(getInstallationPathCandidates());
auto &FS = D.getVFS();
- for (const auto &Candidate : Candidates) {
+ for (const auto &Candidate : HIPSearchDirs) {
InstallPath = Candidate.Path;
if (InstallPath.empty() || !FS.exists(InstallPath))
continue;
+ // HIP runtime built by SPACK is installed to
+ // <rocm_root>/hip-<rocm_release_string>-<hash> directory.
+ auto SPACKPath = findSPACKPackage(Candidate, "hip");
+ InstallPath = SPACKPath.empty() ? InstallPath : SPACKPath;
BinPath = InstallPath;
llvm::sys::path::append(BinPath, "bin");
struct Candidate {
llvm::SmallString<0> Path;
bool StrictChecking;
-
- Candidate(std::string Path, bool StrictChecking = false)
- : Path(Path), StrictChecking(StrictChecking) {}
+ // Release string for ROCm packages built with SPACK if not empty. The
+ // installation directories of ROCm packages built with SPACK follow the
+ // convention <package_name>-<rocm_release_string>-<hash>.
+ std::string SPACKReleaseStr;
+
+ bool isSPACK() const { return !SPACKReleaseStr.empty(); }
+ Candidate(std::string Path, bool StrictChecking = false,
+ StringRef SPACKReleaseStr = {})
+ : Path(Path), StrictChecking(StrictChecking),
+ SPACKReleaseStr(SPACKReleaseStr.str()) {}
};
const Driver &D;
StringRef RocmPathArg;
// ROCm device library paths specified by --rocm-device-lib-path.
std::vector<std::string> RocmDeviceLibPathArg;
+ // HIP runtime path specified by --hip-path.
+ StringRef HIPPathArg;
// HIP version specified by --hip-version.
StringRef HIPVersionArg;
// Wheter -nogpulib is specified.
ConditionalLibrary DenormalsAreZero;
ConditionalLibrary CorrectlyRoundedSqrt;
+ // Cache ROCm installation search paths.
+ SmallVector<Candidate, 4> ROCmSearchDirs;
+ bool PrintROCmSearchDirs;
+
bool allGenericLibsValid() const {
return !OCML.empty() && !OCKL.empty() && !OpenCL.empty() && !HIP.empty() &&
WavefrontSize64.isValid() && FiniteOnly.isValid() &&
void scanLibDevicePath(llvm::StringRef Path);
bool parseHIPVersionFile(llvm::StringRef V);
- SmallVector<Candidate, 4> getInstallationPathCandidates();
+ const SmallVectorImpl<Candidate> &getInstallationPathCandidates();
+
+ /// Find the path to a SPACK package under the ROCm candidate installation
+ /// directory if the candidate is a SPACK ROCm candidate. \returns empty
+ /// string if the candidate is not SPACK ROCm candidate or the requested
+ /// package is not found.
+ llvm::SmallString<0> findSPACKPackage(const Candidate &Cand,
+ StringRef PackageName);
public:
RocmInstallationDetector(const Driver &D, const llvm::Triple &HostTriple,
// REQUIRES: clang-driver
// REQUIRES: x86-registered-target
// REQUIRES: amdgpu-registered-target
+// UNSUPPORTED: system-windows
// Make sure the appropriate device specific library is available.
// RUN: --rocm-path=%S/Inputs/rocm %s 2>&1 \
// RUN: | FileCheck -check-prefixes=COMMON,GFX902-DEFAULTLIBS %s
-
// RUN: %clang -### -v -target x86_64-linux-gnu --cuda-gpu-arch=gfx902 -nogpulib \
// RUN: --rocm-path=%S/Inputs/rocm %s 2>&1 \
// RUN: | FileCheck -check-prefixes=COMMON,NODEFAULTLIBS %s
+// Test ROCm installation built by SPACK by invoke clang at %T/rocm-spack/llvm-amdgpu-*
+// directory through a soft link.
+
+// RUN: rm -rf %T/rocm-spack
+// RUN: cp -r %S/Inputs/rocm-spack %T
+// RUN: ln -fs %clang %T/rocm-spack/llvm-amdgpu-4.0.0-ieagcs7inf7runpyfvepqkurasoglq4z/bin/clang
+// RUN: %T/rocm-spack/llvm-amdgpu-4.0.0-ieagcs7inf7runpyfvepqkurasoglq4z/bin/clang -### -v \
+// RUN: -target x86_64-linux-gnu --cuda-gpu-arch=gfx900 --print-rocm-search-dirs %s 2>&1 \
+// RUN: | FileCheck -check-prefixes=SPACK %s
+
+// Test SPACK installation with multiple hip and rocm-device-libs packages of the same
+// ROCm release. Clang cannot determine which one to use and emit diagnostics. --hip-path
+// and --rocm-device-lib-path can be used to specify them.
+
+// RUN: cp -r %T/rocm-spack/hip-* %T/rocm-spack/hip-4.0.0-abcd
+// RUN: cp -r %T/rocm-spack/rocm-device-libs-* %T/rocm-spack/rocm-device-libs-4.0.0-efgh
+// RUN: %T/rocm-spack/llvm-amdgpu-4.0.0-ieagcs7inf7runpyfvepqkurasoglq4z/bin/clang -### -v \
+// RUN: -target x86_64-linux-gnu --cuda-gpu-arch=gfx900 %s 2>&1 \
+// RUN: | FileCheck -check-prefixes=SPACK-MULT %s
+// RUN: %T/rocm-spack/llvm-amdgpu-4.0.0-ieagcs7inf7runpyfvepqkurasoglq4z/bin/clang -### -v \
+// RUN: -target x86_64-linux-gnu --cuda-gpu-arch=gfx900 \
+// RUN: --hip-path=%T/rocm-spack/hip-4.0.0-abcd \
+// RUN: --rocm-device-lib-path=%T/rocm-spack/rocm-device-libs-4.0.0-efgh/amdgcn/bitcode \
+// RUN: %s 2>&1 | FileCheck -check-prefixes=SPACK-SET %s
+
+// Test invalid SPACK ROCm installation missing hip and rocm-device-libs packages.
+
+// RUN: rm -rf %T/rocm-spack/hip-*
+// RUN: rm -rf %T/rocm-spack/rocm-device-libs-*
+// RUN: %T/rocm-spack/llvm-amdgpu-4.0.0-ieagcs7inf7runpyfvepqkurasoglq4z/bin/clang -### -v \
+// RUN: -target x86_64-linux-gnu --cuda-gpu-arch=gfx900 %s 2>&1 \
+// RUN: | FileCheck -check-prefixes=SPACK-MISS %s
// GFX902-DEFAULTLIBS: error: cannot find ROCm device library for gfx902. Provide its path via --rocm-path or --rocm-device-lib-path, or pass -nogpulib to build without ROCm device library
// NODEFAULTLIBS-NOT: error: cannot find
// COMMON: "-triple" "amdgcn-amd-amdhsa"
+
+// SPACK: ROCm installation search path (Spack 4.0.0): [[DIR:.*]]
+// SPACK: ROCm installation search path: [[CLANG:.*]]
+// SPACK: ROCm installation search path: [[CLANG]]/lib/clang/{{[0-9.]+}}
+// SPACK: ROCm installation search path: /opt/rocm
+// SPACK: InstalledDir: [[DIR]]/llvm-amdgpu-4.0.0-ieagcs7inf7runpyfvepqkurasoglq4z/bin
+// SPACK: Found HIP installation: [[DIR]]/hip-4.0.0-5f63slrursbrvfe2txrrjkynbsywsob5, version 4.0.20214-a2917cd
+// SPACK: "-triple" "amdgcn-amd-amdhsa"
+// SPACK-SAME: "-mlink-builtin-bitcode" "[[DIR]]/rocm-device-libs-4.0.0-6wnyzz4hgl3hr7uswasnagt7j2adctbs/amdgcn/bitcode/hip.bc"
+// SPACK-SAME: "-internal-isystem" "[[DIR]]/hip-4.0.0-5f63slrursbrvfe2txrrjkynbsywsob5/include"
+
+// SPACK-MULT: InstalledDir: [[DIR:.*]]/llvm-amdgpu-4.0.0-ieagcs7inf7runpyfvepqkurasoglq4z/bin
+// SPACK-MULT-DAG: Expecting one SPACK package hip-4.0.0 at [[DIR]] but found more
+// SPACK-MULT-DAG: Expecting one SPACK package rocm-device-libs-4.0.0 at [[DIR]] but found more
+// SPACK-MULT-NOT: Found HIP installation: [[DIR]]/hip-4.0.0-5f63slrursbrvfe2txrrjkynbsywsob5, version 4.0.20214-a2917cd
+// SPACK-MULT-NOT: "-mlink-builtin-bitcode" "[[DIR]]/rocm-device-libs-4.0.0-6wnyzz4hgl3hr7uswasnagt7j2adctbs/amdgcn/bitcode/hip.bc"
+// SPACK-MULT-NOT: "-internal-isystem" "[[DIR]]/hip-4.0.0-5f63slrursbrvfe2txrrjkynbsywsob5/include"
+
+// SPACK-SET: InstalledDir: [[DIR:.*]]/llvm-amdgpu-4.0.0-ieagcs7inf7runpyfvepqkurasoglq4z/bin
+// SPACK-SET: Found HIP installation: [[DIR]]/hip-4.0.0-abcd, version 4.0.20214-a2917cd
+// SPACK-SET: "-triple" "amdgcn-amd-amdhsa"
+// SPACK-SET-SAME: "-mlink-builtin-bitcode" "[[DIR]]/rocm-device-libs-4.0.0-efgh/amdgcn/bitcode/hip.bc"
+// SPACK-SET-SAME: "-internal-isystem" "[[DIR]]/hip-4.0.0-abcd/include"
+
+// SPACK-MISS: InstalledDir: [[DIR:.*]]/llvm-amdgpu-4.0.0-ieagcs7inf7runpyfvepqkurasoglq4z/bin
+// SPACK-MISS-DAG: Expecting SPACK package hip-4.0.0 at [[DIR]] but not found
+// SPACK-MISS-DAG: Expecting SPACK package rocm-device-libs-4.0.0 at [[DIR]] but not found
+// SPACK-MISS-NOT: Found HIP installation: [[DIR]]/hip-4.0.0-5f63slrursbrvfe2txrrjkynbsywsob5, version 4.0.20214-a2917cd
+// SPACK-MISS-NOT: "-mlink-builtin-bitcode" "[[DIR]]/rocm-device-libs-4.0.0-6wnyzz4hgl3hr7uswasnagt7j2adctbs/amdgcn/bitcode/hip.bc"
+// SPACK-MISS-NOT: "-internal-isystem" "[[DIR]]/hip-4.0.0-5f63slrursbrvfe2txrrjkynbsywsob5/include"