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