diff --git a/common/BUILD b/common/BUILD index f7d08dabbb19..41c292327e5d 100644 --- a/common/BUILD +++ b/common/BUILD @@ -342,12 +342,22 @@ cc_library( ], ) +cc_library( + name = "hashing_llvm", + hdrs = ["hashing_llvm.h"], + deps = [ + ":hashing", + "@llvm-project//llvm:Support", + ], +) + cc_test( name = "hashing_test", size = "small", srcs = ["hashing_test.cpp"], deps = [ ":hashing", + ":hashing_llvm", ":raw_string_ostream", "//testing/base:gtest_main", "@googletest//:gtest", @@ -384,6 +394,7 @@ cc_test( size = "small", srcs = ["hashtable_key_context_test.cpp"], deps = [ + ":hashing_llvm", ":hashtable_key_context", "//testing/base:gtest_main", "@googletest//:gtest", @@ -460,6 +471,7 @@ cc_test( ":raw_hashtable_test_helpers", "//testing/base:gtest_main", "@googletest//:gtest", + "@llvm-project//llvm:Support", ], ) @@ -648,6 +660,7 @@ cc_test( ":set", "//testing/base:gtest_main", "@googletest//:gtest", + "@llvm-project//llvm:Support", ], ) diff --git a/common/hashing.cpp b/common/hashing.cpp index e7bb1da4042c..cd5c05765591 100644 --- a/common/hashing.cpp +++ b/common/hashing.cpp @@ -6,8 +6,14 @@ #include +#include "llvm/Support/FormatVariadic.h" + namespace Carbon { +auto HashCode::Print(llvm::raw_ostream& out) const -> void { + out << llvm::formatv("{0:x16}", value_); +} + auto Hasher::HashSizedBytesLarge(llvm::ArrayRef bytes) -> void { const std::byte* data_ptr = bytes.data(); const ssize_t size = bytes.size(); diff --git a/common/hashing.h b/common/hashing.h index 3dbb68d51c31..8d4127a8224c 100644 --- a/common/hashing.h +++ b/common/hashing.h @@ -13,12 +13,9 @@ #include "common/check.h" #include "common/ostream.h" -#include "llvm/ADT/APFloat.h" -#include "llvm/ADT/APInt.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" -#include "llvm/Support/FormatVariadic.h" #ifdef __ARM_ACLE #include @@ -72,9 +69,7 @@ class HashCode : public Printable { // other recursive hashing where that is needed or more efficient. explicit operator uint64_t() const { return value_; } - auto Print(llvm::raw_ostream& out) const -> void { - out << llvm::formatv("{0:x16}", value_); - } + auto Print(llvm::raw_ostream& out) const -> void; private: uint64_t value_ = 0; @@ -527,30 +522,6 @@ inline auto CarbonHashValue(const T (&arg)[N], uint64_t seed) -> HashCode { return CarbonHashValue(llvm::ArrayRef(arg), seed); } -inline auto CarbonHashValue(llvm::APInt value, uint64_t seed) -> HashCode { - Hasher hasher(seed); - if (LLVM_LIKELY(value.isSingleWord())) { - hasher.Hash(value.getBitWidth(), value.getZExtValue()); - } else { - hasher.HashRaw(value.getBitWidth()); - hasher.HashSizedBytes( - llvm::ArrayRef(value.getRawData(), value.getNumWords())); - } - return static_cast(hasher); -} - -inline auto CarbonHashValue(llvm::APFloat value, uint64_t seed) -> HashCode { - Hasher hasher(seed); - // Hashing floating point numbers is complex and depends on the specific - // internal semantics of `APFloat`, so delegate to the LLVM hashing framework - // here. We re-hash the result to mix in our seed. All of this is a bit - // inefficient, and we can revisit this to provide a dedicated implementation - // if it becomes a bottleneck. - using llvm::hash_value; - hasher.HashRaw(hash_value(value)); - return static_cast(hasher); -} - template inline auto CarbonHashValue(const std::tuple& value, uint64_t seed) -> HashCode { @@ -567,6 +538,14 @@ inline auto CarbonHashValue(const std::pair& value, uint64_t seed) return static_cast(hasher); } +// Extension point for types defined outside of Carbon that cannot be found by +// ADL in their own namespace and cannot be declared before this point. +template +struct CustomHashValue; + +template +concept HasCustomHashValue = requires { CustomHashValue::Hash; }; + // Implementation detail predicate to detect if there is a `CarbonHashValue` // overload available for a particular type, either in this namespace or found // via ADL. Note that this should not be moved above any overloads. @@ -618,14 +597,19 @@ concept CanHashAsRawDataType = std::same_as || // `HasCarbonHashValue`, this must not be moved above any of those overloads. template inline auto DispatchImpl(const T& value, uint64_t seed) -> HashCode { - // If we have an explicit overload for `CarbonHashValue`, call it. This may be - // provided above or via ADL, and is preferred as it represents an explicit - // request for how the type is hashed. if constexpr (HasCarbonHashValue) { + // If we have an explicit overload for `CarbonHashValue`, call it. This may + // be provided above or via ADL, and is preferred as it represents an + // explicit request for how the type is hashed. return CarbonHashValue(value, seed); + } else if constexpr (HasCustomHashValue) { + // If we have an explicit specialization for `CustomHashValue`, call it. + // This is a fallback explicit hashing path that doesn't require ADL or + // being in this header. + return CustomHashValue::Hash(value, seed); } else if constexpr (CanHashAsRawDataType) { - // There was no explicit overload to call, but the type allows us to hash it - // as raw data, do so. + // There was no explicit overload or specialization to call, but the type + // allows us to hash it as raw data, do so. Hasher hasher(seed); hasher.HashRaw(MapToRawDataType(value)); return static_cast(hasher); @@ -813,11 +797,13 @@ inline auto Hasher::Hash(const Ts&... values) -> void { using InternalHashDispatch::CanHashAsRawDataType; using InternalHashDispatch::HasCarbonHashValue; + using InternalHashDispatch::HasCustomHashValue; using InternalHashDispatch::MapToRawDataType; // Special-case a single element tuple that we will hash as raw data. - if constexpr (sizeof...(Ts) == 1 && (... && (!HasCarbonHashValue && - CanHashAsRawDataType))) { + if constexpr (sizeof...(Ts) == 1 && + (... && (!HasCarbonHashValue && !HasCustomHashValue && + CanHashAsRawDataType))) { HashRaw(MapToRawDataType(values)...); return; } @@ -830,7 +816,7 @@ inline auto Hasher::Hash(const Ts&... values) -> void { // a little bit wasteful in some cases, collapsing down to a flat array of // 64-bit integers is more efficient to hash. auto map_value = [](const T& value) -> uint64_t { - if constexpr (HasCarbonHashValue) { + if constexpr (HasCarbonHashValue || HasCustomHashValue) { // Use the top-level `HashValue` to re-dispatch to the custom // implementation with a fixed seed. return static_cast(HashValue(value)); @@ -866,11 +852,12 @@ template inline auto Hasher::HashArray(llvm::ArrayRef values) -> void { using InternalHashDispatch::CanHashAsRawDataType; using InternalHashDispatch::HasCarbonHashValue; + using InternalHashDispatch::HasCustomHashValue; // This logic similarly mirrors `InternalHashDispatch::DispatchImpl`, but is // specialized here to allow us to efficiently process the array when it // *doesn't* require recursive hashing. - if constexpr (HasCarbonHashValue) { + if constexpr (HasCarbonHashValue || HasCustomHashValue) { // Use a trivial loop to give consistent behavior for arrays requiring // recursive hashing. This isn't terribly efficient, but if clients care // they should specialize the entire hashing operation. For simple, tiny diff --git a/common/hashing_llvm.h b/common/hashing_llvm.h new file mode 100644 index 000000000000..b924d1d84f31 --- /dev/null +++ b/common/hashing_llvm.h @@ -0,0 +1,47 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#ifndef CARBON_COMMON_HASHING_LLVM_H_ +#define CARBON_COMMON_HASHING_LLVM_H_ + +#include "common/hashing.h" +#include "llvm/ADT/APFloat.h" +#include "llvm/ADT/APInt.h" +#include "llvm/ADT/Hashing.h" + +namespace Carbon::InternalHashDispatch { + +template <> +struct CustomHashValue { + static auto Hash(llvm::APInt value, uint64_t seed) -> HashCode { + Hasher hasher(seed); + if (LLVM_LIKELY(value.isSingleWord())) { + hasher.Hash(value.getBitWidth(), value.getZExtValue()); + } else { + hasher.HashRaw(value.getBitWidth()); + hasher.HashSizedBytes( + llvm::ArrayRef(value.getRawData(), value.getNumWords())); + } + return static_cast(hasher); + } +}; + +template <> +struct CustomHashValue { + static auto Hash(llvm::APFloat value, uint64_t seed) -> HashCode { + Hasher hasher(seed); + // Hashing floating point numbers is complex and depends on the specific + // internal semantics of `APFloat`, so delegate to the LLVM hashing + // framework here. We re-hash the result to mix in our seed. All of this is + // a bit inefficient, and we can revisit this to provide a dedicated + // implementation if it becomes a bottleneck. + using llvm::hash_value; + hasher.HashRaw(hash_value(value)); + return static_cast(hasher); + } +}; + +} // namespace Carbon::InternalHashDispatch + +#endif // CARBON_COMMON_HASHING_LLVM_H_ diff --git a/common/hashing_test.cpp b/common/hashing_test.cpp index 4522e8368774..aff99b873e85 100644 --- a/common/hashing_test.cpp +++ b/common/hashing_test.cpp @@ -13,6 +13,7 @@ #include #include +#include "common/hashing_llvm.h" #include "common/raw_string_ostream.h" #include "llvm/ADT/Sequence.h" #include "llvm/ADT/StringExtras.h" diff --git a/common/hashtable_key_context_test.cpp b/common/hashtable_key_context_test.cpp index 19c31627e0e2..d3d18285a1b4 100644 --- a/common/hashtable_key_context_test.cpp +++ b/common/hashtable_key_context_test.cpp @@ -7,6 +7,8 @@ #include #include +#include "common/hashing_llvm.h" + namespace Carbon { namespace { diff --git a/toolchain/base/BUILD b/toolchain/base/BUILD index 9dc6b1c7efe0..cd205fbdb05b 100644 --- a/toolchain/base/BUILD +++ b/toolchain/base/BUILD @@ -24,7 +24,6 @@ cc_library( ":value_store", ":yaml", "//common:check", - "//common:hashing", "//common:set", "@llvm-project//llvm:Support", ], @@ -55,6 +54,7 @@ cc_test( ":shared_value_stores", ":value_ids", ":value_store", + "//common:hashing_llvm", "//testing/base:gtest_main", "@googletest//:gtest", "@llvm-project//llvm:Support", @@ -67,7 +67,6 @@ cc_library( hdrs = ["clang_invocation.h"], deps = [ ":install_paths", - "//common:check", "//common:string_helpers", "//toolchain/diagnostics:emitter", "@llvm-project//clang:basic", @@ -92,9 +91,6 @@ cc_library( cc_library( name = "for_each_macro", hdrs = ["for_each_macro.h"], - deps = [ - "@llvm-project//llvm:Support", - ], ) cc_library( @@ -292,6 +288,7 @@ cc_library( ":value_store", ":yaml", "//common:check", + "//common:hashing_llvm", "//common:hashtable_key_context", "//common:ostream", "//common:set", @@ -356,6 +353,7 @@ cc_library( ":value_ids", ":value_store", ":yaml", + "//common:hashing_llvm", "@llvm-project//llvm:Support", ], ) diff --git a/toolchain/base/canonical_value_store_test.cpp b/toolchain/base/canonical_value_store_test.cpp index 642a9538fab4..a77278878359 100644 --- a/toolchain/base/canonical_value_store_test.cpp +++ b/toolchain/base/canonical_value_store_test.cpp @@ -9,6 +9,7 @@ #include +#include "common/hashing_llvm.h" #include "llvm/ADT/APFloat.h" #include "llvm/ADT/StringRef.h" #include "toolchain/base/canonical_value_store_impl.h" diff --git a/toolchain/base/int.cpp b/toolchain/base/int.cpp index 9ecb519c1b81..b0d65d0dbfd0 100644 --- a/toolchain/base/int.cpp +++ b/toolchain/base/int.cpp @@ -7,6 +7,7 @@ #include #include +#include "common/hashing_llvm.h" #include "toolchain/base/canonical_value_store_impl.h" #include "toolchain/base/value_store_impl.h" diff --git a/toolchain/base/int.h b/toolchain/base/int.h index f450bb67a133..0323e10a765d 100644 --- a/toolchain/base/int.h +++ b/toolchain/base/int.h @@ -6,6 +6,7 @@ #define CARBON_TOOLCHAIN_BASE_INT_H_ #include "common/check.h" +#include "common/hashing_llvm.h" #include "llvm/ADT/APInt.h" #include "llvm/ADT/SmallVector.h" #include "toolchain/base/canonical_value_store.h" diff --git a/toolchain/base/shared_value_stores.cpp b/toolchain/base/shared_value_stores.cpp index bc3280ea3343..bf9a0ed0bb48 100644 --- a/toolchain/base/shared_value_stores.cpp +++ b/toolchain/base/shared_value_stores.cpp @@ -4,6 +4,7 @@ #include "toolchain/base/shared_value_stores.h" +#include "common/hashing_llvm.h" #include "toolchain/base/canonical_value_store_impl.h" #include "toolchain/base/value_store_impl.h" diff --git a/toolchain/base/shared_value_stores.h b/toolchain/base/shared_value_stores.h index b2d802d10bf4..328f4ef6ab42 100644 --- a/toolchain/base/shared_value_stores.h +++ b/toolchain/base/shared_value_stores.h @@ -5,6 +5,7 @@ #ifndef CARBON_TOOLCHAIN_BASE_SHARED_VALUE_STORES_H_ #define CARBON_TOOLCHAIN_BASE_SHARED_VALUE_STORES_H_ +#include "common/hashing_llvm.h" #include "llvm/ADT/APFloat.h" #include "llvm/ADT/StringRef.h" #include "toolchain/base/canonical_value_store.h"