[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.

llvm-svn: 367948
This commit is contained in:
Johannes Doerfert 2019-08-05 23:22:05 +00:00
parent a5c25c5d46
commit e83f303938
13 changed files with 420 additions and 69 deletions

View 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

View 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()) {

View 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

View 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 }

View 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 {

View 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:

View 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()

View 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
}

View File

@ -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)

View File

@ -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)

View 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);

View 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()

View 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 {