This revision was automatically updated to reflect the committed changes.
Closed by commit rL365407: [Attributor] Deduce the "returned" 
argument attribute (authored by jdoerfert, committed by ).

Changed prior to commit:
  https://reviews.llvm.org/D59919?vs=207924&id=208536#toc

Repository:
  rL LLVM

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D59919/new/

https://reviews.llvm.org/D59919

Files:
  llvm/trunk/include/llvm/Transforms/IPO/Attributor.h
  llvm/trunk/lib/Transforms/IPO/Attributor.cpp
  llvm/trunk/test/Transforms/FunctionAttrs/arg_nocapture.ll
  llvm/trunk/test/Transforms/FunctionAttrs/arg_returned.ll
  llvm/trunk/test/Transforms/FunctionAttrs/read_write_returned_arguments_scc.ll

Index: llvm/trunk/include/llvm/Transforms/IPO/Attributor.h
===================================================================
--- llvm/trunk/include/llvm/Transforms/IPO/Attributor.h
+++ llvm/trunk/include/llvm/Transforms/IPO/Attributor.h
@@ -642,6 +642,27 @@
 ///                       Abstract Attribute Classes
 /// ----------------------------------------------------------------------------
 
+/// An abstract attribute for the returned values of a function.
+struct AAReturnedValues : public AbstractAttribute {
+  /// See AbstractAttribute::AbstractAttribute(...).
+  AAReturnedValues(Function &F, InformationCache &InfoCache)
+      : AbstractAttribute(F, InfoCache) {}
+
+  /// Check \p Pred on all returned values.
+  ///
+  /// This method will evaluate \p Pred on returned values and return
+  /// true if (1) all returned values are known, and (2) \p Pred returned true
+  /// for all returned values.
+  virtual bool
+  checkForallReturnedValues(std::function<bool(Value &)> &Pred) 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;
+};
+
 struct AANoUnwind : public AbstractAttribute {
     /// An abstract interface for all nosync attributes.
     AANoUnwind(Value &V, InformationCache &InfoCache)
Index: llvm/trunk/test/Transforms/FunctionAttrs/arg_nocapture.ll
===================================================================
--- llvm/trunk/test/Transforms/FunctionAttrs/arg_nocapture.ll
+++ llvm/trunk/test/Transforms/FunctionAttrs/arg_nocapture.ll
@@ -112,18 +112,15 @@
 
 ; TEST SCC with various calls, casts, and comparisons agains NULL
 ;
-; FIXME: returned missing for %a
 ; FIXME: no-capture missing for %a
-; CHECK: define float* @scc_A(i32* readnone %a)
+; CHECK: define float* @scc_A(i32* readnone returned %a)
 ;
-; FIXME: returned missing for %a
 ; FIXME: no-capture missing for %a
-; CHECK: define i64* @scc_B(double* readnone %a)
+; CHECK: define 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 i8* @scc_C(i16* %a)
+; CHECK: define 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/trunk/test/Transforms/FunctionAttrs/arg_returned.ll
===================================================================
--- llvm/trunk/test/Transforms/FunctionAttrs/arg_returned.ll
+++ llvm/trunk/test/Transforms/FunctionAttrs/arg_returned.ll
@@ -1,5 +1,6 @@
-; 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.
@@ -7,16 +8,24 @@
 
 ; 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);
@@ -150,16 +159,20 @@
 
 ; 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)
-;
-; 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)
-;
-; 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)
+; 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)
+;
+; 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)
+;
+; 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);
@@ -237,41 +250,95 @@
 }
 
 
-; 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
 ;
