HIPAMD.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368
  1. //===--- HIPAMD.cpp - HIP Tool and ToolChain Implementations ----*- 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. #include "HIPAMD.h"
  9. #include "AMDGPU.h"
  10. #include "CommonArgs.h"
  11. #include "HIPUtility.h"
  12. #include "clang/Basic/Cuda.h"
  13. #include "clang/Basic/TargetID.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 "clang/Driver/SanitizerArgs.h"
  20. #include "llvm/Support/Alignment.h"
  21. #include "llvm/Support/FileSystem.h"
  22. #include "llvm/Support/Path.h"
  23. #include "llvm/Support/TargetParser.h"
  24. using namespace clang::driver;
  25. using namespace clang::driver::toolchains;
  26. using namespace clang::driver::tools;
  27. using namespace clang;
  28. using namespace llvm::opt;
  29. #if defined(_WIN32) || defined(_WIN64)
  30. #define NULL_FILE "nul"
  31. #else
  32. #define NULL_FILE "/dev/null"
  33. #endif
  34. static bool shouldSkipSanitizeOption(const ToolChain &TC,
  35. const llvm::opt::ArgList &DriverArgs,
  36. StringRef TargetID,
  37. const llvm::opt::Arg *A) {
  38. // For actions without targetID, do nothing.
  39. if (TargetID.empty())
  40. return false;
  41. Option O = A->getOption();
  42. if (!O.matches(options::OPT_fsanitize_EQ))
  43. return false;
  44. if (!DriverArgs.hasFlag(options::OPT_fgpu_sanitize,
  45. -options::OPT_fno_gpu_sanitize))
  46. return true;
  47. auto &Diags = TC.getDriver().getDiags();
  48. // For simplicity, we only allow -fsanitize=address
  49. SanitizerMask K = parseSanitizerValue(A->getValue(), /*AllowGroups=*/false);
  50. if (K != SanitizerKind::Address)
  51. return true;
  52. llvm::StringMap<bool> FeatureMap;
  53. auto OptionalGpuArch = parseTargetID(TC.getTriple(), TargetID, &FeatureMap);
  54. assert(OptionalGpuArch && "Invalid Target ID");
  55. (void)OptionalGpuArch;
  56. auto Loc = FeatureMap.find("xnack");
  57. if (Loc == FeatureMap.end() || !Loc->second) {
  58. Diags.Report(
  59. clang::diag::warn_drv_unsupported_option_for_offload_arch_req_feature)
  60. << A->getAsString(DriverArgs) << TargetID << "xnack+";
  61. return true;
  62. }
  63. return false;
  64. }
  65. void AMDGCN::Linker::constructLldCommand(Compilation &C, const JobAction &JA,
  66. const InputInfoList &Inputs,
  67. const InputInfo &Output,
  68. const llvm::opt::ArgList &Args) const {
  69. // Construct lld command.
  70. // The output from ld.lld is an HSA code object file.
  71. ArgStringList LldArgs{"-flavor", "gnu", "--no-undefined", "-shared",
  72. "-plugin-opt=-amdgpu-internalize-symbols"};
  73. auto &TC = getToolChain();
  74. auto &D = TC.getDriver();
  75. assert(!Inputs.empty() && "Must have at least one input.");
  76. bool IsThinLTO = D.getLTOMode(/*IsOffload=*/true) == LTOK_Thin;
  77. addLTOOptions(TC, Args, LldArgs, Output, Inputs[0], IsThinLTO);
  78. // Extract all the -m options
  79. std::vector<llvm::StringRef> Features;
  80. amdgpu::getAMDGPUTargetFeatures(D, TC.getTriple(), Args, Features);
  81. // Add features to mattr such as cumode
  82. std::string MAttrString = "-plugin-opt=-mattr=";
  83. for (auto OneFeature : unifyTargetFeatures(Features)) {
  84. MAttrString.append(Args.MakeArgString(OneFeature));
  85. if (OneFeature != Features.back())
  86. MAttrString.append(",");
  87. }
  88. if (!Features.empty())
  89. LldArgs.push_back(Args.MakeArgString(MAttrString));
  90. // ToDo: Remove this option after AMDGPU backend supports ISA-level linking.
  91. // Since AMDGPU backend currently does not support ISA-level linking, all
  92. // called functions need to be imported.
  93. if (IsThinLTO)
  94. LldArgs.push_back(Args.MakeArgString("-plugin-opt=-force-import-all"));
  95. for (const Arg *A : Args.filtered(options::OPT_mllvm)) {
  96. LldArgs.push_back(
  97. Args.MakeArgString(Twine("-plugin-opt=") + A->getValue(0)));
  98. }
  99. if (C.getDriver().isSaveTempsEnabled())
  100. LldArgs.push_back("-save-temps");
  101. addLinkerCompressDebugSectionsOption(TC, Args, LldArgs);
  102. LldArgs.append({"-o", Output.getFilename()});
  103. for (auto Input : Inputs)
  104. LldArgs.push_back(Input.getFilename());
  105. const char *Lld = Args.MakeArgString(getToolChain().GetProgramPath("lld"));
  106. C.addCommand(std::make_unique<Command>(JA, *this, ResponseFileSupport::None(),
  107. Lld, LldArgs, Inputs, Output));
  108. }
  109. // For amdgcn the inputs of the linker job are device bitcode and output is
  110. // object file. It calls llvm-link, opt, llc, then lld steps.
  111. void AMDGCN::Linker::ConstructJob(Compilation &C, const JobAction &JA,
  112. const InputInfo &Output,
  113. const InputInfoList &Inputs,
  114. const ArgList &Args,
  115. const char *LinkingOutput) const {
  116. if (Inputs.size() > 0 &&
  117. Inputs[0].getType() == types::TY_Image &&
  118. JA.getType() == types::TY_Object)
  119. return HIP::constructGenerateObjFileFromHIPFatBinary(C, Output, Inputs,
  120. Args, JA, *this);
  121. if (JA.getType() == types::TY_HIP_FATBIN)
  122. return HIP::constructHIPFatbinCommand(C, JA, Output.getFilename(), Inputs,
  123. Args, *this);
  124. return constructLldCommand(C, JA, Inputs, Output, Args);
  125. }
  126. HIPAMDToolChain::HIPAMDToolChain(const Driver &D, const llvm::Triple &Triple,
  127. const ToolChain &HostTC, const ArgList &Args)
  128. : ROCMToolChain(D, Triple, Args), HostTC(HostTC) {
  129. // Lookup binaries into the driver directory, this is used to
  130. // discover the clang-offload-bundler executable.
  131. getProgramPaths().push_back(getDriver().Dir);
  132. // Diagnose unsupported sanitizer options only once.
  133. for (auto A : Args.filtered(options::OPT_fsanitize_EQ)) {
  134. SanitizerMask K = parseSanitizerValue(A->getValue(), /*AllowGroups=*/false);
  135. if (K != SanitizerKind::Address)
  136. D.getDiags().Report(clang::diag::warn_drv_unsupported_option_for_target)
  137. << A->getAsString(Args) << getTriple().str();
  138. }
  139. }
  140. void HIPAMDToolChain::addClangTargetOptions(
  141. const llvm::opt::ArgList &DriverArgs, llvm::opt::ArgStringList &CC1Args,
  142. Action::OffloadKind DeviceOffloadingKind) const {
  143. HostTC.addClangTargetOptions(DriverArgs, CC1Args, DeviceOffloadingKind);
  144. assert(DeviceOffloadingKind == Action::OFK_HIP &&
  145. "Only HIP offloading kinds are supported for GPUs.");
  146. CC1Args.push_back("-fcuda-is-device");
  147. if (DriverArgs.hasFlag(options::OPT_fcuda_approx_transcendentals,
  148. options::OPT_fno_cuda_approx_transcendentals, false))
  149. CC1Args.push_back("-fcuda-approx-transcendentals");
  150. if (!DriverArgs.hasFlag(options::OPT_fgpu_rdc, options::OPT_fno_gpu_rdc,
  151. false))
  152. CC1Args.append({"-mllvm", "-amdgpu-internalize-symbols"});
  153. StringRef MaxThreadsPerBlock =
  154. DriverArgs.getLastArgValue(options::OPT_gpu_max_threads_per_block_EQ);
  155. if (!MaxThreadsPerBlock.empty()) {
  156. std::string ArgStr =
  157. std::string("--gpu-max-threads-per-block=") + MaxThreadsPerBlock.str();
  158. CC1Args.push_back(DriverArgs.MakeArgStringRef(ArgStr));
  159. }
  160. CC1Args.push_back("-fcuda-allow-variadic-functions");
  161. // Default to "hidden" visibility, as object level linking will not be
  162. // supported for the foreseeable future.
  163. if (!DriverArgs.hasArg(options::OPT_fvisibility_EQ,
  164. options::OPT_fvisibility_ms_compat)) {
  165. CC1Args.append({"-fvisibility", "hidden"});
  166. CC1Args.push_back("-fapply-global-visibility-to-externs");
  167. }
  168. llvm::for_each(getHIPDeviceLibs(DriverArgs), [&](auto BCFile) {
  169. CC1Args.push_back(BCFile.ShouldInternalize ? "-mlink-builtin-bitcode"
  170. : "-mlink-bitcode-file");
  171. CC1Args.push_back(DriverArgs.MakeArgString(BCFile.Path));
  172. });
  173. }
  174. llvm::opt::DerivedArgList *
  175. HIPAMDToolChain::TranslateArgs(const llvm::opt::DerivedArgList &Args,
  176. StringRef BoundArch,
  177. Action::OffloadKind DeviceOffloadKind) const {
  178. DerivedArgList *DAL =
  179. HostTC.TranslateArgs(Args, BoundArch, DeviceOffloadKind);
  180. if (!DAL)
  181. DAL = new DerivedArgList(Args.getBaseArgs());
  182. const OptTable &Opts = getDriver().getOpts();
  183. for (Arg *A : Args) {
  184. if (!shouldSkipArgument(A) &&
  185. !shouldSkipSanitizeOption(*this, Args, BoundArch, A))
  186. DAL->append(A);
  187. }
  188. if (!BoundArch.empty()) {
  189. DAL->eraseArg(options::OPT_mcpu_EQ);
  190. DAL->AddJoinedArg(nullptr, Opts.getOption(options::OPT_mcpu_EQ), BoundArch);
  191. checkTargetID(*DAL);
  192. }
  193. return DAL;
  194. }
  195. Tool *HIPAMDToolChain::buildLinker() const {
  196. assert(getTriple().getArch() == llvm::Triple::amdgcn);
  197. return new tools::AMDGCN::Linker(*this);
  198. }
  199. void HIPAMDToolChain::addClangWarningOptions(ArgStringList &CC1Args) const {
  200. HostTC.addClangWarningOptions(CC1Args);
  201. }
  202. ToolChain::CXXStdlibType
  203. HIPAMDToolChain::GetCXXStdlibType(const ArgList &Args) const {
  204. return HostTC.GetCXXStdlibType(Args);
  205. }
  206. void HIPAMDToolChain::AddClangSystemIncludeArgs(const ArgList &DriverArgs,
  207. ArgStringList &CC1Args) const {
  208. HostTC.AddClangSystemIncludeArgs(DriverArgs, CC1Args);
  209. }
  210. void HIPAMDToolChain::AddClangCXXStdlibIncludeArgs(
  211. const ArgList &Args, ArgStringList &CC1Args) const {
  212. HostTC.AddClangCXXStdlibIncludeArgs(Args, CC1Args);
  213. }
  214. void HIPAMDToolChain::AddIAMCUIncludeArgs(const ArgList &Args,
  215. ArgStringList &CC1Args) const {
  216. HostTC.AddIAMCUIncludeArgs(Args, CC1Args);
  217. }
  218. void HIPAMDToolChain::AddHIPIncludeArgs(const ArgList &DriverArgs,
  219. ArgStringList &CC1Args) const {
  220. RocmInstallation.AddHIPIncludeArgs(DriverArgs, CC1Args);
  221. }
  222. SanitizerMask HIPAMDToolChain::getSupportedSanitizers() const {
  223. // The HIPAMDToolChain only supports sanitizers in the sense that it allows
  224. // sanitizer arguments on the command line if they are supported by the host
  225. // toolchain. The HIPAMDToolChain will actually ignore any command line
  226. // arguments for any of these "supported" sanitizers. That means that no
  227. // sanitization of device code is actually supported at this time.
  228. //
  229. // This behavior is necessary because the host and device toolchains
  230. // invocations often share the command line, so the device toolchain must
  231. // tolerate flags meant only for the host toolchain.
  232. return HostTC.getSupportedSanitizers();
  233. }
  234. VersionTuple HIPAMDToolChain::computeMSVCVersion(const Driver *D,
  235. const ArgList &Args) const {
  236. return HostTC.computeMSVCVersion(D, Args);
  237. }
  238. llvm::SmallVector<ToolChain::BitCodeLibraryInfo, 12>
  239. HIPAMDToolChain::getHIPDeviceLibs(const llvm::opt::ArgList &DriverArgs) const {
  240. llvm::SmallVector<BitCodeLibraryInfo, 12> BCLibs;
  241. if (DriverArgs.hasArg(options::OPT_nogpulib))
  242. return {};
  243. ArgStringList LibraryPaths;
  244. // Find in --hip-device-lib-path and HIP_LIBRARY_PATH.
  245. for (auto Path : RocmInstallation.getRocmDeviceLibPathArg())
  246. LibraryPaths.push_back(DriverArgs.MakeArgString(Path));
  247. addDirectoryList(DriverArgs, LibraryPaths, "", "HIP_DEVICE_LIB_PATH");
  248. // Maintain compatability with --hip-device-lib.
  249. auto BCLibArgs = DriverArgs.getAllArgValues(options::OPT_hip_device_lib_EQ);
  250. if (!BCLibArgs.empty()) {
  251. llvm::for_each(BCLibArgs, [&](StringRef BCName) {
  252. StringRef FullName;
  253. for (std::string LibraryPath : LibraryPaths) {
  254. SmallString<128> Path(LibraryPath);
  255. llvm::sys::path::append(Path, BCName);
  256. FullName = Path;
  257. if (llvm::sys::fs::exists(FullName)) {
  258. BCLibs.push_back(FullName);
  259. return;
  260. }
  261. }
  262. getDriver().Diag(diag::err_drv_no_such_file) << BCName;
  263. });
  264. } else {
  265. if (!RocmInstallation.hasDeviceLibrary()) {
  266. getDriver().Diag(diag::err_drv_no_rocm_device_lib) << 0;
  267. return {};
  268. }
  269. StringRef GpuArch = getGPUArch(DriverArgs);
  270. assert(!GpuArch.empty() && "Must have an explicit GPU arch.");
  271. // If --hip-device-lib is not set, add the default bitcode libraries.
  272. if (DriverArgs.hasFlag(options::OPT_fgpu_sanitize,
  273. options::OPT_fno_gpu_sanitize) &&
  274. getSanitizerArgs(DriverArgs).needsAsanRt()) {
  275. auto AsanRTL = RocmInstallation.getAsanRTLPath();
  276. if (AsanRTL.empty()) {
  277. unsigned DiagID = getDriver().getDiags().getCustomDiagID(
  278. DiagnosticsEngine::Error,
  279. "AMDGPU address sanitizer runtime library (asanrtl) is not found. "
  280. "Please install ROCm device library which supports address "
  281. "sanitizer");
  282. getDriver().Diag(DiagID);
  283. return {};
  284. } else
  285. BCLibs.push_back({AsanRTL.str(), /*ShouldInternalize=*/false});
  286. }
  287. // Add the HIP specific bitcode library.
  288. BCLibs.push_back(RocmInstallation.getHIPPath());
  289. // Add common device libraries like ocml etc.
  290. for (auto N : getCommonDeviceLibNames(DriverArgs, GpuArch.str()))
  291. BCLibs.push_back(StringRef(N));
  292. // Add instrument lib.
  293. auto InstLib =
  294. DriverArgs.getLastArgValue(options::OPT_gpu_instrument_lib_EQ);
  295. if (InstLib.empty())
  296. return BCLibs;
  297. if (llvm::sys::fs::exists(InstLib))
  298. BCLibs.push_back(InstLib);
  299. else
  300. getDriver().Diag(diag::err_drv_no_such_file) << InstLib;
  301. }
  302. return BCLibs;
  303. }
  304. void HIPAMDToolChain::checkTargetID(
  305. const llvm::opt::ArgList &DriverArgs) const {
  306. auto PTID = getParsedTargetID(DriverArgs);
  307. if (PTID.OptionalTargetID && !PTID.OptionalGPUArch) {
  308. getDriver().Diag(clang::diag::err_drv_bad_target_id)
  309. << PTID.OptionalTargetID.getValue();
  310. }
  311. }