diff options
Diffstat (limited to 'src/qml/compiler/qv4staticvalue_p.h')
-rw-r--r-- | src/qml/compiler/qv4staticvalue_p.h | 548 |
1 files changed, 548 insertions, 0 deletions
diff --git a/src/qml/compiler/qv4staticvalue_p.h b/src/qml/compiler/qv4staticvalue_p.h new file mode 100644 index 0000000000..c6b4bdb158 --- /dev/null +++ b/src/qml/compiler/qv4staticvalue_p.h @@ -0,0 +1,548 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4STATICVALUE_P_H +#define QV4STATICVALUE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtQml/private/qtqmlglobal_p.h> +#include <QtQml/private/qv4global_p.h> +#include <QtCore/private/qnumeric_p.h> + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct Double { + quint64 d; + + Double(double dbl) { + memcpy(&d, &dbl, sizeof(double)); + } + + int sign() const { + return (d >> 63) ? -1 : 1; + } + + bool isDenormal() const { + return static_cast<int>((d << 1) >> 53) == 0; + } + + int exponent() const { + return static_cast<int>((d << 1) >> 53) - 1023; + } + + quint64 significant() const { + quint64 m = (d << 12) >> 12; + if (!isDenormal()) + m |= (static_cast<quint64>(1) << 52); + return m; + } + + static int toInt32(double d) { + int i = static_cast<int>(d); + if (i == d) + return i; + return Double(d).toInt32(); + } + + int toInt32() { + int e = exponent() - 52; + if (e < 0) { + if (e <= -53) + return 0; + return sign() * static_cast<int>(significant() >> -e); + } else { + if (e > 31) + return 0; + return sign() * (static_cast<int>(significant()) << e); + } + } +}; + +struct Q_QML_PRIVATE_EXPORT StaticValue +{ + StaticValue() = default; + constexpr StaticValue(quint64 val) : _val(val) {} + + StaticValue &operator=(ReturnedValue v) + { + _val = v; + return *this; + } + + template<typename Value> + StaticValue &operator=(const Value &); + + template<typename Value> + const Value &asValue() const; + + template<typename Value> + Value &asValue(); + + /* + We use 8 bytes for a value and a different variant of NaN boxing. A Double + NaN (actually -qNaN) is indicated by a number that has the top 13 bits set, and for a + signalling NaN it is the top 14 bits. The other values are usually set to 0 by the + processor, and are thus free for us to store other data. We keep pointers in there for + managed objects, and encode the other types using the free space given to use by the unused + bits for NaN values. This also works for pointers on 64 bit systems, as they all currently + only have 48 bits of addressable memory. (Note: we do leave the lower 49 bits available for + pointers.) + + We xor Doubles with (0xffff8000 << 32). That has the effect that no doubles will + get encoded with bits 63-49 all set to 0. We then use bit 48 to distinguish between + managed/undefined (0), or Null/Int/Bool/Empty (1). So, storing a 49 bit pointer will leave + the top 15 bits 0, which is exactly the 'natural' representation of pointers. If bit 49 is + set, bit 48 indicates Empty (0) or integer-convertible (1). Then the 3 bit below that are + used to encode Null/Int/Bool. + + Undefined is encoded as a managed pointer with value 0. This is the same as a nullptr. + + Specific bit-sequences: + 0 = always 0 + 1 = always 1 + x = stored value + a,b,c,d = specific bit values, see notes + + 32109876 54321098 76543210 98765432 10987654 32109876 54321098 76543210 | + 66665555 55555544 44444444 33333333 33222222 22221111 11111100 00000000 | JS Value + ------------------------------------------------------------------------+-------------- + 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 | Undefined + 00000000 0000000x xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | Managed (heap pointer) + a0000000 0000bc00 00000000 00000000 00000000 00000000 00000000 00000000 | NaN/Inf + dddddddd ddddddxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | double + 00000000 00000010 00000000 00000000 00000000 00000000 00000000 00000000 | empty (non-sparse array hole) + 00000000 00000010 10000000 00000000 00000000 00000000 00000000 00000000 | Null + 00000000 00000011 00000000 00000000 00000000 00000000 00000000 0000000x | Bool + 00000000 00000011 10000000 00000000 xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | Int + + Notes: + - a: xor-ed signbit, always 1 for NaN + - bc, xor-ed values: 11 = inf, 10 = sNaN, 01 = qNaN, 00 = boxed value + - d: xor-ed bits, where at least one bit is set, so: (val >> (64-14)) > 0 + - Undefined maps to C++ nullptr, so the "default" initialization is the same for both C++ + and JS + - Managed has the left 15 bits set to 0, so: (val >> (64-15)) == 0 + - empty, Null, Bool, and Int have the left 14 bits set to 0, and bit 49 set to 1, + so: (val >> (64-15)) == 1 + - Null, Bool, and Int have bit 48 set, indicating integer-convertible + - xoring _val with NaNEncodeMask will convert to a double in "natural" representation, where + any non double results in a NaN + - on 32bit we can use the fact that addresses are 32bits wide, so the tag part (bits 32 to + 63) are zero. No need to shift. + */ + + quint64 _val; + + QML_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR quint64 &rawValueRef() { return _val; } + QML_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR quint64 rawValue() const { return _val; } + QML_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR void setRawValue(quint64 raw) { _val = raw; } + +#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN + static inline int valueOffset() { return 0; } + static inline int tagOffset() { return 4; } +#else // !Q_LITTLE_ENDIAN + static inline int valueOffset() { return 4; } + static inline int tagOffset() { return 0; } +#endif + static inline constexpr quint64 tagValue(quint32 tag, quint32 value) { return quint64(tag) << 32 | value; } + QML_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR void setTagValue(quint32 tag, quint32 value) { _val = quint64(tag) << 32 | value; } + QML_NEARLY_ALWAYS_INLINE constexpr quint32 value() const { return _val & quint64(~quint32(0)); } + QML_NEARLY_ALWAYS_INLINE constexpr quint32 tag() const { return _val >> 32; } + QML_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR void setTag(quint32 tag) { setTagValue(tag, value()); } + + QML_NEARLY_ALWAYS_INLINE constexpr int int_32() const + { + return int(value()); + } + QML_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR void setInt_32(int i) + { + setTagValue(quint32(ValueTypeInternal::Integer), quint32(i)); + } + QML_NEARLY_ALWAYS_INLINE uint uint_32() const { return value(); } + + QML_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR void setEmpty() + { + setTagValue(quint32(ValueTypeInternal::Empty), 0); + } + + // ### Fix for 32 bit (easiest solution is to set highest bit to 1 for mananged/undefined/integercompatible + // and use negative numbers here + enum QuickType { + QT_ManagedOrUndefined = 0, + QT_ManagedOrUndefined1 = 1, + QT_ManagedOrUndefined2 = 2, + QT_ManagedOrUndefined3 = 3, + QT_Empty = 4, + QT_Null = 5, + QT_Bool = 6, + QT_Int = 7 + // all other values are doubles + }; + + enum Type { + Undefined_Type = 0, + Managed_Type = 1, + Empty_Type = 4, + Null_Type = 5, + Boolean_Type = 6, + Integer_Type = 7, + Double_Type = 8 + }; + + inline Type type() const { + int t = quickType(); + if (t < QT_Empty) + return _val ? Managed_Type : Undefined_Type; + if (t > QT_Int) + return Double_Type; + return static_cast<Type>(t); + } + + // Shared between 32-bit and 64-bit encoding + enum { + Tag_Shift = 32 + }; + + // Used only by 64-bit encoding + static const quint64 NaNEncodeMask = 0xfffc000000000000ull; + enum { + IsDouble_Shift = 64-14, + IsManagedOrUndefined_Shift = 64-15, + IsIntegerConvertible_Shift = 64-15, + IsIntegerOrBool_Shift = 64-16, + QuickType_Shift = 64 - 17, + IsPositiveIntShift = 31 + }; + + static const quint64 Immediate_Mask_64 = 0x00020000u; // bit 49 + + enum class ValueTypeInternal_64 { + Empty = Immediate_Mask_64 | 0, + Null = Immediate_Mask_64 | 0x08000u, + Boolean = Immediate_Mask_64 | 0x10000u, + Integer = Immediate_Mask_64 | 0x18000u + }; + + // Used only by 32-bit encoding + enum Masks { + SilentNaNBit = 0x00040000, + NotDouble_Mask = 0x7ffa0000, + }; + static const quint64 Immediate_Mask_32 = NotDouble_Mask | 0x00020000u | SilentNaNBit; + + enum class ValueTypeInternal_32 { + Empty = Immediate_Mask_32 | 0, + Null = Immediate_Mask_32 | 0x08000u, + Boolean = Immediate_Mask_32 | 0x10000u, + Integer = Immediate_Mask_32 | 0x18000u + }; + + enum { + Managed_Type_Internal = 0 + }; + + using ValueTypeInternal = ValueTypeInternal_64; + + enum { + NaN_Mask = 0x7ff80000, + }; + + inline quint64 quickType() const { return (_val >> QuickType_Shift); } + + // used internally in property + inline bool isEmpty() const { return tag() == quint32(ValueTypeInternal::Empty); } + inline bool isNull() const { return tag() == quint32(ValueTypeInternal::Null); } + inline bool isBoolean() const { return tag() == quint32(ValueTypeInternal::Boolean); } + inline bool isInteger() const { return tag() == quint32(ValueTypeInternal::Integer); } + inline bool isNullOrUndefined() const { return isNull() || isUndefined(); } + inline bool isNumber() const { return quickType() >= QT_Int; } + + inline bool isUndefined() const { return _val == 0; } + inline bool isDouble() const { return (_val >> IsDouble_Shift); } + inline bool isManaged() const + { +#if QT_POINTER_SIZE == 4 + return value() && tag() == Managed_Type_Internal; +#else + return _val && ((_val >> IsManagedOrUndefined_Shift) == 0); +#endif + } + inline bool isManagedOrUndefined() const + { +#if QT_POINTER_SIZE == 4 + return tag() == Managed_Type_Internal; +#else + return ((_val >> IsManagedOrUndefined_Shift) == 0); +#endif + } + + inline bool isIntOrBool() const { + return (_val >> IsIntegerOrBool_Shift) == 3; + } + + inline bool integerCompatible() const { + Q_ASSERT(!isEmpty()); + return (_val >> IsIntegerConvertible_Shift) == 1; + } + + static inline bool integerCompatible(StaticValue a, StaticValue b) { + return a.integerCompatible() && b.integerCompatible(); + } + + static inline bool bothDouble(StaticValue a, StaticValue b) { + return a.isDouble() && b.isDouble(); + } + + inline bool isNaN() const + { + return (tag() & 0x7ffc0000 ) == 0x00040000; + } + + inline bool isPositiveInt() const { +#if QT_POINTER_SIZE == 4 + return isInteger() && int_32() >= 0; +#else + return (_val >> IsPositiveIntShift) == (quint64(ValueTypeInternal::Integer) << 1); +#endif + } + + QML_NEARLY_ALWAYS_INLINE double doubleValue() const { + Q_ASSERT(isDouble()); + double d; + StaticValue v = *this; + v._val ^= NaNEncodeMask; + memcpy(&d, &v._val, 8); + return d; + } + + QML_NEARLY_ALWAYS_INLINE void setDouble(double d) { + if (qt_is_nan(d)) + d = qt_qnan(); + memcpy(&_val, &d, 8); + _val ^= NaNEncodeMask; + Q_ASSERT(isDouble()); + } + + inline bool isInt32() { + if (tag() == quint32(ValueTypeInternal::Integer)) + return true; + if (isDouble()) { + double d = doubleValue(); + if (isInt32(d)) { + setInt_32(int(d)); + return true; + } + } + return false; + } + + QML_NEARLY_ALWAYS_INLINE static bool isInt32(double d) { + int i = int(d); + return (i == d && !(d == 0 && std::signbit(d))); + } + + double asDouble() const { + if (tag() == quint32(ValueTypeInternal::Integer)) + return int_32(); + return doubleValue(); + } + + bool booleanValue() const { + return int_32(); + } + + int integerValue() const { + return int_32(); + } + + inline bool tryIntegerConversion() { + bool b = integerCompatible(); + if (b) + setTagValue(quint32(ValueTypeInternal::Integer), value()); + return b; + } + + bool toBoolean() const { + if (integerCompatible()) + return static_cast<bool>(int_32()); + + if (isManagedOrUndefined()) + return false; + + // double + const double d = doubleValue(); + return d && !std::isnan(d); + } + + inline int toInt32() const + { + switch (type()) { + case Null_Type: + case Boolean_Type: + case Integer_Type: + return int_32(); + case Double_Type: + return Double::toInt32(doubleValue()); + case Empty_Type: + case Undefined_Type: + case Managed_Type: + break; + } + return Double::toInt32(std::numeric_limits<double>::quiet_NaN()); + } + + ReturnedValue *data_ptr() { return &_val; } + constexpr ReturnedValue asReturnedValue() const { return _val; } + constexpr static StaticValue fromReturnedValue(ReturnedValue val) { return {val}; } + + inline static constexpr StaticValue emptyValue() { return { tagValue(quint32(ValueTypeInternal::Empty), 0) }; } + static inline constexpr StaticValue fromBoolean(bool b) { return { tagValue(quint32(ValueTypeInternal::Boolean), b) }; } + static inline constexpr StaticValue fromInt32(int i) { return { tagValue(quint32(ValueTypeInternal::Integer), quint32(i)) }; } + inline static constexpr StaticValue undefinedValue() { return { 0 }; } + static inline constexpr StaticValue nullValue() { return { tagValue(quint32(ValueTypeInternal::Null), 0) }; } + + static inline StaticValue fromDouble(double d) + { + StaticValue v; + v.setDouble(d); + return v; + } + + static inline StaticValue fromUInt32(uint i) + { + StaticValue v; + if (i < uint(std::numeric_limits<int>::max())) { + v.setTagValue(quint32(ValueTypeInternal::Integer), i); + } else { + v.setDouble(i); + } + return v; + } + + static double toInteger(double d) + { + if (std::isnan(d)) + return +0; + if (!d || std::isinf(d)) + return d; + return d >= 0 ? std::floor(d) : std::ceil(d); + } + + static int toInt32(double d) + { + return Double::toInt32(d); + } + + static unsigned int toUInt32(double d) + { + return static_cast<uint>(toInt32(d)); + } +}; +Q_STATIC_ASSERT(std::is_trivial<StaticValue>::value); + +struct Encode { + static constexpr ReturnedValue undefined() { + return StaticValue::undefinedValue().asReturnedValue(); + } + static constexpr ReturnedValue null() { + return StaticValue::nullValue().asReturnedValue(); + } + + explicit constexpr Encode(bool b) + : val(StaticValue::fromBoolean(b).asReturnedValue()) + { + } + explicit Encode(double d) { + val = StaticValue::fromDouble(d).asReturnedValue(); + } + explicit constexpr Encode(int i) + : val(StaticValue::fromInt32(i).asReturnedValue()) + { + } + explicit Encode(uint i) { + val = StaticValue::fromUInt32(i).asReturnedValue(); + } + explicit constexpr Encode(ReturnedValue v) + : val(v) + { + } + constexpr Encode(StaticValue v) + : val(v.asReturnedValue()) + { + } + + template<typename HeapBase> + explicit Encode(HeapBase *o); + + explicit Encode(StaticValue *o) { + Q_ASSERT(o); + val = o->asReturnedValue(); + } + + static ReturnedValue smallestNumber(double d) { + if (StaticValue::isInt32(d)) + return Encode(static_cast<int>(d)); + else + return Encode(d); + } + + constexpr operator ReturnedValue() const { + return val; + } + quint64 val; +private: + explicit Encode(void *); +}; + +} + +QT_END_NAMESPACE + +#endif // QV4STATICVALUE_P_H |