When we propagate call site arguments we always need to translate them, this is important as we ended up picking the function argument for a recurisve call not the call site argument. `@recBad` and `@recGood` in `returned.ll` show the problem as they used to transform them the same way. The restructuring cleans the code up and helps derive more "returned" arguments and better information in the presence of recursive calls. The "dropped" attributes are simply dropped because we do not query them anymore, not because we cannot derive them.
141 lines
5.7 KiB
LLVM
141 lines
5.7 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-annotate-decl-cs -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"
|
|
target triple = "x86_64-unknown-linux-gnu"
|
|
|
|
define void @fn2(ptr %P, i1 %C) {
|
|
;
|
|
; TUNIT: Function Attrs: nofree norecurse nosync nounwind memory(argmem: readwrite)
|
|
; TUNIT-LABEL: define {{[^@]+}}@fn2
|
|
; TUNIT-SAME: (ptr nocapture nofree [[P:%.*]], i1 [[C:%.*]]) #[[ATTR0:[0-9]+]] {
|
|
; TUNIT-NEXT: entry:
|
|
; TUNIT-NEXT: br label [[IF_END:%.*]]
|
|
; TUNIT: for.cond1:
|
|
; TUNIT-NEXT: br i1 [[C]], label [[IF_END]], label [[EXIT:%.*]]
|
|
; TUNIT: if.end:
|
|
; TUNIT-NEXT: [[E_2:%.*]] = phi ptr [ [[P]], [[ENTRY:%.*]] ], [ null, [[FOR_COND1:%.*]] ]
|
|
; TUNIT-NEXT: [[TMP0:%.*]] = load i32, ptr [[E_2]], align 4
|
|
; TUNIT-NEXT: store i32 [[TMP0]], ptr [[P]], align 4
|
|
; TUNIT-NEXT: br label [[FOR_COND1]]
|
|
; TUNIT: exit:
|
|
; TUNIT-NEXT: ret void
|
|
;
|
|
; CGSCC: Function Attrs: nofree nosync nounwind memory(argmem: readwrite)
|
|
; CGSCC-LABEL: define {{[^@]+}}@fn2
|
|
; CGSCC-SAME: (ptr nocapture nofree nonnull align 4 dereferenceable(4) [[P:%.*]], i1 [[C:%.*]]) #[[ATTR0:[0-9]+]] {
|
|
; CGSCC-NEXT: entry:
|
|
; CGSCC-NEXT: br label [[IF_END:%.*]]
|
|
; CGSCC: for.cond1:
|
|
; CGSCC-NEXT: br i1 [[C]], label [[IF_END]], label [[EXIT:%.*]]
|
|
; CGSCC: if.end:
|
|
; CGSCC-NEXT: [[E_2:%.*]] = phi ptr [ [[P]], [[ENTRY:%.*]] ], [ null, [[FOR_COND1:%.*]] ]
|
|
; CGSCC-NEXT: [[TMP0:%.*]] = load i32, ptr [[E_2]], align 4
|
|
; CGSCC-NEXT: [[CALL:%.*]] = call i32 @fn1(i32 [[TMP0]]) #[[ATTR3:[0-9]+]]
|
|
; CGSCC-NEXT: store i32 [[CALL]], ptr [[P]], align 4
|
|
; CGSCC-NEXT: br label [[FOR_COND1]]
|
|
; CGSCC: exit:
|
|
; CGSCC-NEXT: ret void
|
|
;
|
|
entry:
|
|
br label %if.end
|
|
|
|
for.cond1: ; preds = %if.end
|
|
br i1 %C, label %if.end, label %exit
|
|
|
|
if.end: ; preds = %entry, %for.cond1
|
|
%e.2 = phi ptr [ %P, %entry ], [ null, %for.cond1 ]
|
|
%0 = load i32, ptr %e.2, align 4
|
|
%call = call i32 @fn1(i32 %0)
|
|
store i32 %call, ptr %P
|
|
br label %for.cond1
|
|
exit:
|
|
ret void
|
|
}
|
|
|
|
define internal i32 @fn1(i32 %p1) {
|
|
; CGSCC: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
|
|
; CGSCC-LABEL: define {{[^@]+}}@fn1
|
|
; CGSCC-SAME: (i32 returned [[P1:%.*]]) #[[ATTR1:[0-9]+]] {
|
|
; CGSCC-NEXT: entry:
|
|
; CGSCC-NEXT: ret i32 [[P1]]
|
|
;
|
|
entry:
|
|
%tobool = icmp ne i32 %p1, 0
|
|
%cond = select i1 %tobool, i32 %p1, i32 %p1
|
|
ret i32 %cond
|
|
}
|
|
|
|
define void @fn_no_null_opt(ptr %P, i1 %C) null_pointer_is_valid {
|
|
;
|
|
; TUNIT: Function Attrs: nofree norecurse nosync nounwind null_pointer_is_valid
|
|
; TUNIT-LABEL: define {{[^@]+}}@fn_no_null_opt
|
|
; TUNIT-SAME: (ptr nocapture nofree writeonly [[P:%.*]], i1 [[C:%.*]]) #[[ATTR1:[0-9]+]] {
|
|
; TUNIT-NEXT: entry:
|
|
; TUNIT-NEXT: br label [[IF_END:%.*]]
|
|
; TUNIT: for.cond1:
|
|
; TUNIT-NEXT: br i1 [[C]], label [[IF_END]], label [[EXIT:%.*]]
|
|
; TUNIT: if.end:
|
|
; TUNIT-NEXT: [[E_2:%.*]] = phi ptr [ undef, [[ENTRY:%.*]] ], [ null, [[FOR_COND1:%.*]] ]
|
|
; TUNIT-NEXT: [[TMP0:%.*]] = load i32, ptr null, align 4294967296
|
|
; TUNIT-NEXT: store i32 [[TMP0]], ptr [[P]], align 4
|
|
; TUNIT-NEXT: br label [[FOR_COND1]]
|
|
; TUNIT: exit:
|
|
; TUNIT-NEXT: ret void
|
|
;
|
|
; CGSCC: Function Attrs: nofree nosync nounwind null_pointer_is_valid
|
|
; CGSCC-LABEL: define {{[^@]+}}@fn_no_null_opt
|
|
; CGSCC-SAME: (ptr nocapture nofree writeonly align 4 dereferenceable_or_null(4) [[P:%.*]], i1 [[C:%.*]]) #[[ATTR2:[0-9]+]] {
|
|
; CGSCC-NEXT: entry:
|
|
; CGSCC-NEXT: br label [[IF_END:%.*]]
|
|
; CGSCC: for.cond1:
|
|
; CGSCC-NEXT: br i1 [[C]], label [[IF_END]], label [[EXIT:%.*]]
|
|
; CGSCC: if.end:
|
|
; CGSCC-NEXT: [[E_2:%.*]] = phi ptr [ undef, [[ENTRY:%.*]] ], [ null, [[FOR_COND1:%.*]] ]
|
|
; CGSCC-NEXT: [[TMP0:%.*]] = load i32, ptr null, align 4294967296
|
|
; CGSCC-NEXT: [[CALL:%.*]] = call i32 @fn0(i32 [[TMP0]]) #[[ATTR3]]
|
|
; CGSCC-NEXT: store i32 [[CALL]], ptr [[P]], align 4
|
|
; CGSCC-NEXT: br label [[FOR_COND1]]
|
|
; CGSCC: exit:
|
|
; CGSCC-NEXT: ret void
|
|
;
|
|
entry:
|
|
br label %if.end
|
|
|
|
for.cond1: ; preds = %if.end
|
|
br i1 %C, label %if.end, label %exit
|
|
|
|
if.end: ; preds = %entry, %for.cond1
|
|
%e.2 = phi ptr [ undef, %entry ], [ null, %for.cond1 ]
|
|
%0 = load i32, ptr %e.2, align 4
|
|
%call = call i32 @fn0(i32 %0)
|
|
store i32 %call, ptr %P
|
|
br label %for.cond1
|
|
exit:
|
|
ret void
|
|
}
|
|
|
|
define internal i32 @fn0(i32 %p1) {
|
|
; CGSCC: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
|
|
; CGSCC-LABEL: define {{[^@]+}}@fn0
|
|
; CGSCC-SAME: (i32 returned [[P1:%.*]]) #[[ATTR1]] {
|
|
; CGSCC-NEXT: entry:
|
|
; CGSCC-NEXT: ret i32 [[P1]]
|
|
;
|
|
entry:
|
|
%tobool = icmp ne i32 %p1, 0
|
|
%cond = select i1 %tobool, i32 %p1, i32 %p1
|
|
ret i32 %cond
|
|
}
|
|
;.
|
|
; TUNIT: attributes #[[ATTR0]] = { nofree norecurse nosync nounwind memory(argmem: readwrite) }
|
|
; TUNIT: attributes #[[ATTR1]] = { nofree norecurse nosync nounwind null_pointer_is_valid }
|
|
;.
|
|
; CGSCC: attributes #[[ATTR0]] = { nofree nosync nounwind memory(argmem: readwrite) }
|
|
; CGSCC: attributes #[[ATTR1]] = { mustprogress nofree norecurse nosync nounwind willreturn memory(none) }
|
|
; CGSCC: attributes #[[ATTR2]] = { nofree nosync nounwind null_pointer_is_valid }
|
|
; CGSCC: attributes #[[ATTR3]] = { nofree nosync }
|
|
;.
|
|
;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
|
|
; CHECK: {{.*}}
|