DxilShaderAccessTracking.cpp 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810
  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/DXIL/DxilResourceBinding.h"
  16. #include "dxc/DXIL/DxilResourceProperties.h"
  17. #include "dxc/DxilPIXPasses/DxilPIXPasses.h"
  18. #include "dxc/HLSL/DxilGenerationPass.h"
  19. #include "dxc/HLSL/DxilSpanAllocator.h"
  20. #include "llvm/IR/PassManager.h"
  21. #include "llvm/Support/FormattedStream.h"
  22. #include "llvm/Transforms/Utils/Local.h"
  23. #include <deque>
  24. #include "PixPassHelpers.h"
  25. #ifdef _WIN32
  26. #include <winerror.h>
  27. #endif
  28. using namespace llvm;
  29. using namespace hlsl;
  30. void ThrowIf(bool a) {
  31. if (a) {
  32. throw ::hlsl::Exception(E_INVALIDARG);
  33. }
  34. }
  35. //---------------------------------------------------------------------------------------------------------------------------------
  36. // These types are taken from PIX's ShaderAccessHelpers.h
  37. enum class ShaderAccessFlags : uint32_t {
  38. None = 0,
  39. Read = 1 << 0,
  40. Write = 1 << 1,
  41. // "Counter" access is only applicable to UAVs; it means the counter buffer
  42. // attached to the UAV was accessed, but not necessarily the UAV resource.
  43. Counter = 1 << 2,
  44. Sampler = 1 << 3,
  45. // Descriptor-only read (if any), but not the resource contents (if any).
  46. // Used for GetDimensions, samplers, and secondary texture for sampler
  47. // feedback.
  48. // TODO: Make this a unique value if supported in PIX, then enable
  49. // GetDimensions
  50. DescriptorRead = 1 << 0,
  51. };
  52. constexpr uint32_t DWORDsPerResource = 3;
  53. constexpr uint32_t BytesPerDWORD = 4;
  54. static uint32_t OffsetFromAccess(ShaderAccessFlags access) {
  55. switch (access) {
  56. case ShaderAccessFlags::Read:
  57. return 0;
  58. case ShaderAccessFlags::Write:
  59. return 1;
  60. case ShaderAccessFlags::Counter:
  61. return 2;
  62. default:
  63. throw ::hlsl::Exception(E_INVALIDARG);
  64. }
  65. }
  66. // This enum doesn't have to match PIX's version, because the values are
  67. // received from PIX encoded in ASCII. However, for ease of comparing this code
  68. // with PIX, and to be less confusing to future maintainers, this enum does
  69. // indeed match the same-named enum in PIX.
  70. enum class RegisterType {
  71. CBV,
  72. SRV,
  73. UAV,
  74. RTV, // not used.
  75. DSV, // not used.
  76. Sampler,
  77. SOV, // not used.
  78. Invalid,
  79. Terminator
  80. };
  81. RegisterType RegisterTypeFromResourceClass(DXIL::ResourceClass c) {
  82. switch (c) {
  83. case DXIL::ResourceClass::SRV:
  84. return RegisterType::SRV;
  85. break;
  86. case DXIL::ResourceClass::UAV:
  87. return RegisterType::UAV;
  88. break;
  89. case DXIL::ResourceClass::CBuffer:
  90. return RegisterType::CBV;
  91. break;
  92. case DXIL::ResourceClass::Sampler:
  93. return RegisterType::Sampler;
  94. break;
  95. case DXIL::ResourceClass::Invalid:
  96. return RegisterType::Invalid;
  97. break;
  98. default:
  99. ThrowIf(true);
  100. return RegisterType::Invalid;
  101. }
  102. }
  103. struct RegisterTypeAndSpace {
  104. bool operator<(const RegisterTypeAndSpace &o) const {
  105. return static_cast<int>(Type) < static_cast<int>(o.Type) ||
  106. (static_cast<int>(Type) == static_cast<int>(o.Type) &&
  107. Space < o.Space);
  108. }
  109. RegisterType Type;
  110. unsigned Space;
  111. };
  112. // Identifies a bind point as defined by the root signature
  113. struct RSRegisterIdentifier {
  114. RegisterType Type;
  115. unsigned Space;
  116. unsigned Index;
  117. bool operator<(const RSRegisterIdentifier &o) const {
  118. return static_cast<unsigned>(Type) < static_cast<unsigned>(o.Type) &&
  119. Space < o.Space && Index < o.Index;
  120. }
  121. };
  122. struct SlotRange {
  123. unsigned startSlot;
  124. unsigned numSlots;
  125. // Number of slots needed if no descriptors from unbounded ranges are included
  126. unsigned numInvariableSlots;
  127. };
  128. enum class AccessStyle { None, FromRootSig, ResourceFromDescriptorHeap, SamplerFromDescriptorHeap };
  129. struct DxilResourceAndClass {
  130. AccessStyle accessStyle;
  131. RegisterType registerType;
  132. int RegisterSpace;
  133. unsigned RegisterID;
  134. Value *index;
  135. Value *dynamicallyBoundIndex;
  136. };
  137. enum class ResourceAccessStyle {
  138. None,
  139. Sampler,
  140. UAVRead,
  141. UAVWrite,
  142. CBVRead,
  143. SRVRead,
  144. EndOfEnum
  145. };
  146. //---------------------------------------------------------------------------------------------------------------------------------
  147. class DxilShaderAccessTracking : public ModulePass {
  148. public:
  149. static char ID; // Pass identification, replacement for typeid
  150. explicit DxilShaderAccessTracking() : ModulePass(ID) {}
  151. const char *getPassName() const override {
  152. return "DXIL shader access tracking";
  153. }
  154. bool runOnModule(Module &M) override;
  155. void applyOptions(PassOptions O) override;
  156. private:
  157. void EmitAccess(LLVMContext &Ctx, OP *HlslOP, IRBuilder<> &, Value *slot,
  158. ShaderAccessFlags access);
  159. bool EmitResourceAccess(DxilResourceAndClass &res, Instruction *instruction,
  160. OP *HlslOP, LLVMContext &Ctx,
  161. ShaderAccessFlags readWrite);
  162. DxilResourceAndClass GetResourceFromHandle(Value* resHandle, DxilModule& DM);
  163. private:
  164. struct DynamicResourceBinding {
  165. int HeapIndex;
  166. bool HeapIsSampler; // else resource
  167. std::string Name;
  168. };
  169. std::vector<DynamicResourceBinding> m_dynamicResourceBindings;
  170. bool m_CheckForDynamicIndexing = false;
  171. int m_DynamicResourceDataOffset = -1;
  172. int m_DynamicSamplerDataOffset = -1;
  173. int m_OutputBufferSize = -1;
  174. std::map<RegisterTypeAndSpace, SlotRange> m_slotAssignments;
  175. std::map<llvm::Function *, CallInst *> m_FunctionToUAVHandle;
  176. std::map<llvm::Function *, std::map<ResourceAccessStyle, Constant *>> m_FunctionToEncodedAccess;
  177. std::set<RSRegisterIdentifier> m_DynamicallyIndexedBindPoints;
  178. };
  179. static unsigned DeserializeInt(std::deque<char> &q) {
  180. unsigned i = 0;
  181. while (!q.empty() && isdigit(q.front())) {
  182. i *= 10;
  183. i += q.front() - '0';
  184. q.pop_front();
  185. }
  186. return i;
  187. }
  188. static char DequeFront(std::deque<char> &q) {
  189. ThrowIf(q.empty());
  190. auto c = q.front();
  191. q.pop_front();
  192. return c;
  193. }
  194. static RegisterType ParseRegisterType(std::deque<char> &q) {
  195. switch (DequeFront(q)) {
  196. case 'C':
  197. return RegisterType::CBV;
  198. case 'S':
  199. return RegisterType::SRV;
  200. case 'U':
  201. return RegisterType::UAV;
  202. case 'M':
  203. return RegisterType::Sampler;
  204. case 'I':
  205. return RegisterType::Invalid;
  206. default:
  207. return RegisterType::Terminator;
  208. }
  209. }
  210. static char EncodeRegisterType(RegisterType r) {
  211. switch (r) {
  212. case RegisterType::CBV:
  213. return 'C';
  214. case RegisterType::SRV:
  215. return 'S';
  216. case RegisterType::UAV:
  217. return 'U';
  218. case RegisterType::Sampler:
  219. return 'M';
  220. case RegisterType::Invalid:
  221. return 'I';
  222. }
  223. return '.';
  224. }
  225. static void ValidateDelimiter(std::deque<char> &q, char d) {
  226. ThrowIf(q.front() != d);
  227. q.pop_front();
  228. }
  229. void DxilShaderAccessTracking::applyOptions(PassOptions O) {
  230. int checkForDynamic;
  231. GetPassOptionInt(O, "checkForDynamicIndexing", &checkForDynamic, 0);
  232. m_CheckForDynamicIndexing = checkForDynamic != 0;
  233. StringRef configOption;
  234. if (GetPassOption(O, "config", &configOption)) {
  235. std::deque<char> config;
  236. config.assign(configOption.begin(), configOption.end());
  237. // Parse slot assignments. Compare with PIX's ShaderAccessHelpers.cpp
  238. // (TrackingConfiguration::SerializedRepresentation)
  239. RegisterType rt = ParseRegisterType(config);
  240. while (rt != RegisterType::Terminator) {
  241. RegisterTypeAndSpace rst;
  242. rst.Type = rt;
  243. rst.Space = DeserializeInt(config);
  244. ValidateDelimiter(config, ':');
  245. SlotRange sr;
  246. sr.startSlot = DeserializeInt(config);
  247. ValidateDelimiter(config, ':');
  248. sr.numSlots = DeserializeInt(config);
  249. ValidateDelimiter(config, 'i');
  250. sr.numInvariableSlots = DeserializeInt(config);
  251. ValidateDelimiter(config, ';');
  252. m_slotAssignments[rst] = sr;
  253. rt = ParseRegisterType(config);
  254. }
  255. m_DynamicResourceDataOffset = DeserializeInt(config);
  256. ValidateDelimiter(config, ';');
  257. m_DynamicSamplerDataOffset = DeserializeInt(config);
  258. ValidateDelimiter(config, ';');
  259. m_OutputBufferSize = DeserializeInt(config);
  260. }
  261. }
  262. void DxilShaderAccessTracking::EmitAccess(LLVMContext &Ctx, OP *HlslOP,
  263. IRBuilder<> &Builder,
  264. Value *ByteIndex,
  265. ShaderAccessFlags access) {
  266. unsigned OffsetForAccessType =
  267. static_cast<unsigned>(OffsetFromAccess(access) * BytesPerDWORD);
  268. auto OffsetByteIndex = Builder.CreateAdd(
  269. ByteIndex, HlslOP->GetU32Const(OffsetForAccessType), "OffsetByteIndex");
  270. UndefValue *UndefIntArg = UndefValue::get(Type::getInt32Ty(Ctx));
  271. Constant *LiteralOne = HlslOP->GetU32Const(1);
  272. Constant *ElementMask = HlslOP->GetI8Const(1);
  273. Function *StoreFunc =
  274. HlslOP->GetOpFunc(OP::OpCode::BufferStore, Type::getInt32Ty(Ctx));
  275. Constant *StoreOpcode =
  276. HlslOP->GetU32Const((unsigned)OP::OpCode::BufferStore);
  277. (void)Builder.CreateCall(
  278. StoreFunc,
  279. {
  280. StoreOpcode, // i32, ; opcode
  281. m_FunctionToUAVHandle.at(
  282. Builder.GetInsertBlock()
  283. ->getParent()), // %dx.types.Handle, ; resource handle
  284. OffsetByteIndex, // i32, ; coordinate c0: byte offset
  285. UndefIntArg, // i32, ; coordinate c1 (unused)
  286. LiteralOne, // i32, ; value v0
  287. UndefIntArg, // i32, ; value v1
  288. UndefIntArg, // i32, ; value v2
  289. UndefIntArg, // i32, ; value v3
  290. ElementMask // i8 ; just the first value is used
  291. });
  292. }
  293. static ResourceAccessStyle AccessStyleFromAccessAndType(
  294. AccessStyle accessStyle,
  295. RegisterType registerType,
  296. ShaderAccessFlags readWrite)
  297. {
  298. switch (accessStyle)
  299. {
  300. case AccessStyle::ResourceFromDescriptorHeap:
  301. switch (registerType)
  302. {
  303. case RegisterType::CBV:
  304. return ResourceAccessStyle::CBVRead;
  305. case RegisterType::SRV:
  306. return ResourceAccessStyle::SRVRead;
  307. case RegisterType::UAV:
  308. return readWrite == ShaderAccessFlags::Read ?
  309. ResourceAccessStyle::UAVRead :
  310. ResourceAccessStyle::UAVWrite;
  311. default:
  312. return ResourceAccessStyle::None;
  313. }
  314. case AccessStyle::SamplerFromDescriptorHeap:
  315. return ResourceAccessStyle::Sampler;
  316. default:
  317. return ResourceAccessStyle::None;
  318. }
  319. }
  320. bool DxilShaderAccessTracking::EmitResourceAccess(DxilResourceAndClass &res,
  321. Instruction *instruction,
  322. OP *HlslOP, LLVMContext &Ctx,
  323. ShaderAccessFlags readWrite) {
  324. IRBuilder<> Builder(instruction);
  325. if (res.accessStyle == AccessStyle::FromRootSig) {
  326. RegisterTypeAndSpace typeAndSpace{
  327. res.registerType,
  328. static_cast<unsigned>(res.RegisterSpace) // reserved spaces are -ve, but user spaces can only be +ve
  329. };
  330. auto slot = m_slotAssignments.find(typeAndSpace);
  331. // If the assignment isn't found, we assume it's not accessed
  332. if (slot != m_slotAssignments.end()) {
  333. Value *slotIndex;
  334. if (isa<ConstantInt>(res.index)) {
  335. unsigned index = cast<ConstantInt>(res.index)->getLimitedValue();
  336. if (index > slot->second.numSlots) {
  337. // out-of-range accesses are written to slot zero:
  338. slotIndex = HlslOP->GetU32Const(0);
  339. } else {
  340. slotIndex = HlslOP->GetU32Const((slot->second.startSlot + index) *
  341. DWORDsPerResource * BytesPerDWORD);
  342. }
  343. } else {
  344. RSRegisterIdentifier id{typeAndSpace.Type, typeAndSpace.Space,
  345. res.RegisterID};
  346. m_DynamicallyIndexedBindPoints.emplace(std::move(id));
  347. // CompareWithSlotLimit will contain 1 if the access is out-of-bounds
  348. // (both over- and and under-flow via the unsigned >= with slot count)
  349. auto CompareWithSlotLimit = Builder.CreateICmpUGE(
  350. res.index, HlslOP->GetU32Const(slot->second.numSlots),
  351. "CompareWithSlotLimit");
  352. auto CompareWithSlotLimitAsUint = Builder.CreateCast(
  353. Instruction::CastOps::ZExt, CompareWithSlotLimit,
  354. Type::getInt32Ty(Ctx), "CompareWithSlotLimitAsUint");
  355. // IsInBounds will therefore contain 0 if the access is out-of-bounds, and
  356. // 1 otherwise.
  357. auto IsInBounds = Builder.CreateSub(
  358. HlslOP->GetU32Const(1), CompareWithSlotLimitAsUint, "IsInBounds");
  359. auto SlotDwordOffset = Builder.CreateAdd(
  360. res.index, HlslOP->GetU32Const(slot->second.startSlot),
  361. "SlotDwordOffset");
  362. auto SlotByteOffset = Builder.CreateMul(
  363. SlotDwordOffset,
  364. HlslOP->GetU32Const(DWORDsPerResource * BytesPerDWORD),
  365. "SlotByteOffset");
  366. // This will drive an out-of-bounds access slot down to 0
  367. slotIndex = Builder.CreateMul(SlotByteOffset, IsInBounds, "slotIndex");
  368. }
  369. EmitAccess(Ctx, HlslOP, Builder, slotIndex, readWrite);
  370. return true; // did modify
  371. }
  372. }
  373. else if (m_DynamicResourceDataOffset != -1) {
  374. if (res.accessStyle == AccessStyle::ResourceFromDescriptorHeap ||
  375. res.accessStyle == AccessStyle::SamplerFromDescriptorHeap)
  376. {
  377. Constant* BaseOfRecordsForType;
  378. int LimitForType;
  379. if (res.accessStyle == AccessStyle::ResourceFromDescriptorHeap) {
  380. LimitForType = m_DynamicSamplerDataOffset - m_DynamicResourceDataOffset;
  381. BaseOfRecordsForType =
  382. HlslOP->GetU32Const(m_DynamicResourceDataOffset);
  383. } else {
  384. LimitForType = m_OutputBufferSize - m_DynamicSamplerDataOffset;
  385. BaseOfRecordsForType =
  386. HlslOP->GetU32Const(m_DynamicSamplerDataOffset);
  387. }
  388. // Branchless limit: compare offset to size of data reserved for that type,
  389. // resulting in a value of 0 or 1.
  390. // Extend that 0/1 to an integer, and multiply the offset by that value.
  391. // Result: expected offset, or 0 if too large.
  392. // Add 1 to the index in order to skip over the zeroth entry: that's
  393. // reserved for "out of bounds" writes.
  394. auto *IndexToWrite =
  395. Builder.CreateAdd(res.dynamicallyBoundIndex, HlslOP->GetU32Const(1));
  396. // Each record is two dwords:
  397. // the first dword is for write access, the second for read.
  398. Constant *SizeofRecord =
  399. HlslOP->GetU32Const(2 * static_cast<unsigned int>(sizeof(uint32_t)));
  400. auto *BaseOfRecord =
  401. Builder.CreateMul(IndexToWrite, SizeofRecord);
  402. Value* OffsetToWrite;
  403. if (readWrite == ShaderAccessFlags::Write) {
  404. OffsetToWrite = BaseOfRecord;
  405. }
  406. else {
  407. OffsetToWrite = Builder.CreateAdd(BaseOfRecord,
  408. HlslOP->GetU32Const(static_cast<unsigned int>(sizeof(uint32_t))));
  409. }
  410. // Generate the 0 (out of bounds) or 1 (in-bounds) multiplier:
  411. Constant *BufferLimit = HlslOP->GetU32Const(LimitForType);
  412. auto *LimitBoolean =
  413. Builder.CreateICmpULT(OffsetToWrite, BufferLimit);
  414. auto * LimitIntegerValue = Builder.CreateCast(
  415. Instruction::CastOps::ZExt, LimitBoolean,
  416. Type::getInt32Ty(Ctx));
  417. // Limit the offset to the out-of-bounds record if the above generated 0,
  418. // or leave it as-is if the above generated 1:
  419. auto *LimitedOffset = Builder.CreateMul(OffsetToWrite, LimitIntegerValue);
  420. // Offset into the range of records for this type of access (resource or sampler)
  421. auto* Offset = Builder.CreateAdd(BaseOfRecordsForType, LimitedOffset);
  422. ResourceAccessStyle accessStyle = AccessStyleFromAccessAndType(
  423. res.accessStyle,
  424. res.registerType,
  425. readWrite);
  426. Constant* EncodedFlags = m_FunctionToEncodedAccess
  427. .at(Builder.GetInsertBlock()->getParent())
  428. .at(accessStyle);
  429. Constant *ElementMask = HlslOP->GetI8Const(1);
  430. Function *StoreFunc =
  431. HlslOP->GetOpFunc(OP::OpCode::BufferStore, Type::getInt32Ty(Ctx));
  432. Constant *StoreOpcode =
  433. HlslOP->GetU32Const((unsigned)OP::OpCode::BufferStore);
  434. UndefValue *UndefArg = UndefValue::get(Type::getInt32Ty(Ctx));
  435. (void)Builder.CreateCall(
  436. StoreFunc,
  437. {
  438. StoreOpcode, // i32, ; opcode
  439. m_FunctionToUAVHandle.at(
  440. Builder.GetInsertBlock()
  441. ->getParent()), // %dx.types.Handle, ; resource handle
  442. Offset, // i32, ; coordinate c0: byte offset
  443. UndefArg, // i32, ; coordinate c1 (unused)
  444. EncodedFlags, // i32, ; value v0
  445. UndefArg, // i32, ; value v1
  446. UndefArg, // i32, ; value v2
  447. UndefArg, // i32, ; value v3
  448. ElementMask // i8 ; just the first value is used
  449. });
  450. return true; // did modify
  451. }
  452. }
  453. return false; // did not modify
  454. }
  455. DxilResourceAndClass
  456. DxilShaderAccessTracking::GetResourceFromHandle(Value *resHandle,
  457. DxilModule &DM) {
  458. DxilResourceAndClass ret{
  459. AccessStyle::None,
  460. RegisterType::Terminator,
  461. 0,
  462. 0,
  463. nullptr,
  464. nullptr};
  465. CallInst *handle = cast<CallInst>(resHandle);
  466. unsigned rangeId = -1;
  467. if (hlsl::OP::IsDxilOpFuncCallInst(handle, hlsl::OP::OpCode::CreateHandle))
  468. {
  469. DxilInst_CreateHandle createHandle(handle);
  470. // Dynamic rangeId is not supported - skip and let validation report the
  471. // error.
  472. if (isa<ConstantInt>(createHandle.get_rangeId())) {
  473. rangeId = cast<ConstantInt>(createHandle.get_rangeId())->getLimitedValue();
  474. auto resClass = static_cast<DXIL::ResourceClass>(createHandle.get_resourceClass_val());
  475. DxilResourceBase* resource = nullptr;
  476. RegisterType registerType = RegisterType::Invalid;
  477. switch (resClass) {
  478. case DXIL::ResourceClass::SRV:
  479. resource = &DM.GetSRV(rangeId);
  480. registerType = RegisterType::SRV;
  481. break;
  482. case DXIL::ResourceClass::UAV:
  483. resource = &DM.GetUAV(rangeId);
  484. registerType = RegisterType::UAV;
  485. break;
  486. case DXIL::ResourceClass::CBuffer:
  487. resource = &DM.GetCBuffer(rangeId);
  488. registerType = RegisterType::CBV;
  489. break;
  490. case DXIL::ResourceClass::Sampler:
  491. resource = &DM.GetSampler(rangeId);
  492. registerType = RegisterType::Sampler;
  493. break;
  494. }
  495. if (resource != nullptr) {
  496. ret.index = createHandle.get_index();
  497. ret.registerType = registerType;
  498. ret.accessStyle = AccessStyle::FromRootSig;
  499. ret.RegisterID = resource->GetID();
  500. ret.RegisterSpace = resource->GetSpaceID();
  501. }
  502. }
  503. } else if (hlsl::OP::IsDxilOpFuncCallInst(handle, hlsl::OP::OpCode::AnnotateHandle)) {
  504. DxilInst_AnnotateHandle annotateHandle(handle);
  505. auto properties = hlsl::resource_helper::loadPropsFromAnnotateHandle(
  506. annotateHandle, *DM.GetShaderModel());
  507. auto* handleCreation = cast<CallInst>(annotateHandle.get_res());
  508. if (hlsl::OP::IsDxilOpFuncCallInst(handleCreation, hlsl::OP::OpCode::CreateHandleFromBinding)) {
  509. DxilInst_CreateHandleFromBinding createHandleFromBinding(handleCreation);
  510. Constant* B = cast<Constant>(createHandleFromBinding.get_bind());
  511. auto binding = hlsl::resource_helper::loadBindingFromConstant(*B);
  512. ret.accessStyle = AccessStyle::FromRootSig;
  513. ret.index = createHandleFromBinding.get_index();
  514. ret.registerType = RegisterTypeFromResourceClass(
  515. static_cast<hlsl::DXIL::ResourceClass>(binding.resourceClass));
  516. ret.RegisterSpace = binding.spaceID;
  517. } else if (hlsl::OP::IsDxilOpFuncCallInst(handleCreation, hlsl::OP::OpCode::CreateHandleFromHeap)) {
  518. DxilInst_CreateHandleFromHeap createHandleFromHeap(handleCreation);
  519. ret.accessStyle = createHandleFromHeap.get_samplerHeap_val()
  520. ? AccessStyle::SamplerFromDescriptorHeap : AccessStyle::ResourceFromDescriptorHeap;
  521. ret.dynamicallyBoundIndex = createHandleFromHeap.get_index();
  522. ret.registerType = RegisterTypeFromResourceClass(properties.getResourceClass());
  523. DynamicResourceBinding drb{};
  524. drb.HeapIsSampler = createHandleFromHeap.get_samplerHeap_val();
  525. drb.HeapIndex = -1;
  526. drb.Name = "ShaderNameTodo";
  527. if (auto * constInt = dyn_cast<ConstantInt>(createHandleFromHeap.get_index()))
  528. {
  529. drb.HeapIndex = constInt->getLimitedValue();
  530. }
  531. m_dynamicResourceBindings.emplace_back(std::move(drb));
  532. return ret;
  533. } else {
  534. DXASSERT_NOMSG(false);
  535. }
  536. }
  537. return ret;
  538. }
  539. static uint32_t EncodeShaderModel(DXIL::ShaderKind kind)
  540. {
  541. DXASSERT_NOMSG(static_cast<int>(DXIL::ShaderKind::Invalid) <= 16);
  542. return static_cast<uint32_t>(kind) << 28;
  543. }
  544. static uint32_t EncodeAccess(ResourceAccessStyle access) {
  545. uint32_t encoded = static_cast<uint32_t>(access);
  546. DXASSERT_NOMSG(encoded < 8);
  547. return encoded << 24;
  548. }
  549. bool DxilShaderAccessTracking::runOnModule(Module &M) {
  550. // This pass adds instrumentation for shader access to resources
  551. DxilModule &DM = M.GetOrCreateDxilModule();
  552. LLVMContext &Ctx = M.getContext();
  553. OP *HlslOP = DM.GetOP();
  554. bool Modified = false;
  555. if (m_CheckForDynamicIndexing) {
  556. bool FoundDynamicIndexing = false;
  557. auto CreateHandleFn =
  558. HlslOP->GetOpFunc(DXIL::OpCode::CreateHandle, Type::getVoidTy(Ctx));
  559. auto CreateHandleUses = CreateHandleFn->uses();
  560. for (auto FI = CreateHandleUses.begin(); FI != CreateHandleUses.end();) {
  561. auto &FunctionUse = *FI++;
  562. auto FunctionUser = FunctionUse.getUser();
  563. auto instruction = cast<Instruction>(FunctionUser);
  564. Value *index = instruction->getOperand(3);
  565. if (!isa<Constant>(index)) {
  566. FoundDynamicIndexing = true;
  567. break;
  568. }
  569. }
  570. if (FoundDynamicIndexing) {
  571. if (OSOverride != nullptr) {
  572. formatted_raw_ostream FOS(*OSOverride);
  573. FOS << "FoundDynamicIndexing";
  574. }
  575. }
  576. } else {
  577. {
  578. if (DM.m_ShaderFlags.GetForceEarlyDepthStencil()) {
  579. if (OSOverride != nullptr) {
  580. formatted_raw_ostream FOS(*OSOverride);
  581. FOS << "ShouldAssumeDsvAccess";
  582. }
  583. }
  584. int uavRegId = 0;
  585. for (llvm::Function &F : M.functions()) {
  586. if (!F.getBasicBlockList().empty()) {
  587. IRBuilder<> Builder(F.getEntryBlock().getFirstInsertionPt());
  588. m_FunctionToUAVHandle[&F] = PIXPassHelpers::CreateUAV(DM, Builder, uavRegId++, "PIX_CountUAV_Handle");
  589. auto const* shaderModel = DM.GetShaderModel();
  590. auto shaderKind = shaderModel->GetKind();
  591. OP *HlslOP = DM.GetOP();
  592. for (int accessStyle = 1;
  593. accessStyle < static_cast<int>(ResourceAccessStyle::EndOfEnum);
  594. ++accessStyle)
  595. {
  596. ResourceAccessStyle style = static_cast<ResourceAccessStyle>(accessStyle);
  597. m_FunctionToEncodedAccess[&F][style] =
  598. HlslOP->GetU32Const(EncodeShaderModel(shaderKind) |
  599. EncodeAccess(style));
  600. }
  601. }
  602. }
  603. DM.ReEmitDxilResources();
  604. }
  605. for (llvm::Function &F : M.functions()) {
  606. // Only used DXIL intrinsics:
  607. if (!F.isDeclaration() || F.isIntrinsic() || F.use_empty() ||
  608. !OP::IsDxilOpFunc(&F))
  609. continue;
  610. // Gather handle parameter indices, if any
  611. FunctionType *fnTy =
  612. cast<FunctionType>(F.getType()->getPointerElementType());
  613. SmallVector<unsigned, 4> handleParams;
  614. for (unsigned iParam = 1; iParam < fnTy->getFunctionNumParams();
  615. ++iParam) {
  616. if (fnTy->getParamType(iParam) == HlslOP->GetHandleType())
  617. handleParams.push_back(iParam);
  618. }
  619. if (handleParams.empty())
  620. continue;
  621. auto FunctionUses = F.uses();
  622. for (auto FI = FunctionUses.begin(); FI != FunctionUses.end();) {
  623. auto &FunctionUse = *FI++;
  624. auto FunctionUser = FunctionUse.getUser();
  625. auto Call = cast<CallInst>(FunctionUser);
  626. auto opCode = OP::GetDxilOpFuncCallInst(Call);
  627. // Base Read/Write on function attribute - should match for all normal
  628. // resource operations
  629. ShaderAccessFlags readWrite = ShaderAccessFlags::Write;
  630. if (OP::GetMemAccessAttr(opCode) == llvm::Attribute::AttrKind::ReadOnly)
  631. readWrite = ShaderAccessFlags::Read;
  632. // Special cases
  633. switch (opCode) {
  634. case DXIL::OpCode::GetDimensions:
  635. // readWrite = ShaderAccessFlags::DescriptorRead; // TODO: Support
  636. // GetDimensions
  637. continue;
  638. case DXIL::OpCode::BufferUpdateCounter:
  639. readWrite = ShaderAccessFlags::Counter;
  640. break;
  641. case DXIL::OpCode::TraceRay:
  642. // Read of AccelerationStructure; doesn't match function attribute
  643. // readWrite = ShaderAccessFlags::Read; // TODO: Support
  644. continue;
  645. case DXIL::OpCode::RayQuery_TraceRayInline: {
  646. // Read of AccelerationStructure; doesn't match function attribute
  647. auto res = GetResourceFromHandle(Call->getArgOperand(2), DM);
  648. if (EmitResourceAccess(
  649. res,
  650. Call,
  651. HlslOP,
  652. Ctx,
  653. ShaderAccessFlags::Read))
  654. {
  655. Modified = true;
  656. }
  657. }
  658. continue;
  659. default:
  660. break;
  661. }
  662. for (unsigned iParam : handleParams) {
  663. auto res = GetResourceFromHandle(Call->getArgOperand(iParam), DM);
  664. if (res.accessStyle == AccessStyle::None) {
  665. continue;
  666. }
  667. // Don't instrument the accesses to the UAV that we just added
  668. if (res.RegisterSpace == -2) {
  669. break;
  670. }
  671. if (EmitResourceAccess(res, Call, HlslOP, Ctx, readWrite)) {
  672. Modified = true;
  673. }
  674. // Remaining resources are DescriptorRead.
  675. readWrite = ShaderAccessFlags::DescriptorRead;
  676. }
  677. }
  678. }
  679. if (OSOverride != nullptr) {
  680. formatted_raw_ostream FOS(*OSOverride);
  681. FOS << "DynamicallyIndexedBindPoints=";
  682. for (auto const &bp : m_DynamicallyIndexedBindPoints) {
  683. FOS << EncodeRegisterType(bp.Type) << bp.Space << ':' << bp.Index
  684. << ';';
  685. }
  686. FOS << ".";
  687. // todo: this will reflect dynamic resource names when the metadata exists
  688. FOS << "DynamicallyBoundResources=";
  689. for (auto const &drb : m_dynamicResourceBindings) {
  690. FOS << (drb.HeapIsSampler ? 'S' : 'R') << drb.HeapIndex << ';';
  691. }
  692. FOS << ".";
  693. }
  694. }
  695. return Modified;
  696. }
  697. char DxilShaderAccessTracking::ID = 0;
  698. ModulePass *llvm::createDxilShaderAccessTrackingPass() {
  699. return new DxilShaderAccessTracking();
  700. }
  701. INITIALIZE_PASS(DxilShaderAccessTracking,
  702. "hlsl-dxil-pix-shader-access-instrumentation",
  703. "HLSL DXIL shader access tracking for PIX", false, false)