1*0eae32dcSDimitry Andric //===--- HIPSPV.cpp - HIPSPV ToolChain Implementation -----------*- C++ -*-===// 2*0eae32dcSDimitry Andric // 3*0eae32dcSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*0eae32dcSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5*0eae32dcSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*0eae32dcSDimitry Andric // 7*0eae32dcSDimitry Andric //===----------------------------------------------------------------------===// 8*0eae32dcSDimitry Andric 9*0eae32dcSDimitry Andric #include "HIPSPV.h" 10*0eae32dcSDimitry Andric #include "CommonArgs.h" 11*0eae32dcSDimitry Andric #include "HIPUtility.h" 12*0eae32dcSDimitry Andric #include "clang/Driver/Compilation.h" 13*0eae32dcSDimitry Andric #include "clang/Driver/Driver.h" 14*0eae32dcSDimitry Andric #include "clang/Driver/DriverDiagnostic.h" 15*0eae32dcSDimitry Andric #include "clang/Driver/InputInfo.h" 16*0eae32dcSDimitry Andric #include "clang/Driver/Options.h" 17*0eae32dcSDimitry Andric #include "llvm/Support/FileSystem.h" 18*0eae32dcSDimitry Andric #include "llvm/Support/Path.h" 19*0eae32dcSDimitry Andric 20*0eae32dcSDimitry Andric using namespace clang::driver; 21*0eae32dcSDimitry Andric using namespace clang::driver::toolchains; 22*0eae32dcSDimitry Andric using namespace clang::driver::tools; 23*0eae32dcSDimitry Andric using namespace clang; 24*0eae32dcSDimitry Andric using namespace llvm::opt; 25*0eae32dcSDimitry Andric 26*0eae32dcSDimitry Andric // Convenience function for creating temporary file for both modes of 27*0eae32dcSDimitry Andric // isSaveTempsEnabled(). 28*0eae32dcSDimitry Andric static const char *getTempFile(Compilation &C, StringRef Prefix, 29*0eae32dcSDimitry Andric StringRef Extension) { 30*0eae32dcSDimitry Andric if (C.getDriver().isSaveTempsEnabled()) { 31*0eae32dcSDimitry Andric return C.getArgs().MakeArgString(Prefix + "." + Extension); 32*0eae32dcSDimitry Andric } 33*0eae32dcSDimitry Andric auto TmpFile = C.getDriver().GetTemporaryPath(Prefix, Extension); 34*0eae32dcSDimitry Andric return C.addTempFile(C.getArgs().MakeArgString(TmpFile)); 35*0eae32dcSDimitry Andric } 36*0eae32dcSDimitry Andric 37*0eae32dcSDimitry Andric // Locates HIP pass plugin. 38*0eae32dcSDimitry Andric static std::string findPassPlugin(const Driver &D, 39*0eae32dcSDimitry Andric const llvm::opt::ArgList &Args) { 40*0eae32dcSDimitry Andric StringRef Path = Args.getLastArgValue(options::OPT_hipspv_pass_plugin_EQ); 41*0eae32dcSDimitry Andric if (!Path.empty()) { 42*0eae32dcSDimitry Andric if (llvm::sys::fs::exists(Path)) 43*0eae32dcSDimitry Andric return Path.str(); 44*0eae32dcSDimitry Andric D.Diag(diag::err_drv_no_such_file) << Path; 45*0eae32dcSDimitry Andric } 46*0eae32dcSDimitry Andric 47*0eae32dcSDimitry Andric StringRef hipPath = Args.getLastArgValue(options::OPT_hip_path_EQ); 48*0eae32dcSDimitry Andric if (!hipPath.empty()) { 49*0eae32dcSDimitry Andric SmallString<128> PluginPath(hipPath); 50*0eae32dcSDimitry Andric llvm::sys::path::append(PluginPath, "lib", "libLLVMHipSpvPasses.so"); 51*0eae32dcSDimitry Andric if (llvm::sys::fs::exists(PluginPath)) 52*0eae32dcSDimitry Andric return PluginPath.str().str(); 53*0eae32dcSDimitry Andric PluginPath.assign(hipPath); 54*0eae32dcSDimitry Andric llvm::sys::path::append(PluginPath, "lib", "llvm", 55*0eae32dcSDimitry Andric "libLLVMHipSpvPasses.so"); 56*0eae32dcSDimitry Andric if (llvm::sys::fs::exists(PluginPath)) 57*0eae32dcSDimitry Andric return PluginPath.str().str(); 58*0eae32dcSDimitry Andric } 59*0eae32dcSDimitry Andric 60*0eae32dcSDimitry Andric return std::string(); 61*0eae32dcSDimitry Andric } 62*0eae32dcSDimitry Andric 63*0eae32dcSDimitry Andric void HIPSPV::Linker::constructLinkAndEmitSpirvCommand( 64*0eae32dcSDimitry Andric Compilation &C, const JobAction &JA, const InputInfoList &Inputs, 65*0eae32dcSDimitry Andric const InputInfo &Output, const llvm::opt::ArgList &Args) const { 66*0eae32dcSDimitry Andric 67*0eae32dcSDimitry Andric assert(!Inputs.empty() && "Must have at least one input."); 68*0eae32dcSDimitry Andric std::string Name = std::string(llvm::sys::path::stem(Output.getFilename())); 69*0eae32dcSDimitry Andric const char *TempFile = getTempFile(C, Name + "-link", "bc"); 70*0eae32dcSDimitry Andric 71*0eae32dcSDimitry Andric // Link LLVM bitcode. 72*0eae32dcSDimitry Andric ArgStringList LinkArgs{}; 73*0eae32dcSDimitry Andric for (auto Input : Inputs) 74*0eae32dcSDimitry Andric LinkArgs.push_back(Input.getFilename()); 75*0eae32dcSDimitry Andric LinkArgs.append({"-o", TempFile}); 76*0eae32dcSDimitry Andric const char *LlvmLink = 77*0eae32dcSDimitry Andric Args.MakeArgString(getToolChain().GetProgramPath("llvm-link")); 78*0eae32dcSDimitry Andric C.addCommand(std::make_unique<Command>(JA, *this, ResponseFileSupport::None(), 79*0eae32dcSDimitry Andric LlvmLink, LinkArgs, Inputs, Output)); 80*0eae32dcSDimitry Andric 81*0eae32dcSDimitry Andric // Post-link HIP lowering. 82*0eae32dcSDimitry Andric 83*0eae32dcSDimitry Andric // Run LLVM IR passes to lower/expand/emulate HIP code that does not translate 84*0eae32dcSDimitry Andric // to SPIR-V (E.g. dynamic shared memory). 85*0eae32dcSDimitry Andric auto PassPluginPath = findPassPlugin(C.getDriver(), Args); 86*0eae32dcSDimitry Andric if (!PassPluginPath.empty()) { 87*0eae32dcSDimitry Andric const char *PassPathCStr = C.getArgs().MakeArgString(PassPluginPath); 88*0eae32dcSDimitry Andric const char *OptOutput = getTempFile(C, Name + "-lower", "bc"); 89*0eae32dcSDimitry Andric ArgStringList OptArgs{TempFile, "-load-pass-plugin", 90*0eae32dcSDimitry Andric PassPathCStr, "-passes=hip-post-link-passes", 91*0eae32dcSDimitry Andric "-o", OptOutput}; 92*0eae32dcSDimitry Andric const char *Opt = Args.MakeArgString(getToolChain().GetProgramPath("opt")); 93*0eae32dcSDimitry Andric C.addCommand(std::make_unique<Command>( 94*0eae32dcSDimitry Andric JA, *this, ResponseFileSupport::None(), Opt, OptArgs, Inputs, Output)); 95*0eae32dcSDimitry Andric TempFile = OptOutput; 96*0eae32dcSDimitry Andric } 97*0eae32dcSDimitry Andric 98*0eae32dcSDimitry Andric // Emit SPIR-V binary. 99*0eae32dcSDimitry Andric 100*0eae32dcSDimitry Andric llvm::opt::ArgStringList TrArgs{"--spirv-max-version=1.1", 101*0eae32dcSDimitry Andric "--spirv-ext=+all"}; 102*0eae32dcSDimitry Andric InputInfo TrInput = InputInfo(types::TY_LLVM_BC, TempFile, ""); 103*0eae32dcSDimitry Andric SPIRV::constructTranslateCommand(C, *this, JA, Output, TrInput, TrArgs); 104*0eae32dcSDimitry Andric } 105*0eae32dcSDimitry Andric 106*0eae32dcSDimitry Andric void HIPSPV::Linker::ConstructJob(Compilation &C, const JobAction &JA, 107*0eae32dcSDimitry Andric const InputInfo &Output, 108*0eae32dcSDimitry Andric const InputInfoList &Inputs, 109*0eae32dcSDimitry Andric const ArgList &Args, 110*0eae32dcSDimitry Andric const char *LinkingOutput) const { 111*0eae32dcSDimitry Andric if (Inputs.size() > 0 && Inputs[0].getType() == types::TY_Image && 112*0eae32dcSDimitry Andric JA.getType() == types::TY_Object) 113*0eae32dcSDimitry Andric return HIP::constructGenerateObjFileFromHIPFatBinary(C, Output, Inputs, 114*0eae32dcSDimitry Andric Args, JA, *this); 115*0eae32dcSDimitry Andric 116*0eae32dcSDimitry Andric if (JA.getType() == types::TY_HIP_FATBIN) 117*0eae32dcSDimitry Andric return HIP::constructHIPFatbinCommand(C, JA, Output.getFilename(), Inputs, 118*0eae32dcSDimitry Andric Args, *this); 119*0eae32dcSDimitry Andric 120*0eae32dcSDimitry Andric constructLinkAndEmitSpirvCommand(C, JA, Inputs, Output, Args); 121*0eae32dcSDimitry Andric } 122*0eae32dcSDimitry Andric 123*0eae32dcSDimitry Andric HIPSPVToolChain::HIPSPVToolChain(const Driver &D, const llvm::Triple &Triple, 124*0eae32dcSDimitry Andric const ToolChain &HostTC, const ArgList &Args) 125*0eae32dcSDimitry Andric : ToolChain(D, Triple, Args), HostTC(HostTC) { 126*0eae32dcSDimitry Andric // Lookup binaries into the driver directory, this is used to 127*0eae32dcSDimitry Andric // discover the clang-offload-bundler executable. 128*0eae32dcSDimitry Andric getProgramPaths().push_back(getDriver().Dir); 129*0eae32dcSDimitry Andric } 130*0eae32dcSDimitry Andric 131*0eae32dcSDimitry Andric void HIPSPVToolChain::addClangTargetOptions( 132*0eae32dcSDimitry Andric const llvm::opt::ArgList &DriverArgs, llvm::opt::ArgStringList &CC1Args, 133*0eae32dcSDimitry Andric Action::OffloadKind DeviceOffloadingKind) const { 134*0eae32dcSDimitry Andric HostTC.addClangTargetOptions(DriverArgs, CC1Args, DeviceOffloadingKind); 135*0eae32dcSDimitry Andric 136*0eae32dcSDimitry Andric assert(DeviceOffloadingKind == Action::OFK_HIP && 137*0eae32dcSDimitry Andric "Only HIP offloading kinds are supported for GPUs."); 138*0eae32dcSDimitry Andric 139*0eae32dcSDimitry Andric CC1Args.append( 140*0eae32dcSDimitry Andric {"-fcuda-is-device", "-fcuda-allow-variadic-functions", 141*0eae32dcSDimitry Andric // A crude workaround for llvm-spirv which does not handle the 142*0eae32dcSDimitry Andric // autovectorized code well (vector reductions, non-i{8,16,32,64} types). 143*0eae32dcSDimitry Andric // TODO: Allow autovectorization when SPIR-V backend arrives. 144*0eae32dcSDimitry Andric "-mllvm", "-vectorize-loops=false", "-mllvm", "-vectorize-slp=false"}); 145*0eae32dcSDimitry Andric 146*0eae32dcSDimitry Andric if (DriverArgs.hasFlag(options::OPT_fcuda_approx_transcendentals, 147*0eae32dcSDimitry Andric options::OPT_fno_cuda_approx_transcendentals, false)) 148*0eae32dcSDimitry Andric CC1Args.push_back("-fcuda-approx-transcendentals"); 149*0eae32dcSDimitry Andric 150*0eae32dcSDimitry Andric // Default to "hidden" visibility, as object level linking will not be 151*0eae32dcSDimitry Andric // supported for the foreseeable future. 152*0eae32dcSDimitry Andric if (!DriverArgs.hasArg(options::OPT_fvisibility_EQ, 153*0eae32dcSDimitry Andric options::OPT_fvisibility_ms_compat)) 154*0eae32dcSDimitry Andric CC1Args.append( 155*0eae32dcSDimitry Andric {"-fvisibility", "hidden", "-fapply-global-visibility-to-externs"}); 156*0eae32dcSDimitry Andric 157*0eae32dcSDimitry Andric llvm::for_each(getHIPDeviceLibs(DriverArgs), 158*0eae32dcSDimitry Andric [&](const BitCodeLibraryInfo &BCFile) { 159*0eae32dcSDimitry Andric CC1Args.append({"-mlink-builtin-bitcode", 160*0eae32dcSDimitry Andric DriverArgs.MakeArgString(BCFile.Path)}); 161*0eae32dcSDimitry Andric }); 162*0eae32dcSDimitry Andric } 163*0eae32dcSDimitry Andric 164*0eae32dcSDimitry Andric Tool *HIPSPVToolChain::buildLinker() const { 165*0eae32dcSDimitry Andric assert(getTriple().getArch() == llvm::Triple::spirv64); 166*0eae32dcSDimitry Andric return new tools::HIPSPV::Linker(*this); 167*0eae32dcSDimitry Andric } 168*0eae32dcSDimitry Andric 169*0eae32dcSDimitry Andric void HIPSPVToolChain::addClangWarningOptions(ArgStringList &CC1Args) const { 170*0eae32dcSDimitry Andric HostTC.addClangWarningOptions(CC1Args); 171*0eae32dcSDimitry Andric } 172*0eae32dcSDimitry Andric 173*0eae32dcSDimitry Andric ToolChain::CXXStdlibType 174*0eae32dcSDimitry Andric HIPSPVToolChain::GetCXXStdlibType(const ArgList &Args) const { 175*0eae32dcSDimitry Andric return HostTC.GetCXXStdlibType(Args); 176*0eae32dcSDimitry Andric } 177*0eae32dcSDimitry Andric 178*0eae32dcSDimitry Andric void HIPSPVToolChain::AddClangSystemIncludeArgs(const ArgList &DriverArgs, 179*0eae32dcSDimitry Andric ArgStringList &CC1Args) const { 180*0eae32dcSDimitry Andric HostTC.AddClangSystemIncludeArgs(DriverArgs, CC1Args); 181*0eae32dcSDimitry Andric } 182*0eae32dcSDimitry Andric 183*0eae32dcSDimitry Andric void HIPSPVToolChain::AddClangCXXStdlibIncludeArgs( 184*0eae32dcSDimitry Andric const ArgList &Args, ArgStringList &CC1Args) const { 185*0eae32dcSDimitry Andric HostTC.AddClangCXXStdlibIncludeArgs(Args, CC1Args); 186*0eae32dcSDimitry Andric } 187*0eae32dcSDimitry Andric 188*0eae32dcSDimitry Andric void HIPSPVToolChain::AddIAMCUIncludeArgs(const ArgList &Args, 189*0eae32dcSDimitry Andric ArgStringList &CC1Args) const { 190*0eae32dcSDimitry Andric HostTC.AddIAMCUIncludeArgs(Args, CC1Args); 191*0eae32dcSDimitry Andric } 192*0eae32dcSDimitry Andric 193*0eae32dcSDimitry Andric void HIPSPVToolChain::AddHIPIncludeArgs(const ArgList &DriverArgs, 194*0eae32dcSDimitry Andric ArgStringList &CC1Args) const { 195*0eae32dcSDimitry Andric if (DriverArgs.hasArg(options::OPT_nogpuinc)) 196*0eae32dcSDimitry Andric return; 197*0eae32dcSDimitry Andric 198*0eae32dcSDimitry Andric StringRef hipPath = DriverArgs.getLastArgValue(options::OPT_hip_path_EQ); 199*0eae32dcSDimitry Andric if (hipPath.empty()) { 200*0eae32dcSDimitry Andric getDriver().Diag(diag::err_drv_hipspv_no_hip_path) << 1 << "'-nogpuinc'"; 201*0eae32dcSDimitry Andric return; 202*0eae32dcSDimitry Andric } 203*0eae32dcSDimitry Andric SmallString<128> P(hipPath); 204*0eae32dcSDimitry Andric llvm::sys::path::append(P, "include"); 205*0eae32dcSDimitry Andric CC1Args.append({"-isystem", DriverArgs.MakeArgString(P)}); 206*0eae32dcSDimitry Andric } 207*0eae32dcSDimitry Andric 208*0eae32dcSDimitry Andric llvm::SmallVector<ToolChain::BitCodeLibraryInfo, 12> 209*0eae32dcSDimitry Andric HIPSPVToolChain::getHIPDeviceLibs(const llvm::opt::ArgList &DriverArgs) const { 210*0eae32dcSDimitry Andric llvm::SmallVector<ToolChain::BitCodeLibraryInfo, 12> BCLibs; 211*0eae32dcSDimitry Andric if (DriverArgs.hasArg(options::OPT_nogpulib)) 212*0eae32dcSDimitry Andric return {}; 213*0eae32dcSDimitry Andric 214*0eae32dcSDimitry Andric ArgStringList LibraryPaths; 215*0eae32dcSDimitry Andric // Find device libraries in --hip-device-lib-path and HIP_DEVICE_LIB_PATH. 216*0eae32dcSDimitry Andric auto HipDeviceLibPathArgs = DriverArgs.getAllArgValues( 217*0eae32dcSDimitry Andric // --hip-device-lib-path is alias to this option. 218*0eae32dcSDimitry Andric clang::driver::options::OPT_rocm_device_lib_path_EQ); 219*0eae32dcSDimitry Andric for (auto Path : HipDeviceLibPathArgs) 220*0eae32dcSDimitry Andric LibraryPaths.push_back(DriverArgs.MakeArgString(Path)); 221*0eae32dcSDimitry Andric 222*0eae32dcSDimitry Andric StringRef HipPath = DriverArgs.getLastArgValue(options::OPT_hip_path_EQ); 223*0eae32dcSDimitry Andric if (!HipPath.empty()) { 224*0eae32dcSDimitry Andric SmallString<128> Path(HipPath); 225*0eae32dcSDimitry Andric llvm::sys::path::append(Path, "lib", "hip-device-lib"); 226*0eae32dcSDimitry Andric LibraryPaths.push_back(DriverArgs.MakeArgString(Path)); 227*0eae32dcSDimitry Andric } 228*0eae32dcSDimitry Andric 229*0eae32dcSDimitry Andric addDirectoryList(DriverArgs, LibraryPaths, "", "HIP_DEVICE_LIB_PATH"); 230*0eae32dcSDimitry Andric 231*0eae32dcSDimitry Andric // Maintain compatability with --hip-device-lib. 232*0eae32dcSDimitry Andric auto BCLibArgs = DriverArgs.getAllArgValues(options::OPT_hip_device_lib_EQ); 233*0eae32dcSDimitry Andric if (!BCLibArgs.empty()) { 234*0eae32dcSDimitry Andric llvm::for_each(BCLibArgs, [&](StringRef BCName) { 235*0eae32dcSDimitry Andric StringRef FullName; 236*0eae32dcSDimitry Andric for (std::string LibraryPath : LibraryPaths) { 237*0eae32dcSDimitry Andric SmallString<128> Path(LibraryPath); 238*0eae32dcSDimitry Andric llvm::sys::path::append(Path, BCName); 239*0eae32dcSDimitry Andric FullName = Path; 240*0eae32dcSDimitry Andric if (llvm::sys::fs::exists(FullName)) { 241*0eae32dcSDimitry Andric BCLibs.emplace_back(FullName.str()); 242*0eae32dcSDimitry Andric return; 243*0eae32dcSDimitry Andric } 244*0eae32dcSDimitry Andric } 245*0eae32dcSDimitry Andric getDriver().Diag(diag::err_drv_no_such_file) << BCName; 246*0eae32dcSDimitry Andric }); 247*0eae32dcSDimitry Andric } else { 248*0eae32dcSDimitry Andric // Search device library named as 'hipspv-<triple>.bc'. 249*0eae32dcSDimitry Andric auto TT = getTriple().normalize(); 250*0eae32dcSDimitry Andric std::string BCName = "hipspv-" + TT + ".bc"; 251*0eae32dcSDimitry Andric for (auto *LibPath : LibraryPaths) { 252*0eae32dcSDimitry Andric SmallString<128> Path(LibPath); 253*0eae32dcSDimitry Andric llvm::sys::path::append(Path, BCName); 254*0eae32dcSDimitry Andric if (llvm::sys::fs::exists(Path)) { 255*0eae32dcSDimitry Andric BCLibs.emplace_back(Path.str().str()); 256*0eae32dcSDimitry Andric return BCLibs; 257*0eae32dcSDimitry Andric } 258*0eae32dcSDimitry Andric } 259*0eae32dcSDimitry Andric getDriver().Diag(diag::err_drv_no_hipspv_device_lib) 260*0eae32dcSDimitry Andric << 1 << ("'" + TT + "' target"); 261*0eae32dcSDimitry Andric return {}; 262*0eae32dcSDimitry Andric } 263*0eae32dcSDimitry Andric 264*0eae32dcSDimitry Andric return BCLibs; 265*0eae32dcSDimitry Andric } 266*0eae32dcSDimitry Andric 267*0eae32dcSDimitry Andric SanitizerMask HIPSPVToolChain::getSupportedSanitizers() const { 268*0eae32dcSDimitry Andric // The HIPSPVToolChain only supports sanitizers in the sense that it allows 269*0eae32dcSDimitry Andric // sanitizer arguments on the command line if they are supported by the host 270*0eae32dcSDimitry Andric // toolchain. The HIPSPVToolChain will actually ignore any command line 271*0eae32dcSDimitry Andric // arguments for any of these "supported" sanitizers. That means that no 272*0eae32dcSDimitry Andric // sanitization of device code is actually supported at this time. 273*0eae32dcSDimitry Andric // 274*0eae32dcSDimitry Andric // This behavior is necessary because the host and device toolchains 275*0eae32dcSDimitry Andric // invocations often share the command line, so the device toolchain must 276*0eae32dcSDimitry Andric // tolerate flags meant only for the host toolchain. 277*0eae32dcSDimitry Andric return HostTC.getSupportedSanitizers(); 278*0eae32dcSDimitry Andric } 279*0eae32dcSDimitry Andric 280*0eae32dcSDimitry Andric VersionTuple HIPSPVToolChain::computeMSVCVersion(const Driver *D, 281*0eae32dcSDimitry Andric const ArgList &Args) const { 282*0eae32dcSDimitry Andric return HostTC.computeMSVCVersion(D, Args); 283*0eae32dcSDimitry Andric } 284*0eae32dcSDimitry Andric 285*0eae32dcSDimitry Andric void HIPSPVToolChain::adjustDebugInfoKind( 286*0eae32dcSDimitry Andric codegenoptions::DebugInfoKind &DebugInfoKind, 287*0eae32dcSDimitry Andric const llvm::opt::ArgList &Args) const { 288*0eae32dcSDimitry Andric // Debug info generation is disabled for SPIRV-LLVM-Translator 289*0eae32dcSDimitry Andric // which currently aborts on the presence of DW_OP_LLVM_convert. 290*0eae32dcSDimitry Andric // TODO: Enable debug info when the SPIR-V backend arrives. 291*0eae32dcSDimitry Andric DebugInfoKind = codegenoptions::NoDebugInfo; 292*0eae32dcSDimitry Andric } 293