brintos

brintos / llvm-project-archived public Read only

0
0
Text · 18.5 KiB · f2f6492 Raw
469 lines · cpp
1//===--- HIPAMD.cpp - HIP Tool and ToolChain Implementations ----*- 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-exception6//7//===----------------------------------------------------------------------===//8 9#include "HIPAMD.h"10#include "AMDGPU.h"11#include "HIPUtility.h"12#include "SPIRV.h"13#include "clang/Basic/Cuda.h"14#include "clang/Driver/CommonArgs.h"15#include "clang/Driver/Compilation.h"16#include "clang/Driver/Driver.h"17#include "clang/Driver/InputInfo.h"18#include "clang/Driver/SanitizerArgs.h"19#include "clang/Options/Options.h"20#include "llvm/Support/FileSystem.h"21#include "llvm/Support/Path.h"22#include "llvm/TargetParser/TargetParser.h"23 24using namespace clang::driver;25using namespace clang::driver::toolchains;26using namespace clang::driver::tools;27using namespace clang;28using namespace llvm::opt;29 30#if defined(_WIN32) || defined(_WIN64)31#define NULL_FILE "nul"32#else33#define NULL_FILE "/dev/null"34#endif35 36void AMDGCN::Linker::constructLlvmLinkCommand(Compilation &C,37                                         const JobAction &JA,38                                         const InputInfoList &Inputs,39                                         const InputInfo &Output,40                                         const llvm::opt::ArgList &Args) const {41  // Construct llvm-link command.42  // The output from llvm-link is a bitcode file.43  ArgStringList LlvmLinkArgs;44 45  assert(!Inputs.empty() && "Must have at least one input.");46 47  LlvmLinkArgs.append({"-o", Output.getFilename()});48  for (auto Input : Inputs)49    LlvmLinkArgs.push_back(Input.getFilename());50 51  // Look for archive of bundled bitcode in arguments, and add temporary files52  // for the extracted archive of bitcode to inputs.53  auto TargetID = Args.getLastArgValue(options::OPT_mcpu_EQ);54  AddStaticDeviceLibsLinking(C, *this, JA, Inputs, Args, LlvmLinkArgs, "amdgcn",55                             TargetID, /*IsBitCodeSDL=*/true);56 57  const char *LlvmLink =58    Args.MakeArgString(getToolChain().GetProgramPath("llvm-link"));59  C.addCommand(std::make_unique<Command>(JA, *this, ResponseFileSupport::None(),60                                         LlvmLink, LlvmLinkArgs, Inputs,61                                         Output));62}63 64void AMDGCN::Linker::constructLldCommand(Compilation &C, const JobAction &JA,65                                         const InputInfoList &Inputs,66                                         const InputInfo &Output,67                                         const llvm::opt::ArgList &Args) const {68  // Construct lld command.69  // The output from ld.lld is an HSA code object file.70  ArgStringList LldArgs{"-flavor",71                        "gnu",72                        "-m",73                        "elf64_amdgpu",74                        "--no-undefined",75                        "-shared",76                        "-plugin-opt=-amdgpu-internalize-symbols"};77  if (Args.hasArg(options::OPT_hipstdpar))78    LldArgs.push_back("-plugin-opt=-amdgpu-enable-hipstdpar");79 80  auto &TC = getToolChain();81  auto &D = TC.getDriver();82  bool IsThinLTO = D.getOffloadLTOMode() == LTOK_Thin;83  addLTOOptions(TC, Args, LldArgs, Output, Inputs, IsThinLTO);84 85  // Extract all the -m options86  std::vector<llvm::StringRef> Features;87  amdgpu::getAMDGPUTargetFeatures(D, TC.getTriple(), Args, Features);88 89  // Add features to mattr such as cumode90  std::string MAttrString = "-plugin-opt=-mattr=";91  for (auto OneFeature : unifyTargetFeatures(Features)) {92    MAttrString.append(Args.MakeArgString(OneFeature));93    if (OneFeature != Features.back())94      MAttrString.append(",");95  }96  if (!Features.empty())97    LldArgs.push_back(Args.MakeArgString(MAttrString));98 99  // ToDo: Remove this option after AMDGPU backend supports ISA-level linking.100  // Since AMDGPU backend currently does not support ISA-level linking, all101  // called functions need to be imported.102  if (IsThinLTO) {103    LldArgs.push_back(Args.MakeArgString("-plugin-opt=-force-import-all"));104    LldArgs.push_back(Args.MakeArgString("-plugin-opt=-avail-extern-to-local"));105    LldArgs.push_back(Args.MakeArgString(106        "-plugin-opt=-avail-extern-gv-in-addrspace-to-local=3"));107  }108 109  for (const Arg *A : Args.filtered(options::OPT_mllvm)) {110    LldArgs.push_back(111        Args.MakeArgString(Twine("-plugin-opt=") + A->getValue(0)));112  }113 114  if (C.getDriver().isSaveTempsEnabled())115    LldArgs.push_back("-save-temps");116 117  addLinkerCompressDebugSectionsOption(TC, Args, LldArgs);118 119  // Given that host and device linking happen in separate processes, the device120  // linker doesn't always have the visibility as to which device symbols are121  // needed by a program, especially for the device symbol dependencies that are122  // introduced through the host symbol resolution.123  // For example: host_A() (A.obj) --> host_B(B.obj) --> device_kernel_B()124  // (B.obj) In this case, the device linker doesn't know that A.obj actually125  // depends on the kernel functions in B.obj.  When linking to static device126  // library, the device linker may drop some of the device global symbols if127  // they aren't referenced.  As a workaround, we are adding to the128  // --whole-archive flag such that all global symbols would be linked in.129  LldArgs.push_back("--whole-archive");130 131  for (auto *Arg : Args.filtered(options::OPT_Xoffload_linker)) {132    StringRef ArgVal = Arg->getValue(1);133    auto SplitArg = ArgVal.split("-mllvm=");134    if (!SplitArg.second.empty()) {135      LldArgs.push_back(136          Args.MakeArgString(Twine("-plugin-opt=") + SplitArg.second));137    } else {138      LldArgs.push_back(Args.MakeArgString(ArgVal));139    }140    Arg->claim();141  }142 143  LldArgs.append({"-o", Output.getFilename()});144  for (auto Input : Inputs)145    LldArgs.push_back(Input.getFilename());146 147  // Look for archive of bundled bitcode in arguments, and add temporary files148  // for the extracted archive of bitcode to inputs.149  auto TargetID = Args.getLastArgValue(options::OPT_mcpu_EQ);150  AddStaticDeviceLibsLinking(C, *this, JA, Inputs, Args, LldArgs, "amdgcn",151                             TargetID, /*IsBitCodeSDL=*/true);152 153  LldArgs.push_back("--no-whole-archive");154 155  const char *Lld = Args.MakeArgString(getToolChain().GetProgramPath("lld"));156  C.addCommand(std::make_unique<Command>(JA, *this, ResponseFileSupport::None(),157                                         Lld, LldArgs, Inputs, Output));158}159 160// For SPIR-V the inputs for the job are device AMDGCN SPIR-V flavoured bitcode161// and the output is either a compiled SPIR-V binary or bitcode (-emit-llvm). It162// calls llvm-link and then the llvm-spirv translator or the SPIR-V BE.163// TODO: consider if we want to run any targeted optimisations over IR here,164// over generic SPIR-V.165void AMDGCN::Linker::constructLinkAndEmitSpirvCommand(166    Compilation &C, const JobAction &JA, const InputInfoList &Inputs,167    const InputInfo &Output, const llvm::opt::ArgList &Args) const {168  assert(!Inputs.empty() && "Must have at least one input.");169 170  std::string LinkedBCFilePrefix(171      Twine(llvm::sys::path::stem(Output.getFilename()), "-linked").str());172  const char *LinkedBCFilePath = HIP::getTempFile(C, LinkedBCFilePrefix, "bc");173  InputInfo LinkedBCFile(&JA, LinkedBCFilePath, Output.getBaseInput());174 175  bool UseSPIRVBackend =176      Args.hasFlag(options::OPT_use_spirv_backend,177                   options::OPT_no_use_spirv_backend, /*Default=*/false);178 179  constructLlvmLinkCommand(C, JA, Inputs, LinkedBCFile, Args);180 181  if (UseSPIRVBackend) {182    // This code handles the case in the new driver when --offload-device-only183    // is unset and clang-linker-wrapper forwards the bitcode that must be184    // compiled to SPIR-V.185 186    llvm::opt::ArgStringList CmdArgs;187    const char *Triple =188        C.getArgs().MakeArgString("-triple=spirv64-amd-amdhsa");189 190    CmdArgs.append({"-cc1", Triple, "-emit-obj", "-disable-llvm-optzns",191                    LinkedBCFile.getFilename(), "-o", Output.getFilename()});192 193    const Driver &Driver = getToolChain().getDriver();194    const char *Exec = Driver.getClangProgramPath();195    C.addCommand(std::make_unique<Command>(196        JA, *this, ResponseFileSupport::None(), Exec, CmdArgs, LinkedBCFile,197        Output, Driver.getPrependArg()));198  } else {199    // Emit SPIR-V binary using the translator200    llvm::opt::ArgStringList TrArgs{201        "--spirv-max-version=1.6",202        "--spirv-ext=+all",203        "--spirv-allow-unknown-intrinsics",204        "--spirv-lower-const-expr",205        "--spirv-preserve-auxdata",206        "--spirv-debug-info-version=nonsemantic-shader-200"};207    SPIRV::constructTranslateCommand(C, *this, JA, Output, LinkedBCFile,208                                     TrArgs);209  }210}211 212// For amdgcn the inputs of the linker job are device bitcode and output is213// either an object file or bitcode (-emit-llvm). It calls llvm-link, opt,214// llc, then lld steps.215void AMDGCN::Linker::ConstructJob(Compilation &C, const JobAction &JA,216                                  const InputInfo &Output,217                                  const InputInfoList &Inputs,218                                  const ArgList &Args,219                                  const char *LinkingOutput) const {220  if (Inputs.size() > 0 &&221      Inputs[0].getType() == types::TY_Image &&222      JA.getType() == types::TY_Object)223    return HIP::constructGenerateObjFileFromHIPFatBinary(C, Output, Inputs,224                                                         Args, JA, *this);225 226  if (JA.getType() == types::TY_HIP_FATBIN)227    return HIP::constructHIPFatbinCommand(C, JA, Output.getFilename(), Inputs,228                                          Args, *this);229 230  if (JA.getType() == types::TY_LLVM_BC)231    return constructLlvmLinkCommand(C, JA, Inputs, Output, Args);232 233  if (getToolChain().getEffectiveTriple().isSPIRV())234    return constructLinkAndEmitSpirvCommand(C, JA, Inputs, Output, Args);235 236  return constructLldCommand(C, JA, Inputs, Output, Args);237}238 239HIPAMDToolChain::HIPAMDToolChain(const Driver &D, const llvm::Triple &Triple,240                                 const ToolChain &HostTC, const ArgList &Args)241    : ROCMToolChain(D, Triple, Args), HostTC(HostTC) {242  // Lookup binaries into the driver directory, this is used to243  // discover the clang-offload-bundler executable.244  getProgramPaths().push_back(getDriver().Dir);245}246 247void HIPAMDToolChain::addClangTargetOptions(248    const llvm::opt::ArgList &DriverArgs, llvm::opt::ArgStringList &CC1Args,249    Action::OffloadKind DeviceOffloadingKind) const {250  HostTC.addClangTargetOptions(DriverArgs, CC1Args, DeviceOffloadingKind);251 252  assert(DeviceOffloadingKind == Action::OFK_HIP &&253         "Only HIP offloading kinds are supported for GPUs.");254 255  CC1Args.append({"-fcuda-is-device", "-fno-threadsafe-statics"});256 257  if (!DriverArgs.hasFlag(options::OPT_fgpu_rdc, options::OPT_fno_gpu_rdc,258                          false)) {259    CC1Args.append({"-mllvm", "-amdgpu-internalize-symbols"});260    if (DriverArgs.hasArgNoClaim(options::OPT_hipstdpar))261      CC1Args.append({"-mllvm", "-amdgpu-enable-hipstdpar"});262  }263 264  StringRef MaxThreadsPerBlock =265      DriverArgs.getLastArgValue(options::OPT_gpu_max_threads_per_block_EQ);266  if (!MaxThreadsPerBlock.empty()) {267    std::string ArgStr =268        (Twine("--gpu-max-threads-per-block=") + MaxThreadsPerBlock).str();269    CC1Args.push_back(DriverArgs.MakeArgStringRef(ArgStr));270  }271 272  CC1Args.push_back("-fcuda-allow-variadic-functions");273 274  // Default to "hidden" visibility, as object level linking will not be275  // supported for the foreseeable future.276  if (!DriverArgs.hasArg(options::OPT_fvisibility_EQ,277                         options::OPT_fvisibility_ms_compat)) {278    CC1Args.append({"-fvisibility=hidden"});279    CC1Args.push_back("-fapply-global-visibility-to-externs");280  }281 282  if (getEffectiveTriple().isSPIRV()) {283    // For SPIR-V we embed the command-line into the generated binary, in order284    // to retrieve it at JIT time and be able to do target specific compilation285    // with options that match the user-supplied ones.286    if (!DriverArgs.hasArg(options::OPT_fembed_bitcode_marker))287      CC1Args.push_back("-fembed-bitcode=marker");288    // For SPIR-V we want to retain the pristine output of Clang CodeGen, since289    // optimizations might lose structure / information that is necessary for290    // generating optimal concrete AMDGPU code. We duplicate this because the291    // HIP TC doesn't invoke the base AMDGPU TC addClangTargetOptions.292    if (!DriverArgs.hasArg(options::OPT_disable_llvm_passes))293      CC1Args.push_back("-disable-llvm-passes");294    return; // No DeviceLibs for SPIR-V.295  }296 297  for (auto BCFile : getDeviceLibs(DriverArgs, DeviceOffloadingKind)) {298    CC1Args.push_back(BCFile.ShouldInternalize ? "-mlink-builtin-bitcode"299                                               : "-mlink-bitcode-file");300    CC1Args.push_back(DriverArgs.MakeArgString(BCFile.Path));301  }302}303 304llvm::opt::DerivedArgList *305HIPAMDToolChain::TranslateArgs(const llvm::opt::DerivedArgList &Args,306                               StringRef BoundArch,307                               Action::OffloadKind DeviceOffloadKind) const {308  DerivedArgList *DAL =309      HostTC.TranslateArgs(Args, BoundArch, DeviceOffloadKind);310  if (!DAL)311    DAL = new DerivedArgList(Args.getBaseArgs());312 313  const OptTable &Opts = getDriver().getOpts();314 315  for (Arg *A : Args) {316    // Filter unsupported sanitizers passed from the HostTC.317    if (!handleSanitizeOption(*this, *DAL, Args, BoundArch, A))318      DAL->append(A);319  }320 321  if (!BoundArch.empty()) {322    DAL->eraseArg(options::OPT_mcpu_EQ);323    DAL->AddJoinedArg(nullptr, Opts.getOption(options::OPT_mcpu_EQ), BoundArch);324    checkTargetID(*DAL);325  }326 327  if (!Args.hasArg(options::OPT_flto_partitions_EQ))328    DAL->AddJoinedArg(nullptr, Opts.getOption(options::OPT_flto_partitions_EQ),329                      "8");330 331  return DAL;332}333 334Tool *HIPAMDToolChain::buildLinker() const {335  assert(getTriple().isAMDGCN() ||336         getTriple().getArch() == llvm::Triple::spirv64);337  return new tools::AMDGCN::Linker(*this);338}339 340void HIPAMDToolChain::addClangWarningOptions(ArgStringList &CC1Args) const {341  AMDGPUToolChain::addClangWarningOptions(CC1Args);342  HostTC.addClangWarningOptions(CC1Args);343}344 345ToolChain::CXXStdlibType346HIPAMDToolChain::GetCXXStdlibType(const ArgList &Args) const {347  return HostTC.GetCXXStdlibType(Args);348}349 350void HIPAMDToolChain::AddClangSystemIncludeArgs(const ArgList &DriverArgs,351                                                ArgStringList &CC1Args) const {352  HostTC.AddClangSystemIncludeArgs(DriverArgs, CC1Args);353}354 355void HIPAMDToolChain::AddClangCXXStdlibIncludeArgs(356    const ArgList &Args, ArgStringList &CC1Args) const {357  HostTC.AddClangCXXStdlibIncludeArgs(Args, CC1Args);358}359 360void HIPAMDToolChain::AddIAMCUIncludeArgs(const ArgList &Args,361                                          ArgStringList &CC1Args) const {362  HostTC.AddIAMCUIncludeArgs(Args, CC1Args);363}364 365void HIPAMDToolChain::AddHIPIncludeArgs(const ArgList &DriverArgs,366                                        ArgStringList &CC1Args) const {367  RocmInstallation->AddHIPIncludeArgs(DriverArgs, CC1Args);368}369 370SanitizerMask HIPAMDToolChain::getSupportedSanitizers() const {371  // The HIPAMDToolChain only supports sanitizers in the sense that it allows372  // sanitizer arguments on the command line if they are supported by the host373  // toolchain. The HIPAMDToolChain will later filter unsupported sanitizers374  // from the command line arguments.375  //376  // This behavior is necessary because the host and device toolchains377  // invocations often share the command line, so the device toolchain must378  // tolerate flags meant only for the host toolchain.379  return HostTC.getSupportedSanitizers();380}381 382VersionTuple HIPAMDToolChain::computeMSVCVersion(const Driver *D,383                                                 const ArgList &Args) const {384  return HostTC.computeMSVCVersion(D, Args);385}386 387llvm::SmallVector<ToolChain::BitCodeLibraryInfo, 12>388HIPAMDToolChain::getDeviceLibs(const llvm::opt::ArgList &DriverArgs,389                               Action::OffloadKind DeviceOffloadingKind) const {390  llvm::SmallVector<BitCodeLibraryInfo, 12> BCLibs;391  if (!DriverArgs.hasFlag(options::OPT_offloadlib, options::OPT_no_offloadlib,392                          true) ||393      getGPUArch(DriverArgs) == "amdgcnspirv")394    return {};395  ArgStringList LibraryPaths;396 397  // Find in --hip-device-lib-path and HIP_LIBRARY_PATH.398  for (StringRef Path : RocmInstallation->getRocmDeviceLibPathArg())399    LibraryPaths.push_back(DriverArgs.MakeArgString(Path));400 401  addDirectoryList(DriverArgs, LibraryPaths, "", "HIP_DEVICE_LIB_PATH");402 403  // Maintain compatability with --hip-device-lib.404  auto BCLibArgs = DriverArgs.getAllArgValues(options::OPT_hip_device_lib_EQ);405  if (!BCLibArgs.empty()) {406    for (StringRef BCName : BCLibArgs) {407      StringRef FullName;408      bool Found = false;409      for (StringRef LibraryPath : LibraryPaths) {410        SmallString<128> Path(LibraryPath);411        llvm::sys::path::append(Path, BCName);412        FullName = Path;413        if (llvm::sys::fs::exists(FullName)) {414          BCLibs.emplace_back(FullName);415          Found = true;416          break;417        }418      }419      if (!Found)420        getDriver().Diag(diag::err_drv_no_such_file) << BCName;421    }422  } else {423    if (!RocmInstallation->hasDeviceLibrary()) {424      getDriver().Diag(diag::err_drv_no_rocm_device_lib) << 0;425      return {};426    }427    StringRef GpuArch = getGPUArch(DriverArgs);428    assert(!GpuArch.empty() && "Must have an explicit GPU arch.");429 430    // Add common device libraries like ocml etc.431    for (auto N : getCommonDeviceLibNames(DriverArgs, GpuArch.str(),432                                          DeviceOffloadingKind))433      BCLibs.emplace_back(N);434 435    // Add instrument lib.436    auto InstLib =437        DriverArgs.getLastArgValue(options::OPT_gpu_instrument_lib_EQ);438    if (InstLib.empty())439      return BCLibs;440    if (llvm::sys::fs::exists(InstLib))441      BCLibs.emplace_back(InstLib);442    else443      getDriver().Diag(diag::err_drv_no_such_file) << InstLib;444  }445 446  return BCLibs;447}448 449void HIPAMDToolChain::checkTargetID(450    const llvm::opt::ArgList &DriverArgs) const {451  auto PTID = getParsedTargetID(DriverArgs);452  if (PTID.OptionalTargetID && !PTID.OptionalGPUArch &&453      PTID.OptionalTargetID != "amdgcnspirv")454    getDriver().Diag(clang::diag::err_drv_bad_target_id)455        << *PTID.OptionalTargetID;456}457 458SPIRVAMDToolChain::SPIRVAMDToolChain(const Driver &D,459                                     const llvm::Triple &Triple,460                                     const ArgList &Args)461    : ROCMToolChain(D, Triple, Args) {462  getProgramPaths().push_back(getDriver().Dir);463}464 465Tool *SPIRVAMDToolChain::buildLinker() const {466  assert(getTriple().getArch() == llvm::Triple::spirv64);467  return new tools::AMDGCN::Linker(*this);468}469