Discern between NaNs that would be safe to tag and NaNs that need some purification...
authorfpizlo@apple.com <fpizlo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 16 Apr 2014 22:44:00 +0000 (22:44 +0000)
committerfpizlo@apple.com <fpizlo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 16 Apr 2014 22:44:00 +0000 (22:44 +0000)
https://bugs.webkit.org/show_bug.cgi?id=131420

Reviewed by Oliver Hunt.

Rationalizes our handling of NaNs. We now have the notion of pureNaN(), or PNaN, which
replaces QNaN and represents a "safe" NaN for our tagging purposes. NaN purification now
goes through the purifyNaN() API.

SpeculatedType and its clients can now distinguish between a PureNaN and an ImpureNaN.

Prediction propagator is made slightly more cautious when dealing with NaNs. It doesn't
have to be too cautious since most prediction-based logic only cares about whether or not
a value could be an integer.

AI is made much more cautious when dealing with NaNs. We don't yet introduce ImpureNaN
anywhere in the compiler, but when we do, we ought to be able to trust AI to propagate it
soundly and precisely.

No performance change because this just unblocks
https://bugs.webkit.org/show_bug.cgi?id=131419.

* API/JSValueRef.cpp:
(JSValueMakeNumber):
(JSValueToNumber):
* JavaScriptCore.xcodeproj/project.pbxproj:
* bytecode/SpeculatedType.cpp:
(JSC::dumpSpeculation):
(JSC::speculationFromValue):
(JSC::typeOfDoubleSum):
(JSC::typeOfDoubleDifference):
(JSC::typeOfDoubleProduct):
(JSC::polluteDouble):
(JSC::typeOfDoubleQuotient):
(JSC::typeOfDoubleMinMax):
(JSC::typeOfDoubleNegation):
(JSC::typeOfDoubleAbs):
(JSC::typeOfDoubleFRound):
(JSC::typeOfDoubleBinaryOp):
(JSC::typeOfDoubleUnaryOp):
* bytecode/SpeculatedType.h:
* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::handleInlining):
(JSC::DFG::ByteCodeParser::parseCodeBlock):
* dfg/DFGCriticalEdgeBreakingPhase.cpp:
(JSC::DFG::CriticalEdgeBreakingPhase::breakCriticalEdge):
* dfg/DFGInPlaceAbstractState.cpp:
(JSC::DFG::InPlaceAbstractState::mergeStateAtTail):
* dfg/DFGLoopPreHeaderCreationPhase.cpp:
(JSC::DFG::createPreHeader):
* dfg/DFGNode.h:
(JSC::DFG::BranchTarget::BranchTarget):
* dfg/DFGOSREntrypointCreationPhase.cpp:
(JSC::DFG::OSREntrypointCreationPhase::run):
* dfg/DFGOSRExitCompiler32_64.cpp:
(JSC::DFG::OSRExitCompiler::compileExit):
* dfg/DFGOSRExitCompiler64.cpp:
(JSC::DFG::OSRExitCompiler::compileExit):
* dfg/DFGPredictionPropagationPhase.cpp:
(JSC::DFG::PredictionPropagationPhase::speculatedDoubleTypeForPrediction):
(JSC::DFG::PredictionPropagationPhase::propagate):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::emitAllocateJSArray):
(JSC::DFG::SpeculativeJIT::compileValueToInt32):
(JSC::DFG::SpeculativeJIT::compileGetByValOnFloatTypedArray):
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGVariableAccessData.h:
(JSC::DFG::VariableAccessData::makePredictionForDoubleFormat):
* ftl/FTLLowerDFGToLLVM.cpp:
(JSC::FTL::LowerDFGToLLVM::compileGetByVal):
(JSC::FTL::LowerDFGToLLVM::compilePutByVal):
(JSC::FTL::LowerDFGToLLVM::compileArrayPush):
(JSC::FTL::LowerDFGToLLVM::compileArrayPop):
(JSC::FTL::LowerDFGToLLVM::compileNewArrayWithSize):
(JSC::FTL::LowerDFGToLLVM::numberOrNotCellToInt32):
(JSC::FTL::LowerDFGToLLVM::allocateJSArray):
* ftl/FTLValueFormat.cpp:
(JSC::FTL::reboxAccordingToFormat):
* jit/AssemblyHelpers.cpp:
(JSC::AssemblyHelpers::purifyNaN):
(JSC::AssemblyHelpers::sanitizeDouble): Deleted.
* jit/AssemblyHelpers.h:
* jit/JITPropertyAccess.cpp:
(JSC::JIT::emitFloatTypedArrayGetByVal):
* runtime/DateConstructor.cpp:
(JSC::constructDate):
* runtime/DateInstanceCache.h:
(JSC::DateInstanceData::DateInstanceData):
(JSC::DateInstanceCache::reset):
* runtime/ExceptionHelpers.cpp:
(JSC::TerminatedExecutionError::defaultValue):
* runtime/JSArray.cpp:
(JSC::JSArray::setLength):
(JSC::JSArray::pop):
(JSC::JSArray::shiftCountWithAnyIndexingType):
(JSC::JSArray::sortVector):
(JSC::JSArray::compactForSorting):
* runtime/JSArray.h:
(JSC::JSArray::create):
(JSC::JSArray::tryCreateUninitialized):
* runtime/JSCJSValue.cpp:
(JSC::JSValue::toNumberSlowCase):
* runtime/JSCJSValue.h:
* runtime/JSCJSValueInlines.h:
(JSC::jsNaN):
(JSC::JSValue::JSValue):
(JSC::JSValue::getPrimitiveNumber):
* runtime/JSGlobalObjectFunctions.cpp:
(JSC::parseInt):
(JSC::jsStrDecimalLiteral):
(JSC::toDouble):
(JSC::jsToNumber):
(JSC::parseFloat):
* runtime/JSObject.cpp:
(JSC::JSObject::createInitialDouble):
(JSC::JSObject::convertUndecidedToDouble):
(JSC::JSObject::convertInt32ToDouble):
(JSC::JSObject::deletePropertyByIndex):
(JSC::JSObject::ensureLengthSlow):
* runtime/MathObject.cpp:
(JSC::mathProtoFuncMax):
(JSC::mathProtoFuncMin):
* runtime/PureNaN.h: Added.
(JSC::pureNaN):
(JSC::isImpureNaN):
(JSC::purifyNaN):
* runtime/TypedArrayAdaptors.h:
(JSC::FloatTypedArrayAdaptor::toJSValue):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@167394 268f45cc-cd09-0410-ab3c-d52691b4dbfc

37 files changed:
Source/JavaScriptCore/API/JSValueRef.cpp
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
Source/JavaScriptCore/bytecode/SpeculatedType.cpp
Source/JavaScriptCore/bytecode/SpeculatedType.h
Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
Source/JavaScriptCore/dfg/DFGCriticalEdgeBreakingPhase.cpp
Source/JavaScriptCore/dfg/DFGInPlaceAbstractState.cpp
Source/JavaScriptCore/dfg/DFGLoopPreHeaderCreationPhase.cpp
Source/JavaScriptCore/dfg/DFGNode.h
Source/JavaScriptCore/dfg/DFGOSREntrypointCreationPhase.cpp
Source/JavaScriptCore/dfg/DFGOSRExitCompiler32_64.cpp
Source/JavaScriptCore/dfg/DFGOSRExitCompiler64.cpp
Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
Source/JavaScriptCore/dfg/DFGVariableAccessData.h
Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp
Source/JavaScriptCore/ftl/FTLValueFormat.cpp
Source/JavaScriptCore/jit/AssemblyHelpers.cpp
Source/JavaScriptCore/jit/AssemblyHelpers.h
Source/JavaScriptCore/jit/JITPropertyAccess.cpp
Source/JavaScriptCore/runtime/DateConstructor.cpp
Source/JavaScriptCore/runtime/DateInstanceCache.h
Source/JavaScriptCore/runtime/ExceptionHelpers.cpp
Source/JavaScriptCore/runtime/JSArray.cpp
Source/JavaScriptCore/runtime/JSArray.h
Source/JavaScriptCore/runtime/JSCJSValue.cpp
Source/JavaScriptCore/runtime/JSCJSValue.h
Source/JavaScriptCore/runtime/JSCJSValueInlines.h
Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp
Source/JavaScriptCore/runtime/JSObject.cpp
Source/JavaScriptCore/runtime/MathObject.cpp
Source/JavaScriptCore/runtime/PureNaN.h [new file with mode: 0644]
Source/JavaScriptCore/runtime/TypedArrayAdaptors.h

index 70df934..4daf926 100644 (file)
@@ -303,13 +303,7 @@ JSValueRef JSValueMakeNumber(JSContextRef ctx, double value)
     ExecState* exec = toJS(ctx);
     JSLockHolder locker(exec);
 
-    // Our JSValue representation relies on a standard bit pattern for NaN. NaNs
-    // generated internally to JavaScriptCore naturally have that representation,
-    // but an external NaN might not.
-    if (std::isnan(value))
-        value = QNaN;
-
-    return toRef(exec, jsNumber(value));
+    return toRef(exec, jsNumber(purifyNaN(value)));
 }
 
 JSValueRef JSValueMakeString(JSContextRef ctx, JSStringRef string)
