diff options
-rw-r--r-- | include/llvm/LinkAllPasses.h | 1 | ||||
-rw-r--r-- | include/llvm/Transforms/IPO.h | 6 | ||||
-rw-r--r-- | lib/Transforms/IPO/AddReadAttrs.cpp | 135 | ||||
-rw-r--r-- | test/Transforms/AddReadAttrs/2008-09-03-Mutual.ll (renamed from test/Analysis/GlobalsModRef/2008-09-03-Mutual.ll) | 2 | ||||
-rw-r--r-- | test/Transforms/AddReadAttrs/2008-09-03-ReadNone.ll (renamed from test/Analysis/GlobalsModRef/2008-09-03-ReadNone.ll) | 2 | ||||
-rw-r--r-- | test/Transforms/AddReadAttrs/2008-09-03-ReadOnly.ll (renamed from test/Analysis/GlobalsModRef/2008-09-03-ReadOnly.ll) | 2 | ||||
-rw-r--r-- | test/Transforms/AddReadAttrs/2008-09-13-VolatileRead.ll (renamed from test/Analysis/GlobalsModRef/2008-09-13-VolatileRead.ll) | 2 |
7 files changed, 146 insertions, 4 deletions
diff --git a/include/llvm/LinkAllPasses.h b/include/llvm/LinkAllPasses.h index f4385ddcee..2e1887b937 100644 --- a/include/llvm/LinkAllPasses.h +++ b/include/llvm/LinkAllPasses.h @@ -119,6 +119,7 @@ namespace { (void) llvm::createPostDomFrontier(); (void) llvm::createInstructionNamerPass(); (void) llvm::createPartialSpecializationPass(); + (void) llvm::createAddReadAttrsPass(); (void)new llvm::IntervalPartition(); (void)new llvm::FindUsedTypes(); diff --git a/include/llvm/Transforms/IPO.h b/include/llvm/Transforms/IPO.h index ad354988c9..ac8da776a2 100644 --- a/include/llvm/Transforms/IPO.h +++ b/include/llvm/Transforms/IPO.h @@ -188,6 +188,12 @@ ModulePass *createStripDeadPrototypesPass(); /// ModulePass* createPartialSpecializationPass(); +//===----------------------------------------------------------------------===// +/// createAddReadAttrsPass - This pass discovers functions that do not access +/// memory, or only read memory, and gives them the readnone/readonly attribute. +/// +Pass* createAddReadAttrsPass(); + } // End llvm namespace #endif diff --git a/lib/Transforms/IPO/AddReadAttrs.cpp b/lib/Transforms/IPO/AddReadAttrs.cpp new file mode 100644 index 0000000000..1052e06b72 --- /dev/null +++ b/lib/Transforms/IPO/AddReadAttrs.cpp @@ -0,0 +1,135 @@ +//===- AddReadAttrs.cpp - Pass which marks functions readnone or readonly -===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// 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. It implements +// this as a bottom-up traversal of the call-graph. +// +//===----------------------------------------------------------------------===// + +#define DEBUG_TYPE "addreadattrs" +#include "llvm/Transforms/IPO.h" +#include "llvm/CallGraphSCCPass.h" +#include "llvm/Instructions.h" +#include "llvm/Analysis/CallGraph.h" +#include "llvm/ADT/Statistic.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/InstIterator.h" +using namespace llvm; + +STATISTIC(NumReadNone, "Number of functions marked readnone"); +STATISTIC(NumReadOnly, "Number of functions marked readonly"); + +namespace { + struct VISIBILITY_HIDDEN AddReadAttrs : public CallGraphSCCPass { + static char ID; // Pass identification, replacement for typeid + AddReadAttrs() : CallGraphSCCPass(&ID) {} + + // runOnSCC - Analyze the SCC, performing the transformation if possible. + bool runOnSCC(const std::vector<CallGraphNode *> &SCC); + }; +} + +char AddReadAttrs::ID = 0; +static RegisterPass<AddReadAttrs> +X("addreadattrs", "Mark functions readnone/readonly"); + +Pass *llvm::createAddReadAttrsPass() { return new AddReadAttrs(); } + + +bool AddReadAttrs::runOnSCC(const std::vector<CallGraphNode *> &SCC) { + CallGraph &CG = getAnalysis<CallGraph>(); + + // Check if any of the functions in the SCC read or write memory. + // If they write memory then just give up. + bool ReadsMemory = false; + for (unsigned i = 0, e = SCC.size(); i != e; ++i) { + Function *F = SCC[i]->getFunction(); + + if (F == 0) + // May write memory. + return false; + + if (F->doesNotAccessMemory()) + // Already perfect! + continue; + + // Definitions with weak linkage may be overridden at linktime with + // something that writes memory, so treat them like declarations. + if (F->isDeclaration() || F->hasWeakLinkage()) { + if (!F->onlyReadsMemory()) + // May write memory. + return false; + + ReadsMemory = true; + continue; + } + + // Scan the function body for explicit loads and stores, or calls to + // functions that may read or write memory. + for (inst_iterator II = inst_begin(F), E = inst_end(F); II != E; ++II) { + Instruction *I = &*II; + if (LoadInst *LI = dyn_cast<LoadInst>(I)) { + if (LI->isVolatile()) + // Volatile loads may have side-effects, so treat them as writing + // memory. + return false; + ReadsMemory = true; + } else if (isa<StoreInst>(I) || isa<MallocInst>(I) || isa<FreeInst>(I)) { + // Writes memory. + return false; + } else if (isa<CallInst>(I) || isa<InvokeInst>(I)) { + CallSite CS(I); + + if (std::find(SCC.begin(), SCC.end(), CG[CS.getCalledFunction()]) != + SCC.end()) + // The callee is inside our current SCC - ignore it. + continue; + + if (!CS.onlyReadsMemory()) + // May write memory. + return false; + + if (!CS.doesNotAccessMemory()) + ReadsMemory = true; + } + } + } + + // Success! Functions in this SCC do not access memory, or only read memory. + // Give them the appropriate attribute. + bool MadeChange = false; + for (unsigned i = 0, e = SCC.size(); i != e; ++i) { + Function *F = SCC[i]->getFunction(); + + if (F->doesNotAccessMemory()) + // Already perfect! + continue; + + if (F->onlyReadsMemory() && ReadsMemory) + // No change. + continue; + + MadeChange = true; + + // Clear out any existing attributes. + F->removeParamAttr(0, ParamAttr::ReadOnly | ParamAttr::ReadNone); + + // Add in the new attribute. + F->addParamAttr(0, ReadsMemory ? ParamAttr::ReadOnly : ParamAttr::ReadNone); + + if (ReadsMemory) + NumReadOnly++; + else + NumReadNone++; + } + + return MadeChange; +} diff --git a/test/Analysis/GlobalsModRef/2008-09-03-Mutual.ll b/test/Transforms/AddReadAttrs/2008-09-03-Mutual.ll index f6f47f9472..0a4f0855ca 100644 --- a/test/Analysis/GlobalsModRef/2008-09-03-Mutual.ll +++ b/test/Transforms/AddReadAttrs/2008-09-03-Mutual.ll @@ -1,4 +1,4 @@ -; RUN: llvm-as < %s | opt -globalsmodref-aa -markmodref | llvm-dis | grep readnone +; RUN: llvm-as < %s | opt -addreadattrs | llvm-dis | grep readnone define i32 @a() { %tmp = call i32 @b( ) ; <i32> [#uses=1] diff --git a/test/Analysis/GlobalsModRef/2008-09-03-ReadNone.ll b/test/Transforms/AddReadAttrs/2008-09-03-ReadNone.ll index b286ada839..3271902004 100644 --- a/test/Analysis/GlobalsModRef/2008-09-03-ReadNone.ll +++ b/test/Transforms/AddReadAttrs/2008-09-03-ReadNone.ll @@ -1,4 +1,4 @@ -; RUN: llvm-as < %s | opt -globalsmodref-aa -markmodref | llvm-dis | grep readnone | count 2 +; RUN: llvm-as < %s | opt -addreadattrs | llvm-dis | grep readnone | count 2 define i32 @f() { entry: diff --git a/test/Analysis/GlobalsModRef/2008-09-03-ReadOnly.ll b/test/Transforms/AddReadAttrs/2008-09-03-ReadOnly.ll index de1666f515..c08e7b1265 100644 --- a/test/Analysis/GlobalsModRef/2008-09-03-ReadOnly.ll +++ b/test/Transforms/AddReadAttrs/2008-09-03-ReadOnly.ll @@ -1,4 +1,4 @@ -; RUN: llvm-as < %s | opt -globalsmodref-aa -markmodref | llvm-dis | grep readonly | count 2 +; RUN: llvm-as < %s | opt -addreadattrs | llvm-dis | grep readonly | count 2 define i32 @f() { entry: diff --git a/test/Analysis/GlobalsModRef/2008-09-13-VolatileRead.ll b/test/Transforms/AddReadAttrs/2008-09-13-VolatileRead.ll index 8ac9637a86..0690083ae7 100644 --- a/test/Analysis/GlobalsModRef/2008-09-13-VolatileRead.ll +++ b/test/Transforms/AddReadAttrs/2008-09-13-VolatileRead.ll @@ -1,4 +1,4 @@ -; RUN: llvm-as < %s | opt -globalsmodref-aa -markmodref | llvm-dis | not grep read +; RUN: llvm-as < %s | opt -addreadattrs | llvm-dis | not grep read ; PR2792 @g = global i32 0 ; <i32*> [#uses=1] |