From 690248bf52b4812d581313848e35cb11199d40e7 Mon Sep 17 00:00:00 2001 From: Reid Kleckner Date: Wed, 11 Feb 2015 01:23:16 +0000 Subject: [PATCH] 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. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@228782 91177308-0d34-0410-b5e6-96231b3b80d8 --- docs/LangRef.rst | 9 ++++-- include/llvm/Analysis/LibCallSemantics.h | 16 +++++++--- lib/Analysis/LibCallSemantics.cpp | 25 ++++++++++++++- lib/CodeGen/WinEHPrepare.cpp | 2 +- lib/Transforms/IPO/PruneEH.cpp | 4 ++- .../InstCombine/InstructionCombining.cpp | 3 +- lib/Transforms/Utils/Local.cpp | 3 +- test/Feature/seh-nounwind.ll | 32 +++++++++++++++++++ test/Other/2009-03-31-CallGraph.ll | 2 ++ .../PruneEH/2003-11-21-PHIUpdate.ll | 15 --------- test/Transforms/PruneEH/seh-nounwind.ll | 31 ++++++++++++++++++ .../SimplifyCFG/2007-11-22-InvokeNoUnwind.ll | 4 +++ test/Transforms/SimplifyCFG/seh-nounwind.ll | 31 ++++++++++++++++++ 13 files changed, 150 insertions(+), 27 deletions(-) create mode 100644 test/Feature/seh-nounwind.ll delete mode 100644 test/Transforms/PruneEH/2003-11-21-PHIUpdate.ll create mode 100644 test/Transforms/PruneEH/seh-nounwind.ll create mode 100644 test/Transforms/SimplifyCFG/seh-nounwind.ll diff --git a/docs/LangRef.rst b/docs/LangRef.rst index f086fbe3e0b..f7b865d0de0 100644 --- a/docs/LangRef.rst +++ b/docs/LangRef.rst @@ -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 diff --git a/include/llvm/Analysis/LibCallSemantics.h b/include/llvm/Analysis/LibCallSemantics.h index e529a426ff8..df26024f45e 100644 --- a/include/llvm/Analysis/LibCallSemantics.h +++ b/include/llvm/Analysis/LibCallSemantics.h @@ -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 diff --git a/lib/Analysis/LibCallSemantics.cpp b/lib/Analysis/LibCallSemantics.cpp index 3a345c45d07..6c2ebdb4594 100644 --- a/lib/Analysis/LibCallSemantics.cpp +++ b/lib/Analysis/LibCallSemantics.cpp @@ -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(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); +} diff --git a/lib/CodeGen/WinEHPrepare.cpp b/lib/CodeGen/WinEHPrepare.cpp index c608bb0a31e..b4019b48617 100644 --- a/lib/CodeGen/WinEHPrepare.cpp +++ b/lib/CodeGen/WinEHPrepare.cpp @@ -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)) diff --git a/lib/Transforms/IPO/PruneEH.cpp b/lib/Transforms/IPO/PruneEH.cpp index 7bd4ce12860..1943b930cbf 100644 --- a/lib/Transforms/IPO/PruneEH.cpp +++ b/lib/Transforms/IPO/PruneEH.cpp @@ -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(BB->getTerminator())) - if (II->doesNotThrow()) { + if (II->doesNotThrow() && canSimplifyInvokeNoUnwind(II)) { SmallVector Args(II->op_begin(), II->op_end() - 3); // Insert a call instruction before the invoke. CallInst *Call = CallInst::Create(II->getCalledValue(), Args, "", II); diff --git a/lib/Transforms/InstCombine/InstructionCombining.cpp b/lib/Transforms/InstCombine/InstructionCombining.cpp index 3cfcc90a8ed..ffc8ff5b16c 100644 --- a/lib/Transforms/InstCombine/InstructionCombining.cpp +++ b/lib/Transforms/InstCombine/InstructionCombining.cpp @@ -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). diff --git a/lib/Transforms/Utils/Local.cpp b/lib/Transforms/Utils/Local.cpp index b8c0a7e47b5..48305684721 100644 --- a/lib/Transforms/Utils/Local.cpp +++ b/lib/Transforms/Utils/Local.cpp @@ -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(Callee) || isa(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); diff --git a/test/Feature/seh-nounwind.ll b/test/Feature/seh-nounwind.ll new file mode 100644 index 00000000000..203471649df --- /dev/null +++ b/test/Feature/seh-nounwind.ll @@ -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(...) diff --git a/test/Other/2009-03-31-CallGraph.ll b/test/Other/2009-03-31-CallGraph.ll index 864903cffba..1e178308464 100644 --- a/test/Other/2009-03-31-CallGraph.ll +++ b/test/Other/2009-03-31-CallGraph.ll @@ -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 diff --git a/test/Transforms/PruneEH/2003-11-21-PHIUpdate.ll b/test/Transforms/PruneEH/2003-11-21-PHIUpdate.ll deleted file mode 100644 index a01070308be..00000000000 --- a/test/Transforms/PruneEH/2003-11-21-PHIUpdate.ll +++ /dev/null @@ -1,15 +0,0 @@ -; RUN: opt < %s -prune-eh -disable-output - -define internal void @callee() { - ret void -} - -define i32 @caller() { -;