| #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 */ |