Don't promote asynch EH invokes of nounwind functions to calls

If the landingpad of the invoke is using a personality function that
catches asynch exceptions, then it can catch a trap.

Also add some landingpads to invalid LLVM IR test cases that lack them.

Over-the-shoulder reviewed by David Majnemer.

llvm-svn: 228782
This commit is contained in:
Reid Kleckner 2015-02-11 01:23:16 +00:00
parent 58e985c2db
commit 96d011315a
13 changed files with 150 additions and 27 deletions

View File

@ -1236,9 +1236,12 @@ example:
normally. This produces undefined behavior at runtime if the
function ever does dynamically return.
``nounwind``
This function attribute indicates that the function never returns
with an unwind or exceptional control flow. If the function does
unwind, its runtime behavior is undefined.
This function attribute indicates that the function never raises an
exception. If the function does raise an exception, its runtime
behavior is undefined. However, functions marked nounwind may still
trap or generate asynchronous exceptions. Exception handling schemes
that are recognized by LLVM to handle asynchronous exceptions, such
as SEH, will still provide their implementation defined semantics.
``optnone``
This function attribute indicates that the function is not optimized
by any optimization or code generator passes with the

View File

@ -18,6 +18,7 @@
#include "llvm/Analysis/AliasAnalysis.h"
namespace llvm {
class InvokeInst;
/// LibCallLocationInfo - This struct describes a set of memory locations that
/// are accessed by libcalls. Identification of a location is doing with a
@ -168,14 +169,21 @@ namespace llvm {
GNU_C,
GNU_CXX,
GNU_ObjC,
MSVC_X86SEH,
MSVC_Win64SEH,
MSVC_CXX,
};
/// ClassifyEHPersonality - See if the given exception handling personality
/// function is one that we understand. If so, return a description of it;
/// otherwise return Unknown_Personality.
EHPersonality ClassifyEHPersonality(Value *Pers);
/// \brief See if the given exception handling personality function is one
/// that we understand. If so, return a description of it; otherwise return
/// Unknown.
EHPersonality classifyEHPersonality(Value *Pers);
/// \brief Returns true if this personality function catches asynchronous
/// exceptions.
bool isAsynchronousEHPersonality(EHPersonality Pers);
bool canSimplifyInvokeNoUnwind(const InvokeInst *II);
} // end namespace llvm

View File

