|
17 | 17 |
|
18 | 18 | #include "CoroInternal.h" |
19 | 19 | #include "llvm/ADT/ScopeExit.h" |
| 20 | +#include "llvm/ADT/SmallSet.h" |
20 | 21 | #include "llvm/ADT/SmallString.h" |
21 | 22 | #include "llvm/Analysis/StackLifetime.h" |
22 | 23 | #include "llvm/IR/DIBuilder.h" |
@@ -975,157 +976,176 @@ static StructType *buildFrameType(Function &F, coro::Shape &Shape, |
975 | 976 | return FrameTy; |
976 | 977 | } |
977 | 978 |
|
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); |
| 979 | +// This function assumes that it is being called on basic block in reversed |
| 980 | +// post-order, meaning predecessors are visited before successors failing to do |
| 981 | +// so will cause VMap to be non-valid and will cause instructions to fail |
| 982 | +// mapping to their corresponding clones |
| 983 | +static void finalizeBasicBlockCloneAndTrackSuccessors( |
| 984 | + BasicBlock *InitialBlock, BasicBlock *ClonedBlock, ValueToValueMapTy &VMap, |
| 985 | + SmallSet<BasicBlock *, 20> &SuccessorBlocksSet) { |
| 986 | + // This code will examine the basic block, fix issues caused by clones |
| 987 | + // for example - tailor cleanupret to the corresponding cleanuppad |
| 988 | + // it will use VMap to do so |
| 989 | + // in addition, it will add the node successors to SuccessorBlocksSet |
1029 | 990 |
|
1030 | 991 | // Remap the instructions, VMap here aggregates instructions across |
1031 | | - // multiple BasicBlocks, and we assume that traversal is post-order, |
| 992 | + // multiple BasicBlocks, and we assume that traversal is reversed post-order, |
1032 | 993 | // therefore successor blocks (for example instructions having funclet |
1033 | 994 | // tags) will be mapped correctly to the new cloned cleanuppad |
1034 | | - for (Instruction &I : *UnspilledAlternativeBlock) { |
1035 | | - RemapInstruction(&I, VMap, |
| 995 | + for (Instruction &ClonedBlockInstruction : *ClonedBlock) { |
| 996 | + RemapInstruction(&ClonedBlockInstruction, VMap, |
1036 | 997 | RF_NoModuleLevelChanges | RF_IgnoreMissingLocals); |
1037 | 998 | } |
1038 | 999 |
|
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 | | - } |
| 1000 | + const auto &InitialBlockTerminator = InitialBlock->getTerminator(); |
1056 | 1001 |
|
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 | | - } |
| 1002 | + // If it's cleanupret, find the correspondant cleanuppad (use the VMap to |
| 1003 | + // find it). |
| 1004 | + if (auto *InitialBlockTerminatorCleanupReturn = |
| 1005 | +dyn_cast<CleanupReturnInst>(InitialBlockTerminator)) { |
| 1006 | + // if none found do nothing |
| 1007 | + if (VMap.find(InitialBlockTerminatorCleanupReturn->getCleanupPad()) == |
| 1008 | + VMap.end()) { |
| 1009 | + return; |
| 1010 | + } |
1079 | 1011 |
|
| 1012 | + auto *ClonedBlockTerminatorCleanupReturn = |
| 1013 | +cast<CleanupReturnInst>(ClonedBlock->getTerminator()); |
| 1014 | + |
| 1015 | + // Assuming reversed post-order traversal |
| 1016 | + llvm::Value *ClonedBlockCleanupPadValue = |
| 1017 | + VMap[InitialBlockTerminatorCleanupReturn->getCleanupPad()]; |
| 1018 | + auto *ClonedBlockCleanupPad = |
| 1019 | +cast<CleanupPadInst>(ClonedBlockCleanupPadValue); |
| 1020 | + ClonedBlockTerminatorCleanupReturn->setCleanupPad(ClonedBlockCleanupPad); |
| 1021 | + |
1080 | 1022 | // 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)) { |
| 1023 | + // calculate dominance between CoroBegin and them also |
| 1024 | + } else if (auto *InitialBlockTerminatorBranch = |
| 1025 | +dyn_cast<BranchInst>(InitialBlockTerminator)) { |
1085 | 1026 | for (unsigned int successorIdx = 0; |
1086 | | -successorIdx < CurrentBlockBranchTerminator->getNumSuccessors(); |
| 1027 | +successorIdx < InitialBlockTerminatorBranch->getNumSuccessors(); |
1087 | 1028 | ++successorIdx) { |
1088 | | - SpillUserBlocks.push_back( |
1089 | | -CurrentBlockBranchTerminator->getSuccessor(successorIdx)); |
| 1029 | + SuccessorBlocksSet.insert( |
| 1030 | + InitialBlockTerminatorBranch->getSuccessor(successorIdx)); |
1090 | 1031 | } |
1091 | | - } else if (auto CurrentBlockInvokeTerminator = |
1092 | | -dyn_cast<InvokeInst>(CurrentBlockTerminator)) { |
1093 | | - SpillUserBlocks.push_back( |
1094 | | -CurrentBlockInvokeTerminator->getUnwindDest()); |
| 1032 | + } else if (auto *InitialBlockTerminatorInvoke = |
| 1033 | +dyn_cast<InvokeInst>(InitialBlockTerminator)) { |
| 1034 | + SuccessorBlocksSet.insert(InitialBlockTerminatorInvoke->getUnwindDest()); |
| 1035 | + } else if (isa<ReturnInst>(InitialBlockTerminator)) { |
| 1036 | + // No action needed |
1095 | 1037 | } else { |
1096 | | - report_fatal_error("Not implemented terminator for this specific " |
1097 | | - "instruction fixup in current block fixups"); |
1098 | | - } |
| 1038 | + InitialBlockTerminator->print(dbgs()); |
| 1039 | + report_fatal_error("Terminator is not implemented in " |
| 1040 | + "finalizeBasicBlockCloneAndTrackSuccessors"); |
| 1041 | + } |
| 1042 | +} |
1099 | 1043 |
|
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)) { |
| 1044 | +// Dominance issue fixer for each predecessor satisfying predicate function |
| 1045 | +void splitIfBasicBlockPredecessors( |
| 1046 | + BasicBlock *InitialBlock, BasicBlock *ReplacementBlock, |
| 1047 | + std::function<bool(BasicBlock *)> Predicate) { |
| 1048 | + |
| 1049 | + SmallVector<BasicBlock *> InitialBlockPredecessors; |
| 1050 | + |
| 1051 | + auto Predecessors = predecessors(InitialBlock); |
| 1052 | + std::copy_if(Predecessors.begin(), Predecessors.end(), |
| 1053 | + std::back_inserter(InitialBlockPredecessors), Predicate); |
| 1054 | + |
| 1055 | + // Fixups on the predecessors terminator - point them to ReplacementBlock. |
| 1056 | + for (auto *InitialBlockPredecessor : InitialBlockPredecessors) { |
| 1057 | + auto *InitialBlockPredecessorTerminator = |
| 1058 | + InitialBlockPredecessor->getTerminator(); |
| 1059 | + if (auto *InitialBlockPredecessorTerminatorInvoke = |
| 1060 | +dyn_cast<InvokeInst>(InitialBlockPredecessorTerminator)) { |
| 1061 | + if (InitialBlock == |
| 1062 | + InitialBlockPredecessorTerminatorInvoke->getUnwindDest()) { |
| 1063 | + InitialBlockPredecessorTerminatorInvoke->setUnwindDest( |
| 1064 | + ReplacementBlock); |
| 1065 | + } else { |
| 1066 | + InitialBlockPredecessorTerminatorInvoke->setNormalDest( |
| 1067 | + ReplacementBlock); |
| 1068 | + } |
| 1069 | + } else if (auto *InitialBlockPredecessorTerminatorBranch = |
| 1070 | +dyn_cast<BranchInst>(InitialBlockPredecessorTerminator)) { |
1108 | 1071 | for (unsigned int successorIdx = 0; |
1109 | | - successorIdx < FixUpPredTerminatorBranch->getNumSuccessors(); |
| 1072 | + successorIdx < |
| 1073 | + InitialBlockPredecessorTerminatorBranch->getNumSuccessors(); |
1110 | 1074 | ++successorIdx) { |
1111 | | - if (CurrentBlock == |
1112 | | -FixUpPredTerminatorBranch->getSuccessor(successorIdx)) { |
1113 | | - FixUpPredTerminatorBranch->setSuccessor( |
1114 | | -successorIdx, UnspilledAlternativeBlock); |
1115 | | - } |
1116 | | - } |
| 1075 | + if (InitialBlock == |
| 1076 | +InitialBlockPredecessorTerminatorBranch->getSuccessor( |
| 1077 | +successorIdx)) { |
| 1078 | + InitialBlockPredecessorTerminatorBranch->setSuccessor( |
| 1079 | +successorIdx, ReplacementBlock); |
| 1080 | + } |
| 1081 | + } |
| 1082 | + } else if (auto *InitialBlockPredecessorTerminatorCleanupReturn = |
| 1083 | + dyn_cast<CleanupReturnInst>( |
| 1084 | + InitialBlockPredecessorTerminator)) { |
| 1085 | + InitialBlockPredecessorTerminatorCleanupReturn->setUnwindDest( |
| 1086 | + ReplacementBlock); |
1117 | 1087 | } else { |
1118 | | - report_fatal_error("Not implemented terminator for this specific " |
1119 | | - "instruction in pred fixups"); |
| 1088 | + InitialBlockPredecessorTerminator->print(dbgs()); |
| 1089 | + report_fatal_error( |
| 1090 | + "Terminator is not implemented in splitIfBasicBlockPredecessors"); |
| 1091 | + } |
| 1092 | + } |
| 1093 | +} |
| 1094 | + |
| 1095 | +// Fixer for the "Instruction does not dominate all uses!" bug |
| 1096 | +// The fix consists of mapping problematic paths (where CoroBegin does not |
| 1097 | +// dominate cleanup nodes) and duplicates them to 2 flows - the one that |
| 1098 | +// insertSpills intended to create (using the spill) and another one, preserving |
| 1099 | +// the logics of pre-splitting, which would be triggered if unwinding happened |
| 1100 | +// before CoroBegin |
| 1101 | +static void |
| 1102 | +splitBasicBlocksNotDominatedByCoroBegin(const FrameDataInfo &FrameData, |
| 1103 | + const coro::Shape &Shape, Function *F, |
| 1104 | + DominatorTree &DT) { |
| 1105 | + ValueToValueMapTy VMap; |
| 1106 | + SmallSet<BasicBlock *, 20> SpillUserBlocksSet; |
| 1107 | + |
| 1108 | + // Prepare the node set, logics will be run only on those nodes |
| 1109 | + for (const auto &E : FrameData.Spills) { |
| 1110 | + for (auto *U : E.second) { |
| 1111 | + auto CurrentBlock = U->getParent(); |
| 1112 | + if (!DT.dominates(Shape.CoroBegin, CurrentBlock)) { |
| 1113 | + SpillUserBlocksSet.insert(CurrentBlock); |
| 1114 | + } |
1120 | 1115 | } |
1121 | 1116 | } |
1122 | 1117 |
|
1123 | | - // We changed dominance tree, so recalculate. |
1124 | | - DT.recalculate(*F); |
| 1118 | + // Run is in reversed post order, to enforce visiting predecessors before |
| 1119 | + // successors |
| 1120 | + for (BasicBlock *CurrentBlock : ReversePostOrderTraversal<Function *>(F)) { |
| 1121 | + if (!SpillUserBlocksSet.contains(CurrentBlock)) { |
1125 | 1122 | continue; |
1126 | 1123 | } |
1127 | | - } |
1128 | | - } while (FunctionHasSomeBlockNotDominatedByCoroBegin); |
| 1124 | + SpillUserBlocksSet.erase(CurrentBlock); |
| 1125 | + |
| 1126 | + // Preserve the current node. the duplicate will become the unspilled |
| 1127 | + // alternative |
| 1128 | + auto UnspilledAlternativeBlock = |
| 1129 | + CloneBasicBlock(CurrentBlock, VMap, ".unspilled_alternative", F); |
| 1130 | + |
| 1131 | + // Remap node instructions, keep track of successors to visit them in next |
| 1132 | + // iterations |
| 1133 | + finalizeBasicBlockCloneAndTrackSuccessors( |
| 1134 | + CurrentBlock, UnspilledAlternativeBlock, VMap, SpillUserBlocksSet); |
| 1135 | + |
| 1136 | + // Split only if predecessor breaks dominance against CoroBegin |
| 1137 | + splitIfBasicBlockPredecessors(CurrentBlock, UnspilledAlternativeBlock, |
| 1138 | + [&DT, &Shape](BasicBlock *PredecessorNode) { |
| 1139 | + return !DT.dominates(Shape.CoroBegin, |
| 1140 | + PredecessorNode); |
| 1141 | + }); |
| 1142 | + |
| 1143 | + // We changed dominance tree, so recalculate |
| 1144 | + DT.recalculate(*F); |
| 1145 | + } |
| 1146 | + |
| 1147 | + assert(SpillUserBlocksSet.empty() && |
| 1148 | + "Graph is corrupted by SpillUserBlocksSet"); |
1129 | 1149 | } |
1130 | 1150 |
|
1131 | 1151 | // Replace all alloca and SSA values that are accessed across suspend points |
|
0 commit comments