Commit 1063d2b8 by Samuel Benzaquen Committed by Copybara-Service

Move the vtable into a function to delay instantiation until the function is

called. When the variable is a global the compiler is allowed to instantiate it more
aggresively and it might happen before the types involved are complete.
When it is inside a function the compiler can't instantiate it until after the
functions are called.

Remove an unused member from the vtable.
Replace transfer_slot_fn with a generic function when relocation is available to reduce duplication.

PiperOrigin-RevId: 492227302
Change-Id: I07499f63b91c59c0ae42402683387c7b84a6f0ee
parent a23d720c
......@@ -63,7 +63,7 @@ struct common_policy_traits {
// UNINITIALIZED
template <class Alloc>
static void transfer(Alloc* alloc, slot_type* new_slot, slot_type* old_slot) {
transfer_impl(alloc, new_slot, old_slot, 0);
transfer_impl(alloc, new_slot, old_slot, Rank0{});
}
// PRECONDITION: `slot` is INITIALIZED
......@@ -80,29 +80,46 @@ struct common_policy_traits {
return P::element(slot);
}
static constexpr bool transfer_uses_memcpy() {
return std::is_same<decltype(transfer_impl<std::allocator<char>>(
nullptr, nullptr, nullptr, Rank0{})),
std::true_type>::value;
}
private:
// To rank the overloads below for overload resoltion. Rank0 is preferred.
struct Rank2 {};
struct Rank1 : Rank2 {};
struct Rank0 : Rank1 {};
// Use auto -> decltype as an enabler.
template <class Alloc, class P = Policy>
static auto transfer_impl(Alloc* alloc, slot_type* new_slot,
slot_type* old_slot, int)
slot_type* old_slot, Rank0)
-> decltype((void)P::transfer(alloc, new_slot, old_slot)) {
P::transfer(alloc, new_slot, old_slot);
}
template <class Alloc>
static void transfer_impl(Alloc* alloc, slot_type* new_slot,
slot_type* old_slot, char) {
#if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606
if (absl::is_trivially_relocatable<value_type>()) {
// TODO(b/247130232,b/251814870): remove casts after fixing warnings.
std::memcpy(static_cast<void*>(
std::launder(const_cast<std::remove_const_t<value_type>*>(
&element(new_slot)))),
static_cast<const void*>(&element(old_slot)),
sizeof(value_type));
return;
}
// This overload returns true_type for the trait below.
// The conditional_t is to make the enabler type dependent.
template <class Alloc,
typename = std::enable_if_t<absl::is_trivially_relocatable<
std::conditional_t<false, Alloc, value_type>>::value>>
static std::true_type transfer_impl(Alloc*, slot_type* new_slot,
slot_type* old_slot, Rank1) {
// TODO(b/247130232): remove casts after fixing warnings.
// TODO(b/251814870): remove casts after fixing warnings.
std::memcpy(
static_cast<void*>(std::launder(
const_cast<std::remove_const_t<value_type>*>(&element(new_slot)))),
static_cast<const void*>(&element(old_slot)), sizeof(value_type));
return {};
}
#endif
template <class Alloc>
static void transfer_impl(Alloc* alloc, slot_type* new_slot,
slot_type* old_slot, Rank2) {
construct(alloc, new_slot, std::move(element(old_slot)));
destroy(alloc, old_slot);
}
......
......@@ -1058,7 +1058,6 @@ ABSL_ATTRIBUTE_NOINLINE void InitializeSlots(CommonFields& c,
// work.
struct PolicyFunctions {
size_t slot_size;
size_t slot_align;
// Return the hash of the pointed-to slot.
size_t (*hash_slot)(void* set, void* slot);
......@@ -1098,6 +1097,14 @@ ABSL_ATTRIBUTE_NOINLINE void DeallocateStandard(void*,
AllocSize(n, policy.slot_size, AlignOfSlot));
}
// For trivially relocatable types we use memcpy directly. This allows us to
// share the same function body for raw_hash_set instantiations that have the
// same slot size as long as they are relocatable.
template <size_t SizeOfSlot>
ABSL_ATTRIBUTE_NOINLINE void TransferRelocatable(void*, void* dst, void* src) {
memcpy(dst, src, SizeOfSlot);
}
// Type-erased version of raw_hash_set::drop_deletes_without_resize.
void DropDeletesWithoutResize(CommonFields& common, size_t& growth_left,
const PolicyFunctions& policy, void* tmp_space);
......@@ -1538,7 +1545,7 @@ class raw_hash_set : private CommonFields {
// Already guaranteed to be empty; so nothing to do.
} else {
destroy_slots();
ClearBackingArray(common(), growth_left(), kPolicyFunctions,
ClearBackingArray(common(), growth_left(), GetPolicyFunctions(),
/*reuse=*/cap < 128);
}
}
......@@ -1846,7 +1853,7 @@ class raw_hash_set : private CommonFields {
void rehash(size_t n) {
if (n == 0 && capacity() == 0) return;
if (n == 0 && size() == 0) {
ClearBackingArray(common(), growth_left(), kPolicyFunctions,
ClearBackingArray(common(), growth_left(), GetPolicyFunctions(),
/*reuse=*/false);
return;
}
......@@ -2127,7 +2134,8 @@ class raw_hash_set : private CommonFields {
inline void drop_deletes_without_resize() {
// Stack-allocate space for swapping elements.
alignas(slot_type) unsigned char tmp[sizeof(slot_type)];
DropDeletesWithoutResize(common(), growth_left(), kPolicyFunctions, tmp);
DropDeletesWithoutResize(common(), growth_left(), GetPolicyFunctions(),
tmp);
}
// Called whenever the table *might* need to conditionally grow.
......@@ -2347,15 +2355,19 @@ class raw_hash_set : private CommonFields {
AllocSize(n, sizeof(slot_type), alignof(slot_type)));
}
static constexpr PolicyFunctions kPolicyFunctions = {
sizeof(slot_type),
alignof(slot_type),
&raw_hash_set::hash_slot_fn,
&raw_hash_set::transfer_slot_fn,
(std::is_same<SlotAlloc, std::allocator<slot_type>>::value
? &DeallocateStandard<alignof(slot_type)>
: &raw_hash_set::dealloc_fn),
};
static const PolicyFunctions& GetPolicyFunctions() {
static constexpr PolicyFunctions value = {
sizeof(slot_type),
&raw_hash_set::hash_slot_fn,
PolicyTraits::transfer_uses_memcpy()
? TransferRelocatable<sizeof(slot_type)>
: &raw_hash_set::transfer_slot_fn,
(std::is_same<SlotAlloc, std::allocator<slot_type>>::value
? &DeallocateStandard<alignof(slot_type)>
: &raw_hash_set::dealloc_fn),
};
return value;
}
// Bundle together growth_left (number of slots that can be filled without
// rehashing) plus other objects which might be empty. CompressedTuple will
......@@ -2366,12 +2378,6 @@ class raw_hash_set : private CommonFields {
settings_{0u, hasher{}, key_equal{}, allocator_type{}};
};
#ifdef ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL
template <class Policy, class Hash, class Eq, class Alloc>
constexpr PolicyFunctions
raw_hash_set<Policy, Hash, Eq, Alloc>::kPolicyFunctions;
#endif
// Erases all elements that satisfy the predicate `pred` from the container `c`.
template <typename P, typename H, typename E, typename A, typename Predicate>
typename raw_hash_set<P, H, E, A>::size_type EraseIf(
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment