1 | // Copyright (C) 2019 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | #ifndef QV4STATICVALUE_P_H |
4 | #define QV4STATICVALUE_P_H |
5 | |
6 | // |
7 | // W A R N I N G |
8 | // ------------- |
9 | // |
10 | // This file is not part of the Qt API. It exists purely as an |
11 | // implementation detail. This header file may change from version to |
12 | // version without notice, or even be removed. |
13 | // |
14 | // We mean it. |
15 | // |
16 | |
17 | #include <qjsnumbercoercion.h> |
18 | |
19 | #include <QtCore/private/qnumeric_p.h> |
20 | #include <private/qtqmlglobal_p.h> |
21 | |
22 | #include <cstring> |
23 | |
24 | #ifdef QT_NO_DEBUG |
25 | #define QV4_NEARLY_ALWAYS_INLINE Q_ALWAYS_INLINE |
26 | #else |
27 | #define QV4_NEARLY_ALWAYS_INLINE inline |
28 | #endif |
29 | |
30 | QT_BEGIN_NAMESPACE |
31 | |
32 | namespace QV4 { |
33 | |
34 | // ReturnedValue is used to return values from runtime methods |
35 | // the type has to be a primitive type (no struct or union), so that the compiler |
36 | // will return it in a register on all platforms. |
37 | // It will be returned in rax on x64, [eax,edx] on x86 and [r0,r1] on arm |
38 | typedef quint64 ReturnedValue; |
39 | |
40 | namespace Heap { |
41 | struct Base; |
42 | } |
43 | |
44 | struct StaticValue |
45 | { |
46 | using HeapBasePtr = Heap::Base *; |
47 | |
48 | StaticValue() = default; |
49 | constexpr StaticValue(quint64 val) : _val(val) {} |
50 | |
51 | StaticValue &operator=(ReturnedValue v) |
52 | { |
53 | _val = v; |
54 | return *this; |
55 | } |
56 | |
57 | template<typename Value> |
58 | StaticValue &operator=(const Value &); |
59 | |
60 | template<typename Value> |
61 | const Value &asValue() const; |
62 | |
63 | template<typename Value> |
64 | Value &asValue(); |
65 | |
66 | /* |
67 | We use 8 bytes for a value. In order to store all possible values we employ a variant of NaN |
68 | boxing. A "special" Double is indicated by a number that has the 11 exponent bits set to 1. |
69 | Those can be NaN, positive or negative infinity. We only store one variant of NaN: The sign |
70 | bit has to be off and the bit after the exponent ("quiet bit") has to be on. However, since |
71 | the exponent bits are enough to identify special doubles, we can use a different bit as |
72 | discriminator to tell us how the rest of the bits (including quiet and sign) are to be |
73 | interpreted. This bit is bit 48. If set, we have an unmanaged value, which includes the |
74 | special doubles and various other values. If unset, we have a managed value, and all of the |
75 | other bits can be used to assemble a pointer. |
76 | |
77 | On 32bit systems the pointer can just live in the lower 4 bytes. On 64 bit systems the lower |
78 | 48 bits can be used for verbatim pointer bits. However, since all our heap objects are |
79 | aligned to 32 bytes, we can use the 5 least significant bits of the pointer to store, e.g. |
80 | pointer tags on android. The same holds for the 3 bits between the double exponent and |
81 | bit 48. |
82 | |
83 | With that out of the way, we can use the other bits to store different values. |
84 | |
85 | We xor Doubles with (0x7ff48000 << 32). That has the effect that any double with all the |
86 | exponent bits set to 0 is one of our special doubles. Those special doubles then get the |
87 | other two bits in the mask (Special and Number) set to 1, as they cannot have 1s in those |
88 | positions to begin with. |
89 | |
90 | We dedicate further bits to integer-convertible and bool-or-int. With those bits we can |
91 | describe all values we need to store. |
92 | |
93 | Undefined is encoded as a managed pointer with value 0. This is the same as a nullptr. |
94 | |
95 | Specific bit-sequences: |
96 | 0 = always 0 |
97 | 1 = always 1 |
98 | x = stored value |
99 | y = stored value, shifted to different position |
100 | a = xor-ed bits, where at least one bit is set |
101 | b = xor-ed bits |
102 | |
103 | 32109876 54321098 76543210 98765432 10987654 32109876 54321098 76543210 | |
104 | 66665555 55555544 44444444 33333333 33222222 22221111 11111100 00000000 | JS Value |
105 | ------------------------------------------------------------------------+-------------- |
106 | 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 | Undefined |
107 | y0000000 0000yyy0 xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxyyyyy | Managed (heap pointer) |
108 | 00000000 00001101 10000000 00000000 00000000 00000000 00000000 00000000 | NaN |
109 | 00000000 00000101 10000000 00000000 00000000 00000000 00000000 00000000 | +Inf |
110 | 10000000 00000101 10000000 00000000 00000000 00000000 00000000 00000000 | -Inf |
111 | xaaaaaaa aaaaxbxb bxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | double |
112 | 00000000 00000001 00000000 00000000 00000000 00000000 00000000 00000000 | empty (non-sparse array hole) |
113 | 00000000 00000011 00000000 00000000 00000000 00000000 00000000 00000000 | Null |
114 | 00000000 00000011 10000000 00000000 00000000 00000000 00000000 0000000x | Bool |
115 | 00000000 00000011 11000000 00000000 xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | Int |
116 | ^ ^^^ ^^ |
117 | | ||| || |
118 | | ||| |+-> Number |
119 | | ||| +--> Int or Bool |
120 | | ||+----> Unmanaged |
121 | | |+-----> Integer compatible |
122 | | +------> Special double |
123 | +--------------------> Double sign, also used for special doubles |
124 | */ |
125 | |
126 | quint64 _val; |
127 | |
128 | QV4_NEARLY_ALWAYS_INLINE constexpr quint64 &rawValueRef() { return _val; } |
129 | QV4_NEARLY_ALWAYS_INLINE constexpr quint64 rawValue() const { return _val; } |
130 | QV4_NEARLY_ALWAYS_INLINE constexpr void setRawValue(quint64 raw) { _val = raw; } |
131 | |
132 | #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN |
133 | static inline int valueOffset() { return 0; } |
134 | static inline int tagOffset() { return 4; } |
135 | #else // !Q_LITTLE_ENDIAN |
136 | static inline int valueOffset() { return 4; } |
137 | static inline int tagOffset() { return 0; } |
138 | #endif |
139 | static inline constexpr quint64 tagValue(quint32 tag, quint32 value) { return quint64(tag) << Tag_Shift | value; } |
140 | QV4_NEARLY_ALWAYS_INLINE constexpr void setTagValue(quint32 tag, quint32 value) { _val = quint64(tag) << Tag_Shift | value; } |
141 | QV4_NEARLY_ALWAYS_INLINE constexpr quint32 value() const { return _val & quint64(~quint32(0)); } |
142 | QV4_NEARLY_ALWAYS_INLINE constexpr quint32 tag() const { return _val >> Tag_Shift; } |
143 | QV4_NEARLY_ALWAYS_INLINE constexpr void setTag(quint32 tag) { setTagValue(tag, value: value()); } |
144 | |
145 | QV4_NEARLY_ALWAYS_INLINE constexpr int int_32() const |
146 | { |
147 | return int(value()); |
148 | } |
149 | QV4_NEARLY_ALWAYS_INLINE constexpr void setInt_32(int i) |
150 | { |
151 | setTagValue(tag: quint32(QuickType::Integer), value: quint32(i)); |
152 | } |
153 | QV4_NEARLY_ALWAYS_INLINE uint uint_32() const { return value(); } |
154 | |
155 | QV4_NEARLY_ALWAYS_INLINE constexpr void setEmpty() |
156 | { |
157 | setTagValue(tag: quint32(QuickType::Empty), value: 0); |
158 | } |
159 | |
160 | enum class TagBit { |
161 | // s: sign bit |
162 | // e: double exponent bit |
163 | // u: upper 3 bits if managed |
164 | // m: bit 48, denotes "unmanaged" if 1 |
165 | // p: significant pointer bits (some re-used for non-managed) |
166 | // seeeeeeeeeeeuuumpppp |
167 | SpecialNegative = 0b10000000000000000000 << 12, |
168 | SpecialQNaN = 0b00000000000010000000 << 12, |
169 | Special = 0b00000000000001000000 << 12, |
170 | IntCompat = 0b00000000000000100000 << 12, |
171 | Unmanaged = 0b00000000000000010000 << 12, |
172 | IntOrBool = 0b00000000000000001000 << 12, |
173 | Number = 0b00000000000000000100 << 12, |
174 | }; |
175 | |
176 | static inline constexpr quint64 tagBitMask(TagBit bit) { return quint64(bit) << Tag_Shift; } |
177 | |
178 | enum Type { |
179 | // Managed, Double and undefined are not directly encoded |
180 | Managed_Type = 0, |
181 | Double_Type = 1, |
182 | Undefined_Type = 2, |
183 | |
184 | Empty_Type = quint32(TagBit::Unmanaged), |
185 | Null_Type = Empty_Type | quint32(TagBit::IntCompat), |
186 | Boolean_Type = Null_Type | quint32(TagBit::IntOrBool), |
187 | Integer_Type = Boolean_Type | quint32(TagBit::Number) |
188 | }; |
189 | |
190 | enum { |
191 | Tag_Shift = 32, |
192 | |
193 | IsIntegerConvertible_Shift = 48, |
194 | IsIntegerConvertible_Value = 3, // Unmanaged | IntCompat after shifting |
195 | |
196 | IsIntegerOrBool_Shift = 47, |
197 | IsIntegerOrBool_Value = 7, // Unmanaged | IntCompat | IntOrBool after shifting |
198 | }; |
199 | |
200 | static_assert(IsIntegerConvertible_Value == |
201 | (quint32(TagBit::IntCompat) | quint32(TagBit::Unmanaged)) |
202 | >> (IsIntegerConvertible_Shift - Tag_Shift)); |
203 | |
204 | static_assert(IsIntegerOrBool_Value == |
205 | (quint32(TagBit::IntOrBool) | quint32(TagBit::IntCompat) | quint32(TagBit::Unmanaged)) |
206 | >> (IsIntegerOrBool_Shift - Tag_Shift)); |
207 | |
208 | static constexpr quint64 ExponentMask = 0b0111111111110000ull << 48; |
209 | |
210 | static constexpr quint64 Top1Mask = 0b1000000000000000ull << 48; |
211 | static constexpr quint64 Upper3Mask = 0b0000000000001110ull << 48; |
212 | static constexpr quint64 Lower5Mask = 0b0000000000011111ull; |
213 | |
214 | static constexpr quint64 ManagedMask = ExponentMask | quint64(TagBit::Unmanaged) << Tag_Shift; |
215 | static constexpr quint64 DoubleMask = ManagedMask | quint64(TagBit::Special) << Tag_Shift; |
216 | static constexpr quint64 NumberMask = ManagedMask | quint64(TagBit::Number) << Tag_Shift; |
217 | static constexpr quint64 IntOrBoolMask = ManagedMask | quint64(TagBit::IntOrBool) << Tag_Shift; |
218 | static constexpr quint64 IntCompatMask = ManagedMask | quint64(TagBit::IntCompat) << Tag_Shift; |
219 | |
220 | static constexpr quint64 EncodeMask = DoubleMask | NumberMask; |
221 | |
222 | static constexpr quint64 DoubleDiscriminator |
223 | = ((quint64(TagBit::Unmanaged) | quint64(TagBit::Special)) << Tag_Shift); |
224 | static constexpr quint64 NumberDiscriminator |
225 | = ((quint64(TagBit::Unmanaged) | quint64(TagBit::Number)) << Tag_Shift); |
226 | |
227 | // Things we can immediately determine by just looking at the upper 4 bytes. |
228 | enum class QuickType : quint32 { |
229 | // Managed takes precedence over all others. That is, other bits may be set if it's managed. |
230 | // However, since all others include the Unmanaged bit, we can still check them with simple |
231 | // equality operations. |
232 | Managed = Managed_Type, |
233 | |
234 | Empty = Empty_Type, |
235 | Null = Null_Type, |
236 | Boolean = Boolean_Type, |
237 | Integer = Integer_Type, |
238 | |
239 | PlusInf = quint32(TagBit::Number) | quint32(TagBit::Special) | quint32(TagBit::Unmanaged), |
240 | MinusInf = PlusInf | quint32(TagBit::SpecialNegative), |
241 | NaN = PlusInf | quint32(TagBit::SpecialQNaN), |
242 | MinusNaN = NaN | quint32(TagBit::SpecialNegative), // Can happen with UMinus on NaN |
243 | // All other values are doubles |
244 | }; |
245 | |
246 | // Aliases for easier porting. Remove those when possible |
247 | using ValueTypeInternal = QuickType; |
248 | enum { |
249 | QT_Empty = Empty_Type, |
250 | QT_Null = Null_Type, |
251 | QT_Bool = Boolean_Type, |
252 | QT_Int = Integer_Type, |
253 | QuickType_Shift = Tag_Shift, |
254 | }; |
255 | |
256 | inline Type type() const |
257 | { |
258 | const quint64 masked = _val & DoubleMask; |
259 | if (masked >= DoubleDiscriminator) |
260 | return Double_Type; |
261 | |
262 | // Any bit set in the exponent would have been caught above, as well as both bits being set. |
263 | // None of them being set as well as only Special being set means "managed". |
264 | // Only Unmanaged being set means "unmanaged". That's all remaining options. |
265 | if (masked != tagBitMask(bit: TagBit::Unmanaged)) { |
266 | Q_ASSERT((_val & tagBitMask(TagBit::Unmanaged)) == 0); |
267 | return isUndefined() ? Undefined_Type : Managed_Type; |
268 | } |
269 | |
270 | const Type ret = Type(tag()); |
271 | Q_ASSERT( |
272 | ret == Empty_Type || |
273 | ret == Null_Type || |
274 | ret == Boolean_Type || |
275 | ret == Integer_Type); |
276 | return ret; |
277 | } |
278 | |
279 | inline quint64 quickType() const { return (_val >> QuickType_Shift); } |
280 | |
281 | // used internally in property |
282 | inline bool isEmpty() const { return tag() == quint32(ValueTypeInternal::Empty); } |
283 | inline bool isNull() const { return tag() == quint32(ValueTypeInternal::Null); } |
284 | inline bool isBoolean() const { return tag() == quint32(ValueTypeInternal::Boolean); } |
285 | inline bool isInteger() const { return tag() == quint32(ValueTypeInternal::Integer); } |
286 | inline bool isNullOrUndefined() const { return isNull() || isUndefined(); } |
287 | inline bool isUndefined() const { return _val == 0; } |
288 | |
289 | inline bool isDouble() const |
290 | { |
291 | // If any of the flipped exponent bits are 1, it's a regular double, and the masked tag is |
292 | // larger than Unmanaged | Special. |
293 | // |
294 | // If all (flipped) exponent bits are 0: |
295 | // 1. If Unmanaged bit is 0, it's managed |
296 | // 2. If the Unmanaged bit it is 1, and the Special bit is 0, it's not a special double |
297 | // 3. If both are 1, it is a special double and the masked tag equals Unmanaged | Special. |
298 | |
299 | return (_val & DoubleMask) >= DoubleDiscriminator; |
300 | } |
301 | |
302 | inline bool isNumber() const |
303 | { |
304 | // If any of the flipped exponent bits are 1, it's a regular double, and the masked tag is |
305 | // larger than Unmanaged | Number. |
306 | // |
307 | // If all (flipped) exponent bits are 0: |
308 | // 1. If Unmanaged bit is 0, it's managed |
309 | // 2. If the Unmanaged bit it is 1, and the Number bit is 0, it's not number |
310 | // 3. If both are 1, it is a number and masked tag equals Unmanaged | Number. |
311 | |
312 | return (_val & NumberMask) >= NumberDiscriminator; |
313 | } |
314 | |
315 | inline bool isManagedOrUndefined() const { return (_val & ManagedMask) == 0; } |
316 | |
317 | // If any other bit is set in addition to the managed mask, it's not undefined. |
318 | inline bool isManaged() const |
319 | { |
320 | return isManagedOrUndefined() && !isUndefined(); |
321 | } |
322 | |
323 | inline bool isIntOrBool() const |
324 | { |
325 | // It's an int or bool if all the exponent bits are 0, |
326 | // and the "int or bool" bit as well as the "umanaged" bit are set, |
327 | return (_val >> IsIntegerOrBool_Shift) == IsIntegerOrBool_Value; |
328 | } |
329 | |
330 | inline bool integerCompatible() const { |
331 | Q_ASSERT(!isEmpty()); |
332 | return (_val >> IsIntegerConvertible_Shift) == IsIntegerConvertible_Value; |
333 | } |
334 | |
335 | static inline bool integerCompatible(StaticValue a, StaticValue b) { |
336 | return a.integerCompatible() && b.integerCompatible(); |
337 | } |
338 | |
339 | static inline bool bothDouble(StaticValue a, StaticValue b) { |
340 | return a.isDouble() && b.isDouble(); |
341 | } |
342 | |
343 | inline bool isNaN() const |
344 | { |
345 | switch (QuickType(tag())) { |
346 | case QuickType::NaN: |
347 | case QuickType::MinusNaN: |
348 | return true; |
349 | default: |
350 | return false; |
351 | } |
352 | } |
353 | |
354 | inline bool isPositiveInt() const { |
355 | return isInteger() && int_32() >= 0; |
356 | } |
357 | |
358 | QV4_NEARLY_ALWAYS_INLINE double doubleValue() const { |
359 | Q_ASSERT(isDouble()); |
360 | double d; |
361 | const quint64 unmasked = _val ^ EncodeMask; |
362 | memcpy(dest: &d, src: &unmasked, n: 8); |
363 | return d; |
364 | } |
365 | |
366 | QV4_NEARLY_ALWAYS_INLINE void setDouble(double d) { |
367 | if (qt_is_nan(d)) { |
368 | // We cannot store just any NaN. It has to be a NaN with only the quiet bit |
369 | // set in the upper bits of the mantissa and the sign bit off. |
370 | // qt_qnan() happens to produce such a thing via std::numeric_limits, |
371 | // but this is actually not guaranteed. Therefore, we make our own. |
372 | _val = (quint64(QuickType::NaN) << Tag_Shift); |
373 | Q_ASSERT(isNaN()); |
374 | } else { |
375 | memcpy(dest: &_val, src: &d, n: 8); |
376 | _val ^= EncodeMask; |
377 | } |
378 | |
379 | Q_ASSERT(isDouble()); |
380 | } |
381 | |
382 | inline bool isInt32() { |
383 | if (tag() == quint32(QuickType::Integer)) |
384 | return true; |
385 | if (isDouble()) { |
386 | double d = doubleValue(); |
387 | if (isInt32(d)) { |
388 | setInt_32(int(d)); |
389 | return true; |
390 | } |
391 | } |
392 | return false; |
393 | } |
394 | |
395 | QV4_NEARLY_ALWAYS_INLINE static bool isInt32(double d) { |
396 | int i = QJSNumberCoercion::toInteger(d); |
397 | return (i == d && !(d == 0 && std::signbit(x: d))); |
398 | } |
399 | |
400 | double asDouble() const { |
401 | if (tag() == quint32(QuickType::Integer)) |
402 | return int_32(); |
403 | return doubleValue(); |
404 | } |
405 | |
406 | bool booleanValue() const { |
407 | return int_32(); |
408 | } |
409 | |
410 | int integerValue() const { |
411 | return int_32(); |
412 | } |
413 | |
414 | inline bool tryIntegerConversion() { |
415 | bool b = integerCompatible(); |
416 | if (b) |
417 | setTagValue(tag: quint32(QuickType::Integer), value: value()); |
418 | return b; |
419 | } |
420 | |
421 | bool toBoolean() const { |
422 | if (integerCompatible()) |
423 | return static_cast<bool>(int_32()); |
424 | |
425 | if (isManagedOrUndefined()) |
426 | return false; |
427 | |
428 | // double |
429 | const double d = doubleValue(); |
430 | return d && !std::isnan(x: d); |
431 | } |
432 | |
433 | inline int toInt32() const |
434 | { |
435 | switch (type()) { |
436 | case Null_Type: |
437 | case Boolean_Type: |
438 | case Integer_Type: |
439 | return int_32(); |
440 | case Double_Type: |
441 | return QJSNumberCoercion::toInteger(d: doubleValue()); |
442 | case Empty_Type: |
443 | case Undefined_Type: |
444 | case Managed_Type: |
445 | return 0; // Coercion of NaN to int, results in 0; |
446 | } |
447 | |
448 | Q_UNREACHABLE_RETURN(0); |
449 | } |
450 | |
451 | ReturnedValue *data_ptr() { return &_val; } |
452 | constexpr ReturnedValue asReturnedValue() const { return _val; } |
453 | constexpr static StaticValue fromReturnedValue(ReturnedValue val) { return {val}; } |
454 | |
455 | inline static constexpr StaticValue emptyValue() { return { tagValue(tag: quint32(QuickType::Empty), value: 0) }; } |
456 | static inline constexpr StaticValue fromBoolean(bool b) { return { tagValue(tag: quint32(QuickType::Boolean), value: b) }; } |
457 | static inline constexpr StaticValue fromInt32(int i) { return { tagValue(tag: quint32(QuickType::Integer), value: quint32(i)) }; } |
458 | inline static constexpr StaticValue undefinedValue() { return { 0 }; } |
459 | static inline constexpr StaticValue nullValue() { return { tagValue(tag: quint32(QuickType::Null), value: 0) }; } |
460 | |
461 | static inline StaticValue fromDouble(double d) |
462 | { |
463 | StaticValue v; |
464 | v.setDouble(d); |
465 | return v; |
466 | } |
467 | |
468 | static inline StaticValue fromUInt32(uint i) |
469 | { |
470 | StaticValue v; |
471 | if (i < uint(std::numeric_limits<int>::max())) { |
472 | v.setTagValue(tag: quint32(QuickType::Integer), value: i); |
473 | } else { |
474 | v.setDouble(i); |
475 | } |
476 | return v; |
477 | } |
478 | |
479 | static double toInteger(double d) |
480 | { |
481 | if (std::isnan(x: d)) |
482 | return +0; |
483 | if (!d || std::isinf(x: d)) |
484 | return d; |
485 | return d >= 0 ? std::floor(x: d) : std::ceil(x: d); |
486 | } |
487 | |
488 | static int toInt32(double d) |
489 | { |
490 | return QJSNumberCoercion::toInteger(d); |
491 | } |
492 | |
493 | static unsigned int toUInt32(double d) |
494 | { |
495 | return static_cast<uint>(toInt32(d)); |
496 | } |
497 | |
498 | // While a value containing a Heap::Base* is not actually static, we still implement |
499 | // the setting and retrieving of heap pointers here in order to have the encoding |
500 | // scheme completely in one place. |
501 | |
502 | #if QT_POINTER_SIZE == 8 |
503 | |
504 | // All pointer shifts are from more significant to less significant bits. |
505 | // When encoding, we shift right by that amount. When decoding, we shift left. |
506 | // Negative numbers mean shifting the other direction. 0 means no shifting. |
507 | // |
508 | // The IA64 and Sparc64 cases are mostly there to demonstrate the idea. Sparc64 |
509 | // and IA64 are not officially supported, but we can expect more platforms with |
510 | // similar "problems" in the future. |
511 | enum PointerShift { |
512 | #if 0 && defined(Q_OS_ANDROID) && defined(Q_PROCESSOR_ARM_64) |
513 | // We used to assume that Android on arm64 uses the top byte to store pointer tags. |
514 | // However, at least currently, the pointer tags are only applied on new/malloc and |
515 | // delete/free, not on mmap() and munmap(). We manage the JS heap directly using |
516 | // mmap, so we don't have to preserve any tags. |
517 | // |
518 | // If this ever changes, here is how to preserve the top byte: |
519 | // Move it to Upper3 and Lower5. |
520 | Top1Shift = 0, |
521 | Upper3Shift = 12, |
522 | Lower5Shift = 56, |
523 | #elif defined(Q_PROCESSOR_IA64) |
524 | // On ia64, bits 63-61 in a 64-bit pointer are used to store the virtual region |
525 | // number. We can move those to Upper3. |
526 | Top1Shift = 0, |
527 | Upper3Shift = 12, |
528 | Lower5Shift = 0, |
529 | #elif defined(Q_PROCESSOR_SPARC_64) |
530 | // Sparc64 wants to use 52 bits for pointers. |
531 | // Upper3 can stay where it is, bit48 moves to the top bit. |
532 | Top1Shift = -15, |
533 | Upper3Shift = 0, |
534 | Lower5Shift = 0, |
535 | #elif 0 // TODO: Once we need 5-level page tables, add the appropriate check here. |
536 | // With 5-level page tables (as possible on linux) we need 57 address bits. |
537 | // Upper3 can stay where it is, bit48 moves to the top bit, the rest moves to Lower5. |
538 | Top1Shift = -15, |
539 | Upper3Shift = 0, |
540 | Lower5Shift = 52, |
541 | #else |
542 | Top1Shift = 0, |
543 | Upper3Shift = 0, |
544 | Lower5Shift = 0 |
545 | #endif |
546 | }; |
547 | |
548 | template<int Offset, quint64 Mask> |
549 | static constexpr quint64 movePointerBits(quint64 val) |
550 | { |
551 | if constexpr (Offset > 0) |
552 | return (val & ~Mask) | ((val & Mask) >> Offset); |
553 | if constexpr (Offset < 0) |
554 | return (val & ~Mask) | ((val & Mask) << -Offset); |
555 | return val; |
556 | } |
557 | |
558 | template<int Offset, quint64 Mask> |
559 | static constexpr quint64 storePointerBits(quint64 val) |
560 | { |
561 | constexpr quint64 OriginMask = movePointerBits<-Offset, Mask>(Mask); |
562 | return movePointerBits<Offset, OriginMask>(val); |
563 | } |
564 | |
565 | template<int Offset, quint64 Mask> |
566 | static constexpr quint64 retrievePointerBits(quint64 val) |
567 | { |
568 | return movePointerBits<-Offset, Mask>(val); |
569 | } |
570 | |
571 | QML_NEARLY_ALWAYS_INLINE HeapBasePtr m() const |
572 | { |
573 | Q_ASSERT(!(_val & ManagedMask)); |
574 | |
575 | // Re-assemble the pointer from its fragments. |
576 | const quint64 tmp = retrievePointerBits<Top1Shift, Top1Mask>( |
577 | val: retrievePointerBits<Upper3Shift, Upper3Mask>( |
578 | val: retrievePointerBits<Lower5Shift, Lower5Mask>(val: _val))); |
579 | |
580 | HeapBasePtr b; |
581 | memcpy(dest: &b, src: &tmp, n: 8); |
582 | return b; |
583 | } |
584 | QML_NEARLY_ALWAYS_INLINE void setM(HeapBasePtr b) |
585 | { |
586 | quint64 tmp; |
587 | memcpy(dest: &tmp, src: &b, n: 8); |
588 | |
589 | // Has to be aligned to 32 bytes |
590 | Q_ASSERT(!(tmp & Lower5Mask)); |
591 | |
592 | // Encode the pointer. |
593 | _val = storePointerBits<Top1Shift, Top1Mask>( |
594 | val: storePointerBits<Upper3Shift, Upper3Mask>( |
595 | val: storePointerBits<Lower5Shift, Lower5Mask>(val: tmp))); |
596 | } |
597 | #elif QT_POINTER_SIZE == 4 |
598 | QML_NEARLY_ALWAYS_INLINE HeapBasePtr m() const |
599 | { |
600 | Q_STATIC_ASSERT(sizeof(HeapBasePtr) == sizeof(quint32)); |
601 | HeapBasePtr b; |
602 | quint32 v = value(); |
603 | memcpy(&b, &v, 4); |
604 | return b; |
605 | } |
606 | QML_NEARLY_ALWAYS_INLINE void setM(HeapBasePtr b) |
607 | { |
608 | quint32 v; |
609 | memcpy(&v, &b, 4); |
610 | setTagValue(quint32(QuickType::Managed), v); |
611 | } |
612 | #else |
613 | # error "unsupported pointer size" |
614 | #endif |
615 | }; |
616 | Q_STATIC_ASSERT(std::is_trivial_v<StaticValue>); |
617 | |
618 | struct Encode { |
619 | static constexpr ReturnedValue undefined() { |
620 | return StaticValue::undefinedValue().asReturnedValue(); |
621 | } |
622 | static constexpr ReturnedValue null() { |
623 | return StaticValue::nullValue().asReturnedValue(); |
624 | } |
625 | |
626 | explicit constexpr Encode(bool b) |
627 | : val(StaticValue::fromBoolean(b).asReturnedValue()) |
628 | { |
629 | } |
630 | explicit Encode(double d) { |
631 | val = StaticValue::fromDouble(d).asReturnedValue(); |
632 | } |
633 | explicit constexpr Encode(int i) |
634 | : val(StaticValue::fromInt32(i).asReturnedValue()) |
635 | { |
636 | } |
637 | explicit Encode(uint i) { |
638 | val = StaticValue::fromUInt32(i).asReturnedValue(); |
639 | } |
640 | explicit constexpr Encode(ReturnedValue v) |
641 | : val(v) |
642 | { |
643 | } |
644 | constexpr Encode(StaticValue v) |
645 | : val(v.asReturnedValue()) |
646 | { |
647 | } |
648 | |
649 | template<typename HeapBase> |
650 | explicit Encode(HeapBase *o); |
651 | |
652 | explicit Encode(StaticValue *o) { |
653 | Q_ASSERT(o); |
654 | val = o->asReturnedValue(); |
655 | } |
656 | |
657 | static ReturnedValue smallestNumber(double d) { |
658 | if (StaticValue::isInt32(d)) |
659 | return Encode(static_cast<int>(d)); |
660 | else |
661 | return Encode(d); |
662 | } |
663 | |
664 | constexpr operator ReturnedValue() const { |
665 | return val; |
666 | } |
667 | quint64 val; |
668 | private: |
669 | explicit Encode(void *); |
670 | }; |
671 | |
672 | } |
673 | |
674 | QT_END_NAMESPACE |
675 | |
676 | #endif // QV4STATICVALUE_P_H |
677 | |