From 9e89ba31f16a960239a750a26a982b4c9dfe8949 Mon Sep 17 00:00:00 2001 From: Duncan Sands Date: Wed, 31 Dec 2008 16:14:43 +0000 Subject: Rename AddReadAttrs to FunctionAttrs, and teach it how to work out (in a very simplistic way) which function arguments (pointer arguments only) are only dereferenced and so do not escape. Mark such arguments 'nocapture'. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@61525 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/llvm/LinkAllPasses.h | 2 +- include/llvm/Transforms/IPO.h | 6 +- lib/Transforms/IPO/AddReadAttrs.cpp | 166 ------------ lib/Transforms/IPO/CMakeLists.txt | 2 +- lib/Transforms/IPO/FunctionAttrs.cpp | 280 +++++++++++++++++++++ test/Transforms/AddReadAttrs/2008-09-03-Mutual.ll | 11 - .../Transforms/AddReadAttrs/2008-09-03-ReadNone.ll | 18 -- .../Transforms/AddReadAttrs/2008-09-03-ReadOnly.ll | 9 - .../AddReadAttrs/2008-09-13-VolatileRead.ll | 9 - .../AddReadAttrs/2008-10-04-LocalMemory.ll | 10 - .../Transforms/AddReadAttrs/2008-12-29-Constant.ll | 8 - test/Transforms/AddReadAttrs/dg.exp | 3 - test/Transforms/FunctionAttrs/2008-09-03-Mutual.ll | 11 + .../FunctionAttrs/2008-09-03-ReadNone.ll | 18 ++ .../FunctionAttrs/2008-09-03-ReadOnly.ll | 9 + .../FunctionAttrs/2008-09-13-VolatileRead.ll | 9 + .../FunctionAttrs/2008-10-04-LocalMemory.ll | 10 + .../FunctionAttrs/2008-12-29-Constant.ll | 8 + .../FunctionAttrs/2008-12-31-NoCapture.ll | 34 +++ test/Transforms/FunctionAttrs/dg.exp | 3 + tools/opt/opt.cpp | 4 +- win32/Transforms/Transforms.vcproj | 2 +- 22 files changed, 391 insertions(+), 241 deletions(-) delete mode 100644 lib/Transforms/IPO/AddReadAttrs.cpp create mode 100644 lib/Transforms/IPO/FunctionAttrs.cpp delete mode 100644 test/Transforms/AddReadAttrs/2008-09-03-Mutual.ll delete mode 100644 test/Transforms/AddReadAttrs/2008-09-03-ReadNone.ll delete mode 100644 test/Transforms/AddReadAttrs/2008-09-03-ReadOnly.ll delete mode 100644 test/Transforms/AddReadAttrs/2008-09-13-VolatileRead.ll delete mode 100644 test/Transforms/AddReadAttrs/2008-10-04-LocalMemory.ll delete mode 100644 test/Transforms/AddReadAttrs/2008-12-29-Constant.ll delete mode 100644 test/Transforms/AddReadAttrs/dg.exp create mode 100644 test/Transforms/FunctionAttrs/2008-09-03-Mutual.ll create mode 100644 test/Transforms/FunctionAttrs/2008-09-03-ReadNone.ll create mode 100644 test/Transforms/FunctionAttrs/2008-09-03-ReadOnly.ll create mode 100644 test/Transforms/FunctionAttrs/2008-09-13-VolatileRead.ll create mode 100644 test/Transforms/FunctionAttrs/2008-10-04-LocalMemory.ll create mode 100644 test/Transforms/FunctionAttrs/2008-12-29-Constant.ll create mode 100644 test/Transforms/FunctionAttrs/2008-12-31-NoCapture.ll create mode 100644 test/Transforms/FunctionAttrs/dg.exp diff --git a/include/llvm/LinkAllPasses.h b/include/llvm/LinkAllPasses.h index bc77c086fd..4113b16dff 100644 --- a/include/llvm/LinkAllPasses.h +++ b/include/llvm/LinkAllPasses.h @@ -122,7 +122,7 @@ namespace { (void) llvm::createPostDomFrontier(); (void) llvm::createInstructionNamerPass(); (void) llvm::createPartialSpecializationPass(); - (void) llvm::createAddReadAttrsPass(); + (void) llvm::createFunctionAttrsPass(); (void) llvm::createMergeFunctionsPass(); (void) llvm::createPrintModulePass(0); (void) llvm::createPrintFunctionPass("", 0); diff --git a/include/llvm/Transforms/IPO.h b/include/llvm/Transforms/IPO.h index 0d4e33af12..eeb2aa26e8 100644 --- a/include/llvm/Transforms/IPO.h +++ b/include/llvm/Transforms/IPO.h @@ -196,10 +196,12 @@ ModulePass *createStripDeadPrototypesPass(); ModulePass *createPartialSpecializationPass(); //===----------------------------------------------------------------------===// -/// createAddReadAttrsPass - This pass discovers functions that do not access +/// createFunctionAttrsPass - This pass discovers functions that do not access /// memory, or only read memory, and gives them the readnone/readonly attribute. +/// It also discovers function arguments that are not captured by the function +/// and marks them with the nocapture attribute. /// -Pass *createAddReadAttrsPass(); +Pass *createFunctionAttrsPass(); //===----------------------------------------------------------------------===// /// createMergeFunctionsPass - This pass discovers identical functions and diff --git a/lib/Transforms/IPO/AddReadAttrs.cpp b/lib/Transforms/IPO/AddReadAttrs.cpp deleted file mode 100644 index 96c1427e0e..0000000000 --- a/lib/Transforms/IPO/AddReadAttrs.cpp +++ /dev/null @@ -1,166 +0,0 @@ -//===- 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/GlobalVariable.h" -#include "llvm/Instructions.h" -#include "llvm/Analysis/CallGraph.h" -#include "llvm/ADT/SmallPtrSet.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); - - virtual void getAnalysisUsage(AnalysisUsage &AU) const { - AU.setPreservesCFG(); - CallGraphSCCPass::getAnalysisUsage(AU); - } - - bool PointsToLocalMemory(Value *V); - }; -} - -char AddReadAttrs::ID = 0; -static RegisterPass -X("addreadattrs", "Mark functions readnone/readonly"); - -Pass *llvm::createAddReadAttrsPass() { return new AddReadAttrs(); } - - -/// PointsToLocalMemory - Returns whether the given pointer value points to -/// memory that is local to the function. Global constants are considered -/// local to all functions. -bool AddReadAttrs::PointsToLocalMemory(Value *V) { - V = V->getUnderlyingObject(); - // An alloca instruction defines local memory. - if (isa(V)) - return true; - // A global constant counts as local memory for our purposes. - if (GlobalVariable *GV = dyn_cast(V)) - return GV->isConstant(); - // Could look through phi nodes and selects here, but it doesn't seem - // to be useful in practice. - return false; -} - -bool AddReadAttrs::runOnSCC(const std::vector &SCC) { - SmallPtrSet SCCNodes; - CallGraph &CG = getAnalysis(); - - // Fill SCCNodes with the elements of the SCC. Used for quickly - // looking up whether a given CallGraphNode is in this SCC. - for (unsigned i = 0, e = SCC.size(); i != e; ++i) - SCCNodes.insert(SCC[i]); - - // Check if any of the functions in the SCC read or write memory. If they - // write memory then they can't be marked readnone or readonly. - bool ReadsMemory = false; - for (unsigned i = 0, e = SCC.size(); i != e; ++i) { - Function *F = SCC[i]->getFunction(); - - if (F == 0) - // External node - may write memory. Just give up. - 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->mayBeOverridden()) { - if (!F->onlyReadsMemory()) - // May write memory. Just give up. - return false; - - ReadsMemory = true; - continue; - } - - // Scan the function body for instructions that may read or write memory. - for (inst_iterator II = inst_begin(F), E = inst_end(F); II != E; ++II) { - Instruction *I = &*II; - - // Some instructions can be ignored even if they read or write memory. - // Detect these now, skipping to the next instruction if one is found. - CallSite CS = CallSite::get(I); - if (CS.getInstruction()) { - // Ignore calls to functions in the same SCC. - if (SCCNodes.count(CG[CS.getCalledFunction()])) - continue; - } else if (LoadInst *LI = dyn_cast(I)) { - // Ignore loads from local memory. - if (PointsToLocalMemory(LI->getPointerOperand())) - continue; - } else if (StoreInst *SI = dyn_cast(I)) { - // Ignore stores to local memory. - if (PointsToLocalMemory(SI->getPointerOperand())) - continue; - } - - // Any remaining instructions need to be taken seriously! Check if they - // read or write memory. - if (I->mayWriteToMemory()) - // Writes memory. Just give up. - return false; - // If this instruction may read memory, remember that. - ReadsMemory |= I->mayReadFromMemory(); - } - } - - // 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->removeAttribute(~0, Attribute::ReadOnly | Attribute::ReadNone); - - // Add in the new attribute. - F->addAttribute(~0, ReadsMemory ? Attribute::ReadOnly : Attribute::ReadNone); - - if (ReadsMemory) - NumReadOnly++; - else - NumReadNone++; - } - - return MadeChange; -} diff --git a/lib/Transforms/IPO/CMakeLists.txt b/lib/Transforms/IPO/CMakeLists.txt index f168083324..4b85e1388a 100644 --- a/lib/Transforms/IPO/CMakeLists.txt +++ b/lib/Transforms/IPO/CMakeLists.txt @@ -1,5 +1,5 @@ add_llvm_library(LLVMipo - AddReadAttrs.cpp + FunctionAttrs.cpp ArgumentPromotion.cpp ConstantMerge.cpp DeadArgumentElimination.cpp diff --git a/lib/Transforms/IPO/FunctionAttrs.cpp b/lib/Transforms/IPO/FunctionAttrs.cpp new file mode 100644 index 0000000000..9ed605cf9f --- /dev/null +++ b/lib/Transforms/IPO/FunctionAttrs.cpp @@ -0,0 +1,280 @@ +//===- FunctionAttrs.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 "functionattrs" +#include "llvm/Transforms/IPO.h" +#include "llvm/CallGraphSCCPass.h" +#include "llvm/GlobalVariable.h" +#include "llvm/Instructions.h" +#include "llvm/Analysis/CallGraph.h" +#include "llvm/ADT/SmallPtrSet.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"); +STATISTIC(NumNoCapture, "Number of arguments marked nocapture"); + +namespace { + struct VISIBILITY_HIDDEN FunctionAttrs : public CallGraphSCCPass { + static char ID; // Pass identification, replacement for typeid + FunctionAttrs() : CallGraphSCCPass(&ID) {} + + // runOnSCC - Analyze the SCC, performing the transformation if possible. + bool runOnSCC(const std::vector &SCC); + + // AddReadAttrs - Deduce readonly/readnone attributes for the SCC. + bool AddReadAttrs(const std::vector &SCC); + + // AddNoCaptureAttrs - Deduce nocapture attributes for the SCC. + bool AddNoCaptureAttrs(const std::vector &SCC); + + // isCaptured - Returns whether this pointer value is captured. + bool isCaptured(Function &F, Value *V); + + virtual void getAnalysisUsage(AnalysisUsage &AU) const { + AU.setPreservesCFG(); + CallGraphSCCPass::getAnalysisUsage(AU); + } + + bool PointsToLocalMemory(Value *V); + }; +} + +char FunctionAttrs::ID = 0; +static RegisterPass +X("functionattrs", "Deduce function attributes"); + +Pass *llvm::createFunctionAttrsPass() { return new FunctionAttrs(); } + + +/// PointsToLocalMemory - Returns whether the given pointer value points to +/// memory that is local to the function. Global constants are considered +/// local to all functions. +bool FunctionAttrs::PointsToLocalMemory(Value *V) { + V = V->getUnderlyingObject(); + // An alloca instruction defines local memory. + if (isa(V)) + return true; + // A global constant counts as local memory for our purposes. + if (GlobalVariable *GV = dyn_cast(V)) + return GV->isConstant(); + // Could look through phi nodes and selects here, but it doesn't seem + // to be useful in practice. + return false; +} + +/// AddReadAttrs - Deduce readonly/readnone attributes for the SCC. +bool FunctionAttrs::AddReadAttrs(const std::vector &SCC) { + SmallPtrSet SCCNodes; + CallGraph &CG = getAnalysis(); + + // Fill SCCNodes with the elements of the SCC. Used for quickly + // looking up whether a given CallGraphNode is in this SCC. + for (unsigned i = 0, e = SCC.size(); i != e; ++i) + SCCNodes.insert(SCC[i]); + + // Check if any of the functions in the SCC read or write memory. If they + // write memory then they can't be marked readnone or readonly. + bool ReadsMemory = false; + for (unsigned i = 0, e = SCC.size(); i != e; ++i) { + Function *F = SCC[i]->getFunction(); + + if (F == 0) + // External node - may write memory. Just give up. + 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->mayBeOverridden()) { + if (!F->onlyReadsMemory()) + // May write memory. Just give up. + return false; + + ReadsMemory = true; + continue; + } + + // Scan the function body for instructions that may read or write memory. + for (inst_iterator II = inst_begin(F), E = inst_end(F); II != E; ++II) { + Instruction *I = &*II; + + // Some instructions can be ignored even if they read or write memory. + // Detect these now, skipping to the next instruction if one is found. + CallSite CS = CallSite::get(I); + if (CS.getInstruction()) { + // Ignore calls to functions in the same SCC. + if (SCCNodes.count(CG[CS.getCalledFunction()])) + continue; + } else if (LoadInst *LI = dyn_cast(I)) { + // Ignore loads from local memory. + if (PointsToLocalMemory(LI->getPointerOperand())) + continue; + } else if (StoreInst *SI = dyn_cast(I)) { + // Ignore stores to local memory. + if (PointsToLocalMemory(SI->getPointerOperand())) + continue; + } + + // Any remaining instructions need to be taken seriously! Check if they + // read or write memory. + if (I->mayWriteToMemory()) + // Writes memory. Just give up. + return false; + // If this instruction may read memory, remember that. + ReadsMemory |= I->mayReadFromMemory(); + } + } + + // 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->removeAttribute(~0, Attribute::ReadOnly | Attribute::ReadNone); + + // Add in the new attribute. + F->addAttribute(~0, ReadsMemory? Attribute::ReadOnly : Attribute::ReadNone); + + if (ReadsMemory) + NumReadOnly++; + else + NumReadNone++; + } + + return MadeChange; +} + +/// isCaptured - Returns whether this pointer value is captured. +bool FunctionAttrs::isCaptured(Function &F, Value *V) { + SmallVector Worklist; + SmallPtrSet Visited; + + for (Value::use_iterator UI = V->use_begin(), UE = V->use_end(); UI != UE; + ++UI) { + Use *U = &UI.getUse(); + Visited.insert(U); + Worklist.push_back(U); + } + + while (!Worklist.empty()) { + Use *U = Worklist.pop_back_val(); + Instruction *I = cast(U->getUser()); + V = U->get(); + + if (isa(I)) { + // Loading a pointer does not cause it to escape. + continue; + } + + if (isa(I)) { + if (V == I->getOperand(0)) + // Stored the pointer - escapes. TODO: improve this. + return true; + // Storing to the pointee does not cause the pointer to escape. + continue; + } + + CallSite CS = CallSite::get(I); + if (CS.getInstruction()) { + // Does not escape if only passed via 'nocapture' arguments. Note + // that calling a function pointer does not in itself cause that + // function pointer to escape. This is a subtle point considering + // that (for example) the callee might return its own address. It + // is analogous to saying that loading a value from a pointer does + // not cause the pointer to escape, even though the loaded value + // might be the pointer itself (think of self-referential objects). + CallSite::arg_iterator B = CS.arg_begin(), E = CS.arg_end(); + for (CallSite::arg_iterator A = B; A != E; ++A) + if (A->get() == V && !CS.paramHasAttr(A-B+1, Attribute::NoCapture)) + // The parameter is not marked 'nocapture' - escapes. + return true; + // Only passed via 'nocapture' arguments, or is the called function. + // Does not escape. + continue; + } + + if (isa(I) || isa(I)) { + // Type conversion or calculating an offset. Does not escape if the new + // value doesn'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); + } + continue; + } + + // Something else - be conservative and say it escapes. + return true; + } + + return false; +} + +/// AddNoCaptureAttrs - Deduce nocapture attributes for the SCC. +bool FunctionAttrs::AddNoCaptureAttrs(const std::vector &SCC) { + bool Changed = false; + + // Check each function in turn, determining which pointer arguments are not + // captured. + for (unsigned i = 0, e = SCC.size(); i != e; ++i) { + Function *F = SCC[i]->getFunction(); + + if (F == 0) + // External node - skip it; + continue; + + // Definitions with weak linkage may be overridden at linktime with + // something that writes memory, so treat them like declarations. + if (F->isDeclaration() || F->mayBeOverridden()) + continue; + + for (Function::arg_iterator A = F->arg_begin(), E = F->arg_end(); A!=E; ++A) + if (isa(A->getType()) && !isCaptured(*F, A)) { + A->addAttr(Attribute::NoCapture); + NumNoCapture++; + Changed = true; + } + } + + return Changed; +} + +bool FunctionAttrs::runOnSCC(const std::vector &SCC) { + bool Changed = AddReadAttrs(SCC); + Changed |= AddNoCaptureAttrs(SCC); + return Changed; +} diff --git a/test/Transforms/AddReadAttrs/2008-09-03-Mutual.ll b/test/Transforms/AddReadAttrs/2008-09-03-Mutual.ll deleted file mode 100644 index 0a4f0855ca..0000000000 --- a/test/Transforms/AddReadAttrs/2008-09-03-Mutual.ll +++ /dev/null @@ -1,11 +0,0 @@ -; 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 deleted file mode 100644 index 3c7b32d9d4..0000000000 --- a/test/Transforms/AddReadAttrs/2008-09-03-ReadNone.ll +++ /dev/null @@ -1,18 +0,0 @@ -; RUN: llvm-as < %s | opt -addreadattrs | llvm-dis | grep readnone | count 4 -@x = global i32 0 - -declare i32 @e() readnone - -define i32 @f() { - %tmp = call i32 @e( ) ; [#uses=1] - ret i32 %tmp -} - -define i32 @g() readonly { - ret i32 0 -} - -define i32 @h() readnone { - %tmp = load i32* @x ; [#uses=1] - ret i32 %tmp -} diff --git a/test/Transforms/AddReadAttrs/2008-09-03-ReadOnly.ll b/test/Transforms/AddReadAttrs/2008-09-03-ReadOnly.ll deleted file mode 100644 index c08e7b1265..0000000000 --- a/test/Transforms/AddReadAttrs/2008-09-03-ReadOnly.ll +++ /dev/null @@ -1,9 +0,0 @@ -; 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 deleted file mode 100644 index 0690083ae7..0000000000 --- a/test/Transforms/AddReadAttrs/2008-09-13-VolatileRead.ll +++ /dev/null @@ -1,9 +0,0 @@ -; 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 -} diff --git a/test/Transforms/AddReadAttrs/2008-10-04-LocalMemory.ll b/test/Transforms/AddReadAttrs/2008-10-04-LocalMemory.ll deleted file mode 100644 index 0f63c1a58b..0000000000 --- a/test/Transforms/AddReadAttrs/2008-10-04-LocalMemory.ll +++ /dev/null @@ -1,10 +0,0 @@ -; RUN: llvm-as < %s | opt -addreadattrs | llvm-dis | grep readnone | count 2 - -declare i32 @g(i32*) readnone - -define i32 @f() { - %x = alloca i32 ; [#uses=2] - store i32 0, i32* %x - %y = call i32 @g(i32* %x) ; [#uses=1] - ret i32 %y -} diff --git a/test/Transforms/AddReadAttrs/2008-12-29-Constant.ll b/test/Transforms/AddReadAttrs/2008-12-29-Constant.ll deleted file mode 100644 index fe038c1be9..0000000000 --- a/test/Transforms/AddReadAttrs/2008-12-29-Constant.ll +++ /dev/null @@ -1,8 +0,0 @@ -; RUN: llvm-as < %s | opt -addreadattrs | llvm-dis | grep readnone - -@s = external constant i8 ; [#uses=1] - -define i8 @f() { - %tmp = load i8* @s ; [#uses=1] - ret i8 %tmp -} diff --git a/test/Transforms/AddReadAttrs/dg.exp b/test/Transforms/AddReadAttrs/dg.exp deleted file mode 100644 index f2005891a5..0000000000 --- a/test/Transforms/AddReadAttrs/dg.exp +++ /dev/null @@ -1,3 +0,0 @@ -load_lib llvm.exp - -RunLLVMTests [lsort [glob -nocomplain $srcdir/$subdir/*.{ll,c,cpp}]] diff --git a/test/Transforms/FunctionAttrs/2008-09-03-Mutual.ll b/test/Transforms/FunctionAttrs/2008-09-03-Mutual.ll new file mode 100644 index 0000000000..5261ac4658 --- /dev/null +++ b/test/Transforms/FunctionAttrs/2008-09-03-Mutual.ll @@ -0,0 +1,11 @@ +; RUN: llvm-as < %s | opt -functionattrs | 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/FunctionAttrs/2008-09-03-ReadNone.ll b/test/Transforms/FunctionAttrs/2008-09-03-ReadNone.ll new file mode 100644 index 0000000000..a17d381eec --- /dev/null +++ b/test/Transforms/FunctionAttrs/2008-09-03-ReadNone.ll @@ -0,0 +1,18 @@ +; RUN: llvm-as < %s | opt -functionattrs | llvm-dis | grep readnone | count 4 +@x = global i32 0 + +declare i32 @e() readnone + +define i32 @f() { + %tmp = call i32 @e( ) ; [#uses=1] + ret i32 %tmp +} + +define i32 @g() readonly { + ret i32 0 +} + +define i32 @h() readnone { + %tmp = load i32* @x ; [#uses=1] + ret i32 %tmp +} diff --git a/test/Transforms/FunctionAttrs/2008-09-03-ReadOnly.ll b/test/Transforms/FunctionAttrs/2008-09-03-ReadOnly.ll new file mode 100644 index 0000000000..cebfdacb4c --- /dev/null +++ b/test/Transforms/FunctionAttrs/2008-09-03-ReadOnly.ll @@ -0,0 +1,9 @@ +; RUN: llvm-as < %s | opt -functionattrs | 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/FunctionAttrs/2008-09-13-VolatileRead.ll b/test/Transforms/FunctionAttrs/2008-09-13-VolatileRead.ll new file mode 100644 index 0000000000..b6077fd8ee --- /dev/null +++ b/test/Transforms/FunctionAttrs/2008-09-13-VolatileRead.ll @@ -0,0 +1,9 @@ +; RUN: llvm-as < %s | opt -functionattrs | 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/FunctionAttrs/2008-10-04-LocalMemory.ll b/test/Transforms/FunctionAttrs/2008-10-04-LocalMemory.ll new file mode 100644 index 0000000000..50ca641200 --- /dev/null +++ b/test/Transforms/FunctionAttrs/2008-10-04-LocalMemory.ll @@ -0,0 +1,10 @@ +; RUN: llvm-as < %s | opt -functionattrs | llvm-dis | grep readnone | count 2 + +declare i32 @g(i32*) readnone + +define i32 @f() { + %x = alloca i32 ; [#uses=2] + store i32 0, i32* %x + %y = call i32 @g(i32* %x) ; [#uses=1] + ret i32 %y +} diff --git a/test/Transforms/FunctionAttrs/2008-12-29-Constant.ll b/test/Transforms/FunctionAttrs/2008-12-29-Constant.ll new file mode 100644 index 0000000000..d9c01171ac --- /dev/null +++ b/test/Transforms/FunctionAttrs/2008-12-29-Constant.ll @@ -0,0 +1,8 @@ +; RUN: llvm-as < %s | opt -functionattrs | llvm-dis | grep readnone + +@s = external constant i8 ; [#uses=1] + +define i8 @f() { + %tmp = load i8* @s ; [#uses=1] + ret i8 %tmp +} diff --git a/test/Transforms/FunctionAttrs/2008-12-31-NoCapture.ll b/test/Transforms/FunctionAttrs/2008-12-31-NoCapture.ll new file mode 100644 index 0000000000..6d2ca1e446 --- /dev/null +++ b/test/Transforms/FunctionAttrs/2008-12-31-NoCapture.ll @@ -0,0 +1,34 @@ +; RUN: llvm-as < %s | opt -functionattrs | llvm-dis | not grep {@c.*nocapture} +; RUN: llvm-as < %s | opt -functionattrs | llvm-dis | grep nocapture | count 3 +@g = global i32* null ; [#uses=1] + +define i32* @c1(i32* %p) { + ret i32* %p +} + +define void @c2(i32* %p) { + store i32* %p, i32** @g + ret void +} + +define void @c3(i32* %p) { + call void @c2(i32* %p) + ret void +} + +define i32 @nc1(i32* %p) { + %tmp = bitcast i32* %p to i32* ; [#uses=2] + %val = load i32* %tmp ; [#uses=1] + store i32 0, i32* %tmp + ret i32 %val +} + +define void @nc2(i32* %p) { + %1 = call i32 @nc1(i32* %p) ; [#uses=0] + ret void +} + +define void @nc3(void ()* %f) { + call void %f() + ret void +} diff --git a/test/Transforms/FunctionAttrs/dg.exp b/test/Transforms/FunctionAttrs/dg.exp new file mode 100644 index 0000000000..f2005891a5 --- /dev/null +++ b/test/Transforms/FunctionAttrs/dg.exp @@ -0,0 +1,3 @@ +load_lib llvm.exp + +RunLLVMTests [lsort [glob -nocomplain $srcdir/$subdir/*.{ll,c,cpp}]] diff --git a/tools/opt/opt.cpp b/tools/opt/opt.cpp index 9039553511..b46f7c9188 100644 --- a/tools/opt/opt.cpp +++ b/tools/opt/opt.cpp @@ -291,7 +291,7 @@ void AddOptimizationPasses(PassManager &MPM, FunctionPassManager &FPM, MPM.add(createCFGSimplificationPass()); // Clean up after IPCP & DAE if (UnitAtATime) { MPM.add(createPruneEHPass()); // Remove dead EH info - MPM.add(createAddReadAttrsPass()); // Set readonly/readnone attrs + MPM.add(createFunctionAttrsPass()); // Deduce function attrs } if (OptLevel > 1) MPM.add(createFunctionInliningPass()); // Inline small functions @@ -363,7 +363,7 @@ void AddStandardCompilePasses(PassManager &PM) { addPass(PM, createCFGSimplificationPass()); // Clean up after IPCP & DAE addPass(PM, createPruneEHPass()); // Remove dead EH info - addPass(PM, createAddReadAttrsPass()); // Set readonly/readnone attrs + addPass(PM, createFunctionAttrsPass()); // Deduce function attrs if (!DisableInline) addPass(PM, createFunctionInliningPass()); // Inline small functions diff --git a/win32/Transforms/Transforms.vcproj b/win32/Transforms/Transforms.vcproj index 8a5e8985e4..0bde3d89ff 100644 --- a/win32/Transforms/Transforms.vcproj +++ b/win32/Transforms/Transforms.vcproj @@ -344,7 +344,7 @@ Name="IPO" >