xref: /freebsd-src/contrib/llvm-project/clang/lib/Driver/ToolChains/HIPSPV.cpp (revision 0eae32dcef82f6f06de6419a0d623d7def0cc8f6)
1*0eae32dcSDimitry Andric //===--- HIPSPV.cpp - HIPSPV ToolChain Implementation -----------*- C++ -*-===//
2*0eae32dcSDimitry Andric //
3*0eae32dcSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*0eae32dcSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5*0eae32dcSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*0eae32dcSDimitry Andric //
7*0eae32dcSDimitry Andric //===----------------------------------------------------------------------===//
8*0eae32dcSDimitry Andric 
9*0eae32dcSDimitry Andric #include "HIPSPV.h"
10*0eae32dcSDimitry Andric #include "CommonArgs.h"
11*0eae32dcSDimitry Andric #include "HIPUtility.h"
12*0eae32dcSDimitry Andric #include "clang/Driver/Compilation.h"
13*0eae32dcSDimitry Andric #include "clang/Driver/Driver.h"
14*0eae32dcSDimitry Andric #include "clang/Driver/DriverDiagnostic.h"
15*0eae32dcSDimitry Andric #include "clang/Driver/InputInfo.h"
16*0eae32dcSDimitry Andric #include "clang/Driver/Options.h"
17*0eae32dcSDimitry Andric #include "llvm/Support/FileSystem.h"
18*0eae32dcSDimitry Andric #include "llvm/Support/Path.h"
19*0eae32dcSDimitry Andric 
20*0eae32dcSDimitry Andric using namespace clang::driver;
21*0eae32dcSDimitry Andric using namespace clang::driver::toolchains;
22*0eae32dcSDimitry Andric using namespace clang::driver::tools;
23*0eae32dcSDimitry Andric using namespace clang;
24*0eae32dcSDimitry Andric using namespace llvm::opt;
25*0eae32dcSDimitry Andric 
26*0eae32dcSDimitry Andric // Convenience function for creating temporary file for both modes of
27*0eae32dcSDimitry Andric // isSaveTempsEnabled().
28*0eae32dcSDimitry Andric static const char *getTempFile(Compilation &C, StringRef Prefix,
29*0eae32dcSDimitry Andric                                StringRef Extension) {
30*0eae32dcSDimitry Andric   if (C.getDriver().isSaveTempsEnabled()) {
31*0eae32dcSDimitry Andric     return C.getArgs().MakeArgString(Prefix + "." + Extension);
32*0eae32dcSDimitry Andric   }
33*0eae32dcSDimitry Andric   auto TmpFile = C.getDriver().GetTemporaryPath(Prefix, Extension);
34*0eae32dcSDimitry Andric   return C.addTempFile(C.getArgs().MakeArgString(TmpFile));
35*0eae32dcSDimitry Andric }
36*0eae32dcSDimitry Andric 
37*0eae32dcSDimitry Andric // Locates HIP pass plugin.
38*0eae32dcSDimitry Andric static std::string findPassPlugin(const Driver &D,
39*0eae32dcSDimitry Andric                                   const llvm::opt::ArgList &Args) {
40*0eae32dcSDimitry Andric   StringRef Path = Args.getLastArgValue(options::OPT_hipspv_pass_plugin_EQ);
41*0eae32dcSDimitry Andric   if (!Path.empty()) {
42*0eae32dcSDimitry Andric     if (llvm::sys::fs::exists(Path))
43*0eae32dcSDimitry Andric       return Path.str();
44*0eae32dcSDimitry Andric     D.Diag(diag::err_drv_no_such_file) << Path;
45*0eae32dcSDimitry Andric   }
46*0eae32dcSDimitry Andric 
47*0eae32dcSDimitry Andric   StringRef hipPath = Args.getLastArgValue(options::OPT_hip_path_EQ);
48*0eae32dcSDimitry Andric   if (!hipPath.empty()) {
49*0eae32dcSDimitry Andric     SmallString<128> PluginPath(hipPath);
50*0eae32dcSDimitry Andric     llvm::sys::path::append(PluginPath, "lib", "libLLVMHipSpvPasses.so");
51*0eae32dcSDimitry Andric     if (llvm::sys::fs::exists(PluginPath))
52*0eae32dcSDimitry Andric       return PluginPath.str().str();
53*0eae32dcSDimitry Andric     PluginPath.assign(hipPath);
54*0eae32dcSDimitry Andric     llvm::sys::path::append(PluginPath, "lib", "llvm",
55*0eae32dcSDimitry Andric                             "libLLVMHipSpvPasses.so");
56*0eae32dcSDimitry Andric     if (llvm::sys::fs::exists(PluginPath))
57*0eae32dcSDimitry Andric       return PluginPath.str().str();
58*0eae32dcSDimitry Andric   }
59*0eae32dcSDimitry Andric 
60*0eae32dcSDimitry Andric   return std::string();
61*0eae32dcSDimitry Andric }
62*0eae32dcSDimitry Andric 
63*0eae32dcSDimitry Andric void HIPSPV::Linker::constructLinkAndEmitSpirvCommand(
64*0eae32dcSDimitry Andric     Compilation &C, const JobAction &JA, const InputInfoList &Inputs,
65*0eae32dcSDimitry Andric     const InputInfo &Output, const llvm::opt::ArgList &Args) const {
66*0eae32dcSDimitry Andric 
67*0eae32dcSDimitry Andric   assert(!Inputs.empty() && "Must have at least one input.");
68*0eae32dcSDimitry Andric   std::string Name = std::string(llvm::sys::path::stem(Output.getFilename()));
69*0eae32dcSDimitry Andric   const char *TempFile = getTempFile(C, Name + "-link", "bc");
70*0eae32dcSDimitry Andric 
71*0eae32dcSDimitry Andric   // Link LLVM bitcode.
72*0eae32dcSDimitry Andric   ArgStringList LinkArgs{};
73*0eae32dcSDimitry Andric   for (auto Input : Inputs)
74*0eae32dcSDimitry Andric     LinkArgs.push_back(Input.getFilename());
75*0eae32dcSDimitry Andric   LinkArgs.append({"-o", TempFile});
76*0eae32dcSDimitry Andric   const char *LlvmLink =
77*0eae32dcSDimitry Andric       Args.MakeArgString(getToolChain().GetProgramPath("llvm-link"));
78*0eae32dcSDimitry Andric   C.addCommand(std::make_unique<Command>(JA, *this, ResponseFileSupport::None(),
79*0eae32dcSDimitry Andric                                          LlvmLink, LinkArgs, Inputs, Output));
80*0eae32dcSDimitry Andric 
81*0eae32dcSDimitry Andric   // Post-link HIP lowering.
82*0eae32dcSDimitry Andric 
83*0eae32dcSDimitry Andric   // Run LLVM IR passes to lower/expand/emulate HIP code that does not translate
84*0eae32dcSDimitry Andric   // to SPIR-V (E.g. dynamic shared memory).
85*0eae32dcSDimitry Andric   auto PassPluginPath = findPassPlugin(C.getDriver(), Args);
86*0eae32dcSDimitry Andric   if (!PassPluginPath.empty()) {
87*0eae32dcSDimitry Andric     const char *PassPathCStr = C.getArgs().MakeArgString(PassPluginPath);
88*0eae32dcSDimitry Andric     const char *OptOutput = getTempFile(C, Name + "-lower", "bc");
89*0eae32dcSDimitry Andric     ArgStringList OptArgs{TempFile,     "-load-pass-plugin",
90*0eae32dcSDimitry Andric                           PassPathCStr, "-passes=hip-post-link-passes",
91*0eae32dcSDimitry Andric                           "-o",         OptOutput};
92*0eae32dcSDimitry Andric     const char *Opt = Args.MakeArgString(getToolChain().GetProgramPath("opt"));
93*0eae32dcSDimitry Andric     C.addCommand(std::make_unique<Command>(
94*0eae32dcSDimitry Andric         JA, *this, ResponseFileSupport::None(), Opt, OptArgs, Inputs, Output));
95*0eae32dcSDimitry Andric     TempFile = OptOutput;
96*0eae32dcSDimitry Andric   }
97*0eae32dcSDimitry Andric 
98*0eae32dcSDimitry Andric   // Emit SPIR-V binary.
99*0eae32dcSDimitry Andric 
100*0eae32dcSDimitry Andric   llvm::opt::ArgStringList TrArgs{"--spirv-max-version=1.1",
101*0eae32dcSDimitry Andric                                   "--spirv-ext=+all"};
102*0eae32dcSDimitry Andric   InputInfo TrInput = InputInfo(types::TY_LLVM_BC, TempFile, "");
103*0eae32dcSDimitry Andric   SPIRV::constructTranslateCommand(C, *this, JA, Output, TrInput, TrArgs);
104*0eae32dcSDimitry Andric }
105*0eae32dcSDimitry Andric 
106*0eae32dcSDimitry Andric void HIPSPV::Linker::ConstructJob(Compilation &C, const JobAction &JA,
107*0eae32dcSDimitry Andric                                   const InputInfo &Output,
108*0eae32dcSDimitry Andric                                   const InputInfoList &Inputs,
109*0eae32dcSDimitry Andric                                   const ArgList &Args,
110*0eae32dcSDimitry Andric                                   const char *LinkingOutput) const {
111*0eae32dcSDimitry Andric   if (Inputs.size() > 0 && Inputs[0].getType() == types::TY_Image &&
112*0eae32dcSDimitry Andric       JA.getType() == types::TY_Object)
113*0eae32dcSDimitry Andric     return HIP::constructGenerateObjFileFromHIPFatBinary(C, Output, Inputs,
114*0eae32dcSDimitry Andric                                                          Args, JA, *this);
115*0eae32dcSDimitry Andric 
116*0eae32dcSDimitry Andric   if (JA.getType() == types::TY_HIP_FATBIN)
117*0eae32dcSDimitry Andric     return HIP::constructHIPFatbinCommand(C, JA, Output.getFilename(), Inputs,
118*0eae32dcSDimitry Andric                                           Args, *this);
119*0eae32dcSDimitry Andric 
120*0eae32dcSDimitry Andric   constructLinkAndEmitSpirvCommand(C, JA, Inputs, Output, Args);
121*0eae32dcSDimitry Andric }
122*0eae32dcSDimitry Andric 
123*0eae32dcSDimitry Andric HIPSPVToolChain::HIPSPVToolChain(const Driver &D, const llvm::Triple &Triple,
124*0eae32dcSDimitry Andric                                  const ToolChain &HostTC, const ArgList &Args)
125*0eae32dcSDimitry Andric     : ToolChain(D, Triple, Args), HostTC(HostTC) {
126*0eae32dcSDimitry Andric   // Lookup binaries into the driver directory, this is used to
127*0eae32dcSDimitry Andric   // discover the clang-offload-bundler executable.
128*0eae32dcSDimitry Andric   getProgramPaths().push_back(getDriver().Dir);
129*0eae32dcSDimitry Andric }
130*0eae32dcSDimitry Andric 
131*0eae32dcSDimitry Andric void HIPSPVToolChain::addClangTargetOptions(
132*0eae32dcSDimitry Andric     const llvm::opt::ArgList &DriverArgs, llvm::opt::ArgStringList &CC1Args,
133*0eae32dcSDimitry Andric     Action::OffloadKind DeviceOffloadingKind) const {
134*0eae32dcSDimitry Andric   HostTC.addClangTargetOptions(DriverArgs, CC1Args, DeviceOffloadingKind);
135*0eae32dcSDimitry Andric 
136*0eae32dcSDimitry Andric   assert(DeviceOffloadingKind == Action::OFK_HIP &&
137*0eae32dcSDimitry Andric          "Only HIP offloading kinds are supported for GPUs.");
138*0eae32dcSDimitry Andric 
139*0eae32dcSDimitry Andric   CC1Args.append(
140*0eae32dcSDimitry Andric       {"-fcuda-is-device", "-fcuda-allow-variadic-functions",
141*0eae32dcSDimitry Andric        // A crude workaround for llvm-spirv which does not handle the
142*0eae32dcSDimitry Andric        // autovectorized code well (vector reductions, non-i{8,16,32,64} types).
143*0eae32dcSDimitry Andric        // TODO: Allow autovectorization when SPIR-V backend arrives.
144*0eae32dcSDimitry Andric        "-mllvm", "-vectorize-loops=false", "-mllvm", "-vectorize-slp=false"});
145*0eae32dcSDimitry Andric 
146*0eae32dcSDimitry Andric   if (DriverArgs.hasFlag(options::OPT_fcuda_approx_transcendentals,
147*0eae32dcSDimitry Andric                          options::OPT_fno_cuda_approx_transcendentals, false))
148*0eae32dcSDimitry Andric     CC1Args.push_back("-fcuda-approx-transcendentals");
149*0eae32dcSDimitry Andric 
150*0eae32dcSDimitry Andric   // Default to "hidden" visibility, as object level linking will not be
151*0eae32dcSDimitry Andric   // supported for the foreseeable future.
152*0eae32dcSDimitry Andric   if (!DriverArgs.hasArg(options::OPT_fvisibility_EQ,
153*0eae32dcSDimitry Andric                          options::OPT_fvisibility_ms_compat))
154*0eae32dcSDimitry Andric     CC1Args.append(
155*0eae32dcSDimitry Andric         {"-fvisibility", "hidden", "-fapply-global-visibility-to-externs"});
156*0eae32dcSDimitry Andric 
157*0eae32dcSDimitry Andric   llvm::for_each(getHIPDeviceLibs(DriverArgs),
158*0eae32dcSDimitry Andric                  [&](const BitCodeLibraryInfo &BCFile) {
159*0eae32dcSDimitry Andric                    CC1Args.append({"-mlink-builtin-bitcode",
160*0eae32dcSDimitry Andric                                    DriverArgs.MakeArgString(BCFile.Path)});
161*0eae32dcSDimitry Andric                  });
162*0eae32dcSDimitry Andric }
163*0eae32dcSDimitry Andric 
164*0eae32dcSDimitry Andric Tool *HIPSPVToolChain::buildLinker() const {
165*0eae32dcSDimitry Andric   assert(getTriple().getArch() == llvm::Triple::spirv64);
166*0eae32dcSDimitry Andric   return new tools::HIPSPV::Linker(*this);
167*0eae32dcSDimitry Andric }
168*0eae32dcSDimitry Andric 
169*0eae32dcSDimitry Andric void HIPSPVToolChain::addClangWarningOptions(ArgStringList &CC1Args) const {
170*0eae32dcSDimitry Andric   HostTC.addClangWarningOptions(CC1Args);
171*0eae32dcSDimitry Andric }
172*0eae32dcSDimitry Andric 
173*0eae32dcSDimitry Andric ToolChain::CXXStdlibType
174*0eae32dcSDimitry Andric HIPSPVToolChain::GetCXXStdlibType(const ArgList &Args) const {
175*0eae32dcSDimitry Andric   return HostTC.GetCXXStdlibType(Args);
176*0eae32dcSDimitry Andric }
177*0eae32dcSDimitry Andric 
178*0eae32dcSDimitry Andric void HIPSPVToolChain::AddClangSystemIncludeArgs(const ArgList &DriverArgs,
179*0eae32dcSDimitry Andric                                                 ArgStringList &CC1Args) const {
180*0eae32dcSDimitry Andric   HostTC.AddClangSystemIncludeArgs(DriverArgs, CC1Args);
181*0eae32dcSDimitry Andric }
182*0eae32dcSDimitry Andric 
183*0eae32dcSDimitry Andric void HIPSPVToolChain::AddClangCXXStdlibIncludeArgs(
184*0eae32dcSDimitry Andric     const ArgList &Args, ArgStringList &CC1Args) const {
185*0eae32dcSDimitry Andric   HostTC.AddClangCXXStdlibIncludeArgs(Args, CC1Args);
186*0eae32dcSDimitry Andric }
187*0eae32dcSDimitry Andric 
188*0eae32dcSDimitry Andric void HIPSPVToolChain::AddIAMCUIncludeArgs(const ArgList &Args,
189*0eae32dcSDimitry Andric                                           ArgStringList &CC1Args) const {
190*0eae32dcSDimitry Andric   HostTC.AddIAMCUIncludeArgs(Args, CC1Args);
191*0eae32dcSDimitry Andric }
192*0eae32dcSDimitry Andric 
193*0eae32dcSDimitry Andric void HIPSPVToolChain::AddHIPIncludeArgs(const ArgList &DriverArgs,
194*0eae32dcSDimitry Andric                                         ArgStringList &CC1Args) const {
195*0eae32dcSDimitry Andric   if (DriverArgs.hasArg(options::OPT_nogpuinc))
196*0eae32dcSDimitry Andric     return;
197*0eae32dcSDimitry Andric 
198*0eae32dcSDimitry Andric   StringRef hipPath = DriverArgs.getLastArgValue(options::OPT_hip_path_EQ);
199*0eae32dcSDimitry Andric   if (hipPath.empty()) {
200*0eae32dcSDimitry Andric     getDriver().Diag(diag::err_drv_hipspv_no_hip_path) << 1 << "'-nogpuinc'";
201*0eae32dcSDimitry Andric     return;
202*0eae32dcSDimitry Andric   }
203*0eae32dcSDimitry Andric   SmallString<128> P(hipPath);
204*0eae32dcSDimitry Andric   llvm::sys::path::append(P, "include");
205*0eae32dcSDimitry Andric   CC1Args.append({"-isystem", DriverArgs.MakeArgString(P)});
206*0eae32dcSDimitry Andric }
207*0eae32dcSDimitry Andric 
208*0eae32dcSDimitry Andric llvm::SmallVector<ToolChain::BitCodeLibraryInfo, 12>
209*0eae32dcSDimitry Andric HIPSPVToolChain::getHIPDeviceLibs(const llvm::opt::ArgList &DriverArgs) const {
210*0eae32dcSDimitry Andric   llvm::SmallVector<ToolChain::BitCodeLibraryInfo, 12> BCLibs;
211*0eae32dcSDimitry Andric   if (DriverArgs.hasArg(options::OPT_nogpulib))
212*0eae32dcSDimitry Andric     return {};
213*0eae32dcSDimitry Andric 
214*0eae32dcSDimitry Andric   ArgStringList LibraryPaths;
215*0eae32dcSDimitry Andric   // Find device libraries in --hip-device-lib-path and HIP_DEVICE_LIB_PATH.
216*0eae32dcSDimitry Andric   auto HipDeviceLibPathArgs = DriverArgs.getAllArgValues(
217*0eae32dcSDimitry Andric       // --hip-device-lib-path is alias to this option.
218*0eae32dcSDimitry Andric       clang::driver::options::OPT_rocm_device_lib_path_EQ);
219*0eae32dcSDimitry Andric   for (auto Path : HipDeviceLibPathArgs)
220*0eae32dcSDimitry Andric     LibraryPaths.push_back(DriverArgs.MakeArgString(Path));
221*0eae32dcSDimitry Andric 
222*0eae32dcSDimitry Andric   StringRef HipPath = DriverArgs.getLastArgValue(options::OPT_hip_path_EQ);
223*0eae32dcSDimitry Andric   if (!HipPath.empty()) {
224*0eae32dcSDimitry Andric     SmallString<128> Path(HipPath);
225*0eae32dcSDimitry Andric     llvm::sys::path::append(Path, "lib", "hip-device-lib");
226*0eae32dcSDimitry Andric     LibraryPaths.push_back(DriverArgs.MakeArgString(Path));
227*0eae32dcSDimitry Andric   }
228*0eae32dcSDimitry Andric 
229*0eae32dcSDimitry Andric   addDirectoryList(DriverArgs, LibraryPaths, "", "HIP_DEVICE_LIB_PATH");
230*0eae32dcSDimitry Andric 
231*0eae32dcSDimitry Andric   // Maintain compatability with --hip-device-lib.
232*0eae32dcSDimitry Andric   auto BCLibArgs = DriverArgs.getAllArgValues(options::OPT_hip_device_lib_EQ);
233*0eae32dcSDimitry Andric   if (!BCLibArgs.empty()) {
234*0eae32dcSDimitry Andric     llvm::for_each(BCLibArgs, [&](StringRef BCName) {
235*0eae32dcSDimitry Andric       StringRef FullName;
236*0eae32dcSDimitry Andric       for (std::string LibraryPath : LibraryPaths) {
237*0eae32dcSDimitry Andric         SmallString<128> Path(LibraryPath);
238*0eae32dcSDimitry Andric         llvm::sys::path::append(Path, BCName);
239*0eae32dcSDimitry Andric         FullName = Path;
240*0eae32dcSDimitry Andric         if (llvm::sys::fs::exists(FullName)) {
241*0eae32dcSDimitry Andric           BCLibs.emplace_back(FullName.str());
242*0eae32dcSDimitry Andric           return;
243*0eae32dcSDimitry Andric         }
244*0eae32dcSDimitry Andric       }
245*0eae32dcSDimitry Andric       getDriver().Diag(diag::err_drv_no_such_file) << BCName;
246*0eae32dcSDimitry Andric     });
247*0eae32dcSDimitry Andric   } else {
248*0eae32dcSDimitry Andric     // Search device library named as 'hipspv-<triple>.bc'.
249*0eae32dcSDimitry Andric     auto TT = getTriple().normalize();
250*0eae32dcSDimitry Andric     std::string BCName = "hipspv-" + TT + ".bc";
251*0eae32dcSDimitry Andric     for (auto *LibPath : LibraryPaths) {
252*0eae32dcSDimitry Andric       SmallString<128> Path(LibPath);
253*0eae32dcSDimitry Andric       llvm::sys::path::append(Path, BCName);
254*0eae32dcSDimitry Andric       if (llvm::sys::fs::exists(Path)) {
255*0eae32dcSDimitry Andric         BCLibs.emplace_back(Path.str().str());
256*0eae32dcSDimitry Andric         return BCLibs;
257*0eae32dcSDimitry Andric       }
258*0eae32dcSDimitry Andric     }
259*0eae32dcSDimitry Andric     getDriver().Diag(diag::err_drv_no_hipspv_device_lib)
260*0eae32dcSDimitry Andric         << 1 << ("'" + TT + "' target");
261*0eae32dcSDimitry Andric     return {};
262*0eae32dcSDimitry Andric   }
263*0eae32dcSDimitry Andric 
264*0eae32dcSDimitry Andric   return BCLibs;
265*0eae32dcSDimitry Andric }
266*0eae32dcSDimitry Andric 
267*0eae32dcSDimitry Andric SanitizerMask HIPSPVToolChain::getSupportedSanitizers() const {
268*0eae32dcSDimitry Andric   // The HIPSPVToolChain only supports sanitizers in the sense that it allows
269*0eae32dcSDimitry Andric   // sanitizer arguments on the command line if they are supported by the host
270*0eae32dcSDimitry Andric   // toolchain. The HIPSPVToolChain will actually ignore any command line
271*0eae32dcSDimitry Andric   // arguments for any of these "supported" sanitizers. That means that no
272*0eae32dcSDimitry Andric   // sanitization of device code is actually supported at this time.
273*0eae32dcSDimitry Andric   //
274*0eae32dcSDimitry Andric   // This behavior is necessary because the host and device toolchains
275*0eae32dcSDimitry Andric   // invocations often share the command line, so the device toolchain must
276*0eae32dcSDimitry Andric   // tolerate flags meant only for the host toolchain.
277*0eae32dcSDimitry Andric   return HostTC.getSupportedSanitizers();
278*0eae32dcSDimitry Andric }
279*0eae32dcSDimitry Andric 
280*0eae32dcSDimitry Andric VersionTuple HIPSPVToolChain::computeMSVCVersion(const Driver *D,
281*0eae32dcSDimitry Andric                                                  const ArgList &Args) const {
282*0eae32dcSDimitry Andric   return HostTC.computeMSVCVersion(D, Args);
283*0eae32dcSDimitry Andric }
284*0eae32dcSDimitry Andric 
285*0eae32dcSDimitry Andric void HIPSPVToolChain::adjustDebugInfoKind(
286*0eae32dcSDimitry Andric     codegenoptions::DebugInfoKind &DebugInfoKind,
287*0eae32dcSDimitry Andric     const llvm::opt::ArgList &Args) const {
288*0eae32dcSDimitry Andric   // Debug info generation is disabled for SPIRV-LLVM-Translator
289*0eae32dcSDimitry Andric   // which currently aborts on the presence of DW_OP_LLVM_convert.
290*0eae32dcSDimitry Andric   // TODO: Enable debug info when the SPIR-V backend arrives.
291*0eae32dcSDimitry Andric   DebugInfoKind = codegenoptions::NoDebugInfo;
292*0eae32dcSDimitry Andric }
293