summaryrefslogtreecommitdiffstats
path: root/js/src/ds/PointerAndUint7.h
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/ds/PointerAndUint7.h')
-rw-r--r--js/src/ds/PointerAndUint7.h125
1 files changed, 125 insertions, 0 deletions
diff --git a/js/src/ds/PointerAndUint7.h b/js/src/ds/PointerAndUint7.h
new file mode 100644
index 0000000000..6313b0df48
--- /dev/null
+++ b/js/src/ds/PointerAndUint7.h
@@ -0,0 +1,125 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sw=2 et tw=80:
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef gc_PointerAndUint7_h
+#define gc_PointerAndUint7_h
+
+#include "mozilla/Assertions.h"
+
+#include <stdint.h>
+
+namespace js {
+
+// A class that can store an address and a 7-bit unsigned integer in 64 bits,
+// even on a 64-bit target.
+//
+// On 64-bit targets, it assumes that all supported target architectures
+// contain at most 57 significant bits in their addresses, and that the valid
+// address space is split evenly between addresses increasing from 0--(64)--0
+// and addresses decreasing from 1--(64)--1.
+//
+// The 57-significant-bit constraint comes from Intel's 5-level paging scheme
+// as introduced in the Ice Lake processor line, circa late 2019; see
+// https://en.wikipedia.org/wiki/Intel_5-level_paging. Prior to that, Intel
+// required only 48 significant bits. AArch64 requires 52 significant bits,
+// as of the ARMv8.2 LVA (Large Virtual Addressing) extension, and so is less
+// constraining than Intel.
+//
+// In any case, NaN-boxing of pointers in JS::Value gives us a pretty hard
+// requirement that we can store pointers in 47 bits. So that constraint will
+// break before the 57-bit constraint here breaks. See SMDOC in
+// js/public/Value.h.
+//
+// On 32-bit targets, both components are stored unmodified in the upper and
+// lower 32-bit chunks of the value, and there are no constraints on the
+// component values.
+
+#ifdef JS_64BIT
+
+// The implementation for 64-bit targets.
+class PointerAndUint7 final {
+ // The representation is: the lowest 57 bits of the pointer are stored in
+ // the top 57 bits of val_, and the Uint7 is stored in the bottom 7 bits.
+ // Hence recovering the pointer is 7-bit signed shift right of val_, and
+ // recovering the UInt7 is an AND with 127. In both cases, that's a single
+ // machine instruction.
+ uint64_t val_;
+
+ static const uint8_t SHIFT_PTR = 7;
+ static const uint64_t MASK_UINT7 = (uint64_t(1) << SHIFT_PTR) - 1;
+
+ static inline bool isRepresentablePtr(void* ptr) {
+ // We require that the top 7 bits (bits 63:57) are the same as bit 56.
+ // That will be the case iff, when we signedly shift `ptr` right by 56
+ // bits, the value is all zeroes or all ones.
+ int64_t s = int64_t(ptr);
+ // s should be bbbb'bbbb'X--(56)--X, for b = 0 or 1, and X can be anything
+ s >>= (64 - SHIFT_PTR - 1); // 56
+ // s should be 0--(64)--0 or 1--(64)--1
+ uint64_t u = uint64_t(s);
+ // Note, this addition can overflow, intentionally.
+ u += 1;
+ // u should be 0--(64)--0 or 0--(63)--01
+ return u <= uint64_t(1);
+ }
+ static inline bool isRepresentableUint7(uint32_t uint7) {
+ return uint7 <= MASK_UINT7;
+ }
+
+ public:
+ inline PointerAndUint7() : val_(0) {}
+ inline PointerAndUint7(void* ptr, uint32_t uint7)
+ : val_((uint64_t(ptr) << SHIFT_PTR) | (uint64_t(uint7 & MASK_UINT7))) {
+ MOZ_ASSERT(isRepresentablePtr(ptr));
+ MOZ_ASSERT(isRepresentableUint7(uint7));
+ }
+ inline void* pointer() const { return (void*)(int64_t(val_) >> SHIFT_PTR); }
+ inline uint32_t uint7() const { return uint32_t(val_ & MASK_UINT7); }
+};
+
+static_assert(sizeof(void*) == 8);
+// "int64_t really is signed"
+static_assert(((int64_t(1) << 63) >> 63) == int64_t(0xFFFFFFFFFFFFFFFFULL));
+
+#else
+
+// The implementation for 32-bit targets.
+class PointerAndUint7 final {
+ // The representation places the pointer in the upper 32 bits of val_ and
+ // the Uint7 in the lower 32 bits. This is represented using a single
+ // 64-bit field in the hope of increasing the chance that the class will be
+ // passed around in a register-pair rather than through memory.
+ uint64_t val_;
+
+ static const uint8_t SHIFT_PTR = 32;
+ static const uint64_t MASK_UINT7 = (uint64_t(1) << 7) - 1;
+
+ static inline bool isRepresentableUint7(uint32_t uint7) {
+ return uint7 <= MASK_UINT7;
+ }
+
+ public:
+ inline PointerAndUint7() : val_(0) {}
+ inline PointerAndUint7(void* ptr, uint32_t uint7)
+ : val_((uint64_t(uint32_t(ptr)) << SHIFT_PTR) |
+ (uint64_t(uint7) & MASK_UINT7)) {
+ MOZ_ASSERT(isRepresentableUint7(uint7));
+ }
+ inline void* pointer() const { return (void*)(int32_t(val_ >> SHIFT_PTR)); }
+ inline uint32_t uint7() const { return uint32_t(val_ & MASK_UINT7); }
+};
+
+static_assert(sizeof(void*) == 4);
+
+#endif // JS_64BIT
+
+// We require this for both 32- and 64-bit targets.
+static_assert(sizeof(PointerAndUint7) == 8);
+
+} // namespace js
+
+#endif // gc_PointerAndUint7_h