Skip to content

Commit cdeef58

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 66801fd commit cdeef58

File tree

13 files changed

+290
-103
lines changed

13 files changed

+290
-103
lines changed

include/swift/ClangImporter/ClangImporter.h

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

515515
std::string getClangModuleHash() const;
516516

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

lib/ClangImporter/ClangImporter.cpp

Lines changed: 108 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"
@@ -73,8 +75,10 @@
7375
#include "clang/Sema/Sema.h"
7476
#include "clang/Serialization/ASTReader.h"
7577
#include "clang/Serialization/ASTWriter.h"
78+
#include "clang/Tooling/DependencyScanning/ScanAndUpdateArgs.h"
7679
#include "llvm/ADT/IntrusiveRefCntPtr.h"
7780
#include "llvm/ADT/STLExtras.h"
81+
#include "llvm/ADT/SmallVector.h"
7882
#include "llvm/ADT/StringExtras.h"
7983
#include "llvm/Support/CrashRecoveryContext.h"
8084
#include "llvm/Support/FileCollector.h"
@@ -1067,26 +1071,7 @@ std::optional<std::vector<std::string>> ClangImporter::getClangCC1Arguments(
10671071
ClangImporter *importer, ASTContext &ctx,
10681072
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS,
10691073
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-
}
1074+
std::unique_ptr<clang::CompilerInvocation> CI;
10901075

