[ESNext][BigInt] Implement "~" unary operation
authorticaiolima@gmail.com <ticaiolima@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 11 Mar 2019 17:21:41 +0000 (17:21 +0000)
committerticaiolima@gmail.com <ticaiolima@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 11 Mar 2019 17:21:41 +0000 (17:21 +0000)
https://bugs.webkit.org/show_bug.cgi?id=182216

Reviewed by Keith Miller.

JSTests:

* stress/big-int-bit-not-general.js: Added.
* stress/big-int-bitwise-not-jit.js: Added.
* stress/big-int-bitwise-not-wrapped-value.js: Added.
* stress/bit-op-with-object-returning-int32.js:
* stress/bitwise-not-fixup-rules.js: Added.
* stress/value-bit-not-ai-rule.js: Added.

PerformanceTests:

* BigIntBench/big-int-simple-bit-not.js: Added.

Source/JavaScriptCore:

This patch is adding support of BigInt into op_bitnot operations. In
addition, we are changing ArithBitNot to handle only Number operands,
while introducing a new node named ValueBitNot to handle Untyped and
BigInt. This node follows the same approach we are doing into other
arithimetic operations into DFG.

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

It is possible that fixup and prediction propagation don't convert a
ValueBitNot(ConstInt32) into ArithBitNot(ConstInt32) because these
analysis are conservative. In such case, we are adding constant
folding rules to ValueBitNot AI.

* dfg/DFGBackwardsPropagationPhase.cpp:
(JSC::DFG::BackwardsPropagationPhase::propagate):

ValueBitNot has same rules as ArithBitNot on backwards propagation.

* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::parseBlock):

We can emit ArithBitNot if we know that operand of op_bitnot is a
Number or any int. Otherwise we fallback to ValueBitNot and rely on
fixup to convert the node to ArithBitNot when it is possible.
ValueBitNot uses heap prediction on prediction propagation and we
collect its type from op_bitnot's value profiler.

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

When we have the case with ValueBitNot(BigInt), we don't clobberize
world.

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

ValueBitNot can GC on BigIntUse because, right now, all bitNot
operation allocates temporary BigInts to perform calculations and it
can potentially trigger GC.

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

ValueBitNot is responsible do handle BigIntUse and UntypedUse. To all
other uses, we fallback to ArithBitNot.

* dfg/DFGNode.h:
(JSC::DFG::Node::hasHeapPrediction):
* dfg/DFGNodeType.h:
* dfg/DFGOperations.cpp:
(JSC::DFG::bitwiseBinaryOp):

This template function is abstracting the new semantics of numeric
values operations on bitwise operations. These operations usually
folow these steps:

    1. rhsNumeric = GetInt32OrBigInt(rhs)
    2. lhsNumeric = GetInt32OrBigInt(lhs)
    3. trhow error if TypeOf(rhsNumeric) != TypeOf(lhsNumeric)
    4. return BigInt::bitwiseOp(bitOp, rhs, lhs) if TypeOf(lhsNumeric) == BigInt
    5. return rhs <int32BitOp> lhs

Since we have almost the same code for every bitwise op,
we use such template to avoid code duplication. The template receives
Int32 and BigInt operations as parameter. Error message is received as
`const char*` instead of `String&` to avoid String allocation even when
there is no error to throw.

* dfg/DFGOperations.h:
* dfg/DFGPredictionPropagationPhase.cpp:
* dfg/DFGSafeToExecute.h:
(JSC::DFG::safeToExecute):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compileValueBitNot):

ValueBitNot generates speculative code for BigIntUse and this code is a
call to `operationBitNotBigInt`. This operation is faster than
`operationValueBitNot` because there is no need to check types of
operands and execute properly operation. We still need to check
exceptions after `operationBitNotBigInt` because it can throw OOM.

(JSC::DFG::SpeculativeJIT::compileBitwiseNot):
* 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::compileValueBitNot):
(JSC::FTL::DFG::LowerDFGToB3::compileArithBitNot):
* runtime/CommonSlowPaths.cpp:
(JSC::SLOW_PATH_DECL):
* runtime/JSBigInt.cpp:
(JSC::JSBigInt::bitwiseNot):
* runtime/JSBigInt.h:

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

31 files changed:
JSTests/ChangeLog
JSTests/stress/big-int-bit-not-general.js [new file with mode: 0644]
JSTests/stress/big-int-bitwise-not-jit.js [new file with mode: 0644]
JSTests/stress/big-int-bitwise-not-wrapped-value.js [new file with mode: 0644]
JSTests/stress/bit-op-with-object-returning-int32.js
JSTests/stress/bitwise-not-fixup-rules.js [new file with mode: 0644]
JSTests/stress/value-bit-not-ai-rule.js [new file with mode: 0644]
PerformanceTests/BigIntBench/big-int-simple-bit-not.js [new file with mode: 0644]
PerformanceTests/ChangeLog
Source/JavaScriptCore/ChangeLog
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/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/runtime/CommonSlowPaths.cpp
Source/JavaScriptCore/runtime/JSBigInt.cpp
Source/JavaScriptCore/runtime/JSBigInt.h

