+2018-04-26 Caio Lima <ticaiolima@gmail.com>
+
+ [ESNext][BigInt] Implement support for "*" operation
+ https://bugs.webkit.org/show_bug.cgi?id=183721
+
+ Reviewed by Saam Barati.
+
+ * bigIntTests.yaml:
+ * stress/big-int-mul-jit.js: Added.
+ * stress/big-int-mul-to-primitive-precedence.js: Added.
+ * stress/big-int-mul-to-primitive.js: Added.
+ * stress/big-int-mul-type-error.js: Added.
+ * stress/big-int-mul-wrapped-value.js: Added.
+ * stress/big-int-multiplication.js: Added.
+ * stress/big-int-multiply-memory-stress.js: Added.
+
2018-04-25 Robin Morisset <rmorisset@apple.com>
In FTLLowerDFGToB3.cpp::compileCreateRest, always use a contiguous array as the indexing type when under isWatchingHavingABadTimeWatchpoint
- path: stress/big-int-to-string.js
cmd: runBigIntEnabled
+- path: stress/big-int-mul-jit.js
+ cmd: runBigIntEnabled
+
+- path: stress/big-int-mul-to-primitive-precedence.js
+ cmd: runBigIntEnabled
+
+- path: stress/big-int-mul-to-primitive.js
+ cmd: runBigIntEnabled
+
+- path: stress/big-int-mul-type-error.js
+ cmd: runBigIntEnabled
+
+- path: stress/big-int-mul-wrapped-value.js
+ cmd: runBigIntEnabled
+
+- path: stress/big-int-multiplication.js
+ cmd: runBigIntEnabled
+
+- path: stress/big-int-multiply-memory-stress.js
+ cmd: runBigIntEnabled
+
--- /dev/null
+//@ runBigIntEnabled
+
+let assert = {
+ sameValue: function(i, e, m) {
+ if (i !== e)
+ throw new Error(m);
+ }
+}
+
+function bigIntMul(x, y) {
+ return x * y;
+}
+noInline(bigIntMul);
+
+for (let i = 0; i < 10000; i++) {
+ let r = bigIntMul(3n, 10n);
+ assert.sameValue(r, 30n, 3n + " * " + 10n + " = " + r);
+}
+
--- /dev/null
+//@ runBigIntEnabled
+
+assert = {
+ sameValue: function (input, expected, message) {
+ if (input !== expected)
+ throw new Error(message);
+ }
+};
+
+function testMul(x, y, z, message) {
+ assert.sameValue(x * y, z, message);
+ assert.sameValue(y * x, z, message);
+}
+
+testMul(Object(2n), 1n, 2n, "ToPrimitive: unbox object with internal slot");
+
+let o = {
+ [Symbol.toPrimitive]: function() {
+ return 2n;
+ },
+ valueOf: function () {
+ throw new Error("Should never execute it");
+ },
+ toString: function () {
+ throw new Error("Should never execute it");
+ }
+};
+testMul(o, 1n, 2n, "ToPrimitive: @@toPrimitive");
+
+o = {
+ valueOf: function() {
+ return 2n;
+ },
+ toString: function () {
+ throw new Error("Should never execute it");
+ }
+};
+testMul(o, 1n, 2n, "ToPrimitive: valueOf");
+
--- /dev/null
+//@ 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 testMul(x, y, z) {
+ assert.sameValue(x * y, z, x + " * " + y + " = " + z);
+ assert.sameValue(y * x, z, y + " * " + x + " = " + z);
+}
+
+let o = {
+ [Symbol.toPrimitive]: function () { return 300000000000000n; }
+}
+
+testMul(500000000000438n, o, 150000000000131400000000000000n);
+
+o.valueOf = function () {
+ throw new Error("Should never execute it");
+};
+
+testMul(700000000000438n, o, 210000000000131400000000000000n);
+
+o.toString = function () {
+ throw new Error("Should never execute it");
+};
+
+testMul(700000000000438n, o, 210000000000131400000000000000n);
+
--- /dev/null
+//@ 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");
+
--- /dev/null
+//@ runBigIntEnabled
+
+assert = {
+ sameValue: function (input, expected, message) {
+ if (input !== expected)
+ throw new Error(message);
+ }
+};
+
+function testMul(x, y, z, message) {
+ assert.sameValue(x * y, z, message);
+ assert.sameValue(y * x, z, message);
+}
+
+testMul(Object(2n), 1n, 2n, "ToPrimitive: unbox object with internal slot");
+
+let o = {
+ [Symbol.toPrimitive]: function() {
+ return 2n;
+ }
+};
+testMul(o, 1n, 2n, "ToPrimitive: @@toPrimitive");
+
+o = {
+ valueOf: function() {
+ return 2n;
+ }
+};
+testMul(o, 1n, 2n, "ToPrimitive: valueOf");
+
+o = {
+ toString: function() {
+ return 2n;
+ }
+}
+testMul(o, 1n, 2n, "ToPrimitive: toString");
+
--- /dev/null
+//@ runBigIntEnabled
+
+// Copyright (C) 2017 Robin Templeton. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+assert = {
+ sameValue: function (input, expected, message) {
+ if (input !== expected)
+ throw new Error(message);
+ }
+};
+
+function testMul(x, y, z) {
+ assert.sameValue(x * y, z, x + " * " + y + " = " + z);
+ assert.sameValue(y * x, z, y + " * " + x + " = " + z);
+}
+
+testMul(0xFEDCBA9876543210n, 0xFEDCBA9876543210n, 0xFDBAC097C8DC5ACCDEEC6CD7A44A4100n);
+testMul(0xFEDCBA9876543210n, 0xFEDCBA98n, 0xFDBAC097530ECA86541D5980n);
+testMul(0xFEDCBA9876543210n, 0x1234n, 0x121F49F49F49F49F4B40n);
+testMul(0xFEDCBA9876543210n, 0x3n, 0x2FC962FC962FC9630n);
+testMul(0xFEDCBA9876543210n, 0x2n, 0x1FDB97530ECA86420n);
+testMul(0xFEDCBA9876543210n, 0x1n, 0xFEDCBA9876543210n);
+testMul(0xFEDCBA9876543210n, 0x0n, 0x0n);
+testMul(0xFEDCBA9876543210n, BigInt("-1"), BigInt("-18364758544493064720"));
+testMul(0xFEDCBA9876543210n, BigInt("-2"), BigInt("-36729517088986129440"));
+testMul(0xFEDCBA9876543210n, BigInt("-3"), BigInt("-55094275633479194160"));
+testMul(0xFEDCBA9876543210n, BigInt("-4660"), BigInt("-85579774817337681595200"));
+testMul(0xFEDCBA9876543210n, BigInt("-4275878551"), BigInt("-78525477154691874604502820720"));
+testMul(0xFEDCBA987654320Fn, 0xFEDCBA987654320Fn, 0xFDBAC097C8DC5ACAE132F7A6B7A1DCE1n);
+testMul(0xFEDCBA987654320Fn, 0xFEDCBA97n, 0xFDBAC09654320FECDEEC6CD9n);
+testMul(0xFEDCBA987654320Fn, 0x3n, 0x2FC962FC962FC962Dn);
+testMul(0xFEDCBA987654320Fn, 0x2n, 0x1FDB97530ECA8641En);
+testMul(0xFEDCBA987654320Fn, 0x1n, 0xFEDCBA987654320Fn);
+testMul(0xFEDCBA987654320Fn, 0x0n, 0x0n);
+testMul(0xFEDCBA987654320Fn, BigInt("-1"), BigInt("-18364758544493064719"));
+testMul(0xFEDCBA987654320Fn, BigInt("-2"), BigInt("-36729517088986129438"));
+testMul(0xFEDCBA987654320Fn, BigInt("-3"), BigInt("-55094275633479194157"));
+testMul(0xFEDCBA987654320Fn, BigInt("-4275878551"), BigInt("-78525477154691874600226942169"));
+testMul(0xFEDCBA987654320Fn, BigInt("-18364758544493064720"), BigInt("-337264356397531028976608289633615613680"));
+testMul(0xFEDCBA98n, 0xFEDCBA98n, 0xFDBAC096DD413A40n);
+testMul(0xFEDCBA98n, 0x1234n, 0x121F49F496E0n);
+testMul(0xFEDCBA98n, 0x3n, 0x2FC962FC8n);
+testMul(0xFEDCBA98n, 0x2n, 0x1FDB97530n);
+testMul(0xFEDCBA98n, 0x1n, 0xFEDCBA98n);
+testMul(0xFEDCBA98n, 0x0n, 0x0n);
+testMul(0xFEDCBA98n, BigInt("-1"), BigInt("-4275878552"));
+testMul(0xFEDCBA98n, BigInt("-2"), BigInt("-8551757104"));
+testMul(0xFEDCBA98n, BigInt("-3"), BigInt("-12827635656"));
+testMul(0xFEDCBA98n, BigInt("-4275878551"), BigInt("-18283137387177738152"));
+testMul(0xFEDCBA98n, BigInt("-18364758544493064720"), BigInt("-78525477173056633148995885440"));
+testMul(0x3n, 0x3n, 0x9n);
+testMul(0x3n, 0x2n, 0x6n);
+testMul(0x3n, 0x1n, 0x3n);
+testMul(0x3n, 0x0n, 0x0n);
+testMul(0x3n, BigInt("-1"), BigInt("-3"));
+testMul(0x3n, BigInt("-2"), BigInt("-6"));
+testMul(0x3n, BigInt("-3"), BigInt("-9"));
+testMul(0x3n, BigInt("-4660"), BigInt("-13980"));
+testMul(0x3n, BigInt("-4275878552"), BigInt("-12827635656"));
+testMul(0x3n, BigInt("-18364758544493064720"), BigInt("-55094275633479194160"));
+testMul(0x0n, 0x0n, 0x0n);
+testMul(0x0n, BigInt("-1"), 0x0n);
+testMul(0x0n, BigInt("-2"), 0x0n);
+testMul(0x0n, BigInt("-3"), 0x0n);
+testMul(0x0n, BigInt("-4275878551"), 0x0n);
+testMul(0x0n, BigInt("-18364758544493064719"), 0x0n);
+testMul(BigInt("-1"), BigInt("-1"), 0x1n);
+testMul(BigInt("-1"), BigInt("-2"), 0x2n);
+testMul(BigInt("-1"), BigInt("-3"), 0x3n);
+testMul(BigInt("-1"), BigInt("-4660"), 0x1234n);
+testMul(BigInt("-1"), BigInt("-4275878551"), 0xFEDCBA97n);
+testMul(BigInt("-1"), BigInt("-4275878552"), 0xFEDCBA98n);
+testMul(BigInt("-1"), BigInt("-18364758544493064719"), 0xFEDCBA987654320Fn);
+testMul(BigInt("-1"), BigInt("-18364758544493064720"), 0xFEDCBA9876543210n);
+testMul(BigInt("-3"), BigInt("-3"), 0x9n);
+testMul(BigInt("-3"), BigInt("-4660"), 0x369Cn);
+testMul(BigInt("-3"), BigInt("-4275878551"), 0x2FC962FC5n);
+testMul(BigInt("-3"), BigInt("-4275878552"), 0x2FC962FC8n);
+testMul(BigInt("-3"), BigInt("-18364758544493064719"), 0x2FC962FC962FC962Dn);
+testMul(BigInt("-3"), BigInt("-18364758544493064720"), 0x2FC962FC962FC9630n);
+testMul(BigInt("-18364758544493064720"), BigInt("-18364758544493064720"), 0xFDBAC097C8DC5ACCDEEC6CD7A44A4100n);
+
--- /dev/null
+//@ runBigIntEnabled
+
+function assert(a) {
+ if (!a)
+ throw new Error("Bad assertion");
+}
+
+let a = 0n;
+let b = 1n;
+for (let i = 0; i < 1000000; i++) {
+ a = b * 30n;
+}
+
+assert(a === 30n);
+
+2018-04-26 Caio Lima <ticaiolima@gmail.com>
+
+ [ESNext][BigInt] Implement support for "*" operation
+ https://bugs.webkit.org/show_bug.cgi?id=183721
+
+ Reviewed by Saam Barati.
+
+ Added BigInt support into times binary operator into LLInt and on
+ JITOperations profiledMul and unprofiledMul. We are also replacing all
+ uses of int to unsigned when there is no negative values for
+ variables.
+
+ * dfg/DFGConstantFoldingPhase.cpp:
+ (JSC::DFG::ConstantFoldingPhase::foldConstants):
+ * jit/JITOperations.cpp:
+ * runtime/CommonSlowPaths.cpp:
+ (JSC::SLOW_PATH_DECL):
+ * runtime/JSBigInt.cpp:
+ (JSC::JSBigInt::JSBigInt):
+ (JSC::JSBigInt::allocationSize):
+ (JSC::JSBigInt::createWithLength):
+ (JSC::JSBigInt::toString):
+ (JSC::JSBigInt::multiply):
+ (JSC::JSBigInt::digitDiv):
+ (JSC::JSBigInt::internalMultiplyAdd):
+ (JSC::JSBigInt::multiplyAccumulate):
+ (JSC::JSBigInt::equals):
+ (JSC::JSBigInt::absoluteDivSmall):
+ (JSC::JSBigInt::calculateMaximumCharactersRequired):
+ (JSC::JSBigInt::toStringGeneric):
+ (JSC::JSBigInt::rightTrim):
+ (JSC::JSBigInt::allocateFor):
+ (JSC::JSBigInt::parseInt):
+ (JSC::JSBigInt::digit):
+ (JSC::JSBigInt::setDigit):
+ * runtime/JSBigInt.h:
+ * runtime/Operations.h:
+ (JSC::jsMul):
+
2018-04-26 Mark Lam <mark.lam@apple.com>
Gardening: Speculative build fix for Windows.
JSValue child2Constant = m_state.forNode(node->child2().node()).value();
// FIXME: Revisit this condition when introducing BigInt to JSC.
- auto isNonStringCellConstant = [] (JSValue value) {
- return value && value.isCell() && !value.isString();
+ auto isNonStringOrBigIntCellConstant = [] (JSValue value) {
+ return value && value.isCell() && !value.isString() && !value.isBigInt();
};
- if (isNonStringCellConstant(child1Constant)) {
+ if (isNonStringOrBigIntCellConstant(child1Constant)) {
node->convertToCompareEqPtr(m_graph.freezeStrong(child1Constant.asCell()), node->child2());
changed = true;
- } else if (isNonStringCellConstant(child2Constant)) {
+ } else if (isNonStringOrBigIntCellConstant(child2Constant)) {
node->convertToCompareEqPtr(m_graph.freezeStrong(child2Constant.asCell()), node->child1());
changed = true;
}
return JSValue::encode(result);
}
-ALWAYS_INLINE static EncodedJSValue unprofiledMul(VM& vm, ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)
+ALWAYS_INLINE static EncodedJSValue unprofiledMul(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)
{
- auto scope = DECLARE_THROW_SCOPE(vm);
JSValue op1 = JSValue::decode(encodedOp1);
JSValue op2 = JSValue::decode(encodedOp2);
- double a = op1.toNumber(exec);
- RETURN_IF_EXCEPTION(scope, encodedJSValue());
- scope.release();
- double b = op2.toNumber(exec);
- return JSValue::encode(jsNumber(a * b));
+ return JSValue::encode(jsMul(exec, op1, op2));
}
-ALWAYS_INLINE static EncodedJSValue profiledMul(VM& vm, ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, ArithProfile& arithProfile, bool shouldObserveLHSAndRHSTypes = true)
+ALWAYS_INLINE static EncodedJSValue profiledMul(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, ArithProfile& arithProfile, bool shouldObserveLHSAndRHSTypes = true)
{
- auto scope = DECLARE_THROW_SCOPE(vm);
JSValue op1 = JSValue::decode(encodedOp1);
JSValue op2 = JSValue::decode(encodedOp2);
if (shouldObserveLHSAndRHSTypes)
arithProfile.observeLHSAndRHS(op1, op2);
- double a = op1.toNumber(exec);
- RETURN_IF_EXCEPTION(scope, encodedJSValue());
- double b = op2.toNumber(exec);
- RETURN_IF_EXCEPTION(scope, encodedJSValue());
-
- JSValue result = jsNumber(a * b);
+ JSValue result = jsMul(exec, op1, op2);
arithProfile.observeResult(result);
return JSValue::encode(result);
}
VM* vm = &exec->vm();
NativeCallFrameTracer tracer(vm, exec);
- return unprofiledMul(*vm, exec, encodedOp1, encodedOp2);
+ return unprofiledMul(exec, encodedOp1, encodedOp2);
}
EncodedJSValue JIT_OPERATION operationValueMulNoOptimize(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, JITMulIC*)
VM* vm = &exec->vm();
NativeCallFrameTracer tracer(vm, exec);
- return unprofiledMul(*vm, exec, encodedOp1, encodedOp2);
+ return unprofiledMul(exec, encodedOp1, encodedOp2);
}
EncodedJSValue JIT_OPERATION operationValueMulOptimize(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, JITMulIC* mulIC)
exec->codeBlock()->dumpMathICStats();
#endif
- return unprofiledMul(*vm, exec, encodedOp1, encodedOp2);
+ return unprofiledMul(exec, encodedOp1, encodedOp2);
}
EncodedJSValue JIT_OPERATION operationValueMulProfiled(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, ArithProfile* arithProfile)
NativeCallFrameTracer tracer(vm, exec);
ASSERT(arithProfile);
- return profiledMul(*vm, exec, encodedOp1, encodedOp2, *arithProfile);
+ return profiledMul(exec, encodedOp1, encodedOp2, *arithProfile);
}
EncodedJSValue JIT_OPERATION operationValueMulProfiledOptimize(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, JITMulIC* mulIC)
exec->codeBlock()->dumpMathICStats();
#endif
- return profiledMul(*vm, exec, encodedOp1, encodedOp2, *arithProfile, false);
+ return profiledMul(exec, encodedOp1, encodedOp2, *arithProfile, false);
}
EncodedJSValue JIT_OPERATION operationValueMulProfiledNoOptimize(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, JITMulIC* mulIC)
ArithProfile* arithProfile = mulIC->arithProfile();
ASSERT(arithProfile);
- return profiledMul(*vm, exec, encodedOp1, encodedOp2, *arithProfile);
+ return profiledMul(exec, encodedOp1, encodedOp2, *arithProfile);
}
ALWAYS_INLINE static EncodedJSValue unprofiledNegate(ExecState* exec, EncodedJSValue encodedOperand)
BEGIN();
JSValue left = OP_C(2).jsValue();
JSValue right = OP_C(3).jsValue();
- double a = left.toNumber(exec);
- if (UNLIKELY(throwScope.exception()))
- RETURN(JSValue());
- double b = right.toNumber(exec);
+ JSValue leftPrimitive = left.toPrimitive(exec, PreferNumber);
+ CHECK_EXCEPTION();
+ JSValue rightPrimitive = right.toPrimitive(exec, PreferNumber);
+ CHECK_EXCEPTION();
+
+ if (leftPrimitive.isBigInt() || rightPrimitive.isBigInt()) {
+ if (leftPrimitive.isBigInt() && rightPrimitive.isBigInt()) {
+ JSValue result(JSBigInt::multiply(exec, asBigInt(leftPrimitive), asBigInt(rightPrimitive)));
+ RETURN_WITH_PROFILING(result, {
+ updateArithProfileForBinaryArithOp(exec, pc, result, left, right);
+ });
+ }
+
+ THROW(createTypeError(exec, "Invalid mix of BigInt and other type in multiplication."));
+ }
+
+ double a = leftPrimitive.toNumber(exec);
+ double b = rightPrimitive.toNumber(exec);
JSValue result = jsNumber(a * b);
RETURN_WITH_PROFILING(result, {
updateArithProfileForBinaryArithOp(exec, pc, result, left, right);
Base::visitChildren(thisObject, visitor);
}
-JSBigInt::JSBigInt(VM& vm, Structure* structure, int length)
+JSBigInt::JSBigInt(VM& vm, Structure* structure, unsigned length)
: Base(vm, structure)
, m_length(length)
{ }
return zeroBigInt;
}
-size_t JSBigInt::allocationSize(int length)
+size_t JSBigInt::allocationSize(unsigned length)
{
size_t sizeWithPadding = WTF::roundUpToMultipleOf<sizeof(size_t)>(sizeof(JSBigInt));
return sizeWithPadding + length * sizeof(Digit);
}
-JSBigInt* JSBigInt::createWithLength(VM& vm, int length)
+JSBigInt* JSBigInt::createWithLength(VM& vm, unsigned length)
{
JSBigInt* bigInt = new (NotNull, allocateCell<JSBigInt>(vm.heap, allocationSize(length))) JSBigInt(vm, vm.bigIntStructure.get(), length);
bigInt->finishCreation(vm);
return parseInt(state, vm, s.characters16(), s.length(), 0, radix, false);
}
-String JSBigInt::toString(ExecState& state, int radix)
+String JSBigInt::toString(ExecState& state, unsigned radix)
{
if (this->isZero())
return state.vm().smallStrings.singleCharacterStringRep('0');
internalMultiplyAdd(this, factor, summand, length(), this);
}
+JSBigInt* JSBigInt::multiply(ExecState* state, JSBigInt* x, JSBigInt* y)
+{
+ VM& vm = state->vm();
+
+ if (x->isZero())
+ return x;
+ if (y->isZero())
+ return y;
+
+ unsigned resultLength = x->length() + y->length();
+ JSBigInt* result = JSBigInt::createWithLength(vm, resultLength);
+ result->initialize(InitializationType::WithZero);
+
+ for (unsigned i = 0; i < x->length(); i++)
+ multiplyAccumulate(y, x->digit(i), result, i);
+
+ result->setSign(x->sign() != y->sign());
+ return result->rightTrim(vm);
+}
+
#if USE(JSVALUE32_64)
#define HAVE_TWO_DIGIT 1
typedef uint64_t TwoDigit;
static const Digit kHalfDigitBase = 1ull << halfDigitBits;
// Adapted from Warren, Hacker's Delight, p. 152.
#if USE(JSVALUE64)
- int s = clz64(divisor);
+ unsigned s = clz64(divisor);
#else
- int s = clz32(divisor);
+ unsigned s = clz32(divisor);
#endif
divisor <<= s;
// Multiplies {source} with {factor} and adds {summand} to the result.
// {result} and {source} may be the same BigInt for inplace modification.
-void JSBigInt::internalMultiplyAdd(JSBigInt* source, Digit factor, Digit summand, int n, JSBigInt* result)
+void JSBigInt::internalMultiplyAdd(JSBigInt* source, Digit factor, Digit summand, unsigned n, JSBigInt* result)
{
ASSERT(source->length() >= n);
ASSERT(result->length() >= n);
Digit carry = summand;
Digit high = 0;
- for (int i = 0; i < n; i++) {
+ for (unsigned i = 0; i < n; i++) {
Digit current = source->digit(i);
Digit newCarry = 0;
ASSERT(!(carry + high));
}
+// Multiplies {multiplicand} with {multiplier} and adds the result to
+// {accumulator}, starting at {accumulatorIndex} for the least-significant
+// digit.
+// Callers must ensure that {accumulator} is big enough to hold the result.
+void JSBigInt::multiplyAccumulate(JSBigInt* multiplicand, Digit multiplier, JSBigInt* accumulator, unsigned accumulatorIndex)
+{
+ ASSERT(accumulator->length() > multiplicand->length() + accumulatorIndex);
+ if (!multiplier)
+ return;
+
+ Digit carry = 0;
+ Digit high = 0;
+ for (unsigned i = 0; i < multiplicand->length(); i++, accumulatorIndex++) {
+ Digit acc = accumulator->digit(accumulatorIndex);
+ Digit newCarry = 0;
+
+ // Add last round's carryovers.
+ acc = digitAdd(acc, high, newCarry);
+ acc = digitAdd(acc, carry, newCarry);
+
+ // Compute this round's multiplication.
+ Digit multiplicandDigit = multiplicand->digit(i);
+ Digit low = digitMul(multiplier, multiplicandDigit, high);
+ acc = digitAdd(acc, low, newCarry);
+
+ // Store result and prepare for next round.
+ accumulator->setDigit(accumulatorIndex, acc);
+ carry = newCarry;
+ }
+
+ while (carry || high) {
+ ASSERT(accumulatorIndex < accumulator->length());
+ Digit acc = accumulator->digit(accumulatorIndex);
+ Digit newCarry = 0;
+ acc = digitAdd(acc, high, newCarry);
+ high = 0;
+ acc = digitAdd(acc, carry, newCarry);
+ accumulator->setDigit(accumulatorIndex, acc);
+ carry = newCarry;
+ accumulatorIndex++;
+ }
+}
+
bool JSBigInt::equals(JSBigInt* x, JSBigInt* y)
{
if (x->sign() != y->sign())
if (x->length() != y->length())
return false;
- for (int i = 0; i < x->length(); i++) {
+ for (unsigned i = 0; i < x->length(); i++) {
if (x->digit(i) != y->digit(i))
return false;
}
return;
}
- int length = x->length();
+ unsigned length = x->length();
if (quotient != nullptr) {
if (*quotient == nullptr)
*quotient = JSBigInt::createWithLength(vm, length);
162, 163, 165, 166, // 33..36
};
-static const int bitsPerCharTableShift = 5;
+static const unsigned bitsPerCharTableShift = 5;
static const size_t bitsPerCharTableMultiplier = 1u << bitsPerCharTableShift;
// Compute (an overapproximation of) the length of the resulting string:
// Divide bit length of the BigInt by bits representable per character.
-uint64_t JSBigInt::calculateMaximumCharactersRequired(int length, int radix, Digit lastDigit, bool sign)
+uint64_t JSBigInt::calculateMaximumCharactersRequired(unsigned length, unsigned radix, Digit lastDigit, bool sign)
{
- int leadingZeros;
+ unsigned leadingZeros;
if (sizeof(lastDigit) == 8)
leadingZeros = clz64(lastDigit);
else
return maximumCharactersRequired;
}
-String JSBigInt::toStringGeneric(ExecState& state, JSBigInt* x, int radix)
+String JSBigInt::toStringGeneric(ExecState& state, JSBigInt* x, unsigned radix)
{
// FIXME: [JSC] Revisit usage of StringVector into JSBigInt::toString
// https://bugs.webkit.org/show_bug.cgi?id=18067
ASSERT(radix >= 2 && radix <= 36);
ASSERT(!x->isZero());
- int length = x->length();
+ unsigned length = x->length();
bool sign = x->sign();
uint8_t maxBitsPerChar = maxBitsPerCharTable[radix];
if (length == 1)
lastDigit = x->digit(0);
else {
- int chunkChars = digitBits * bitsPerCharTableMultiplier / maxBitsPerChar;
+ unsigned chunkChars = digitBits * bitsPerCharTableMultiplier / maxBitsPerChar;
Digit chunkDivisor = digitPow(radix, chunkChars);
// By construction of chunkChars, there can't have been overflow.
ASSERT(chunkDivisor);
- int nonZeroDigit = length - 1;
+ unsigned nonZeroDigit = length - 1;
ASSERT(x->digit(nonZeroDigit));
// {rest} holds the part of the BigInt that we haven't looked at yet.
ASSERT(rest);
dividend = &rest;
- for (int i = 0; i < chunkChars; i++) {
+ for (unsigned i = 0; i < chunkChars; i++) {
resultString.append(radixDigits[chunk % radix]);
chunk /= radix;
}
ASSERT(resultString.size() <= static_cast<size_t>(maximumCharactersRequired));
// Remove leading zeroes.
- int newSizeNoLeadingZeroes = resultString.size();
+ unsigned newSizeNoLeadingZeroes = resultString.size();
while (newSizeNoLeadingZeroes > 1 && resultString[newSizeNoLeadingZeroes - 1] == '0')
newSizeNoLeadingZeroes--;
if (isZero())
return this;
+ ASSERT(m_length);
+
int nonZeroIndex = m_length - 1;
while (nonZeroIndex >= 0 && !digit(nonZeroIndex))
nonZeroIndex--;
- if (nonZeroIndex == m_length - 1)
+ if (nonZeroIndex == static_cast<int>(m_length - 1))
return this;
- int newLength = nonZeroIndex + 1;
+ unsigned newLength = nonZeroIndex + 1;
JSBigInt* trimmedBigInt = createWithLength(vm, newLength);
RELEASE_ASSERT(trimmedBigInt);
std::copy(dataStorage(), dataStorage() + newLength, trimmedBigInt->dataStorage());
return trimmedBigInt;
}
-JSBigInt* JSBigInt::allocateFor(ExecState* state, VM& vm, int radix, int charcount)
+JSBigInt* JSBigInt::allocateFor(ExecState* state, VM& vm, unsigned radix, unsigned charcount)
{
ASSERT(2 <= radix && radix <= 36);
ASSERT(charcount >= 0);
size_t bitsPerChar = maxBitsPerCharTable[radix];
size_t chars = charcount;
- const int roundup = bitsPerCharTableMultiplier - 1;
+ const unsigned roundup = bitsPerCharTableMultiplier - 1;
if (chars <= (std::numeric_limits<size_t>::max() - roundup) / bitsPerChar) {
size_t bitsMin = bitsPerChar * chars;
bitsMin = (bitsMin + roundup) >> bitsPerCharTableShift;
if (bitsMin <= static_cast<size_t>(maxInt)) {
// Divide by kDigitsBits, rounding up.
- int length = (static_cast<int>(bitsMin) + digitBits - 1) / digitBits;
+ unsigned length = (bitsMin + digitBits - 1) / digitBits;
if (length <= maxLength) {
JSBigInt* result = JSBigInt::createWithLength(vm, length);
return result;
}
template <typename CharType>
-JSBigInt* JSBigInt::parseInt(ExecState* state, CharType* data, int length)
+JSBigInt* JSBigInt::parseInt(ExecState* state, CharType* data, unsigned length)
{
VM& vm = state->vm();
- int p = 0;
+ unsigned p = 0;
while (p < length && isStrWhiteSpace(data[p]))
++p;
}
template <typename CharType>
-JSBigInt* JSBigInt::parseInt(ExecState* state, VM& vm, CharType* data, int length, int startIndex, int radix, bool allowEmptyString)
+JSBigInt* JSBigInt::parseInt(ExecState* state, VM& vm, CharType* data, unsigned length, unsigned startIndex, unsigned radix, bool allowEmptyString)
{
ASSERT(length >= 0);
- int p = startIndex;
+ unsigned p = startIndex;
auto scope = DECLARE_THROW_SCOPE(vm);
int endIndex = length - 1;
// Removing trailing spaces
- while (endIndex >= p && isStrWhiteSpace(data[endIndex]))
+ while (endIndex >= static_cast<int>(p) && isStrWhiteSpace(data[endIndex]))
--endIndex;
length = endIndex + 1;
if (p == length)
return createZero(vm);
- int limit0 = '0' + (radix < 10 ? radix : 10);
- int limita = 'a' + (radix - 10);
- int limitA = 'A' + (radix - 10);
+ unsigned limit0 = '0' + (radix < 10 ? radix : 10);
+ unsigned limita = 'a' + (radix - 10);
+ unsigned limitA = 'A' + (radix - 10);
JSBigInt* result = allocateFor(state, vm, radix, length - p);
RETURN_IF_EXCEPTION(scope, nullptr);
result->initialize(InitializationType::WithZero);
- for (int i = p; i < length; i++, p++) {
+ for (unsigned i = p; i < length; i++, p++) {
uint32_t digit;
if (data[i] >= '0' && data[i] < limit0)
digit = data[i] - '0';
return reinterpret_cast<Digit*>(reinterpret_cast<char*>(this) + offsetOfData());
}
-JSBigInt::Digit JSBigInt::digit(int n)
+JSBigInt::Digit JSBigInt::digit(unsigned n)
{
ASSERT(n >= 0 && n < length());
return dataStorage()[n];
}
-void JSBigInt::setDigit(int n, Digit value)
+void JSBigInt::setDigit(unsigned n, Digit value)
{
ASSERT(n >= 0 && n < length());
dataStorage()[n] = value;
public:
- JSBigInt(VM&, Structure*, int length);
+ JSBigInt(VM&, Structure*, unsigned length);
enum class InitializationType { None, WithZero };
void initialize(InitializationType);
static void visitChildren(JSCell*, SlotVisitor&);
static size_t estimatedSize(JSCell*);
- static size_t allocationSize(int length);
+ static size_t allocationSize(unsigned length);
static Structure* createStructure(VM&, JSGlobalObject*, JSValue prototype);
-
static JSBigInt* createZero(VM&);
- static JSBigInt* createWithLength(VM&, int length);
+ static JSBigInt* createWithLength(VM&, unsigned length);
static JSBigInt* createFrom(VM&, int32_t value);
static JSBigInt* createFrom(VM&, uint32_t value);
void setSign(bool sign) { m_sign = sign; }
bool sign() const { return m_sign; }
- void setLength(int length) { m_length = length; }
- int length() const { return m_length; }
+ void setLength(unsigned length) { m_length = length; }
+ unsigned length() const { return m_length; }
static JSBigInt* parseInt(ExecState*, VM&, StringView, uint8_t radix);
static JSBigInt* parseInt(ExecState*, StringView);
std::optional<uint8_t> singleDigitValueForString();
- String toString(ExecState&, int radix);
+ String toString(ExecState&, unsigned radix);
JS_EXPORT_PRIVATE static bool equals(JSBigInt*, JSBigInt*);
double toNumber(ExecState*) const;
JSObject* toObject(ExecState*, JSGlobalObject*) const;
+
+ static JSBigInt* multiply(ExecState*, JSBigInt* x, JSBigInt* y);
private:
using Digit = uintptr_t;
- static constexpr const int bitsPerByte = 8;
- static constexpr const int digitBits = sizeof(Digit) * bitsPerByte;
- static constexpr const int halfDigitBits = digitBits / 2;
+ static constexpr const unsigned bitsPerByte = 8;
+ static constexpr const unsigned digitBits = sizeof(Digit) * bitsPerByte;
+ static constexpr const unsigned halfDigitBits = digitBits / 2;
static constexpr const Digit halfDigitMask = (1ull << halfDigitBits) - 1;
static constexpr const int maxInt = 0x7FFFFFFF;
// maxInt / digitBits. However, we use a lower limit for now, because
// raising it later is easier than lowering it.
// Support up to 1 million bits.
- static const int maxLength = 1024 * 1024 / (sizeof(void*) * bitsPerByte);
+ static const unsigned maxLength = 1024 * 1024 / (sizeof(void*) * bitsPerByte);
- static uint64_t calculateMaximumCharactersRequired(int length, int radix, Digit lastDigit, bool sign);
+ static uint64_t calculateMaximumCharactersRequired(unsigned length, unsigned radix, Digit lastDigit, bool sign);
static void absoluteDivSmall(ExecState&, JSBigInt* x, Digit divisor, JSBigInt** quotient, Digit& remainder);
- static void internalMultiplyAdd(JSBigInt* source, Digit factor, Digit summand, int, JSBigInt* result);
+ static void internalMultiplyAdd(JSBigInt* source, Digit factor, Digit summand, unsigned, JSBigInt* result);
+ static void multiplyAccumulate(JSBigInt* multiplicand, Digit multiplier, JSBigInt* accumulator, unsigned accumulatorIndex);
// Digit arithmetic helpers.
static Digit digitAdd(Digit a, Digit b, Digit& carry);
static Digit digitDiv(Digit high, Digit low, Digit divisor, Digit& remainder);
static Digit digitPow(Digit base, Digit exponent);
- static String toStringGeneric(ExecState&, JSBigInt*, int radix);
+ static String toStringGeneric(ExecState&, JSBigInt*, unsigned radix);
bool isZero();
template <typename CharType>
- static JSBigInt* parseInt(ExecState*, CharType* data, int length);
+ static JSBigInt* parseInt(ExecState*, CharType* data, unsigned length);
template <typename CharType>
- static JSBigInt* parseInt(ExecState*, VM&, CharType* data, int length, int startIndex, int radix, bool allowEmptyString = true);
+ static JSBigInt* parseInt(ExecState*, VM&, CharType* data, unsigned length, unsigned startIndex, unsigned radix, bool allowEmptyString = true);
- static JSBigInt* allocateFor(ExecState*, VM&, int radix, int charcount);
+ static JSBigInt* allocateFor(ExecState*, VM&, unsigned radix, unsigned charcount);
JSBigInt* rightTrim(VM&);
static size_t offsetOfData();
Digit* dataStorage();
- Digit digit(int);
- void setDigit(int, Digit);
+ Digit digit(unsigned);
+ void setDigit(unsigned, Digit);
- int m_length;
+ unsigned m_length;
bool m_sign;
};
#include "CallFrame.h"
#include "ExceptionHelpers.h"
+#include "JSBigInt.h"
#include "JSCJSValue.h"
namespace JSC {
return jsAddSlowCase(callFrame, v1, v2);
}
+ALWAYS_INLINE JSValue jsMul(ExecState* state, JSValue v1, JSValue v2)
+{
+ VM& vm = state->vm();
+ auto scope = DECLARE_THROW_SCOPE(vm);
+
+ JSValue leftNumber = v1.toPrimitive(state, PreferNumber);
+ RETURN_IF_EXCEPTION(scope, { });
+ JSValue rightNumber = v2.toPrimitive(state, PreferNumber);
+ RETURN_IF_EXCEPTION(scope, { });
+
+ if (leftNumber.isBigInt() || rightNumber.isBigInt()) {
+ if (leftNumber.isBigInt() && rightNumber.isBigInt())
+ return JSBigInt::multiply(state, asBigInt(leftNumber), asBigInt(rightNumber));
+
+ throwTypeError(state, scope, ASCIILiteral("Invalid mix of BigInt and other type in multiplication."));
+ return { };
+ }
+
+ double leftValue = leftNumber.toNumber(state);
+ double rightValue = rightNumber.toNumber(state);
+ return jsNumber(leftValue * rightValue);
+}
+
inline bool scribbleFreeCells()
{
return !ASSERT_DISABLED || Options::scribbleFreeCells();