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