index f3dea19..f345dd3 100644 (file)
@@ -1,3 +1,17 @@
+2019-03-11  Caio Lima  <ticaiolima@gmail.com>
+
+        [ESNext][BigInt] Implement "~" unary operation
+        https://bugs.webkit.org/show_bug.cgi?id=182216
+
+        Reviewed by Keith Miller.
+
+        * stress/big-int-bit-not-general.js: Added.
+        * stress/big-int-bitwise-not-jit.js: Added.
+        * stress/big-int-bitwise-not-wrapped-value.js: Added.
+        * stress/bit-op-with-object-returning-int32.js:
+        * stress/bitwise-not-fixup-rules.js: Added.
+        * stress/value-bit-not-ai-rule.js: Added.
+
 2019-03-10  Ross Kirsling  <ross.kirsling@sony.com>
 
         Invalid flags in a RegExp literal should be an early SyntaxError
diff --git a/JSTests/stress/big-int-bit-not-general.js b/JSTests/stress/big-int-bit-not-general.js
new file mode 100644 (file)
index 0000000..bbf989b
--- /dev/null
@@ -0,0 +1,68 @@
+//@ runBigIntEnabled
+
+// Copyright (C) 2017 Josh Wolfe. All rights reserved.
+// This code is governed by the BSD license below.
+//
+// The << Software identified by reference to the Ecma Standard* ("Software)">>  is protected by copyright and is being
+// made available under the  "BSD License", included below. This Software may be subject to third party rights (rights
+// from parties other than Ecma International), including patent rights, and no licenses under such third party rights
+// are granted under this license even if the third party concerned is a member of Ecma International.  SEE THE ECMA
+// CODE OF CONDUCT IN PATENT MATTERS AVAILABLE AT http://www.ecma-international.org/memento/codeofconduct.htm FOR
+// INFORMATION REGARDING THE LICENSING OF PATENT CLAIMS THAT ARE REQUIRED TO IMPLEMENT ECMA INTERNATIONAL STANDARDS*.
+//
+// Copyright (C) 2012-2013 Ecma International
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
+// following conditions are met:
+// 1.   Redistributions of source code must retain the above copyright notice, this list of conditions and the following
+//      disclaimer.
+// 2.   Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
+//      following disclaimer in the documentation and/or other materials provided with the distribution.
+// 3.   Neither the name of the authors nor Ecma International may be used to endorse or promote products derived from
+//      this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE ECMA INTERNATIONAL "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+// SHALL ECMA INTERNATIONAL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+// DAMAGE.
+//
+// * Ecma International Standards hereafter means Ecma International Standards as well as Ecma Technical Reports
+
+function assert(a) {
+    if (!a)
+        throw new Error("Bad assertion");
+}
+
+assert.sameValue = function (input, expected, message) {
+    if (input !== expected)
+        throw new Error(message);
+}
+
+assert.sameValue(~0n, -1n, "~0n === -1n");
+assert.sameValue(~(0n), -1n, "~(0n) === -1n");
+assert.sameValue(~1n, -2n, "~1n === -2n");
+assert.sameValue(~-1n, 0n, "~-1n === 0n");
+assert.sameValue(~(-1n), 0n, "~(-1n) === 0n");
+assert.sameValue(~~1n, 1n, "~~1n === 1n");
+assert.sameValue(~0x5an, -0x5bn, "~0x5an === -0x5bn");
+assert.sameValue(~-0x5an, 0x59n, "~-0x5an === 0x59n");
+assert.sameValue(~0xffn, -0x100n, "~0xffn === -0x100n");
+assert.sameValue(~-0xffn, 0xfen, "~-0xffn === 0xfen");
+assert.sameValue(~0xffffn, -0x10000n, "~0xffffn === -0x10000n");
+assert.sameValue(~-0xffffn, 0xfffen, "~-0xffffn === 0xfffen");
+assert.sameValue(~0xffffffffn, -0x100000000n, "~0xffffffffn === -0x100000000n");
+assert.sameValue(~-0xffffffffn, 0xfffffffen, "~-0xffffffffn === 0xfffffffen");
+assert.sameValue(
+  ~0xffffffffffffffffn, -0x10000000000000000n,
+  "~0xffffffffffffffffn === -0x10000000000000000n");
+assert.sameValue(
+  ~-0xffffffffffffffffn, 0xfffffffffffffffen,
+  "~-0xffffffffffffffffn === 0xfffffffffffffffen");
+assert.sameValue(
+  ~0x123456789abcdef0fedcba9876543210n, -0x123456789abcdef0fedcba9876543211n,
+  "~0x123456789abcdef0fedcba9876543210n === -0x123456789abcdef0fedcba9876543211n");
+
diff --git a/JSTests/stress/big-int-bitwise-not-jit.js b/JSTests/stress/big-int-bitwise-not-jit.js
new file mode 100644 (file)
index 0000000..d761e1f
--- /dev/null
@@ -0,0 +1,19 @@
+//@ runBigIntEnabled
+
+let assert = {
+    sameValue: function(i, e, m) {
+        if (i !== e)
+            throw new Error(m);
+    }
+}
+
+function bigIntBitNot(a) {
+    return ~(~a);
+}
+noInline(bigIntBitNot);
+
+for (let i = 0; i < 10000; i++) {
+    let r = bigIntBitNot(3n);
+    assert.sameValue(r, 3n, "~~" + 3n + " = " + r);
+}
+
diff --git a/JSTests/stress/big-int-bitwise-not-wrapped-value.js b/JSTests/stress/big-int-bitwise-not-wrapped-value.js
new file mode 100644 (file)
index 0000000..7f4a5df
--- /dev/null
@@ -0,0 +1,59 @@
+//@ runBigIntEnabled
+
+assert = {
+    sameValue: function (input, expected, message) {
+        if (input !== expected)
+            throw new Error(message);
+    }
+};
+
+function testBitNot(x, z, message) {
+    assert.sameValue(~x, z, message);
+}
+
+testBitNot(Object(1n), -2n, "ToPrimitive: unbox object with internal slot");
+
+let o = {
+    [Symbol.toPrimitive]: function() {
+        return 1n;
+    }
+};
+testBitNot(o, -2n, "ToPrimitive: @@toPrimitive");
+
+o = {
+    valueOf: function() {
+        return 1n;
+    }
+};
+testBitNot(o, -2n, "ToPrimitive: valueOf");
+
+o = {
+    toString: function() {
+        return 1n;
+    }
+}
+testBitNot(o, -2n, "ToPrimitive: toString");
+
+// Test priority
+
+function badAssertion() {
+    throw new Error("This should never be called");
+}
+
+o = {
+    [Symbol.toPrimitive]: function() {
+        return 1n;
+    },
+    valueOf: badAssertion,
+    toString: badAssertion
+};
+testBitNot(o, -2n, "ToPrimitive: @@toPrimitive and others throw");
+
+o = {
+    valueOf: function() {
+        return 1n;
+    },
+    toString: badAssertion
+};
+testBitNot(o, -2n, "ToPrimitive: valueOf and toString throws");
+
index 7e91344..1749afb 100644 (file)
@@ -36,3 +36,13 @@ for (var i = 0; i < 10000; i++)
 
 assert(numberOfDFGCompiles(bitXor) <= 1, true);
 
