Virtualize swift compiler outputs (#63206)

Using a virutal output backend to capture all the outputs from
swift-frontend invocation. This allows redirecting and/or mirroring
compiler outputs to multiple location using different OutputBackend.

As an example usage for the virtual outputs, teach swift compiler to
check its output determinism by running the compiler invocation
twice and compare the hash of all its outputs.

Virtual output will be used to enable caching in the future.
This commit is contained in:
Steven Wu
2023-04-05 23:34:37 +08:00
committed by GitHub
parent 829491b230
commit 09b8af86fb
52 changed files with 690 additions and 327 deletions

View File

@@ -40,6 +40,7 @@
#include "llvm/Support/Debug.h"
#include "llvm/Support/Errc.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/VirtualOutputBackend.h"
#include "llvm/Support/YAMLParser.h"
#include "llvm/Support/YAMLTraits.h"
#include "llvm/Support/xxhash.h"
@@ -901,8 +902,8 @@ class ModuleInterfaceLoaderImpl {
/// this. If the write was successful, it also updates the
/// list of dependencies to match what was written to the forwarding file.
bool writeForwardingModuleAndUpdateDeps(
const DiscoveredModule &mod, StringRef outputPath,
SmallVectorImpl<FileDependency> &deps) {
const DiscoveredModule &mod, llvm::vfs::OutputBackend &backend,
StringRef outputPath, SmallVectorImpl<FileDependency> &deps) {
assert(mod.isPrebuilt() &&
"cannot write forwarding file for non-prebuilt module");
ForwardingModule fwd(mod.path);
@@ -938,16 +939,12 @@ class ModuleInterfaceLoaderImpl {
depsAdjustedToMTime.push_back(adjustedDep);
}
// Create the module cache if we haven't created it yet.
StringRef parentDir = path::parent_path(outputPath);
(void)llvm::sys::fs::create_directories(parentDir);
auto hadError = withOutputFile(diags, outputPath,
[&](llvm::raw_pwrite_stream &out) {
llvm::yaml::Output yamlWriter(out);
yamlWriter << fwd;
return false;
});
auto hadError = withOutputPath(diags, backend, outputPath,
[&](llvm::raw_pwrite_stream &out) {
llvm::yaml::Output yamlWriter(out);
yamlWriter << fwd;
return false;
});
if (hadError)
return true;
@@ -1006,8 +1003,8 @@ class ModuleInterfaceLoaderImpl {
// If it's prebuilt, use this time to generate a forwarding module and
// update the dependencies to use modification times.
if (module.isPrebuilt())
if (writeForwardingModuleAndUpdateDeps(module, cachedOutputPath,
allDeps))
if (writeForwardingModuleAndUpdateDeps(module, ctx.getOutputBackend(),
cachedOutputPath, allDeps))
return std::make_error_code(std::errc::not_supported);
// Report the module's dependencies to the dependencyTracker
@@ -1251,7 +1248,8 @@ ModuleInterfaceCheckerImpl::getCompiledModuleCandidatesForInterface(StringRef mo
bool ModuleInterfaceCheckerImpl::tryEmitForwardingModule(
StringRef moduleName, StringRef interfacePath,
ArrayRef<std::string> candidates, StringRef outputPath) {
ArrayRef<std::string> candidates, llvm::vfs::OutputBackend &backend,
StringRef outputPath) {
// Derive .swiftmodule path from the .swiftinterface path.
auto newExt = file_types::getExtension(file_types::TY_SwiftModuleFile);
llvm::SmallString<32> modulePath = interfacePath;
@@ -1269,12 +1267,12 @@ bool ModuleInterfaceCheckerImpl::tryEmitForwardingModule(
deps, moduleBuffer)) {
// If so, emit a forwarding module to the candidate.
ForwardingModule FM(mod);
auto hadError = withOutputFile(Ctx.Diags, outputPath,
[&](llvm::raw_pwrite_stream &out) {
llvm::yaml::Output yamlWriter(out);
yamlWriter << FM;
return false;
});
auto hadError = withOutputPath(Ctx.Diags, backend, outputPath,
[&](llvm::raw_pwrite_stream &out) {
llvm::yaml::Output yamlWriter(out);
yamlWriter << FM;
return false;
});
if (!hadError)
return true;
}
@@ -1402,19 +1400,25 @@ bool ModuleInterfaceLoader::buildExplicitSwiftModuleFromSwiftInterface(
ArrayRef<std::string> CompiledCandidates,
DependencyTracker *tracker) {
// First, check if the expected output already exists and possibly up-to-date w.r.t.
// all of the dependencies it was built with. If so, early exit.
UpToDateModuleCheker checker(Instance.getASTContext(),
RequireOSSAModules_t(Instance.getSILOptions()));
ModuleRebuildInfo rebuildInfo;
SmallVector<FileDependency, 3> allDeps;
std::unique_ptr<llvm::MemoryBuffer> moduleBuffer;
if (checker.swiftModuleIsUpToDate(outputPath, rebuildInfo, allDeps, moduleBuffer)) {
if (Instance.getASTContext().LangOpts.EnableSkipExplicitInterfaceModuleBuildRemarks) {
Instance.getDiags().diagnose(SourceLoc(),
diag::explicit_interface_build_skipped, outputPath);
if (!Instance.getInvocation().getIRGenOptions().AlwaysCompile) {
// First, check if the expected output already exists and possibly
// up-to-date w.r.t. all of the dependencies it was built with. If so, early
// exit.
UpToDateModuleCheker checker(
Instance.getASTContext(),
RequireOSSAModules_t(Instance.getSILOptions()));
ModuleRebuildInfo rebuildInfo;
SmallVector<FileDependency, 3> allDeps;
std::unique_ptr<llvm::MemoryBuffer> moduleBuffer;
if (checker.swiftModuleIsUpToDate(outputPath, rebuildInfo, allDeps,
moduleBuffer)) {
if (Instance.getASTContext()
.LangOpts.EnableSkipExplicitInterfaceModuleBuildRemarks) {
Instance.getDiags().diagnose(
SourceLoc(), diag::explicit_interface_build_skipped, outputPath);
}
return false;
}
return false;
}
// Read out the compiler version.