manually merge some changes from abseil-cpp

This commit is contained in:
greg
2022-03-06 16:54:03 -05:00
parent cfacd26023
commit 73be7c2ab2
5 changed files with 174 additions and 63 deletions
+39 -17
View File
@@ -858,14 +858,14 @@ namespace priv {
// Upper bound for the available space for values. This is largest for leaf
// nodes, which have overhead of at least a pointer + 4 bytes (for storing
// 3 field_types and an enum).
kNodeValueSpace =
TargetNodeSize - /*minimum overhead=*/(sizeof(void *) + 4),
kNodeSlotSpace =
TargetNodeSize - /*minimum overhead=*/(sizeof(void *) + 4),
};
// This is an integral type large enough to hold as many
// ValueSize-values as will fit a node of TargetNodeSize bytes.
using node_count_type =
phmap::conditional_t<(kNodeValueSpace / sizeof(value_type) >
phmap::conditional_t<(kNodeSlotSpace / sizeof(slot_type) >
(std::numeric_limits<uint8_t>::max)()),
uint16_t, uint8_t>; // NOLINT
@@ -2501,7 +2501,7 @@ namespace priv {
"key comparison function must return phmap::{weak,strong}_ordering or "
"bool.");
// Test the assumption made in setting kNodeValueSpace.
// Test the assumption made in setting kNodeSlotSpace.
static_assert(node_type::MinimumOverhead() >= sizeof(void *) + 4,
"node space assumption incorrect");
@@ -3314,6 +3314,12 @@ namespace priv {
const_reverse_iterator crend() const { return tree_.rend(); }
// Lookup routines.
// ----------------
template <typename K = key_type>
size_type count(const key_arg<K> &key) const {
auto equal_range = this->equal_range(key);
return std::distance(equal_range.first, equal_range.second);
}
template <typename K = key_type>
iterator find(const key_arg<K> &key) {
return tree_.find(key);
@@ -3350,7 +3356,11 @@ namespace priv {
iterator erase(const_iterator first, const_iterator last) {
return tree_.erase(iterator(first), iterator(last)).second;
}
template <typename K = key_type>
size_type erase(const key_arg<K> &key) {
auto equal_range = this->equal_range(key);
return tree_.erase_range(equal_range.first, equal_range.second).first;
}
node_type extract(iterator position) {
// Use Move instead of Transfer, because the rebalancing code expects to
// have a valid object to scribble metadata bits on top of.
@@ -3449,6 +3459,10 @@ namespace priv {
const allocator_type &alloc = allocator_type())
: btree_set_container(init.begin(), init.end(), comp, alloc) {}
btree_set_container(std::initializer_list<init_type> init,
const allocator_type &alloc)
: btree_set_container(init.begin(), init.end(), alloc) {}
// Lookup routines.
template <typename K = key_type>
size_type count(const key_arg<K> &key) const {
@@ -3467,23 +3481,23 @@ namespace priv {
init_type v(std::forward<Args>(args)...);
return this->tree_.insert_unique(params_type::key(v), std::move(v));
}
iterator insert(const_iterator position, const value_type &x) {
iterator insert(const_iterator hint, const value_type &x) {
return this->tree_
.insert_hint_unique(iterator(position), params_type::key(x), x)
.insert_hint_unique(iterator(hint), params_type::key(x), x)
.first;
}
iterator insert(const_iterator position, value_type &&x) {
iterator insert(const_iterator hint, value_type &&x) {
return this->tree_
.insert_hint_unique(iterator(position), params_type::key(x),
.insert_hint_unique(iterator(hint), params_type::key(x),
std::move(x))
.first;
}
template <typename... Args>
iterator emplace_hint(const_iterator position, Args &&... args) {
iterator emplace_hint(const_iterator hint, Args &&... args) {
init_type v(std::forward<Args>(args)...);
return this->tree_
.insert_hint_unique(iterator(position), params_type::key(v),
.insert_hint_unique(iterator(hint), params_type::key(v),
std::move(v))
.first;
}
@@ -3705,11 +3719,11 @@ namespace priv {
iterator insert(value_type &&x) {
return this->tree_.insert_multi(std::move(x));
}
iterator insert(const_iterator position, const value_type &x) {
return this->tree_.insert_hint_multi(iterator(position), x);
iterator insert(const_iterator hint, const value_type &x) {
return this->tree_.insert_hint_multi(iterator(hint), x);
}
iterator insert(const_iterator position, value_type &&x) {
return this->tree_.insert_hint_multi(iterator(position), std::move(x));
iterator insert(const_iterator hint, value_type &&x) {
return this->tree_.insert_hint_multi(iterator(hint), std::move(x));
}
template <typename InputIterator>
void insert(InputIterator b, InputIterator e) {
@@ -3723,9 +3737,9 @@ namespace priv {
return this->tree_.insert_multi(init_type(std::forward<Args>(args)...));
}
template <typename... Args>
iterator emplace_hint(const_iterator position, Args &&... args) {
iterator emplace_hint(const_iterator hint, Args &&... args) {
return this->tree_.insert_hint_multi(
iterator(position), init_type(std::forward<Args>(args)...));
iterator(hint), init_type(std::forward<Args>(args)...));
}
iterator insert(node_type &&node) {
if (!node) return this->end();
@@ -3839,6 +3853,8 @@ namespace priv {
using Base::contains;
using Base::count;
using Base::equal_range;
using Base::lower_bound;
using Base::upper_bound;
using Base::find;
using Base::get_allocator;
using Base::key_comp;
@@ -3896,6 +3912,8 @@ namespace priv {
using Base::contains;
using Base::count;
using Base::equal_range;
using Base::lower_bound;
using Base::upper_bound;
using Base::find;
using Base::get_allocator;
using Base::key_comp;
@@ -3956,6 +3974,8 @@ namespace priv {
using Base::contains;
using Base::count;
using Base::equal_range;
using Base::lower_bound;
using Base::upper_bound;
using Base::find;
using Base::operator[];
using Base::get_allocator;
@@ -4013,6 +4033,8 @@ namespace priv {
using Base::contains;
using Base::count;
using Base::equal_range;
using Base::lower_bound;
using Base::upper_bound;
using Base::find;
using Base::get_allocator;
using Base::key_comp;
+95 -26
View File
@@ -34,6 +34,58 @@
// limitations under the License.
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// IMPLEMENTATION DETAILS
//
// The table stores elements inline in a slot array. In addition to the slot
// array the table maintains some control state per slot. The extra state is one
// byte per slot and stores empty or deleted marks, or alternatively 7 bits from
// the hash of an occupied slot. The table is split into logical groups of
// slots, like so:
//
// Group 1 Group 2 Group 3
// +---------------+---------------+---------------+
// | | | | | | | | | | | | | | | | | | | | | | | | |
// +---------------+---------------+---------------+
//
// On lookup the hash is split into two parts:
// - H2: 7 bits (those stored in the control bytes)
// - H1: the rest of the bits
// The groups are probed using H1. For each group the slots are matched to H2 in
// parallel. Because H2 is 7 bits (128 states) and the number of slots per group
// is low (8 or 16) in almost all cases a match in H2 is also a lookup hit.
//
// On insert, once the right group is found (as in lookup), its slots are
// filled in order.
//
// On erase a slot is cleared. In case the group did not have any empty slots
// before the erase, the erased slot is marked as deleted.
//
// Groups without empty slots (but maybe with deleted slots) extend the probe
// sequence. The probing algorithm is quadratic. Given N the number of groups,
// the probing function for the i'th probe is:
//
// P(0) = H1 % N
//
// P(i) = (P(i - 1) + i) % N
//
// This probing function guarantees that after N probes, all the groups of the
// table will be probed exactly once.
//
// The control state and slot array are stored contiguously in a shared heap
// allocation. The layout of this allocation is: `capacity()` control bytes,
// one sentinel control byte, `Group::kWidth - 1` cloned control bytes,
// <possible padding>, `capacity()` slots. The sentinel control byte is used in
// iteration so we know when we reach the end of the table. The cloned control
// bytes at the end of the table are cloned from the beginning of the table so
// groups that begin near the end of the table can see a full group. In cases in
// which there are more than `capacity()` cloned control bytes, the extra bytes
// are `kEmpty`, and these ensure that we always see at least one empty slot and
// can stop an unsuccessful search.
// ---------------------------------------------------------------------------
#ifdef _MSC_VER
#pragma warning(push)
@@ -76,6 +128,17 @@ namespace phmap {
namespace priv {
// --------------------------------------------------------------------------
template <typename AllocType>
void SwapAlloc(AllocType& lhs, AllocType& rhs,
std::true_type /* propagate_on_container_swap */) {
using std::swap;
swap(lhs, rhs);
}
template <typename AllocType>
void SwapAlloc(AllocType& /*lhs*/, AllocType& /*rhs*/,
std::false_type /* propagate_on_container_swap */) {}
// --------------------------------------------------------------------------
template <size_t Width>
class probe_seq
@@ -181,26 +244,24 @@ public:
return *this;
}
explicit operator bool() const { return mask_ != 0; }
int operator*() const { return LowestBitSet(); }
int LowestBitSet() const {
uint32_t operator*() const { return LowestBitSet(); }
uint32_t LowestBitSet() const {
return priv::TrailingZeros(mask_) >> Shift;
}
int HighestBitSet() const {
return (sizeof(T) * CHAR_BIT - priv::LeadingZeros(mask_) -
1) >>
Shift;
uint32_t HighestBitSet() const {
return (sizeof(T) * CHAR_BIT - priv::LeadingZeros(mask_) - 1) >> Shift;
}
BitMask begin() const { return *this; }
BitMask end() const { return BitMask(0); }
int TrailingZeros() const {
uint32_t TrailingZeros() const {
return priv::TrailingZeros(mask_) >> Shift;
}
int LeadingZeros() const {
constexpr int total_significant_bits = SignificantBits << Shift;
constexpr int extra_bits = sizeof(T) * 8 - total_significant_bits;
uint32_t LeadingZeros() const {
constexpr uint32_t total_significant_bits = SignificantBits << Shift;
constexpr uint32_t extra_bits = sizeof(T) * 8 - total_significant_bits;
return priv::LeadingZeros(mask_ << extra_bits) >> Shift;
}
@@ -286,10 +347,10 @@ inline size_t H1(size_t hashval, const ctrl_t* ) {
#endif
inline ctrl_t H2(size_t hashval) { return (ctrl_t)(hashval & 0x7F); }
inline h2_t H2(size_t hashval) { return (ctrl_t)(hashval & 0x7F); }
inline bool IsEmpty(ctrl_t c) { return c == kEmpty; }
inline bool IsFull(ctrl_t c) { return c >= 0; }
inline bool IsFull(ctrl_t c) { return c >= static_cast<ctrl_t>(0); }
inline bool IsDeleted(ctrl_t c) { return c == kDeleted; }
inline bool IsEmptyOrDeleted(ctrl_t c) { return c < kSentinel; }
@@ -338,7 +399,7 @@ struct GroupSse2Impl
BitMask<uint32_t, kWidth> Match(h2_t hash) const {
auto match = _mm_set1_epi8((char)hash);
return BitMask<uint32_t, kWidth>(
_mm_movemask_epi8(_mm_cmpeq_epi8(match, ctrl)));
static_cast<uint32_t>(_mm_movemask_epi8(_mm_cmpeq_epi8(match, ctrl))));
}
// Returns a bitmask representing the positions of empty slots.
@@ -347,7 +408,7 @@ struct GroupSse2Impl
#if PHMAP_HAVE_SSSE3
// This only works because kEmpty is -128.
return BitMask<uint32_t, kWidth>(
_mm_movemask_epi8(_mm_sign_epi8(ctrl, ctrl)));
static_cast<uint32_t>(_mm_movemask_epi8(_mm_sign_epi8(ctrl, ctrl))));
#else
return Match(static_cast<h2_t>(kEmpty));
#endif
@@ -356,17 +417,17 @@ struct GroupSse2Impl
// Returns a bitmask representing the positions of empty or deleted slots.
// -----------------------------------------------------------------------
BitMask<uint32_t, kWidth> MatchEmptyOrDeleted() const {
auto special = _mm_set1_epi8(kSentinel);
auto special = _mm_set1_epi8(static_cast<uint8_t>(kSentinel));
return BitMask<uint32_t, kWidth>(
_mm_movemask_epi8(_mm_cmpgt_epi8_fixed(special, ctrl)));
static_cast<uint32_t>(_mm_movemask_epi8(_mm_cmpgt_epi8_fixed(special, ctrl))));
}
// Returns the number of trailing empty or deleted elements in the group.
// ----------------------------------------------------------------------
uint32_t CountLeadingEmptyOrDeleted() const {
auto special = _mm_set1_epi8(kSentinel);
auto special = _mm_set1_epi8(static_cast<uint8_t>(kSentinel));
return TrailingZeros(
_mm_movemask_epi8(_mm_cmpgt_epi8_fixed(special, ctrl)) + 1);
static_cast<uint32_t>(_mm_movemask_epi8(_mm_cmpgt_epi8_fixed(special, ctrl)) + 1));
}
// ----------------------------------------------------------------------
@@ -453,6 +514,11 @@ struct GroupPortableImpl
using Group = GroupPortableImpl;
#endif
// The number of cloned control bytes that we copy from the beginning to the
// end of the control bytes array.
// -------------------------------------------------------------------------
constexpr size_t NumClonedBytes() { return Group::kWidth - 1; }
template <class Policy, class Hash, class Eq, class Alloc>
class raw_hash_set;
@@ -1341,7 +1407,9 @@ public:
}
iterator insert(const_iterator, node_type&& node) {
return insert(std::move(node)).first;
auto res = insert(std::move(node));
node = std::move(res.node);
return res.position;
}
// This overload kicks in if we can deduce the key from args. This enables us
@@ -1759,7 +1827,7 @@ private:
auto seq = probe(hashval);
while (true) {
Group g{ ctrl_ + seq.offset() };
for (int i : g.Match((h2_t)H2(hashval))) {
for (uint32_t i : g.Match((h2_t)H2(hashval))) {
offset = seq.offset((size_t)i);
if (PHMAP_PREDICT_TRUE(PolicyTraits::apply(
EqualElement<K>{key, eq_ref()},
@@ -2033,7 +2101,7 @@ private:
auto seq = probe(hashval);
while (true) {
Group g{ctrl_ + seq.offset()};
for (int i : g.Match((h2_t)H2(hashval))) {
for (uint32_t i : g.Match((h2_t)H2(hashval))) {
if (PHMAP_PREDICT_TRUE(PolicyTraits::element(slots_ + seq.offset((size_t)i)) ==
elem))
return true;
@@ -2095,7 +2163,7 @@ protected:
auto seq = probe(hashval);
while (true) {
Group g{ctrl_ + seq.offset()};
for (int i : g.Match((h2_t)H2(hashval))) {
for (uint32_t i : g.Match((h2_t)H2(hashval))) {
if (PHMAP_PREDICT_TRUE(PolicyTraits::apply(
EqualElement<K>{key, eq_ref()},
PolicyTraits::element(slots_ + seq.offset((size_t)i)))))
@@ -2261,9 +2329,10 @@ public:
using key_arg = typename KeyArgImpl::template type<K, key_type>;
static_assert(!std::is_reference<key_type>::value, "");
// TODO(alkis): remove this assertion and verify that reference mapped_type is
// supported.
static_assert(!std::is_reference<mapped_type>::value, "");
// TODO(b/187807849): Evaluate whether to support reference mapped_type and
// remove this assertion if/when it is supported.
static_assert(!std::is_reference<mapped_type>::value, "");
using iterator = typename raw_hash_map::raw_hash_set::iterator;
using const_iterator = typename raw_hash_map::raw_hash_set::const_iterator;
@@ -4327,7 +4396,7 @@ struct HashtableDebugAccess<Set, phmap::void_t<typename Set::raw_hash_set>>
auto seq = set.probe(hashval);
while (true) {
priv::Group g{set.ctrl_ + seq.offset()};
for (int i : g.Match(priv::H2(hashval))) {
for (uint32_t i : g.Match(priv::H2(hashval))) {
if (Traits::apply(
typename Set::template EqualElement<typename Set::key_type>{
key, set.eq_ref()},
+4 -3
View File
@@ -2856,6 +2856,7 @@ class node_handle<Policy, PolicyTraits, Alloc,
: public node_handle_base<PolicyTraits, Alloc>
{
using Base = node_handle_base<PolicyTraits, Alloc>;
using slot_type = typename PolicyTraits::slot_type;
public:
using key_type = typename Policy::key_type;
@@ -3888,7 +3889,7 @@ public:
constexpr size_t Offset() const {
static_assert(N < NumOffsets, "Index out of bounds");
return adl_barrier::Align(
Offset<N - 1>() + SizeOf<ElementType<N - 1>>() * size_[N - 1],
Offset<N - 1>() + SizeOf<ElementType<N - 1>>::value * size_[N - 1],
ElementAlignment<N>::value);
}
@@ -4081,7 +4082,7 @@ public:
constexpr size_t AllocSize() const {
static_assert(NumTypes == NumSizes, "You must specify sizes of all fields");
return Offset<NumTypes - 1>() +
SizeOf<ElementType<NumTypes - 1>>() * size_[NumTypes - 1];
SizeOf<ElementType<NumTypes - 1>>::value * size_[NumTypes - 1];
}
// If built with --config=asan, poisons padding bytes (if any) in the
@@ -4105,7 +4106,7 @@ public:
// The `if` is an optimization. It doesn't affect the observable behaviour.
if (ElementAlignment<N - 1>::value % ElementAlignment<N>::value) {
size_t start =
Offset<N - 1>() + SizeOf<ElementType<N - 1>>() * size_[N - 1];
Offset<N - 1>() + SizeOf<ElementType<N - 1>>::value * size_[N - 1];
ASAN_POISON_MEMORY_REGION(p + start, Offset<N>() - start);
}
#endif
+13 -12
View File
@@ -315,19 +315,19 @@ PHMAP_BASE_INTERNAL_FORCEINLINE int CountLeadingZeros64(uint64_t n) {
#endif
}
PHMAP_BASE_INTERNAL_FORCEINLINE int CountLeadingZeros32Slow(uint64_t n) {
int zeroes = 28;
PHMAP_BASE_INTERNAL_FORCEINLINE uint32_t CountLeadingZeros32Slow(uint64_t n) {
uint32_t zeroes = 28;
if (n >> 16) zeroes -= 16, n >>= 16;
if (n >> 8) zeroes -= 8, n >>= 8;
if (n >> 4) zeroes -= 4, n >>= 4;
return "\4\3\2\2\1\1\1\1\0\0\0\0\0\0\0"[n] + zeroes;
}
PHMAP_BASE_INTERNAL_FORCEINLINE int CountLeadingZeros32(uint32_t n) {
PHMAP_BASE_INTERNAL_FORCEINLINE uint32_t CountLeadingZeros32(uint32_t n) {
#if defined(_MSC_VER) && !defined(__clang__)
unsigned long result = 0; // NOLINT(runtime/int)
if (_BitScanReverse(&result, n)) {
return (int)(31 - result);
return (uint32_t)(31 - result);
}
return 32;
#elif defined(__GNUC__) || defined(__clang__)
@@ -348,8 +348,8 @@ PHMAP_BASE_INTERNAL_FORCEINLINE int CountLeadingZeros32(uint32_t n) {
#endif
}
PHMAP_BASE_INTERNAL_FORCEINLINE int CountTrailingZerosNonZero64Slow(uint64_t n) {
int c = 63;
PHMAP_BASE_INTERNAL_FORCEINLINE uint32_t CountTrailingZerosNonZero64Slow(uint64_t n) {
uint32_t c = 63;
n &= ~n + 1;
if (n & 0x00000000FFFFFFFF) c -= 32;
if (n & 0x0000FFFF0000FFFF) c -= 16;
@@ -360,11 +360,11 @@ PHMAP_BASE_INTERNAL_FORCEINLINE int CountTrailingZerosNonZero64Slow(uint64_t n)
return c;
}
PHMAP_BASE_INTERNAL_FORCEINLINE int CountTrailingZerosNonZero64(uint64_t n) {
PHMAP_BASE_INTERNAL_FORCEINLINE uint32_t CountTrailingZerosNonZero64(uint64_t n) {
#if defined(_MSC_VER) && !defined(__clang__) && defined(_M_X64)
unsigned long result = 0; // NOLINT(runtime/int)
_BitScanForward64(&result, n);
return (int)result;
return (uint32_t)result;
#elif defined(_MSC_VER) && !defined(__clang__)
unsigned long result = 0; // NOLINT(runtime/int)
if (static_cast<uint32_t>(n) == 0) {
@@ -382,8 +382,8 @@ PHMAP_BASE_INTERNAL_FORCEINLINE int CountTrailingZerosNonZero64(uint64_t n) {
#endif
}
PHMAP_BASE_INTERNAL_FORCEINLINE int CountTrailingZerosNonZero32Slow(uint32_t n) {
int c = 31;
PHMAP_BASE_INTERNAL_FORCEINLINE uint32_t CountTrailingZerosNonZero32Slow(uint32_t n) {
uint32_t c = 31;
n &= ~n + 1;
if (n & 0x0000FFFF) c -= 16;
if (n & 0x00FF00FF) c -= 8;
@@ -393,11 +393,11 @@ PHMAP_BASE_INTERNAL_FORCEINLINE int CountTrailingZerosNonZero32Slow(uint32_t n)
return c;
}
PHMAP_BASE_INTERNAL_FORCEINLINE int CountTrailingZerosNonZero32(uint32_t n) {
PHMAP_BASE_INTERNAL_FORCEINLINE uint32_t CountTrailingZerosNonZero32(uint32_t n) {
#if defined(_MSC_VER) && !defined(__clang__)
unsigned long result = 0; // NOLINT(runtime/int)
_BitScanForward(&result, n);
return (int)result;
return (uint32_t)result;
#elif defined(__GNUC__) || defined(__clang__)
static_assert(sizeof(int) == sizeof(n),
"__builtin_ctz does not take 32-bit arg");
@@ -568,6 +568,7 @@ namespace little_endian {
#endif /* ENDIAN */
// Functions to do unaligned loads and stores in little-endian order.
// ------------------------------------------------------------------
inline uint16_t Load16(const void *p) {
return ToHost16(PHMAP_INTERNAL_UNALIGNED_LOAD16(p));
}
+23 -5
View File
@@ -128,15 +128,33 @@
#define PHMAP_BRANCHLESS 1
#ifdef __has_feature
#define PHMAP_HAVE_FEATURE(f) __has_feature(f)
#else
#define PHMAP_HAVE_FEATURE(f) 0
#endif
// Portable check for GCC minimum version:
// https://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html
#if defined(__GNUC__) && defined(__GNUC_MINOR__)
#define PHMAP_INTERNAL_HAVE_MIN_GNUC_VERSION(x, y) (__GNUC__ > (x) || __GNUC__ == (x) && __GNUC_MINOR__ >= (y))
#else
#define PHMAP_INTERNAL_HAVE_MIN_GNUC_VERSION(x, y) 0
#endif
#if defined(__clang__) && defined(__clang_major__) && defined(__clang_minor__)
#define PHMAP_INTERNAL_HAVE_MIN_CLANG_VERSION(x, y) (__clang_major__ > (x) || __clang_major__ == (x) && __clang_minor__ >= (y))
#else
#define PHMAP_INTERNAL_HAVE_MIN_CLANG_VERSION(x, y) 0
#endif
// ----------------------------------------------------------------
// Checks whether `std::is_trivially_destructible<T>` is supported.
// ----------------------------------------------------------------
#ifdef PHMAP_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE
#error PHMAP_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE cannot be directly set
#elif defined(_LIBCPP_VERSION) || \
(!defined(__clang__) && defined(__GNUC__) && defined(__GLIBCXX__) && \
(__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) || \
defined(_MSC_VER)
#elif defined(_LIBCPP_VERSION) || defined(_MSC_VER) || \
(!defined(__clang__) && defined(__GNUC__) && defined(__GLIBCXX__) && PHMAP_INTERNAL_HAVE_MIN_GNUC_VERSION(4, 8))
#define PHMAP_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE 1
#endif
@@ -150,7 +168,7 @@
#error PHMAP_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE cannot directly set
#elif (defined(__clang__) && defined(_LIBCPP_VERSION)) || \
(!defined(__clang__) && defined(__GNUC__) && \
(__GNUC__ > 5 || (__GNUC__ == 5 && __GNUC_MINOR__ >= 1)) && \
PHMAP_INTERNAL_HAVE_MIN_GNUC_VERSION(5, 1) && \
(defined(_LIBCPP_VERSION) || defined(__GLIBCXX__))) || \
(defined(_MSC_VER) && !defined(__NVCC__))
#define PHMAP_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE 1