1 #include "duk_internal.h"
2 
3 #if defined(DUK_USE_FASTINT)
4 
5 /*
6  *  Manually optimized double-to-fastint downgrade check.
7  *
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.
12  *
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.
18  *
19  *  See doc/fastint.rst for details.
20  */
21 
duk_tval_set_number_chkfast(duk_tval * tv,duk_double_t x)22 DUK_INTERNAL DUK_ALWAYS_INLINE void duk_tval_set_number_chkfast(duk_tval *tv, duk_double_t x) {
23 	duk_double_union du;
24 	duk_int64_t i;
25 	duk_small_int_t expt;
26 	duk_small_int_t shift;
27 
28 	/* XXX: optimize for packed duk_tval directly? */
29 
30 	du.d = x;
31 	i = (duk_int64_t) DUK_DBLUNION_GET_INT64(&du);
32 	expt = (duk_small_int_t) ((i >> 52) & 0x07ff);
33 	shift = expt - 1023;
34 
35 	if (shift >= 0 && shift <= 46) {  /* exponents 1023 to 1069 */
36 		duk_int64_t t;
37 
38 		if (((0x000fffffffffffffLL >> shift) & i) == 0) {
39 			t = i | 0x0010000000000000LL;  /* implicit leading one */
40 			t = t & 0x001fffffffffffffLL;
41 			t = t >> (52 - shift);
42 			if (i < 0) {
43 				t = -t;
44 			}
45 			DUK_TVAL_SET_FASTINT(tv, t);
46 			return;
47 		}
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);
52 			return;
53 		}
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);
57 			return;
58 		}
59 	}
60 
61 	DUK_TVAL_SET_DOUBLE(tv, x);
62 	return;
63 }
64 
65 /*
66  *  Manually optimized number-to-double conversion
67  */
68 
69 #if defined(DUK_USE_FASTINT) && defined(DUK_USE_PACKED_TVAL)
duk_tval_get_number_packed(duk_tval * tv)70 DUK_INTERNAL DUK_ALWAYS_INLINE duk_double_t duk_tval_get_number_packed(duk_tval *tv) {
71 	duk_double_union du;
72 	duk_uint64_t t;
73 
74 	t = (duk_uint64_t) DUK_DBLUNION_GET_UINT64(tv);
75 	if ((t >> 48) != DUK_TAG_FASTINT) {
76 		return tv->d;
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 */
83 	} else if (t != 0) {
84 		t &= 0x0000ffffffffffffULL;  /* positive */
85 		t |= 0x4330000000000000ULL;
86 		DUK_DBLUNION_SET_UINT64(&du, t);
87 		return du.d - 4503599627370496.0;  /* 1 << 52 */
88 	} else {
89 		return 0.0;  /* zero */
90 	}
91 }
92 #endif  /* DUK_USE_FASTINT && DUK_USE_PACKED_TVAL */
93 
94 #if 0  /* unused */
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) {
97 	duk_double_union du;
98 	duk_uint64_t t;
99 
100 	DUK_ASSERT(tv->t == DUK__TAG_NUMBER || tv->t == DUK_TAG_FASTINT);
101 
102 	if (tv->t == DUK_TAG_FASTINT) {
103 		if (tv->v.fi >= 0) {
104 			t = 0x4330000000000000ULL | (duk_uint64_t) tv->v.fi;
105 			DUK_DBLUNION_SET_UINT64(&du, t);
106 			return du.d - 4503599627370496.0;  /* 1 << 52 */
107 		} else {
108 			t = 0xc330000000000000ULL | (duk_uint64_t) (-tv->v.fi);
109 			DUK_DBLUNION_SET_UINT64(&du, t);
110 			return du.d + 4503599627370496.0;  /* 1 << 52 */
111 		}
112 	} else {
113 		return tv->v.d;
114 	}
115 }
116 #endif  /* DUK_USE_FASTINT && DUK_USE_PACKED_TVAL */
117 #endif  /* 0 */
118 
119 #if defined(DUK_USE_FASTINT) && !defined(DUK_USE_PACKED_TVAL)
duk_tval_get_number_unpacked_fastint(duk_tval * tv)120 DUK_INTERNAL DUK_ALWAYS_INLINE duk_double_t duk_tval_get_number_unpacked_fastint(duk_tval *tv) {
121 	duk_double_union du;
122 	duk_uint64_t t;
123 
124 	DUK_ASSERT(tv->t == DUK_TAG_FASTINT);
125 
126 	if (tv->v.fi >= 0) {
127 		t = 0x4330000000000000ULL | (duk_uint64_t) tv->v.fi;
128 		DUK_DBLUNION_SET_UINT64(&du, t);
129 		return du.d - 4503599627370496.0;  /* 1 << 52 */
130 	} else {
131 		t = 0xc330000000000000ULL | (duk_uint64_t) (-tv->v.fi);
132 		DUK_DBLUNION_SET_UINT64(&du, t);
133 		return du.d + 4503599627370496.0;  /* 1 << 52 */
134 	}
135 }
136 #endif  /* DUK_USE_FASTINT && DUK_USE_PACKED_TVAL */
137 
138 #endif  /* DUK_USE_FASTINT */
139