[ESNext][BigInt] Implement support for "**"
authorticaiolima@gmail.com <ticaiolima@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 3 Jun 2019 18:42:34 +0000 (18:42 +0000)
committerticaiolima@gmail.com <ticaiolima@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 3 Jun 2019 18:42:34 +0000 (18:42 +0000)
https://bugs.webkit.org/show_bug.cgi?id=190799

Reviewed by Saam Barati.

JSTests:

* stress/big-int-exp-basic.js: Added.
* stress/big-int-exp-jit-osr.js: Added.
* stress/big-int-exp-jit-untyped.js: Added.
* stress/big-int-exp-jit.js: Added.
* stress/big-int-exp-negative-exponent.js: Added.
* stress/big-int-exp-to-primitive.js: Added.
* stress/big-int-exp-type-error.js: Added.
* stress/big-int-exp-wrapped-value.js: Added.
* stress/value-pow-ai-rule.js: Added.

Source/JavaScriptCore:

We are introducing support for BigInt into "**" operator. This Patch
also includes changes into DFG, introducing a new node "ValuePow" that
is responsible to handle UntypedUse and BigIntUse.

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

ValuePow(Untyped, Untyped) still can propagate constant if AI proves
it. We are doing so if AI proves rhs and lhs as numbers.

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

When compiling op_pow, we first verify if rhs and lhs can be any Int
or number. If this happen, we emit ArithPow, otherwise we fallback to
ValuePow and rely on fixup to convert it to ArithPow if possible.

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

We only clobberize world if ValuePow is UntypedUse. Otherwise, we can
properly support CSE.

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

JSBigInt::exponentiate allocates JSBigInts to perform calculation and
it can trigger GC. ValuePow(UntypedUse) can trigger GC because it can
execute user code.

* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupArithPow):
(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::compileValuePow):
* dfg/DFGSpeculativeJIT.h:
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGValidate.cpp:
* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::compileNode):
(JSC::FTL::DFG::LowerDFGToB3::compileValuePow):
* runtime/CommonSlowPaths.cpp:
(JSC::SLOW_PATH_DECL):

We are adding proper support to BigInt on op_pow. The specification
defines that we can only apply pow when both operands have the same
type after calling ToNumeric().

* runtime/JSBigInt.cpp:
(JSC::JSBigInt::exponentiate):
* runtime/JSBigInt.h:

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

31 files changed:
JSTests/ChangeLog
JSTests/stress/big-int-exp-basic.js [new file with mode: 0644]
JSTests/stress/big-int-exp-jit-osr.js [new file with mode: 0644]
JSTests/stress/big-int-exp-jit-untyped.js [new file with mode: 0644]
JSTests/stress/big-int-exp-jit.js [new file with mode: 0644]
JSTests/stress/big-int-exp-negative-exponent.js [new file with mode: 0644]
JSTests/stress/big-int-exp-to-primitive.js [new file with mode: 0644]
JSTests/stress/big-int-exp-type-error.js [new file with mode: 0644]
JSTests/stress/big-int-exp-wrapped-value.js [new file with mode: 0644]
JSTests/stress/value-pow-ai-rule.js [new file with mode: 0644]
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
Source/JavaScriptCore/dfg/DFGClobberize.h
Source/JavaScriptCore/dfg/DFGDoesGC.cpp
Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
Source/JavaScriptCore/dfg/DFGNodeType.h
Source/JavaScriptCore/dfg/DFGOperations.cpp
Source/JavaScriptCore/dfg/DFGOperations.h
Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
Source/JavaScriptCore/dfg/DFGSafeToExecute.h
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
Source/JavaScriptCore/dfg/DFGValidate.cpp
Source/JavaScriptCore/ftl/FTLCapabilities.cpp
Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp
Source/JavaScriptCore/runtime/CommonSlowPaths.cpp
Source/JavaScriptCore/runtime/JSBigInt.cpp
Source/JavaScriptCore/runtime/JSBigInt.h

index 4aad595..bb67886 100644 (file)
@@ -1,3 +1,20 @@
+2019-06-03  Caio Lima  <ticaiolima@gmail.com>
+
+        [ESNext][BigInt] Implement support for "**"
+        https://bugs.webkit.org/show_bug.cgi?id=190799
+
+        Reviewed by Saam Barati.
+
+        * stress/big-int-exp-basic.js: Added.
+        * stress/big-int-exp-jit-osr.js: Added.
+        * stress/big-int-exp-jit-untyped.js: Added.
+        * stress/big-int-exp-jit.js: Added.
+        * stress/big-int-exp-negative-exponent.js: Added.
+        * stress/big-int-exp-to-primitive.js: Added.
+        * stress/big-int-exp-type-error.js: Added.
+        * stress/big-int-exp-wrapped-value.js: Added.
+        * stress/value-pow-ai-rule.js: Added.
+
 2019-05-30  Tadeu Zagallo  <tzagallo@apple.com> and Yusuke Suzuki  <ysuzuki@apple.com>
 
         [JSC] Implement op_wide16 / op_wide32 and introduce 16bit version bytecode
