Browse Source

Fixed incorrect PHI value in DxilValueCache. Also using it in a few more places. (#2668)

Adam Yang 5 years ago
parent
commit
7a8c4a20da

+ 2 - 0
include/dxc/DXIL/DxilMetadataHelper.h

@@ -12,6 +12,7 @@
 #pragma once
 #pragma once
 
 
 #include "dxc/DXIL/DxilConstants.h"
 #include "dxc/DXIL/DxilConstants.h"
+#include "llvm/ADT/ArrayRef.h"
 #include <memory>
 #include <memory>
 #include <string>
 #include <string>
 #include <vector>
 #include <vector>
@@ -504,6 +505,7 @@ public:
     unsigned &StartOffsetInBits, std::vector<DxilDIArrayDim> &ArrayDims);
     unsigned &StartOffsetInBits, std::vector<DxilDIArrayDim> &ArrayDims);
   static void SetVariableDebugLayout(llvm::DbgDeclareInst *inst,
   static void SetVariableDebugLayout(llvm::DbgDeclareInst *inst,
     unsigned StartOffsetInBits, const std::vector<DxilDIArrayDim> &ArrayDims);
     unsigned StartOffsetInBits, const std::vector<DxilDIArrayDim> &ArrayDims);
+  static void CopyMetadata(llvm::Instruction &I, llvm::Instruction &SrcInst, llvm::ArrayRef<unsigned>WL = llvm::ArrayRef<unsigned>());
 
 
 private:
 private:
   llvm::LLVMContext &m_Ctx;
   llvm::LLVMContext &m_Ctx;

+ 11 - 7
include/dxc/HLSL/DxilValueCache.h

@@ -15,7 +15,7 @@ namespace llvm {
 class Module;
 class Module;
 class DominatorTree;
 class DominatorTree;
 
 
-struct DxilValueCache : public ModulePass {
+struct DxilValueCache : public ImmutablePass {
   static char ID;
   static char ID;
 
 
   // Special Weak Value to Weak Value map.
   // Special Weak Value to Weak Value map.
@@ -36,6 +36,7 @@ struct DxilValueCache : public ModulePass {
     void Set(Value *Key, Value *V);
     void Set(Value *Key, Value *V);
     bool Seen(Value *v);
     bool Seen(Value *v);
     void SetSentinel(Value *V);
     void SetSentinel(Value *V);
+    void ResetUnknowns();
     void dump() const;
     void dump() const;
   private:
   private:
     Value *GetSentinel(LLVMContext &Ctx);
     Value *GetSentinel(LLVMContext &Ctx);
@@ -47,30 +48,33 @@ private:
   WeakValueMap ValueMap;
   WeakValueMap ValueMap;
 
 
   void MarkAlwaysReachable(BasicBlock *BB);
   void MarkAlwaysReachable(BasicBlock *BB);
-  void MarkNeverReachable(BasicBlock *BB);
+  void MarkUnreachable(BasicBlock *BB);
   bool IsAlwaysReachable_(BasicBlock *BB);
   bool IsAlwaysReachable_(BasicBlock *BB);
-  bool IsNeverReachable_(BasicBlock *BB);
-  Value *OptionallyGetValue(Value *V);
+  bool IsUnreachable_(BasicBlock *BB);
+  bool MayBranchTo(BasicBlock *A, BasicBlock *B);
+  Value *TryGetCachedValue(Value *V);
   Value *ProcessValue(Value *V, DominatorTree *DT);
   Value *ProcessValue(Value *V, DominatorTree *DT);
 
 
   Value *ProcessAndSimplify_PHI(Instruction *I, DominatorTree *DT);
   Value *ProcessAndSimplify_PHI(Instruction *I, DominatorTree *DT);
   Value *ProcessAndSimpilfy_Br(Instruction *I, DominatorTree *DT);
   Value *ProcessAndSimpilfy_Br(Instruction *I, DominatorTree *DT);
+  Value *ProcessAndSimpilfy_Load(Instruction *LI, DominatorTree *DT);
   Value *SimplifyAndCacheResult(Instruction *I, DominatorTree *DT);
   Value *SimplifyAndCacheResult(Instruction *I, DominatorTree *DT);
 
 
 public:
 public:
 
 
   const char *getPassName() const override;
   const char *getPassName() const override;
   DxilValueCache();
   DxilValueCache();
+  void getAnalysisUsage(AnalysisUsage &) const;
 
 
-  bool runOnModule(Module &M) override { return false; } // Doesn't do anything by itself.
   void dump() const;
   void dump() const;
   Value *GetValue(Value *V, DominatorTree *DT=nullptr);
   Value *GetValue(Value *V, DominatorTree *DT=nullptr);
+  void ResetUnknowns() { ValueMap.ResetUnknowns(); }
   bool IsAlwaysReachable(BasicBlock *BB, DominatorTree *DT=nullptr);
   bool IsAlwaysReachable(BasicBlock *BB, DominatorTree *DT=nullptr);
-  bool IsNeverReachable(BasicBlock *BB, DominatorTree *DT=nullptr);
+  bool IsUnreachable(BasicBlock *BB, DominatorTree *DT=nullptr);
 };
 };
 
 
 void initializeDxilValueCachePass(class llvm::PassRegistry &);
 void initializeDxilValueCachePass(class llvm::PassRegistry &);
-ModulePass *createDxilValueCachePass();
+Pass *createDxilValueCachePass();
 
 
 }
 }
 
 

+ 21 - 0
lib/DXIL/DxilMetadataHelper.cpp

@@ -27,6 +27,7 @@
 #include "llvm/IR/Metadata.h"
 #include "llvm/IR/Metadata.h"
 #include "llvm/IR/Module.h"
 #include "llvm/IR/Module.h"
 #include "llvm/Support/raw_ostream.h"
 #include "llvm/Support/raw_ostream.h"
+#include "llvm/ADT/DenseSet.h"
 #include <array>
 #include <array>
 #include <algorithm>
 #include <algorithm>
 
 
@@ -2483,4 +2484,24 @@ void DxilMDHelper::SetVariableDebugLayout(llvm::DbgDeclareInst *inst,
   inst->setMetadata(DxilMDHelper::kDxilVariableDebugLayoutMDName, MDNode::get(Ctx, MDVals));
   inst->setMetadata(DxilMDHelper::kDxilVariableDebugLayoutMDName, MDNode::get(Ctx, MDVals));
 }
 }
 
 
+void DxilMDHelper::CopyMetadata(Instruction &I, Instruction &SrcInst, ArrayRef<unsigned> WL) {
+  if (!SrcInst.hasMetadata())
+    return;
+
+  DenseSet<unsigned> WLS;
+  for (unsigned M : WL)
+    WLS.insert(M);
+
+  // Otherwise, enumerate and copy over metadata from the old instruction to the
+  // new one.
+  SmallVector<std::pair<unsigned, MDNode *>, 4> TheMDs;
+  SrcInst.getAllMetadataOtherThanDebugLoc(TheMDs);
+  for (const auto &MD : TheMDs) {
+    if (WL.empty() || WLS.count(MD.first))
+      I.setMetadata(MD.first, MD.second);
+  }
+  if (WL.empty() || WLS.count(LLVMContext::MD_dbg))
+    I.setDebugLoc(SrcInst.getDebugLoc());
+}
+
 } // namespace hlsl
 } // namespace hlsl

