1 //===--- HIPAMD.cpp - HIP Tool and ToolChain Implementations ----*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
11 #include "CommonArgs.h"
12 #include "HIPUtility.h"
13 #include "clang/Basic/Cuda.h"
14 #include "clang/Basic/TargetID.h"
15 #include "clang/Driver/Compilation.h"
16 #include "clang/Driver/Driver.h"
17 #include "clang/Driver/DriverDiagnostic.h"
18 #include "clang/Driver/InputInfo.h"
19 #include "clang/Driver/Options.h"
20 #include "clang/Driver/SanitizerArgs.h"
21 #include "llvm/Support/Alignment.h"
22 #include "llvm/Support/FileSystem.h"
23 #include "llvm/Support/Path.h"
24 #include "llvm/Support/TargetParser.h"
26 using namespace clang::driver
;
27 using namespace clang::driver::toolchains
;
28 using namespace clang::driver::tools
;
29 using namespace clang
;
30 using namespace llvm::opt
;
32 #if defined(_WIN32) || defined(_WIN64)
33 #define NULL_FILE "nul"
35 #define NULL_FILE "/dev/null"
38 static bool shouldSkipSanitizeOption(const ToolChain
&TC
,
39 const llvm::opt::ArgList
&DriverArgs
,
41 const llvm::opt::Arg
*A
) {
42 // For actions without targetID, do nothing.
45 Option O
= A
->getOption();
46 if (!O
.matches(options::OPT_fsanitize_EQ
))
49 if (!DriverArgs
.hasFlag(options::OPT_fgpu_sanitize
,
50 options::OPT_fno_gpu_sanitize
, true))
53 auto &Diags
= TC
.getDriver().getDiags();
55 // For simplicity, we only allow -fsanitize=address
56 SanitizerMask K
= parseSanitizerValue(A
->getValue(), /*AllowGroups=*/false);
57 if (K
!= SanitizerKind::Address
)
60 llvm::StringMap
<bool> FeatureMap
;
61 auto OptionalGpuArch
= parseTargetID(TC
.getTriple(), TargetID
, &FeatureMap
);
63 assert(OptionalGpuArch
&& "Invalid Target ID");
64 (void)OptionalGpuArch
;
65 auto Loc
= FeatureMap
.find("xnack");
66 if (Loc
== FeatureMap
.end() || !Loc
->second
) {
68 clang::diag::warn_drv_unsupported_option_for_offload_arch_req_feature
)
69 << A
->getAsString(DriverArgs
) << TargetID
<< "xnack+";
75 void AMDGCN::Linker::constructLlvmLinkCommand(Compilation
&C
,
77 const InputInfoList
&Inputs
,
78 const InputInfo
&Output
,
79 const llvm::opt::ArgList
&Args
) const {
80 // Construct llvm-link command.
81 // The output from llvm-link is a bitcode file.
82 ArgStringList LlvmLinkArgs
;
84 assert(!Inputs
.empty() && "Must have at least one input.");
86 LlvmLinkArgs
.append({"-o", Output
.getFilename()});
87 for (auto Input
: Inputs
)
88 LlvmLinkArgs
.push_back(Input
.getFilename());
90 // Look for archive of bundled bitcode in arguments, and add temporary files
91 // for the extracted archive of bitcode to inputs.
92 auto TargetID
= Args
.getLastArgValue(options::OPT_mcpu_EQ
);
93 AddStaticDeviceLibsLinking(C
, *this, JA
, Inputs
, Args
, LlvmLinkArgs
, "amdgcn",
95 /*IsBitCodeSDL=*/true,
96 /*PostClangLink=*/false);
98 const char *LlvmLink
=
99 Args
.MakeArgString(getToolChain().GetProgramPath("llvm-link"));
100 C
.addCommand(std::make_unique
<Command
>(JA
, *this, ResponseFileSupport::None(),
101 LlvmLink
, LlvmLinkArgs
, Inputs
,
105 void AMDGCN::Linker::constructLldCommand(Compilation
&C
, const JobAction
&JA
,
106 const InputInfoList
&Inputs
,
107 const InputInfo
&Output
,
108 const llvm::opt::ArgList
&Args
) const {
109 // Construct lld command.
110 // The output from ld.lld is an HSA code object file.
111 ArgStringList LldArgs
{"-flavor", "gnu", "--no-undefined", "-shared",
112 "-plugin-opt=-amdgpu-internalize-symbols"};
114 auto &TC
= getToolChain();
115 auto &D
= TC
.getDriver();
116 assert(!Inputs
.empty() && "Must have at least one input.");
117 bool IsThinLTO
= D
.getLTOMode(/*IsOffload=*/true) == LTOK_Thin
;
118 addLTOOptions(TC
, Args
, LldArgs
, Output
, Inputs
[0], IsThinLTO
);
120 // Extract all the -m options
121 std::vector
<llvm::StringRef
> Features
;
122 amdgpu::getAMDGPUTargetFeatures(D
, TC
.getTriple(), Args
, Features
);
124 // Add features to mattr such as cumode
125 std::string MAttrString
= "-plugin-opt=-mattr=";
126 for (auto OneFeature
: unifyTargetFeatures(Features
)) {
127 MAttrString
.append(Args
.MakeArgString(OneFeature
));
128 if (OneFeature
!= Features
.back())
129 MAttrString
.append(",");
131 if (!Features
.empty())
132 LldArgs
.push_back(Args
.MakeArgString(MAttrString
));
134 // ToDo: Remove this option after AMDGPU backend supports ISA-level linking.
135 // Since AMDGPU backend currently does not support ISA-level linking, all
136 // called functions need to be imported.
138 LldArgs
.push_back(Args
.MakeArgString("-plugin-opt=-force-import-all"));
140 for (const Arg
*A
: Args
.filtered(options::OPT_mllvm
)) {
142 Args
.MakeArgString(Twine("-plugin-opt=") + A
->getValue(0)));
145 if (C
.getDriver().isSaveTempsEnabled())
146 LldArgs
.push_back("-save-temps");
148 addLinkerCompressDebugSectionsOption(TC
, Args
, LldArgs
);
150 for (auto *Arg
: Args
.filtered(options::OPT_Xoffload_linker
))
151 LldArgs
.push_back(Arg
->getValue(1));
153 LldArgs
.append({"-o", Output
.getFilename()});
154 for (auto Input
: Inputs
)
155 LldArgs
.push_back(Input
.getFilename());
157 // Look for archive of bundled bitcode in arguments, and add temporary files
158 // for the extracted archive of bitcode to inputs.
159 auto TargetID
= Args
.getLastArgValue(options::OPT_mcpu_EQ
);
160 AddStaticDeviceLibsLinking(C
, *this, JA
, Inputs
, Args
, LldArgs
, "amdgcn",
162 /*IsBitCodeSDL=*/true,
163 /*PostClangLink=*/false);
165 const char *Lld
= Args
.MakeArgString(getToolChain().GetProgramPath("lld"));
166 C
.addCommand(std::make_unique
<Command
>(JA
, *this, ResponseFileSupport::None(),
167 Lld
, LldArgs
, Inputs
, Output
));
170 // For amdgcn the inputs of the linker job are device bitcode and output is
171 // either an object file or bitcode (-emit-llvm). It calls llvm-link, opt,
172 // llc, then lld steps.
173 void AMDGCN::Linker::ConstructJob(Compilation
&C
, const JobAction
&JA
,
174 const InputInfo
&Output
,
175 const InputInfoList
&Inputs
,
177 const char *LinkingOutput
) const {
178 if (Inputs
.size() > 0 &&
179 Inputs
[0].getType() == types::TY_Image
&&
180 JA
.getType() == types::TY_Object
)
181 return HIP::constructGenerateObjFileFromHIPFatBinary(C
, Output
, Inputs
,
184 if (JA
.getType() == types::TY_HIP_FATBIN
)
185 return HIP::constructHIPFatbinCommand(C
, JA
, Output
.getFilename(), Inputs
,
188 if (JA
.getType() == types::TY_LLVM_BC
)
189 return constructLlvmLinkCommand(C
, JA
, Inputs
, Output
, Args
);
191 return constructLldCommand(C
, JA
, Inputs
, Output
, Args
);
194 HIPAMDToolChain::HIPAMDToolChain(const Driver
&D
, const llvm::Triple
&Triple
,
195 const ToolChain
&HostTC
, const ArgList
&Args
)
196 : ROCMToolChain(D
, Triple
, Args
), HostTC(HostTC
) {
197 // Lookup binaries into the driver directory, this is used to
198 // discover the clang-offload-bundler executable.
199 getProgramPaths().push_back(getDriver().Dir
);
201 // Diagnose unsupported sanitizer options only once.
202 if (!Args
.hasFlag(options::OPT_fgpu_sanitize
, options::OPT_fno_gpu_sanitize
,
205 for (auto *A
: Args
.filtered(options::OPT_fsanitize_EQ
)) {
206 SanitizerMask K
= parseSanitizerValue(A
->getValue(), /*AllowGroups=*/false);
207 if (K
!= SanitizerKind::Address
)
208 D
.getDiags().Report(clang::diag::warn_drv_unsupported_option_for_target
)
209 << A
->getAsString(Args
) << getTriple().str();
213 void HIPAMDToolChain::addClangTargetOptions(
214 const llvm::opt::ArgList
&DriverArgs
, llvm::opt::ArgStringList
&CC1Args
,
215 Action::OffloadKind DeviceOffloadingKind
) const {
216 HostTC
.addClangTargetOptions(DriverArgs
, CC1Args
, DeviceOffloadingKind
);
218 assert(DeviceOffloadingKind
== Action::OFK_HIP
&&
219 "Only HIP offloading kinds are supported for GPUs.");
221 CC1Args
.push_back("-fcuda-is-device");
223 if (DriverArgs
.hasFlag(options::OPT_fcuda_approx_transcendentals
,
224 options::OPT_fno_cuda_approx_transcendentals
, false))
225 CC1Args
.push_back("-fcuda-approx-transcendentals");
227 if (!DriverArgs
.hasFlag(options::OPT_fgpu_rdc
, options::OPT_fno_gpu_rdc
,
229 CC1Args
.append({"-mllvm", "-amdgpu-internalize-symbols"});
231 StringRef MaxThreadsPerBlock
=
232 DriverArgs
.getLastArgValue(options::OPT_gpu_max_threads_per_block_EQ
);
233 if (!MaxThreadsPerBlock
.empty()) {
235 std::string("--gpu-max-threads-per-block=") + MaxThreadsPerBlock
.str();
236 CC1Args
.push_back(DriverArgs
.MakeArgStringRef(ArgStr
));
239 CC1Args
.push_back("-fcuda-allow-variadic-functions");
241 // Default to "hidden" visibility, as object level linking will not be
242 // supported for the foreseeable future.
243 if (!DriverArgs
.hasArg(options::OPT_fvisibility_EQ
,
244 options::OPT_fvisibility_ms_compat
)) {
245 CC1Args
.append({"-fvisibility=hidden"});
246 CC1Args
.push_back("-fapply-global-visibility-to-externs");
249 for (auto BCFile
: getDeviceLibs(DriverArgs
)) {
250 CC1Args
.push_back(BCFile
.ShouldInternalize
? "-mlink-builtin-bitcode"
251 : "-mlink-bitcode-file");
252 CC1Args
.push_back(DriverArgs
.MakeArgString(BCFile
.Path
));
256 llvm::opt::DerivedArgList
*
257 HIPAMDToolChain::TranslateArgs(const llvm::opt::DerivedArgList
&Args
,
259 Action::OffloadKind DeviceOffloadKind
) const {
260 DerivedArgList
*DAL
=
261 HostTC
.TranslateArgs(Args
, BoundArch
, DeviceOffloadKind
);
263 DAL
= new DerivedArgList(Args
.getBaseArgs());
265 const OptTable
&Opts
= getDriver().getOpts();
267 for (Arg
*A
: Args
) {
268 if (!shouldSkipArgument(A
) &&
269 !shouldSkipSanitizeOption(*this, Args
, BoundArch
, A
))
273 if (!BoundArch
.empty()) {
274 DAL
->eraseArg(options::OPT_mcpu_EQ
);
275 DAL
->AddJoinedArg(nullptr, Opts
.getOption(options::OPT_mcpu_EQ
), BoundArch
);
282 Tool
*HIPAMDToolChain::buildLinker() const {
283 assert(getTriple().getArch() == llvm::Triple::amdgcn
);
284 return new tools::AMDGCN::Linker(*this);
287 void HIPAMDToolChain::addClangWarningOptions(ArgStringList
&CC1Args
) const {
288 HostTC
.addClangWarningOptions(CC1Args
);
291 ToolChain::CXXStdlibType
292 HIPAMDToolChain::GetCXXStdlibType(const ArgList
&Args
) const {
293 return HostTC
.GetCXXStdlibType(Args
);
296 void HIPAMDToolChain::AddClangSystemIncludeArgs(const ArgList
&DriverArgs
,
297 ArgStringList
&CC1Args
) const {
298 HostTC
.AddClangSystemIncludeArgs(DriverArgs
, CC1Args
);
301 void HIPAMDToolChain::AddClangCXXStdlibIncludeArgs(
302 const ArgList
&Args
, ArgStringList
&CC1Args
) const {
303 HostTC
.AddClangCXXStdlibIncludeArgs(Args
, CC1Args
);
306 void HIPAMDToolChain::AddIAMCUIncludeArgs(const ArgList
&Args
,
307 ArgStringList
&CC1Args
) const {
308 HostTC
.AddIAMCUIncludeArgs(Args
, CC1Args
);
311 void HIPAMDToolChain::AddHIPIncludeArgs(const ArgList
&DriverArgs
,
312 ArgStringList
&CC1Args
) const {
313 RocmInstallation
.AddHIPIncludeArgs(DriverArgs
, CC1Args
);
316 SanitizerMask
HIPAMDToolChain::getSupportedSanitizers() const {
317 // The HIPAMDToolChain only supports sanitizers in the sense that it allows
318 // sanitizer arguments on the command line if they are supported by the host
319 // toolchain. The HIPAMDToolChain will actually ignore any command line
320 // arguments for any of these "supported" sanitizers. That means that no
321 // sanitization of device code is actually supported at this time.
323 // This behavior is necessary because the host and device toolchains
324 // invocations often share the command line, so the device toolchain must
325 // tolerate flags meant only for the host toolchain.
326 return HostTC
.getSupportedSanitizers();
329 VersionTuple
HIPAMDToolChain::computeMSVCVersion(const Driver
*D
,
330 const ArgList
&Args
) const {
331 return HostTC
.computeMSVCVersion(D
, Args
);
334 llvm::SmallVector
<ToolChain::BitCodeLibraryInfo
, 12>
335 HIPAMDToolChain::getDeviceLibs(const llvm::opt::ArgList
&DriverArgs
) const {
336 llvm::SmallVector
<BitCodeLibraryInfo
, 12> BCLibs
;
337 if (DriverArgs
.hasArg(options::OPT_nogpulib
))
339 ArgStringList LibraryPaths
;
341 // Find in --hip-device-lib-path and HIP_LIBRARY_PATH.
342 for (auto Path
: RocmInstallation
.getRocmDeviceLibPathArg())
343 LibraryPaths
.push_back(DriverArgs
.MakeArgString(Path
));
345 addDirectoryList(DriverArgs
, LibraryPaths
, "", "HIP_DEVICE_LIB_PATH");
347 // Maintain compatability with --hip-device-lib.
348 auto BCLibArgs
= DriverArgs
.getAllArgValues(options::OPT_hip_device_lib_EQ
);
349 if (!BCLibArgs
.empty()) {
350 llvm::for_each(BCLibArgs
, [&](StringRef BCName
) {
352 for (std::string LibraryPath
: LibraryPaths
) {
353 SmallString
<128> Path(LibraryPath
);
354 llvm::sys::path::append(Path
, BCName
);
356 if (llvm::sys::fs::exists(FullName
)) {
357 BCLibs
.push_back(FullName
);
361 getDriver().Diag(diag::err_drv_no_such_file
) << BCName
;
364 if (!RocmInstallation
.hasDeviceLibrary()) {
365 getDriver().Diag(diag::err_drv_no_rocm_device_lib
) << 0;
368 StringRef GpuArch
= getGPUArch(DriverArgs
);
369 assert(!GpuArch
.empty() && "Must have an explicit GPU arch.");
371 // If --hip-device-lib is not set, add the default bitcode libraries.
372 if (DriverArgs
.hasFlag(options::OPT_fgpu_sanitize
,
373 options::OPT_fno_gpu_sanitize
, true) &&
374 getSanitizerArgs(DriverArgs
).needsAsanRt()) {
375 auto AsanRTL
= RocmInstallation
.getAsanRTLPath();
376 if (AsanRTL
.empty()) {
377 unsigned DiagID
= getDriver().getDiags().getCustomDiagID(
378 DiagnosticsEngine::Error
,
379 "AMDGPU address sanitizer runtime library (asanrtl) is not found. "
380 "Please install ROCm device library which supports address "
382 getDriver().Diag(DiagID
);
385 BCLibs
.push_back({AsanRTL
.str(), /*ShouldInternalize=*/false});
388 // Add the HIP specific bitcode library.
389 BCLibs
.push_back(RocmInstallation
.getHIPPath());
391 // Add common device libraries like ocml etc.
392 for (auto N
: getCommonDeviceLibNames(DriverArgs
, GpuArch
.str()))
393 BCLibs
.push_back(StringRef(N
));
395 // Add instrument lib.
397 DriverArgs
.getLastArgValue(options::OPT_gpu_instrument_lib_EQ
);
400 if (llvm::sys::fs::exists(InstLib
))
401 BCLibs
.push_back(InstLib
);
403 getDriver().Diag(diag::err_drv_no_such_file
) << InstLib
;
409 void HIPAMDToolChain::checkTargetID(
410 const llvm::opt::ArgList
&DriverArgs
) const {
411 auto PTID
= getParsedTargetID(DriverArgs
);
412 if (PTID
.OptionalTargetID
&& !PTID
.OptionalGPUArch
) {
413 getDriver().Diag(clang::diag::err_drv_bad_target_id
)
414 << *PTID
.OptionalTargetID
;