Add a --check-graph option to llvmc.
authorMikhail Glushenkov <foldr@codedgers.com>
Fri, 9 Jan 2009 16:16:27 +0000 (16:16 +0000)
committerMikhail Glushenkov <foldr@codedgers.com>
Fri, 9 Jan 2009 16:16:27 +0000 (16:16 +0000)
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@61989 91177308-0d34-0410-b5e6-96231b3b80d8

docs/CommandGuide/llvmc.pod
docs/CompilerDriver.html
include/llvm/CompilerDriver/CompilationGraph.h
tools/llvmc/doc/LLVMC-Reference.rst
tools/llvmc/driver/CompilationGraph.cpp
tools/llvmc/driver/llvmc.cpp

index 7bfc3d71cbac70b036a7f6818dd37722a89bb672..17d85d738901235e963ccef1dc520b6b06bfd905 100644 (file)
@@ -42,6 +42,12 @@ S<-load $LLVM_DIR/Release/lib/LLVMCSimple.so>.
 
 Enable verbose mode, i.e. print out all executed commands.
 
+=item B<--check-graph>
+
+Check the compilation for common errors like mismatched output/input
+language names, multiple default edges and cycles. Hidden option,
+useful for debugging.
+
 =item B<--view-graph>
 
 Show a graphical representation of the compilation graph. Requires
index 7d0399016d0dde1b3f48ac339efbc1b1b841ed39..e49b2e98ae26af4d304b15d19aa48b1a4ef9a5ca 100644 (file)
@@ -107,6 +107,9 @@ until the next -x option.</li>
 <li><tt class="docutils literal"><span class="pre">-load</span> <span class="pre">PLUGIN_NAME</span></tt> - Load the specified plugin DLL. Example:
 <tt class="docutils literal"><span class="pre">-load</span> <span class="pre">$LLVM_DIR/Release/lib/LLVMCSimple.so</span></tt>.</li>
 <li><tt class="docutils literal"><span class="pre">-v</span></tt> - Enable verbose mode, i.e. print out all executed commands.</li>
+<li><tt class="docutils literal"><span class="pre">--check-graph</span></tt> - Check the compilation for common errors like
+mismatched output/input language names, multiple default edges and
+cycles. Hidden option, useful for debugging.</li>
 <li><tt class="docutils literal"><span class="pre">--view-graph</span></tt> - Show a graphical representation of the compilation
 graph. Requires that you have <tt class="docutils literal"><span class="pre">dot</span></tt> and <tt class="docutils literal"><span class="pre">gv</span></tt> programs
 installed. Hidden option, useful for debugging.</li>
@@ -566,6 +569,13 @@ line option <tt class="docutils literal"><span class="pre">--view-graph</span></
 <a class="reference" href="http://pages.cs.wisc.edu/~ghost/">Ghostview</a> are installed. There is also a <tt class="docutils literal"><span class="pre">--dump-graph</span></tt> option that
 creates a Graphviz source file (<tt class="docutils literal"><span class="pre">compilation-graph.dot</span></tt>) in the
 current directory.</p>
+<p>Another useful option is <tt class="docutils literal"><span class="pre">--check-graph</span></tt>. It checks the compilation
+graph for common errors like mismatched output/input language names,
+multiple default edges and cycles. These checks can't be performed at
+compile-time because the plugins can load code dynamically. When
+invoked with <tt class="docutils literal"><span class="pre">--check-graph</span></tt>, <tt class="docutils literal"><span class="pre">llvmc</span></tt> doesn't perform any
+compilation tasks and returns the number of encountered errors as its
+status code.</p>
 <hr />
 <address>
 <a href="http://jigsaw.w3.org/css-validator/check/referer">