@@ -384,7 +378,7 @@ double JSValueToNumber(JSContextRef ctx, JSValueRef value, JSValueRef* exception
 {
     if (!ctx) {
         ASSERT_NOT_REACHED();
-        return QNaN;
+        return PNaN;
     }
     ExecState* exec = toJS(ctx);
     JSLockHolder locker(exec);
@@ -400,7 +394,7 @@ double JSValueToNumber(JSContextRef ctx, JSValueRef value, JSValueRef* exception
 #if ENABLE(REMOTE_INSPECTOR)
         exec->vmEntryGlobalObject()->inspectorController().reportAPIException(exec, exceptionValue);
 #endif
-        number = QNaN;
+        number = PNaN;
     }
     return number;
 }
index ef45c87..1251c02 100644 (file)
@@ -1,3 +1,139 @@
+2014-04-16  Filip Pizlo  <fpizlo@apple.com>
+
+        Discern between NaNs that would be safe to tag and NaNs that need some purification before tagging
+        https://bugs.webkit.org/show_bug.cgi?id=131420
+
+        Reviewed by Oliver Hunt.
+        
+        Rationalizes our handling of NaNs. We now have the notion of pureNaN(), or PNaN, which
+        replaces QNaN and represents a "safe" NaN for our tagging purposes. NaN purification now
+        goes through the purifyNaN() API.
+        
+        SpeculatedType and its clients can now distinguish between a PureNaN and an ImpureNaN.
+        
+        Prediction propagator is made slightly more cautious when dealing with NaNs. It doesn't
+        have to be too cautious since most prediction-based logic only cares about whether or not
+        a value could be an integer.
+        
+        AI is made much more cautious when dealing with NaNs. We don't yet introduce ImpureNaN
+        anywhere in the compiler, but when we do, we ought to be able to trust AI to propagate it
+        soundly and precisely.
+        
+        No performance change because this just unblocks
+        https://bugs.webkit.org/show_bug.cgi?id=131419.
+
+        * API/JSValueRef.cpp:
+        (JSValueMakeNumber):
+        (JSValueToNumber):
+        * JavaScriptCore.xcodeproj/project.pbxproj:
+        * bytecode/SpeculatedType.cpp:
+        (JSC::dumpSpeculation):
+        (JSC::speculationFromValue):
+        (JSC::typeOfDoubleSum):
+        (JSC::typeOfDoubleDifference):
+        (JSC::typeOfDoubleProduct):
+        (JSC::polluteDouble):
+        (JSC::typeOfDoubleQuotient):
+        (JSC::typeOfDoubleMinMax):
+        (JSC::typeOfDoubleNegation):
+        (JSC::typeOfDoubleAbs):
+        (JSC::typeOfDoubleFRound):
+        (JSC::typeOfDoubleBinaryOp):
+        (JSC::typeOfDoubleUnaryOp):
+        * bytecode/SpeculatedType.h:
+        * dfg/DFGAbstractInterpreterInlines.h:
+        (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+        * dfg/DFGByteCodeParser.cpp:
+        (JSC::DFG::ByteCodeParser::handleInlining):
+        (JSC::DFG::ByteCodeParser::parseCodeBlock):
+        * dfg/DFGCriticalEdgeBreakingPhase.cpp:
+        (JSC::DFG::CriticalEdgeBreakingPhase::breakCriticalEdge):
+        * dfg/DFGInPlaceAbstractState.cpp:
+        (JSC::DFG::InPlaceAbstractState::mergeStateAtTail):
+        * dfg/DFGLoopPreHeaderCreationPhase.cpp:
+        (JSC::DFG::createPreHeader):
+        * dfg/DFGNode.h:
+        (JSC::DFG::BranchTarget::BranchTarget):
+        * dfg/DFGOSREntrypointCreationPhase.cpp:
+        (JSC::DFG::OSREntrypointCreationPhase::run):
+        * dfg/DFGOSRExitCompiler32_64.cpp:
+        (JSC::DFG::OSRExitCompiler::compileExit):
+        * dfg/DFGOSRExitCompiler64.cpp:
+        (JSC::DFG::OSRExitCompiler::compileExit):
+        * dfg/DFGPredictionPropagationPhase.cpp:
+        (JSC::DFG::PredictionPropagationPhase::speculatedDoubleTypeForPrediction):
+        (JSC::DFG::PredictionPropagationPhase::propagate):
+        * dfg/DFGSpeculativeJIT.cpp:
+        (JSC::DFG::SpeculativeJIT::emitAllocateJSArray):
+        (JSC::DFG::SpeculativeJIT::compileValueToInt32):
+        (JSC::DFG::SpeculativeJIT::compileGetByValOnFloatTypedArray):
+        * dfg/DFGSpeculativeJIT32_64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGSpeculativeJIT64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGVariableAccessData.h:
+        (JSC::DFG::VariableAccessData::makePredictionForDoubleFormat):
+        * ftl/FTLLowerDFGToLLVM.cpp:
+        (JSC::FTL::LowerDFGToLLVM::compileGetByVal):
+        (JSC::FTL::LowerDFGToLLVM::compilePutByVal):
+        (JSC::FTL::LowerDFGToLLVM::compileArrayPush):
+        (JSC::FTL::LowerDFGToLLVM::compileArrayPop):
+        (JSC::FTL::LowerDFGToLLVM::compileNewArrayWithSize):
+        (JSC::FTL::LowerDFGToLLVM::numberOrNotCellToInt32):
+        (JSC::FTL::LowerDFGToLLVM::allocateJSArray):
+        * ftl/FTLValueFormat.cpp:
+        (JSC::FTL::reboxAccordingToFormat):
+        * jit/AssemblyHelpers.cpp:
+        (JSC::AssemblyHelpers::purifyNaN):
+        (JSC::AssemblyHelpers::sanitizeDouble): Deleted.
+        * jit/AssemblyHelpers.h:
+        * jit/JITPropertyAccess.cpp:
+        (JSC::JIT::emitFloatTypedArrayGetByVal):
+        * runtime/DateConstructor.cpp:
+        (JSC::constructDate):
+        * runtime/DateInstanceCache.h:
+        (JSC::DateInstanceData::DateInstanceData):
+        (JSC::DateInstanceCache::reset):
+        * runtime/ExceptionHelpers.cpp:
+        (JSC::TerminatedExecutionError::defaultValue):
+        * runtime/JSArray.cpp:
+        (JSC::JSArray::setLength):
+        (JSC::JSArray::pop):
+        (JSC::JSArray::shiftCountWithAnyIndexingType):
+        (JSC::JSArray::sortVector):
+        (JSC::JSArray::compactForSorting):
+        * runtime/JSArray.h:
+        (JSC::JSArray::create):
+        (JSC::JSArray::tryCreateUninitialized):
+        * runtime/JSCJSValue.cpp:
+        (JSC::JSValue::toNumberSlowCase):
+        * runtime/JSCJSValue.h:
+        * runtime/JSCJSValueInlines.h:
+        (JSC::jsNaN):
+        (JSC::JSValue::JSValue):
+        (JSC::JSValue::getPrimitiveNumber):
+        * runtime/JSGlobalObjectFunctions.cpp:
+        (JSC::parseInt):
+        (JSC::jsStrDecimalLiteral):
+        (JSC::toDouble):
+        (JSC::jsToNumber):
+        (JSC::parseFloat):
+        * runtime/JSObject.cpp:
+        (JSC::JSObject::createInitialDouble):
+        (JSC::JSObject::convertUndecidedToDouble):
+        (JSC::JSObject::convertInt32ToDouble):
+        (JSC::JSObject::deletePropertyByIndex):
+        (JSC::JSObject::ensureLengthSlow):
+        * runtime/MathObject.cpp:
+        (JSC::mathProtoFuncMax):
+        (JSC::mathProtoFuncMin):
+        * runtime/PureNaN.h: Added.
+        (JSC::pureNaN):
+        (JSC::isImpureNaN):
+        (JSC::purifyNaN):
+        * runtime/TypedArrayAdaptors.h:
+        (JSC::FloatTypedArrayAdaptor::toJSValue):
+
 2014-04-16  Juergen Ributzka  <juergen@apple.com>
 
         Enable system library calls in FTL for ARM64
index 4ad6a15..64f7ec3 100644 (file)
                0F56A1D315000F35002992B1 /* ExecutionCounter.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F56A1D115000F31002992B1 /* ExecutionCounter.h */; settings = {ATTRIBUTES = (Private, ); }; };
                0F56A1D515001CF4002992B1 /* ExecutionCounter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F56A1D415001CF2002992B1 /* ExecutionCounter.cpp */; };
                0F572D4F16879FDD00E57FBD /* ThunkGenerator.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F572D4D16879FDB00E57FBD /* ThunkGenerator.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               0F5780A218FE1E98001E72D9 /* PureNaN.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F5780A118FE1E98001E72D9 /* PureNaN.h */; settings = {ATTRIBUTES = (Private, ); }; };
                0F5A52D017ADD717008ECB2D /* CopyToken.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F5A52CF17ADD717008ECB2D /* CopyToken.h */; settings = {ATTRIBUTES = (Private, ); }; };
                0F5A6283188C98D40072C9DF /* FTLValueRange.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F5A6281188C98D40072C9DF /* FTLValueRange.cpp */; };
                0F5A6284188C98D40072C9DF /* FTLValueRange.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F5A6282188C98D40072C9DF /* FTLValueRange.h */; settings = {ATTRIBUTES = (Private, ); }; };
                0F56A1D115000F31002992B1 /* ExecutionCounter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ExecutionCounter.h; sourceTree = "<group>"; };
                0F56A1D415001CF2002992B1 /* ExecutionCounter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ExecutionCounter.cpp; sourceTree = "<group>"; };
                0F572D4D16879FDB00E57FBD /* ThunkGenerator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ThunkGenerator.h; sourceTree = "<group>"; };
+               0F5780A118FE1E98001E72D9 /* PureNaN.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PureNaN.h; sourceTree = "<group>"; };
                0F5A52CF17ADD717008ECB2D /* CopyToken.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CopyToken.h; sourceTree = "<group>"; };
                0F5A6281188C98D40072C9DF /* FTLValueRange.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = FTLValueRange.cpp; path = ftl/FTLValueRange.cpp; sourceTree = "<group>"; };
                0F5A6282188C98D40072C9DF /* FTLValueRange.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FTLValueRange.h; path = ftl/FTLValueRange.h; sourceTree = "<group>"; };
                7EF6E0BB0EB7A1EC0079AFAF /* runtime */ = {
                        isa = PBXGroup;
                        children = (
-                               2AF7382A18BBBF92008A5A37 /* StructureIDTable.cpp */,
-                               2AF7382B18BBBF92008A5A37 /* StructureIDTable.h */,
                                BCF605110E203EF800B9A64D /* ArgList.cpp */,
                                BCF605120E203EF800B9A64D /* ArgList.h */,
                                BC257DE50E1F51C50016B6C9 /* Arguments.cpp */,
                                969A09220ED1E09C00F1F681 /* Completion.cpp */,
                                F5BB2BC5030F772101FCFE1D /* Completion.h */,
                                0FDB2CE9174896C7007B3C1B /* ConcurrentJITLock.h */,
-                               A53CE08918BC21C300BEDF76 /* ConsoleClient.h */,
                                A5B6A74C18C6DBA600F11E91 /* ConsoleClient.cpp */,
+                               A53CE08918BC21C300BEDF76 /* ConsoleClient.h */,
                                A53CE08118BC1A5600BEDF76 /* ConsolePrototype.cpp */,
                                A53CE08218BC1A5600BEDF76 /* ConsolePrototype.h */,
                                A5FD0071189B038C00633231 /* ConsoleTypes.h */,
                                BC7F8FBA0E19D1EF008632C0 /* JSCell.cpp */,
                                BC1167D80E19BCC9008066DD /* JSCell.h */,
                                0F97496F1687ADE200A4FF6A /* JSCellInlines.h */,
-                               A53CE08318BC1A5600BEDF76 /* JSConsole.cpp */,
-                               A53CE08418BC1A5600BEDF76 /* JSConsole.h */,
                                0F1DD84918A945BE0026F3FA /* JSCInlines.h */,
                                F692A8870255597D01FF60F7 /* JSCJSValue.cpp */,
                                14ABB36E099C076400E2A24F /* JSCJSValue.h */,
                                865A30F0135007E100CDB49E /* JSCJSValueInlines.h */,
+                               A53CE08318BC1A5600BEDF76 /* JSConsole.cpp */,
+                               A53CE08418BC1A5600BEDF76 /* JSConsole.h */,
                                0F2B66BD17B6B5AB00A7AE3F /* JSDataView.cpp */,
                                0F2B66BE17B6B5AB00A7AE3F /* JSDataView.h */,
                                0F2B66BF17B6B5AB00A7AE3F /* JSDataViewPrototype.cpp */,
                                65C02FBB0637462A003E7EE6 /* Protect.h */,
                                14D844A216AA2C7000A65AF0 /* PrototypeMap.cpp */,
                                14D844A316AA2C7000A65AF0 /* PrototypeMap.h */,
+                               0F5780A118FE1E98001E72D9 /* PureNaN.h */,
                                0F0CD4C015F1A6040032F1C0 /* PutDirectIndexMode.h */,
                                147B84620E6DE6B1004775A4 /* PutPropertySlot.h */,
                                F692A87D0255597D01FF60F7 /* RegExp.cpp */,
                                BCDE3AB10E6C82CF001453A7 /* Structure.h */,
                                7E4EE70E0EBB7A5B005934AA /* StructureChain.cpp */,
                                7E4EE7080EBB7963005934AA /* StructureChain.h */,
+                               2AAAA31018BD49D100394CC8 /* StructureIDBlob.h */,
+                               2AF7382A18BBBF92008A5A37 /* StructureIDTable.cpp */,
+                               2AF7382B18BBBF92008A5A37 /* StructureIDTable.h */,
                                0FD2C92316D01EE900C7803F /* StructureInlines.h */,
                                C2F0F2D016BAEEE900187C19 /* StructureRareData.cpp */,
                                C2FE18A316BAEC4000AF3061 /* StructureRareData.h */,
                                1420BE7A10AA6DDB00F455D2 /* WeakRandom.h */,
                                A7DCB77912E3D90500911940 /* WriteBarrier.h */,
                                C2B6D75218A33793004A9301 /* WriteBarrierInlines.h */,
-                               2AAAA31018BD49D100394CC8 /* StructureIDBlob.h */,
                        );
                        path = runtime;
                        sourceTree = "<group>";
                                BC18C43F0E16F5CD00B34460 /* Nodes.h in Headers */,
                                99E45A2818A1B2590026D88F /* NondeterministicInput.h in Headers */,
                                BC18C4410E16F5CD00B34460 /* NumberConstructor.h in Headers */,
+                               0F5780A218FE1E98001E72D9 /* PureNaN.h in Headers */,
                                BC18C4420E16F5CD00B34460 /* NumberConstructor.lut.h in Headers */,
                                BC18C4430E16F5CD00B34460 /* NumberObject.h in Headers */,
                                BC18C4440E16F5CD00B34460 /* NumberPrototype.h in Headers */,
index f4501dd..19aeab7 100644 (file)
@@ -161,8 +161,8 @@ void dumpSpeculation(PrintStream& out, SpeculatedType value)
     if (value & SpecInt52)
         myOut.print("Int52");
         
-    if ((value & SpecDouble) == SpecDouble)
-        myOut.print("Double");
+    if ((value & SpecBytecodeDouble) == SpecBytecodeDouble)
+        myOut.print("Bytecodedouble");
     else {
         if (value & SpecInt52AsDouble)
             myOut.print("Int52asdouble");
@@ -174,12 +174,15 @@ void dumpSpeculation(PrintStream& out, SpeculatedType value)
         else
             isTop = false;
         
-        if (value & SpecDoubleNaN)
-            myOut.print("Doublenan");
+        if (value & SpecDoublePureNaN)
+            myOut.print("Doublepurenan");
         else
             isTop = false;
     }
     
+    if (value & SpecDoubleImpureNaN)
+        out.print("Doubleimpurenan");
+    
     if (value & SpecBoolean)
         myOut.print("Bool");
     else
@@ -348,7 +351,7 @@ SpeculatedType speculationFromValue(JSValue value)
     if (value.isDouble()) {
         double number = value.asNumber();
         if (number != number)
-            return SpecDoubleNaN;
+            return SpecDoublePureNaN;
         if (value.isMachineInt())
             return SpecInt52AsDouble;
         return SpecNonIntAsDouble;
@@ -428,5 +431,92 @@ bool valuesCouldBeEqual(SpeculatedType a, SpeculatedType b)
     return !!(a & b);
 }
 
+SpeculatedType typeOfDoubleSum(SpeculatedType a, SpeculatedType b)
+{
+    SpeculatedType result = a | b;
+    // Impure NaN could become pure NaN during addition because addition may clear bits.
+    if (result & SpecDoubleImpureNaN)
+        result |= SpecDoublePureNaN;
+    // Values could overflow, or fractions could become integers.
+    if (result & SpecDoubleReal)
+        result |= SpecDoubleReal;
+    return result;
+}
+
+SpeculatedType typeOfDoubleDifference(SpeculatedType a, SpeculatedType b)
+{
+    return typeOfDoubleSum(a, b);
+}
+
+SpeculatedType typeOfDoubleProduct(SpeculatedType a, SpeculatedType b)
+{
+    return typeOfDoubleSum(a, b);
+}
+
+static SpeculatedType polluteDouble(SpeculatedType value)
+{
+    // Impure NaN could become pure NaN because the operation could clear some bits.
+    if (value & SpecDoubleImpureNaN)
+        value |= SpecDoubleNaN;
+    // Values could overflow, fractions could become integers, or an error could produce
+    // PureNaN.
+    if (value & SpecDoubleReal)
+        value |= SpecDoubleReal | SpecDoublePureNaN;
+    return value;
+}
+
+SpeculatedType typeOfDoubleQuotient(SpeculatedType a, SpeculatedType b)
+{
+    return polluteDouble(a | b);
+}
+
+SpeculatedType typeOfDoubleMinMax(SpeculatedType a, SpeculatedType b)
+{
+    SpeculatedType result = a | b;
+    // Impure NaN could become pure NaN during addition because addition may clear bits.
+    if (result & SpecDoubleImpureNaN)
+        result |= SpecDoublePureNaN;
+    return result;
+}
+
+SpeculatedType typeOfDoubleNegation(SpeculatedType value)
+{
+    // Impure NaN could become pure NaN because bits might get cleared.
+    if (value & SpecDoubleImpureNaN)
+        value |= SpecDoublePureNaN;
+    // We could get negative zero, which mixes SpecInt52AsDouble and SpecNotIntAsDouble.
+    // We could also overflow a large negative int into something that is no longer
+    // representable as an int.
+    if (value & SpecDoubleReal)
+        value |= SpecDoubleReal;
+    return value;
+}
+
+SpeculatedType typeOfDoubleAbs(SpeculatedType value)
+{
+    return typeOfDoubleNegation(value);
+}
+
+SpeculatedType typeOfDoubleFRound(SpeculatedType value)
+{
+    // We might lose bits, which leads to a NaN being purified.
+    if (value & SpecDoubleImpureNaN)
+        value |= SpecDoublePureNaN;
+    // We might lose bits, which leads to a value becoming integer-representable.
+    if (value & SpecNonIntAsDouble)
+        value |= SpecInt52AsDouble;
+    return value;
+}
+
+SpeculatedType typeOfDoubleBinaryOp(SpeculatedType a, SpeculatedType b)
+{
+    return polluteDouble(a | b);
+}
+
+SpeculatedType typeOfDoubleUnaryOp(SpeculatedType value)
+{
+    return polluteDouble(value);
+}
+
 } // namespace JSC
 
