[ESNext][BigInt] Implement support for "&"
authorticaiolima@gmail.com <ticaiolima@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 29 Sep 2018 04:55:40 +0000 (04:55 +0000)
committerticaiolima@gmail.com <ticaiolima@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 29 Sep 2018 04:55:40 +0000 (04:55 +0000)
https://bugs.webkit.org/show_bug.cgi?id=186228

Reviewed by Yusuke Suzuki.

JSTests:

* stress/big-int-bitwise-and-general.js: Added.
(assert):
(assert.sameValue):
* stress/big-int-bitwise-and-jit.js: Added.
(let.assert.sameValue):
(bigIntBitAnd):
* stress/big-int-bitwise-and-memory-stress.js: Added.
(assert):
* stress/big-int-bitwise-and-to-primitive-precedence.js: Added.
(assert.sameValue):
(let.o.Symbol.toPrimitive):
(catch):
* stress/big-int-bitwise-and-type-error.js: Added.
(assert):
(assertThrowTypeError):
(let.o.valueOf):
(o.valueOf):
(o.toString):
(o.Symbol.toPrimitive):
* stress/big-int-bitwise-and-wrapped-value.js: Added.
(assert.sameValue):
(testBitAnd):
(let.o.Symbol.toPrimitive):
(o.valueOf):
(o.toString):

Source/JavaScriptCore:

This patch introduces support of BigInt into bitwise "&" operation.
We are also introducing the ValueBitAnd DFG node, that is responsible
to take care of JIT for non-Int32 operands. With the introduction of this
new node, we renamed the BitAnd node to ArithBitAnd. The ArithBitAnd
follows the behavior of ArithAdd and other arithmetic nodes, where
the Arith<op> version always results in Number (in the case of
ArithBitAnd, its is always an Int32).

* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::finishCreation):
* bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::emitBinaryOp):
* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
* dfg/DFGBackwardsPropagationPhase.cpp:
(JSC::DFG::BackwardsPropagationPhase::isWithinPowerOfTwo):
(JSC::DFG::BackwardsPropagationPhase::propagate):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::parseBlock):
* dfg/DFGClobberize.h:
(JSC::DFG::clobberize):
* dfg/DFGDoesGC.cpp:
(JSC::DFG::doesGC):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
* dfg/DFGNodeType.h:
* dfg/DFGOperations.cpp:
* dfg/DFGOperations.h:
* dfg/DFGPredictionPropagationPhase.cpp:
* dfg/DFGSafeToExecute.h:
(JSC::DFG::safeToExecute):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compileValueBitwiseOp):
(JSC::DFG::SpeculativeJIT::compileBitwiseOp):
* dfg/DFGSpeculativeJIT.h:
(JSC::DFG::SpeculativeJIT::bitOp):
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGStrengthReductionPhase.cpp:
(JSC::DFG::StrengthReductionPhase::handleNode):
* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::compileNode):
(JSC::FTL::DFG::LowerDFGToB3::compileValueBitAnd):
(JSC::FTL::DFG::LowerDFGToB3::compileArithBitAnd):
(JSC::FTL::DFG::LowerDFGToB3::compileBitAnd): Deleted.
* jit/JIT.h:
* jit/JITArithmetic.cpp:
(JSC::JIT::emitBitBinaryOpFastPath):
(JSC::JIT::emit_op_bitand):
* llint/LowLevelInterpreter32_64.asm:
* llint/LowLevelInterpreter64.asm:
* runtime/CommonSlowPaths.cpp:
(JSC::SLOW_PATH_DECL):
* runtime/JSBigInt.cpp:
(JSC::JSBigInt::JSBigInt):
(JSC::JSBigInt::initialize):
(JSC::JSBigInt::createZero):
(JSC::JSBigInt::createFrom):
(JSC::JSBigInt::bitwiseAnd):
(JSC::JSBigInt::absoluteBitwiseOp):
(JSC::JSBigInt::absoluteAnd):
(JSC::JSBigInt::absoluteOr):
(JSC::JSBigInt::absoluteAndNot):
(JSC::JSBigInt::absoluteAddOne):
(JSC::JSBigInt::absoluteSubOne):
* runtime/JSBigInt.h:
* runtime/JSCJSValue.h:
* runtime/JSCJSValueInlines.h:
(JSC::JSValue::toBigIntOrInt32 const):

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

37 files changed:
JSTests/ChangeLog
JSTests/stress/big-int-bitwise-and-general.js [new file with mode: 0644]
JSTests/stress/big-int-bitwise-and-jit.js [new file with mode: 0644]
JSTests/stress/big-int-bitwise-and-memory-stress.js [new file with mode: 0644]
JSTests/stress/big-int-bitwise-and-to-primitive-precedence.js [new file with mode: 0644]
JSTests/stress/big-int-bitwise-and-type-error.js [new file with mode: 0644]
JSTests/stress/big-int-bitwise-and-wrapped-value.js [new file with mode: 0644]
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/bytecode/CodeBlock.cpp
Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
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/DFGStrengthReductionPhase.cpp
Source/JavaScriptCore/ftl/FTLCapabilities.cpp
Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp
Source/JavaScriptCore/jit/JIT.h
Source/JavaScriptCore/jit/JITArithmetic.cpp
Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm
Source/JavaScriptCore/llint/LowLevelInterpreter64.asm
Source/JavaScriptCore/runtime/CommonSlowPaths.cpp
Source/JavaScriptCore/runtime/JSBigInt.cpp
Source/JavaScriptCore/runtime/JSBigInt.h
Source/JavaScriptCore/runtime/JSCJSValue.h
Source/JavaScriptCore/runtime/JSCJSValueInlines.h

index fba245e..28a5794 100644 (file)
@@ -1,3 +1,36 @@
+2018-09-28  Caio Lima  <ticaiolima@gmail.com>
+
+        [ESNext][BigInt] Implement support for "&"
+        https://bugs.webkit.org/show_bug.cgi?id=186228
+
+        Reviewed by Yusuke Suzuki.
+
+        * stress/big-int-bitwise-and-general.js: Added.
+        (assert):
+        (assert.sameValue):
+        * stress/big-int-bitwise-and-jit.js: Added.
+        (let.assert.sameValue):
+        (bigIntBitAnd):
+        * stress/big-int-bitwise-and-memory-stress.js: Added.
+        (assert):
+        * stress/big-int-bitwise-and-to-primitive-precedence.js: Added.
+        (assert.sameValue):
+        (let.o.Symbol.toPrimitive):
+        (catch):
+        * stress/big-int-bitwise-and-type-error.js: Added.
+        (assert):
+        (assertThrowTypeError):
+        (let.o.valueOf):
+        (o.valueOf):
+        (o.toString):
+        (o.Symbol.toPrimitive):
+        * stress/big-int-bitwise-and-wrapped-value.js: Added.
+        (assert.sameValue):
+        (testBitAnd):
+        (let.o.Symbol.toPrimitive):
+        (o.valueOf):
+        (o.toString):
+
 2018-09-28  Ross Kirsling  <ross.kirsling@sony.com>
 
         JSC test stress/jsc-read.js doesn't support CRLF
