aboutsummarylogtreecommitdiffstats
path: root/0001-V4-Free-up-2-address-bits-in-64bit-mode.patch
blob: 477d44332fb242eb77ed19f97ae55d62f8179c1c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
From 681ceebc9625294243613e8737c5c2aac485462f Mon Sep 17 00:00:00 2001
From: Erik Verbruggen <erik.verbruggen@digia.com>
Date: Thu, 1 Sep 2016 15:34:57 +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/jit/qv4assembler.cpp   |  38 +++++++--
 src/qml/jit/qv4assembler_p.h   |   5 ++
 src/qml/jit/qv4isel_masm.cpp   |  85 ++++++++++++++-----
 src/qml/jit/qv4isel_masm_p.h   |   4 +-
 src/qml/jsruntime/qv4value_p.h | 186 ++++++++++++++++++-----------------------
 5 files changed, 186 insertions(+), 132 deletions(-)

diff --git a/src/qml/jit/qv4assembler.cpp b/src/qml/jit/qv4assembler.cpp
index 929726f..b7dbc81 100644
--- a/src/qml/jit/qv4assembler.cpp
+++ b/src/qml/jit/qv4assembler.cpp
@@ -133,8 +133,30 @@ void Assembler::generateCJumpOnNonZero(RegisterID reg, IR::BasicBlock *currentBl
     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(RelationalCondition cond, RegisterID left
     }
 }
 
-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::genTryDoubleConversion(IR::Expr *src, Assembler::FPRe
     // 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,
diff --git a/src/qml/jit/qv4assembler_p.h b/src/qml/jit/qv4assembler_p.h
index 532a311..ba9ca66 100644
--- a/src/qml/jit/qv4assembler_p.h
+++ b/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);
diff --git a/src/qml/jit/qv4isel_masm.cpp b/src/qml/jit/qv4isel_masm.cpp
index b6df5fb..cfd0862 100644
--- a/src/qml/jit/qv4isel_masm.cpp
+++ b/src/qml/jit/qv4isel_masm.cpp
@@ -703,7 +703,7 @@ void InstructionSelection::loadString(const QString &str, IR::Expr *target)
 #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::convertTypeToDouble(IR::Expr *source, IR::Expr *targe
         // 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,12 @@ void InstructionSelection::convertTypeToSInt32(IR::Expr *source, IR::Expr *targe
         _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::IsManagedOrUndefined_Shift), Assembler::ScratchRegister);
+        Assembler::Jump isIntConvertible = _as->branch32(Assembler::Equal, Assembler::ScratchRegister, Assembler::TrustedImm32(3));
+
+        // nope, not integer convertible, so check for a double:
+        Assembler::Jump fallback = _as->branch32(Assembler::LessThan, Assembler::ScratchRegister, Assembler::TrustedImm32(3));
 
         // it's a double
         _as->move(Assembler::TrustedImm64(QV4::Value::NaNEncodeMask), Assembler::ScratchRegister);
@@ -1212,7 +1214,7 @@ void InstructionSelection::convertTypeToSInt32(IR::Expr *source, IR::Expr *targe
         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 +1786,9 @@ void InstructionSelection::visitCJumpStrict(IR::Binop *binop, IR::BasicBlock *tr
 {
     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 +1804,14 @@ void InstructionSelection::visitCJumpStrict(IR::Binop *binop, IR::BasicBlock *tr
 }
 
 // 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 +1822,7 @@ bool InstructionSelection::visitCJumpStrictNullUndefined(IR::Type nullOrUndef, I
     }
 
     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 +1835,54 @@ bool InstructionSelection::visitCJumpStrictNullUndefined(IR::Type nullOrUndef, I
     _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;
 }
diff --git a/src/qml/jit/qv4isel_masm_p.h b/src/qml/jit/qv4isel_masm_p.h
index 6e9b02b..f6d9364 100644
--- a/src/qml/jit/qv4isel_masm_p.h
+++ b/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);
diff --git a/src/qml/jsruntime/qv4value_p.h b/src/qml/jsruntime/qv4value_p.h
index 5abf5ad..942b6ee 100644
--- a/src/qml/jsruntime/qv4value_p.h
+++ b/src/qml/jsruntime/qv4value_p.h
@@ -70,23 +70,27 @@ 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.
     */
 
     quint64 _val;
@@ -137,7 +141,7 @@ public:
     {
         quint32 v;
         memcpy(&v, &b, 4);
-        setTagValue(Managed_Type, v);
+        setTagValue(Managed_Type_Internal, v);
     }
 #endif
 
@@ -153,12 +157,32 @@ 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
@@ -166,101 +190,65 @@ public:
         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 48
 
     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-16,
+        Tag_Shift = 32,
+        IsDoubleTag_Shift = IsDouble_Shift - Tag_Shift
     };
-
-
-    enum ValueTypeInternal {
-        Null_Type_Internal = Null_Type,
-        Boolean_Type_Internal = Boolean_Type,
-        Integer_Type_Internal = Integer_Type
+    enum {
+        Managed_Type_Internal  = 0
     };
 #endif
+    enum ValueTypeInternal {
+        Empty_Type_Internal   = Immediate_Mask   | 0,
+        ConvertibleToInt      = Immediate_Mask   | 0x10000u, // bit 47
+        Null_Type_Internal    = ConvertibleToInt | 0x08000u,
+        Boolean_Type_Internal = ConvertibleToInt | 0x04000u,
+        Integer_Type_Internal = ConvertibleToInt | 0x02000u,
+    };
 
-    inline unsigned type() const {
-        return tag() & Type_Mask;
-    }
+    inline bool isUndefined() const { return tag() == Managed_Type_Internal && value() == 0; }
 
     // 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 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 (tag() >> (IsManagedOrUndefined_Shift - Tag_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 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;
@@ -500,14 +488,14 @@ struct Q_QML_PRIVATE_EXPORT Primitive : public Value
 inline Primitive Primitive::undefinedValue()
 {
     Primitive v;
-    v.setTagValue(Undefined_Type, 0);
+    v.setM(nullptr);
     return v;
 }
 
 inline Primitive Primitive::emptyValue()
 {
     Primitive v;
-    v.setTagValue(Value::Empty_Type, 0);
+    v.setEmpty(0);
     return v;
 }
 
@@ -553,31 +541,23 @@ inline Primitive Primitive::fromUInt32(uint i)
 
 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;
-- 
2.9.3