10eae32dcSDimitry Andric //===--- HIPSPV.cpp - HIPSPV ToolChain Implementation -----------*- C++ -*-===// 20eae32dcSDimitry Andric // 30eae32dcSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 40eae32dcSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 50eae32dcSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60eae32dcSDimitry Andric // 70eae32dcSDimitry Andric //===----------------------------------------------------------------------===// 80eae32dcSDimitry Andric 90eae32dcSDimitry Andric #include "HIPSPV.h" 100eae32dcSDimitry Andric #include "CommonArgs.h" 110eae32dcSDimitry Andric #include "HIPUtility.h" 120eae32dcSDimitry Andric #include "clang/Driver/Compilation.h" 130eae32dcSDimitry Andric #include "clang/Driver/Driver.h" 140eae32dcSDimitry Andric #include "clang/Driver/DriverDiagnostic.h" 150eae32dcSDimitry Andric #include "clang/Driver/InputInfo.h" 160eae32dcSDimitry Andric #include "clang/Driver/Options.h" 170eae32dcSDimitry Andric #include "llvm/Support/FileSystem.h" 180eae32dcSDimitry Andric #include "llvm/Support/Path.h" 190eae32dcSDimitry Andric 200eae32dcSDimitry Andric using namespace clang::driver; 210eae32dcSDimitry Andric using namespace clang::driver::toolchains; 220eae32dcSDimitry Andric using namespace clang::driver::tools; 230eae32dcSDimitry Andric using namespace clang; 240eae32dcSDimitry Andric using namespace llvm::opt; 250eae32dcSDimitry Andric 260eae32dcSDimitry Andric // Convenience function for creating temporary file for both modes of 270eae32dcSDimitry Andric // isSaveTempsEnabled(). 280eae32dcSDimitry Andric static const char *getTempFile(Compilation &C, StringRef Prefix, 290eae32dcSDimitry Andric StringRef Extension) { 300eae32dcSDimitry Andric if (C.getDriver().isSaveTempsEnabled()) { 310eae32dcSDimitry Andric return C.getArgs().MakeArgString(Prefix + "." + Extension); 320eae32dcSDimitry Andric } 330eae32dcSDimitry Andric auto TmpFile = C.getDriver().GetTemporaryPath(Prefix, Extension); 340eae32dcSDimitry Andric return C.addTempFile(C.getArgs().MakeArgString(TmpFile)); 350eae32dcSDimitry Andric } 360eae32dcSDimitry Andric 370eae32dcSDimitry Andric // Locates HIP pass plugin. 380eae32dcSDimitry Andric static std::string findPassPlugin(const Driver &D, 390eae32dcSDimitry Andric const llvm::opt::ArgList &Args) { 400eae32dcSDimitry Andric StringRef Path = Args.getLastArgValue(options::OPT_hipspv_pass_plugin_EQ); 410eae32dcSDimitry Andric if (!Path.empty()) { 420eae32dcSDimitry Andric if (llvm::sys::fs::exists(Path)) 430eae32dcSDimitry Andric return Path.str(); 440eae32dcSDimitry Andric D.Diag(diag::err_drv_no_such_file) << Path; 450eae32dcSDimitry Andric } 460eae32dcSDimitry Andric 470eae32dcSDimitry Andric StringRef hipPath = Args.getLastArgValue(options::OPT_hip_path_EQ); 480eae32dcSDimitry Andric if (!hipPath.empty()) { 490eae32dcSDimitry Andric SmallString<128> PluginPath(hipPath); 500eae32dcSDimitry Andric llvm::sys::path::append(PluginPath, "lib", "libLLVMHipSpvPasses.so"); 510eae32dcSDimitry Andric if (llvm::sys::fs::exists(PluginPath)) 520eae32dcSDimitry Andric return PluginPath.str().str(); 530eae32dcSDimitry Andric PluginPath.assign(hipPath); 540eae32dcSDimitry Andric llvm::sys::path::append(PluginPath, "lib", "llvm", 550eae32dcSDimitry Andric "libLLVMHipSpvPasses.so"); 560eae32dcSDimitry Andric if (llvm::sys::fs::exists(PluginPath)) 570eae32dcSDimitry Andric return PluginPath.str().str(); 580eae32dcSDimitry Andric } 590eae32dcSDimitry Andric 600eae32dcSDimitry Andric return std::string(); 610eae32dcSDimitry Andric } 620eae32dcSDimitry Andric 630eae32dcSDimitry Andric void HIPSPV::Linker::constructLinkAndEmitSpirvCommand( 640eae32dcSDimitry Andric Compilation &C, const JobAction &JA, const InputInfoList &Inputs, 650eae32dcSDimitry Andric const InputInfo &Output, const llvm::opt::ArgList &Args) const { 660eae32dcSDimitry Andric 670eae32dcSDimitry Andric assert(!Inputs.empty() && "Must have at least one input."); 680eae32dcSDimitry Andric std::string Name = std::string(llvm::sys::path::stem(Output.getFilename())); 690eae32dcSDimitry Andric const char *TempFile = getTempFile(C, Name + "-link", "bc"); 700eae32dcSDimitry Andric 710eae32dcSDimitry Andric // Link LLVM bitcode. 720eae32dcSDimitry Andric ArgStringList LinkArgs{}; 730eae32dcSDimitry Andric for (auto Input : Inputs) 740eae32dcSDimitry Andric LinkArgs.push_back(Input.getFilename()); 750eae32dcSDimitry Andric LinkArgs.append({"-o", TempFile}); 760eae32dcSDimitry Andric const char *LlvmLink = 770eae32dcSDimitry Andric Args.MakeArgString(getToolChain().GetProgramPath("llvm-link")); 780eae32dcSDimitry Andric C.addCommand(std::make_unique<Command>(JA, *this, ResponseFileSupport::None(), 790eae32dcSDimitry Andric LlvmLink, LinkArgs, Inputs, Output)); 800eae32dcSDimitry Andric 810eae32dcSDimitry Andric // Post-link HIP lowering. 820eae32dcSDimitry Andric 830eae32dcSDimitry Andric // Run LLVM IR passes to lower/expand/emulate HIP code that does not translate 840eae32dcSDimitry Andric // to SPIR-V (E.g. dynamic shared memory). 850eae32dcSDimitry Andric auto PassPluginPath = findPassPlugin(C.getDriver(), Args); 860eae32dcSDimitry Andric if (!PassPluginPath.empty()) { 870eae32dcSDimitry Andric const char *PassPathCStr = C.getArgs().MakeArgString(PassPluginPath); 880eae32dcSDimitry Andric const char *OptOutput = getTempFile(C, Name + "-lower", "bc"); 890eae32dcSDimitry Andric ArgStringList OptArgs{TempFile, "-load-pass-plugin", 900eae32dcSDimitry Andric PassPathCStr, "-passes=hip-post-link-passes", 910eae32dcSDimitry Andric "-o", OptOutput}; 920eae32dcSDimitry Andric const char *Opt = Args.MakeArgString(getToolChain().GetProgramPath("opt")); 930eae32dcSDimitry Andric C.addCommand(std::make_unique<Command>( 940eae32dcSDimitry Andric JA, *this, ResponseFileSupport::None(), Opt, OptArgs, Inputs, Output)); 950eae32dcSDimitry Andric TempFile = OptOutput; 960eae32dcSDimitry Andric } 970eae32dcSDimitry Andric 980eae32dcSDimitry Andric // Emit SPIR-V binary. 990eae32dcSDimitry Andric 1000eae32dcSDimitry Andric llvm::opt::ArgStringList TrArgs{"--spirv-max-version=1.1", 1010eae32dcSDimitry Andric "--spirv-ext=+all"}; 1020eae32dcSDimitry Andric InputInfo TrInput = InputInfo(types::TY_LLVM_BC, TempFile, ""); 1030eae32dcSDimitry Andric SPIRV::constructTranslateCommand(C, *this, JA, Output, TrInput, TrArgs); 1040eae32dcSDimitry Andric } 1050eae32dcSDimitry Andric 1060eae32dcSDimitry Andric void HIPSPV::Linker::ConstructJob(Compilation &C, const JobAction &JA, 1070eae32dcSDimitry Andric const InputInfo &Output, 1080eae32dcSDimitry Andric const InputInfoList &Inputs, 1090eae32dcSDimitry Andric const ArgList &Args, 1100eae32dcSDimitry Andric const char *LinkingOutput) const { 1110eae32dcSDimitry Andric if (Inputs.size() > 0 && Inputs[0].getType() == types::TY_Image && 1120eae32dcSDimitry Andric JA.getType() == types::TY_Object) 1130eae32dcSDimitry Andric return HIP::constructGenerateObjFileFromHIPFatBinary(C, Output, Inputs, 1140eae32dcSDimitry Andric Args, JA, *this); 1150eae32dcSDimitry Andric 1160eae32dcSDimitry Andric if (JA.getType() == types::TY_HIP_FATBIN) 1170eae32dcSDimitry Andric return HIP::constructHIPFatbinCommand(C, JA, Output.getFilename(), Inputs, 1180eae32dcSDimitry Andric Args, *this); 1190eae32dcSDimitry Andric 1200eae32dcSDimitry Andric constructLinkAndEmitSpirvCommand(C, JA, Inputs, Output, Args); 1210eae32dcSDimitry Andric } 1220eae32dcSDimitry Andric 1230eae32dcSDimitry Andric HIPSPVToolChain::HIPSPVToolChain(const Driver &D, const llvm::Triple &Triple, 1240eae32dcSDimitry Andric const ToolChain &HostTC, const ArgList &Args) 1250eae32dcSDimitry Andric : ToolChain(D, Triple, Args), HostTC(HostTC) { 1260eae32dcSDimitry Andric // Lookup binaries into the driver directory, this is used to 1270eae32dcSDimitry Andric // discover the clang-offload-bundler executable. 1280eae32dcSDimitry Andric getProgramPaths().push_back(getDriver().Dir); 1290eae32dcSDimitry Andric } 1300eae32dcSDimitry Andric 1310eae32dcSDimitry Andric void HIPSPVToolChain::addClangTargetOptions( 1320eae32dcSDimitry Andric const llvm::opt::ArgList &DriverArgs, llvm::opt::ArgStringList &CC1Args, 1330eae32dcSDimitry Andric Action::OffloadKind DeviceOffloadingKind) const { 1340eae32dcSDimitry Andric HostTC.addClangTargetOptions(DriverArgs, CC1Args, DeviceOffloadingKind); 1350eae32dcSDimitry Andric 1360eae32dcSDimitry Andric assert(DeviceOffloadingKind == Action::OFK_HIP && 1370eae32dcSDimitry Andric "Only HIP offloading kinds are supported for GPUs."); 1380eae32dcSDimitry Andric 1390eae32dcSDimitry Andric CC1Args.append( 1400eae32dcSDimitry Andric {"-fcuda-is-device", "-fcuda-allow-variadic-functions", 1410eae32dcSDimitry Andric // A crude workaround for llvm-spirv which does not handle the 1420eae32dcSDimitry Andric // autovectorized code well (vector reductions, non-i{8,16,32,64} types). 1430eae32dcSDimitry Andric // TODO: Allow autovectorization when SPIR-V backend arrives. 1440eae32dcSDimitry Andric "-mllvm", "-vectorize-loops=false", "-mllvm", "-vectorize-slp=false"}); 1450eae32dcSDimitry Andric 1460eae32dcSDimitry Andric // Default to "hidden" visibility, as object level linking will not be 1470eae32dcSDimitry Andric // supported for the foreseeable future. 1480eae32dcSDimitry Andric if (!DriverArgs.hasArg(options::OPT_fvisibility_EQ, 1490eae32dcSDimitry Andric options::OPT_fvisibility_ms_compat)) 1500eae32dcSDimitry Andric CC1Args.append( 151bdd1243dSDimitry Andric {"-fvisibility=hidden", "-fapply-global-visibility-to-externs"}); 1520eae32dcSDimitry Andric 153bdd1243dSDimitry Andric llvm::for_each(getDeviceLibs(DriverArgs), 1540eae32dcSDimitry Andric [&](const BitCodeLibraryInfo &BCFile) { 1550eae32dcSDimitry Andric CC1Args.append({"-mlink-builtin-bitcode", 1560eae32dcSDimitry Andric DriverArgs.MakeArgString(BCFile.Path)}); 1570eae32dcSDimitry Andric }); 1580eae32dcSDimitry Andric } 1590eae32dcSDimitry Andric 1600eae32dcSDimitry Andric Tool *HIPSPVToolChain::buildLinker() const { 1610eae32dcSDimitry Andric assert(getTriple().getArch() == llvm::Triple::spirv64); 1620eae32dcSDimitry Andric return new tools::HIPSPV::Linker(*this); 1630eae32dcSDimitry Andric } 1640eae32dcSDimitry Andric 1650eae32dcSDimitry Andric void HIPSPVToolChain::addClangWarningOptions(ArgStringList &CC1Args) const { 1660eae32dcSDimitry Andric HostTC.addClangWarningOptions(CC1Args); 1670eae32dcSDimitry Andric } 1680eae32dcSDimitry Andric 1690eae32dcSDimitry Andric ToolChain::CXXStdlibType 1700eae32dcSDimitry Andric HIPSPVToolChain::GetCXXStdlibType(const ArgList &Args) const { 1710eae32dcSDimitry Andric return HostTC.GetCXXStdlibType(Args); 1720eae32dcSDimitry Andric } 1730eae32dcSDimitry Andric 1740eae32dcSDimitry Andric void HIPSPVToolChain::AddClangSystemIncludeArgs(const ArgList &DriverArgs, 1750eae32dcSDimitry Andric ArgStringList &CC1Args) const { 1760eae32dcSDimitry Andric HostTC.AddClangSystemIncludeArgs(DriverArgs, CC1Args); 1770eae32dcSDimitry Andric } 1780eae32dcSDimitry Andric 1790eae32dcSDimitry Andric void HIPSPVToolChain::AddClangCXXStdlibIncludeArgs( 1800eae32dcSDimitry Andric const ArgList &Args, ArgStringList &CC1Args) const { 1810eae32dcSDimitry Andric HostTC.AddClangCXXStdlibIncludeArgs(Args, CC1Args); 1820eae32dcSDimitry Andric } 1830eae32dcSDimitry Andric 1840eae32dcSDimitry Andric void HIPSPVToolChain::AddIAMCUIncludeArgs(const ArgList &Args, 1850eae32dcSDimitry Andric ArgStringList &CC1Args) const { 1860eae32dcSDimitry Andric HostTC.AddIAMCUIncludeArgs(Args, CC1Args); 1870eae32dcSDimitry Andric } 1880eae32dcSDimitry Andric 1890eae32dcSDimitry Andric void HIPSPVToolChain::AddHIPIncludeArgs(const ArgList &DriverArgs, 1900eae32dcSDimitry Andric ArgStringList &CC1Args) const { 1910eae32dcSDimitry Andric if (DriverArgs.hasArg(options::OPT_nogpuinc)) 1920eae32dcSDimitry Andric return; 1930eae32dcSDimitry Andric 1940eae32dcSDimitry Andric StringRef hipPath = DriverArgs.getLastArgValue(options::OPT_hip_path_EQ); 1950eae32dcSDimitry Andric if (hipPath.empty()) { 196*0fca6ea1SDimitry Andric getDriver().Diag(diag::err_drv_hipspv_no_hip_path); 1970eae32dcSDimitry Andric return; 1980eae32dcSDimitry Andric } 1990eae32dcSDimitry Andric SmallString<128> P(hipPath); 2000eae32dcSDimitry Andric llvm::sys::path::append(P, "include"); 2010eae32dcSDimitry Andric CC1Args.append({"-isystem", DriverArgs.MakeArgString(P)}); 2020eae32dcSDimitry Andric } 2030eae32dcSDimitry Andric 2040eae32dcSDimitry Andric llvm::SmallVector<ToolChain::BitCodeLibraryInfo, 12> 205bdd1243dSDimitry Andric HIPSPVToolChain::getDeviceLibs(const llvm::opt::ArgList &DriverArgs) const { 2060eae32dcSDimitry Andric llvm::SmallVector<ToolChain::BitCodeLibraryInfo, 12> BCLibs; 2070eae32dcSDimitry Andric if (DriverArgs.hasArg(options::OPT_nogpulib)) 2080eae32dcSDimitry Andric return {}; 2090eae32dcSDimitry Andric 2100eae32dcSDimitry Andric ArgStringList LibraryPaths; 2110eae32dcSDimitry Andric // Find device libraries in --hip-device-lib-path and HIP_DEVICE_LIB_PATH. 2120eae32dcSDimitry Andric auto HipDeviceLibPathArgs = DriverArgs.getAllArgValues( 2130eae32dcSDimitry Andric // --hip-device-lib-path is alias to this option. 2140eae32dcSDimitry Andric clang::driver::options::OPT_rocm_device_lib_path_EQ); 2150eae32dcSDimitry Andric for (auto Path : HipDeviceLibPathArgs) 2160eae32dcSDimitry Andric LibraryPaths.push_back(DriverArgs.MakeArgString(Path)); 2170eae32dcSDimitry Andric 2180eae32dcSDimitry Andric StringRef HipPath = DriverArgs.getLastArgValue(options::OPT_hip_path_EQ); 2190eae32dcSDimitry Andric if (!HipPath.empty()) { 2200eae32dcSDimitry Andric SmallString<128> Path(HipPath); 2210eae32dcSDimitry Andric llvm::sys::path::append(Path, "lib", "hip-device-lib"); 2220eae32dcSDimitry Andric LibraryPaths.push_back(DriverArgs.MakeArgString(Path)); 2230eae32dcSDimitry Andric } 2240eae32dcSDimitry Andric 2250eae32dcSDimitry Andric addDirectoryList(DriverArgs, LibraryPaths, "", "HIP_DEVICE_LIB_PATH"); 2260eae32dcSDimitry Andric 2270eae32dcSDimitry Andric // Maintain compatability with --hip-device-lib. 2280eae32dcSDimitry Andric auto BCLibArgs = DriverArgs.getAllArgValues(options::OPT_hip_device_lib_EQ); 2290eae32dcSDimitry Andric if (!BCLibArgs.empty()) { 2300eae32dcSDimitry Andric llvm::for_each(BCLibArgs, [&](StringRef BCName) { 2310eae32dcSDimitry Andric StringRef FullName; 2320eae32dcSDimitry Andric for (std::string LibraryPath : LibraryPaths) { 2330eae32dcSDimitry Andric SmallString<128> Path(LibraryPath); 2340eae32dcSDimitry Andric llvm::sys::path::append(Path, BCName); 2350eae32dcSDimitry Andric FullName = Path; 2360eae32dcSDimitry Andric if (llvm::sys::fs::exists(FullName)) { 2370eae32dcSDimitry Andric BCLibs.emplace_back(FullName.str()); 2380eae32dcSDimitry Andric return; 2390eae32dcSDimitry Andric } 2400eae32dcSDimitry Andric } 2410eae32dcSDimitry Andric getDriver().Diag(diag::err_drv_no_such_file) << BCName; 2420eae32dcSDimitry Andric }); 2430eae32dcSDimitry Andric } else { 2440eae32dcSDimitry Andric // Search device library named as 'hipspv-<triple>.bc'. 2450eae32dcSDimitry Andric auto TT = getTriple().normalize(); 2460eae32dcSDimitry Andric std::string BCName = "hipspv-" + TT + ".bc"; 2470eae32dcSDimitry Andric for (auto *LibPath : LibraryPaths) { 2480eae32dcSDimitry Andric SmallString<128> Path(LibPath); 2490eae32dcSDimitry Andric llvm::sys::path::append(Path, BCName); 2500eae32dcSDimitry Andric if (llvm::sys::fs::exists(Path)) { 2510eae32dcSDimitry Andric BCLibs.emplace_back(Path.str().str()); 2520eae32dcSDimitry Andric return BCLibs; 2530eae32dcSDimitry Andric } 2540eae32dcSDimitry Andric } 2550eae32dcSDimitry Andric getDriver().Diag(diag::err_drv_no_hipspv_device_lib) 2560eae32dcSDimitry Andric << 1 << ("'" + TT + "' target"); 2570eae32dcSDimitry Andric return {}; 2580eae32dcSDimitry Andric } 2590eae32dcSDimitry Andric 2600eae32dcSDimitry Andric return BCLibs; 2610eae32dcSDimitry Andric } 2620eae32dcSDimitry Andric 2630eae32dcSDimitry Andric SanitizerMask HIPSPVToolChain::getSupportedSanitizers() const { 2640eae32dcSDimitry Andric // The HIPSPVToolChain only supports sanitizers in the sense that it allows 2650eae32dcSDimitry Andric // sanitizer arguments on the command line if they are supported by the host 2660eae32dcSDimitry Andric // toolchain. The HIPSPVToolChain will actually ignore any command line 2670eae32dcSDimitry Andric // arguments for any of these "supported" sanitizers. That means that no 2680eae32dcSDimitry Andric // sanitization of device code is actually supported at this time. 2690eae32dcSDimitry Andric // 2700eae32dcSDimitry Andric // This behavior is necessary because the host and device toolchains 2710eae32dcSDimitry Andric // invocations often share the command line, so the device toolchain must 2720eae32dcSDimitry Andric // tolerate flags meant only for the host toolchain. 2730eae32dcSDimitry Andric return HostTC.getSupportedSanitizers(); 2740eae32dcSDimitry Andric } 2750eae32dcSDimitry Andric 2760eae32dcSDimitry Andric VersionTuple HIPSPVToolChain::computeMSVCVersion(const Driver *D, 2770eae32dcSDimitry Andric const ArgList &Args) const { 2780eae32dcSDimitry Andric return HostTC.computeMSVCVersion(D, Args); 2790eae32dcSDimitry Andric } 2800eae32dcSDimitry Andric 2810eae32dcSDimitry Andric void HIPSPVToolChain::adjustDebugInfoKind( 28206c3fb27SDimitry Andric llvm::codegenoptions::DebugInfoKind &DebugInfoKind, 2830eae32dcSDimitry Andric const llvm::opt::ArgList &Args) const { 2840eae32dcSDimitry Andric // Debug info generation is disabled for SPIRV-LLVM-Translator 2850eae32dcSDimitry Andric // which currently aborts on the presence of DW_OP_LLVM_convert. 2860eae32dcSDimitry Andric // TODO: Enable debug info when the SPIR-V backend arrives. 28706c3fb27SDimitry Andric DebugInfoKind = llvm::codegenoptions::NoDebugInfo; 2880eae32dcSDimitry Andric } 289