1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
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
|