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/CompilationGraph.h"
15 #include "llvm/CompilerDriver/Error.h"
17 #include "llvm/ADT/STLExtras.h"
18 #include "llvm/Support/CommandLine.h"
19 #include "llvm/Support/DOTGraphTraits.h"
20 #include "llvm/Support/GraphWriter.h"
31 using namespace llvmc
;
33 extern cl::list
<std::string
> InputFilenames
;
34 extern cl::list
<std::string
> Languages
;
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());
48 /// ChooseEdge - Return the edge with the maximum weight.
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;
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
);
65 } else if (EW
== MaxWeight
) {
71 throw std::runtime_error("Node " + NodeName
+
72 ": multiple maximal outward edges found!"
73 " Most probably a specification error.");
75 throw std::runtime_error("Node " + NodeName
+
76 ": no maximal outward edge found!"
77 " Most probably a specification error.");
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();
89 if ((*B
)->ToolName() == ToolName
) {
90 llvm::IntrusiveRefCntPtr
<Edge
>(Edg
).swap(*B
);
94 OutEdges
.push_back(llvm::IntrusiveRefCntPtr
<Edge
>(Edg
));
97 CompilationGraph::CompilationGraph() {
98 NodesMap
["root"] = Node(this);
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");
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!");
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
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");
126 void CompilationGraph::insertNode(Tool
* V
) {
127 if (NodesMap
.count(V
->Name()) == 0)
128 NodesMap
[V
->Name()] = Node(this, V
);
131 void CompilationGraph::insertEdge(const std::string
& A
, Edge
* Edg
) {
132 Node
& B
= getNode(Edg
->ToolName());
134 const char** InLangs
= B
.ToolPtr
->InputLanguages();
135 for (;*InLangs
; ++InLangs
)
136 ToolsMap
[*InLangs
].push_back(IntrusiveRefCntPtr
<Edge
>(Edg
));
137 NodesMap
["root"].AddEdge(Edg
);
140 Node
& N
= getNode(A
);
143 // Increase the inward edge counter.
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
;
158 Tool
* CurTool
= CurNode
->ToolPtr
.getPtr();
160 if (CurTool
->IsJoin()) {
161 JoinTool
& JT
= dynamic_cast<JoinTool
&>(*CurTool
);
162 JT
.AddToJoinList(In
);
166 Action CurAction
= CurTool
->GenerateAction(In
, CurNode
->HasChildren(),
167 TempDir
, InLangs
, LangMap
);
169 if (int ret
= CurAction
.Execute())
170 throw error_code(ret
);
172 if (CurAction
.StopCompilation())
175 CurNode
= &getNode(ChooseEdge(CurNode
->OutEdges
,
177 CurNode
->Name())->ToolName());
178 In
= CurAction
.OutFile();
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 {
188 // Determine the input language.
189 const std::string
& InLanguage
=
190 ForceLanguage
? *ForceLanguage
: LangMap
.GetLanguage(In
);
192 // Add the current input language to the input language set.
193 InLangs
.insert(InLanguage
);
195 // Find the toolchain for the input language.
196 const tools_vector_type
& TV
= getToolsVector(InLanguage
);
198 throw std::runtime_error("No toolchain corresponding to language "
199 + InLanguage
+ " found");
200 return &getNode(ChooseEdge(TV
, InLangs
)->ToolName());
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();
213 const std::string
* xLanguage
= 0;
214 unsigned xPos
= 0, xPosNext
= 0, filePos
= 0;
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
);
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
);
230 // Code for handling the -x option.
231 // Output: std::string* xLanguage (can be NULL).
233 filePos
= InputFilenames
.getPosition(B
- CB
);
235 if (xPos
< filePos
) {
236 if (filePos
< xPosNext
) {
237 xLanguage
= (*xIter
== "none") ? 0 : &(*xIter
);
239 else { // filePos >= xPosNext
240 // Skip xIters while filePos > xPosNext
241 while (filePos
> xPosNext
) {
245 cl::list
<std::string
>::const_iterator xNext
= llvm::next(xIter
);
247 xPosNext
= std::numeric_limits
<unsigned>::max();
249 xPosNext
= Languages
.getPosition(xNext
- xBegin
);
250 xLanguage
= (*xIter
== "none") ? 0 : &(*xIter
);
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
);
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"));
269 const Node
* A
= Q
.front();
272 for (Node::const_iterator EB
= A
->EdgesBegin(), EE
= A
->EdgesEnd();
274 Node
* B
= &getNode((*EB
)->ToolName());
276 if (B
->HasNoInEdges())
283 bool NotJoinNode(const Node
* N
) {
284 return N
->ToolPtr
? !N
->ToolPtr
->IsJoin() : true;
288 // Call TopologicalSort and filter the resulting list to include
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
);
298 int CompilationGraph::Build (const sys::Path
& TempDir
,
299 const LanguageMap
& LangMap
) {
301 InputLanguagesSet InLangs
;
303 // Traverse initial parts of the toolchains and fill in InLangs.
304 BuildInitial(InLangs
, TempDir
, LangMap
);
306 std::vector
<const Node
*> JTV
;
307 TopologicalSortFilterJoinNodes(JTV
);
309 // For all join nodes in topological order:
310 for (std::vector
<const Node
*>::iterator B
= JTV
.begin(), E
= JTV
.end();
313 const Node
* CurNode
= *B
;
314 JoinTool
* JT
= &dynamic_cast<JoinTool
&>(*CurNode
->ToolPtr
.getPtr());
316 // Are there any files in the join list?
317 if (JT
->JoinListEmpty())
320 Action CurAction
= JT
->GenerateAction(CurNode
->HasChildren(),
321 TempDir
, InLangs
, LangMap
);
323 if (int ret
= CurAction
.Execute())
324 throw error_code(ret
);
326 if (CurAction
.StopCompilation())
329 const Node
* NextNode
= &getNode(ChooseEdge(CurNode
->OutEdges
, InLangs
,
330 CurNode
->Name())->ToolName());
331 PassThroughGraph(sys::Path(CurAction
.OutFile()), NextNode
,
332 InLangs
, TempDir
, LangMap
);
338 int CompilationGraph::CheckLanguageNames() const {
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
) {
344 const Node
& N1
= B
->second
;
346 for (Node::const_iterator EB
= N1
.EdgesBegin(), EE
= N1
.EdgesEnd();
348 const Node
& N2
= this->getNode((*EB
)->ToolName());
352 std::cerr
<< "Error: there is an edge from '" << N1
.ToolPtr
->Name()
353 << "' back to the root!\n\n";
357 const char* OutLang
= N1
.ToolPtr
->OutputLanguage();
358 const char** InLangs
= N2
.ToolPtr
->InputLanguages();
360 for (;*InLangs
; ++InLangs
) {
361 if (std::strcmp(OutLang
, *InLangs
) == 0) {
369 std::cerr
<< "Error: Output->input language mismatch in the edge '" <<
370 N1
.ToolPtr
->Name() << "' -> '" << N2
.ToolPtr
->Name() << "'!\n";
372 std::cerr
<< "Expected one of { ";
374 InLangs
= N2
.ToolPtr
->InputLanguages();
375 for (;*InLangs
; ++InLangs
) {
376 std::cerr
<< '\'' << *InLangs
<< (*(InLangs
+1) ? "', " : "'");
379 std::cerr
<< " }, but got '" << OutLang
<< "'!\n\n";
389 int CompilationGraph::CheckMultipleDefaultEdges() const {
391 InputLanguagesSet Dummy
;
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;
400 // Ignore the root node.
404 for (Node::const_iterator EB
= N
.EdgesBegin(), EE
= N
.EdgesEnd();
406 unsigned EdgeWeight
= (*EB
)->Weight(Dummy
);
407 if (EdgeWeight
> MaxWeight
) {
408 MaxWeight
= EdgeWeight
;
410 else if (EdgeWeight
== MaxWeight
) {
413 << "Error: there are multiple maximal edges stemming from the '"
414 << N
.ToolPtr
->Name() << "' node!\n\n";
423 int CompilationGraph::CheckCycles() {
424 unsigned deleted
= 0;
426 Q
.push(&getNode("root"));
428 // Try to delete all nodes that have no ingoing edges, starting from the
429 // root. If there are any nodes left after this operation, then we have a
430 // cycle. This relies on '--check-graph' not performing the topological sort.
436 for (Node::iterator EB
= A
->EdgesBegin(), EE
= A
->EdgesEnd();
438 Node
* B
= &getNode((*EB
)->ToolName());
440 if (B
->HasNoInEdges())
445 if (deleted
!= NodesMap
.size()) {
446 std::cerr
<< "Error: there are cycles in the compilation graph!\n"
447 << "Try inspecting the diagram produced by "
448 "'llvmc --view-graph'.\n\n";
455 int CompilationGraph::Check () {
456 // We try to catch as many errors as we can in one go.
459 // Check that output/input language names match.
460 ret
+= this->CheckLanguageNames();
462 // Check for multiple default edges.
463 ret
+= this->CheckMultipleDefaultEdges();
466 ret
+= this->CheckCycles();
471 // Code related to graph visualization.
475 struct DOTGraphTraits
<llvmc::CompilationGraph
*>
476 : public DefaultDOTGraphTraits
479 template<typename GraphType
>
480 static std::string
getNodeLabel(const Node
* N
, const GraphType
&)
483 if (N
->ToolPtr
->IsJoin())
484 return N
->Name() + "\n (join" +
485 (N
->HasChildren() ? ")"
486 : std::string(": ") + N
->ToolPtr
->OutputLanguage() + ')');
493 template<typename EdgeIter
>
494 static std::string
getEdgeSourceLabel(const Node
* N
, EdgeIter I
) {
496 return N
->ToolPtr
->OutputLanguage();
499 const char** InLangs
= I
->ToolPtr
->InputLanguages();
502 for (; *InLangs
; ++InLangs
) {
503 if (*(InLangs
+ 1)) {
519 void CompilationGraph::writeGraph(const std::string
& OutputFilename
) {
520 std::ofstream
O(OutputFilename
.c_str());
523 std::cerr
<< "Writing '"<< OutputFilename
<< "' file...";
524 llvm::WriteGraph(O
, this);
525 std::cerr
<< "done.\n";
529 throw std::runtime_error("Error opening file '" + OutputFilename
534 void CompilationGraph::viewGraph() {
535 llvm::ViewGraph(this, "compilation-graph");