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
|
#include "duk_internal.h"
#if defined(DUK_USE_FASTINT)
/*
* Manually optimized double-to-fastint downgrade check.
*
* This check has a large impact on performance, especially for fastint
* slow paths, so must be changed carefully. The code should probably be
* optimized for the case where the result does not fit into a fastint,
* to minimize the penalty for "slow path code" dealing with fractions etc.
*
* At least on one tested soft float ARM platform double-to-int64 coercion
* is very slow (and sometimes produces incorrect results, see self tests).
* This algorithm combines a fastint compatibility check and extracting the
* integer value from an IEEE double for setting the tagged fastint. For
* other platforms a more naive approach might be better.
*
* See doc/fastint.rst for details.
*/
DUK_INTERNAL DUK_ALWAYS_INLINE void duk_tval_set_number_chkfast(duk_tval *tv, duk_double_t x) {
duk_double_union du;
duk_int64_t i;
duk_small_int_t expt;
duk_small_int_t shift;
/* XXX: optimize for packed duk_tval directly? */
du.d = x;
i = (duk_int64_t) DUK_DBLUNION_GET_INT64(&du);
expt = (duk_small_int_t) ((i >> 52) & 0x07ff);
shift = expt - 1023;
if (shift >= 0 && shift <= 46) { /* exponents 1023 to 1069 */
duk_int64_t t;
if (((0x000fffffffffffffLL >> shift) & i) == 0) {
t = i | 0x0010000000000000LL; /* implicit leading one */
t = t & 0x001fffffffffffffLL;
t = t >> (52 - shift);
if (i < 0) {
t = -t;
}
DUK_TVAL_SET_FASTINT(tv, t);
return;
}
} else if (shift == -1023) { /* exponent 0 */
if (i >= 0 && (i & 0x000fffffffffffffLL) == 0) {
/* Note: reject negative zero. */
DUK_TVAL_SET_FASTINT(tv, (duk_int64_t) 0);
return;
}
} else if (shift == 47) { /* exponent 1070 */
if (i < 0 && (i & 0x000fffffffffffffLL) == 0) {
DUK_TVAL_SET_FASTINT(tv, (duk_int64_t) DUK_FASTINT_MIN);
return;
}
}
DUK_TVAL_SET_DOUBLE(tv, x);
return;
}
/*
* Manually optimized number-to-double conversion
*/
#if defined(DUK_USE_FASTINT) && defined(DUK_USE_PACKED_TVAL)
DUK_INTERNAL DUK_ALWAYS_INLINE duk_double_t duk_tval_get_number_packed(duk_tval *tv) {
duk_double_union du;
duk_uint64_t t;
t = (duk_uint64_t) DUK_DBLUNION_GET_UINT64(tv);
if ((t >> 48) != DUK_TAG_FASTINT) {
return tv->d;
} else if (t & 0x0000800000000000ULL) {
t = (duk_uint64_t) (-((duk_int64_t) t)); /* avoid unary minus on unsigned */
t = t & 0x0000ffffffffffffULL; /* negative */
t |= 0xc330000000000000ULL;
DUK_DBLUNION_SET_UINT64(&du, t);
return du.d + 4503599627370496.0; /* 1 << 52 */
} else if (t != 0) {
t &= 0x0000ffffffffffffULL; /* positive */
t |= 0x4330000000000000ULL;
DUK_DBLUNION_SET_UINT64(&du, t);
return du.d - 4503599627370496.0; /* 1 << 52 */
} else {
return 0.0; /* zero */
}
}
#endif /* DUK_USE_FASTINT && DUK_USE_PACKED_TVAL */
#if 0 /* unused */
#if defined(DUK_USE_FASTINT) && !defined(DUK_USE_PACKED_TVAL)
DUK_INTERNAL DUK_ALWAYS_INLINE duk_double_t duk_tval_get_number_unpacked(duk_tval *tv) {
duk_double_union du;
duk_uint64_t t;
DUK_ASSERT(tv->t == DUK__TAG_NUMBER || tv->t == DUK_TAG_FASTINT);
if (tv->t == DUK_TAG_FASTINT) {
if (tv->v.fi >= 0) {
t = 0x4330000000000000ULL | (duk_uint64_t) tv->v.fi;
DUK_DBLUNION_SET_UINT64(&du, t);
return du.d - 4503599627370496.0; /* 1 << 52 */
} else {
t = 0xc330000000000000ULL | (duk_uint64_t) (-tv->v.fi);
DUK_DBLUNION_SET_UINT64(&du, t);
return du.d + 4503599627370496.0; /* 1 << 52 */
}
} else {
return tv->v.d;
}
}
#endif /* DUK_USE_FASTINT && DUK_USE_PACKED_TVAL */
#endif /* 0 */
#if defined(DUK_USE_FASTINT) && !defined(DUK_USE_PACKED_TVAL)
DUK_INTERNAL DUK_ALWAYS_INLINE duk_double_t duk_tval_get_number_unpacked_fastint(duk_tval *tv) {
duk_double_union du;
duk_uint64_t t;
DUK_ASSERT(tv->t == DUK_TAG_FASTINT);
if (tv->v.fi >= 0) {
t = 0x4330000000000000ULL | (duk_uint64_t) tv->v.fi;
DUK_DBLUNION_SET_UINT64(&du, t);
return du.d - 4503599627370496.0; /* 1 << 52 */
} else {
t = 0xc330000000000000ULL | (duk_uint64_t) (-tv->v.fi);
DUK_DBLUNION_SET_UINT64(&du, t);
return du.d + 4503599627370496.0; /* 1 << 52 */
}
}
#endif /* DUK_USE_FASTINT && DUK_USE_PACKED_TVAL */
#endif /* DUK_USE_FASTINT */
|