Index: clang/include/clang/Driver/Options.td =================================================================== --- clang/include/clang/Driver/Options.td +++ clang/include/clang/Driver/Options.td @@ -901,6 +901,8 @@ [], TargetOpts<"NVPTXUseShortPointers">>; def rocm_path_EQ : Joined<["--"], "rocm-path=">, Group, HelpText<"ROCm installation path, used for finding and automatically linking required bitcode libraries.">; +def hip_path_EQ : Joined<["--"], "hip-path=">, Group, + HelpText<"HIP runtime installation path, used for finding HIP version and adding HIP include path.">; def rocm_device_lib_path_EQ : Joined<["--"], "rocm-device-lib-path=">, Group, HelpText<"ROCm device library path. Alternative to rocm-path.">; def : Joined<["--"], "hip-device-lib-path=">, Alias; Index: clang/lib/Driver/ToolChains/AMDGPU.cpp =================================================================== --- clang/lib/Driver/ToolChains/AMDGPU.cpp +++ clang/lib/Driver/ToolChains/AMDGPU.cpp @@ -22,6 +22,48 @@ using namespace clang; using namespace llvm::opt; +// Look for sub-directory starts with Prefix under 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. +static llvm::SmallString<0> findSPACKPackage(const Driver &D, + const llvm::SmallString<0> &Path, + StringRef Prefix) { + std::error_code EC; + llvm::SmallVector> SubDirs; + for (llvm::vfs::directory_iterator File = D.getVFS().dir_begin(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 = 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 << 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 << Path; + return {}; +} + void RocmInstallationDetector::scanLibDevicePath(llvm::StringRef Path) { assert(!Path.empty()); @@ -117,12 +159,12 @@ } // For candidate specified by --rocm-path we do not do strict check. -SmallVector +RocmInstallationDetector::CandidatesInfo RocmInstallationDetector::getInstallationPathCandidates() { - SmallVector Candidates; + CandidatesInfo CanInfo; if (!RocmPathArg.empty()) { - Candidates.emplace_back(RocmPathArg.str()); - return Candidates; + CanInfo.Candidates.emplace_back(RocmPathArg.str()); + return CanInfo; } // Try to find relative to the compiler binary. @@ -146,17 +188,34 @@ if (ParentName == "llvm") ParentDir = llvm::sys::path::parent_path(ParentDir); - Candidates.emplace_back(ParentDir.str(), /*StrictChecking=*/true); + // Detect ROCm packages built with SPACK. + // clang is installed at + // /llvm-amdgpu--/bin directory. + // We only consider the parent directory of llvm-amdgpu package as ROCm + // installation candidate for SPACK. + if (ParentName.startswith("llvm-amdgpu-")) { + auto SPACKReleaseStr = + ParentName.drop_front(strlen("llvm-amdgpu-")).split('-').first; + if (!SPACKReleaseStr.empty()) { + ParentDir = llvm::sys::path::parent_path(ParentDir); + CanInfo.SPACKReleaseStr = SPACKReleaseStr.str(); + CanInfo.Candidates.emplace_back(ParentDir.str(), /*StrictChecking=*/true); + return CanInfo; + } + } + + CanInfo.Candidates.emplace_back(ParentDir.str(), /*StrictChecking=*/true); // Device library may be installed in clang resource directory. - Candidates.emplace_back(D.ResourceDir, /*StrictChecking=*/true); + CanInfo.Candidates.emplace_back(D.ResourceDir, /*StrictChecking=*/true); - Candidates.emplace_back(D.SysRoot + "/opt/rocm", /*StrictChecking=*/true); + CanInfo.Candidates.emplace_back(D.SysRoot + "/opt/rocm", + /*StrictChecking=*/true); if (ParentName.startswith("aomp")) { // Some versions of the aomp package install to /opt/rocm/aomp/bin - Candidates.emplace_back(ParentDir.str()); + CanInfo.Candidates.emplace_back(ParentDir.str()); } - return Candidates; + return CanInfo; } RocmInstallationDetector::RocmInstallationDetector( @@ -166,6 +225,7 @@ RocmPathArg = Args.getLastArgValue(clang::driver::options::OPT_rocm_path_EQ); 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; @@ -228,8 +288,8 @@ // 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 CanInfo = getInstallationPathCandidates(); + for (const auto &Candidate : CanInfo.Candidates) { auto CandidatePath = Candidate.Path; // Check device library exists at the given path. @@ -267,6 +327,15 @@ // Make a path by appending sub-directories to InstallPath. auto MakePath = [&](const llvm::ArrayRef &SubDirs) { auto Path = CandidatePath; + // Device library built by SPACK is installed to + // /rocm-device-libs-- directory. + if (!CanInfo.SPACKReleaseStr.empty()) { + auto SPACKPath = findSPACKPackage( + D, Path, + llvm::Twine("rocm-device-libs-" + CanInfo.SPACKReleaseStr).str()); + if (!SPACKPath.empty()) + Path = SPACKPath; + } for (auto SubDir : SubDirs) llvm::sys::path::append(Path, SubDir); return Path; @@ -282,13 +351,25 @@ } void RocmInstallationDetector::detectHIPRuntime() { - auto Candidates = getInstallationPathCandidates(); + CandidatesInfo CanInfo; + if (!HIPPathArg.empty()) + CanInfo.Candidates.emplace_back(HIPPathArg.str(), /*StrictChecking=*/true); + else + CanInfo = getInstallationPathCandidates(); auto &FS = D.getVFS(); - for (const auto &Candidate : Candidates) { + for (const auto &Candidate : CanInfo.Candidates) { InstallPath = Candidate.Path; if (InstallPath.empty() || !FS.exists(InstallPath)) continue; + // HIP runtime built by SPACK is installed to + // /hip-- directory. + if (!CanInfo.SPACKReleaseStr.empty()) { + auto SPACKPath = findSPACKPackage( + D, InstallPath, llvm::Twine("hip-" + CanInfo.SPACKReleaseStr).str()); + if (!SPACKPath.empty()) + InstallPath = SPACKPath; + } BinPath = InstallPath; llvm::sys::path::append(BinPath, "bin"); Index: clang/lib/Driver/ToolChains/ROCm.h =================================================================== --- clang/lib/Driver/ToolChains/ROCm.h +++ clang/lib/Driver/ToolChains/ROCm.h @@ -47,6 +47,15 @@ : Path(Path), StrictChecking(StrictChecking) {} }; + struct CandidatesInfo { + // Candidates for ROCm root directory. + SmallVector Candidates; + // Release string for ROCm packages built with SPACK if not empty. The + // installation directories of ROCm packages built with SPACK follow the + // convention --. + std::string SPACKReleaseStr; + }; + const Driver &D; bool HasHIPRuntime = false; bool HasDeviceLibrary = false; @@ -67,6 +76,8 @@ StringRef RocmPathArg; // ROCm device library paths specified by --rocm-device-lib-path. std::vector RocmDeviceLibPathArg; + // HIP runtime path specified by --hip-path. + StringRef HIPPathArg; // HIP version specified by --hip-version. StringRef HIPVersionArg; // Wheter -nogpulib is specified. @@ -107,7 +118,7 @@ void scanLibDevicePath(llvm::StringRef Path); bool parseHIPVersionFile(llvm::StringRef V); - SmallVector getInstallationPathCandidates(); + CandidatesInfo getInstallationPathCandidates(); public: RocmInstallationDetector(const Driver &D, const llvm::Triple &HostTriple, Index: clang/test/Driver/Inputs/rocm-spack/hip-4.0.0-5f63slrursbrvfe2txrrjkynbsywsob5/bin/.hipVersion =================================================================== --- /dev/null +++ clang/test/Driver/Inputs/rocm-spack/hip-4.0.0-5f63slrursbrvfe2txrrjkynbsywsob5/bin/.hipVersion @@ -0,0 +1,5 @@ +# NOTE: The trailing whitespace is added on purpose to verify that these +# whitespaces are trimmed before paring. +HIP_VERSION_MAJOR=4 +HIP_VERSION_MINOR=0 +HIP_VERSION_PATCH=20214-a2917cd Index: clang/test/Driver/rocm-detect.hip =================================================================== --- clang/test/Driver/rocm-detect.hip +++ clang/test/Driver/rocm-detect.hip @@ -16,14 +16,71 @@ // 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 %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: 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"