Skip to content

Commit f6f6f63

Browse files
committed
[BasicAA] Fix BatchAA results for phi-phi assumptions
Change the way NoAlias assumptions in BasicAA are handled. Instead of handling this inside the phi-phi code, always initially insert a NoAlias result into the map and keep track whether it is used. If it is used, then we require that we also get back NoAlias from the recursive queries. Otherwise, the entry is changed to MayAlias. Additionally, keep track of all location pairs we inserted that may still be based on assumptions higher up. If it turns out one of those assumptions is incorrect, we flush them from the cache. The compile-time impact for the new implementation is significantly higher than the previous iteration of this patch: https://llvm-compile-time-tracker.com/compare.php?from=c0bb9859de6991cc233e2dedb978dd118da8c382&to=c07112373279143e37568b5bcd293daf81a35973&stat=instructions However, it should avoid the exponential runtime cases we run into if we don't cache assumption-based results entirely. This also produces better results in some cases, because NoAlias assumptions can now start at any root, rather than just phi-phi pairs. This is not just relevant for analysis quality, but also for BatchAA consistency: Otherwise, results would once again depend on query order, though at least they wouldn't be wrong. This ended up both more complicated and more expensive than I hoped, but I wasn't able to come up with another solution that satisfies all the constraints. Differential Revision: https://reviews.llvm.org/D91936
1 parent 0e874fc commit f6f6f63

File tree

5 files changed

+101
-65
lines changed

5 files changed

+101
-65
lines changed

