File V4-Free-up-2-address-bits-in-64bit-mode.patch of Package libqt5-qtdeclarative.3900

From 60c669f13fd331788a2e2aab8af926d6aa7c46a6 Mon Sep 17 00:00:00 2001
From: Erik Verbruggen <erik.verbruggen@digia.com>
Date: Fri, 23 Sep 2016 11:34:12 +0200
Subject: [PATCH] V4: Free up 2 address bits in 64bit mode

This allows for the OS to use 49 address bits. It also maps JS Undefined
to the C++ nullptr on 64bit.

Task-number: QTBUG-54822
Change-Id: I7cc90620f499be1506a61aac77d72d067308838c
---
 src/qml/debugger/qqmldebug.cpp       |   2 +-
 src/qml/jit/qv4assembler.cpp         |  38 +++++-
 src/qml/jit/qv4assembler_p.h         |   5 +
 src/qml/jit/qv4isel_masm.cpp         |  98 ++++++++++----
 src/qml/jit/qv4isel_masm_p.h         |   4 +-
 src/qml/jsruntime/qv4scopedvalue_p.h |   2 +-
 src/qml/jsruntime/qv4value_p.h       | 246 ++++++++++++++++++++---------------
 src/qml/jsruntime/qv4vme_moth.cpp    |  26 ++--
 8 files changed, 269 insertions(+), 152 deletions(-)

Index: qtdeclarative-opensource-src-5.6.1/src/qml/debugger/qqmldebug.cpp
===================================================================
--- qtdeclarative-opensource-src-5.6.1.orig/src/qml/debugger/qqmldebug.cpp
+++ qtdeclarative-opensource-src-5.6.1/src/qml/debugger/qqmldebug.cpp
@@ -132,7 +132,7 @@ quintptr Q_QML_EXPORT qtDeclarativeHookD
     // TypeInformationVersion, an integral value, bumped whenever private
     // object sizes or member offsets that are used in Qt Creator's
     // data structure "pretty printing" change.
-    1
+    2
 };
 
 Q_STATIC_ASSERT(HookCount == sizeof(qtDeclarativeHookData) / sizeof(qtDeclarativeHookData[0]));
Index: qtdeclarative-opensource-src-5.6.1/src/qml/jit/qv4assembler.cpp
===================================================================
--- qtdeclarative-opensource-src-5.6.1.orig/src/qml/jit/qv4assembler.cpp
+++ qtdeclarative-opensource-src-5.6.1/src/qml/jit/qv4assembler.cpp
@@ -133,8 +133,30 @@ void Assembler::generateCJumpOnNonZero(R
     generateCJumpOnCompare(NotEqual, reg, TrustedImm32(0), currentBlock, trueBlock, falseBlock);
 }
 
