[BigInt] Add support to BigInt into ValueAdd
authorticaiolima@gmail.com <ticaiolima@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 8 Nov 2018 01:47:27 +0000 (01:47 +0000)
committerticaiolima@gmail.com <ticaiolima@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 8 Nov 2018 01:47:27 +0000 (01:47 +0000)
https://bugs.webkit.org/show_bug.cgi?id=186177

Reviewed by Keith Miller.

JSTests:

* stress/big-int-negate-jit.js:
* stress/value-add-big-int-and-string.js: Added.
* stress/value-add-big-int-prediction-propagation.js: Added.
* stress/value-add-big-int-untyped.js: Added.

PerformanceTests:

The idea of BigIntBench is to provide a set of microbenchmarks and
benchmarks to evaluate how fast BigInt computations are happening on
JSC implementation.

Now, we are adding microbenchmarks in this set,
but the plan is to move these tests to "JSTest/microbenchmarks" when
BigInt is enabled by default. After that, the focus of Bigint bench is
to provide a set of tests that represents real use cases of BigInt in
JS programs.

* BigIntBench/big-int-add-prediction-propagation.js: Added.
* BigIntBench/big-int-simple-add.js: Added.
* BigIntBench/big-int-simple-sub.js: Added.

Source/JavaScriptCore:

We are adding a very primitive specialization case of BigInts into ValueAdd.
When compiling a speculated version of this node to BigInt, we are currently
calling 'operationAddBigInt', a function that expects only BigInts as
parameter and effectly add numbers using JSBigInt::add. To properly
speculate BigInt operands, we changed ArithProfile to observe when
its result is a BigInt. With this new observation, we are able to identify
when ValueAdd results into a String or BigInt.

Here are some numbers for this specialization running
microbenchmarks:

big-int-simple-add                   21.5411+-1.1096  ^  15.3502+-0.7027  ^ definitely 1.4033x faster
big-int-add-prediction-propagation   13.7762+-0.5578  ^  10.8117+-0.5330  ^ definitely 1.2742x faster

* bytecode/ArithProfile.cpp:
(JSC::ArithProfile::emitObserveResult):
(JSC::ArithProfile::shouldEmitSetNonNumeric const):
(JSC::ArithProfile::shouldEmitSetBigInt const):
(JSC::ArithProfile::emitSetNonNumeric const):
(JSC::ArithProfile::emitSetBigInt const):
(WTF::printInternal):
(JSC::ArithProfile::shouldEmitSetNonNumber const): Deleted.
(JSC::ArithProfile::emitSetNonNumber const): Deleted.
* bytecode/ArithProfile.h:
(JSC::ArithProfile::observedUnaryInt):
(JSC::ArithProfile::observedUnaryNumber):
(JSC::ArithProfile::observedBinaryIntInt):
(JSC::ArithProfile::observedBinaryNumberInt):
(JSC::ArithProfile::observedBinaryIntNumber):
(JSC::ArithProfile::observedBinaryNumberNumber):
(JSC::ArithProfile::didObserveNonInt32 const):
(JSC::ArithProfile::didObserveNonNumeric const):
(JSC::ArithProfile::didObserveBigInt const):
(JSC::ArithProfile::setObservedNonNumeric):
(JSC::ArithProfile::setObservedBigInt):
(JSC::ArithProfile::observeResult):
(JSC::ArithProfile::didObserveNonNumber const): Deleted.
(JSC::ArithProfile::setObservedNonNumber): Deleted.
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::makeSafe):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
* dfg/DFGNode.h:
(JSC::DFG::Node::mayHaveNonNumericResult):
(JSC::DFG::Node::mayHaveBigIntResult):
(JSC::DFG::Node::mayHaveNonNumberResult): Deleted.
* dfg/DFGNodeFlags.cpp:
(JSC::DFG::dumpNodeFlags):
* dfg/DFGNodeFlags.h:
* dfg/DFGOperations.cpp:
* dfg/DFGOperations.h:
* dfg/DFGPredictionPropagationPhase.cpp:
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compileValueAdd):
* ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::compileValueAdd):
* runtime/CommonSlowPaths.cpp:
(JSC::updateArithProfileForUnaryArithOp):
(JSC::updateArithProfileForBinaryArithOp):

Tools:

* Scripts/run-jsc-benchmarks:

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

25 files changed:
JSTests/ChangeLog
JSTests/stress/big-int-negate-jit.js
JSTests/stress/value-add-big-int-and-string.js [new file with mode: 0644]
JSTests/stress/value-add-big-int-prediction-propagation.js [new file with mode: 0644]
JSTests/stress/value-add-big-int-untyped.js [new file with mode: 0644]
PerformanceTests/BigIntBench/big-int-add-prediction-propagation.js [new file with mode: 0644]
PerformanceTests/BigIntBench/big-int-simple-add.js [new file with mode: 0644]
PerformanceTests/BigIntBench/big-int-simple-sub.js [new file with mode: 0644]
PerformanceTests/ChangeLog
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/bytecode/ArithProfile.cpp
Source/JavaScriptCore/bytecode/ArithProfile.h
Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
Source/JavaScriptCore/dfg/DFGNode.h
Source/JavaScriptCore/dfg/DFGNodeFlags.cpp
Source/JavaScriptCore/dfg/DFGNodeFlags.h
Source/JavaScriptCore/dfg/DFGOperations.cpp
Source/JavaScriptCore/dfg/DFGOperations.h
Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp
Source/JavaScriptCore/runtime/CommonSlowPaths.cpp
Tools/ChangeLog
Tools/Scripts/run-jsc-benchmarks

