Fix misc. small issues with debug visualization.
[oota-llvm.git] / lib / CompilerDriver / CompilationGraph.cpp
1 //===--- CompilationGraph.cpp - The LLVM Compiler Driver --------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open
6 // Source License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 //  Compilation graph - implementation.
11 //
12 //===----------------------------------------------------------------------===//
13
14 #include "llvm/CompilerDriver/CompilationGraph.h"
15 #include "llvm/CompilerDriver/Error.h"
16
17 #include "llvm/ADT/STLExtras.h"
18 #include "llvm/Support/CommandLine.h"
19 #include "llvm/Support/DOTGraphTraits.h"
20 #include "llvm/Support/GraphWriter.h"
21
22 #include <algorithm>
23 #include <cstring>
24 #include <iostream>
25 #include <iterator>
26 #include <limits>
27 #include <queue>
28 #include <stdexcept>
29
30 using namespace llvm;
31 using namespace llvmc;
32
33 extern cl::list<std::string> InputFilenames;
34 extern cl::list<std::string> Languages;
35
36 namespace llvmc {
37
38   const std::string& LanguageMap::GetLanguage(const sys::Path& File) const {
39     LanguageMap::const_iterator Lang = this->find(File.getSuffix());
40     if (Lang == this->end())
41       throw std::runtime_error("Unknown suffix: " + File.getSuffix());
42     return Lang->second;
43   }
44 }
45
46 namespace {
47
48   /// ChooseEdge - Return the edge with the maximum weight.
49   template <class C>
50   const Edge* ChooseEdge(const C& EdgesContainer,
51                          const InputLanguagesSet& InLangs,
52                          const std::string& NodeName = "root") {
53     const Edge* MaxEdge = 0;
54     unsigned MaxWeight = 0;
55     bool SingleMax = true;
56
57     for (typename C::const_iterator B = EdgesContainer.begin(),
58            E = EdgesContainer.end(); B != E; ++B) {
59       const Edge* e = B->getPtr();
60       unsigned EW = e->Weight(InLangs);
61       if (EW > MaxWeight) {
62         MaxEdge = e;
63         MaxWeight = EW;
64         SingleMax = true;
65       } else if (EW == MaxWeight) {
66         SingleMax = false;
67       }
68     }
69
70     if (!SingleMax)
71       throw std::runtime_error("Node " + NodeName +
72                                ": multiple maximal outward edges found!"
73                                " Most probably a specification error.");
74     if (!MaxEdge)
75       throw std::runtime_error("Node " + NodeName +
76                                ": no maximal outward edge found!"
77                                " Most probably a specification error.");
78     return MaxEdge;
79   }
80
81 }
82
83 void Node::AddEdge(Edge* Edg) {
84   // If there already was an edge between two nodes, modify it instead
85   // of adding a new edge.
86   const std::string& ToolName = Edg->ToolName();
87   for (container_type::iterator B = OutEdges.begin(), E = OutEdges.end();
88        B != E; ++B) {
89     if ((*B)->ToolName() == ToolName) {
90       llvm::IntrusiveRefCntPtr<Edge>(Edg).swap(*B);
91       return;
92     }
93   }
94   OutEdges.push_back(llvm::IntrusiveRefCntPtr<Edge>(Edg));
95 }
96
97 CompilationGraph::CompilationGraph() {
98   NodesMap["root"] = Node(this);
99 }
100
101 Node& CompilationGraph::getNode(const std::string& ToolName) {
102   nodes_map_type::iterator I = NodesMap.find(ToolName);
103   if (I == NodesMap.end())
104     throw std::runtime_error("Node " + ToolName + " is not in the graph");
105   return I->second;
106 }
107
108 const Node& CompilationGraph::getNode(const std::string& ToolName) const {
109   nodes_map_type::const_iterator I = NodesMap.find(ToolName);
110   if (I == NodesMap.end())
111     throw std::runtime_error("Node " + ToolName + " is not in the graph!");
112   return I->second;
113 }
114
115 // Find the tools list corresponding to the given language name.
116 const CompilationGraph::tools_vector_type&
117 CompilationGraph::getToolsVector(const std::string& LangName) const
118 {
119   tools_map_type::const_iterator I = ToolsMap.find(LangName);
120   if (I == ToolsMap.end())
121     throw std::runtime_error("No tool corresponding to the language "
122                              + LangName + " found");
123   return I->second;
124 }
125
126 void CompilationGraph::insertNode(Tool* V) {
127   if (NodesMap.count(V->Name()) == 0)
128     NodesMap[V->Name()] = Node(this, V);
129 }
130
131 void CompilationGraph::insertEdge(const std::string& A, Edge* Edg) {
132   Node& B = getNode(Edg->ToolName());
133   if (A == "root") {
134     const char** InLangs = B.ToolPtr->InputLanguages();
135     for (;*InLangs; ++InLangs)
136       ToolsMap[*InLangs].push_back(IntrusiveRefCntPtr<Edge>(Edg));
137     NodesMap["root"].AddEdge(Edg);
138   }
139   else {
140     Node& N = getNode(A);
141     N.AddEdge(Edg);
142   }
143   // Increase the inward edge counter.
144   B.IncrInEdges();
145 }
146
147 // Pass input file through the chain until we bump into a Join node or
148 // a node that says that it is the last.
149 void CompilationGraph::PassThroughGraph (const sys::Path& InFile,
150                                          const Node* StartNode,
151                                          const InputLanguagesSet& InLangs,
152                                          const sys::Path& TempDir,
153                                          const LanguageMap& LangMap) const {
154   sys::Path In = InFile;
155   const Node* CurNode = StartNode;
156
157   while(true) {
158     Tool* CurTool = CurNode->ToolPtr.getPtr();
159
160     if (CurTool->IsJoin()) {
161       JoinTool& JT = dynamic_cast<JoinTool&>(*CurTool);
162       JT.AddToJoinList(In);
163       break;
164     }
165
166     Action CurAction = CurTool->GenerateAction(In, CurNode->HasChildren(),
167                                                TempDir, InLangs, LangMap);
168
169     if (int ret = CurAction.Execute())
170       throw error_code(ret);
171
172     if (CurAction.StopCompilation())
173       return;
174
175     CurNode = &getNode(ChooseEdge(CurNode->OutEdges,
176                                   InLangs,
177                                   CurNode->Name())->ToolName());
178     In = CurAction.OutFile();
179   }
180 }
181
182 // Find the head of the toolchain corresponding to the given file.
183 // Also, insert an input language into InLangs.
184 const Node* CompilationGraph::
185 FindToolChain(const sys::Path& In, const std::string* ForceLanguage,
186               InputLanguagesSet& InLangs, const LanguageMap& LangMap) const {
187
188   // Determine the input language.
189   const std::string& InLanguage =
190     ForceLanguage ? *ForceLanguage : LangMap.GetLanguage(In);
191
192   // Add the current input language to the input language set.
193   InLangs.insert(InLanguage);
194
195   // Find the toolchain for the input language.
196   const tools_vector_type& TV = getToolsVector(InLanguage);
197   if (TV.empty())
198     throw std::runtime_error("No toolchain corresponding to language "
199                              + InLanguage + " found");
200   return &getNode(ChooseEdge(TV, InLangs)->ToolName());
201 }
202
203 // Helper function used by Build().
204 // Traverses initial portions of the toolchains (up to the first Join node).
205 // This function is also responsible for handling the -x option.
206 void CompilationGraph::BuildInitial (InputLanguagesSet& InLangs,
207                                      const sys::Path& TempDir,
208                                      const LanguageMap& LangMap) {
209   // This is related to -x option handling.
210   cl::list<std::string>::const_iterator xIter = Languages.begin(),
211     xBegin = xIter, xEnd = Languages.end();
212   bool xEmpty = true;
213   const std::string* xLanguage = 0;
214   unsigned xPos = 0, xPosNext = 0, filePos = 0;
215
216   if (xIter != xEnd) {
217     xEmpty = false;
218     xPos = Languages.getPosition(xIter - xBegin);
219     cl::list<std::string>::const_iterator xNext = llvm::next(xIter);
220     xPosNext = (xNext == xEnd) ? std::numeric_limits<unsigned>::max()
221       : Languages.getPosition(xNext - xBegin);
222     xLanguage = (*xIter == "none") ? 0 : &(*xIter);
223   }
224
225   // For each input file:
226   for (cl::list<std::string>::const_iterator B = InputFilenames.begin(),
227          CB = B, E = InputFilenames.end(); B != E; ++B) {
228     sys::Path In = sys::Path(*B);
229
230     // Code for handling the -x option.
231     // Output: std::string* xLanguage (can be NULL).
232     if (!xEmpty) {
233       filePos = InputFilenames.getPosition(B - CB);
234
235       if (xPos < filePos) {
236         if (filePos < xPosNext) {
237           xLanguage = (*xIter == "none") ? 0 : &(*xIter);
238         }
239         else { // filePos >= xPosNext
240           // Skip xIters while filePos > xPosNext
241           while (filePos > xPosNext) {
242             ++xIter;
243             xPos = xPosNext;
244
245             cl::list<std::string>::const_iterator xNext = llvm::next(xIter);
246             if (xNext == xEnd)
247               xPosNext = std::numeric_limits<unsigned>::max();
248             else
249               xPosNext = Languages.getPosition(xNext - xBegin);
250             xLanguage = (*xIter == "none") ? 0 : &(*xIter);
251           }
252         }
253       }
254     }
255
256     // Find the toolchain corresponding to this file.
257     const Node* N = FindToolChain(In, xLanguage, InLangs, LangMap);
258     // Pass file through the chain starting at head.
259     PassThroughGraph(In, N, InLangs, TempDir, LangMap);
260   }
261 }
262
263 // Sort the nodes in topological order.
264 void CompilationGraph::TopologicalSort(std::vector<const Node*>& Out) {
265   std::queue<const Node*> Q;
266   Q.push(&getNode("root"));
267
268   while (!Q.empty()) {
269     const Node* A = Q.front();
270     Q.pop();
271     Out.push_back(A);
272     for (Node::const_iterator EB = A->EdgesBegin(), EE = A->EdgesEnd();
273          EB != EE; ++EB) {
274       Node* B = &getNode((*EB)->ToolName());
275       B->DecrInEdges();
276       if (B->HasNoInEdges())
277         Q.push(B);
278     }
279   }
280 }
281
282 namespace {
283   bool NotJoinNode(const Node* N) {
284     return N->ToolPtr ? !N->ToolPtr->IsJoin() : true;
285   }
286 }
287
288 // Call TopologicalSort and filter the resulting list to include
289 // only Join nodes.
290 void CompilationGraph::
291 TopologicalSortFilterJoinNodes(std::vector<const Node*>& Out) {
292   std::vector<const Node*> TopSorted;
293   TopologicalSort(TopSorted);
294   std::remove_copy_if(TopSorted.begin(), TopSorted.end(),
295                       std::back_inserter(Out), NotJoinNode);
296 }
297
298 int CompilationGraph::Build (const sys::Path& TempDir,
299                              const LanguageMap& LangMap) {
300
301   InputLanguagesSet InLangs;
302
303   // Traverse initial parts of the toolchains and fill in InLangs.
304   BuildInitial(InLangs, TempDir, LangMap);
305
306   std::vector<const Node*> JTV;
307   TopologicalSortFilterJoinNodes(JTV);
308
309   // For all join nodes in topological order:
310   for (std::vector<const Node*>::iterator B = JTV.begin(), E = JTV.end();
311        B != E; ++B) {
312
313     const Node* CurNode = *B;
314     JoinTool* JT = &dynamic_cast<JoinTool&>(*CurNode->ToolPtr.getPtr());
315
316     // Are there any files in the join list?
317     if (JT->JoinListEmpty())
318       continue;
319
320     Action CurAction = JT->GenerateAction(CurNode->HasChildren(),
321                                           TempDir, InLangs, LangMap);
322
323     if (int ret = CurAction.Execute())
324       throw error_code(ret);
325
326     if (CurAction.StopCompilation())
327       return 0;
328
329     const Node* NextNode = &getNode(ChooseEdge(CurNode->OutEdges, InLangs,
330                                                CurNode->Name())->ToolName());
331     PassThroughGraph(sys::Path(CurAction.OutFile()), NextNode,
332                      InLangs, TempDir, LangMap);
333   }
334
335   return 0;
336 }
337
338 int CompilationGraph::CheckLanguageNames() const {
339   int ret = 0;
340   // Check that names for output and input languages on all edges do match.
341   for (const_nodes_iterator B = this->NodesMap.begin(),
342          E = this->NodesMap.end(); B != E; ++B) {
343
344     const Node & N1 = B->second;
345     if (N1.ToolPtr) {
346       for (Node::const_iterator EB = N1.EdgesBegin(), EE = N1.EdgesEnd();
347            EB != EE; ++EB) {
348         const Node& N2 = this->getNode((*EB)->ToolName());
349
350         if (!N2.ToolPtr) {
351           ++ret;
352           std::cerr << "Error: there is an edge from '" << N1.ToolPtr->Name()
353                     << "' back to the root!\n\n";
354           continue;
355         }
356
357         const char* OutLang = N1.ToolPtr->OutputLanguage();
358         const char** InLangs = N2.ToolPtr->InputLanguages();
359         bool eq = false;
360         for (;*InLangs; ++InLangs) {
361           if (std::strcmp(OutLang, *InLangs) == 0) {
362             eq = true;
363             break;
364           }
365         }
366
367         if (!eq) {
368           ++ret;
369           std::cerr << "Error: Output->input language mismatch in the edge '" <<
370             N1.ToolPtr->Name() << "' -> '" << N2.ToolPtr->Name() << "'!\n";
371
372           std::cerr << "Expected one of { ";
373
374           InLangs = N2.ToolPtr->InputLanguages();
375           for (;*InLangs; ++InLangs) {
376             std::cerr << '\'' << *InLangs << (*(InLangs+1) ? "', " : "'");
377           }
378
379           std::cerr << " }, but got '" << OutLang << "'!\n\n";
380         }
381
382       }
383     }
384   }
385
386   return ret;
387 }
388
389 int CompilationGraph::CheckMultipleDefaultEdges() const {
390   int ret = 0;
391   InputLanguagesSet Dummy;
392
393   // For all nodes, just iterate over the outgoing edges and check if there is
394   // more than one edge with maximum weight.
395   for (const_nodes_iterator B = this->NodesMap.begin(),
396          E = this->NodesMap.end(); B != E; ++B) {
397     const Node& N = B->second;
398     unsigned MaxWeight = 0;
399
400     // Ignore the root node.
401     if (!N.ToolPtr)
402       continue;
403
404     for (Node::const_iterator EB = N.EdgesBegin(), EE = N.EdgesEnd();
405          EB != EE; ++EB) {
406       unsigned EdgeWeight = (*EB)->Weight(Dummy);
407       if (EdgeWeight > MaxWeight) {
408         MaxWeight = EdgeWeight;
409       }
410       else if (EdgeWeight == MaxWeight) {
411         ++ret;
412         std::cerr
413           << "Error: there are multiple maximal edges stemming from the '"
414           << N.ToolPtr->Name() << "' node!\n\n";
415         break;
416       }
417     }
418   }
419
420   return ret;
421 }
422
423 int CompilationGraph::CheckCycles() {
424   unsigned deleted = 0;
425   std::queue<Node*> Q;
426   Q.push(&getNode("root"));
427
428   // Try to delete all nodes that have no ingoing edges, starting from the
429   // root. If there are any nodes left after this operation, then we have a
430   // cycle. This relies on '--check-graph' not performing the topological sort.
431   while (!Q.empty()) {
432     Node* A = Q.front();
433     Q.pop();
434     ++deleted;
435
436     for (Node::iterator EB = A->EdgesBegin(), EE = A->EdgesEnd();
437          EB != EE; ++EB) {
438       Node* B = &getNode((*EB)->ToolName());
439       B->DecrInEdges();
440       if (B->HasNoInEdges())
441         Q.push(B);
442     }
443   }
444
445   if (deleted != NodesMap.size()) {
446     std::cerr << "Error: there are cycles in the compilation graph!\n"
447               << "Try inspecting the diagram produced by "
448       "'llvmc --view-graph'.\n\n";
449     return 1;
450   }
451
452   return 0;
453 }
454
455 int CompilationGraph::Check () {
456   // We try to catch as many errors as we can in one go.
457   int ret = 0;
458
459   // Check that output/input language names match.
460   ret += this->CheckLanguageNames();
461
462   // Check for multiple default edges.
463   ret += this->CheckMultipleDefaultEdges();
464
465   // Check for cycles.
466   ret += this->CheckCycles();
467
468   if (!ret)
469     std::cerr << "check-graph: no errors found.\n";
470
471   return ret;
472 }
473
474 // Code related to graph visualization.
475
476 namespace llvm {
477   template <>
478   struct DOTGraphTraits<llvmc::CompilationGraph*>
479     : public DefaultDOTGraphTraits
480   {
481
482     template<typename GraphType>
483     static std::string getNodeLabel(const Node* N, const GraphType&)
484     {
485       if (N->ToolPtr)
486         if (N->ToolPtr->IsJoin())
487           return N->Name() + "\n (join" +
488             (N->HasChildren() ? ")"
489              : std::string(": ") + N->ToolPtr->OutputLanguage() + ')');
490         else
491           return N->Name();
492       else
493         return "root";
494     }
495
496     template<typename EdgeIter>
497     static std::string getEdgeSourceLabel(const Node* N, EdgeIter I) {
498       if (N->ToolPtr) {
499         return N->ToolPtr->OutputLanguage();
500       }
501       else {
502         const char** InLangs = I->ToolPtr->InputLanguages();
503         std::string ret;
504
505         for (; *InLangs; ++InLangs) {
506           if (*(InLangs + 1)) {
507             ret += *InLangs;
508             ret +=  ", ";
509           }
510           else {
511             ret += *InLangs;
512           }
513         }
514
515         return ret;
516       }
517     }
518   };
519
520 }
521
522 void CompilationGraph::writeGraph() {
523   std::ofstream O("compilation-graph.dot");
524
525   if (O.good()) {
526     std::cerr << "Writing 'compilation-graph.dot' file...";
527     llvm::WriteGraph(O, this);
528     std::cerr << "done.\n";
529     O.close();
530   }
531   else {
532     throw std::runtime_error("Error opening file 'compilation-graph.dot'"
533                              " for writing!");
534   }
535 }
536
537 void CompilationGraph::viewGraph() {
538   llvm::ViewGraph(this, "compilation-graph");
539 }