Skip to content

Commit be0aa7b

Browse files
authored
[clang][DependencyScanning] Implementation of CompilerInstanceWithContext to Improve By-Name Queries (#164345)
This PR implements `CompilerInstanceWithContext` to improve by-name dependency queries. Cases exist where we query the dependency of different names, with otherwise identical working directory and compile command line inputs. In these cases, we can create one `CompilerInstance`, whose lifetime is the same as the dependency scanning worker, and reuse the same compiler instance to complete the queries. This way we reduce the amount of header search we need to perform per query, since the already completed header search results are cached in the compiler instance. Using a microbenchmark on a prototype of this implementation, we are seeing a scanning performance improvement of about 20%. The microbenchmark scans a Swift file that imports everything importable. When measuring against a set of internal project builds, the geo mean of total build time improvement is around 1.02x to 1.04x depending on whether the module caches are populated or not. Part of work for rdar://136303612.
1 parent 1f8d5d4 commit be0aa7b

18 files changed

+596
-108
lines changed

clang/include/clang/Frontend/CompilerInstance.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -946,6 +946,12 @@ class CompilerInstance : public ModuleLoader {
946946
DependencyCollectors.push_back(std::move(Listener));
947947
}
948948

949+
void clearDependencyCollectors() { DependencyCollectors.clear(); }
950+
951+
std::vector<std::shared_ptr<DependencyCollector>> &getDependencyCollectors() {
952+
return DependencyCollectors;
953+
}
954+
949955
void setExternalSemaSource(IntrusiveRefCntPtr<ExternalSemaSource> ESS);
950956

951957
ModuleCache &getModuleCache() const { return *ModCache; }

clang/include/clang/Frontend/FrontendActions.h

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -320,15 +320,6 @@ class PrintPreprocessedAction : public PreprocessorFrontendAction {
320320
bool hasPCHSupport() const override { return true; }
321321
};
322322

323-
class GetDependenciesByModuleNameAction : public PreprocessOnlyAction {
324-
StringRef ModuleName;
325-
void ExecuteAction() override;
326-
327-
public:
328-
GetDependenciesByModuleNameAction(StringRef ModuleName)
329-
: ModuleName(ModuleName) {}
330-
};
331-
332323
//===----------------------------------------------------------------------===//
333324
// HLSL Specific Actions
334325
//===----------------------------------------------------------------------===//

clang/include/clang/Lex/Preprocessor.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1327,6 +1327,7 @@ class Preprocessor {
13271327
std::move(Callbacks));
13281328
Callbacks = std::move(C);
13291329
}
1330+
void removePPCallbacks() { Callbacks.reset(); }
13301331
/// \}
13311332

13321333
/// Get the number of tokens processed so far.

clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,11 +154,52 @@ class DependencyScanningTool {
154154
/// Given a compilation context specified via the Clang driver command-line,
155155
/// gather modular dependencies of module with the given name, and return the
156156
/// information needed for explicit build.
157+
/// TODO: this method should be removed as soon as Swift and our C-APIs adopt
158+
/// CompilerInstanceWithContext. We are keeping it here so that it is easier
159+
/// to coordinate with Swift and C-API changes.
157160
llvm::Expected<TranslationUnitDeps> getModuleDependencies(
158161
StringRef ModuleName, const std::vector<std::string> &CommandLine,
159162
StringRef CWD, const llvm::DenseSet<ModuleID> &AlreadySeen,
160163
LookupModuleOutputCallback LookupModuleOutput);
161164

165+
/// The following three methods provide a new interface to perform
166+
/// by name dependency scan. The new interface's intention is to improve
167+
/// dependency scanning performance when a sequence of name is looked up
168+
/// with the same current working directory and the command line.
169+
170+
/// @brief Initializing the context and the compiler instance.
171+
/// This method must be called before calling
172+
/// computeDependenciesByNameWithContext.
173+
/// @param CWD The current working directory used during the scan.
174+
/// @param CommandLine The commandline used for the scan.
175+
/// @return Error if the initializaiton fails.
176+
llvm::Error initializeCompilerInstanceWithContext(
177+
StringRef CWD, const std::vector<std::string> &CommandLine);
178+
179+
/// @brief Computes the dependeny for the module named ModuleName.
180+
/// @param ModuleName The name of the module for which this method computes
181+
///. dependencies.
182+
/// @param AlreadySeen This stores modules which have previously been
183+
/// reported. Use the same instance for all calls to this
184+
/// function for a single \c DependencyScanningTool in a
185+
/// single build. Note that this parameter is not part of
186+
/// the context because it can be shared across different
187+
/// worker threads and each worker thread may update it.
188+
/// @param LookupModuleOutput This function is called to fill in
189+
/// "-fmodule-file=", "-o" and other output
190+
/// arguments for dependencies.
191+
/// @return An instance of \c TranslationUnitDeps if the scan is successful.
192+
/// Otherwise it returns an error.
193+
llvm::Expected<TranslationUnitDeps> computeDependenciesByNameWithContext(
194+
StringRef ModuleName, const llvm::DenseSet<ModuleID> &AlreadySeen,
195+
LookupModuleOutputCallback LookupModuleOutput);
196+
197+
/// @brief This method finializes the compiler instance. It finalizes the
198+
/// diagnostics and deletes the compiler instance. Call this method
199+
/// once all names for a same commandline are scanned.
200+
/// @return Error if an error occured during finalization.
201+
llvm::Error finalizeCompilerInstanceWithContext();
202+
162203
llvm::vfs::FileSystem &getWorkerVFS() const { return Worker.getVFS(); }
163204

164205
private:

clang/include/clang/Tooling/DependencyScanning/DependencyScanningWorker.h

Lines changed: 47 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ namespace tooling {
2929
namespace dependencies {
3030

3131
class DependencyScanningWorkerFilesystem;
32+
class CompilerInstanceWithContext;
3233

3334
/// A command-line tool invocation that is part of building a TU.
3435
///
@@ -89,6 +90,8 @@ class DependencyScanningWorker {
8990
DependencyScanningWorker(DependencyScanningService &Service,
9091
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS);
9192

93+
~DependencyScanningWorker();
94+
9295
/// Run the dependency scanning tool for a given clang driver command-line,
9396
/// and report the discovered dependencies to the provided consumer. If
9497
/// TUBuffer is not nullopt, it is used as TU input for the dependency
@@ -103,18 +106,6 @@ class DependencyScanningWorker {
103106
DiagnosticConsumer &DiagConsumer,
104107
std::optional<llvm::MemoryBufferRef> TUBuffer = std::nullopt);
105108

106-
/// Run the dependency scanning tool for a given clang driver command-line
107-
/// for a specific module.
108-
///
109-
/// \returns false if clang errors occurred (with diagnostics reported to
110-
/// \c DiagConsumer), true otherwise.
111-
bool computeDependencies(StringRef WorkingDirectory,
112-
const std::vector<std::string> &CommandLine,
113-
DependencyConsumer &DepConsumer,
114-
DependencyActionController &Controller,
115-
DiagnosticConsumer &DiagConsumer,
116-
StringRef ModuleName);
117-
118109
/// Run the dependency scanning tool for a given clang driver command-line
119110
/// for a specific translation unit via file system or memory buffer.
120111
///
@@ -125,16 +116,46 @@ class DependencyScanningWorker {
125116
DependencyConsumer &Consumer, DependencyActionController &Controller,
126117
std::optional<llvm::MemoryBufferRef> TUBuffer = std::nullopt);
127118

128-
/// Run the dependency scanning tool for a given clang driver command-line
129-
/// for a specific module.
130-
///
131-
/// \returns A \c StringError with the diagnostic output if clang errors
132-
/// occurred, success otherwise.
133-
llvm::Error computeDependencies(StringRef WorkingDirectory,
134-
const std::vector<std::string> &CommandLine,
135-
DependencyConsumer &Consumer,
136-
DependencyActionController &Controller,
137-
StringRef ModuleName);
119+
/// The three method below implements a new interface for by name
120+
/// dependency scanning. They together enable the dependency scanning worker
121+
/// to more effectively perform scanning for a sequence of modules
122+
/// by name when the CWD and CommandLine do not change across the queries.
123+
124+
/// @brief Initializing the context and the compiler instance.
125+
/// @param CWD The current working directory used during the scan.
126+
/// @param CommandLine The commandline used for the scan.
127+
/// @return Error if the initializaiton fails.
128+
llvm::Error initializeCompilerInstanceWithContextOrError(
129+
StringRef CWD, const std::vector<std::string> &CommandLine);
130+
131+
/// @brief Performaces dependency scanning for the module whose name is
132+
/// specified.
133+
/// @param ModuleName The name of the module whose dependency will be
134+
/// scanned.
135+
/// @param Consumer The dependency consumer that stores the results.
136+
/// @param Controller The controller for the dependency scanning action.
137+
/// @return Error if the scanner incurs errors.
138+
llvm::Error computeDependenciesByNameWithContextOrError(
139+
StringRef ModuleName, DependencyConsumer &Consumer,
140+
DependencyActionController &Controller);
141+
142+
/// @brief Finalizes the diagnostics engine and deletes the compiler instance.
143+
/// @return Error if errors occur during finalization.
144+
llvm::Error finalizeCompilerInstanceWithContextOrError();
145+
146+
/// The three methods below provides the same functionality as the
147+
/// three methods above. Instead of returning `llvm::Error`s, these
148+
/// three methods return a flag to indicate if the call is successful.
149+
/// The initialization function asks the client for a DiagnosticsConsumer
150+
/// that it direct the diagnostics to.
151+
bool initializeCompilerInstanceWithContext(
152+
StringRef CWD, const std::vector<std::string> &CommandLine,
153+
DiagnosticConsumer *DC = nullptr);
154+
bool
155+
computeDependenciesByNameWithContext(StringRef ModuleName,
156+
DependencyConsumer &Consumer,
157+
DependencyActionController &Controller);
158+
bool finalizeCompilerInstance();
138159

139160
llvm::vfs::FileSystem &getVFS() const { return *BaseFS; }
140161

@@ -151,14 +172,16 @@ class DependencyScanningWorker {
151172
/// (passed in the constructor).
152173
llvm::IntrusiveRefCntPtr<DependencyScanningWorkerFilesystem> DepFS;
153174

175+
friend CompilerInstanceWithContext;
176+
std::unique_ptr<CompilerInstanceWithContext> CIWithContext;
177+
154178
/// Private helper functions.
155179
bool scanDependencies(StringRef WorkingDirectory,
156180
const std::vector<std::string> &CommandLine,
157181
DependencyConsumer &Consumer,
158182
DependencyActionController &Controller,
159183
DiagnosticConsumer &DC,
160-
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS,
161-
std::optional<StringRef> ModuleName);
184+
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS);
162185
};
163186

164187
} // end namespace dependencies

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,8 @@ class ModuleDepCollector final : public DependencyCollector {
288288
void attachToPreprocessor(Preprocessor &PP) override;
289289
void attachToASTReader(ASTReader &R) override;
290290

291+
PPCallbacks *getPPCallbacks() { return CollectorPPPtr; }
292+
291293
/// Apply any changes implied by the discovered dependencies to the given
292294
/// invocation, (e.g. disable implicit modules, add explicit module paths).
293295
void applyDiscoveredDependencies(CompilerInvocation &CI);
@@ -339,6 +341,11 @@ class ModuleDepCollector final : public DependencyCollector {
339341
std::optional<P1689ModuleInfo> ProvidedStdCXXModule;
340342
std::vector<P1689ModuleInfo> RequiredStdCXXModules;
341343

344+
/// A pointer to the preprocessor callback so we can invoke it directly
345+
/// if needed. The callback is created and added to a Preprocessor instance by
346+
/// attachToPreprocessor and the Preprocessor instance owns it.
347+
ModuleDepCollectorPP *CollectorPPPtr = nullptr;
348+
342349
/// Checks whether the module is known as being prebuilt.
343350
bool isPrebuiltModule(const Module *M);
344351

clang/lib/Frontend/FrontendActions.cpp

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1233,20 +1233,6 @@ void PrintDependencyDirectivesSourceMinimizerAction::ExecuteAction() {
12331233
llvm::outs());
12341234
}
12351235

1236-
void GetDependenciesByModuleNameAction::ExecuteAction() {
1237-
CompilerInstance &CI = getCompilerInstance();
1238-
Preprocessor &PP = CI.getPreprocessor();
1239-
SourceManager &SM = PP.getSourceManager();
1240-
FileID MainFileID = SM.getMainFileID();
1241-
SourceLocation FileStart = SM.getLocForStartOfFile(MainFileID);
1242-
SmallVector<IdentifierLoc, 2> Path;
1243-
IdentifierInfo *ModuleID = PP.getIdentifierInfo(ModuleName);
1244-
Path.emplace_back(FileStart, ModuleID);
1245-
auto ModResult = CI.loadModule(FileStart, Path, Module::Hidden, false);
1246-
PPCallbacks *CB = PP.getPPCallbacks();
1247-
CB->moduleImport(SourceLocation(), Path, ModResult);
1248-
}
1249-
12501236
//===----------------------------------------------------------------------===//
12511237
// HLSL Specific Actions
12521238
//===----------------------------------------------------------------------===//

0 commit comments

Comments
 (0)