diff options
author | Gábor Spaits <gaborspaits1@gmail.com> | 2025-09-17 09:47:29 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2025-09-17 09:47:29 +0200 |
commit | 41cef78227eb909181cb9360099b2d92de8d649f (patch) | |
tree | a13478edd1743359fe1bf78ea1cb9bad4c6a267d /llvm/lib/Transforms/Utils/BasicBlockUtils.cpp | |
parent | ff05dc4526dce68f10450d77a1da2b8061e80dc2 (diff) | |
download | llvm-41cef78227eb909181cb9360099b2d92de8d649f.zip llvm-41cef78227eb909181cb9360099b2d92de8d649f.tar.gz llvm-41cef78227eb909181cb9360099b2d92de8d649f.tar.bz2 |
Reland "[BasicBlockUtils] Handle funclets when detaching EH pad blocks" (#158435)
When removing EH Pad blocks, the value defined by them becomes poison. These poison values are then used by `catchret` and `cleanupret`, which is invalid. This commit replaces those unreachable `catchret` and `cleanupret` instructions with `unreachable`.
Diffstat (limited to 'llvm/lib/Transforms/Utils/BasicBlockUtils.cpp')
-rw-r--r-- | llvm/lib/Transforms/Utils/BasicBlockUtils.cpp | 113 |
1 files changed, 85 insertions, 28 deletions
diff --git a/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp b/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp index cad0b4c..91e245e 100644 --- a/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp +++ b/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp @@ -58,37 +58,94 @@ static cl::opt<unsigned> MaxDeoptOrUnreachableSuccessorCheckDepth( "is followed by a block that either has a terminating " "deoptimizing call or is terminated with an unreachable")); -void llvm::detachDeadBlocks( - ArrayRef<BasicBlock *> BBs, - SmallVectorImpl<DominatorTree::UpdateType> *Updates, - bool KeepOneInputPHIs) { +/// Zap all the instructions in the block and replace them with an unreachable +/// instruction and notify the basic block's successors that one of their +/// predecessors is going away. +static void +emptyAndDetachBlock(BasicBlock *BB, + SmallVectorImpl<DominatorTree::UpdateType> *Updates, + bool KeepOneInputPHIs) { + // Loop through all of our successors and make sure they know that one + // of their predecessors is going away. + SmallPtrSet<BasicBlock *, 4> UniqueSuccessors; + for (BasicBlock *Succ : successors(BB)) { + Succ->removePredecessor(BB, KeepOneInputPHIs); + if (Updates && UniqueSuccessors.insert(Succ).second) + Updates->push_back({DominatorTree::Delete, BB, Succ}); + } + + // Zap all the instructions in the block. + while (!BB->empty()) { + Instruction &I = BB->back(); + // If this instruction is used, replace uses with an arbitrary value. + // Because control flow can't get here, we don't care what we replace the + // value with. Note that since this block is unreachable, and all values + // contained within it must dominate their uses, that all uses will + // eventually be removed (they are themselves dead). + if (!I.use_empty()) + I.replaceAllUsesWith(PoisonValue::get(I.getType())); + BB->back().eraseFromParent(); + } + new UnreachableInst(BB->getContext(), BB); + assert(BB->size() == 1 && isa<UnreachableInst>(BB->getTerminator()) && + "The successor list of BB isn't empty before " + "applying corresponding DTU updates."); +} + +void llvm::detachDeadBlocks(ArrayRef<BasicBlock *> BBs, + SmallVectorImpl<DominatorTree::UpdateType> *Updates, + bool KeepOneInputPHIs) { for (auto *BB : BBs) { - // Loop through all of our successors and make sure they know that one - // of their predecessors is going away. - SmallPtrSet<BasicBlock *, 4> UniqueSuccessors; - for (BasicBlock *Succ : successors(BB)) { - Succ->removePredecessor(BB, KeepOneInputPHIs); - if (Updates && UniqueSuccessors.insert(Succ).second) - Updates->push_back({DominatorTree::Delete, BB, Succ}); + auto NonFirstPhiIt = BB->getFirstNonPHIIt(); + if (NonFirstPhiIt != BB->end()) { + Instruction &I = *NonFirstPhiIt; + // Exception handling funclets need to be explicitly addressed. + // These funclets must begin with cleanuppad or catchpad and end with + // cleanupred or catchret. The return instructions can be in different + // basic blocks than the pad instruction. If we would only delete the + // first block, the we would have possible cleanupret and catchret + // instructions with poison arguments, which wouldn't be valid. + if (isa<FuncletPadInst>(I)) { + for (User *User : make_early_inc_range(I.users())) { + Instruction *ReturnInstr = dyn_cast<Instruction>(User); + // If we have a cleanupret or catchret block, replace it with just an + // unreachable. The other alternative, that may use a catchpad is a + // catchswitch. That does not need special handling for now. + if (isa<CatchReturnInst>(ReturnInstr) || + isa<CleanupReturnInst>(ReturnInstr)) { + BasicBlock *ReturnInstrBB = ReturnInstr->getParent(); + // This catchret or catchpad basic block is detached now. Let the + // successors know it. + // This basic block also may have some predecessors too. For + // example the following LLVM-IR is valid: + // + // [cleanuppad_block] + // | + // [regular_block] + // | + // [cleanupret_block] + // + // The IR after the cleanup will look like this: + // + // [cleanuppad_block] + // | + // [regular_block] + // | + // [unreachable] + // + // So regular_block will lead to an unreachable block, which is also + // valid. There is no need to replace regular_block with unreachable + // in this context now. + // On the other hand, the cleanupret/catchret block's successors + // need to know about the deletion of their predecessors. + emptyAndDetachBlock(ReturnInstrBB, Updates, KeepOneInputPHIs); + } + } + } } - // Zap all the instructions in the block. - while (!BB->empty()) { - Instruction &I = BB->back(); - // If this instruction is used, replace uses with an arbitrary value. - // Because control flow can't get here, we don't care what we replace the - // value with. Note that since this block is unreachable, and all values - // contained within it must dominate their uses, that all uses will - // eventually be removed (they are themselves dead). - if (!I.use_empty()) - I.replaceAllUsesWith(PoisonValue::get(I.getType())); - BB->back().eraseFromParent(); - } - new UnreachableInst(BB->getContext(), BB); - assert(BB->size() == 1 && - isa<UnreachableInst>(BB->getTerminator()) && - "The successor list of BB isn't empty before " - "applying corresponding DTU updates."); + // Detaching and emptying the current basic block. + emptyAndDetachBlock(BB, Updates, KeepOneInputPHIs); } } |