From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from simark.ca by simark.ca with LMTP id rs59BTqJhGp6EC8AWB0awg (envelope-from ) for ; Tue, 18 Aug 2026 12:32:58 -0400 Received: by simark.ca (Postfix, from userid 112) id 036181E033; Tue, 18 Aug 2026 12:32:58 -0400 (EDT) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-25) on simark.ca X-Spam-Level: X-Spam-Status: No, score=-5.3 required=5.0 tests=ARC_SIGNED,ARC_VALID,BAYES_00, MAILING_LIST_MULTI,RCVD_IN_DNSWL_MED autolearn=ham autolearn_force=no version=4.0.1 Received: from vm01.sourceware.org (vm01.sourceware.org [IPv6:2620:52:6:3111::32]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange x25519 server-signature ECDSA (prime256v1) server-digest SHA256) (No client certificate requested) by simark.ca (Postfix) with ESMTPS id 07D801E033 for ; Tue, 18 Aug 2026 12:32:55 -0400 (EDT) Received: from vm01.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id B174A4BA799C for ; Tue, 18 Aug 2026 16:32:53 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org B174A4BA799C Received: from simark.ca (simark.ca [158.69.221.121]) by sourceware.org (Postfix) with ESMTPS id 0BB4B4BA2E26 for ; Tue, 18 Aug 2026 16:32:17 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 0BB4B4BA2E26 Authentication-Results: sourceware.org; dmarc=fail (p=none dis=none) header.from=efficios.com Authentication-Results: sourceware.org; spf=fail smtp.mailfrom=efficios.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 0BB4B4BA2E26 Authentication-Results: sourceware.org; arc=none smtp.remote-ip=158.69.221.121 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1787070737; cv=none; b=PgRBPJxKc4cXOChmehpAqv5TiEP9c7c186Xi+mBetrNSK642MvpgNxGMeVhjgv2v03V39c6Au0qm3NIhELCK4lFcUWTUHpYyCWKm4i5B2WqvJkZyXtR/Jf9dJk/qsmqLUWo8S6YSwvnLRldXR1gNO9CCwlHeKLsKl0k/1yk8TJ8= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1787070737; c=relaxed/simple; bh=FsZ7xWtDx+Hst46qRkC3CzZSBVEpFLsZEcSR6TZXt7c=; h=From:To:Subject:Date:Message-ID:MIME-Version; b=F2SAJLcNO9FxoGOREOTjvQm6i+Y+2LmRWcyD6V8bI/VVlIfnJySg8M8doogvNQhz4aihesUP1vNvaYUrzgTXLVUWIZn3WTSiNSr8z2fOecJHc7vpXYJK3YYSicTOEiJOa+UhgI55ZzOiU5sjoXsPcyJiByb25dtgu12QB8ZiwY8= ARC-Authentication-Results: i=1; sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 0BB4B4BA2E26 Received: by simark.ca (Postfix) id 303B61E033; Tue, 18 Aug 2026 12:32:15 -0400 (EDT) From: Simon Marchi To: gdb-patches@sourceware.org Cc: Simon Marchi Subject: [PATCH] gdbsupport: upgrade unordered_dense to v4.9.2 Date: Tue, 18 Aug 2026 12:31:59 -0400 Message-ID: <20260818163214.77765-1-simon.marchi@efficios.com> X-Mailer: git-send-email 2.55.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gdb-patches-bounces~public-inbox=simark.ca@sourceware.org Synchronize with upstream [1], tag v4.9.2 (commit aa9af528227ab). There is one change I am particularly happy about (even though it doesn't change anything functionally), because I did file the issue for that: 👉 A default-constructed map allocates nothing, and copying or move- assigning an empty one drops two further allocations. Ppotentially interesting new features: 👉 map.hash_for(key) returns a precomputed hash that find, contains, count, equal_range and at now accept, so it's now possible for repeated lookups of the same key to hash it only once. 👉 hash_is_avalanching can be used to mark a third-party hash as high quality without editing it, or as false_type to force mixing on a hash that overpromises. The only way to mark a hash as avalanching before that was to define the is_avalanching type in the hash type itself. Behaviour changes that could affect us: 👉 The is_avalanching marker is now read as a compile-time bool, the way Boost.Unordered reads it. "using is_avalanching = std::false_type;" previously meant yes here and now correctly means no. "= void" still works. Anything else is now a compile error rather than a guess. There are some claimed performance improvements, and some bug fixes, but nothing that I could see for things we actually use. I did a test run on Linux, didn't spot any regression. [1] https://github.com/martinus/unordered_dense/ Change-Id: I8b2dda1a74b59f78a7dcc9f1b1931a03e8781393 --- gdbsupport/unordered_dense/stl.h | 8 +- gdbsupport/unordered_dense/unordered_dense.h | 1037 +++++++++++++++--- 2 files changed, 873 insertions(+), 172 deletions(-) diff --git a/gdbsupport/unordered_dense/stl.h b/gdbsupport/unordered_dense/stl.h index 8c198773195e..2389d0c8cf0a 100644 --- a/gdbsupport/unordered_dense/stl.h +++ b/gdbsupport/unordered_dense/stl.h @@ -1,12 +1,12 @@ ///////////////////////// ankerl::unordered_dense::{map, set} ///////////////////////// // A fast & densely stored hashmap and hashset based on robin-hood backward shift deletion. -// Version 4.8.0 +// Version 4.9.2 // https://github.com/martinus/unordered_dense // // Licensed under the MIT License . // SPDX-License-Identifier: MIT -// Copyright (c) 2022-2024 Martin Leitner-Ankerl +// Copyright (c) 2022 Martin Leitner-Ankerl // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -77,7 +77,9 @@ #if defined(_MSC_VER) && defined(_M_X64) # include -# pragma intrinsic(_umul128) +# if !defined(_M_ARM64EC) +# pragma intrinsic(_umul128) +# endif #endif #endif diff --git a/gdbsupport/unordered_dense/unordered_dense.h b/gdbsupport/unordered_dense/unordered_dense.h index 56383b6ce8f7..58bb57da12ca 100644 --- a/gdbsupport/unordered_dense/unordered_dense.h +++ b/gdbsupport/unordered_dense/unordered_dense.h @@ -1,12 +1,12 @@ ///////////////////////// ankerl::unordered_dense::{map, set} ///////////////////////// // A fast & densely stored hashmap and hashset based on robin-hood backward shift deletion. -// Version 4.8.0 +// Version 4.9.2 // https://github.com/martinus/unordered_dense // // Licensed under the MIT License . // SPDX-License-Identifier: MIT -// Copyright (c) 2022-2024 Martin Leitner-Ankerl +// Copyright (c) 2022 Martin Leitner-Ankerl // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -31,8 +31,8 @@ // see https://semver.org/spec/v2.0.0.html #define ANKERL_UNORDERED_DENSE_VERSION_MAJOR 4 // NOLINT(cppcoreguidelines-macro-usage) incompatible API changes -#define ANKERL_UNORDERED_DENSE_VERSION_MINOR 8 // NOLINT(cppcoreguidelines-macro-usage) backwards compatible functionality -#define ANKERL_UNORDERED_DENSE_VERSION_PATCH 0 // NOLINT(cppcoreguidelines-macro-usage) backwards compatible bug fixes +#define ANKERL_UNORDERED_DENSE_VERSION_MINOR 9 // NOLINT(cppcoreguidelines-macro-usage) backwards compatible functionality +#define ANKERL_UNORDERED_DENSE_VERSION_PATCH 2 // NOLINT(cppcoreguidelines-macro-usage) backwards compatible bug fixes // API versioning with inline namespace, see https://www.foonathan.net/2018/11/inline-namespaces/ @@ -70,6 +70,13 @@ # define ANKERL_UNORDERED_DENSE_NOINLINE __attribute__((noinline)) #endif +// data prefetch hint, a no-op when not supported +#if defined(__GNUC__) || defined(__clang__) +# define ANKERL_UNORDERED_DENSE_PREFETCH(addr) __builtin_prefetch(addr) // NOLINT(cppcoreguidelines-macro-usage) +#else +# define ANKERL_UNORDERED_DENSE_PREFETCH(addr) static_cast(addr) // NOLINT(cppcoreguidelines-macro-usage) +#endif + #if defined(__clang__) && defined(__has_attribute) # if __has_attribute(__no_sanitize__) # define ANKERL_UNORDERED_DENSE_DISABLE_UBSAN_UNSIGNED_INTEGER_CHECK \ @@ -212,28 +219,35 @@ inline void mum(std::uint64_t* a, std::uint64_t* b) { static constexpr auto secret = std::array{UINT64_C(0xa0761d6478bd642f), UINT64_C(0xe7037ed1a0b428db), UINT64_C(0x8ebc6af09c88c6e3), - UINT64_C(0x589965cc75374cc3)}; + UINT64_C(0x589965cc75374cc3), + UINT64_C(0x2d358dccaa6c78a5), + UINT64_C(0x8bb84b93962eacc9), + UINT64_C(0x4b33a62ed433d4a3)}; - auto const* p = static_cast(key); + auto const* p = static_cast(key); std::uint64_t seed = secret[0]; std::uint64_t a{}; std::uint64_t b{}; if (ANKERL_UNORDERED_DENSE_LIKELY(len <= 16)) ANKERL_UNORDERED_DENSE_LIKELY_ATTR { - if (ANKERL_UNORDERED_DENSE_LIKELY(len >= 4)) + if (ANKERL_UNORDERED_DENSE_LIKELY(len >= 8)) ANKERL_UNORDERED_DENSE_LIKELY_ATTR { - a = (r4(p) << 32U) | r4(p + ((len >> 3U) << 2U)); - b = (r4(p + len - 4) << 32U) | r4(p + len - 4 - ((len >> 3U) << 2U)); + // two (potentially overlapping) 8 byte reads cover the whole input + a = r8(p); + b = r8(p + len - 8); } - else if (ANKERL_UNORDERED_DENSE_LIKELY(len > 0)) + else if (len >= 4) { + a = r4(p); + b = r4(p + len - 4); + } else if (ANKERL_UNORDERED_DENSE_LIKELY(len > 0)) ANKERL_UNORDERED_DENSE_LIKELY_ATTR { + // b stays zero: r3 packs all len bytes it is given into a, and there are at + // most three of them. a = r3(p, len); - b = 0; } - else { - a = 0; - b = 0; - } + // ... and an empty input needs no branch of its own: it hashes whatever a and b were + // declared with, which is the zero it has to be. Assigning it again here is what a + // deletion sweep of this file kept pointing at. } else { std::size_t i = len; @@ -241,14 +255,41 @@ inline void mum(std::uint64_t* a, std::uint64_t* b) { ANKERL_UNORDERED_DENSE_UNLIKELY_ATTR { std::uint64_t see1 = seed; std::uint64_t see2 = seed; - do { + if (i > 96) { + // 6 independent lanes: twice the instruction level parallelism of the 48 byte loop below + std::uint64_t see3 = seed; + std::uint64_t see4 = seed; + std::uint64_t see5 = seed; + do { + seed = mix(r8(p) ^ secret[1], r8(p + 8) ^ seed); + see1 = mix(r8(p + 16) ^ secret[2], r8(p + 24) ^ see1); + see2 = mix(r8(p + 32) ^ secret[3], r8(p + 40) ^ see2); + see3 = mix(r8(p + 48) ^ secret[4], r8(p + 56) ^ see3); + see4 = mix(r8(p + 64) ^ secret[5], r8(p + 72) ^ see4); + see5 = mix(r8(p + 80) ^ secret[6], r8(p + 88) ^ see5); + p += 96; + i -= 96; + } while (ANKERL_UNORDERED_DENSE_LIKELY(i > 96)); + seed ^= see3 ^ see4 ^ see5; + } + while (i > 48) { seed = mix(r8(p) ^ secret[1], r8(p + 8) ^ seed); see1 = mix(r8(p + 16) ^ secret[2], r8(p + 24) ^ see1); see2 = mix(r8(p + 32) ^ secret[3], r8(p + 40) ^ see2); p += 48; i -= 48; - } while (ANKERL_UNORDERED_DENSE_LIKELY(i > 48)); + } seed ^= see1 ^ see2; + while (i > 16) { + seed = mix(r8(p) ^ secret[1], r8(p + 8) ^ seed); + i -= 16; + p += 16; + } + + // the tail lane only depends on the input, not on seed, so it can execute in parallel + // with the lane loops above, and a single dependent mix finishes the hash + auto tail = mix(r8(p + i - 16) ^ secret[2], r8(p + i - 8) ^ secret[3]); + return mix(secret[1] ^ len, seed ^ tail); } while (ANKERL_UNORDERED_DENSE_UNLIKELY(i > 16)) ANKERL_UNORDERED_DENSE_UNLIKELY_ATTR { @@ -269,6 +310,85 @@ inline void mum(std::uint64_t* a, std::uint64_t* b) { } // namespace detail::wyhash +namespace detail { + +struct nonesuch {}; + +template class Op, class... Args> +struct detector { + using value_t = std::false_type; + using type = Default; +}; + +template class Op, class... Args> +struct detector>, Op, Args...> { + using value_t = std::true_type; + using type = Op; +}; + +template