//===- ReduceBasicBlocks.cpp - Specialized Delta Pass ---------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file implements a function which calls the Generic Delta pass in order // to reduce uninteresting BasicBlocks from defined functions. // //===----------------------------------------------------------------------===// #include "ReduceBasicBlocks.h" #include "Utils.h" #include "llvm/ADT/DenseSet.h" #include "llvm/IR/BasicBlock.h" #include "llvm/IR/Constants.h" #include "llvm/IR/Instruction.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Value.h" #include "llvm/Support/Casting.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Transforms/Utils/BasicBlockUtils.h" #include "llvm/Transforms/Utils/Local.h" #include #define DEBUG_TYPE "llvm-reduce" using namespace llvm; /// Replaces BB Terminator with one that only contains Chunk BBs static void replaceBranchTerminator(BasicBlock &BB, const DenseSet &BBsToDelete) { auto *Term = BB.getTerminator(); std::vector ChunkSuccessors; for (auto *Succ : successors(&BB)) { if (!BBsToDelete.count(Succ)) ChunkSuccessors.push_back(Succ); } // BB only references Chunk BBs if (ChunkSuccessors.size() == Term->getNumSuccessors()) return; bool IsBranch = isa(Term); if (InvokeInst *Invoke = dyn_cast(Term)) { LandingPadInst *LP = Invoke->getLandingPadInst(); // Remove landingpad instruction if the containing block isn't used by other // invokes. if (none_of(LP->getParent()->users(), [Invoke](User *U) { return U != Invoke && isa(U); })) { LP->replaceAllUsesWith(getDefaultValue(LP->getType())); LP->eraseFromParent(); } else if (!ChunkSuccessors.empty() && ChunkSuccessors[0] == LP->getParent()) { // If the selected successor is the landing pad, clear the chunk // successors to avoid creating a regular branch to the landing pad which // would result in invalid IR. ChunkSuccessors.clear(); } IsBranch = true; } Value *Address = nullptr; if (auto *IndBI = dyn_cast(Term)) Address = IndBI->getAddress(); Term->replaceAllUsesWith(getDefaultValue(Term->getType())); Term->eraseFromParent(); if (ChunkSuccessors.empty()) { // If that fails then resort to replacing with a ret. auto *FnRetTy = BB.getParent()->getReturnType(); ReturnInst::Create(BB.getContext(), FnRetTy->isVoidTy() ? nullptr : getDefaultValue(FnRetTy), &BB); return; } if (IsBranch) BranchInst::Create(ChunkSuccessors[0], &BB); if (Address) { auto *NewIndBI = IndirectBrInst::Create(Address, ChunkSuccessors.size(), &BB); for (auto *Dest : ChunkSuccessors) NewIndBI->addDestination(Dest); } } /// Removes uninteresting BBs from switch, if the default case ends up being /// uninteresting, the switch is replaced with a void return (since it has to be /// replace with something) static void removeUninterestingBBsFromSwitch(SwitchInst &SwInst, const DenseSet &BBsToDelete) { for (int I = 0, E = SwInst.getNumCases(); I != E; ++I) { auto Case = SwInst.case_begin() + I; if (BBsToDelete.count(Case->getCaseSuccessor())) { SwInst.removeCase(Case); --I; --E; } } if (BBsToDelete.count(SwInst.getDefaultDest())) { if (SwInst.getNumCases() == 0) { auto *FnRetTy = SwInst.getParent()->getParent()->getReturnType(); Value *RetValue = FnRetTy->isVoidTy() ? nullptr : getDefaultValue(FnRetTy); ReturnInst::Create(SwInst.getContext(), RetValue, SwInst.getParent()); SwInst.eraseFromParent(); return; } // Replace the default dest with one of the other cases auto Case = SwInst.case_begin(); BasicBlock *NewDefault = Case->getCaseSuccessor(); SwInst.setDefaultDest(NewDefault); for (PHINode &SuccPHI : NewDefault->phis()) { SuccPHI.addIncoming(SuccPHI.getIncomingValueForBlock(SwInst.getParent()), SwInst.getParent()); } } } /// Removes out-of-chunk arguments from functions, and modifies their calls /// accordingly. It also removes allocations of out-of-chunk arguments. static void extractBasicBlocksFromModule(Oracle &O, ReducerWorkItem &WorkItem) { DenseSet BBsToDelete; df_iterator_default_set Reachable; for (auto &F : WorkItem.getModule()) { if (F.empty()) continue; BasicBlock &Entry = F.getEntryBlock(); for (auto *BB : depth_first_ext(&Entry, Reachable)) (void)BB; // Skip any function with unreachable blocks. It's somewhat difficult to // avoid producing invalid IR without deleting them. // // We also do not want to unconditionally delete them, as doing so would // break the invariant of changing the number of chunks during counting. const bool HasUnreachableBlocks = Reachable.size() != F.size(); Reachable.clear(); if (HasUnreachableBlocks) { LLVM_DEBUG(dbgs() << "Skipping function with unreachable blocks\n"); continue; } for (BasicBlock &BB : F) { if (&BB != &Entry && !O.shouldKeep()) BBsToDelete.insert(&BB); } // Replace terminators that reference out-of-chunk BBs for (BasicBlock &BB : F) { if (auto *SwInst = dyn_cast(BB.getTerminator())) removeUninterestingBBsFromSwitch(*SwInst, BBsToDelete); else replaceBranchTerminator(BB, BBsToDelete); } // Cleanup any blocks that are now dead after eliminating this set. This // will likely be larger than the number of blocks the oracle told us to // delete. EliminateUnreachableBlocks(F); BBsToDelete.clear(); } } void llvm::reduceBasicBlocksDeltaPass(TestRunner &Test) { runDeltaPass(Test, extractBasicBlocksFromModule, "Reducing Basic Blocks"); } static void removeUnreachableBasicBlocksFromModule(Oracle &O, ReducerWorkItem &WorkItem) { std::vector DeadBlocks; df_iterator_default_set Reachable; for (Function &F : WorkItem.getModule()) { if (F.empty()) continue; // Mark all reachable blocks. for (BasicBlock *BB : depth_first_ext(&F, Reachable)) (void)BB; if (Reachable.size() != F.size() && !O.shouldKeep()) { for (BasicBlock &BB : F) { if (!Reachable.count(&BB)) DeadBlocks.push_back(&BB); } // Delete the dead blocks. DeleteDeadBlocks(DeadBlocks, nullptr, /*KeepOneInputPHIs*/ false); DeadBlocks.clear(); } Reachable.clear(); } } void llvm::reduceUnreachableBasicBlocksDeltaPass(TestRunner &Test) { runDeltaPass(Test, removeUnreachableBasicBlocksFromModule, "Removing Unreachable Basic Blocks"); }