[MSSA] Fix a caching bug.

This fixes a bug where we'd sometimes cache overly-conservative results
with our walker. This bug was made more obvious by r277480, which makes
our cache far more spotty than it was. Test case is llvm-unit, because
we're likely going to use CachingWalker only for def optimization in the
future.

The bug stems from that there was a place where the walker assumed that
`DefNode.Last` was a valid target to cache to when failing to optimize
phis. This is sometimes incorrect if we have a cache hit. The fix is to
use the thing we *can* assume is a valid target to cache to. :)

llvm-svn: 277559
This commit is contained in:
George Burgess IV 2016-08-03 01:22:19 +00:00
parent 9f0ef01197
commit 14633b5cd3
2 changed files with 82 additions and 8 deletions

View File

@ -594,7 +594,7 @@ class ClobberWalker {
///
/// If this returns None, NewPaused is a vector of searches that terminated
/// at StopWhere. Otherwise, NewPaused is left in an unspecified state.
Optional<ListIndex>
Optional<TerminatedPath>
getBlockingAccess(MemoryAccess *StopWhere,
SmallVectorImpl<ListIndex> &PausedSearches,
SmallVectorImpl<ListIndex> &NewPaused,
@ -633,11 +633,12 @@ class ClobberWalker {
assert(Res.Result != StopWhere || Res.FromCache);
// If this wasn't a cache hit, we hit a clobber when walking. That's a
// failure.
TerminatedPath Term{Res.Result, PathIndex};
if (!Res.FromCache || !MSSA.dominates(Res.Result, StopWhere))
return PathIndex;
return Term;
// Otherwise, it's a valid thing to potentially optimize to.
Terminated.push_back({Res.Result, PathIndex});
Terminated.push_back(Term);
continue;
}
@ -769,22 +770,21 @@ class ClobberWalker {
// liveOnEntry, and we'll happily wait for that to disappear (read: never)
// For the moment, this is fine, since we do basically nothing with
// blocker info.
if (Optional<ListIndex> Blocker = getBlockingAccess(
if (Optional<TerminatedPath> Blocker = getBlockingAccess(
Target, PausedSearches, NewPaused, TerminatedPaths)) {
MemoryAccess *BlockingAccess = Paths[*Blocker].Last;
// Cache our work on the blocking node, since we know that's correct.
cacheDefPath(Paths[*Blocker], BlockingAccess);
cacheDefPath(Paths[Blocker->LastNode], Blocker->Clobber);
// Find the node we started at. We can't search based on N->Last, since
// we may have gone around a loop with a different MemoryLocation.
auto Iter = find_if(def_path(*Blocker), [&](const DefPath &N) {
auto Iter = find_if(def_path(Blocker->LastNode), [&](const DefPath &N) {
return defPathIndex(N) < PriorPathsSize;
});
assert(Iter != def_path_iterator());
DefPath &CurNode = *Iter;
assert(CurNode.Last == Current);
CurNode.Blocker = BlockingAccess;
CurNode.Blocker = Blocker->Clobber;
// Two things:
// A. We can't reliably cache all of NewPaused back. Consider a case

View File

@ -344,3 +344,77 @@ TEST_F(MemorySSATest, TestStoreDoubleQuery) {
EXPECT_EQ(Clobber, StoreAccess);
EXPECT_TRUE(MSSA.isLiveOnEntryDef(LiveOnEntry));
}
// Bug: During phi optimization, the walker wouldn't cache to the proper result
// in the farthest-walked BB.
//
// Specifically, it would assume that whatever we walked to was a clobber.
// "Whatever we walked to" isn't a clobber if we hit a cache entry.
//
// ...So, we need a test case that looks like:
// A
// / \
// B |
// \ /
// C
//
// Where, when we try to optimize a thing in 'C', a blocker is found in 'B'.
// The walk must determine that the blocker exists by using cache entries *while
// walking* 'B'.
TEST_F(MemorySSATest, PartialWalkerCacheWithPhis) {
F = Function::Create(FunctionType::get(B.getVoidTy(), {}, false),
GlobalValue::ExternalLinkage, "F", &M);
B.SetInsertPoint(BasicBlock::Create(C, "A", F));
Type *Int8 = Type::getInt8Ty(C);
Constant *One = ConstantInt::get(Int8, 1);
Constant *Zero = ConstantInt::get(Int8, 0);
Value *AllocA = B.CreateAlloca(Int8, One, "a");
Value *AllocB = B.CreateAlloca(Int8, One, "b");
BasicBlock *IfThen = BasicBlock::Create(C, "B", F);
BasicBlock *IfEnd = BasicBlock::Create(C, "C", F);
B.CreateCondBr(UndefValue::get(Type::getInt1Ty(C)), IfThen, IfEnd);
B.SetInsertPoint(IfThen);
Instruction *FirstStore = B.CreateStore(Zero, AllocA);
B.CreateStore(Zero, AllocB);
Instruction *ALoad0 = B.CreateLoad(AllocA, "");
Instruction *BStore = B.CreateStore(Zero, AllocB);
// Due to use optimization/etc. we make a store to A, which is removed after
// we build MSSA. This helps keep the test case simple-ish.
Instruction *KillStore = B.CreateStore(Zero, AllocA);
Instruction *ALoad = B.CreateLoad(AllocA, "");
B.CreateBr(IfEnd);
B.SetInsertPoint(IfEnd);
Instruction *BelowPhi = B.CreateStore(Zero, AllocA);
setupAnalyses();
MemorySSA &MSSA = Analyses->MSSA;
MemorySSAWalker *Walker = Analyses->Walker;
// Kill `KillStore`; it exists solely so that the load after it won't be
// optimized to FirstStore.
MSSA.removeMemoryAccess(MSSA.getMemoryAccess(KillStore));
KillStore->eraseFromParent();
auto *ALoadMA = cast<MemoryUse>(MSSA.getMemoryAccess(ALoad));
EXPECT_EQ(ALoadMA->getDefiningAccess(), MSSA.getMemoryAccess(BStore));
// Populate the cache for the store to AllocB directly after FirstStore. It
// should point to something in block B (so something in D can't be optimized
// to it).
MemoryAccess *Load0Clobber = Walker->getClobberingMemoryAccess(ALoad0);
EXPECT_EQ(MSSA.getMemoryAccess(FirstStore), Load0Clobber);
// If the bug exists, this will introduce a bad cache entry for %a on BStore.
// It will point to the store to %b after FirstStore. This only happens during
// phi optimization.
MemoryAccess *BottomClobber = Walker->getClobberingMemoryAccess(BelowPhi);
MemoryAccess *Phi = MSSA.getMemoryAccess(IfEnd);
EXPECT_EQ(BottomClobber, Phi);
// This query will first check the cache for {%a, BStore}. It should point to
// FirstStore, not to the store after FirstStore.
MemoryAccess *UseClobber = Walker->getClobberingMemoryAccess(ALoad);
EXPECT_EQ(UseClobber, MSSA.getMemoryAccess(FirstStore));
}