DxilShaderAccessTracking.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522
  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/HLSL/DxilGenerationPass.h"
  13. #include "dxc/HLSL/DxilOperations.h"
  14. #include "dxc/HLSL/DxilInstructions.h"
  15. #include "dxc/HLSL/DxilModule.h"
  16. #include "dxc/HLSL/DxilPIXPasses.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. {
  29. if (a) {
  30. throw ::hlsl::Exception(E_INVALIDARG);
  31. }
  32. }
  33. //---------------------------------------------------------------------------------------------------------------------------------
  34. // These types are taken from PIX's ShaderAccessHelpers.h
  35. enum class ShaderAccessFlags : uint32_t
  36. {
  37. None = 0,
  38. Read = 1 << 0,
  39. Write = 1 << 1,
  40. // "Counter" access is only applicable to UAVs; it means the counter buffer attached to the UAV
  41. // was accessed, but not necessarily the UAV resource.
  42. Counter = 1 << 2
  43. };
  44. // This enum doesn't have to match PIX's version, because the values are received from PIX encoded in ASCII.
  45. // However, for ease of comparing this code with PIX, and to be less confusing to future maintainers, this
  46. // enum does indeed match the same-named enum in PIX.
  47. enum class RegisterType
  48. {
  49. CBV,
  50. SRV,
  51. UAV,
  52. RTV, // not used.
  53. DSV, // not used.
  54. Sampler,
  55. SOV, // not used.
  56. Invalid,
  57. Terminator
  58. };
  59. RegisterType RegisterTypeFromResourceClass(DXIL::ResourceClass c) {
  60. switch (c)
  61. {
  62. case DXIL::ResourceClass::SRV : return RegisterType::SRV ; break;
  63. case DXIL::ResourceClass::UAV : return RegisterType::UAV ; break;
  64. case DXIL::ResourceClass::CBuffer: return RegisterType::CBV ; break;
  65. case DXIL::ResourceClass::Sampler: return RegisterType::Sampler; break;
  66. case DXIL::ResourceClass::Invalid: return RegisterType::Invalid; break;
  67. default:
  68. ThrowIf(true);
  69. return RegisterType::Invalid;
  70. }
  71. }
  72. struct RegisterTypeAndSpace
  73. {
  74. bool operator < (const RegisterTypeAndSpace & o) const {
  75. return static_cast<int>(Type) < static_cast<int>(o.Type) ||
  76. (static_cast<int>(Type) == static_cast<int>(o.Type) && Space < o.Space);
  77. }
  78. RegisterType Type;
  79. unsigned Space;
  80. };
  81. // Identifies a bind point as defined by the root signature
  82. struct RSRegisterIdentifier
  83. {
  84. RegisterType Type;
  85. unsigned Space;
  86. unsigned Index;
  87. bool operator < (const RSRegisterIdentifier & o) const {
  88. return
  89. static_cast<unsigned>(Type) < static_cast<unsigned>(o.Type) &&
  90. Space < o.Space &&
  91. Index < o.Index;
  92. }
  93. };
  94. struct SlotRange
  95. {
  96. unsigned startSlot;
  97. unsigned numSlots;
  98. // Number of slots needed if no descriptors from unbounded ranges are included
  99. unsigned numInvariableSlots;
  100. };
  101. struct DxilResourceAndClass {
  102. DxilResourceBase * resource;
  103. Value * index;
  104. DXIL::ResourceClass resClass;
  105. };
  106. //---------------------------------------------------------------------------------------------------------------------------------
  107. class DxilShaderAccessTracking : public ModulePass {
  108. public:
  109. static char ID; // Pass identification, replacement for typeid
  110. explicit DxilShaderAccessTracking() : ModulePass(ID) {}
  111. const char *getPassName() const override { return "DXIL shader access tracking"; }
  112. bool runOnModule(Module &M) override;
  113. void applyOptions(PassOptions O) override;
  114. private:
  115. void EmitAccess(LLVMContext & Ctx, OP *HlslOP, IRBuilder<> &, Value *slot, ShaderAccessFlags access);
  116. bool EmitResourceAccess(DxilResourceAndClass &res, Instruction * instruction, OP * HlslOP, LLVMContext & Ctx, ShaderAccessFlags readWrite);
  117. private:
  118. bool m_CheckForDynamicIndexing = false;
  119. std::map<RegisterTypeAndSpace, SlotRange> m_slotAssignments;
  120. CallInst *m_HandleForUAV;
  121. std::set<RSRegisterIdentifier> m_DynamicallyIndexedBindPoints;
  122. };
  123. static unsigned DeserializeInt(std::deque<char> & q) {
  124. unsigned i = 0;
  125. while(!q.empty() && isdigit(q.front()))
  126. {
  127. i *= 10;
  128. i += q.front() - '0';
  129. q.pop_front();
  130. }
  131. return i;
  132. }
  133. static char DequeFront(std::deque<char> & q) {
  134. ThrowIf(q.empty());
  135. auto c = q.front();
  136. q.pop_front();
  137. return c;
  138. }
  139. static RegisterType ParseRegisterType(std::deque<char> & q) {
  140. switch (DequeFront(q))
  141. {
  142. case 'C': return RegisterType::CBV;
  143. case 'S': return RegisterType::SRV;
  144. case 'U': return RegisterType::UAV;
  145. case 'M': return RegisterType::Sampler;
  146. case 'I': return RegisterType::Invalid;
  147. default: return RegisterType::Terminator;
  148. }
  149. }
  150. static char EncodeRegisterType(RegisterType r) {
  151. switch (r)
  152. {
  153. case RegisterType::CBV: return 'C';
  154. case RegisterType::SRV: return 'S';
  155. case RegisterType::UAV: return 'U';
  156. case RegisterType::Sampler: return 'M';
  157. case RegisterType::Invalid: return 'I';
  158. }
  159. return '.';
  160. }
  161. static void ValidateDelimiter(std::deque<char> & q, char d) {
  162. ThrowIf(q.front() != d);
  163. q.pop_front();
  164. }
  165. void DxilShaderAccessTracking::applyOptions(PassOptions O) {
  166. int checkForDynamic;
  167. GetPassOptionInt(O, "checkForDynamicIndexing", &checkForDynamic, 0);
  168. m_CheckForDynamicIndexing = checkForDynamic != 0;
  169. StringRef configOption;
  170. if (GetPassOption(O, "config", &configOption)) {
  171. std::deque<char> config;
  172. config.assign(configOption.begin(), configOption.end());
  173. // Parse slot assignments. Compare with PIX's ShaderAccessHelpers.cpp (TrackingConfiguration::SerializedRepresentation)
  174. RegisterType rt = ParseRegisterType(config);
  175. while (rt != RegisterType::Terminator) {
  176. RegisterTypeAndSpace rst;
  177. rst.Type = rt;
  178. rst.Space = DeserializeInt(config);
  179. ValidateDelimiter(config, ':');
  180. SlotRange sr;
  181. sr.startSlot = DeserializeInt(config);
  182. ValidateDelimiter(config, ':');
  183. sr.numSlots = DeserializeInt(config);
  184. ValidateDelimiter(config, 'i');
  185. sr.numInvariableSlots = DeserializeInt(config);
  186. ValidateDelimiter(config, ';');
  187. m_slotAssignments[rst] = sr;
  188. rt = ParseRegisterType(config);
  189. }
  190. }
  191. }
  192. void DxilShaderAccessTracking::EmitAccess(LLVMContext & Ctx, OP *HlslOP, IRBuilder<> & Builder, Value * slot, ShaderAccessFlags access)
  193. {
  194. // Slots are four bytes each:
  195. auto ByteIndex = Builder.CreateMul(slot, HlslOP->GetU32Const(4));
  196. // Insert the UAV increment instruction:
  197. Function* AtomicOpFunc = HlslOP->GetOpFunc(OP::OpCode::AtomicBinOp, Type::getInt32Ty(Ctx));
  198. Constant* AtomicBinOpcode = HlslOP->GetU32Const((unsigned)OP::OpCode::AtomicBinOp);
  199. Constant* AtomicOr = HlslOP->GetU32Const((unsigned)DXIL::AtomicBinOpCode::Or);
  200. Constant* AccessValue = HlslOP->GetU32Const(static_cast<unsigned>(access));
  201. UndefValue* UndefArg = UndefValue::get(Type::getInt32Ty(Ctx));
  202. (void)Builder.CreateCall(AtomicOpFunc, {
  203. AtomicBinOpcode,// i32, ; opcode
  204. m_HandleForUAV, // %dx.types.Handle, ; resource handle
  205. AtomicOr, // i32, ; binary operation code : EXCHANGE, IADD, AND, OR, XOR, IMIN, IMAX, UMIN, UMAX
  206. ByteIndex, // i32, ; coordinate c0: byte offset
  207. UndefArg, // i32, ; coordinate c1 (unused)
  208. UndefArg, // i32, ; coordinate c2 (unused)
  209. AccessValue // i32) ; OR value
  210. }, "UAVOrResult");
  211. }
  212. bool DxilShaderAccessTracking::EmitResourceAccess(DxilResourceAndClass &res, Instruction * instruction, OP * HlslOP, LLVMContext & Ctx, ShaderAccessFlags readWrite) {
  213. RegisterTypeAndSpace typeAndSpace{ RegisterTypeFromResourceClass(res.resClass), res.resource->GetSpaceID() };
  214. auto slot = m_slotAssignments.find(typeAndSpace);
  215. // If the assignment isn't found, we assume it's not accessed
  216. if (slot != m_slotAssignments.end()) {
  217. IRBuilder<> Builder(instruction);
  218. Value * slotIndex;
  219. if (isa<ConstantInt>(res.index)) {
  220. unsigned index = cast<ConstantInt>(res.index)->getLimitedValue();
  221. if (index > slot->second.numSlots) {
  222. // out-of-range accesses are written to slot zero:
  223. slotIndex = HlslOP->GetU32Const(0);
  224. }
  225. else {
  226. slotIndex = HlslOP->GetU32Const(slot->second.startSlot + index);
  227. }
  228. }
  229. else {
  230. RSRegisterIdentifier id{ typeAndSpace.Type, typeAndSpace.Space, res.resource->GetID() };
  231. m_DynamicallyIndexedBindPoints.emplace(std::move(id));
  232. // CompareWithSlotLimit will contain 1 if the access is out-of-bounds (both over- and and under-flow
  233. // via the unsigned >= with slot count)
  234. auto CompareWithSlotLimit = Builder.CreateICmpUGE(res.index, HlslOP->GetU32Const(slot->second.numSlots), "CompareWithSlotLimit");
  235. auto CompareWithSlotLimitAsUint = Builder.CreateCast(Instruction::CastOps::ZExt, CompareWithSlotLimit, Type::getInt32Ty(Ctx), "CompareWithSlotLimitAsUint");
  236. // IsInBounds will therefore contain 0 if the access is out-of-bounds, and 1 otherwise.
  237. auto IsInBounds = Builder.CreateSub(HlslOP->GetU32Const(1), CompareWithSlotLimitAsUint, "IsInBounds");
  238. auto SlotOffset = Builder.CreateAdd(res.index, HlslOP->GetU32Const(slot->second.startSlot), "SlotOffset");
  239. // This will drive an out-of-bounds access slot down to 0
  240. slotIndex = Builder.CreateMul(SlotOffset, IsInBounds, "slotIndex");
  241. }
  242. EmitAccess(Ctx, HlslOP, Builder, slotIndex, readWrite);
  243. return true; // did modify
  244. }
  245. return false; // did not modify
  246. }
  247. DxilResourceAndClass GetResourceFromHandle(Value * resHandle, DxilModule &DM) {
  248. DxilResourceAndClass ret{ nullptr, nullptr, DXIL::ResourceClass::Invalid };
  249. CallInst *handle = cast<CallInst>(resHandle);
  250. DxilInst_CreateHandle createHandle(handle);
  251. // Dynamic rangeId is not supported - skip and let validation report the
  252. // error.
  253. if (!isa<ConstantInt>(createHandle.get_rangeId()))
  254. return ret;
  255. unsigned rangeId =
  256. cast<ConstantInt>(createHandle.get_rangeId())->getLimitedValue();
  257. auto resClass = static_cast<DXIL::ResourceClass>(createHandle.get_resourceClass_val());
  258. switch (resClass) {
  259. case DXIL::ResourceClass::SRV:
  260. ret.resource = &DM.GetSRV(rangeId);
  261. break;
  262. case DXIL::ResourceClass::UAV:
  263. ret.resource = &DM.GetUAV(rangeId);
  264. break;
  265. case DXIL::ResourceClass::CBuffer:
  266. ret.resource = &DM.GetCBuffer(rangeId);
  267. break;
  268. case DXIL::ResourceClass::Sampler:
  269. ret.resource = &DM.GetSampler(rangeId);
  270. break;
  271. default:
  272. DXASSERT(0, "invalid res class");
  273. return ret;
  274. }
  275. ret.index = createHandle.get_index();
  276. ret.resClass = resClass;
  277. return ret;
  278. }
  279. bool DxilShaderAccessTracking::runOnModule(Module &M)
  280. {
  281. // This pass adds instrumentation for shader access to resources
  282. DxilModule &DM = M.GetOrCreateDxilModule();
  283. LLVMContext & Ctx = M.getContext();
  284. OP *HlslOP = DM.GetOP();
  285. bool Modified = false;
  286. if (m_CheckForDynamicIndexing) {
  287. bool FoundDynamicIndexing = false;
  288. auto CreateHandleFn = HlslOP->GetOpFunc(DXIL::OpCode::CreateHandle, Type::getVoidTy(Ctx));
  289. auto CreateHandleUses = CreateHandleFn->uses();
  290. for (auto FI = CreateHandleUses.begin(); FI != CreateHandleUses.end(); ) {
  291. auto & FunctionUse = *FI++;
  292. auto FunctionUser = FunctionUse.getUser();
  293. auto instruction = cast<Instruction>(FunctionUser);
  294. Value * index = instruction->getOperand(3);
  295. if (!isa<Constant>(index)) {
  296. FoundDynamicIndexing = true;
  297. break;
  298. }
  299. }
  300. if (FoundDynamicIndexing) {
  301. if (OSOverride != nullptr) {
  302. formatted_raw_ostream FOS(*OSOverride);
  303. FOS << "FoundDynamicIndexing";
  304. }
  305. }
  306. }
  307. else {
  308. {
  309. if (DM.m_ShaderFlags.GetForceEarlyDepthStencil()) {
  310. if (OSOverride != nullptr) {
  311. formatted_raw_ostream FOS(*OSOverride);
  312. FOS << "ShouldAssumeDsvAccess";
  313. }
  314. }
  315. IRBuilder<> Builder(DM.GetEntryFunction()->getEntryBlock().getFirstInsertionPt());
  316. unsigned int UAVResourceHandle = static_cast<unsigned int>(DM.GetUAVs().size());
  317. // Set up a UAV with structure of a single int
  318. SmallVector<llvm::Type*, 1> Elements{ Type::getInt32Ty(Ctx) };
  319. llvm::StructType *UAVStructTy = llvm::StructType::create(Elements, "class.RWStructuredBuffer");
  320. std::unique_ptr<DxilResource> pUAV = llvm::make_unique<DxilResource>();
  321. pUAV->SetGlobalName("PIX_CountUAVName");
  322. pUAV->SetGlobalSymbol(UndefValue::get(UAVStructTy->getPointerTo()));
  323. pUAV->SetID(UAVResourceHandle);
  324. pUAV->SetSpaceID((unsigned int)-2); // This is the reserved-for-tools register space
  325. pUAV->SetSampleCount(1);
  326. pUAV->SetGloballyCoherent(false);
  327. pUAV->SetHasCounter(false);
  328. pUAV->SetCompType(CompType::getI32());
  329. pUAV->SetLowerBound(0);
  330. pUAV->SetRangeSize(1);
  331. pUAV->SetKind(DXIL::ResourceKind::RawBuffer);
  332. auto pAnnotation = DM.GetTypeSystem().GetStructAnnotation(UAVStructTy);
  333. if (pAnnotation == nullptr) {
  334. pAnnotation = DM.GetTypeSystem().AddStructAnnotation(UAVStructTy);
  335. pAnnotation->GetFieldAnnotation(0).SetCBufferOffset(0);
  336. pAnnotation->GetFieldAnnotation(0).SetCompType(hlsl::DXIL::ComponentType::I32);
  337. pAnnotation->GetFieldAnnotation(0).SetFieldName("count");
  338. }
  339. ID = DM.AddUAV(std::move(pUAV));
  340. assert((unsigned)ID == UAVResourceHandle);
  341. // Create handle for the newly-added UAV
  342. Function* CreateHandleOpFunc = HlslOP->GetOpFunc(DXIL::OpCode::CreateHandle, Type::getVoidTy(Ctx));
  343. Constant* CreateHandleOpcodeArg = HlslOP->GetU32Const((unsigned)DXIL::OpCode::CreateHandle);
  344. Constant* UAVArg = HlslOP->GetI8Const(static_cast<std::underlying_type<DxilResourceBase::Class>::type>(DXIL::ResourceClass::UAV));
  345. Constant* MetaDataArg = HlslOP->GetU32Const(ID); // position of the metadata record in the corresponding metadata list
  346. Constant* IndexArg = HlslOP->GetU32Const(0); //
  347. Constant* FalseArg = HlslOP->GetI1Const(0); // non-uniform resource index: false
  348. m_HandleForUAV = Builder.CreateCall(CreateHandleOpFunc,
  349. { CreateHandleOpcodeArg, UAVArg, MetaDataArg, IndexArg, FalseArg }, "PIX_CountUAV_Handle");
  350. DM.ReEmitDxilResources();
  351. }
  352. struct ResourceAccessFunction
  353. {
  354. DXIL::OpCode opcode;
  355. ShaderAccessFlags readWrite;
  356. bool functionUsesSamplerAtIndex2;
  357. std::vector<Type*> overloads;
  358. };
  359. std::vector<Type*> voidType = { Type::getVoidTy(Ctx) };
  360. std::vector<Type*> i32 = { Type::getInt32Ty(Ctx) };
  361. std::vector<Type*> f16f32 = { Type::getHalfTy(Ctx), Type::getFloatTy(Ctx) };
  362. std::vector<Type*> f32i32 = { Type::getFloatTy(Ctx), Type::getInt32Ty(Ctx) };
  363. std::vector<Type*> f32i32f64 = { Type::getFloatTy(Ctx), Type::getInt32Ty(Ctx), Type::getDoubleTy(Ctx) };
  364. std::vector<Type*> f16f32i16i32 = { Type::getHalfTy(Ctx), Type::getFloatTy(Ctx), Type::getInt16Ty(Ctx), Type::getInt32Ty(Ctx) };
  365. std::vector<Type*> f16f32f64i16i32i64 = { Type::getHalfTy(Ctx), Type::getFloatTy(Ctx), Type::getDoubleTy(Ctx), Type::getInt16Ty(Ctx), Type::getInt32Ty(Ctx), Type::getInt64Ty(Ctx) };
  366. // todo: should "GetDimensions" mean a resource access?
  367. ResourceAccessFunction raFunctions[] = {
  368. { DXIL::OpCode::CBufferLoadLegacy , ShaderAccessFlags::Read , false, f32i32f64 },
  369. { DXIL::OpCode::CBufferLoad , ShaderAccessFlags::Read , false, f16f32f64i16i32i64 },
  370. { DXIL::OpCode::Sample , ShaderAccessFlags::Read , true , f16f32 },
  371. { DXIL::OpCode::SampleBias , ShaderAccessFlags::Read , true , f16f32 },
  372. { DXIL::OpCode::SampleLevel , ShaderAccessFlags::Read , true , f16f32 },
  373. { DXIL::OpCode::SampleGrad , ShaderAccessFlags::Read , true , f16f32 },
  374. { DXIL::OpCode::SampleCmp , ShaderAccessFlags::Read , true , f16f32 },
  375. { DXIL::OpCode::SampleCmpLevelZero , ShaderAccessFlags::Read , true , f16f32 },
  376. { DXIL::OpCode::TextureLoad , ShaderAccessFlags::Read , false, f16f32i16i32 },
  377. { DXIL::OpCode::TextureStore , ShaderAccessFlags::Write , false, f16f32i16i32 },
  378. { DXIL::OpCode::TextureGather , ShaderAccessFlags::Read , true , f32i32 }, // todo: SM6: f16f32i16i32 },
  379. { DXIL::OpCode::TextureGatherCmp , ShaderAccessFlags::Read , false, f32i32 }, // todo: SM6: f16f32i16i32 },
  380. { DXIL::OpCode::BufferLoad , ShaderAccessFlags::Read , false, f32i32 },
  381. { DXIL::OpCode::RawBufferLoad , ShaderAccessFlags::Read , false, f32i32 },
  382. { DXIL::OpCode::BufferStore , ShaderAccessFlags::Write , false, f32i32 },
  383. { DXIL::OpCode::BufferUpdateCounter , ShaderAccessFlags::Counter, false, voidType },
  384. { DXIL::OpCode::AtomicBinOp , ShaderAccessFlags::Write , false, i32 },
  385. { DXIL::OpCode::AtomicCompareExchange , ShaderAccessFlags::Write , false, i32 },
  386. };
  387. for (const auto & raFunction : raFunctions) {
  388. for (const auto & Overload : raFunction.overloads) {
  389. Function * TheFunction = HlslOP->GetOpFunc(raFunction.opcode, Overload);
  390. auto TexLoadFunctionUses = TheFunction->uses();
  391. for (auto FI = TexLoadFunctionUses.begin(); FI != TexLoadFunctionUses.end(); ) {
  392. auto & FunctionUse = *FI++;
  393. auto FunctionUser = FunctionUse.getUser();
  394. auto instruction = cast<Instruction>(FunctionUser);
  395. auto res = GetResourceFromHandle(instruction->getOperand(1), DM);
  396. // Don't instrument the accesses to the UAV that we just added
  397. if (res.resource->GetSpaceID() == (unsigned)-2) {
  398. continue;
  399. }
  400. if (EmitResourceAccess(res, instruction, HlslOP, Ctx, raFunction.readWrite)) {
  401. Modified = true;
  402. }
  403. if (raFunction.functionUsesSamplerAtIndex2) {
  404. auto sampler = GetResourceFromHandle(instruction->getOperand(2), DM);
  405. if (EmitResourceAccess(sampler, instruction, HlslOP, Ctx, ShaderAccessFlags::Read)) {
  406. Modified = true;
  407. }
  408. }
  409. }
  410. }
  411. }
  412. if (OSOverride != nullptr) {
  413. formatted_raw_ostream FOS(*OSOverride);
  414. FOS << "DynamicallyIndexedBindPoints=";
  415. for (auto const & bp : m_DynamicallyIndexedBindPoints) {
  416. FOS << EncodeRegisterType(bp.Type) << bp.Space << ':' << bp.Index <<';';
  417. }
  418. FOS << ".";
  419. }
  420. }
  421. return Modified;
  422. }
  423. char DxilShaderAccessTracking::ID = 0;
  424. ModulePass *llvm::createDxilShaderAccessTrackingPass() {
  425. return new DxilShaderAccessTracking();
  426. }
  427. INITIALIZE_PASS(DxilShaderAccessTracking, "hlsl-dxil-pix-shader-access-instrumentation", "HLSL DXIL shader access tracking for PIX", false, false)