+function bitNot(a) {
+    return ~a;
+}
+noInline(bitNot);
+
+for (var i = 0; i < 10000; i++)
+    assert(bitNot(o), -14);
+
+assert(numberOfDFGCompiles(bitNot) <= 1, true);
+
diff --git a/JSTests/stress/bitwise-not-fixup-rules.js b/JSTests/stress/bitwise-not-fixup-rules.js
new file mode 100644 (file)
index 0000000..ead2ce2
--- /dev/null
@@ -0,0 +1,27 @@
+//@ if $jitTests then runBigIntEnabled else skip end
+
+function assert(a, e) {
+    if (a !== e)
+        throw new Error("Expected to be: " + e + " but got: " + a);
+}
+
+function foo(a) {
+    return ~a;
+}
+noInline(foo);
+
+let c = 0;
+let o = {
+    valueOf: () => {
+        c++;
+        return 3;
+    }
+};
+
+for (let i = 0; i < 10000; i++)
+    foo(o);
+
+assert(c, 10000);
+if (numberOfDFGCompiles(foo) > 1)
+    throw new Error("Function 'foo' should be compiled just once");
+
diff --git a/JSTests/stress/value-bit-not-ai-rule.js b/JSTests/stress/value-bit-not-ai-rule.js
new file mode 100644 (file)
index 0000000..de5dd3e
--- /dev/null
@@ -0,0 +1,22 @@
+//@ runBigIntEnabled
+
+function assert(a, e) {
+    if (a !== e)
+        throw new Error("Expected: " + e + " bug got: " + a);
+}
+
+let predicate = true;
+function foo(a, b) {
+    let v = a;
+    if (predicate)
+        v = 10;
+
+    let c = ~v;
+    return c;
+}
+noInline(foo);
+
+for (let i = 0; i < 10000; i++) {
+    assert(foo(10n, 10), -11);
+}
+
diff --git a/PerformanceTests/BigIntBench/big-int-simple-bit-not.js b/PerformanceTests/BigIntBench/big-int-simple-bit-not.js
new file mode 100644 (file)
index 0000000..488a70d
--- /dev/null
@@ -0,0 +1,15 @@
+function bigInt(a, b) {
+    let c = ~a;
+    return ~a + ~c;
+}
+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 ef440de..4ad2df3 100644 (file)
@@ -1,3 +1,12 @@
+2019-03-11  Caio Lima  <ticaiolima@gmail.com>
+
+        [ESNext][BigInt] Implement "~" unary operation
+        https://bugs.webkit.org/show_bug.cgi?id=182216
+
+        Reviewed by Keith Miller.
+
+        * BigIntBench/big-int-simple-bit-not.js: Added.
+
 2019-03-04  Saam Barati  <sbarati@apple.com>
 
         Add a detailed summary page for JetStream 2
