Skip to content

Commit 22fe097

Browse files
author
tnowicki
committed
[llvm/llvm-project][Coroutines] Improve debugging and minor refactoring
* Fix comments in several places * Instead of using BB-getName() which will return an empty string if the BB has no name, getBasicBlockLabel will give the BBs label e.g. #### this makes the IR easier to read, otherwise we get lots of blocks with names like ".from.". * 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. *
1 parent 46c94be commit 22fe097

File tree

3 files changed

+115
-83
lines changed

3 files changed

+115
-83
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: 110 additions & 77 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 preceeds 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
@@ -52,6 +53,16 @@ extern cl::opt<bool> UseNewDbgInfoFormat;
5253

5354
enum { SmallVectorThreshold = 32 };
5455

56+
static std::string getBasicBlockLabel(const BasicBlock *BB) {
57+
if (BB->hasName())
58+
return BB->getName().str();
59+
60+
std::string S;
61+
raw_string_ostream OS(S);
62+
BB->printAsOperand(OS, false);
63+
return OS.str().substr(1);
64+
}
65+
5566
// Provides two way mapping between the blocks and numbers.
5667
namespace {
5768
class BlockToIndexMapping {
@@ -123,8 +134,9 @@ class SuspendCrossingInfo {
123134

124135
public:
125136
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
126-
void dump() const;
127-
void dump(StringRef Label, BitVector const &BV) const;
137+
void dump(const ReversePostOrderTraversal<Function *> &RPOT) const;
138+
void dump(StringRef Label, BitVector const &BV,
139+
const ReversePostOrderTraversal<Function *> &RPOT) const;
128140
#endif
129141

130142
SuspendCrossingInfo(Function &F, coro::Shape &Shape);
@@ -207,21 +219,25 @@ class SuspendCrossingInfo {
207219
} // end anonymous namespace
208220

209221
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
210-
LLVM_DUMP_METHOD void SuspendCrossingInfo::dump(StringRef Label,
211-
BitVector const &BV) const {
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

219-
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);
234+
LLVM_DUMP_METHOD void SuspendCrossingInfo::dump(
235+
const ReversePostOrderTraversal<Function *> &RPOT) const {
236+
for (const BasicBlock *BB : RPOT) {
237+
auto BBNo = Mapping.blockToIndex(BB);
238+
dbgs() << getBasicBlockLabel(BB) << ":\n";
239+
dump(" Consumes", Block[BBNo].Consumes, RPOT);
240+
dump(" Kills", Block[BBNo].Kills, RPOT);
225241
}
226242
dbgs() << "\n";
227243
}
@@ -335,7 +351,7 @@ SuspendCrossingInfo::SuspendCrossingInfo(Function &F, coro::Shape &Shape)
335351
while (computeBlockData</*Initialize*/ false>(RPOT))
336352
;
337353

338-
LLVM_DEBUG(dump());
354+
LLVM_DEBUG(dump(RPOT));
339355
}
340356

341357
namespace {
@@ -419,7 +435,7 @@ struct RematGraph {
419435
void dump() const {
420436
dbgs() << "Entry (";
421437
if (EntryNode->Node->getParent()->hasName())
422-
dbgs() << EntryNode->Node->getParent()->getName();
438+
dbgs() << getBasicBlockLabel(EntryNode->Node->getParent());
423439
else
424440
EntryNode->Node->getParent()->printAsOperand(dbgs(), false);
425441
dbgs() << ") : " << *EntryNode->Node << "\n";
@@ -551,7 +567,7 @@ struct FrameDataInfo {
551567

552568
#ifndef NDEBUG
553569
static void dumpSpills(StringRef Title, const SpillInfo &Spills) {
554-
dbgs() << "------------- " << Title << "--------------\n";
570+
dbgs() << "------------- " << Title << " --------------\n";
555571
for (const auto &E : Spills) {
556572
E.first->dump();
557573
dbgs() << " user: ";
@@ -813,7 +829,7 @@ void FrameTypeBuilder::addFieldForAllocas(const Function &F,
813829
StackLifetime StackLifetimeAnalyzer(F, ExtractAllocas(),
814830
StackLifetime::LivenessType::May);
815831
StackLifetimeAnalyzer.run();
816-
auto IsAllocaInferenre = [&](const AllocaInst *AI1, const AllocaInst *AI2) {
832+
auto DoAllocasInterfere = [&](const AllocaInst *AI1, const AllocaInst *AI2) {
817833
return StackLifetimeAnalyzer.getLiveRange(AI1).overlaps(
818834
StackLifetimeAnalyzer.getLiveRange(AI2));
819835
};
@@ -833,13 +849,13 @@ void FrameTypeBuilder::addFieldForAllocas(const Function &F,
833849
for (const auto &A : FrameData.Allocas) {
834850
AllocaInst *Alloca = A.Alloca;
835851
bool Merged = false;
836-
// Try to find if the Alloca is not inferenced with any existing
852+
// Try to find if the Alloca does not interfere with any existing
837853
// NonOverlappedAllocaSet. If it is true, insert the alloca to that
838854
// NonOverlappedAllocaSet.
839855
for (auto &AllocaSet : NonOverlapedAllocas) {
840856
assert(!AllocaSet.empty() && "Processing Alloca Set is not empty.\n");
841-
bool NoInference = none_of(AllocaSet, [&](auto Iter) {
842-
return IsAllocaInferenre(Alloca, Iter);
857+
bool NoInterference = none_of(AllocaSet, [&](auto Iter) {
858+
return DoAllocasInterfere(Alloca, Iter);
843859
});
844860
// If the alignment of A is multiple of the alignment of B, the address
845861
// of A should satisfy the requirement for aligning for B.
@@ -852,7 +868,7 @@ void FrameTypeBuilder::addFieldForAllocas(const Function &F,
852868
return LargestAlloca->getAlign().value() % Alloca->getAlign().value() ==
853869
0;
854870
}();
855-
bool CouldMerge = NoInference && Alignable;
871+
bool CouldMerge = NoInterference && Alignable;
856872
if (!CouldMerge)
857873
continue;
858874
AllocaSet.push_back(Alloca);
@@ -1713,6 +1729,51 @@ static Instruction *splitBeforeCatchSwitch(CatchSwitchInst *CatchSwitch) {
17131729
return CleanupRet;
17141730
}
17151731

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

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

18441873
auto Index = FrameData.getFieldIndex(Def);
@@ -1980,7 +2009,7 @@ static void insertSpills(const FrameDataInfo &FrameData, coro::Shape &Shape) {
19802009
UsersToUpdate.clear();
19812010
for (User *U : Alloca->users()) {
19822011
auto *I = cast<Instruction>(U);
1983-
if (DT.dominates(CB, I))
2012+
if (DT.dominates(Shape.CoroBegin, I))
19842013
UsersToUpdate.push_back(I);
19852014
}
19862015
if (UsersToUpdate.empty())
@@ -2022,7 +2051,7 @@ static void insertSpills(const FrameDataInfo &FrameData, coro::Shape &Shape) {
20222051
Builder.CreateStore(Value, G);
20232052
}
20242053
// For each alias to Alloca created before CoroBegin but used after
2025-
// CoroBegin, we recreate them after CoroBegin by appplying the offset
2054+
// CoroBegin, we recreate them after CoroBegin by applying the offset
20262055
// to the pointer in the frame.
20272056
for (const auto &Alias : A.Aliases) {
20282057
auto *FramePtr = GetFramePointer(Alloca);
@@ -2031,7 +2060,7 @@ static void insertSpills(const FrameDataInfo &FrameData, coro::Shape &Shape) {
20312060
auto *AliasPtr =
20322061
Builder.CreatePtrAdd(FramePtr, ConstantInt::get(ITy, Value));
20332062
Alias.first->replaceUsesWithIf(
2034-
AliasPtr, [&](Use &U) { return DT.dominates(CB, U); });
2063+
AliasPtr, [&](Use &U) { return DT.dominates(Shape.CoroBegin, U); });
20352064
}
20362065
}
20372066

@@ -2044,7 +2073,7 @@ static void insertSpills(const FrameDataInfo &FrameData, coro::Shape &Shape) {
20442073
// If there is memory accessing to promise alloca before CoroBegin;
20452074
bool HasAccessingPromiseBeforeCB = llvm::any_of(PA->uses(), [&](Use &U) {
20462075
auto *Inst = dyn_cast<Instruction>(U.getUser());
2047-
if (!Inst || DT.dominates(CB, Inst))
2076+
if (!Inst || DT.dominates(Shape.CoroBegin, Inst))
20482077
return false;
20492078

20502079
if (auto *CI = dyn_cast<CallInst>(Inst)) {
@@ -2087,8 +2116,9 @@ static void movePHIValuesToInsertedBlock(BasicBlock *SuccBB,
20872116
do {
20882117
int Index = PN->getBasicBlockIndex(InsertedBB);
20892118
Value *V = PN->getIncomingValue(Index);
2090-
PHINode *InputV = PHINode::Create(
2091-
V->getType(), 1, V->getName() + Twine(".") + SuccBB->getName());
2119+
PHINode *InputV =
2120+
PHINode::Create(V->getType(), 1,
2121+
V->getName() + Twine(".") + getBasicBlockLabel(SuccBB));
20922122
InputV->insertBefore(InsertedBB->begin());
20932123
InputV->addIncoming(V, PredBB);
20942124
PN->setIncomingValue(Index, InputV);
@@ -2133,10 +2163,10 @@ static void rewritePHIsForCleanupPad(BasicBlock *CleanupPadBB,
21332163
Builder.CreateUnreachable();
21342164

21352165
// Create a new cleanuppad which will be the dispatcher.
2136-
auto *NewCleanupPadBB =
2137-
BasicBlock::Create(CleanupPadBB->getContext(),
2138-
CleanupPadBB->getName() + Twine(".corodispatch"),
2139-
CleanupPadBB->getParent(), CleanupPadBB);
2166+
auto *NewCleanupPadBB = BasicBlock::Create(
2167+
CleanupPadBB->getContext(),
2168+
getBasicBlockLabel(CleanupPadBB) + Twine(".corodispatch"),
2169+
CleanupPadBB->getParent(), CleanupPadBB);
21402170
Builder.SetInsertPoint(NewCleanupPadBB);
21412171
auto *SwitchType = Builder.getInt8Ty();
21422172
auto *SetDispatchValuePN =
@@ -2150,13 +2180,14 @@ static void rewritePHIsForCleanupPad(BasicBlock *CleanupPadBB,
21502180
SmallVector<BasicBlock *, 8> Preds(predecessors(CleanupPadBB));
21512181
for (BasicBlock *Pred : Preds) {
21522182
// Create a new cleanuppad and move the PHI values to there.
2153-
auto *CaseBB = BasicBlock::Create(CleanupPadBB->getContext(),
2154-
CleanupPadBB->getName() +
2155-
Twine(".from.") + Pred->getName(),
2156-
CleanupPadBB->getParent(), CleanupPadBB);
2183+
auto *CaseBB =
2184+
BasicBlock::Create(CleanupPadBB->getContext(),
2185+
getBasicBlockLabel(CleanupPadBB) + Twine(".from.") +
2186+
getBasicBlockLabel(Pred),
2187+
CleanupPadBB->getParent(), CleanupPadBB);
21572188
updatePhiNodes(CleanupPadBB, Pred, CaseBB);
2158-
CaseBB->setName(CleanupPadBB->getName() + Twine(".from.") +
2159-
Pred->getName());
2189+
CaseBB->setName(getBasicBlockLabel(CleanupPadBB) + Twine(".from.") +
2190+
getBasicBlockLabel(Pred));
21602191
Builder.SetInsertPoint(CaseBB);
21612192
Builder.CreateBr(CleanupPadBB);
21622193
movePHIValuesToInsertedBlock(CleanupPadBB, CaseBB, NewCleanupPadBB);
@@ -2246,7 +2277,8 @@ static void rewritePHIs(BasicBlock &BB) {
22462277
SmallVector<BasicBlock *, 8> Preds(predecessors(&BB));
22472278
for (BasicBlock *Pred : Preds) {
22482279
auto *IncomingBB = ehAwareSplitEdge(Pred, &BB, LandingPad, ReplPHI);
2249-
IncomingBB->setName(BB.getName() + Twine(".from.") + Pred->getName());
2280+
IncomingBB->setName(getBasicBlockLabel(&BB) + Twine(".from.") +
2281+
getBasicBlockLabel(Pred));
22502282

22512283
// Stop the moving of values at ReplPHI, as this is either null or the PHI
22522284
// that replaced the landing pad.
@@ -2690,7 +2722,7 @@ static void eliminateSwiftError(Function &F, coro::Shape &Shape) {
26902722
}
26912723
}
26922724

2693-
/// retcon and retcon.once conventions assume that all spill uses can be sunk
2725+
/// Async and Retcon{Once} conventions assume that all spill uses can be sunk
26942726
/// after the coro.begin intrinsic.
26952727
static void sinkSpillUsesAfterCoroBegin(Function &F,
26962728
const FrameDataInfo &FrameData,
@@ -3122,7 +3154,7 @@ void coro::buildCoroutineFrame(
31223154
cleanupSinglePredPHIs(F);
31233155

31243156
// Transforms multi-edge PHI Nodes, so that any value feeding into a PHI will
3125-
// never has its definition separated from the PHI by the suspend point.
3157+
// never have its definition separated from the PHI by the suspend point.
31263158
rewritePHIs(F);
31273159

31283160
// Build suspend crossing info.
@@ -3219,6 +3251,7 @@ void coro::buildCoroutineFrame(
32193251
Shape.FramePtr = Shape.CoroBegin;
32203252
// For now, this works for C++ programs only.
32213253
buildFrameDebugInfo(F, Shape, FrameData);
3254+
// Insert spills and reloads
32223255
insertSpills(FrameData, Shape);
32233256
lowerLocalAllocas(LocalAllocas, DeadInstructions);
32243257

0 commit comments

Comments
 (0)