[ESNext][BigInt] Implement support for "|"
authorticaiolima@gmail.com <ticaiolima@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 7 Oct 2018 00:20:59 +0000 (00:20 +0000)
committerticaiolima@gmail.com <ticaiolima@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 7 Oct 2018 00:20:59 +0000 (00:20 +0000)
https://bugs.webkit.org/show_bug.cgi?id=186229

Reviewed by Yusuke Suzuki.

JSTests:

* stress/big-int-bitwise-and-jit.js:
* stress/big-int-bitwise-or-general.js: Added.
* stress/big-int-bitwise-or-jit-untyped.js: Added.
* stress/big-int-bitwise-or-jit.js: Added.
* stress/big-int-bitwise-or-memory-stress.js: Added.
* stress/big-int-bitwise-or-to-primitive-precedence.js: Added.
* stress/big-int-bitwise-or-type-error.js: Added.
* stress/big-int-bitwise-or-wrapped-value.js: Added.

Source/JavaScriptCore:

This patch is introducing support for BigInt into bitwise "or" operator.
In addition, we are also introducing 2 new DFG nodes, named "ArithBitOr" and
"ValueBitOr", to replace "BitOr" node. The idea is to follow the
difference that we make on Arith<op> and Value<op>, where ArithBitOr
handles cases when the operands are Int32 and ValueBitOr handles
the remaining cases.

We are also changing op_bitor to use ValueProfile. We are using
ValueProfile during DFG generation to emit "ArithBitOr" when
outcome prediction is Int32.

* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::finishCreation):
(JSC::CodeBlock::arithProfileForPC):
* 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:
(JSC::DFG::bitwiseOp):
* 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::compileValueBitOr):
(JSC::FTL::DFG::LowerDFGToB3::compileArithBitOr):
(JSC::FTL::DFG::LowerDFGToB3::compileBitOr): Deleted.
* jit/JITArithmetic.cpp:
(JSC::JIT::emit_op_bitor):
* llint/LowLevelInterpreter32_64.asm:
* llint/LowLevelInterpreter64.asm:
* runtime/CommonSlowPaths.cpp:
(JSC::SLOW_PATH_DECL):
* runtime/JSBigInt.cpp:
(JSC::JSBigInt::bitwiseAnd):
(JSC::JSBigInt::bitwiseOr):
(JSC::JSBigInt::absoluteBitwiseOp):
(JSC::JSBigInt::absoluteAddOne):
* runtime/JSBigInt.h:

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

36 files changed:
JSTests/ChangeLog
JSTests/stress/big-int-bitwise-and-jit.js
JSTests/stress/big-int-bitwise-or-general.js [new file with mode: 0644]
JSTests/stress/big-int-bitwise-or-jit-untyped.js [new file with mode: 0644]
JSTests/stress/big-int-bitwise-or-jit.js [new file with mode: 0644]
JSTests/stress/big-int-bitwise-or-memory-stress.js [new file with mode: 0644]
JSTests/stress/big-int-bitwise-or-to-primitive-precedence.js [new file with mode: 0644]
JSTests/stress/big-int-bitwise-or-type-error.js [new file with mode: 0644]
JSTests/stress/big-int-bitwise-or-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/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

index 17e458c..4a257a0 100644 (file)
@@ -1,3 +1,19 @@
+2018-10-06  Caio Lima  <ticaiolima@gmail.com>
+
+        [ESNext][BigInt] Implement support for "|"
+        https://bugs.webkit.org/show_bug.cgi?id=186229
+
+        Reviewed by Yusuke Suzuki.
+
+        * stress/big-int-bitwise-and-jit.js:
+        * stress/big-int-bitwise-or-general.js: Added.
+        * stress/big-int-bitwise-or-jit-untyped.js: Added.
+        * stress/big-int-bitwise-or-jit.js: Added.
+        * stress/big-int-bitwise-or-memory-stress.js: Added.
+        * stress/big-int-bitwise-or-to-primitive-precedence.js: Added.
+        * stress/big-int-bitwise-or-type-error.js: Added.
+        * stress/big-int-bitwise-or-wrapped-value.js: Added.
+
 2018-10-05  Dominik Infuehr  <dominik.infuehr@gmail.com>
 
         Skip test on systems with limited memory
