[llvm] r367948 - [Attributor] Deduce the "no-return" attribute for functions

Johannes Doerfert via llvm-commits llvm-commits at lists.llvm.org
Mon Aug 5 16:22:05 PDT 2019


Author: jdoerfert
Date: Mon Aug  5 16:22:05 2019
New Revision: 367948

URL: http://llvm.org/viewvc/llvm-project?rev=367948&view=rev
Log:
[Attributor] Deduce the "no-return" attribute for functions

A function is "no-return" if we never reach a return instruction, either
because there are none or the ones that exist are dead.

Test have been adjusted:
  - either noreturn was added, or
  - noreturn was avoided by modifying the code.

The new noreturn_{sync,async} test make sure we do handle invoke
instructions with a noreturn (and potentially nowunwind) callee
correctly, even in the presence of potential asynchronous exceptions.

Added:
    llvm/trunk/test/Transforms/FunctionAttrs/noreturn_async.ll
    llvm/trunk/test/Transforms/FunctionAttrs/noreturn_sync.ll
Modified:
    llvm/trunk/docs/LangRef.rst
    llvm/trunk/lib/Transforms/IPO/Attributor.cpp
    llvm/trunk/test/Transforms/FunctionAttrs/arg_nocapture.ll
    llvm/trunk/test/Transforms/FunctionAttrs/arg_returned.ll
    llvm/trunk/test/Transforms/FunctionAttrs/fn_noreturn.ll
    llvm/trunk/test/Transforms/FunctionAttrs/liveness.ll
    llvm/trunk/test/Transforms/FunctionAttrs/nofree-attributor.ll
    llvm/trunk/test/Transforms/FunctionAttrs/nonnull.ll
    llvm/trunk/test/Transforms/FunctionAttrs/nosync.ll
    llvm/trunk/test/Transforms/FunctionAttrs/nounwind.ll
    llvm/trunk/test/Transforms/FunctionAttrs/willreturn.ll

Modified: llvm/trunk/docs/LangRef.rst
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/docs/LangRef.rst?rev=367948&r1=367947&r2=367948&view=diff
==============================================================================
--- llvm/trunk/docs/LangRef.rst (original)
+++ llvm/trunk/docs/LangRef.rst Mon Aug  5 16:22:05 2019
@@ -1480,8 +1480,9 @@ example:
     target-specific ABI normally permits it.
 ``noreturn``
     This function attribute indicates that the function never returns
-    normally. This produces undefined behavior at runtime if the
-    function ever does dynamically return.
+    normally, hence through a return instruction. This produces undefined
+    behavior at runtime if the function ever does dynamically return. Annotated
+    functions may still raise an exception, i.a., ``nounwind`` is not implied.
 ``norecurse``
     This function attribute indicates that the function does not call itself
     either directly or indirectly down any possible call path. This produces

