Skip to content

Commit 6f59de3

Browse files
alanzhao1nikic
authored andcommitted
Reapply "[coro][CoroSplit] Use llvm.lifetime.end to compute putting objects on the frame vs the stack (#90265)"
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 fix clang format issue Use SmallPtrSet instead of DenseMap remove extra change use SmallPtrSet for loops Also make curly braces more consistent deduplicate code by using templates remove unnecessary change..again reduce size of StopBBReachable to align with size of CoroSuspendBBs ditto with StopLoops Improve template parameter names Co-authored-by: Chuanqi Xu <[email protected]> fix build failure create and use SingleEntrySet
1 parent c5cd049 commit 6f59de3

File tree

4 files changed

+263
-29
lines changed

4 files changed

+263
-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: 66 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -130,14 +130,20 @@ 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, bool IsManyStop>
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+
SmallPtrSet<const BasicBlock *, 2> StopBBReachable;
141+
if (DT) {
142+
for (auto *BB : StopSet) {
143+
if (DT->isReachableFromEntry(BB))
144+
StopBBReachable.insert(BB);
145+
}
146+
}
141147

142148
// We can't skip directly from a block that dominates the stop block if the
143149
// exclusion block is potentially in between.
@@ -155,20 +161,31 @@ bool llvm::isPotentiallyReachableFromMany(
155161
}
156162
}
157163

158-
const Loop *StopLoop = LI ? getOutermostLoop(LI, StopBB) : nullptr;
164+
SmallPtrSet<const Loop *, 2> StopLoops;
165+
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 StopBBReachable.contains(BB) && 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,43 @@ bool llvm::isPotentiallyReachableFromMany(
204221
return false;
205222
}
206223

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