brintos

brintos / llvm-project-archived public Read only

0
0
Text · 18.9 KiB · 5d7221b Raw
574 lines · cpp
1//===--- HLSL.cpp - HLSL 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 "HLSL.h"10#include "clang/Driver/CommonArgs.h"11#include "clang/Driver/Compilation.h"12#include "clang/Driver/Job.h"13#include "llvm/ADT/StringSwitch.h"14#include "llvm/TargetParser/Triple.h"15#include <regex>16 17using namespace clang::driver;18using namespace clang::driver::tools;19using namespace clang::driver::toolchains;20using namespace clang;21using namespace llvm::opt;22using namespace llvm;23 24namespace {25 26const unsigned OfflineLibMinor = 0xF;27 28bool isLegalShaderModel(Triple &T) {29  if (T.getOS() != Triple::OSType::ShaderModel)30    return false;31 32  auto Version = T.getOSVersion();33  if (Version.getBuild())34    return false;35  if (Version.getSubminor())36    return false;37 38  auto Kind = T.getEnvironment();39 40  switch (Kind) {41  default:42    return false;43  case Triple::EnvironmentType::Vertex:44  case Triple::EnvironmentType::Hull:45  case Triple::EnvironmentType::Domain:46  case Triple::EnvironmentType::Geometry:47  case Triple::EnvironmentType::Pixel:48  case Triple::EnvironmentType::Compute: {49    VersionTuple MinVer(4, 0);50    return MinVer <= Version;51  } break;52  case Triple::EnvironmentType::Library: {53    VersionTuple SM6x(6, OfflineLibMinor);54    if (Version == SM6x)55      return true;56 57    VersionTuple MinVer(6, 3);58    return MinVer <= Version;59  } break;60  case Triple::EnvironmentType::Amplification:61  case Triple::EnvironmentType::Mesh: {62    VersionTuple MinVer(6, 5);63    return MinVer <= Version;64  } break;65  case Triple::EnvironmentType::RootSignature:66    VersionTuple MinVer(1, 0);67    VersionTuple MaxVer(1, 2);68    return MinVer <= Version && Version <= MaxVer;69  }70  return false;71}72 73std::optional<llvm::Triple> tryParseTriple(StringRef Profile) {74  // [ps|vs|gs|hs|ds|cs|ms|as]_[major]_[minor]75  SmallVector<StringRef, 3> Parts;76  Profile.split(Parts, "_");77  if (Parts.size() != 3)78    return std::nullopt;79 80  Triple::EnvironmentType Kind =81      StringSwitch<Triple::EnvironmentType>(Parts[0])82          .Case("ps", Triple::EnvironmentType::Pixel)83          .Case("vs", Triple::EnvironmentType::Vertex)84          .Case("gs", Triple::EnvironmentType::Geometry)85          .Case("hs", Triple::EnvironmentType::Hull)86          .Case("ds", Triple::EnvironmentType::Domain)87          .Case("cs", Triple::EnvironmentType::Compute)88          .Case("lib", Triple::EnvironmentType::Library)89          .Case("ms", Triple::EnvironmentType::Mesh)90          .Case("as", Triple::EnvironmentType::Amplification)91          .Case("rootsig", Triple::EnvironmentType::RootSignature)92          .Default(Triple::EnvironmentType::UnknownEnvironment);93  if (Kind == Triple::EnvironmentType::UnknownEnvironment)94    return std::nullopt;95 96  unsigned long long Major = 0;97  if (llvm::getAsUnsignedInteger(Parts[1], 0, Major))98    return std::nullopt;99 100  unsigned long long Minor = 0;101  if (Parts[2] == "x" && Kind == Triple::EnvironmentType::Library)102    Minor = OfflineLibMinor;103  else if (llvm::getAsUnsignedInteger(Parts[2], 0, Minor))104    return std::nullopt;105 106  // Determine DXIL version using the minor version number of Shader107  // Model version specified in target profile. Prior to decoupling DXIL version108  // numbering from that of Shader Model DXIL version 1.Y corresponds to SM 6.Y.109  // E.g., dxilv1.Y-unknown-shadermodelX.Y-hull110  llvm::Triple T;111  Triple::SubArchType SubArch = llvm::Triple::NoSubArch;112  switch (Minor) {113  case 0:114    SubArch = llvm::Triple::DXILSubArch_v1_0;115    break;116  case 1:117    SubArch = llvm::Triple::DXILSubArch_v1_1;118    break;119  case 2:120    SubArch = llvm::Triple::DXILSubArch_v1_2;121    break;122  case 3:123    SubArch = llvm::Triple::DXILSubArch_v1_3;124    break;125  case 4:126    SubArch = llvm::Triple::DXILSubArch_v1_4;127    break;128  case 5:129    SubArch = llvm::Triple::DXILSubArch_v1_5;130    break;131  case 6:132    SubArch = llvm::Triple::DXILSubArch_v1_6;133    break;134  case 7:135    SubArch = llvm::Triple::DXILSubArch_v1_7;136    break;137  case 8:138    SubArch = llvm::Triple::DXILSubArch_v1_8;139    break;140  case 9:141    SubArch = llvm::Triple::DXILSubArch_v1_9;142    break;143  case OfflineLibMinor:144    // Always consider minor version x as the latest supported DXIL version145    SubArch = llvm::Triple::LatestDXILSubArch;146    break;147  default:148    // No DXIL Version corresponding to specified Shader Model version found149    return std::nullopt;150  }151  T.setArch(Triple::ArchType::dxil, SubArch);152  T.setOSName(Triple::getOSTypeName(Triple::OSType::ShaderModel).str() +153              VersionTuple(Major, Minor).getAsString());154  T.setEnvironment(Kind);155 156  return T;157}158 159std::optional<std::string> tryParseProfile(StringRef Profile) {160  std::optional<llvm::Triple> MaybeT = tryParseTriple(Profile);161  if (MaybeT && isLegalShaderModel(*MaybeT))162    return MaybeT->getTriple();163  else164    return std::nullopt;165}166 167bool isLegalValidatorVersion(StringRef ValVersionStr, const Driver &D) {168  VersionTuple Version;169  if (Version.tryParse(ValVersionStr) || Version.getBuild() ||170      Version.getSubminor() || !Version.getMinor()) {171    D.Diag(diag::err_drv_invalid_format_dxil_validator_version)172        << ValVersionStr;173    return false;174  }175 176  uint64_t Major = Version.getMajor();177  uint64_t Minor = *Version.getMinor();178  if (Major == 0 && Minor != 0) {179    D.Diag(diag::err_drv_invalid_empty_dxil_validator_version) << ValVersionStr;180    return false;181  }182  VersionTuple MinVer(1, 0);183  if (Version < MinVer) {184    D.Diag(diag::err_drv_invalid_range_dxil_validator_version) << ValVersionStr;185    return false;186  }187  return true;188}189 190void getSpirvExtOperand(StringRef SpvExtensionArg, raw_ostream &out) {191  // The extensions that are commented out are supported in DXC, but the SPIR-V192  // backend does not know about them yet.193  static const std::vector<StringRef> DxcSupportedExtensions = {194      "SPV_KHR_16bit_storage",195      "SPV_KHR_device_group",196      "SPV_KHR_fragment_shading_rate",197      "SPV_KHR_multiview",198      "SPV_KHR_post_depth_coverage",199      "SPV_KHR_non_semantic_info",200      "SPV_KHR_shader_draw_parameters",201      "SPV_KHR_ray_tracing",202      "SPV_KHR_shader_clock",203      "SPV_EXT_demote_to_helper_invocation",204      "SPV_EXT_descriptor_indexing",205      "SPV_EXT_fragment_fully_covered",206      "SPV_EXT_fragment_invocation_density",207      "SPV_EXT_fragment_shader_interlock",208      "SPV_EXT_mesh_shader",209      "SPV_EXT_shader_stencil_export",210      "SPV_EXT_shader_viewport_index_layer",211      // "SPV_AMD_shader_early_and_late_fragment_tests",212      "SPV_GOOGLE_hlsl_functionality1",213      "SPV_GOOGLE_user_type",214      "SPV_KHR_ray_query",215      "SPV_EXT_shader_image_int64",216      "SPV_KHR_fragment_shader_barycentric",217      "SPV_KHR_physical_storage_buffer",218      "SPV_KHR_vulkan_memory_model",219      // "SPV_KHR_compute_shader_derivatives",220      "SPV_KHR_maximal_reconvergence",221      "SPV_KHR_float_controls",222      "SPV_NV_shader_subgroup_partitioned",223      // "SPV_KHR_quad_control"224  };225 226  if (SpvExtensionArg.starts_with("SPV_")) {227    out << "+" << SpvExtensionArg;228    return;229  }230 231  if (SpvExtensionArg.compare_insensitive("DXC") == 0) {232    bool first = true;233    for (StringRef E : DxcSupportedExtensions) {234      if (!first)235        out << ",";236      else237        first = false;238      out << "+" << E;239    }240    return;241  }242  out << SpvExtensionArg;243}244 245SmallString<1024> getSpirvExtArg(ArrayRef<std::string> SpvExtensionArgs) {246  if (SpvExtensionArgs.empty()) {247    return StringRef("-spirv-ext=all");248  }249 250  llvm::SmallString<1024> LlvmOption;251  raw_svector_ostream out(LlvmOption);252 253  out << "-spirv-ext=";254  getSpirvExtOperand(SpvExtensionArgs[0], out);255 256  SpvExtensionArgs = SpvExtensionArgs.slice(1);257  for (StringRef Extension : SpvExtensionArgs) {258    out << ",";259    getSpirvExtOperand(Extension, out);260  }261  return LlvmOption;262}263 264bool isValidSPIRVExtensionName(const std::string &str) {265  std::regex pattern("dxc|DXC|khr|KHR|SPV_[a-zA-Z0-9_]+");266  return std::regex_match(str, pattern);267}268 269// SPIRV extension names are of the form `SPV_[a-zA-Z0-9_]+`. We want to270// disallow obviously invalid names to avoid issues when parsing `spirv-ext`.271bool checkExtensionArgsAreValid(ArrayRef<std::string> SpvExtensionArgs,272                                const Driver &Driver) {273  bool AllValid = true;274  for (auto Extension : SpvExtensionArgs) {275    if (!isValidSPIRVExtensionName(Extension)) {276      Driver.Diag(diag::err_drv_invalid_value)277          << "-fspv-extension" << Extension;278      AllValid = false;279    }280  }281  return AllValid;282}283 284bool isRootSignatureTarget(StringRef Profile) {285  if (std::optional<llvm::Triple> T = tryParseTriple(Profile))286    return T->getEnvironment() == Triple::EnvironmentType::RootSignature;287  return false;288}289 290bool isRootSignatureTarget(DerivedArgList &Args) {291  if (const Arg *A = Args.getLastArg(options::OPT_target_profile))292    return isRootSignatureTarget(A->getValue());293  return false;294}295 296} // namespace297 298void tools::hlsl::Validator::ConstructJob(Compilation &C, const JobAction &JA,299                                          const InputInfo &Output,300                                          const InputInfoList &Inputs,301                                          const ArgList &Args,302                                          const char *LinkingOutput) const {303  std::string DxvPath = getToolChain().GetProgramPath("dxv");304  assert(DxvPath != "dxv" && "cannot find dxv");305 306  ArgStringList CmdArgs;307  assert(Inputs.size() == 1 && "Unable to handle multiple inputs.");308  const InputInfo &Input = Inputs[0];309  CmdArgs.push_back(Input.getFilename());310  CmdArgs.push_back("-o");311  CmdArgs.push_back(Output.getFilename());312 313  const char *Exec = Args.MakeArgString(DxvPath);314  C.addCommand(std::make_unique<Command>(JA, *this, ResponseFileSupport::None(),315                                         Exec, CmdArgs, Inputs, Input));316}317 318void tools::hlsl::MetalConverter::ConstructJob(319    Compilation &C, const JobAction &JA, const InputInfo &Output,320    const InputInfoList &Inputs, const ArgList &Args,321    const char *LinkingOutput) const {322  std::string MSCPath = getToolChain().GetProgramPath("metal-shaderconverter");323  ArgStringList CmdArgs;324  assert(Inputs.size() == 1 && "Unable to handle multiple inputs.");325  const InputInfo &Input = Inputs[0];326  CmdArgs.push_back(Input.getFilename());327  CmdArgs.push_back("-o");328  CmdArgs.push_back(Output.getFilename());329 330  const char *Exec = Args.MakeArgString(MSCPath);331  C.addCommand(std::make_unique<Command>(JA, *this, ResponseFileSupport::None(),332                                         Exec, CmdArgs, Inputs, Input));333}334 335void tools::hlsl::LLVMObjcopy::ConstructJob(Compilation &C, const JobAction &JA,336                                            const InputInfo &Output,337                                            const InputInfoList &Inputs,338                                            const ArgList &Args,339                                            const char *LinkingOutput) const {340 341  std::string ObjcopyPath = getToolChain().GetProgramPath("llvm-objcopy");342  const char *Exec = Args.MakeArgString(ObjcopyPath);343 344  ArgStringList CmdArgs;345  assert(Inputs.size() == 1 && "Unable to handle multiple inputs.");346  const InputInfo &Input = Inputs[0];347  CmdArgs.push_back(Input.getFilename());348  CmdArgs.push_back(Output.getFilename());349 350  if (Args.hasArg(options::OPT_dxc_strip_rootsignature)) {351    const char *StripRS = Args.MakeArgString("--remove-section=RTS0");352    CmdArgs.push_back(StripRS);353  }354 355  if (Arg *Arg = Args.getLastArg(options::OPT_dxc_Frs)) {356    const char *Frs =357        Args.MakeArgString("--extract-section=RTS0=" + Twine(Arg->getValue()));358    CmdArgs.push_back(Frs);359  }360 361  if (const Arg *A = Args.getLastArg(options::OPT_target_profile))362    if (isRootSignatureTarget(A->getValue())) {363      const char *Fos = Args.MakeArgString("--only-section=RTS0");364      CmdArgs.push_back(Fos);365    }366 367  assert(CmdArgs.size() > 2 && "Unnecessary invocation of objcopy.");368 369  C.addCommand(std::make_unique<Command>(JA, *this, ResponseFileSupport::None(),370                                         Exec, CmdArgs, Inputs, Input));371}372 373/// DirectX Toolchain374HLSLToolChain::HLSLToolChain(const Driver &D, const llvm::Triple &Triple,375                             const ArgList &Args)376    : ToolChain(D, Triple, Args) {377  if (Args.hasArg(options::OPT_dxc_validator_path_EQ))378    getProgramPaths().push_back(379        Args.getLastArgValue(options::OPT_dxc_validator_path_EQ).str());380}381 382Tool *clang::driver::toolchains::HLSLToolChain::getTool(383    Action::ActionClass AC) const {384  switch (AC) {385  case Action::BinaryAnalyzeJobClass:386    if (!Validator)387      Validator.reset(new tools::hlsl::Validator(*this));388    return Validator.get();389  case Action::BinaryTranslatorJobClass:390    if (!MetalConverter)391      MetalConverter.reset(new tools::hlsl::MetalConverter(*this));392    return MetalConverter.get();393  case Action::ObjcopyJobClass:394    if (!LLVMObjcopy)395      LLVMObjcopy.reset(new tools::hlsl::LLVMObjcopy(*this));396    return LLVMObjcopy.get();397  default:398    return ToolChain::getTool(AC);399  }400}401 402std::optional<std::string>403clang::driver::toolchains::HLSLToolChain::parseTargetProfile(404    StringRef TargetProfile) {405  return tryParseProfile(TargetProfile);406}407 408DerivedArgList *409HLSLToolChain::TranslateArgs(const DerivedArgList &Args, StringRef BoundArch,410                             Action::OffloadKind DeviceOffloadKind) const {411  DerivedArgList *DAL = new DerivedArgList(Args.getBaseArgs());412 413  const OptTable &Opts = getDriver().getOpts();414 415  for (Arg *A : Args) {416    if (A->getOption().getID() == options::OPT_dxil_validator_version) {417      StringRef ValVerStr = A->getValue();418      if (!isLegalValidatorVersion(ValVerStr, getDriver()))419        continue;420    }421    if (A->getOption().getID() == options::OPT_dxc_entrypoint) {422      DAL->AddSeparateArg(nullptr, Opts.getOption(options::OPT_hlsl_entrypoint),423                          A->getValue());424      A->claim();425      continue;426    }427    if (A->getOption().getID() == options::OPT_dxc_rootsig_ver) {428      DAL->AddJoinedArg(nullptr,429                        Opts.getOption(options::OPT_fdx_rootsignature_version),430                        A->getValue());431      A->claim();432      continue;433    }434    if (A->getOption().getID() == options::OPT_dxc_rootsig_define) {435      DAL->AddJoinedArg(nullptr,436                        Opts.getOption(options::OPT_fdx_rootsignature_define),437                        A->getValue());438      A->claim();439      continue;440    }441    if (A->getOption().getID() == options::OPT__SLASH_O) {442      StringRef OStr = A->getValue();443      if (OStr == "d") {444        DAL->AddFlagArg(nullptr, Opts.getOption(options::OPT_O0));445        A->claim();446        continue;447      } else {448        DAL->AddJoinedArg(nullptr, Opts.getOption(options::OPT_O), OStr);449        A->claim();450        continue;451      }452    }453    if (A->getOption().getID() == options::OPT_emit_pristine_llvm) {454      // Translate -fcgl into -emit-llvm and -disable-llvm-passes.455      DAL->AddFlagArg(nullptr, Opts.getOption(options::OPT_emit_llvm));456      DAL->AddFlagArg(nullptr,457                      Opts.getOption(options::OPT_disable_llvm_passes));458      A->claim();459      continue;460    }461    if (A->getOption().getID() == options::OPT_dxc_hlsl_version) {462      // Translate -HV into -std for llvm463      // depending on the value given464      LangStandard::Kind LangStd = LangStandard::getHLSLLangKind(A->getValue());465      if (LangStd != LangStandard::lang_unspecified) {466        LangStandard l = LangStandard::getLangStandardForKind(LangStd);467        DAL->AddSeparateArg(nullptr, Opts.getOption(options::OPT_std_EQ),468                            l.getName());469      } else {470        getDriver().Diag(diag::err_drv_invalid_value) << "HV" << A->getValue();471      }472 473      A->claim();474      continue;475    }476    if (A->getOption().getID() == options::OPT_dxc_gis) {477      // Translate -Gis into -ffp_model_EQ=strict478      DAL->AddSeparateArg(nullptr, Opts.getOption(options::OPT_ffp_model_EQ),479                          "strict");480      A->claim();481      continue;482    }483    if (A->getOption().getID() == options::OPT_fvk_use_dx_layout) {484      // This is the only implemented layout so far.485      A->claim();486      continue;487    }488 489    if (A->getOption().getID() == options::OPT_fvk_use_scalar_layout) {490      getDriver().Diag(diag::err_drv_clang_unsupported) << A->getAsString(Args);491      A->claim();492      continue;493    }494 495    if (A->getOption().getID() == options::OPT_fvk_use_gl_layout) {496      getDriver().Diag(diag::err_drv_clang_unsupported) << A->getAsString(Args);497      A->claim();498      continue;499    }500 501    if (A->getOption().getID() == options::OPT_enable_16bit_types) {502      // Translate -enable-16bit-types into -fnative-half-type and503      // -fnative-int16-type504      DAL->AddFlagArg(nullptr, Opts.getOption(options::OPT_fnative_half_type));505      DAL->AddFlagArg(nullptr, Opts.getOption(options::OPT_fnative_int16_type));506      A->claim();507      continue;508    }509 510    DAL->append(A);511  }512 513  if (getArch() == llvm::Triple::spirv) {514    std::vector<std::string> SpvExtensionArgs =515        Args.getAllArgValues(options::OPT_fspv_extension_EQ);516    if (checkExtensionArgsAreValid(SpvExtensionArgs, getDriver())) {517      SmallString<1024> LlvmOption = getSpirvExtArg(SpvExtensionArgs);518      DAL->AddSeparateArg(nullptr, Opts.getOption(options::OPT_mllvm),519                          LlvmOption);520    }521    Args.claimAllArgs(options::OPT_fspv_extension_EQ);522  }523 524  if (!DAL->hasArg(options::OPT_O_Group)) {525    DAL->AddJoinedArg(nullptr, Opts.getOption(options::OPT_O), "3");526  }527 528  return DAL;529}530 531bool HLSLToolChain::requiresValidation(DerivedArgList &Args) const {532  if (!Args.hasArg(options::OPT_dxc_Fo))533    return false;534 535  if (Args.getLastArg(options::OPT_dxc_disable_validation))536    return false;537 538  std::string DxvPath = GetProgramPath("dxv");539  if (DxvPath != "dxv")540    return true;541 542  getDriver().Diag(diag::warn_drv_dxc_missing_dxv);543  return false;544}545 546bool HLSLToolChain::requiresBinaryTranslation(DerivedArgList &Args) const {547  return Args.hasArg(options::OPT_metal) && Args.hasArg(options::OPT_dxc_Fo);548}549 550bool HLSLToolChain::requiresObjcopy(DerivedArgList &Args) const {551  return Args.hasArg(options::OPT_dxc_Fo) &&552         (Args.hasArg(options::OPT_dxc_strip_rootsignature) ||553          Args.hasArg(options::OPT_dxc_Frs) || isRootSignatureTarget(Args));554}555 556bool HLSLToolChain::isLastJob(DerivedArgList &Args,557                              Action::ActionClass AC) const {558  // Note: we check in the reverse order of execution559  if (requiresBinaryTranslation(Args))560    return AC == Action::Action::BinaryTranslatorJobClass;561  if (requiresValidation(Args))562    return AC == Action::Action::BinaryAnalyzeJobClass;563  if (requiresObjcopy(Args))564    return AC == Action::Action::ObjcopyJobClass;565 566  // No translation, validation, or objcopy are required, so this action must567  // output to the result file.568  return true;569}570 571void HLSLToolChain::addClangWarningOptions(ArgStringList &CC1Args) const {572  CC1Args.push_back("-Wconversion");573}574