[LVI Printer] Rely on the LVI analysis functions rather than the LVI cache

Summary:
LVIPrinter pass was previously relying on the LVICache. We now directly call the
the LVI functions which solves the value if the LVI information is not already
available in the cache. This has 2 benefits over the printing of LVI cache:
1. higher coverage (i.e. catches errors) in LVI code when cache value is
invalidated.
2. relies on the core functions, and not dependent on the LVI cache (which may
be scrapped at some point).
It would still catch any cache invalidation errors, since we first go through
the cache.

Reviewers: reames, dberlin, sanjoy

Reviewed by: reames

Subscribers: llvm-commits

Differential Revision: https://reviews.llvm.org/D32135

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@304819 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Anna Thomas 2017-06-06 19:25:31 +00:00
parent 53a472fd29
commit 46747f1821
3 changed files with 138 additions and 92 deletions

View File

@ -100,8 +100,11 @@ public:
/// Inform the analysis cache that we have erased a block.
void eraseBlock(BasicBlock *BB);
/// Print the \LazyValueInfoCache.
void printCache(Function &F, raw_ostream &OS);
/// Print the \LazyValueInfo Analysis.
/// We pass in the DTree that is required for identifying which basic blocks
/// we can solve/print for, in the LVIPrinter. The DT is optional
/// in LVI, so we need to pass it here as an argument.
void printLVI(Function &F, DominatorTree &DTree, raw_ostream &OS);
// For old PM pass. Delete once LazyValueInfoWrapperPass is gone.
void releaseMemory();

View File

