DxilAnnotateWithVirtualRegister.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413
  1. ///////////////////////////////////////////////////////////////////////////////
  2. // //
  3. // DxilAnnotateWithVirtualRegister.cpp //
  4. // Copyright (C) Microsoft Corporation. All rights reserved. //
  5. // This file is distributed under the University of Illinois Open Source //
  6. // This file is distributed under the University of Illinois Open Source //
  7. // License. See LICENSE.TXT for details. //
  8. // //
  9. // Annotates the llvm instructions with a virtual register number to be used //
  10. // during PIX debugging. //
  11. // //
  12. ///////////////////////////////////////////////////////////////////////////////
  13. #include <memory>
  14. #include "dxc/DXIL/DxilModule.h"
  15. #include "dxc/DxilPIXPasses/DxilPIXPasses.h"
  16. #include "dxc/DxilPIXPasses/DxilPIXVirtualRegisters.h"
  17. #include "dxc/Support/Global.h"
  18. #include "llvm/ADT/DenseMap.h"
  19. #include "llvm/ADT/StringRef.h"
  20. #include "llvm/IR/Constant.h"
  21. #include "llvm/IR/Constants.h"
  22. #include "llvm/IR/DebugInfo.h"
  23. #include "llvm/IR/DebugInfoMetadata.h"
  24. #include "llvm/IR/IRBuilder.h"
  25. #include "llvm/IR/InstIterator.h"
  26. #include "llvm/IR/Instruction.h"
  27. #include "llvm/IR/Instructions.h"
  28. #include "llvm/IR/IntrinsicInst.h"
  29. #include "llvm/IR/Module.h"
  30. #include "llvm/IR/ModuleSlotTracker.h"
  31. #include "llvm/IR/Type.h"
  32. #include "llvm/Pass.h"
  33. #include "llvm/Support/Casting.h"
  34. #include "llvm/Support/raw_ostream.h"
  35. #define DEBUG_TYPE "dxil-annotate-with-virtual-regs"
  36. uint32_t CountStructMembers(llvm::Type const *pType) {
  37. uint32_t Count = 0;
  38. if (auto *ST = llvm::dyn_cast<llvm::StructType>(pType)) {
  39. for (auto &El : ST->elements()) {
  40. Count += CountStructMembers(El);
  41. }
  42. } else if (auto *AT = llvm::dyn_cast<llvm::ArrayType>(pType)) {
  43. Count = CountStructMembers(AT->getArrayElementType()) *
  44. AT->getArrayNumElements();
  45. } else {
  46. Count = 1;
  47. }
  48. return Count;
  49. }
  50. namespace {
  51. using namespace pix_dxil;
  52. static bool IsInstrumentableFundamentalType(llvm::Type *pAllocaTy) {
  53. return
  54. pAllocaTy->isFloatingPointTy() || pAllocaTy->isIntegerTy();
  55. }
  56. class DxilAnnotateWithVirtualRegister : public llvm::ModulePass {
  57. public:
  58. static char ID;
  59. DxilAnnotateWithVirtualRegister() : llvm::ModulePass(ID) {}
  60. bool runOnModule(llvm::Module &M) override;
  61. private:
  62. void AnnotateValues(llvm::Instruction *pI);
  63. void AnnotateStore(llvm::Instruction *pI);
  64. bool IsAllocaRegisterWrite(llvm::Value *V, llvm::AllocaInst **pAI,
  65. llvm::Value **pIdx);
  66. void AnnotateAlloca(llvm::AllocaInst *pAlloca);
  67. void AnnotateGeneric(llvm::Instruction *pI);
  68. void AssignNewDxilRegister(llvm::Instruction *pI);
  69. void PrintSingleRegister(llvm::Instruction* pI, uint32_t Register);
  70. void AssignNewAllocaRegister(llvm::AllocaInst* pAlloca, std::uint32_t C);
  71. void PrintAllocaMember(llvm::AllocaInst* pAlloca, uint32_t Base, uint32_t Offset);
  72. hlsl::DxilModule* m_DM;
  73. std::uint32_t m_uVReg;
  74. std::unique_ptr<llvm::ModuleSlotTracker> m_MST;
  75. void Init(llvm::Module &M) {
  76. m_DM = &M.GetOrCreateDxilModule();
  77. m_uVReg = 0;
  78. m_MST.reset(new llvm::ModuleSlotTracker(&M));
  79. m_MST->incorporateFunction(*m_DM->GetEntryFunction());
  80. }
  81. };
  82. char DxilAnnotateWithVirtualRegister::ID = 0;
  83. bool DxilAnnotateWithVirtualRegister::runOnModule(llvm::Module &M) {
  84. Init(M);
  85. if (m_DM == nullptr) {
  86. return false;
  87. }
  88. unsigned int Major = 0;
  89. unsigned int Minor = 0;
  90. m_DM->GetDxilVersion(Major, Minor);
  91. if (Major < 6 || (Major == 6 && Minor <= 4)) {
  92. m_DM->SetValidatorVersion(1, 4);
  93. }
  94. std::uint32_t InstNum = 0;
  95. for (llvm::Instruction &I : llvm::inst_range(m_DM->GetEntryFunction())) {
  96. if (!llvm::isa<llvm::DbgDeclareInst>(&I)) {
  97. pix_dxil::PixDxilInstNum::AddMD(M.getContext(), &I, InstNum++);
  98. }
  99. }
  100. if (OSOverride != nullptr) {
  101. *OSOverride << "\nInstructionCount:" << InstNum << "\n";
  102. }
  103. if (OSOverride != nullptr) {
  104. *OSOverride << "\nEnd - instruction ID to line\n";
  105. }
  106. if (OSOverride != nullptr) {
  107. *OSOverride << "\nBegin - dxil values to virtual register mapping\n";
  108. }
  109. for (llvm::Instruction &I : llvm::inst_range(m_DM->GetEntryFunction())) {
  110. AnnotateValues(&I);
  111. }
  112. for (llvm::Instruction &I : llvm::inst_range(m_DM->GetEntryFunction())) {
  113. AnnotateStore(&I);
  114. }
  115. if (OSOverride != nullptr) {
  116. *OSOverride << "\nEnd - dxil values to virtual register mapping\n";
  117. }
  118. m_DM = nullptr;
  119. return m_uVReg > 0;
  120. }
  121. void DxilAnnotateWithVirtualRegister::AnnotateValues(llvm::Instruction *pI) {
  122. if (auto* pAlloca = llvm::dyn_cast<llvm::AllocaInst>(pI)) {
  123. AnnotateAlloca(pAlloca);
  124. } else if (!pI->getType()->isPointerTy()) {
  125. AnnotateGeneric(pI);
  126. } else if (!pI->getType()->isVoidTy()) {
  127. AnnotateGeneric(pI);
  128. }
  129. }
  130. void DxilAnnotateWithVirtualRegister::AnnotateStore(llvm::Instruction *pI) {
  131. auto *pSt = llvm::dyn_cast<llvm::StoreInst>(pI);
  132. if (pSt == nullptr) {
  133. return;
  134. }
  135. llvm::AllocaInst *Alloca;
  136. llvm::Value *Index;
  137. if (!IsAllocaRegisterWrite(pSt->getPointerOperand(), &Alloca, &Index)) {
  138. return;
  139. }
  140. llvm::MDNode *AllocaReg = Alloca->getMetadata(PixAllocaReg::MDName);
  141. if (AllocaReg == nullptr) {
  142. return;
  143. }
  144. PixAllocaRegWrite::AddMD(m_DM->GetCtx(), pSt, AllocaReg, Index);
  145. }
  146. static uint32_t GetStructOffset(
  147. llvm::GetElementPtrInst* pGEP,
  148. uint32_t& GEPOperandIndex,
  149. llvm::Type* pElementType)
  150. {
  151. if (IsInstrumentableFundamentalType(pElementType)) {
  152. return 0;
  153. }
  154. else if (auto* pArray = llvm::dyn_cast<llvm::ArrayType>(pElementType))
  155. {
  156. // 1D-array example:
  157. //
  158. // When referring to the zeroth member of the array in this struct:
  159. // struct smallPayload {
  160. // uint32_t Array[2];
  161. // };
  162. // getelementptr inbounds% struct.smallPayload, % struct.smallPayload*% p,
  163. // i32 0, i32 0, i32 0 The zeros above are:
  164. // -The zeroth element in the array pointed to (so, the actual struct)
  165. // -The zeroth element in the struct (which is the array)
  166. // -The zeroth element in that array
  167. auto* pArrayIndex =
  168. llvm::dyn_cast<llvm::ConstantInt>(pGEP->getOperand(GEPOperandIndex++));
  169. if (pArrayIndex == nullptr) {
  170. return 0;
  171. }
  172. uint32_t ArrayIndex = pArrayIndex->getLimitedValue();
  173. auto pArrayElementType = pArray->getArrayElementType();
  174. uint32_t MemberIndex = ArrayIndex * CountStructMembers(pArrayElementType);
  175. return MemberIndex + GetStructOffset(pGEP, GEPOperandIndex, pArrayElementType);
  176. }
  177. else if (auto* pStruct = llvm::dyn_cast<llvm::StructType>(pElementType))
  178. {
  179. DXASSERT(GEPOperandIndex < pGEP->getNumOperands(), "Unexpectedly read too many GetElementPtrInst operands");
  180. auto* pMemberIndex =
  181. llvm::dyn_cast<llvm::ConstantInt>(pGEP->getOperand(GEPOperandIndex++));
  182. if (pMemberIndex == nullptr) {
  183. return 0;
  184. }
  185. uint32_t MemberIndex = pMemberIndex->getLimitedValue();
  186. uint32_t MemberOffset = 0;
  187. for (uint32_t i = 0; i < MemberIndex; ++i)
  188. {
  189. MemberOffset += CountStructMembers(pStruct->getElementType(i));
  190. }
  191. return MemberOffset +
  192. GetStructOffset(pGEP, GEPOperandIndex, pStruct->getElementType(MemberIndex));
  193. }
  194. else
  195. {
  196. return 0;
  197. }
  198. }
  199. bool DxilAnnotateWithVirtualRegister::IsAllocaRegisterWrite(
  200. llvm::Value *V, llvm::AllocaInst **pAI, llvm::Value **pIdx) {
  201. llvm::IRBuilder<> B(m_DM->GetCtx());
  202. *pAI = nullptr;
  203. *pIdx = nullptr;
  204. if (auto *pGEP = llvm::dyn_cast<llvm::GetElementPtrInst>(V)) {
  205. uint32_t precedingMemberCount = 0;
  206. auto *Alloca = llvm::dyn_cast<llvm::AllocaInst>(pGEP->getPointerOperand());
  207. if (Alloca == nullptr) {
  208. // In the case of vector types (floatN, matrixNxM), the pointer operand will actually
  209. // point to another element pointer instruction. But this isn't a recursive thing-
  210. // we only need to check these two levels.
  211. if (auto* pPointerGEP = llvm::dyn_cast<llvm::GetElementPtrInst>(pGEP->getPointerOperand())) {
  212. Alloca =
  213. llvm::dyn_cast<llvm::AllocaInst>(pPointerGEP->getPointerOperand());
  214. if (Alloca == nullptr) {
  215. return false;
  216. }
  217. // And of course the member we're after might not be at the beginning of the struct:
  218. auto* pStructType = llvm::dyn_cast<llvm::StructType>(pPointerGEP->getPointerOperandType()->getPointerElementType());
  219. auto* pStructMember = llvm::dyn_cast<llvm::ConstantInt>(pPointerGEP->getOperand(2));
  220. uint64_t memberIndex = pStructMember->getLimitedValue();
  221. for(uint64_t i = 0; i < memberIndex; ++i)
  222. {
  223. precedingMemberCount += CountStructMembers(pStructType->getStructElementType(i));
  224. }
  225. }
  226. else
  227. {
  228. return false;
  229. }
  230. }
  231. // Deref pointer type to get struct type:
  232. llvm::Type *pStructType = pGEP->getPointerOperandType();
  233. pStructType = pStructType->getContainedType(0);
  234. // The 1th operand is an index into the array of the above type. In DXIL derived from HLSL,
  235. // we always expect this to be 0 (since llvm structs are only used for single-valued
  236. // objects in HLSL, such as the amplification-to-mesh or TraceRays payloads).
  237. uint32_t GEPOperandIndex = 1;
  238. auto *pBaseArrayIndex =
  239. llvm::dyn_cast<llvm::ConstantInt>(pGEP->getOperand(GEPOperandIndex++));
  240. DXASSERT_LOCALVAR(pBaseArrayIndex, pBaseArrayIndex != nullptr,
  241. "null base array index pointer");
  242. DXASSERT_LOCALVAR(pBaseArrayIndex, pBaseArrayIndex->getLimitedValue() == 0,
  243. "unexpected >0 array index");
  244. // From here on, the indices always come in groups: first, the type
  245. // referenced in the current struct. If that type is an (n-dimensional)
  246. // array, then there follow n indices.
  247. auto offset = GetStructOffset(
  248. pGEP,
  249. GEPOperandIndex,
  250. pStructType);
  251. llvm::Value* IndexValue = B.getInt32(offset + precedingMemberCount);
  252. if (IndexValue != nullptr)
  253. {
  254. *pAI = Alloca;
  255. *pIdx = IndexValue;
  256. return true;
  257. }
  258. return false;
  259. }
  260. if (auto *pAlloca = llvm::dyn_cast<llvm::AllocaInst>(V)) {
  261. llvm::Type *pAllocaTy = pAlloca->getType()->getElementType();
  262. if (!IsInstrumentableFundamentalType(pAllocaTy)) {
  263. return false;
  264. }
  265. *pAI = pAlloca;
  266. *pIdx = B.getInt32(0);
  267. return true;
  268. }
  269. return false;
  270. }
  271. void DxilAnnotateWithVirtualRegister::AnnotateAlloca(
  272. llvm::AllocaInst *pAlloca) {
  273. llvm::Type *pAllocaTy = pAlloca->getType()->getElementType();
  274. if (IsInstrumentableFundamentalType(pAllocaTy)) {
  275. AssignNewAllocaRegister(pAlloca, 1);
  276. } else if (auto *AT = llvm::dyn_cast<llvm::ArrayType>(pAllocaTy)) {
  277. AssignNewAllocaRegister(pAlloca, AT->getNumElements());
  278. } else if (auto *ST = llvm::dyn_cast<llvm::StructType>(pAllocaTy)) {
  279. AssignNewAllocaRegister(pAlloca, CountStructMembers(ST));
  280. } else {
  281. DXASSERT_ARGS(false, "Unhandled alloca kind: %d", pAllocaTy->getTypeID());
  282. }
  283. }
  284. void DxilAnnotateWithVirtualRegister::AnnotateGeneric(llvm::Instruction *pI) {
  285. if (auto *GEP = llvm::dyn_cast<llvm::GetElementPtrInst>(pI)) {
  286. // https://llvm.org/docs/LangRef.html#getelementptr-instruction
  287. DXASSERT(!GEP->getOperand(1)->getType()->isVectorTy(),
  288. "struct vectors not supported");
  289. llvm::AllocaInst *StructAlloca =
  290. llvm::dyn_cast<llvm::AllocaInst>(GEP->getOperand(0));
  291. if (StructAlloca != nullptr) {
  292. // This is the case of a pointer to a struct member.
  293. // We treat it as an alias of the actual member in the alloca.
  294. std::uint32_t baseStructRegNum = 0;
  295. std::uint32_t regSize = 0;
  296. if (pix_dxil::PixAllocaReg::FromInst(StructAlloca, &baseStructRegNum, & regSize)) {
  297. llvm::ConstantInt *OffsetAsInt =
  298. llvm::dyn_cast<llvm::ConstantInt>(GEP->getOperand(2));
  299. if (OffsetAsInt != nullptr)
  300. {
  301. std::uint32_t Offset = static_cast<std::uint32_t>(
  302. OffsetAsInt->getValue().getLimitedValue());
  303. DXASSERT(Offset < regSize,
  304. "Structure member offset out of expected range");
  305. PixDxilReg::AddMD(m_DM->GetCtx(), pI, baseStructRegNum + Offset);
  306. }
  307. }
  308. }
  309. } else {
  310. if (!IsInstrumentableFundamentalType(pI->getType())) {
  311. return;
  312. }
  313. AssignNewDxilRegister(pI);
  314. }
  315. }
  316. void DxilAnnotateWithVirtualRegister::AssignNewDxilRegister(
  317. llvm::Instruction *pI) {
  318. PixDxilReg::AddMD(m_DM->GetCtx(), pI, m_uVReg);
  319. PrintSingleRegister(pI, m_uVReg);
  320. m_uVReg++;
  321. }
  322. void DxilAnnotateWithVirtualRegister::AssignNewAllocaRegister(
  323. llvm::AllocaInst *pAlloca, std::uint32_t C) {
  324. PixAllocaReg::AddMD(m_DM->GetCtx(), pAlloca, m_uVReg, C);
  325. PrintAllocaMember(pAlloca, m_uVReg, C);
  326. m_uVReg += C;
  327. }
  328. void DxilAnnotateWithVirtualRegister::PrintSingleRegister(
  329. llvm::Instruction* pI, uint32_t Register) {
  330. if (OSOverride != nullptr) {
  331. static constexpr bool DontPrintType = false;
  332. pI->printAsOperand(*OSOverride, DontPrintType, *m_MST.get());
  333. *OSOverride << " dxil " << Register << "\n";
  334. }
  335. }
  336. void DxilAnnotateWithVirtualRegister::PrintAllocaMember(llvm::AllocaInst* pAlloca,
  337. uint32_t Base,
  338. uint32_t Offset) {
  339. if (OSOverride != nullptr) {
  340. static constexpr bool DontPrintType = false;
  341. pAlloca->printAsOperand(*OSOverride, DontPrintType, *m_MST.get());
  342. *OSOverride << " alloca " << Base << " " << Offset << "\n";
  343. }
  344. }
  345. } // namespace
  346. using namespace llvm;
  347. INITIALIZE_PASS(DxilAnnotateWithVirtualRegister, DEBUG_TYPE,
  348. "Annotates each instruction in the DXIL module with a virtual "
  349. "register number",
  350. false, false)
  351. ModulePass *llvm::createDxilAnnotateWithVirtualRegisterPass() {
  352. return new DxilAnnotateWithVirtualRegister();
  353. }