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 
165   // Produce readable assembly if save-temps is enabled.
166   if (C.getDriver().isSaveTempsEnabled())
167     constructLlcCommand(C, JA, Inputs, Args, GPUArch, Prefix, LLVMLinkCommand,
168                         /*OutputIsAsm=*/true);
169   const char *LlcCommand = constructLlcCommand(C, JA, Inputs, Args, GPUArch,
170                                                Prefix, LLVMLinkCommand);
171   constructLldCommand(C, JA, Inputs, Output, Args, LlcCommand);
172 }
173 
174 AMDGPUOpenMPToolChain::AMDGPUOpenMPToolChain(const Driver &D,
175                                              const llvm::Triple &Triple,
176                                              const ToolChain &HostTC,
177                                              const ArgList &Args)
178     : ROCMToolChain(D, Triple, Args), HostTC(HostTC) {
179   // Lookup binaries into the driver directory, this is used to
180   // discover the clang-offload-bundler executable.
181   getProgramPaths().push_back(getDriver().Dir);
182 }
183 
184 void AMDGPUOpenMPToolChain::addClangTargetOptions(
185     const llvm::opt::ArgList &DriverArgs, llvm::opt::ArgStringList &CC1Args,
186     Action::OffloadKind DeviceOffloadingKind) const {
187   HostTC.addClangTargetOptions(DriverArgs, CC1Args, DeviceOffloadingKind);
188 
189   StringRef GpuArch = DriverArgs.getLastArgValue(options::OPT_march_EQ);
190   assert(!GpuArch.empty() && "Must have an explicit GPU arch.");
191   assert(DeviceOffloadingKind == Action::OFK_OpenMP &&
192          "Only OpenMP offloading kinds are supported.");
193 
194   CC1Args.push_back("-target-cpu");
195   CC1Args.push_back(DriverArgs.MakeArgStringRef(GpuArch));
196   CC1Args.push_back("-fcuda-is-device");
197 
198   if (DriverArgs.hasArg(options::OPT_nogpulib))
199     return;
200   std::string BitcodeSuffix = "amdgcn-" + GpuArch.str();
201   addOpenMPDeviceRTL(getDriver(), DriverArgs, CC1Args, BitcodeSuffix,
202                      getTriple());
203 }
204 
205 llvm::opt::DerivedArgList *AMDGPUOpenMPToolChain::TranslateArgs(
206     const llvm::opt::DerivedArgList &Args, StringRef BoundArch,
207     Action::OffloadKind DeviceOffloadKind) const {
208   DerivedArgList *DAL =
209       HostTC.TranslateArgs(Args, BoundArch, DeviceOffloadKind);
210   if (!DAL)
211     DAL = new DerivedArgList(Args.getBaseArgs());
212 
213   const OptTable &Opts = getDriver().getOpts();
214 
215   if (DeviceOffloadKind != Action::OFK_OpenMP) {
216     for (Arg *A : Args) {
217       DAL->append(A);
218     }
219   }
220 
221   if (!BoundArch.empty()) {
222     DAL->eraseArg(options::OPT_march_EQ);
223     DAL->AddJoinedArg(nullptr, Opts.getOption(options::OPT_march_EQ),
224                       BoundArch);
225   }
226 
227   return DAL;
228 }
229 
230 Tool *AMDGPUOpenMPToolChain::buildLinker() const {
231   assert(getTriple().isAMDGCN());
232   return new tools::AMDGCN::OpenMPLinker(*this);
233 }
234 
235 void AMDGPUOpenMPToolChain::addClangWarningOptions(
236     ArgStringList &CC1Args) const {
237   HostTC.addClangWarningOptions(CC1Args);
238 }
239 
240 ToolChain::CXXStdlibType
241 AMDGPUOpenMPToolChain::GetCXXStdlibType(const ArgList &Args) const {
242   return HostTC.GetCXXStdlibType(Args);
243 }
244 
245 void AMDGPUOpenMPToolChain::AddClangSystemIncludeArgs(
246     const ArgList &DriverArgs, ArgStringList &CC1Args) const {
247   HostTC.AddClangSystemIncludeArgs(DriverArgs, CC1Args);
248 }
249 
250 void AMDGPUOpenMPToolChain::AddIAMCUIncludeArgs(const ArgList &Args,
251                                                 ArgStringList &CC1Args) const {
252   HostTC.AddIAMCUIncludeArgs(Args, CC1Args);
253 }
254 
255 SanitizerMask AMDGPUOpenMPToolChain::getSupportedSanitizers() const {
256   // The AMDGPUOpenMPToolChain only supports sanitizers in the sense that it
257   // allows sanitizer arguments on the command line if they are supported by the
258   // host toolchain. The AMDGPUOpenMPToolChain will actually ignore any command
259   // line arguments for any of these "supported" sanitizers. That means that no
260   // sanitization of device code is actually supported at this time.
261   //
262   // This behavior is necessary because the host and device toolchains
263   // invocations often share the command line, so the device toolchain must
264   // tolerate flags meant only for the host toolchain.
265   return HostTC.getSupportedSanitizers();
266 }
267 
268 VersionTuple
269 AMDGPUOpenMPToolChain::computeMSVCVersion(const Driver *D,
270                                           const ArgList &Args) const {
271   return HostTC.computeMSVCVersion(D, Args);
272 }
273