#include "HLSL.h"
#include "CommonArgs.h"
#include "clang/Driver/DriverDiagnostic.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/ADT/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;
}
llvm::Optional<std::string> tryParseProfile(StringRef Profile) {
SmallVector<StringRef, 3> Parts;
Profile.split(Parts, "_");
if (Parts.size() != 3)
return NoneType();
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 NoneType();
unsigned long long Major = 0;
if (llvm::getAsUnsignedInteger(Parts[1], 0, Major))
return NoneType();
unsigned long long Minor = 0;
if (Parts[2] == "x" && Kind == Triple::EnvironmentType::Library)
Minor = OfflineLibMinor;
else if (llvm::getAsUnsignedInteger(Parts[2], 0, Minor))
return NoneType();
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 NoneType();
}
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;
}
}
HLSLToolChain::HLSLToolChain(const Driver &D, const llvm::Triple &Triple,
const ArgList &Args)
: ToolChain(D, Triple, Args) {}
llvm::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_emit_pristine_llvm) {
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;
}
DAL->append(A);
}
if (!DAL->hasArg(options::OPT_dxil_validator_version)) {
const StringRef DefaultValidatorVer = "1.7";
DAL->AddSeparateArg(nullptr,
Opts.getOption(options::OPT_dxil_validator_version),
DefaultValidatorVer);
}
return DAL;
}