Skip to content

Commit 0c8bc08

Browse files
authored
Reapply "[coro][CoroSplit] Use llvm.lifetime.end to compute putting objects on the frame vs the stack (#90265) (#91372)
This reverts commit 9243841. This reland addresses the performance regressions seen in #90265 by retaining the original definition of `isPotentiallyReachableFromMany(...)` instead of reimplementing it with `isManyPotentiallyReachableFromMany(...)`. Fixes #86580
1 parent dab1f7c commit 0c8bc08

File tree

4 files changed

+259
-29
lines changed

4 files changed

+259
-29
lines changed

llvm/include/llvm/Analysis/CFG.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,18 @@ bool isPotentiallyReachableFromMany(
9696
const SmallPtrSetImpl<BasicBlock *> *ExclusionSet,
9797
const DominatorTree *DT = nullptr, const LoopInfo *LI = nullptr);
9898

99+
/// Determine whether there is a potentially a path from at least one block in
100+
/// 'Worklist' to at least one block in 'StopSet' within a single function
101+
/// without passing through any of the blocks in 'ExclusionSet'. Returns false
102+
/// only if we can prove that once any block in 'Worklist' has been reached then
103+
/// no blocks in 'StopSet' can be executed without passing through any blocks in
104+
/// 'ExclusionSet'. Conservatively returns true.
105+
bool isManyPotentiallyReachableFromMany(
106+
SmallVectorImpl<BasicBlock *> &Worklist,
107+
const SmallPtrSetImpl<const BasicBlock *> &StopSet,
108+
const SmallPtrSetImpl<BasicBlock *> *ExclusionSet,
109+
const DominatorTree *DT = nullptr, const LoopInfo *LI = nullptr);
110+
99111
/// Return true if the control flow in \p RPOTraversal is irreducible.
100112
///
101113
/// This is a generic implementation to detect CFG irreducibility based on loop

llvm/lib/Analysis/CFG.cpp

Lines changed: 62 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -130,14 +130,21 @@ static const Loop *getOutermostLoop(const LoopInfo *LI, const BasicBlock *BB) {
130130
return L ? L->getOutermostLoop() : nullptr;
131131
}
132132

