[BigInt] Add ValueMod into DFG
authorticaiolima@gmail.com <ticaiolima@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 8 May 2019 19:38:17 +0000 (19:38 +0000)
committerticaiolima@gmail.com <ticaiolima@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 8 May 2019 19:38:17 +0000 (19:38 +0000)
https://bugs.webkit.org/show_bug.cgi?id=186174

Reviewed by Saam Barati.

JSTests:

* microbenchmarks/mod-untyped.js: Added.
* stress/big-int-mod-osr.js: Added.
* stress/value-div-ai-rule.js: Added.
* stress/value-mod-ai-rule.js: Added.

PerformanceTests:

* BigIntBench/big-int-simple-mod.js: Added.

Source/JavaScriptCore:

This patch is introducing a new DFG node called ValueMod, that is
responsible to handle BigInt and Untyped specialization of op_mod.
With the introduction of BigInt, we think that cases with
ValueMod(Untyped, Untyped) can be more common and we introduced
support for such kind of node.

* dfg/DFGAbstractInterpreter.h:
* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::handleConstantDivOp):

We are abstracting the constant rules of division operations. It
includes ArithDiv, ValueDiv, ArithMod and ValueMod, since they perform
the same analysis.

(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
* dfg/DFGBackwardsPropagationPhase.cpp:
(JSC::DFG::BackwardsPropagationPhase::propagate):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::makeSafe):
(JSC::DFG::ByteCodeParser::parseBlock):

Here we check if lhs and rhs have number result to emit ArithMod.
Otherwise, we need to fallback to ValueMod and let fixup replace this
operation when possible.

* dfg/DFGClobberize.h:
(JSC::DFG::clobberize):

ValueMod(BigIntUse) doesn't clobberize world because it only calls
`operationModBigInt`.

* dfg/DFGDoesGC.cpp:
(JSC::DFG::doesGC):

ValueMod(BigIntUse) can trigger GC since it allocates intermediate
JSBigInt to perform calculation. ValueMod(UntypedUse) can trigger GC
because it can execute arbritary code from user.

* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupArithDivInt32):

Function created to simplify readability of ArithDiv/AirthMod fixup
operation.

(JSC::DFG::FixupPhase::fixupArithDiv):
(JSC::DFG::FixupPhase::fixupNode):

Following the same fixup rules of ArithDiv.

* dfg/DFGNodeType.h:
* dfg/DFGOperations.cpp:
(JSC::DFG::binaryOp):
* dfg/DFGOperations.h:
* dfg/DFGPredictionPropagationPhase.cpp:

ValueMod follows the same prediction propagation rules of ArithMod and
the same rules for `doDoubleVoting`.

* dfg/DFGSafeToExecute.h:
(JSC::DFG::safeToExecute):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compileValueMod):
* dfg/DFGSpeculativeJIT.h:
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGValidate.cpp:
* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::compileNode):
(JSC::FTL::DFG::LowerDFGToB3::compileValueMod):

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

27 files changed:
JSTests/ChangeLog
JSTests/microbenchmarks/mod-untyped.js [new file with mode: 0644]
JSTests/stress/big-int-mod-osr.js [new file with mode: 0644]
JSTests/stress/value-div-ai-rule.js [new file with mode: 0644]
JSTests/stress/value-mod-ai-rule.js [new file with mode: 0644]
PerformanceTests/BigIntBench/big-int-simple-mod.js [new file with mode: 0644]
PerformanceTests/ChangeLog
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/dfg/DFGAbstractInterpreter.h
Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
Source/JavaScriptCore/dfg/DFGBackwardsPropagationPhase.cpp
Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
Source/JavaScriptCore/dfg/DFGClobberize.h
Source/JavaScriptCore/dfg/DFGDoesGC.cpp
Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
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/dfg/DFGValidate.cpp
Source/JavaScriptCore/ftl/FTLCapabilities.cpp
Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp

index cde66f5..8648ac9 100644 (file)
@@ -1,3 +1,15 @@
+2019-05-08  Caio Lima  <ticaiolima@gmail.com>
+
+        [BigInt] Add ValueMod into DFG
+        https://bugs.webkit.org/show_bug.cgi?id=186174
+
+        Reviewed by Saam Barati.
+
+        * microbenchmarks/mod-untyped.js: Added.
+        * stress/big-int-mod-osr.js: Added.
+        * stress/value-div-ai-rule.js: Added.
+        * stress/value-mod-ai-rule.js: Added.
+
 2019-05-07  Yusuke Suzuki  <ysuzuki@apple.com>
 
         [JSC] DFG_ASSERT failed in lowInt52
diff --git a/JSTests/microbenchmarks/mod-untyped.js b/JSTests/microbenchmarks/mod-untyped.js
new file mode 100644 (file)
index 0000000..800864d
--- /dev/null
@@ -0,0 +1,39 @@
+let assert = {
+    sameValue: function(i, e, m) {
+        if (i !== e)
+            throw new Error(m);
+    }
+}
+
+function untypedMod(x, y) {
+    return x % y;
+}
+noInline(untypedMod);
+
+let o =  {valueOf: () => 10};
+
+for (let i = 0; i < 100000; i++) {
+    let r = untypedMod(30, o);
+    assert.sameValue(r, 0, 30 + " % {valueOf: () => 10} = " + r);
+}
+
+o2 =  {valueOf: () => 10000};
+
+for (let i = 0; i < 100000; i++) {
+    let r = untypedMod(o2, o);
+    assert.sameValue(r, 0, "{valueOf: () => 10000} % {valueOf: () => 10}  = " + r);
+}
+
+o = Object(10);
+let r = untypedMod(30, o);
+assert.sameValue(r, 0, 30 + " % Object(10) = " + r);
+
+o2 = Object(3240);
+r = untypedMod(o2, o);
+assert.sameValue(r, 0, "Object(3240) % Object(10) = " + r);
+
+for (let i = 0; i < 100000; i++) {
+    let r = untypedMod("9", "8");
+    assert.sameValue(r, 1, "9 % 8 = " + r);
+}
+
diff --git a/JSTests/stress/big-int-mod-osr.js b/JSTests/stress/big-int-mod-osr.js
new file mode 100644 (file)
index 0000000..8c10905
--- /dev/null
@@ -0,0 +1,30 @@
+//@ runBigIntEnabled
+
+let assert = {
+    sameValue: function(i, e, m) {
+        if (i !== e)
+            throw new Error(m);
+    }
+}
+
+function bigIntMod(x, y) {
+    return x % y;
+}
+noInline(bigIntMod);
+
+for (let i = 0; i < 10000; i++) {
+    let r = bigIntMod(3n, 10n);
+    assert.sameValue(r, 3n, 3n + " % " + 10n + " = " + r);
+}
+
+let r = bigIntMod(3, 10);
+assert.sameValue(r, 3, 3 + " % " + 10 + " = " + r);
+
+for (let i = 0; i < 10000; i++) {
+    let r = bigIntMod(3n, 10n);
+    assert.sameValue(r, 3n, 3n + " % " + 10n + " = " + r);
+}
+
+r = bigIntMod("3", "10");
+assert.sameValue(r, 3, 3 + " % " + 10 + " = " + r);
+
diff --git a/JSTests/stress/value-div-ai-rule.js b/JSTests/stress/value-div-ai-rule.js
new file mode 100644 (file)
index 0000000..816b71b
--- /dev/null
@@ -0,0 +1,38 @@
+function assert(a, e) {
+    if (a !== e)
+        throw new Error("Expected: " + e + " bug got: " + a);
+}
+
+(() => {
+    let predicate = true;
+    function foo(a) {
+        let v = a;
+        if (predicate)
+            v = 10;
+
+        let c = v / 2;
+        return c;
+    }
+    noInline(foo);
+
+    for (let i = 0; i < 10000; i++) {
+        assert(foo("10"), 5);
+    }
+})();
+
+(() => {
+    let predicate = true;
+    function foo(a) {
+        let v = a;
+        if (predicate)
+            v = 10.5;
+
+        let c = v / 2;
+        return c;
+    }
+    noInline(foo);
+
+    for (let i = 0; i < 10000; i++) {
+        assert(foo("10"), 5.25);
+    }
+})();
diff --git a/JSTests/stress/value-mod-ai-rule.js b/JSTests/stress/value-mod-ai-rule.js
new file mode 100644 (file)
index 0000000..6a44dab
--- /dev/null
@@ -0,0 +1,39 @@
+function assert(a, e) {
+    if (a !== e)
+        throw new Error("Expected: " + e + " bug got: " + a);
+}
+
+(() => {
+    let predicate = true;
+    function foo(a) {
+        let v = a;
+        if (predicate)
+            v = 10;
+
+        let c = v % 2;
+        return c;
+    }
+    noInline(foo);
+
+    for (let i = 0; i < 10000; i++) {
+        assert(foo("10"), 0);
+    }
+})();
+
+(() => {
+    let predicate = true;
+    function foo(a) {
+        let v = a;
+        if (predicate)
+            v = 10.5;
+
+        let c = v % 2;
+        return c;
+    }
+    noInline(foo);
+
+    for (let i = 0; i < 10000; i++) {
+        assert(foo("10"), 0.5);
+    }
+})();
+
diff --git a/PerformanceTests/BigIntBench/big-int-simple-mod.js b/PerformanceTests/BigIntBench/big-int-simple-mod.js
new file mode 100644 (file)
index 0000000..a50dc6f
--- /dev/null
@@ -0,0 +1,15 @@
+function bigInt(a, b) {
+    let c = a % b;
+    return a + c % b;
+}
+noInline(bigInt);
+
+for (let i = 0; i < 100000; i++) {
+    bigInt(0b1111n, 0b1010n);
+}
+
+let out;
+for (let i = 0; i < 100000; i++) {
+    out = bigInt(0xffffffffffffffffffn, 0xaaffffffffffffffffffn);
+}
+
index 4ee19b6..e35516f 100644 (file)
@@ -1,3 +1,12 @@
+2019-05-08  Caio Lima  <ticaiolima@gmail.com>
+
+        [BigInt] Add ValueMod into DFG
+        https://bugs.webkit.org/show_bug.cgi?id=186174
+
+        Reviewed by Saam Barati.
+
+        * BigIntBench/big-int-simple-mod.js: Added.
+
 2019-05-06  Saam Barati  <sbarati@apple.com>
 
         JS2 should print scores for different categories
index b876d10..d62b6f8 100644 (file)
@@ -1,3 +1,84 @@
+2019-05-08  Caio Lima  <ticaiolima@gmail.com>
+
+        [BigInt] Add ValueMod into DFG
+        https://bugs.webkit.org/show_bug.cgi?id=186174
+
+        Reviewed by Saam Barati.
+
+        This patch is introducing a new DFG node called ValueMod, that is
+        responsible to handle BigInt and Untyped specialization of op_mod.
+        With the introduction of BigInt, we think that cases with
+        ValueMod(Untyped, Untyped) can be more common and we introduced
+        support for such kind of node.
+
+        * dfg/DFGAbstractInterpreter.h:
+        * dfg/DFGAbstractInterpreterInlines.h:
+        (JSC::DFG::AbstractInterpreter<AbstractStateType>::handleConstantDivOp):
+
+        We are abstracting the constant rules of division operations. It
+        includes ArithDiv, ValueDiv, ArithMod and ValueMod, since they perform
+        the same analysis.
+
+        (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+        * dfg/DFGBackwardsPropagationPhase.cpp:
+        (JSC::DFG::BackwardsPropagationPhase::propagate):
+        * dfg/DFGByteCodeParser.cpp:
+        (JSC::DFG::ByteCodeParser::makeSafe):
+        (JSC::DFG::ByteCodeParser::parseBlock):
+
+        Here we check if lhs and rhs have number result to emit ArithMod.
+        Otherwise, we need to fallback to ValueMod and let fixup replace this
+        operation when possible.
+
+        * dfg/DFGClobberize.h:
+        (JSC::DFG::clobberize):
+
+        ValueMod(BigIntUse) doesn't clobberize world because it only calls
+        `operationModBigInt`.
+
+        * dfg/DFGDoesGC.cpp:
+        (JSC::DFG::doesGC):
+
+        ValueMod(BigIntUse) can trigger GC since it allocates intermediate
+        JSBigInt to perform calculation. ValueMod(UntypedUse) can trigger GC
+        because it can execute arbritary code from user.
+
+        * dfg/DFGFixupPhase.cpp:
+        (JSC::DFG::FixupPhase::fixupArithDivInt32):
+
+        Function created to simplify readability of ArithDiv/AirthMod fixup
+        operation.
+
+        (JSC::DFG::FixupPhase::fixupArithDiv):
+        (JSC::DFG::FixupPhase::fixupNode):
+
+        Following the same fixup rules of ArithDiv.
+
+        * dfg/DFGNodeType.h:
+        * dfg/DFGOperations.cpp:
+        (JSC::DFG::binaryOp):
+        * dfg/DFGOperations.h:
+        * dfg/DFGPredictionPropagationPhase.cpp:
+
+        ValueMod follows the same prediction propagation rules of ArithMod and
+        the same rules for `doDoubleVoting`.
+
+        * dfg/DFGSafeToExecute.h:
+        (JSC::DFG::safeToExecute):
+        * dfg/DFGSpeculativeJIT.cpp:
+        (JSC::DFG::SpeculativeJIT::compileValueMod):
+        * dfg/DFGSpeculativeJIT.h:
+        * dfg/DFGSpeculativeJIT32_64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGSpeculativeJIT64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGValidate.cpp:
+        * ftl/FTLCapabilities.cpp:
+        (JSC::FTL::canCompile):
+        * ftl/FTLLowerDFGToB3.cpp:
+        (JSC::FTL::DFG::LowerDFGToB3::compileNode):
+        (JSC::FTL::DFG::LowerDFGToB3::compileValueMod):
+
 2019-05-07  Yusuke Suzuki  <ysuzuki@apple.com>
 
         [JSC] DFG_ASSERT failed in lowInt52
index fba1299..5d5988c 100644 (file)
@@ -253,6 +253,8 @@ private:
     void verifyEdges(Node*);
     void executeDoubleUnaryOpEffects(Node*, double(*equivalentFunction)(double));
     
+    bool handleConstantDivOp(Node*);
+
     CodeBlock* m_codeBlock;
     Graph& m_graph;
     VM& m_vm;
index 74d82ea..ac28dcd 100644 (file)
@@ -234,6 +234,57 @@ inline ToThisResult isToThisAnIdentity(VM& vm, bool isStrictMode, AbstractValue&
 }
 
 template<typename AbstractStateType>
+bool AbstractInterpreter<AbstractStateType>::handleConstantDivOp(Node* node)
+{
+    JSValue left = forNode(node->child1()).value();
+    JSValue right = forNode(node->child2()).value();
+
+    if (left && right) {
+        NodeType op = node->op();
+        bool isDivOperation = op == ValueDiv || op == ArithDiv;
+
+        // Only possible case of ValueOp below is UntypedUse,
+        // so we need to reflect clobberize rules.
+        bool isClobbering = op == ValueDiv || op == ValueMod;
+
+        if (left.isInt32() && right.isInt32()) {
+            double doubleResult;
+            if (isDivOperation)
+                doubleResult = left.asNumber() / right.asNumber();
+            else
+                doubleResult = fmod(left.asNumber(), right.asNumber());
+
+            if (node->hasArithMode()) {
+                if (!shouldCheckOverflow(node->arithMode()))
+                    doubleResult = toInt32(doubleResult);
+                else if (!shouldCheckNegativeZero(node->arithMode()))
+                    doubleResult += 0; // Sanitizes zero.
+            }
+
+            JSValue valueResult = jsNumber(doubleResult);
+            if (valueResult.isInt32()) {
+                if (isClobbering)
+                    didFoldClobberWorld();
+                setConstant(node, valueResult);
+                return true;
+            }
+        } else if (left.isNumber() && right.isNumber()) {
+            if (isClobbering)
+                didFoldClobberWorld();
+
+            if (isDivOperation)
+                setConstant(node, jsDoubleNumber(left.asNumber() / right.asNumber()));
+            else
+                setConstant(node, jsDoubleNumber(fmod(left.asNumber(), right.asNumber())));
+
+            return true;
+        }
+    }
+
+    return false;
+}
+
+template<typename AbstractStateType>
 bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimit, Node* node)
 {
     verifyEdges(node);
@@ -911,7 +962,11 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
         break;
     }
         
