[llvm] [DenseMap] Do not align pointer sentinel values (NFC) (PR #146595)
Nikita Popov via llvm-commits
llvm-commits at lists.llvm.org
Tue Jul 1 13:15:15 PDT 2025
https://github.com/nikic created https://github.com/llvm/llvm-project/pull/146595
DenseMapInfo for pointers currently uses empty/tombstone values that are aligned (by assuming a very conservative alignment). However, this means that we have to work with larger immediates.
This patch proposes to use the values -1 and -2 instead, without caring about pointer alignment. This puts us into unspecified territory from the perspective of the C standard, but general implementer consensus (including Clang) is that raw pointers do not carry alignment guarantees, only memory accesses do.
We already have lots of places that rely on this using variations on `reinterpret_cast<T*>(-1)`, so it seems odd to insist on strict standard conformance in this one place.
This is a small improvement for both compile-time and clang binary size: https://llvm-compile-time-tracker.com/compare.php?from=b8b74945514358f1ebeac0874172f37fc7bfa38e&to=3c83bf05608d720d6ff88adfaca7191331eeb30f&stat=instructions:u
>From 8bfa43c61c47c978ddcba509f22cf0a605b83fa0 Mon Sep 17 00:00:00 2001
From: Nikita Popov <npopov at redhat.com>
Date: Tue, 1 Jul 2025 13:51:43 +0200
Subject: [PATCH] [DenseMap] Do not align pointer sentinel values (NFC)
DenseMapInfo for pointers currently uses empty/tombstone values
that are aligned (by assuming a very conservative alignment).
However, this means that we have to work with large immediates.
This patch proposed to use the values -1 and -2 instead, without
caring about pointer alignment. This puts us into unspecified
territory from the perspective of the C standard, but it is
Clang's explicit position that raw pointers do not carry alignment
guarantees (only accesses do).
We already have lots of places that do variations on
`reinterpret_cast<T*>(-1)` and assume that to work, so I'm not
sure it's worthwhile to be strictly standards conforming in this
single place.
---
llvm/include/llvm/ADT/DenseMapInfo.h | 25 ++++---------------------
llvm/include/llvm/ADT/PointerUnion.h | 14 ++++++++++----
2 files changed, 14 insertions(+), 25 deletions(-)
diff --git a/llvm/include/llvm/ADT/DenseMapInfo.h b/llvm/include/llvm/ADT/DenseMapInfo.h
index 07c37e353a40b..b371611e7d948 100644
--- a/llvm/include/llvm/ADT/DenseMapInfo.h
+++ b/llvm/include/llvm/ADT/DenseMapInfo.h
@@ -56,30 +56,13 @@ struct DenseMapInfo {
//static bool isEqual(const T &LHS, const T &RHS);
};
-// Provide DenseMapInfo for all pointers. Come up with sentinel pointer values
-// that are aligned to alignof(T) bytes, but try to avoid requiring T to be
-// complete. This allows clients to instantiate DenseMap<T*, ...> with forward
-// declared key types. Assume that no pointer key type requires more than 4096
-// bytes of alignment.
-template<typename T>
-struct DenseMapInfo<T*> {
- // The following should hold, but it would require T to be complete:
- // static_assert(alignof(T) <= (1 << Log2MaxAlign),
- // "DenseMap does not support pointer keys requiring more than "
- // "Log2MaxAlign bits of alignment");
- static constexpr uintptr_t Log2MaxAlign = 12;
-
+template <typename T> struct DenseMapInfo<T *> {
static inline T* getEmptyKey() {
- uintptr_t Val = static_cast<uintptr_t>(-1);
- Val <<= Log2MaxAlign;
- return reinterpret_cast<T*>(Val);
+ // We assume that raw pointers do not carry alignment requirements.
+ return reinterpret_cast<T *>(-1);
}
- static inline T* getTombstoneKey() {
- uintptr_t Val = static_cast<uintptr_t>(-2);
- Val <<= Log2MaxAlign;
- return reinterpret_cast<T*>(Val);
- }
+ static inline T *getTombstoneKey() { return reinterpret_cast<T *>(-2); }
static unsigned getHashValue(const T *PtrVal) {
return (unsigned((uintptr_t)PtrVal) >> 4) ^
diff --git a/llvm/include/llvm/ADT/PointerUnion.h b/llvm/include/llvm/ADT/PointerUnion.h
index cdbd76d7f505b..86248a2cf836f 100644
--- a/llvm/include/llvm/ADT/PointerUnion.h
+++ b/llvm/include/llvm/ADT/PointerUnion.h
@@ -286,13 +286,19 @@ struct PointerLikeTypeTraits<PointerUnion<PTs...>> {
// Teach DenseMap how to use PointerUnions as keys.
template <typename ...PTs> struct DenseMapInfo<PointerUnion<PTs...>> {
using Union = PointerUnion<PTs...>;
- using FirstInfo =
- DenseMapInfo<typename pointer_union_detail::GetFirstType<PTs...>::type>;
+ using FirstTypeTraits = PointerLikeTypeTraits<
+ typename pointer_union_detail::GetFirstType<PTs...>::type>;
- static inline Union getEmptyKey() { return Union(FirstInfo::getEmptyKey()); }
+ static inline Union getEmptyKey() {
+ uintptr_t Val = static_cast<uintptr_t>(-1);
+ Val <<= FirstTypeTraits::NumLowBitsAvailable;
+ return FirstTypeTraits::getFromVoidPointer(reinterpret_cast<void *>(Val));
+ }
static inline Union getTombstoneKey() {
- return Union(FirstInfo::getTombstoneKey());
+ uintptr_t Val = static_cast<uintptr_t>(-2);
+ Val <<= FirstTypeTraits::NumLowBitsAvailable;
+ return FirstTypeTraits::getFromVoidPointer(reinterpret_cast<void *>(Val));
}
static unsigned getHashValue(const Union &UnionVal) {
More information about the llvm-commits
mailing list