Browse Source

Merge pull request #1051 from svaarala/smaller-signbit-checks

Small IEEE double handling improvements
pull/1058/head
Sami Vaarala 8 years ago
committed by GitHub
parent
commit
61d9d3df01
  1. 5
      RELEASES.rst
  2. 2
      src-input/duk_api_stack.c
  3. 14
      src-input/duk_bi_math.c
  4. 31
      src-input/duk_bi_string.c
  5. 29
      src-input/duk_dblunion.h.in
  6. 4
      src-input/duk_js.h
  7. 418
      src-input/duk_js_ops.c
  8. 4
      src-input/duk_numconv.c
  9. 13
      src-input/duk_util.h
  10. 112
      src-input/duk_util_misc.c

5
RELEASES.rst

@ -2021,12 +2021,13 @@ Planned
(GH-891); call related bytecode simplification (GH-896); minor bytecode
opcode handler optimizations (GH-903); refcount optimizations (GH-443,
GH-973, GH-1042); minor RegExp compile/execute optimizations (GH-974,
GH-1033)
GH-1033); minor IEEE double handling optimizations (GH-1051)
* Miscellaneous footprint improvements: RegExp compiler/executor (GH-977);
internal duk_dup() variants (GH-990); allow stripping of (almost) all
built-ins for low memory builds (GH-989); remove internal accessor setup
helper and use duk_def_prop() instead (GH-1010)
helper and use duk_def_prop() instead (GH-1010); minor IEEE double handling
optimizations (GH-1051)
* Internal change: rework shared internal string handling so that shared
strings are plain string constants used in macro values, rather than

2
src-input/duk_api_stack.c

