diff options
| author | Nick Lewycky <nicholas@mxc.ca> | 2013-07-06 00:29:58 +0000 |
|---|---|---|
| committer | Nick Lewycky <nicholas@mxc.ca> | 2013-07-06 00:29:58 +0000 |
| commit | dc89737bcdbb8f69d8ae7578bdfa904cabcfc5ed (patch) | |
| tree | 1838b5d8368383a083fad1cdca2fe777528e5a69 /lib | |
| parent | 202eb7b18e220205ec86a03ddf18f2066c70ab15 (diff) | |
| download | external_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
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/Analysis/CaptureTracking.cpp | 8 | ||||
| -rw-r--r-- | lib/AsmParser/LLParser.cpp | 10 | ||||
| -rw-r--r-- | lib/IR/Attributes.cpp | 2 | ||||
| -rw-r--r-- | lib/IR/Function.cpp | 10 | ||||
| -rw-r--r-- | lib/IR/Verifier.cpp | 21 | ||||
| -rw-r--r-- | lib/Transforms/IPO/FunctionAttrs.cpp | 401 |
6 files changed, 398 insertions, 54 deletions
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; } |