index 2f60247..42c1791 100644 (file)
@@ -1,3 +1,15 @@
+2018-11-07  Caio Lima  <ticaiolima@gmail.com>
+
+        [BigInt] Add support to BigInt into ValueAdd
+        https://bugs.webkit.org/show_bug.cgi?id=186177
+
+        Reviewed by Keith Miller.
+
+        * stress/big-int-negate-jit.js:
+        * stress/value-add-big-int-and-string.js: Added.
+        * stress/value-add-big-int-prediction-propagation.js: Added.
+        * stress/value-add-big-int-untyped.js: Added.
+
 2018-11-07  Tadeu Zagallo  <tzagallo@apple.com>
 
         REGRESSION(r237547): Test failures on 32-bit JSC since the JIT was disabled
index ab49783..6baa8e7 100644 (file)
@@ -44,5 +44,5 @@ for (let i = 0; i < 100000; i++) {
 }
 
 if (numberOfDFGCompiles(mixedSpeculationNegateBigInt) > 1)
-    throw "Failed negateBigInt(). We should have compiled a single negate for the BigInt type.";
+    throw "Failed mixedSpeculationNegateBigInt(). We should have compiled a single negate for the BigInt type.";
 
diff --git a/JSTests/stress/value-add-big-int-and-string.js b/JSTests/stress/value-add-big-int-and-string.js
new file mode 100644 (file)
index 0000000..eb0a4ea
--- /dev/null
@@ -0,0 +1,18 @@
+//@ runBigIntEnabled
+
+function assert(v, e) {
+    if (v !== e)
+        throw new Error("Expected value: " + e + " but got: " + v)
+}
+
+function bigIntOperations(a, b) {
+    let c = a + b;
+    return a + c;
+}
+noInline(bigIntOperations);
+
+for (let i = 0; i < 100000; i++) {
+    let out = bigIntOperations(0b1111n, "16");
+    assert(out, "151516");
+}
+
diff --git a/JSTests/stress/value-add-big-int-prediction-propagation.js b/JSTests/stress/value-add-big-int-prediction-propagation.js
new file mode 100644 (file)
index 0000000..4463711
--- /dev/null
@@ -0,0 +1,18 @@
+//@ runBigIntEnabled
+
+function assert(v, e) {
+    if (v !== e)
+        throw new Error("Expected value: " + e + " but got: " + v)
+}
+
+function bigIntPropagation(a, b) {
+    let c = a + b;
+    return c + 0n;
+}
+noInline(bigIntPropagation);
+
+for (let i = 0; i < 100000; i++) {
+    let out = bigIntPropagation(0xffffffffffffffffffffffffffffffn, 0x1n);
+    assert(out, 0x1000000000000000000000000000000n)
+}
+
diff --git a/JSTests/stress/value-add-big-int-untyped.js b/JSTests/stress/value-add-big-int-untyped.js
new file mode 100644 (file)
index 0000000..02a66d3
--- /dev/null
@@ -0,0 +1,26 @@
+//@ runBigIntEnabled
+
+function assert(v, e) {
+    if (v !== e)
+        throw new Error("Expected value: " + e + " but got: " + v)
+}
+
+function bigIntOperations(a, b) {
+    let c = a + b;
+    return a + c;
+}
+noInline(bigIntOperations);
+
+c = 0;
+let o = { valueOf: function () {
+    c++;
+    return 0b1111n;
+}};
+
+for (let i = 0; i < 100000; i++) {
+    let out = bigIntOperations(o, 0b1010n);
+    assert(out, 40n);
+}
+
+assert(c, 200000);
+
diff --git a/PerformanceTests/BigIntBench/big-int-add-prediction-propagation.js b/PerformanceTests/BigIntBench/big-int-add-prediction-propagation.js
new file mode 100644 (file)
index 0000000..3fd4e5c
--- /dev/null
@@ -0,0 +1,21 @@
+function assert(v, e) {
+    if (v !== e)
+        throw new Error("Expected value: " + e + " but got: " + v)
+}
+
+function bigIntOperations(a, b) {
+    let c = a + b;
+    return c & 0b111111111n;
+}
+noInline(bigIntOperations);
+
+for (let i = 0; i < 100000; i++) {
+    let out = bigIntOperations(0xffffffffffffffffffffffffffffffn, 0x1n);
+    assert(out, 0n)
+}
+
+for (let i = 0; i < 100000; i++) {
+    let out = bigIntOperations(0b111111n, 0b1n);
+    assert(out, 0b1000000n)
+}
+
diff --git a/PerformanceTests/BigIntBench/big-int-simple-add.js b/PerformanceTests/BigIntBench/big-int-simple-add.js
new file mode 100644 (file)
index 0000000..68f883d
--- /dev/null
@@ -0,0 +1,15 @@
+function bigInt(a, b) {
+    let c = a + b;
+    return a + c + b;
+}
+noInline(bigInt);
+
+for (let i = 0; i < 100000; i++) {
+    bigInt(0b1111n, 0b1010n);
+}
+
+let out;
+for (let i = 0; i < 100000; i++) {
+    out = bigInt(0xffffffffffffffffffn, 0xaaffffffffffffffffffn);
+}
+
diff --git a/PerformanceTests/BigIntBench/big-int-simple-sub.js b/PerformanceTests/BigIntBench/big-int-simple-sub.js
new file mode 100644 (file)
index 0000000..c7de284
--- /dev/null
@@ -0,0 +1,17 @@
+function bigInt(a, b) {
+    let c = a - b;
+    return a - c - b;
+}
+noInline(bigInt);
+
+for (let i = 0; i < 100000; i++) {
+    bigInt(0b1111n, 0b1010n);
+}
+
+let out;
+for (let i = 0; i < 100000; i++) {
+    out = bigInt(0xffffffffffffffffffn, 0xaaffffffffffffffffffn);
+}
+
+print(out);
+
index d4891c4..5d89525 100644 (file)
@@ -1,3 +1,24 @@
+2018-11-07  Caio Lima  <ticaiolima@gmail.com>
+
+        [BigInt] Add support to BigInt into ValueAdd
+        https://bugs.webkit.org/show_bug.cgi?id=186177
+
+        Reviewed by Keith Miller.
+
+        The idea of BigIntBench is to provide a set of microbenchmarks and
+        benchmarks to evaluate how fast BigInt computations are happening on
+        JSC implementation.
+
+        Now, we are adding microbenchmarks in this set,
+        but the plan is to move these tests to "JSTest/microbenchmarks" when
+        BigInt is enabled by default. After that, the focus of Bigint bench is
+        to provide a set of tests that represents real use cases of BigInt in
+        JS programs.
+
+        * BigIntBench/big-int-add-prediction-propagation.js: Added.
+        * BigIntBench/big-int-simple-add.js: Added.
+        * BigIntBench/big-int-simple-sub.js: Added.
+
 2018-11-07  Tadeu Zagallo  <tzagallo@apple.com>
 
         REGRESSION(r237547): Test failures on 32-bit JSC since the JIT was disabled
