Skip to content

Commit 2dd49f5

Browse files
Merge pull request #80474 from cachemeifyoucan/eng/PR-148465899
[Caching][Macro] Make macro plugin options cacheable
2 parents 1445b81 + 3d38d0d commit 2dd49f5

12 files changed

+174
-11
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1265,6 +1265,10 @@ REMARK(macro_loaded,none,
12651265
"compiler plugin server '%2' with shared library '%3'}1",
12661266
(Identifier, unsigned, StringRef, StringRef))
12671267

1268+
ERROR(resolved_macro_changed,none,
1269+
"resolved macro library '%0' failed verification: %1",
1270+
(StringRef, StringRef))
1271+
12681272
REMARK(transitive_dependency_behavior,none,
12691273
"%1 has %select{a required|an optional|an ignored}2 "
12701274
"transitive dependency on '%0'",

include/swift/AST/PluginLoader.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,17 @@ class PluginLoader {
5252
/// Get or lazily create and populate 'PluginMap'.
5353
llvm::DenseMap<swift::Identifier, PluginEntry> &getPluginMap();
5454

55+
/// Resolved plugin path remappings.
56+
std::vector<std::string> PathRemap;
57+
5558
public:
5659
PluginLoader(ASTContext &Ctx, DependencyTracker *DepTracker,
60+
std::optional<std::vector<std::string>> Remap = std::nullopt,
5761
bool disableSandbox = false)
58-
: Ctx(Ctx), DepTracker(DepTracker), disableSandbox(disableSandbox) {}
62+
: Ctx(Ctx), DepTracker(DepTracker), disableSandbox(disableSandbox) {
63+
if (Remap)
64+
PathRemap = std::move(*Remap);
65+
}
5966

6067
void setRegistry(PluginRegistry *newValue);
6168
PluginRegistry *getRegistry();

include/swift/AST/SearchPathOptions.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -510,6 +510,9 @@ class SearchPathOptions {
510510
/// Scanner Prefix Mapper.
511511
std::vector<std::string> ScannerPrefixMapper;
512512

513+
/// Verify resolved plugin is not changed.
514+
bool ResolvedPluginVerification = false;
515+
513516
/// When set, don't validate module system dependencies.
514517
///
515518
/// If a system header is modified and this is not set, the compiler will

include/swift/Option/Options.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2263,6 +2263,10 @@ def load_resolved_plugin:
22632263
"and exectuable path can be empty if not used">,
22642264
MetaVarName<"<library-path>#<executable-path>#<module-names>">;
22652265

2266+
def resolved_plugin_verification : Flag<["-"], "resolved-plugin-verification">,
2267+
Flags<[FrontendOption, NoDriverOption]>,
2268+
HelpText<"verify resolved plugins">;
2269+
22662270
def in_process_plugin_server_path : Separate<["-"], "in-process-plugin-server-path">,
22672271
Flags<[FrontendOption, ArgumentIsPath]>,
22682272
HelpText<"Path to dynamic library plugin server">;

lib/AST/PluginLoader.cpp

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,12 @@
1313
#include "swift/AST/PluginLoader.h"
1414
#include "swift/AST/ASTContext.h"
1515
#include "swift/AST/DiagnosticEngine.h"
16-
#include "swift/AST/DiagnosticsFrontend.h"
16+
#include "swift/AST/DiagnosticsSema.h"
1717
#include "swift/Basic/Assertions.h"
1818
#include "swift/Basic/SourceManager.h"
1919
#include "swift/Parse/Lexer.h"
2020
#include "llvm/Config/config.h"
21+
#include "llvm/Support/PrefixMapper.h"
2122
#include "llvm/Support/VirtualFileSystem.h"
2223

2324
using namespace swift;
@@ -96,9 +97,45 @@ PluginLoader::getPluginMap() {
9697
map[moduleNameIdentifier] = {libPath, execPath};
9798
};
9899

100+
std::optional<llvm::PrefixMapper> mapper;
101+
if (!PathRemap.empty()) {
102+
SmallVector<llvm::MappedPrefix, 4> prefixes;
103+
llvm::MappedPrefix::transformJoinedIfValid(PathRemap, prefixes);
104+
mapper.emplace();
105+
mapper->addRange(prefixes);
106+
mapper->sort();
107+
}
108+
auto remapPath = [&mapper](StringRef path) {
109+
if (!mapper)
110+
return path.str();
111+
return mapper->mapToString(path);
112+
};
113+
99114
auto fs = getPluginLoadingFS(Ctx);
100115
std::error_code ec;
101116

117+
auto validateLibrary = [&](StringRef path) -> llvm::Expected<std::string> {
118+
auto remappedPath = remapPath(path);
119+
if (!Ctx.SearchPathOpts.ResolvedPluginVerification || path.empty())
120+
return remappedPath;
121+
122+
auto currentStat = fs->status(remappedPath);
123+
if (!currentStat)
124+
return llvm::createFileError(remappedPath, currentStat.getError());
125+
126+
auto goldStat = Ctx.SourceMgr.getFileSystem()->status(path);
127+
if (!goldStat)
128+
return llvm::createStringError(
129+
"cannot open gold reference library to compare");
130+
131+
// Compare the size for difference for now.
132+
if (currentStat->getSize() != goldStat->getSize())
133+
return llvm::createStringError(
134+
"plugin has changed since dependency scanning");
135+
136+
return remappedPath;
137+
};
138+
102139
for (auto &entry : Ctx.SearchPathOpts.PluginSearchOpts) {
103140
switch (entry.getKind()) {
104141

@@ -156,9 +193,17 @@ PluginLoader::getPluginMap() {
156193
// Respect resolved plugin config above other search path, and it can
157194
// overwrite plugins found by other options or previous resolved
158195
// configuration.
159-
for (auto &moduleName : val.ModuleNames)
160-
try_emplace(moduleName, val.LibraryPath, val.ExecutablePath,
196+
for (auto &moduleName : val.ModuleNames) {
197+
auto libPath = validateLibrary(val.LibraryPath);
198+
if (!libPath) {
199+
Ctx.Diags.diagnose(SourceLoc(), diag::resolved_macro_changed,
200+
remapPath(val.LibraryPath),
201+
toString(libPath.takeError()));
202+
continue;
203+
}
204+
try_emplace(moduleName, *libPath, remapPath(val.ExecutablePath),
161205
/*overwrite*/ true);
206+
}
162207
continue;
163208
}
164209
}

lib/DependencyScan/ModuleDependencyScanner.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,7 @@ ModuleDependencyScanningWorker::ModuleDependencyScanningWorker(
201201
ScanASTContext.SourceMgr, Diagnostics));
202202
auto loader = std::make_unique<PluginLoader>(
203203
*workerASTContext, /*DepTracker=*/nullptr,
204+
workerCompilerInvocation->getFrontendOptions().CacheReplayPrefixMap,
204205
workerCompilerInvocation->getFrontendOptions().DisableSandbox);
205206
workerASTContext->setPluginLoader(std::move(loader));
206207

lib/DependencyScan/ScanDependencies.cpp

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -214,9 +214,23 @@ class ExplicitModuleDependencyResolver {
214214
depInfo.addMacroDependency(macro.first(), macro.second.LibraryPath,
215215
macro.second.ExecutablePath);
216216

217+
bool needPathRemapping = instance.getInvocation()
218+
.getSearchPathOptions()
219+
.ResolvedPluginVerification &&
220+
cache.getScanService().hasPathMapping();
221+
auto mapPath = [&](StringRef path) {
222+
if (!needPathRemapping)
223+
return path.str();
224+
225+
return cache.getScanService().remapPath(path);
226+
};
227+
if (needPathRemapping)
228+
commandline.push_back("-resolved-plugin-verification");
229+
217230
for (auto &macro : depInfo.getMacroDependencies()) {
218-
std::string arg = macro.second.LibraryPath + "#" +
219-
macro.second.ExecutablePath + "#" + macro.first;
231+
std::string arg = mapPath(macro.second.LibraryPath) + "#" +
232+
mapPath(macro.second.ExecutablePath) + "#" +
233+
macro.first;
220234
commandline.push_back("-load-resolved-plugin");
221235
commandline.push_back(arg);
222236
}
@@ -480,9 +494,10 @@ class ExplicitModuleDependencyResolver {
480494
llvm::for_each(
481495
sourceDep->auxiliaryFiles,
482496
[this](const std::string &file) { tracker->trackFile(file); });
483-
llvm::for_each(sourceDep->macroDependencies, [this](const auto &entry) {
484-
tracker->trackFile(entry.second.LibraryPath);
485-
});
497+
llvm::for_each(dependencyInfoCopy.getMacroDependencies(),
498+
[this](const auto &entry) {
499+
tracker->trackFile(entry.second.LibraryPath);
500+
});
486501
auto root = tracker->createTreeFromDependencies();
487502
if (!root)
488503
return root.takeError();
@@ -496,7 +511,7 @@ class ExplicitModuleDependencyResolver {
496511
llvm::for_each(
497512
textualDep->auxiliaryFiles,
498513
[this](const std::string &file) { tracker->trackFile(file); });
499-
llvm::for_each(textualDep->macroDependencies,
514+
llvm::for_each(dependencyInfoCopy.getMacroDependencies(),
500515
[this](const auto &entry) {
501516
tracker->trackFile(entry.second.LibraryPath);
502517
});

lib/Frontend/CachedDiagnostics.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@
1818

1919
#include "swift/AST/DiagnosticBridge.h"
2020
#include "swift/AST/DiagnosticConsumer.h"
21+
#include "swift/AST/DiagnosticsCommon.h"
2122
#include "swift/AST/DiagnosticsFrontend.h"
23+
#include "swift/AST/DiagnosticsSema.h"
2224
#include "swift/Basic/Assertions.h"
2325
#include "swift/Basic/SourceManager.h"
2426
#include "swift/Frontend/Frontend.h"
@@ -755,6 +757,13 @@ class CachingDiagnosticsProcessor::Implementation
755757
auto &Serializer = getSerializer();
756758
assert(SM.getFileSystem() == Serializer.getSourceMgr().getFileSystem() &&
757759
"Caching for a different file system");
760+
761+
// Bypass the caching.
762+
if (BypassDiagIDs.count(Info.ID)) {
763+
for (auto *Diag : OrigConsumers)
764+
Diag->handleDiagnostic(Serializer.getSourceMgr(), Info);
765+
return;
766+
}
758767
Serializer.handleDiagnostic(SM, Info, [&](const DiagnosticInfo &Info) {
759768
for (auto *Diag : OrigConsumers)
760769
Diag->handleDiagnostic(Serializer.getSourceMgr(), Info);
@@ -809,6 +818,8 @@ class CachingDiagnosticsProcessor::Implementation
809818
// Processor/Serializer alive until then.
810819
std::unique_ptr<DiagnosticSerializer> Serializer;
811820

821+
const llvm::SmallDenseSet<DiagID> BypassDiagIDs = {diag::macro_loaded.ID};
822+
812823
SourceManager &InstanceSourceMgr;
813824
const FrontendInputsAndOutputs &InAndOut;
814825
DiagnosticEngine &Diags;

lib/Frontend/CompilerInvocation.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2417,6 +2417,9 @@ static bool ParseSearchPathArgs(SearchPathOptions &Opts, ArgList &Args,
24172417
Opts.ScannerPrefixMapper.push_back(Opt.str());
24182418
}
24192419

2420+
Opts.ResolvedPluginVerification |=
2421+
Args.hasArg(OPT_resolved_plugin_verification);
2422+
24202423
// rdar://132340493 disable scanner-side validation for non-caching builds
24212424
Opts.ScannerModuleValidation |= Args.hasFlag(OPT_scanner_module_validation,
24222425
OPT_no_scanner_module_validation,

lib/Frontend/Frontend.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -878,6 +878,7 @@ bool CompilerInstance::setUpPluginLoader() {
878878
/// FIXME: If Invocation has 'PluginRegistry', we can set it. But should we?
879879
auto loader = std::make_unique<PluginLoader>(
880880
*Context, getDependencyTracker(),
881+
Invocation.getFrontendOptions().CacheReplayPrefixMap,
881882
Invocation.getFrontendOptions().DisableSandbox);
882883
Context->setPluginLoader(std::move(loader));
883884
return false;

lib/Frontend/ModuleInterfaceLoader.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1932,6 +1932,8 @@ InterfaceSubContextDelegateImpl::InterfaceSubContextDelegateImpl(
19321932
// Load plugin libraries for macro expression as default arguments
19331933
genericSubInvocation.getSearchPathOptions().PluginSearchOpts =
19341934
searchPathOpts.PluginSearchOpts;
1935+
genericSubInvocation.getSearchPathOptions().ResolvedPluginVerification =
1936+
searchPathOpts.ResolvedPluginVerification;
19351937

19361938
// Get module loading behavior options.
19371939
genericSubInvocation.getSearchPathOptions().ScannerModuleValidation = searchPathOpts.ScannerModuleValidation;

test/CAS/macro_plugin_external.swift

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,78 @@
4343
// RUN: %target-swift-frontend \
4444
// RUN: -typecheck -verify -cache-compile-job -cas-path %t/cas \
4545
// RUN: -swift-version 5 -disable-implicit-swift-modules \
46-
// RUN: -external-plugin-path %t/plugins/#%swift-plugin-server \
4746
// RUN: -disable-implicit-string-processing-module-import -disable-implicit-concurrency-module-import \
4847
// RUN: -module-name MyApp -explicit-swift-module-map-file @%t/map.casid \
4948
// RUN: %t/macro.swift @%t/MyApp.cmd
5049

50+
// RUN: %target-swift-frontend -scan-dependencies -module-load-mode prefer-serialized -module-name MyApp -module-cache-path %t/clang-module-cache -O \
51+
// RUN: -disable-implicit-string-processing-module-import -disable-implicit-concurrency-module-import \
52+
// RUN: %t/macro.swift -o %t/deps2.json -swift-version 5 -cache-compile-job -cas-path %t/cas -external-plugin-path %t/plugins#%swift-plugin-server \
53+
// RUN: -scanner-prefix-map %t=/^test -scanner-prefix-map %swift-bin-dir=/^bin -resolved-plugin-verification
54+
55+
// RUN: %S/Inputs/SwiftDepsExtractor.py %t/deps2.json MyApp casFSRootID > %t/fs.casid
56+
// RUN: %cache-tool -cas-path %t/cas -cache-tool-action print-include-tree-list @%t/fs.casid | %FileCheck %s --check-prefix=FS-REMAP -DLIB=%target-library-name(MacroDefinition)
57+
58+
/// CASFS is remapped.
59+
// FS-REMAP: /^test/plugins/[[LIB]]
60+
61+
// RUN: %S/Inputs/BuildCommandExtractor.py %t/deps2.json clang:SwiftShims > %t/SwiftShims2.cmd
62+
// RUN: %swift_frontend_plain @%t/SwiftShims2.cmd
63+
64+
// RUN: %S/Inputs/BuildCommandExtractor.py %t/deps2.json MyApp > %t/MyApp2.cmd
65+
// RUN: %{python} %S/Inputs/GenerateExplicitModuleMap.py %t/deps2.json > %t/map2.json
66+
// RUN: llvm-cas --cas %t/cas --make-blob --data %t/map2.json > %t/map2.casid
67+
68+
/// Command-line is remapped.
69+
// RUN: %FileCheck %s --check-prefix=CMD-REMAP --input-file=%t/MyApp2.cmd -DLIB=%target-library-name(MacroDefinition)
70+
71+
// CMD-REMAP: -resolved-plugin-verification
72+
// CMD-REMAP-NEXT: -load-resolved-plugin
73+
// CMD-REMAP-NEXT: /^test/plugins/[[LIB]]#/^bin/swift-plugin-server#MacroDefinition
74+
75+
// RUN: %target-swift-frontend \
76+
// RUN: -emit-module -o %t/Macro.swiftmodule -cache-compile-job -cas-path %t/cas \
77+
// RUN: -swift-version 5 -disable-implicit-swift-modules -O \
78+
// RUN: -disable-implicit-string-processing-module-import -disable-implicit-concurrency-module-import \
79+
// RUN: -module-name MyApp -explicit-swift-module-map-file @%t/map2.casid -Rmacro-loading -Rcache-compile-job \
80+
// RUN: /^test/macro.swift @%t/MyApp2.cmd -cache-replay-prefix-map /^test=%t -cache-replay-prefix-map /^bin=%swift-bin-dir 2>&1 | %FileCheck %s --check-prefix=REMARK
81+
// REMAKR: remark: cache miss
82+
// REMARK: remark: loaded macro implementation module 'MacroDefinition' from compiler plugin server
83+
84+
/// Encoded PLUGIN_SEARCH_OPTION is remapped.
85+
// RUN: llvm-bcanalyzer -dump %t/Macro.swiftmodule | %FileCheck %s --check-prefix=MOD -DLIB=%target-library-name(MacroDefinition)
86+
87+
// MOD: <PLUGIN_SEARCH_OPTION abbrevid=7 op0=4/> blob data = '/^test/plugins/[[LIB]]#/^bin/swift-plugin-server#MacroDefinition'
88+
89+
/// Cache hit has no macro-loading remarks because no macro is actually loaded and the path might not be correct due to different mapping.
90+
// RUN: %target-swift-frontend \
91+
// RUN: -emit-module -o %t/Macro.swiftmodule -cache-compile-job -cas-path %t/cas \
92+
// RUN: -swift-version 5 -disable-implicit-swift-modules -O \
93+
// RUN: -disable-implicit-string-processing-module-import -disable-implicit-concurrency-module-import \
94+
// RUN: -module-name MyApp -explicit-swift-module-map-file @%t/map2.casid -Rmacro-loading -Rcache-compile-job \
95+
// RUN: /^test/macro.swift @%t/MyApp2.cmd -cache-replay-prefix-map /^test=%t -cache-replay-prefix-map /^bin=%swift-bin-dir 2>&1 | %FileCheck %s --check-prefix=NO-REMARK
96+
// NO-REMARK: remark: replay output file
97+
// NO-REMARK-NOT: remark: loaded macro implementation module 'MacroDefinition' from compiler plugin server
98+
99+
/// Update timestamp, the build should still work.
100+
// RUN: touch %t/plugins/%target-library-name(MacroDefinition)
101+
// RUN: %target-swift-frontend \
102+
// RUN: -emit-module -o %t/Macro.swiftmodule -cache-compile-job -cas-path %t/cas \
103+
// RUN: -swift-version 5 -disable-implicit-swift-modules -O \
104+
// RUN: -disable-implicit-string-processing-module-import -disable-implicit-concurrency-module-import \
105+
// RUN: -module-name MyApp -explicit-swift-module-map-file @%t/map2.casid -Rmacro-loading -Rcache-compile-job -cache-disable-replay \
106+
// RUN: /^test/macro.swift @%t/MyApp2.cmd -cache-replay-prefix-map /^test=%t -cache-replay-prefix-map /^bin=%swift-bin-dir
107+
108+
/// Change the dylib content, this will fail the build.
109+
// RUN: echo " " >> %t/plugins/%target-library-name(MacroDefinition)
110+
// RUN: not %target-swift-frontend \
111+
// RUN: -emit-module -o %t/Macro.swiftmodule -cache-compile-job -cas-path %t/cas \
112+
// RUN: -swift-version 5 -disable-implicit-swift-modules -O \
113+
// RUN: -disable-implicit-string-processing-module-import -disable-implicit-concurrency-module-import \
114+
// RUN: -module-name MyApp -explicit-swift-module-map-file @%t/map2.casid -Rmacro-loading -Rcache-compile-job -cache-disable-replay \
115+
// RUN: /^test/macro.swift @%t/MyApp2.cmd -cache-replay-prefix-map /^test=%t -cache-replay-prefix-map /^bin=%swift-bin-dir 2>&1 | %FileCheck %s --check-prefix=FAILED
116+
// FAILED: plugin has changed since dependency scanning
117+
51118
//--- nomacro.swift
52119
func test() {}
53120

0 commit comments

Comments
 (0)