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/Target/TargetLowering.h"
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
#include "llvm/Transforms/Utils/PromoteMemToReg.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");
STATISTIC(NumStackTempsIntroduced, "Number of stack temporaries introduced");
namespace {
class DwarfEHPrepare : public FunctionPass {
const TargetLowering *TLI;
bool CompileFast;
Function *ExceptionValueIntrinsic;
Function *SelectorIntrinsic;
Constant *URoR;
GlobalVariable *EHCatchAllValue;
Constant *RewindFunction;
DominatorTree *DT;
DominanceFrontier *DF;
Function *F;
typedef SmallPtrSet<BasicBlock*, 8> BBSet;
BBSet LandingPads;
AllocaInst *ExceptionValueVar;
bool NormalizeLandingPads();
bool LowerUnwinds();
bool MoveExceptionValueCalls();
bool FinishStackTemporaries();
bool PromoteStackTemporaries();
Instruction *CreateExceptionValueCall(BasicBlock *BB);
Instruction *CreateValueLoad(BasicBlock *BB);
Instruction *CreateReadOfExceptionValue(BasicBlock *BB) {
return LandingPads.count(BB) ?
CreateExceptionValueCall(BB) : CreateValueLoad(BB);
}
bool CleanupSelectors();
bool HasCatchAllInSelector(IntrinsicInst *);
void FindAllCleanupSelectors(SmallPtrSet<IntrinsicInst*, 32> &Sels);
void FindAllURoRInvokes(SmallPtrSet<InvokeInst*, 32> &URoRInvokes);
bool HandleURoRInvokes();
bool FindSelectorAndURoR(Instruction *Inst, bool &URoRInvoke,
SmallPtrSet<IntrinsicInst*, 8> &SelCalls,
SmallPtrSet<PHINode*, 32> &SeenPHIs);
bool DoMem2RegPromotion(Value *V) {
AllocaInst *AI = dyn_cast<AllocaInst>(V);
if (!AI || !isAllocaPromotable(AI)) return false;
std::vector<AllocaInst*> Allocas(1, AI);
PromoteMemToReg(Allocas, *DT, *DF);
return true;
}
bool PromoteStoreInst(StoreInst *SI) {
if (!SI || !DT || !DF) return false;
if (DoMem2RegPromotion(SI->getOperand(1)))
return true;
return false;
}
bool PromoteEHPtrStore(IntrinsicInst *II) {
if (!DT || !DF) return false;
bool Changed = false;
StoreInst *SI;
while (1) {
SI = 0;
for (Value::use_iterator
I = II->use_begin(), E = II->use_end(); I != E; ++I) {
SI = dyn_cast<StoreInst>(I);
if (SI) break;
}
if (!PromoteStoreInst(SI))
break;
Changed = true;
}
return false;
}
public:
static char ID; DwarfEHPrepare(const TargetLowering *tli, bool fast) :
FunctionPass(&ID), TLI(tli), CompileFast(fast),
ExceptionValueIntrinsic(0), SelectorIntrinsic(0),
URoR(0), EHCatchAllValue(0), RewindFunction(0) {}
virtual bool runOnFunction(Function &Fn);
virtual void getAnalysisUsage(AnalysisUsage &AU) const {
if (!CompileFast)
AU.addRequired<DominatorTree>();
AU.addPreserved<DominatorTree>();
if (!CompileFast)
AU.addRequired<DominanceFrontier>();
AU.addPreserved<DominanceFrontier>();
}
const char *getPassName() const {
return "Exception handling preparation";
}
};
}
char DwarfEHPrepare::ID = 0;
FunctionPass *llvm::createDwarfEHPass(const TargetLowering *tli, bool fast) {
return new DwarfEHPrepare(tli, fast);
}
bool DwarfEHPrepare::HasCatchAllInSelector(IntrinsicInst *II) {
if (!EHCatchAllValue) return false;
unsigned OpIdx = II->getNumOperands() - 1;
GlobalVariable *GV = dyn_cast<GlobalVariable>(II->getOperand(OpIdx));
return GV == EHCatchAllValue;
}
void DwarfEHPrepare::
FindAllCleanupSelectors(SmallPtrSet<IntrinsicInst*, 32> &Sels) {
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);
}
}
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() {
if (!EHCatchAllValue) return false;
if (!SelectorIntrinsic) {
SelectorIntrinsic =
Intrinsic::getDeclaration(F->getParent(), Intrinsic::eh_selector);
if (!SelectorIntrinsic) return false;
}
bool Changed = false;
for (Value::use_iterator
I = SelectorIntrinsic->use_begin(),
E = SelectorIntrinsic->use_end(); I != E; ++I) {
IntrinsicInst *Sel = dyn_cast<IntrinsicInst>(I);
if (!Sel || Sel->getParent()->getParent() != F) continue;
unsigned OpIdx = Sel->getNumOperands() - 1;
GlobalVariable *GV = dyn_cast<GlobalVariable>(Sel->getOperand(OpIdx));
if (GV != EHCatchAllValue) continue;
Sel->setOperand(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;
restart:
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 (StoreInst *SI = dyn_cast<StoreInst>(II)) {
if (!PromoteStoreInst(SI)) continue;
Changed = true;
SeenPHIs.clear();
goto restart; } else if (PHINode *PN = dyn_cast<PHINode>(II)) {
if (SeenPHIs.insert(PN))
Changed |= FindSelectorAndURoR(PN, URoRInvoke, SelCalls, SeenPHIs);
}
}
return Changed;
}
bool DwarfEHPrepare::HandleURoRInvokes() {
if (!DT) return CleanupSelectors();
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;
}
if (!URoR) {
URoR = F->getParent()->getFunction("_Unwind_Resume_or_Rethrow");
if (!URoR) return CleanupSelectors();
}
SmallPtrSet<IntrinsicInst*, 32> Sels;
SmallPtrSet<InvokeInst*, 32> URoRInvokes;
FindAllCleanupSelectors(Sels);
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 (SelBB == URoRBB || 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();
}
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;
Changed |= PromoteEHPtrStore(EHPtr);
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;
SmallVector<Value*, 8> Args;
Args.push_back(II->getOperand(1)); Args.push_back(II->getOperand(2));
unsigned I = 3;
unsigned E = II->getNumOperands() -
(isa<ConstantInt>(II->getOperand(II->getNumOperands() - 1)) ? 1 : 0);
for (; I < E; ++I)
Args.push_back(II->getOperand(I));
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();
return Changed;
}
bool DwarfEHPrepare::NormalizeLandingPads() {
bool Changed = false;
const MCAsmInfo *MAI = TLI->getTargetMachine().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->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);
if (DT)
DT->splitBlock(NewBB);
if (DF)
DF->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,
CreateReadOfExceptionValue(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 (Function::iterator BB = F->begin(), E = F->end(); BB != E; ++BB) {
for (BasicBlock::iterator II = BB->begin(), E = BB->end(); II != E;)
if (IntrinsicInst *CI = dyn_cast<IntrinsicInst>(II++))
if (CI->getIntrinsicID() == Intrinsic::eh_exception) {
if (!CI->use_empty()) {
Value *ExceptionValue = CreateReadOfExceptionValue(BB);
if (CI == ExceptionValue) {
assert(LandingPads.count(BB) &&
"Created eh.exception call outside landing pad!");
continue;
}
CI->replaceAllUsesWith(ExceptionValue);
}
CI->eraseFromParent();
++NumExceptionValuesMoved;
Changed = true;
}
}
return Changed;
}
bool DwarfEHPrepare::FinishStackTemporaries() {
if (!ExceptionValueVar)
return false;
bool Changed = false;
for (BBSet::iterator LI = LandingPads.begin(), LE = LandingPads.end();
LI != LE; ++LI) {
Instruction *ExceptionValue = CreateReadOfExceptionValue(*LI);
Instruction *Store = new StoreInst(ExceptionValue, ExceptionValueVar);
Store->insertAfter(ExceptionValue);
Changed = true;
}
return Changed;
}
bool DwarfEHPrepare::PromoteStackTemporaries() {
if (ExceptionValueVar && DT && DF && isAllocaPromotable(ExceptionValueVar)) {
std::vector<AllocaInst*> Allocas(1, ExceptionValueVar);
PromoteMemToReg(Allocas, *DT, *DF);
return true;
}
return false;
}
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);
}
Instruction *DwarfEHPrepare::CreateValueLoad(BasicBlock *BB) {
Instruction *Start = BB->getFirstNonPHIOrDbg();
if (ExceptionValueVar)
if (LoadInst* LI = dyn_cast<LoadInst>(Start))
if (LI->getPointerOperand() == ExceptionValueVar)
return Start;
if (!ExceptionValueVar) {
ExceptionValueVar = new AllocaInst(PointerType::getUnqual(
Type::getInt8Ty(BB->getContext())), "eh.value", F->begin()->begin());
++NumStackTempsIntroduced;
}
return new LoadInst(ExceptionValueVar, "eh.value.load", Start);
}
bool DwarfEHPrepare::runOnFunction(Function &Fn) {
bool Changed = false;
DT = getAnalysisIfAvailable<DominatorTree>();
DF = getAnalysisIfAvailable<DominanceFrontier>();
ExceptionValueVar = 0;
F = &Fn;
Changed |= NormalizeLandingPads();
Changed |= LowerUnwinds();
Changed |= MoveExceptionValueCalls();
Changed |= FinishStackTemporaries();
if (!CompileFast)
Changed |= PromoteStackTemporaries();
Changed |= HandleURoRInvokes();
LandingPads.clear();
return Changed;
}