blob: a442bfabed3a793763f371a9922cee0028cebe17 [file] [edit]
///////////////////////////////////////////////////////////////////////////////
// //
// DxilNonUniformResourceIndexInstrumentation.cpp //
// Copyright (C) Microsoft Corporation. All rights reserved. //
// This file is distributed under the University of Illinois Open Source //
// License. See LICENSE.TXT for details. //
// //
// Provides a pass to add instrumentation to determine missing usage of the //
// NonUniformResourceIndex qualifier when dynamically indexing resources. //
// Used by PIX. //
// //
///////////////////////////////////////////////////////////////////////////////
#include "PixPassHelpers.h"
#include "dxc/DXIL/DxilInstructions.h"
#include "dxc/DxilPIXPasses/DxilPIXPasses.h"
#include "dxc/DxilPIXPasses/DxilPIXVirtualRegisters.h"
#include "dxc/Support/Global.h"
#include "llvm/IR/Module.h"
#include "llvm/Support/FormattedStream.h"
using namespace llvm;
using namespace hlsl;
class DxilNonUniformResourceIndexInstrumentation : public ModulePass {
public:
static char ID; // Pass identification, replacement for typeid
explicit DxilNonUniformResourceIndexInstrumentation() : ModulePass(ID) {}
StringRef getPassName() const override {
return "DXIL NonUniformResourceIndex Instrumentation";
}
bool runOnModule(Module &M) override;
};
bool DxilNonUniformResourceIndexInstrumentation::runOnModule(Module &M) {
// This pass adds instrumentation for incorrect NonUniformResourceIndex usage
DxilModule &DM = M.GetOrCreateDxilModule();
LLVMContext &Ctx = M.getContext();
OP *HlslOP = DM.GetOP();
hlsl::DxilResource *PixUAVResource = nullptr;
UndefValue *UndefArg = UndefValue::get(Type::getInt32Ty(Ctx));
// Use WaveActiveAllEqual to check if a dynamic index is uniform
Function *WaveActiveAllEqualFunc = HlslOP->GetOpFunc(
DXIL::OpCode::WaveActiveAllEqual, Type::getInt32Ty(Ctx));
Constant *WaveActiveAllEqualOpCode =
HlslOP->GetI32Const((int32_t)DXIL::OpCode::WaveActiveAllEqual);
// Atomic operation to use for writing to the result uav resource
Function *AtomicOpFunc =
HlslOP->GetOpFunc(OP::OpCode::AtomicBinOp, Type::getInt32Ty(Ctx));
Constant *AtomicBinOpcode =
HlslOP->GetU32Const((uint32_t)OP::OpCode::AtomicBinOp);
Constant *AtomicOr = HlslOP->GetU32Const((uint32_t)DXIL::AtomicBinOpCode::Or);
std::map<Function *, CallInst *> FunctionToUAVHandle;
// This is the main pass that will iterate through all of the resources that
// are dynamically indexed. If not already marked NonUniformResourceIndex,
// then insert WaveActiveAllEqual to determine if the index is uniform
// and finally write to a UAV resource with the result.
PIXPassHelpers::ForEachDynamicallyIndexedResource(
DM, [&](bool IsNonUniformIndex, Instruction *CreateHandle,
Value *IndexOperand) {
if (IsNonUniformIndex) {
// The NonUniformResourceIndex qualifier was used, continue.
return true;
}
if (!PixUAVResource) {
PixUAVResource =
PIXPassHelpers::CreateGlobalUAVResource(DM, 0, "PixUAVResource");
}
CallInst *PixUAVHandle = nullptr;
Function *F = CreateHandle->getParent()->getParent();
const auto FunctionToUAVHandleIter = FunctionToUAVHandle.lower_bound(F);
if ((FunctionToUAVHandleIter != FunctionToUAVHandle.end()) &&
(FunctionToUAVHandleIter->first == F)) {
PixUAVHandle = FunctionToUAVHandleIter->second;
} else {
IRBuilder<> Builder(F->getEntryBlock().getFirstInsertionPt());
PixUAVHandle = PIXPassHelpers::CreateHandleForResource(
DM, Builder, PixUAVResource, "PixUAVHandle");
FunctionToUAVHandle.insert(FunctionToUAVHandleIter,
{F, PixUAVHandle});
}
IRBuilder<> Builder(CreateHandle);
uint32_t InstructionNumber = 0;
if (!pix_dxil::PixDxilInstNum::FromInst(CreateHandle,
&InstructionNumber)) {
DXASSERT_NOMSG(false);
}
// The output UAV is treated as a bit array where each bit corresponds
// to an instruction number. This determines what byte offset to write
// our result to based on the instruction number.
const uint32_t InstructionNumByteOffset =
(InstructionNumber / 32u) * sizeof(uint32_t);
const uint32_t InstructionNumBitPosition = (InstructionNumber % 32u);
const uint32_t InstructionNumBitMask = 1u << InstructionNumBitPosition;
Constant *UAVByteOffsetArg =
HlslOP->GetU32Const(InstructionNumByteOffset);
CallInst *WaveActiveAllEqualCall = Builder.CreateCall(
WaveActiveAllEqualFunc, {WaveActiveAllEqualOpCode, IndexOperand});
// This takes the result of the WaveActiveAllEqual result and shifts
// it into the same bit position as the instruction number, followed
// by an xor to determine what to write to the UAV
Value *IsWaveEqual =
Builder.CreateZExt(WaveActiveAllEqualCall, Builder.getInt32Ty());
Value *WaveEqualBitMask =
Builder.CreateShl(IsWaveEqual, InstructionNumBitPosition);
Value *FinalResult =
Builder.CreateXor(WaveEqualBitMask, InstructionNumBitMask);
// Generate instructions to bitwise OR a UAV value corresponding
// to the instruction number and result of WaveActiveAllEqual.
// If WaveActiveAllEqual was false, we write a 1, otherwise a 0.
Builder.CreateCall(
AtomicOpFunc,
{
AtomicBinOpcode, // i32, ; opcode
PixUAVHandle, // %dx.types.Handle, ; resource handle
AtomicOr, // i32, ; binary operation code :
// EXCHANGE, IADD, AND, OR, XOR
// IMIN, IMAX, UMIN, UMAX
UAVByteOffsetArg, // i32, ; coordinate c0: byte offset
UndefArg, // i32, ; coordinate c1 (unused)
UndefArg, // i32, ; coordinate c2 (unused)
FinalResult // i32); value
},
"UAVInstructionNumberBitSet");
return true;
});
const bool modified = (PixUAVResource != nullptr);
if (modified) {
DM.ReEmitDxilResources();
if (OSOverride != nullptr) {
formatted_raw_ostream FOS(*OSOverride);
FOS << "\nFoundDynamicIndexingNoNuri\n";
}
}
return modified;
}
char DxilNonUniformResourceIndexInstrumentation::ID = 0;
ModulePass *llvm::createDxilNonUniformResourceIndexInstrumentationPass() {
return new DxilNonUniformResourceIndexInstrumentation();
}
INITIALIZE_PASS(DxilNonUniformResourceIndexInstrumentation,
"hlsl-dxil-non-uniform-resource-index-instrumentation",
"HLSL DXIL NonUniformResourceIndex instrumentation for PIX",
false, false)