index 029623fdc8e30e956aa4a16615eb19fe12b1648d..090ff5fc64c1cd6a41f7e9d404347f7be6b6fb4d 100644 (file)
@@ -123,6 +123,9 @@ namespace llvmc {
 
   public:
 
+    typedef nodes_map_type::iterator nodes_iterator;
+    typedef nodes_map_type::const_iterator const_nodes_iterator;
+
     CompilationGraph();
 
     /// insertNode - Insert a new node into the graph. Takes
@@ -137,6 +140,11 @@ namespace llvmc {
     /// options are passed implicitly as global variables.
     int Build(llvm::sys::Path const& TempDir, const LanguageMap& LangMap);
 
+    /// Check - Check the compilation graph for common errors like
+    /// cycles, input/output language mismatch and multiple default
+    /// edges. Prints error messages and in case it finds any errors.
+    int Check();
+
     /// getNode - Return a reference to the node correponding to the
     /// given tool name. Throws std::runtime_error.
     Node& getNode(const std::string& ToolName);
@@ -171,7 +179,8 @@ namespace llvmc {
                            const llvm::sys::Path& TempDir,
                            const LanguageMap& LangMap) const;
 
-    /// FindToolChain - Find head of the toolchain corresponding to the given file.
+    /// FindToolChain - Find head of the toolchain corresponding to
+    /// the given file.
     const Node* FindToolChain(const llvm::sys::Path& In,
                               const std::string* ForceLanguage,
                               InputLanguagesSet& InLangs,
@@ -187,6 +196,18 @@ namespace llvmc {
     /// TopologicalSortFilterJoinNodes - Call TopologicalSort and
     /// filter the resulting list to include only Join nodes.
     void TopologicalSortFilterJoinNodes(std::vector<const Node*>& Out);
+
+    // Functions used to implement Check().
+
+    /// CheckLanguageNames - Check that output/input language names
+    /// match for all nodes.
+    int CheckLanguageNames() const;
+    /// CheckMultipleDefaultEdges - check that there are no multiple
+    /// default default edges.
+    int CheckMultipleDefaultEdges() const;
+    /// CheckCycles - Check that there are no cycles in the graph.
+    int CheckCycles();
+
   };
 
   // GraphTraits support code.
@@ -194,8 +215,8 @@ namespace llvmc {
   /// NodesIterator - Auxiliary class needed to implement GraphTraits
   /// support. Can be generalised to something like value_iterator
   /// for map-like containers.
-  class NodesIterator : public llvm::StringMap<Node>::iterator {
-    typedef llvm::StringMap<Node>::iterator super;
+  class NodesIterator : public CompilationGraph::nodes_iterator {
+    typedef CompilationGraph::nodes_iterator super;
     typedef NodesIterator ThisType;
     typedef Node* pointer;
     typedef Node& reference;
index 6189ec21828d9dc9bde23bafd2b9c012a1cda718..1c0da18189293981d5057f6622d975512cdb3328 100644 (file)
@@ -92,6 +92,10 @@ configuration libraries:
 
 * ``-v`` - Enable verbose mode, i.e. print out all executed commands.
 
+* ``--check-graph`` - Check the compilation for common errors like
+  mismatched output/input language names, multiple default edges and
+  cycles. Hidden option, useful for debugging.
+
 * ``--view-graph`` - Show a graphical representation of the compilation
   graph. Requires that you have ``dot`` and ``gv`` programs
   installed. Hidden option, useful for debugging.
@@ -605,6 +609,14 @@ Ghostview_ are installed. There is also a ``--dump-graph`` option that
 creates a Graphviz source file (``compilation-graph.dot``) in the
 current directory.
 
+Another useful option is ``--check-graph``. It checks the compilation
+graph for common errors like mismatched output/input language names,
+multiple default edges and cycles. These checks can't be performed at
+compile-time because the plugins can load code dynamically. When
+invoked with ``--check-graph``, ``llvmc`` doesn't perform any
+compilation tasks and returns the number of encountered errors as its
+status code.
+
 .. _Graphviz: http://www.graphviz.org/
 .. _Ghostview: http://pages.cs.wisc.edu/~ghost/
 
index 758268f79b9ce1132412a56df52b30f74a1ed534..2c59ee6314c0975eee6a3b2fed9285b76c28fbed 100644 (file)
@@ -20,6 +20,8 @@
 #include "llvm/Support/GraphWriter.h"
 
 #include <algorithm>
+#include <cstring>
+#include <iostream>
 #include <iterator>
 #include <limits>
 #include <queue>
@@ -333,6 +335,135 @@ int CompilationGraph::Build (const sys::Path& TempDir,
   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 (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"));
+
+  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 {
index f3a1e57192659e97186b610af4b9d8c48b3a6ff6..b295c633d90ab12edb9cbe7e03a10538b461c6ca 100644 (file)
@@ -45,6 +45,10 @@ cl::opt<bool> DryRun("dry-run",
                      cl::desc("Only pretend to run commands"));
 cl::opt<bool> VerboseMode("v",
                           cl::desc("Enable verbose mode"));
+
+cl::opt<bool> CheckGraph("check-graph",
+                         cl::desc("Check the compilation graph for errors"),
+                         cl::Hidden);
 cl::opt<bool> WriteGraph("write-graph",
                          cl::desc("Write compilation-graph.dot file"),
                          cl::Hidden);
@@ -89,14 +93,18 @@ int main(int argc, char** argv) {
     Plugins.PopulateLanguageMap(langMap);
     Plugins.PopulateCompilationGraph(graph);
 
-    if (WriteGraph) {
-      graph.writeGraph();
-      if (!ViewGraph)
-        return 0;
+    if (CheckGraph) {
+      return graph.Check();
     }
 
     if (ViewGraph) {
       graph.viewGraph();
+      if (!WriteGraph)
+        return 0;
+    }
+
+    if (WriteGraph) {
+      graph.writeGraph();
       return 0;
     }