|
@@ -10,11 +10,12 @@
|
|
|
// //
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
-#include "dxc/HLSL/DxilGenerationPass.h"
|
|
|
#include "dxc/DXIL/DxilOperations.h"
|
|
|
+
|
|
|
#include "dxc/DXIL/DxilInstructions.h"
|
|
|
#include "dxc/DXIL/DxilModule.h"
|
|
|
#include "dxc/DxilPIXPasses/DxilPIXPasses.h"
|
|
|
+#include "dxc/HLSL/DxilGenerationPass.h"
|
|
|
#include "dxc/HLSL/DxilSpanAllocator.h"
|
|
|
|
|
|
#include "llvm/IR/PassManager.h"
|
|
@@ -29,9 +30,7 @@
|
|
|
using namespace llvm;
|
|
|
using namespace hlsl;
|
|
|
|
|
|
-
|
|
|
-void ThrowIf(bool a)
|
|
|
-{
|
|
|
+void ThrowIf(bool a) {
|
|
|
if (a) {
|
|
|
throw ::hlsl::Exception(E_INVALIDARG);
|
|
|
}
|
|
@@ -40,19 +39,20 @@ void ThrowIf(bool a)
|
|
|
//---------------------------------------------------------------------------------------------------------------------------------
|
|
|
// These types are taken from PIX's ShaderAccessHelpers.h
|
|
|
|
|
|
-enum class ShaderAccessFlags : uint32_t
|
|
|
-{
|
|
|
+enum class ShaderAccessFlags : uint32_t {
|
|
|
None = 0,
|
|
|
Read = 1 << 0,
|
|
|
Write = 1 << 1,
|
|
|
|
|
|
- // "Counter" access is only applicable to UAVs; it means the counter buffer attached to the UAV
|
|
|
- // was accessed, but not necessarily the UAV resource.
|
|
|
+ // "Counter" access is only applicable to UAVs; it means the counter buffer
|
|
|
+ // attached to the UAV was accessed, but not necessarily the UAV resource.
|
|
|
Counter = 1 << 2,
|
|
|
|
|
|
// Descriptor-only read (if any), but not the resource contents (if any).
|
|
|
- // Used for GetDimensions, samplers, and secondary texture for sampler feedback.
|
|
|
- // TODO: Make this a unique value if supported in PIX, then enable GetDimensions
|
|
|
+ // Used for GetDimensions, samplers, and secondary texture for sampler
|
|
|
+ // feedback.
|
|
|
+ // TODO: Make this a unique value if supported in PIX, then enable
|
|
|
+ // GetDimensions
|
|
|
DescriptorRead = 1 << 0,
|
|
|
};
|
|
|
|
|
@@ -72,16 +72,16 @@ static uint32_t OffsetFromAccess(ShaderAccessFlags access) {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-// This enum doesn't have to match PIX's version, because the values are received from PIX encoded in ASCII.
|
|
|
-// However, for ease of comparing this code with PIX, and to be less confusing to future maintainers, this
|
|
|
-// enum does indeed match the same-named enum in PIX.
|
|
|
-enum class RegisterType
|
|
|
-{
|
|
|
+// This enum doesn't have to match PIX's version, because the values are
|
|
|
+// received from PIX encoded in ASCII. However, for ease of comparing this code
|
|
|
+// with PIX, and to be less confusing to future maintainers, this enum does
|
|
|
+// indeed match the same-named enum in PIX.
|
|
|
+enum class RegisterType {
|
|
|
CBV,
|
|
|
SRV,
|
|
|
UAV,
|
|
|
- RTV, // not used.
|
|
|
- DSV, // not used.
|
|
|
+ RTV, // not used.
|
|
|
+ DSV, // not used.
|
|
|
Sampler,
|
|
|
SOV, // not used.
|
|
|
Invalid,
|
|
@@ -89,46 +89,51 @@ enum class RegisterType
|
|
|
};
|
|
|
|
|
|
RegisterType RegisterTypeFromResourceClass(DXIL::ResourceClass c) {
|
|
|
- switch (c)
|
|
|
- {
|
|
|
- case DXIL::ResourceClass::SRV : return RegisterType::SRV ; break;
|
|
|
- case DXIL::ResourceClass::UAV : return RegisterType::UAV ; break;
|
|
|
- case DXIL::ResourceClass::CBuffer: return RegisterType::CBV ; break;
|
|
|
- case DXIL::ResourceClass::Sampler: return RegisterType::Sampler; break;
|
|
|
- case DXIL::ResourceClass::Invalid: return RegisterType::Invalid; break;
|
|
|
+ switch (c) {
|
|
|
+ case DXIL::ResourceClass::SRV:
|
|
|
+ return RegisterType::SRV;
|
|
|
+ break;
|
|
|
+ case DXIL::ResourceClass::UAV:
|
|
|
+ return RegisterType::UAV;
|
|
|
+ break;
|
|
|
+ case DXIL::ResourceClass::CBuffer:
|
|
|
+ return RegisterType::CBV;
|
|
|
+ break;
|
|
|
+ case DXIL::ResourceClass::Sampler:
|
|
|
+ return RegisterType::Sampler;
|
|
|
+ break;
|
|
|
+ case DXIL::ResourceClass::Invalid:
|
|
|
+ return RegisterType::Invalid;
|
|
|
+ break;
|
|
|
default:
|
|
|
ThrowIf(true);
|
|
|
return RegisterType::Invalid;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-struct RegisterTypeAndSpace
|
|
|
-{
|
|
|
- bool operator < (const RegisterTypeAndSpace & o) const {
|
|
|
+struct RegisterTypeAndSpace {
|
|
|
+ bool operator<(const RegisterTypeAndSpace &o) const {
|
|
|
return static_cast<int>(Type) < static_cast<int>(o.Type) ||
|
|
|
- (static_cast<int>(Type) == static_cast<int>(o.Type) && Space < o.Space);
|
|
|
+ (static_cast<int>(Type) == static_cast<int>(o.Type) &&
|
|
|
+ Space < o.Space);
|
|
|
}
|
|
|
RegisterType Type;
|
|
|
- unsigned Space;
|
|
|
+ unsigned Space;
|
|
|
};
|
|
|
|
|
|
// Identifies a bind point as defined by the root signature
|
|
|
-struct RSRegisterIdentifier
|
|
|
-{
|
|
|
+struct RSRegisterIdentifier {
|
|
|
RegisterType Type;
|
|
|
- unsigned Space;
|
|
|
- unsigned Index;
|
|
|
-
|
|
|
- bool operator < (const RSRegisterIdentifier & o) const {
|
|
|
- return
|
|
|
- static_cast<unsigned>(Type) < static_cast<unsigned>(o.Type) &&
|
|
|
- Space < o.Space &&
|
|
|
- Index < o.Index;
|
|
|
+ unsigned Space;
|
|
|
+ unsigned Index;
|
|
|
+
|
|
|
+ bool operator<(const RSRegisterIdentifier &o) const {
|
|
|
+ return static_cast<unsigned>(Type) < static_cast<unsigned>(o.Type) &&
|
|
|
+ Space < o.Space && Index < o.Index;
|
|
|
}
|
|
|
};
|
|
|
|
|
|
-struct SlotRange
|
|
|
-{
|
|
|
+struct SlotRange {
|
|
|
unsigned startSlot;
|
|
|
unsigned numSlots;
|
|
|
|
|
@@ -136,10 +141,9 @@ struct SlotRange
|
|
|
unsigned numInvariableSlots;
|
|
|
};
|
|
|
|
|
|
-
|
|
|
struct DxilResourceAndClass {
|
|
|
- DxilResourceBase * resource;
|
|
|
- Value * index;
|
|
|
+ DxilResourceBase *resource;
|
|
|
+ Value *index;
|
|
|
DXIL::ResourceClass resClass;
|
|
|
};
|
|
|
|
|
@@ -149,26 +153,30 @@ class DxilShaderAccessTracking : public ModulePass {
|
|
|
public:
|
|
|
static char ID; // Pass identification, replacement for typeid
|
|
|
explicit DxilShaderAccessTracking() : ModulePass(ID) {}
|
|
|
- const char *getPassName() const override { return "DXIL shader access tracking"; }
|
|
|
+ const char *getPassName() const override {
|
|
|
+ return "DXIL shader access tracking";
|
|
|
+ }
|
|
|
bool runOnModule(Module &M) override;
|
|
|
void applyOptions(PassOptions O) override;
|
|
|
|
|
|
private:
|
|
|
- void EmitAccess(LLVMContext & Ctx, OP *HlslOP, IRBuilder<> &, Value *slot, ShaderAccessFlags access);
|
|
|
- bool EmitResourceAccess(DxilResourceAndClass &res, Instruction * instruction, OP * HlslOP, LLVMContext & Ctx, ShaderAccessFlags readWrite);
|
|
|
+ void EmitAccess(LLVMContext &Ctx, OP *HlslOP, IRBuilder<> &, Value *slot,
|
|
|
+ ShaderAccessFlags access);
|
|
|
+ bool EmitResourceAccess(DxilResourceAndClass &res, Instruction *instruction,
|
|
|
+ OP *HlslOP, LLVMContext &Ctx,
|
|
|
+ ShaderAccessFlags readWrite);
|
|
|
|
|
|
private:
|
|
|
bool m_CheckForDynamicIndexing = false;
|
|
|
std::map<RegisterTypeAndSpace, SlotRange> m_slotAssignments;
|
|
|
- std::map<llvm::Function*, CallInst *> m_FunctionToUAVHandle;
|
|
|
+ std::map<llvm::Function *, CallInst *> m_FunctionToUAVHandle;
|
|
|
std::set<RSRegisterIdentifier> m_DynamicallyIndexedBindPoints;
|
|
|
};
|
|
|
|
|
|
-static unsigned DeserializeInt(std::deque<char> & q) {
|
|
|
+static unsigned DeserializeInt(std::deque<char> &q) {
|
|
|
unsigned i = 0;
|
|
|
|
|
|
- while(!q.empty() && isdigit(q.front()))
|
|
|
- {
|
|
|
+ while (!q.empty() && isdigit(q.front())) {
|
|
|
i *= 10;
|
|
|
i += q.front() - '0';
|
|
|
q.pop_front();
|
|
@@ -176,38 +184,47 @@ static unsigned DeserializeInt(std::deque<char> & q) {
|
|
|
return i;
|
|
|
}
|
|
|
|
|
|
-static char DequeFront(std::deque<char> & q) {
|
|
|
+static char DequeFront(std::deque<char> &q) {
|
|
|
ThrowIf(q.empty());
|
|
|
auto c = q.front();
|
|
|
q.pop_front();
|
|
|
return c;
|
|
|
}
|
|
|
|
|
|
-static RegisterType ParseRegisterType(std::deque<char> & q) {
|
|
|
- switch (DequeFront(q))
|
|
|
- {
|
|
|
- case 'C': return RegisterType::CBV;
|
|
|
- case 'S': return RegisterType::SRV;
|
|
|
- case 'U': return RegisterType::UAV;
|
|
|
- case 'M': return RegisterType::Sampler;
|
|
|
- case 'I': return RegisterType::Invalid;
|
|
|
- default: return RegisterType::Terminator;
|
|
|
+static RegisterType ParseRegisterType(std::deque<char> &q) {
|
|
|
+ switch (DequeFront(q)) {
|
|
|
+ case 'C':
|
|
|
+ return RegisterType::CBV;
|
|
|
+ case 'S':
|
|
|
+ return RegisterType::SRV;
|
|
|
+ case 'U':
|
|
|
+ return RegisterType::UAV;
|
|
|
+ case 'M':
|
|
|
+ return RegisterType::Sampler;
|
|
|
+ case 'I':
|
|
|
+ return RegisterType::Invalid;
|
|
|
+ default:
|
|
|
+ return RegisterType::Terminator;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
static char EncodeRegisterType(RegisterType r) {
|
|
|
- switch (r)
|
|
|
- {
|
|
|
- case RegisterType::CBV: return 'C';
|
|
|
- case RegisterType::SRV: return 'S';
|
|
|
- case RegisterType::UAV: return 'U';
|
|
|
- case RegisterType::Sampler: return 'M';
|
|
|
- case RegisterType::Invalid: return 'I';
|
|
|
+ switch (r) {
|
|
|
+ case RegisterType::CBV:
|
|
|
+ return 'C';
|
|
|
+ case RegisterType::SRV:
|
|
|
+ return 'S';
|
|
|
+ case RegisterType::UAV:
|
|
|
+ return 'U';
|
|
|
+ case RegisterType::Sampler:
|
|
|
+ return 'M';
|
|
|
+ case RegisterType::Invalid:
|
|
|
+ return 'I';
|
|
|
}
|
|
|
return '.';
|
|
|
}
|
|
|
|
|
|
-static void ValidateDelimiter(std::deque<char> & q, char d) {
|
|
|
+static void ValidateDelimiter(std::deque<char> &q, char d) {
|
|
|
ThrowIf(q.front() != d);
|
|
|
q.pop_front();
|
|
|
}
|
|
@@ -222,7 +239,8 @@ void DxilShaderAccessTracking::applyOptions(PassOptions O) {
|
|
|
std::deque<char> config;
|
|
|
config.assign(configOption.begin(), configOption.end());
|
|
|
|
|
|
- // Parse slot assignments. Compare with PIX's ShaderAccessHelpers.cpp (TrackingConfiguration::SerializedRepresentation)
|
|
|
+ // Parse slot assignments. Compare with PIX's ShaderAccessHelpers.cpp
|
|
|
+ // (TrackingConfiguration::SerializedRepresentation)
|
|
|
RegisterType rt = ParseRegisterType(config);
|
|
|
while (rt != RegisterType::Terminator) {
|
|
|
|
|
@@ -253,65 +271,87 @@ void DxilShaderAccessTracking::EmitAccess(LLVMContext &Ctx, OP *HlslOP,
|
|
|
IRBuilder<> &Builder,
|
|
|
Value *ByteIndex,
|
|
|
ShaderAccessFlags access) {
|
|
|
-
|
|
|
- unsigned OffsetForAccessType = static_cast<unsigned>(OffsetFromAccess(access) * BytesPerDWORD);
|
|
|
- auto OffsetByteIndex = Builder.CreateAdd(ByteIndex, HlslOP->GetU32Const(OffsetForAccessType), "OffsetByteIndex");
|
|
|
-
|
|
|
- UndefValue* UndefIntArg = UndefValue::get(Type::getInt32Ty(Ctx));
|
|
|
- Constant* LiteralOne = HlslOP->GetU32Const(1);
|
|
|
- Constant* ElementMask = HlslOP->GetI8Const(1);
|
|
|
-
|
|
|
- Function* StoreFunc = HlslOP->GetOpFunc(OP::OpCode::BufferStore, Type::getInt32Ty(Ctx));
|
|
|
- Constant* StoreOpcode = HlslOP->GetU32Const((unsigned)OP::OpCode::BufferStore);
|
|
|
- (void)Builder.CreateCall(StoreFunc, {
|
|
|
- StoreOpcode, // i32, ; opcode
|
|
|
- m_FunctionToUAVHandle.at(Builder.GetInsertBlock()->getParent()), // %dx.types.Handle, ; resource handle
|
|
|
- OffsetByteIndex, // i32, ; coordinate c0: byte offset
|
|
|
- UndefIntArg, // i32, ; coordinate c1 (unused)
|
|
|
- LiteralOne, // i32, ; value v0
|
|
|
- UndefIntArg, // i32, ; value v1
|
|
|
- UndefIntArg, // i32, ; value v2
|
|
|
- UndefIntArg, // i32, ; value v3
|
|
|
- ElementMask // i8 ; just the first value is used
|
|
|
- });
|
|
|
+
|
|
|
+ unsigned OffsetForAccessType =
|
|
|
+ static_cast<unsigned>(OffsetFromAccess(access) * BytesPerDWORD);
|
|
|
+ auto OffsetByteIndex = Builder.CreateAdd(
|
|
|
+ ByteIndex, HlslOP->GetU32Const(OffsetForAccessType), "OffsetByteIndex");
|
|
|
+
|
|
|
+ UndefValue *UndefIntArg = UndefValue::get(Type::getInt32Ty(Ctx));
|
|
|
+ Constant *LiteralOne = HlslOP->GetU32Const(1);
|
|
|
+ Constant *ElementMask = HlslOP->GetI8Const(1);
|
|
|
+
|
|
|
+ Function *StoreFunc =
|
|
|
+ HlslOP->GetOpFunc(OP::OpCode::BufferStore, Type::getInt32Ty(Ctx));
|
|
|
+ Constant *StoreOpcode =
|
|
|
+ HlslOP->GetU32Const((unsigned)OP::OpCode::BufferStore);
|
|
|
+ (void)Builder.CreateCall(
|
|
|
+ StoreFunc,
|
|
|
+ {
|
|
|
+ StoreOpcode, // i32, ; opcode
|
|
|
+ m_FunctionToUAVHandle.at(
|
|
|
+ Builder.GetInsertBlock()
|
|
|
+ ->getParent()), // %dx.types.Handle, ; resource handle
|
|
|
+ OffsetByteIndex, // i32, ; coordinate c0: byte offset
|
|
|
+ UndefIntArg, // i32, ; coordinate c1 (unused)
|
|
|
+ LiteralOne, // i32, ; value v0
|
|
|
+ UndefIntArg, // i32, ; value v1
|
|
|
+ UndefIntArg, // i32, ; value v2
|
|
|
+ UndefIntArg, // i32, ; value v3
|
|
|
+ ElementMask // i8 ; just the first value is used
|
|
|
+ });
|
|
|
}
|
|
|
|
|
|
-bool DxilShaderAccessTracking::EmitResourceAccess(DxilResourceAndClass &res, Instruction * instruction, OP * HlslOP, LLVMContext & Ctx, ShaderAccessFlags readWrite) {
|
|
|
+bool DxilShaderAccessTracking::EmitResourceAccess(DxilResourceAndClass &res,
|
|
|
+ Instruction *instruction,
|
|
|
+ OP *HlslOP, LLVMContext &Ctx,
|
|
|
+ ShaderAccessFlags readWrite) {
|
|
|
|
|
|
- RegisterTypeAndSpace typeAndSpace{ RegisterTypeFromResourceClass(res.resClass), res.resource->GetSpaceID() };
|
|
|
+ RegisterTypeAndSpace typeAndSpace{RegisterTypeFromResourceClass(res.resClass),
|
|
|
+ res.resource->GetSpaceID()};
|
|
|
|
|
|
auto slot = m_slotAssignments.find(typeAndSpace);
|
|
|
// If the assignment isn't found, we assume it's not accessed
|
|
|
if (slot != m_slotAssignments.end()) {
|
|
|
|
|
|
IRBuilder<> Builder(instruction);
|
|
|
- Value * slotIndex;
|
|
|
+ Value *slotIndex;
|
|
|
|
|
|
if (isa<ConstantInt>(res.index)) {
|
|
|
unsigned index = cast<ConstantInt>(res.index)->getLimitedValue();
|
|
|
if (index > slot->second.numSlots) {
|
|
|
// out-of-range accesses are written to slot zero:
|
|
|
slotIndex = HlslOP->GetU32Const(0);
|
|
|
+ } else {
|
|
|
+ slotIndex = HlslOP->GetU32Const((slot->second.startSlot + index) *
|
|
|
+ DWORDsPerResource * BytesPerDWORD);
|
|
|
}
|
|
|
- else {
|
|
|
- slotIndex = HlslOP->GetU32Const((slot->second.startSlot + index) * DWORDsPerResource * BytesPerDWORD);
|
|
|
- }
|
|
|
- }
|
|
|
- else {
|
|
|
- RSRegisterIdentifier id{ typeAndSpace.Type, typeAndSpace.Space, res.resource->GetID() };
|
|
|
+ } else {
|
|
|
+ RSRegisterIdentifier id{typeAndSpace.Type, typeAndSpace.Space,
|
|
|
+ res.resource->GetID()};
|
|
|
m_DynamicallyIndexedBindPoints.emplace(std::move(id));
|
|
|
|
|
|
-
|
|
|
- // CompareWithSlotLimit will contain 1 if the access is out-of-bounds (both over- and and under-flow
|
|
|
- // via the unsigned >= with slot count)
|
|
|
- auto CompareWithSlotLimit = Builder.CreateICmpUGE(res.index, HlslOP->GetU32Const(slot->second.numSlots), "CompareWithSlotLimit");
|
|
|
- auto CompareWithSlotLimitAsUint = Builder.CreateCast(Instruction::CastOps::ZExt, CompareWithSlotLimit, Type::getInt32Ty(Ctx), "CompareWithSlotLimitAsUint");
|
|
|
-
|
|
|
- // IsInBounds will therefore contain 0 if the access is out-of-bounds, and 1 otherwise.
|
|
|
- auto IsInBounds = Builder.CreateSub(HlslOP->GetU32Const(1), CompareWithSlotLimitAsUint, "IsInBounds");
|
|
|
-
|
|
|
- auto SlotDwordOffset = Builder.CreateAdd(res.index, HlslOP->GetU32Const(slot->second.startSlot), "SlotDwordOffset");
|
|
|
- auto SlotByteOffset = Builder.CreateMul(SlotDwordOffset, HlslOP->GetU32Const(DWORDsPerResource * BytesPerDWORD),"SlotByteOffset");
|
|
|
+ // CompareWithSlotLimit will contain 1 if the access is out-of-bounds
|
|
|
+ // (both over- and and under-flow via the unsigned >= with slot count)
|
|
|
+ auto CompareWithSlotLimit = Builder.CreateICmpUGE(
|
|
|
+ res.index, HlslOP->GetU32Const(slot->second.numSlots),
|
|
|
+ "CompareWithSlotLimit");
|
|
|
+ auto CompareWithSlotLimitAsUint = Builder.CreateCast(
|
|
|
+ Instruction::CastOps::ZExt, CompareWithSlotLimit,
|
|
|
+ Type::getInt32Ty(Ctx), "CompareWithSlotLimitAsUint");
|
|
|
+
|
|
|
+ // IsInBounds will therefore contain 0 if the access is out-of-bounds, and
|
|
|
+ // 1 otherwise.
|
|
|
+ auto IsInBounds = Builder.CreateSub(
|
|
|
+ HlslOP->GetU32Const(1), CompareWithSlotLimitAsUint, "IsInBounds");
|
|
|
+
|
|
|
+ auto SlotDwordOffset = Builder.CreateAdd(
|
|
|
+ res.index, HlslOP->GetU32Const(slot->second.startSlot),
|
|
|
+ "SlotDwordOffset");
|
|
|
+ auto SlotByteOffset = Builder.CreateMul(
|
|
|
+ SlotDwordOffset,
|
|
|
+ HlslOP->GetU32Const(DWORDsPerResource * BytesPerDWORD),
|
|
|
+ "SlotByteOffset");
|
|
|
|
|
|
// This will drive an out-of-bounds access slot down to 0
|
|
|
slotIndex = Builder.CreateMul(SlotByteOffset, IsInBounds, "slotIndex");
|
|
@@ -324,24 +364,23 @@ bool DxilShaderAccessTracking::EmitResourceAccess(DxilResourceAndClass &res, Ins
|
|
|
return false; // did not modify
|
|
|
}
|
|
|
|
|
|
+DxilResourceAndClass GetResourceFromHandle(Value *resHandle, DxilModule &DM) {
|
|
|
|
|
|
-DxilResourceAndClass GetResourceFromHandle(Value * resHandle, DxilModule &DM) {
|
|
|
-
|
|
|
- DxilResourceAndClass ret{ nullptr, nullptr, DXIL::ResourceClass::Invalid };
|
|
|
+ DxilResourceAndClass ret{nullptr, nullptr, DXIL::ResourceClass::Invalid};
|
|
|
|
|
|
CallInst *handle = cast<CallInst>(resHandle);
|
|
|
DxilInst_CreateHandle createHandle(handle);
|
|
|
|
|
|
-
|
|
|
// Dynamic rangeId is not supported - skip and let validation report the
|
|
|
// error.
|
|
|
if (!isa<ConstantInt>(createHandle.get_rangeId()))
|
|
|
return ret;
|
|
|
|
|
|
unsigned rangeId =
|
|
|
- cast<ConstantInt>(createHandle.get_rangeId())->getLimitedValue();
|
|
|
+ cast<ConstantInt>(createHandle.get_rangeId())->getLimitedValue();
|
|
|
|
|
|
- auto resClass = static_cast<DXIL::ResourceClass>(createHandle.get_resourceClass_val());
|
|
|
+ auto resClass =
|
|
|
+ static_cast<DXIL::ResourceClass>(createHandle.get_resourceClass_val());
|
|
|
|
|
|
switch (resClass) {
|
|
|
case DXIL::ResourceClass::SRV:
|
|
@@ -367,12 +406,11 @@ DxilResourceAndClass GetResourceFromHandle(Value * resHandle, DxilModule &DM) {
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
-bool DxilShaderAccessTracking::runOnModule(Module &M)
|
|
|
-{
|
|
|
+bool DxilShaderAccessTracking::runOnModule(Module &M) {
|
|
|
// This pass adds instrumentation for shader access to resources
|
|
|
|
|
|
DxilModule &DM = M.GetOrCreateDxilModule();
|
|
|
- LLVMContext & Ctx = M.getContext();
|
|
|
+ LLVMContext &Ctx = M.getContext();
|
|
|
OP *HlslOP = DM.GetOP();
|
|
|
|
|
|
bool Modified = false;
|
|
@@ -381,13 +419,14 @@ bool DxilShaderAccessTracking::runOnModule(Module &M)
|
|
|
|
|
|
bool FoundDynamicIndexing = false;
|
|
|
|
|
|
- auto CreateHandleFn = HlslOP->GetOpFunc(DXIL::OpCode::CreateHandle, Type::getVoidTy(Ctx));
|
|
|
+ auto CreateHandleFn =
|
|
|
+ HlslOP->GetOpFunc(DXIL::OpCode::CreateHandle, Type::getVoidTy(Ctx));
|
|
|
auto CreateHandleUses = CreateHandleFn->uses();
|
|
|
- for (auto FI = CreateHandleUses.begin(); FI != CreateHandleUses.end(); ) {
|
|
|
- auto & FunctionUse = *FI++;
|
|
|
+ for (auto FI = CreateHandleUses.begin(); FI != CreateHandleUses.end();) {
|
|
|
+ auto &FunctionUse = *FI++;
|
|
|
auto FunctionUser = FunctionUse.getUser();
|
|
|
auto instruction = cast<Instruction>(FunctionUser);
|
|
|
- Value * index = instruction->getOperand(3);
|
|
|
+ Value *index = instruction->getOperand(3);
|
|
|
if (!isa<Constant>(index)) {
|
|
|
FoundDynamicIndexing = true;
|
|
|
break;
|
|
@@ -400,8 +439,7 @@ bool DxilShaderAccessTracking::runOnModule(Module &M)
|
|
|
FOS << "FoundDynamicIndexing";
|
|
|
}
|
|
|
}
|
|
|
- }
|
|
|
- else {
|
|
|
+ } else {
|
|
|
{
|
|
|
if (DM.m_ShaderFlags.GetForceEarlyDepthStencil()) {
|
|
|
if (OSOverride != nullptr) {
|
|
@@ -410,20 +448,24 @@ bool DxilShaderAccessTracking::runOnModule(Module &M)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- for (llvm::Function & F : M.functions()) {
|
|
|
+ for (llvm::Function &F : M.functions()) {
|
|
|
if (!F.getBasicBlockList().empty()) {
|
|
|
IRBuilder<> Builder(F.getEntryBlock().getFirstInsertionPt());
|
|
|
|
|
|
- unsigned int UAVResourceHandle = static_cast<unsigned int>(DM.GetUAVs().size());
|
|
|
+ unsigned int UAVResourceHandle =
|
|
|
+ static_cast<unsigned int>(DM.GetUAVs().size());
|
|
|
|
|
|
// Set up a UAV with structure of a single int
|
|
|
- SmallVector<llvm::Type*, 1> Elements{ Type::getInt32Ty(Ctx) };
|
|
|
- llvm::StructType *UAVStructTy = llvm::StructType::create(Elements, "class.RWStructuredBuffer");
|
|
|
- std::unique_ptr<DxilResource> pUAV = llvm::make_unique<DxilResource>();
|
|
|
+ SmallVector<llvm::Type *, 1> Elements{Type::getInt32Ty(Ctx)};
|
|
|
+ llvm::StructType *UAVStructTy =
|
|
|
+ llvm::StructType::create(Elements, "class.RWStructuredBuffer");
|
|
|
+ std::unique_ptr<DxilResource> pUAV =
|
|
|
+ llvm::make_unique<DxilResource>();
|
|
|
pUAV->SetGlobalName("PIX_CountUAVName");
|
|
|
pUAV->SetGlobalSymbol(UndefValue::get(UAVStructTy->getPointerTo()));
|
|
|
pUAV->SetID(UAVResourceHandle);
|
|
|
- pUAV->SetSpaceID((unsigned int)-2); // This is the reserved-for-tools register space
|
|
|
+ pUAV->SetSpaceID((
|
|
|
+ unsigned int)-2); // This is the reserved-for-tools register space
|
|
|
pUAV->SetSampleCount(1);
|
|
|
pUAV->SetGloballyCoherent(false);
|
|
|
pUAV->SetHasCounter(false);
|
|
@@ -432,12 +474,14 @@ bool DxilShaderAccessTracking::runOnModule(Module &M)
|
|
|
pUAV->SetRangeSize(1);
|
|
|
pUAV->SetKind(DXIL::ResourceKind::RawBuffer);
|
|
|
|
|
|
- auto pAnnotation = DM.GetTypeSystem().GetStructAnnotation(UAVStructTy);
|
|
|
+ auto pAnnotation =
|
|
|
+ DM.GetTypeSystem().GetStructAnnotation(UAVStructTy);
|
|
|
if (pAnnotation == nullptr) {
|
|
|
|
|
|
pAnnotation = DM.GetTypeSystem().AddStructAnnotation(UAVStructTy);
|
|
|
pAnnotation->GetFieldAnnotation(0).SetCBufferOffset(0);
|
|
|
- pAnnotation->GetFieldAnnotation(0).SetCompType(hlsl::DXIL::ComponentType::I32);
|
|
|
+ pAnnotation->GetFieldAnnotation(0).SetCompType(
|
|
|
+ hlsl::DXIL::ComponentType::I32);
|
|
|
pAnnotation->GetFieldAnnotation(0).SetFieldName("count");
|
|
|
}
|
|
|
|
|
@@ -446,28 +490,40 @@ bool DxilShaderAccessTracking::runOnModule(Module &M)
|
|
|
assert((unsigned)ID == UAVResourceHandle);
|
|
|
|
|
|
// Create handle for the newly-added UAV
|
|
|
- Function* CreateHandleOpFunc = HlslOP->GetOpFunc(DXIL::OpCode::CreateHandle, Type::getVoidTy(Ctx));
|
|
|
- Constant* CreateHandleOpcodeArg = HlslOP->GetU32Const((unsigned)DXIL::OpCode::CreateHandle);
|
|
|
- Constant* UAVArg = HlslOP->GetI8Const(static_cast<std::underlying_type<DxilResourceBase::Class>::type>(DXIL::ResourceClass::UAV));
|
|
|
- Constant* MetaDataArg = HlslOP->GetU32Const(ID); // position of the metadata record in the corresponding metadata list
|
|
|
- Constant* IndexArg = HlslOP->GetU32Const(0); //
|
|
|
- Constant* FalseArg = HlslOP->GetI1Const(0); // non-uniform resource index: false
|
|
|
- m_FunctionToUAVHandle[&F] = Builder.CreateCall(CreateHandleOpFunc,
|
|
|
- { CreateHandleOpcodeArg, UAVArg, MetaDataArg, IndexArg, FalseArg }, "PIX_CountUAV_Handle");
|
|
|
+ Function *CreateHandleOpFunc = HlslOP->GetOpFunc(
|
|
|
+ DXIL::OpCode::CreateHandle, Type::getVoidTy(Ctx));
|
|
|
+ Constant *CreateHandleOpcodeArg =
|
|
|
+ HlslOP->GetU32Const((unsigned)DXIL::OpCode::CreateHandle);
|
|
|
+ Constant *UAVArg = HlslOP->GetI8Const(
|
|
|
+ static_cast<std::underlying_type<DxilResourceBase::Class>::type>(
|
|
|
+ DXIL::ResourceClass::UAV));
|
|
|
+ Constant *MetaDataArg =
|
|
|
+ HlslOP->GetU32Const(ID); // position of the metadata record in the
|
|
|
+ // corresponding metadata list
|
|
|
+ Constant *IndexArg = HlslOP->GetU32Const(0); //
|
|
|
+ Constant *FalseArg =
|
|
|
+ HlslOP->GetI1Const(0); // non-uniform resource index: false
|
|
|
+ m_FunctionToUAVHandle[&F] = Builder.CreateCall(
|
|
|
+ CreateHandleOpFunc,
|
|
|
+ {CreateHandleOpcodeArg, UAVArg, MetaDataArg, IndexArg, FalseArg},
|
|
|
+ "PIX_CountUAV_Handle");
|
|
|
}
|
|
|
}
|
|
|
DM.ReEmitDxilResources();
|
|
|
}
|
|
|
|
|
|
- for (llvm::Function & F : M.functions()) {
|
|
|
+ for (llvm::Function &F : M.functions()) {
|
|
|
// Only used DXIL intrinsics:
|
|
|
- if (!F.isDeclaration() || F.isIntrinsic() || F.use_empty() || !OP::IsDxilOpFunc(&F))
|
|
|
+ if (!F.isDeclaration() || F.isIntrinsic() || F.use_empty() ||
|
|
|
+ !OP::IsDxilOpFunc(&F))
|
|
|
continue;
|
|
|
|
|
|
// Gather handle parameter indices, if any
|
|
|
- FunctionType *fnTy = cast<FunctionType>(F.getType()->getPointerElementType());
|
|
|
+ FunctionType *fnTy =
|
|
|
+ cast<FunctionType>(F.getType()->getPointerElementType());
|
|
|
SmallVector<unsigned, 4> handleParams;
|
|
|
- for (unsigned iParam = 1; iParam < fnTy->getFunctionNumParams(); ++iParam) {
|
|
|
+ for (unsigned iParam = 1; iParam < fnTy->getFunctionNumParams();
|
|
|
+ ++iParam) {
|
|
|
if (fnTy->getParamType(iParam) == HlslOP->GetHandleType())
|
|
|
handleParams.push_back(iParam);
|
|
|
}
|
|
@@ -475,13 +531,14 @@ bool DxilShaderAccessTracking::runOnModule(Module &M)
|
|
|
continue;
|
|
|
|
|
|
auto FunctionUses = F.uses();
|
|
|
- for (auto FI = FunctionUses.begin(); FI != FunctionUses.end(); ) {
|
|
|
- auto & FunctionUse = *FI++;
|
|
|
+ for (auto FI = FunctionUses.begin(); FI != FunctionUses.end();) {
|
|
|
+ auto &FunctionUse = *FI++;
|
|
|
auto FunctionUser = FunctionUse.getUser();
|
|
|
auto Call = cast<CallInst>(FunctionUser);
|
|
|
auto opCode = OP::GetDxilOpFuncCallInst(Call);
|
|
|
|
|
|
- // Base Read/Write on function attribute - should match for all normal resource operations
|
|
|
+ // Base Read/Write on function attribute - should match for all normal
|
|
|
+ // resource operations
|
|
|
ShaderAccessFlags readWrite = ShaderAccessFlags::Write;
|
|
|
if (OP::GetMemAccessAttr(opCode) == llvm::Attribute::AttrKind::ReadOnly)
|
|
|
readWrite = ShaderAccessFlags::Read;
|
|
@@ -489,7 +546,8 @@ bool DxilShaderAccessTracking::runOnModule(Module &M)
|
|
|
// Special cases
|
|
|
switch (opCode) {
|
|
|
case DXIL::OpCode::GetDimensions:
|
|
|
- // readWrite = ShaderAccessFlags::DescriptorRead; // TODO: Support GetDimensions
|
|
|
+ // readWrite = ShaderAccessFlags::DescriptorRead; // TODO: Support
|
|
|
+ // GetDimensions
|
|
|
continue;
|
|
|
case DXIL::OpCode::BufferUpdateCounter:
|
|
|
readWrite = ShaderAccessFlags::Counter;
|
|
@@ -497,7 +555,8 @@ bool DxilShaderAccessTracking::runOnModule(Module &M)
|
|
|
case DXIL::OpCode::TraceRay:
|
|
|
case DXIL::OpCode::RayQuery_TraceRayInline:
|
|
|
// Read of AccelerationStructure; doesn't match function attribute
|
|
|
- // readWrite = ShaderAccessFlags::Read; // TODO: Support TraceRay[Inline]
|
|
|
+ // readWrite = ShaderAccessFlags::Read; // TODO: Support
|
|
|
+ // TraceRay[Inline]
|
|
|
continue;
|
|
|
default:
|
|
|
break;
|
|
@@ -506,7 +565,8 @@ bool DxilShaderAccessTracking::runOnModule(Module &M)
|
|
|
for (unsigned iParam : handleParams) {
|
|
|
auto res = GetResourceFromHandle(Call->getArgOperand(iParam), DM);
|
|
|
// Don't instrument the accesses to the UAV that we just added
|
|
|
- if (res.resClass == DXIL::ResourceClass::UAV && res.resource->GetSpaceID() == (unsigned)-2) {
|
|
|
+ if (res.resClass == DXIL::ResourceClass::UAV &&
|
|
|
+ res.resource->GetSpaceID() == (unsigned)-2) {
|
|
|
break;
|
|
|
}
|
|
|
if (EmitResourceAccess(res, Call, HlslOP, Ctx, readWrite)) {
|
|
@@ -521,8 +581,9 @@ bool DxilShaderAccessTracking::runOnModule(Module &M)
|
|
|
if (OSOverride != nullptr) {
|
|
|
formatted_raw_ostream FOS(*OSOverride);
|
|
|
FOS << "DynamicallyIndexedBindPoints=";
|
|
|
- for (auto const & bp : m_DynamicallyIndexedBindPoints) {
|
|
|
- FOS << EncodeRegisterType(bp.Type) << bp.Space << ':' << bp.Index <<';';
|
|
|
+ for (auto const &bp : m_DynamicallyIndexedBindPoints) {
|
|
|
+ FOS << EncodeRegisterType(bp.Type) << bp.Space << ':' << bp.Index
|
|
|
+ << ';';
|
|
|
}
|
|
|
FOS << ".";
|
|
|
}
|
|
@@ -537,4 +598,6 @@ ModulePass *llvm::createDxilShaderAccessTrackingPass() {
|
|
|
return new DxilShaderAccessTracking();
|
|
|
}
|
|
|
|
|
|
-INITIALIZE_PASS(DxilShaderAccessTracking, "hlsl-dxil-pix-shader-access-instrumentation", "HLSL DXIL shader access tracking for PIX", false, false)
|
|
|
+INITIALIZE_PASS(DxilShaderAccessTracking,
|
|
|
+ "hlsl-dxil-pix-shader-access-instrumentation",
|
|
|
+ "HLSL DXIL shader access tracking for PIX", false, false)
|