xref: /freebsd-src/contrib/llvm-project/clang/lib/Driver/ToolChains/AMDGPU.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
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