1 /**
2 * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #ifndef PANDA_RUNTIME_INCLUDE_CORETYPES_TAGGED_VALUE_H_
17 #define PANDA_RUNTIME_INCLUDE_CORETYPES_TAGGED_VALUE_H_
18
19 #include <climits>
20 #include <cstddef>
21 #include "macros.h"
22 #include "libpandabase/mem/mem.h"
23 #include "utils/bit_utils.h"
24
25 namespace panda {
26 class ObjectHeader;
27 } // namespace panda
28
29 namespace panda::coretypes {
30
31 // Every double with all of its exponent bits set and its highest mantissa bit set is a quiet NaN.
32 // That leaves 51 bits unaccounted for. We’ll avoid one of those so that we don’t step on Intel’s
33 // “QNaN Floating-Point Indefinite” value, leaving us 50 bits. Those remaining bits can be anything.
34 // so we use a special quietNaN as TaggedInt tag(highest 16bits as 0xFFFF), and need to encode double
35 // to the value will begin with a 16-bit pattern within the range 0x0001..0xFFFE.
36
37 // Nan-boxing pointer is used and the first four bytes are used as tag:
38 // Object: [0x0000] [48 bit direct pointer]
39 // WeakRef: [0x0000] [47 bits direct pointer] | 1 bit 1
40 // / [0x0001] [48 bit any value]
41 // TaggedDouble: ......
42 // \ [0xFFFE] [48 bit any value]
43 // TaggedInt: [0xFFFF] [0x0000] [32 bit signed integer]
44 //
45 // There are some special markers of Object:
46 // False: [56 bits 0] | 0x06 // 0110
47 // True: [56 bits 0] | 0x07 // 0111
48 // Undefined: [56 bits 0] | 0x0a // 1010
49 // Null: [56 bits 0] | 0x02 // 0010
50 // Hole: [56 bits 0] | 0x00 // 0000
51
52 using TaggedType = uint64_t;
53
54 static const TaggedType NULL_POINTER = 0;
55
ReinterpretDoubleToTaggedType(double value)56 inline TaggedType ReinterpretDoubleToTaggedType(double value)
57 {
58 return bit_cast<TaggedType>(value);
59 }
ReinterpretTaggedTypeToDouble(TaggedType value)60 inline double ReinterpretTaggedTypeToDouble(TaggedType value)
61 {
62 return bit_cast<double>(value);
63 }
64
65 class TaggedValue {
66 public:
67 static constexpr size_t TAG_BITS_SIZE = 16;
68 static constexpr size_t TAG_BITS_SHIFT = BitNumbers<TaggedType>() - TAG_BITS_SIZE;
69 static_assert((TAG_BITS_SHIFT + TAG_BITS_SIZE) == sizeof(TaggedType) * CHAR_BIT, "Insufficient bits!");
70 static constexpr TaggedType TAG_MASK = ((1ULL << TAG_BITS_SIZE) - 1ULL) << TAG_BITS_SHIFT;
71 static constexpr TaggedType TAG_INT = TAG_MASK;
72 static constexpr TaggedType TAG_OBJECT = 0x0000ULL << TAG_BITS_SHIFT;
73 static constexpr TaggedType OBJECT_MASK = ~TAG_INT;
74
75 static constexpr TaggedType TAG_SPECIAL_MASK = 0xFFULL;
76 static constexpr TaggedType TAG_SPECIAL_VALUE = 0x02ULL;
77 static constexpr TaggedType TAG_BOOLEAN = 0x04ULL;
78 static constexpr TaggedType TAG_UNDEFINED = 0x08ULL;
79 static constexpr TaggedType TAG_EXCEPTION = 0x10ULL;
80 static constexpr TaggedType TAG_WEAK_FILTER = 0x03ULL;
81 static constexpr TaggedType VALUE_HOLE = TAG_OBJECT | 0x00ULL;
82 static constexpr TaggedType TAG_WEAK_MASK = TAG_OBJECT | 0x01ULL;
83 static constexpr TaggedType VALUE_NULL = TAG_OBJECT | TAG_SPECIAL_VALUE;
84 static constexpr TaggedType VALUE_FALSE =
85 TAG_OBJECT | TAG_BOOLEAN | TAG_SPECIAL_VALUE | static_cast<TaggedType>(false);
86 static constexpr TaggedType VALUE_TRUE =
87 TAG_OBJECT | TAG_BOOLEAN | TAG_SPECIAL_VALUE | static_cast<TaggedType>(true);
88 static constexpr TaggedType VALUE_ZERO = TAG_INT | 0x00ULL;
89 static constexpr TaggedType VALUE_UNDEFINED = TAG_OBJECT | TAG_SPECIAL_VALUE | TAG_UNDEFINED;
90 static constexpr TaggedType VALUE_EXCEPTION = TAG_OBJECT | TAG_SPECIAL_VALUE | TAG_EXCEPTION;
91
92 static constexpr size_t DOUBLE_ENCODE_OFFSET_BIT = 48;
93 static constexpr TaggedType DOUBLE_ENCODE_OFFSET = 1ULL << DOUBLE_ENCODE_OFFSET_BIT;
94
95 TaggedValue(void *) = delete;
96
TaggedValue()97 constexpr TaggedValue() : value_(NULL_POINTER) {}
98
TaggedValue(TaggedType v)99 constexpr explicit TaggedValue(TaggedType v) : value_(v) {}
100
TaggedValue(int v)101 constexpr explicit TaggedValue(int v) : value_(static_cast<TaggedType>(v) | TAG_INT) {}
102
TaggedValue(unsigned int v)103 explicit TaggedValue(unsigned int v)
104 {
105 if (static_cast<int32_t>(v) < 0) {
106 value_ = TaggedValue(static_cast<double>(v)).GetRawData();
107 return;
108 }
109 value_ = TaggedValue(static_cast<int32_t>(v)).GetRawData();
110 }
111
GetIntTaggedValue(uint64_t v)112 static uint64_t GetIntTaggedValue(uint64_t v)
113 {
114 ASSERT(INT32_MIN <= static_cast<int32_t>(bit_cast<int64_t>(v)));
115 ASSERT(static_cast<int32_t>(bit_cast<int64_t>(v)) <= INT32_MAX);
116 return static_cast<uint32_t>(v) | TAG_INT;
117 }
118
GetDoubleTaggedValue(uint64_t v)119 static uint64_t GetDoubleTaggedValue(uint64_t v)
120 {
121 return v + DOUBLE_ENCODE_OFFSET;
122 }
123
GetBoolTaggedValue(uint64_t v)124 static uint64_t GetBoolTaggedValue(uint64_t v)
125 {
126 ASSERT(v == 0 || v == 1);
127 return (v == 0) ? static_cast<uint64_t>(coretypes::TaggedValue::False().GetRawData())
128 : static_cast<uint64_t>(coretypes::TaggedValue::True().GetRawData());
129 }
130
GetObjectTaggedValue(uint64_t v)131 static uint64_t GetObjectTaggedValue(uint64_t v)
132 {
133 ASSERT(static_cast<uint32_t>(v) == v);
134 return v;
135 }
136
TaggedValue(int64_t v)137 explicit TaggedValue(int64_t v)
138 {
139 if (UNLIKELY(static_cast<int32_t>(v) != v)) {
140 value_ = TaggedValue(static_cast<double>(v)).GetRawData();
141 return;
142 }
143 value_ = TaggedValue(static_cast<int32_t>(v)).GetRawData();
144 }
145
TaggedValue(bool v)146 constexpr explicit TaggedValue(bool v)
147 : value_(static_cast<TaggedType>(v) | TAG_OBJECT | TAG_BOOLEAN | TAG_SPECIAL_VALUE)
148 {
149 }
150
TaggedValue(double v)151 explicit TaggedValue(double v)
152 {
153 ASSERT_PRINT(!IsImpureNaN(v), "pureNaN will break the encoding of tagged double: "
154 << std::hex << ReinterpretDoubleToTaggedType(v));
155 value_ = ReinterpretDoubleToTaggedType(v) + DOUBLE_ENCODE_OFFSET;
156 }
157
TaggedValue(ObjectHeader * v)158 explicit TaggedValue(ObjectHeader *v) : value_(static_cast<TaggedType>(ToUintPtr(v))) {}
159
TaggedValue(const ObjectHeader * v)160 explicit TaggedValue(const ObjectHeader *v) : value_(static_cast<TaggedType>(ToUintPtr(v))) {}
161
CreateWeakRef()162 inline void CreateWeakRef()
163 {
164 ASSERT_PRINT(IsHeapObject() && ((value_ & TAG_WEAK_FILTER) == 0U),
165 "The least significant two bits of TaggedValue are not zero.");
166 value_ = value_ | TAG_WEAK_MASK;
167 }
168
RemoveWeakTag()169 inline void RemoveWeakTag()
170 {
171 ASSERT_PRINT(IsHeapObject() && ((value_ & TAG_WEAK_MASK) == 1U), "The tagged value is not a weak ref.");
172 value_ = value_ & (~TAG_WEAK_FILTER);
173 }
174
CreateAndGetWeakRef()175 inline TaggedValue CreateAndGetWeakRef()
176 {
177 ASSERT_PRINT(IsHeapObject() && ((value_ & TAG_WEAK_FILTER) == 0U),
178 "The least significant two bits of TaggedValue are not zero.");
179 return TaggedValue(value_ | TAG_WEAK_MASK);
180 }
181
IsWeak()182 inline bool IsWeak() const
183 {
184 return IsHeapObject() && ((value_ & TAG_WEAK_MASK) == 1U);
185 }
186
IsDouble()187 inline bool IsDouble() const
188 {
189 return !IsInt() && !IsObject();
190 }
191
IsInt()192 inline bool IsInt() const
193 {
194 return (value_ & TAG_MASK) == TAG_INT;
195 }
196
IsSpecial()197 inline bool IsSpecial() const
198 {
199 return ((value_ & (~TAG_SPECIAL_MASK)) == 0U) && (((value_ & TAG_SPECIAL_VALUE) != 0U) || IsHole());
200 }
201
IsObject()202 inline bool IsObject() const
203 {
204 return ((value_ & TAG_MASK) == TAG_OBJECT);
205 }
206
IsHeapObject()207 inline bool IsHeapObject() const
208 {
209 return IsObject() && !IsSpecial();
210 }
211
IsNumber()212 inline bool IsNumber() const
213 {
214 return !IsObject();
215 }
216
IsBoolean()217 inline bool IsBoolean() const
218 {
219 return value_ == VALUE_FALSE || value_ == VALUE_TRUE;
220 }
221
GetDouble()222 inline double GetDouble() const
223 {
224 ASSERT_PRINT(IsDouble(), "can not convert TaggedValue to Double : " << std::hex << value_);
225 return ReinterpretTaggedTypeToDouble(value_ - DOUBLE_ENCODE_OFFSET);
226 }
227
GetInt()228 inline int GetInt() const
229 {
230 ASSERT_PRINT(IsInt(), "can not convert TaggedValue to Int :" << std::hex << value_);
231 return static_cast<int>(value_ & (~TAG_MASK));
232 }
233
GetRawData()234 inline constexpr TaggedType GetRawData() const
235 {
236 return value_;
237 }
238
GetHeapObject()239 inline ObjectHeader *GetHeapObject() const
240 {
241 ASSERT_PRINT(IsHeapObject(), "can not convert TaggedValue to HeapObject :" << std::hex << value_);
242 // TODO(vpukhov): weakref ignored
243 return reinterpret_cast<ObjectHeader *>(value_ & (~TAG_WEAK_MASK));
244 }
245
246 // This function returns the heap object pointer which may have the weak tag.
GetRawHeapObject()247 inline ObjectHeader *GetRawHeapObject() const
248 {
249 ASSERT_PRINT(IsHeapObject(), "can not convert TaggedValue to HeapObject :" << std::hex << value_);
250 return reinterpret_cast<ObjectHeader *>(value_);
251 }
252
GetWeakReferent()253 inline ObjectHeader *GetWeakReferent() const
254 {
255 ASSERT_PRINT(IsWeak(), "can not convert TaggedValue to WeakRef HeapObject :" << std::hex << value_);
256 return reinterpret_cast<ObjectHeader *>(value_ & (~TAG_WEAK_MASK));
257 }
258
Cast(void * ptr)259 static inline TaggedType Cast(void *ptr)
260 {
261 ASSERT_PRINT(sizeof(void *) == TaggedTypeSize(), "32bit platform is not support yet");
262 return static_cast<TaggedType>(ToUintPtr(ptr));
263 }
264
IsFalse()265 inline bool IsFalse() const
266 {
267 return value_ == VALUE_FALSE;
268 }
269
IsTrue()270 inline bool IsTrue() const
271 {
272 return value_ == VALUE_TRUE;
273 }
274
IsUndefined()275 inline bool IsUndefined() const
276 {
277 return value_ == VALUE_UNDEFINED;
278 }
279
IsNull()280 inline bool IsNull() const
281 {
282 return value_ == VALUE_NULL;
283 }
284
IsUndefinedOrNull()285 inline bool IsUndefinedOrNull() const
286 {
287 return IsNull() || IsUndefined();
288 }
289
IsHole()290 inline bool IsHole() const
291 {
292 return value_ == VALUE_HOLE;
293 }
294
IsException()295 inline bool IsException() const
296 {
297 return value_ == VALUE_EXCEPTION;
298 }
299
False()300 static inline constexpr TaggedValue False()
301 {
302 return TaggedValue(VALUE_FALSE);
303 }
304
True()305 static inline constexpr TaggedValue True()
306 {
307 return TaggedValue(VALUE_TRUE);
308 }
309
Undefined()310 static inline constexpr TaggedValue Undefined()
311 {
312 return TaggedValue(VALUE_UNDEFINED);
313 }
314
Null()315 static inline constexpr TaggedValue Null()
316 {
317 return TaggedValue(VALUE_NULL);
318 }
319
Hole()320 static inline constexpr TaggedValue Hole()
321 {
322 return TaggedValue(VALUE_HOLE);
323 }
324
Exception()325 static inline constexpr TaggedValue Exception()
326 {
327 return TaggedValue(VALUE_EXCEPTION);
328 }
329
TaggedTypeSize()330 static inline constexpr size_t TaggedTypeSize()
331 {
332 return sizeof(TaggedType);
333 }
334
IsImpureNaN(double value)335 static inline bool IsImpureNaN(double value)
336 {
337 // Tests if the double value would break tagged double encoding.
338 return bit_cast<TaggedType>(value) >= (TAG_INT - DOUBLE_ENCODE_OFFSET);
339 }
340
341 inline bool operator==(const TaggedValue &other) const
342 {
343 return value_ == other.value_;
344 }
345
346 inline bool operator!=(const TaggedValue &other) const
347 {
348 return value_ != other.value_;
349 }
350
351 ~TaggedValue() = default;
352
353 DEFAULT_COPY_SEMANTIC(TaggedValue);
354 DEFAULT_MOVE_SEMANTIC(TaggedValue);
355
356 private:
357 TaggedType value_;
358 };
359 } // namespace panda::coretypes
360 #endif // PANDA_RUNTIME_INCLUDE_CORETYPES_TAGGED_VALUE_H_
361