forked from OSchip/llvm-project
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:
parent
58e985c2db
commit
96d011315a
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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).
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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(...)
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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(...)
|
|
@ -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(...)
|
||||
|
|
|
@ -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(...)
|
Loading…
Reference in New Issue