1*fe6060f1SDimitry Andric //===- AMDGPUOpenMP.cpp - AMDGPUOpenMP ToolChain Implementation -*- C++ -*-===// 2*fe6060f1SDimitry Andric // 3*fe6060f1SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*fe6060f1SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5*fe6060f1SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*fe6060f1SDimitry Andric // 7*fe6060f1SDimitry Andric //===----------------------------------------------------------------------===// 8*fe6060f1SDimitry Andric 9*fe6060f1SDimitry Andric #include "AMDGPUOpenMP.h" 10*fe6060f1SDimitry Andric #include "AMDGPU.h" 11*fe6060f1SDimitry Andric #include "CommonArgs.h" 12*fe6060f1SDimitry Andric #include "clang/Basic/DiagnosticDriver.h" 13*fe6060f1SDimitry Andric #include "clang/Driver/Compilation.h" 14*fe6060f1SDimitry Andric #include "clang/Driver/Driver.h" 15*fe6060f1SDimitry Andric #include "clang/Driver/DriverDiagnostic.h" 16*fe6060f1SDimitry Andric #include "clang/Driver/InputInfo.h" 17*fe6060f1SDimitry Andric #include "clang/Driver/Options.h" 18*fe6060f1SDimitry Andric #include "llvm/Support/FileSystem.h" 19*fe6060f1SDimitry Andric #include "llvm/Support/FormatAdapters.h" 20*fe6060f1SDimitry Andric #include "llvm/Support/FormatVariadic.h" 21*fe6060f1SDimitry Andric #include "llvm/Support/Path.h" 22*fe6060f1SDimitry Andric 23*fe6060f1SDimitry Andric using namespace clang::driver; 24*fe6060f1SDimitry Andric using namespace clang::driver::toolchains; 25*fe6060f1SDimitry Andric using namespace clang::driver::tools; 26*fe6060f1SDimitry Andric using namespace clang; 27*fe6060f1SDimitry Andric using namespace llvm::opt; 28*fe6060f1SDimitry Andric 29*fe6060f1SDimitry Andric namespace { 30*fe6060f1SDimitry Andric 31*fe6060f1SDimitry Andric static const char *getOutputFileName(Compilation &C, StringRef Base, 32*fe6060f1SDimitry Andric const char *Postfix, 33*fe6060f1SDimitry Andric const char *Extension) { 34*fe6060f1SDimitry Andric const char *OutputFileName; 35*fe6060f1SDimitry Andric if (C.getDriver().isSaveTempsEnabled()) { 36*fe6060f1SDimitry Andric OutputFileName = 37*fe6060f1SDimitry Andric C.getArgs().MakeArgString(Base.str() + Postfix + "." + Extension); 38*fe6060f1SDimitry Andric } else { 39*fe6060f1SDimitry Andric std::string TmpName = 40*fe6060f1SDimitry Andric C.getDriver().GetTemporaryPath(Base.str() + Postfix, Extension); 41*fe6060f1SDimitry Andric OutputFileName = C.addTempFile(C.getArgs().MakeArgString(TmpName)); 42*fe6060f1SDimitry Andric } 43*fe6060f1SDimitry Andric return OutputFileName; 44*fe6060f1SDimitry Andric } 45*fe6060f1SDimitry Andric 46*fe6060f1SDimitry Andric static void addLLCOptArg(const llvm::opt::ArgList &Args, 47*fe6060f1SDimitry Andric llvm::opt::ArgStringList &CmdArgs) { 48*fe6060f1SDimitry Andric if (Arg *A = Args.getLastArg(options::OPT_O_Group)) { 49*fe6060f1SDimitry Andric StringRef OOpt = "0"; 50*fe6060f1SDimitry Andric if (A->getOption().matches(options::OPT_O4) || 51*fe6060f1SDimitry Andric A->getOption().matches(options::OPT_Ofast)) 52*fe6060f1SDimitry Andric OOpt = "3"; 53*fe6060f1SDimitry Andric else if (A->getOption().matches(options::OPT_O0)) 54*fe6060f1SDimitry Andric OOpt = "0"; 55*fe6060f1SDimitry Andric else if (A->getOption().matches(options::OPT_O)) { 56*fe6060f1SDimitry Andric // Clang and opt support -Os/-Oz; llc only supports -O0, -O1, -O2 and -O3 57*fe6060f1SDimitry Andric // so we map -Os/-Oz to -O2. 58*fe6060f1SDimitry Andric // Only clang supports -Og, and maps it to -O1. 59*fe6060f1SDimitry Andric // We map anything else to -O2. 60*fe6060f1SDimitry Andric OOpt = llvm::StringSwitch<const char *>(A->getValue()) 61*fe6060f1SDimitry Andric .Case("1", "1") 62*fe6060f1SDimitry Andric .Case("2", "2") 63*fe6060f1SDimitry Andric .Case("3", "3") 64*fe6060f1SDimitry Andric .Case("s", "2") 65*fe6060f1SDimitry Andric .Case("z", "2") 66*fe6060f1SDimitry Andric .Case("g", "1") 67*fe6060f1SDimitry Andric .Default("0"); 68*fe6060f1SDimitry Andric } 69*fe6060f1SDimitry Andric CmdArgs.push_back(Args.MakeArgString("-O" + OOpt)); 70*fe6060f1SDimitry Andric } 71*fe6060f1SDimitry Andric } 72*fe6060f1SDimitry Andric 73*fe6060f1SDimitry Andric static bool checkSystemForAMDGPU(const ArgList &Args, const AMDGPUToolChain &TC, 74*fe6060f1SDimitry Andric std::string &GPUArch) { 75*fe6060f1SDimitry Andric if (auto Err = TC.getSystemGPUArch(Args, GPUArch)) { 76*fe6060f1SDimitry Andric std::string ErrMsg = 77*fe6060f1SDimitry Andric llvm::formatv("{0}", llvm::fmt_consume(std::move(Err))); 78*fe6060f1SDimitry Andric TC.getDriver().Diag(diag::err_drv_undetermined_amdgpu_arch) << ErrMsg; 79*fe6060f1SDimitry Andric return false; 80*fe6060f1SDimitry Andric } 81*fe6060f1SDimitry Andric 82*fe6060f1SDimitry Andric return true; 83*fe6060f1SDimitry Andric } 84*fe6060f1SDimitry Andric } // namespace 85*fe6060f1SDimitry Andric 86*fe6060f1SDimitry Andric const char *AMDGCN::OpenMPLinker::constructLLVMLinkCommand( 87*fe6060f1SDimitry Andric Compilation &C, const JobAction &JA, const InputInfoList &Inputs, 88*fe6060f1SDimitry Andric const ArgList &Args, StringRef SubArchName, 89*fe6060f1SDimitry Andric StringRef OutputFilePrefix) const { 90*fe6060f1SDimitry Andric ArgStringList CmdArgs; 91*fe6060f1SDimitry Andric 92*fe6060f1SDimitry Andric for (const auto &II : Inputs) 93*fe6060f1SDimitry Andric if (II.isFilename()) 94*fe6060f1SDimitry Andric CmdArgs.push_back(II.getFilename()); 95*fe6060f1SDimitry Andric // Add an intermediate output file. 96*fe6060f1SDimitry Andric CmdArgs.push_back("-o"); 97*fe6060f1SDimitry Andric const char *OutputFileName = 98*fe6060f1SDimitry Andric getOutputFileName(C, OutputFilePrefix, "-linked", "bc"); 99*fe6060f1SDimitry Andric CmdArgs.push_back(OutputFileName); 100*fe6060f1SDimitry Andric const char *Exec = 101*fe6060f1SDimitry Andric Args.MakeArgString(getToolChain().GetProgramPath("llvm-link")); 102*fe6060f1SDimitry Andric C.addCommand(std::make_unique<Command>( 103*fe6060f1SDimitry Andric JA, *this, ResponseFileSupport::AtFileCurCP(), Exec, CmdArgs, Inputs, 104*fe6060f1SDimitry Andric InputInfo(&JA, Args.MakeArgString(OutputFileName)))); 105*fe6060f1SDimitry Andric return OutputFileName; 106*fe6060f1SDimitry Andric } 107*fe6060f1SDimitry Andric 108*fe6060f1SDimitry Andric const char *AMDGCN::OpenMPLinker::constructLlcCommand( 109*fe6060f1SDimitry Andric Compilation &C, const JobAction &JA, const InputInfoList &Inputs, 110*fe6060f1SDimitry Andric const llvm::opt::ArgList &Args, llvm::StringRef SubArchName, 111*fe6060f1SDimitry Andric llvm::StringRef OutputFilePrefix, const char *InputFileName, 112*fe6060f1SDimitry Andric bool OutputIsAsm) const { 113*fe6060f1SDimitry Andric // Construct llc command. 114*fe6060f1SDimitry Andric ArgStringList LlcArgs; 115*fe6060f1SDimitry Andric // The input to llc is the output from opt. 116*fe6060f1SDimitry Andric LlcArgs.push_back(InputFileName); 117*fe6060f1SDimitry Andric // Pass optimization arg to llc. 118*fe6060f1SDimitry Andric addLLCOptArg(Args, LlcArgs); 119*fe6060f1SDimitry Andric LlcArgs.push_back("-mtriple=amdgcn-amd-amdhsa"); 120*fe6060f1SDimitry Andric LlcArgs.push_back(Args.MakeArgString("-mcpu=" + SubArchName)); 121*fe6060f1SDimitry Andric LlcArgs.push_back( 122*fe6060f1SDimitry Andric Args.MakeArgString(Twine("-filetype=") + (OutputIsAsm ? "asm" : "obj"))); 123*fe6060f1SDimitry Andric 124*fe6060f1SDimitry Andric for (const Arg *A : Args.filtered(options::OPT_mllvm)) { 125*fe6060f1SDimitry Andric LlcArgs.push_back(A->getValue(0)); 126*fe6060f1SDimitry Andric } 127*fe6060f1SDimitry Andric 128*fe6060f1SDimitry Andric // Add output filename 129*fe6060f1SDimitry Andric LlcArgs.push_back("-o"); 130*fe6060f1SDimitry Andric const char *LlcOutputFile = 131*fe6060f1SDimitry Andric getOutputFileName(C, OutputFilePrefix, "", OutputIsAsm ? "s" : "o"); 132*fe6060f1SDimitry Andric LlcArgs.push_back(LlcOutputFile); 133*fe6060f1SDimitry Andric const char *Llc = Args.MakeArgString(getToolChain().GetProgramPath("llc")); 134*fe6060f1SDimitry Andric C.addCommand(std::make_unique<Command>( 135*fe6060f1SDimitry Andric JA, *this, ResponseFileSupport::AtFileCurCP(), Llc, LlcArgs, Inputs, 136*fe6060f1SDimitry Andric InputInfo(&JA, Args.MakeArgString(LlcOutputFile)))); 137*fe6060f1SDimitry Andric return LlcOutputFile; 138*fe6060f1SDimitry Andric } 139*fe6060f1SDimitry Andric 140*fe6060f1SDimitry Andric void AMDGCN::OpenMPLinker::constructLldCommand( 141*fe6060f1SDimitry Andric Compilation &C, const JobAction &JA, const InputInfoList &Inputs, 142*fe6060f1SDimitry Andric const InputInfo &Output, const llvm::opt::ArgList &Args, 143*fe6060f1SDimitry Andric const char *InputFileName) const { 144*fe6060f1SDimitry Andric // Construct lld command. 145*fe6060f1SDimitry Andric // The output from ld.lld is an HSA code object file. 146*fe6060f1SDimitry Andric ArgStringList LldArgs{"-flavor", "gnu", "--no-undefined", 147*fe6060f1SDimitry Andric "-shared", "-o", Output.getFilename(), 148*fe6060f1SDimitry Andric InputFileName}; 149*fe6060f1SDimitry Andric 150*fe6060f1SDimitry Andric const char *Lld = Args.MakeArgString(getToolChain().GetProgramPath("lld")); 151*fe6060f1SDimitry Andric C.addCommand(std::make_unique<Command>( 152*fe6060f1SDimitry Andric JA, *this, ResponseFileSupport::AtFileCurCP(), Lld, LldArgs, Inputs, 153*fe6060f1SDimitry Andric InputInfo(&JA, Args.MakeArgString(Output.getFilename())))); 154*fe6060f1SDimitry Andric } 155*fe6060f1SDimitry Andric 156*fe6060f1SDimitry Andric // For amdgcn the inputs of the linker job are device bitcode and output is 157*fe6060f1SDimitry Andric // object file. It calls llvm-link, opt, llc, then lld steps. 158*fe6060f1SDimitry Andric void AMDGCN::OpenMPLinker::ConstructJob(Compilation &C, const JobAction &JA, 159*fe6060f1SDimitry Andric const InputInfo &Output, 160*fe6060f1SDimitry Andric const InputInfoList &Inputs, 161*fe6060f1SDimitry Andric const ArgList &Args, 162*fe6060f1SDimitry Andric const char *LinkingOutput) const { 163*fe6060f1SDimitry Andric const ToolChain &TC = getToolChain(); 164*fe6060f1SDimitry Andric assert(getToolChain().getTriple().isAMDGCN() && "Unsupported target"); 165*fe6060f1SDimitry Andric 166*fe6060f1SDimitry Andric const toolchains::AMDGPUOpenMPToolChain &AMDGPUOpenMPTC = 167*fe6060f1SDimitry Andric static_cast<const toolchains::AMDGPUOpenMPToolChain &>(TC); 168*fe6060f1SDimitry Andric 169*fe6060f1SDimitry Andric std::string GPUArch = Args.getLastArgValue(options::OPT_march_EQ).str(); 170*fe6060f1SDimitry Andric if (GPUArch.empty()) { 171*fe6060f1SDimitry Andric if (!checkSystemForAMDGPU(Args, AMDGPUOpenMPTC, GPUArch)) 172*fe6060f1SDimitry Andric return; 173*fe6060f1SDimitry Andric } 174*fe6060f1SDimitry Andric 175*fe6060f1SDimitry Andric // Prefix for temporary file name. 176*fe6060f1SDimitry Andric std::string Prefix; 177*fe6060f1SDimitry Andric for (const auto &II : Inputs) 178*fe6060f1SDimitry Andric if (II.isFilename()) 179*fe6060f1SDimitry Andric Prefix = llvm::sys::path::stem(II.getFilename()).str() + "-" + GPUArch; 180*fe6060f1SDimitry Andric assert(Prefix.length() && "no linker inputs are files "); 181*fe6060f1SDimitry Andric 182*fe6060f1SDimitry Andric // Each command outputs different files. 183*fe6060f1SDimitry Andric const char *LLVMLinkCommand = 184*fe6060f1SDimitry Andric constructLLVMLinkCommand(C, JA, Inputs, Args, GPUArch, Prefix); 185*fe6060f1SDimitry Andric 186*fe6060f1SDimitry Andric // Produce readable assembly if save-temps is enabled. 187*fe6060f1SDimitry Andric if (C.getDriver().isSaveTempsEnabled()) 188*fe6060f1SDimitry Andric constructLlcCommand(C, JA, Inputs, Args, GPUArch, Prefix, LLVMLinkCommand, 189*fe6060f1SDimitry Andric /*OutputIsAsm=*/true); 190*fe6060f1SDimitry Andric const char *LlcCommand = constructLlcCommand(C, JA, Inputs, Args, GPUArch, 191*fe6060f1SDimitry Andric Prefix, LLVMLinkCommand); 192*fe6060f1SDimitry Andric constructLldCommand(C, JA, Inputs, Output, Args, LlcCommand); 193*fe6060f1SDimitry Andric } 194*fe6060f1SDimitry Andric 195*fe6060f1SDimitry Andric AMDGPUOpenMPToolChain::AMDGPUOpenMPToolChain(const Driver &D, 196*fe6060f1SDimitry Andric const llvm::Triple &Triple, 197*fe6060f1SDimitry Andric const ToolChain &HostTC, 198*fe6060f1SDimitry Andric const ArgList &Args) 199*fe6060f1SDimitry Andric : ROCMToolChain(D, Triple, Args), HostTC(HostTC) { 200*fe6060f1SDimitry Andric // Lookup binaries into the driver directory, this is used to 201*fe6060f1SDimitry Andric // discover the clang-offload-bundler executable. 202*fe6060f1SDimitry Andric getProgramPaths().push_back(getDriver().Dir); 203*fe6060f1SDimitry Andric } 204*fe6060f1SDimitry Andric 205*fe6060f1SDimitry Andric void AMDGPUOpenMPToolChain::addClangTargetOptions( 206*fe6060f1SDimitry Andric const llvm::opt::ArgList &DriverArgs, llvm::opt::ArgStringList &CC1Args, 207*fe6060f1SDimitry Andric Action::OffloadKind DeviceOffloadingKind) const { 208*fe6060f1SDimitry Andric HostTC.addClangTargetOptions(DriverArgs, CC1Args, DeviceOffloadingKind); 209*fe6060f1SDimitry Andric 210*fe6060f1SDimitry Andric std::string GPUArch = DriverArgs.getLastArgValue(options::OPT_march_EQ).str(); 211*fe6060f1SDimitry Andric if (GPUArch.empty()) { 212*fe6060f1SDimitry Andric if (!checkSystemForAMDGPU(DriverArgs, *this, GPUArch)) 213*fe6060f1SDimitry Andric return; 214*fe6060f1SDimitry Andric } 215*fe6060f1SDimitry Andric 216*fe6060f1SDimitry Andric assert(DeviceOffloadingKind == Action::OFK_OpenMP && 217*fe6060f1SDimitry Andric "Only OpenMP offloading kinds are supported."); 218*fe6060f1SDimitry Andric 219*fe6060f1SDimitry Andric CC1Args.push_back("-target-cpu"); 220*fe6060f1SDimitry Andric CC1Args.push_back(DriverArgs.MakeArgStringRef(GPUArch)); 221*fe6060f1SDimitry Andric CC1Args.push_back("-fcuda-is-device"); 222*fe6060f1SDimitry Andric 223*fe6060f1SDimitry Andric if (DriverArgs.hasArg(options::OPT_nogpulib)) 224*fe6060f1SDimitry Andric return; 225*fe6060f1SDimitry Andric 226*fe6060f1SDimitry Andric std::string BitcodeSuffix; 227*fe6060f1SDimitry Andric if (DriverArgs.hasFlag(options::OPT_fopenmp_target_new_runtime, 228*fe6060f1SDimitry Andric options::OPT_fno_openmp_target_new_runtime, false)) 229*fe6060f1SDimitry Andric BitcodeSuffix = "new-amdgcn-" + GPUArch; 230*fe6060f1SDimitry Andric else 231*fe6060f1SDimitry Andric BitcodeSuffix = "amdgcn-" + GPUArch; 232*fe6060f1SDimitry Andric 233*fe6060f1SDimitry Andric addOpenMPDeviceRTL(getDriver(), DriverArgs, CC1Args, BitcodeSuffix, 234*fe6060f1SDimitry Andric getTriple()); 235*fe6060f1SDimitry Andric } 236*fe6060f1SDimitry Andric 237*fe6060f1SDimitry Andric llvm::opt::DerivedArgList *AMDGPUOpenMPToolChain::TranslateArgs( 238*fe6060f1SDimitry Andric const llvm::opt::DerivedArgList &Args, StringRef BoundArch, 239*fe6060f1SDimitry Andric Action::OffloadKind DeviceOffloadKind) const { 240*fe6060f1SDimitry Andric DerivedArgList *DAL = 241*fe6060f1SDimitry Andric HostTC.TranslateArgs(Args, BoundArch, DeviceOffloadKind); 242*fe6060f1SDimitry Andric if (!DAL) 243*fe6060f1SDimitry Andric DAL = new DerivedArgList(Args.getBaseArgs()); 244*fe6060f1SDimitry Andric 245*fe6060f1SDimitry Andric const OptTable &Opts = getDriver().getOpts(); 246*fe6060f1SDimitry Andric 247*fe6060f1SDimitry Andric if (DeviceOffloadKind != Action::OFK_OpenMP) { 248*fe6060f1SDimitry Andric for (Arg *A : Args) { 249*fe6060f1SDimitry Andric DAL->append(A); 250*fe6060f1SDimitry Andric } 251*fe6060f1SDimitry Andric } 252*fe6060f1SDimitry Andric 253*fe6060f1SDimitry Andric if (!BoundArch.empty()) { 254*fe6060f1SDimitry Andric DAL->eraseArg(options::OPT_march_EQ); 255*fe6060f1SDimitry Andric DAL->AddJoinedArg(nullptr, Opts.getOption(options::OPT_march_EQ), 256*fe6060f1SDimitry Andric BoundArch); 257*fe6060f1SDimitry Andric } 258*fe6060f1SDimitry Andric 259*fe6060f1SDimitry Andric return DAL; 260*fe6060f1SDimitry Andric } 261*fe6060f1SDimitry Andric 262*fe6060f1SDimitry Andric Tool *AMDGPUOpenMPToolChain::buildLinker() const { 263*fe6060f1SDimitry Andric assert(getTriple().isAMDGCN()); 264*fe6060f1SDimitry Andric return new tools::AMDGCN::OpenMPLinker(*this); 265*fe6060f1SDimitry Andric } 266*fe6060f1SDimitry Andric 267*fe6060f1SDimitry Andric void AMDGPUOpenMPToolChain::addClangWarningOptions( 268*fe6060f1SDimitry Andric ArgStringList &CC1Args) const { 269*fe6060f1SDimitry Andric HostTC.addClangWarningOptions(CC1Args); 270*fe6060f1SDimitry Andric } 271*fe6060f1SDimitry Andric 272*fe6060f1SDimitry Andric ToolChain::CXXStdlibType 273*fe6060f1SDimitry Andric AMDGPUOpenMPToolChain::GetCXXStdlibType(const ArgList &Args) const { 274*fe6060f1SDimitry Andric return HostTC.GetCXXStdlibType(Args); 275*fe6060f1SDimitry Andric } 276*fe6060f1SDimitry Andric 277*fe6060f1SDimitry Andric void AMDGPUOpenMPToolChain::AddClangSystemIncludeArgs( 278*fe6060f1SDimitry Andric const ArgList &DriverArgs, ArgStringList &CC1Args) const { 279*fe6060f1SDimitry Andric HostTC.AddClangSystemIncludeArgs(DriverArgs, CC1Args); 280*fe6060f1SDimitry Andric } 281*fe6060f1SDimitry Andric 282*fe6060f1SDimitry Andric void AMDGPUOpenMPToolChain::AddIAMCUIncludeArgs(const ArgList &Args, 283*fe6060f1SDimitry Andric ArgStringList &CC1Args) const { 284*fe6060f1SDimitry Andric HostTC.AddIAMCUIncludeArgs(Args, CC1Args); 285*fe6060f1SDimitry Andric } 286*fe6060f1SDimitry Andric 287*fe6060f1SDimitry Andric SanitizerMask AMDGPUOpenMPToolChain::getSupportedSanitizers() const { 288*fe6060f1SDimitry Andric // The AMDGPUOpenMPToolChain only supports sanitizers in the sense that it 289*fe6060f1SDimitry Andric // allows sanitizer arguments on the command line if they are supported by the 290*fe6060f1SDimitry Andric // host toolchain. The AMDGPUOpenMPToolChain will actually ignore any command 291*fe6060f1SDimitry Andric // line arguments for any of these "supported" sanitizers. That means that no 292*fe6060f1SDimitry Andric // sanitization of device code is actually supported at this time. 293*fe6060f1SDimitry Andric // 294*fe6060f1SDimitry Andric // This behavior is necessary because the host and device toolchains 295*fe6060f1SDimitry Andric // invocations often share the command line, so the device toolchain must 296*fe6060f1SDimitry Andric // tolerate flags meant only for the host toolchain. 297*fe6060f1SDimitry Andric return HostTC.getSupportedSanitizers(); 298*fe6060f1SDimitry Andric } 299*fe6060f1SDimitry Andric 300*fe6060f1SDimitry Andric VersionTuple 301*fe6060f1SDimitry Andric AMDGPUOpenMPToolChain::computeMSVCVersion(const Driver *D, 302*fe6060f1SDimitry Andric const ArgList &Args) const { 303*fe6060f1SDimitry Andric return HostTC.computeMSVCVersion(D, Args); 304*fe6060f1SDimitry Andric } 305