@@ -282,11 +349,12 @@
 ;    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 {
@@ -313,6 +381,12 @@
 ;
 ; 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
@@ -324,6 +398,36 @@
   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
 ;
@@ -334,9 +438,11 @@
 ;   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
@@ -362,9 +468,11 @@
 ;   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
@@ -389,9 +497,11 @@
 ;   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*
@@ -408,9 +518,11 @@
 ;   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*
@@ -442,8 +554,11 @@
 ;   /* 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*
@@ -475,8 +590,11 @@
 ;   /* 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*
@@ -508,8 +626,11 @@
 ;   /* 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*
@@ -534,13 +655,17 @@
 ; 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 {
@@ -573,11 +698,40 @@
   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 #
Index: llvm/trunk/test/Transforms/FunctionAttrs/read_write_returned_arguments_scc.ll
===================================================================
--- llvm/trunk/test/Transforms/FunctionAttrs/read_write_returned_arguments_scc.ll
+++ llvm/trunk/test/Transforms/FunctionAttrs/read_write_returned_arguments_scc.ll
@@ -31,7 +31,7 @@
 target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
 
 ; CHECK: Function Attrs: nofree nounwind
-; CHECK-NEXT: define i32* @external_ret2_nrw(i32* %n0, i32* %r0, i32* %w0)
+; CHECK-NEXT: define i32* @external_ret2_nrw(i32* %n0, i32* %r0, i32* returned %w0)
 define i32* @external_ret2_nrw(i32* %n0, i32* %r0, i32* %w0) {
 entry:
   %call = call i32* @internal_ret0_nw(i32* %n0, i32* %w0)
@@ -42,7 +42,7 @@
 }
 
 ; CHECK: Function Attrs: nofree nounwind
-; CHECK-NEXT: define internal i32* @internal_ret0_nw(i32* %n0, i32* %w0)
+; CHECK-NEXT: define internal i32* @internal_ret0_nw(i32* returned %n0, i32* %w0)
 define internal i32* @internal_ret0_nw(i32* %n0, i32* %w0) {
 entry:
   %r0 = alloca i32, align 4
@@ -71,7 +71,7 @@
 }
 
 ; CHECK: Function Attrs: nofree nounwind
-; CHECK-NEXT: define internal i32* @internal_ret1_rrw(i32* %r0, i32* %r1, i32* %w0)
+; CHECK-NEXT: define internal i32* @internal_ret1_rrw(i32* %r0, i32* returned %r1, i32* %w0)
 define internal i32* @internal_ret1_rrw(i32* %r0, i32* %r1, i32* %w0) {
 entry:
   %0 = load i32, i32* %r0, align 4
@@ -122,7 +122,7 @@
 }
 
 ; CHECK: Function Attrs: nofree nounwind
-; CHECK-NEXT: define internal i32* @internal_ret1_rw(i32* %r0, i32* %w0)
+; CHECK-NEXT: define internal i32* @internal_ret1_rw(i32* %r0, i32* returned %w0)
 define internal i32* @internal_ret1_rw(i32* %r0, i32* %w0) {
 entry:
   %0 = load i32, i32* %r0, align 4
@@ -148,7 +148,7 @@
 }
 
 ; CHECK: Function Attrs: nofree nounwind
-; CHECK-NEXT: define i32* @external_source_ret2_nrw(i32* %n0, i32* %r0, i32* %w0)
+; CHECK-NEXT: define i32* @external_source_ret2_nrw(i32* %n0, i32* %r0, i32* returned %w0)
 define 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/trunk/lib/Transforms/IPO/Attributor.cpp
===================================================================
--- llvm/trunk/lib/Transforms/IPO/Attributor.cpp
+++ llvm/trunk/lib/Transforms/IPO/Attributor.cpp
@@ -44,6 +44,11 @@
           "Number of abstract attributes manifested in IR");
 STATISTIC(NumFnNoUnwind, "Number of functions marked nounwind");
 
+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.
 //
 // In the LLVM-TS and SPEC2006, 32 seems to not induce compile time overheads
@@ -91,11 +96,97 @@
   case Attribute::NoUnwind:
     NumFnNoUnwind++;
     return;
+  case Attribute::Returned:
+    NumFnArgumentReturned++;
+    return;
   default:
     return;
   }
 }
 
+template <typename StateTy>
+using followValueCB_t = std::function<bool(Value *, StateTy &State)>;
+template <typename StateTy>
+using visitValueCB_t = std::function<void(Value *, StateTy &State)>;
+
+/// Recursively visit all values that might become \p InitV at some point. This
+/// will be done by looking through cast instructions, selects, phis, and calls
+/// with the "returned" attribute. The callback \p FollowValueCB is asked before
+/// a potential origin value is looked at. If no \p FollowValueCB is passed, a
+/// default one is used that will make sure we visit every value only once. Once
+/// we cannot look through the value any further, the callback \p VisitValueCB
+/// is invoked and passed the current value and the \p State. To limit how much
+/// effort is invested, we will never visit more than \p MaxValues values.
+template <typename StateTy>
+static bool genericValueTraversal(
+    Value *InitV, StateTy &State, visitValueCB_t<StateTy> &VisitValueCB,
+    followValueCB_t<StateTy> *FollowValueCB = nullptr, int MaxValues = 8) {
+
+  SmallPtrSet<Value *, 16> Visited;
+  followValueCB_t<bool> DefaultFollowValueCB = [&](Value *Val, bool &) {
+    return Visited.insert(Val).second;
+  };
+
+  if (!FollowValueCB)
+    FollowValueCB = &DefaultFollowValueCB;
+
+  SmallVector<Value *, 16> Worklist;
+  Worklist.push_back(InitV);
+
+  int Iteration = 0;
+  do {
+    Value *V = Worklist.pop_back_val();
+
+    // Check if we should process the current value. To prevent endless
+    // recursion keep a record of the values we followed!
+    if (!(*FollowValueCB)(V, State))
+      continue;
+
+    // Make sure we limit the compile time for complex expressions.
+    if (Iteration++ >= MaxValues)
+      return false;
+
+    // Explicitly look through calls with a "returned" attribute if we do
+    // not have a pointer as stripPointerCasts only works on them.
+    if (V->getType()->isPointerTy()) {
+      V = V->stripPointerCasts();
+    } else {
+      CallSite CS(V);
+      if (CS && CS.getCalledFunction()) {
+        Value *NewV = nullptr;
+        for (Argument &Arg : CS.getCalledFunction()->args())
+          if (Arg.hasReturnedAttr()) {
+            NewV = CS.getArgOperand(Arg.getArgNo());
+            break;
+          }
+        if (NewV) {
+          Worklist.push_back(NewV);
+          continue;
+        }
+      }
+    }
+
+    // Look through select instructions, visit both potential values.
+    if (auto *SI = dyn_cast<SelectInst>(V)) {
+      Worklist.push_back(SI->getTrueValue());
+      Worklist.push_back(SI->getFalseValue());
+      continue;
+    }
+
+    // Look through phi nodes, visit all operands.
+    if (auto *PHI = dyn_cast<PHINode>(V)) {
+      Worklist.append(PHI->op_begin(), PHI->op_end());
+      continue;
+    }
+
+    // Once a leaf is reached we inform the user through the callback.
+    VisitValueCB(V, State);
+  } while (!Worklist.empty());
+
+  // All values have been visited.
+  return true;
+}
+
 /// Helper to identify the correct offset into an attribute list.
 static unsigned getAttrIndex(AbstractAttribute::ManifestPosition MP,
                              unsigned ArgNo = 0) {
@@ -303,6 +394,331 @@
   return ChangeStatus::UNCHANGED;
 }
 
+/// --------------------- 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;
+
+  /// State flags
+  ///
+  ///{
+  bool IsFixed;
+  bool IsValidState;
+  bool HasOverdefinedReturnedCalls;
+  ///}
+
+  /// Collect values that could become \p V in the set \p Values, each mapped to
+  /// \p ReturnInsts.
+  void collectValuesRecursively(
+      Attributor &A, Value *V, SmallPtrSetImpl<ReturnInst *> &ReturnInsts,
+      DenseMap<Value *, SmallPtrSet<ReturnInst *, 2>> &Values) {
+
+    visitValueCB_t<bool> VisitValueCB = [&](Value *Val, bool &) {
+      assert(!isa<Instruction>(Val) ||
+             &getAnchorScope() == cast<Instruction>(Val)->getFunction());
+      Values[Val].insert(ReturnInsts.begin(), ReturnInsts.end());
+    };
+
+    bool UnusedBool;
+    bool Success = genericValueTraversal(V, UnusedBool, VisitValueCB);
+
+    // If we did abort the above traversal we haven't see all the values.
+    // Consequently, we cannot know if the information we would derive is
+    // accurate so we give up early.
+    if (!Success)
+      indicatePessimisticFixpoint();
+  }
+
+public:
+  /// See AbstractAttribute::AbstractAttribute(...).
+  AAReturnedValuesImpl(Function &F, InformationCache &InfoCache)
+      : AAReturnedValues(F, InfoCache) {
+    // We do not have an associated argument yet.
+    AssociatedVal = nullptr;
+  }
+
+  /// See AbstractAttribute::initialize(...).
+  void initialize(Attributor &A) override {
+    // Reset the state.
+    AssociatedVal = nullptr;
+    IsFixed = false;
+    IsValidState = true;
+    HasOverdefinedReturnedCalls = false;
+    ReturnedValues.clear();
+
+    Function &F = cast<Function>(getAnchoredValue());
+
+    // The map from instruction opcodes to those instructions in the function.
+    auto &OpcodeInstMap = InfoCache.getOpcodeInstMapForFunction(F);
+
+    // Look through all arguments, if one is marked as returned we are done.
+    for (Argument &Arg : F.args()) {
+      if (Arg.hasReturnedAttr()) {
+
+        auto &ReturnInstSet = ReturnedValues[&Arg];
+        for (Instruction *RI : OpcodeInstMap[Instruction::Ret])
+          ReturnInstSet.insert(cast<ReturnInst>(RI));
+
+        indicateOptimisticFixpoint();
+        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)});
+      collectValuesRecursively(A, cast<ReturnInst>(RI)->getReturnValue(), RISet,
+                               ReturnedValues);
+    }
+  }
+
+  /// 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;
+
+  /// Return the number of potential return values, -1 if unknown.
+  size_t getNumReturnValues() const {
+    return isValidState() ? ReturnedValues.size() : -1;
+  }
+
+  /// 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 *> getAssumedUniqueReturnValue() const;
+
+  /// See AbstractState::checkForallReturnedValues(...).
+  virtual bool
+  checkForallReturnedValues(std::function<bool(Value &)> &Pred) const override;
+
+  /// 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::indicateOptimisticFixpoint(...).
+  void indicateOptimisticFixpoint() override {
+    IsFixed = true;
+    IsValidState &= true;
+  }
+  void indicatePessimisticFixpoint() override {
+    IsFixed = true;
+    IsValidState = false;
+  }
+};
+
+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 = getAssumedUniqueReturnValue();
+
+  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::getAssumedUniqueReturnValue() const {
+  // If checkForallReturnedValues provides a unique value, 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.
+  Optional<Value *> UniqueRV;
+
+  std::function<bool(Value &)> Pred = [&](Value &RV) -> bool {
+    // 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()))) {
+      UniqueRV = nullptr;
+      return false;
+    }
+
+    // Do not overwrite a value with an undef.
+    if (!UniqueRV.hasValue() || !isa<UndefValue>(RV))
+      UniqueRV = &RV;
+
+    return true;
+  };
+
+  if (!checkForallReturnedValues(Pred))
+    UniqueRV = nullptr;
+
+  return UniqueRV;
+}
+
+bool AAReturnedValuesImpl::checkForallReturnedValues(
+    std::function<bool(Value &)> &Pred) const {
+  if (!isValidState())
+    return false;
+
+  // Check all returned values but ignore call sites as long as we have not
+  // encountered an overdefined one during an update.
+  for (auto &It : ReturnedValues) {
+    Value *RV = It.first;
+
+    ImmutableCallSite ICS(RV);
+    if (ICS && !HasOverdefinedReturnedCalls)
+      continue;
+
+    if (!Pred(*RV))
+      return false;
+  }
+
+  return true;
+}
+
+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) {
+    indicateOptimisticFixpoint();
+    return ChangeStatus::UNCHANGED;
+  }
+
+  // Check if any of the returned values is a call site we can refine.
+  decltype(ReturnedValues) AddRVs;
+  bool HasCallSite = false;
+
+  // Look at all returned call sites.
+  for (auto &It : ReturnedValues) {
+    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;
+
+    // Try to find a assumed unique return value for the called function.
+    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 assumed unique return value for the called function.
+    Optional<Value *> AssumedUniqueRV = RetCSAA->getAssumedUniqueReturnValue();
+
+    // If no assumed unique 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 sites as return values. 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;
+    }
+
+    LLVM_DEBUG({
+      bool UniqueRVIsKnown = RetCSAA->isAtFixpoint();
+      dbgs() << "[AAReturnedValues] Returned call site "
+             << (UniqueRVIsKnown ? "known" : "assumed")
+             << " unique return value: " << *AssumedUniqueRV << "\n";
+    });
+
+    // The assumed unique return value.
+    Value *AssumedRetVal = AssumedUniqueRV.getValue();
+
+    // If the assumed unique 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(A,
+                               RetCS.getArgOperand(AssumedRetArg->getArgNo()),
+                               ReturnInsts, AddRVs);
+    else
+      AddRVs[AssumedRetVal].insert(ReturnInsts.begin(), ReturnInsts.end());
+  }
+
+  // Keep track of any change to trigger updates on dependent attributes.
+  ChangeStatus Changed = ChangeStatus::UNCHANGED;
+
+  for (auto &It : AddRVs) {
+    assert(!It.second.empty() && "Entry does not add anything.");
+    auto &ReturnInsts = ReturnedValues[It.first];
+    for (ReturnInst *RI : It.second)
+      if (ReturnInsts.insert(RI).second) {
+        LLVM_DEBUG(dbgs() << "[AAReturnedValues] Add new returned value "
+                          << *It.first << " => " << *RI << "\n");
+        Changed = ChangeStatus::CHANGED;
+      }
+  }
+
+  // If there is no call site in the returned values we are done.
+  if (!HasCallSite) {
+    indicateOptimisticFixpoint();
+    return ChangeStatus::CHANGED;
+  }
+
+  return Changed;
+}
+
 /// ----------------------------------------------------------------------------
 ///                               Attributor
 /// ----------------------------------------------------------------------------
@@ -448,6 +864,15 @@
   // Every function can be nounwind.
   registerAA(*new AANoUnwindFunction(F, InfoCache));
 
+  // 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.
+    if (!Whitelist || Whitelist->count(AAReturnedValues::ID))
+      registerAA(*new AAReturnedValuesImpl(F, InfoCache));
+  }
+
   // Walk all instructions to find more attribute opportunities and also
   // interesting instructions that might be queried by abstract attributes
   // during their initialization or update.
@@ -474,6 +899,7 @@
     case Instruction::CleanupRet:
     case Instruction::CatchSwitch:
     case Instruction::Resume:
+    case Instruction::Ret:
       IsInterestingOpcode = true;
     }
     if (IsInterestingOpcode)
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to