Don't promote asynch EH invokes of nounwind functions to calls
authorReid Kleckner <reid@kleckner.net>
Wed, 11 Feb 2015 01:23:16 +0000 (01:23 +0000)
committerReid Kleckner <reid@kleckner.net>
Wed, 11 Feb 2015 01:23:16 +0000 (01:23 +0000)
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

13 files changed:
docs/LangRef.rst
include/llvm/Analysis/LibCallSemantics.h
lib/Analysis/LibCallSemantics.cpp
lib/CodeGen/WinEHPrepare.cpp
lib/Transforms/IPO/PruneEH.cpp
lib/Transforms/InstCombine/InstructionCombining.cpp
lib/Transforms/Utils/Local.cpp
test/Feature/seh-nounwind.ll [new file with mode: 0644]
test/Other/2009-03-31-CallGraph.ll
test/Transforms/PruneEH/2003-11-21-PHIUpdate.ll [deleted file]
test/Transforms/PruneEH/seh-nounwind.ll [new file with mode: 0644]
test/Transforms/SimplifyCFG/2007-11-22-InvokeNoUnwind.ll
test/Transforms/SimplifyCFG/seh-nounwind.ll [new file with mode: 0644]

index f086fbe3e0b7da24471348a6ea2633000f0e3be2..f7b865d0de0259a3add7392bcb68fc61c767b451 100644 (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
index e529a426ff8462d702d0dbbbe73da61b954553eb..df26024f45e2cf5a5f33f3cc78c7fe5a753e615d 100644 (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
 
index 3a345c45d079ae2f52a5f77ece35afad02046172..6c2ebdb4594fbb7a939185b122c843f2a1983ec5 100644 (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);
+}
index c608bb0a31e9f235b918cf67acf16af88eba5e73..b4019b4861706e749dbf9e189360c86d11d82732 100644 (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))
index 7bd4ce12860d29a885051c596a7631c6ef54c70a..1943b930cbf959d91be067d6ab22ddc064c95ce5 100644 (file)
 #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);
index 3cfcc90a8ed9ae61ef18f8555e317812252e77cb..ffc8ff5b16ce30c2ca801730d102ca5ef3612787 100644 (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).
index b8c0a7e47b5bc0dbac2f0c4f6554bf9efebfeeaa..483056847219a22e5a2f00e76181861e1fb1eb2d 100644 (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);
diff --git a/test/Feature/seh-nounwind.ll b/test/Feature/seh-nounwind.ll
new file mode 100644 (file)
index 0000000..2034716
--- /dev/null
@@ -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(...)
index 864903cffba2452e461f47e26fcb5c49eb0379ed..1e1783084649a99d66755655ec9f50d93e8c055d 100644 (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
 
diff --git a/test/Transforms/PruneEH/2003-11-21-PHIUpdate.ll b/test/Transforms/PruneEH/2003-11-21-PHIUpdate.ll
deleted file mode 100644 (file)
index a010703..0000000
+++ /dev/null
@@ -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
-}
-
diff --git a/test/Transforms/PruneEH/seh-nounwind.ll b/test/Transforms/PruneEH/seh-nounwind.ll
new file mode 100644 (file)
index 0000000..4b69ae4
--- /dev/null
@@ -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(...)
index a90e072762534c208f251f989fb137839e292495..76f41e8fc2189bed49186dee3b74e9231d6a333b 100644 (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(...)
diff --git a/test/Transforms/SimplifyCFG/seh-nounwind.ll b/test/Transforms/SimplifyCFG/seh-nounwind.ll
new file mode 100644 (file)
index 0000000..3845e31
--- /dev/null
@@ -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(...)