index a1e2c8f..d6cc1c0 100644 (file)
@@ -68,18 +68,21 @@ static const SpeculatedType SpecInt52AsDouble      = 0x00800000; // It's definit
 static const SpeculatedType SpecInteger            = 0x00e00000; // It's definitely some kind of integer.
 static const SpeculatedType SpecNonIntAsDouble     = 0x01000000; // It's definitely not an Int52 but it's a real number and it's a double.
 static const SpeculatedType SpecDoubleReal         = 0x01800000; // It's definitely a non-NaN double.
-static const SpeculatedType SpecDoubleNaN          = 0x02000000; // It's definitely a NaN.
-static const SpeculatedType SpecDouble             = 0x03800000; // It's either a non-NaN or a NaN double.
+static const SpeculatedType SpecDoublePureNaN      = 0x02000000; // It's definitely a NaN that is sae to tag (i.e. pure).
+static const SpeculatedType SpecDoubleImpureNaN    = 0x04000000; // It's definitely a NaN that is unsafe to tag (i.e. impure).
+static const SpeculatedType SpecDoubleNaN          = 0x06000000; // It's definitely some kind of NaN.
+static const SpeculatedType SpecBytecodeDouble     = 0x03800000; // It's either a non-NaN or a NaN double, but it's definitely not impure NaN.
+static const SpeculatedType SpecDouble             = 0x07800000; // It's either a non-NaN or a NaN double.
 static const SpeculatedType SpecBytecodeRealNumber = 0x01a00000; // It's either an Int32 or a DoubleReal.
 static const SpeculatedType SpecFullRealNumber     = 0x01e00000; // It's either an Int32 or a DoubleReal, or a Int52.
