diff options
| author | Ed Schouten <ed@FreeBSD.org> | 2009-06-02 17:52:33 +0000 | 
|---|---|---|
| committer | Ed Schouten <ed@FreeBSD.org> | 2009-06-02 17:52:33 +0000 | 
| commit | 009b1c42aa6266385f2c37e227516b24077e6dd7 (patch) | |
| tree | 64ba909838c23261cace781ece27d106134ea451 /lib/CompilerDriver/CompilationGraph.cpp | |
Notes
Diffstat (limited to 'lib/CompilerDriver/CompilationGraph.cpp')
| -rw-r--r-- | lib/CompilerDriver/CompilationGraph.cpp | 536 | 
1 files changed, 536 insertions, 0 deletions
| diff --git a/lib/CompilerDriver/CompilationGraph.cpp b/lib/CompilerDriver/CompilationGraph.cpp new file mode 100644 index 0000000000000..dece4e8e0ae33 --- /dev/null +++ b/lib/CompilerDriver/CompilationGraph.cpp @@ -0,0 +1,536 @@ +//===--- CompilationGraph.cpp - The LLVM Compiler Driver --------*- C++ -*-===// +// +//                     The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open +// Source License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +//  Compilation graph - implementation. +// +//===----------------------------------------------------------------------===// + +#include "llvm/CompilerDriver/CompilationGraph.h" +#include "llvm/CompilerDriver/Error.h" + +#include "llvm/ADT/STLExtras.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/DOTGraphTraits.h" +#include "llvm/Support/GraphWriter.h" + +#include <algorithm> +#include <cstring> +#include <iostream> +#include <iterator> +#include <limits> +#include <queue> +#include <stdexcept> + +using namespace llvm; +using namespace llvmc; + +extern cl::list<std::string> InputFilenames; +extern cl::list<std::string> Languages; + +namespace llvmc { + +  const std::string& LanguageMap::GetLanguage(const sys::Path& File) const { +    LanguageMap::const_iterator Lang = this->find(File.getSuffix()); +    if (Lang == this->end()) +      throw std::runtime_error("Unknown suffix: " + File.getSuffix()); +    return Lang->second; +  } +} + +namespace { + +  /// ChooseEdge - Return the edge with the maximum weight. +  template <class C> +  const Edge* ChooseEdge(const C& EdgesContainer, +                         const InputLanguagesSet& InLangs, +                         const std::string& NodeName = "root") { +    const Edge* MaxEdge = 0; +    unsigned MaxWeight = 0; +    bool SingleMax = true; + +    for (typename C::const_iterator B = EdgesContainer.begin(), +           E = EdgesContainer.end(); B != E; ++B) { +      const Edge* e = B->getPtr(); +      unsigned EW = e->Weight(InLangs); +      if (EW > MaxWeight) { +        MaxEdge = e; +        MaxWeight = EW; +        SingleMax = true; +      } else if (EW == MaxWeight) { +        SingleMax = false; +      } +    } + +    if (!SingleMax) +      throw std::runtime_error("Node " + NodeName + +                               ": multiple maximal outward edges found!" +                               " Most probably a specification error."); +    if (!MaxEdge) +      throw std::runtime_error("Node " + NodeName + +                               ": no maximal outward edge found!" +                               " Most probably a specification error."); +    return MaxEdge; +  } + +} + +void Node::AddEdge(Edge* Edg) { +  // If there already was an edge between two nodes, modify it instead +  // of adding a new edge. +  const std::string& ToolName = Edg->ToolName(); +  for (container_type::iterator B = OutEdges.begin(), E = OutEdges.end(); +       B != E; ++B) { +    if ((*B)->ToolName() == ToolName) { +      llvm::IntrusiveRefCntPtr<Edge>(Edg).swap(*B); +      return; +    } +  } +  OutEdges.push_back(llvm::IntrusiveRefCntPtr<Edge>(Edg)); +} + +CompilationGraph::CompilationGraph() { +  NodesMap["root"] = Node(this); +} + +Node& CompilationGraph::getNode(const std::string& ToolName) { +  nodes_map_type::iterator I = NodesMap.find(ToolName); +  if (I == NodesMap.end()) +    throw std::runtime_error("Node " + ToolName + " is not in the graph"); +  return I->second; +} + +const Node& CompilationGraph::getNode(const std::string& ToolName) const { +  nodes_map_type::const_iterator I = NodesMap.find(ToolName); +  if (I == NodesMap.end()) +    throw std::runtime_error("Node " + ToolName + " is not in the graph!"); +  return I->second; +} + +// Find the tools list corresponding to the given language name. +const CompilationGraph::tools_vector_type& +CompilationGraph::getToolsVector(const std::string& LangName) const +{ +  tools_map_type::const_iterator I = ToolsMap.find(LangName); +  if (I == ToolsMap.end()) +    throw std::runtime_error("No tool corresponding to the language " +                             + LangName + " found"); +  return I->second; +} + +void CompilationGraph::insertNode(Tool* V) { +  if (NodesMap.count(V->Name()) == 0) +    NodesMap[V->Name()] = Node(this, V); +} + +void CompilationGraph::insertEdge(const std::string& A, Edge* Edg) { +  Node& B = getNode(Edg->ToolName()); +  if (A == "root") { +    const char** InLangs = B.ToolPtr->InputLanguages(); +    for (;*InLangs; ++InLangs) +      ToolsMap[*InLangs].push_back(IntrusiveRefCntPtr<Edge>(Edg)); +    NodesMap["root"].AddEdge(Edg); +  } +  else { +    Node& N = getNode(A); +    N.AddEdge(Edg); +  } +  // Increase the inward edge counter. +  B.IncrInEdges(); +} + +// Pass input file through the chain until we bump into a Join node or +// a node that says that it is the last. +void CompilationGraph::PassThroughGraph (const sys::Path& InFile, +                                         const Node* StartNode, +                                         const InputLanguagesSet& InLangs, +                                         const sys::Path& TempDir, +                                         const LanguageMap& LangMap) const { +  sys::Path In = InFile; +  const Node* CurNode = StartNode; + +  while(true) { +    Tool* CurTool = CurNode->ToolPtr.getPtr(); + +    if (CurTool->IsJoin()) { +      JoinTool& JT = dynamic_cast<JoinTool&>(*CurTool); +      JT.AddToJoinList(In); +      break; +    } + +    Action CurAction = CurTool->GenerateAction(In, CurNode->HasChildren(), +                                               TempDir, InLangs, LangMap); + +    if (int ret = CurAction.Execute()) +      throw error_code(ret); + +    if (CurAction.StopCompilation()) +      return; + +    CurNode = &getNode(ChooseEdge(CurNode->OutEdges, +                                  InLangs, +                                  CurNode->Name())->ToolName()); +    In = CurAction.OutFile(); +  } +} + +// Find the head of the toolchain corresponding to the given file. +// Also, insert an input language into InLangs. +const Node* CompilationGraph:: +FindToolChain(const sys::Path& In, const std::string* ForceLanguage, +              InputLanguagesSet& InLangs, const LanguageMap& LangMap) const { + +  // Determine the input language. +  const std::string& InLanguage = +    ForceLanguage ? *ForceLanguage : LangMap.GetLanguage(In); + +  // Add the current input language to the input language set. +  InLangs.insert(InLanguage); + +  // Find the toolchain for the input language. +  const tools_vector_type& TV = getToolsVector(InLanguage); +  if (TV.empty()) +    throw std::runtime_error("No toolchain corresponding to language " +                             + InLanguage + " found"); +  return &getNode(ChooseEdge(TV, InLangs)->ToolName()); +} + +// Helper function used by Build(). +// Traverses initial portions of the toolchains (up to the first Join node). +// This function is also responsible for handling the -x option. +void CompilationGraph::BuildInitial (InputLanguagesSet& InLangs, +                                     const sys::Path& TempDir, +                                     const LanguageMap& LangMap) { +  // This is related to -x option handling. +  cl::list<std::string>::const_iterator xIter = Languages.begin(), +    xBegin = xIter, xEnd = Languages.end(); +  bool xEmpty = true; +  const std::string* xLanguage = 0; +  unsigned xPos = 0, xPosNext = 0, filePos = 0; + +  if (xIter != xEnd) { +    xEmpty = false; +    xPos = Languages.getPosition(xIter - xBegin); +    cl::list<std::string>::const_iterator xNext = llvm::next(xIter); +    xPosNext = (xNext == xEnd) ? std::numeric_limits<unsigned>::max() +      : Languages.getPosition(xNext - xBegin); +    xLanguage = (*xIter == "none") ? 0 : &(*xIter); +  } + +  // For each input file: +  for (cl::list<std::string>::const_iterator B = InputFilenames.begin(), +         CB = B, E = InputFilenames.end(); B != E; ++B) { +    sys::Path In = sys::Path(*B); + +    // Code for handling the -x option. +    // Output: std::string* xLanguage (can be NULL). +    if (!xEmpty) { +      filePos = InputFilenames.getPosition(B - CB); + +      if (xPos < filePos) { +        if (filePos < xPosNext) { +          xLanguage = (*xIter == "none") ? 0 : &(*xIter); +        } +        else { // filePos >= xPosNext +          // Skip xIters while filePos > xPosNext +          while (filePos > xPosNext) { +            ++xIter; +            xPos = xPosNext; + +            cl::list<std::string>::const_iterator xNext = llvm::next(xIter); +            if (xNext == xEnd) +              xPosNext = std::numeric_limits<unsigned>::max(); +            else +              xPosNext = Languages.getPosition(xNext - xBegin); +            xLanguage = (*xIter == "none") ? 0 : &(*xIter); +          } +        } +      } +    } + +    // Find the toolchain corresponding to this file. +    const Node* N = FindToolChain(In, xLanguage, InLangs, LangMap); +    // Pass file through the chain starting at head. +    PassThroughGraph(In, N, InLangs, TempDir, LangMap); +  } +} + +// Sort the nodes in topological order. +void CompilationGraph::TopologicalSort(std::vector<const Node*>& Out) { +  std::queue<const Node*> Q; +  Q.push(&getNode("root")); + +  while (!Q.empty()) { +    const Node* A = Q.front(); +    Q.pop(); +    Out.push_back(A); +    for (Node::const_iterator EB = A->EdgesBegin(), EE = A->EdgesEnd(); +         EB != EE; ++EB) { +      Node* B = &getNode((*EB)->ToolName()); +      B->DecrInEdges(); +      if (B->HasNoInEdges()) +        Q.push(B); +    } +  } +} + +namespace { +  bool NotJoinNode(const Node* N) { +    return N->ToolPtr ? !N->ToolPtr->IsJoin() : true; +  } +} + +// Call TopologicalSort and filter the resulting list to include +// only Join nodes. +void CompilationGraph:: +TopologicalSortFilterJoinNodes(std::vector<const Node*>& Out) { +  std::vector<const Node*> TopSorted; +  TopologicalSort(TopSorted); +  std::remove_copy_if(TopSorted.begin(), TopSorted.end(), +                      std::back_inserter(Out), NotJoinNode); +} + +int CompilationGraph::Build (const sys::Path& TempDir, +                             const LanguageMap& LangMap) { + +  InputLanguagesSet InLangs; + +  // Traverse initial parts of the toolchains and fill in InLangs. +  BuildInitial(InLangs, TempDir, LangMap); + +  std::vector<const Node*> JTV; +  TopologicalSortFilterJoinNodes(JTV); + +  // For all join nodes in topological order: +  for (std::vector<const Node*>::iterator B = JTV.begin(), E = JTV.end(); +       B != E; ++B) { + +    const Node* CurNode = *B; +    JoinTool* JT = &dynamic_cast<JoinTool&>(*CurNode->ToolPtr.getPtr()); + +    // Are there any files in the join list? +    if (JT->JoinListEmpty()) +      continue; + +    Action CurAction = JT->GenerateAction(CurNode->HasChildren(), +                                          TempDir, InLangs, LangMap); + +    if (int ret = CurAction.Execute()) +      throw error_code(ret); + +    if (CurAction.StopCompilation()) +      return 0; + +    const Node* NextNode = &getNode(ChooseEdge(CurNode->OutEdges, InLangs, +                                               CurNode->Name())->ToolName()); +    PassThroughGraph(sys::Path(CurAction.OutFile()), NextNode, +                     InLangs, TempDir, LangMap); +  } + +  return 0; +} + +int CompilationGraph::CheckLanguageNames() const { +  int ret = 0; +  // Check that names for output and input languages on all edges do match. +  for (const_nodes_iterator B = this->NodesMap.begin(), +         E = this->NodesMap.end(); B != E; ++B) { + +    const Node & N1 = B->second; +    if (N1.ToolPtr) { +      for (Node::const_iterator EB = N1.EdgesBegin(), EE = N1.EdgesEnd(); +           EB != EE; ++EB) { +        const Node& N2 = this->getNode((*EB)->ToolName()); + +        if (!N2.ToolPtr) { +          ++ret; +          std::cerr << "Error: there is an edge from '" << N1.ToolPtr->Name() +                    << "' back to the root!\n\n"; +          continue; +        } + +        const char* OutLang = N1.ToolPtr->OutputLanguage(); +        const char** InLangs = N2.ToolPtr->InputLanguages(); +        bool eq = false; +        for (;*InLangs; ++InLangs) { +          if (std::strcmp(OutLang, *InLangs) == 0) { +            eq = true; +            break; +          } +        } + +        if (!eq) { +          ++ret; +          std::cerr << "Error: Output->input language mismatch in the edge '" << +            N1.ToolPtr->Name() << "' -> '" << N2.ToolPtr->Name() << "'!\n"; + +          std::cerr << "Expected one of { "; + +          InLangs = N2.ToolPtr->InputLanguages(); +          for (;*InLangs; ++InLangs) { +            std::cerr << '\'' << *InLangs << (*(InLangs+1) ? "', " : "'"); +          } + +          std::cerr << " }, but got '" << OutLang << "'!\n\n"; +        } + +      } +    } +  } + +  return ret; +} + +int CompilationGraph::CheckMultipleDefaultEdges() const { +  int ret = 0; +  InputLanguagesSet Dummy; + +  // For all nodes, just iterate over the outgoing edges and check if there is +  // more than one edge with maximum weight. +  for (const_nodes_iterator B = this->NodesMap.begin(), +         E = this->NodesMap.end(); B != E; ++B) { +    const Node& N = B->second; +    unsigned MaxWeight = 0; + +    // Ignore the root node. +    if (!N.ToolPtr) +      continue; + +    for (Node::const_iterator EB = N.EdgesBegin(), EE = N.EdgesEnd(); +         EB != EE; ++EB) { +      unsigned EdgeWeight = (*EB)->Weight(Dummy); +      if (EdgeWeight > MaxWeight) { +        MaxWeight = EdgeWeight; +      } +      else if (EdgeWeight == MaxWeight) { +        ++ret; +        std::cerr +          << "Error: there are multiple maximal edges stemming from the '" +          << N.ToolPtr->Name() << "' node!\n\n"; +        break; +      } +    } +  } + +  return ret; +} + +int CompilationGraph::CheckCycles() { +  unsigned deleted = 0; +  std::queue<Node*> Q; +  Q.push(&getNode("root")); + +  // Try to delete all nodes that have no ingoing edges, starting from the +  // root. If there are any nodes left after this operation, then we have a +  // cycle. This relies on '--check-graph' not performing the topological sort. +  while (!Q.empty()) { +    Node* A = Q.front(); +    Q.pop(); +    ++deleted; + +    for (Node::iterator EB = A->EdgesBegin(), EE = A->EdgesEnd(); +         EB != EE; ++EB) { +      Node* B = &getNode((*EB)->ToolName()); +      B->DecrInEdges(); +      if (B->HasNoInEdges()) +        Q.push(B); +    } +  } + +  if (deleted != NodesMap.size()) { +    std::cerr << "Error: there are cycles in the compilation graph!\n" +              << "Try inspecting the diagram produced by " +      "'llvmc --view-graph'.\n\n"; +    return 1; +  } + +  return 0; +} + +int CompilationGraph::Check () { +  // We try to catch as many errors as we can in one go. +  int ret = 0; + +  // Check that output/input language names match. +  ret += this->CheckLanguageNames(); + +  // Check for multiple default edges. +  ret += this->CheckMultipleDefaultEdges(); + +  // Check for cycles. +  ret += this->CheckCycles(); + +  return ret; +} + +// Code related to graph visualization. + +namespace llvm { +  template <> +  struct DOTGraphTraits<llvmc::CompilationGraph*> +    : public DefaultDOTGraphTraits +  { + +    template<typename GraphType> +    static std::string getNodeLabel(const Node* N, const GraphType&) +    { +      if (N->ToolPtr) +        if (N->ToolPtr->IsJoin()) +          return N->Name() + "\n (join" + +            (N->HasChildren() ? ")" +             : std::string(": ") + N->ToolPtr->OutputLanguage() + ')'); +        else +          return N->Name(); +      else +        return "root"; +    } + +    template<typename EdgeIter> +    static std::string getEdgeSourceLabel(const Node* N, EdgeIter I) { +      if (N->ToolPtr) { +        return N->ToolPtr->OutputLanguage(); +      } +      else { +        const char** InLangs = I->ToolPtr->InputLanguages(); +        std::string ret; + +        for (; *InLangs; ++InLangs) { +          if (*(InLangs + 1)) { +            ret += *InLangs; +            ret +=  ", "; +          } +          else { +            ret += *InLangs; +          } +        } + +        return ret; +      } +    } +  }; + +} + +void CompilationGraph::writeGraph(const std::string& OutputFilename) { +  std::ofstream O(OutputFilename.c_str()); + +  if (O.good()) { +    std::cerr << "Writing '"<< OutputFilename << "' file..."; +    llvm::WriteGraph(O, this); +    std::cerr << "done.\n"; +    O.close(); +  } +  else { +    throw std::runtime_error("Error opening file '" + OutputFilename +                             + "' for writing!"); +  } +} + +void CompilationGraph::viewGraph() { +  llvm::ViewGraph(this, "compilation-graph"); +} | 
