diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -1480,8 +1480,9 @@ 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 diff --git a/llvm/lib/Transforms/IPO/Attributor.cpp b/llvm/lib/Transforms/IPO/Attributor.cpp --- a/llvm/lib/Transforms/IPO/Attributor.cpp +++ b/llvm/lib/Transforms/IPO/Attributor.cpp @@ -75,6 +75,7 @@ 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. // @@ -178,6 +179,9 @@ case Attribute::WillReturn: NumFnWillReturn++; break; + case Attribute::NoReturn: + NumFnNoReturn++; + return; case Attribute::NoAlias: NumFnArgumentNoAlias++; return; @@ -2298,6 +2302,60 @@ : 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(*this, F); + if (!LivenessAA || + LivenessAA->isLiveInstSet(ReturnInsts.begin(), ReturnInsts.end())) + return indicatePessimisticFixpoint(); + + return ChangeStatus::UNCHANGED; + } +}; + /// ---------------------------------------------------------------------------- /// Attributor /// ---------------------------------------------------------------------------- @@ -2501,6 +2559,9 @@ // 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()) { diff --git a/llvm/test/Transforms/FunctionAttrs/arg_nocapture.ll b/llvm/test/Transforms/FunctionAttrs/arg_nocapture.ll --- a/llvm/test/Transforms/FunctionAttrs/arg_nocapture.ll +++ b/llvm/test/Transforms/FunctionAttrs/arg_nocapture.ll @@ -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 @@ ; ; 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 @@ ; } ; ; 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 @@ 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 diff --git a/llvm/test/Transforms/FunctionAttrs/arg_returned.ll b/llvm/test/Transforms/FunctionAttrs/arg_returned.ll --- a/llvm/test/Transforms/FunctionAttrs/arg_returned.ll +++ b/llvm/test/Transforms/FunctionAttrs/arg_returned.ll @@ -259,10 +259,9 @@ ; 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 @@ ; 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 @@ ; 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 } diff --git a/llvm/test/Transforms/FunctionAttrs/fn_noreturn.ll b/llvm/test/Transforms/FunctionAttrs/fn_noreturn.ll --- a/llvm/test/Transforms/FunctionAttrs/fn_noreturn.ll +++ b/llvm/test/Transforms/FunctionAttrs/fn_noreturn.ll @@ -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 @@ } -; 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 @@ %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 @@ } -; 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 @@ } -; 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 { diff --git a/llvm/test/Transforms/FunctionAttrs/liveness.ll b/llvm/test/Transforms/FunctionAttrs/liveness.ll --- a/llvm/test/Transforms/FunctionAttrs/liveness.ll +++ b/llvm/test/Transforms/FunctionAttrs/liveness.ll @@ -26,7 +26,7 @@ } ; 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: diff --git a/llvm/test/Transforms/FunctionAttrs/nofree-attributor.ll b/llvm/test/Transforms/FunctionAttrs/nofree-attributor.ll --- a/llvm/test/Transforms/FunctionAttrs/nofree-attributor.ll +++ b/llvm/test/Transforms/FunctionAttrs/nofree-attributor.ll @@ -67,8 +67,15 @@ ; ATTRIBUTOR-NOT: nofree ; ATTRIBUTOR: define void @free_in_scc2(i8* nocapture) local_unnamed_addr define void @free_in_scc2(i8* nocapture) 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 @@ ; 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 @@ ; 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() diff --git a/llvm/test/Transforms/FunctionAttrs/nonnull.ll b/llvm/test/Transforms/FunctionAttrs/nonnull.ll --- a/llvm/test/Transforms/FunctionAttrs/nonnull.ll +++ b/llvm/test/Transforms/FunctionAttrs/nonnull.ll @@ -21,16 +21,20 @@ ; 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 @@ ; 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/llvm/test/Transforms/FunctionAttrs/noreturn_stackoverflow.ll b/llvm/test/Transforms/FunctionAttrs/noreturn_stackoverflow.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/FunctionAttrs/noreturn_stackoverflow.ll @@ -0,0 +1,133 @@ +; RUN: opt -functionattrs -attributor -attributor-disable=false -S < %s | FileCheck %s +; +; 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: Function Attrs: nofree noreturn nosync nounwind +; CHECK-NEXT: @"?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 + 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 (...)* @__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/llvm/test/Transforms/FunctionAttrs/nosync.ll b/llvm/test/Transforms/FunctionAttrs/nosync.ll --- a/llvm/test/Transforms/FunctionAttrs/nosync.ll +++ b/llvm/test/Transforms/FunctionAttrs/nosync.ll @@ -180,13 +180,12 @@ 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*) -; 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*) define i32 @scc1(i32*) noinline nounwind uwtable { tail call void @scc2(i32* %0); @@ -196,8 +195,7 @@ ; FNATTR: Function Attrs: nofree noinline nounwind uwtable ; FNATTR-NEXT: define void @scc2(i32*) -; 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*) define void @scc2(i32*) noinline nounwind uwtable { tail call i32 @scc1(i32* %0); diff --git a/llvm/test/Transforms/FunctionAttrs/nounwind.ll b/llvm/test/Transforms/FunctionAttrs/nounwind.ll --- a/llvm/test/Transforms/FunctionAttrs/nounwind.ll +++ b/llvm/test/Transforms/FunctionAttrs/nounwind.ll @@ -13,7 +13,7 @@ ; 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 @@ ; 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() diff --git a/llvm/test/Transforms/FunctionAttrs/willreturn.ll b/llvm/test/Transforms/FunctionAttrs/willreturn.ll --- a/llvm/test/Transforms/FunctionAttrs/willreturn.ll +++ b/llvm/test/Transforms/FunctionAttrs/willreturn.ll @@ -125,24 +125,28 @@ ; 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 @@ ; 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 @@ ; 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 @@ ; 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 @@ ; 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 @@ ; FNATTR: Function Attrs: noinline nounwind uwtable ; FNATTR-NOT: willreturn ; FNATTR-NEXT: define void @call_longjmp(i8* nocapture readnone) 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) local_unnamed_addr define void @call_longjmp(i8* nocapture readnone) local_unnamed_addr #0 {