@ -364,7 +364,6 @@ namespace {
/// This is the cache kept by LazyValueInfo which
/// maintains information about queries across the clients' queries.
class LazyValueInfoCache {
friend class LazyValueInfoAnnotatedWriter;
/// This is all of the cached block information for exactly one Value*.
/// The entries are sorted by the BasicBlock* of the
/// entries, allowing us to do a lookup with a binary search.
@ -384,7 +383,6 @@ namespace {
/// don't spend time removing unused blocks from our caches.
DenseSet<PoisoningVH<BasicBlock> > SeenBlocks;
protected:
/// This is all of the cached information for all values,
/// mapped from Value* to key information.
DenseMap<Value *, std::unique_ptr<ValueCacheEntryTy>> ValueCache;
@ -443,7 +441,6 @@ namespace {
return BBI->second;
}
void printCache(Function &F, raw_ostream &OS);
/// clear - Empty the cache.
void clear() {
SeenBlocks.clear();
@ -467,61 +464,6 @@ namespace {
};
}
namespace {
/// An assembly annotator class to print LazyValueCache information in
/// comments.
class LazyValueInfoAnnotatedWriter : public AssemblyAnnotationWriter {
const LazyValueInfoCache* LVICache;
public:
LazyValueInfoAnnotatedWriter(const LazyValueInfoCache *L) : LVICache(L) {}
virtual void emitBasicBlockStartAnnot(const BasicBlock *BB,
formatted_raw_ostream &OS) {
auto ODI = LVICache->OverDefinedCache.find(const_cast<BasicBlock*>(BB));
if (ODI == LVICache->OverDefinedCache.end())
return;
OS << "; OverDefined values for block are: \n";
for (auto *V : ODI->second)
OS << ";" << *V << "\n";
// Find if there are latticevalues defined for arguments of the function.
auto *F = const_cast<Function *>(BB->getParent());
for (auto &Arg : F->args()) {
auto VI = LVICache->ValueCache.find_as(&Arg);
if (VI == LVICache->ValueCache.end())
continue;
auto BBI = VI->second->BlockVals.find(const_cast<BasicBlock *>(BB));
if (BBI != VI->second->BlockVals.end())
OS << "; CachedLatticeValue for: '" << *VI->first << "' is: '"
<< BBI->second << "'\n";
}
}
virtual void emitInstructionAnnot(const Instruction *I,
formatted_raw_ostream &OS) {
auto VI = LVICache->ValueCache.find_as(const_cast<Instruction *>(I));
if (VI == LVICache->ValueCache.end())
return;
OS << "; CachedLatticeValues for: '" << *VI->first << "'\n";
for (auto &BV : VI->second->BlockVals) {
OS << "; at beginning of BasicBlock: '";
BV.first->printAsOperand(OS, false);
OS << "' LatticeVal: '" << BV.second << "' \n";
}
}
};
}
void LazyValueInfoCache::printCache(Function &F, raw_ostream &OS) {
LazyValueInfoAnnotatedWriter Writer(this);
F.print(OS, &Writer);
}
void LazyValueInfoCache::eraseValue(Value *V) {
for (auto I = OverDefinedCache.begin(), E = OverDefinedCache.end(); I != E;) {
// Copy and increment the iterator immediately so we can erase behind
@ -615,6 +557,30 @@ void LazyValueInfoCache::threadEdgeImpl(BasicBlock *OldSucc,
}
}
namespace {
/// An assembly annotator class to print LazyValueCache information in
/// comments.
class LazyValueInfoImpl;
class LazyValueInfoAnnotatedWriter : public AssemblyAnnotationWriter {
LazyValueInfoImpl *LVIImpl;
// While analyzing which blocks we can solve values for, we need the dominator
// information. Since this is an optional parameter in LVI, we require this
// DomTreeAnalysis pass in the printer pass, and pass the dominator
// tree to the LazyValueInfoAnnotatedWriter.
DominatorTree &DT;
public:
LazyValueInfoAnnotatedWriter(LazyValueInfoImpl *L, DominatorTree &DTree)
: LVIImpl(L), DT(DTree) {}
virtual void emitBasicBlockStartAnnot(const BasicBlock *BB,
formatted_raw_ostream &OS);
virtual void emitInstructionAnnot(const Instruction *I,
formatted_raw_ostream &OS);
};
}
namespace {
// The actual implementation of the lazy analysis and update. Note that the
// inheritance from LazyValueInfoCache is intended to be temporary while
@ -693,9 +659,10 @@ namespace {
TheCache.clear();
}
/// Printing the LazyValueInfoCache.
void printCache(Function &F, raw_ostream &OS) {
TheCache.printCache(F, OS);
/// Printing the LazyValueInfo Analysis.
void printLVI(Function &F, DominatorTree &DTree, raw_ostream &OS) {
LazyValueInfoAnnotatedWriter Writer(this, DTree);
F.print(OS, &Writer);
}
/// This is part of the update interface to inform the cache
@ -714,6 +681,7 @@ namespace {
};
} // end anonymous namespace
void LazyValueInfoImpl::solve() {
SmallVector<std::pair<BasicBlock *, Value *>, 8> StartingStack(
BlockValueStack.begin(), BlockValueStack.end());
@ -1890,12 +1858,65 @@ void LazyValueInfo::eraseBlock(BasicBlock *BB) {
}
void LazyValueInfo::printCache(Function &F, raw_ostream &OS) {
void LazyValueInfo::printLVI(Function &F, DominatorTree &DTree, raw_ostream &OS) {
if (PImpl) {
getImpl(PImpl, AC, DL, DT).printCache(F, OS);
getImpl(PImpl, AC, DL, DT).printLVI(F, DTree, OS);
}
}
// Print the LVI for the function arguments at the start of each basic block.
void LazyValueInfoAnnotatedWriter::emitBasicBlockStartAnnot(
const BasicBlock *BB, formatted_raw_ostream &OS) {
// Find if there are latticevalues defined for arguments of the function.
auto *F = BB->getParent();
for (auto &Arg : F->args()) {
LVILatticeVal Result = LVIImpl->getValueInBlock(
const_cast<Argument *>(&Arg), const_cast<BasicBlock *>(BB));
if (Result.isUndefined())
continue;
OS << "; LatticeVal for: '" << Arg << "' is: " << Result << "\n";
}
}
// This function prints the LVI analysis for the instruction I at the beginning
// of various basic blocks. It relies on calculated values that are stored in
// the LazyValueInfoCache, and in the absence of cached values, recalculte the
// LazyValueInfo for `I`, and print that info.
void LazyValueInfoAnnotatedWriter::emitInstructionAnnot(
const Instruction *I, formatted_raw_ostream &OS) {
auto *ParentBB = I->getParent();
SmallPtrSet<const BasicBlock*, 16> BlocksContainingLVI;
// We can generate (solve) LVI values only for blocks that are dominated by
// the I's parent. However, to avoid generating LVI for all dominating blocks,
// that contain redundant/uninteresting information, we print LVI for
// blocks that may use this LVI information (such as immediate successor
// blocks, and blocks that contain uses of `I`).
auto printResult = [&](const BasicBlock *BB) {
if (!BlocksContainingLVI.insert(BB).second)
return;
LVILatticeVal Result = LVIImpl->getValueInBlock(
const_cast<Instruction *>(I), const_cast<BasicBlock *>(BB));
OS << "; LatticeVal for: '" << *I << "' in BB: '";
BB->printAsOperand(OS, false);
OS << "' is: " << Result << "\n";
};
printResult(ParentBB);
// Print the LVI analysis results for the the immediate successor blocks, that
// are dominated by `ParentBB`.
for (auto *BBSucc : successors(ParentBB))
if (DT.dominates(ParentBB, BBSucc))
printResult(BBSucc);
// Print LVI in blocks where `I` is used.
for (auto *U : I->users())
if (auto *UseI = dyn_cast<Instruction>(U))
if (!isa<PHINode>(UseI) || DT.dominates(ParentBB, UseI->getParent()))
printResult(UseI->getParent());
}
namespace {
// Printer class for LazyValueInfo results.
class LazyValueInfoPrinter : public FunctionPass {
@ -1908,12 +1929,16 @@ public:
void getAnalysisUsage(AnalysisUsage &AU) const override {
AU.setPreservesAll();
AU.addRequired<LazyValueInfoWrapperPass>();
AU.addRequired<DominatorTreeWrapperPass>();
}
// Get the mandatory dominator tree analysis and pass this in to the
// LVIPrinter. We cannot rely on the LVI's DT, since it's optional.
bool runOnFunction(Function &F) override {
dbgs() << "LVI for function '" << F.getName() << "':\n";
auto &LVI = getAnalysis<LazyValueInfoWrapperPass>().getLVI();
LVI.printCache(F, dbgs());
auto &DTree = getAnalysis<DominatorTreeWrapperPass>().getDomTree();
LVI.printLVI(F, DTree, dbgs());
return false;
}
};

View File

@ -10,17 +10,23 @@
define i8 @test1(i32 %a, i32 %length) {
; CHECK-LABEL: LVI for function 'test1':
entry:
; CHECK-LABEL: entry:
; CHECK-NEXT: ; LatticeVal for: 'i32 %a' is: overdefined
; CHECK-NEXT: ; LatticeVal for: 'i32 %length' is: overdefined
br label %loop
; CHECK-LABEL: backedge:
; CHECK-NEXT: ; CachedLatticeValues for: ' %iv = phi i32 [ 0, %entry ], [ %iv.next, %backedge ]'
; CHECK-DAG: ; at beginning of BasicBlock: '%backedge' LatticeVal: 'constantrange<0, 400>'
; CHECK-NEXT: %iv = phi i32 [ 0, %entry ], [ %iv.next, %backedge ]
; CHECK-NEXT: ; CachedLatticeValues for: ' %iv.next = add nsw i32 %iv, 1'
; CHECK-NEXT: ; at beginning of BasicBlock: '%backedge' LatticeVal: 'constantrange<1, 401>'
; CHECK-NEXT: %iv.next = add nsw i32 %iv, 1
; CHECK-NEXT: %cont = icmp slt i32 %iv.next, 400
; CHECK-NEXT: br i1 %cont, label %backedge, label %exit
; CHECK-LABEL: backedge:
; CHECK-NEXT: ; LatticeVal for: 'i32 %a' is: overdefined
; CHECK-NEXT: ; LatticeVal for: 'i32 %length' is: overdefined
; CHECK-NEXT: ; LatticeVal for: ' %iv = phi i32 [ 0, %entry ], [ %iv.next, %backedge ]' in BB: '%backedge' is: constantrange<0, 400>
; CHECK-NEXT: ; LatticeVal for: ' %iv = phi i32 [ 0, %entry ], [ %iv.next, %backedge ]' in BB: '%exit' is: constantrange<399, 400>
; CHECK-NEXT: %iv = phi i32 [ 0, %entry ], [ %iv.next, %backedge ]
; CHECK-NEXT: ; LatticeVal for: ' %iv.next = add nsw i32 %iv, 1' in BB: '%backedge' is: constantrange<1, 401>
; CHECK-NEXT: ; LatticeVal for: ' %iv.next = add nsw i32 %iv, 1' in BB: '%exit' is: constantrange<400, 401>
; CHECK-NEXT: %iv.next = add nsw i32 %iv, 1
; CHECK-NEXT: ; LatticeVal for: ' %cont = icmp slt i32 %iv.next, 400' in BB: '%backedge' is: overdefined
; CHECK-NEXT: ; LatticeVal for: ' %cont = icmp slt i32 %iv.next, 400' in BB: '%exit' is: constantrange<0, -1>
; CHECK-NEXT: %cont = icmp slt i32 %iv.next, 400
; CHECK-NOT: loop
loop:
%iv = phi i32 [0, %entry], [%iv.next, %backedge]
@ -36,46 +42,58 @@ exit:
ret i8 0
}
; Here JT does not transform the code, but LVICache is populated during the processing of blocks.
define i8 @test2(i32 %n) {
; CHECK-LABEL: LVI for function 'test2':
; CHECK-LABEL: entry:
; CHECK-LABEL: ; OverDefined values for block are:
; CHECK-NEXT: ;i32 %n
; CHECK-NEXT: ; LatticeVal for: 'i32 %n' is: overdefined
; CHECK-NEXT: br label %loop
entry:
br label %loop
; CHECK-LABEL: loop:
; CHECK-LABEL: ; OverDefined values for block are:
; CHECK-NEXT: ; %iv2 = phi i32 [ %n, %entry ], [ %iv2.next, %backedge ]
; CHECK-NEXT: ; CachedLatticeValues for: ' %iv = phi i32 [ 0, %entry ], [ %iv.next, %backedge ]'
; CHECK-DAG: ; at beginning of BasicBlock: '%loop' LatticeVal: 'constantrange<0, -2147483647>'
; CHECK-DAG: ; at beginning of BasicBlock: '%backedge' LatticeVal: 'constantrange<0, -2147483648>'
; CHECK-NEXT: %iv = phi i32 [ 0, %entry ], [ %iv.next, %backedge ]
; CHECK: %cnd = and i1 %cnd1, %cnd2
; CHECK: br i1 %cnd, label %backedge, label %exit
; CHECK-NEXT: ; LatticeVal for: 'i32 %n' is: overdefined
; CHECK-NEXT: ; LatticeVal for: ' %iv = phi i32 [ 0, %entry ], [ %iv.next, %backedge ]' in BB: '%loop' is: constantrange<0, -2147483647>
; CHECK-DAG: ; LatticeVal for: ' %iv = phi i32 [ 0, %entry ], [ %iv.next, %backedge ]' in BB: '%backedge' is: constantrange<0, -2147483648>
; CHECK-DAG: ; LatticeVal for: ' %iv = phi i32 [ 0, %entry ], [ %iv.next, %backedge ]' in BB: '%exit' is: constantrange<0, -2147483647>
; CHECK-NEXT: %iv = phi i32 [ 0, %entry ], [ %iv.next, %backedge ]
loop:
%iv = phi i32 [0, %entry], [%iv.next, %backedge]
; CHECK-NEXT: ; LatticeVal for: ' %iv2 = phi i32 [ %n, %entry ], [ %iv2.next, %backedge ]' in BB: '%loop' is: overdefined
; CHECK-DAG: ; LatticeVal for: ' %iv2 = phi i32 [ %n, %entry ], [ %iv2.next, %backedge ]' in BB: '%backedge' is: constantrange<1, -2147483648>
; CHECK-DAG: ; LatticeVal for: ' %iv2 = phi i32 [ %n, %entry ], [ %iv2.next, %backedge ]' in BB: '%exit' is: overdefined
; CHECK-NEXT: %iv2 = phi i32 [ %n, %entry ], [ %iv2.next, %backedge ]
%iv2 = phi i32 [%n, %entry], [%iv2.next, %backedge]
; CHECK-NEXT: ; LatticeVal for: ' %cnd1 = icmp sge i32 %iv, 0' in BB: '%loop' is: overdefined
; CHECK-DAG: ; LatticeVal for: ' %cnd1 = icmp sge i32 %iv, 0' in BB: '%backedge' is: overdefined
; CHECK-DAG: ; LatticeVal for: ' %cnd1 = icmp sge i32 %iv, 0' in BB: '%exit' is: overdefined
; CHECK-NEXT: %cnd1 = icmp sge i32 %iv, 0
%cnd1 = icmp sge i32 %iv, 0
%cnd2 = icmp sgt i32 %iv2, 0
; CHECK: %cnd2 = icmp sgt i32 %iv2, 0
; CHECK: ; LatticeVal for: ' %cnd = and i1 %cnd1, %cnd2' in BB: '%loop' is: overdefined
; CHECK-DAG: ; LatticeVal for: ' %cnd = and i1 %cnd1, %cnd2' in BB: '%backedge' is: constantrange<-1, 0>
; CHECK-DAG: ; LatticeVal for: ' %cnd = and i1 %cnd1, %cnd2' in BB: '%exit' is: overdefined
; CHECK-NEXT: %cnd = and i1 %cnd1, %cnd2
%cnd = and i1 %cnd1, %cnd2
br i1 %cnd, label %backedge, label %exit
; CHECK-LABEL: backedge:
; CHECK-NEXT: ; CachedLatticeValues for: ' %iv.next = add nsw i32 %iv, 1'
; CHECK-NEXT: ; at beginning of BasicBlock: '%backedge' LatticeVal: 'constantrange<1, -2147483647>'
; CHECK-NEXT: %iv.next = add nsw i32 %iv, 1
; CHECK-NEXT: %iv2.next = sub nsw i32 %iv2, 1
; CHECK: %cont = and i1 %cont1, %cont2
; CHECK: br i1 %cont, label %loop, label %exit
; CHECK-NEXT: ; LatticeVal for: 'i32 %n' is: overdefined
; CHECK-NEXT: ; LatticeVal for: ' %iv.next = add nsw i32 %iv, 1' in BB: '%backedge' is: constantrange<1, -2147483647>
; CHECK-NEXT: %iv.next = add nsw i32 %iv, 1
backedge:
%iv.next = add nsw i32 %iv, 1
%iv2.next = sub nsw i32 %iv2, 1
; CHECK: ; LatticeVal for: ' %cont1 = icmp slt i32 %iv.next, 400' in BB: '%backedge' is: overdefined
; CHECK-NEXT: %cont1 = icmp slt i32 %iv.next, 400
%cont1 = icmp slt i32 %iv.next, 400
; CHECK-NEXT: ; LatticeVal for: ' %cont2 = icmp sgt i32 %iv2.next, 0' in BB: '%backedge' is: overdefined
; CHECK-NEXT: %cont2 = icmp sgt i32 %iv2.next, 0
%cont2 = icmp sgt i32 %iv2.next, 0
; CHECK-NEXT: ; LatticeVal for: ' %cont = and i1 %cont1, %cont2' in BB: '%backedge' is: overdefined
; CHECK-NEXT: %cont = and i1 %cont1, %cont2
%cont = and i1 %cont1, %cont2
br i1 %cont, label %loop, label %exit