Skip to content

Commit 10b3328

Browse files
Add Preview Hashing sentinel value to dxil validator (microsoft#7053)
When the dxil validator detects that the target shader model is a preview shader model that hasn't officially been released, it should sign the dxil container with a special sentinel hash: 02020202020202020202020202020202 Fixes microsoft#7083 --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
1 parent 25faa88 commit 10b3328

File tree

9 files changed

+250
-23
lines changed

9 files changed

+250
-23
lines changed

include/dxc/DXIL/DxilShaderModel.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ class ShaderModel {
2626
public:
2727
using Kind = DXIL::ShaderKind;
2828

29-
// Major/Minor version of highest shader model
29+
// Major/Minor version of highest recognized shader model
3030
// clang-format off
3131
// Python lines need to be not formatted.
3232
/* <py::lines('VALRULE-TEXT')>hctdb_instrhelp.get_highest_shader_model()</py>*/
@@ -35,6 +35,15 @@ class ShaderModel {
3535
static const unsigned kHighestMajor = 6;
3636
static const unsigned kHighestMinor = 9;
3737
// VALRULE-TEXT:END
38+
39+
// Major/Minor version of highest released shader model
40+
/* <py::lines('VALRULE-TEXT')>hctdb_instrhelp.get_highest_released_shader_model()</py>*/
41+
// clang-format on
42+
// VALRULE-TEXT:BEGIN
43+
static const unsigned kHighestReleasedMajor = 6;
44+
static const unsigned kHighestReleasedMinor = 8;
45+
// VALRULE-TEXT:END
46+
3847
static const unsigned kOfflineMinor = 0xF;
3948

4049
bool IsPS() const { return m_Kind == Kind::Pixel; }
@@ -86,6 +95,8 @@ class ShaderModel {
8695
static const ShaderModel *Get(Kind Kind, unsigned Major, unsigned Minor);
8796
static const ShaderModel *GetByName(llvm::StringRef Name);
8897
static const char *GetKindName(Kind kind);
98+
static bool IsPreReleaseShaderModel(int Major, int Minor);
99+
static Kind GetKindFromName(llvm::StringRef Name);
89100
static DXIL::ShaderKind KindFromFullName(llvm::StringRef Name);
90101
static const llvm::StringRef FullNameFromKind(DXIL::ShaderKind sk);
91102
static const char *GetNodeLaunchTypeName(DXIL::NodeLaunchType launchTy);

include/dxc/DxilContainer/DxilContainer.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ struct DxilContainerHash {
3636
uint8_t Digest[DxilContainerHashSize];
3737
};
3838

39+
static const DxilContainerHash PreviewByPassHash = {2, 2, 2, 2, 2, 2, 2, 2,
40+
2, 2, 2, 2, 2, 2, 2, 2};
41+
3942
enum class DxilShaderHashFlags : uint32_t {
4043
None = 0, // No flags defined.
4144
IncludesSource = 1, // This flag indicates that the shader hash was computed

include/dxc/Support/ErrorCodes.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,3 +153,8 @@
153153
// 0X80AA001E - External validator (DXIL.dll) required, and missing.
154154
#define DXC_E_VALIDATOR_MISSING \
155155
DXC_MAKE_HRESULT(DXC_SEVERITY_ERROR, FACILITY_DXC, (0x001E))
156+
157+
// 0X80AA001F - DXIL container Program Version mismatches Dxil module shader
158+
// model
159+
#define DXC_E_INCORRECT_PROGRAM_VERSION \
160+
DXC_MAKE_HRESULT(DXC_SEVERITY_ERROR, FACILITY_DXC, (0x001F))

lib/DXIL/DxilShaderModel.cpp

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -193,11 +193,21 @@ const ShaderModel *ShaderModel::Get(Kind Kind, unsigned Major, unsigned Minor) {
193193
// VALRULE-TEXT:END
194194
}
195195

196-
const ShaderModel *ShaderModel::GetByName(llvm::StringRef Name) {
197-
// [ps|vs|gs|hs|ds|cs|ms|as]_[major]_[minor]
196+
bool ShaderModel::IsPreReleaseShaderModel(int major, int minor) {
197+
if (DXIL::CompareVersions(major, minor, kHighestReleasedMajor,
198+
kHighestReleasedMinor) <= 0)
199+
return false;
200+
201+
// now compare against highest recognized
202+
if (DXIL::CompareVersions(major, minor, kHighestMajor, kHighestMinor) <= 0)
203+
return true;
204+
return false;
205+
}
206+
207+
ShaderModel::Kind ShaderModel::GetKindFromName(llvm::StringRef Name) {
198208
Kind kind;
199209
if (Name.empty()) {
200-
return GetInvalid();
210+
return Kind::Invalid;
201211
}
202212

203213
switch (Name[0]) {
@@ -229,8 +239,17 @@ const ShaderModel *ShaderModel::GetByName(llvm::StringRef Name) {
229239
kind = Kind::Amplification;
230240
break;
231241
default:
232-
return GetInvalid();
242+
return Kind::Invalid;
233243
}
244+
return kind;
245+
}
246+
247+
const ShaderModel *ShaderModel::GetByName(llvm::StringRef Name) {
248+
// [ps|vs|gs|hs|ds|cs|ms|as]_[major]_[minor]
249+
Kind kind = GetKindFromName(Name);
250+
if (kind == Kind::Invalid)
251+
return GetInvalid();
252+
234253
unsigned Idx = 3;
235254
if (kind != Kind::Library) {
236255
if (Name[1] != 's' || Name[2] != '_')

lib/DxilValidation/DxilContainerValidation.cpp

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1033,8 +1033,29 @@ HRESULT ValidateDxilContainerParts(llvm::Module *pModule,
10331033
case DFCC_ResourceDef:
10341034
case DFCC_ShaderStatistics:
10351035
case DFCC_PrivateData:
1036+
break;
10361037
case DFCC_DXIL:
1037-
case DFCC_ShaderDebugInfoDXIL:
1038+
case DFCC_ShaderDebugInfoDXIL: {
1039+
const DxilProgramHeader *pProgramHeader =
1040+
reinterpret_cast<const DxilProgramHeader *>(GetDxilPartData(pPart));
1041+
if (!pProgramHeader)
1042+
continue;
1043+
1044+
int PV = pProgramHeader->ProgramVersion;
1045+
int major = (PV >> 4) & 0xF; // Extract the major version (next 4 bits)
1046+
int minor = PV & 0xF; // Extract the minor version (lowest 4 bits)
1047+
1048+
int moduleMajor = pDxilModule->GetShaderModel()->GetMajor();
1049+
int moduleMinor = pDxilModule->GetShaderModel()->GetMinor();
1050+
if (moduleMajor != major || moduleMinor != minor) {
1051+
ValCtx.EmitFormatError(ValidationRule::SmProgramVersion,
1052+
{std::to_string(major), std::to_string(minor),
1053+
std::to_string(moduleMajor),
1054+
std::to_string(moduleMinor)});
1055+
return DXC_E_INCORRECT_PROGRAM_VERSION;
1056+
}
1057+
continue;
1058+
}
10381059
case DFCC_ShaderDebugName:
10391060
continue;
10401061

tools/clang/tools/dxcvalidator/dxcvalidator.cpp

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "dxc/dxcapi.h"
2121
#include "dxcvalidator.h"
2222

23+
#include "dxc/DXIL/DxilShaderModel.h"
2324
#include "dxc/DxilRootSignature/DxilRootSignature.h"
2425
#include "dxc/Support/FileIOHelper.h"
2526
#include "dxc/Support/Global.h"
@@ -32,7 +33,13 @@
3233
using namespace llvm;
3334
using namespace hlsl;
3435

35-
static void HashAndUpdate(DxilContainerHeader *Container) {
36+
static void HashAndUpdate(DxilContainerHeader *Container, bool isPreRelease) {
37+
if (isPreRelease) {
38+
// If preview bypass is enabled, use the preview hash.
39+
memcpy(Container->Hash.Digest, PreviewByPassHash.Digest,
40+
sizeof(PreviewByPassHash.Digest));
41+
return;
42+
}
3643
// Compute hash and update stored hash.
3744
// Hash the container from this offset to the end.
3845
static const uint32_t DXBCHashStartOffset =
@@ -45,8 +52,26 @@ static void HashAndUpdate(DxilContainerHeader *Container) {
4552

4653
static void HashAndUpdateOrCopy(uint32_t Flags, IDxcBlob *Shader,
4754
IDxcBlob **Hashed) {
55+
bool isPreRelease = false;
56+
const DxilContainerHeader *DxilContainer =
57+
IsDxilContainerLike(Shader->GetBufferPointer(), Shader->GetBufferSize());
58+
if (!DxilContainer)
59+
return;
60+
61+
const DxilProgramHeader *ProgramHeader =
62+
GetDxilProgramHeader(DxilContainer, DFCC_DXIL);
63+
64+
// ProgramHeader may be null here, when hashing a root signature container
65+
if (ProgramHeader) {
66+
int PV = ProgramHeader->ProgramVersion;
67+
int major = (PV >> 4) & 0xF; // Extract the major version (next 4 bits)
68+
int minor = PV & 0xF; // Extract the minor version (lowest 4 bits)
69+
isPreRelease = ShaderModel::IsPreReleaseShaderModel(major, minor);
70+
}
71+
4872
if (Flags & DxcValidatorFlags_InPlaceEdit) {
49-
HashAndUpdate((DxilContainerHeader *)Shader->GetBufferPointer());
73+
HashAndUpdate((DxilContainerHeader *)Shader->GetBufferPointer(),
74+
isPreRelease);
5075
*Hashed = Shader;
5176
Shader->AddRef();
5277
} else {
@@ -55,7 +80,8 @@ static void HashAndUpdateOrCopy(uint32_t Flags, IDxcBlob *Shader,
5580
unsigned long CB;
5681
IFT(HashedBlobStream->Write(Shader->GetBufferPointer(),
5782
Shader->GetBufferSize(), &CB));
58-
HashAndUpdate((DxilContainerHeader *)HashedBlobStream->GetPtr());
83+
HashAndUpdate((DxilContainerHeader *)HashedBlobStream->GetPtr(),
84+
isPreRelease);
5985
IFT(HashedBlobStream.QueryInterface(Hashed));
6086
}
6187
}

tools/clang/unittests/HLSL/ValidationTest.cpp

Lines changed: 131 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include "dxc/Support/FileIOHelper.h"
3232
#include "dxc/Support/Global.h"
3333

34+
#include "dxc/DXIL/DxilShaderModel.h"
3435
#include "dxc/Test/DxcTestUtils.h"
3536
#include "dxc/Test/HlslTestUtils.h"
3637

@@ -300,6 +301,8 @@ class ValidationTest : public ::testing::Test {
300301

301302
TEST_METHOD(ValidateWithHash)
302303
TEST_METHOD(ValidateVersionNotAllowed)
304+
TEST_METHOD(ValidatePreviewBypassHash)
305+
TEST_METHOD(ValidateProgramVersionAgainstDxilModule)
303306
TEST_METHOD(CreateHandleNotAllowedSM66)
304307

305308
TEST_METHOD(AtomicsConsts)
@@ -537,18 +540,10 @@ class ValidationTest : public ::testing::Test {
537540
pLookFors, pReplacements, pErrorMsgs, bRegex);
538541
}
539542

540-
bool RewriteAssemblyToText(IDxcBlobEncoding *pSource, LPCSTR pShaderModel,
541-
LPCWSTR *pArguments, UINT32 argCount,
542-
const DxcDefine *pDefines, UINT32 defineCount,
543-
llvm::ArrayRef<LPCSTR> pLookFors,
544-
llvm::ArrayRef<LPCSTR> pReplacements,
545-
IDxcBlob **pBlob, bool bRegex = false) {
546-
CComPtr<IDxcBlob> pProgram;
547-
std::string disassembly;
548-
if (!CompileSource(pSource, pShaderModel, pArguments, argCount, pDefines,
549-
defineCount, &pProgram))
550-
return false;
551-
DisassembleProgram(pProgram, &disassembly);
543+
void PerformReplacementOnDisassembly(std::string disassembly,
544+
llvm::ArrayRef<LPCSTR> pLookFors,
545+
llvm::ArrayRef<LPCSTR> pReplacements,
546+
IDxcBlob **pBlob, bool bRegex = false) {
552547
for (unsigned i = 0; i < pLookFors.size(); ++i) {
553548
LPCSTR pLookFor = pLookFors[i];
554549
bool bOptional = false;
@@ -605,6 +600,22 @@ class ValidationTest : public ::testing::Test {
605600
}
606601
}
607602
Utf8ToBlob(m_dllSupport, disassembly.c_str(), pBlob);
603+
}
604+
605+
bool RewriteAssemblyToText(IDxcBlobEncoding *pSource, LPCSTR pShaderModel,
606+
LPCWSTR *pArguments, UINT32 argCount,
607+
const DxcDefine *pDefines, UINT32 defineCount,
608+
llvm::ArrayRef<LPCSTR> pLookFors,
609+
llvm::ArrayRef<LPCSTR> pReplacements,
610+
IDxcBlob **pBlob, bool bRegex = false) {
611+
CComPtr<IDxcBlob> pProgram;
612+
std::string disassembly;
613+
if (!CompileSource(pSource, pShaderModel, pArguments, argCount, pDefines,
614+
defineCount, &pProgram))
615+
return false;
616+
DisassembleProgram(pProgram, &disassembly);
617+
PerformReplacementOnDisassembly(disassembly, pLookFors, pReplacements,
618+
pBlob, bRegex);
608619
return true;
609620
}
610621

@@ -4114,7 +4125,7 @@ TEST_F(ValidationTest, ValidatePrintfNotAllowed) {
41144125
}
41154126

41164127
TEST_F(ValidationTest, ValidateWithHash) {
4117-
if (m_ver.SkipDxilVersion(1, 8))
4128+
if (m_ver.SkipDxilVersion(1, ShaderModel::kHighestReleasedMinor))
41184129
return;
41194130
CComPtr<IDxcBlob> pProgram;
41204131
CompileSource("float4 main(float a:A, float b:B) : SV_Target { return 1; }",
@@ -4149,6 +4160,113 @@ TEST_F(ValidationTest, ValidateWithHash) {
41494160
VERIFY_ARE_EQUAL(memcmp(Result, pHeader->Hash.Digest, sizeof(Result)), 0);
41504161
}
41514162

4163+
TEST_F(ValidationTest, ValidatePreviewBypassHash) {
4164+
if (m_ver.SkipDxilVersion(1, ShaderModel::kHighestMinor))
4165+
return;
4166+
// If there is no available pre-release version to test, return
4167+
if (DXIL::CompareVersions(ShaderModel::kHighestMajor,
4168+
ShaderModel::kHighestMinor,
4169+
ShaderModel::kHighestReleasedMajor,
4170+
ShaderModel::kHighestReleasedMinor) <= 0) {
4171+
return;
4172+
}
4173+
4174+
// Now test a pre-release version.
4175+
CComPtr<IDxcBlob> pProgram;
4176+
LPCSTR pSource =
4177+
R"(float4 main(float a:A, float b:B) : SV_Target { return 1; })";
4178+
4179+
CComPtr<IDxcBlobEncoding> pSourceBlob;
4180+
Utf8ToBlob(m_dllSupport, pSource, &pSourceBlob);
4181+
4182+
LPCSTR pShaderModel =
4183+
ShaderModel::Get(ShaderModel::Kind::Pixel, ShaderModel::kHighestMajor,
4184+
ShaderModel::kHighestMinor)
4185+
->GetName();
4186+
4187+
bool result = CompileSource(pSourceBlob, pShaderModel, nullptr, 0, nullptr, 0,
4188+
&pProgram);
4189+
VERIFY_IS_TRUE(result);
4190+
4191+
hlsl::DxilContainerHeader *pHeader =
4192+
(hlsl::DxilContainerHeader *)pProgram->GetBufferPointer();
4193+
4194+
// Should be equal, this proves the hash is set to the preview bypass hash
4195+
// when a prerelease version is used
4196+
VERIFY_ARE_EQUAL(memcmp(&hlsl::PreviewByPassHash, pHeader->Hash.Digest,
4197+
sizeof(hlsl::PreviewByPassHash)),
4198+
0);
4199+
}
4200+
4201+
TEST_F(ValidationTest, ValidateProgramVersionAgainstDxilModule) {
4202+
if (m_ver.SkipDxilVersion(1, 8))
4203+
return;
4204+
4205+
CComPtr<IDxcBlob> pProgram;
4206+
LPCSTR pSource =
4207+
R"(float4 main(float a:A, float b:B) : SV_Target { return 1; })";
4208+
4209+
CComPtr<IDxcBlobEncoding> pSourceBlob;
4210+
Utf8ToBlob(m_dllSupport, pSource, &pSourceBlob);
4211+
4212+
LPCSTR pShaderModel =
4213+
ShaderModel::Get(ShaderModel::Kind::Pixel, 6, 0)->GetName();
4214+
4215+
bool result = CompileSource(pSourceBlob, pShaderModel, nullptr, 0, nullptr, 0,
4216+
&pProgram);
4217+
VERIFY_IS_TRUE(result);
4218+
4219+
hlsl::DxilContainerHeader *pHeader =
4220+
(hlsl::DxilContainerHeader *)pProgram->GetBufferPointer();
4221+
// test that when the program version differs from the dxil module shader
4222+
// model version, the validator fails
4223+
DxilPartHeader *pPart = GetDxilPartByType(pHeader, DxilFourCC::DFCC_DXIL);
4224+
4225+
DxilProgramHeader *pMutableProgramHeader =
4226+
reinterpret_cast<DxilProgramHeader *>(GetDxilPartData(pPart));
4227+
int oldMajor = 0;
4228+
int oldMinor = 0;
4229+
int newMajor = 0;
4230+
int newMinor = 0;
4231+
VERIFY_IS_NOT_NULL(pMutableProgramHeader);
4232+
uint32_t &PV = pMutableProgramHeader->ProgramVersion;
4233+
oldMajor = (PV >> 4) & 0xF; // Extract the major version (next 4 bits)
4234+
oldMinor = PV & 0xF; // Extract the minor version (lowest 4 bits)
4235+
4236+
// Add one to the last bit of the program version, which is 0, because
4237+
// the program version (shader model version) is 6.0, and we want to
4238+
// test that the validation fails when the program version is changed to 6.1
4239+
PV += 1;
4240+
4241+
newMajor = (PV >> 4) & 0xF; // Extract the major version (next 4 bits)
4242+
newMinor = PV & 0xF; // Extract the new minor version (lowest 4 bits)
4243+
4244+
// now test that the validation fails
4245+
CComPtr<IDxcValidator> pValidator;
4246+
CComPtr<IDxcOperationResult> pResult;
4247+
unsigned Flags = 0;
4248+
VERIFY_SUCCEEDED(
4249+
m_dllSupport.CreateInstance(CLSID_DxcValidator, &pValidator));
4250+
4251+
HRESULT status;
4252+
VERIFY_SUCCEEDED(pValidator->Validate(pProgram, Flags, &pResult));
4253+
VERIFY_IS_NOT_NULL(pResult);
4254+
pResult->GetStatus(&status);
4255+
4256+
// expect validation to fail
4257+
VERIFY_FAILED(status);
4258+
// validation succeeded prior, so by inference we know that oldMajor /
4259+
// oldMinor were the old dxil module shader model versions
4260+
char buffer[100];
4261+
std::snprintf(buffer, sizeof(buffer),
4262+
"error: Program Version is %d.%d but Dxil Module shader model "
4263+
"version is %d.%d.\nValidation failed.\n",
4264+
newMajor, newMinor, oldMajor, oldMinor);
4265+
std::string formattedString = buffer;
4266+
4267+
CheckOperationResultMsgs(pResult, {buffer}, false, false);
4268+
}
4269+
41524270
TEST_F(ValidationTest, ValidateVersionNotAllowed) {
41534271
if (m_ver.SkipDxilVersion(1, 6))
41544272
return;

utils/hct/hctdb.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7500,6 +7500,11 @@ def build_valrules(self):
75007500
"Target shader model requires specific Dxil Version",
75017501
"Shader model requires Dxil Version %0.%1.",
75027502
)
7503+
self.add_valrule_msg(
7504+
"Sm.ProgramVersion",
7505+
"Program Version in Dxil Container does not match Dxil Module shader model version",
7506+
"Program Version is %0.%1 but Dxil Module shader model version is %2.%3.",
7507+
)
75037508
self.add_valrule_msg(
75047509
"Sm.Opcode",
75057510
"Opcode must be defined in target shader model",

0 commit comments

Comments
 (0)