DxilShaderAccessTracking.cpp 23 KB

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