Local<v8::Value> v8::Date::New(double time) {
EnsureInitialized("v8::Date::New()");
LOG_API("Date::New");
+ if (isnan(time)) {
+ // Introduce only canonical NaN value into the VM, to avoid signaling NaNs.
+ time = i::OS::nan_value();
+ }
ENTER_V8;
EXCEPTION_PREAMBLE();
i::Handle<i::Object> obj =
Local<Number> v8::Number::New(double value) {
EnsureInitialized("v8::Number::New()");
+ if (isnan(value)) {
+ // Introduce only canonical NaN value into the VM, to avoid signaling NaNs.
+ value = i::OS::nan_value();
+ }
ENTER_V8;
i::Handle<i::Object> result = i::Factory::NewNumber(value);
return Utils::NumberToLocal(result);
#endif
+// Constants relevant to double precision floating point numbers.
+
+// Quiet NaNs have bits 51 to 62 set, possibly the sign bit, and no
+// other bits set.
+const uint64_t kQuietNaNMask = static_cast<uint64_t>(0xfff) << 51;
+// If looking only at the top 32 bits, the QNaN mask is bits 19 to 30.
+const uint32_t kQuietNaNHighBitsMask = 0xfff << (51 - 32);
+
+
// -----------------------------------------------------------------------------
// Forward declarations for frequently used classes
// (sorted alphabetically)
// not NaN.
// The representation of NaN values has all exponent bits (52..62) set,
// and not all mantissa bits (0..51) clear.
+ // We only accept QNaNs, which have bit 51 set.
// Read top bits of double representation (second word of value).
- __ mov(eax, FieldOperand(edx, HeapNumber::kExponentOffset));
- // Test that exponent bits are all set.
- __ not_(eax);
- __ test(eax, Immediate(0x7ff00000));
- __ j(not_zero, &return_equal);
- __ not_(eax);
-
- // Shift out flag and all exponent bits, retaining only mantissa.
- __ shl(eax, 12);
- // Or with all low-bits of mantissa.
- __ or_(eax, FieldOperand(edx, HeapNumber::kMantissaOffset));
- // Return zero equal if all bits in mantissa is zero (it's an Infinity)
- // and non-zero if not (it's a NaN).
+
+ // Value is a QNaN if value & kQuietNaNMask == kQuietNaNMask, i.e.,
+ // all bits in the mask are set. We only need to check the word
+ // that contains the exponent and high bit of the mantissa.
+ ASSERT_NE(0, (kQuietNaNHighBitsMask << 1) & 0x80000000u);
+ __ mov(edx, FieldOperand(edx, HeapNumber::kExponentOffset));
+ __ xor_(eax, Operand(eax));
+ // Shift value and mask so kQuietNaNHighBitsMask applies to topmost bits.
+ __ add(edx, Operand(edx));
+ __ cmp(edx, kQuietNaNHighBitsMask << 1);
+ __ setcc(above_equal, eax);
__ ret(0);
__ bind(¬_identical);
double OS::nan_value() {
#ifdef _MSC_VER
- static const __int64 nanval = 0xfff8000000000000;
+ // Positive Quiet NaN with no payload (aka. Indeterminate) has all bits
+ // in mask set, so value equals mask.
+ static const __int64 nanval = kQuietNaNMask;
return *reinterpret_cast<const double*>(&nanval);
#else // _MSC_VER
return NAN;
// not NaN.
// The representation of NaN values has all exponent bits (52..62) set,
// and not all mantissa bits (0..51) clear.
- // Read double representation into rax.
- __ movq(rbx, V8_UINT64_C(0x7ff0000000000000), RelocInfo::NONE);
- __ movq(rax, FieldOperand(rdx, HeapNumber::kValueOffset));
- // Test that exponent bits are all set.
- __ or_(rbx, rax);
- __ cmpq(rbx, rax);
- __ j(not_equal, &return_equal);
- // Shift out flag and all exponent bits, retaining only mantissa.
- __ shl(rax, Immediate(12));
- // If all bits in the mantissa are zero the number is Infinity, and
- // we return zero. Otherwise it is a NaN, and we return non-zero.
- // We cannot just return rax because only eax is tested on return.
- __ setcc(not_zero, rax);
+ // We only allow QNaNs, which have bit 51 set (which also rules out
+ // the value being Infinity).
+
+ // Value is a QNaN if value & kQuietNaNMask == kQuietNaNMask, i.e.,
+ // all bits in the mask are set. We only need to check the word
+ // that contains the exponent and high bit of the mantissa.
+ ASSERT_NE(0, (kQuietNaNHighBitsMask << 1) & 0x80000000u);
+ __ movl(rdx, FieldOperand(rdx, HeapNumber::kExponentOffset));
+ __ xorl(rax, rax);
+ __ addl(rdx, rdx); // Shift value and mask so mask applies to top bits.
+ __ cmpl(rdx, Immediate(kQuietNaNHighBitsMask << 1));
+ __ setcc(above_equal, rax);
__ ret(0);
__ bind(¬_identical);
CHECK_NE(heap_statistics.total_heap_size(), 0);
CHECK_NE(heap_statistics.used_heap_size(), 0);
}
+
+
+static double DoubleFromBits(uint64_t value) {
+ double target;
+ memcpy(&target, &value, sizeof(target));
+ return target;
+}
+
+
+static uint64_t DoubleToBits(double value) {
+ uint64_t target;
+ memcpy(&target, &value, sizeof(target));
+ return target;
+}
+
+
+static double DoubleToDateTime(double input) {
+ double date_limit = 864e13;
+ if (IsNaN(input) || input < -date_limit || input > date_limit) {
+ return i::OS::nan_value();
+ }
+ return (input < 0) ? -(floor(-input)) : floor(input);
+}
+
+// We don't have a consistent way to write 64-bit constants syntactically, so we
+// split them into two 32-bit constants and combine them programmatically.
+static double DoubleFromBits(uint32_t high_bits, uint32_t low_bits) {
+ return DoubleFromBits((static_cast<uint64_t>(high_bits) << 32) | low_bits);
+}
+
+
+THREADED_TEST(QuietSignalingNaNs) {
+ v8::HandleScope scope;
+ LocalContext context;
+ v8::TryCatch try_catch;
+
+ // Special double values.
+ double snan = DoubleFromBits(0x7ff00000, 0x00000001);
+ double qnan = DoubleFromBits(0x7ff80000, 0x00000000);
+ double infinity = DoubleFromBits(0x7ff00000, 0x00000000);
+ double max_normal = DoubleFromBits(0x7fefffff, 0xffffffffu);
+ double min_normal = DoubleFromBits(0x00100000, 0x00000000);
+ double max_denormal = DoubleFromBits(0x000fffff, 0xffffffffu);
+ double min_denormal = DoubleFromBits(0x00000000, 0x00000001);
+
+ // Date values are capped at +/-100000000 days (times 864e5 ms per day)
+ // on either side of the epoch.
+ double date_limit = 864e13;
+
+ double test_values[] = {
+ snan,
+ qnan,
+ infinity,
+ max_normal,
+ date_limit + 1,
+ date_limit,
+ min_normal,
+ max_denormal,
+ min_denormal,
+ 0,
+ -0,
+ -min_denormal,
+ -max_denormal,
+ -min_normal,
+ -date_limit,
+ -date_limit - 1,
+ -max_normal,
+ -infinity,
+ -qnan,
+ -snan
+ };
+ int num_test_values = 20;
+
+ for (int i = 0; i < num_test_values; i++) {
+ double test_value = test_values[i];
+
+ // Check that Number::New preserves non-NaNs and quiets SNaNs.
+ v8::Handle<v8::Value> number = v8::Number::New(test_value);
+ double stored_number = number->NumberValue();
+ if (!IsNaN(test_value)) {
+ CHECK_EQ(test_value, stored_number);
+ } else {
+ uint64_t stored_bits = DoubleToBits(stored_number);
+ // Check if quiet nan (bits 51..62 all set).
+ CHECK_EQ(0xfff, (stored_bits >> 51) & 0xfff);
+ }
+
+ // Check that Date::New preserves non-NaNs in the date range and
+ // quiets SNaNs.
+ v8::Handle<v8::Value> date = v8::Date::New(test_value);
+ double expected_stored_date = DoubleToDateTime(test_value);
+ double stored_date = date->NumberValue();
+ if (!IsNaN(expected_stored_date)) {
+ CHECK_EQ(expected_stored_date, stored_date);
+ } else {
+ uint64_t stored_bits = DoubleToBits(stored_date);
+ // Check if quiet nan (bits 51..62 all set).
+ CHECK_EQ(0xfff, (stored_bits >> 51) & 0xfff);
+ }
+ }
+}
using v8::internal::r8;
using v8::internal::r9;
using v8::internal::r11;
-using v8::internal::r12;
+using v8::internal::r12; // Remember: r12..r15 are callee save!
using v8::internal::r13;
using v8::internal::r14;
using v8::internal::r15;
masm->set_allow_stub_calls(false);
Label exit;
+ __ push(r12);
+ __ push(r15);
TestSmiDiv(masm, &exit, 0x10, 1, 1);
TestSmiDiv(masm, &exit, 0x20, 1, 0);
TestSmiDiv(masm, &exit, 0x30, -1, 0);
__ xor_(r15, r15); // Success.
__ bind(&exit);
__ movq(rax, r15);
+ __ pop(r15);
+ __ pop(r12);
__ ret(0);
CodeDesc desc;
masm->set_allow_stub_calls(false);
Label exit;
+ __ push(r12);
+ __ push(r15);
TestSmiMod(masm, &exit, 0x10, 1, 1);
TestSmiMod(masm, &exit, 0x20, 1, 0);
TestSmiMod(masm, &exit, 0x30, -1, 0);
__ xor_(r15, r15); // Success.
__ bind(&exit);
__ movq(rax, r15);
+ __ pop(r15);
+ __ pop(r12);
__ ret(0);
CodeDesc desc;