1 //===--- CompilationGraph.cpp - The LLVM Compiler Driver --------*- C++ -*-===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open
6 // Source License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 // Compilation graph - implementation.
12 //===----------------------------------------------------------------------===//
14 #include "llvm/CompilerDriver/BuiltinOptions.h"
15 #include "llvm/CompilerDriver/CompilationGraph.h"
16 #include "llvm/CompilerDriver/Error.h"
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"
30 using namespace llvmc
;
34 const std::string
* LanguageMap::GetLanguage(const sys::Path
& File
) const {
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 '"
50 /// ChooseEdge - Return the edge with the maximum weight. Returns 0 on error.
52 const Edge
* ChooseEdge(const C
& EdgesContainer
,
53 const InputLanguagesSet
& InLangs
,
54 const std::string
& NodeName
= "root") {
55 const Edge
* MaxEdge
= 0;
57 bool SingleMax
= true;
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
);
65 // (error) invocation in TableGen -> we don't need to print an error
73 } else if (EW
== MaxWeight
) {
79 PrintError("Node " + NodeName
+ ": multiple maximal outward edges found!"
80 " Most probably a specification error.");
84 PrintError("Node " + NodeName
+ ": no maximal outward edge found!"
85 " Most probably a specification error.");
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();
99 if ((*B
)->ToolName() == ToolName
) {
100 llvm::IntrusiveRefCntPtr
<Edge
>(Edg
).swap(*B
);
104 OutEdges
.push_back(llvm::IntrusiveRefCntPtr
<Edge
>(Edg
));
107 CompilationGraph::CompilationGraph() {
108 NodesMap
["root"] = Node(this);
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");
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!");
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
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");
141 void CompilationGraph::insertNode(Tool
* V
) {
142 if (NodesMap
.count(V
->Name()) == 0)
143 NodesMap
[V
->Name()] = Node(this, V
);
146 int CompilationGraph::insertEdge(const std::string
& A
, Edge
* Edg
) {
147 Node
* B
= getNode(Edg
->ToolName());
152 const char** InLangs
= B
->ToolPtr
->InputLanguages();
153 for (;*InLangs
; ++InLangs
)
154 ToolsMap
[*InLangs
].push_back(IntrusiveRefCntPtr
<Edge
>(Edg
));
155 NodesMap
["root"].AddEdge(Edg
);
158 Node
* N
= getNode(A
);
164 // Increase the inward edge counter.
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
;
181 Tool
* CurTool
= CurNode
->ToolPtr
.getPtr();
183 if (CurTool
->IsJoin()) {
184 JoinTool
& JT
= static_cast<JoinTool
&>(*CurTool
);
185 JT
.AddToJoinList(In
);
190 if (int ret
= CurTool
->GenerateAction(CurAction
, In
, CurNode
->HasChildren(),
191 TempDir
, InLangs
, LangMap
)) {
195 if (int ret
= CurAction
.Execute())
198 if (CurAction
.StopCompilation())
201 const Edge
* Edg
= ChooseEdge(CurNode
->OutEdges
, InLangs
, CurNode
->Name());
205 CurNode
= getNode(Edg
->ToolName());
209 In
= CurAction
.OutFile();
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 {
221 // Determine the input language.
222 const std::string
* InLang
= (ForceLanguage
? ForceLanguage
223 : LangMap
.GetLanguage(In
));
226 const std::string
& InLanguage
= *InLang
;
228 // Add the current input language to the input language set.
229 InLangs
.insert(InLanguage
);
231 // Find the toolchain for the input language.
232 const tools_vector_type
* pTV
= getToolsVector(InLanguage
);
236 const tools_vector_type
& TV
= *pTV
;
238 PrintError("No toolchain corresponding to language "
239 + InLanguage
+ " found");
243 const Edge
* Edg
= ChooseEdge(TV
, InLangs
);
247 return getNode(Edg
->ToolName());
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();
260 const std::string
* xLanguage
= 0;
261 unsigned xPos
= 0, xPosNext
= 0, filePos
= 0;
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
);
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
);
277 // Code for handling the -x option.
278 // Output: std::string* xLanguage (can be NULL).
280 filePos
= InputFilenames
.getPosition(B
- CB
);
282 if (xPos
< filePos
) {
283 if (filePos
< xPosNext
) {
284 xLanguage
= (*xIter
== "none") ? 0 : &(*xIter
);
286 else { // filePos >= xPosNext
287 // Skip xIters while filePos > xPosNext
288 while (filePos
> xPosNext
) {
292 cl::list
<std::string
>::const_iterator xNext
= llvm::next(xIter
);
294 xPosNext
= std::numeric_limits
<unsigned>::max();
296 xPosNext
= Languages
.getPosition(xNext
- xBegin
);
297 xLanguage
= (*xIter
== "none") ? 0 : &(*xIter
);
303 // Find the toolchain corresponding to this file.
304 const Node
* N
= FindToolChain(In
, xLanguage
, InLangs
, LangMap
);
307 // Pass file through the chain starting at head.
308 if (int ret
= PassThroughGraph(In
, N
, InLangs
, TempDir
, LangMap
))
315 // Sort the nodes in topological order.
316 int CompilationGraph::TopologicalSort(std::vector
<const Node
*>& Out
) {
317 std::queue
<const Node
*> Q
;
319 Node
* Root
= getNode("root");
326 const Node
* A
= Q
.front();
329 for (Node::const_iterator EB
= A
->EdgesBegin(), EE
= A
->EdgesEnd();
331 Node
* B
= getNode((*EB
)->ToolName());
336 if (B
->HasNoInEdges())
345 bool NotJoinNode(const Node
* N
) {
346 return N
->ToolPtr
? !N
->ToolPtr
->IsJoin() : true;
350 // Call TopologicalSort and filter the resulting list to include
352 int CompilationGraph::
353 TopologicalSortFilterJoinNodes(std::vector
<const Node
*>& Out
) {
354 std::vector
<const Node
*> TopSorted
;
355 if (int ret
= TopologicalSort(TopSorted
))
357 std::remove_copy_if(TopSorted
.begin(), TopSorted
.end(),
358 std::back_inserter(Out
), NotJoinNode
);
363 int CompilationGraph::Build (const sys::Path
& TempDir
,
364 const LanguageMap
& LangMap
) {
365 InputLanguagesSet InLangs
;
366 bool WasSomeActionGenerated
= !InputFilenames
.empty();
368 // Traverse initial parts of the toolchains and fill in InLangs.
369 if (int ret
= BuildInitial(InLangs
, TempDir
, LangMap
))
372 std::vector
<const Node
*> JTV
;
373 if (int ret
= TopologicalSortFilterJoinNodes(JTV
))
376 // For all join nodes in topological order:
377 for (std::vector
<const Node
*>::iterator B
= JTV
.begin(), E
= JTV
.end();
380 const Node
* CurNode
= *B
;
381 JoinTool
* JT
= &static_cast<JoinTool
&>(*CurNode
->ToolPtr
.getPtr());
383 // Are there any files in the join list?
384 if (JT
->JoinListEmpty() && !(JT
->WorksOnEmpty() && InputFilenames
.empty()))
387 WasSomeActionGenerated
= true;
389 if (int ret
= JT
->GenerateAction(CurAction
, CurNode
->HasChildren(),
390 TempDir
, InLangs
, LangMap
)) {
394 if (int ret
= CurAction
.Execute())
397 if (CurAction
.StopCompilation())
400 const Edge
* Edg
= ChooseEdge(CurNode
->OutEdges
, InLangs
, CurNode
->Name());
404 const Node
* NextNode
= getNode(Edg
->ToolName());
408 if (int ret
= PassThroughGraph(sys::Path(CurAction
.OutFile()), NextNode
,
409 InLangs
, TempDir
, LangMap
)) {
414 if (!WasSomeActionGenerated
) {
415 PrintError("no input files");
422 int CompilationGraph::CheckLanguageNames() const {
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
) {
429 const Node
& N1
= B
->second
;
431 for (Node::const_iterator EB
= N1
.EdgesBegin(), EE
= N1
.EdgesEnd();
433 const Node
* N2
= this->getNode((*EB
)->ToolName());
439 errs() << "Error: there is an edge from '" << N1
.ToolPtr
->Name()
440 << "' back to the root!\n\n";
444 const char** OutLangs
= N1
.ToolPtr
->OutputLanguages();
445 const char** InLangs
= N2
->ToolPtr
->InputLanguages();
447 const char* OutLang
= 0;
448 for (;*OutLangs
; ++OutLangs
) {
450 for (;*InLangs
; ++InLangs
) {
451 if (std::strcmp(OutLang
, *InLangs
) == 0) {
460 errs() << "Error: Output->input language mismatch in the edge '"
461 << N1
.ToolPtr
->Name() << "' -> '" << N2
->ToolPtr
->Name()
463 << "Expected one of { ";
465 InLangs
= N2
->ToolPtr
->InputLanguages();
466 for (;*InLangs
; ++InLangs
) {
467 errs() << '\'' << *InLangs
<< (*(InLangs
+1) ? "', " : "'");
470 errs() << " }, but got '" << OutLang
<< "'!\n\n";
480 int CompilationGraph::CheckMultipleDefaultEdges() const {
482 InputLanguagesSet Dummy
;
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;
491 // Ignore the root node.
495 for (Node::const_iterator EB
= N
.EdgesBegin(), EE
= N
.EdgesEnd();
497 int EdgeWeight
= (*EB
)->Weight(Dummy
);
498 if (EdgeWeight
> MaxWeight
) {
499 MaxWeight
= EdgeWeight
;
501 else if (EdgeWeight
== MaxWeight
) {
503 errs() << "Error: there are multiple maximal edges stemming from the '"
504 << N
.ToolPtr
->Name() << "' node!\n\n";
513 int CompilationGraph::CheckCycles() {
514 unsigned deleted
= 0;
517 Node
* Root
= getNode("root");
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.
531 for (Node::iterator EB
= A
->EdgesBegin(), EE
= A
->EdgesEnd();
533 Node
* B
= getNode((*EB
)->ToolName());
538 if (B
->HasNoInEdges())
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";
553 int CompilationGraph::Check () {
554 // We try to catch as many errors as we can in one go.
558 // Check that output/input language names match.
559 ret
= this->CheckLanguageNames();
564 // Check for multiple default edges.
565 ret
= this->CheckMultipleDefaultEdges();
571 ret
= this->CheckCycles();
579 // Code related to graph visualization.
583 std::string
SquashStrArray (const char** StrArr
) {
586 for (; *StrArr
; ++StrArr
) {
599 } // End anonymous namespace.
603 struct DOTGraphTraits
<llvmc::CompilationGraph
*>
604 : public DefaultDOTGraphTraits
606 DOTGraphTraits (bool isSimple
=false) : DefaultDOTGraphTraits(isSimple
) {}
608 template<typename GraphType
>
609 static std::string
getNodeLabel(const Node
* N
, const GraphType
&)
612 if (N
->ToolPtr
->IsJoin())
613 return N
->Name() + "\n (join" +
614 (N
->HasChildren() ? ")"
615 : std::string(": ") +
616 SquashStrArray(N
->ToolPtr
->OutputLanguages()) + ')');
623 template<typename EdgeIter
>
624 static std::string
getEdgeSourceLabel(const Node
* N
, EdgeIter I
) {
626 return SquashStrArray(N
->ToolPtr
->OutputLanguages());
629 return SquashStrArray(I
->ToolPtr
->InputLanguages());
634 } // End namespace llvm
636 int CompilationGraph::writeGraph(const std::string
& OutputFilename
) {
637 std::string ErrorInfo
;
638 raw_fd_ostream
O(OutputFilename
.c_str(), ErrorInfo
);
640 if (ErrorInfo
.empty()) {
641 errs() << "Writing '"<< OutputFilename
<< "' file...";
642 llvm::WriteGraph(O
, this);
646 PrintError("Error opening file '" + OutputFilename
+ "' for writing!");
653 void CompilationGraph::viewGraph() {
654 llvm::ViewGraph(this, "compilation-graph");