index 4e6e363..32fbfb5 100644 (file)
@@ -1,3 +1,70 @@
+2018-11-07  Caio Lima  <ticaiolima@gmail.com>
+
+        [BigInt] Add support to BigInt into ValueAdd
+        https://bugs.webkit.org/show_bug.cgi?id=186177
+
+        Reviewed by Keith Miller.
+
+        We are adding a very primitive specialization case of BigInts into ValueAdd.
+        When compiling a speculated version of this node to BigInt, we are currently
+        calling 'operationAddBigInt', a function that expects only BigInts as
+        parameter and effectly add numbers using JSBigInt::add. To properly
+        speculate BigInt operands, we changed ArithProfile to observe when
+        its result is a BigInt. With this new observation, we are able to identify
+        when ValueAdd results into a String or BigInt.
+
+        Here are some numbers for this specialization running
+        microbenchmarks:
+
+        big-int-simple-add                   21.5411+-1.1096  ^  15.3502+-0.7027  ^ definitely 1.4033x faster
+        big-int-add-prediction-propagation   13.7762+-0.5578  ^  10.8117+-0.5330  ^ definitely 1.2742x faster
+
+        * bytecode/ArithProfile.cpp:
+        (JSC::ArithProfile::emitObserveResult):
+        (JSC::ArithProfile::shouldEmitSetNonNumeric const):
+        (JSC::ArithProfile::shouldEmitSetBigInt const):
+        (JSC::ArithProfile::emitSetNonNumeric const):
+        (JSC::ArithProfile::emitSetBigInt const):
+        (WTF::printInternal):
+        (JSC::ArithProfile::shouldEmitSetNonNumber const): Deleted.
+        (JSC::ArithProfile::emitSetNonNumber const): Deleted.
+        * bytecode/ArithProfile.h:
+        (JSC::ArithProfile::observedUnaryInt):
+        (JSC::ArithProfile::observedUnaryNumber):
+        (JSC::ArithProfile::observedBinaryIntInt):
+        (JSC::ArithProfile::observedBinaryNumberInt):
+        (JSC::ArithProfile::observedBinaryIntNumber):
+        (JSC::ArithProfile::observedBinaryNumberNumber):
+        (JSC::ArithProfile::didObserveNonInt32 const):
+        (JSC::ArithProfile::didObserveNonNumeric const):
+        (JSC::ArithProfile::didObserveBigInt const):
+        (JSC::ArithProfile::setObservedNonNumeric):
+        (JSC::ArithProfile::setObservedBigInt):
+        (JSC::ArithProfile::observeResult):
+        (JSC::ArithProfile::didObserveNonNumber const): Deleted.
+        (JSC::ArithProfile::setObservedNonNumber): Deleted.
+        * dfg/DFGByteCodeParser.cpp:
+        (JSC::DFG::ByteCodeParser::makeSafe):
+        * dfg/DFGFixupPhase.cpp:
+        (JSC::DFG::FixupPhase::fixupNode):
+        * dfg/DFGNode.h:
+        (JSC::DFG::Node::mayHaveNonNumericResult):
+        (JSC::DFG::Node::mayHaveBigIntResult):
+        (JSC::DFG::Node::mayHaveNonNumberResult): Deleted.
+        * dfg/DFGNodeFlags.cpp:
+        (JSC::DFG::dumpNodeFlags):
+        * dfg/DFGNodeFlags.h:
+        * dfg/DFGOperations.cpp:
+        * dfg/DFGOperations.h:
+        * dfg/DFGPredictionPropagationPhase.cpp:
+        * dfg/DFGSpeculativeJIT.cpp:
+        (JSC::DFG::SpeculativeJIT::compileValueAdd):
+        * ftl/FTLLowerDFGToB3.cpp:
+        (JSC::FTL::DFG::LowerDFGToB3::compileValueAdd):
+        * runtime/CommonSlowPaths.cpp:
+        (JSC::updateArithProfileForUnaryArithOp):
+        (JSC::updateArithProfileForBinaryArithOp):
+
 2018-11-07  Joseph Pecoraro  <pecoraro@apple.com>
 
         Web Inspector: Fix "Javascript" => "JavaScript" enum in protocol generated objects
