aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChandler Carruth <chandlerc@gmail.com>2012-03-02 09:26:36 +0000
committerChandler Carruth <chandlerc@gmail.com>2012-03-02 09:26:36 +0000
commit4d628e200f7133e353c38806b57a229ef6ad2ab4 (patch)
tree9cb8db0b305fbf1266adc7327b1a35ec1278eec3
parentc7384cfc7addb5d2818ac0bb4492778f28183c49 (diff)
downloadexternal_llvm-4d628e200f7133e353c38806b57a229ef6ad2ab4.zip
external_llvm-4d628e200f7133e353c38806b57a229ef6ad2ab4.tar.gz
external_llvm-4d628e200f7133e353c38806b57a229ef6ad2ab4.tar.bz2
We really want to hash pairs of directly-hashable data as directly
hashable data. This matters when we have pair<T*, U*> as a key, which is quite common in DenseMap, etc. To that end, we need to detect when this is safe. The requirements on a generic std::pair<T, U> are: 1) Both T and U must satisfy the existing is_hashable_data trait. Note that this includes the requirement that T and U have no internal padding bits or other bits not contributing directly to equality. 2) The alignment constraints of std::pair<T, U> do not require padding between consecutive objects. 3) The alignment constraints of U and the size of T do not conspire to require padding between the first and second elements. Grow two somewhat magical traits to detect this by forming a pod structure and inspecting offset artifacts on it. Hopefully this won't cause any compilers to panic. Added and adjusted tests now that pairs, even nested pairs, are treated as just sequences of data. Thanks to Jeffrey Yasskin for helping me sort through this and reviewing the somewhat subtle traits. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@151883 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--include/llvm/ADT/Hashing.h10
-rw-r--r--include/llvm/Support/type_traits.h18
-rw-r--r--unittests/ADT/HashingTest.cpp12
3 files changed, 35 insertions, 5 deletions
diff --git a/include/llvm/ADT/Hashing.h b/include/llvm/ADT/Hashing.h
index 06dd76a..1b0e669 100644
--- a/include/llvm/ADT/Hashing.h
+++ b/include/llvm/ADT/Hashing.h
@@ -350,6 +350,16 @@ template <typename T> struct is_hashable_data
: integral_constant<bool, ((is_integral<T>::value || is_pointer<T>::value) &&
64 % sizeof(T) == 0)> {};
+// Special case std::pair to detect when both types are viable and when there
+// is no alignment-derived padding in the pair. This is a bit of a lie because
+// std::pair isn't truly POD, but it's close enough in all reasonable
+// implementations for our use case of hashing the underlying data.
+template <typename T, typename U> struct is_hashable_data<std::pair<T, U> >
+ : integral_constant<bool, (is_hashable_data<T>::value &&
+ is_hashable_data<U>::value &&
+ !is_alignment_padded<std::pair<T, U> >::value &&
+ !is_pod_pair_padded<T, U>::value)> {};
+
/// \brief Helper to get the hashable data representation for a type.
/// This variant is enabled when the type itself can be used.
template <typename T>
diff --git a/include/llvm/Support/type_traits.h b/include/llvm/Support/type_traits.h
index 03f85e9..f488a29 100644
--- a/include/llvm/Support/type_traits.h
+++ b/include/llvm/Support/type_traits.h
@@ -125,6 +125,24 @@ struct is_integral : is_integral_impl<T> {};
template <typename T> struct is_pointer : false_type {};
template <typename T> struct is_pointer<T*> : true_type {};
+/// \brief Metafunction to compute whether a type requires alignment padding.
+/// When true, an object of this type will have padding bytes inside its
+/// 'sizeof' bytes.
+template <typename T> class is_alignment_padded {
+ struct pod_size_tester { T t; char c; };
+public:
+ enum { value = offsetof(pod_size_tester, c) != sizeof(T) };
+};
+
+/// \brief Metafunction to determine whether an adjacent pair of two types will
+/// require padding between them due to alignment.
+template <typename T, typename U> class is_pod_pair_padded {
+ struct pod_pair { T t; U u; };
+ struct pod_char_pair { T t; char c; };
+public:
+ enum { value = offsetof(pod_pair, u) != offsetof(pod_char_pair, c) };
+};
+
// enable_if_c - Enable/disable a template based on a metafunction
template<bool Cond, typename T = void>
diff --git a/unittests/ADT/HashingTest.cpp b/unittests/ADT/HashingTest.cpp
index a9458bb..19a036c 100644
--- a/unittests/ADT/HashingTest.cpp
+++ b/unittests/ADT/HashingTest.cpp
@@ -66,11 +66,13 @@ TEST(HashingTest, HashValueBasicTest) {
EXPECT_NE(hash_combine(42, 43), hash_value(std::make_pair(42ull, 43ull)));
EXPECT_NE(hash_combine(42, 43), hash_value(std::make_pair(42, 43ull)));
EXPECT_NE(hash_combine(42, 43), hash_value(std::make_pair(42ull, 43)));
- EXPECT_EQ(hash_combine(42, hash_combine(43, hash_combine(44, 45))),
- hash_value(
- std::make_pair(42, std::make_pair(43, std::make_pair(44, 45)))));
- EXPECT_EQ(hash_combine(42, 43), hash_value(std::make_pair(42, 43)));
- EXPECT_EQ(hash_combine(42, 43), hash_value(std::make_pair(42, 43)));
+
+ // Note that pairs are implicitly flattened to a direct sequence of data and
+ // hashed efficiently as a consequence.
+ EXPECT_EQ(hash_combine(42, 43, 44),
+ hash_value(std::make_pair(42, std::make_pair(43, 44))));
+ EXPECT_EQ(hash_value(std::make_pair(42, std::make_pair(43, 44))),
+ hash_value(std::make_pair(std::make_pair(42, 43), 44)));
}
template <typename T, size_t N> T *begin(T (&arr)[N]) { return arr; }