llvm/include/llvm/Analysis/AliasAnalysis.h

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -346,19 +346,21 @@ createModRefInfo(const FunctionModRefBehavior FMRB) {
346346
class AAQueryInfo {
347347
public:
348348
using LocPair = std::pair<MemoryLocation, MemoryLocation>;
349-
using AliasCacheT = SmallDenseMap<LocPair, AliasResult, 8>;
349+
struct CacheEntry {
350+
AliasResult Result;
351+
/// Number of times a NoAlias assumption has been used.
352+
/// 0 for assumptions that have not been used, -1 for definitive results.
353+
int NumAssumptionUses;
354+
/// Whether this is a definitive (non-assumption) result.
355+
bool isDefinitive() const { return NumAssumptionUses < 0; }
356+
};
357+
using AliasCacheT = SmallDenseMap<LocPair, CacheEntry, 8>;
350358
AliasCacheT AliasCache;
351359

352360
using IsCapturedCacheT = SmallDenseMap<const Value *, bool, 8>;
353361
IsCapturedCacheT IsCapturedCache;
354362

355363
AAQueryInfo() : AliasCache(), IsCapturedCache() {}
356-
357-
AliasResult updateResult(const LocPair &Locs, AliasResult Result) {
358-
auto It = AliasCache.find(Locs);
359-
assert(It != AliasCache.end() && "Entry must have existed");
360-
return It->second = Result;
361-
}
362364
};
363365

364366
class BatchAAResults;

llvm/include/llvm/Analysis/BasicAliasAnalysis.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,14 @@ class BasicAAResult : public AAResultBase<BasicAAResult> {
190190
/// Tracks instructions visited by pointsToConstantMemory.
191191
SmallPtrSet<const Value *, 16> Visited;
192192

193+
/// How many active NoAlias assumption uses there are.
194+
int NumAssumptionUses = 0;
195+
196+
/// Location pairs for which an assumption based result is currently stored.
197+
/// Used to remove all potentially incorrect results from the cache if an
198+
/// assumption is disproven.
199+
SmallVector<AAQueryInfo::LocPair, 4> AssumptionBasedResults;
200+
193201
static const Value *
194202
GetLinearExpression(const Value *V, APInt &Scale, APInt &Offset,
195203
unsigned &ZExtBits, unsigned &SExtBits,
@@ -244,6 +252,12 @@ class BasicAAResult : public AAResultBase<BasicAAResult> {
244252
LocationSize V2Size, const AAMDNodes &V2AATag,
245253
AAQueryInfo &AAQI, const Value *O1 = nullptr,
246254
const Value *O2 = nullptr);
255+
256+
AliasResult aliasCheckRecursive(const Value *V1, LocationSize V1Size,
257+
const AAMDNodes &V1AATag, const Value *V2,
258+
LocationSize V2Size, const AAMDNodes &V2AATag,
259+
AAQueryInfo &AAQI, const Value *O1,
260+
const Value *O2);
247261
};
248262

249263
/// Analysis pass providing a never-invalidated alias analysis result.

llvm/lib/Analysis/BasicAliasAnalysis.cpp

Lines changed: 73 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -806,8 +806,12 @@ AliasResult BasicAAResult::alias(const MemoryLocation &LocA,
806806
if (Locs.first.Ptr > Locs.second.Ptr)
807807
std::swap(Locs.first, Locs.second);
808808
auto CacheIt = AAQI.AliasCache.find(Locs);
809-
if (CacheIt != AAQI.AliasCache.end())
810-
return CacheIt->second;
809+
if (CacheIt != AAQI.AliasCache.end()) {
810+
// This code exists to skip a second BasicAA call while recursing into
811+
// BestAA. Don't make use of assumptions here.
812+
const auto &Entry = CacheIt->second;
813+
return Entry.isDefinitive() ? Entry.Result : MayAlias;
814+
}
811815

812816
AliasResult Alias = aliasCheck(LocA.Ptr, LocA.Size, LocA.AATags, LocB.Ptr,
813817
LocB.Size, LocB.AATags, AAQI);
@@ -1376,42 +1380,20 @@ AliasResult BasicAAResult::aliasPHI(const PHINode *PN, LocationSize PNSize,
13761380
// on corresponding edges.
13771381
if (const PHINode *PN2 = dyn_cast<PHINode>(V2))
13781382
if (PN2->getParent() == PN->getParent()) {
1379-
AAQueryInfo::LocPair Locs(MemoryLocation(PN, PNSize, PNAAInfo),
1380-
MemoryLocation(V2, V2Size, V2AAInfo));
1381-
if (PN > V2)
1382-
std::swap(Locs.first, Locs.second);
1383-
// Analyse the PHIs' inputs under the assumption that the PHIs are
1384-
// NoAlias.
1385-
// If the PHIs are May/MustAlias there must be (recursively) an input
1386-
// operand from outside the PHIs' cycle that is MayAlias/MustAlias or
1387-
// there must be an operation on the PHIs within the PHIs' value cycle
1388-
// that causes a MayAlias.
1389-
// Pretend the phis do not alias.
1390-
AliasResult Alias = NoAlias;
1391-
AliasResult OrigAliasResult;
1392-
{
1393-
// Limited lifetime iterator invalidated by the aliasCheck call below.
1394-
auto CacheIt = AAQI.AliasCache.find(Locs);
1395-
assert((CacheIt != AAQI.AliasCache.end()) &&
1396-
"There must exist an entry for the phi node");
1397-
OrigAliasResult = CacheIt->second;
1398-
CacheIt->second = NoAlias;
1399-
}
1400-
1383+
Optional<AliasResult> Alias;
14011384
for (unsigned i = 0, e = PN->getNumIncomingValues(); i != e; ++i) {
14021385
AliasResult ThisAlias =
14031386
aliasCheck(PN->getIncomingValue(i), PNSize, PNAAInfo,
14041387
PN2->getIncomingValueForBlock(PN->getIncomingBlock(i)),
14051388
V2Size, V2AAInfo, AAQI);
1406-
Alias = MergeAliasResults(ThisAlias, Alias);
1407-
if (Alias == MayAlias)
1389+
if (Alias)
1390+
*Alias = MergeAliasResults(*Alias, ThisAlias);
1391+
else
1392+
Alias = ThisAlias;
1393+
if (*Alias == MayAlias)
14081394
break;
14091395
}
1410-
1411-
// Reset if speculation failed.
1412-
if (Alias != NoAlias)
1413-
AAQI.updateResult(Locs, OrigAliasResult);
1414-
return Alias;
1396+
return *Alias;
14151397
}
14161398

14171399
SmallVector<Value *, 4> V1Srcs;
@@ -1630,64 +1612,106 @@ AliasResult BasicAAResult::aliasCheck(const Value *V1, LocationSize V1Size,
16301612
MemoryLocation(V2, V2Size, V2AAInfo));
16311613
if (V1 > V2)
16321614
std::swap(Locs.first, Locs.second);
1633-
std::pair<AAQueryInfo::AliasCacheT::iterator, bool> Pair =
1634-
AAQI.AliasCache.try_emplace(Locs, MayAlias);
1635-
if (!Pair.second)
1636-
return Pair.first->second;
1615+
const auto &Pair = AAQI.AliasCache.try_emplace(
1616+
Locs, AAQueryInfo::CacheEntry{NoAlias, 0});
1617+
if (!Pair.second) {
1618+
auto &Entry = Pair.first->second;
1619+
if (!Entry.isDefinitive()) {
1620+
// Remember that we used an assumption.
1621+
++Entry.NumAssumptionUses;
1622+
++NumAssumptionUses;
1623+
}
1624+
return Entry.Result;
1625+
}
16371626

1627+
int OrigNumAssumptionUses = NumAssumptionUses;
1628+
unsigned OrigNumAssumptionBasedResults = AssumptionBasedResults.size();
1629+
AliasResult Result = aliasCheckRecursive(V1, V1Size, V1AAInfo, V2, V2Size,
1630+
V2AAInfo, AAQI, O1, O2);
1631+
1632+
auto It = AAQI.AliasCache.find(Locs);
1633+
assert(It != AAQI.AliasCache.end() && "Must be in cache");
1634+
auto &Entry = It->second;
1635+
1636+
// Check whether a NoAlias assumption has been used, but disproven.
1637+
bool AssumptionDisproven = Entry.NumAssumptionUses > 0 && Result != NoAlias;
1638+
if (AssumptionDisproven)
1639+
Result = MayAlias;
1640+
1641+
// This is a definitive result now, when considered as a root query.
1642+
NumAssumptionUses -= Entry.NumAssumptionUses;
1643+
Entry.Result = Result;
1644+
Entry.NumAssumptionUses = -1;
1645+
1646+
// If the assumption has been disproven, remove any results that may have
1647+
// been based on this assumption. Do this after the Entry updates above to
1648+
// avoid iterator invalidation.
1649+
if (AssumptionDisproven)
1650+
while (AssumptionBasedResults.size() > OrigNumAssumptionBasedResults)
1651+
AAQI.AliasCache.erase(AssumptionBasedResults.pop_back_val());
1652+
1653+
// The result may still be based on assumptions higher up in the chain.
1654+
// Remember it, so it can be purged from the cache later.
1655+
if (OrigNumAssumptionUses != NumAssumptionUses && Result != MayAlias)
1656+
AssumptionBasedResults.push_back(Locs);
1657+
return Result;
1658+
}
1659+
1660+
AliasResult BasicAAResult::aliasCheckRecursive(
1661+
const Value *V1, LocationSize V1Size, const AAMDNodes &V1AAInfo,
1662+
const Value *V2, LocationSize V2Size, const AAMDNodes &V2AAInfo,
1663+
AAQueryInfo &AAQI, const Value *O1, const Value *O2) {
16381664
if (const GEPOperator *GV1 = dyn_cast<GEPOperator>(V1)) {
16391665
AliasResult Result =
16401666
aliasGEP(GV1, V1Size, V1AAInfo, V2, V2Size, V2AAInfo, O1, O2, AAQI);
16411667
if (Result != MayAlias)
1642-
return AAQI.updateResult(Locs, Result);
1668+
return Result;
16431669
} else if (const GEPOperator *GV2 = dyn_cast<GEPOperator>(V2)) {
16441670
AliasResult Result =
16451671
aliasGEP(GV2, V2Size, V2AAInfo, V1, V1Size, V1AAInfo, O2, O1, AAQI);
16461672
if (Result != MayAlias)
1647-
return AAQI.updateResult(Locs, Result);
1673+
return Result;
16481674
}
16491675

16501676
if (const PHINode *PN = dyn_cast<PHINode>(V1)) {
16511677
AliasResult Result =
16521678
aliasPHI(PN, V1Size, V1AAInfo, V2, V2Size, V2AAInfo, O2, AAQI);
16531679
if (Result != MayAlias)
1654-
return AAQI.updateResult(Locs, Result);
1680+
return Result;
16551681
} else if (const PHINode *PN = dyn_cast<PHINode>(V2)) {
16561682
AliasResult Result =
16571683
aliasPHI(PN, V2Size, V2AAInfo, V1, V1Size, V1AAInfo, O1, AAQI);
16581684
if (Result != MayAlias)
1659-
return AAQI.updateResult(Locs, Result);
1685+
return Result;
16601686
}
16611687

16621688
if (const SelectInst *S1 = dyn_cast<SelectInst>(V1)) {
16631689
AliasResult Result =
16641690
aliasSelect(S1, V1Size, V1AAInfo, V2, V2Size, V2AAInfo, O2, AAQI);
16651691
if (Result != MayAlias)
1666-
return AAQI.updateResult(Locs, Result);
1692+
return Result;
16671693
} else if (const SelectInst *S2 = dyn_cast<SelectInst>(V2)) {
16681694
AliasResult Result =
16691695
aliasSelect(S2, V2Size, V2AAInfo, V1, V1Size, V1AAInfo, O1, AAQI);
16701696
if (Result != MayAlias)
1671-
return AAQI.updateResult(Locs, Result);
1697+
return Result;
16721698
}
16731699

16741700
// If both pointers are pointing into the same object and one of them
16751701
// accesses the entire object, then the accesses must overlap in some way.
1676-
if (O1 == O2)
1702+
if (O1 == O2) {
1703+
bool NullIsValidLocation = NullPointerIsDefined(&F);
16771704
if (V1Size.isPrecise() && V2Size.isPrecise() &&
16781705
(isObjectSize(O1, V1Size.getValue(), DL, TLI, NullIsValidLocation) ||
16791706
isObjectSize(O2, V2Size.getValue(), DL, TLI, NullIsValidLocation)))
1680-
return AAQI.updateResult(Locs, PartialAlias);
1707+
return PartialAlias;
1708+
}
16811709

16821710
// Recurse back into the best AA results we have, potentially with refined
16831711
// memory locations. We have already ensured that BasicAA has a MayAlias
16841712
// cache result for these, so any recursion back into BasicAA won't loop.
1685-
AliasResult Result = getBestAAResults().alias(Locs.first, Locs.second, AAQI);
1686-
if (Result != MayAlias)
1687-
return AAQI.updateResult(Locs, Result);
1688-
1689-
// MayAlias is already in the cache.
1690-
return MayAlias;
1713+
return getBestAAResults().alias(MemoryLocation(V1, V1Size, V1AAInfo),
1714+
MemoryLocation(V2, V2Size, V2AAInfo), AAQI);
16911715
}
16921716

16931717
/// Check whether two Values can be considered equivalent.

llvm/test/Analysis/BasicAA/phi-speculation.ll

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@ target datalayout =
66
; ptr_phi and ptr2_phi do not alias.
77
; CHECK: test_noalias_1
88
; CHECK: NoAlias: i32* %ptr2_phi, i32* %ptr_phi
9-
; CHECK: MayAlias: i32* %ptr2_inc, i32* %ptr_inc
10-
; TODO: The incs should also be NoAlias.
9+
; CHECK: NoAlias: i32* %ptr2_inc, i32* %ptr_inc
1110
define i32 @test_noalias_1(i32* %ptr2, i32 %count, i32* %coeff) {
1211
entry:
1312
%ptr = getelementptr inbounds i32, i32* %ptr2, i64 1
@@ -36,10 +35,9 @@ the_exit:
3635

3736
; CHECK: test_noalias_2
3837
; CHECK: NoAlias: i32* %ptr_outer_phi, i32* %ptr_outer_phi2
39-
; CHECK: MayAlias: i32* %ptr2_inc_outer, i32* %ptr_inc_outer
38+
; CHECK: NoAlias: i32* %ptr2_inc_outer, i32* %ptr_inc_outer
4039
; CHECK: NoAlias: i32* %ptr2_phi, i32* %ptr_phi
41-
; CHECK: MayAlias: i32* %ptr2_inc, i32* %ptr_inc
42-
; TODO: The incs should also be NoAlias.
40+
; CHECK: NoAlias: i32* %ptr2_inc, i32* %ptr_inc
4341
define i32 @test_noalias_2(i32* %ptr2, i32 %count, i32* %coeff) {
4442
entry:
4543
%ptr = getelementptr inbounds i32, i32* %ptr2, i64 1
@@ -118,8 +116,7 @@ exit:
118116
}
119117

120118
; CHECK-LABEL: test_no_loop_mustalias
121-
; CHECK: MayAlias: i16* %z16, i8* %z8
122-
; TODO: (z8, z16) could be MustAlias
119+
; CHECK: MustAlias: i16* %z16, i8* %z8
123120
define void @test_no_loop_mustalias(i1 %c, i8* noalias %x8, i8* noalias %y8) {
124121
br i1 %c, label %if, label %else
125122

llvm/unittests/Analysis/AliasAnalysisTest.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -295,8 +295,7 @@ TEST_F(AliasAnalysisTest, BatchAAPhiAssumption) {
295295

296296
BatchAAResults BatchAA(AA);
297297
EXPECT_EQ(MayAlias, BatchAA.alias(ALoc, BLoc));
298-
// TODO: This is incorrect.
299-
EXPECT_EQ(NoAlias, BatchAA.alias(ANextLoc, BNextLoc));
298+
EXPECT_EQ(MayAlias, BatchAA.alias(ANextLoc, BNextLoc));
300299
}
301300

302301
class AAPassInfraTest : public testing::Test {

0 commit comments

Comments
 (0)