Skip to content

Commit ec7e332

Browse files
authored
PIX: Check SM66 handle types for dynamic indexing (#3819)
While working on another issue I noticed that I had forgotten these cases. (PIX uses this pass to detect non-constant indexing into resource arrays in order to decide if it should run the whole "shader access tracking" pass to check for out-of-range access etc. So it was failing to detect dynamic indexing and so could choose not to run the SAT pass.)
1 parent d7ee5e1 commit ec7e332

3 files changed

Lines changed: 163 additions & 6 deletions

File tree

include/dxc/DXIL/DxilConstants.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1119,6 +1119,9 @@ namespace DXIL {
11191119
const unsigned kCreateHandleFromHeapSamplerHeapOpIdx = 2;
11201120
const unsigned kCreateHandleFromHeapNonUniformIndexOpIdx = 3;
11211121

1122+
// CreateHandleFromBinding
1123+
const unsigned kCreateHandleFromBindingResIndexOpIdx = 2;
1124+
11221125
// TraceRay
11231126
const unsigned kTraceRayRayDescOpIdx = 7;
11241127
const unsigned kTraceRayPayloadOpIdx = 15;

lib/DxilPIXPasses/DxilShaderAccessTracking.cpp

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
#include "dxc/DXIL/DxilOperations.h"
1414

15+
#include "dxc/DXIL/DxilConstants.h"
1516
#include "dxc/DXIL/DxilInstructions.h"
1617
#include "dxc/DXIL/DxilModule.h"
1718
#include "dxc/DXIL/DxilResourceBinding.h"
@@ -33,6 +34,7 @@
3334

3435
using namespace llvm;
3536
using namespace hlsl;
37+
using namespace hlsl::DXIL::OperandIndex;
3638

3739
void ThrowIf(bool a) {
3840
if (a) {
@@ -649,12 +651,35 @@ bool DxilShaderAccessTracking::runOnModule(Module &M) {
649651

650652
auto CreateHandleFn =
651653
HlslOP->GetOpFunc(DXIL::OpCode::CreateHandle, Type::getVoidTy(Ctx));
652-
auto CreateHandleUses = CreateHandleFn->uses();
653-
for (auto FI = CreateHandleUses.begin(); FI != CreateHandleUses.end();) {
654-
auto &FunctionUse = *FI++;
655-
auto FunctionUser = FunctionUse.getUser();
654+
for (auto FI = CreateHandleFn->user_begin(); FI != CreateHandleFn->user_end();) {
655+
auto *FunctionUser = *FI++;
656656
auto instruction = cast<Instruction>(FunctionUser);
657-
Value *index = instruction->getOperand(3);
657+
Value *index = instruction->getOperand(kCreateHandleResIndexOpIdx);
658+
if (!isa<Constant>(index)) {
659+
FoundDynamicIndexing = true;
660+
break;
661+
}
662+
}
663+
664+
auto CreateHandleFromBindingFn =
665+
HlslOP->GetOpFunc(DXIL::OpCode::CreateHandleFromBinding, Type::getVoidTy(Ctx));
666+
for (auto FI = CreateHandleFromBindingFn->user_begin(); FI != CreateHandleFromBindingFn->user_end();) {
667+
auto * FunctionUser = *FI++;
668+
auto instruction = cast<Instruction>(FunctionUser);
669+
Value *index = instruction->getOperand(kCreateHandleFromBindingResIndexOpIdx);
670+
if (!isa<Constant>(index)) {
671+
FoundDynamicIndexing = true;
672+
break;
673+
}
674+
}
675+
676+
auto CreateHandleFromHeapFn = HlslOP->GetOpFunc(
677+
DXIL::OpCode::CreateHandleFromHeap, Type::getVoidTy(Ctx));
678+
for (auto FI = CreateHandleFromHeapFn->user_begin();
679+
FI != CreateHandleFromHeapFn->user_end();) {
680+
auto *FunctionUser = *FI++;
681+
auto instruction = cast<Instruction>(FunctionUser);
682+
Value *index = instruction->getOperand(kCreateHandleFromHeapHeapIndexOpIdx);
658683
if (!isa<Constant>(index)) {
659684
FoundDynamicIndexing = true;
660685
break;

tools/clang/unittests/HLSL/PixTest.cpp

Lines changed: 130 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,10 @@ class PixTest {
192192
TEST_METHOD(DiaCompileArgs)
193193
TEST_METHOD(PixDebugCompileInfo)
194194

195+
TEST_METHOD(CheckSATPassFor66_NoDynamicAccess)
196+
TEST_METHOD(CheckSATPassFor66_DynamicFromRootSig)
197+
TEST_METHOD(CheckSATPassFor66_DynamicFromHeap)
198+
195199
TEST_METHOD(PixStructAnnotation_Simple)
196200
TEST_METHOD(PixStructAnnotation_CopiedStruct)
197201
TEST_METHOD(PixStructAnnotation_MixedSizes)
@@ -978,7 +982,8 @@ class PixTest {
978982

979983
TestableResults TestStructAnnotationCase(const char* hlsl, const wchar_t* optimizationLevel, bool validateCoverage = true);
980984
void ValidateAllocaWrite(std::vector<AllocaWrite> const& allocaWrites, size_t index, const char* name);
981-
985+
std::string RunShaderAccessTrackingPassAndReturnOutputMessages(IDxcBlob* blob);
986+
CComPtr<IDxcBlob> Compile(const char* hlsl, const wchar_t* profile);
982987
};
983988

984989

@@ -1640,6 +1645,130 @@ TEST_F(PixTest, PixDebugCompileInfo) {
16401645
VERIFY_ARE_EQUAL(std::wstring(profile), std::wstring(hlslTarget));
16411646
}
16421647

1648+
CComPtr<IDxcBlob> PixTest::Compile(const char* hlsl, const wchar_t* profile)
1649+
{
1650+
1651+
CComPtr<IDxcLibrary> pLib;
1652+
VERIFY_SUCCEEDED(m_dllSupport.CreateInstance(CLSID_DxcLibrary, &pLib));
1653+
1654+
CComPtr<IDxcCompiler> pCompiler;
1655+
CComPtr<IDxcCompiler2> pCompiler2;
1656+
1657+
CComPtr<IDxcOperationResult> pResult;
1658+
CComPtr<IDxcBlobEncoding> pSource;
1659+
CComPtr<IDxcBlob> pProgram;
1660+
CComPtr<IDxcBlob> pPdbBlob;
1661+
WCHAR *pDebugName = nullptr;
1662+
1663+
VERIFY_SUCCEEDED(CreateCompiler(&pCompiler));
1664+
VERIFY_SUCCEEDED(pCompiler.QueryInterface(&pCompiler2));
1665+
CreateBlobFromText(hlsl, &pSource);
1666+
LPCWSTR args[] = {L"/Zi", L"/Qembed_debug"};
1667+
VERIFY_SUCCEEDED(pCompiler2->CompileWithDebug(
1668+
pSource, L"source.hlsl", L"main", profile, args, _countof(args),
1669+
nullptr, 0, nullptr, &pResult, &pDebugName, &pPdbBlob));
1670+
1671+
HRESULT hr;
1672+
VERIFY_SUCCEEDED(pResult->GetStatus(&hr));
1673+
if (FAILED(hr))
1674+
{
1675+
CComPtr<IDxcBlobEncoding> pErrors;
1676+
pResult->GetErrorBuffer(&pErrors);
1677+
1678+
if (pErrors && pErrors->GetBufferSize() > 0) {
1679+
auto errorMessages =
1680+
reinterpret_cast<const char *>(pErrors->GetBufferPointer());
1681+
std::wstring wideErrrors;
1682+
wideErrrors.assign(errorMessages,
1683+
errorMessages + pErrors->GetBufferSize());
1684+
WEX::Logging::Log::Comment(L"Compilation failed. Error messages:");
1685+
WEX::Logging::Log::Comment(wideErrrors.c_str());
1686+
}
1687+
1688+
VERIFY_SUCCEEDED(hr);
1689+
}
1690+
VERIFY_SUCCEEDED(pResult->GetResult(&pProgram));
1691+
1692+
return pProgram;
1693+
}
1694+
1695+
std::string PixTest::RunShaderAccessTrackingPassAndReturnOutputMessages(IDxcBlob* blob)
1696+
{
1697+
CComPtr<IDxcBlob> dxil = FindModule(DFCC_ShaderDebugInfoDXIL, blob);
1698+
CComPtr<IDxcOptimizer> pOptimizer;
1699+
VERIFY_SUCCEEDED(
1700+
m_dllSupport.CreateInstance(CLSID_DxcOptimizer, &pOptimizer));
1701+
std::vector<LPCWSTR> Options;
1702+
Options.push_back(L"-opt-mod-passes");
1703+
Options.push_back(L"-hlsl-dxil-pix-shader-access-instrumentation,config=,checkForDynamicIndexing=1");
1704+
1705+
CComPtr<IDxcBlob> pOptimizedModule;
1706+
CComPtr<IDxcBlobEncoding> pText;
1707+
VERIFY_SUCCEEDED(pOptimizer->RunOptimizer(
1708+
dxil, Options.data(), Options.size(), &pOptimizedModule, &pText));
1709+
1710+
std::string outputText;
1711+
if (pText->GetBufferSize() != 0) {
1712+
outputText = reinterpret_cast<const char *>(pText->GetBufferPointer());
1713+
}
1714+
1715+
return outputText;
1716+
}
1717+
1718+
TEST_F(PixTest, CheckSATPassFor66_NoDynamicAccess) {
1719+
1720+
const char *noDynamicAccess = R"(
1721+
[RootSignature("")]
1722+
float main(float pos : A) : SV_Target {
1723+
float x = abs(pos);
1724+
float y = sin(pos);
1725+
float z = x + y;
1726+
return z;
1727+
}
1728+
)";
1729+
1730+
auto compiled = Compile(noDynamicAccess, L"ps_6_6");
1731+
auto satResults = RunShaderAccessTrackingPassAndReturnOutputMessages(compiled);
1732+
VERIFY_IS_TRUE(satResults.empty());
1733+
}
1734+
1735+
TEST_F(PixTest, CheckSATPassFor66_DynamicFromRootSig) {
1736+
1737+
const char *dynamicTextureAccess = R"x(
1738+
Texture1D<float4> tex[5] : register(t3);
1739+
SamplerState SS[3] : register(s2);
1740+
1741+
[RootSignature("DescriptorTable(SRV(t3, numDescriptors=5)),\
1742+
DescriptorTable(Sampler(s2, numDescriptors=3))")]
1743+
float4 main(int i : A, float j : B) : SV_TARGET
1744+
{
1745+
float4 r = tex[i].Sample(SS[i], i);
1746+
return r;
1747+
}
1748+
)x";
1749+
1750+
auto compiled = Compile(dynamicTextureAccess, L"ps_6_6");
1751+
auto satResults = RunShaderAccessTrackingPassAndReturnOutputMessages(compiled);
1752+
VERIFY_IS_TRUE(satResults.find("FoundDynamicIndexing") != string::npos);
1753+
}
1754+
1755+
TEST_F(PixTest, CheckSATPassFor66_DynamicFromHeap) {
1756+
1757+
const char *dynamicResourceDecriptorHeapAccess = R"(
1758+
static sampler sampler0 = SamplerDescriptorHeap[0];
1759+
float4 main(int input : INPUT) : SV_Target
1760+
{
1761+
Texture2D texture = ResourceDescriptorHeap[input];
1762+
return texture.Sample(sampler0, float2(0,0));
1763+
}
1764+
)";
1765+
1766+
auto compiled = Compile(dynamicResourceDecriptorHeapAccess, L"ps_6_6");
1767+
auto satResults =
1768+
RunShaderAccessTrackingPassAndReturnOutputMessages(compiled);
1769+
VERIFY_IS_TRUE(satResults.find("FoundDynamicIndexing") != string::npos);
1770+
}
1771+
16431772
// This function lives in lib\DxilPIXPasses\DxilAnnotateWithVirtualRegister.cpp
16441773
// Declared here so we can test it.
16451774
uint32_t CountStructMembers(llvm::Type const* pType);

0 commit comments

Comments
 (0)