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
61QT_BEGIN_NAMESPACE
62
63namespace 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
69typedef quint64 ReturnedValue;
70
71struct 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
118struct 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};
498Q_STATIC_ASSERT(std::is_trivial<StaticValue>::value);
499
500struct 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;
550private:
551 explicit Encode(void *);
552};
553
554}
555
556QT_END_NAMESPACE
557
558#endif // QV4STATICVALUE_P_H
559