Input files with empty suffixes must be passed to linker.
[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/BuiltinOptions.h"
15 #include "llvm/CompilerDriver/CompilationGraph.h"
16 #include "llvm/CompilerDriver/Error.h"
17
18 #include "llvm/ADT/STLExtras.h"
19 #include "llvm/Support/DOTGraphTraits.h"
20 #include "llvm/Support/GraphWriter.h"
21 #include "llvm/Support/raw_ostream.h"
22
23 #include <algorithm>
24 #include <cstring>
25 #include <iterator>
26 #include <limits>
27 #include <queue>
28 #include <stdexcept>
29
30 using namespace llvm;
31 using namespace llvmc;
32
33 namespace llvmc {
34
35   const std::string& LanguageMap::GetLanguage(const sys::Path& File) const {
36     StringRef suf = File.getSuffix();
37     LanguageMap::const_iterator Lang =
38       this->find(suf.empty() ? "*empty*" : suf);
39     if (Lang == this->end())
40       throw std::runtime_error("File '" + File.str() +
41                                 "' has unknown suffix '" + suf.str() + '\'');
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() && !(JT->WorksOnEmpty() && InputFilenames.empty()))
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           errs() << "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           errs() << "Error: Output->input language mismatch in the edge '"
370                  << N1.ToolPtr->Name() << "' -> '" << N2.ToolPtr->Name()
371                  << "'!\n"
372                  << "Expected one of { ";
373
374           InLangs = N2.ToolPtr->InputLanguages();
375           for (;*InLangs; ++InLangs) {
376             errs() << '\'' << *InLangs << (*(InLangs+1) ? "', " : "'");
377           }
378
379           errs() << " }, 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         errs() << "Error: there are multiple maximal edges stemming from the '"
413                << N.ToolPtr->Name() << "' node!\n\n";
414         break;
415       }
416     }
417   }
418
419   return ret;
420 }
421
422 int CompilationGraph::CheckCycles() {
423   unsigned deleted = 0;
424   std::queue<Node*> Q;
425   Q.push(&getNode("root"));
426
427   // Try to delete all nodes that have no ingoing edges, starting from the
428   // root. If there are any nodes left after this operation, then we have a
429   // cycle. This relies on '--check-graph' not performing the topological sort.
430   while (!Q.empty()) {
431     Node* A = Q.front();
432     Q.pop();
433     ++deleted;
434
435     for (Node::iterator EB = A->EdgesBegin(), EE = A->EdgesEnd();
436          EB != EE; ++EB) {
437       Node* B = &getNode((*EB)->ToolName());
438       B->DecrInEdges();
439       if (B->HasNoInEdges())
440         Q.push(B);
441     }
442   }
443
444   if (deleted != NodesMap.size()) {
445     errs() << "Error: there are cycles in the compilation graph!\n"
446            << "Try inspecting the diagram produced by "
447            << "'llvmc --view-graph'.\n\n";
448     return 1;
449   }
450
451   return 0;
452 }
453
454 int CompilationGraph::Check () {
455   // We try to catch as many errors as we can in one go.
456   int ret = 0;
457
458   // Check that output/input language names match.
459   ret += this->CheckLanguageNames();
460
461   // Check for multiple default edges.
462   ret += this->CheckMultipleDefaultEdges();
463
464   // Check for cycles.
465   ret += this->CheckCycles();
466
467   return ret;
468 }
469
470 // Code related to graph visualization.
471
472 namespace llvm {
473   template <>
474   struct DOTGraphTraits<llvmc::CompilationGraph*>
475     : public DefaultDOTGraphTraits
476   {
477     DOTGraphTraits (bool isSimple=false) : DefaultDOTGraphTraits(isSimple) {}
478
479     template<typename GraphType>
480     static std::string getNodeLabel(const Node* N, const GraphType&)
481     {
482       if (N->ToolPtr)
483         if (N->ToolPtr->IsJoin())
484           return N->Name() + "\n (join" +
485             (N->HasChildren() ? ")"
486              : std::string(": ") + N->ToolPtr->OutputLanguage() + ')');
487         else
488           return N->Name();
489       else
490         return "root";
491     }
492
493     template<typename EdgeIter>
494     static std::string getEdgeSourceLabel(const Node* N, EdgeIter I) {
495       if (N->ToolPtr) {
496         return N->ToolPtr->OutputLanguage();
497       }
498       else {
499         const char** InLangs = I->ToolPtr->InputLanguages();
500         std::string ret;
501
502         for (; *InLangs; ++InLangs) {
503           if (*(InLangs + 1)) {
504             ret += *InLangs;
505             ret +=  ", ";
506           }
507           else {
508             ret += *InLangs;
509           }
510         }
511
512         return ret;
513       }
514     }
515   };
516
517 }
518
519 void CompilationGraph::writeGraph(const std::string& OutputFilename) {
520   std::string ErrorInfo;
521   raw_fd_ostream O(OutputFilename.c_str(), ErrorInfo);
522
523   if (ErrorInfo.empty()) {
524     errs() << "Writing '"<< OutputFilename << "' file...";
525     llvm::WriteGraph(O, this);
526     errs() << "done.\n";
527   }
528   else {
529     throw std::runtime_error("Error opening file '" + OutputFilename
530                              + "' for writing!");
531   }
532 }
533
534 void CompilationGraph::viewGraph() {
535   llvm::ViewGraph(this, "compilation-graph");
536 }