@@ -1,22 +1,31 @@
; RUN: opt -functionattrs -attributor -attributor-disable=false -S < %s | FileCheck %s
; RUN: opt -functionattrs -attributor -attributor-disable=false -attributor-verify=true -S < %s | FileCheck %s
; RUN: opt -functionattrs -S < %s | FileCheck %s --check-prefix=FNATTR
; RUN: opt -attributor -attributor-disable=false -S < %s | FileCheck %s --check-prefix=ATTRIBUTOR
; RUN: opt -attributor -attributor-disable=false -functionattrs -S < %s | FileCheck %s --check-prefix=BOTH
;
; Test cases specifically designed for the "returned" argument attribute.
; We use FIXME's to indicate problems and missing attributes.
;
; TEST SCC test returning an integer value argument
;
; CHECK: Function Attrs: noinline norecurse nounwind readnone uwtable
; CHECK: define i32 @sink_r0(i32 returned %r)
;
; FIXME: returned on %r missing:
; CHECK: Function Attrs: noinline nounwind readnone uwtable
; CHECK: define i32 @scc_r1(i32 %a, i32 %r, i32 %b)
;
; FIXME: returned on %r missing:
; CHECK: Function Attrs: noinline nounwind readnone uwtable
; CHECK: define i32 @scc_r2(i32 %a, i32 %b, i32 %r)
; BOTH: Function Attrs: noinline norecurse nounwind readnone uwtable
; BOTH-NEXT: define i32 @sink_r0(i32 returned %r)
; BOTH: Function Attrs: noinline nounwind readnone uwtable
; BOTH-NEXT: define i32 @scc_r1(i32 %a, i32 returned %r, i32 %b)
; BOTH: Function Attrs: noinline nounwind readnone uwtable
; BOTH-NEXT: define i32 @scc_r2(i32 %a, i32 %b, i32 returned %r)
; BOTH: Function Attrs: noinline nounwind readnone uwtable
; BOTH-NEXT: define i32 @scc_rX(i32 %a, i32 %b, i32 %r)
;
; FNATTR: define i32 @sink_r0(i32 returned %r)
; FNATTR: define i32 @scc_r1(i32 %a, i32 %r, i32 %b)
; FNATTR: define i32 @scc_r2(i32 %a, i32 %b, i32 %r)
; FNATTR: define i32 @scc_rX(i32 %a, i32 %b, i32 %r)
;
; ATTRIBUTOR: define i32 @sink_r0(i32 returned %r)
; ATTRIBUTOR: define i32 @scc_r1(i32 %a, i32 returned %r, i32 %b)
; ATTRIBUTOR: define i32 @scc_r2(i32 %a, i32 %b, i32 returned %r)
; ATTRIBUTOR: define i32 @scc_rX(i32 %a, i32 %b, i32 %r)
;
; int scc_r1(int a, int b, int r);
; int scc_r2(int a, int b, int r);
Expand Down
Expand Up
@@ -150,16 +159,20 @@ return: ; preds = %cond.end, %if.then3
; TEST SCC test returning a pointer value argument
;
; CHECK: Function Attrs: noinline norecurse nounwind readnone uwtable
; CHECK: define double* @ptr_sink_r0(double* readnone returned %r)
; BOTH: Function Attrs: noinline norecurse nounwind readnone uwtable
; BOTH-NEXT: define double* @ptr_sink_r0(double* readnone returned %r)
; BOTH: Function Attrs: noinline nounwind readnone uwtable
; BOTH-NEXT: define double* @ptr_scc_r1(double* %a, double* readnone returned %r, double* nocapture readnone %b)
; BOTH: Function Attrs: noinline nounwind readnone uwtable
; BOTH-NEXT: define double* @ptr_scc_r2(double* readnone %a, double* readnone %b, double* readnone returned %r)
;
; FIXME: returned on %r missing:
; CHECK: Function Attrs: noinline nounwind readnone uwtable
; CHECK : define double* @ptr_scc_r1 (double* %a, double* readnone %r , double* nocapture readnone %b )
; FNATTR: define double* @ptr_sink_r0(double* readnone returned %r)
; FNATTR: define double* @ptr_scc_r1(double* %a, double* readnone %r, double* nocapture readnone %b)
; FNATTR : define double* @ptr_scc_r2 (double* readnone %a, double* readnone %b , double* readnone %r )
;
; FIXME: returned on %r missing:
; CHECK: Function Attrs: noinline nounwind readnone uwtable
; CHECK : define double* @ptr_scc_r2(double* readnone %a, double* readnone %b, double* readnone %r)
; ATTRIBUTOR: define double* @ptr_sink_r0(double* returned %r)
; ATTRIBUTOR: define double* @ptr_scc_r1(double* %a, double* returned %r, double* %b)
; ATTRIBUTOR : define double* @ptr_scc_r2(double* %a, double* %b, double* returned %r)
;
; double* ptr_scc_r1(double* a, double* b, double* r);
; double* ptr_scc_r2(double* a, double* b, double* r);
Expand Down
Expand Up
@@ -237,41 +250,95 @@ return: ; preds = %cond.end, %if.then3
}
; TEST a singleton SCC with a lot of recursive calls
; TEST a no-return singleton SCC
;
; int* ret0 (int *a) {
; return *a ? a : ret0(ret0(ret0(...ret0(a)...)) );
; int* rt0 (int *a) {
; return *a ? a : rt0(a );
; }
;
; FIXME: returned on %a missing:
; CHECK: Function Attrs: noinline nounwind readonly uwtable
; CHECK: define i32* @ret0(i32* readonly %a)
define i32* @ret0 (i32* %a ) #0 {
; FIXME: no-return missing
; FNATTR: define i32* @rt0(i32* readonly %a)
; BOTH: Function Attrs: noinline nounwind readonly uwtable
; BOTH-NEXT: define i32* @rt0(i32* readonly returned %a)
define i32* @rt0 (i32* %a ) #0 {
entry:
%v = load i32 , i32* %a , align 4
%tobool = icmp ne i32 %v , 0
%call = call i32* @ret0 (i32* %a )
%call1 = call i32* @ret0 (i32* %call )
%call2 = call i32* @ret0 (i32* %call1 )
%call3 = call i32* @ret0 (i32* %call2 )
%call4 = call i32* @ret0 (i32* %call3 )
%call5 = call i32* @ret0 (i32* %call4 )
%call6 = call i32* @ret0 (i32* %call5 )
%call7 = call i32* @ret0 (i32* %call6 )
%call8 = call i32* @ret0 (i32* %call7 )
%call9 = call i32* @ret0 (i32* %call8 )
%call10 = call i32* @ret0 (i32* %call9 )
%call11 = call i32* @ret0 (i32* %call10 )
%call12 = call i32* @ret0 (i32* %call11 )
%call13 = call i32* @ret0 (i32* %call12 )
%call14 = call i32* @ret0 (i32* %call13 )
%call15 = call i32* @ret0 (i32* %call14 )
%call16 = call i32* @ret0 (i32* %call15 )
%call17 = call i32* @ret0 (i32* %call16 )
%sel = select i1 %tobool , i32* %a , i32* %call17
%call = call i32* @rt0 (i32* %a )
%sel = select i1 %tobool , i32* %a , i32* %call
ret i32* %sel
}
; TEST a no-return singleton SCC
;
; int* rt1(int *a) {
; return *a ? undef : rt1(a);
; }
;
; FIXME: no-return missing
; FNATTR: define noalias i32* @rt1(i32* nocapture readonly %a)
; BOTH: Function Attrs: noinline nounwind readonly uwtable
; BOTH-NEXT: define noalias i32* @rt1(i32* nocapture readonly %a)
define i32* @rt1 (i32* %a ) #0 {
entry:
%v = load i32 , i32* %a , align 4
%tobool = icmp ne i32 %v , 0
%call = call i32* @rt1 (i32* %a )
%sel = select i1 %tobool , i32* undef , i32* %call
ret i32* %sel
}
; TEST another SCC test
;
; FNATTR: define i32* @rt2_helper(i32* %a)
; FNATTR: define i32* @rt2(i32* readnone %a, i32* readnone %b)
; BOTH: define i32* @rt2_helper(i32* %a)
; BOTH: define i32* @rt2(i32* readnone %a, i32* readnone %b)
define i32* @rt2_helper (i32* %a ) #0 {
entry:
%call = call i32* @rt2 (i32* %a , i32* %a )
ret i32* %call
}
define i32* @rt2 (i32* %a , i32 *%b ) #0 {
entry:
%cmp = icmp eq i32* %a , null
br i1 %cmp , label %if.then , label %if.end
if.then:
%call = call i32* @rt2_helper (i32* %a )
br label %if.end
if.end:
%sel = phi i32* [ %b , %entry ], [%call , %if.then ]
ret i32* %sel
}
; TEST another SCC test
;
; FNATTR: define i32* @rt3_helper(i32* %a, i32* %b)
; FNATTR: define i32* @rt3(i32* readnone %a, i32* readnone %b)
; BOTH: define i32* @rt3_helper(i32* %a, i32* returned %b)
; BOTH: define i32* @rt3(i32* readnone %a, i32* readnone returned %b)
define i32* @rt3_helper (i32* %a , i32* %b ) #0 {
entry:
%call = call i32* @rt3 (i32* %a , i32* %b )
ret i32* %call
}
define i32* @rt3 (i32* %a , i32 *%b ) #0 {
entry:
%cmp = icmp eq i32* %a , null
br i1 %cmp , label %if.then , label %if.end
if.then:
%call = call i32* @rt3_helper (i32* %a , i32* %b )
br label %if.end
if.end:
%sel = phi i32* [ %b , %entry ], [%call , %if.then ]
ret i32* %sel
}
; TEST address taken function with call to an external functions
;
Expand All
@@ -282,11 +349,12 @@ entry:
; return r;
; }
;
; CHECK: Function Attrs: noinline nounwind uwtable
; CHECK: declare void @unknown_fn(i32* (i32*)*)
; BOTH: declare void @unknown_fn(i32* (i32*)*)
;
; CHECK: Function Attrs: noinline nounwind uwtable
; CHECK: define i32* @calls_unknown_fn(i32* readnone returned %r)
; BOTH: Function Attrs: noinline nounwind uwtable
; BOTH-NEXT: define i32* @calls_unknown_fn(i32* readnone returned %r)
; FNATTR: define i32* @calls_unknown_fn(i32* readnone returned %r)
; ATTRIBUTOR: define i32* @calls_unknown_fn(i32* returned %r)
declare void @unknown_fn (i32* (i32* )*) #0
define i32* @calls_unknown_fn (i32* %r ) #0 {
Expand All
@@ -313,6 +381,12 @@ define i32* @calls_unknown_fn(i32* %r) #0 {
;
; CHECK: Function Attrs: noinline nounwind uwtable
; CHECK: define i32* @calls_maybe_redefined_fn(i32* returned %r)
;
; BOTH: Function Attrs: noinline nounwind uwtable
; BOTH-NEXT: define linkonce_odr i32* @maybe_redefined_fn(i32* %r)
;
; BOTH: Function Attrs: noinline nounwind uwtable
; BOTH-NEXT: define i32* @calls_maybe_redefined_fn(i32* returned %r)
define linkonce_odr i32* @maybe_redefined_fn (i32* %r ) #0 {
entry:
ret i32* %r
Expand All
@@ -324,6 +398,36 @@ entry:
ret i32* %r
}
; TEST return call to a function that might be redifined at link time
;
; int *maybe_redefined_fn2(int *r) {
; return r;
; }
;
; int *calls_maybe_redefined_fn2(int *r) {
; return maybe_redefined_fn2(r);
; }
;
; Verify the maybe-redefined function is not annotated:
;
; BOTH: Function Attrs: noinline nounwind uwtable
; BOTH-NEXT: define linkonce_odr i32* @maybe_redefined_fn2(i32* %r)
; BOTH: Function Attrs: noinline nounwind uwtable
; BOTH-NEXT: define i32* @calls_maybe_redefined_fn2(i32* %r)
;
; FNATTR: define i32* @calls_maybe_redefined_fn2(i32* %r)
; ATTRIBUTOR: define i32* @calls_maybe_redefined_fn2(i32* %r)
define linkonce_odr i32* @maybe_redefined_fn2 (i32* %r ) #0 {
entry:
ret i32* %r
}
define i32* @calls_maybe_redefined_fn2 (i32* %r ) #0 {
entry:
%call = call i32* @maybe_redefined_fn2 (i32* %r )
ret i32* %call
}
; TEST returned argument goes through select and phi
;
Expand All
@@ -334,9 +438,11 @@ entry:
; return b == 0? b : x;
; }
;
; FIXME: returned on %b missing:
; CHECK: Function Attrs: noinline norecurse nounwind readnone uwtable
; CHECK: define double @select_and_phi(double %b)
; BOTH: Function Attrs: noinline norecurse nounwind readnone uwtable
; BOTH-NEXT: define double @select_and_phi(double returned %b)
;
; FNATTR: define double @select_and_phi(double %b)
; ATTRIBUTOR: define double @select_and_phi(double returned %b)
define double @select_and_phi (double %b ) #0 {
entry:
%cmp = fcmp ogt double %b , 0 .000000e+00
Expand All
@@ -362,9 +468,11 @@ if.end: ; preds = %if.then, %entry
; return b == 0? b : x;
; }
;
; FIXME: returned on %b missing:
; CHECK: Function Attrs: noinline nounwind readnone uwtable
; CHECK: define double @recursion_select_and_phi(i32 %a, double %b)
; BOTH: Function Attrs: noinline nounwind readnone uwtable
; BOTH-NEXT: define double @recursion_select_and_phi(i32 %a, double returned %b)
;
; FNATTR: define double @recursion_select_and_phi(i32 %a, double %b)
; ATTRIBUTOR: define double @recursion_select_and_phi(i32 %a, double returned %b)
define double @recursion_select_and_phi (i32 %a , double %b ) #0 {
entry:
%dec = add nsw i32 %a , -1
Expand All
@@ -389,9 +497,11 @@ if.end: ; preds = %if.then, %entry
; return (double*)b;
; }
;
; FIXME: returned on %b missing:
; CHECK: Function Attrs: noinline norecurse nounwind readnone uwtable
; CHECK: define double* @bitcast(i32* readnone %b)
; BOTH: Function Attrs: noinline norecurse nounwind readnone uwtable
; BOTH-NEXT: define double* @bitcast(i32* readnone returned %b)
;
; FNATTR: define double* @bitcast(i32* readnone %b)
; ATTRIBUTOR: define double* @bitcast(i32* returned %b)
define double * @bitcast (i32* %b ) #0 {
entry:
%bc0 = bitcast i32* %b to double *
Expand All
@@ -408,9 +518,11 @@ entry:
; return b != 0 ? b : x;
; }
;
; FIXME: returned on %b missing:
; CHECK: Function Attrs: noinline norecurse nounwind readnone uwtable
; CHECK: define double* @bitcasts_select_and_phi(i32* readnone %b)
; BOTH: Function Attrs: noinline norecurse nounwind readnone uwtable
; BOTH-NEXT: define double* @bitcasts_select_and_phi(i32* readnone returned %b)
;
; FNATTR: define double* @bitcasts_select_and_phi(i32* readnone %b)
; ATTRIBUTOR: define double* @bitcasts_select_and_phi(i32* returned %b)
define double * @bitcasts_select_and_phi (i32* %b ) #0 {
entry:
%bc0 = bitcast i32* %b to double *
Expand Down
Expand Up
@@ -442,8 +554,11 @@ if.end: ; preds = %if.then, %entry
; /* return undef */
; }
;
; CHECK: Function Attrs: noinline norecurse nounwind readnone uwtable
; CHECK: define double* @ret_arg_arg_undef(i32* readnone %b)
; BOTH: Function Attrs: noinline norecurse nounwind readnone uwtable
; BOTH-NEXT: define double* @ret_arg_arg_undef(i32* readnone returned %b)
;
; FNATTR: define double* @ret_arg_arg_undef(i32* readnone %b)
; ATTRIBUTOR: define double* @ret_arg_arg_undef(i32* returned %b)
define double * @ret_arg_arg_undef (i32* %b ) #0 {
entry:
%bc0 = bitcast i32* %b to double *
Expand Down
Expand Up
@@ -475,8 +590,11 @@ ret_undef:
; /* return undef */
; }
;
; CHECK: Function Attrs: noinline norecurse nounwind readnone uwtable
; CHECK: define double* @ret_undef_arg_arg(i32* readnone %b)
; BOTH: Function Attrs: noinline norecurse nounwind readnone uwtable
; BOTH-NEXT: define double* @ret_undef_arg_arg(i32* readnone returned %b)
;
; FNATTR: define double* @ret_undef_arg_arg(i32* readnone %b)
; ATTRIBUTOR: define double* @ret_undef_arg_arg(i32* returned %b)
define double * @ret_undef_arg_arg (i32* %b ) #0 {
entry:
%bc0 = bitcast i32* %b to double *
Expand Down
Expand Up
@@ -508,8 +626,11 @@ ret_arg1:
; /* return undef */
; }
;
; CHECK: Function Attrs: noinline norecurse nounwind readnone uwtable
; CHECK: define double* @ret_undef_arg_undef(i32* readnone %b)
; BOTH: Function Attrs: noinline norecurse nounwind readnone uwtable
; BOTH-NEXT: define double* @ret_undef_arg_undef(i32* readnone returned %b)
;
; FNATTR: define double* @ret_undef_arg_undef(i32* readnone %b)
; ATTRIBUTOR: define double* @ret_undef_arg_undef(i32* returned %b)
define double * @ret_undef_arg_undef (i32* %b ) #0 {
entry:
%bc0 = bitcast i32* %b to double *
Expand All
@@ -534,13 +655,17 @@ ret_undef1:
; int* ret_arg_or_unknown(int* b) {
; if (b == 0)
; return b;
; return unknown(b );
; return unknown();
; }
;
; Verify we do not assume b is returned>
; Verify we do not assume b is returned
;
; CHECK: define i32* @ret_arg_or_unknown(i32* %b)
; CHECK: define i32* @ret_arg_or_unknown_through_phi(i32* %b)
; FNATTR: define i32* @ret_arg_or_unknown(i32* %b)
; FNATTR: define i32* @ret_arg_or_unknown_through_phi(i32* %b)
; ATTRIBUTOR: define i32* @ret_arg_or_unknown(i32* %b)
; ATTRIBUTOR: define i32* @ret_arg_or_unknown_through_phi(i32* %b)
; BOTH: define i32* @ret_arg_or_unknown(i32* %b)
; BOTH: define i32* @ret_arg_or_unknown_through_phi(i32* %b)
declare i32* @unknown (i32* )
define i32* @ret_arg_or_unknown (i32* %b ) #0 {
Expand Down
Expand Up
@@ -573,11 +698,40 @@ r:
ret i32* %phi
}
; TEST inconsistent IR in dead code.
;
; FNATTR: define i32 @deadblockcall1(i32 %A)
; FNATTR: define i32 @deadblockcall2(i32 %A)
; ATTRIBUTOR: define i32 @deadblockcall1(i32 returned %A)
; ATTRIBUTOR: define i32 @deadblockcall2(i32 returned %A)
; BOTH: define i32 @deadblockcall1(i32 returned %A)
; BOTH: define i32 @deadblockcall2(i32 returned %A)
define i32 @deadblockcall1 (i32 %A ) #0 {
entry:
ret i32 %A
unreachableblock:
%B = call i32 @deadblockcall1 (i32 %B )
ret i32 %B
}
declare i32 @deadblockcall_helper (i32 returned %A );
define i32 @deadblockcall2 (i32 %A ) #0 {
entry:
ret i32 %A
unreachableblock1:
%B = call i32 @deadblockcall_helper (i32 %B )
ret i32 %B
unreachableblock2:
%C = call i32 @deadblockcall1 (i32 %C )
ret i32 %C
}
attributes #0 = { noinline nounwind uwtable }
; CHECK -NOT: attributes #
; CHECK -DAG: attributes #{{[0-9]*}} = { noinline norecurse nounwind readnone uwtable }
; CHECK -DAG: attributes #{{[0-9]*}} = { noinline nounwind readnone uwtable }
; CHECK -DAG: attributes #{{[0-9]*}} = { noinline nounwind readonly uwtable }
; CHECK -DAG: attributes #{{[0-9]*}} = { noinline nounwind uwtable }
; CHECK -NOT: attributes #
; BOTH -NOT: attributes #
; BOTH -DAG: attributes #{{[0-9]*}} = { noinline norecurse nounwind readnone uwtable }
; BOTH -DAG: attributes #{{[0-9]*}} = { noinline nounwind readnone uwtable }
; BOTH -DAG: attributes #{{[0-9]*}} = { noinline nounwind readonly uwtable }
; BOTH -DAG: attributes #{{[0-9]*}} = { noinline nounwind uwtable }
; BOTH -NOT: attributes #