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