Files
clang-p2996/llvm/test/Transforms/Attributor/nonnull.ll
Nikita Popov e44b11d9b6 [ValueTracking] Treat branch on undef as UB as well
We were already treating branch on poison as UB, but branch on
undef is also UB. Move the checks into the correct function.

From LangRef for br:

> If ‘cond’ is poison or undef, this instruction has undefined behavior.

From LangRef for switch:

> If ‘value’ is poison or undef, this instruction has undefined behavior.

There is a minor regression in dont-distribute-phi.ll, apparently
we handle that pattern in logical but not bitwise form.
2023-01-02 12:34:23 +01:00

1554 lines
59 KiB
LLVM

; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --check-attributes --check-globals
; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=15 -S < %s | FileCheck %s --check-prefixes=CHECK,TUNIT
; RUN: opt -aa-pipeline=basic-aa -passes=attributor-cgscc -attributor-manifest-internal -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=CHECK,CGSCC
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
declare nonnull ptr @ret_nonnull()
declare void @llvm.assume(i1)
; Return a pointer trivially nonnull (call return attribute)
define ptr @test1() {
; CHECK-LABEL: define {{[^@]+}}@test1() {
; CHECK-NEXT: [[RET:%.*]] = call nonnull ptr @ret_nonnull()
; CHECK-NEXT: ret ptr [[RET]]
;
%ret = call ptr @ret_nonnull()
ret ptr %ret
}
; Return a pointer trivially nonnull (argument attribute)
define ptr @test2(ptr nonnull %p) {
; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none)
; CHECK-LABEL: define {{[^@]+}}@test2
; CHECK-SAME: (ptr nofree nonnull readnone returned "no-capture-maybe-returned" [[P:%.*]]) #[[ATTR1:[0-9]+]] {
; CHECK-NEXT: ret ptr [[P]]
;
ret ptr %p
}
define ptr @test2A(i1 %c, ptr %ret) {
; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(inaccessiblemem: readwrite)
; CHECK-LABEL: define {{[^@]+}}@test2A
; CHECK-SAME: (i1 noundef [[C:%.*]], ptr nofree nonnull readnone returned "no-capture-maybe-returned" [[RET:%.*]]) #[[ATTR2:[0-9]+]] {
; CHECK-NEXT: br i1 [[C]], label [[A:%.*]], label [[B:%.*]]
; CHECK: A:
; CHECK-NEXT: call void @llvm.assume(i1 noundef true) #[[ATTR13:[0-9]+]] [ "nonnull"(ptr [[RET]]) ]
; CHECK-NEXT: ret ptr [[RET]]
; CHECK: B:
; CHECK-NEXT: call void @llvm.assume(i1 noundef true) #[[ATTR13]] [ "nonnull"(ptr [[RET]]) ]
; CHECK-NEXT: ret ptr [[RET]]
;
br i1 %c, label %A, label %B
A:
call void @llvm.assume(i1 true) [ "nonnull"(ptr %ret) ]
ret ptr %ret
B:
call void @llvm.assume(i1 true) [ "nonnull"(ptr %ret) ]
ret ptr %ret
}
define ptr @test2B(i1 %c, ptr %ret) {
; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(inaccessiblemem: readwrite)
; CHECK-LABEL: define {{[^@]+}}@test2B
; CHECK-SAME: (i1 noundef [[C:%.*]], ptr nofree nonnull readnone returned dereferenceable(4) "no-capture-maybe-returned" [[RET:%.*]]) #[[ATTR2]] {
; CHECK-NEXT: br i1 [[C]], label [[A:%.*]], label [[B:%.*]]
; CHECK: A:
; CHECK-NEXT: call void @llvm.assume(i1 noundef true) #[[ATTR13]] [ "dereferenceable"(ptr [[RET]], i32 4) ]
; CHECK-NEXT: ret ptr [[RET]]
; CHECK: B:
; CHECK-NEXT: call void @llvm.assume(i1 noundef true) #[[ATTR13]] [ "dereferenceable"(ptr [[RET]], i32 4) ]
; CHECK-NEXT: ret ptr [[RET]]
;
br i1 %c, label %A, label %B
A:
call void @llvm.assume(i1 true) [ "dereferenceable"(ptr %ret, i32 4) ]
ret ptr %ret
B:
call void @llvm.assume(i1 true) [ "dereferenceable"(ptr %ret, i32 4) ]
ret ptr %ret
}
; 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 ptr @scc_binder(i1 %c) {
; CHECK-LABEL: define {{[^@]+}}@scc_binder
; CHECK-SAME: (i1 noundef [[C:%.*]]) {
; CHECK-NEXT: br i1 [[C]], label [[REC:%.*]], label [[END:%.*]]
; CHECK: rec:
; CHECK-NEXT: [[TMP1:%.*]] = call ptr @test3(i1 noundef [[C]])
; CHECK-NEXT: br label [[END]]
; CHECK: end:
; CHECK-NEXT: ret ptr null
;
br i1 %c, label %rec, label %end
rec:
call ptr @test3(i1 %c)
br label %end
end:
ret ptr null
}
define ptr @test3(i1 %c) {
; CHECK-LABEL: define {{[^@]+}}@test3
; CHECK-SAME: (i1 [[C:%.*]]) {
; CHECK-NEXT: [[TMP1:%.*]] = call ptr @scc_binder(i1 [[C]])
; CHECK-NEXT: [[RET:%.*]] = call nonnull ptr @ret_nonnull()
; CHECK-NEXT: ret ptr [[RET]]
;
call ptr @scc_binder(i1 %c)
%ret = call ptr @ret_nonnull()
ret ptr %ret
}
; Given a mutual recursive set of functions, we can mark them
; nonnull if neither can ever return null. (In this case, they
; just never return period.)
define ptr @test4_helper() {
; TUNIT: Function Attrs: nofree nosync nounwind willreturn memory(none)
; TUNIT-LABEL: define {{[^@]+}}@test4_helper
; TUNIT-SAME: () #[[ATTR3:[0-9]+]] {
; TUNIT-NEXT: ret ptr undef
;
; CGSCC: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none)
; CGSCC-LABEL: define {{[^@]+}}@test4_helper
; CGSCC-SAME: () #[[ATTR1]] {
; CGSCC-NEXT: ret ptr undef
;
%ret = call ptr @test4()
ret ptr %ret
}
define ptr @test4() {
; TUNIT: Function Attrs: nofree nosync nounwind willreturn memory(none)
; TUNIT-LABEL: define {{[^@]+}}@test4
; TUNIT-SAME: () #[[ATTR3]] {
; TUNIT-NEXT: ret ptr undef
;
; CGSCC: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none)
; CGSCC-LABEL: define {{[^@]+}}@test4
; CGSCC-SAME: () #[[ATTR1]] {
; CGSCC-NEXT: ret ptr undef
;
%ret = call ptr @test4_helper()
ret ptr %ret
}
; Given a mutual recursive set of functions which *can* return null
; make sure we haven't marked them as nonnull.
define ptr @test5_helper(i1 %c) {
; TUNIT: Function Attrs: nofree nosync nounwind willreturn memory(none)
; TUNIT-LABEL: define {{[^@]+}}@test5_helper
; TUNIT-SAME: (i1 noundef [[C:%.*]]) #[[ATTR3]] {
; TUNIT-NEXT: br i1 [[C]], label [[REC:%.*]], label [[END:%.*]]
; TUNIT: rec:
; TUNIT-NEXT: br label [[END]]
; TUNIT: end:
; TUNIT-NEXT: ret ptr null
;
; CGSCC: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none)
; CGSCC-LABEL: define {{[^@]+}}@test5_helper
; CGSCC-SAME: (i1 noundef [[C:%.*]]) #[[ATTR1]] {
; CGSCC-NEXT: br i1 [[C]], label [[REC:%.*]], label [[END:%.*]]
; CGSCC: rec:
; CGSCC-NEXT: br label [[END]]
; CGSCC: end:
; CGSCC-NEXT: ret ptr null
;
br i1 %c, label %rec, label %end
rec:
%ret = call ptr @test5(i1 %c)
br label %end
end:
ret ptr null
}
define ptr @test5(i1 %c) {
; TUNIT: Function Attrs: nofree nosync nounwind willreturn memory(none)
; TUNIT-LABEL: define {{[^@]+}}@test5
; TUNIT-SAME: (i1 [[C:%.*]]) #[[ATTR3]] {
; TUNIT-NEXT: ret ptr null
;
; CGSCC: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none)
; CGSCC-LABEL: define {{[^@]+}}@test5
; CGSCC-SAME: (i1 [[C:%.*]]) #[[ATTR1]] {
; CGSCC-NEXT: ret ptr null
;
%ret = call ptr @test5_helper(i1 %c)
ret ptr %ret
}
; Local analysis, but going through a self recursive phi
define ptr @test6a() {
;
; TUNIT: Function Attrs: noreturn
; TUNIT-LABEL: define {{[^@]+}}@test6a
; TUNIT-SAME: () #[[ATTR4:[0-9]+]] {
; TUNIT-NEXT: entry:
; TUNIT-NEXT: [[RET:%.*]] = call ptr @ret_nonnull()
; TUNIT-NEXT: br label [[LOOP:%.*]]
; TUNIT: loop:
; TUNIT-NEXT: unreachable
; TUNIT: exit:
; TUNIT-NEXT: unreachable
;
; CGSCC: Function Attrs: noreturn
; CGSCC-LABEL: define {{[^@]+}}@test6a
; CGSCC-SAME: () #[[ATTR3:[0-9]+]] {
; CGSCC-NEXT: entry:
; CGSCC-NEXT: [[RET:%.*]] = call ptr @ret_nonnull()
; CGSCC-NEXT: br label [[LOOP:%.*]]
; CGSCC: loop:
; CGSCC-NEXT: unreachable
; CGSCC: exit:
; CGSCC-NEXT: unreachable
;
entry:
%ret = call ptr @ret_nonnull()
br label %loop
loop:
%phi = phi ptr [%ret, %entry], [%phi, %loop]
br i1 undef, label %loop, label %exit
exit:
ret ptr %phi
}
define ptr @test6b(i1 %c) {
; CHECK-LABEL: define {{[^@]+}}@test6b
; CHECK-SAME: (i1 [[C:%.*]]) {
; CHECK-NEXT: entry:
; CHECK-NEXT: [[RET:%.*]] = call nonnull ptr @ret_nonnull()
; CHECK-NEXT: br label [[LOOP:%.*]]
; CHECK: loop:
; CHECK-NEXT: [[PHI:%.*]] = phi ptr [ [[RET]], [[ENTRY:%.*]] ], [ [[RET]], [[LOOP]] ]
; CHECK-NEXT: br i1 [[C]], label [[LOOP]], label [[EXIT:%.*]]
; CHECK: exit:
; CHECK-NEXT: ret ptr [[RET]]
;
entry:
%ret = call ptr @ret_nonnull()
br label %loop
loop:
%phi = phi ptr [%ret, %entry], [%phi, %loop]
br i1 %c, label %loop, label %exit
exit:
ret ptr %phi
}
define ptr @test7(ptr %a) {
; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none)
; CHECK-LABEL: define {{[^@]+}}@test7
; CHECK-SAME: (ptr nofree readnone returned "no-capture-maybe-returned" [[A:%.*]]) #[[ATTR1]] {
; CHECK-NEXT: ret ptr [[A]]
;
ret ptr %a
}
define ptr @test8(ptr %a) {
; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none)
; CHECK-LABEL: define {{[^@]+}}@test8
; CHECK-SAME: (ptr nofree readnone "no-capture-maybe-returned" [[A:%.*]]) #[[ATTR1]] {
; CHECK-NEXT: [[B:%.*]] = getelementptr inbounds i8, ptr [[A]], i64 1
; CHECK-NEXT: ret ptr [[B]]
;
%b = getelementptr inbounds i8, ptr %a, i64 1
ret ptr %b
}
define ptr @test9(ptr %a, i64 %n) {
; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none)
; CHECK-LABEL: define {{[^@]+}}@test9
; CHECK-SAME: (ptr nofree readnone "no-capture-maybe-returned" [[A:%.*]], i64 [[N:%.*]]) #[[ATTR1]] {
; CHECK-NEXT: [[B:%.*]] = getelementptr inbounds i8, ptr [[A]], i64 [[N]]
; CHECK-NEXT: ret ptr [[B]]
;
%b = getelementptr inbounds i8, ptr %a, i64 %n
ret ptr %b
}
; ATTRIBUTOR_OPM: define ptr @test10
; ATTRIBUTOR_NPM: define nonnull ptr @test10
define ptr @test10(ptr %a, i64 %n) {
; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(inaccessiblemem: readwrite)
; CHECK-LABEL: define {{[^@]+}}@test10
; CHECK-SAME: (ptr nofree readnone "no-capture-maybe-returned" [[A:%.*]], i64 [[N:%.*]]) #[[ATTR2]] {
; CHECK-NEXT: [[CMP:%.*]] = icmp ne i64 [[N]], 0
; CHECK-NEXT: call void @llvm.assume(i1 noundef [[CMP]]) #[[ATTR13]]
; CHECK-NEXT: [[B:%.*]] = getelementptr inbounds i8, ptr [[A]], i64 [[N]]
; CHECK-NEXT: ret ptr [[B]]
;
%cmp = icmp ne i64 %n, 0
call void @llvm.assume(i1 %cmp)
%b = getelementptr inbounds i8, ptr %a, i64 %n
ret ptr %b
}
; TEST 11
; char* test11(char *p) {
; return p? p: nonnull();
; }
; FIXME: missing nonnull
define ptr @test11(ptr) local_unnamed_addr {
; CHECK-LABEL: define {{[^@]+}}@test11
; CHECK-SAME: (ptr [[TMP0:%.*]]) local_unnamed_addr {
; CHECK-NEXT: [[TMP2:%.*]] = icmp eq ptr [[TMP0]], null
; CHECK-NEXT: br i1 [[TMP2]], label [[TMP3:%.*]], label [[TMP5:%.*]]
; CHECK: 3:
; CHECK-NEXT: [[TMP4:%.*]] = tail call ptr @ret_nonnull()
; CHECK-NEXT: br label [[TMP5]]
; CHECK: 5:
; CHECK-NEXT: [[TMP6:%.*]] = phi ptr [ [[TMP4]], [[TMP3]] ], [ [[TMP0]], [[TMP1:%.*]] ]
; CHECK-NEXT: ret ptr [[TMP6]]
;
%2 = icmp eq ptr %0, null
br i1 %2, label %3, label %5
; <label>:3: ; preds = %1
%4 = tail call ptr @ret_nonnull()
br label %5
; <label>:5: ; preds = %3, %1
%6 = phi ptr [ %4, %3 ], [ %0, %1 ]
ret ptr %6
}
; TEST 12
; Simple CallSite Test
declare void @test12_helper(ptr)
define void @test12(ptr nonnull %a) {
; CHECK-LABEL: define {{[^@]+}}@test12
; CHECK-SAME: (ptr nonnull [[A:%.*]]) {
; CHECK-NEXT: tail call void @test12_helper(ptr nonnull [[A]])
; CHECK-NEXT: ret void
;
tail call void @test12_helper(ptr %a)
ret void
}
; TEST 13
; Simple Argument Tests
declare ptr @unknown()
define void @test13_helper() {
; TUNIT-LABEL: define {{[^@]+}}@test13_helper() {
; TUNIT-NEXT: [[NONNULLPTR:%.*]] = tail call nonnull ptr @ret_nonnull()
; TUNIT-NEXT: [[MAYBENULLPTR:%.*]] = tail call ptr @unknown()
; TUNIT-NEXT: tail call void @test13(ptr noalias nocapture nofree nonnull readnone [[NONNULLPTR]], ptr noalias nocapture nofree nonnull readnone [[NONNULLPTR]], ptr noalias nocapture nofree readnone [[MAYBENULLPTR]]) #[[ATTR5:[0-9]+]]
; TUNIT-NEXT: tail call void @test13(ptr noalias nocapture nofree nonnull readnone [[NONNULLPTR]], ptr noalias nocapture nofree readnone [[MAYBENULLPTR]], ptr noalias nocapture nofree nonnull readnone [[NONNULLPTR]]) #[[ATTR5]]
; TUNIT-NEXT: ret void
;
; CGSCC-LABEL: define {{[^@]+}}@test13_helper() {
; CGSCC-NEXT: [[NONNULLPTR:%.*]] = tail call nonnull ptr @ret_nonnull()
; CGSCC-NEXT: [[MAYBENULLPTR:%.*]] = tail call ptr @unknown()
; CGSCC-NEXT: tail call void @test13(ptr noalias nocapture nofree nonnull readnone [[NONNULLPTR]], ptr noalias nocapture nofree nonnull readnone [[NONNULLPTR]], ptr noalias nocapture nofree readnone [[MAYBENULLPTR]]) #[[ATTR4:[0-9]+]]
; CGSCC-NEXT: tail call void @test13(ptr noalias nocapture nofree nonnull readnone [[NONNULLPTR]], ptr noalias nocapture nofree readnone [[MAYBENULLPTR]], ptr noalias nocapture nofree nonnull readnone [[NONNULLPTR]]) #[[ATTR4]]
; CGSCC-NEXT: ret void
;
%nonnullptr = tail call ptr @ret_nonnull()
%maybenullptr = tail call ptr @unknown()
tail call void @test13(ptr %nonnullptr, ptr %nonnullptr, ptr %maybenullptr)
tail call void @test13(ptr %nonnullptr, ptr %maybenullptr, ptr %nonnullptr)
ret void
}
define internal void @test13(ptr %a, ptr %b, ptr %c) {
;
; TUNIT: Function Attrs: nounwind
; TUNIT-LABEL: define {{[^@]+}}@test13
; TUNIT-SAME: (ptr noalias nocapture nofree nonnull readnone [[A:%.*]], ptr noalias nocapture nofree readnone [[B:%.*]], ptr noalias nocapture nofree readnone [[C:%.*]]) #[[ATTR5]] {
; TUNIT-NEXT: call void @use_i8_ptr(ptr noalias nocapture nofree nonnull readnone [[A]]) #[[ATTR5]]
; TUNIT-NEXT: call void @use_i8_ptr(ptr noalias nocapture nofree readnone [[B]]) #[[ATTR5]]
; TUNIT-NEXT: call void @use_i8_ptr(ptr noalias nocapture nofree readnone [[C]]) #[[ATTR5]]
; TUNIT-NEXT: ret void
;
; CGSCC: Function Attrs: nounwind
; CGSCC-LABEL: define {{[^@]+}}@test13
; CGSCC-SAME: (ptr noalias nocapture nofree nonnull readnone [[A:%.*]], ptr noalias nocapture nofree readnone [[B:%.*]], ptr noalias nocapture nofree readnone [[C:%.*]]) #[[ATTR4]] {
; CGSCC-NEXT: call void @use_i8_ptr(ptr noalias nocapture nofree nonnull readnone [[A]]) #[[ATTR4]]
; CGSCC-NEXT: call void @use_i8_ptr(ptr noalias nocapture nofree readnone [[B]]) #[[ATTR4]]
; CGSCC-NEXT: call void @use_i8_ptr(ptr noalias nocapture nofree readnone [[C]]) #[[ATTR4]]
; CGSCC-NEXT: ret void
;
call void @use_i8_ptr(ptr %a)
call void @use_i8_ptr(ptr %b)
call void @use_i8_ptr(ptr %c)
ret void
}
declare nonnull ptr @nonnull()
; TEST 14
; Complex propagation
; Argument of f1, f2, f3 can be marked with nonnull.
; * Argument
; 1. In f1:bb6, %arg can be marked with nonnull because of the comparison in bb1
; 2. Because f2 is internal function, f2(ptr %arg) -> @f2(ptr nonnull %arg)
; 3. In f1:bb4 %tmp5 is nonnull and f3 is internal function.
; Then, f3(ptr %arg) -> @f3(ptr nonnull %arg)
; 4. We get nonnull in whole f1 call sites so f1(ptr %arg) -> @f1(ptr nonnull %arg)
define internal ptr @f1(ptr %arg) {
; FIXME: missing nonnull It should be nonnull @f1(ptr nonnull readonly %arg)
; TUNIT: Function Attrs: nofree nosync nounwind memory(argmem: read)
; TUNIT-LABEL: define {{[^@]+}}@f1
; TUNIT-SAME: (ptr nofree readonly [[ARG:%.*]]) #[[ATTR6:[0-9]+]] {
; TUNIT-NEXT: bb:
; TUNIT-NEXT: [[TMP:%.*]] = icmp eq ptr [[ARG]], null
; TUNIT-NEXT: br i1 [[TMP]], label [[BB9:%.*]], label [[BB1:%.*]]
; TUNIT: bb1:
; TUNIT-NEXT: [[TMP2:%.*]] = load i32, ptr [[ARG]], align 4
; TUNIT-NEXT: [[TMP3:%.*]] = icmp eq i32 [[TMP2]], 0
; TUNIT-NEXT: br i1 [[TMP3]], label [[BB6:%.*]], label [[BB4:%.*]]
; TUNIT: bb4:
; TUNIT-NEXT: [[TMP5:%.*]] = getelementptr inbounds i32, ptr [[ARG]], i64 1
; TUNIT-NEXT: [[TMP5B:%.*]] = tail call ptr @f3(ptr nofree nonnull readonly [[TMP5]]) #[[ATTR14:[0-9]+]]
; TUNIT-NEXT: [[TMP5C:%.*]] = getelementptr inbounds i32, ptr [[TMP5B]], i64 -1
; TUNIT-NEXT: br label [[BB9]]
; TUNIT: bb6:
; TUNIT-NEXT: [[TMP7:%.*]] = tail call ptr @f2(ptr nofree nonnull readonly align 4 dereferenceable(4) [[ARG]]) #[[ATTR14]]
; TUNIT-NEXT: ret ptr [[TMP7]]
; TUNIT: bb9:
; TUNIT-NEXT: [[TMP10:%.*]] = phi ptr [ [[TMP5C]], [[BB4]] ], [ inttoptr (i64 4 to ptr), [[BB:%.*]] ]
; TUNIT-NEXT: ret ptr [[TMP10]]
;
; CGSCC: Function Attrs: nofree nosync nounwind memory(argmem: read)
; CGSCC-LABEL: define {{[^@]+}}@f1
; CGSCC-SAME: (ptr nofree readonly [[ARG:%.*]]) #[[ATTR5:[0-9]+]] {
; CGSCC-NEXT: bb:
; CGSCC-NEXT: [[TMP:%.*]] = icmp eq ptr [[ARG]], null
; CGSCC-NEXT: br i1 [[TMP]], label [[BB9:%.*]], label [[BB1:%.*]]
; CGSCC: bb1:
; CGSCC-NEXT: [[TMP2:%.*]] = load i32, ptr [[ARG]], align 4
; CGSCC-NEXT: [[TMP3:%.*]] = icmp eq i32 [[TMP2]], 0
; CGSCC-NEXT: br i1 [[TMP3]], label [[BB6:%.*]], label [[BB4:%.*]]
; CGSCC: bb4:
; CGSCC-NEXT: [[TMP5:%.*]] = getelementptr inbounds i32, ptr [[ARG]], i64 1
; CGSCC-NEXT: [[TMP5B:%.*]] = tail call ptr @f3(ptr nofree nonnull readonly [[TMP5]]) #[[ATTR14:[0-9]+]]
; CGSCC-NEXT: [[TMP5C:%.*]] = getelementptr inbounds i32, ptr [[TMP5B]], i64 -1
; CGSCC-NEXT: br label [[BB9]]
; CGSCC: bb6:
; CGSCC-NEXT: [[TMP7:%.*]] = tail call ptr @f2(ptr nofree nonnull readonly align 4 dereferenceable(4) [[ARG]]) #[[ATTR14]]
; CGSCC-NEXT: ret ptr [[TMP7]]
; CGSCC: bb9:
; CGSCC-NEXT: [[TMP10:%.*]] = phi ptr [ [[TMP5C]], [[BB4]] ], [ inttoptr (i64 4 to ptr), [[BB:%.*]] ]
; CGSCC-NEXT: ret ptr [[TMP10]]
;
bb:
%tmp = icmp eq ptr %arg, null
br i1 %tmp, label %bb9, label %bb1
bb1: ; preds = %bb
%tmp2 = load i32, ptr %arg, align 4
%tmp3 = icmp eq i32 %tmp2, 0
br i1 %tmp3, label %bb6, label %bb4
bb4: ; preds = %bb1
%tmp5 = getelementptr inbounds i32, ptr %arg, i64 1
%tmp5b = tail call ptr @f3(ptr %tmp5)
%tmp5c = getelementptr inbounds i32, ptr %tmp5b, i64 -1
br label %bb9
bb6: ; preds = %bb1
%tmp7 = tail call ptr @f2(ptr %arg)
ret ptr %tmp7
bb9: ; preds = %bb4, %bb
%tmp10 = phi ptr [ %tmp5c, %bb4 ], [ inttoptr (i64 4 to ptr), %bb ]
ret ptr %tmp10
}
define internal ptr @f2(ptr %arg) {
; TUNIT: Function Attrs: nofree nosync nounwind memory(argmem: read)
; TUNIT-LABEL: define {{[^@]+}}@f2
; TUNIT-SAME: (ptr nofree nonnull readonly align 4 dereferenceable(4) [[ARG:%.*]]) #[[ATTR6]] {
; TUNIT-NEXT: bb:
; TUNIT-NEXT: [[TMP:%.*]] = tail call ptr @f1(ptr nofree readonly [[ARG]]) #[[ATTR14]]
; TUNIT-NEXT: ret ptr [[TMP]]
;
; CGSCC: Function Attrs: nofree nosync nounwind memory(argmem: read)
; CGSCC-LABEL: define {{[^@]+}}@f2
; CGSCC-SAME: (ptr nofree nonnull readonly align 4 dereferenceable(4) [[ARG:%.*]]) #[[ATTR5]] {
; CGSCC-NEXT: bb:
; CGSCC-NEXT: [[TMP:%.*]] = tail call ptr @f1(ptr nofree readonly [[ARG]]) #[[ATTR14]]
; CGSCC-NEXT: ret ptr [[TMP]]
;
bb:
%tmp = tail call ptr @f1(ptr %arg)
ret ptr %tmp
}
define dso_local noalias ptr @f3(ptr %arg) {
; FIXME: missing nonnull. It should be nonnull @f3(ptr nonnull readonly %arg)
; TUNIT: Function Attrs: nofree nosync nounwind memory(argmem: read)
; TUNIT-LABEL: define {{[^@]+}}@f3
; TUNIT-SAME: (ptr nofree readonly [[ARG:%.*]]) #[[ATTR6]] {
; TUNIT-NEXT: bb:
; TUNIT-NEXT: [[TMP:%.*]] = call ptr @f1(ptr nofree readonly [[ARG]]) #[[ATTR14]]
; TUNIT-NEXT: ret ptr [[TMP]]
;
; CGSCC: Function Attrs: nofree nosync nounwind memory(argmem: read)
; CGSCC-LABEL: define {{[^@]+}}@f3
; CGSCC-SAME: (ptr nofree readonly [[ARG:%.*]]) #[[ATTR5]] {
; CGSCC-NEXT: bb:
; CGSCC-NEXT: [[TMP:%.*]] = call ptr @f1(ptr nofree readonly [[ARG]]) #[[ATTR14]]
; CGSCC-NEXT: ret ptr [[TMP]]
;
bb:
; FIXME: missing nonnull. It should be @f1(ptr nonnull readonly %arg)
%tmp = call ptr @f1(ptr %arg)
ret ptr %tmp
}
; TEST 15
define void @f15(ptr %arg) {
; CHECK-LABEL: define {{[^@]+}}@f15
; CHECK-SAME: (ptr noundef nonnull dereferenceable(4) [[ARG:%.*]]) {
; CHECK-NEXT: tail call void @use1(ptr noundef nonnull dereferenceable(4) [[ARG]])
; CHECK-NEXT: ret void
;
tail call void @use1(ptr dereferenceable(4) %arg)
ret void
}
declare void @fun0() #1
declare void @fun1(ptr) #1
declare void @fun2(ptr, ptr) #1
declare void @fun3(ptr, ptr, ptr) #1
; TEST 16 simple path test
; if(..)
; fun2(nonnull %a, nonnull %b)
; else
; fun2(nonnull %a, %b)
; We can say that %a is nonnull but %b is not.
define void @f16(ptr %a, ptr %b, i8 %c) {
; TUNIT: Function Attrs: nounwind willreturn
; TUNIT-LABEL: define {{[^@]+}}@f16
; TUNIT-SAME: (ptr nonnull [[A:%.*]], ptr [[B:%.*]], i8 [[C:%.*]]) #[[ATTR7:[0-9]+]] {
; TUNIT-NEXT: [[CMP:%.*]] = icmp eq i8 [[C]], 0
; TUNIT-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
; TUNIT: if.then:
; TUNIT-NEXT: tail call void @fun2(ptr nonnull [[A]], ptr nonnull [[B]]) #[[ATTR7]]
; TUNIT-NEXT: ret void
; TUNIT: if.else:
; TUNIT-NEXT: tail call void @fun2(ptr nonnull [[A]], ptr [[B]]) #[[ATTR7]]
; TUNIT-NEXT: ret void
;
; CGSCC: Function Attrs: nounwind willreturn
; CGSCC-LABEL: define {{[^@]+}}@f16
; CGSCC-SAME: (ptr nonnull [[A:%.*]], ptr [[B:%.*]], i8 [[C:%.*]]) #[[ATTR6:[0-9]+]] {
; CGSCC-NEXT: [[CMP:%.*]] = icmp eq i8 [[C]], 0
; CGSCC-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
; CGSCC: if.then:
; CGSCC-NEXT: tail call void @fun2(ptr nonnull [[A]], ptr nonnull [[B]]) #[[ATTR6]]
; CGSCC-NEXT: ret void
; CGSCC: if.else:
; CGSCC-NEXT: tail call void @fun2(ptr nonnull [[A]], ptr [[B]]) #[[ATTR6]]
; CGSCC-NEXT: ret void
;
%cmp = icmp eq i8 %c, 0
br i1 %cmp, label %if.then, label %if.else
if.then:
tail call void @fun2(ptr nonnull %a, ptr nonnull %b)
ret void
if.else:
tail call void @fun2(ptr nonnull %a, ptr %b)
ret void
}
; TEST 17 explore child BB test
; if(..)
; ... (willreturn & nounwind)
; else
; ... (willreturn & nounwind)
; fun1(nonnull %a)
; We can say that %a is nonnull
define void @f17(ptr %a, i8 %c) {
;
; TUNIT: Function Attrs: nounwind willreturn
; TUNIT-LABEL: define {{[^@]+}}@f17
; TUNIT-SAME: (ptr nonnull [[A:%.*]], i8 [[C:%.*]]) #[[ATTR7]] {
; TUNIT-NEXT: [[CMP:%.*]] = icmp eq i8 [[C]], 0
; TUNIT-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
; TUNIT: if.then:
; TUNIT-NEXT: tail call void @fun0() #[[ATTR7]]
; TUNIT-NEXT: br label [[CONT:%.*]]
; TUNIT: if.else:
; TUNIT-NEXT: tail call void @fun0() #[[ATTR7]]
; TUNIT-NEXT: br label [[CONT]]
; TUNIT: cont:
; TUNIT-NEXT: tail call void @fun1(ptr nonnull [[A]]) #[[ATTR7]]
; TUNIT-NEXT: ret void
;
; CGSCC: Function Attrs: nounwind willreturn
; CGSCC-LABEL: define {{[^@]+}}@f17
; CGSCC-SAME: (ptr nonnull [[A:%.*]], i8 [[C:%.*]]) #[[ATTR6]] {
; CGSCC-NEXT: [[CMP:%.*]] = icmp eq i8 [[C]], 0
; CGSCC-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
; CGSCC: if.then:
; CGSCC-NEXT: tail call void @fun0() #[[ATTR6]]
; CGSCC-NEXT: br label [[CONT:%.*]]
; CGSCC: if.else:
; CGSCC-NEXT: tail call void @fun0() #[[ATTR6]]
; CGSCC-NEXT: br label [[CONT]]
; CGSCC: cont:
; CGSCC-NEXT: tail call void @fun1(ptr nonnull [[A]]) #[[ATTR6]]
; CGSCC-NEXT: ret void
;
%cmp = icmp eq i8 %c, 0
br i1 %cmp, label %if.then, label %if.else
if.then:
tail call void @fun0()
br label %cont
if.else:
tail call void @fun0()
br label %cont
cont:
tail call void @fun1(ptr nonnull %a)
ret void
}
; TEST 18 More complex test
; if(..)
; ... (willreturn & nounwind)
; else
; ... (willreturn & nounwind)
; if(..)
; ... (willreturn & nounwind)
; else
; ... (willreturn & nounwind)
; fun1(nonnull %a)
define void @f18(ptr %a, ptr %b, i8 %c) {
; TUNIT: Function Attrs: nounwind willreturn
; TUNIT-LABEL: define {{[^@]+}}@f18
; TUNIT-SAME: (ptr nonnull [[A:%.*]], ptr [[B:%.*]], i8 [[C:%.*]]) #[[ATTR7]] {
; TUNIT-NEXT: [[CMP1:%.*]] = icmp eq i8 [[C]], 0
; TUNIT-NEXT: br i1 [[CMP1]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
; TUNIT: if.then:
; TUNIT-NEXT: tail call void @fun0() #[[ATTR7]]
; TUNIT-NEXT: br label [[CONT:%.*]]
; TUNIT: if.else:
; TUNIT-NEXT: tail call void @fun0() #[[ATTR7]]
; TUNIT-NEXT: br label [[CONT]]
; TUNIT: cont:
; TUNIT-NEXT: [[CMP2:%.*]] = icmp eq i8 [[C]], 1
; TUNIT-NEXT: br i1 [[CMP2]], label [[CONT_THEN:%.*]], label [[CONT_ELSE:%.*]]
; TUNIT: cont.then:
; TUNIT-NEXT: tail call void @fun1(ptr nonnull [[B]]) #[[ATTR7]]
; TUNIT-NEXT: br label [[CONT2:%.*]]
; TUNIT: cont.else:
; TUNIT-NEXT: tail call void @fun0() #[[ATTR7]]
; TUNIT-NEXT: br label [[CONT2]]
; TUNIT: cont2:
; TUNIT-NEXT: tail call void @fun1(ptr nonnull [[A]]) #[[ATTR7]]
; TUNIT-NEXT: ret void
;
; CGSCC: Function Attrs: nounwind willreturn
; CGSCC-LABEL: define {{[^@]+}}@f18
; CGSCC-SAME: (ptr nonnull [[A:%.*]], ptr [[B:%.*]], i8 [[C:%.*]]) #[[ATTR6]] {
; CGSCC-NEXT: [[CMP1:%.*]] = icmp eq i8 [[C]], 0
; CGSCC-NEXT: br i1 [[CMP1]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
; CGSCC: if.then:
; CGSCC-NEXT: tail call void @fun0() #[[ATTR6]]
; CGSCC-NEXT: br label [[CONT:%.*]]
; CGSCC: if.else:
; CGSCC-NEXT: tail call void @fun0() #[[ATTR6]]
; CGSCC-NEXT: br label [[CONT]]
; CGSCC: cont:
; CGSCC-NEXT: [[CMP2:%.*]] = icmp eq i8 [[C]], 1
; CGSCC-NEXT: br i1 [[CMP2]], label [[CONT_THEN:%.*]], label [[CONT_ELSE:%.*]]
; CGSCC: cont.then:
; CGSCC-NEXT: tail call void @fun1(ptr nonnull [[B]]) #[[ATTR6]]
; CGSCC-NEXT: br label [[CONT2:%.*]]
; CGSCC: cont.else:
; CGSCC-NEXT: tail call void @fun0() #[[ATTR6]]
; CGSCC-NEXT: br label [[CONT2]]
; CGSCC: cont2:
; CGSCC-NEXT: tail call void @fun1(ptr nonnull [[A]]) #[[ATTR6]]
; CGSCC-NEXT: ret void
;
%cmp1 = icmp eq i8 %c, 0
br i1 %cmp1, label %if.then, label %if.else
if.then:
tail call void @fun0()
br label %cont
if.else:
tail call void @fun0()
br label %cont
cont:
%cmp2 = icmp eq i8 %c, 1
br i1 %cmp2, label %cont.then, label %cont.else
cont.then:
tail call void @fun1(ptr nonnull %b)
br label %cont2
cont.else:
tail call void @fun0()
br label %cont2
cont2:
tail call void @fun1(ptr nonnull %a)
ret void
}
; TEST 19: Loop
define void @f19(ptr %a, ptr %b, i8 %c) {
; TUNIT: Function Attrs: nounwind
; TUNIT-LABEL: define {{[^@]+}}@f19
; TUNIT-SAME: (ptr [[A:%.*]], ptr nonnull [[B:%.*]], i8 [[C:%.*]]) #[[ATTR5]] {
; TUNIT-NEXT: br label [[LOOP_HEADER:%.*]]
; TUNIT: loop.header:
; TUNIT-NEXT: [[CMP2:%.*]] = icmp eq i8 [[C]], 0
; TUNIT-NEXT: br i1 [[CMP2]], label [[LOOP_BODY:%.*]], label [[LOOP_EXIT:%.*]]
; TUNIT: loop.body:
; TUNIT-NEXT: tail call void @fun1(ptr nonnull [[B]]) #[[ATTR5]]
; TUNIT-NEXT: tail call void @fun1(ptr nonnull [[A]]) #[[ATTR5]]
; TUNIT-NEXT: br label [[LOOP_HEADER]]
; TUNIT: loop.exit:
; TUNIT-NEXT: tail call void @fun1(ptr nonnull [[B]]) #[[ATTR5]]
; TUNIT-NEXT: ret void
;
; CGSCC: Function Attrs: nounwind
; CGSCC-LABEL: define {{[^@]+}}@f19
; CGSCC-SAME: (ptr [[A:%.*]], ptr nonnull [[B:%.*]], i8 [[C:%.*]]) #[[ATTR4]] {
; CGSCC-NEXT: br label [[LOOP_HEADER:%.*]]
; CGSCC: loop.header:
; CGSCC-NEXT: [[CMP2:%.*]] = icmp eq i8 [[C]], 0
; CGSCC-NEXT: br i1 [[CMP2]], label [[LOOP_BODY:%.*]], label [[LOOP_EXIT:%.*]]
; CGSCC: loop.body:
; CGSCC-NEXT: tail call void @fun1(ptr nonnull [[B]]) #[[ATTR4]]
; CGSCC-NEXT: tail call void @fun1(ptr nonnull [[A]]) #[[ATTR4]]
; CGSCC-NEXT: br label [[LOOP_HEADER]]
; CGSCC: loop.exit:
; CGSCC-NEXT: tail call void @fun1(ptr nonnull [[B]]) #[[ATTR4]]
; CGSCC-NEXT: ret void
;
br label %loop.header
loop.header:
%cmp2 = icmp eq i8 %c, 0
br i1 %cmp2, label %loop.body, label %loop.exit
loop.body:
tail call void @fun1(ptr nonnull %b)
tail call void @fun1(ptr nonnull %a)
br label %loop.header
loop.exit:
tail call void @fun1(ptr nonnull %b)
ret void
}
; Test propagation of nonnull callsite args back to caller.
declare void @use1(ptr %x)
declare void @use2(ptr %x, ptr %y);
declare void @use3(ptr %x, ptr %y, ptr %z);
declare void @use1nonnull(ptr nonnull %x);
declare void @use2nonnull(ptr nonnull %x, ptr nonnull %y);
declare void @use3nonnull(ptr nonnull %x, ptr nonnull %y, ptr nonnull %z);
declare i8 @use1safecall(ptr %x) readonly nounwind willreturn ; nounwind+willreturn guarantees that execution continues to successor
; Can't extend non-null to parent for any argument because the 2nd call is not guaranteed to execute.
define void @parent1(ptr %a, ptr %b, ptr %c) {
; CHECK-LABEL: define {{[^@]+}}@parent1
; CHECK-SAME: (ptr [[A:%.*]], ptr [[B:%.*]], ptr [[C:%.*]]) {
; CHECK-NEXT: call void @use3(ptr [[C]], ptr [[A]], ptr [[B]])
; CHECK-NEXT: call void @use3nonnull(ptr nonnull [[B]], ptr nonnull [[C]], ptr nonnull [[A]])
; CHECK-NEXT: ret void
;
call void @use3(ptr %c, ptr %a, ptr %b)
call void @use3nonnull(ptr %b, ptr %c, ptr %a)
ret void
}
; Extend non-null to parent for all arguments.
define void @parent2(ptr %a, ptr %b, ptr %c) {
; CHECK-LABEL: define {{[^@]+}}@parent2
; CHECK-SAME: (ptr nonnull [[A:%.*]], ptr nonnull [[B:%.*]], ptr nonnull [[C:%.*]]) {
; CHECK-NEXT: call void @use3nonnull(ptr nonnull [[B]], ptr nonnull [[C]], ptr nonnull [[A]])
; CHECK-NEXT: call void @use3(ptr nonnull [[C]], ptr nonnull [[A]], ptr nonnull [[B]])
; CHECK-NEXT: ret void
;
call void @use3nonnull(ptr %b, ptr %c, ptr %a)
call void @use3(ptr %c, ptr %a, ptr %b)
ret void
}
; Extend non-null to parent for 1st argument.
define void @parent3(ptr %a, ptr %b, ptr %c) {
; CHECK-LABEL: define {{[^@]+}}@parent3
; CHECK-SAME: (ptr nonnull [[A:%.*]], ptr [[B:%.*]], ptr [[C:%.*]]) {
; CHECK-NEXT: call void @use1nonnull(ptr nonnull [[A]])
; CHECK-NEXT: call void @use3(ptr [[C]], ptr [[B]], ptr nonnull [[A]])
; CHECK-NEXT: ret void
;
call void @use1nonnull(ptr %a)
call void @use3(ptr %c, ptr %b, ptr %a)
ret void
}
; Extend non-null to parent for last 2 arguments.
define void @parent4(ptr %a, ptr %b, ptr %c) {
; CHECK-LABEL: define {{[^@]+}}@parent4
; CHECK-SAME: (ptr [[A:%.*]], ptr nonnull [[B:%.*]], ptr nonnull [[C:%.*]]) {
; CHECK-NEXT: call void @use2nonnull(ptr nonnull [[C]], ptr nonnull [[B]])
; CHECK-NEXT: call void @use2(ptr [[A]], ptr nonnull [[C]])
; CHECK-NEXT: call void @use1(ptr nonnull [[B]])
; CHECK-NEXT: ret void
;
call void @use2nonnull(ptr %c, ptr %b)
call void @use2(ptr %a, ptr %c)
call void @use1(ptr %b)
ret void
}
; The callsite must execute in order for the attribute to transfer to the parent.
; It appears benign to extend non-null to the parent in this case, but we can't do that
; because it would incorrectly propagate the wrong information to its callers.
define void @parent5(ptr %a, i1 %a_is_notnull) {
; CHECK-LABEL: define {{[^@]+}}@parent5
; CHECK-SAME: (ptr [[A:%.*]], i1 noundef [[A_IS_NOTNULL:%.*]]) {
; CHECK-NEXT: br i1 [[A_IS_NOTNULL]], label [[T:%.*]], label [[F:%.*]]
; CHECK: t:
; CHECK-NEXT: call void @use1nonnull(ptr nonnull [[A]])
; CHECK-NEXT: ret void
; CHECK: f:
; CHECK-NEXT: ret void
;
br i1 %a_is_notnull, label %t, label %f
t:
call void @use1nonnull(ptr %a)
ret void
f:
ret void
}
; The callsite must execute in order for the attribute to transfer to the parent.
; The volatile load can't trap, so we can guarantee that we'll get to the call.
define i8 @parent6(ptr %a, ptr %b) {
; CHECK-LABEL: define {{[^@]+}}@parent6
; CHECK-SAME: (ptr nonnull [[A:%.*]], ptr nofree noundef [[B:%.*]]) {
; CHECK-NEXT: [[C:%.*]] = load volatile i8, ptr [[B]], align 1
; CHECK-NEXT: call void @use1nonnull(ptr nonnull [[A]])
; CHECK-NEXT: ret i8 [[C]]
;
%c = load volatile i8, ptr %b
call void @use1nonnull(ptr %a)
ret i8 %c
}
; The nonnull callsite is guaranteed to execute, so the argument must be nonnull throughout the parent.
define i8 @parent7(ptr %a) {
; CHECK-LABEL: define {{[^@]+}}@parent7
; CHECK-SAME: (ptr nonnull [[A:%.*]]) {
; CHECK-NEXT: [[RET:%.*]] = call i8 @use1safecall(ptr nonnull readonly [[A]]) #[[ATTR13]]
; CHECK-NEXT: call void @use1nonnull(ptr nonnull [[A]])
; CHECK-NEXT: ret i8 [[RET]]
;
%ret = call i8 @use1safecall(ptr %a)
call void @use1nonnull(ptr %a)
ret i8 %ret
}
; Make sure that an invoke works similarly to a call.
declare i32 @esfp(...)
define i1 @parent8(ptr %a, ptr %bogus1, ptr %b) personality ptr @esfp{
; TUNIT: Function Attrs: nounwind
; TUNIT-LABEL: define {{[^@]+}}@parent8
; TUNIT-SAME: (ptr nonnull [[A:%.*]], ptr nocapture nofree readnone [[BOGUS1:%.*]], ptr nonnull [[B:%.*]]) #[[ATTR5]] personality ptr @esfp {
; TUNIT-NEXT: entry:
; TUNIT-NEXT: invoke void @use2nonnull(ptr nonnull [[A]], ptr nonnull [[B]])
; TUNIT-NEXT: to label [[CONT:%.*]] unwind label [[EXC:%.*]]
; TUNIT: cont:
; TUNIT-NEXT: ret i1 false
; TUNIT: exc:
; TUNIT-NEXT: [[LP:%.*]] = landingpad { ptr, i32 }
; TUNIT-NEXT: filter [0 x ptr] zeroinitializer
; TUNIT-NEXT: unreachable
;
; CGSCC: Function Attrs: nounwind
; CGSCC-LABEL: define {{[^@]+}}@parent8
; CGSCC-SAME: (ptr nonnull [[A:%.*]], ptr nocapture nofree readnone [[BOGUS1:%.*]], ptr nonnull [[B:%.*]]) #[[ATTR4]] personality ptr @esfp {
; CGSCC-NEXT: entry:
; CGSCC-NEXT: invoke void @use2nonnull(ptr nonnull [[A]], ptr nonnull [[B]])
; CGSCC-NEXT: to label [[CONT:%.*]] unwind label [[EXC:%.*]]
; CGSCC: cont:
; CGSCC-NEXT: ret i1 false
; CGSCC: exc:
; CGSCC-NEXT: [[LP:%.*]] = landingpad { ptr, i32 }
; CGSCC-NEXT: filter [0 x ptr] zeroinitializer
; CGSCC-NEXT: unreachable
;
entry:
invoke void @use2nonnull(ptr %a, ptr %b)
to label %cont unwind label %exc
cont:
%null_check = icmp eq ptr %b, null
ret i1 %null_check
exc:
%lp = landingpad { ptr, i32 }
filter [0 x ptr] zeroinitializer
unreachable
}
define ptr @gep1(ptr %p) {
; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none)
; CHECK-LABEL: define {{[^@]+}}@gep1
; CHECK-SAME: (ptr nofree readnone "no-capture-maybe-returned" [[P:%.*]]) #[[ATTR1]] {
; CHECK-NEXT: [[Q:%.*]] = getelementptr inbounds i32, ptr [[P]], i32 1
; CHECK-NEXT: ret ptr [[Q]]
;
%q = getelementptr inbounds i32, ptr %p, i32 1
ret ptr %q
}
define ptr @gep1_no_null_opt(ptr %p) #0 {
; Should't be able to derive nonnull based on gep.
; TUNIT: Function Attrs: nofree norecurse nosync nounwind null_pointer_is_valid willreturn memory(none)
; TUNIT-LABEL: define {{[^@]+}}@gep1_no_null_opt
; TUNIT-SAME: (ptr nofree readnone "no-capture-maybe-returned" [[P:%.*]]) #[[ATTR9:[0-9]+]] {
; TUNIT-NEXT: [[Q:%.*]] = getelementptr inbounds i32, ptr [[P]], i32 1
; TUNIT-NEXT: ret ptr [[Q]]
;
; CGSCC: Function Attrs: nofree norecurse nosync nounwind null_pointer_is_valid willreturn memory(none)
; CGSCC-LABEL: define {{[^@]+}}@gep1_no_null_opt
; CGSCC-SAME: (ptr nofree readnone "no-capture-maybe-returned" [[P:%.*]]) #[[ATTR8:[0-9]+]] {
; CGSCC-NEXT: [[Q:%.*]] = getelementptr inbounds i32, ptr [[P]], i32 1
; CGSCC-NEXT: ret ptr [[Q]]
;
%q = getelementptr inbounds i32, ptr %p, i32 1
ret ptr %q
}
define ptr addrspace(3) @gep2(ptr addrspace(3) %p) {
; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none)
; CHECK-LABEL: define {{[^@]+}}@gep2
; CHECK-SAME: (ptr addrspace(3) nofree readnone "no-capture-maybe-returned" [[P:%.*]]) #[[ATTR1]] {
; CHECK-NEXT: [[Q:%.*]] = getelementptr inbounds i32, ptr addrspace(3) [[P]], i32 1
; CHECK-NEXT: ret ptr addrspace(3) [[Q]]
;
%q = getelementptr inbounds i32, ptr addrspace(3) %p, i32 1
ret ptr addrspace(3) %q
}
; FIXME: We should propagate dereferenceable here but *not* nonnull
define ptr addrspace(3) @as(ptr addrspace(3) dereferenceable(4) %p) {
; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none)
; CHECK-LABEL: define {{[^@]+}}@as
; CHECK-SAME: (ptr addrspace(3) nofree nonnull readnone returned dereferenceable(4) "no-capture-maybe-returned" [[P:%.*]]) #[[ATTR1]] {
; CHECK-NEXT: ret ptr addrspace(3) [[P]]
;
ret ptr addrspace(3) %p
}
; CHECK-NOT: @g2()
define internal ptr @g2() {
; CGSCC: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none)
; CGSCC-LABEL: define {{[^@]+}}@g2
; CGSCC-SAME: () #[[ATTR1]] {
; CGSCC-NEXT: ret ptr inttoptr (i64 4 to ptr)
;
ret ptr inttoptr (i64 4 to ptr)
}
define ptr @g1() {
; TUNIT: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none)
; TUNIT-LABEL: define {{[^@]+}}@g1
; TUNIT-SAME: () #[[ATTR1]] {
; TUNIT-NEXT: ret ptr inttoptr (i64 4 to ptr)
;
; CGSCC: Function Attrs: nofree nosync nounwind willreturn memory(none)
; CGSCC-LABEL: define {{[^@]+}}@g1
; CGSCC-SAME: () #[[ATTR9:[0-9]+]] {
; CGSCC-NEXT: [[C:%.*]] = call noundef nonnull align 4 ptr @g2() #[[ATTR13]]
; CGSCC-NEXT: ret ptr [[C]]
;
%c = call ptr @g2()
ret ptr %c
}
declare void @use_i32_ptr(ptr readnone nocapture) nounwind
define internal void @called_by_weak(ptr %a) {
; TUNIT: Function Attrs: nounwind
; TUNIT-LABEL: define {{[^@]+}}@called_by_weak
; TUNIT-SAME: (ptr noalias nocapture nonnull readnone [[A:%.*]]) #[[ATTR5]] {
; TUNIT-NEXT: call void @use_i32_ptr(ptr noalias nocapture nonnull readnone [[A]]) #[[ATTR5]]
; TUNIT-NEXT: ret void
;
; CGSCC: Function Attrs: nounwind
; CGSCC-LABEL: define {{[^@]+}}@called_by_weak
; CGSCC-SAME: (ptr noalias nocapture nonnull readnone [[A:%.*]]) #[[ATTR4]] {
; CGSCC-NEXT: call void @use_i32_ptr(ptr noalias nocapture nonnull readnone [[A]]) #[[ATTR4]]
; CGSCC-NEXT: ret void
;
call void @use_i32_ptr(ptr %a)
ret void
}
; Check we do not annotate the function interface of this weak function.
define weak_odr void @weak_caller(ptr nonnull %a) {
;
; TUNIT-LABEL: define {{[^@]+}}@weak_caller
; TUNIT-SAME: (ptr nonnull [[A:%.*]]) {
; TUNIT-NEXT: call void @called_by_weak(ptr noalias nocapture nonnull readnone [[A]]) #[[ATTR5]]
; TUNIT-NEXT: ret void
;
; CGSCC-LABEL: define {{[^@]+}}@weak_caller
; CGSCC-SAME: (ptr nonnull [[A:%.*]]) {
; CGSCC-NEXT: call void @called_by_weak(ptr noalias nocapture nonnull readnone [[A]]) #[[ATTR4]]
; CGSCC-NEXT: ret void
;
call void @called_by_weak(ptr %a)
ret void
}
; Expect nonnull
define internal void @control(ptr dereferenceable(4) %a) {
; TUNIT: Function Attrs: nounwind
; TUNIT-LABEL: define {{[^@]+}}@control
; TUNIT-SAME: (ptr noalias nocapture noundef nonnull readnone align 16 dereferenceable(8) [[A:%.*]]) #[[ATTR5]] {
; TUNIT-NEXT: call void @use_i32_ptr(ptr noalias nocapture noundef nonnull readnone align 16 dereferenceable(8) [[A]]) #[[ATTR5]]
; TUNIT-NEXT: ret void
;
; CGSCC: Function Attrs: nounwind
; CGSCC-LABEL: define {{[^@]+}}@control
; CGSCC-SAME: (ptr noalias nocapture noundef nonnull readnone align 16 dereferenceable(8) [[A:%.*]]) #[[ATTR4]] {
; CGSCC-NEXT: call void @use_i32_ptr(ptr noalias nocapture noundef nonnull readnone align 16 dereferenceable(8) [[A]]) #[[ATTR4]]
; CGSCC-NEXT: ret void
;
call void @use_i32_ptr(ptr %a)
ret void
}
; Avoid nonnull as we do not touch naked functions
define internal void @naked(ptr dereferenceable(4) %a) naked {
; CHECK: Function Attrs: naked
; CHECK-LABEL: define {{[^@]+}}@naked
; CHECK-SAME: (ptr dereferenceable(4) [[A:%.*]]) #[[ATTR10:[0-9]+]] {
; CHECK-NEXT: call void @use_i32_ptr(ptr [[A]])
; CHECK-NEXT: ret void
;
call void @use_i32_ptr(ptr %a)
ret void
}
; Avoid nonnull as we do not touch optnone
define internal void @optnone(ptr dereferenceable(4) %a) optnone noinline {
;
; CHECK: Function Attrs: noinline optnone
; CHECK-LABEL: define {{[^@]+}}@optnone
; CHECK-SAME: (ptr dereferenceable(4) [[A:%.*]]) #[[ATTR11:[0-9]+]] {
; CHECK-NEXT: call void @use_i32_ptr(ptr [[A]])
; CHECK-NEXT: ret void
;
call void @use_i32_ptr(ptr %a)
ret void
}
define void @make_live(ptr nonnull dereferenceable(8) %a) {
; TUNIT-LABEL: define {{[^@]+}}@make_live
; TUNIT-SAME: (ptr noundef nonnull align 16 dereferenceable(8) [[A:%.*]]) {
; TUNIT-NEXT: call void @naked(ptr noundef nonnull align 16 dereferenceable(8) [[A]])
; TUNIT-NEXT: call void @control(ptr noalias nocapture noundef nonnull readnone align 16 dereferenceable(8) [[A]]) #[[ATTR5]]
; TUNIT-NEXT: call void @optnone(ptr noundef nonnull align 16 dereferenceable(8) [[A]])
; TUNIT-NEXT: ret void
;
; CGSCC-LABEL: define {{[^@]+}}@make_live
; CGSCC-SAME: (ptr noundef nonnull align 16 dereferenceable(8) [[A:%.*]]) {
; CGSCC-NEXT: call void @naked(ptr noundef nonnull align 16 dereferenceable(8) [[A]])
; CGSCC-NEXT: call void @control(ptr noalias nocapture noundef nonnull readnone align 16 dereferenceable(8) [[A]]) #[[ATTR4]]
; CGSCC-NEXT: call void @optnone(ptr noundef nonnull align 16 dereferenceable(8) [[A]])
; CGSCC-NEXT: ret void
;
call void @naked(ptr nonnull dereferenceable(8) align 16 %a)
call void @control(ptr nonnull dereferenceable(8) align 16 %a)
call void @optnone(ptr nonnull dereferenceable(8) align 16 %a)
ret void
}
;int f(int *u, int n){
; for(int i = 0;i<n;i++){
; h(u);
; }
; return g(nonnull u);
;}
declare void @h(ptr) willreturn nounwind
declare i32 @g(ptr) willreturn nounwind
define i32 @nonnull_exec_ctx_1(ptr %a, i32 %b) {
;
; TUNIT: Function Attrs: nounwind willreturn
; TUNIT-LABEL: define {{[^@]+}}@nonnull_exec_ctx_1
; TUNIT-SAME: (ptr [[A:%.*]], i32 [[B:%.*]]) #[[ATTR7]] {
; TUNIT-NEXT: en:
; TUNIT-NEXT: [[TMP3:%.*]] = icmp eq i32 [[B]], 0
; TUNIT-NEXT: br i1 [[TMP3]], label [[EX:%.*]], label [[HD:%.*]]
; TUNIT: ex:
; TUNIT-NEXT: [[TMP5:%.*]] = tail call i32 @g(ptr nonnull [[A]]) #[[ATTR7]]
; TUNIT-NEXT: ret i32 [[TMP5]]
; TUNIT: hd:
; TUNIT-NEXT: [[TMP7:%.*]] = phi i32 [ [[TMP8:%.*]], [[HD]] ], [ 0, [[EN:%.*]] ]
; TUNIT-NEXT: tail call void @h(ptr [[A]]) #[[ATTR7]]
; TUNIT-NEXT: [[TMP8]] = add nuw i32 [[TMP7]], 1
; TUNIT-NEXT: [[TMP9:%.*]] = icmp eq i32 [[TMP8]], [[B]]
; TUNIT-NEXT: br i1 [[TMP9]], label [[EX]], label [[HD]]
;
; CGSCC: Function Attrs: nounwind willreturn
; CGSCC-LABEL: define {{[^@]+}}@nonnull_exec_ctx_1
; CGSCC-SAME: (ptr [[A:%.*]], i32 [[B:%.*]]) #[[ATTR6]] {
; CGSCC-NEXT: en:
; CGSCC-NEXT: [[TMP3:%.*]] = icmp eq i32 [[B]], 0
; CGSCC-NEXT: br i1 [[TMP3]], label [[EX:%.*]], label [[HD:%.*]]
; CGSCC: ex:
; CGSCC-NEXT: [[TMP5:%.*]] = tail call i32 @g(ptr nonnull [[A]]) #[[ATTR6]]
; CGSCC-NEXT: ret i32 [[TMP5]]
; CGSCC: hd:
; CGSCC-NEXT: [[TMP7:%.*]] = phi i32 [ [[TMP8:%.*]], [[HD]] ], [ 0, [[EN:%.*]] ]
; CGSCC-NEXT: tail call void @h(ptr [[A]]) #[[ATTR6]]
; CGSCC-NEXT: [[TMP8]] = add nuw i32 [[TMP7]], 1
; CGSCC-NEXT: [[TMP9:%.*]] = icmp eq i32 [[TMP8]], [[B]]
; CGSCC-NEXT: br i1 [[TMP9]], label [[EX]], label [[HD]]
;
en:
%tmp3 = icmp eq i32 %b, 0
br i1 %tmp3, label %ex, label %hd
ex:
%tmp5 = tail call i32 @g(ptr nonnull %a)
ret i32 %tmp5
hd:
%tmp7 = phi i32 [ %tmp8, %hd ], [ 0, %en ]
tail call void @h(ptr %a)
%tmp8 = add nuw i32 %tmp7, 1
%tmp9 = icmp eq i32 %tmp8, %b
br i1 %tmp9, label %ex, label %hd
}
define i32 @nonnull_exec_ctx_1b(ptr %a, i32 %b) {
;
; TUNIT: Function Attrs: nounwind willreturn
; TUNIT-LABEL: define {{[^@]+}}@nonnull_exec_ctx_1b
; TUNIT-SAME: (ptr [[A:%.*]], i32 [[B:%.*]]) #[[ATTR7]] {
; TUNIT-NEXT: en:
; TUNIT-NEXT: [[TMP3:%.*]] = icmp eq i32 [[B]], 0
; TUNIT-NEXT: br i1 [[TMP3]], label [[EX:%.*]], label [[HD:%.*]]
; TUNIT: ex:
; TUNIT-NEXT: [[TMP5:%.*]] = tail call i32 @g(ptr nonnull [[A]]) #[[ATTR7]]
; TUNIT-NEXT: ret i32 [[TMP5]]
; TUNIT: hd:
; TUNIT-NEXT: [[TMP7:%.*]] = phi i32 [ [[TMP8:%.*]], [[HD2:%.*]] ], [ 0, [[EN:%.*]] ]
; TUNIT-NEXT: tail call void @h(ptr [[A]]) #[[ATTR7]]
; TUNIT-NEXT: br label [[HD2]]
; TUNIT: hd2:
; TUNIT-NEXT: [[TMP8]] = add nuw i32 [[TMP7]], 1
; TUNIT-NEXT: [[TMP9:%.*]] = icmp eq i32 [[TMP8]], [[B]]
; TUNIT-NEXT: br i1 [[TMP9]], label [[EX]], label [[HD]]
;
; CGSCC: Function Attrs: nounwind willreturn
; CGSCC-LABEL: define {{[^@]+}}@nonnull_exec_ctx_1b
; CGSCC-SAME: (ptr [[A:%.*]], i32 [[B:%.*]]) #[[ATTR6]] {
; CGSCC-NEXT: en:
; CGSCC-NEXT: [[TMP3:%.*]] = icmp eq i32 [[B]], 0
; CGSCC-NEXT: br i1 [[TMP3]], label [[EX:%.*]], label [[HD:%.*]]
; CGSCC: ex:
; CGSCC-NEXT: [[TMP5:%.*]] = tail call i32 @g(ptr nonnull [[A]]) #[[ATTR6]]
; CGSCC-NEXT: ret i32 [[TMP5]]
; CGSCC: hd:
; CGSCC-NEXT: [[TMP7:%.*]] = phi i32 [ [[TMP8:%.*]], [[HD2:%.*]] ], [ 0, [[EN:%.*]] ]
; CGSCC-NEXT: tail call void @h(ptr [[A]]) #[[ATTR6]]
; CGSCC-NEXT: br label [[HD2]]
; CGSCC: hd2:
; CGSCC-NEXT: [[TMP8]] = add nuw i32 [[TMP7]], 1
; CGSCC-NEXT: [[TMP9:%.*]] = icmp eq i32 [[TMP8]], [[B]]
; CGSCC-NEXT: br i1 [[TMP9]], label [[EX]], label [[HD]]
;
en:
%tmp3 = icmp eq i32 %b, 0
br i1 %tmp3, label %ex, label %hd
ex:
%tmp5 = tail call i32 @g(ptr nonnull %a)
ret i32 %tmp5
hd:
%tmp7 = phi i32 [ %tmp8, %hd2 ], [ 0, %en ]
tail call void @h(ptr %a)
br label %hd2
hd2:
%tmp8 = add nuw i32 %tmp7, 1
%tmp9 = icmp eq i32 %tmp8, %b
br i1 %tmp9, label %ex, label %hd
}
define i32 @nonnull_exec_ctx_2(ptr %a, i32 %b) willreturn nounwind {
;
; TUNIT: Function Attrs: nounwind willreturn
; TUNIT-LABEL: define {{[^@]+}}@nonnull_exec_ctx_2
; TUNIT-SAME: (ptr nonnull [[A:%.*]], i32 [[B:%.*]]) #[[ATTR7]] {
; TUNIT-NEXT: en:
; TUNIT-NEXT: [[TMP3:%.*]] = icmp eq i32 [[B]], 0
; TUNIT-NEXT: br i1 [[TMP3]], label [[EX:%.*]], label [[HD:%.*]]
; TUNIT: ex:
; TUNIT-NEXT: [[TMP5:%.*]] = tail call i32 @g(ptr nonnull [[A]]) #[[ATTR5]]
; TUNIT-NEXT: ret i32 [[TMP5]]
; TUNIT: hd:
; TUNIT-NEXT: [[TMP7:%.*]] = phi i32 [ [[TMP8:%.*]], [[HD]] ], [ 0, [[EN:%.*]] ]
; TUNIT-NEXT: tail call void @h(ptr nonnull [[A]]) #[[ATTR5]]
; TUNIT-NEXT: [[TMP8]] = add nuw i32 [[TMP7]], 1
; TUNIT-NEXT: [[TMP9:%.*]] = icmp eq i32 [[TMP8]], [[B]]
; TUNIT-NEXT: br i1 [[TMP9]], label [[EX]], label [[HD]]
;
; CGSCC: Function Attrs: nounwind willreturn
; CGSCC-LABEL: define {{[^@]+}}@nonnull_exec_ctx_2
; CGSCC-SAME: (ptr nonnull [[A:%.*]], i32 [[B:%.*]]) #[[ATTR6]] {
; CGSCC-NEXT: en:
; CGSCC-NEXT: [[TMP3:%.*]] = icmp eq i32 [[B]], 0
; CGSCC-NEXT: br i1 [[TMP3]], label [[EX:%.*]], label [[HD:%.*]]
; CGSCC: ex:
; CGSCC-NEXT: [[TMP5:%.*]] = tail call i32 @g(ptr nonnull [[A]]) #[[ATTR4]]
; CGSCC-NEXT: ret i32 [[TMP5]]
; CGSCC: hd:
; CGSCC-NEXT: [[TMP7:%.*]] = phi i32 [ [[TMP8:%.*]], [[HD]] ], [ 0, [[EN:%.*]] ]
; CGSCC-NEXT: tail call void @h(ptr nonnull [[A]]) #[[ATTR4]]
; CGSCC-NEXT: [[TMP8]] = add nuw i32 [[TMP7]], 1
; CGSCC-NEXT: [[TMP9:%.*]] = icmp eq i32 [[TMP8]], [[B]]
; CGSCC-NEXT: br i1 [[TMP9]], label [[EX]], label [[HD]]
;
en:
%tmp3 = icmp eq i32 %b, 0
br i1 %tmp3, label %ex, label %hd
ex:
%tmp5 = tail call i32 @g(ptr nonnull %a)
ret i32 %tmp5
hd:
%tmp7 = phi i32 [ %tmp8, %hd ], [ 0, %en ]
tail call void @h(ptr %a)
%tmp8 = add nuw i32 %tmp7, 1
%tmp9 = icmp eq i32 %tmp8, %b
br i1 %tmp9, label %ex, label %hd
}
define i32 @nonnull_exec_ctx_2b(ptr %a, i32 %b) willreturn nounwind {
;
; TUNIT: Function Attrs: nounwind willreturn
; TUNIT-LABEL: define {{[^@]+}}@nonnull_exec_ctx_2b
; TUNIT-SAME: (ptr nonnull [[A:%.*]], i32 [[B:%.*]]) #[[ATTR7]] {
; TUNIT-NEXT: en:
; TUNIT-NEXT: [[TMP3:%.*]] = icmp eq i32 [[B]], 0
; TUNIT-NEXT: br i1 [[TMP3]], label [[EX:%.*]], label [[HD:%.*]]
; TUNIT: ex:
; TUNIT-NEXT: [[TMP5:%.*]] = tail call i32 @g(ptr nonnull [[A]]) #[[ATTR5]]
; TUNIT-NEXT: ret i32 [[TMP5]]
; TUNIT: hd:
; TUNIT-NEXT: [[TMP7:%.*]] = phi i32 [ [[TMP8:%.*]], [[HD2:%.*]] ], [ 0, [[EN:%.*]] ]
; TUNIT-NEXT: tail call void @h(ptr nonnull [[A]]) #[[ATTR5]]
; TUNIT-NEXT: br label [[HD2]]
; TUNIT: hd2:
; TUNIT-NEXT: [[TMP8]] = add nuw i32 [[TMP7]], 1
; TUNIT-NEXT: [[TMP9:%.*]] = icmp eq i32 [[TMP8]], [[B]]
; TUNIT-NEXT: br i1 [[TMP9]], label [[EX]], label [[HD]]
;
; CGSCC: Function Attrs: nounwind willreturn
; CGSCC-LABEL: define {{[^@]+}}@nonnull_exec_ctx_2b
; CGSCC-SAME: (ptr nonnull [[A:%.*]], i32 [[B:%.*]]) #[[ATTR6]] {
; CGSCC-NEXT: en:
; CGSCC-NEXT: [[TMP3:%.*]] = icmp eq i32 [[B]], 0
; CGSCC-NEXT: br i1 [[TMP3]], label [[EX:%.*]], label [[HD:%.*]]
; CGSCC: ex:
; CGSCC-NEXT: [[TMP5:%.*]] = tail call i32 @g(ptr nonnull [[A]]) #[[ATTR4]]
; CGSCC-NEXT: ret i32 [[TMP5]]
; CGSCC: hd:
; CGSCC-NEXT: [[TMP7:%.*]] = phi i32 [ [[TMP8:%.*]], [[HD2:%.*]] ], [ 0, [[EN:%.*]] ]
; CGSCC-NEXT: tail call void @h(ptr nonnull [[A]]) #[[ATTR4]]
; CGSCC-NEXT: br label [[HD2]]
; CGSCC: hd2:
; CGSCC-NEXT: [[TMP8]] = add nuw i32 [[TMP7]], 1
; CGSCC-NEXT: [[TMP9:%.*]] = icmp eq i32 [[TMP8]], [[B]]
; CGSCC-NEXT: br i1 [[TMP9]], label [[EX]], label [[HD]]
;
en:
%tmp3 = icmp eq i32 %b, 0
br i1 %tmp3, label %ex, label %hd
ex:
%tmp5 = tail call i32 @g(ptr nonnull %a)
ret i32 %tmp5
hd:
%tmp7 = phi i32 [ %tmp8, %hd2 ], [ 0, %en ]
tail call void @h(ptr %a)
br label %hd2
hd2:
%tmp8 = add nuw i32 %tmp7, 1
%tmp9 = icmp eq i32 %tmp8, %b
br i1 %tmp9, label %ex, label %hd
}
; Original from PR43833
declare void @sink(ptr)
define void @PR43833(ptr %0, i32 %1) {
; CHECK-LABEL: define {{[^@]+}}@PR43833
; CHECK-SAME: (ptr [[TMP0:%.*]], i32 [[TMP1:%.*]]) {
; CHECK-NEXT: [[TMP3:%.*]] = icmp sgt i32 [[TMP1]], 1
; CHECK-NEXT: br i1 [[TMP3]], label [[TMP4:%.*]], label [[TMP7:%.*]]
; CHECK: 4:
; CHECK-NEXT: [[TMP5:%.*]] = zext i32 [[TMP1]] to i64
; CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 [[TMP5]]
; CHECK-NEXT: br label [[TMP8:%.*]]
; CHECK: 7:
; CHECK-NEXT: ret void
; CHECK: 8:
; CHECK-NEXT: [[TMP9:%.*]] = phi i32 [ 1, [[TMP4]] ], [ [[TMP10:%.*]], [[TMP8]] ]
; CHECK-NEXT: tail call void @sink(ptr nonnull [[TMP6]])
; CHECK-NEXT: [[TMP10]] = add nuw nsw i32 [[TMP9]], 1
; CHECK-NEXT: [[TMP11:%.*]] = icmp eq i32 [[TMP10]], [[TMP1]]
; CHECK-NEXT: br i1 [[TMP11]], label [[TMP7]], label [[TMP8]]
;
%3 = icmp sgt i32 %1, 1
br i1 %3, label %4, label %7
4: ; preds = %2
%5 = zext i32 %1 to i64
%6 = getelementptr inbounds i32, ptr %0, i64 %5
br label %8
7: ; preds = %8, %2
ret void
8: ; preds = %8, %4
%9 = phi i32 [ 1, %4 ], [ %10, %8 ]
tail call void @sink(ptr %6)
%10 = add nuw nsw i32 %9, 1
%11 = icmp eq i32 %10, %1
br i1 %11, label %7, label %8
}
; Adjusted from PR43833
define void @PR43833_simple(ptr %0, i32 %1) {
; CHECK-LABEL: define {{[^@]+}}@PR43833_simple
; CHECK-SAME: (ptr [[TMP0:%.*]], i32 [[TMP1:%.*]]) {
; CHECK-NEXT: [[TMP3:%.*]] = icmp ne i32 [[TMP1]], 0
; CHECK-NEXT: br i1 [[TMP3]], label [[TMP4:%.*]], label [[TMP7:%.*]]
; CHECK: 4:
; CHECK-NEXT: [[TMP5:%.*]] = zext i32 [[TMP1]] to i64
; CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 [[TMP5]]
; CHECK-NEXT: br label [[TMP8:%.*]]
; CHECK: 7:
; CHECK-NEXT: ret void
; CHECK: 8:
; CHECK-NEXT: [[TMP9:%.*]] = phi i32 [ 1, [[TMP4]] ], [ [[TMP10:%.*]], [[TMP8]] ]
; CHECK-NEXT: tail call void @sink(ptr nonnull [[TMP6]])
; CHECK-NEXT: [[TMP10]] = add nuw nsw i32 [[TMP9]], 1
; CHECK-NEXT: [[TMP11:%.*]] = icmp eq i32 [[TMP10]], [[TMP1]]
; CHECK-NEXT: br i1 [[TMP11]], label [[TMP7]], label [[TMP8]]
;
%3 = icmp ne i32 %1, 0
br i1 %3, label %4, label %7
4: ; preds = %2
%5 = zext i32 %1 to i64
%6 = getelementptr inbounds i32, ptr %0, i64 %5
br label %8
7: ; preds = %8, %2
ret void
8: ; preds = %8, %4
%9 = phi i32 [ 1, %4 ], [ %10, %8 ]
tail call void @sink(ptr %6)
%10 = add nuw nsw i32 %9, 1
%11 = icmp eq i32 %10, %1
br i1 %11, label %7, label %8
}
declare ptr @strrchr(ptr %0, i32 %1) nofree nounwind readonly willreturn
; We should not mark the return of @strrchr as `nonnull`, it may well be NULL!
define ptr @mybasename(ptr nofree readonly %str) {
; CHECK: Function Attrs: nofree nounwind willreturn memory(read)
; CHECK-LABEL: define {{[^@]+}}@mybasename
; CHECK-SAME: (ptr nofree readonly [[STR:%.*]]) #[[ATTR12:[0-9]+]] {
; CHECK-NEXT: [[CALL:%.*]] = call ptr @strrchr(ptr nofree readonly [[STR]], i32 noundef 47) #[[ATTR13]]
; CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[CALL]], null
; CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[CALL]], i64 1
; CHECK-NEXT: [[COND:%.*]] = select i1 [[TOBOOL]], ptr [[ADD_PTR]], ptr [[STR]]
; CHECK-NEXT: ret ptr [[COND]]
;
%call = call ptr @strrchr(ptr %str, i32 47)
%tobool = icmp ne ptr %call, null
%add.ptr = getelementptr inbounds i8, ptr %call, i64 1
%cond = select i1 %tobool, ptr %add.ptr, ptr %str
ret ptr %cond
}
define void @nonnull_assume_pos(ptr %arg) {
; ATTRIBUTOR-LABEL: define {{[^@]+}}@nonnull_assume_pos
; ATTRIBUTOR-SAME: (ptr nocapture nofree nonnull readnone [[ARG:%.*]])
; ATTRIBUTOR-NEXT: call void @llvm.assume(i1 true) #11 [ "nonnull"(ptr [[ARG]]) ]
; ATTRIBUTOR-NEXT: call void @use_i8_ptr(ptr noalias nocapture nofree nonnull readnone [[ARG]])
; ATTRIBUTOR-NEXT: [[TMP1:%.*]] = call ptr @unknown()
; ATTRIBUTOR-NEXT: ret void
;
; TUNIT-LABEL: define {{[^@]+}}@nonnull_assume_pos
; TUNIT-SAME: (ptr nocapture nofree nonnull readnone [[ARG:%.*]]) {
; TUNIT-NEXT: call void @llvm.assume(i1 noundef true) #[[ATTR13]] [ "nonnull"(ptr [[ARG]]) ]
; TUNIT-NEXT: call void @use_i8_ptr(ptr noalias nocapture nofree nonnull readnone [[ARG]]) #[[ATTR5]]
; TUNIT-NEXT: [[TMP1:%.*]] = call ptr @unknown()
; TUNIT-NEXT: ret void
;
; CGSCC-LABEL: define {{[^@]+}}@nonnull_assume_pos
; CGSCC-SAME: (ptr nocapture nofree nonnull readnone [[ARG:%.*]]) {
; CGSCC-NEXT: call void @llvm.assume(i1 noundef true) #[[ATTR13]] [ "nonnull"(ptr [[ARG]]) ]
; CGSCC-NEXT: call void @use_i8_ptr(ptr noalias nocapture nofree nonnull readnone [[ARG]]) #[[ATTR4]]
; CGSCC-NEXT: [[TMP1:%.*]] = call ptr @unknown()
; CGSCC-NEXT: ret void
;
call void @llvm.assume(i1 true) ["nonnull"(ptr %arg)]
call void @use_i8_ptr(ptr %arg)
call ptr @unknown()
ret void
}
define void @nonnull_assume_neg(ptr %arg) {
; ATTRIBUTOR-LABEL: define {{[^@]+}}@nonnull_assume_neg
; ATTRIBUTOR-SAME: (ptr nocapture nofree readnone [[ARG:%.*]])
; ATTRIBUTOR-NEXT: [[TMP1:%.*]] = call ptr @unknown()
; ATTRIBUTOR-NEXT: call void @use_i8_ptr(ptr noalias nocapture nofree readnone [[ARG]])
; ATTRIBUTOR-NEXT: call void @llvm.assume(i1 true) [ "nonnull"(ptr [[ARG]]) ]
; ATTRIBUTOR-NEXT: call void @use_i8_ptr(ptr noalias nocapture nofree nonnull readnone [[ARG]])
; ATTRIBUTOR-NEXT: [[TMP2:%.*]] = call ptr @unknown()
; ATTRIBUTOR-NEXT: call void @use_i8_ptr_ret(ptr noalias nocapture nofree nonnull readnone [[ARG]])
; ATTRIBUTOR-NEXT: call void @llvm.assume(i1 true) [ "nonnull"(ptr [[ARG]]) ]
; ATTRIBUTOR-NEXT: call void @use_i8_ptr_ret(ptr noalias nocapture nofree nonnull readnone [[ARG]])
; ATTRIBUTOR-NEXT: ret void
;
;
; TUNIT-LABEL: define {{[^@]+}}@nonnull_assume_neg
; TUNIT-SAME: (ptr nocapture nofree readnone [[ARG:%.*]]) {
; TUNIT-NEXT: [[TMP1:%.*]] = call ptr @unknown()
; TUNIT-NEXT: call void @use_i8_ptr(ptr noalias nocapture nofree readnone [[ARG]]) #[[ATTR5]]
; TUNIT-NEXT: call void @llvm.assume(i1 noundef true) [ "nonnull"(ptr [[ARG]]) ]
; TUNIT-NEXT: call void @use_i8_ptr(ptr noalias nocapture nofree nonnull readnone [[ARG]]) #[[ATTR5]]
; TUNIT-NEXT: [[TMP2:%.*]] = call ptr @unknown()
; TUNIT-NEXT: call void @use_i8_ptr_ret(ptr noalias nocapture nofree nonnull readnone [[ARG]]) #[[ATTR5]]
; TUNIT-NEXT: call void @llvm.assume(i1 noundef true) [ "nonnull"(ptr [[ARG]]) ]
; TUNIT-NEXT: call void @use_i8_ptr_ret(ptr noalias nocapture nofree nonnull readnone [[ARG]]) #[[ATTR5]]
; TUNIT-NEXT: ret void
;
; CGSCC-LABEL: define {{[^@]+}}@nonnull_assume_neg
; CGSCC-SAME: (ptr nocapture nofree readnone [[ARG:%.*]]) {
; CGSCC-NEXT: [[TMP1:%.*]] = call ptr @unknown()
; CGSCC-NEXT: call void @use_i8_ptr(ptr noalias nocapture nofree readnone [[ARG]]) #[[ATTR4]]
; CGSCC-NEXT: call void @llvm.assume(i1 noundef true) [ "nonnull"(ptr [[ARG]]) ]
; CGSCC-NEXT: call void @use_i8_ptr(ptr noalias nocapture nofree nonnull readnone [[ARG]]) #[[ATTR4]]
; CGSCC-NEXT: [[TMP2:%.*]] = call ptr @unknown()
; CGSCC-NEXT: call void @use_i8_ptr_ret(ptr noalias nocapture nofree nonnull readnone [[ARG]]) #[[ATTR4]]
; CGSCC-NEXT: call void @llvm.assume(i1 noundef true) [ "nonnull"(ptr [[ARG]]) ]
; CGSCC-NEXT: call void @use_i8_ptr_ret(ptr noalias nocapture nofree nonnull readnone [[ARG]]) #[[ATTR4]]
; CGSCC-NEXT: ret void
;
call ptr @unknown()
call void @use_i8_ptr(ptr %arg)
call void @llvm.assume(i1 true) ["nonnull"(ptr %arg)]
call void @use_i8_ptr(ptr %arg)
call ptr @unknown()
call void @use_i8_ptr_ret(ptr %arg)
call void @llvm.assume(i1 true) ["nonnull"(ptr %arg)]
call void @use_i8_ptr_ret(ptr %arg)
ret void
}
declare void @use_i8_ptr(ptr nofree nocapture readnone) nounwind
declare void @use_i8_ptr_ret(ptr nofree nocapture readnone) nounwind willreturn
define ptr @nonnull_function_ptr_1() {
; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none)
; CHECK-LABEL: define {{[^@]+}}@nonnull_function_ptr_1
; CHECK-SAME: () #[[ATTR1]] {
; CHECK-NEXT: ret ptr @nonnull_function_ptr_1
;
ret ptr @nonnull_function_ptr_1
}
declare ptr @function_decl()
define ptr @nonnull_function_ptr_2() {
; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none)
; CHECK-LABEL: define {{[^@]+}}@nonnull_function_ptr_2
; CHECK-SAME: () #[[ATTR1]] {
; CHECK-NEXT: ret ptr @function_decl
;
ret ptr @function_decl
}
; FIXME: nonnull should not be propagated to the caller's p unless there is noundef
define void @nonnull_caller(ptr %p) {
; CHECK-LABEL: define {{[^@]+}}@nonnull_caller
; CHECK-SAME: (ptr nonnull [[P:%.*]]) {
; CHECK-NEXT: call void @nonnull_callee(ptr nonnull [[P]])
; CHECK-NEXT: ret void
;
call void @nonnull_callee(ptr %p)
ret void
}
declare void @nonnull_callee(ptr nonnull %p)
attributes #0 = { null_pointer_is_valid }
attributes #1 = { nounwind willreturn}
;.
; TUNIT: attributes #[[ATTR0:[0-9]+]] = { nocallback nofree nosync nounwind willreturn memory(inaccessiblemem: readwrite) }
; TUNIT: attributes #[[ATTR1]] = { nofree norecurse nosync nounwind willreturn memory(none) }
; TUNIT: attributes #[[ATTR2]] = { nofree norecurse nosync nounwind willreturn memory(inaccessiblemem: readwrite) }
; TUNIT: attributes #[[ATTR3]] = { nofree nosync nounwind willreturn memory(none) }
; TUNIT: attributes #[[ATTR4]] = { noreturn }
; TUNIT: attributes #[[ATTR5]] = { nounwind }
; TUNIT: attributes #[[ATTR6]] = { nofree nosync nounwind memory(argmem: read) }
; TUNIT: attributes #[[ATTR7]] = { nounwind willreturn }
; TUNIT: attributes #[[ATTR8:[0-9]+]] = { nounwind willreturn memory(read) }
; TUNIT: attributes #[[ATTR9]] = { nofree norecurse nosync nounwind null_pointer_is_valid willreturn memory(none) }
; TUNIT: attributes #[[ATTR10]] = { naked }
; TUNIT: attributes #[[ATTR11]] = { noinline optnone }
; TUNIT: attributes #[[ATTR12]] = { nofree nounwind willreturn memory(read) }
; TUNIT: attributes #[[ATTR13]] = { willreturn }
; TUNIT: attributes #[[ATTR14]] = { nofree nosync nounwind }
;.
; CGSCC: attributes #[[ATTR0:[0-9]+]] = { nocallback nofree nosync nounwind willreturn memory(inaccessiblemem: readwrite) }
; CGSCC: attributes #[[ATTR1]] = { nofree norecurse nosync nounwind willreturn memory(none) }
; CGSCC: attributes #[[ATTR2]] = { nofree norecurse nosync nounwind willreturn memory(inaccessiblemem: readwrite) }
; CGSCC: attributes #[[ATTR3]] = { noreturn }
; CGSCC: attributes #[[ATTR4]] = { nounwind }
; CGSCC: attributes #[[ATTR5]] = { nofree nosync nounwind memory(argmem: read) }
; CGSCC: attributes #[[ATTR6]] = { nounwind willreturn }
; CGSCC: attributes #[[ATTR7:[0-9]+]] = { nounwind willreturn memory(read) }
; CGSCC: attributes #[[ATTR8]] = { nofree norecurse nosync nounwind null_pointer_is_valid willreturn memory(none) }
; CGSCC: attributes #[[ATTR9]] = { nofree nosync nounwind willreturn memory(none) }
; CGSCC: attributes #[[ATTR10]] = { naked }
; CGSCC: attributes #[[ATTR11]] = { noinline optnone }
; CGSCC: attributes #[[ATTR12]] = { nofree nounwind willreturn memory(read) }
; CGSCC: attributes #[[ATTR13]] = { willreturn }
; CGSCC: attributes #[[ATTR14]] = { nofree nosync nounwind }
;.