[llvm] r200903 - [PM] Add a new "lazy" call graph analysis pass for the new pass manager.

Kostya Serebryany kcc at google.com
Wed Feb 5 23:48:04 PST 2014


This makes our bootstrap bot sad...
http://lab.llvm.org:8011/builders/sanitizer-x86_64-linux-bootstrap/builds/2043/steps/check-llvm%20msan/logs/stdio

FAIL: LLVM :: Analysis/LazyCallGraph/basic.ll (127 of 9712)
******************** TEST 'LLVM :: Analysis/LazyCallGraph/basic.ll'
FAILED ********************
Script:
--
/home/dtoolsbot/build/sanitizer-x86_64-linux-bootstrap/build/llvm_build_msan/./bin/opt
-disable-output -passes=print-cg
/home/dtoolsbot/build/sanitizer-x86_64-linux-bootstrap/build/llvm/test/Analysis/LazyCallGraph/basic.ll
2>&1 | /home/dtoolsbot/build/sanitizer-x86_64-linux-bootstrap/build/llvm_build_msan/./bin/FileCheck
/home/dtoolsbot/build/sanitizer-x86_64-linux-bootstrap/build/llvm/test/Analysis/LazyCallGraph/basic.ll
--
Exit Code: 1

Command Output (stderr):
--
/home/dtoolsbot/build/sanitizer-x86_64-linux-bootstrap/build/llvm/test/Analysis/LazyCallGraph/basic.ll:6:16:
error: expected string not found in input
; CHECK-LABEL: Call edges in function: f
               ^
<stdin>:1:1: note: scanning from here
Printing the call graph for module:
/home/dtoolsbot/build/sanitizer-x86_64-linux-bootstrap/build/llvm/test/Analysis/LazyCallGraph/basic.ll
^
<stdin>:1:14: note: possible intended match here
Printing the call graph for module:
/home/dtoolsbot/build/sanitizer-x86_64-linux-bootstrap/build/llvm/test/Analysis/LazyCallGraph/basic.ll
             ^




On Thu, Feb 6, 2014 at 8:37 AM, Chandler Carruth <chandlerc at gmail.com>wrote:

