//===--- HIP.cpp - HIP Tool and ToolChain Implementations -------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "HIP.h" #include "CommonArgs.h" #include "InputInfo.h" #include "clang/Basic/Cuda.h" #include "clang/Driver/Compilation.h" #include "clang/Driver/Driver.h" #include "clang/Driver/DriverDiagnostic.h" #include "clang/Driver/Options.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" using namespace clang::driver; using namespace clang::driver::toolchains; using namespace clang::driver::tools; using namespace clang; using namespace llvm::opt; namespace { static void addBCLib(Compilation &C, const ArgList &Args, ArgStringList &CmdArgs, ArgStringList LibraryPaths, StringRef BCName) { StringRef FullName; for (std::string LibraryPath : LibraryPaths) { SmallString<128> Path(LibraryPath); llvm::sys::path::append(Path, BCName); FullName = Path; if (llvm::sys::fs::exists(FullName)) { CmdArgs.push_back(Args.MakeArgString(FullName)); return; } } C.getDriver().Diag(diag::err_drv_no_such_file) << BCName; } } // namespace const char *AMDGCN::Linker::constructLLVMLinkCommand( Compilation &C, const JobAction &JA, const InputInfoList &Inputs, const ArgList &Args, StringRef SubArchName, StringRef OutputFilePrefix) const { ArgStringList CmdArgs; // Add the input bc's created by compile step. for (const auto &II : Inputs) CmdArgs.push_back(II.getFilename()); ArgStringList LibraryPaths; // Find in --hip-device-lib-path and HIP_LIBRARY_PATH. for (auto Path : Args.getAllArgValues(options::OPT_hip_device_lib_path_EQ)) LibraryPaths.push_back(Args.MakeArgString(Path)); addDirectoryList(Args, LibraryPaths, "-L", "HIP_DEVICE_LIB_PATH"); llvm::SmallVector BCLibs; // Add bitcode library in --hip-device-lib. for (auto Lib : Args.getAllArgValues(options::OPT_hip_device_lib_EQ)) { BCLibs.push_back(Args.MakeArgString(Lib)); } // If --hip-device-lib is not set, add the default bitcode libraries. if (BCLibs.empty()) { // Get the bc lib file name for ISA version. For example, // gfx803 => oclc_isa_version_803.amdgcn.bc. std::string ISAVerBC = "oclc_isa_version_" + SubArchName.drop_front(3).str() + ".amdgcn.bc"; llvm::StringRef FlushDenormalControlBC; if (Args.hasArg(options::OPT_fcuda_flush_denormals_to_zero)) FlushDenormalControlBC = "oclc_daz_opt_on.amdgcn.bc"; else FlushDenormalControlBC = "oclc_daz_opt_off.amdgcn.bc"; BCLibs.append({"opencl.amdgcn.bc", "ocml.amdgcn.bc", "ockl.amdgcn.bc", "irif.amdgcn.bc", "oclc_finite_only_off.amdgcn.bc", FlushDenormalControlBC, "oclc_correctly_rounded_sqrt_on.amdgcn.bc", "oclc_unsafe_math_off.amdgcn.bc", ISAVerBC}); } for (auto Lib : BCLibs) addBCLib(C, Args, CmdArgs, LibraryPaths, Lib); // Add an intermediate output file. CmdArgs.push_back("-o"); std::string TmpName = C.getDriver().GetTemporaryPath(OutputFilePrefix.str() + "-linked", "bc"); const char *OutputFileName = C.addTempFile(C.getArgs().MakeArgString(TmpName)); CmdArgs.push_back(OutputFileName); SmallString<128> ExecPath(C.getDriver().Dir); llvm::sys::path::append(ExecPath, "llvm-link"); const char *Exec = Args.MakeArgString(ExecPath); C.addCommand(llvm::make_unique(JA, *this, Exec, CmdArgs, Inputs)); return OutputFileName; } const char *AMDGCN::Linker::constructOptCommand( Compilation &C, const JobAction &JA, const InputInfoList &Inputs, const llvm::opt::ArgList &Args, llvm::StringRef SubArchName, llvm::StringRef OutputFilePrefix, const char *InputFileName) const { // Construct opt command. ArgStringList OptArgs; // The input to opt is the output from llvm-link. OptArgs.push_back(InputFileName); // Pass optimization arg to opt. if (Arg *A = Args.getLastArg(options::OPT_O_Group)) { StringRef OOpt = "3"; if (A->getOption().matches(options::OPT_O4) || A->getOption().matches(options::OPT_Ofast)) OOpt = "3"; else if (A->getOption().matches(options::OPT_O0)) OOpt = "0"; else if (A->getOption().matches(options::OPT_O)) { // -Os, -Oz, and -O(anything else) map to -O2 OOpt = llvm::StringSwitch(A->getValue()) .Case("1", "1") .Case("2", "2") .Case("3", "3") .Case("s", "2") .Case("z", "2") .Default("2"); } OptArgs.push_back(Args.MakeArgString("-O" + OOpt)); } OptArgs.push_back("-mtriple=amdgcn-amd-amdhsa"); OptArgs.push_back(Args.MakeArgString("-mcpu=" + SubArchName)); OptArgs.push_back("-o"); std::string TmpFileName = C.getDriver().GetTemporaryPath( OutputFilePrefix.str() + "-optimized", "bc"); const char *OutputFileName = C.addTempFile(C.getArgs().MakeArgString(TmpFileName)); OptArgs.push_back(OutputFileName); SmallString<128> OptPath(C.getDriver().Dir); llvm::sys::path::append(OptPath, "opt"); const char *OptExec = Args.MakeArgString(OptPath); C.addCommand(llvm::make_unique(JA, *this, OptExec, OptArgs, Inputs)); return OutputFileName; } const char *AMDGCN::Linker::constructLlcCommand( Compilation &C, const JobAction &JA, const InputInfoList &Inputs, const llvm::opt::ArgList &Args, llvm::StringRef SubArchName, llvm::StringRef OutputFilePrefix, const char *InputFileName) const { // Construct llc command. ArgStringList LlcArgs{InputFileName, "-mtriple=amdgcn-amd-amdhsa", "-filetype=obj", Args.MakeArgString("-mcpu=" + SubArchName), "-o"}; std::string LlcOutputFileName = C.getDriver().GetTemporaryPath(OutputFilePrefix, "o"); const char *LlcOutputFile = C.addTempFile(C.getArgs().MakeArgString(LlcOutputFileName)); LlcArgs.push_back(LlcOutputFile); SmallString<128> LlcPath(C.getDriver().Dir); llvm::sys::path::append(LlcPath, "llc"); const char *Llc = Args.MakeArgString(LlcPath); C.addCommand(llvm::make_unique(JA, *this, Llc, LlcArgs, Inputs)); return LlcOutputFile; } void AMDGCN::Linker::constructLldCommand(Compilation &C, const JobAction &JA, const InputInfoList &Inputs, const InputInfo &Output, const llvm::opt::ArgList &Args, const char *InputFileName) const { // Construct lld command. // The output from ld.lld is an HSA code object file. ArgStringList LldArgs{"-flavor", "gnu", "--no-undefined", "-shared", "-o", Output.getFilename(), InputFileName}; SmallString<128> LldPath(C.getDriver().Dir); llvm::sys::path::append(LldPath, "lld"); const char *Lld = Args.MakeArgString(LldPath); C.addCommand(llvm::make_unique(JA, *this, Lld, LldArgs, Inputs)); } // For amdgcn the inputs of the linker job are device bitcode and output is // object file. It calls llvm-link, opt, llc, then lld steps. void AMDGCN::Linker::ConstructJob(Compilation &C, const JobAction &JA, const InputInfo &Output, const InputInfoList &Inputs, const ArgList &Args, const char *LinkingOutput) const { assert(getToolChain().getTriple().getArch() == llvm::Triple::amdgcn && "Unsupported target"); std::string SubArchName = JA.getOffloadingArch(); assert(StringRef(SubArchName).startswith("gfx") && "Unsupported sub arch"); // Prefix for temporary file name. std::string Prefix = llvm::sys::path::stem(Inputs[0].getFilename()).str() + "-" + SubArchName; // Each command outputs different files. const char *LLVMLinkCommand = constructLLVMLinkCommand(C, JA, Inputs, Args, SubArchName, Prefix); const char *OptCommand = constructOptCommand(C, JA, Inputs, Args, SubArchName, Prefix, LLVMLinkCommand); const char *LlcCommand = constructLlcCommand(C, JA, Inputs, Args, SubArchName, Prefix, OptCommand); constructLldCommand(C, JA, Inputs, Output, Args, LlcCommand); } HIPToolChain::HIPToolChain(const Driver &D, const llvm::Triple &Triple, const ToolChain &HostTC, const ArgList &Args) : ToolChain(D, Triple, Args), HostTC(HostTC) { // Lookup binaries into the driver directory, this is used to // discover the clang-offload-bundler executable. getProgramPaths().push_back(getDriver().Dir); } void HIPToolChain::addClangTargetOptions( const llvm::opt::ArgList &DriverArgs, llvm::opt::ArgStringList &CC1Args, Action::OffloadKind DeviceOffloadingKind) const { HostTC.addClangTargetOptions(DriverArgs, CC1Args, DeviceOffloadingKind); StringRef GpuArch = DriverArgs.getLastArgValue(options::OPT_march_EQ); assert(!GpuArch.empty() && "Must have an explicit GPU arch."); (void) GpuArch; assert(DeviceOffloadingKind == Action::OFK_HIP && "Only HIP offloading kinds are supported for GPUs."); CC1Args.push_back("-target-cpu"); CC1Args.push_back(DriverArgs.MakeArgStringRef(GpuArch)); CC1Args.push_back("-fcuda-is-device"); if (DriverArgs.hasFlag(options::OPT_fcuda_flush_denormals_to_zero, options::OPT_fno_cuda_flush_denormals_to_zero, false)) CC1Args.push_back("-fcuda-flush-denormals-to-zero"); if (DriverArgs.hasFlag(options::OPT_fcuda_approx_transcendentals, options::OPT_fno_cuda_approx_transcendentals, false)) CC1Args.push_back("-fcuda-approx-transcendentals"); if (DriverArgs.hasFlag(options::OPT_fcuda_rdc, options::OPT_fno_cuda_rdc, false)) CC1Args.push_back("-fcuda-rdc"); } llvm::opt::DerivedArgList * HIPToolChain::TranslateArgs(const llvm::opt::DerivedArgList &Args, StringRef BoundArch, Action::OffloadKind DeviceOffloadKind) const { DerivedArgList *DAL = HostTC.TranslateArgs(Args, BoundArch, DeviceOffloadKind); if (!DAL) DAL = new DerivedArgList(Args.getBaseArgs()); const OptTable &Opts = getDriver().getOpts(); for (Arg *A : Args) { if (A->getOption().matches(options::OPT_Xarch__)) { // Skip this argument unless the architecture matches BoundArch. if (BoundArch.empty() || A->getValue(0) != BoundArch) continue; unsigned Index = Args.getBaseArgs().MakeIndex(A->getValue(1)); unsigned Prev = Index; std::unique_ptr XarchArg(Opts.ParseOneArg(Args, Index)); // If the argument parsing failed or more than one argument was // consumed, the -Xarch_ argument's parameter tried to consume // extra arguments. Emit an error and ignore. // // We also want to disallow any options which would alter the // driver behavior; that isn't going to work in our model. We // use isDriverOption() as an approximation, although things // like -O4 are going to slip through. if (!XarchArg || Index > Prev + 1) { getDriver().Diag(diag::err_drv_invalid_Xarch_argument_with_args) << A->getAsString(Args); continue; } else if (XarchArg->getOption().hasFlag(options::DriverOption)) { getDriver().Diag(diag::err_drv_invalid_Xarch_argument_isdriver) << A->getAsString(Args); continue; } XarchArg->setBaseArg(A); A = XarchArg.release(); DAL->AddSynthesizedArg(A); } DAL->append(A); } if (!BoundArch.empty()) { DAL->eraseArg(options::OPT_march_EQ); DAL->AddJoinedArg(nullptr, Opts.getOption(options::OPT_march_EQ), BoundArch); } return DAL; } Tool *HIPToolChain::buildLinker() const { assert(getTriple().getArch() == llvm::Triple::amdgcn); return new tools::AMDGCN::Linker(*this); } void HIPToolChain::addClangWarningOptions(ArgStringList &CC1Args) const { HostTC.addClangWarningOptions(CC1Args); } ToolChain::CXXStdlibType HIPToolChain::GetCXXStdlibType(const ArgList &Args) const { return HostTC.GetCXXStdlibType(Args); } void HIPToolChain::AddClangSystemIncludeArgs(const ArgList &DriverArgs, ArgStringList &CC1Args) const { HostTC.AddClangSystemIncludeArgs(DriverArgs, CC1Args); } void HIPToolChain::AddClangCXXStdlibIncludeArgs(const ArgList &Args, ArgStringList &CC1Args) const { HostTC.AddClangCXXStdlibIncludeArgs(Args, CC1Args); } void HIPToolChain::AddIAMCUIncludeArgs(const ArgList &Args, ArgStringList &CC1Args) const { HostTC.AddIAMCUIncludeArgs(Args, CC1Args); } SanitizerMask HIPToolChain::getSupportedSanitizers() const { // The HIPToolChain only supports sanitizers in the sense that it allows // sanitizer arguments on the command line if they are supported by the host // toolchain. The HIPToolChain will actually ignore any command line // arguments for any of these "supported" sanitizers. That means that no // sanitization of device code is actually supported at this time. // // This behavior is necessary because the host and device toolchains // invocations often share the command line, so the device toolchain must // tolerate flags meant only for the host toolchain. return HostTC.getSupportedSanitizers(); } VersionTuple HIPToolChain::computeMSVCVersion(const Driver *D, const ArgList &Args) const { return HostTC.computeMSVCVersion(D, Args); }