+ 162 - 115
lib/HLSL/DxilCondenseResources.cpp

@@ -22,6 +22,7 @@
 #include "dxc/HLSL/HLMatrixType.h"
 #include "dxc/HLSL/HLMatrixType.h"
 #include "dxc/HLSL/HLModule.h"
 #include "dxc/HLSL/HLModule.h"
 #include "dxc/HLSL/DxilValueCache.h"
 #include "dxc/HLSL/DxilValueCache.h"
+#include "dxc/DXIL/DxilMetadataHelper.h"
 
 
 #include "llvm/IR/Instructions.h"
 #include "llvm/IR/Instructions.h"
 #include "llvm/IR/IntrinsicInst.h"
 #include "llvm/IR/IntrinsicInst.h"
@@ -424,164 +425,209 @@ ModulePass *llvm::createDxilCondenseResourcesPass() {
 INITIALIZE_PASS(DxilCondenseResources, "hlsl-dxil-condense", "DXIL Condense Resources", false, false)
 INITIALIZE_PASS(DxilCondenseResources, "hlsl-dxil-condense", "DXIL Condense Resources", false, false)
 
 
 static
 static
-bool LegalizeResourcesPHIs(Module &M, DxilValueCache *DVC) {
+bool EraseDeadBlocks(Module &M, DxilValueCache *DVC) {
+  std::unordered_set<BasicBlock *> Seen;
+  std::vector<BasicBlock *> WorkList;
+
+  bool Changed = false;
 
 
-  // Simple pass to collect resource PHI's
-  SmallVector<PHINode *, 8> PHIs;
   for (Function &F : M) {
   for (Function &F : M) {
-    for (BasicBlock &BB : F) {
-      for (Instruction &I : BB) {
-        if (PHINode *PN = dyn_cast<PHINode>(&I)) {
-          if (hlsl::dxilutil::IsHLSLObjectType(PN->getType())) {
-            PHIs.push_back(PN);
-          }
+    if (F.isDeclaration())
+      continue;
+    Seen.clear();
+    WorkList.clear();
+
+    auto Add = [&WorkList, &Seen](BasicBlock *BB) {
+      if (!Seen.count(BB)) {
+        WorkList.push_back(BB);
+        Seen.insert(BB);
+      }
+    };
+
+    Add(&F.getEntryBlock());
+
+    // Go through blocks
+    while (WorkList.size()) {
+      BasicBlock *BB = WorkList.back();
+      WorkList.pop_back();
+
+      if (BranchInst *Br = dyn_cast<BranchInst>(BB->getTerminator())) {
+        if (Br->isUnconditional()) {
+          BasicBlock *Succ = Br->getSuccessor(0);
+          Add(Succ);
         }
         }
         else {
         else {
-          break;
+          bool IsConstant = false;
+          if (Value *V = DVC->GetValue(Br->getCondition())) {
+            if (ConstantInt *C = dyn_cast<ConstantInt>(V)) {
+              bool IsTrue = C->getLimitedValue() != 0;
+              BasicBlock *Succ = IsTrue ?
+                Br->getSuccessor(0) : Br->getSuccessor(1);
+              Add(Succ);
+              IsConstant = true;
+            }
+          }
+          if (!IsConstant) {
+            Add(Br->getSuccessor(0));
+            Add(Br->getSuccessor(1));
+          }
+        }
+      }
+      else if (SwitchInst *Switch = dyn_cast<SwitchInst>(BB->getTerminator())) {
+        for (unsigned i = 0; i < Switch->getNumSuccessors(); i++) {
+          Add(Switch->getSuccessor(i));
         }
         }
-
       }
       }
     }
     }
-  }
 
 
-  if (PHIs.empty())
-    return false;
+    if (Seen.size() == F.size())
+      continue;
 
 
-  // Do a very simple CFG simplification of removing diamond graphs.
-  std::vector<BasicBlock *> DeadBlocks;
-  std::unordered_set<BasicBlock *> DeadBlocksSet;
-  for (PHINode *PN : PHIs) {
-    for (unsigned i = 0; i < PN->getNumIncomingValues(); i++) {
-      BasicBlock *BB = PN->getIncomingBlock(i);
-      if (DeadBlocksSet.count(BB)) continue;
-      if (DVC->IsNeverReachable(BB)) {
-        DeadBlocksSet.insert(BB);
-        DeadBlocks.push_back(BB);
-      }
-    }
-  }
+    Changed = true;
 
 
-  bool Changed = false;
-  SmallVector<Value *, 3> CleanupValues;
-  SmallPtrSet<Value *, 3> CleanupValuesSet;
-  auto AddCleanupValues = [&CleanupValues, &CleanupValuesSet](Value *V) {
-    if (!CleanupValuesSet.count(V)) {
-      CleanupValuesSet.insert(V);
-      CleanupValues.push_back(V);
-    }
-  };
+    std::vector<BasicBlock *> DeadBlocks;
 
 
-  for (unsigned i = 0; i < DeadBlocks.size(); i++) {
-    BasicBlock *BB = DeadBlocks[i];
-    BasicBlock *Pred = BB->getSinglePredecessor();
-    BasicBlock *Succ = BB->getSingleSuccessor();
+    // Reconnect edges and everything
+    for (auto it = F.begin(); it != F.end();) {
+      BasicBlock *BB = &*(it++);
+      if (Seen.count(BB))
+        continue;
 
 
-    if (!Pred || !Succ)
-      continue;
+      DeadBlocks.push_back(BB);
 
 
-    // A very simple folding of diamond graph.
-    BranchInst *Br = cast<BranchInst>(Pred->getTerminator());
-    BasicBlock *Peer = nullptr;
-    if (Br->isConditional())
-      Peer = Br->getSuccessor(0) == BB ? 
+      // Make predecessors branch somewhere else and fix the phi nodes
+      for (auto pred_it = pred_begin(BB); pred_it != pred_end(BB);) {
+        BasicBlock *PredBB = *(pred_it++);
+        if (!Seen.count(PredBB))
+          continue;
+        TerminatorInst *TI = PredBB->getTerminator();
+        if (!TI) continue;
+        BranchInst *Br = dyn_cast<BranchInst>(TI);
+        if (!Br || Br->isUnconditional()) continue;
+
+        BasicBlock *Other = Br->getSuccessor(0) == BB ?
           Br->getSuccessor(1) : Br->getSuccessor(0);
           Br->getSuccessor(1) : Br->getSuccessor(0);
 
 
-    if (Peer && Peer->getSingleSuccessor() == Succ) {
-      Changed = true;
+        BranchInst *NewBr = BranchInst::Create(Other, Br);
+        hlsl::DxilMDHelper::CopyMetadata(*NewBr, *Br);
+        Br->eraseFromParent();
+      }
 
 
-      BranchInst::Create(Peer, Pred);
+      // Fix phi nodes in successors
+      for (auto succ_it = succ_begin(BB); succ_it != succ_end(BB); succ_it++) {
+        BasicBlock *SuccBB = *succ_it;
+        if (!Seen.count(SuccBB)) continue;
+        for (auto inst_it = SuccBB->begin(); inst_it != SuccBB->end();) {
+          Instruction *I = &*(inst_it++);
+          if (PHINode *PN = dyn_cast<PHINode>(I))
+            PN->removeIncomingValue(BB, true);
+          else
+            break;
+        }
+      }
 
 
-      Br->dropAllReferences();
-      Br->eraseFromParent();
-      auto PhiEnd = PHIs.end();
-      for (Instruction &I : *Succ)
-        if (PHINode *PN = dyn_cast<PHINode>(&I)) {
-          if (Instruction *IncomingI = dyn_cast<Instruction>(PN->getIncomingValueForBlock(BB))) {
-            if (!DeadBlocksSet.count(IncomingI->getParent()))
-              AddCleanupValues(IncomingI); // Mark the incoming value for deletion
-          }
-          PN->removeIncomingValue(BB);
+      // Erase all instructions in block
+      while (BB->size()) {
+        Instruction *I = &BB->back();
+        if (!I->getType()->isVoidTy())
+          I->replaceAllUsesWith(UndefValue::get(I->getType()));
+        I->eraseFromParent();
+      }
+    }
+
+    for (BasicBlock *BB : DeadBlocks) {
+      BB->eraseFromParent();
+    }
+
+  }
+
+  return Changed;
+}
 
 
-          if (PN->getNumIncomingValues() == 1) {
-            PN->replaceAllUsesWith(PN->getIncomingValue(0));
-            PhiEnd = std::remove(PHIs.begin(), PhiEnd, PN);
-            AddCleanupValues(PN); // Mark for deletion
+static
+bool LegalizeResourcesPHIs(Module &M, DxilValueCache *DVC) {
+  // Simple pass to collect resource PHI's
+  SmallVector<PHINode *, 8> PHIs;
+  for (Function &F : M) {
+    for (BasicBlock &BB : F) {
+      for (Instruction &I : BB) {
+        if (PHINode *PN = dyn_cast<PHINode>(&I)) {
+          if (hlsl::dxilutil::IsHLSLResourceType(PN->getType())) {
+            PHIs.push_back(PN);
           }
           }
         }
         }
-        else
+        else {
           break;
           break;
+        }
 
 
-      BB->dropAllReferences();
-      while (!BB->empty()){
-        Instruction *ChildI = &*BB->rbegin();
-        if (PHINode *PN = dyn_cast<PHINode>(ChildI))
-          PhiEnd = std::remove(PHIs.begin(), PhiEnd, PN);
-        ChildI->eraseFromParent();
       }
       }
-      BB->eraseFromParent();
     }
     }
   }
   }
 
 
-  unsigned Attempts = PHIs.size();
-  for (unsigned AttemptIdx = 0; AttemptIdx < Attempts; AttemptIdx++) {
+  if (PHIs.empty())
+    return false;
+
+  bool Changed = false;
+
+  SmallVector<Instruction *, 8> DCEWorklist;
+
+  // Try to simplify those PHI's with DVC and collect them in DCEWorklist
+  for (unsigned Attempt = 0, MaxAttempt = PHIs.size(); Attempt < MaxAttempt; Attempt++) {
     bool LocalChanged = false;
     bool LocalChanged = false;
-    for (auto It = PHIs.begin(); It != PHIs.end();) {
-      PHINode *PN = *It;
+    for (unsigned i = 0; i < PHIs.size(); i++) {
+      PHINode *PN = PHIs[i];
       if (Value *V = DVC->GetValue(PN)) {
       if (Value *V = DVC->GetValue(PN)) {
-
-        PHIs.erase(It);
-        AddCleanupValues(PN); // Mark for deletion later
         PN->replaceAllUsesWith(V);
         PN->replaceAllUsesWith(V);
-        Changed = true;
         LocalChanged = true;
         LocalChanged = true;
-
-        for (unsigned i = 0, C = PN->getNumIncomingValues(); i < C; i++) {
-          Value *IncomingV = PN->getIncomingValue(i);
-          if (IncomingV != V)
-            AddCleanupValues(IncomingV); // Mark the incoming value for deletion later
-        }
+        DCEWorklist.push_back(PN);
+        PHIs.erase(PHIs.begin() + i);
       }
       }
       else {
       else {
-        It++;
+        i++;
       }
       }
     }
     }
 
 
+    Changed |= LocalChanged;
     if (!LocalChanged)
     if (!LocalChanged)
       break;
       break;
   }
   }
 
 
-  // Simple DCE to remove all dependencies of the resource PHI nodes we removed.
-  // This may be a little too agressive
-  for (;;) {
-    bool LocalChanged = false;
-    // Must use a numeric idx instead of an interator, because
-    // we're modifying the array as we go. Iterator gets invalidated
-    // because they're just pointers.
-    for (unsigned Idx = 0; Idx < CleanupValues.size();) {
-      Value *V = CleanupValues[Idx];
-      if (Instruction *I = dyn_cast<Instruction>(V)) {
-        if (I->user_empty()) {
-          // Add dependencies to process
-          for (Value *Op : I->operands()) {
-            AddCleanupValues(Op);
-          }
-          LocalChanged = true;
-          I->eraseFromParent();
-          CleanupValues.erase(CleanupValues.begin() + Idx);
-        }
-        else {
-          Idx++;
-        }
-      }
-      else {
-        CleanupValues.erase(CleanupValues.begin() + Idx);
+  // Collect Resource GV loads
+  for (GlobalVariable &GV : M.globals()) {
+    Type *Ty = GV.getType()->getPointerElementType();
+    while (Ty->isArrayTy())
+      Ty = Ty->getArrayElementType();
+    if (!hlsl::dxilutil::IsHLSLResourceType(Ty))
+      continue;
+
+    SmallVector<User *, 4> WorkList(GV.user_begin(), GV.user_end());
+    while (WorkList.size()) {
+      User *U = WorkList.pop_back_val();
+      if (LoadInst *Load = dyn_cast<LoadInst>(U)) {
+        DCEWorklist.push_back(Load);
       }
       }
+      else if (GEPOperator *GEP = dyn_cast<GEPOperator>(U)) {
+        for (User *GepU : GEP->users())
+          WorkList.push_back(GepU);
+      } 
     }
     }
+  }
 
 
-    Changed |= LocalChanged;
-    if (!LocalChanged)
-      break;
+  // Simple DCE
+  while (DCEWorklist.size()) {
+    Instruction *I = DCEWorklist.back();
+    DCEWorklist.pop_back();
+    if (llvm::isInstructionTriviallyDead(I)) {
+      for (Use &Op : I->operands())
+        if (Instruction *OpI = dyn_cast<Instruction>(Op.get()))
+          DCEWorklist.push_back(OpI);
+      I->eraseFromParent();
+      // Remove the instruction from the worklist if it still exists in it.
+      DCEWorklist.erase(std::remove(DCEWorklist.begin(), DCEWorklist.end(), I),
+                     DCEWorklist.end());
+    }
   }
   }
+
   return Changed;
   return Changed;
 }
 }
 
 
@@ -651,6 +697,7 @@ public:
       return bChanged;
       return bChanged;
 
 
     DxilValueCache *DVC = &getAnalysis<DxilValueCache>();
     DxilValueCache *DVC = &getAnalysis<DxilValueCache>();
+    bChanged |= EraseDeadBlocks(M, DVC);
     bChanged |= LegalizeResourcesPHIs(M, DVC);
     bChanged |= LegalizeResourcesPHIs(M, DVC);
 
 
     // Make sure no select on resource.
     // Make sure no select on resource.

+ 25 - 4
lib/HLSL/DxilLegalizeSampleOffsetPass.cpp

@@ -10,6 +10,7 @@
 ///////////////////////////////////////////////////////////////////////////////
 ///////////////////////////////////////////////////////////////////////////////
 
 
 #include "dxc/HLSL/DxilGenerationPass.h"
 #include "dxc/HLSL/DxilGenerationPass.h"
+#include "dxc/HLSL/DxilValueCache.h"
 #include "dxc/DXIL/DxilModule.h"
 #include "dxc/DXIL/DxilModule.h"
 #include "dxc/DXIL/DxilOperations.h"
 #include "dxc/DXIL/DxilOperations.h"
 
 
@@ -46,6 +47,11 @@ public:
     return "DXIL legalize sample offset";
     return "DXIL legalize sample offset";
   }
   }
 
 