@ -64,7 +64,7 @@ LibCallInfo::getFunctionInfo(const Function *F) const {
/// See if the given exception handling personality function is one that we
/// understand. If so, return a description of it; otherwise return Unknown.
EHPersonality llvm::ClassifyEHPersonality(Value *Pers) {
EHPersonality llvm::classifyEHPersonality(Value *Pers) {
Function *F = dyn_cast<Function>(Pers->stripPointerCasts());
if (!F)
return EHPersonality::Unknown;
@ -73,7 +73,30 @@ EHPersonality llvm::ClassifyEHPersonality(Value *Pers) {
.Case("__gxx_personality_v0", EHPersonality::GNU_CXX)
.Case("__gcc_personality_v0", EHPersonality::GNU_C)
.Case("__objc_personality_v0", EHPersonality::GNU_ObjC)
.Case("__except_handler3", EHPersonality::MSVC_X86SEH)
.Case("__except_handler4", EHPersonality::MSVC_X86SEH)
.Case("__C_specific_handler", EHPersonality::MSVC_Win64SEH)
.Case("__CxxFrameHandler3", EHPersonality::MSVC_CXX)
.Default(EHPersonality::Unknown);
}
bool llvm::isAsynchronousEHPersonality(EHPersonality Pers) {
// The two SEH personality functions can catch asynch exceptions. We assume
// unknown personalities don't catch asynch exceptions.
switch (Pers) {
case EHPersonality::MSVC_X86SEH:
case EHPersonality::MSVC_Win64SEH:
return true;
default: return false;
}
llvm_unreachable("invalid enum");
}
bool llvm::canSimplifyInvokeNoUnwind(const InvokeInst *II) {
const LandingPadInst *LP = II->getLandingPadInst();
EHPersonality Personality = classifyEHPersonality(LP->getPersonalityFn());
// We can't simplify any invokes to nounwind functions if the personality
// function wants to catch asynch exceptions. The nounwind attribute only
// implies that the function does not throw synchronous exceptions.
return !isAsynchronousEHPersonality(Personality);
}

View File

@ -75,7 +75,7 @@ bool WinEHPrepare::runOnFunction(Function &Fn) {
return false;
// Classify the personality to see what kind of preparation we need.
EHPersonality Pers = ClassifyEHPersonality(LPads.back()->getPersonalityFn());
EHPersonality Pers = classifyEHPersonality(LPads.back()->getPersonalityFn());
// Delegate through to the DWARF pass if this is unrecognized.
if (!isMSVCPersonality(Pers))

View File

@ -18,8 +18,10 @@
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Analysis/CallGraph.h"
#include "llvm/Analysis/CallGraphSCCPass.h"
#include "llvm/Analysis/LibCallSemantics.h"
#include "llvm/IR/CFG.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/Function.h"
@ -175,7 +177,7 @@ bool PruneEH::SimplifyFunction(Function *F) {
bool MadeChange = false;
for (Function::iterator BB = F->begin(), E = F->end(); BB != E; ++BB) {
if (InvokeInst *II = dyn_cast<InvokeInst>(BB->getTerminator()))
if (II->doesNotThrow()) {
if (II->doesNotThrow() && canSimplifyInvokeNoUnwind(II)) {
SmallVector<Value*, 8> Args(II->op_begin(), II->op_end() - 3);
// Insert a call instruction before the invoke.
CallInst *Call = CallInst::Create(II->getCalledValue(), Args, "", II);

View File

@ -2275,6 +2275,7 @@ static bool isCatchAll(EHPersonality Personality, Constant *TypeInfo) {
return false;
case EHPersonality::GNU_CXX:
case EHPersonality::GNU_ObjC:
case EHPersonality::MSVC_X86SEH:
case EHPersonality::MSVC_Win64SEH:
case EHPersonality::MSVC_CXX:
return TypeInfo->isNullValue();
@ -2293,7 +2294,7 @@ Instruction *InstCombiner::visitLandingPadInst(LandingPadInst &LI) {
// The logic here should be correct for any real-world personality function.
// However if that turns out not to be true, the offending logic can always
// be conditioned on the personality function, like the catch-all logic is.
EHPersonality Personality = ClassifyEHPersonality(LI.getPersonalityFn());
EHPersonality Personality = classifyEHPersonality(LI.getPersonalityFn());
// Simplify the list of clauses, eg by removing repeated catch clauses
// (these are often created by inlining).

View File

@ -17,6 +17,7 @@
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/Analysis/LibCallSemantics.h"
#include "llvm/Analysis/InstructionSimplify.h"
#include "llvm/Analysis/MemoryBuiltins.h"
#include "llvm/Analysis/ValueTracking.h"
@ -1260,7 +1261,7 @@ static bool markAliveBlocks(BasicBlock *BB,
if (isa<ConstantPointerNull>(Callee) || isa<UndefValue>(Callee)) {
changeToUnreachable(II, true);
Changed = true;
} else if (II->doesNotThrow()) {
} else if (II->doesNotThrow() && canSimplifyInvokeNoUnwind(II)) {
if (II->use_empty() && II->onlyReadsMemory()) {
// jump to the normal destination branch.
BranchInst::Create(II->getNormalDest(), II);

View File

@ -0,0 +1,32 @@
; RUN: opt -S -O2 < %s | FileCheck %s
; Feature test that verifies that all optimizations leave asynch personality
; invokes of nounwind functions alone.
; The @div function in this test can fault, even though it can't
; throw a synchronous exception.
define i32 @div(i32 %n, i32 %d) nounwind noinline {
entry:
%div = sdiv i32 %n, %d
ret i32 %div
}
define i32 @main() nounwind {
entry:
%call = invoke i32 @div(i32 10, i32 0)
to label %__try.cont unwind label %lpad
lpad:
%0 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__C_specific_handler to i8*)
catch i8* null
br label %__try.cont
__try.cont:
%retval.0 = phi i32 [ %call, %entry ], [ 0, %lpad ]
ret i32 %retval.0
}
; CHECK-LABEL: define i32 @main()
; CHECK: invoke i32 @div(i32 10, i32 0)
declare i32 @__C_specific_handler(...)

View File

@ -7,6 +7,8 @@ ok1:
ret void
lpad1:
landingpad {i8*, i32} personality i32 (...)* @__gxx_personality_v0
cleanup
invoke void @f4()
to label %ok2 unwind label %lpad2

View File

@ -1,15 +0,0 @@
; RUN: opt < %s -prune-eh -disable-output
define internal void @callee() {
ret void
}
define i32 @caller() {
; <label>:0
invoke void @callee( )
to label %E unwind label %E
E: ; preds = %0, %0
%X = phi i32 [ 0, %0 ], [ 0, %0 ] ; <i32> [#uses=1]
ret i32 %X
}

View File

@ -0,0 +1,31 @@
; RUN: opt -S -prune-eh < %s | FileCheck %s
; Don't remove invokes of nounwind functions if the personality handles async
; exceptions. The @div function in this test can fault, even though it can't
; throw a synchronous exception.
define i32 @div(i32 %n, i32 %d) nounwind {
entry:
%div = sdiv i32 %n, %d
ret i32 %div
}
define i32 @main() nounwind {
entry:
%call = invoke i32 @div(i32 10, i32 0)
to label %__try.cont unwind label %lpad
lpad:
%0 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__C_specific_handler to i8*)
catch i8* null
br label %__try.cont
__try.cont:
%retval.0 = phi i32 [ %call, %entry ], [ 0, %lpad ]
ret i32 %retval.0
}
; CHECK-LABEL: define i32 @main()
; CHECK: invoke i32 @div(i32 10, i32 0)
declare i32 @__C_specific_handler(...)

View File

@ -12,5 +12,9 @@ Cont: ; preds = %0
ret i32 0
Other: ; preds = %0
landingpad { i8*, i32 } personality i32 (...)* @__gxx_personality_v0
catch i8* null
ret i32 1
}
declare i32 @__gxx_personality_v0(...)

View File

@ -0,0 +1,31 @@
; RUN: opt -S -simplifycfg < %s | FileCheck %s
; Don't remove invokes of nounwind functions if the personality handles async
; exceptions. The @div function in this test can fault, even though it can't
; throw a synchronous exception.
define i32 @div(i32 %n, i32 %d) nounwind {
entry:
%div = sdiv i32 %n, %d
ret i32 %div
}
define i32 @main() nounwind {
entry:
%call = invoke i32 @div(i32 10, i32 0)
to label %__try.cont unwind label %lpad
lpad:
%0 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__C_specific_handler to i8*)
catch i8* null
br label %__try.cont
__try.cont:
%retval.0 = phi i32 [ %call, %entry ], [ 0, %lpad ]
ret i32 %retval.0
}
; CHECK-LABEL: define i32 @main()
; CHECK: invoke i32 @div(i32 10, i32 0)
declare i32 @__C_specific_handler(...)