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 9 #include "HIPAMD.h" 10 #include "AMDGPU.h" 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" 25 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; 31 32 #if defined(_WIN32) || defined(_WIN64) 33 #define NULL_FILE "nul" 34 #else 35 #define NULL_FILE "/dev/null" 36 #endif 37 38 static bool shouldSkipSanitizeOption(const ToolChain &TC, 39 const llvm::opt::ArgList &DriverArgs, 40 StringRef TargetID, 41 const llvm::opt::Arg *A) { 42 // For actions without targetID, do nothing. 43 if (TargetID.empty()) 44 return false; 45 Option O = A->getOption(); 46 if (!O.matches(options::OPT_fsanitize_EQ)) 47 return false; 48 49 if (!DriverArgs.hasFlag(options::OPT_fgpu_sanitize, 50 options::OPT_fno_gpu_sanitize, true)) 51 return true; 52 53 auto &Diags = TC.getDriver().getDiags(); 54 55 // For simplicity, we only allow -fsanitize=address 56 SanitizerMask K = parseSanitizerValue(A->getValue(), /*AllowGroups=*/false); 57 if (K != SanitizerKind::Address) 58 return true; 59 60 llvm::StringMap<bool> FeatureMap; 61 auto OptionalGpuArch = parseTargetID(TC.getTriple(), TargetID, &FeatureMap); 62 63 assert(OptionalGpuArch && "Invalid Target ID"); 64 (void)OptionalGpuArch; 65 auto Loc = FeatureMap.find("xnack"); 66 if (Loc == FeatureMap.end() || !Loc->second) { 67 Diags.Report( 68 clang::diag::warn_drv_unsupported_option_for_offload_arch_req_feature) 69 << A->getAsString(DriverArgs) << TargetID << "xnack+"; 70 return true; 71 } 72 return false; 73 } 74 75 void AMDGCN::Linker::constructLlvmLinkCommand(Compilation &C, 76 const JobAction &JA, 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; 83 84 assert(!Inputs.empty() && "Must have at least one input."); 85 86 LlvmLinkArgs.append({"-o", Output.getFilename()}); 87 for (auto Input : Inputs) 88 LlvmLinkArgs.push_back(Input.getFilename()); 89 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", 94 TargetID, 95 /*IsBitCodeSDL=*/true, 96 /*PostClangLink=*/false); 97 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, 102 Output)); 103 } 104 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"}; 113 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); 119 120 // Extract all the -m options 121 std::vector<llvm::StringRef> Features; 122 amdgpu::getAMDGPUTargetFeatures(D, TC.getTriple(), Args, Features); 123 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(","); 130 } 131 if (!Features.empty()) 132 LldArgs.push_back(Args.MakeArgString(MAttrString)); 133 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. 137 if (IsThinLTO) 138 LldArgs.push_back(Args.MakeArgString("-plugin-opt=-force-import-all")); 139 140 for (const Arg *A : Args.filtered(options::OPT_mllvm)) { 141 LldArgs.push_back( 142 Args.MakeArgString(Twine("-plugin-opt=") + A->getValue(0))); 143 } 144 145 if (C.getDriver().isSaveTempsEnabled()) 146 LldArgs.push_back("-save-temps"); 147 148 addLinkerCompressDebugSectionsOption(TC, Args, LldArgs); 149 150 for (auto *Arg : Args.filtered(options::OPT_Xoffload_linker)) 151 LldArgs.push_back(Arg->getValue(1)); 152 153 LldArgs.append({"-o", Output.getFilename()}); 154 for (auto Input : Inputs) 155 LldArgs.push_back(Input.getFilename()); 156 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", 161 TargetID, 162 /*IsBitCodeSDL=*/true, 163 /*PostClangLink=*/false); 164 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)); 168 } 169 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, 176 const ArgList &Args, 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, 182 Args, JA, *this); 183 184 if (JA.getType() == types::TY_HIP_FATBIN) 185 return HIP::constructHIPFatbinCommand(C, JA, Output.getFilename(), Inputs, 186 Args, *this); 187 188 if (JA.getType() == types::TY_LLVM_BC) 189 return constructLlvmLinkCommand(C, JA, Inputs, Output, Args); 190 191 return constructLldCommand(C, JA, Inputs, Output, Args); 192 } 193 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); 200 201 // Diagnose unsupported sanitizer options only once. 202 if (!Args.hasFlag(options::OPT_fgpu_sanitize, options::OPT_fno_gpu_sanitize, 203 true)) 204 return; 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(); 210 } 211 } 212 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); 217 218 assert(DeviceOffloadingKind == Action::OFK_HIP && 219 "Only HIP offloading kinds are supported for GPUs."); 220 221 CC1Args.push_back("-fcuda-is-device"); 222 223 if (DriverArgs.hasFlag(options::OPT_fcuda_approx_transcendentals, 224 options::OPT_fno_cuda_approx_transcendentals, false)) 225 CC1Args.push_back("-fcuda-approx-transcendentals"); 226 227 if (!DriverArgs.hasFlag(options::OPT_fgpu_rdc, options::OPT_fno_gpu_rdc, 228 false)) 229 CC1Args.append({"-mllvm", "-amdgpu-internalize-symbols"}); 230 231 StringRef MaxThreadsPerBlock = 232 DriverArgs.getLastArgValue(options::OPT_gpu_max_threads_per_block_EQ); 233 if (!MaxThreadsPerBlock.empty()) { 234 std::string ArgStr = 235 std::string("--gpu-max-threads-per-block=") + MaxThreadsPerBlock.str(); 236 CC1Args.push_back(DriverArgs.MakeArgStringRef(ArgStr)); 237 } 238 239 CC1Args.push_back("-fcuda-allow-variadic-functions"); 240 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"); 247 } 248 249 for (auto BCFile : getHIPDeviceLibs(DriverArgs)) { 250 CC1Args.push_back(BCFile.ShouldInternalize ? "-mlink-builtin-bitcode" 251 : "-mlink-bitcode-file"); 252 CC1Args.push_back(DriverArgs.MakeArgString(BCFile.Path)); 253 } 254 } 255 256 llvm::opt::DerivedArgList * 257 HIPAMDToolChain::TranslateArgs(const llvm::opt::DerivedArgList &Args, 258 StringRef BoundArch, 259 Action::OffloadKind DeviceOffloadKind) const { 260 DerivedArgList *DAL = 261 HostTC.TranslateArgs(Args, BoundArch, DeviceOffloadKind); 262 if (!DAL) 263 DAL = new DerivedArgList(Args.getBaseArgs()); 264 265 const OptTable &Opts = getDriver().getOpts(); 266 267 for (Arg *A : Args) { 268 if (!shouldSkipArgument(A) && 269 !shouldSkipSanitizeOption(*this, Args, BoundArch, A)) 270 DAL->append(A); 271 } 272 273 if (!BoundArch.empty()) { 274 DAL->eraseArg(options::OPT_mcpu_EQ); 275 DAL->AddJoinedArg(nullptr, Opts.getOption(options::OPT_mcpu_EQ), BoundArch); 276 checkTargetID(*DAL); 277 } 278 279 return DAL; 280 } 281 282 Tool *HIPAMDToolChain::buildLinker() const { 283 assert(getTriple().getArch() == llvm::Triple::amdgcn); 284 return new tools::AMDGCN::Linker(*this); 285 } 286 287 void HIPAMDToolChain::addClangWarningOptions(ArgStringList &CC1Args) const { 288 HostTC.addClangWarningOptions(CC1Args); 289 } 290 291 ToolChain::CXXStdlibType 292 HIPAMDToolChain::GetCXXStdlibType(const ArgList &Args) const { 293 return HostTC.GetCXXStdlibType(Args); 294 } 295 296 void HIPAMDToolChain::AddClangSystemIncludeArgs(const ArgList &DriverArgs, 297 ArgStringList &CC1Args) const { 298 HostTC.AddClangSystemIncludeArgs(DriverArgs, CC1Args); 299 } 300 301 void HIPAMDToolChain::AddClangCXXStdlibIncludeArgs( 302 const ArgList &Args, ArgStringList &CC1Args) const { 303 HostTC.AddClangCXXStdlibIncludeArgs(Args, CC1Args); 304 } 305 306 void HIPAMDToolChain::AddIAMCUIncludeArgs(const ArgList &Args, 307 ArgStringList &CC1Args) const { 308 HostTC.AddIAMCUIncludeArgs(Args, CC1Args); 309 } 310 311 void HIPAMDToolChain::AddHIPIncludeArgs(const ArgList &DriverArgs, 312 ArgStringList &CC1Args) const { 313 RocmInstallation.AddHIPIncludeArgs(DriverArgs, CC1Args); 314 } 315 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. 322 // 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(); 327 } 328 329 VersionTuple HIPAMDToolChain::computeMSVCVersion(const Driver *D, 330 const ArgList &Args) const { 331 return HostTC.computeMSVCVersion(D, Args); 332 } 333 334 llvm::SmallVector<ToolChain::BitCodeLibraryInfo, 12> 335 HIPAMDToolChain::getHIPDeviceLibs(const llvm::opt::ArgList &DriverArgs) const { 336 llvm::SmallVector<BitCodeLibraryInfo, 12> BCLibs; 337 if (DriverArgs.hasArg(options::OPT_nogpulib)) 338 return {}; 339 ArgStringList LibraryPaths; 340 341 // Find in --hip-device-lib-path and HIP_LIBRARY_PATH. 342 for (auto Path : RocmInstallation.getRocmDeviceLibPathArg()) 343 LibraryPaths.push_back(DriverArgs.MakeArgString(Path)); 344 345 addDirectoryList(DriverArgs, LibraryPaths, "", "HIP_DEVICE_LIB_PATH"); 346 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) { 351 StringRef FullName; 352 for (std::string LibraryPath : LibraryPaths) { 353 SmallString<128> Path(LibraryPath); 354 llvm::sys::path::append(Path, BCName); 355 FullName = Path; 356 if (llvm::sys::fs::exists(FullName)) { 357 BCLibs.push_back(FullName); 358 return; 359 } 360 } 361 getDriver().Diag(diag::err_drv_no_such_file) << BCName; 362 }); 363 } else { 364 if (!RocmInstallation.hasDeviceLibrary()) { 365 getDriver().Diag(diag::err_drv_no_rocm_device_lib) << 0; 366 return {}; 367 } 368 StringRef GpuArch = getGPUArch(DriverArgs); 369 assert(!GpuArch.empty() && "Must have an explicit GPU arch."); 370 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 " 381 "sanitizer"); 382 getDriver().Diag(DiagID); 383 return {}; 384 } else 385 BCLibs.push_back({AsanRTL.str(), /*ShouldInternalize=*/false}); 386 } 387 388 // Add the HIP specific bitcode library. 389 BCLibs.push_back(RocmInstallation.getHIPPath()); 390 391 // Add common device libraries like ocml etc. 392 for (auto N : getCommonDeviceLibNames(DriverArgs, GpuArch.str())) 393 BCLibs.push_back(StringRef(N)); 394 395 // Add instrument lib. 396 auto InstLib = 397 DriverArgs.getLastArgValue(options::OPT_gpu_instrument_lib_EQ); 398 if (InstLib.empty()) 399 return BCLibs; 400 if (llvm::sys::fs::exists(InstLib)) 401 BCLibs.push_back(InstLib); 402 else 403 getDriver().Diag(diag::err_drv_no_such_file) << InstLib; 404 } 405 406 return BCLibs; 407 } 408 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; 415 } 416 } 417