]> CyberLeo.Net >> Repos - FreeBSD/stable/9.git/blob - contrib/llvm/lib/CompilerDriver/CompilationGraph.cpp
Copy head to stable/9 as part of 9.0-RELEASE release cycle.
[FreeBSD/stable/9.git] / contrib / llvm / 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
29 using namespace llvm;
30 using namespace llvmc;
31
32 namespace llvmc {
33
34   const std::string* LanguageMap::GetLanguage(const sys::Path& File) const {
35     // Remove the '.'.
36     StringRef suf = sys::path::extension(File.str()).substr(1);
37     LanguageMap::const_iterator Lang =
38       this->find(suf.empty() ? "*empty*" : suf);
39     if (Lang == this->end()) {
40       PrintError("File '" + File.str() + "' has unknown suffix '"
41                  + suf.str() + '\'');
42       return 0;
43     }
44     return &Lang->second;
45   }
46 }
47
48 namespace {
49
50   /// ChooseEdge - Return the edge with the maximum weight. Returns 0 on error.
51   template <class C>
52   const Edge* ChooseEdge(const C& EdgesContainer,
53                          const InputLanguagesSet& InLangs,
54                          const std::string& NodeName = "root") {
55     const Edge* MaxEdge = 0;
56     int MaxWeight = 0;
57     bool SingleMax = true;
58
59     // TODO: fix calculation of SingleMax.
60     for (typename C::const_iterator B = EdgesContainer.begin(),
61            E = EdgesContainer.end(); B != E; ++B) {
62       const Edge* e = B->getPtr();
63       int EW = e->Weight(InLangs);
64       if (EW < 0) {
65         // (error) invocation in TableGen -> we don't need to print an error
66         // message.
67         return 0;
68       }
69       if (EW > MaxWeight) {
70         MaxEdge = e;
71         MaxWeight = EW;
72         SingleMax = true;
73       } else if (EW == MaxWeight) {
74         SingleMax = false;
75       }
76     }
77
78     if (!SingleMax) {
79       PrintError("Node " + NodeName + ": multiple maximal outward edges found!"
80                  " Most probably a specification error.");
81       return 0;
82     }
83     if (!MaxEdge) {
84       PrintError("Node " + NodeName + ": no maximal outward edge found!"
85                  " Most probably a specification error.");
86       return 0;
87     }
88     return MaxEdge;
89   }
90
91 }
92
93 void Node::AddEdge(Edge* Edg) {
94   // If there already was an edge between two nodes, modify it instead
95   // of adding a new edge.
96   const std::string& ToolName = Edg->ToolName();
97   for (container_type::iterator B = OutEdges.begin(), E = OutEdges.end();
98        B != E; ++B) {
99     if ((*B)->ToolName() == ToolName) {
100       llvm::IntrusiveRefCntPtr<Edge>(Edg).swap(*B);
101       return;
102     }
103   }
104   OutEdges.push_back(llvm::IntrusiveRefCntPtr<Edge>(Edg));
105 }
106
107 CompilationGraph::CompilationGraph() {
108   NodesMap["root"] = Node(this);
109 }
110
111 Node* CompilationGraph::getNode(const std::string& ToolName) {
112   nodes_map_type::iterator I = NodesMap.find(ToolName);
113   if (I == NodesMap.end()) {
114     PrintError("Node " + ToolName + " is not in the graph");
115     return 0;
116   }
117   return &I->second;
118 }
119
120 const Node* CompilationGraph::getNode(const std::string& ToolName) const {
121   nodes_map_type::const_iterator I = NodesMap.find(ToolName);
122   if (I == NodesMap.end()) {
123     PrintError("Node " + ToolName + " is not in the graph!");
124     return 0;
125   }
126   return &I->second;
127 }
128
129 // Find the tools list corresponding to the given language name.
130 const CompilationGraph::tools_vector_type*
131 CompilationGraph::getToolsVector(const std::string& LangName) const
132 {
133   tools_map_type::const_iterator I = ToolsMap.find(LangName);
134   if (I == ToolsMap.end()) {
135     PrintError("No tool corresponding to the language " + LangName + " found");
136     return 0;
137   }
138   return &I->second;
139 }
140
141 void CompilationGraph::insertNode(Tool* V) {
142   if (NodesMap.count(V->Name()) == 0)
143     NodesMap[V->Name()] = Node(this, V);
144 }
145
146 int CompilationGraph::insertEdge(const std::string& A, Edge* Edg) {
147   Node* B = getNode(Edg->ToolName());
148   if (B == 0)
149     return 1;
150
151   if (A == "root") {
152     const char** InLangs = B->ToolPtr->InputLanguages();
153     for (;*InLangs; ++InLangs)
154       ToolsMap[*InLangs].push_back(IntrusiveRefCntPtr<Edge>(Edg));
155     NodesMap["root"].AddEdge(Edg);
156   }
157   else {
158     Node* N = getNode(A);
159     if (N == 0)
160       return 1;
161
162     N->AddEdge(Edg);
163   }
164   // Increase the inward edge counter.
165   B->IncrInEdges();
166
167   return 0;
168 }
169
170 // Pass input file through the chain until we bump into a Join node or
171 // a node that says that it is the last.
172 int CompilationGraph::PassThroughGraph (const sys::Path& InFile,
173                                         const Node* StartNode,
174                                         const InputLanguagesSet& InLangs,
175                                         const sys::Path& TempDir,
176                                         const LanguageMap& LangMap) const {
177   sys::Path In = InFile;
178   const Node* CurNode = StartNode;
179
180   while(true) {
181     Tool* CurTool = CurNode->ToolPtr.getPtr();
182
183     if (CurTool->IsJoin()) {
184       JoinTool& JT = static_cast<JoinTool&>(*CurTool);
185       JT.AddToJoinList(In);
186       break;
187     }
188
189     Action CurAction;
190     if (int ret = CurTool->GenerateAction(CurAction, In, CurNode->HasChildren(),
191                                           TempDir, InLangs, LangMap)) {
192       return ret;
193     }
194
195     if (int ret = CurAction.Execute())
196       return ret;
197
198     if (CurAction.StopCompilation())
199       return 0;
200
201     const Edge* Edg = ChooseEdge(CurNode->OutEdges, InLangs, CurNode->Name());
202     if (Edg == 0)
203       return 1;
204
205     CurNode = getNode(Edg->ToolName());
206     if (CurNode == 0)
207       return 1;
208
209     In = CurAction.OutFile();
210   }
211
212   return 0;
213 }
214
215 // Find the head of the toolchain corresponding to the given file.
216 // Also, insert an input language into InLangs.
217 const Node* CompilationGraph::
218 FindToolChain(const sys::Path& In, const std::string* ForceLanguage,
219               InputLanguagesSet& InLangs, const LanguageMap& LangMap) const {
220
221   // Determine the input language.
222   const std::string* InLang = (ForceLanguage ? ForceLanguage
223                                : LangMap.GetLanguage(In));
224   if (InLang == 0)
225     return 0;
226   const std::string& InLanguage = *InLang;
227
228   // Add the current input language to the input language set.
229   InLangs.insert(InLanguage);
230
231   // Find the toolchain for the input language.
232   const tools_vector_type* pTV = getToolsVector(InLanguage);
233   if (pTV == 0)
234     return 0;
235
236   const tools_vector_type& TV = *pTV;
237   if (TV.empty()) {
238     PrintError("No toolchain corresponding to language "
239                + InLanguage + " found");
240     return 0;
241   }
242
243   const Edge* Edg = ChooseEdge(TV, InLangs);
244   if (Edg == 0)
245     return 0;
246
247   return getNode(Edg->ToolName());
248 }
249
250 // Helper function used by Build().
251 // Traverses initial portions of the toolchains (up to the first Join node).
252 // This function is also responsible for handling the -x option.
253 int CompilationGraph::BuildInitial (InputLanguagesSet& InLangs,
254                                     const sys::Path& TempDir,
255                                     const LanguageMap& LangMap) {
256   // This is related to -x option handling.
257   cl::list<std::string>::const_iterator xIter = Languages.begin(),
258     xBegin = xIter, xEnd = Languages.end();
259   bool xEmpty = true;
260   const std::string* xLanguage = 0;
261   unsigned xPos = 0, xPosNext = 0, filePos = 0;
262
263   if (xIter != xEnd) {
264     xEmpty = false;
265     xPos = Languages.getPosition(xIter - xBegin);
266     cl::list<std::string>::const_iterator xNext = llvm::next(xIter);
267     xPosNext = (xNext == xEnd) ? std::numeric_limits<unsigned>::max()
268       : Languages.getPosition(xNext - xBegin);
269     xLanguage = (*xIter == "none") ? 0 : &(*xIter);
270   }
271
272   // For each input file:
273   for (cl::list<std::string>::const_iterator B = InputFilenames.begin(),
274          CB = B, E = InputFilenames.end(); B != E; ++B) {
275     sys::Path In = sys::Path(*B);
276
277     // Code for handling the -x option.
278     // Output: std::string* xLanguage (can be NULL).
279     if (!xEmpty) {
280       filePos = InputFilenames.getPosition(B - CB);
281
282       if (xPos < filePos) {
283         if (filePos < xPosNext) {
284           xLanguage = (*xIter == "none") ? 0 : &(*xIter);
285         }
286         else { // filePos >= xPosNext
287           // Skip xIters while filePos > xPosNext
288           while (filePos > xPosNext) {
289             ++xIter;
290             xPos = xPosNext;
291
292             cl::list<std::string>::const_iterator xNext = llvm::next(xIter);
293             if (xNext == xEnd)
294               xPosNext = std::numeric_limits<unsigned>::max();
295             else
296               xPosNext = Languages.getPosition(xNext - xBegin);
297             xLanguage = (*xIter == "none") ? 0 : &(*xIter);
298           }
299         }
300       }
301     }
302
303     // Find the toolchain corresponding to this file.
304     const Node* N = FindToolChain(In, xLanguage, InLangs, LangMap);
305     if (N == 0)
306       return 1;
307     // Pass file through the chain starting at head.
308     if (int ret = PassThroughGraph(In, N, InLangs, TempDir, LangMap))
309       return ret;
310   }
311
312   return 0;
313 }
314
315 // Sort the nodes in topological order.
316 int CompilationGraph::TopologicalSort(std::vector<const Node*>& Out) {
317   std::queue<const Node*> Q;
318
319   Node* Root = getNode("root");
320   if (Root == 0)
321     return 1;
322
323   Q.push(Root);
324
325   while (!Q.empty()) {
326     const Node* A = Q.front();
327     Q.pop();
328     Out.push_back(A);
329     for (Node::const_iterator EB = A->EdgesBegin(), EE = A->EdgesEnd();
330          EB != EE; ++EB) {
331       Node* B = getNode((*EB)->ToolName());
332       if (B == 0)
333         return 1;
334
335       B->DecrInEdges();
336       if (B->HasNoInEdges())
337         Q.push(B);
338     }
339   }
340
341   return 0;
342 }
343
344 namespace {
345   bool NotJoinNode(const Node* N) {
346     return N->ToolPtr ? !N->ToolPtr->IsJoin() : true;
347   }
348 }
349
350 // Call TopologicalSort and filter the resulting list to include
351 // only Join nodes.
352 int CompilationGraph::
353 TopologicalSortFilterJoinNodes(std::vector<const Node*>& Out) {
354   std::vector<const Node*> TopSorted;
355   if (int ret = TopologicalSort(TopSorted))
356     return ret;
357   std::remove_copy_if(TopSorted.begin(), TopSorted.end(),
358                       std::back_inserter(Out), NotJoinNode);
359
360   return 0;
361 }
362
363 int CompilationGraph::Build (const sys::Path& TempDir,
364                              const LanguageMap& LangMap) {
365   InputLanguagesSet InLangs;
366   bool WasSomeActionGenerated = !InputFilenames.empty();
367
368   // Traverse initial parts of the toolchains and fill in InLangs.
369   if (int ret = BuildInitial(InLangs, TempDir, LangMap))
370     return ret;
371
372   std::vector<const Node*> JTV;
373   if (int ret = TopologicalSortFilterJoinNodes(JTV))
374     return ret;
375
376   // For all join nodes in topological order:
377   for (std::vector<const Node*>::iterator B = JTV.begin(), E = JTV.end();
378        B != E; ++B) {
379
380     const Node* CurNode = *B;
381     JoinTool* JT = &static_cast<JoinTool&>(*CurNode->ToolPtr.getPtr());
382
383     // Are there any files in the join list?
384     if (JT->JoinListEmpty() && !(JT->WorksOnEmpty() && InputFilenames.empty()))
385       continue;
386
387     WasSomeActionGenerated = true;
388     Action CurAction;
389     if (int ret = JT->GenerateAction(CurAction, CurNode->HasChildren(),
390                                      TempDir, InLangs, LangMap)) {
391       return ret;
392     }
393
394     if (int ret = CurAction.Execute())
395       return ret;
396
397     if (CurAction.StopCompilation())
398       return 0;
399
400     const Edge* Edg = ChooseEdge(CurNode->OutEdges, InLangs, CurNode->Name());
401     if (Edg == 0)
402       return 1;
403
404     const Node* NextNode = getNode(Edg->ToolName());
405     if (NextNode == 0)
406       return 1;
407
408     if (int ret = PassThroughGraph(sys::Path(CurAction.OutFile()), NextNode,
409                                    InLangs, TempDir, LangMap)) {
410       return ret;
411     }
412   }
413
414   if (!WasSomeActionGenerated) {
415     PrintError("no input files");
416     return 1;
417   }
418
419   return 0;
420 }
421
422 int CompilationGraph::CheckLanguageNames() const {
423   int ret = 0;
424
425   // Check that names for output and input languages on all edges do match.
426   for (const_nodes_iterator B = this->NodesMap.begin(),
427          E = this->NodesMap.end(); B != E; ++B) {
428
429     const Node & N1 = B->second;
430     if (N1.ToolPtr) {
431       for (Node::const_iterator EB = N1.EdgesBegin(), EE = N1.EdgesEnd();
432            EB != EE; ++EB) {
433         const Node* N2 = this->getNode((*EB)->ToolName());
434         if (N2 == 0)
435           return 1;
436
437         if (!N2->ToolPtr) {
438           ++ret;
439           errs() << "Error: there is an edge from '" << N1.ToolPtr->Name()
440                  << "' back to the root!\n\n";
441           continue;
442         }
443
444         const char** OutLangs = N1.ToolPtr->OutputLanguages();
445         const char** InLangs = N2->ToolPtr->InputLanguages();
446         bool eq = false;
447         const char* OutLang = 0;
448         for (;*OutLangs; ++OutLangs) {
449           OutLang = *OutLangs;
450           for (;*InLangs; ++InLangs) {
451             if (std::strcmp(OutLang, *InLangs) == 0) {
452               eq = true;
453               break;
454             }
455           }
456         }
457
458         if (!eq) {
459           ++ret;
460           errs() << "Error: Output->input language mismatch in the edge '"
461                  << N1.ToolPtr->Name() << "' -> '" << N2->ToolPtr->Name()
462                  << "'!\n"
463                  << "Expected one of { ";
464
465           InLangs = N2->ToolPtr->InputLanguages();
466           for (;*InLangs; ++InLangs) {
467             errs() << '\'' << *InLangs << (*(InLangs+1) ? "', " : "'");
468           }
469
470           errs() << " }, but got '" << OutLang << "'!\n\n";
471         }
472
473       }
474     }
475   }
476
477   return ret;
478 }
479
480 int CompilationGraph::CheckMultipleDefaultEdges() const {
481   int ret = 0;
482   InputLanguagesSet Dummy;
483
484   // For all nodes, just iterate over the outgoing edges and check if there is
485   // more than one edge with maximum weight.
486   for (const_nodes_iterator B = this->NodesMap.begin(),
487          E = this->NodesMap.end(); B != E; ++B) {
488     const Node& N = B->second;
489     int MaxWeight = -1024;
490
491     // Ignore the root node.
492     if (!N.ToolPtr)
493       continue;
494
495     for (Node::const_iterator EB = N.EdgesBegin(), EE = N.EdgesEnd();
496          EB != EE; ++EB) {
497       int EdgeWeight = (*EB)->Weight(Dummy);
498       if (EdgeWeight > MaxWeight) {
499         MaxWeight = EdgeWeight;
500       }
501       else if (EdgeWeight == MaxWeight) {
502         ++ret;
503         errs() << "Error: there are multiple maximal edges stemming from the '"
504                << N.ToolPtr->Name() << "' node!\n\n";
505         break;
506       }
507     }
508   }
509
510   return ret;
511 }
512
513 int CompilationGraph::CheckCycles() {
514   unsigned deleted = 0;
515   std::queue<Node*> Q;
516
517   Node* Root = getNode("root");
518   if (Root == 0)
519     return 1;
520
521   Q.push(Root);
522
523   // Try to delete all nodes that have no ingoing edges, starting from the
524   // root. If there are any nodes left after this operation, then we have a
525   // cycle. This relies on '--check-graph' not performing the topological sort.
526   while (!Q.empty()) {
527     Node* A = Q.front();
528     Q.pop();
529     ++deleted;
530
531     for (Node::iterator EB = A->EdgesBegin(), EE = A->EdgesEnd();
532          EB != EE; ++EB) {
533       Node* B = getNode((*EB)->ToolName());
534       if (B == 0)
535         return 1;
536
537       B->DecrInEdges();
538       if (B->HasNoInEdges())
539         Q.push(B);
540     }
541   }
542
543   if (deleted != NodesMap.size()) {
544     errs() << "Error: there are cycles in the compilation graph!\n"
545            << "Try inspecting the diagram produced by "
546            << "'llvmc --view-graph'.\n\n";
547     return 1;
548   }
549
550   return 0;
551 }
552
553 int CompilationGraph::Check () {
554   // We try to catch as many errors as we can in one go.
555   int errs = 0;
556   int ret = 0;
557
558   // Check that output/input language names match.
559   ret = this->CheckLanguageNames();
560   if (ret < 0)
561     return 1;
562   errs += ret;
563
564   // Check for multiple default edges.
565   ret = this->CheckMultipleDefaultEdges();
566   if (ret < 0)
567     return 1;
568   errs += ret;
569
570   // Check for cycles.
571   ret = this->CheckCycles();
572   if (ret < 0)
573     return 1;
574   errs += ret;
575
576   return errs;
577 }
578
579 // Code related to graph visualization.
580
581 namespace {
582
583 std::string SquashStrArray (const char** StrArr) {
584   std::string ret;
585
586   for (; *StrArr; ++StrArr) {
587     if (*(StrArr + 1)) {
588       ret += *StrArr;
589       ret +=  ", ";
590     }
591     else {
592       ret += *StrArr;
593     }
594   }
595
596   return ret;
597 }
598
599 } // End anonymous namespace.
600
601 namespace llvm {
602   template <>
603   struct DOTGraphTraits<llvmc::CompilationGraph*>
604     : public DefaultDOTGraphTraits
605   {
606     DOTGraphTraits (bool isSimple=false) : DefaultDOTGraphTraits(isSimple) {}
607
608     template<typename GraphType>
609     static std::string getNodeLabel(const Node* N, const GraphType&)
610     {
611       if (N->ToolPtr)
612         if (N->ToolPtr->IsJoin())
613           return N->Name() + "\n (join" +
614             (N->HasChildren() ? ")"
615              : std::string(": ") +
616              SquashStrArray(N->ToolPtr->OutputLanguages()) + ')');
617         else
618           return N->Name();
619       else
620         return "root";
621     }
622
623     template<typename EdgeIter>
624     static std::string getEdgeSourceLabel(const Node* N, EdgeIter I) {
625       if (N->ToolPtr) {
626         return SquashStrArray(N->ToolPtr->OutputLanguages());
627       }
628       else {
629         return SquashStrArray(I->ToolPtr->InputLanguages());
630       }
631     }
632   };
633
634 } // End namespace llvm
635
636 int CompilationGraph::writeGraph(const std::string& OutputFilename) {
637   std::string ErrorInfo;
638   raw_fd_ostream O(OutputFilename.c_str(), ErrorInfo);
639
640   if (ErrorInfo.empty()) {
641     errs() << "Writing '"<< OutputFilename << "' file...";
642     llvm::WriteGraph(O, this);
643     errs() << "done.\n";
644   }
645   else {
646     PrintError("Error opening file '" + OutputFilename + "' for writing!");
647     return 1;
648   }
649
650   return 0;
651 }
652
653 void CompilationGraph::viewGraph() {
654   llvm::ViewGraph(this, "compilation-graph");
655 }