index 1fa7c79..999933b 100644 (file)
@@ -34,17 +34,28 @@ namespace JSC {
 #if ENABLE(JIT)
 void ArithProfile::emitObserveResult(CCallHelpers& jit, JSValueRegs regs, TagRegistersMode mode)
 {
-    if (!shouldEmitSetDouble() && !shouldEmitSetNonNumber())
+    if (!shouldEmitSetDouble() && !shouldEmitSetNonNumeric() && !shouldEmitSetBigInt())
         return;
 
-    CCallHelpers::Jump isInt32 = jit.branchIfInt32(regs, mode);
+    CCallHelpers::JumpList done;
+    CCallHelpers::JumpList nonNumeric;
+
+    done.append(jit.branchIfInt32(regs, mode));
     CCallHelpers::Jump notDouble = jit.branchIfNotDoubleKnownNotInt32(regs, mode);
     emitSetDouble(jit);
-    CCallHelpers::Jump done = jit.jump();
+    done.append(jit.jump());
+
     notDouble.link(&jit);
-    emitSetNonNumber(jit);
+
+    nonNumeric.append(jit.branchIfNotCell(regs, mode));
+    nonNumeric.append(jit.branchIfNotBigInt(regs.payloadGPR()));
+    emitSetBigInt(jit);
+    done.append(jit.jump());
+
+    nonNumeric.link(&jit);
+    emitSetNonNumeric(jit);
+
     done.link(&jit);
-    isInt32.link(&jit);
 }
 
 bool ArithProfile::shouldEmitSetDouble() const
@@ -59,16 +70,28 @@ void ArithProfile::emitSetDouble(CCallHelpers& jit) const
         jit.or32(CCallHelpers::TrustedImm32(ArithProfile::Int32Overflow | ArithProfile::Int52Overflow | ArithProfile::NegZeroDouble | ArithProfile::NonNegZeroDouble), CCallHelpers::AbsoluteAddress(addressOfBits()));
 }
 
-bool ArithProfile::shouldEmitSetNonNumber() const
+bool ArithProfile::shouldEmitSetNonNumeric() const
+{
+    uint32_t mask = ArithProfile::NonNumeric;
+    return (m_bits & mask) != mask;
+}
+
+bool ArithProfile::shouldEmitSetBigInt() const
 {
-    uint32_t mask = ArithProfile::NonNumber;
+    uint32_t mask = ArithProfile::BigInt;
     return (m_bits & mask) != mask;
 }
 
-void ArithProfile::emitSetNonNumber(CCallHelpers& jit) const
+void ArithProfile::emitSetNonNumeric(CCallHelpers& jit) const
 {
-    if (shouldEmitSetNonNumber())
-        jit.or32(CCallHelpers::TrustedImm32(ArithProfile::NonNumber), CCallHelpers::AbsoluteAddress(addressOfBits()));
+    if (shouldEmitSetNonNumeric())
+        jit.or32(CCallHelpers::TrustedImm32(ArithProfile::NonNumeric), CCallHelpers::AbsoluteAddress(addressOfBits()));
+}
+
+void ArithProfile::emitSetBigInt(CCallHelpers& jit) const
+{
+    if (shouldEmitSetBigInt())
+        jit.or32(CCallHelpers::TrustedImm32(ArithProfile::BigInt), CCallHelpers::AbsoluteAddress(addressOfBits()));
 }
 #endif // ENABLE(JIT)
 
@@ -95,8 +118,8 @@ void printInternal(PrintStream& out, const ArithProfile& profile)
             out.print(separator, "NonNegZeroDouble");
             separator = "|";
         }
-        if (profile.didObserveNonNumber()) {
-            out.print(separator, "NonNumber");
+        if (profile.didObserveNonNumeric()) {
+            out.print(separator, "NonNumeric");
             separator = "|";
         }
         if (profile.didObserveInt32Overflow()) {
@@ -107,6 +130,10 @@ void printInternal(PrintStream& out, const ArithProfile& profile)
             out.print(separator, "Int52Overflow");
             separator = "|";
         }
+        if (profile.didObserveBigInt()) {
+            out.print(separator, "BigInt");
+            separator = "|";
+        }
     }
     if (profile.tookSpecialFastPath())
         out.print(separator, "Took special fast path.");
index 645c258..b1bda5b 100644 (file)
@@ -68,7 +68,7 @@ private:
 
 struct ArithProfile {
 private:
-    static constexpr uint32_t numberOfFlagBits = 5;
+    static constexpr uint32_t numberOfFlagBits = 6;
     static constexpr uint32_t rhsResultTypeShift = numberOfFlagBits;
     static constexpr uint32_t lhsResultTypeShift = rhsResultTypeShift + ResultType::numBitsNeeded;
     static constexpr uint32_t rhsObservedTypeShift = lhsResultTypeShift + ResultType::numBitsNeeded;
@@ -121,21 +121,21 @@ public:
     {
         constexpr ObservedType observedInt32 { ObservedType().withInt32() };
         constexpr uint32_t bits = observedInt32.bits() << lhsObservedTypeShift;
-        static_assert(bits == 0x400000, "");
+        static_assert(bits == 0x800000, "");
         return fromInt(bits);
     }
     static constexpr ArithProfile observedUnaryNumber()
     {
         constexpr ObservedType observedNumber { ObservedType().withNumber() };
         constexpr uint32_t bits = observedNumber.bits() << lhsObservedTypeShift;
-        static_assert(bits == 0x800000, "");
+        static_assert(bits == 0x1000000, "");
         return fromInt(bits);
     }
     static constexpr ArithProfile observedBinaryIntInt()
     {
         constexpr ObservedType observedInt32 { ObservedType().withInt32() };
         constexpr uint32_t bits = (observedInt32.bits() << lhsObservedTypeShift) | (observedInt32.bits() << rhsObservedTypeShift);
-        static_assert(bits == 0x480000, "");
+        static_assert(bits == 0x900000, "");
         return fromInt(bits);
     }
     static constexpr ArithProfile observedBinaryNumberInt()
@@ -143,7 +143,7 @@ public:
         constexpr ObservedType observedNumber { ObservedType().withNumber() };
         constexpr ObservedType observedInt32 { ObservedType().withInt32() };
         constexpr uint32_t bits = (observedNumber.bits() << lhsObservedTypeShift) | (observedInt32.bits() << rhsObservedTypeShift);
-        static_assert(bits == 0x880000, "");
+        static_assert(bits == 0x1100000, "");
         return fromInt(bits);
     }
     static constexpr ArithProfile observedBinaryIntNumber()
