aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNick Lewycky <nicholas@mxc.ca>2013-07-06 00:29:58 +0000
committerNick Lewycky <nicholas@mxc.ca>2013-07-06 00:29:58 +0000
commitdc89737bcdbb8f69d8ae7578bdfa904cabcfc5ed (patch)
tree1838b5d8368383a083fad1cdca2fe777528e5a69
parent202eb7b18e220205ec86a03ddf18f2066c70ab15 (diff)
downloadexternal_llvm-dc89737bcdbb8f69d8ae7578bdfa904cabcfc5ed.zip
external_llvm-dc89737bcdbb8f69d8ae7578bdfa904cabcfc5ed.tar.gz
external_llvm-dc89737bcdbb8f69d8ae7578bdfa904cabcfc5ed.tar.bz2
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. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@185735 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--docs/LangRef.rst17
-rw-r--r--include/llvm/IR/Argument.h5
-rw-r--r--include/llvm/IR/Function.h8
-rw-r--r--include/llvm/IR/Intrinsics.td18
-rw-r--r--include/llvm/Support/CallSite.h9
-rw-r--r--lib/Analysis/CaptureTracking.cpp8
-rw-r--r--lib/AsmParser/LLParser.cpp10
-rw-r--r--lib/IR/Attributes.cpp2
-rw-r--r--lib/IR/Function.cpp10
-rw-r--r--lib/IR/Verifier.cpp21
-rw-r--r--lib/Transforms/IPO/FunctionAttrs.cpp401
-rw-r--r--test/Analysis/TypeBasedAliasAnalysis/functionattrs.ll2
-rw-r--r--test/Transforms/FunctionAttrs/2009-01-02-LocalStores.ll13
-rw-r--r--test/Transforms/FunctionAttrs/2009-01-04-Annotate.ll2
-rw-r--r--test/Transforms/FunctionAttrs/annotate-1.ll2
-rw-r--r--test/Transforms/FunctionAttrs/atomic.ll2
-rw-r--r--test/Transforms/FunctionAttrs/nocapture.ll20
-rw-r--r--test/Transforms/FunctionAttrs/readattrs.ll47
-rw-r--r--test/Transforms/InstCombine/strto-1.ll14
-rw-r--r--utils/TableGen/CodeGenIntrinsics.h4
-rw-r--r--utils/TableGen/CodeGenTarget.cpp6
-rw-r--r--utils/TableGen/IntrinsicEmitter.cpp6
22 files changed, 544 insertions, 83 deletions
diff --git a/docs/LangRef.rst b/docs/LangRef.rst
index 13706e1..1e65f0b 100644
--- a/docs/LangRef.rst
+++ b/docs/LangRef.rst
@@ -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
diff --git a/include/llvm/IR/Argument.h b/include/llvm/IR/Argument.h
index 40d61ff..eb6ed46 100644
--- a/include/llvm/IR/Argument.h
+++ b/include/llvm/IR/Argument.h
@@ -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);
diff --git a/include/llvm/IR/Function.h b/include/llvm/IR/Function.h
index 0645539..cfb862d 100644
--- a/include/llvm/IR/Function.h
+++ b/include/llvm/IR/Function.h
@@ -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);
diff --git a/include/llvm/IR/Intrinsics.td b/include/llvm/IR/Intrinsics.td
index 6530187..e102382 100644
--- a/include/llvm/IR/Intrinsics.td
+++ b/include/llvm/IR/Intrinsics.td
@@ -55,6 +55,18 @@ class NoCapture<int argNo> : IntrinsicProperty {
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<[], [llvm_ptr_ty, llvm_ptrptr_ty], []>;
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],
diff --git a/include/llvm/Support/CallSite.h b/include/llvm/Support/CallSite.h
index 961c38e..2a1c5ca 100644
--- a/include/llvm/Support/CallSite.h
+++ b/include/llvm/Support/CallSite.h
@@ -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 {
diff --git a/lib/Analysis/CaptureTracking.cpp b/lib/Analysis/CaptureTracking.cpp
index a729270..9eb76a8 100644
--- a/lib/Analysis/CaptureTracking.cpp
+++ b/lib/Analysis/CaptureTracking.cpp
@@ -158,10 +158,10 @@ void llvm::PointerMayBeCaptured(const Value *V, CaptureTracker *Tracker) {
// 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.
diff --git a/lib/AsmParser/LLParser.cpp b/lib/AsmParser/LLParser.cpp
index 9349007..941a371 100644
--- a/lib/AsmParser/LLParser.cpp
+++ b/lib/AsmParser/LLParser.cpp
@@ -1159,6 +1159,8 @@ bool LLParser::ParseOptionalParamAttrs(AttrBuilder &B) {
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(AttrBuilder &B) {
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(AttrBuilder &B) {
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(AttrBuilder &B) {
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();
diff --git a/lib/IR/Attributes.cpp b/lib/IR/Attributes.cpp
index 2160ea2..59da815 100644
--- a/lib/IR/Attributes.cpp
+++ b/lib/IR/Attributes.cpp
@@ -1157,6 +1157,8 @@ AttributeSet AttributeFuncs::typeIncompatible(Type *Ty, uint64_t Index) {
.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);
diff --git a/lib/IR/Function.cpp b/lib/IR/Function.cpp
index 7f7efabf..bf9d949 100644
--- a/lib/IR/Function.cpp
+++ b/lib/IR/Function.cpp
@@ -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::callsFunctionThatReturnsTwice() const {
return false;
}
-
diff --git a/lib/IR/Verifier.cpp b/lib/IR/Verifier.cpp
index 8b4c165..420bc15 100644
--- a/lib/IR/Verifier.cpp
+++ b/lib/IR/Verifier.cpp
@@ -654,7 +654,7 @@ void Verifier::visitModuleFlag(MDNode *Op, DenseMap<MDString*, MDNode*>&SeenIDs,
}
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(AttributeSet Attrs, unsigned Idx,
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(AttributeSet Attrs, unsigned Idx,
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;
}
}
}
diff --git a/lib/Transforms/IPO/FunctionAttrs.cpp b/lib/Transforms/IPO/FunctionAttrs.cpp
index cd5842a..8f46bd4 100644
--- a/lib/Transforms/IPO/FunctionAttrs.cpp
+++ b/lib/Transforms/IPO/FunctionAttrs.cpp
@@ -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(const CallGraphSCC &SCC) {
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(const CallGraphSCC &SCC) {
// 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(const CallGraphSCC &SCC) {
// 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(const CallGraphSCC &SCC) {
++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::inferPrototypeAttributes(Function &F) {
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::inferPrototypeAttributes(Function &F) {
setDoesNotThrow(F);
setDoesNotCapture(F, 1);
setDoesNotCapture(F, 2);
- break;
- case LibFunc::strcmp:
- case LibFunc::strspn:
- case LibFunc::strncmp:
- case LibFunc::strcspn:
- case LibFunc::strcoll:
- case LibFunc::strcasecmp:
- case LibFunc::strncasecmp:
+ setOnlyReadsMemory(F, 2);
+ break;
+ 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::inferPrototypeAttributes(Function &F) {
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::inferPrototypeAttributes(Function &F) {
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::inferPrototypeAttributes(Function &F) {
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::inferPrototypeAttributes(Function &F) {
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::inferPrototypeAttributes(Function &F) {
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::inferPrototypeAttributes(Function &F) {
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::inferPrototypeAttributes(Function &F) {
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::inferPrototypeAttributes(Function &F) {
return false;
setDoesNotThrow(F);
setDoesNotCapture(F, 2);
+ setOnlyReadsMemory(F, 2);
break;
case LibFunc::memalign:
if (!FTy->getReturnType()->isPointerTy())
@@ -833,6 +1037,13 @@ bool FunctionAttrs::inferPrototypeAttributes(Function &F) {
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::inferPrototypeAttributes(Function &F) {
// 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::inferPrototypeAttributes(Function &F) {
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::inferPrototypeAttributes(Function &F) {
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::inferPrototypeAttributes(Function &F) {
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::inferPrototypeAttributes(Function &F) {
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::inferPrototypeAttributes(Function &F) {
return false;
setDoesNotThrow(F);
setDoesNotCapture(F, 1);
+ setOnlyReadsMemory(F, 1);
break;
case LibFunc::fopen:
if (FTy->getNumParams() != 2 ||
@@ -950,6 +1188,8 @@ bool FunctionAttrs::inferPrototypeAttributes(Function &F) {
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::inferPrototypeAttributes(Function &F) {
setDoesNotThrow(F);
setDoesNotAlias(F, 0);
setDoesNotCapture(F, 2);
+ setOnlyReadsMemory(F, 2);
break;
case LibFunc::feof:
case LibFunc::free:
@@ -1006,6 +1247,14 @@ bool FunctionAttrs::inferPrototypeAttributes(Function &F) {
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::inferPrototypeAttributes(Function &F) {
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::inferPrototypeAttributes(Function &F) {
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::inferPrototypeAttributes(Function &F) {
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::inferPrototypeAttributes(Function &F) {
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::inferPrototypeAttributes(Function &F) {
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::inferPrototypeAttributes(Function &F) {
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::inferPrototypeAttributes(Function &F) {
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::inferPrototypeAttributes(Function &F) {
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::inferPrototypeAttributes(Function &F) {
return false;
setDoesNotThrow(F);
setDoesNotCapture(F, 1);
+ setOnlyReadsMemory(F, 1);
break;
case LibFunc::vfprintf:
case LibFunc::vsprintf:
@@ -1160,6 +1464,7 @@ bool FunctionAttrs::inferPrototypeAttributes(Function &F) {
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::inferPrototypeAttributes(Function &F) {
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::inferPrototypeAttributes(Function &F) {
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::inferPrototypeAttributes(Function &F) {
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::inferPrototypeAttributes(Function &F) {
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::inferPrototypeAttributes(Function &F) {
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::inferPrototypeAttributes(Function &F) {
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::inferPrototypeAttributes(Function &F) {
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::inferPrototypeAttributes(Function &F) {
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::inferPrototypeAttributes(Function &F) {
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(CallGraphSCC &SCC) {
bool Changed = annotateLibraryCalls(SCC);
Changed |= AddReadAttrs(SCC);
- Changed |= AddNoCaptureAttrs(SCC);
+ Changed |= AddArgumentAttrs(SCC);
Changed |= AddNoAliasAttrs(SCC);
return Changed;
}
diff --git a/test/Analysis/TypeBasedAliasAnalysis/functionattrs.ll b/test/Analysis/TypeBasedAliasAnalysis/functionattrs.ll
index c6cc26a..0a30b30 100644
--- a/test/Analysis/TypeBasedAliasAnalysis/functionattrs.ll
+++ b/test/Analysis/TypeBasedAliasAnalysis/functionattrs.ll
@@ -49,7 +49,7 @@ define void @test2_yes(i8* %p, i8* %q, i64 %n) nounwind {
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
diff --git a/test/Transforms/FunctionAttrs/2009-01-02-LocalStores.ll b/test/Transforms/FunctionAttrs/2009-01-02-LocalStores.ll
index f38c03ac..0cf1cb7 100644
--- a/test/Transforms/FunctionAttrs/2009-01-02-LocalStores.ll
+++ b/test/Transforms/FunctionAttrs/2009-01-02-LocalStores.ll
@@ -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)
+@g = global i32 0
+define i32* @c(i32 *%r) {
+ %a = icmp eq i32* %r, null
+ store i32 1, i32* @g
+ ret i32* %r
+}
diff --git a/test/Transforms/FunctionAttrs/2009-01-04-Annotate.ll b/test/Transforms/FunctionAttrs/2009-01-04-Annotate.ll
index d414b73..fa06cc7 100644
--- a/test/Transforms/FunctionAttrs/2009-01-04-Annotate.ll
+++ b/test/Transforms/FunctionAttrs/2009-01-04-Annotate.ll
@@ -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
diff --git a/test/Transforms/FunctionAttrs/annotate-1.ll b/test/Transforms/FunctionAttrs/annotate-1.ll
index ca78212..adb7bce 100644
--- a/test/Transforms/FunctionAttrs/annotate-1.ll
+++ b/test/Transforms/FunctionAttrs/annotate-1.ll
@@ -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]]]
diff --git a/test/Transforms/FunctionAttrs/atomic.ll b/test/Transforms/FunctionAttrs/atomic.ll
index 027ee0f..d5a8db7 100644
--- a/test/Transforms/FunctionAttrs/atomic.ll
+++ b/test/Transforms/FunctionAttrs/atomic.ll
@@ -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
diff --git a/test/Transforms/FunctionAttrs/nocapture.ll b/test/Transforms/FunctionAttrs/nocapture.ll
index 3027acd..110bd03 100644
--- a/test/Transforms/FunctionAttrs/nocapture.ll
+++ b/test/Transforms/FunctionAttrs/nocapture.ll
@@ -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 %bitno) readnone nounwind {
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
diff --git a/test/Transforms/FunctionAttrs/readattrs.ll b/test/Transforms/FunctionAttrs/readattrs.ll
new file mode 100644
index 0000000..0842f56
--- /dev/null
+++ b/test/Transforms/FunctionAttrs/readattrs.ll
@@ -0,0 +1,47 @@
+; RUN: opt < %s -functionattrs -S | FileCheck %s
+@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
+}
diff --git a/test/Transforms/InstCombine/strto-1.ll b/test/Transforms/InstCombine/strto-1.ll
index 7139972..dfd772f 100644
--- a/test/Transforms/InstCombine/strto-1.ll
+++ b/test/Transforms/InstCombine/strto-1.ll
@@ -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
diff --git a/utils/TableGen/CodeGenIntrinsics.h b/utils/TableGen/CodeGenIntrinsics.h
index f0570f9..ababfa4 100644
--- a/utils/TableGen/CodeGenIntrinsics.h
+++ b/utils/TableGen/CodeGenIntrinsics.h
@@ -77,7 +77,9 @@ namespace llvm {
bool isNoReturn;
enum ArgAttribute {
- NoCapture
+ NoCapture,
+ ReadOnly,
+ ReadNone
};
std::vector<std::pair<unsigned, ArgAttribute> > ArgumentAttributes;
diff --git a/utils/TableGen/CodeGenTarget.cpp b/utils/TableGen/CodeGenTarget.cpp
index 8b292b9..a85ad09 100644
--- a/utils/TableGen/CodeGenTarget.cpp
+++ b/utils/TableGen/CodeGenTarget.cpp
@@ -552,6 +552,12 @@ CodeGenIntrinsic::CodeGenIntrinsic(Record *R) {
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!");
}
diff --git a/utils/TableGen/IntrinsicEmitter.cpp b/utils/TableGen/IntrinsicEmitter.cpp
index df4d847..c83797c 100644
--- a/utils/TableGen/IntrinsicEmitter.cpp
+++ b/utils/TableGen/IntrinsicEmitter.cpp
@@ -579,6 +579,12 @@ EmitAttributes(const std::vector<CodeGenIntrinsic> &Ints, raw_ostream &OS) {
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;