+    case ValueMod:
     case ValueDiv: {
+        if (handleConstantDivOp(node))
+            break;
+
         if (node->binaryUseKind() == BigIntUse)
             setTypeForNode(node, SpecBigInt);
         else {
@@ -921,68 +976,26 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
         break;
     }
 
+    case ArithMod:
     case ArithDiv: {
-        JSValue left = forNode(node->child1()).value();
-        JSValue right = forNode(node->child2()).value();
-        switch (node->binaryUseKind()) {
-        case Int32Use:
-            if (left && right && left.isInt32() && right.isInt32()) {
-                double doubleResult = left.asNumber() / right.asNumber();
-                if (!shouldCheckOverflow(node->arithMode()))
-                    doubleResult = toInt32(doubleResult);
-                else if (!shouldCheckNegativeZero(node->arithMode()))
-                    doubleResult += 0; // Sanitizes zero.
-                JSValue valueResult = jsNumber(doubleResult);
-                if (valueResult.isInt32()) {
-                    setConstant(node, valueResult);
-                    break;
-                }
-            }
-            setNonCellTypeForNode(node, SpecInt32Only);
-            break;
-        case DoubleRepUse:
-            if (left && right && left.isNumber() && right.isNumber()) {
-                setConstant(node, jsDoubleNumber(left.asNumber() / right.asNumber()));
-                break;
-            }
-            setNonCellTypeForNode(node, 
-                typeOfDoubleQuotient(
-                    forNode(node->child1()).m_type, forNode(node->child2()).m_type));
-            break;
-        default:
-            RELEASE_ASSERT_NOT_REACHED();
+        if (handleConstantDivOp(node))
             break;
-        }
-        break;
-    }
 
-    case ArithMod: {
-        JSValue left = forNode(node->child1()).value();
-        JSValue right = forNode(node->child2()).value();
         switch (node->binaryUseKind()) {
         case Int32Use:
-            if (left && right && left.isInt32() && right.isInt32()) {
-                double doubleResult = fmod(left.asNumber(), right.asNumber());
-                if (!shouldCheckOverflow(node->arithMode()))
-                    doubleResult = toInt32(doubleResult);
-                else if (!shouldCheckNegativeZero(node->arithMode()))
-                    doubleResult += 0; // Sanitizes zero.
-                JSValue valueResult = jsNumber(doubleResult);
-                if (valueResult.isInt32()) {
-                    setConstant(node, valueResult);
-                    break;
-                }
-            }
             setNonCellTypeForNode(node, SpecInt32Only);
             break;
         case DoubleRepUse:
-            if (left && right && left.isNumber() && right.isNumber()) {
-                setConstant(node, jsDoubleNumber(fmod(left.asNumber(), right.asNumber())));
-                break;
+            if (node->op() == ArithDiv) {
+                setNonCellTypeForNode(node, 
+                    typeOfDoubleQuotient(
+                        forNode(node->child1()).m_type, forNode(node->child2()).m_type));
+            } else {
+                setNonCellTypeForNode(node, 
+                    typeOfDoubleBinaryOp(
+                        forNode(node->child1()).m_type, forNode(node->child2()).m_type));
             }
-            setNonCellTypeForNode(node, 
-                typeOfDoubleBinaryOp(
-                    forNode(node->child1()).m_type, forNode(node->child2()).m_type));
+            
             break;
         default:
             RELEASE_ASSERT_NOT_REACHED();
index 6ecc199..90d575b 100644 (file)
@@ -363,6 +363,7 @@ private:
             break;
         }
             
+        case ValueMod:
         case ArithMod: {
             flags |= NodeBytecodeUsesAsNumber;
             flags &= ~NodeBytecodeUsesAsOther;
index 9f04275..841da18 100644 (file)
@@ -935,7 +935,7 @@ private:
         if (m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, NegativeZero))
             node->mergeFlags(NodeMayNegZeroInDFG);
         
-        if (!isX86() && node->op() == ArithMod)
+        if (!isX86() && (node->op() == ArithMod || node->op() == ValueMod))
             return node;
 
         {
@@ -996,6 +996,7 @@ private:
             case ArithAdd:
             case ArithSub:
             case ValueAdd:
+            case ValueMod:
             case ArithMod: // for ArithMod "MayOverflow" means we tried to divide by zero, or we saw double.
                 node->mergeFlags(NodeMayOverflowInt32InBaseline);
                 break;
@@ -5106,7 +5107,10 @@ void ByteCodeParser::parseBlock(unsigned limit)
             auto bytecode = currentInstruction->as<OpMod>();
             Node* op1 = get(bytecode.m_lhs);
             Node* op2 = get(bytecode.m_rhs);
-            set(bytecode.m_dst, makeSafe(addToGraph(ArithMod, op1, op2)));
+            if (op1->hasNumberResult() && op2->hasNumberResult())
+                set(bytecode.m_dst, makeSafe(addToGraph(ArithMod, op1, op2)));
+            else
+                set(bytecode.m_dst, makeSafe(addToGraph(ValueMod, op1, op2)));
             NEXT_OPCODE(op_mod);
         }
 
index b1598d4..72dbaab 100644 (file)
@@ -680,6 +680,7 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu
     case ValueSub:
     case ValueMul:
     case ValueDiv:
+    case ValueMod:
         if (node->isBinaryUseKind(BigIntUse)) {
             def(PureValue(node));
             return;
index 10991a5..5560e8e 100644 (file)
@@ -379,6 +379,7 @@ bool doesGC(Graph& graph, Node* node)
     case ValueSub:
     case ValueMul:
     case ValueDiv:
+    case ValueMod:
     case ValueBitNot:
     case ValueNegate:
 #else
index a0cd01a..f929fe3 100644 (file)
@@ -75,38 +75,45 @@ public:
     }
 
 private:
-    void fixupArithDiv(Node* node, Edge& leftChild, Edge& rightChild)
+
+    void fixupArithDivInt32(Node* node, Edge& leftChild, Edge& rightChild)
     {
-        if (m_graph.binaryArithShouldSpeculateInt32(node, FixupPass)) {
-            if (optimizeForX86() || optimizeForARM64() || optimizeForARMv7IDIVSupported()) {
-                fixIntOrBooleanEdge(leftChild);
-                fixIntOrBooleanEdge(rightChild);
-                if (bytecodeCanTruncateInteger(node->arithNodeFlags()))
-                    node->setArithMode(Arith::Unchecked);
-                else if (bytecodeCanIgnoreNegativeZero(node->arithNodeFlags()))
-                    node->setArithMode(Arith::CheckOverflow);
-                else
-                    node->setArithMode(Arith::CheckOverflowAndNegativeZero);
-                return;
-            }
-            
-            // This will cause conversion nodes to be inserted later.
-            fixDoubleOrBooleanEdge(leftChild);
-            fixDoubleOrBooleanEdge(rightChild);
-            
-            // We don't need to do ref'ing on the children because we're stealing them from
-            // the original division.
-            Node* newDivision = m_insertionSet.insertNode(m_indexInBlock, SpecBytecodeDouble, *node);
-            newDivision->setResult(NodeResultDouble);
-            
-            node->setOp(DoubleAsInt32);
-            node->children.initialize(Edge(newDivision, DoubleRepUse), Edge(), Edge());
-            if (bytecodeCanIgnoreNegativeZero(node->arithNodeFlags()))
+        if (optimizeForX86() || optimizeForARM64() || optimizeForARMv7IDIVSupported()) {
+            fixIntOrBooleanEdge(leftChild);
+            fixIntOrBooleanEdge(rightChild);
+            if (bytecodeCanTruncateInteger(node->arithNodeFlags()))
+                node->setArithMode(Arith::Unchecked);
+            else if (bytecodeCanIgnoreNegativeZero(node->arithNodeFlags()))
                 node->setArithMode(Arith::CheckOverflow);
             else
                 node->setArithMode(Arith::CheckOverflowAndNegativeZero);
             return;
         }
+
+        // This will cause conversion nodes to be inserted later.
+        fixDoubleOrBooleanEdge(leftChild);
+        fixDoubleOrBooleanEdge(rightChild);
+
+        // We don't need to do ref'ing on the children because we're stealing them from
+        // the original division.
+        Node* newDivision = m_insertionSet.insertNode(m_indexInBlock, SpecBytecodeDouble, *node);
+        newDivision->setResult(NodeResultDouble);
+
+        node->setOp(DoubleAsInt32);
+        node->children.initialize(Edge(newDivision, DoubleRepUse), Edge(), Edge());
+        if (bytecodeCanIgnoreNegativeZero(node->arithNodeFlags()))
+            node->setArithMode(Arith::CheckOverflow);
+        else
+            node->setArithMode(Arith::CheckOverflowAndNegativeZero);
+
+    }
+
+    void fixupArithDiv(Node* node, Edge& leftChild, Edge& rightChild)
+    {
+        if (m_graph.binaryArithShouldSpeculateInt32(node, FixupPass)) {
+            fixupArithDivInt32(node, leftChild, rightChild);
+            return;
+        }
         
         fixDoubleOrBooleanEdge(leftChild);
         fixDoubleOrBooleanEdge(rightChild);
@@ -474,6 +481,7 @@ private:
             if (Node::shouldSpeculateBigInt(leftChild.node(), rightChild.node())) {
                 fixEdge<BigIntUse>(node->child1());
                 fixEdge<BigIntUse>(node->child2());
+                node->clearFlags(NodeMustGenerate);
                 break;
             }
 
@@ -508,6 +516,7 @@ private:
             break;
         }
 
+        case ValueMod: 
         case ValueDiv: {
             Edge& leftChild = node->child1();
             Edge& rightChild = node->child2();
@@ -515,6 +524,7 @@ private:
             if (Node::shouldSpeculateBigInt(leftChild.node(), rightChild.node())) {
                 fixEdge<BigIntUse>(leftChild);
                 fixEdge<BigIntUse>(rightChild);
+                node->clearFlags(NodeMustGenerate);
                 break; 
             }
 
@@ -523,7 +533,12 @@ private:
                 fixEdge<UntypedUse>(rightChild);
                 break;
             }
-            node->setOp(ArithDiv);
+
+            if (op == ValueDiv)
+                node->setOp(ArithDiv);
+            else
+                node->setOp(ArithMod);
+
             node->setResult(NodeResultNumber);
             fixupArithDiv(node, leftChild, rightChild);
             break;
index 7f67872..b2c0b41 100644 (file)
@@ -179,6 +179,7 @@ namespace JSC { namespace DFG {
     macro(ValueSub, NodeResultJS | NodeMustGenerate) \
     macro(ValueMul, NodeResultJS | NodeMustGenerate) \
     macro(ValueDiv, NodeResultJS | NodeMustGenerate) \
+    macro(ValueMod, NodeResultJS | NodeMustGenerate) \
     \
     /* Add of values that always convers its inputs to strings. May have two or three kids. */\
     macro(StrCat, NodeResultJS | NodeMustGenerate) \
index 1fa9b6b..316d846 100644 (file)
@@ -213,6 +213,33 @@ static ALWAYS_INLINE void putWithThis(ExecState* exec, EncodedJSValue encodedBas
     baseValue.putInline(exec, ident, putValue, slot);
 }
 
+template<typename BigIntOperation, typename NumberOperation>
+static ALWAYS_INLINE EncodedJSValue binaryOp(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, BigIntOperation&& bigIntOp, NumberOperation&& numberOp, const char* errorMessage)
+{
+    VM* vm = &exec->vm();
+    NativeCallFrameTracer tracer(vm, exec);
+    auto scope = DECLARE_THROW_SCOPE(*vm);
+
+    JSValue op1 = JSValue::decode(encodedOp1);
+    JSValue op2 = JSValue::decode(encodedOp2);
+
+    auto leftNumeric = op1.toNumeric(exec);
+    RETURN_IF_EXCEPTION(scope, encodedJSValue());
+    auto rightNumeric = op2.toNumeric(exec);
+    RETURN_IF_EXCEPTION(scope, encodedJSValue());
+
+    if (WTF::holds_alternative<JSBigInt*>(leftNumeric) || WTF::holds_alternative<JSBigInt*>(rightNumeric)) {
+        if (WTF::holds_alternative<JSBigInt*>(leftNumeric) && WTF::holds_alternative<JSBigInt*>(rightNumeric))
+            RELEASE_AND_RETURN(scope, JSValue::encode(bigIntOp(exec, WTF::get<JSBigInt*>(leftNumeric), WTF::get<JSBigInt*>(rightNumeric))));
+
+        return throwVMTypeError(exec, scope, errorMessage);
+    }
+
+    scope.release();
+
+    return JSValue::encode(jsNumber(numberOp(WTF::get<double>(leftNumeric), WTF::get<double>(rightNumeric))));
+}
+
 template<typename BigIntOperation, typename Int32Operation>
 static ALWAYS_INLINE EncodedJSValue bitwiseBinaryOp(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, BigIntOperation&& bigIntOp, Int32Operation&& int32Op, const char* errorMessage)
 {
@@ -380,6 +407,19 @@ JSCell* JIT_OPERATION operationToObject(ExecState* exec, JSGlobalObject* globalO
     RELEASE_AND_RETURN(scope, value.toObject(exec, globalObject));
 }
 
+EncodedJSValue JIT_OPERATION operationValueMod(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)
+{
+    auto bigIntOp = [] (ExecState* exec, JSBigInt* left, JSBigInt* right) -> JSBigInt* {
+        return JSBigInt::remainder(exec, left, right);
+    };
+
+    auto numberOp = [] (double left, double right) -> double {
+        return jsMod(left, right);
+    };
+
+    return binaryOp(exec, encodedOp1, encodedOp2, bigIntOp, numberOp, "Invalid mix of BigInt and other type in remainder operation.");
+}
+
 EncodedJSValue JIT_OPERATION operationValueBitNot(ExecState* exec, EncodedJSValue encodedOp1)
 {
     VM* vm = &exec->vm();
@@ -497,33 +537,15 @@ EncodedJSValue JIT_OPERATION operationValueAddNotNumber(ExecState* exec, Encoded
 
 EncodedJSValue JIT_OPERATION operationValueDiv(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)
 {
-    VM* vm = &exec->vm();
-    NativeCallFrameTracer tracer(vm, exec);
-    auto scope = DECLARE_THROW_SCOPE(*vm);
-
-    JSValue op1 = JSValue::decode(encodedOp1);
-    JSValue op2 = JSValue::decode(encodedOp2);
-
-    auto leftNumeric = op1.toNumeric(exec);
-    RETURN_IF_EXCEPTION(scope, encodedJSValue());
-    auto rightNumeric = op2.toNumeric(exec);
-    RETURN_IF_EXCEPTION(scope, encodedJSValue());
-
-    if (WTF::holds_alternative<JSBigInt*>(leftNumeric) || WTF::holds_alternative<JSBigInt*>(rightNumeric)) {
-        if (WTF::holds_alternative<JSBigInt*>(leftNumeric) && WTF::holds_alternative<JSBigInt*>(rightNumeric)) {
-            JSBigInt* result = JSBigInt::divide(exec, WTF::get<JSBigInt*>(leftNumeric), WTF::get<JSBigInt*>(rightNumeric));
-            RETURN_IF_EXCEPTION(scope, encodedJSValue());
-            return JSValue::encode(result);
-        }
-
-        return throwVMTypeError(exec, scope, "Invalid mix of BigInt and other type in division operation.");
-    }
+    auto bigIntOp = [] (ExecState* exec, JSBigInt* left, JSBigInt* right) -> JSBigInt* {
+        return JSBigInt::divide(exec, left, right);
+    };
 
-    scope.release();
+    auto numberOp = [] (double left, double right) -> double {
+        return left / right;
+    };
 
-    double a = WTF::get<double>(leftNumeric);
-    double b = WTF::get<double>(rightNumeric);
-    return JSValue::encode(jsNumber(a / b));
+    return binaryOp(exec, encodedOp1, encodedOp2, bigIntOp, numberOp, "Invalid mix of BigInt and other type in division operation.");
 }
 
 double JIT_OPERATION operationArithAbs(ExecState* exec, EncodedJSValue encodedOp1)
@@ -1352,6 +1374,17 @@ JSCell* JIT_OPERATION operationMulBigInt(ExecState* exec, JSCell* op1, JSCell* o
     return JSBigInt::multiply(exec, leftOperand, rightOperand);
 }
     
+JSCell* JIT_OPERATION operationModBigInt(ExecState* exec, JSCell* op1, JSCell* op2)
+{
+    VM* vm = &exec->vm();
+    NativeCallFrameTracer tracer(vm, exec);
+    
+    JSBigInt* leftOperand = jsCast<JSBigInt*>(op1);
+    JSBigInt* rightOperand = jsCast<JSBigInt*>(op2);
+    
+    return JSBigInt::remainder(exec, leftOperand, rightOperand);
+}
+
 JSCell* JIT_OPERATION operationDivBigInt(ExecState* exec, JSCell* op1, JSCell* op2)
 {
     VM* vm = &exec->vm();
index 949e39a..0ce0079 100644 (file)
@@ -50,6 +50,7 @@ JSCell* JIT_OPERATION operationObjectCreateObject(ExecState*, JSObject*) WTF_INT
 JSCell* JIT_OPERATION operationCreateThis(ExecState*, JSObject* constructor, uint32_t inlineCapacity) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationToThis(ExecState*, EncodedJSValue encodedOp1) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationToThisStrict(ExecState*, EncodedJSValue encodedOp1) WTF_INTERNAL;
+EncodedJSValue JIT_OPERATION operationValueMod(ExecState*, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationValueBitNot(ExecState*, EncodedJSValue encodedOp1) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationValueBitAnd(ExecState*, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationValueBitOr(ExecState*, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2) WTF_INTERNAL;
@@ -168,6 +169,7 @@ size_t JIT_OPERATION operationRegExpTestGeneric(ExecState*, JSGlobalObject*, Enc
 size_t JIT_OPERATION operationCompareStrictEqCell(ExecState*, JSCell* op1, JSCell* op2) WTF_INTERNAL;
 JSCell* JIT_OPERATION operationSubBigInt(ExecState*, JSCell* op1, JSCell* op2) WTF_INTERNAL;
 JSCell* JIT_OPERATION operationMulBigInt(ExecState*, JSCell* op1, JSCell* op2) WTF_INTERNAL;
+JSCell* JIT_OPERATION operationModBigInt(ExecState*, JSCell* op1, JSCell* op2) WTF_INTERNAL;
 JSCell* JIT_OPERATION operationDivBigInt(ExecState*, JSCell* op1, JSCell* op2) WTF_INTERNAL;
 JSCell* JIT_OPERATION operationBitAndBigInt(ExecState*, JSCell* op1, JSCell* op2) WTF_INTERNAL;
 JSCell* JIT_OPERATION operationBitNotBigInt(ExecState*, JSCell* op1) WTF_INTERNAL;
index 6e3fb25..098af63 100644 (file)
@@ -348,6 +348,7 @@ private:
         }
 
         case ValueDiv:
+        case ValueMod:
         case ArithDiv:
         case ArithMod: {
             SpeculatedType left = node->child1()->prediction();
@@ -360,11 +361,11 @@ private:
                         changed |= mergePrediction(SpecInt32Only);
                     else
                         changed |= mergePrediction(SpecBytecodeDouble);
-                } else if (op == ValueDiv && isBigIntSpeculation(left) && isBigIntSpeculation(right))
+                } else if ((op == ValueDiv || op == ValueMod) && isBigIntSpeculation(left) && isBigIntSpeculation(right))
                     changed |= mergePrediction(SpecBigInt);
                 else {
                     changed |= mergePrediction(SpecInt32Only | SpecBytecodeDouble);
-                    if (op == ValueDiv && (node->mayHaveBigIntResult()
+                    if ((op == ValueDiv || op == ValueMod) && (node->mayHaveBigIntResult()
                         || (left & SpecBigInt)
                         || (right & SpecBigInt)))
                         changed |= mergePrediction(SpecBigInt);
@@ -601,6 +602,7 @@ private:
         case ArithMax:
         case ArithMod:
         case ValueDiv:
+        case ValueMod:
         case ArithDiv: {
             SpeculatedType left = node->child1()->prediction();
             SpeculatedType right = node->child2()->prediction();
@@ -1117,6 +1119,7 @@ private:
         case ValueSub:
         case ValueMul:
         case ValueDiv:
+        case ValueMod:
         case ArithAdd:
         case ArithSub:
         case ArithNegate:
index 992bcf8..2bf682b 100644 (file)
@@ -238,6 +238,7 @@ bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node, bool igno
     case ValueSub:
     case ValueMul:
     case ValueDiv:
+    case ValueMod:
     case TryGetById:
     case DeleteById:
     case DeleteByVal:
index 3c03907..389e510 100644 (file)
@@ -5221,6 +5221,44 @@ void SpeculativeJIT::compileArithFRound(Node* node)
     doubleResult(result.fpr(), node);
 }
 
+void SpeculativeJIT::compileValueMod(Node* node)
+{
+    Edge& leftChild = node->child1();
+    Edge& rightChild = node->child2();
+
+    if (node->binaryUseKind() == BigIntUse) {
+        SpeculateCellOperand left(this, leftChild);
+        SpeculateCellOperand right(this, rightChild);
+        GPRReg leftGPR = left.gpr();
+        GPRReg rightGPR = right.gpr();
+
+        speculateBigInt(leftChild, leftGPR);
+        speculateBigInt(rightChild, rightGPR);
+
+        flushRegisters();
+        GPRFlushedCallResult result(this);
+        GPRReg resultGPR = result.gpr();
+
+        callOperation(operationModBigInt, resultGPR, leftGPR, rightGPR);
+
+        m_jit.exceptionCheck();
+        cellResult(resultGPR, node);
+        return;
+    }
+
+    DFG_ASSERT(m_jit.graph(), node, node->binaryUseKind() == UntypedUse, node->binaryUseKind());
+    JSValueOperand op1(this, leftChild);
+    JSValueOperand op2(this, rightChild);
+    JSValueRegs op1Regs = op1.jsValueRegs();
+    JSValueRegs op2Regs = op2.jsValueRegs();
+    flushRegisters();
+    JSValueRegsFlushedCallResult result(this);
+    JSValueRegs resultRegs = result.regs();
+    callOperation(operationValueMod, resultRegs, op1Regs, op2Regs);
+    m_jit.exceptionCheck();
+    jsValueResult(resultRegs, node);
+}
+
 void SpeculativeJIT::compileArithMod(Node* node)
 {
     switch (node->binaryUseKind()) {
index e65de38..e5c5c56 100644 (file)
@@ -1356,6 +1356,7 @@ public:
     void compileValueDiv(Node*);
     void compileArithDiv(Node*);
     void compileArithFRound(Node*);
+    void compileValueMod(Node*);
     void compileArithMod(Node*);
     void compileArithPow(Node*);
     void compileArithRounding(Node*);
index 1e33745..db2d464 100644 (file)
@@ -2087,6 +2087,11 @@ void SpeculativeJIT::compile(Node* node)
         break;
     }
 
+    case ValueMod: {
+        compileValueMod(node);
+        break;
+    }
+
     case ArithMod: {
         compileArithMod(node);
         break;
index f009e6b..f569724 100644 (file)
@@ -2233,6 +2233,11 @@ void SpeculativeJIT::compile(Node* node)
         break;
     }
 
+    case ValueMod: {
+        compileValueMod(node);
+        break;
+    }
+
     case ArithMod: {
         compileArithMod(node);
         break;
index 04dfdcb..0aa5b09 100644 (file)
@@ -257,6 +257,7 @@ public:
                 case ValueSub:
                 case ValueMul:
                 case ValueDiv:
+                case ValueMod:
                 case ArithAdd:
                 case ArithSub:
                 case ArithMul:
index 316ab8d..c5d4f18 100644 (file)
@@ -98,6 +98,7 @@ inline CapabilityLevel canCompile(Node* node)
     case ValueSub:
     case ValueMul:
     case ValueDiv:
+    case ValueMod:
     case StrCat:
     case ArithAdd:
     case ArithClz32:
index 8ba6dde..be6905c 100644 (file)
@@ -780,6 +780,9 @@ private:
         case ArithDiv:
             compileArithDiv();
             break;
+        case ValueMod:
+            compileValueMod();
+            break;
         case ArithMod:
             compileArithMod();
             break;
@@ -2546,6 +2549,24 @@ private:
         }
     }
     
+    void compileValueMod()
+    {
+        if (m_node->binaryUseKind() == BigIntUse) {
+            LValue left = lowBigInt(m_node->child1());
+            LValue right = lowBigInt(m_node->child2());
+
+            LValue result = vmCall(pointerType(), m_out.operation(operationModBigInt), m_callFrame, left, right);
+            setJSValue(result);
+            return;
+        }
+
+        DFG_ASSERT(m_graph, m_node, m_node->binaryUseKind() == UntypedUse, m_node->binaryUseKind());
+        LValue left = lowJSValue(m_node->child1());
+        LValue right = lowJSValue(m_node->child2());
+        LValue result = vmCall(Int64, m_out.operation(operationValueMod), m_callFrame, left, right);
+        setJSValue(result);
+    }
+
     void compileArithMod()
     {
         switch (m_node->binaryUseKind()) {