DxilShaderAccessTracking.cpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615
  1. ///////////////////////////////////////////////////////////////////////////////
  2. // //
  3. // DxilShaderAccessTracking.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 pixel hit count and //
  9. // cost. Used by PIX. //
  10. // //
  11. ///////////////////////////////////////////////////////////////////////////////
  12. #include "dxc/DXIL/DxilOperations.h"
  13. #include "dxc/DXIL/DxilInstructions.h"
  14. #include "dxc/DXIL/DxilModule.h"
  15. #include "dxc/DxilPIXPasses/DxilPIXPasses.h"
  16. #include "dxc/HLSL/DxilGenerationPass.h"
  17. #include "dxc/HLSL/DxilSpanAllocator.h"
  18. #include "llvm/IR/PassManager.h"
  19. #include "llvm/Support/FormattedStream.h"
  20. #include "llvm/Transforms/Utils/Local.h"
  21. #include <deque>
  22. #ifdef _WIN32
  23. #include <winerror.h>
  24. #endif
  25. using namespace llvm;
  26. using namespace hlsl;
  27. void ThrowIf(bool a) {
  28. if (a) {
  29. throw ::hlsl::Exception(E_INVALIDARG);
  30. }
  31. }
  32. //---------------------------------------------------------------------------------------------------------------------------------
  33. // These types are taken from PIX's ShaderAccessHelpers.h
  34. enum class ShaderAccessFlags : uint32_t {
  35. None = 0,
  36. Read = 1 << 0,
  37. Write = 1 << 1,
  38. // "Counter" access is only applicable to UAVs; it means the counter buffer
  39. // attached to the UAV was accessed, but not necessarily the UAV resource.
  40. Counter = 1 << 2,
  41. // Descriptor-only read (if any), but not the resource contents (if any).
  42. // Used for GetDimensions, samplers, and secondary texture for sampler
  43. // feedback.
  44. // TODO: Make this a unique value if supported in PIX, then enable
  45. // GetDimensions
  46. DescriptorRead = 1 << 0,
  47. };
  48. constexpr uint32_t DWORDsPerResource = 3;
  49. constexpr uint32_t BytesPerDWORD = 4;
  50. static uint32_t OffsetFromAccess(ShaderAccessFlags access) {
  51. switch (access) {
  52. case ShaderAccessFlags::Read:
  53. return 0;
  54. case ShaderAccessFlags::Write:
  55. return 1;
  56. case ShaderAccessFlags::Counter:
  57. return 2;
  58. default:
  59. throw ::hlsl::Exception(E_INVALIDARG);
  60. }
  61. }
  62. // This enum doesn't have to match PIX's version, because the values are
  63. // received from PIX encoded in ASCII. However, for ease of comparing this code
  64. // with PIX, and to be less confusing to future maintainers, this enum does
  65. // indeed match the same-named enum in PIX.
  66. enum class RegisterType {
  67. CBV,
  68. SRV,
  69. UAV,
  70. RTV, // not used.
  71. DSV, // not used.
  72. Sampler,
  73. SOV, // not used.
  74. Invalid,
  75. Terminator
  76. };
  77. RegisterType RegisterTypeFromResourceClass(DXIL::ResourceClass c) {
  78. switch (c) {
  79. case DXIL::ResourceClass::SRV:
  80. return RegisterType::SRV;
  81. break;
  82. case DXIL::ResourceClass::UAV:
  83. return RegisterType::UAV;
  84. break;
  85. case DXIL::ResourceClass::CBuffer:
  86. return RegisterType::CBV;
  87. break;
  88. case DXIL::ResourceClass::Sampler:
  89. return RegisterType::Sampler;
  90. break;
  91. case DXIL::ResourceClass::Invalid:
  92. return RegisterType::Invalid;
  93. break;
  94. default:
  95. ThrowIf(true);
  96. return RegisterType::Invalid;
  97. }
  98. }
  99. struct RegisterTypeAndSpace {
  100. bool operator<(const RegisterTypeAndSpace &o) const {
  101. return static_cast<int>(Type) < static_cast<int>(o.Type) ||
  102. (static_cast<int>(Type) == static_cast<int>(o.Type) &&
  103. Space < o.Space);
  104. }
  105. RegisterType Type;
  106. unsigned Space;
  107. };
  108. // Identifies a bind point as defined by the root signature
  109. struct RSRegisterIdentifier {
  110. RegisterType Type;
  111. unsigned Space;
  112. unsigned Index;
  113. bool operator<(const RSRegisterIdentifier &o) const {
  114. return static_cast<unsigned>(Type) < static_cast<unsigned>(o.Type) &&
  115. Space < o.Space && Index < o.Index;
  116. }
  117. };
  118. struct SlotRange {
  119. unsigned startSlot;
  120. unsigned numSlots;
  121. // Number of slots needed if no descriptors from unbounded ranges are included
  122. unsigned numInvariableSlots;
  123. };
  124. struct DxilResourceAndClass {
  125. DxilResourceBase *resource;
  126. Value *index;
  127. DXIL::ResourceClass resClass;
  128. };
  129. //---------------------------------------------------------------------------------------------------------------------------------
  130. class DxilShaderAccessTracking : public ModulePass {
  131. public:
  132. static char ID; // Pass identification, replacement for typeid
  133. explicit DxilShaderAccessTracking() : ModulePass(ID) {}
  134. const char *getPassName() const override {
  135. return "DXIL shader access tracking";
  136. }
  137. bool runOnModule(Module &M) override;
  138. void applyOptions(PassOptions O) override;
  139. private:
  140. void EmitAccess(LLVMContext &Ctx, OP *HlslOP, IRBuilder<> &, Value *slot,
  141. ShaderAccessFlags access);
  142. bool EmitResourceAccess(DxilResourceAndClass &res, Instruction *instruction,
  143. OP *HlslOP, LLVMContext &Ctx,
  144. ShaderAccessFlags readWrite);
  145. private:
  146. bool m_CheckForDynamicIndexing = false;
  147. std::map<RegisterTypeAndSpace, SlotRange> m_slotAssignments;
  148. std::map<llvm::Function *, CallInst *> m_FunctionToUAVHandle;
  149. std::set<RSRegisterIdentifier> m_DynamicallyIndexedBindPoints;
  150. };
  151. static unsigned DeserializeInt(std::deque<char> &q) {
  152. unsigned i = 0;
  153. while (!q.empty() && isdigit(q.front())) {
  154. i *= 10;
  155. i += q.front() - '0';
  156. q.pop_front();
  157. }
  158. return i;
  159. }
  160. static char DequeFront(std::deque<char> &q) {
  161. ThrowIf(q.empty());
  162. auto c = q.front();
  163. q.pop_front();
  164. return c;
  165. }
  166. static RegisterType ParseRegisterType(std::deque<char> &q) {
  167. switch (DequeFront(q)) {
  168. case 'C':
  169. return RegisterType::CBV;
  170. case 'S':
  171. return RegisterType::SRV;
  172. case 'U':
  173. return RegisterType::UAV;
  174. case 'M':
  175. return RegisterType::Sampler;
  176. case 'I':
  177. return RegisterType::Invalid;
  178. default:
  179. return RegisterType::Terminator;
  180. }
  181. }
  182. static char EncodeRegisterType(RegisterType r) {
  183. switch (r) {
  184. case RegisterType::CBV:
  185. return 'C';
  186. case RegisterType::SRV:
  187. return 'S';
  188. case RegisterType::UAV:
  189. return 'U';
  190. case RegisterType::Sampler:
  191. return 'M';
  192. case RegisterType::Invalid:
  193. return 'I';
  194. }
  195. return '.';
  196. }
  197. static void ValidateDelimiter(std::deque<char> &q, char d) {
  198. ThrowIf(q.front() != d);
  199. q.pop_front();
  200. }
  201. void DxilShaderAccessTracking::applyOptions(PassOptions O) {
  202. int checkForDynamic;
  203. GetPassOptionInt(O, "checkForDynamicIndexing", &checkForDynamic, 0);
  204. m_CheckForDynamicIndexing = checkForDynamic != 0;
  205. StringRef configOption;
  206. if (GetPassOption(O, "config", &configOption)) {
  207. std::deque<char> config;
  208. config.assign(configOption.begin(), configOption.end());
  209. // Parse slot assignments. Compare with PIX's ShaderAccessHelpers.cpp
  210. // (TrackingConfiguration::SerializedRepresentation)
  211. RegisterType rt = ParseRegisterType(config);
  212. while (rt != RegisterType::Terminator) {
  213. RegisterTypeAndSpace rst;
  214. rst.Type = rt;
  215. rst.Space = DeserializeInt(config);
  216. ValidateDelimiter(config, ':');
  217. SlotRange sr;
  218. sr.startSlot = DeserializeInt(config);
  219. ValidateDelimiter(config, ':');
  220. sr.numSlots = DeserializeInt(config);
  221. ValidateDelimiter(config, 'i');
  222. sr.numInvariableSlots = DeserializeInt(config);
  223. ValidateDelimiter(config, ';');
  224. m_slotAssignments[rst] = sr;
  225. rt = ParseRegisterType(config);
  226. }
  227. }
  228. }
  229. void DxilShaderAccessTracking::EmitAccess(LLVMContext &Ctx, OP *HlslOP,
  230. IRBuilder<> &Builder,
  231. Value *ByteIndex,
  232. ShaderAccessFlags access) {
  233. unsigned OffsetForAccessType =
  234. static_cast<unsigned>(OffsetFromAccess(access) * BytesPerDWORD);
  235. auto OffsetByteIndex = Builder.CreateAdd(
  236. ByteIndex, HlslOP->GetU32Const(OffsetForAccessType), "OffsetByteIndex");
  237. UndefValue *UndefIntArg = UndefValue::get(Type::getInt32Ty(Ctx));
  238. Constant *LiteralOne = HlslOP->GetU32Const(1);
  239. Constant *ElementMask = HlslOP->GetI8Const(1);
  240. Function *StoreFunc =
  241. HlslOP->GetOpFunc(OP::OpCode::BufferStore, Type::getInt32Ty(Ctx));
  242. Constant *StoreOpcode =
  243. HlslOP->GetU32Const((unsigned)OP::OpCode::BufferStore);
  244. (void)Builder.CreateCall(
  245. StoreFunc,
  246. {
  247. StoreOpcode, // i32, ; opcode
  248. m_FunctionToUAVHandle.at(
  249. Builder.GetInsertBlock()
  250. ->getParent()), // %dx.types.Handle, ; resource handle
  251. OffsetByteIndex, // i32, ; coordinate c0: byte offset
  252. UndefIntArg, // i32, ; coordinate c1 (unused)
  253. LiteralOne, // i32, ; value v0
  254. UndefIntArg, // i32, ; value v1
  255. UndefIntArg, // i32, ; value v2
  256. UndefIntArg, // i32, ; value v3
  257. ElementMask // i8 ; just the first value is used
  258. });
  259. }
  260. bool DxilShaderAccessTracking::EmitResourceAccess(DxilResourceAndClass &res,
  261. Instruction *instruction,
  262. OP *HlslOP, LLVMContext &Ctx,
  263. ShaderAccessFlags readWrite) {
  264. RegisterTypeAndSpace typeAndSpace{RegisterTypeFromResourceClass(res.resClass),
  265. res.resource->GetSpaceID()};
  266. auto slot = m_slotAssignments.find(typeAndSpace);
  267. // If the assignment isn't found, we assume it's not accessed
  268. if (slot != m_slotAssignments.end()) {
  269. IRBuilder<> Builder(instruction);
  270. Value *slotIndex;
  271. if (isa<ConstantInt>(res.index)) {
  272. unsigned index = cast<ConstantInt>(res.index)->getLimitedValue();
  273. if (index > slot->second.numSlots) {
  274. // out-of-range accesses are written to slot zero:
  275. slotIndex = HlslOP->GetU32Const(0);
  276. } else {
  277. slotIndex = HlslOP->GetU32Const((slot->second.startSlot + index) *
  278. DWORDsPerResource * BytesPerDWORD);
  279. }
  280. } else {
  281. RSRegisterIdentifier id{typeAndSpace.Type, typeAndSpace.Space,
  282. res.resource->GetID()};
  283. m_DynamicallyIndexedBindPoints.emplace(std::move(id));
  284. // CompareWithSlotLimit will contain 1 if the access is out-of-bounds
  285. // (both over- and and under-flow via the unsigned >= with slot count)
  286. auto CompareWithSlotLimit = Builder.CreateICmpUGE(
  287. res.index, HlslOP->GetU32Const(slot->second.numSlots),
  288. "CompareWithSlotLimit");
  289. auto CompareWithSlotLimitAsUint = Builder.CreateCast(
  290. Instruction::CastOps::ZExt, CompareWithSlotLimit,
  291. Type::getInt32Ty(Ctx), "CompareWithSlotLimitAsUint");
  292. // IsInBounds will therefore contain 0 if the access is out-of-bounds, and
  293. // 1 otherwise.
  294. auto IsInBounds = Builder.CreateSub(
  295. HlslOP->GetU32Const(1), CompareWithSlotLimitAsUint, "IsInBounds");
  296. auto SlotDwordOffset = Builder.CreateAdd(
  297. res.index, HlslOP->GetU32Const(slot->second.startSlot),
  298. "SlotDwordOffset");
  299. auto SlotByteOffset = Builder.CreateMul(
  300. SlotDwordOffset,
  301. HlslOP->GetU32Const(DWORDsPerResource * BytesPerDWORD),
  302. "SlotByteOffset");
  303. // This will drive an out-of-bounds access slot down to 0
  304. slotIndex = Builder.CreateMul(SlotByteOffset, IsInBounds, "slotIndex");
  305. }
  306. EmitAccess(Ctx, HlslOP, Builder, slotIndex, readWrite);
  307. return true; // did modify
  308. }
  309. return false; // did not modify
  310. }
  311. DxilResourceAndClass GetResourceFromHandle(Value *resHandle, DxilModule &DM) {
  312. DxilResourceAndClass ret{nullptr, nullptr, DXIL::ResourceClass::Invalid};
  313. CallInst *handle = cast<CallInst>(resHandle);
  314. DxilInst_CreateHandle createHandle(handle);
  315. // Dynamic rangeId is not supported - skip and let validation report the
  316. // error.
  317. if (!isa<ConstantInt>(createHandle.get_rangeId()))
  318. return ret;
  319. unsigned rangeId =
  320. cast<ConstantInt>(createHandle.get_rangeId())->getLimitedValue();
  321. auto resClass =
  322. static_cast<DXIL::ResourceClass>(createHandle.get_resourceClass_val());
  323. switch (resClass) {
  324. case DXIL::ResourceClass::SRV:
  325. ret.resource = &DM.GetSRV(rangeId);
  326. break;
  327. case DXIL::ResourceClass::UAV:
  328. ret.resource = &DM.GetUAV(rangeId);
  329. break;
  330. case DXIL::ResourceClass::CBuffer:
  331. ret.resource = &DM.GetCBuffer(rangeId);
  332. break;
  333. case DXIL::ResourceClass::Sampler:
  334. ret.resource = &DM.GetSampler(rangeId);
  335. break;
  336. default:
  337. DXASSERT(0, "invalid res class");
  338. return ret;
  339. }
  340. ret.index = createHandle.get_index();
  341. ret.resClass = resClass;
  342. return ret;
  343. }
  344. bool DxilShaderAccessTracking::runOnModule(Module &M) {
  345. // This pass adds instrumentation for shader access to resources
  346. DxilModule &DM = M.GetOrCreateDxilModule();
  347. LLVMContext &Ctx = M.getContext();
  348. OP *HlslOP = DM.GetOP();
  349. bool Modified = false;
  350. if (m_CheckForDynamicIndexing) {
  351. bool FoundDynamicIndexing = false;
  352. auto CreateHandleFn =
  353. HlslOP->GetOpFunc(DXIL::OpCode::CreateHandle, Type::getVoidTy(Ctx));
  354. auto CreateHandleUses = CreateHandleFn->uses();
  355. for (auto FI = CreateHandleUses.begin(); FI != CreateHandleUses.end();) {
  356. auto &FunctionUse = *FI++;
  357. auto FunctionUser = FunctionUse.getUser();
  358. auto instruction = cast<Instruction>(FunctionUser);
  359. Value *index = instruction->getOperand(3);
  360. if (!isa<Constant>(index)) {
  361. FoundDynamicIndexing = true;
  362. break;
  363. }
  364. }
  365. if (FoundDynamicIndexing) {
  366. if (OSOverride != nullptr) {
  367. formatted_raw_ostream FOS(*OSOverride);
  368. FOS << "FoundDynamicIndexing";
  369. }
  370. }
  371. } else {
  372. {
  373. if (DM.m_ShaderFlags.GetForceEarlyDepthStencil()) {
  374. if (OSOverride != nullptr) {
  375. formatted_raw_ostream FOS(*OSOverride);
  376. FOS << "ShouldAssumeDsvAccess";
  377. }
  378. }
  379. for (llvm::Function &F : M.functions()) {
  380. if (!F.getBasicBlockList().empty()) {
  381. IRBuilder<> Builder(F.getEntryBlock().getFirstInsertionPt());
  382. unsigned int UAVResourceHandle =
  383. static_cast<unsigned int>(DM.GetUAVs().size());
  384. // Set up a UAV with structure of a single int
  385. SmallVector<llvm::Type *, 1> Elements{Type::getInt32Ty(Ctx)};
  386. llvm::StructType *UAVStructTy =
  387. llvm::StructType::create(Elements, "class.RWStructuredBuffer");
  388. std::unique_ptr<DxilResource> pUAV =
  389. llvm::make_unique<DxilResource>();
  390. pUAV->SetGlobalName("PIX_CountUAVName");
  391. pUAV->SetGlobalSymbol(UndefValue::get(UAVStructTy->getPointerTo()));
  392. pUAV->SetID(UAVResourceHandle);
  393. pUAV->SetSpaceID((
  394. unsigned int)-2); // This is the reserved-for-tools register space
  395. pUAV->SetSampleCount(1);
  396. pUAV->SetGloballyCoherent(false);
  397. pUAV->SetHasCounter(false);
  398. pUAV->SetCompType(CompType::getI32());
  399. pUAV->SetLowerBound(0);
  400. pUAV->SetRangeSize(1);
  401. pUAV->SetKind(DXIL::ResourceKind::RawBuffer);
  402. auto pAnnotation =
  403. DM.GetTypeSystem().GetStructAnnotation(UAVStructTy);
  404. if (pAnnotation == nullptr) {
  405. pAnnotation = DM.GetTypeSystem().AddStructAnnotation(UAVStructTy);
  406. pAnnotation->GetFieldAnnotation(0).SetCBufferOffset(0);
  407. pAnnotation->GetFieldAnnotation(0).SetCompType(
  408. hlsl::DXIL::ComponentType::I32);
  409. pAnnotation->GetFieldAnnotation(0).SetFieldName("count");
  410. }
  411. ID = DM.AddUAV(std::move(pUAV));
  412. assert((unsigned)ID == UAVResourceHandle);
  413. // Create handle for the newly-added UAV
  414. Function *CreateHandleOpFunc = HlslOP->GetOpFunc(
  415. DXIL::OpCode::CreateHandle, Type::getVoidTy(Ctx));
  416. Constant *CreateHandleOpcodeArg =
  417. HlslOP->GetU32Const((unsigned)DXIL::OpCode::CreateHandle);
  418. Constant *UAVArg = HlslOP->GetI8Const(
  419. static_cast<std::underlying_type<DxilResourceBase::Class>::type>(
  420. DXIL::ResourceClass::UAV));
  421. Constant *MetaDataArg =
  422. HlslOP->GetU32Const(ID); // position of the metadata record in the
  423. // corresponding metadata list
  424. Constant *IndexArg = HlslOP->GetU32Const(0); //
  425. Constant *FalseArg =
  426. HlslOP->GetI1Const(0); // non-uniform resource index: false
  427. m_FunctionToUAVHandle[&F] = Builder.CreateCall(
  428. CreateHandleOpFunc,
  429. {CreateHandleOpcodeArg, UAVArg, MetaDataArg, IndexArg, FalseArg},
  430. "PIX_CountUAV_Handle");
  431. }
  432. }
  433. DM.ReEmitDxilResources();
  434. }
  435. for (llvm::Function &F : M.functions()) {
  436. // Only used DXIL intrinsics:
  437. if (!F.isDeclaration() || F.isIntrinsic() || F.use_empty() ||
  438. !OP::IsDxilOpFunc(&F))
  439. continue;
  440. // Gather handle parameter indices, if any
  441. FunctionType *fnTy =
  442. cast<FunctionType>(F.getType()->getPointerElementType());
  443. SmallVector<unsigned, 4> handleParams;
  444. for (unsigned iParam = 1; iParam < fnTy->getFunctionNumParams();
  445. ++iParam) {
  446. if (fnTy->getParamType(iParam) == HlslOP->GetHandleType())
  447. handleParams.push_back(iParam);
  448. }
  449. if (handleParams.empty())
  450. continue;
  451. auto FunctionUses = F.uses();
  452. for (auto FI = FunctionUses.begin(); FI != FunctionUses.end();) {
  453. auto &FunctionUse = *FI++;
  454. auto FunctionUser = FunctionUse.getUser();
  455. auto Call = cast<CallInst>(FunctionUser);
  456. auto opCode = OP::GetDxilOpFuncCallInst(Call);
  457. // Base Read/Write on function attribute - should match for all normal
  458. // resource operations
  459. ShaderAccessFlags readWrite = ShaderAccessFlags::Write;
  460. if (OP::GetMemAccessAttr(opCode) == llvm::Attribute::AttrKind::ReadOnly)
  461. readWrite = ShaderAccessFlags::Read;
  462. // Special cases
  463. switch (opCode) {
  464. case DXIL::OpCode::GetDimensions:
  465. // readWrite = ShaderAccessFlags::DescriptorRead; // TODO: Support
  466. // GetDimensions
  467. continue;
  468. case DXIL::OpCode::BufferUpdateCounter:
  469. readWrite = ShaderAccessFlags::Counter;
  470. break;
  471. case DXIL::OpCode::TraceRay:
  472. // Read of AccelerationStructure; doesn't match function attribute
  473. // readWrite = ShaderAccessFlags::Read; // TODO: Support
  474. continue;
  475. case DXIL::OpCode::RayQuery_TraceRayInline: {
  476. // Read of AccelerationStructure; doesn't match function attribute
  477. auto res = GetResourceFromHandle(Call->getArgOperand(2), DM);
  478. if (EmitResourceAccess(
  479. res,
  480. Call,
  481. HlslOP,
  482. Ctx,
  483. ShaderAccessFlags::Read))
  484. {
  485. Modified = true;
  486. }
  487. }
  488. continue;
  489. default:
  490. break;
  491. }
  492. for (unsigned iParam : handleParams) {
  493. auto res = GetResourceFromHandle(Call->getArgOperand(iParam), DM);
  494. // Don't instrument the accesses to the UAV that we just added
  495. if (res.resClass == DXIL::ResourceClass::UAV &&
  496. res.resource->GetSpaceID() == (unsigned)-2) {
  497. break;
  498. }
  499. if (EmitResourceAccess(res, Call, HlslOP, Ctx, readWrite)) {
  500. Modified = true;
  501. }
  502. // Remaining resources are DescriptorRead.
  503. readWrite = ShaderAccessFlags::DescriptorRead;
  504. }
  505. }
  506. }
  507. if (OSOverride != nullptr) {
  508. formatted_raw_ostream FOS(*OSOverride);
  509. FOS << "DynamicallyIndexedBindPoints=";
  510. for (auto const &bp : m_DynamicallyIndexedBindPoints) {
  511. FOS << EncodeRegisterType(bp.Type) << bp.Space << ':' << bp.Index
  512. << ';';
  513. }
  514. FOS << ".";
  515. }
  516. }
  517. return Modified;
  518. }
  519. char DxilShaderAccessTracking::ID = 0;
  520. ModulePass *llvm::createDxilShaderAccessTrackingPass() {
  521. return new DxilShaderAccessTracking();
  522. }
  523. INITIALIZE_PASS(DxilShaderAccessTracking,
  524. "hlsl-dxil-pix-shader-access-instrumentation",
  525. "HLSL DXIL shader access tracking for PIX", false, false)