133-
bool llvm::isPotentiallyReachableFromMany(
134-
SmallVectorImpl<BasicBlock *> &Worklist, const BasicBlock *StopBB,
135-
const SmallPtrSetImpl<BasicBlock *> *ExclusionSet, const DominatorTree *DT,
136-
const LoopInfo *LI) {
137-
// When the stop block is unreachable, it's dominated from everywhere,
133+
template <class StopSetT>
134+
static bool isReachableImpl(SmallVectorImpl<BasicBlock *> &Worklist,
135+
const StopSetT &StopSet,
136+
const SmallPtrSetImpl<BasicBlock *> *ExclusionSet,
137+
const DominatorTree *DT, const LoopInfo *LI) {
138+
// When a stop block is unreachable, it's dominated from everywhere,
138139
// regardless of whether there's a path between the two blocks.
139-
if (DT && !DT->isReachableFromEntry(StopBB))
140-
DT = nullptr;
140+
if (DT) {
141+
for (auto *BB : StopSet) {
142+
if (!DT->isReachableFromEntry(BB)) {
143+
DT = nullptr;
144+
break;
145+
}
146+
}
147+
}
141148

142149
// We can't skip directly from a block that dominates the stop block if the
143150
// exclusion block is potentially in between.
@@ -155,20 +162,30 @@ bool llvm::isPotentiallyReachableFromMany(
155162
}
156163
}
157164

158-
const Loop *StopLoop = LI ? getOutermostLoop(LI, StopBB) : nullptr;
165+
SmallPtrSet<const Loop *, 2> StopLoops;
166+
if (LI) {
167+
for (auto *StopSetBB : StopSet) {
168+
if (const Loop *L = getOutermostLoop(LI, StopSetBB))
169+
StopLoops.insert(L);
170+
}
171+
}
159172

160173
unsigned Limit = DefaultMaxBBsToExplore;
161174
SmallPtrSet<const BasicBlock*, 32> Visited;
162175
do {
163176
BasicBlock *BB = Worklist.pop_back_val();
164177
if (!Visited.insert(BB).second)
165178
continue;
166-
if (BB == StopBB)
179+
if (StopSet.contains(BB))
167180
return true;
168181
if (ExclusionSet && ExclusionSet->count(BB))
169182
continue;
170-
if (DT && DT->dominates(BB, StopBB))
171-
return true;
183+
if (DT) {
184+
if (llvm::any_of(StopSet, [&](const BasicBlock *StopBB) {
185+
return DT->dominates(BB, StopBB);
186+
}))
187+
return true;
188+
}
172189

173190
const Loop *Outer = nullptr;
174191
if (LI) {
@@ -179,7 +196,7 @@ bool llvm::isPotentiallyReachableFromMany(
179196
// excluded block. Clear Outer so we process BB's successors.
180197
if (LoopsWithHoles.count(Outer))
181198
Outer = nullptr;
182-
if (StopLoop && Outer == StopLoop)
199+
if (StopLoops.contains(Outer))
183200
return true;
184201
}
185202

@@ -204,6 +221,39 @@ bool llvm::isPotentiallyReachableFromMany(
204221
return false;
205222
}
206223

224+
template <class T> class SingleEntrySet {
225+
public:
226+
using const_iterator = const T *;
227+
228+
SingleEntrySet(T Elem) : Elem(Elem) {}
229+
230+
bool contains(T Other) const { return Elem == Other; }
231+
232+
const_iterator begin() const { return &Elem; }
233+
const_iterator end() const { return &Elem + 1; }
234+
235+
private:
236+
T Elem;
237+
};
238+
239+
bool llvm::isPotentiallyReachableFromMany(
240+
SmallVectorImpl<BasicBlock *> &Worklist, const BasicBlock *StopBB,
241+
const SmallPtrSetImpl<BasicBlock *> *ExclusionSet, const DominatorTree *DT,
242+
const LoopInfo *LI) {
243+
return isReachableImpl<SingleEntrySet<const BasicBlock *>>(
244+
Worklist, SingleEntrySet<const BasicBlock *>(StopBB), ExclusionSet, DT,
245+
LI);
246+
}
247+
248+
bool llvm::isManyPotentiallyReachableFromMany(
249+
SmallVectorImpl<BasicBlock *> &Worklist,
250+
const SmallPtrSetImpl<const BasicBlock *> &StopSet,
251+
const SmallPtrSetImpl<BasicBlock *> *ExclusionSet, const DominatorTree *DT,
252+
const LoopInfo *LI) {
253+
return isReachableImpl<SmallPtrSetImpl<const BasicBlock *>>(
254+
Worklist, StopSet, ExclusionSet, DT, LI);
255+
}
256+
207257
bool llvm::isPotentiallyReachable(
208258
const BasicBlock *A, const BasicBlock *B,
209259
const SmallPtrSetImpl<BasicBlock *> *ExclusionSet, const DominatorTree *DT,

llvm/lib/Transforms/Coroutines/CoroFrame.cpp

Lines changed: 43 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "llvm/ADT/PostOrderIterator.h"
2020
#include "llvm/ADT/ScopeExit.h"
2121
#include "llvm/ADT/SmallString.h"
22+
#include "llvm/Analysis/CFG.h"
2223
#include "llvm/Analysis/PtrUseVisitor.h"
2324
#include "llvm/Analysis/StackLifetime.h"
2425
#include "llvm/Config/llvm-config.h"
@@ -1440,17 +1441,22 @@ namespace {
14401441
struct AllocaUseVisitor : PtrUseVisitor<AllocaUseVisitor> {
14411442
using Base = PtrUseVisitor<AllocaUseVisitor>;
14421443
AllocaUseVisitor(const DataLayout &DL, const DominatorTree &DT,
1443-
const CoroBeginInst &CB, const SuspendCrossingInfo &Checker,
1444+
const coro::Shape &CoroShape,
1445+
const SuspendCrossingInfo &Checker,
14441446
bool ShouldUseLifetimeStartInfo)
1445-
: PtrUseVisitor(DL), DT(DT), CoroBegin(CB), Checker(Checker),
1446-
ShouldUseLifetimeStartInfo(ShouldUseLifetimeStartInfo) {}
1447+
: PtrUseVisitor(DL), DT(DT), CoroShape(CoroShape), Checker(Checker),
1448+
ShouldUseLifetimeStartInfo(ShouldUseLifetimeStartInfo) {
1449+
for (AnyCoroSuspendInst *SuspendInst : CoroShape.CoroSuspends)
1450+
CoroSuspendBBs.insert(SuspendInst->getParent());
1451+
}
14471452

14481453
void visit(Instruction &I) {
14491454
Users.insert(&I);
14501455
Base::visit(I);
14511456
// If the pointer is escaped prior to CoroBegin, we have to assume it would
14521457
// be written into before CoroBegin as well.
1453-
if (PI.isEscaped() && !DT.dominates(&CoroBegin, PI.getEscapingInst())) {
1458+
if (PI.isEscaped() &&
1459+
!DT.dominates(CoroShape.CoroBegin, PI.getEscapingInst())) {
14541460
MayWriteBeforeCoroBegin = true;
14551461
}
14561462
}
@@ -1553,10 +1559,19 @@ struct AllocaUseVisitor : PtrUseVisitor<AllocaUseVisitor> {
15531559
// When we found the lifetime markers refers to a
15541560
// subrange of the original alloca, ignore the lifetime
15551561
// markers to avoid misleading the analysis.
1556-
if (II.getIntrinsicID() != Intrinsic::lifetime_start || !IsOffsetKnown ||
1557-
!Offset.isZero())
1562+
if (!IsOffsetKnown || !Offset.isZero())
1563+
return Base::visitIntrinsicInst(II);
1564+
switch (II.getIntrinsicID()) {
1565+
default:
15581566
return Base::visitIntrinsicInst(II);
1559-
LifetimeStarts.insert(&II);
1567+
case Intrinsic::lifetime_start:
1568+
LifetimeStarts.insert(&II);
1569+
LifetimeStartBBs.push_back(II.getParent());
1570+
break;
1571+
case Intrinsic::lifetime_end:
1572+
LifetimeEndBBs.insert(II.getParent());
1573+
break;
1574+
}
15601575
}
15611576

15621577
void visitCallBase(CallBase &CB) {
@@ -1586,14 +1601,17 @@ struct AllocaUseVisitor : PtrUseVisitor<AllocaUseVisitor> {
15861601

15871602
private:
15881603
const DominatorTree &DT;
1589-
const CoroBeginInst &CoroBegin;
1604+
const coro::Shape &CoroShape;
15901605
const SuspendCrossingInfo &Checker;
15911606
// All alias to the original AllocaInst, created before CoroBegin and used
15921607
// after CoroBegin. Each entry contains the instruction and the offset in the
15931608
// original Alloca. They need to be recreated after CoroBegin off the frame.
15941609
DenseMap<Instruction *, std::optional<APInt>> AliasOffetMap{};
15951610
SmallPtrSet<Instruction *, 4> Users{};
15961611
SmallPtrSet<IntrinsicInst *, 2> LifetimeStarts{};
1612+
SmallVector<BasicBlock *> LifetimeStartBBs{};
1613+
SmallPtrSet<BasicBlock *, 2> LifetimeEndBBs{};
1614+
SmallPtrSet<const BasicBlock *, 2> CoroSuspendBBs{};
15971615
bool MayWriteBeforeCoroBegin{false};
15981616
bool ShouldUseLifetimeStartInfo{true};
15991617

@@ -1605,10 +1623,19 @@ struct AllocaUseVisitor : PtrUseVisitor<AllocaUseVisitor> {
16051623
// every basic block that uses the pointer to see if they cross suspension
16061624
// points. The uses cover both direct uses as well as indirect uses.
16071625
if (ShouldUseLifetimeStartInfo && !LifetimeStarts.empty()) {
1608-
for (auto *I : Users)
1609-
for (auto *S : LifetimeStarts)
1610-
if (Checker.isDefinitionAcrossSuspend(*S, I))
1611-
return true;
1626+
// If there is no explicit lifetime.end, then assume the address can
1627+
// cross suspension points.
1628+
if (LifetimeEndBBs.empty())
1629+
return true;
1630+
1631+
// If there is a path from a lifetime.start to a suspend without a
1632+
// corresponding lifetime.end, then the alloca's lifetime persists
1633+
// beyond that suspension point and the alloca must go on the frame.
1634+
llvm::SmallVector<BasicBlock *> Worklist(LifetimeStartBBs);
1635+
if (isManyPotentiallyReachableFromMany(Worklist, CoroSuspendBBs,
1636+
&LifetimeEndBBs, &DT))
1637+
return true;
1638+
16121639
// Addresses are guaranteed to be identical after every lifetime.start so
16131640
// we cannot use the local stack if the address escaped and there is a
16141641
// suspend point between lifetime markers. This should also cover the
@@ -1646,13 +1673,13 @@ struct AllocaUseVisitor : PtrUseVisitor<AllocaUseVisitor> {
16461673
}
16471674

16481675
void handleMayWrite(const Instruction &I) {
1649-
if (!DT.dominates(&CoroBegin, &I))
1676+
if (!DT.dominates(CoroShape.CoroBegin, &I))
16501677
MayWriteBeforeCoroBegin = true;
16511678
}
16521679

16531680
bool usedAfterCoroBegin(Instruction &I) {
16541681
for (auto &U : I.uses())
1655-
if (DT.dominates(&CoroBegin, U))
1682+
if (DT.dominates(CoroShape.CoroBegin, U))
16561683
return true;
16571684
return false;
16581685
}
@@ -1661,7 +1688,7 @@ struct AllocaUseVisitor : PtrUseVisitor<AllocaUseVisitor> {
16611688
// We track all aliases created prior to CoroBegin but used after.
16621689
// These aliases may need to be recreated after CoroBegin if the alloca
16631690
// need to live on the frame.
1664-
if (DT.dominates(&CoroBegin, &I) || !usedAfterCoroBegin(I))
1691+
if (DT.dominates(CoroShape.CoroBegin, &I) || !usedAfterCoroBegin(I))
16651692
return;
16661693

16671694
if (!IsOffsetKnown) {
@@ -2830,8 +2857,7 @@ static void collectFrameAlloca(AllocaInst *AI, coro::Shape &Shape,
28302857
bool ShouldUseLifetimeStartInfo =
28312858
(Shape.ABI != coro::ABI::Async && Shape.ABI != coro::ABI::Retcon &&
28322859
Shape.ABI != coro::ABI::RetconOnce);
2833-
AllocaUseVisitor Visitor{AI->getModule()->getDataLayout(), DT,
2834-
*Shape.CoroBegin, Checker,
2860+
AllocaUseVisitor Visitor{AI->getModule()->getDataLayout(), DT, Shape, Checker,
28352861
ShouldUseLifetimeStartInfo};
28362862
Visitor.visitPtr(*AI);
28372863
if (!Visitor.getShouldLiveOnFrame())

0 commit comments

Comments
 (0)