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
getOutputFileName(Compilation & C,StringRef Base,const char * Postfix,const char * Extension)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
addLLCOptArg(const llvm::opt::ArgList & Args,llvm::opt::ArgStringList & CmdArgs)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
checkSystemForAMDGPU(const ArgList & Args,const AMDGPUToolChain & TC,std::string & GPUArch)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
constructLLVMLinkCommand(Compilation & C,const JobAction & JA,const InputInfoList & Inputs,const ArgList & Args,StringRef SubArchName,StringRef OutputFilePrefix) const86 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
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) const108 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
constructLldCommand(Compilation & C,const JobAction & JA,const InputInfoList & Inputs,const InputInfo & Output,const llvm::opt::ArgList & Args,const char * InputFileName) const140 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.
ConstructJob(Compilation & C,const JobAction & JA,const InputInfo & Output,const InputInfoList & Inputs,const ArgList & Args,const char * LinkingOutput) const158 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
AMDGPUOpenMPToolChain(const Driver & D,const llvm::Triple & Triple,const ToolChain & HostTC,const ArgList & Args)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
addClangTargetOptions(const llvm::opt::ArgList & DriverArgs,llvm::opt::ArgStringList & CC1Args,Action::OffloadKind DeviceOffloadingKind) const205 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
TranslateArgs(const llvm::opt::DerivedArgList & Args,StringRef BoundArch,Action::OffloadKind DeviceOffloadKind) const230 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
buildLinker() const255 Tool *AMDGPUOpenMPToolChain::buildLinker() const {
256 assert(getTriple().isAMDGCN());
257 return new tools::AMDGCN::OpenMPLinker(*this);
258 }
259
addClangWarningOptions(ArgStringList & CC1Args) const260 void AMDGPUOpenMPToolChain::addClangWarningOptions(
261 ArgStringList &CC1Args) const {
262 HostTC.addClangWarningOptions(CC1Args);
263 }
264
265 ToolChain::CXXStdlibType
GetCXXStdlibType(const ArgList & Args) const266 AMDGPUOpenMPToolChain::GetCXXStdlibType(const ArgList &Args) const {
267 return HostTC.GetCXXStdlibType(Args);
268 }
269
AddClangSystemIncludeArgs(const ArgList & DriverArgs,ArgStringList & CC1Args) const270 void AMDGPUOpenMPToolChain::AddClangSystemIncludeArgs(
271 const ArgList &DriverArgs, ArgStringList &CC1Args) const {
272 HostTC.AddClangSystemIncludeArgs(DriverArgs, CC1Args);
273 }
274
AddIAMCUIncludeArgs(const ArgList & Args,ArgStringList & CC1Args) const275 void AMDGPUOpenMPToolChain::AddIAMCUIncludeArgs(const ArgList &Args,
276 ArgStringList &CC1Args) const {
277 HostTC.AddIAMCUIncludeArgs(Args, CC1Args);
278 }
279
getSupportedSanitizers() const280 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
computeMSVCVersion(const Driver * D,const ArgList & Args) const294 AMDGPUOpenMPToolChain::computeMSVCVersion(const Driver *D,
295 const ArgList &Args) const {
296 return HostTC.computeMSVCVersion(D, Args);
297 }
298