Modified: llvm/trunk/lib/Transforms/IPO/Attributor.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Transforms/IPO/Attributor.cpp?rev=367948&r1=367947&r2=367948&view=diff
==============================================================================
--- llvm/trunk/lib/Transforms/IPO/Attributor.cpp (original)
+++ llvm/trunk/lib/Transforms/IPO/Attributor.cpp Mon Aug  5 16:22:05 2019
@@ -76,6 +76,7 @@ STATISTIC(NumCSArgumentDereferenceable,
 STATISTIC(NumFnReturnedAlign, "Number of function return values marked align");
 STATISTIC(NumFnArgumentAlign, "Number of function arguments marked align");
 STATISTIC(NumCSArgumentAlign, "Number of call site arguments marked align");
+STATISTIC(NumFnNoReturn, "Number of functions marked noreturn");
 
 // TODO: Determine a good default value.
 //
@@ -179,6 +180,9 @@ static void bookkeeping(AbstractAttribut
   case Attribute::WillReturn:
     NumFnWillReturn++;
     break;
+  case Attribute::NoReturn:
+    NumFnNoReturn++;
+    return;
   case Attribute::NoAlias:
     NumFnArgumentNoAlias++;
     return;
@@ -2336,6 +2340,60 @@ ChangeStatus AAAlignCallSiteArgument::up
                                      : ChangeStatus::CHANGED;
 }
 
+/// ------------------ Function No-Return Attribute ----------------------------
+struct AANoReturnFunction final : public AANoReturn, BooleanState {
+
+  AANoReturnFunction(Function &F, InformationCache &InfoCache)
+      : AANoReturn(F, InfoCache) {}
+
+  /// See AbstractAttribute::getState()
+  /// {
+  AbstractState &getState() override { return *this; }
+  const AbstractState &getState() const override { return *this; }
+  /// }
+
+  /// Return true if the underlying object is known to never return.
+  bool isKnownNoReturn() const override { return getKnown(); }
+
+  /// Return true if the underlying object is assumed to never return.
+  bool isAssumedNoReturn() const override { return getAssumed(); }
+
+  /// See AbstractAttribute::getManifestPosition().
+  ManifestPosition getManifestPosition() const override { return MP_FUNCTION; }
+
+  /// See AbstractAttribute::getAsStr().
+  const std::string getAsStr() const override {
+    return getAssumed() ? "noreturn" : "may-return";
+  }
+
+  /// See AbstractAttribute::initialize(...).
+  void initialize(Attributor &A) override {
+    Function &F = getAnchorScope();
+    if (F.hasFnAttribute(getAttrKind()))
+      indicateOptimisticFixpoint();
+  }
+
+  /// See AbstractAttribute::updateImpl(Attributor &A).
+  virtual ChangeStatus updateImpl(Attributor &A) override {
+    Function &F = getAnchorScope();
+
+    // The map from instruction opcodes to those instructions in the function.
+    auto &OpcodeInstMap = InfoCache.getOpcodeInstMapForFunction(F);
+
+    // Look at all return instructions.
+    auto &ReturnInsts = OpcodeInstMap[Instruction::Ret];
+    if (ReturnInsts.empty())
+      return indicateOptimisticFixpoint();
+
+    auto *LivenessAA = A.getAAFor<AAIsDead>(*this, F);
+    if (!LivenessAA ||
+        LivenessAA->isLiveInstSet(ReturnInsts.begin(), ReturnInsts.end()))
+      return indicatePessimisticFixpoint();
+
+    return ChangeStatus::UNCHANGED;
+  }
+};
+
 /// ----------------------------------------------------------------------------
 ///                               Attributor
 /// ----------------------------------------------------------------------------
@@ -2539,6 +2597,9 @@ void Attributor::identifyDefaultAbstract
   // Every function might be "no-free".
   registerAA(*new AANoFreeFunction(F, InfoCache));
 
+  // Every function might be "no-return".
+  registerAA(*new AANoReturnFunction(F, InfoCache));
+
   // Return attributes are only appropriate if the return type is non void.
   Type *ReturnType = F.getReturnType();
   if (!ReturnType->isVoidTy()) {

Modified: llvm/trunk/test/Transforms/FunctionAttrs/arg_nocapture.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Transforms/FunctionAttrs/arg_nocapture.ll?rev=367948&r1=367947&r2=367948&view=diff
==============================================================================
--- llvm/trunk/test/Transforms/FunctionAttrs/arg_nocapture.ll (original)
+++ llvm/trunk/test/Transforms/FunctionAttrs/arg_nocapture.ll Mon Aug  5 16:22:05 2019
@@ -1,5 +1,4 @@
 ; RUN: opt -functionattrs -attributor -attributor-disable=false -S < %s | FileCheck %s
-; RUN: opt -functionattrs -attributor -attributor-disable=false -attributor-verify=true -S < %s | FileCheck %s
 ;
 ; Test cases specifically designed for the "no-capture" argument attribute.
 ; We use FIXME's to indicate problems and missing attributes.
@@ -87,11 +86,12 @@ entry:
 ;
 ; Other arguments are possible here due to the no-return behavior.
 ;
-; FIXME: no-return missing
 ; CHECK: define noalias nonnull align 536870912 dereferenceable(4294967295) i32* @srec16(i32* nocapture readnone %a)
 define i32* @srec16(i32* %a) #0 {
 entry:
   %call = call i32* @srec16(i32* %a)
+; CHECK:      %call = call i32* @srec16(i32* %a)
+; CHECK-NEXT: unreachable
   %call1 = call i32* @srec16(i32* %call)
   %call2 = call i32* @srec16(i32* %call1)
   %call3 = call i32* @srec16(i32* %call2)
@@ -131,7 +131,7 @@ entry:
 ; }
 ;
 ; void *scc_C(short *a) {
-;   return scc_A((int*)(scc_C(a) ? scc_B((double*)a) : scc_C(a)));
+;   return scc_A((int*)(scc_A(a) ? scc_B((double*)a) : scc_C(a)));
 ; }
 define float* @scc_A(i32* %a) {
 entry:
@@ -183,8 +183,10 @@ cond.end:
 
 define i8* @scc_C(i16* %a) {
 entry:
-  %call = call i8* @scc_C(i16* %a)
-  %tobool = icmp ne i8* %call, null
+  %bc = bitcast i16* %a to i32*
+  %call = call float* @scc_A(i32* %bc)
+  %bc2 = bitcast float* %call to i8*
+  %tobool = icmp ne i8* %bc2, null
   br i1 %tobool, label %cond.true, label %cond.false
 
 cond.true:                                        ; preds = %entry

Modified: llvm/trunk/test/Transforms/FunctionAttrs/arg_returned.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Transforms/FunctionAttrs/arg_returned.ll?rev=367948&r1=367947&r2=367948&view=diff
==============================================================================
--- llvm/trunk/test/Transforms/FunctionAttrs/arg_returned.ll (original)
+++ llvm/trunk/test/Transforms/FunctionAttrs/arg_returned.ll Mon Aug  5 16:22:05 2019
@@ -259,10 +259,9 @@ return:
 ;   return *a ? a : rt0(a);
 ; }
 ;
-; FIXME: no-return missing
 ; FNATTR:  define i32* @rt0(i32* readonly %a)
-; BOTH: Function Attrs: nofree noinline nosync nounwind readonly uwtable
-; BOTH-NEXT:    define i32* @rt0(i32* readonly returned %a)
+; BOTH: Function Attrs: nofree noinline noreturn nosync nounwind readonly uwtable
+; BOTH-NEXT:    define i32* @rt0(i32* readonly %a)
 define i32* @rt0(i32* %a) #0 {
 entry:
   %v = load i32, i32* %a, align 4
@@ -278,9 +277,8 @@ entry:
 ;   return *a ? undef : rt1(a);
 ; }
 ;
-; FIXME: no-return missing
 ; FNATTR:  define noalias i32* @rt1(i32* nocapture readonly %a)
-; BOTH: Function Attrs: nofree noinline nosync nounwind readonly uwtable
+; BOTH: Function Attrs: nofree noinline noreturn nosync nounwind readonly uwtable
 ; BOTH-NEXT:    define noalias i32* @rt1(i32* nocapture readonly %a)
 define i32* @rt1(i32* %a) #0 {
 entry:
@@ -746,7 +744,7 @@ attributes #0 = { noinline nounwind uwta
 ; BOTH-NOT: attributes #
 ; BOTH-DAG: attributes #{{[0-9]*}} = { nofree noinline norecurse nosync nounwind readnone uwtable willreturn }
 ; BOTH-DAG: attributes #{{[0-9]*}} = { nofree noinline nosync nounwind readnone uwtable }
-; BOTH-DAG: attributes #{{[0-9]*}} = { nofree noinline nosync nounwind readonly uwtable }
+; BOTH-DAG: attributes #{{[0-9]*}} = { nofree noinline noreturn nosync nounwind readonly uwtable }
 ; BOTH-DAG: attributes #{{[0-9]*}} = { noinline nounwind uwtable }
 ; BOTH-DAG: attributes #{{[0-9]*}} = { nofree noinline nosync nounwind readnone uwtable willreturn }
 ; BOTH-DAG: attributes #{{[0-9]*}} = { nofree noinline nosync nounwind uwtable willreturn }

Modified: llvm/trunk/test/Transforms/FunctionAttrs/fn_noreturn.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Transforms/FunctionAttrs/fn_noreturn.ll?rev=367948&r1=367947&r2=367948&view=diff
==============================================================================
--- llvm/trunk/test/Transforms/FunctionAttrs/fn_noreturn.ll (original)
+++ llvm/trunk/test/Transforms/FunctionAttrs/fn_noreturn.ll Mon Aug  5 16:22:05 2019
@@ -1,26 +1,18 @@
 ; RUN: opt -functionattrs -attributor -attributor-disable=false -S < %s | FileCheck %s
-; RUN: opt -functionattrs -attributor -attributor-disable=false -attributor-verify=true -S < %s | FileCheck %s
 ;
 ; Test cases specifically designed for the "no-return" function attribute.
 ; We use FIXME's to indicate problems and missing attributes.
-;
-; TEST 1: singleton SCC void return type
-; TEST 2: singleton SCC int return type with a lot of recursive calls
-; TEST 3: endless loop, no return instruction
-; TEST 4: endless loop, dead return instruction
-; TEST 5: all paths contain a no-return function call
-;
+
 target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
 
 
-; TEST 1
+; TEST 1, singleton SCC void return type
 ;
 ; void srec0() {
 ;   return srec0();
 ; }
 ;
-; FIXME: no-return missing
-; CHECK: Function Attrs: nofree noinline nosync nounwind readnone uwtable
+; CHECK: Function Attrs: nofree noinline noreturn nosync nounwind readnone uwtable
 ; CHECK: define void @srec0()
 ;
 define void @srec0() #0 {
@@ -30,14 +22,13 @@ entry:
 }
 
 
