DwarfEHPrepare.cpp [plain text]
#define DEBUG_TYPE "dwarfehprepare"
#include "llvm/Function.h"
#include "llvm/Instructions.h"
#include "llvm/IntrinsicInst.h"
#include "llvm/Module.h"
#include "llvm/Pass.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/Analysis/Dominators.h"
#include "llvm/CodeGen/Passes.h"
#include "llvm/MC/MCAsmInfo.h"
#include "llvm/Support/CallSite.h"
#include "llvm/Target/TargetLowering.h"
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
#include "llvm/Transforms/Utils/SSAUpdater.h"
using namespace llvm;
STATISTIC(NumLandingPadsSplit, "Number of landing pads split");
STATISTIC(NumUnwindsLowered, "Number of unwind instructions lowered");
STATISTIC(NumExceptionValuesMoved, "Number of eh.exception calls moved");
namespace {
class DwarfEHPrepare : public FunctionPass {
const TargetMachine *TM;
const TargetLowering *TLI;
Function *ExceptionValueIntrinsic;
Function *SelectorIntrinsic;
Constant *URoR;
GlobalVariable *EHCatchAllValue;
Constant *RewindFunction;
DominatorTree *DT;
Function *F;
typedef SmallPtrSet<BasicBlock*, 8> BBSet;
BBSet LandingPads;
bool NormalizeLandingPads();
bool LowerUnwinds();
bool MoveExceptionValueCalls();
Instruction *CreateExceptionValueCall(BasicBlock *BB);
bool CleanupSelectors(SmallPtrSet<IntrinsicInst*, 32> &Sels);
bool HasCatchAllInSelector(IntrinsicInst *);
void FindAllCleanupSelectors(SmallPtrSet<IntrinsicInst*, 32> &Sels,
SmallPtrSet<IntrinsicInst*, 32> &CatchAllSels);
void FindAllURoRInvokes(SmallPtrSet<InvokeInst*, 32> &URoRInvokes);
bool HandleURoRInvokes();
bool FindSelectorAndURoR(Instruction *Inst, bool &URoRInvoke,
SmallPtrSet<IntrinsicInst*, 8> &SelCalls,
SmallPtrSet<PHINode*, 32> &SeenPHIs);
public:
static char ID; DwarfEHPrepare(const TargetMachine *tm) :
FunctionPass(ID), TM(tm), TLI(TM->getTargetLowering()),
ExceptionValueIntrinsic(0), SelectorIntrinsic(0),
URoR(0), EHCatchAllValue(0), RewindFunction(0) {
initializeDominatorTreePass(*PassRegistry::getPassRegistry());
}
virtual bool runOnFunction(Function &Fn);
virtual void getAnalysisUsage(AnalysisUsage &AU) const {
AU.addRequired<DominatorTree>();
AU.addPreserved<DominatorTree>();
}
const char *getPassName() const {
return "Exception handling preparation";
}
};
}
char DwarfEHPrepare::ID = 0;
FunctionPass *llvm::createDwarfEHPass(const TargetMachine *tm) {
return new DwarfEHPrepare(tm);
}
bool DwarfEHPrepare::HasCatchAllInSelector(IntrinsicInst *II) {
if (!EHCatchAllValue) return false;
unsigned ArgIdx = II->getNumArgOperands() - 1;
GlobalVariable *GV = dyn_cast<GlobalVariable>(II->getArgOperand(ArgIdx));
return GV == EHCatchAllValue;
}
void DwarfEHPrepare::
FindAllCleanupSelectors(SmallPtrSet<IntrinsicInst*, 32> &Sels,
SmallPtrSet<IntrinsicInst*, 32> &CatchAllSels) {
for (Value::use_iterator
I = SelectorIntrinsic->use_begin(),
E = SelectorIntrinsic->use_end(); I != E; ++I) {
IntrinsicInst *II = cast<IntrinsicInst>(*I);
if (II->getParent()->getParent() != F)
continue;
if (!HasCatchAllInSelector(II))
Sels.insert(II);
else
CatchAllSels.insert(II);
}
}
void DwarfEHPrepare::
FindAllURoRInvokes(SmallPtrSet<InvokeInst*, 32> &URoRInvokes) {
for (Value::use_iterator
I = URoR->use_begin(),
E = URoR->use_end(); I != E; ++I) {
if (InvokeInst *II = dyn_cast<InvokeInst>(*I))
URoRInvokes.insert(II);
}
}
bool DwarfEHPrepare::CleanupSelectors(SmallPtrSet<IntrinsicInst*, 32> &Sels) {
if (!EHCatchAllValue) return false;
if (!SelectorIntrinsic) {
SelectorIntrinsic =
Intrinsic::getDeclaration(F->getParent(), Intrinsic::eh_selector);
if (!SelectorIntrinsic) return false;
}
bool Changed = false;
for (SmallPtrSet<IntrinsicInst*, 32>::iterator
I = Sels.begin(), E = Sels.end(); I != E; ++I) {
IntrinsicInst *Sel = *I;
unsigned OpIdx = Sel->getNumArgOperands() - 1;
GlobalVariable *GV = dyn_cast<GlobalVariable>(Sel->getArgOperand(OpIdx));
if (GV != EHCatchAllValue) continue;
Sel->setArgOperand(OpIdx, EHCatchAllValue->getInitializer());
Changed = true;
}
return Changed;
}
bool
DwarfEHPrepare::FindSelectorAndURoR(Instruction *Inst, bool &URoRInvoke,
SmallPtrSet<IntrinsicInst*, 8> &SelCalls,
SmallPtrSet<PHINode*, 32> &SeenPHIs) {
bool Changed = false;
for (Value::use_iterator
I = Inst->use_begin(), E = Inst->use_end(); I != E; ++I) {
Instruction *II = dyn_cast<Instruction>(*I);
if (!II || II->getParent()->getParent() != F) continue;
if (IntrinsicInst *Sel = dyn_cast<IntrinsicInst>(II)) {
if (Sel->getIntrinsicID() == Intrinsic::eh_selector)
SelCalls.insert(Sel);
} else if (InvokeInst *Invoke = dyn_cast<InvokeInst>(II)) {
if (Invoke->getCalledFunction() == URoR)
URoRInvoke = true;
} else if (CastInst *CI = dyn_cast<CastInst>(II)) {
Changed |= FindSelectorAndURoR(CI, URoRInvoke, SelCalls, SeenPHIs);
} else if (PHINode *PN = dyn_cast<PHINode>(II)) {
if (SeenPHIs.insert(PN))
Changed |= FindSelectorAndURoR(PN, URoRInvoke, SelCalls, SeenPHIs);
}
}
return Changed;
}
bool DwarfEHPrepare::HandleURoRInvokes() {
if (!EHCatchAllValue) {
EHCatchAllValue =
F->getParent()->getNamedGlobal("llvm.eh.catch.all.value");
if (!EHCatchAllValue) return false;
}
if (!SelectorIntrinsic) {
SelectorIntrinsic =
Intrinsic::getDeclaration(F->getParent(), Intrinsic::eh_selector);
if (!SelectorIntrinsic) return false;
}
SmallPtrSet<IntrinsicInst*, 32> Sels;
SmallPtrSet<IntrinsicInst*, 32> CatchAllSels;
FindAllCleanupSelectors(Sels, CatchAllSels);
if (!URoR) {
URoR = F->getParent()->getFunction("_Unwind_Resume_or_Rethrow");
if (!URoR) {
URoR = F->getParent()->getFunction("_Unwind_SjLj_Resume");
if (!URoR) return CleanupSelectors(CatchAllSels);
}
}
SmallPtrSet<InvokeInst*, 32> URoRInvokes;
FindAllURoRInvokes(URoRInvokes);
SmallPtrSet<IntrinsicInst*, 32> SelsToConvert;
for (SmallPtrSet<IntrinsicInst*, 32>::iterator
SI = Sels.begin(), SE = Sels.end(); SI != SE; ++SI) {
const BasicBlock *SelBB = (*SI)->getParent();
for (SmallPtrSet<InvokeInst*, 32>::iterator
UI = URoRInvokes.begin(), UE = URoRInvokes.end(); UI != UE; ++UI) {
const BasicBlock *URoRBB = (*UI)->getParent();
if (DT->dominates(SelBB, URoRBB)) {
SelsToConvert.insert(*SI);
break;
}
}
}
bool Changed = false;
if (Sels.size() != SelsToConvert.size()) {
if (!ExceptionValueIntrinsic) {
ExceptionValueIntrinsic =
Intrinsic::getDeclaration(F->getParent(), Intrinsic::eh_exception);
if (!ExceptionValueIntrinsic)
return CleanupSelectors(CatchAllSels);
}
for (Value::use_iterator
I = ExceptionValueIntrinsic->use_begin(),
E = ExceptionValueIntrinsic->use_end(); I != E; ++I) {
IntrinsicInst *EHPtr = dyn_cast<IntrinsicInst>(*I);
if (!EHPtr || EHPtr->getParent()->getParent() != F) continue;
bool URoRInvoke = false;
SmallPtrSet<IntrinsicInst*, 8> SelCalls;
SmallPtrSet<PHINode*, 32> SeenPHIs;
Changed |= FindSelectorAndURoR(EHPtr, URoRInvoke, SelCalls, SeenPHIs);
if (URoRInvoke) {
for (SmallPtrSet<IntrinsicInst*, 8>::iterator
SI = SelCalls.begin(), SE = SelCalls.end(); SI != SE; ++SI)
if (!HasCatchAllInSelector(*SI))
SelsToConvert.insert(*SI);
}
}
}
if (!SelsToConvert.empty()) {
Changed = true;
for (SmallPtrSet<IntrinsicInst*, 8>::iterator
SI = SelsToConvert.begin(), SE = SelsToConvert.end();
SI != SE; ++SI) {
IntrinsicInst *II = *SI;
CallSite CS(II);
IntrinsicInst::op_iterator I = CS.arg_begin();
IntrinsicInst::op_iterator E = CS.arg_end();
IntrinsicInst::op_iterator B = prior(E);
if (isa<ConstantInt>(B)) E = B;
SmallVector<Value*, 8> Args(I, E);
Args.push_back(EHCatchAllValue->getInitializer());
CallInst *NewSelector =
CallInst::Create(SelectorIntrinsic, Args.begin(), Args.end(),
"eh.sel.catch.all", II);
NewSelector->setTailCall(II->isTailCall());
NewSelector->setAttributes(II->getAttributes());
NewSelector->setCallingConv(II->getCallingConv());
II->replaceAllUsesWith(NewSelector);
II->eraseFromParent();
}
}
Changed |= CleanupSelectors(CatchAllSels);
return Changed;
}
bool DwarfEHPrepare::NormalizeLandingPads() {
bool Changed = false;
const MCAsmInfo *MAI = TM->getMCAsmInfo();
bool usingSjLjEH = MAI->getExceptionHandlingType() == ExceptionHandling::SjLj;
for (Function::iterator I = F->begin(), E = F->end(); I != E; ++I) {
TerminatorInst *TI = I->getTerminator();
if (!isa<InvokeInst>(TI))
continue;
BasicBlock *LPad = TI->getSuccessor(1);
if (LandingPads.count(LPad))
continue;
bool OnlyUnwoundTo = true;
bool SwitchOK = usingSjLjEH;
for (pred_iterator PI = pred_begin(LPad), PE = pred_end(LPad);
PI != PE; ++PI) {
TerminatorInst *PT = (*PI)->getTerminator();
if (SwitchOK && isa<SwitchInst>(PT)) {
SwitchOK = false;
continue;
}
if (!isa<InvokeInst>(PT) || LPad == PT->getSuccessor(0)) {
OnlyUnwoundTo = false;
break;
}
}
if (OnlyUnwoundTo) {
LandingPads.insert(LPad);
continue;
}
BasicBlock *NewBB = BasicBlock::Create(F->getContext(),
LPad->getName() + "_unwind_edge");
LPad->getParent()->getBasicBlockList().insert(LPad, NewBB);
for (pred_iterator PI = pred_begin(LPad), PE = pred_end(LPad); PI != PE; ) {
TerminatorInst *PT = (*PI++)->getTerminator();
if (isa<InvokeInst>(PT) && PT->getSuccessor(1) == LPad)
PT->setSuccessor(1, NewBB);
}
for (BasicBlock::iterator II = LPad->begin(); isa<PHINode>(II); ++II) {
PHINode *PN = cast<PHINode>(II);
pred_iterator PB = pred_begin(NewBB), PE = pred_end(NewBB);
Value *InVal = PN->getIncomingValueForBlock(*PB);
for (pred_iterator PI = PB; PI != PE; ++PI) {
if (PI != PB && InVal != PN->getIncomingValueForBlock(*PI)) {
InVal = 0;
break;
}
}
if (InVal == 0) {
PHINode *NewPN = PHINode::Create(PN->getType(),
PN->getNumIncomingValues(),
PN->getName()+".unwind", NewBB);
for (pred_iterator PI = PB; PI != PE; ++PI)
NewPN->addIncoming(PN->getIncomingValueForBlock(*PI), *PI);
InVal = NewPN;
}
for (pred_iterator PI = PB; PI != PE; ++PI)
PN->removeIncomingValue(*PI);
PN->addIncoming(InVal, NewBB);
}
BranchInst::Create(LPad, NewBB);
DT->splitBlock(NewBB);
LandingPads.insert(NewBB);
++NumLandingPadsSplit;
Changed = true;
}
return Changed;
}
bool DwarfEHPrepare::LowerUnwinds() {
SmallVector<TerminatorInst*, 16> UnwindInsts;
for (Function::iterator I = F->begin(), E = F->end(); I != E; ++I) {
TerminatorInst *TI = I->getTerminator();
if (isa<UnwindInst>(TI))
UnwindInsts.push_back(TI);
}
if (UnwindInsts.empty()) return false;
if (!RewindFunction) {
LLVMContext &Ctx = UnwindInsts[0]->getContext();
std::vector<const Type*>
Params(1, Type::getInt8PtrTy(Ctx));
FunctionType *FTy = FunctionType::get(Type::getVoidTy(Ctx),
Params, false);
const char *RewindName = TLI->getLibcallName(RTLIB::UNWIND_RESUME);
RewindFunction = F->getParent()->getOrInsertFunction(RewindName, FTy);
}
bool Changed = false;
for (SmallVectorImpl<TerminatorInst*>::iterator
I = UnwindInsts.begin(), E = UnwindInsts.end(); I != E; ++I) {
TerminatorInst *TI = *I;
CallInst *CI = CallInst::Create(RewindFunction,
CreateExceptionValueCall(TI->getParent()),
"", TI);
CI->setCallingConv(TLI->getLibcallCallingConv(RTLIB::UNWIND_RESUME));
new UnreachableInst(TI->getContext(), TI);
TI->eraseFromParent();
++NumUnwindsLowered;
Changed = true;
}
return Changed;
}
bool DwarfEHPrepare::MoveExceptionValueCalls() {
if (!ExceptionValueIntrinsic &&
!F->getParent()->getFunction(Intrinsic::getName(Intrinsic::eh_exception)))
return false;
bool Changed = false;
for (BBSet::const_iterator LI = LandingPads.begin(), LE = LandingPads.end();
LI != LE; ++LI) {
BasicBlock *LP = *LI;
for (BasicBlock::iterator II = LP->getFirstNonPHIOrDbg(), IE = LP->end();
II != IE;)
if (EHExceptionInst *EI = dyn_cast<EHExceptionInst>(II++)) {
if (!EI->use_empty()) {
Value *CallAtStart = CreateExceptionValueCall(LP);
if (EI == CallAtStart)
continue;
EI->replaceAllUsesWith(CallAtStart);
}
EI->eraseFromParent();
++NumExceptionValuesMoved;
Changed = true;
}
}
SSAUpdater SSA;
bool FoundCallOutsideLandingPad = false;
Function::iterator BB = F->begin();
for (Function::iterator BE = F->end(); BB != BE; ++BB) {
if (LandingPads.count(BB))
continue;
for (BasicBlock::iterator II = BB->getFirstNonPHIOrDbg(), IE = BB->end();
II != IE; ++II)
if (isa<EHExceptionInst>(II)) {
SSA.Initialize(II->getType(), II->getName());
FoundCallOutsideLandingPad = true;
break;
}
if (FoundCallOutsideLandingPad)
break;
}
if (!FoundCallOutsideLandingPad)
return Changed;
for (BBSet::iterator LI = LandingPads.begin(), LE = LandingPads.end();
LI != LE; ++LI)
SSA.AddAvailableValue(*LI, CreateExceptionValueCall(*LI));
for (Function::iterator BE = F->end(); BB != BE; ++BB) {
if (LandingPads.count(BB))
continue;
for (BasicBlock::iterator II = BB->getFirstNonPHIOrDbg(), IE = BB->end();
II != IE;)
if (EHExceptionInst *EI = dyn_cast<EHExceptionInst>(II++)) {
EI->replaceAllUsesWith(SSA.GetValueAtEndOfBlock(BB));
EI->eraseFromParent();
++NumExceptionValuesMoved;
}
}
return true;
}
Instruction *DwarfEHPrepare::CreateExceptionValueCall(BasicBlock *BB) {
Instruction *Start = BB->getFirstNonPHIOrDbg();
if (IntrinsicInst *CI = dyn_cast<IntrinsicInst>(Start))
if (CI->getIntrinsicID() == Intrinsic::eh_exception)
return Start;
if (!ExceptionValueIntrinsic)
ExceptionValueIntrinsic = Intrinsic::getDeclaration(F->getParent(),
Intrinsic::eh_exception);
return CallInst::Create(ExceptionValueIntrinsic, "eh.value.call", Start);
}
bool DwarfEHPrepare::runOnFunction(Function &Fn) {
bool Changed = false;
DT = &getAnalysis<DominatorTree>();
F = &Fn;
Changed |= NormalizeLandingPads();
Changed |= LowerUnwinds();
Changed |= MoveExceptionValueCalls();
Changed |= HandleURoRInvokes();
LandingPads.clear();
return Changed;
}