index de331db..855d91e 100644 (file)
@@ -1,3 +1,110 @@
+2019-03-11  Caio Lima  <ticaiolima@gmail.com>
+
+        [ESNext][BigInt] Implement "~" unary operation
+        https://bugs.webkit.org/show_bug.cgi?id=182216
+
+        Reviewed by Keith Miller.
+
+        This patch is adding support of BigInt into op_bitnot operations. In
+        addition, we are changing ArithBitNot to handle only Number operands,
+        while introducing a new node named ValueBitNot to handle Untyped and
+        BigInt. This node follows the same approach we are doing into other
+        arithimetic operations into DFG.
+
+        * dfg/DFGAbstractInterpreterInlines.h:
+        (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+
+        It is possible that fixup and prediction propagation don't convert a
+        ValueBitNot(ConstInt32) into ArithBitNot(ConstInt32) because these
+        analysis are conservative. In such case, we are adding constant
+        folding rules to ValueBitNot AI.
+
+        * dfg/DFGBackwardsPropagationPhase.cpp:
+        (JSC::DFG::BackwardsPropagationPhase::propagate):
+
+        ValueBitNot has same rules as ArithBitNot on backwards propagation.
+
+        * dfg/DFGByteCodeParser.cpp:
+        (JSC::DFG::ByteCodeParser::parseBlock):
+
+        We can emit ArithBitNot if we know that operand of op_bitnot is a
+        Number or any int. Otherwise we fallback to ValueBitNot and rely on
+        fixup to convert the node to ArithBitNot when it is possible.
+        ValueBitNot uses heap prediction on prediction propagation and we
+        collect its type from op_bitnot's value profiler.
+
+        * dfg/DFGClobberize.h:
+        (JSC::DFG::clobberize):
+
+        When we have the case with ValueBitNot(BigInt), we don't clobberize
+        world.
+
+        * dfg/DFGDoesGC.cpp:
+        (JSC::DFG::doesGC):
+
+        ValueBitNot can GC on BigIntUse because, right now, all bitNot
+        operation allocates temporary BigInts to perform calculations and it
+        can potentially trigger GC.
+
+        * dfg/DFGFixupPhase.cpp:
+        (JSC::DFG::FixupPhase::fixupNode):
+
+        ValueBitNot is responsible do handle BigIntUse and UntypedUse. To all
+        other uses, we fallback to ArithBitNot.
+
+        * dfg/DFGNode.h:
+        (JSC::DFG::Node::hasHeapPrediction):
+        * dfg/DFGNodeType.h:
+        * dfg/DFGOperations.cpp:
+        (JSC::DFG::bitwiseBinaryOp):
+
+        This template function is abstracting the new semantics of numeric
+        values operations on bitwise operations. These operations usually
+        folow these steps:
+
+            1. rhsNumeric = GetInt32OrBigInt(rhs)
+            2. lhsNumeric = GetInt32OrBigInt(lhs)
+            3. trhow error if TypeOf(rhsNumeric) != TypeOf(lhsNumeric)
+            4. return BigInt::bitwiseOp(bitOp, rhs, lhs) if TypeOf(lhsNumeric) == BigInt
+            5. return rhs <int32BitOp> lhs
+
+        Since we have almost the same code for every bitwise op,
+        we use such template to avoid code duplication. The template receives
+        Int32 and BigInt operations as parameter. Error message is received as
+        `const char*` instead of `String&` to avoid String allocation even when
+        there is no error to throw.
+
+        * dfg/DFGOperations.h:
+        * dfg/DFGPredictionPropagationPhase.cpp:
+        * dfg/DFGSafeToExecute.h:
+        (JSC::DFG::safeToExecute):
+        * dfg/DFGSpeculativeJIT.cpp:
+        (JSC::DFG::SpeculativeJIT::compileValueBitNot):
+
+        ValueBitNot generates speculative code for BigIntUse and this code is a
+        call to `operationBitNotBigInt`. This operation is faster than
+        `operationValueBitNot` because there is no need to check types of
+        operands and execute properly operation. We still need to check
+        exceptions after `operationBitNotBigInt` because it can throw OOM.
+
+        (JSC::DFG::SpeculativeJIT::compileBitwiseNot):
+        * 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::compileValueBitNot):
+        (JSC::FTL::DFG::LowerDFGToB3::compileArithBitNot):
+        * runtime/CommonSlowPaths.cpp:
+        (JSC::SLOW_PATH_DECL):
+        * runtime/JSBigInt.cpp:
+        (JSC::JSBigInt::bitwiseNot):
+        * runtime/JSBigInt.h:
+
 2019-03-11  Darin Adler  <darin@apple.com>
 
         Specify fixed precision explicitly to prepare to change String::number and StringBuilder::appendNumber floating point behavior
index f6f0ace..eb75cef 100644 (file)
@@ -376,13 +376,26 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
         break;
     }
 