-; TEST 2
+; TEST 2: singleton SCC int return type with a lot of recursive calls
 ;
 ; int srec16(int a) {
 ;   return srec16(srec16(srec16(srec16(srec16(srec16(srec16(srec16(srec16(srec16(srec16(srec16(srec16(srec16(srec16(srec16(a))))))))))))))));
 ; }
 ;
-; FIXME: no-return missing
-; CHECK: Function Attrs: nofree noinline nosync nounwind readnone uwtable
+; CHECK: Function Attrs: nofree noinline noreturn nosync nounwind readnone uwtable
 ; CHECK: define i32 @srec16(i32 %a)
 ;
 define i32 @srec16(i32 %a) #0 {
@@ -58,18 +49,20 @@ entry:
   %call13 = call i32 @srec16(i32 %call12)
   %call14 = call i32 @srec16(i32 %call13)
   %call15 = call i32 @srec16(i32 %call14)
+  br label %exit
+
+exit:
   ret i32 %call15
 }
 
 
-; TEST 3
+; TEST 3: endless loop, no return instruction
 ;
 ; int endless_loop(int a) {
 ;   while (1);
 ; }
 ;
-; FIXME: no-return missing
-; CHECK: Function Attrs: nofree noinline norecurse nosync nounwind readnone uwtable
+; CHECK: Function Attrs: nofree noinline norecurse noreturn nosync nounwind readnone uwtable
 ; CHECK: define i32 @endless_loop(i32 %a)
 ;
 define i32 @endless_loop(i32 %a) #0 {
@@ -81,15 +74,15 @@ while.body:
 }
 
 
-; TEST 4
+; TEST 4: endless loop, dead return instruction
 ;
 ; int endless_loop(int a) {
 ;   while (1);
 ;   return a;
 ; }
 ;
-; FIXME: no-return missing
-; CHECK: Function Attrs: nofree noinline norecurse nosync nounwind readnone uwtable
+; FIXME: no-return missing (D65243 should fix this)
+; CHECK: Function Attrs: nofree noinline norecurse noreturn nosync nounwind readnone uwtable
 ; CHECK: define i32 @dead_return(i32 returned %a)
 ;
 define i32 @dead_return(i32 %a) #0 {
@@ -104,14 +97,13 @@ return:
 }
 
 
-; TEST 5
+; TEST 5: all paths contain a no-return function call
 ;
 ; int multiple_noreturn_calls(int a) {
 ;   return a == 0 ? endless_loop(a) : srec16(a);
 ; }
 ;
-; FIXME: no-return missing
-; CHECK: Function Attrs: nofree noinline nosync nounwind readnone uwtable
+; CHECK: Function Attrs: nofree noinline noreturn nosync nounwind readnone uwtable
 ; CHECK: define i32 @multiple_noreturn_calls(i32 %a)
 ;
 define i32 @multiple_noreturn_calls(i32 %a) #0 {

Modified: llvm/trunk/test/Transforms/FunctionAttrs/liveness.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Transforms/FunctionAttrs/liveness.ll?rev=367948&r1=367947&r2=367948&view=diff
==============================================================================
--- llvm/trunk/test/Transforms/FunctionAttrs/liveness.ll (original)
+++ llvm/trunk/test/Transforms/FunctionAttrs/liveness.ll Mon Aug  5 16:22:05 2019
@@ -26,7 +26,7 @@ define internal i32 @internal_load(i32*)
 }
 ; TEST 1: Only first block is live.
 