-static const SpeculatedType SpecBytecodeNumber     = 0x03a00000; // It's either an Int32 or a Double.
-static const SpeculatedType SpecFullNumber         = 0x03e00000; // It's either an Int32, Int52, or a Double.
+static const SpeculatedType SpecBytecodeNumber     = 0x03a00000; // It's either an Int32 or a Double, and the Double cannot be an impure NaN.
+static const SpeculatedType SpecFullNumber         = 0x07e00000; // It's either an Int32, Int52, or a Double, and the Double can be impure NaN.
 static const SpeculatedType SpecBoolean            = 0x10000000; // It's definitely a Boolean.
 static const SpeculatedType SpecOther              = 0x20000000; // It's definitely either Null or Undefined.
 static const SpeculatedType SpecMisc               = 0x30000000; // It's definitely either a boolean, Null, or Undefined.
-static const SpeculatedType SpecHeapTop            = 0x3fbfffff; // It can be any of the above, except for SpecInt52.
+static const SpeculatedType SpecHeapTop            = 0x3bbfffff; // It can be any of the above, except for SpecInt52.
 static const SpeculatedType SpecEmpty              = 0x40000000; // It's definitely an empty value marker.
-static const SpeculatedType SpecBytecodeTop        = 0x7fbfffff; // It can be any of the above, except for SpecInt52.
+static const SpeculatedType SpecBytecodeTop        = 0x7bbfffff; // It can be any of the above, except for SpecInt52.
 static const SpeculatedType SpecFullTop            = 0x7fffffff; // It can be any of the above plus anything the DFG chooses.
 
 typedef bool (*SpeculatedTypeChecker)(SpeculatedType);
@@ -397,6 +400,22 @@ SpeculatedType leastUpperBoundOfStrictlyEquivalentSpeculations(SpeculatedType);
 
 bool valuesCouldBeEqual(SpeculatedType, SpeculatedType);
 
+// Precise computation of the type of the result of a double computation after we
+// already know that the inputs are doubles and that the result must be a double. Use
+// the closest one of these that applies.
+SpeculatedType typeOfDoubleSum(SpeculatedType, SpeculatedType);
+SpeculatedType typeOfDoubleDifference(SpeculatedType, SpeculatedType);
+SpeculatedType typeOfDoubleProduct(SpeculatedType, SpeculatedType);
+SpeculatedType typeOfDoubleQuotient(SpeculatedType, SpeculatedType);
+SpeculatedType typeOfDoubleMinMax(SpeculatedType, SpeculatedType);
+SpeculatedType typeOfDoubleNegation(SpeculatedType);
+SpeculatedType typeOfDoubleAbs(SpeculatedType);
+SpeculatedType typeOfDoubleFRound(SpeculatedType);
+
+// This conservatively models the behavior of arbitrary double operations.
+SpeculatedType typeOfDoubleBinaryOp(SpeculatedType, SpeculatedType);
+SpeculatedType typeOfDoubleUnaryOp(SpeculatedType);
+
 } // namespace JSC
 
 #endif // SpeculatedType_h
index 777e844..2584819 100644 (file)
@@ -255,7 +255,7 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
                 setConstant(node, jsNumber(value));
                 break;
             }
-            forNode(node).setType(SpecDouble);
+            forNode(node).setType(SpecInt52AsDouble);
             break;
         }
         if (child && child.isInt32()) {
@@ -390,11 +390,9 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
                 setConstant(node, jsDoubleNumber(left.asNumber() + right.asNumber()));
                 break;
             }
-            if (isFullRealNumberSpeculation(forNode(node->child1()).m_type)
-                && isFullRealNumberSpeculation(forNode(node->child2()).m_type))
-                forNode(node).setType(SpecDoubleReal);
-            else
-                forNode(node).setType(SpecDouble);
+            forNode(node).setType(
+                typeOfDoubleSum(
+                    forNode(node->child1()).m_type, forNode(node->child2()).m_type));
             break;
         default:
             RELEASE_ASSERT_NOT_REACHED();
@@ -447,7 +445,9 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
                 setConstant(node, jsDoubleNumber(left.asNumber() - right.asNumber()));
                 break;
             }
-            forNode(node).setType(SpecDouble);
+            forNode(node).setType(
+                typeOfDoubleDifference(
+                    forNode(node->child1()).m_type, forNode(node->child2()).m_type));
             break;
         default:
             RELEASE_ASSERT_NOT_REACHED();
@@ -504,7 +504,9 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
                 setConstant(node, jsDoubleNumber(-child.asNumber()));
                 break;
             }
-            forNode(node).setType(SpecDouble);
+            forNode(node).setType(
+                typeOfDoubleNegation(
+                    forNode(node->child1()).m_type));
             break;
         default:
             RELEASE_ASSERT_NOT_REACHED();
@@ -555,11 +557,9 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
                 setConstant(node, jsDoubleNumber(left.asNumber() * right.asNumber()));
                 break;
             }
-            if (isFullRealNumberSpeculation(forNode(node->child1()).m_type)
-                || isFullRealNumberSpeculation(forNode(node->child2()).m_type))
-                forNode(node).setType(SpecDoubleReal);
-            else
-                forNode(node).setType(SpecDouble);
+            forNode(node).setType(
+                typeOfDoubleProduct(
+                    forNode(node->child1()).m_type, forNode(node->child2()).m_type));
             break;
         default:
             RELEASE_ASSERT_NOT_REACHED();
@@ -593,7 +593,9 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
                 setConstant(node, jsDoubleNumber(left.asNumber() / right.asNumber()));
                 break;
             }
-            forNode(node).setType(SpecDouble);
+            forNode(node).setType(
+                typeOfDoubleQuotient(
+                    forNode(node->child1()).m_type, forNode(node->child2()).m_type));
             break;
         default:
             RELEASE_ASSERT_NOT_REACHED();
@@ -627,7 +629,9 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
                 setConstant(node, jsDoubleNumber(fmod(left.asNumber(), right.asNumber())));
                 break;
             }
-            forNode(node).setType(SpecDouble);
+            forNode(node).setType(
+                typeOfDoubleBinaryOp(
+                    forNode(node->child1()).m_type, forNode(node->child2()).m_type));
             break;
         default:
             RELEASE_ASSERT_NOT_REACHED();
@@ -655,7 +659,9 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
                 setConstant(node, jsDoubleNumber(a < b ? a : (b <= a ? b : a + b)));
                 break;
             }
-            forNode(node).setType(SpecDouble);
+            forNode(node).setType(
+                typeOfDoubleMinMax(
+                    forNode(node->child1()).m_type, forNode(node->child2()).m_type));
             break;
         default:
             RELEASE_ASSERT_NOT_REACHED();
@@ -683,7 +689,9 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
                 setConstant(node, jsDoubleNumber(a > b ? a : (b >= a ? b : a + b)));
                 break;
             }
-            forNode(node).setType(SpecDouble);
+            forNode(node).setType(
+                typeOfDoubleMinMax(
+                    forNode(node->child1()).m_type, forNode(node->child2()).m_type));
             break;
         default:
             RELEASE_ASSERT_NOT_REACHED();
@@ -711,7 +719,7 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
                 setConstant(node, jsDoubleNumber(child.asNumber()));
                 break;
             }
-            forNode(node).setType(SpecDouble);
+            forNode(node).setType(typeOfDoubleAbs(forNode(node->child1()).m_type));
             break;
         default:
             RELEASE_ASSERT_NOT_REACHED();
@@ -726,7 +734,7 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
             setConstant(node, jsDoubleNumber(sqrt(child.asNumber())));
             break;
         }
-        forNode(node).setType(SpecDouble);
+        forNode(node).setType(typeOfDoubleUnaryOp(forNode(node->child1()).m_type));
         break;
     }
         
@@ -736,7 +744,7 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
             setConstant(node, jsDoubleNumber(static_cast<float>(child.asNumber())));
             break;
         }
-        forNode(node).setType(SpecDouble);
+        forNode(node).setType(typeOfDoubleFRound(forNode(node->child1()).m_type));
         break;
     }
         
@@ -746,7 +754,7 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
             setConstant(node, jsDoubleNumber(sin(child.asNumber())));
             break;
         }
-        forNode(node).setType(SpecDouble);
+        forNode(node).setType(typeOfDoubleUnaryOp(forNode(node->child1()).m_type));
         break;
     }
     
@@ -756,7 +764,7 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
             setConstant(node, jsDoubleNumber(cos(child.asNumber())));
             break;
         }
-        forNode(node).setType(SpecDouble);
+        forNode(node).setType(typeOfDoubleUnaryOp(forNode(node->child1()).m_type));
         break;
     }
             
@@ -1046,7 +1054,7 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
                 clobberWorld(node->origin.semantic, clobberLimit);
                 forNode(node).makeHeapTop();
             } else if (node->arrayMode().isSaneChain())
-                forNode(node).setType(SpecDouble);
+                forNode(node).setType(SpecBytecodeDouble);
             else
                 forNode(node).setType(SpecDoubleReal);
             break;
@@ -1081,13 +1089,13 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
             else if (enableInt52() && node->shouldSpeculateMachineInt())
                 forNode(node).setType(SpecInt52);
             else
-                forNode(node).setType(SpecDouble);
+                forNode(node).setType(SpecInt52AsDouble);
             break;
         case Array::Float32Array:
