Bug 747197 - Move basic numeric conversion operations (double->uint32_t, double->int32_t, double->integer, but not Value->* just yet) into vm/NumericConversions.h, a header with minimized dependencies. r=luke

--HG--
extra : rebase_source : a564ba8f6a4350c1c49359f08e9de44670b89aeb
This commit is contained in:
Jeff Walden 2012-04-19 16:18:24 -07:00
parent 6e1a835eec
commit 4d293739ec
17 changed files with 400 additions and 333 deletions

View File

@ -40,17 +40,16 @@
#include "mozilla/FloatingPoint.h"
#include "frontend/FoldConstants.h"
#include "jslibmath.h"
#include "frontend/BytecodeEmitter.h"
#include "frontend/ParseNode.h"
#if JS_HAS_XML_SUPPORT
#include "jsxml.h"
#endif
#include "frontend/BytecodeEmitter.h"
#include "frontend/FoldConstants.h"
#include "frontend/ParseNode.h"
#include "vm/NumericConversions.h"
#include "jsatominlines.h"
#include "vm/String-inl.h"
@ -157,16 +156,16 @@ FoldBinaryNumeric(JSContext *cx, JSOp op, ParseNode *pn1, ParseNode *pn2,
switch (op) {
case JSOP_LSH:
case JSOP_RSH:
i = js_DoubleToECMAInt32(d);
j = js_DoubleToECMAInt32(d2);
i = ToInt32(d);
j = ToInt32(d2);
j &= 31;
d = (op == JSOP_LSH) ? i << j : i >> j;
break;
case JSOP_URSH:
j = js_DoubleToECMAInt32(d2);
j = ToInt32(d2);
j &= 31;
d = js_DoubleToECMAUint32(d) >> j;
d = ToUint32(d) >> j;
break;
case JSOP_ADD:
@ -828,7 +827,7 @@ js::FoldConstants(JSContext *cx, ParseNode *pn, TreeContext *tc, bool inCond)
d = pn1->pn_dval;
switch (pn->getOp()) {
case JSOP_BITNOT:
d = ~js_DoubleToECMAInt32(d);
d = ~ToInt32(d);
break;
case JSOP_NEG:

View File

@ -95,6 +95,7 @@
#include "js/MemoryMetrics.h"
#include "yarr/BumpPointerAllocator.h"
#include "vm/MethodGuard.h"
#include "vm/NumericConversions.h"
#include "vm/StringBuffer.h"
#include "vm/Xdr.h"
@ -345,7 +346,7 @@ JS_ConvertArgumentsVA(JSContext *cx, unsigned argc, jsval *argv, const char *for
case 'I':
if (!JS_ValueToNumber(cx, *sp, &d))
return JS_FALSE;
*va_arg(ap, double *) = js_DoubleToInteger(d);
*va_arg(ap, double *) = ToInteger(d);
break;
case 'S':
case 'W':
@ -556,13 +557,13 @@ JS_DoubleIsInt32(double d, int32_t *ip)
JS_PUBLIC_API(int32_t)
JS_DoubleToInt32(double d)
{
return js_DoubleToECMAInt32(d);
return ToInt32(d);
}
JS_PUBLIC_API(uint32_t)
JS_DoubleToUint32(double d)
{
return js_DoubleToECMAUint32(d);
return ToUint32(d);
}
JS_PUBLIC_API(JSBool)

View File

@ -129,6 +129,7 @@
#include "vm/ArgumentsObject.h"
#include "vm/MethodGuard.h"
#include "vm/NumericConversions.h"
#include "vm/StringBuffer.h"
#include "ds/Sort.h"
@ -3709,7 +3710,7 @@ js_Array(JSContext *cx, unsigned argc, Value *vp)
length = uint32_t(i);
} else {
double d = args[0].toDouble();
length = js_DoubleToECMAUint32(d);
length = ToUint32(d);
if (d != double(length)) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_ARRAY_LENGTH);
return false;

View File

@ -75,6 +75,7 @@
#include "jslibmath.h"
#include "vm/GlobalObject.h"
#include "vm/NumericConversions.h"
#include "vm/StringBuffer.h"
#include "jsinferinlines.h"
@ -614,7 +615,7 @@ date_msecFromArgs(JSContext *cx, CallArgs args, double *rval)
*rval = js_NaN;
return JS_TRUE;
}
array[loop] = js_DoubleToInteger(d);
array[loop] = ToInteger(d);
} else {
if (loop == 2) {
array[loop] = 1; /* Default the date argument to 1. */
@ -1785,7 +1786,7 @@ date_makeTime(JSContext *cx, Native native, unsigned maxargs, JSBool local, unsi
if (!MOZ_DOUBLE_IS_FINITE(nums[i])) {
argIsNotFinite = true;
} else {
nums[i] = js_DoubleToInteger(nums[i]);
nums[i] = ToInteger(nums[i]);
}
}
@ -1921,7 +1922,7 @@ date_makeDate(JSContext *cx, Native native, unsigned maxargs, JSBool local, unsi
if (!MOZ_DOUBLE_IS_FINITE(nums[i])) {
argIsNotFinite = true;
} else {
nums[i] = js_DoubleToInteger(nums[i]);
nums[i] = ToInteger(nums[i]);
}
}
@ -2036,7 +2037,7 @@ date_setYear(JSContext *cx, unsigned argc, Value *vp)
SetDateToNaN(cx, obj, &args.rval());
return true;
}
year = js_DoubleToInteger(year);
year = ToInteger(year);
if (year >= 0 && year <= 99)
year += 1900;

View File

@ -46,13 +46,32 @@
#include "mozilla/FloatingPoint.h"
#include "jscntxt.h"
#include <math.h>
#define HalfTimeDomain 8.64e15
#include "jstypes.h"
#define TIMECLIP(d) ((MOZ_DOUBLE_IS_FINITE(d) \
&& !((d < 0 ? -d : d) > HalfTimeDomain)) \
? js_DoubleToInteger(d + (+0.)) : js_NaN)
#include "vm/NumericConversions.h"
extern "C" {
struct JSObject;
struct JSContext;
}
namespace js {
/* ES5 15.9.1.14. */
inline double
TIMECLIP(double time)
{
/* Steps 1-2. */
if (!MOZ_DOUBLE_IS_FINITE(time) || abs(time) > 8.64e15)
return js_NaN;
/* Step 3. */
return ToInteger(time + (+0.0));
}
} /* namespace js */
extern JSObject *
js_InitDateClass(JSContext *cx, JSObject *obj);

View File

@ -64,7 +64,7 @@
struct JSCompartment;
extern "C" void
extern void
js_TraceXML(JSTracer *trc, JSXML* thing);
#if JS_STACK_GROWTH_DIRECTION > 0

View File

@ -79,6 +79,7 @@
#include "vm/GlobalObject.h"
#include "vm/MethodGuard.h"
#include "vm/NumericConversions.h"
#include "vm/StringBuffer.h"
#include "jsatominlines.h"
@ -1274,7 +1275,7 @@ ToInt32Slow(JSContext *cx, const Value &v, int32_t *out)
if (!ToNumberSlow(cx, v, &d))
return false;
}
*out = js_DoubleToECMAInt32(d);
*out = ToInt32(d);
return true;
}
@ -1289,7 +1290,7 @@ ToUint32Slow(JSContext *cx, const Value &v, uint32_t *out)
if (!ToNumberSlow(cx, v, &d))
return false;
}
*out = js_DoubleToECMAUint32(d);
*out = ToUint32(d);
return true;
}

View File

@ -46,6 +46,8 @@
#include "jsobj.h"
#include "vm/NumericConversions.h"
extern double js_NaN;
extern double js_PositiveInfinity;
extern double js_NegativeInfinity;
@ -221,275 +223,6 @@ num_parseInt(JSContext *cx, unsigned argc, Value *vp);
} /* namespace js */
union jsdpun {
struct {
#if defined(IS_LITTLE_ENDIAN) && !defined(FPU_IS_ARM_FPA)
uint32_t lo, hi;
#else
uint32_t hi, lo;
#endif
} s;
uint64_t u64;
double d;
};
/*
* Specialized ToInt32 and ToUint32 converters for doubles.
*/
/*
* From the ES3 spec, 9.5
* 2. If Result(1) is NaN, +0, -0, +Inf, or -Inf, return +0.
* 3. Compute sign(Result(1)) * floor(abs(Result(1))).
* 4. Compute Result(3) modulo 2^32; that is, a finite integer value k of Number
* type with positive sign and less than 2^32 in magnitude such the mathematical
* difference of Result(3) and k is mathematically an integer multiple of 2^32.
* 5. If Result(4) is greater than or equal to 2^31, return Result(4)- 2^32,
* otherwise return Result(4).
*/
static inline int32_t
js_DoubleToECMAInt32(double d)
{
#if defined(__i386__) || defined(__i386) || defined(__x86_64__) || \
defined(_M_IX86) || defined(_M_X64)
jsdpun du, duh, two32;
uint32_t di_h, u_tmp, expon, shift_amount;
int32_t mask32;
/*
* Algorithm Outline
* Step 1. If d is NaN, +/-Inf or |d|>=2^84 or |d|<1, then return 0
* All of this is implemented based on an exponent comparison.
* Step 2. If |d|<2^31, then return (int)d
* The cast to integer (conversion in RZ mode) returns the correct result.
* Step 3. If |d|>=2^32, d:=fmod(d, 2^32) is taken -- but without a call
* Step 4. If |d|>=2^31, then the fractional bits are cleared before
* applying the correction by 2^32: d - sign(d)*2^32
* Step 5. Return (int)d
*/
du.d = d;
di_h = du.s.hi;
u_tmp = (di_h & 0x7ff00000) - 0x3ff00000;
if (u_tmp >= (0x45300000-0x3ff00000)) {
// d is Nan, +/-Inf or +/-0, or |d|>=2^(32+52) or |d|<1, in which case result=0
return 0;
}
if (u_tmp < 0x01f00000) {
// |d|<2^31
return int32_t(d);
}
if (u_tmp > 0x01f00000) {
// |d|>=2^32
expon = u_tmp >> 20;
shift_amount = expon - 21;
duh.u64 = du.u64;
mask32 = 0x80000000;
if (shift_amount < 32) {
mask32 >>= shift_amount;
duh.s.hi = du.s.hi & mask32;
duh.s.lo = 0;
} else {
mask32 >>= (shift_amount-32);
duh.s.hi = du.s.hi;
duh.s.lo = du.s.lo & mask32;
}
du.d -= duh.d;
}
di_h = du.s.hi;
// eliminate fractional bits
u_tmp = (di_h & 0x7ff00000);
if (u_tmp >= 0x41e00000) {
// |d|>=2^31
expon = u_tmp >> 20;
shift_amount = expon - (0x3ff - 11);
mask32 = 0x80000000;
if (shift_amount < 32) {
mask32 >>= shift_amount;
du.s.hi &= mask32;
du.s.lo = 0;
} else {
mask32 >>= (shift_amount-32);
du.s.lo &= mask32;
}
two32.s.hi = 0x41f00000 ^ (du.s.hi & 0x80000000);
two32.s.lo = 0;
du.d -= two32.d;
}
return int32_t(du.d);
#elif defined (__arm__) && defined (__GNUC__)
int32_t i;
uint32_t tmp0;
uint32_t tmp1;
uint32_t tmp2;
asm (
// We use a pure integer solution here. In the 'softfp' ABI, the argument
// will start in r0 and r1, and VFP can't do all of the necessary ECMA
// conversions by itself so some integer code will be required anyway. A
// hybrid solution is faster on A9, but this pure integer solution is
// notably faster for A8.
// %0 is the result register, and may alias either of the %[QR]1 registers.
// %Q4 holds the lower part of the mantissa.
// %R4 holds the sign, exponent, and the upper part of the mantissa.
// %1, %2 and %3 are used as temporary values.
// Extract the exponent.
" mov %1, %R4, LSR #20\n"
" bic %1, %1, #(1 << 11)\n" // Clear the sign.
// Set the implicit top bit of the mantissa. This clobbers a bit of the
// exponent, but we have already extracted that.
" orr %R4, %R4, #(1 << 20)\n"
// Special Cases
// We should return zero in the following special cases:
// - Exponent is 0x000 - 1023: +/-0 or subnormal.
// - Exponent is 0x7ff - 1023: +/-INFINITY or NaN
// - This case is implicitly handled by the standard code path anyway,
// as shifting the mantissa up by the exponent will result in '0'.
//
// The result is composed of the mantissa, prepended with '1' and
// bit-shifted left by the (decoded) exponent. Note that because the r1[20]
// is the bit with value '1', r1 is effectively already shifted (left) by
// 20 bits, and r0 is already shifted by 52 bits.
// Adjust the exponent to remove the encoding offset. If the decoded
// exponent is negative, quickly bail out with '0' as such values round to
// zero anyway. This also catches +/-0 and subnormals.
" sub %1, %1, #0xff\n"
" subs %1, %1, #0x300\n"
" bmi 8f\n"
// %1 = (decoded) exponent >= 0
// %R4 = upper mantissa and sign
// ---- Lower Mantissa ----
" subs %3, %1, #52\n" // Calculate exp-52
" bmi 1f\n"
// Shift r0 left by exp-52.
// Ensure that we don't overflow ARM's 8-bit shift operand range.
// We need to handle anything up to an 11-bit value here as we know that
// 52 <= exp <= 1024 (0x400). Any shift beyond 31 bits results in zero
// anyway, so as long as we don't touch the bottom 5 bits, we can use
// a logical OR to push long shifts into the 32 <= (exp&0xff) <= 255 range.
" bic %2, %3, #0xff\n"
" orr %3, %3, %2, LSR #3\n"
// We can now perform a straight shift, avoiding the need for any
// conditional instructions or extra branches.
" mov %Q4, %Q4, LSL %3\n"
" b 2f\n"
"1:\n" // Shift r0 right by 52-exp.
// We know that 0 <= exp < 52, and we can shift up to 255 bits so 52-exp
// will always be a valid shift and we can sk%3 the range check for this case.
" rsb %3, %1, #52\n"
" mov %Q4, %Q4, LSR %3\n"
// %1 = (decoded) exponent
// %R4 = upper mantissa and sign
// %Q4 = partially-converted integer
"2:\n"
// ---- Upper Mantissa ----
// This is much the same as the lower mantissa, with a few different
// boundary checks and some masking to hide the exponent & sign bit in the
// upper word.
// Note that the upper mantissa is pre-shifted by 20 in %R4, but we shift
// it left more to remove the sign and exponent so it is effectively
// pre-shifted by 31 bits.
" subs %3, %1, #31\n" // Calculate exp-31
" mov %1, %R4, LSL #11\n" // Re-use %1 as a temporary register.
" bmi 3f\n"
// Shift %R4 left by exp-31.
// Avoid overflowing the 8-bit shift range, as before.
" bic %2, %3, #0xff\n"
" orr %3, %3, %2, LSR #3\n"
// Perform the shift.
" mov %2, %1, LSL %3\n"
" b 4f\n"
"3:\n" // Shift r1 right by 31-exp.
// We know that 0 <= exp < 31, and we can shift up to 255 bits so 31-exp
// will always be a valid shift and we can skip the range check for this case.
" rsb %3, %3, #0\n" // Calculate 31-exp from -(exp-31)
" mov %2, %1, LSR %3\n" // Thumb-2 can't do "LSR %3" in "orr".
// %Q4 = partially-converted integer (lower)
// %R4 = upper mantissa and sign
// %2 = partially-converted integer (upper)
"4:\n"
// Combine the converted parts.
" orr %Q4, %Q4, %2\n"
// Negate the result if we have to, and move it to %0 in the process. To
// avoid conditionals, we can do this by inverting on %R4[31], then adding
// %R4[31]>>31.
" eor %Q4, %Q4, %R4, ASR #31\n"
" add %0, %Q4, %R4, LSR #31\n"
" b 9f\n"
"8:\n"
// +/-INFINITY, +/-0, subnormals, NaNs, and anything else out-of-range that
// will result in a conversion of '0'.
" mov %0, #0\n"
"9:\n"
: "=r" (i), "=&r" (tmp0), "=&r" (tmp1), "=&r" (tmp2)
: "r" (d)
: "cc"
);
return i;
#else
int32_t i;
double two32, two31;
if (!MOZ_DOUBLE_IS_FINITE(d))
return 0;
i = (int32_t) d;
if ((double) i == d)
return i;
two32 = 4294967296.0;
two31 = 2147483648.0;
d = fmod(d, two32);
d = (d >= 0) ? floor(d) : ceil(d) + two32;
return (int32_t) (d >= two31 ? d - two32 : d);
#endif
}
inline uint32_t
js_DoubleToECMAUint32(double d)
{
return uint32_t(js_DoubleToECMAInt32(d));
}
/*
* Convert a double to an integral number, stored in a double.
* If d is NaN, return 0. If d is an infinity, return it without conversion.
*/
static inline double
js_DoubleToInteger(double d)
{
if (d == 0)
return d;
if (!MOZ_DOUBLE_IS_FINITE(d)) {
if (MOZ_DOUBLE_IS_NaN(d))
return 0;
return d;
}
JSBool neg = (d < 0);
d = floor(neg ? -d : d);
return neg ? -d : d;
}
/*
* Similar to strtod except that it replaces overflows with infinities of the
* correct sign, and underflows with zeros of the correct sign. Guaranteed to
@ -560,7 +293,7 @@ ToInteger(JSContext *cx, const js::Value &v, double *dp)
if (!ToNumberSlow(cx, v, dp))
return false;
}
*dp = js_DoubleToInteger(*dp);
*dp = ToInteger(*dp);
return true;
}

View File

@ -40,6 +40,7 @@
#ifndef jsnuminlines_h___
#define jsnuminlines_h___
#include "vm/NumericConversions.h"
#include "vm/Unicode.h"
#include "jsstrinlines.h"
@ -51,7 +52,7 @@ template<typename T> struct NumberTraits { };
template<> struct NumberTraits<int32_t> {
static JS_ALWAYS_INLINE int32_t NaN() { return 0; }
static JS_ALWAYS_INLINE int32_t toSelfType(int32_t i) { return i; }
static JS_ALWAYS_INLINE int32_t toSelfType(double d) { return js_DoubleToECMAUint32(d); }
static JS_ALWAYS_INLINE int32_t toSelfType(double d) { return ToUint32(d); }
};
template<> struct NumberTraits<double> {
static JS_ALWAYS_INLINE double NaN() { return js_NaN; }

View File

@ -76,6 +76,7 @@
#include "builtin/RegExp.h"
#include "vm/GlobalObject.h"
#include "vm/NumericConversions.h"
#include "vm/RegExpObject.h"
#include "vm/StringBuffer.h"
@ -1242,7 +1243,7 @@ str_lastIndexOf(JSContext *cx, unsigned argc, Value *vp)
if (!ToNumber(cx, args[1], &d))
return false;
if (!MOZ_DOUBLE_IS_NaN(d)) {
d = js_DoubleToInteger(d);
d = ToInteger(d);
if (d <= 0)
i = 0;
else if (d < i)
@ -2603,7 +2604,7 @@ js::str_split(JSContext *cx, unsigned argc, Value *vp)
double d;
if (!ToNumber(cx, args[1], &d))
return false;
limit = js_DoubleToECMAUint32(d);
limit = ToUint32(d);
} else {
limit = UINT32_MAX;
}

View File

@ -61,6 +61,7 @@
#include "jstypedarray.h"
#include "vm/GlobalObject.h"
#include "vm/NumericConversions.h"
#include "jsatominlines.h"
#include "jsinferinlines.h"
@ -1186,7 +1187,7 @@ class TypedArrayTemplate
setIndex(tarray, index, NativeType(d));
} else if (ArrayTypeIsUnsigned()) {
JS_ASSERT(sizeof(NativeType) <= 4);
uint32_t n = js_DoubleToECMAUint32(d);
uint32_t n = ToUint32(d);
setIndex(tarray, index, NativeType(n));
} else if (ArrayTypeID() == TypedArray::TYPE_UINT8_CLAMPED) {
// The uint8_clamped type has a special rounding converter
@ -1194,7 +1195,7 @@ class TypedArrayTemplate
setIndex(tarray, index, NativeType(d));
} else {
JS_ASSERT(sizeof(NativeType) <= 4);
int32_t n = js_DoubleToECMAInt32(d);
int32_t n = ToInt32(d);
setIndex(tarray, index, NativeType(n));
}
@ -1758,8 +1759,8 @@ class TypedArrayTemplate
if (TypeIsFloatingPoint<NativeType>())
return NativeType(d);
if (TypeIsUnsigned<NativeType>())
return NativeType(js_DoubleToECMAUint32(d));
return NativeType(js_DoubleToECMAInt32(d));
return NativeType(ToUint32(d));
return NativeType(ToInt32(d));
}
static NativeType

View File

@ -41,16 +41,18 @@
#include "jsbool.h"
#include "jscntxt.h"
#include "jslibmath.h"
#include "jsnum.h"
#include "jsscope.h"
#include "jsobjinlines.h"
#include "jsscriptinlines.h"
#include "jstypedarrayinlines.h"
#include "frontend/BytecodeEmitter.h"
#include "methodjit/MethodJIT.h"
#include "methodjit/Compiler.h"
#include "methodjit/StubCalls.h"
#include "vm/NumericConversions.h"
#include "jsobjinlines.h"
#include "jsscriptinlines.h"
#include "jstypedarrayinlines.h"
#include "methodjit/FrameState-inl.h"
#include "jsautooplen.h"
@ -1262,7 +1264,7 @@ mjit::Compiler::convertForTypedArray(int atype, ValueRemat *vr, bool *allocated)
} else {
i32 = (atype == TypedArray::TYPE_UINT8_CLAMPED)
? ClampDoubleToUint8(v.toDouble())
: js_DoubleToECMAInt32(v.toDouble());
: ToInt32(v.toDouble());
}
*vr = ValueRemat::FromConstant(Int32Value(i32));
}
@ -1698,9 +1700,8 @@ mjit::Compiler::jsop_setelem(bool popGuaranteed)
ic.fastPathRejoin = masm.label();
// When generating typed array stubs, it may be necessary to call
// js_DoubleToECMAInt32(), which would clobber registers. To deal with
// this, we tell the IC exactly which registers need to be saved
// across calls.
// ToInt32(), which would clobber registers. To deal with this, we tell the
// IC exactly which registers need to be saved across calls.
ic.volatileMask = frame.regsInUse();
// If the RHS will be popped, and doesn't overlap any live values, then

View File

@ -43,7 +43,7 @@
#include "assembler/assembler/MacroAssembler.h"
#include "methodjit/MachineRegs.h"
#include "methodjit/RematInfo.h"
#include "jsnum.h"
#include "jsval.h"
namespace js {
namespace mjit {
@ -397,9 +397,12 @@ class PunboxAssembler : public JSC::MacroAssembler
}
void loadStaticDouble(const double *dp, FPRegisterID dest, RegisterID scratch) {
jsdpun du;
du.d = *dp;
move(ImmPtr(reinterpret_cast<void*>(du.u64)), scratch);
union DoublePun {
double d;
uint64_t u;
} pun;
pun.d = *dp;
move(ImmPtr(reinterpret_cast<void*>(pun.u)), scratch);
m_assembler.movq_rr(scratch, dest);
}

View File

@ -51,7 +51,9 @@
#include "jsbool.h"
#include "assembler/assembler/MacroAssemblerCodeRef.h"
#include "jstypes.h"
#include "vm/Debugger.h"
#include "vm/NumericConversions.h"
#include "vm/String.h"
#include "methodjit/Compiler.h"
#include "methodjit/StubCalls.h"
@ -1734,7 +1736,7 @@ stubs::ConvertToTypedInt(JSContext *cx, Value *vp)
if (vp->isDouble()) {
if (Clamped)
return ClampDoubleToUint8(vp->toDouble());
return js_DoubleToECMAInt32(vp->toDouble());
return ToInt32(vp->toDouble());
}
if (vp->isNull() || vp->isObject() || vp->isUndefined())

View File

@ -42,7 +42,8 @@
#include "jscntxt.h"
#include "jstypedarray.h"
#include "jstypedarrayinlines.h"
#include "vm/NumericConversions.h"
#include "jsnuminlines.h"
#include "jstypedarrayinlines.h"
@ -129,7 +130,7 @@ ConstantFoldForIntArray(JSContext *cx, JSObject *tarray, ValueRemat *vr)
if (v.isDouble()) {
i32 = (TypedArray::getType(tarray) == js::TypedArray::TYPE_UINT8_CLAMPED)
? ClampDoubleToUint8(v.toDouble())
: js_DoubleToECMAInt32(v.toDouble());
: ToInt32(v.toDouble());
} else if (v.isInt32()) {
i32 = v.toInt32();
if (TypedArray::getType(tarray) == js::TypedArray::TYPE_UINT8_CLAMPED)

View File

@ -0,0 +1,281 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sw=4 et tw=78:
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef NumericConversions_h___
#define NumericConversions_h___
#include "mozilla/FloatingPoint.h"
#include <math.h>
/* A NaN whose bit pattern conforms to JS::Value's bit pattern restrictions. */
extern double js_NaN;
namespace js {
namespace detail {
union DoublePun {
struct {
#if defined(IS_LITTLE_ENDIAN) && !defined(FPU_IS_ARM_FPA)
uint32_t lo, hi;
#else
uint32_t hi, lo;
#endif
} s;
uint64_t u64;
double d;
};
} /* namespace detail */
/* ES5 9.5 ToInt32 (specialized for doubles). */
inline int32_t
ToInt32(double d)
{
#if defined(__i386__) || defined(__i386) || defined(__x86_64__) || \
defined(_M_IX86) || defined(_M_X64)
detail::DoublePun du, duh, two32;
uint32_t di_h, u_tmp, expon, shift_amount;
int32_t mask32;
/*
* Algorithm Outline
* Step 1. If d is NaN, +/-Inf or |d|>=2^84 or |d|<1, then return 0
* All of this is implemented based on an exponent comparison.
* Step 2. If |d|<2^31, then return (int)d
* The cast to integer (conversion in RZ mode) returns the correct result.
* Step 3. If |d|>=2^32, d:=fmod(d, 2^32) is taken -- but without a call
* Step 4. If |d|>=2^31, then the fractional bits are cleared before
* applying the correction by 2^32: d - sign(d)*2^32
* Step 5. Return (int)d
*/
du.d = d;
di_h = du.s.hi;
u_tmp = (di_h & 0x7ff00000) - 0x3ff00000;
if (u_tmp >= (0x45300000-0x3ff00000)) {
// d is Nan, +/-Inf or +/-0, or |d|>=2^(32+52) or |d|<1, in which case result=0
return 0;
}
if (u_tmp < 0x01f00000) {
// |d|<2^31
return int32_t(d);
}
if (u_tmp > 0x01f00000) {
// |d|>=2^32
expon = u_tmp >> 20;
shift_amount = expon - 21;
duh.u64 = du.u64;
mask32 = 0x80000000;
if (shift_amount < 32) {
mask32 >>= shift_amount;
duh.s.hi = du.s.hi & mask32;
duh.s.lo = 0;
} else {
mask32 >>= (shift_amount-32);
duh.s.hi = du.s.hi;
duh.s.lo = du.s.lo & mask32;
}
du.d -= duh.d;
}
di_h = du.s.hi;
// eliminate fractional bits
u_tmp = (di_h & 0x7ff00000);
if (u_tmp >= 0x41e00000) {
// |d|>=2^31
expon = u_tmp >> 20;
shift_amount = expon - (0x3ff - 11);
mask32 = 0x80000000;
if (shift_amount < 32) {
mask32 >>= shift_amount;
du.s.hi &= mask32;
du.s.lo = 0;
} else {
mask32 >>= (shift_amount-32);
du.s.lo &= mask32;
}
two32.s.hi = 0x41f00000 ^ (du.s.hi & 0x80000000);
two32.s.lo = 0;
du.d -= two32.d;
}
return int32_t(du.d);
#elif defined (__arm__) && defined (__GNUC__)
int32_t i;
uint32_t tmp0;
uint32_t tmp1;
uint32_t tmp2;
asm (
// We use a pure integer solution here. In the 'softfp' ABI, the argument
// will start in r0 and r1, and VFP can't do all of the necessary ECMA
// conversions by itself so some integer code will be required anyway. A
// hybrid solution is faster on A9, but this pure integer solution is
// notably faster for A8.
// %0 is the result register, and may alias either of the %[QR]1 registers.
// %Q4 holds the lower part of the mantissa.
// %R4 holds the sign, exponent, and the upper part of the mantissa.
// %1, %2 and %3 are used as temporary values.
// Extract the exponent.
" mov %1, %R4, LSR #20\n"
" bic %1, %1, #(1 << 11)\n" // Clear the sign.
// Set the implicit top bit of the mantissa. This clobbers a bit of the
// exponent, but we have already extracted that.
" orr %R4, %R4, #(1 << 20)\n"
// Special Cases
// We should return zero in the following special cases:
// - Exponent is 0x000 - 1023: +/-0 or subnormal.
// - Exponent is 0x7ff - 1023: +/-INFINITY or NaN
// - This case is implicitly handled by the standard code path anyway,
// as shifting the mantissa up by the exponent will result in '0'.
//
// The result is composed of the mantissa, prepended with '1' and
// bit-shifted left by the (decoded) exponent. Note that because the r1[20]
// is the bit with value '1', r1 is effectively already shifted (left) by
// 20 bits, and r0 is already shifted by 52 bits.
// Adjust the exponent to remove the encoding offset. If the decoded
// exponent is negative, quickly bail out with '0' as such values round to
// zero anyway. This also catches +/-0 and subnormals.
" sub %1, %1, #0xff\n"
" subs %1, %1, #0x300\n"
" bmi 8f\n"
// %1 = (decoded) exponent >= 0
// %R4 = upper mantissa and sign
// ---- Lower Mantissa ----
" subs %3, %1, #52\n" // Calculate exp-52
" bmi 1f\n"
// Shift r0 left by exp-52.
// Ensure that we don't overflow ARM's 8-bit shift operand range.
// We need to handle anything up to an 11-bit value here as we know that
// 52 <= exp <= 1024 (0x400). Any shift beyond 31 bits results in zero
// anyway, so as long as we don't touch the bottom 5 bits, we can use
// a logical OR to push long shifts into the 32 <= (exp&0xff) <= 255 range.
" bic %2, %3, #0xff\n"
" orr %3, %3, %2, LSR #3\n"
// We can now perform a straight shift, avoiding the need for any
// conditional instructions or extra branches.
" mov %Q4, %Q4, LSL %3\n"
" b 2f\n"
"1:\n" // Shift r0 right by 52-exp.
// We know that 0 <= exp < 52, and we can shift up to 255 bits so 52-exp
// will always be a valid shift and we can sk%3 the range check for this case.
" rsb %3, %1, #52\n"
" mov %Q4, %Q4, LSR %3\n"
// %1 = (decoded) exponent
// %R4 = upper mantissa and sign
// %Q4 = partially-converted integer
"2:\n"
// ---- Upper Mantissa ----
// This is much the same as the lower mantissa, with a few different
// boundary checks and some masking to hide the exponent & sign bit in the
// upper word.
// Note that the upper mantissa is pre-shifted by 20 in %R4, but we shift
// it left more to remove the sign and exponent so it is effectively
// pre-shifted by 31 bits.
" subs %3, %1, #31\n" // Calculate exp-31
" mov %1, %R4, LSL #11\n" // Re-use %1 as a temporary register.
" bmi 3f\n"
// Shift %R4 left by exp-31.
// Avoid overflowing the 8-bit shift range, as before.
" bic %2, %3, #0xff\n"
" orr %3, %3, %2, LSR #3\n"
// Perform the shift.
" mov %2, %1, LSL %3\n"
" b 4f\n"
"3:\n" // Shift r1 right by 31-exp.
// We know that 0 <= exp < 31, and we can shift up to 255 bits so 31-exp
// will always be a valid shift and we can skip the range check for this case.
" rsb %3, %3, #0\n" // Calculate 31-exp from -(exp-31)
" mov %2, %1, LSR %3\n" // Thumb-2 can't do "LSR %3" in "orr".
// %Q4 = partially-converted integer (lower)
// %R4 = upper mantissa and sign
// %2 = partially-converted integer (upper)
"4:\n"
// Combine the converted parts.
" orr %Q4, %Q4, %2\n"
// Negate the result if we have to, and move it to %0 in the process. To
// avoid conditionals, we can do this by inverting on %R4[31], then adding
// %R4[31]>>31.
" eor %Q4, %Q4, %R4, ASR #31\n"
" add %0, %Q4, %R4, LSR #31\n"
" b 9f\n"
"8:\n"
// +/-INFINITY, +/-0, subnormals, NaNs, and anything else out-of-range that
// will result in a conversion of '0'.
" mov %0, #0\n"
"9:\n"
: "=r" (i), "=&r" (tmp0), "=&r" (tmp1), "=&r" (tmp2)
: "r" (d)
: "cc"
);
return i;
#else
int32_t i;
double two32, two31;
if (!MOZ_DOUBLE_IS_FINITE(d))
return 0;
/* FIXME: This relies on undefined behavior; see bug 667739. */
i = (int32_t) d;
if ((double) i == d)
return i;
two32 = 4294967296.0;
two31 = 2147483648.0;
d = fmod(d, two32);
d = (d >= 0) ? floor(d) : ceil(d) + two32;
return (int32_t) (d >= two31 ? d - two32 : d);
#endif
}
/* ES5 9.6 (specialized for doubles). */
inline uint32_t
ToUint32(double d)
{
return uint32_t(ToInt32(d));
}
/* ES5 9.4 ToInteger (specialized for doubles). */
inline double
ToInteger(double d)
{
if (d == 0)
return d;
if (!MOZ_DOUBLE_IS_FINITE(d)) {
if (MOZ_DOUBLE_IS_NaN(d))
return 0;
return d;
}
bool neg = (d < 0);
d = floor(neg ? -d : d);
return neg ? -d : d;
}
} /* namespace js */
#endif /* NumericConversions_h__ */

