Skip to content

Commit 8441a7b

Browse files
alanzhao1nikic
authored andcommitted
Reapply "[coro][CoroSplit] Use `llvm.lt log
ifetime.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
1 parent 7098cd2 commit 8441a7b

File tree

4 files changed

+281
-31
lines changed

4 files changed

+281
-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: 84 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 T, bool IsMany>
134+
static bool isReachableImpl(SmallVectorImpl<BasicBlock *> &Worklist,
135+
const T *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 (IsMany)
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 (IsMany) {
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,47 @@ 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 constexpr (IsMany) {
183+
if (LI) {
184+
for (auto *StopSetBB : *StopSet) {
185+
if (const Loop *L = getOutermostLoop(LI, StopSetBB))
186+
StopLoops.insert(L);
187+
}
188+
}
189+
} else {
190+
if (LI)
191+
StopLoop = getOutermostLoop(LI, StopBB);
192+
}
159193

160194
unsigned Limit = DefaultMaxBBsToExplore;
161195
SmallPtrSet<const BasicBlock*, 32> Visited;
162196
do {
163197
BasicBlock *BB = Worklist.pop_back_val();
164198
if (!Visited.insert(BB).second)
165199
continue;
166-
if (BB == StopBB)
167-
return true;
200+
if constexpr (IsMany) {
201+
if (StopSet->contains(BB))
202+
return true;
203+
} else {
204+
if (BB == StopBB)
205+
return true;
206+
}
168207
if (ExclusionSet && ExclusionSet->count(BB))
169208
continue;
170-
if (DT && DT->dominates(BB, StopBB))
171-
return true;
209+
if (DT) {
210+
if constexpr (IsMany) {
211+
if (llvm::any_of(*StopSet, [&](const BasicBlock *StopBB) {
212+
return StopBBReachable.contains(BB) && DT->dominates(BB, StopBB);
213+
}))
214+
return true;
215+
} else {
216+
if (DT->dominates(BB, StopBB))
217+
return true;
218+
}
219+
}
172220

173221
const Loop *Outer = nullptr;
174222
if (LI) {
@@ -179,8 +227,13 @@ bool llvm::isPotentiallyReachableFromMany(
179227
// excluded block. Clear Outer so we process BB's successors.
180228
if (LoopsWithHoles.count(Outer))
181229
Outer = nullptr;
182-
if (StopLoop && Outer == StopLoop)
183-
return true;
230+
if constexpr (IsMany) {
231+
if (StopLoops.contains(Outer))
232+
return true;
233+
} else {
234+
if (StopLoop && Outer == StopLoop)
235+
return true;
236+
}
184237
}
185238

186239
if (!--Limit) {
@@ -204,6 +257,23 @@ bool llvm::isPotentiallyReachableFromMany(
204257
return false;
205258
}
206259

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