Bug 918350 - SpiderMonkey: Remove js_NaN in favor of a new mozilla::GenericNaN() function. r=waldo
authorDan Gohman <sunfish@google.com>
Thu, 19 Sep 2013 18:42:56 -0700
changeset 148030 9e727132b19cf1bb91c8f41423a12b971d5242e5
parent 148029 77a16602b99c4f31bd5cb79c8e3abf2555442739
child 148031 141c61d174ee681c8b93541f2ce8a6efa4607ee6
push id25321
push useremorley@mozilla.com
push dateFri, 20 Sep 2013 09:19:10 +0000
treeherdermozilla-central@d923570ed720 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerswaldo
bugs918350
milestone27.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 918350 - SpiderMonkey: Remove js_NaN in favor of a new mozilla::GenericNaN() function. r=waldo
js/public/Value.h
js/src/builtin/MapObject.cpp
js/src/frontend/FoldConstants.cpp
js/src/jit/AsmJS.cpp
js/src/jit/AsmJSSignalHandlers.cpp
js/src/jit/CodeGenerator.cpp
js/src/jit/IonMacroAssembler.cpp
js/src/jit/IonMacroAssembler.h
js/src/jit/Lowering.cpp
js/src/jit/TypePolicy.cpp
js/src/jit/arm/CodeGenerator-arm.cpp
js/src/jit/x86/CodeGenerator-x86.cpp
js/src/jsdate.cpp
js/src/jslibmath.h
js/src/jsmath.cpp
js/src/jsnum.cpp
js/src/jsstr.cpp
js/src/vm/DateTime.h
js/src/vm/NumericConversions.h
js/src/vm/ObjectImpl.cpp
js/src/vm/TypedArrayObject.cpp
mfbt/tests/TestFloatingPoint.cpp
--- a/js/public/Value.h
+++ b/js/public/Value.h
@@ -854,16 +854,28 @@ JS_CANONICALIZE_NAN(double d)
 
 static inline jsval_layout JSVAL_TO_IMPL(JS::Value v);
 static inline JS_VALUE_CONSTEXPR JS::Value IMPL_TO_JSVAL(jsval_layout l);
 
 namespace JS {
 
 static inline JS_VALUE_CONSTEXPR JS::Value UndefinedValue();
 
+/**
+ * Returns a generic quiet NaN value, with all payload bits set to zero.
+ *
+ * Among other properties, this NaN's bit pattern conforms to JS::Value's
+ * bit pattern restrictions.
+ */
+static MOZ_ALWAYS_INLINE double
+GenericNaN()
+{
+  return mozilla::SpecificNaN(0, 0x8000000000000ULL);
+}
+
 /*
  * JS::Value is the interface for a single JavaScript Engine value.  A few
  * general notes on JS::Value:
  *
  * - JS::Value has setX() and isX() members for X in
  *
  *     { Int32, Double, String, Boolean, Undefined, Null, Object, Magic }
  *
@@ -924,16 +936,20 @@ class Value
         MOZ_ASSERT(isInt32());
         return data.s.payload.i32;
     }
 
     void setDouble(double d) {
         data = DOUBLE_TO_JSVAL_IMPL(d);
     }
 
+    void setNaN() {
+        setDouble(GenericNaN());
+    }
+
     double &getDoubleRef() {
         MOZ_ASSERT(isDouble());
         return data.asDouble;
     }
 
     void setString(JSString *str) {
         MOZ_ASSERT(!IsPoisonedPtr(str));
         data = STRING_TO_JSVAL_IMPL(str);
@@ -1269,16 +1285,24 @@ static inline Value
 DoubleValue(double dbl)
 {
     Value v;
     v.setDouble(dbl);
     return v;
 }
 
 static inline Value
+DoubleNaNValue()
+{
+    Value v;
+    v.setNaN();
+    return v;
+}
+
+static inline Value
 Float32Value(float f)
 {
     Value v;
     v.setDouble(f);
     return v;
 }
 
 static inline Value
@@ -1546,16 +1570,17 @@ class UnbarrieredMutableValueOperations 
     friend class MutableValueOperations<Outer>;
     JS::Value * value() { return static_cast<Outer*>(this)->extractMutable(); }
 
   public:
     void setNull() { value()->setNull(); }
     void setUndefined() { value()->setUndefined(); }
     void setInt32(int32_t i) { value()->setInt32(i); }
     void setDouble(double d) { value()->setDouble(d); }
+    void setNaN() { setDouble(JS::GenericNaN()); }
     void setBoolean(bool b) { value()->setBoolean(b); }
     void setMagic(JSWhyMagic why) { value()->setMagic(why); }
     bool setNumber(uint32_t ui) { return value()->setNumber(ui); }
     bool setNumber(double d) { return value()->setNumber(d); }
 };
 
 /*
  * A class designed for CRTP use in implementing all the mutating parts of the
--- a/js/src/builtin/MapObject.cpp
+++ b/js/src/builtin/MapObject.cpp
@@ -20,16 +20,17 @@
 #include "jsobjinlines.h"
 
 using namespace js;
 
 using mozilla::DoubleIsInt32;
 using mozilla::IsNaN;
 using mozilla::OldMove;
 using mozilla::MoveRef;
+using JS::DoubleNaNValue;
 
 
 /*** OrderedHashTable ****************************************************************************/
 
 /*
  * Define two collection templates, js::OrderedHashMap and js::OrderedHashSet.
  * They are like js::HashMap and js::HashSet except that:
  *
@@ -797,17 +798,17 @@ HashableValue::setValue(JSContext *cx, H
     } else if (v.isDouble()) {
         double d = v.toDouble();
         int32_t i;
         if (DoubleIsInt32(d, &i)) {
             // Normalize int32_t-valued doubles to int32_t for faster hashing and testing.
             value = Int32Value(i);
         } else if (IsNaN(d)) {
             // NaNs with different bits must hash and test identically.
-            value = DoubleValue(js_NaN);
+            value = DoubleNaNValue();
         } else {
             value = v;
         }
     } else {
         value = v;
     }
 
     JS_ASSERT(value.isUndefined() || value.isNull() || value.isBoolean() ||
--- a/js/src/frontend/FoldConstants.cpp
+++ b/js/src/frontend/FoldConstants.cpp
@@ -144,33 +144,33 @@ FoldBinaryNumeric(ExclusiveContext *cx, 
         d *= d2;
         break;
 
       case JSOP_DIV:
         if (d2 == 0) {
 #if defined(XP_WIN)
             /* XXX MSVC miscompiles such that (NaN == 0) */
             if (IsNaN(d2))
-                d = js_NaN;
+                d = GenericNaN();
             else
 #endif
             if (d == 0 || IsNaN(d))
-                d = js_NaN;
+                d = JS::GenericNaN();
             else if (IsNegative(d) != IsNegative(d2))
                 d = NegativeInfinity();
             else
                 d = PositiveInfinity();
         } else {
             d /= d2;
         }
         break;
 
       case JSOP_MOD:
         if (d2 == 0) {
-            d = js_NaN;
+            d = JS::GenericNaN();
         } else {
             d = js_fmod(d, d2);
         }
         break;
 
       default:;
     }
 
--- a/js/src/jit/AsmJS.cpp
+++ b/js/src/jit/AsmJS.cpp
@@ -42,16 +42,17 @@ using mozilla::CountLeadingZeroes32;
 using mozilla::DebugOnly;
 using mozilla::HashGeneric;
 using mozilla::IsNaN;
 using mozilla::IsNegativeZero;
 using mozilla::Maybe;
 using mozilla::OldMove;
 using mozilla::MoveRef;
 using mozilla::PositiveInfinity;
+using JS::GenericNaN;
 
 static const size_t LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 1 << 12;
 
 /*****************************************************************************/
 // ParseNode utilities
 
 static inline ParseNode *
 NextNode(ParseNode *pn)
@@ -2957,17 +2958,17 @@ CheckGlobalDotImport(ModuleCompiler &m, 
         if (!m.lookupStandardLibraryMathName(field, &mathBuiltin))
             return m.failName(initNode, "'%s' is not a standard Math builtin", field);
 
         return m.addMathBuiltin(varName, mathBuiltin, field);
     }
 
     if (IsUseOfName(base, m.module().globalArgumentName())) {
         if (field == m.cx()->names().NaN)
-            return m.addGlobalConstant(varName, js_NaN, field);
+            return m.addGlobalConstant(varName, GenericNaN(), field);
         if (field == m.cx()->names().Infinity)
             return m.addGlobalConstant(varName, PositiveInfinity(), field);
         return m.failName(initNode, "'%s' is not a standard global constant", field);
     }
 
     if (IsUseOfName(base, m.module().importArgumentName()))
         return m.addFFI(varName, field);
 
@@ -5512,17 +5513,17 @@ GenerateEntry(ModuleCompiler &m, const A
     LoadAsmJSActivationIntoRegister(masm, activation);
     masm.storePtr(StackPointer, Address(activation, AsmJSActivation::offsetOfErrorRejoinSP()));
 
     // ARM has a globally-pinned GlobalReg (x64 uses RIP-relative addressing,
     // x86 uses immediates in effective addresses) and NaN register (used as
     // part of the out-of-bounds handling in heap loads/stores).
 #if defined(JS_CPU_ARM)
     masm.movePtr(IntArgReg1, GlobalReg);
-    masm.ma_vimm(js_NaN, NANReg);
+    masm.ma_vimm(GenericNaN(), NANReg);
 #endif
 
     // ARM and x64 have a globally-pinned HeapReg (x86 uses immediates in
     // effective addresses).
 #if defined(JS_CPU_X64) || defined(JS_CPU_ARM)
     masm.loadPtr(Address(IntArgReg1, m.module().heapOffset()), HeapReg);
 #endif
 
--- a/js/src/jit/AsmJSSignalHandlers.cpp
+++ b/js/src/jit/AsmJSSignalHandlers.cpp
@@ -8,16 +8,18 @@
 
 #include "assembler/assembler/MacroAssembler.h"
 #include "jit/AsmJSModule.h"
 
 using namespace js;
 using namespace js::jit;
 using namespace mozilla;
 
+using JS::GenericNaN;
+
 #if defined(XP_WIN)
 # define XMM_sig(p,i) ((p)->Xmm##i)
 # define EIP_sig(p) ((p)->Eip)
 # define RIP_sig(p) ((p)->Rip)
 # define RAX_sig(p) ((p)->Rax)
 # define RCX_sig(p) ((p)->Rcx)
 # define RDX_sig(p) ((p)->Rdx)
 # define RBX_sig(p) ((p)->Rbx)
@@ -245,24 +247,24 @@ bool InstallSignalHandlersMutex::Lock::s
 #if defined(JS_CPU_X64)
 template <class T>
 static void
 SetXMMRegToNaN(bool isFloat32, T *xmm_reg)
 {
     if (isFloat32) {
         JS_STATIC_ASSERT(sizeof(T) == 4 * sizeof(float));
         float *floats = reinterpret_cast<float*>(xmm_reg);
-        floats[0] = js_NaN;
+        floats[0] = GenericNaN();
         floats[1] = 0;
         floats[2] = 0;
         floats[3] = 0;
     } else {
         JS_STATIC_ASSERT(sizeof(T) == 2 * sizeof(double));
         double *dbls = reinterpret_cast<double*>(xmm_reg);
-        dbls[0] = js_NaN;
+        dbls[0] = GenericNaN();
         dbls[1] = 0;
     }
 }
 
 // Perform a binary search on the projected offsets of the known heap accesses
 // in the module.
 static const AsmJSHeapAccess *
 LookupHeapAccess(const AsmJSModule &module, uint8_t *pc)
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -34,16 +34,17 @@
 #include "jit/shared/CodeGenerator-shared-inl.h"
 #include "vm/Interpreter-inl.h"
 
 using namespace js;
 using namespace js::jit;
 
 using mozilla::DebugOnly;
 using mozilla::Maybe;
+using JS::GenericNaN;
 
 namespace js {
 namespace jit {
 
 // This out-of-line cache is used to do a double dispatch including it-self and
 // the wrapped IonCache.
 class OutOfLineUpdateCache :
   public OutOfLineCodeBase<CodeGenerator>,
@@ -235,17 +236,17 @@ CodeGenerator::visitValueToDouble(LValue
     if (hasNull) {
         masm.bind(&isNull);
         masm.loadConstantDouble(DoubleZero, output);
         masm.jump(&done);
     }
 
     if (hasUndefined) {
         masm.bind(&isUndefined);
-        masm.loadConstantDouble(js_NaN, output);
+        masm.loadConstantDouble(GenericNaN(), output);
         masm.jump(&done);
     }
 
     if (hasBoolean) {
         masm.bind(&isBool);
         masm.boolValueToDouble(operand, output);
         masm.jump(&done);
     }
@@ -293,17 +294,17 @@ CodeGenerator::visitValueToFloat32(LValu
     if (hasNull) {
         masm.bind(&isNull);
         masm.loadConstantFloat32((float)DoubleZero, output);
         masm.jump(&done);
     }
 
     if (hasUndefined) {
         masm.bind(&isUndefined);
-        masm.loadConstantFloat32((float)js_NaN, output);
+        masm.loadConstantFloat32(float(GenericNaN()), output);
         masm.jump(&done);
     }
 
     if (hasBoolean) {
         masm.bind(&isBool);
         masm.boolValueToFloat32(operand, output);
         masm.jump(&done);
     }
--- a/js/src/jit/IonMacroAssembler.cpp
+++ b/js/src/jit/IonMacroAssembler.cpp
@@ -20,16 +20,18 @@
 #include "vm/ForkJoin.h"
 
 #include "jsgcinlines.h"
 #include "jsinferinlines.h"
 
 using namespace js;
 using namespace js::jit;
 
+using JS::GenericNaN;
+
 namespace {
 
 // Emulate a TypeSet logic from a Type object to avoid duplicating the guard
 // logic.
 class TypeWrapper {
     types::Type t_;
 
   public:
@@ -1436,34 +1438,33 @@ MacroAssembler::convertInt32ValueToDoubl
     convertInt32ToDouble(scratch, ScratchFloatReg);
     storeDouble(ScratchFloatReg, address);
 }
 
 static const double DoubleZero = 0.0;
 static const double DoubleOne  = 1.0;
 static const float FloatZero = 0.0;
 static const float FloatOne  = 1.0;
-static const float FloatNaN = js_NaN;
 
 void
 MacroAssembler::convertValueToFloatingPoint(ValueOperand value, FloatRegister output,
                                             Label *fail, MIRType outputType)
 {
     Register tag = splitTagForTest(value);
 
     Label isDouble, isInt32, isBool, isNull, done;
 
     branchTestDouble(Assembler::Equal, tag, &isDouble);
     branchTestInt32(Assembler::Equal, tag, &isInt32);
     branchTestBoolean(Assembler::Equal, tag, &isBool);
     branchTestNull(Assembler::Equal, tag, &isNull);
     branchTestUndefined(Assembler::NotEqual, tag, fail);
 
     // fall-through: undefined
-    loadConstantFloatingPoint(js_NaN, FloatNaN, output, outputType);
+    loadConstantFloatingPoint(GenericNaN(), float(GenericNaN()), output, outputType);
     jump(&done);
 
     bind(&isNull);
     loadConstantFloatingPoint(DoubleZero, FloatZero, output, outputType);
     jump(&done);
 
     bind(&isBool);
     boolValueToFloatingPoint(value, output, outputType);
@@ -1504,17 +1505,17 @@ MacroAssembler::convertValueToFloatingPo
     }
 
     if (v.isNull()) {
         loadConstantFloatingPoint(DoubleZero, FloatZero, output, outputType);
         return true;
     }
 
     if (v.isUndefined()) {
-        loadConstantFloatingPoint(js_NaN, FloatNaN, output, outputType);
+        loadConstantFloatingPoint(GenericNaN(), float(GenericNaN()), output, outputType);
         return true;
     }
 
     JS_ASSERT(v.isObject());
     jump(fail);
     return true;
 }
 
@@ -1605,17 +1606,17 @@ MacroAssembler::convertTypedOrValueToFlo
             convertDoubleToFloat(src.typedReg().fpu(), output);
         }
         break;
       case MIRType_Object:
       case MIRType_String:
         jump(fail);
         break;
       case MIRType_Undefined:
-        loadConstantFloatingPoint(js_NaN, FloatNaN, output, outputType);
+        loadConstantFloatingPoint(GenericNaN(), float(GenericNaN()), output, outputType);
         break;
       default:
         MOZ_ASSUME_UNREACHABLE("Bad MIRType");
     }
 }
 
 void
 MacroAssembler::convertDoubleToInt(FloatRegister src, Register output, FloatRegister temp,
--- a/js/src/jit/IonMacroAssembler.h
+++ b/js/src/jit/IonMacroAssembler.h
@@ -664,24 +664,24 @@ class MacroAssembler : public MacroAssem
 
     void branchNurseryPtr(Condition cond, const Address &ptr1, const ImmMaybeNurseryPtr &ptr2,
                           Label *label);
     void moveNurseryPtr(const ImmMaybeNurseryPtr &ptr, const Register &reg);
 
     void canonicalizeDouble(FloatRegister reg) {
         Label notNaN;
         branchDouble(DoubleOrdered, reg, reg, &notNaN);
-        loadConstantDouble(js_NaN, reg);
+        loadConstantDouble(JS::GenericNaN(), reg);
         bind(&notNaN);
     }
 
     void canonicalizeFloat(FloatRegister reg) {
         Label notNaN;
         branchFloat(DoubleOrdered, reg, reg, &notNaN);
-        loadConstantFloat32((float)js_NaN, reg);
+        loadConstantFloat32(float(JS::GenericNaN()), reg);
         bind(&notNaN);
     }
 
     template<typename T>
     void loadFromTypedArray(int arrayType, const T &src, AnyRegister dest, Register temp, Label *fail);
 
     template<typename T>
     void loadFromTypedArray(int arrayType, const T &src, const ValueOperand &dest, bool allowDouble,
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -18,16 +18,18 @@
 
 #include "jsinferinlines.h"
 
 #include "jit/shared/Lowering-shared-inl.h"
 
 using namespace js;
 using namespace jit;
 
+using JS::GenericNaN;
+
 bool
 LIRGenerator::visitParameter(MParameter *param)
 {
     ptrdiff_t offset;
     if (param->index() == MParameter::THIS_SLOT)
         offset = THIS_FRAME_SLOT;
     else
         offset = 1 + param->index();
@@ -1549,17 +1551,17 @@ LIRGenerator::visitToDouble(MToDouble *c
       }
 
       case MIRType_Null:
         JS_ASSERT(conversion != MToDouble::NumbersOnly && conversion != MToDouble::NonNullNonStringPrimitives);
         return lowerConstantDouble(0, convert);
 
       case MIRType_Undefined:
         JS_ASSERT(conversion != MToDouble::NumbersOnly);
-        return lowerConstantDouble(js_NaN, convert);
+        return lowerConstantDouble(GenericNaN(), convert);
 
       case MIRType_Boolean:
         JS_ASSERT(conversion != MToDouble::NumbersOnly);
         /* FALLTHROUGH */
 
       case MIRType_Int32:
       {
         LInt32ToDouble *lir = new LInt32ToDouble(useRegister(opd));
@@ -1598,17 +1600,17 @@ LIRGenerator::visitToFloat32(MToFloat32 
       }
 
       case MIRType_Null:
         JS_ASSERT(conversion != MToFloat32::NonStringPrimitives);
         return lowerConstantFloat32(0, convert);
 
       case MIRType_Undefined:
         JS_ASSERT(conversion != MToFloat32::NumbersOnly);
-        return lowerConstantFloat32(js_NaN, convert);
+        return lowerConstantFloat32(GenericNaN(), convert);
 
       case MIRType_Boolean:
         JS_ASSERT(conversion != MToFloat32::NumbersOnly);
         /* FALLTHROUGH */
 
       case MIRType_Int32:
       {
         LInt32ToFloat32 *lir = new LInt32ToFloat32(useRegister(opd));
--- a/js/src/jit/TypePolicy.cpp
+++ b/js/src/jit/TypePolicy.cpp
@@ -8,16 +8,18 @@
 
 #include "jit/Lowering.h"
 #include "jit/MIR.h"
 #include "jit/MIRGraph.h"
 
 using namespace js;
 using namespace js::jit;
 
+using JS::DoubleNaNValue;
+
 MDefinition *
 BoxInputsPolicy::boxAt(MInstruction *at, MDefinition *operand)
 {
     if (operand->isUnbox())
         return operand->toUnbox()->input();
     return alwaysBoxAt(at, operand);
 }
 
@@ -616,17 +618,17 @@ StoreTypedArrayPolicy::adjustValueInput(
       case MIRType_Null:
         value->setFoldedUnchecked();
         value = MConstant::New(Int32Value(0));
         ins->block()->insertBefore(ins, value->toInstruction());
         break;
       case MIRType_Object:
       case MIRType_Undefined:
         value->setFoldedUnchecked();
-        value = MConstant::New(DoubleValue(js_NaN));
+        value = MConstant::New(DoubleNaNValue());
         ins->block()->insertBefore(ins, value->toInstruction());
         break;
       case MIRType_String:
         value = boxAt(ins, value);
         break;
       default:
         MOZ_ASSUME_UNREACHABLE("Unexpected type");
     }
--- a/js/src/jit/arm/CodeGenerator-arm.cpp
+++ b/js/src/jit/arm/CodeGenerator-arm.cpp
@@ -314,17 +314,17 @@ CodeGeneratorARM::visitMinMaxD(LMinMaxD 
     } else {
         masm.ma_vneg(first, first);
         masm.ma_vsub(first, second, first);
         masm.ma_vneg(first, first);
     }
     masm.ma_b(&done);
 
     masm.bind(&nan);
-    masm.loadConstantDouble(js_NaN, output);
+    masm.loadConstantDouble(GenericNaN(), output);
     masm.ma_b(&done);
 
     masm.bind(&returnSecond);
     masm.ma_vmov(second, output);
 
     masm.bind(&done);
     return true;
 }
--- a/js/src/jit/x86/CodeGenerator-x86.cpp
+++ b/js/src/jit/x86/CodeGenerator-x86.cpp
@@ -529,17 +529,17 @@ CodeGeneratorX86::visitAsmJSLoadHeap(LAs
     masm.bind(ool->rejoin());
     return gen->noteHeapAccess(AsmJSHeapAccess(before, after, vt, ToAnyRegister(out), cmp.offset()));
 }
 
 bool
 CodeGeneratorX86::visitOutOfLineLoadTypedArrayOutOfBounds(OutOfLineLoadTypedArrayOutOfBounds *ool)
 {
     if (ool->dest().isFloat()) {
-        masm.loadConstantDouble(js_NaN, ool->dest().fpu());
+        masm.loadConstantDouble(GenericNaN(), ool->dest().fpu());
     } else {
         Register destReg = ool->dest().gpr();
         masm.xorl(destReg, destReg);
     }
     masm.jmp(ool->rejoin());
     return true;
 }
 
--- a/js/src/jsdate.cpp
+++ b/js/src/jsdate.cpp
@@ -45,16 +45,17 @@
 #include "jsobjinlines.h"
 
 using namespace js;
 using namespace js::types;
 
 using mozilla::ArrayLength;
 using mozilla::IsFinite;
 using mozilla::IsNaN;
+using JS::GenericNaN;
 
 /*
  * The JS 'Date' object is patterned after the Java 'Date' object.
  * Here is a script:
  *
  *    today = new Date();
  *
  *    print(today.toLocaleString());
@@ -121,17 +122,17 @@ IsLeapYear(double year)
     JS_ASSERT(ToInteger(year) == year);
     return fmod(year, 4) == 0 && (fmod(year, 100) != 0 || fmod(year, 400) == 0);
 }
 
 inline double
 DaysInYear(double year)
 {
     if (!IsFinite(year))
-        return js_NaN;
+        return GenericNaN();
     return IsLeapYear(year) ? 366 : 365;
 }
 
 inline double
 DayFromYear(double y)
 {
     return 365 * (y - 1970) +
            floor((y - 1969) / 4.0) -
@@ -144,17 +145,17 @@ TimeFromYear(double y)
 {
     return DayFromYear(y) * msPerDay;
 }
 
 static double
 YearFromTime(double t)
 {
     if (!IsFinite(t))
-        return js_NaN;
+        return GenericNaN();
 
     JS_ASSERT(ToInteger(t) == t);
 
     double y = floor(t / (msPerDay * 365.2425)) + 1970;
     double t2 = TimeFromYear(y);
 
     /*
      * Adjust the year if the approximation was wrong.  Since the year was
@@ -183,17 +184,17 @@ DayWithinYear(double t, double year)
     JS_ASSERT_IF(IsFinite(t), YearFromTime(t) == year);
     return Day(t) - DayFromYear(year);
 }
 
 static double
 MonthFromTime(double t)
 {
     if (!IsFinite(t))
-        return js_NaN;
+        return GenericNaN();
 
     double year = YearFromTime(t);
     double d = DayWithinYear(t, year);
 
     int step;
     if (d < (step = 31))
         return 0;
     if (d < (step += DaysInFebruary(year)))
@@ -219,17 +220,17 @@ MonthFromTime(double t)
     return 11;
 }
 
 /* ES5 15.9.1.5. */
 static double
 DateFromTime(double t)
 {
     if (!IsFinite(t))
-        return js_NaN;
+        return GenericNaN();
 
     double year = YearFromTime(t);
     double d = DayWithinYear(t, year);
 
     int next;
     if (d <= (next = 30))
         return d + 1;
     int step = next;
@@ -302,17 +303,17 @@ inline int
 DayFromMonth(T month, bool isLeapYear) MOZ_DELETE;
 
 /* ES5 15.9.1.12 (out of order to accommodate DaylightSavingTA). */
 static double
 MakeDay(double year, double month, double date)
 {
     /* Step 1. */
     if (!IsFinite(year) || !IsFinite(month) || !IsFinite(date))
-        return js_NaN;
+        return GenericNaN();
 
     /* Steps 2-4. */
     double y = ToInteger(year);
     double m = ToInteger(month);
     double dt = ToInteger(date);
 
     /* Step 5. */
     double ym = y + floor(m / 12);
@@ -332,17 +333,17 @@ MakeDay(double year, double month, doubl
 }
 
 /* ES5 15.9.1.13 (out of order to accommodate DaylightSavingTA). */
 inline double
 MakeDate(double day, double time)
 {
     /* Step 1. */
     if (!IsFinite(day) || !IsFinite(time))
-        return js_NaN;
+        return GenericNaN();
 
     /* Step 2. */
     return day * msPerDay + time;
 }
 
 JS_PUBLIC_API(double)
 JS::MakeDate(double year, unsigned month, unsigned day)
 {
@@ -398,17 +399,17 @@ EquivalentYearForDST(int year)
     return yearStartingWith[IsLeapYear(year)][day];
 }
 
 /* ES5 15.9.1.8. */
 static double
 DaylightSavingTA(double t, DateTimeInfo *dtInfo)
 {
     if (!IsFinite(t))
-        return js_NaN;
+        return GenericNaN();
 
     /*
      * If earlier than 1970 or after 2038, potentially beyond the ken of
      * many OSes, map it to an equivalent year before asking.
      */
     if (t < 0.0 || t > 2145916800000.0) {
         int year = EquivalentYearForDST(int(YearFromTime(t)));
         double day = MakeDay(year, MonthFromTime(t), DateFromTime(t));
@@ -483,17 +484,17 @@ static double
 MakeTime(double hour, double min, double sec, double ms)
 {
     /* Step 1. */
     if (!IsFinite(hour) ||
         !IsFinite(min) ||
         !IsFinite(sec) ||
         !IsFinite(ms))
     {
-        return js_NaN;
+        return GenericNaN();
     }
 
     /* Step 2. */
     double h = ToInteger(hour);
 
     /* Step 3. */
     double m = ToInteger(min);
 
@@ -612,17 +613,17 @@ date_msecFromArgs(JSContext *cx, CallArg
 
     for (loop = 0; loop < MAXARGS; loop++) {
         if (loop < args.length()) {
             double d;
             if (!ToNumber(cx, args[loop], &d))
                 return false;
             /* return NaN if any arg is not finite */
             if (!IsFinite(d)) {
-                *rval = js_NaN;
+                *rval = GenericNaN();
                 return true;
             }
             array[loop] = ToInteger(d);
         } else {
             if (loop == 2) {
                 array[loop] = 1; /* Default the date argument to 1. */
             } else {
                 array[loop] = 0;
@@ -1196,31 +1197,31 @@ syntax:
     return false;
 }
 
 static bool
 date_parse(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     if (args.length() == 0) {
-        vp->setDouble(js_NaN);
+        vp->setNaN();
         return true;
     }
 
     JSString *str = ToString<CanGC>(cx, args[0]);
     if (!str)
         return false;
 
     JSLinearString *linearStr = str->ensureLinear(cx);
     if (!linearStr)
         return false;
 
     double result;
     if (!date_parseString(linearStr, &result, &cx->runtime()->dateTimeInfo)) {
-        vp->setDouble(js_NaN);
+        vp->setNaN();
         return true;
     }
 
     result = TimeClip(result);
     vp->setNumber(result);
     return true;
 }
 
@@ -1703,17 +1704,17 @@ date_getTimezoneOffset(JSContext *cx, un
     return CallNonGenericMethod<IsDate, DateObject::getTimezoneOffset_impl>(cx, args);
 }
 
 JS_ALWAYS_INLINE bool
 date_setTime_impl(JSContext *cx, CallArgs args)
 {
     Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
     if (args.length() == 0) {
-        dateObj->setUTCTime(js_NaN, args.rval().address());
+        dateObj->setUTCTime(GenericNaN(), args.rval().address());
         return true;
     }
 
     double result;
     if (!ToNumber(cx, args[0], &result))
         return false;
 
     dateObj->setUTCTime(TimeClip(result), args.rval().address());
@@ -2338,17 +2339,17 @@ date_setYear_impl(JSContext *cx, CallArg
 
     /* Step 2. */
     double y;
     if (!ToNumber(cx, args.get(0), &y))
         return false;
 
     /* Step 3. */
     if (IsNaN(y)) {
-        dateObj->setUTCTime(js_NaN, args.rval().address());
+        dateObj->setUTCTime(GenericNaN(), args.rval().address());
         return true;
     }
 
     /* Step 4. */
     double yint = ToInteger(y);
     if (0 <= yint && yint <= 99)
         yint += 1900;
 
@@ -2992,17 +2993,17 @@ js_Date(JSContext *cx, unsigned argc, Va
             if (!str)
                 return false;
 
             JSLinearString *linearStr = str->ensureLinear(cx);
             if (!linearStr)
                 return false;
 
             if (!date_parseString(linearStr, &d, &cx->runtime()->dateTimeInfo))
-                d = js_NaN;
+                d = GenericNaN();
             else
                 d = TimeClip(d);
         } else {
             /* Step 3. */
             if (!ToNumber(cx, args[0], &d))
                 return false;
             d = TimeClip(d);
         }
@@ -3031,17 +3032,17 @@ js_InitDateClass(JSContext *cx, HandleOb
 {
     JS_ASSERT(obj->isNative());
 
     Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
 
     RootedObject dateProto(cx, global->createBlankPrototype(cx, &DateObject::class_));
     if (!dateProto)
         return NULL;
-    dateProto->as<DateObject>().setUTCTime(js_NaN);
+    dateProto->as<DateObject>().setUTCTime(GenericNaN());
 
     RootedFunction ctor(cx);
     ctor = global->createConstructor(cx, js_Date, cx->names().Date, MAXARGS);
     if (!ctor)
         return NULL;
 
     if (!LinkConstructorAndPrototype(cx, ctor, dateProto))
         return NULL;
--- a/js/src/jslibmath.h
+++ b/js/src/jslibmath.h
@@ -49,28 +49,28 @@ inline double
 NumberDiv(double a, double b)
 {
     if (b == 0) {
         if (a == 0 || mozilla::IsNaN(a)
 #ifdef XP_WIN
             || mozilla::IsNaN(b) /* XXX MSVC miscompiles such that (NaN == 0) */
 #endif
         )
-            return js_NaN;
+            return JS::GenericNaN();
 
         if (mozilla::IsNegative(a) != mozilla::IsNegative(b))
             return mozilla::NegativeInfinity();
         return mozilla::PositiveInfinity();
     }
 
     return a / b;
 }
 
 inline double
 NumberMod(double a, double b) {
     if (b == 0) 
-        return js_NaN;
+        return JS::GenericNaN();
     return js_fmod(a, b);
 }
 
 }
 
 #endif /* jslibmath_h */
--- a/js/src/jsmath.cpp
+++ b/js/src/jsmath.cpp
@@ -46,16 +46,17 @@ using mozilla::IsFinite;
 using mozilla::IsInfinite;
 using mozilla::IsNaN;
 using mozilla::IsNegative;
 using mozilla::IsNegativeZero;
 using mozilla::PositiveInfinity;
 using mozilla::NegativeInfinity;
 using mozilla::SpecificNaN;
 using JS::ToNumber;
+using JS::GenericNaN;
 
 #ifndef M_E
 #define M_E             2.7182818284590452354
 #endif
 #ifndef M_LOG2E
 #define M_LOG2E         1.4426950408889634074
 #endif
 #ifndef M_LOG10E
@@ -114,31 +115,31 @@ const Class js::MathClass = {
 };
 
 bool
 js_math_abs(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     if (args.length() == 0) {
-        args.rval().setDouble(js_NaN);
+        args.rval().setNaN();
         return true;
     }
 
     double x;
     if (!ToNumber(cx, args[0], &x))
         return false;
 
     double z = Abs(x);
     args.rval().setNumber(z);
     return true;
 }
 
 #if defined(SOLARIS) && defined(__GNUC__)
-#define ACOS_IF_OUT_OF_RANGE(x) if (x < -1 || 1 < x) return js_NaN;
+#define ACOS_IF_OUT_OF_RANGE(x) if (x < -1 || 1 < x) return GenericNaN();
 #else
 #define ACOS_IF_OUT_OF_RANGE(x)
 #endif
 
 double
 js::math_acos_impl(MathCache *cache, double x)
 {
     ACOS_IF_OUT_OF_RANGE(x);
@@ -155,17 +156,17 @@ js::math_acos_uncached(double x)
 #undef ACOS_IF_OUT_OF_RANGE
 
 bool
 js::math_acos(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     if (args.length() == 0) {
-        args.rval().setDouble(js_NaN);
+        args.rval().setNaN();
         return true;
     }
 
     double x;
     if (!ToNumber(cx, args[0], &x))
         return false;
 
     MathCache *mathCache = cx->runtime()->getMathCache(cx);
@@ -173,17 +174,17 @@ js::math_acos(JSContext *cx, unsigned ar
         return false;
 
     double z = math_acos_impl(mathCache, x);
     args.rval().setDouble(z);
     return true;
 }
 
 #if defined(SOLARIS) && defined(__GNUC__)
-#define ASIN_IF_OUT_OF_RANGE(x) if (x < -1 || 1 < x) return js_NaN;
+#define ASIN_IF_OUT_OF_RANGE(x) if (x < -1 || 1 < x) return GenericNaN();
 #else
 #define ASIN_IF_OUT_OF_RANGE(x)
 #endif
 
 double
 js::math_asin_impl(MathCache *cache, double x)
 {
     ASIN_IF_OUT_OF_RANGE(x);
@@ -200,17 +201,17 @@ js::math_asin_uncached(double x)
 #undef ASIN_IF_OUT_OF_RANGE
 
 bool
 js::math_asin(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     if (args.length() == 0) {
-        args.rval().setDouble(js_NaN);
+        args.rval().setNaN();
         return true;
     }
 
     double x;
     if (!ToNumber(cx, args[0], &x))
         return false;
 
     MathCache *mathCache = cx->runtime()->getMathCache(cx);
@@ -235,17 +236,17 @@ js::math_atan_uncached(double x)
 }
 
 bool
 js::math_atan(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     if (args.length() == 0) {
-        args.rval().setDouble(js_NaN);
+        args.rval().setNaN();
         return true;
     }
 
     double x;
     if (!ToNumber(cx, args[0], &x))
         return false;
 
     MathCache *mathCache = cx->runtime()->getMathCache(cx);
@@ -288,17 +289,17 @@ js::ecmaAtan2(double y, double x)
 }
 
 bool
 js::math_atan2(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     if (args.length() <= 1) {
-        args.rval().setDouble(js_NaN);
+        args.rval().setNaN();
         return true;
     }
 
     double x, y;
     if (!ToNumber(cx, args[0], &x) || !ToNumber(cx, args[1], &y))
         return false;
 
     double z = ecmaAtan2(x, y);
@@ -317,17 +318,17 @@ js_math_ceil_impl(double x)
 }
 
 bool
 js_math_ceil(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     if (args.length() == 0) {
-        args.rval().setDouble(js_NaN);
+        args.rval().setNaN();
         return true;
     }
 
     double x;
     if (!ToNumber(cx, args[0], &x))
         return false;
 
     double z = js_math_ceil_impl(x);
@@ -348,17 +349,17 @@ js::math_cos_uncached(double x)
 }
 
 bool
 js::math_cos(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     if (args.length() == 0) {
-        args.rval().setDouble(js_NaN);
+        args.rval().setNaN();
         return true;
     }
 
     double x;
     if (!ToNumber(cx, args[0], &x))
         return false;
 
     MathCache *mathCache = cx->runtime()->getMathCache(cx);
@@ -399,17 +400,17 @@ js::math_exp_uncached(double x)
 #undef EXP_IF_OUT_OF_RANGE
 
 bool
 js::math_exp(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     if (args.length() == 0) {
-        args.rval().setDouble(js_NaN);
+        args.rval().setNaN();
         return true;
     }
 
     double x;
     if (!ToNumber(cx, args[0], &x))
         return false;
 
     MathCache *mathCache = cx->runtime()->getMathCache(cx);
@@ -428,17 +429,17 @@ js_math_floor_impl(double x)
 }
 
 bool
 js_math_floor(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     if (args.length() == 0) {
-        args.rval().setDouble(js_NaN);
+        args.rval().setNaN();
         return true;
     }
 
     double x;
     if (!ToNumber(cx, args[0], &x))
         return false;
 
     double z = js_math_floor_impl(x);
@@ -465,31 +466,31 @@ js::math_imul(JSContext *cx, unsigned ar
 }
 
 bool
 js::math_fround(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     if (args.length() == 0) {
-        args.rval().setDouble(js_NaN);
+        args.rval().setNaN();
         return true;
     }
 
     double x;
     if (!ToNumber(cx, args[0], &x))
         return false;
 
     float f = x;
     args.rval().setDouble(static_cast<double>(f));
     return true;
 }
 
 #if defined(SOLARIS) && defined(__GNUC__)
-#define LOG_IF_OUT_OF_RANGE(x) if (x < 0) return js_NaN;
+#define LOG_IF_OUT_OF_RANGE(x) if (x < 0) return GenericNaN();
 #else
 #define LOG_IF_OUT_OF_RANGE(x)
 #endif
 
 double
 js::math_log_impl(MathCache *cache, double x)
 {
     LOG_IF_OUT_OF_RANGE(x);
@@ -506,17 +507,17 @@ js::math_log_uncached(double x)
 #undef LOG_IF_OUT_OF_RANGE
 
 bool
 js::math_log(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     if (args.length() == 0) {
-        args.rval().setDouble(js_NaN);
+        args.rval().setNaN();
         return true;
     }
 
     double x;
     if (!ToNumber(cx, args[0], &x))
         return false;
 
     MathCache *mathCache = cx->runtime()->getMathCache(cx);
@@ -613,17 +614,17 @@ js::ecmaPow(double x, double y)
     if (int32_t(y) == y)
         return powi(x, int32_t(y));
 
     /*
      * Because C99 and ECMA specify different behavior for pow(),
      * we need to wrap the libm call to make it ECMA compliant.
      */
     if (!IsFinite(y) && (x == 1.0 || x == -1.0))
-        return js_NaN;
+        return GenericNaN();
     /* pow(x, +-0) is always 1, even for x = NaN (MSVC gets this wrong). */
     if (y == 0)
         return 1;
     return pow(x, y);
 }
 #if defined(_MSC_VER)
 # pragma optimize("", on)
 #endif
@@ -633,17 +634,17 @@ js::ecmaPow(double x, double y)
 # pragma optimize("g", off)
 #endif
 bool
 js_math_pow(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     if (args.length() <= 1) {
-        args.rval().setDouble(js_NaN);
+        args.rval().setNaN();
         return true;
     }
 
     double x, y;
     if (!ToNumber(cx, args[0], &x) || !ToNumber(cx, args[1], &y))
         return false;
 
     /*
@@ -770,17 +771,17 @@ js_math_random(JSContext *cx, unsigned a
 }
 
 bool /* ES5 15.8.2.15. */
 js_math_round(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     if (args.length() == 0) {
-        args.rval().setDouble(js_NaN);
+        args.rval().setNaN();
         return true;
     }
 
     double x;
     if (!ToNumber(cx, args[0], &x))
         return false;
 
     int32_t i;
@@ -812,17 +813,17 @@ js::math_sin_uncached(double x)
 }
 
 bool
 js::math_sin(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     if (args.length() == 0) {
-        args.rval().setDouble(js_NaN);
+        args.rval().setNaN();
         return true;
     }
 
     double x;
     if (!ToNumber(cx, args[0], &x))
         return false;
 
     MathCache *mathCache = cx->runtime()->getMathCache(cx);
@@ -835,17 +836,17 @@ js::math_sin(JSContext *cx, unsigned arg
 }
 
 bool
 js_math_sqrt(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     if (args.length() == 0) {
-        args.rval().setDouble(js_NaN);
+        args.rval().setNaN();
         return true;
     }
 
     double x;
     if (!ToNumber(cx, args[0], &x))
         return false;
 
     MathCache *mathCache = cx->runtime()->getMathCache(cx);
@@ -870,17 +871,17 @@ js::math_tan_uncached(double x)
 }
 
 bool
 js::math_tan(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     if (args.length() == 0) {
-        args.rval().setDouble(js_NaN);
+        args.rval().setNaN();
         return true;
     }
 
     double x;
     if (!ToNumber(cx, args[0], &x))
         return false;
 
     MathCache *mathCache = cx->runtime()->getMathCache(cx);
@@ -895,17 +896,17 @@ js::math_tan(JSContext *cx, unsigned arg
 
 typedef double (*UnaryMathFunctionType)(MathCache *cache, double);
 
 template <UnaryMathFunctionType F>
 bool math_function(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     if (args.length() == 0) {
-        args.rval().setNumber(js_NaN);
+        args.rval().setNumber(GenericNaN());
         return true;
     }
 
     double x;
     if (!ToNumber(cx, args[0], &x))
         return false;
 
     MathCache *mathCache = cx->runtime()->getMathCache(cx);
@@ -1273,17 +1274,17 @@ js::math_atanh(JSContext *cx, unsigned a
 #if 0
 #if !HAVE_HYPOT
 double hypot(double x, double y)
 {
     if (mozilla::IsInfinite(x) || mozilla::IsInfinite(y))
         return PositiveInfinity();
 
     if (mozilla::IsNaN(x) || mozilla::IsNaN(y))
-        return js_NaN;
+        return GenericNaN();
 
     double xabs = mozilla::Abs(x);
     double yabs = mozilla::Abs(y);
 
     double min = std::min(xabs, yabs);
     double max = std::max(xabs, yabs);
 
     if (min == 0) {
@@ -1306,17 +1307,17 @@ js::math_hypot_impl(double x, double y)
     return hypot(x, y);
 }
 
 bool
 js::math_hypot(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     if (args.length() < 2) {
-        args.rval().setNumber(js_NaN);
+        args.rval().setNumber(GenericNaN());
         return true;
     }
 
     double x, y;
     if (!ToNumber(cx, args[0], &x))
         return false;
 
     if (!ToNumber(cx, args[1], &y))
@@ -1361,17 +1362,17 @@ bool
 js::math_trunc(JSContext *cx, unsigned argc, Value *vp)
 {
     return math_function<math_trunc_impl>(cx, argc, vp);
 }
 
 double sign(double x)
 {
     if (mozilla::IsNaN(x))
-        return js_NaN;
+        return GenericNaN();
 
     return x == 0 ? x : x < 0 ? -1 : 1;
 }
 
 double
 js::math_sign_impl(MathCache *cache, double x)
 {
     return cache->lookup(sign, x);
--- a/js/src/jsnum.cpp
+++ b/js/src/jsnum.cpp
@@ -46,16 +46,17 @@
 
 using namespace js;
 using namespace js::types;
 
 using mozilla::NegativeInfinity;
 using mozilla::PodCopy;
 using mozilla::PositiveInfinity;
 using mozilla::RangedPtr;
+using JS::GenericNaN;
 
 /*
  * If we're accumulating a decimal number and the number is >= 2^53, then the
  * fast result from the loop in Get{Prefix,Decimal}Integer may be inaccurate.
  * Call js_strtod_harder to get the correct answer.
  */
 static bool
 ComputeAccurateDecimalInteger(ThreadSafeContext *cx,
@@ -299,47 +300,47 @@ num_isFinite(JSContext *cx, unsigned arg
 }
 
 static bool
 num_parseFloat(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     if (args.length() == 0) {
-        args.rval().setDouble(js_NaN);
+        args.rval().setNaN();
         return true;
     }
     JSString *str = ToString<CanGC>(cx, args[0]);
     if (!str)
         return false;
     const jschar *bp = str->getChars(cx);
     if (!bp)
         return false;
     const jschar *end = bp + str->length();
     const jschar *ep;
     double d;
     if (!js_strtod(cx, bp, end, &ep, &d))
         return false;
     if (ep == bp) {
-        args.rval().setDouble(js_NaN);
+        args.rval().setNaN();
         return true;
     }
     args.rval().setDouble(d);
     return true;
 }
 
 /* ES5 15.1.2.2. */
 bool
 js::num_parseInt(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     /* Fast paths and exceptional cases. */
     if (args.length() == 0) {
-        args.rval().setDouble(js_NaN);
+        args.rval().setNaN();
         return true;
     }
 
     if (args.length() == 1 ||
         (args[1].isInt32() && (args[1].toInt32() == 0 || args[1].toInt32() == 10))) {
         if (args[0].isInt32()) {
             args.rval().set(args[0]);
             return true;
@@ -386,17 +387,17 @@ js::num_parseInt(JSContext *cx, unsigned
         radix = 10;
     } else {
         if (!ToInt32(cx, args[1], &radix))
             return false;
         if (radix == 0) {
             radix = 10;
         } else {
             if (radix < 2 || radix > 36) {
-                args.rval().setDouble(js_NaN);
+                args.rval().setNaN();
                 return true;
             }
             if (radix != 16)
                 stripPrefix = false;
         }
     }
 
     /* Step 2. */
@@ -429,17 +430,17 @@ js::num_parseInt(JSContext *cx, unsigned
     }
 
     /* Steps 11-15. */
     const jschar *actualEnd;
     double number;
     if (!GetPrefixInteger(cx, s, end, radix, &actualEnd, &number))
         return false;
     if (s == actualEnd)
-        args.rval().setNumber(js_NaN);
+        args.rval().setNaN();
     else
         args.rval().setNumber(negative ? -number : number);
     return true;
 }
 
 static const JSFunctionSpec number_functions[] = {
     JS_FN(js_isNaN_str,         num_isNaN,           1,0),
     JS_FN(js_isFinite_str,      num_isFinite,        1,0),
@@ -1111,18 +1112,16 @@ static JSConstDoubleSpec number_constant
     {0,                         "NEGATIVE_INFINITY", 0,{0,0,0}},
     {1.7976931348623157E+308,   "MAX_VALUE",         0,{0,0,0}},
     {0,                         "MIN_VALUE",         0,{0,0,0}},
     /* ES6 (May 2013 draft) 15.7.3.7 */
     {2.2204460492503130808472633361816e-16, "EPSILON", 0,{0,0,0}},
     {0,0,0,{0,0,0}}
 };
 
-double js_NaN;
-
 #if (defined __GNUC__ && defined __i386__) || \
     (defined __SUNPRO_CC && defined __i386)
 
 /*
  * Set the exception mask to mask all exceptions and set the FPU precision
  * to 53 bit mantissa (64 bit doubles).
  */
 inline void FIX_FPU() {
@@ -1145,18 +1144,18 @@ js::InitRuntimeNumberState(JSRuntime *rt
     FIX_FPU();
 
     double d;
 
     /*
      * Our NaN must be one particular canonical value, because we rely on NaN
      * encoding for our value representation.  See Value.h.
      */
-    d = mozilla::SpecificNaN(0, 0x8000000000000ULL);
-    number_constants[NC_NaN].dval = js_NaN = d;
+    d = GenericNaN();
+    number_constants[NC_NaN].dval = d;
     rt->NaNValue.setDouble(d);
 
     d = mozilla::PositiveInfinity();
     number_constants[NC_POSITIVE_INFINITY].dval = d;
     rt->positiveInfinityValue.setDouble(d);
 
     d = mozilla::NegativeInfinity();
     number_constants[NC_NEGATIVE_INFINITY].dval = d;
@@ -1499,17 +1498,17 @@ CharsToNumber(ThreadSafeContext *cx, con
 {
     if (length == 1) {
         jschar c = chars[0];
         if ('0' <= c && c <= '9')
             *result = c - '0';
         else if (unicode::IsSpace(c))
             *result = 0.0;
         else
-            *result = js_NaN;
+            *result = GenericNaN();
         return;
     }
 
     const jschar *end = chars + length;
     const jschar *bp = SkipSpace(chars, end);
 
     /* ECMA doesn't allow signed hex numbers (bug 273467). */
     if (end - bp >= 2 && bp[0] == '0' && (bp[1] == 'x' || bp[1] == 'X')) {
@@ -1519,34 +1518,34 @@ CharsToNumber(ThreadSafeContext *cx, con
          * the hex digits.
          */
         const jschar *endptr;
         double d;
         if (!GetPrefixInteger(cx, bp + 2, end, 16, &endptr, &d) ||
             endptr == bp + 2 ||
             SkipSpace(endptr, end) != end)
         {
-            *result = js_NaN;
+            *result = GenericNaN();
         } else {
             *result = d;
         }
         return;
     }
 
     /*
      * Note that ECMA doesn't treat a string beginning with a '0' as
      * an octal number here. This works because all such numbers will
      * be interpreted as decimal by js_strtod.  Also, any hex numbers
      * that have made it here (which can only be negative ones) will
      * be treated as 0 without consuming the 'x' by js_strtod.
      */
     const jschar *ep;
     double d;
     if (!js_strtod(cx, bp, end, &ep, &d) || SkipSpace(ep, end) != end)
-        *result = js_NaN;
+        *result = GenericNaN();
     else
         *result = d;
 }
 
 bool
 js::StringToNumber(ThreadSafeContext *cx, JSString *str, double *result)
 {
     ScopedThreadSafeStringInspector inspector(str);
@@ -1569,17 +1568,17 @@ js::NonObjectToNumberSlow(ThreadSafeCont
         return true;
     }
     if (v.isNull()) {
         *out = 0.0;
         return true;
     }
 
     JS_ASSERT(v.isUndefined());
-    *out = js_NaN;
+    *out = GenericNaN();
     return true;
 }
 
 #if defined(_MSC_VER)
 # pragma optimize("g", off)
 #endif
 
 bool
@@ -1622,17 +1621,17 @@ js::ToNumberSlow(ExclusiveContext *cx, V
         RootedValue v2(cx, v);
         if (!ToPrimitive(cx->asJSContext(), JSTYPE_NUMBER, &v2))
             return false;
         v = v2;
         if (v.isObject())
             break;
     }
 
-    *out = js_NaN;
+    *out = GenericNaN();
     return true;
 }
 
 JS_PUBLIC_API(bool)
 js::ToNumberSlow(JSContext *cx, Value v, double *out)
 {
     return ToNumberSlow(static_cast<ExclusiveContext *>(cx), v, out);
 }
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -903,17 +903,17 @@ js_str_charCodeAt(JSContext *cx, unsigne
 
     jschar c;
     if (!str->getChar(cx, i, &c))
         return false;
     args.rval().setInt32(c);
     return true;
 
 out_of_range:
-    args.rval().setDouble(js_NaN);
+    args.rval().setNaN();
     return true;
 }
 
 /*
  * Boyer-Moore-Horspool superlinear search for pat:patlen in text:textlen.
  * The patlen argument must be positive and no greater than sBMHPatLenMax.
  *
  * Return the index of pat in text, or -1 if not found.
--- a/js/src/vm/DateTime.h
+++ b/js/src/vm/DateTime.h
@@ -7,16 +7,17 @@
 #ifndef vm_DateTime_h
 #define vm_DateTime_h
 
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/MathAlgorithms.h"
 
 #include <stdint.h>
 
+#include "js/Value.h"
 #include "vm/NumericConversions.h"
 
 namespace js {
 
 /* Constants defined by ES5 15.9.1.10. */
 const double HoursPerDay = 24;
 const double MinutesPerHour = 60;
 const double SecondsPerMinute = 60;
@@ -41,17 +42,17 @@ const double EndOfTime = 8.64e15;
 const double MaxTimeMagnitude = 8.64e15;
 
 /* ES5 15.9.1.14. */
 inline double
 TimeClip(double time)
 {
     /* Steps 1-2. */
     if (!mozilla::IsFinite(time) || mozilla::Abs(time) > MaxTimeMagnitude)
-        return js_NaN;
+        return JS::GenericNaN();
 
     /* Step 3. */
     return ToInteger(time + (+0.0));
 }
 
 /*
  * Stores date/time information, particularly concerning the current local
  * time zone, and implements a small cache for daylight saving time offset
--- a/js/src/vm/NumericConversions.h
+++ b/js/src/vm/NumericConversions.h
@@ -9,19 +9,16 @@
 
 #include "mozilla/Assertions.h"
 #include "mozilla/Casting.h"
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/TypeTraits.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 {
 
 /*
  * Convert a double value to ResultType (an unsigned integral type) using
  * ECMAScript-style semantics (that is, in like manner to how ECMAScript's
  * ToInt32 converts to int32_t).
--- a/js/src/vm/ObjectImpl.cpp
+++ b/js/src/vm/ObjectImpl.cpp
@@ -9,16 +9,18 @@
 #include "gc/Marking.h"
 #include "js/Value.h"
 #include "vm/Debugger.h"
 
 #include "jsobjinlines.h"
 
 using namespace js;
 
+using JS::GenericNaN;
+
 PropDesc::PropDesc()
   : pd_(UndefinedValue()),
     value_(UndefinedValue()),
     get_(UndefinedValue()),
     set_(UndefinedValue()),
     attrs(0),
     hasGet_(false),
     hasSet_(false),
@@ -893,23 +895,23 @@ TypedElementsHeader<T>::setElement(JSCon
         d = v.toNumber();
     } else if (v.isNull()) {
         d = 0.0;
     } else if (v.isPrimitive()) {
         if (v.isString()) {
             if (!StringToNumber(cx, v.toString(), &d))
                 return false;
         } else if (v.isUndefined()) {
-            d = js_NaN;
+            d = GenericNaN();
         } else {
             d = double(v.toBoolean());
         }
     } else {
         // non-primitive assignments become NaN or 0 (for float/int arrays)
-        d = js_NaN;
+        d = GenericNaN();
     }
 
     assign(index, d);
     *succeeded = true;
     return true;
 }
 
 bool
--- a/js/src/vm/TypedArrayObject.cpp
+++ b/js/src/vm/TypedArrayObject.cpp
@@ -48,16 +48,17 @@
 #endif
 
 using namespace js;
 using namespace js::gc;
 using namespace js::types;
 
 using mozilla::IsNaN;
 using mozilla::PodCopy;
+using JS::GenericNaN;
 
 /*
  * Allocate array buffers with the maximum number of fixed slots marked as
  * reserved, so that the fixed slots may be used for the buffer's contents.
  * The last fixed slot is kept for the object's private data.
  */
 static const uint8_t ARRAYBUFFER_RESERVED_SLOTS = JSObject::MAX_FIXED_SLOTS - 1;
 
@@ -1242,23 +1243,23 @@ js::ToDoubleForTypedArray(JSContext *cx,
     } else if (vp.isNull()) {
         *d = 0.0;
     } else if (vp.isPrimitive()) {
         JS_ASSERT(vp.isString() || vp.isUndefined() || vp.isBoolean());
         if (vp.isString()) {
             if (!ToNumber(cx, vp, d))
                 return false;
         } else if (vp.isUndefined()) {
-            *d = js_NaN;
+            *d = GenericNaN();
         } else {
             *d = double(vp.toBoolean());
         }
     } else {
         // non-primitive assignments become NaN or 0 (for float/int arrays)
-        *d = js_NaN;
+        *d = GenericNaN();
     }
 
 #ifdef JS_MORE_DETERMINISTIC
     // It's possible to have a NaN value with the sign bit set. The spec allows
     // this but it can confuse differential testing when this value is stored
     // to a float array and then read back as integer. To work around this, we
     // always canonicalize NaN values in more-deterministic builds.
     *d = JS_CANONICALIZE_NAN(*d);
@@ -2230,17 +2231,17 @@ class TypedArrayObjectTemplate : public 
             // ToNumber will only fail from OOM
             if (!ToNumber(cx, primitive, &dval))
                 return false;
             *result = nativeFromDouble(dval);
             return true;
         }
 
         *result = ArrayTypeIsFloatingPoint()
-                  ? NativeType(js_NaN)
+                  ? NativeType(GenericNaN())
                   : NativeType(int32_t(0));
         return true;
     }
 
     static bool
     copyFromArray(JSContext *cx, HandleObject thisTypedArrayObj,
                   HandleObject ar, uint32_t len, uint32_t offset = 0)
     {
--- a/mfbt/tests/TestFloatingPoint.cpp
+++ b/mfbt/tests/TestFloatingPoint.cpp
@@ -136,16 +136,17 @@ TestPredicates()
   MOZ_ASSERT(!IsNegative(1.0));
 
   MOZ_ASSERT(!IsNegativeZero(PositiveInfinity()));
   MOZ_ASSERT(!IsNegativeZero(NegativeInfinity()));
   MOZ_ASSERT(!IsNegativeZero(SpecificNaN(1, 17)));;
   MOZ_ASSERT(!IsNegativeZero(SpecificNaN(1, 0xfffffffffff0fULL)));
   MOZ_ASSERT(!IsNegativeZero(SpecificNaN(0, 17)));;
   MOZ_ASSERT(!IsNegativeZero(SpecificNaN(0, 0xfffffffffff0fULL)));
+  MOZ_ASSERT(!IsNegativeZero(UnspecifiedNaN()));
   MOZ_ASSERT(IsNegativeZero(-0.0));
   MOZ_ASSERT(!IsNegativeZero(0.0));
   MOZ_ASSERT(!IsNegativeZero(-1.0));
   MOZ_ASSERT(!IsNegativeZero(1.0));
 }
 
 int
 main()