blob: 1169b5d8c92dd683495736b429809e1dd452f207 [file] [log] [blame]
//===--- 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;
}