[llvm] r185735 - Extend 'readonly' and 'readnone' to work on function arguments as well as

Nick Lewycky nicholas at mxc.ca
Fri Jul 5 17:29:58 PDT 2013


Author: nicholas
Date: Fri Jul  5 19:29:58 2013
New Revision: 185735

URL: http://llvm.org/viewvc/llvm-project?rev=185735&view=rev
Log:
Extend 'readonly' and 'readnone' to work on function arguments as well as
functions. Make the function attributes pass add it to known library functions
and when it can deduce it.

Added:
    llvm/trunk/test/Transforms/FunctionAttrs/readattrs.ll
Modified:
    llvm/trunk/docs/LangRef.rst
    llvm/trunk/include/llvm/IR/Argument.h
    llvm/trunk/include/llvm/IR/Function.h
    llvm/trunk/include/llvm/IR/Intrinsics.td
    llvm/trunk/include/llvm/Support/CallSite.h
    llvm/trunk/lib/Analysis/CaptureTracking.cpp
    llvm/trunk/lib/AsmParser/LLParser.cpp
    llvm/trunk/lib/IR/Attributes.cpp
    llvm/trunk/lib/IR/Function.cpp
    llvm/trunk/lib/IR/Verifier.cpp
    llvm/trunk/lib/Transforms/IPO/FunctionAttrs.cpp
    llvm/trunk/test/Analysis/TypeBasedAliasAnalysis/functionattrs.ll
    llvm/trunk/test/Transforms/FunctionAttrs/2009-01-02-LocalStores.ll
    llvm/trunk/test/Transforms/FunctionAttrs/2009-01-04-Annotate.ll
    llvm/trunk/test/Transforms/FunctionAttrs/annotate-1.ll
    llvm/trunk/test/Transforms/FunctionAttrs/atomic.ll
    llvm/trunk/test/Transforms/FunctionAttrs/nocapture.ll
    llvm/trunk/test/Transforms/InstCombine/strto-1.ll
    llvm/trunk/utils/TableGen/CodeGenIntrinsics.h
    llvm/trunk/utils/TableGen/CodeGenTarget.cpp
    llvm/trunk/utils/TableGen/IntrinsicEmitter.cpp

Modified: llvm/trunk/docs/LangRef.rst
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/docs/LangRef.rst?rev=185735&r1=185734&r2=185735&view=diff
==============================================================================
--- llvm/trunk/docs/LangRef.rst (original)
+++ llvm/trunk/docs/LangRef.rst Fri Jul  5 19:29:58 2013
@@ -879,17 +879,22 @@ example:
     passes make choices that keep the code size of this function low,
     and otherwise do optimizations specifically to reduce code size.
 ``readnone``
-    This attribute indicates that the function computes its result (or
-    decides to unwind an exception) based strictly on its arguments,
+    On a function, this attribute indicates that the function computes its
+    result (or decides to unwind an exception) based strictly on its arguments,
     without dereferencing any pointer arguments or otherwise accessing
     any mutable state (e.g. memory, control registers, etc) visible to
     caller functions. It does not write through any pointer arguments
     (including ``byval`` arguments) and never changes any state visible
     to callers. This means that it cannot unwind exceptions by calling
     the ``C++`` exception throwing methods.
+    
+    On an argument, this attribute indicates that the function does not
+    dereference that pointer argument, even though it may read or write the
+    memory that the pointer points to through if accessed through other
+    pointers.
 ``readonly``
-    This attribute indicates that the function does not write through
-    any pointer arguments (including ``byval`` arguments) or otherwise
+    On a function, this attribute indicates that the function does not write
+    through any pointer arguments (including ``byval`` arguments) or otherwise
     modify any state (e.g. memory, control registers, etc) visible to
     caller functions. It may dereference pointer arguments and read
     state that may be set in the caller. A readonly function always
@@ -897,6 +902,10 @@ example:
     called with the same set of arguments and global state. It cannot
     unwind an exception by calling the ``C++`` exception throwing
     methods.
+    
+    On an argument, this attribute indicates that the function does not write
+    through this pointer argument, even though it may write to the memory that
+    the pointer points to.
 ``returns_twice``
     This attribute indicates that this function can return twice. The C
     ``setjmp`` is an example of such a function. The compiler disables

Modified: llvm/trunk/include/llvm/IR/Argument.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/IR/Argument.h?rev=185735&r1=185734&r2=185735&view=diff
==============================================================================
--- llvm/trunk/include/llvm/IR/Argument.h (original)
+++ llvm/trunk/include/llvm/IR/Argument.h Fri Jul  5 19:29:58 2013
@@ -82,6 +82,11 @@ public:
   /// its containing function.
   bool hasReturnedAttr() const;
 
+  /// \brief Return true if this argument has the readonly or readnone attribute
+  /// on it in its containing function.
+  bool onlyReadsMemory() const;
+
+
   /// \brief Add a Attribute to an argument.
   void addAttr(AttributeSet AS);
 

Modified: llvm/trunk/include/llvm/IR/Function.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/IR/Function.h?rev=185735&r1=185734&r2=185735&view=diff
==============================================================================
--- llvm/trunk/include/llvm/IR/Function.h (original)
+++ llvm/trunk/include/llvm/IR/Function.h Fri Jul  5 19:29:58 2013
@@ -310,6 +310,14 @@ public:
     addAttribute(n, Attribute::NoCapture);
   }
 
+  bool onlyReadsMemory(unsigned n) const {
+    return AttributeSets.hasAttribute(n, Attribute::ReadOnly) ||
+           AttributeSets.hasAttribute(n, Attribute::ReadNone);
+  }
+  void setOnlyReadsMemory(unsigned n) {
+    addAttribute(n, Attribute::ReadOnly);
+  }
+
   /// copyAttributesFrom - copy all additional attributes (those not needed to
   /// create a Function) from the Function Src to this one.
   void copyAttributesFrom(const GlobalValue *Src);

Modified: llvm/trunk/include/llvm/IR/Intrinsics.td
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/IR/Intrinsics.td?rev=185735&r1=185734&r2=185735&view=diff
==============================================================================
--- llvm/trunk/include/llvm/IR/Intrinsics.td (original)
+++ llvm/trunk/include/llvm/IR/Intrinsics.td Fri Jul  5 19:29:58 2013
@@ -55,6 +55,18 @@ class NoCapture<int argNo> : IntrinsicPr
   int ArgNo = argNo;
 }
 
+// ReadOnly - The specified argument pointer is not written to through the
+// pointer by the intrinsic.
+class ReadOnly<int argNo> : IntrinsicProperty {
+  int ArgNo = argNo;
+}
+
+// ReadNone - The specified argument pointer is not dereferenced by the
+// intrinsic.
+class ReadNone<int argNo> : IntrinsicProperty {
+  int ArgNo = argNo;
+}
+
 def IntrNoReturn : IntrinsicProperty;
 
 //===----------------------------------------------------------------------===//
@@ -253,11 +265,13 @@ def int_stackprotector : Intrinsic<[], [
 def int_memcpy  : Intrinsic<[],
                              [llvm_anyptr_ty, llvm_anyptr_ty, llvm_anyint_ty,
                               llvm_i32_ty, llvm_i1_ty],
-                            [IntrReadWriteArgMem, NoCapture<0>, NoCapture<1>]>;
+                            [IntrReadWriteArgMem, NoCapture<0>, NoCapture<1>,
+                             ReadOnly<1>]>;
 def int_memmove : Intrinsic<[],
                             [llvm_anyptr_ty, llvm_anyptr_ty, llvm_anyint_ty,
                              llvm_i32_ty, llvm_i1_ty],
-                            [IntrReadWriteArgMem, NoCapture<0>, NoCapture<1>]>;
+                            [IntrReadWriteArgMem, NoCapture<0>, NoCapture<1>,
+                             ReadOnly<1>]>;
 def int_memset  : Intrinsic<[],
                             [llvm_anyptr_ty, llvm_i8_ty, llvm_anyint_ty,
                              llvm_i32_ty, llvm_i1_ty],

Modified: llvm/trunk/include/llvm/Support/CallSite.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/Support/CallSite.h?rev=185735&r1=185734&r2=185735&view=diff
==============================================================================
--- llvm/trunk/include/llvm/Support/CallSite.h (original)
+++ llvm/trunk/include/llvm/Support/CallSite.h Fri Jul  5 19:29:58 2013
@@ -257,6 +257,15 @@ public:
     return paramHasAttr(ArgNo + 1, Attribute::ByVal);
   }
 