-; CHECK: Function Attrs: nofree nosync nounwind
+; CHECK: Function Attrs: nofree noreturn nosync nounwind
 ; CHECK-NEXT: define i32 @first_block_no_return(i32 %a, i32* nonnull %ptr1, i32* %ptr2)
 define i32 @first_block_no_return(i32 %a, i32* nonnull %ptr1, i32* %ptr2) #0 {
 entry:

Modified: llvm/trunk/test/Transforms/FunctionAttrs/nofree-attributor.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Transforms/FunctionAttrs/nofree-attributor.ll?rev=367948&r1=367947&r2=367948&view=diff
==============================================================================
--- llvm/trunk/test/Transforms/FunctionAttrs/nofree-attributor.ll (original)
+++ llvm/trunk/test/Transforms/FunctionAttrs/nofree-attributor.ll Mon Aug  5 16:22:05 2019
@@ -67,8 +67,15 @@ define void @free_in_scc1(i8* nocapture
 ; ATTRIBUTOR-NOT: nofree
 ; ATTRIBUTOR: define void @free_in_scc2(i8* nocapture %0) local_unnamed_addr
 define void @free_in_scc2(i8* nocapture %0) local_unnamed_addr #0 {
-  tail call void @free_in_scc1(i8* %0)
+  %cmp = icmp eq i8* %0, null
+  br i1 %cmp, label %rec, label %call
+call:
   tail call void @free(i8* %0) #1
+  br label %end
+rec:
+  tail call void @free_in_scc1(i8* %0)
+  br label %end
+end:
   ret void
 }
 
