Commit b92a0594 authored by lrn@chromium.org's avatar lrn@chromium.org

Change NaN-test to only check for QNaNs, and API to only introduce QNaNs.

Review URL: http://codereview.chromium.org/339002


git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@3135 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent e9909cbf
......@@ -3183,6 +3183,10 @@ Local<v8::Object> v8::Object::New() {
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 =
......@@ -3255,6 +3259,10 @@ Local<String> v8::String::NewSymbol(const char* data, int length) {
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);
......
......@@ -170,6 +170,15 @@ const Address kFromSpaceZapValue = reinterpret_cast<Address>(0xbeefdad);
#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)
......
......@@ -7400,20 +7400,19 @@ void CompareStub::Generate(MacroAssembler* masm) {
// 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(&not_identical);
......
......@@ -1318,7 +1318,9 @@ int OS::StackWalk(Vector<OS::StackFrame> frames) { return 0; }
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;
......
......@@ -6379,19 +6379,18 @@ void CompareStub::Generate(MacroAssembler* masm) {
// 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(&not_identical);
......
......@@ -8495,3 +8495,104 @@ THREADED_TEST(GetHeapStatistics) {
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);
}
}
}
......@@ -57,7 +57,7 @@ using v8::internal::rsp;
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;
......@@ -1144,6 +1144,8 @@ TEST(SmiDiv) {
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);
......@@ -1168,6 +1170,8 @@ TEST(SmiDiv) {
__ xor_(r15, r15); // Success.
__ bind(&exit);
__ movq(rax, r15);
__ pop(r15);
__ pop(r12);
__ ret(0);
CodeDesc desc;
......@@ -1247,6 +1251,8 @@ TEST(SmiMod) {
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);
......@@ -1271,6 +1277,8 @@ TEST(SmiMod) {
__ xor_(r15, r15); // Success.
__ bind(&exit);
__ movq(rax, r15);
__ pop(r15);
__ pop(r12);
__ ret(0);
CodeDesc desc;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment