Skip to content

Commit 0c1f9c4

Browse files
authored
Merge pull request #32985 from nkcsgexi/forwarding-module-build
ModuleInterface: teach -compile-module-from-interface to emit forwarding module
2 parents d2f9867 + f9396f2 commit 0c1f9c4

File tree

9 files changed

+105
-7
lines changed

9 files changed

+105
-7
lines changed

include/swift/AST/SearchPathOptions.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,9 @@ class SearchPathOptions {
8585
/// The paths to a set of explicitly built modules from interfaces.
8686
std::vector<std::string> ExplicitSwiftModules;
8787

88+
/// A set of compiled modules that may be ready to use.
89+
std::vector<std::string> CandidateCompiledModules;
90+
8891
/// A map of explict Swift module information.
8992
std::string ExplicitSwiftModuleMap;
9093
private:

include/swift/Frontend/ModuleInterfaceLoader.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,14 @@ class ModuleInterfaceLoader : public SerializedModuleLoaderBase {
240240
std::vector<std::string>
241241
getCompiledModuleCandidatesForInterface(StringRef moduleName,
242242
StringRef interfacePath) override;
243+
244+
/// Given a list of potential ready-to-use compiled modules for \p interfacePath,
245+
/// check if any one of them is up-to-date. If so, emit a forwarding module
246+
/// to the candidate binary module to \p outPath.
247+
bool tryEmitForwardingModule(StringRef moduleName,
248+
StringRef interfacePath,
249+
ArrayRef<std::string> candidates,
250+
StringRef outPath) override;
243251
};
244252

245253
struct InterfaceSubContextDelegateImpl: InterfaceSubContextDelegate {

include/swift/Option/FrontendOptions.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -719,4 +719,8 @@ def target_variant_sdk_version : Separate<["-"], "target-variant-sdk-version">,
719719

720720
def extra_clang_options_only : Flag<["-"], "only-use-extra-clang-opts">,
721721
HelpText<"Options passed via -Xcc are sufficient for Clang configuration">;
722+
723+
def candidate_module_file
724+
: Separate<["-"], "candidate-module-file">, MetaVarName<"<path>">,
725+
HelpText<"Specify Swift module may be ready to use for an interface">;
722726
} // end let Flags = [FrontendOption, NoDriverOption, HelpHidden]

include/swift/Serialization/SerializedModuleLoader.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,12 @@ class SerializedModuleLoaderBase : public ModuleLoader {
206206
StringRef interfacePath) {
207207
return std::vector<std::string>();
208208
}
209+
virtual bool tryEmitForwardingModule(StringRef moduleName,
210+
StringRef interfacePath,
211+
ArrayRef<std::string> candidates,
212+
StringRef outPath) {
213+
return false;
214+
}
209215
};
210216

211217
/// Imports serialized Swift modules into an ASTContext.

lib/Frontend/CompilerInvocation.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -900,6 +900,10 @@ static bool ParseSearchPathArgs(SearchPathOptions &Opts,
900900
}
901901
if (const Arg *A = Args.getLastArg(OPT_explict_swift_module_map))
902902
Opts.ExplicitSwiftModuleMap = A->getValue();
903+
for (auto A: Args.filtered(OPT_candidate_module_file)) {
904+
Opts.CandidateCompiledModules.push_back(resolveSearchPath(A->getValue()));
905+
}
906+
903907
// Opts.RuntimeIncludePath is set by calls to
904908
// setRuntimeIncludePath() or setMainExecutablePath().
905909
// Opts.RuntimeImportPath is set by calls to

