diff options
Diffstat (limited to 'lib/CodeGen/DwarfEHPrepare.cpp')
-rw-r--r-- | lib/CodeGen/DwarfEHPrepare.cpp | 322 |
1 files changed, 315 insertions, 7 deletions
diff --git a/lib/CodeGen/DwarfEHPrepare.cpp b/lib/CodeGen/DwarfEHPrepare.cpp index 39fc85e..8bae9ed 100644 --- a/lib/CodeGen/DwarfEHPrepare.cpp +++ b/lib/CodeGen/DwarfEHPrepare.cpp @@ -8,19 +8,19 @@ //===----------------------------------------------------------------------===// // // This pass mulches exception handling code into a form adapted to code -// generation. Required if using dwarf exception handling. +// generation. Required if using dwarf exception handling. // //===----------------------------------------------------------------------===// #define DEBUG_TYPE "dwarfehprepare" -#include "llvm/ADT/Statistic.h" -#include "llvm/Analysis/Dominators.h" -#include "llvm/CodeGen/Passes.h" #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" @@ -40,6 +40,15 @@ namespace { // The eh.exception intrinsic. Function *ExceptionValueIntrinsic; + // The eh.selector intrinsic. + Function *SelectorIntrinsic; + + // _Unwind_Resume_or_Rethrow call. + Constant *URoR; + + // The EH language-specific catch-all type. + GlobalVariable *EHCatchAllValue; + // _Unwind_Resume or the target equivalent. Constant *RewindFunction; @@ -67,18 +76,88 @@ namespace { Instruction *CreateValueLoad(BasicBlock *BB); /// CreateReadOfExceptionValue - Return the result of the eh.exception - /// intrinsic by calling the intrinsic if in a landing pad, or loading - /// it from the exception value variable otherwise. + /// intrinsic by calling the intrinsic if in a landing pad, or loading it + /// from the exception value variable otherwise. Instruction *CreateReadOfExceptionValue(BasicBlock *BB) { return LandingPads.count(BB) ? CreateExceptionValueCall(BB) : CreateValueLoad(BB); } + /// CleanupSelectors - Any remaining eh.selector intrinsic calls which still + /// use the ".llvm.eh.catch.all.value" call need to convert to using it's + /// initializer instead. + bool CleanupSelectors(); + + /// FindAllCleanupSelectors - Find all eh.selector calls that are clean-ups. + void FindAllCleanupSelectors(SmallPtrSet<IntrinsicInst*, 32> &Sels); + + /// FindAllURoRInvokes - Find all URoR invokes in the function. + void FindAllURoRInvokes(SmallPtrSet<InvokeInst*, 32> &URoRInvokes); + + /// HandleURoRInvokes - Handle invokes of "_Unwind_Resume_or_Rethrow" + /// calls. The "unwind" part of these invokes jump to a landing pad within + /// the current function. This is a candidate to merge the selector + /// associated with the URoR invoke with the one from the URoR's landing + /// pad. + bool HandleURoRInvokes(); + + /// FindSelectorAndURoR - Find the eh.selector call and URoR call associated + /// with the eh.exception call. This recursively looks past instructions + /// which don't change the EH pointer value, like casts or PHI nodes. + bool FindSelectorAndURoR(Instruction *Inst, bool &URoRInvoke, + SmallPtrSet<IntrinsicInst*, 8> &SelCalls); + + /// DoMem2RegPromotion - Take an alloca call and promote it from memory to a + /// register. + bool DoMem2RegPromotion(Value *V) { + AllocaInst *AI = dyn_cast<AllocaInst>(V); + if (!AI || !isAllocaPromotable(AI)) return false; + + // Turn the alloca into a register. + std::vector<AllocaInst*> Allocas(1, AI); + PromoteMemToReg(Allocas, *DT, *DF); + return true; + } + + /// PromoteStoreInst - Perform Mem2Reg on a StoreInst. + bool PromoteStoreInst(StoreInst *SI) { + if (!SI || !DT || !DF) return false; + if (DoMem2RegPromotion(SI->getOperand(1))) + return true; + return false; + } + + /// PromoteEHPtrStore - Promote the storing of an EH pointer into a + /// register. This should get rid of the store and subsequent loads. + 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; // Pass identification, replacement for typeid. DwarfEHPrepare(const TargetLowering *tli, bool fast) : FunctionPass(&ID), TLI(tli), CompileFast(fast), - ExceptionValueIntrinsic(0), RewindFunction(0) {} + ExceptionValueIntrinsic(0), SelectorIntrinsic(0), + URoR(0), EHCatchAllValue(0), RewindFunction(0) {} virtual bool runOnFunction(Function &Fn); @@ -105,6 +184,233 @@ FunctionPass *llvm::createDwarfEHPass(const TargetLowering *tli, bool fast) { return new DwarfEHPrepare(tli, fast); } +/// FindAllCleanupSelectors - Find all eh.selector calls that are clean-ups. +void DwarfEHPrepare:: +FindAllCleanupSelectors(SmallPtrSet<IntrinsicInst*, 32> &Sels) { + for (Value::use_iterator + I = SelectorIntrinsic->use_begin(), + E = SelectorIntrinsic->use_end(); I != E; ++I) { + IntrinsicInst *SI = cast<IntrinsicInst>(I); + if (!SI || SI->getParent()->getParent() != F) continue; + + unsigned NumOps = SI->getNumOperands(); + if (NumOps > 4) continue; + bool IsCleanUp = (NumOps == 3); + + if (!IsCleanUp) + if (ConstantInt *CI = dyn_cast<ConstantInt>(SI->getOperand(3))) + IsCleanUp = (CI->getZExtValue() == 0); + + if (IsCleanUp) + Sels.insert(SI); + } +} + +/// FindAllURoRInvokes - Find all URoR invokes in the function. +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); + } +} + +/// CleanupSelectors - Any remaining eh.selector intrinsic calls which still use +/// the ".llvm.eh.catch.all.value" call need to convert to using it's +/// initializer instead. +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; + + // Index of the ".llvm.eh.catch.all.value" variable. + 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; +} + +/// FindSelectorAndURoR - Find the eh.selector call associated with the +/// eh.exception call. And indicate if there is a URoR "invoke" associated with +/// the eh.exception call. This recursively looks past instructions which don't +/// change the EH pointer value, like casts or PHI nodes. +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); + } else if (StoreInst *SI = dyn_cast<StoreInst>(II)) { + if (!PromoteStoreInst(SI)) continue; + Changed = true; + SeenPHIs.clear(); + goto restart; // Uses may have changed, restart loop. + } else if (PHINode *PN = dyn_cast<PHINode>(II)) { + if (SeenPHIs.insert(PN)) + // Don't process a PHI node more than once. + Changed |= FindSelectorAndURoR(PN, URoRInvoke, SelCalls); + } + } + + return Changed; +} + +/// HandleURoRInvokes - Handle invokes of "_Unwind_Resume_or_Rethrow" calls. The +/// "unwind" part of these invokes jump to a landing pad within the current +/// function. This is a candidate to merge the selector associated with the URoR +/// invoke with the one from the URoR's landing pad. +bool DwarfEHPrepare::HandleURoRInvokes() { + if (!DT) return CleanupSelectors(); // We require DominatorTree information. + + 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 we haven't been able to convert all of the clean-up selectors, then + // loop through the slow way to see if they still need to be converted. + 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; + Changed |= FindSelectorAndURoR(EHPtr, URoRInvoke, SelCalls); + + if (URoRInvoke) { + // This EH pointer is being used by an invoke of an URoR instruction and + // an eh.selector intrinsic call. If the eh.selector is a 'clean-up', we + // need to convert it to a 'catch-all'. + for (SmallPtrSet<IntrinsicInst*, 8>::iterator + SI = SelCalls.begin(), SE = SelCalls.end(); SI != SE; ++SI) { + IntrinsicInst *II = *SI; + unsigned NumOps = II->getNumOperands(); + + if (NumOps <= 4) { + bool IsCleanUp = (NumOps == 3); + + if (!IsCleanUp) + if (ConstantInt *CI = dyn_cast<ConstantInt>(II->getOperand(3))) + IsCleanUp = (CI->getZExtValue() == 0); + + if (IsCleanUp) + SelsToConvert.insert(II); + } + } + } + } + } + + if (!SelsToConvert.empty()) { + // Convert all clean-up eh.selectors, which are associated with "invokes" of + // URoR calls, into catch-all eh.selectors. + Changed = true; + + for (SmallPtrSet<IntrinsicInst*, 8>::iterator + SI = SelsToConvert.begin(), SE = SelsToConvert.end(); + SI != SE; ++SI) { + IntrinsicInst *II = *SI; + SmallVector<Value*, 8> Args; + + // Use the exception object pointer and the personality function + // from the original selector. + Args.push_back(II->getOperand(1)); // Exception object pointer. + Args.push_back(II->getOperand(2)); // Personality function. + Args.push_back(EHCatchAllValue->getInitializer()); // Catch-all indicator. + + 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; +} + /// NormalizeLandingPads - Normalize and discover landing pads, noting them /// in the LandingPads set. A landing pad is normal if the only CFG edges /// that end at it are unwind edges from invoke instructions. If we inlined @@ -422,6 +728,8 @@ bool DwarfEHPrepare::runOnFunction(Function &Fn) { if (!CompileFast) Changed |= PromoteStackTemporaries(); + Changed |= HandleURoRInvokes(); + LandingPads.clear(); return Changed; |