Skip to content

Commit 366bbf4

Browse files
author
Harlan Haskins
committed
[ParseableInterface] Add ‘forwarding modules’
A ‘forwarding module’ is a YAML file that’s meant to stand in for a .swiftmodule file and provide an up-to-date description of its dependencies, always using modification times. When a ‘prebuilt module’ is first loaded, we verify that it’s up-to-date by hashing all of its dependencies. Since this is orders of magnitude slower than reading mtimes, we’ll install a `forwarding module` containing the mtimes of the now-validated dependencies.
1 parent 67a447f commit 366bbf4

17 files changed

+1315
-703
lines changed

include/swift/Frontend/FrontendInputsAndOutputs.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ class FrontendInputsAndOutputs {
173173

174174
private:
175175
friend class ArgsToFrontendOptionsConverter;
176-
friend class ParseableInterfaceModuleLoader;
176+
friend class ParseableInterfaceBuilder;
177177
void setMainAndSupplementaryOutputs(
178178
ArrayRef<std::string> outputFiles,
179179
ArrayRef<SupplementaryOutputPaths> supplementaryOutputs);
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
//===--- ParseableInterfaceModuleLoader.h - Loads .swiftinterface files ---===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2019 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
/// \file This implements the logic for loading and building parseable module
13+
/// interfaces.
14+
///
15+
/// === Loading Parseable Modules ===
16+
///
17+
/// If there is a .swiftinterface file corresponding to a given module name
18+
/// present in the frontend's search paths, then this module loader will look in
19+
/// the following places for a module:
20+
///
21+
/// - First, look in the module cache (specified by -module-cache-path)
22+
/// - We check here first because an existing .swiftmodule might be
23+
/// out-of-date, necessitating a rebuild. If a cached module is out-of-date,
24+
/// it's simply rebuilt.
25+
/// - Next, look adjacent to the .swiftinterface. If we find a module that's
26+
/// either loadable by this compiler, valid, and up-to-date, or totally
27+
/// unreadable, then delegate to the serialized module loader to either load
28+
/// or diagnose it.
29+
/// - Finally, look in the prebuilt module cache (specified
30+
/// by -prebuilt-module-cache-path)
31+
///
32+
/// If we can't find an appropriate module to load, we can always fall back and
33+
/// recompile the .swiftinterface file.
34+
///
35+
/// === Dependency Checking ===
36+
///
37+
/// Cached modules keep track of their dependencies' last modification time and
38+
/// file size. This means that checking if a module is up-to-date requires
39+
/// `stat`ing the dependencies and comparing the results from the filesystem
40+
/// with the results in the module.
41+
///
42+
/// Prebuilt modules, on the other hand, won't have a reliable modification
43+
/// time, as their dependencies live in the SDK. Prebuilt modules will instead
44+
/// keep track of the size and content hash of their dependencies.
45+
/// In order to avoid constantly re-hashing the dependencies, however, we will
46+
/// install a "forwarding module" in the regular cache.
47+
/// This "forwarding module"
48+
/// - Points to the prebuilt module on disk, and
49+
/// - Lists modification times from the last time we verified the content
50+
///
51+
/// So, to recap, there are 4 kinds of modules:
52+
/// ┌───────────────────────────────┐
53+
/// │ ┌───┐ ┌───┐ │
54+
/// │ │ I │ │ M │ │
55+
/// │ └───┘ └───┘ │
56+
/// │ .swiftinterface .swiftmodule │
57+
/// │ ┌───┐ ┌───┐ │
58+
/// │ │ P │ │ F │ │
59+
/// │ └───┘ └───┘ │
60+
/// │ Prebuilt Forwarding │
61+
/// │ .swiftmodule .swiftmodule │
62+
/// └───────────────────────────────┘
63+
///
64+
/// - Prebuilt modules have hash-based dependencies, cached modules have
65+
/// mod-time-based dependencies
66+
/// - Forwarding modules point to prebuilt modules and augment them with
67+
/// modification times
68+
///
69+
/// === Example Cache ===
70+
///
71+
/// Here's an example of what's in a prebuilt cache or module cache.
72+
///
73+
/// Say there are 4 frameworks, each exposing a .swiftinterface file.
74+
/// Then, we pre-build 2 of those frameworks and put them in the prebuilt cache.
75+
/// Finally, we import all 4 of those frameworks while building a project.
76+
///
77+
/// For the 2 frameworks with modules in the prebuilt cache, we'll have
78+
/// installed 2 forwarding modules. For the other 2 frameworks, we'll have
79+
/// compiled the interfaces and put them in the module cache.
80+
///
81+
/// ┌─────┐
82+
/// ┌─────────────────────────────┤ SDK ├─────────────────────────┐
83+
/// │ ┌────────────────┐ └─────┘ ┌────────────────┐ │
84+
/// │ ┌───────┤ Framework Dirs ├────────┐ ┌┤ Prebuilt Cache ├┐ │
85+
/// │ │ └────────────────┘ │ │└────────────────┘│ │
86+
/// │ │ ┌───┐ ┌───┐ ┌───┐ ┌───┐ │ │ ┌───┐ ┌───┐ │ │
87+
/// │ │ │ I │ │ I │ │ I │ │ I │◀─┼───┼───│ P │ │ P │◀═╗│ │
88+
/// │ │ └───┘ └───┘ └───┘ └───┘ │ │ └───┘ └───┘ ║│ │
89+
/// │ │ ▲ ▲ ▲ │ │ ▲ │ ║│ │
90+
/// │ └────┼───────┼───────┼────────────┘ └─────╫──────┼────╫┘ │
91+
/// │ │ │ └──────────────────────╫──────┘ ║ │
92+
/// └───────┼───────┼──────────────────────────────╫───────────╫──┘
93+
/// │ │ ┌───────────────┐ ║ ║
94+
/// │ ┌────┼───┤ Module Cache ├────────┐ ║ ║
95+
/// │ │ │ └───────────────┘ │ ║ ║
96+
/// │ │ ┌───┐ ┌───┐ ┌───┐ ┌───┐ │ ║ ║
97+
/// │ │ │ M │ │ M │ │ F │ │ F │ │ ║ ║
98+
/// │ │ └───┘ └───┘ └───┘ └───┘ │ ║ ║
99+
/// │ │ │ ║ ╚════╪═╝ ║
100+
/// │ └────────────┼───────╫────────────┘ ║
101+
/// └───────────────┘ ╚══════════════════════════╝
102+
///
103+
//===----------------------------------------------------------------------===//
104+
105+
#include "swift/Basic/LLVM.h"
106+
#include "swift/Frontend/ParseableInterfaceSupport.h"
107+
#include "swift/Serialization/SerializedModuleLoader.h"
108+
109+
namespace clang {
110+
class CompilerInstance;
111+
}
112+
113+
namespace swift {
114+
115+
/// A ModuleLoader that runs a subordinate \c CompilerInvocation and
116+
/// \c CompilerInstance to convert .swiftinterface files to .swiftmodule
117+
/// files on the fly, caching the resulting .swiftmodules in the module cache
118+
/// directory, and loading the serialized .swiftmodules from there.
119+
class ParseableInterfaceModuleLoader : public SerializedModuleLoaderBase {
120+
explicit ParseableInterfaceModuleLoader(ASTContext &ctx, StringRef cacheDir,
121+
StringRef prebuiltCacheDir,
122+
DependencyTracker *tracker,
123+
ModuleLoadingMode loadMode)
124+
: SerializedModuleLoaderBase(ctx, tracker, loadMode),
125+
CacheDir(cacheDir), PrebuiltCacheDir(prebuiltCacheDir)
126+
{}
127+
128+
std::string CacheDir;
129+
std::string PrebuiltCacheDir;
130+
131+
std::error_code findModuleFilesInDirectory(
132+
AccessPathElem ModuleID, StringRef DirPath, StringRef ModuleFilename,
133+
StringRef ModuleDocFilename,
134+
std::unique_ptr<llvm::MemoryBuffer> *ModuleBuffer,
135+
std::unique_ptr<llvm::MemoryBuffer> *ModuleDocBuffer) override;
136+
137+
138+
public:
139+
static std::unique_ptr<ParseableInterfaceModuleLoader>
140+
create(ASTContext &ctx, StringRef cacheDir, StringRef prebuiltCacheDir,
141+
DependencyTracker *tracker, ModuleLoadingMode loadMode) {
142+
return std::unique_ptr<ParseableInterfaceModuleLoader>(
143+
new ParseableInterfaceModuleLoader(ctx, cacheDir, prebuiltCacheDir,
144+
tracker, loadMode));
145+
}
146+
147+
/// Unconditionally build \p InPath (a swiftinterface file) to \p OutPath (as
148+
/// a swiftmodule file).
149+
///
150+
/// A simplified version of the core logic in #openModuleFiles, mostly for
151+
/// testing purposes.
152+
static bool buildSwiftModuleFromSwiftInterface(
153+
ASTContext &Ctx, StringRef CacheDir, StringRef PrebuiltCacheDir,
154+
StringRef ModuleName, StringRef InPath, StringRef OutPath,
155+
bool SerializeDependencyHashes);
156+
};
157+
158+
/// Extract the specified-or-defaulted -module-cache-path that winds up in
159+
/// the clang importer, for reuse as the .swiftmodule cache path when
160+
/// building a ParseableInterfaceModuleLoader.
161+
std::string
162+
getModuleCachePathFromClang(const clang::CompilerInstance &Instance);
163+
164+
}

include/swift/Frontend/ParseableInterfaceSupport.h

Lines changed: 8 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//
33
// This source file is part of the Swift.org open source project
44
//
5-
// Copyright (c) 2018 Apple Inc. and the Swift project authors
5+
// Copyright (c) 2019 Apple Inc. and the Swift project authors
66
// Licensed under Apache License v2.0 with Runtime Library Exception
77
//
88
// See https://swift.org/LICENSE.txt for license information
@@ -14,9 +14,13 @@
1414
#define SWIFT_FRONTEND_PARSEABLEINTERFACESUPPORT_H
1515

1616
#include "swift/Basic/LLVM.h"
17-
#include "swift/Serialization/SerializedModuleLoader.h"
17+
#include "swift/Basic/Version.h"
1818
#include "llvm/Support/Regex.h"
1919

20+
#define SWIFT_INTERFACE_FORMAT_VERSION_KEY "swift-interface-format-version"
21+
#define SWIFT_TOOLS_VERSION_KEY "swift-tools-version"
22+
#define SWIFT_MODULE_FLAGS_KEY "swift-module-flags"
23+
2024
namespace swift {
2125

2226
class ModuleDecl;
@@ -29,6 +33,8 @@ struct ParseableInterfaceOptions {
2933
std::string ParseableInterfaceFlags;
3034
};
3135

36+
extern version::Version InterfaceFormatVersion;
37+
3238
llvm::Regex getSwiftInterfaceFormatVersionRegex();
3339
llvm::Regex getSwiftInterfaceModuleFlagsRegex();
3440

@@ -49,71 +55,6 @@ bool emitParseableInterface(raw_ostream &out,
4955
ParseableInterfaceOptions const &Opts,
5056
ModuleDecl *M);
5157

52-
/// Extract the specified-or-defaulted -module-cache-path that winds up in
53-
/// the clang importer, for reuse as the .swiftmodule cache path when
54-
/// building a ParseableInterfaceModuleLoader.
55-
std::string
56-
getModuleCachePathFromClang(const clang::CompilerInstance &Instance);
57-
58-
/// A ModuleLoader that runs a subordinate \c CompilerInvocation and \c
59-
/// CompilerInstance to convert .swiftinterface files to .swiftmodule
60-
/// files on the fly, caching the resulting .swiftmodules in the module cache
61-
/// directory, and loading the serialized .swiftmodules from there.
62-
class ParseableInterfaceModuleLoader : public SerializedModuleLoaderBase {
63-
explicit ParseableInterfaceModuleLoader(ASTContext &ctx, StringRef cacheDir,
64-
StringRef prebuiltCacheDir,
65-
DependencyTracker *tracker,
66-
ModuleLoadingMode loadMode)
67-
: SerializedModuleLoaderBase(ctx, tracker, loadMode),
68-
CacheDir(cacheDir), PrebuiltCacheDir(prebuiltCacheDir)
69-
{}
70-
71-
std::string CacheDir;
72-
std::string PrebuiltCacheDir;
73-
74-
/// Wire up the SubInvocation's InputsAndOutputs to contain both input and
75-
/// output filenames.
76-
///
77-
/// This is a method rather than a helper function in the implementation file
78-
/// because it accesses non-public bits of FrontendInputsAndOutputs.
79-
static void configureSubInvocationInputsAndOutputs(
80-
CompilerInvocation &SubInvocation, StringRef InPath, StringRef OutPath);
81-
82-
static bool buildSwiftModuleFromSwiftInterface(
83-
llvm::vfs::FileSystem &FS, DiagnosticEngine &Diags, SourceLoc DiagLoc,
84-
CompilerInvocation &SubInvocation, StringRef InPath, StringRef OutPath,
85-
StringRef ModuleCachePath, DependencyTracker *OuterTracker,
86-
bool ShouldSerializeDeps);
87-
88-
std::error_code findModuleFilesInDirectory(
89-
AccessPathElem ModuleID, StringRef DirPath, StringRef ModuleFilename,
90-
StringRef ModuleDocFilename,
91-
std::unique_ptr<llvm::MemoryBuffer> *ModuleBuffer,
92-
std::unique_ptr<llvm::MemoryBuffer> *ModuleDocBuffer) override;
93-
94-
public:
95-
static std::unique_ptr<ParseableInterfaceModuleLoader>
96-
create(ASTContext &ctx, StringRef cacheDir, StringRef prebuiltCacheDir,
97-
DependencyTracker *tracker, ModuleLoadingMode loadMode) {
98-
return std::unique_ptr<ParseableInterfaceModuleLoader>(
99-
new ParseableInterfaceModuleLoader(ctx, cacheDir, prebuiltCacheDir,
100-
tracker, loadMode));
101-
}
102-
103-
/// Unconditionally build \p InPath (a swiftinterface file) to \p OutPath (as
104-
/// a swiftmodule file).
105-
///
106-
/// A simplified version of the core logic in #openModuleFiles, mostly for
107-
/// testing purposes.
108-
static bool buildSwiftModuleFromSwiftInterface(ASTContext &Ctx,
109-
StringRef CacheDir,
110-
StringRef PrebuiltCacheDir,
111-
StringRef ModuleName,
112-
StringRef InPath,
113-
StringRef OutPath);
114-
};
115-
116-
11758
} // end namespace swift
11859

11960
#endif

lib/Frontend/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ add_swift_host_library(swiftFrontend STATIC
77
Frontend.cpp
88
FrontendInputsAndOutputs.cpp
99
FrontendOptions.cpp
10+
ParseableInterfaceModuleLoader.cpp
1011
ParseableInterfaceSupport.cpp
1112
PrintingDiagnosticConsumer.cpp
1213
SerializedDiagnosticConsumer.cpp

lib/Frontend/Frontend.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "swift/Basic/SourceManager.h"
2525
#include "swift/Basic/Statistic.h"
2626
#include "swift/DWARFImporter/DWARFImporter.h"
27+
#include "swift/Frontend/ParseableInterfaceModuleLoader.h"
2728
#include "swift/Parse/DelayedParsingCallbacks.h"
2829
#include "swift/Parse/Lexer.h"
2930
#include "swift/SIL/SILModule.h"

0 commit comments

Comments
 (0)