Skip to content

Commit 714a2b4

Browse files
author
Harlan Haskins
authored
Merge pull request #23173 from harlanhaskins/never-trust-the-system
[ParseableInterface] Serialize module into buffer and load from it
2 parents 526678c + 5a29cdc commit 714a2b4

File tree

9 files changed

+360
-91
lines changed

9 files changed

+360
-91
lines changed

include/swift/Frontend/ParseableInterfaceModuleLoader.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,13 +110,18 @@ namespace clang {
110110
class CompilerInstance;
111111
}
112112

113+
namespace unittest {
114+
class ParseableInterfaceModuleLoaderTest;
115+
}
116+
113117
namespace swift {
114118

115119
/// A ModuleLoader that runs a subordinate \c CompilerInvocation and
116120
/// \c CompilerInstance to convert .swiftinterface files to .swiftmodule
117121
/// files on the fly, caching the resulting .swiftmodules in the module cache
118122
/// directory, and loading the serialized .swiftmodules from there.
119123
class ParseableInterfaceModuleLoader : public SerializedModuleLoaderBase {
124+
friend class unittest::ParseableInterfaceModuleLoaderTest;
120125
explicit ParseableInterfaceModuleLoader(ASTContext &ctx, StringRef cacheDir,
121126
StringRef prebuiltCacheDir,
122127
DependencyTracker *tracker,
@@ -134,7 +139,6 @@ class ParseableInterfaceModuleLoader : public SerializedModuleLoaderBase {
134139
std::unique_ptr<llvm::MemoryBuffer> *ModuleBuffer,
135140
std::unique_ptr<llvm::MemoryBuffer> *ModuleDocBuffer) override;
136141

137-
138142
public:
139143
static std::unique_ptr<ParseableInterfaceModuleLoader>
140144
create(ASTContext &ctx, StringRef cacheDir, StringRef prebuiltCacheDir,

include/swift/Serialization/SerializedModuleLoader.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,11 @@ class SerializedModuleLoaderBase : public ModuleLoader {
6363
std::unique_ptr<llvm::MemoryBuffer> *ModuleBuffer,
6464
std::unique_ptr<llvm::MemoryBuffer> *ModuleDocBuffer);
6565

66+
std::error_code
67+
openModuleDocFile(AccessPathElem ModuleID,
68+
StringRef ModuleDocPath,
69+
std::unique_ptr<llvm::MemoryBuffer> *ModuleDocBuffer);
70+
6671
/// If the module loader subclass knows that all options have been tried for
6772
/// loading an architecture-specific file out of a swiftmodule bundle, try
6873
/// to list the architectures that \e are present.

include/swift/Subsystems.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,18 @@ namespace swift {
264264
void serialize(ModuleOrSourceFile DC, const SerializationOptions &options,
265265
const SILModule *M = nullptr);
266266

267+
/// Serializes a module or single source file to the given output file and
268+
/// returns back the file's contents as a memory buffer.
269+
///
270+
/// Use this if you intend to immediately load the serialized module, as that
271+
/// will both avoid extra filesystem traffic and will ensure you read back
272+
/// exactly what was written.
273+
void serializeToBuffers(ModuleOrSourceFile DC,
274+
const SerializationOptions &opts,
275+
std::unique_ptr<llvm::MemoryBuffer> *moduleBuffer,
276+
std::unique_ptr<llvm::MemoryBuffer> *moduleDocBuffer,
277+
const SILModule *M = nullptr);
278+
267279
/// Get the CPU, subtarget feature options, and triple to use when emitting code.
268280
std::tuple<llvm::TargetOptions, std::string, std::vector<std::string>,
269281
std::string>

lib/ClangImporter/ClangImporter.cpp

Lines changed: 32 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,35 @@ void ClangImporter::clearTypeResolver() {
407407

408408
#pragma mark Module loading
409409

410+
/// Finds the glibc.modulemap file relative to the provided resource dir.
411+
///
412+
/// Note that the module map used for Glibc depends on the target we're
413+
/// compiling for, and is not included in the resource directory with the other
414+
/// implicit module maps. It's at {freebsd|linux}/{arch}/glibc.modulemap.
415+
static Optional<StringRef>
416+
getGlibcModuleMapPath(StringRef resourceDir, llvm::Triple triple,
417+
SmallVectorImpl<char> &scratch) {
418+
if (resourceDir.empty())
419+
return None;
420+
421+
scratch.append(resourceDir.begin(), resourceDir.end());
422+
llvm::sys::path::append(
423+
scratch,
424+
swift::getPlatformNameForTriple(triple),
425+
swift::getMajorArchitectureName(triple),
426+
"glibc.modulemap");
427+
428+
// Only specify the module map if that file actually exists.
429+
// It may not--for example in the case that
430+
// `swiftc -target x86_64-unknown-linux-gnu -emit-ir` is invoked using
431+
// a Swift compiler not built for Linux targets.
432+
if (llvm::sys::fs::exists(scratch)) {
433+
return StringRef(scratch.data(), scratch.size());
434+
} else {
435+
return None;
436+
}
437+
}
438+
410439
static void
411440
getNormalInvocationArguments(std::vector<std::string> &invocationArgStrs,
412441
ASTContext &ctx,
@@ -571,28 +600,10 @@ getNormalInvocationArguments(std::vector<std::string> &invocationArgStrs,
571600
}
572601
}
573602

574-
// The module map used for Glibc depends on the target we're compiling for,
575-
// and is not included in the resource directory with the other implicit
576-
// module maps. It's at {freebsd|linux}/{arch}/glibc.modulemap.
577603
SmallString<128> GlibcModuleMapPath;
578-
GlibcModuleMapPath = searchPathOpts.RuntimeResourcePath;
579-
580-
// Running without a resource directory is not a supported configuration.
581-
assert(!GlibcModuleMapPath.empty());
582-
583-
llvm::sys::path::append(
584-
GlibcModuleMapPath,
585-
swift::getPlatformNameForTriple(triple),
586-
swift::getMajorArchitectureName(triple),
587-
"glibc.modulemap");
588-
589-
// Only specify the module map if that file actually exists.
590-
// It may not--for example in the case that
591-
// `swiftc -target x86_64-unknown-linux-gnu -emit-ir` is invoked using
592-
// a Swift compiler not built for Linux targets.
593-
if (llvm::sys::fs::exists(GlibcModuleMapPath)) {
594-
invocationArgStrs.push_back(
595-
(Twine("-fmodule-map-file=") + GlibcModuleMapPath).str());
604+
if (auto path = getGlibcModuleMapPath(searchPathOpts.RuntimeResourcePath,
605+
triple, GlibcModuleMapPath)) {
606+
invocationArgStrs.push_back((Twine("-fmodule-map-file=") + *path).str());
596607
} else {
597608
// FIXME: Emit a warning of some kind.
598609
}

lib/Frontend/ParseableInterfaceModuleLoader.cpp

Lines changed: 84 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -173,30 +173,40 @@ class DiscoveredModule {
173173
/// The kind of module that's been discovered.
174174
const Kind kind;
175175

176-
DiscoveredModule(StringRef path, Kind kind): kind(kind), path(path) {}
176+
DiscoveredModule(StringRef path, Kind kind,
177+
std::unique_ptr<llvm::MemoryBuffer> moduleBuffer)
178+
: kind(kind), moduleBuffer(std::move(moduleBuffer)), path(path) {}
179+
177180
public:
181+
/// The contents of the .swiftmodule, if we've read it while validating
182+
/// dependencies.
183+
std::unique_ptr<llvm::MemoryBuffer> moduleBuffer;
184+
178185
/// The path to the discovered serialized .swiftmodule on disk.
179186
const std::string path;
180187

181188
/// Creates a \c Normal discovered module.
182-
static DiscoveredModule normal(StringRef path) {
183-
return { path, Kind::Normal };
189+
static DiscoveredModule normal(StringRef path,
190+
std::unique_ptr<llvm::MemoryBuffer> moduleBuffer) {
191+
return { path, Kind::Normal, std::move(moduleBuffer) };
184192
}
185193

186194
/// Creates a \c Prebuilt discovered module.
187-
static DiscoveredModule prebuilt(StringRef path) {
188-
return { path, Kind::Prebuilt };
195+
static DiscoveredModule prebuilt(
196+
StringRef path, std::unique_ptr<llvm::MemoryBuffer> moduleBuffer) {
197+
return { path, Kind::Prebuilt, std::move(moduleBuffer) };
189198
}
190199

191200
/// Creates a \c Forwarded discovered module, whose dependencies have been
192201
/// externally validated by a \c ForwardingModule.
193-
static DiscoveredModule forwarded(StringRef path) {
194-
return { path, Kind::Forwarded };
202+
static DiscoveredModule forwarded(
203+
StringRef path, std::unique_ptr<llvm::MemoryBuffer> moduleBuffer) {
204+
return { path, Kind::Forwarded, std::move(moduleBuffer) };
195205
}
196206

197207
bool isNormal() const { return kind == Kind::Normal; }
198208
bool isPrebuilt() const { return kind == Kind::Prebuilt; }
199-
bool isForwarding() const { return kind == Kind::Forwarded; }
209+
bool isForwarded() const { return kind == Kind::Forwarded; }
200210
};
201211

202212
} // end anonymous namespace
@@ -453,7 +463,8 @@ class swift::ParseableInterfaceBuilder {
453463
return subInvocation;
454464
}
455465

456-
bool buildSwiftModule(StringRef OutPath, bool ShouldSerializeDeps) {
466+
bool buildSwiftModule(StringRef OutPath, bool ShouldSerializeDeps,
467+
std::unique_ptr<llvm::MemoryBuffer> *ModuleBuffer) {
457468
bool SubError = false;
458469
bool RunSuccess = llvm::CrashRecoveryContext().RunSafelyOnThread([&] {
459470
// Note that we don't assume cachePath is the same as the Clang
@@ -559,7 +570,10 @@ class swift::ParseableInterfaceBuilder {
559570
if (ShouldSerializeDeps)
560571
SerializationOpts.Dependencies = Deps;
561572
SILMod->setSerializeSILAction([&]() {
562-
serialize(Mod, SerializationOpts, SILMod.get());
573+
// We don't want to serialize module docs in the cache -- they
574+
// will be serialized beside the interface file.
575+
serializeToBuffers(Mod, SerializationOpts, ModuleBuffer,
576+
/*ModuleDocBuffer*/nullptr, SILMod.get());
563577
});
564578

565579
LLVM_DEBUG(llvm::dbgs() << "Running SIL processing passes\n");
@@ -717,22 +731,23 @@ class ParseableInterfaceModuleLoaderImpl {
717731

718732
// Check that the output .swiftmodule file is at least as new as all the
719733
// dependencies it read when it was built last time.
720-
bool swiftModuleIsUpToDate(StringRef modulePath,
721-
SmallVectorImpl<FileDependency> &AllDeps) {
734+
bool swiftModuleIsUpToDate(
735+
StringRef modulePath, SmallVectorImpl<FileDependency> &AllDeps,
736+
std::unique_ptr<llvm::MemoryBuffer> &moduleBuffer) {
722737
auto OutBuf = fs.getBufferForFile(modulePath);
723738
if (!OutBuf)
724739
return false;
725-
return serializedASTBufferIsUpToDate(*OutBuf.get(), AllDeps);
740+
moduleBuffer = std::move(*OutBuf);
741+
return serializedASTBufferIsUpToDate(*moduleBuffer, AllDeps);
726742
}
727743

728744
// Check that a "forwarding" .swiftmodule file is at least as new as all the
729745
// dependencies it read when it was built last time. Requires that the
730746
// forwarding module has been loaded from disk.
731-
bool forwardingModuleIsUpToDate(const ForwardingModule &fwd,
732-
SmallVectorImpl<FileDependency> &deps) {
747+
bool forwardingModuleIsUpToDate(
748+
const ForwardingModule &fwd, SmallVectorImpl<FileDependency> &deps,
749+
std::unique_ptr<llvm::MemoryBuffer> &moduleBuffer) {
733750
// First, make sure the underlying module path exists and is valid.
734-
// FIXME: We should preserve this buffer, rather than opening it again
735-
// when loading the module.
736751
auto modBuf = fs.getBufferForFile(fwd.underlyingModulePath);
737752
if (!modBuf || !serializedASTLooksValid(*modBuf.get()))
738753
return false;
@@ -743,7 +758,11 @@ class ParseableInterfaceModuleLoaderImpl {
743758
FileDependency::modTimeBased(
744759
dep.path, dep.size, dep.lastModificationTime));
745760
}
746-
return dependenciesAreUpToDate(deps);
761+
if (!dependenciesAreUpToDate(deps))
762+
return false;
763+
764+
moduleBuffer = std::move(*modBuf);
765+
return true;
747766
}
748767

749768
Optional<StringRef>
@@ -809,22 +828,25 @@ class ParseableInterfaceModuleLoaderImpl {
809828
// First, check the cached module path. Whatever's in this cache represents
810829
// the most up-to-date knowledge we have about the module.
811830
if (auto cachedBufOrError = fs.getBufferForFile(cachedOutputPath)) {
812-
auto &buf = *cachedBufOrError.get();
831+
auto buf = std::move(*cachedBufOrError);
813832

814833
// Check to see if the module is a serialized AST. If it's not, then we're
815834
// probably dealing with a Forwarding Module, which is a YAML file.
816835
bool isForwardingModule =
817-
!serialization::isSerializedAST(buf.getBuffer());
836+
!serialization::isSerializedAST(buf->getBuffer());
818837

819838
// If it's a forwarding module, load the YAML file from disk and check
820839
// if it's up-to-date.
821840
if (isForwardingModule) {
822-
auto modOrErr = ForwardingModule::load(buf);
823-
if (modOrErr && forwardingModuleIsUpToDate(*modOrErr, deps))
824-
return DiscoveredModule::forwarded(modOrErr->underlyingModulePath);
841+
if (auto forwardingModule = ForwardingModule::load(*buf)) {
842+
std::unique_ptr<llvm::MemoryBuffer> moduleBuffer;
843+
if (forwardingModuleIsUpToDate(*forwardingModule, deps, moduleBuffer))
844+
return DiscoveredModule::forwarded(
845+
forwardingModule->underlyingModulePath, std::move(moduleBuffer));
846+
}
825847
// Otherwise, check if the AST buffer itself is up to date.
826-
} else if (serializedASTBufferIsUpToDate(buf, deps)) {
827-
return DiscoveredModule::normal(cachedOutputPath);
848+
} else if (serializedASTBufferIsUpToDate(*buf, deps)) {
849+
return DiscoveredModule::normal(cachedOutputPath, std::move(buf));
828850
}
829851
}
830852

@@ -835,9 +857,10 @@ class ParseableInterfaceModuleLoaderImpl {
835857
// from the SDK.
836858
if (!prebuiltCacheDir.empty()) {
837859
llvm::SmallString<256> scratch;
860+
std::unique_ptr<llvm::MemoryBuffer> moduleBuffer;
838861
auto path = computePrebuiltModulePath(scratch);
839-
if (path && swiftModuleIsUpToDate(*path, deps))
840-
return DiscoveredModule::prebuilt(*path);
862+
if (path && swiftModuleIsUpToDate(*path, deps, moduleBuffer))
863+
return DiscoveredModule::prebuilt(*path, std::move(moduleBuffer));
841864
}
842865

843866
// Finally, if there's a module adjacent to the .swiftinterface that we can
@@ -899,10 +922,12 @@ class ParseableInterfaceModuleLoaderImpl {
899922
});
900923
}
901924

902-
/// Looks up the best module to load for a given interface. See the main
903-
/// comment in \c ParseableInterfaceModuleLoader.h for an explanation of
904-
/// the module loading strategy.
905-
llvm::ErrorOr<std::string> findOrBuildLoadableModule() {
925+
/// Looks up the best module to load for a given interface, and returns a
926+
/// buffer of the module's contents. See the main comment in
927+
/// \c ParseableInterfaceModuleLoader.h for an explanation of the module
928+
/// loading strategy.
929+
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
930+
findOrBuildLoadableModule() {
906931

907932
// Set up a builder if we need to build the module. It'll also set up
908933
// the subinvocation we'll need to use to compute the cache paths.
@@ -927,24 +952,27 @@ class ParseableInterfaceModuleLoaderImpl {
927952
moduleOrErr.getError() != std::errc::no_such_file_or_directory)
928953
return moduleOrErr.getError();
929954

930-
// We discovered a module! Return the module's path so we know what to load.
955+
// We discovered a module! Return that module's buffer so we can load it.
931956
if (moduleOrErr) {
932-
auto &module = *moduleOrErr;
957+
auto module = std::move(moduleOrErr.get());
958+
933959
// If it's prebuilt, use this time to generate a forwarding module.
934960
if (module.isPrebuilt())
935961
if (writeForwardingModule(module, cachedOutputPath, allDeps))
936962
return std::make_error_code(std::errc::not_supported);
937963

938-
// FIXME: return and load module buffer directly
939-
return module.path;
964+
return std::move(module.moduleBuffer);
940965
}
941966

967+
std::unique_ptr<llvm::MemoryBuffer> moduleBuffer;
942968
// We didn't discover a module corresponding to this interface. Build one.
943-
if (builder.buildSwiftModule(cachedOutputPath, /*shouldSerializeDeps*/true))
969+
if (builder.buildSwiftModule(cachedOutputPath, /*shouldSerializeDeps*/true,
970+
&moduleBuffer))
944971
return std::make_error_code(std::errc::invalid_argument);
945972

946-
// FIXME: return and load module buffer directly
947-
return cachedOutputPath.str();
973+
assert(moduleBuffer &&
974+
"failed to write module buffer but returned success?");
975+
return std::move(moduleBuffer);
948976
}
949977
};
950978

@@ -984,26 +1012,24 @@ std::error_code ParseableInterfaceModuleLoader::findModuleFilesInDirectory(
9841012

9851013
// Ask the impl to find us a module that we can load or give us an error
9861014
// telling us that we couldn't load it.
987-
auto PathOrErr = Impl.findOrBuildLoadableModule();
988-
if (!PathOrErr)
989-
return PathOrErr.getError();
990-
std::string FinalPath = std::move(*PathOrErr);
991-
992-
// Finish off by delegating back up to the SerializedModuleLoaderBase
993-
// routine that can load the recently-manufactured serialized module.
994-
LLVM_DEBUG(llvm::dbgs() << "Loading " << FinalPath
995-
<< " via normal module loader\n");
1015+
auto ModuleBufferOrErr = Impl.findOrBuildLoadableModule();
1016+
if (!ModuleBufferOrErr)
1017+
return ModuleBufferOrErr.getError();
1018+
1019+
if (ModuleBuffer) {
1020+
*ModuleBuffer = std::move(*ModuleBufferOrErr);
1021+
}
1022+
1023+
// Delegate back to the serialized module loader to load the module doc.
9961024
llvm::SmallString<256> DocPath{DirPath};
9971025
path::append(DocPath, ModuleDocFilename);
998-
auto ErrorCode = SerializedModuleLoaderBase::openModuleFiles(
999-
ModuleID, FinalPath, DocPath, ModuleBuffer, ModuleDocBuffer);
1000-
LLVM_DEBUG(llvm::dbgs() << "Loaded " << FinalPath
1001-
<< " via normal module loader");
1002-
if (ErrorCode) {
1003-
LLVM_DEBUG(llvm::dbgs() << " with error: " << ErrorCode.message());
1004-
}
1005-
LLVM_DEBUG(llvm::dbgs() << "\n");
1006-
return ErrorCode;
1026+
auto DocLoadErr =
1027+
SerializedModuleLoaderBase::openModuleDocFile(ModuleID, DocPath,
1028+
ModuleDocBuffer);
1029+
if (DocLoadErr)
1030+
return DocLoadErr;
1031+
1032+
return std::error_code();
10071033
}
10081034

10091035

@@ -1018,5 +1044,6 @@ bool ParseableInterfaceModuleLoader::buildSwiftModuleFromSwiftInterface(
10181044
// make them relocatable (SDK-relative) if we want to ship the built
10191045
// swiftmodules to another machine. Just track them as absolute paths
10201046
// for now, so we can test the dependency tracking locally.
1021-
return builder.buildSwiftModule(OutPath, /*shouldSerializeDeps*/true);
1047+
return builder.buildSwiftModule(OutPath, /*shouldSerializeDeps*/true,
1048+
/*ModuleBuffer*/nullptr);
10221049
}

0 commit comments

Comments
 (0)