Convert internal representation to use DAG. This gives us more flexibility and enables future improvements.


git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@50724 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/tools/llvmc2/CompilationGraph.cpp b/tools/llvmc2/CompilationGraph.cpp
index 36e5fc7..bff1aa5 100644
--- a/tools/llvmc2/CompilationGraph.cpp
+++ b/tools/llvmc2/CompilationGraph.cpp
@@ -14,46 +14,120 @@
 #include "CompilationGraph.h"
 
 #include "llvm/Support/CommandLine.h"
-#include "llvm/ADT/STLExtras.h"
+#include "llvm/Support/DOTGraphTraits.h"
+#include "llvm/Support/GraphWriter.h"
 
 #include <stdexcept>
 
 using namespace llvm;
+using namespace llvmcc;
 
 extern cl::list<std::string> InputFilenames;
 extern cl::opt<std::string> OutputFilename;
 
-int llvmcc::CompilationGraph::Build (const sys::Path& tempDir) const {
-  sys::Path In(InputFilenames.at(0)), Out;
+CompilationGraph::CompilationGraph() {
+  NodesMap["root"] = Node(this);
+}
 
-  // Find out which language corresponds to the suffix of the first input file
-  LanguageMap::const_iterator Lang = ExtsToLangs.find(In.getSuffix());
+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 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 graph!");
+  return I->second;
+}
+
+const std::string& CompilationGraph::getLanguage(const sys::Path& File) const {
+  LanguageMap::const_iterator Lang = ExtsToLangs.find(File.getSuffix());
   if (Lang == ExtsToLangs.end())
-    throw std::runtime_error("Unknown suffix!");
+    throw std::runtime_error("Unknown suffix: " + File.getSuffix() + '!');
+  return Lang->second;
+}
 
-  // Find the toolchain corresponding to this language
-  ToolChainMap::const_iterator ToolsIt = ToolChains.find(Lang->second);
-  if (ToolsIt == ToolChains.end())
-    throw std::runtime_error("Unknown language!");
-  ToolChain Tools = ToolsIt->second;
+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 tools corresponding to " + LangName
+                             + " found!");
+  return I->second;
+}
 
+void CompilationGraph::insertVertex(const IntrusiveRefCntPtr<Tool> V) {
+  if (!NodesMap.count(V->Name())) {
+    Node N;
+    N.OwningGraph = this;
+    N.ToolPtr = V;
+    NodesMap[V->Name()] = N;
+  }
+}
+
+void CompilationGraph::insertEdge(const std::string& A,
+                                  const std::string& B) {
+  // TOTHINK: check this at compile-time?
+  if (B == "root")
+    throw std::runtime_error("Edges back to the root are not allowed!"
+                             "Compilation graph should be acyclic!");
+
+  if (A == "root") {
+    const Node& N = getNode(B);
+    const std::string& InputLanguage = N.ToolPtr->InputLanguage();
+    ToolsMap[InputLanguage].push_back(B);
+
+    // Needed to support iteration via GraphTraits.
+    NodesMap["root"].Children.push_back(B);
+  }
+  else {
+    Node& NA = getNode(A);
+    // Check that there is a node at B.
+    getNode(B);
+    NA.Children.push_back(B);
+  }
+}
+
+// TOFIX: extend, add an ability to choose between different
+// toolchains, support more interesting graph topologies.
+int CompilationGraph::Build (const sys::Path& tempDir) const {
   PathVector JoinList;
+  const Tool* JoinTool = 0;
+  sys::Path In, Out;
 
+  // For each input file
   for (cl::list<std::string>::const_iterator B = InputFilenames.begin(),
         E = InputFilenames.end(); B != E; ++B) {
     In = sys::Path(*B);
 
-    // Pass input file through the toolchain
-    for (ToolChain::const_iterator B = Tools.begin(), E = Tools.end();
-         B != E; ++B) {
+    // Get to the head of the toolchain.
+    const tools_vector_type& TV = getToolsVector(getLanguage(In));
+    if(TV.empty())
+      throw std::runtime_error("Tool names vector is empty!");
+    const Node* N = &getNode(*TV.begin());
 
-      const Tool* CurTool = B->getPtr();
+    // Pass it through the chain until we bump into a Join node or a
+    // node that says that it is the last.
+    bool Last = false;
+    while(!Last) {
+      const Tool* CurTool = N->ToolPtr.getPtr();
 
-      // Is this the last step in the chain?
-      if (llvm::next(B) == E || CurTool->IsLast()) {
+      if(CurTool->IsJoin()) {
         JoinList.push_back(In);
+        JoinTool = CurTool;
         break;
       }
+
+      // Is this the last tool?
+      if (N->Children.empty() || CurTool->IsLast()) {
+        Out.appendComponent(In.getBasename());
+        Out.appendSuffix(CurTool->OutputSuffix());
+        Last = true;
+      }
       else {
         Out = tempDir;
         Out.appendComponent(In.getBasename());
@@ -65,24 +139,57 @@
       if (CurTool->GenerateAction(In, Out).Execute() != 0)
         throw std::runtime_error("Tool returned error code!");
 
+      N = &getNode(*N->Children.begin());
       In = Out; Out.clear();
     }
   }
 
-  // Pass .o files to linker
-  const Tool* JoinNode = (--Tools.end())->getPtr();
+  if(JoinTool) {
+    // If the final output name is empty, set it to "a.out"
+    if (!OutputFilename.empty()) {
+      Out = sys::Path(OutputFilename);
+    }
+    else {
+      Out = sys::Path("a");
+      Out.appendSuffix(JoinTool->OutputSuffix());
+    }
 
-  // If the final output name is empty, set it to "a.out"
-  if (!OutputFilename.empty()) {
-    Out = sys::Path(OutputFilename);
+    if (JoinTool->GenerateAction(JoinList, Out).Execute() != 0)
+      throw std::runtime_error("Tool returned error code!");
   }
-  else {
-    Out = sys::Path("a");
-    Out.appendSuffix(JoinNode->OutputSuffix());
-  }
-
-  if (JoinNode->GenerateAction(JoinList, Out).Execute() != 0)
-    throw std::runtime_error("Tool returned error code!");
 
   return 0;
 }
+
+namespace llvm {
+  template <>
+  struct DOTGraphTraits<llvmcc::CompilationGraph*>
+    : public DefaultDOTGraphTraits
+  {
+
+  template<typename GraphType>
+  static std::string getNodeLabel(const Node* N, const GraphType&) {
+    if (N->ToolPtr)
+      return N->ToolPtr->Name();
+    else
+      return "root";
+  }
+
+  };
+}
+
+void CompilationGraph::writeGraph() {
+  std::ofstream O("CompilationGraph.dot");
+
+  if(O.good()) {
+    llvm::WriteGraph(this, "CompilationGraph");
+    O.close();
+  }
+  else {
+    throw std::runtime_error("");
+  }
+}
+
+void CompilationGraph::viewGraph() {
+  llvm::ViewGraph(this, "CompilationGraph");
+}