/src/llvm-project/clang/lib/Serialization/ModuleManager.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | //===- ModuleManager.cpp - Module Manager ---------------------------------===// |
2 | | // |
3 | | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | | // See https://llvm.org/LICENSE.txt for license information. |
5 | | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | | // |
7 | | //===----------------------------------------------------------------------===// |
8 | | // |
9 | | // This file defines the ModuleManager class, which manages a set of loaded |
10 | | // modules for the ASTReader. |
11 | | // |
12 | | //===----------------------------------------------------------------------===// |
13 | | |
14 | | #include "clang/Serialization/ModuleManager.h" |
15 | | #include "clang/Basic/FileManager.h" |
16 | | #include "clang/Basic/LLVM.h" |
17 | | #include "clang/Lex/HeaderSearch.h" |
18 | | #include "clang/Lex/ModuleMap.h" |
19 | | #include "clang/Serialization/GlobalModuleIndex.h" |
20 | | #include "clang/Serialization/InMemoryModuleCache.h" |
21 | | #include "clang/Serialization/ModuleFile.h" |
22 | | #include "clang/Serialization/PCHContainerOperations.h" |
23 | | #include "llvm/ADT/STLExtras.h" |
24 | | #include "llvm/ADT/SetVector.h" |
25 | | #include "llvm/ADT/SmallPtrSet.h" |
26 | | #include "llvm/ADT/SmallVector.h" |
27 | | #include "llvm/ADT/StringRef.h" |
28 | | #include "llvm/ADT/iterator.h" |
29 | | #include "llvm/Support/Chrono.h" |
30 | | #include "llvm/Support/DOTGraphTraits.h" |
31 | | #include "llvm/Support/ErrorOr.h" |
32 | | #include "llvm/Support/GraphWriter.h" |
33 | | #include "llvm/Support/MemoryBuffer.h" |
34 | | #include "llvm/Support/VirtualFileSystem.h" |
35 | | #include <algorithm> |
36 | | #include <cassert> |
37 | | #include <memory> |
38 | | #include <string> |
39 | | #include <system_error> |
40 | | |
41 | | using namespace clang; |
42 | | using namespace serialization; |
43 | | |
44 | 0 | ModuleFile *ModuleManager::lookupByFileName(StringRef Name) const { |
45 | 0 | auto Entry = FileMgr.getFile(Name, /*OpenFile=*/false, |
46 | 0 | /*CacheFailure=*/false); |
47 | 0 | if (Entry) |
48 | 0 | return lookup(*Entry); |
49 | | |
50 | 0 | return nullptr; |
51 | 0 | } |
52 | | |
53 | 0 | ModuleFile *ModuleManager::lookupByModuleName(StringRef Name) const { |
54 | 0 | if (const Module *Mod = HeaderSearchInfo.getModuleMap().findModule(Name)) |
55 | 0 | if (OptionalFileEntryRef File = Mod->getASTFile()) |
56 | 0 | return lookup(*File); |
57 | | |
58 | 0 | return nullptr; |
59 | 0 | } |
60 | | |
61 | 0 | ModuleFile *ModuleManager::lookup(const FileEntry *File) const { |
62 | 0 | return Modules.lookup(File); |
63 | 0 | } |
64 | | |
65 | | std::unique_ptr<llvm::MemoryBuffer> |
66 | 0 | ModuleManager::lookupBuffer(StringRef Name) { |
67 | 0 | auto Entry = FileMgr.getFile(Name, /*OpenFile=*/false, |
68 | 0 | /*CacheFailure=*/false); |
69 | 0 | if (!Entry) |
70 | 0 | return nullptr; |
71 | 0 | return std::move(InMemoryBuffers[*Entry]); |
72 | 0 | } |
73 | | |
74 | | static bool checkSignature(ASTFileSignature Signature, |
75 | | ASTFileSignature ExpectedSignature, |
76 | 0 | std::string &ErrorStr) { |
77 | 0 | if (!ExpectedSignature || Signature == ExpectedSignature) |
78 | 0 | return false; |
79 | | |
80 | 0 | ErrorStr = |
81 | 0 | Signature ? "signature mismatch" : "could not read module signature"; |
82 | 0 | return true; |
83 | 0 | } |
84 | | |
85 | | static void updateModuleImports(ModuleFile &MF, ModuleFile *ImportedBy, |
86 | 0 | SourceLocation ImportLoc) { |
87 | 0 | if (ImportedBy) { |
88 | 0 | MF.ImportedBy.insert(ImportedBy); |
89 | 0 | ImportedBy->Imports.insert(&MF); |
90 | 0 | } else { |
91 | 0 | if (!MF.DirectlyImported) |
92 | 0 | MF.ImportLoc = ImportLoc; |
93 | |
|
94 | 0 | MF.DirectlyImported = true; |
95 | 0 | } |
96 | 0 | } |
97 | | |
98 | | ModuleManager::AddModuleResult |
99 | | ModuleManager::addModule(StringRef FileName, ModuleKind Type, |
100 | | SourceLocation ImportLoc, ModuleFile *ImportedBy, |
101 | | unsigned Generation, |
102 | | off_t ExpectedSize, time_t ExpectedModTime, |
103 | | ASTFileSignature ExpectedSignature, |
104 | | ASTFileSignatureReader ReadSignature, |
105 | | ModuleFile *&Module, |
106 | 0 | std::string &ErrorStr) { |
107 | 0 | Module = nullptr; |
108 | | |
109 | | // Look for the file entry. This only fails if the expected size or |
110 | | // modification time differ. |
111 | 0 | OptionalFileEntryRef Entry; |
112 | 0 | if (Type == MK_ExplicitModule || Type == MK_PrebuiltModule) { |
113 | | // If we're not expecting to pull this file out of the module cache, it |
114 | | // might have a different mtime due to being moved across filesystems in |
115 | | // a distributed build. The size must still match, though. (As must the |
116 | | // contents, but we can't check that.) |
117 | 0 | ExpectedModTime = 0; |
118 | 0 | } |
119 | | // Note: ExpectedSize and ExpectedModTime will be 0 for MK_ImplicitModule |
120 | | // when using an ASTFileSignature. |
121 | 0 | if (lookupModuleFile(FileName, ExpectedSize, ExpectedModTime, Entry)) { |
122 | 0 | ErrorStr = "module file out of date"; |
123 | 0 | return OutOfDate; |
124 | 0 | } |
125 | | |
126 | 0 | if (!Entry) { |
127 | 0 | ErrorStr = "module file not found"; |
128 | 0 | return Missing; |
129 | 0 | } |
130 | | |
131 | | // The ModuleManager's use of FileEntry nodes as the keys for its map of |
132 | | // loaded modules is less than ideal. Uniqueness for FileEntry nodes is |
133 | | // maintained by FileManager, which in turn uses inode numbers on hosts |
134 | | // that support that. When coupled with the module cache's proclivity for |
135 | | // turning over and deleting stale PCMs, this means entries for different |
136 | | // module files can wind up reusing the same underlying inode. When this |
137 | | // happens, subsequent accesses to the Modules map will disagree on the |
138 | | // ModuleFile associated with a given file. In general, it is not sufficient |
139 | | // to resolve this conundrum with a type like FileEntryRef that stores the |
140 | | // name of the FileEntry node on first access because of path canonicalization |
141 | | // issues. However, the paths constructed for implicit module builds are |
142 | | // fully under Clang's control. We *can*, therefore, rely on their structure |
143 | | // being consistent across operating systems and across subsequent accesses |
144 | | // to the Modules map. |
145 | 0 | auto implicitModuleNamesMatch = [](ModuleKind Kind, const ModuleFile *MF, |
146 | 0 | FileEntryRef Entry) -> bool { |
147 | 0 | if (Kind != MK_ImplicitModule) |
148 | 0 | return true; |
149 | 0 | return Entry.getName() == MF->FileName; |
150 | 0 | }; |
151 | | |
152 | | // Check whether we already loaded this module, before |
153 | 0 | if (ModuleFile *ModuleEntry = Modules.lookup(*Entry)) { |
154 | 0 | if (implicitModuleNamesMatch(Type, ModuleEntry, *Entry)) { |
155 | | // Check the stored signature. |
156 | 0 | if (checkSignature(ModuleEntry->Signature, ExpectedSignature, ErrorStr)) |
157 | 0 | return OutOfDate; |
158 | | |
159 | 0 | Module = ModuleEntry; |
160 | 0 | updateModuleImports(*ModuleEntry, ImportedBy, ImportLoc); |
161 | 0 | return AlreadyLoaded; |
162 | 0 | } |
163 | 0 | } |
164 | | |
165 | | // Allocate a new module. |
166 | 0 | auto NewModule = std::make_unique<ModuleFile>(Type, *Entry, Generation); |
167 | 0 | NewModule->Index = Chain.size(); |
168 | 0 | NewModule->FileName = FileName.str(); |
169 | 0 | NewModule->ImportLoc = ImportLoc; |
170 | 0 | NewModule->InputFilesValidationTimestamp = 0; |
171 | |
|
172 | 0 | if (NewModule->Kind == MK_ImplicitModule) { |
173 | 0 | std::string TimestampFilename = NewModule->getTimestampFilename(); |
174 | 0 | llvm::vfs::Status Status; |
175 | | // A cached stat value would be fine as well. |
176 | 0 | if (!FileMgr.getNoncachedStatValue(TimestampFilename, Status)) |
177 | 0 | NewModule->InputFilesValidationTimestamp = |
178 | 0 | llvm::sys::toTimeT(Status.getLastModificationTime()); |
179 | 0 | } |
180 | | |
181 | | // Load the contents of the module |
182 | 0 | if (std::unique_ptr<llvm::MemoryBuffer> Buffer = lookupBuffer(FileName)) { |
183 | | // The buffer was already provided for us. |
184 | 0 | NewModule->Buffer = &ModuleCache->addBuiltPCM(FileName, std::move(Buffer)); |
185 | | // Since the cached buffer is reused, it is safe to close the file |
186 | | // descriptor that was opened while stat()ing the PCM in |
187 | | // lookupModuleFile() above, it won't be needed any longer. |
188 | 0 | Entry->closeFile(); |
189 | 0 | } else if (llvm::MemoryBuffer *Buffer = |
190 | 0 | getModuleCache().lookupPCM(FileName)) { |
191 | 0 | NewModule->Buffer = Buffer; |
192 | | // As above, the file descriptor is no longer needed. |
193 | 0 | Entry->closeFile(); |
194 | 0 | } else if (getModuleCache().shouldBuildPCM(FileName)) { |
195 | | // Report that the module is out of date, since we tried (and failed) to |
196 | | // import it earlier. |
197 | 0 | Entry->closeFile(); |
198 | 0 | return OutOfDate; |
199 | 0 | } else { |
200 | | // Get a buffer of the file and close the file descriptor when done. |
201 | | // The file is volatile because in a parallel build we expect multiple |
202 | | // compiler processes to use the same module file rebuilding it if needed. |
203 | | // |
204 | | // RequiresNullTerminator is false because module files don't need it, and |
205 | | // this allows the file to still be mmapped. |
206 | 0 | auto Buf = FileMgr.getBufferForFile(NewModule->File, |
207 | 0 | /*IsVolatile=*/true, |
208 | 0 | /*RequiresNullTerminator=*/false); |
209 | |
|
210 | 0 | if (!Buf) { |
211 | 0 | ErrorStr = Buf.getError().message(); |
212 | 0 | return Missing; |
213 | 0 | } |
214 | | |
215 | 0 | NewModule->Buffer = &getModuleCache().addPCM(FileName, std::move(*Buf)); |
216 | 0 | } |
217 | | |
218 | | // Initialize the stream. |
219 | 0 | NewModule->Data = PCHContainerRdr.ExtractPCH(*NewModule->Buffer); |
220 | | |
221 | | // Read the signature eagerly now so that we can check it. Avoid calling |
222 | | // ReadSignature unless there's something to check though. |
223 | 0 | if (ExpectedSignature && checkSignature(ReadSignature(NewModule->Data), |
224 | 0 | ExpectedSignature, ErrorStr)) |
225 | 0 | return OutOfDate; |
226 | | |
227 | | // We're keeping this module. Store it everywhere. |
228 | 0 | Module = Modules[*Entry] = NewModule.get(); |
229 | |
|
230 | 0 | updateModuleImports(*NewModule, ImportedBy, ImportLoc); |
231 | |
|
232 | 0 | if (!NewModule->isModule()) |
233 | 0 | PCHChain.push_back(NewModule.get()); |
234 | 0 | if (!ImportedBy) |
235 | 0 | Roots.push_back(NewModule.get()); |
236 | |
|
237 | 0 | Chain.push_back(std::move(NewModule)); |
238 | 0 | return NewlyLoaded; |
239 | 0 | } |
240 | | |
241 | 0 | void ModuleManager::removeModules(ModuleIterator First) { |
242 | 0 | auto Last = end(); |
243 | 0 | if (First == Last) |
244 | 0 | return; |
245 | | |
246 | | // Explicitly clear VisitOrder since we might not notice it is stale. |
247 | 0 | VisitOrder.clear(); |
248 | | |
249 | | // Collect the set of module file pointers that we'll be removing. |
250 | 0 | llvm::SmallPtrSet<ModuleFile *, 4> victimSet( |
251 | 0 | (llvm::pointer_iterator<ModuleIterator>(First)), |
252 | 0 | (llvm::pointer_iterator<ModuleIterator>(Last))); |
253 | |
|
254 | 0 | auto IsVictim = [&](ModuleFile *MF) { |
255 | 0 | return victimSet.count(MF); |
256 | 0 | }; |
257 | | // Remove any references to the now-destroyed modules. |
258 | 0 | for (auto I = begin(); I != First; ++I) { |
259 | 0 | I->Imports.remove_if(IsVictim); |
260 | 0 | I->ImportedBy.remove_if(IsVictim); |
261 | 0 | } |
262 | 0 | llvm::erase_if(Roots, IsVictim); |
263 | | |
264 | | // Remove the modules from the PCH chain. |
265 | 0 | for (auto I = First; I != Last; ++I) { |
266 | 0 | if (!I->isModule()) { |
267 | 0 | PCHChain.erase(llvm::find(PCHChain, &*I), PCHChain.end()); |
268 | 0 | break; |
269 | 0 | } |
270 | 0 | } |
271 | | |
272 | | // Delete the modules. |
273 | 0 | for (ModuleIterator victim = First; victim != Last; ++victim) |
274 | 0 | Modules.erase(victim->File); |
275 | |
|
276 | 0 | Chain.erase(Chain.begin() + (First - begin()), Chain.end()); |
277 | 0 | } |
278 | | |
279 | | void |
280 | | ModuleManager::addInMemoryBuffer(StringRef FileName, |
281 | 0 | std::unique_ptr<llvm::MemoryBuffer> Buffer) { |
282 | 0 | const FileEntry *Entry = |
283 | 0 | FileMgr.getVirtualFile(FileName, Buffer->getBufferSize(), 0); |
284 | 0 | InMemoryBuffers[Entry] = std::move(Buffer); |
285 | 0 | } |
286 | | |
287 | 0 | std::unique_ptr<ModuleManager::VisitState> ModuleManager::allocateVisitState() { |
288 | | // Fast path: if we have a cached state, use it. |
289 | 0 | if (FirstVisitState) { |
290 | 0 | auto Result = std::move(FirstVisitState); |
291 | 0 | FirstVisitState = std::move(Result->NextState); |
292 | 0 | return Result; |
293 | 0 | } |
294 | | |
295 | | // Allocate and return a new state. |
296 | 0 | return std::make_unique<VisitState>(size()); |
297 | 0 | } |
298 | | |
299 | 0 | void ModuleManager::returnVisitState(std::unique_ptr<VisitState> State) { |
300 | 0 | assert(State->NextState == nullptr && "Visited state is in list?"); |
301 | 0 | State->NextState = std::move(FirstVisitState); |
302 | 0 | FirstVisitState = std::move(State); |
303 | 0 | } |
304 | | |
305 | 0 | void ModuleManager::setGlobalIndex(GlobalModuleIndex *Index) { |
306 | 0 | GlobalIndex = Index; |
307 | 0 | if (!GlobalIndex) { |
308 | 0 | ModulesInCommonWithGlobalIndex.clear(); |
309 | 0 | return; |
310 | 0 | } |
311 | | |
312 | | // Notify the global module index about all of the modules we've already |
313 | | // loaded. |
314 | 0 | for (ModuleFile &M : *this) |
315 | 0 | if (!GlobalIndex->loadedModuleFile(&M)) |
316 | 0 | ModulesInCommonWithGlobalIndex.push_back(&M); |
317 | 0 | } |
318 | | |
319 | 0 | void ModuleManager::moduleFileAccepted(ModuleFile *MF) { |
320 | 0 | if (!GlobalIndex || GlobalIndex->loadedModuleFile(MF)) |
321 | 0 | return; |
322 | | |
323 | 0 | ModulesInCommonWithGlobalIndex.push_back(MF); |
324 | 0 | } |
325 | | |
326 | | ModuleManager::ModuleManager(FileManager &FileMgr, |
327 | | InMemoryModuleCache &ModuleCache, |
328 | | const PCHContainerReader &PCHContainerRdr, |
329 | | const HeaderSearch &HeaderSearchInfo) |
330 | | : FileMgr(FileMgr), ModuleCache(&ModuleCache), |
331 | 0 | PCHContainerRdr(PCHContainerRdr), HeaderSearchInfo(HeaderSearchInfo) {} |
332 | | |
333 | | void ModuleManager::visit(llvm::function_ref<bool(ModuleFile &M)> Visitor, |
334 | 0 | llvm::SmallPtrSetImpl<ModuleFile *> *ModuleFilesHit) { |
335 | | // If the visitation order vector is the wrong size, recompute the order. |
336 | 0 | if (VisitOrder.size() != Chain.size()) { |
337 | 0 | unsigned N = size(); |
338 | 0 | VisitOrder.clear(); |
339 | 0 | VisitOrder.reserve(N); |
340 | | |
341 | | // Record the number of incoming edges for each module. When we |
342 | | // encounter a module with no incoming edges, push it into the queue |
343 | | // to seed the queue. |
344 | 0 | SmallVector<ModuleFile *, 4> Queue; |
345 | 0 | Queue.reserve(N); |
346 | 0 | llvm::SmallVector<unsigned, 4> UnusedIncomingEdges; |
347 | 0 | UnusedIncomingEdges.resize(size()); |
348 | 0 | for (ModuleFile &M : llvm::reverse(*this)) { |
349 | 0 | unsigned Size = M.ImportedBy.size(); |
350 | 0 | UnusedIncomingEdges[M.Index] = Size; |
351 | 0 | if (!Size) |
352 | 0 | Queue.push_back(&M); |
353 | 0 | } |
354 | | |
355 | | // Traverse the graph, making sure to visit a module before visiting any |
356 | | // of its dependencies. |
357 | 0 | while (!Queue.empty()) { |
358 | 0 | ModuleFile *CurrentModule = Queue.pop_back_val(); |
359 | 0 | VisitOrder.push_back(CurrentModule); |
360 | | |
361 | | // For any module that this module depends on, push it on the |
362 | | // stack (if it hasn't already been marked as visited). |
363 | 0 | for (ModuleFile *M : llvm::reverse(CurrentModule->Imports)) { |
364 | | // Remove our current module as an impediment to visiting the |
365 | | // module we depend on. If we were the last unvisited module |
366 | | // that depends on this particular module, push it into the |
367 | | // queue to be visited. |
368 | 0 | unsigned &NumUnusedEdges = UnusedIncomingEdges[M->Index]; |
369 | 0 | if (NumUnusedEdges && (--NumUnusedEdges == 0)) |
370 | 0 | Queue.push_back(M); |
371 | 0 | } |
372 | 0 | } |
373 | |
|
374 | 0 | assert(VisitOrder.size() == N && "Visitation order is wrong?"); |
375 | | |
376 | 0 | FirstVisitState = nullptr; |
377 | 0 | } |
378 | | |
379 | 0 | auto State = allocateVisitState(); |
380 | 0 | unsigned VisitNumber = State->NextVisitNumber++; |
381 | | |
382 | | // If the caller has provided us with a hit-set that came from the global |
383 | | // module index, mark every module file in common with the global module |
384 | | // index that is *not* in that set as 'visited'. |
385 | 0 | if (ModuleFilesHit && !ModulesInCommonWithGlobalIndex.empty()) { |
386 | 0 | for (unsigned I = 0, N = ModulesInCommonWithGlobalIndex.size(); I != N; ++I) |
387 | 0 | { |
388 | 0 | ModuleFile *M = ModulesInCommonWithGlobalIndex[I]; |
389 | 0 | if (!ModuleFilesHit->count(M)) |
390 | 0 | State->VisitNumber[M->Index] = VisitNumber; |
391 | 0 | } |
392 | 0 | } |
393 | |
|
394 | 0 | for (unsigned I = 0, N = VisitOrder.size(); I != N; ++I) { |
395 | 0 | ModuleFile *CurrentModule = VisitOrder[I]; |
396 | | // Should we skip this module file? |
397 | 0 | if (State->VisitNumber[CurrentModule->Index] == VisitNumber) |
398 | 0 | continue; |
399 | | |
400 | | // Visit the module. |
401 | 0 | assert(State->VisitNumber[CurrentModule->Index] == VisitNumber - 1); |
402 | 0 | State->VisitNumber[CurrentModule->Index] = VisitNumber; |
403 | 0 | if (!Visitor(*CurrentModule)) |
404 | 0 | continue; |
405 | | |
406 | | // The visitor has requested that cut off visitation of any |
407 | | // module that the current module depends on. To indicate this |
408 | | // behavior, we mark all of the reachable modules as having been visited. |
409 | 0 | ModuleFile *NextModule = CurrentModule; |
410 | 0 | do { |
411 | | // For any module that this module depends on, push it on the |
412 | | // stack (if it hasn't already been marked as visited). |
413 | 0 | for (llvm::SetVector<ModuleFile *>::iterator |
414 | 0 | M = NextModule->Imports.begin(), |
415 | 0 | MEnd = NextModule->Imports.end(); |
416 | 0 | M != MEnd; ++M) { |
417 | 0 | if (State->VisitNumber[(*M)->Index] != VisitNumber) { |
418 | 0 | State->Stack.push_back(*M); |
419 | 0 | State->VisitNumber[(*M)->Index] = VisitNumber; |
420 | 0 | } |
421 | 0 | } |
422 | |
|
423 | 0 | if (State->Stack.empty()) |
424 | 0 | break; |
425 | | |
426 | | // Pop the next module off the stack. |
427 | 0 | NextModule = State->Stack.pop_back_val(); |
428 | 0 | } while (true); |
429 | 0 | } |
430 | |
|
431 | 0 | returnVisitState(std::move(State)); |
432 | 0 | } |
433 | | |
434 | | bool ModuleManager::lookupModuleFile(StringRef FileName, off_t ExpectedSize, |
435 | | time_t ExpectedModTime, |
436 | 0 | OptionalFileEntryRef &File) { |
437 | 0 | if (FileName == "-") { |
438 | 0 | File = expectedToOptional(FileMgr.getSTDIN()); |
439 | 0 | return false; |
440 | 0 | } |
441 | | |
442 | | // Open the file immediately to ensure there is no race between stat'ing and |
443 | | // opening the file. |
444 | 0 | File = FileMgr.getOptionalFileRef(FileName, /*OpenFile=*/true, |
445 | 0 | /*CacheFailure=*/false); |
446 | |
|
447 | 0 | if (File && |
448 | 0 | ((ExpectedSize && ExpectedSize != File->getSize()) || |
449 | 0 | (ExpectedModTime && ExpectedModTime != File->getModificationTime()))) |
450 | | // Do not destroy File, as it may be referenced. If we need to rebuild it, |
451 | | // it will be destroyed by removeModules. |
452 | 0 | return true; |
453 | | |
454 | 0 | return false; |
455 | 0 | } |
456 | | |
457 | | #ifndef NDEBUG |
458 | | namespace llvm { |
459 | | |
460 | | template<> |
461 | | struct GraphTraits<ModuleManager> { |
462 | | using NodeRef = ModuleFile *; |
463 | | using ChildIteratorType = llvm::SetVector<ModuleFile *>::const_iterator; |
464 | | using nodes_iterator = pointer_iterator<ModuleManager::ModuleConstIterator>; |
465 | | |
466 | 0 | static ChildIteratorType child_begin(NodeRef Node) { |
467 | 0 | return Node->Imports.begin(); |
468 | 0 | } |
469 | | |
470 | 0 | static ChildIteratorType child_end(NodeRef Node) { |
471 | 0 | return Node->Imports.end(); |
472 | 0 | } |
473 | | |
474 | 0 | static nodes_iterator nodes_begin(const ModuleManager &Manager) { |
475 | 0 | return nodes_iterator(Manager.begin()); |
476 | 0 | } |
477 | | |
478 | 0 | static nodes_iterator nodes_end(const ModuleManager &Manager) { |
479 | 0 | return nodes_iterator(Manager.end()); |
480 | 0 | } |
481 | | }; |
482 | | |
483 | | template<> |
484 | | struct DOTGraphTraits<ModuleManager> : public DefaultDOTGraphTraits { |
485 | | explicit DOTGraphTraits(bool IsSimple = false) |
486 | 0 | : DefaultDOTGraphTraits(IsSimple) {} |
487 | | |
488 | 0 | static bool renderGraphFromBottomUp() { return true; } |
489 | | |
490 | 0 | std::string getNodeLabel(ModuleFile *M, const ModuleManager&) { |
491 | 0 | return M->ModuleName; |
492 | 0 | } |
493 | | }; |
494 | | |
495 | | } // namespace llvm |
496 | | |
497 | 0 | void ModuleManager::viewGraph() { |
498 | 0 | llvm::ViewGraph(*this, "Modules"); |
499 | 0 | } |
500 | | #endif |