Changeset View
Changeset View
Standalone View
Standalone View
llvm/test/Transforms/FunctionAttrs/nonnull.ll
; RUN: opt -S -functionattrs -enable-nonnull-arg-prop %s | FileCheck %s | ; RUN: opt -S -functionattrs -enable-nonnull-arg-prop %s | FileCheck %s | ||||
; RUN: opt -S -passes=function-attrs -enable-nonnull-arg-prop %s | FileCheck %s | ; RUN: opt -S -passes=function-attrs -enable-nonnull-arg-prop %s | FileCheck %s | ||||
; RUN: opt -attributor --attributor-disable=false -S < %s | FileCheck %s --check-prefix=ATTRIBUTOR | |||||
jdoerfert: We need a target layout here. The test is now dependent on the sizes of pointer (I think) and… | |||||
declare nonnull i8* @ret_nonnull() | declare nonnull i8* @ret_nonnull() | ||||
; Return a pointer trivially nonnull (call return attribute) | ; Return a pointer trivially nonnull (call return attribute) | ||||
define i8* @test1() { | define i8* @test1() { | ||||
; CHECK: define nonnull i8* @test1 | ; CHECK: define nonnull i8* @test1 | ||||
; ATTRIBUTOR: define nonnull i8* @test1 | |||||
%ret = call i8* @ret_nonnull() | %ret = call i8* @ret_nonnull() | ||||
ret i8* %ret | ret i8* %ret | ||||
} | } | ||||
; Return a pointer trivially nonnull (argument attribute) | ; Return a pointer trivially nonnull (argument attribute) | ||||
define i8* @test2(i8* nonnull %p) { | define i8* @test2(i8* nonnull %p) { | ||||
; CHECK: define nonnull i8* @test2 | ; CHECK: define nonnull i8* @test2 | ||||
; ATTRIBUTOR: define nonnull i8* @test2 | |||||
ret i8* %p | ret i8* %p | ||||
} | } | ||||
; Given an SCC where one of the functions can not be marked nonnull, | ; Given an SCC where one of the functions can not be marked nonnull, | ||||
; can we still mark the other one which is trivially nonnull | ; can we still mark the other one which is trivially nonnull | ||||
define i8* @scc_binder() { | define i8* @scc_binder() { | ||||
; CHECK: define i8* @scc_binder | ; CHECK: define i8* @scc_binder | ||||
; ATTRIBUTOR: define i8* @scc_binder | |||||
call i8* @test3() | call i8* @test3() | ||||
ret i8* null | ret i8* null | ||||
} | } | ||||
define i8* @test3() { | define i8* @test3() { | ||||
; CHECK: define nonnull i8* @test3 | ; CHECK: define nonnull i8* @test3 | ||||
; ATTRIBUTOR: define nonnull i8* @test3 | |||||
call i8* @scc_binder() | call i8* @scc_binder() | ||||
%ret = call i8* @ret_nonnull() | %ret = call i8* @ret_nonnull() | ||||
ret i8* %ret | ret i8* %ret | ||||
} | } | ||||
; Given a mutual recursive set of functions, we can mark them | ; Given a mutual recursive set of functions, we can mark them | ||||
; nonnull if neither can ever return null. (In this case, they | ; nonnull if neither can ever return null. (In this case, they | ||||
; just never return period.) | ; just never return period.) | ||||
define i8* @test4_helper() { | define i8* @test4_helper() { | ||||
; CHECK: define noalias nonnull i8* @test4_helper | ; CHECK: define noalias nonnull i8* @test4_helper | ||||
; ATTRIBUTOR: define nonnull i8* @test4_helper | |||||
%ret = call i8* @test4() | %ret = call i8* @test4() | ||||
ret i8* %ret | ret i8* %ret | ||||
} | } | ||||
define i8* @test4() { | define i8* @test4() { | ||||
; CHECK: define noalias nonnull i8* @test4 | ; CHECK: define noalias nonnull i8* @test4 | ||||
; ATTRIBUTOR: define nonnull i8* @test4 | |||||
%ret = call i8* @test4_helper() | %ret = call i8* @test4_helper() | ||||
ret i8* %ret | ret i8* %ret | ||||
} | } | ||||
; Given a mutual recursive set of functions which *can* return null | ; Given a mutual recursive set of functions which *can* return null | ||||
; make sure we haven't marked them as nonnull. | ; make sure we haven't marked them as nonnull. | ||||
define i8* @test5_helper() { | define i8* @test5_helper() { | ||||
; CHECK: define noalias i8* @test5_helper | ; CHECK: define noalias i8* @test5_helper | ||||
; ATTRIBUTOR: define i8* @test5_helper | |||||
%ret = call i8* @test5() | %ret = call i8* @test5() | ||||
ret i8* null | ret i8* null | ||||
} | } | ||||
define i8* @test5() { | define i8* @test5() { | ||||
; CHECK: define noalias i8* @test5 | ; CHECK: define noalias i8* @test5 | ||||
; ATTRIBUTOR: define i8* @test5 | |||||
%ret = call i8* @test5_helper() | %ret = call i8* @test5_helper() | ||||
ret i8* %ret | ret i8* %ret | ||||
} | } | ||||
; Local analysis, but going through a self recursive phi | ; Local analysis, but going through a self recursive phi | ||||
define i8* @test6() { | define i8* @test6() { | ||||
entry: | entry: | ||||
; CHECK: define nonnull i8* @test6 | ; CHECK: define nonnull i8* @test6 | ||||
; ATTRIBUTOR: define nonnull i8* @test6 | |||||
%ret = call i8* @ret_nonnull() | %ret = call i8* @ret_nonnull() | ||||
br label %loop | br label %loop | ||||
loop: | loop: | ||||
%phi = phi i8* [%ret, %entry], [%phi, %loop] | %phi = phi i8* [%ret, %entry], [%phi, %loop] | ||||
br i1 undef, label %loop, label %exit | br i1 undef, label %loop, label %exit | ||||
exit: | exit: | ||||
ret i8* %phi | ret i8* %phi | ||||
} | } | ||||
; CHECK: define i8* @test7 | |||||
; ATTRIBUTOR: define i8* @test7 | |||||
define i8* @test7(i8* %a) { | |||||
%b = getelementptr inbounds i8, i8* %a, i64 0 | |||||
ret i8* %b | |||||
} | |||||
; CHECK: define nonnull i8* @test8 | |||||
; ATTRIBUTOR: define nonnull i8* @test8 | |||||
define i8* @test8(i8* %a) { | |||||
%b = getelementptr inbounds i8, i8* %a, i64 1 | |||||
ret i8* %b | |||||
} | |||||
; CHECK: define i8* @test9 | |||||
; ATTRIBUTOR: define i8* @test9 | |||||
define i8* @test9(i8* %a, i64 %n) { | |||||
%b = getelementptr inbounds i8, i8* %a, i64 %n | |||||
ret i8* %b | |||||
} | |||||
declare void @llvm.assume(i1) | |||||
One more with inbounds (below, untested!) and maybe some without inbounds while we are at it. ; CHECK: define i8* @test10 ; FIXME: missing nonnull ; ATTRIBUTOR: define i8* @test10 define i8* @test10(i8* %a, i64 %n) { %cmp = icmp ne i64 %n, null call void @llvm.assume(i1 %cmp) %b = getelementptr inbounds i8, i8* %a, i64 %n ret i8* %b } jdoerfert: One more with inbounds (below, untested!) and maybe some without inbounds while we are at it. | |||||
; CHECK: define i8* @test10 | |||||
; FIXME: missing nonnull | |||||
; ATTRIBUTOR: define i8* @test10 | |||||
define i8* @test10(i8* %a, i64 %n) { | |||||
%cmp = icmp ne i64 %n, 0 | |||||
call void @llvm.assume(i1 %cmp) | |||||
%b = getelementptr inbounds i8, i8* %a, i64 %n | |||||
ret i8* %b | |||||
} | |||||
; TEST 11 | |||||
; char* test11(char *p) { | |||||
; return p? p: nonnull(); | |||||
; } | |||||
; CHECK: define i8* @test11 | |||||
; FIXME: missing nonnull | |||||
; ATTRIBUTOR: define i8* @test11 | |||||
define i8* @test11(i8*) local_unnamed_addr { | |||||
%2 = icmp eq i8* %0, null | |||||
br i1 %2, label %3, label %5 | |||||
; <label>:3: ; preds = %1 | |||||
%4 = tail call i8* @nonnull(i64 1) | |||||
br label %5 | |||||
; <label>:5: ; preds = %3, %1 | |||||
%6 = phi i8* [ %4, %3 ], [ %0, %1 ] | |||||
ret i8* %6 | |||||
} | |||||
declare nonnull i8* @nonnull(i64) | |||||
; Test propagation of nonnull callsite args back to caller. | ; Test propagation of nonnull callsite args back to caller. | ||||
declare void @use1(i8* %x) | declare void @use1(i8* %x) | ||||
declare void @use2(i8* %x, i8* %y); | declare void @use2(i8* %x, i8* %y); | ||||
declare void @use3(i8* %x, i8* %y, i8* %z); | declare void @use3(i8* %x, i8* %y, i8* %z); | ||||
declare void @use1nonnull(i8* nonnull %x); | declare void @use1nonnull(i8* nonnull %x); | ||||
declare void @use2nonnull(i8* nonnull %x, i8* nonnull %y); | declare void @use2nonnull(i8* nonnull %x, i8* nonnull %y); | ||||
declare void @use3nonnull(i8* nonnull %x, i8* nonnull %y, i8* nonnull %z); | declare void @use3nonnull(i8* nonnull %x, i8* nonnull %y, i8* nonnull %z); | ||||
declare i8 @use1safecall(i8* %x) readonly nounwind ; readonly+nounwind guarantees that execution continues to successor | declare i8 @use1safecall(i8* %x) readonly nounwind ; readonly+nounwind guarantees that execution continues to successor | ||||
Not Done ReplyInline ActionsWe derive nonnull here because test13 is dead, correct? Maybe that is not what you wanted to test ;) jdoerfert: We derive `nonnull` here because `test13` is dead, correct? Maybe that is not what you wanted… | |||||
; Can't extend non-null to parent for any argument because the 2nd call is not guaranteed to execute. | ; Can't extend non-null to parent for any argument because the 2nd call is not guaranteed to execute. | ||||
define void @parent1(i8* %a, i8* %b, i8* %c) { | define void @parent1(i8* %a, i8* %b, i8* %c) { | ||||
; CHECK-LABEL: @parent1(i8* %a, i8* %b, i8* %c) | ; CHECK-LABEL: @parent1(i8* %a, i8* %b, i8* %c) | ||||
; CHECK-NEXT: call void @use3(i8* %c, i8* %a, i8* %b) | ; CHECK-NEXT: call void @use3(i8* %c, i8* %a, i8* %b) | ||||
; CHECK-NEXT: call void @use3nonnull(i8* %b, i8* %c, i8* %a) | ; CHECK-NEXT: call void @use3nonnull(i8* %b, i8* %c, i8* %a) | ||||
; CHECK-NEXT: ret void | ; CHECK-NEXT: ret void | ||||
; | ; | ||||
call void @use3(i8* %c, i8* %a, i8* %b) | call void @use3(i8* %c, i8* %a, i8* %b) | ||||
call void @use3nonnull(i8* %b, i8* %c, i8* %a) | call void @use3nonnull(i8* %b, i8* %c, i8* %a) | ||||
ret void | ret void | ||||
} | } | ||||
; Extend non-null to parent for all arguments. | ; Extend non-null to parent for all arguments. | ||||
define void @parent2(i8* %a, i8* %b, i8* %c) { | define void @parent2(i8* %a, i8* %b, i8* %c) { | ||||
; CHECK-LABEL: @parent2(i8* nonnull %a, i8* nonnull %b, i8* nonnull %c) | ; CHECK-LABEL: @parent2(i8* nonnull %a, i8* nonnull %b, i8* nonnull %c) | ||||
; CHECK-NEXT: call void @use3nonnull(i8* %b, i8* %c, i8* %a) | ; CHECK-NEXT: call void @use3nonnull(i8* %b, i8* %c, i8* %a) | ||||
; CHECK-NEXT: call void @use3(i8* %c, i8* %a, i8* %b) | ; CHECK-NEXT: call void @use3(i8* %c, i8* %a, i8* %b) | ||||
; CHECK-NEXT: ret void | ; CHECK-NEXT: ret void | ||||
; | ; | ||||
call void @use3nonnull(i8* %b, i8* %c, i8* %a) | call void @use3nonnull(i8* %b, i8* %c, i8* %a) | ||||
call void @use3(i8* %c, i8* %a, i8* %b) | call void @use3(i8* %c, i8* %a, i8* %b) | ||||
Not Done ReplyInline ActionsWill be fixed with D64708. jdoerfert: Will be fixed with D64708. | |||||
ret void | ret void | ||||
} | } | ||||
; Extend non-null to parent for 1st argument. | ; Extend non-null to parent for 1st argument. | ||||
define void @parent3(i8* %a, i8* %b, i8* %c) { | define void @parent3(i8* %a, i8* %b, i8* %c) { | ||||
; CHECK-LABEL: @parent3(i8* nonnull %a, i8* %b, i8* %c) | ; CHECK-LABEL: @parent3(i8* nonnull %a, i8* %b, i8* %c) | ||||
; CHECK-NEXT: call void @use1nonnull(i8* %a) | ; CHECK-NEXT: call void @use1nonnull(i8* %a) | ||||
▲ Show 20 Lines • Show All 91 Lines • ▼ Show 20 Lines | |||||
cont: | cont: | ||||
%null_check = icmp eq i8* %b, null | %null_check = icmp eq i8* %b, null | ||||
ret i1 %null_check | ret i1 %null_check | ||||
exc: | exc: | ||||
%lp = landingpad { i8*, i32 } | %lp = landingpad { i8*, i32 } | ||||
filter [0 x i8*] zeroinitializer | filter [0 x i8*] zeroinitializer | ||||
unreachable | unreachable | ||||
These are a lot of copies. Could we somehow make the "default" check and only specialize/copy when the two (-funcattr and -attributor) generate different code? Or is this already what you did and I just missed it? Also, could you add FIXMEs where we fail to deduce something with the attributor? jdoerfert: These are a lot of copies. Could we somehow make the "default" check and only specialize/copy… | |||||
} | } | ||||
; CHECK: define nonnull i32* @gep1( | ; CHECK: define nonnull i32* @gep1( | ||||
define i32* @gep1(i32* %p) { | define i32* @gep1(i32* %p) { | ||||
%q = getelementptr inbounds i32, i32* %p, i32 1 | %q = getelementptr inbounds i32, i32* %p, i32 1 | ||||
ret i32* %q | ret i32* %q | ||||
} | } | ||||
Show All 14 Lines |
We need a target layout here. The test is now dependent on the sizes of pointer (I think) and without target layout it might take the host values which would be bad for 32bit machines.