Skip to content

Commit 51aceb5

Browse files
TylerNowickitnowicki
andauthored
[llvm/llvm-project][Coroutines] Improve debugging and minor refactoring (#104642)
No Functional Changes * Fix comments in several places * Instead of using BB-getName() (in dump methods) use getBasicBlockLabel. This fixes the poor output of the dumped info that resulted in missing BB labels. * Use RPO when dumping SuspendCrossingInfo. Without this the dump order is determined by the ptr addresses and so is not consistent from run to run making IR diffs difficult to read. * Inference -> Interference * Pull the logic that determines insertion location out of insertSpills and into getSpillInsertionPt, to differentiate between these two operations. * Use Shape getters for CoroId instead of getting it manually. --------- Co-authored-by: tnowicki <[email protected]>
1 parent d22bee1 commit 51aceb5

File tree

3 files changed

+103
-71
lines changed

3 files changed

+103
-71
lines changed

llvm/lib/Transforms/Coroutines/CoroEarly.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ void Lowerer::lowerEarlyIntrinsics(Function &F) {
203203
if (auto *CII = cast<CoroIdInst>(&I)) {
204204
if (CII->getInfo().isPreSplit()) {
205205
assert(F.isPresplitCoroutine() &&
206-
"The frontend uses Swtich-Resumed ABI should emit "
206+
"The frontend uses Switch-Resumed ABI should emit "
207207
"\"presplitcoroutine\" attribute for the coroutine.");
208208
setCannotDuplicate(CII);
209209
CII->setCoroutineSelf();

llvm/lib/Transforms/Coroutines/CoroFrame.cpp

Lines changed: 98 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
//
77
//===----------------------------------------------------------------------===//
88
// This file contains classes used to discover if for a particular value
9-
// there from sue to definition that crosses a suspend block.
9+
// its definition precedes and its uses follow a suspend block. This is
10+
// referred to as a suspend crossing value.
1011
//
1112
// Using the information discovered we form a Coroutine Frame structure to
1213
// contain those values. All uses of those values are replaced with appropriate
@@ -124,7 +125,8 @@ class SuspendCrossingInfo {
124125
public:
125126
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
126127
void dump() const;
127-
void dump(StringRef Label, BitVector const &BV) const;
128+
void dump(StringRef Label, BitVector const &BV,
129+
const ReversePostOrderTraversal<Function *> &RPOT) const;
128130
#endif
129131

130132
SuspendCrossingInfo(Function &F, coro::Shape &Shape);
@@ -207,21 +209,41 @@ class SuspendCrossingInfo {
207209
} // end anonymous namespace
208210

209211
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
210-
LLVM_DUMP_METHOD void SuspendCrossingInfo::dump(StringRef Label,
211-
BitVector const &BV) const {
212+
static std::string getBasicBlockLabel(const BasicBlock *BB) {
213+
if (BB->hasName())
214+
return BB->getName().str();
215+
216+
std::string S;
217+
raw_string_ostream OS(S);
218+
BB->printAsOperand(OS, false);
219+
return OS.str().substr(1);
220+
}
221+
222+
LLVM_DUMP_METHOD void SuspendCrossingInfo::dump(
223+
StringRef Label, BitVector const &BV,
224+
const ReversePostOrderTraversal<Function *> &RPOT) const {
212225
dbgs() << Label << ":";
213-
for (size_t I = 0, N = BV.size(); I < N; ++I)
214-
if (BV[I])
215-
dbgs() << " " << Mapping.indexToBlock(I)->getName();
226+
for (const BasicBlock *BB : RPOT) {
227+
auto BBNo = Mapping.blockToIndex(BB);
228+
if (BV[BBNo])
229+
dbgs() << " " << getBasicBlockLabel(BB);
230+
}
216231
dbgs() << "\n";
217232
}
218233

219234
LLVM_DUMP_METHOD void SuspendCrossingInfo::dump() const {
220-
for (size_t I = 0, N = Block.size(); I < N; ++I) {
221-
BasicBlock *const B = Mapping.indexToBlock(I);
222-
dbgs() << B->getName() << ":\n";
223-
dump(" Consumes", Block[I].Consumes);
224-
dump(" Kills", Block[I].Kills);
235+
if (Block.empty())
236+
return;
237+
238+
BasicBlock *const B = Mapping.indexToBlock(0);
239+
Function *F = B->getParent();
240+
241+
ReversePostOrderTraversal<Function *> RPOT(F);
242+
for (const BasicBlock *BB : RPOT) {
243+
auto BBNo = Mapping.blockToIndex(BB);
244+
dbgs() << getBasicBlockLabel(BB) << ":\n";
245+
dump(" Consumes", Block[BBNo].Consumes, RPOT);
246+
dump(" Kills", Block[BBNo].Kills, RPOT);
225247
}
226248
dbgs() << "\n";
227249
}
@@ -418,10 +440,7 @@ struct RematGraph {
418440
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
419441
void dump() const {
420442
dbgs() << "Entry (";
421-
if (EntryNode->Node->getParent()->hasName())
422-
dbgs() << EntryNode->Node->getParent()->getName();
423-
else
424-
EntryNode->Node->getParent()->printAsOperand(dbgs(), false);
443+
dbgs() << getBasicBlockLabel(EntryNode->Node->getParent());
425444
dbgs() << ") : " << *EntryNode->Node << "\n";
426445
for (auto &E : Remats) {
427446
dbgs() << *(E.first) << "\n";
@@ -551,7 +570,7 @@ struct FrameDataInfo {
551570

552571
#ifndef NDEBUG
553572
static void dumpSpills(StringRef Title, const SpillInfo &Spills) {
554-
dbgs() << "------------- " << Title << "--------------\n";
573+
dbgs() << "------------- " << Title << " --------------\n";
555574
for (const auto &E : Spills) {
556575
E.first->dump();
557576
dbgs() << " user: ";
@@ -813,7 +832,7 @@ void FrameTypeBuilder::addFieldForAllocas(const Function &F,
813832
StackLifetime StackLifetimeAnalyzer(F, ExtractAllocas(),
814833
StackLifetime::LivenessType::May);
815834
StackLifetimeAnalyzer.run();
816-
auto IsAllocaInferenre = [&](const AllocaInst *AI1, const AllocaInst *AI2) {
835+
auto DoAllocasInterfere = [&](const AllocaInst *AI1, const AllocaInst *AI2) {
817836
return StackLifetimeAnalyzer.getLiveRange(AI1).overlaps(
818837
StackLifetimeAnalyzer.getLiveRange(AI2));
819838
};
@@ -833,13 +852,13 @@ void FrameTypeBuilder::addFieldForAllocas(const Function &F,
833852
for (const auto &A : FrameData.Allocas) {
834853
AllocaInst *Alloca = A.Alloca;
835854
bool Merged = false;
836-
// Try to find if the Alloca is not inferenced with any existing
855+
// Try to find if the Alloca does not interfere with any existing
837856
// NonOverlappedAllocaSet. If it is true, insert the alloca to that
838857
// NonOverlappedAllocaSet.
839858
for (auto &AllocaSet : NonOverlapedAllocas) {
840859
assert(!AllocaSet.empty() && "Processing Alloca Set is not empty.\n");
841-
bool NoInference = none_of(AllocaSet, [&](auto Iter) {
842-
return IsAllocaInferenre(Alloca, Iter);
860+
bool NoInterference = none_of(AllocaSet, [&](auto Iter) {
861+
return DoAllocasInterfere(Alloca, Iter);
843862
});
844863
// If the alignment of A is multiple of the alignment of B, the address
845864
// of A should satisfy the requirement for aligning for B.
@@ -852,7 +871,7 @@ void FrameTypeBuilder::addFieldForAllocas(const Function &F,
852871
return LargestAlloca->getAlign().value() % Alloca->getAlign().value() ==
853872
0;
854873
}();
855-
bool CouldMerge = NoInference && Alignable;
874+
bool CouldMerge = NoInterference && Alignable;
856875
if (!CouldMerge)
857876
continue;
858877
AllocaSet.push_back(Alloca);
@@ -1714,6 +1733,51 @@ static Instruction *splitBeforeCatchSwitch(CatchSwitchInst *CatchSwitch) {
17141733
return CleanupRet;
17151734
}
17161735

1736+
static BasicBlock::iterator getSpillInsertionPt(const coro::Shape &Shape,
1737+
Value *Def,
1738+
const DominatorTree &DT) {
1739+
BasicBlock::iterator InsertPt;
1740+
if (auto *Arg = dyn_cast<Argument>(Def)) {
1741+
// For arguments, we will place the store instruction right after
1742+
// the coroutine frame pointer instruction, i.e. coro.begin.
1743+
InsertPt = Shape.getInsertPtAfterFramePtr();
1744+
1745+
// If we're spilling an Argument, make sure we clear 'nocapture'
1746+
// from the coroutine function.
1747+
Arg->getParent()->removeParamAttr(Arg->getArgNo(), Attribute::NoCapture);
1748+
} else if (auto *CSI = dyn_cast<AnyCoroSuspendInst>(Def)) {
1749+
// Don't spill immediately after a suspend; splitting assumes
1750+
// that the suspend will be followed by a branch.
1751+
InsertPt = CSI->getParent()->getSingleSuccessor()->getFirstNonPHIIt();
1752+
} else {
1753+
auto *I = cast<Instruction>(Def);
1754+
if (!DT.dominates(Shape.CoroBegin, I)) {
1755+
// If it is not dominated by CoroBegin, then spill should be
1756+
// inserted immediately after CoroFrame is computed.
1757+
InsertPt = Shape.getInsertPtAfterFramePtr();
1758+
} else if (auto *II = dyn_cast<InvokeInst>(I)) {
1759+
// If we are spilling the result of the invoke instruction, split
1760+
// the normal edge and insert the spill in the new block.
1761+
auto *NewBB = SplitEdge(II->getParent(), II->getNormalDest());
1762+
InsertPt = NewBB->getTerminator()->getIterator();
1763+
} else if (isa<PHINode>(I)) {
1764+
// Skip the PHINodes and EH pads instructions.
1765+
BasicBlock *DefBlock = I->getParent();
1766+
if (auto *CSI = dyn_cast<CatchSwitchInst>(DefBlock->getTerminator()))
1767+
InsertPt = splitBeforeCatchSwitch(CSI)->getIterator();
1768+
else
1769+
InsertPt = DefBlock->getFirstInsertionPt();
1770+
} else {
1771+
assert(!I->isTerminator() && "unexpected terminator");
1772+
// For all other values, the spill is placed immediately after
1773+
// the definition.
1774+
InsertPt = I->getNextNode()->getIterator();
1775+
}
1776+
}
1777+
1778+
return InsertPt;
1779+
}
1780+
17171781
// Replace all alloca and SSA values that are accessed across suspend points
17181782
// with GetElementPointer from coroutine frame + loads and stores. Create an
17191783
// AllocaSpillBB that will become the new entry block for the resume parts of
@@ -1736,9 +1800,8 @@ static Instruction *splitBeforeCatchSwitch(CatchSwitchInst *CatchSwitch) {
17361800
//
17371801
//
17381802
static void insertSpills(const FrameDataInfo &FrameData, coro::Shape &Shape) {
1739-
auto *CB = Shape.CoroBegin;
1740-
LLVMContext &C = CB->getContext();
1741-
Function *F = CB->getFunction();
1803+
LLVMContext &C = Shape.CoroBegin->getContext();
1804+
Function *F = Shape.CoroBegin->getFunction();
17421805
IRBuilder<> Builder(C);
17431806
StructType *FrameTy = Shape.FrameTy;
17441807
Value *FramePtr = Shape.FramePtr;
@@ -1799,47 +1862,16 @@ static void insertSpills(const FrameDataInfo &FrameData, coro::Shape &Shape) {
17991862
auto SpillAlignment = Align(FrameData.getAlign(Def));
18001863
// Create a store instruction storing the value into the
18011864
// coroutine frame.
1802-
BasicBlock::iterator InsertPt;
1865+
BasicBlock::iterator InsertPt = getSpillInsertionPt(Shape, Def, DT);
1866+
18031867
Type *ByValTy = nullptr;
18041868
if (auto *Arg = dyn_cast<Argument>(Def)) {
1805-
// For arguments, we will place the store instruction right after
1806-
// the coroutine frame pointer instruction, i.e. coro.begin.
1807-
InsertPt = Shape.getInsertPtAfterFramePtr();
1808-
18091869
// If we're spilling an Argument, make sure we clear 'nocapture'
18101870
// from the coroutine function.
18111871
Arg->getParent()->removeParamAttr(Arg->getArgNo(), Attribute::NoCapture);
18121872

18131873
if (Arg->hasByValAttr())
18141874
ByValTy = Arg->getParamByValType();
1815-
} else if (auto *CSI = dyn_cast<AnyCoroSuspendInst>(Def)) {
1816-
// Don't spill immediately after a suspend; splitting assumes
1817-
// that the suspend will be followed by a branch.
1818-
InsertPt = CSI->getParent()->getSingleSuccessor()->getFirstNonPHIIt();
1819-
} else {
1820-
auto *I = cast<Instruction>(Def);
1821-
if (!DT.dominates(CB, I)) {
1822-
// If it is not dominated by CoroBegin, then spill should be
1823-
// inserted immediately after CoroFrame is computed.
1824-
InsertPt = Shape.getInsertPtAfterFramePtr();
1825-
} else if (auto *II = dyn_cast<InvokeInst>(I)) {
1826-
// If we are spilling the result of the invoke instruction, split
1827-
// the normal edge and insert the spill in the new block.
1828-
auto *NewBB = SplitEdge(II->getParent(), II->getNormalDest());
1829-
InsertPt = NewBB->getTerminator()->getIterator();
1830-
} else if (isa<PHINode>(I)) {
1831-
// Skip the PHINodes and EH pads instructions.
1832-
BasicBlock *DefBlock = I->getParent();
1833-
if (auto *CSI = dyn_cast<CatchSwitchInst>(DefBlock->getTerminator()))
1834-
InsertPt = splitBeforeCatchSwitch(CSI)->getIterator();
1835-
else
1836-
InsertPt = DefBlock->getFirstInsertionPt();
1837-
} else {
1838-
assert(!I->isTerminator() && "unexpected terminator");
1839-
// For all other values, the spill is placed immediately after
1840-
// the definition.
1841-
InsertPt = I->getNextNode()->getIterator();
1842-
}
18431875
}
18441876

18451877
auto Index = FrameData.getFieldIndex(Def);
@@ -1982,7 +2014,7 @@ static void insertSpills(const FrameDataInfo &FrameData, coro::Shape &Shape) {
19822014
UsersToUpdate.clear();
19832015
for (User *U : Alloca->users()) {
19842016
auto *I = cast<Instruction>(U);
1985-
if (DT.dominates(CB, I))
2017+
if (DT.dominates(Shape.CoroBegin, I))
19862018
UsersToUpdate.push_back(I);
19872019
}
19882020
if (UsersToUpdate.empty())
@@ -2024,7 +2056,7 @@ static void insertSpills(const FrameDataInfo &FrameData, coro::Shape &Shape) {
20242056
Builder.CreateStore(Value, G);
20252057
}
20262058
// For each alias to Alloca created before CoroBegin but used after
2027-
// CoroBegin, we recreate them after CoroBegin by appplying the offset
2059+
// CoroBegin, we recreate them after CoroBegin by applying the offset
20282060
// to the pointer in the frame.
20292061
for (const auto &Alias : A.Aliases) {
20302062
auto *FramePtr = GetFramePointer(Alloca);
@@ -2033,7 +2065,7 @@ static void insertSpills(const FrameDataInfo &FrameData, coro::Shape &Shape) {
20332065
auto *AliasPtr =
20342066
Builder.CreatePtrAdd(FramePtr, ConstantInt::get(ITy, Value));
20352067
Alias.first->replaceUsesWithIf(
2036-
AliasPtr, [&](Use &U) { return DT.dominates(CB, U); });
2068+
AliasPtr, [&](Use &U) { return DT.dominates(Shape.CoroBegin, U); });
20372069
}
20382070
}
20392071

@@ -2046,7 +2078,7 @@ static void insertSpills(const FrameDataInfo &FrameData, coro::Shape &Shape) {
20462078
// If there is memory accessing to promise alloca before CoroBegin;
20472079
bool HasAccessingPromiseBeforeCB = llvm::any_of(PA->uses(), [&](Use &U) {
20482080
auto *Inst = dyn_cast<Instruction>(U.getUser());
2049-
if (!Inst || DT.dominates(CB, Inst))
2081+
if (!Inst || DT.dominates(Shape.CoroBegin, Inst))
20502082
return false;
20512083

20522084
if (auto *CI = dyn_cast<CallInst>(Inst)) {
@@ -2692,7 +2724,7 @@ static void eliminateSwiftError(Function &F, coro::Shape &Shape) {
26922724
}
26932725
}
26942726

2695-
/// retcon and retcon.once conventions assume that all spill uses can be sunk
2727+
/// Async and Retcon{Once} conventions assume that all spill uses can be sunk
26962728
/// after the coro.begin intrinsic.
26972729
static void sinkSpillUsesAfterCoroBegin(Function &F,
26982730
const FrameDataInfo &FrameData,
@@ -2728,7 +2760,7 @@ static void sinkSpillUsesAfterCoroBegin(Function &F,
27282760
// Sort by dominance.
27292761
SmallVector<Instruction *, 64> InsertionList(ToMove.begin(), ToMove.end());
27302762
llvm::sort(InsertionList, [&Dom](Instruction *A, Instruction *B) -> bool {
2731-
// If a dominates b it should preceed (<) b.
2763+
// If a dominates b it should precede (<) b.
27322764
return Dom.dominates(A, B);
27332765
});
27342766

@@ -3126,7 +3158,7 @@ void coro::buildCoroutineFrame(
31263158
cleanupSinglePredPHIs(F);
31273159

31283160
// Transforms multi-edge PHI Nodes, so that any value feeding into a PHI will
3129-
// never has its definition separated from the PHI by the suspend point.
3161+
// never have its definition separated from the PHI by the suspend point.
31303162
rewritePHIs(F);
31313163

31323164
// Build suspend crossing info.
@@ -3223,6 +3255,7 @@ void coro::buildCoroutineFrame(
32233255
Shape.FramePtr = Shape.CoroBegin;
32243256
// For now, this works for C++ programs only.
32253257
buildFrameDebugInfo(F, Shape, FrameData);
3258+
// Insert spills and reloads
32263259
insertSpills(FrameData, Shape);
32273260
lowerLocalAllocas(LocalAllocas, DeadInstructions);
32283261

llvm/lib/Transforms/Coroutines/CoroSplit.cpp

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1221,11 +1221,10 @@ static void postSplitCleanup(Function &F) {
12211221
// frame if possible.
12221222
static void handleNoSuspendCoroutine(coro::Shape &Shape) {
12231223
auto *CoroBegin = Shape.CoroBegin;
1224-
auto *CoroId = CoroBegin->getId();
1225-
auto *AllocInst = CoroId->getCoroAlloc();
12261224
switch (Shape.ABI) {
12271225
case coro::ABI::Switch: {
1228-
auto SwitchId = cast<CoroIdInst>(CoroId);
1226+
auto SwitchId = Shape.getSwitchCoroId();
1227+
auto *AllocInst = SwitchId->getCoroAlloc();
12291228
coro::replaceCoroFree(SwitchId, /*Elide=*/AllocInst != nullptr);
12301229
if (AllocInst) {
12311230
IRBuilder<> Builder(AllocInst);
@@ -1689,7 +1688,7 @@ static void splitAsyncCoroutine(Function &F, coro::Shape &Shape,
16891688
auto &Context = F.getContext();
16901689
auto *Int8PtrTy = PointerType::getUnqual(Context);
16911690

1692-
auto *Id = cast<CoroIdAsyncInst>(Shape.CoroBegin->getId());
1691+
auto *Id = Shape.getAsyncCoroId();
16931692
IRBuilder<> Builder(Id);
16941693

16951694
auto *FramePtr = Id->getStorage();
@@ -1783,7 +1782,7 @@ static void splitRetconCoroutine(Function &F, coro::Shape &Shape,
17831782
F.removeRetAttr(Attribute::NonNull);
17841783

17851784
// Allocate the frame.
1786-
auto *Id = cast<AnyCoroIdRetconInst>(Shape.CoroBegin->getId());
1785+
auto *Id = Shape.getRetconCoroId();
17871786
Value *RawFramePtr;
17881787
if (Shape.RetconLowering.IsFrameInlineInStorage) {
17891788
RawFramePtr = Id->getStorage();

0 commit comments

Comments
 (0)