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