aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn McCall <rjmccall@apple.com>2011-05-27 18:34:38 +0000
committerJohn McCall <rjmccall@apple.com>2011-05-27 18:34:38 +0000
commita3de16bc8f36638d5444e3e7b0112998af54f826 (patch)
tree347a748b21b29f74599252689782c948b28362ea
parent11f6cc96bf794c7ede7bf8e24805f4187b24c549 (diff)
Fix the inliner to maintain the current de facto invoke semantics:
- the selector for the landing pad must provide all available information about the handlers, filters, and cleanups within that landing pad - calls to _Unwind_Resume must be converted to branches to the enclosing lpad so as to avoid re-entering the unwinder when the lpad claimed it was going to handle the exception in some way This is quite specific to libUnwind-based unwinding. In an effort to not interfere too badly with other unwinders, and with existing hacks in frontends, this only triggers on _Unwind_Resume (not _Unwind_Resume_or_Rethrow) and does nothing with selectors if it cannot find a selector call for either lpad. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@132200 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--lib/Transforms/Utils/InlineFunction.cpp197
-rw-r--r--test/Transforms/Inline/inline_invoke.ll103
2 files changed, 258 insertions, 42 deletions
diff --git a/lib/Transforms/Utils/InlineFunction.cpp b/lib/Transforms/Utils/InlineFunction.cpp
index bcdb72e23e..69793ab836 100644
--- a/lib/Transforms/Utils/InlineFunction.cpp
+++ b/lib/Transforms/Utils/InlineFunction.cpp
@@ -10,6 +10,13 @@
// This file implements inlining of a function into a call site, resolving
// parameters and the return value as appropriate.
//
+// The code in this file for handling inlines through invoke
+// instructions preserves semantics only under some assumptions about
+// the behavior of unwinders which correspond to gcc-style libUnwind
+// exception personality functions. Eventually the IR will be
+// improved to make this unnecessary, but until then, this code is
+// marked [LIBUNWIND].
+//
//===----------------------------------------------------------------------===//
#include "llvm/Transforms/Utils/Cloning.h"
@@ -38,6 +45,75 @@ bool llvm::InlineFunction(InvokeInst *II, InlineFunctionInfo &IFI) {
return InlineFunction(CallSite(II), IFI);
}
+namespace {
+ /// A class for recording information about inlining through an invoke.
+ class InvokeInliningInfo {
+ BasicBlock *UnwindDest;
+ SmallVector<Value*, 8> UnwindDestPHIValues;
+
+ public:
+ InvokeInliningInfo(InvokeInst *II) : UnwindDest(II->getUnwindDest()) {
+ // If there are PHI nodes in the unwind destination block, we
+ // need to keep track of which values came into them from the
+ // invoke before removing the edge from this block.
+ llvm::BasicBlock *InvokeBlock = II->getParent();
+ for (BasicBlock::iterator I = UnwindDest->begin(); isa<PHINode>(I); ++I) {
+ PHINode *PN = cast<PHINode>(I);
+ // Save the value to use for this edge.
+ llvm::Value *Incoming = PN->getIncomingValueForBlock(InvokeBlock);
+ UnwindDestPHIValues.push_back(Incoming);
+ }
+ }
+
+ BasicBlock *getUnwindDest() const {
+ return UnwindDest;
+ }
+
+ /// Add incoming-PHI values to the unwind destination block for
+ /// the given basic block, using the values for the original
+ /// invoke's source block.
+ void addIncomingPHIValuesFor(BasicBlock *BB) const {
+ BasicBlock::iterator I = UnwindDest->begin();
+ for (unsigned i = 0, e = UnwindDestPHIValues.size(); i != e; ++i, ++I) {
+ PHINode *PN = cast<PHINode>(I);
+ PN->addIncoming(UnwindDestPHIValues[i], BB);
+ }
+ }
+ };
+}
+
+/// [LIBUNWIND] Check whether the given value is the _Unwind_Resume
+/// function specified by the Itanium EH ABI.
+static bool isUnwindResume(Value *value) {
+ Function *fn = dyn_cast<Function>(value);
+ if (!fn) return false;
+
+ // declare void @_Unwind_Resume(i8*)
+ if (fn->getName() != "_Unwind_Resume") return false;
+ const FunctionType *fnType = fn->getFunctionType();
+ if (!fnType->getReturnType()->isVoidTy()) return false;
+ if (fnType->isVarArg()) return false;
+ if (fnType->getNumParams() != 1) return false;
+ const PointerType *paramType = dyn_cast<PointerType>(fnType->getParamType(0));
+ return (paramType && paramType->getElementType()->isIntegerTy(8));
+}
+
+/// [LIBUNWIND] Find the (possibly absent) call to @llvm.eh.selector in
+/// the given landing pad.
+static EHSelectorInst *findSelectorForLandingPad(BasicBlock *lpad) {
+ for (BasicBlock::iterator i = lpad->begin(), e = lpad->end(); i != e; i++)
+ if (EHSelectorInst *selector = dyn_cast<EHSelectorInst>(i))
+ return selector;
+ return 0;
+}
+
+/// [LIBUNWIND] Check whether this selector is "only cleanups":
+/// call i32 @llvm.eh.selector(blah, blah, i32 0)
+static bool isCleanupOnlySelector(EHSelectorInst *selector) {
+ if (selector->getNumArgOperands() != 3) return false;
+ ConstantInt *val = dyn_cast<ConstantInt>(selector->getArgOperand(2));
+ return (val && val->isZero());
+}
/// HandleCallsInBlockInlinedThroughInvoke - When we inline a basic block into
/// an invoke, we have to turn all of the calls that can throw into
@@ -45,9 +121,9 @@ bool llvm::InlineFunction(InvokeInst *II, InlineFunctionInfo &IFI) {
/// it rewrites them to be invokes that jump to InvokeDest and fills in the PHI
/// nodes in that block with the values specified in InvokeDestPHIValues.
///
-static void HandleCallsInBlockInlinedThroughInvoke(BasicBlock *BB,
- BasicBlock *InvokeDest,
- const SmallVectorImpl<Value*> &InvokeDestPHIValues) {
+/// Returns true to indicate that the next block should be skipped.
+static bool HandleCallsInBlockInlinedThroughInvoke(BasicBlock *BB,
+ InvokeInliningInfo &Invoke) {
for (BasicBlock::iterator BBI = BB->begin(), E = BB->end(); BBI != E; ) {
Instruction *I = BBI++;
@@ -55,6 +131,38 @@ static void HandleCallsInBlockInlinedThroughInvoke(BasicBlock *BB,
// instructions require no special handling.
CallInst *CI = dyn_cast<CallInst>(I);
if (CI == 0) continue;
+
+ // LIBUNWIND: merge selector instructions.
+ if (EHSelectorInst *Inner = dyn_cast<EHSelectorInst>(CI)) {
+ EHSelectorInst *Outer = findSelectorForLandingPad(Invoke.getUnwindDest());
+ if (!Outer) continue;
+
+ bool innerIsOnlyCleanup = isCleanupOnlySelector(Inner);
+ bool outerIsOnlyCleanup = isCleanupOnlySelector(Outer);
+
+ // If both selectors contain only cleanups, we don't need to do
+ // anything. TODO: this is really just a very specific instance
+ // of a much more general optimization.
+ if (innerIsOnlyCleanup && outerIsOnlyCleanup) continue;
+
+ // Otherwise, we just append the outer selector to the inner selector.
+ SmallVector<Value*, 16> NewSelector;
+ for (unsigned i = 0, e = Inner->getNumArgOperands(); i != e; ++i)
+ NewSelector.push_back(Inner->getArgOperand(i));
+ for (unsigned i = 2, e = Outer->getNumArgOperands(); i != e; ++i)
+ NewSelector.push_back(Outer->getArgOperand(i));
+
+ CallInst *NewInner = CallInst::Create(Inner->getCalledValue(),
+ NewSelector.begin(),
+ NewSelector.end(),
+ "",
+ Inner);
+ // No need to copy attributes, calling convention, etc.
+ NewInner->takeName(Inner);
+ Inner->replaceAllUsesWith(NewInner);
+ Inner->eraseFromParent();
+ continue;
+ }
// If this call cannot unwind, don't convert it to an invoke.
if (CI->doesNotThrow())
@@ -63,37 +171,52 @@ static void HandleCallsInBlockInlinedThroughInvoke(BasicBlock *BB,
// Convert this function call into an invoke instruction.
// First, split the basic block.
BasicBlock *Split = BB->splitBasicBlock(CI, CI->getName()+".noexc");
+
+ bool skipNextBlock = false;
+
+ // LIBUNWIND: If this is a call to @_Unwind_Resume, just branch
+ // directly to the new landing pad.
+ if (isUnwindResume(CI->getCalledValue())) {
+ BranchInst::Create(Invoke.getUnwindDest(), BB->getTerminator());
+
+ // TODO: 'Split' is now unreachable; clean it up.
+
+ // We want to leave the original call intact so that the call
+ // graph and other structures won't get misled. We also have to
+ // avoid processing the next block, or we'll iterate here forever.
+ skipNextBlock = true;
+
+ // Otherwise, create the new invoke instruction.
+ } else {
+ ImmutableCallSite CS(CI);
+ SmallVector<Value*, 8> InvokeArgs(CS.arg_begin(), CS.arg_end());
+ InvokeInst *II =
+ InvokeInst::Create(CI->getCalledValue(), Split, Invoke.getUnwindDest(),
+ InvokeArgs.begin(), InvokeArgs.end(),
+ CI->getName(), BB->getTerminator());
+ II->setCallingConv(CI->getCallingConv());
+ II->setAttributes(CI->getAttributes());
- // Next, create the new invoke instruction, inserting it at the end
- // of the old basic block.
- ImmutableCallSite CS(CI);
- SmallVector<Value*, 8> InvokeArgs(CS.arg_begin(), CS.arg_end());
- InvokeInst *II =
- InvokeInst::Create(CI->getCalledValue(), Split, InvokeDest,
- InvokeArgs.begin(), InvokeArgs.end(),
- CI->getName(), BB->getTerminator());
- II->setCallingConv(CI->getCallingConv());
- II->setAttributes(CI->getAttributes());
-
- // Make sure that anything using the call now uses the invoke! This also
- // updates the CallGraph if present, because it uses a WeakVH.
- CI->replaceAllUsesWith(II);
+ // Make sure that anything using the call now uses the invoke! This also
+ // updates the CallGraph if present, because it uses a WeakVH.
+ CI->replaceAllUsesWith(II);
+
+ Split->getInstList().pop_front(); // Delete the original call
+ }
// Delete the unconditional branch inserted by splitBasicBlock
BB->getInstList().pop_back();
- Split->getInstList().pop_front(); // Delete the original call
// Update any PHI nodes in the exceptional block to indicate that
// there is now a new entry in them.
- unsigned i = 0;
- for (BasicBlock::iterator I = InvokeDest->begin();
- isa<PHINode>(I); ++I, ++i)
- cast<PHINode>(I)->addIncoming(InvokeDestPHIValues[i], BB);
+ Invoke.addIncomingPHIValuesFor(BB);
// This basic block is now complete, the caller will continue scanning the
// next one.
- return;
+ return skipNextBlock;
}
+
+ return false;
}
@@ -107,17 +230,6 @@ static void HandleCallsInBlockInlinedThroughInvoke(BasicBlock *BB,
static void HandleInlinedInvoke(InvokeInst *II, BasicBlock *FirstNewBlock,
ClonedCodeInfo &InlinedCodeInfo) {
BasicBlock *InvokeDest = II->getUnwindDest();
- SmallVector<Value*, 8> InvokeDestPHIValues;
-
- // If there are PHI nodes in the unwind destination block, we need to
- // keep track of which values came into them from this invoke, then remove
- // the entry for this block.
- BasicBlock *InvokeBlock = II->getParent();
- for (BasicBlock::iterator I = InvokeDest->begin(); isa<PHINode>(I); ++I) {
- PHINode *PN = cast<PHINode>(I);
- // Save the value to use for this edge.
- InvokeDestPHIValues.push_back(PN->getIncomingValueForBlock(InvokeBlock));
- }
Function *Caller = FirstNewBlock->getParent();
@@ -133,11 +245,17 @@ static void HandleInlinedInvoke(InvokeInst *II, BasicBlock *FirstNewBlock,
InvokeDest->removePredecessor(II->getParent());
return;
}
+
+ InvokeInliningInfo Invoke(II);
for (Function::iterator BB = FirstNewBlock, E = Caller->end(); BB != E; ++BB){
if (InlinedCodeInfo.ContainsCalls)
- HandleCallsInBlockInlinedThroughInvoke(BB, InvokeDest,
- InvokeDestPHIValues);
+ if (HandleCallsInBlockInlinedThroughInvoke(BB, Invoke)) {
+ // Honor a request to skip the next block. We don't need to
+ // consider UnwindInsts in this case either.
+ ++BB;
+ continue;
+ }
if (UnwindInst *UI = dyn_cast<UnwindInst>(BB->getTerminator())) {
// An UnwindInst requires special handling when it gets inlined into an
@@ -151,12 +269,7 @@ static void HandleInlinedInvoke(InvokeInst *II, BasicBlock *FirstNewBlock,
// Update any PHI nodes in the exceptional block to indicate that
// there is now a new entry in them.
- unsigned i = 0;
- for (BasicBlock::iterator I = InvokeDest->begin();
- isa<PHINode>(I); ++I, ++i) {
- PHINode *PN = cast<PHINode>(I);
- PN->addIncoming(InvokeDestPHIValues[i], BB);
- }
+ Invoke.addIncomingPHIValuesFor(BB);
}
}
diff --git a/test/Transforms/Inline/inline_invoke.ll b/test/Transforms/Inline/inline_invoke.ll
new file mode 100644
index 0000000000..bd955e3bc9
--- /dev/null
+++ b/test/Transforms/Inline/inline_invoke.ll
@@ -0,0 +1,103 @@
+; RUN: opt < %s -inline -S | FileCheck %s
+
+; Test that the inliner correctly handles inlining into invoke sites
+; by appending selectors and forwarding _Unwind_Resume directly to the
+; enclosing landing pad.
+
+%struct.A = type { i8 }
+
+@_ZTIi = external constant i8*
+
+declare void @_ZN1AC1Ev(%struct.A*)
+
+declare void @_ZN1AD1Ev(%struct.A*)
+
+declare i8* @llvm.eh.exception() nounwind readonly
+
+declare i32 @llvm.eh.selector(i8*, i8*, ...) nounwind
+
+declare i32 @llvm.eh.typeid.for(i8*) nounwind
+
+declare void @_Unwind_Resume(i8*)
+
+declare i32 @__gxx_personality_v0(...)
+
+declare i8* @__cxa_begin_catch(i8*)
+
+declare void @__cxa_end_catch()
+
+declare void @_ZSt9terminatev()
+
+define internal void @test0_in() alwaysinline uwtable ssp {
+entry:
+ %a = alloca %struct.A, align 1
+ %b = alloca %struct.A, align 1
+ call void @_ZN1AC1Ev(%struct.A* %a)
+ invoke void @_ZN1AC1Ev(%struct.A* %b)
+ to label %invoke.cont unwind label %lpad
+
+invoke.cont:
+ invoke void @_ZN1AD1Ev(%struct.A* %b)
+ to label %invoke.cont1 unwind label %lpad
+
+invoke.cont1:
+ call void @_ZN1AD1Ev(%struct.A* %a)
+ ret void
+
+lpad:
+ %exn = call i8* @llvm.eh.exception() nounwind
+ %eh.selector = call i32 (i8*, i8*, ...)* @llvm.eh.selector(i8* %exn, i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*), i32 0) nounwind
+ invoke void @_ZN1AD1Ev(%struct.A* %a)
+ to label %invoke.cont2 unwind label %terminate.lpad
+
+invoke.cont2:
+ call void @_Unwind_Resume(i8* %exn) noreturn
+ unreachable
+
+terminate.lpad:
+ %exn3 = call i8* @llvm.eh.exception() nounwind
+ %eh.selector4 = call i32 (i8*, i8*, ...)* @llvm.eh.selector(i8* %exn3, i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*), i8* null) nounwind
+ call void @_ZSt9terminatev() noreturn nounwind
+ unreachable
+}
+
+define void @test0_out() uwtable ssp {
+entry:
+ invoke void @test0_in()
+ to label %ret unwind label %lpad
+
+ret:
+ ret void
+
+lpad: ; preds = %entry
+ %exn = call i8* @llvm.eh.exception() nounwind
+ %eh.selector = call i32 (i8*, i8*, ...)* @llvm.eh.selector(i8* %exn, i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*), i8* bitcast (i8** @_ZTIi to i8*)) nounwind
+ %0 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIi to i8*)) nounwind
+ %1 = icmp eq i32 %eh.selector, %0
+ br i1 %1, label %catch, label %eh.resume
+
+catch:
+ %ignored = call i8* @__cxa_begin_catch(i8* %exn) nounwind
+ call void @__cxa_end_catch() nounwind
+ br label %ret
+
+eh.resume:
+ call void @_Unwind_Resume(i8* %exn) noreturn
+ unreachable
+}
+
+; CHECK: define void @test0_out()
+; CHECK: [[A:%.*]] = alloca %struct.A,
+; CHECK: [[B:%.*]] = alloca %struct.A,
+; CHECK: invoke void @_ZN1AC1Ev(%struct.A* [[A]])
+; CHECK: invoke void @_ZN1AC1Ev(%struct.A* [[B]])
+; CHECK: invoke void @_ZN1AD1Ev(%struct.A* [[B]])
+; CHECK: invoke void @_ZN1AD1Ev(%struct.A* [[A]])
+; CHECK: call i32 (i8*, i8*, ...)* @llvm.eh.selector(i8* {{%.*}}, i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*), i32 0, i8* bitcast (i8** @_ZTIi to i8*))
+; CHECK-NEXT: invoke void @_ZN1AD1Ev(%struct.A* [[A]])
+; CHECK-NEXT: to label %[[LBL:[^\s]+]] unwind
+; CHECK: [[LBL]]:
+; CHECK-NEXT: br label %[[LPAD:[^\s]+]]
+; CHECK: [[LPAD]]:
+; CHECK-NEXT: call i8* @llvm.eh.exception()
+; CHECK-NEXT: call i32 (i8*, i8*, ...)* @llvm.eh.selector(i8* {{%.*}}, i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*), i8* bitcast (i8** @_ZTIi to i8*))