blob: f5cefbf0a90c02bad87f54af146aae1fd5cd48fe [file] [log] [blame]
Mikhail Glushenkovb90cd832008-05-06 16:34:12 +00001//===--- CompilationGraph.cpp - The LLVM Compiler Driver --------*- C++ -*-===//
Anton Korobeynikovac67b7e2008-03-23 08:57:20 +00002//
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//
Mikhail Glushenkovb90cd832008-05-06 16:34:12 +000010// Compilation graph - implementation.
Anton Korobeynikovac67b7e2008-03-23 08:57:20 +000011//
12//===----------------------------------------------------------------------===//
13
Mikhail Glushenkovb90cd832008-05-06 16:34:12 +000014#include "CompilationGraph.h"
Anton Korobeynikovac67b7e2008-03-23 08:57:20 +000015
Mikhail Glushenkov87416b42008-05-06 18:10:53 +000016#include "llvm/ADT/STLExtras.h"
Anton Korobeynikovac67b7e2008-03-23 08:57:20 +000017#include "llvm/Support/CommandLine.h"
Mikhail Glushenkov0d08db02008-05-06 16:35:25 +000018#include "llvm/Support/DOTGraphTraits.h"
19#include "llvm/Support/GraphWriter.h"
Anton Korobeynikovac67b7e2008-03-23 08:57:20 +000020
Mikhail Glushenkov02606582008-05-06 18:07:14 +000021#include <algorithm>
Mikhail Glushenkov02606582008-05-06 18:07:14 +000022#include <iterator>
Mikhail Glushenkov76b1b242008-05-06 18:15:12 +000023#include <iostream>
Mikhail Glushenkov87416b42008-05-06 18:10:53 +000024#include <limits>
Mikhail Glushenkov02606582008-05-06 18:07:14 +000025#include <queue>
Anton Korobeynikovac67b7e2008-03-23 08:57:20 +000026#include <stdexcept>
27
28using namespace llvm;
Mikhail Glushenkovbe9d9a12008-05-06 18:08:59 +000029using namespace llvmc;
Anton Korobeynikovac67b7e2008-03-23 08:57:20 +000030
31extern cl::list<std::string> InputFilenames;
32extern cl::opt<std::string> OutputFilename;
Mikhail Glushenkov87416b42008-05-06 18:10:53 +000033extern cl::list<std::string> Languages;
Anton Korobeynikovac67b7e2008-03-23 08:57:20 +000034
Mikhail Glushenkov4f6e3a42008-05-06 17:28:03 +000035namespace {
36
Mikhail Glushenkovbb8b58d2008-05-06 18:14:24 +000037 // Return the edge with the maximum weight.
Mikhail Glushenkov4f6e3a42008-05-06 17:28:03 +000038 template <class C>
39 const Edge* ChooseEdge(const C& EdgesContainer,
Mikhail Glushenkov76b1b242008-05-06 18:15:12 +000040 const InputLanguagesSet& InLangs,
Mikhail Glushenkov4f6e3a42008-05-06 17:28:03 +000041 const std::string& NodeName = "root") {
Mikhail Glushenkovbb8b58d2008-05-06 18:14:24 +000042 const Edge* MaxEdge = 0;
43 unsigned MaxWeight = 0;
44 bool SingleMax = true;
Mikhail Glushenkov4f6e3a42008-05-06 17:28:03 +000045
46 for (typename C::const_iterator B = EdgesContainer.begin(),
47 E = EdgesContainer.end(); B != E; ++B) {
48 const Edge* E = B->getPtr();
Mikhail Glushenkov76b1b242008-05-06 18:15:12 +000049 unsigned EW = E->Weight(InLangs);
Mikhail Glushenkovbb8b58d2008-05-06 18:14:24 +000050 if (EW > MaxWeight) {
51 MaxEdge = E;
52 MaxWeight = EW;
53 SingleMax = true;
54 }
55 else if (EW == MaxWeight) {
56 SingleMax = false;
57 }
Mikhail Glushenkov4f6e3a42008-05-06 17:28:03 +000058 }
59
Mikhail Glushenkovbb8b58d2008-05-06 18:14:24 +000060 if (!SingleMax)
61 throw std::runtime_error("Node " + NodeName +
62 ": multiple maximal outward edges found!"
Mikhail Glushenkov87416b42008-05-06 18:10:53 +000063 " Most probably a specification error.");
Mikhail Glushenkovbb8b58d2008-05-06 18:14:24 +000064 if (!MaxEdge)
65 throw std::runtime_error("Node " + NodeName +
66 ": no maximal outward edge found!"
67 " Most probably a specification error.");
68 return MaxEdge;
Mikhail Glushenkov6591c892008-05-06 17:23:50 +000069 }
70
Mikhail Glushenkov6591c892008-05-06 17:23:50 +000071}
72
Mikhail Glushenkov0d08db02008-05-06 16:35:25 +000073CompilationGraph::CompilationGraph() {
74 NodesMap["root"] = Node(this);
75}
Anton Korobeynikovac67b7e2008-03-23 08:57:20 +000076
Mikhail Glushenkov0d08db02008-05-06 16:35:25 +000077Node& CompilationGraph::getNode(const std::string& ToolName) {
78 nodes_map_type::iterator I = NodesMap.find(ToolName);
79 if (I == NodesMap.end())
Mikhail Glushenkovd752c3f2008-05-06 16:36:50 +000080 throw std::runtime_error("Node " + ToolName + " is not in the graph");
Mikhail Glushenkov0d08db02008-05-06 16:35:25 +000081 return I->second;
82}
83
84const Node& CompilationGraph::getNode(const std::string& ToolName) const {
85 nodes_map_type::const_iterator I = NodesMap.find(ToolName);
86 if (I == NodesMap.end())
Mikhail Glushenkovd752c3f2008-05-06 16:36:50 +000087 throw std::runtime_error("Node " + ToolName + " is not in the graph!");
Mikhail Glushenkov0d08db02008-05-06 16:35:25 +000088 return I->second;
89}
90
91const std::string& CompilationGraph::getLanguage(const sys::Path& File) const {
92 LanguageMap::const_iterator Lang = ExtsToLangs.find(File.getSuffix());
Anton Korobeynikovac67b7e2008-03-23 08:57:20 +000093 if (Lang == ExtsToLangs.end())
Mikhail Glushenkov0d08db02008-05-06 16:35:25 +000094 throw std::runtime_error("Unknown suffix: " + File.getSuffix() + '!');
95 return Lang->second;
96}
Anton Korobeynikovac67b7e2008-03-23 08:57:20 +000097
Mikhail Glushenkov0d08db02008-05-06 16:35:25 +000098const CompilationGraph::tools_vector_type&
99CompilationGraph::getToolsVector(const std::string& LangName) const
100{
101 tools_map_type::const_iterator I = ToolsMap.find(LangName);
102 if (I == ToolsMap.end())
Mikhail Glushenkov87416b42008-05-06 18:10:53 +0000103 throw std::runtime_error("No tool corresponding to the language "
104 + LangName + "found!");
Mikhail Glushenkov0d08db02008-05-06 16:35:25 +0000105 return I->second;
106}
Anton Korobeynikovac67b7e2008-03-23 08:57:20 +0000107
Mikhail Glushenkov0a174932008-05-06 16:36:06 +0000108void CompilationGraph::insertNode(Tool* V) {
Mikhail Glushenkovc74bfc92008-05-06 17:26:53 +0000109 if (NodesMap.count(V->Name()) == 0) {
Mikhail Glushenkov0d08db02008-05-06 16:35:25 +0000110 Node N;
111 N.OwningGraph = this;
112 N.ToolPtr = V;
113 NodesMap[V->Name()] = N;
114 }
115}
116
Mikhail Glushenkovd752c3f2008-05-06 16:36:50 +0000117void CompilationGraph::insertEdge(const std::string& A, Edge* E) {
Mikhail Glushenkovc74bfc92008-05-06 17:26:53 +0000118 Node& B = getNode(E->ToolName());
Mikhail Glushenkov0d08db02008-05-06 16:35:25 +0000119 if (A == "root") {
Mikhail Glushenkovc74bfc92008-05-06 17:26:53 +0000120 const std::string& InputLanguage = B.ToolPtr->InputLanguage();
Mikhail Glushenkov4f6e3a42008-05-06 17:28:03 +0000121 ToolsMap[InputLanguage].push_back(IntrusiveRefCntPtr<Edge>(E));
Mikhail Glushenkovd752c3f2008-05-06 16:36:50 +0000122 NodesMap["root"].AddEdge(E);
Mikhail Glushenkov0d08db02008-05-06 16:35:25 +0000123 }
124 else {
Mikhail Glushenkov0a174932008-05-06 16:36:06 +0000125 Node& N = getNode(A);
Mikhail Glushenkovd752c3f2008-05-06 16:36:50 +0000126 N.AddEdge(E);
Mikhail Glushenkov0d08db02008-05-06 16:35:25 +0000127 }
Mikhail Glushenkovc74bfc92008-05-06 17:26:53 +0000128 // Increase the inward edge counter.
129 B.IncrInEdges();
Mikhail Glushenkov0d08db02008-05-06 16:35:25 +0000130}
131
Mikhail Glushenkov87416b42008-05-06 18:10:53 +0000132namespace {
133 sys::Path MakeTempFile(const sys::Path& TempDir, const std::string& BaseName,
134 const std::string& Suffix) {
135 sys::Path Out = TempDir;
136 Out.appendComponent(BaseName);
137 Out.appendSuffix(Suffix);
138 Out.makeUnique(true, NULL);
139 return Out;
140 }
Mikhail Glushenkov35a85e82008-05-06 18:10:20 +0000141}
142
Mikhail Glushenkov2ba4c5a2008-05-06 17:25:51 +0000143// Pass input file through the chain until we bump into a Join node or
144// a node that says that it is the last.
Mikhail Glushenkov35a85e82008-05-06 18:10:20 +0000145void CompilationGraph::PassThroughGraph (const sys::Path& InFile,
146 const Node* StartNode,
Mikhail Glushenkov76b1b242008-05-06 18:15:12 +0000147 const InputLanguagesSet& InLangs,
Mikhail Glushenkov35a85e82008-05-06 18:10:20 +0000148 const sys::Path& TempDir) const {
Mikhail Glushenkov2ba4c5a2008-05-06 17:25:51 +0000149 bool Last = false;
Mikhail Glushenkov35a85e82008-05-06 18:10:20 +0000150 sys::Path In = InFile;
Mikhail Glushenkov4f6e3a42008-05-06 17:28:03 +0000151 const Node* CurNode = StartNode;
Mikhail Glushenkov2ba4c5a2008-05-06 17:25:51 +0000152
Mikhail Glushenkov2ba4c5a2008-05-06 17:25:51 +0000153 while(!Last) {
Mikhail Glushenkovbbbc9d42008-05-06 17:27:37 +0000154 sys::Path Out;
Mikhail Glushenkov4f6e3a42008-05-06 17:28:03 +0000155 Tool* CurTool = CurNode->ToolPtr.getPtr();
Mikhail Glushenkov2ba4c5a2008-05-06 17:25:51 +0000156
157 if (CurTool->IsJoin()) {
Mikhail Glushenkov35a85e82008-05-06 18:10:20 +0000158 JoinTool& JT = dynamic_cast<JoinTool&>(*CurTool);
159 JT.AddToJoinList(In);
Mikhail Glushenkov2ba4c5a2008-05-06 17:25:51 +0000160 break;
161 }
162
Mikhail Glushenkov35a85e82008-05-06 18:10:20 +0000163 // Since toolchains do not have to end with a Join node, we should
164 // check if this Node is the last.
Mikhail Glushenkov4f6e3a42008-05-06 17:28:03 +0000165 if (!CurNode->HasChildren() || CurTool->IsLast()) {
Mikhail Glushenkov35a85e82008-05-06 18:10:20 +0000166 if (!OutputFilename.empty()) {
167 Out.set(OutputFilename);
168 }
169 else {
Mikhail Glushenkov2ba4c5a2008-05-06 17:25:51 +0000170 Out.set(In.getBasename());
Mikhail Glushenkov35a85e82008-05-06 18:10:20 +0000171 Out.appendSuffix(CurTool->OutputSuffix());
172 }
Mikhail Glushenkov2ba4c5a2008-05-06 17:25:51 +0000173 Last = true;
174 }
175 else {
Mikhail Glushenkov35a85e82008-05-06 18:10:20 +0000176 Out = MakeTempFile(TempDir, In.getBasename(), CurTool->OutputSuffix());
Mikhail Glushenkov2ba4c5a2008-05-06 17:25:51 +0000177 }
178
179 if (CurTool->GenerateAction(In, Out).Execute() != 0)
180 throw std::runtime_error("Tool returned error code!");
181
Mikhail Glushenkov87416b42008-05-06 18:10:53 +0000182 if (Last)
183 return;
184
Mikhail Glushenkov4f6e3a42008-05-06 17:28:03 +0000185 CurNode = &getNode(ChooseEdge(CurNode->OutEdges,
Mikhail Glushenkov76b1b242008-05-06 18:15:12 +0000186 InLangs,
Mikhail Glushenkov4f6e3a42008-05-06 17:28:03 +0000187 CurNode->Name())->ToolName());
Mikhail Glushenkov2ba4c5a2008-05-06 17:25:51 +0000188 In = Out; Out.clear();
189 }
Mikhail Glushenkov2ba4c5a2008-05-06 17:25:51 +0000190}
191
Mikhail Glushenkov02606582008-05-06 18:07:14 +0000192// Sort the nodes in topological order.
193void CompilationGraph::TopologicalSort(std::vector<const Node*>& Out) {
194 std::queue<const Node*> Q;
195 Q.push(&getNode("root"));
196
197 while (!Q.empty()) {
198 const Node* A = Q.front();
199 Q.pop();
200 Out.push_back(A);
201 for (Node::const_iterator EB = A->EdgesBegin(), EE = A->EdgesEnd();
202 EB != EE; ++EB) {
203 Node* B = &getNode((*EB)->ToolName());
204 B->DecrInEdges();
205 if (B->HasNoInEdges())
206 Q.push(B);
207 }
208 }
209}
210
211namespace {
212 bool NotJoinNode(const Node* N) {
213 return N->ToolPtr ? !N->ToolPtr->IsJoin() : true;
214 }
215}
216
217// Call TopologicalSort and filter the resulting list to include
218// only Join nodes.
219void CompilationGraph::
220TopologicalSortFilterJoinNodes(std::vector<const Node*>& Out) {
221 std::vector<const Node*> TopSorted;
222 TopologicalSort(TopSorted);
223 std::remove_copy_if(TopSorted.begin(), TopSorted.end(),
224 std::back_inserter(Out), NotJoinNode);
225}
226
227// Find head of the toolchain corresponding to the given file.
Mikhail Glushenkov76b1b242008-05-06 18:15:12 +0000228// Also, insert an input language into InLangs.
Mikhail Glushenkov87416b42008-05-06 18:10:53 +0000229const Node* CompilationGraph::
Mikhail Glushenkov76b1b242008-05-06 18:15:12 +0000230FindToolChain(const sys::Path& In, const std::string* forceLanguage,
231 InputLanguagesSet& InLangs) const {
232
233 // Determine the input language.
Mikhail Glushenkov87416b42008-05-06 18:10:53 +0000234 const std::string& InLanguage =
235 forceLanguage ? *forceLanguage : getLanguage(In);
Mikhail Glushenkov76b1b242008-05-06 18:15:12 +0000236
237 // Add the current input language to the input language set.
238 InLangs.insert(InLanguage);
239
240 // Find the toolchain for the input language.
Mikhail Glushenkov02606582008-05-06 18:07:14 +0000241 const tools_vector_type& TV = getToolsVector(InLanguage);
242 if (TV.empty())
243 throw std::runtime_error("No toolchain corresponding to language"
244 + InLanguage + " found!");
Mikhail Glushenkov76b1b242008-05-06 18:15:12 +0000245 return &getNode(ChooseEdge(TV, InLangs)->ToolName());
Mikhail Glushenkov02606582008-05-06 18:07:14 +0000246}
247
Mikhail Glushenkovbe9d9a12008-05-06 18:08:59 +0000248// Build the targets. Command-line options are passed through
249// temporary variables.
Mikhail Glushenkov02606582008-05-06 18:07:14 +0000250int CompilationGraph::Build (const sys::Path& TempDir) {
Anton Korobeynikovac67b7e2008-03-23 08:57:20 +0000251
Mikhail Glushenkov76b1b242008-05-06 18:15:12 +0000252 InputLanguagesSet InLangs;
253
Mikhail Glushenkov87416b42008-05-06 18:10:53 +0000254 // This is related to -x option handling.
255 cl::list<std::string>::const_iterator xIter = Languages.begin(),
256 xBegin = xIter, xEnd = Languages.end();
257 bool xEmpty = true;
258 const std::string* xLanguage = 0;
259 unsigned xPos = 0, xPosNext = 0, filePos = 0;
260
261 if (xIter != xEnd) {
262 xEmpty = false;
263 xPos = Languages.getPosition(xIter - xBegin);
264 cl::list<std::string>::const_iterator xNext = llvm::next(xIter);
265 xPosNext = (xNext == xEnd) ? std::numeric_limits<unsigned>::max()
266 : Languages.getPosition(xNext - xBegin);
267 xLanguage = (*xIter == "none") ? 0 : &(*xIter);
268 }
269
Mikhail Glushenkov4f6e3a42008-05-06 17:28:03 +0000270 // For each input file:
Anton Korobeynikovac67b7e2008-03-23 08:57:20 +0000271 for (cl::list<std::string>::const_iterator B = InputFilenames.begin(),
Mikhail Glushenkov87416b42008-05-06 18:10:53 +0000272 CB = B, E = InputFilenames.end(); B != E; ++B) {
Mikhail Glushenkovbbbc9d42008-05-06 17:27:37 +0000273 sys::Path In = sys::Path(*B);
Anton Korobeynikovac67b7e2008-03-23 08:57:20 +0000274
Mikhail Glushenkov87416b42008-05-06 18:10:53 +0000275 // Code for handling the -x option.
276 // Output: std::string* xLanguage (can be NULL).
277 if (!xEmpty) {
278 filePos = InputFilenames.getPosition(B - CB);
279
280 if (xPos < filePos) {
281 if (filePos < xPosNext) {
282 xLanguage = (*xIter == "none") ? 0 : &(*xIter);
283 }
284 else { // filePos >= xPosNext
285 // Skip xIters while filePos > xPosNext
286 while (filePos > xPosNext) {
287 ++xIter;
288 xPos = xPosNext;
289
290 cl::list<std::string>::const_iterator xNext = llvm::next(xIter);
291 if (xNext == xEnd)
292 xPosNext = std::numeric_limits<unsigned>::max();
293 else
294 xPosNext = Languages.getPosition(xNext - xBegin);
295 xLanguage = (*xIter == "none") ? 0 : &(*xIter);
296 }
297 }
298 }
299 }
300
Mikhail Glushenkov02606582008-05-06 18:07:14 +0000301 // Find the toolchain corresponding to this file.
Mikhail Glushenkov76b1b242008-05-06 18:15:12 +0000302 const Node* N = FindToolChain(In, xLanguage, InLangs);
Mikhail Glushenkov02606582008-05-06 18:07:14 +0000303 // Pass file through the chain starting at head.
Mikhail Glushenkov76b1b242008-05-06 18:15:12 +0000304 PassThroughGraph(In, N, InLangs, TempDir);
Anton Korobeynikovac67b7e2008-03-23 08:57:20 +0000305 }
306
Mikhail Glushenkov02606582008-05-06 18:07:14 +0000307 std::vector<const Node*> JTV;
308 TopologicalSortFilterJoinNodes(JTV);
Anton Korobeynikovac67b7e2008-03-23 08:57:20 +0000309
Mikhail Glushenkov02606582008-05-06 18:07:14 +0000310 // For all join nodes in topological order:
311 for (std::vector<const Node*>::iterator B = JTV.begin(), E = JTV.end();
312 B != E; ++B) {
Mikhail Glushenkovbe9d9a12008-05-06 18:08:59 +0000313
Mikhail Glushenkov3d688222008-05-06 18:07:48 +0000314 sys::Path Out;
315 const Node* CurNode = *B;
316 JoinTool* JT = &dynamic_cast<JoinTool&>(*CurNode->ToolPtr.getPtr());
317 bool IsLast = false;
318
Mikhail Glushenkov35a85e82008-05-06 18:10:20 +0000319 // Are there any files to be joined?
Mikhail Glushenkov3d688222008-05-06 18:07:48 +0000320 if (JT->JoinListEmpty())
321 continue;
322
Mikhail Glushenkov35a85e82008-05-06 18:10:20 +0000323 // Is this the last tool in the chain?
324 // NOTE: we can process several chains in parallel.
Mikhail Glushenkov3d688222008-05-06 18:07:48 +0000325 if (!CurNode->HasChildren() || JT->IsLast()) {
326 if (OutputFilename.empty()) {
327 Out.set("a");
328 Out.appendSuffix(JT->OutputSuffix());
329 }
330 else
331 Out.set(OutputFilename);
332 IsLast = true;
333 }
334 else {
Mikhail Glushenkov35a85e82008-05-06 18:10:20 +0000335 Out = MakeTempFile(TempDir, "tmp", JT->OutputSuffix());
Mikhail Glushenkov3d688222008-05-06 18:07:48 +0000336 }
337
338 if (JT->GenerateAction(Out).Execute() != 0)
339 throw std::runtime_error("Tool returned error code!");
340
341 if (!IsLast) {
Mikhail Glushenkov76b1b242008-05-06 18:15:12 +0000342 const Node* NextNode =
343 &getNode(ChooseEdge(CurNode->OutEdges, InLangs,
344 CurNode->Name())->ToolName());
345 PassThroughGraph(Out, NextNode, InLangs, TempDir);
Mikhail Glushenkov3d688222008-05-06 18:07:48 +0000346 }
Anton Korobeynikovac67b7e2008-03-23 08:57:20 +0000347 }
Anton Korobeynikovac67b7e2008-03-23 08:57:20 +0000348
349 return 0;
350}
Mikhail Glushenkov0d08db02008-05-06 16:35:25 +0000351
Mikhail Glushenkovbbbc9d42008-05-06 17:27:37 +0000352// Code related to graph visualization.
353
Mikhail Glushenkov0d08db02008-05-06 16:35:25 +0000354namespace llvm {
355 template <>
Mikhail Glushenkovbe9d9a12008-05-06 18:08:59 +0000356 struct DOTGraphTraits<llvmc::CompilationGraph*>
Mikhail Glushenkov0d08db02008-05-06 16:35:25 +0000357 : public DefaultDOTGraphTraits
358 {
359
Mikhail Glushenkov97fda6d2008-05-06 17:26:14 +0000360 template<typename GraphType>
361 static std::string getNodeLabel(const Node* N, const GraphType&)
362 {
363 if (N->ToolPtr)
364 if (N->ToolPtr->IsJoin())
365 return N->Name() + "\n (join" +
366 (N->HasChildren() ? ")"
367 : std::string(": ") + N->ToolPtr->OutputLanguage() + ')');
368 else
369 return N->Name();
370 else
371 return "root";
372 }
Mikhail Glushenkov0d08db02008-05-06 16:35:25 +0000373
Mikhail Glushenkov97fda6d2008-05-06 17:26:14 +0000374 template<typename EdgeIter>
375 static std::string getEdgeSourceLabel(const Node* N, EdgeIter I) {
376 if (N->ToolPtr)
377 return N->ToolPtr->OutputLanguage();
378 else
379 return I->ToolPtr->InputLanguage();
380 }
Mikhail Glushenkov0d08db02008-05-06 16:35:25 +0000381 };
Mikhail Glushenkov97fda6d2008-05-06 17:26:14 +0000382
Mikhail Glushenkov0d08db02008-05-06 16:35:25 +0000383}
384
385void CompilationGraph::writeGraph() {
386 std::ofstream O("CompilationGraph.dot");
387
Mikhail Glushenkova4db8c02008-05-06 16:37:33 +0000388 if (O.good()) {
Mikhail Glushenkov3d688222008-05-06 18:07:48 +0000389 llvm::WriteGraph(this, "compilation-graph");
Mikhail Glushenkov0d08db02008-05-06 16:35:25 +0000390 O.close();
391 }
392 else {
393 throw std::runtime_error("");
394 }
395}
396
397void CompilationGraph::viewGraph() {
Mikhail Glushenkovd752c3f2008-05-06 16:36:50 +0000398 llvm::ViewGraph(this, "compilation-graph");
Mikhail Glushenkov0d08db02008-05-06 16:35:25 +0000399}