Skip to content

Commit dc4a2b6

Browse files
authored
[PIX] Add a pass for PIX to log missing NonUniformResourceIndex usage into a UAV (microsoft#7272)
This is a pass to add instructions to determine missing usage of the NonUniformResourceIndex qualifier when dynamically indexing resources. The instruction numbers will be written out to a UAV for later ingestion by PIX to present a view of the output.
1 parent 9e91844 commit dc4a2b6

File tree

8 files changed

+499
-83
lines changed

8 files changed

+499
-83
lines changed

include/dxc/DxilPIXPasses/DxilPIXPasses.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ ModulePass *createDxilDebugInstrumentationPass();
2727
ModulePass *createDxilShaderAccessTrackingPass();
2828
ModulePass *createDxilPIXAddTidToAmplificationShaderPayloadPass();
2929
ModulePass *createDxilPIXDXRInvocationsLogPass();
30+
ModulePass *createDxilNonUniformResourceIndexInstrumentationPass();
3031

3132
void initializeDxilAddPixelHitInstrumentationPass(llvm::PassRegistry &);
3233
void initializeDxilDbgValueToDbgDeclarePass(llvm::PassRegistry &);
@@ -41,5 +42,7 @@ void initializeDxilShaderAccessTrackingPass(llvm::PassRegistry &);
4142
void initializeDxilPIXAddTidToAmplificationShaderPayloadPass(
4243
llvm::PassRegistry &);
4344
void initializeDxilPIXDXRInvocationsLogPass(llvm::PassRegistry &);
45+
void initializeDxilNonUniformResourceIndexInstrumentationPass(
46+
llvm::PassRegistry &);
4447

4548
} // namespace llvm

