10b57cec5SDimitry Andric //===--- AMDGPU.cpp - AMDGPU ToolChain Implementations ----------*- C++ -*-===// 20b57cec5SDimitry Andric // 30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60b57cec5SDimitry Andric // 70b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 80b57cec5SDimitry Andric 90b57cec5SDimitry Andric #include "AMDGPU.h" 100b57cec5SDimitry Andric #include "CommonArgs.h" 11e8d8bef9SDimitry Andric #include "clang/Basic/TargetID.h" 12bdd1243dSDimitry Andric #include "clang/Config/config.h" 130b57cec5SDimitry Andric #include "clang/Driver/Compilation.h" 140b57cec5SDimitry Andric #include "clang/Driver/DriverDiagnostic.h" 15fe6060f1SDimitry Andric #include "clang/Driver/InputInfo.h" 16fe6060f1SDimitry Andric #include "clang/Driver/Options.h" 17*0fca6ea1SDimitry Andric #include "clang/Driver/SanitizerArgs.h" 1806c3fb27SDimitry Andric #include "llvm/ADT/StringExtras.h" 190b57cec5SDimitry Andric #include "llvm/Option/ArgList.h" 20fe6060f1SDimitry Andric #include "llvm/Support/Error.h" 21fe6060f1SDimitry Andric #include "llvm/Support/LineIterator.h" 225ffd83dbSDimitry Andric #include "llvm/Support/Path.h" 23bdd1243dSDimitry Andric #include "llvm/Support/Process.h" 245ffd83dbSDimitry Andric #include "llvm/Support/VirtualFileSystem.h" 2506c3fb27SDimitry Andric #include "llvm/TargetParser/Host.h" 26bdd1243dSDimitry Andric #include <optional> 27fe6060f1SDimitry Andric #include <system_error> 28fe6060f1SDimitry Andric 290b57cec5SDimitry Andric using namespace clang::driver; 300b57cec5SDimitry Andric using namespace clang::driver::tools; 310b57cec5SDimitry Andric using namespace clang::driver::toolchains; 320b57cec5SDimitry Andric using namespace clang; 330b57cec5SDimitry Andric using namespace llvm::opt; 340b57cec5SDimitry Andric 35fe6060f1SDimitry Andric // Look for sub-directory starts with PackageName under ROCm candidate path. 36fe6060f1SDimitry Andric // If there is one and only one matching sub-directory found, append the 37fe6060f1SDimitry Andric // sub-directory to Path. If there is no matching sub-directory or there are 38fe6060f1SDimitry Andric // more than one matching sub-directories, diagnose them. Returns the full 39fe6060f1SDimitry Andric // path of the package if there is only one matching sub-directory, otherwise 40fe6060f1SDimitry Andric // returns an empty string. 41fe6060f1SDimitry Andric llvm::SmallString<0> 42fe6060f1SDimitry Andric RocmInstallationDetector::findSPACKPackage(const Candidate &Cand, 43fe6060f1SDimitry Andric StringRef PackageName) { 44fe6060f1SDimitry Andric if (!Cand.isSPACK()) 45fe6060f1SDimitry Andric return {}; 46fe6060f1SDimitry Andric std::error_code EC; 47fe6060f1SDimitry Andric std::string Prefix = Twine(PackageName + "-" + Cand.SPACKReleaseStr).str(); 48fe6060f1SDimitry Andric llvm::SmallVector<llvm::SmallString<0>> SubDirs; 49fe6060f1SDimitry Andric for (llvm::vfs::directory_iterator File = D.getVFS().dir_begin(Cand.Path, EC), 50fe6060f1SDimitry Andric FileEnd; 51fe6060f1SDimitry Andric File != FileEnd && !EC; File.increment(EC)) { 52fe6060f1SDimitry Andric llvm::StringRef FileName = llvm::sys::path::filename(File->path()); 535f757f3fSDimitry Andric if (FileName.starts_with(Prefix)) { 54fe6060f1SDimitry Andric SubDirs.push_back(FileName); 55fe6060f1SDimitry Andric if (SubDirs.size() > 1) 56fe6060f1SDimitry Andric break; 57fe6060f1SDimitry Andric } 58fe6060f1SDimitry Andric } 59fe6060f1SDimitry Andric if (SubDirs.size() == 1) { 60fe6060f1SDimitry Andric auto PackagePath = Cand.Path; 61fe6060f1SDimitry Andric llvm::sys::path::append(PackagePath, SubDirs[0]); 62fe6060f1SDimitry Andric return PackagePath; 63fe6060f1SDimitry Andric } 64fe6060f1SDimitry Andric if (SubDirs.size() == 0 && Verbose) { 65fe6060f1SDimitry Andric llvm::errs() << "SPACK package " << Prefix << " not found at " << Cand.Path 66fe6060f1SDimitry Andric << '\n'; 67fe6060f1SDimitry Andric return {}; 68fe6060f1SDimitry Andric } 69fe6060f1SDimitry Andric 70fe6060f1SDimitry Andric if (SubDirs.size() > 1 && Verbose) { 71fe6060f1SDimitry Andric llvm::errs() << "Cannot use SPACK package " << Prefix << " at " << Cand.Path 72fe6060f1SDimitry Andric << " due to multiple installations for the same version\n"; 73fe6060f1SDimitry Andric } 74fe6060f1SDimitry Andric return {}; 75fe6060f1SDimitry Andric } 76fe6060f1SDimitry Andric 775ffd83dbSDimitry Andric void RocmInstallationDetector::scanLibDevicePath(llvm::StringRef Path) { 785ffd83dbSDimitry Andric assert(!Path.empty()); 795ffd83dbSDimitry Andric 805ffd83dbSDimitry Andric const StringRef Suffix(".bc"); 815ffd83dbSDimitry Andric const StringRef Suffix2(".amdgcn.bc"); 825ffd83dbSDimitry Andric 835ffd83dbSDimitry Andric std::error_code EC; 845ffd83dbSDimitry Andric for (llvm::vfs::directory_iterator LI = D.getVFS().dir_begin(Path, EC), LE; 855ffd83dbSDimitry Andric !EC && LI != LE; LI = LI.increment(EC)) { 865ffd83dbSDimitry Andric StringRef FilePath = LI->path(); 875ffd83dbSDimitry Andric StringRef FileName = llvm::sys::path::filename(FilePath); 885f757f3fSDimitry Andric if (!FileName.ends_with(Suffix)) 895ffd83dbSDimitry Andric continue; 905ffd83dbSDimitry Andric 915ffd83dbSDimitry Andric StringRef BaseName; 925f757f3fSDimitry Andric if (FileName.ends_with(Suffix2)) 935ffd83dbSDimitry Andric BaseName = FileName.drop_back(Suffix2.size()); 945f757f3fSDimitry Andric else if (FileName.ends_with(Suffix)) 955ffd83dbSDimitry Andric BaseName = FileName.drop_back(Suffix.size()); 965ffd83dbSDimitry Andric 9781ad6265SDimitry Andric const StringRef ABIVersionPrefix = "oclc_abi_version_"; 985ffd83dbSDimitry Andric if (BaseName == "ocml") { 995ffd83dbSDimitry Andric OCML = FilePath; 1005ffd83dbSDimitry Andric } else if (BaseName == "ockl") { 1015ffd83dbSDimitry Andric OCKL = FilePath; 1025ffd83dbSDimitry Andric } else if (BaseName == "opencl") { 1035ffd83dbSDimitry Andric OpenCL = FilePath; 1045ffd83dbSDimitry Andric } else if (BaseName == "hip") { 1055ffd83dbSDimitry Andric HIP = FilePath; 106fe6060f1SDimitry Andric } else if (BaseName == "asanrtl") { 107fe6060f1SDimitry Andric AsanRTL = FilePath; 1085ffd83dbSDimitry Andric } else if (BaseName == "oclc_finite_only_off") { 1095ffd83dbSDimitry Andric FiniteOnly.Off = FilePath; 1105ffd83dbSDimitry Andric } else if (BaseName == "oclc_finite_only_on") { 1115ffd83dbSDimitry Andric FiniteOnly.On = FilePath; 1125ffd83dbSDimitry Andric } else if (BaseName == "oclc_daz_opt_on") { 1135ffd83dbSDimitry Andric DenormalsAreZero.On = FilePath; 1145ffd83dbSDimitry Andric } else if (BaseName == "oclc_daz_opt_off") { 1155ffd83dbSDimitry Andric DenormalsAreZero.Off = FilePath; 1165ffd83dbSDimitry Andric } else if (BaseName == "oclc_correctly_rounded_sqrt_on") { 1175ffd83dbSDimitry Andric CorrectlyRoundedSqrt.On = FilePath; 1185ffd83dbSDimitry Andric } else if (BaseName == "oclc_correctly_rounded_sqrt_off") { 1195ffd83dbSDimitry Andric CorrectlyRoundedSqrt.Off = FilePath; 1205ffd83dbSDimitry Andric } else if (BaseName == "oclc_unsafe_math_on") { 1215ffd83dbSDimitry Andric UnsafeMath.On = FilePath; 1225ffd83dbSDimitry Andric } else if (BaseName == "oclc_unsafe_math_off") { 1235ffd83dbSDimitry Andric UnsafeMath.Off = FilePath; 1245ffd83dbSDimitry Andric } else if (BaseName == "oclc_wavefrontsize64_on") { 1255ffd83dbSDimitry Andric WavefrontSize64.On = FilePath; 1265ffd83dbSDimitry Andric } else if (BaseName == "oclc_wavefrontsize64_off") { 1275ffd83dbSDimitry Andric WavefrontSize64.Off = FilePath; 1285f757f3fSDimitry Andric } else if (BaseName.starts_with(ABIVersionPrefix)) { 12981ad6265SDimitry Andric unsigned ABIVersionNumber; 13081ad6265SDimitry Andric if (BaseName.drop_front(ABIVersionPrefix.size()) 13181ad6265SDimitry Andric .getAsInteger(/*Redex=*/0, ABIVersionNumber)) 13281ad6265SDimitry Andric continue; 13381ad6265SDimitry Andric ABIVersionMap[ABIVersionNumber] = FilePath.str(); 1345ffd83dbSDimitry Andric } else { 1355ffd83dbSDimitry Andric // Process all bitcode filenames that look like 1365ffd83dbSDimitry Andric // ocl_isa_version_XXX.amdgcn.bc 1375ffd83dbSDimitry Andric const StringRef DeviceLibPrefix = "oclc_isa_version_"; 1385f757f3fSDimitry Andric if (!BaseName.starts_with(DeviceLibPrefix)) 1395ffd83dbSDimitry Andric continue; 1405ffd83dbSDimitry Andric 1415ffd83dbSDimitry Andric StringRef IsaVersionNumber = 1425ffd83dbSDimitry Andric BaseName.drop_front(DeviceLibPrefix.size()); 1435ffd83dbSDimitry Andric 1445ffd83dbSDimitry Andric llvm::Twine GfxName = Twine("gfx") + IsaVersionNumber; 1455ffd83dbSDimitry Andric SmallString<8> Tmp; 1465ffd83dbSDimitry Andric LibDeviceMap.insert( 1475ffd83dbSDimitry Andric std::make_pair(GfxName.toStringRef(Tmp), FilePath.str())); 1485ffd83dbSDimitry Andric } 1495ffd83dbSDimitry Andric } 1505ffd83dbSDimitry Andric } 1515ffd83dbSDimitry Andric 152e8d8bef9SDimitry Andric // Parse and extract version numbers from `.hipVersion`. Return `true` if 153e8d8bef9SDimitry Andric // the parsing fails. 154e8d8bef9SDimitry Andric bool RocmInstallationDetector::parseHIPVersionFile(llvm::StringRef V) { 1555ffd83dbSDimitry Andric SmallVector<StringRef, 4> VersionParts; 1565ffd83dbSDimitry Andric V.split(VersionParts, '\n'); 157e8d8bef9SDimitry Andric unsigned Major = ~0U; 158e8d8bef9SDimitry Andric unsigned Minor = ~0U; 1595ffd83dbSDimitry Andric for (auto Part : VersionParts) { 160e8d8bef9SDimitry Andric auto Splits = Part.rtrim().split('='); 161e8d8bef9SDimitry Andric if (Splits.first == "HIP_VERSION_MAJOR") { 162e8d8bef9SDimitry Andric if (Splits.second.getAsInteger(0, Major)) 163e8d8bef9SDimitry Andric return true; 164e8d8bef9SDimitry Andric } else if (Splits.first == "HIP_VERSION_MINOR") { 165e8d8bef9SDimitry Andric if (Splits.second.getAsInteger(0, Minor)) 166e8d8bef9SDimitry Andric return true; 167e8d8bef9SDimitry Andric } else if (Splits.first == "HIP_VERSION_PATCH") 1685ffd83dbSDimitry Andric VersionPatch = Splits.second.str(); 1695ffd83dbSDimitry Andric } 170e8d8bef9SDimitry Andric if (Major == ~0U || Minor == ~0U) 171e8d8bef9SDimitry Andric return true; 1725ffd83dbSDimitry Andric VersionMajorMinor = llvm::VersionTuple(Major, Minor); 1735ffd83dbSDimitry Andric DetectedVersion = 1745ffd83dbSDimitry Andric (Twine(Major) + "." + Twine(Minor) + "." + VersionPatch).str(); 175e8d8bef9SDimitry Andric return false; 1765ffd83dbSDimitry Andric } 1775ffd83dbSDimitry Andric 178fe6060f1SDimitry Andric /// \returns a list of candidate directories for ROCm installation, which is 179fe6060f1SDimitry Andric /// cached and populated only once. 180fe6060f1SDimitry Andric const SmallVectorImpl<RocmInstallationDetector::Candidate> & 1815ffd83dbSDimitry Andric RocmInstallationDetector::getInstallationPathCandidates() { 182fe6060f1SDimitry Andric 183fe6060f1SDimitry Andric // Return the cached candidate list if it has already been populated. 184fe6060f1SDimitry Andric if (!ROCmSearchDirs.empty()) 185fe6060f1SDimitry Andric return ROCmSearchDirs; 186fe6060f1SDimitry Andric 187fe6060f1SDimitry Andric auto DoPrintROCmSearchDirs = [&]() { 188fe6060f1SDimitry Andric if (PrintROCmSearchDirs) 189fe6060f1SDimitry Andric for (auto Cand : ROCmSearchDirs) { 190fe6060f1SDimitry Andric llvm::errs() << "ROCm installation search path"; 191fe6060f1SDimitry Andric if (Cand.isSPACK()) 192fe6060f1SDimitry Andric llvm::errs() << " (Spack " << Cand.SPACKReleaseStr << ")"; 193fe6060f1SDimitry Andric llvm::errs() << ": " << Cand.Path << '\n'; 194fe6060f1SDimitry Andric } 195fe6060f1SDimitry Andric }; 196fe6060f1SDimitry Andric 197fe6060f1SDimitry Andric // For candidate specified by --rocm-path we do not do strict check, i.e., 198fe6060f1SDimitry Andric // checking existence of HIP version file and device library files. 1995ffd83dbSDimitry Andric if (!RocmPathArg.empty()) { 200fe6060f1SDimitry Andric ROCmSearchDirs.emplace_back(RocmPathArg.str()); 201fe6060f1SDimitry Andric DoPrintROCmSearchDirs(); 202fe6060f1SDimitry Andric return ROCmSearchDirs; 203bdd1243dSDimitry Andric } else if (std::optional<std::string> RocmPathEnv = 204bdd1243dSDimitry Andric llvm::sys::Process::GetEnv("ROCM_PATH")) { 205bdd1243dSDimitry Andric if (!RocmPathEnv->empty()) { 206bdd1243dSDimitry Andric ROCmSearchDirs.emplace_back(std::move(*RocmPathEnv)); 207fe6060f1SDimitry Andric DoPrintROCmSearchDirs(); 208fe6060f1SDimitry Andric return ROCmSearchDirs; 209fe6060f1SDimitry Andric } 2105ffd83dbSDimitry Andric } 2115ffd83dbSDimitry Andric 2125ffd83dbSDimitry Andric // Try to find relative to the compiler binary. 213*0fca6ea1SDimitry Andric StringRef InstallDir = D.Dir; 2145ffd83dbSDimitry Andric 2155ffd83dbSDimitry Andric // Check both a normal Unix prefix position of the clang binary, as well as 2165ffd83dbSDimitry Andric // the Windows-esque layout the ROCm packages use with the host architecture 2175ffd83dbSDimitry Andric // subdirectory of bin. 218fe6060f1SDimitry Andric auto DeduceROCmPath = [](StringRef ClangPath) { 2195ffd83dbSDimitry Andric // Strip off directory (usually bin) 220fe6060f1SDimitry Andric StringRef ParentDir = llvm::sys::path::parent_path(ClangPath); 2215ffd83dbSDimitry Andric StringRef ParentName = llvm::sys::path::filename(ParentDir); 2225ffd83dbSDimitry Andric 2235ffd83dbSDimitry Andric // Some builds use bin/{host arch}, so go up again. 2245ffd83dbSDimitry Andric if (ParentName == "bin") { 2255ffd83dbSDimitry Andric ParentDir = llvm::sys::path::parent_path(ParentDir); 2265ffd83dbSDimitry Andric ParentName = llvm::sys::path::filename(ParentDir); 2275ffd83dbSDimitry Andric } 2285ffd83dbSDimitry Andric 229fe6060f1SDimitry Andric // Detect ROCm packages built with SPACK. 230fe6060f1SDimitry Andric // clang is installed at 231fe6060f1SDimitry Andric // <rocm_root>/llvm-amdgpu-<rocm_release_string>-<hash>/bin directory. 232fe6060f1SDimitry Andric // We only consider the parent directory of llvm-amdgpu package as ROCm 233fe6060f1SDimitry Andric // installation candidate for SPACK. 2345f757f3fSDimitry Andric if (ParentName.starts_with("llvm-amdgpu-")) { 235fe6060f1SDimitry Andric auto SPACKPostfix = 236fe6060f1SDimitry Andric ParentName.drop_front(strlen("llvm-amdgpu-")).split('-'); 237fe6060f1SDimitry Andric auto SPACKReleaseStr = SPACKPostfix.first; 238fe6060f1SDimitry Andric if (!SPACKReleaseStr.empty()) { 239fe6060f1SDimitry Andric ParentDir = llvm::sys::path::parent_path(ParentDir); 240fe6060f1SDimitry Andric return Candidate(ParentDir.str(), /*StrictChecking=*/true, 241fe6060f1SDimitry Andric SPACKReleaseStr); 242fe6060f1SDimitry Andric } 243fe6060f1SDimitry Andric } 244fe6060f1SDimitry Andric 2455ffd83dbSDimitry Andric // Some versions of the rocm llvm package install to /opt/rocm/llvm/bin 246fe6060f1SDimitry Andric // Some versions of the aomp package install to /opt/rocm/aomp/bin 2475f757f3fSDimitry Andric if (ParentName == "llvm" || ParentName.starts_with("aomp")) 2485ffd83dbSDimitry Andric ParentDir = llvm::sys::path::parent_path(ParentDir); 2495ffd83dbSDimitry Andric 250fe6060f1SDimitry Andric return Candidate(ParentDir.str(), /*StrictChecking=*/true); 251fe6060f1SDimitry Andric }; 2525ffd83dbSDimitry Andric 253fe6060f1SDimitry Andric // Deduce ROCm path by the path used to invoke clang. Do not resolve symbolic 254fe6060f1SDimitry Andric // link of clang itself. 255fe6060f1SDimitry Andric ROCmSearchDirs.emplace_back(DeduceROCmPath(InstallDir)); 2565ffd83dbSDimitry Andric 257fe6060f1SDimitry Andric // Deduce ROCm path by the real path of the invoked clang, resolving symbolic 258fe6060f1SDimitry Andric // link of clang itself. 259fe6060f1SDimitry Andric llvm::SmallString<256> RealClangPath; 260fe6060f1SDimitry Andric llvm::sys::fs::real_path(D.getClangProgramPath(), RealClangPath); 261fe6060f1SDimitry Andric auto ParentPath = llvm::sys::path::parent_path(RealClangPath); 262fe6060f1SDimitry Andric if (ParentPath != InstallDir) 263fe6060f1SDimitry Andric ROCmSearchDirs.emplace_back(DeduceROCmPath(ParentPath)); 264fe6060f1SDimitry Andric 265fe6060f1SDimitry Andric // Device library may be installed in clang or resource directory. 266fe6060f1SDimitry Andric auto ClangRoot = llvm::sys::path::parent_path(InstallDir); 267fe6060f1SDimitry Andric auto RealClangRoot = llvm::sys::path::parent_path(ParentPath); 268fe6060f1SDimitry Andric ROCmSearchDirs.emplace_back(ClangRoot.str(), /*StrictChecking=*/true); 269fe6060f1SDimitry Andric if (RealClangRoot != ClangRoot) 270fe6060f1SDimitry Andric ROCmSearchDirs.emplace_back(RealClangRoot.str(), /*StrictChecking=*/true); 271fe6060f1SDimitry Andric ROCmSearchDirs.emplace_back(D.ResourceDir, 272fe6060f1SDimitry Andric /*StrictChecking=*/true); 273fe6060f1SDimitry Andric 274fe6060f1SDimitry Andric ROCmSearchDirs.emplace_back(D.SysRoot + "/opt/rocm", 275fe6060f1SDimitry Andric /*StrictChecking=*/true); 276fe6060f1SDimitry Andric 277fe6060f1SDimitry Andric // Find the latest /opt/rocm-{release} directory. 278fe6060f1SDimitry Andric std::error_code EC; 279fe6060f1SDimitry Andric std::string LatestROCm; 280fe6060f1SDimitry Andric llvm::VersionTuple LatestVer; 281fe6060f1SDimitry Andric // Get ROCm version from ROCm directory name. 282fe6060f1SDimitry Andric auto GetROCmVersion = [](StringRef DirName) { 283fe6060f1SDimitry Andric llvm::VersionTuple V; 284fe6060f1SDimitry Andric std::string VerStr = DirName.drop_front(strlen("rocm-")).str(); 285fe6060f1SDimitry Andric // The ROCm directory name follows the format of 286fe6060f1SDimitry Andric // rocm-{major}.{minor}.{subMinor}[-{build}] 287fe6060f1SDimitry Andric std::replace(VerStr.begin(), VerStr.end(), '-', '.'); 288fe6060f1SDimitry Andric V.tryParse(VerStr); 289fe6060f1SDimitry Andric return V; 290fe6060f1SDimitry Andric }; 291fe6060f1SDimitry Andric for (llvm::vfs::directory_iterator 292fe6060f1SDimitry Andric File = D.getVFS().dir_begin(D.SysRoot + "/opt", EC), 293fe6060f1SDimitry Andric FileEnd; 294fe6060f1SDimitry Andric File != FileEnd && !EC; File.increment(EC)) { 295fe6060f1SDimitry Andric llvm::StringRef FileName = llvm::sys::path::filename(File->path()); 2965f757f3fSDimitry Andric if (!FileName.starts_with("rocm-")) 297fe6060f1SDimitry Andric continue; 298fe6060f1SDimitry Andric if (LatestROCm.empty()) { 299fe6060f1SDimitry Andric LatestROCm = FileName.str(); 300fe6060f1SDimitry Andric LatestVer = GetROCmVersion(LatestROCm); 301fe6060f1SDimitry Andric continue; 302fe6060f1SDimitry Andric } 303fe6060f1SDimitry Andric auto Ver = GetROCmVersion(FileName); 304fe6060f1SDimitry Andric if (LatestVer < Ver) { 305fe6060f1SDimitry Andric LatestROCm = FileName.str(); 306fe6060f1SDimitry Andric LatestVer = Ver; 307fe6060f1SDimitry Andric } 308fe6060f1SDimitry Andric } 309fe6060f1SDimitry Andric if (!LatestROCm.empty()) 310fe6060f1SDimitry Andric ROCmSearchDirs.emplace_back(D.SysRoot + "/opt/" + LatestROCm, 311fe6060f1SDimitry Andric /*StrictChecking=*/true); 312fe6060f1SDimitry Andric 313bdd1243dSDimitry Andric ROCmSearchDirs.emplace_back(D.SysRoot + "/usr/local", 314bdd1243dSDimitry Andric /*StrictChecking=*/true); 315bdd1243dSDimitry Andric ROCmSearchDirs.emplace_back(D.SysRoot + "/usr", 316bdd1243dSDimitry Andric /*StrictChecking=*/true); 317bdd1243dSDimitry Andric 318fe6060f1SDimitry Andric DoPrintROCmSearchDirs(); 319fe6060f1SDimitry Andric return ROCmSearchDirs; 3205ffd83dbSDimitry Andric } 3215ffd83dbSDimitry Andric 3225ffd83dbSDimitry Andric RocmInstallationDetector::RocmInstallationDetector( 3235ffd83dbSDimitry Andric const Driver &D, const llvm::Triple &HostTriple, 3245ffd83dbSDimitry Andric const llvm::opt::ArgList &Args, bool DetectHIPRuntime, bool DetectDeviceLib) 3255ffd83dbSDimitry Andric : D(D) { 326fe6060f1SDimitry Andric Verbose = Args.hasArg(options::OPT_v); 3275ffd83dbSDimitry Andric RocmPathArg = Args.getLastArgValue(clang::driver::options::OPT_rocm_path_EQ); 328fe6060f1SDimitry Andric PrintROCmSearchDirs = 329fe6060f1SDimitry Andric Args.hasArg(clang::driver::options::OPT_print_rocm_search_dirs); 3305ffd83dbSDimitry Andric RocmDeviceLibPathArg = 3315ffd83dbSDimitry Andric Args.getAllArgValues(clang::driver::options::OPT_rocm_device_lib_path_EQ); 332fe6060f1SDimitry Andric HIPPathArg = Args.getLastArgValue(clang::driver::options::OPT_hip_path_EQ); 3335f757f3fSDimitry Andric HIPStdParPathArg = 3345f757f3fSDimitry Andric Args.getLastArgValue(clang::driver::options::OPT_hipstdpar_path_EQ); 3355f757f3fSDimitry Andric HasHIPStdParLibrary = 3365f757f3fSDimitry Andric !HIPStdParPathArg.empty() && D.getVFS().exists(HIPStdParPathArg + 3375f757f3fSDimitry Andric "/hipstdpar_lib.hpp"); 3385f757f3fSDimitry Andric HIPRocThrustPathArg = 3395f757f3fSDimitry Andric Args.getLastArgValue(clang::driver::options::OPT_hipstdpar_thrust_path_EQ); 3405f757f3fSDimitry Andric HasRocThrustLibrary = !HIPRocThrustPathArg.empty() && 3415f757f3fSDimitry Andric D.getVFS().exists(HIPRocThrustPathArg + "/thrust"); 3425f757f3fSDimitry Andric HIPRocPrimPathArg = 3435f757f3fSDimitry Andric Args.getLastArgValue(clang::driver::options::OPT_hipstdpar_prim_path_EQ); 3445f757f3fSDimitry Andric HasRocPrimLibrary = !HIPRocPrimPathArg.empty() && 3455f757f3fSDimitry Andric D.getVFS().exists(HIPRocPrimPathArg + "/rocprim"); 3465f757f3fSDimitry Andric 3475ffd83dbSDimitry Andric if (auto *A = Args.getLastArg(clang::driver::options::OPT_hip_version_EQ)) { 3485ffd83dbSDimitry Andric HIPVersionArg = A->getValue(); 349fe6060f1SDimitry Andric unsigned Major = ~0U; 350fe6060f1SDimitry Andric unsigned Minor = ~0U; 3515ffd83dbSDimitry Andric SmallVector<StringRef, 3> Parts; 3525ffd83dbSDimitry Andric HIPVersionArg.split(Parts, '.'); 3535ffd83dbSDimitry Andric if (Parts.size()) 3545ffd83dbSDimitry Andric Parts[0].getAsInteger(0, Major); 3555ffd83dbSDimitry Andric if (Parts.size() > 1) 3565ffd83dbSDimitry Andric Parts[1].getAsInteger(0, Minor); 3575ffd83dbSDimitry Andric if (Parts.size() > 2) 3585ffd83dbSDimitry Andric VersionPatch = Parts[2].str(); 3595ffd83dbSDimitry Andric if (VersionPatch.empty()) 3605ffd83dbSDimitry Andric VersionPatch = "0"; 361fe6060f1SDimitry Andric if (Major != ~0U && Minor == ~0U) 362fe6060f1SDimitry Andric Minor = 0; 363fe6060f1SDimitry Andric if (Major == ~0U || Minor == ~0U) 3645ffd83dbSDimitry Andric D.Diag(diag::err_drv_invalid_value) 3655ffd83dbSDimitry Andric << A->getAsString(Args) << HIPVersionArg; 3665ffd83dbSDimitry Andric 3675ffd83dbSDimitry Andric VersionMajorMinor = llvm::VersionTuple(Major, Minor); 3685ffd83dbSDimitry Andric DetectedVersion = 3695ffd83dbSDimitry Andric (Twine(Major) + "." + Twine(Minor) + "." + VersionPatch).str(); 3705ffd83dbSDimitry Andric } else { 3715ffd83dbSDimitry Andric VersionPatch = DefaultVersionPatch; 3725ffd83dbSDimitry Andric VersionMajorMinor = 3735ffd83dbSDimitry Andric llvm::VersionTuple(DefaultVersionMajor, DefaultVersionMinor); 3745ffd83dbSDimitry Andric DetectedVersion = (Twine(DefaultVersionMajor) + "." + 3755ffd83dbSDimitry Andric Twine(DefaultVersionMinor) + "." + VersionPatch) 3765ffd83dbSDimitry Andric .str(); 3775ffd83dbSDimitry Andric } 3785ffd83dbSDimitry Andric 3795ffd83dbSDimitry Andric if (DetectHIPRuntime) 3805ffd83dbSDimitry Andric detectHIPRuntime(); 3815ffd83dbSDimitry Andric if (DetectDeviceLib) 3825ffd83dbSDimitry Andric detectDeviceLibrary(); 3835ffd83dbSDimitry Andric } 3845ffd83dbSDimitry Andric 3855ffd83dbSDimitry Andric void RocmInstallationDetector::detectDeviceLibrary() { 3865ffd83dbSDimitry Andric assert(LibDevicePath.empty()); 3875ffd83dbSDimitry Andric 3885ffd83dbSDimitry Andric if (!RocmDeviceLibPathArg.empty()) 3895ffd83dbSDimitry Andric LibDevicePath = RocmDeviceLibPathArg[RocmDeviceLibPathArg.size() - 1]; 390bdd1243dSDimitry Andric else if (std::optional<std::string> LibPathEnv = 391bdd1243dSDimitry Andric llvm::sys::Process::GetEnv("HIP_DEVICE_LIB_PATH")) 392bdd1243dSDimitry Andric LibDevicePath = std::move(*LibPathEnv); 3935ffd83dbSDimitry Andric 3945ffd83dbSDimitry Andric auto &FS = D.getVFS(); 3955ffd83dbSDimitry Andric if (!LibDevicePath.empty()) { 3965ffd83dbSDimitry Andric // Maintain compatability with HIP flag/envvar pointing directly at the 3975ffd83dbSDimitry Andric // bitcode library directory. This points directly at the library path instead 3985ffd83dbSDimitry Andric // of the rocm root installation. 3995ffd83dbSDimitry Andric if (!FS.exists(LibDevicePath)) 4005ffd83dbSDimitry Andric return; 4015ffd83dbSDimitry Andric 4025ffd83dbSDimitry Andric scanLibDevicePath(LibDevicePath); 4035ffd83dbSDimitry Andric HasDeviceLibrary = allGenericLibsValid() && !LibDeviceMap.empty(); 4045ffd83dbSDimitry Andric return; 4055ffd83dbSDimitry Andric } 4065ffd83dbSDimitry Andric 4075ffd83dbSDimitry Andric // Check device library exists at the given path. 408bdd1243dSDimitry Andric auto CheckDeviceLib = [&](StringRef Path, bool StrictChecking) { 409bdd1243dSDimitry Andric bool CheckLibDevice = (!NoBuiltinLibs || StrictChecking); 4105ffd83dbSDimitry Andric if (CheckLibDevice && !FS.exists(Path)) 4115ffd83dbSDimitry Andric return false; 4125ffd83dbSDimitry Andric 4135ffd83dbSDimitry Andric scanLibDevicePath(Path); 4145ffd83dbSDimitry Andric 4155ffd83dbSDimitry Andric if (!NoBuiltinLibs) { 4165ffd83dbSDimitry Andric // Check that the required non-target libraries are all available. 4175ffd83dbSDimitry Andric if (!allGenericLibsValid()) 4185ffd83dbSDimitry Andric return false; 4195ffd83dbSDimitry Andric 4205ffd83dbSDimitry Andric // Check that we have found at least one libdevice that we can link in 4215ffd83dbSDimitry Andric // if -nobuiltinlib hasn't been specified. 4225ffd83dbSDimitry Andric if (LibDeviceMap.empty()) 4235ffd83dbSDimitry Andric return false; 4245ffd83dbSDimitry Andric } 4255ffd83dbSDimitry Andric return true; 4265ffd83dbSDimitry Andric }; 4275ffd83dbSDimitry Andric 428bdd1243dSDimitry Andric // Find device libraries in <LLVM_DIR>/lib/clang/<ver>/lib/amdgcn/bitcode 429bdd1243dSDimitry Andric LibDevicePath = D.ResourceDir; 430bdd1243dSDimitry Andric llvm::sys::path::append(LibDevicePath, CLANG_INSTALL_LIBDIR_BASENAME, 431bdd1243dSDimitry Andric "amdgcn", "bitcode"); 432bdd1243dSDimitry Andric HasDeviceLibrary = CheckDeviceLib(LibDevicePath, true); 4335ffd83dbSDimitry Andric if (HasDeviceLibrary) 4345ffd83dbSDimitry Andric return; 435bdd1243dSDimitry Andric 436bdd1243dSDimitry Andric // Find device libraries in a legacy ROCm directory structure 437bdd1243dSDimitry Andric // ${ROCM_ROOT}/amdgcn/bitcode/* 438bdd1243dSDimitry Andric auto &ROCmDirs = getInstallationPathCandidates(); 439bdd1243dSDimitry Andric for (const auto &Candidate : ROCmDirs) { 440bdd1243dSDimitry Andric LibDevicePath = Candidate.Path; 441bdd1243dSDimitry Andric llvm::sys::path::append(LibDevicePath, "amdgcn", "bitcode"); 442bdd1243dSDimitry Andric HasDeviceLibrary = CheckDeviceLib(LibDevicePath, Candidate.StrictChecking); 443bdd1243dSDimitry Andric if (HasDeviceLibrary) 444bdd1243dSDimitry Andric return; 4455ffd83dbSDimitry Andric } 4465ffd83dbSDimitry Andric } 4475ffd83dbSDimitry Andric 4485ffd83dbSDimitry Andric void RocmInstallationDetector::detectHIPRuntime() { 449fe6060f1SDimitry Andric SmallVector<Candidate, 4> HIPSearchDirs; 450fe6060f1SDimitry Andric if (!HIPPathArg.empty()) 45106c3fb27SDimitry Andric HIPSearchDirs.emplace_back(HIPPathArg.str()); 45206c3fb27SDimitry Andric else if (std::optional<std::string> HIPPathEnv = 45306c3fb27SDimitry Andric llvm::sys::Process::GetEnv("HIP_PATH")) { 45406c3fb27SDimitry Andric if (!HIPPathEnv->empty()) 45506c3fb27SDimitry Andric HIPSearchDirs.emplace_back(std::move(*HIPPathEnv)); 45606c3fb27SDimitry Andric } 45706c3fb27SDimitry Andric if (HIPSearchDirs.empty()) 458fe6060f1SDimitry Andric HIPSearchDirs.append(getInstallationPathCandidates()); 4595ffd83dbSDimitry Andric auto &FS = D.getVFS(); 4605ffd83dbSDimitry Andric 461fe6060f1SDimitry Andric for (const auto &Candidate : HIPSearchDirs) { 4625ffd83dbSDimitry Andric InstallPath = Candidate.Path; 4635ffd83dbSDimitry Andric if (InstallPath.empty() || !FS.exists(InstallPath)) 4645ffd83dbSDimitry Andric continue; 465fe6060f1SDimitry Andric // HIP runtime built by SPACK is installed to 466fe6060f1SDimitry Andric // <rocm_root>/hip-<rocm_release_string>-<hash> directory. 467fe6060f1SDimitry Andric auto SPACKPath = findSPACKPackage(Candidate, "hip"); 468fe6060f1SDimitry Andric InstallPath = SPACKPath.empty() ? InstallPath : SPACKPath; 4695ffd83dbSDimitry Andric 4705ffd83dbSDimitry Andric BinPath = InstallPath; 4715ffd83dbSDimitry Andric llvm::sys::path::append(BinPath, "bin"); 4725ffd83dbSDimitry Andric IncludePath = InstallPath; 4735ffd83dbSDimitry Andric llvm::sys::path::append(IncludePath, "include"); 4745ffd83dbSDimitry Andric LibPath = InstallPath; 4755ffd83dbSDimitry Andric llvm::sys::path::append(LibPath, "lib"); 476bdd1243dSDimitry Andric SharePath = InstallPath; 477bdd1243dSDimitry Andric llvm::sys::path::append(SharePath, "share"); 4785ffd83dbSDimitry Andric 47906c3fb27SDimitry Andric // Get parent of InstallPath and append "share" 48006c3fb27SDimitry Andric SmallString<0> ParentSharePath = llvm::sys::path::parent_path(InstallPath); 48106c3fb27SDimitry Andric llvm::sys::path::append(ParentSharePath, "share"); 48206c3fb27SDimitry Andric 48306c3fb27SDimitry Andric auto Append = [](SmallString<0> &path, const Twine &a, const Twine &b = "", 48406c3fb27SDimitry Andric const Twine &c = "", const Twine &d = "") { 48506c3fb27SDimitry Andric SmallString<0> newpath = path; 48606c3fb27SDimitry Andric llvm::sys::path::append(newpath, a, b, c, d); 48706c3fb27SDimitry Andric return newpath; 48806c3fb27SDimitry Andric }; 489bdd1243dSDimitry Andric // If HIP version file can be found and parsed, use HIP version from there. 490*0fca6ea1SDimitry Andric std::vector<SmallString<0>> VersionFilePaths = { 491*0fca6ea1SDimitry Andric Append(SharePath, "hip", "version"), 492*0fca6ea1SDimitry Andric InstallPath != D.SysRoot + "/usr/local" 493*0fca6ea1SDimitry Andric ? Append(ParentSharePath, "hip", "version") 494*0fca6ea1SDimitry Andric : SmallString<0>(), 495*0fca6ea1SDimitry Andric Append(BinPath, ".hipVersion")}; 496*0fca6ea1SDimitry Andric 497*0fca6ea1SDimitry Andric for (const auto &VersionFilePath : VersionFilePaths) { 498*0fca6ea1SDimitry Andric if (VersionFilePath.empty()) 499*0fca6ea1SDimitry Andric continue; 5005ffd83dbSDimitry Andric llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> VersionFile = 501bdd1243dSDimitry Andric FS.getBufferForFile(VersionFilePath); 502bdd1243dSDimitry Andric if (!VersionFile) 5035ffd83dbSDimitry Andric continue; 5045ffd83dbSDimitry Andric if (HIPVersionArg.empty() && VersionFile) 505e8d8bef9SDimitry Andric if (parseHIPVersionFile((*VersionFile)->getBuffer())) 506e8d8bef9SDimitry Andric continue; 5075ffd83dbSDimitry Andric 5085ffd83dbSDimitry Andric HasHIPRuntime = true; 5095ffd83dbSDimitry Andric return; 5105ffd83dbSDimitry Andric } 511bdd1243dSDimitry Andric // Otherwise, if -rocm-path is specified (no strict checking), use the 512bdd1243dSDimitry Andric // default HIP version or specified by --hip-version. 513bdd1243dSDimitry Andric if (!Candidate.StrictChecking) { 514bdd1243dSDimitry Andric HasHIPRuntime = true; 515bdd1243dSDimitry Andric return; 516bdd1243dSDimitry Andric } 517bdd1243dSDimitry Andric } 5185ffd83dbSDimitry Andric HasHIPRuntime = false; 5195ffd83dbSDimitry Andric } 5205ffd83dbSDimitry Andric 5215ffd83dbSDimitry Andric void RocmInstallationDetector::print(raw_ostream &OS) const { 5225ffd83dbSDimitry Andric if (hasHIPRuntime()) 5235ffd83dbSDimitry Andric OS << "Found HIP installation: " << InstallPath << ", version " 5245ffd83dbSDimitry Andric << DetectedVersion << '\n'; 5255ffd83dbSDimitry Andric } 5265ffd83dbSDimitry Andric 5275ffd83dbSDimitry Andric void RocmInstallationDetector::AddHIPIncludeArgs(const ArgList &DriverArgs, 5285ffd83dbSDimitry Andric ArgStringList &CC1Args) const { 5290eae32dcSDimitry Andric bool UsesRuntimeWrapper = VersionMajorMinor > llvm::VersionTuple(3, 5) && 5300eae32dcSDimitry Andric !DriverArgs.hasArg(options::OPT_nohipwrapperinc); 5315f757f3fSDimitry Andric bool HasHipStdPar = DriverArgs.hasArg(options::OPT_hipstdpar); 5325ffd83dbSDimitry Andric 5335ffd83dbSDimitry Andric if (!DriverArgs.hasArg(options::OPT_nobuiltininc)) { 5345ffd83dbSDimitry Andric // HIP header includes standard library wrapper headers under clang 5355ffd83dbSDimitry Andric // cuda_wrappers directory. Since these wrapper headers include_next 5365ffd83dbSDimitry Andric // standard C++ headers, whereas libc++ headers include_next other clang 5375ffd83dbSDimitry Andric // headers. The include paths have to follow this order: 5385ffd83dbSDimitry Andric // - wrapper include path 5395ffd83dbSDimitry Andric // - standard C++ include path 5405ffd83dbSDimitry Andric // - other clang include path 5415ffd83dbSDimitry Andric // Since standard C++ and other clang include paths are added in other 5425ffd83dbSDimitry Andric // places after this function, here we only need to make sure wrapper 5435ffd83dbSDimitry Andric // include path is added. 5445ffd83dbSDimitry Andric // 5455ffd83dbSDimitry Andric // ROCm 3.5 does not fully support the wrapper headers. Therefore it needs 5465ffd83dbSDimitry Andric // a workaround. 5475ffd83dbSDimitry Andric SmallString<128> P(D.ResourceDir); 5485ffd83dbSDimitry Andric if (UsesRuntimeWrapper) 5495ffd83dbSDimitry Andric llvm::sys::path::append(P, "include", "cuda_wrappers"); 5505ffd83dbSDimitry Andric CC1Args.push_back("-internal-isystem"); 5515ffd83dbSDimitry Andric CC1Args.push_back(DriverArgs.MakeArgString(P)); 5525ffd83dbSDimitry Andric } 5535ffd83dbSDimitry Andric 5545f757f3fSDimitry Andric const auto HandleHipStdPar = [=, &DriverArgs, &CC1Args]() { 5557a6dacacSDimitry Andric StringRef Inc = getIncludePath(); 5567a6dacacSDimitry Andric auto &FS = D.getVFS(); 5577a6dacacSDimitry Andric 5587a6dacacSDimitry Andric if (!hasHIPStdParLibrary()) 5597a6dacacSDimitry Andric if (!HIPStdParPathArg.empty() || 5607a6dacacSDimitry Andric !FS.exists(Inc + "/thrust/system/hip/hipstdpar/hipstdpar_lib.hpp")) { 5615f757f3fSDimitry Andric D.Diag(diag::err_drv_no_hipstdpar_lib); 5625ffd83dbSDimitry Andric return; 5635f757f3fSDimitry Andric } 5647a6dacacSDimitry Andric if (!HasRocThrustLibrary && !FS.exists(Inc + "/thrust")) { 5655f757f3fSDimitry Andric D.Diag(diag::err_drv_no_hipstdpar_thrust_lib); 5665f757f3fSDimitry Andric return; 5675f757f3fSDimitry Andric } 5687a6dacacSDimitry Andric if (!HasRocPrimLibrary && !FS.exists(Inc + "/rocprim")) { 5695f757f3fSDimitry Andric D.Diag(diag::err_drv_no_hipstdpar_prim_lib); 5705f757f3fSDimitry Andric return; 5715f757f3fSDimitry Andric } 5725f757f3fSDimitry Andric const char *ThrustPath; 5735f757f3fSDimitry Andric if (HasRocThrustLibrary) 5745f757f3fSDimitry Andric ThrustPath = DriverArgs.MakeArgString(HIPRocThrustPathArg); 5755f757f3fSDimitry Andric else 5767a6dacacSDimitry Andric ThrustPath = DriverArgs.MakeArgString(Inc + "/thrust"); 5777a6dacacSDimitry Andric 5787a6dacacSDimitry Andric const char *HIPStdParPath; 5797a6dacacSDimitry Andric if (hasHIPStdParLibrary()) 5807a6dacacSDimitry Andric HIPStdParPath = DriverArgs.MakeArgString(HIPStdParPathArg); 5817a6dacacSDimitry Andric else 5827a6dacacSDimitry Andric HIPStdParPath = DriverArgs.MakeArgString(StringRef(ThrustPath) + 5837a6dacacSDimitry Andric "/system/hip/hipstdpar"); 5845f757f3fSDimitry Andric 5855f757f3fSDimitry Andric const char *PrimPath; 5865f757f3fSDimitry Andric if (HasRocPrimLibrary) 5875f757f3fSDimitry Andric PrimPath = DriverArgs.MakeArgString(HIPRocPrimPathArg); 5885f757f3fSDimitry Andric else 5895f757f3fSDimitry Andric PrimPath = DriverArgs.MakeArgString(getIncludePath() + "/rocprim"); 5905f757f3fSDimitry Andric 5915f757f3fSDimitry Andric CC1Args.append({"-idirafter", ThrustPath, "-idirafter", PrimPath, 5927a6dacacSDimitry Andric "-idirafter", HIPStdParPath, "-include", 5937a6dacacSDimitry Andric "hipstdpar_lib.hpp"}); 5945f757f3fSDimitry Andric }; 5955f757f3fSDimitry Andric 5965f757f3fSDimitry Andric if (DriverArgs.hasArg(options::OPT_nogpuinc)) { 5975f757f3fSDimitry Andric if (HasHipStdPar) 5985f757f3fSDimitry Andric HandleHipStdPar(); 5995f757f3fSDimitry Andric 6005f757f3fSDimitry Andric return; 6015f757f3fSDimitry Andric } 6025ffd83dbSDimitry Andric 6035ffd83dbSDimitry Andric if (!hasHIPRuntime()) { 6045ffd83dbSDimitry Andric D.Diag(diag::err_drv_no_hip_runtime); 6055ffd83dbSDimitry Andric return; 6065ffd83dbSDimitry Andric } 6075ffd83dbSDimitry Andric 6082a66634dSDimitry Andric CC1Args.push_back("-idirafter"); 6095ffd83dbSDimitry Andric CC1Args.push_back(DriverArgs.MakeArgString(getIncludePath())); 6105ffd83dbSDimitry Andric if (UsesRuntimeWrapper) 6115ffd83dbSDimitry Andric CC1Args.append({"-include", "__clang_hip_runtime_wrapper.h"}); 6125f757f3fSDimitry Andric if (HasHipStdPar) 6135f757f3fSDimitry Andric HandleHipStdPar(); 6145ffd83dbSDimitry Andric } 6155ffd83dbSDimitry Andric 6160b57cec5SDimitry Andric void amdgpu::Linker::ConstructJob(Compilation &C, const JobAction &JA, 6170b57cec5SDimitry Andric const InputInfo &Output, 6180b57cec5SDimitry Andric const InputInfoList &Inputs, 6190b57cec5SDimitry Andric const ArgList &Args, 6200b57cec5SDimitry Andric const char *LinkingOutput) const { 621*0fca6ea1SDimitry Andric std::string Linker = getToolChain().GetLinkerPath(); 6220b57cec5SDimitry Andric ArgStringList CmdArgs; 6235f757f3fSDimitry Andric CmdArgs.push_back("--no-undefined"); 6245f757f3fSDimitry Andric CmdArgs.push_back("-shared"); 6255f757f3fSDimitry Andric 62619587d74SEd Maste addLinkerCompressDebugSectionsOption(getToolChain(), Args, CmdArgs); 62706c3fb27SDimitry Andric Args.AddAllArgs(CmdArgs, options::OPT_L); 628*0fca6ea1SDimitry Andric getToolChain().AddFilePathLibArgs(Args, CmdArgs); 6290b57cec5SDimitry Andric AddLinkerInputs(getToolChain(), Inputs, Args, CmdArgs, JA); 63006c3fb27SDimitry Andric if (C.getDriver().isUsingLTO()) 63106c3fb27SDimitry Andric addLTOOptions(getToolChain(), Args, CmdArgs, Output, Inputs[0], 63206c3fb27SDimitry Andric C.getDriver().getLTOMode() == LTOK_Thin); 63306c3fb27SDimitry Andric else if (Args.hasArg(options::OPT_mcpu_EQ)) 63406c3fb27SDimitry Andric CmdArgs.push_back(Args.MakeArgString( 63506c3fb27SDimitry Andric "-plugin-opt=mcpu=" + Args.getLastArgValue(options::OPT_mcpu_EQ))); 6360b57cec5SDimitry Andric CmdArgs.push_back("-o"); 6370b57cec5SDimitry Andric CmdArgs.push_back(Output.getFilename()); 638e8d8bef9SDimitry Andric C.addCommand(std::make_unique<Command>( 639e8d8bef9SDimitry Andric JA, *this, ResponseFileSupport::AtFileCurCP(), Args.MakeArgString(Linker), 640e8d8bef9SDimitry Andric CmdArgs, Inputs, Output)); 6410b57cec5SDimitry Andric } 6420b57cec5SDimitry Andric 6430b57cec5SDimitry Andric void amdgpu::getAMDGPUTargetFeatures(const Driver &D, 644e8d8bef9SDimitry Andric const llvm::Triple &Triple, 6450b57cec5SDimitry Andric const llvm::opt::ArgList &Args, 6460b57cec5SDimitry Andric std::vector<StringRef> &Features) { 647e8d8bef9SDimitry Andric // Add target ID features to -target-feature options. No diagnostics should 648e8d8bef9SDimitry Andric // be emitted here since invalid target ID is diagnosed at other places. 649*0fca6ea1SDimitry Andric StringRef TargetID; 650*0fca6ea1SDimitry Andric if (Args.hasArg(options::OPT_mcpu_EQ)) 651*0fca6ea1SDimitry Andric TargetID = Args.getLastArgValue(options::OPT_mcpu_EQ); 652*0fca6ea1SDimitry Andric else if (Args.hasArg(options::OPT_march_EQ)) 653*0fca6ea1SDimitry Andric TargetID = Args.getLastArgValue(options::OPT_march_EQ); 654e8d8bef9SDimitry Andric if (!TargetID.empty()) { 655e8d8bef9SDimitry Andric llvm::StringMap<bool> FeatureMap; 656e8d8bef9SDimitry Andric auto OptionalGpuArch = parseTargetID(Triple, TargetID, &FeatureMap); 657e8d8bef9SDimitry Andric if (OptionalGpuArch) { 65881ad6265SDimitry Andric StringRef GpuArch = *OptionalGpuArch; 659e8d8bef9SDimitry Andric // Iterate through all possible target ID features for the given GPU. 660e8d8bef9SDimitry Andric // If it is mapped to true, add +feature. 661e8d8bef9SDimitry Andric // If it is mapped to false, add -feature. 662e8d8bef9SDimitry Andric // If it is not in the map (default), do not add it 663e8d8bef9SDimitry Andric for (auto &&Feature : getAllPossibleTargetIDFeatures(Triple, GpuArch)) { 664e8d8bef9SDimitry Andric auto Pos = FeatureMap.find(Feature); 665e8d8bef9SDimitry Andric if (Pos == FeatureMap.end()) 666e8d8bef9SDimitry Andric continue; 667e8d8bef9SDimitry Andric Features.push_back(Args.MakeArgStringRef( 668e8d8bef9SDimitry Andric (Twine(Pos->second ? "+" : "-") + Feature).str())); 669e8d8bef9SDimitry Andric } 670e8d8bef9SDimitry Andric } 671e8d8bef9SDimitry Andric } 6720b57cec5SDimitry Andric 673e8d8bef9SDimitry Andric if (Args.hasFlag(options::OPT_mwavefrontsize64, 674e8d8bef9SDimitry Andric options::OPT_mno_wavefrontsize64, false)) 6750b57cec5SDimitry Andric Features.push_back("+wavefrontsize64"); 6760b57cec5SDimitry Andric 677*0fca6ea1SDimitry Andric if (Args.hasFlag(options::OPT_mamdgpu_precise_memory_op, 678*0fca6ea1SDimitry Andric options::OPT_mno_amdgpu_precise_memory_op, false)) 679*0fca6ea1SDimitry Andric Features.push_back("+precise-memory"); 680*0fca6ea1SDimitry Andric 68106c3fb27SDimitry Andric handleTargetFeaturesGroup(D, Triple, Args, Features, 68206c3fb27SDimitry Andric options::OPT_m_amdgpu_Features_Group); 6830b57cec5SDimitry Andric } 6840b57cec5SDimitry Andric 6850b57cec5SDimitry Andric /// AMDGPU Toolchain 6860b57cec5SDimitry Andric AMDGPUToolChain::AMDGPUToolChain(const Driver &D, const llvm::Triple &Triple, 6870b57cec5SDimitry Andric const ArgList &Args) 6880b57cec5SDimitry Andric : Generic_ELF(D, Triple, Args), 689e8d8bef9SDimitry Andric OptionsDefault( 690e8d8bef9SDimitry Andric {{options::OPT_O, "3"}, {options::OPT_cl_std_EQ, "CL1.2"}}) { 691e8d8bef9SDimitry Andric // Check code object version options. Emit warnings for legacy options 692e8d8bef9SDimitry Andric // and errors for the last invalid code object version options. 693e8d8bef9SDimitry Andric // It is done here to avoid repeated warning or error messages for 694e8d8bef9SDimitry Andric // each tool invocation. 695fe6060f1SDimitry Andric checkAMDGPUCodeObjectVersion(D, Args); 696e8d8bef9SDimitry Andric } 6970b57cec5SDimitry Andric 6980b57cec5SDimitry Andric Tool *AMDGPUToolChain::buildLinker() const { 6990b57cec5SDimitry Andric return new tools::amdgpu::Linker(*this); 7000b57cec5SDimitry Andric } 7010b57cec5SDimitry Andric 7020b57cec5SDimitry Andric DerivedArgList * 7030b57cec5SDimitry Andric AMDGPUToolChain::TranslateArgs(const DerivedArgList &Args, StringRef BoundArch, 7040b57cec5SDimitry Andric Action::OffloadKind DeviceOffloadKind) const { 7050b57cec5SDimitry Andric 7060b57cec5SDimitry Andric DerivedArgList *DAL = 7070b57cec5SDimitry Andric Generic_ELF::TranslateArgs(Args, BoundArch, DeviceOffloadKind); 7080b57cec5SDimitry Andric 709e8d8bef9SDimitry Andric const OptTable &Opts = getDriver().getOpts(); 7100b57cec5SDimitry Andric 7110b57cec5SDimitry Andric if (!DAL) 7120b57cec5SDimitry Andric DAL = new DerivedArgList(Args.getBaseArgs()); 7130b57cec5SDimitry Andric 71406c3fb27SDimitry Andric for (Arg *A : Args) 715e8d8bef9SDimitry Andric DAL->append(A); 71606c3fb27SDimitry Andric 71706c3fb27SDimitry Andric // Replace -mcpu=native with detected GPU. 71806c3fb27SDimitry Andric Arg *LastMCPUArg = DAL->getLastArg(options::OPT_mcpu_EQ); 71906c3fb27SDimitry Andric if (LastMCPUArg && StringRef(LastMCPUArg->getValue()) == "native") { 72006c3fb27SDimitry Andric DAL->eraseArg(options::OPT_mcpu_EQ); 72106c3fb27SDimitry Andric auto GPUsOrErr = getSystemGPUArchs(Args); 72206c3fb27SDimitry Andric if (!GPUsOrErr) { 72306c3fb27SDimitry Andric getDriver().Diag(diag::err_drv_undetermined_gpu_arch) 72406c3fb27SDimitry Andric << llvm::Triple::getArchTypeName(getArch()) 72506c3fb27SDimitry Andric << llvm::toString(GPUsOrErr.takeError()) << "-mcpu"; 72606c3fb27SDimitry Andric } else { 72706c3fb27SDimitry Andric auto &GPUs = *GPUsOrErr; 72806c3fb27SDimitry Andric if (GPUs.size() > 1) { 72906c3fb27SDimitry Andric getDriver().Diag(diag::warn_drv_multi_gpu_arch) 73006c3fb27SDimitry Andric << llvm::Triple::getArchTypeName(getArch()) 73106c3fb27SDimitry Andric << llvm::join(GPUs, ", ") << "-mcpu"; 73206c3fb27SDimitry Andric } 73306c3fb27SDimitry Andric DAL->AddJoinedArg(nullptr, Opts.getOption(options::OPT_mcpu_EQ), 73406c3fb27SDimitry Andric Args.MakeArgString(GPUs.front())); 73506c3fb27SDimitry Andric } 736e8d8bef9SDimitry Andric } 737e8d8bef9SDimitry Andric 738e8d8bef9SDimitry Andric checkTargetID(*DAL); 739e8d8bef9SDimitry Andric 740*0fca6ea1SDimitry Andric if (Args.getLastArgValue(options::OPT_x) != "cl") 741e8d8bef9SDimitry Andric return DAL; 7420b57cec5SDimitry Andric 7430b57cec5SDimitry Andric // Phase 1 (.cl -> .bc) 7440b57cec5SDimitry Andric if (Args.hasArg(options::OPT_c) && Args.hasArg(options::OPT_emit_llvm)) { 7450b57cec5SDimitry Andric DAL->AddFlagArg(nullptr, Opts.getOption(getTriple().isArch64Bit() 7460b57cec5SDimitry Andric ? options::OPT_m64 7470b57cec5SDimitry Andric : options::OPT_m32)); 7480b57cec5SDimitry Andric 7490b57cec5SDimitry Andric // Have to check OPT_O4, OPT_O0 & OPT_Ofast separately 7500b57cec5SDimitry Andric // as they defined that way in Options.td 7510b57cec5SDimitry Andric if (!Args.hasArg(options::OPT_O, options::OPT_O0, options::OPT_O4, 7520b57cec5SDimitry Andric options::OPT_Ofast)) 7530b57cec5SDimitry Andric DAL->AddJoinedArg(nullptr, Opts.getOption(options::OPT_O), 7540b57cec5SDimitry Andric getOptionDefault(options::OPT_O)); 7550b57cec5SDimitry Andric } 7560b57cec5SDimitry Andric 7570b57cec5SDimitry Andric return DAL; 7580b57cec5SDimitry Andric } 7590b57cec5SDimitry Andric 7605ffd83dbSDimitry Andric bool AMDGPUToolChain::getDefaultDenormsAreZeroForTarget( 7615ffd83dbSDimitry Andric llvm::AMDGPU::GPUKind Kind) { 7625ffd83dbSDimitry Andric 7635ffd83dbSDimitry Andric // Assume nothing without a specific target. 7645ffd83dbSDimitry Andric if (Kind == llvm::AMDGPU::GK_NONE) 7655ffd83dbSDimitry Andric return false; 7665ffd83dbSDimitry Andric 7675ffd83dbSDimitry Andric const unsigned ArchAttr = llvm::AMDGPU::getArchAttrAMDGCN(Kind); 7685ffd83dbSDimitry Andric 7695ffd83dbSDimitry Andric // Default to enabling f32 denormals by default on subtargets where fma is 7705ffd83dbSDimitry Andric // fast with denormals 7715ffd83dbSDimitry Andric const bool BothDenormAndFMAFast = 7725ffd83dbSDimitry Andric (ArchAttr & llvm::AMDGPU::FEATURE_FAST_FMA_F32) && 7735ffd83dbSDimitry Andric (ArchAttr & llvm::AMDGPU::FEATURE_FAST_DENORMAL_F32); 7745ffd83dbSDimitry Andric return !BothDenormAndFMAFast; 7755ffd83dbSDimitry Andric } 7765ffd83dbSDimitry Andric 7775ffd83dbSDimitry Andric llvm::DenormalMode AMDGPUToolChain::getDefaultDenormalModeForType( 7785ffd83dbSDimitry Andric const llvm::opt::ArgList &DriverArgs, const JobAction &JA, 7795ffd83dbSDimitry Andric const llvm::fltSemantics *FPType) const { 7805ffd83dbSDimitry Andric // Denormals should always be enabled for f16 and f64. 7815ffd83dbSDimitry Andric if (!FPType || FPType != &llvm::APFloat::IEEEsingle()) 7825ffd83dbSDimitry Andric return llvm::DenormalMode::getIEEE(); 7835ffd83dbSDimitry Andric 7845ffd83dbSDimitry Andric if (JA.getOffloadingDeviceKind() == Action::OFK_HIP || 7855ffd83dbSDimitry Andric JA.getOffloadingDeviceKind() == Action::OFK_Cuda) { 786e8d8bef9SDimitry Andric auto Arch = getProcessorFromTargetID(getTriple(), JA.getOffloadingArch()); 787e8d8bef9SDimitry Andric auto Kind = llvm::AMDGPU::parseArchAMDGCN(Arch); 7885ffd83dbSDimitry Andric if (FPType && FPType == &llvm::APFloat::IEEEsingle() && 789fe6060f1SDimitry Andric DriverArgs.hasFlag(options::OPT_fgpu_flush_denormals_to_zero, 790fe6060f1SDimitry Andric options::OPT_fno_gpu_flush_denormals_to_zero, 7915ffd83dbSDimitry Andric getDefaultDenormsAreZeroForTarget(Kind))) 7925ffd83dbSDimitry Andric return llvm::DenormalMode::getPreserveSign(); 7935ffd83dbSDimitry Andric 7945ffd83dbSDimitry Andric return llvm::DenormalMode::getIEEE(); 7955ffd83dbSDimitry Andric } 7965ffd83dbSDimitry Andric 797e8d8bef9SDimitry Andric const StringRef GpuArch = getGPUArch(DriverArgs); 7985ffd83dbSDimitry Andric auto Kind = llvm::AMDGPU::parseArchAMDGCN(GpuArch); 7995ffd83dbSDimitry Andric 8005ffd83dbSDimitry Andric // TODO: There are way too many flags that change this. Do we need to check 8015ffd83dbSDimitry Andric // them all? 8025ffd83dbSDimitry Andric bool DAZ = DriverArgs.hasArg(options::OPT_cl_denorms_are_zero) || 8035ffd83dbSDimitry Andric getDefaultDenormsAreZeroForTarget(Kind); 8045ffd83dbSDimitry Andric 8055ffd83dbSDimitry Andric // Outputs are flushed to zero (FTZ), preserving sign. Denormal inputs are 8065ffd83dbSDimitry Andric // also implicit treated as zero (DAZ). 8075ffd83dbSDimitry Andric return DAZ ? llvm::DenormalMode::getPreserveSign() : 8085ffd83dbSDimitry Andric llvm::DenormalMode::getIEEE(); 8095ffd83dbSDimitry Andric } 8105ffd83dbSDimitry Andric 8115ffd83dbSDimitry Andric bool AMDGPUToolChain::isWave64(const llvm::opt::ArgList &DriverArgs, 8125ffd83dbSDimitry Andric llvm::AMDGPU::GPUKind Kind) { 8135ffd83dbSDimitry Andric const unsigned ArchAttr = llvm::AMDGPU::getArchAttrAMDGCN(Kind); 814e8d8bef9SDimitry Andric bool HasWave32 = (ArchAttr & llvm::AMDGPU::FEATURE_WAVE32); 8155ffd83dbSDimitry Andric 8165ffd83dbSDimitry Andric return !HasWave32 || DriverArgs.hasFlag( 8175ffd83dbSDimitry Andric options::OPT_mwavefrontsize64, options::OPT_mno_wavefrontsize64, false); 8185ffd83dbSDimitry Andric } 8195ffd83dbSDimitry Andric 8205ffd83dbSDimitry Andric 8215ffd83dbSDimitry Andric /// ROCM Toolchain 8225ffd83dbSDimitry Andric ROCMToolChain::ROCMToolChain(const Driver &D, const llvm::Triple &Triple, 8235ffd83dbSDimitry Andric const ArgList &Args) 8245ffd83dbSDimitry Andric : AMDGPUToolChain(D, Triple, Args) { 82506c3fb27SDimitry Andric RocmInstallation->detectDeviceLibrary(); 8265ffd83dbSDimitry Andric } 8275ffd83dbSDimitry Andric 8280b57cec5SDimitry Andric void AMDGPUToolChain::addClangTargetOptions( 8290b57cec5SDimitry Andric const llvm::opt::ArgList &DriverArgs, 8300b57cec5SDimitry Andric llvm::opt::ArgStringList &CC1Args, 8310b57cec5SDimitry Andric Action::OffloadKind DeviceOffloadingKind) const { 8320b57cec5SDimitry Andric // Default to "hidden" visibility, as object level linking will not be 8330b57cec5SDimitry Andric // supported for the foreseeable future. 8340b57cec5SDimitry Andric if (!DriverArgs.hasArg(options::OPT_fvisibility_EQ, 8350b57cec5SDimitry Andric options::OPT_fvisibility_ms_compat)) { 836bdd1243dSDimitry Andric CC1Args.push_back("-fvisibility=hidden"); 8370b57cec5SDimitry Andric CC1Args.push_back("-fapply-global-visibility-to-externs"); 8380b57cec5SDimitry Andric } 8390b57cec5SDimitry Andric } 8405ffd83dbSDimitry Andric 841*0fca6ea1SDimitry Andric void AMDGPUToolChain::addClangWarningOptions(ArgStringList &CC1Args) const { 842*0fca6ea1SDimitry Andric // AMDGPU does not support atomic lib call. Treat atomic alignment 843*0fca6ea1SDimitry Andric // warnings as errors. 844*0fca6ea1SDimitry Andric CC1Args.push_back("-Werror=atomic-alignment"); 845*0fca6ea1SDimitry Andric } 846*0fca6ea1SDimitry Andric 847e8d8bef9SDimitry Andric StringRef 848e8d8bef9SDimitry Andric AMDGPUToolChain::getGPUArch(const llvm::opt::ArgList &DriverArgs) const { 849e8d8bef9SDimitry Andric return getProcessorFromTargetID( 850e8d8bef9SDimitry Andric getTriple(), DriverArgs.getLastArgValue(options::OPT_mcpu_EQ)); 851e8d8bef9SDimitry Andric } 852e8d8bef9SDimitry Andric 853fe6060f1SDimitry Andric AMDGPUToolChain::ParsedTargetIDType 854fe6060f1SDimitry Andric AMDGPUToolChain::getParsedTargetID(const llvm::opt::ArgList &DriverArgs) const { 855e8d8bef9SDimitry Andric StringRef TargetID = DriverArgs.getLastArgValue(options::OPT_mcpu_EQ); 856e8d8bef9SDimitry Andric if (TargetID.empty()) 857bdd1243dSDimitry Andric return {std::nullopt, std::nullopt, std::nullopt}; 858e8d8bef9SDimitry Andric 859e8d8bef9SDimitry Andric llvm::StringMap<bool> FeatureMap; 860e8d8bef9SDimitry Andric auto OptionalGpuArch = parseTargetID(getTriple(), TargetID, &FeatureMap); 861fe6060f1SDimitry Andric if (!OptionalGpuArch) 862bdd1243dSDimitry Andric return {TargetID.str(), std::nullopt, std::nullopt}; 863fe6060f1SDimitry Andric 86481ad6265SDimitry Andric return {TargetID.str(), OptionalGpuArch->str(), FeatureMap}; 865e8d8bef9SDimitry Andric } 866fe6060f1SDimitry Andric 867fe6060f1SDimitry Andric void AMDGPUToolChain::checkTargetID( 868fe6060f1SDimitry Andric const llvm::opt::ArgList &DriverArgs) const { 869fe6060f1SDimitry Andric auto PTID = getParsedTargetID(DriverArgs); 870fe6060f1SDimitry Andric if (PTID.OptionalTargetID && !PTID.OptionalGPUArch) { 871fe6060f1SDimitry Andric getDriver().Diag(clang::diag::err_drv_bad_target_id) 87281ad6265SDimitry Andric << *PTID.OptionalTargetID; 873fe6060f1SDimitry Andric } 874fe6060f1SDimitry Andric } 875fe6060f1SDimitry Andric 876bdd1243dSDimitry Andric Expected<SmallVector<std::string>> 877bdd1243dSDimitry Andric AMDGPUToolChain::getSystemGPUArchs(const ArgList &Args) const { 878bdd1243dSDimitry Andric // Detect AMD GPUs availible on the system. 879fe6060f1SDimitry Andric std::string Program; 880fe6060f1SDimitry Andric if (Arg *A = Args.getLastArg(options::OPT_amdgpu_arch_tool_EQ)) 881fe6060f1SDimitry Andric Program = A->getValue(); 882fe6060f1SDimitry Andric else 883bdd1243dSDimitry Andric Program = GetProgramPath("amdgpu-arch"); 884fe6060f1SDimitry Andric 885*0fca6ea1SDimitry Andric auto StdoutOrErr = executeToolChainProgram(Program, /*SecondsToWait=*/10); 886bdd1243dSDimitry Andric if (!StdoutOrErr) 887bdd1243dSDimitry Andric return StdoutOrErr.takeError(); 888fe6060f1SDimitry Andric 889fe6060f1SDimitry Andric SmallVector<std::string, 1> GPUArchs; 890bdd1243dSDimitry Andric for (StringRef Arch : llvm::split((*StdoutOrErr)->getBuffer(), "\n")) 891bdd1243dSDimitry Andric if (!Arch.empty()) 892bdd1243dSDimitry Andric GPUArchs.push_back(Arch.str()); 893bdd1243dSDimitry Andric 894bdd1243dSDimitry Andric if (GPUArchs.empty()) 895fe6060f1SDimitry Andric return llvm::createStringError(std::error_code(), 896fe6060f1SDimitry Andric "No AMD GPU detected in the system"); 897bdd1243dSDimitry Andric 898bdd1243dSDimitry Andric return std::move(GPUArchs); 899e8d8bef9SDimitry Andric } 900e8d8bef9SDimitry Andric 9015ffd83dbSDimitry Andric void ROCMToolChain::addClangTargetOptions( 9025ffd83dbSDimitry Andric const llvm::opt::ArgList &DriverArgs, llvm::opt::ArgStringList &CC1Args, 9035ffd83dbSDimitry Andric Action::OffloadKind DeviceOffloadingKind) const { 9045ffd83dbSDimitry Andric AMDGPUToolChain::addClangTargetOptions(DriverArgs, CC1Args, 9055ffd83dbSDimitry Andric DeviceOffloadingKind); 9065ffd83dbSDimitry Andric 9075ffd83dbSDimitry Andric // For the OpenCL case where there is no offload target, accept -nostdlib to 9085ffd83dbSDimitry Andric // disable bitcode linking. 9095ffd83dbSDimitry Andric if (DeviceOffloadingKind == Action::OFK_None && 9105ffd83dbSDimitry Andric DriverArgs.hasArg(options::OPT_nostdlib)) 9115ffd83dbSDimitry Andric return; 9125ffd83dbSDimitry Andric 9135ffd83dbSDimitry Andric if (DriverArgs.hasArg(options::OPT_nogpulib)) 9145ffd83dbSDimitry Andric return; 9155ffd83dbSDimitry Andric 9165ffd83dbSDimitry Andric // Get the device name and canonicalize it 917e8d8bef9SDimitry Andric const StringRef GpuArch = getGPUArch(DriverArgs); 9185ffd83dbSDimitry Andric auto Kind = llvm::AMDGPU::parseArchAMDGCN(GpuArch); 9195ffd83dbSDimitry Andric const StringRef CanonArch = llvm::AMDGPU::getArchNameAMDGCN(Kind); 92006c3fb27SDimitry Andric StringRef LibDeviceFile = RocmInstallation->getLibDeviceFile(CanonArch); 92181ad6265SDimitry Andric auto ABIVer = DeviceLibABIVersion::fromCodeObjectVersion( 92281ad6265SDimitry Andric getAMDGPUCodeObjectVersion(getDriver(), DriverArgs)); 92306c3fb27SDimitry Andric if (!RocmInstallation->checkCommonBitcodeLibs(CanonArch, LibDeviceFile, 92481ad6265SDimitry Andric ABIVer)) 9255ffd83dbSDimitry Andric return; 9265ffd83dbSDimitry Andric 9275ffd83dbSDimitry Andric bool Wave64 = isWave64(DriverArgs, Kind); 9285ffd83dbSDimitry Andric 9295ffd83dbSDimitry Andric // TODO: There are way too many flags that change this. Do we need to check 9305ffd83dbSDimitry Andric // them all? 9315ffd83dbSDimitry Andric bool DAZ = DriverArgs.hasArg(options::OPT_cl_denorms_are_zero) || 9325ffd83dbSDimitry Andric getDefaultDenormsAreZeroForTarget(Kind); 9335ffd83dbSDimitry Andric bool FiniteOnly = DriverArgs.hasArg(options::OPT_cl_finite_math_only); 9345ffd83dbSDimitry Andric 9355ffd83dbSDimitry Andric bool UnsafeMathOpt = 9365ffd83dbSDimitry Andric DriverArgs.hasArg(options::OPT_cl_unsafe_math_optimizations); 9375ffd83dbSDimitry Andric bool FastRelaxedMath = DriverArgs.hasArg(options::OPT_cl_fast_relaxed_math); 9385ffd83dbSDimitry Andric bool CorrectSqrt = 9395ffd83dbSDimitry Andric DriverArgs.hasArg(options::OPT_cl_fp32_correctly_rounded_divide_sqrt); 9405ffd83dbSDimitry Andric 9415ffd83dbSDimitry Andric // Add the OpenCL specific bitcode library. 942fe6060f1SDimitry Andric llvm::SmallVector<std::string, 12> BCLibs; 94306c3fb27SDimitry Andric BCLibs.push_back(RocmInstallation->getOpenCLPath().str()); 9445ffd83dbSDimitry Andric 9455ffd83dbSDimitry Andric // Add the generic set of libraries. 94606c3fb27SDimitry Andric BCLibs.append(RocmInstallation->getCommonBitcodeLibs( 947fe6060f1SDimitry Andric DriverArgs, LibDeviceFile, Wave64, DAZ, FiniteOnly, UnsafeMathOpt, 94881ad6265SDimitry Andric FastRelaxedMath, CorrectSqrt, ABIVer, false)); 949fe6060f1SDimitry Andric 950*0fca6ea1SDimitry Andric if (getSanitizerArgs(DriverArgs).needsAsanRt()) { 951*0fca6ea1SDimitry Andric CC1Args.push_back("-mlink-bitcode-file"); 952*0fca6ea1SDimitry Andric CC1Args.push_back( 953*0fca6ea1SDimitry Andric DriverArgs.MakeArgString(RocmInstallation->getAsanRTLPath())); 954*0fca6ea1SDimitry Andric } 95581ad6265SDimitry Andric for (StringRef BCFile : BCLibs) { 956fe6060f1SDimitry Andric CC1Args.push_back("-mlink-builtin-bitcode"); 957fe6060f1SDimitry Andric CC1Args.push_back(DriverArgs.MakeArgString(BCFile)); 95881ad6265SDimitry Andric } 95981ad6265SDimitry Andric } 96081ad6265SDimitry Andric 96181ad6265SDimitry Andric bool RocmInstallationDetector::checkCommonBitcodeLibs( 96281ad6265SDimitry Andric StringRef GPUArch, StringRef LibDeviceFile, 96381ad6265SDimitry Andric DeviceLibABIVersion ABIVer) const { 96481ad6265SDimitry Andric if (!hasDeviceLibrary()) { 96581ad6265SDimitry Andric D.Diag(diag::err_drv_no_rocm_device_lib) << 0; 96681ad6265SDimitry Andric return false; 96781ad6265SDimitry Andric } 96881ad6265SDimitry Andric if (LibDeviceFile.empty()) { 96981ad6265SDimitry Andric D.Diag(diag::err_drv_no_rocm_device_lib) << 1 << GPUArch; 97081ad6265SDimitry Andric return false; 97181ad6265SDimitry Andric } 97281ad6265SDimitry Andric if (ABIVer.requiresLibrary() && getABIVersionPath(ABIVer).empty()) { 97381ad6265SDimitry Andric D.Diag(diag::err_drv_no_rocm_device_lib) << 2 << ABIVer.toString(); 97481ad6265SDimitry Andric return false; 97581ad6265SDimitry Andric } 97681ad6265SDimitry Andric return true; 9775ffd83dbSDimitry Andric } 9785ffd83dbSDimitry Andric 979fe6060f1SDimitry Andric llvm::SmallVector<std::string, 12> 980fe6060f1SDimitry Andric RocmInstallationDetector::getCommonBitcodeLibs( 981fe6060f1SDimitry Andric const llvm::opt::ArgList &DriverArgs, StringRef LibDeviceFile, bool Wave64, 982fe6060f1SDimitry Andric bool DAZ, bool FiniteOnly, bool UnsafeMathOpt, bool FastRelaxedMath, 98381ad6265SDimitry Andric bool CorrectSqrt, DeviceLibABIVersion ABIVer, bool isOpenMP = false) const { 984fe6060f1SDimitry Andric llvm::SmallVector<std::string, 12> BCLibs; 9855ffd83dbSDimitry Andric 986fe6060f1SDimitry Andric auto AddBCLib = [&](StringRef BCFile) { BCLibs.push_back(BCFile.str()); }; 9875ffd83dbSDimitry Andric 988fe6060f1SDimitry Andric AddBCLib(getOCMLPath()); 9895f757f3fSDimitry Andric if (!isOpenMP) 990fe6060f1SDimitry Andric AddBCLib(getOCKLPath()); 991fe6060f1SDimitry Andric AddBCLib(getDenormalsAreZeroPath(DAZ)); 992fe6060f1SDimitry Andric AddBCLib(getUnsafeMathPath(UnsafeMathOpt || FastRelaxedMath)); 993fe6060f1SDimitry Andric AddBCLib(getFiniteOnlyPath(FiniteOnly || FastRelaxedMath)); 994fe6060f1SDimitry Andric AddBCLib(getCorrectlyRoundedSqrtPath(CorrectSqrt)); 995fe6060f1SDimitry Andric AddBCLib(getWavefrontSize64Path(Wave64)); 996fe6060f1SDimitry Andric AddBCLib(LibDeviceFile); 99781ad6265SDimitry Andric auto ABIVerPath = getABIVersionPath(ABIVer); 99881ad6265SDimitry Andric if (!ABIVerPath.empty()) 99981ad6265SDimitry Andric AddBCLib(ABIVerPath); 10005ffd83dbSDimitry Andric 1001fe6060f1SDimitry Andric return BCLibs; 10025ffd83dbSDimitry Andric } 1003e8d8bef9SDimitry Andric 100469ade1e0SDimitry Andric llvm::SmallVector<std::string, 12> 100569ade1e0SDimitry Andric ROCMToolChain::getCommonDeviceLibNames(const llvm::opt::ArgList &DriverArgs, 100681ad6265SDimitry Andric const std::string &GPUArch, 100781ad6265SDimitry Andric bool isOpenMP) const { 100869ade1e0SDimitry Andric auto Kind = llvm::AMDGPU::parseArchAMDGCN(GPUArch); 100969ade1e0SDimitry Andric const StringRef CanonArch = llvm::AMDGPU::getArchNameAMDGCN(Kind); 101069ade1e0SDimitry Andric 101106c3fb27SDimitry Andric StringRef LibDeviceFile = RocmInstallation->getLibDeviceFile(CanonArch); 101281ad6265SDimitry Andric auto ABIVer = DeviceLibABIVersion::fromCodeObjectVersion( 101381ad6265SDimitry Andric getAMDGPUCodeObjectVersion(getDriver(), DriverArgs)); 101406c3fb27SDimitry Andric if (!RocmInstallation->checkCommonBitcodeLibs(CanonArch, LibDeviceFile, 101581ad6265SDimitry Andric ABIVer)) 101669ade1e0SDimitry Andric return {}; 101769ade1e0SDimitry Andric 101869ade1e0SDimitry Andric // If --hip-device-lib is not set, add the default bitcode libraries. 101969ade1e0SDimitry Andric // TODO: There are way too many flags that change this. Do we need to check 102069ade1e0SDimitry Andric // them all? 102169ade1e0SDimitry Andric bool DAZ = DriverArgs.hasFlag(options::OPT_fgpu_flush_denormals_to_zero, 102269ade1e0SDimitry Andric options::OPT_fno_gpu_flush_denormals_to_zero, 102369ade1e0SDimitry Andric getDefaultDenormsAreZeroForTarget(Kind)); 102469ade1e0SDimitry Andric bool FiniteOnly = DriverArgs.hasFlag( 102569ade1e0SDimitry Andric options::OPT_ffinite_math_only, options::OPT_fno_finite_math_only, false); 102669ade1e0SDimitry Andric bool UnsafeMathOpt = 102769ade1e0SDimitry Andric DriverArgs.hasFlag(options::OPT_funsafe_math_optimizations, 102869ade1e0SDimitry Andric options::OPT_fno_unsafe_math_optimizations, false); 102969ade1e0SDimitry Andric bool FastRelaxedMath = DriverArgs.hasFlag(options::OPT_ffast_math, 103069ade1e0SDimitry Andric options::OPT_fno_fast_math, false); 103169ade1e0SDimitry Andric bool CorrectSqrt = DriverArgs.hasFlag( 103269ade1e0SDimitry Andric options::OPT_fhip_fp32_correctly_rounded_divide_sqrt, 103381ad6265SDimitry Andric options::OPT_fno_hip_fp32_correctly_rounded_divide_sqrt, true); 103469ade1e0SDimitry Andric bool Wave64 = isWave64(DriverArgs, Kind); 103569ade1e0SDimitry Andric 103606c3fb27SDimitry Andric return RocmInstallation->getCommonBitcodeLibs( 103769ade1e0SDimitry Andric DriverArgs, LibDeviceFile, Wave64, DAZ, FiniteOnly, UnsafeMathOpt, 103881ad6265SDimitry Andric FastRelaxedMath, CorrectSqrt, ABIVer, isOpenMP); 103969ade1e0SDimitry Andric } 1040