DxilLegalizeSampleOffsetPass.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. ///////////////////////////////////////////////////////////////////////////////
  2. // //
  3. // DxilSignature.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. // DxilLegalizeSampleOffsetPass implementation. //
  9. // //
  10. ///////////////////////////////////////////////////////////////////////////////
  11. #include "dxc/HLSL/DxilGenerationPass.h"
  12. #include "llvm/Analysis/DxilValueCache.h"
  13. #include "dxc/DXIL/DxilModule.h"
  14. #include "dxc/DXIL/DxilOperations.h"
  15. #include "dxc/DXIL/DxilUtil.h"
  16. #include "llvm/Analysis/InstructionSimplify.h"
  17. #include "llvm/Analysis/LoopInfo.h"
  18. #include "llvm/IR/DebugInfo.h"
  19. #include "llvm/IR/Constants.h"
  20. #include "llvm/IR/Dominators.h"
  21. #include "llvm/IR/Instructions.h"
  22. #include "llvm/IR/LLVMContext.h"
  23. #include "llvm/IR/LegacyPassManager.h"
  24. #include "llvm/IR/PassManager.h"
  25. #include "llvm/Pass.h"
  26. #include "llvm/Transforms/Scalar.h"
  27. #include <unordered_set>
  28. using std::vector;
  29. using std::unique_ptr;
  30. using namespace llvm;
  31. using namespace hlsl;
  32. ///////////////////////////////////////////////////////////////////////////////
  33. // Legalize Sample offset.
  34. namespace {
  35. // record of the offset value and the call that uses it
  36. // Used mainly for error detection and reporting
  37. struct Offset {
  38. Value *offset;
  39. CallInst *call;
  40. };
  41. // When optimizations are disabled, try to legalize sample offset.
  42. class DxilLegalizeSampleOffsetPass : public FunctionPass {
  43. LoopInfo LI;
  44. public:
  45. static char ID; // Pass identification, replacement for typeid
  46. explicit DxilLegalizeSampleOffsetPass() : FunctionPass(ID) {}
  47. const char *getPassName() const override {
  48. return "DXIL legalize sample offset";
  49. }
  50. void getAnalysisUsage(AnalysisUsage &AU) const override {
  51. AU.addRequired<DxilValueCache>();
  52. AU.setPreservesAll();
  53. }
  54. bool runOnFunction(Function &F) override {
  55. DxilModule &DM = F.getParent()->GetOrCreateDxilModule();
  56. hlsl::OP *hlslOP = DM.GetOP();
  57. std::vector<Offset> illegalOffsets;
  58. CollectIllegalOffsets(illegalOffsets, F, hlslOP);
  59. if (illegalOffsets.empty())
  60. return false;
  61. // Loop unroll if has offset inside loop.
  62. TryUnrollLoop(illegalOffsets, F);
  63. // Collect offset again after mem2reg.
  64. std::vector<Offset> ssaIllegalOffsets;
  65. CollectIllegalOffsets(ssaIllegalOffsets, F, hlslOP);
  66. // Run simple optimization to legalize offsets.
  67. LegalizeOffsets(ssaIllegalOffsets);
  68. FinalCheck(F, hlslOP);
  69. return true;
  70. }
  71. private:
  72. void TryUnrollLoop(std::vector<Offset> &illegalOffsets, Function &F);
  73. void CollectIllegalOffsets(std::vector<Offset> &illegalOffsets,
  74. Function &F, hlsl::OP *hlslOP);
  75. void CollectIllegalOffsets(std::vector<Offset> &illegalOffsets,
  76. Function &F, DXIL::OpCode opcode,
  77. hlsl::OP *hlslOP);
  78. void LegalizeOffsets(const std::vector<Offset> &illegalOffsets);
  79. void FinalCheck(Function &F, hlsl::OP *hlslOP);
  80. };
  81. char DxilLegalizeSampleOffsetPass::ID = 0;
  82. bool HasIllegalOffsetInLoop(std::vector<Offset> &illegalOffsets, LoopInfo &LI,
  83. Function &F) {
  84. DominatorTreeAnalysis DTA;
  85. DominatorTree DT = DTA.run(F);
  86. LI.Analyze(DT);
  87. bool findOffset = false;
  88. for (auto it : illegalOffsets) {
  89. if (const Instruction *I = dyn_cast<Instruction>(it.offset)) {
  90. const BasicBlock *BB = I->getParent();
  91. // TODO: determine whether values are actually loop dependent, not just in a loop
  92. if (LI.getLoopFor(BB)) {
  93. findOffset = true;
  94. break;
  95. }
  96. }
  97. }
  98. return findOffset;
  99. }
  100. void GetOffsetRange(DXIL::OpCode opcode, unsigned &offsetStart, unsigned &offsetEnd)
  101. {
  102. switch(opcode) {
  103. case DXIL::OpCode::TextureLoad:
  104. offsetStart = DXIL::OperandIndex::kTextureLoadOffset0OpIdx;
  105. offsetEnd = DXIL::OperandIndex::kTextureLoadOffset2OpIdx;
  106. break;
  107. case DXIL::OpCode::TextureGather:
  108. case DXIL::OpCode::TextureGatherCmp:
  109. case DXIL::OpCode::TextureGatherImm:
  110. case DXIL::OpCode::TextureGatherCmpImm:
  111. offsetStart = DXIL::OperandIndex::kTextureGatherOffset0OpIdx;
  112. offsetEnd = DXIL::OperandIndex::kTextureGatherOffset1OpIdx;
  113. break;
  114. default:
  115. // everything else are sample variants
  116. offsetStart = DXIL::OperandIndex::kTextureSampleOffset0OpIdx;
  117. offsetEnd = DXIL::OperandIndex::kTextureSampleOffset2OpIdx;
  118. break;
  119. }
  120. }
  121. void CollectIllegalOffset(CallInst *CI, DXIL::OpCode opcode,
  122. std::vector<Offset> &illegalOffsets) {
  123. unsigned offsetStart = 0, offsetEnd = 0;
  124. GetOffsetRange(opcode, offsetStart, offsetEnd);
  125. Value *offset0 =
  126. CI->getArgOperand(offsetStart);
  127. // No offsets
  128. if (isa<UndefValue>(offset0))
  129. return;
  130. for (unsigned i = offsetStart; i <= offsetEnd; i++) {
  131. Value *offset = CI->getArgOperand(i);
  132. if (Instruction *I = dyn_cast<Instruction>(offset)) {
  133. Offset offset = {I, CI};
  134. illegalOffsets.emplace_back(offset);
  135. }
  136. else if(ConstantInt *cOffset = dyn_cast<ConstantInt>(offset)) {
  137. int64_t val = cOffset->getValue().getSExtValue();
  138. if (val > 7 || val < -8) {
  139. Offset offset = {cOffset, CI};
  140. illegalOffsets.emplace_back(offset);
  141. }
  142. }
  143. }
  144. }
  145. }
  146. // Return true if the call instruction in pair a and b are the same
  147. bool InstEq(const Offset &a, const Offset &b) {
  148. return a.call == b.call;
  149. }
  150. // Return true if the call instruction in pair a is before that in pair b
  151. bool InstLT(const Offset &a, const Offset &b) {
  152. DebugLoc aLoc = a.call->getDebugLoc();
  153. DebugLoc bLoc = b.call->getDebugLoc();
  154. if (aLoc && bLoc) {
  155. DIScope *aScope = cast<DIScope>(aLoc->getRawScope());
  156. DIScope *bScope = cast<DIScope>(bLoc->getRawScope());
  157. std::string aFile = aScope->getFilename();
  158. std::string bFile = bScope->getFilename();
  159. return aFile < bFile || (aFile == bFile && aLoc.getLine() < bLoc.getLine());
  160. }
  161. // No line numbers, just compare pointers so that matching instructions will be adjacent
  162. return a.call < b.call;
  163. }
  164. void DxilLegalizeSampleOffsetPass::FinalCheck(Function &F, hlsl::OP *hlslOP) {
  165. // Collect offset to make sure no illegal offsets.
  166. std::vector<Offset> finalIllegalOffsets;
  167. CollectIllegalOffsets(finalIllegalOffsets, F, hlslOP);
  168. if (!finalIllegalOffsets.empty()) {
  169. std::string errorMsg = "Offsets to texture access operations must be immediate values. ";
  170. auto offsetBegin = finalIllegalOffsets.begin();
  171. auto offsetEnd = finalIllegalOffsets.end();
  172. std::sort(offsetBegin, offsetEnd, InstLT);
  173. offsetEnd = std::unique(offsetBegin, offsetEnd, InstEq);
  174. for (auto it = offsetBegin; it != offsetEnd; it++) {
  175. CallInst *CI = it->call;
  176. if (Instruction *offset = dyn_cast<Instruction>(it->offset)) {
  177. if (LI.getLoopFor(offset->getParent()))
  178. dxilutil::EmitErrorOnInstruction(CI, errorMsg + "Unrolling the loop containing the offset value"
  179. " manually and using -O3 may help in some cases.\n");
  180. else
  181. dxilutil::EmitErrorOnInstruction(CI, errorMsg);
  182. } else {
  183. dxilutil::EmitErrorOnInstruction(CI, "Offsets to texture access operations must be between -8 and 7. ");
  184. }
  185. }
  186. }
  187. }
  188. void DxilLegalizeSampleOffsetPass::TryUnrollLoop(
  189. std::vector<Offset> &illegalOffsets, Function &F) {
  190. legacy::FunctionPassManager PM(F.getParent());
  191. // Scalarize aggregates as mem2reg only applies on scalars.
  192. PM.add(createSROAPass());
  193. // Always need mem2reg for simplify illegal offsets.
  194. PM.add(createPromoteMemoryToRegisterPass());
  195. bool UnrollLoop = HasIllegalOffsetInLoop(illegalOffsets, LI, F);
  196. if (UnrollLoop) {
  197. PM.add(createCFGSimplificationPass());
  198. PM.add(createLCSSAPass());
  199. PM.add(createLoopSimplifyPass());
  200. PM.add(createLoopRotatePass());
  201. PM.add(createLoopUnrollPass(-2, -1, 0, 0));
  202. }
  203. PM.run(F);
  204. if (UnrollLoop) {
  205. DxilValueCache *DVC = &getAnalysis<DxilValueCache>();
  206. DVC->ResetUnknowns();
  207. }
  208. }
  209. void DxilLegalizeSampleOffsetPass::CollectIllegalOffsets(
  210. std::vector<Offset> &illegalOffsets, Function &CurF,
  211. hlsl::OP *hlslOP) {
  212. CollectIllegalOffsets(illegalOffsets, CurF, DXIL::OpCode::Sample, hlslOP);
  213. CollectIllegalOffsets(illegalOffsets, CurF, DXIL::OpCode::SampleBias, hlslOP);
  214. CollectIllegalOffsets(illegalOffsets, CurF, DXIL::OpCode::SampleCmp, hlslOP);
  215. CollectIllegalOffsets(illegalOffsets, CurF, DXIL::OpCode::SampleCmpLevelZero,
  216. hlslOP);
  217. CollectIllegalOffsets(illegalOffsets, CurF, DXIL::OpCode::SampleGrad, hlslOP);
  218. CollectIllegalOffsets(illegalOffsets, CurF, DXIL::OpCode::SampleLevel,
  219. hlslOP);
  220. CollectIllegalOffsets(illegalOffsets, CurF, DXIL::OpCode::TextureGatherImm, hlslOP);
  221. CollectIllegalOffsets(illegalOffsets, CurF, DXIL::OpCode::TextureGatherCmpImm, hlslOP);
  222. CollectIllegalOffsets(illegalOffsets, CurF, DXIL::OpCode::TextureLoad, hlslOP);
  223. }
  224. void DxilLegalizeSampleOffsetPass::CollectIllegalOffsets(
  225. std::vector<Offset> &illegalOffsets, Function &CurF,
  226. DXIL::OpCode opcode, hlsl::OP *hlslOP) {
  227. auto &intrFuncList = hlslOP->GetOpFuncList(opcode);
  228. for (auto it : intrFuncList) {
  229. Function *intrFunc = it.second;
  230. if (!intrFunc)
  231. continue;
  232. for (User *U : intrFunc->users()) {
  233. CallInst *CI = cast<CallInst>(U);
  234. // Skip inst not in current function.
  235. if (CI->getParent()->getParent() != &CurF)
  236. continue;
  237. CollectIllegalOffset(CI, opcode, illegalOffsets);
  238. }
  239. }
  240. }
  241. void DxilLegalizeSampleOffsetPass::LegalizeOffsets(
  242. const std::vector<Offset> &illegalOffsets) {
  243. if (!illegalOffsets.empty()) {
  244. DxilValueCache *DVC = &getAnalysis<DxilValueCache>();
  245. for (auto it : illegalOffsets) {
  246. if (Instruction *I = dyn_cast<Instruction>(it.offset))
  247. if (Value *V = DVC->GetValue(I))
  248. I->replaceAllUsesWith(V);
  249. }
  250. }
  251. }
  252. FunctionPass *llvm::createDxilLegalizeSampleOffsetPass() {
  253. return new DxilLegalizeSampleOffsetPass();
  254. }
  255. INITIALIZE_PASS_BEGIN(DxilLegalizeSampleOffsetPass, "dxil-legalize-sample-offset",
  256. "DXIL legalize sample offset", false, false)
  257. INITIALIZE_PASS_DEPENDENCY(DxilValueCache)
  258. INITIALIZE_PASS_END(DxilLegalizeSampleOffsetPass, "dxil-legalize-sample-offset",
  259. "DXIL legalize sample offset", false, false)