1 // Copyright 2022 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #ifndef PARTITION_ALLOC_COMPRESSED_POINTER_H_
6 #define PARTITION_ALLOC_COMPRESSED_POINTER_H_
7
8 #include <bit>
9 #include <climits>
10 #include <type_traits>
11
12 #include "partition_alloc/partition_address_space.h"
13 #include "partition_alloc/partition_alloc_base/compiler_specific.h"
14 #include "partition_alloc/partition_alloc_base/component_export.h"
15 #include "partition_alloc/partition_alloc_buildflags.h"
16
17 #if BUILDFLAG(ENABLE_POINTER_COMPRESSION)
18
19 #if !BUILDFLAG(GLUE_CORE_POOLS)
20 #error "Pointer compression only works with glued pools"
21 #endif
22 #if PA_CONFIG(DYNAMICALLY_SELECT_POOL_SIZE)
23 #error "Pointer compression currently supports constant pool size"
24 #endif
25
26 #endif // BUILDFLAG(ENABLE_POINTER_COMPRESSION)
27
28 namespace partition_alloc {
29
30 namespace internal {
31
32 template <typename T1, typename T2>
33 constexpr bool IsDecayedSame =
34 std::is_same_v<std::decay_t<T1>, std::decay_t<T2>>;
35
36 #if BUILDFLAG(ENABLE_POINTER_COMPRESSION)
37
38 // Pointer compression works by storing only the 'useful' 32-bit part of the
39 // pointer. The other half (the base) is stored in a global variable
40 // (CompressedPointerBaseGlobal::g_base_), which is used on decompression. To
41 // support fast branchless decompression of nullptr, we use the most significant
42 // bit in the compressed pointer to leverage sign-extension (for non-nullptr
43 // pointers, the most significant bit is set, whereas for nullptr it's not).
44 // Using this bit and supporting heaps larger than 4GB relies on having
45 // alignment bits in pointers. Assuming that all pointers point to at least
46 // 8-byte alignment objects, pointer compression can support heaps of size <=
47 // 16GB.
48 // ((3 alignment bits) = (1 bit for sign-extension) + (2 bits for 16GB heap)).
49 //
50 // Example: heap base: 0x4b0'ffffffff
51 // - g_base: 0x4b3'ffffffff (lower 34 bits set)
52 // - normal pointer: 0x4b2'a08b6480
53 // - compression:
54 // - shift right by 3: 0x96'54116c90
55 // - truncate: 0x54116c90
56 // - mark MSB: 0xd4116c90
57 // - decompression:
58 // - sign-extend: 0xffffffff'd4116c90
59 // - shift left by 3: 0xfffffffe'a08b6480
60 // - 'and' with g_base: 0x000004b2'a08b6480
61 //
62 // - nullptr: 0x00000000'00000000
63 // - compression:
64 // - shift right by 3: 0x00000000'00000000
65 // - truncate: 0x00000000
66 // - (don't mark MSB for nullptr)
67 // - decompression:
68 // - sign-extend: 0x00000000'00000000
69 // - shift left by 3: 0x00000000'00000000
70 // - 'and' with g_base: 0x00000000'00000000
71 //
72 // Pointer compression relies on having both the regular and the BRP pool (core
73 // pools) 'glued', so that the same base could be used for both. For simplicity,
74 // the configurations with dynamically selected pool size are not supported.
75 // However, they can be at the cost of performing an extra load for
76 // core-pools-shift-size on both compression and decompression.
77
78 class CompressedPointerBaseGlobal final {
79 public:
80 static constexpr size_t kUsefulBits =
81 std::countr_zero(PartitionAddressSpace::CorePoolsSize());
82 static_assert(kUsefulBits >= sizeof(uint32_t) * CHAR_BIT);
83 static constexpr size_t kBitsToShift =
84 kUsefulBits - sizeof(uint32_t) * CHAR_BIT;
85
86 CompressedPointerBaseGlobal() = delete;
87
88 // Attribute const allows the compiler to assume that
89 // CompressedPointerBaseGlobal::g_base_ doesn't change (e.g. across calls) and
90 // thereby avoid redundant loads.
Get()91 PA_ALWAYS_INLINE __attribute__((const)) static uintptr_t Get() {
92 PA_DCHECK(IsBaseConsistent());
93 return g_base_.base;
94 }
95
IsSet()96 PA_ALWAYS_INLINE static bool IsSet() {
97 PA_DCHECK(IsBaseConsistent());
98 return (g_base_.base & ~kUsefulBitsMask) != 0;
99 }
100
101 private:
102 static constexpr uintptr_t kUsefulBitsMask =
103 PartitionAddressSpace::CorePoolsSize() - 1;
104
105 static union alignas(kPartitionCachelineSize)
PA_COMPONENT_EXPORT(PARTITION_ALLOC)106 PA_COMPONENT_EXPORT(PARTITION_ALLOC) Base {
107 uintptr_t base;
108 char cache_line[kPartitionCachelineSize];
109 } g_base_ PA_CONSTINIT;
110
IsBaseConsistent()111 PA_ALWAYS_INLINE static bool IsBaseConsistent() {
112 return kUsefulBitsMask == (g_base_.base & kUsefulBitsMask);
113 }
114
115 static void SetBase(uintptr_t base);
116 static void ResetBaseForTesting();
117
118 friend class PartitionAddressSpace;
119 };
120
121 #endif // BUILDFLAG(ENABLE_POINTER_COMPRESSION)
122
123 } // namespace internal
124
125 #if BUILDFLAG(ENABLE_POINTER_COMPRESSION)
126
127 template <typename T>
128 class PA_TRIVIAL_ABI CompressedPointer final {
129 public:
130 using UnderlyingType = uint32_t;
131
132 PA_ALWAYS_INLINE constexpr CompressedPointer() = default;
CompressedPointer(T * ptr)133 PA_ALWAYS_INLINE explicit CompressedPointer(T* ptr) : value_(Compress(ptr)) {}
CompressedPointer(std::nullptr_t)134 PA_ALWAYS_INLINE constexpr explicit CompressedPointer(std::nullptr_t)
135 : value_(0u) {}
136
137 PA_ALWAYS_INLINE constexpr CompressedPointer(const CompressedPointer&) =
138 default;
139 PA_ALWAYS_INLINE constexpr CompressedPointer(
140 CompressedPointer&& other) noexcept = default;
141
142 template <typename U,
143 std::enable_if_t<std::is_convertible_v<U*, T*>>* = nullptr>
CompressedPointer(const CompressedPointer<U> & other)144 PA_ALWAYS_INLINE constexpr CompressedPointer(
145 const CompressedPointer<U>& other) {
146 if constexpr (internal::IsDecayedSame<T, U>) {
147 // When pointers have the same type modulo constness, avoid the
148 // compress-decompress round.
149 value_ = other.value_;
150 } else {
151 // When the types are different, perform the round, because the pointer
152 // may need to be adjusted.
153 // TODO(1376980): Avoid the cycle here.
154 value_ = Compress(other.get());
155 }
156 }
157
158 template <typename U,
159 std::enable_if_t<std::is_convertible_v<U*, T*>>* = nullptr>
CompressedPointer(CompressedPointer<U> && other)160 PA_ALWAYS_INLINE constexpr CompressedPointer(
161 CompressedPointer<U>&& other) noexcept
162 : CompressedPointer(other) {}
163
164 ~CompressedPointer() = default;
165
166 PA_ALWAYS_INLINE constexpr CompressedPointer& operator=(
167 const CompressedPointer&) = default;
168 PA_ALWAYS_INLINE constexpr CompressedPointer& operator=(
169 CompressedPointer&& other) noexcept = default;
170
171 template <typename U,
172 std::enable_if_t<std::is_convertible_v<U*, T*>>* = nullptr>
173 PA_ALWAYS_INLINE constexpr CompressedPointer& operator=(
174 const CompressedPointer<U>& other) {
175 CompressedPointer copy(other);
176 value_ = copy.value_;
177 return *this;
178 }
179
180 template <typename U,
181 std::enable_if_t<std::is_convertible_v<U*, T*>>* = nullptr>
182 PA_ALWAYS_INLINE constexpr CompressedPointer& operator=(
183 CompressedPointer<U>&& other) noexcept {
184 *this = other;
185 return *this;
186 }
187
188 // Don't perform compression when assigning to nullptr.
189 PA_ALWAYS_INLINE constexpr CompressedPointer& operator=(std::nullptr_t) {
190 value_ = 0u;
191 return *this;
192 }
193
get()194 PA_ALWAYS_INLINE T* get() const { return Decompress(value_); }
195
is_nonnull()196 PA_ALWAYS_INLINE constexpr bool is_nonnull() const { return value_; }
197
GetAsIntegral()198 PA_ALWAYS_INLINE constexpr UnderlyingType GetAsIntegral() const {
199 return value_;
200 }
201
202 PA_ALWAYS_INLINE constexpr explicit operator bool() const {
203 return is_nonnull();
204 }
205
206 template <typename U = T,
207 std::enable_if_t<!std::is_void_v<std::remove_cv_t<U>>>* = nullptr>
208 PA_ALWAYS_INLINE U& operator*() const {
209 PA_DCHECK(is_nonnull());
210 return *get();
211 }
212
213 PA_ALWAYS_INLINE T* operator->() const {
214 PA_DCHECK(is_nonnull());
215 return get();
216 }
217
swap(CompressedPointer & other)218 PA_ALWAYS_INLINE constexpr void swap(CompressedPointer& other) {
219 std::swap(value_, other.value_);
220 }
221
222 private:
223 template <typename>
224 friend class CompressedPointer;
225
226 static constexpr size_t kBitsForSignExtension = 1;
227 static constexpr size_t kOverallBitsToShift =
228 internal::CompressedPointerBaseGlobal::kBitsToShift +
229 kBitsForSignExtension;
230
Compress(T * ptr)231 PA_ALWAYS_INLINE static UnderlyingType Compress(T* ptr) {
232 static constexpr size_t kMinimalRequiredAlignment = 8;
233 static_assert((1 << kOverallBitsToShift) == kMinimalRequiredAlignment);
234
235 #if BUILDFLAG(PA_DCHECK_IS_ON)
236 PA_DCHECK(reinterpret_cast<uintptr_t>(ptr) % kMinimalRequiredAlignment ==
237 0);
238 PA_DCHECK(internal::CompressedPointerBaseGlobal::IsSet());
239
240 const uintptr_t base = internal::CompressedPointerBaseGlobal::Get();
241 static constexpr size_t kCorePoolsBaseMask =
242 ~(internal::PartitionAddressSpace::CorePoolsSize() - 1);
243 PA_DCHECK(!ptr ||
244 (base & kCorePoolsBaseMask) ==
245 (reinterpret_cast<uintptr_t>(ptr) & kCorePoolsBaseMask));
246 #endif // BUILDFLAG(PA_DCHECK_IS_ON)
247
248 const auto uptr = reinterpret_cast<uintptr_t>(ptr);
249 // Shift the pointer and truncate.
250 auto compressed = static_cast<UnderlyingType>(uptr >> kOverallBitsToShift);
251 // If the pointer is non-null, mark the most-significant-bit to sign-extend
252 // it on decompression. Assuming compression is a significantly less
253 // frequent operation, we let more work here in favor of faster
254 // decompression.
255 // TODO(1376980): Avoid this by overreserving the heap.
256 if (compressed) {
257 compressed |= (1u << (sizeof(uint32_t) * CHAR_BIT - 1));
258 }
259
260 return compressed;
261 }
262
Decompress(UnderlyingType ptr)263 PA_ALWAYS_INLINE static T* Decompress(UnderlyingType ptr) {
264 PA_DCHECK(internal::CompressedPointerBaseGlobal::IsSet());
265 const uintptr_t base = internal::CompressedPointerBaseGlobal::Get();
266 // Treat compressed pointer as signed and cast it to uint64_t, which will
267 // sign-extend it. Then, shift the result by one. It's important to shift
268 // the already unsigned value, as otherwise it would result in undefined
269 // behavior.
270 const uint64_t mask = static_cast<uint64_t>(static_cast<int32_t>(ptr))
271 << (kOverallBitsToShift);
272 return reinterpret_cast<T*>(mask & base);
273 }
274
275 UnderlyingType value_;
276 };
277
278 template <typename T>
swap(CompressedPointer<T> & a,CompressedPointer<T> & b)279 PA_ALWAYS_INLINE constexpr void swap(CompressedPointer<T>& a,
280 CompressedPointer<T>& b) {
281 a.swap(b);
282 }
283
284 // operators==.
285 template <typename T, typename U>
286 PA_ALWAYS_INLINE bool operator==(CompressedPointer<T> a,
287 CompressedPointer<U> b) {
288 if constexpr (internal::IsDecayedSame<T, U>) {
289 // When pointers have the same type modulo constness, simply compare
290 // compressed values.
291 return a.GetAsIntegral() == b.GetAsIntegral();
292 } else {
293 // When the types are different, compare decompressed pointers, because the
294 // pointers may need to be adjusted.
295 // TODO(1376980): Avoid decompression here.
296 return a.get() == b.get();
297 }
298 }
299
300 template <typename T, typename U>
301 PA_ALWAYS_INLINE constexpr bool operator==(CompressedPointer<T> a, U* b) {
302 // Do compression, since it is less expensive.
303 return a == static_cast<CompressedPointer<U>>(b);
304 }
305
306 template <typename T, typename U>
307 PA_ALWAYS_INLINE constexpr bool operator==(T* a, CompressedPointer<U> b) {
308 return b == a;
309 }
310
311 template <typename T>
312 PA_ALWAYS_INLINE constexpr bool operator==(CompressedPointer<T> a,
313 std::nullptr_t) {
314 return !a.is_nonnull();
315 }
316
317 template <typename T, typename U>
318 PA_ALWAYS_INLINE constexpr bool operator==(std::nullptr_t,
319 CompressedPointer<U> b) {
320 return b == nullptr;
321 }
322
323 // operators!=.
324 template <typename T, typename U>
325 PA_ALWAYS_INLINE constexpr bool operator!=(CompressedPointer<T> a,
326 CompressedPointer<U> b) {
327 return !(a == b);
328 }
329
330 template <typename T, typename U>
331 PA_ALWAYS_INLINE constexpr bool operator!=(CompressedPointer<T> a, U* b) {
332 // Do compression, since it is less expensive.
333 return a != static_cast<CompressedPointer<U>>(b);
334 }
335
336 template <typename T, typename U>
337 PA_ALWAYS_INLINE constexpr bool operator!=(T* a, CompressedPointer<U> b) {
338 return b != a;
339 }
340
341 template <typename T>
342 PA_ALWAYS_INLINE constexpr bool operator!=(CompressedPointer<T> a,
343 std::nullptr_t) {
344 return a.is_nonnull();
345 }
346
347 template <typename T, typename U>
348 PA_ALWAYS_INLINE constexpr bool operator!=(std::nullptr_t,
349 CompressedPointer<U> b) {
350 return b != nullptr;
351 }
352
353 // operators<.
354 template <typename T, typename U>
355 PA_ALWAYS_INLINE constexpr bool operator<(CompressedPointer<T> a,
356 CompressedPointer<U> b) {
357 if constexpr (internal::IsDecayedSame<T, U>) {
358 // When pointers have the same type modulo constness, simply compare
359 // compressed values.
360 return a.GetAsIntegral() < b.GetAsIntegral();
361 } else {
362 // When the types are different, compare decompressed pointers, because the
363 // pointers may need to be adjusted.
364 // TODO(1376980): Avoid decompression here.
365 return a.get() < b.get();
366 }
367 }
368
369 template <typename T, typename U>
370 PA_ALWAYS_INLINE constexpr bool operator<(CompressedPointer<T> a, U* b) {
371 // Do compression, since it is less expensive.
372 return a < static_cast<CompressedPointer<U>>(b);
373 }
374
375 template <typename T, typename U>
376 PA_ALWAYS_INLINE constexpr bool operator<(T* a, CompressedPointer<U> b) {
377 // Do compression, since it is less expensive.
378 return static_cast<CompressedPointer<T>>(a) < b;
379 }
380
381 // operators<=.
382 template <typename T, typename U>
383 PA_ALWAYS_INLINE constexpr bool operator<=(CompressedPointer<T> a,
384 CompressedPointer<U> b) {
385 if constexpr (internal::IsDecayedSame<T, U>) {
386 // When pointers have the same type modulo constness, simply compare
387 // compressed values.
388 return a.GetAsIntegral() <= b.GetAsIntegral();
389 } else {
390 // When the types are different, compare decompressed pointers, because the
391 // pointers may need to be adjusted.
392 // TODO(1376980): Avoid decompression here.
393 return a.get() <= b.get();
394 }
395 }
396
397 template <typename T, typename U>
398 PA_ALWAYS_INLINE constexpr bool operator<=(CompressedPointer<T> a, U* b) {
399 // Do compression, since it is less expensive.
400 return a <= static_cast<CompressedPointer<U>>(b);
401 }
402
403 template <typename T, typename U>
404 PA_ALWAYS_INLINE constexpr bool operator<=(T* a, CompressedPointer<U> b) {
405 // Do compression, since it is less expensive.
406 return static_cast<CompressedPointer<T>>(a) <= b;
407 }
408
409 // operators>.
410 template <typename T, typename U>
411 PA_ALWAYS_INLINE constexpr bool operator>(CompressedPointer<T> a,
412 CompressedPointer<U> b) {
413 return !(a <= b);
414 }
415
416 template <typename T, typename U>
417 PA_ALWAYS_INLINE constexpr bool operator>(CompressedPointer<T> a, U* b) {
418 // Do compression, since it is less expensive.
419 return a > static_cast<CompressedPointer<U>>(b);
420 }
421
422 template <typename T, typename U>
423 PA_ALWAYS_INLINE constexpr bool operator>(T* a, CompressedPointer<U> b) {
424 // Do compression, since it is less expensive.
425 return static_cast<CompressedPointer<T>>(a) > b;
426 }
427
428 // operators>=.
429 template <typename T, typename U>
430 PA_ALWAYS_INLINE constexpr bool operator>=(CompressedPointer<T> a,
431 CompressedPointer<U> b) {
432 return !(a < b);
433 }
434
435 template <typename T, typename U>
436 PA_ALWAYS_INLINE constexpr bool operator>=(CompressedPointer<T> a, U* b) {
437 // Do compression, since it is less expensive.
438 return a >= static_cast<CompressedPointer<U>>(b);
439 }
440
441 template <typename T, typename U>
442 PA_ALWAYS_INLINE constexpr bool operator>=(T* a, CompressedPointer<U> b) {
443 // Do compression, since it is less expensive.
444 return static_cast<CompressedPointer<T>>(a) >= b;
445 }
446
447 #endif // BUILDFLAG(ENABLE_POINTER_COMPRESSION)
448
449 // Simple wrapper over the raw pointer.
450 template <typename T>
451 class PA_TRIVIAL_ABI UncompressedPointer final {
452 public:
453 PA_ALWAYS_INLINE constexpr UncompressedPointer() = default;
UncompressedPointer(T * ptr)454 PA_ALWAYS_INLINE constexpr explicit UncompressedPointer(T* ptr) : ptr_(ptr) {}
UncompressedPointer(std::nullptr_t)455 PA_ALWAYS_INLINE constexpr explicit UncompressedPointer(std::nullptr_t)
456 : ptr_(nullptr) {}
457
458 PA_ALWAYS_INLINE constexpr UncompressedPointer(const UncompressedPointer&) =
459 default;
460 PA_ALWAYS_INLINE constexpr UncompressedPointer(
461 UncompressedPointer&& other) noexcept = default;
462
463 template <typename U,
464 std::enable_if_t<std::is_convertible_v<U*, T*>>* = nullptr>
UncompressedPointer(const UncompressedPointer<U> & other)465 PA_ALWAYS_INLINE constexpr explicit UncompressedPointer(
466 const UncompressedPointer<U>& other)
467 : ptr_(other.ptr_) {}
468
469 template <typename U,
470 std::enable_if_t<std::is_convertible_v<U*, T*>>* = nullptr>
UncompressedPointer(UncompressedPointer<U> && other)471 PA_ALWAYS_INLINE constexpr explicit UncompressedPointer(
472 UncompressedPointer<U>&& other) noexcept
473 : ptr_(std::move(other.ptr_)) {}
474
475 ~UncompressedPointer() = default;
476
477 PA_ALWAYS_INLINE constexpr UncompressedPointer& operator=(
478 const UncompressedPointer&) = default;
479 PA_ALWAYS_INLINE constexpr UncompressedPointer& operator=(
480 UncompressedPointer&& other) noexcept = default;
481
482 template <typename U,
483 std::enable_if_t<std::is_convertible_v<U*, T*>>* = nullptr>
484 PA_ALWAYS_INLINE constexpr UncompressedPointer& operator=(
485 const UncompressedPointer<U>& other) {
486 ptr_ = other.ptr_;
487 return *this;
488 }
489
490 template <typename U,
491 std::enable_if_t<std::is_convertible_v<U*, T*>>* = nullptr>
492 PA_ALWAYS_INLINE constexpr UncompressedPointer& operator=(
493 UncompressedPointer<U>&& other) noexcept {
494 ptr_ = std::move(other.ptr_);
495 return *this;
496 }
497
498 PA_ALWAYS_INLINE constexpr UncompressedPointer& operator=(std::nullptr_t) {
499 ptr_ = nullptr;
500 return *this;
501 }
502
get()503 PA_ALWAYS_INLINE constexpr T* get() const { return ptr_; }
504
is_nonnull()505 PA_ALWAYS_INLINE constexpr bool is_nonnull() const { return ptr_; }
506
507 PA_ALWAYS_INLINE constexpr explicit operator bool() const {
508 return is_nonnull();
509 }
510
511 template <typename U = T,
512 std::enable_if_t<!std::is_void_v<std::remove_cv_t<U>>>* = nullptr>
513 PA_ALWAYS_INLINE constexpr U& operator*() const {
514 PA_DCHECK(is_nonnull());
515 return *get();
516 }
517
518 PA_ALWAYS_INLINE constexpr T* operator->() const {
519 PA_DCHECK(is_nonnull());
520 return get();
521 }
522
swap(UncompressedPointer & other)523 PA_ALWAYS_INLINE constexpr void swap(UncompressedPointer& other) {
524 std::swap(ptr_, other.ptr_);
525 }
526
527 private:
528 template <typename>
529 friend class UncompressedPointer;
530
531 T* ptr_;
532 };
533
534 template <typename T>
swap(UncompressedPointer<T> & a,UncompressedPointer<T> & b)535 PA_ALWAYS_INLINE constexpr void swap(UncompressedPointer<T>& a,
536 UncompressedPointer<T>& b) {
537 a.swap(b);
538 }
539
540 // operators==.
541 template <typename T, typename U>
542 PA_ALWAYS_INLINE constexpr bool operator==(UncompressedPointer<T> a,
543 UncompressedPointer<U> b) {
544 return a.get() == b.get();
545 }
546
547 template <typename T, typename U>
548 PA_ALWAYS_INLINE constexpr bool operator==(UncompressedPointer<T> a, U* b) {
549 return a == static_cast<UncompressedPointer<U>>(b);
550 }
551
552 template <typename T, typename U>
553 PA_ALWAYS_INLINE constexpr bool operator==(T* a, UncompressedPointer<U> b) {
554 return b == a;
555 }
556
557 template <typename T>
558 PA_ALWAYS_INLINE constexpr bool operator==(UncompressedPointer<T> a,
559 std::nullptr_t) {
560 return !a.is_nonnull();
561 }
562
563 template <typename T, typename U>
564 PA_ALWAYS_INLINE constexpr bool operator==(std::nullptr_t,
565 UncompressedPointer<U> b) {
566 return b == nullptr;
567 }
568
569 // operators!=.
570 template <typename T, typename U>
571 PA_ALWAYS_INLINE constexpr bool operator!=(UncompressedPointer<T> a,
572 UncompressedPointer<U> b) {
573 return !(a == b);
574 }
575
576 template <typename T, typename U>
577 PA_ALWAYS_INLINE constexpr bool operator!=(UncompressedPointer<T> a, U* b) {
578 return a != static_cast<UncompressedPointer<U>>(b);
579 }
580
581 template <typename T, typename U>
582 PA_ALWAYS_INLINE constexpr bool operator!=(T* a, UncompressedPointer<U> b) {
583 return b != a;
584 }
585
586 template <typename T>
587 PA_ALWAYS_INLINE constexpr bool operator!=(UncompressedPointer<T> a,
588 std::nullptr_t) {
589 return a.is_nonnull();
590 }
591
592 template <typename T, typename U>
593 PA_ALWAYS_INLINE constexpr bool operator!=(std::nullptr_t,
594 UncompressedPointer<U> b) {
595 return b != nullptr;
596 }
597
598 // operators<.
599 template <typename T, typename U>
600 PA_ALWAYS_INLINE constexpr bool operator<(UncompressedPointer<T> a,
601 UncompressedPointer<U> b) {
602 return a.get() < b.get();
603 }
604
605 template <typename T, typename U>
606 PA_ALWAYS_INLINE constexpr bool operator<(UncompressedPointer<T> a, U* b) {
607 return a < static_cast<UncompressedPointer<U>>(b);
608 }
609
610 template <typename T, typename U>
611 PA_ALWAYS_INLINE constexpr bool operator<(T* a, UncompressedPointer<U> b) {
612 return static_cast<UncompressedPointer<T>>(a) < b;
613 }
614
615 // operators<=.
616 template <typename T, typename U>
617 PA_ALWAYS_INLINE constexpr bool operator<=(UncompressedPointer<T> a,
618 UncompressedPointer<U> b) {
619 return a.get() <= b.get();
620 }
621
622 template <typename T, typename U>
623 PA_ALWAYS_INLINE constexpr bool operator<=(UncompressedPointer<T> a, U* b) {
624 return a <= static_cast<UncompressedPointer<U>>(b);
625 }
626
627 template <typename T, typename U>
628 PA_ALWAYS_INLINE constexpr bool operator<=(T* a, UncompressedPointer<U> b) {
629 return static_cast<UncompressedPointer<T>>(a) <= b;
630 }
631
632 // operators>.
633 template <typename T, typename U>
634 PA_ALWAYS_INLINE constexpr bool operator>(UncompressedPointer<T> a,
635 UncompressedPointer<U> b) {
636 return !(a <= b);
637 }
638
639 template <typename T, typename U>
640 PA_ALWAYS_INLINE constexpr bool operator>(UncompressedPointer<T> a, U* b) {
641 return a > static_cast<UncompressedPointer<U>>(b);
642 }
643
644 template <typename T, typename U>
645 PA_ALWAYS_INLINE constexpr bool operator>(T* a, UncompressedPointer<U> b) {
646 return static_cast<UncompressedPointer<T>>(a) > b;
647 }
648
649 // operators>=.
650 template <typename T, typename U>
651 PA_ALWAYS_INLINE constexpr bool operator>=(UncompressedPointer<T> a,
652 UncompressedPointer<U> b) {
653 return !(a < b);
654 }
655
656 template <typename T, typename U>
657 PA_ALWAYS_INLINE constexpr bool operator>=(UncompressedPointer<T> a, U* b) {
658 return a >= static_cast<UncompressedPointer<U>>(b);
659 }
660
661 template <typename T, typename U>
662 PA_ALWAYS_INLINE constexpr bool operator>=(T* a, UncompressedPointer<U> b) {
663 return static_cast<UncompressedPointer<T>>(a) >= b;
664 }
665
666 } // namespace partition_alloc
667
668 #endif // PARTITION_ALLOC_COMPRESSED_POINTER_H_
669