+  void getAnalysisUsage(AnalysisUsage &AU) const {
+    AU.addRequired<DxilValueCache>();
+    AU.setPreservesAll();
+  }
+
   bool runOnFunction(Function &F) override {
   bool runOnFunction(Function &F) override {
     DxilModule &DM = F.getParent()->GetOrCreateDxilModule();
     DxilModule &DM = F.getParent()->GetOrCreateDxilModule();
     hlsl::OP *hlslOP = DM.GetOP();
     hlsl::OP *hlslOP = DM.GetOP();
@@ -158,7 +164,8 @@ void DxilLegalizeSampleOffsetPass::TryUnrollLoop(
   // Always need mem2reg for simplify illegal offsets.
   // Always need mem2reg for simplify illegal offsets.
   PM.add(createPromoteMemoryToRegisterPass());
   PM.add(createPromoteMemoryToRegisterPass());
 
 
-  if (HasIllegalOffsetInLoop(illegalOffsets, F)) {
+  bool UnrollLoop = HasIllegalOffsetInLoop(illegalOffsets, F);
+  if (UnrollLoop) {
     PM.add(createCFGSimplificationPass());
     PM.add(createCFGSimplificationPass());
     PM.add(createLCSSAPass());
     PM.add(createLCSSAPass());
     PM.add(createLoopSimplifyPass());
     PM.add(createLoopSimplifyPass());
@@ -166,6 +173,11 @@ void DxilLegalizeSampleOffsetPass::TryUnrollLoop(
     PM.add(createLoopUnrollPass(-2, -1, 0, 0));
     PM.add(createLoopUnrollPass(-2, -1, 0, 0));
   }
   }
   PM.run(F);
   PM.run(F);
+
+  if (UnrollLoop) {
+    DxilValueCache *DVC = &getAnalysis<DxilValueCache>();
+    DVC->ResetUnknowns();
+  }
 }
 }
 
 
 void DxilLegalizeSampleOffsetPass::CollectIllegalOffsets(
 void DxilLegalizeSampleOffsetPass::CollectIllegalOffsets(
@@ -202,13 +214,22 @@ void DxilLegalizeSampleOffsetPass::CollectIllegalOffsets(
 
 
 void DxilLegalizeSampleOffsetPass::LegalizeOffsets(
 void DxilLegalizeSampleOffsetPass::LegalizeOffsets(
     const std::vector<Instruction *> &illegalOffsets) {
     const std::vector<Instruction *> &illegalOffsets) {
-  for (Instruction *I : illegalOffsets)
-    llvm::recursivelySimplifyInstruction(I);
+  if (illegalOffsets.size()) {
+    DxilValueCache *DVC = &getAnalysis<DxilValueCache>();
+    for (Instruction *I : illegalOffsets) {
+      if (Value *V = DVC->GetValue(I)) {
+        I->replaceAllUsesWith(V);
+      }
+    }
+  }
 }
 }
 
 
 FunctionPass *llvm::createDxilLegalizeSampleOffsetPass() {
 FunctionPass *llvm::createDxilLegalizeSampleOffsetPass() {
   return new DxilLegalizeSampleOffsetPass();
   return new DxilLegalizeSampleOffsetPass();
 }
 }
 
 
-INITIALIZE_PASS(DxilLegalizeSampleOffsetPass, "dxil-legalize-sample-offset",
+INITIALIZE_PASS_BEGIN(DxilLegalizeSampleOffsetPass, "dxil-legalize-sample-offset",
+                "DXIL legalize sample offset", false, false)
+INITIALIZE_PASS_DEPENDENCY(DxilValueCache)
+INITIALIZE_PASS_END(DxilLegalizeSampleOffsetPass, "dxil-legalize-sample-offset",
                 "DXIL legalize sample offset", false, false)
                 "DXIL legalize sample offset", false, false)

+ 89 - 35
lib/HLSL/DxilValueCache.cpp

@@ -22,9 +22,11 @@
 #include "llvm/IR/Dominators.h"
 #include "llvm/IR/Dominators.h"
 #include "llvm/Transforms/Scalar.h"
 #include "llvm/Transforms/Scalar.h"
 #include "llvm/Analysis/InstructionSimplify.h"
 #include "llvm/Analysis/InstructionSimplify.h"
+#include "llvm/Analysis/ConstantFolding.h"
 #include "llvm/ADT/Statistic.h"
 #include "llvm/ADT/Statistic.h"
 
 
 #include "dxc/HLSL/DxilValueCache.h"
 #include "dxc/HLSL/DxilValueCache.h"
+
 #include <unordered_set>
 #include <unordered_set>
 
 
 #define DEBUG_TYPE "dxil-value-cache"
 #define DEBUG_TYPE "dxil-value-cache"
@@ -52,7 +54,7 @@ bool IsEntryBlock(const BasicBlock *BB) {
 void DxilValueCache::MarkAlwaysReachable(BasicBlock *BB) {
 void DxilValueCache::MarkAlwaysReachable(BasicBlock *BB) {
   ValueMap.Set(BB, ConstantInt::get(Type::getInt1Ty(BB->getContext()), 1));
   ValueMap.Set(BB, ConstantInt::get(Type::getInt1Ty(BB->getContext()), 1));
 }
 }
-void DxilValueCache::MarkNeverReachable(BasicBlock *BB) {
+void DxilValueCache::MarkUnreachable(BasicBlock *BB) {
   ValueMap.Set(BB, ConstantInt::get(Type::getInt1Ty(BB->getContext()), 0));
   ValueMap.Set(BB, ConstantInt::get(Type::getInt1Ty(BB->getContext()), 0));
 }
 }
 
 
@@ -63,7 +65,21 @@ bool DxilValueCache::IsAlwaysReachable_(BasicBlock *BB) {
   return false;
   return false;
 }
 }
 
 
-bool DxilValueCache::IsNeverReachable_(BasicBlock *BB) {
+bool DxilValueCache::MayBranchTo(BasicBlock *A, BasicBlock *B) {
+  BranchInst *Br = dyn_cast<BranchInst>(A->getTerminator());
+  if (!Br) return false;
+
+  if (Br->isUnconditional() && Br->getSuccessor(0) == B)
+    return true;
+
+  if (ConstantInt *C = dyn_cast<ConstantInt>(TryGetCachedValue(Br->getCondition()))) {
+    unsigned SuccIndex = C->getLimitedValue() != 0 ? 0 : 1;
+    return Br->getSuccessor(SuccIndex) == B;
+  }
+  return true;
+}
+
+bool DxilValueCache::IsUnreachable_(BasicBlock *BB) {
   if (Value *V = ValueMap.Get(BB))
   if (Value *V = ValueMap.Get(BB))
     if (IsConstantFalse(V))
     if (IsConstantFalse(V))
       return true;
       return true;
@@ -74,14 +90,16 @@ Value *DxilValueCache::ProcessAndSimplify_PHI(Instruction *I, DominatorTree *DT)
   PHINode *PN = cast<PHINode>(I);
   PHINode *PN = cast<PHINode>(I);
   BasicBlock *SoleIncoming = nullptr;
   BasicBlock *SoleIncoming = nullptr;
 
 
+  bool Unreachable = true;
   Value *Simplified = nullptr;
   Value *Simplified = nullptr;
   for (unsigned i = 0; i < PN->getNumIncomingValues(); i++) {
   for (unsigned i = 0; i < PN->getNumIncomingValues(); i++) {
     BasicBlock *PredBB = PN->getIncomingBlock(i);
     BasicBlock *PredBB = PN->getIncomingBlock(i);
-    if (IsAlwaysReachable_(PredBB)) {
-      SoleIncoming = PredBB;
-      break;
-    }
-    else if (!IsNeverReachable_(PredBB)) {
+    if (IsUnreachable_(PredBB))
+      continue;
+
+    Unreachable = false;
+
+    if (MayBranchTo(PredBB, PN->getParent())) {
       if (SoleIncoming) {
       if (SoleIncoming) {
         SoleIncoming = nullptr;
         SoleIncoming = nullptr;
         break;
         break;
@@ -90,8 +108,12 @@ Value *DxilValueCache::ProcessAndSimplify_PHI(Instruction *I, DominatorTree *DT)
     }
     }
   }
   }
 
 
+  if (Unreachable) {
+    return UndefValue::get(I->getType());
+  }
+
   if (SoleIncoming) {
   if (SoleIncoming) {
-    Value *V = OptionallyGetValue(PN->getIncomingValueForBlock(SoleIncoming));
+    Value *V = TryGetCachedValue(PN->getIncomingValueForBlock(SoleIncoming));
     if (isa<Constant>(V))
     if (isa<Constant>(V))
       Simplified = V;
       Simplified = V;
     else if (Instruction *I = dyn_cast<Instruction>(V)) {
     else if (Instruction *I = dyn_cast<Instruction>(V)) {
@@ -119,7 +141,7 @@ Value *DxilValueCache::ProcessAndSimplify_PHI(Instruction *I, DominatorTree *DT)
   // One last step, to check if we have anything cached for whatever we
   // One last step, to check if we have anything cached for whatever we
   // simplified to.
   // simplified to.
   if (Simplified)
   if (Simplified)
-    Simplified = OptionallyGetValue(Simplified);
+    Simplified = TryGetCachedValue(Simplified);
 
 
   return Simplified;
   return Simplified;
 }
 }
@@ -138,39 +160,47 @@ Value *DxilValueCache::ProcessAndSimpilfy_Br(Instruction *I, DominatorTree *DT)
     BasicBlock *TrueSucc = Br->getSuccessor(0);
     BasicBlock *TrueSucc = Br->getSuccessor(0);
     BasicBlock *FalseSucc = Br->getSuccessor(1);
     BasicBlock *FalseSucc = Br->getSuccessor(1);
 
 
-    Value *Cond = OptionallyGetValue(Br->getCondition());
+    Value *Cond = TryGetCachedValue(Br->getCondition());
 
 
-    if (IsNeverReachable_(BB)) {
-      MarkNeverReachable(FalseSucc);
-      MarkNeverReachable(TrueSucc);
+    if (IsUnreachable_(BB)) {
+      MarkUnreachable(FalseSucc);
+      MarkUnreachable(TrueSucc);
     }
     }
     else if (IsConstantTrue(Cond)) {
     else if (IsConstantTrue(Cond)) {
       if (IsAlwaysReachable_(BB)) {
       if (IsAlwaysReachable_(BB)) {
         MarkAlwaysReachable(TrueSucc);
         MarkAlwaysReachable(TrueSucc);
       }
       }
       if (FalseSucc->getSinglePredecessor())
       if (FalseSucc->getSinglePredecessor())
-        MarkNeverReachable(FalseSucc);
+        MarkUnreachable(FalseSucc);
     }
     }
     else if (IsConstantFalse(Cond)) {
     else if (IsConstantFalse(Cond)) {
       if (IsAlwaysReachable_(BB)) {
       if (IsAlwaysReachable_(BB)) {
         MarkAlwaysReachable(FalseSucc);
         MarkAlwaysReachable(FalseSucc);
       }
       }
       if (TrueSucc->getSinglePredecessor())
       if (TrueSucc->getSinglePredecessor())
-        MarkNeverReachable(TrueSucc);
+        MarkUnreachable(TrueSucc);
     }
     }
   }
   }
   else {
   else {
     BasicBlock *Succ = Br->getSuccessor(0);
     BasicBlock *Succ = Br->getSuccessor(0);
     if (IsAlwaysReachable_(BB))
     if (IsAlwaysReachable_(BB))
       MarkAlwaysReachable(Succ);
       MarkAlwaysReachable(Succ);
-    else if (Succ->getSinglePredecessor() && IsNeverReachable_(BB))
-      MarkNeverReachable(Succ);
+    else if (Succ->getSinglePredecessor() && IsUnreachable_(BB))
+      MarkUnreachable(Succ);
   }
   }
 
 
   return nullptr;
   return nullptr;
 }
 }
 
 
-
+Value *DxilValueCache::ProcessAndSimpilfy_Load(Instruction *I, DominatorTree *DT) {
+  LoadInst *LI = cast<LoadInst>(I);
+  Value *V = TryGetCachedValue(LI->getPointerOperand());
+  if (Constant *ConstPtr = dyn_cast<Constant>(V)) {
+    const DataLayout &DL = I->getModule()->getDataLayout();
+    return llvm::ConstantFoldLoadFromConstPtr(ConstPtr, DL);
+  }
+  return nullptr;
+}
 
 
 Value *DxilValueCache::SimplifyAndCacheResult(Instruction *I, DominatorTree *DT) {
 Value *DxilValueCache::SimplifyAndCacheResult(Instruction *I, DominatorTree *DT) {
 
 
@@ -183,43 +213,54 @@ Value *DxilValueCache::SimplifyAndCacheResult(Instruction *I, DominatorTree *DT)
   else if (Instruction::PHI == I->getOpcode()) {
   else if (Instruction::PHI == I->getOpcode()) {
     Simplified = ProcessAndSimplify_PHI(I, DT);
     Simplified = ProcessAndSimplify_PHI(I, DT);
   }
   }
+  else if (Instruction::Load == I->getOpcode()) {
+    Simplified = ProcessAndSimpilfy_Load(I, DT);
+  }
   // The rest of the checks use LLVM stock simplifications
   // The rest of the checks use LLVM stock simplifications
   else if (I->isBinaryOp()) {
   else if (I->isBinaryOp()) {
     Simplified =
     Simplified =
       llvm::SimplifyBinOp(
       llvm::SimplifyBinOp(
         I->getOpcode(),
         I->getOpcode(),
-        OptionallyGetValue(I->getOperand(0)),
-        OptionallyGetValue(I->getOperand(1)),
+        TryGetCachedValue(I->getOperand(0)),
+        TryGetCachedValue(I->getOperand(1)),
         DL);
         DL);
   }
   }
+  else if (GetElementPtrInst *Gep = dyn_cast<GetElementPtrInst>(I)) {
+    SmallVector<Value *, 4> Values;
+    for (Value *V : Gep->operand_values()) {
+      Values.push_back(TryGetCachedValue(V));
+    }
+    Simplified =
+      llvm::SimplifyGEPInst(Values, DL, nullptr, DT, nullptr, nullptr);
+  }
   else if (CmpInst *Cmp = dyn_cast<CmpInst>(I)) {
   else if (CmpInst *Cmp = dyn_cast<CmpInst>(I)) {
     Simplified =
     Simplified =
       llvm::SimplifyCmpInst(Cmp->getPredicate(),
       llvm::SimplifyCmpInst(Cmp->getPredicate(),
-        OptionallyGetValue(I->getOperand(0)),
-        OptionallyGetValue(I->getOperand(1)),
+        TryGetCachedValue(I->getOperand(0)),
+        TryGetCachedValue(I->getOperand(1)),
         DL);
         DL);
   }
   }
   else if (SelectInst *Select = dyn_cast<SelectInst>(I)) {
   else if (SelectInst *Select = dyn_cast<SelectInst>(I)) {
     Simplified = 
     Simplified = 
       llvm::SimplifySelectInst(
       llvm::SimplifySelectInst(
-        OptionallyGetValue(Select->getCondition()),
-        OptionallyGetValue(Select->getTrueValue()),
-        OptionallyGetValue(Select->getFalseValue()),
+        TryGetCachedValue(Select->getCondition()),
+        TryGetCachedValue(Select->getTrueValue()),
+        TryGetCachedValue(Select->getFalseValue()),
         DL
         DL
       );
       );
   }
   }
   else if (ExtractElementInst *IE = dyn_cast<ExtractElementInst>(I)) {
   else if (ExtractElementInst *IE = dyn_cast<ExtractElementInst>(I)) {
     Simplified =
     Simplified =
       llvm::SimplifyExtractElementInst(
       llvm::SimplifyExtractElementInst(
-        OptionallyGetValue(IE->getVectorOperand()),
-        OptionallyGetValue(IE->getIndexOperand()),
+        TryGetCachedValue(IE->getVectorOperand()),
+        TryGetCachedValue(IE->getIndexOperand()),
         DL, nullptr, DT);
         DL, nullptr, DT);
   }
   }
   else if (CastInst *Cast = dyn_cast<CastInst>(I)) {
   else if (CastInst *Cast = dyn_cast<CastInst>(I)) {
     Simplified =
     Simplified =
       llvm::SimplifyCastInst(
       llvm::SimplifyCastInst(
         Cast->getOpcode(),
         Cast->getOpcode(),
-        OptionallyGetValue(Cast->getOperand(0)),
+        TryGetCachedValue(Cast->getOperand(0)),
         Cast->getType(), DL);
         Cast->getType(), DL);
   }
   }
 
 
@@ -269,6 +310,15 @@ Value *DxilValueCache::WeakValueMap::GetSentinel(LLVMContext &Ctx) {
   return Sentinel.get();
   return Sentinel.get();
 }
 }
 
 
+void DxilValueCache::WeakValueMap::ResetUnknowns() {
+  if (!Sentinel)
+    return;
+  for (auto it = Map.begin(); it != Map.end(); it++) {
+    if (it->second.Value == Sentinel.get())
+      it->second.Value = nullptr;
+  }
+}
+
 LLVM_DUMP_METHOD
 LLVM_DUMP_METHOD
 void DxilValueCache::WeakValueMap::dump() const {
 void DxilValueCache::WeakValueMap::dump() const {
   for (auto It = Map.begin(), E = Map.end(); It != E; It++) {
   for (auto It = Map.begin(), E = Map.end(); It != E; It++) {
@@ -305,13 +355,13 @@ void DxilValueCache::WeakValueMap::Set(Value *Key, Value *V) {
 
 
 // If there's a cached value, return it. Otherwise, return
 // If there's a cached value, return it. Otherwise, return
 // the value itself.
 // the value itself.
-Value *DxilValueCache::OptionallyGetValue(Value *V) {
+Value *DxilValueCache::TryGetCachedValue(Value *V) {
   if (Value *Simplified = ValueMap.Get(V))
   if (Value *Simplified = ValueMap.Get(V))
     return Simplified;
     return Simplified;
   return V;
   return V;
 }
 }
 
 
-DxilValueCache::DxilValueCache() : ModulePass(ID) {
+DxilValueCache::DxilValueCache() : ImmutablePass(ID) {
   initializeDxilValueCachePass(*PassRegistry::getPassRegistry());
   initializeDxilValueCachePass(*PassRegistry::getPassRegistry());
 }
 }
 
 
@@ -330,9 +380,9 @@ bool DxilValueCache::IsAlwaysReachable(BasicBlock *BB, DominatorTree *DT) {
   return IsAlwaysReachable_(BB);
   return IsAlwaysReachable_(BB);
 }
 }
 
 
-bool DxilValueCache::IsNeverReachable(BasicBlock *BB, DominatorTree *DT) {
+bool DxilValueCache::IsUnreachable(BasicBlock *BB, DominatorTree *DT) {
   ProcessValue(BB, DT);
   ProcessValue(BB, DT);
-  return IsNeverReachable_(BB);
+  return IsUnreachable_(BB);
 }
 }
 
 
 LLVM_DUMP_METHOD
 LLVM_DUMP_METHOD
@@ -340,6 +390,10 @@ void DxilValueCache::dump() const {
   ValueMap.dump();
   ValueMap.dump();
 }
 }
 
 
+void DxilValueCache::getAnalysisUsage(AnalysisUsage &AU) const {
+  AU.setPreservesAll();
+}
+
 Value *DxilValueCache::ProcessValue(Value *NewV, DominatorTree *DT) {
 Value *DxilValueCache::ProcessValue(Value *NewV, DominatorTree *DT) {
 
 
   Value *Result = nullptr;
   Value *Result = nullptr;
@@ -425,13 +479,13 @@ Value *DxilValueCache::ProcessValue(Value *NewV, DominatorTree *DT) {
         if (!IsEntryBlock(BB)) {
         if (!IsEntryBlock(BB)) {
           bool AllNeverReachable = true;
           bool AllNeverReachable = true;
           for (pred_iterator PI = pred_begin(BB), E = pred_end(BB); PI != E; PI++) {
           for (pred_iterator PI = pred_begin(BB), E = pred_end(BB); PI != E; PI++) {
-            if (!IsNeverReachable_(BB)) {
+            if (!IsUnreachable_(BB)) {
               AllNeverReachable = false;
               AllNeverReachable = false;
               break;
               break;
             }
             }
           }
           }
           if (AllNeverReachable)
           if (AllNeverReachable)
-            MarkNeverReachable(BB);
+            MarkUnreachable(BB);
         }
         }
 
 
       }
       }
@@ -443,7 +497,7 @@ Value *DxilValueCache::ProcessValue(Value *NewV, DominatorTree *DT) {
 
 
 char DxilValueCache::ID;
 char DxilValueCache::ID;
 
 
-ModulePass *llvm::createDxilValueCachePass() {
+Pass *llvm::createDxilValueCachePass() {
   return new DxilValueCache();
   return new DxilValueCache();
 }
 }
 
 

+ 7 - 2
tools/clang/test/HLSLFileCheck/dxil/debug/value_cache/cfg.hlsl

@@ -7,7 +7,7 @@ static bool gG2;
 static bool gG3;
 static bool gG3;
 
 
 Texture2D tex0 : register(t0);
 Texture2D tex0 : register(t0);
-Texture2D tex1 : register(t1);
+Texture2D tex1 : register(t42);
 Texture2D tex2 : register(t2);
 Texture2D tex2 : register(t2);
 
 
 Texture2D f(bool foo) {
 Texture2D f(bool foo) {
@@ -21,11 +21,16 @@ Texture2D h(bool foo3) {
   return foo3 ? f(gG2) : tex2;
   return foo3 ? f(gG2) : tex2;
 }
 }
 
 
-[RootSignature("DescriptorTable(SRV(t0, numDescriptors=3))")]
+[RootSignature("DescriptorTable(SRV(t0, numDescriptors=3), SRV(t42))")]
 float4 main() : sv_target {
 float4 main() : sv_target {
+
+  // CHECK: %[[handle:.+]] = call %dx.types.Handle @dx.op.createHandle(i32 57, i8 0, i32 1, i32 42
+
   gG = true;
   gG = true;
   gG2 = false;
   gG2 = false;
   gG3 = false;
   gG3 = false;
+
+  // CHECK: @dx.op.textureLoad.f32(i32 66, %dx.types.Handle %[[handle]]
   return h(gG).Load(0);
   return h(gG).Load(0);
 };
 };
 
 

+ 12 - 4
tools/clang/test/HLSLFileCheck/dxil/debug/value_cache/cfg2.hlsl

@@ -1,5 +1,8 @@
 // RUN: %dxc -E main -T ps_6_0 -Od %s | FileCheck %s
 // RUN: %dxc -E main -T ps_6_0 -Od %s | FileCheck %s
 
 
+// Make sure unused resources are handled even when
+// they're arrays.
+
 // CHECK: @main
 // CHECK: @main
 
 
 static bool gG;
 static bool gG;
@@ -7,8 +10,9 @@ static bool gG2;
 static bool gG3;
 static bool gG3;
 
 
 Texture2D tex0 : register(t0);
 Texture2D tex0 : register(t0);
-Texture2D tex1 : register(t1);
+Texture2D tex1 : register(t42);
 Texture2D tex2 : register(t2);
 Texture2D tex2 : register(t2);
+Texture2D tex3 : register(t3);
 
 
 Texture2D f(bool foo) {
 Texture2D f(bool foo) {
   [branch]
   [branch]
@@ -20,20 +24,24 @@ Texture2D f(bool foo) {
 Texture2D g(bool foo) {
 Texture2D g(bool foo) {
   [branch]
   [branch]
   if (foo)
   if (foo)
-    return tex1;
-  else
     return tex2;
     return tex2;
+  else
+    return tex3;
 }
 }
 
 
 Texture2D h(bool foo3) {
 Texture2D h(bool foo3) {
   return foo3 ? f(gG2) : g(gG3);
   return foo3 ? f(gG2) : g(gG3);
 }
 }
 
 
-[RootSignature("DescriptorTable(SRV(t0, numDescriptors=3))")]
+[RootSignature("DescriptorTable(SRV(t0, numDescriptors=4), SRV(t42))")]
 float4 main() : sv_target {
 float4 main() : sv_target {
+  // CHECK: %[[handle:.+]] = call %dx.types.Handle @dx.op.createHandle(i32 57, i8 0, i32 1, i32 42
+
   gG = true;
   gG = true;
   gG2 = false;
   gG2 = false;
   gG3 = false;
   gG3 = false;
+
+  // CHECK: @dx.op.textureLoad.f32(i32 66, %dx.types.Handle %[[handle]]
   return h(gG).Load(0);
   return h(gG).Load(0);
 };
 };
 
 

+ 21 - 0
tools/clang/test/HLSLFileCheck/dxil/debug/value_cache/const_global.hlsl

@@ -0,0 +1,21 @@
+// RUN: %dxc -E main -T ps_6_6 %s -Od | FileCheck %s
+
+// CHECK: local resource not guaranteed to map to unique global resource
+
+Texture2D tex0 : register(t0);
+Texture2D tex1 : register(t42);
+
+const float my_const = 0;
+
+[RootSignature("CBV(b0), DescriptorTable(SRV(t0), SRV(t42))")]
+float4 main(uint2 uv : TEXCOORD) : SV_Target {
+
+  float val = my_const;
+
+  Texture2D tex = tex0;
+  if (val > 0) {
+    tex = tex1;
+  }
+  return tex.Load(0);
+}
+

+ 28 - 0
tools/clang/test/HLSLFileCheck/dxil/debug/value_cache/load.hlsl

@@ -0,0 +1,28 @@
+// RUN: %dxc -E main -T ps_6_6 %s -Od | FileCheck %s
+
+Texture2D tex0 : register(t0);
+Texture2D tex1 : register(t42);
+
+const static float2 my_offsets[] = {
+  float2(1,2),
+  float2(3,4),
+  float2(5,6),
+  float2(7,8),
+};
+
+[RootSignature("DescriptorTable(SRV(t0), SRV(t42)), DescriptorTable(Sampler(s0))")]
+float4 main(uint2 uv : TEXCOORD) : SV_Target {
+  // CHECK: %[[handle:.+]] = call %dx.types.Handle @dx.op.createHandle(i32 57, i8 0, i32 1, i32 42
+  int x = 0;
+  int y = 0;
+
+  float2 val = my_offsets[x+y+1];
+
+  Texture2D tex = tex0;
+  if (val.x > 0) {
+    tex = tex1;
+  }
+  // CHECK: @dx.op.textureLoad.f32(i32 66, %dx.types.Handle %[[handle]]
+  return tex.Load(0);
+}
+

+ 27 - 0
tools/clang/test/HLSLFileCheck/dxil/debug/value_cache/phi.hlsl

@@ -0,0 +1,27 @@
+// RUN: %dxc -E main -T ps_6_0 %s -Od | FileCheck %s
+
+// CHECK: Offsets for Sample* must be immediated value
+
+// Regression test that DxilValueCache (DVC) isn't so over-zealous.
+
+// There was a bug where DVC decides for a PHI node, the predecessor that is
+// always reachable must always be the block that branched to it, and could
+// therefore take on its incoming value (assuming the value dominates the PHI
+// node)
+
+// In the case below, the value of 'x' at the sample statement is not decidable
+// at compile time. This test makes sure the compilation fails and displays the
+// correct message.
+
+Texture2D tex0 : register(t0);
+SamplerState samp0 : register(s0);
+
+[RootSignature("DescriptorTable(SRV(t0)), DescriptorTable(Sampler(s0))")]
+float4 main(float2 uv : TEXCOORD, int a : A, int b : B) : SV_Target {
+  int x = 0;
+  if (a > b) {
+    x = 1;
+  }
+  return tex0.Sample(samp0, uv, int2(x,x));
+}
+

+ 28 - 0
tools/clang/test/HLSLFileCheck/dxil/debug/value_cache/resource_array.hlsl

@@ -0,0 +1,28 @@
+// RUN: %dxc -E main -T ps_6_6 %s -Od | FileCheck %s
+
+Texture2D tex0[42] : register(t0);
+Texture2D tex1 : register(t42);
+
+const static float2 my_offsets[] = {
+  float2(1,2),
+  float2(3,4),
+  float2(5,6),
+  float2(7,8),
+};
+
+[RootSignature("DescriptorTable(SRV(t0, numDescriptors=42), SRV(t42)), DescriptorTable(Sampler(s0))")]
+float4 main(uint2 uv : TEXCOORD, uint a : A) : SV_Target {
+  // CHECK: %[[handle:.+]] = call %dx.types.Handle @dx.op.createHandle(i32 57, i8 0, i32 1, i32 42
+  int x = 0;
+  int y = 0;
+
+  float2 val = my_offsets[x+y+1];
+
+  Texture2D tex = tex0[a];
+  if (val.x > 0) {
+    tex = tex1;
+  }
+  // CHECK: @dx.op.textureLoad.f32(i32 66, %dx.types.Handle %[[handle]]
+  return tex.Load(0);
+}
+

+ 59 - 0
tools/clang/test/HLSLFileCheck/dxil/debug/value_cache/resource_array2.hlsl

@@ -0,0 +1,59 @@
+// RUN: %dxc -E main -T ps_6_0 -Od %s | FileCheck %s
+
+// Make sure unused resources are handled even when
+// they're arrays.
+
+// CHECK: @main
+
+static bool gG;
+static bool gG2;
+static bool gG3;
+
+Texture2D tex0[42] : register(t0);
+Texture2D tex1 : register(t42);
+Texture2D tex2[2][2] : register(t43);
+
+const uint idx1;
+const uint idx2;
+const uint idx3;
+
+static Texture2D local_tex1;
+static Texture2D local_tex2;
+static Texture2D local_tex3;
+
+Texture2D f(bool foo) {
+  [branch]
+  if (foo)
+    return local_tex1;
+  else
+    return tex1;
+}
+Texture2D g(bool foo) {
+  [branch]
+  if (foo)
+    return local_tex2;
+  else
+    return local_tex3;
+}
+
+Texture2D h(bool foo3) {
+  return foo3 ? f(gG2) : g(gG3);
+}
+
+[RootSignature("CBV(b0), DescriptorTable(SRV(t0, numDescriptors=42), SRV(t42), SRV(t43, numDescriptors=4))")]
+float4 main() : sv_target {
+  // CHECK: %[[handle:.+]] = call %dx.types.Handle @dx.op.createHandle(i32 57, i8 0, i32 1, i32 42
+
+  local_tex1 = tex0[idx1];
+  local_tex2 = tex0[idx2];
+  local_tex3 = tex2[idx3][idx1];
+
+  gG = true;
+  gG2 = false;
+  gG3 = false;
+
+  // CHECK: @dx.op.textureLoad.f32(i32 66, %dx.types.Handle %[[handle]]
+  return h(gG).Load(0);
+};
+
+

+ 26 - 0
tools/clang/test/HLSLFileCheck/dxil/debug/value_cache/right_branch.hlsl

@@ -0,0 +1,26 @@
+// RUN: %dxc -E main -T ps_6_6 %s -Od | FileCheck %s
+
+// Make sure DxilValueCache actually predicts the correct branch
+
+Texture2D tex0 : register(t0);
+Texture2D tex1 : register(t42);
+
+[RootSignature("DescriptorTable(SRV(t0), SRV(t42))")]
+float4 main() : SV_Target {
+  // CHECK: %[[handle:.+]] = call %dx.types.Handle @dx.op.createHandle(i32 57, i8 0, i32 1, i32 42
+
+  float x = 10;
+  float y = x + 5;
+  float z = y * 2;
+  float w = z / 0.5;
+
+  Texture2D tex = tex0; 
+
+  if (w >= 0) {
+    tex = tex1;
+  }
+
+  // CHECK: @dx.op.textureLoad.f32(i32 66, %dx.types.Handle %[[handle]]
+  return tex.Load(0) + float4(x,y,z,w);
+}
+

+ 31 - 0
tools/clang/test/HLSLFileCheck/dxil/debug/value_cache/sample_unroll.hlsl

@@ -0,0 +1,31 @@
+// RUN: %dxc -E main -T ps_6_0 %s -Od | FileCheck %s
+
+// When we sample texture in a loop, in effort to legalize
+// the offsets, an unroll will be done. This tests that
+// it still works in Od.
+
+// CHECK: @main
+// CHECK: call %dx.types.ResRet.f32 @dx.op.sample.f32(i32 60
+// CHECK-SAME: i32 -1, i32 -1
+// CHECK: call %dx.types.ResRet.f32 @dx.op.sample.f32(i32 60
+// CHECK-SAME: i32 0, i32 0
+// CHECK: call %dx.types.ResRet.f32 @dx.op.sample.f32(i32 60
+// CHECK-SAME: i32 1, i32 1
+// CHECK: call %dx.types.ResRet.f32 @dx.op.sample.f32(i32 60
+// CHECK-SAME: i32 2, i32 2
+
+Texture2D tex0 : register(t0);
+Texture2D tex1 : register(t1);
+SamplerState samp0 : register(s0);
+
+[RootSignature("DescriptorTable(SRV(t0), SRV(t1)), DescriptorTable(Sampler(s0))")]
+float4 main(float2 uv : TEXCOORD) : SV_Target {
+
+  float4 ret = float4(0,0,0,0);
+  for (int i = -1; i < 3; i++) {
+    ret += tex0.Sample(samp0, uv, int2(i,i));
+  }
+
+  return ret;
+}
+