Skip to content

Commit 1365b5b

Browse files
authored
[clang][DependencyScanning] Track dependencies from prebuilt modules to determine IsInStableDir (llvm#132237)
When a module is being scanned, it can depend on modules that have already been built from a pch dependency. When this happens, the pcm files are reused for the module dependencies. When this is the case, check if input files recorded from the PCMs come from the provided stable directories transitively since the scanner will not have access to the full set of file dependencies from prebuilt modules.
1 parent b712068 commit 1365b5b

File tree

7 files changed

+278
-81
lines changed

7 files changed

+278
-81
lines changed

clang/include/clang/Serialization/ASTReader.h

+12
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,18 @@ class ASTReaderListener {
237237
return true;
238238
}
239239

240+
/// Overloaded member function of \c visitInputFile that should
241+
/// be defined when there is a distinction between
242+
/// the file name and name-as-requested. For example, when deserializing input
243+
/// files from precompiled AST files.
244+
///
245+
/// \returns true to continue receiving the next input file, false to stop.
246+
virtual bool visitInputFile(StringRef FilenameAsRequested, StringRef Filename,
247+
bool isSystem, bool isOverridden,
248+
bool isExplicitModule) {
249+
return true;
250+
}
251+
240252
/// Returns true if this \c ASTReaderListener wants to receive the
241253
/// imports of the AST file via \c visitImport, false otherwise.
242254
virtual bool needsImportVisitation() const { return false; }

clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h

+54-5
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ namespace dependencies {
3333

3434
class DependencyActionController;
3535
class DependencyConsumer;
36+
class PrebuiltModuleASTAttrs;
3637

3738
/// Modular dependency that has already been built prior to the dependency scan.
3839
struct PrebuiltModuleDep {
@@ -46,6 +47,47 @@ struct PrebuiltModuleDep {
4647
ModuleMapFile(M->PresumedModuleMapFile) {}
4748
};
4849

50+
/// Attributes loaded from AST files of prebuilt modules collected prior to
51+
/// ModuleDepCollector creation.
52+
using PrebuiltModulesAttrsMap = llvm::StringMap<PrebuiltModuleASTAttrs>;
53+
class PrebuiltModuleASTAttrs {
54+
public:
55+
/// When a module is discovered to not be in stable directories, traverse &
56+
/// update all modules that depend on it.
57+
void
58+
updateDependentsNotInStableDirs(PrebuiltModulesAttrsMap &PrebuiltModulesMap);
59+
60+
/// Read-only access to whether the module is made up of dependencies in
61+
/// stable directories.
62+
bool isInStableDir() const { return IsInStableDirs; }
63+
64+
/// Read-only access to vfs map files.
65+
const llvm::StringSet<> &getVFS() const { return VFSMap; }
66+
67+
/// Update the VFSMap to the one discovered from serializing the AST file.
68+
void setVFS(llvm::StringSet<> &&VFS) { VFSMap = std::move(VFS); }
69+
70+
/// Add a direct dependent module file, so it can be updated if the current
71+
/// module is from stable directores.
72+
void addDependent(StringRef ModuleFile) {
73+
ModuleFileDependents.insert(ModuleFile);
74+
}
75+
76+
/// Update whether the prebuilt module resolves entirely in a stable
77+
/// directories.
78+
void setInStableDir(bool V = false) {
79+
// Cannot reset attribute once it's false.
80+
if (!IsInStableDirs)
81+
return;
82+
IsInStableDirs = V;
83+
}
84+
85+
private:
86+
llvm::StringSet<> VFSMap;
87+
bool IsInStableDirs = true;
88+
std::set<StringRef> ModuleFileDependents;
89+
};
90+
4991
/// This is used to identify a specific module.
5092
struct ModuleID {
5193
/// The name of the module. This may include `:` for C++20 module partitions,
@@ -171,8 +213,6 @@ struct ModuleDeps {
171213
BuildInfo;
172214
};
173215

174-
using PrebuiltModuleVFSMapT = llvm::StringMap<llvm::StringSet<>>;
175-
176216
class ModuleDepCollector;
177217

178218
/// Callback that records textual includes and direct modular includes/imports
@@ -242,7 +282,7 @@ class ModuleDepCollector final : public DependencyCollector {
242282
CompilerInstance &ScanInstance, DependencyConsumer &C,
243283
DependencyActionController &Controller,
244284
CompilerInvocation OriginalCI,
245-
PrebuiltModuleVFSMapT PrebuiltModuleVFSMap);
285+
const PrebuiltModulesAttrsMap PrebuiltModulesASTMap);
246286

247287
void attachToPreprocessor(Preprocessor &PP) override;
248288
void attachToASTReader(ASTReader &R) override;
@@ -262,8 +302,9 @@ class ModuleDepCollector final : public DependencyCollector {
262302
DependencyConsumer &Consumer;
263303
/// Callbacks for computing dependency information.
264304
DependencyActionController &Controller;
265-
/// Mapping from prebuilt AST files to their sorted list of VFS overlay files.
266-
PrebuiltModuleVFSMapT PrebuiltModuleVFSMap;
305+
/// Mapping from prebuilt AST filepaths to their attributes referenced during
306+
/// dependency collecting.
307+
const PrebuiltModulesAttrsMap PrebuiltModulesASTMap;
267308
/// Path to the main source file.
268309
std::string MainFile;
269310
/// Hash identifying the compilation conditions of the current TU.
@@ -339,6 +380,14 @@ void resetBenignCodeGenOptions(frontend::ActionKind ProgramAction,
339380
bool isPathInStableDir(const ArrayRef<StringRef> Directories,
340381
const StringRef Input);
341382

383+
/// Determine if options collected from a module's
384+
/// compilation can safely be considered as stable.
385+
///
386+
/// \param Directories Paths known to be in a stable location. e.g. Sysroot.
387+
/// \param HSOpts Header search options derived from the compiler invocation.
388+
bool areOptionsInStableDir(const ArrayRef<StringRef> Directories,
389+
const HeaderSearchOptions &HSOpts);
390+
342391
} // end namespace dependencies
343392
} // end namespace tooling
344393
} // end namespace clang

clang/lib/Frontend/FrontendActions.cpp

+4-3
Original file line numberDiff line numberDiff line change
@@ -777,10 +777,11 @@ namespace {
777777
/// Indicates that the AST file contains particular input file.
778778
///
779779
/// \returns true to continue receiving the next input file, false to stop.
780-
bool visitInputFile(StringRef Filename, bool isSystem,
781-
bool isOverridden, bool isExplicitModule) override {
780+
bool visitInputFile(StringRef FilenameAsRequested, StringRef Filename,
781+
bool isSystem, bool isOverridden,
782+
bool isExplicitModule) override {
782783

783-
Out.indent(2) << "Input file: " << Filename;
784+
Out.indent(2) << "Input file: " << FilenameAsRequested;
784785

785786
if (isSystem || isOverridden || isExplicitModule) {
786787
Out << " [";

clang/lib/Serialization/ASTReader.cpp

+33-7
Original file line numberDiff line numberDiff line change
@@ -2616,6 +2616,14 @@ bool ASTReader::shouldDisableValidationForFile(
26162616
return false;
26172617
}
26182618

2619+
static std::pair<StringRef, StringRef>
2620+
getUnresolvedInputFilenames(const ASTReader::RecordData &Record,
2621+
const StringRef InputBlob) {
2622+
uint16_t AsRequestedLength = Record[7];
2623+
return {InputBlob.substr(0, AsRequestedLength),
2624+
InputBlob.substr(AsRequestedLength)};
2625+
}
2626+
26192627
InputFileInfo ASTReader::getInputFileInfo(ModuleFile &F, unsigned ID) {
26202628
// If this ID is bogus, just return an empty input file.
26212629
if (ID == 0 || ID > F.InputFileInfosLoaded.size())
@@ -2659,11 +2667,12 @@ InputFileInfo ASTReader::getInputFileInfo(ModuleFile &F, unsigned ID) {
26592667
R.Transient = static_cast<bool>(Record[4]);
26602668
R.TopLevel = static_cast<bool>(Record[5]);
26612669
R.ModuleMap = static_cast<bool>(Record[6]);
2662-
uint16_t AsRequestedLength = Record[7];
2663-
R.UnresolvedImportedFilenameAsRequested = Blob.substr(0, AsRequestedLength);
2664-
R.UnresolvedImportedFilename = Blob.substr(AsRequestedLength);
2665-
if (R.UnresolvedImportedFilename.empty())
2666-
R.UnresolvedImportedFilename = R.UnresolvedImportedFilenameAsRequested;
2670+
auto [UnresolvedFilenameAsRequested, UnresolvedFilename] =
2671+
getUnresolvedInputFilenames(Record, Blob);
2672+
R.UnresolvedImportedFilenameAsRequested = UnresolvedFilenameAsRequested;
2673+
R.UnresolvedImportedFilename = UnresolvedFilename.empty()
2674+
? UnresolvedFilenameAsRequested
2675+
: UnresolvedFilename;
26672676

26682677
Expected<llvm::BitstreamEntry> MaybeEntry = Cursor.advance();
26692678
if (!MaybeEntry) // FIXME this drops errors on the floor.
@@ -5716,6 +5725,11 @@ bool ASTReader::readASTFileControlBlock(
57165725
bool DoneWithControlBlock = false;
57175726
SmallString<0> PathBuf;
57185727
PathBuf.reserve(256);
5728+
// Additional path buffer to use when multiple paths need to be resolved.
5729+
// For example, when deserializing input files that contains a path that was
5730+
// resolved from a vfs overlay and an external location.
5731+
SmallString<0> AdditionalPathBuf;
5732+
AdditionalPathBuf.reserve(256);
57195733
while (!DoneWithControlBlock) {
57205734
Expected<llvm::BitstreamEntry> MaybeEntry = Stream.advance();
57215735
if (!MaybeEntry) {
@@ -5847,9 +5861,21 @@ bool ASTReader::readASTFileControlBlock(
58475861
break;
58485862
case INPUT_FILE:
58495863
bool Overridden = static_cast<bool>(Record[3]);
5850-
auto Filename = ResolveImportedPath(PathBuf, Blob, ModuleDir);
5864+
auto [UnresolvedFilenameAsRequested, UnresolvedFilename] =
5865+
getUnresolvedInputFilenames(Record, Blob);
5866+
auto FilenameAsRequestedBuf = ResolveImportedPath(
5867+
PathBuf, UnresolvedFilenameAsRequested, ModuleDir);
5868+
StringRef Filename;
5869+
if (UnresolvedFilename.empty())
5870+
Filename = *FilenameAsRequestedBuf;
5871+
else {
5872+
auto FilenameBuf = ResolveImportedPath(
5873+
AdditionalPathBuf, UnresolvedFilename, ModuleDir);
5874+
Filename = *FilenameBuf;
5875+
}
58515876
shouldContinue = Listener.visitInputFile(
5852-
*Filename, isSystemFile, Overridden, /*IsExplicitModule=*/false);
5877+
*FilenameAsRequestedBuf, Filename, isSystemFile, Overridden,
5878+
/*IsExplicitModule=*/false);
58535879
break;
58545880
}
58555881
if (!shouldContinue)

clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp

+98-19
Original file line numberDiff line numberDiff line change
@@ -90,63 +90,140 @@ static bool checkHeaderSearchPaths(const HeaderSearchOptions &HSOpts,
9090

9191
using PrebuiltModuleFilesT = decltype(HeaderSearchOptions::PrebuiltModuleFiles);
9292

93-
/// A listener that collects the imported modules and optionally the input
94-
/// files.
93+
/// A listener that collects the imported modules and the input
94+
/// files. While visiting, collect vfsoverlays and file inputs that determine
95+
/// whether prebuilt modules fully resolve in stable directories.
9596
class PrebuiltModuleListener : public ASTReaderListener {
9697
public:
9798
PrebuiltModuleListener(PrebuiltModuleFilesT &PrebuiltModuleFiles,
9899
llvm::SmallVector<std::string> &NewModuleFiles,
99-
PrebuiltModuleVFSMapT &PrebuiltModuleVFSMap,
100+
PrebuiltModulesAttrsMap &PrebuiltModulesASTMap,
100101
const HeaderSearchOptions &HSOpts,
101-
const LangOptions &LangOpts, DiagnosticsEngine &Diags)
102+
const LangOptions &LangOpts, DiagnosticsEngine &Diags,
103+
const llvm::SmallVector<StringRef> &StableDirs)
102104
: PrebuiltModuleFiles(PrebuiltModuleFiles),
103105
NewModuleFiles(NewModuleFiles),
104-
PrebuiltModuleVFSMap(PrebuiltModuleVFSMap), ExistingHSOpts(HSOpts),
105-
ExistingLangOpts(LangOpts), Diags(Diags) {}
106+
PrebuiltModulesASTMap(PrebuiltModulesASTMap), ExistingHSOpts(HSOpts),
107+
ExistingLangOpts(LangOpts), Diags(Diags), StableDirs(StableDirs) {}
106108

107109
bool needsImportVisitation() const override { return true; }
110+
bool needsInputFileVisitation() override { return true; }
111+
bool needsSystemInputFileVisitation() override { return true; }
108112

113+
/// Accumulate the modules are transitively depended on by the initial
114+
/// prebuilt module.
109115
void visitImport(StringRef ModuleName, StringRef Filename) override {
110116
if (PrebuiltModuleFiles.insert({ModuleName.str(), Filename.str()}).second)
111117
NewModuleFiles.push_back(Filename.str());
118+
119+
auto PrebuiltMapEntry = PrebuiltModulesASTMap.try_emplace(Filename);
120+
PrebuiltModuleASTAttrs &PrebuiltModule = PrebuiltMapEntry.first->second;
121+
if (PrebuiltMapEntry.second)
122+
PrebuiltModule.setInStableDir(!StableDirs.empty());
123+
124+
if (auto It = PrebuiltModulesASTMap.find(CurrentFile);
125+
It != PrebuiltModulesASTMap.end() && CurrentFile != Filename)
126+
PrebuiltModule.addDependent(It->getKey());
127+
}
128+
129+
/// For each input file discovered, check whether it's external path is in a
130+
/// stable directory. Traversal is stopped if the current module is not
131+
/// considered stable.
132+
bool visitInputFile(StringRef FilenameAsRequested, StringRef Filename,
133+
bool isSystem, bool isOverridden,
134+
bool isExplicitModule) override {
135+
if (StableDirs.empty())
136+
return false;
137+
auto PrebuiltEntryIt = PrebuiltModulesASTMap.find(CurrentFile);
138+
if ((PrebuiltEntryIt == PrebuiltModulesASTMap.end()) ||
139+
(!PrebuiltEntryIt->second.isInStableDir()))
140+
return false;
141+
142+
PrebuiltEntryIt->second.setInStableDir(
143+
isPathInStableDir(StableDirs, Filename));
144+
return PrebuiltEntryIt->second.isInStableDir();
112145
}
113146

147+
/// Update which module that is being actively traversed.
114148
void visitModuleFile(StringRef Filename,
115149
serialization::ModuleKind Kind) override {
150+
// If the CurrentFile is not
151+
// considered stable, update any of it's transitive dependents.
152+
auto PrebuiltEntryIt = PrebuiltModulesASTMap.find(CurrentFile);
153+
if ((PrebuiltEntryIt != PrebuiltModulesASTMap.end()) &&
154+
!PrebuiltEntryIt->second.isInStableDir())
155+
PrebuiltEntryIt->second.updateDependentsNotInStableDirs(
156+
PrebuiltModulesASTMap);
116157
CurrentFile = Filename;
117158
}
118159

160+
/// Check the header search options for a given module when considering
161+
/// if the module comes from stable directories.
162+
bool ReadHeaderSearchOptions(const HeaderSearchOptions &HSOpts,
163+
StringRef ModuleFilename,
164+
StringRef SpecificModuleCachePath,
165+
bool Complain) override {
166+
167+
auto PrebuiltMapEntry = PrebuiltModulesASTMap.try_emplace(CurrentFile);
168+
PrebuiltModuleASTAttrs &PrebuiltModule = PrebuiltMapEntry.first->second;
169+
if (PrebuiltMapEntry.second)
170+
PrebuiltModule.setInStableDir(!StableDirs.empty());
171+
172+
if (PrebuiltModule.isInStableDir())
173+
PrebuiltModule.setInStableDir(areOptionsInStableDir(StableDirs, HSOpts));
174+
175+
return false;
176+
}
177+
178+
/// Accumulate vfsoverlays used to build these prebuilt modules.
119179
bool ReadHeaderSearchPaths(const HeaderSearchOptions &HSOpts,
120180
bool Complain) override {
121-
std::vector<std::string> VFSOverlayFiles = HSOpts.VFSOverlayFiles;
122-
PrebuiltModuleVFSMap.try_emplace(CurrentFile, llvm::from_range,
123-
VFSOverlayFiles);
181+
182+
auto PrebuiltMapEntry = PrebuiltModulesASTMap.try_emplace(CurrentFile);
183+
PrebuiltModuleASTAttrs &PrebuiltModule = PrebuiltMapEntry.first->second;
184+
if (PrebuiltMapEntry.second)
185+
PrebuiltModule.setInStableDir(!StableDirs.empty());
186+
187+
PrebuiltModule.setVFS(
188+
llvm::StringSet<>(llvm::from_range, HSOpts.VFSOverlayFiles));
189+
124190
return checkHeaderSearchPaths(
125191
HSOpts, ExistingHSOpts, Complain ? &Diags : nullptr, ExistingLangOpts);
126192
}
127193

128194
private:
129195
PrebuiltModuleFilesT &PrebuiltModuleFiles;
130196
llvm::SmallVector<std::string> &NewModuleFiles;
131-
PrebuiltModuleVFSMapT &PrebuiltModuleVFSMap;
197+
PrebuiltModulesAttrsMap &PrebuiltModulesASTMap;
132198
const HeaderSearchOptions &ExistingHSOpts;
133199
const LangOptions &ExistingLangOpts;
134200
DiagnosticsEngine &Diags;
135201
std::string CurrentFile;
202+
const llvm::SmallVector<StringRef> &StableDirs;
136203
};
137204

138205
/// Visit the given prebuilt module and collect all of the modules it
139206
/// transitively imports and contributing input files.
140207
static bool visitPrebuiltModule(StringRef PrebuiltModuleFilename,
141208
CompilerInstance &CI,
142209
PrebuiltModuleFilesT &ModuleFiles,
143-
PrebuiltModuleVFSMapT &PrebuiltModuleVFSMap,
210+
PrebuiltModulesAttrsMap &PrebuiltModulesASTMap,
144211
DiagnosticsEngine &Diags) {
212+
213+
// Gather the set of stable directories to use as transitive dependencies are
214+
// discovered.
215+
llvm::SmallVector<StringRef> StableDirs;
216+
std::string SysrootToUse(CI.getHeaderSearchOpts().Sysroot);
217+
if (!SysrootToUse.empty() &&
218+
(llvm::sys::path::root_directory(SysrootToUse) != SysrootToUse))
219+
StableDirs = {SysrootToUse, CI.getHeaderSearchOpts().ResourceDir};
220+
145221
// List of module files to be processed.
146222
llvm::SmallVector<std::string> Worklist;
147-
PrebuiltModuleListener Listener(ModuleFiles, Worklist, PrebuiltModuleVFSMap,
223+
224+
PrebuiltModuleListener Listener(ModuleFiles, Worklist, PrebuiltModulesASTMap,
148225
CI.getHeaderSearchOpts(), CI.getLangOpts(),
149-
Diags);
226+
Diags, StableDirs);
150227

151228
Listener.visitModuleFile(PrebuiltModuleFilename,
152229
serialization::MK_ExplicitModule);
@@ -371,16 +448,18 @@ class DependencyScanningAction : public tooling::ToolAction {
371448
auto *FileMgr = ScanInstance.createFileManager(FS);
372449
ScanInstance.createSourceManager(*FileMgr);
373450

374-
// Store the list of prebuilt module files into header search options. This
375-
// will prevent the implicit build to create duplicate modules and will
376-
// force reuse of the existing prebuilt module files instead.
377-
PrebuiltModuleVFSMapT PrebuiltModuleVFSMap;
451+
// Store a mapping of prebuilt module files and their properties like header
452+
// search options. This will prevent the implicit build to create duplicate
453+
// modules and will force reuse of the existing prebuilt module files
454+
// instead.
455+
PrebuiltModulesAttrsMap PrebuiltModulesASTMap;
456+
378457
if (!ScanInstance.getPreprocessorOpts().ImplicitPCHInclude.empty())
379458
if (visitPrebuiltModule(
380459
ScanInstance.getPreprocessorOpts().ImplicitPCHInclude,
381460
ScanInstance,
382461
ScanInstance.getHeaderSearchOpts().PrebuiltModuleFiles,
383-
PrebuiltModuleVFSMap, ScanInstance.getDiagnostics()))
462+
PrebuiltModulesASTMap, ScanInstance.getDiagnostics()))
384463
return false;
385464

386465
// Create the dependency collector that will collect the produced
@@ -410,7 +489,7 @@ class DependencyScanningAction : public tooling::ToolAction {
410489
case ScanningOutputFormat::Full:
411490
MDC = std::make_shared<ModuleDepCollector>(
412491
Service, std::move(Opts), ScanInstance, Consumer, Controller,
413-
OriginalInvocation, std::move(PrebuiltModuleVFSMap));
492+
OriginalInvocation, std::move(PrebuiltModulesASTMap));
414493
ScanInstance.addDependencyCollector(MDC);
415494
break;
416495
}

0 commit comments

Comments
 (0)