diff --git a/JSTests/stress/big-int-exp-basic.js b/JSTests/stress/big-int-exp-basic.js
new file mode 100644 (file)
index 0000000..c815964
--- /dev/null
@@ -0,0 +1,111 @@
+//@ runBigIntEnabled
+
+// Copyright (C) 2017 Josh Wolfe. All rights reserved.
+// This code is governed by the BSD license below.
+//
+// The << Software identified by reference to the Ecma Standard* ("Software)">>  is protected by copyright and is being
+// made available under the  "BSD License", included below. This Software may be subject to third party rights (rights
+// from parties other than Ecma International), including patent rights, and no licenses under such third party rights
+// are granted under this license even if the third party concerned is a member of Ecma International.  SEE THE ECMA
+// CODE OF CONDUCT IN PATENT MATTERS AVAILABLE AT http://www.ecma-international.org/memento/codeofconduct.htm FOR
+// INFORMATION REGARDING THE LICENSING OF PATENT CLAIMS THAT ARE REQUIRED TO IMPLEMENT ECMA INTERNATIONAL STANDARDS*.
+//
+// Copyright (C) 2012-2013 Ecma International
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
+// following conditions are met:
+// 1.   Redistributions of source code must retain the above copyright notice, this list of conditions and the following
+//      disclaimer.
+// 2.   Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
+//      following disclaimer in the documentation and/or other materials provided with the distribution.
+// 3.   Neither the name of the authors nor Ecma International may be used to endorse or promote products derived from
+//      this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE ECMA INTERNATIONAL "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+// SHALL ECMA INTERNATIONAL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+// DAMAGE.
+//
+// * Ecma International Standards hereafter means Ecma International Standards as well as Ecma Technical Reports
+
+var assert = {
+    sameValue: function (input, expected, message) {
+    if (input !== expected)
+        throw new Error(message);
+    }
+};
+
+assert.sameValue(
+  0x123n ** 0x123n,
+  0x37AA7FAA38F2F6026AABEFE979EA730BA9EA4CB99E2E3F645D515D3BBE2D84BCD89F8A034BADF3E3DC0CF417258371B31F4555DC0883DA96760AB157DB7DFFF5E3E97A3EAAB8328B2B178060B5A5E4C4DD8BC8D66B7F4D9F0E0B1AC3A566FDE0A15EBF8DBDBD0565C5FBDB7C123CF250E271DF5C38BC6746A1327F09C7FB4B96E0EDA45C429799CA80B1DB039692C70DFFE4E66F1D9CAB4270863B09A7918F774D686F685F560FEDC6B7B85CB45DE5EEEF6A5FE2FC8B5037FB421204641909347C91F2DC252F49B8F310E867E56D1CA2E81EE9A3AA568682C7B8B41D709A2E7F8D9A8D8C56D6BE78B6CA8365E362B81A64974C315FB8FA50CED4F7944F28FA3ECA77B8BCB56DC69814328F891E6065D108EEE7B8E695038090CCA10C0E68DD7A36CFAA1C26CDFEC369FBn,
+  'The result of (0x123n ** 0x123n) is 0x37AA7FAA38F2F6026AABEFE979EA730BA9EA4CB99E2E3F645D515D3BBE2D84BCD89F8A034BADF3E3DC0CF417258371B31F4555DC0883DA96760AB157DB7DFFF5E3E97A3EAAB8328B2B178060B5A5E4C4DD8BC8D66B7F4D9F0E0B1AC3A566FDE0A15EBF8DBDBD0565C5FBDB7C123CF250E271DF5C38BC6746A1327F09C7FB4B96E0EDA45C429799CA80B1DB039692C70DFFE4E66F1D9CAB4270863B09A7918F774D686F685F560FEDC6B7B85CB45DE5EEEF6A5FE2FC8B5037FB421204641909347C91F2DC252F49B8F310E867E56D1CA2E81EE9A3AA568682C7B8B41D709A2E7F8D9A8D8C56D6BE78B6CA8365E362B81A64974C315FB8FA50CED4F7944F28FA3ECA77B8BCB56DC69814328F891E6065D108EEE7B8E695038090CCA10C0E68DD7A36CFAA1C26CDFEC369FBn'
+);
+
+assert.sameValue(
+  0x123n ** 0xFFn,
+  0x8D5BB75861377EC967BF78FDF39CE51696FBD34722999943F8865938772B517167CD5ED775A78987F5106831F4978E0709032B26ED8F13F814699DB8AB3ACD5CF631F2D8B8B706FCF5EF441AAEE745A795EC5CB86A5E8D87D09F648EFC557B98F73E750FEC9AED061D47806F269CCCDFB6D513912A82AE79B171D76AF6D926BC4F4C4DA43A6EFB4D9D1672E356CC1F74A29AF80D53A8F27592F6191AB9B3D57FA2C435CB2CE8F18A3B3448F88F4BAD3606A9878DA9528B569BADAC0C1EC0B1A2B06CD4C64DEEC940807DFD05C56E3E17ADB1A88EDAF0D67C87C1F871BFB5C47CAE8365FE33538317EE2DF4EE52636CE1BDA9E41C7DA72826E4C097A53BD73D8D697E10D28Bn,
+  'The result of (0x123n ** 0xFFn) is 0x8D5BB75861377EC967BF78FDF39CE51696FBD34722999943F8865938772B517167CD5ED775A78987F5106831F4978E0709032B26ED8F13F814699DB8AB3ACD5CF631F2D8B8B706FCF5EF441AAEE745A795EC5CB86A5E8D87D09F648EFC557B98F73E750FEC9AED061D47806F269CCCDFB6D513912A82AE79B171D76AF6D926BC4F4C4DA43A6EFB4D9D1672E356CC1F74A29AF80D53A8F27592F6191AB9B3D57FA2C435CB2CE8F18A3B3448F88F4BAD3606A9878DA9528B569BADAC0C1EC0B1A2B06CD4C64DEEC940807DFD05C56E3E17ADB1A88EDAF0D67C87C1F871BFB5C47CAE8365FE33538317EE2DF4EE52636CE1BDA9E41C7DA72826E4C097A53BD73D8D697E10D28Bn'
+);
+
+assert.sameValue(0x123n ** 0x3n, 0x178027Bn, 'The result of (0x123n ** 0x3n) is 0x178027Bn');
+assert.sameValue(0x123n ** 0x2n, 0x14AC9n, 'The result of (0x123n ** 0x2n) is 0x14AC9n');
+assert.sameValue(0x123n ** 0x1n, 0x123n, 'The result of (0x123n ** 0x1n) is 0x123n');
+
+assert.sameValue(
+  0xFFn ** 0x123n,
+  0x51F5CA2E1A36F5FF1ED3D393D76FBC3612B38EB64E00EDAC5E95ADE0D16D0B044C8E9F2B77B3F31AF9159F482205541E9D3BE9D248FF39CE6524874EBCA60E06302E8B505D11EEEEE869C7F801A82B9739C197E6D63A1EB2D29B5AD5EED4773C762106E9F66BFCB6C11450218973C69DED3FE51FF881AD0430675BF54320513EA766117C50C554E86E22A5ACFD8047D5470B4FCBCB9EFC86196CA77C58F1BEB09F76160D641B82E2481BEDAE089207D49FE0FB7DE14B6C4BC82E9C58140746AC8E74C3353AAF5F9CF47ED1F87C52F463C053DB63CD08CC9866EBA274D39B6B357ADADAD4D210167EF7363453D42BC225D90070336861F2D259489D78B7F04B05FE65E29151ADD2B8F4D318011988550CE590DBA4C868AC65AA325051DF613D6C2E22FFn,
+  'The result of (0xFFn ** 0x123n) is 0x51F5CA2E1A36F5FF1ED3D393D76FBC3612B38EB64E00EDAC5E95ADE0D16D0B044C8E9F2B77B3F31AF9159F482205541E9D3BE9D248FF39CE6524874EBCA60E06302E8B505D11EEEEE869C7F801A82B9739C197E6D63A1EB2D29B5AD5EED4773C762106E9F66BFCB6C11450218973C69DED3FE51FF881AD0430675BF54320513EA766117C50C554E86E22A5ACFD8047D5470B4FCBCB9EFC86196CA77C58F1BEB09F76160D641B82E2481BEDAE089207D49FE0FB7DE14B6C4BC82E9C58140746AC8E74C3353AAF5F9CF47ED1F87C52F463C053DB63CD08CC9866EBA274D39B6B357ADADAD4D210167EF7363453D42BC225D90070336861F2D259489D78B7F04B05FE65E29151ADD2B8F4D318011988550CE590DBA4C868AC65AA325051DF613D6C2E22FFn'
+);
+
+assert.sameValue(
+  0xFFn ** 0xFFn,
+  0x5E5C8B0EB95AB08F9D37EF127FC01BD0E33DE52647528396D78D5F8DA31989E67814F6BBA1FB0F0207010FF5F2347B19D5F6598FC91BF5A88F77DAA3D7B382FEC484F3D205C06A34445384C0E7AB0D883788C68C012CB433055EDDA746A48409444EA91147273B79FC3EABB70ECA552AF650C234BB01ED404427F17CDDDD71D08E39EF9C3982E3CE44E670456AA8154C1FDBD9C35947F494636A425C69BF89E9C75AD3B7A0A559AF0F5DA9947C8DEBA64417310713B23E7EF4DE50BB2A3E90BC2AC3DA5201CCA8D6E5DFEA887C4F7A4E92175D9F88BD2779B57F9EB35BE7528F965A06DA0AC41DCB3A34F1D8AB7D8FEE620A94FAA42C395997756B007FFEFFn,
+  'The result of (0xFFn ** 0xFFn) is 0x5E5C8B0EB95AB08F9D37EF127FC01BD0E33DE52647528396D78D5F8DA31989E67814F6BBA1FB0F0207010FF5F2347B19D5F6598FC91BF5A88F77DAA3D7B382FEC484F3D205C06A34445384C0E7AB0D883788C68C012CB433055EDDA746A48409444EA91147273B79FC3EABB70ECA552AF650C234BB01ED404427F17CDDDD71D08E39EF9C3982E3CE44E670456AA8154C1FDBD9C35947F494636A425C69BF89E9C75AD3B7A0A559AF0F5DA9947C8DEBA64417310713B23E7EF4DE50BB2A3E90BC2AC3DA5201CCA8D6E5DFEA887C4F7A4E92175D9F88BD2779B57F9EB35BE7528F965A06DA0AC41DCB3A34F1D8AB7D8FEE620A94FAA42C395997756B007FFEFFn'
+);
+
+assert.sameValue(0xFFn ** 0x3n, 0xFD02FFn, 'The result of (0xFFn ** 0x3n) is 0xFD02FFn');
+assert.sameValue(0xFFn ** 0x2n, 0xFE01n, 'The result of (0xFFn ** 0x2n) is 0xFE01n');
+assert.sameValue(0xFFn ** 0x1n, 0xFFn, 'The result of (0xFFn ** 0x1n) is 0xFFn');
+
+assert.sameValue(
+  0x3n ** 0x123n,
+  0x25609213623D7D6219085CF49D306450BF6519835586C19D3A4F3A2C5F35B44A300C8A76E11708B5495B9C3EE756BBF19E3FD15CE625D3C0539Bn,
+  'The result of (0x3n ** 0x123n) is 0x25609213623D7D6219085CF49D306450BF6519835586C19D3A4F3A2C5F35B44A300C8A76E11708B5495B9C3EE756BBF19E3FD15CE625D3C0539Bn'
+);
+
+assert.sameValue(
+  0x3n ** 0xFFn,
+  0x11F1B08E87EC42C5D83C3218FC83C41DCFD9F4428F4F92AF1AAA80AA46162B1F71E981273601F4AD1DD4709B5ACA650265A6ABn,
+  'The result of (0x3n ** 0xFFn) is 0x11F1B08E87EC42C5D83C3218FC83C41DCFD9F4428F4F92AF1AAA80AA46162B1F71E981273601F4AD1DD4709B5ACA650265A6ABn'
+);
+
+assert.sameValue(0x3n ** 0x3n, 0x1Bn, 'The result of (0x3n ** 0x3n) is 0x1Bn');
+assert.sameValue(0x3n ** 0x2n, 0x9n, 'The result of (0x3n ** 0x2n) is 0x9n');
+assert.sameValue(0x3n ** 0x1n, 0x3n, 'The result of (0x3n ** 0x1n) is 0x3n');
+
+assert.sameValue(
+  0x2n ** 0x123n,
+  0x8000000000000000000000000000000000000000000000000000000000000000000000000n,
+  'The result of (0x2n ** 0x123n) is 0x8000000000000000000000000000000000000000000000000000000000000000000000000n'
+);
+
+assert.sameValue(
+  0x2n ** 0xFFn,
+  0x8000000000000000000000000000000000000000000000000000000000000000n,
+  'The result of (0x2n ** 0xFFn) is 0x8000000000000000000000000000000000000000000000000000000000000000n'
+);
+
+assert.sameValue(0x2n ** 0x3n, 0x8n, 'The result of (0x2n ** 0x3n) is 0x8n');
+assert.sameValue(0x2n ** 0x2n, 0x4n, 'The result of (0x2n ** 0x2n) is 0x4n');
+assert.sameValue(0x2n ** 0x1n, 0x2n, 'The result of (0x2n ** 0x1n) is 0x2n');
+assert.sameValue(0x1n ** 0x123n, 0x1n, 'The result of (0x1n ** 0x123n) is 0x1n');
+assert.sameValue(0x1n ** 0xFFn, 0x1n, 'The result of (0x1n ** 0xFFn) is 0x1n');
+assert.sameValue(0x1n ** 0x3n, 0x1n, 'The result of (0x1n ** 0x3n) is 0x1n');
+assert.sameValue(0x1n ** 0x2n, 0x1n, 'The result of (0x1n ** 0x2n) is 0x1n');
+assert.sameValue(0x1n ** 0x1n, 0x1n, 'The result of (0x1n ** 0x1n) is 0x1n');
+assert.sameValue(0n ** 0n, 1n, 'The result of (0n ** 0n) is 1n');
+
diff --git a/JSTests/stress/big-int-exp-jit-osr.js b/JSTests/stress/big-int-exp-jit-osr.js
new file mode 100644 (file)
index 0000000..ea8efbb
--- /dev/null
@@ -0,0 +1,25 @@
+//@ runBigIntEnabled
+
+let assert = {
+    sameValue: function(i, e, m) {
+        if (i !== e)
+            throw new Error(m);
+    }
+}
+
+function bigIntExp(x, y) {
+    return x ** y;
+}
+noInline(bigIntExp);
+
+for (let i = 0; i < 10000; i++) {
+    let r = bigIntExp(3n, 10n);
+    assert.sameValue(r, 59049n, 3n + " ** " + 10n + " = " + r);
+}
+
+let r = bigIntExp(3, 10);
+assert.sameValue(r, 59049, 3 + " ** " + 10 + " = " + r);
+
+r = bigIntExp("3", "10");
+assert.sameValue(r, 59049, "'" + 3 + "' ** '" + 10 + "' = " + r);
+
diff --git a/JSTests/stress/big-int-exp-jit-untyped.js b/JSTests/stress/big-int-exp-jit-untyped.js
new file mode 100644 (file)
index 0000000..c94483e
--- /dev/null
@@ -0,0 +1,39 @@
+//@ runBigIntEnabled
+
+let assert = {
+    sameValue: function(i, e, m) {
+        if (i !== e)
+            throw new Error(m);
+    }
+}
+
+function bigIntExp(x, y) {
+    return x ** y;
+}
+noInline(bigIntExp);
+
+let o =  {valueOf: () => 10n};
+
+for (let i = 0; i < 10000; i++) {
+    let r = bigIntExp(3n, o);
+    assert.sameValue(r, 59049n, 3n + " ** {valueOf: () => 10n} = " + r);
+}
+
+o2 =  {valueOf: () => 3n};
+
+for (let i = 0; i < 10000; i++) {
+    let r = bigIntExp(o2, o);
+    assert.sameValue(r, 59049n, "{valueOf: () => 3n} ** {valueOf: () => 10n}  = " + r);
+}
+
+o = Object(10n);
+let r = bigIntExp(3n, o);
+assert.sameValue(r, 59049n, 3n + " ** Object(10n) = " + r);
+
+o2 = Object(3n);
+r = bigIntExp(o2, o);
+assert.sameValue(r, 59049n, "Object(3n) ** Object(10n) = " + r);
+
+r = bigIntExp(3, 3);
+assert.sameValue(r, 27, "3 ** 3 = " + r);
+
diff --git a/JSTests/stress/big-int-exp-jit.js b/JSTests/stress/big-int-exp-jit.js
new file mode 100644 (file)
index 0000000..dc9d31d
--- /dev/null
@@ -0,0 +1,19 @@
+//@ runBigIntEnabled
+
+let assert = {
+    sameValue: function(i, e, m) {
+        if (i !== e)
+            throw new Error(m);
+    }
+}
+
+function bigIntExp(x, y) {
+    return x ** y;
+}
+noInline(bigIntExp);
+
+for (let i = 0; i < 10000; i++) {
+    let r = bigIntExp(3n, 10n);
+    assert.sameValue(r, 59049n, 3n + " ** " + 10n + " = " + r);
+}
+
diff --git a/JSTests/stress/big-int-exp-negative-exponent.js b/JSTests/stress/big-int-exp-negative-exponent.js
new file mode 100644 (file)
index 0000000..c6779f3
--- /dev/null
@@ -0,0 +1,21 @@
+//@ runBigIntEnabled
+
+function assert(a, message) {
+    if (!a)
+        throw new Error(message);
+}
+
+function assertThrowRangeError(a, b) {
+    try {
+        let n = a ** b;
+        assert(false, "Should throw RangeError, but executed without exception");
+    } catch (e) {
+        assert(e instanceof RangeError, "Expected RangeError, got: " + e);
+    }
+}
+
+assertThrowRangeError(1n, -1n);
+assertThrowRangeError(0n, -1n);
+assertThrowRangeError(-1n, -1n);
+assertThrowRangeError(1n, -100000000000000000n);
+
diff --git a/JSTests/stress/big-int-exp-to-primitive.js b/JSTests/stress/big-int-exp-to-primitive.js
new file mode 100644 (file)
index 0000000..e0b83f3
--- /dev/null
@@ -0,0 +1,34 @@
+//@ runBigIntEnabled
+
+function assert(a) {
+    if (!a)
+        throw new Error("Bad assertion");
+}
+
+assert.sameValue = function (input, expected, message) {
+    if (input !== expected)
+        throw new Error(message);
+}
+
+function testExp(x, y, z) {
+    assert.sameValue(x ** y, z, x + " * " + y + " = " + z);
+}
+
+let o = {
+    [Symbol.toPrimitive]: function () { return 15n; }
+}
+
+testExp(15n, o, 437893890380859375n);
+
+o.valueOf = function () {
+    throw new Error("Should never execute it");
+};
+
+testExp(22n, o, 136880068015412051968n);
+
+o.toString = function () {
+    throw new Error("Should never execute it");
+};
+
+testExp(103n, o, 1557967416600764580522382952407n);
+
diff --git a/JSTests/stress/big-int-exp-type-error.js b/JSTests/stress/big-int-exp-type-error.js
new file mode 100644 (file)
index 0000000..8c2b94b
--- /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-exp-wrapped-value.js b/JSTests/stress/big-int-exp-wrapped-value.js
new file mode 100644 (file)
index 0000000..5a9a181
--- /dev/null
@@ -0,0 +1,36 @@
+//@ runBigIntEnabled
+
+assert = {
+    sameValue: function (input, expected, message) {
+        if (input !== expected)
+            throw new Error(message);
+    }
+};
+
+function testExp(x, y, z, message) {
+    assert.sameValue(x ** y, z, message);
+}
+
+testExp(Object(2n), 1n, 2n, "ToPrimitive: unbox object with internal slot");
+
+let o = {
+    [Symbol.toPrimitive]: function() {
+        return 2n;
+    }
+};
+testExp(o, 1n, 2n, "ToPrimitive: @@toPrimitive");
+
+o = {
+    valueOf: function() {
+        return 2n;
+    }
+};
+testExp(o, 1n, 2n, "ToPrimitive: valueOf");
+
+o = {
+    toString: function() {
+        return 2n;
+    }
+}
+testExp(o, 1n, 2n, "ToPrimitive: toString");
+
diff --git a/JSTests/stress/value-pow-ai-rule.js b/JSTests/stress/value-pow-ai-rule.js
new file mode 100644 (file)
index 0000000..ed48265
--- /dev/null
@@ -0,0 +1,20 @@
+function assert(a, e) {
+    if (a !== e)
+        throw new Error("Expected: " + e + " bug got: " + a);
+}
+
+let predicate = true;
+function foo(a) {
+    let v = a;
+    if (predicate)
+        v = 10;
+
+    let c = v ** 5;
+    return c;
+}
+noInline(foo);
+
+for (let i = 0; i < 10000; i++) {
+    assert(foo("10"), 100000);
+}
+
index b6b428c..593fb21 100644 (file)
@@ -1,3 +1,73 @@
+2019-06-03  Caio Lima  <ticaiolima@gmail.com>
+
+        [ESNext][BigInt] Implement support for "**"
+        https://bugs.webkit.org/show_bug.cgi?id=190799
+
+        Reviewed by Saam Barati.
+
+        We are introducing support for BigInt into "**" operator. This Patch
+        also includes changes into DFG, introducing a new node "ValuePow" that
+        is responsible to handle UntypedUse and BigIntUse.
+
+        * dfg/DFGAbstractInterpreterInlines.h:
+        (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+
+        ValuePow(Untyped, Untyped) still can propagate constant if AI proves
+        it. We are doing so if AI proves rhs and lhs as numbers.
+
+        * dfg/DFGByteCodeParser.cpp:
+        (JSC::DFG::ByteCodeParser::parseBlock):
+
+        When compiling op_pow, we first verify if rhs and lhs can be any Int
+        or number. If this happen, we emit ArithPow, otherwise we fallback to
+        ValuePow and rely on fixup to convert it to ArithPow if possible.
+
+        * dfg/DFGClobberize.h:
+        (JSC::DFG::clobberize):
+
+        We only clobberize world if ValuePow is UntypedUse. Otherwise, we can
+        properly support CSE.
+
+        * dfg/DFGDoesGC.cpp:
+        (JSC::DFG::doesGC):
+
+        JSBigInt::exponentiate allocates JSBigInts to perform calculation and
+        it can trigger GC. ValuePow(UntypedUse) can trigger GC because it can
+        execute user code.
+
+        * dfg/DFGFixupPhase.cpp:
+        (JSC::DFG::FixupPhase::fixupArithPow):
+        (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::compileValuePow):
+        * dfg/DFGSpeculativeJIT.h:
+        * dfg/DFGSpeculativeJIT32_64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGSpeculativeJIT64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGValidate.cpp:
+        * ftl/FTLCapabilities.cpp:
+        (JSC::FTL::canCompile):
+        * ftl/FTLLowerDFGToB3.cpp:
+        (JSC::FTL::DFG::LowerDFGToB3::compileNode):
+        (JSC::FTL::DFG::LowerDFGToB3::compileValuePow):
+        * runtime/CommonSlowPaths.cpp:
+        (JSC::SLOW_PATH_DECL):
+
+        We are adding proper support to BigInt on op_pow. The specification
+        defines that we can only apply pow when both operands have the same
+        type after calling ToNumeric().
+
+        * runtime/JSBigInt.cpp:
+        (JSC::JSBigInt::exponentiate):
+        * runtime/JSBigInt.h:
+
 2019-06-03  Yusuke Suzuki  <ysuzuki@apple.com>
 
         [JSC] JSObject::attemptToInterceptPutByIndexOnHole should use getPrototype instead of getPrototypeDirect
index ac28dcd..80c33f7 100644 (file)
@@ -902,6 +902,27 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
         break;
     }
         
