Skip to content

Commit

Permalink
[ModuleInterface] Switch from mtime to xxhash in FILE_DEPENDENCY reco…
Browse files Browse the repository at this point in the history
…rds.
  • Loading branch information
graydon committed Nov 8, 2018
1 parent d389d24 commit 47e6e5a
Show file tree
Hide file tree
Showing 7 changed files with 49 additions and 24 deletions.
5 changes: 3 additions & 2 deletions include/swift/Serialization/ModuleFormat.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0;
/// describe what change you made. The content of this comment isn't important;
/// it just ensures a conflict if two people change the module format.
/// Don't worry about adhering to the 80-column limit for this line.
const uint16_t SWIFTMODULE_VERSION_MINOR = 462; // Last change: Add dynamicReplacement(for:)
const uint16_t SWIFTMODULE_VERSION_MINOR = 463; // Last change: switch FILE_DEPENDENCY records to hashes.

using DeclIDField = BCFixed<31>;

Expand Down Expand Up @@ -108,6 +108,7 @@ using CharOffsetField = BitOffsetField;

using FileSizeField = BCVBR<16>;
using FileModTimeField = BCVBR<16>;
using FileHashField = BCVBR<16>;

// These IDs must \em not be renumbered or reordered without incrementing
// the module version.
Expand Down Expand Up @@ -654,7 +655,7 @@ namespace input_block {
IMPORTED_HEADER,
BCFixed<1>, // exported?
FileSizeField, // file size (for validation)
FileModTimeField, // file mtime (for validation)
FileHashField, // file hash (for validation)
BCBlob // file path
>;

Expand Down
2 changes: 1 addition & 1 deletion include/swift/Serialization/SerializationOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ namespace swift {

struct FileDependency {
uint64_t Size;
llvm::sys::TimePoint<> LastModTime;
uint64_t Hash;
StringRef Path;
};
ArrayRef<FileDependency> Dependencies;
Expand Down
42 changes: 33 additions & 9 deletions lib/Frontend/ParseableInterfaceSupport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "clang/Lex/Preprocessor.h"
#include "clang/Lex/HeaderSearch.h"
#include "llvm/ADT/Hashing.h"
#include "llvm/Support/xxhash.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/CrashRecoveryContext.h"
Expand Down Expand Up @@ -72,6 +73,22 @@ extractSwiftInterfaceVersionAndArgs(DiagnosticEngine &Diags,
return false;
}

static bool
getHashOfFile(clang::vfs::FileSystem &FS,
StringRef Path, uint64_t &HashOut,
DiagnosticEngine &Diags) {
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> Buf =
FS.getBufferForFile(Path, /*FileSize=*/-1,
/*RequiresNullTerminator=*/false);
if (!Buf) {
Diags.diagnose(SourceLoc(), diag::cannot_open_file, Path,
Buf.getError().message());
return true;
}
HashOut = xxHash64(Buf.get()->getBuffer());
return false;
}

/// Construct a cache key for the .swiftmodule being generated. There is a
/// balance to be struck here between things that go in the cache key and
/// things that go in the "up to date" check of the cache entry. We want to
Expand All @@ -82,9 +99,9 @@ extractSwiftInterfaceVersionAndArgs(DiagnosticEngine &Diags,
/// -- rather than making a new one and potentially filling up the cache
/// with dead entries -- when other factors change, such as the contents of
/// the .swiftinterface input or its dependencies.
std::string getCacheHash(ASTContext &Ctx,
CompilerInvocation &SubInvocation,
StringRef InPath) {
static std::string getCacheHash(ASTContext &Ctx,
CompilerInvocation &SubInvocation,
StringRef InPath) {
// Start with the compiler version (which will be either tag names or revs).
std::string vers = swift::version::getSwiftFullVersion(
Ctx.LangOpts.EffectiveLanguageVersion);
Expand Down Expand Up @@ -153,7 +170,8 @@ ParseableInterfaceModuleLoader::configureSubInvocationAndOutputPaths(
static bool
swiftModuleIsUpToDate(clang::vfs::FileSystem &FS,
StringRef ModuleCachePath,
StringRef OutPath) {
StringRef OutPath,
DiagnosticEngine &Diags) {

if (!FS.exists(OutPath))
return false;
Expand All @@ -176,10 +194,12 @@ swiftModuleIsUpToDate(clang::vfs::FileSystem &FS,
return false;

for (auto In : AllDeps) {
uint64_t Hash = 0;
auto InStatus = FS.status(In.Path);
if (!InStatus ||
(InStatus.get().getSize() != In.Size) ||
(InStatus.get().getLastModificationTime() != In.LastModTime)) {
getHashOfFile(FS, In.Path, Hash, Diags) ||
(Hash != In.Hash)) {
LLVM_DEBUG(llvm::dbgs() << "Dep " << In.Path
<< " is directly out of date\n");
return false;
Expand All @@ -189,7 +209,7 @@ swiftModuleIsUpToDate(clang::vfs::FileSystem &FS,
auto Ty = file_types::lookupTypeForExtension(Ext);
if (Ty == file_types::TY_SwiftModuleFile &&
In.Path.startswith(ModuleCachePath) &&
!swiftModuleIsUpToDate(FS, ModuleCachePath, In.Path)) {
!swiftModuleIsUpToDate(FS, ModuleCachePath, In.Path, Diags)) {
LLVM_DEBUG(llvm::dbgs() << "Dep " << In.Path
<< " is indirectly out of date\n");
return false;
Expand Down Expand Up @@ -275,9 +295,13 @@ static bool buildSwiftModuleFromSwiftInterface(
SubError = true;
return;
}
uint64_t Hash = 0;
if (getHashOfFile(FS, Dep, Hash, Diags)) {
SubError = true;
return;
}
Deps.push_back(SerializationOptions::FileDependency{
DepStatus.get().getSize(), DepStatus.get().getLastModificationTime(),
Dep});
DepStatus.get().getSize(), Hash, Dep});
}
serializationOpts.Dependencies = Deps;
SILMod->setSerializeSILAction([&]() {
Expand Down Expand Up @@ -316,7 +340,7 @@ std::error_code ParseableInterfaceModuleLoader::openModuleFiles(
configureSubInvocationAndOutputPaths(SubInvocation, InPath, OutPath);

// Evaluate if we need to run this sub-invocation, and if so run it.
if (!swiftModuleIsUpToDate(FS, CacheDir, OutPath)) {
if (!swiftModuleIsUpToDate(FS, CacheDir, OutPath, Diags)) {
if (buildSwiftModuleFromSwiftInterface(FS, Diags, SubInvocation, InPath,
OutPath))
return std::make_error_code(std::errc::invalid_argument);
Expand Down
2 changes: 1 addition & 1 deletion lib/Serialization/ModuleFile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ static bool validateInputBlock(
switch (kind) {
case input_block::FILE_DEPENDENCY:
dependencies.push_back(SerializationOptions::FileDependency{
scratch[0], llvm::sys::toTimePoint(scratch[1]), blobData});
scratch[0], scratch[1], blobData});
break;
default:
// Unknown metadata record, possibly for use by a future version of the
Expand Down
4 changes: 1 addition & 3 deletions lib/Serialization/Serialization.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1042,9 +1042,7 @@ void Serializer::writeInputBlock(const SerializationOptions &options) {
}

for (auto const &dep : options.Dependencies) {
FileDependency.emit(ScratchRecord, dep.Size,
llvm::sys::toTimeT(dep.LastModTime),
dep.Path);
FileDependency.emit(ScratchRecord, dep.Size, dep.Hash, dep.Path);
}

SmallVector<ModuleDecl::ImportedModule, 8> allImports;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
// Setup builds a module TestModule that depends on OtherModule and LeafModule (built from other.swift and leaf.swift).
// During setup, input and intermediate mtimes are all set to a constant 'old' time (long in the past).
//
// We then modify OtherModule.swiftinterface, and check only OtherModule-*.swiftmodule has a new mtime, LeafModule is unchanged.
// We then modify OtherModule.swiftinterface's content (but not size), and check only OtherModule-*.swiftmodule has a new mtime, LeafModule is unchanged.
//
//
// Setup phase 1: Write input files.
Expand All @@ -13,6 +13,7 @@
//
// RUN: echo 'import LeafModule' >%t/other.swift
// RUN: echo 'public func OtherFunc() -> Int { return LeafFunc(); }' >>%t/other.swift
// RUN: echo 'public func OtherFunc2() -> Int { return LeafFunc(); }' >>%t/other.swift
//
//
// Setup phase 2: build modules, pushing timestamps of inputs and intermediates into the past as we go.
Expand All @@ -26,12 +27,12 @@
// RUN: %S/Inputs/make-old.py %t/modulecache/OtherModule-*.swiftmodule
//
//
// Actual test: make OtherModule.swiftinterface newer, check we only rebuild its cached module.
// Actual test: Change a byte in OtherModule.swiftinterface, check we only rebuild its cached module.
//
// RUN: %S/Inputs/check-is-old.py %t/OtherModule.swiftinterface %t/LeafModule.swiftinterface
// RUN: %S/Inputs/check-is-old.py %t/modulecache/OtherModule-*.swiftmodule %t/modulecache/LeafModule-*.swiftmodule
// RUN: touch %t/OtherModule.swiftinterface
// RUN: %S/Inputs/check-is-new.py %t/OtherModule.swiftinterface
// RUN: sed -e 's/OtherFunc2/OtterFunc2/' -i.prev %t/OtherModule.swiftinterface
// RUN: %S/Inputs/make-old.py %t/OtherModule.swiftinterface
// RUN: rm %t/TestModule.swiftmodule
// RUN: %target-swift-frontend -I %t -module-cache-path %t/modulecache -enable-parseable-module-interface -emit-module -o %t/TestModule.swiftmodule -module-name TestModule %s
// RUN: %S/Inputs/check-is-new.py %t/modulecache/OtherModule-*.swiftmodule
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@
// Setup builds a module TestModule that depends on OtherModule and LeafModule (built from other.swift and leaf.swift).
// During setup, input and intermediate mtimes are all set to a constant 'old' time (long in the past).
//
// We then modify LeafModule.swiftinterface, and check both cached modules have new mtimes.
// We then modify LeafModule.swiftinterface's content (but not size), and check both cached modules have new mtimes.
//
//
// Setup phase 1: Write input files.
//
// RUN: echo 'public func LeafFunc() -> Int { return 10; }' >%t/leaf.swift
// RUN: echo 'public func LeafFunc2() -> Int { return 11; }' >>%t/leaf.swift
//
// RUN: echo 'import LeafModule' >%t/other.swift
// RUN: echo 'public func OtherFunc() -> Int { return LeafFunc(); }' >>%t/other.swift
Expand All @@ -26,12 +27,12 @@
// RUN: %S/Inputs/make-old.py %t/modulecache/OtherModule-*.swiftmodule
//
//
// Actual test: make LeafModule.swiftinterface newer, check both cached modules get rebuilt.
// Actual test: Change a byte in LeafModule.swiftinterface, check both cached modules get rebuilt.
//
// RUN: %S/Inputs/check-is-old.py %t/OtherModule.swiftinterface %t/LeafModule.swiftinterface
// RUN: %S/Inputs/check-is-old.py %t/modulecache/OtherModule-*.swiftmodule %t/modulecache/LeafModule-*.swiftmodule
// RUN: touch %t/LeafModule.swiftinterface
// RUN: %S/Inputs/check-is-new.py %t/LeafModule.swiftinterface
// RUN: sed -e 's/LeafFunc2/LoafFunc2/' -i.prev %t/LeafModule.swiftinterface
// RUN: %S/Inputs/make-old.py %t/LeafModule.swiftinterface
// RUN: rm %t/TestModule.swiftmodule
// RUN: %target-swift-frontend -I %t -module-cache-path %t/modulecache -enable-parseable-module-interface -emit-module -o %t/TestModule.swiftmodule -module-name TestModule %s
// RUN: %S/Inputs/check-is-new.py %t/modulecache/OtherModule-*.swiftmodule %t/modulecache/LeafModule-*.swiftmodule
Expand Down

0 comments on commit 47e6e5a

Please sign in to comment.