Merge empty landing pads in SimplifyCFG

This patch tries to merge duplicate landing pads when they branch to a common shared target.

Given IR that looks like this:
lpad1:
  %exn = landingpad {i8*, i32} personality i32 (...)* @__gxx_personality_v0
         cleanup
  br label %shared_resume
lpad2:
  %exn2 = landingpad {i8*, i32} personality i32 (...)* @__gxx_personality_v0
          cleanup
  br label %shared_resume
shared_resume:
  call void @fn()
  ret void
}

We can rewrite the users of both landing pad blocks to use one of them. This will generally allow the shared_resume block to be merged with the common landing pad as well.

Without this change, tail duplication would likely kick in - creating N (2 in this case) copies of the shared_resume basic block.

Differential Revision: http://reviews.llvm.org/D8297

llvm-svn: 233125
This commit is contained in:
Philip Reames 2015-03-24 22:28:45 +00:00
parent 14ebbc6d99
commit 2b969d7010
2 changed files with 195 additions and 0 deletions

View File

@ -4349,6 +4349,82 @@ bool SimplifyCFGOpt::SimplifyIndirectBr(IndirectBrInst *IBI) {
return Changed;
}
/// Given an block with only a single landing pad and a unconditional branch
/// try to find another basic block which this one can be merged with. This
/// handles cases where we have multiple invokes with unique landing pads, but
/// a shared handler.
///
/// We specifically choose to not worry about merging non-empty blocks
/// here. That is a PRE/scheduling problem and is best solved elsewhere. In
/// practice, the optimizer produces empty landing pad blocks quite frequently
/// when dealing with exception dense code. (see: instcombine, gvn, if-else
/// sinking in this file)
///
/// This is primarily a code size optimization. We need to avoid performing
/// any transform which might inhibit optimization (such as our ability to
/// specialize a particular handler via tail commoning). We do this by not
/// merging any blocks which require us to introduce a phi. Since the same
/// values are flowing through both blocks, we don't loose any ability to
/// specialize. If anything, we make such specialization more likely.
///
/// TODO - This transformation could remove entries from a phi in the target
/// block when the inputs in the phi are the same for the two blocks being
/// merged. In some cases, this could result in removal of the PHI entirely.
static bool TryToMergeLandingPad(LandingPadInst *LPad, BranchInst *BI,
BasicBlock *BB) {
auto Succ = BB->getUniqueSuccessor();
assert(Succ);
// If there's a phi in the successor block, we'd likely have to introduce
// a phi into the merged landing pad block.
if (isa<PHINode>(*Succ->begin()))
return false;
for (BasicBlock *OtherPred : predecessors(Succ)) {
if (BB == OtherPred)
continue;
BasicBlock::iterator I = OtherPred->begin();
LandingPadInst *LPad2 = dyn_cast<LandingPadInst>(I);
if (!LPad2 || !LPad2->isIdenticalTo(LPad))
continue;
for (++I; isa<DbgInfoIntrinsic>(I); ++I) {}
BranchInst *BI2 = dyn_cast<BranchInst>(I);
if (!BI2 || !BI2->isIdenticalTo(BI))
continue;
// We've found an identical block. Update our predeccessors to take that
// path instead and make ourselves dead.
SmallSet<BasicBlock *, 16> Preds;
Preds.insert(pred_begin(BB), pred_end(BB));
for (BasicBlock *Pred : Preds) {
InvokeInst *II = cast<InvokeInst>(Pred->getTerminator());
assert(II->getNormalDest() != BB &&
II->getUnwindDest() == BB && "unexpected successor");
II->setUnwindDest(OtherPred);
}
// The debug info in OtherPred doesn't cover the merged control flow that
// used to go through BB. We need to delete it or update it.
for (auto I = OtherPred->begin(), E = OtherPred->end();
I != E;) {
Instruction &Inst = *I; I++;
if (isa<DbgInfoIntrinsic>(Inst))
Inst.eraseFromParent();
}
SmallSet<BasicBlock *, 16> Succs;
Succs.insert(succ_begin(BB), succ_end(BB));
for (BasicBlock *Succ : Succs) {
Succ->removePredecessor(BB);
}
IRBuilder<> Builder(BI);
Builder.CreateUnreachable();
BI->eraseFromParent();
return true;
}
return false;
}
bool SimplifyCFGOpt::SimplifyUncondBranch(BranchInst *BI, IRBuilder<> &Builder){
BasicBlock *BB = BI->getParent();
@ -4373,6 +4449,15 @@ bool SimplifyCFGOpt::SimplifyUncondBranch(BranchInst *BI, IRBuilder<> &Builder){
return true;
}
// See if we can merge an empty landing pad block with another which is
// equivalent.
if (LandingPadInst *LPad = dyn_cast<LandingPadInst>(I)) {
for (++I; isa<DbgInfoIntrinsic>(I); ++I) {}
if (I->isTerminator() &&
TryToMergeLandingPad(LPad, BI, BB))
return true;
}
// If this basic block is ONLY a compare and a branch, and if a predecessor
// branches to us and our successor, fold the comparison into the
// predecessor and use logical operations to update the incoming value

View File

@ -0,0 +1,110 @@
; RUN: opt < %s -simplifycfg -S | FileCheck %s
target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128"
declare i32 @__gxx_personality_v0(...)
declare void @fn()
; CHECK-LABEL: @test1
define void @test1() {
entry:
; CHECK-LABEL: entry:
; CHECK: to label %invoke2 unwind label %lpad2
invoke void @fn()
to label %invoke2 unwind label %lpad1
invoke2:
; CHECK-LABEL: invoke2:
; CHECK: to label %invoke.cont unwind label %lpad2
invoke void @fn()
to label %invoke.cont unwind label %lpad2
invoke.cont:
ret void
lpad1:
%exn = landingpad {i8*, i32} personality i32 (...)* @__gxx_personality_v0
cleanup
br label %shared_resume
lpad2:
; CHECK-LABEL: lpad2:
; CHECK: landingpad { i8*, i32 } personality i32 (...)* @__gxx_personality_v0
; CHECK-NEXT: cleanup
; CHECK-NEXT: call void @fn()
; CHECK-NEXT: ret void
%exn2 = landingpad {i8*, i32} personality i32 (...)* @__gxx_personality_v0
cleanup
br label %shared_resume
shared_resume:
call void @fn()
ret void
}
; Don't trigger if blocks aren't the same/empty
define void @neg1() {
; CHECK-LABEL: @neg1
entry:
; CHECK-LABEL: entry:
; CHECK: to label %invoke2 unwind label %lpad1
invoke void @fn()
to label %invoke2 unwind label %lpad1
invoke2:
; CHECK-LABEL: invoke2:
; CHECK: to label %invoke.cont unwind label %lpad2
invoke void @fn()
to label %invoke.cont unwind label %lpad2
invoke.cont:
ret void
lpad1:
%exn = landingpad {i8*, i32} personality i32 (...)* @__gxx_personality_v0
filter [0 x i8*] zeroinitializer
call void @fn()
br label %shared_resume
lpad2:
%exn2 = landingpad {i8*, i32} personality i32 (...)* @__gxx_personality_v0
cleanup
br label %shared_resume
shared_resume:
call void @fn()
ret void
}
; Should not trigger when the landing pads are not the exact same
define void @neg2() {
; CHECK-LABEL: @neg2
entry:
; CHECK-LABEL: entry:
; CHECK: to label %invoke2 unwind label %lpad1
invoke void @fn()
to label %invoke2 unwind label %lpad1
invoke2:
; CHECK-LABEL: invoke2:
; CHECK: to label %invoke.cont unwind label %lpad2
invoke void @fn()
to label %invoke.cont unwind label %lpad2
invoke.cont:
ret void
lpad1:
%exn = landingpad {i8*, i32} personality i32 (...)* @__gxx_personality_v0
filter [0 x i8*] zeroinitializer
br label %shared_resume
lpad2:
%exn2 = landingpad {i8*, i32} personality i32 (...)* @__gxx_personality_v0
cleanup
br label %shared_resume
shared_resume:
call void @fn()
ret void
}