-            forNode(node).setType(SpecDouble);
+            forNode(node).setType(SpecBytecodeDouble);
             break;
         case Array::Float64Array:
-            forNode(node).setType(SpecDouble);
+            forNode(node).setType(SpecBytecodeDouble);
             break;
         default:
             RELEASE_ASSERT_NOT_REACHED();
index 25998cd..9b6b152 100644 (file)
@@ -1486,7 +1486,7 @@ bool ByteCodeParser::handleInlining(Node* callTargetNode, int resultOperand, con
     
 
     // Need to create a new basic block for the continuation at the caller.
-    RefPtr<BasicBlock> block = adoptRef(new BasicBlock(nextOffset, m_numArguments, m_numLocals, QNaN));
+    RefPtr<BasicBlock> block = adoptRef(new BasicBlock(nextOffset, m_numArguments, m_numLocals, PNaN));
 
     // Link the early returns to the basic block we're about to create.
     for (size_t i = 0; i < inlineStackEntry.m_unlinkedBlocks.size(); ++i) {
@@ -3578,7 +3578,7 @@ void ByteCodeParser::parseCodeBlock()
                     m_currentBlock = m_graph.lastBlock();
                     m_currentBlock->bytecodeBegin = m_currentIndex;
                 } else {
-                    RefPtr<BasicBlock> block = adoptRef(new BasicBlock(m_currentIndex, m_numArguments, m_numLocals, QNaN));
+                    RefPtr<BasicBlock> block = adoptRef(new BasicBlock(m_currentIndex, m_numArguments, m_numLocals, PNaN));
                     m_currentBlock = block.get();
                     // This assertion checks two things:
                     // 1) If the bytecodeBegin is greater than currentIndex, then something has gone
index 9720a55..1656774 100644 (file)
@@ -75,7 +75,7 @@ private:
     {
         // Note that we pass NaN for the count of the critical edge block, because we honestly
         // don't know its execution frequency.
-        BasicBlock* pad = m_insertionSet.insertBefore(*successor, QNaN);
+        BasicBlock* pad = m_insertionSet.insertBefore(*successor, PNaN);
         pad->appendNode(
             m_graph, SpecNone, Jump, (*successor)->at(0)->origin, OpInfo(*successor));
         pad->predecessors.append(predecessor);
index b87365f..f872a78 100644 (file)
@@ -286,14 +286,8 @@ bool InPlaceAbstractState::mergeStateAtTail(AbstractValue& destination, Abstract
             // The block sets the variable, and potentially refines it, both
             // before and after setting it.
             source = forNode(node->child1());
-            if (node->variableAccessData()->flushFormat() == FlushedDouble) {
-                ASSERT(!(source.m_type & ~SpecFullNumber));
-                ASSERT(!!(source.m_type & ~SpecDouble) == !!(source.m_type & SpecMachineInt));
-                if (!(source.m_type & ~SpecDouble)) {
-                    source.merge(SpecInt52AsDouble);
-                    source.filter(SpecDouble);
-                }
-            }
+            if (node->variableAccessData()->flushFormat() == FlushedDouble)
+                RELEASE_ASSERT(!(source.m_type & ~SpecDouble));
             break;
         
         default:
index 6d96d11..b11b2db 100644 (file)
@@ -40,7 +40,7 @@ namespace JSC { namespace DFG {
 BasicBlock* createPreHeader(Graph& graph, BlockInsertionSet& insertionSet, BasicBlock* block)
 {
     // Don't bother to preserve execution frequencies for now.
-    BasicBlock* preHeader = insertionSet.insertBefore(block, QNaN);
+    BasicBlock* preHeader = insertionSet.insertBefore(block, PNaN);
     preHeader->appendNode(
         graph, SpecNone, Jump, block->at(0)->origin, OpInfo(block));
     
index 25cef02..6d470dd 100644 (file)
@@ -89,13 +89,13 @@ struct NewArrayBufferData {
 struct BranchTarget {
     BranchTarget()
         : block(0)
-        , count(QNaN)
+        , count(PNaN)
     {
     }
     
     explicit BranchTarget(BasicBlock* block)
         : block(block)
-        , count(QNaN)
+        , count(PNaN)
     {
     }
     
index 13b1de5..af87ebd 100644 (file)
@@ -83,7 +83,7 @@ public:
         
         BlockInsertionSet insertionSet(m_graph);
         
-        BasicBlock* newRoot = insertionSet.insert(0, QNaN);
+        BasicBlock* newRoot = insertionSet.insert(0, PNaN);
         NodeOrigin origin = target->at(0)->origin;
         
         Vector<Node*> locals(baseline->m_numCalleeRegisters);
index 60b2858..17f569d 100644 (file)
@@ -288,7 +288,7 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, const Operands<ValueRecov
         case DoubleDisplacedInJSStack:
             m_jit.move(AssemblyHelpers::TrustedImmPtr(scratch + index), GPRInfo::regT0);
             m_jit.loadDouble(GPRInfo::regT0, FPRInfo::fpRegT0);
-            m_jit.sanitizeDouble(FPRInfo::fpRegT0);
+            m_jit.purifyNaN(FPRInfo::fpRegT0);
             m_jit.storeDouble(FPRInfo::fpRegT0, AssemblyHelpers::addressFor(operand));
             break;
 
index fd70159..33e4c39 100644 (file)
@@ -297,7 +297,7 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, const Operands<ValueRecov
         case DoubleDisplacedInJSStack:
             m_jit.move(AssemblyHelpers::TrustedImmPtr(scratch + index), GPRInfo::regT0);
             m_jit.loadDouble(GPRInfo::regT0, FPRInfo::fpRegT0);
-            m_jit.sanitizeDouble(FPRInfo::fpRegT0);
+            m_jit.purifyNaN(FPRInfo::fpRegT0);
             m_jit.boxDouble(FPRInfo::fpRegT0, GPRInfo::regT0);
             m_jit.store64(GPRInfo::regT0, AssemblyHelpers::addressFor(operand));
             break;
index 87db750..a969e24 100644 (file)
@@ -113,11 +113,14 @@ private:
     
     SpeculatedType speculatedDoubleTypeForPrediction(SpeculatedType value)
     {
+        SpeculatedType result = SpecDoubleReal;
+        if (value & SpecDoubleImpureNaN)
+            result |= SpecDoubleImpureNaN;
+        if (value & SpecDoublePureNaN)
+            result |= SpecDoublePureNaN;
         if (!isFullNumberSpeculation(value))
-            return SpecDouble;
-        if (value & SpecDoubleNaN)
-            return SpecDouble;
-        return SpecDoubleReal;
+            result |= SpecDoublePureNaN;
+        return result;
     }
 
     SpeculatedType speculatedDoubleTypeForPredictions(SpeculatedType left, SpeculatedType right)
@@ -216,7 +219,7 @@ private:
                     // left or right is definitely something other than a number.
                     changed |= mergePrediction(SpecString);
                 } else
-                    changed |= mergePrediction(SpecString | SpecInt32 | SpecDouble);
+                    changed |= mergePrediction(SpecString | SpecInt32 | SpecBytecodeDouble);
             }
             break;
         }
@@ -301,7 +304,7 @@ private:
                     && nodeCanSpeculateInt32(node->arithNodeFlags()))
                     changed |= mergePrediction(SpecInt32);
                 else
-                    changed |= mergePrediction(SpecDouble);
+                    changed |= mergePrediction(SpecBytecodeDouble);
             }
             break;
         }
@@ -315,7 +318,7 @@ private:
                     && nodeCanSpeculateInt32(node->arithNodeFlags()))
                     changed |= mergePrediction(SpecInt32);
                 else
-                    changed |= mergePrediction(SpecDouble);
+                    changed |= mergePrediction(SpecBytecodeDouble);
             }
             break;
         }
@@ -324,7 +327,7 @@ private:
         case ArithFRound:
         case ArithSin:
         case ArithCos: {
-            changed |= setPrediction(SpecDouble);
+            changed |= setPrediction(SpecBytecodeDouble);
             break;
         }
             