@@ -85,7 +92,7 @@ define void @free_in_scc2(i8* nocapture
 
 ; FNATTR: Function Attrs: noinline nounwind readnone uwtable
 ; FNATTR-NEXT: define void @mutual_recursion1()
-; ATTRIBUTOR: Function Attrs: nofree noinline nosync nounwind uwtable
+; ATTRIBUTOR: Function Attrs: nofree noinline noreturn nosync nounwind uwtable
 ; ATTRIBUTOR-NEXT: define void @mutual_recursion1()
 define void @mutual_recursion1() #0 {
   call void @mutual_recursion2()
@@ -94,7 +101,7 @@ define void @mutual_recursion1() #0 {
 
 ; FNATTR: Function Attrs: noinline nounwind readnone uwtable
 ; FNATTR-NEXT: define void @mutual_recursion2()
-; ATTRIBUTOR: Function Attrs: nofree noinline nosync nounwind uwtable
+; ATTRIBUTOR: Function Attrs: nofree noinline noreturn nosync nounwind uwtable
 ; ATTRIBUTOR-NEXT: define void @mutual_recursion2()
 define void @mutual_recursion2() #0 {
   call void @mutual_recursion1()

Modified: llvm/trunk/test/Transforms/FunctionAttrs/nonnull.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Transforms/FunctionAttrs/nonnull.ll?rev=367948&r1=367947&r2=367948&view=diff
==============================================================================
--- llvm/trunk/test/Transforms/FunctionAttrs/nonnull.ll (original)
+++ llvm/trunk/test/Transforms/FunctionAttrs/nonnull.ll Mon Aug  5 16:22:05 2019
@@ -21,16 +21,20 @@ define i8* @test2(i8* nonnull %p) {
 
 ; Given an SCC where one of the functions can not be marked nonnull,
 ; can we still mark the other one which is trivially nonnull
-define i8* @scc_binder() {
+define i8* @scc_binder(i1 %c) {
 ; FNATTR: define i8* @scc_binder
 ; ATTRIBUTOR: define noalias i8* @scc_binder
-  call i8* @test3()
+  br i1 %c, label %rec, label %end
+rec:
+  call i8* @test3(i1 %c)
+  br label %end
+end:
   ret i8* null
 }
 
-define i8* @test3() {
+define i8* @test3(i1 %c) {
 ; BOTH: define nonnull i8* @test3
-  call i8* @scc_binder()
+  call i8* @scc_binder(i1 %c)
   %ret = call i8* @ret_nonnull()
   ret i8* %ret
 }
@@ -54,17 +58,21 @@ define i8* @test4() {
 
 ; Given a mutual recursive set of functions which *can* return null
 ; make sure we haven't marked them as nonnull.
-define i8* @test5_helper() {
+define i8* @test5_helper(i1 %c) {
 ; FNATTR: define noalias i8* @test5_helper
 ; ATTRIBUTOR: define noalias i8* @test5_helper
-  %ret = call i8* @test5()
+  br i1 %c, label %rec, label %end
+rec:
+  %ret = call i8* @test5(i1 %c)
+  br label %end
+end:
   ret i8* null
 }
 
-define i8* @test5() {
+define i8* @test5(i1 %c) {
 ; FNATTR: define noalias i8* @test5
 ; ATTRIBUTOR: define noalias i8* @test5
-  %ret = call i8* @test5_helper()
+  %ret = call i8* @test5_helper(i1 %c)
   ret i8* %ret
 }
 

Added: llvm/trunk/test/Transforms/FunctionAttrs/noreturn_async.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Transforms/FunctionAttrs/noreturn_async.ll?rev=367948&view=auto
==============================================================================
--- llvm/trunk/test/Transforms/FunctionAttrs/noreturn_async.ll (added)
+++ llvm/trunk/test/Transforms/FunctionAttrs/noreturn_async.ll Mon Aug  5 16:22:05 2019
@@ -0,0 +1,142 @@
+; RUN: opt -functionattrs -attributor -attributor-disable=false -S < %s | FileCheck %s
+;
+; This file is the same as noreturn_sync.ll but with a personality which
+; indicates that the exception handler *can* catch asynchronous exceptions. As
+; a consequence, invokes to noreturn and nounwind functions are not translated
+; to calls followed by an unreachable but the unwind edge is considered live.
+;
+; https://reviews.llvm.org/D59978#inline-586873
+;
+; Make sure we handle invoke of a noreturn function correctly.
+;
+; This test is also a reminder of how we handle (=ignore) stackoverflow exception handling.
+;
+target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-pc-windows-msvc19.16.27032"
+
+@"??_C at _0BG@CMNEKHOP at Exception?5NOT?5caught?6?$AA@" = linkonce_odr dso_local unnamed_addr constant [22 x i8] c"Exception NOT caught\0A\00", align 1
+@"??_C at _0BC@NKPAGFFJ at Exception?5caught?6?$AA@" = linkonce_odr dso_local unnamed_addr constant [18 x i8] c"Exception caught\0A\00", align 1
+@"??_C at _0BK@JHJLGDKL at Done?5execution?5result?$DN?$CFi?6?$AA@" = linkonce_odr dso_local unnamed_addr constant [26 x i8] c"Done execution result=%i\0A\00", align 1
+@"?_OptionsStorage@?1??__local_stdio_printf_options@@9 at 4_KA" = linkonce_odr dso_local global i64 0, align 8
+
+
+define dso_local void @"?overflow@@YAXXZ"() {
+entry:
+; CHECK:      Function Attrs: nofree noreturn nosync nounwind
+; CHECK-NEXT: define
+; CHECK-NEXT:   entry:
+; CHECK-NEXT:   call void @"?overflow@@YAXXZ"()
+; CHECK-NEXT:   unreachable
+  call void @"?overflow@@YAXXZ"()
+  %call3 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([18 x i8], [18 x i8]* @"??_C at _0BC@NKPAGFFJ at Exception?5caught?6?$AA@", i64 0, i64 0))
+  ret void
+}
+
+
+; CHECK-NOT:    nounwind
+; CHECK-NOT:    noreturn
+; CHECK:        define
+; CHECK-SAME:   @"?catchoverflow@@YAHXZ"()
+define dso_local i32 @"?catchoverflow@@YAHXZ"()  personality i8* bitcast (i32 (...)* @__C_specific_handler to i8*) {
+entry:
+  %retval = alloca i32, align 4
+  %__exception_code = alloca i32, align 4
+; CHECK: invoke void @"?overflow@@YAXXZ"()
+; CHECK:          to label %invoke.cont unwind label %catch.dispatch
+  invoke void @"?overflow@@YAXXZ"()
+          to label %invoke.cont unwind label %catch.dispatch
+
+invoke.cont:                                      ; preds = %entry
+; CHECK:      invoke.cont:
+; CHECK-NEXT: unreachable
+  br label %invoke.cont1
+
+catch.dispatch:                                   ; preds = %invoke.cont, %entry
+  %0 = catchswitch within none [label %__except] unwind to caller
+
+__except:                                         ; preds = %catch.dispatch
+  %1 = catchpad within %0 [i8* null]
+  catchret from %1 to label %__except2
+
+__except2:                                        ; preds = %__except
+  %2 = call i32 @llvm.eh.exceptioncode(token %1)
+  store i32 1, i32* %retval, align 4
+  br label %return
+
+invoke.cont1:                                     ; preds = %invoke.cont
+  store i32 0, i32* %retval, align 4
+  br label %return
+
+__try.cont:                                       ; No predecessors!
+  store i32 2, i32* %retval, align 4
+  br label %return
+
+return:                                           ; preds = %__try.cont, %__except2, %invoke.cont1
+  %3 = load i32, i32* %retval, align 4
+  ret i32 %3
+}
+
+
+define dso_local void @"?overflow@@YAXXZ_may_throw"()  {
+entry:
+; CHECK:      Function Attrs: noreturn
+; CHECK-NOT:  nounwind
+; CHECK-NEXT: define
+; CHECK-NEXT:   entry:
+; CHECK-NEXT:   %call3 = call i32 (i8*, ...) @printf(i8* dereferenceable_or_null(18) getelementptr inbounds ([18 x i8], [18 x i8]* @"??_C at _0BC@NKPAGFFJ at Exception?5caught?6?$AA@", i64 0, i64 0))
+; CHECK-NEXT:   call void @"?overflow@@YAXXZ_may_throw"()
+; CHECK-NEXT:   unreachable
+  %call3 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([18 x i8], [18 x i8]* @"??_C at _0BC@NKPAGFFJ at Exception?5caught?6?$AA@", i64 0, i64 0))
+  call void @"?overflow@@YAXXZ_may_throw"()
+  ret void
+}
+
+
+; CHECK-NOT:    nounwind
+; CHECK-NOT:    noreturn
+; CHECK:        define
+; CHECK-SAME:   @"?catchoverflow@@YAHXZ_may_throw"()
+define dso_local i32 @"?catchoverflow@@YAHXZ_may_throw"()  personality i8* bitcast (i32 (...)* @__C_specific_handler to i8*) {
+entry:
+  %retval = alloca i32, align 4
+  %__exception_code = alloca i32, align 4
+; CHECK: invoke void @"?overflow@@YAXXZ_may_throw"() 
+; CHECK:          to label %invoke.cont unwind label %catch.dispatch
+  invoke void @"?overflow@@YAXXZ_may_throw"() 
+          to label %invoke.cont unwind label %catch.dispatch
+
+invoke.cont:                                      ; preds = %entry
+; CHECK:      invoke.cont:
+; CHECK-NEXT: unreachable
+  br label %invoke.cont1
+
+catch.dispatch:                                   ; preds = %invoke.cont, %entry
+  %0 = catchswitch within none [label %__except] unwind to caller
+
+__except:                                         ; preds = %catch.dispatch
+  %1 = catchpad within %0 [i8* null]
+  catchret from %1 to label %__except2
+
+__except2:                                        ; preds = %__except
+  %2 = call i32 @llvm.eh.exceptioncode(token %1)
+  store i32 1, i32* %retval, align 4
+  br label %return
+
+invoke.cont1:                                     ; preds = %invoke.cont
+  store i32 0, i32* %retval, align 4
+  br label %return
+
+__try.cont:                                       ; No predecessors!
+  store i32 2, i32* %retval, align 4
+  br label %return
+
+return:                                           ; preds = %__try.cont, %__except2, %invoke.cont1
+  %3 = load i32, i32* %retval, align 4
+  ret i32 %3
+}
+
+declare dso_local i32 @__C_specific_handler(...)
+
+declare dso_local i32 @printf(i8* %_Format, ...)
+
+declare i32 @llvm.eh.exceptioncode(token)

Added: llvm/trunk/test/Transforms/FunctionAttrs/noreturn_sync.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Transforms/FunctionAttrs/noreturn_sync.ll?rev=367948&view=auto
==============================================================================
--- llvm/trunk/test/Transforms/FunctionAttrs/noreturn_sync.ll (added)
+++ llvm/trunk/test/Transforms/FunctionAttrs/noreturn_sync.ll Mon Aug  5 16:22:05 2019
@@ -0,0 +1,138 @@
+; RUN: opt -functionattrs -attributor -attributor-disable=false -S < %s | FileCheck %s
+;
+; This file is the same as noreturn_async.ll but with a personality which
+; indicates that the exception handler *cannot* catch asynchronous exceptions.
+; As a consequence, invokes to noreturn and nounwind functions are translated
+; to calls followed by an unreachable.
+;
+; https://reviews.llvm.org/D59978#inline-586873
+;
+; Make sure we handle invoke of a noreturn function correctly.
+;
+; This test is also a reminder of how we handle (=ignore) stackoverflow exception handling.
+;
+target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-pc-linux-gnu"
+
+@"??_C at _0BG@CMNEKHOP at Exception?5NOT?5caught?6?$AA@" = linkonce_odr dso_local unnamed_addr constant [22 x i8] c"Exception NOT caught\0A\00", align 1
+@"??_C at _0BC@NKPAGFFJ at Exception?5caught?6?$AA@" = linkonce_odr dso_local unnamed_addr constant [18 x i8] c"Exception caught\0A\00", align 1
+@"??_C at _0BK@JHJLGDKL at Done?5execution?5result?$DN?$CFi?6?$AA@" = linkonce_odr dso_local unnamed_addr constant [26 x i8] c"Done execution result=%i\0A\00", align 1
+@"?_OptionsStorage@?1??__local_stdio_printf_options@@9 at 4_KA" = linkonce_odr dso_local global i64 0, align 8
+
+
+define dso_local void @"?overflow@@YAXXZ"() {
+entry:
+; CHECK:      Function Attrs: nofree noreturn nosync nounwind
+; CHECK-NEXT: define
+; CHECK-NEXT:   entry:
+; CHECK-NEXT:   call void @"?overflow@@YAXXZ"()
+; CHECK-NEXT:   unreachable
+  call void @"?overflow@@YAXXZ"()
+  %call3 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([18 x i8], [18 x i8]* @"??_C at _0BC@NKPAGFFJ at Exception?5caught?6?$AA@", i64 0, i64 0))
+  ret void
+}
+
+
+; CHECK:       Function Attrs: nofree noreturn nosync nounwind
+; CHECK-NEXT:   @"?catchoverflow@@YAHXZ"()
+define dso_local i32 @"?catchoverflow@@YAHXZ"()  personality i8* bitcast (i32 (...)* @__gcc_personality_v0 to i8*) {
+entry:
+  %retval = alloca i32, align 4
+  %__exception_code = alloca i32, align 4
+  invoke void @"?overflow@@YAXXZ"() 
+          to label %invoke.cont unwind label %catch.dispatch
+; CHECK:      call void @"?overflow@@YAXXZ"()
+; CHECK-NEXT: unreachable
+
+invoke.cont:                                      ; preds = %entry
+  br label %invoke.cont1
+
+catch.dispatch:                                   ; preds = %invoke.cont, %entry
+  %0 = catchswitch within none [label %__except] unwind to caller
+
+__except:                                         ; preds = %catch.dispatch
+  %1 = catchpad within %0 [i8* null]
+  catchret from %1 to label %__except2
+
+__except2:                                        ; preds = %__except
+  %2 = call i32 @llvm.eh.exceptioncode(token %1)
+  store i32 1, i32* %retval, align 4
+  br label %return
+
+invoke.cont1:                                     ; preds = %invoke.cont
+  store i32 0, i32* %retval, align 4
+  br label %return
+
+__try.cont:                                       ; No predecessors!
+  store i32 2, i32* %retval, align 4
+  br label %return
+
+return:                                           ; preds = %__try.cont, %__except2, %invoke.cont1
+  %3 = load i32, i32* %retval, align 4
+  ret i32 %3
+}
+
+
+define dso_local void @"?overflow@@YAXXZ_may_throw"()  {
+entry:
+; CHECK:      Function Attrs: noreturn
+; CHECK-NOT:  nounwind
+; CHECK-NEXT: define
+; CHECK-NEXT:   entry:
+; CHECK-NEXT:   %call3 = call i32 (i8*, ...) @printf(i8* dereferenceable_or_null(18) getelementptr inbounds ([18 x i8], [18 x i8]* @"??_C at _0BC@NKPAGFFJ at Exception?5caught?6?$AA@", i64 0, i64 0))
+; CHECK-NEXT:   call void @"?overflow@@YAXXZ_may_throw"()
+; CHECK-NEXT:   unreachable
+  %call3 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([18 x i8], [18 x i8]* @"??_C at _0BC@NKPAGFFJ at Exception?5caught?6?$AA@", i64 0, i64 0))
+  call void @"?overflow@@YAXXZ_may_throw"()
+  ret void
+}
+
+
+; CHECK-NOT:    nounwind
+; CHECK-NOT:    noreturn
+; CHECK:        define
+; CHECK-SAME:   @"?catchoverflow@@YAHXZ_may_throw"()
+define dso_local i32 @"?catchoverflow@@YAHXZ_may_throw"()  personality i8* bitcast (i32 (...)* @__gcc_personality_v0 to i8*) {
+entry:
+  %retval = alloca i32, align 4
+  %__exception_code = alloca i32, align 4
+; CHECK: invoke void @"?overflow@@YAXXZ_may_throw"() 
+; CHECK:          to label %invoke.cont unwind label %catch.dispatch
+  invoke void @"?overflow@@YAXXZ_may_throw"() 
+          to label %invoke.cont unwind label %catch.dispatch
+
+invoke.cont:                                      ; preds = %entry
+; CHECK:      invoke.cont:
+; CHECK-NEXT: unreachable
+  br label %invoke.cont1
+
+catch.dispatch:                                   ; preds = %invoke.cont, %entry
+  %0 = catchswitch within none [label %__except] unwind to caller
+
+__except:                                         ; preds = %catch.dispatch
+  %1 = catchpad within %0 [i8* null]
+  catchret from %1 to label %__except2
+
+__except2:                                        ; preds = %__except
+  %2 = call i32 @llvm.eh.exceptioncode(token %1)
+  store i32 1, i32* %retval, align 4
+  br label %return
+
+invoke.cont1:                                     ; preds = %invoke.cont
+  store i32 0, i32* %retval, align 4
+  br label %return
+
+__try.cont:                                       ; No predecessors!
+  store i32 2, i32* %retval, align 4
+  br label %return
+
+return:                                           ; preds = %__try.cont, %__except2, %invoke.cont1
+  %3 = load i32, i32* %retval, align 4
+  ret i32 %3
+}
+
+declare dso_local i32 @__gcc_personality_v0(...)
+
+declare dso_local i32 @printf(i8* %_Format, ...)
+
+declare i32 @llvm.eh.exceptioncode(token)

Modified: llvm/trunk/test/Transforms/FunctionAttrs/nosync.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Transforms/FunctionAttrs/nosync.ll?rev=367948&r1=367947&r2=367948&view=diff
==============================================================================
--- llvm/trunk/test/Transforms/FunctionAttrs/nosync.ll (original)
+++ llvm/trunk/test/Transforms/FunctionAttrs/nosync.ll Mon Aug  5 16:22:05 2019
@@ -180,13 +180,12 @@ define void @call_might_sync() nounwind
   ret void
 }
 
-; TEST 11 - negative, should not deduce nosync
-; volatile operation in same scc. Call volatile_load defined in TEST 8.
+; TEST 11 - positive, should deduce nosync
+; volatile operation in same scc but dead. Call volatile_load defined in TEST 8.
 
 ; FNATTR: Function Attrs: nofree noinline nounwind uwtable
 ; FNATTR-NEXT: define i32 @scc1(i32* %0)
-; ATTRIBUTOR: Function Attrs: nofree noinline nounwind uwtable
-; ATTRIBUTOR-NOT: nosync
+; ATTRIBUTOR: Function Attrs: nofree noinline noreturn nosync nounwind uwtable
 ; ATTRIBUTOR-NEXT: define i32 @scc1(i32* %0)
 define i32 @scc1(i32* %0) noinline nounwind uwtable {
   tail call void @scc2(i32* %0);
@@ -196,8 +195,7 @@ define i32 @scc1(i32* %0) noinline nounw
 
 ; FNATTR: Function Attrs: nofree noinline nounwind uwtable
 ; FNATTR-NEXT: define void @scc2(i32* %0)
-; ATTRIBUTOR: Function Attrs: nofree noinline nounwind uwtable
-; ATTRIBUTOR-NOT: nosync
+; ATTRIBUTOR: Function Attrs: nofree noinline noreturn nosync nounwind uwtable
 ; ATTRIBUTOR-NEXT: define void @scc2(i32* %0)
 define void @scc2(i32* %0) noinline nounwind uwtable {
   tail call i32 @scc1(i32* %0);

Modified: llvm/trunk/test/Transforms/FunctionAttrs/nounwind.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Transforms/FunctionAttrs/nounwind.ll?rev=367948&r1=367947&r2=367948&view=diff
==============================================================================
--- llvm/trunk/test/Transforms/FunctionAttrs/nounwind.ll (original)
+++ llvm/trunk/test/Transforms/FunctionAttrs/nounwind.ll Mon Aug  5 16:22:05 2019
@@ -13,7 +13,7 @@ define i32 @foo1() {
 ; TEST 2
 ; CHECK: Function Attrs: nounwind readnone
 ; CHECK-NEXT: define i32 @scc1_foo()
-; ATTRIBUTOR: Function Attrs: nofree nosync nounwind
+; ATTRIBUTOR: Function Attrs: nofree noreturn nosync nounwind
 ; ATTRIBUTOR-NEXT: define i32 @scc1_foo()
 define i32 @scc1_foo() {
   %1 = call i32 @scc1_bar()
@@ -24,7 +24,7 @@ define i32 @scc1_foo() {
 ; TEST 3
 ; CHECK: Function Attrs: nounwind readnone
 ; CHECK-NEXT: define i32 @scc1_bar()
-; ATTRIBUTOR: Function Attrs: nofree nosync nounwind
+; ATTRIBUTOR: Function Attrs: nofree noreturn nosync nounwind
 ; ATTRIBUTOR-NEXT: define i32 @scc1_bar()
 define i32 @scc1_bar() {
   %1 = call i32 @scc1_foo()

Modified: llvm/trunk/test/Transforms/FunctionAttrs/willreturn.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Transforms/FunctionAttrs/willreturn.ll?rev=367948&r1=367947&r2=367948&view=diff
==============================================================================
--- llvm/trunk/test/Transforms/FunctionAttrs/willreturn.ll (original)
+++ llvm/trunk/test/Transforms/FunctionAttrs/willreturn.ll Mon Aug  5 16:22:05 2019
@@ -125,24 +125,28 @@ define i32 @fact_loop(i32 %0) local_unna
 
 ; FNATTR: Function Attrs: noinline nounwind readnone uwtable
 ; FNATTR-NOT: willreturn
-; FNATTR-NEXT: define void @mutual_recursion1()
+; FNATTR-NEXT: define void @mutual_recursion1(i1 %c)
 ; ATTRIBUTOR: Function Attrs: nofree noinline nosync nounwind uwtable
 ; ATTRIBUTOR-NOT: willreturn
-; ATTRIBUTOR-NEXT: define void @mutual_recursion1()
-define void @mutual_recursion1() #0 {
-  call void @mutual_recursion2()
+; ATTRIBUTOR-NEXT: define void @mutual_recursion1(i1 %c)
+define void @mutual_recursion1(i1 %c) #0 {
+  br i1 %c, label %rec, label %end
+rec:
+  call void @mutual_recursion2(i1 %c)
+  br label %end
+end:
   ret void
 }
 
 
 ; FNATTR: Function Attrs: noinline nounwind readnone uwtable
 ; FNATTR-NOT: willreturn
-; FNATTR-NEXT: define void @mutual_recursion2()
+; FNATTR-NEXT: define void @mutual_recursion2(i1 %c)
 ; ATTRIBUTOR: Function Attrs: nofree noinline nosync nounwind uwtable
 ; ATTRIBUTOR-NOT: willreturn
-; ATTRIBUTOR-NEXT: define void @mutual_recursion2()
-define void @mutual_recursion2() #0 {
-  call void @mutual_recursion1()
+; ATTRIBUTOR-NEXT: define void @mutual_recursion2(i1 %c)
+define void @mutual_recursion2(i1 %c) #0 {
+  call void @mutual_recursion1(i1 %c)
   ret void
 }
 
@@ -158,7 +162,7 @@ declare void @exit(i32 %0) local_unnamed
 ; FNATTR: Function Attrs: noinline nounwind uwtable
 ; FNATTR-NOT: willreturn
 ; FNATTR-NEXT: define void @only_exit()
-; ATTRIBUTOR: Function Attrs: noinline nounwind uwtable
+; ATTRIBUTOR: Function Attrs: noinline noreturn nounwind uwtable
 ; ATTRIBUTOR-NOT: willreturn
 ; ATTRIBUTOR-NEXT: define void @only_exit() local_unnamed_addr
 define void @only_exit() local_unnamed_addr #0 {
@@ -283,7 +287,7 @@ define void @f2() #0 {
 ; FNATTR: Function Attrs: noinline nounwind uwtable
 ; FNATTR-NOT: willreturn
 ; FNATTR-NEXT: define void @call_will_return_but_has_loop()
-; ATTRIBUTOR: Function Attrs: noinline nounwind uwtable
+; ATTRIBUTOR: Function Attrs: noinline noreturn nounwind uwtable
 ; ATTRIBUTOR-NOT: willreturn
 ; ATTRIBUTOR-NEXT: define void @call_will_return_but_has_loop()
 define void @call_will_return_but_has_loop() #0 {
@@ -499,7 +503,7 @@ unreachable_label:
 ; FNATTR: Function Attrs: noinline nounwind uwtable
 ; FNATTR-NOT: willreturn
 ; FNATTR-NEXT: define void @unreachable_exit_negative1()
-; ATTRIBUTOR: Function Attrs: noinline nounwind uwtable
+; ATTRIBUTOR: Function Attrs: noinline noreturn nounwind uwtable
 ; ATTRIBUTOR-NOT: willreturn
 ; ATTRIBUTOR-NEXT: define void @unreachable_exit_negative1()
 define void @unreachable_exit_negative1() #0 {
@@ -514,7 +518,7 @@ unreachable_label:
 ; FNATTR: Function Attrs: noinline nounwind uwtable
 ; FNATTR-NOT: willreturn
 ; FNATTR-NEXT: define void @unreachable_exit_negative2()
-; ATTRIBUTOR: Function Attrs: nofree noinline nosync nounwind uwtable
+; ATTRIBUTOR: Function Attrs: nofree noinline noreturn nosync nounwind uwtable
 ; ATTRIBUTOR-NOT: willreturn
 ; ATTRIBUTOR-NEXT: define void @unreachable_exit_negative2()
 define void @unreachable_exit_negative2() #0 {
@@ -539,7 +543,7 @@ declare void @llvm.eh.sjlj.longjmp(i8*)
 ; FNATTR: Function Attrs: noinline nounwind uwtable
 ; FNATTR-NOT: willreturn
 ; FNATTR-NEXT: define void @call_longjmp(i8* nocapture readnone %0) local_unnamed_addr #3 {
-; ATTRIBUTOR: Function Attrs: noinline nounwind uwtable
+; ATTRIBUTOR: Function Attrs: noinline noreturn nounwind uwtable
 ; ATTRIBUTOR-NOT: willreturn
 ; ATTRIBUTOR-NEXT: define void @call_longjmp(i8* nocapture readnone %0) local_unnamed_addr
 define void @call_longjmp(i8* nocapture readnone %0) local_unnamed_addr #0 {




More information about the llvm-commits mailing list