diff --git a/JSTests/stress/big-int-bitwise-and-general.js b/JSTests/stress/big-int-bitwise-and-general.js
new file mode 100644 (file)
index 0000000..56e798e
--- /dev/null
@@ -0,0 +1,97 @@
+//@ runBigIntEnabled
+
+// Copyright (C) 2017 Josh Wolfe. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+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(0b00n & 0b00n, 0b00n, "0b00n & 0b00n === 0b00n");
+assert.sameValue(0b00n & 0b01n, 0b00n, "0b00n & 0b01n === 0b00n");
+assert.sameValue(0b01n & 0b00n, 0b00n, "0b01n & 0b00n === 0b00n");
+assert.sameValue(0b00n & 0b10n, 0b00n, "0b00n & 0b10n === 0b00n");
+assert.sameValue(0b10n & 0b00n, 0b00n, "0b10n & 0b00n === 0b00n");
+assert.sameValue(0b00n & 0b11n, 0b00n, "0b00n & 0b11n === 0b00n");
+assert.sameValue(0b11n & 0b00n, 0b00n, "0b11n & 0b00n === 0b00n");
+assert.sameValue(0b01n & 0b01n, 0b01n, "0b01n & 0b01n === 0b01n");
+assert.sameValue(0b01n & 0b10n, 0b00n, "0b01n & 0b10n === 0b00n");
+assert.sameValue(0b10n & 0b01n, 0b00n, "0b10n & 0b01n === 0b00n");
+assert.sameValue(0b01n & 0b11n, 0b01n, "0b01n & 0b11n === 0b01n");
+assert.sameValue(0b11n & 0b01n, 0b01n, "0b11n & 0b01n === 0b01n");
+assert.sameValue(0b10n & 0b10n, 0b10n, "0b10n & 0b10n === 0b10n");
+assert.sameValue(0b10n & 0b11n, 0b10n, "0b10n & 0b11n === 0b10n");
+assert.sameValue(0b11n & 0b10n, 0b10n, "0b11n & 0b10n === 0b10n");
+assert.sameValue(0xffffffffn & 0n, 0n, "0xffffffffn & 0n === 0n");
+assert.sameValue(0n & 0xffffffffn, 0n, "0n & 0xffffffffn === 0n");
+assert.sameValue(0xffffffffn & 0xffffffffn, 0xffffffffn, "0xffffffffn & 0xffffffffn === 0xffffffffn");
+assert.sameValue(0xffffffffffffffffn & 0n, 0n, "0xffffffffffffffffn & 0n === 0n");
+assert.sameValue(0n & 0xffffffffffffffffn, 0n, "0n & 0xffffffffffffffffn === 0n");
+assert.sameValue(0xffffffffffffffffn & 0xffffffffn, 0xffffffffn, "0xffffffffffffffffn & 0xffffffffn === 0xffffffffn");
+assert.sameValue(0xffffffffn & 0xffffffffffffffffn, 0xffffffffn, "0xffffffffn & 0xffffffffffffffffn === 0xffffffffn");
+assert.sameValue(
+  0xffffffffffffffffn & 0xffffffffffffffffn, 0xffffffffffffffffn,
+  "0xffffffffffffffffn & 0xffffffffffffffffn === 0xffffffffffffffffn");
+assert.sameValue(
+  0xbf2ed51ff75d380fd3be813ec6185780n & 0x4aabef2324cedff5387f1f65n, 0x42092803008e813400181700n,
+  "0xbf2ed51ff75d380fd3be813ec6185780n & 0x4aabef2324cedff5387f1f65n === 0x42092803008e813400181700n");
+assert.sameValue(
+  0x4aabef2324cedff5387f1f65n & 0xbf2ed51ff75d380fd3be813ec6185780n, 0x42092803008e813400181700n,
+  "0x4aabef2324cedff5387f1f65n & 0xbf2ed51ff75d380fd3be813ec6185780n === 0x42092803008e813400181700n");
+assert.sameValue(0n & -1n, 0n, "0n & -1n === 0n");
+assert.sameValue(-1n & 0n, 0n, "-1n & 0n === 0n");
+assert.sameValue(0n & -2n, 0n, "0n & -2n === 0n");
+assert.sameValue(-2n & 0n, 0n, "-2n & 0n === 0n");
+assert.sameValue(1n & -2n, 0n, "1n & -2n === 0n");
+assert.sameValue(-2n & 1n, 0n, "-2n & 1n === 0n");
+assert.sameValue(2n & -2n, 2n, "2n & -2n === 2n");
+assert.sameValue(-2n & 2n, 2n, "-2n & 2n === 2n");
+assert.sameValue(2n & -3n, 0n, "2n & -3n === 0n");
+assert.sameValue(-3n & 2n, 0n, "-3n & 2n === 0n");
+assert.sameValue(-1n & -2n, -2n, "-1n & -2n === -2n");
+assert.sameValue(-2n & -1n, -2n, "-2n & -1n === -2n");
+assert.sameValue(-2n & -2n, -2n, "-2n & -2n === -2n");
+assert.sameValue(-2n & -3n, -4n, "-2n & -3n === -4n");
+assert.sameValue(-3n & -2n, -4n, "-3n & -2n === -4n");
+assert.sameValue(0xffffffffn & -1n, 0xffffffffn, "0xffffffffn & -1n === 0xffffffffn");
+assert.sameValue(-1n & 0xffffffffn, 0xffffffffn, "-1n & 0xffffffffn === 0xffffffffn");
+assert.sameValue(0xffffffffffffffffn & -1n, 0xffffffffffffffffn, "0xffffffffffffffffn & -1n === 0xffffffffffffffffn");
+assert.sameValue(-1n & 0xffffffffffffffffn, 0xffffffffffffffffn, "-1n & 0xffffffffffffffffn === 0xffffffffffffffffn");
+assert.sameValue(
+  0xbf2ed51ff75d380fd3be813ec6185780n & -0x4aabef2324cedff5387f1f65n, 0xbf2ed51fb554100cd330000ac6004080n,
+  "0xbf2ed51ff75d380fd3be813ec6185780n & -0x4aabef2324cedff5387f1f65n === 0xbf2ed51fb554100cd330000ac6004080n");
+assert.sameValue(
+  -0x4aabef2324cedff5387f1f65n & 0xbf2ed51ff75d380fd3be813ec6185780n, 0xbf2ed51fb554100cd330000ac6004080n,
+  "-0x4aabef2324cedff5387f1f65n & 0xbf2ed51ff75d380fd3be813ec6185780n === 0xbf2ed51fb554100cd330000ac6004080n");
+assert.sameValue(
+  -0xbf2ed51ff75d380fd3be813ec6185780n & 0x4aabef2324cedff5387f1f65n, 0x8a2c72024405ec138670800n,
+  "-0xbf2ed51ff75d380fd3be813ec6185780n & 0x4aabef2324cedff5387f1f65n === 0x8a2c72024405ec138670800n");
+assert.sameValue(
+  0x4aabef2324cedff5387f1f65n & -0xbf2ed51ff75d380fd3be813ec6185780n, 0x8a2c72024405ec138670800n,
+  "0x4aabef2324cedff5387f1f65n & -0xbf2ed51ff75d380fd3be813ec6185780n === 0x8a2c72024405ec138670800n");
+assert.sameValue(
+  -0xbf2ed51ff75d380fd3be813ec6185780n & -0x4aabef2324cedff5387f1f65n, -0xbf2ed51fffffff2ff7fedffffe7f5f80n,
+  "-0xbf2ed51ff75d380fd3be813ec6185780n & -0x4aabef2324cedff5387f1f65n === -0xbf2ed51fffffff2ff7fedffffe7f5f80n");
+assert.sameValue(
+  -0x4aabef2324cedff5387f1f65n & -0xbf2ed51ff75d380fd3be813ec6185780n, -0xbf2ed51fffffff2ff7fedffffe7f5f80n,
+  "-0x4aabef2324cedff5387f1f65n & -0xbf2ed51ff75d380fd3be813ec6185780n === -0xbf2ed51fffffff2ff7fedffffe7f5f80n");
+assert.sameValue(-0xffffffffn & 0n, 0n, "-0xffffffffn & 0n === 0n");
+assert.sameValue(0n & -0xffffffffn, 0n, "0n & -0xffffffffn === 0n");
+assert.sameValue(
+  -0xffffffffffffffffn & 0x10000000000000000n, 0x10000000000000000n,
+  "-0xffffffffffffffffn & 0x10000000000000000n === 0x10000000000000000n");
+assert.sameValue(
+  0x10000000000000000n & -0xffffffffffffffffn, 0x10000000000000000n,
+  "0x10000000000000000n & -0xffffffffffffffffn === 0x10000000000000000n");
+assert.sameValue(
+  -0xffffffffffffffffffffffffn & 0x10000000000000000n, 0n,
+  "-0xffffffffffffffffffffffffn & 0x10000000000000000n === 0n");
+assert.sameValue(
+  0x10000000000000000n & -0xffffffffffffffffffffffffn, 0n,
+  "0x10000000000000000n & -0xffffffffffffffffffffffffn === 0n");
diff --git a/JSTests/stress/big-int-bitwise-and-jit.js b/JSTests/stress/big-int-bitwise-and-jit.js
new file mode 100644 (file)
index 0000000..ee766af
--- /dev/null
@@ -0,0 +1,20 @@
+//@ runBigIntEnabled
+
+let assert = {
+    sameValue: function(i, e, m) {
+        if (i !== e)
+            throw new Error(m);
+    }
+}
+
+function bigIntBitAnd(a, b) {
+    return (a & b) & (a & 0b11n);
+
+}
+noInline(bigIntBitAnd);
+
+for (let i = 0; i < 10000; i++) {
+    let r = bigIntBitAnd(0b11n, 0b1010n);
+    assert.sameValue(r, 0b10n, 0b11n + " & " + 0b1010n + " = " + r);
+}
+
diff --git a/JSTests/stress/big-int-bitwise-and-memory-stress.js b/JSTests/stress/big-int-bitwise-and-memory-stress.js
new file mode 100644 (file)
index 0000000..61e7ae3
--- /dev/null
@@ -0,0 +1,14 @@
+//@ runBigIntEnabled
+
+function assert(a) {
+    if (!a)
+        throw new Error("Bad assertion");
+}
+
+let a = 0b11n;
+for (let i = 0; i < 1000000; i++) {
+    a &= 0b01n;
+}
+
+assert(a === 0b01n);
+
diff --git a/JSTests/stress/big-int-bitwise-and-to-primitive-precedence.js b/JSTests/stress/big-int-bitwise-and-to-primitive-precedence.js
new file mode 100644 (file)
index 0000000..bfa8e16
--- /dev/null
@@ -0,0 +1,29 @@
+//@ runBigIntEnabled
+
+assert = {
+    sameValue: function (input, expected, message) {
+        if (input !== expected)
+            throw new Error(message);
+    }
+};
+
+let o = {
+    [Symbol.toPrimitive]: function() {
+        throw new Error("Bad");
+    }
+};
+
+try{
+    o & Symbol("2");
+    assert.sameValue(true, false, "Exception expected to be throwed, but executed without error");
+} catch (e) {
+    assert.sameValue(e.message, "Bad", "Expected to throw Error('Bad'), but got: " + e);
+}
+
+try{
+    Symbol("2") & o;
+    assert.sameValue(true, false, "Exception expected to be throwed, but executed without error");
+} catch (e) {
+    assert.sameValue(e instanceof TypeError, true, "Expected to throw TypeError, but got: " + e)
+}
+
diff --git a/JSTests/stress/big-int-bitwise-and-type-error.js b/JSTests/stress/big-int-bitwise-and-type-error.js
new file mode 100644 (file)
index 0000000..dcb7768
--- /dev/null
@@ -0,0 +1,106 @@
+//@ runBigIntEnabled
+
+function assert(a, message) {
+    if (!a)
+        throw new Error(message);
+}
+
+function assertThrowTypeError(a, b, message) {
+    try {
+        let n = a & b;
+        assert(false, message + ": Should throw TypeError, but executed without exception");
+    } catch (e) {
+        assert(e instanceof TypeError, message + ": expected TypeError, got: " + e);
+    }
+}
+
+assertThrowTypeError(30n, "foo", "BigInt & String");
+assertThrowTypeError("bar", 18757382984821n, "String & BigInt");
+assertThrowTypeError(30n, Symbol("foo"), "BigInt & Symbol");
+assertThrowTypeError(Symbol("bar"), 18757382984821n, "Symbol & BigInt");
+assertThrowTypeError(30n, 3320, "BigInt & Int32");
+assertThrowTypeError(33256, 18757382984821n, "Int32 & BigInt");
+assertThrowTypeError(30n, 0.543, "BigInt & Double");
+assertThrowTypeError(230.19293, 18757382984821n, "Double & BigInt");
+assertThrowTypeError(30n, NaN, "BigInt & NaN");
+assertThrowTypeError(NaN, 18757382984821n, "NaN & BigInt");
+assertThrowTypeError(30n, NaN, "BigInt & NaN");
+assertThrowTypeError(NaN, 18757382984821n, "NaN & BigInt");
+assertThrowTypeError(30n, +Infinity, "BigInt & NaN");
+assertThrowTypeError(+Infinity, 18757382984821n, "NaN & BigInt");
+assertThrowTypeError(30n, -Infinity, "BigInt & -Infinity");
+assertThrowTypeError(-Infinity, 18757382984821n, "-Infinity & BigInt");
+assertThrowTypeError(30n, null, "BigInt & null");
+assertThrowTypeError(null, 18757382984821n, "null & BigInt");
+assertThrowTypeError(30n, undefined, "BigInt & undefined");
+assertThrowTypeError(undefined, 18757382984821n, "undefined & BigInt");
+assertThrowTypeError(30n, true, "BigInt & true");
+assertThrowTypeError(true, 18757382984821n, "true & BigInt");
+assertThrowTypeError(30n, false, "BigInt & false");
+assertThrowTypeError(false, 18757382984821n, "false & BigInt");
+
+// Error when returning from object
+
+let o = {
+    valueOf: function () { return Symbol("Foo"); }
+};
+
+assertThrowTypeError(30n, o, "BigInt & Object.valueOf returning Symbol");
+assertThrowTypeError(o, 18757382984821n, "Object.valueOf returning Symbol & BigInt");
+
+o = {
+    valueOf: function () { return 33256; }
+};
+
+assertThrowTypeError(30n, o, "BigInt & Object.valueOf returning Int32");
+assertThrowTypeError(o, 18757382984821n, "Object.valueOf returning Int32 & BigInt");
+
+o = {
+    valueOf: function () { return 0.453; }
+};
+
+assertThrowTypeError(30n, o, "BigInt & Object.valueOf returning Double");
+assertThrowTypeError(o, 18757382984821n, "Object.valueOf returning Double & BigInt");
+
+o = {
+    toString: function () { return Symbol("Foo"); }
+};
+
+assertThrowTypeError(30n, o, "BigInt & Object.toString returning Symbol");
+assertThrowTypeError(o, 18757382984821n, "Object.toString returning Symbol & BigInt");
+
+o = {
+    toString: function () { return 33256; }
+};
+
+assertThrowTypeError(30n, o, "BigInt & Object.toString returning Int32");
+assertThrowTypeError(o, 18757382984821n, "Object.toString returning Int32 & BigInt");
+
+o = {
+    toString: function () { return 0.453; }
+};
+
+assertThrowTypeError(30n, o, "BigInt & Object.toString returning Double");
+assertThrowTypeError(o, 18757382984821n, "Object.toString returning Double & BigInt");
+
+o = {
+    [Symbol.toPrimitive]: function () { return Symbol("Foo"); }
+};
+
+assertThrowTypeError(30n, o, "BigInt & Object.@@toPrimitive returning Symbol");
+assertThrowTypeError(o, 18757382984821n, "Object.@@toPrimitive returning Symbol & BigInt");
+
+o = {
+    [Symbol.toPrimitive]: function () { return 33256; }
+};
+
+assertThrowTypeError(30n, o, "BigInt & Object.@@toPrimitive returning Int32");
+assertThrowTypeError(o, 18757382984821n, "Object.@@toPrimitive returning Int32 & BigInt");
+
+o = {
+    [Symbol.toPrimitive]: function () { return 0.453; }
+};
+
+assertThrowTypeError(30n, o, "BigInt & Object.@@toPrimitive returning Double");
+assertThrowTypeError(o, 18757382984821n, "Object.@@toPrimitive returning Double & BigInt");
+
diff --git a/JSTests/stress/big-int-bitwise-and-wrapped-value.js b/JSTests/stress/big-int-bitwise-and-wrapped-value.js
new file mode 100644 (file)
index 0000000..c4377f4
--- /dev/null
@@ -0,0 +1,37 @@
+//@ runBigIntEnabled
+
+assert = {
+    sameValue: function (input, expected, message) {
+        if (input !== expected)
+            throw new Error(message);
+    }
+};
+
+function testBitAnd(x, y, z, message) {
+    assert.sameValue(x & y, z, message);
+    assert.sameValue(y & x, z, message);
+}
+
+testBitAnd(Object(0b10n), 0b01n, 0b00n, "ToPrimitive: unbox object with internal slot");
+
+let o = {
+    [Symbol.toPrimitive]: function() {
+        return 0b10n;
+    }
+};
+testBitAnd(o, 0b01n, 0b00n, "ToPrimitive: @@toPrimitive");
+
+o = {
+    valueOf: function() {
+        return 0b10n;
+    }
+};
+testBitAnd(o, 0b01n, 0b00n, "ToPrimitive: valueOf");
+
+o = {
+    toString: function() {
+        return 0b10n;
+    }
+}
+testBitAnd(o, 0b01n, 0b00n, "ToPrimitive: toString");
+
index a1d7b16..2c270be 100644 (file)
@@ -1,3 +1,84 @@
+2018-09-28  Caio Lima  <ticaiolima@gmail.com>
+
+        [ESNext][BigInt] Implement support for "&"
+        https://bugs.webkit.org/show_bug.cgi?id=186228
+
+        Reviewed by Yusuke Suzuki.
+
+        This patch introduces support of BigInt into bitwise "&" operation.
+        We are also introducing the ValueBitAnd DFG node, that is responsible
+        to take care of JIT for non-Int32 operands. With the introduction of this
+        new node, we renamed the BitAnd node to ArithBitAnd. The ArithBitAnd
+        follows the behavior of ArithAdd and other arithmetic nodes, where
+        the Arith<op> version always results in Number (in the case of
+        ArithBitAnd, its is always an Int32).
+
+        * bytecode/CodeBlock.cpp:
+        (JSC::CodeBlock::finishCreation):
+        * bytecompiler/BytecodeGenerator.cpp:
+        (JSC::BytecodeGenerator::emitBinaryOp):
+        * dfg/DFGAbstractInterpreterInlines.h:
+        (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+        * dfg/DFGBackwardsPropagationPhase.cpp:
+        (JSC::DFG::BackwardsPropagationPhase::isWithinPowerOfTwo):
+        (JSC::DFG::BackwardsPropagationPhase::propagate):
+        * dfg/DFGByteCodeParser.cpp:
+        (JSC::DFG::ByteCodeParser::parseBlock):
+        * dfg/DFGClobberize.h:
+        (JSC::DFG::clobberize):
+        * dfg/DFGDoesGC.cpp:
+        (JSC::DFG::doesGC):
+        * dfg/DFGFixupPhase.cpp:
+        (JSC::DFG::FixupPhase::fixupNode):
+        * dfg/DFGNodeType.h:
+        * dfg/DFGOperations.cpp:
+        * dfg/DFGOperations.h:
+        * dfg/DFGPredictionPropagationPhase.cpp:
+        * dfg/DFGSafeToExecute.h:
+        (JSC::DFG::safeToExecute):
+        * dfg/DFGSpeculativeJIT.cpp:
+        (JSC::DFG::SpeculativeJIT::compileValueBitwiseOp):
+        (JSC::DFG::SpeculativeJIT::compileBitwiseOp):
+        * dfg/DFGSpeculativeJIT.h:
+        (JSC::DFG::SpeculativeJIT::bitOp):
+        * dfg/DFGSpeculativeJIT32_64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGSpeculativeJIT64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGStrengthReductionPhase.cpp:
+        (JSC::DFG::StrengthReductionPhase::handleNode):
+        * ftl/FTLCapabilities.cpp:
+        (JSC::FTL::canCompile):
+        * ftl/FTLLowerDFGToB3.cpp:
+        (JSC::FTL::DFG::LowerDFGToB3::compileNode):
+        (JSC::FTL::DFG::LowerDFGToB3::compileValueBitAnd):
+        (JSC::FTL::DFG::LowerDFGToB3::compileArithBitAnd):
+        (JSC::FTL::DFG::LowerDFGToB3::compileBitAnd): Deleted.
+        * jit/JIT.h:
+        * jit/JITArithmetic.cpp:
+        (JSC::JIT::emitBitBinaryOpFastPath):
+        (JSC::JIT::emit_op_bitand):
+        * llint/LowLevelInterpreter32_64.asm:
+        * llint/LowLevelInterpreter64.asm:
+        * runtime/CommonSlowPaths.cpp:
+        (JSC::SLOW_PATH_DECL):
+        * runtime/JSBigInt.cpp:
+        (JSC::JSBigInt::JSBigInt):
+        (JSC::JSBigInt::initialize):
+        (JSC::JSBigInt::createZero):
+        (JSC::JSBigInt::createFrom):
+        (JSC::JSBigInt::bitwiseAnd):
+        (JSC::JSBigInt::absoluteBitwiseOp):
+        (JSC::JSBigInt::absoluteAnd):
+        (JSC::JSBigInt::absoluteOr):
+        (JSC::JSBigInt::absoluteAndNot):
+        (JSC::JSBigInt::absoluteAddOne):
+        (JSC::JSBigInt::absoluteSubOne):
+        * runtime/JSBigInt.h:
+        * runtime/JSCJSValue.h:
+        * runtime/JSCJSValueInlines.h:
+        (JSC::JSValue::toBigIntOrInt32 const):
+
 2018-09-28  Mark Lam  <mark.lam@apple.com>
 
         Gardening: speculative build fix.
index 77d728d..29a1859 100644 (file)
@@ -581,6 +581,7 @@ bool CodeBlock::finishCreation(VM& vm, ScriptExecutable* ownerExecutable, Unlink
             break;
         }
 
+        case op_bitand:
         case op_to_this: {
             linkValueProfile(i, opLength);
             break;
index 4b88a1b..72692e1 100644 (file)
@@ -1732,12 +1732,22 @@ RegisterID* BytecodeGenerator::emitDec(RegisterID* srcDst)
 
 RegisterID* BytecodeGenerator::emitBinaryOp(OpcodeID opcodeID, RegisterID* dst, RegisterID* src1, RegisterID* src2, OperandTypes types)
 {
+
+    if (opcodeID == op_bitand) {
+        UnlinkedValueProfile profile = emitProfiledOpcode(opcodeID);
+        instructions().append(dst->index());
+        instructions().append(src1->index());
+        instructions().append(src2->index());
+        instructions().append(profile);
+        return dst;
+    }
+
     emitOpcode(opcodeID);
     instructions().append(dst->index());
     instructions().append(src1->index());
     instructions().append(src2->index());
 
-    if (opcodeID == op_bitor || opcodeID == op_bitand || opcodeID == op_bitxor ||
+    if (opcodeID == op_bitor || opcodeID == op_bitxor ||
         opcodeID == op_add || opcodeID == op_mul || opcodeID == op_sub || opcodeID == op_div)
         instructions().append(ArithProfile(types.first(), types.second()).bits());
 
index f845c58..dc25bbf 100644 (file)
@@ -375,7 +375,12 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
         break;
     }
             
-    case BitAnd:
+    case ValueBitAnd:
+        clobberWorld();
+        setTypeForNode(node, SpecBoolInt32 | SpecBigInt);
+        break;
+            
+    case ArithBitAnd:
     case BitOr:
     case BitXor:
     case BitRShift:
@@ -393,7 +398,7 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
             int32_t a = left.asInt32();
             int32_t b = right.asInt32();
             switch (node->op()) {
-            case BitAnd:
+            case ArithBitAnd:
                 setConstant(node, JSValue(a & b));
                 break;
             case BitOr:
@@ -418,7 +423,7 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
             break;
         }
         
-        if (node->op() == BitAnd
+        if (node->op() == ArithBitAnd
             && (isBoolInt32Speculation(forNode(node->child1()).m_type) ||
                 isBoolInt32Speculation(forNode(node->child2()).m_type))) {
             setNonCellTypeForNode(node, SpecBoolInt32);
index 058aacf..13e9518 100644 (file)
@@ -110,7 +110,7 @@ private:
             return isWithinPowerOfTwoForConstant<power>(node);
         }
             
-        case BitAnd: {
+        case ArithBitAnd: {
             if (power > 31)
                 return true;
             
@@ -207,7 +207,7 @@ private:
         case CheckVarargs:
             break;
             
-        case BitAnd:
+        case ArithBitAnd:
         case BitOr:
         case BitXor:
         case BitRShift:
index f106149..b7b1200 100644 (file)
@@ -4845,7 +4845,11 @@ void ByteCodeParser::parseBlock(unsigned limit)
         case op_bitand: {
             Node* op1 = get(VirtualRegister(currentInstruction[2].u.operand));
             Node* op2 = get(VirtualRegister(currentInstruction[3].u.operand));
-            set(VirtualRegister(currentInstruction[1].u.operand), addToGraph(BitAnd, op1, op2));
+            if (isInt32Speculation(getPrediction()))
+                set(VirtualRegister(currentInstruction[1].u.operand), addToGraph(ArithBitAnd, op1, op2));
+            else
+                set(VirtualRegister(currentInstruction[1].u.operand), addToGraph(ValueBitAnd, op1, op2));
+
             NEXT_OPCODE(op_bitand);
         }
 
index 4fd9db6..70ff3bf 100644 (file)
@@ -262,7 +262,7 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu
         def(PureValue(node, node->queriedType()));
         return;
 
-    case BitAnd:
+    case ArithBitAnd:
     case BitOr:
     case BitXor:
     case BitLShift:
@@ -635,6 +635,7 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu
     case InByVal:
     case InById:
     case HasOwnProperty:
+    case ValueBitAnd:
     case ValueNegate:
     case ValueAdd:
     case SetFunctionName:
index cb167f0..13d4c96 100644 (file)
@@ -67,7 +67,7 @@ bool doesGC(Graph& graph, Node* node)
     case Flush:
     case PhantomLocal:
     case SetArgument:
-    case BitAnd:
+    case ArithBitAnd:
     case BitOr:
     case BitXor:
     case BitLShift:
@@ -96,6 +96,7 @@ bool doesGC(Graph& graph, Node* node)
     case ArithTrunc:
     case ArithFRound:
     case ArithUnary:
+    case ValueBitAnd:
     case ValueAdd:
     case ValueNegate:
     case TryGetById:
index 7c72f03..0269024 100644 (file)
@@ -99,7 +99,30 @@ private:
             return;
         }
             
-        case BitAnd:
+        case ValueBitAnd: {
+            if (Node::shouldSpeculateBigInt(node->child1().node(), node->child2().node())) {
+                fixEdge<BigIntUse>(node->child1());
+                fixEdge<BigIntUse>(node->child2());
+                break;
+            }
+
+            fixEdge<UntypedUse>(node->child1());
+            fixEdge<UntypedUse>(node->child2());
+            break;
+        }
+    
+        case ArithBitAnd: {
+            if (Node::shouldSpeculateUntypedForBitOps(node->child1().node(), node->child2().node())) {
+                fixEdge<UntypedUse>(node->child1());
+                fixEdge<UntypedUse>(node->child2());
+                node->setOpAndDefaultFlags(ValueBitAnd);
+                break;
+            }
+            fixIntConvertingEdge(node->child1());
+            fixIntConvertingEdge(node->child2());
+            break;
+        }
+
         case BitOr:
         case BitXor:
         case BitRShift:
index d30c925..2495597 100644 (file)
@@ -111,7 +111,8 @@ namespace JSC { namespace DFG {
     macro(InvalidationPoint, NodeMustGenerate) \
     \
     /* Nodes for bitwise operations. */\
-    macro(BitAnd, NodeResultInt32) \
+    macro(ValueBitAnd, NodeResultJS | NodeMustGenerate) \
+    macro(ArithBitAnd, NodeResultInt32) \
     macro(BitOr, NodeResultInt32) \
     macro(BitXor, NodeResultInt32) \
     macro(BitLShift, NodeResultInt32) \
index 2c011e8..27b8f29 100644 (file)
@@ -49,6 +49,7 @@
 #include "JIT.h"
 #include "JITExceptions.h"
 #include "JSArrayInlines.h"
+#include "JSBigInt.h"
 #include "JSCInlines.h"
 #include "JSFixedArray.h"
 #include "JSGenericTypedArrayViewConstructorInlines.h"
@@ -346,11 +347,22 @@ EncodedJSValue JIT_OPERATION operationValueBitAnd(ExecState* exec, EncodedJSValu
     JSValue op1 = JSValue::decode(encodedOp1);
     JSValue op2 = JSValue::decode(encodedOp2);
 
-    int32_t a = op1.toInt32(exec);
+    auto leftNumeric = op1.toBigIntOrInt32(exec);
     RETURN_IF_EXCEPTION(scope, encodedJSValue());
-    scope.release();
-    int32_t b = op2.toInt32(exec);
-    return JSValue::encode(jsNumber(a & b));
+    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::bitwiseAnd(*vm, 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)));
 }
 
 EncodedJSValue JIT_OPERATION operationValueBitOr(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)
@@ -470,7 +482,7 @@ EncodedJSValue JIT_OPERATION operationValueDiv(ExecState* exec, EncodedJSValue e
             return JSValue::encode(result);
         }
 
-        return throwVMTypeError(exec, scope, "Invalid operand in BigInt operation.");
+        return throwVMTypeError(exec, scope, "Invalid mix of BigInt and other type in division operation.");
     }
 
     scope.release();
@@ -1280,6 +1292,17 @@ size_t JIT_OPERATION operationRegExpTestGeneric(ExecState* exec, JSGlobalObject*
     return regexp->test(exec, globalObject, input);
 }
 
+JSCell* JIT_OPERATION operationBitAndBigInt(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::bitwiseAnd(*vm, leftOperand, rightOperand);
+}
+
 size_t JIT_OPERATION operationCompareStrictEqCell(ExecState* exec, JSCell* op1, JSCell* op2)
 {
     VM* vm = &exec->vm();
index 6cb515c..d069def 100644 (file)
@@ -163,6 +163,7 @@ size_t JIT_OPERATION operationRegExpTestString(ExecState*, JSGlobalObject*, RegE
 size_t JIT_OPERATION operationRegExpTest(ExecState*, JSGlobalObject*, RegExpObject*, EncodedJSValue) WTF_INTERNAL;
 size_t JIT_OPERATION operationRegExpTestGeneric(ExecState*, JSGlobalObject*, EncodedJSValue, EncodedJSValue) WTF_INTERNAL;
 size_t JIT_OPERATION operationCompareStrictEqCell(ExecState*, JSCell* op1, JSCell* op2) WTF_INTERNAL;
+JSCell* JIT_OPERATION operationBitAndBigInt(ExecState*, JSCell* op1, JSCell* op2) WTF_INTERNAL;
 size_t JIT_OPERATION operationSameValue(ExecState*, EncodedJSValue, EncodedJSValue) WTF_INTERNAL;
 JSCell* JIT_OPERATION operationCreateActivationDirect(ExecState*, Structure*, JSScope*, SymbolTable*, EncodedJSValue);
 JSCell* JIT_OPERATION operationCreateDirectArguments(ExecState*, Structure*, uint32_t length, uint32_t minCapacity);
index f37fd06..8d2f7ab 100644 (file)
@@ -246,6 +246,12 @@ private:
             break;
         }
 
+        case ValueBitAnd: {
+            if (node->child1()->shouldSpeculateBigInt() && node->child2()->shouldSpeculateBigInt())
+                changed |= mergePrediction(SpecBigInt);
+            break;
+        }
+
         case ValueNegate:
         case ArithNegate: {
             SpeculatedType prediction = node->child1()->prediction();
@@ -700,7 +706,8 @@ private:
             setPrediction(type);
             break;
         }
-        case BitAnd:
+
+        case ArithBitAnd:
         case BitOr:
         case BitXor:
         case BitRShift:
@@ -1056,6 +1063,7 @@ private:
         case GetLocal:
         case SetLocal:
         case UInt32ToNumber:
+        case ValueBitAnd:
         case ValueNegate:
         case ValueAdd:
         case ArithAdd:
index 60801d0..aeca211 100644 (file)
@@ -197,7 +197,7 @@ bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node, bool igno
     case Flush:
     case PhantomLocal:
     case SetArgument:
-    case BitAnd:
+    case ArithBitAnd:
     case BitOr:
     case BitXor:
     case BitLShift:
@@ -226,6 +226,7 @@ bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node, bool igno
     case ArithCeil:
     case ArithTrunc:
     case ArithUnary:
+    case ValueBitAnd:
     case ValueNegate:
     case ValueAdd:
     case TryGetById:
index 4b78d11..9e028c5 100644 (file)
@@ -3590,7 +3590,7 @@ void SpeculativeJIT::emitUntypedBitOp(Node* node)
     jsValueResult(resultRegs, node);
 }
 
-void SpeculativeJIT::compileBitwiseOp(Node* node)
+void SpeculativeJIT::compileValueBitwiseOp(Node* node)
 {
     NodeType op = node->op();
     Edge& leftChild = node->child1();
@@ -3598,9 +3598,40 @@ void SpeculativeJIT::compileBitwiseOp(Node* node)
 
     if (leftChild.useKind() == UntypedUse || rightChild.useKind() == UntypedUse) {
         switch (op) {
-        case BitAnd:
+        case ValueBitAnd:
             emitUntypedBitOp<JITBitAndGenerator, operationValueBitAnd>(node);
             return;
+        default:
+            RELEASE_ASSERT_NOT_REACHED();
+        }
+    }
+    
+    ASSERT(leftChild.useKind() == BigIntUse && rightChild.useKind() == BigIntUse);
+    
+    SpeculateCellOperand left(this, node->child1());
+    SpeculateCellOperand right(this, node->child2());
+    GPRReg leftGPR = left.gpr();
+    GPRReg rightGPR = right.gpr();
+
+    speculateBigInt(leftChild, leftGPR);
+    speculateBigInt(rightChild, rightGPR);
+
+    flushRegisters();
+    GPRFlushedCallResult result(this);
+    GPRReg resultGPR = result.gpr();
+    callOperation(operationBitAndBigInt, resultGPR, leftGPR, rightGPR);
+    m_jit.exceptionCheck();
+    cellResult(resultGPR, node);
+}
+
+void SpeculativeJIT::compileBitwiseOp(Node* node)
+{
+    NodeType op = node->op();
+    Edge& leftChild = node->child1();
+    Edge& rightChild = node->child2();
+
+    if (leftChild.useKind() == UntypedUse || rightChild.useKind() == UntypedUse) {
+        switch (op) {
         case BitOr:
             emitUntypedBitOp<JITBitOrGenerator, operationValueBitOr>(node);
             return;
index e5c30af..98afb6e 100644 (file)
@@ -633,7 +633,7 @@ public:
     void bitOp(NodeType op, int32_t imm, GPRReg op1, GPRReg result)
     {
         switch (op) {
-        case BitAnd:
+        case ArithBitAnd:
             m_jit.and32(Imm32(imm), op1, result);
             break;
         case BitOr:
@@ -649,7 +649,7 @@ public:
     void bitOp(NodeType op, GPRReg op1, GPRReg op2, GPRReg result)
     {
         switch (op) {
-        case BitAnd:
+        case ArithBitAnd:
             m_jit.and32(op1, op2, result);
             break;
         case BitOr:
@@ -1332,6 +1332,7 @@ public:
     template<typename SnippetGenerator, J_JITOperation_EJJ slowPathFunction>
     void emitUntypedBitOp(Node*);
     void compileBitwiseOp(Node*);
+    void compileValueBitwiseOp(Node*);
 
     void emitUntypedRightShiftBitOp(Node*);
     void compileShiftOp(Node*);
index 166e4a4..89c3517 100644 (file)
@@ -1979,7 +1979,11 @@ void SpeculativeJIT::compile(Node* node)
         recordSetLocal(dataFormatFor(node->variableAccessData()->flushFormat()));
         break;
 
-    case BitAnd:
+    case ValueBitAnd:
+        compileValueBitwiseOp(node);
+        break;
+
+    case ArithBitAnd:
     case BitOr:
     case BitXor:
         compileBitwiseOp(node);
index c708c9c..33a6540 100644 (file)
@@ -2074,7 +2074,11 @@ void SpeculativeJIT::compile(Node* node)
         recordSetLocal(dataFormatFor(node->variableAccessData()->flushFormat()));
         break;
 
-    case BitAnd:
+    case ValueBitAnd:
+        compileValueBitwiseOp(node);
+        break;
+
+    case ArithBitAnd:
     case BitOr:
     case BitXor:
         compileBitwiseOp(node);
index 3aaac3e..86ade8c 100644 (file)
@@ -88,7 +88,7 @@ private:
             break;
             
         case BitXor:
-        case BitAnd:
+        case ArithBitAnd:
             handleCommutativity();
             break;
             
index 3e4afad..adb28cf 100644 (file)
@@ -58,7 +58,7 @@ inline CapabilityLevel canCompile(Node* node)
     case PhantomLocal:
     case SetArgument:
     case Return:
-    case BitAnd:
+    case ArithBitAnd:
     case BitOr:
     case BitXor:
     case BitRShift:
@@ -86,6 +86,7 @@ inline CapabilityLevel canCompile(Node* node)
     case GetGlobalVar:
     case GetGlobalLexicalVariable:
     case PutGlobalVariable:
+    case ValueBitAnd:
     case ValueNegate:
     case ValueAdd:
     case StrCat:
index 707bd77..f849ffa 100644 (file)
@@ -649,8 +649,11 @@ private:
         case ArithUnary:
             compileArithUnary();
             break;
-        case DFG::BitAnd:
-            compileBitAnd();
+        case ValueBitAnd:
+            compileValueBitAnd();
+            break;
+        case ArithBitAnd:
+            compileArithBitAnd();
             break;
         case DFG::BitOr:
             compileBitOr();
@@ -2806,12 +2809,22 @@ private:
         }
     }
     
-    void compileBitAnd()
+    void compileValueBitAnd()
     {
-        if (m_node->isBinaryUseKind(UntypedUse)) {
-            emitBinaryBitOpSnippet<JITBitAndGenerator>(operationValueBitAnd);
+        if (m_node->isBinaryUseKind(BigIntUse)) {
+            LValue left = lowBigInt(m_node->child1());
+            LValue right = lowBigInt(m_node->child2());
+            
+            LValue result = vmCall(pointerType(), m_out.operation(operationBitAndBigInt), m_callFrame, left, right);
+            setJSValue(result);
             return;
         }
+        
+        emitBinaryBitOpSnippet<JITBitAndGenerator>(operationValueBitAnd);
+    }
+    
+    void compileArithBitAnd()
+    {
         setInt32(m_out.bitAnd(lowInt32(m_node->child1()), lowInt32(m_node->child2())));
     }
     
index e92bdf8..b8b8acc 100644 (file)
@@ -793,8 +793,13 @@ namespace JSC {
             return appendCallWithCallFrameRollbackOnException(operation);
         }
 
+        enum class ProfilingPolicy {
+            ShouldEmitProfiling,
+            NoProfiling
+        };
+
         template<typename SnippetGenerator>
-        void emitBitBinaryOpFastPath(Instruction* currentInstruction);
+        void emitBitBinaryOpFastPath(Instruction* currentInstruction, ProfilingPolicy shouldEmitProfiling = ProfilingPolicy::NoProfiling);
 
         void emitRightShiftFastPath(Instruction* currentInstruction, OpcodeID);
 
index 3981d03..9e13d31 100644 (file)
@@ -528,7 +528,7 @@ void JIT::emitSlow_op_negate(Instruction* currentInstruction, Vector<SlowCaseEnt
 }
 
 template<typename SnippetGenerator>
-void JIT::emitBitBinaryOpFastPath(Instruction* currentInstruction)
+void JIT::emitBitBinaryOpFastPath(Instruction* currentInstruction, ProfilingPolicy profilingPolicy)
 {
     int result = currentInstruction[1].u.operand;
     int op1 = currentInstruction[2].u.operand;
@@ -567,6 +567,8 @@ void JIT::emitBitBinaryOpFastPath(Instruction* currentInstruction)
 
     ASSERT(gen.didEmitFastPath());
     gen.endJumpList().link(this);
+    if (profilingPolicy == ProfilingPolicy::ShouldEmitProfiling)
+        emitValueProfilingSite();
     emitPutVirtualRegister(result, resultRegs);
 
     addSlowCase(gen.slowPathJumpList());
@@ -574,7 +576,7 @@ void JIT::emitBitBinaryOpFastPath(Instruction* currentInstruction)
 
 void JIT::emit_op_bitand(Instruction* currentInstruction)
 {
-    emitBitBinaryOpFastPath<JITBitAndGenerator>(currentInstruction);
+    emitBitBinaryOpFastPath<JITBitAndGenerator>(currentInstruction, ProfilingPolicy::ShouldEmitProfiling);
 }
 
 void JIT::emit_op_bitor(Instruction* currentInstruction)
index daaa8a4..06865b9 100644 (file)
@@ -1148,6 +1148,25 @@ _llint_op_div:
         _slow_path_div)
 
 
+macro bitOpProfiled(operation, slowPath, advance)
+    loadi 12[PC], t2
+    loadi 8[PC], t0
+    loadConstantOrVariable(t2, t3, t1)
+    loadConstantOrVariable2Reg(t0, t2, t0)
+    bineq t3, Int32Tag, .slow
+    bineq t2, Int32Tag, .slow
+    loadi 4[PC], t2
+    operation(t1, t0)
+    storei t3, TagOffset[cfr, t2, 8]
+    storei t0, PayloadOffset[cfr, t2, 8]
+    valueProfile(t3, t0, (advance - 1) * 4, t2)
+    dispatch(advance)
+
+.slow:
+    callSlowPath(slowPath)
+    dispatch(advance)
+end
+
 macro bitOp(operation, slowPath, advance)
     loadi 12[PC], t2
     loadi 8[PC], t0
@@ -1206,7 +1225,7 @@ _llint_op_unsigned:
 
 _llint_op_bitand:
     traceExecution()
-    bitOp(
+    bitOpProfiled(
         macro (left, right) andi left, right end,
         _slow_path_bitand,
         constexpr op_bitand_length)
index ece418d..5a0b6bc 100644 (file)
@@ -1092,6 +1092,25 @@ _llint_op_div:
     end
 
 
+macro bitOpProfiled(operation, slowPath, advance)
+    loadisFromInstruction(3, t0)
+    loadisFromInstruction(2, t2)
+    loadisFromInstruction(1, t3)
+    loadConstantOrVariable(t0, t1)
+    loadConstantOrVariable(t2, t0)
+    bqb t0, tagTypeNumber, .slow
+    bqb t1, tagTypeNumber, .slow
+    operation(t1, t0)
+    orq tagTypeNumber, t0
+    storeq t0, [cfr, t3, 8]
+    valueProfile(t0, advance - 1, t2)
+    dispatch(advance)
+
+.slow:
+    callSlowPath(slowPath)
+    dispatch(advance)
+end
+
 macro bitOp(operation, slowPath, advance)
     loadisFromInstruction(3, t0)
     loadisFromInstruction(2, t2)
@@ -1149,7 +1168,7 @@ _llint_op_unsigned:
 
 _llint_op_bitand:
     traceExecution()
-    bitOp(
+    bitOpProfiled(
         macro (left, right) andi left, right end,
         _slow_path_bitand,
         constexpr op_bitand_length)
index 4e23fd7..b7e1a32 100644 (file)
@@ -656,11 +656,21 @@ SLOW_PATH_DECL(slow_path_unsigned)
 SLOW_PATH_DECL(slow_path_bitand)
 {
     BEGIN();
-    int32_t a = OP_C(2).jsValue().toInt32(exec);
-    if (UNLIKELY(throwScope.exception()))
-        RETURN(JSValue());
-    int32_t b = OP_C(3).jsValue().toInt32(exec);
-    RETURN(jsNumber(a & b));
+    auto leftNumeric = OP_C(2).jsValue().toBigIntOrInt32(exec);
+    CHECK_EXCEPTION();
+    auto rightNumeric = OP_C(3).jsValue().toBigIntOrInt32(exec);
+    CHECK_EXCEPTION();
+    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(vm, WTF::get<JSBigInt*>(leftNumeric), WTF::get<JSBigInt*>(rightNumeric));
+            CHECK_EXCEPTION();
+            RETURN_PROFILED(op_bitand, result);
+        }
+
+        THROW(createTypeError(exec, "Invalid mix of BigInt and other type in bitwise and operation."));
+    }
+
+    RETURN_PROFILED(op_bitand, jsNumber(WTF::get<int32_t>(leftNumeric) & WTF::get<int32_t>(rightNumeric)));
 }
 
 SLOW_PATH_DECL(slow_path_bitor)
index da529d3..d15a33c 100644 (file)
@@ -64,11 +64,11 @@ const ClassInfo JSBigInt::s_info =
 JSBigInt::JSBigInt(VM& vm, Structure* structure, unsigned length)
     : Base(vm, structure)
     , m_length(length)
+    , m_sign(false)
 { }
 
 void JSBigInt::initialize(InitializationType initType)
 {
-    setSign(false);
     if (initType == InitializationType::WithZero)
         memset(dataStorage(), 0, length() * sizeof(Digit));
 }
@@ -81,7 +81,6 @@ Structure* JSBigInt::createStructure(VM& vm, JSGlobalObject* globalObject, JSVal
 JSBigInt* JSBigInt::createZero(VM& vm)
 {
     JSBigInt* zeroBigInt = createWithLength(vm, 0);
-    zeroBigInt->setSign(false);
     return zeroBigInt;
 }
 
@@ -108,10 +107,8 @@ JSBigInt* JSBigInt::createFrom(VM& vm, int32_t value)
     if (value < 0) {
         bigInt->setDigit(0, static_cast<Digit>(-1 * static_cast<int64_t>(value)));
         bigInt->setSign(true);
-    } else {
+    } else
         bigInt->setDigit(0, static_cast<Digit>(value));
-        bigInt->setSign(false);
-    }
 
     return bigInt;
 }
@@ -123,7 +120,6 @@ JSBigInt* JSBigInt::createFrom(VM& vm, uint32_t value)
     
     JSBigInt* bigInt = createWithLength(vm, 1);
     bigInt->setDigit(0, static_cast<Digit>(value));
-    bigInt->setSign(false);
     return bigInt;
 }
 
@@ -138,10 +134,8 @@ JSBigInt* JSBigInt::createFrom(VM& vm, int64_t value)
         if (value < 0) {
             bigInt->setDigit(0, static_cast<Digit>(static_cast<uint64_t>(-(value + 1)) + 1));
             bigInt->setSign(true);
-        } else {
+        } else
             bigInt->setDigit(0, static_cast<Digit>(value));
-            bigInt->setSign(false);
-        }
         
         return bigInt;
     }
@@ -173,7 +167,6 @@ JSBigInt* JSBigInt::createFrom(VM& vm, bool value)
     
     JSBigInt* bigInt = createWithLength(vm, 1);
     bigInt->setDigit(0, static_cast<Digit>(value));
-    bigInt->setSign(false);
     return bigInt;
 }
 
@@ -381,6 +374,31 @@ JSBigInt* JSBigInt::sub(VM& vm, JSBigInt* x, JSBigInt* y)
     return absoluteSub(vm, y, x, !xSign);
 }
 
+JSBigInt* JSBigInt::bitwiseAnd(VM& vm, JSBigInt* x, JSBigInt* y)
+{
+    if (!x->sign() && !y->sign())
+        return absoluteAnd(vm, x, y);
+    
+    if (x->sign() && y->sign()) {
+        int resultLength = std::max(x->length(), y->length()) + 1;
+        // (-x) & (-y) == ~(x-1) & ~(y-1) == ~((x-1) | (y-1))
+        // == -(((x-1) | (y-1)) + 1)
+        JSBigInt* result = absoluteSubOne(vm, x, resultLength);
+        JSBigInt* y1 = absoluteSubOne(vm, y, y->length());
+        result = absoluteOr(vm, result, y1);
+
+        return absoluteAddOne(vm, result, true);
+    }
+
+    ASSERT(x->sign() != y->sign());
+    // Assume that x is the positive BigInt.
+    if (x->sign())
+        std::swap(x, y);
+    
+    // x & (-y) == x & ~(y-1) == x & ~(y-1)
+    return absoluteAndNot(vm, x, absoluteSubOne(vm, y, y->length()));
+}
+
 #if USE(JSVALUE32_64)
 #define HAVE_TWO_DIGIT 1
 typedef uint64_t TwoDigit;
@@ -993,6 +1011,128 @@ JSBigInt* JSBigInt::absoluteLeftShiftAlwaysCopy(VM& vm, JSBigInt* x, unsigned sh
     return result;
 }
 
+// Helper for Absolute{And,AndNot,Or,Xor}.
+// Performs the given binary {op} on digit pairs of {x} and {y}; when the
+// end of the shorter of the two is reached, {extraDigits} configures how
+// remaining digits in the longer input (if {symmetric} == Symmetric, in
+// {x} otherwise) are handled: copied to the result or ignored.
+// Example:
+//       y:             [ y2 ][ y1 ][ y0 ]
+//       x:       [ x3 ][ x2 ][ x1 ][ x0 ]
+//                   |     |     |     |
+//                (Copy)  (op)  (op)  (op)
+//                   |     |     |     |
+//                   v     v     v     v
+// result: [  0 ][ x3 ][ r2 ][ r1 ][ r0 ]
+inline JSBigInt* JSBigInt::absoluteBitwiseOp(VM& vm, JSBigInt* x, JSBigInt* y, ExtraDigitsHandling extraDigits, SymmetricOp symmetric, std::function<Digit(Digit, Digit)> op)
+{
+    unsigned xLength = x->length();
+    unsigned yLength = y->length();
+    unsigned numPairs = yLength;
+    if (xLength < yLength) {
+        numPairs = xLength;
+        if (symmetric == SymmetricOp::Symmetric) {
+            std::swap(x, y);
+            std::swap(xLength, yLength);
+        }
+    }
+
+    ASSERT(numPairs == std::min(xLength, yLength));
+    unsigned resultLength = extraDigits == ExtraDigitsHandling::Copy ? xLength : numPairs;
+    JSBigInt* result = createWithLength(vm, resultLength);
+
+    unsigned i = 0;
+    for (; i < numPairs; i++)
+        result->setDigit(i, op(x->digit(i), y->digit(i)));
+
+    if (extraDigits == ExtraDigitsHandling::Copy) {
+        for (; i < xLength; i++)
+            result->setDigit(i, x->digit(i));
+    }
+
+    for (; i < resultLength; i++)
+        result->setDigit(i, 0);
+
+    return result->rightTrim(vm);
+}
+
+JSBigInt* JSBigInt::absoluteAnd(VM& vm, JSBigInt* x, JSBigInt* y)
+{
+    auto digitOperation = [](Digit a, Digit b) {
+        return a & b;
+    };
+    return absoluteBitwiseOp(vm, x, y, ExtraDigitsHandling::Skip, SymmetricOp::Symmetric, digitOperation);
+}
+
+JSBigInt* JSBigInt::absoluteOr(VM& vm, JSBigInt* x, JSBigInt* y)
+{
+    auto digitOperation = [](Digit a, Digit b) {
+        return a | b;
+    };
+    return absoluteBitwiseOp(vm, x, y, ExtraDigitsHandling::Copy, SymmetricOp::Symmetric, digitOperation);
+}
+
+JSBigInt* JSBigInt::absoluteAndNot(VM& vm, JSBigInt* x, JSBigInt* y)
+{
+    auto digitOperation = [](Digit a, Digit b) {
+        return a & ~b;
+    };
+    return absoluteBitwiseOp(vm, x, y, ExtraDigitsHandling::Copy, SymmetricOp::NotSymmetric, digitOperation);
+}
+
+JSBigInt* JSBigInt::absoluteAddOne(VM& vm, JSBigInt* x, bool sign)
+{
+    unsigned inputLength = x->length();
+    // The addition will overflow into a new digit if all existing digits are
+    // at maximum.
+    bool willOverflow = true;
+    for (unsigned i = 0; i < inputLength; i++) {
+        if (std::numeric_limits<Digit>::max() != x->digit(i)) {
+            willOverflow = false;
+            break;
+        }
+    }
+
+    unsigned resultLength = inputLength + willOverflow;
+    JSBigInt* result = createWithLength(vm, resultLength);
+
+    Digit carry = 1;
+    for (unsigned i = 0; i < inputLength; i++) {
+        Digit newCarry = 0;
+        result->setDigit(i, digitAdd(x->digit(i), carry, newCarry));
+        carry = newCarry;
+    }
+    if (resultLength > inputLength)
+        result->setDigit(inputLength, carry);
+    else
+        ASSERT(!carry);
+
+    result->setSign(sign);
+    return result->rightTrim(vm);
+}
+
+// Like the above, but you can specify that the allocated result should have
+// length {resultLength}, which must be at least as large as {x->length()}.
+JSBigInt* JSBigInt::absoluteSubOne(VM& vm, JSBigInt* x, unsigned resultLength)
+{
+    ASSERT(!x->isZero());
+    ASSERT(resultLength >= x->length());
+    JSBigInt* result = createWithLength(vm, resultLength);
+
+    unsigned length = x->length();
+    Digit borrow = 1;
+    for (unsigned i = 0; i < length; i++) {
+        Digit newBorrow = 0;
+        result->setDigit(i, digitSub(x->digit(i), borrow, newBorrow));
+        borrow = newBorrow;
+    }
+    ASSERT(!borrow);
+    for (unsigned i = length; i < resultLength; i++)
+        result->setDigit(i, borrow);
+
+    return result->rightTrim(vm);
+}
+
 // Lookup table for the maximum number of bits required per character of a
 // base-N string representation of a number. To increase accuracy, the array
 // value is the actual value multiplied by 32. To generate this table:
index 5b7db74..3f9e232 100644 (file)
@@ -112,7 +112,9 @@ public:
     static JSBigInt* divide(ExecState*, JSBigInt* x, JSBigInt* y);
     static JSBigInt* remainder(ExecState*, JSBigInt* x, JSBigInt* y);
     static JSBigInt* unaryMinus(VM&, JSBigInt* x);
-    
+
+    static JSBigInt* bitwiseAnd(VM&, JSBigInt* x, JSBigInt* y);
+
 private:
 
     using Digit = uintptr_t;
@@ -148,6 +150,24 @@ private:
     Digit absoluteInplaceSub(JSBigInt* subtrahend, unsigned startIndex);
     void inplaceRightShift(unsigned shift);
 
+    enum class ExtraDigitsHandling {
+        Copy,
+        Skip
+    };
+
+    enum class SymmetricOp {
+        Symmetric,
+        NotSymmetric
+    };
+
+    static JSBigInt* absoluteBitwiseOp(VM&, JSBigInt* x, JSBigInt* y, ExtraDigitsHandling, SymmetricOp, std::function<Digit(Digit, Digit)> op);
+    static JSBigInt* absoluteAnd(VM&, JSBigInt* x, JSBigInt* y);
+    static JSBigInt* absoluteOr(VM&, JSBigInt* x, JSBigInt* y);
+    static JSBigInt* absoluteAndNot(VM&, JSBigInt* x, JSBigInt* y);
+
+    static JSBigInt* absoluteAddOne(VM&, JSBigInt* x, bool sign);
+    static JSBigInt* absoluteSubOne(VM&, JSBigInt* x, unsigned resultLength);
+
     // Digit arithmetic helpers.
     static Digit digitAdd(Digit a, Digit b, Digit& carry);
     static Digit digitSub(Digit a, Digit b, Digit& borrow);
index f7e6575..7c7e8c3 100644 (file)
@@ -259,6 +259,7 @@ public:
     double toNumber(ExecState*) const;
     
     Variant<JSBigInt*, double> toNumeric(ExecState*) const;
+    Variant<JSBigInt*, int32_t> toBigIntOrInt32(ExecState*) const;
 
     // toNumber conversion if it can be done without side effects.
     std::optional<double> toNumberFromPrimitive() const;
index a948844..b885daf 100644 (file)
@@ -750,6 +750,26 @@ ALWAYS_INLINE Variant<JSBigInt*, double> JSValue::toNumeric(ExecState* exec) con
     return value;
 }
 
+ALWAYS_INLINE Variant<JSBigInt*, int32_t> JSValue::toBigIntOrInt32(ExecState* exec) const
+{
+    if (isInt32())
+        return asInt32();
+    if (isDouble() && canBeInt32(asDouble()))
+        return static_cast<int32_t>(asDouble());
+    if (isBigInt())
+        return asBigInt(*this);
+
+    VM& vm = exec->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+    JSValue primValue = this->toPrimitive(exec, PreferNumber);
+    RETURN_IF_EXCEPTION(scope, 0);
+    if (primValue.isBigInt())
+        return asBigInt(primValue);
+    int32_t value = primValue.toInt32(exec);
+    RETURN_IF_EXCEPTION(scope, 0);
+    return value;
+}
+
 inline JSObject* JSValue::toObject(ExecState* exec) const
 {
     return isCell() ? asCell()->toObject(exec, exec->lexicalGlobalObject()) : toObjectSlowCase(exec, exec->lexicalGlobalObject());