+    case ValuePow: {
+        JSValue childX = forNode(node->child1()).value();
+        JSValue childY = forNode(node->child2()).value();
+        if (childX && childY && childX.isNumber() && childY.isNumber()) {
+            // We need to call `didFoldClobberWorld` here because this path is only possible
+            // when node->useKind is UntypedUse. In the case of BigIntUse, children will be
+            // cleared by `AbstractInterpreter::executeEffects`.
+            didFoldClobberWorld();
+            setConstant(node, jsDoubleNumber(operationMathPow(childX.asNumber(), childY.asNumber())));
+            break;
+        }
+
+        if (node->binaryUseKind() == BigIntUse)
+            setTypeForNode(node, SpecBigInt);
+        else {
+            clobberWorld();
+            setTypeForNode(node, SpecBytecodeNumber | SpecBigInt);
+        }
+        break;
+    }
+
     case ValueMul: {
         if (node->binaryUseKind() == BigIntUse)
             setTypeForNode(node, SpecBigInt);
index 38023fc..bf9f55d 100644 (file)
@@ -5125,12 +5125,13 @@ void ByteCodeParser::parseBlock(unsigned limit)
         }
 
         case op_pow: {
-            // FIXME: ArithPow(Untyped, Untyped) should be supported as the same to ArithMul, ArithSub etc.
-            // https://bugs.webkit.org/show_bug.cgi?id=160012
             auto bytecode = currentInstruction->as<OpPow>();
             Node* op1 = get(bytecode.m_lhs);
             Node* op2 = get(bytecode.m_rhs);
-            set(bytecode.m_dst, addToGraph(ArithPow, op1, op2));
+            if (op1->hasNumberOrAnyIntResult() && op2->hasNumberOrAnyIntResult())
+                set(bytecode.m_dst, addToGraph(ArithPow, op1, op2));
+            else
+                set(bytecode.m_dst, addToGraph(ValuePow, op1, op2));
             NEXT_OPCODE(op_pow);
         }
 
