Skip to content

Commit 8654db7

Browse files
[Caching] Create clang importer from cc1 args directly
When caching build is enabled, teach dependency scanner to report command-lines with `-direct-clang-cc1-module-build` so the later compilation can instantiate clang importer with cc1 args directly. This avoids running clang driver code, which might involve file system lookups, which are the file deps that are not captured and might result in different compilation mode. rdar://119275464
1 parent 63c0bf3 commit 8654db7

12 files changed

+253
-66
lines changed

include/swift/ClangImporter/ClangImporter.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -509,6 +509,9 @@ class ClangImporter final : public ClangModuleLoader {
509509

510510
std::string getClangModuleHash() const;
511511

512+
/// Get clang import creation cc1 args for swift explicit module build.
513+
std::vector<std::string> getSwiftExplicitModuleDirectCC1Args() const;
514+
512515
/// If we already imported a given decl successfully, return the corresponding
513516
/// Swift decl as an Optional<Decl *>, but if we previously tried and failed
514517
/// to import said decl then return nullptr.

lib/ClangImporter/ClangImporter.cpp

Lines changed: 113 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,9 @@
5858
#include "clang/Basic/Module.h"
5959
#include "clang/Basic/TargetInfo.h"
6060
#include "clang/Basic/Version.h"
61+
#include "clang/CAS/CASOptions.h"
6162
#include "clang/CodeGen/ObjectFilePCHContainerOperations.h"
63+
#include "clang/Frontend/CompilerInvocation.h"
6264
#include "clang/Frontend/FrontendActions.h"
6365
#include "clang/Frontend/TextDiagnosticPrinter.h"
6466
#include "clang/Frontend/Utils.h"
@@ -75,6 +77,7 @@
7577
#include "clang/Serialization/ASTWriter.h"
7678
#include "llvm/ADT/IntrusiveRefCntPtr.h"
7779
#include "llvm/ADT/STLExtras.h"
80+
#include "llvm/ADT/SmallVector.h"
7881
#include "llvm/ADT/StringExtras.h"
7982
#include "llvm/Support/CrashRecoveryContext.h"
8083
#include "llvm/Support/FileCollector.h"
@@ -1067,26 +1070,7 @@ std::optional<std::vector<std::string>> ClangImporter::getClangCC1Arguments(
10671070
ClangImporter *importer, ASTContext &ctx,
10681071
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS,
10691072
bool ignoreClangTarget) {
1070-
// If using direct cc1 module build, return extra args only.
1071-
if (ctx.ClangImporterOpts.DirectClangCC1ModuleBuild)
1072-
return ctx.ClangImporterOpts.ExtraArgs;
1073-
1074-
// Otherwise, create cc1 arguments from driver args.
1075-
auto driverArgs = getClangDriverArguments(ctx, ignoreClangTarget);
1076-
1077-
llvm::SmallVector<const char *> invocationArgs;
1078-
invocationArgs.reserve(driverArgs.size());
1079-
llvm::for_each(driverArgs, [&](const std::string &Arg) {
1080-
invocationArgs.push_back(Arg.c_str());
1081-
});
1082-
1083-
if (ctx.ClangImporterOpts.DumpClangDiagnostics) {
1084-
llvm::errs() << "clang importer driver args: '";
1085-
llvm::interleave(
1086-
invocationArgs, [](StringRef arg) { llvm::errs() << arg; },
1087-
[] { llvm::errs() << "' '"; });
1088-
llvm::errs() << "'\n";
1089-
}
1073+
std::unique_ptr<clang::CompilerInvocation> CI;
10901074

10911075
// Set up a temporary diagnostic client to report errors from parsing the
10921076
// command line, which may be important for Swift clients if, for example,
@@ -1098,24 +1082,62 @@ std::optional<std::vector<std::string>> ClangImporter::getClangCC1Arguments(
10981082
// clang::CompilerInstance is created.
10991083
llvm::IntrusiveRefCntPtr<clang::DiagnosticOptions> tempDiagOpts{
11001084
new clang::DiagnosticOptions};
1101-
11021085
auto *tempDiagClient =
11031086
new ClangDiagnosticConsumer(importer->Impl, *tempDiagOpts,
11041087
ctx.ClangImporterOpts.DumpClangDiagnostics);
1105-
11061088
auto clangDiags = clang::CompilerInstance::createDiagnostics(
11071089
tempDiagOpts.get(), tempDiagClient,
11081090
/*owned*/ true);
11091091

1110-
clang::CreateInvocationOptions CIOpts;
1111-
CIOpts.VFS = VFS;
1112-
CIOpts.Diags = clangDiags;
1113-
CIOpts.RecoverOnError = false;
1114-
CIOpts.ProbePrecompiled = true;
1115-
auto CI = clang::createInvocation(invocationArgs, std::move(CIOpts));
1092+
// If using direct cc1 module build, use extra args to setup ClangImporter.
1093+
if (ctx.ClangImporterOpts.DirectClangCC1ModuleBuild) {
1094+
llvm::SmallVector<const char *> clangArgs;
1095+
clangArgs.reserve(ctx.ClangImporterOpts.ExtraArgs.size());
1096+
llvm::for_each(
1097+
ctx.ClangImporterOpts.ExtraArgs,
1098+
[&](const std::string &Arg) { clangArgs.push_back(Arg.c_str()); });
1099+
1100+
// Try parse extra args, if failed, return nullopt.
1101+
CI = std::make_unique<clang::CompilerInvocation>();
1102+
if (!clang::CompilerInvocation::CreateFromArgs(*CI, clangArgs,
1103+
*clangDiags))
1104+
return std::nullopt;
11161105

1117-
if (!CI)
1118-
return std::nullopt;
1106+
// Forwards some options from swift to clang even using direct mode. This is
1107+
// to reduce the number of argument passing on the command-line and swift
1108+
// compiler can be more efficient to compute swift cache key without having
1109+
// the knowledge about clang command-line options.
1110+
if (ctx.CASOpts.EnableCaching) {
1111+
CI->getFrontendOpts().IncludeTimestamps = false;
1112+
CI->getCASOpts() = ctx.CASOpts.CASOpts;
1113+
}
1114+
} else {
1115+
// Otherwise, create cc1 arguments from driver args.
1116+
auto driverArgs = getClangDriverArguments(ctx, ignoreClangTarget);
1117+
1118+
llvm::SmallVector<const char *> invocationArgs;
1119+
invocationArgs.reserve(driverArgs.size());
1120+
llvm::for_each(driverArgs, [&](const std::string &Arg) {
1121+
invocationArgs.push_back(Arg.c_str());
1122+
});
1123+
1124+
if (ctx.ClangImporterOpts.DumpClangDiagnostics) {
1125+
llvm::errs() << "clang importer driver args: '";
1126+
llvm::interleave(
1127+
invocationArgs, [](StringRef arg) { llvm::errs() << arg; },
1128+
[] { llvm::errs() << "' '"; });
1129+
llvm::errs() << "'\n";
1130+
}
1131+
1132+
clang::CreateInvocationOptions CIOpts;
1133+
CIOpts.VFS = VFS;
1134+
CIOpts.Diags = clangDiags;
1135+
CIOpts.RecoverOnError = false;
1136+
CIOpts.ProbePrecompiled = true;
1137+
CI = clang::createInvocation(invocationArgs, std::move(CIOpts));
1138+
if (!CI)
1139+
return std::nullopt;
1140+
}
11191141

11201142
// FIXME: clang fails to generate a module if there is a `-fmodule-map-file`
11211143
// argument pointing to a missing file.
@@ -3919,6 +3941,67 @@ std::string ClangImporter::getClangModuleHash() const {
39193941
return Impl.Invocation->getModuleHash(Impl.Instance->getDiagnostics());
39203942
}
39213943

3944+
std::vector<std::string>
3945+
ClangImporter::getSwiftExplicitModuleDirectCC1Args() const {
3946+
llvm::SmallVector<const char*> clangArgs;
3947+
clangArgs.reserve(Impl.ClangArgs.size());
3948+
llvm::for_each(Impl.ClangArgs, [&](const std::string &Arg) {
3949+
clangArgs.push_back(Arg.c_str());
3950+
});
3951+
3952+
clang::CompilerInvocation instance;
3953+
clang::DiagnosticsEngine clangDiags(new clang::DiagnosticIDs(),
3954+
new clang::DiagnosticOptions(),
3955+
new clang::IgnoringDiagConsumer());
3956+
bool success = clang::CompilerInvocation::CreateFromArgs(instance, clangArgs,
3957+
clangDiags);
3958+
(void)success;
3959+
assert(success && "clang options from clangImporter failed to parse");
3960+
3961+
if (!Impl.SwiftContext.CASOpts.EnableCaching)
3962+
return instance.getCC1CommandLine();
3963+
3964+
// Clear some options that are not needed.
3965+
// CASOpts are forwarded from swift arguments.
3966+
instance.getCASOpts() = clang::CASOptions();
3967+
3968+
// HeaderSearchOptions.
3969+
// Clang search options are only used by scanner and clang importer from main
3970+
// module should not using search paths to find modules.
3971+
auto &HSOpts = instance.getHeaderSearchOpts();
3972+
HSOpts.BuildSessionTimestamp = 0;
3973+
HSOpts.ImplicitModuleMaps = 0;
3974+
HSOpts.VFSOverlayFiles.clear();
3975+
HSOpts.UserEntries.clear();
3976+
HSOpts.SystemHeaderPrefixes.clear();
3977+
3978+
// LanguageOptions.
3979+
auto &LangOpts = instance.getLangOpts();
3980+
LangOpts.ImplicitModules = false;
3981+
3982+
// FrontendOptions.
3983+
auto &FEOpts = instance.getFrontendOpts();
3984+
FEOpts.IncludeTimestamps = false;
3985+
FEOpts.ModuleMapFiles.clear();
3986+
3987+
// PreprocessorOptions.
3988+
// Cannot clear macros as the main module clang importer doesn't have clang
3989+
// include tree created and it has to be created from command-line. However,
3990+
// include files are no collected into CASFS so they will not be found so
3991+
// clear them to avoid problem.
3992+
auto &PPOpts = instance.getPreprocessorOpts();
3993+
PPOpts.MacroIncludes.clear();
3994+
PPOpts.Includes.clear();
3995+
3996+
if (Impl.SwiftContext.ClangImporterOpts.UseClangIncludeTree) {
3997+
// FileSystemOptions.
3998+
auto &FSOpts = instance.getFileSystemOpts();
3999+
FSOpts.WorkingDir.clear();
4000+
}
4001+
4002+
return instance.getCC1CommandLine();
4003+
}
4004+
39224005
std::optional<Decl *>
39234006
ClangImporter::importDeclCached(const clang::NamedDecl *ClangDecl) {
39244007
return Impl.importDeclCached(ClangDecl, Impl.CurrentVersion);

lib/DependencyScan/ModuleDependencyScanner.cpp

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,7 @@ ModuleDependencyScanner::getMainModuleDependencyInfo(
346346
{"-Xcc", "-target", "-Xcc", ScanASTContext.LangOpts.Target.str()});
347347

348348
std::string rootID;
349+
std::vector<std::string> buildArgs;
349350
if (tracker) {
350351
tracker->startTracking();
351352
for (auto fileUnit : mainModule->getFiles()) {
@@ -371,10 +372,22 @@ ModuleDependencyScanner::getMainModuleDependencyInfo(
371372
return std::make_error_code(std::errc::io_error);
372373
}
373374
rootID = root->getID().toString();
375+
376+
buildArgs.push_back("-direct-clang-cc1-module-build");
377+
for (auto &arg : clangImporter->getSwiftExplicitModuleDirectCC1Args()) {
378+
buildArgs.push_back("-Xcc");
379+
buildArgs.push_back(arg);
380+
}
374381
}
375382

376-
auto mainDependencies =
377-
ModuleDependencyInfo::forSwiftSourceModule(rootID, {}, {}, ExtraPCMArgs);
383+
llvm::SmallVector<StringRef> buildCommands;
384+
buildCommands.reserve(buildArgs.size());
385+
llvm::for_each(buildArgs, [&](const std::string &arg) {
386+
buildCommands.emplace_back(arg);
387+
});
388+
389+
auto mainDependencies = ModuleDependencyInfo::forSwiftSourceModule(
390+
rootID, buildCommands, {}, ExtraPCMArgs);
378391

379392
llvm::StringSet<> alreadyAddedModules;
380393
// Compute Implicit dependencies of the main module

lib/DependencyScan/ScanDependencies.cpp

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -295,19 +295,11 @@ static llvm::Error resolveExplicitModuleInputs(
295295
}
296296
}
297297
if (!clangDepDetails->moduleCacheKey.empty()) {
298-
auto appendXclang = [&]() {
299-
if (!resolvingDepInfo.isClangModule()) {
300-
// clang module build using cc1 arg so this is not needed.
301-
commandLine.push_back("-Xcc");
302-
commandLine.push_back("-Xclang");
303-
}
304-
commandLine.push_back("-Xcc");
305-
};
306-
appendXclang();
298+
commandLine.push_back("-Xcc");
307299
commandLine.push_back("-fmodule-file-cache-key");
308-
appendXclang();
300+
commandLine.push_back("-Xcc");
309301
commandLine.push_back(remapPath(clangDepDetails->pcmOutputPath));
310-
appendXclang();
302+
commandLine.push_back("-Xcc");
311303
commandLine.push_back(clangDepDetails->moduleCacheKey);
312304
}
313305

lib/Frontend/ModuleInterfaceLoader.cpp

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1688,6 +1688,8 @@ void InterfaceSubContextDelegateImpl::inheritOptionsForBuildingInterface(
16881688
genericSubInvocation.getCASOptions().CASOpts = casOpts.CASOpts;
16891689
casOpts.enumerateCASConfigurationFlags(
16901690
[&](StringRef Arg) { GenericArgs.push_back(ArgSaver.save(Arg)); });
1691+
// ClangIncludeTree is default on when caching is enabled.
1692+
genericSubInvocation.getClangImporterOptions().UseClangIncludeTree = true;
16911693
}
16921694

16931695
if (!clangImporterOpts.UseClangIncludeTree) {
@@ -2065,10 +2067,16 @@ InterfaceSubContextDelegateImpl::runInSubCompilerInstance(StringRef moduleName,
20652067
== originalTargetTriple.getEnvironment()) {
20662068
parsedTargetTriple.setArchName(originalTargetTriple.getArchName());
20672069
subInvocation.setTargetTriple(parsedTargetTriple.str());
2070+
}
20682071

2069-
// Overload the target in the BuildArgs as well
2070-
BuildArgs.push_back("-target");
2071-
BuildArgs.push_back(parsedTargetTriple.str());
2072+
// Find and overload all "-target" to be parsedTargetTriple. This make sure
2073+
// the build command for the interface is the same no matter what the parent
2074+
// triple is so there is no need to spawn identical jobs.
2075+
assert(llvm::find(BuildArgs, "-target") != BuildArgs.end() &&
2076+
"missing target option");
2077+
for (unsigned idx = 0, end = BuildArgs.size(); idx < end; ++idx) {
2078+
if (BuildArgs[idx] == "-target" && ++idx < end)
2079+
BuildArgs[idx] = parsedTargetTriple.str();
20722080
}
20732081

20742082
// restore `StrictImplicitModuleContext`
@@ -2419,6 +2427,11 @@ struct ExplicitCASModuleLoader::Implementation {
24192427

24202428
std::set<std::string> moduleMapsSeen;
24212429
std::vector<std::string> &extraClangArgs = Ctx.ClangImporterOpts.ExtraArgs;
2430+
// Append -Xclang if we are not in direct cc1 mode.
2431+
auto appendXclang = [&]() {
2432+
if (!Ctx.ClangImporterOpts.DirectClangCC1ModuleBuild)
2433+
extraClangArgs.push_back("-Xclang");
2434+
};
24222435
for (auto &entry : ExplicitClangModuleMap) {
24232436
const auto &moduleMapPath = entry.getValue().moduleMapPath;
24242437
if (!moduleMapPath.empty() &&
@@ -2437,11 +2450,11 @@ struct ExplicitCASModuleLoader::Implementation {
24372450
}
24382451
auto cachePath = entry.getValue().moduleCacheKey;
24392452
if (cachePath) {
2440-
extraClangArgs.push_back("-Xclang");
2453+
appendXclang();
24412454
extraClangArgs.push_back("-fmodule-file-cache-key");
2442-
extraClangArgs.push_back("-Xclang");
2455+
appendXclang();
24432456
extraClangArgs.push_back(modulePath);
2444-
extraClangArgs.push_back("-Xclang");
2457+
appendXclang();
24452458
extraClangArgs.push_back(*cachePath);
24462459
}
24472460
}

lib/Serialization/ScanningLoaders.cpp

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -171,10 +171,23 @@ SwiftModuleScanner::scanInterfaceFile(Twine moduleInterfacePath,
171171
// Add explicit Swift dependency compilation flags
172172
Args.push_back("-explicit-interface-module-build");
173173
Args.push_back("-disable-implicit-swift-modules");
174-
Args.push_back("-Xcc");
175-
Args.push_back("-fno-implicit-modules");
176-
Args.push_back("-Xcc");
177-
Args.push_back("-fno-implicit-module-maps");
174+
175+
// Handle clang arguments. For caching build, all arguments are passed
176+
// with `-direct-clang-cc1-module-build`.
177+
if (Ctx.CASOpts.EnableCaching) {
178+
Args.push_back("-direct-clang-cc1-module-build");
179+
auto *importer =
180+
static_cast<ClangImporter *>(Ctx.getClangModuleLoader());
181+
for (auto &Arg : importer->getSwiftExplicitModuleDirectCC1Args()) {
182+
Args.push_back("-Xcc");
183+
Args.push_back(Arg);
184+
}
185+
} else {
186+
Args.push_back("-Xcc");
187+
Args.push_back("-fno-implicit-modules");
188+
Args.push_back("-Xcc");
189+
Args.push_back("-fno-implicit-module-maps");
190+
}
178191
for (const auto &candidate : compiledCandidates) {
179192
Args.push_back("-candidate-module-file");
180193
Args.push_back(candidate);

0 commit comments

Comments
 (0)