+  bool doesNotAccessMemory(unsigned ArgNo) const {
+    return paramHasAttr(ArgNo + 1, Attribute::ReadNone);
+  }
+
+  bool onlyReadsMemory(unsigned ArgNo) const {
+    return paramHasAttr(ArgNo + 1, Attribute::ReadOnly) ||
+           paramHasAttr(ArgNo + 1, Attribute::ReadNone);
+  }
+
   /// hasArgument - Returns true if this CallSite passes the given Value* as an
   /// argument to the called function.
   bool hasArgument(const Value *Arg) const {

Modified: llvm/trunk/lib/Analysis/CaptureTracking.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Analysis/CaptureTracking.cpp?rev=185735&r1=185734&r2=185735&view=diff
==============================================================================
--- llvm/trunk/lib/Analysis/CaptureTracking.cpp (original)
+++ llvm/trunk/lib/Analysis/CaptureTracking.cpp Fri Jul  5 19:29:58 2013
@@ -158,10 +158,10 @@ void llvm::PointerMayBeCaptured(const Va
       // Don't count comparisons of a no-alias return value against null as
       // captures. This allows us to ignore comparisons of malloc results
       // with null, for example.
-      if (isNoAliasCall(V->stripPointerCasts()))
-        if (ConstantPointerNull *CPN =
-              dyn_cast<ConstantPointerNull>(I->getOperand(1)))
-          if (CPN->getType()->getAddressSpace() == 0)
+      if (ConstantPointerNull *CPN =
+          dyn_cast<ConstantPointerNull>(I->getOperand(1)))
+        if (CPN->getType()->getAddressSpace() == 0)
+          if (isNoAliasCall(V->stripPointerCasts()))
             break;
       // Otherwise, be conservative. There are crazy ways to capture pointers
       // using comparisons.

Modified: llvm/trunk/lib/AsmParser/LLParser.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/AsmParser/LLParser.cpp?rev=185735&r1=185734&r2=185735&view=diff
==============================================================================
--- llvm/trunk/lib/AsmParser/LLParser.cpp (original)
+++ llvm/trunk/lib/AsmParser/LLParser.cpp Fri Jul  5 19:29:58 2013
@@ -1159,6 +1159,8 @@ bool LLParser::ParseOptionalParamAttrs(A
     case lltok::kw_nest:            B.addAttribute(Attribute::Nest); break;
     case lltok::kw_noalias:         B.addAttribute(Attribute::NoAlias); break;
     case lltok::kw_nocapture:       B.addAttribute(Attribute::NoCapture); break;
+    case lltok::kw_readnone:        B.addAttribute(Attribute::ReadNone); break;
+    case lltok::kw_readonly:        B.addAttribute(Attribute::ReadOnly); break;
     case lltok::kw_returned:        B.addAttribute(Attribute::Returned); break;
     case lltok::kw_signext:         B.addAttribute(Attribute::SExt); break;
     case lltok::kw_sret:            B.addAttribute(Attribute::StructRet); break;
@@ -1179,8 +1181,6 @@ bool LLParser::ParseOptionalParamAttrs(A
     case lltok::kw_noreturn:
     case lltok::kw_nounwind:
     case lltok::kw_optsize:
-    case lltok::kw_readnone:
-    case lltok::kw_readonly:
     case lltok::kw_returns_twice:
     case lltok::kw_sanitize_address:
     case lltok::kw_sanitize_memory:
@@ -1239,8 +1239,6 @@ bool LLParser::ParseOptionalReturnAttrs(
     case lltok::kw_noreturn:
     case lltok::kw_nounwind:
     case lltok::kw_optsize:
-    case lltok::kw_readnone:
-    case lltok::kw_readonly:
     case lltok::kw_returns_twice:
     case lltok::kw_sanitize_address:
     case lltok::kw_sanitize_memory:
@@ -1251,6 +1249,10 @@ bool LLParser::ParseOptionalReturnAttrs(
     case lltok::kw_uwtable:
       HaveError |= Error(Lex.getLoc(), "invalid use of function-only attribute");
       break;
+
+    case lltok::kw_readnone:
+    case lltok::kw_readonly:
+      HaveError |= Error(Lex.getLoc(), "invalid use of attribute on return type");
     }
 
     Lex.Lex();

Modified: llvm/trunk/lib/IR/Attributes.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/IR/Attributes.cpp?rev=185735&r1=185734&r2=185735&view=diff
==============================================================================
--- llvm/trunk/lib/IR/Attributes.cpp (original)
+++ llvm/trunk/lib/IR/Attributes.cpp Fri Jul  5 19:29:58 2013
@@ -1157,6 +1157,8 @@ AttributeSet AttributeFuncs::typeIncompa
       .addAttribute(Attribute::Nest)
       .addAttribute(Attribute::NoAlias)
       .addAttribute(Attribute::NoCapture)
+      .addAttribute(Attribute::ReadNone)
+      .addAttribute(Attribute::ReadOnly)
       .addAttribute(Attribute::StructRet);
 
   return AttributeSet::get(Ty->getContext(), Index, Incompatible);

Modified: llvm/trunk/lib/IR/Function.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/IR/Function.cpp?rev=185735&r1=185734&r2=185735&view=diff
==============================================================================
--- llvm/trunk/lib/IR/Function.cpp (original)
+++ llvm/trunk/lib/IR/Function.cpp Fri Jul  5 19:29:58 2013
@@ -131,6 +131,15 @@ bool Argument::hasReturnedAttr() const {
     hasAttribute(getArgNo()+1, Attribute::Returned);
 }
 
+/// Return true if this argument has the readonly or readnone attribute on it
+/// in its containing function.
+bool Argument::onlyReadsMemory() const {
+  return getParent()->getAttributes().
+      hasAttribute(getArgNo()+1, Attribute::ReadOnly) ||
+      getParent()->getAttributes().
+      hasAttribute(getArgNo()+1, Attribute::ReadNone);
+}
+
 /// addAttr - Add attributes to an argument.
 void Argument::addAttr(AttributeSet AS) {
   assert(AS.getNumSlots() <= 1 &&
@@ -711,4 +720,3 @@ bool Function::callsFunctionThatReturnsT
 
   return false;
 }
-

Modified: llvm/trunk/lib/IR/Verifier.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/IR/Verifier.cpp?rev=185735&r1=185734&r2=185735&view=diff
==============================================================================
--- llvm/trunk/lib/IR/Verifier.cpp (original)
+++ llvm/trunk/lib/IR/Verifier.cpp Fri Jul  5 19:29:58 2013
@@ -654,7 +654,7 @@ void Verifier::visitModuleFlag(MDNode *O
 }
 
 void Verifier::VerifyAttributeTypes(AttributeSet Attrs, unsigned Idx,
-                                    bool isFunction, const Value* V) {
+                                    bool isFunction, const Value *V) {
   unsigned Slot = ~0U;
   for (unsigned I = 0, E = Attrs.getNumSlots(); I != E; ++I)
     if (Attrs.getSlotIndex(I) == Idx) {
@@ -671,8 +671,6 @@ void Verifier::VerifyAttributeTypes(Attr
 
     if (I->getKindAsEnum() == Attribute::NoReturn ||
         I->getKindAsEnum() == Attribute::NoUnwind ||
-        I->getKindAsEnum() == Attribute::ReadNone ||
-        I->getKindAsEnum() == Attribute::ReadOnly ||
         I->getKindAsEnum() == Attribute::NoInline ||
         I->getKindAsEnum() == Attribute::AlwaysInline ||
         I->getKindAsEnum() == Attribute::OptimizeForSize ||
@@ -696,14 +694,21 @@ void Verifier::VerifyAttributeTypes(Attr
         I->getKindAsEnum() == Attribute::NoBuiltin ||
         I->getKindAsEnum() == Attribute::Cold) {
       if (!isFunction) {
-          CheckFailed("Attribute '" + I->getAsString() +
-                      "' only applies to functions!", V);
-          return;
+        CheckFailed("Attribute '" + I->getAsString() +
+                    "' only applies to functions!", V);
+        return;
       }
-    } else if (isFunction) {
+    } else if (I->getKindAsEnum() == Attribute::ReadOnly ||
+               I->getKindAsEnum() == Attribute::ReadNone) {
+      if (Idx == 0) {
         CheckFailed("Attribute '" + I->getAsString() +
-                    "' does not apply to functions!", V);
+                    "' does not apply to function returns");
         return;
+      }
+    } else if (isFunction) {
+      CheckFailed("Attribute '" + I->getAsString() +
+                  "' does not apply to functions!", V);
+      return;
     }
   }
 }

Modified: llvm/trunk/lib/Transforms/IPO/FunctionAttrs.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Transforms/IPO/FunctionAttrs.cpp?rev=185735&r1=185734&r2=185735&view=diff
==============================================================================
--- llvm/trunk/lib/Transforms/IPO/FunctionAttrs.cpp (original)
+++ llvm/trunk/lib/Transforms/IPO/FunctionAttrs.cpp Fri Jul  5 19:29:58 2013
@@ -9,14 +9,12 @@
 //
 // This file implements a simple interprocedural pass which walks the
 // call-graph, looking for functions which do not access or only read
-// non-local memory, and marking them readnone/readonly.  In addition,
-// it marks function arguments (of pointer type) 'nocapture' if a call
-// to the function does not create any copies of the pointer value that
-// outlive the call.  This more or less means that the pointer is only
-// dereferenced, and not returned from the function or stored in a global.
-// Finally, well-known library call declarations are marked with all
-// attributes that are consistent with the function's standard definition.
-// This pass is implemented as a bottom-up traversal of the call-graph.
+// non-local memory, and marking them readnone/readonly.  It does the
+// same with function arguments independently, marking them readonly/
+// readnone/nocapture.  Finally, well-known library call declarations
+// are marked with all attributes that are consistent with the
+// function's standard definition. This pass is implemented as a
+// bottom-up traversal of the call-graph.
 //
 //===----------------------------------------------------------------------===//
 
@@ -40,6 +38,8 @@ using namespace llvm;
 STATISTIC(NumReadNone, "Number of functions marked readnone");
 STATISTIC(NumReadOnly, "Number of functions marked readonly");
 STATISTIC(NumNoCapture, "Number of arguments marked nocapture");
+STATISTIC(NumReadNoneArg, "Number of arguments marked readnone");
+STATISTIC(NumReadOnlyArg, "Number of arguments marked readonly");
 STATISTIC(NumNoAlias, "Number of function returns marked noalias");
 STATISTIC(NumAnnotated, "Number of attributes added to library functions");
 
@@ -56,8 +56,8 @@ namespace {
     // AddReadAttrs - Deduce readonly/readnone attributes for the SCC.
     bool AddReadAttrs(const CallGraphSCC &SCC);
 
-    // AddNoCaptureAttrs - Deduce nocapture attributes for the SCC.
-    bool AddNoCaptureAttrs(const CallGraphSCC &SCC);
+    // AddArgumentAttrs - Deduce nocapture attributes for the SCC.
+    bool AddArgumentAttrs(const CallGraphSCC &SCC);
 
     // IsFunctionMallocLike - Does this function allocate new memory?
     bool IsFunctionMallocLike(Function *F,
@@ -97,6 +97,13 @@ namespace {
       }
     }
 
+    void setOnlyReadsMemory(Function &F, unsigned n) {
+      if (!F.onlyReadsMemory(n)) {
+        F.setOnlyReadsMemory(n);
+        ++NumAnnotated;
+      }
+    }
+
     void setDoesNotAlias(Function &F, unsigned n) {
       if (!F.doesNotAlias(n)) {
         F.setDoesNotAlias(n);
@@ -343,6 +350,7 @@ namespace {
       Function *F = CS.getCalledFunction();
       if (!F || !SCCNodes.count(F)) { Captured = true; return true; }
 
+      bool Found = false;
       Function::arg_iterator AI = F->arg_begin(), AE = F->arg_end();
       for (CallSite::arg_iterator PI = CS.arg_begin(), PE = CS.arg_end();
            PI != PE; ++PI, ++AI) {
@@ -353,10 +361,11 @@ namespace {
         }
         if (PI == U) {
           Uses.push_back(AI);
+          Found = true;
           break;
         }
       }
-      assert(!Uses.empty() && "Capturing call-site captured nothing?");
+      assert(Found && "Capturing call-site captured nothing?");
       return false;
     }
 
@@ -394,8 +403,100 @@ namespace llvm {
   };
 }
 
-/// AddNoCaptureAttrs - Deduce nocapture attributes for the SCC.
-bool FunctionAttrs::AddNoCaptureAttrs(const CallGraphSCC &SCC) {
+// Returns Attribute::None, Attribute::ReadOnly or Attribute::ReadNone.
+static Attribute::AttrKind
+determinePointerReadAttrs(Argument *A,
+                          const SmallPtrSet<Argument*, 8> &SCCNodes) {
+                                                       
+  SmallVector<Use*, 32> Worklist;
+  SmallSet<Use*, 32> Visited;
+  int Count = 0;
+
+  bool IsRead = false;
+  // We don't need to track IsWritten. If A is written to, return immediately.
+
+  for (Value::use_iterator UI = A->use_begin(), UE = A->use_end();
+       UI != UE; ++UI) {
+    if (Count++ >= 20)
+      return Attribute::None;
+
+    Use *U = &UI.getUse();
+    Visited.insert(U);
+    Worklist.push_back(U);
+  }
+
+  while (!Worklist.empty()) {
+    Use *U = Worklist.pop_back_val();
+    Instruction *I = cast<Instruction>(U->getUser());
+    Value *V = U->get();
+
+    switch (I->getOpcode()) {
+    case Instruction::BitCast:
+    case Instruction::GetElementPtr:
+    case Instruction::PHI:
+    case Instruction::Select:
+      // The original value is not read/written via this if the new value isn't.
+      for (Instruction::use_iterator UI = I->use_begin(), UE = I->use_end();
+           UI != UE; ++UI) {
+        Use *U = &UI.getUse();
+        if (Visited.insert(U))
+          Worklist.push_back(U);
+      }
+      break;
+
+    case Instruction::Call:
+    case Instruction::Invoke: {
+      CallSite CS(I);
+      if (CS.doesNotAccessMemory())
+        continue;
+
+      Function *F = CS.getCalledFunction();
+      if (!F) {
+        if (CS.onlyReadsMemory()) {
+          IsRead = true;
+          continue;
+        }
+        return Attribute::None;
+      }
+
+      Function::arg_iterator AI = F->arg_begin(), AE = F->arg_end();
+      CallSite::arg_iterator B = CS.arg_begin(), E = CS.arg_end();
+      for (CallSite::arg_iterator A = B; A != E; ++A, ++AI) {
+        if (A->get() == V) {
+          if (AI == AE) {
+            assert(F->isVarArg() &&
+                   "More params than args in non-varargs call.");
+            return Attribute::None;
+          }
+          if (SCCNodes.count(AI))
+            continue;
+          if (!CS.onlyReadsMemory() && !CS.onlyReadsMemory(A - B))
+            return Attribute::None;
+          if (!CS.doesNotAccessMemory(A - B))
+            IsRead = true;
+        }
+      }
+      break;
+    }
+
+    case Instruction::Load:
+      IsRead = true;
+      break;
+
+    case Instruction::ICmp:
+    case Instruction::Ret:
+      break;
+
+    default:
+      return Attribute::None;
+    }
+  }
+
+  return IsRead ? Attribute::ReadOnly : Attribute::ReadNone;
+}
+
+/// AddArgumentAttrs - Deduce nocapture attributes for the SCC.
+bool FunctionAttrs::AddArgumentAttrs(const CallGraphSCC &SCC) {
   bool Changed = false;
 
   SmallPtrSet<Function*, 8> SCCNodes;
@@ -442,8 +543,11 @@ bool FunctionAttrs::AddNoCaptureAttrs(co
       continue;
     }
 
-    for (Function::arg_iterator A = F->arg_begin(), E = F->arg_end(); A!=E; ++A)
-      if (A->getType()->isPointerTy() && !A->hasNoCaptureAttr()) {
+    for (Function::arg_iterator A = F->arg_begin(), E = F->arg_end();
+         A != E; ++A) {
+      if (!A->getType()->isPointerTy()) continue;
+      bool HasNonLocalUses = false;
+      if (!A->hasNoCaptureAttr()) {
         ArgumentUsesTracker Tracker(SCCNodes);
         PointerMayBeCaptured(A, &Tracker);
         if (!Tracker.Captured) {
@@ -458,12 +562,32 @@ bool FunctionAttrs::AddNoCaptureAttrs(co
             // its particulars for Argument-SCC analysis later.
             ArgumentGraphNode *Node = AG[A];
             for (SmallVectorImpl<Argument*>::iterator UI = Tracker.Uses.begin(),
-                   UE = Tracker.Uses.end(); UI != UE; ++UI)
+                     UE = Tracker.Uses.end(); UI != UE; ++UI) {
               Node->Uses.push_back(AG[*UI]);
+              if (*UI != A)
+                HasNonLocalUses = true;
+            }
           }
         }
         // Otherwise, it's captured. Don't bother doing SCC analysis on it.
       }
+      if (!HasNonLocalUses && !A->onlyReadsMemory()) {
+        // Can we determine that it's readonly/readnone without doing an SCC?
+        // Note that we don't allow any calls at all here, or else our result
+        // will be dependent on the iteration order through the functions in the
+        // SCC.
+        SmallPtrSet<Argument*, 8> Self;
+        Self.insert(A);
+        Attribute::AttrKind R = determinePointerReadAttrs(A, Self);
+        if (R != Attribute::None) {
+          AttrBuilder B;
+          B.addAttribute(R);
+          A->addAttr(AttributeSet::get(A->getContext(), A->getArgNo() + 1, B));
+          Changed = true;
+          R == Attribute::ReadOnly ? ++NumReadOnlyArg : ++NumReadNoneArg;
+        }
+      }
+    }
   }
 
   // The graph we've collected is partial because we stopped scanning for
@@ -482,11 +606,8 @@ bool FunctionAttrs::AddNoCaptureAttrs(co
       // eg. "void f(int* x) { if (...) f(x); }"
       if (ArgumentSCC[0]->Uses.size() == 1 &&
           ArgumentSCC[0]->Uses[0] == ArgumentSCC[0]) {
-        ArgumentSCC[0]->
-          Definition->
-          addAttr(AttributeSet::get(ArgumentSCC[0]->Definition->getContext(),
-                                    ArgumentSCC[0]->Definition->getArgNo() + 1,
-                                    B));
+        Argument *A = ArgumentSCC[0]->Definition;
+        A->addAttr(AttributeSet::get(A->getContext(), A->getArgNo() + 1, B));
         ++NumNoCapture;
         Changed = true;
       }
@@ -532,6 +653,42 @@ bool FunctionAttrs::AddNoCaptureAttrs(co
       ++NumNoCapture;
       Changed = true;
     }
+
+    // We also want to compute readonly/readnone. With a small number of false
+    // negatives, we can assume that any pointer which is captured isn't going
+    // to be provably readonly or readnone, since by definition we can't
+    // analyze all uses of a captured pointer.
+    //
+    // The false negatives happen when the pointer is captured by a function
+    // that promises readonly/readnone behaviour on the pointer, then the
+    // pointer's lifetime ends before anything that writes to arbitrary memory.
+    // Also, a readonly/readnone pointer may be returned, but returning a
+    // pointer is capturing it.
+
+    Attribute::AttrKind ReadAttr = Attribute::ReadNone;
+    for (unsigned i = 0, e = ArgumentSCC.size(); i != e; ++i) {
+      Argument *A = ArgumentSCC[i]->Definition;
+      Attribute::AttrKind K = determinePointerReadAttrs(A, ArgumentSCCNodes);
+      if (K == Attribute::ReadNone)
+        continue;
+      if (K == Attribute::ReadOnly) {
+        ReadAttr = Attribute::ReadOnly;
+        continue;
+      }
+      ReadAttr = K;
+      break;
+    }
+
+    if (ReadAttr != Attribute::None) {
+      AttrBuilder B;
+      B.addAttribute(ReadAttr);
+      for (unsigned i = 0, e = ArgumentSCC.size(); i != e; ++i) {
+        Argument *A = ArgumentSCC[i]->Definition;
+        A->addAttr(AttributeSet::get(A->getContext(), A->getArgNo() + 1, B));
+        ReadAttr == Attribute::ReadOnly ? ++NumReadOnlyArg : ++NumReadNoneArg;
+        Changed = true;
+      }
+    }
   }
 
   return Changed;
@@ -678,24 +835,32 @@ bool FunctionAttrs::inferPrototypeAttrib
     setOnlyReadsMemory(F);
     setDoesNotThrow(F);
     break;
-  case LibFunc::strcpy:
-  case LibFunc::stpcpy:
-  case LibFunc::strcat:
   case LibFunc::strtol:
   case LibFunc::strtod:
   case LibFunc::strtof:
   case LibFunc::strtoul:
   case LibFunc::strtoll:
   case LibFunc::strtold:
+  case LibFunc::strtoull:
+    if (FTy->getNumParams() < 2 ||
+        !FTy->getParamType(1)->isPointerTy())
+      return false;
+    setDoesNotThrow(F);
+    setDoesNotCapture(F, 2);
+    setOnlyReadsMemory(F, 1);
+    break;
+  case LibFunc::strcpy:
+  case LibFunc::stpcpy:
+  case LibFunc::strcat:
   case LibFunc::strncat:
   case LibFunc::strncpy:
   case LibFunc::stpncpy:
-  case LibFunc::strtoull:
     if (FTy->getNumParams() < 2 ||
         !FTy->getParamType(1)->isPointerTy())
       return false;
     setDoesNotThrow(F);
     setDoesNotCapture(F, 2);
+    setOnlyReadsMemory(F, 2);
     break;
   case LibFunc::strxfrm:
     if (FTy->getNumParams() != 3 ||
@@ -705,14 +870,15 @@ bool FunctionAttrs::inferPrototypeAttrib
     setDoesNotThrow(F);
     setDoesNotCapture(F, 1);
     setDoesNotCapture(F, 2);
+    setOnlyReadsMemory(F, 2);
     break;
-  case LibFunc::strcmp:
-  case LibFunc::strspn:
-  case LibFunc::strncmp:
-  case LibFunc::strcspn:
-  case LibFunc::strcoll:
-  case LibFunc::strcasecmp:
-  case LibFunc::strncasecmp:
+  case LibFunc::strcmp: //0,1
+    case LibFunc::strspn: // 0,1
+    case LibFunc::strncmp: // 0,1
+    case LibFunc::strcspn: //0,1
+    case LibFunc::strcoll: //0,1
+    case LibFunc::strcasecmp:  // 0,1
+    case LibFunc::strncasecmp: // 
     if (FTy->getNumParams() < 2 ||
         !FTy->getParamType(0)->isPointerTy() ||
         !FTy->getParamType(1)->isPointerTy())
@@ -736,8 +902,15 @@ bool FunctionAttrs::inferPrototypeAttrib
       return false;
     setDoesNotThrow(F);
     setDoesNotCapture(F, 2);
+    setOnlyReadsMemory(F, 2);
     break;
   case LibFunc::scanf:
+    if (FTy->getNumParams() < 1 || !FTy->getParamType(0)->isPointerTy())
+      return false;
+    setDoesNotThrow(F);
+    setDoesNotCapture(F, 1);
+    setOnlyReadsMemory(F, 1);
+    break;
   case LibFunc::setbuf:
   case LibFunc::setvbuf:
     if (FTy->getNumParams() < 1 || !FTy->getParamType(0)->isPointerTy())
@@ -753,11 +926,31 @@ bool FunctionAttrs::inferPrototypeAttrib
     setDoesNotThrow(F);
     setDoesNotAlias(F, 0);
     setDoesNotCapture(F, 1);
+    setOnlyReadsMemory(F, 1);
     break;
   case LibFunc::stat:
+  case LibFunc::statvfs:
+    if (FTy->getNumParams() < 2 ||
+        !FTy->getParamType(0)->isPointerTy() ||
+        !FTy->getParamType(1)->isPointerTy())
+      return false;
+    setDoesNotThrow(F);
+    setDoesNotCapture(F, 1);
+    setDoesNotCapture(F, 2);
+    setOnlyReadsMemory(F, 1);
+    break;
   case LibFunc::sscanf:
+    if (FTy->getNumParams() < 2 ||
+        !FTy->getParamType(0)->isPointerTy() ||
+        !FTy->getParamType(1)->isPointerTy())
+      return false;
+    setDoesNotThrow(F);
+    setDoesNotCapture(F, 1);
+    setDoesNotCapture(F, 2);
+    setOnlyReadsMemory(F, 1);
+    setOnlyReadsMemory(F, 2);
+    break;
   case LibFunc::sprintf:
-  case LibFunc::statvfs:
     if (FTy->getNumParams() < 2 ||
         !FTy->getParamType(0)->isPointerTy() ||
         !FTy->getParamType(1)->isPointerTy())
@@ -765,6 +958,7 @@ bool FunctionAttrs::inferPrototypeAttrib
     setDoesNotThrow(F);
     setDoesNotCapture(F, 1);
     setDoesNotCapture(F, 2);
+    setOnlyReadsMemory(F, 2);
     break;
   case LibFunc::snprintf:
     if (FTy->getNumParams() != 3 ||
@@ -774,6 +968,7 @@ bool FunctionAttrs::inferPrototypeAttrib
     setDoesNotThrow(F);
     setDoesNotCapture(F, 1);
     setDoesNotCapture(F, 3);
+    setOnlyReadsMemory(F, 3);
     break;
   case LibFunc::setitimer:
     if (FTy->getNumParams() != 3 ||
@@ -783,6 +978,7 @@ bool FunctionAttrs::inferPrototypeAttrib
     setDoesNotThrow(F);
     setDoesNotCapture(F, 2);
     setDoesNotCapture(F, 3);
+    setOnlyReadsMemory(F, 2);
     break;
   case LibFunc::system:
     if (FTy->getNumParams() != 1 ||
@@ -790,6 +986,7 @@ bool FunctionAttrs::inferPrototypeAttrib
       return false;
     // May throw; "system" is a valid pthread cancellation point.
     setDoesNotCapture(F, 1);
+    setOnlyReadsMemory(F, 1);
     break;
   case LibFunc::malloc:
     if (FTy->getNumParams() != 1 ||
@@ -818,6 +1015,12 @@ bool FunctionAttrs::inferPrototypeAttrib
   case LibFunc::modf:
   case LibFunc::modff:
   case LibFunc::modfl:
+    if (FTy->getNumParams() < 2 ||
+        !FTy->getParamType(1)->isPointerTy())
+      return false;
+    setDoesNotThrow(F);
+    setDoesNotCapture(F, 2);
+    break;
   case LibFunc::memcpy:
   case LibFunc::memccpy:
   case LibFunc::memmove:
@@ -826,6 +1029,7 @@ bool FunctionAttrs::inferPrototypeAttrib
       return false;
     setDoesNotThrow(F);
     setDoesNotCapture(F, 2);
+    setOnlyReadsMemory(F, 2);
     break;
   case LibFunc::memalign:
     if (!FTy->getReturnType()->isPointerTy())
@@ -833,6 +1037,13 @@ bool FunctionAttrs::inferPrototypeAttrib
     setDoesNotAlias(F, 0);
     break;
   case LibFunc::mkdir:
+    if (FTy->getNumParams() == 0 ||
+        !FTy->getParamType(0)->isPointerTy())
+      return false;
+    setDoesNotThrow(F);
+    setDoesNotCapture(F, 1);
+    setOnlyReadsMemory(F, 1);
+    break;
   case LibFunc::mktime:
     if (FTy->getNumParams() == 0 ||
         !FTy->getParamType(0)->isPointerTy())
@@ -856,8 +1067,14 @@ bool FunctionAttrs::inferPrototypeAttrib
     // May throw; "read" is a valid pthread cancellation point.
     setDoesNotCapture(F, 2);
     break;
-  case LibFunc::rmdir:
   case LibFunc::rewind:
+    if (FTy->getNumParams() < 1 ||
+        !FTy->getParamType(0)->isPointerTy())
+      return false;
+    setDoesNotThrow(F);
+    setDoesNotCapture(F, 1);
+    break;
+  case LibFunc::rmdir:
   case LibFunc::remove:
   case LibFunc::realpath:
     if (FTy->getNumParams() < 1 ||
@@ -865,8 +1082,19 @@ bool FunctionAttrs::inferPrototypeAttrib
       return false;
     setDoesNotThrow(F);
     setDoesNotCapture(F, 1);
+    setOnlyReadsMemory(F, 1);
     break;
   case LibFunc::rename:
+    if (FTy->getNumParams() < 2 ||
+        !FTy->getParamType(0)->isPointerTy() ||
+        !FTy->getParamType(1)->isPointerTy())
+      return false;
+    setDoesNotThrow(F);
+    setDoesNotCapture(F, 1);
+    setDoesNotCapture(F, 2);
+    setOnlyReadsMemory(F, 1);
+    setOnlyReadsMemory(F, 2);
+    break;
   case LibFunc::readlink:
     if (FTy->getNumParams() < 2 ||
         !FTy->getParamType(0)->isPointerTy() ||
@@ -875,12 +1103,14 @@ bool FunctionAttrs::inferPrototypeAttrib
     setDoesNotThrow(F);
     setDoesNotCapture(F, 1);
     setDoesNotCapture(F, 2);
+    setOnlyReadsMemory(F, 1);
     break;
   case LibFunc::write:
     if (FTy->getNumParams() != 3 || !FTy->getParamType(1)->isPointerTy())
       return false;
     // May throw; "write" is a valid pthread cancellation point.
     setDoesNotCapture(F, 2);
+    setOnlyReadsMemory(F, 2);
     break;
   case LibFunc::bcopy:
     if (FTy->getNumParams() != 3 ||
@@ -890,6 +1120,7 @@ bool FunctionAttrs::inferPrototypeAttrib
     setDoesNotThrow(F);
     setDoesNotCapture(F, 1);
     setDoesNotCapture(F, 2);
+    setOnlyReadsMemory(F, 1);
     break;
   case LibFunc::bcmp:
     if (FTy->getNumParams() != 3 ||
@@ -916,6 +1147,12 @@ bool FunctionAttrs::inferPrototypeAttrib
     break;
   case LibFunc::chmod:
   case LibFunc::chown:
+    if (FTy->getNumParams() == 0 || !FTy->getParamType(0)->isPointerTy())
+      return false;
+    setDoesNotThrow(F);
+    setDoesNotCapture(F, 1);
+    setOnlyReadsMemory(F, 1);
+    break;
   case LibFunc::ctermid:
   case LibFunc::clearerr:
   case LibFunc::closedir:
@@ -939,6 +1176,7 @@ bool FunctionAttrs::inferPrototypeAttrib
       return false;
     setDoesNotThrow(F);
     setDoesNotCapture(F, 1);
+    setOnlyReadsMemory(F, 1);
     break;
   case LibFunc::fopen:
     if (FTy->getNumParams() != 2 ||
@@ -950,6 +1188,8 @@ bool FunctionAttrs::inferPrototypeAttrib
     setDoesNotAlias(F, 0);
     setDoesNotCapture(F, 1);
     setDoesNotCapture(F, 2);
+    setOnlyReadsMemory(F, 1);
+    setOnlyReadsMemory(F, 2);
     break;
   case LibFunc::fdopen:
     if (FTy->getNumParams() != 2 ||
@@ -959,6 +1199,7 @@ bool FunctionAttrs::inferPrototypeAttrib
     setDoesNotThrow(F);
     setDoesNotAlias(F, 0);
     setDoesNotCapture(F, 2);
+    setOnlyReadsMemory(F, 2);
     break;
   case LibFunc::feof:
   case LibFunc::free:
@@ -1006,6 +1247,14 @@ bool FunctionAttrs::inferPrototypeAttrib
     setDoesNotCapture(F, 3);
     break;
   case LibFunc::fread:
+    if (FTy->getNumParams() != 4 ||
+        !FTy->getParamType(0)->isPointerTy() ||
+        !FTy->getParamType(3)->isPointerTy())
+      return false;
+    setDoesNotThrow(F);
+    setDoesNotCapture(F, 1);
+    setDoesNotCapture(F, 4);
+    break;
   case LibFunc::fwrite:
     if (FTy->getNumParams() != 4 ||
         !FTy->getParamType(0)->isPointerTy() ||
@@ -1016,8 +1265,26 @@ bool FunctionAttrs::inferPrototypeAttrib
     setDoesNotCapture(F, 4);
     break;
   case LibFunc::fputs:
+    if (FTy->getNumParams() < 2 ||
+        !FTy->getParamType(0)->isPointerTy() ||
+        !FTy->getParamType(1)->isPointerTy())
+      return false;
+    setDoesNotThrow(F);
+    setDoesNotCapture(F, 1);
+    setDoesNotCapture(F, 2);
+    setOnlyReadsMemory(F, 1);
+    break;
   case LibFunc::fscanf:
   case LibFunc::fprintf:
+    if (FTy->getNumParams() < 2 ||
+        !FTy->getParamType(0)->isPointerTy() ||
+        !FTy->getParamType(1)->isPointerTy())
+      return false;
+    setDoesNotThrow(F);
+    setDoesNotCapture(F, 1);
+    setDoesNotCapture(F, 2);
+    setOnlyReadsMemory(F, 2);
+    break;
   case LibFunc::fgetpos:
     if (FTy->getNumParams() < 2 ||
         !FTy->getParamType(0)->isPointerTy() ||
@@ -1057,6 +1324,7 @@ bool FunctionAttrs::inferPrototypeAttrib
       return false;
     setDoesNotThrow(F);
     setDoesNotCapture(F, 1);
+    setOnlyReadsMemory(F, 1);
     break;
   case LibFunc::ungetc:
     if (FTy->getNumParams() != 2 || !FTy->getParamType(1)->isPointerTy())
@@ -1065,12 +1333,24 @@ bool FunctionAttrs::inferPrototypeAttrib
     setDoesNotCapture(F, 2);
     break;
   case LibFunc::uname:
+    if (FTy->getNumParams() != 1 || !FTy->getParamType(0)->isPointerTy())
+      return false;
+    setDoesNotThrow(F);
+    setDoesNotCapture(F, 1);
+    break;
   case LibFunc::unlink:
+    if (FTy->getNumParams() != 1 || !FTy->getParamType(0)->isPointerTy())
+      return false;
+    setDoesNotThrow(F);
+    setDoesNotCapture(F, 1);
+    setOnlyReadsMemory(F, 2);
+    break;
   case LibFunc::unsetenv:
     if (FTy->getNumParams() != 1 || !FTy->getParamType(0)->isPointerTy())
       return false;
     setDoesNotThrow(F);
     setDoesNotCapture(F, 1);
+    setOnlyReadsMemory(F, 1);
     break;
   case LibFunc::utime:
   case LibFunc::utimes:
@@ -1081,6 +1361,8 @@ bool FunctionAttrs::inferPrototypeAttrib
     setDoesNotThrow(F);
     setDoesNotCapture(F, 1);
     setDoesNotCapture(F, 2);
+    setOnlyReadsMemory(F, 1);
+    setOnlyReadsMemory(F, 2);
     break;
   case LibFunc::putc:
     if (FTy->getNumParams() != 2 || !FTy->getParamType(1)->isPointerTy())
@@ -1095,13 +1377,20 @@ bool FunctionAttrs::inferPrototypeAttrib
       return false;
     setDoesNotThrow(F);
     setDoesNotCapture(F, 1);
+    setOnlyReadsMemory(F, 1);
     break;
   case LibFunc::pread:
+    if (FTy->getNumParams() != 4 || !FTy->getParamType(1)->isPointerTy())
+      return false;
+    // May throw; "pread" is a valid pthread cancellation point.
+    setDoesNotCapture(F, 2);
+    break;
   case LibFunc::pwrite:
     if (FTy->getNumParams() != 4 || !FTy->getParamType(1)->isPointerTy())
       return false;
-    // May throw; these are valid pthread cancellation points.
+    // May throw; "pwrite" is a valid pthread cancellation point.
     setDoesNotCapture(F, 2);
+    setOnlyReadsMemory(F, 2);
     break;
   case LibFunc::putchar:
     setDoesNotThrow(F);
@@ -1116,6 +1405,8 @@ bool FunctionAttrs::inferPrototypeAttrib
     setDoesNotAlias(F, 0);
     setDoesNotCapture(F, 1);
     setDoesNotCapture(F, 2);
+    setOnlyReadsMemory(F, 1);
+    setOnlyReadsMemory(F, 2);
     break;
   case LibFunc::pclose:
     if (FTy->getNumParams() != 1 || !FTy->getParamType(0)->isPointerTy())
@@ -1128,8 +1419,19 @@ bool FunctionAttrs::inferPrototypeAttrib
       return false;
     setDoesNotThrow(F);
     setDoesNotCapture(F, 1);
+    setOnlyReadsMemory(F, 1);
     break;
   case LibFunc::vsscanf:
+    if (FTy->getNumParams() != 3 ||
+        !FTy->getParamType(1)->isPointerTy() ||
+        !FTy->getParamType(2)->isPointerTy())
+      return false;
+    setDoesNotThrow(F);
+    setDoesNotCapture(F, 1);
+    setDoesNotCapture(F, 2);
+    setOnlyReadsMemory(F, 1);
+    setOnlyReadsMemory(F, 2);
+    break;
   case LibFunc::vfscanf:
     if (FTy->getNumParams() != 3 ||
         !FTy->getParamType(1)->isPointerTy() ||
@@ -1138,6 +1440,7 @@ bool FunctionAttrs::inferPrototypeAttrib
     setDoesNotThrow(F);
     setDoesNotCapture(F, 1);
     setDoesNotCapture(F, 2);
+    setOnlyReadsMemory(F, 2);
     break;
   case LibFunc::valloc:
     if (!FTy->getReturnType()->isPointerTy())
@@ -1150,6 +1453,7 @@ bool FunctionAttrs::inferPrototypeAttrib
       return false;
     setDoesNotThrow(F);
     setDoesNotCapture(F, 1);
+    setOnlyReadsMemory(F, 1);
     break;
   case LibFunc::vfprintf:
   case LibFunc::vsprintf:
@@ -1160,6 +1464,7 @@ bool FunctionAttrs::inferPrototypeAttrib
     setDoesNotThrow(F);
     setDoesNotCapture(F, 1);
     setDoesNotCapture(F, 2);
+    setOnlyReadsMemory(F, 2);
     break;
   case LibFunc::vsnprintf:
     if (FTy->getNumParams() != 4 ||
@@ -1169,12 +1474,14 @@ bool FunctionAttrs::inferPrototypeAttrib
     setDoesNotThrow(F);
     setDoesNotCapture(F, 1);
     setDoesNotCapture(F, 3);
+    setOnlyReadsMemory(F, 3);
     break;
   case LibFunc::open:
     if (FTy->getNumParams() < 2 || !FTy->getParamType(0)->isPointerTy())
       return false;
     // May throw; "open" is a valid pthread cancellation point.
     setDoesNotCapture(F, 1);
+    setOnlyReadsMemory(F, 1);
     break;
   case LibFunc::opendir:
     if (FTy->getNumParams() != 1 ||
@@ -1184,6 +1491,7 @@ bool FunctionAttrs::inferPrototypeAttrib
     setDoesNotThrow(F);
     setDoesNotAlias(F, 0);
     setDoesNotCapture(F, 1);
+    setOnlyReadsMemory(F, 1);
     break;
   case LibFunc::tmpfile:
     if (!FTy->getReturnType()->isPointerTy())
@@ -1212,12 +1520,14 @@ bool FunctionAttrs::inferPrototypeAttrib
     setDoesNotThrow(F);
     setDoesNotCapture(F, 1);
     setDoesNotCapture(F, 2);
+    setOnlyReadsMemory(F, 1);
     break;
   case LibFunc::lchown:
     if (FTy->getNumParams() != 3 || !FTy->getParamType(0)->isPointerTy())
       return false;
     setDoesNotThrow(F);
     setDoesNotCapture(F, 1);
+    setOnlyReadsMemory(F, 1);
     break;
   case LibFunc::qsort:
     if (FTy->getNumParams() != 4 || !FTy->getParamType(3)->isPointerTy())
@@ -1234,6 +1544,7 @@ bool FunctionAttrs::inferPrototypeAttrib
     setDoesNotThrow(F);
     setDoesNotAlias(F, 0);
     setDoesNotCapture(F, 1);
+    setOnlyReadsMemory(F, 1);
     break;
   case LibFunc::dunder_strtok_r:
     if (FTy->getNumParams() != 3 ||
@@ -1241,6 +1552,7 @@ bool FunctionAttrs::inferPrototypeAttrib
       return false;
     setDoesNotThrow(F);
     setDoesNotCapture(F, 2);
+    setOnlyReadsMemory(F, 2);
     break;
   case LibFunc::under_IO_getc:
     if (FTy->getNumParams() != 1 || !FTy->getParamType(0)->isPointerTy())
@@ -1260,10 +1572,20 @@ bool FunctionAttrs::inferPrototypeAttrib
       return false;
     setDoesNotThrow(F);
     setDoesNotCapture(F, 1);
+    setOnlyReadsMemory(F, 1);
     break;
   case LibFunc::stat64:
   case LibFunc::lstat64:
   case LibFunc::statvfs64:
+    if (FTy->getNumParams() < 1 ||
+        !FTy->getParamType(0)->isPointerTy() ||
+        !FTy->getParamType(1)->isPointerTy())
+      return false;
+    setDoesNotThrow(F);
+    setDoesNotCapture(F, 1);
+    setDoesNotCapture(F, 2);
+    setOnlyReadsMemory(F, 1);
+    break;
   case LibFunc::dunder_isoc99_sscanf:
     if (FTy->getNumParams() < 1 ||
         !FTy->getParamType(0)->isPointerTy() ||
@@ -1272,6 +1594,8 @@ bool FunctionAttrs::inferPrototypeAttrib
     setDoesNotThrow(F);
     setDoesNotCapture(F, 1);
     setDoesNotCapture(F, 2);
+    setOnlyReadsMemory(F, 1);
+    setOnlyReadsMemory(F, 2);
     break;
   case LibFunc::fopen64:
     if (FTy->getNumParams() != 2 ||
@@ -1283,6 +1607,8 @@ bool FunctionAttrs::inferPrototypeAttrib
     setDoesNotAlias(F, 0);
     setDoesNotCapture(F, 1);
     setDoesNotCapture(F, 2);
+    setOnlyReadsMemory(F, 1);
+    setOnlyReadsMemory(F, 2);
     break;
   case LibFunc::fseeko64:
   case LibFunc::ftello64:
@@ -1309,6 +1635,7 @@ bool FunctionAttrs::inferPrototypeAttrib
       return false;
     // May throw; "open" is a valid pthread cancellation point.
     setDoesNotCapture(F, 1);
+    setOnlyReadsMemory(F, 1);
     break;
   case LibFunc::gettimeofday:
     if (FTy->getNumParams() != 2 || !FTy->getParamType(0)->isPointerTy() ||
@@ -1351,7 +1678,7 @@ bool FunctionAttrs::runOnSCC(CallGraphSC
 
   bool Changed = annotateLibraryCalls(SCC);
   Changed |= AddReadAttrs(SCC);
-  Changed |= AddNoCaptureAttrs(SCC);
+  Changed |= AddArgumentAttrs(SCC);
   Changed |= AddNoAliasAttrs(SCC);
   return Changed;
 }

Modified: llvm/trunk/test/Analysis/TypeBasedAliasAnalysis/functionattrs.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Analysis/TypeBasedAliasAnalysis/functionattrs.ll?rev=185735&r1=185734&r2=185735&view=diff
==============================================================================
--- llvm/trunk/test/Analysis/TypeBasedAliasAnalysis/functionattrs.ll (original)
+++ llvm/trunk/test/Analysis/TypeBasedAliasAnalysis/functionattrs.ll Fri Jul  5 19:29:58 2013
@@ -49,7 +49,7 @@ define void @test2_yes(i8* %p, i8* %q, i
   ret void
 }
 
-; CHECK: define void @test2_no(i8* nocapture %p, i8* nocapture %q, i64 %n) #1 {
+; CHECK: define void @test2_no(i8* nocapture %p, i8* nocapture readonly %q, i64 %n) #1 {
 define void @test2_no(i8* %p, i8* %q, i64 %n) nounwind {
   call void @llvm.memcpy.p0i8.p0i8.i64(i8* %p, i8* %q, i64 %n, i32 1, i1 false), !tbaa !2
   ret void

Modified: llvm/trunk/test/Transforms/FunctionAttrs/2009-01-02-LocalStores.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Transforms/FunctionAttrs/2009-01-02-LocalStores.ll?rev=185735&r1=185734&r2=185735&view=diff
==============================================================================
--- llvm/trunk/test/Transforms/FunctionAttrs/2009-01-02-LocalStores.ll (original)
+++ llvm/trunk/test/Transforms/FunctionAttrs/2009-01-02-LocalStores.ll Fri Jul  5 19:29:58 2013
@@ -1,14 +1,23 @@
-; RUN: opt < %s -functionattrs -S | not grep "nocapture *%%q"
-; RUN: opt < %s -functionattrs -S | grep "nocapture *%%p"
+; RUN: opt < %s -functionattrs -S | FileCheck %s
 
+; CHECK: define i32* @a(i32** nocapture readonly %p)
 define i32* @a(i32** %p) {
 	%tmp = load i32** %p
 	ret i32* %tmp
 }
 
+; CHECK: define i32* @b(i32* %q)
 define i32* @b(i32 *%q) {
 	%mem = alloca i32*
 	store i32* %q, i32** %mem
 	%tmp = call i32* @a(i32** %mem)
 	ret i32* %tmp
 }
+
+; CHECK: define i32* @c(i32* readnone %r)
+ at g = global i32 0
+define i32* @c(i32 *%r) {
+	%a = icmp eq i32* %r, null
+	store i32 1, i32* @g
+	ret i32* %r
+}

Modified: llvm/trunk/test/Transforms/FunctionAttrs/2009-01-04-Annotate.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Transforms/FunctionAttrs/2009-01-04-Annotate.ll?rev=185735&r1=185734&r2=185735&view=diff
==============================================================================
--- llvm/trunk/test/Transforms/FunctionAttrs/2009-01-04-Annotate.ll (original)
+++ llvm/trunk/test/Transforms/FunctionAttrs/2009-01-04-Annotate.ll Fri Jul  5 19:29:58 2013
@@ -1,6 +1,6 @@
 ; RUN: opt < %s -functionattrs -S | FileCheck %s
 
-; CHECK: declare noalias i8* @fopen(i8* nocapture, i8* nocapture) #0
+; CHECK: declare noalias i8* @fopen(i8* nocapture readonly, i8* nocapture readonly) #0
 declare i8* @fopen(i8*, i8*)
 
 ; CHECK: declare i8 @strlen(i8* nocapture) #1

Modified: llvm/trunk/test/Transforms/FunctionAttrs/annotate-1.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Transforms/FunctionAttrs/annotate-1.ll?rev=185735&r1=185734&r2=185735&view=diff
==============================================================================
--- llvm/trunk/test/Transforms/FunctionAttrs/annotate-1.ll (original)
+++ llvm/trunk/test/Transforms/FunctionAttrs/annotate-1.ll Fri Jul  5 19:29:58 2013
@@ -2,7 +2,7 @@
 ; RUN: opt < %s -mtriple=x86_64-apple-macosx10.8.0 -functionattrs -S | FileCheck -check-prefix=POSIX %s
 
 declare i8* @fopen(i8*, i8*)
-; CHECK: declare noalias i8* @fopen(i8* nocapture, i8* nocapture) [[G0:#[0-9]]] 
+; CHECK: declare noalias i8* @fopen(i8* nocapture readonly, i8* nocapture readonly) [[G0:#[0-9]]] 
 
 declare i8 @strlen(i8*)
 ; CHECK: declare i8 @strlen(i8* nocapture) [[G1:#[0-9]]]

Modified: llvm/trunk/test/Transforms/FunctionAttrs/atomic.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Transforms/FunctionAttrs/atomic.ll?rev=185735&r1=185734&r2=185735&view=diff
==============================================================================
--- llvm/trunk/test/Transforms/FunctionAttrs/atomic.ll (original)
+++ llvm/trunk/test/Transforms/FunctionAttrs/atomic.ll Fri Jul  5 19:29:58 2013
@@ -13,7 +13,7 @@ entry:
 
 ; A function with an Acquire load is not readonly.
 define i32 @test2(i32* %x) uwtable ssp {
-; CHECK: define i32 @test2(i32* nocapture %x) #1 {
+; CHECK: define i32 @test2(i32* nocapture readonly %x) #1 {
 entry:
   %r = load atomic i32* %x seq_cst, align 4
   ret i32 %r

Modified: llvm/trunk/test/Transforms/FunctionAttrs/nocapture.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Transforms/FunctionAttrs/nocapture.ll?rev=185735&r1=185734&r2=185735&view=diff
==============================================================================
--- llvm/trunk/test/Transforms/FunctionAttrs/nocapture.ll (original)
+++ llvm/trunk/test/Transforms/FunctionAttrs/nocapture.ll Fri Jul  5 19:29:58 2013
@@ -1,12 +1,13 @@
 ; RUN: opt < %s -functionattrs -S | FileCheck %s
 @g = global i32* null		; <i32**> [#uses=1]
 
-; CHECK: define i32* @c1(i32* %q)
+; CHECK: define i32* @c1(i32* readnone %q)
 define i32* @c1(i32* %q) {
 	ret i32* %q
 }
 
 ; CHECK: define void @c2(i32* %q)
+; It would also be acceptable to mark %q as readnone. Update @c3 too.
 define void @c2(i32* %q) {
 	store i32* %q, i32** @g
 	ret void
@@ -45,7 +46,7 @@ define i1 @c5(i32* %q, i32 %bitno) {
 
 declare void @throw_if_bit_set(i8*, i8) readonly
 
-; CHECK: define i1 @c6(i8* %q, i8 %bit)
+; CHECK: define i1 @c6(i8* readonly %q, i8 %bit)
 define i1 @c6(i8* %q, i8 %bit) {
 	invoke void @throw_if_bit_set(i8* %q, i8 %bit)
 		to label %ret0 unwind label %ret1
@@ -67,7 +68,7 @@ define i1* @lookup_bit(i32* %q, i32 %bit
 	ret i1* %lookup
 }
 
-; CHECK: define i1 @c7(i32* %q, i32 %bitno)
+; CHECK: define i1 @c7(i32* readnone %q, i32 %bitno)
 define i1 @c7(i32* %q, i32 %bitno) {
 	%ptr = call i1* @lookup_bit(i32* %q, i32 %bitno)
 	%val = load i1* %ptr
@@ -103,7 +104,7 @@ define void @nc3(void ()* %p) {
 }
 
 declare void @external(i8*) readonly nounwind
-; CHECK: define void @nc4(i8* nocapture %p)
+; CHECK: define void @nc4(i8* nocapture readonly %p)
 define void @nc4(i8* %p) {
 	call void @external(i8* %p)
 	ret void
@@ -116,28 +117,29 @@ define void @nc5(void (i8*)* %f, i8* %p)
 	ret void
 }
 
-; CHECK: define void @test1_1(i8* nocapture %x1_1, i8* %y1_1)
+; CHECK: define void @test1_1(i8* nocapture readnone %x1_1, i8* %y1_1)
+; It would be acceptable to add readnone to %y1_1 and %y1_2.
 define void @test1_1(i8* %x1_1, i8* %y1_1) {
   call i8* @test1_2(i8* %x1_1, i8* %y1_1)
   store i32* null, i32** @g
   ret void
 }
 
-; CHECK: define i8* @test1_2(i8* nocapture %x1_2, i8* %y1_2)
+; CHECK: define i8* @test1_2(i8* nocapture readnone %x1_2, i8* %y1_2)
 define i8* @test1_2(i8* %x1_2, i8* %y1_2) {
   call void @test1_1(i8* %x1_2, i8* %y1_2)
   store i32* null, i32** @g
   ret i8* %y1_2
 }
 
-; CHECK: define void @test2(i8* nocapture %x2)
+; CHECK: define void @test2(i8* nocapture readnone %x2)
 define void @test2(i8* %x2) {
   call void @test2(i8* %x2)
   store i32* null, i32** @g
   ret void
 }
 
-; CHECK: define void @test3(i8* nocapture %x3, i8* nocapture %y3, i8* nocapture %z3)
+; CHECK: define void @test3(i8* nocapture readnone %x3, i8* nocapture readnone %y3, i8* nocapture readnone %z3)
 define void @test3(i8* %x3, i8* %y3, i8* %z3) {
   call void @test3(i8* %z3, i8* %y3, i8* %x3)
   store i32* null, i32** @g
@@ -151,7 +153,7 @@ define void @test4_1(i8* %x4_1) {
   ret void
 }
 
-; CHECK: define i8* @test4_2(i8* nocapture %x4_2, i8* %y4_2, i8* nocapture %z4_2)
+; CHECK: define i8* @test4_2(i8* nocapture readnone %x4_2, i8* readnone %y4_2, i8* nocapture readnone %z4_2)
 define i8* @test4_2(i8* %x4_2, i8* %y4_2, i8* %z4_2) {
   call void @test4_1(i8* null)
   store i32* null, i32** @g

Added: llvm/trunk/test/Transforms/FunctionAttrs/readattrs.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Transforms/FunctionAttrs/readattrs.ll?rev=185735&view=auto
==============================================================================
--- llvm/trunk/test/Transforms/FunctionAttrs/readattrs.ll (added)
+++ llvm/trunk/test/Transforms/FunctionAttrs/readattrs.ll Fri Jul  5 19:29:58 2013
@@ -0,0 +1,47 @@
+; RUN: opt < %s -functionattrs -S | FileCheck %s
+ at x = global i32 0
+
+declare void @test1_1(i8* %x1_1, i8* readonly %y1_1, ...)
+
+; CHECK: define void @test1_2(i8* %x1_2, i8* readonly %y1_2, i8* %z1_2)
+define void @test1_2(i8* %x1_2, i8* %y1_2, i8* %z1_2) {
+  call void (i8*, i8*, ...)* @test1_1(i8* %x1_2, i8* %y1_2, i8* %z1_2)
+  store i32 0, i32* @x
+  ret void
+}
+
+; CHECK: define i8* @test2(i8* readnone %p)
+define i8* @test2(i8* %p) {
+  store i32 0, i32* @x
+  ret i8* %p
+}
+
+; CHECK: define i1 @test3(i8* readnone %p, i8* readnone %q)
+define i1 @test3(i8* %p, i8* %q) {
+  %A = icmp ult i8* %p, %q
+  ret i1 %A
+}
+
+declare void @test4_1(i8* nocapture) readonly
+
+; CHECK: define void @test4_2(i8* nocapture readonly %p)
+define void @test4_2(i8* %p) {
+  call void @test4_1(i8* %p)
+  ret void
+}
+
+; CHECK: define void @test5(i8** nocapture %p, i8* %q)
+; Missed optz'n: we could make %q readnone, but don't break test6!
+define void @test5(i8** %p, i8* %q) {
+  store i8* %q, i8** %p
+  ret void
+}
+
+declare void @test6_1()
+; CHECK: define void @test6_2(i8** nocapture %p, i8* %q)
+; This is not a missed optz'n.
+define void @test6_2(i8** %p, i8* %q) {
+  store i8* %q, i8** %p
+  call void @test6_1()
+  ret void
+}

Modified: llvm/trunk/test/Transforms/InstCombine/strto-1.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Transforms/InstCombine/strto-1.ll?rev=185735&r1=185734&r2=185735&view=diff
==============================================================================
--- llvm/trunk/test/Transforms/InstCombine/strto-1.ll (original)
+++ llvm/trunk/test/Transforms/InstCombine/strto-1.ll Fri Jul  5 19:29:58 2013
@@ -5,25 +5,25 @@
 target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:128:128"
 
 declare i64 @strtol(i8* %s, i8** %endptr, i32 %base)
-; CHECK: declare i64 @strtol(i8*, i8** nocapture, i32)
+; CHECK: declare i64 @strtol(i8* readonly, i8** nocapture, i32)
 
 declare double @strtod(i8* %s, i8** %endptr, i32 %base)
-; CHECK: declare double @strtod(i8*, i8** nocapture, i32)
+; CHECK: declare double @strtod(i8* readonly, i8** nocapture, i32)
 
 declare float @strtof(i8* %s, i8** %endptr, i32 %base)
-; CHECK: declare float @strtof(i8*, i8** nocapture, i32)
+; CHECK: declare float @strtof(i8* readonly, i8** nocapture, i32)
 
 declare i64 @strtoul(i8* %s, i8** %endptr, i32 %base)
-; CHECK: declare i64 @strtoul(i8*, i8** nocapture, i32)
+; CHECK: declare i64 @strtoul(i8* readonly, i8** nocapture, i32)
 
 declare i64 @strtoll(i8* %s, i8** %endptr, i32 %base)
-; CHECK: declare i64 @strtoll(i8*, i8** nocapture, i32)
+; CHECK: declare i64 @strtoll(i8* readonly, i8** nocapture, i32)
 
 declare double @strtold(i8* %s, i8** %endptr)
-; CHECK: declare double @strtold(i8*, i8** nocapture)
+; CHECK: declare double @strtold(i8* readonly, i8** nocapture)
 
 declare i64 @strtoull(i8* %s, i8** %endptr, i32 %base)
-; CHECK: declare i64 @strtoull(i8*, i8** nocapture, i32)
+; CHECK: declare i64 @strtoull(i8* readonly, i8** nocapture, i32)
 
 define void @test_simplify1(i8* %x, i8** %endptr) {
 ; CHECK: @test_simplify1

Modified: llvm/trunk/utils/TableGen/CodeGenIntrinsics.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/utils/TableGen/CodeGenIntrinsics.h?rev=185735&r1=185734&r2=185735&view=diff
==============================================================================
--- llvm/trunk/utils/TableGen/CodeGenIntrinsics.h (original)
+++ llvm/trunk/utils/TableGen/CodeGenIntrinsics.h Fri Jul  5 19:29:58 2013
@@ -77,7 +77,9 @@ namespace llvm {
     bool isNoReturn;
 
     enum ArgAttribute {
-      NoCapture
+      NoCapture,
+      ReadOnly,
+      ReadNone
     };
     std::vector<std::pair<unsigned, ArgAttribute> > ArgumentAttributes;
 

Modified: llvm/trunk/utils/TableGen/CodeGenTarget.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/utils/TableGen/CodeGenTarget.cpp?rev=185735&r1=185734&r2=185735&view=diff
==============================================================================
--- llvm/trunk/utils/TableGen/CodeGenTarget.cpp (original)
+++ llvm/trunk/utils/TableGen/CodeGenTarget.cpp Fri Jul  5 19:29:58 2013
@@ -552,6 +552,12 @@ CodeGenIntrinsic::CodeGenIntrinsic(Recor
     else if (Property->isSubClassOf("NoCapture")) {
       unsigned ArgNo = Property->getValueAsInt("ArgNo");
       ArgumentAttributes.push_back(std::make_pair(ArgNo, NoCapture));
+    } else if (Property->isSubClassOf("ReadOnly")) {
+      unsigned ArgNo = Property->getValueAsInt("ArgNo");
+      ArgumentAttributes.push_back(std::make_pair(ArgNo, ReadOnly));
+    } else if (Property->isSubClassOf("ReadNone")) {
+      unsigned ArgNo = Property->getValueAsInt("ArgNo");
+      ArgumentAttributes.push_back(std::make_pair(ArgNo, ReadNone));
     } else
       llvm_unreachable("Unknown property!");
   }

Modified: llvm/trunk/utils/TableGen/IntrinsicEmitter.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/utils/TableGen/IntrinsicEmitter.cpp?rev=185735&r1=185734&r2=185735&view=diff
==============================================================================
--- llvm/trunk/utils/TableGen/IntrinsicEmitter.cpp (original)
+++ llvm/trunk/utils/TableGen/IntrinsicEmitter.cpp Fri Jul  5 19:29:58 2013
@@ -579,6 +579,12 @@ EmitAttributes(const std::vector<CodeGen
           case CodeGenIntrinsic::NoCapture:
             OS << "      AttrVec.push_back(Attribute::NoCapture);\n";
             break;
+          case CodeGenIntrinsic::ReadOnly:
+            OS << "      AttrVec.push_back(Attribute::ReadOnly);\n";
+            break;
+          case CodeGenIntrinsic::ReadNone:
+            OS << "      AttrVec.push_back(Attribute::ReadNone);\n";
+            break;
           }
 
           ++ai;





More information about the llvm-commits mailing list