Skip to content

Commit 4bfe4ef

Browse files
committed
[ParseableInterfaces] Add -prebuilt-module-cache-path to the frontend
When trying to load a swiftinterface, search this directory before doing all the work of building a swiftmodule.
1 parent f6eeb3a commit 4bfe4ef

File tree

10 files changed

+184
-22
lines changed

10 files changed

+184
-22
lines changed

include/swift/Basic/STLExtras.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -792,6 +792,30 @@ inline T accumulate(const Container &C, T init, BinaryOperation op) {
792792
return std::accumulate(C.begin(), C.end(), init, op);
793793
}
794794

795+
/// Returns true if the range defined by \p mainBegin ..< \p mainEnd starts with
796+
/// the same elements as the range defined by \p prefixBegin ..< \p prefixEnd.
797+
///
798+
/// This includes cases where the prefix range is empty, as well as when the two
799+
/// ranges are the same length and contain the same elements.
800+
template <typename MainInputIterator, typename PrefixInputIterator>
801+
inline bool hasPrefix(MainInputIterator mainBegin,
802+
const MainInputIterator mainEnd,
803+
PrefixInputIterator prefixBegin,
804+
const PrefixInputIterator prefixEnd) {
805+
while (prefixBegin != prefixEnd) {
806+
// If "main" is shorter than "prefix", it does not start with "prefix".
807+
if (mainBegin == mainEnd)
808+
return false;
809+
// If there's a mismatch, "main" does not start with "prefix".
810+
if (*mainBegin != *prefixBegin)
811+
return false;
812+
++prefixBegin;
813+
++mainBegin;
814+
}
815+
// If we checked every element of "prefix", "main" does start with "prefix".
816+
return true;
817+
}
818+
795819
/// Provides default implementations of !=, <=, >, and >= based on == and <.
796820
template <typename T>
797821
class RelationalOperationsBase {

include/swift/Frontend/FrontendOptions.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,10 @@ class FrontendOptions {
7272
/// The path to which we should store indexing data, if any.
7373
std::string IndexStorePath;
7474

75+
/// The path to look in when loading a parseable interface file, to see if a
76+
/// binary module has already been built for use by the compiler.
77+
std::string PrebuiltModuleCachePath;
78+
7579
/// Emit index data for imported serialized swift system modules.
7680
bool IndexSystemModules = false;
7781

include/swift/Frontend/ParseableInterfaceSupport.h

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -61,13 +61,15 @@ getModuleCachePathFromClang(const clang::CompilerInstance &Instance);
6161
/// directory, and loading the serialized .swiftmodules from there.
6262
class ParseableInterfaceModuleLoader : public SerializedModuleLoaderBase {
6363
explicit ParseableInterfaceModuleLoader(ASTContext &ctx, StringRef cacheDir,
64+
StringRef prebuiltCacheDir,
6465
DependencyTracker *tracker,
6566
ModuleLoadingMode loadMode)
6667
: SerializedModuleLoaderBase(ctx, tracker, loadMode),
67-
CacheDir(cacheDir)
68+
CacheDir(cacheDir), PrebuiltCacheDir(prebuiltCacheDir)
6869
{}
6970

7071
std::string CacheDir;
72+
std::string PrebuiltCacheDir;
7173

7274
/// Wire up the SubInvocation's InputsAndOutputs to contain both input and
7375
/// output filenames.
@@ -86,11 +88,11 @@ class ParseableInterfaceModuleLoader : public SerializedModuleLoaderBase {
8688

8789
public:
8890
static std::unique_ptr<ParseableInterfaceModuleLoader>
89-
create(ASTContext &ctx, StringRef cacheDir,
90-
DependencyTracker *tracker,
91-
ModuleLoadingMode loadMode) {
91+
create(ASTContext &ctx, StringRef cacheDir, StringRef prebuiltCacheDir,
92+
DependencyTracker *tracker, ModuleLoadingMode loadMode) {
9293
return std::unique_ptr<ParseableInterfaceModuleLoader>(
93-
new ParseableInterfaceModuleLoader(ctx, cacheDir, tracker, loadMode));
94+
new ParseableInterfaceModuleLoader(ctx, cacheDir, prebuiltCacheDir,
95+
tracker, loadMode));
9496
}
9597

9698
/// Unconditionally build \p InPath (a swiftinterface file) to \p OutPath (as

include/swift/Option/FrontendOptions.td

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -518,6 +518,13 @@ def build_module_from_parseable_interface :
518518
HelpText<"Treat the (single) input as a swiftinterface and produce a module">,
519519
ModeOpt;
520520

521+
def prebuilt_module_cache_path :
522+
Separate<["-"], "prebuilt-module-cache-path">,
523+
HelpText<"Directory of prebuilt modules for loading parseable interfaces">;
524+
def prebuilt_module_cache_path_EQ :
525+
Joined<["-"], "prebuilt-module-cache-path=">,
526+
Alias<prebuilt_module_cache_path>;
527+
521528
def enable_resilience_bypass : Flag<["-"], "enable-resilience-bypass">,
522529
HelpText<"Completely bypass resilience when accessing types in resilient frameworks">;
523530

lib/Frontend/ArgsToFrontendOptionsConverter.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,9 @@ bool ArgsToFrontendOptionsConverter::convert(
6060
if (const Arg *A = Args.getLastArg(OPT_index_store_path)) {
6161
Opts.IndexStorePath = A->getValue();
6262
}
63+
if (const Arg *A = Args.getLastArg(OPT_prebuilt_module_cache_path)) {
64+
Opts.PrebuiltModuleCachePath = A->getValue();
65+
}
6366

6467
Opts.IndexSystemModules |= Args.hasArg(OPT_index_system_modules);
6568

lib/Frontend/Frontend.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,8 +331,11 @@ bool CompilerInstance::setUpModuleLoaders() {
331331
if (MLM != ModuleLoadingMode::OnlySerialized) {
332332
auto const &Clang = clangImporter->getClangInstance();
333333
std::string ModuleCachePath = getModuleCachePathFromClang(Clang);
334+
StringRef PrebuiltModuleCachePath =
335+
Invocation.getFrontendOptions().PrebuiltModuleCachePath;
334336
auto PIML = ParseableInterfaceModuleLoader::create(*Context,
335337
ModuleCachePath,
338+
PrebuiltModuleCachePath,
336339
getDependencyTracker(),
337340
MLM);
338341
Context->addModuleLoader(std::move(PIML));

lib/Frontend/ParseableInterfaceSupport.cpp

Lines changed: 56 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "swift/AST/FileSystem.h"
2020
#include "swift/AST/Module.h"
2121
#include "swift/AST/ProtocolConformance.h"
22+
#include "swift/Basic/STLExtras.h"
2223
#include "swift/Frontend/Frontend.h"
2324
#include "swift/Frontend/ParseableInterfaceSupport.h"
2425
#include "swift/Frontend/PrintingDiagnosticConsumer.h"
@@ -472,32 +473,70 @@ std::error_code ParseableInterfaceModuleLoader::openModuleFiles(
472473
return std::make_error_code(std::errc::not_supported);
473474
}
474475

475-
// At this point we're either in PreferParseable mode or there's no credible
476-
// adjacent .swiftmodule so we'll go ahead and start trying to convert the
477-
// .swiftinterface.
476+
// If we have a prebuilt cache path, check that too if the interface comes
477+
// from the SDK.
478+
if (!PrebuiltCacheDir.empty()) {
479+
StringRef SDKPath = Ctx.SearchPathOpts.SDKPath;
480+
if (!SDKPath.empty() && hasPrefix(llvm::sys::path::begin(InPath),
481+
llvm::sys::path::end(InPath),
482+
llvm::sys::path::begin(SDKPath),
483+
llvm::sys::path::end(SDKPath))) {
484+
// Assemble the expected path: $PREBUILT_CACHE/Foo.swiftmodule or
485+
// $PREBUILT_CACHE/Foo.swiftmodule/arch.swiftmodule. Note that there's no
486+
// cache key here.
487+
OutPath = PrebuiltCacheDir;
488+
489+
// FIXME: Would it be possible to only have architecture-specific names
490+
// here? Then we could skip this check.
491+
StringRef InParentDirName =
492+
llvm::sys::path::filename(llvm::sys::path::parent_path(InPath));
493+
if (llvm::sys::path::extension(InParentDirName) == ".swiftmodule") {
494+
assert(llvm::sys::path::stem(InParentDirName) == ModuleID.first.str());
495+
llvm::sys::path::append(OutPath, InParentDirName);
496+
}
497+
llvm::sys::path::append(OutPath, ModuleFilename);
478498

479-
// Set up a _potential_ sub-invocation to consume the .swiftinterface and emit
480-
// the .swiftmodule.
481-
CompilerInvocation SubInvocation =
482-
createInvocationForBuildingFromInterface(Ctx, ModuleID.first.str(), CacheDir);
483-
computeCachedOutputPath(Ctx, SubInvocation, InPath, OutPath);
484-
configureSubInvocationInputsAndOutputs(SubInvocation, InPath, OutPath);
499+
if (!swiftModuleIsUpToDate(FS, ModuleID, OutPath, Diags,
500+
dependencyTracker)) {
501+
OutPath.clear();
502+
}
503+
}
504+
}
485505

486-
// Evaluate if we need to run this sub-invocation, and if so run it.
487-
if (!swiftModuleIsUpToDate(FS, ModuleID, OutPath, Diags, dependencyTracker)) {
488-
if (::buildSwiftModuleFromSwiftInterface(FS, Diags, ModuleID.second,
489-
SubInvocation, CacheDir,
490-
dependencyTracker))
491-
return std::make_error_code(std::errc::invalid_argument);
506+
if (OutPath.empty()) {
507+
// At this point we're either in PreferParseable mode or there's no credible
508+
// adjacent .swiftmodule so we'll go ahead and start trying to convert the
509+
// .swiftinterface.
510+
511+
// Set up a _potential_ sub-invocation to consume the .swiftinterface and
512+
// emit the .swiftmodule.
513+
CompilerInvocation SubInvocation =
514+
createInvocationForBuildingFromInterface(Ctx, ModuleID.first.str(),
515+
CacheDir);
516+
computeCachedOutputPath(Ctx, SubInvocation, InPath, OutPath);
517+
configureSubInvocationInputsAndOutputs(SubInvocation, InPath, OutPath);
518+
519+
// Evaluate if we need to run this sub-invocation, and if so run it.
520+
if (!swiftModuleIsUpToDate(FS, ModuleID, OutPath, Diags,
521+
dependencyTracker)) {
522+
if (::buildSwiftModuleFromSwiftInterface(FS, Diags, ModuleID.second,
523+
SubInvocation, CacheDir,
524+
dependencyTracker))
525+
return std::make_error_code(std::errc::invalid_argument);
526+
}
492527
}
493528

494529
// Finish off by delegating back up to the SerializedModuleLoaderBase
495530
// routine that can load the recently-manufactured serialized module.
496531
LLVM_DEBUG(llvm::dbgs() << "Loading " << OutPath
497532
<< " via normal module loader\n");
533+
// FIXME: This will never find the swiftdoc file, because that's in the
534+
// original directory. We should probably just not try to match the signature
535+
// of the overridable entry point.
498536
auto ErrorCode = SerializedModuleLoaderBase::openModuleFiles(
499-
ModuleID, CacheDir, llvm::sys::path::filename(OutPath),
500-
ModuleDocFilename, ModuleBuffer, ModuleDocBuffer, Scratch);
537+
ModuleID, llvm::sys::path::parent_path(OutPath),
538+
llvm::sys::path::filename(OutPath), ModuleDocFilename, ModuleBuffer,
539+
ModuleDocBuffer, Scratch);
501540
LLVM_DEBUG(llvm::dbgs() << "Loaded " << OutPath
502541
<< " via normal module loader");
503542
if (ErrorCode) {
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// swift-interface-format-version: 1.0
2+
// swift-module-flags: -parse-stdlib -module-name Lib
3+
4+
public struct FromInterface {
5+
@inlinable public init() {}
6+
}
7+
public var testValue: FromInterface {
8+
@inlinable get { return FromInterface() }
9+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %empty-directory(%t/include/Lib.swiftmodule)
3+
// RUN: cp %S/Inputs/prebuilt-module-cache/Lib.swiftinterface %t/include/Lib.swiftmodule/$(basename %target-swiftmodule-name .swiftmodule).swiftinterface
4+
5+
// Baseline check: if the prebuilt cache path does not exist, everything should
6+
// still work.
7+
// RUN: not %target-swift-frontend -typecheck -enable-parseable-module-interface -parse-stdlib -module-cache-path %t/MCP -sdk %t/include -I %t/include/ -prebuilt-module-cache-path %t/prebuilt-cache %s 2>&1 | %FileCheck -check-prefix=FROM-INTERFACE %s
8+
9+
// Baseline check: if the module is not in the prebuilt cache, build it
10+
// normally.
11+
// RUN: %empty-directory(%t/prebuilt-cache)
12+
// RUN: not %target-swift-frontend -typecheck -enable-parseable-module-interface -parse-stdlib -module-cache-path %t/MCP -sdk %t/include -I %t/include/ -prebuilt-module-cache-path %t/prebuilt-cache %s 2>&1 | %FileCheck -check-prefix=FROM-INTERFACE %s
13+
14+
// Do a manual prebuild, and see if it gets picked up.
15+
// RUN: %empty-directory(%t/MCP)
16+
// RUN: %empty-directory(%t/prebuilt-cache/Lib.swiftmodule)
17+
// RUN: sed -e 's/FromInterface/FromPrebuilt/g' %S/Inputs/prebuilt-module-cache/Lib.swiftinterface | %target-swift-frontend -parse-stdlib -module-cache-path %t/MCP -emit-module-path %t/prebuilt-cache/Lib.swiftmodule/%target-swiftmodule-name - -module-name Lib
18+
// RUN: not %target-swift-frontend -typecheck -enable-parseable-module-interface -parse-stdlib -module-cache-path %t/MCP -sdk %t/include -I %t/include/ -prebuilt-module-cache-path %t/prebuilt-cache %s 2>&1 | %FileCheck -check-prefix=FROM-PREBUILT %s
19+
// RUN: ls %t/MCP | grep -v swiftmodule
20+
21+
// What if the module is invalid?
22+
// RUN: rm %t/prebuilt-cache/Lib.swiftmodule/%target-swiftmodule-name && touch %t/prebuilt-cache/Lib.swiftmodule/%target-swiftmodule-name
23+
// RUN: not %target-swift-frontend -typecheck -enable-parseable-module-interface -parse-stdlib -module-cache-path %t/MCP -sdk %t/include -I %t/include/ -prebuilt-module-cache-path %t/prebuilt-cache %s 2>&1 | %FileCheck -check-prefix=FROM-INTERFACE %s
24+
25+
// What if the arch is missing?
26+
// RUN: rm %t/prebuilt-cache/Lib.swiftmodule/%target-swiftmodule-name
27+
// RUN: not %target-swift-frontend -typecheck -enable-parseable-module-interface -parse-stdlib -module-cache-path %t/MCP -sdk %t/include -I %t/include/ -prebuilt-module-cache-path %t/prebuilt-cache %s 2>&1 | %FileCheck -check-prefix=FROM-INTERFACE %s
28+
29+
import Lib
30+
31+
struct X {}
32+
let _: X = Lib.testValue
33+
// FROM-INTERFACE: [[@LINE-1]]:16: error: cannot convert value of type 'FromInterface' to specified type 'X'
34+
// FROM-PREBUILT: [[@LINE-2]]:16: error: cannot convert value of type 'FromPrebuilt' to specified type 'X'
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// RUN: %empty-directory(%t)
2+
3+
// Baseline check: if the prebuilt cache path does not exist, everything should
4+
// still work.
5+
// RUN: not %target-swift-frontend -typecheck -enable-parseable-module-interface -parse-stdlib -module-cache-path %t/MCP -sdk %S/Inputs -I %S/Inputs/prebuilt-module-cache/ -prebuilt-module-cache-path %t/prebuilt-cache %s 2>&1 | %FileCheck -check-prefix=FROM-INTERFACE %s
6+
7+
// Baseline check: if the module is not in the prebuilt cache, build it
8+
// normally.
9+
// RUN: %empty-directory(%t/prebuilt-cache)
10+
// RUN: not %target-swift-frontend -typecheck -enable-parseable-module-interface -parse-stdlib -module-cache-path %t/MCP -sdk %S/Inputs -I %S/Inputs/prebuilt-module-cache/ -prebuilt-module-cache-path %t/prebuilt-cache %s 2>&1 | %FileCheck -check-prefix=FROM-INTERFACE %s
11+
12+
// Do a manual prebuild, and see if it gets picked up.
13+
// RUN: %empty-directory(%t/MCP)
14+
// RUN: sed -e 's/FromInterface/FromPrebuilt/g' %S/Inputs/prebuilt-module-cache/Lib.swiftinterface | %target-swift-frontend -parse-stdlib -module-cache-path %t/MCP -emit-module-path %t/prebuilt-cache/Lib.swiftmodule - -module-name Lib
15+
// RUN: not %target-swift-frontend -typecheck -enable-parseable-module-interface -parse-stdlib -module-cache-path %t/MCP -sdk %S/Inputs -I %S/Inputs/prebuilt-module-cache/ -prebuilt-module-cache-path %t/prebuilt-cache %s 2>&1 | %FileCheck -check-prefix=FROM-PREBUILT %s
16+
// RUN: ls %t/MCP | grep -v swiftmodule
17+
18+
// Try some variations on the detection that the search path is in the SDK:
19+
// RUN: not %target-swift-frontend -typecheck -enable-parseable-module-interface -parse-stdlib -module-cache-path %t/MCP -sdk %S/Inputs -I %S/Inputs/prebuilt-module-cache/ -prebuilt-module-cache-path %t/prebuilt-cache %s 2>&1 | %FileCheck -check-prefix=FROM-PREBUILT %s
20+
// RUN: not %target-swift-frontend -typecheck -enable-parseable-module-interface -parse-stdlib -module-cache-path %t/MCP -sdk %S//Inputs -I %S/Inputs/prebuilt-module-cache/ -prebuilt-module-cache-path %t/prebuilt-cache %s 2>&1 | %FileCheck -check-prefix=FROM-PREBUILT %s
21+
// RUN: not %target-swift-frontend -typecheck -enable-parseable-module-interface -parse-stdlib -module-cache-path %t/MCP -sdk %S/Inputs/prebuilt-module-cache -I %S/Inputs/prebuilt-module-cache/ -prebuilt-module-cache-path %t/prebuilt-cache %s 2>&1 | %FileCheck -check-prefix=FROM-PREBUILT %s
22+
// RUN: not %target-swift-frontend -typecheck -enable-parseable-module-interface -parse-stdlib -module-cache-path %t/MCP -sdk / -I %S/Inputs/prebuilt-module-cache/ -prebuilt-module-cache-path %t/prebuilt-cache %s 2>&1 | %FileCheck -check-prefix=FROM-PREBUILT %s
23+
24+
// RUN: not %target-swift-frontend -typecheck -enable-parseable-module-interface -parse-stdlib -module-cache-path %t/MCP -sdk %S/Inputs/p -I %S/Inputs/prebuilt-module-cache/ -prebuilt-module-cache-path %t/prebuilt-cache %s 2>&1 | %FileCheck -check-prefix=FROM-INTERFACE %s
25+
// RUN: not %target-swift-frontend -typecheck -enable-parseable-module-interface -parse-stdlib -module-cache-path %t/MCP -sdk %S/garbage-path -I %S/Inputs/prebuilt-module-cache/ -prebuilt-module-cache-path %t/prebuilt-cache %s 2>&1 | %FileCheck -check-prefix=FROM-INTERFACE %s
26+
// RUN: not %target-swift-frontend -typecheck -enable-parseable-module-interface -parse-stdlib -module-cache-path %t/MCP -sdk "" -I %S/Inputs/prebuilt-module-cache/ -prebuilt-module-cache-path %t/prebuilt-cache %s 2>&1 | %FileCheck -check-prefix=FROM-INTERFACE %s
27+
28+
// What if the module is invalid?
29+
// RUN: rm %t/prebuilt-cache/Lib.swiftmodule && touch %t/prebuilt-cache/Lib.swiftmodule
30+
// RUN: not %target-swift-frontend -typecheck -enable-parseable-module-interface -parse-stdlib -module-cache-path %t/MCP -sdk %S/Inputs/ -I %S/Inputs/prebuilt-module-cache/ -prebuilt-module-cache-path %t/prebuilt-cache %s 2>&1 | %FileCheck -check-prefix=FROM-INTERFACE %s
31+
32+
import Lib
33+
34+
struct X {}
35+
let _: X = Lib.testValue
36+
// FROM-INTERFACE: [[@LINE-1]]:16: error: cannot convert value of type 'FromInterface' to specified type 'X'
37+
// FROM-PREBUILT: [[@LINE-2]]:16: error: cannot convert value of type 'FromPrebuilt' to specified type 'X'

0 commit comments

Comments
 (0)