index c92ff69..b5378a0 100644 (file)
@@ -84,12 +84,12 @@ void SpeculativeJIT::emitAllocateJSArray(GPRReg resultGPR, Structure* structure,
     
     if (hasDouble(structure->indexingType()) && numElements < vectorLength) {
 #if USE(JSVALUE64)
-        m_jit.move(TrustedImm64(bitwise_cast<int64_t>(QNaN)), scratchGPR);
+        m_jit.move(TrustedImm64(bitwise_cast<int64_t>(PNaN)), scratchGPR);
         for (unsigned i = numElements; i < vectorLength; ++i)
             m_jit.store64(scratchGPR, MacroAssembler::Address(storageGPR, sizeof(double) * i));
 #else
         EncodedValueDescriptor value;
-        value.asInt64 = JSValue::encode(JSValue(JSValue::EncodeAsDouble, QNaN));
+        value.asInt64 = JSValue::encode(JSValue(JSValue::EncodeAsDouble, PNaN));
         for (unsigned i = numElements; i < vectorLength; ++i) {
             m_jit.store32(TrustedImm32(value.asBits.tag), MacroAssembler::Address(storageGPR, sizeof(double) * i + OBJECT_OFFSETOF(JSValue, u.asBits.tag)));
             m_jit.store32(TrustedImm32(value.asBits.payload), MacroAssembler::Address(storageGPR, sizeof(double) * i + OBJECT_OFFSETOF(JSValue, u.asBits.payload)));
@@ -1891,7 +1891,7 @@ void SpeculativeJIT::compileValueToInt32(Node* node)
 
             if (node->child1().useKind() == NumberUse) {
                 DFG_TYPE_CHECK(
-                    JSValueRegs(gpr), node->child1(), SpecFullNumber,
+                    JSValueRegs(gpr), node->child1(), SpecBytecodeNumber,
                     m_jit.branchTest64(
                         MacroAssembler::Zero, gpr, GPRInfo::tagTypeNumberRegister));
             } else {
@@ -1945,7 +1945,7 @@ void SpeculativeJIT::compileValueToInt32(Node* node)
 
                 if (node->child1().useKind() == NumberUse) {
                     DFG_TYPE_CHECK(
-                        op1.jsValueRegs(), node->child1(), SpecFullNumber,
+                        op1.jsValueRegs(), node->child1(), SpecBytecodeNumber,
                         m_jit.branch32(
                             MacroAssembler::AboveOrEqual, tagGPR,
                             TrustedImm32(JSValue::LowestTag)));
@@ -2507,7 +2507,7 @@ void SpeculativeJIT::compileGetByValOnFloatTypedArray(Node* node, TypedArrayType
         RELEASE_ASSERT_NOT_REACHED();
     }
     
-    m_jit.sanitizeDouble(resultReg);
+    m_jit.purifyNaN(resultReg);
     
     doubleResult(resultReg, node);
 }
index 0dd9004..eca4701 100644 (file)
@@ -2718,7 +2718,7 @@ void SpeculativeJIT::compile(Node* node)
             FPRReg valueFPR = value.fpr();
 
             DFG_TYPE_CHECK(
-                JSValueRegs(), node->child2(), SpecFullRealNumber,
+                JSValueRegs(), node->child2(), SpecDoubleReal,
                 m_jit.branchDouble(MacroAssembler::DoubleNotEqualOrUnordered, valueFPR, valueFPR));
             
             m_jit.load32(MacroAssembler::Address(storageGPR, Butterfly::offsetOfPublicLength()), storageLengthGPR);
@@ -2834,7 +2834,7 @@ void SpeculativeJIT::compile(Node* node)
                 MacroAssembler::BaseIndex(storageGPR, valuePayloadGPR, MacroAssembler::TimesEight),
                 tempFPR);
             MacroAssembler::Jump slowCase = m_jit.branchDouble(MacroAssembler::DoubleNotEqualOrUnordered, tempFPR, tempFPR);
-            JSValue nan = JSValue(JSValue::EncodeAsDouble, QNaN);
+            JSValue nan = JSValue(JSValue::EncodeAsDouble, PNaN);
             m_jit.store32(
                 MacroAssembler::TrustedImm32(nan.u.asBits.tag),
                 MacroAssembler::BaseIndex(storageGPR, valuePayloadGPR, MacroAssembler::TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.tag)));
@@ -3064,7 +3064,7 @@ void SpeculativeJIT::compile(Node* node)
                     SpeculateDoubleOperand operand(this, use);
                     FPRReg opFPR = operand.fpr();
                     DFG_TYPE_CHECK(
-                        JSValueRegs(), use, SpecFullRealNumber,
+                        JSValueRegs(), use, SpecDoubleReal,
                         m_jit.branchDouble(MacroAssembler::DoubleNotEqualOrUnordered, opFPR, opFPR));
         
                     m_jit.storeDouble(opFPR, MacroAssembler::Address(storageGPR, sizeof(double) * operandIdx));
@@ -3227,7 +3227,7 @@ void SpeculativeJIT::compile(Node* node)
             m_jit.store32(sizeGPR, MacroAssembler::Address(storageGPR, Butterfly::offsetOfVectorLength()));
             
             if (hasDouble(node->indexingType())) {
-                JSValue nan = JSValue(JSValue::EncodeAsDouble, QNaN);
+                JSValue nan = JSValue(JSValue::EncodeAsDouble, PNaN);
                 
                 m_jit.move(sizeGPR, scratchGPR);
                 MacroAssembler::Jump done = m_jit.branchTest32(MacroAssembler::Zero, scratchGPR);
index 2786730..7ffca88 100644 (file)
@@ -2868,7 +2868,7 @@ void SpeculativeJIT::compile(Node* node)
             FPRReg valueFPR = value.fpr();
 
             DFG_TYPE_CHECK(
-                JSValueRegs(), node->child2(), SpecFullRealNumber,
+                JSValueRegs(), node->child2(), SpecDoubleReal,
                 m_jit.branchDouble(MacroAssembler::DoubleNotEqualOrUnordered, valueFPR, valueFPR));
             
             m_jit.load32(MacroAssembler::Address(storageGPR, Butterfly::offsetOfPublicLength()), storageLengthGPR);
@@ -2955,7 +2955,7 @@ void SpeculativeJIT::compile(Node* node)
                 // FIXME: This would not have to be here if changing the publicLength also zeroed the values between the old
                 // length and the new length.
                 m_jit.store64(
-                    MacroAssembler::TrustedImm64(bitwise_cast<int64_t>(QNaN)), MacroAssembler::BaseIndex(storageGPR, storageLengthGPR, MacroAssembler::TimesEight));
+                    MacroAssembler::TrustedImm64(bitwise_cast<int64_t>(PNaN)), MacroAssembler::BaseIndex(storageGPR, storageLengthGPR, MacroAssembler::TimesEight));
                 slowCase = m_jit.branchDouble(MacroAssembler::DoubleNotEqualOrUnordered, tempFPR, tempFPR);
                 boxDouble(tempFPR, valueGPR);
             } else {
@@ -3160,7 +3160,7 @@ void SpeculativeJIT::compile(Node* node)
                     SpeculateDoubleOperand operand(this, use);
                     FPRReg opFPR = operand.fpr();
                     DFG_TYPE_CHECK(
-                        JSValueRegs(), use, SpecFullRealNumber,
+                        JSValueRegs(), use, SpecDoubleReal,
                         m_jit.branchDouble(
                             MacroAssembler::DoubleNotEqualOrUnordered, opFPR, opFPR));
                     m_jit.storeDouble(opFPR, MacroAssembler::Address(storageGPR, sizeof(double) * operandIdx));
@@ -3225,7 +3225,7 @@ void SpeculativeJIT::compile(Node* node)
                 FPRReg opFPR = operand.fpr();
                 GPRReg scratchGPR = scratch.gpr();
                 DFG_TYPE_CHECK(
-                    JSValueRegs(), use, SpecFullRealNumber,
+                    JSValueRegs(), use, SpecDoubleReal,
                     m_jit.branchDouble(
                         MacroAssembler::DoubleNotEqualOrUnordered, opFPR, opFPR));
                 m_jit.boxDouble(opFPR, scratchGPR);
@@ -3326,7 +3326,7 @@ void SpeculativeJIT::compile(Node* node)
             m_jit.store32(sizeGPR, MacroAssembler::Address(storageGPR, Butterfly::offsetOfVectorLength()));
             
             if (hasDouble(node->indexingType())) {
-                m_jit.move(TrustedImm64(bitwise_cast<int64_t>(QNaN)), scratchGPR);
+                m_jit.move(TrustedImm64(bitwise_cast<int64_t>(PNaN)), scratchGPR);
                 m_jit.move(sizeGPR, scratch2GPR);
                 MacroAssembler::Jump done = m_jit.branchTest32(MacroAssembler::Zero, scratch2GPR);
                 MacroAssembler::Label loop = m_jit.label();
index 03a7d3f..960def2 100644 (file)
@@ -314,7 +314,12 @@ public:
         if (m_doubleFormatState != UsingDoubleFormat)
             return false;
         
-        return mergeSpeculation(m_prediction, SpecDouble);
+        SpeculatedType type = m_prediction;
+        if (type & ~SpecBytecodeNumber)
+            type |= SpecDoublePureNaN;
+        if (type & SpecMachineInt)
+            type |= SpecInt52AsDouble;
+        return checkAndSet(m_prediction, type);
     }
     
     NodeFlags flags() const { return m_flags; }
index 3631ae5..3a30477 100644 (file)
@@ -2128,7 +2128,7 @@ private:
                 }
                 
                 result = m_out.select(
-                    m_out.doubleEqual(result, result), result, m_out.constDouble(QNaN));
+                    m_out.doubleEqual(result, result), result, m_out.constDouble(PNaN));
                 setDouble(result);
                 return;
             }
@@ -2215,7 +2215,7 @@ private:
                 LValue value = lowDouble(child3);
                 
                 FTL_TYPE_CHECK(
-                    doubleValue(value), child3, SpecFullRealNumber,
+                    doubleValue(value), child3, SpecDoubleReal,
                     m_out.doubleNotEqualOrUnordered(value, value));
                 
                 TypedPointer elementPointer = m_out.baseIndex(
@@ -2414,7 +2414,7 @@ private:
             } else {
                 value = lowDouble(m_node->child2());
                 FTL_TYPE_CHECK(
-                    doubleValue(value), m_node->child2(), SpecFullRealNumber,
+                    doubleValue(value), m_node->child2(), SpecDoubleReal,
                     m_out.doubleNotEqualOrUnordered(value, value));
                 refType = m_out.refDouble;
             }
