2c59ee6314c0975eee6a3b2fed9285b76c28fbed
[oota-llvm.git] / tools / llvmc / driver / 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 "Error.h"
15 #include "llvm/CompilerDriver/CompilationGraph.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 (const_nodes_iterator B = this->NodesMap.begin(),
394          E = this->NodesMap.end(); B != E; ++B) {
395     const Node& N = B->second;
396     unsigned MaxWeight = 0;
397
398     // Ignore the root node.
399     if (!N.ToolPtr)
400       continue;
401
402     for (Node::const_iterator EB = N.EdgesBegin(), EE = N.EdgesEnd();
403          EB != EE; ++EB) {
404       unsigned EdgeWeight = (*EB)->Weight(Dummy);
405       if (EdgeWeight > MaxWeight) {
406         MaxWeight = EdgeWeight;
407       }
408       else if (EdgeWeight == MaxWeight) {
409         ++ret;
410         std::cerr
411           << "Error: there are multiple maximal edges stemming from the '"
412           << N.ToolPtr->Name() << "' node!\n\n";
413         break;
414       }
415     }
416   }
417
418   return ret;
419 }
420
421 int CompilationGraph::CheckCycles() {
422   unsigned deleted = 0;
423   std::queue<Node*> Q;
424   Q.push(&getNode("root"));
425
426   while (!Q.empty()) {
427     Node* A = Q.front();
428     Q.pop();
429     ++deleted;
430
431     for (Node::iterator EB = A->EdgesBegin(), EE = A->EdgesEnd();
432          EB != EE; ++EB) {
433       Node* B = &getNode((*EB)->ToolName());
434       B->DecrInEdges();
435       if (B->HasNoInEdges())
436         Q.push(B);
437     }
438   }
439
440   if (deleted != NodesMap.size()) {
441     std::cerr << "Error: there are cycles in the compilation graph!\n"
442               << "Try inspecting the diagram produced by "
443       "'llvmc --view-graph'.\n\n";
444     return 1;
445   }
446
447   return 0;
448 }
449
450
451 int CompilationGraph::Check () {
452   // We try to catch as many errors as we can in one go.
453   int ret = 0;
454
455   // Check that output/input language names match.
456   ret += this->CheckLanguageNames();
457
458   // Check for multiple default edges.
459   ret += this->CheckMultipleDefaultEdges();
460
461   // Check for cycles.
462   ret += this->CheckCycles();
463
464   return ret;
465 }
466
467 // Code related to graph visualization.
468
469 namespace llvm {
470   template <>
471   struct DOTGraphTraits<llvmc::CompilationGraph*>
472     : public DefaultDOTGraphTraits
473   {
474
475     template<typename GraphType>
476     static std::string getNodeLabel(const Node* N, const GraphType&)
477     {
478       if (N->ToolPtr)
479         if (N->ToolPtr->IsJoin())
480           return N->Name() + "\n (join" +
481             (N->HasChildren() ? ")"
482              : std::string(": ") + N->ToolPtr->OutputLanguage() + ')');
483         else
484           return N->Name();
485       else
486         return "root";
487     }
488
489     template<typename EdgeIter>
490     static std::string getEdgeSourceLabel(const Node* N, EdgeIter I) {
491       if (N->ToolPtr) {
492         return N->ToolPtr->OutputLanguage();
493       }
494       else {
495         const char** InLangs = I->ToolPtr->InputLanguages();
496         std::string ret;
497
498         for (; *InLangs; ++InLangs) {
499           if (*(InLangs + 1)) {
500             ret += *InLangs;
501             ret +=  ", ";
502           }
503           else {
504             ret += *InLangs;
505           }
506         }
507
508         return ret;
509       }
510     }
511   };
512
513 }
514
515 void CompilationGraph::writeGraph() {
516   std::ofstream O("compilation-graph.dot");
517
518   if (O.good()) {
519     llvm::WriteGraph(this, "compilation-graph");
520     O.close();
521   }
522   else {
523     throw std::runtime_error("Error opening file 'compilation-graph.dot'"
524                              " for writing!");
525   }
526 }
527
528 void CompilationGraph::viewGraph() {
529   llvm::ViewGraph(this, "compilation-graph");
530 }