@@ -151,23 +151,24 @@ public:
         constexpr ObservedType observedNumber { ObservedType().withNumber() };
         constexpr ObservedType observedInt32 { ObservedType().withInt32() };
         constexpr uint32_t bits = (observedInt32.bits() << lhsObservedTypeShift) | (observedNumber.bits() << rhsObservedTypeShift);
-        static_assert(bits == 0x500000, "");
+        static_assert(bits == 0xa00000, "");
         return fromInt(bits);
     }
     static constexpr ArithProfile observedBinaryNumberNumber()
     {
         constexpr ObservedType observedNumber { ObservedType().withNumber() };
         constexpr uint32_t bits = (observedNumber.bits() << lhsObservedTypeShift) | (observedNumber.bits() << rhsObservedTypeShift);
-        static_assert(bits == 0x900000, "");
+        static_assert(bits == 0x1200000, "");
         return fromInt(bits);
     }
 
     enum ObservedResults {
         NonNegZeroDouble = 1 << 0,
         NegZeroDouble    = 1 << 1,
-        NonNumber        = 1 << 2,
+        NonNumeric       = 1 << 2,
         Int32Overflow    = 1 << 3,
         Int52Overflow    = 1 << 4,
+        BigInt           = 1 << 5,
     };
 
     ResultType lhsResultType() const { return ResultType((m_bits >> lhsResultTypeShift) & resultTypeMask); }
@@ -195,17 +196,19 @@ public:
 
     bool tookSpecialFastPath() const { return m_bits & specialFastPathBit; }
 
-    bool didObserveNonInt32() const { return hasBits(NonNegZeroDouble | NegZeroDouble | NonNumber); }
+    bool didObserveNonInt32() const { return hasBits(NonNegZeroDouble | NegZeroDouble | NonNumeric | BigInt); }
     bool didObserveDouble() const { return hasBits(NonNegZeroDouble | NegZeroDouble); }
     bool didObserveNonNegZeroDouble() const { return hasBits(NonNegZeroDouble); }
     bool didObserveNegZeroDouble() const { return hasBits(NegZeroDouble); }
-    bool didObserveNonNumber() const { return hasBits(NonNumber); }
+    bool didObserveNonNumeric() const { return hasBits(NonNumeric); }
+    bool didObserveBigInt() const { return hasBits(BigInt); }
     bool didObserveInt32Overflow() const { return hasBits(Int32Overflow); }
     bool didObserveInt52Overflow() const { return hasBits(Int52Overflow); }
 
     void setObservedNonNegZeroDouble() { setBit(NonNegZeroDouble); }
     void setObservedNegZeroDouble() { setBit(NegZeroDouble); }
-    void setObservedNonNumber() { setBit(NonNumber); }
+    void setObservedNonNumeric() { setBit(NonNumeric); }
+    void setObservedBigInt() { setBit(BigInt); }
     void setObservedInt32Overflow() { setBit(Int32Overflow); }
     void setObservedInt52Overflow() { setBit(Int52Overflow); }
 
@@ -219,7 +222,11 @@ public:
             m_bits |= Int32Overflow | Int52Overflow | NonNegZeroDouble | NegZeroDouble;
             return;
         }
-        m_bits |= NonNumber;
+        if (value && value.isBigInt()) {
+            m_bits |= BigInt;
+            return;
+        }
+        m_bits |= NonNumeric;
     }
 
     void lhsSawInt32() { setLhsObservedType(lhsObservedType().withInt32()); }
@@ -261,7 +268,7 @@ public:
 
 #if ENABLE(JIT)    
     // Sets (Int32Overflow | Int52Overflow | NonNegZeroDouble | NegZeroDouble) if it sees a
-    // double. Sets NonNumber if it sees a non-number.
+    // double. Sets NonNumeric if it sees a non-numeric.
     void emitObserveResult(CCallHelpers&, JSValueRegs, TagRegistersMode = HaveTagRegisters);
     
     // Sets (Int32Overflow | Int52Overflow | NonNegZeroDouble | NegZeroDouble).
@@ -269,8 +276,12 @@ public:
     void emitSetDouble(CCallHelpers&) const;
     
     // Sets NonNumber.
-    void emitSetNonNumber(CCallHelpers&) const;
-    bool shouldEmitSetNonNumber() const;
+    void emitSetNonNumeric(CCallHelpers&) const;
+    bool shouldEmitSetNonNumeric() const;
+
+    // Sets BigInt
+    void emitSetBigInt(CCallHelpers&) const;
+    bool shouldEmitSetBigInt() const;
 #endif // ENABLE(JIT)
 
     constexpr uint32_t bits() const { return m_bits; }
index 43ec0fd..dca4d58 100644 (file)
@@ -941,8 +941,10 @@ private:
                 case ValueAdd:
                     if (arithProfile->didObserveDouble())
                         node->mergeFlags(NodeMayHaveDoubleResult);
-                    if (arithProfile->didObserveNonNumber())
-                        node->mergeFlags(NodeMayHaveNonNumberResult);
+                    if (arithProfile->didObserveNonNumeric())
+                        node->mergeFlags(NodeMayHaveNonNumericResult);
+                    if (arithProfile->didObserveBigInt())
+                        node->mergeFlags(NodeMayHaveBigIntResult);
                     break;
                 
                 case ArithMul: {
@@ -954,8 +956,8 @@ private:
                         node->mergeFlags(NodeMayNegZeroInBaseline);
                     if (arithProfile->didObserveDouble())
                         node->mergeFlags(NodeMayHaveDoubleResult);
-                    if (arithProfile->didObserveNonNumber())
-                        node->mergeFlags(NodeMayHaveNonNumberResult);
+                    if (arithProfile->didObserveNonNumeric())
+                        node->mergeFlags(NodeMayHaveNonNumericResult);
                     break;
                 }
                 case ValueNegate:
@@ -966,11 +968,10 @@ private:
                         node->mergeFlags(NodeMayNegZeroInBaseline);
                     if (arithProfile->didObserveInt32Overflow() || m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, Overflow))
                         node->mergeFlags(NodeMayOverflowInt32InBaseline);
-                    if (arithProfile->didObserveNonNumber()) {
-                        // FIXME: We should add support to BigInt into speculation
-                        // https://bugs.webkit.org/show_bug.cgi?id=182470
-                        node->mergeFlags(NodeMayHaveNonNumberResult);
-                    }
+                    if (arithProfile->didObserveNonNumeric())
+                        node->mergeFlags(NodeMayHaveNonNumericResult);
+                    if (arithProfile->didObserveBigInt())
+                        node->mergeFlags(NodeMayHaveBigIntResult);
                     break;
                 }
                 
index 91a26dd..937b2a1 100644 (file)
@@ -287,8 +287,14 @@ private:
                 }
             }
 
-            fixEdge<UntypedUse>(child1);
-            fixEdge<UntypedUse>(child2);
+            if (Node::shouldSpeculateBigInt(child1.node(), child2.node())) {
+                fixEdge<BigIntUse>(child1);
+                fixEdge<BigIntUse>(child2);
+            } else {
+                fixEdge<UntypedUse>(child1);
+                fixEdge<UntypedUse>(child2);
+            }
+
             node->setResult(NodeResultJS);
             break;
         }
index 7d58930..9952806 100644 (file)
@@ -1144,9 +1144,14 @@ public:
         return m_flags & NodeMayHaveDoubleResult;
     }
     
-    bool mayHaveNonNumberResult()
+    bool mayHaveNonNumericResult()
     {
-        return m_flags & NodeMayHaveNonNumberResult;
+        return m_flags & NodeMayHaveNonNumericResult;
+    }
+
+    bool mayHaveBigIntResult()
+    {
+        return m_flags & NodeMayHaveBigIntResult;
     }
 
     bool hasNewArrayBufferData()
@@ -2869,7 +2874,7 @@ private:
 
     unsigned m_index { std::numeric_limits<unsigned>::max() };
     unsigned m_op : 10; // real type is NodeType
-    unsigned m_flags : 20;
+    unsigned m_flags : 21;
     // The virtual register number (spill location) associated with this .
     VirtualRegister m_virtualRegister;
     // The number of uses of the result of this operation (+1 for 'must generate' nodes, which have side-effects).
index 3408421..a091c09 100644 (file)
@@ -88,8 +88,11 @@ void dumpNodeFlags(PrintStream& actualOut, NodeFlags flags)
     if (flags & NodeMayHaveDoubleResult)
         out.print(comma, "MayHaveDoubleResult");
 
-    if (flags & NodeMayHaveNonNumberResult)
-        out.print(comma, "MayHaveNonNumberResult");
+    if (flags & NodeMayHaveBigIntResult)
+        out.print(comma, "MayHaveBigIntResult");
+
+    if (flags & NodeMayHaveNonNumericResult)
+        out.print(comma, "MayHaveNonNumericResult");
 
     if (flags & NodeMayOverflowInt52)
         out.print(comma, "MayOverflowInt52");
