jdoerfert updated this revision to Diff 192738.
jdoerfert added a comment.
Fix the last bug exposed by llvm-test-suite & SPEC2006
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D59919/new/
https://reviews.llvm.org/D59919
Files:
clang/test/CodeGenOpenCL/as_type.cl
llvm/include/llvm/Transforms/IPO/Attributor.h
llvm/lib/Transforms/IPO/Attributor.cpp
llvm/test/Transforms/FunctionAttrs/SCC1.ll
llvm/test/Transforms/FunctionAttrs/arg_nocapture.ll
llvm/test/Transforms/FunctionAttrs/arg_returned.ll
Index: llvm/test/Transforms/FunctionAttrs/arg_returned.ll
===================================================================
--- llvm/test/Transforms/FunctionAttrs/arg_returned.ll
+++ llvm/test/Transforms/FunctionAttrs/arg_returned.ll
@@ -1,28 +1,43 @@
-; RUN: opt -functionattrs -attributor -S < %s | FileCheck %s
+; RUN: opt -functionattrs -S < %s | FileCheck %s --check-prefix=FNATTR
+; RUN: opt -attributor -S < %s | FileCheck %s --check-prefix=ATTRIBUTOR
+; RUN: opt -attributor -functionattrs -S < %s | FileCheck %s --check-prefix=BOTH
+; RUN: opt -attributor -attributor-max-iterations=18 -S < %s | FileCheck %s --check-prefix=FEW_IT
+; RUN: opt -attributor -attributor-max-iterations=19 -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 1: SCC test returning an integer value argument
-; TEST 2: the same SCC as in 1 returning a pointer value argument
-; TEST 3: a singleton SCC with a lot of recursive calls
-; TEST 4: address taken function with call to an external functions
-; TEST 5: call to a function that might be redifined at link time
-; TEST 6: returned argument goes through select and phi
-; TEST 7: returned argument goes through recursion, select, and phi
-; TEST 8: returned argument goes through bitcasts
-; TEST 9: returned argument goes through select and phi interleaved with bitcasts
+; TEST 1: SCC test returning an integer value argument
+; TEST 2: the same SCC as in 1 returning a pointer value argument
+; TEST 3: a singleton SCC with a lot of recursive calls
+; TEST 4: address taken function with call to an external functions
+; TEST 5: call to a function that might be redifined at link time
+; TEST 6: returned argument goes through select and phi
+; TEST 7: returned argument goes through recursion, select, and phi
+; TEST 8: returned argument goes through bitcasts
+; TEST 9: returned argument goes through select and phi interleaved with bitcasts
+; TEST 10: return argument or argument or undef
+; TEST 11: return undef or argument or argument
+; TEST 12: return undef or argument or undef
+; TEST 13: return argument or unknown call result
; TEST 1
;
-; CHECK: define dso_local i32 @sink_r0(i32 returned %r) [[NoInlineNoRecurseNoUnwindReadnoneUwtable:#[0-9]]]
+; BOTH: define dso_local i32 @sink_r0(i32 returned %r) [[NoInlineNoRecurseNoUnwindReadnoneUwtable:#[0-9]]]
+; BOTH: define dso_local i32 @scc_r1(i32 %a, i32 returned %r, i32 %b) [[NoInlineNoUnwindReadnoneUwtable:#[0-9]]]
+; BOTH: define dso_local i32 @scc_r2(i32 %a, i32 %b, i32 returned %r) [[NoInlineNoUnwindReadnoneUwtable]]
+; BOTH: define dso_local i32 @scc_rX(i32 %a, i32 %b, i32 %r) [[NoInlineNoUnwindReadnoneUwtable]]
;
-; FIXME: returned on %r missing:
-; CHECK: define dso_local i32 @scc_r1(i32 %a, i32 %r, i32 %b) [[NoInlineNoUnwindReadnoneUwtable:#[0-9]]]
+; FNATTR: define dso_local i32 @sink_r0(i32 returned %r) [[NoInlineNoRecurseNoUnwindReadnoneUwtable:#[0-9]]]
+; FNATTR: define dso_local i32 @scc_r1(i32 %a, i32 %r, i32 %b) [[NoInlineNoUnwindReadnoneUwtable:#[0-9]]]
+; FNATTR: define dso_local i32 @scc_r2(i32 %a, i32 %b, i32 %r) [[NoInlineNoUnwindReadnoneUwtable]]
+; FNATTR: define dso_local i32 @scc_rX(i32 %a, i32 %b, i32 %r) [[NoInlineNoUnwindReadnoneUwtable]]
;
-; FIXME: returned on %r missing:
-; CHECK: define dso_local i32 @scc_r2(i32 %a, i32 %b, i32 %r) [[NoInlineNoUnwindReadnoneUwtable]]
+; ATTRIBUTOR: define dso_local i32 @sink_r0(i32 returned %r) [[NoInlineNoRecurseNoUnwindReadnoneUwtable:#[0-9]]]
+; ATTRIBUTOR: define dso_local i32 @scc_r1(i32 %a, i32 returned %r, i32 %b) [[NoInlineNoUnwindReadnoneUwtable:#[0-9]]]
+; ATTRIBUTOR: define dso_local i32 @scc_r2(i32 %a, i32 %b, i32 returned %r) [[NoInlineNoUnwindReadnoneUwtable]]
+; ATTRIBUTOR: define dso_local i32 @scc_rX(i32 %a, i32 %b, i32 %r) [[NoInlineNoUnwindReadnoneUwtable]]
;
; int scc_r1(int a, int b, int r);
; int scc_r2(int a, int b, int r);
@@ -157,13 +172,17 @@
; TEST 2
;
-; CHECK: define dso_local double* @ptr_sink_r0(double* readnone returned %r) [[NoInlineNoRecurseNoUnwindReadnoneUwtable]]
+; BOTH: define dso_local double* @ptr_sink_r0(double* readnone returned %r) [[NoInlineNoRecurseNoUnwindReadnoneUwtable]]
+; BOTH: define dso_local double* @ptr_scc_r1(double* %a, double* readnone returned %r, double* nocapture readnone %b) [[NoInlineNoUnwindReadnoneUwtable]]
+; BOTH: define dso_local double* @ptr_scc_r2(double* readnone %a, double* readnone %b, double* readnone returned %r) [[NoInlineNoUnwindReadnoneUwtable]]
;
-; FIXME: returned on %r missing:
-; CHECK: define dso_local double* @ptr_scc_r1(double* %a, double* readnone %r, double* nocapture readnone %b) [[NoInlineNoUnwindReadnoneUwtable]]
+; FNATTR: define dso_local double* @ptr_sink_r0(double* readnone returned %r) [[NoInlineNoRecurseNoUnwindReadnoneUwtable]]
+; FNATTR: define dso_local double* @ptr_scc_r1(double* %a, double* readnone %r, double* nocapture readnone %b) [[NoInlineNoUnwindReadnoneUwtable]]
+; FNATTR: define dso_local double* @ptr_scc_r2(double* readnone %a, double* readnone %b, double* readnone %r) [[NoInlineNoUnwindReadnoneUwtable]]
;
-; FIXME: returned on %r missing:
-; CHECK: define dso_local double* @ptr_scc_r2(double* readnone %a, double* readnone %b, double* readnone %r) [[NoInlineNoUnwindReadnoneUwtable]]
+; ATTRIBUTOR: define dso_local double* @ptr_sink_r0(double* returned %r) [[NoInlineNoRecurseNoUnwindReadnoneUwtable]]
+; ATTRIBUTOR: define dso_local double* @ptr_scc_r1(double* %a, double* returned %r, double* %b) [[NoInlineNoUnwindReadnoneUwtable]]
+; ATTRIBUTOR: define dso_local double* @ptr_scc_r2(double* %a, double* %b, double* returned %r) [[NoInlineNoUnwindReadnoneUwtable]]
;
; double* ptr_scc_r1(double* a, double* b, double* r);
; double* ptr_scc_r2(double* a, double* b, double* r);
@@ -247,8 +266,9 @@
; return *a ? a : ret0(ret0(ret0(...ret0(a)...)));
; }
;
-; FIXME: returned on %a missing:
-; CHECK: define dso_local i32* @ret0(i32* readonly %a) [[NoInlineNoUnwindReadonlyUwtable:#[0-9]*]]
+; FEW_IT: define dso_local i32* @ret0(i32* %a)
+; FNATTR: define dso_local i32* @ret0(i32* readonly %a) [[NoInlineNoUnwindReadonlyUwtable:#[0-9]*]]
+; BOTH: define dso_local i32* @ret0(i32* readonly returned %a) [[NoInlineNoUnwindReadonlyUwtable:#[0-9]*]]
define dso_local i32* @ret0(i32* %a) #0 {
entry:
%v = load i32, i32* %a, align 4
@@ -285,9 +305,11 @@
; return r;
; }
;
-; CHECK: declare void @unknown_fn(i32* (i32*)*) [[NoInlineNoUnwindUwtable:#[0-9]*]]
+; BOTH: declare void @unknown_fn(i32* (i32*)*) [[NoInlineNoUnwindUwtable:#[0-9]*]]
;
-; CHECK: define dso_local i32* @calls_unknown_fn(i32* readnone returned %r) [[NoInlineNoUnwindUwtable]]
+; BOTH: define dso_local i32* @calls_unknown_fn(i32* readnone returned %r) [[NoInlineNoUnwindUwtable]]
+; FNATTR: define dso_local i32* @calls_unknown_fn(i32* readnone returned %r) [[NoInlineNoUnwindUwtable:#[0-9]*]]
+; ATTRIBUTOR: define dso_local i32* @calls_unknown_fn(i32* returned %r) [[NoInlineNoUnwindUwtable:#[0-9]*]]
;
declare void @unknown_fn(i32* (i32*)*) #0
@@ -309,11 +331,12 @@
; }
;
; Verify the maybe-redefined function is not annotated:
-; CHECK: define linkonce_odr i32* @maybe_redefined_fn(i32* %r) [[NoInlineNoRecurseNoUnwindUwtable:#[0-9]*]]
+; BOTH: define linkonce_odr i32* @maybe_redefined_fn(i32* %r) [[NoInlineNoRecurseNoUnwindUwtable:#[0-9]*]]
; FIXME: We should not derive norecurse for potentially redefined functions!
; define linkonce_odr i32* @maybe_redefined_fn(i32* %r) [[NoInlineNoUnwindUwtable]]
;
-; CHECK: define dso_local i32* @calls_maybe_redefined_fn(i32* returned %r) [[NoInlineNoRecurseNoUnwindUwtable]]
+; FNATTR: define dso_local i32* @calls_maybe_redefined_fn(i32* returned %r) [[NoInlineNoRecurseNoUnwindUwtable:#[0-9]*]]
+; ATTRIBUTOR: define dso_local i32* @calls_maybe_redefined_fn(i32* returned %r) [[NoInlineNoUnwindUwtable]]
; FIXME: We should not derive norecurse for potentially redefined functions!
; define dso_local i32* @calls_maybe_redefined_fn(i32* returned %r) [[NoInlineNoUnwindUwtable]]
;
@@ -338,8 +361,8 @@
; return b == 0? b : x;
; }
;
-; FIXME: returned on %b missing:
-; CHECK: define dso_local double @select_and_phi(double %b) [[NoInlineNoRecurseNoUnwindReadnoneUwtable]]
+; FNATTR: define dso_local double @select_and_phi(double %b) [[NoInlineNoRecurseNoUnwindReadnoneUwtable]]
+; ATTRIBUTOR: define dso_local double @select_and_phi(double returned %b) [[NoInlineNoUnwindUwtable]]
;
define dso_local double @select_and_phi(double %b) #0 {
entry:
@@ -366,8 +389,8 @@
; return b == 0? b : x;
; }
;
-; FIXME: returned on %b missing:
-; CHECK: define dso_local double @recursion_select_and_phi(i32 %a, double %b) [[NoInlineNoUnwindReadnoneUwtable]]
+; FNATTR: define dso_local double @recursion_select_and_phi(i32 %a, double %b) [[NoInlineNoUnwindReadnoneUwtable]]
+; ATTRIBUTOR: define dso_local double @recursion_select_and_phi(i32 %a, double returned %b) [[NoInlineNoUnwindUwtable]]
;
define dso_local double @recursion_select_and_phi(i32 %a, double %b) #0 {
entry:
@@ -393,8 +416,9 @@
; return (double*)b;
; }
;
-; FIXME: returned on %b missing:
-; CHECK: define dso_local double* @bitcast(i32* readnone %b) [[NoInlineNoRecurseNoUnwindReadnoneUwtable]]
+; FNATTR: define dso_local double* @bitcast(i32* readnone %b) [[NoInlineNoRecurseNoUnwindReadnoneUwtable]]
+; ATTRIBUTOR: define dso_local double* @bitcast(i32* returned %b) [[NoInlineNoUnwindUwtable]]
+; BOTH: define dso_local double* @bitcast(i32* readnone returned %b) [[NoInlineNoRecurseNoUnwindReadnoneUwtable]]
;
define dso_local double* @bitcast(i32* %b) #0 {
entry:
@@ -412,8 +436,9 @@
; return b != 0 ? b : x;
; }
;
-; FIXME: returned on %b missing:
-; CHECK: define dso_local double* @bitcasts_select_and_phi(i32* readnone %b) [[NoInlineNoRecurseNoUnwindReadnoneUwtable]]
+; FNATTR: define dso_local double* @bitcasts_select_and_phi(i32* readnone %b) [[NoInlineNoRecurseNoUnwindReadnoneUwtable]]
+; ATTRIBUTOR: define dso_local double* @bitcasts_select_and_phi(i32* returned %b) [[NoInlineNoUnwindUwtable]]
+; BOTH: define dso_local double* @bitcasts_select_and_phi(i32* readnone returned %b) [[NoInlineNoRecurseNoUnwindReadnoneUwtable]]
;
define dso_local double* @bitcasts_select_and_phi(i32* %b) #0 {
entry:
@@ -436,10 +461,162 @@
}
+; TEST 10
+;
+; double* ret_arg_arg_undef(int* b) {
+; if (b == 0)
+; return (double*)b;
+; if (b == 0)
+; return (double*)b;
+; /* return undef */
+; }
+;
+; FNATTR: define dso_local double* @ret_arg_arg_undef(i32* readnone %b) [[NoInlineNoRecurseNoUnwindReadnoneUwtable]]
+; ATTRIBUTOR: define dso_local double* @ret_arg_arg_undef(i32* returned %b) [[NoInlineNoUnwindUwtable]]
+; BOTH: define dso_local double* @ret_arg_arg_undef(i32* readnone returned %b) [[NoInlineNoRecurseNoUnwindReadnoneUwtable]]
+;
+define dso_local double* @ret_arg_arg_undef(i32* %b) #0 {
+entry:
+ %bc0 = bitcast i32* %b to double*
+ %cmp = icmp eq double* %bc0, null
+ br i1 %cmp, label %ret_arg0, label %if.end
+
+ret_arg0:
+ %bc1 = bitcast i32* %b to double*
+ ret double* %bc1
+
+if.end:
+ br i1 %cmp, label %ret_arg1, label %ret_undef
+
+ret_arg1:
+ ret double* %bc0
+
+ret_undef:
+ ret double *undef
+}
+
+
+; TEST 11
+;
+; double* ret_undef_arg_arg(int* b) {
+; if (b == 0)
+; return (double*)b;
+; if (b == 0)
+; return (double*)b;
+; /* return undef */
+; }
+;
+; FNATTR: define dso_local double* @ret_undef_arg_arg(i32* readnone %b) [[NoInlineNoRecurseNoUnwindReadnoneUwtable]]
+; ATTRIBUTOR: define dso_local double* @ret_undef_arg_arg(i32* returned %b) [[NoInlineNoUnwindUwtable]]
+; BOTH: define dso_local double* @ret_undef_arg_arg(i32* readnone returned %b) [[NoInlineNoRecurseNoUnwindReadnoneUwtable]]
+;
+define dso_local double* @ret_undef_arg_arg(i32* %b) #0 {
+entry:
+ %bc0 = bitcast i32* %b to double*
+ %cmp = icmp eq double* %bc0, null
+ br i1 %cmp, label %ret_undef, label %if.end
+
+ret_undef:
+ ret double *undef
+
+if.end:
+ br i1 %cmp, label %ret_arg0, label %ret_arg1
+
+ret_arg0:
+ ret double* %bc0
+
+ret_arg1:
+ %bc1 = bitcast i32* %b to double*
+ ret double* %bc1
+}
+
+
+; TEST 12
+;
+; double* ret_undef_arg_undef(int* b) {
+; if (b == 0)
+; /* return undef */
+; if (b == 0)
+; return (double*)b;
+; /* return undef */
+; }
+;
+; FNATTR: define dso_local double* @ret_undef_arg_undef(i32* readnone %b) [[NoInlineNoRecurseNoUnwindReadnoneUwtable]]
+; ATTRIBUTOR: define dso_local double* @ret_undef_arg_undef(i32* returned %b) [[NoInlineNoUnwindUwtable]]
+; BOTH: define dso_local double* @ret_undef_arg_undef(i32* readnone returned %b) [[NoInlineNoRecurseNoUnwindReadnoneUwtable]]
+;
+define dso_local double* @ret_undef_arg_undef(i32* %b) #0 {
+entry:
+ %bc0 = bitcast i32* %b to double*
+ %cmp = icmp eq double* %bc0, null
+ br i1 %cmp, label %ret_undef0, label %if.end
+
+ret_undef0:
+ ret double *undef
+
+if.end:
+ br i1 %cmp, label %ret_arg, label %ret_undef1
+
+ret_arg:
+ ret double* %bc0
+
+ret_undef1:
+ ret double *undef
+}
+
+; TEST 13
+;
+; int* ret_arg_or_unknown(int* b) {
+; if (b == 0)
+; return b;
+; return unknown();
+; }
+;
+; Verify we do not assume b is returned>
+;
+; FNATTR: define dso_local i32* @ret_arg_or_unknown(i32* %b)
+; FNATTR: define dso_local i32* @ret_arg_or_unknown_through_phi(i32* %b)
+; ATTRIBUTOR: define dso_local i32* @ret_arg_or_unknown(i32* %b)
+; ATTRIBUTOR: define dso_local i32* @ret_arg_or_unknown_through_phi(i32* %b)
+; BOTH: define dso_local i32* @ret_arg_or_unknown(i32* %b)
+; BOTH: define dso_local i32* @ret_arg_or_unknown_through_phi(i32* %b)
+;
+declare dso_local i32* @unknown(i32*)
+
+define dso_local i32* @ret_arg_or_unknown(i32* %b) #0 {
+entry:
+ %cmp = icmp eq i32* %b, null
+ br i1 %cmp, label %ret_arg, label %ret_unknown
+
+ret_arg:
+ ret i32* %b
+
+ret_unknown:
+ %call = call i32* @unknown(i32* %b)
+ ret i32* %call
+}
+
+define dso_local i32* @ret_arg_or_unknown_through_phi(i32* %b) #0 {
+entry:
+ %cmp = icmp eq i32* %b, null
+ br i1 %cmp, label %ret_arg, label %ret_unknown
+
+ret_arg:
+ br label %r
+
+ret_unknown:
+ %call = call i32* @unknown(i32* %b)
+ br label %r
+
+r:
+ %phi = phi i32* [ %b, %ret_arg ], [ %call, %ret_unknown ]
+ ret i32* %phi
+}
+
+
attributes #0 = { noinline nounwind uwtable }
-; CHECK-DAG: attributes [[NoInlineNoRecurseNoUnwindReadnoneUwtable]] = { noinline norecurse nounwind readnone uwtable }
-; CHECK-DAG: attributes [[NoInlineNoUnwindReadnoneUwtable]] = { noinline nounwind readnone uwtable }
-; CHECK-DAG: attributes [[NoInlineNoUnwindReadonlyUwtable]] = { noinline nounwind readonly uwtable }
-; CHECK-DAG: attributes [[NoInlineNoUnwindUwtable]] = { noinline nounwind uwtable }
-; CHECK-DAG: attributes [[NoInlineNoRecurseNoUnwindUwtable]] = { noinline norecurse nounwind uwtable }
+; BOTH-DAG: attributes [[NoInlineNoRecurseNoUnwindReadnoneUwtable]] = { noinline norecurse nounwind readnone uwtable }
+; BOTH-DAG: attributes [[NoInlineNoUnwindReadnoneUwtable]] = { noinline nounwind readnone uwtable }
+; BOTH-DAG: attributes [[NoInlineNoUnwindReadonlyUwtable]] = { noinline nounwind readonly uwtable }
+; BOTH-DAG: attributes [[NoInlineNoRecurseNoUnwindUwtable]] = { noinline norecurse nounwind uwtable }
Index: llvm/test/Transforms/FunctionAttrs/arg_nocapture.ll
===================================================================
--- llvm/test/Transforms/FunctionAttrs/arg_nocapture.ll
+++ llvm/test/Transforms/FunctionAttrs/arg_nocapture.ll
@@ -127,18 +127,15 @@
; TEST 5:
;
-; FIXME: returned missing for %a
; FIXME: no-capture missing for %a
-; CHECK: define dso_local float* @scc_A(i32* readnone %a)
+; CHECK: define dso_local float* @scc_A(i32* readnone returned %a)
;
-; FIXME: returned missing for %a
; FIXME: no-capture missing for %a
-; CHECK: define dso_local i64* @scc_B(double* readnone %a)
+; CHECK: define dso_local i64* @scc_B(double* readnone returned %a)
;
-; FIXME: returned missing for %a
; FIXME: readnone missing for %s
; FIXME: no-capture missing for %a
-; CHECK: define dso_local i8* @scc_C(i16* %a)
+; CHECK: define dso_local i8* @scc_C(i16* returned %a)
;
; float *scc_A(int *a) {
; return (float*)(a ? (int*)scc_A((int*)scc_B((double*)scc_C((short*)a))) : a);
Index: llvm/test/Transforms/FunctionAttrs/SCC1.ll
===================================================================
--- llvm/test/Transforms/FunctionAttrs/SCC1.ll
+++ llvm/test/Transforms/FunctionAttrs/SCC1.ll
@@ -45,7 +45,7 @@
;
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
-; CHECK: define dso_local i32* @external_ret2_nrw(i32* %n0, i32* %r0, i32* %w0) #[[NOUNWIND:[0-9]*]]
+; CHECK: define dso_local i32* @external_ret2_nrw(i32* %n0, i32* %r0, i32* returned %w0) #[[NOUNWIND:[0-9]*]]
define dso_local i32* @external_ret2_nrw(i32* %n0, i32* %r0, i32* %w0) {
entry:
%call = call i32* @internal_ret0_nw(i32* %n0, i32* %w0)
@@ -55,7 +55,7 @@
ret i32* %call3
}
-; CHECK: define internal i32* @internal_ret0_nw(i32* %n0, i32* %w0) #[[NOUNWIND]]
+; CHECK: define internal i32* @internal_ret0_nw(i32* returned %n0, i32* %w0) #[[NOUNWIND]]
define internal i32* @internal_ret0_nw(i32* %n0, i32* %w0) {
entry:
%r0 = alloca i32, align 4
@@ -83,7 +83,7 @@
ret i32* %retval.0
}
-; CHECK: define internal i32* @internal_ret1_rrw(i32* %r0, i32* %r1, i32* %w0) #[[NOUNWIND]]
+; CHECK: define internal i32* @internal_ret1_rrw(i32* %r0, i32* returned %r1, i32* %w0) #[[NOUNWIND]]
define internal i32* @internal_ret1_rrw(i32* %r0, i32* %r1, i32* %w0) {
entry:
%0 = load i32, i32* %r0, align 4
@@ -132,7 +132,7 @@
ret i32* %w0
}
-; CHECK: define internal i32* @internal_ret1_rw(i32* %r0, i32* %w0) #[[NOUNWIND]]
+; CHECK: define internal i32* @internal_ret1_rw(i32* %r0, i32* returned %w0) #[[NOUNWIND]]
define internal i32* @internal_ret1_rw(i32* %r0, i32* %w0) {
entry:
%0 = load i32, i32* %r0, align 4
@@ -157,7 +157,7 @@
ret i32* %retval.0
}
-; CHECK: define dso_local i32* @external_source_ret2_nrw(i32* %n0, i32* %r0, i32* %w0) #[[NOUNWIND]]
+; CHECK: define dso_local i32* @external_source_ret2_nrw(i32* %n0, i32* %r0, i32* returned %w0) #[[NOUNWIND]]
define dso_local i32* @external_source_ret2_nrw(i32* %n0, i32* %r0, i32* %w0) {
entry:
%call = call i32* @external_sink_ret2_nrw(i32* %n0, i32* %r0, i32* %w0)
Index: llvm/lib/Transforms/IPO/Attributor.cpp
===================================================================
--- llvm/lib/Transforms/IPO/Attributor.cpp
+++ llvm/lib/Transforms/IPO/Attributor.cpp
@@ -38,6 +38,11 @@
STATISTIC(NumFnWithoutExactDefinition,
"Number of function without exact definitions");
+STATISTIC(NumFnUniqueReturned, "Number of function with unique return");
+STATISTIC(NumFnKnownReturns, "Number of function with known return values");
+STATISTIC(NumFnArgumentReturned,
+ "Number of function arguments marked returned");
+
// TODO: Determine a good default value.
static cl::opt<unsigned>
MaxFixpointIterations("attributor-max-iterations", cl::Hidden,
@@ -64,11 +69,54 @@
static void bookkeeping(AbstractAttribute::ManifestPosition MP,
const Attribute &Attr) {
switch (Attr.getKindAsEnum()) {
+ case Attribute::Returned:
+ NumFnArgumentReturned++;
+ return;
default:
return;
}
}
+/// Recursively collect returned values, starting from \p V but look through
+/// through select/phi/casts.
+static void collectValuesRecursively(
+ Value *V, SmallPtrSetImpl<ReturnInst *> &ReturnInsts,
+ DenseMap<Value *, SmallPtrSet<ReturnInst *, 2>> &Values,
+ SmallPtrSetImpl<Value *> &Visited) {
+ // TODO: This (looking through casts/phis/selects should be a general
+ // functionality weith a callback.
+ V = V->stripPointerCasts();
+
+ // Explicitly look through calls with a "returned" attribute.
+ CallSite CS(V);
+ if (CS && CS.getCalledFunction())
+ for (Argument &Arg : CS.getCalledFunction()->args())
+ if (Arg.hasReturnedAttr())
+ return collectValuesRecursively(CS.getArgOperand(Arg.getArgNo()),
+ ReturnInsts, Values, Visited);
+
+ if (!Visited.insert(V).second)
+ return;
+
+ // Look through select instructions.
+ if (auto *SI = dyn_cast<SelectInst>(V)) {
+ collectValuesRecursively(SI->getTrueValue(), ReturnInsts, Values, Visited);
+ collectValuesRecursively(SI->getFalseValue(), ReturnInsts, Values, Visited);
+ return;
+ }
+
+ // Look through phi nodes. Recursion is not a problem as we keep the PHI
+ // node in the container as well (see above).
+ if (auto *PHI = dyn_cast<PHINode>(V)) {
+ for (Value *PHIOp : PHI->operands())
+ collectValuesRecursively(PHIOp, ReturnInsts, Values, Visited);
+ return;
+ }
+
+ // Remember that V can be returned by all return instructions in ReturnInsts.
+ Values[V].insert(ReturnInsts.begin(), ReturnInsts.end());
+}
+
ChangeStatus AbstractAttribute::update(Attributor &A) {
ChangeStatus Changed = ChangeStatus::UNCHANGED;
if (getState().isAtFixpoint())
@@ -120,6 +168,287 @@
return Changed;
}
+/// --------------------- Function Return Values -------------------------------
+
+/// "Attribute" that collects all potential returned values and the return
+/// instructions that they arise from.
+///
+/// If there is a unique returned value R, the manifest method will:
+/// - mark R with the "returned" attribute, if R is an argument.
+class AAReturnedValuesImpl final : public AAReturnedValues, AbstractState {
+
+ /// Mapping of values potentially returned by the associated function to the
+ /// return instructions that might return them.
+ DenseMap<Value *, SmallPtrSet<ReturnInst *, 2>> ReturnedValues;
+
+ /// Return an assumed unique return value if a single candidate is found. If
+ /// there cannot be one, return a nullptr. If it is not clear yet, return the
+ /// Optional::NoneType.
+ Optional<Value *> getUniqueReturnValue();
+
+ /// State flags
+ ///
+ ///{
+ bool IsFixed = false;
+ bool IsValidState = true;
+ bool HasOverdefinedReturnedCalls = false;
+ ///}
+
+public:
+ /// See AbstractAttribute::AbstractAttribute(...).
+ AAReturnedValuesImpl(Function &F) : AAReturnedValues(F) {
+ // We do not have an associated argument yet.
+ AssociatedVal = nullptr;
+ }
+
+ /// See AbstractAttribute::initialize(...).
+ void initialize(Attributor &A) override {
+ Function &F = cast<Function>(getAnchoredValue());
+
+ // The map from instruction opcodes to those instructions in the function.
+ auto &OpcodeInstMap = A.getOpcodeInstMapForFunction(F);
+
+ // Look through all arguments, if one is marked as returned we are done.
+ for (Argument &Arg : F.args()) {
+ if (Arg.hasReturnedAttr()) {
+ assert(ReturnedValues.empty());
+
+ auto &ReturnInstSet = ReturnedValues[&Arg];
+ for (Instruction *RI : OpcodeInstMap[Instruction::Ret])
+ ReturnInstSet.insert(cast<ReturnInst>(RI));
+
+ indicateFixpoint(/* Optimistic */ true);
+ return;
+ }
+ }
+
+ // If no argument was marked as returned we look at all return instructions
+ // and collect potentially returned values.
+ for (Instruction *RI : OpcodeInstMap[Instruction::Ret]) {
+ SmallPtrSet<ReturnInst *, 1> RISet({cast<ReturnInst>(RI)});
+ SmallPtrSet<Value *, 8> Visited;
+ collectValuesRecursively(cast<ReturnInst>(RI)->getReturnValue(), RISet,
+ ReturnedValues, Visited);
+ }
+ }
+
+ /// See AbstractAttribute::manifest(...).
+ virtual ChangeStatus manifest(Attributor &A) override;
+
+ /// See AbstractAttribute::getState(...).
+ virtual AbstractState &getState() override { return *this; }
+
+ /// See AbstractAttribute::getState(...).
+ virtual const AbstractState &getState() const override { return *this; }
+
+ /// See AbstractAttribute::getManifestPosition().
+ virtual ManifestPosition getManifestPosition() const override {
+ return MP_ARGUMENT;
+ }
+
+ /// See AbstractAttribute::updateImpl(Attributor &A).
+ virtual ChangeStatus updateImpl(Attributor &A) override;
+
+ /// See AAReturnedValues::getNumReturnValues().
+ virtual size_t getNumReturnValues() const override {
+ return isValidState() ? ReturnedValues.size() : -1;
+ }
+
+ /// Iterators to walk through all possibly returned values.
+ ///{
+ using iterator = decltype(ReturnedValues)::iterator;
+ iterator begin() { return ReturnedValues.begin(); }
+ iterator end() { return ReturnedValues.end(); }
+ ///}
+
+ /// Pretty print the attribute similar to the IR representation.
+ virtual const std::string getAsStr() const override;
+
+ /// See AbstractState::isAtFixpoint().
+ bool isAtFixpoint() const override { return IsFixed; }
+
+ /// See AbstractState::isValidState().
+ bool isValidState() const override { return IsValidState; }
+
+ /// See AbstractState::indicateFixpoint(...).
+ void indicateFixpoint(bool Optimistic) override {
+ IsFixed = true;
+ IsValidState &= Optimistic;
+ }
+};
+
+ChangeStatus AAReturnedValuesImpl::manifest(Attributor &A) {
+ ChangeStatus Changed = ChangeStatus::UNCHANGED;
+
+ // Bookkeeping.
+ assert(isValidState());
+ NumFnKnownReturns++;
+
+ // Check if we have an assumed unique return value that we could manifest.
+ Optional<Value *> UniqueRV = getUniqueReturnValue();
+
+ if (!UniqueRV.hasValue() || !UniqueRV.getValue())
+ return Changed;
+
+ // Bookkeeping.
+ NumFnUniqueReturned++;
+
+ // If the assumed unique return value is an argument, annotate it.
+ if (auto *UniqueRVArg = dyn_cast<Argument>(UniqueRV.getValue())) {
+ AssociatedVal = UniqueRVArg;
+ Changed = AbstractAttribute::manifest(A) | Changed;
+ }
+
+ return Changed;
+}
+
+const std::string AAReturnedValuesImpl::getAsStr() const {
+ return (isAtFixpoint() ? "returns(#" : "may-return(#") +
+ (isValidState() ? std::to_string(getNumReturnValues()) : "?") + ")";
+}
+
+Optional<Value *> AAReturnedValuesImpl::getUniqueReturnValue() {
+ Optional<Value *> UniqueRV;
+
+ // Check all returned values but ignore call sites as long as we have not
+ // encountered an overdefined one during an update. If there is a unique value
+ // always returned, ignoring potential undef values that can also be present,
+ // it is assumed to be the actual return value and forwarded to the caller of
+ // this method. If there are multiple, a nullptr is returned indicating there
+ // cannot be a unique returned value.
+ for (auto &It : ReturnedValues) {
+ Value *RV = It.first;
+
+ ImmutableCallSite ICS(RV);
+ if (ICS && !HasOverdefinedReturnedCalls)
+ continue;
+
+ // If we found a second returned value and neither the current nor the saved
+ // one is an undef, there is no unique returned value. Undefs are special
+ // since we can pretend they have any value.
+ if (UniqueRV.hasValue() && UniqueRV != RV &&
+ !(isa<UndefValue>(RV) || isa<UndefValue>(UniqueRV.getValue())))
+ return Optional<Value *>(nullptr);
+
+ // Do not overwrite a value with an undef.
+ if (UniqueRV.hasValue() && isa<UndefValue>(RV))
+ continue;
+
+ UniqueRV = RV;
+ }
+
+ return UniqueRV;
+}
+
+ChangeStatus AAReturnedValuesImpl::updateImpl(Attributor &A) {
+
+ // Check if we know of any values returned by the associated function,
+ // if not, we are done.
+ if (getNumReturnValues() == 0) {
+ indicateFixpoint(/* Optimistic */ true);
+ return ChangeStatus::UNCHANGED;
+ }
+
+ // Check if any of the returned values is a call site we can refine.
+ SmallPtrSet<Value *, 16> DelRVs;
+ decltype(ReturnedValues) AddRVs;
+ bool HasCallSite = false;
+
+ // Look at all returned call sites.
+ for (auto &It : ReturnedValues) {
+ SmallPtrSet<Value *, 8> Visited;
+ SmallPtrSet<ReturnInst *, 2> &ReturnInsts = It.second;
+ Value *RV = It.first;
+ LLVM_DEBUG(dbgs() << "[AAReturnedValues] Potentially returned value " << *RV
+ << "\n");
+
+ // Only call sites can change during an update, ignore the rest.
+ CallSite RetCS(RV);
+ if (!RetCS)
+ continue;
+
+ // For now, any call site we see will prevent us from directly fixing the
+ // state. However, if the information on the callees is fixed, the call
+ // sites will be removed and we will fix the information for this state.
+ HasCallSite = true;
+
+ // If we do not have information on the
+ auto *RetCSAA = A.getAAFor<AAReturnedValuesImpl>(*this, *RV);
+ if (!RetCSAA || !RetCSAA->isValidState()) {
+ HasOverdefinedReturnedCalls = true;
+ LLVM_DEBUG(dbgs() << "[AAReturnedValues] Returned call site (" << *RV
+ << ") with " << (RetCSAA ? "invalid" : "no")
+ << " associated state\n");
+ continue;
+ }
+
+ // Try to find a unique assumed return value for the called function.
+ Optional<Value *> AssumedUniqueRV = RetCSAA->getUniqueReturnValue();
+
+ // If no unique assumed return value was found due to the lack of
+ // candidates, we may need to resolve more calls (through more update
+ // iterations) or the called function will not return. Either way, we simply
+ // stick with the call site as return value. Because there were not multiple
+ // possibilities, we do not treat it as overdefined.
+ if (!AssumedUniqueRV.hasValue())
+ continue;
+
+ // If multiple, non-refinable values were found, there cannot be a unique
+ // return value for the called function. The returned call is overdefined!
+ if (!AssumedUniqueRV.getValue()) {
+ HasOverdefinedReturnedCalls = true;
+ LLVM_DEBUG(dbgs() << "[AAReturnedValues] Returned call site has multiple "
+ "potentially returned values\n");
+ continue;
+ }
+
+ bool UniqueRVIsKnown = RetCSAA->isAtFixpoint();
+ LLVM_DEBUG(dbgs() << "[AAReturnedValues] Returned call site "
+ << (UniqueRVIsKnown ? "known" : "assumed")
+ << " unique return value: " << *AssumedUniqueRV << "\n");
+
+ // The unique assumed return value.
+ Value *AssumedRetVal = AssumedUniqueRV.getValue();
+
+ // If the unique assumed return value is an argument, lookup the matching
+ // call site operand and recursively collect new returned values.
+ // If it is not an argument, it is just put into the set of returned values
+ // as we would have already looked through casts, phis, and similar values.
+ if (Argument *AssumedRetArg = dyn_cast<Argument>(AssumedRetVal))
+ collectValuesRecursively(RetCS.getArgument(AssumedRetArg->getArgNo()),
+ ReturnInsts, AddRVs, Visited);
+ else
+ AddRVs[AssumedRetVal].insert(ReturnInsts.begin(), ReturnInsts.end());
+
+ // If the information for the called function is known to hold we drop the
+ // call from the set of returned values.
+ if (UniqueRVIsKnown)
+ DelRVs.insert(RV);
+ }
+
+ ChangeStatus Changed =
+ DelRVs.empty() ? ChangeStatus::UNCHANGED : ChangeStatus::CHANGED;
+
+ // Update the return values set after we stopped iterating over it.
+ for (Value *RV : DelRVs)
+ ReturnedValues.erase(RV);
+
+ for (auto &It : AddRVs) {
+ auto &ReturnInsts = ReturnedValues[It.first];
+ for (ReturnInst *RI : It.second)
+ if (ReturnInsts.insert(RI).second)
+ Changed = ChangeStatus::CHANGED;
+ }
+
+ // If there is no call site in the returned values we are done.
+ if (!HasCallSite) {
+ indicateFixpoint(/* Optimistic */ true);
+ return ChangeStatus::CHANGED;
+ }
+
+ return Changed;
+}
+
/// ----------------------------------------------------------------------------
/// Attributor
/// ----------------------------------------------------------------------------
@@ -292,6 +621,14 @@
}
void Attributor::identifyAbstractAttributes(Function &F) {
+ // Return attributes are only appropriate if the return type is non void.
+ Type *ReturnType = F.getReturnType();
+ if (!ReturnType->isVoidTy()) {
+ // Argument attribute "returned" --- Create only one per function even
+ // though it is an argument attribute.
+ registerAA(*new AAReturnedValuesImpl(F));
+ }
+
// Walk all instructions to find more attribute opportunities and also
// interesting instructions that might be querried by abstract attributes
// during their initialziation or update.
@@ -301,6 +638,8 @@
switch (I.getOpcode()) {
default:
break;
+ case Instruction::Ret: // ReturnInst are interesting for AAReturnedValues.
+ IsInteresting = true;
}
if (IsInteresting)
InstOpcodeMap[I.getOpcode()].push_back(&I);
Index: llvm/include/llvm/Transforms/IPO/Attributor.h
===================================================================
--- llvm/include/llvm/Transforms/IPO/Attributor.h
+++ llvm/include/llvm/Transforms/IPO/Attributor.h
@@ -235,6 +235,21 @@
/// Abstract Attribute Classes
/// ----------------------------------------------------------------------------
+/// An abstract attribute for the returned values of a function.
+struct AAReturnedValues : public AbstractAttribute {
+
+ /// See AbstractAttribute::AbstractAttribute(...).
+ AAReturnedValues(Function &F) : AbstractAttribute(F) {}
+
+ /// Return the number of potential return values, -1 if unknown.
+ virtual size_t getNumReturnValues() const = 0;
+
+ /// See AbstractAttribute::getAttrKind()
+ virtual Attribute::AttrKind getAttrKind() const override { return ID; }
+
+ /// The identifier used by the Attributor for this class of attributes.
+ static constexpr Attribute::AttrKind ID = Attribute::Returned;
+};
/// ----------------------------------------------------------------------------
/// Pass (Manager) Boilerplate
Index: clang/test/CodeGenOpenCL/as_type.cl
===================================================================
--- clang/test/CodeGenOpenCL/as_type.cl
+++ clang/test/CodeGenOpenCL/as_type.cl
@@ -67,7 +67,7 @@
return __builtin_astype(x, int3);
}
-//CHECK: define spir_func i32 addrspace(1)* @addr_cast(i32* readnone %[[x:.*]])
+//CHECK: define spir_func i32 addrspace(1)* @addr_cast(i32* readnone returned %[[x:.*]])
//CHECK: %[[cast:.*]] = addrspacecast i32* %[[x]] to i32 addrspace(1)*
//CHECK: ret i32 addrspace(1)* %[[cast]]
global int* addr_cast(int *x) {
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits