| //===--- HLSL.cpp - HLSL ToolChain Implementations --------------*- C++ -*-===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "HLSL.h" |
| #include "CommonArgs.h" |
| #include "clang/Driver/Compilation.h" |
| #include "clang/Driver/DriverDiagnostic.h" |
| #include "clang/Driver/Job.h" |
| #include "llvm/ADT/StringSwitch.h" |
| #include "llvm/TargetParser/Triple.h" |
| |
| using namespace clang::driver; |
| using namespace clang::driver::tools; |
| using namespace clang::driver::toolchains; |
| using namespace clang; |
| using namespace llvm::opt; |
| using namespace llvm; |
| |
| namespace { |
| |
| const unsigned OfflineLibMinor = 0xF; |
| |
| bool isLegalShaderModel(Triple &T) { |
| if (T.getOS() != Triple::OSType::ShaderModel) |
| return false; |
| |
| auto Version = T.getOSVersion(); |
| if (Version.getBuild()) |
| return false; |
| if (Version.getSubminor()) |
| return false; |
| |
| auto Kind = T.getEnvironment(); |
| |
| switch (Kind) { |
| default: |
| return false; |
| case Triple::EnvironmentType::Vertex: |
| case Triple::EnvironmentType::Hull: |
| case Triple::EnvironmentType::Domain: |
| case Triple::EnvironmentType::Geometry: |
| case Triple::EnvironmentType::Pixel: |
| case Triple::EnvironmentType::Compute: { |
| VersionTuple MinVer(4, 0); |
| return MinVer <= Version; |
| } break; |
| case Triple::EnvironmentType::Library: { |
| VersionTuple SM6x(6, OfflineLibMinor); |
| if (Version == SM6x) |
| return true; |
| |
| VersionTuple MinVer(6, 3); |
| return MinVer <= Version; |
| } break; |
| case Triple::EnvironmentType::Amplification: |
| case Triple::EnvironmentType::Mesh: { |
| VersionTuple MinVer(6, 5); |
| return MinVer <= Version; |
| } break; |
| } |
| return false; |
| } |
| |
| std::optional<std::string> tryParseProfile(StringRef Profile) { |
| // [ps|vs|gs|hs|ds|cs|ms|as]_[major]_[minor] |
| SmallVector<StringRef, 3> Parts; |
| Profile.split(Parts, "_"); |
| if (Parts.size() != 3) |
| return std::nullopt; |
| |
| Triple::EnvironmentType Kind = |
| StringSwitch<Triple::EnvironmentType>(Parts[0]) |
| .Case("ps", Triple::EnvironmentType::Pixel) |
| .Case("vs", Triple::EnvironmentType::Vertex) |
| .Case("gs", Triple::EnvironmentType::Geometry) |
| .Case("hs", Triple::EnvironmentType::Hull) |
| .Case("ds", Triple::EnvironmentType::Domain) |
| .Case("cs", Triple::EnvironmentType::Compute) |
| .Case("lib", Triple::EnvironmentType::Library) |
| .Case("ms", Triple::EnvironmentType::Mesh) |
| .Case("as", Triple::EnvironmentType::Amplification) |
| .Default(Triple::EnvironmentType::UnknownEnvironment); |
| if (Kind == Triple::EnvironmentType::UnknownEnvironment) |
| return std::nullopt; |
| |
| unsigned long long Major = 0; |
| if (llvm::getAsUnsignedInteger(Parts[1], 0, Major)) |
| return std::nullopt; |
| |
| unsigned long long Minor = 0; |
| if (Parts[2] == "x" && Kind == Triple::EnvironmentType::Library) |
| Minor = OfflineLibMinor; |
| else if (llvm::getAsUnsignedInteger(Parts[2], 0, Minor)) |
| return std::nullopt; |
| |
| // dxil-unknown-shadermodel-hull |
| llvm::Triple T; |
| T.setArch(Triple::ArchType::dxil); |
| T.setOSName(Triple::getOSTypeName(Triple::OSType::ShaderModel).str() + |
| VersionTuple(Major, Minor).getAsString()); |
| T.setEnvironment(Kind); |
| if (isLegalShaderModel(T)) |
| return T.getTriple(); |
| else |
| return std::nullopt; |
| } |
| |
| bool isLegalValidatorVersion(StringRef ValVersionStr, const Driver &D) { |
| VersionTuple Version; |
| if (Version.tryParse(ValVersionStr) || Version.getBuild() || |
| Version.getSubminor() || !Version.getMinor()) { |
| D.Diag(diag::err_drv_invalid_format_dxil_validator_version) |
| << ValVersionStr; |
| return false; |
| } |
| |
| uint64_t Major = Version.getMajor(); |
| uint64_t Minor = *Version.getMinor(); |
| if (Major == 0 && Minor != 0) { |
| D.Diag(diag::err_drv_invalid_empty_dxil_validator_version) << ValVersionStr; |
| return false; |
| } |
| VersionTuple MinVer(1, 0); |
| if (Version < MinVer) { |
| D.Diag(diag::err_drv_invalid_range_dxil_validator_version) << ValVersionStr; |
| return false; |
| } |
| return true; |
| } |
| |
| } // namespace |
| |
| void tools::hlsl::Validator::ConstructJob(Compilation &C, const JobAction &JA, |
| const InputInfo &Output, |
| const InputInfoList &Inputs, |
| const ArgList &Args, |
| const char *LinkingOutput) const { |
| std::string DxvPath = getToolChain().GetProgramPath("dxv"); |
| assert(DxvPath != "dxv" && "cannot find dxv"); |
| |
| ArgStringList CmdArgs; |
| assert(Inputs.size() == 1 && "Unable to handle multiple inputs."); |
| const InputInfo &Input = Inputs[0]; |
| assert(Input.isFilename() && "Unexpected verify input"); |
| // Grabbing the output of the earlier cc1 run. |
| CmdArgs.push_back(Input.getFilename()); |
| // Use the same name as output. |
| CmdArgs.push_back("-o"); |
| CmdArgs.push_back(Input.getFilename()); |
| |
| const char *Exec = Args.MakeArgString(DxvPath); |
| C.addCommand(std::make_unique<Command>(JA, *this, ResponseFileSupport::None(), |
| Exec, CmdArgs, Inputs, Input)); |
| } |
| |
| /// DirectX Toolchain |
| HLSLToolChain::HLSLToolChain(const Driver &D, const llvm::Triple &Triple, |
| const ArgList &Args) |
| : ToolChain(D, Triple, Args) { |
| if (Args.hasArg(options::OPT_dxc_validator_path_EQ)) |
| getProgramPaths().push_back( |
| Args.getLastArgValue(options::OPT_dxc_validator_path_EQ).str()); |
| } |
| |
| Tool *clang::driver::toolchains::HLSLToolChain::getTool( |
| Action::ActionClass AC) const { |
| switch (AC) { |
| case Action::BinaryAnalyzeJobClass: |
| if (!Validator) |
| Validator.reset(new tools::hlsl::Validator(*this)); |
| return Validator.get(); |
| default: |
| return ToolChain::getTool(AC); |
| } |
| } |
| |
| std::optional<std::string> |
| clang::driver::toolchains::HLSLToolChain::parseTargetProfile( |
| StringRef TargetProfile) { |
| return tryParseProfile(TargetProfile); |
| } |
| |
| DerivedArgList * |
| HLSLToolChain::TranslateArgs(const DerivedArgList &Args, StringRef BoundArch, |
| Action::OffloadKind DeviceOffloadKind) const { |
| DerivedArgList *DAL = new DerivedArgList(Args.getBaseArgs()); |
| |
| const OptTable &Opts = getDriver().getOpts(); |
| |
| for (Arg *A : Args) { |
| if (A->getOption().getID() == options::OPT_dxil_validator_version) { |
| StringRef ValVerStr = A->getValue(); |
| std::string ErrorMsg; |
| if (!isLegalValidatorVersion(ValVerStr, getDriver())) |
| continue; |
| } |
| if (A->getOption().getID() == options::OPT_dxc_entrypoint) { |
| DAL->AddSeparateArg(nullptr, Opts.getOption(options::OPT_hlsl_entrypoint), |
| A->getValue()); |
| A->claim(); |
| continue; |
| } |
| if (A->getOption().getID() == options::OPT__SLASH_O) { |
| StringRef OStr = A->getValue(); |
| if (OStr == "d") { |
| DAL->AddFlagArg(nullptr, Opts.getOption(options::OPT_O0)); |
| A->claim(); |
| continue; |
| } else { |
| DAL->AddJoinedArg(nullptr, Opts.getOption(options::OPT_O), OStr); |
| A->claim(); |
| continue; |
| } |
| } |
| if (A->getOption().getID() == options::OPT_emit_pristine_llvm) { |
| // Translate fcgl into -S -emit-llvm and -disable-llvm-passes. |
| DAL->AddFlagArg(nullptr, Opts.getOption(options::OPT_S)); |
| DAL->AddFlagArg(nullptr, Opts.getOption(options::OPT_emit_llvm)); |
| DAL->AddFlagArg(nullptr, |
| Opts.getOption(options::OPT_disable_llvm_passes)); |
| A->claim(); |
| continue; |
| } |
| if (A->getOption().getID() == options::OPT_dxc_hlsl_version) { |
| // Translate -HV into -std for llvm |
| // depending on the value given |
| LangStandard::Kind LangStd = LangStandard::getHLSLLangKind(A->getValue()); |
| if (LangStd != LangStandard::lang_unspecified) { |
| LangStandard l = LangStandard::getLangStandardForKind(LangStd); |
| DAL->AddSeparateArg(nullptr, Opts.getOption(options::OPT_std_EQ), |
| l.getName()); |
| } else { |
| getDriver().Diag(diag::err_drv_invalid_value) << "HV" << A->getValue(); |
| } |
| |
| A->claim(); |
| continue; |
| } |
| DAL->append(A); |
| } |
| |
| // Add default validator version if not set. |
| // TODO: remove this once read validator version from validator. |
| if (!DAL->hasArg(options::OPT_dxil_validator_version)) { |
| const StringRef DefaultValidatorVer = "1.7"; |
| DAL->AddSeparateArg(nullptr, |
| Opts.getOption(options::OPT_dxil_validator_version), |
| DefaultValidatorVer); |
| } |
| if (!DAL->hasArg(options::OPT_O_Group)) { |
| DAL->AddJoinedArg(nullptr, Opts.getOption(options::OPT_O), "3"); |
| } |
| |
| return DAL; |
| } |
| |
| bool HLSLToolChain::requiresValidation(DerivedArgList &Args) const { |
| if (Args.getLastArg(options::OPT_dxc_disable_validation)) |
| return false; |
| |
| std::string DxvPath = GetProgramPath("dxv"); |
| if (DxvPath != "dxv") |
| return true; |
| |
| getDriver().Diag(diag::warn_drv_dxc_missing_dxv); |
| return false; |
| } |