| ; 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 nocapture nofree readonly [[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() |
| ; 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 |