Переглянути джерело

Only promote local resource when has store of handle. (#87)

* Only promote local resource when has store of handle.

* Report error when cannot map local resource usage to global resource.

* Promote static resource when optimization is disabled too.

* Take care PHINode on local resource.
Xiang Li 8 роки тому
батько
коміт
726c754221

+ 165 - 28
lib/HLSL/DxilGenerationPass.cpp

@@ -44,13 +44,13 @@ using namespace hlsl;
 
 namespace {
 
-class ResourcePromoter : public LoadAndStorePromoter {
+class LocalResourcePromoter : public LoadAndStorePromoter {
   AllocaInst *AI;
   AllocaInst *NewAI;
   bool        HasUnmappedLd;
   std::unordered_map<Instruction *, Value *> &handleMap;
 public:
-  ResourcePromoter(ArrayRef<Instruction *> Insts, SSAUpdater &S,
+  LocalResourcePromoter(ArrayRef<Instruction *> Insts, SSAUpdater &S,
                    std::unordered_map<Instruction *, Value *> &handleMap)
       : LoadAndStorePromoter(Insts, S), AI(nullptr), handleMap(handleMap),
         HasUnmappedLd(false) {}
@@ -87,19 +87,84 @@ public:
     return cast<StoreInst>(I)->getPointerOperand() == AI;
   }
 
-  void updateDebugInfo(Instruction *Inst) const override {
-    // Store use go this path.
+  void replaceLoadWithValue(LoadInst *LI, Value *V) const override {
+    if (PHINode *phi = dyn_cast<PHINode>(V)) {
+      LI->replaceAllUsesWith(phi);
+      // Add nullptr for phi, will create real handle in
+      // AddCreateHandleForPhiNode.
+      handleMap[phi] = nullptr;
+      return;
+    }
+
+    // Load use go here.
     // Clone to keep the debug info.
-    Instruction *NewInst = Inst->clone();
+    Instruction *NewInst = LI->clone();
     NewInst->replaceUsesOfWith(AI, NewAI);
-    IRBuilder<> Builder(Inst);
+    IRBuilder<> Builder(LI);
     Builder.Insert(NewInst);
+    LI->replaceAllUsesWith(NewInst);
+
+    // Mark handle map.
+    // If cannot find, will return false in run();
+    if (Instruction *I = dyn_cast<Instruction>(V)) {
+      if (handleMap.count(I)) {
+        Instruction *handle = cast<Instruction>(handleMap[I]);
+        // Clone the handle to save debug info of LI.
+        handle = handle->clone();
+        Builder.Insert(handle);
+        handleMap[NewInst] = handle;
+      }
+    }
+  }
+};
+
+class StaticResourcePromoter : public LoadAndStorePromoter {
+  GlobalVariable *GV;
+  bool        HasUnmappedLd;
+  std::unordered_map<Instruction *, Value *> &handleMap;
+public:
+  StaticResourcePromoter(ArrayRef<Instruction *> Insts, SSAUpdater &S,
+                   std::unordered_map<Instruction *, Value *> &handleMap)
+      : LoadAndStorePromoter(Insts, S), GV(nullptr), handleMap(handleMap),
+        HasUnmappedLd(false) {}
+
+  bool run(GlobalVariable *GV, const SmallVectorImpl<Instruction *> &Insts) {
+    // Remember which alloca we're promoting (for isInstInList).
+    this->GV = GV;
+
+    LoadAndStorePromoter::run(Insts);
+
+    bool allLoadMapped = true;
+    for (User *U : GV->users()) {
+      if (LoadInst *LI = dyn_cast<LoadInst>(U)) {
+        if (handleMap.count(LI) == 0) {
+          allLoadMapped = false;
+          break;
+        }
+      }
+    }
+    return allLoadMapped;
   }
+
+  bool
+  isInstInList(Instruction *I,
+               const SmallVectorImpl<Instruction *> &Insts) const override {
+    if (LoadInst *LI = dyn_cast<LoadInst>(I))
+      return LI->getOperand(0) == GV;
+    return cast<StoreInst>(I)->getPointerOperand() == GV;
+  }
+
   void replaceLoadWithValue(LoadInst *LI, Value *V) const override {
+    if (PHINode *phi = dyn_cast<PHINode>(V)) {
+      LI->replaceAllUsesWith(phi);
+      // Add nullptr for phi, will create real handle in
+      // AddCreateHandleForPhiNode.
+      handleMap[phi] = nullptr;
+      return;
+    }
     // Load use go here.
     // Clone to keep the debug info.
     Instruction *NewInst = LI->clone();
-    NewInst->replaceUsesOfWith(AI, NewAI);
     IRBuilder<> Builder(LI);
     Builder.Insert(NewInst);
     LI->replaceAllUsesWith(NewInst);
@@ -147,6 +212,7 @@ public:
       if (PHI->user_empty())
         unusedPhis.insert(PHI);
     }
+    LI->replaceAllUsesWith(UndefValue::get(LI->getType()));
   }
 };
 
@@ -349,6 +415,9 @@ public:
       // Add local resource load to handle map.
       MapLocalDxilResourceHandles(handleMap);
     }
+    // Take care phi node of resource.
+    AddCreateHandleForPhiNode(handleMap, m_pHLModule->GetOP());
+
     GenerateParamDxilResourceHandles(handleMap);
 
     GenerateDxilOperations(M, handleMap);
@@ -398,13 +467,16 @@ private:
   void GenerateClipPlanesForVS(Value *outPosition);
   bool HasClipPlanes();
 
-  void TranslateLocalDxilResourceUses(Function *F, std::unordered_map<Instruction *, Value *> &handleMap);
+  void TranslateLocalDxilResourceUses(
+      Function *F, std::vector<GlobalVariable *> &staticResources,
+      std::unordered_map<Instruction *, Value *> &handleMap);
   void RemoveLocalDxilResourceAllocas(Function *F);
   void MapLocalDxilResourceHandles(
       std::unordered_map<Instruction *, Value *> &handleMap);
   void TranslateDxilResourceUses(
       DxilResourceBase &res,
       std::unordered_map<Instruction *, Value *> &handleMap);
+  void AddCreateHandleForPhiNode(std::unordered_map<Instruction *, Value *> &handleMap, OP *hlslOP);
   void GenerateDxilResourceHandles(
       std::unordered_map<Instruction *, Value *> &handleMap);
   void TranslateParamDxilResourceHandles(Function *F, std::unordered_map<Instruction *, Value *> &handleMap);
@@ -1734,7 +1806,9 @@ static Value *MergeImmResClass(Value *resClass) {
   }
 }
 
-static void AddCreateHandleForPhiNode(std::unordered_map<Instruction *, Value *> &handleMap, OP *hlslOP) {
+static const StringRef kResourceMapErrorMsg = "local resource not guaranteed to map to unique global resource.";
+
+void DxilGenerationPass::AddCreateHandleForPhiNode(std::unordered_map<Instruction *, Value *> &handleMap, OP *hlslOP) {
   Function *createHandle = hlslOP->GetOpFunc(
       OP::OpCode::CreateHandle, llvm::Type::getVoidTy(hlslOP->GetCtx()));
 
@@ -1804,9 +1878,17 @@ static void AddCreateHandleForPhiNode(std::unordered_map<Instruction *, Value *>
     for (unsigned i = 0; i < numOperands; i++) {
 
       BasicBlock *BB = phi->getIncomingBlock(i);
-
+      if (isa<UndefValue>(phi->getOperand(i))) {
+        phi->getContext().emitError(
+            phi, kResourceMapErrorMsg);
+        return;
+      }
       Instruction *phiOperand = cast<Instruction>(phi->getOperand(i));
-      DXASSERT(handleMap.count(phiOperand), "must map to handle");
+      if (!handleMap.count(phiOperand)) {
+        phi->getContext().emitError(
+            phi, kResourceMapErrorMsg);
+        return;
+      }
       CallInst *handleI = cast<CallInst>(handleMap[phiOperand]);
 
       Value *resClassI = handleI->getArgOperand(
@@ -1832,6 +1914,22 @@ static void AddCreateHandleForPhiNode(std::unordered_map<Instruction *, Value *>
     Value *immResClass = MergeImmResClass(resClass);
     handle0->setArgOperand(DXIL::OperandIndex::kCreateHandleResClassOpIdx,
                            immResClass);
+
+    CallInst *handlePhi = cast<CallInst>(handleMap[phi]);
+
+    if (PHINode *resID = dyn_cast<PHINode>(handlePhi->getArgOperand(
+            DXIL::OperandIndex::kCreateHandleResIDOpIdx))) {
+      unsigned numOperands = resID->getNumOperands();
+      if (numOperands > 0) {
+        Value *resID0 = resID->getIncomingValue(0);
+        for (unsigned i=1;i<numOperands;i++) {
+          if (resID->getIncomingValue(i) != resID0) {
+            resID->getContext().emitError(handle0, kResourceMapErrorMsg);
+            break;
+          }
+        }
+      }
+    }
   }
 
   // Drop all ref of the phi to help remove the useless createHandles.
@@ -1844,10 +1942,10 @@ static void AddCreateHandleForPhiNode(std::unordered_map<Instruction *, Value *>
   }
 }
 
-static bool IsResourceAlloc(AllocaInst *AI) {
-  bool isResource = HLModule::IsHLSLObjectType(AI->getAllocatedType());
+static bool IsResourceType(Type *Ty) {
+  bool isResource = HLModule::IsHLSLObjectType(Ty);
 
-  if (ArrayType *AT = dyn_cast<ArrayType>(AI->getAllocatedType())) {
+  if (ArrayType *AT = dyn_cast<ArrayType>(Ty)) {
     Type *EltTy = AT->getElementType();
     while (isa<ArrayType>(EltTy)) {
       EltTy = EltTy->getArrayElementType();
@@ -1859,12 +1957,15 @@ static bool IsResourceAlloc(AllocaInst *AI) {
   return isResource;
 }
 
-void DxilGenerationPass::TranslateLocalDxilResourceUses(Function *F, std::unordered_map<Instruction *, Value *> &handleMap) {
+void DxilGenerationPass::TranslateLocalDxilResourceUses(Function *F, std::vector<GlobalVariable*> &staticResources,
+    std::unordered_map<Instruction *, Value *> &handleMap) {
   BasicBlock &BB = F->getEntryBlock(); // Get the entry node for the function
-  std::unordered_set<AllocaInst *> localResources;
+  std::unordered_set<Value *> localResources(staticResources.begin(),
+                                             staticResources.end());
+
   for (BasicBlock::iterator I = BB.begin(), E = --BB.end(); I != E; ++I)
     if (AllocaInst *AI = dyn_cast<AllocaInst>(I)) { // Is it an alloca?
-      if (IsResourceAlloc(AI)) {
+      if (IsResourceType(AI->getAllocatedType())) {
         localResources.insert(AI);
       }
     }
@@ -1873,19 +1974,49 @@ void DxilGenerationPass::TranslateLocalDxilResourceUses(Function *F, std::unorde
   SmallVector<Instruction *, 4> Insts;
   // Make sure every resource load has mapped to handle.
   while (!localResources.empty()) {
+    bool bUpdated = false;
     for (auto it = localResources.begin(); it != localResources.end();) {
-      AllocaInst *AI = *(it++);
+      Value *V = *(it++);
+      bool hasHandleStore = false;
       // Build list of instructions to promote.
-      for (User *U : AI->users())
-        Insts.emplace_back(cast<Instruction>(U));
+      for (User *U : V->users()) {
+        Instruction *I = cast<Instruction>(U);
+        if (StoreInst *SI = dyn_cast<StoreInst>(I)) {
+          if (Instruction *resI =
+                  dyn_cast<Instruction>(SI->getValueOperand())) {
+            if (handleMap.count(resI))
+              hasHandleStore = true;
+          }
+        }
+        Insts.emplace_back(I);
+      }
 
-      AllocaInst *NewAI = ResourcePromoter(Insts, SSA, handleMap).run(AI, Insts);
-      localResources.erase(AI);
-      if (NewAI)
-        localResources.insert(NewAI);
+      // No handle here, wait for next round.
+      if (!hasHandleStore) {
+        Insts.clear();
+        continue;
+      }
 
+      bUpdated = true;
+
+      if (AllocaInst *AI = dyn_cast<AllocaInst>(V)) {
+        AllocaInst *NewAI =
+            LocalResourcePromoter(Insts, SSA, handleMap).run(AI, Insts);
+        localResources.erase(AI);
+        if (NewAI)
+          localResources.insert(NewAI);
+      } else {
+        GlobalVariable *GV = cast<GlobalVariable>(V);
+        if (StaticResourcePromoter(Insts, SSA, handleMap).run(GV, Insts)) {
+          localResources.erase(GV);
+        }
+      }
       Insts.clear();
     }
+    if (!bUpdated) {
+      F->getContext().emitError(kResourceMapErrorMsg);
+      break;
+    }
   }
 }
 
@@ -1894,7 +2025,7 @@ void DxilGenerationPass::RemoveLocalDxilResourceAllocas(Function *F) {
   std::unordered_set<AllocaInst *> localResources;
   for (BasicBlock::iterator I = BB.begin(), E = --BB.end(); I != E; ++I)
     if (AllocaInst *AI = dyn_cast<AllocaInst>(I)) { // Is it an alloca?
-      if (IsResourceAlloc(AI)) {
+      if (IsResourceType(AI->getAllocatedType())) {
         localResources.insert(AI);
       }
     }
@@ -1916,9 +2047,17 @@ void DxilGenerationPass::RemoveLocalDxilResourceAllocas(Function *F) {
 void DxilGenerationPass::MapLocalDxilResourceHandles(
     std::unordered_map<Instruction *, Value *> &handleMap) {
   Module &M = *m_pHLModule->GetModule();
+  std::vector<GlobalVariable*> staticResources;
+  for (auto &GV : M.globals()) {
+    if (GV.getLinkage() == GlobalValue::LinkageTypes::InternalLinkage &&
+        IsResourceType(GV.getType()->getElementType())) {
+      staticResources.emplace_back(&GV);
+    }
+  }
+
   for (Function &F : M.functions()) {
     if (!F.isDeclaration())
-      TranslateLocalDxilResourceUses(&F, handleMap);
+      TranslateLocalDxilResourceUses(&F, staticResources, handleMap);
   }
 }
 
@@ -2138,8 +2277,6 @@ void DxilGenerationPass::GenerateDxilResourceHandles(std::unordered_map<Instruct
     HLResource &UAV = m_pHLModule->GetUAV(i);
     TranslateDxilResourceUses(UAV, handleMap);
   }
-
-  AddCreateHandleForPhiNode(handleMap, m_pHLModule->GetOP());
 }
 
 void DxilGenerationPass::GenerateDxilCBufferHandles(std::unordered_map<Instruction *, Value *> &handleMap) {

+ 101 - 34
lib/HLSL/HLOperationLower.cpp

@@ -56,9 +56,14 @@ HLOperationLowerHelper::HLOperationLowerHelper(HLModule &HLM)
 }
 
 struct HLObjectOperationLowerHelper {
+private:
   // For object intrinsics.
   std::unordered_map<llvm::Instruction *, llvm::Value *> &handleMap;
   HLModule &HLM;
+public:
+  HLObjectOperationLowerHelper(std::unordered_map<llvm::Instruction *, llvm::Value *> &handleMap,
+  HLModule &HLM) : HLM(HLM), handleMap(handleMap) {
+  }
   DXIL::ResourceClass GetRC(Type *Ty) {
     return HLM.GetResourceClass(Ty);
   }
@@ -106,6 +111,18 @@ struct HLObjectOperationLowerHelper {
       }
     }
   }
+
+  Value *FindHandle(Instruction *I) {
+    if (handleMap.count(I)) {
+      return handleMap[I];
+    } else {
+      I->getContext().emitError(I, "cannot map resource to handle");
+      return nullptr;
+    }
+  }
+  void EraseHandle(Instruction *I) {
+    handleMap.erase(I);
+  }
 };
 
 using IntrinsicLowerFuncTy = Value *(CallInst *CI, IntrinsicOp IOP,
@@ -1818,8 +1835,11 @@ Value *TranslateGetSamplePosition(CallInst *CI, IntrinsicOp IOP, OP::OpCode op,
   hlsl::OP *hlslOP = &helper.hlslOP;
   Instruction *thisArg =
       cast<Instruction>(CI->getArgOperand(HLOperandIndex::kHandleOpIdx));
-  DXASSERT(pObjHelper->handleMap.count(thisArg), "must find handle");
-  Value *handle = pObjHelper->handleMap[thisArg];
+  Value *handle = pObjHelper->FindHandle(thisArg);
+  if (!handle) {
+    Translated = false;
+    return nullptr;
+  }
 
   IRBuilder<> Builder(CI);
   Value *sampleIdx =
@@ -1847,8 +1867,12 @@ Value *TranslateGetDimensions(CallInst *CI, IntrinsicOp IOP, OP::OpCode op,
 
   Instruction *thisArg =
       cast<Instruction>(CI->getArgOperand(HLOperandIndex::kHandleOpIdx));
-  DXASSERT(pObjHelper->handleMap.count(thisArg), "must find handle");
-  Value *handle = pObjHelper->handleMap[thisArg];
+
+  Value *handle = pObjHelper->FindHandle(thisArg);
+  if (!handle) {
+    Translated = false;
+    return nullptr;
+  }
   DxilResource::Kind RK = pObjHelper->GetRK(thisArg->getType());
 
   IRBuilder<> Builder(CI);
@@ -1947,8 +1971,11 @@ Value *GenerateUpdateCounter(CallInst *CI, IntrinsicOp IOP, OP::OpCode opcode,
   hlsl::OP *hlslOP = &helper.hlslOP;
   Instruction *thisArg =
       cast<Instruction>(CI->getArgOperand(HLOperandIndex::kHandleOpIdx));
-  DXASSERT(pObjHelper->handleMap.count(thisArg), "must find handle");
-  Value *handle = pObjHelper->handleMap[thisArg];
+  Value *handle = pObjHelper->FindHandle(thisArg);
+  if (!handle) {
+    Translated = false;
+    return nullptr;
+  }
   pObjHelper->MarkHasCounter(thisArg->getType(), handle);
 
   bool bInc = IOP == IntrinsicOp::MOP_IncrementCounter;
@@ -2090,13 +2117,13 @@ SampleHelper::SampleHelper(
   Instruction *samplerArg =
       cast<Instruction>(CI->getArgOperand(kSamplerArgIndex));
 
-  std::unordered_map<Instruction *, Value *> &handleMap = pObjHelper->handleMap;
-
   IRBuilder<> Builder(CI);
-  DXASSERT(handleMap.count(texArg), "must find handle");
-  DXASSERT(handleMap.count(samplerArg), "must find handle");
-  texHandle = handleMap[texArg];
-  samplerHandle = handleMap[samplerArg];
+  texHandle = pObjHelper->FindHandle(texArg);
+  samplerHandle = pObjHelper->FindHandle(samplerArg);
+  if (!texHandle || !samplerHandle) {
+    opcode = DXIL::OpCode::NumOpCodes;
+    return;
+  }
 
   DXIL::ResourceKind RK = pObjHelper->GetRK(texArg->getType());
   unsigned coordDimensions = DxilResource::GetNumCoords(RK);
@@ -2163,6 +2190,11 @@ Value *TranslateCalculateLOD(CallInst *CI, IntrinsicOp IOP, OP::OpCode opcode,
                              HLOperationLowerHelper &helper,  HLObjectOperationLowerHelper *pObjHelper, bool &Translated) {
   hlsl::OP *hlslOP = &helper.hlslOP;
   SampleHelper sampleHelper(CI, OP::OpCode::CalculateLOD, pObjHelper);
+  if (sampleHelper.opcode == DXIL::OpCode::NumOpCodes) {
+    Translated = false;
+    return nullptr;
+  }
+
   bool bClamped = IOP == IntrinsicOp::MOP_CalculateLevelOfDetail;
   IRBuilder<> Builder(CI);
   Value *opArg =
@@ -2205,6 +2237,10 @@ Value *TranslateSample(CallInst *CI, IntrinsicOp IOP, OP::OpCode opcode,
   hlsl::OP *hlslOP = &helper.hlslOP;
   SampleHelper sampleHelper(CI, opcode, pObjHelper);
 
+  if (sampleHelper.opcode == DXIL::OpCode::NumOpCodes) {
+    Translated = false;
+    return nullptr;
+  }
   Type *Ty = CI->getType();
 
   Function *F = hlslOP->GetOpFunc(opcode, Ty->getScalarType());
@@ -2425,11 +2461,13 @@ GatherHelper::GatherHelper(
     break;
   }
 
-  std::unordered_map<Instruction *, Value *> &handleMap = pObjHelper->handleMap;
-
   IRBuilder<> Builder(CI);
-  texHandle = handleMap[texArg];
-  samplerHandle = handleMap[samplerArg];
+  texHandle = pObjHelper->FindHandle(texArg);
+  samplerHandle = pObjHelper->FindHandle(samplerArg);
+  if (!texHandle || !samplerHandle) {
+    opcode = DXIL::OpCode::NumOpCodes;
+    return;
+  }
 
   DXIL::ResourceKind RK = pObjHelper->GetRK(texArg->getType());
   unsigned coordSize = DxilResource::GetNumCoords(RK);
@@ -2543,6 +2581,10 @@ Value *TranslateGather(CallInst *CI, IntrinsicOp IOP, OP::OpCode opcode,
 
   GatherHelper gatherHelper(CI, opcode, pObjHelper, ch);
 
+  if (gatherHelper.opcode == DXIL::OpCode::NumOpCodes) {
+    Translated = false;
+    return nullptr;
+  }
   Type *Ty = CI->getType();
 
   Function *F = hlslOP->GetOpFunc(opcode, Ty->getScalarType());
@@ -2858,8 +2900,11 @@ Value *TranslateResourceLoad(CallInst *CI, IntrinsicOp IOP, OP::OpCode opcode,
   hlsl::OP *hlslOP = &helper.hlslOP;
   Instruction *thisArg =
       cast<Instruction>(CI->getArgOperand(HLOperandIndex::kHandleOpIdx));
-  DXASSERT(pObjHelper->handleMap.count(thisArg), "must find handle");
-  Value *handle = pObjHelper->handleMap[thisArg];
+  Value *handle = pObjHelper->FindHandle(thisArg);
+  if (!handle) {
+    Translated = false;
+    return nullptr;
+  }
   IRBuilder<> Builder(CI);
 
   Type *resTy = thisArg->getType();
@@ -3056,8 +3101,11 @@ Value *TranslateResourceStore(CallInst *CI, IntrinsicOp IOP, OP::OpCode opcode,
   hlsl::OP *hlslOP = &helper.hlslOP;
   Instruction *thisArg =
       cast<Instruction>(CI->getArgOperand(HLOperandIndex::kHandleOpIdx));
-  DXASSERT(pObjHelper->handleMap.count(thisArg), "must find handle");
-  Value *handle = pObjHelper->handleMap[thisArg];
+  Value *handle = pObjHelper->FindHandle(thisArg);
+  if (!handle) {
+    Translated = false;
+    return nullptr;
+  }
   IRBuilder<> Builder(CI);
   DXIL::ResourceKind RK = pObjHelper->GetRK(thisArg->getType());
 
@@ -3174,8 +3222,12 @@ Value *TranslateMopAtomicBinaryOperation(CallInst *CI, IntrinsicOp IOP,
   hlsl::OP *hlslOP = &helper.hlslOP;
   Instruction *thisArg =
       cast<Instruction>(CI->getArgOperand(HLOperandIndex::kHandleOpIdx));
-  DXASSERT(pObjHelper->handleMap.count(thisArg), "must find handle");
-  Value *handle = pObjHelper->handleMap[thisArg];
+
+  Value *handle = pObjHelper->FindHandle(thisArg);
+  if (!handle) {
+    Translated = false;
+    return nullptr;
+  }
   IRBuilder<> Builder(CI);
 
   switch (IOP) {
@@ -3275,8 +3327,12 @@ Value *TranslateMopAtomicCmpXChg(CallInst *CI, IntrinsicOp IOP,
   hlsl::OP *hlslOP = &helper.hlslOP;
   Instruction *thisArg =
       cast<Instruction>(CI->getArgOperand(HLOperandIndex::kHandleOpIdx));
-  DXASSERT(pObjHelper->handleMap.count(thisArg), "must find handle");
-  Value *handle = pObjHelper->handleMap[thisArg];
+
+  Value *handle = pObjHelper->FindHandle(thisArg);
+  if (!handle) {
+    Translated = false;
+    return nullptr;
+  }
   IRBuilder<> Builder(CI);
   AtomicHelper atomicHelper(CI, OP::OpCode::AtomicCompareExchange, handle);
   TranslateAtomicCmpXChg(atomicHelper, Builder, hlslOP);
@@ -5711,12 +5767,14 @@ void TranslateDefaultSubscript(CallInst *CI, HLOperationLowerHelper &helper,  HL
 
   Value *ptr = CI->getArgOperand(HLOperandIndex::kSubscriptObjectOpIdx);
   Instruction *ptrInst = dyn_cast<Instruction>(ptr);
-  DXASSERT(pObjHelper->handleMap.count(ptrInst), "must find handle");
 
   hlsl::OP *hlslOP = &helper.hlslOP;
   // Resource ptr.
-  Value *handle = pObjHelper->handleMap[ptrInst];
-
+  Value *handle = pObjHelper->FindHandle(ptrInst);
+  if (!handle) {
+    Translated = false;
+    return;
+  }
   Type *resTy = ptrInst->getType();
   DXIL::ResourceClass RC = pObjHelper->GetRC(resTy);
   DXIL::ResourceKind RK = pObjHelper->GetRK(resTy);
@@ -5910,9 +5968,12 @@ void TranslateHLSubscript(CallInst *CI, HLSubscriptOpcode opcode,
 
   Value *ptr = CI->getArgOperand(HLOperandIndex::kSubscriptObjectOpIdx);
   if (opcode == HLSubscriptOpcode::CBufferSubscript) {
-    DXASSERT(pObjHelper->handleMap.count(CI), "must find handle");
     // Resource ptr.
-    Value *handle = pObjHelper->handleMap[CI];
+    Value *handle = pObjHelper->FindHandle(CI);
+    if (!handle) {
+      Translated = false;
+      return;
+    }
     if (helper.bLegacyCBufferLoad)
       TranslateCBOperationsLegacy(handle, CI, hlslOP, helper.dxilTypeSys,
                                   helper.legacyDataLayout);
@@ -5922,14 +5983,17 @@ void TranslateHLSubscript(CallInst *CI, HLSubscriptOpcode opcode,
                             CI->getModule()->getDataLayout());
     }
     // CI will be removed, so erase it from handleMap.
-    pObjHelper->handleMap.erase(CI);
+    pObjHelper->EraseHandle(CI);
     Translated = true;
     return;
   } else if (opcode == HLSubscriptOpcode::DoubleSubscript) {
     Instruction *ptrInst = dyn_cast<Instruction>(ptr);
-    DXASSERT(pObjHelper->handleMap.count(ptrInst), "must find handle");
     // Resource ptr.
-    Value *handle = pObjHelper->handleMap[ptrInst];
+    Value *handle = pObjHelper->FindHandle(ptrInst);
+    if (!handle) {
+      Translated = false;
+      return;
+    }
     DXIL::ResourceKind RK = pObjHelper->GetRK(ptrInst->getType());
     Value *coord = CI->getArgOperand(HLOperandIndex::kSubscriptIndexOpIdx);
     Value *mipLevel =
@@ -5947,9 +6011,12 @@ void TranslateHLSubscript(CallInst *CI, HLSubscriptOpcode opcode,
     return;
   } else if (Instruction *ptrInst = dyn_cast<Instruction>(ptr)) {
     if (HLModule::IsHLSLObjectType(ptrInst->getType())) {
-      DXASSERT(pObjHelper->handleMap.count(ptrInst), "must find handle");
       // Resource ptr.
-      Value *handle = pObjHelper->handleMap[ptrInst];
+      Value *handle = pObjHelper->FindHandle(ptrInst);
+      if (!handle) {
+        Translated = false;
+        return;
+      }
       DXIL::ResourceKind RK = pObjHelper->GetRK(ptrInst->getType());
       Translated = true;
       Type *ObjTy = ptrInst->getType();

+ 26 - 0
tools/clang/test/CodeGenHLSL/local_resource1.hlsl

@@ -0,0 +1,26 @@
+// RUN: %dxc -E main -Zi -Od -T ps_6_0 %s | FileCheck %s
+
+// CHECK: main
+
+
+float4 Tex2D(Texture2D<float4> t,
+  SamplerState s, float2 c) {
+  return t.Sample(s, c);
+}
+
+float4 test(Texture2D<float4> t,
+SamplerState s, float2 c) {
+  float4 r = Tex2D(t, s, c);
+  r += Tex2D(t, s, c+1);
+  r += Tex2D(t, s, c+2);
+  r += Tex2D(t, s, c+3);
+  r += Tex2D(t, s, c+4);
+  return r;
+}
+
+Texture2D<float4> g_texture;
+SamplerState g_ss;
+
+float4 main(float2 c: T) : SV_Target {
+  return test(g_texture, g_ss, c);
+}

+ 15 - 0
tools/clang/test/CodeGenHLSL/local_resource2.hlsl

@@ -0,0 +1,15 @@
+// RUN: %dxc -E main -Zi -Od -T ps_6_0 %s | FileCheck %s
+
+// CHECK: local resource not guaranteed to map to unique global resource
+
+float4 Tex2D(Texture2D<float4> t,
+  SamplerState s, float2 c) {
+  return t.Sample(s, c);
+}
+
+SamplerState g_ss;
+
+float4 main(float2 c: T) : SV_Target {
+Texture2D<float4> g_texture;
+  return Tex2D(g_texture, g_ss, c);
+}

+ 25 - 0
tools/clang/test/CodeGenHLSL/local_resource3.hlsl

@@ -0,0 +1,25 @@
+// RUN: %dxc -E main -Zi -Od -T ps_6_0 %s | FileCheck %s
+
+// CHECK: local resource not guaranteed to map to unique global resource
+
+float4 Tex2D(Texture2D<float4> t,
+  SamplerState s, float2 c) {
+  return t.Sample(s, c);
+}
+
+float4 test(Texture2D<float4> t,
+SamplerState s, float2 c) {
+  float4 r = Tex2D(t, s, c);
+  r += Tex2D(t, s, c+1);
+  r += Tex2D(t, s, c+2);
+  r += Tex2D(t, s, c+3);
+  r += Tex2D(t, s, c+4);
+  return r;
+}
+
+static Texture2D<float4> g_texture;
+SamplerState g_ss;
+
+float4 main(float2 c: T) : SV_Target {
+  return test(g_texture, g_ss, c);
+}

+ 20 - 0
tools/clang/test/CodeGenHLSL/local_resource4.hlsl

@@ -0,0 +1,20 @@
+// RUN: %dxc -E main -Zi -Od -T ps_6_0 %s | FileCheck %s
+
+// CHECK: main
+
+float4 Tex2D(Texture2D<float4> t,
+  SamplerState s, float2 c) {
+  return t.Sample(s, c);
+}
+
+
+Texture2D<float4> g_texture;
+SamplerState g_ss;
+
+
+static Texture2D<float4> g_texture2;
+
+float4 main(float2 c: T) : SV_Target {
+  g_texture2 = g_texture;
+  return Tex2D(g_texture2, g_ss, c);
+}

+ 18 - 0
tools/clang/test/CodeGenHLSL/local_resource5.hlsl

@@ -0,0 +1,18 @@
+// RUN: %dxc -E main -T ps_6_0 %s | FileCheck %s
+
+// CHECK: local resource not guaranteed to map to unique global resource
+
+float4 Tex2D(Texture2D<float4> t,
+  SamplerState s, float2 c) {
+  return t.Sample(s, c);
+}
+
+SamplerState g_ss;
+Texture2D<float4> g_texture;
+
+float4 main(float2 c: T) : SV_Target {
+Texture2D<float4> texture;
+  if (c.x>0)
+    texture = g_texture;
+  return Tex2D(texture, g_ss, c);
+}

+ 18 - 0
tools/clang/test/CodeGenHLSL/local_resource5_dbg.hlsl

@@ -0,0 +1,18 @@
+// RUN: %dxc -E main -Zi -Od -T ps_6_0 %s | FileCheck %s
+
+// CHECK: local resource not guaranteed to map to unique global resource
+
+float4 Tex2D(Texture2D<float4> t,
+  SamplerState s, float2 c) {
+  return t.Sample(s, c);
+}
+
+SamplerState g_ss;
+Texture2D<float4> g_texture;
+
+float4 main(float2 c: T) : SV_Target {
+Texture2D<float4> texture;
+  if (c.x>0)
+    texture = g_texture;
+  return Tex2D(texture, g_ss, c);
+}

+ 22 - 0
tools/clang/test/CodeGenHLSL/local_resource6.hlsl

@@ -0,0 +1,22 @@
+// RUN: %dxc -E main -T ps_6_0 %s | FileCheck %s
+
+// CHECK: local resource not guaranteed to map to unique global resource
+
+float4 Tex2D(Texture2D<float4> t,
+  SamplerState s, float2 c) {
+  return t.Sample(s, c);
+}
+
+SamplerState g_ss;
+Texture2D<float4> g_texture;
+Texture2D<float4> g_texture2;
+
+float4 main(float2 c: T) : SV_Target {
+Texture2D<float4> l_texture;
+  if (c.x>0)
+    l_texture = g_texture;
+  else
+    l_texture = g_texture2;
+
+  return Tex2D(l_texture, g_ss, c);
+}

+ 22 - 0
tools/clang/test/CodeGenHLSL/local_resource6_dbg.hlsl

@@ -0,0 +1,22 @@
+// RUN: %dxc -E main -Zi -Od -T ps_6_0 %s | FileCheck %s
+
+// CHECK: local resource not guaranteed to map to unique global resource
+
+float4 Tex2D(Texture2D<float4> t,
+  SamplerState s, float2 c) {
+  return t.Sample(s, c);
+}
+
+SamplerState g_ss;
+Texture2D<float4> g_texture;
+Texture2D<float4> g_texture2;
+
+float4 main(float2 c: T) : SV_Target {
+Texture2D<float4> l_texture;
+  if (c.x>0)
+    l_texture = g_texture;
+  else
+    l_texture = g_texture2;
+
+  return Tex2D(l_texture, g_ss, c);
+}

+ 21 - 0
tools/clang/test/CodeGenHLSL/local_resource7.hlsl

@@ -0,0 +1,21 @@
+// RUN: %dxc -E main -T ps_6_0 %s | FileCheck %s
+
+// CHECK: main
+
+float4 Tex2D(Texture2D<float4> t,
+  SamplerState s, float2 c) {
+  return t.Sample(s, c);
+}
+
+SamplerState g_ss;
+Texture2D<float4> g_texture[6];
+
+float4 main(float2 c: T) : SV_Target {
+Texture2D<float4> texture;
+  if (c.x>0)
+    texture = g_texture[0];
+  else
+    texture = g_texture[2];
+
+  return Tex2D(texture, g_ss, c);
+}

+ 21 - 0
tools/clang/test/CodeGenHLSL/local_resource7_dbg.hlsl

@@ -0,0 +1,21 @@
+// RUN: %dxc -E main -Zi -Od -T ps_6_0 %s | FileCheck %s
+
+// CHECK: main
+
+float4 Tex2D(Texture2D<float4> t,
+  SamplerState s, float2 c) {
+  return t.Sample(s, c);
+}
+
+SamplerState g_ss;
+Texture2D<float4> g_texture[6];
+
+float4 main(float2 c: T) : SV_Target {
+Texture2D<float4> texture;
+  if (c.x>0)
+    texture = g_texture[0];
+  else
+    texture = g_texture[2];
+
+  return Tex2D(texture, g_ss, c);
+}

+ 2 - 2
tools/clang/test/CodeGenHLSL/selectObj.hlsl

@@ -1,7 +1,7 @@
 // RUN: %dxc -E main -T cs_6_0 %s | FileCheck %s
 
-// CHECK: buffer load/store only works on Raw/Typed/StructuredBuffer
-// CHECK: buffer load/store only works on Raw/Typed/StructuredBuffer
+// CHECK: local resource not guaranteed to map to unique global resource
+
 
 RWStructuredBuffer<float2x2> oA;
 RWStructuredBuffer<float2x2> oB;

+ 2 - 2
tools/clang/test/CodeGenHLSL/selectObj2.hlsl

@@ -1,7 +1,7 @@
 // RUN: %dxc -E main -T cs_6_0 %s | FileCheck %s
 
-// CHECK: buffer load/store only works on Raw/Typed/StructuredBuffer
-// CHECK: buffer load/store only works on Raw/Typed/StructuredBuffer
+// CHECK: local resource not guaranteed to map to unique global resource
+
 
 RWStructuredBuffer<float2x2> oA;
 RWStructuredBuffer<float2x2> oB;

+ 1 - 2
tools/clang/test/CodeGenHLSL/selectObj3.hlsl

@@ -1,7 +1,6 @@
 // RUN: %dxc -E main -T cs_6_0 %s | FileCheck %s
 
-// CHECK: buffer load/store only works on Raw/Typed/StructuredBuffer
-// CHECK: buffer load/store only works on Raw/Typed/StructuredBuffer
+// CHECK: local resource not guaranteed to map to unique global resource
 
 RWStructuredBuffer<float2x2> oA;
 RWStructuredBuffer<float2x2> oB;

+ 20 - 0
tools/clang/unittests/HLSL/CompilerTest.cpp

@@ -387,6 +387,10 @@ public:
   TEST_METHOD(CodeGenLitInParen)
   TEST_METHOD(CodeGenLiteralShift)
   TEST_METHOD(CodeGenLiveness1)
+  TEST_METHOD(CodeGenLocalRes1)
+  TEST_METHOD(CodeGenLocalRes4)
+  TEST_METHOD(CodeGenLocalRes7)
+  TEST_METHOD(CodeGenLocalRes7Dbg)
   TEST_METHOD(CodeGenLoop1)
   TEST_METHOD(CodeGenLoop2)
   TEST_METHOD(CodeGenLoop3)
@@ -2198,6 +2202,22 @@ TEST_F(CompilerTest, CodeGenLoop1) {
   CodeGenTestCheck(L"..\\CodeGenHLSL\\loop1.hlsl");
 }
 
+TEST_F(CompilerTest, CodeGenLocalRes1) {
+  CodeGenTestCheck(L"..\\CodeGenHLSL\\local_resource1.hlsl");
+}
+
+TEST_F(CompilerTest, CodeGenLocalRes4) {
+  CodeGenTestCheck(L"..\\CodeGenHLSL\\local_resource4.hlsl");
+}
+
+TEST_F(CompilerTest, CodeGenLocalRes7) {
+  CodeGenTestCheck(L"..\\CodeGenHLSL\\local_resource7.hlsl");
+}
+
+TEST_F(CompilerTest, CodeGenLocalRes7Dbg) {
+  CodeGenTestCheck(L"..\\CodeGenHLSL\\local_resource7_dbg.hlsl");
+}
+
 TEST_F(CompilerTest, CodeGenLoop2) {
   CodeGenTestCheck(L"..\\CodeGenHLSL\\loop2.hlsl");
 }

+ 30 - 0
tools/clang/unittests/HLSL/ValidationTest.cpp

@@ -121,6 +121,12 @@ public:
   TEST_METHOD(UDivByZero)
   TEST_METHOD(UnusedMetadata)
   TEST_METHOD(MemoryOutOfBound)
+  TEST_METHOD(LocalRes2)
+  TEST_METHOD(LocalRes3)
+  TEST_METHOD(LocalRes5)
+  TEST_METHOD(LocalRes5Dbg)
+  TEST_METHOD(LocalRes6)
+  TEST_METHOD(LocalRes6Dbg)
   TEST_METHOD(AddrSpaceCast)
   TEST_METHOD(PtrBitCast)
   TEST_METHOD(MinPrecisionBitCast)
@@ -1351,6 +1357,30 @@ TEST_F(ValidationTest, MemoryOutOfBound) {
                           "Access to out-of-bounds memory is disallowed");
 }
 
+TEST_F(ValidationTest, LocalRes2) {
+  TestCheck(L"..\\CodeGenHLSL\\local_resource2.hlsl");
+}
+
+TEST_F(ValidationTest, LocalRes3) {
+  TestCheck(L"..\\CodeGenHLSL\\local_resource3.hlsl");
+}
+
+TEST_F(ValidationTest, LocalRes5) {
+  TestCheck(L"..\\CodeGenHLSL\\local_resource5.hlsl");
+}
+
+TEST_F(ValidationTest, LocalRes5Dbg) {
+  TestCheck(L"..\\CodeGenHLSL\\local_resource5_dbg.hlsl");
+}
+
+TEST_F(ValidationTest, LocalRes6) {
+  TestCheck(L"..\\CodeGenHLSL\\local_resource6.hlsl");
+}
+
+TEST_F(ValidationTest, LocalRes6Dbg) {
+  TestCheck(L"..\\CodeGenHLSL\\local_resource6_dbg.hlsl");
+}
+
 TEST_F(ValidationTest, AddrSpaceCast) {
   RewriteAssemblyCheckMsg(L"..\\CodeGenHLSL\\staticGlobals.hlsl", "ps_6_0",
                           "%([0-9]+) = getelementptr \\[4 x float\\], \\[4 x float\\]\\* %([0-9]+), i32 0, i32 0\n"