index ee766af..2d4472d 100644 (file)
@@ -9,7 +9,6 @@ let assert = {
 
 function bigIntBitAnd(a, b) {
     return (a & b) & (a & 0b11n);
-
 }
 noInline(bigIntBitAnd);
 
diff --git a/JSTests/stress/big-int-bitwise-or-general.js b/JSTests/stress/big-int-bitwise-or-general.js
new file mode 100644 (file)
index 0000000..51c7173
--- /dev/null
@@ -0,0 +1,98 @@
+//@ 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, 0b01n, "0b00n | 0b01n === 0b01n");
+assert.sameValue(0b01n | 0b00n, 0b01n, "0b01n | 0b00n === 0b01n");
+assert.sameValue(0b00n | 0b10n, 0b10n, "0b00n | 0b10n === 0b10n");
+assert.sameValue(0b10n | 0b00n, 0b10n, "0b10n | 0b00n === 0b10n");
+assert.sameValue(0b00n | 0b11n, 0b11n, "0b00n | 0b11n === 0b11n");
+assert.sameValue(0b11n | 0b00n, 0b11n, "0b11n | 0b00n === 0b11n");
+assert.sameValue(0b01n | 0b01n, 0b01n, "0b01n | 0b01n === 0b01n");
+assert.sameValue(0b01n | 0b10n, 0b11n, "0b01n | 0b10n === 0b11n");
+assert.sameValue(0b10n | 0b01n, 0b11n, "0b10n | 0b01n === 0b11n");
+assert.sameValue(0b01n | 0b11n, 0b11n, "0b01n | 0b11n === 0b11n");
+assert.sameValue(0b11n | 0b01n, 0b11n, "0b11n | 0b01n === 0b11n");
+assert.sameValue(0b10n | 0b10n, 0b10n, "0b10n | 0b10n === 0b10n");
+assert.sameValue(0b10n | 0b11n, 0b11n, "0b10n | 0b11n === 0b11n");
+assert.sameValue(0b11n | 0b10n, 0b11n, "0b11n | 0b10n === 0b11n");
+assert.sameValue(0xffffffffn | 0n, 0xffffffffn, "0xffffffffn | 0n === 0xffffffffn");
+assert.sameValue(0n | 0xffffffffn, 0xffffffffn, "0n | 0xffffffffn === 0xffffffffn");
+assert.sameValue(0xffffffffn | 0xffffffffn, 0xffffffffn, "0xffffffffn | 0xffffffffn === 0xffffffffn");
+assert.sameValue(0xffffffffffffffffn | 0n, 0xffffffffffffffffn, "0xffffffffffffffffn | 0n === 0xffffffffffffffffn");
+assert.sameValue(0n | 0xffffffffffffffffn, 0xffffffffffffffffn, "0n | 0xffffffffffffffffn === 0xffffffffffffffffn");
+assert.sameValue(0xffffffffffffffffn | 0xffffffffn, 0xffffffffffffffffn, "0xffffffffffffffffn | 0xffffffffn === 0xffffffffffffffffn");
+assert.sameValue(0xffffffffn | 0xffffffffffffffffn, 0xffffffffffffffffn, "0xffffffffn | 0xffffffffffffffffn === 0xffffffffffffffffn");
+assert.sameValue(
+  0xffffffffffffffffn | 0xffffffffffffffffn, 0xffffffffffffffffn,
+  "0xffffffffffffffffn | 0xffffffffffffffffn === 0xffffffffffffffffn");
+assert.sameValue(
+  0xbf2ed51ff75d380fd3be813ec6185780n | 0x4aabef2324cedff5387f1f65n, 0xbf2ed51fffffff2ff7fedffffe7f5fe5n,
+  "0xbf2ed51ff75d380fd3be813ec6185780n | 0x4aabef2324cedff5387f1f65n === 0xbf2ed51fffffff2ff7fedffffe7f5fe5n");
+assert.sameValue(
+  0x4aabef2324cedff5387f1f65n | 0xbf2ed51ff75d380fd3be813ec6185780n, 0xbf2ed51fffffff2ff7fedffffe7f5fe5n,
+  "0x4aabef2324cedff5387f1f65n | 0xbf2ed51ff75d380fd3be813ec6185780n === 0xbf2ed51fffffff2ff7fedffffe7f5fe5n");
+assert.sameValue(0n | -1n, -1n, "0n | -1n === -1n");
+assert.sameValue(-1n | 0n, -1n, "-1n | 0n === -1n");
+assert.sameValue(0n | -2n, -2n, "0n | -2n === -2n");
+assert.sameValue(-2n | 0n, -2n, "-2n | 0n === -2n");
+assert.sameValue(1n | -2n, -1n, "1n | -2n === -1n");
+assert.sameValue(-2n | 1n, -1n, "-2n | 1n === -1n");
+assert.sameValue(2n | -2n, -2n, "2n | -2n === -2n");
+assert.sameValue(-2n | 2n, -2n, "-2n | 2n === -2n");
+assert.sameValue(2n | -3n, -1n, "2n | -3n === -1n");
+assert.sameValue(-3n | 2n, -1n, "-3n | 2n === -1n");
+assert.sameValue(-1n | -2n, -1n, "-1n | -2n === -1n");
+assert.sameValue(-2n | -1n, -1n, "-2n | -1n === -1n");
+assert.sameValue(-2n | -2n, -2n, "-2n | -2n === -2n");
+assert.sameValue(-2n | -3n, -1n, "-2n | -3n === -1n");
+assert.sameValue(-3n | -2n, -1n, "-3n | -2n === -1n");
+assert.sameValue(0xffffffffn | -1n, -1n, "0xffffffffn | -1n === -1n");
+assert.sameValue(-1n | 0xffffffffn, -1n, "-1n | 0xffffffffn === -1n");
+assert.sameValue(0xffffffffffffffffn | -1n, -1n, "0xffffffffffffffffn | -1n === -1n");
+assert.sameValue(-1n | 0xffffffffffffffffn, -1n, "-1n | 0xffffffffffffffffn === -1n");
+assert.sameValue(
+  0xbf2ed51ff75d380fd3be813ec6185780n | -0x4aabef2324cedff5387f1f65n, -0x8a2c72024405ec138670865n,
+  "0xbf2ed51ff75d380fd3be813ec6185780n | -0x4aabef2324cedff5387f1f65n === -0x8a2c72024405ec138670865n");
+assert.sameValue(
+  -0x4aabef2324cedff5387f1f65n | 0xbf2ed51ff75d380fd3be813ec6185780n, -0x8a2c72024405ec138670865n,
+  "-0x4aabef2324cedff5387f1f65n | 0xbf2ed51ff75d380fd3be813ec6185780n === -0x8a2c72024405ec138670865n");
+assert.sameValue(
+  -0xbf2ed51ff75d380fd3be813ec6185780n | 0x4aabef2324cedff5387f1f65n, -0xbf2ed51fb554100cd330000ac600401bn,
+  "-0xbf2ed51ff75d380fd3be813ec6185780n | 0x4aabef2324cedff5387f1f65n === -0xbf2ed51fb554100cd330000ac600401bn");
+assert.sameValue(
+  0x4aabef2324cedff5387f1f65n | -0xbf2ed51ff75d380fd3be813ec6185780n, -0xbf2ed51fb554100cd330000ac600401bn,
+  "0x4aabef2324cedff5387f1f65n | -0xbf2ed51ff75d380fd3be813ec6185780n === -0xbf2ed51fb554100cd330000ac600401bn");
+assert.sameValue(
+  -0xbf2ed51ff75d380fd3be813ec6185780n | -0x4aabef2324cedff5387f1f65n, -0x42092803008e813400181765n,
+  "-0xbf2ed51ff75d380fd3be813ec6185780n | -0x4aabef2324cedff5387f1f65n === -0x42092803008e813400181765n");
+assert.sameValue(
+  -0x4aabef2324cedff5387f1f65n | -0xbf2ed51ff75d380fd3be813ec6185780n, -0x42092803008e813400181765n,
+  "-0x4aabef2324cedff5387f1f65n | -0xbf2ed51ff75d380fd3be813ec6185780n === -0x42092803008e813400181765n");
+assert.sameValue(-0xffffffffn | 0n, -0xffffffffn, "-0xffffffffn | 0n === -0xffffffffn");
+assert.sameValue(0n | -0xffffffffn, -0xffffffffn, "0n | -0xffffffffn === -0xffffffffn");
+assert.sameValue(
+  -0xffffffffffffffffn | 0x10000000000000000n, -0xffffffffffffffffn,
+  "-0xffffffffffffffffn | 0x10000000000000000n === -0xffffffffffffffffn");
+assert.sameValue(
+  0x10000000000000000n | -0xffffffffffffffffn, -0xffffffffffffffffn,
+  "0x10000000000000000n | -0xffffffffffffffffn === -0xffffffffffffffffn");
+assert.sameValue(
+  -0xffffffffffffffffffffffffn | 0x10000000000000000n, -0xfffffffeffffffffffffffffn,
+  "-0xffffffffffffffffffffffffn | 0x10000000000000000n === -0xfffffffeffffffffffffffffn");
+assert.sameValue(
+  0x10000000000000000n | -0xffffffffffffffffffffffffn, -0xfffffffeffffffffffffffffn,
+  "0x10000000000000000n | -0xffffffffffffffffffffffffn === -0xfffffffeffffffffffffffffn");
+
diff --git a/JSTests/stress/big-int-bitwise-or-jit-untyped.js b/JSTests/stress/big-int-bitwise-or-jit-untyped.js
new file mode 100644 (file)
index 0000000..936a679
--- /dev/null
@@ -0,0 +1,20 @@
+//@ runBigIntEnabled
+
+let assert = {
+    sameValue: function(i, e, m) {
+        if (i !== e)
+            throw new Error(m);
+    }
+}
+
+function bigIntBitOrUntyped(a, b) {
+    return a | b;
+}
+noInline(bigIntBitOrUntyped);
+
+let o = { valueOf: () => 0b1111n };
+for (let i = 0; i < 10000; i++) {
+    let r = bigIntBitOrUntyped(o, 0b1010n);
+    assert.sameValue(r, 0b1111n, 0b101n + " | " + 0b1010n + " = " + r);
+}
+
diff --git a/JSTests/stress/big-int-bitwise-or-jit.js b/JSTests/stress/big-int-bitwise-or-jit.js
new file mode 100644 (file)
index 0000000..2203da0
--- /dev/null
@@ -0,0 +1,19 @@
+//@ runBigIntEnabled
+
+let assert = {
+    sameValue: function(i, e, m) {
+        if (i !== e)
+            throw new Error(m);
+    }
+}
+
+function bigIntBitOr(a, b) {
+    return (a | b) | (a | 0b111n);
+}
+noInline(bigIntBitOr);
+
+for (let i = 0; i < 10000; i++) {
+    let r = bigIntBitOr(0b101n, 0b1010n);
+    assert.sameValue(r, 0b1111n, 0b101n + " | " + 0b1010n + " = " + r);
+}
+
diff --git a/JSTests/stress/big-int-bitwise-or-memory-stress.js b/JSTests/stress/big-int-bitwise-or-memory-stress.js
new file mode 100644 (file)
index 0000000..0ef0f95
--- /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 === 0b11n);
+
diff --git a/JSTests/stress/big-int-bitwise-or-to-primitive-precedence.js b/JSTests/stress/big-int-bitwise-or-to-primitive-precedence.js
new file mode 100644 (file)
index 0000000..f9dad0b
--- /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-or-type-error.js b/JSTests/stress/big-int-bitwise-or-type-error.js
new file mode 100644 (file)
index 0000000..b3072f7
--- /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 | +Infinity");
+assertThrowTypeError(+Infinity, 18757382984821n, "+Infinity | 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-or-wrapped-value.js b/JSTests/stress/big-int-bitwise-or-wrapped-value.js
new file mode 100644 (file)
index 0000000..31122ca
--- /dev/null
@@ -0,0 +1,62 @@
+//@ runBigIntEnabled
+
+assert = {
+    sameValue: function (input, expected, message) {
+        if (input !== expected)
+            throw new Error(message);
+    }
+};
+
+function testBitOr(x, y, z, message) {
+    assert.sameValue(x | y, z, message);
+    assert.sameValue(y | x, z, message);
+}
+
+testBitOr(Object(0b10n), 0b01n, 0b11n, "ToPrimitive: unbox object with internal slot");
+
+let o = {
+    [Symbol.toPrimitive]: function() {
+        return 0b10n;
+    }
+};
+testBitOr(o, 0b01n, 0b11n, "ToPrimitive: @@toPrimitive");
+
+o = {
+    valueOf: function() {
+        return 0b10n;
+    }
+};
+testBitOr(o, 0b01n, 0b11n, "ToPrimitive: valueOf");
+
+o = {
+    toString: function() {
+        return 0b10n;
+    }
+}
+testBitOr(o, 0b01n, 0b11n, "ToPrimitive: toString");
+
+// BigInt with length > 1
+
+testBitOr(Object(0b1111000000000000000000000000000000000000000000000000000000000000000n), 0b1000000000000000000000000000000000000000000000000000000000000001111n, 0b1111000000000000000000000000000000000000000000000000000000000001111n, "ToPrimitive: unbox object with internal slot");
+
+o = {
+    [Symbol.toPrimitive]: function() {
+        return 0b1111000000000000000000000000000000000000000000000000000000000000000n;
+    }
+};
+testBitOr(o, 0b1000000000000000000000000000000000000000000000000000000000000001111n, 0b1111000000000000000000000000000000000000000000000000000000000001111n, "ToPrimitive: @@toPrimitive");
+
+o = {
+    valueOf: function() {
+        return 0b1111000000000000000000000000000000000000000000000000000000000000000n;
+    }
+};
+testBitOr(o, 0b1000000000000000000000000000000000000000000000000000000000000001111n, 0b1111000000000000000000000000000000000000000000000000000000000001111n, "ToPrimitive: valueOf");
+
+o = {
+    toString: function() {
+        return 0b1111000000000000000000000000000000000000000000000000000000000000000n;
+    }
+}
+testBitOr(o, 0b1000000000000000000000000000000000000000000000000000000000000001111n, 0b1111000000000000000000000000000000000000000000000000000000000001111n, "ToPrimitive: toString");
+
index 9a19cb2..7186193 100644 (file)
@@ -1,3 +1,77 @@
+2018-10-06  Caio Lima  <ticaiolima@gmail.com>
+
+        [ESNext][BigInt] Implement support for "|"
+        https://bugs.webkit.org/show_bug.cgi?id=186229
+
+        Reviewed by Yusuke Suzuki.
+
+        This patch is introducing support for BigInt into bitwise "or" operator.
+        In addition, we are also introducing 2 new DFG nodes, named "ArithBitOr" and
+        "ValueBitOr", to replace "BitOr" node. The idea is to follow the
+        difference that we make on Arith<op> and Value<op>, where ArithBitOr
+        handles cases when the operands are Int32 and ValueBitOr handles
+        the remaining cases.
+
+        We are also changing op_bitor to use ValueProfile. We are using
+        ValueProfile during DFG generation to emit "ArithBitOr" when
+        outcome prediction is Int32.
+
+        * bytecode/CodeBlock.cpp:
+        (JSC::CodeBlock::finishCreation):
+        (JSC::CodeBlock::arithProfileForPC):
+        * 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:
+        (JSC::DFG::bitwiseOp):
+        * 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::compileValueBitOr):
+        (JSC::FTL::DFG::LowerDFGToB3::compileArithBitOr):
+        (JSC::FTL::DFG::LowerDFGToB3::compileBitOr): Deleted.
+        * jit/JITArithmetic.cpp:
+        (JSC::JIT::emit_op_bitor):
+        * llint/LowLevelInterpreter32_64.asm:
+        * llint/LowLevelInterpreter64.asm:
+        * runtime/CommonSlowPaths.cpp:
+        (JSC::SLOW_PATH_DECL):
+        * runtime/JSBigInt.cpp:
+        (JSC::JSBigInt::bitwiseAnd):
+        (JSC::JSBigInt::bitwiseOr):
+        (JSC::JSBigInt::absoluteBitwiseOp):
+        (JSC::JSBigInt::absoluteAddOne):
+        * runtime/JSBigInt.h:
+
 2018-10-05  Yusuke Suzuki  <yusukesuzuki@slowstart.org>
 
         [JSC] Use new extra memory reporting in SparseArrayMap