lib/DxilPIXPasses/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ add_llvm_library(LLVMDxilPIXPasses
2020
PixPassHelpers.cpp
2121
DxilPIXAddTidToAmplificationShaderPayload.cpp
2222
DxilPIXDXRInvocationsLog.cpp
23+
DxilNonUniformResourceIndexInstrumentation.cpp
2324

2425
ADDITIONAL_HEADER_DIRS
2526
${LLVM_MAIN_INCLUDE_DIR}/llvm/IR
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
///////////////////////////////////////////////////////////////////////////////
2+
// //
3+
// DxilNonUniformResourceIndexInstrumentation.cpp //
4+
// Copyright (C) Microsoft Corporation. All rights reserved. //
5+
// This file is distributed under the University of Illinois Open Source //
6+
// License. See LICENSE.TXT for details. //
7+
// //
8+
// Provides a pass to add instrumentation to determine missing usage of the //
9+
// NonUniformResourceIndex qualifier when dynamically indexing resources. //
10+
// Used by PIX. //
11+
// //
12+
///////////////////////////////////////////////////////////////////////////////
13+
14+
#include "PixPassHelpers.h"
15+
#include "dxc/DXIL/DxilInstructions.h"
16+
#include "dxc/DxilPIXPasses/DxilPIXPasses.h"
17+
#include "dxc/DxilPIXPasses/DxilPIXVirtualRegisters.h"
18+
#include "dxc/Support/Global.h"
19+
#include "llvm/IR/Module.h"
20+
#include "llvm/Support/FormattedStream.h"
21+
22+
using namespace llvm;
23+
using namespace hlsl;
24+
25+
class DxilNonUniformResourceIndexInstrumentation : public ModulePass {
26+
27+
public:
28+
static char ID; // Pass identification, replacement for typeid
29+
explicit DxilNonUniformResourceIndexInstrumentation() : ModulePass(ID) {}
30+
StringRef getPassName() const override {
31+
return "DXIL NonUniformResourceIndex Instrumentation";
32+
}
33+
bool runOnModule(Module &M) override;
34+
};
35+
36+
bool DxilNonUniformResourceIndexInstrumentation::runOnModule(Module &M) {
37+
// This pass adds instrumentation for incorrect NonUniformResourceIndex usage
38+
39+
DxilModule &DM = M.GetOrCreateDxilModule();
40+
LLVMContext &Ctx = M.getContext();
41+
OP *HlslOP = DM.GetOP();
42+
43+
hlsl::DxilResource *PixUAVResource = nullptr;
44+
45+
UndefValue *UndefArg = UndefValue::get(Type::getInt32Ty(Ctx));
46+
47+
// Use WaveActiveAllEqual to check if a dynamic index is uniform
48+
Function *WaveActiveAllEqualFunc = HlslOP->GetOpFunc(
49+
DXIL::OpCode::WaveActiveAllEqual, Type::getInt32Ty(Ctx));
50+
Constant *WaveActiveAllEqualOpCode =
51+
HlslOP->GetI32Const((int32_t)DXIL::OpCode::WaveActiveAllEqual);
52+
53+
// Atomic operation to use for writing to the result uav resource
54+
Function *AtomicOpFunc =
55+
HlslOP->GetOpFunc(OP::OpCode::AtomicBinOp, Type::getInt32Ty(Ctx));
56+
Constant *AtomicBinOpcode =
57+
HlslOP->GetU32Const((uint32_t)OP::OpCode::AtomicBinOp);
58+
Constant *AtomicOr = HlslOP->GetU32Const((uint32_t)DXIL::AtomicBinOpCode::Or);
59+
60+
std::map<Function *, CallInst *> FunctionToUAVHandle;
61+
62+
// This is the main pass that will iterate through all of the resources that
63+
// are dynamically indexed. If not already marked NonUniformResourceIndex,
64+
// then insert WaveActiveAllEqual to determine if the index is uniform
65+
// and finally write to a UAV resource with the result.
66+
67+
PIXPassHelpers::ForEachDynamicallyIndexedResource(
68+
DM, [&](bool IsNonUniformIndex, Instruction *CreateHandle,
69+
Value *IndexOperand) {
70+
if (IsNonUniformIndex) {
71+
// The NonUniformResourceIndex qualifier was used, continue.
72+
return true;
73+
}
74+
75+
if (!PixUAVResource) {
76+
PixUAVResource =
77+
PIXPassHelpers::CreateGlobalUAVResource(DM, 0, "PixUAVResource");
78+
}
79+
80+
CallInst *PixUAVHandle = nullptr;
81+
Function *F = CreateHandle->getParent()->getParent();
82+
83+
const auto FunctionToUAVHandleIter = FunctionToUAVHandle.lower_bound(F);
84+
85+
if ((FunctionToUAVHandleIter != FunctionToUAVHandle.end()) &&
86+
(FunctionToUAVHandleIter->first == F)) {
87+
PixUAVHandle = FunctionToUAVHandleIter->second;
88+
} else {
89+
IRBuilder<> Builder(F->getEntryBlock().getFirstInsertionPt());
90+
91+
PixUAVHandle = PIXPassHelpers::CreateHandleForResource(
92+
DM, Builder, PixUAVResource, "PixUAVHandle");
93+
94+
FunctionToUAVHandle.insert(FunctionToUAVHandleIter,
95+
{F, PixUAVHandle});
96+
}
97+
98+
IRBuilder<> Builder(CreateHandle);
99+
100+
uint32_t InstructionNumber = 0;
101+
if (!pix_dxil::PixDxilInstNum::FromInst(CreateHandle,
102+
&InstructionNumber)) {
103+
DXASSERT_NOMSG(false);
104+
}
105+
106+
// The output UAV is treated as a bit array where each bit corresponds
107+
// to an instruction number. This determines what byte offset to write
108+
// our result to based on the instruction number.
109+
const uint32_t InstructionNumByteOffset =
110+
(InstructionNumber / 32u) * sizeof(uint32_t);
111+
const uint32_t InstructionNumBitPosition = (InstructionNumber % 32u);
112+
const uint32_t InstructionNumBitMask = 1u << InstructionNumBitPosition;
113+
114+
Constant *UAVByteOffsetArg =
115+
HlslOP->GetU32Const(InstructionNumByteOffset);
116+
117+
CallInst *WaveActiveAllEqualCall = Builder.CreateCall(
118+
WaveActiveAllEqualFunc, {WaveActiveAllEqualOpCode, IndexOperand});
119+
120+
// This takes the result of the WaveActiveAllEqual result and shifts
121+
// it into the same bit position as the instruction number, followed
122+
// by an xor to determine what to write to the UAV
123+
Value *IsWaveEqual =
124+
Builder.CreateZExt(WaveActiveAllEqualCall, Builder.getInt32Ty());
125+
Value *WaveEqualBitMask =
126+
Builder.CreateShl(IsWaveEqual, InstructionNumBitPosition);
127+
Value *FinalResult =
128+
Builder.CreateXor(WaveEqualBitMask, InstructionNumBitMask);
129+
130+
// Generate instructions to bitwise OR a UAV value corresponding
131+
// to the instruction number and result of WaveActiveAllEqual.
132+
// If WaveActiveAllEqual was false, we write a 1, otherwise a 0.
133+
Builder.CreateCall(
134+
AtomicOpFunc,
135+
{
136+
AtomicBinOpcode, // i32, ; opcode
137+
PixUAVHandle, // %dx.types.Handle, ; resource handle
138+
AtomicOr, // i32, ; binary operation code :
139+
// EXCHANGE, IADD, AND, OR, XOR
140+
// IMIN, IMAX, UMIN, UMAX
141+
UAVByteOffsetArg, // i32, ; coordinate c0: byte offset
142+
UndefArg, // i32, ; coordinate c1 (unused)
143+
UndefArg, // i32, ; coordinate c2 (unused)
144+
FinalResult // i32); value
145+
},
146+
"UAVInstructionNumberBitSet");
147+
return true;
148+
});
149+
150+
const bool modified = (PixUAVResource != nullptr);
151+
152+
if (modified) {
153+
DM.ReEmitDxilResources();
154+
155+
if (OSOverride != nullptr) {
156+
formatted_raw_ostream FOS(*OSOverride);
157+
FOS << "\nFoundDynamicIndexingNoNuri\n";
158+
}
159+
}
160+
161+
return modified;
162+
}
163+
164+
char DxilNonUniformResourceIndexInstrumentation::ID = 0;
165+
166+
ModulePass *llvm::createDxilNonUniformResourceIndexInstrumentationPass() {
167+
return new DxilNonUniformResourceIndexInstrumentation();
168+
}
169+
170+
INITIALIZE_PASS(DxilNonUniformResourceIndexInstrumentation,
171+
"hlsl-dxil-non-uniform-resource-index-instrumentation",
172+
"HLSL DXIL NonUniformResourceIndex instrumentation for PIX",
173+
false, false)

lib/DxilPIXPasses/DxilShaderAccessTracking.cpp

Lines changed: 7 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -795,87 +795,6 @@ DxilShaderAccessTracking::GetResourceFromHandle(Value *resHandle,
795795
return ret;
796796
}
797797

798-
static bool CheckForDynamicIndexing(OP *HlslOP, LLVMContext &Ctx,
799-
DxilModule &DM) {
800-
bool FoundDynamicIndexing = false;
801-
802-
for (llvm::Function &F : DM.GetModule()->functions()) {
803-
if (F.isDeclaration() && !F.use_empty() && OP::IsDxilOpFunc(&F)) {
804-
if (F.hasName()) {
805-
if (F.getName().find("createHandleForLib") != StringRef::npos) {
806-
auto FunctionUses = F.uses();
807-
for (auto FI = FunctionUses.begin(); FI != FunctionUses.end();) {
808-
auto &FunctionUse = *FI++;
809-
auto FunctionUser = FunctionUse.getUser();
810-
auto instruction = cast<Instruction>(FunctionUser);
811-
Value *resourceLoad =
812-
instruction->getOperand(kCreateHandleForLibResOpIdx);
813-
if (auto *load = cast<LoadInst>(resourceLoad)) {
814-
auto *resOrGep = load->getOperand(0);
815-
if (isa<GetElementPtrInst>(resOrGep)) {
816-
FoundDynamicIndexing = true;
817-
break;
818-
}
819-
}
820-
}
821-
}
822-
}
823-
}
824-
if (FoundDynamicIndexing) {
825-
break;
826-
}
827-
}
828-
829-
if (!FoundDynamicIndexing) {
830-
auto CreateHandleFn =
831-
HlslOP->GetOpFunc(DXIL::OpCode::CreateHandle, Type::getVoidTy(Ctx));
832-
for (auto FI = CreateHandleFn->user_begin();
833-
FI != CreateHandleFn->user_end();) {
834-
auto *FunctionUser = *FI++;
835-
auto instruction = cast<Instruction>(FunctionUser);
836-
Value *index = instruction->getOperand(kCreateHandleResIndexOpIdx);
837-
if (!isa<Constant>(index)) {
838-
FoundDynamicIndexing = true;
839-
break;
840-
}
841-
}
842-
}
843-
844-
if (!FoundDynamicIndexing) {
845-
auto CreateHandleFromBindingFn = HlslOP->GetOpFunc(
846-
DXIL::OpCode::CreateHandleFromBinding, Type::getVoidTy(Ctx));
847-
for (auto FI = CreateHandleFromBindingFn->user_begin();
848-
FI != CreateHandleFromBindingFn->user_end();) {
849-
auto *FunctionUser = *FI++;
850-
auto instruction = cast<Instruction>(FunctionUser);
851-
Value *index =
852-
instruction->getOperand(kCreateHandleFromBindingResIndexOpIdx);
853-
if (!isa<Constant>(index)) {
854-
FoundDynamicIndexing = true;
855-
break;
856-
}
857-
}
858-
}
859-
860-
if (!FoundDynamicIndexing) {
861-
auto CreateHandleFromHeapFn = HlslOP->GetOpFunc(
862-
DXIL::OpCode::CreateHandleFromHeap, Type::getVoidTy(Ctx));
863-
for (auto FI = CreateHandleFromHeapFn->user_begin();
864-
FI != CreateHandleFromHeapFn->user_end();) {
865-
auto *FunctionUser = *FI++;
866-
auto instruction = cast<Instruction>(FunctionUser);
867-
Value *index =
868-
instruction->getOperand(kCreateHandleFromHeapHeapIndexOpIdx);
869-
if (!isa<Constant>(index)) {
870-
FoundDynamicIndexing = true;
871-
break;
872-
}
873-
}
874-
}
875-
876-
return FoundDynamicIndexing;
877-
}
878-
879798
bool DxilShaderAccessTracking::runOnModule(Module &M) {
880799
// This pass adds instrumentation for shader access to resources
881800

@@ -887,7 +806,13 @@ bool DxilShaderAccessTracking::runOnModule(Module &M) {
887806

888807
if (m_CheckForDynamicIndexing) {
889808

890-
bool FoundDynamicIndexing = CheckForDynamicIndexing(HlslOP, Ctx, DM);
809+
bool FoundDynamicIndexing = false;
810+
811+
PIXPassHelpers::ForEachDynamicallyIndexedResource(
812+
DM, [&FoundDynamicIndexing](bool, Instruction *, Value *) {
813+
FoundDynamicIndexing = true;
814+
return false;
815+
});
891816

892817
if (FoundDynamicIndexing) {
893818
if (OSOverride != nullptr) {

lib/DxilPIXPasses/PixPassHelpers.cpp

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -512,6 +512,90 @@ unsigned int FindOrAddSV_Position(hlsl::DxilModule &DM,
512512
}
513513
}
514514

515+
void ForEachDynamicallyIndexedResource(
516+
hlsl::DxilModule &DM,
517+
const std::function<bool(bool, Instruction *, Value *)> &Visitor) {
518+
OP *HlslOP = DM.GetOP();
519+
LLVMContext &Ctx = DM.GetModule()->getContext();
520+
521+
for (llvm::Function &F : DM.GetModule()->functions()) {
522+
if (F.isDeclaration() && !F.use_empty() && OP::IsDxilOpFunc(&F)) {
523+
if (F.hasName()) {
524+
if (F.getName().find("createHandleForLib") != StringRef::npos) {
525+
auto FunctionUses = F.uses();
526+
for (auto FI = FunctionUses.begin(); FI != FunctionUses.end();) {
527+
auto &FunctionUse = *FI++;
528+
auto FunctionUser = FunctionUse.getUser();
529+
auto instruction = cast<Instruction>(FunctionUser);
530+
Value *resourceLoad = instruction->getOperand(
531+
DXIL::OperandIndex::kCreateHandleForLibResOpIdx);
532+
if (auto *load = cast<LoadInst>(resourceLoad)) {
533+
auto *resOrGep = load->getOperand(0);
534+
if (auto *gep = dyn_cast<GetElementPtrInst>(resOrGep)) {
535+
if (!Visitor(DxilMDHelper::IsMarkedNonUniform(gep), load,
536+
gep->getOperand(2))) {
537+
return;
538+
}
539+
}
540+
}
541+
}
542+
}
543+
}
544+
}
545+
}
546+
547+
auto CreateHandleFn =
548+
HlslOP->GetOpFunc(DXIL::OpCode::CreateHandle, Type::getVoidTy(Ctx));
549+
for (auto FI = CreateHandleFn->user_begin();
550+
FI != CreateHandleFn->user_end();) {
551+
auto *FunctionUser = *FI++;
552+
auto instruction = cast<Instruction>(FunctionUser);
553+
Value *index =
554+
instruction->getOperand(DXIL::OperandIndex::kCreateHandleResIndexOpIdx);
555+
if (!isa<Constant>(index)) {
556+
const DxilInst_CreateHandle createHandle(instruction);
557+
if (!Visitor(createHandle.get_nonUniformIndex_val(), instruction,
558+
index)) {
559+
return;
560+
}
561+
}
562+
}
563+
564+
auto CreateHandleFromBindingFn = HlslOP->GetOpFunc(
565+
DXIL::OpCode::CreateHandleFromBinding, Type::getVoidTy(Ctx));
566+
for (auto FI = CreateHandleFromBindingFn->user_begin();
567+
FI != CreateHandleFromBindingFn->user_end();) {
568+
auto *FunctionUser = *FI++;
569+
auto instruction = cast<Instruction>(FunctionUser);
570+
Value *index = instruction->getOperand(
571+
DXIL::OperandIndex::kCreateHandleFromBindingResIndexOpIdx);
572+
if (!isa<Constant>(index)) {
573+
const DxilInst_CreateHandleFromBinding createHandle(instruction);
574+
if (!Visitor(createHandle.get_nonUniformIndex_val(), instruction,
575+
index)) {
576+
return;
577+
}
578+
}
579+
}
580+
581+
auto CreateHandleFromHeapFn = HlslOP->GetOpFunc(
582+
DXIL::OpCode::CreateHandleFromHeap, Type::getVoidTy(Ctx));
583+
for (auto FI = CreateHandleFromHeapFn->user_begin();
584+
FI != CreateHandleFromHeapFn->user_end();) {
585+
auto *FunctionUser = *FI++;
586+
auto instruction = cast<Instruction>(FunctionUser);
587+
Value *index = instruction->getOperand(
588+
DXIL::OperandIndex::kCreateHandleFromHeapHeapIndexOpIdx);
589+
if (!isa<Constant>(index)) {
590+
const DxilInst_CreateHandleFromHeap createHandle(instruction);
591+
if (!Visitor(createHandle.get_nonUniformIndex_val(), instruction,
592+
index)) {
593+
return;
594+
}
595+
}
596+
}
597+
}
598+
515599
#ifdef PIX_DEBUG_DUMP_HELPER
516600

517601
static int g_logIndent = 0;

0 commit comments

Comments
 (0)