xref: /freebsd-src/contrib/llvm-project/clang/lib/Driver/ToolChains/HIPSPV.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
10eae32dcSDimitry Andric //===--- HIPSPV.cpp - HIPSPV ToolChain Implementation -----------*- C++ -*-===//
20eae32dcSDimitry Andric //
30eae32dcSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40eae32dcSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50eae32dcSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60eae32dcSDimitry Andric //
70eae32dcSDimitry Andric //===----------------------------------------------------------------------===//
80eae32dcSDimitry Andric 
90eae32dcSDimitry Andric #include "HIPSPV.h"
100eae32dcSDimitry Andric #include "CommonArgs.h"
110eae32dcSDimitry Andric #include "HIPUtility.h"
120eae32dcSDimitry Andric #include "clang/Driver/Compilation.h"
130eae32dcSDimitry Andric #include "clang/Driver/Driver.h"
140eae32dcSDimitry Andric #include "clang/Driver/DriverDiagnostic.h"
150eae32dcSDimitry Andric #include "clang/Driver/InputInfo.h"
160eae32dcSDimitry Andric #include "clang/Driver/Options.h"
170eae32dcSDimitry Andric #include "llvm/Support/FileSystem.h"
180eae32dcSDimitry Andric #include "llvm/Support/Path.h"
190eae32dcSDimitry Andric 
200eae32dcSDimitry Andric using namespace clang::driver;
210eae32dcSDimitry Andric using namespace clang::driver::toolchains;
220eae32dcSDimitry Andric using namespace clang::driver::tools;
230eae32dcSDimitry Andric using namespace clang;
240eae32dcSDimitry Andric using namespace llvm::opt;
250eae32dcSDimitry Andric 
260eae32dcSDimitry Andric // Convenience function for creating temporary file for both modes of
270eae32dcSDimitry Andric // isSaveTempsEnabled().
280eae32dcSDimitry Andric static const char *getTempFile(Compilation &C, StringRef Prefix,
290eae32dcSDimitry Andric                                StringRef Extension) {
300eae32dcSDimitry Andric   if (C.getDriver().isSaveTempsEnabled()) {
310eae32dcSDimitry Andric     return C.getArgs().MakeArgString(Prefix + "." + Extension);
320eae32dcSDimitry Andric   }
330eae32dcSDimitry Andric   auto TmpFile = C.getDriver().GetTemporaryPath(Prefix, Extension);
340eae32dcSDimitry Andric   return C.addTempFile(C.getArgs().MakeArgString(TmpFile));
350eae32dcSDimitry Andric }
360eae32dcSDimitry Andric 
370eae32dcSDimitry Andric // Locates HIP pass plugin.
380eae32dcSDimitry Andric static std::string findPassPlugin(const Driver &D,
390eae32dcSDimitry Andric                                   const llvm::opt::ArgList &Args) {
400eae32dcSDimitry Andric   StringRef Path = Args.getLastArgValue(options::OPT_hipspv_pass_plugin_EQ);
410eae32dcSDimitry Andric   if (!Path.empty()) {
420eae32dcSDimitry Andric     if (llvm::sys::fs::exists(Path))
430eae32dcSDimitry Andric       return Path.str();
440eae32dcSDimitry Andric     D.Diag(diag::err_drv_no_such_file) << Path;
450eae32dcSDimitry Andric   }
460eae32dcSDimitry Andric 
470eae32dcSDimitry Andric   StringRef hipPath = Args.getLastArgValue(options::OPT_hip_path_EQ);
480eae32dcSDimitry Andric   if (!hipPath.empty()) {
490eae32dcSDimitry Andric     SmallString<128> PluginPath(hipPath);
500eae32dcSDimitry Andric     llvm::sys::path::append(PluginPath, "lib", "libLLVMHipSpvPasses.so");
510eae32dcSDimitry Andric     if (llvm::sys::fs::exists(PluginPath))
520eae32dcSDimitry Andric       return PluginPath.str().str();
530eae32dcSDimitry Andric     PluginPath.assign(hipPath);
540eae32dcSDimitry Andric     llvm::sys::path::append(PluginPath, "lib", "llvm",
550eae32dcSDimitry Andric                             "libLLVMHipSpvPasses.so");
560eae32dcSDimitry Andric     if (llvm::sys::fs::exists(PluginPath))
570eae32dcSDimitry Andric       return PluginPath.str().str();
580eae32dcSDimitry Andric   }
590eae32dcSDimitry Andric 
600eae32dcSDimitry Andric   return std::string();
610eae32dcSDimitry Andric }
620eae32dcSDimitry Andric 
630eae32dcSDimitry Andric void HIPSPV::Linker::constructLinkAndEmitSpirvCommand(
640eae32dcSDimitry Andric     Compilation &C, const JobAction &JA, const InputInfoList &Inputs,
650eae32dcSDimitry Andric     const InputInfo &Output, const llvm::opt::ArgList &Args) const {
660eae32dcSDimitry Andric 
670eae32dcSDimitry Andric   assert(!Inputs.empty() && "Must have at least one input.");
680eae32dcSDimitry Andric   std::string Name = std::string(llvm::sys::path::stem(Output.getFilename()));
690eae32dcSDimitry Andric   const char *TempFile = getTempFile(C, Name + "-link", "bc");
700eae32dcSDimitry Andric 
710eae32dcSDimitry Andric   // Link LLVM bitcode.
720eae32dcSDimitry Andric   ArgStringList LinkArgs{};
730eae32dcSDimitry Andric   for (auto Input : Inputs)
740eae32dcSDimitry Andric     LinkArgs.push_back(Input.getFilename());
750eae32dcSDimitry Andric   LinkArgs.append({"-o", TempFile});
760eae32dcSDimitry Andric   const char *LlvmLink =
770eae32dcSDimitry Andric       Args.MakeArgString(getToolChain().GetProgramPath("llvm-link"));
780eae32dcSDimitry Andric   C.addCommand(std::make_unique<Command>(JA, *this, ResponseFileSupport::None(),
790eae32dcSDimitry Andric                                          LlvmLink, LinkArgs, Inputs, Output));
800eae32dcSDimitry Andric 
810eae32dcSDimitry Andric   // Post-link HIP lowering.
820eae32dcSDimitry Andric 
830eae32dcSDimitry Andric   // Run LLVM IR passes to lower/expand/emulate HIP code that does not translate
840eae32dcSDimitry Andric   // to SPIR-V (E.g. dynamic shared memory).
850eae32dcSDimitry Andric   auto PassPluginPath = findPassPlugin(C.getDriver(), Args);
860eae32dcSDimitry Andric   if (!PassPluginPath.empty()) {
870eae32dcSDimitry Andric     const char *PassPathCStr = C.getArgs().MakeArgString(PassPluginPath);
880eae32dcSDimitry Andric     const char *OptOutput = getTempFile(C, Name + "-lower", "bc");
890eae32dcSDimitry Andric     ArgStringList OptArgs{TempFile,     "-load-pass-plugin",
900eae32dcSDimitry Andric                           PassPathCStr, "-passes=hip-post-link-passes",
910eae32dcSDimitry Andric                           "-o",         OptOutput};
920eae32dcSDimitry Andric     const char *Opt = Args.MakeArgString(getToolChain().GetProgramPath("opt"));
930eae32dcSDimitry Andric     C.addCommand(std::make_unique<Command>(
940eae32dcSDimitry Andric         JA, *this, ResponseFileSupport::None(), Opt, OptArgs, Inputs, Output));
950eae32dcSDimitry Andric     TempFile = OptOutput;
960eae32dcSDimitry Andric   }
970eae32dcSDimitry Andric 
980eae32dcSDimitry Andric   // Emit SPIR-V binary.
990eae32dcSDimitry Andric 
1000eae32dcSDimitry Andric   llvm::opt::ArgStringList TrArgs{"--spirv-max-version=1.1",
1010eae32dcSDimitry Andric                                   "--spirv-ext=+all"};
1020eae32dcSDimitry Andric   InputInfo TrInput = InputInfo(types::TY_LLVM_BC, TempFile, "");
1030eae32dcSDimitry Andric   SPIRV::constructTranslateCommand(C, *this, JA, Output, TrInput, TrArgs);
1040eae32dcSDimitry Andric }
1050eae32dcSDimitry Andric 
1060eae32dcSDimitry Andric void HIPSPV::Linker::ConstructJob(Compilation &C, const JobAction &JA,
1070eae32dcSDimitry Andric                                   const InputInfo &Output,
1080eae32dcSDimitry Andric                                   const InputInfoList &Inputs,
1090eae32dcSDimitry Andric                                   const ArgList &Args,
1100eae32dcSDimitry Andric                                   const char *LinkingOutput) const {
1110eae32dcSDimitry Andric   if (Inputs.size() > 0 && Inputs[0].getType() == types::TY_Image &&
1120eae32dcSDimitry Andric       JA.getType() == types::TY_Object)
1130eae32dcSDimitry Andric     return HIP::constructGenerateObjFileFromHIPFatBinary(C, Output, Inputs,
1140eae32dcSDimitry Andric                                                          Args, JA, *this);
1150eae32dcSDimitry Andric 
1160eae32dcSDimitry Andric   if (JA.getType() == types::TY_HIP_FATBIN)
1170eae32dcSDimitry Andric     return HIP::constructHIPFatbinCommand(C, JA, Output.getFilename(), Inputs,
1180eae32dcSDimitry Andric                                           Args, *this);
1190eae32dcSDimitry Andric 
1200eae32dcSDimitry Andric   constructLinkAndEmitSpirvCommand(C, JA, Inputs, Output, Args);
1210eae32dcSDimitry Andric }
1220eae32dcSDimitry Andric 
1230eae32dcSDimitry Andric HIPSPVToolChain::HIPSPVToolChain(const Driver &D, const llvm::Triple &Triple,
1240eae32dcSDimitry Andric                                  const ToolChain &HostTC, const ArgList &Args)
1250eae32dcSDimitry Andric     : ToolChain(D, Triple, Args), HostTC(HostTC) {
1260eae32dcSDimitry Andric   // Lookup binaries into the driver directory, this is used to
1270eae32dcSDimitry Andric   // discover the clang-offload-bundler executable.
1280eae32dcSDimitry Andric   getProgramPaths().push_back(getDriver().Dir);
1290eae32dcSDimitry Andric }
1300eae32dcSDimitry Andric 
1310eae32dcSDimitry Andric void HIPSPVToolChain::addClangTargetOptions(
1320eae32dcSDimitry Andric     const llvm::opt::ArgList &DriverArgs, llvm::opt::ArgStringList &CC1Args,
1330eae32dcSDimitry Andric     Action::OffloadKind DeviceOffloadingKind) const {
1340eae32dcSDimitry Andric   HostTC.addClangTargetOptions(DriverArgs, CC1Args, DeviceOffloadingKind);
1350eae32dcSDimitry Andric 
1360eae32dcSDimitry Andric   assert(DeviceOffloadingKind == Action::OFK_HIP &&
1370eae32dcSDimitry Andric          "Only HIP offloading kinds are supported for GPUs.");
1380eae32dcSDimitry Andric 
1390eae32dcSDimitry Andric   CC1Args.append(
1400eae32dcSDimitry Andric       {"-fcuda-is-device", "-fcuda-allow-variadic-functions",
1410eae32dcSDimitry Andric        // A crude workaround for llvm-spirv which does not handle the
1420eae32dcSDimitry Andric        // autovectorized code well (vector reductions, non-i{8,16,32,64} types).
1430eae32dcSDimitry Andric        // TODO: Allow autovectorization when SPIR-V backend arrives.
1440eae32dcSDimitry Andric        "-mllvm", "-vectorize-loops=false", "-mllvm", "-vectorize-slp=false"});
1450eae32dcSDimitry Andric 
1460eae32dcSDimitry Andric   // Default to "hidden" visibility, as object level linking will not be
1470eae32dcSDimitry Andric   // supported for the foreseeable future.
1480eae32dcSDimitry Andric   if (!DriverArgs.hasArg(options::OPT_fvisibility_EQ,
1490eae32dcSDimitry Andric                          options::OPT_fvisibility_ms_compat))
1500eae32dcSDimitry Andric     CC1Args.append(
151bdd1243dSDimitry Andric         {"-fvisibility=hidden", "-fapply-global-visibility-to-externs"});
1520eae32dcSDimitry Andric 
153bdd1243dSDimitry Andric   llvm::for_each(getDeviceLibs(DriverArgs),
1540eae32dcSDimitry Andric                  [&](const BitCodeLibraryInfo &BCFile) {
1550eae32dcSDimitry Andric                    CC1Args.append({"-mlink-builtin-bitcode",
1560eae32dcSDimitry Andric                                    DriverArgs.MakeArgString(BCFile.Path)});
1570eae32dcSDimitry Andric                  });
1580eae32dcSDimitry Andric }
1590eae32dcSDimitry Andric 
1600eae32dcSDimitry Andric Tool *HIPSPVToolChain::buildLinker() const {
1610eae32dcSDimitry Andric   assert(getTriple().getArch() == llvm::Triple::spirv64);
1620eae32dcSDimitry Andric   return new tools::HIPSPV::Linker(*this);
1630eae32dcSDimitry Andric }
1640eae32dcSDimitry Andric 
1650eae32dcSDimitry Andric void HIPSPVToolChain::addClangWarningOptions(ArgStringList &CC1Args) const {
1660eae32dcSDimitry Andric   HostTC.addClangWarningOptions(CC1Args);
1670eae32dcSDimitry Andric }
1680eae32dcSDimitry Andric 
1690eae32dcSDimitry Andric ToolChain::CXXStdlibType
1700eae32dcSDimitry Andric HIPSPVToolChain::GetCXXStdlibType(const ArgList &Args) const {
1710eae32dcSDimitry Andric   return HostTC.GetCXXStdlibType(Args);
1720eae32dcSDimitry Andric }
1730eae32dcSDimitry Andric 
1740eae32dcSDimitry Andric void HIPSPVToolChain::AddClangSystemIncludeArgs(const ArgList &DriverArgs,
1750eae32dcSDimitry Andric                                                 ArgStringList &CC1Args) const {
1760eae32dcSDimitry Andric   HostTC.AddClangSystemIncludeArgs(DriverArgs, CC1Args);
1770eae32dcSDimitry Andric }
1780eae32dcSDimitry Andric 
1790eae32dcSDimitry Andric void HIPSPVToolChain::AddClangCXXStdlibIncludeArgs(
1800eae32dcSDimitry Andric     const ArgList &Args, ArgStringList &CC1Args) const {
1810eae32dcSDimitry Andric   HostTC.AddClangCXXStdlibIncludeArgs(Args, CC1Args);
1820eae32dcSDimitry Andric }
1830eae32dcSDimitry Andric 
1840eae32dcSDimitry Andric void HIPSPVToolChain::AddIAMCUIncludeArgs(const ArgList &Args,
1850eae32dcSDimitry Andric                                           ArgStringList &CC1Args) const {
1860eae32dcSDimitry Andric   HostTC.AddIAMCUIncludeArgs(Args, CC1Args);
1870eae32dcSDimitry Andric }
1880eae32dcSDimitry Andric 
1890eae32dcSDimitry Andric void HIPSPVToolChain::AddHIPIncludeArgs(const ArgList &DriverArgs,
1900eae32dcSDimitry Andric                                         ArgStringList &CC1Args) const {
1910eae32dcSDimitry Andric   if (DriverArgs.hasArg(options::OPT_nogpuinc))
1920eae32dcSDimitry Andric     return;
1930eae32dcSDimitry Andric 
1940eae32dcSDimitry Andric   StringRef hipPath = DriverArgs.getLastArgValue(options::OPT_hip_path_EQ);
1950eae32dcSDimitry Andric   if (hipPath.empty()) {
196*0fca6ea1SDimitry Andric     getDriver().Diag(diag::err_drv_hipspv_no_hip_path);
1970eae32dcSDimitry Andric     return;
1980eae32dcSDimitry Andric   }
1990eae32dcSDimitry Andric   SmallString<128> P(hipPath);
2000eae32dcSDimitry Andric   llvm::sys::path::append(P, "include");
2010eae32dcSDimitry Andric   CC1Args.append({"-isystem", DriverArgs.MakeArgString(P)});
2020eae32dcSDimitry Andric }
2030eae32dcSDimitry Andric 
2040eae32dcSDimitry Andric llvm::SmallVector<ToolChain::BitCodeLibraryInfo, 12>
205bdd1243dSDimitry Andric HIPSPVToolChain::getDeviceLibs(const llvm::opt::ArgList &DriverArgs) const {
2060eae32dcSDimitry Andric   llvm::SmallVector<ToolChain::BitCodeLibraryInfo, 12> BCLibs;
2070eae32dcSDimitry Andric   if (DriverArgs.hasArg(options::OPT_nogpulib))
2080eae32dcSDimitry Andric     return {};
2090eae32dcSDimitry Andric 
2100eae32dcSDimitry Andric   ArgStringList LibraryPaths;
2110eae32dcSDimitry Andric   // Find device libraries in --hip-device-lib-path and HIP_DEVICE_LIB_PATH.
2120eae32dcSDimitry Andric   auto HipDeviceLibPathArgs = DriverArgs.getAllArgValues(
2130eae32dcSDimitry Andric       // --hip-device-lib-path is alias to this option.
2140eae32dcSDimitry Andric       clang::driver::options::OPT_rocm_device_lib_path_EQ);
2150eae32dcSDimitry Andric   for (auto Path : HipDeviceLibPathArgs)
2160eae32dcSDimitry Andric     LibraryPaths.push_back(DriverArgs.MakeArgString(Path));
2170eae32dcSDimitry Andric 
2180eae32dcSDimitry Andric   StringRef HipPath = DriverArgs.getLastArgValue(options::OPT_hip_path_EQ);
2190eae32dcSDimitry Andric   if (!HipPath.empty()) {
2200eae32dcSDimitry Andric     SmallString<128> Path(HipPath);
2210eae32dcSDimitry Andric     llvm::sys::path::append(Path, "lib", "hip-device-lib");
2220eae32dcSDimitry Andric     LibraryPaths.push_back(DriverArgs.MakeArgString(Path));
2230eae32dcSDimitry Andric   }
2240eae32dcSDimitry Andric 
2250eae32dcSDimitry Andric   addDirectoryList(DriverArgs, LibraryPaths, "", "HIP_DEVICE_LIB_PATH");
2260eae32dcSDimitry Andric 
2270eae32dcSDimitry Andric   // Maintain compatability with --hip-device-lib.
2280eae32dcSDimitry Andric   auto BCLibArgs = DriverArgs.getAllArgValues(options::OPT_hip_device_lib_EQ);
2290eae32dcSDimitry Andric   if (!BCLibArgs.empty()) {
2300eae32dcSDimitry Andric     llvm::for_each(BCLibArgs, [&](StringRef BCName) {
2310eae32dcSDimitry Andric       StringRef FullName;
2320eae32dcSDimitry Andric       for (std::string LibraryPath : LibraryPaths) {
2330eae32dcSDimitry Andric         SmallString<128> Path(LibraryPath);
2340eae32dcSDimitry Andric         llvm::sys::path::append(Path, BCName);
2350eae32dcSDimitry Andric         FullName = Path;
2360eae32dcSDimitry Andric         if (llvm::sys::fs::exists(FullName)) {
2370eae32dcSDimitry Andric           BCLibs.emplace_back(FullName.str());
2380eae32dcSDimitry Andric           return;
2390eae32dcSDimitry Andric         }
2400eae32dcSDimitry Andric       }
2410eae32dcSDimitry Andric       getDriver().Diag(diag::err_drv_no_such_file) << BCName;
2420eae32dcSDimitry Andric     });
2430eae32dcSDimitry Andric   } else {
2440eae32dcSDimitry Andric     // Search device library named as 'hipspv-<triple>.bc'.
2450eae32dcSDimitry Andric     auto TT = getTriple().normalize();
2460eae32dcSDimitry Andric     std::string BCName = "hipspv-" + TT + ".bc";
2470eae32dcSDimitry Andric     for (auto *LibPath : LibraryPaths) {
2480eae32dcSDimitry Andric       SmallString<128> Path(LibPath);
2490eae32dcSDimitry Andric       llvm::sys::path::append(Path, BCName);
2500eae32dcSDimitry Andric       if (llvm::sys::fs::exists(Path)) {
2510eae32dcSDimitry Andric         BCLibs.emplace_back(Path.str().str());
2520eae32dcSDimitry Andric         return BCLibs;
2530eae32dcSDimitry Andric       }
2540eae32dcSDimitry Andric     }
2550eae32dcSDimitry Andric     getDriver().Diag(diag::err_drv_no_hipspv_device_lib)
2560eae32dcSDimitry Andric         << 1 << ("'" + TT + "' target");
2570eae32dcSDimitry Andric     return {};
2580eae32dcSDimitry Andric   }
2590eae32dcSDimitry Andric 
2600eae32dcSDimitry Andric   return BCLibs;
2610eae32dcSDimitry Andric }
2620eae32dcSDimitry Andric 
2630eae32dcSDimitry Andric SanitizerMask HIPSPVToolChain::getSupportedSanitizers() const {
2640eae32dcSDimitry Andric   // The HIPSPVToolChain only supports sanitizers in the sense that it allows
2650eae32dcSDimitry Andric   // sanitizer arguments on the command line if they are supported by the host
2660eae32dcSDimitry Andric   // toolchain. The HIPSPVToolChain will actually ignore any command line
2670eae32dcSDimitry Andric   // arguments for any of these "supported" sanitizers. That means that no
2680eae32dcSDimitry Andric   // sanitization of device code is actually supported at this time.
2690eae32dcSDimitry Andric   //
2700eae32dcSDimitry Andric   // This behavior is necessary because the host and device toolchains
2710eae32dcSDimitry Andric   // invocations often share the command line, so the device toolchain must
2720eae32dcSDimitry Andric   // tolerate flags meant only for the host toolchain.
2730eae32dcSDimitry Andric   return HostTC.getSupportedSanitizers();
2740eae32dcSDimitry Andric }
2750eae32dcSDimitry Andric 
2760eae32dcSDimitry Andric VersionTuple HIPSPVToolChain::computeMSVCVersion(const Driver *D,
2770eae32dcSDimitry Andric                                                  const ArgList &Args) const {
2780eae32dcSDimitry Andric   return HostTC.computeMSVCVersion(D, Args);
2790eae32dcSDimitry Andric }
2800eae32dcSDimitry Andric 
2810eae32dcSDimitry Andric void HIPSPVToolChain::adjustDebugInfoKind(
28206c3fb27SDimitry Andric     llvm::codegenoptions::DebugInfoKind &DebugInfoKind,
2830eae32dcSDimitry Andric     const llvm::opt::ArgList &Args) const {
2840eae32dcSDimitry Andric   // Debug info generation is disabled for SPIRV-LLVM-Translator
2850eae32dcSDimitry Andric   // which currently aborts on the presence of DW_OP_LLVM_convert.
2860eae32dcSDimitry Andric   // TODO: Enable debug info when the SPIR-V backend arrives.
28706c3fb27SDimitry Andric   DebugInfoKind = llvm::codegenoptions::NoDebugInfo;
2880eae32dcSDimitry Andric }
289