@ -2885,6 +2885,8 @@ DUK_EXTERNAL duk_bool_t duk_is_nan(duk_context *ctx, duk_idx_t idx) {
tv = duk_get_tval_or_unused(ctx, idx);
DUK_ASSERT(tv != NULL);
/* XXX: for packed duk_tval an explicit "is number" check is unnecessary */
if (!DUK_TVAL_IS_NUMBER(tv)) {
return 0;
}

14
src-input/duk_bi_math.c

@ -54,8 +54,20 @@ DUK_LOCAL double duk__fmin_fixed(double x, double y) {
* -0 as Ecmascript requires.
*/
if (x == 0 && y == 0) {
duk_double_union du1, du2;
du1.d = x;
du2.d = y;
/* Already checked to be zero so these must hold, and allow us
* to check for "x is -0 or y is -0" by ORing the high parts
* for comparison.
*/
DUK_ASSERT(du1.ui[DUK_DBL_IDX_UI0] == 0 || du1.ui[DUK_DBL_IDX_UI0] == 0x80000000UL);
DUK_ASSERT(du2.ui[DUK_DBL_IDX_UI0] == 0 || du2.ui[DUK_DBL_IDX_UI0] == 0x80000000UL);
/* XXX: what's the safest way of creating a negative zero? */
if (DUK_SIGNBIT(x) != 0 || DUK_SIGNBIT(y) != 0) {
if ((du1.ui[DUK_DBL_IDX_UI0] | du2.ui[DUK_DBL_IDX_UI0]) != 0) {
/* Enter here if either x or y (or both) is -0. */
return -0.0;
} else {
return +0.0;

31
src-input/duk_bi_string.c

@ -1304,31 +1304,29 @@ DUK_INTERNAL duk_ret_t duk_bi_string_prototype_repeat(duk_context *ctx) {
DUK_ASSERT(h_input != NULL);
input_blen = DUK_HSTRING_GET_BYTELEN(h_input);
/* Not strictly correct but pretty close: duk_to_int() clamps so we
* can check for infinity (almost correctly) against DUK_INT_MAX.
* +Infinity must be rejected even if input string length is 0.
/* Count is ToNumber() coerced; +Infinity must be always rejected
* (even if input string is zero length), as well as negative values
* and -Infinity. -Infinity doesn't require an explicit check
* because duk_get_int() clamps it to DUK_INT_MIN which gets rejected
* as a negative value (regardless of input string length).
*/
d = duk_to_number(ctx, 0);
if (duk_is_posinf(d)) {
/* Positive infinity needs to be rejected even with input
* size 0. Negative infinity doesn't need special handling
* because it clamps to DUK_INT_MIN below and is rejected
* even is input size is 0.
*/
if (duk_double_is_posinf(d)) {
goto fail_range;
}
count_signed = duk_get_int(ctx, 0);
if (count_signed < 0) {
/* Covers -Infinity. */
goto fail_range;
}
count = (duk_uint_t) count_signed;
/* Overflow check for result length. */
result_len = count * input_blen;
if (count != 0 && result_len / count != input_blen) {
/* Overflow. */
goto fail_range;
}
/* Temporary fixed buffer, later converted to string. */
buf = (duk_uint8_t *) duk_push_fixed_buffer_nozero(ctx, result_len);
src = (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h_input);
@ -1342,10 +1340,8 @@ DUK_INTERNAL duk_ret_t duk_bi_string_prototype_repeat(duk_context *ctx) {
/* Take advantage of already copied pieces to speed up the process
* especially for small repeated strings.
*/
p = buf;
p_end = p + result_len;
copy_size = input_blen;
for (;;) {
duk_size_t remain = (duk_size_t) (p_end - p);
@ -1368,6 +1364,15 @@ DUK_INTERNAL duk_ret_t duk_bi_string_prototype_repeat(duk_context *ctx) {
}
#endif /* DUK_USE_PREFER_SIZE */
/* XXX: It would be useful to be able to create a duk_hstring with
* a certain byte size whose data area wasn't initialized and which
* wasn't in the string table yet. This would allow a string to be
* constructed directly without a buffer temporary and when it was
* finished, it could be injected into the string table. Currently
* this isn't possible because duk_hstrings are only tracked by the
* intern table (they are not in heap_allocated).
*/
duk_buffer_to_string(ctx, -1);
return 1;

29
src-input/duk_dblunion.h.in

@ -239,6 +239,12 @@ typedef union duk_double_union duk_double_union;
((u)->ull[DUK_DBL_IDX_ULL0] == 0x000000007ff00000ULL)
#define DUK__DBLUNION_IS_NEGINF(u) \
((u)->ull[DUK_DBL_IDX_ULL0] == 0x00000000fff00000ULL)
#define DUK__DBLUNION_IS_ANYZERO(u) \
(((u)->ull[DUK_DBL_IDX_ULL0] & 0xffffffff7fffffffULL) == 0x0000000000000000ULL)
#define DUK__DBLUNION_IS_POSZERO(u) \
((u)->ull[DUK_DBL_IDX_ULL0] == 0x0000000000000000ULL)
#define DUK__DBLUNION_IS_NEGZERO(u) \
((u)->ull[DUK_DBL_IDX_ULL0] == 0x0000000080000000ULL)
#else
/* Macros for 64-bit ops + big/little endian doubles. */
#define DUK__DBLUNION_SET_NAN_FULL(u) do { \
@ -255,6 +261,12 @@ typedef union duk_double_union duk_double_union;
((u)->ull[DUK_DBL_IDX_ULL0] == 0x7ff0000000000000ULL)
#define DUK__DBLUNION_IS_NEGINF(u) \
((u)->ull[DUK_DBL_IDX_ULL0] == 0xfff0000000000000ULL)
#define DUK__DBLUNION_IS_ANYZERO(u) \
(((u)->ull[DUK_DBL_IDX_ULL0] & 0x7fffffffffffffffULL) == 0x0000000000000000ULL)
#define DUK__DBLUNION_IS_POSZERO(u) \
((u)->ull[DUK_DBL_IDX_ULL0] == 0x0000000000000000ULL)
#define DUK__DBLUNION_IS_NEGZERO(u) \
((u)->ull[DUK_DBL_IDX_ULL0] == 0x8000000000000000ULL)
#endif
#else /* DUK_USE_64BIT_OPS */
/* Macros for no 64-bit ops, any endianness. */
@ -278,6 +290,15 @@ typedef union duk_double_union duk_double_union;
#define DUK__DBLUNION_IS_NEGINF(u) \
(((u)->ui[DUK_DBL_IDX_UI0] == 0xfff00000UL) && \
((u)->ui[DUK_DBL_IDX_UI1] == 0x00000000UL))
#define DUK__DBLUNION_IS_ANYZERO(u) \
((((u)->ui[DUK_DBL_IDX_UI0] & 0x7fffffffUL) == 0x00000000UL) && \
((u)->ui[DUK_DBL_IDX_UI1] == 0x00000000UL))
#define DUK__DBLUNION_IS_POSZERO(u) \
(((u)->ui[DUK_DBL_IDX_UI0] == 0x00000000UL) && \
((u)->ui[DUK_DBL_IDX_UI1] == 0x00000000UL))
#define DUK__DBLUNION_IS_NEGZERO(u) \
(((u)->ui[DUK_DBL_IDX_UI0] == 0x80000000UL) && \
((u)->ui[DUK_DBL_IDX_UI1] == 0x00000000UL))
#endif /* DUK_USE_64BIT_OPS */
#define DUK__DBLUNION_SET_NAN_NOTFULL(u) do { \
@ -328,8 +349,8 @@ typedef union duk_double_union duk_double_union;
DUK_DBLUNION_IS_NORMALIZED_NAN((u))) /* or is a normalized NaN */
#else /* DUK_USE_PACKED_TVAL */
#define DUK_DBLUNION_NORMALIZE_NAN_CHECK(u) /* nop: no need to normalize */
#define DUK_DBLUNION_IS_NAN(u) (DUK_ISNAN((u)->d))
#define DUK_DBLUNION_IS_NORMALIZED_NAN(u) (DUK_ISNAN((u)->d))
#define DUK_DBLUNION_IS_NAN(u) DUK__DBLUNION_IS_NAN_FULL((u)) /* (DUK_ISNAN((u)->d)) */
#define DUK_DBLUNION_IS_NORMALIZED_NAN(u) DUK__DBLUNION_IS_NAN_FULL((u)) /* (DUK_ISNAN((u)->d)) */
#define DUK_DBLUNION_IS_NORMALIZED(u) 1 /* all doubles are considered normalized */
#define DUK_DBLUNION_SET_NAN(u) do { \
/* in non-packed representation we don't care about which NaN is used */ \
@ -341,6 +362,10 @@ typedef union duk_double_union duk_double_union;
#define DUK_DBLUNION_IS_POSINF(u) DUK__DBLUNION_IS_POSINF((u))
#define DUK_DBLUNION_IS_NEGINF(u) DUK__DBLUNION_IS_NEGINF((u))
#define DUK_DBLUNION_IS_ANYZERO(u) DUK__DBLUNION_IS_ANYZERO((u))
#define DUK_DBLUNION_IS_POSZERO(u) DUK__DBLUNION_IS_POSZERO((u))
#define DUK_DBLUNION_IS_NEGZERO(u) DUK__DBLUNION_IS_NEGZERO((u))
/* XXX: native 64-bit byteswaps when available */
/* 64-bit byteswap, same operation independent of target endianness. */

4
src-input/duk_js.h

@ -17,8 +17,8 @@
#define DUK_EQUALS_FLAG_STRICT (1 << 1) /* use strict equality instead of non-strict equality */
/* Flags for duk_js_compare_helper(). */
#define DUK_COMPARE_FLAG_EVAL_LEFT_FIRST (1 << 0) /* eval left argument first */
#define DUK_COMPARE_FLAG_NEGATE (1 << 1) /* negate result */
#define DUK_COMPARE_FLAG_NEGATE (1 << 0) /* negate result */
#define DUK_COMPARE_FLAG_EVAL_LEFT_FIRST (1 << 1) /* eval left argument first */
/* conversions, coercions, comparison, etc */
DUK_INTERNAL_DECL duk_bool_t duk_js_toboolean(duk_tval *tv);

418
src-input/duk_js_ops.c

@ -1,13 +1,12 @@
/*
* Ecmascript specification algorithm and conversion helpers.
*
* These helpers encapsulate the primitive Ecmascript operation
* semantics, and are used by the bytecode executor and the API
* (among other places). Note that some primitives are only
* implemented as part of the API and have no "internal" helper.
* (This is the case when an internal helper would not really be
* useful; e.g. the operation is rare, uses value stack heavily,
* etc.)
* These helpers encapsulate the primitive Ecmascript operation semantics,
* and are used by the bytecode executor and the API (among other places).
* Some primitives are only implemented as part of the API and have no
* "internal" helper. This is the case when an internal helper would not
* really be useful; e.g. the operation is rare, uses value stack heavily,
* etc.
*
* The operation arguments depend on what is required to implement
* the operation:
@ -62,6 +61,7 @@ DUK_INTERNAL duk_bool_t duk_js_toboolean(duk_tval *tv) {
case DUK_TAG_NULL:
return 0;
case DUK_TAG_BOOLEAN:
DUK_ASSERT(DUK_TVAL_GET_BOOLEAN(tv) == 0 || DUK_TVAL_GET_BOOLEAN(tv) == 1);
return DUK_TVAL_GET_BOOLEAN(tv);
case DUK_TAG_STRING: {
duk_hstring *h = DUK_TVAL_GET_STRING(tv);
@ -95,16 +95,23 @@ DUK_INTERNAL duk_bool_t duk_js_toboolean(duk_tval *tv) {
default: {
/* number */
duk_double_t d;
#if defined(DUK_USE_PREFER_SIZE)
int c;
#endif
DUK_ASSERT(!DUK_TVAL_IS_UNUSED(tv));
DUK_ASSERT(DUK_TVAL_IS_DOUBLE(tv));
d = DUK_TVAL_GET_DOUBLE(tv);
#if defined(DUK_USE_PREFER_SIZE)
c = DUK_FPCLASSIFY((double) d);
if (c == DUK_FP_ZERO || c == DUK_FP_NAN) {
return 0;
} else {
return 1;
}
#else
DUK_ASSERT(duk_double_is_nan_or_zero(d) == 0 || duk_double_is_nan_or_zero(d) == 1);
return duk_double_is_nan_or_zero(d) ^ 1;
#endif
}
}
DUK_UNREACHABLE();
@ -160,8 +167,17 @@ DUK_LOCAL duk_double_t duk__tonumber_string_raw(duk_hthread *thr) {
DUK_S2N_FLAG_ALLOW_AUTO_HEX_INT;
duk_numconv_parse(ctx, 10 /*radix*/, s2n_flags);
#if defined(DUK_USE_PREFER_SIZE)
d = duk_get_number(ctx, -1);
duk_pop(ctx);
#else
thr->valstack_top--;
DUK_ASSERT(DUK_TVAL_IS_NUMBER(thr->valstack_top));
DUK_ASSERT(DUK_TVAL_IS_DOUBLE(thr->valstack_top)); /* no fastint conversion in numconv now */
DUK_ASSERT(!DUK_TVAL_NEEDS_REFCOUNT_UPDATE(thr->valstack_top));
d = DUK_TVAL_GET_DOUBLE(thr->valstack_top); /* assumes not a fastint */
DUK_TVAL_SET_UNDEFINED(thr->valstack_top);
#endif
return d;
}
@ -240,23 +256,33 @@ DUK_INTERNAL duk_double_t duk_js_tonumber(duk_hthread *thr, duk_tval *tv) {
/* exposed, used by e.g. duk_bi_date.c */
DUK_INTERNAL duk_double_t duk_js_tointeger_number(duk_double_t x) {
#if defined(DUK_USE_PREFER_SIZE)
duk_small_int_t c = (duk_small_int_t) DUK_FPCLASSIFY(x);
if (c == DUK_FP_NAN) {
if (DUK_UNLIKELY(c == DUK_FP_NAN)) {
return 0.0;
} else if (c == DUK_FP_ZERO || c == DUK_FP_INFINITE) {
/* XXX: FP_ZERO check can be removed, the else clause handles it
* correctly (preserving sign).
*/
} else if (DUK_UNLIKELY(c == DUK_FP_INFINITE)) {
return x;
} else {
duk_small_int_t s = (duk_small_int_t) DUK_SIGNBIT(x);
x = DUK_FLOOR(DUK_FABS(x)); /* truncate towards zero */
if (s) {
x = -x;
/* Finite, including neg/pos zero. Neg zero sign must be
* preserved.
*/
return duk_double_trunc_towards_zero(x);
}
#else /* DUK_USE_PREFER_SIZE */
/* NaN and Infinity have the same exponent so it's a cheap
* initial check for the rare path.
*/
if (DUK_UNLIKELY(duk_double_is_nan_or_inf(x))) {
if (duk_double_is_nan(x)) {
return 0.0;
} else {
return x;
}
return x;
} else {
return duk_double_trunc_towards_zero(x);
}
#endif /* DUK_USE_PREFER_SIZE */
}
DUK_INTERNAL duk_double_t duk_js_tointeger(duk_hthread *thr, duk_tval *tv) {
@ -271,20 +297,23 @@ DUK_INTERNAL duk_double_t duk_js_tointeger(duk_hthread *thr, duk_tval *tv) {
/* combined algorithm matching E5 Sections 9.5 and 9.6 */
DUK_LOCAL duk_double_t duk__toint32_touint32_helper(duk_double_t x, duk_bool_t is_toint32) {
duk_small_int_t c = (duk_small_int_t) DUK_FPCLASSIFY(x);
duk_small_int_t s;
#if defined (DUK_USE_PREFER_SIZE)
duk_small_int_t c;
#endif
#if defined (DUK_USE_PREFER_SIZE)
c = (duk_small_int_t) DUK_FPCLASSIFY(x);
if (c == DUK_FP_NAN || c == DUK_FP_ZERO || c == DUK_FP_INFINITE) {
return 0.0;
}
#else
if (duk_double_is_nan_zero_inf(x)) {
return 0.0;
}
#endif
/* x = sign(x) * floor(abs(x)), i.e. truncate towards zero, keep sign */
s = (duk_small_int_t) DUK_SIGNBIT(x);
x = DUK_FLOOR(DUK_FABS(x));
if (s) {
x = -x;
}
x = duk_double_trunc_towards_zero(x);
/* NOTE: fmod(x) result sign is same as sign of x, which
* differs from what Javascript wants (see Section 9.6).
@ -295,7 +324,7 @@ DUK_LOCAL duk_double_t duk__toint32_touint32_helper(duk_double_t x, duk_bool_t i
if (x < 0.0) {
x += DUK_DOUBLE_2TO32;
}
/* -> x in [0, 2**32[ */
DUK_ASSERT(x >= 0 && x < DUK_DOUBLE_2TO32); /* -> x in [0, 2**32[ */
if (is_toint32) {
if (x >= DUK_DOUBLE_2TO31) {
@ -351,58 +380,13 @@ DUK_INTERNAL duk_uint16_t duk_js_touint16(duk_hthread *thr, duk_tval *tv) {
/*
* ToString() (E5 Section 9.8)
*
* ==> implemented in the API.
*/
/*
* ToObject() (E5 Section 9.9)
*
* ==> implemented in the API.
*/
/*
* CheckObjectCoercible() (E5 Section 9.10)
*
* Note: no API equivalent now.
*/
#if 0 /* unused */
DUK_INTERNAL void duk_js_checkobjectcoercible(duk_hthread *thr, duk_tval *tv_x) {
duk_small_uint_t tag = DUK_TVAL_GET_TAG(tv_x);
/* Note: this must match ToObject() behavior */
if (tag == DUK_TAG_UNDEFINED ||
tag == DUK_TAG_NULL ||
tag == DUK_TAG_POINTER ||
tag == DUK_TAG_BUFFER) {
DUK_ERROR_TYPE(thr, "not object coercible");
}
}
#endif
/*
* IsCallable() (E5 Section 9.11)
*
* XXX: API equivalent is a separate implementation now, and this has
* currently no callers.
* ==> implemented in the API.
*/
#if 0 /* unused */
DUK_INTERNAL duk_bool_t duk_js_iscallable(duk_tval *tv_x) {
duk_hobject *obj;
if (!DUK_TVAL_IS_OBJECT(tv_x)) {
return 0;
}
obj = DUK_TVAL_GET_OBJECT(tv_x);
DUK_ASSERT(obj != NULL);
return DUK_HOBJECT_IS_CALLABLE(obj);
}
#endif
/*
* Loose equality, strict equality, and SameValue (E5 Sections 11.9.1, 11.9.4,
* 9.12). These have much in common so they can share some helpers.
@ -474,8 +458,8 @@ DUK_LOCAL duk_bool_t duk__js_samevalue_number(duk_double_t x, duk_double_t y) {
*
* signbit(x) == signbit(y)
*/
duk_small_int_t sx = (DUK_SIGNBIT(x) ? 1 : 0);
duk_small_int_t sy = (DUK_SIGNBIT(y) ? 1 : 0);
duk_small_int_t sx = DUK_SIGNBIT(x) ? 1 : 0;
duk_small_int_t sy = DUK_SIGNBIT(y) ? 1 : 0;
return (sx == sy);
}
@ -502,14 +486,12 @@ DUK_LOCAL duk_bool_t duk__js_samevalue_number(duk_double_t x, duk_double_t y) {
*
* signbit(x) == signbit(y)
*/
duk_small_int_t sx = (DUK_SIGNBIT(x) ? 1 : 0);
duk_small_int_t sy = (DUK_SIGNBIT(y) ? 1 : 0);
return (sx == sy);
return duk_double_same_sign(x, y);
}
return 1;
} else {
/* IEEE requires that zeros compare the same regardless
* of their signed, so if both x and y are zeroes, they
* of their sign, so if both x and y are zeroes, they
* are caught above.
*/
DUK_ASSERT(!(DUK_FPCLASSIFY(x) == DUK_FP_ZERO && DUK_FPCLASSIFY(y) == DUK_FP_ZERO));
@ -553,15 +535,19 @@ DUK_INTERNAL duk_bool_t duk_js_equals_helper(duk_hthread *thr, duk_tval *tv_x, d
else
#endif
if (DUK_TVAL_IS_NUMBER(tv_x) && DUK_TVAL_IS_NUMBER(tv_y)) {
/* Catches both doubles and cases where only one argument is a fastint */
duk_double_t d1, d2;
/* Catches both doubles and cases where only one argument is
* a fastint so can't assume a double.
*/
d1 = DUK_TVAL_GET_NUMBER(tv_x);
d2 = DUK_TVAL_GET_NUMBER(tv_y);
if (DUK_UNLIKELY((flags & DUK_EQUALS_FLAG_SAMEVALUE) != 0)) {
/* SameValue */
return duk__js_samevalue_number(DUK_TVAL_GET_NUMBER(tv_x),
DUK_TVAL_GET_NUMBER(tv_y));
return duk__js_samevalue_number(d1, d2);
} else {
/* equals and strict equals */
return duk__js_equals_number(DUK_TVAL_GET_NUMBER(tv_x),
DUK_TVAL_GET_NUMBER(tv_y));
return duk__js_equals_number(d1, d2);
}
} else if (DUK_TVAL_GET_TAG(tv_x) == DUK_TVAL_GET_TAG(tv_y)) {
switch (DUK_TVAL_GET_TAG(tv_x)) {
@ -628,6 +614,8 @@ DUK_INTERNAL duk_bool_t duk_js_equals_helper(duk_hthread *thr, duk_tval *tv_x, d
* code size.
*/
/* XXX: here getting a type mask would be useful */
/* Undefined/null are considered equal (e.g. "null == undefined" -> true). */
if ((DUK_TVAL_IS_UNDEFINED(tv_x) && DUK_TVAL_IS_NULL(tv_y)) ||
(DUK_TVAL_IS_NULL(tv_x) && DUK_TVAL_IS_UNDEFINED(tv_y))) {
@ -778,56 +766,139 @@ DUK_INTERNAL duk_small_int_t duk_js_buffer_compare(duk_heap *heap, duk_hbuffer *
}
#endif
#if defined(DUK_USE_FASTINT)
DUK_LOCAL duk_bool_t duk__compare_fastint(duk_bool_t retval, duk_int64_t v1, duk_int64_t v2) {
DUK_ASSERT(retval == 0 || retval == 1);
if (v1 < v2) {
return retval ^ 1;
} else {
return retval;
}
}
#endif
#if defined(DUK_USE_PARANOID_MATH)
DUK_LOCAL duk_bool_t duk__compare_number(duk_bool_t retval, duk_double_t d1, duk_double_t d2) {
duk_small_int_t c1, s1, c2, s2;
DUK_ASSERT(retval == 0 || retval == 1);
c1 = (duk_small_int_t) DUK_FPCLASSIFY(d1);
s1 = (duk_small_int_t) DUK_SIGNBIT(d1);
c2 = (duk_small_int_t) DUK_FPCLASSIFY(d2);
s2 = (duk_small_int_t) DUK_SIGNBIT(d2);
if (c1 == DUK_FP_NAN || c2 == DUK_FP_NAN) {
return 0; /* Always false, regardless of negation. */
}
if (c1 == DUK_FP_ZERO && c2 == DUK_FP_ZERO) {
/* For all combinations: +0 < +0, +0 < -0, -0 < +0, -0 < -0,
* steps e, f, and g.
*/
return retval; /* false */
}
if (d1 == d2) {
return retval; /* false */
}
if (c1 == DUK_FP_INFINITE && s1 == 0) {
/* x == +Infinity */
return retval; /* false */
}
if (c2 == DUK_FP_INFINITE && s2 == 0) {
/* y == +Infinity */
return retval ^ 1; /* true */
}
if (c2 == DUK_FP_INFINITE && s2 != 0) {
/* y == -Infinity */
return retval; /* false */
}
if (c1 == DUK_FP_INFINITE && s1 != 0) {
/* x == -Infinity */
return retval ^ 1; /* true */
}
if (d1 < d2) {
return retval ^ 1; /* true */
}
return retval; /* false */
}
#else /* DUK_USE_PARANOID_MATH */
DUK_LOCAL duk_bool_t duk__compare_number(duk_bool_t retval, duk_double_t d1, duk_double_t d2) {
/* This comparison tree relies doesn't match the exact steps in
* E5 Section 11.8.5 but should produce the same results. The
* steps rely on exact IEEE semantics for NaNs, etc.
*/
DUK_ASSERT(retval == 0 || retval == 1);
if (d1 < d2) {
/* In no case should both (d1 < d2) and (d2 < d1) be true.
* It's possible that neither is true though, and that's
* handled below.
*/
DUK_ASSERT(!(d2 < d1));
/* - d1 < d2, both d1/d2 are normals (not Infinity, not NaN)
* - d2 is +Infinity, d1 != +Infinity and NaN
* - d1 is -Infinity, d2 != -Infinity and NaN
*/
return retval ^ 1;
} else {
if (d2 < d1) {
/* - !(d1 < d2), both d1/d2 are normals (not Infinity, not NaN)
* - d1 is +Infinity, d2 != +Infinity and NaN
* - d2 is -Infinity, d1 != -Infinity and NaN
*/
return retval;
} else {
/* - d1 and/or d2 is NaN
* - d1 and d2 are both +/- 0
* - d1 == d2 (including infinities)
*/
if (duk_double_is_nan(d1) || duk_double_is_nan(d2)) {
/* Note: undefined from Section 11.8.5 always
* results in false return (see e.g. Section
* 11.8.3) - hence special treatment here.
*/
return 0; /* zero regardless of negation */
} else {
return retval;
}
}
}
}
#endif /* DUK_USE_PARANOID_MATH */
DUK_INTERNAL duk_bool_t duk_js_compare_helper(duk_hthread *thr, duk_tval *tv_x, duk_tval *tv_y, duk_small_int_t flags) {
duk_context *ctx = (duk_context *) thr;
duk_double_t d1, d2;
duk_small_int_t c1, c2;
duk_small_int_t s1, s2;
duk_small_int_t rc;
duk_bool_t retval;
DUK_ASSERT(DUK_COMPARE_FLAG_NEGATE == 1); /* Rely on this flag being lowest. */
retval = flags & DUK_COMPARE_FLAG_NEGATE;
DUK_ASSERT(retval == 0 || retval == 1);
/* Fast path for fastints */
#if defined(DUK_USE_FASTINT)
if (DUK_TVAL_IS_FASTINT(tv_x) && DUK_TVAL_IS_FASTINT(tv_y)) {
duk_int64_t v1 = DUK_TVAL_GET_FASTINT(tv_x);
duk_int64_t v2 = DUK_TVAL_GET_FASTINT(tv_y);
if (v1 < v2) {
/* 'lt is true' */
retval = 1;
} else {
retval = 0;
}
if (flags & DUK_COMPARE_FLAG_NEGATE) {
retval ^= 1;
}
return retval;
if (DUK_LIKELY(DUK_TVAL_IS_FASTINT(tv_x) && DUK_TVAL_IS_FASTINT(tv_y))) {
return duk__compare_fastint(retval,
DUK_TVAL_GET_FASTINT(tv_x),
DUK_TVAL_GET_FASTINT(tv_y));
}
#endif /* DUK_USE_FASTINT */
/* Fast path for numbers (one of which may be a fastint) */
#if 1 /* XXX: make fast paths optional for size minimization? */
if (DUK_TVAL_IS_NUMBER(tv_x) && DUK_TVAL_IS_NUMBER(tv_y)) {
d1 = DUK_TVAL_GET_NUMBER(tv_x);
d2 = DUK_TVAL_GET_NUMBER(tv_y);
c1 = DUK_FPCLASSIFY(d1);
c2 = DUK_FPCLASSIFY(d2);
if (c1 == DUK_FP_NORMAL && c2 == DUK_FP_NORMAL) {
/* XXX: this is a very narrow check, and doesn't cover
* zeroes, subnormals, infinities, which compare normally.
*/
if (d1 < d2) {
/* 'lt is true' */
retval = 1;
} else {
retval = 0;
}
if (flags & DUK_COMPARE_FLAG_NEGATE) {
retval ^= 1;
}
return retval;
}
#if !defined(DUK_USE_PREFER_SIZE)
if (DUK_LIKELY(DUK_TVAL_IS_NUMBER(tv_x) && DUK_TVAL_IS_NUMBER(tv_y))) {
return duk__compare_number(retval,
DUK_TVAL_GET_NUMBER(tv_x),
DUK_TVAL_GET_NUMBER(tv_y));
}
#endif
@ -855,16 +926,15 @@ DUK_INTERNAL duk_bool_t duk_js_compare_helper(duk_hthread *thr, duk_tval *tv_x,
DUK_ASSERT(h2 != NULL);
rc = duk_js_string_compare(h1, h2);
duk_pop_2(ctx);
if (rc < 0) {
goto lt_true;
return retval ^ 1;
} else {
goto lt_false;
return retval;
}
} else {
/* Ordering should not matter (E5 Section 11.8.5, step 3.a) but
* preserve it just in case.
*/
/* Ordering should not matter (E5 Section 11.8.5, step 3.a). */
#if 0
if (flags & DUK_COMPARE_FLAG_EVAL_LEFT_FIRST) {
d1 = duk_to_number(ctx, -2);
d2 = duk_to_number(ctx, -1);
@ -872,84 +942,28 @@ DUK_INTERNAL duk_bool_t duk_js_compare_helper(duk_hthread *thr, duk_tval *tv_x,
d2 = duk_to_number(ctx, -1);
d1 = duk_to_number(ctx, -2);
}
#endif
d1 = duk_to_number(ctx, -2);
d2 = duk_to_number(ctx, -1);
c1 = (duk_small_int_t) DUK_FPCLASSIFY(d1);
s1 = (duk_small_int_t) DUK_SIGNBIT(d1);
c2 = (duk_small_int_t) DUK_FPCLASSIFY(d2);
s2 = (duk_small_int_t) DUK_SIGNBIT(d2);
if (c1 == DUK_FP_NAN || c2 == DUK_FP_NAN) {
goto lt_undefined;
}
if (c1 == DUK_FP_ZERO && c2 == DUK_FP_ZERO) {
/* For all combinations: +0 < +0, +0 < -0, -0 < +0, -0 < -0,
* steps e, f, and g.
*/
goto lt_false;
}
if (d1 == d2) {
goto lt_false;
}
if (c1 == DUK_FP_INFINITE && s1 == 0) {
/* x == +Infinity */
goto lt_false;
}
if (c2 == DUK_FP_INFINITE && s2 == 0) {
/* y == +Infinity */
goto lt_true;
}
if (c2 == DUK_FP_INFINITE && s2 != 0) {
/* y == -Infinity */
goto lt_false;
}
if (c1 == DUK_FP_INFINITE && s1 != 0) {
/* x == -Infinity */
goto lt_true;
}
if (d1 < d2) {
goto lt_true;
}
goto lt_false;
}
lt_undefined:
/* Note: undefined from Section 11.8.5 always results in false
* return (see e.g. Section 11.8.3) - hence special treatment here.
*/
retval = 0;
goto cleanup;
lt_true:
if (flags & DUK_COMPARE_FLAG_NEGATE) {
retval = 0;
goto cleanup;
} else {
retval = 1;
goto cleanup;
}
/* never here */
/* We want to duk_pop_2(ctx); because the values are numbers
* no decref check is needed.
*/
#if defined(DUK_USE_PREFER_SIZE)
duk_pop_2(ctx);
#else
DUK_ASSERT(!DUK_TVAL_NEEDS_REFCOUNT_UPDATE(duk_get_tval(ctx, -2)));
DUK_ASSERT(!DUK_TVAL_NEEDS_REFCOUNT_UPDATE(duk_get_tval(ctx, -1)));
DUK_ASSERT(duk_get_top(ctx) >= 2);
((duk_hthread *) ctx)->valstack_top -= 2;
tv_x = ((duk_hthread *) ctx)->valstack_top;
tv_y = tv_x + 1;
DUK_TVAL_SET_UNDEFINED(tv_x); /* Value stack policy */
DUK_TVAL_SET_UNDEFINED(tv_y);
#endif
lt_false:
if (flags & DUK_COMPARE_FLAG_NEGATE) {
retval = 1;
goto cleanup;
} else {
retval = 0;
goto cleanup;
return duk__compare_number(retval, d1, d2);
}
/* never here */
cleanup:
duk_pop_2(ctx);
return retval;
}
/*

4
src-input/duk_numconv.c

@ -2239,6 +2239,10 @@ DUK_INTERNAL void duk_numconv_parse(duk_context *ctx, duk_small_int_t radix, duk
/*
* Convert binary digits into an IEEE double. Need to handle
* denormals and rounding correctly.
*
* Some call sites currently assume the result is always a
* non-fastint double. If this is changed, check all call
* sites.
*/
duk__dragon4_ctx_to_double(nc_ctx, &res);

13
src-input/duk_util.h

@ -537,8 +537,15 @@ DUK_INTERNAL_DECL void duk_byteswap_bytes(duk_uint8_t *p, duk_small_uint_t len);
DUK_INTERNAL_DECL duk_bool_t duk_is_whole_get_int32_nonegzero(duk_double_t x, duk_int32_t *ival);
DUK_INTERNAL_DECL duk_bool_t duk_is_whole_get_int32(duk_double_t x, duk_int32_t *ival);
DUK_INTERNAL_DECL duk_bool_t duk_is_anyinf(duk_double_t x);
DUK_INTERNAL_DECL duk_bool_t duk_is_posinf(duk_double_t x);
DUK_INTERNAL_DECL duk_bool_t duk_is_neginf(duk_double_t x);
DUK_INTERNAL_DECL duk_bool_t duk_double_is_anyinf(duk_double_t x);
DUK_INTERNAL_DECL duk_bool_t duk_double_is_posinf(duk_double_t x);
DUK_INTERNAL_DECL duk_bool_t duk_double_is_neginf(duk_double_t x);
DUK_INTERNAL_DECL duk_bool_t duk_double_is_nan(duk_double_t x);
DUK_INTERNAL_DECL duk_bool_t duk_double_is_nan_or_zero(duk_double_t x);
DUK_INTERNAL_DECL duk_bool_t duk_double_is_nan_or_inf(duk_double_t x);
DUK_INTERNAL_DECL duk_bool_t duk_double_is_nan_zero_inf(duk_double_t x);
DUK_INTERNAL_DECL duk_small_uint_t duk_double_signbit(duk_double_t x);
DUK_INTERNAL_DECL duk_double_t duk_double_trunc_towards_zero(duk_double_t x);
DUK_INTERNAL_DECL duk_bool_t duk_double_same_sign(duk_double_t x, duk_double_t y);
#endif /* DUK_UTIL_H_INCLUDED */

112
src-input/duk_util_misc.c

@ -262,20 +262,126 @@ DUK_INTERNAL duk_bool_t duk_is_whole_get_int32(duk_double_t x, duk_int32_t *ival
return 1;
}
DUK_INTERNAL duk_bool_t duk_is_anyinf(duk_double_t x) {
/*
* IEEE double checks
*/
DUK_INTERNAL duk_bool_t duk_double_is_anyinf(duk_double_t x) {
duk_double_union du;
du.d = x;
return DUK_DBLUNION_IS_ANYINF(&du);
}
DUK_INTERNAL duk_bool_t duk_is_posinf(duk_double_t x) {
DUK_INTERNAL duk_bool_t duk_double_is_posinf(duk_double_t x) {
duk_double_union du;
du.d = x;
return DUK_DBLUNION_IS_POSINF(&du);
}
DUK_INTERNAL duk_bool_t duk_is_neginf(duk_double_t x) {
DUK_INTERNAL duk_bool_t duk_double_is_neginf(duk_double_t x) {
duk_double_union du;
du.d = x;
return DUK_DBLUNION_IS_NEGINF(&du);
}
DUK_INTERNAL duk_bool_t duk_double_is_nan(duk_double_t x) {
duk_double_union du;
du.d = x;
/* Assumes we're dealing with a Duktape internal NaN which is
* NaN normalized if duk_tval requires it.
*/
DUK_ASSERT(DUK_DBLUNION_IS_NORMALIZED(&du));
return DUK_DBLUNION_IS_NAN(&du);
}
DUK_INTERNAL duk_bool_t duk_double_is_nan_or_zero(duk_double_t x) {
duk_double_union du;
du.d = x;
/* Assumes we're dealing with a Duktape internal NaN which is
* NaN normalized if duk_tval requires it.
*/
DUK_ASSERT(DUK_DBLUNION_IS_NORMALIZED(&du));
return DUK_DBLUNION_IS_NAN(&du) || DUK_DBLUNION_IS_ANYZERO(&du);
}
DUK_INTERNAL duk_bool_t duk_double_is_nan_or_inf(duk_double_t x) {
duk_double_union du;
du.d = x;
/* If exponent is 0x7FF the argument is either a NaN or an
* infinity. We don't need to check any other fields.
*/
#if defined(DUK_USE_64BIT_OPS)
#if defined(DUK_USE_DOUBLE_ME)
return (du.ull[DUK_DBL_IDX_ULL0] & 0x000000007ff00000ULL) == 0x000000007ff00000ULL;
#else
return (du.ull[DUK_DBL_IDX_ULL0] & 0x7ff0000000000000ULL) == 0x7ff0000000000000ULL;
#endif
#else
return (du.ui[DUK_DBL_IDX_UI0] & 0x7ff00000UL) == 0x7ff00000UL;
#endif
}
DUK_INTERNAL duk_bool_t duk_double_is_nan_zero_inf(duk_double_t x) {
duk_double_union du;
#if defined(DUK_USE_64BIT_OPS)
duk_uint64_t t;
#else
duk_uint32_t t;
#endif
du.d = x;
#if defined(DUK_USE_64BIT_OPS)
#if defined(DUK_USE_DOUBLE_ME)
t = du.ull[DUK_DBL_IDX_ULL0] & 0x000000007ff00000ULL;
if (t == 0x0000000000000000ULL) {
t = du.ull[DUK_DBL_IDX_ULL0] & 0x0000000080000000ULL;
return t == 0;
}
if (t == 0x000000007ff00000UL) {
return 1;
}
#else
t = du.ull[DUK_DBL_IDX_ULL0] & 0x7ff0000000000000ULL;
if (t == 0x0000000000000000ULL) {
t = du.ull[DUK_DBL_IDX_ULL0] & 0x8000000000000000ULL;
return t == 0;
}
if (t == 0x7ff0000000000000ULL) {
return 1;
}
#endif
#else
t = du.ui[DUK_DBL_IDX_UI0] & 0x7ff00000UL;
if (t == 0x00000000UL) {
return DUK_DBLUNION_IS_ANYZERO(&du);
}
if (t == 0x7ff00000UL) {
return 1;
}
#endif
return 0;
}
DUK_INTERNAL duk_small_uint_t duk_double_signbit(duk_double_t x) {
duk_double_union du;
du.d = x;
return (duk_small_uint_t) DUK_DBLUNION_GET_SIGNBIT(&du);
}
DUK_INTERNAL duk_double_t duk_double_trunc_towards_zero(duk_double_t x) {
/* XXX: optimize */
duk_small_int_t s = duk_double_signbit(x);
x = DUK_FLOOR(DUK_FABS(x)); /* truncate towards zero */
if (s) {
x = -x;
}
return x;
}
DUK_INTERNAL duk_bool_t duk_double_same_sign(duk_double_t x, duk_double_t y) {
duk_double_union du1;
duk_double_union du2;
du1.d = x;
du2.d = y;
return (((du1.ui[DUK_DBL_IDX_UI0] ^ du2.ui[DUK_DBL_IDX_UI0]) & 0x80000000UL) == 0);
}

Loading…
Cancel
Save