10911076
// Set up a temporary diagnostic client to report errors from parsing the
10921077
// command line, which may be important for Swift clients if, for example,
@@ -1098,24 +1083,60 @@ std::optional<std::vector<std::string>> ClangImporter::getClangCC1Arguments(
10981083
// clang::CompilerInstance is created.
10991084
llvm::IntrusiveRefCntPtr<clang::DiagnosticOptions> tempDiagOpts{
11001085
new clang::DiagnosticOptions};
1101-
11021086
auto *tempDiagClient =
11031087
new ClangDiagnosticConsumer(importer->Impl, *tempDiagOpts,
11041088
ctx.ClangImporterOpts.DumpClangDiagnostics);
1105-
11061089
auto clangDiags = clang::CompilerInstance::createDiagnostics(
11071090
tempDiagOpts.get(), tempDiagClient,
11081091
/*owned*/ true);
11091092

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));
1093+
// If using direct cc1 module build, use extra args to setup ClangImporter.
1094+
if (ctx.ClangImporterOpts.DirectClangCC1ModuleBuild) {
1095+
llvm::SmallVector<const char *> clangArgs;
1096+
clangArgs.reserve(ctx.ClangImporterOpts.ExtraArgs.size());
1097+
llvm::for_each(
1098+
ctx.ClangImporterOpts.ExtraArgs,
1099+
[&](const std::string &Arg) { clangArgs.push_back(Arg.c_str()); });
1100+
1101+
// Try parse extra args, if failed, return nullopt.
1102+
CI = std::make_unique<clang::CompilerInvocation>();
1103+
if (!clang::CompilerInvocation::CreateFromArgs(*CI, clangArgs,
1104+
*clangDiags))
1105+
return std::nullopt;
11161106

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

11201141
// FIXME: clang fails to generate a module if there is a `-fmodule-map-file`
11211142
// argument pointing to a missing file.
@@ -3919,6 +3940,63 @@ std::string ClangImporter::getClangModuleHash() const {
39193940
return Impl.Invocation->getModuleHash(Impl.Instance->getDiagnostics());
39203941
}
39213942

3943+
std::vector<std::string>
3944+
ClangImporter::getSwiftExplicitModuleDirectCC1Args() const {
3945+
llvm::SmallVector<const char*> clangArgs;
3946+
clangArgs.reserve(Impl.ClangArgs.size());
3947+
llvm::for_each(Impl.ClangArgs, [&](const std::string &Arg) {
3948+
clangArgs.push_back(Arg.c_str());
3949+
});
3950+
3951+
clang::CompilerInvocation instance;
3952+
clang::DiagnosticsEngine clangDiags(new clang::DiagnosticIDs(),
3953+
new clang::DiagnosticOptions(),
3954+
new clang::IgnoringDiagConsumer());
3955+
bool success = clang::CompilerInvocation::CreateFromArgs(instance, clangArgs,
3956+
clangDiags);
3957+
(void)success;
3958+
assert(success && "clang options from clangImporter failed to parse");
3959+
3960+
if (!Impl.SwiftContext.CASOpts.EnableCaching)
3961+
return instance.getCC1CommandLine();
3962+
3963+
// Clear some options that are not needed.
3964+
instance.clearImplicitModuleBuildOptions();
3965+
3966+
// CASOpts are forwarded from swift arguments.
3967+
instance.getCASOpts() = clang::CASOptions();
3968+
3969+
// HeaderSearchOptions.
3970+
// Clang search options are only used by scanner and clang importer from main
3971+
// module should not using search paths to find modules.
3972+
auto &HSOpts = instance.getHeaderSearchOpts();
3973+
HSOpts.VFSOverlayFiles.clear();
3974+
HSOpts.UserEntries.clear();
3975+
HSOpts.SystemHeaderPrefixes.clear();
3976+
3977+
// FrontendOptions.
3978+
auto &FEOpts = instance.getFrontendOpts();
3979+
FEOpts.IncludeTimestamps = false;
3980+
FEOpts.ModuleMapFiles.clear();
3981+
3982+
// PreprocessorOptions.
3983+
// Cannot clear macros as the main module clang importer doesn't have clang
3984+
// include tree created and it has to be created from command-line. However,
3985+
// include files are no collected into CASFS so they will not be found so
3986+
// clear them to avoid problem.
3987+
auto &PPOpts = instance.getPreprocessorOpts();
3988+
PPOpts.MacroIncludes.clear();
3989+
PPOpts.Includes.clear();
3990+
3991+
if (Impl.SwiftContext.ClangImporterOpts.UseClangIncludeTree) {
3992+
// FileSystemOptions.
3993+
auto &FSOpts = instance.getFileSystemOpts();
3994+
FSOpts.WorkingDir.clear();
3995+
}
3996+
3997+
return instance.getCC1CommandLine();
3998+
}
3999+
39224000
std::optional<Decl *>
39234001
ClangImporter::importDeclCached(const clang::NamedDecl *ClangDecl) {
39244002
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
@@ -290,19 +290,11 @@ static llvm::Error resolveExplicitModuleInputs(
290290
}
291291
}
292292
if (!clangDepDetails->moduleCacheKey.empty()) {
293-
auto appendXclang = [&]() {
294-
if (!resolvingDepInfo.isClangModule()) {
295-
// clang module build using cc1 arg so this is not needed.
296-
commandLine.push_back("-Xcc");
297-
commandLine.push_back("-Xclang");
298-
}
299-
commandLine.push_back("-Xcc");
300-
};
301-
appendXclang();
293+
commandLine.push_back("-Xcc");
302294
commandLine.push_back("-fmodule-file-cache-key");
303-
appendXclang();
295+
commandLine.push_back("-Xcc");
304296
commandLine.push_back(clangDepDetails->mappedPCMPath);
305-
appendXclang();
297+
commandLine.push_back("-Xcc");
306298
commandLine.push_back(clangDepDetails->moduleCacheKey);
307299
}
308300

lib/Frontend/CompilerInvocation.cpp

Lines changed: 39 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1659,43 +1659,6 @@ static bool ParseClangImporterArgs(ClangImporterOptions &Opts, ArgList &Args,
16591659
Opts.ExtraArgs.push_back(A->getValue());
16601660
}
16611661

1662-
for (const Arg *A : Args.filtered(OPT_file_prefix_map,
1663-
OPT_debug_prefix_map)) {
1664-
std::string Val(A->getValue());
1665-
// Forward -debug-prefix-map arguments from Swift to Clang as
1666-
// -fdebug-prefix-map= and -file-prefix-map as -ffile-prefix-map=.
1667-
//
1668-
// This is required to ensure DIFiles created there, like
1669-
/// "<swift-imported-modules>", as well as index data, have their paths
1670-
// remapped properly.
1671-
//
1672-
// (Note, however, that Clang's usage of std::map means that the remapping
1673-
// may not be applied in the same order, which can matter if one mapping is
1674-
// a prefix of another.)
1675-
if (A->getOption().matches(OPT_file_prefix_map))
1676-
Opts.ExtraArgs.push_back("-ffile-prefix-map=" + Val);
1677-
else
1678-
Opts.ExtraArgs.push_back("-fdebug-prefix-map=" + Val);
1679-
}
1680-
1681-
if (auto *A = Args.getLastArg(OPT_file_compilation_dir)) {
1682-
// Forward the -file-compilation-dir flag to correctly set the
1683-
// debug compilation directory.
1684-
std::string Val(A->getValue());
1685-
Opts.ExtraArgs.push_back("-ffile-compilation-dir=" + Val);
1686-
}
1687-
1688-
if (CASOpts.CASFSRootIDs.empty() &&
1689-
CASOpts.ClangIncludeTrees.empty()) {
1690-
if (!workingDirectory.empty()) {
1691-
// Provide a working directory to Clang as well if there are any -Xcc
1692-
// options, in case some of them are search-related. But do it at the
1693-
// beginning, so that an explicit -Xcc -working-directory will win.
1694-
Opts.ExtraArgs.insert(Opts.ExtraArgs.begin(),
1695-
{"-working-directory", workingDirectory.str()});
1696-
}
1697-
}
1698-
16991662
Opts.DumpClangDiagnostics |= Args.hasArg(OPT_dump_clang_diagnostics);
17001663

17011664
// When the repl is invoked directly (ie. `lldb --repl="..."`) the action
@@ -1755,6 +1718,45 @@ static bool ParseClangImporterArgs(ClangImporterOptions &Opts, ArgList &Args,
17551718
Opts.HasClangIncludeTreeRoot |= Args.hasArg(OPT_clang_include_tree_root);
17561719
}
17571720

1721+
// If in direct clang cc1 module build mode, return early.
1722+
if (Opts.DirectClangCC1ModuleBuild)
1723+
return false;
1724+
1725+
// Only amend the following path option when not in direct cc1 mode.
1726+
for (const Arg *A : Args.filtered(OPT_file_prefix_map,
1727+
OPT_debug_prefix_map)) {
1728+
std::string Val(A->getValue());
1729+
// Forward -debug-prefix-map arguments from Swift to Clang as
1730+
// -fdebug-prefix-map= and -file-prefix-map as -ffile-prefix-map=.
1731+
//
1732+
// This is required to ensure DIFiles created there, like
1733+
/// "<swift-imported-modules>", as well as index data, have their paths
1734+
// remapped properly.
1735+
//
1736+
// (Note, however, that Clang's usage of std::map means that the remapping
1737+
// may not be applied in the same order, which can matter if one mapping is
1738+
// a prefix of another.)
1739+
if (A->getOption().matches(OPT_file_prefix_map))
1740+
Opts.ExtraArgs.push_back("-ffile-prefix-map=" + Val);
1741+
else
1742+
Opts.ExtraArgs.push_back("-fdebug-prefix-map=" + Val);
1743+
}
1744+
1745+
if (auto *A = Args.getLastArg(OPT_file_compilation_dir)) {
1746+
// Forward the -file-compilation-dir flag to correctly set the
1747+
// debug compilation directory.
1748+
std::string Val(A->getValue());
1749+
Opts.ExtraArgs.push_back("-ffile-compilation-dir=" + Val);
1750+
}
1751+
1752+
if (!workingDirectory.empty()) {
1753+
// Provide a working directory to Clang as well if there are any -Xcc
1754+
// options, in case some of them are search-related. But do it at the
1755+
// beginning, so that an explicit -Xcc -working-directory will win.
1756+
Opts.ExtraArgs.insert(Opts.ExtraArgs.begin(),
1757+
{"-working-directory", workingDirectory.str()});
1758+
}
1759+
17581760
return false;
17591761
}
17601762

0 commit comments

Comments
 (0)