llvm/llvm/test/Transforms/FunctionAttrs/willreturn.ll

; 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() #[[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