View File

@ -44,6 +44,8 @@
#include "jsprvtd.h"
#include "jsnum.h"
#include "vm/NumericConversions.h"
namespace js {
/*
@ -212,28 +214,47 @@ class XDRState {
return true;
}
bool codeDouble(double *dp) {
jsdpun tmp;
bool codeUint64(uint64_t *n) {
if (mode == XDR_ENCODE) {
uint8_t *ptr = buf.write(sizeof tmp);
uint8_t *ptr = buf.write(sizeof(*n));
if (!ptr)
return false;
tmp.d = *dp;
tmp.s.lo = NormalizeByteOrder32(tmp.s.lo);
tmp.s.hi = NormalizeByteOrder32(tmp.s.hi);
memcpy(ptr, &tmp.s.lo, sizeof tmp.s.lo);
memcpy(ptr + sizeof tmp.s.lo, &tmp.s.hi, sizeof tmp.s.hi);
ptr[0] = (*n >> 0) & 0xFF;
ptr[1] = (*n >> 8) & 0xFF;
ptr[2] = (*n >> 16) & 0xFF;
ptr[3] = (*n >> 24) & 0xFF;
ptr[4] = (*n >> 32) & 0xFF;
ptr[5] = (*n >> 40) & 0xFF;
ptr[6] = (*n >> 48) & 0xFF;
ptr[7] = (*n >> 56) & 0xFF;
} else {
const uint8_t *ptr = buf.read(sizeof tmp);
memcpy(&tmp.s.lo, ptr, sizeof tmp.s.lo);
memcpy(&tmp.s.hi, ptr + sizeof tmp.s.lo, sizeof tmp.s.hi);
tmp.s.lo = NormalizeByteOrder32(tmp.s.lo);
tmp.s.hi = NormalizeByteOrder32(tmp.s.hi);
*dp = tmp.d;
const uint8_t *ptr = buf.read(sizeof(*n));
*n = (uint64_t(ptr[0]) << 0) |
(uint64_t(ptr[1]) << 8) |
(uint64_t(ptr[2]) << 16) |
(uint64_t(ptr[3]) << 24) |
(uint64_t(ptr[4]) << 32) |
(uint64_t(ptr[5]) << 40) |
(uint64_t(ptr[6]) << 48) |
(uint64_t(ptr[7]) << 56);
}
return true;
}
bool codeDouble(double *dp) {
union DoublePun {
double d;
uint64_t u;
} pun;
if (mode == XDR_ENCODE)
pun.d = *dp;
if (!codeUint64(&pun.u))
return false;
if (mode == XDR_DECODE)
*dp = pun.d;
return true;
}
bool codeBytes(void *bytes, size_t len) {
if (mode == XDR_ENCODE) {
uint8_t *ptr = buf.write(len);