lib/Frontend/ModuleInterfaceBuilder.cpp

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,8 @@ bool ModuleInterfaceBuilder::collectDepsForSerialization(
147147

148148
bool ModuleInterfaceBuilder::buildSwiftModuleInternal(
149149
StringRef OutPath, bool ShouldSerializeDeps,
150-
std::unique_ptr<llvm::MemoryBuffer> *ModuleBuffer) {
150+
std::unique_ptr<llvm::MemoryBuffer> *ModuleBuffer,
151+
ArrayRef<std::string> CompiledCandidates) {
151152

152153
auto outerPrettyStackState = llvm::SavePrettyStackState();
153154

@@ -167,6 +168,13 @@ bool ModuleInterfaceBuilder::buildSwiftModuleInternal(
167168
[&](SubCompilerInstanceInfo &info) {
168169
auto &SubInstance = *info.Instance;
169170
auto subInvocation = SubInstance.getInvocation();
171+
// Try building forwarding module first. If succeed, return.
172+
if (static_cast<ModuleInterfaceLoader*>(SubInstance.getASTContext()
173+
.getModuleInterfaceLoader())->tryEmitForwardingModule(moduleName,
174+
interfacePath,
175+
CompiledCandidates, OutPath)) {
176+
return false;
177+
}
170178
FrontendOptions &FEOpts = subInvocation.getFrontendOptions();
171179
const auto &InputInfo = FEOpts.InputsAndOutputs.firstInput();
172180
StringRef InPath = InputInfo.file();
@@ -256,12 +264,14 @@ bool ModuleInterfaceBuilder::buildSwiftModuleInternal(
256264
bool ModuleInterfaceBuilder::buildSwiftModule(StringRef OutPath,
257265
bool ShouldSerializeDeps,
258266
std::unique_ptr<llvm::MemoryBuffer> *ModuleBuffer,
259-
llvm::function_ref<void()> RemarkRebuild) {
267+
llvm::function_ref<void()> RemarkRebuild,
268+
ArrayRef<std::string> CompiledCandidates) {
260269
auto build = [&]() {
261270
if (RemarkRebuild) {
262271
RemarkRebuild();
263272
}
264-
return buildSwiftModuleInternal(OutPath, ShouldSerializeDeps, ModuleBuffer);
273+
return buildSwiftModuleInternal(OutPath, ShouldSerializeDeps, ModuleBuffer,
274+
CompiledCandidates);
265275
};
266276
if (disableInterfaceFileLock) {
267277
return build();

lib/Frontend/ModuleInterfaceBuilder.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,8 @@ class ModuleInterfaceBuilder {
8585
bool IsHashBased);
8686

8787
bool buildSwiftModuleInternal(StringRef OutPath, bool ShouldSerializeDeps,
88-
std::unique_ptr<llvm::MemoryBuffer> *ModuleBuffer);
88+
std::unique_ptr<llvm::MemoryBuffer> *ModuleBuffer,
89+
ArrayRef<std::string> CandidateModules);
8990
public:
9091
ModuleInterfaceBuilder(SourceManager &sourceMgr, DiagnosticEngine &diags,
9192
InterfaceSubContextDelegate &subASTDelegate,
@@ -111,7 +112,8 @@ class ModuleInterfaceBuilder {
111112

112113
bool buildSwiftModule(StringRef OutPath, bool ShouldSerializeDeps,
113114
std::unique_ptr<llvm::MemoryBuffer> *ModuleBuffer,
114-
llvm::function_ref<void()> RemarkRebuild = nullptr);
115+
llvm::function_ref<void()> RemarkRebuild = nullptr,
116+
ArrayRef<std::string> CandidateModules = {});
115117
};
116118

117119
} // end namespace swift

lib/Frontend/ModuleInterfaceLoader.cpp

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1029,7 +1029,6 @@ ModuleInterfaceLoader::getCompiledModuleCandidatesForInterface(StringRef moduleN
10291029
dependencyTracker,
10301030
llvm::is_contained(PreferInterfaceForModules, moduleName) ?
10311031
ModuleLoadingMode::PreferInterface : LoadMode);
1032-
SmallVector<FileDependency, 16> allDeps;
10331032
std::vector<std::string> results;
10341033
auto pair = Impl.getCompiledModuleCandidates();
10351034
// Add compiled module candidates only when they are non-empty.
@@ -1040,6 +1039,41 @@ ModuleInterfaceLoader::getCompiledModuleCandidatesForInterface(StringRef moduleN
10401039
return results;
10411040
}
10421041

1042+
bool ModuleInterfaceLoader::tryEmitForwardingModule(StringRef moduleName,
1043+
StringRef interfacePath,
1044+
ArrayRef<std::string> candidates,
1045+
StringRef outputPath) {
1046+
// Derive .swiftmodule path from the .swiftinterface path.
1047+
auto newExt = file_types::getExtension(file_types::TY_SwiftModuleFile);
1048+
llvm::SmallString<32> modulePath = interfacePath;
1049+
llvm::sys::path::replace_extension(modulePath, newExt);
1050+
ModuleInterfaceLoaderImpl Impl(
1051+
Ctx, modulePath, interfacePath, moduleName,
1052+
CacheDir, PrebuiltCacheDir, SourceLoc(),
1053+
Opts,
1054+
dependencyTracker,
1055+
llvm::is_contained(PreferInterfaceForModules, moduleName) ?
1056+
ModuleLoadingMode::PreferInterface : LoadMode);
1057+
SmallVector<FileDependency, 16> deps;
1058+
std::unique_ptr<llvm::MemoryBuffer> moduleBuffer;
1059+
for (auto mod: candidates) {
1060+
// Check if the candidate compiled module is still up-to-date.
1061+
if (Impl.swiftModuleIsUpToDate(mod, deps, moduleBuffer)) {
1062+
// If so, emit a forwarding module to the candidate.
1063+
ForwardingModule FM(mod);
1064+
auto hadError = withOutputFile(Ctx.Diags, outputPath,
1065+
[&](llvm::raw_pwrite_stream &out) {
1066+
llvm::yaml::Output yamlWriter(out);
1067+
yamlWriter << FM;
1068+
return false;
1069+
});
1070+
if (!hadError)
1071+
return true;
1072+
}
1073+
}
1074+
return false;
1075+
}
1076+
10431077
bool ModuleInterfaceLoader::buildSwiftModuleFromSwiftInterface(
10441078
SourceManager &SourceMgr, DiagnosticEngine &Diags,
10451079
const SearchPathOptions &SearchPathOpts, const LangOptions &LangOpts,
@@ -1069,7 +1103,8 @@ bool ModuleInterfaceLoader::buildSwiftModuleFromSwiftInterface(
10691103
// FIXME: We really only want to serialize 'important' dependencies here, if
10701104
// we want to ship the built swiftmodules to another machine.
10711105
return builder.buildSwiftModule(OutPath, /*shouldSerializeDeps*/true,
1072-
/*ModuleBuffer*/nullptr);
1106+
/*ModuleBuffer*/nullptr, nullptr,
1107+
SearchPathOpts.CandidateCompiledModules);
10731108
}
10741109

10751110
void ModuleInterfaceLoader::collectVisibleTopLevelModuleNames(
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %empty-directory(%t/Foo.swiftmodule)
3+
// RUN: %empty-directory(%t/ResourceDir/%target-sdk-name/prebuilt-modules/Foo.swiftmodule)
4+
// RUN: echo "public func foo() {}" > %t/Foo.swift
5+
6+
import Foo
7+
8+
// Step 1: build swift interface from the source
9+
// RUN: %target-swift-frontend -emit-module %t/Foo.swift -module-name Foo -emit-module-interface-path %t/Foo.swiftmodule/%target-swiftinterface-name
10+
11+
// Step 2: building a module from the interface, and the module should not be a forwarding module.
12+
// RUN: %target-swift-frontend -compile-module-from-interface %t/Foo.swiftmodule/%target-swiftinterface-name -o %t/Foo.swiftmodule/%target-swiftmodule-name -module-name Foo
13+
14+
// RUN: not %{python} %S/ModuleCache/Inputs/check-is-forwarding-module.py %t/Foo.swiftmodule/%target-swiftmodule-name
15+
16+
// Step 3: given the adjacent binary module as a candidate, building a module from the interface should give us a forwarding module
17+
// RUN: %target-swift-frontend -compile-module-from-interface %t/Foo.swiftmodule/%target-swiftinterface-name -o %t/Foo-from-interface.swiftmodule -module-name Foo -candidate-module-file %t/Foo.swiftmodule/%target-swiftmodule-name
18+
19+
// RUN: %{python} %S/ModuleCache/Inputs/check-is-forwarding-module.py %t/Foo-from-interface.swiftmodule
20+
21+
// Step 4: given the stale adjacent binary module as a candidate, building a module from the interface should give us a binary module
22+
// RUN: touch %t/Foo.swiftmodule/%target-swiftinterface-name
23+
24+
// RUN: %target-swift-frontend -compile-module-from-interface %t/Foo.swiftmodule/%target-swiftinterface-name -o %t/Foo-from-interface.swiftmodule -module-name Foo -candidate-module-file %t/Foo.swiftmodule/%target-swiftmodule-name
25+
26+
// RUN: not %{python} %S/ModuleCache/Inputs/check-is-forwarding-module.py %t/Foo-from-interface.swiftmodule

0 commit comments

Comments
 (0)