109 changes: 76 additions & 33 deletions llvm/tools/llvm-reduce/deltas/ReduceBasicBlocks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,24 @@
#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 <vector>

#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<BasicBlock *> &BBsToKeep) {
const DenseSet<BasicBlock *> &BBsToDelete) {
auto *Term = BB.getTerminator();
std::vector<BasicBlock *> ChunkSuccessors;
for (auto *Succ : successors(&BB))
if (BBsToKeep.count(Succ))
for (auto *Succ : successors(&BB)) {
if (!BBsToDelete.count(Succ))
ChunkSuccessors.push_back(Succ);
}

// BB only references Chunk BBs
if (ChunkSuccessors.size() == Term->getNumSuccessors())
Expand All @@ -54,7 +60,7 @@ static void replaceBranchTerminator(BasicBlock &BB,
FI++;
while (FI != F.end()) {
auto &FIB = *FI;
if (BBsToKeep.count(&FIB) && !isa<PHINode>(FIB.begin())) {
if (!BBsToDelete.count(&FIB) && !isa<PHINode>(FIB.begin())) {
BranchInst::Create(&FIB, &BB);
return;
}
Expand Down Expand Up @@ -84,17 +90,17 @@ static void replaceBranchTerminator(BasicBlock &BB,
/// replace with something)
static void
removeUninterestingBBsFromSwitch(SwitchInst &SwInst,
const DenseSet<BasicBlock *> &BBsToKeep) {
const DenseSet<BasicBlock *> &BBsToDelete) {
for (int I = 0, E = SwInst.getNumCases(); I != E; ++I) {
auto Case = SwInst.case_begin() + I;
if (!BBsToKeep.count(Case->getCaseSuccessor())) {
if (BBsToDelete.count(Case->getCaseSuccessor())) {
SwInst.removeCase(Case);
--I;
--E;
}
}

if (!BBsToKeep.count(SwInst.getDefaultDest())) {
if (BBsToDelete.count(SwInst.getDefaultDest())) {
if (SwInst.getNumCases() == 0) {
auto *FnRetTy = SwInst.getParent()->getParent()->getReturnType();
Value *RetValue =
Expand All @@ -120,47 +126,84 @@ removeUninterestingBBsFromSwitch(SwitchInst &SwInst,
/// 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, Module &Program) {
DenseSet<BasicBlock *> BBsToKeep, BBsToDelete;
DenseSet<BasicBlock *> BBsToDelete;
df_iterator_default_set<BasicBlock *> Reachable;

for (auto &F : Program) {
if (F.empty())
continue;

// Never try to delete the entry block.
for (auto &BB : make_range(++F.begin(), F.end())) {
if (O.shouldKeep())
BBsToKeep.insert(&BB);
else
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 (auto &F : Program)
for (auto &BB : F) {
// Replace terminators that reference out-of-chunk BBs
for (BasicBlock &BB : F) {
if (auto *SwInst = dyn_cast<SwitchInst>(BB.getTerminator()))
removeUninterestingBBsFromSwitch(*SwInst, BBsToKeep);
removeUninterestingBBsFromSwitch(*SwInst, BBsToDelete);
else
replaceBranchTerminator(BB, BBsToKeep);
replaceBranchTerminator(BB, BBsToDelete);
}

// Remove out-of-chunk BB from successor phi nodes
for (auto &BB : BBsToDelete) {
for (auto *Succ : successors(BB))
Succ->removePredecessor(BB, /*KeepOneInputPHIs=*/true);
}

// Replace out-of-chunk switch uses
for (auto &BB : BBsToDelete) {
// Instructions might be referenced in other BBs
for (auto &I : *BB)
I.replaceAllUsesWith(getDefaultValue(I.getType()));
// Should not be completely removing the body of a function.
assert(BB->getParent()->size() > 1);
BB->eraseFromParent();
// 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, Module &M) {
std::vector<BasicBlock *> DeadBlocks;
df_iterator_default_set<BasicBlock *> Reachable;

for (Function &F : M) {
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");
}
3 changes: 2 additions & 1 deletion llvm/tools/llvm-reduce/deltas/ReduceBasicBlocks.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
//===----------------------------------------------------------------------===//
//
// This file implements a function which calls the Generic Delta pass in order
// to reduce uninteresting Arguments from defined functions.
// to reduce uninteresting BasicBlocks from defined functions.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_TOOLS_LLVM_REDUCE_DELTAS_REDUCEBASICBLOCKS_H
Expand All @@ -19,6 +19,7 @@

namespace llvm {
void reduceBasicBlocksDeltaPass(TestRunner &Test);
void reduceUnreachableBasicBlocksDeltaPass(TestRunner &Test);
} // namespace llvm

#endif