> Author: chandlerc
> Date: Wed Feb  5 22:37:03 2014
> New Revision: 200903
>
> URL: http://llvm.org/viewvc/llvm-project?rev=200903&view=rev
> Log:
> [PM] Add a new "lazy" call graph analysis pass for the new pass manager.
>
> The primary motivation for this pass is to separate the call graph
> analysis used by the new pass manager's CGSCC pass management from the
> existing call graph analysis pass. That analysis pass is (somewhat
> unfortunately) over-constrained by the existing CallGraphSCCPassManager
> requirements. Those requirements make it *really* hard to cleanly layer
> the needed functionality for the new pass manager on top of the existing
> analysis.
>
> However, there are also a bunch of things that the pass manager would
> specifically benefit from doing differently from the existing call graph
> analysis, and this new implementation tries to address several of them:
>
> - Be lazy about scanning function definitions. The existing pass eagerly
>   scans the entire module to build the initial graph. This new pass is
>   significantly more lazy, and I plan to push this even further to
>   maximize locality during CGSCC walks.
> - Don't use a single synthetic node to partition functions with an
>   indirect call from functions whose address is taken. This node creates
>   a huge choke-point which would preclude good parallelization across
>   the fanout of the SCC graph when we got to the point of looking at
>   such changes to LLVM.
> - Use a memory dense and lightweight representation of the call graph
>   rather than value handles and tracking call instructions. This will
>   require explicit update calls instead of some updates working
>   transparently, but should end up being significantly more efficient.
>   The explicit update calls ended up being needed in many cases for the
>   existing call graph so we don't really lose anything.
> - Doesn't explicitly model SCCs and thus doesn't provide an "identity"
>   for an SCC which is stable across updates. This is essential for the
>   new pass manager to work correctly.
> - Only form the graph necessary for traversing all of the functions in
>   an SCC friendly order. This is a much simpler graph structure and
>   should be more memory dense. It does limit the ways in which it is
>   appropriate to use this analysis. I wish I had a better name than
>   "call graph". I've commented extensively this aspect.
>
> This is still very much a WIP, in fact it is really just the initial
> bits. But it is about the fourth version of the initial bits that I've
> implemented with each of the others running into really frustrating
> problms. This looks like it will actually work and I'd like to split the
> actual complexity across commits for the sake of my reviewers. =] The
> rest of the implementation along with lots of wiring will follow
> somewhat more rapidly now that there is a good path forward.
>
> Naturally, this doesn't impact any of the existing optimizer. This code
> is specific to the new pass manager.
>
> A bunch of thanks are deserved for the various folks that have helped
> with the design of this, especially Nick Lewycky who actually sat with
> me to go through the fundamentals of the final version here.
>
> Added:
>     llvm/trunk/include/llvm/Analysis/LazyCallGraph.h
>     llvm/trunk/lib/Analysis/LazyCallGraph.cpp
>     llvm/trunk/test/Analysis/LazyCallGraph/
>     llvm/trunk/test/Analysis/LazyCallGraph/basic.ll
> Modified:
>     llvm/trunk/lib/Analysis/CMakeLists.txt
>     llvm/trunk/tools/opt/NewPMDriver.cpp
>     llvm/trunk/tools/opt/Passes.cpp
>
> Added: llvm/trunk/include/llvm/Analysis/LazyCallGraph.h
> URL:
> http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/Analysis/LazyCallGraph.h?rev=200903&view=auto
>
> ==============================================================================
> --- llvm/trunk/include/llvm/Analysis/LazyCallGraph.h (added)
> +++ llvm/trunk/include/llvm/Analysis/LazyCallGraph.h Wed Feb  5 22:37:03
> 2014
> @@ -0,0 +1,337 @@
> +//===- LazyCallGraph.h - Analysis of a Module's call graph ------*- C++
> -*-===//
> +//
> +//                     The LLVM Compiler Infrastructure
> +//
> +// This file is distributed under the University of Illinois Open Source
> +// License. See LICENSE.TXT for details.
> +//
>
> +//===----------------------------------------------------------------------===//
> +/// \file
> +///
> +/// Implements a lazy call graph analysis and related passes for the new
> pass
> +/// manager.
> +///
> +/// NB: This is *not* a traditional call graph! It is a graph which
> models both
> +/// the current calls and potential calls. As a consequence there are many
> +/// edges in this call graph that do not correspond to a 'call' or
> 'invoke'
> +/// instruction.
> +///
> +/// The primary use cases of this graph analysis is to facilitate
> iterating
> +/// across the functions of a module in ways that ensure all callees are
> +/// visited prior to a caller (given any SCC constraints), or vice versa.
> As
> +/// such is it particularly well suited to organizing CGSCC optimizations
> such
> +/// as inlining, outlining, argument promotion, etc. That is its primary
> use
> +/// case and motivates the design. It may not be appropriate for other
> +/// purposes. The use graph of functions or some other conservative
> analysis of
> +/// call instructions may be interesting for optimizations and subsequent
> +/// analyses which don't work in the context of an overly specified
> +/// potential-call-edge graph.
> +///
> +/// To understand the specific rules and nature of this call graph
> analysis,
> +/// see the documentation of the \c LazyCallGraph below.
> +///
>
> +//===----------------------------------------------------------------------===//
> +
> +#ifndef LLVM_ANALYSIS_LAZY_CALL_GRAPH
> +#define LLVM_ANALYSIS_LAZY_CALL_GRAPH
> +
> +#include "llvm/ADT/DenseMap.h"
> +#include "llvm/ADT/PointerUnion.h"
> +#include "llvm/ADT/SmallVector.h"
> +#include "llvm/ADT/SmallPtrSet.h"
> +#include "llvm/ADT/STLExtras.h"
> +#include "llvm/IR/Module.h"
> +#include "llvm/IR/Function.h"
> +#include "llvm/IR/BasicBlock.h"
> +#include "llvm/Support/Allocator.h"
> +#include <iterator>
> +
> +namespace llvm {
> +class ModuleAnalysisManager;
> +class PreservedAnalyses;
> +class raw_ostream;
> +
> +/// \brief A lazily constructed view of the call graph of a module.
> +///
> +/// With the edges of this graph, the motivating constraint that we are
> +/// attempting to maintain is that function-local optimization,
> CGSCC-local
> +/// optimizations, and optimizations transforming a pair of functions
> connected
> +/// by an edge in the graph, do not invalidate a bottom-up traversal of
> the SCC
> +/// DAG. That is, no optimizations will delete, remove, or add an edge
> such
> +/// that functions already visited in a bottom-up order of the SCC DAG
> are no
> +/// longer valid to have visited, or such that functions not yet visited
> in
> +/// a bottom-up order of the SCC DAG are not required to have already been
> +/// visited.
> +///
> +/// Within this constraint, the desire is to minimize the merge points of
> the
> +/// SCC DAG. The greater the fanout of the SCC DAG and the fewer merge
> points
> +/// in the SCC DAG, the more independence there is in optimizing within
> it.
> +/// There is a strong desire to enable parallelization of optimizations
> over
> +/// the call graph, and both limited fanout and merge points will
> (artificially
> +/// in some cases) limit the scaling of such an effort.
> +///
> +/// To this end, graph represents both direct and any potential
> resolution to
> +/// an indirect call edge. Another way to think about it is that it
> represents
> +/// both the direct call edges and any direct call edges that might be
> formed
> +/// through static optimizations. Specifically, it considers taking the
> address
> +/// of a function to be an edge in the call graph because this might be
> +/// forwarded to become a direct call by some subsequent function-local
> +/// optimization. The result is that the graph closely follows the use-def
> +/// edges for functions. Walking "up" the graph can be done by looking at
> all
> +/// of the uses of a function.
> +///
> +/// The roots of the call graph are the external functions and functions
> +/// escaped into global variables. Those functions can be called from
> outside
> +/// of the module or via unknowable means in the IR -- we may not be able
> to
> +/// form even a potential call edge from a function body which may
> dynamically
> +/// load the function and call it.
> +///
> +/// This analysis still requires updates to remain valid after
> optimizations
> +/// which could potentially change the set of potential callees. The
> +/// constraints it operates under only make the traversal order remain
> valid.
> +///
> +/// The entire analysis must be re-computed if full interprocedural
> +/// optimizations run at any point. For example, globalopt completely
> +/// invalidates the information in this analysis.
> +///
> +/// FIXME: This class is named LazyCallGraph in a lame attempt to
> distinguish
> +/// it from the existing CallGraph. At some point, it is expected that
> this
> +/// will be the only call graph and it will be renamed accordingly.
> +class LazyCallGraph {
> +public:
> +  class Node;
> +  typedef SmallVector<PointerUnion<Function *, Node *>, 4> NodeVectorT;
> +  typedef SmallVectorImpl<PointerUnion<Function *, Node *> >
> NodeVectorImplT;
> +
> +  /// \brief A lazy iterator used for both the entry nodes and child
> nodes.
> +  ///
> +  /// When this iterator is dereferenced, if not yet available, a
> function will
> +  /// be scanned for "calls" or uses of functions and its child
> information
> +  /// will be constructed. All of these results are accumulated and
> cached in
> +  /// the graph.
> +  class iterator : public std::iterator<std::bidirectional_iterator_tag,
> Node *,
> +                                        ptrdiff_t, Node *, Node *> {
> +    friend class LazyCallGraph;
> +    friend class LazyCallGraph::Node;
> +    typedef std::iterator<std::bidirectional_iterator_tag, Node *,
> ptrdiff_t,
> +                          Node *, Node *> BaseT;
> +
> +    /// \brief Nonce type to select the constructor for the end iterator.
> +    struct IsAtEndT {};
> +
> +    LazyCallGraph &G;
> +    NodeVectorImplT::iterator NI;
> +
> +    // Build the begin iterator for a node.
> +    explicit iterator(LazyCallGraph &G, NodeVectorImplT &Nodes)
> +        : G(G), NI(Nodes.begin()) {}
> +
> +    // Build the end iterator for a node. This is selected purely by
> overload.
> +    iterator(LazyCallGraph &G, NodeVectorImplT &Nodes, IsAtEndT /*Nonce*/)
> +        : G(G), NI(Nodes.end()) {}
> +
> +  public:
> +    iterator(const iterator &Arg) : G(Arg.G), NI(Arg.NI) {}
> +
> +    iterator &operator=(iterator Arg) {
> +      std::swap(Arg, *this);
> +      return *this;
> +    }
> +
> +    bool operator==(const iterator &Arg) { return NI == Arg.NI; }
> +    bool operator!=(const iterator &Arg) { return !operator==(Arg); }
> +
> +    reference operator*() const {
> +      if (NI->is<Node *>())
> +        return NI->get<Node *>();
> +
> +      Function *F = NI->get<Function *>();
> +      Node *ChildN = G.get(*F);
> +      *NI = ChildN;
> +      return ChildN;
> +    }
> +    pointer operator->() const { return operator*(); }
> +
> +    iterator &operator++() {
> +      ++NI;
> +      return *this;
> +    }
> +    iterator operator++(int) {
> +      iterator prev = *this;
> +      ++*this;
> +      return prev;
> +    }
> +
> +    iterator &operator--() {
> +      --NI;
> +      return *this;
> +    }
> +    iterator operator--(int) {
> +      iterator next = *this;
> +      --*this;
> +      return next;
> +    }
> +  };
> +
> +  /// \brief Construct a graph for the given module.
> +  ///
> +  /// This sets up the graph and computes all of the entry points of the
> graph.
> +  /// No function definitions are scanned until their nodes in the graph
> are
> +  /// requested during traversal.
> +  LazyCallGraph(Module &M);
> +
> +  /// \brief Copy constructor.
> +  ///
> +  /// This does a deep copy of the graph. It does no verification that the
> +  /// graph remains valid for the module. It is also relatively expensive.
> +  LazyCallGraph(const LazyCallGraph &G);
> +
> +#if LLVM_HAS_RVALUE_REFERENCES
> +  /// \brief Move constructor.
> +  ///
> +  /// This is a deep move. It leaves G in an undefined but destroyable
> state.
> +  /// Any other operation on G is likely to fail.
> +  LazyCallGraph(LazyCallGraph &&G);
> +#endif
> +
> +  iterator begin() { return iterator(*this, EntryNodes); }
> +  iterator end() { return iterator(*this, EntryNodes,
> iterator::IsAtEndT()); }
> +
> +  /// \brief Lookup a function in the graph which has already been
> scanned and
> +  /// added.
> +  Node *lookup(const Function &F) const { return NodeMap.lookup(&F); }
> +
> +  /// \brief Get a graph node for a given function, scanning it to
> populate the
> +  /// graph data as necessary.
> +  Node *get(Function &F) {
> +    Node *&N = NodeMap[&F];
> +    if (N)
> +      return N;
> +
> +    return insertInto(F, N);
> +  }
> +
> +private:
> +  Module &M;
> +
> +  /// \brief Allocator that holds all the call graph nodes.
> +  SpecificBumpPtrAllocator<Node> BPA;
> +
> +  /// \brief Maps function->node for fast lookup.
> +  DenseMap<const Function *, Node *> NodeMap;
> +
> +  /// \brief The entry nodes to the graph.
> +  ///
> +  /// These nodes are reachable through "external" means. Put another
> way, they
> +  /// escape at the module scope.
> +  NodeVectorT EntryNodes;
> +
> +  /// \brief Set of the entry nodes to the graph.
> +  SmallPtrSet<Function *, 4> EntryNodeSet;
> +
> +  /// \brief Helper to insert a new function, with an already looked-up
> entry in
> +  /// the NodeMap.
> +  Node *insertInto(Function &F, Node *&MappedN);
> +
> +  /// \brief Helper to copy a node from another graph into this one.
> +  Node *copyInto(const Node &OtherN);
> +
> +#if LLVM_HAS_RVALUE_REFERENCES
> +  /// \brief Helper to move a node from another graph into this one.
> +  Node *moveInto(Node &&OtherN);
> +#endif
> +};
> +
> +/// \brief A node in the call graph.
> +///
> +/// This represents a single node. It's primary roles are to cache the
> list of
> +/// callees, de-duplicate and provide fast testing of whether a function
> is
> +/// a callee, and facilitate iteration of child nodes in the graph.
> +class LazyCallGraph::Node {
> +  friend LazyCallGraph;
> +
> +  LazyCallGraph &G;
> +  Function &F;
> +  mutable NodeVectorT Callees;
> +  SmallPtrSet<Function *, 4> CalleeSet;
> +
> +  /// \brief Basic constructor implements the scanning of F into Callees
> and
> +  /// CalleeSet.
> +  Node(LazyCallGraph &G, Function &F);
> +
> +  /// \brief Constructor used when copying a node from one graph to
> another.
> +  Node(LazyCallGraph &G, const Node &OtherN);
> +
> +#if LLVM_HAS_RVALUE_REFERENCES
> +  /// \brief Constructor used when moving a node from one graph to
> another.
> +  Node(LazyCallGraph &G, Node &&OtherN);
> +#endif
> +
> +public:
> +  typedef LazyCallGraph::iterator iterator;
> +
> +  Function &getFunction() const {
> +    return F;
> +  };
> +
> +  iterator begin() const { return iterator(G, Callees); }
> +  iterator end() const { return iterator(G, Callees,
> iterator::IsAtEndT()); }
> +
> +  /// Equality is defined as address equality.
> +  bool operator==(const Node &N) const { return this == &N; }
> +  bool operator!=(const Node &N) const { return !operator==(N); }
> +};
> +
> +// Provide GraphTraits specializations for call graphs.
> +template <> struct GraphTraits<LazyCallGraph::Node *> {
> +  typedef LazyCallGraph::Node NodeType;
> +  typedef LazyCallGraph::iterator ChildIteratorType;
> +
> +  static NodeType *getEntryNode(NodeType *N) { return N; }
> +  static ChildIteratorType child_begin(NodeType *N) { return N->begin(); }
> +  static ChildIteratorType child_end(NodeType *N) { return N->end(); }
> +};
> +template <> struct GraphTraits<LazyCallGraph *> {
> +  typedef LazyCallGraph::Node NodeType;
> +  typedef LazyCallGraph::iterator ChildIteratorType;
> +
> +  static NodeType *getEntryNode(NodeType *N) { return N; }
> +  static ChildIteratorType child_begin(NodeType *N) { return N->begin(); }
> +  static ChildIteratorType child_end(NodeType *N) { return N->end(); }
> +};
> +
> +/// \brief An analysis pass which computes the call graph for a module.
> +class LazyCallGraphAnalysis {
> +public:
> +  /// \brief Inform generic clients of the result type.
> +  typedef LazyCallGraph Result;
> +
> +  static void *ID() { return (void *)&PassID; }
> +
> +  /// \brief Compute the \c LazyCallGraph for a the module \c M.
> +  ///
> +  /// This just builds the set of entry points to the call graph. The
> rest is
> +  /// built lazily as it is walked.
> +  LazyCallGraph run(Module *M) { return LazyCallGraph(*M); }
> +
> +private:
> +  static char PassID;
> +};
> +
> +/// \brief A pass which prints the call graph to a \c raw_ostream.
> +///
> +/// This is primarily useful for testing the analysis.
> +class LazyCallGraphPrinterPass {
> +  raw_ostream &OS;
> +
> +public:
> +  explicit LazyCallGraphPrinterPass(raw_ostream &OS);
> +
> +  PreservedAnalyses run(Module *M, ModuleAnalysisManager *AM);
> +
> +  static StringRef name() { return "LazyCallGraphPrinterPass"; }
> +};
> +
> +}
> +
> +#endif
>
> Modified: llvm/trunk/lib/Analysis/CMakeLists.txt
> URL:
> http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Analysis/CMakeLists.txt?rev=200903&r1=200902&r2=200903&view=diff
>
> ==============================================================================
> --- llvm/trunk/lib/Analysis/CMakeLists.txt (original)
> +++ llvm/trunk/lib/Analysis/CMakeLists.txt Wed Feb  5 22:37:03 2014
> @@ -23,6 +23,7 @@ add_llvm_library(LLVMAnalysis
>    InstructionSimplify.cpp
>    Interval.cpp
>    IntervalPartition.cpp
> +  LazyCallGraph.cpp
>    LazyValueInfo.cpp
>    LibCallAliasAnalysis.cpp
>    LibCallSemantics.cpp
>
> Added: llvm/trunk/lib/Analysis/LazyCallGraph.cpp
> URL:
> http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Analysis/LazyCallGraph.cpp?rev=200903&view=auto
>
> ==============================================================================
> --- llvm/trunk/lib/Analysis/LazyCallGraph.cpp (added)
> +++ llvm/trunk/lib/Analysis/LazyCallGraph.cpp Wed Feb  5 22:37:03 2014
> @@ -0,0 +1,195 @@
> +//===- LazyCallGraph.cpp - Analysis of a Module's call graph
> --------------===//
> +//
> +//                     The LLVM Compiler Infrastructure
> +//
> +// This file is distributed under the University of Illinois Open Source
> +// License. See LICENSE.TXT for details.
> +//
>
> +//===----------------------------------------------------------------------===//
> +
> +#include "llvm/Analysis/LazyCallGraph.h"
> +#include "llvm/ADT/SCCIterator.h"
> +#include "llvm/IR/Instructions.h"
> +#include "llvm/IR/PassManager.h"
> +#include "llvm/Support/CallSite.h"
> +#include "llvm/Support/raw_ostream.h"
> +#include "llvm/InstVisitor.h"
> +
> +using namespace llvm;
> +
> +static void findCallees(
> +    SmallVectorImpl<Constant *> &Worklist, SmallPtrSetImpl<Constant *>
> &Visited,
> +    SmallVectorImpl<PointerUnion<Function *, LazyCallGraph::Node *> >
> &Callees,
> +    SmallPtrSetImpl<Function *> &CalleeSet) {
> +  while (!Worklist.empty()) {
> +    Constant *C = Worklist.pop_back_val();
> +
> +    if (Function *F = dyn_cast<Function>(C)) {
> +      // Note that we consider *any* function with a definition to be a
> viable
> +      // edge. Even if the function's definition is subject to
> replacement by
> +      // some other module (say, a weak definition) there may still be
> +      // optimizations which essentially speculate based on the
> definition and
> +      // a way to check that the specific definition is in fact the one
> being
> +      // used. For example, this could be done by moving the weak
> definition to
> +      // a strong (internal) definition and making the weak definition be
> an
> +      // alias. Then a test of the address of the weak function against
> the new
> +      // strong definition's address would be an effective way to
> determine the
> +      // safety of optimizing a direct call edge.
> +      if (!F->isDeclaration() && CalleeSet.insert(F))
> +          Callees.push_back(F);
> +      continue;
> +    }
> +
> +    for (User::value_op_iterator OI = C->value_op_begin(),
> +                                 OE = C->value_op_end();
> +         OI != OE; ++OI)
> +      if (Visited.insert(cast<Constant>(*OI)))
> +        Worklist.push_back(cast<Constant>(*OI));
> +  }
> +}
> +
> +LazyCallGraph::Node::Node(LazyCallGraph &G, Function &F) : G(G), F(F) {
> +  SmallVector<Constant *, 16> Worklist;
> +  SmallPtrSet<Constant *, 16> Visited;
> +  // Find all the potential callees in this function. First walk the
> +  // instructions and add every operand which is a constant to the
> worklist.
> +  for (Function::iterator BBI = F.begin(), BBE = F.end(); BBI != BBE;
> ++BBI)
> +    for (BasicBlock::iterator II = BBI->begin(), IE = BBI->end(); II !=
> IE;
> +         ++II)
> +      for (User::value_op_iterator OI = II->value_op_begin(),
> +                                   OE = II->value_op_end();
> +           OI != OE; ++OI)
> +        if (Constant *C = dyn_cast<Constant>(*OI))
> +          if (Visited.insert(C))
> +            Worklist.push_back(C);
> +
> +  // We've collected all the constant (and thus potentially function or
> +  // function containing) operands to all of the instructions in the
> function.
> +  // Process them (recursively) collecting every function found.
> +  findCallees(Worklist, Visited, Callees, CalleeSet);
> +}
> +
> +LazyCallGraph::Node::Node(LazyCallGraph &G, const Node &OtherN)
> +    : G(G), F(OtherN.F), CalleeSet(OtherN.CalleeSet) {
> +  // Loop over the other node's callees, adding the Function*s to our list
> +  // directly, and recursing to add the Node*s.
> +  Callees.reserve(OtherN.Callees.size());
> +  for (NodeVectorImplT::iterator OI = OtherN.Callees.begin(),
> +                                 OE = OtherN.Callees.end();
> +       OI != OE; ++OI)
> +    if (Function *Callee = OI->dyn_cast<Function *>())
> +      Callees.push_back(Callee);
> +    else
> +      Callees.push_back(G.copyInto(*OI->get<Node *>()));
> +}
> +
> +#if LLVM_HAS_RVALUE_REFERENCES
> +LazyCallGraph::Node::Node(LazyCallGraph &G, Node &&OtherN)
> +    : G(G), F(OtherN.F), Callees(std::move(OtherN.Callees)),
> +      CalleeSet(std::move(OtherN.CalleeSet)) {
> +  // Loop over our Callees. They've been moved from another node, but we
> need
> +  // to move the Node*s to live under our bump ptr allocator.
> +  for (NodeVectorImplT::iterator CI = Callees.begin(), CE = Callees.end();
> +       CI != CE; ++CI)
> +    if (Node *ChildN = CI->dyn_cast<Node *>())
> +      *CI = G.moveInto(std::move(*ChildN));
> +}
> +#endif
> +
> +LazyCallGraph::LazyCallGraph(Module &M) : M(M) {
> +  for (Module::iterator FI = M.begin(), FE = M.end(); FI != FE; ++FI)
> +    if (!FI->isDeclaration() && !FI->hasLocalLinkage())
> +      if (EntryNodeSet.insert(&*FI))
> +        EntryNodes.push_back(&*FI);
> +
> +  // Now add entry nodes for functions reachable via initializers to
> globals.
> +  SmallVector<Constant *, 16> Worklist;
> +  SmallPtrSet<Constant *, 16> Visited;
> +  for (Module::global_iterator GI = M.global_begin(), GE =
> M.global_end(); GI != GE; ++GI)
> +    if (GI->hasInitializer())
> +      if (Visited.insert(GI->getInitializer()))
> +        Worklist.push_back(GI->getInitializer());
> +
> +  findCallees(Worklist, Visited, EntryNodes, EntryNodeSet);
> +}
> +
> +LazyCallGraph::LazyCallGraph(const LazyCallGraph &G)
> +    : M(G.M), EntryNodeSet(G.EntryNodeSet) {
> +  EntryNodes.reserve(EntryNodes.size());
> +  for (NodeVectorImplT::iterator EI = EntryNodes.begin(),
> +                                 EE = EntryNodes.end();
> +       EI != EE; ++EI)
> +    if (Function *Callee = EI->dyn_cast<Function *>())
> +      EntryNodes.push_back(Callee);
> +    else
> +      EntryNodes.push_back(copyInto(*EI->get<Node *>()));
> +}
> +
> +#if LLVM_HAS_RVALUE_REFERENCES
> +// FIXME: This would be crazy simpler if BumpPtrAllocator were movable
> without
> +// invalidating any of the allocated memory. We should make that be the
> case at
> +// some point and delete this.
> +LazyCallGraph::LazyCallGraph(LazyCallGraph &&G)
> +    : M(G.M), EntryNodes(std::move(G.EntryNodes)),
> +      EntryNodeSet(std::move(G.EntryNodeSet)) {
> +  // Loop over our EntryNodes. They've been moved from another graph, but
> we
> +  // need to move the Node*s to live under our bump ptr allocator.
> +  for (NodeVectorImplT::iterator EI = EntryNodes.begin(), EE =
> EntryNodes.end();
> +       EI != EE; ++EI)
> +    if (Node *EntryN = EI->dyn_cast<Node *>())
> +      *EI = G.moveInto(std::move(*EntryN));
> +}
> +#endif
> +
> +LazyCallGraph::Node *LazyCallGraph::insertInto(Function &F, Node
> *&MappedN) {
> +  return new (MappedN = BPA.Allocate()) Node(*this, F);
> +}
> +
> +LazyCallGraph::Node *LazyCallGraph::copyInto(const Node &OtherN) {
> +  Node *&N = NodeMap[&OtherN.F];
> +  if (N)
> +    return N;
> +
> +  return new (N = BPA.Allocate()) Node(*this, OtherN);
> +}
> +
> +#if LLVM_HAS_RVALUE_REFERENCES
> +LazyCallGraph::Node *LazyCallGraph::moveInto(Node &&OtherN) {
> +  Node *&N = NodeMap[&OtherN.F];
> +  if (N)
> +    return N;
> +
> +  return new (N = BPA.Allocate()) Node(*this, std::move(OtherN));
> +}
> +#endif
> +
> +char LazyCallGraphAnalysis::PassID;
> +
> +LazyCallGraphPrinterPass::LazyCallGraphPrinterPass(raw_ostream &OS) :
> OS(OS) {}
> +
> +static void printNodes(raw_ostream &OS, LazyCallGraph::Node &N,
> +                       SmallPtrSetImpl<LazyCallGraph::Node *> &Printed) {
> +  // Recurse depth first through the nodes.
> +  for (LazyCallGraph::iterator I = N.begin(), E = N.end(); I != E; ++I)
> +    if (Printed.insert(*I))
> +      printNodes(OS, **I, Printed);
> +
> +  OS << "  Call edges in function: " << N.getFunction().getName() << "\n";
> +  for (LazyCallGraph::iterator I = N.begin(), E = N.end(); I != E; ++I)
> +    OS << "    -> " << I->getFunction().getName() << "\n";
> +
> +  OS << "\n";
> +}
> +
> +PreservedAnalyses LazyCallGraphPrinterPass::run(Module *M,
> ModuleAnalysisManager *AM) {
> +  LazyCallGraph &G = AM->getResult<LazyCallGraphAnalysis>(M);
> +
> +  OS << "Printing the call graph for module: " <<
> M->getModuleIdentifier() << "\n\n";
> +
> +  SmallPtrSet<LazyCallGraph::Node *, 16> Printed;
> +  for (LazyCallGraph::iterator I = G.begin(), E = G.end(); I != E; ++I)
> +    if (Printed.insert(*I))
> +      printNodes(OS, **I, Printed);
> +
> +  return PreservedAnalyses::all();
> +}
>
> Added: llvm/trunk/test/Analysis/LazyCallGraph/basic.ll
> URL:
> http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Analysis/LazyCallGraph/basic.ll?rev=200903&view=auto
>
> ==============================================================================
> --- llvm/trunk/test/Analysis/LazyCallGraph/basic.ll (added)
> +++ llvm/trunk/test/Analysis/LazyCallGraph/basic.ll Wed Feb  5 22:37:03
> 2014
> @@ -0,0 +1,126 @@
> +; RUN: opt -disable-output -passes=print-cg %s 2>&1 | FileCheck %s
> +;
> +; Basic validation of the call graph analysis used in the new pass
> manager.
> +
> +define void @f() {
> +; CHECK-LABEL: Call edges in function: f
> +; CHECK-NOT: ->
> +
> +entry:
> +  ret void
> +}
> +
> +; A bunch more functions just to make it easier to test several call
> edges at once.
> +define void @f1() {
> +  ret void
> +}
> +define void @f2() {
> +  ret void
> +}
> +define void @f3() {
> +  ret void
> +}
> +define void @f4() {
> +  ret void
> +}
> +define void @f5() {
> +  ret void
> +}
> +define void @f6() {
> +  ret void
> +}
> +define void @f7() {
> +  ret void
> +}
> +define void @f8() {
> +  ret void
> +}
> +define void @f9() {
> +  ret void
> +}
> +define void @f10() {
> +  ret void
> +}
> +define void @f11() {
> +  ret void
> +}
> +define void @f12() {
> +  ret void
> +}
> +
> +declare i32 @__gxx_personality_v0(...)
> +
> +define void @test0() {
> +; CHECK-LABEL: Call edges in function: test0
> +; CHECK-NEXT: -> f
> +; CHECK-NOT: ->
> +
> +entry:
> +  call void @f()
> +  call void @f()
> +  call void @f()
> +  call void @f()
> +  ret void
> +}
> +
> +define void ()* @test1(void ()** %x) {
> +; CHECK-LABEL: Call edges in function: test1
> +; CHECK-NEXT: -> f12
> +; CHECK-NEXT: -> f11
> +; CHECK-NEXT: -> f10
> +; CHECK-NEXT: -> f7
> +; CHECK-NEXT: -> f9
> +; CHECK-NEXT: -> f8
> +; CHECK-NEXT: -> f6
> +; CHECK-NEXT: -> f5
> +; CHECK-NEXT: -> f4
> +; CHECK-NEXT: -> f3
> +; CHECK-NEXT: -> f2
> +; CHECK-NEXT: -> f1
> +; CHECK-NOT: ->
> +
> +entry:
> +  br label %next
> +
> +dead:
> +  br label %next
> +
> +next:
> +  phi void ()* [ @f1, %entry ], [ @f2, %dead ]
> +  select i1 true, void ()* @f3, void ()* @f4
> +  store void ()* @f5, void ()** %x
> +  call void @f6()
> +  call void (void ()*, void ()*)* bitcast (void ()* @f7 to void (void
> ()*, void ()*)*)(void ()* @f8, void ()* @f9)
> +  invoke void @f10() to label %exit unwind label %unwind
> +
> +exit:
> +  ret void ()* @f11
> +
> +unwind:
> +  %res = landingpad { i8*, i32 } personality i32 (...)*
> @__gxx_personality_v0
> +          cleanup
> +  resume { i8*, i32 } { i8* bitcast (void ()* @f12 to i8*), i32 42 }
> +}
> +
> + at g = global void ()* @f1
> + at g1 = global [4 x void ()*] [void ()* @f2, void ()* @f3, void ()* @f4,
> void ()* @f5]
> + at g2 = global {i8, void ()*, i8} {i8 1, void ()* @f6, i8 2}
> + at h = constant void ()* @f7
> +
> +define void @test2() {
> +; CHECK-LABEL: Call edges in function: test2
> +; CHECK-NEXT: -> f7
> +; CHECK-NEXT: -> f6
> +; CHECK-NEXT: -> f5
> +; CHECK-NEXT: -> f4
> +; CHECK-NEXT: -> f3
> +; CHECK-NEXT: -> f2
> +; CHECK-NEXT: -> f1
> +; CHECK-NOT: ->
> +
> +  load i8** bitcast (void ()** @g to i8**)
> +  load i8** bitcast (void ()** getelementptr ([4 x void ()*]* @g1, i32 0,
> i32 2) to i8**)
> +  load i8** bitcast (void ()** getelementptr ({i8, void ()*, i8}* @g2,
> i32 0, i32 1) to i8**)
> +  load i8** bitcast (void ()** @h to i8**)
> +  ret void
> +}
>
> Modified: llvm/trunk/tools/opt/NewPMDriver.cpp
> URL:
> http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/opt/NewPMDriver.cpp?rev=200903&r1=200902&r2=200903&view=diff
>
> ==============================================================================
> --- llvm/trunk/tools/opt/NewPMDriver.cpp (original)
> +++ llvm/trunk/tools/opt/NewPMDriver.cpp Wed Feb  5 22:37:03 2014
> @@ -16,6 +16,7 @@
>  #include "NewPMDriver.h"
>  #include "Passes.h"
>  #include "llvm/ADT/StringRef.h"
> +#include "llvm/Analysis/LazyCallGraph.h"
>  #include "llvm/Bitcode/BitcodeWriterPass.h"
>  #include "llvm/IR/IRPrintingPasses.h"
>  #include "llvm/IR/LLVMContext.h"
> @@ -35,6 +36,10 @@ bool llvm::runPassPipeline(StringRef Arg
>    FunctionAnalysisManager FAM;
>    ModuleAnalysisManager MAM;
>
> +  // FIXME: Lift this registration of analysis passes into a .def file
> adjacent
> +  // to the one used to associate names with passes.
> +  MAM.registerPass(LazyCallGraphAnalysis());
> +
>    // Cross register the analysis managers through their proxies.
>    MAM.registerPass(FunctionAnalysisManagerModuleProxy(FAM));
>    FAM.registerPass(ModuleAnalysisManagerFunctionProxy(MAM));
>
> Modified: llvm/trunk/tools/opt/Passes.cpp
> URL:
> http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/opt/Passes.cpp?rev=200903&r1=200902&r2=200903&view=diff
>
> ==============================================================================
> --- llvm/trunk/tools/opt/Passes.cpp (original)
> +++ llvm/trunk/tools/opt/Passes.cpp Wed Feb  5 22:37:03 2014
> @@ -15,6 +15,7 @@
>
>  //===----------------------------------------------------------------------===//
>
>  #include "Passes.h"
> +#include "llvm/Analysis/LazyCallGraph.h"
>  #include "llvm/IR/IRPrintingPasses.h"
>  #include "llvm/IR/PassManager.h"
>  #include "llvm/IR/Verifier.h"
> @@ -43,6 +44,7 @@ struct NoOpFunctionPass {
>  static bool isModulePassName(StringRef Name) {
>    if (Name == "no-op-module") return true;
>    if (Name == "print") return true;
> +  if (Name == "print-cg") return true;
>
>    return false;
>  }
> @@ -63,6 +65,10 @@ static bool parseModulePassName(ModulePa
>      MPM.addPass(PrintModulePass(dbgs()));
>      return true;
>    }
> +  if (Name == "print-cg") {
> +    MPM.addPass(LazyCallGraphPrinterPass(dbgs()));
> +    return true;
> +  }
>    return false;
>  }
>
>
>
> _______________________________________________
> llvm-commits mailing list
> llvm-commits at cs.uiuc.edu
> http://lists.cs.uiuc.edu/mailman/listinfo/llvm-commits
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-commits/attachments/20140206/e2c60961/attachment.html>


More information about the llvm-commits mailing list