From 8f28c78e952b3943f4bb714254e802f126505e7a Mon Sep 17 00:00:00 2001 From: Owen Anderson Date: Fri, 10 Oct 2008 08:36:25 +0000 Subject: [PATCH] Add a basic intra-procedural escape analysis. This hasn't be extensively tested yet, but feedback is welcome. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@57342 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/llvm/Analysis/EscapeAnalysis.h | 59 +++++++++++ lib/Analysis/EscapeAnalysis.cpp | 131 +++++++++++++++++++++++++ 2 files changed, 190 insertions(+) create mode 100644 include/llvm/Analysis/EscapeAnalysis.h create mode 100644 lib/Analysis/EscapeAnalysis.cpp diff --git a/include/llvm/Analysis/EscapeAnalysis.h b/include/llvm/Analysis/EscapeAnalysis.h new file mode 100644 index 00000000000..085028d98f8 --- /dev/null +++ b/include/llvm/Analysis/EscapeAnalysis.h @@ -0,0 +1,59 @@ +//===------------- EscapeAnalysis.h - Pointer escape analysis -------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the interface for the pointer escape analysis. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_ANALYSIS_LOOPVR_H +#define LLVM_ANALYSIS_LOOPVR_H + +#include "llvm/Pass.h" +#include "llvm/Instructions.h" +#include "llvm/Analysis/AliasAnalysis.h" +#include "llvm/Target/TargetData.h" +#include +#include + +namespace llvm { + +/// EscapeAnalysis - This class determines whether an allocation (a MallocInst +/// or an AllocaInst) can escape from the current function. It performs some +/// precomputation, with the rest of the work happening on-demand. +class EscapeAnalysis : public FunctionPass { +private: + std::set EscapePoints; + +public: + static char ID; // Class identification, replacement for typeinfo + + EscapeAnalysis() : FunctionPass(intptr_t(&ID)) {} + + bool runOnFunction(Function &F); + + void releaseMemory() { + EscapePoints.clear(); + } + + void getAnalysisUsage(AnalysisUsage &AU) const { + AU.addRequiredTransitive(); + AU.addRequiredTransitive(); + AU.setPreservesAll(); + } + + //===--------------------------------------------------------------------- + // Client API + + /// escapes - returns true if the AllocationInst can escape. + bool escapes(AllocationInst* A); +}; + +} // end llvm namespace + +#endif diff --git a/lib/Analysis/EscapeAnalysis.cpp b/lib/Analysis/EscapeAnalysis.cpp new file mode 100644 index 00000000000..07f4761fd5f --- /dev/null +++ b/lib/Analysis/EscapeAnalysis.cpp @@ -0,0 +1,131 @@ +//===------------- EscapeAnalysis.h - Pointer escape analysis -------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file provides the implementation of the pointer escape analysis. +// +//===----------------------------------------------------------------------===// + +#define DEBUG_TYPE "escape-analysis" +#include "llvm/Analysis/EscapeAnalysis.h" +#include "llvm/Module.h" +#include "llvm/Support/InstIterator.h" +#include "llvm/ADT/SmallPtrSet.h" +using namespace llvm; + +char EscapeAnalysis::ID = 0; +static RegisterPass X("escape-analysis", + "Pointer Escape Analysis", true, true); + + +/// runOnFunction - Precomputation for escape analysis. This collects all know +/// "escape points" in the def-use graph of the function. These are +/// instructions which allow their inputs to escape from the current function. +bool EscapeAnalysis::runOnFunction(Function& F) { + EscapePoints.clear(); + + TargetData& TD = getAnalysis(); + AliasAnalysis& AA = getAnalysis(); + Module* M = F.getParent(); + + // Walk through all instructions in the function, identifying those that + // may allow their inputs to escape. + for(inst_iterator II = inst_begin(F), IE = inst_end(F); II != IE; ++II) { + Instruction* I = &*II; + + // The most obvious case is stores. Any store that may write to global + // memory or to a function argument potentially allows its input to escape. + if (StoreInst* S = dyn_cast(I)) { + const Type* StoreType = S->getOperand(0)->getType(); + unsigned StoreSize = TD.getTypeStoreSize(StoreType); + Value* Pointer = S->getPointerOperand(); + + bool inserted = false; + for (Function::arg_iterator AI = F.arg_begin(), AE = F.arg_end(); + AI != AE; ++AI) { + AliasAnalysis::AliasResult R = AA.alias(Pointer, StoreSize, AI, ~0UL); + if (R != AliasAnalysis::NoAlias) { + EscapePoints.insert(S); + inserted = true; + break; + } + } + + if (inserted) + continue; + + for (Module::global_iterator GI = M->global_begin(), GE = M->global_end(); + GI != GE; ++GI) { + AliasAnalysis::AliasResult R = AA.alias(Pointer, StoreSize, GI, ~0UL); + if (R != AliasAnalysis::NoAlias) { + EscapePoints.insert(S); + break; + } + } + + // Calls and invokes potentially allow their parameters to escape. + // FIXME: This can and should be refined. Intrinsics have known escape + // behavior, and alias analysis may be able to tell us more about callees. + } else if (isa(I) || isa(I)) { + EscapePoints.insert(I); + + // Returns allow the return value to escape. This is mostly important + // for malloc to alloca promotion. + } else if (isa(I)) { + EscapePoints.insert(I); + } + + // FIXME: Are there any other possible escape points? + } + + return false; +} + +/// escapes - Determines whether the passed allocation can escape from the +/// current function. It does this by using a simple worklist algorithm to +/// search for a path in the def-use graph from the allocation to an +/// escape point. +/// FIXME: Once we've discovered a path, it would be a good idea to memoize it, +/// and all of its subpaths, to amortize the cost of future queries. +bool EscapeAnalysis::escapes(AllocationInst* A) { + std::vector worklist; + worklist.push_back(A); + + SmallPtrSet visited; + while (!worklist.empty()) { + Value* curr = worklist.back(); + worklist.pop_back(); + + visited.insert(curr); + + if (Instruction* CurrInst = dyn_cast(curr)) + if (EscapePoints.count(CurrInst)) + return true; + + for (Instruction::use_iterator UI = curr->use_begin(), UE = curr->use_end(); + UI != UE; ++UI) + if (Instruction* U = dyn_cast(UI)) + if (!visited.count(U)) + if (StoreInst* S = dyn_cast(U)) { + // We know this must be an instruction, because constant gep's would + // have been found to alias a global, so stores to them would have + // been in EscapePoints. + worklist.push_back(cast(S->getPointerOperand())); + } else if (isa(U) || isa(U)) { + // Because branches on the pointer value can hide data dependencies, + // we need to track values that were generated by branching on the + // pointer (or some derived value). To do that, we push the block, + // whose uses will be the PHINodes that generate information based + // one it. + worklist.push_back(U->getParent()); + } else + worklist.push_back(U); + } + + return false; +} \ No newline at end of file