[ESNext][BigInt] Add support for op_inc
authorrmorisset@apple.com <rmorisset@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 20 Nov 2019 03:41:57 +0000 (03:41 +0000)
committerrmorisset@apple.com <rmorisset@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 20 Nov 2019 03:41:57 +0000 (03:41 +0000)
https://bugs.webkit.org/show_bug.cgi?id=193240

Reviewed by Yusuke Suzuki.

JSTests:

Some parts of these tests are inspired by tests in a WIP patch by Caio Lima.
Thanks to him for allowing their reuse.

* stress/inc-osr-exit-from-big-int.js: Added.
(let.assert.sameValue):
(postInc):
(preInc):
(postDec):
(preDec):
* stress/inc-osr-exit-to-big-int.js: Added.
(let.assert.sameValue):
(postInc):
(preInc):
(postDec):
(preDec):
(o.valueOf):

Source/JavaScriptCore:

This patch adds support for both ++ and -- on BigInts.

It required the following secondary changes:
- teaching FixupPhase how to replace it by ArithAdd/ArithSub/ValueAdd/ValueSub when the type is Int32/Double/BigInt
- pulling ObservedResults out of UnaryArithProfile/BinaryArithProfile, so that it can be used by ArithAdd regardless of whether it comes from an Inc or from an Add
- adding the constant 1n to the VM object so that it can be used by FixupPhase since it cannot allocate a new JSValue.
- adding an UnaryArithProfile to op_inc and op_dec, and teaching the llint to update them.
- adding ToNumeric (identity on bigints, same as toNumber on everything else) to all tiers

* bytecode/ArithProfile.cpp:
(JSC::ArithProfile<BitfieldType>::shouldEmitSetDouble const):
(JSC::ArithProfile<BitfieldType>::emitSetDouble const):
(JSC::ArithProfile<BitfieldType>::shouldEmitSetNonNumeric const):
(JSC::ArithProfile<BitfieldType>::shouldEmitSetBigInt const):
(JSC::ArithProfile<BitfieldType>::emitSetNonNumeric const):
(JSC::ArithProfile<BitfieldType>::emitSetBigInt const):
* bytecode/ArithProfile.h:
(JSC::ObservedResults::ObservedResults):
(JSC::ObservedResults::didObserveNonInt32):
(JSC::ObservedResults::didObserveDouble):
(JSC::ObservedResults::didObserveNonNegZeroDouble):
(JSC::ObservedResults::didObserveNegZeroDouble):
(JSC::ObservedResults::didObserveNonNumeric):
(JSC::ObservedResults::didObserveBigInt):
(JSC::ObservedResults::didObserveInt32Overflow):
(JSC::ObservedResults::didObserveInt52Overflow):
(JSC::ArithProfile::observedResults const):
(JSC::ArithProfile::didObserveNonInt32 const):
(JSC::ArithProfile::didObserveDouble const):
(JSC::ArithProfile::didObserveNonNegZeroDouble const):
(JSC::ArithProfile::didObserveNegZeroDouble const):
(JSC::ArithProfile::didObserveNonNumeric const):
(JSC::ArithProfile::didObserveBigInt const):
(JSC::ArithProfile::didObserveInt32Overflow const):
(JSC::ArithProfile::didObserveInt52Overflow const):
(JSC::ArithProfile::setObservedNonNegZeroDouble):
(JSC::ArithProfile::setObservedNegZeroDouble):
(JSC::ArithProfile::setObservedNonNumeric):
(JSC::ArithProfile::setObservedBigInt):
(JSC::ArithProfile::setObservedInt32Overflow):
(JSC::ArithProfile::setObservedInt52Overflow):
(JSC::ArithProfile::observeResult):
* bytecode/BytecodeList.rb:
* bytecode/BytecodeUseDef.h:
(JSC::computeUsesForBytecodeIndex):
(JSC::computeDefsForBytecodeIndex):
* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::unaryArithProfileForPC):
* bytecode/ExitKind.h:
* bytecode/SpeculatedType.h:
(JSC::isInt32SpeculationForArithmetic):
(JSC::isInt32OrBooleanSpeculationForArithmetic):
* bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::emitUnaryOp):
(JSC::BytecodeGenerator::emitToNumeric):
* bytecompiler/BytecodeGenerator.h:
* bytecompiler/NodesCodegen.cpp:
(JSC::emitPostIncOrDec):
* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
* dfg/DFGBackwardsPropagationPhase.cpp:
(JSC::DFG::BackwardsPropagationPhase::propagate):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::makeSafe):
(JSC::DFG::ByteCodeParser::parseBlock):
* dfg/DFGCapabilities.cpp:
(JSC::DFG::capabilityLevel):
* dfg/DFGClobberize.h:
(JSC::DFG::clobberize):
* dfg/DFGConstantFoldingPhase.cpp:
(JSC::DFG::ConstantFoldingPhase::foldConstants):
* dfg/DFGDoesGC.cpp:
(JSC::DFG::doesGC):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
(JSC::DFG::FixupPhase::fixupToNumeric):
* dfg/DFGMayExit.cpp:
* dfg/DFGNode.h:
(JSC::DFG::Node::hasHeapPrediction):
* dfg/DFGNodeType.h:
* dfg/DFGOperations.cpp:
* dfg/DFGOperations.h:
* dfg/DFGPredictionPropagationPhase.cpp:
* dfg/DFGSafeToExecute.h:
(JSC::DFG::safeToExecute):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compileIncOrDec):
(JSC::DFG::SpeculativeJIT::compileToPrimitive):
(JSC::DFG::SpeculativeJIT::compileToNumeric):
* dfg/DFGSpeculativeJIT.h:
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::compileNode):
(JSC::FTL::DFG::LowerDFGToB3::compileIncOrDec):
* jit/JIT.cpp:
(JSC::JIT::privateCompileMainPass):
(JSC::JIT::privateCompileSlowCases):
* jit/JIT.h:
* jit/JITMathIC.h:
(JSC::JITMathIC::generateInline):
* jit/JITMulGenerator.cpp:
(JSC::JITMulGenerator::generateFastPath):
* jit/JITOpcodes.cpp:
(JSC::JIT::emit_op_to_numeric):
* jit/JITOpcodes32_64.cpp:
(JSC::JIT::emit_op_to_numeric):
* llint/LowLevelInterpreter.asm:
* llint/LowLevelInterpreter32_64.asm:
* llint/LowLevelInterpreter64.asm:
* runtime/CommonSlowPaths.cpp:
(JSC::SLOW_PATH_DECL):
* runtime/CommonSlowPaths.h:
* runtime/JSBigInt.cpp:
(JSC::JSBigInt::inc):
(JSC::JSBigInt::dec):
* runtime/JSBigInt.h:
* runtime/VM.cpp:
(JSC::VM::VM):
* runtime/VM.h:

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

52 files changed:
JSTests/ChangeLog
JSTests/stress/inc-osr-exit-from-big-int.js [new file with mode: 0644]
JSTests/stress/inc-osr-exit-to-big-int.js [new file with mode: 0644]
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/bytecode/ArithProfile.cpp
Source/JavaScriptCore/bytecode/ArithProfile.h
Source/JavaScriptCore/bytecode/BytecodeList.rb
Source/JavaScriptCore/bytecode/BytecodeUseDef.h
Source/JavaScriptCore/bytecode/CodeBlock.cpp
Source/JavaScriptCore/bytecode/ExitKind.h
Source/JavaScriptCore/bytecode/Opcode.h
Source/JavaScriptCore/bytecode/SpeculatedType.cpp
Source/JavaScriptCore/bytecode/SpeculatedType.h
Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h
Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp
Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
Source/JavaScriptCore/dfg/DFGBackwardsPropagationPhase.cpp
Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
Source/JavaScriptCore/dfg/DFGCapabilities.cpp
Source/JavaScriptCore/dfg/DFGClobberize.h
Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp
Source/JavaScriptCore/dfg/DFGDoesGC.cpp
Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
Source/JavaScriptCore/dfg/DFGMayExit.cpp
Source/JavaScriptCore/dfg/DFGNode.h
Source/JavaScriptCore/dfg/DFGNodeType.h
Source/JavaScriptCore/dfg/DFGOperations.cpp
Source/JavaScriptCore/dfg/DFGOperations.h
Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
Source/JavaScriptCore/dfg/DFGSafeToExecute.h
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
Source/JavaScriptCore/ftl/FTLCapabilities.cpp
Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp
Source/JavaScriptCore/jit/JIT.cpp
Source/JavaScriptCore/jit/JIT.h
Source/JavaScriptCore/jit/JITMathIC.h
Source/JavaScriptCore/jit/JITMulGenerator.cpp
Source/JavaScriptCore/jit/JITOpcodes.cpp
Source/JavaScriptCore/jit/JITOpcodes32_64.cpp
Source/JavaScriptCore/llint/LowLevelInterpreter.asm
Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm
Source/JavaScriptCore/llint/LowLevelInterpreter64.asm
Source/JavaScriptCore/runtime/CommonSlowPaths.cpp
Source/JavaScriptCore/runtime/CommonSlowPaths.h
Source/JavaScriptCore/runtime/JSBigInt.cpp
Source/JavaScriptCore/runtime/JSBigInt.h
Source/JavaScriptCore/runtime/VM.cpp
Source/JavaScriptCore/runtime/VM.h

index 08bf22f..ac54231 100644 (file)
@@ -1,3 +1,27 @@
+2019-11-19  Robin Morisset  <rmorisset@apple.com>
+
+        [ESNext][BigInt] Add support for op_inc
+        https://bugs.webkit.org/show_bug.cgi?id=193240
+
+        Reviewed by Yusuke Suzuki.
+
+        Some parts of these tests are inspired by tests in a WIP patch by Caio Lima.
+        Thanks to him for allowing their reuse.
+
+        * stress/inc-osr-exit-from-big-int.js: Added.
+        (let.assert.sameValue):
+        (postInc):
+        (preInc):
+        (postDec):
+        (preDec):
+        * stress/inc-osr-exit-to-big-int.js: Added.
+        (let.assert.sameValue):
+        (postInc):
+        (preInc):
+        (postDec):
+        (preDec):
+        (o.valueOf):
+
 2019-11-19  Saam Barati  <sbarati@apple.com>
 
         Remove runNullishAwareOperatorsEnabled
diff --git a/JSTests/stress/inc-osr-exit-from-big-int.js b/JSTests/stress/inc-osr-exit-from-big-int.js
new file mode 100644 (file)
index 0000000..5081863
--- /dev/null
@@ -0,0 +1,59 @@
+//@ runBigIntEnabled
+
+let assert = {
+    sameValue: function(i, e, m) {
+        if (i !== e)
+            throw new Error(m);
+    }
+}
+
+function postInc(x) {
+    return x++;
+}
+noInline(postInc);
+function preInc(x) {
+    return ++x;
+}
+noInline(preInc);
+function postDec(x) {
+    return x--;
+}
+noInline(postDec);
+function preDec(x) {
+    return --x;
+}
+noInline(preDec);
+
+for (let i = 0; i < 10000; i++) {
+    var r = postInc(3012n);
+    assert.sameValue(r, 3012n, "3012n++ = " + r);
+
+    r = preInc(3012n)
+    assert.sameValue(r, 3013n, "++3012n = " + r);
+
+    r = postDec(3012n);
+    assert.sameValue(r, 3012n, "3012n-- = " + r);
+    
+    r = preDec(3012n)
+    assert.sameValue(r, 3011n, "--3012n = " + r);
+}
+
+var r = postInc(3);
+assert.sameValue(r, 3, 3 + "++ = " + r);
+r = postInc("3");
+assert.sameValue(r, 3, 3 + "++ = " + r);
+
+r = postDec(3);
+assert.sameValue(r, 3, 3 + "-- = " + r);
+r = postDec("3");
+assert.sameValue(r, 3, 3 + "-- = " + r);
+
+r = preInc(3);
+assert.sameValue(r, 4, "++" + 3 + " = " + r);
+r = preInc("3");
+assert.sameValue(r, 4, "++" + 3 + " = " + r);
+
+r = preDec(3);
+assert.sameValue(r, 2, "--" + 3 + " = " + r);
+r = preDec("3");
+assert.sameValue(r, 2, "--" + 3 + " = " + r);
diff --git a/JSTests/stress/inc-osr-exit-to-big-int.js b/JSTests/stress/inc-osr-exit-to-big-int.js
new file mode 100644 (file)
index 0000000..ae0a442
--- /dev/null
@@ -0,0 +1,55 @@
+//@ runBigIntEnabled
+
+let assert = {
+    sameValue: function(i, e, m) {
+        if (i !== e)
+            throw new Error(m);
+    }
+}
+
+function postInc(x) {
+    return x++;
+}
+noInline(postInc);
+function preInc(x) {
+    return ++x;
+}
+noInline(preInc);
+function postDec(x) {
+    return x--;
+}
+noInline(postDec);
+function preDec(x) {
+    return --x;
+}
+noInline(preDec);
+
+for (let i = 0; i < 10000; i++) {
+    var r = postInc(3012);
+    assert.sameValue(r, 3012, 3012 + "++ = " + r);
+
+    r = preInc(3012)
+    assert.sameValue(r, 3013, "++" + 3012 + " = " + r);
+
+    r = postDec(3012);
+    assert.sameValue(r, 3012, 3012 + "-- = " + r);
+    
+    r = preDec(3012)
+    assert.sameValue(r, 3011, "--" + 3012 + " = " + r);
+}
+
+var r = postInc(3n);
+assert.sameValue(r, 3n, 3n + "++ = " + r);
+
+r = preInc(12345678901234567890n);
+assert.sameValue(r, 12345678901234567891n, "++" + 12345678901234567890n, " = ", r);
+
+var count = 0;
+var o = {};
+o.valueOf = () => { count++; return 42n; };
+r = postDec(o)
+assert.sameValue(r, 42n, "{valueOf: () => 42n} -- = " + r);
+assert.sameValue(count, 1, "execution count of valueOf on o = " + count);
+
+r = preDec(123456789000n);
+assert.sameValue(r, 123456788999n, "--123456789000n = " + r);
index 13eda49..ad40ad9 100644 (file)
@@ -1,3 +1,135 @@
+2019-11-19  Robin Morisset  <rmorisset@apple.com>
+
+        [ESNext][BigInt] Add support for op_inc
+        https://bugs.webkit.org/show_bug.cgi?id=193240
+
+        Reviewed by Yusuke Suzuki.
+
+        This patch adds support for both ++ and -- on BigInts.
+
+        It required the following secondary changes:
+        - teaching FixupPhase how to replace it by ArithAdd/ArithSub/ValueAdd/ValueSub when the type is Int32/Double/BigInt
+        - pulling ObservedResults out of UnaryArithProfile/BinaryArithProfile, so that it can be used by ArithAdd regardless of whether it comes from an Inc or from an Add
+        - adding the constant 1n to the VM object so that it can be used by FixupPhase since it cannot allocate a new JSValue.
+        - adding an UnaryArithProfile to op_inc and op_dec, and teaching the llint to update them.
+        - adding ToNumeric (identity on bigints, same as toNumber on everything else) to all tiers
+
+        * bytecode/ArithProfile.cpp:
+        (JSC::ArithProfile<BitfieldType>::shouldEmitSetDouble const):
+        (JSC::ArithProfile<BitfieldType>::emitSetDouble const):
+        (JSC::ArithProfile<BitfieldType>::shouldEmitSetNonNumeric const):
+        (JSC::ArithProfile<BitfieldType>::shouldEmitSetBigInt const):
+        (JSC::ArithProfile<BitfieldType>::emitSetNonNumeric const):
+        (JSC::ArithProfile<BitfieldType>::emitSetBigInt const):
+        * bytecode/ArithProfile.h:
+        (JSC::ObservedResults::ObservedResults):
+        (JSC::ObservedResults::didObserveNonInt32):
+        (JSC::ObservedResults::didObserveDouble):
+        (JSC::ObservedResults::didObserveNonNegZeroDouble):
+        (JSC::ObservedResults::didObserveNegZeroDouble):
+        (JSC::ObservedResults::didObserveNonNumeric):
+        (JSC::ObservedResults::didObserveBigInt):
+        (JSC::ObservedResults::didObserveInt32Overflow):
+        (JSC::ObservedResults::didObserveInt52Overflow):
+        (JSC::ArithProfile::observedResults const):
+        (JSC::ArithProfile::didObserveNonInt32 const):
+        (JSC::ArithProfile::didObserveDouble const):
+        (JSC::ArithProfile::didObserveNonNegZeroDouble const):
+        (JSC::ArithProfile::didObserveNegZeroDouble const):
+        (JSC::ArithProfile::didObserveNonNumeric const):
+        (JSC::ArithProfile::didObserveBigInt const):
+        (JSC::ArithProfile::didObserveInt32Overflow const):
+        (JSC::ArithProfile::didObserveInt52Overflow const):
+        (JSC::ArithProfile::setObservedNonNegZeroDouble):
+        (JSC::ArithProfile::setObservedNegZeroDouble):
+        (JSC::ArithProfile::setObservedNonNumeric):
+        (JSC::ArithProfile::setObservedBigInt):
+        (JSC::ArithProfile::setObservedInt32Overflow):
+        (JSC::ArithProfile::setObservedInt52Overflow):
+        (JSC::ArithProfile::observeResult):
+        * bytecode/BytecodeList.rb:
+        * bytecode/BytecodeUseDef.h:
+        (JSC::computeUsesForBytecodeIndex):
+        (JSC::computeDefsForBytecodeIndex):
+        * bytecode/CodeBlock.cpp:
+        (JSC::CodeBlock::unaryArithProfileForPC):
+        * bytecode/ExitKind.h:
+        * bytecode/SpeculatedType.h:
+        (JSC::isInt32SpeculationForArithmetic):
+        (JSC::isInt32OrBooleanSpeculationForArithmetic):
+        * bytecompiler/BytecodeGenerator.cpp:
+        (JSC::BytecodeGenerator::emitUnaryOp):
+        (JSC::BytecodeGenerator::emitToNumeric):
+        * bytecompiler/BytecodeGenerator.h:
+        * bytecompiler/NodesCodegen.cpp:
+        (JSC::emitPostIncOrDec):
+        * dfg/DFGAbstractInterpreterInlines.h:
+        (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+        * dfg/DFGBackwardsPropagationPhase.cpp:
+        (JSC::DFG::BackwardsPropagationPhase::propagate):
+        * dfg/DFGByteCodeParser.cpp:
+        (JSC::DFG::ByteCodeParser::makeSafe):
+        (JSC::DFG::ByteCodeParser::parseBlock):
+        * dfg/DFGCapabilities.cpp:
+        (JSC::DFG::capabilityLevel):
+        * dfg/DFGClobberize.h:
+        (JSC::DFG::clobberize):
+        * dfg/DFGConstantFoldingPhase.cpp:
+        (JSC::DFG::ConstantFoldingPhase::foldConstants):
+        * dfg/DFGDoesGC.cpp:
+        (JSC::DFG::doesGC):
+        * dfg/DFGFixupPhase.cpp:
+        (JSC::DFG::FixupPhase::fixupNode):
+        (JSC::DFG::FixupPhase::fixupToNumeric):
+        * dfg/DFGMayExit.cpp:
+        * dfg/DFGNode.h:
+        (JSC::DFG::Node::hasHeapPrediction):
+        * dfg/DFGNodeType.h:
+        * dfg/DFGOperations.cpp:
+        * dfg/DFGOperations.h:
+        * dfg/DFGPredictionPropagationPhase.cpp:
+        * dfg/DFGSafeToExecute.h:
+        (JSC::DFG::safeToExecute):
+        * dfg/DFGSpeculativeJIT.cpp:
+        (JSC::DFG::SpeculativeJIT::compileIncOrDec):
+        (JSC::DFG::SpeculativeJIT::compileToPrimitive):
+        (JSC::DFG::SpeculativeJIT::compileToNumeric):
+        * dfg/DFGSpeculativeJIT.h:
+        * dfg/DFGSpeculativeJIT32_64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGSpeculativeJIT64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * ftl/FTLCapabilities.cpp:
+        (JSC::FTL::canCompile):
+        * ftl/FTLLowerDFGToB3.cpp:
+        (JSC::FTL::DFG::LowerDFGToB3::compileNode):
+        (JSC::FTL::DFG::LowerDFGToB3::compileIncOrDec):
+        * jit/JIT.cpp:
+        (JSC::JIT::privateCompileMainPass):
+        (JSC::JIT::privateCompileSlowCases):
+        * jit/JIT.h:
+        * jit/JITMathIC.h:
+        (JSC::JITMathIC::generateInline):
+        * jit/JITMulGenerator.cpp:
+        (JSC::JITMulGenerator::generateFastPath):
+        * jit/JITOpcodes.cpp:
+        (JSC::JIT::emit_op_to_numeric):
+        * jit/JITOpcodes32_64.cpp:
+        (JSC::JIT::emit_op_to_numeric):
+        * llint/LowLevelInterpreter.asm:
+        * llint/LowLevelInterpreter32_64.asm:
+        * llint/LowLevelInterpreter64.asm:
+        * runtime/CommonSlowPaths.cpp:
+        (JSC::SLOW_PATH_DECL):
+        * runtime/CommonSlowPaths.h:
+        * runtime/JSBigInt.cpp:
+        (JSC::JSBigInt::inc):
+        (JSC::JSBigInt::dec):
+        * runtime/JSBigInt.h:
+        * runtime/VM.cpp:
+        (JSC::VM::VM):
+        * runtime/VM.h:
+
 2019-11-19  Yusuke Suzuki  <ysuzuki@apple.com>
 
         [JSC] MetadataTable::sizeInBytes should not touch m_rawBuffer in UnlinkedMetadataTable unless MetadataTable is linked to that UnlinkedMetadataTable
index aaed3f7..fea6674 100644 (file)
@@ -62,7 +62,7 @@ void ArithProfile<BitfieldType>::emitObserveResult(CCallHelpers& jit, JSValueReg
 template<typename BitfieldType>
 bool ArithProfile<BitfieldType>::shouldEmitSetDouble() const
 {
-    BitfieldType mask = Int32Overflow | Int52Overflow | NegZeroDouble | NonNegZeroDouble;
+    BitfieldType mask = ObservedResults::Int32Overflow | ObservedResults::Int52Overflow | ObservedResults::NegZeroDouble | ObservedResults::NonNegZeroDouble;
     return (m_bits & mask) != mask;
 }
 
@@ -70,13 +70,13 @@ template<typename BitfieldType>
 void ArithProfile<BitfieldType>::emitSetDouble(CCallHelpers& jit) const
 {
     if (shouldEmitSetDouble())
-        emitUnconditionalSet(jit, Int32Overflow | Int52Overflow | NegZeroDouble | NonNegZeroDouble);
+        emitUnconditionalSet(jit, ObservedResults::Int32Overflow | ObservedResults::Int52Overflow | ObservedResults::NegZeroDouble | ObservedResults::NonNegZeroDouble);
 }
 
 template<typename BitfieldType>
 bool ArithProfile<BitfieldType>::shouldEmitSetNonNumeric() const
 {
-    BitfieldType mask = ArithProfile::NonNumeric;
+    BitfieldType mask = ObservedResults::NonNumeric;
     return (m_bits & mask) != mask;
 }
 
@@ -84,13 +84,13 @@ template<typename BitfieldType>
 void ArithProfile<BitfieldType>::emitSetNonNumeric(CCallHelpers& jit) const
 {
     if (shouldEmitSetNonNumeric())
-        emitUnconditionalSet(jit, NonNumeric);
+        emitUnconditionalSet(jit, ObservedResults::NonNumeric);
 }
 
 template<typename BitfieldType>
 bool ArithProfile<BitfieldType>::shouldEmitSetBigInt() const
 {
-    BitfieldType mask = ArithProfile::BigInt;
+    BitfieldType mask = ObservedResults::BigInt;
     return (m_bits & mask) != mask;
 }
 
@@ -98,7 +98,7 @@ template<typename BitfieldType>
 void ArithProfile<BitfieldType>::emitSetBigInt(CCallHelpers& jit) const
 {
     if (shouldEmitSetBigInt())
-        emitUnconditionalSet(jit, BigInt);
+        emitUnconditionalSet(jit, ObservedResults::BigInt);
 }
 
 template<typename BitfieldType>
index 0689faa..0bfe6af 100644 (file)
@@ -66,10 +66,9 @@ private:
     uint8_t m_bits { 0 };
 };
 
-template <typename BitfieldType>
-class ArithProfile {
+class ObservedResults {
 public:
-    enum ObservedResults : BitfieldType {
+    enum Tags : uint8_t {
         NonNegZeroDouble = 1 << 0,
         NegZeroDouble    = 1 << 1,
         NonNumeric       = 1 << 2,
@@ -77,37 +76,62 @@ public:
         Int52Overflow    = 1 << 4,
         BigInt           = 1 << 5,
     };
-    static constexpr uint32_t observedResultsNumBitsNeeded = 6;
-
-    bool didObserveNonInt32() const { return hasBits(NonNegZeroDouble | NegZeroDouble | NonNumeric | BigInt); }
-    bool didObserveDouble() const { return hasBits(NonNegZeroDouble | NegZeroDouble); }
-    bool didObserveNonNegZeroDouble() const { return hasBits(NonNegZeroDouble); }
-    bool didObserveNegZeroDouble() const { return hasBits(NegZeroDouble); }
-    bool didObserveNonNumeric() const { return hasBits(NonNumeric); }
-    bool didObserveBigInt() const { return hasBits(BigInt); }
-    bool didObserveInt32Overflow() const { return hasBits(Int32Overflow); }
-    bool didObserveInt52Overflow() const { return hasBits(Int52Overflow); }
-
-    void setObservedNonNegZeroDouble() { setBit(NonNegZeroDouble); }
-    void setObservedNegZeroDouble() { setBit(NegZeroDouble); }
-    void setObservedNonNumeric() { setBit(NonNumeric); }
-    void setObservedBigInt() { setBit(BigInt); }
-    void setObservedInt32Overflow() { setBit(Int32Overflow); }
-    void setObservedInt52Overflow() { setBit(Int52Overflow); }
+    static constexpr uint32_t numBitsNeeded = 6;
+
+    ObservedResults() = default;
+    explicit ObservedResults(uint8_t bits)
+        : m_bits(bits)
+    { }
+
+    bool didObserveNonInt32() { return m_bits & (NonNegZeroDouble | NegZeroDouble | NonNumeric | BigInt); }
+    bool didObserveDouble() { return m_bits & (NonNegZeroDouble | NegZeroDouble); }
+    bool didObserveNonNegZeroDouble() { return m_bits & NonNegZeroDouble; }
+    bool didObserveNegZeroDouble() { return m_bits & NegZeroDouble; }
+    bool didObserveNonNumeric() { return m_bits & NonNumeric; }
+    bool didObserveBigInt() { return m_bits & BigInt; }
+    bool didObserveInt32Overflow() { return m_bits & Int32Overflow; }
+    bool didObserveInt52Overflow() { return m_bits & Int52Overflow; }
+
+private:
+    uint8_t m_bits { 0 };
+};
+
+template <typename BitfieldType>
+class ArithProfile {
+public:
+    ObservedResults observedResults() const
+    {
+        return ObservedResults(m_bits & ((1 << ObservedResults::numBitsNeeded) - 1));
+    }
+    bool didObserveNonInt32() const { return observedResults().didObserveNonInt32();}
+    bool didObserveDouble() const { return observedResults().didObserveDouble(); }
+    bool didObserveNonNegZeroDouble() const { return observedResults().didObserveNonNegZeroDouble(); }
+    bool didObserveNegZeroDouble() const { return observedResults().didObserveNegZeroDouble(); }
+    bool didObserveNonNumeric() const { return observedResults().didObserveNonNumeric(); }
+    bool didObserveBigInt() const { return observedResults().didObserveBigInt(); }
+    bool didObserveInt32Overflow() const { return observedResults().didObserveInt32Overflow(); }
+    bool didObserveInt52Overflow() const { return observedResults().didObserveInt52Overflow(); }
+
+    void setObservedNonNegZeroDouble() { setBit(ObservedResults::NonNegZeroDouble); }
+    void setObservedNegZeroDouble() { setBit(ObservedResults::NegZeroDouble); }
+    void setObservedNonNumeric() { setBit(ObservedResults::NonNumeric); }
+    void setObservedBigInt() { setBit(ObservedResults::BigInt); }
+    void setObservedInt32Overflow() { setBit(ObservedResults::Int32Overflow); }
+    void setObservedInt52Overflow() { setBit(ObservedResults::Int52Overflow); }
 
     void observeResult(JSValue value)
     {
         if (value.isInt32())
             return;
         if (value.isNumber()) {
-            m_bits |= Int32Overflow | Int52Overflow | NonNegZeroDouble | NegZeroDouble;
+            m_bits |= ObservedResults::Int32Overflow | ObservedResults::Int52Overflow | ObservedResults::NonNegZeroDouble | ObservedResults::NegZeroDouble;
             return;
         }
         if (value && value.isBigInt()) {
-            m_bits |= BigInt;
+            m_bits |= ObservedResults::BigInt;
             return;
         }
-        m_bits |= NonNumeric;
+        m_bits |= ObservedResults::NonNumeric;
     }
 
     const void* addressOfBits() const { return &m_bits; }
@@ -135,9 +159,7 @@ public:
     constexpr uint32_t bits() const { return m_bits; }
 
 protected:
-    ArithProfile()
-    {
-    }
+    ArithProfile() = default;
 
     bool hasBits(int mask) const { return m_bits & mask; }
     void setBit(int mask) { m_bits |= mask; }
@@ -151,7 +173,7 @@ protected:
  */
 using UnaryArithProfileBase = uint16_t;
 class UnaryArithProfile : public ArithProfile<UnaryArithProfileBase> {
-    static constexpr unsigned argObservedTypeShift = observedResultsNumBitsNeeded;
+    static constexpr unsigned argObservedTypeShift = ObservedResults::numBitsNeeded;
 
     static_assert(argObservedTypeShift + ObservedType::numBitsNeeded <= sizeof(UnaryArithProfileBase) * 8, "Should fit in a the type of the underlying bitfield.");
 
@@ -224,7 +246,7 @@ public:
  */
 using BinaryArithProfileBase = uint16_t;
 class BinaryArithProfile : public ArithProfile<BinaryArithProfileBase> {
-    static constexpr uint32_t rhsObservedTypeShift = observedResultsNumBitsNeeded;
+    static constexpr uint32_t rhsObservedTypeShift = ObservedResults::numBitsNeeded;
     static constexpr uint32_t lhsObservedTypeShift = rhsObservedTypeShift + ObservedType::numBitsNeeded;
 
     static_assert(ObservedType::numBitsNeeded == 3, "We make a hard assumption about that here.");
index 72de609..dc96d89 100644 (file)
@@ -330,14 +330,16 @@ op_group :UnaryOp,
         operand: VirtualRegister,
     }
 
-op :inc,
-    args: {
-        srcDst: VirtualRegister,
-    }
-
-op :dec,
+op_group :UnaryInPlaceProfiledOp,
+    [
+        :inc,
+        :dec,
+    ],
     args: {
         srcDst: VirtualRegister,
+    },
+    metadata: {
+        arithProfile: UnaryArithProfile
     }
 
 op :to_object,
@@ -350,7 +352,11 @@ op :to_object,
         profile: ValueProfile,
     }
 
-op :to_number,
+op_group :ValueProfiledUnaryOp,
+    [
+        :to_number,
+        :to_numeric,
+    ],
     args: {
         dst: VirtualRegister,
         operand: VirtualRegister,
index ca33479..c059940 100644 (file)
@@ -183,6 +183,7 @@ void computeUsesForBytecodeIndex(Block* codeBlock, OpcodeID opcodeID, const Inst
     USES(OpIsCellWithType, operand)
     USES(OpIsFunction, operand)
     USES(OpToNumber, operand)
+    USES(OpToNumeric, operand)
     USES(OpToString, operand)
     USES(OpToObject, operand)
     USES(OpNegate, operand)
@@ -436,6 +437,7 @@ void computeDefsForBytecodeIndex(Block* codeBlock, OpcodeID opcodeID, const Inst
     DEFS(OpInById, dst)
     DEFS(OpInByVal, dst)
     DEFS(OpToNumber, dst)
+    DEFS(OpToNumeric, dst)
     DEFS(OpToString, dst)
     DEFS(OpToObject, dst)
     DEFS(OpNegate, dst)
index 681dfc9..869f53f 100644 (file)
@@ -532,6 +532,7 @@ bool CodeBlock::finishCreation(VM& vm, ScriptExecutable* ownerExecutable, Unlink
         LINK(OpGetByValWithThis, profile)
         LINK(OpGetFromArguments, profile)
         LINK(OpToNumber, profile)
+        LINK(OpToNumeric, profile)
         LINK(OpToObject, profile)
         LINK(OpGetArgument, profile)
         LINK(OpGetInternalField, profile)
@@ -571,6 +572,8 @@ bool CodeBlock::finishCreation(VM& vm, ScriptExecutable* ownerExecutable, Unlink
         LINK(OpSub)
 
         LINK(OpNegate)
+        LINK(OpInc)
+        LINK(OpDec)
 
         LINK(OpJneqPtr)
 
@@ -3117,6 +3120,10 @@ UnaryArithProfile* CodeBlock::unaryArithProfileForPC(const Instruction* pc)
     switch (pc->opcodeID()) {
     case op_negate:
         return &pc->as<OpNegate>().metadata(this).m_arithProfile;
+    case op_inc:
+        return &pc->as<OpInc>().metadata(this).m_arithProfile;
+    case op_dec:
+        return &pc->as<OpDec>().metadata(this).m_arithProfile;
     default:
         break;
     }
index c2f0e32..9105dac 100644 (file)
@@ -54,7 +54,7 @@ enum ExitKind : uint8_t {
     WatchdogTimerFired, // We exited because we need to service the watchdog timer.
     DebuggerEvent, // We exited because we need to service the debugger.
     ExceptionCheck, // We exited because a direct exception check showed that we threw an exception from a C call.
-    GenericUnwind, // We exited because we arrived at this OSR exit from genericUnwind.
+    GenericUnwind, // We exited because we arrived at this OSR exit from genericUnwind.
 };
 
 const char* exitKindToString(ExitKind);
index d3debed..c02a6c6 100644 (file)
@@ -102,6 +102,7 @@ extern const unsigned wasmOpcodeLengths[];
     macro(OpGetByValWithThis) \
     macro(OpGetFromArguments) \
     macro(OpToNumber) \
+    macro(OpToNumeric) \
     macro(OpToObject) \
     macro(OpGetArgument) \
     macro(OpGetInternalField) \
index 419eebd..1053ba5 100644 (file)
@@ -703,6 +703,17 @@ SpeculatedType typeOfDoubleDifference(SpeculatedType a, SpeculatedType b)
     return typeOfDoubleSumOrDifferenceOrProduct(a, b);
 }
 
+SpeculatedType typeOfDoubleIncOrDec(SpeculatedType t)
+{
+    // Impure NaN could become pure NaN during addition because addition may clear bits.
+    if (t & SpecDoubleImpureNaN)
+        t |= SpecDoublePureNaN;
+    // Values could overflow, or fractions could become integers.
+    if (t & SpecDoubleReal)
+        t |= SpecDoubleReal;
+    return t;
+}
+
 SpeculatedType typeOfDoubleProduct(SpeculatedType a, SpeculatedType b)
 {
     return typeOfDoubleSumOrDifferenceOrProduct(a, b);
index a11238c..5db18c6 100644 (file)
@@ -344,12 +344,12 @@ inline bool isInt32OrBooleanSpeculation(SpeculatedType value)
 
 inline bool isInt32SpeculationForArithmetic(SpeculatedType value)
 {
-    return !(value & (SpecFullDouble | SpecNonInt32AsInt52));
+    return !(value & (SpecFullDouble | SpecNonInt32AsInt52 | SpecBigInt));
 }
 
 inline bool isInt32OrBooleanSpeculationForArithmetic(SpeculatedType value)
 {
-    return !(value & (SpecFullDouble | SpecNonInt32AsInt52));
+    return !(value & (SpecFullDouble | SpecNonInt32AsInt52 | SpecBigInt));
 }
 
 inline bool isInt32OrBooleanSpeculationExpectingDefined(SpeculatedType value)
@@ -512,6 +512,7 @@ bool valuesCouldBeEqual(SpeculatedType, SpeculatedType);
 // the closest one of these that applies.
 SpeculatedType typeOfDoubleSum(SpeculatedType, SpeculatedType);
 SpeculatedType typeOfDoubleDifference(SpeculatedType, SpeculatedType);
+SpeculatedType typeOfDoubleIncOrDec(SpeculatedType);
 SpeculatedType typeOfDoubleProduct(SpeculatedType, SpeculatedType);
 SpeculatedType typeOfDoubleQuotient(SpeculatedType, SpeculatedType);
 SpeculatedType typeOfDoubleMinMax(SpeculatedType, SpeculatedType);
index f2bce62..164696b 100644 (file)
@@ -1531,6 +1531,9 @@ RegisterID* BytecodeGenerator::emitUnaryOp(OpcodeID opcodeID, RegisterID* dst, R
     case op_to_number:
         emitUnaryOp<OpToNumber>(dst, src);
         break;
+    case op_to_numeric:
+        emitUnaryOp<OpToNumeric>(dst, src);
+        break;
     default:
         ASSERT_NOT_REACHED();
     }
@@ -1601,6 +1604,11 @@ RegisterID* BytecodeGenerator::emitToNumber(RegisterID* dst, RegisterID* src)
     return emitUnaryOp<OpToNumber>(dst, src);
 }
 
+RegisterID* BytecodeGenerator::emitToNumeric(RegisterID* dst, RegisterID* src)
+{
+    return emitUnaryOp<OpToNumeric>(dst, src);
+}
+
 RegisterID* BytecodeGenerator::emitToString(RegisterID* dst, RegisterID* src)
 {
     return emitUnaryOp<OpToString>(dst, src);
index 75a0ba0..93b13f6 100644 (file)
@@ -752,6 +752,7 @@ namespace JSC {
         RegisterID* moveEmptyValue(RegisterID* dst);
 
         RegisterID* emitToNumber(RegisterID* dst, RegisterID* src);
+        RegisterID* emitToNumeric(RegisterID* dst, RegisterID* src);
         RegisterID* emitToString(RegisterID* dst, RegisterID* src);
         RegisterID* emitToObject(RegisterID* dst, RegisterID* src, const Identifier& message);
         RegisterID* emitInc(RegisterID* srcDst);
index 01064ca..b52ca82 100644 (file)
@@ -1790,8 +1790,8 @@ static RegisterID* emitIncOrDec(BytecodeGenerator& generator, RegisterID* srcDst
 static RegisterID* emitPostIncOrDec(BytecodeGenerator& generator, RegisterID* dst, RegisterID* srcDst, Operator oper)
 {
     if (dst == srcDst)
-        return generator.emitToNumber(generator.finalDestination(dst), srcDst);
-    RefPtr<RegisterID> tmp = generator.emitToNumber(generator.newTemporary(), srcDst);
+        return generator.emitToNumeric(generator.finalDestination(dst), srcDst);
+    RefPtr<RegisterID> tmp = generator.emitToNumeric(generator.newTemporary(), srcDst);
     RefPtr<RegisterID> result = generator.tempDestination(srcDst);
     generator.move(result.get(), tmp.get());
     emitIncOrDec(generator, result.get(), oper);
index aee808b..d0ae0a6 100644 (file)
@@ -930,6 +930,31 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
         }
         break;
     }
+
+    case Inc:
+    case Dec: {
+        // FIXME: support some form of constant folding here.
+        // https://bugs.webkit.org/show_bug.cgi?id=204258
+        switch (node->child1().useKind()) {
+        case Int32Use:
+            setNonCellTypeForNode(node, SpecInt32Only);
+            break;
+        case Int52RepUse:
+            setNonCellTypeForNode(node, SpecInt52Any);
+            break;
+        case DoubleRepUse:
+            setNonCellTypeForNode(node, typeOfDoubleIncOrDec(forNode(node->child1()).m_type));
+            break;
+        case BigIntUse:
+            setTypeForNode(node, SpecBigInt);
+            break;
+        default:
+            setTypeForNode(node, SpecBytecodeNumber | SpecBigInt);
+            clobberWorld(); // Because of the call to ToNumeric()
+            break;
+        }
+        break;
+    }
         
     case ValuePow: {
         JSValue childX = forNode(node->child1()).value();
@@ -2512,6 +2537,28 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
         setNonCellTypeForNode(node, SpecBytecodeNumber);
         break;
     }
+
+    case ToNumeric: {
+        JSValue childConst = forNode(node->child1()).value();
+        if (childConst && (childConst.isNumber() || childConst.isBigInt())) {
+            didFoldClobberWorld();
+            setConstant(node, childConst);
+            break;
+        }
+
+        ASSERT(node->child1().useKind() == UntypedUse);
+
+        if (!(forNode(node->child1()).m_type & ~(SpecBytecodeNumber | SpecBigInt))) {
+            m_state.setShouldTryConstantFolding(true);
+            didFoldClobberWorld();
+            setForNode(node, forNode(node->child1()));
+            break;
+        }
+
+        clobberWorld();
+        setTypeForNode(node, SpecBytecodeNumber | SpecBigInt);
+        break;
+    }
         
     case ToString:
     case CallStringConstructor: {
index 0280171..36865a5 100644 (file)
@@ -335,7 +335,20 @@ private:
             node->child1()->mergeFlags(flags);
             break;
         }
-            
+
+        case Inc:
+        case Dec: {
+            flags &= ~NodeBytecodeNeedsNegZero;
+            flags &= ~NodeBytecodeUsesAsOther;
+            if (!isWithinPowerOfTwo<32>(node->child1()))
+                flags |= NodeBytecodeUsesAsNumber;
+            if (!m_allowNestedOverflowingAdditions)
+                flags |= NodeBytecodeUsesAsNumber;
+
+            node->child1()->mergeFlags(flags);
+            break;
+        }
+
         case ValueMul:
         case ArithMul: {
             // As soon as a multiply happens, we can easily end up in the part
@@ -401,7 +414,8 @@ private:
         }
             
         case ToPrimitive:
-        case ToNumber: {
+        case ToNumber:
+        case ToNumeric: {
             node->child1()->mergeFlags(flags);
             break;
         }
index 98e68ec..34f45e8 100644 (file)
@@ -946,14 +946,20 @@ private:
         case ArithAdd:
         case ArithSub:
         case ValueAdd: {
-            BinaryArithProfile* arithProfile = m_inlineStackTop->m_profiledBlock->binaryArithProfileForBytecodeIndex(m_currentIndex);
-            if (!arithProfile)
+            ObservedResults observed;
+            if (BinaryArithProfile* arithProfile = m_inlineStackTop->m_profiledBlock->binaryArithProfileForBytecodeIndex(m_currentIndex))
+                observed = arithProfile->observedResults();
+            else if (UnaryArithProfile* arithProfile = m_inlineStackTop->m_profiledBlock->unaryArithProfileForBytecodeIndex(m_currentIndex)) {
+                // Happens for OpInc/OpDec
+                observed = arithProfile->observedResults();
+            } else
                 break;
-            if (arithProfile->didObserveDouble())
+
+            if (observed.didObserveDouble())
                 node->mergeFlags(NodeMayHaveDoubleResult);
-            if (arithProfile->didObserveNonNumeric())
+            if (observed.didObserveNonNumeric())
                 node->mergeFlags(NodeMayHaveNonNumericResult);
-            if (arithProfile->didObserveBigInt())
+            if (observed.didObserveBigInt())
                 node->mergeFlags(NodeMayHaveBigIntResult);
             break;
         }
@@ -977,7 +983,9 @@ private:
             break;
         }
         case ValueNegate:
-        case ArithNegate: {
+        case ArithNegate:
+        case Inc:
+        case Dec: {
             UnaryArithProfile* arithProfile = m_inlineStackTop->m_profiledBlock->unaryArithProfileForBytecodeIndex(m_currentIndex);
             if (!arithProfile)
                 break;
@@ -5215,14 +5223,20 @@ void ByteCodeParser::parseBlock(unsigned limit)
         case op_inc: {
             auto bytecode = currentInstruction->as<OpInc>();
             Node* op = get(bytecode.m_srcDst);
-            set(bytecode.m_srcDst, makeSafe(addToGraph(ArithAdd, op, addToGraph(JSConstant, OpInfo(m_constantOne)))));
+            // FIXME: we can replace the Inc by either ArithAdd with m_constantOne or ArithAdd with the equivalent BigInt in many cases.
+            // For now we only do so in DFGFixupPhase.
+            // We could probably do it earlier in some cases, but it is not clearly worth the trouble.
+            set(bytecode.m_srcDst, makeSafe(addToGraph(Inc, op)));
             NEXT_OPCODE(op_inc);
         }
 
         case op_dec: {
             auto bytecode = currentInstruction->as<OpDec>();
             Node* op = get(bytecode.m_srcDst);
-            set(bytecode.m_srcDst, makeSafe(addToGraph(ArithSub, op, addToGraph(JSConstant, OpInfo(m_constantOne)))));
+            // FIXME: we can replace the Inc by either ArithSub with m_constantOne or ArithSub with the equivalent BigInt in many cases.
+            // For now we only do so in DFGFixupPhase.
+            // We could probably do it earlier in some cases, but it is not clearly worth the trouble.
+            set(bytecode.m_srcDst, makeSafe(addToGraph(Dec, op)));
             NEXT_OPCODE(op_dec);
         }
 
@@ -6971,6 +6985,14 @@ void ByteCodeParser::parseBlock(unsigned limit)
             NEXT_OPCODE(op_to_number);
         }
 
+        case op_to_numeric: {
+            auto bytecode = currentInstruction->as<OpToNumeric>();
+            SpeculatedType prediction = getPrediction();
+            Node* value = get(bytecode.m_operand);
+            set(bytecode.m_dst, addToGraph(ToNumeric, OpInfo(0), OpInfo(prediction), value));
+            NEXT_OPCODE(op_to_numeric);
+        }
+
         case op_to_string: {
             auto bytecode = currentInstruction->as<OpToString>();
             Node* value = get(bytecode.m_operand);
index 1889d99..fd0b6d7 100644 (file)
@@ -240,6 +240,7 @@ CapabilityLevel capabilityLevel(OpcodeID opcodeID, CodeBlock* codeBlock, const I
     case op_jneq_ptr:
     case op_typeof:
     case op_to_number:
+    case op_to_numeric:
     case op_to_string:
     case op_to_object:
     case op_switch_imm:
index b747915..5df6300 100644 (file)
@@ -665,6 +665,7 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu
     case GetDirectPname:
     case InstanceOfCustom:
     case ToNumber:
+    case ToNumeric:
     case NumberToStringWithRadix:
     case CreateThis:
     case CreatePromise:
@@ -677,6 +678,23 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu
         write(Heap);
         return;
 
+    case Inc:
+    case Dec:
+        switch (node->child1().useKind()) {
+        case Int32Use:
+        case Int52RepUse:
+        case DoubleRepUse:
+        case BigIntUse:
+            def(PureValue(node));
+            return;
+        case UntypedUse:
+            read(World);
+            write(Heap);
+            return;
+        default:
+            DFG_CRASH(graph, node, "Bad use kind");
+        }
+
     case ValueBitAnd:
     case ValueBitXor:
     case ValueBitOr:
index a4f6a8a..4a44513 100644 (file)
@@ -863,6 +863,15 @@ private:
                 break;
             }
 
+            case ToNumeric: {
+                if (m_state.forNode(node->child1()).m_type & ~(SpecBytecodeNumber | SpecBigInt))
+                    break;
+
+                node->convertToIdentity();
+                changed = true;
+                break;
+            }
+
             case NormalizeMapKey: {
                 SpeculatedType typeMaybeNormalized = (SpecFullNumber & ~SpecInt32Only);
                 if (m_state.forNode(node->child1()).m_type & typeMaybeNormalized)
index 1d2be0b..e9662ae 100644 (file)
@@ -329,6 +329,7 @@ bool doesGC(Graph& graph, Node* node)
     case TailCallVarargsInlinedCaller:
     case Throw:
     case ToNumber:
+    case ToNumeric:
     case ToObject:
     case ToPrimitive:
     case ToThis:
@@ -528,6 +529,17 @@ bool doesGC(Graph& graph, Node* node)
         }
         RELEASE_ASSERT_NOT_REACHED();
 
+    case Inc:
+    case Dec:
+        switch (node->child1().useKind()) {
+        case Int32Use:
+        case Int52RepUse:
+        case DoubleRepUse:
+            return false;
+        default:
+            return true;
+        }
+
     case LastNodeType:
         RELEASE_ASSERT_NOT_REACHED();
     }
index 6bcb515..bba4304 100644 (file)
@@ -185,6 +185,51 @@ private:
             return;
         }
 
+        case Inc:
+        case Dec: {
+            if (node->child1()->shouldSpeculateUntypedForArithmetic()) {
+                fixEdge<UntypedUse>(node->child1());
+                break;
+            }
+
+            Node* nodeConstantOne;
+            if (node->child1()->shouldSpeculateInt32OrBoolean() && node->canSpeculateInt32(FixupPass)) {
+                node->setOp(op == Inc ? ArithAdd : ArithSub);
+                node->setArithMode(Arith::CheckOverflow);
+                nodeConstantOne = m_insertionSet.insertNode(m_indexInBlock, SpecInt32Only, JSConstant, node->origin, OpInfo(m_graph.freeze(jsNumber(1))));
+                node->children.setChild2(Edge(nodeConstantOne));
+                fixEdge<Int32Use>(node->child1());
+                fixEdge<Int32Use>(node->child2());
+                node->setResult(NodeResultInt32);
+            } else if (node->child1()->shouldSpeculateBigInt()) {
+                // FIXME: the freezing does not appear useful (since the JSCell is kept alive by vm), but it refuses to compile otherwise.
+                node->setOp(op == Inc ? ValueAdd : ValueSub);
+                nodeConstantOne = m_insertionSet.insertNode(m_indexInBlock, SpecBigInt, JSConstant, node->origin, OpInfo(m_graph.freeze(vm().bigIntConstantOne.get())));
+                node->children.setChild2(Edge(nodeConstantOne));
+                fixEdge<BigIntUse>(node->child1());
+                fixEdge<BigIntUse>(node->child2());
+                // BigInts are currently cells, so the default of NodeResultJS is good here
+            } else if (node->child1()->shouldSpeculateInt52()) {
+                node->setOp(op == Inc ? ArithAdd : ArithSub);
+                node->setArithMode(Arith::CheckOverflow);
+                nodeConstantOne = m_insertionSet.insertNode(m_indexInBlock, SpecInt32AsInt52, JSConstant, node->origin, OpInfo(m_graph.freeze(jsNumber(1))));
+                node->children.setChild2(Edge(nodeConstantOne));
+                fixEdge<Int52RepUse>(node->child1());
+                fixEdge<Int52RepUse>(node->child2());
+                node->setResult(NodeResultInt52);
+            } else {
+                node->setOp(op == Inc ? ArithAdd : ArithSub);
+                node->setArithMode(Arith::Unchecked);
+                nodeConstantOne = m_insertionSet.insertNode(m_indexInBlock, SpecBytecodeDouble, JSConstant, node->origin, OpInfo(m_graph.freeze(jsNumber(1))));
+                node->children.setChild2(Edge(nodeConstantOne));
+                fixEdge<DoubleRepUse>(node->child1());
+                fixEdge<DoubleRepUse>(node->child2());
+                node->setResult(NodeResultDouble);
+            }
+            node->clearFlags(NodeMustGenerate);
+            break;
+        }
+
         case ValueSub: {
             Edge& child1 = node->child1();
             Edge& child2 = node->child2();
@@ -1350,6 +1395,11 @@ private:
             fixupToNumber(node);
             break;
         }
+
+        case ToNumeric: {
+            fixupToNumeric(node);
+            break;
+        }
             
         case ToString:
         case CallStringConstructor: {
@@ -2835,6 +2885,18 @@ private:
         }
     }
 
+    void fixupToNumeric(Node* node)
+    {
+        // If the prediction of the child is BigInt, we attempt to convert ToNumeric to Identity, since it can only return a BigInt when fed a BigInt.
+        if (node->child1()->shouldSpeculateBigInt()) {
+            fixEdge<BigIntUse>(node->child1());
+            node->convertToIdentity();
+            return;
+        }
+
+        fixupToNumber(node);
+    }
+
     void fixupToNumber(Node* node)
     {
         // At first, attempt to fold Boolean or Int32 to Int32.
index f8ee99c..c7fa5c1 100644 (file)
@@ -129,6 +129,7 @@ ExitMode mayExitImpl(Graph& graph, Node* node, StateType& state)
     case NewSymbol:
     case NewRegexp:
     case ToNumber:
+    case ToNumeric:
     case RegExpExecNonGlobalOrSticky:
     case RegExpMatchFastGlobal:
         result = ExitsForExceptions;
index c1053c7..2df78a9 100644 (file)
@@ -1738,6 +1738,7 @@ public:
         case StringReplace:
         case StringReplaceRegExp:
         case ToNumber:
+        case ToNumeric:
         case ToObject:
         case ValueBitAnd:
         case ValueBitOr:
index 2881f4c..f1c0a39 100644 (file)
@@ -175,7 +175,10 @@ namespace JSC { namespace DFG {
     macro(ArithSqrt, NodeResultDouble | NodeMustGenerate) \
     macro(ArithUnary, NodeResultDouble | NodeMustGenerate) \
     \
-    /* BigInt is a valid operand for negate operations */\
+    /* BigInt is a valid operand for these three operations */\
+    /* Inc and Dec don't update their operand in-place, they are typically combined with some form of SetLocal */\
+    macro(Inc, NodeResultJS | NodeMustGenerate) \
+    macro(Dec, NodeResultJS | NodeMustGenerate) \
     macro(ValueNegate, NodeResultJS | NodeMustGenerate) \
     \
     /* Add of values may either be arithmetic, or result in string concatenation. */\
@@ -394,6 +397,7 @@ namespace JSC { namespace DFG {
     macro(ToPrimitive, NodeResultJS | NodeMustGenerate) \
     macro(ToString, NodeResultJS | NodeMustGenerate) \
     macro(ToNumber, NodeResultJS | NodeMustGenerate) \
+    macro(ToNumeric, NodeResultJS | NodeMustGenerate) \
     macro(ToObject, NodeResultJS | NodeMustGenerate) \
     macro(CallObjectConstructor, NodeResultJS) \
     macro(CallStringConstructor, NodeResultJS | NodeMustGenerate) \
index 2076cd6..e1a5bf0 100644 (file)
@@ -481,6 +481,44 @@ EncodedJSValue JIT_OPERATION operationValueMod(JSGlobalObject* globalObject, Enc
     return binaryOp(globalObject, vm, encodedOp1, encodedOp2, bigIntOp, numberOp, "Invalid mix of BigInt and other type in remainder operation.");
 }
 
+EncodedJSValue JIT_OPERATION operationInc(JSGlobalObject* globalObject, EncodedJSValue encodedOp1)
+{
+    VM& vm = globalObject->vm();
+    CallFrame* callFrame = DECLARE_CALL_FRAME(vm);
+    JITOperationPrologueCallFrameTracer tracer(vm, callFrame);
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
+    JSValue op1 = JSValue::decode(encodedOp1);
+
+    auto operandNumeric = op1.toNumeric(globalObject);
+    RETURN_IF_EXCEPTION(scope, encodedJSValue());
+
+    if (WTF::holds_alternative<JSBigInt*>(operandNumeric))
+        RELEASE_AND_RETURN(scope, JSValue::encode(JSBigInt::inc(globalObject, WTF::get<JSBigInt*>(operandNumeric))));
+
+    double value = WTF::get<double>(operandNumeric);
+    return JSValue::encode(jsNumber(value + 1));
+}
+
+EncodedJSValue JIT_OPERATION operationDec(JSGlobalObject* globalObject, EncodedJSValue encodedOp1)
+{
+    VM& vm = globalObject->vm();
+    CallFrame* callFrame = DECLARE_CALL_FRAME(vm);
+    JITOperationPrologueCallFrameTracer tracer(vm, callFrame);
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
+    JSValue op1 = JSValue::decode(encodedOp1);
+
+    auto operandNumeric = op1.toNumeric(globalObject);
+    RETURN_IF_EXCEPTION(scope, encodedJSValue());
+
+    if (WTF::holds_alternative<JSBigInt*>(operandNumeric))
+        RELEASE_AND_RETURN(scope, JSValue::encode(JSBigInt::dec(globalObject, WTF::get<JSBigInt*>(operandNumeric))));
+
+    double value = WTF::get<double>(operandNumeric);
+    return JSValue::encode(jsNumber(value - 1));
+}
+
 EncodedJSValue JIT_OPERATION operationValueBitNot(JSGlobalObject* globalObject, EncodedJSValue encodedOp1)
 {
     VM& vm = globalObject->vm();
@@ -1676,6 +1714,18 @@ EncodedJSValue JIT_OPERATION operationToNumber(JSGlobalObject* globalObject, Enc
     return JSValue::encode(jsNumber(JSValue::decode(value).toNumber(globalObject)));
 }
 
+EncodedJSValue JIT_OPERATION operationToNumeric(JSGlobalObject* globalObject, EncodedJSValue value)
+{
+    VM& vm = globalObject->vm();
+    CallFrame* callFrame = DECLARE_CALL_FRAME(vm);
+    JITOperationPrologueCallFrameTracer tracer(vm, callFrame);
+
+    auto variant = JSValue::decode(value).toNumeric(globalObject);
+    if (WTF::holds_alternative<JSBigInt*>(variant))
+        return JSValue::encode(WTF::get<JSBigInt*>(variant));
+    return JSValue::encode(jsNumber(WTF::get<double>(variant)));
+}
+
 EncodedJSValue JIT_OPERATION operationGetByValWithThis(JSGlobalObject* globalObject, EncodedJSValue encodedBase, EncodedJSValue encodedThis, EncodedJSValue encodedSubscript)
 {
     VM& vm = globalObject->vm();
index 274f8a5..7090cfe 100644 (file)
@@ -69,6 +69,8 @@ EncodedJSValue JIT_OPERATION operationValueBitURShift(JSGlobalObject*, EncodedJS
 EncodedJSValue JIT_OPERATION operationValueAddNotNumber(JSGlobalObject*, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationValueDiv(JSGlobalObject*, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationValuePow(JSGlobalObject*, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2) WTF_INTERNAL;
+EncodedJSValue JIT_OPERATION operationInc(JSGlobalObject*, EncodedJSValue encodedOp1) WTF_INTERNAL;
+EncodedJSValue JIT_OPERATION operationDec(JSGlobalObject*, EncodedJSValue encodedOp1) WTF_INTERNAL;
 double JIT_OPERATION operationArithAbs(JSGlobalObject*, EncodedJSValue encodedOp1) WTF_INTERNAL;
 uint32_t JIT_OPERATION operationArithClz32(JSGlobalObject*, EncodedJSValue encodedOp1) WTF_INTERNAL;
 double JIT_OPERATION operationArithFRound(JSGlobalObject*, EncodedJSValue encodedOp1) WTF_INTERNAL;
@@ -91,6 +93,7 @@ EncodedJSValue JIT_OPERATION operationGetByValObjectString(JSGlobalObject*, JSCe
 EncodedJSValue JIT_OPERATION operationGetByValObjectSymbol(JSGlobalObject*, JSCell*, JSCell* symbol) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationToPrimitive(JSGlobalObject*, EncodedJSValue) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationToNumber(JSGlobalObject*, EncodedJSValue) WTF_INTERNAL;
+EncodedJSValue JIT_OPERATION operationToNumeric(JSGlobalObject*, EncodedJSValue) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationGetByValWithThis(JSGlobalObject*, EncodedJSValue, EncodedJSValue, EncodedJSValue) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationGetPrototypeOf(JSGlobalObject*, EncodedJSValue) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationGetPrototypeOfObject(JSGlobalObject*, JSObject*) WTF_INTERNAL;
index 4a5cd95..5a7abe8 100644 (file)
@@ -294,6 +294,31 @@ private:
             break;
         }
 
+        case Inc:
+        case Dec: {
+            SpeculatedType prediction = node->child1()->prediction();
+
+            if (prediction) {
+                if (isFullNumberOrBooleanSpeculationExpectingDefined(prediction)) {
+                    if (m_graph.unaryArithShouldSpeculateInt32(node, m_pass))
+                        changed |= mergePrediction(SpecInt32Only);
+                    else if (m_graph.unaryArithShouldSpeculateInt52(node, m_pass))
+                        changed |= mergePrediction(SpecInt52Any);
+                    else
+                        changed |= mergePrediction(speculatedDoubleTypeForPrediction(prediction));
+                } else if (isBigIntSpeculation(prediction))
+                    changed |= mergePrediction(SpecBigInt);
+                else {
+                    changed |= mergePrediction(SpecInt32Only);
+                    if (node->mayHaveDoubleResult())
+                        changed |= mergePrediction(SpecBytecodeDouble);
+                    if (node->mayHaveBigIntResult())
+                        changed |= mergePrediction(SpecBigInt);
+                }
+            }
+            break;
+        }
+
         case ValuePow: {
             SpeculatedType left = node->child1()->prediction();
             SpeculatedType right = node->child2()->prediction();
@@ -833,6 +858,7 @@ private:
         case LoadKeyFromMapBucket:
         case LoadValueFromMapBucket:
         case ToNumber:
+        case ToNumeric:
         case ToObject:
         case ValueBitAnd:
         case ValueBitXor:
@@ -1176,6 +1202,8 @@ private:
         case ValuePow:
         case ValueBitLShift:
         case ValueBitRShift:
+        case Inc:
+        case Dec:
         case ArithAdd:
         case ArithSub:
         case ArithNegate:
index 69c4173..5635c7c 100644 (file)
@@ -240,6 +240,8 @@ bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node, bool igno
     case ValueBitNot:
     case ValueBitLShift:
     case ValueBitRShift:
+    case Inc:
+    case Dec:
     case ValueNegate:
     case ValueAdd:
     case ValueSub:
@@ -358,6 +360,7 @@ bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node, bool igno
     case ToPrimitive:
     case ToString:
     case ToNumber:
+    case ToNumeric:
     case ToObject:
     case NumberToStringWithRadix:
     case NumberToStringWithValidRadixConstant:
index 89634f3..109f79a 100644 (file)
@@ -4609,6 +4609,22 @@ void SpeculativeJIT::compileArithSub(Node* node)
     }
 }
 
+void SpeculativeJIT::compileIncOrDec(Node* node)
+{
+    // In all other cases the node should have been transformed into an add or a sub by FixupPhase
+    ASSERT(node->child1().useKind() == UntypedUse);
+
+    JSValueOperand op1(this, node->child1());
+    JSValueRegs op1Regs = op1.jsValueRegs();
+    flushRegisters();
+    GPRFlushedCallResult result(this);
+    GPRReg resultGPR = result.gpr();
+    auto operation = node->op() == Inc ? operationInc : operationDec;
+    callOperation(operation, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), op1Regs);
+    m_jit.exceptionCheck();
+    jsValueResult(result.gpr(), node);
+}
+
 void SpeculativeJIT::compileValueNegate(Node* node)
 {
     CodeBlock* baselineCodeBlock = m_jit.graph().baselineCodeBlockFor(node->origin.semantic);
@@ -12935,6 +12951,34 @@ void SpeculativeJIT::compileToPrimitive(Node* node)
     jsValueResult(resultRegs, node, DataFormatJS, UseChildrenCalledExplicitly);
 }
 
+void SpeculativeJIT::compileToNumeric(Node* node)
+{
+    DFG_ASSERT(m_jit.graph(), node, node->child1().useKind() == UntypedUse, node->child1().useKind());
+    JSValueOperand argument(this, node->child1());
+    JSValueRegsTemporary result(this);
+    GPRTemporary temp(this);
+
+    JSValueRegs argumentRegs = argument.jsValueRegs();
+    JSValueRegs resultRegs = result.regs();
+    GPRReg scratch = temp.gpr();
+
+    MacroAssembler::JumpList slowCases;
+
+    MacroAssembler::Jump notCell = m_jit.branchIfNotCell(argumentRegs);
+    slowCases.append(m_jit.branchIfNotBigInt(argumentRegs.payloadGPR()));
+    MacroAssembler::Jump isBigInt = m_jit.jump();
+
+    notCell.link(&m_jit);
+    slowCases.append(m_jit.branchIfNotNumber(argumentRegs, scratch));
+
+    isBigInt.link(&m_jit);
+    m_jit.moveValueRegs(argumentRegs, resultRegs);
+
+    addSlowPathGenerator(slowPathCall(slowCases, this, operationToNumeric, resultRegs, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), argumentRegs));
+
+    jsValueResult(resultRegs, node, DataFormatJS);
+}
+
 void SpeculativeJIT::compileLogShadowChickenPrologue(Node* node)
 {
     flushRegisters();
index 1b20d94..ed9ae10 100644 (file)
@@ -1341,6 +1341,7 @@ public:
     void compileArithAbs(Node*);
     void compileArithClz32(Node*);
     void compileArithSub(Node*);
+    void compileIncOrDec(Node*);
     void compileValueNegate(Node*);
     void compileArithNegate(Node*);
     void compileValueMul(Node*);
@@ -1482,6 +1483,7 @@ public:
     void compileNewGenerator(Node*);
     void compileNewAsyncGenerator(Node*);
     void compileToPrimitive(Node*);
+    void compileToNumeric(Node*);
     void compileLogShadowChickenPrologue(Node*);
     void compileLogShadowChickenTail(Node*);
     void compileHasIndexedProperty(Node*);
index f6c6e2d..7e57539 100644 (file)
@@ -3079,6 +3079,11 @@ void SpeculativeJIT::compile(Node* node)
         jsValueResult(resultRegs.tagGPR(), resultRegs.payloadGPR(), node, UseChildrenCalledExplicitly);
         break;
     }
+
+    case ToNumeric: {
+        compileToNumeric(node);
+        break;
+    }
         
     case ToString:
     case CallStringConstructor:
index 2aa9356..fc0342c 100644 (file)
@@ -1930,6 +1930,11 @@ void SpeculativeJIT::compile(Node* node)
         break;
     }
 
+    case Inc:
+    case Dec:
+        compileIncOrDec(node);
+        break;
+
     case GetLocal: {
         AbstractValue& value = m_state.operand(node->local());
 
@@ -3408,7 +3413,12 @@ void SpeculativeJIT::compile(Node* node)
         jsValueResult(resultGPR, node, UseChildrenCalledExplicitly);
         break;
     }
-        
+
+    case ToNumeric: {
+        compileToNumeric(node);
+        break;
+    }
+
     case ToString:
     case CallStringConstructor:
     case StringValueOf: {
index 30fc4d4..02f20e3 100644 (file)
@@ -105,6 +105,8 @@ inline CapabilityLevel canCompile(Node* node)
     case ValueDiv:
     case ValueMod:
     case ValuePow:
+    case Inc:
+    case Dec:
     case StrCat:
     case ArithAdd:
     case ArithClz32:
@@ -208,6 +210,7 @@ inline CapabilityLevel canCompile(Node* node)
     case GetArgumentCountIncludingThis:
     case SetArgumentCountIncludingThis:
     case ToNumber:
+    case ToNumeric:
     case ToString:
     case ToObject:
     case CallObjectConstructor:
index e5e3753..eed7df3 100644 (file)
@@ -752,6 +752,10 @@ private:
         case ToThis:
             compileToThis();
             break;
+        case Inc:
+        case Dec:
+            compileIncOrDec();
+            break;
         case ValueNegate:
             compileValueNegate();
             break;
@@ -1114,6 +1118,9 @@ private:
         case ToNumber:
             compileToNumber();
             break;
+        case ToNumeric:
+            compileToNumeric();
+            break;
         case ToString:
         case CallStringConstructor:
         case StringValueOf:
@@ -3093,6 +3100,15 @@ private:
         setDouble(result);
     }
 
+    void compileIncOrDec()
+    {
+        DFG_ASSERT(m_graph, m_node, m_node->child1().useKind() == UntypedUse);
+        JSGlobalObject* globalObject = m_graph.globalObjectFor(m_node->origin.semantic);
+        LValue operand = lowJSValue(m_node->child1());
+        LValue result = vmCall(Int64, m_node->op() == Inc ? operationInc : operationDec, weakPointer(globalObject), operand);
+        setJSValue(result);
+    }
+
     void compileValueNegate()
     {
         DFG_ASSERT(m_graph, m_node, m_node->child1().useKind() == UntypedUse);
@@ -6900,6 +6916,41 @@ private:
         }
     }
     
+    void compileToNumeric()
+    {
+        JSGlobalObject* globalObject = m_graph.globalObjectFor(m_node->origin.semantic);
+        LValue value = lowJSValue(m_node->child1());
+        
+        if (abstractValue(m_node->child1()).m_type & (SpecBytecodeNumber | SpecBigInt)) {
+            LBasicBlock notNumber = m_out.newBlock();
+            LBasicBlock isCellPath = m_out.newBlock();
+            LBasicBlock slowPath = m_out.newBlock();
+            LBasicBlock continuation = m_out.newBlock();
+
+            ValueFromBlock fastResult = m_out.anchor(value);
+            m_out.branch(isNumber(value, provenType(m_node->child1())), unsure(continuation), unsure(notNumber));
+
+            // notNumber case.
+            LBasicBlock lastNext = m_out.appendTo(notNumber, continuation);
+            m_out.branch(isCell(value, provenType(m_node->child1())), unsure(isCellPath), unsure(slowPath));
+
+            m_out.appendTo(isCellPath);
+            m_out.branch(isBigInt(value, provenType(m_node->child1())), unsure(continuation), unsure(slowPath));
+            
+            m_out.appendTo(slowPath);
+            // We have several attempts to remove ToNumeric. But ToNumeric still exists.
+            // It means that the slow path is not rare.
+            // Instead of the lazy slow path generator, we call the operation here.
+            ValueFromBlock slowResult = m_out.anchor(vmCall(Int64, operationToNumeric, weakPointer(globalObject), value));
+            m_out.jump(continuation);
+
+            // continuation case.
+            m_out.appendTo(continuation, lastNext);
+            setJSValue(m_out.phi(Int64, fastResult, slowResult));
+        } else
+            setJSValue(vmCall(Int64, operationToNumeric, weakPointer(globalObject), value));
+    }
+    
     void compileToStringOrCallStringConstructorOrStringValueOf()
     {
         ASSERT(m_node->op() != StringValueOf || m_node->child1().useKind() == UntypedUse);
index 28a7317..a4d912e 100644 (file)
@@ -440,6 +440,7 @@ void JIT::privateCompileMainPass()
         DEFINE_OP(op_switch_string)
         DEFINE_OP(op_throw)
         DEFINE_OP(op_to_number)
+        DEFINE_OP(op_to_numeric)
         DEFINE_OP(op_to_string)
         DEFINE_OP(op_to_object)
         DEFINE_OP(op_to_primitive)
@@ -586,6 +587,7 @@ void JIT::privateCompileSlowCases()
         DEFINE_SLOWCASE_SLOW_OP(to_this)
         DEFINE_SLOWCASE_SLOW_OP(to_primitive)
         DEFINE_SLOWCASE_SLOW_OP(to_number)
+        DEFINE_SLOWCASE_SLOW_OP(to_numeric)
         DEFINE_SLOWCASE_SLOW_OP(to_string)
         DEFINE_SLOWCASE_SLOW_OP(to_object)
         DEFINE_SLOWCASE_SLOW_OP(not)
index d7fe4ae..f60f007 100644 (file)
@@ -625,6 +625,7 @@ namespace JSC {
         void emit_op_tear_off_arguments(const Instruction*);
         void emit_op_throw(const Instruction*);
         void emit_op_to_number(const Instruction*);
+        void emit_op_to_numeric(const Instruction*);
         void emit_op_to_string(const Instruction*);
         void emit_op_to_object(const Instruction*);
         void emit_op_to_primitive(const Instruction*);
index fdaec92..f01402e 100644 (file)
@@ -70,21 +70,19 @@ public:
         state.fastPathStart = jit.label();
         size_t startSize = jit.m_assembler.buffer().codeSize();
 
-        if (m_arithProfile) {
-            if (m_arithProfile->isObservedTypeEmpty()) {
-                // It looks like the MathIC has yet to execute. We don't want to emit code in this
-                // case for a couple reasons. First, the operation may never execute, so if we don't emit
-                // code, it's a win. Second, if the operation does execute, we can emit better code
-                // once we have an idea about the types.
-                state.slowPathJumps.append(jit.patchableJump());
-                size_t inlineSize = jit.m_assembler.buffer().codeSize() - startSize;
-                ASSERT_UNUSED(inlineSize, static_cast<ptrdiff_t>(inlineSize) <= MacroAssembler::patchableJumpSize());
-                state.shouldSlowPathRepatch = true;
-                state.fastPathEnd = jit.label();
-                ASSERT(!m_generateFastPathOnRepatch); // We should have gathered some observed type info about the types before trying to regenerate again.
-                m_generateFastPathOnRepatch = true;
-                return true;
-            }
+        if (m_arithProfile && m_arithProfile->isObservedTypeEmpty()) {
+            // It looks like the MathIC has yet to execute. We don't want to emit code in this
+            // case for a couple reasons. First, the operation may never execute, so if we don't emit
+            // code, it's a win. Second, if the operation does execute, we can emit better code
+            // once we have an idea about the types.
+            state.slowPathJumps.append(jit.patchableJump());
+            size_t inlineSize = jit.m_assembler.buffer().codeSize() - startSize;
+            ASSERT_UNUSED(inlineSize, static_cast<ptrdiff_t>(inlineSize) <= MacroAssembler::patchableJumpSize());
+            state.shouldSlowPathRepatch = true;
+            state.fastPathEnd = jit.label();
+            ASSERT(!m_generateFastPathOnRepatch); // We should have gathered some observed type info about the types before trying to regenerate again.
+            m_generateFastPathOnRepatch = true;
+            return true;
         }
 
         JITMathICInlineResult result = m_generator.generateInline(jit, state, m_arithProfile);
index 94487c0..0f38c43 100644 (file)
@@ -205,18 +205,18 @@ bool JITMulGenerator::generateFastPath(CCallHelpers& jit, CCallHelpers::JumpList
 
         CCallHelpers::Jump notNegativeZero = jit.branch64(CCallHelpers::NotEqual, m_result.payloadGPR(), CCallHelpers::TrustedImm64(negativeZeroBits));
 
-        arithProfile->emitUnconditionalSet(jit, BinaryArithProfile::NegZeroDouble);
+        arithProfile->emitUnconditionalSet(jit, ObservedResults::NegZeroDouble);
         CCallHelpers::Jump done = jit.jump();
 
         notNegativeZero.link(&jit);
-        arithProfile->emitUnconditionalSet(jit, BinaryArithProfile::NonNegZeroDouble);
+        arithProfile->emitUnconditionalSet(jit, ObservedResults::NonNegZeroDouble);
 
         jit.move(m_result.payloadGPR(), m_scratchGPR);
         jit.urshiftPtr(CCallHelpers::Imm32(52), m_scratchGPR);
         jit.and32(CCallHelpers::Imm32(0x7ff), m_scratchGPR);
         CCallHelpers::Jump noInt52Overflow = jit.branch32(CCallHelpers::LessThanOrEqual, m_scratchGPR, CCallHelpers::TrustedImm32(0x431));
 
-        arithProfile->emitUnconditionalSet(jit, BinaryArithProfile::Int52Overflow);
+        arithProfile->emitUnconditionalSet(jit, ObservedResults::Int52Overflow);
         noInt52Overflow.link(&jit);
 
         done.link(&jit);
@@ -227,18 +227,18 @@ bool JITMulGenerator::generateFastPath(CCallHelpers& jit, CCallHelpers::JumpList
         notNegativeZero.append(jit.branch32(CCallHelpers::NotEqual, m_result.payloadGPR(), CCallHelpers::TrustedImm32(0)));
         notNegativeZero.append(jit.branch32(CCallHelpers::NotEqual, m_result.tagGPR(), CCallHelpers::TrustedImm32(negativeZeroBits >> 32)));
 
-        arithProfile->emitUnconditionalSet(jit, BinaryArithProfile::NegZeroDouble);
+        arithProfile->emitUnconditionalSet(jit, ObservedResults::NegZeroDouble);
         CCallHelpers::Jump done = jit.jump();
 
         notNegativeZero.link(&jit);
-        arithProfile->emitUnconditionalSet(jit, BinaryArithProfile::NonNegZeroDouble);
+        arithProfile->emitUnconditionalSet(jit, ObservedResults::NonNegZeroDouble);
 
         jit.move(m_result.tagGPR(), m_scratchGPR);
         jit.urshiftPtr(CCallHelpers::Imm32(52 - 32), m_scratchGPR);
         jit.and32(CCallHelpers::Imm32(0x7ff), m_scratchGPR);
         CCallHelpers::Jump noInt52Overflow = jit.branch32(CCallHelpers::LessThanOrEqual, m_scratchGPR, CCallHelpers::TrustedImm32(0x431));
 
-        arithProfile->emitUnconditionalSet(jit, BinaryArithProfile::Int52Overflow);
+        arithProfile->emitUnconditionalSet(jit, ObservedResults::Int52Overflow);
 
         endJumpList.append(noInt52Overflow);
         if (m_scratchGPR == m_result.tagGPR() || m_scratchGPR == m_result.payloadGPR())
index 8ebe49f..7cfa095 100644 (file)
@@ -661,6 +661,26 @@ void JIT::emit_op_to_number(const Instruction* currentInstruction)
         emitPutVirtualRegister(dstVReg);
 }
 
+void JIT::emit_op_to_numeric(const Instruction* currentInstruction)
+{
+    auto bytecode = currentInstruction->as<OpToNumeric>();
+    int dstVReg = bytecode.m_dst.offset();
+    int srcVReg = bytecode.m_operand.offset();
+    emitGetVirtualRegister(srcVReg, regT0);
+
+    Jump isNotCell = branchIfNotCell(regT0);
+    addSlowCase(branchIfNotBigInt(regT0));
+    Jump isBigInt = jump();
+
+    isNotCell.link(this);
+    addSlowCase(branchIfNotNumber(regT0));
+    isBigInt.link(this);
+
+    emitValueProfilingSite(bytecode.metadata(m_codeBlock));
+    if (srcVReg != dstVReg)
+        emitPutVirtualRegister(dstVReg);
+}
+
 void JIT::emit_op_to_string(const Instruction* currentInstruction)
 {
     auto bytecode = currentInstruction->as<OpToString>();
index 4e94245..e25fe96 100644 (file)
@@ -839,6 +839,27 @@ void JIT::emit_op_to_number(const Instruction* currentInstruction)
         emitStore(dst, regT1, regT0);
 }
 
+void JIT::emit_op_to_numeric(const Instruction* currentInstruction)
+{
+    auto bytecode = currentInstruction->as<OpToNumeric>();
+    int dst = bytecode.m_dst.offset();
+    int src = bytecode.m_operand.offset();
+
+    emitLoad(src, regT1, regT0);
+
+    Jump isNotCell = branchIfNotCell(regT1);
+    addSlowCase(branchIfNotBigInt(regT0));
+    Jump isBigInt = jump();
+
+    isNotCell.link(this);
+    addSlowCase(branchIfNotNumber(regT1));
+    isBigInt.link(this);
+
+    emitValueProfilingSite(bytecode.metadata(m_codeBlock));
+    if (src != dst)
+        emitStore(dst, regT1, regT0);
+}
+
 void JIT::emit_op_to_string(const Instruction* currentInstruction)
 {
     auto bytecode = currentInstruction->as<OpToString>();
index 560387a..b2ab791 100644 (file)
@@ -1773,7 +1773,6 @@ compareUnsignedJumpOp(
 preOp(inc, OpInc,
     macro (value, slow) baddio 1, value, slow end)
 
-
 preOp(dec, OpDec,
     macro (value, slow) bsubio 1, value, slow end)
 
index c880cf9..514b3eb 100644 (file)
@@ -941,13 +941,20 @@ strictEqualityJumpOp(jnstricteq, OpJnstricteq,
     macro (left, right, target) bineq left, right, target end)
 
 
-macro preOp(opcodeName, opcodeStruct, operation)
-    llintOp(op_%opcodeName%, opcodeStruct, macro (size, get, dispatch)
+macro preOp(opcodeName, opcodeStruct, integerOperation)
+    llintOpWithMetadata(op_%opcodeName%, opcodeStruct, macro (size, get, dispatch, metadata, return)
+        macro updateArithProfile(type)
+            orh type, %opcodeStruct%::Metadata::m_arithProfile + UnaryArithProfile::m_bits[t1]
+        end
+
+        metadata(t1, t2)
         get(m_srcDst, t0)
         bineq TagOffset[cfr, t0, 8], Int32Tag, .slow
-        loadi PayloadOffset[cfr, t0, 8], t1
-        operation(t1, .slow)
-        storei t1, PayloadOffset[cfr, t0, 8]
+        loadi PayloadOffset[cfr, t0, 8], t2
+        # Metadata in t1, srcDst in t2
+        integerOperation(t2, .slow)
+        storei t2, PayloadOffset[cfr, t0, 8]
+        updateArithProfile(ArithProfileInt)
         dispatch()
 
     .slow:
@@ -970,6 +977,19 @@ llintOpWithProfile(op_to_number, OpToNumber, macro (size, get, dispatch, return)
     dispatch()
 end)
 
+llintOpWithProfile(op_to_numeric, OpToNumeric, macro (size, get, dispatch, return)
+    get(m_operand, t0)
+    loadConstantOrVariable(size, t0, t2, t3)
+    bieq t2, Int32Tag, .opToNumericIsInt
+    biaeq t2, LowestTag, .opToNumericSlow
+.opToNumericIsInt:
+    return(t2, t3)
+
+.opToNumericSlow:
+    callSlowPath(_slow_path_to_numeric)
+    dispatch()
+end)
+
 
 llintOpWithReturn(op_to_string, OpToString, macro (size, get, dispatch, return)
     get(m_operand, t0)
index d949101..713039a 100644 (file)
@@ -923,16 +923,23 @@ strictEqualityJumpOp(jstricteq, OpJstricteq,
 strictEqualityJumpOp(jnstricteq, OpJnstricteq,
     macro (left, right, target) bqneq left, right, target end)
 
+macro preOp(opcodeName, opcodeStruct, integerOperation)
+    llintOpWithMetadata(op_%opcodeName%, opcodeStruct, macro (size, get, dispatch, metadata, return)
+        macro updateArithProfile(type)
+            orh type, %opcodeStruct%::Metadata::m_arithProfile + UnaryArithProfile::m_bits[t1]
+        end
 
-macro preOp(opcodeName, opcodeStruct, arithmeticOperation)
-    llintOp(op_%opcodeName%, opcodeStruct, macro (size, get, dispatch)
         get(m_srcDst, t0)
-        loadq [cfr, t0, 8], t1
-        bqb t1, numberTag, .slow
-        arithmeticOperation(t1, .slow)
-        orq numberTag, t1
-        storeq t1, [cfr, t0, 8]
+        loadq [cfr, t0, 8], t3
+        metadata(t1, t2)
+        # Metadata in t1, srcDst in t3
+        bqb t3, numberTag, .slow
+        integerOperation(t3, .slow)
+        orq numberTag, t3
+        storeq t3, [cfr, t0, 8]
+        updateArithProfile(ArithProfileInt)
         dispatch()
+
     .slow:
         callSlowPath(_slow_path_%opcodeName%)
         dispatch()
@@ -952,6 +959,19 @@ llintOpWithProfile(op_to_number, OpToNumber, macro (size, get, dispatch, return)
     dispatch()
 end)
 
+llintOpWithProfile(op_to_numeric, OpToNumeric, macro (size, get, dispatch, return)
+    get(m_operand, t0)
+    loadConstantOrVariable(size, t0, t2)
+    bqaeq t2, numberTag, .opToNumericIsImmediate
+    btqz t2, numberTag, .opToNumericSlow
+.opToNumericIsImmediate:
+    return(t2)
+
+.opToNumericSlow:
+    callSlowPath(_slow_path_to_numeric)
+    dispatch()
+end)
+
 
 llintOpWithReturn(op_to_string, OpToString, macro (size, get, dispatch, return)
     get(m_operand, t1)
@@ -1012,7 +1032,7 @@ macro binaryOpCustomStore(opcodeName, opcodeStruct, integerOperationAndStore, do
         metadata(t5, t0)
 
         macro profile(type)
-            orh type, %opcodeStruct%::Metadata::m_arithProfile + UnaryArithProfile::m_bits[t5]
+            orh type, %opcodeStruct%::Metadata::m_arithProfile + BinaryArithProfile::m_bits[t5]
         end
 
         get(m_rhs, t0)
index da9fbca..ab1889d 100644 (file)
@@ -457,14 +457,30 @@ SLOW_PATH_DECL(slow_path_inc)
 {
     BEGIN();
     auto bytecode = pc->as<OpInc>();
-    RETURN_WITH_PROFILING_CUSTOM(bytecode.m_srcDst, jsNumber(GET(bytecode.m_srcDst).jsValue().toNumber(globalObject) + 1), { });
+    JSValue argument = GET_C(bytecode.m_srcDst).jsValue();
+    Variant<JSBigInt*, double> resultVariant = argument.toNumeric(globalObject);
+    CHECK_EXCEPTION();
+    JSValue result;
+    if (WTF::holds_alternative<JSBigInt*>(resultVariant))
+        result = JSBigInt::inc(globalObject, WTF::get<JSBigInt*>(resultVariant));
+    else
+        result = jsNumber(WTF::get<double>(resultVariant) + 1);
+    RETURN_WITH_PROFILING_CUSTOM(bytecode.m_srcDst, result, { });
 }
 
 SLOW_PATH_DECL(slow_path_dec)
 {
     BEGIN();
     auto bytecode = pc->as<OpDec>();
-    RETURN_WITH_PROFILING_CUSTOM(bytecode.m_srcDst, jsNumber(GET(bytecode.m_srcDst).jsValue().toNumber(globalObject) - 1), { });
+    JSValue argument = GET_C(bytecode.m_srcDst).jsValue();
+    Variant<JSBigInt*, double> resultVariant = argument.toNumeric(globalObject);
+    CHECK_EXCEPTION();
+    JSValue result;
+    if (WTF::holds_alternative<JSBigInt*>(resultVariant))
+        result = JSBigInt::dec(globalObject, WTF::get<JSBigInt*>(resultVariant));
+    else
+        result = jsNumber(WTF::get<double>(resultVariant) - 1);
+    RETURN_WITH_PROFILING_CUSTOM(bytecode.m_srcDst, result, { });
 }
 
 SLOW_PATH_DECL(slow_path_to_string)
@@ -575,6 +591,21 @@ SLOW_PATH_DECL(slow_path_to_number)
     RETURN_PROFILED(result);
 }
 
+SLOW_PATH_DECL(slow_path_to_numeric)
+{
+    BEGIN();
+    auto bytecode = pc->as<OpToNumeric>();
+    JSValue argument = GET_C(bytecode.m_operand).jsValue();
+    Variant<JSBigInt*, double> resultVariant = argument.toNumeric(globalObject);
+    CHECK_EXCEPTION();
+    JSValue result;
+    if (WTF::holds_alternative<JSBigInt*>(resultVariant))
+        result = WTF::get<JSBigInt*>(resultVariant);
+    else
+        result = jsNumber(WTF::get<double>(resultVariant));
+    RETURN_PROFILED(result);
+}
+
 SLOW_PATH_DECL(slow_path_to_object)
 {
     BEGIN();
index 5938594..46bfe15 100644 (file)
@@ -339,6 +339,7 @@ SLOW_PATH_HIDDEN_DECL(slow_path_greatereq);
 SLOW_PATH_HIDDEN_DECL(slow_path_inc);
 SLOW_PATH_HIDDEN_DECL(slow_path_dec);
 SLOW_PATH_HIDDEN_DECL(slow_path_to_number);
+SLOW_PATH_HIDDEN_DECL(slow_path_to_numeric);
 SLOW_PATH_HIDDEN_DECL(slow_path_to_string);
 SLOW_PATH_HIDDEN_DECL(slow_path_to_object);
 SLOW_PATH_HIDDEN_DECL(slow_path_negate);
index 6940e37..0af9342 100644 (file)
@@ -452,6 +452,20 @@ JSBigInt* JSBigInt::remainder(JSGlobalObject* globalObject, JSBigInt* x, JSBigIn
     return remainder->rightTrim(vm);
 }
 
+JSBigInt* JSBigInt::inc(JSGlobalObject* globalObject, JSBigInt* x)
+{
+    // FIXME: we can probably do something a fair bit more efficient here
+    VM& vm = globalObject->vm();
+    return add(globalObject, x, vm.bigIntConstantOne.get());
+}
+
+JSBigInt* JSBigInt::dec(JSGlobalObject* globalObject, JSBigInt* x)
+{
+    // FIXME: we can probably do something a fair bit more efficient here
+    VM& vm = globalObject->vm();
+    return sub(globalObject, x, vm.bigIntConstantOne.get());
+}
+
 JSBigInt* JSBigInt::add(JSGlobalObject* globalObject, JSBigInt* x, JSBigInt* y)
 {
     VM& vm = globalObject->vm();
index d0ef4ab..8a4e609 100644 (file)
@@ -127,6 +127,8 @@ public:
     
     ComparisonResult static compareToDouble(JSBigInt* x, double y);
 
+    static JSBigInt* inc(JSGlobalObject*, JSBigInt* x);
+    static JSBigInt* dec(JSGlobalObject*, JSBigInt* x);
     static JSBigInt* add(JSGlobalObject*, JSBigInt* x, JSBigInt* y);
     static JSBigInt* sub(JSGlobalObject*, JSBigInt* x, JSBigInt* y);
     static JSBigInt* divide(JSGlobalObject*, JSBigInt* x, JSBigInt* y);
index f5be239..0714c3e 100644 (file)
@@ -400,6 +400,7 @@ VM::VM(VMType vmType, HeapType heapType)
         sentinelMapBucket();
         sentinelSetBucket();
     }
+    bigIntConstantOne.set(*this, JSBigInt::createFrom(*this, 1));
 
     Thread::current().setCurrentAtomStringTable(existingEntryAtomStringTable);
     
index 1e8b72b..8ad7e9c 100644 (file)
@@ -586,6 +586,8 @@ public:
     AtomStringTable* atomStringTable() const { return m_atomStringTable; }
     WTF::SymbolRegistry& symbolRegistry() { return m_symbolRegistry; }
 
+    Strong<JSBigInt> bigIntConstantOne;
+
     Structure* setIteratorStructure()
     {
         if (LIKELY(m_setIteratorStructure))