; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes ; RUN: opt -passes=function-attrs -S %s | FileCheck --check-prefixes=COMMON,FNATTRS %s ; RUN: opt -passes=attributor-light -S %s | FileCheck --check-prefixes=COMMON,ATTRIBUTOR %s define void @mustprogress_readnone() mustprogress { ; FNATTRS: Function Attrs: mustprogress nofree norecurse noreturn nosync nounwind willreturn memory(none) ; FNATTRS-LABEL: @mustprogress_readnone( ; FNATTRS-NEXT: entry: ; FNATTRS-NEXT: br label [[WHILE_BODY:%.*]] ; FNATTRS: while.body: ; FNATTRS-NEXT: br label [[WHILE_BODY]] ; ; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse noreturn nosync nounwind memory(none) ; ATTRIBUTOR-LABEL: @mustprogress_readnone( ; ATTRIBUTOR-NEXT: entry: ; ATTRIBUTOR-NEXT: br label [[WHILE_BODY:%.*]] ; ATTRIBUTOR: while.body: ; ATTRIBUTOR-NEXT: br label [[WHILE_BODY]] ; entry: br label %while.body while.body: br label %while.body } define i32 @mustprogress_load(ptr %ptr) mustprogress { ; FNATTRS: Function Attrs: mustprogress nofree norecurse noreturn nosync nounwind willreturn memory(argmem: read) ; FNATTRS-LABEL: @mustprogress_load( ; FNATTRS-NEXT: entry: ; FNATTRS-NEXT: br label [[WHILE_BODY:%.*]] ; FNATTRS: while.body: ; FNATTRS-NEXT: [[R:%.*]] = load i32, ptr [[PTR:%.*]], align 4 ; FNATTRS-NEXT: br label [[WHILE_BODY]] ; ; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse noreturn nosync nounwind memory(argmem: read) ; ATTRIBUTOR-LABEL: @mustprogress_load( ; ATTRIBUTOR-NEXT: entry: ; ATTRIBUTOR-NEXT: br label [[WHILE_BODY:%.*]] ; ATTRIBUTOR: while.body: ; ATTRIBUTOR-NEXT: [[R:%.*]] = load i32, ptr [[PTR:%.*]], align 4 ; ATTRIBUTOR-NEXT: br label [[WHILE_BODY]] ; entry: br label %while.body while.body: %r = load i32, ptr %ptr br label %while.body } define void @mustprogress_store(ptr %ptr) mustprogress { ; COMMON: Function Attrs: mustprogress nofree norecurse noreturn nosync nounwind memory(argmem: write) ; COMMON-LABEL: @mustprogress_store( ; COMMON-NEXT: entry: ; COMMON-NEXT: br label [[WHILE_BODY:%.*]] ; COMMON: while.body: ; COMMON-NEXT: store i32 0, ptr [[PTR:%.*]], align 4 ; COMMON-NEXT: br label [[WHILE_BODY]] ; entry: br label %while.body while.body: store i32 0, ptr %ptr br label %while.body } declare void @unknown_fn() define void @mustprogress_call_unknown_fn() mustprogress { ; COMMON: Function Attrs: mustprogress ; COMMON-LABEL: @mustprogress_call_unknown_fn( ; COMMON-NEXT: call void @unknown_fn() ; COMMON-NEXT: ret void ; call void @unknown_fn() ret void } define i32 @mustprogress_call_known_functions(ptr %ptr) mustprogress { ; FNATTRS: Function Attrs: mustprogress nofree norecurse noreturn nosync nounwind willreturn memory(argmem: read) ; FNATTRS-LABEL: @mustprogress_call_known_functions( ; FNATTRS-NEXT: call void @mustprogress_readnone() ; FNATTRS-NEXT: [[R:%.*]] = call i32 @mustprogress_load(ptr [[PTR:%.*]]) ; FNATTRS-NEXT: ret i32 [[R]] ; ; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read) ; ATTRIBUTOR-LABEL: @mustprogress_call_known_functions( ; ATTRIBUTOR-NEXT: call void @mustprogress_readnone() #[[ATTR9:[0-9]+]] ; ATTRIBUTOR-NEXT: [[R:%.*]] = call i32 @mustprogress_load(ptr nofree readonly captures(none) [[PTR:%.*]]) #[[ATTR12:[0-9]+]] ; ATTRIBUTOR-NEXT: ret i32 [[R]] ; call void @mustprogress_readnone() %r = call i32 @mustprogress_load(ptr %ptr) ret i32 %r } declare i32 @__gxx_personality_v0(...) define i64 @mustprogress_mayunwind() mustprogress personality ptr @__gxx_personality_v0 { ; FNATTRS: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(none) ; FNATTRS-LABEL: @mustprogress_mayunwind( ; FNATTRS-NEXT: [[A:%.*]] = invoke i64 @fn_noread() ; FNATTRS-NEXT: to label [[A:%.*]] unwind label [[B:%.*]] ; FNATTRS: A: ; FNATTRS-NEXT: ret i64 10 ; FNATTRS: B: ; FNATTRS-NEXT: [[VAL:%.*]] = landingpad { ptr, i32 } ; FNATTRS-NEXT: catch ptr null ; FNATTRS-NEXT: ret i64 0 ; ; ATTRIBUTOR: Function Attrs: mustprogress nosync nounwind willreturn memory(none) ; ATTRIBUTOR-LABEL: @mustprogress_mayunwind( ; ATTRIBUTOR-NEXT: [[A:%.*]] = invoke i64 @fn_noread() #[[ATTR13:[0-9]+]] ; ATTRIBUTOR-NEXT: to label [[A:%.*]] unwind label [[B:%.*]] ; ATTRIBUTOR: A: ; ATTRIBUTOR-NEXT: ret i64 10 ; ATTRIBUTOR: B: ; ATTRIBUTOR-NEXT: [[VAL:%.*]] = landingpad { ptr, i32 } ; ATTRIBUTOR-NEXT: catch ptr null ; ATTRIBUTOR-NEXT: ret i64 0 ; %a = invoke i64 @fn_noread() to label %A unwind label %B A: ret i64 10 B: %val = landingpad { ptr, i32 } catch ptr null ret i64 0 } ; Function without loops or non-willreturn calls will return. define void @willreturn_no_loop(i1 %c, ptr %p) { ; FNATTRS: Function Attrs: mustprogress willreturn ; FNATTRS-LABEL: @willreturn_no_loop( ; FNATTRS-NEXT: br i1 [[C:%.*]], label [[IF:%.*]], label [[ELSE:%.*]] ; FNATTRS: if: ; FNATTRS-NEXT: [[TMP1:%.*]] = load atomic i32, ptr [[P:%.*]] seq_cst, align 4 ; FNATTRS-NEXT: call void @fn_willreturn() ; FNATTRS-NEXT: br label [[END:%.*]] ; FNATTRS: else: ; FNATTRS-NEXT: store atomic i32 0, ptr [[P]] seq_cst, align 4 ; FNATTRS-NEXT: br label [[END]] ; FNATTRS: end: ; FNATTRS-NEXT: ret void ; ; ATTRIBUTOR: Function Attrs: mustprogress willreturn ; ATTRIBUTOR-LABEL: @willreturn_no_loop( ; ATTRIBUTOR-NEXT: br i1 [[C:%.*]], label [[IF:%.*]], label [[ELSE:%.*]] ; ATTRIBUTOR: if: ; ATTRIBUTOR-NEXT: [[TMP1:%.*]] = load atomic i32, ptr [[P:%.*]] seq_cst, align 4 ; ATTRIBUTOR-NEXT: call void @fn_willreturn() #[[ATTR11:[0-9]+]] ; ATTRIBUTOR-NEXT: br label [[END:%.*]] ; ATTRIBUTOR: else: ; ATTRIBUTOR-NEXT: store atomic i32 0, ptr [[P]] seq_cst, align 4 ; ATTRIBUTOR-NEXT: br label [[END]] ; ATTRIBUTOR: end: ; ATTRIBUTOR-NEXT: ret void ; br i1 %c, label %if, label %else if: load atomic i32, ptr %p seq_cst, align 4 call void @fn_willreturn() br label %end else: store atomic i32 0, ptr %p seq_cst, align 4 br label %end end: ret void } ; Calls a function that is not guaranteed to return, not willreturn. define void @willreturn_non_returning_function(i1 %c, ptr %p) { ; COMMON-LABEL: @willreturn_non_returning_function( ; COMMON-NEXT: call void @unknown_fn() ; COMMON-NEXT: ret void ; call void @unknown_fn() ret void } ; Infinite loop without mustprogress, will not return. define void @willreturn_loop() { ; COMMON: Function Attrs: nofree norecurse noreturn nosync nounwind memory(none) ; COMMON-LABEL: @willreturn_loop( ; COMMON-NEXT: br label [[LOOP:%.*]] ; COMMON: loop: ; COMMON-NEXT: br label [[LOOP]] ; br label %loop loop: br label %loop } ; Finite loop. Could be willreturn but not detected. ; FIXME define void @willreturn_finite_loop() { ; COMMON: Function Attrs: nofree norecurse nosync nounwind memory(none) ; COMMON-LABEL: @willreturn_finite_loop( ; COMMON-NEXT: entry: ; COMMON-NEXT: br label [[LOOP:%.*]] ; COMMON: loop: ; COMMON-NEXT: [[I:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[I_INC:%.*]], [[LOOP]] ] ; COMMON-NEXT: [[I_INC]] = add nuw i32 [[I]], 1 ; COMMON-NEXT: [[C:%.*]] = icmp ne i32 [[I_INC]], 100 ; COMMON-NEXT: br i1 [[C]], label [[LOOP]], label [[END:%.*]] ; COMMON: end: ; COMMON-NEXT: ret void ; entry: br label %loop loop: %i = phi i32 [ 0, %entry], [ %i.inc, %loop ] %i.inc = add nuw i32 %i, 1 %c = icmp ne i32 %i.inc, 100 br i1 %c, label %loop, label %end end: ret void } ; Infinite recursion without mustprogress, will not return. define void @willreturn_recursion() { ; FNATTRS: Function Attrs: nofree nosync nounwind memory(none) ; FNATTRS-LABEL: @willreturn_recursion( ; FNATTRS-NEXT: tail call void @willreturn_recursion() ; FNATTRS-NEXT: ret void ; ; ATTRIBUTOR: Function Attrs: nofree nosync nounwind memory(none) ; ATTRIBUTOR-LABEL: @willreturn_recursion( ; ATTRIBUTOR-NEXT: tail call void @willreturn_recursion() #[[ATTR9]] ; ATTRIBUTOR-NEXT: ret void ; tail call void @willreturn_recursion() ret void } ; Irreducible infinite loop, will not return. define void @willreturn_irreducible(i1 %c) { ; COMMON: Function Attrs: nofree norecurse noreturn nosync nounwind memory(none) ; COMMON-LABEL: @willreturn_irreducible( ; COMMON-NEXT: br i1 [[C:%.*]], label [[BB1:%.*]], label [[BB2:%.*]] ; COMMON: bb1: ; COMMON-NEXT: br label [[BB2]] ; COMMON: bb2: ; COMMON-NEXT: br label [[BB1]] ; br i1 %c, label %bb1, label %bb2 bb1: br label %bb2 bb2: br label %bb1 } define linkonce i32 @square(i32) { ; COMMON-LABEL: @square( ; COMMON-NEXT: [[TMP2:%.*]] = mul nsw i32 [[TMP0:%.*]], [[TMP0]] ; COMMON-NEXT: ret i32 [[TMP2]] ; %2 = mul nsw i32 %0, %0 ret i32 %2 } declare i64 @fn_noread() readnone declare void @fn_willreturn() willreturn