diff --git a/clang/include/clang/Basic/ObjCRuntime.h b/clang/include/clang/Basic/ObjCRuntime.h --- a/clang/include/clang/Basic/ObjCRuntime.h +++ b/clang/include/clang/Basic/ObjCRuntime.h @@ -484,6 +484,10 @@ raw_ostream &operator<<(raw_ostream &out, const ObjCRuntime &value); +template void updateHash(HashT &Hash, const ObjCRuntime &OCR) { + Hash.update(OCR.getKind()); + Hash.update(OCR.getVersion()); +} } // namespace clang #endif // LLVM_CLANG_BASIC_OBJCRUNTIME_H diff --git a/clang/include/clang/Basic/Sanitizers.h b/clang/include/clang/Basic/Sanitizers.h --- a/clang/include/clang/Basic/Sanitizers.h +++ b/clang/include/clang/Basic/Sanitizers.h @@ -78,6 +78,10 @@ llvm::hash_code hash_value() const; + template void updateHash(HashT &Hash) const { + Hash.updateRange(&maskLoToHigh[0], &maskLoToHigh[0] + kNumElem); + } + constexpr explicit operator bool() const { return maskLoToHigh[0] || maskLoToHigh[1]; } @@ -124,6 +128,10 @@ // Declaring in clang namespace so that it can be found by ADL. llvm::hash_code hash_value(const clang::SanitizerMask &Arg); +template +void updateHash(HashT &Hash, const clang::SanitizerMask &Arg) { + Arg.updateHash(Hash); +} // Define the set of sanitizer kinds, as well as the set of sanitizers each // sanitizer group expands into. diff --git a/clang/include/clang/Lex/HeaderSearchOptions.h b/clang/include/clang/Lex/HeaderSearchOptions.h --- a/clang/include/clang/Lex/HeaderSearchOptions.h +++ b/clang/include/clang/Lex/HeaderSearchOptions.h @@ -256,11 +256,25 @@ return llvm::hash_combine(E.Path, E.Group, E.IsFramework, E.IgnoreSysRoot); } +template +void updateHash(HashT &Hash, const HeaderSearchOptions::Entry &E) { + Hash.update(E.Path); + Hash.update(E.Group); + Hash.update(E.IsFramework); + Hash.update(E.IgnoreSysRoot); +} + inline llvm::hash_code hash_value(const HeaderSearchOptions::SystemHeaderPrefix &SHP) { return llvm::hash_combine(SHP.Prefix, SHP.IsSystemHeader); } +template +void updateHash(HashT &Hash, + const HeaderSearchOptions::SystemHeaderPrefix &SHP) { + Hash.update(SHP.Prefix); + Hash.update(SHP.IsSystemHeader); +} } // namespace clang #endif // LLVM_CLANG_LEX_HEADERSEARCHOPTIONS_H diff --git a/clang/include/clang/Serialization/ModuleFileExtension.h b/clang/include/clang/Serialization/ModuleFileExtension.h --- a/clang/include/clang/Serialization/ModuleFileExtension.h +++ b/clang/include/clang/Serialization/ModuleFileExtension.h @@ -9,6 +9,7 @@ #ifndef LLVM_CLANG_SERIALIZATION_MODULEFILEEXTENSION_H #define LLVM_CLANG_SERIALIZATION_MODULEFILEEXTENSION_H +#include "clang/Serialization/ModuleHash.h" #include "llvm/ADT/IntrusiveRefCntPtr.h" #include "llvm/Support/ExtensibleRTTI.h" #include @@ -74,19 +75,19 @@ virtual ModuleFileExtensionMetadata getExtensionMetadata() const = 0; /// Hash information about the presence of this extension into the - /// module hash code. + /// module hash. /// - /// The module hash code is used to distinguish different variants + /// The module hash is used to distinguish different variants /// of a module that are incompatible. If the presence, absence, or /// version of the module file extension should force the creation /// of a separate set of module files, override this method to /// combine that distinguishing information into the module hash /// code. /// - /// The default implementation of this function simply returns the - /// hash code as given, so the presence/absence of this extension - /// does not distinguish module files. - virtual llvm::hash_code hashExtension(llvm::hash_code c) const; + /// The default implementation of this function does not update the hash in + /// any way. So the presence/absence of this extension does not distinguish + /// module files. + virtual void hashExtension(ModuleHash &Hash) const {} /// Create a new module file extension writer, which will be /// responsible for writing the extension contents into a particular diff --git a/clang/include/clang/Serialization/ModuleHash.h b/clang/include/clang/Serialization/ModuleHash.h new file mode 100644 --- /dev/null +++ b/clang/include/clang/Serialization/ModuleHash.h @@ -0,0 +1,123 @@ +//===-- ModuleHash.h - Module Hash ------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_SERIALIZATION_MODULEHASH_H +#define LLVM_CLANG_SERIALIZATION_MODULEHASH_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/MD5.h" +#include + +namespace module_hash { +namespace detail { +/// Wrapper interface around a hash type. +/// The only methods expected from the hash type are: +/// * a default constructor +/// * void update(llvm::ArrayRef) +/// +/// Various types are forwarded to the canonical version of +/// `HashValueT::update(llvm::ArrayRef)`. Types requiring particular +/// hashing behavior can specify it in a similar way to `llvm::hash_code`, by +/// declaring +/// +/// ``` +/// template +/// void updateHash(HashT &Hash, const ValueType &Value) { +/// Hash.update(Value.member1); +/// Hash.update(Value.member2); +/// } +/// ``` +template class Hash { +private: + template struct update_impl { + update_impl(Hash &H, T Value) { updateHash(H, Value); } + }; + +public: + HashValueT HashValue; + + template void update(T Value) { update_impl(*this, Value); } + + template + void updateRange(InputIteratorT first, InputIteratorT last) { + for (auto It = first; It != last; ++It) + update(*It); + } + +private: + template + struct is_hashable_data + : std::integral_constant::value || + std::is_pointer::value> {}; + +public: + template + typename std::enable_if::value>::type + update(const std::initializer_list &Value) { + update(llvm::ArrayRef(Value.begin(), Value.size())); + } + +private: + // `void HashValueT::update(llvm::ArrayRef)` is the canonical + // expected method to update the hash. + template <> struct update_impl> { + update_impl(Hash &H, llvm::ArrayRef Value) { + H.HashValue.update(Value); + } + }; + + // Plain hashable data is forwarded to `llvm::ArrayRef`. + template + struct update_impl::value>::type> + : update_impl> { + update_impl(Hash &H, T value) + : update_impl>( + H, llvm::ArrayRef(reinterpret_cast(&value), + sizeof(value))) {} + }; + + // Forward `llvm::ArrayRef` to `llvm::ArrayRef`. + template + struct update_impl> : update_impl> { + update_impl(Hash &H, llvm::ArrayRef Value) + : update_impl>( + H, llvm::ArrayRef( + reinterpret_cast(Value.data()), + Value.size() * sizeof(T))) {} + }; + + // Forward `llvm::StringRef` to `llvm::ArrayRef`. + template <> + struct update_impl : update_impl> { + update_impl(Hash &H, llvm::StringRef Value) + : update_impl>( + H, llvm::ArrayRef( + reinterpret_cast(Value.data()), + Value.size())) {} + }; + + // Forward `std::string` to `llvm::StringRef`. + template <> struct update_impl : update_impl { + update_impl(Hash &H, std::string Value) + : update_impl(H, Value) {} + }; +}; +} // namespace detail +} // namespace module_hash + +struct ModuleHash : public module_hash::detail::Hash { + uint64_t getValue() { + llvm::MD5::MD5Result HashResult; + HashValue.final(HashResult); + return HashResult.low(); + } +}; + +#endif // LLVM_CLANG_FRONTEND_MODULEHASH_H diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -45,13 +45,13 @@ #include "clang/Sema/CodeCompleteOptions.h" #include "clang/Serialization/ASTBitCodes.h" #include "clang/Serialization/ModuleFileExtension.h" +#include "clang/Serialization/ModuleHash.h" #include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" #include "llvm/ADT/APInt.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/CachedHashString.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/FloatingPointMode.h" -#include "llvm/ADT/Hashing.h" #include "llvm/ADT/None.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/STLExtras.h" @@ -4474,47 +4474,43 @@ std::string CompilerInvocation::getModuleHash() const { // Note: For QoI reasons, the things we use as a hash here should all be // dumped via the -module-info flag. - using llvm::hash_code; - using llvm::hash_value; - using llvm::hash_combine; - using llvm::hash_combine_range; + ModuleHash Hash; // Start the signature with the compiler version. - // FIXME: We'd rather use something more cryptographically sound than - // CityHash, but this will do for now. - hash_code code = hash_value(getClangFullRepositoryVersion()); + Hash.update(getClangFullRepositoryVersion()); // Also include the serialization version, in case LLVM_APPEND_VC_REV is off // and getClangFullRepositoryVersion() doesn't include git revision. - code = hash_combine(code, serialization::VERSION_MAJOR, - serialization::VERSION_MINOR); + Hash.update({serialization::VERSION_MAJOR, serialization::VERSION_MINOR}); // Extend the signature with the language options -#define LANGOPT(Name, Bits, Default, Description) \ - code = hash_combine(code, LangOpts->Name); -#define ENUM_LANGOPT(Name, Type, Bits, Default, Description) \ - code = hash_combine(code, static_cast(LangOpts->get##Name())); +#define LANGOPT(Name, Bits, Default, Description) Hash.update(LangOpts->Name); +#define ENUM_LANGOPT(Name, Type, Bits, Default, Description) \ + Hash.update(static_cast(LangOpts->get##Name())); #define BENIGN_LANGOPT(Name, Bits, Default, Description) #define BENIGN_ENUM_LANGOPT(Name, Type, Bits, Default, Description) #include "clang/Basic/LangOptions.def" for (StringRef Feature : LangOpts->ModuleFeatures) - code = hash_combine(code, Feature); + Hash.update(Feature); - code = hash_combine(code, LangOpts->ObjCRuntime); + Hash.update(LangOpts->ObjCRuntime); const auto &BCN = LangOpts->CommentOpts.BlockCommandNames; - code = hash_combine(code, hash_combine_range(BCN.begin(), BCN.end())); + Hash.updateRange(BCN.begin(), BCN.end()); // Extend the signature with the target options. - code = hash_combine(code, TargetOpts->Triple, TargetOpts->CPU, - TargetOpts->TuneCPU, TargetOpts->ABI); + Hash.update(TargetOpts->Triple); + Hash.update(TargetOpts->CPU); + Hash.update(TargetOpts->TuneCPU); + Hash.update(TargetOpts->ABI); for (const auto &FeatureAsWritten : TargetOpts->FeaturesAsWritten) - code = hash_combine(code, FeatureAsWritten); + Hash.update(FeatureAsWritten); // Extend the signature with preprocessor options. const PreprocessorOptions &ppOpts = getPreprocessorOpts(); const HeaderSearchOptions &hsOpts = getHeaderSearchOpts(); - code = hash_combine(code, ppOpts.UsePredefines, ppOpts.DetailedRecord); + Hash.update(ppOpts.UsePredefines); + Hash.update(ppOpts.DetailedRecord); for (const auto &I : getPreprocessorOpts().Macros) { // If we're supposed to ignore this macro for the purposes of modules, @@ -4527,61 +4523,61 @@ continue; } - code = hash_combine(code, I.first, I.second); + Hash.update(I.first); + Hash.update(I.second); } // Extend the signature with the sysroot and other header search options. - code = hash_combine(code, hsOpts.Sysroot, - hsOpts.ModuleFormat, - hsOpts.UseDebugInfo, - hsOpts.UseBuiltinIncludes, - hsOpts.UseStandardSystemIncludes, - hsOpts.UseStandardCXXIncludes, - hsOpts.UseLibcxx, - hsOpts.ModulesValidateDiagnosticOptions); - code = hash_combine(code, hsOpts.ResourceDir); + Hash.update(hsOpts.Sysroot); + Hash.update(hsOpts.ModuleFormat); + Hash.update(hsOpts.UseDebugInfo); + Hash.update(hsOpts.UseBuiltinIncludes); + Hash.update(hsOpts.UseStandardSystemIncludes); + Hash.update(hsOpts.UseStandardCXXIncludes); + Hash.update(hsOpts.UseLibcxx); + Hash.update(hsOpts.ModulesValidateDiagnosticOptions); + Hash.update(hsOpts.ResourceDir); if (hsOpts.ModulesStrictContextHash) { - hash_code SHPC = hash_combine_range(hsOpts.SystemHeaderPrefixes.begin(), - hsOpts.SystemHeaderPrefixes.end()); - hash_code UEC = hash_combine_range(hsOpts.UserEntries.begin(), - hsOpts.UserEntries.end()); - code = hash_combine(code, hsOpts.SystemHeaderPrefixes.size(), SHPC, - hsOpts.UserEntries.size(), UEC); + Hash.update(hsOpts.SystemHeaderPrefixes.size()); + Hash.updateRange(hsOpts.SystemHeaderPrefixes.begin(), + hsOpts.SystemHeaderPrefixes.end()); + Hash.update(hsOpts.UserEntries.size()); + Hash.updateRange(hsOpts.UserEntries.begin(), hsOpts.UserEntries.end()); const DiagnosticOptions &diagOpts = getDiagnosticOpts(); - #define DIAGOPT(Name, Bits, Default) \ - code = hash_combine(code, diagOpts.Name); - #define ENUM_DIAGOPT(Name, Type, Bits, Default) \ - code = hash_combine(code, diagOpts.get##Name()); - #include "clang/Basic/DiagnosticOptions.def" - #undef DIAGOPT - #undef ENUM_DIAGOPT +#define DIAGOPT(Name, Bits, Default) Hash.update(diagOpts.Name); +#define ENUM_DIAGOPT(Name, Type, Bits, Default) \ + Hash.update(diagOpts.get##Name()); +#include "clang/Basic/DiagnosticOptions.def" +#undef DIAGOPT +#undef ENUM_DIAGOPT } // Extend the signature with the user build path. - code = hash_combine(code, hsOpts.ModuleUserBuildPath); + Hash.update(hsOpts.ModuleUserBuildPath); // Extend the signature with the module file extensions. const FrontendOptions &frontendOpts = getFrontendOpts(); - for (const auto &ext : frontendOpts.ModuleFileExtensions) { - code = ext->hashExtension(code); - } + for (const auto &ext : frontendOpts.ModuleFileExtensions) + ext->hashExtension(Hash); // When compiling with -gmodules, also hash -fdebug-prefix-map as it // affects the debug info in the PCM. if (getCodeGenOpts().DebugTypeExtRefs) - for (const auto &KeyValue : getCodeGenOpts().DebugPrefixMap) - code = hash_combine(code, KeyValue.first, KeyValue.second); + for (const auto &KeyValue : getCodeGenOpts().DebugPrefixMap) { + Hash.update(KeyValue.first); + Hash.update(KeyValue.second); + } // Extend the signature with the enabled sanitizers, if at least one is // enabled. Sanitizers which cannot affect AST generation aren't hashed. SanitizerSet SanHash = LangOpts->Sanitize; SanHash.clear(getPPTransparentSanitizers()); if (!SanHash.empty()) - code = hash_combine(code, SanHash.Mask); + Hash.update(SanHash.Mask); - return llvm::APInt(64, code).toString(36, /*Signed=*/false); + return llvm::APInt(64, Hash.getValue()).toString(36, /*Signed=*/false); } void CompilerInvocation::generateCC1CommandLine( diff --git a/clang/lib/Frontend/TestModuleFileExtension.h b/clang/lib/Frontend/TestModuleFileExtension.h --- a/clang/lib/Frontend/TestModuleFileExtension.h +++ b/clang/lib/Frontend/TestModuleFileExtension.h @@ -55,7 +55,7 @@ ModuleFileExtensionMetadata getExtensionMetadata() const override; - llvm::hash_code hashExtension(llvm::hash_code Code) const override; + void hashExtension(ModuleHash &Hash) const override; std::unique_ptr createExtensionWriter(ASTWriter &Writer) override; diff --git a/clang/lib/Frontend/TestModuleFileExtension.cpp b/clang/lib/Frontend/TestModuleFileExtension.cpp --- a/clang/lib/Frontend/TestModuleFileExtension.cpp +++ b/clang/lib/Frontend/TestModuleFileExtension.cpp @@ -93,16 +93,13 @@ return { BlockName, MajorVersion, MinorVersion, UserInfo }; } -llvm::hash_code TestModuleFileExtension::hashExtension( - llvm::hash_code Code) const { +void TestModuleFileExtension::hashExtension(ModuleHash &Hash) const { if (Hashed) { - Code = llvm::hash_combine(Code, BlockName); - Code = llvm::hash_combine(Code, MajorVersion); - Code = llvm::hash_combine(Code, MinorVersion); - Code = llvm::hash_combine(Code, UserInfo); + Hash.update(BlockName); + Hash.update(MajorVersion); + Hash.update(MinorVersion); + Hash.update(UserInfo); } - - return Code; } std::unique_ptr diff --git a/clang/lib/Serialization/ModuleFileExtension.cpp b/clang/lib/Serialization/ModuleFileExtension.cpp --- a/clang/lib/Serialization/ModuleFileExtension.cpp +++ b/clang/lib/Serialization/ModuleFileExtension.cpp @@ -13,10 +13,6 @@ ModuleFileExtension::~ModuleFileExtension() { } -llvm::hash_code ModuleFileExtension::hashExtension(llvm::hash_code Code) const { - return Code; -} - ModuleFileExtensionWriter::~ModuleFileExtensionWriter() { } ModuleFileExtensionReader::~ModuleFileExtensionReader() { } diff --git a/clang/unittests/Frontend/CompilerInvocationTest.cpp b/clang/unittests/Frontend/CompilerInvocationTest.cpp --- a/clang/unittests/Frontend/CompilerInvocationTest.cpp +++ b/clang/unittests/Frontend/CompilerInvocationTest.cpp @@ -860,9 +860,7 @@ return {}; }; - llvm::hash_code hashExtension(llvm::hash_code Code) const override { - return {}; - } + void hashExtension(ModuleHash &Hash) const override {} std::unique_ptr createExtensionWriter(ASTWriter &Writer) override { diff --git a/llvm/include/llvm/Support/VersionTuple.h b/llvm/include/llvm/Support/VersionTuple.h --- a/llvm/include/llvm/Support/VersionTuple.h +++ b/llvm/include/llvm/Support/VersionTuple.h @@ -161,5 +161,10 @@ /// Print a version number. raw_ostream &operator<<(raw_ostream &Out, const VersionTuple &V); +template void updateHash(HashT &Hash, const VersionTuple &V) { + Hash.update({V.getMajor(), V.getMinor().getValueOr(0), + V.getSubminor().getValueOr(0), V.getBuild().getValueOr(0)}); +} + } // end namespace llvm #endif // LLVM_SUPPORT_VERSIONTUPLE_H