index 72dbaab..c56d3ed 100644 (file)
@@ -681,6 +681,7 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu
     case ValueMul:
     case ValueDiv:
     case ValueMod:
+    case ValuePow:
         if (node->isBinaryUseKind(BigIntUse)) {
             def(PureValue(node));
             return;
index 5560e8e..8d8d6cd 100644 (file)
@@ -380,6 +380,7 @@ bool doesGC(Graph& graph, Node* node)
     case ValueMul:
     case ValueDiv:
     case ValueMod:
+    case ValuePow:
     case ValueBitNot:
     case ValueNegate:
 #else
index f929fe3..aa177b9 100644 (file)
@@ -108,6 +108,18 @@ private:
 
     }
 
+    void fixupArithPow(Node* node)
+    {
+        if (node->child2()->shouldSpeculateInt32OrBooleanForArithmetic()) {
+            fixDoubleOrBooleanEdge(node->child1());
+            fixIntOrBooleanEdge(node->child2());
+            return;
+        }
+
+        fixDoubleOrBooleanEdge(node->child1());
+        fixDoubleOrBooleanEdge(node->child2());
+    }
+
     void fixupArithDiv(Node* node, Edge& leftChild, Edge& rightChild)
     {
         if (m_graph.binaryArithShouldSpeculateInt32(node, FixupPass)) {
@@ -589,15 +601,30 @@ private:
             break;
         }
 
-        case ArithPow: {
-            if (node->child2()->shouldSpeculateInt32OrBooleanForArithmetic()) {
-                fixDoubleOrBooleanEdge(node->child1());
-                fixIntOrBooleanEdge(node->child2());
+        case ValuePow: {
+            if (Node::shouldSpeculateBigInt(node->child1().node(), node->child2().node())) {
+                fixEdge<BigIntUse>(node->child1());
+                fixEdge<BigIntUse>(node->child2());
+                node->clearFlags(NodeMustGenerate);
                 break;
             }
 
-            fixDoubleOrBooleanEdge(node->child1());
-            fixDoubleOrBooleanEdge(node->child2());
+            if (Node::shouldSpeculateUntypedForArithmetic(node->child1().node(), node->child2().node())) {
+                fixEdge<UntypedUse>(node->child1());
+                fixEdge<UntypedUse>(node->child2());
+                break;
+            }
+
+            node->setOp(ArithPow);
+            node->clearFlags(NodeMustGenerate);
+            node->setResult(NodeResultDouble);
+
+            fixupArithPow(node);
+            break;
+        }
+
+        case ArithPow: {
+            fixupArithPow(node);
             break;
         }
 
index b2c0b41..4194c2a 100644 (file)
@@ -179,6 +179,7 @@ namespace JSC { namespace DFG {
     macro(ValueSub, NodeResultJS | NodeMustGenerate) \
     macro(ValueMul, NodeResultJS | NodeMustGenerate) \
     macro(ValueDiv, NodeResultJS | NodeMustGenerate) \
+    macro(ValuePow, NodeResultJS | NodeMustGenerate) \
     macro(ValueMod, NodeResultJS | NodeMustGenerate) \
     \
     /* Add of values that always convers its inputs to strings. May have two or three kids. */\
index 25c4c09..5cb276f 100644 (file)
@@ -548,6 +548,19 @@ EncodedJSValue JIT_OPERATION operationValueDiv(ExecState* exec, EncodedJSValue e
     return binaryOp(exec, encodedOp1, encodedOp2, bigIntOp, numberOp, "Invalid mix of BigInt and other type in division operation.");
 }
 
+EncodedJSValue JIT_OPERATION operationValuePow(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)
+{
+    auto bigIntOp = [] (ExecState* exec, JSBigInt* left, JSBigInt* right) -> JSBigInt* {
+        return JSBigInt::exponentiate(exec, left, right);
+    };
+
+    auto numberOp = [] (double left, double right) -> double {
+        return operationMathPow(left, right);
+    };
+
+    return binaryOp(exec, encodedOp1, encodedOp2, bigIntOp, numberOp, "Invalid mix of BigInt and other type in exponentiation operation."_s);
+}
+
 double JIT_OPERATION operationArithAbs(ExecState* exec, EncodedJSValue encodedOp1)
 {
     VM* vm = &exec->vm();
@@ -1396,6 +1409,17 @@ JSCell* JIT_OPERATION operationDivBigInt(ExecState* exec, JSCell* op1, JSCell* o
     return JSBigInt::divide(exec, leftOperand, rightOperand);
 }
 
+JSCell* JIT_OPERATION operationPowBigInt(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::exponentiate(exec, leftOperand, rightOperand);
+}
+
 JSCell* JIT_OPERATION operationBitAndBigInt(ExecState* exec, JSCell* op1, JSCell* op2)
 {
     VM* vm = &exec->vm();
index 0ce0079..7ee6c35 100644 (file)
@@ -60,6 +60,7 @@ EncodedJSValue JIT_OPERATION operationValueBitRShift(ExecState*, EncodedJSValue
 EncodedJSValue JIT_OPERATION operationValueBitURShift(ExecState*, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationValueAddNotNumber(ExecState*, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationValueDiv(ExecState*, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2) WTF_INTERNAL;
+EncodedJSValue JIT_OPERATION operationValuePow(ExecState*, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2) WTF_INTERNAL;
 double JIT_OPERATION operationArithAbs(ExecState*, EncodedJSValue encodedOp1) WTF_INTERNAL;
 uint32_t JIT_OPERATION operationArithClz32(ExecState*, EncodedJSValue encodedOp1) WTF_INTERNAL;
 double JIT_OPERATION operationArithFRound(ExecState*, EncodedJSValue encodedOp1) WTF_INTERNAL;
@@ -171,6 +172,7 @@ JSCell* JIT_OPERATION operationSubBigInt(ExecState*, JSCell* op1, JSCell* op2) W
 JSCell* JIT_OPERATION operationMulBigInt(ExecState*, JSCell* op1, JSCell* op2) WTF_INTERNAL;
 JSCell* JIT_OPERATION operationModBigInt(ExecState*, JSCell* op1, JSCell* op2) WTF_INTERNAL;
 JSCell* JIT_OPERATION operationDivBigInt(ExecState*, JSCell* op1, JSCell* op2) WTF_INTERNAL;
+JSCell* JIT_OPERATION operationPowBigInt(ExecState*, JSCell* op1, JSCell* op2) WTF_INTERNAL;
 JSCell* JIT_OPERATION operationBitAndBigInt(ExecState*, JSCell* op1, JSCell* op2) WTF_INTERNAL;
 JSCell* JIT_OPERATION operationBitNotBigInt(ExecState*, JSCell* op1) WTF_INTERNAL;
 JSCell* JIT_OPERATION operationBitOrBigInt(ExecState*, JSCell* op1, JSCell* op2) WTF_INTERNAL;
index 098af63..84d61de 100644 (file)
@@ -277,6 +277,22 @@ private:
             break;
         }
 
+        case ValuePow: {
+            SpeculatedType left = node->child1()->prediction();
+            SpeculatedType right = node->child2()->prediction();
+
+            if (left && right) {
+                if (node->child1()->shouldSpeculateBigInt() && node->child2()->shouldSpeculateBigInt())          
+                    changed |= mergePrediction(SpecBigInt);
+                else if (isFullNumberOrBooleanSpeculationExpectingDefined(left)
+                    && isFullNumberOrBooleanSpeculationExpectingDefined(right))
+                    setPrediction(SpecBytecodeDouble);
+                else
+                    setPrediction(SpecBytecodeDouble | SpecBigInt);
+            }
+            break;
+        }
+
         case ValueNegate:
         case ArithNegate: {
             SpeculatedType prediction = node->child1()->prediction();
@@ -1120,6 +1136,7 @@ private:
         case ValueMul:
         case ValueDiv:
         case ValueMod:
+        case ValuePow:
         case ArithAdd:
         case ArithSub:
         case ArithNegate:
index 2bf682b..72ca69a 100644 (file)
@@ -239,6 +239,7 @@ bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node, bool igno
     case ValueMul:
     case ValueDiv:
     case ValueMod:
+    case ValuePow:
     case TryGetById:
     case DeleteById:
     case DeleteByVal:
index d0410b3..e5538f1 100644 (file)
@@ -5796,6 +5796,47 @@ static MacroAssembler::Jump compileArithPowIntegerFastPath(JITCompiler& assemble
     return skipSlowPath;
 }
 
+void SpeculativeJIT::compileValuePow(Node* node)
+{
+    Edge& leftChild = node->child1();
+    Edge& rightChild = node->child2();
+
+    if (node->binaryUseKind() == BigIntUse) {
+        SpeculateCellOperand left(this, leftChild);
+        SpeculateCellOperand right(this, rightChild);
+        GPRReg leftGPR = left.gpr();
+        GPRReg rightGPR = right.gpr();
+
+        speculateBigInt(leftChild, leftGPR);
+        speculateBigInt(rightChild, rightGPR);
+
+        flushRegisters();
+        GPRFlushedCallResult result(this);
+        GPRReg resultGPR = result.gpr();
+
+        callOperation(operationPowBigInt, resultGPR, leftGPR, rightGPR);
+
+        m_jit.exceptionCheck();
+        cellResult(resultGPR, node);
+        return;
+    }
+
+    DFG_ASSERT(m_jit.graph(), node, node->binaryUseKind() == UntypedUse, node->binaryUseKind());
+
+    JSValueOperand left(this, leftChild);
+    JSValueOperand right(this, rightChild);
+    JSValueRegs leftRegs = left.jsValueRegs();
+    JSValueRegs rightRegs = right.jsValueRegs();
+
+    flushRegisters();
+    JSValueRegsFlushedCallResult result(this);
+    JSValueRegs resultRegs = result.regs();
+    callOperation(operationValuePow, resultRegs, leftRegs, rightRegs);
+    m_jit.exceptionCheck();
+
+    jsValueResult(resultRegs, node);
+}
+
 void SpeculativeJIT::compileArithPow(Node* node)
 {
     if (node->child2().useKind() == Int32Use) {
index d9ce5ba..9f1d893 100644 (file)
@@ -1359,6 +1359,7 @@ public:
     void compileValueMod(Node*);
     void compileArithMod(Node*);
     void compileArithPow(Node*);
+    void compileValuePow(Node*);
     void compileArithRounding(Node*);
     void compileArithRandom(Node*);
     void compileArithUnary(Node*);
index db2d464..9d382cf 100644 (file)
@@ -2097,6 +2097,11 @@ void SpeculativeJIT::compile(Node* node)
         break;
     }
 
+    case ValuePow: {
+        compileValuePow(node);
+        break;
+    }
+
     case ArithPow: {
         compileArithPow(node);
         break;
index e9f467f..2d62faa 100644 (file)
@@ -2253,6 +2253,10 @@ void SpeculativeJIT::compile(Node* node)
         break;
     }
 
+    case ValuePow:
+        compileValuePow(node);
+        break;
+
     case ArithPow:
         compileArithPow(node);
         break;
index 0aa5b09..e3141e6 100644 (file)
@@ -258,6 +258,7 @@ public:
                 case ValueMul:
                 case ValueDiv:
                 case ValueMod:
+                case ValuePow:
                 case ArithAdd:
                 case ArithSub:
                 case ArithMul:
index c5d4f18..a22c117 100644 (file)
@@ -99,6 +99,7 @@ inline CapabilityLevel canCompile(Node* node)
     case ValueMul:
     case ValueDiv:
     case ValueMod:
+    case ValuePow:
     case StrCat:
     case ArithAdd:
     case ArithClz32:
index 5e28a1a..94c9658 100644 (file)
@@ -793,6 +793,9 @@ private:
         case ArithAbs:
             compileArithAbs();
             break;
+        case ValuePow:
+            compileValuePow();
+            break;
         case ArithPow:
             compileArithPow();
             break;
@@ -2718,6 +2721,23 @@ private:
         setDouble(result);
     }
 
+    void compileValuePow()
+    {
+        if (m_node->isBinaryUseKind(BigIntUse)) {
+            LValue base = lowBigInt(m_node->child1());
+            LValue exponent = lowBigInt(m_node->child2());
+            
+            LValue result = vmCall(pointerType(), m_out.operation(operationPowBigInt), m_callFrame, base, exponent);
+            setJSValue(result);
+            return;
+        }
+
+        LValue base = lowJSValue(m_node->child1());
+        LValue exponent = lowJSValue(m_node->child2());
+        LValue result = vmCall(Int64, m_out.operation(operationValuePow), m_callFrame, base, exponent);
+        setJSValue(result);
+    }
+
     void compileArithPow()
     {
         if (m_node->child2().useKind() == Int32Use)
index 4e78a48..29b2022 100644 (file)
@@ -636,12 +636,26 @@ SLOW_PATH_DECL(slow_path_pow)
 {
     BEGIN();
     auto bytecode = pc->as<OpPow>();
-    double a = GET_C(bytecode.m_lhs).jsValue().toNumber(exec);
-    if (UNLIKELY(throwScope.exception()))
-        RETURN(JSValue());
-    double b = GET_C(bytecode.m_rhs).jsValue().toNumber(exec);
-    if (UNLIKELY(throwScope.exception()))
-        RETURN(JSValue());
+    JSValue left = GET_C(bytecode.m_lhs).jsValue();
+    JSValue right = GET_C(bytecode.m_rhs).jsValue();
+    auto leftNumeric = left.toNumeric(exec);
+    CHECK_EXCEPTION();
+    auto rightNumeric = right.toNumeric(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::exponentiate(exec, WTF::get<JSBigInt*>(leftNumeric), WTF::get<JSBigInt*>(rightNumeric));
+            CHECK_EXCEPTION();
+            RETURN(result);
+        }
+
+        THROW(createTypeError(exec, "Invalid mix of BigInt and other type in exponentiation operation."));
+    }
+    
+    double a = WTF::get<double>(leftNumeric);
+    double b = WTF::get<double>(rightNumeric);
+
     RETURN(jsNumber(operationMathPow(a, b)));
 }
 
index 7e3ccb4..4c0c1f9 100644 (file)
@@ -237,6 +237,95 @@ void JSBigInt::inplaceMultiplyAdd(Digit factor, Digit summand)
     internalMultiplyAdd(this, factor, summand, length(), this);
 }
 
+JSBigInt* JSBigInt::exponentiate(ExecState* exec, JSBigInt* base, JSBigInt* exponent)
+{
+    VM& vm = exec->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
+    if (exponent->sign()) {
+        throwRangeError(exec, scope, "Negative exponent is not allowed"_s);
+        return nullptr;
+    }
+
+    // 2. If base is 0n and exponent is 0n, return 1n.
+    if (exponent->isZero())
+        return JSBigInt::createFrom(vm, 1);
+    
+    // 3. Return a BigInt representing the mathematical value of base raised
+    //    to the power exponent.
+    if (base->isZero())
+        return base;
+
+    if (base->length() == 1 && base->digit(0) == 1) {
+        // (-1) ** even_number == 1.
+        if (base->sign() && !(exponent->digit(0) & 1))
+            return JSBigInt::unaryMinus(vm, base);
+
+        // (-1) ** odd_number == -1; 1 ** anything == 1.
+        return base;
+    }
+
+    // For all bases >= 2, very large exponents would lead to unrepresentable
+    // results.
+    static_assert(maxLengthBits < std::numeric_limits<Digit>::max(), "maxLengthBits needs to be less than digit::max()");
+    if (exponent->length() > 1) {
+        throwRangeError(exec, scope, "BigInt generated from this operation is too big"_s);
+        return nullptr;
+    }
+
+    Digit expValue = exponent->digit(0);
+    if (expValue == 1)
+        return base;
+    if (expValue >= maxLengthBits) {
+        throwRangeError(exec, scope, "BigInt generated from this operation is too big"_s);
+        return nullptr;
+    }
+
+    static_assert(maxLengthBits <= maxInt, "maxLengthBits needs to be <= maxInt");
+    int n = static_cast<int>(expValue);
+    if (base->length() == 1 && base->digit(0) == 2) {
+        // Fast path for 2^n.
+        int neededDigits = 1 + (n / digitBits);
+        JSBigInt* result = JSBigInt::tryCreateWithLength(exec, neededDigits);
+        RETURN_IF_EXCEPTION(scope, nullptr);
+
+        result->initialize(InitializationType::WithZero);
+        // All bits are zero. Now set the n-th bit.
+        Digit msd = static_cast<Digit>(1) << (n % digitBits);
+        result->setDigit(neededDigits - 1, msd);
+        // Result is negative for odd powers of -2n.
+        if (base->sign()) 
+            result->setSign(static_cast<bool>(n & 1));
+
+        return result;
+    }
+
+    JSBigInt* result = nullptr;
+    JSBigInt* runningSquare = base;
+
+    // This implicitly sets the result's sign correctly.
+    if (n & 1)
+        result = base;
+
+    n >>= 1;
+    for (; n; n >>= 1) {
+        JSBigInt* maybeResult = JSBigInt::multiply(exec, runningSquare, runningSquare);
+        RETURN_IF_EXCEPTION(scope, nullptr);
+        runningSquare = maybeResult;
+        if (n & 1) {
+            if (!result)
+                result = runningSquare;
+            else {
+                maybeResult = JSBigInt::multiply(exec, result, runningSquare);
+                RETURN_IF_EXCEPTION(scope, nullptr);
+                result = maybeResult;
+            }
+        }
+    }
+
+    return result;
+}
+
 JSBigInt* JSBigInt::multiply(ExecState* exec, JSBigInt* x, JSBigInt* y)
 {
     VM& vm = exec->vm();
@@ -1055,7 +1144,7 @@ void JSBigInt::absoluteDivWithBigIntDivisor(ExecState* exec, JSBigInt* dividend,
     }
 }
 
-// Returns whether (factor1 * factor2) > (high << kDigitBits) + low.
+// Returns whether (factor1 * factor2) > (high << digitBits) + low.
 inline bool JSBigInt::productGreaterThan(Digit factor1, Digit factor2, Digit high, Digit low)
 {
     Digit resultHigh;
index 5cc9e09..414ed3c 100644 (file)
@@ -111,6 +111,8 @@ public:
     JSObject* toObject(ExecState*, JSGlobalObject*) const;
     inline bool toBoolean() const { return !isZero(); }
 
+    static JSBigInt* exponentiate(ExecState*, JSBigInt* base, JSBigInt* exponent);
+
     static JSBigInt* multiply(ExecState*, JSBigInt* x, JSBigInt* y);
     
     ComparisonResult static compareToDouble(JSBigInt* x, double y);