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 RocmInstallationDetector::RocmInstallationDetector(
25     const Driver &D, const llvm::Triple &HostTriple,
26     const llvm::opt::ArgList &Args)
27     : D(D) {
28   struct Candidate {
29     std::string Path;
30     bool StrictChecking;
31 
32     Candidate(std::string Path, bool StrictChecking = false)
33         : Path(Path), StrictChecking(StrictChecking) {}
34   };
35 
36   SmallVector<Candidate, 4> Candidates;
37 
38   if (Args.hasArg(clang::driver::options::OPT_rocm_path_EQ)) {
39     Candidates.emplace_back(
40         Args.getLastArgValue(clang::driver::options::OPT_rocm_path_EQ).str());
41   } else {
42     // Try to find relative to the compiler binary.
43     const char *InstallDir = D.getInstalledDir();
44 
45     // Check both a normal Unix prefix position of the clang binary, as well as
46     // the Windows-esque layout the ROCm packages use with the host architecture
47     // subdirectory of bin.
48 
49     StringRef ParentDir = llvm::sys::path::parent_path(InstallDir);
50     if (ParentDir == HostTriple.getArchName())
51       ParentDir = llvm::sys::path::parent_path(ParentDir);
52 
53     if (ParentDir == "bin") {
54       Candidates.emplace_back(llvm::sys::path::parent_path(ParentDir).str(),
55                               /*StrictChecking=*/true);
56     }
57 
58     Candidates.emplace_back(D.SysRoot + "/opt/rocm");
59   }
60 
61   bool NoBuiltinLibs = Args.hasArg(options::OPT_nogpulib);
62 
63   for (const auto &Candidate : Candidates) {
64     InstallPath = Candidate.Path;
65     if (InstallPath.empty() || !D.getVFS().exists(InstallPath))
66       continue;
67 
68     // FIXME: The install path situation is a real mess.
69 
70     // For a cmake install, these are placed directly in
71     // ${INSTALL_PREFIX}/lib
72 
73     // In the separate OpenCL builds, the bitcode libraries are placed in
74     // ${OPENCL_ROOT}/lib/x86_64/bitcode/*
75 
76     // For the rocm installed packages, these are placed at
77     // /opt/rocm/opencl/lib/x86_64/bitcode
78 
79     // An additional copy is installed, in scattered locations between
80     // /opt/rocm/hcc/rocdl/oclc
81     // /opt/rocm/hcc/rocdl/ockl
82     // /opt/rocm/hcc/rocdl/lib
83     //
84     // Yet another complete set is installed to
85     // /opt/rocm/hcc/rocdl/lib
86 
87     // For now just recognize the opencl package layout.
88 
89     // BinPath = InstallPath + "/bin";
90     llvm::sys::path::append(IncludePath, InstallPath, "include");
91     llvm::sys::path::append(LibDevicePath, InstallPath, "lib");
92 
93     auto &FS = D.getVFS();
94 
95     // We don't need the include path for OpenCL, since clang already ships with
96     // the default header.
97 
98     bool CheckLibDevice = (!NoBuiltinLibs || Candidate.StrictChecking);
99     if (CheckLibDevice && !FS.exists(LibDevicePath))
100       continue;
101 
102     const StringRef Suffix(".amdgcn.bc");
103 
104     std::error_code EC;
105     for (llvm::sys::fs::directory_iterator LI(LibDevicePath, EC), LE;
106          !EC && LI != LE; LI = LI.increment(EC)) {
107       StringRef FilePath = LI->path();
108       StringRef FileName = llvm::sys::path::filename(FilePath);
109       if (!FileName.endswith(Suffix))
110         continue;
111 
112       StringRef BaseName = FileName.drop_back(Suffix.size());
113 
114       if (BaseName == "ocml") {
115         OCML = FilePath;
116       } else if (BaseName == "ockl") {
117         OCKL = FilePath;
118       } else if (BaseName == "opencl") {
119         OpenCL = FilePath;
120       } else if (BaseName == "hip") {
121         HIP = FilePath;
122       } else if (BaseName == "oclc_finite_only_off") {
123         FiniteOnly.Off = FilePath;
124       } else if (BaseName == "oclc_finite_only_on") {
125         FiniteOnly.On = FilePath;
126       } else if (BaseName == "oclc_daz_opt_on") {
127         DenormalsAreZero.On = FilePath;
128       } else if (BaseName == "oclc_daz_opt_off") {
129         DenormalsAreZero.Off = FilePath;
130       } else if (BaseName == "oclc_correctly_rounded_sqrt_on") {
131         CorrectlyRoundedSqrt.On = FilePath;
132       } else if (BaseName == "oclc_correctly_rounded_sqrt_off") {
133         CorrectlyRoundedSqrt.Off = FilePath;
134       } else if (BaseName == "oclc_unsafe_math_on") {
135         UnsafeMath.On = FilePath;
136       } else if (BaseName == "oclc_unsafe_math_off") {
137         UnsafeMath.Off = FilePath;
138       } else if (BaseName == "oclc_wavefrontsize64_on") {
139         WavefrontSize64.On = FilePath;
140       } else if (BaseName == "oclc_wavefrontsize64_off") {
141         WavefrontSize64.Off = FilePath;
142       } else {
143         // Process all bitcode filenames that look like
144         // ocl_isa_version_XXX.amdgcn.bc
145         const StringRef DeviceLibPrefix = "oclc_isa_version_";
146         if (!BaseName.startswith(DeviceLibPrefix))
147           continue;
148 
149         StringRef IsaVersionNumber =
150             BaseName.drop_front(DeviceLibPrefix.size());
151 
152         llvm::Twine GfxName = Twine("gfx") + IsaVersionNumber;
153         SmallString<8> Tmp;
154         LibDeviceMap.insert(
155             std::make_pair(GfxName.toStringRef(Tmp), FilePath.str()));
156       }
157     }
158 
159     if (!NoBuiltinLibs) {
160       // Check that the required non-target libraries are all available.
161       if (!allGenericLibsValid())
162         continue;
163 
164       // Check that we have found at least one libdevice that we can link in if
165       // -nobuiltinlib hasn't been specified.
166       if (LibDeviceMap.empty())
167         continue;
168     }
169 
170     IsValid = true;
171     break;
172   }
173 }
174 
175 void RocmInstallationDetector::print(raw_ostream &OS) const {
176   if (isValid())
177     OS << "Found ROCm installation: " << InstallPath << '\n';
178 }
179 
180 void amdgpu::Linker::ConstructJob(Compilation &C, const JobAction &JA,
181                                   const InputInfo &Output,
182                                   const InputInfoList &Inputs,
183                                   const ArgList &Args,
184                                   const char *LinkingOutput) const {
185 
186   std::string Linker = getToolChain().GetProgramPath(getShortName());
187   ArgStringList CmdArgs;
188   AddLinkerInputs(getToolChain(), Inputs, Args, CmdArgs, JA);
189   CmdArgs.push_back("-shared");
190   CmdArgs.push_back("-o");
191   CmdArgs.push_back(Output.getFilename());
192   C.addCommand(std::make_unique<Command>(JA, *this, Args.MakeArgString(Linker),
193                                           CmdArgs, Inputs));
194 }
195 
196 void amdgpu::getAMDGPUTargetFeatures(const Driver &D,
197                                      const llvm::opt::ArgList &Args,
198                                      std::vector<StringRef> &Features) {
199   if (const Arg *dAbi = Args.getLastArg(options::OPT_mamdgpu_debugger_abi))
200     D.Diag(diag::err_drv_clang_unsupported) << dAbi->getAsString(Args);
201 
202   if (Args.getLastArg(options::OPT_mwavefrontsize64)) {
203     Features.push_back("-wavefrontsize16");
204     Features.push_back("-wavefrontsize32");
205     Features.push_back("+wavefrontsize64");
206   }
207   if (Args.getLastArg(options::OPT_mno_wavefrontsize64)) {
208     Features.push_back("-wavefrontsize16");
209     Features.push_back("+wavefrontsize32");
210     Features.push_back("-wavefrontsize64");
211   }
212 
213   handleTargetFeaturesGroup(
214     Args, Features, options::OPT_m_amdgpu_Features_Group);
215 }
216 
217 /// AMDGPU Toolchain
218 AMDGPUToolChain::AMDGPUToolChain(const Driver &D, const llvm::Triple &Triple,
219                                  const ArgList &Args)
220     : Generic_ELF(D, Triple, Args),
221       OptionsDefault({{options::OPT_O, "3"},
222                       {options::OPT_cl_std_EQ, "CL1.2"}}) {}
223 
224 Tool *AMDGPUToolChain::buildLinker() const {
225   return new tools::amdgpu::Linker(*this);
226 }
227 
228 DerivedArgList *
229 AMDGPUToolChain::TranslateArgs(const DerivedArgList &Args, StringRef BoundArch,
230                                Action::OffloadKind DeviceOffloadKind) const {
231 
232   DerivedArgList *DAL =
233       Generic_ELF::TranslateArgs(Args, BoundArch, DeviceOffloadKind);
234 
235   // Do nothing if not OpenCL (-x cl)
236   if (!Args.getLastArgValue(options::OPT_x).equals("cl"))
237     return DAL;
238 
239   if (!DAL)
240     DAL = new DerivedArgList(Args.getBaseArgs());
241   for (auto *A : Args)
242     DAL->append(A);
243 
244   const OptTable &Opts = getDriver().getOpts();
245 
246   // Phase 1 (.cl -> .bc)
247   if (Args.hasArg(options::OPT_c) && Args.hasArg(options::OPT_emit_llvm)) {
248     DAL->AddFlagArg(nullptr, Opts.getOption(getTriple().isArch64Bit()
249                                                 ? options::OPT_m64
250                                                 : options::OPT_m32));
251 
252     // Have to check OPT_O4, OPT_O0 & OPT_Ofast separately
253     // as they defined that way in Options.td
254     if (!Args.hasArg(options::OPT_O, options::OPT_O0, options::OPT_O4,
255                      options::OPT_Ofast))
256       DAL->AddJoinedArg(nullptr, Opts.getOption(options::OPT_O),
257                         getOptionDefault(options::OPT_O));
258   }
259 
260   return DAL;
261 }
262 
263 bool AMDGPUToolChain::getDefaultDenormsAreZeroForTarget(
264     llvm::AMDGPU::GPUKind Kind) {
265   const unsigned ArchAttr = llvm::AMDGPU::getArchAttrAMDGCN(Kind);
266 
267   // Default to enabling f32 denormals by default on subtargets where fma is
268   // fast with denormals
269   const bool BothDenormAndFMAFast =
270       (ArchAttr & llvm::AMDGPU::FEATURE_FAST_FMA_F32) &&
271       (ArchAttr & llvm::AMDGPU::FEATURE_FAST_DENORMAL_F32);
272   return !BothDenormAndFMAFast;
273 }
274 
275 llvm::DenormalMode AMDGPUToolChain::getDefaultDenormalModeForType(
276     const llvm::opt::ArgList &DriverArgs, const JobAction &JA,
277     const llvm::fltSemantics *FPType) const {
278   // Denormals should always be enabled for f16 and f64.
279   if (!FPType || FPType != &llvm::APFloat::IEEEsingle())
280     return llvm::DenormalMode::getIEEE();
281 
282   if (JA.getOffloadingDeviceKind() == Action::OFK_HIP ||
283       JA.getOffloadingDeviceKind() == Action::OFK_Cuda) {
284     auto Kind = llvm::AMDGPU::parseArchAMDGCN(JA.getOffloadingArch());
285     if (FPType && FPType == &llvm::APFloat::IEEEsingle() &&
286         DriverArgs.hasFlag(options::OPT_fcuda_flush_denormals_to_zero,
287                            options::OPT_fno_cuda_flush_denormals_to_zero,
288                            getDefaultDenormsAreZeroForTarget(Kind)))
289       return llvm::DenormalMode::getPreserveSign();
290 
291     return llvm::DenormalMode::getIEEE();
292   }
293 
294   const StringRef GpuArch = DriverArgs.getLastArgValue(options::OPT_mcpu_EQ);
295   auto Kind = llvm::AMDGPU::parseArchAMDGCN(GpuArch);
296 
297   // TODO: There are way too many flags that change this. Do we need to check
298   // them all?
299   bool DAZ = DriverArgs.hasArg(options::OPT_cl_denorms_are_zero) ||
300              getDefaultDenormsAreZeroForTarget(Kind);
301 
302   // Outputs are flushed to zero (FTZ), preserving sign. Denormal inputs are
303   // also implicit treated as zero (DAZ).
304   return DAZ ? llvm::DenormalMode::getPreserveSign() :
305                llvm::DenormalMode::getIEEE();
306 }
307 
308 /// ROCM Toolchain
309 ROCMToolChain::ROCMToolChain(const Driver &D, const llvm::Triple &Triple,
310                              const ArgList &Args)
311   : AMDGPUToolChain(D, Triple, Args),
312     RocmInstallation(D, Triple, Args) { }
313 
314 void AMDGPUToolChain::addClangTargetOptions(
315     const llvm::opt::ArgList &DriverArgs,
316     llvm::opt::ArgStringList &CC1Args,
317     Action::OffloadKind DeviceOffloadingKind) const {
318   // Default to "hidden" visibility, as object level linking will not be
319   // supported for the foreseeable future.
320   if (!DriverArgs.hasArg(options::OPT_fvisibility_EQ,
321                          options::OPT_fvisibility_ms_compat)) {
322     CC1Args.push_back("-fvisibility");
323     CC1Args.push_back("hidden");
324     CC1Args.push_back("-fapply-global-visibility-to-externs");
325   }
326 }
327 
328 void ROCMToolChain::addClangTargetOptions(
329     const llvm::opt::ArgList &DriverArgs, llvm::opt::ArgStringList &CC1Args,
330     Action::OffloadKind DeviceOffloadingKind) const {
331   AMDGPUToolChain::addClangTargetOptions(DriverArgs, CC1Args,
332                                          DeviceOffloadingKind);
333 
334   if (DriverArgs.hasArg(options::OPT_nogpulib))
335     return;
336 
337   if (!RocmInstallation.isValid()) {
338     getDriver().Diag(diag::err_drv_no_rocm_installation);
339     return;
340   }
341 
342   // Get the device name and canonicalize it
343   const StringRef GpuArch = DriverArgs.getLastArgValue(options::OPT_mcpu_EQ);
344   auto Kind = llvm::AMDGPU::parseArchAMDGCN(GpuArch);
345   const StringRef CanonArch = llvm::AMDGPU::getArchNameAMDGCN(Kind);
346   std::string LibDeviceFile = RocmInstallation.getLibDeviceFile(CanonArch);
347   if (LibDeviceFile.empty()) {
348     getDriver().Diag(diag::err_drv_no_rocm_device_lib) << GpuArch;
349     return;
350   }
351 
352   const unsigned ArchAttr = llvm::AMDGPU::getArchAttrAMDGCN(Kind);
353   static bool HasWave32 = (ArchAttr & llvm::AMDGPU::FEATURE_WAVE32);
354 
355   bool Wave64 = !HasWave32 || DriverArgs.hasFlag(
356     options::OPT_mwavefrontsize64, options::OPT_mno_wavefrontsize64, false);
357 
358   // TODO: There are way too many flags that change this. Do we need to check
359   // them all?
360   bool DAZ = DriverArgs.hasArg(options::OPT_cl_denorms_are_zero) ||
361              getDefaultDenormsAreZeroForTarget(Kind);
362   bool FiniteOnly = DriverArgs.hasArg(options::OPT_cl_finite_math_only);
363 
364   bool UnsafeMathOpt =
365       DriverArgs.hasArg(options::OPT_cl_unsafe_math_optimizations);
366   bool FastRelaxedMath = DriverArgs.hasArg(options::OPT_cl_fast_relaxed_math);
367   bool CorrectSqrt =
368       DriverArgs.hasArg(options::OPT_cl_fp32_correctly_rounded_divide_sqrt);
369 
370   // Add the OpenCL specific bitcode library.
371   CC1Args.push_back("-mlink-builtin-bitcode");
372   CC1Args.push_back(DriverArgs.MakeArgString(RocmInstallation.getOpenCLPath()));
373 
374   // Add the generic set of libraries.
375   RocmInstallation.addCommonBitcodeLibCC1Args(
376       DriverArgs, CC1Args, LibDeviceFile, Wave64, DAZ, FiniteOnly,
377       UnsafeMathOpt, FastRelaxedMath, CorrectSqrt);
378 }
379 
380 void RocmInstallationDetector::addCommonBitcodeLibCC1Args(
381     const llvm::opt::ArgList &DriverArgs, llvm::opt::ArgStringList &CC1Args,
382     StringRef LibDeviceFile, bool Wave64, bool DAZ, bool FiniteOnly,
383     bool UnsafeMathOpt, bool FastRelaxedMath, bool CorrectSqrt) const {
384   static const char LinkBitcodeFlag[] = "-mlink-builtin-bitcode";
385 
386   CC1Args.push_back(LinkBitcodeFlag);
387   CC1Args.push_back(DriverArgs.MakeArgString(getOCMLPath()));
388 
389   CC1Args.push_back(LinkBitcodeFlag);
390   CC1Args.push_back(DriverArgs.MakeArgString(getOCKLPath()));
391 
392   CC1Args.push_back(LinkBitcodeFlag);
393   CC1Args.push_back(DriverArgs.MakeArgString(getDenormalsAreZeroPath(DAZ)));
394 
395   CC1Args.push_back(LinkBitcodeFlag);
396   CC1Args.push_back(DriverArgs.MakeArgString(
397       getUnsafeMathPath(UnsafeMathOpt || FastRelaxedMath)));
398 
399   CC1Args.push_back(LinkBitcodeFlag);
400   CC1Args.push_back(DriverArgs.MakeArgString(
401       getFiniteOnlyPath(FiniteOnly || FastRelaxedMath)));
402 
403   CC1Args.push_back(LinkBitcodeFlag);
404   CC1Args.push_back(
405       DriverArgs.MakeArgString(getCorrectlyRoundedSqrtPath(CorrectSqrt)));
406 
407   CC1Args.push_back(LinkBitcodeFlag);
408   CC1Args.push_back(DriverArgs.MakeArgString(getWavefrontSize64Path(Wave64)));
409 
410   CC1Args.push_back(LinkBitcodeFlag);
411   CC1Args.push_back(DriverArgs.MakeArgString(LibDeviceFile));
412 }
413