index 4abaddf..7e96a26 100644 (file)
@@ -46,31 +46,32 @@ namespace JSC { namespace DFG {
 #define NodeMustGenerate                 0x0008 // set on nodes that have side effects, and may not trivially be removed by DCE.
 #define NodeHasVarArgs                   0x0010
     
-#define NodeBehaviorMask                 0x07e0
-#define NodeMayHaveDoubleResult          0x0020
-#define NodeMayOverflowInt52             0x0040
-#define NodeMayOverflowInt32InBaseline   0x0080
-#define NodeMayOverflowInt32InDFG        0x0100
-#define NodeMayNegZeroInBaseline         0x0200
-#define NodeMayNegZeroInDFG              0x0400
-#define NodeMayHaveNonNumberResult       0x0800
-#define NodeMayHaveNonIntResult          (NodeMayHaveDoubleResult | NodeMayHaveNonNumberResult)
+#define NodeBehaviorMask                 0x007e0
+#define NodeMayHaveDoubleResult          0x00020
+#define NodeMayOverflowInt52             0x00040
+#define NodeMayOverflowInt32InBaseline   0x00080
+#define NodeMayOverflowInt32InDFG        0x00100
+#define NodeMayNegZeroInBaseline         0x00200
+#define NodeMayNegZeroInDFG              0x00400
+#define NodeMayHaveNonNumericResult      0x00800
+#define NodeMayHaveBigIntResult          0x01000
+#define NodeMayHaveNonIntResult          (NodeMayHaveDoubleResult | NodeMayHaveNonNumericResult | NodeMayHaveBigIntResult)
                                 
-#define NodeBytecodeBackPropMask        0x1f000
+#define NodeBytecodeBackPropMask        0x3e000
 #define NodeBytecodeUseBottom           0x00000
-#define NodeBytecodeUsesAsNumber        0x01000 // The result of this computation may be used in a context that observes fractional, or bigger-than-int32, results.
-#define NodeBytecodeNeedsNegZero        0x02000 // The result of this computation may be used in a context that observes -0.
-#define NodeBytecodeUsesAsOther         0x04000 // The result of this computation may be used in a context that distinguishes between NaN and other things (like undefined).
+#define NodeBytecodeUsesAsNumber        0x02000 // The result of this computation may be used in a context that observes fractional, or bigger-than-int32, results.
+#define NodeBytecodeNeedsNegZero        0x04000 // The result of this computation may be used in a context that observes -0.
+#define NodeBytecodeUsesAsOther         0x08000 // The result of this computation may be used in a context that distinguishes between NaN and other things (like undefined).
 #define NodeBytecodeUsesAsValue         (NodeBytecodeUsesAsNumber | NodeBytecodeNeedsNegZero | NodeBytecodeUsesAsOther)
-#define NodeBytecodeUsesAsInt           0x08000 // The result of this computation is known to be used in a context that prefers, but does not require, integer values.
-#define NodeBytecodeUsesAsArrayIndex    0x10000 // The result of this computation is known to be used in a context that strongly prefers integer values, to the point that we should avoid using doubles if at all possible.
+#define NodeBytecodeUsesAsInt           0x10000 // The result of this computation is known to be used in a context that prefers, but does not require, integer values.
+#define NodeBytecodeUsesAsArrayIndex    0x20000 // The result of this computation is known to be used in a context that strongly prefers integer values, to the point that we should avoid using doubles if at all possible.
 
 #define NodeArithFlagsMask               (NodeBehaviorMask | NodeBytecodeBackPropMask)
 
-#define NodeIsFlushed                   0x20000 // Computed by CPSRethreadingPhase, will tell you which local nodes are backwards-reachable from a Flush.
+#define NodeIsFlushed                   0x40000 // Computed by CPSRethreadingPhase, will tell you which local nodes are backwards-reachable from a Flush.
 
-#define NodeMiscFlag1                   0x40000
-#define NodeMiscFlag2                   0x80000
+#define NodeMiscFlag1                   0x80000
+#define NodeMiscFlag2                   0x100000
 
 typedef uint32_t NodeFlags;
 
index d30483b..df3fd1e 100644 (file)
@@ -1301,11 +1301,22 @@ JSCell* JIT_OPERATION operationBitAndBigInt(ExecState* exec, JSCell* op1, JSCell
 {
     VM* vm = &exec->vm();
     NativeCallFrameTracer tracer(vm, exec);
+
+    JSBigInt* leftOperand = jsCast<JSBigInt*>(op1);
+    JSBigInt* rightOperand = jsCast<JSBigInt*>(op2);
+
+    return JSBigInt::bitwiseAnd(*vm, leftOperand, rightOperand);
+}
+
+JSCell* JIT_OPERATION operationAddBigInt(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);
+    return JSBigInt::add(*vm, leftOperand, rightOperand);
 }
 
 JSCell* JIT_OPERATION operationBitOrBigInt(ExecState* exec, JSCell* op1, JSCell* op2)
index 2a294a7..3498791 100644 (file)
@@ -166,6 +166,7 @@ size_t JIT_OPERATION operationCompareStrictEqCell(ExecState*, JSCell* op1, JSCel
 JSCell* JIT_OPERATION operationSubBigInt(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;
+JSCell* JIT_OPERATION operationAddBigInt(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 0d71293..fe89b98 100644 (file)
@@ -195,11 +195,15 @@ private:
                 } else if (isStringOrStringObjectSpeculation(left) || isStringOrStringObjectSpeculation(right)) {
                     // left or right is definitely something other than a number.
                     changed |= mergePrediction(SpecString);
-                } else {
+                } else if (isBigIntSpeculation(left) && isBigIntSpeculation(right))
+                    changed |= mergePrediction(SpecBigInt);
+                else {
                     changed |= mergePrediction(SpecInt32Only);
                     if (node->mayHaveDoubleResult())
                         changed |= mergePrediction(SpecBytecodeDouble);
-                    if (node->mayHaveNonNumberResult())
+                    if (node->mayHaveBigIntResult())
+                        changed |= mergePrediction(SpecBigInt);
+                    if (node->mayHaveNonNumericResult())
                         changed |= mergePrediction(SpecString);
                 }
             }
@@ -265,7 +269,7 @@ private:
                     changed |= mergePrediction(SpecInt32Only);
                     if (node->mayHaveDoubleResult())
                         changed |= mergePrediction(SpecBytecodeDouble);
-                    if (node->mayHaveNonNumberResult())
+                    if (node->mayHaveBigIntResult())
                         changed |= mergePrediction(SpecBigInt);
                 }
             }
@@ -292,11 +296,8 @@ private:
                     changed |= mergePrediction(speculatedDoubleTypeForPrediction(node->child1()->prediction()));
                 else {
                     changed |= mergePrediction(SpecInt32Only);
-                    if (node->op() == ValueNegate && node->mayHaveNonNumberResult()) {
-                        // FIXME: We should add support to BigInt into speculatio
-                        // https://bugs.webkit.org/show_bug.cgi?id=182470
+                    if (node->op() == ValueNegate && node->mayHaveBigIntResult())
                         changed |= mergePrediction(SpecBigInt);
-                    }
                     if (node->mayHaveDoubleResult())
                         changed |= mergePrediction(SpecBytecodeDouble);
                 }
index 02b1a7f..fb4c5d7 100644 (file)
@@ -3837,6 +3837,25 @@ void SpeculativeJIT::compileValueAdd(Node* node)
     Edge& leftChild = node->child1();
     Edge& rightChild = node->child2();
 
+    if (node->isBinaryUseKind(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(operationAddBigInt, resultGPR, leftGPR, rightGPR);
+        m_jit.exceptionCheck();
+
+        cellResult(resultGPR, node);
+        return;
+    }
+
     if (isKnownNotNumber(leftChild.node()) || isKnownNotNumber(rightChild.node())) {
         JSValueOperand left(this, leftChild);
         JSValueOperand right(this, rightChild);
index 3825ad7..a538990 100644 (file)
@@ -1862,6 +1862,15 @@ private:
 
     void compileValueAdd()
     {
+        if (m_node->isBinaryUseKind(BigIntUse)) {
+            LValue left = lowBigInt(m_node->child1());
+            LValue right = lowBigInt(m_node->child2());
+
+            LValue result = vmCall(pointerType(), m_out.operation(operationAddBigInt), m_callFrame, left, right);
+            setJSValue(result);
+            return;
+        }
+
         CodeBlock* baselineCodeBlock = m_ftlState.graph.baselineCodeBlockFor(m_node->origin.semantic);
         ArithProfile* arithProfile = baselineCodeBlock->arithProfileForBytecodeOffset(m_node->origin.semantic.bytecodeIndex);
         const Instruction* instruction = baselineCodeBlock->instructions().at(m_node->origin.semantic.bytecodeIndex).ptr();
index 70e3449..5163c33 100644 (file)
@@ -418,8 +418,10 @@ static void updateArithProfileForUnaryArithOp(OpNegate::Metadata& metadata, JSVa
                     profile.setObservedInt52Overflow();
             }
         }
-    } else
-        profile.setObservedNonNumber();
+    } else if (result.isBigInt())
+        profile.setObservedBigInt();
+    else
+        profile.setObservedNonNumeric();
 }
 #else
 static void updateArithProfileForUnaryArithOp(OpNegate::Metadata&, JSValue, JSValue) { }