index 29a1859..3e974bf 100644 (file)
@@ -582,6 +582,7 @@ bool CodeBlock::finishCreation(VM& vm, ScriptExecutable* ownerExecutable, Unlink
         }
 
         case op_bitand:
+        case op_bitor:
         case op_to_this: {
             linkValueProfile(i, opLength);
             break;
@@ -2935,8 +2936,6 @@ ArithProfile* CodeBlock::arithProfileForPC(Instruction* pc)
     switch (opcodeID) {
     case op_negate:
         return bitwise_cast<ArithProfile*>(&pc[3].u.operand);
-    case op_bitor:
-    case op_bitand:
     case op_bitxor:
     case op_add:
     case op_mul:
index 72692e1..061e6ab 100644 (file)
@@ -1733,7 +1733,7 @@ RegisterID* BytecodeGenerator::emitDec(RegisterID* srcDst)
 RegisterID* BytecodeGenerator::emitBinaryOp(OpcodeID opcodeID, RegisterID* dst, RegisterID* src1, RegisterID* src2, OperandTypes types)
 {
 
-    if (opcodeID == op_bitand) {
+    if (opcodeID == op_bitand || opcodeID == op_bitor) {
         UnlinkedValueProfile profile = emitProfiledOpcode(opcodeID);
         instructions().append(dst->index());
         instructions().append(src1->index());
@@ -1747,8 +1747,8 @@ RegisterID* BytecodeGenerator::emitBinaryOp(OpcodeID opcodeID, RegisterID* dst,
     instructions().append(src1->index());
     instructions().append(src2->index());
 
-    if (opcodeID == op_bitor || opcodeID == op_bitxor ||
-        opcodeID == op_add || opcodeID == op_mul || opcodeID == op_sub || opcodeID == op_div)
+    if (opcodeID == op_bitxor || opcodeID == op_add ||
+        opcodeID == op_mul || opcodeID == op_sub || opcodeID == op_div)
         instructions().append(ArithProfile(types.first(), types.second()).bits());
 
     return dst;
index dc25bbf..c20522a 100644 (file)
@@ -375,13 +375,17 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
         break;
     }
             
+    case ValueBitOr:
     case ValueBitAnd:
         clobberWorld();
-        setTypeForNode(node, SpecBoolInt32 | SpecBigInt);
+        if (node->binaryUseKind() == BigIntUse)
+            setTypeForNode(node, SpecBigInt);
+        else
+            setTypeForNode(node, SpecBoolInt32 | SpecBigInt);
         break;
             
     case ArithBitAnd:
-    case BitOr:
+    case ArithBitOr:
     case BitXor:
     case BitRShift:
     case BitLShift:
@@ -401,7 +405,7 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
             case ArithBitAnd:
                 setConstant(node, JSValue(a & b));
                 break;
-            case BitOr:
+            case ArithBitOr:
                 setConstant(node, JSValue(a | b));
                 break;
             case BitXor:
index 13e9518..31a342e 100644 (file)
@@ -118,7 +118,7 @@ private:
                 || isWithinPowerOfTwoNonRecursive<power>(node->child2().node());
         }
             
-        case BitOr:
+        case ArithBitOr:
         case BitXor:
         case BitLShift: {
             return power > 31;
@@ -208,7 +208,7 @@ private:
             break;
             
         case ArithBitAnd:
-        case BitOr:
+        case ArithBitOr:
         case BitXor:
         case BitRShift:
         case BitLShift:
index b7b1200..a819d83 100644 (file)
@@ -4856,7 +4856,10 @@ void ByteCodeParser::parseBlock(unsigned limit)
         case op_bitor: {
             Node* op1 = get(VirtualRegister(currentInstruction[2].u.operand));
             Node* op2 = get(VirtualRegister(currentInstruction[3].u.operand));
-            set(VirtualRegister(currentInstruction[1].u.operand), addToGraph(BitOr, op1, op2));
+            if (isInt32Speculation(getPrediction()))
+                set(VirtualRegister(currentInstruction[1].u.operand), addToGraph(ArithBitOr, op1, op2));
+            else
+                set(VirtualRegister(currentInstruction[1].u.operand), addToGraph(ValueBitOr, op1, op2));
             NEXT_OPCODE(op_bitor);
         }
 
index 70ff3bf..c5ba734 100644 (file)
@@ -263,7 +263,7 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu
         return;
 
     case ArithBitAnd:
-    case BitOr:
+    case ArithBitOr:
     case BitXor:
     case BitLShift:
     case BitRShift:
@@ -636,6 +636,7 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu
     case InById:
     case HasOwnProperty:
     case ValueBitAnd:
+    case ValueBitOr:
     case ValueNegate:
     case ValueAdd:
     case SetFunctionName:
index 13d4c96..90fff25 100644 (file)
@@ -68,7 +68,7 @@ bool doesGC(Graph& graph, Node* node)
     case PhantomLocal:
     case SetArgument:
     case ArithBitAnd:
-    case BitOr:
+    case ArithBitOr:
     case BitXor:
     case BitLShift:
     case BitRShift:
@@ -97,6 +97,7 @@ bool doesGC(Graph& graph, Node* node)
     case ArithFRound:
     case ArithUnary:
     case ValueBitAnd:
+    case ValueBitOr:
     case ValueAdd:
     case ValueNegate:
     case TryGetById:
index 0269024..7f36e78 100644 (file)
@@ -99,6 +99,7 @@ private:
             return;
         }
             
+        case ValueBitOr:
         case ValueBitAnd: {
             if (Node::shouldSpeculateBigInt(node->child1().node(), node->child2().node())) {
                 fixEdge<BigIntUse>(node->child1());
@@ -111,11 +112,24 @@ private:
             break;
         }
     
+        case ArithBitOr:
         case ArithBitAnd: {
             if (Node::shouldSpeculateUntypedForBitOps(node->child1().node(), node->child2().node())) {
                 fixEdge<UntypedUse>(node->child1());
                 fixEdge<UntypedUse>(node->child2());
-                node->setOpAndDefaultFlags(ValueBitAnd);
+
+                switch (op) {
+                case ArithBitOr:
+                    node->setOpAndDefaultFlags(ValueBitOr);
+                    break;
+                case ArithBitAnd:
+                    node->setOpAndDefaultFlags(ValueBitAnd);
+                    break;
+                default:
+                    DFG_CRASH(m_graph, node, "Unexpected node during ArithBit operation fixup");
+                    break;
+                }
+
                 break;
             }
             fixIntConvertingEdge(node->child1());
@@ -123,7 +137,6 @@ private:
             break;
         }
 
-        case BitOr:
         case BitXor:
         case BitRShift:
         case BitLShift:
index 2495597..a9689b2 100644 (file)
@@ -113,7 +113,8 @@ namespace JSC { namespace DFG {
     /* Nodes for bitwise operations. */\
     macro(ValueBitAnd, NodeResultJS | NodeMustGenerate) \
     macro(ArithBitAnd, NodeResultInt32) \
-    macro(BitOr, NodeResultInt32) \
+    macro(ValueBitOr, NodeResultJS | NodeMustGenerate) \
+    macro(ArithBitOr, NodeResultInt32) \
     macro(BitXor, NodeResultInt32) \
     macro(BitLShift, NodeResultInt32) \
     macro(BitRShift, NodeResultInt32) \
index 6360e55..a24c30b 100644 (file)
@@ -354,7 +354,7 @@ EncodedJSValue JIT_OPERATION operationValueBitAnd(ExecState* exec, EncodedJSValu
             return JSValue::encode(result);
         }
 
-        return throwVMTypeError(exec, scope, "Invalid mix of BigInt and other type in bitwise and operation.");
+        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)));
@@ -369,11 +369,22 @@ EncodedJSValue JIT_OPERATION operationValueBitOr(ExecState* exec, EncodedJSValue
     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::bitwiseOr(*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 'or' operation.");
+    }
+
+    return JSValue::encode(jsNumber(WTF::get<int32_t>(leftNumeric) | WTF::get<int32_t>(rightNumeric)));
 }
 
 EncodedJSValue JIT_OPERATION operationValueBitXor(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)
@@ -1286,6 +1297,17 @@ JSCell* JIT_OPERATION operationBitAndBigInt(ExecState* exec, JSCell* op1, JSCell
     return JSBigInt::bitwiseAnd(*vm, leftOperand, rightOperand);
 }
 
+JSCell* JIT_OPERATION operationBitOrBigInt(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::bitwiseOr(*vm, leftOperand, rightOperand);
+}
+
 size_t JIT_OPERATION operationCompareStrictEqCell(ExecState* exec, JSCell* op1, JSCell* op2)
 {
     VM* vm = &exec->vm();
index d069def..da6d2ed 100644 (file)
@@ -164,6 +164,7 @@ size_t JIT_OPERATION operationRegExpTest(ExecState*, JSGlobalObject*, RegExpObje
 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;
+JSCell* JIT_OPERATION operationBitOrBigInt(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 8d2f7ab..aedf226 100644 (file)
@@ -246,6 +246,7 @@ private:
             break;
         }
 
+        case ValueBitOr:
         case ValueBitAnd: {
             if (node->child1()->shouldSpeculateBigInt() && node->child2()->shouldSpeculateBigInt())
                 changed |= mergePrediction(SpecBigInt);
@@ -708,7 +709,7 @@ private:
         }
 
         case ArithBitAnd:
-        case BitOr:
+        case ArithBitOr:
         case BitXor:
         case BitRShift:
         case BitLShift:
@@ -1063,6 +1064,7 @@ private:
         case GetLocal:
         case SetLocal:
         case UInt32ToNumber:
+        case ValueBitOr:
         case ValueBitAnd:
         case ValueNegate:
         case ValueAdd:
index aeca211..91bbd59 100644 (file)
@@ -198,7 +198,7 @@ bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node, bool igno
     case PhantomLocal:
     case SetArgument:
     case ArithBitAnd:
-    case BitOr:
+    case ArithBitOr:
     case BitXor:
     case BitLShift:
     case BitRShift:
@@ -227,6 +227,7 @@ bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node, bool igno
     case ArithTrunc:
     case ArithUnary:
     case ValueBitAnd:
+    case ValueBitOr:
     case ValueNegate:
     case ValueAdd:
     case TryGetById:
index 7d4128e..aefe455 100644 (file)
@@ -3599,6 +3599,9 @@ void SpeculativeJIT::compileValueBitwiseOp(Node* node)
         case ValueBitAnd:
             emitUntypedBitOp<JITBitAndGenerator, operationValueBitAnd>(node);
             return;
+        case ValueBitOr:
+            emitUntypedBitOp<JITBitOrGenerator, operationValueBitOr>(node);
+            return;
         default:
             RELEASE_ASSERT_NOT_REACHED();
         }
@@ -3617,7 +3620,18 @@ void SpeculativeJIT::compileValueBitwiseOp(Node* node)
     flushRegisters();
     GPRFlushedCallResult result(this);
     GPRReg resultGPR = result.gpr();
-    callOperation(operationBitAndBigInt, resultGPR, leftGPR, rightGPR);
+
+    switch (op) {
+    case ValueBitAnd:
+        callOperation(operationBitAndBigInt, resultGPR, leftGPR, rightGPR);
+        break;
+    case ValueBitOr:
+        callOperation(operationBitOrBigInt, resultGPR, leftGPR, rightGPR);
+        break;
+    default:
+        RELEASE_ASSERT_NOT_REACHED();
+    }
+
     m_jit.exceptionCheck();
     cellResult(resultGPR, node);
 }
@@ -3630,9 +3644,6 @@ void SpeculativeJIT::compileBitwiseOp(Node* node)
 
     if (leftChild.useKind() == UntypedUse || rightChild.useKind() == UntypedUse) {
         switch (op) {
-        case BitOr:
-            emitUntypedBitOp<JITBitOrGenerator, operationValueBitOr>(node);
-            return;
         case BitXor:
             emitUntypedBitOp<JITBitXorGenerator, operationValueBitXor>(node);
             return;
index 98afb6e..0dfd314 100644 (file)
@@ -636,7 +636,7 @@ public:
         case ArithBitAnd:
             m_jit.and32(Imm32(imm), op1, result);
             break;
-        case BitOr:
+        case ArithBitOr:
             m_jit.or32(Imm32(imm), op1, result);
             break;
         case BitXor:
@@ -652,7 +652,7 @@ public:
         case ArithBitAnd:
             m_jit.and32(op1, op2, result);
             break;
-        case BitOr:
+        case ArithBitOr:
             m_jit.or32(op1, op2, result);
             break;
         case BitXor:
index 42a8a4a..34ac1b1 100644 (file)
@@ -1979,12 +1979,13 @@ void SpeculativeJIT::compile(Node* node)
         recordSetLocal(dataFormatFor(node->variableAccessData()->flushFormat()));
         break;
 
+    case ValueBitOr:
     case ValueBitAnd:
         compileValueBitwiseOp(node);
         break;
 
     case ArithBitAnd:
-    case BitOr:
+    case ArithBitOr:
     case BitXor:
         compileBitwiseOp(node);
         break;
index 054ac30..41c2cdc 100644 (file)
@@ -2075,11 +2075,12 @@ void SpeculativeJIT::compile(Node* node)
         break;
 
     case ValueBitAnd:
+    case ValueBitOr:
         compileValueBitwiseOp(node);
         break;
 
     case ArithBitAnd:
-    case BitOr:
+    case ArithBitOr:
     case BitXor:
         compileBitwiseOp(node);
         break;
index 86ade8c..642f856 100644 (file)
@@ -78,7 +78,7 @@ private:
     void handleNode()
     {
         switch (m_node->op()) {
-        case BitOr:
+        case ArithBitOr:
             handleCommutativity();
 
             if (m_node->child1().useKind() != UntypedUse && m_node->child2()->isInt32Constant() && !m_node->child2()->asInt32()) {
index adb28cf..a644444 100644 (file)
@@ -59,7 +59,7 @@ inline CapabilityLevel canCompile(Node* node)
     case SetArgument:
     case Return:
     case ArithBitAnd:
-    case BitOr:
+    case ArithBitOr:
     case BitXor:
     case BitRShift:
     case BitLShift:
@@ -87,6 +87,7 @@ inline CapabilityLevel canCompile(Node* node)
     case GetGlobalLexicalVariable:
     case PutGlobalVariable:
     case ValueBitAnd:
+    case ValueBitOr:
     case ValueNegate:
     case ValueAdd:
     case StrCat:
index 7ffa052..607d734 100644 (file)
@@ -655,8 +655,11 @@ private:
         case ArithBitAnd:
             compileArithBitAnd();
             break;
-        case DFG::BitOr:
-            compileBitOr();
+        case ValueBitOr:
+            compileValueBitOr();
+            break;
+        case ArithBitOr:
+            compileArithBitOr();
             break;
         case DFG::BitXor:
             compileBitXor();
@@ -2828,12 +2831,22 @@ private:
         setInt32(m_out.bitAnd(lowInt32(m_node->child1()), lowInt32(m_node->child2())));
     }
     
-    void compileBitOr()
+    void compileValueBitOr()
     {
-        if (m_node->isBinaryUseKind(UntypedUse)) {
-            emitBinaryBitOpSnippet<JITBitOrGenerator>(operationValueBitOr);
+        if (m_node->isBinaryUseKind(BigIntUse)) {
+            LValue left = lowBigInt(m_node->child1());
+            LValue right = lowBigInt(m_node->child2());
+
+            LValue result = vmCall(pointerType(), m_out.operation(operationBitOrBigInt), m_callFrame, left, right);
+            setJSValue(result);
             return;
         }
+
+        emitBinaryBitOpSnippet<JITBitOrGenerator>(operationValueBitOr);
+    }
+
+    void compileArithBitOr()
+    {
         setInt32(m_out.bitOr(lowInt32(m_node->child1()), lowInt32(m_node->child2())));
     }
     
index 9e13d31..b9b7b35 100644 (file)
@@ -581,7 +581,7 @@ void JIT::emit_op_bitand(Instruction* currentInstruction)
 
 void JIT::emit_op_bitor(Instruction* currentInstruction)
 {
-    emitBitBinaryOpFastPath<JITBitOrGenerator>(currentInstruction);
+    emitBitBinaryOpFastPath<JITBitOrGenerator>(currentInstruction, ProfilingPolicy::ShouldEmitProfiling);
 }
 
 void JIT::emit_op_bitxor(Instruction* currentInstruction)
index 06865b9..ea971b4 100644 (file)
@@ -1241,7 +1241,7 @@ _llint_op_bitxor:
 
 _llint_op_bitor:
     traceExecution()
-    bitOp(
+    bitOpProfiled(
         macro (left, right) ori left, right end,
         _slow_path_bitor,
         constexpr op_bitor_length)
index 5a0b6bc..5db450b 100644 (file)
@@ -1184,7 +1184,7 @@ _llint_op_bitxor:
 
 _llint_op_bitor:
     traceExecution()
-    bitOp(
+    bitOpProfiled(
         macro (left, right) ori left, right end,
         _slow_path_bitor,
         constexpr op_bitor_length)
index b7e1a32..c26f31b 100644 (file)
@@ -667,7 +667,7 @@ SLOW_PATH_DECL(slow_path_bitand)
             RETURN_PROFILED(op_bitand, result);
         }
 
-        THROW(createTypeError(exec, "Invalid mix of BigInt and other type in bitwise and operation."));
+        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)));
@@ -676,11 +676,21 @@ SLOW_PATH_DECL(slow_path_bitand)
 SLOW_PATH_DECL(slow_path_bitor)
 {
     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::bitwiseOr(vm, WTF::get<JSBigInt*>(leftNumeric), WTF::get<JSBigInt*>(rightNumeric));
+            CHECK_EXCEPTION();
+            RETURN_PROFILED(op_bitor, result);
+        }
+
+        THROW(createTypeError(exec, "Invalid mix of BigInt and other type in bitwise 'or' operation."));
+    }
+
+    RETURN_PROFILED(op_bitor, jsNumber(WTF::get<int32_t>(leftNumeric) | WTF::get<int32_t>(rightNumeric)));
 }
 
 SLOW_PATH_DECL(slow_path_bitxor)
index 29e8bb5..3a6c1b7 100644 (file)
@@ -391,7 +391,7 @@ JSBigInt* JSBigInt::bitwiseAnd(VM& vm, JSBigInt* x, JSBigInt* y)
         JSBigInt* y1 = absoluteSubOne(vm, y, y->length());
         result = absoluteOr(vm, result, y1);
 
-        return absoluteAddOne(vm, result, true);
+        return absoluteAddOne(vm, result, SignOption::Signed);
     }
 
     ASSERT(x->sign() != y->sign());
@@ -403,6 +403,34 @@ JSBigInt* JSBigInt::bitwiseAnd(VM& vm, JSBigInt* x, JSBigInt* y)
     return absoluteAndNot(vm, x, absoluteSubOne(vm, y, y->length()));
 }
 
+JSBigInt* JSBigInt::bitwiseOr(VM& vm, JSBigInt* x, JSBigInt* y)
+{
+    unsigned resultLength = std::max(x->length(), y->length());
+
+    if (!x->sign() && !y->sign())
+        return absoluteOr(vm, x, y);
+    
+    if (x->sign() && y->sign()) {
+        // (-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 = absoluteAnd(vm, result, y1);
+        return absoluteAddOne(vm, result, SignOption::Signed);
+    }
+    
+    ASSERT(x->sign() != y->sign());
+
+    // Assume that x is the positive BigInt.
+    if (x->sign())
+        std::swap(x, y);
+    
+    // x | (-y) == x | ~(y-1) == ~((y-1) &~ x) == -(((y-1) &~ x) + 1)
+    JSBigInt* result = absoluteSubOne(vm, y, resultLength);
+    result = absoluteAndNot(vm, result, x);
+    return absoluteAddOne(vm, result, SignOption::Signed);
+}
+
 #if USE(JSVALUE32_64)
 #define HAVE_TWO_DIGIT 1
 typedef uint64_t TwoDigit;
@@ -1028,7 +1056,8 @@ JSBigInt* JSBigInt::absoluteLeftShiftAlwaysCopy(VM& vm, JSBigInt* x, unsigned sh
 //                   |     |     |     |
 //                   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)
+template<typename BitwiseOp>
+inline JSBigInt* JSBigInt::absoluteBitwiseOp(VM& vm, JSBigInt* x, JSBigInt* y, ExtraDigitsHandling extraDigits, SymmetricOp symmetric, BitwiseOp&& op)
 {
     unsigned xLength = x->length();
     unsigned yLength = y->length();
@@ -1084,7 +1113,7 @@ JSBigInt* JSBigInt::absoluteAndNot(VM& vm, JSBigInt* x, JSBigInt* y)
     return absoluteBitwiseOp(vm, x, y, ExtraDigitsHandling::Copy, SymmetricOp::NotSymmetric, digitOperation);
 }
 
-JSBigInt* JSBigInt::absoluteAddOne(VM& vm, JSBigInt* x, bool sign)
+JSBigInt* JSBigInt::absoluteAddOne(VM& vm, JSBigInt* x, SignOption signOption)
 {
     unsigned inputLength = x->length();
     // The addition will overflow into a new digit if all existing digits are
@@ -1111,7 +1140,7 @@ JSBigInt* JSBigInt::absoluteAddOne(VM& vm, JSBigInt* x, bool sign)
     else
         ASSERT(!carry);
 
-    result->setSign(sign);
+    result->setSign(signOption == SignOption::Signed);
     return result->rightTrim(vm);
 }
 
index 52ff88c..b40f239 100644 (file)
@@ -114,6 +114,7 @@ public:
     static JSBigInt* unaryMinus(VM&, JSBigInt* x);
 
     static JSBigInt* bitwiseAnd(VM&, JSBigInt* x, JSBigInt* y);
+    static JSBigInt* bitwiseOr(VM&, JSBigInt* x, JSBigInt* y);
 
 private:
 
@@ -160,12 +161,19 @@ private:
         NotSymmetric
     };
 
-    static JSBigInt* absoluteBitwiseOp(VM&, JSBigInt* x, JSBigInt* y, ExtraDigitsHandling, SymmetricOp, std::function<Digit(Digit, Digit)> op);
+    template<typename BitwiseOp>
+    static JSBigInt* absoluteBitwiseOp(VM&, JSBigInt* x, JSBigInt* y, ExtraDigitsHandling, SymmetricOp, BitwiseOp&&);
+
     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);
+    enum class SignOption {
+        Signed,
+        Unsigned
+    };
+
+    static JSBigInt* absoluteAddOne(VM&, JSBigInt* x, SignOption);
     static JSBigInt* absoluteSubOne(VM&, JSBigInt* x, unsigned resultLength);
 
     // Digit arithmetic helpers.