-    case ArithBitNot: {
-        if (node->child1().useKind() == UntypedUse) {
-            clobberWorld();
-            setNonCellTypeForNode(node, SpecInt32Only);
+    case ValueBitNot: {
+        JSValue operand = forNode(node->child1()).value();
+        if (operand && operand.isInt32()) {
+            didFoldClobberWorld();
+            int32_t a = operand.asInt32();
+            setConstant(node, JSValue(~a));
             break;
         }
 
+        if (node->child1().useKind() == BigIntUse)
+            setTypeForNode(node, SpecBigInt);
+        else {
+            clobberWorld();
+            setTypeForNode(node, SpecBoolInt32 | SpecBigInt);
+        }
+
+        break;
+    }
+
+    case ArithBitNot: {
         JSValue operand = forNode(node->child1()).value();
         if (operand && operand.isInt32()) {
             int32_t a = operand.asInt32();
index f4ce71e..6ecc199 100644 (file)
@@ -210,6 +210,7 @@ private:
         case CheckVarargs:
             break;
             
+        case ValueBitNot:
         case ArithBitNot: {
             flags |= NodeBytecodeUsesAsInt;
             flags &= ~(NodeBytecodeUsesAsNumber | NodeBytecodeNeedsNegZero | NodeBytecodeUsesAsOther);
index 69bf14a..25a5403 100644 (file)
@@ -4918,8 +4918,12 @@ void ByteCodeParser::parseBlock(unsigned limit)
 
         case op_bitnot: {
             auto bytecode = currentInstruction->as<OpBitnot>();
+            SpeculatedType prediction = getPrediction();
             Node* op1 = get(bytecode.m_operand);
-            set(bytecode.m_dst, addToGraph(ArithBitNot, op1));
+            if (op1->hasNumberOrAnyIntResult())
+                set(bytecode.m_dst, addToGraph(ArithBitNot, op1));
+            else
+                set(bytecode.m_dst, addToGraph(ValueBitNot, OpInfo(), OpInfo(prediction), op1));
             NEXT_OPCODE(op_bitnot);
         }
 
index add1499..3725d63 100644 (file)
@@ -259,6 +259,15 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu
         def(PureValue(node, node->queriedType()));
         return;
 
+    case ValueBitNot:
+        if (node->child1().useKind() == BigIntUse) {
+            def(PureValue(node));
+            return;
+        }
+        read(World);
+        write(Heap);
+        return;
+
     case ArithBitNot:
         if (node->child1().useKind() == UntypedUse) {
             read(World);
index a211b1a..44050f6 100644 (file)
@@ -378,6 +378,7 @@ bool doesGC(Graph& graph, Node* node)
     case ValueSub:
     case ValueMul:
     case ValueDiv:
+    case ValueBitNot:
     case ValueNegate:
 #else
     // See comment at the top for why be default for all nodes should be to
index c68d6e2..f6448eb 100644 (file)
@@ -234,14 +234,27 @@ private:
             break;
         }
 
-        case ArithBitNot: {
-            if (node->child1().node()->shouldSpeculateUntypedForBitOps()) {
-                fixEdge<UntypedUse>(node->child1());
-                break;
+        case ValueBitNot: {
+            Edge& operandEdge = node->child1();
+
+            if (operandEdge.node()->shouldSpeculateBigInt()) {
+                node->clearFlags(NodeMustGenerate);
+                fixEdge<BigIntUse>(operandEdge);
+            } else if (operandEdge.node()->shouldSpeculateUntypedForBitOps())
+                fixEdge<UntypedUse>(operandEdge);
+            else {
+                node->setOp(ArithBitNot);
+                node->setResult(NodeResultInt32);
+                node->clearFlags(NodeMustGenerate);
+                fixIntConvertingEdge(operandEdge);
             }
+            break;
+        }
 
-            fixIntConvertingEdge(node->child1());
-            node->clearFlags(NodeMustGenerate);
+        case ArithBitNot: {
+            Edge& operandEdge = node->child1();
+
+            fixIntConvertingEdge(operandEdge);
             break;
         }
 
index fb3679a..211dd0b 100644 (file)
@@ -1693,6 +1693,7 @@ public:
         case ValueBitAnd:
         case ValueBitOr:
         case ValueBitXor:
+        case ValueBitNot:
         case CallObjectConstructor:
         case LoadKeyFromMapBucket:
         case LoadValueFromMapBucket:
index ef63cdb..95d13a3 100644 (file)
@@ -111,7 +111,8 @@ namespace JSC { namespace DFG {
     macro(InvalidationPoint, NodeMustGenerate) \
     \
     /* Nodes for bitwise operations. */\
-    macro(ArithBitNot, NodeResultInt32 | NodeMustGenerate) \
+    macro(ValueBitNot, NodeResultJS | NodeMustGenerate) \
+    macro(ArithBitNot, NodeResultInt32) \
     macro(ValueBitAnd, NodeResultJS | NodeMustGenerate) \
     macro(ArithBitAnd, NodeResultInt32) \
     macro(ValueBitOr, NodeResultJS | NodeMustGenerate) \
index 86bca3b..a5d1848 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 Int32Operation>
+static ALWAYS_INLINE EncodedJSValue bitwiseBinaryOp(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, BigIntOperation&& bigIntOp, Int32Operation&& int32Op, 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.toBigIntOrInt32(exec);
+    RETURN_IF_EXCEPTION(scope, encodedJSValue());
+    auto rightNumeric = op2.toBigIntOrInt32(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(int32Op(WTF::get<int32_t>(leftNumeric), WTF::get<int32_t>(rightNumeric))));
+}
+
 static ALWAYS_INLINE EncodedJSValue parseIntResult(double input)
 {
     int asInt = static_cast<int>(input);
@@ -361,91 +388,52 @@ EncodedJSValue JIT_OPERATION operationValueBitNot(ExecState* exec, EncodedJSValu
 
     JSValue op1 = JSValue::decode(encodedOp1);
 
-    int32_t operandValue = op1.toInt32(exec);
+    auto operandNumeric = op1.toBigIntOrInt32(exec);
     RETURN_IF_EXCEPTION(scope, encodedJSValue());
 
-    return JSValue::encode(jsNumber(~operandValue));
+    if (WTF::holds_alternative<JSBigInt*>(operandNumeric))
+        RELEASE_AND_RETURN(scope, JSValue::encode(JSBigInt::bitwiseNot(exec, WTF::get<JSBigInt*>(operandNumeric))));
+
+    return JSValue::encode(jsNumber(~WTF::get<int32_t>(operandNumeric)));
 }
 
 EncodedJSValue JIT_OPERATION operationValueBitAnd(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)
 {
-    VM* vm = &exec->vm();
-    NativeCallFrameTracer tracer(vm, exec);
-    auto scope = DECLARE_THROW_SCOPE(*vm);
+    auto bigIntOp = [] (ExecState* exec, JSBigInt* left, JSBigInt* right) -> JSBigInt* {
+        return JSBigInt::bitwiseAnd(exec, left, right);
+    };
 
-    JSValue op1 = JSValue::decode(encodedOp1);
-    JSValue op2 = JSValue::decode(encodedOp2);
-
-    auto leftNumeric = op1.toBigIntOrInt32(exec);
-    RETURN_IF_EXCEPTION(scope, encodedJSValue());
-    auto rightNumeric = op2.toBigIntOrInt32(exec);
-    RETURN_IF_EXCEPTION(scope, encodedJSValue());
+    auto int32Op = [] (int32_t left, int32_t right) -> int32_t {
+        return left & right;
+    };
 
-    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::bitwiseAnd(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 bitwise 'and' operation.");
-    }
-
-    return JSValue::encode(jsNumber(WTF::get<int32_t>(leftNumeric) & WTF::get<int32_t>(rightNumeric)));
+    return bitwiseBinaryOp(exec, encodedOp1, encodedOp2, bigIntOp, int32Op, "Invalid mix of BigInt and other type in bitwise 'and' operation."_s);
 }
 
 EncodedJSValue JIT_OPERATION operationValueBitOr(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)
 {
-    VM* vm = &exec->vm();
-    NativeCallFrameTracer tracer(vm, exec);
-    auto scope = DECLARE_THROW_SCOPE(*vm);
+    auto bigIntOp = [] (ExecState* exec, JSBigInt* left, JSBigInt* right) -> JSBigInt* {
+        return JSBigInt::bitwiseOr(exec, left, right);
+    };
 
-    JSValue op1 = JSValue::decode(encodedOp1);
-    JSValue op2 = JSValue::decode(encodedOp2);
+    auto int32Op = [] (int32_t left, int32_t right) -> int32_t {
+        return left | right;
+    };
 
-    auto leftNumeric = op1.toBigIntOrInt32(exec);
-    RETURN_IF_EXCEPTION(scope, encodedJSValue());
-    auto rightNumeric = op2.toBigIntOrInt32(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::bitwiseOr(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 bitwise 'or' operation.");
-    }
-
-    return JSValue::encode(jsNumber(WTF::get<int32_t>(leftNumeric) | WTF::get<int32_t>(rightNumeric)));
+    return bitwiseBinaryOp(exec, encodedOp1, encodedOp2, bigIntOp, int32Op, "Invalid mix of BigInt and other type in bitwise 'or' operation."_s);
 }
 
 EncodedJSValue JIT_OPERATION operationValueBitXor(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)
 {
-    VM* vm = &exec->vm();
-    NativeCallFrameTracer tracer(vm, exec);
-    auto scope = DECLARE_THROW_SCOPE(*vm);
+    auto bigIntOp = [] (ExecState* exec, JSBigInt* left, JSBigInt* right) -> JSBigInt* {
+        return JSBigInt::bitwiseXor(exec, left, right);
+    };
 
-    JSValue op1 = JSValue::decode(encodedOp1);
-    JSValue op2 = JSValue::decode(encodedOp2);
+    auto int32Op = [] (int32_t left, int32_t right) -> int32_t {
+        return left ^ right;
+    };
 
-    auto leftNumeric = op1.toBigIntOrInt32(exec);
-    RETURN_IF_EXCEPTION(scope, encodedJSValue());
-    auto rightNumeric = op2.toBigIntOrInt32(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::bitwiseXor(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 bitwise 'xor' operation.");
-    }
-
-    return JSValue::encode(jsNumber(WTF::get<int32_t>(leftNumeric) ^ WTF::get<int32_t>(rightNumeric)));
+    return bitwiseBinaryOp(exec, encodedOp1, encodedOp2, bigIntOp, int32Op, "Invalid mix of BigInt and other type in bitwise 'xor' operation."_s);
 }
 
 EncodedJSValue JIT_OPERATION operationValueBitLShift(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)
@@ -1338,6 +1326,16 @@ JSCell* JIT_OPERATION operationSubBigInt(ExecState* exec, JSCell* op1, JSCell* o
     return JSBigInt::sub(exec, leftOperand, rightOperand);
 }
 
+JSCell* JIT_OPERATION operationBitNotBigInt(ExecState* exec, JSCell* op1)
+{
+    VM* vm = &exec->vm();
+    NativeCallFrameTracer tracer(vm, exec);
+
+    JSBigInt* operand = jsCast<JSBigInt*>(op1);
+
+    return JSBigInt::bitwiseNot(exec, operand);
+}
+
 JSCell* JIT_OPERATION operationMulBigInt(ExecState* exec, JSCell* op1, JSCell* op2)
 {
     VM* vm = &exec->vm();
index 45b9c93..e76d445 100644 (file)
@@ -170,6 +170,7 @@ JSCell* JIT_OPERATION operationSubBigInt(ExecState*, JSCell* op1, JSCell* op2) W
 JSCell* JIT_OPERATION operationMulBigInt(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;
 JSCell* JIT_OPERATION operationBitOrBigInt(ExecState*, JSCell* op1, JSCell* op2) WTF_INTERNAL;
 JSCell* JIT_OPERATION operationAddBigInt(ExecState*, JSCell* op1, JSCell* op2) WTF_INTERNAL;
 JSCell* JIT_OPERATION operationBitXorBigInt(ExecState*, JSCell* op1, JSCell* op2) WTF_INTERNAL;
index c2afe5b..206d424 100644 (file)
@@ -802,6 +802,7 @@ private:
         case ValueBitAnd:
         case ValueBitXor:
         case ValueBitOr:
+        case ValueBitNot:
         case CallObjectConstructor:
         case GetArgument:
         case CallDOMGetter:
index 883f799..1cf15c4 100644 (file)
@@ -231,6 +231,7 @@ bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node, bool igno
     case ValueBitAnd:
     case ValueBitXor:
     case ValueBitOr:
+    case ValueBitNot:
     case ValueNegate:
     case ValueAdd:
     case ValueSub:
index 2fae9ed..5b68a30 100644 (file)
@@ -3522,24 +3522,43 @@ void SpeculativeJIT::compileInstanceOf(Node* node)
     return;
 }
 
-void SpeculativeJIT::compileBitwiseNot(Node* node)
+void SpeculativeJIT::compileValueBitNot(Node* node)
 {
     Edge& child1 = node->child1();
 
-    if (child1.useKind() == UntypedUse) {
-        JSValueOperand operand(this, child1);
-        JSValueRegs operandRegs = operand.jsValueRegs();
+    if (child1.useKind() == BigIntUse) {
+        SpeculateCellOperand operand(this, child1);
+        GPRReg operandGPR = operand.gpr();
+
+        speculateBigInt(child1, operandGPR);
 
         flushRegisters();
-        JSValueRegsFlushedCallResult result(this);
-        JSValueRegs resultRegs = result.regs();
-        callOperation(operationValueBitNot, resultRegs, operandRegs);
+        GPRFlushedCallResult result(this);
+        GPRReg resultGPR = result.gpr();
+
+        callOperation(operationBitNotBigInt, resultGPR, operandGPR);
         m_jit.exceptionCheck();
+        cellResult(resultGPR, node);
 
-        jsValueResult(resultRegs, node);
         return;
     }
 
+    JSValueOperand operand(this, child1);
+    JSValueRegs operandRegs = operand.jsValueRegs();
+
+    flushRegisters();
+    JSValueRegsFlushedCallResult result(this);
+    JSValueRegs resultRegs = result.regs();
+    callOperation(operationValueBitNot, resultRegs, operandRegs);
+    m_jit.exceptionCheck();
+
+    jsValueResult(resultRegs, node);
+}
+
+void SpeculativeJIT::compileBitwiseNot(Node* node)
+{
+    Edge& child1 = node->child1();
+
     SpeculateInt32Operand operand(this, child1);
     GPRTemporary result(this);
     GPRReg resultGPR = result.gpr();
index 7e07854..017ec8c 100644 (file)
@@ -1325,6 +1325,7 @@ public:
     void compileUInt32ToNumber(Node*);
     void compileDoubleAsInt32(Node*);
 
+    void compileValueBitNot(Node*);
     void compileBitwiseNot(Node*);
 
     template<typename SnippetGenerator, J_JITOperation_EJJ slowPathFunction>
index cd3d351..ca3b95e 100644 (file)
@@ -1991,6 +1991,10 @@ void SpeculativeJIT::compile(Node* node)
         compileBitwiseOp(node);
         break;
 
+    case ValueBitNot:
+        compileValueBitNot(node);
+        break;
+
     case ArithBitNot:
         compileBitwiseNot(node);
         break;
index 88cf41b..93146f5 100644 (file)
@@ -2080,6 +2080,10 @@ void SpeculativeJIT::compile(Node* node)
         recordSetLocal(dataFormatFor(node->variableAccessData()->flushFormat()));
         break;
 
+    case ValueBitNot:
+        compileValueBitNot(node);
+        break;
+
     case ArithBitNot:
         compileBitwiseNot(node);
         break;
index 5357212..8aeaca7 100644 (file)
@@ -91,6 +91,7 @@ inline CapabilityLevel canCompile(Node* node)
     case ValueBitAnd:
     case ValueBitXor:
     case ValueBitOr:
+    case ValueBitNot:
     case ValueNegate:
     case ValueAdd:
     case ValueSub:
index 444e669..64a35a6 100644 (file)
@@ -665,6 +665,9 @@ private:
         case ArithUnary:
             compileArithUnary();
             break;
+        case ValueBitNot:
+            compileValueBitNot();
+            break;
         case ArithBitNot:
             compileArithBitNot();
             break;
@@ -2890,15 +2893,22 @@ private:
         }
     }
     
-    void compileArithBitNot()
+    void compileValueBitNot()
     {
-        if (m_node->child1().useKind() == UntypedUse) {
-            LValue operand = lowJSValue(m_node->child1());
-            LValue result = vmCall(Int64, m_out.operation(operationValueBitNot), m_callFrame, operand);
+        if (m_node->child1().useKind() == BigIntUse) {
+            LValue operand = lowBigInt(m_node->child1());
+            LValue result = vmCall(pointerType(), m_out.operation(operationBitNotBigInt), m_callFrame, operand);
             setJSValue(result);
             return;
         }
 
+        LValue operand = lowJSValue(m_node->child1());
+        LValue result = vmCall(Int64, m_out.operation(operationValueBitNot), m_callFrame, operand);
+        setJSValue(result);
+    }
+
+    void compileArithBitNot()
+    {
         setInt32(m_out.bitNot(lowInt32(m_node->child1())));
     }
 
index b8d8bdb..c5dfe1c 100644 (file)
@@ -715,9 +715,16 @@ SLOW_PATH_DECL(slow_path_bitnot)
 {
     BEGIN();
     auto bytecode = pc->as<OpBitnot>();
-    int32_t operand = GET_C(bytecode.m_operand).jsValue().toInt32(exec);
+    auto operandNumeric = GET_C(bytecode.m_operand).jsValue().toBigIntOrInt32(exec);
     CHECK_EXCEPTION();
-    RETURN_PROFILED(jsNumber(~operand));
+
+    if (WTF::holds_alternative<JSBigInt*>(operandNumeric)) {
+        JSBigInt* result = JSBigInt::bitwiseNot(exec, WTF::get<JSBigInt*>(operandNumeric));
+        CHECK_EXCEPTION();
+        RETURN_PROFILED(result);
+    }
+
+    RETURN_PROFILED(jsNumber(~WTF::get<int32_t>(operandNumeric)));
 }
 
 SLOW_PATH_DECL(slow_path_bitand)
index 2bcaa9b..c588e3f 100644 (file)
@@ -525,6 +525,16 @@ JSBigInt* JSBigInt::signedRightShift(ExecState* exec, JSBigInt* x, JSBigInt* y)
     return rightShiftByAbsolute(exec, x, y);
 }
 
+JSBigInt* JSBigInt::bitwiseNot(ExecState* exec, JSBigInt* x)
+{
+    if (x->sign()) {
+        // ~(-x) == ~(~(x-1)) == x-1
+        return absoluteSubOne(exec, x, x->length());
+    } 
+    // ~x == -x-1 == -(x+1)
+    return absoluteAddOne(exec, x, SignOption::Signed);
+}
+
 #if USE(JSVALUE32_64)
 #define HAVE_TWO_DIGIT 1
 typedef uint64_t TwoDigit;
index 697f23c..5cc9e09 100644 (file)
@@ -124,6 +124,7 @@ public:
     static JSBigInt* bitwiseAnd(ExecState*, JSBigInt* x, JSBigInt* y);
     static JSBigInt* bitwiseOr(ExecState*, JSBigInt* x, JSBigInt* y);
     static JSBigInt* bitwiseXor(ExecState*, JSBigInt* x, JSBigInt* y);
+    static JSBigInt* bitwiseNot(ExecState*, JSBigInt* x);
 
     static JSBigInt* leftShift(ExecState*, JSBigInt* x, JSBigInt* y);
     static JSBigInt* signedRightShift(ExecState*, JSBigInt* x, JSBigInt* y);