]> granicus.if.org Git - llvm/commitdiff
[Attributor] Deduce the "no-return" attribute for functions
authorJohannes Doerfert <jdoerfert@anl.gov>
Mon, 5 Aug 2019 23:22:05 +0000 (23:22 +0000)
committerJohannes Doerfert <jdoerfert@anl.gov>
Mon, 5 Aug 2019 23:22:05 +0000 (23:22 +0000)
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.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@367948 91177308-0d34-0410-b5e6-96231b3b80d8

13 files changed:
docs/LangRef.rst
lib/Transforms/IPO/Attributor.cpp
test/Transforms/FunctionAttrs/arg_nocapture.ll
test/Transforms/FunctionAttrs/arg_returned.ll
test/Transforms/FunctionAttrs/fn_noreturn.ll
test/Transforms/FunctionAttrs/liveness.ll
test/Transforms/FunctionAttrs/nofree-attributor.ll
test/Transforms/FunctionAttrs/nonnull.ll
test/Transforms/FunctionAttrs/noreturn_async.ll [new file with mode: 0644]
test/Transforms/FunctionAttrs/noreturn_sync.ll [new file with mode: 0644]
test/Transforms/FunctionAttrs/nosync.ll
test/Transforms/FunctionAttrs/nounwind.ll
test/Transforms/FunctionAttrs/willreturn.ll

index a6acdeaf85e51beb86497727b34b1acc2c9562d2..abbd7941b5d82ed0aaa446545a8f5a2e26ce07e3 100644 (file)
@@ -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
index 3ecad8b03ebabb545fd81825062b1df11f3c59ff..dc6bcb59d5d14564d2e060ed87859c5ade96b476 100644 (file)
@@ -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(AbstractAttribute::ManifestPosition MP,
   case Attribute::WillReturn:
     NumFnWillReturn++;
     break;
+  case Attribute::NoReturn:
+    NumFnNoReturn++;
+    return;
   case Attribute::NoAlias:
     NumFnArgumentNoAlias++;
     return;
@@ -2336,6 +2340,60 @@ ChangeStatus AAAlignCallSiteArgument::updateImpl(Attributor &A) {
                                      : 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::identifyDefaultAbstractAttributes(
   // 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()) {
index 94d692e1d7913f75e4f125d8b401aab36470cd04..917d037ab3aa02eef0943f7719f9c191d2b4fe32 100644 (file)
@@ -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:                                         ; preds = %cond.false, %cond.t
 
 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
index f7d1de0f5d9c10df6cfc95d18c4ddac7cf3a993a..1cd01a7a340732aa89a868f85c90b104c1b9b392 100644 (file)
@@ -259,10 +259,9 @@ return:                                           ; preds = %cond.end, %if.then3
 ;   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 uwtable }
 ; 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 }
index 9513ef8222a277ef554dc4144ca260de4332618c..885c5af4527aff76a1640a4ec844d32c03e6996d 100644 (file)
@@ -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:                                       ; preds = %entry, %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:                                           ; No predecessors!
 }
 
 
-; 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 {
index 166b15441e4af0152d05ac11eccfb6d3e7ee14e7..535ba8779e3ed0131da6bb5b325ae5d04812a231 100644 (file)
@@ -26,7 +26,7 @@ define internal i32 @internal_load(i32*) norecurse nounwind uwtable {
 }
 ; 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:
index ceb49d4a347b49b1985b50dd874b3960a6d04fd2..8ccb52fac6d704d8f149dd70a4c62016efffa5f5 100644 (file)
@@ -67,8 +67,15 @@ define void @free_in_scc1(i8* nocapture %0) local_unnamed_addr #0 {
 ; 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 %0) local_unnamed_addr #0 {
 
 ; 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()
index a236aeab8ac7ac63243795574b9684d22415447e..466276a062db29af95104810862802cdf5e48442 100644 (file)
@@ -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
 }
 
diff --git a/test/Transforms/FunctionAttrs/noreturn_async.ll b/test/Transforms/FunctionAttrs/noreturn_async.ll
new file mode 100644 (file)
index 0000000..44de35e
--- /dev/null
@@ -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@_0BG@CMNEKHOP@Exception?5NOT?5caught?6?$AA@" = linkonce_odr dso_local unnamed_addr constant [22 x i8] c"Exception NOT caught\0A\00", align 1
+@"??_C@_0BC@NKPAGFFJ@Exception?5caught?6?$AA@" = linkonce_odr dso_local unnamed_addr constant [18 x i8] c"Exception caught\0A\00", align 1
+@"??_C@_0BK@JHJLGDKL@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@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@_0BC@NKPAGFFJ@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@_0BC@NKPAGFFJ@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@_0BC@NKPAGFFJ@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)
diff --git a/test/Transforms/FunctionAttrs/noreturn_sync.ll b/test/Transforms/FunctionAttrs/noreturn_sync.ll
new file mode 100644 (file)
index 0000000..44d3a7b
--- /dev/null
@@ -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@_0BG@CMNEKHOP@Exception?5NOT?5caught?6?$AA@" = linkonce_odr dso_local unnamed_addr constant [22 x i8] c"Exception NOT caught\0A\00", align 1
+@"??_C@_0BC@NKPAGFFJ@Exception?5caught?6?$AA@" = linkonce_odr dso_local unnamed_addr constant [18 x i8] c"Exception caught\0A\00", align 1
+@"??_C@_0BK@JHJLGDKL@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@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@_0BC@NKPAGFFJ@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@_0BC@NKPAGFFJ@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@_0BC@NKPAGFFJ@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)
index 1d7f46af20304d740252d6b7a2ac1d37234792ba..c86facb819215c8306960ea236a49292d0d8ed6a 100644 (file)
@@ -180,13 +180,12 @@ define void @call_might_sync() nounwind uwtable noinline {
   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 nounwind uwtable {
 
 ; 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);
index fa2dddd31ae871448255cc24ab71abff3ed245aa..1c9d019960cb0a826d51d8b59d1961bc15984880 100644 (file)
@@ -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()
index 6d7d35a5108fd3cb1c1053d4d44b7ab3452d34bc..8ddcf7e7e15fb4a2ec900e7e5a6344939d9620b0 100644 (file)
@@ -125,24 +125,28 @@ define i32 @fact_loop(i32 %0) local_unnamed_addr #0 {
 
 ; 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_addr noreturn
 ; 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 {