-void Assembler::generateCJumpOnCompare(RelationalCondition cond, RegisterID left,TrustedImm32 right,
-                                       IR::BasicBlock *currentBlock,  IR::BasicBlock *trueBlock,
+#ifdef QV4_USE_64_BIT_VALUE_ENCODING
+void Assembler::generateCJumpOnCompare(RelationalCondition cond,
+                                       RegisterID left,
+                                       TrustedImm64 right,
+                                       IR::BasicBlock *currentBlock,
+                                       IR::BasicBlock *trueBlock,
+                                       IR::BasicBlock *falseBlock)
+{
+    if (trueBlock == _nextBlock) {
+        Jump target = branch64(invert(cond), left, right);
+        addPatch(falseBlock, target);
+    } else {
+        Jump target = branch64(cond, left, right);
+        addPatch(trueBlock, target);
+        jumpToBlock(currentBlock, falseBlock);
+    }
+}
+#endif
+
+void Assembler::generateCJumpOnCompare(RelationalCondition cond,
+                                       RegisterID left,
+                                       TrustedImm32 right,
+                                       IR::BasicBlock *currentBlock,
+                                       IR::BasicBlock *trueBlock,
                                        IR::BasicBlock *falseBlock)
 {
     if (trueBlock == _nextBlock) {
@@ -147,8 +169,11 @@ void Assembler::generateCJumpOnCompare(R
     }
 }
 
-void Assembler::generateCJumpOnCompare(RelationalCondition cond, RegisterID left, RegisterID right,
-                                       IR::BasicBlock *currentBlock,  IR::BasicBlock *trueBlock,
+void Assembler::generateCJumpOnCompare(RelationalCondition cond,
+                                       RegisterID left,
+                                       RegisterID right,
+                                       IR::BasicBlock *currentBlock,
+                                       IR::BasicBlock *trueBlock,
                                        IR::BasicBlock *falseBlock)
 {
     if (trueBlock == _nextBlock) {
@@ -334,9 +359,8 @@ Assembler::Jump Assembler::genTryDoubleC
     // not an int, check if it's a double:
     isNoInt.link(this);
 #ifdef QV4_USE_64_BIT_VALUE_ENCODING
-    and32(Assembler::TrustedImm32(Value::IsDouble_Mask), Assembler::ScratchRegister);
-    Assembler::Jump isNoDbl = branch32(Assembler::Equal, Assembler::ScratchRegister,
-                                            Assembler::TrustedImm32(0));
+    rshift32(TrustedImm32(Value::IsDoubleTag_Shift), ScratchRegister);
+    Assembler::Jump isNoDbl = branch32(Equal, ScratchRegister, TrustedImm32(0));
 #else
     and32(Assembler::TrustedImm32(Value::NotDouble_Mask), Assembler::ScratchRegister);
     Assembler::Jump isNoDbl = branch32(Assembler::Equal, Assembler::ScratchRegister,
Index: qtdeclarative-opensource-src-5.6.1/src/qml/jit/qv4assembler_p.h
===================================================================
--- qtdeclarative-opensource-src-5.6.1.orig/src/qml/jit/qv4assembler_p.h
+++ qtdeclarative-opensource-src-5.6.1/src/qml/jit/qv4assembler_p.h
@@ -374,6 +374,11 @@ public:
     void addPatch(DataLabelPtr patch, IR::BasicBlock *target);
     void generateCJumpOnNonZero(RegisterID reg, IR::BasicBlock *currentBlock,
                              IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock);
+#ifdef QV4_USE_64_BIT_VALUE_ENCODING
+    void generateCJumpOnCompare(RelationalCondition cond, RegisterID left, TrustedImm64 right,
+                                IR::BasicBlock *currentBlock, IR::BasicBlock *trueBlock,
+                                IR::BasicBlock *falseBlock);
+#endif
     void generateCJumpOnCompare(RelationalCondition cond, RegisterID left, TrustedImm32 right,
                                 IR::BasicBlock *currentBlock, IR::BasicBlock *trueBlock,
                                 IR::BasicBlock *falseBlock);
Index: qtdeclarative-opensource-src-5.6.1/src/qml/jit/qv4isel_masm.cpp
===================================================================
--- qtdeclarative-opensource-src-5.6.1.orig/src/qml/jit/qv4isel_masm.cpp
+++ qtdeclarative-opensource-src-5.6.1/src/qml/jit/qv4isel_masm.cpp
@@ -703,7 +703,7 @@ void InstructionSelection::loadString(co
 #else
     _as->store32(Assembler::ReturnValueRegister, destAddr);
     destAddr.offset += 4;
-    _as->store32(Assembler::TrustedImm32(QV4::Value::Managed_Type), destAddr);
+    _as->store32(Assembler::TrustedImm32(QV4::Value::Managed_Type_Internal), destAddr);
 #endif
 }
 
@@ -1103,7 +1103,7 @@ void InstructionSelection::convertTypeTo
         // not an int, check if it's NOT a double:
         isNoInt.link(_as);
 #ifdef QV4_USE_64_BIT_VALUE_ENCODING
-        _as->and32(Assembler::TrustedImm32(Value::IsDouble_Mask), Assembler::ScratchRegister);
+        _as->rshift32(Assembler::TrustedImm32(Value::IsDoubleTag_Shift), Assembler::ScratchRegister);
         Assembler::Jump isDbl = _as->branch32(Assembler::NotEqual, Assembler::ScratchRegister,
                                               Assembler::TrustedImm32(0));
 #else
@@ -1194,10 +1194,15 @@ void InstructionSelection::convertTypeTo
         _as->load64(addr, Assembler::ScratchRegister);
         _as->move(Assembler::ScratchRegister, Assembler::ReturnValueRegister);
 
-        // check if it's a number
-        _as->urshift64(Assembler::TrustedImm32(QV4::Value::IsNumber_Shift), Assembler::ScratchRegister);
-        Assembler::Jump isInt = _as->branch32(Assembler::Equal, Assembler::ScratchRegister, Assembler::TrustedImm32(1));
-        Assembler::Jump fallback = _as->branch32(Assembler::Equal, Assembler::ScratchRegister, Assembler::TrustedImm32(0));
+        // check if it's integer convertible
+        _as->urshift64(Assembler::TrustedImm32(QV4::Value::IsIntegerConvertible_Shift), Assembler::ScratchRegister);
+        Assembler::Jump isIntConvertible = _as->branch32(Assembler::Equal, Assembler::ScratchRegister, Assembler::TrustedImm32(3));
+
+        // nope, not integer convertible, so check for a double:
+        _as->urshift64(Assembler::TrustedImm32(
+                           QV4::Value::IsDoubleTag_Shift - QV4::Value::IsIntegerConvertible_Shift),
+                       Assembler::ScratchRegister);
+        Assembler::Jump fallback = _as->branch32(Assembler::GreaterThan, Assembler::ScratchRegister, Assembler::TrustedImm32(0));
 
         // it's a double
         _as->move(Assembler::TrustedImm64(QV4::Value::NaNEncodeMask), Assembler::ScratchRegister);
@@ -1212,7 +1217,7 @@ void InstructionSelection::convertTypeTo
         generateFunctionCall(Assembler::ReturnValueRegister, Runtime::toInt,
                              _as->loadAddress(Assembler::ScratchRegister, source));
 
-        isInt.link(_as);
+        isIntConvertible.link(_as);
         success.link(_as);
         IR::Temp *targetTemp = target->asTemp();
         if (!targetTemp || targetTemp->kind == IR::Temp::StackSlot) {
@@ -1784,9 +1789,9 @@ void InstructionSelection::visitCJumpStr
 {
     Q_ASSERT(binop->op == IR::OpStrictEqual || binop->op == IR::OpStrictNotEqual);
 
-    if (visitCJumpStrictNullUndefined(IR::NullType, binop, trueBlock, falseBlock))
+    if (visitCJumpStrictNull(binop, trueBlock, falseBlock))
         return;
-    if (visitCJumpStrictNullUndefined(IR::UndefinedType, binop, trueBlock, falseBlock))
+    if (visitCJumpStrictUndefined(binop, trueBlock, falseBlock))
         return;
     if (visitCJumpStrictBool(binop, trueBlock, falseBlock))
         return;
@@ -1802,16 +1807,14 @@ void InstructionSelection::visitCJumpStr
 }
 
 // Only load the non-null temp.
-bool InstructionSelection::visitCJumpStrictNullUndefined(IR::Type nullOrUndef, IR::Binop *binop,
-                                                         IR::BasicBlock *trueBlock,
-                                                         IR::BasicBlock *falseBlock)
+bool InstructionSelection::visitCJumpStrictNull(IR::Binop *binop,
+                                                IR::BasicBlock *trueBlock,
+                                                IR::BasicBlock *falseBlock)
 {
-    Q_ASSERT(nullOrUndef == IR::NullType || nullOrUndef == IR::UndefinedType);
-
     IR::Expr *varSrc = 0;
-    if (binop->left->type == IR::VarType && binop->right->type == nullOrUndef)
+    if (binop->left->type == IR::VarType && binop->right->type == IR::NullType)
         varSrc = binop->left;
-    else if (binop->left->type == nullOrUndef && binop->right->type == IR::VarType)
+    else if (binop->left->type == IR::NullType && binop->right->type == IR::VarType)
         varSrc = binop->right;
     if (!varSrc)
         return false;
@@ -1822,7 +1825,7 @@ bool InstructionSelection::visitCJumpStr
     }
 
     if (IR::Const *c = varSrc->asConst()) {
-        if (c->type == nullOrUndef)
+        if (c->type == IR::NullType)
             _as->jumpToBlock(_block, trueBlock);
         else
             _as->jumpToBlock(_block, falseBlock);
@@ -1835,9 +1838,54 @@ bool InstructionSelection::visitCJumpStr
     _as->load32(tagAddr, tagReg);
 
     Assembler::RelationalCondition cond = binop->op == IR::OpStrictEqual ? Assembler::Equal
-                                                                           : Assembler::NotEqual;
-    const Assembler::TrustedImm32 tag(nullOrUndef == IR::NullType ? int(QV4::Value::Null_Type_Internal)
-                                                                    : int(QV4::Value::Undefined_Type));
+                                                                         : Assembler::NotEqual;
+    const Assembler::TrustedImm32 tag(QV4::Value::Null_Type_Internal);
+    _as->generateCJumpOnCompare(cond, tagReg, tag, _block, trueBlock, falseBlock);
+    return true;
+}
+
+bool InstructionSelection::visitCJumpStrictUndefined(IR::Binop *binop,
+                                                     IR::BasicBlock *trueBlock,
+                                                     IR::BasicBlock *falseBlock)
+{
+    IR::Expr *varSrc = 0;
+    if (binop->left->type == IR::VarType && binop->right->type == IR::UndefinedType)
+        varSrc = binop->left;
+    else if (binop->left->type == IR::UndefinedType && binop->right->type == IR::VarType)
+        varSrc = binop->right;
+    if (!varSrc)
+        return false;
+
+    if (varSrc->asTemp() && varSrc->asTemp()->kind == IR::Temp::PhysicalRegister) {
+        _as->jumpToBlock(_block, falseBlock);
+        return true;
+    }
+
+    if (IR::Const *c = varSrc->asConst()) {
+        if (c->type == IR::UndefinedType)
+            _as->jumpToBlock(_block, trueBlock);
+        else
+            _as->jumpToBlock(_block, falseBlock);
+        return true;
+    }
+
+    Assembler::RelationalCondition cond = binop->op == IR::OpStrictEqual ? Assembler::Equal
+                                                                         : Assembler::NotEqual;
+    const Assembler::RegisterID tagReg = Assembler::ScratchRegister;
+#ifdef QV4_USE_64_BIT_VALUE_ENCODING
+    Assembler::Pointer addr = _as->loadAddress(Assembler::ScratchRegister, varSrc);
+    _as->load64(addr, tagReg);
+    const Assembler::TrustedImm64 tag(0);
+#else // !QV4_USE_64_BIT_VALUE_ENCODING
+    Assembler::Pointer tagAddr = _as->loadAddress(Assembler::ScratchRegister, varSrc);
+    _as->load32(tagAddr, tagReg);
+    Assembler::Jump j = _as->branch32(Assembler::invert(cond), tagReg, Assembler::TrustedImm32(0));
+    _as->addPatch(falseBlock, j);
+
+    tagAddr.offset += 4;
+    _as->load32(tagAddr, tagReg);
+    const Assembler::TrustedImm32 tag(QV4::Value::Managed_Type_Internal);
+#endif
     _as->generateCJumpOnCompare(cond, tagReg, tag, _block, trueBlock, falseBlock);
     return true;
 }
@@ -1928,10 +1976,14 @@ bool InstructionSelection::visitCJumpNul
     if (binop->op == IR::OpNotEqual)
         qSwap(trueBlock, falseBlock);
     Assembler::Jump isNull = _as->branch32(Assembler::Equal, tagReg, Assembler::TrustedImm32(int(QV4::Value::Null_Type_Internal)));
-    Assembler::Jump isUndefined = _as->branch32(Assembler::Equal, tagReg, Assembler::TrustedImm32(int(QV4::Value::Undefined_Type)));
+    Assembler::Jump isNotUndefinedTag = _as->branch32(Assembler::NotEqual, tagReg, Assembler::TrustedImm32(int(QV4::Value::Managed_Type_Internal)));
+    tagAddr.offset -= 4;
+    _as->load32(tagAddr, tagReg);
+    Assembler::Jump isNotUndefinedValue = _as->branch32(Assembler::NotEqual, tagReg, Assembler::TrustedImm32(0));
     _as->addPatch(trueBlock, isNull);
-    _as->addPatch(trueBlock, isUndefined);
-    _as->jumpToBlock(_block, falseBlock);
+    _as->addPatch(falseBlock, isNotUndefinedTag);
+    _as->addPatch(falseBlock, isNotUndefinedValue);
+    _as->jumpToBlock(_block, trueBlock);
 
     return true;
 }
Index: qtdeclarative-opensource-src-5.6.1/src/qml/jit/qv4isel_masm_p.h
===================================================================
--- qtdeclarative-opensource-src-5.6.1.orig/src/qml/jit/qv4isel_masm_p.h
+++ qtdeclarative-opensource-src-5.6.1/src/qml/jit/qv4isel_masm_p.h
@@ -166,8 +166,8 @@ protected:
     bool visitCJumpSInt32(IR::AluOp op, IR::Expr *left, IR::Expr *right,
                           IR::BasicBlock *iftrue, IR::BasicBlock *iffalse);
     void visitCJumpStrict(IR::Binop *binop, IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock);
-    bool visitCJumpStrictNullUndefined(IR::Type nullOrUndef, IR::Binop *binop,
-                                       IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock);
+    bool visitCJumpStrictNull(IR::Binop *binop, IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock);
+    bool visitCJumpStrictUndefined(IR::Binop *binop, IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock);
     bool visitCJumpStrictBool(IR::Binop *binop, IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock);
     bool visitCJumpNullUndefined(IR::Type nullOrUndef, IR::Binop *binop,
                                  IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock);
Index: qtdeclarative-opensource-src-5.6.1/src/qml/jsruntime/qv4scopedvalue_p.h
===================================================================
--- qtdeclarative-opensource-src-5.6.1.orig/src/qml/jsruntime/qv4scopedvalue_p.h
+++ qtdeclarative-opensource-src-5.6.1/src/qml/jsruntime/qv4scopedvalue_p.h
@@ -309,7 +309,7 @@ struct ScopedCallData {
     {
         int size = qMax(argc, (int)QV4::Global::ReservedArgumentCount) + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value);
         ptr = reinterpret_cast<CallData *>(scope.alloc(size));
-        ptr->tag = QV4::Value::Integer_Type;
+        ptr->tag = QV4::Value::Integer_Type_Internal;
         ptr->argc = argc;
     }
 
Index: qtdeclarative-opensource-src-5.6.1/src/qml/jsruntime/qv4value_p.h
===================================================================
--- qtdeclarative-opensource-src-5.6.1.orig/src/qml/jsruntime/qv4value_p.h
+++ qtdeclarative-opensource-src-5.6.1/src/qml/jsruntime/qv4value_p.h
@@ -70,23 +70,85 @@ private:
     /*
         We use two different ways of encoding JS values. One for 32bit and one for 64bit systems.
 
-        In both cases, 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. 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.
-
-        On 32bit, we store doubles as doubles. All other values, have the high 32bits set to a value that
-        will make the number a NaN. The Masks below are used for encoding the other types.
-
-        On 64 bit, we xor Doubles with (0xffff8000 << 32). That has the effect that no doubles will get encoded
-        with the 13 highest bits all 0. We are now using special values for bits 14-17 to encode our values. These
-        can be used, as the highest valid pointer on a 64 bit system is 2^48-1.
-
-        If they are all 0, we have a pointer to a Managed object. If bit 14 is set we have an integer.
-        This makes testing for pointers and numbers very fast (we have a number if any of the highest 14 bits is set).
-
-        Bit 15-17 is then used to encode other immediates.
+        In both cases, 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.)
+
+        On 32bit, we store doubles as doubles. All other values, have the high 32bits set to a value
+        that will make the number a NaN. The Masks below are used for encoding the other types.
+
+        On 64 bit, 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.
+
+        On both 32bit and 64bit, 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
+
+        64bit:
+
+        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 00000011 10000000 00000000 00000000 00000000 00000000 00000000 | Null
+        00000000 00000011 01000000 00000000 00000000 00000000 00000000 0000000x | Bool
+        00000000 00000011 00100000 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
+
+        32bit:
+
+        32109876 54321098 76543210 98765432 10987654 32109876 54321098 76543210 |
+        66665555 55555544 44444444 33333333 33222222 22221111 11111100 00000000 | JS Value
+        ------------------------------------------------------------------------+--------------
+        01111111 11111100 00000000 00000000 00000000 00000000 00000000 00000000 | Undefined
+        01111111 11111100 00000000 00000000 xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | Managed (heap pointer)
+        a1111111 1111bc00 00000000 00000000 00000000 00000000 00000000 00000000 | NaN/Inf
+        xddddddd ddddddxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | double
+        01111111 11111110 00000000 00000000 00000000 00000000 00000000 00000000 | empty (non-sparse array hole)
+        01111111 11111111 10000000 00000000 00000000 00000000 00000000 00000000 | Null
+        01111111 11111111 01000000 00000000 00000000 00000000 00000000 0000000x | Bool
+        01111111 11111111 00100000 00000000 xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | Int
+
+        Notes:
+        - the upper 32 bits are the tag, the lower 32 bits the value
+        - Undefined has a nullptr in the value, Managed has a non-nullptr stored in the value
+        - a: sign bit, always 0 for NaN
+        - b,c: 00=inf, 01 = sNaN, 10 = qNaN, 11 = boxed value
+        - d: stored double value, as long as not *all* of them are 1, because that's a boxed value
+          (see above)
+        - empty, Null, Bool, and Int have bit 63 set to 0, bits 62-50 set to 1 (same as undefined
+          and managed), and bit 49 set to 1 (where undefined and managed have it set to 0)
+        - Null, Bool, and Int have bit 48 set, indicating integer-convertible
     */
 
     quint64 _val;
@@ -137,7 +199,7 @@ public:
     {
         quint32 v;
         memcpy(&v, &b, 4);
-        setTagValue(Managed_Type, v);
+        setTagValue(Managed_Type_Internal, v);
     }
 #endif
 
@@ -153,113 +215,96 @@ public:
 
     Q_ALWAYS_INLINE void setEmpty()
     {
-        setTagValue(Empty_Type, value());
+        setTagValue(Empty_Type_Internal, value());
     }
 
     Q_ALWAYS_INLINE void setEmpty(int i)
     {
-        setTagValue(Empty_Type, quint32(i));
+        setTagValue(Empty_Type_Internal, quint32(i));
+    }
+
+    enum Type {
+        Undefined_Type,
+        Managed_Type,
+        Empty_Type,
+        Integer_Type,
+        Boolean_Type,
+        Null_Type,
+        Double_Type
+    };
+
+    inline Type type() const {
+        if (isUndefined()) return Undefined_Type;
+        if (isManaged()) return Managed_Type;
+        if (isEmpty()) return Empty_Type;
+        if (isInteger()) return Integer_Type;
+        if (isBoolean()) return Boolean_Type;
+        if (isNull()) return Null_Type;
+        Q_ASSERT(isDouble()); return Double_Type;
     }
 #ifndef QV4_USE_64_BIT_VALUE_ENCODING
     enum Masks {
         SilentNaNBit           =                  0x00040000,
         NaN_Mask               =                  0x7ff80000,
         NotDouble_Mask         =                  0x7ffa0000,
-        Type_Mask              =                  0xffffc000,
-        Immediate_Mask         = NotDouble_Mask | 0x00004000 | SilentNaNBit,
-        IsNullOrUndefined_Mask = Immediate_Mask |    0x08000,
+        Immediate_Mask         = NotDouble_Mask | 0x00020000u | SilentNaNBit,
         Tag_Shift = 32
     };
-    enum ValueType {
-        Undefined_Type = Immediate_Mask | 0x00000,
-        Null_Type      = Immediate_Mask | 0x10000,
-        Boolean_Type   = Immediate_Mask | 0x08000,
-        Integer_Type   = Immediate_Mask | 0x18000,
-        Managed_Type   = NotDouble_Mask | 0x00000 | SilentNaNBit,
-        Empty_Type     = NotDouble_Mask | 0x18000 | SilentNaNBit
-    };
-
-    enum ImmediateFlags {
-        ConvertibleToInt = Immediate_Mask | 0x1
-    };
-
-    enum ValueTypeInternal {
-        Null_Type_Internal = Null_Type | ConvertibleToInt,
-        Boolean_Type_Internal = Boolean_Type | ConvertibleToInt,
-        Integer_Type_Internal = Integer_Type | ConvertibleToInt,
 
+    enum {
+        Managed_Type_Internal  = NotDouble_Mask
     };
 #else
-    static const quint64 NaNEncodeMask = 0xffff800000000000ll;
-    static const quint64 IsInt32Mask  = 0x0002000000000000ll;
-    static const quint64 IsDoubleMask = 0xfffc000000000000ll;
-    static const quint64 IsNumberMask = IsInt32Mask|IsDoubleMask;
-    static const quint64 IsNullOrUndefinedMask = 0x0000800000000000ll;
-    static const quint64 IsNullOrBooleanMask = 0x0001000000000000ll;
-    static const quint64 IsConvertibleToIntMask = IsInt32Mask|IsNullOrBooleanMask;
+    static const quint64 NaNEncodeMask  = 0xfffc000000000000ll;
+    static const quint64 Immediate_Mask = 0x00020000u; // bit 49
 
     enum Masks {
         NaN_Mask = 0x7ff80000,
-        Type_Mask = 0xffff8000,
-        IsDouble_Mask = 0xfffc0000,
-        Immediate_Mask = 0x00018000,
-        IsNullOrUndefined_Mask = 0x00008000,
-        IsNullOrBoolean_Mask = 0x00010000,
-        Tag_Shift = 32
-    };
-    enum ValueType {
-        Undefined_Type = IsNullOrUndefined_Mask,
-        Null_Type = IsNullOrUndefined_Mask|IsNullOrBoolean_Mask,
-        Boolean_Type = IsNullOrBoolean_Mask,
-        Integer_Type = 0x20000|IsNullOrBoolean_Mask,
-        Managed_Type = 0,
-        Empty_Type = Undefined_Type | 0x4000
     };
     enum {
         IsDouble_Shift = 64-14,
-        IsNumber_Shift = 64-15,
-        IsConvertibleToInt_Shift = 64-16,
-        IsManaged_Shift = 64-17
+        IsManagedOrUndefined_Shift = 64-15,
+        IsIntegerConvertible_Shift = 64-16,
+        Tag_Shift = 32,
+        IsDoubleTag_Shift = IsDouble_Shift - Tag_Shift,
+        Managed_Type_Internal = 0
     };
-
-
+#endif
     enum ValueTypeInternal {
-        Null_Type_Internal = Null_Type,
-        Boolean_Type_Internal = Boolean_Type,
-        Integer_Type_Internal = Integer_Type
+        Empty_Type_Internal   = Immediate_Mask   | 0,
+        ConvertibleToInt      = Immediate_Mask   | 0x10000u, // bit 48
+        Null_Type_Internal    = ConvertibleToInt | 0x08000u,
+        Boolean_Type_Internal = ConvertibleToInt | 0x04000u,
+        Integer_Type_Internal = ConvertibleToInt | 0x02000u
     };
-#endif
-
-    inline unsigned type() const {
-        return tag() & Type_Mask;
-    }
 
     // used internally in property
-    inline bool isEmpty() const { return tag() == Empty_Type; }
-
-    inline bool isUndefined() const { return tag() == Undefined_Type; }
+    inline bool isEmpty() const { return tag() == Empty_Type_Internal; }
     inline bool isNull() const { return tag() == Null_Type_Internal; }
-    inline bool isBoolean() const { return tag ()== Boolean_Type_Internal; }
+    inline bool isBoolean() const { return tag() == Boolean_Type_Internal; }
+    inline bool isInteger() const { return tag() == Integer_Type_Internal; }
+    inline bool isNullOrUndefined() const { return isNull() || isUndefined(); }
+    inline bool isNumber() const { return isDouble() || isInteger(); }
+
 #ifdef QV4_USE_64_BIT_VALUE_ENCODING
-    inline bool isInteger() const { return (_val >> IsNumber_Shift) == 1; }
+    inline bool isUndefined() const { return _val == 0; }
     inline bool isDouble() const { return (_val >> IsDouble_Shift); }
-    inline bool isNumber() const { return (_val >> IsNumber_Shift); }
-    inline bool isManaged() const { return !(_val >> IsManaged_Shift); }
-    inline bool isNullOrUndefined() const { return ((_val >> IsManaged_Shift) & ~2) == 1; }
-    inline bool integerCompatible() const { return ((_val >> IsConvertibleToInt_Shift) & ~2) == 1; }
+    inline bool isManaged() const { return !isUndefined() && ((_val >> IsManagedOrUndefined_Shift) == 0); }
+
+    inline bool integerCompatible() const {
+        return (_val >> IsIntegerConvertible_Shift) == 3;
+    }
     static inline bool integerCompatible(Value a, Value b) {
         return a.integerCompatible() && b.integerCompatible();
     }
     static inline bool bothDouble(Value a, Value b) {
         return a.isDouble() && b.isDouble();
     }
-    inline bool isNaN() const { return (tag() & 0x7fff8000) == 0x00078000; }
+    inline bool isNaN() const { return (tag() & 0x7ffc0000  ) == 0x00040000; }
 #else
-    inline bool isInteger() const { return tag() == Integer_Type_Internal; }
+    inline bool isUndefined() const { return tag() == Managed_Type_Internal && value() == 0; }
     inline bool isDouble() const { return (tag() & NotDouble_Mask) != NotDouble_Mask; }
-    inline bool isNumber() const { return tag() == Integer_Type_Internal || (tag() & NotDouble_Mask) != NotDouble_Mask; }
-    inline bool isManaged() const { return tag() == Managed_Type; }
-    inline bool isNullOrUndefined() const { return (tag() & IsNullOrUndefined_Mask) == Undefined_Type; }
+    inline bool isManaged() const { return tag() == Managed_Type_Internal && !isUndefined(); }
     inline bool integerCompatible() const { return (tag() & ConvertibleToInt) == ConvertibleToInt; }
     static inline bool integerCompatible(Value a, Value b) {
         return ((a.tag() & b.tag()) & ConvertibleToInt) == ConvertibleToInt;
@@ -499,14 +544,14 @@ struct Q_QML_PRIVATE_EXPORT Primitive :
 inline Primitive Primitive::undefinedValue()
 {
     Primitive v;
-    v.setTagValue(Undefined_Type, 0);
+    v.setM(Q_NULLPTR);
     return v;
 }
 
 inline Primitive Primitive::emptyValue()
 {
     Primitive v;
-    v.setTagValue(Value::Empty_Type, 0);
+    v.setEmpty(0);
     return v;
 }
 
@@ -534,7 +579,6 @@ inline Primitive Primitive::fromDouble(d
 inline Primitive Primitive::fromInt32(int i)
 {
     Primitive v;
-    v.setTagValue(Integer_Type_Internal, 0);
     v.setInt_32(i);
     return v;
 }
@@ -552,31 +596,23 @@ inline Primitive Primitive::fromUInt32(u
 
 struct Encode {
     static ReturnedValue undefined() {
-        return quint64(Value::Undefined_Type) << Value::Tag_Shift;
+        return Primitive::undefinedValue().rawValue();
     }
     static ReturnedValue null() {
-        return quint64(Value::Null_Type_Internal) << Value::Tag_Shift;
+        return Primitive::nullValue().rawValue();
     }
 
     Encode(bool b) {
-        val = (quint64(Value::Boolean_Type_Internal) << Value::Tag_Shift) | (uint)b;
+        val = Primitive::fromBoolean(b).rawValue();
     }
     Encode(double d) {
-        Value v;
-        v.setDouble(d);
-        val = v.rawValue();
+        val = Primitive::fromDouble(d).rawValue();
     }
     Encode(int i) {
-        val = (quint64(Value::Integer_Type_Internal) << Value::Tag_Shift) | (uint)i;
+        val = Primitive::fromInt32(i).rawValue();
     }
     Encode(uint i) {
-        if (i <= INT_MAX) {
-            val = (quint64(Value::Integer_Type_Internal) << Value::Tag_Shift) | i;
-        } else {
-            Value v;
-            v.setDouble(i);
-            val = v.rawValue();
-        }
+        val = Primitive::fromUInt32(i).rawValue();
     }
     Encode(ReturnedValue v) {
         val = v;
Index: qtdeclarative-opensource-src-5.6.1/src/qml/jsruntime/qv4vme_moth.cpp
===================================================================
--- qtdeclarative-opensource-src-5.6.1.orig/src/qml/jsruntime/qv4vme_moth.cpp
+++ qtdeclarative-opensource-src-5.6.1/src/qml/jsruntime/qv4vme_moth.cpp
@@ -563,7 +563,7 @@ QV4::ReturnedValue VME::run(ExecutionEng
 #endif // DO_TRACE_INSTR
         Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize);
         QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData);
-        callData->tag = QV4::Value::Integer_Type;
+        callData->tag = QV4::Value::Integer_Type_Internal;
         callData->argc = instr.argc;
         callData->thisObject = QV4::Primitive::undefinedValue();
         STOREVALUE(instr.result, Runtime::callValue(engine, VALUE(instr.dest), callData));
@@ -573,7 +573,7 @@ QV4::ReturnedValue VME::run(ExecutionEng
         TRACE(property name, "%s, args=%u, argc=%u, this=%s", qPrintable(runtimeStrings[instr.name]->toQString()), instr.callData, instr.argc, (VALUE(instr.base)).toString(context)->toQString().toUtf8().constData());
         Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize);
         QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData);
-        callData->tag = QV4::Value::Integer_Type;
+        callData->tag = QV4::Value::Integer_Type_Internal;
         callData->argc = instr.argc;
         callData->thisObject = VALUE(instr.base);
         STOREVALUE(instr.result, Runtime::callProperty(engine, instr.name, callData));
@@ -582,7 +582,7 @@ QV4::ReturnedValue VME::run(ExecutionEng
     MOTH_BEGIN_INSTR(CallPropertyLookup)
         Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize);
         QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData);
-        callData->tag = QV4::Value::Integer_Type;
+        callData->tag = QV4::Value::Integer_Type_Internal;
         callData->argc = instr.argc;
         callData->thisObject = VALUE(instr.base);
         STOREVALUE(instr.result, Runtime::callPropertyLookup(engine, instr.lookupIndex, callData));
@@ -592,7 +592,7 @@ QV4::ReturnedValue VME::run(ExecutionEng
         TRACE(property name, "%s, args=%u, argc=%u, this=%s", qPrintable(runtimeStrings[instr.name]->toQString()), instr.callData, instr.argc, (VALUE(instr.base)).toString(context)->toQString().toUtf8().constData());
         Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize);
         QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData);
-        callData->tag = QV4::Value::Integer_Type;
+        callData->tag = QV4::Value::Integer_Type_Internal;
         callData->argc = instr.argc;
         callData->thisObject = VALUE(instr.base);
         STOREVALUE(instr.result, Runtime::callQmlScopeObjectProperty(engine, instr.index, callData));
@@ -602,7 +602,7 @@ QV4::ReturnedValue VME::run(ExecutionEng
         TRACE(property name, "%s, args=%u, argc=%u, this=%s", qPrintable(runtimeStrings[instr.name]->toQString()), instr.callData, instr.argc, (VALUE(instr.base)).toString(context)->toQString().toUtf8().constData());
         Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize);
         QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData);
-        callData->tag = QV4::Value::Integer_Type;
+        callData->tag = QV4::Value::Integer_Type_Internal;
         callData->argc = instr.argc;
         callData->thisObject = VALUE(instr.base);
         STOREVALUE(instr.result, Runtime::callQmlContextObjectProperty(engine, instr.index, callData));
@@ -611,7 +611,7 @@ QV4::ReturnedValue VME::run(ExecutionEng
     MOTH_BEGIN_INSTR(CallElement)
         Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize);
         QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData);
-        callData->tag = QV4::Value::Integer_Type;
+        callData->tag = QV4::Value::Integer_Type_Internal;
         callData->argc = instr.argc;
         callData->thisObject = VALUE(instr.base);
         STOREVALUE(instr.result, Runtime::callElement(engine, VALUE(instr.index), callData));
@@ -620,7 +620,7 @@ QV4::ReturnedValue VME::run(ExecutionEng
     MOTH_BEGIN_INSTR(CallActivationProperty)
         Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize);
         QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData);
-        callData->tag = QV4::Value::Integer_Type;
+        callData->tag = QV4::Value::Integer_Type_Internal;
         callData->argc = instr.argc;
         callData->thisObject = QV4::Primitive::undefinedValue();
         STOREVALUE(instr.result, Runtime::callActivationProperty(engine, instr.name, callData));
@@ -629,7 +629,7 @@ QV4::ReturnedValue VME::run(ExecutionEng
     MOTH_BEGIN_INSTR(CallGlobalLookup)
         Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize);
         QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData);
-        callData->tag = QV4::Value::Integer_Type;
+        callData->tag = QV4::Value::Integer_Type_Internal;
         callData->argc = instr.argc;
         callData->thisObject = QV4::Primitive::undefinedValue();
         STOREVALUE(instr.result, Runtime::callGlobalLookup(engine, instr.index, callData));
@@ -735,7 +735,7 @@ QV4::ReturnedValue VME::run(ExecutionEng
     MOTH_BEGIN_INSTR(CreateValue)
         Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize);
         QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData);
-        callData->tag = QV4::Value::Integer_Type;
+        callData->tag = QV4::Value::Integer_Type_Internal;
         callData->argc = instr.argc;
         callData->thisObject = QV4::Primitive::undefinedValue();
         STOREVALUE(instr.result, Runtime::constructValue(engine, VALUE(instr.func), callData));
@@ -744,7 +744,7 @@ QV4::ReturnedValue VME::run(ExecutionEng
     MOTH_BEGIN_INSTR(CreateProperty)
         Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize);
         QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData);
-        callData->tag = QV4::Value::Integer_Type;
+        callData->tag = QV4::Value::Integer_Type_Internal;
         callData->argc = instr.argc;
         callData->thisObject = VALUE(instr.base);
         STOREVALUE(instr.result, Runtime::constructProperty(engine, instr.name, callData));
@@ -753,7 +753,7 @@ QV4::ReturnedValue VME::run(ExecutionEng
     MOTH_BEGIN_INSTR(ConstructPropertyLookup)
         Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize);
         QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData);
-        callData->tag = QV4::Value::Integer_Type;
+        callData->tag = QV4::Value::Integer_Type_Internal;
         callData->argc = instr.argc;
         callData->thisObject = VALUE(instr.base);
         STOREVALUE(instr.result, Runtime::constructPropertyLookup(engine, instr.index, callData));
@@ -762,7 +762,7 @@ QV4::ReturnedValue VME::run(ExecutionEng
     MOTH_BEGIN_INSTR(CreateActivationProperty)
         Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize);
         QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData);
-        callData->tag = QV4::Value::Integer_Type;
+        callData->tag = QV4::Value::Integer_Type_Internal;
         callData->argc = instr.argc;
         callData->thisObject = QV4::Primitive::undefinedValue();
         STOREVALUE(instr.result, Runtime::constructActivationProperty(engine, instr.name, callData));
@@ -771,7 +771,7 @@ QV4::ReturnedValue VME::run(ExecutionEng
     MOTH_BEGIN_INSTR(ConstructGlobalLookup)
         Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize);
         QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData);
-        callData->tag = QV4::Value::Integer_Type;
+        callData->tag = QV4::Value::Integer_Type_Internal;
         callData->argc = instr.argc;
         callData->thisObject = QV4::Primitive::undefinedValue();
         STOREVALUE(instr.result, Runtime::constructGlobalLookup(engine, instr.index, callData));
openSUSE Build Service is sponsored by