1 #include "duk_internal.h"
3 #if defined(DUK_USE_FASTINT)
6 * Manually optimized double-to-fastint downgrade check.
8 * This check has a large impact on performance, especially for fastint
9 * slow paths, so must be changed carefully. The code should probably be
10 * optimized for the case where the result does not fit into a fastint,
11 * to minimize the penalty for "slow path code" dealing with fractions etc.
13 * At least on one tested soft float ARM platform double-to-int64 coercion
14 * is very slow (and sometimes produces incorrect results, see self tests).
15 * This algorithm combines a fastint compatibility check and extracting the
16 * integer value from an IEEE double for setting the tagged fastint. For
17 * other platforms a more naive approach might be better.
19 * See doc/fastint.rst for details.
22 DUK_INTERNAL DUK_ALWAYS_INLINE
void duk_tval_set_number_chkfast(duk_tval
*tv
, duk_double_t x
) {
26 duk_small_int_t shift
;
28 /* XXX: optimize for packed duk_tval directly? */
31 i
= (duk_int64_t
) DUK_DBLUNION_GET_INT64(&du
);
32 expt
= (duk_small_int_t
) ((i
>> 52) & 0x07ff);
35 if (shift
>= 0 && shift
<= 46) { /* exponents 1023 to 1069 */
38 if (((0x000fffffffffffffLL
>> shift
) & i
) == 0) {
39 t
= i
| 0x0010000000000000LL
; /* implicit leading one */
40 t
= t
& 0x001fffffffffffffLL
;
41 t
= t
>> (52 - shift
);
45 DUK_TVAL_SET_FASTINT(tv
, t
);
48 } else if (shift
== -1023) { /* exponent 0 */
49 if (i
>= 0 && (i
& 0x000fffffffffffffLL
) == 0) {
50 /* Note: reject negative zero. */
51 DUK_TVAL_SET_FASTINT(tv
, (duk_int64_t
) 0);
54 } else if (shift
== 47) { /* exponent 1070 */
55 if (i
< 0 && (i
& 0x000fffffffffffffLL
) == 0) {
56 DUK_TVAL_SET_FASTINT(tv
, (duk_int64_t
) DUK_FASTINT_MIN
);
61 DUK_TVAL_SET_DOUBLE(tv
, x
);
66 * Manually optimized number-to-double conversion
69 #if defined(DUK_USE_FASTINT) && defined(DUK_USE_PACKED_TVAL)
70 DUK_INTERNAL DUK_ALWAYS_INLINE duk_double_t
duk_tval_get_number_packed(duk_tval
*tv
) {
74 t
= (duk_uint64_t
) DUK_DBLUNION_GET_UINT64(tv
);
75 if ((t
>> 48) != DUK_TAG_FASTINT
) {
77 } else if (t
& 0x0000800000000000ULL
) {
78 t
= (duk_uint64_t
) (-((duk_int64_t
) t
)); /* avoid unary minus on unsigned */
79 t
= t
& 0x0000ffffffffffffULL
; /* negative */
80 t
|= 0xc330000000000000ULL
;
81 DUK_DBLUNION_SET_UINT64(&du
, t
);
82 return du
.d
+ 4503599627370496.0; /* 1 << 52 */
84 t
&= 0x0000ffffffffffffULL
; /* positive */
85 t
|= 0x4330000000000000ULL
;
86 DUK_DBLUNION_SET_UINT64(&du
, t
);
87 return du
.d
- 4503599627370496.0; /* 1 << 52 */
89 return 0.0; /* zero */
92 #endif /* DUK_USE_FASTINT && DUK_USE_PACKED_TVAL */
95 #if defined(DUK_USE_FASTINT) && !defined(DUK_USE_PACKED_TVAL)
96 DUK_INTERNAL DUK_ALWAYS_INLINE duk_double_t
duk_tval_get_number_unpacked(duk_tval
*tv
) {
100 DUK_ASSERT(tv
->t
== DUK__TAG_NUMBER
|| tv
->t
== DUK_TAG_FASTINT
);
102 if (tv
->t
== DUK_TAG_FASTINT
) {
104 t
= 0x4330000000000000ULL
| (duk_uint64_t
) tv
->v
.fi
;
105 DUK_DBLUNION_SET_UINT64(&du
, t
);
106 return du
.d
- 4503599627370496.0; /* 1 << 52 */
108 t
= 0xc330000000000000ULL
| (duk_uint64_t
) (-tv
->v
.fi
);
109 DUK_DBLUNION_SET_UINT64(&du
, t
);
110 return du
.d
+ 4503599627370496.0; /* 1 << 52 */
116 #endif /* DUK_USE_FASTINT && DUK_USE_PACKED_TVAL */
119 #if defined(DUK_USE_FASTINT) && !defined(DUK_USE_PACKED_TVAL)
120 DUK_INTERNAL DUK_ALWAYS_INLINE duk_double_t
duk_tval_get_number_unpacked_fastint(duk_tval
*tv
) {
124 DUK_ASSERT(tv
->t
== DUK_TAG_FASTINT
);
127 t
= 0x4330000000000000ULL
| (duk_uint64_t
) tv
->v
.fi
;
128 DUK_DBLUNION_SET_UINT64(&du
, t
);
129 return du
.d
- 4503599627370496.0; /* 1 << 52 */
131 t
= 0xc330000000000000ULL
| (duk_uint64_t
) (-tv
->v
.fi
);
132 DUK_DBLUNION_SET_UINT64(&du
, t
);
133 return du
.d
+ 4503599627370496.0; /* 1 << 52 */
136 #endif /* DUK_USE_FASTINT && DUK_USE_PACKED_TVAL */
138 #endif /* DUK_USE_FASTINT */