llvm/llvm/test/Transforms/Inline/ret_attr_update.ll

; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt < %s -inline-threshold=0 -passes=always-inline -S | FileCheck %s
; RUN: opt < %s -passes=always-inline -S | FileCheck %s

declare ptr @foo(ptr) nounwind willreturn

define ptr @callee(ptr %p) alwaysinline {
; CHECK-LABEL: @callee(
; CHECK-NEXT:    [[R:%.*]] = call ptr @foo(ptr noalias [[P:%.*]])
; CHECK-NEXT:    ret ptr [[R]]
;
  %r = call ptr @foo(ptr noalias %p)
  ret ptr %r
}

define ptr @caller(ptr %ptr, i64 %x) {
; CHECK-LABEL: @caller(
; CHECK-NEXT:    [[GEP:%.*]] = getelementptr inbounds i8, ptr [[PTR:%.*]], i64 [[X:%.*]]
; CHECK-NEXT:    [[R_I:%.*]] = call nonnull ptr @foo(ptr noalias [[GEP]])
; CHECK-NEXT:    ret ptr [[R_I]]
;
  %gep = getelementptr inbounds i8, ptr %ptr, i64 %x
  %p = call nonnull ptr @callee(ptr %gep)
  ret ptr %p
}

declare void @llvm.experimental.guard(i1,...)
; Cannot add nonnull attribute to foo
; because the guard is a throwing call
define internal ptr @callee_with_throwable(ptr %p) alwaysinline {
  %r = call ptr @foo(ptr %p)
  %cond = icmp ne ptr %r, null
  call void (i1, ...) @llvm.experimental.guard(i1 %cond) [ "deopt"() ]
  ret ptr %r
}

declare ptr @bar(ptr) readonly nounwind
; Here also we cannot add nonnull attribute to the call bar.
define internal ptr @callee_with_explicit_control_flow(ptr %p) alwaysinline {
  %r = call ptr @bar(ptr %p)
  %cond = icmp ne ptr %r, null
  br i1 %cond, label %ret, label %orig

ret:
  ret ptr %r

orig:
  ret ptr %p
}

define ptr @caller2(ptr %ptr, i64 %x, i1 %cond) {
; CHECK-LABEL: @caller2(
; CHECK-NEXT:    [[GEP:%.*]] = getelementptr inbounds i8, ptr [[PTR:%.*]], i64 [[X:%.*]]
; CHECK-NEXT:    [[R_I:%.*]] = call ptr @foo(ptr [[GEP]])
; CHECK-NEXT:    [[COND_I:%.*]] = icmp ne ptr [[R_I]], null
; CHECK-NEXT:    call void (i1, ...) @llvm.experimental.guard(i1 [[COND_I]]) [ "deopt"() ]
; CHECK-NEXT:    [[R_I1:%.*]] = call ptr @bar(ptr [[GEP]])
; CHECK-NEXT:    [[COND_I2:%.*]] = icmp ne ptr [[R_I1]], null
; CHECK-NEXT:    br i1 [[COND_I2]], label [[RET_I:%.*]], label [[ORIG_I:%.*]]
; CHECK:       ret.i:
; CHECK-NEXT:    br label [[CALLEE_WITH_EXPLICIT_CONTROL_FLOW_EXIT:%.*]]
; CHECK:       orig.i:
; CHECK-NEXT:    br label [[CALLEE_WITH_EXPLICIT_CONTROL_FLOW_EXIT]]
; CHECK:       callee_with_explicit_control_flow.exit:
; CHECK-NEXT:    [[Q3:%.*]] = phi ptr [ [[R_I1]], [[RET_I]] ], [ [[GEP]], [[ORIG_I]] ]
; CHECK-NEXT:    br i1 [[COND:%.*]], label [[PRET:%.*]], label [[QRET:%.*]]
; CHECK:       pret:
; CHECK-NEXT:    ret ptr [[R_I]]
; CHECK:       qret:
; CHECK-NEXT:    ret ptr [[Q3]]
;
  %gep = getelementptr inbounds i8, ptr %ptr, i64 %x
  %p = call nonnull ptr @callee_with_throwable(ptr %gep)
  %q = call nonnull ptr @callee_with_explicit_control_flow(ptr %gep)
  br i1 %cond, label %pret, label %qret

pret:
  ret ptr %p

qret:
  ret ptr %q
}

define internal ptr @callee3(ptr %p) alwaysinline {
  %r = call noalias ptr @foo(ptr %p)
  ret ptr %r
}

; add the deref attribute to the existing attributes on foo.
define ptr @caller3(ptr %ptr, i64 %x) {
; CHECK-LABEL: @caller3(
; CHECK-NEXT:    [[GEP:%.*]] = getelementptr inbounds i8, ptr [[PTR:%.*]], i64 [[X:%.*]]
; CHECK-NEXT:    [[R_I:%.*]] = call noalias dereferenceable_or_null(12) ptr @foo(ptr [[GEP]])
; CHECK-NEXT:    ret ptr [[R_I]]
;
  %gep = getelementptr inbounds i8, ptr %ptr, i64 %x
  %p = call dereferenceable_or_null(12) ptr @callee3(ptr %gep)
  ret ptr %p
}

declare ptr @inf_loop_call(ptr) nounwind
; We cannot propagate attributes to foo because we do not know whether inf_loop_call
; will return execution.
define internal ptr @callee_with_sideeffect_callsite(ptr %p) alwaysinline {
  %r = call ptr @foo(ptr %p)
  %v = call ptr @inf_loop_call(ptr %p)
  ret ptr %r
}

; do not add deref attribute to foo
define ptr @test4(ptr %ptr, i64 %x) {
; CHECK-LABEL: @test4(
; CHECK-NEXT:    [[GEP:%.*]] = getelementptr inbounds i8, ptr [[PTR:%.*]], i64 [[X:%.*]]
; CHECK-NEXT:    [[R_I:%.*]] = call ptr @foo(ptr [[GEP]])
; CHECK-NEXT:    [[V_I:%.*]] = call ptr @inf_loop_call(ptr [[GEP]])
; CHECK-NEXT:    ret ptr [[R_I]]
;
  %gep = getelementptr inbounds i8, ptr %ptr, i64 %x
  %p = call dereferenceable_or_null(12) ptr @callee_with_sideeffect_callsite(ptr %gep)
  ret ptr %p
}

declare ptr @baz(ptr) nounwind willreturn
define internal ptr @callee5(ptr %p) alwaysinline {
  %r = call ptr @foo(ptr %p)
  %v = call ptr @baz(ptr %p)
  ret ptr %r
}

; add the deref attribute to foo.
define ptr @test5(ptr %ptr, i64 %x) {
; CHECK-LABEL: @test5(
; CHECK-NEXT:    [[GEP:%.*]] = getelementptr inbounds i8, ptr [[PTR:%.*]], i64 [[X:%.*]]
; CHECK-NEXT:    [[R_I:%.*]] = call dereferenceable_or_null(12) ptr @foo(ptr [[GEP]])
; CHECK-NEXT:    [[V_I:%.*]] = call ptr @baz(ptr [[GEP]])
; CHECK-NEXT:    ret ptr [[R_I]]
;
  %gep = getelementptr inbounds i8, ptr %ptr, i64 %x
  %s = call dereferenceable_or_null(12) ptr @callee5(ptr %gep)
  ret ptr %s
}

; deref attributes have different values on the callee and the call feeding into
; the return.
; AttrBuilder overwrites the existing value.
define internal ptr @callee6(ptr %p) alwaysinline {
  %r = call dereferenceable_or_null(16) ptr @foo(ptr %p)
  %v = call ptr @baz(ptr %p)
  ret ptr %r
}


define ptr @test6(ptr %ptr, i64 %x) {
; CHECK-LABEL: @test6(
; CHECK-NEXT:    [[GEP:%.*]] = getelementptr inbounds i8, ptr [[PTR:%.*]], i64 [[X:%.*]]
; CHECK-NEXT:    [[R_I:%.*]] = call dereferenceable_or_null(16) ptr @foo(ptr [[GEP]])
; CHECK-NEXT:    [[V_I:%.*]] = call ptr @baz(ptr [[GEP]])
; CHECK-NEXT:    ret ptr [[R_I]]
;
  %gep = getelementptr inbounds i8, ptr %ptr, i64 %x
  %s = call dereferenceable_or_null(12) ptr @callee6(ptr %gep)
  ret ptr %s
}

; We add the attributes from the callee to both the calls below.
define internal ptr @callee7(ptr %ptr, i1 %cond) alwaysinline {
  br i1 %cond, label %pass, label %fail

pass:
  %r = call ptr @foo(ptr noalias %ptr)
  ret ptr %r

fail:
  %s = call ptr @baz(ptr %ptr)
  ret ptr %s
}

define void @test7(ptr %ptr, i64 %x, i1 %cond) {
; CHECK-LABEL: @test7(
; CHECK-NEXT:    [[GEP:%.*]] = getelementptr inbounds i8, ptr [[PTR:%.*]], i64 [[X:%.*]]
; CHECK-NEXT:    br i1 [[COND:%.*]], label [[PASS_I:%.*]], label [[FAIL_I:%.*]]
; CHECK:       pass.i:
; CHECK-NEXT:    [[R_I:%.*]] = call nonnull ptr @foo(ptr noalias [[GEP]])
; CHECK-NEXT:    br label [[CALLEE7_EXIT:%.*]]
; CHECK:       fail.i:
; CHECK-NEXT:    [[S_I:%.*]] = call nonnull ptr @baz(ptr [[GEP]])
; CHECK-NEXT:    br label [[CALLEE7_EXIT]]
; CHECK:       callee7.exit:
; CHECK-NEXT:    [[T1:%.*]] = phi ptr [ [[R_I]], [[PASS_I]] ], [ [[S_I]], [[FAIL_I]] ]
; CHECK-NEXT:    call void @snort(ptr [[T1]])
; CHECK-NEXT:    ret void
;

  %gep = getelementptr inbounds i8, ptr %ptr, i64 %x
  %t = call nonnull ptr @callee7(ptr %gep, i1 %cond)
  call void @snort(ptr %t)
  ret void
}
declare void @snort(ptr)

declare i32 @intrinsic(ptr) nounwind argmemonly

define internal i32 @callee8(ptr %ptr) alwaysinline {
  %r = call i32 @intrinsic(ptr noalias %ptr)
  ret i32 %r
}


; signext is an attribute specific to the target ABI and not the
; callee/callsite.
; We cannot propagate that attribute to another call since it can be invalid at
; that call.
define i32 @test8(ptr %ptr, i64 %x) {
; CHECK-LABEL: @test8(
; CHECK-NEXT:    [[GEP:%.*]] = getelementptr inbounds i8, ptr [[PTR:%.*]], i64 [[X:%.*]]
; CHECK-NEXT:    [[R_I:%.*]] = call i32 @intrinsic(ptr noalias [[GEP]])
; CHECK-NEXT:    ret i32 [[R_I]]
;

  %gep = getelementptr inbounds i8, ptr %ptr, i64 %x
  %t = call signext i32 @callee8(ptr %gep)
  ret i32 %t
}