@@ -474,8 +476,10 @@ static void updateArithProfileForBinaryArithOp(ExecState* exec, const Instructio
                     profile.setObservedInt52Overflow();
             }
         }
-    } else
-        profile.setObservedNonNumber();
+    } else if (result.isBigInt())
+        profile.setObservedBigInt();
+    else 
+        profile.setObservedNonNumeric();
 }
 #else
 static void updateArithProfileForBinaryArithOp(ExecState*, const Instruction*, JSValue, JSValue, JSValue) { }
index 48ece3a..84e5b36 100644 (file)
@@ -1,3 +1,12 @@
+2018-11-07  Caio Lima  <ticaiolima@gmail.com>
+
+        [BigInt] Add support to BigInt into ValueAdd
+        https://bugs.webkit.org/show_bug.cgi?id=186177
+
+        Reviewed by Keith Miller.
+
+        * Scripts/run-jsc-benchmarks:
+
 2018-11-07  Sihui Liu  <sihui_liu@apple.com>
 
         RELEASE_ASSERT(!m_hardClosedForUserDelete) fails in WebCore::IDBServer::UniqueIDBDatabase::invokeOperationAndTransactionTimer
index 4da1c6c..2ccfe92 100755 (executable)
@@ -49,6 +49,7 @@ SUNSPIDER_PATH = PERFORMANCETESTS_PATH + "SunSpider" + "tests" + "sunspider-1.0"
 LONGSPIDER_PATH = PERFORMANCETESTS_PATH + "LongSpider"
 V8_PATH = PERFORMANCETESTS_PATH + "SunSpider" + "tests" + "v8-v6"
 TAILBENCH_PATH = PERFORMANCETESTS_PATH + "TailBench9000"
+BIGINTBENCH_PATH = PERFORMANCETESTS_PATH + "BigIntBench"
 MICROBENCHMARKS_PATH = OPENSOURCE_PATH + "JSTests" + "microbenchmarks"
 OPENSOURCE_OCTANE_PATH = PERFORMANCETESTS_PATH + "Octane"
 OCTANE_WRAPPER_PATH = OPENSOURCE_OCTANE_PATH + "wrappers"
@@ -231,6 +232,7 @@ $includeOctane=true
 $includeCompressionBench = false
 $includeSixSpeed = false
 $includeTailBench = true
+$includeBigIntBench = false
 $measureGC=false
 $benchmarkPattern=nil
 $verbosity=0
@@ -1745,6 +1747,23 @@ class TailBenchBenchmark
   end
 end
 
+class BigIntBenchBenchmark
+  include Benchmark
+
+  def initialize(name)
+    @name = name
+  end
+
+  def emitRunCode(plan)
+    emitBenchRunCode(fullname, plan, SingleFileTimedBenchmarkParameters.new(ensureFile("BigIntBench-#{@name}", "#{BIGINTBENCH_PATH}/#{@name}.js")))
+  end
+
+  def environment
+    {"JSC_useBigInt" => "true"}
+  end
+end
+
+
 class MicrobenchmarksBenchmark
   include Benchmark
   
@@ -2828,6 +2847,7 @@ begin
                  ['--compression-bench', GetoptLong::NO_ARGUMENT],
                  ['--six-speed', GetoptLong::NO_ARGUMENT],
                  ['--tail-bench', GetoptLong::NO_ARGUMENT],
+                 ['--big-int-bench', GetoptLong::NO_ARGUMENT],
                  ['--benchmarks', GetoptLong::REQUIRED_ARGUMENT],
                  ['--measure-gc', GetoptLong::OPTIONAL_ARGUMENT],
                  ['--force-vm-kind', GetoptLong::REQUIRED_ARGUMENT],
@@ -2940,6 +2960,9 @@ begin
     when '--six-speed'
       resetBenchOptionsIfNecessary
       $includeSixSpeed = true
+    when '--big-int-bench'
+      resetBenchOptionsIfNecessary
+      $includeBigIntBench = true
     when '--benchmarks'
       $benchmarkPattern = Regexp.new(arg)
     when '--measure-gc'
@@ -3168,6 +3191,15 @@ begin
     TAILBENCH.add TailBenchBenchmark.new(name);
   }
 
+  BIGINTBENCH = BenchmarkSuite.new("BigIntBench", :geometricMean, 0)
+  Dir.foreach(BIGINTBENCH_PATH) {
+    | filename |
+    if filename =~ /\.js$/
+      name = $~.pre_match
+      BIGINTBENCH.add BigIntBenchBenchmark.new(name)
+    end
+  }
+
   MICROBENCHMARKS = BenchmarkSuite.new("Microbenchmarks", :geometricMean, 0)
   Dir.foreach(MICROBENCHMARKS_PATH) {
     | filename |
@@ -3322,6 +3354,10 @@ begin
     $suites << MICROBENCHMARKS
   end
 
+  if $includeBigIntBench and not BIGINTBENCH.empty?
+    $suites << BIGINTBENCH
+  end
+
   if $includeAsmBench and not ASMBENCH.empty?
     if ASMBENCH_PATH
       $suites << ASMBENCH