[WinEH] Permit branch folding in the face of funclets

Track which basic blocks belong to which funclets.  Permit branch
folding to fire but only if it can prove that doing so will not cause
code in one funclet to be reused in another.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@249257 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
David Majnemer 2015-10-04 02:22:52 +00:00
parent 9636baa291
commit 492f1cfa37
6 changed files with 157 additions and 87 deletions

View File

@ -15,6 +15,7 @@
#define LLVM_CODEGEN_ANALYSIS_H #define LLVM_CODEGEN_ANALYSIS_H
#include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/SmallVector.h" #include "llvm/ADT/SmallVector.h"
#include "llvm/CodeGen/ISDOpcodes.h" #include "llvm/CodeGen/ISDOpcodes.h"
#include "llvm/IR/CallSite.h" #include "llvm/IR/CallSite.h"
@ -23,6 +24,8 @@
namespace llvm { namespace llvm {
class GlobalValue; class GlobalValue;
class MachineBasicBlock;
class MachineFunction;
class TargetLoweringBase; class TargetLoweringBase;
class TargetLowering; class TargetLowering;
class TargetMachine; class TargetMachine;
@ -115,6 +118,9 @@ bool returnTypeIsEligibleForTailCall(const Function *F,
// or we are in LTO. // or we are in LTO.
bool canBeOmittedFromSymbolTable(const GlobalValue *GV); bool canBeOmittedFromSymbolTable(const GlobalValue *GV);
DenseMap<const MachineBasicBlock *, int>
getFuncletMembership(const MachineFunction &MF);
} // End llvm namespace } // End llvm namespace
#endif #endif

View File

@ -14,6 +14,7 @@
#include "llvm/CodeGen/Analysis.h" #include "llvm/CodeGen/Analysis.h"
#include "llvm/Analysis/ValueTracking.h" #include "llvm/Analysis/ValueTracking.h"
#include "llvm/CodeGen/MachineFunction.h" #include "llvm/CodeGen/MachineFunction.h"
#include "llvm/CodeGen/MachineModuleInfo.h"
#include "llvm/CodeGen/SelectionDAG.h" #include "llvm/CodeGen/SelectionDAG.h"
#include "llvm/IR/DataLayout.h" #include "llvm/IR/DataLayout.h"
#include "llvm/IR/DerivedTypes.h" #include "llvm/IR/DerivedTypes.h"
@ -25,6 +26,7 @@
#include "llvm/Support/ErrorHandling.h" #include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/MathExtras.h" #include "llvm/Support/MathExtras.h"
#include "llvm/Target/TargetLowering.h" #include "llvm/Target/TargetLowering.h"
#include "llvm/Target/TargetInstrInfo.h"
#include "llvm/Target/TargetSubtargetInfo.h" #include "llvm/Target/TargetSubtargetInfo.h"
#include "llvm/Transforms/Utils/GlobalStatus.h" #include "llvm/Transforms/Utils/GlobalStatus.h"
@ -643,3 +645,86 @@ bool llvm::canBeOmittedFromSymbolTable(const GlobalValue *GV) {
return !GS.IsCompared; return !GS.IsCompared;
} }
static void collectFuncletMembers(
DenseMap<const MachineBasicBlock *, int> &FuncletMembership, int Funclet,
const MachineBasicBlock *MBB) {
// Don't revisit blocks.
if (FuncletMembership.count(MBB) > 0) {
if (FuncletMembership[MBB] != Funclet) {
assert(false && "MBB is part of two funclets!");
report_fatal_error("MBB is part of two funclets!");
}
return;
}
// Add this MBB to our funclet.
FuncletMembership[MBB] = Funclet;
bool IsReturn = false;
int NumTerminators = 0;
for (const MachineInstr &MI : MBB->terminators()) {
IsReturn |= MI.isReturn();
++NumTerminators;
}
assert((!IsReturn || NumTerminators == 1) &&
"Expected only one terminator when a return is present!");
// Returns are boundaries where funclet transfer can occur, don't follow
// successors.
if (IsReturn)
return;
for (const MachineBasicBlock *SMBB : MBB->successors())
if (!SMBB->isEHPad())
collectFuncletMembers(FuncletMembership, Funclet, SMBB);
}
DenseMap<const MachineBasicBlock *, int>
llvm::getFuncletMembership(const MachineFunction &MF) {
DenseMap<const MachineBasicBlock *, int> FuncletMembership;
// We don't have anything to do if there aren't any EH pads.
if (!MF.getMMI().hasEHFunclets())
return FuncletMembership;
bool IsSEH = isAsynchronousEHPersonality(
classifyEHPersonality(MF.getFunction()->getPersonalityFn()));
const TargetInstrInfo *TII = MF.getSubtarget().getInstrInfo();
SmallVector<const MachineBasicBlock *, 16> FuncletBlocks;
SmallVector<std::pair<const MachineBasicBlock *, int>, 16> CatchRetSuccessors;
for (const MachineBasicBlock &MBB : MF) {
if (MBB.isEHFuncletEntry())
FuncletBlocks.push_back(&MBB);
MachineBasicBlock::const_iterator MBBI = MBB.getFirstTerminator();
// CatchPads are not funclets for SEH so do not consider CatchRet to
// transfer control to another funclet.
if (IsSEH || MBBI->getOpcode() != TII->getCatchReturnOpcode())
continue;
const MachineBasicBlock *Successor = MBBI->getOperand(0).getMBB();
const MachineBasicBlock *SuccessorColor = MBBI->getOperand(1).getMBB();
CatchRetSuccessors.push_back({Successor, SuccessorColor->getNumber()});
}
// We don't have anything to do if there aren't any EH pads.
if (FuncletBlocks.empty())
return FuncletMembership;
// Identify all the basic blocks reachable from the function entry.
collectFuncletMembers(FuncletMembership, MF.front().getNumber(), MF.begin());
// Next, identify all the blocks inside the funclets.
for (const MachineBasicBlock *MBB : FuncletBlocks)
collectFuncletMembers(FuncletMembership, MBB->getNumber(), MBB);
// Finally, identify all the targets of a catchret.
for (std::pair<const MachineBasicBlock *, int> CatchRetPair :
CatchRetSuccessors)
collectFuncletMembers(FuncletMembership, CatchRetPair.second,
CatchRetPair.first);
// All blocks not part of a funclet are in the parent function.
for (const MachineBasicBlock &MBB : MF)
FuncletMembership.insert({&MBB, MF.front().getNumber()});
return FuncletMembership;
}

View File

@ -21,6 +21,7 @@
#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallSet.h" #include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/Statistic.h" #include "llvm/ADT/Statistic.h"
#include "llvm/CodeGen/Analysis.h"
#include "llvm/CodeGen/MachineBlockFrequencyInfo.h" #include "llvm/CodeGen/MachineBlockFrequencyInfo.h"
#include "llvm/CodeGen/MachineBranchProbabilityInfo.h" #include "llvm/CodeGen/MachineBranchProbabilityInfo.h"
#include "llvm/CodeGen/MachineFunctionPass.h" #include "llvm/CodeGen/MachineFunctionPass.h"
@ -94,10 +95,8 @@ bool BranchFolderPass::runOnMachineFunction(MachineFunction &MF) {
TargetPassConfig *PassConfig = &getAnalysis<TargetPassConfig>(); TargetPassConfig *PassConfig = &getAnalysis<TargetPassConfig>();
// TailMerge can create jump into if branches that make CFG irreducible for // TailMerge can create jump into if branches that make CFG irreducible for
// HW that requires structurized CFG. It can also cause BBs to get shared // HW that requires structurized CFG.
// between funclets.
bool EnableTailMerge = !MF.getTarget().requiresStructuredCFG() && bool EnableTailMerge = !MF.getTarget().requiresStructuredCFG() &&
!MF.getMMI().hasEHFunclets() &&
PassConfig->getEnableTailMerge(); PassConfig->getEnableTailMerge();
BranchFolder Folder(EnableTailMerge, /*CommonHoist=*/true, BranchFolder Folder(EnableTailMerge, /*CommonHoist=*/true,
getAnalysis<MachineBlockFrequencyInfo>(), getAnalysis<MachineBlockFrequencyInfo>(),
@ -135,6 +134,7 @@ void BranchFolder::RemoveDeadBlock(MachineBasicBlock *MBB) {
// Remove the block. // Remove the block.
MF->erase(MBB); MF->erase(MBB);
FuncletMembership.erase(MBB);
} }
/// OptimizeImpDefsBlock - If a basic block is just a bunch of implicit_def /// OptimizeImpDefsBlock - If a basic block is just a bunch of implicit_def
@ -222,6 +222,9 @@ bool BranchFolder::OptimizeFunction(MachineFunction &MF,
MadeChange |= OptimizeImpDefsBlock(&MBB); MadeChange |= OptimizeImpDefsBlock(&MBB);
} }
// Recalculate funclet membership.
FuncletMembership = getFuncletMembership(MF);
bool MadeChangeThisIteration = true; bool MadeChangeThisIteration = true;
while (MadeChangeThisIteration) { while (MadeChangeThisIteration) {
MadeChangeThisIteration = TailMergeBlocks(MF); MadeChangeThisIteration = TailMergeBlocks(MF);
@ -448,6 +451,11 @@ MachineBasicBlock *BranchFolder::SplitMBBAt(MachineBasicBlock &CurMBB,
// For targets that use the register scavenger, we must maintain LiveIns. // For targets that use the register scavenger, we must maintain LiveIns.
MaintainLiveIns(&CurMBB, NewMBB); MaintainLiveIns(&CurMBB, NewMBB);
// Add the new block to the funclet.
const auto &FuncletI = FuncletMembership.find(&CurMBB);
if (FuncletI != FuncletMembership.end())
FuncletMembership[NewMBB] = FuncletI->second;
return NewMBB; return NewMBB;
} }
@ -552,14 +560,23 @@ static unsigned CountTerminators(MachineBasicBlock *MBB,
/// and decide if it would be profitable to merge those tails. Return the /// and decide if it would be profitable to merge those tails. Return the
/// length of the common tail and iterators to the first common instruction /// length of the common tail and iterators to the first common instruction
/// in each block. /// in each block.
static bool ProfitableToMerge(MachineBasicBlock *MBB1, static bool
MachineBasicBlock *MBB2, ProfitableToMerge(MachineBasicBlock *MBB1, MachineBasicBlock *MBB2,
unsigned minCommonTailLength, unsigned minCommonTailLength, unsigned &CommonTailLen,
unsigned &CommonTailLen, MachineBasicBlock::iterator &I1,
MachineBasicBlock::iterator &I1, MachineBasicBlock::iterator &I2, MachineBasicBlock *SuccBB,
MachineBasicBlock::iterator &I2, MachineBasicBlock *PredBB,
MachineBasicBlock *SuccBB, DenseMap<const MachineBasicBlock *, int> &FuncletMembership) {
MachineBasicBlock *PredBB) { // It is never profitable to tail-merge blocks from two different funclets.
if (!FuncletMembership.empty()) {
auto Funclet1 = FuncletMembership.find(MBB1);
assert(Funclet1 != FuncletMembership.end());
auto Funclet2 = FuncletMembership.find(MBB2);
assert(Funclet2 != FuncletMembership.end());
if (Funclet1->second != Funclet2->second)
return false;
}
CommonTailLen = ComputeCommonTailLength(MBB1, MBB2, I1, I2); CommonTailLen = ComputeCommonTailLength(MBB1, MBB2, I1, I2);
if (CommonTailLen == 0) if (CommonTailLen == 0)
return false; return false;
@ -636,7 +653,8 @@ unsigned BranchFolder::ComputeSameTails(unsigned CurHash,
if (ProfitableToMerge(CurMPIter->getBlock(), I->getBlock(), if (ProfitableToMerge(CurMPIter->getBlock(), I->getBlock(),
minCommonTailLength, minCommonTailLength,
CommonTailLen, TrialBBI1, TrialBBI2, CommonTailLen, TrialBBI1, TrialBBI2,
SuccBB, PredBB)) { SuccBB, PredBB,
FuncletMembership)) {
if (CommonTailLen > maxCommonTailLength) { if (CommonTailLen > maxCommonTailLength) {
SameTails.clear(); SameTails.clear();
maxCommonTailLength = CommonTailLen; maxCommonTailLength = CommonTailLen;
@ -1099,6 +1117,8 @@ bool BranchFolder::OptimizeBranches(MachineFunction &MF) {
// Make sure blocks are numbered in order // Make sure blocks are numbered in order
MF.RenumberBlocks(); MF.RenumberBlocks();
// Renumbering blocks alters funclet membership, recalculate it.
FuncletMembership = getFuncletMembership(MF);
for (MachineFunction::iterator I = std::next(MF.begin()), E = MF.end(); for (MachineFunction::iterator I = std::next(MF.begin()), E = MF.end();
I != E; ) { I != E; ) {
@ -1112,6 +1132,7 @@ bool BranchFolder::OptimizeBranches(MachineFunction &MF) {
++NumDeadBlocks; ++NumDeadBlocks;
} }
} }
return MadeChange; return MadeChange;
} }
@ -1171,11 +1192,22 @@ ReoptimizeBlock:
MachineFunction::iterator FallThrough = MBB; MachineFunction::iterator FallThrough = MBB;
++FallThrough; ++FallThrough;
// Make sure MBB and FallThrough belong to the same funclet.
bool SameFunclet = true;
if (!FuncletMembership.empty() && FallThrough != MF.end()) {
auto MBBFunclet = FuncletMembership.find(MBB);
assert(MBBFunclet != FuncletMembership.end());
auto FallThroughFunclet = FuncletMembership.find(FallThrough);
assert(FallThroughFunclet != FuncletMembership.end());
SameFunclet = MBBFunclet->second == FallThroughFunclet->second;
}
// If this block is empty, make everyone use its fall-through, not the block // If this block is empty, make everyone use its fall-through, not the block
// explicitly. Landing pads should not do this since the landing-pad table // explicitly. Landing pads should not do this since the landing-pad table
// points to this block. Blocks with their addresses taken shouldn't be // points to this block. Blocks with their addresses taken shouldn't be
// optimized away. // optimized away.
if (IsEmptyBlock(MBB) && !MBB->isEHPad() && !MBB->hasAddressTaken()) { if (IsEmptyBlock(MBB) && !MBB->isEHPad() && !MBB->hasAddressTaken() &&
SameFunclet) {
// Dead block? Leave for cleanup later. // Dead block? Leave for cleanup later.
if (MBB->pred_empty()) return MadeChange; if (MBB->pred_empty()) return MadeChange;

View File

@ -54,6 +54,7 @@ namespace llvm {
typedef std::vector<MergePotentialsElt>::iterator MPIterator; typedef std::vector<MergePotentialsElt>::iterator MPIterator;
std::vector<MergePotentialsElt> MergePotentials; std::vector<MergePotentialsElt> MergePotentials;
SmallPtrSet<const MachineBasicBlock*, 2> TriedMerging; SmallPtrSet<const MachineBasicBlock*, 2> TriedMerging;
DenseMap<const MachineBasicBlock *, int> FuncletMembership;
class SameTailElt { class SameTailElt {
MPIterator MPIter; MPIterator MPIter;

View File

@ -12,12 +12,9 @@
// //
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
#include "llvm/CodeGen/Passes.h" #include "llvm/CodeGen/Passes.h"
#include "llvm/CodeGen/MachineBasicBlock.h" #include "llvm/CodeGen/Analysis.h"
#include "llvm/CodeGen/MachineFunction.h" #include "llvm/CodeGen/MachineFunction.h"
#include "llvm/CodeGen/MachineFunctionPass.h" #include "llvm/CodeGen/MachineFunctionPass.h"
#include "llvm/CodeGen/MachineModuleInfo.h"
#include "llvm/Target/TargetInstrInfo.h"
#include "llvm/Target/TargetSubtargetInfo.h"
using namespace llvm; using namespace llvm;
#define DEBUG_TYPE "funclet-layout" #define DEBUG_TYPE "funclet-layout"
@ -34,81 +31,17 @@ public:
}; };
} }
static void
collectFuncletMembers(DenseMap<MachineBasicBlock *, int> &FuncletMembership,
int Funclet, MachineBasicBlock *MBB) {
// Don't revisit blocks.
if (FuncletMembership.count(MBB) > 0) {
if (FuncletMembership[MBB] != Funclet) {
assert(false && "MBB is part of two funclets!");
report_fatal_error("MBB is part of two funclets!");
}
return;
}
// Add this MBB to our funclet.
FuncletMembership[MBB] = Funclet;
bool IsReturn = false;
int NumTerminators = 0;
for (MachineInstr &MI : MBB->terminators()) {
IsReturn |= MI.isReturn();
++NumTerminators;
}
assert((!IsReturn || NumTerminators == 1) &&
"Expected only one terminator when a return is present!");
// Returns are boundaries where funclet transfer can occur, don't follow
// successors.
if (IsReturn)
return;
for (MachineBasicBlock *SMBB : MBB->successors())
if (!SMBB->isEHPad())
collectFuncletMembers(FuncletMembership, Funclet, SMBB);
}
char FuncletLayout::ID = 0; char FuncletLayout::ID = 0;
char &llvm::FuncletLayoutID = FuncletLayout::ID; char &llvm::FuncletLayoutID = FuncletLayout::ID;
INITIALIZE_PASS(FuncletLayout, "funclet-layout", INITIALIZE_PASS(FuncletLayout, "funclet-layout",
"Contiguously Lay Out Funclets", false, false) "Contiguously Lay Out Funclets", false, false)
bool FuncletLayout::runOnMachineFunction(MachineFunction &F) { bool FuncletLayout::runOnMachineFunction(MachineFunction &F) {
// We don't have anything to do if there aren't any EH pads. DenseMap<const MachineBasicBlock *, int> FuncletMembership =
if (!F.getMMI().hasEHFunclets()) getFuncletMembership(F);
if (FuncletMembership.empty())
return false; return false;
const TargetInstrInfo *TII = F.getSubtarget().getInstrInfo();
SmallVector<MachineBasicBlock *, 16> FuncletBlocks;
SmallVector<std::pair<MachineBasicBlock *, int>, 16> CatchRetSuccessors;
for (MachineBasicBlock &MBB : F) {
if (MBB.isEHFuncletEntry())
FuncletBlocks.push_back(&MBB);
MachineBasicBlock::iterator MBBI = MBB.getFirstTerminator();
if (MBBI->getOpcode() != TII->getCatchReturnOpcode())
continue;
MachineBasicBlock *Successor = MBBI->getOperand(0).getMBB();
MachineBasicBlock *SuccessorColor = MBBI->getOperand(1).getMBB();
CatchRetSuccessors.push_back({Successor, SuccessorColor->getNumber()});
}
// We don't have anything to do if there aren't any EH pads.
if (FuncletBlocks.empty())
return false;
DenseMap<MachineBasicBlock *, int> FuncletMembership;
// Identify all the basic blocks reachable from the function entry.
collectFuncletMembers(FuncletMembership, F.front().getNumber(), F.begin());
// Next, identify all the blocks inside the funclets.
for (MachineBasicBlock *MBB : FuncletBlocks)
collectFuncletMembers(FuncletMembership, MBB->getNumber(), MBB);
// Finally, identify all the targets of a catchret.
for (std::pair<MachineBasicBlock *, int> CatchRetPair : CatchRetSuccessors)
collectFuncletMembers(FuncletMembership, CatchRetPair.second,
CatchRetPair.first);
F.sort([&](MachineBasicBlock &x, MachineBasicBlock &y) { F.sort([&](MachineBasicBlock &x, MachineBasicBlock &y) {
return FuncletMembership[&x] < FuncletMembership[&y]; return FuncletMembership[&x] < FuncletMembership[&y];
}); });

View File

@ -103,7 +103,7 @@ unreachable: ; preds = %catch, %entry
; CHECK: retq ; CHECK: retq
define void @test3() #0 personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) { define void @test3(i1 %V) #0 personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) {
entry: entry:
invoke void @g() invoke void @g()
to label %try.cont unwind label %catch.dispatch to label %try.cont unwind label %catch.dispatch
@ -128,7 +128,15 @@ catchendblock: ; preds = %catch.dispatch.1
catchendpad unwind to caller catchendpad unwind to caller
try.cont: ; preds = %entry try.cont: ; preds = %entry
ret void br i1 %V, label %exit_one, label %exit_two
exit_one:
tail call void @exit(i32 0)
unreachable
exit_two:
tail call void @exit(i32 0)
unreachable
} }
; CHECK-LABEL: test3: ; CHECK-LABEL: test3:
@ -136,15 +144,20 @@ try.cont: ; preds = %entry
; The entry funclet contains %entry and %try.cont ; The entry funclet contains %entry and %try.cont
; CHECK: # %entry ; CHECK: # %entry
; CHECK: # %try.cont ; CHECK: # %try.cont
; CHECK: retq ; CHECK: callq exit
; CHECK-NOT: # exit_one
; CHECK-NOT: # exit_two
; CHECK: ud2
; The catch(int) funclet contains %catch.2 ; The catch(int) funclet contains %catch.2
; CHECK: # %catch.2 ; CHECK: # %catch.2
; CHECK: callq exit ; CHECK: callq exit
; CHECK: ud2
; The catch(...) funclet contains %catch ; The catch(...) funclet contains %catch
; CHECK: # %catch{{$}} ; CHECK: # %catch{{$}}
; CHECK: callq exit ; CHECK: callq exit
; CHECK: ud2
declare void @exit(i32) noreturn nounwind declare void @exit(i32) noreturn nounwind
declare void @_CxxThrowException(i8*, %eh.ThrowInfo*) declare void @_CxxThrowException(i8*, %eh.ThrowInfo*)