|
35 | 35 | #include "llvm/Transforms/Coroutines/SpillUtils.h" |
36 | 36 | #include "llvm/Transforms/Coroutines/SuspendCrossingInfo.h" |
37 | 37 | #include "llvm/Transforms/Utils/BasicBlockUtils.h" |
| 38 | +#include "llvm/Transforms/Utils/Cloning.h" |
38 | 39 | #include "llvm/Transforms/Utils/Local.h" |
39 | 40 | #include "llvm/Transforms/Utils/PromoteMemToReg.h" |
40 | 41 | #include <algorithm> |
@@ -974,6 +975,159 @@ static StructType *buildFrameType(Function &F, coro::Shape &Shape, |
974 | 975 | return FrameTy; |
975 | 976 | } |
976 | 977 |
|
| 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 | + |
977 | 1131 | // Replace all alloca and SSA values that are accessed across suspend points |
978 | 1132 | // with GetElementPointer from coroutine frame + loads and stores. Create an |
979 | 1133 | // 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) { |
1053 | 1207 | return GEP; |
1054 | 1208 | }; |
1055 | 1209 |
|
| 1210 | + splitBasicBlocksNotDominatedByCoroBegin(FrameData, Shape, F, DT); |
| 1211 | + |
1056 | 1212 | for (auto const &E : FrameData.Spills) { |
1057 | 1213 | Value *Def = E.first; |
1058 | 1214 | auto SpillAlignment = Align(FrameData.getAlign(Def)); |
|
0 commit comments