Home | History | Annotate | Line # | Download | only in ToolChains
      1 //===- AMDGPUOpenMP.cpp - AMDGPUOpenMP ToolChain Implementation -*- 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 "AMDGPUOpenMP.h"
     10 #include "AMDGPU.h"
     11 #include "CommonArgs.h"
     12 #include "InputInfo.h"
     13 #include "clang/Basic/DiagnosticDriver.h"
     14 #include "clang/Driver/Compilation.h"
     15 #include "clang/Driver/Driver.h"
     16 #include "clang/Driver/DriverDiagnostic.h"
     17 #include "clang/Driver/Options.h"
     18 #include "llvm/Support/FileSystem.h"
     19 #include "llvm/Support/FormatAdapters.h"
     20 #include "llvm/Support/FormatVariadic.h"
     21 #include "llvm/Support/Path.h"
     22 
     23 using namespace clang::driver;
     24 using namespace clang::driver::toolchains;
     25 using namespace clang::driver::tools;
     26 using namespace clang;
     27 using namespace llvm::opt;
     28 
     29 namespace {
     30 
     31 static const char *getOutputFileName(Compilation &C, StringRef Base,
     32                                      const char *Postfix,
     33                                      const char *Extension) {
     34   const char *OutputFileName;
     35   if (C.getDriver().isSaveTempsEnabled()) {
     36     OutputFileName =
     37         C.getArgs().MakeArgString(Base.str() + Postfix + "." + Extension);
     38   } else {
     39     std::string TmpName =
     40         C.getDriver().GetTemporaryPath(Base.str() + Postfix, Extension);
     41     OutputFileName = C.addTempFile(C.getArgs().MakeArgString(TmpName));
     42   }
     43   return OutputFileName;
     44 }
     45 
     46 static void addLLCOptArg(const llvm::opt::ArgList &Args,
     47                          llvm::opt::ArgStringList &CmdArgs) {
     48   if (Arg *A = Args.getLastArg(options::OPT_O_Group)) {
     49     StringRef OOpt = "0";
     50     if (A->getOption().matches(options::OPT_O4) ||
     51         A->getOption().matches(options::OPT_Ofast))
     52       OOpt = "3";
     53     else if (A->getOption().matches(options::OPT_O0))
     54       OOpt = "0";
     55     else if (A->getOption().matches(options::OPT_O)) {
     56       // Clang and opt support -Os/-Oz; llc only supports -O0, -O1, -O2 and -O3
     57       // so we map -Os/-Oz to -O2.
     58       // Only clang supports -Og, and maps it to -O1.
     59       // We map anything else to -O2.
     60       OOpt = llvm::StringSwitch<const char *>(A->getValue())
     61                  .Case("1", "1")
     62                  .Case("2", "2")
     63                  .Case("3", "3")
     64                  .Case("s", "2")
     65                  .Case("z", "2")
     66                  .Case("g", "1")
     67                  .Default("0");
     68     }
     69     CmdArgs.push_back(Args.MakeArgString("-O" + OOpt));
     70   }
     71 }
     72 
     73 static bool checkSystemForAMDGPU(const ArgList &Args, const AMDGPUToolChain &TC,
     74                                  std::string &GPUArch) {
     75   if (auto Err = TC.getSystemGPUArch(Args, GPUArch)) {
     76     std::string ErrMsg =
     77         llvm::formatv("{0}", llvm::fmt_consume(std::move(Err)));
     78     TC.getDriver().Diag(diag::err_drv_undetermined_amdgpu_arch) << ErrMsg;
     79     return false;
     80   }
     81 
     82   return true;
     83 }
     84 } // namespace
     85 
     86 const char *AMDGCN::OpenMPLinker::constructLLVMLinkCommand(
     87     Compilation &C, const JobAction &JA, const InputInfoList &Inputs,
     88     const ArgList &Args, StringRef SubArchName,
     89     StringRef OutputFilePrefix) const {
     90   ArgStringList CmdArgs;
     91 
     92   for (const auto &II : Inputs)
     93     if (II.isFilename())
     94       CmdArgs.push_back(II.getFilename());
     95   // Add an intermediate output file.
     96   CmdArgs.push_back("-o");
     97   const char *OutputFileName =
     98       getOutputFileName(C, OutputFilePrefix, "-linked", "bc");
     99   CmdArgs.push_back(OutputFileName);
    100   const char *Exec =
    101       Args.MakeArgString(getToolChain().GetProgramPath("llvm-link"));
    102   C.addCommand(std::make_unique<Command>(
    103       JA, *this, ResponseFileSupport::AtFileCurCP(), Exec, CmdArgs, Inputs,
    104       InputInfo(&JA, Args.MakeArgString(OutputFileName))));
    105   return OutputFileName;
    106 }
    107 
    108 const char *AMDGCN::OpenMPLinker::constructLlcCommand(
    109     Compilation &C, const JobAction &JA, const InputInfoList &Inputs,
    110     const llvm::opt::ArgList &Args, llvm::StringRef SubArchName,
    111     llvm::StringRef OutputFilePrefix, const char *InputFileName,
    112     bool OutputIsAsm) const {
    113   // Construct llc command.
    114   ArgStringList LlcArgs;
    115   // The input to llc is the output from opt.
    116   LlcArgs.push_back(InputFileName);
    117   // Pass optimization arg to llc.
    118   addLLCOptArg(Args, LlcArgs);
    119   LlcArgs.push_back("-mtriple=amdgcn-amd-amdhsa");
    120   LlcArgs.push_back(Args.MakeArgString("-mcpu=" + SubArchName));
    121   LlcArgs.push_back(
    122       Args.MakeArgString(Twine("-filetype=") + (OutputIsAsm ? "asm" : "obj")));
    123 
    124   for (const Arg *A : Args.filtered(options::OPT_mllvm)) {
    125     LlcArgs.push_back(A->getValue(0));
    126   }
    127 
    128   // Add output filename
    129   LlcArgs.push_back("-o");
    130   const char *LlcOutputFile =
    131       getOutputFileName(C, OutputFilePrefix, "", OutputIsAsm ? "s" : "o");
    132   LlcArgs.push_back(LlcOutputFile);
    133   const char *Llc = Args.MakeArgString(getToolChain().GetProgramPath("llc"));
    134   C.addCommand(std::make_unique<Command>(
    135       JA, *this, ResponseFileSupport::AtFileCurCP(), Llc, LlcArgs, Inputs,
    136       InputInfo(&JA, Args.MakeArgString(LlcOutputFile))));
    137   return LlcOutputFile;
    138 }
    139 
    140 void AMDGCN::OpenMPLinker::constructLldCommand(
    141     Compilation &C, const JobAction &JA, const InputInfoList &Inputs,
    142     const InputInfo &Output, const llvm::opt::ArgList &Args,
    143     const char *InputFileName) const {
    144   // Construct lld command.
    145   // The output from ld.lld is an HSA code object file.
    146   ArgStringList LldArgs{"-flavor",    "gnu", "--no-undefined",
    147                         "-shared",    "-o",  Output.getFilename(),
    148                         InputFileName};
    149 
    150   const char *Lld = Args.MakeArgString(getToolChain().GetProgramPath("lld"));
    151   C.addCommand(std::make_unique<Command>(
    152       JA, *this, ResponseFileSupport::AtFileCurCP(), Lld, LldArgs, Inputs,
    153       InputInfo(&JA, Args.MakeArgString(Output.getFilename()))));
    154 }
    155 
    156 // For amdgcn the inputs of the linker job are device bitcode and output is
    157 // object file. It calls llvm-link, opt, llc, then lld steps.
    158 void AMDGCN::OpenMPLinker::ConstructJob(Compilation &C, const JobAction &JA,
    159                                         const InputInfo &Output,
    160                                         const InputInfoList &Inputs,
    161                                         const ArgList &Args,
    162                                         const char *LinkingOutput) const {
    163   const ToolChain &TC = getToolChain();
    164   assert(getToolChain().getTriple().isAMDGCN() && "Unsupported target");
    165 
    166   const toolchains::AMDGPUOpenMPToolChain &AMDGPUOpenMPTC =
    167       static_cast<const toolchains::AMDGPUOpenMPToolChain &>(TC);
    168 
    169   std::string GPUArch = Args.getLastArgValue(options::OPT_march_EQ).str();
    170   if (GPUArch.empty()) {
    171     if (!checkSystemForAMDGPU(Args, AMDGPUOpenMPTC, GPUArch))
    172       return;
    173   }
    174 
    175   // Prefix for temporary file name.
    176   std::string Prefix;
    177   for (const auto &II : Inputs)
    178     if (II.isFilename())
    179       Prefix = llvm::sys::path::stem(II.getFilename()).str() + "-" + GPUArch;
    180   assert(Prefix.length() && "no linker inputs are files ");
    181 
    182   // Each command outputs different files.
    183   const char *LLVMLinkCommand =
    184       constructLLVMLinkCommand(C, JA, Inputs, Args, GPUArch, Prefix);
    185 
    186   // Produce readable assembly if save-temps is enabled.
    187   if (C.getDriver().isSaveTempsEnabled())
    188     constructLlcCommand(C, JA, Inputs, Args, GPUArch, Prefix, LLVMLinkCommand,
    189                         /*OutputIsAsm=*/true);
    190   const char *LlcCommand = constructLlcCommand(C, JA, Inputs, Args, GPUArch,
    191                                                Prefix, LLVMLinkCommand);
    192   constructLldCommand(C, JA, Inputs, Output, Args, LlcCommand);
    193 }
    194 
    195 AMDGPUOpenMPToolChain::AMDGPUOpenMPToolChain(const Driver &D,
    196                                              const llvm::Triple &Triple,
    197                                              const ToolChain &HostTC,
    198                                              const ArgList &Args)
    199     : ROCMToolChain(D, Triple, Args), HostTC(HostTC) {
    200   // Lookup binaries into the driver directory, this is used to
    201   // discover the clang-offload-bundler executable.
    202   getProgramPaths().push_back(getDriver().Dir);
    203 }
    204 
    205 void AMDGPUOpenMPToolChain::addClangTargetOptions(
    206     const llvm::opt::ArgList &DriverArgs, llvm::opt::ArgStringList &CC1Args,
    207     Action::OffloadKind DeviceOffloadingKind) const {
    208   HostTC.addClangTargetOptions(DriverArgs, CC1Args, DeviceOffloadingKind);
    209 
    210   std::string GPUArch = DriverArgs.getLastArgValue(options::OPT_march_EQ).str();
    211   if (GPUArch.empty()) {
    212     if (!checkSystemForAMDGPU(DriverArgs, *this, GPUArch))
    213       return;
    214   }
    215 
    216   assert(DeviceOffloadingKind == Action::OFK_OpenMP &&
    217          "Only OpenMP offloading kinds are supported.");
    218 
    219   CC1Args.push_back("-target-cpu");
    220   CC1Args.push_back(DriverArgs.MakeArgStringRef(GPUArch));
    221   CC1Args.push_back("-fcuda-is-device");
    222 
    223   if (DriverArgs.hasArg(options::OPT_nogpulib))
    224     return;
    225   std::string BitcodeSuffix = "amdgcn-" + GPUArch;
    226   addOpenMPDeviceRTL(getDriver(), DriverArgs, CC1Args, BitcodeSuffix,
    227                      getTriple());
    228 }
    229 
    230 llvm::opt::DerivedArgList *AMDGPUOpenMPToolChain::TranslateArgs(
    231     const llvm::opt::DerivedArgList &Args, StringRef BoundArch,
    232     Action::OffloadKind DeviceOffloadKind) const {
    233   DerivedArgList *DAL =
    234       HostTC.TranslateArgs(Args, BoundArch, DeviceOffloadKind);
    235   if (!DAL)
    236     DAL = new DerivedArgList(Args.getBaseArgs());
    237 
    238   const OptTable &Opts = getDriver().getOpts();
    239 
    240   if (DeviceOffloadKind != Action::OFK_OpenMP) {
    241     for (Arg *A : Args) {
    242       DAL->append(A);
    243     }
    244   }
    245 
    246   if (!BoundArch.empty()) {
    247     DAL->eraseArg(options::OPT_march_EQ);
    248     DAL->AddJoinedArg(nullptr, Opts.getOption(options::OPT_march_EQ),
    249                       BoundArch);
    250   }
    251 
    252   return DAL;
    253 }
    254 
    255 Tool *AMDGPUOpenMPToolChain::buildLinker() const {
    256   assert(getTriple().isAMDGCN());
    257   return new tools::AMDGCN::OpenMPLinker(*this);
    258 }
    259 
    260 void AMDGPUOpenMPToolChain::addClangWarningOptions(
    261     ArgStringList &CC1Args) const {
    262   HostTC.addClangWarningOptions(CC1Args);
    263 }
    264 
    265 ToolChain::CXXStdlibType
    266 AMDGPUOpenMPToolChain::GetCXXStdlibType(const ArgList &Args) const {
    267   return HostTC.GetCXXStdlibType(Args);
    268 }
    269 
    270 void AMDGPUOpenMPToolChain::AddClangSystemIncludeArgs(
    271     const ArgList &DriverArgs, ArgStringList &CC1Args) const {
    272   HostTC.AddClangSystemIncludeArgs(DriverArgs, CC1Args);
    273 }
    274 
    275 void AMDGPUOpenMPToolChain::AddIAMCUIncludeArgs(const ArgList &Args,
    276                                                 ArgStringList &CC1Args) const {
    277   HostTC.AddIAMCUIncludeArgs(Args, CC1Args);
    278 }
    279 
    280 SanitizerMask AMDGPUOpenMPToolChain::getSupportedSanitizers() const {
    281   // The AMDGPUOpenMPToolChain only supports sanitizers in the sense that it
    282   // allows sanitizer arguments on the command line if they are supported by the
    283   // host toolchain. The AMDGPUOpenMPToolChain will actually ignore any command
    284   // line arguments for any of these "supported" sanitizers. That means that no
    285   // sanitization of device code is actually supported at this time.
    286   //
    287   // This behavior is necessary because the host and device toolchains
    288   // invocations often share the command line, so the device toolchain must
    289   // tolerate flags meant only for the host toolchain.
    290   return HostTC.getSupportedSanitizers();
    291 }
    292 
    293 VersionTuple
    294 AMDGPUOpenMPToolChain::computeMSVCVersion(const Driver *D,
    295                                           const ArgList &Args) const {
    296   return HostTC.computeMSVCVersion(D, Args);
    297 }
    298