/Users/buildslave/jenkins/workspace/coverage/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 | 145 | ModuleFile *ModuleManager::lookupByFileName(StringRef Name) const { |
45 | 145 | auto Entry = FileMgr.getFile(Name, /*OpenFile=*/false, |
46 | 145 | /*CacheFailure=*/false); |
47 | 145 | if (Entry) |
48 | 145 | return lookup(*Entry); |
49 | | |
50 | 0 | return nullptr; |
51 | 145 | } |
52 | | |
53 | 62.3M | ModuleFile *ModuleManager::lookupByModuleName(StringRef Name) const { |
54 | 62.3M | if (const Module *Mod = HeaderSearchInfo.getModuleMap().findModule(Name)) |
55 | 62.3M | if (const FileEntry *File = Mod->getASTFile()) |
56 | 62.3M | return lookup(File); |
57 | | |
58 | 0 | return nullptr; |
59 | 62.3M | } |
60 | | |
61 | 62.3M | ModuleFile *ModuleManager::lookup(const FileEntry *File) const { |
62 | 62.3M | return Modules.lookup(File); |
63 | 62.3M | } |
64 | | |
65 | | std::unique_ptr<llvm::MemoryBuffer> |
66 | 477k | ModuleManager::lookupBuffer(StringRef Name) { |
67 | 477k | auto Entry = FileMgr.getFile(Name, /*OpenFile=*/false, |
68 | 477k | /*CacheFailure=*/false); |
69 | 477k | if (!Entry) |
70 | 3 | return nullptr; |
71 | 477k | return std::move(InMemoryBuffers[*Entry]); |
72 | 477k | } |
73 | | |
74 | | static bool checkSignature(ASTFileSignature Signature, |
75 | | ASTFileSignature ExpectedSignature, |
76 | 3.31M | std::string &ErrorStr) { |
77 | 3.31M | if (!ExpectedSignature || Signature == ExpectedSignature3.30M ) |
78 | 3.31M | return false; |
79 | | |
80 | 7 | ErrorStr = |
81 | 7 | Signature ? "signature mismatch" : "could not read module signature"0 ; |
82 | 7 | return true; |
83 | 3.31M | } |
84 | | |
85 | | static void updateModuleImports(ModuleFile &MF, ModuleFile *ImportedBy, |
86 | 3.32M | SourceLocation ImportLoc) { |
87 | 3.32M | if (ImportedBy) { |
88 | 3.30M | MF.ImportedBy.insert(ImportedBy); |
89 | 3.30M | ImportedBy->Imports.insert(&MF); |
90 | 3.30M | } else { |
91 | 17.2k | if (!MF.DirectlyImported) |
92 | 17.2k | MF.ImportLoc = ImportLoc; |
93 | | |
94 | 17.2k | MF.DirectlyImported = true; |
95 | 17.2k | } |
96 | 3.32M | } |
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 | 3.32M | std::string &ErrorStr) { |
107 | 3.32M | Module = nullptr; |
108 | | |
109 | | // Look for the file entry. This only fails if the expected size or |
110 | | // modification time differ. |
111 | 3.32M | OptionalFileEntryRefDegradesToFileEntryPtr Entry; |
112 | 3.32M | if (Type == MK_ExplicitModule || Type == MK_PrebuiltModule3.32M ) { |
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 | 2.87k | ExpectedModTime = 0; |
118 | 2.87k | } |
119 | | // Note: ExpectedSize and ExpectedModTime will be 0 for MK_ImplicitModule |
120 | | // when using an ASTFileSignature. |
121 | 3.32M | if (lookupModuleFile(FileName, ExpectedSize, ExpectedModTime, Entry)) { |
122 | 1 | ErrorStr = "module file out of date"; |
123 | 1 | return OutOfDate; |
124 | 1 | } |
125 | | |
126 | 3.32M | if (!Entry && FileName != "-"2.89k ) { |
127 | 2.88k | ErrorStr = "module file not found"; |
128 | 2.88k | return Missing; |
129 | 2.88k | } |
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 | 3.32M | auto implicitModuleNamesMatch = [](ModuleKind Kind, const ModuleFile *MF, |
146 | 3.32M | FileEntryRef Entry) -> bool { |
147 | 2.84M | if (Kind != MK_ImplicitModule) |
148 | 928 | return true; |
149 | 2.84M | return Entry.getName() == MF->FileName; |
150 | 2.84M | }; |
151 | | |
152 | | // Check whether we already loaded this module, before |
153 | 3.32M | if (ModuleFile *ModuleEntry = Modules.lookup(Entry)) { |
154 | 2.84M | if (implicitModuleNamesMatch(Type, ModuleEntry, *Entry)) { |
155 | | // Check the stored signature. |
156 | 2.84M | if (checkSignature(ModuleEntry->Signature, ExpectedSignature, ErrorStr)) |
157 | 4 | return OutOfDate; |
158 | | |
159 | 2.84M | Module = ModuleEntry; |
160 | 2.84M | updateModuleImports(*ModuleEntry, ImportedBy, ImportLoc); |
161 | 2.84M | return AlreadyLoaded; |
162 | 2.84M | } |
163 | 2.84M | } |
164 | | |
165 | | // Allocate a new module. |
166 | 477k | auto NewModule = std::make_unique<ModuleFile>(Type, Generation); |
167 | 477k | NewModule->Index = Chain.size(); |
168 | 477k | NewModule->FileName = FileName.str(); |
169 | 477k | NewModule->File = Entry; |
170 | 477k | NewModule->ImportLoc = ImportLoc; |
171 | 477k | NewModule->InputFilesValidationTimestamp = 0; |
172 | | |
173 | 477k | if (NewModule->Kind == MK_ImplicitModule) { |
174 | 471k | std::string TimestampFilename = NewModule->getTimestampFilename(); |
175 | 471k | llvm::vfs::Status Status; |
176 | | // A cached stat value would be fine as well. |
177 | 471k | if (!FileMgr.getNoncachedStatValue(TimestampFilename, Status)) |
178 | 16 | NewModule->InputFilesValidationTimestamp = |
179 | 16 | llvm::sys::toTimeT(Status.getLastModificationTime()); |
180 | 471k | } |
181 | | |
182 | | // Load the contents of the module |
183 | 477k | if (std::unique_ptr<llvm::MemoryBuffer> Buffer = lookupBuffer(FileName)) { |
184 | | // The buffer was already provided for us. |
185 | 75 | NewModule->Buffer = &ModuleCache->addBuiltPCM(FileName, std::move(Buffer)); |
186 | | // Since the cached buffer is reused, it is safe to close the file |
187 | | // descriptor that was opened while stat()ing the PCM in |
188 | | // lookupModuleFile() above, it won't be needed any longer. |
189 | 75 | Entry->closeFile(); |
190 | 477k | } else if (llvm::MemoryBuffer *Buffer = |
191 | 477k | getModuleCache().lookupPCM(FileName)) { |
192 | 179k | NewModule->Buffer = Buffer; |
193 | | // As above, the file descriptor is no longer needed. |
194 | 179k | Entry->closeFile(); |
195 | 297k | } else if (getModuleCache().shouldBuildPCM(FileName)) { |
196 | | // Report that the module is out of date, since we tried (and failed) to |
197 | | // import it earlier. |
198 | 19 | Entry->closeFile(); |
199 | 19 | return OutOfDate; |
200 | 297k | } else { |
201 | | // Open the AST file. |
202 | 297k | llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> Buf((std::error_code())); |
203 | 297k | if (FileName == "-") { |
204 | 3 | Buf = llvm::MemoryBuffer::getSTDIN(); |
205 | 297k | } else { |
206 | | // Get a buffer of the file and close the file descriptor when done. |
207 | | // The file is volatile because in a parallel build we expect multiple |
208 | | // compiler processes to use the same module file rebuilding it if needed. |
209 | | // |
210 | | // RequiresNullTerminator is false because module files don't need it, and |
211 | | // this allows the file to still be mmapped. |
212 | 297k | Buf = FileMgr.getBufferForFile(*NewModule->File, |
213 | 297k | /*IsVolatile=*/true, |
214 | 297k | /*RequiresNullTerminator=*/false); |
215 | 297k | } |
216 | | |
217 | 297k | if (!Buf) { |
218 | 0 | ErrorStr = Buf.getError().message(); |
219 | 0 | return Missing; |
220 | 0 | } |
221 | | |
222 | 297k | NewModule->Buffer = &getModuleCache().addPCM(FileName, std::move(*Buf)); |
223 | 297k | } |
224 | | |
225 | | // Initialize the stream. |
226 | 477k | NewModule->Data = PCHContainerRdr.ExtractPCH(*NewModule->Buffer); |
227 | | |
228 | | // Read the signature eagerly now so that we can check it. Avoid calling |
229 | | // ReadSignature unless there's something to check though. |
230 | 477k | if (ExpectedSignature && checkSignature(ReadSignature(NewModule->Data), |
231 | 463k | ExpectedSignature, ErrorStr)) |
232 | 3 | return OutOfDate; |
233 | | |
234 | | // We're keeping this module. Store it everywhere. |
235 | 477k | Module = Modules[Entry] = NewModule.get(); |
236 | | |
237 | 477k | updateModuleImports(*NewModule, ImportedBy, ImportLoc); |
238 | | |
239 | 477k | if (!NewModule->isModule()) |
240 | 4.38k | PCHChain.push_back(NewModule.get()); |
241 | 477k | if (!ImportedBy) |
242 | 12.6k | Roots.push_back(NewModule.get()); |
243 | | |
244 | 477k | Chain.push_back(std::move(NewModule)); |
245 | 477k | return NewlyLoaded; |
246 | 477k | } |
247 | | |
248 | 3.02k | void ModuleManager::removeModules(ModuleIterator First) { |
249 | 3.02k | auto Last = end(); |
250 | 3.02k | if (First == Last) |
251 | 2.90k | return; |
252 | | |
253 | | // Explicitly clear VisitOrder since we might not notice it is stale. |
254 | 125 | VisitOrder.clear(); |
255 | | |
256 | | // Collect the set of module file pointers that we'll be removing. |
257 | 125 | llvm::SmallPtrSet<ModuleFile *, 4> victimSet( |
258 | 125 | (llvm::pointer_iterator<ModuleIterator>(First)), |
259 | 125 | (llvm::pointer_iterator<ModuleIterator>(Last))); |
260 | | |
261 | 157 | auto IsVictim = [&](ModuleFile *MF) { |
262 | 157 | return victimSet.count(MF); |
263 | 157 | }; |
264 | | // Remove any references to the now-destroyed modules. |
265 | 149 | for (auto I = begin(); I != First; ++I24 ) { |
266 | 24 | I->Imports.remove_if(IsVictim); |
267 | 24 | I->ImportedBy.remove_if(IsVictim); |
268 | 24 | } |
269 | 125 | llvm::erase_if(Roots, IsVictim); |
270 | | |
271 | | // Remove the modules from the PCH chain. |
272 | 242 | for (auto I = First; I != Last; ++I117 ) { |
273 | 152 | if (!I->isModule()) { |
274 | 35 | PCHChain.erase(llvm::find(PCHChain, &*I), PCHChain.end()); |
275 | 35 | break; |
276 | 35 | } |
277 | 152 | } |
278 | | |
279 | | // Delete the modules. |
280 | 287 | for (ModuleIterator victim = First; victim != Last; ++victim162 ) |
281 | 162 | Modules.erase(victim->File); |
282 | | |
283 | 125 | Chain.erase(Chain.begin() + (First - begin()), Chain.end()); |
284 | 125 | } |
285 | | |
286 | | void |
287 | | ModuleManager::addInMemoryBuffer(StringRef FileName, |
288 | 75 | std::unique_ptr<llvm::MemoryBuffer> Buffer) { |
289 | 75 | const FileEntry *Entry = |
290 | 75 | FileMgr.getVirtualFile(FileName, Buffer->getBufferSize(), 0); |
291 | 75 | InMemoryBuffers[Entry] = std::move(Buffer); |
292 | 75 | } |
293 | | |
294 | 3.80M | std::unique_ptr<ModuleManager::VisitState> ModuleManager::allocateVisitState() { |
295 | | // Fast path: if we have a cached state, use it. |
296 | 3.80M | if (FirstVisitState) { |
297 | 3.79M | auto Result = std::move(FirstVisitState); |
298 | 3.79M | FirstVisitState = std::move(Result->NextState); |
299 | 3.79M | return Result; |
300 | 3.79M | } |
301 | | |
302 | | // Allocate and return a new state. |
303 | 12.2k | return std::make_unique<VisitState>(size()); |
304 | 3.80M | } |
305 | | |
306 | 3.80M | void ModuleManager::returnVisitState(std::unique_ptr<VisitState> State) { |
307 | 3.80M | assert(State->NextState == nullptr && "Visited state is in list?"); |
308 | 3.80M | State->NextState = std::move(FirstVisitState); |
309 | 3.80M | FirstVisitState = std::move(State); |
310 | 3.80M | } |
311 | | |
312 | 3.45k | void ModuleManager::setGlobalIndex(GlobalModuleIndex *Index) { |
313 | 3.45k | GlobalIndex = Index; |
314 | 3.45k | if (!GlobalIndex) { |
315 | 3.02k | ModulesInCommonWithGlobalIndex.clear(); |
316 | 3.02k | return; |
317 | 3.02k | } |
318 | | |
319 | | // Notify the global module index about all of the modules we've already |
320 | | // loaded. |
321 | 428 | for (ModuleFile &M : *this) |
322 | 3.66k | if (!GlobalIndex->loadedModuleFile(&M)) |
323 | 610 | ModulesInCommonWithGlobalIndex.push_back(&M); |
324 | 428 | } |
325 | | |
326 | 477k | void ModuleManager::moduleFileAccepted(ModuleFile *MF) { |
327 | 477k | if (!GlobalIndex || GlobalIndex->loadedModuleFile(MF)733 ) |
328 | 477k | return; |
329 | | |
330 | 171 | ModulesInCommonWithGlobalIndex.push_back(MF); |
331 | 171 | } |
332 | | |
333 | | ModuleManager::ModuleManager(FileManager &FileMgr, |
334 | | InMemoryModuleCache &ModuleCache, |
335 | | const PCHContainerReader &PCHContainerRdr, |
336 | | const HeaderSearch &HeaderSearchInfo) |
337 | 15.1k | : FileMgr(FileMgr), ModuleCache(&ModuleCache), |
338 | 15.1k | PCHContainerRdr(PCHContainerRdr), HeaderSearchInfo(HeaderSearchInfo) {} |
339 | | |
340 | | void ModuleManager::visit(llvm::function_ref<bool(ModuleFile &M)> Visitor, |
341 | 3.80M | llvm::SmallPtrSetImpl<ModuleFile *> *ModuleFilesHit) { |
342 | | // If the visitation order vector is the wrong size, recompute the order. |
343 | 3.80M | if (VisitOrder.size() != Chain.size()) { |
344 | 11.9k | unsigned N = size(); |
345 | 11.9k | VisitOrder.clear(); |
346 | 11.9k | VisitOrder.reserve(N); |
347 | | |
348 | | // Record the number of incoming edges for each module. When we |
349 | | // encounter a module with no incoming edges, push it into the queue |
350 | | // to seed the queue. |
351 | 11.9k | SmallVector<ModuleFile *, 4> Queue; |
352 | 11.9k | Queue.reserve(N); |
353 | 11.9k | llvm::SmallVector<unsigned, 4> UnusedIncomingEdges; |
354 | 11.9k | UnusedIncomingEdges.resize(size()); |
355 | 1.02M | for (ModuleFile &M : llvm::reverse(*this)) { |
356 | 1.02M | unsigned Size = M.ImportedBy.size(); |
357 | 1.02M | UnusedIncomingEdges[M.Index] = Size; |
358 | 1.02M | if (!Size) |
359 | 40.3k | Queue.push_back(&M); |
360 | 1.02M | } |
361 | | |
362 | | // Traverse the graph, making sure to visit a module before visiting any |
363 | | // of its dependencies. |
364 | 1.03M | while (!Queue.empty()) { |
365 | 1.02M | ModuleFile *CurrentModule = Queue.pop_back_val(); |
366 | 1.02M | VisitOrder.push_back(CurrentModule); |
367 | | |
368 | | // For any module that this module depends on, push it on the |
369 | | // stack (if it hasn't already been marked as visited). |
370 | 5.56M | for (ModuleFile *M : llvm::reverse(CurrentModule->Imports)) { |
371 | | // Remove our current module as an impediment to visiting the |
372 | | // module we depend on. If we were the last unvisited module |
373 | | // that depends on this particular module, push it into the |
374 | | // queue to be visited. |
375 | 5.56M | unsigned &NumUnusedEdges = UnusedIncomingEdges[M->Index]; |
376 | 5.56M | if (NumUnusedEdges && (--NumUnusedEdges == 0)) |
377 | 980k | Queue.push_back(M); |
378 | 5.56M | } |
379 | 1.02M | } |
380 | | |
381 | 11.9k | assert(VisitOrder.size() == N && "Visitation order is wrong?"); |
382 | | |
383 | 11.9k | FirstVisitState = nullptr; |
384 | 11.9k | } |
385 | | |
386 | 3.80M | auto State = allocateVisitState(); |
387 | 3.80M | unsigned VisitNumber = State->NextVisitNumber++; |
388 | | |
389 | | // If the caller has provided us with a hit-set that came from the global |
390 | | // module index, mark every module file in common with the global module |
391 | | // index that is *not* in that set as 'visited'. |
392 | 3.80M | if (ModuleFilesHit && !ModulesInCommonWithGlobalIndex.empty()30.9k ) { |
393 | 139k | for (unsigned I = 0, N = ModulesInCommonWithGlobalIndex.size(); I != N; ++I120k ) |
394 | 120k | { |
395 | 120k | ModuleFile *M = ModulesInCommonWithGlobalIndex[I]; |
396 | 120k | if (!ModuleFilesHit->count(M)) |
397 | 106k | State->VisitNumber[M->Index] = VisitNumber; |
398 | 120k | } |
399 | 18.9k | } |
400 | | |
401 | 384M | for (unsigned I = 0, N = VisitOrder.size(); I != N; ++I381M ) { |
402 | 381M | ModuleFile *CurrentModule = VisitOrder[I]; |
403 | | // Should we skip this module file? |
404 | 381M | if (State->VisitNumber[CurrentModule->Index] == VisitNumber) |
405 | 22.8M | continue; |
406 | | |
407 | | // Visit the module. |
408 | 358M | assert(State->VisitNumber[CurrentModule->Index] == VisitNumber - 1); |
409 | 358M | State->VisitNumber[CurrentModule->Index] = VisitNumber; |
410 | 358M | if (!Visitor(*CurrentModule)) |
411 | 356M | continue; |
412 | | |
413 | | // The visitor has requested that cut off visitation of any |
414 | | // module that the current module depends on. To indicate this |
415 | | // behavior, we mark all of the reachable modules as having been visited. |
416 | 2.01M | ModuleFile *NextModule = CurrentModule; |
417 | 24.7M | do { |
418 | | // For any module that this module depends on, push it on the |
419 | | // stack (if it hasn't already been marked as visited). |
420 | 24.7M | for (llvm::SetVector<ModuleFile *>::iterator |
421 | 24.7M | M = NextModule->Imports.begin(), |
422 | 24.7M | MEnd = NextModule->Imports.end(); |
423 | 119M | M != MEnd; ++M94.4M ) { |
424 | 94.4M | if (State->VisitNumber[(*M)->Index] != VisitNumber) { |
425 | 22.7M | State->Stack.push_back(*M); |
426 | 22.7M | State->VisitNumber[(*M)->Index] = VisitNumber; |
427 | 22.7M | } |
428 | 94.4M | } |
429 | | |
430 | 24.7M | if (State->Stack.empty()) |
431 | 2.01M | break; |
432 | | |
433 | | // Pop the next module off the stack. |
434 | 22.7M | NextModule = State->Stack.pop_back_val(); |
435 | 22.7M | } while (true); |
436 | 2.01M | } |
437 | | |
438 | 3.80M | returnVisitState(std::move(State)); |
439 | 3.80M | } |
440 | | |
441 | | bool ModuleManager::lookupModuleFile(StringRef FileName, off_t ExpectedSize, |
442 | | time_t ExpectedModTime, |
443 | 3.32M | OptionalFileEntryRef &File) { |
444 | 3.32M | File = std::nullopt; |
445 | 3.32M | if (FileName == "-") |
446 | 3 | return false; |
447 | | |
448 | | // Open the file immediately to ensure there is no race between stat'ing and |
449 | | // opening the file. |
450 | 3.32M | OptionalFileEntryRef FileOrErr = |
451 | 3.32M | expectedToOptional(FileMgr.getFileRef(FileName, /*OpenFile=*/true, |
452 | 3.32M | /*CacheFailure=*/false)); |
453 | 3.32M | if (!FileOrErr) |
454 | 2.88k | return false; |
455 | | |
456 | 3.32M | File = *FileOrErr; |
457 | | |
458 | 3.32M | if ((ExpectedSize && ExpectedSize != File->getSize()2.07k ) || |
459 | 3.32M | (ExpectedModTime && ExpectedModTime != File->getModificationTime()122 )) |
460 | | // Do not destroy File, as it may be referenced. If we need to rebuild it, |
461 | | // it will be destroyed by removeModules. |
462 | 1 | return true; |
463 | | |
464 | 3.32M | return false; |
465 | 3.32M | } |
466 | | |
467 | | #ifndef NDEBUG |
468 | | namespace llvm { |
469 | | |
470 | | template<> |
471 | | struct GraphTraits<ModuleManager> { |
472 | | using NodeRef = ModuleFile *; |
473 | | using ChildIteratorType = llvm::SetVector<ModuleFile *>::const_iterator; |
474 | | using nodes_iterator = pointer_iterator<ModuleManager::ModuleConstIterator>; |
475 | | |
476 | 0 | static ChildIteratorType child_begin(NodeRef Node) { |
477 | 0 | return Node->Imports.begin(); |
478 | 0 | } |
479 | | |
480 | 0 | static ChildIteratorType child_end(NodeRef Node) { |
481 | 0 | return Node->Imports.end(); |
482 | 0 | } |
483 | | |
484 | 0 | static nodes_iterator nodes_begin(const ModuleManager &Manager) { |
485 | 0 | return nodes_iterator(Manager.begin()); |
486 | 0 | } |
487 | | |
488 | 0 | static nodes_iterator nodes_end(const ModuleManager &Manager) { |
489 | 0 | return nodes_iterator(Manager.end()); |
490 | 0 | } |
491 | | }; |
492 | | |
493 | | template<> |
494 | | struct DOTGraphTraits<ModuleManager> : public DefaultDOTGraphTraits { |
495 | | explicit DOTGraphTraits(bool IsSimple = false) |
496 | 0 | : DefaultDOTGraphTraits(IsSimple) {} |
497 | | |
498 | 0 | static bool renderGraphFromBottomUp() { return true; } |
499 | | |
500 | 0 | std::string getNodeLabel(ModuleFile *M, const ModuleManager&) { |
501 | 0 | return M->ModuleName; |
502 | 0 | } |
503 | | }; |
504 | | |
505 | | } // namespace llvm |
506 | | |
507 | 0 | void ModuleManager::viewGraph() { |
508 | 0 | llvm::ViewGraph(*this, "Modules"); |
509 | 0 | } |
510 | | #endif |