Change NaN-test to only check for QNaNs, and API to only introduce QNaNs.
authorlrn@chromium.org <lrn@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 27 Oct 2009 08:13:59 +0000 (08:13 +0000)
committerlrn@chromium.org <lrn@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 27 Oct 2009 08:13:59 +0000 (08:13 +0000)
Review URL: http://codereview.chromium.org/339002

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@3135 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

src/api.cc
src/globals.h
src/ia32/codegen-ia32.cc
src/platform-win32.cc
src/x64/codegen-x64.cc
test/cctest/test-api.cc
test/cctest/test-macro-assembler-x64.cc

index c3c6690..4cb655d 100644 (file)
@@ -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);
index efe0127..6d8bb63 100644 (file)
@@ -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)
index a339e90..05cad89 100644 (file)
@@ -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);
index 3088baa..d1f5319 100644 (file)
@@ -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;
index 0029b74..3970744 100644 (file)
@@ -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);
index a943f30..7ec5e64 100644 (file)
@@ -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);
+    }
+  }
+}
index 9c1197f..f344ac8 100755 (executable)
@@ -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;