1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2019 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the QtQml module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 3 requirements |
23 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
24 | ** |
25 | ** GNU General Public License Usage |
26 | ** Alternatively, this file may be used under the terms of the GNU |
27 | ** General Public License version 2.0 or (at your option) the GNU General |
28 | ** Public license version 3 or any later version approved by the KDE Free |
29 | ** Qt Foundation. The licenses are as published by the Free Software |
30 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
31 | ** included in the packaging of this file. Please review the following |
32 | ** information to ensure the GNU General Public License requirements will |
33 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
34 | ** https://www.gnu.org/licenses/gpl-3.0.html. |
35 | ** |
36 | ** $QT_END_LICENSE$ |
37 | ** |
38 | ****************************************************************************/ |
39 | #ifndef QV4STATICVALUE_P_H |
40 | #define QV4STATICVALUE_P_H |
41 | |
42 | // |
43 | // W A R N I N G |
44 | // ------------- |
45 | // |
46 | // This file is not part of the Qt API. It exists purely as an |
47 | // implementation detail. This header file may change from version to |
48 | // version without notice, or even be removed. |
49 | // |
50 | // We mean it. |
51 | // |
52 | |
53 | #include <QtCore/private/qnumeric_p.h> |
54 | |
55 | #ifdef QT_NO_DEBUG |
56 | #define QV4_NEARLY_ALWAYS_INLINE Q_ALWAYS_INLINE |
57 | #else |
58 | #define QV4_NEARLY_ALWAYS_INLINE inline |
59 | #endif |
60 | |
61 | QT_BEGIN_NAMESPACE |
62 | |
63 | namespace QV4 { |
64 | |
65 | // ReturnedValue is used to return values from runtime methods |
66 | // the type has to be a primitive type (no struct or union), so that the compiler |
67 | // will return it in a register on all platforms. |
68 | // It will be returned in rax on x64, [eax,edx] on x86 and [r0,r1] on arm |
69 | typedef quint64 ReturnedValue; |
70 | |
71 | struct Double { |
72 | quint64 d; |
73 | |
74 | Double(double dbl) { |
75 | memcpy(&d, &dbl, sizeof(double)); |
76 | } |
77 | |
78 | int sign() const { |
79 | return (d >> 63) ? -1 : 1; |
80 | } |
81 | |
82 | bool isDenormal() const { |
83 | return static_cast<int>((d << 1) >> 53) == 0; |
84 | } |
85 | |
86 | int exponent() const { |
87 | return static_cast<int>((d << 1) >> 53) - 1023; |
88 | } |
89 | |
90 | quint64 significant() const { |
91 | quint64 m = (d << 12) >> 12; |
92 | if (!isDenormal()) |
93 | m |= (static_cast<quint64>(1) << 52); |
94 | return m; |
95 | } |
96 | |
97 | static int toInt32(double d) { |
98 | int i = static_cast<int>(d); |
99 | if (i == d) |
100 | return i; |
101 | return Double(d).toInt32(); |
102 | } |
103 | |
104 | int toInt32() { |
105 | int e = exponent() - 52; |
106 | if (e < 0) { |
107 | if (e <= -53) |
108 | return 0; |
109 | return sign() * static_cast<int>(significant() >> -e); |
110 | } else { |
111 | if (e > 31) |
112 | return 0; |
113 | return sign() * (static_cast<int>(significant()) << e); |
114 | } |
115 | } |
116 | }; |
117 | |
118 | struct StaticValue |
119 | { |
120 | StaticValue() = default; |
121 | constexpr StaticValue(quint64 val) : _val(val) {} |
122 | |
123 | StaticValue &operator=(ReturnedValue v) |
124 | { |
125 | _val = v; |
126 | return *this; |
127 | } |
128 | |
129 | template<typename Value> |
130 | StaticValue &operator=(const Value &); |
131 | |
132 | template<typename Value> |
133 | const Value &asValue() const; |
134 | |
135 | template<typename Value> |
136 | Value &asValue(); |
137 | |
138 | /* |
139 | We use 8 bytes for a value and a different variant of NaN boxing. A Double |
140 | NaN (actually -qNaN) is indicated by a number that has the top 13 bits set, and for a |
141 | signalling NaN it is the top 14 bits. The other values are usually set to 0 by the |
142 | processor, and are thus free for us to store other data. We keep pointers in there for |
143 | managed objects, and encode the other types using the free space given to use by the unused |
144 | bits for NaN values. This also works for pointers on 64 bit systems, as they all currently |
145 | only have 48 bits of addressable memory. (Note: we do leave the lower 49 bits available for |
146 | pointers.) |
147 | |
148 | We xor Doubles with (0xffff8000 << 32). That has the effect that no doubles will |
149 | get encoded with bits 63-49 all set to 0. We then use bit 48 to distinguish between |
150 | managed/undefined (0), or Null/Int/Bool/Empty (1). So, storing a 49 bit pointer will leave |
151 | the top 15 bits 0, which is exactly the 'natural' representation of pointers. If bit 49 is |
152 | set, bit 48 indicates Empty (0) or integer-convertible (1). Then the 3 bit below that are |
153 | used to encode Null/Int/Bool. |
154 | |
155 | Undefined is encoded as a managed pointer with value 0. This is the same as a nullptr. |
156 | |
157 | Specific bit-sequences: |
158 | 0 = always 0 |
159 | 1 = always 1 |
160 | x = stored value |
161 | a,b,c,d = specific bit values, see notes |
162 | |
163 | 32109876 54321098 76543210 98765432 10987654 32109876 54321098 76543210 | |
164 | 66665555 55555544 44444444 33333333 33222222 22221111 11111100 00000000 | JS Value |
165 | ------------------------------------------------------------------------+-------------- |
166 | 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 | Undefined |
167 | 00000000 0000000x xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | Managed (heap pointer) |
168 | a0000000 0000bc00 00000000 00000000 00000000 00000000 00000000 00000000 | NaN/Inf |
169 | dddddddd ddddddxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | double |
170 | 00000000 00000010 00000000 00000000 00000000 00000000 00000000 00000000 | empty (non-sparse array hole) |
171 | 00000000 00000010 10000000 00000000 00000000 00000000 00000000 00000000 | Null |
172 | 00000000 00000011 00000000 00000000 00000000 00000000 00000000 0000000x | Bool |
173 | 00000000 00000011 10000000 00000000 xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | Int |
174 | |
175 | Notes: |
176 | - a: xor-ed signbit, always 1 for NaN |
177 | - bc, xor-ed values: 11 = inf, 10 = sNaN, 01 = qNaN, 00 = boxed value |
178 | - d: xor-ed bits, where at least one bit is set, so: (val >> (64-14)) > 0 |
179 | - Undefined maps to C++ nullptr, so the "default" initialization is the same for both C++ |
180 | and JS |
181 | - Managed has the left 15 bits set to 0, so: (val >> (64-15)) == 0 |
182 | - empty, Null, Bool, and Int have the left 14 bits set to 0, and bit 49 set to 1, |
183 | so: (val >> (64-15)) == 1 |
184 | - Null, Bool, and Int have bit 48 set, indicating integer-convertible |
185 | - xoring _val with NaNEncodeMask will convert to a double in "natural" representation, where |
186 | any non double results in a NaN |
187 | - on 32bit we can use the fact that addresses are 32bits wide, so the tag part (bits 32 to |
188 | 63) are zero. No need to shift. |
189 | */ |
190 | |
191 | quint64 _val; |
192 | |
193 | QV4_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR quint64 &rawValueRef() { return _val; } |
194 | QV4_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR quint64 rawValue() const { return _val; } |
195 | QV4_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR void setRawValue(quint64 raw) { _val = raw; } |
196 | |
197 | #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN |
198 | static inline int valueOffset() { return 0; } |
199 | static inline int tagOffset() { return 4; } |
200 | #else // !Q_LITTLE_ENDIAN |
201 | static inline int valueOffset() { return 4; } |
202 | static inline int tagOffset() { return 0; } |
203 | #endif |
204 | static inline constexpr quint64 tagValue(quint32 tag, quint32 value) { return quint64(tag) << 32 | value; } |
205 | QV4_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR void setTagValue(quint32 tag, quint32 value) { _val = quint64(tag) << 32 | value; } |
206 | QV4_NEARLY_ALWAYS_INLINE constexpr quint32 value() const { return _val & quint64(~quint32(0)); } |
207 | QV4_NEARLY_ALWAYS_INLINE constexpr quint32 tag() const { return _val >> 32; } |
208 | QV4_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR void setTag(quint32 tag) { setTagValue(tag, value()); } |
209 | |
210 | QV4_NEARLY_ALWAYS_INLINE constexpr int int_32() const |
211 | { |
212 | return int(value()); |
213 | } |
214 | QV4_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR void setInt_32(int i) |
215 | { |
216 | setTagValue(quint32(ValueTypeInternal::Integer), quint32(i)); |
217 | } |
218 | QV4_NEARLY_ALWAYS_INLINE uint uint_32() const { return value(); } |
219 | |
220 | QV4_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR void setEmpty() |
221 | { |
222 | setTagValue(quint32(ValueTypeInternal::Empty), 0); |
223 | } |
224 | |
225 | // ### Fix for 32 bit (easiest solution is to set highest bit to 1 for mananged/undefined/integercompatible |
226 | // and use negative numbers here |
227 | enum QuickType { |
228 | QT_ManagedOrUndefined = 0, |
229 | QT_ManagedOrUndefined1 = 1, |
230 | QT_ManagedOrUndefined2 = 2, |
231 | QT_ManagedOrUndefined3 = 3, |
232 | QT_Empty = 4, |
233 | QT_Null = 5, |
234 | QT_Bool = 6, |
235 | QT_Int = 7 |
236 | // all other values are doubles |
237 | }; |
238 | |
239 | enum Type { |
240 | Undefined_Type = 0, |
241 | Managed_Type = 1, |
242 | Empty_Type = 4, |
243 | Null_Type = 5, |
244 | Boolean_Type = 6, |
245 | Integer_Type = 7, |
246 | Double_Type = 8 |
247 | }; |
248 | |
249 | inline Type type() const { |
250 | int t = quickType(); |
251 | if (t < QT_Empty) |
252 | return _val ? Managed_Type : Undefined_Type; |
253 | if (t > QT_Int) |
254 | return Double_Type; |
255 | return static_cast<Type>(t); |
256 | } |
257 | |
258 | // Shared between 32-bit and 64-bit encoding |
259 | enum { |
260 | Tag_Shift = 32 |
261 | }; |
262 | |
263 | // Used only by 64-bit encoding |
264 | static const quint64 NaNEncodeMask = 0xfffc000000000000ull; |
265 | enum { |
266 | IsDouble_Shift = 64-14, |
267 | IsManagedOrUndefined_Shift = 64-15, |
268 | IsIntegerConvertible_Shift = 64-15, |
269 | IsIntegerOrBool_Shift = 64-16, |
270 | QuickType_Shift = 64 - 17, |
271 | IsPositiveIntShift = 31 |
272 | }; |
273 | |
274 | static const quint64 Immediate_Mask_64 = 0x00020000u; // bit 49 |
275 | |
276 | enum class ValueTypeInternal_64 { |
277 | Empty = Immediate_Mask_64 | 0, |
278 | Null = Immediate_Mask_64 | 0x08000u, |
279 | Boolean = Immediate_Mask_64 | 0x10000u, |
280 | Integer = Immediate_Mask_64 | 0x18000u |
281 | }; |
282 | |
283 | // Used only by 32-bit encoding |
284 | enum Masks { |
285 | SilentNaNBit = 0x00040000, |
286 | NotDouble_Mask = 0x7ffa0000, |
287 | }; |
288 | static const quint64 Immediate_Mask_32 = NotDouble_Mask | 0x00020000u | SilentNaNBit; |
289 | |
290 | enum class ValueTypeInternal_32 { |
291 | Empty = Immediate_Mask_32 | 0, |
292 | Null = Immediate_Mask_32 | 0x08000u, |
293 | Boolean = Immediate_Mask_32 | 0x10000u, |
294 | Integer = Immediate_Mask_32 | 0x18000u |
295 | }; |
296 | |
297 | enum { |
298 | Managed_Type_Internal = 0 |
299 | }; |
300 | |
301 | using ValueTypeInternal = ValueTypeInternal_64; |
302 | |
303 | enum { |
304 | NaN_Mask = 0x7ff80000, |
305 | }; |
306 | |
307 | inline quint64 quickType() const { return (_val >> QuickType_Shift); } |
308 | |
309 | // used internally in property |
310 | inline bool isEmpty() const { return tag() == quint32(ValueTypeInternal::Empty); } |
311 | inline bool isNull() const { return tag() == quint32(ValueTypeInternal::Null); } |
312 | inline bool isBoolean() const { return tag() == quint32(ValueTypeInternal::Boolean); } |
313 | inline bool isInteger() const { return tag() == quint32(ValueTypeInternal::Integer); } |
314 | inline bool isNullOrUndefined() const { return isNull() || isUndefined(); } |
315 | inline bool isNumber() const { return quickType() >= QT_Int; } |
316 | |
317 | inline bool isUndefined() const { return _val == 0; } |
318 | inline bool isDouble() const { return (_val >> IsDouble_Shift); } |
319 | inline bool isManaged() const |
320 | { |
321 | #if QT_POINTER_SIZE == 4 |
322 | return value() && tag() == Managed_Type_Internal; |
323 | #else |
324 | return _val && ((_val >> IsManagedOrUndefined_Shift) == 0); |
325 | #endif |
326 | } |
327 | inline bool isManagedOrUndefined() const |
328 | { |
329 | #if QT_POINTER_SIZE == 4 |
330 | return tag() == Managed_Type_Internal; |
331 | #else |
332 | return ((_val >> IsManagedOrUndefined_Shift) == 0); |
333 | #endif |
334 | } |
335 | |
336 | inline bool isIntOrBool() const { |
337 | return (_val >> IsIntegerOrBool_Shift) == 3; |
338 | } |
339 | |
340 | inline bool integerCompatible() const { |
341 | Q_ASSERT(!isEmpty()); |
342 | return (_val >> IsIntegerConvertible_Shift) == 1; |
343 | } |
344 | |
345 | static inline bool integerCompatible(StaticValue a, StaticValue b) { |
346 | return a.integerCompatible() && b.integerCompatible(); |
347 | } |
348 | |
349 | static inline bool bothDouble(StaticValue a, StaticValue b) { |
350 | return a.isDouble() && b.isDouble(); |
351 | } |
352 | |
353 | inline bool isNaN() const |
354 | { |
355 | return (tag() & 0x7ffc0000 ) == 0x00040000; |
356 | } |
357 | |
358 | inline bool isPositiveInt() const { |
359 | #if QT_POINTER_SIZE == 4 |
360 | return isInteger() && int_32() >= 0; |
361 | #else |
362 | return (_val >> IsPositiveIntShift) == (quint64(ValueTypeInternal::Integer) << 1); |
363 | #endif |
364 | } |
365 | |
366 | QV4_NEARLY_ALWAYS_INLINE double doubleValue() const { |
367 | Q_ASSERT(isDouble()); |
368 | double d; |
369 | StaticValue v = *this; |
370 | v._val ^= NaNEncodeMask; |
371 | memcpy(&d, &v._val, 8); |
372 | return d; |
373 | } |
374 | |
375 | QV4_NEARLY_ALWAYS_INLINE void setDouble(double d) { |
376 | if (qt_is_nan(d)) |
377 | d = qt_qnan(); |
378 | memcpy(&_val, &d, 8); |
379 | _val ^= NaNEncodeMask; |
380 | Q_ASSERT(isDouble()); |
381 | } |
382 | |
383 | inline bool isInt32() { |
384 | if (tag() == quint32(ValueTypeInternal::Integer)) |
385 | return true; |
386 | if (isDouble()) { |
387 | double d = doubleValue(); |
388 | if (isInt32(d)) { |
389 | setInt_32(int(d)); |
390 | return true; |
391 | } |
392 | } |
393 | return false; |
394 | } |
395 | |
396 | QV4_NEARLY_ALWAYS_INLINE static bool isInt32(double d) { |
397 | int i = int(d); |
398 | return (i == d && !(d == 0 && std::signbit(d))); |
399 | } |
400 | |
401 | double asDouble() const { |
402 | if (tag() == quint32(ValueTypeInternal::Integer)) |
403 | return int_32(); |
404 | return doubleValue(); |
405 | } |
406 | |
407 | bool booleanValue() const { |
408 | return int_32(); |
409 | } |
410 | |
411 | int integerValue() const { |
412 | return int_32(); |
413 | } |
414 | |
415 | inline bool tryIntegerConversion() { |
416 | bool b = integerCompatible(); |
417 | if (b) |
418 | setTagValue(quint32(ValueTypeInternal::Integer), value()); |
419 | return b; |
420 | } |
421 | |
422 | bool toBoolean() const { |
423 | if (integerCompatible()) |
424 | return static_cast<bool>(int_32()); |
425 | |
426 | if (isManagedOrUndefined()) |
427 | return false; |
428 | |
429 | // double |
430 | const double d = doubleValue(); |
431 | return d && !std::isnan(d); |
432 | } |
433 | |
434 | inline int toInt32() const |
435 | { |
436 | switch (type()) { |
437 | case Null_Type: |
438 | case Boolean_Type: |
439 | case Integer_Type: |
440 | return int_32(); |
441 | case Double_Type: |
442 | return Double::toInt32(doubleValue()); |
443 | case Empty_Type: |
444 | case Undefined_Type: |
445 | case Managed_Type: |
446 | break; |
447 | } |
448 | return Double::toInt32(std::numeric_limits<double>::quiet_NaN()); |
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(quint32(ValueTypeInternal::Empty), 0) }; } |
456 | static inline constexpr StaticValue fromBoolean(bool b) { return { tagValue(quint32(ValueTypeInternal::Boolean), b) }; } |
457 | static inline constexpr StaticValue fromInt32(int i) { return { tagValue(quint32(ValueTypeInternal::Integer), quint32(i)) }; } |
458 | inline static constexpr StaticValue undefinedValue() { return { 0 }; } |
459 | static inline constexpr StaticValue nullValue() { return { tagValue(quint32(ValueTypeInternal::Null), 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(quint32(ValueTypeInternal::Integer), i); |
473 | } else { |
474 | v.setDouble(i); |
475 | } |
476 | return v; |
477 | } |
478 | |
479 | static double toInteger(double d) |
480 | { |
481 | if (std::isnan(d)) |
482 | return +0; |
483 | if (!d || std::isinf(d)) |
484 | return d; |
485 | return d >= 0 ? std::floor(d) : std::ceil(d); |
486 | } |
487 | |
488 | static int toInt32(double d) |
489 | { |
490 | return Double::toInt32(d); |
491 | } |
492 | |
493 | static unsigned int toUInt32(double d) |
494 | { |
495 | return static_cast<uint>(toInt32(d)); |
496 | } |
497 | }; |
498 | Q_STATIC_ASSERT(std::is_trivial<StaticValue>::value); |
499 | |
500 | struct Encode { |
501 | static constexpr ReturnedValue undefined() { |
502 | return StaticValue::undefinedValue().asReturnedValue(); |
503 | } |
504 | static constexpr ReturnedValue null() { |
505 | return StaticValue::nullValue().asReturnedValue(); |
506 | } |
507 | |
508 | explicit constexpr Encode(bool b) |
509 | : val(StaticValue::fromBoolean(b).asReturnedValue()) |
510 | { |
511 | } |
512 | explicit Encode(double d) { |
513 | val = StaticValue::fromDouble(d).asReturnedValue(); |
514 | } |
515 | explicit constexpr Encode(int i) |
516 | : val(StaticValue::fromInt32(i).asReturnedValue()) |
517 | { |
518 | } |
519 | explicit Encode(uint i) { |
520 | val = StaticValue::fromUInt32(i).asReturnedValue(); |
521 | } |
522 | explicit constexpr Encode(ReturnedValue v) |
523 | : val(v) |
524 | { |
525 | } |
526 | constexpr Encode(StaticValue v) |
527 | : val(v.asReturnedValue()) |
528 | { |
529 | } |
530 | |
531 | template<typename HeapBase> |
532 | explicit Encode(HeapBase *o); |
533 | |
534 | explicit Encode(StaticValue *o) { |
535 | Q_ASSERT(o); |
536 | val = o->asReturnedValue(); |
537 | } |
538 | |
539 | static ReturnedValue smallestNumber(double d) { |
540 | if (StaticValue::isInt32(d)) |
541 | return Encode(static_cast<int>(d)); |
542 | else |
543 | return Encode(d); |
544 | } |
545 | |
546 | constexpr operator ReturnedValue() const { |
547 | return val; |
548 | } |
549 | quint64 val; |
550 | private: |
551 | explicit Encode(void *); |
552 | }; |
553 | |
554 | } |
555 | |
556 | QT_END_NAMESPACE |
557 | |
558 | #endif // QV4STATICVALUE_P_H |
559 | |