| //===---------- DxilValueCache.cpp - Dxil Constant Value Cache ------------===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // Utility to compute and cache constant values for instructions. |
| // |
| |
| #include "dxc/DXIL/DxilConstants.h" |
| #include "dxc/Support/Global.h" |
| #include "llvm/ADT/Statistic.h" |
| #include "llvm/Analysis/ConstantFolding.h" |
| #include "llvm/Analysis/DxilSimplify.h" |
| #include "llvm/Analysis/InstructionSimplify.h" |
| #include "llvm/IR/CFG.h" |
| #include "llvm/IR/Constants.h" |
| #include "llvm/IR/Dominators.h" |
| #include "llvm/IR/GlobalVariable.h" |
| #include "llvm/IR/Instructions.h" |
| #include "llvm/IR/Module.h" |
| #include "llvm/IR/ModuleSlotTracker.h" |
| #include "llvm/IR/Operator.h" |
| #include "llvm/Pass.h" |
| #include "llvm/Support/Debug.h" |
| #include "llvm/Transforms/Scalar.h" |
| |
| #include "llvm/Analysis/DxilValueCache.h" |
| |
| #include <unordered_map> |
| #include <unordered_set> |
| |
| #define DEBUG_TYPE "dxil-value-cache" |
| |
| using namespace llvm; |
| |
| static bool IsConstantTrue(const Value *V) { |
| if (const ConstantInt *C = dyn_cast<ConstantInt>(V)) |
| return C->getLimitedValue() != 0; |
| return false; |
| } |
| static bool IsConstantFalse(const Value *V) { |
| if (const ConstantInt *C = dyn_cast<ConstantInt>(V)) |
| return C->getLimitedValue() == 0; |
| return false; |
| } |
| |
| static bool IsEntryBlock(const BasicBlock *BB) { |
| return BB == &BB->getParent()->getEntryBlock(); |
| } |
| |
| void DxilValueCache::MarkUnreachable(BasicBlock *BB) { |
| Map.Set(BB, ConstantInt::get(Type::getInt1Ty(BB->getContext()), 0)); |
| } |
| |
| bool DxilValueCache::MayBranchTo(BasicBlock *A, BasicBlock *B) { |
| TerminatorInst *Term = A->getTerminator(); |
| if (BranchInst *Br = dyn_cast<BranchInst>(Term)) { |
| 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; |
| } |
| |
| } else if (SwitchInst *Sw = dyn_cast<SwitchInst>(Term)) { |
| if (ConstantInt *C = |
| dyn_cast<ConstantInt>(TryGetCachedValue(Sw->getCondition()))) { |
| for (auto Case : Sw->cases()) { |
| if (Case.getCaseValue() == C) |
| return Case.getCaseSuccessor() == B; |
| } |
| return Sw->getDefaultDest() == B; |
| } |
| |
| } else if (isa<ReturnInst>(Term) || isa<UnreachableInst>(Term)) { |
| return false; |
| |
| } else { |
| // Should not see: IndirectBrInst, InvokeInst, ResumeInst |
| DXASSERT(false, "otherwise, unexpected terminator instruction."); |
| } |
| |
| return true; |
| } |
| |
| bool DxilValueCache::IsUnreachable_(BasicBlock *BB) { |
| if (Value *V = Map.Get(BB)) |
| if (IsConstantFalse(V)) |
| return true; |
| return false; |
| } |
| |
| Value *DxilValueCache::ProcessAndSimplify_PHI(Instruction *I, |
| DominatorTree *DT) { |
| PHINode *PN = cast<PHINode>(I); |
| BasicBlock *SoleIncoming = nullptr; |
| |
| bool Unreachable = true; |
| Value *Simplified = nullptr; |
| Value *SimplifiedNotDominating = nullptr; |
| |
| for (unsigned i = 0; i < PN->getNumIncomingValues(); i++) { |
| BasicBlock *PredBB = PN->getIncomingBlock(i); |
| if (IsUnreachable_(PredBB)) |
| continue; |
| |
| Unreachable = false; |
| |
| if (MayBranchTo(PredBB, PN->getParent())) { |
| if (SoleIncoming) { |
| SoleIncoming = nullptr; |
| break; |
| } |
| SoleIncoming = PredBB; |
| } |
| } |
| |
| if (Unreachable) { |
| return UndefValue::get(I->getType()); |
| } |
| |
| if (SoleIncoming) { |
| Value *V = TryGetCachedValue(PN->getIncomingValueForBlock(SoleIncoming)); |
| if (isa<Constant>(V)) |
| Simplified = V; |
| else if (Instruction *I = dyn_cast<Instruction>(V)) { |
| // If this is an instruction, we have to make sure it |
| // dominates this PHI. |
| // There are several conditions that qualify: |
| // 1. There's only one predecessor |
| // 2. If the instruction is in the entry block, then it must dominate |
| // 3. If we are provided with a Dominator tree, and it decides that |
| // it dominates. |
| if (PN->getNumIncomingValues() == 1 || IsEntryBlock(I->getParent()) || |
| (DT && DT->dominates(I, PN))) { |
| Simplified = I; |
| } else { |
| SimplifiedNotDominating = I; |
| } |
| } |
| } |
| |
| // If we have a value but it's not dominating our PHI, see if it has a cached |
| // value that were computed previously. |
| if (!Simplified) { |
| if (SimplifiedNotDominating) |
| if (Value *CachedV = Map.Get(SimplifiedNotDominating)) |
| Simplified = CachedV; |
| } |
| |
| // If we coulnd't deduce it, run the LLVM stock simplification to see |
| // if we could do anything. |
| if (!Simplified) |
| Simplified = llvm::SimplifyInstruction(I, I->getModule()->getDataLayout()); |
| |
| // One last step, to check if we have anything cached for whatever we |
| // simplified to. |
| if (Simplified) |
| Simplified = TryGetCachedValue(Simplified); |
| |
| return Simplified; |
| } |
| |
| Value *DxilValueCache::ProcessAndSimplify_Switch(Instruction *I, |
| DominatorTree *DT) { |
| SwitchInst *Sw = cast<SwitchInst>(I); |
| BasicBlock *BB = Sw->getParent(); |
| |
| Value *Cond = TryGetCachedValue(Sw->getCondition()); |
| if (IsUnreachable_(BB)) { |
| for (unsigned i = 0; i < Sw->getNumSuccessors(); i++) { |
| BasicBlock *Succ = Sw->getSuccessor(i); |
| if (Succ->getUniquePredecessor()) |
| MarkUnreachable(Succ); |
| } |
| } else if (isa<Constant>(Cond)) { |
| BasicBlock *ConstDest = nullptr; |
| for (auto Case : Sw->cases()) { |
| BasicBlock *Succ = Case.getCaseSuccessor(); |
| if (Case.getCaseValue() == Cond) { |
| ConstDest = Succ; |
| break; |
| } |
| } |
| if (!ConstDest) { |
| ConstDest = Sw->getDefaultDest(); |
| } |
| DXASSERT_NOMSG(ConstDest); |
| if (ConstDest) { |
| for (unsigned i = 0; i < Sw->getNumSuccessors(); i++) { |
| BasicBlock *Succ = Sw->getSuccessor(i); |
| if (Succ != ConstDest && Succ->getUniquePredecessor()) { |
| MarkUnreachable(Succ); |
| } |
| } |
| } |
| } |
| |
| return nullptr; |
| } |
| |
| Value *DxilValueCache::ProcessAndSimplify_Br(Instruction *I, |
| DominatorTree *DT) { |
| |
| // The *only* reason we're paying special attention to the |
| // branch inst, is to mark certain Basic Blocks as always |
| // reachable or unreachable. |
| |
| BranchInst *Br = cast<BranchInst>(I); |
| BasicBlock *BB = Br->getParent(); |
| if (Br->isConditional()) { |
| |
| BasicBlock *TrueSucc = Br->getSuccessor(0); |
| BasicBlock *FalseSucc = Br->getSuccessor(1); |
| |
| Value *Cond = TryGetCachedValue(Br->getCondition()); |
| |
| if (IsUnreachable_(BB)) { |
| if (FalseSucc->getSinglePredecessor()) |
| MarkUnreachable(FalseSucc); |
| if (TrueSucc->getSinglePredecessor()) |
| MarkUnreachable(TrueSucc); |
| } else if (IsConstantTrue(Cond)) { |
| if (FalseSucc->getSinglePredecessor()) |
| MarkUnreachable(FalseSucc); |
| } else if (IsConstantFalse(Cond)) { |
| if (TrueSucc->getSinglePredecessor()) |
| MarkUnreachable(TrueSucc); |
| } |
| } else { |
| BasicBlock *Succ = Br->getSuccessor(0); |
| if (Succ->getSinglePredecessor() && IsUnreachable_(BB)) |
| MarkUnreachable(Succ); |
| } |
| |
| return nullptr; |
| } |
| |
| Value *DxilValueCache::ProcessAndSimplify_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) { |
| |
| if (ShouldSkipCallback && ShouldSkipCallback(I)) |
| return nullptr; |
| |
| const DataLayout &DL = I->getModule()->getDataLayout(); |
| |
| Value *Simplified = nullptr; |
| if (Instruction::Br == I->getOpcode()) { |
| Simplified = ProcessAndSimplify_Br(I, DT); |
| } |
| if (Instruction::Switch == I->getOpcode()) { |
| Simplified = ProcessAndSimplify_Switch(I, DT); |
| } else if (Instruction::PHI == I->getOpcode()) { |
| Simplified = ProcessAndSimplify_PHI(I, DT); |
| } else if (Instruction::Load == I->getOpcode()) { |
| Simplified = ProcessAndSimplify_Load(I, DT); |
| } else if (Instruction::GetElementPtr == I->getOpcode()) { |
| SmallVector<Value *, 4> Ops; |
| for (unsigned i = 0; i < I->getNumOperands(); i++) |
| Ops.push_back(TryGetCachedValue(I->getOperand(i))); |
| Simplified = llvm::SimplifyGEPInst(Ops, DL, nullptr, DT); |
| } else if (Instruction::Call == I->getOpcode()) { |
| Module *M = I->getModule(); |
| CallInst *CI = cast<CallInst>(I); |
| Value *Callee = CI->getCalledValue(); |
| Function *CalledFunction = dyn_cast<Function>(Callee); |
| |
| if (CalledFunction && |
| CalledFunction->getName() == hlsl::DXIL::kDxBreakFuncName) { |
| llvm::Type *i1Ty = llvm::Type::getInt1Ty(M->getContext()); |
| Simplified = llvm::ConstantInt::get(i1Ty, 1); |
| } else { |
| SmallVector<Value *, 16> Args; |
| for (unsigned i = 0; i < CI->getNumArgOperands(); i++) { |
| Args.push_back(TryGetCachedValue(CI->getArgOperand(i))); |
| } |
| |
| if (CalledFunction && hlsl::CanSimplify(CalledFunction)) { |
| Simplified = hlsl::SimplifyDxilCall(CalledFunction, Args, CI, |
| /* MayInsert */ false); |
| } else { |
| Simplified = llvm::SimplifyCall(Callee, Args, DL, nullptr, DT); |
| } |
| } |
| } |
| // The rest of the checks use LLVM stock simplifications |
| else if (I->isBinaryOp()) { |
| if (FPMathOperator *FPOp = dyn_cast<FPMathOperator>(I)) { |
| Simplified = llvm::SimplifyFPBinOp( |
| I->getOpcode(), TryGetCachedValue(I->getOperand(0)), |
| TryGetCachedValue(I->getOperand(1)), FPOp->getFastMathFlags(), DL); |
| } else { |
| Simplified = llvm::SimplifyBinOp(I->getOpcode(), |
| TryGetCachedValue(I->getOperand(0)), |
| TryGetCachedValue(I->getOperand(1)), 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)) { |
| if (FPMathOperator *FPOp = dyn_cast<FPMathOperator>(I)) { |
| Simplified = llvm::SimplifyFCmpInst( |
| Cmp->getPredicate(), TryGetCachedValue(I->getOperand(0)), |
| TryGetCachedValue(I->getOperand(1)), FPOp->getFastMathFlags(), DL); |
| } else { |
| Simplified = llvm::SimplifyCmpInst( |
| Cmp->getPredicate(), TryGetCachedValue(I->getOperand(0)), |
| TryGetCachedValue(I->getOperand(1)), DL); |
| } |
| } else if (SelectInst *Select = dyn_cast<SelectInst>(I)) { |
| Simplified = llvm::SimplifySelectInst( |
| TryGetCachedValue(Select->getCondition()), |
| TryGetCachedValue(Select->getTrueValue()), |
| TryGetCachedValue(Select->getFalseValue()), DL); |
| } else if (ExtractElementInst *IE = dyn_cast<ExtractElementInst>(I)) { |
| Simplified = llvm::SimplifyExtractElementInst( |
| TryGetCachedValue(IE->getVectorOperand()), |
| TryGetCachedValue(IE->getIndexOperand()), DL, nullptr, DT); |
| } else if (CastInst *Cast = dyn_cast<CastInst>(I)) { |
| Simplified = llvm::SimplifyCastInst(Cast->getOpcode(), |
| TryGetCachedValue(Cast->getOperand(0)), |
| Cast->getType(), DL); |
| } |
| |
| if (Simplified && isa<Constant>(Simplified)) |
| Map.Set(I, Simplified); |
| |
| return Simplified; |
| } |
| |
| bool DxilValueCache::WeakValueMap::Seen(Value *V) { |
| auto FindIt = Map.find(V); |
| if (FindIt == Map.end()) |
| return false; |
| |
| auto &Entry = FindIt->second; |
| if (Entry.IsStale()) |
| return false; |
| return Entry.Value; |
| } |
| |
| Value *DxilValueCache::WeakValueMap::Get(Value *V) { |
| auto FindIt = Map.find(V); |
| if (FindIt == Map.end()) |
| return nullptr; |
| |
| auto &Entry = FindIt->second; |
| if (Entry.IsStale()) |
| return nullptr; |
| |
| Value *Result = Entry.Value; |
| if (Result == GetSentinel(V->getContext())) |
| return nullptr; |
| |
| return Result; |
| } |
| |
| void DxilValueCache::WeakValueMap::SetSentinel(Value *Key) { |
| Map[Key].Set(Key, GetSentinel(Key->getContext())); |
| } |
| |
| Value *DxilValueCache::WeakValueMap::GetSentinel(LLVMContext &Ctx) { |
| if (!Sentinel) { |
| Sentinel.reset(PHINode::Create(Type::getInt1Ty(Ctx), 0)); |
| } |
| return Sentinel.get(); |
| } |
| |
| void DxilValueCache::WeakValueMap::ResetAll() { Map.clear(); } |
| |
| void DxilValueCache::WeakValueMap::ResetUnknowns() { |
| if (!Sentinel) |
| return; |
| |
| for (auto it = Map.begin(); it != Map.end();) { |
| auto nextIt = std::next(it); |
| if (it->second.Value == Sentinel.get()) |
| Map.erase(it); |
| it = nextIt; |
| } |
| } |
| |
| LLVM_DUMP_METHOD |
| void DxilValueCache::WeakValueMap::dump() const { |
| std::unordered_map<const Module *, std::unique_ptr<ModuleSlotTracker>> MSTs; |
| for (auto It = Map.begin(), E = Map.end(); It != E; It++) { |
| const Value *Key = It->first; |
| |
| if (It->second.IsStale()) |
| continue; |
| |
| if (!Key) |
| continue; |
| |
| ModuleSlotTracker *MST = nullptr; |
| { |
| const Module *M = nullptr; |
| if (auto I = dyn_cast<Instruction>(Key)) |
| M = I->getModule(); |
| else if (auto BB = dyn_cast<BasicBlock>(Key)) |
| M = BB->getModule(); |
| else { |
| errs() << *Key; |
| llvm_unreachable("How can a key be neither an instruction or BB?"); |
| } |
| std::unique_ptr<ModuleSlotTracker> &optMst = MSTs[M]; |
| if (!optMst) { |
| optMst = llvm::make_unique<ModuleSlotTracker>(M); |
| } |
| MST = optMst.get(); |
| } |
| |
| const Value *V = It->second.Value; |
| bool IsSentinel = Sentinel && V == Sentinel.get(); |
| |
| if (const BasicBlock *BB = dyn_cast<BasicBlock>(Key)) { |
| dbgs() << "[BB]"; |
| BB->printAsOperand(dbgs(), false, *MST); |
| dbgs() << " -> "; |
| if (IsSentinel) |
| dbgs() << "NO_VALUE"; |
| else { |
| if (IsConstantTrue(V)) |
| dbgs() << "Always Reachable!"; |
| else if (IsConstantFalse(V)) |
| dbgs() << "Never Reachable!"; |
| } |
| } else { |
| dbgs() << *Key << " -> "; |
| if (IsSentinel) |
| dbgs() << "NO_VALUE"; |
| else |
| dbgs() << *V; |
| } |
| dbgs() << "\n"; |
| } |
| } |
| |
| void DxilValueCache::WeakValueMap::Set(Value *Key, Value *V) { |
| Map[Key].Set(Key, V); |
| } |
| |
| // If there's a cached value, return it. Otherwise, return |
| // the value itself. |
| Value *DxilValueCache::TryGetCachedValue(Value *V) { |
| if (Value *Simplified = Map.Get(V)) |
| return Simplified; |
| return V; |
| } |
| |
| DxilValueCache::DxilValueCache() : ImmutablePass(ID) { |
| initializeDxilValueCachePass(*PassRegistry::getPassRegistry()); |
| } |
| |
| StringRef DxilValueCache::getPassName() const { return "Dxil Value Cache"; } |
| |
| Value *DxilValueCache::GetValue(Value *V, DominatorTree *DT) { |
| if (dyn_cast<Constant>(V)) |
| return V; |
| if (Value *NewV = Map.Get(V)) |
| return NewV; |
| |
| return ProcessValue(V, DT); |
| } |
| |
| Constant *DxilValueCache::GetConstValue(Value *V, DominatorTree *DT) { |
| if (Value *NewV = GetValue(V)) |
| return dyn_cast<Constant>(NewV); |
| return nullptr; |
| } |
| |
| ConstantInt *DxilValueCache::GetConstInt(Value *V, DominatorTree *DT) { |
| if (Value *NewV = GetValue(V)) |
| return dyn_cast<ConstantInt>(NewV); |
| return nullptr; |
| } |
| |
| bool DxilValueCache::IsUnreachable(BasicBlock *BB, DominatorTree *DT) { |
| ProcessValue(BB, DT); |
| return IsUnreachable_(BB); |
| } |
| |
| LLVM_DUMP_METHOD |
| void DxilValueCache::dump() const { Map.dump(); } |
| |
| void DxilValueCache::getAnalysisUsage(AnalysisUsage &AU) const { |
| AU.setPreservesAll(); |
| } |
| |
| Value *DxilValueCache::ProcessValue(Value *NewV, DominatorTree *DT) { |
| if (NewV->getType()->isVoidTy()) |
| return nullptr; |
| |
| Value *Result = nullptr; |
| |
| SmallVector<Value *, 16> WorkList; |
| |
| // Although we accept all values for convenience, we only process |
| // Instructions. |
| if (Instruction *I = dyn_cast<Instruction>(NewV)) { |
| WorkList.push_back(I); |
| } else if (BasicBlock *BB = dyn_cast<BasicBlock>(NewV)) { |
| WorkList.push_back(BB->getTerminator()); |
| WorkList.push_back(BB); |
| } else { |
| return nullptr; |
| } |
| |
| // Unconditionally process this one instruction, whether we've seen |
| // it or not. The simplification might be able to do something to |
| // simplify it even when we don't have its value cached. |
| |
| // This is a basic DFS setup. |
| while (WorkList.size()) { |
| Value *V = WorkList.back(); |
| |
| // If we haven't seen this value, go in and push things it depends on |
| // into the worklist. |
| if (!Map.Seen(V)) { |
| Map.SetSentinel(V); |
| if (Instruction *I = dyn_cast<Instruction>(V)) { |
| |
| for (Use &U : I->operands()) { |
| Instruction *UseI = dyn_cast<Instruction>(U.get()); |
| if (!UseI) |
| continue; |
| if (!Map.Seen(UseI)) |
| WorkList.push_back(UseI); |
| } |
| |
| if (PHINode *PN = dyn_cast<PHINode>(I)) { |
| for (unsigned i = 0; i < PN->getNumIncomingValues(); i++) { |
| BasicBlock *BB = PN->getIncomingBlock(i); |
| TerminatorInst *Term = BB->getTerminator(); |
| if (!Map.Seen(Term)) |
| WorkList.push_back(Term); |
| if (!Map.Seen(BB)) |
| WorkList.push_back(BB); |
| } |
| } |
| } else if (BasicBlock *BB = dyn_cast<BasicBlock>(V)) { |
| for (pred_iterator PI = pred_begin(BB), E = pred_end(BB); PI != E; |
| PI++) { |
| BasicBlock *PredBB = *PI; |
| TerminatorInst *Term = PredBB->getTerminator(); |
| if (!Map.Seen(Term)) |
| WorkList.push_back(Term); |
| if (!Map.Seen(PredBB)) |
| WorkList.push_back(PredBB); |
| } |
| } |
| } |
| // If we've seen this values, all its dependencies must have been processed |
| // as well. |
| else { |
| WorkList.pop_back(); |
| if (Instruction *I = dyn_cast<Instruction>(V)) { |
| Value *SimplifiedValue = SimplifyAndCacheResult(I, DT); |
| // Set the result if this is the input inst. |
| // SimplifyInst may not have cached the value |
| // so we return it directly. |
| if (I == NewV) |
| Result = SimplifiedValue; |
| } else if (BasicBlock *BB = dyn_cast<BasicBlock>(V)) { |
| // Deduce the basic block's reachability based on |
| // other analysis. |
| if (!IsEntryBlock(BB)) { |
| bool AllNeverReachable = true; |
| for (pred_iterator PI = pred_begin(BB), E = pred_end(BB); PI != E; |
| PI++) { |
| if (!IsUnreachable_(*PI)) { |
| AllNeverReachable = false; |
| break; |
| } |
| } |
| if (AllNeverReachable) |
| MarkUnreachable(BB); |
| } |
| } |
| } |
| } |
| |
| return Result; |
| } |
| |
| char DxilValueCache::ID; |
| |
| Pass *llvm::createDxilValueCachePass() { return new DxilValueCache(); } |
| |
| INITIALIZE_PASS(DxilValueCache, DEBUG_TYPE, "Dxil Value Cache", false, false) |