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