@@ -2499,7 +2499,7 @@ private:
                     m_out.notZero64(result), usually(continuation), rarely(slowCase));
             } else {
                 LValue result = m_out.loadDouble(pointer);
-                m_out.store64(m_out.constInt64(bitwise_cast<int64_t>(QNaN)), pointer);
+                m_out.store64(m_out.constInt64(bitwise_cast<int64_t>(PNaN)), pointer);
                 results.append(m_out.anchor(boxDouble(result)));
                 m_out.branch(
                     m_out.doubleEqual(result, result),
@@ -2730,7 +2730,7 @@ private:
                 LValue pointer = m_out.phi(m_out.intPtr, originalPointer);
                 
                 m_out.store64(
-                    m_out.constInt64(bitwise_cast<int64_t>(QNaN)),
+                    m_out.constInt64(bitwise_cast<int64_t>(PNaN)),
                     TypedPointer(m_heaps.indexedDoubleProperties.atAnyIndex(), pointer));
                 
                 LValue nextIndex = m_out.sub(index, m_out.int32One);
@@ -3982,7 +3982,7 @@ private:
         
         if (edge.useKind() == NumberUse) {
             m_out.appendTo(notIntCase, continuation);
-            FTL_TYPE_CHECK(jsValueValue(value), edge, SpecFullNumber, isCellOrMisc(value));
+            FTL_TYPE_CHECK(jsValueValue(value), edge, SpecBytecodeNumber, isCellOrMisc(value));
             results.append(m_out.anchor(doubleToInt32(unboxDouble(value))));
             m_out.jump(continuation);
         } else {
@@ -4386,7 +4386,7 @@ private:
         if (hasDouble(structure->indexingType())) {
             for (unsigned i = numElements; i < vectorLength; ++i) {
                 m_out.store64(
-                    m_out.constInt64(bitwise_cast<int64_t>(QNaN)),
+                    m_out.constInt64(bitwise_cast<int64_t>(PNaN)),
                     butterfly, m_heaps.indexedDoubleProperties[i]);
             }
         }
index 133ef86..45fdc09 100644 (file)
@@ -71,7 +71,7 @@ void reboxAccordingToFormat(
     case ValueFormatDouble: {
         jit.moveDoubleTo64(FPRInfo::fpRegT0, scratch1);
         jit.move64ToDouble(value, FPRInfo::fpRegT0);
-        jit.sanitizeDouble(FPRInfo::fpRegT0);
+        jit.purifyNaN(FPRInfo::fpRegT0);
         jit.boxDouble(FPRInfo::fpRegT0, value);
         jit.move64ToDouble(scratch1, FPRInfo::fpRegT0);
         break;
index 2697092..76dcbfe 100644 (file)
@@ -54,10 +54,10 @@ Vector<BytecodeAndMachineOffset>& AssemblyHelpers::decodedCodeMapFor(CodeBlock*
     return result.iterator->value;
 }
 
-void AssemblyHelpers::sanitizeDouble(FPRReg fpr)
+void AssemblyHelpers::purifyNaN(FPRReg fpr)
 {
     MacroAssembler::Jump notNaN = branchDouble(DoubleEqual, fpr, fpr);
-    static const double NaN = QNaN;
+    static const double NaN = PNaN;
     loadDouble(&NaN, fpr);
     notNaN.link(this);
 }
index a75e5f7..80ae7ff 100644 (file)
@@ -418,7 +418,7 @@ public:
     void jitAssertArgumentCountSane() { }
 #endif
     
-    void sanitizeDouble(FPRReg);
+    void purifyNaN(FPRReg);
 
     // These methods convert between doubles, and doubles boxed and JSValues.
 #if USE(JSVALUE64)
index 49ef7d8..b5d8c5e 100644 (file)
@@ -1258,7 +1258,7 @@ JIT::JumpList JIT::emitFloatTypedArrayGetByVal(Instruction*, PatchableJump& badT
     }
     
     Jump notNaN = branchDouble(DoubleEqual, fpRegT0, fpRegT0);
-    static const double NaN = QNaN;
+    static const double NaN = PNaN;
     loadDouble(&NaN, fpRegT0);
     notNaN.link(this);
     
index 2bbdd39..2315554 100644 (file)
@@ -154,7 +154,7 @@ JSObject* constructDate(ExecState* exec, JSGlobalObject* globalObject, const Arg
             || (numArgs >= 5 && (!std::isfinite(doubleArguments[4]) || (doubleArguments[4] > INT_MAX) || (doubleArguments[4] < INT_MIN)))
             || (numArgs >= 6 && (!std::isfinite(doubleArguments[5]) || (doubleArguments[5] > INT_MAX) || (doubleArguments[5] < INT_MIN)))
             || (numArgs >= 7 && (!std::isfinite(doubleArguments[6]) || (doubleArguments[6] > INT_MAX) || (doubleArguments[6] < INT_MIN))))
-            value = QNaN;
+            value = PNaN;
         else {
             GregorianDateTime t;
             int year = JSC::toInt32(doubleArguments[0]);
index 1c8c011..0705306 100644 (file)
@@ -46,8 +46,8 @@ namespace JSC {
 
     private:
         DateInstanceData()
-            : m_gregorianDateTimeCachedForMS(QNaN)
-            , m_gregorianDateTimeUTCCachedForMS(QNaN)
+            : m_gregorianDateTimeCachedForMS(PNaN)
+            , m_gregorianDateTimeUTCCachedForMS(PNaN)
         {
         }
     };
@@ -62,7 +62,7 @@ namespace JSC {
         void reset()
         {
             for (size_t i = 0; i < cacheSize; ++i)
-                m_cache[i].key = QNaN;
+                m_cache[i].key = PNaN;
         }
         
         DateInstanceData* add(double d)
index 280d082..6a668d4 100644 (file)
@@ -50,7 +50,7 @@ JSValue TerminatedExecutionError::defaultValue(const JSObject*, ExecState* exec,
 {
     if (hint == PreferString)
         return jsNontrivialString(exec, String(ASCIILiteral("JavaScript execution terminated.")));
-    return JSValue(QNaN);
+    return JSValue(PNaN);
 }
 
 JSObject* createTerminatedExecutionException(VM* vm)
index 7e890f3..06b2cff 100644 (file)
@@ -422,7 +422,7 @@ bool JSArray::setLength(ExecState* exec, unsigned newLength, bool throwException
         }
         if (indexingType() == ArrayWithDouble) {
             for (unsigned i = m_butterfly->publicLength(); i-- > newLength;)
-                m_butterfly->contiguousDouble()[i] = QNaN;
+                m_butterfly->contiguousDouble()[i] = PNaN;
         } else {
             for (unsigned i = m_butterfly->publicLength(); i-- > newLength;)
                 m_butterfly->contiguous()[i].clear();
@@ -478,7 +478,7 @@ JSValue JSArray::pop(ExecState* exec)
         RELEASE_ASSERT(length < m_butterfly->vectorLength());
         double value = m_butterfly->contiguousDouble()[length];
         if (value == value) {
-            m_butterfly->contiguousDouble()[length] = QNaN;
+            m_butterfly->contiguousDouble()[length] = PNaN;
             m_butterfly->setPublicLength(length);
             return JSValue(JSValue::EncodeAsDouble, value);
         }
@@ -822,7 +822,7 @@ bool JSArray::shiftCountWithAnyIndexingType(ExecState* exec, unsigned startIndex
             m_butterfly->contiguousDouble()[i] = v;
         }
         for (unsigned i = end; i < oldLength; ++i)
-            m_butterfly->contiguousDouble()[i] = QNaN;
+            m_butterfly->contiguousDouble()[i] = PNaN;
         
         m_butterfly->setPublicLength(oldLength - count);
         return true;
@@ -1440,7 +1440,7 @@ void JSArray::sortVector(ExecState* exec, JSValue compareFunction, CallType call
     for (unsigned i = undefinedElementsThreshold; i < clearElementsThreshold; ++i) {
         ASSERT(i < butterfly()->vectorLength());
         if (indexingType() == ArrayWithDouble)
-            butterfly()->contiguousDouble()[i] = QNaN;
+            butterfly()->contiguousDouble()[i] = PNaN;
         else
             currentIndexingData()[i].clear();
     }
@@ -1684,7 +1684,7 @@ void JSArray::compactForSorting(unsigned& numDefined, unsigned& newRelevantLengt
     for (unsigned i = newRelevantLength; i < myRelevantLength; ++i) {
         ASSERT(i < m_butterfly->vectorLength());
         if (arrayIndexingType == ArrayWithDouble)
-            m_butterfly->contiguousDouble()[i] = QNaN;
+            m_butterfly->contiguousDouble()[i] = PNaN;
         else
             indexingData<arrayIndexingType>()[i].clear();
     }
index 4fc77dc..8cca481 100644 (file)
@@ -219,7 +219,7 @@ inline JSArray* JSArray::create(VM& vm, Structure* structure, unsigned initialLe
         ASSERT(initialLength < MIN_SPARSE_ARRAY_INDEX);
         if (hasDouble(structure->indexingType())) {
             for (unsigned i = 0; i < vectorLength; ++i)
-                butterfly->contiguousDouble()[i] = QNaN;
+                butterfly->contiguousDouble()[i] = PNaN;
         }
     } else {
         ASSERT(
@@ -254,7 +254,7 @@ inline JSArray* JSArray::tryCreateUninitialized(VM& vm, Structure* structure, un
         butterfly->setPublicLength(initialLength);
         if (hasDouble(structure->indexingType())) {
             for (unsigned i = initialLength; i < vectorLength; ++i)
-                butterfly->contiguousDouble()[i] = QNaN;
+                butterfly->contiguousDouble()[i] = PNaN;
         }
     } else {
         void* temp;
index d552c03..329c84a 100644 (file)
@@ -61,7 +61,7 @@ double JSValue::toNumberSlowCase(ExecState* exec) const
         return asCell()->toNumber(exec);
     if (isTrue())
         return 1.0;
-    return isUndefined() ? QNaN : 0; // null and false both convert to 0.
+    return isUndefined() ? PNaN : 0; // null and false both convert to 0.
 }
 
 JSObject* JSValue::toObjectSlowCase(ExecState* exec, JSGlobalObject* globalObject) const
index f7262b6..9788b67 100644 (file)
@@ -24,6 +24,7 @@
 #define JSCJSValue_h
 
 #include <math.h>
+#include "PureNaN.h"
 #include <stddef.h> // for size_t
 #include <stdint.h>
 #include <wtf/Assertions.h>
 
 namespace JSC {
 
-// This is used a lot throughout JavaScriptCore for everything from value boxing to marking
-// values as being missing, so it is useful to have it abbreviated.
-#define QNaN (std::numeric_limits<double>::quiet_NaN())
-
 class AssemblyHelpers;
 class ExecState;
 class JSCell;
index a037070..2f8cc46 100644 (file)
@@ -66,7 +66,7 @@ inline double JSValue::asNumber() const
 
 inline JSValue jsNaN()
 {
-    return JSValue(QNaN);
+    return JSValue(PNaN);
 }
 
 inline JSValue::JSValue(char i)
@@ -302,6 +302,7 @@ ALWAYS_INLINE JSCell* JSValue::asCell() const
 
 ALWAYS_INLINE JSValue::JSValue(EncodeAsDoubleTag, double d)
 {
+    ASSERT(!isImpureNaN(d));
     u.asDouble = d;
 }
 
@@ -468,6 +469,7 @@ inline double reinterpretInt64ToDouble(int64_t value)
 
 ALWAYS_INLINE JSValue::JSValue(EncodeAsDoubleTag, double d)
 {
+    ASSERT(!isImpureNaN(d));
     u.asInt64 = reinterpretDoubleToInt64(d) + DoubleEncodeOffset;
 }
 
@@ -614,7 +616,7 @@ inline bool JSValue::getPrimitiveNumber(ExecState* exec, double& number, JSValue
         return true;
     }
     ASSERT(isUndefined());
-    number = QNaN;
+    number = PNaN;
     value = *this;
     return true;
 }
index c52e938..604de2b 100644 (file)
@@ -282,7 +282,7 @@ static double parseInt(const String& s, const CharType* data, int radix)
 
     // 8.a If R < 2 or R > 36, then return NaN.
     if (radix < 2 || radix > 36)
-        return QNaN;
+        return PNaN;
 
     // 13. Let mathInt be the mathematical integer value that is represented by Z in radix-R notation, using the letters
     //     A-Z and a-z for digits with values 10 through 35. (However, if R is 10 and Z contains more than 20 significant
@@ -305,7 +305,7 @@ static double parseInt(const String& s, const CharType* data, int radix)
 
     // 12. If Z is empty, return NaN.
     if (!sawDigit)
-        return QNaN;
+        return PNaN;
 
     // Alternate code path for certain large numbers.
     if (number >= mantissaOverflowLowerBound) {
@@ -403,7 +403,7 @@ static double jsStrDecimalLiteral(const CharType*& data, const CharType* end)
     }
 
     // Not a number.
-    return QNaN;
+    return PNaN;
 }
 
 template <typename CharType>
@@ -433,7 +433,7 @@ static double toDouble(const CharType* characters, unsigned size)
             break;
     }
     if (characters != endCharacters)
-        return QNaN;
+        return PNaN;
     
     return number;
 }
@@ -449,7 +449,7 @@ double jsToNumber(const String& s)
             return c - '0';
         if (isStrWhiteSpace(c))
             return 0;
-        return QNaN;
+        return PNaN;
     }
 
     if (s.is8Bit())
@@ -465,7 +465,7 @@ static double parseFloat(const String& s)
         UChar c = s[0];
         if (isASCIIDigit(c))
             return c - '0';
-        return QNaN;
+        return PNaN;
     }
 
     if (s.is8Bit()) {
@@ -480,7 +480,7 @@ static double parseFloat(const String& s)
 
         // Empty string.
         if (data == end)
-            return QNaN;
+            return PNaN;
 
         return jsStrDecimalLiteral(data, end);
     }
@@ -496,7 +496,7 @@ static double parseFloat(const String& s)
 
     // Empty string.
     if (data == end)
-        return QNaN;
+        return PNaN;
 
     return jsStrDecimalLiteral(data, end);
 }
index 7d3ed6a..87efa81 100644 (file)
@@ -636,7 +636,7 @@ ContiguousDoubles JSObject::createInitialDouble(VM& vm, unsigned length)
     DeferGC deferGC(vm.heap);
     Butterfly* newButterfly = createInitialIndexedStorage(vm, length, sizeof(double));
     for (unsigned i = newButterfly->vectorLength(); i--;)
-        newButterfly->contiguousDouble()[i] = QNaN;
+        newButterfly->contiguousDouble()[i] = PNaN;
     Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), AllocateDouble);
     setStructureAndButterfly(vm, newStructure, newButterfly);
     return newButterfly->contiguousDouble();
@@ -690,7 +690,7 @@ ContiguousDoubles JSObject::convertUndecidedToDouble(VM& vm)
     ASSERT(hasUndecided(indexingType()));
     
     for (unsigned i = m_butterfly->vectorLength(); i--;)
-        m_butterfly->contiguousDouble()[i] = QNaN;
+        m_butterfly->contiguousDouble()[i] = PNaN;
     
     setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), AllocateDouble));
     return m_butterfly->contiguousDouble();
@@ -760,7 +760,7 @@ ContiguousDoubles JSObject::convertInt32ToDouble(VM& vm)
         double* currentAsDouble = bitwise_cast<double*>(current);
         JSValue v = current->get();
         if (!v) {
-            *currentAsDouble = QNaN;
+            *currentAsDouble = PNaN;
             continue;
         }
         ASSERT(v.isInt32());
@@ -1320,7 +1320,7 @@ bool JSObject::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned i)
         Butterfly* butterfly = thisObject->butterfly();
         if (i >= butterfly->vectorLength())
             return true;
-        butterfly->contiguousDouble()[i] = QNaN;
+        butterfly->contiguousDouble()[i] = PNaN;
         return true;
     }
         
