Skip to content

Commit 887bcb2

Browse files
committed
[Coroutines] fixed a bug when insertSpills performed spilling of CoroBegin on async-exception ready function, it created instruction dominance issues. the fix is to clone those bbs and keep unwinding path that happens before CoroBegin
1 parent 1d303ca commit 887bcb2

File tree

1 file changed

+156
-0
lines changed

1 file changed

+156
-0
lines changed

llvm/lib/Transforms/Coroutines/CoroFrame.cpp

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
#include "llvm/Transforms/Coroutines/SpillUtils.h"
3636
#include "llvm/Transforms/Coroutines/SuspendCrossingInfo.h"
3737
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
38+
#include "llvm/Transforms/Utils/Cloning.h"
3839
#include "llvm/Transforms/Utils/Local.h"
3940
#include "llvm/Transforms/Utils/PromoteMemToReg.h"
4041
#include <algorithm>
@@ -974,6 +975,159 @@ static StructType *buildFrameType(Function &F, coro::Shape &Shape,
974975
return FrameTy;
975976
}
976977

978+
// Fixer for the "Instruction does not dominate all uses!" bug
979+
// The fix consists of mapping problematic paths (where CoroBegin does not
980+
// dominate cleanup BBs) and clones them to 2 flows - the one that insertSpills
981+
// intended to create (using the spill) and another one, preserving the logics
982+
// of pre-splitting, which would be triggered if unwinding happened before
983+
// CoroBegin
984+
static void
985+
splitBasicBlocksNotDominatedByCoroBegin(const FrameDataInfo &FrameData,
986+
coro::Shape &Shape, Function *F,
987+
DominatorTree &DT) {
988+
ValueToValueMapTy VMap;
989+
DenseMap<BasicBlock *, BasicBlock *>
990+
ProcessedSpillBlockToAlternativeUnspilledBlockMap;
991+
bool FunctionHasSomeBlockNotDominatedByCoroBegin;
992+
SmallVector<BasicBlock *> SpillUserBlocks;
993+
994+
for (const auto &E : FrameData.Spills) {
995+
for (auto *U : E.second) {
996+
if (std::find(SpillUserBlocks.begin(), SpillUserBlocks.end(),
997+
U->getParent()) == SpillUserBlocks.end()) {
998+
SpillUserBlocks.push_back(U->getParent());
999+
}
1000+
}
1001+
}
1002+
SpillUserBlocks.push_back(Shape.AllocaSpillBlock);
1003+
1004+
do {
1005+
FunctionHasSomeBlockNotDominatedByCoroBegin = false;
1006+
// We want to traverse the function post-order (predecessors first),
1007+
// and check dominance starting CoroBegin
1008+
bool HaveTraversedCoroBegin = false;
1009+
for (BasicBlock *CurrentBlock : ReversePostOrderTraversal<Function *>(F)) {
1010+
if (!HaveTraversedCoroBegin &&
1011+
CurrentBlock != Shape.CoroBegin->getParent()) {
1012+
continue;
1013+
}
1014+
HaveTraversedCoroBegin = true;
1015+
1016+
// Focus on 2 types of users that produce errors - those in
1017+
// FrameData.Spills, and decendants of Shape.AllocaSpillBlocks
1018+
if (!DT.dominates(Shape.CoroBegin, CurrentBlock) &&
1019+
std::find(SpillUserBlocks.begin(), SpillUserBlocks.end(),
1020+
CurrentBlock) != SpillUserBlocks.end()) {
1021+
// Mark another iteration of the loop is needed, to verify that no more
1022+
// dominance issues after current run
1023+
FunctionHasSomeBlockNotDominatedByCoroBegin = true;
1024+
1025+
// Clone (preserve) the current basic block, before it will be modified
1026+
// by insertSpills
1027+
auto UnspilledAlternativeBlock =
1028+
CloneBasicBlock(CurrentBlock, VMap, ".unspilled_alternative", F);
1029+
1030+
// Remap the instructions, VMap here aggregates instructions across
1031+
// multiple BasicBlocks, and we assume that traversal is post-order,
1032+
// therefore successor blocks (for example instructions having funclet
1033+
// tags) will be mapped correctly to the new cloned cleanuppad
1034+
for (Instruction &I : *UnspilledAlternativeBlock) {
1035+
RemapInstruction(&I, VMap,
1036+
RF_NoModuleLevelChanges | RF_IgnoreMissingLocals);
1037+
}
1038+
1039+
// Keep track between the processed spill basic block and the cloned
1040+
// alternative unspilled basic block Will help us fix instructions that
1041+
// their context is complex (for example cleanuppad of funclet is
1042+
// defined in another BB)
1043+
ProcessedSpillBlockToAlternativeUnspilledBlockMap[CurrentBlock] =
1044+
UnspilledAlternativeBlock;
1045+
1046+
SmallVector<Instruction *> FixUpPredTerminators;
1047+
1048+
// Find the specific predecessors that does not dominated by
1049+
// Shape.CoroBegin We don't fix them here but later because it's not
1050+
// safe while using predecessors as iterator function
1051+
for (BasicBlock *Pred : predecessors(CurrentBlock)) {
1052+
if (!DT.dominates(Shape.CoroBegin, Pred)) {
1053+
FixUpPredTerminators.push_back(Pred->getTerminator());
1054+
}
1055+
}
1056+
1057+
// Fixups for current block terminator
1058+
const auto &CurrentBlockTerminator = CurrentBlock->getTerminator();
1059+
1060+
// If it's cleanupret, find the correspondant cleanuppad (use the map to
1061+
// find it)
1062+
if (auto CurrentBlockCleanupReturnTerminator =
1063+
dyn_cast<CleanupReturnInst>(CurrentBlockTerminator)) {
1064+
BasicBlock *CBCPBB =
1065+
CurrentBlockCleanupReturnTerminator->getCleanupPad()->getParent();
1066+
CleanupReturnInst *DBT = dyn_cast<CleanupReturnInst>(
1067+
UnspilledAlternativeBlock->getTerminator());
1068+
1069+
// Again assuming post-order traversal - if we mapped the predecessing
1070+
// cleanuppad block before, we should find it here If not, do nothing
1071+
if (ProcessedSpillBlockToAlternativeUnspilledBlockMap.contains(
1072+
CBCPBB)) {
1073+
Instruction *DCPr =
1074+
&ProcessedSpillBlockToAlternativeUnspilledBlockMap[CBCPBB]
1075+
->front();
1076+
CleanupPadInst *DCP = cast<CleanupPadInst>(DCPr);
1077+
DBT->setCleanupPad(DCP);
1078+
}
1079+
1080+
// If it's a branch/invoke, keep track of its successors, we want to
1081+
// calculate dominance between CoroBegin and them also They might need
1082+
// clone as well
1083+
} else if (auto CurrentBlockBranchTerminator =
1084+
dyn_cast<BranchInst>(CurrentBlockTerminator)) {
1085+
for (unsigned int successorIdx = 0;
1086+
successorIdx < CurrentBlockBranchTerminator->getNumSuccessors();
1087+
++successorIdx) {
1088+
SpillUserBlocks.push_back(
1089+
CurrentBlockBranchTerminator->getSuccessor(successorIdx));
1090+
}
1091+
} else if (auto CurrentBlockInvokeTerminator =
1092+
dyn_cast<InvokeInst>(CurrentBlockTerminator)) {
1093+
SpillUserBlocks.push_back(
1094+
CurrentBlockInvokeTerminator->getUnwindDest());
1095+
} else {
1096+
report_fatal_error("Not implemented terminator for this specific "
1097+
"instruction fixup in current block fixups");
1098+
}
1099+
1100+
// Fixups on the predecessors terminator - direct them to out untouched
1101+
// alternative block to break dominance error.
1102+
for (auto FixUpPredTerminator : FixUpPredTerminators) {
1103+
if (auto FixUpPredTerminatorInvoke =
1104+
dyn_cast<InvokeInst>(FixUpPredTerminator)) {
1105+
FixUpPredTerminatorInvoke->setUnwindDest(UnspilledAlternativeBlock);
1106+
} else if (auto FixUpPredTerminatorBranch =
1107+
dyn_cast<BranchInst>(FixUpPredTerminator)) {
1108+
for (unsigned int successorIdx = 0;
1109+
successorIdx < FixUpPredTerminatorBranch->getNumSuccessors();
1110+
++successorIdx) {
1111+
if (CurrentBlock ==
1112+
FixUpPredTerminatorBranch->getSuccessor(successorIdx)) {
1113+
FixUpPredTerminatorBranch->setSuccessor(
1114+
successorIdx, UnspilledAlternativeBlock);
1115+
}
1116+
}
1117+
} else {
1118+
report_fatal_error("Not implemented terminator for this specific "
1119+
"instruction in pred fixups");
1120+
}
1121+
}
1122+
1123+
// We changed dominance tree, so recalculate.
1124+
DT.recalculate(*F);
1125+
continue;
1126+
}
1127+
}
1128+
} while (FunctionHasSomeBlockNotDominatedByCoroBegin);
1129+
}
1130+
9771131
// Replace all alloca and SSA values that are accessed across suspend points
9781132
// with GetElementPointer from coroutine frame + loads and stores. Create an
9791133
// AllocaSpillBB that will become the new entry block for the resume parts of
@@ -1053,6 +1207,8 @@ static void insertSpills(const FrameDataInfo &FrameData, coro::Shape &Shape) {
10531207
return GEP;
10541208
};
10551209

1210+
splitBasicBlocksNotDominatedByCoroBegin(FrameData, Shape, F, DT);
1211+
10561212
for (auto const &E : FrameData.Spills) {
10571213
Value *Def = E.first;
10581214
auto SpillAlignment = Align(FrameData.getAlign(Def));

0 commit comments

Comments
 (0)