From: Duncan Sands Date: Fri, 19 Sep 2008 08:17:05 +0000 (+0000) Subject: Add a new pass AddReadAttrs which works out which functions X-Git-Url: http://plrg.eecs.uci.edu/git/?a=commitdiff_plain;h=9f07a290b92a8ff06dfb9f3c28d6aa082217d9a6;p=oota-llvm.git Add a new pass AddReadAttrs which works out which functions can get the readnone/readonly attributes, and gives them it. The plan is to remove markmodref (which did the same thing by querying GlobalsModRef) and delete the analogous functionality from GlobalsModRef. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@56341 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/llvm/LinkAllPasses.h b/include/llvm/LinkAllPasses.h index f4385ddcee9..2e1887b9377 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 ad354988c91..ac8da776a2f 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 00000000000..1052e06b72f --- /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 &SCC); + }; +} + +char AddReadAttrs::ID = 0; +static RegisterPass +X("addreadattrs", "Mark functions readnone/readonly"); + +Pass *llvm::createAddReadAttrsPass() { return new AddReadAttrs(); } + + +bool AddReadAttrs::runOnSCC(const std::vector &SCC) { + CallGraph &CG = getAnalysis(); + + // 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(I)) { + if (LI->isVolatile()) + // Volatile loads may have side-effects, so treat them as writing + // memory. + return false; + ReadsMemory = true; + } else if (isa(I) || isa(I) || isa(I)) { + // Writes memory. + return false; + } else if (isa(I) || isa(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/Analysis/GlobalsModRef/2008-09-03-Mutual.ll deleted file mode 100644 index f6f47f94727..00000000000 --- a/test/Analysis/GlobalsModRef/2008-09-03-Mutual.ll +++ /dev/null @@ -1,11 +0,0 @@ -; RUN: llvm-as < %s | opt -globalsmodref-aa -markmodref | llvm-dis | grep readnone - -define i32 @a() { - %tmp = call i32 @b( ) ; [#uses=1] - ret i32 %tmp -} - -define i32 @b() { - %tmp = call i32 @a( ) ; [#uses=1] - ret i32 %tmp -} diff --git a/test/Analysis/GlobalsModRef/2008-09-03-ReadNone.ll b/test/Analysis/GlobalsModRef/2008-09-03-ReadNone.ll deleted file mode 100644 index b286ada8390..00000000000 --- a/test/Analysis/GlobalsModRef/2008-09-03-ReadNone.ll +++ /dev/null @@ -1,9 +0,0 @@ -; RUN: llvm-as < %s | opt -globalsmodref-aa -markmodref | llvm-dis | grep readnone | count 2 - -define i32 @f() { -entry: - %tmp = call i32 @e( ) ; [#uses=1] - ret i32 %tmp -} - -declare i32 @e() readnone diff --git a/test/Analysis/GlobalsModRef/2008-09-03-ReadOnly.ll b/test/Analysis/GlobalsModRef/2008-09-03-ReadOnly.ll deleted file mode 100644 index de1666f5152..00000000000 --- a/test/Analysis/GlobalsModRef/2008-09-03-ReadOnly.ll +++ /dev/null @@ -1,9 +0,0 @@ -; RUN: llvm-as < %s | opt -globalsmodref-aa -markmodref | llvm-dis | grep readonly | count 2 - -define i32 @f() { -entry: - %tmp = call i32 @e( ) ; [#uses=1] - ret i32 %tmp -} - -declare i32 @e() readonly diff --git a/test/Analysis/GlobalsModRef/2008-09-13-VolatileRead.ll b/test/Analysis/GlobalsModRef/2008-09-13-VolatileRead.ll deleted file mode 100644 index 8ac9637a86b..00000000000 --- a/test/Analysis/GlobalsModRef/2008-09-13-VolatileRead.ll +++ /dev/null @@ -1,9 +0,0 @@ -; RUN: llvm-as < %s | opt -globalsmodref-aa -markmodref | llvm-dis | not grep read -; PR2792 - -@g = global i32 0 ; [#uses=1] - -define i32 @f() { - %t = volatile load i32* @g ; [#uses=1] - ret i32 %t -} diff --git a/test/Transforms/AddReadAttrs/2008-09-03-Mutual.ll b/test/Transforms/AddReadAttrs/2008-09-03-Mutual.ll new file mode 100644 index 00000000000..0a4f0855ca4 --- /dev/null +++ b/test/Transforms/AddReadAttrs/2008-09-03-Mutual.ll @@ -0,0 +1,11 @@ +; RUN: llvm-as < %s | opt -addreadattrs | llvm-dis | grep readnone + +define i32 @a() { + %tmp = call i32 @b( ) ; [#uses=1] + ret i32 %tmp +} + +define i32 @b() { + %tmp = call i32 @a( ) ; [#uses=1] + ret i32 %tmp +} diff --git a/test/Transforms/AddReadAttrs/2008-09-03-ReadNone.ll b/test/Transforms/AddReadAttrs/2008-09-03-ReadNone.ll new file mode 100644 index 00000000000..32719020040 --- /dev/null +++ b/test/Transforms/AddReadAttrs/2008-09-03-ReadNone.ll @@ -0,0 +1,9 @@ +; RUN: llvm-as < %s | opt -addreadattrs | llvm-dis | grep readnone | count 2 + +define i32 @f() { +entry: + %tmp = call i32 @e( ) ; [#uses=1] + ret i32 %tmp +} + +declare i32 @e() readnone diff --git a/test/Transforms/AddReadAttrs/2008-09-03-ReadOnly.ll b/test/Transforms/AddReadAttrs/2008-09-03-ReadOnly.ll new file mode 100644 index 00000000000..c08e7b12653 --- /dev/null +++ b/test/Transforms/AddReadAttrs/2008-09-03-ReadOnly.ll @@ -0,0 +1,9 @@ +; RUN: llvm-as < %s | opt -addreadattrs | llvm-dis | grep readonly | count 2 + +define i32 @f() { +entry: + %tmp = call i32 @e( ) ; [#uses=1] + ret i32 %tmp +} + +declare i32 @e() readonly diff --git a/test/Transforms/AddReadAttrs/2008-09-13-VolatileRead.ll b/test/Transforms/AddReadAttrs/2008-09-13-VolatileRead.ll new file mode 100644 index 00000000000..0690083ae78 --- /dev/null +++ b/test/Transforms/AddReadAttrs/2008-09-13-VolatileRead.ll @@ -0,0 +1,9 @@ +; RUN: llvm-as < %s | opt -addreadattrs | llvm-dis | not grep read +; PR2792 + +@g = global i32 0 ; [#uses=1] + +define i32 @f() { + %t = volatile load i32* @g ; [#uses=1] + ret i32 %t +}