@@ -2433,7 +2433,7 @@ void JSObject::ensureLengthSlow(VM& vm, unsigned length)
 
     if (hasDouble(indexingType())) {
         for (unsigned i = oldVectorLength; i < newVectorLength; ++i)
-            m_butterfly->contiguousDouble().data()[i] = QNaN;
+            m_butterfly->contiguousDouble().data()[i] = PNaN;
     }
 }
 
index c35980b..fdf5abd 100644 (file)
@@ -219,7 +219,7 @@ EncodedJSValue JSC_HOST_CALL mathProtoFuncMax(ExecState* exec)
     for (unsigned k = 0; k < argsCount; ++k) {
         double val = exec->uncheckedArgument(k).toNumber(exec);
         if (std::isnan(val)) {
-            result = QNaN;
+            result = PNaN;
         } else if (val > result || (!val && !result && !std::signbit(val)))
             result = val;
     }
@@ -233,7 +233,7 @@ EncodedJSValue JSC_HOST_CALL mathProtoFuncMin(ExecState* exec)
     for (unsigned k = 0; k < argsCount; ++k) {
         double val = exec->uncheckedArgument(k).toNumber(exec);
         if (std::isnan(val)) {
-            result = QNaN;
+            result = PNaN;
         } else if (val < result || (!val && !result && std::signbit(val)))
             result = val;
     }
diff --git a/Source/JavaScriptCore/runtime/PureNaN.h b/Source/JavaScriptCore/runtime/PureNaN.h
new file mode 100644 (file)
index 0000000..69aec59
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2014 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#ifndef PureNaN_h
+#define PureNaN_h
+
+#include <wtf/Assertions.h>
+#include <wtf/StdLibExtras.h>
+
+namespace JSC {
+
+// NaN (not-a-number) double values are central to how JavaScriptCore encodes JavaScript
+// values (JSValues).  All values, including integers and non-numeric values, are always
+// encoded using the IEEE 854 binary double format.  Non-double values are encoded using
+// a NaN with the sign bit set.  The 51-bit payload is then used for encoding the actual
+// value - be it an integer or a pointer to an object, or something else. But we only
+// make use of the low 49 bits and the top 15 bits being all set to 1 is the indicator
+// that a value is not a double. Top 15 bits being set to 1 also indicate a signed
+// signaling NaN with some additional NaN payload bits.
+//
+// Our use of NaN encoding means that we have to be careful with how we use NaNs for
+// ordinary doubles. For example, it would be wrong to ever use a NaN that has the top
+// 15 bits set, as that would look like a non-double value to JSC.
+//
+// We can trust that on all of the hardware/OS combinations that we care about,
+// NaN-producing math operations never produce a NaN that looks like a tagged value. But
+// if we're ever in a situation where we worry about it, we can use purifyNaN() to get a
+// NaN that doesn't look like a tagged non-double value. The JavaScript language doesn't
+// distinguish between different flavors of NaN and there is no way to detect what kind
+// of NaN you have - hence so long as all double NaNs are purified then our tagging
+// scheme remains sound.
+//
+// It's worth noting that there are cases, like sin(), that will almost produce a NaN
+// that breaks us. sin(-inf) returns 0xfff8000000000000. This doesn't break us because
+// not all of the top 15 bits are set. But it's very close. Hence our assumptions about
+// NaN are just about the most aggressive assumptions we could possibly make without
+// having to call purifyNaN() in surprising places.
+//
+// For naming purposes, we say that a NaN is "pure" if it is safe to tag, in the sense
+// that doing so would result in a tagged value that would base the "are you a double"
+// test. We say that a NaN is "impure" if attempting to tag it would result in a value
+// that would look like something other than a double.
+
+// Returns some kind of pure NaN.
+inline double pureNaN()
+{
+    // Be sure that we return exactly the kind of NaN that is safe. We engineer the bits
+    // ourselves to ensure that it's !isImpureNaN(). FWIW, this is what
+    // numeric_limits<double>::quiet_NaN() returns on Mac/X86_64. But AFAICT there is
+    // no guarantee that quiet_NaN would return a pureNaN on all platforms. For example,
+    // the docs appear to imply that quiet_NaN could even return a double with the
+    // signaling bit set on hardware that doesn't do signaling. That would probably
+    // never happen, but it's healthy to be paranoid.
+    return bitwise_cast<double>(0x7ff8000000000000ll);
+}
+
+#define PNaN (pureNaN())
+
+inline bool isImpureNaN(double value)
+{
+    // Tests if the double value would break JSVALUE64 encoding, which is the most
+    // aggressive kind of encoding that we currently use.
+    return bitwise_cast<uint64_t>(value) >= 0xfffe000000000000llu;
+}
+
+// If the given value is NaN then return a NaN that is known to be pure.
+inline double purifyNaN(double value)
+{
+    if (value != value)
+        return PNaN;
+    return value;
+}   
+
+} // namespace JSC
+
+#endif // PureNaN_h
index 84d3db5..d4dc535 100644 (file)
@@ -89,9 +89,7 @@ struct FloatTypedArrayAdaptor {
     
     static JSValue toJSValue(Type value)
     {
-        if (value != value)
-            return jsDoubleNumber(QNaN);
-        return jsDoubleNumber(value);
+        return jsDoubleNumber(purifyNaN(value));
     }
     
     static double toDouble(Type value)