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