1 //===--- AMDGPU.cpp - AMDGPU ToolChain Implementations ----------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "AMDGPU.h"
10 #include "CommonArgs.h"
11 #include "InputInfo.h"
12 #include "clang/Driver/Compilation.h"
13 #include "clang/Driver/DriverDiagnostic.h"
14 #include "llvm/Option/ArgList.h"
15 #include "llvm/Support/Path.h"
16 #include "llvm/Support/VirtualFileSystem.h"
17 
18 using namespace clang::driver;
19 using namespace clang::driver::tools;
20 using namespace clang::driver::toolchains;
21 using namespace clang;
22 using namespace llvm::opt;
23 
24 void RocmInstallationDetector::scanLibDevicePath() {
25   assert(!LibDevicePath.empty());
26 
27   const StringRef Suffix(".bc");
28   const StringRef Suffix2(".amdgcn.bc");
29 
30   std::error_code EC;
31   for (llvm::vfs::directory_iterator
32            LI = D.getVFS().dir_begin(LibDevicePath, EC),
33            LE;
34        !EC && LI != LE; LI = LI.increment(EC)) {
35     StringRef FilePath = LI->path();
36     StringRef FileName = llvm::sys::path::filename(FilePath);
37     if (!FileName.endswith(Suffix))
38       continue;
39 
40     StringRef BaseName;
41     if (FileName.endswith(Suffix2))
42       BaseName = FileName.drop_back(Suffix2.size());
43     else if (FileName.endswith(Suffix))
44       BaseName = FileName.drop_back(Suffix.size());
45 
46     if (BaseName == "ocml") {
47       OCML = FilePath;
48     } else if (BaseName == "ockl") {
49       OCKL = FilePath;
50     } else if (BaseName == "opencl") {
51       OpenCL = FilePath;
52     } else if (BaseName == "hip") {
53       HIP = FilePath;
54     } else if (BaseName == "oclc_finite_only_off") {
55       FiniteOnly.Off = FilePath;
56     } else if (BaseName == "oclc_finite_only_on") {
57       FiniteOnly.On = FilePath;
58     } else if (BaseName == "oclc_daz_opt_on") {
59       DenormalsAreZero.On = FilePath;
60     } else if (BaseName == "oclc_daz_opt_off") {
61       DenormalsAreZero.Off = FilePath;
62     } else if (BaseName == "oclc_correctly_rounded_sqrt_on") {
63       CorrectlyRoundedSqrt.On = FilePath;
64     } else if (BaseName == "oclc_correctly_rounded_sqrt_off") {
65       CorrectlyRoundedSqrt.Off = FilePath;
66     } else if (BaseName == "oclc_unsafe_math_on") {
67       UnsafeMath.On = FilePath;
68     } else if (BaseName == "oclc_unsafe_math_off") {
69       UnsafeMath.Off = FilePath;
70     } else if (BaseName == "oclc_wavefrontsize64_on") {
71       WavefrontSize64.On = FilePath;
72     } else if (BaseName == "oclc_wavefrontsize64_off") {
73       WavefrontSize64.Off = FilePath;
74     } else {
75       // Process all bitcode filenames that look like
76       // ocl_isa_version_XXX.amdgcn.bc
77       const StringRef DeviceLibPrefix = "oclc_isa_version_";
78       if (!BaseName.startswith(DeviceLibPrefix))
79         continue;
80 
81       StringRef IsaVersionNumber =
82         BaseName.drop_front(DeviceLibPrefix.size());
83 
84       llvm::Twine GfxName = Twine("gfx") + IsaVersionNumber;
85       SmallString<8> Tmp;
86       LibDeviceMap.insert(
87         std::make_pair(GfxName.toStringRef(Tmp), FilePath.str()));
88     }
89   }
90 }
91 
92 RocmInstallationDetector::RocmInstallationDetector(
93     const Driver &D, const llvm::Triple &HostTriple,
94     const llvm::opt::ArgList &Args)
95     : D(D) {
96   struct Candidate {
97     std::string Path;
98     bool StrictChecking;
99 
100     Candidate(std::string Path, bool StrictChecking = false)
101         : Path(Path), StrictChecking(StrictChecking) {}
102   };
103 
104   SmallVector<Candidate, 4> Candidates;
105 
106   if (Args.hasArg(clang::driver::options::OPT_rocm_path_EQ)) {
107     Candidates.emplace_back(
108         Args.getLastArgValue(clang::driver::options::OPT_rocm_path_EQ).str());
109   } else {
110     // Try to find relative to the compiler binary.
111     const char *InstallDir = D.getInstalledDir();
112 
113     // Check both a normal Unix prefix position of the clang binary, as well as
114     // the Windows-esque layout the ROCm packages use with the host architecture
115     // subdirectory of bin.
116 
117     // Strip off directory (usually bin)
118     StringRef ParentDir = llvm::sys::path::parent_path(InstallDir);
119     StringRef ParentName = llvm::sys::path::filename(ParentDir);
120 
121     // Some builds use bin/{host arch}, so go up again.
122     if (ParentName == "bin") {
123       ParentDir = llvm::sys::path::parent_path(ParentDir);
124       ParentName = llvm::sys::path::filename(ParentDir);
125     }
126 
127     if (ParentName == "llvm") {
128       // Some versions of the rocm llvm package install to /opt/rocm/llvm/bin
129       Candidates.emplace_back(llvm::sys::path::parent_path(ParentDir).str(),
130                               /*StrictChecking=*/true);
131     }
132 
133     Candidates.emplace_back(D.SysRoot + "/opt/rocm");
134   }
135 
136   bool NoBuiltinLibs = Args.hasArg(options::OPT_nogpulib);
137 
138   assert(LibDevicePath.empty());
139 
140   if (Args.hasArg(clang::driver::options::OPT_hip_device_lib_path_EQ)) {
141     LibDevicePath
142       = Args.getLastArgValue(clang::driver::options::OPT_hip_device_lib_path_EQ);
143   } else if (const char *LibPathEnv = ::getenv("HIP_DEVICE_LIB_PATH")) {
144     LibDevicePath = LibPathEnv;
145   }
146 
147   auto &FS = D.getVFS();
148   if (!LibDevicePath.empty()) {
149     // Maintain compatability with HIP flag/envvar pointing directly at the
150     // bitcode library directory. This points directly at the library path instead
151     // of the rocm root installation.
152     if (!FS.exists(LibDevicePath))
153       return;
154 
155     scanLibDevicePath();
156     IsValid = allGenericLibsValid() && !LibDeviceMap.empty();
157     return;
158   }
159 
160   for (const auto &Candidate : Candidates) {
161     InstallPath = Candidate.Path;
162     if (InstallPath.empty() || !FS.exists(InstallPath))
163       continue;
164 
165     // The install path situation in old versions of ROCm is a real mess, and
166     // use a different install layout. Multiple copies of the device libraries
167     // exist for each frontend project, and differ depending on which build
168     // system produced the packages. Standalone OpenCL builds also have a
169     // different directory structure from the ROCm OpenCL package.
170     //
171     // The desired structure is (${ROCM_ROOT} or
172     // ${OPENCL_ROOT})/amdgcn/bitcode/*, so try to detect this layout.
173 
174     // BinPath = InstallPath + "/bin";
175     llvm::sys::path::append(IncludePath, InstallPath, "include");
176     llvm::sys::path::append(LibDevicePath, InstallPath, "amdgcn", "bitcode");
177 
178     // We don't need the include path for OpenCL, since clang already ships with
179     // the default header.
180 
181     bool CheckLibDevice = (!NoBuiltinLibs || Candidate.StrictChecking);
182     if (CheckLibDevice && !FS.exists(LibDevicePath))
183       continue;
184 
185     scanLibDevicePath();
186 
187     if (!NoBuiltinLibs) {
188       // Check that the required non-target libraries are all available.
189       if (!allGenericLibsValid())
190         continue;
191 
192       // Check that we have found at least one libdevice that we can link in if
193       // -nobuiltinlib hasn't been specified.
194       if (LibDeviceMap.empty())
195         continue;
196     }
197 
198     IsValid = true;
199     break;
200   }
201 }
202 
203 void RocmInstallationDetector::print(raw_ostream &OS) const {
204   if (isValid())
205     OS << "Found ROCm installation: " << InstallPath << '\n';
206 }
207 
208 void RocmInstallationDetector::AddHIPIncludeArgs(const ArgList &DriverArgs,
209                                                  ArgStringList &CC1Args) const {
210   if (!DriverArgs.hasArg(options::OPT_nobuiltininc)) {
211     // HIP header includes standard library wrapper headers under clang
212     // cuda_wrappers directory. Since these wrapper headers include_next
213     // standard C++ headers, whereas libc++ headers include_next other clang
214     // headers. The include paths have to follow this order:
215     // - wrapper include path
216     // - standard C++ include path
217     // - other clang include path
218     // Since standard C++ and other clang include paths are added in other
219     // places after this function, here we only need to make sure wrapper
220     // include path is added.
221     SmallString<128> P(D.ResourceDir);
222     llvm::sys::path::append(P, "include");
223     llvm::sys::path::append(P, "cuda_wrappers");
224     CC1Args.push_back("-internal-isystem");
225     CC1Args.push_back(DriverArgs.MakeArgString(P));
226   }
227 
228   if (DriverArgs.hasArg(options::OPT_nogpuinc))
229     return;
230 
231   if (!isValid()) {
232     D.Diag(diag::err_drv_no_rocm_installation);
233     return;
234   }
235 
236   CC1Args.push_back("-internal-isystem");
237   CC1Args.push_back(DriverArgs.MakeArgString(getIncludePath()));
238   CC1Args.push_back("-include");
239   CC1Args.push_back("__clang_hip_runtime_wrapper.h");
240 }
241 
242 void amdgpu::Linker::ConstructJob(Compilation &C, const JobAction &JA,
243                                   const InputInfo &Output,
244                                   const InputInfoList &Inputs,
245                                   const ArgList &Args,
246                                   const char *LinkingOutput) const {
247 
248   std::string Linker = getToolChain().GetProgramPath(getShortName());
249   ArgStringList CmdArgs;
250   AddLinkerInputs(getToolChain(), Inputs, Args, CmdArgs, JA);
251   CmdArgs.push_back("-shared");
252   CmdArgs.push_back("-o");
253   CmdArgs.push_back(Output.getFilename());
254   C.addCommand(std::make_unique<Command>(JA, *this, Args.MakeArgString(Linker),
255                                           CmdArgs, Inputs));
256 }
257 
258 void amdgpu::getAMDGPUTargetFeatures(const Driver &D,
259                                      const llvm::opt::ArgList &Args,
260                                      std::vector<StringRef> &Features) {
261   if (const Arg *dAbi = Args.getLastArg(options::OPT_mamdgpu_debugger_abi))
262     D.Diag(diag::err_drv_clang_unsupported) << dAbi->getAsString(Args);
263 
264   if (Args.getLastArg(options::OPT_mwavefrontsize64)) {
265     Features.push_back("-wavefrontsize16");
266     Features.push_back("-wavefrontsize32");
267     Features.push_back("+wavefrontsize64");
268   }
269   if (Args.getLastArg(options::OPT_mno_wavefrontsize64)) {
270     Features.push_back("-wavefrontsize16");
271     Features.push_back("+wavefrontsize32");
272     Features.push_back("-wavefrontsize64");
273   }
274 
275   handleTargetFeaturesGroup(
276     Args, Features, options::OPT_m_amdgpu_Features_Group);
277 }
278 
279 /// AMDGPU Toolchain
280 AMDGPUToolChain::AMDGPUToolChain(const Driver &D, const llvm::Triple &Triple,
281                                  const ArgList &Args)
282     : Generic_ELF(D, Triple, Args),
283       OptionsDefault({{options::OPT_O, "3"},
284                       {options::OPT_cl_std_EQ, "CL1.2"}}) {}
285 
286 Tool *AMDGPUToolChain::buildLinker() const {
287   return new tools::amdgpu::Linker(*this);
288 }
289 
290 DerivedArgList *
291 AMDGPUToolChain::TranslateArgs(const DerivedArgList &Args, StringRef BoundArch,
292                                Action::OffloadKind DeviceOffloadKind) const {
293 
294   DerivedArgList *DAL =
295       Generic_ELF::TranslateArgs(Args, BoundArch, DeviceOffloadKind);
296 
297   // Do nothing if not OpenCL (-x cl)
298   if (!Args.getLastArgValue(options::OPT_x).equals("cl"))
299     return DAL;
300 
301   if (!DAL)
302     DAL = new DerivedArgList(Args.getBaseArgs());
303   for (auto *A : Args)
304     DAL->append(A);
305 
306   const OptTable &Opts = getDriver().getOpts();
307 
308   // Phase 1 (.cl -> .bc)
309   if (Args.hasArg(options::OPT_c) && Args.hasArg(options::OPT_emit_llvm)) {
310     DAL->AddFlagArg(nullptr, Opts.getOption(getTriple().isArch64Bit()
311                                                 ? options::OPT_m64
312                                                 : options::OPT_m32));
313 
314     // Have to check OPT_O4, OPT_O0 & OPT_Ofast separately
315     // as they defined that way in Options.td
316     if (!Args.hasArg(options::OPT_O, options::OPT_O0, options::OPT_O4,
317                      options::OPT_Ofast))
318       DAL->AddJoinedArg(nullptr, Opts.getOption(options::OPT_O),
319                         getOptionDefault(options::OPT_O));
320   }
321 
322   return DAL;
323 }
324 
325 bool AMDGPUToolChain::getDefaultDenormsAreZeroForTarget(
326     llvm::AMDGPU::GPUKind Kind) {
327 
328   // Assume nothing without a specific target.
329   if (Kind == llvm::AMDGPU::GK_NONE)
330     return false;
331 
332   const unsigned ArchAttr = llvm::AMDGPU::getArchAttrAMDGCN(Kind);
333 
334   // Default to enabling f32 denormals by default on subtargets where fma is
335   // fast with denormals
336   const bool BothDenormAndFMAFast =
337       (ArchAttr & llvm::AMDGPU::FEATURE_FAST_FMA_F32) &&
338       (ArchAttr & llvm::AMDGPU::FEATURE_FAST_DENORMAL_F32);
339   return !BothDenormAndFMAFast;
340 }
341 
342 llvm::DenormalMode AMDGPUToolChain::getDefaultDenormalModeForType(
343     const llvm::opt::ArgList &DriverArgs, const JobAction &JA,
344     const llvm::fltSemantics *FPType) const {
345   // Denormals should always be enabled for f16 and f64.
346   if (!FPType || FPType != &llvm::APFloat::IEEEsingle())
347     return llvm::DenormalMode::getIEEE();
348 
349   if (JA.getOffloadingDeviceKind() == Action::OFK_HIP ||
350       JA.getOffloadingDeviceKind() == Action::OFK_Cuda) {
351     auto Kind = llvm::AMDGPU::parseArchAMDGCN(JA.getOffloadingArch());
352     if (FPType && FPType == &llvm::APFloat::IEEEsingle() &&
353         DriverArgs.hasFlag(options::OPT_fcuda_flush_denormals_to_zero,
354                            options::OPT_fno_cuda_flush_denormals_to_zero,
355                            getDefaultDenormsAreZeroForTarget(Kind)))
356       return llvm::DenormalMode::getPreserveSign();
357 
358     return llvm::DenormalMode::getIEEE();
359   }
360 
361   const StringRef GpuArch = DriverArgs.getLastArgValue(options::OPT_mcpu_EQ);
362   auto Kind = llvm::AMDGPU::parseArchAMDGCN(GpuArch);
363 
364   // TODO: There are way too many flags that change this. Do we need to check
365   // them all?
366   bool DAZ = DriverArgs.hasArg(options::OPT_cl_denorms_are_zero) ||
367              getDefaultDenormsAreZeroForTarget(Kind);
368 
369   // Outputs are flushed to zero (FTZ), preserving sign. Denormal inputs are
370   // also implicit treated as zero (DAZ).
371   return DAZ ? llvm::DenormalMode::getPreserveSign() :
372                llvm::DenormalMode::getIEEE();
373 }
374 
375 bool AMDGPUToolChain::isWave64(const llvm::opt::ArgList &DriverArgs,
376                                llvm::AMDGPU::GPUKind Kind) {
377   const unsigned ArchAttr = llvm::AMDGPU::getArchAttrAMDGCN(Kind);
378   static bool HasWave32 = (ArchAttr & llvm::AMDGPU::FEATURE_WAVE32);
379 
380   return !HasWave32 || DriverArgs.hasFlag(
381     options::OPT_mwavefrontsize64, options::OPT_mno_wavefrontsize64, false);
382 }
383 
384 
385 /// ROCM Toolchain
386 ROCMToolChain::ROCMToolChain(const Driver &D, const llvm::Triple &Triple,
387                              const ArgList &Args)
388   : AMDGPUToolChain(D, Triple, Args),
389     RocmInstallation(D, Triple, Args) { }
390 
391 void AMDGPUToolChain::addClangTargetOptions(
392     const llvm::opt::ArgList &DriverArgs,
393     llvm::opt::ArgStringList &CC1Args,
394     Action::OffloadKind DeviceOffloadingKind) const {
395   // Default to "hidden" visibility, as object level linking will not be
396   // supported for the foreseeable future.
397   if (!DriverArgs.hasArg(options::OPT_fvisibility_EQ,
398                          options::OPT_fvisibility_ms_compat)) {
399     CC1Args.push_back("-fvisibility");
400     CC1Args.push_back("hidden");
401     CC1Args.push_back("-fapply-global-visibility-to-externs");
402   }
403 }
404 
405 void ROCMToolChain::addClangTargetOptions(
406     const llvm::opt::ArgList &DriverArgs, llvm::opt::ArgStringList &CC1Args,
407     Action::OffloadKind DeviceOffloadingKind) const {
408   AMDGPUToolChain::addClangTargetOptions(DriverArgs, CC1Args,
409                                          DeviceOffloadingKind);
410 
411   // For the OpenCL case where there is no offload target, accept -nostdlib to
412   // disable bitcode linking.
413   if (DeviceOffloadingKind == Action::OFK_None &&
414       DriverArgs.hasArg(options::OPT_nostdlib))
415     return;
416 
417   if (DriverArgs.hasArg(options::OPT_nogpulib))
418     return;
419 
420   if (!RocmInstallation.isValid()) {
421     getDriver().Diag(diag::err_drv_no_rocm_installation);
422     return;
423   }
424 
425   // Get the device name and canonicalize it
426   const StringRef GpuArch = DriverArgs.getLastArgValue(options::OPT_mcpu_EQ);
427   auto Kind = llvm::AMDGPU::parseArchAMDGCN(GpuArch);
428   const StringRef CanonArch = llvm::AMDGPU::getArchNameAMDGCN(Kind);
429   std::string LibDeviceFile = RocmInstallation.getLibDeviceFile(CanonArch);
430   if (LibDeviceFile.empty()) {
431     getDriver().Diag(diag::err_drv_no_rocm_device_lib) << GpuArch;
432     return;
433   }
434 
435   bool Wave64 = isWave64(DriverArgs, Kind);
436 
437   // TODO: There are way too many flags that change this. Do we need to check
438   // them all?
439   bool DAZ = DriverArgs.hasArg(options::OPT_cl_denorms_are_zero) ||
440              getDefaultDenormsAreZeroForTarget(Kind);
441   bool FiniteOnly = DriverArgs.hasArg(options::OPT_cl_finite_math_only);
442 
443   bool UnsafeMathOpt =
444       DriverArgs.hasArg(options::OPT_cl_unsafe_math_optimizations);
445   bool FastRelaxedMath = DriverArgs.hasArg(options::OPT_cl_fast_relaxed_math);
446   bool CorrectSqrt =
447       DriverArgs.hasArg(options::OPT_cl_fp32_correctly_rounded_divide_sqrt);
448 
449   // Add the OpenCL specific bitcode library.
450   CC1Args.push_back("-mlink-builtin-bitcode");
451   CC1Args.push_back(DriverArgs.MakeArgString(RocmInstallation.getOpenCLPath()));
452 
453   // Add the generic set of libraries.
454   RocmInstallation.addCommonBitcodeLibCC1Args(
455       DriverArgs, CC1Args, LibDeviceFile, Wave64, DAZ, FiniteOnly,
456       UnsafeMathOpt, FastRelaxedMath, CorrectSqrt);
457 }
458 
459 void RocmInstallationDetector::addCommonBitcodeLibCC1Args(
460     const llvm::opt::ArgList &DriverArgs, llvm::opt::ArgStringList &CC1Args,
461     StringRef LibDeviceFile, bool Wave64, bool DAZ, bool FiniteOnly,
462     bool UnsafeMathOpt, bool FastRelaxedMath, bool CorrectSqrt) const {
463   static const char LinkBitcodeFlag[] = "-mlink-builtin-bitcode";
464 
465   CC1Args.push_back(LinkBitcodeFlag);
466   CC1Args.push_back(DriverArgs.MakeArgString(getOCMLPath()));
467 
468   CC1Args.push_back(LinkBitcodeFlag);
469   CC1Args.push_back(DriverArgs.MakeArgString(getOCKLPath()));
470 
471   CC1Args.push_back(LinkBitcodeFlag);
472   CC1Args.push_back(DriverArgs.MakeArgString(getDenormalsAreZeroPath(DAZ)));
473 
474   CC1Args.push_back(LinkBitcodeFlag);
475   CC1Args.push_back(DriverArgs.MakeArgString(
476       getUnsafeMathPath(UnsafeMathOpt || FastRelaxedMath)));
477 
478   CC1Args.push_back(LinkBitcodeFlag);
479   CC1Args.push_back(DriverArgs.MakeArgString(
480       getFiniteOnlyPath(FiniteOnly || FastRelaxedMath)));
481 
482   CC1Args.push_back(LinkBitcodeFlag);
483   CC1Args.push_back(
484       DriverArgs.MakeArgString(getCorrectlyRoundedSqrtPath(CorrectSqrt)));
485 
486   CC1Args.push_back(LinkBitcodeFlag);
487   CC1Args.push_back(DriverArgs.MakeArgString(getWavefrontSize64Path(Wave64)));
488 
489   CC1Args.push_back(LinkBitcodeFlag);
490   CC1Args.push_back(DriverArgs.MakeArgString(LibDeviceFile));
491 }
492