Skip to content

Commit f0b2725

Browse files
alanzhao1nikic
authored andcommitted
Reapply "[coro][CoroSplit] Use llvm.lifetime.end to compute putting objects on the frame vs the stack (llvm#90265)"
This reverts commit 9243841. This reland addresses the performance regressions seen in llvm#90265 by retaining the original definition of `isPotentiallyReachableFromMany(...)` instead of reimplementing it with `isManyPotentiallyReachableFromMany(...)`. Fixes llvm#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
1 parent 2163ae7 commit f0b2725

File tree

4 files changed

+280
-31
lines changed

4 files changed

+280
-31
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: 83 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -130,14 +130,35 @@ 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 StopT, bool IsManyStop>
134+
static bool isReachableImpl(SmallVectorImpl<BasicBlock *> &Worklist,
135+
const StopT *StopBBOrSet,
136+
const SmallPtrSetImpl<BasicBlock *> *ExclusionSet,
137+
const DominatorTree *DT, const LoopInfo *LI) {
138+
const BasicBlock *StopBB;
139+
const SmallPtrSetImpl<const BasicBlock *> *StopSet;
140+
141+
// SmallPtrSetImpl is incompatible with LLVM's casting functions.
142+
if constexpr (IsManyStop)
143+
StopSet =
144+
static_cast<const SmallPtrSetImpl<const BasicBlock *> *>(StopBBOrSet);
145+
else
146+
StopBB = static_cast<const BasicBlock *>(StopBBOrSet);
147+
148+
// When a stop block is unreachable, it's dominated from everywhere,
138149
// regardless of whether there's a path between the two blocks.
139-
if (DT && !DT->isReachableFromEntry(StopBB))
140-
DT = nullptr;
150+
SmallPtrSet<const BasicBlock *, 2> StopBBReachable;
151+
if (DT) {
152+
if constexpr (IsManyStop) {
153+
for (auto *BB : *StopSet) {
154+
if (DT->isReachableFromEntry(BB))
155+
StopBBReachable.insert(BB);
156+
}
157+
} else {
158+
if (!DT->isReachableFromEntry(StopBB))
159+
DT = nullptr;
160+
}
161+
}
141162

142163
// We can't skip directly from a block that dominates the stop block if the
143164
// exclusion block is potentially in between.
@@ -155,20 +176,46 @@ bool llvm::isPotentiallyReachableFromMany(
155176
}
156177
}
157178

158-
const Loop *StopLoop = LI ? getOutermostLoop(LI, StopBB) : nullptr;
179+
const Loop *StopLoop = nullptr;
180+
SmallPtrSet<const Loop *, 2> StopLoops;
181+
182+
if (LI) {
183+
if constexpr (IsManyStop) {
184+
for (auto *StopSetBB : *StopSet) {
185+
if (const Loop *L = getOutermostLoop(LI, StopSetBB))
186+
StopLoops.insert(L);
187+
}
188+
} else {
189+
StopLoop = getOutermostLoop(LI, StopBB);
190+
}
191+
}
159192

160193
unsigned Limit = DefaultMaxBBsToExplore;
161194
SmallPtrSet<const BasicBlock*, 32> Visited;
162195
do {
163196
BasicBlock *BB = Worklist.pop_back_val();
164197
if (!Visited.insert(BB).second)
165198
continue;
166-
if (BB == StopBB)
167-
return true;
199+
if constexpr (IsManyStop) {
200+
if (StopSet->contains(BB))
201+
return true;
202+
} else {
203+
if (BB == StopBB)
204+
return true;
205+
}
168206
if (ExclusionSet && ExclusionSet->count(BB))
169207
continue;
170-
if (DT && DT->dominates(BB, StopBB))
171-
return true;
208+
if (DT) {
209+
if constexpr (IsManyStop) {
210+
if (llvm::any_of(*StopSet, [&](const BasicBlock *StopBB) {
211+
return StopBBReachable.contains(BB) && DT->dominates(BB, StopBB);
212+
}))
213+
return true;
214+
} else {
215+
if (DT->dominates(BB, StopBB))
216+
return true;
217+
}
218+
}
172219

173220
const Loop *Outer = nullptr;
174221
if (LI) {
@@ -179,8 +226,13 @@ bool llvm::isPotentiallyReachableFromMany(
179226
// excluded block. Clear Outer so we process BB's successors.
180227
if (LoopsWithHoles.count(Outer))
181228
Outer = nullptr;
182-
if (StopLoop && Outer == StopLoop)
183-
return true;
229+
if constexpr (IsManyStop) {
230+
if (StopLoops.contains(Outer))
231+
return true;
232+
} else {
233+
if (StopLoop && Outer == StopLoop)
234+
return true;
235+
}
184236
}
185237

186238
if (!--Limit) {
@@ -204,6 +256,23 @@ bool llvm::isPotentiallyReachableFromMany(
204256
return false;
205257
}
206258

259+
bool llvm::isPotentiallyReachableFromMany(
260+
SmallVectorImpl<BasicBlock *> &Worklist, const BasicBlock *StopBB,
261+
const SmallPtrSetImpl<BasicBlock *> *ExclusionSet, const DominatorTree *DT,
262+
const LoopInfo *LI) {
263+
return isReachableImpl<BasicBlock, false>(Worklist, StopBB, ExclusionSet, DT,
264+
LI);
265+
}
266+
267+
bool llvm::isManyPotentiallyReachableFromMany(
268+
SmallVectorImpl<BasicBlock *> &Worklist,
269+
const SmallPtrSetImpl<const BasicBlock *> &StopSet,
270+
const SmallPtrSetImpl<BasicBlock *> *ExclusionSet, const DominatorTree *DT,
271+
const LoopInfo *LI) {
272+
return isReachableImpl<SmallPtrSetImpl<const BasicBlock *>, true>(
273+
Worklist, &StopSet, ExclusionSet, DT, LI);
274+
}
275+
207276
bool llvm::isPotentiallyReachable(
208277
const BasicBlock *A, const BasicBlock *B,
209278
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)