tint: Castable - optimize IsAnyOf()

`HashCodeOf()` sets two bits in the mask to `1`.
We can check for two bits set, after masking, to quickly eliminate impossible Is() tests.

Change-Id: I8d9c1ece87b714e83bd292d02e02274d42e143ef
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/92664
Commit-Queue: Ben Clayton <bclayton@chromium.org>
Commit-Queue: Ben Clayton <bclayton@google.com>
Reviewed-by: Dan Sinclair <dsinclair@chromium.org>
Kokoro: Kokoro <noreply+kokoro@google.com>
This commit is contained in:
Ben Clayton 2022-08-23 15:39:15 +00:00 committed by Dawn LUCI CQ
parent cdcc85973b
commit e5d337171a
1 changed files with 20 additions and 15 deletions

View File

@ -159,7 +159,7 @@ struct TypeInfo {
} }
/// @returns a compile-time hashcode for the type `T`. /// @returns a compile-time hashcode for the type `T`.
/// @note the returned hashcode will have at most 2 bits set, as the hashes /// @note the returned hashcode will have exactly 2 bits set, as the hashes
/// are expected to be used in bloom-filters which will quickly saturate when /// are expected to be used in bloom-filters which will quickly saturate when
/// multiple hashcodes are bitwise-or'd together. /// multiple hashcodes are bitwise-or'd together.
template <typename T> template <typename T>
@ -176,7 +176,8 @@ struct TypeInfo {
#endif #endif
constexpr uint32_t bit_a = (crc & 63); constexpr uint32_t bit_a = (crc & 63);
constexpr uint32_t bit_b = ((crc >> 6) & 63); constexpr uint32_t bit_b = ((crc >> 6) & 63);
return (static_cast<HashCode>(1) << bit_a) | (static_cast<HashCode>(1) << bit_b); constexpr uint32_t bit_c = (bit_a == bit_b) ? ((bit_a + 1) & 63) : bit_b;
return (static_cast<HashCode>(1) << bit_a) | (static_cast<HashCode>(1) << bit_c);
} }
/// @returns the hashcode of the given type, bitwise-or'd with the hashcodes /// @returns the hashcode of the given type, bitwise-or'd with the hashcodes
@ -221,17 +222,16 @@ struct TypeInfo {
if constexpr (kCount == 0) { if constexpr (kCount == 0) {
return false; return false;
} else if constexpr (kCount == 1) { } else if constexpr (kCount == 1) {
return Is<std::tuple_element_t<0, TUPLE>>(); return Is(&Of<std::tuple_element_t<0, TUPLE>>());
} else if constexpr (kCount == 2) {
return Is<std::tuple_element_t<0, TUPLE>>() || Is<std::tuple_element_t<1, TUPLE>>();
} else if constexpr (kCount == 3) {
return Is<std::tuple_element_t<0, TUPLE>>() || Is<std::tuple_element_t<1, TUPLE>>() ||
Is<std::tuple_element_t<2, TUPLE>>();
} else { } else {
// Optimization: Compare the object's hashcode to the bitwise-or of all // Optimization: Compare the object's hashcode to the bitwise-or of all the tested
// the tested type's hashcodes. If there's no intersection of bits in // type's hashcodes. If there's no intersection of bits in the two masks, then we can
// the two masks, then we can guarantee that the type is not in `TO`. // guarantee that the type is not in `TO`.
if (full_hashcode & TypeInfo::CombinedHashCodeOfTuple<TUPLE>()) { HashCode mask = full_hashcode & TypeInfo::CombinedHashCodeOfTuple<TUPLE>();
// HashCodeOf() ensures that two bits are always set for every hash, so we can quickly
// eliminate the bitmask where only one bit is set.
HashCode two_bits = mask & (mask - 1);
if (two_bits) {
// Possibly one of the types in `TUPLE`. // Possibly one of the types in `TUPLE`.
// Split the search in two, and scan each block. // Split the search in two, and scan each block.
static constexpr auto kMid = kCount / 2; static constexpr auto kMid = kCount / 2;
@ -607,9 +607,14 @@ inline bool NonDefaultCases(T* object,
return false; return false;
} else { } else {
// Multiple cases. // Multiple cases.
// Check the hashcode bits to see if there's any possibility of a case // Check the hashcode bits to see if there's any possibility of a case matching in these
// matching in these cases. If there isn't, we can skip all these cases. // cases. If there isn't, we can skip all these cases.
if (type->full_hashcode & TypeInfo::CombinedHashCodeOf<SwitchCaseType<CASES>...>()) { TypeInfo::HashCode mask =
type->full_hashcode & TypeInfo::CombinedHashCodeOf<SwitchCaseType<CASES>...>();
// HashCodeOf() ensures that two bits are always set for every hash, so we can quickly
// eliminate the bitmask where only one bit is set.
TypeInfo::HashCode two_bits = mask & (mask - 1);
if (two_bits) {
// There's a possibility. We need to scan further. // There's a possibility. We need to scan further.
// Split the cases into two, and recurse. // Split the cases into two, and recurse.
constexpr size_t kMid = kNumCases / 2; constexpr size_t kMid = kNumCases / 2;