#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_fast(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 (((DUK_I64_CONSTANT(0x000fffffffffffff) >> shift) & i) == 0) { t = i | DUK_I64_CONSTANT(0x0010000000000000); /* implicit leading one */ t = t & DUK_I64_CONSTANT(0x001fffffffffffff); 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 & DUK_I64_CONSTANT(0x000fffffffffffff)) == 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 & DUK_I64_CONSTANT(0x000fffffffffffff)) == 0) { DUK_TVAL_SET_FASTINT(tv, (duk_int64_t) DUK_FASTINT_MIN); return; } } DUK_TVAL_SET_DOUBLE(tv, x); return; } DUK_INTERNAL DUK_NOINLINE void duk_tval_set_number_chkfast_slow(duk_tval *tv, duk_double_t x) { duk_tval_set_number_chkfast_fast(tv, x); } /* * 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 & DUK_U64_CONSTANT(0x0000800000000000)) { t = (duk_uint64_t) (-((duk_int64_t) t)); /* avoid unary minus on unsigned */ t = t & DUK_U64_CONSTANT(0x0000ffffffffffff); /* negative */ t |= DUK_U64_CONSTANT(0xc330000000000000); DUK_DBLUNION_SET_UINT64(&du, t); return du.d + 4503599627370496.0; /* 1 << 52 */ } else if (t != 0) { t &= DUK_U64_CONSTANT(0x0000ffffffffffff); /* positive */ t |= DUK_U64_CONSTANT(0x4330000000000000); 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 = DUK_U64_CONSTANT(0x4330000000000000) | (duk_uint64_t) tv->v.fi; DUK_DBLUNION_SET_UINT64(&du, t); return du.d - 4503599627370496.0; /* 1 << 52 */ } else { t = DUK_U64_CONSTANT(0xc330000000000000) | (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 = DUK_U64_CONSTANT(0x4330000000000000) | (duk_uint64_t) tv->v.fi; DUK_DBLUNION_SET_UINT64(&du, t); return du.d - 4503599627370496.0; /* 1 << 52 */ } else { t = DUK_U64_CONSTANT(0xc330000000000000) | (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 */ /* * Assertion helpers. */ #if defined(DUK_USE_ASSERTIONS) DUK_INTERNAL void duk_tval_assert_valid(duk_tval *tv) { DUK_ASSERT(tv != NULL); } #endif