summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrian Smith <brian@dbsoft.org>2023-09-17 04:23:57 -0500
committerMoonchild <moonchild@palemoon.org>2023-09-20 03:33:41 +0200
commit80ae8aec7cf0e08467aadad6087710afeef36b40 (patch)
tree57aa428a751bdf45733ed1102a091133b0506c37
parentd8441d7c8175608835f3b56a76666f48e4da0548 (diff)
downloaduxp-80ae8aec7cf0e08467aadad6087710afeef36b40.tar.gz
Issue #2308 & #1240 Follow-up - Add BigInt support to JSOP_INC and JSOP_DEC.
https://bugzilla.mozilla.org/show_bug.cgi?id=1526309
-rw-r--r--js/public/TrackedOptimizationInfo.h5
-rw-r--r--js/src/jit/IonBuilder.cpp201
-rw-r--r--js/src/jit/IonBuilder.h11
-rw-r--r--js/src/jit/MIR.cpp17
-rw-r--r--js/src/jit/MIR.h1
-rw-r--r--js/src/vm/BigIntType.cpp143
-rw-r--r--js/src/vm/BigIntType.h10
-rw-r--r--js/src/vm/Interpreter-inl.h22
8 files changed, 283 insertions, 127 deletions
diff --git a/js/public/TrackedOptimizationInfo.h b/js/public/TrackedOptimizationInfo.h
index ce3508cd68..ebaf5915cd 100644
--- a/js/public/TrackedOptimizationInfo.h
+++ b/js/public/TrackedOptimizationInfo.h
@@ -57,6 +57,10 @@ namespace JS {
_(BinaryArith_SharedCache) \
_(BinaryArith_Call) \
\
+ _(UnaryArith_SpecializedTypes) \
+ _(UnaryArith_SpecializedOnBaselineTypes) \
+ _(UnaryArith_InlineCache) \
+ \
_(InlineCache_OptimizedStub) \
\
_(Call_Inline)
@@ -112,6 +116,7 @@ namespace JS {
_(GetElemStringNotCached) \
_(NonNativeReceiver) \
_(IndexType) \
+ _(SpeculationOnInputTypesFailed) \
_(SetElemNonDenseNonTANotCached) \
_(NoSimdJitSupport) \
_(SimdTypeNotOptimized) \
diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp
index 2134d1b613..fdbdb55bd8 100644
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -4858,7 +4858,7 @@ IonBuilder::jsop_bitop(JSOp op)
}
MDefinition::Opcode
-JSOpToMDefinition(JSOp op)
+BinaryJSOpToMDefinition(JSOp op)
{
switch (op) {
case JSOP_ADD:
@@ -4959,6 +4959,40 @@ IonBuilder::powTrySpecialized(bool* emitted, MDefinition* base, MDefinition* pow
return true;
}
+MIRType
+IonBuilder::binaryArithNumberSpecialization(MDefinition* left, MDefinition* right)
+{
+ // Try to specialize as int32.
+ if (left->type() == MIRType::Int32 && right->type() == MIRType::Int32 &&
+ !inspector->hasSeenDoubleResult(pc)) {
+ return MIRType::Int32;
+ }
+ return MIRType::Double;
+}
+
+MBinaryArithInstruction*
+IonBuilder::binaryArithEmitSpecialized(MDefinition::Opcode op, MIRType specialization,
+ MDefinition* left, MDefinition* right)
+{
+ MBinaryArithInstruction* ins = MBinaryArithInstruction::New(alloc(), op, left, right);
+ ins->setSpecialization(specialization);
+
+ if (op == MDefinition::Op_Add || op == MDefinition::Op_Mul) {
+ ins->setCommutative();
+ }
+
+ current->add(ins);
+ current->push(ins);
+
+ MOZ_ASSERT(!ins->isEffectful());
+
+ if(!maybeInsertResume()) {
+ return nullptr;
+ }
+
+ return ins;
+}
+
static inline bool
SimpleArithOperand(MDefinition* op)
{
@@ -4992,19 +5026,20 @@ IonBuilder::binaryArithTrySpecialized(bool* emitted, JSOp op, MDefinition* left,
return true;
}
- MDefinition::Opcode defOp = JSOpToMDefinition(op);
- MBinaryArithInstruction* ins = MBinaryArithInstruction::New(alloc(), defOp, left, right);
- ins->setNumberSpecialization(alloc(), inspector, pc);
-
- if (op == JSOP_ADD || op == JSOP_MUL)
- ins->setCommutative();
-
- current->add(ins);
- current->push(ins);
+ MDefinition::Opcode defOp = BinaryJSOpToMDefinition(op);
+ MIRType specialization = binaryArithNumberSpecialization(left, right);
+ MBinaryArithInstruction* ins = binaryArithEmitSpecialized(defOp, specialization, left, right);
- MOZ_ASSERT(!ins->isEffectful());
- if (!maybeInsertResume())
+ if(!ins) {
return false;
+ }
+
+ // Relax int32 to double if, despite the fact that we have int32 operands and
+ // we've never seen a double result, we know the result may overflow or be a
+ // double.
+ if (specialization == MIRType::Int32 && ins->constantDoubleResult(alloc())) {
+ ins->setSpecialization(MIRType::Double);
+ }
trackOptimizationSuccess();
*emitted = true;
@@ -5028,16 +5063,10 @@ IonBuilder::binaryArithTrySpecializedOnBaselineInspector(bool* emitted, JSOp op,
return true;
}
- MDefinition::Opcode def_op = JSOpToMDefinition(op);
- MBinaryArithInstruction* ins = MBinaryArithInstruction::New(alloc(), def_op, left, right);
- ins->setSpecialization(specialization);
-
- current->add(ins);
- current->push(ins);
-
- MOZ_ASSERT(!ins->isEffectful());
- if (!maybeInsertResume())
+ MDefinition::Opcode defOp = BinaryJSOpToMDefinition(op);
+ if(!binaryArithEmitSpecialized(defOp, specialization, left, right)) {
return false;
+ }
trackOptimizationSuccess();
*emitted = true;
@@ -5076,14 +5105,6 @@ IonBuilder::arithTrySharedStub(bool* emitted, JSOp op,
stub = MUnarySharedStub::New(alloc(), right);
break;
- case JSOP_INC:
- MOZ_ASSERT(op == JSOP_ADD && right->toConstant()->toInt32() == 1);
- stub = MUnarySharedStub::New(alloc(), left);
- break;
- case JSOP_DEC:
- MOZ_ASSERT(op == JSOP_SUB && right->toConstant()->toInt32() == 1);
- stub = MUnarySharedStub::New(alloc(), left);
- break;
case JSOP_ADD:
case JSOP_SUB:
case JSOP_MUL:
@@ -5138,8 +5159,8 @@ IonBuilder::jsop_binary_arith(JSOp op, MDefinition* left, MDefinition* right)
trackOptimizationAttempt(TrackedStrategy::BinaryArith_Call);
trackOptimizationSuccess();
- MDefinition::Opcode def_op = JSOpToMDefinition(op);
- MBinaryArithInstruction* ins = MBinaryArithInstruction::New(alloc(), def_op, left, right);
+ MDefinition::Opcode defOp = BinaryJSOpToMDefinition(op);
+ MBinaryArithInstruction* ins = MBinaryArithInstruction::New(alloc(), defOp, left, right);
// Decrease type from 'any type' to 'empty type' when one of the operands
// is 'empty typed'.
@@ -5216,27 +5237,117 @@ IonBuilder::jsop_neg()
return jsop_binary_arith(JSOP_MUL, negator, right);
}
+MDefinition*
+IonBuilder::unaryArithConvertToBinary(JSOp op, MDefinition::Opcode* defOp)
+{
+ switch (op) {
+ case JSOP_INC: {
+ *defOp = MDefinition::Op_Add;
+ MConstant* right = MConstant::New(alloc(), Int32Value(1));
+ current->add(right);
+ return right;
+ }
+ case JSOP_DEC: {
+ *defOp = MDefinition::Op_Sub;
+ MConstant* right = MConstant::New(alloc(), Int32Value(1));
+ current->add(right);
+ return right;
+ }
+ default:
+ MOZ_CRASH("unexpected unary opcode");
+ }
+}
+
bool
-IonBuilder::jsop_inc_or_dec(JSOp op)
+IonBuilder::unaryArithTrySpecialized(bool* emitted, JSOp op, MDefinition* value)
{
- // As above, pass constant without slot traffic.
- MConstant* one = MConstant::New(alloc(), Int32Value(1));
- current->add(one);
+ MOZ_ASSERT(*emitted == false);
+ // Try to convert Inc(x) or Dec(x) to Add(x,1) or Sub(x,1) if the operand is a
+ // number.
+
+ trackOptimizationAttempt(TrackedStrategy::UnaryArith_SpecializedTypes);
+
+ if (!IsNumberType(value->type())) {
+ trackOptimizationOutcome(TrackedOutcome::OperandNotNumber);
+ return true;
+ }
+
+ MDefinition::Opcode defOp;
+ MDefinition* rhs = unaryArithConvertToBinary(op, &defOp);
+ MIRType specialization = binaryArithNumberSpecialization(value, rhs);
+ if (!binaryArithEmitSpecialized(defOp, specialization, value, rhs)) {
+ return false;
+ }
+
+ trackOptimizationSuccess();
+ *emitted = true;
+ return true;
+}
+
+bool
+IonBuilder::unaryArithTrySpecializedOnBaselineInspector(bool* emitted, JSOp op, MDefinition* value)
+{
+ MOZ_ASSERT(*emitted == false);
+
+ // Try to emit a specialized binary instruction speculating the
+ // type using the baseline caches.
+
+ trackOptimizationAttempt(TrackedStrategy::UnaryArith_SpecializedOnBaselineTypes);
+
+ MIRType specialization = inspector->expectedBinaryArithSpecialization(pc);
+ if (specialization == MIRType::None) {
+ trackOptimizationOutcome(TrackedOutcome::SpeculationOnInputTypesFailed);
+ return true;
+ }
+
+ MDefinition::Opcode defOp;
+ MDefinition* rhs = unaryArithConvertToBinary(op, &defOp);
+ if (!binaryArithEmitSpecialized(defOp, specialization, value, rhs)) {
+ return false;
+ }
+
+ trackOptimizationSuccess();
+ *emitted = true;
+ return true;
+}
+
+bool
+IonBuilder::jsop_inc_or_dec(JSOp op)
+{
+ bool emitted = false;
MDefinition* value = current->pop();
- switch (op) {
- case JSOP_INC:
- op = JSOP_ADD;
- break;
- case JSOP_DEC:
- op = JSOP_SUB;
- break;
- default:
- MOZ_CRASH("jsop_inc_or_dec with bad op");
+ startTrackingOptimizations();
+
+ trackTypeInfo(TrackedTypeSite::Operand, value->type(), value->resultTypeSet());
+
+ if (!unaryArithTrySpecialized(&emitted, op, value)) {
+ return false;
}
+ if (emitted) {
+ return true;
+ }
+
+ if (!unaryArithTrySpecializedOnBaselineInspector(&emitted, op, value)) {
+ return false;
+ }
+ if (emitted) {
+ return true;
+ }
+
+ trackOptimizationAttempt(TrackedStrategy::UnaryArith_InlineCache);
+ trackOptimizationSuccess();
+
+ MInstruction* stub = MUnarySharedStub::New(alloc(), value);
+ current->add(stub);
+ current->push(stub);
+
+ // Decrease type from 'any type' to 'empty type' when one of the operands
+ // is 'empty typed'.
+ maybeMarkEmpty(stub);
- return jsop_binary_arith(op, value, one);
+ return resumeAfter(stub);
}
bool
diff --git a/js/src/jit/IonBuilder.h b/js/src/jit/IonBuilder.h
index 162edb36f2..f12a24bcfc 100644
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -497,8 +497,13 @@ class IonBuilder
// jsop_binary_arith helpers.
MBinaryArithInstruction* binaryArithInstruction(JSOp op, MDefinition* left, MDefinition* right);
+ MIRType binaryArithNumberSpecialization(MDefinition* left, MDefinition* right);
MOZ_MUST_USE bool binaryArithTryConcat(bool* emitted, JSOp op, MDefinition* left,
MDefinition* right);
+ MOZ_MUST_USE MBinaryArithInstruction* binaryArithEmitSpecialized(MDefinition::Opcode op,
+ MIRType specialization,
+ MDefinition* left,
+ MDefinition* right);
MOZ_MUST_USE bool binaryArithTrySpecialized(bool* emitted, JSOp op, MDefinition* left,
MDefinition* right);
MOZ_MUST_USE bool binaryArithTrySpecializedOnBaselineInspector(bool* emitted, JSOp op,
@@ -510,6 +515,12 @@ class IonBuilder
// jsop_bitnot helpers.
MOZ_MUST_USE bool bitnotTrySpecialized(bool* emitted, MDefinition* input);
+ // jsop_inc_or_dec helpers.
+ MDefinition* unaryArithConvertToBinary(JSOp op, MDefinition::Opcode* defOp);
+ MOZ_MUST_USE bool unaryArithTrySpecialized(bool* emitted, JSOp op, MDefinition* value);
+ MOZ_MUST_USE bool unaryArithTrySpecializedOnBaselineInspector(bool* emitted, JSOp op,
+ MDefinition* value);
+
// jsop_pow helpers.
MOZ_MUST_USE bool powTrySpecialized(bool* emitted, MDefinition* base, MDefinition* power,
MIRType outputType);
diff --git a/js/src/jit/MIR.cpp b/js/src/jit/MIR.cpp
index 2264bed4f2..05e37cdc6a 100644
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -3110,23 +3110,6 @@ MBinaryArithInstruction::New(TempAllocator& alloc, Opcode op,
}
}
-void
-MBinaryArithInstruction::setNumberSpecialization(TempAllocator& alloc, BaselineInspector* inspector,
- jsbytecode* pc)
-{
- setSpecialization(MIRType::Double);
-
- // Try to specialize as int32.
- if (getOperand(0)->type() == MIRType::Int32 && getOperand(1)->type() == MIRType::Int32) {
- bool seenDouble = inspector->hasSeenDoubleResult(pc);
-
- // Use int32 specialization if the operation doesn't overflow on its
- // constant operands and if the operation has never overflowed.
- if (!seenDouble && !constantDoubleResult(alloc))
- setInt32Specialization();
- }
-}
-
bool
MBinaryArithInstruction::constantDoubleResult(TempAllocator& alloc)
{
diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h
index 72c5214845..327310122c 100644
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -6219,7 +6219,6 @@ class MBinaryArithInstruction
specialization_ = MIRType::Int32;
setResultType(MIRType::Int32);
}
- void setNumberSpecialization(TempAllocator& alloc, BaselineInspector* inspector, jsbytecode* pc);
virtual void trySpecializeFloat32(TempAllocator& alloc) override;
diff --git a/js/src/vm/BigIntType.cpp b/js/src/vm/BigIntType.cpp
index 8382c641fa..1f8b061e02 100644
--- a/js/src/vm/BigIntType.cpp
+++ b/js/src/vm/BigIntType.cpp
@@ -185,16 +185,21 @@ BigInt* BigInt::zero(ExclusiveContext* cx) {
return createUninitialized(cx, 0, false);
}
-BigInt* BigInt::one(ExclusiveContext* cx) {
- BigInt* ret = createUninitialized(cx, 1, false);
-
- if (!ret) {
+BigInt* BigInt::createFromDigit(ExclusiveContext* cx, Digit d, bool isNegative) {
+ MOZ_ASSERT(d != 0);
+ BigInt* res = createUninitialized(cx, 1, isNegative);
+ if (!res) {
return nullptr;
}
- ret->setDigit(0, 1);
+ res->setDigit(0, d);
+ return res;
+}
- return ret;
+BigInt* BigInt::one(ExclusiveContext* cx) { return createFromDigit(cx, 1, false); }
+
+BigInt* BigInt::negativeOne(ExclusiveContext* cx) {
+ return createFromDigit(cx, 1, true);
}
BigInt* BigInt::neg(ExclusiveContext* cx, HandleBigInt x) {
@@ -977,21 +982,26 @@ BigInt* BigInt::absoluteAddOne(ExclusiveContext* cx, HandleBigInt x,
return destructivelyTrimHighZeroDigits(cx, result);
}
-// Like the above, but you can specify that the allocated result should have
-// length `resultLength`, which must be at least as large as `x->digitLength()`.
-// The result will be unsigned.
BigInt* BigInt::absoluteSubOne(ExclusiveContext* cx, HandleBigInt x,
- unsigned resultLength) {
+ bool resultNegative) {
MOZ_ASSERT(!x->isZero());
- MOZ_ASSERT(resultLength >= x->digitLength());
- bool resultNegative = false;
- RootedBigInt result(cx,
- createUninitialized(cx, resultLength, resultNegative));
+
+ unsigned length = x->digitLength();
+
+ if (length == 1) {
+ Digit d = x->digit(0);
+ if (d == 1) {
+ // Ignore resultNegative.
+ return zero(cx);
+ }
+ return createFromDigit(cx, d - 1, resultNegative);
+ }
+
+ RootedBigInt result(cx, createUninitialized(cx, length, resultNegative));
if (!result) {
return nullptr;
}
- unsigned length = x->digitLength();
Digit borrow = 1;
for (unsigned i = 0; i < length; i++) {
Digit newBorrow = 0;
@@ -999,13 +1009,36 @@ BigInt* BigInt::absoluteSubOne(ExclusiveContext* cx, HandleBigInt x,
borrow = newBorrow;
}
MOZ_ASSERT(!borrow);
- for (unsigned i = length; i < resultLength; i++) {
- result->setDigit(i, 0);
- }
return destructivelyTrimHighZeroDigits(cx, result);
}
+BigInt* BigInt::inc(ExclusiveContext* cx, HandleBigInt x) {
+ if (x->isZero()) {
+ return one(cx);
+ }
+
+ bool isNegative = x->isNegative();
+ if (isNegative) {
+ return absoluteSubOne(cx, x, isNegative);
+ }
+
+ return absoluteAddOne(cx, x, isNegative);
+}
+
+BigInt* BigInt::dec(ExclusiveContext* cx, HandleBigInt x) {
+ if (x->isZero()) {
+ return negativeOne(cx);
+ }
+
+ bool isNegative = x->isNegative();
+ if (isNegative) {
+ return absoluteAddOne(cx, x, isNegative);
+ }
+
+ return absoluteSubOne(cx, x, isNegative);
+}
+
// Lookup table for the maximum number of bits required per character of a
// base-N string representation of a number. To increase accuracy, the array
// value is the actual value multiplied by 32. To generate this table:
@@ -1569,13 +1602,7 @@ BigInt* BigInt::createFromUint64(ExclusiveContext* cx, uint64_t n) {
return res;
}
- BigInt* res = createUninitialized(cx, 1, isNegative);
- if (!res) {
- return nullptr;
- }
-
- res->setDigit(0, n);
- return res;
+ return createFromDigit(cx, n, isNegative);
}
BigInt* BigInt::createFromInt64(ExclusiveContext* cx, int64_t n) {
@@ -1770,12 +1797,7 @@ BigInt* BigInt::mod(ExclusiveContext* cx, HandleBigInt x, HandleBigInt y) {
return zero(cx);
}
- BigInt* remainder = createUninitialized(cx, 1, x->isNegative());
- if (!remainder) {
- return nullptr;
- }
- remainder->setDigit(0, remainderDigit);
- return remainder;
+ return createFromDigit(cx, remainderDigit, x->isNegative());
} else {
RootedBigInt remainder(cx);
if (!absoluteDivWithBigIntDivisor(cx, x, y, Nothing(), Some(&remainder),
@@ -1930,15 +1952,7 @@ BigInt* BigInt::lshByAbsolute(ExclusiveContext* cx, HandleBigInt x, HandleBigInt
}
BigInt* BigInt::rshByMaximum(ExclusiveContext* cx, bool isNegative) {
- if (isNegative) {
- RootedBigInt negativeOne(cx, createUninitialized(cx, 1, isNegative));
- if (!negativeOne) {
- return nullptr;
- }
- negativeOne->setDigit(0, 1);
- return negativeOne;
- }
- return zero(cx);
+ return isNegative ? negativeOne(cx) : zero(cx);
}
BigInt* BigInt::rshByAbsolute(ExclusiveContext* cx, HandleBigInt x, HandleBigInt y) {
@@ -2049,14 +2063,13 @@ BigInt* BigInt::bitAnd(ExclusiveContext* cx, HandleBigInt x, HandleBigInt y) {
}
if (x->isNegative() && y->isNegative()) {
- int resultLength = std::max(x->digitLength(), y->digitLength()) + 1;
// (-x) & (-y) == ~(x-1) & ~(y-1) == ~((x-1) | (y-1))
// == -(((x-1) | (y-1)) + 1)
- RootedBigInt x1(cx, absoluteSubOne(cx, x, resultLength));
+ RootedBigInt x1(cx, absoluteSubOne(cx, x));
if (!x1) {
return nullptr;
}
- RootedBigInt y1(cx, absoluteSubOne(cx, y, y->digitLength()));
+ RootedBigInt y1(cx, absoluteSubOne(cx, y));
if (!y1) {
return nullptr;
}
@@ -2072,7 +2085,7 @@ BigInt* BigInt::bitAnd(ExclusiveContext* cx, HandleBigInt x, HandleBigInt y) {
HandleBigInt& pos = x->isNegative() ? y : x;
HandleBigInt& neg = x->isNegative() ? x : y;
- RootedBigInt neg1(cx, absoluteSubOne(cx, neg, neg->digitLength()));
+ RootedBigInt neg1(cx, absoluteSubOne(cx, neg));
if (!neg1) {
return nullptr;
}
@@ -2096,27 +2109,24 @@ BigInt* BigInt::bitXor(ExclusiveContext* cx, HandleBigInt x, HandleBigInt y) {
}
if (x->isNegative() && y->isNegative()) {
- int resultLength = std::max(x->digitLength(), y->digitLength());
-
// (-x) ^ (-y) == ~(x-1) ^ ~(y-1) == (x-1) ^ (y-1)
- RootedBigInt x1(cx, absoluteSubOne(cx, x, resultLength));
+ RootedBigInt x1(cx, absoluteSubOne(cx, x));
if (!x1) {
return nullptr;
}
- RootedBigInt y1(cx, absoluteSubOne(cx, y, y->digitLength()));
+ RootedBigInt y1(cx, absoluteSubOne(cx, y));
if (!y1) {
return nullptr;
}
return absoluteXor(cx, x1, y1);
}
MOZ_ASSERT(x->isNegative() != y->isNegative());
- int resultLength = std::max(x->digitLength(), y->digitLength()) + 1;
HandleBigInt& pos = x->isNegative() ? y : x;
HandleBigInt& neg = x->isNegative() ? x : y;
// x ^ (-y) == x ^ ~(y-1) == ~(x ^ (y-1)) == -((x ^ (y-1)) + 1)
- RootedBigInt result(cx, absoluteSubOne(cx, neg, resultLength));
+ RootedBigInt result(cx, absoluteSubOne(cx, neg));
if (!result) {
return nullptr;
}
@@ -2138,7 +2148,6 @@ BigInt* BigInt::bitOr(ExclusiveContext* cx, HandleBigInt x, HandleBigInt y) {
return x;
}
- unsigned resultLength = std::max(x->digitLength(), y->digitLength());
bool resultNegative = x->isNegative() || y->isNegative();
if (!resultNegative) {
@@ -2148,11 +2157,11 @@ BigInt* BigInt::bitOr(ExclusiveContext* cx, HandleBigInt x, HandleBigInt y) {
if (x->isNegative() && y->isNegative()) {
// (-x) | (-y) == ~(x-1) | ~(y-1) == ~((x-1) & (y-1))
// == -(((x-1) & (y-1)) + 1)
- RootedBigInt result(cx, absoluteSubOne(cx, x, resultLength));
+ RootedBigInt result(cx, absoluteSubOne(cx, x));
if (!result) {
return nullptr;
}
- RootedBigInt y1(cx, absoluteSubOne(cx, y, y->digitLength()));
+ RootedBigInt y1(cx, absoluteSubOne(cx, y));
if (!y1) {
return nullptr;
}
@@ -2168,7 +2177,7 @@ BigInt* BigInt::bitOr(ExclusiveContext* cx, HandleBigInt x, HandleBigInt y) {
HandleBigInt& neg = x->isNegative() ? x : y;
// x | (-y) == x | ~(y-1) == ~((y-1) &~ x) == -(((y-1) &~ x) + 1)
- RootedBigInt result(cx, absoluteSubOne(cx, neg, resultLength));
+ RootedBigInt result(cx, absoluteSubOne(cx, neg));
if (!result) {
return nullptr;
}
@@ -2183,7 +2192,7 @@ BigInt* BigInt::bitOr(ExclusiveContext* cx, HandleBigInt x, HandleBigInt y) {
BigInt* BigInt::bitNot(ExclusiveContext* cx, HandleBigInt x) {
if (x->isNegative()) {
// ~(-x) == ~(~(x-1)) == x-1
- return absoluteSubOne(cx, x, x->digitLength());
+ return absoluteSubOne(cx, x);
} else {
// ~x == -x-1 == -(x+1)
bool resultNegative = true;
@@ -2535,6 +2544,30 @@ bool BigInt::neg(ExclusiveContext* cx, HandleValue operand, MutableHandleValue r
return true;
}
+bool BigInt::inc(ExclusiveContext* cx, HandleValue operand, MutableHandleValue res) {
+ MOZ_ASSERT(operand.isBigInt());
+
+ RootedBigInt operandBigInt(cx, operand.toBigInt());
+ BigInt* resBigInt = BigInt::inc(cx, operandBigInt);
+ if (!resBigInt) {
+ return false;
+ }
+ res.setBigInt(resBigInt);
+ return true;
+}
+
+bool BigInt::dec(ExclusiveContext* cx, HandleValue operand, MutableHandleValue res) {
+ MOZ_ASSERT(operand.isBigInt());
+
+ RootedBigInt operandBigInt(cx, operand.toBigInt());
+ BigInt* resBigInt = BigInt::dec(cx, operandBigInt);
+ if (!resBigInt) {
+ return false;
+ }
+ res.setBigInt(resBigInt);
+ return true;
+}
+
bool BigInt::lsh(ExclusiveContext* cx, HandleValue lhs, HandleValue rhs,
MutableHandleValue res) {
if (!ValidBigIntOperands(cx, lhs, rhs)) {
diff --git a/js/src/vm/BigIntType.h b/js/src/vm/BigIntType.h
index ea0317fd9c..693f8bf36c 100644
--- a/js/src/vm/BigIntType.h
+++ b/js/src/vm/BigIntType.h
@@ -97,9 +97,11 @@ class BigInt final : public js::gc::TenuredCell {
static BigInt* createFromDouble(js::ExclusiveContext* cx, double d);
static BigInt* createFromUint64(js::ExclusiveContext* cx, uint64_t n);
static BigInt* createFromInt64(js::ExclusiveContext* cx, int64_t n);
+ static BigInt* createFromDigit(js::ExclusiveContext* cx, Digit d, bool isNegative);
// FIXME: Cache these values.
static BigInt* zero(js::ExclusiveContext* cx);
static BigInt* one(js::ExclusiveContext* cx);
+ static BigInt* negativeOne(js::ExclusiveContext* cx);
static BigInt* copy(js::ExclusiveContext* cx, Handle<BigInt*> x);
static BigInt* add(js::ExclusiveContext* cx, Handle<BigInt*> x, Handle<BigInt*> y);
@@ -109,6 +111,8 @@ class BigInt final : public js::gc::TenuredCell {
static BigInt* mod(js::ExclusiveContext* cx, Handle<BigInt*> x, Handle<BigInt*> y);
static BigInt* pow(js::ExclusiveContext* cx, Handle<BigInt*> x, Handle<BigInt*> y);
static BigInt* neg(js::ExclusiveContext* cx, Handle<BigInt*> x);
+ static BigInt* inc(js::ExclusiveContext* cx, Handle<BigInt*> x);
+ static BigInt* dec(js::ExclusiveContext* cx, Handle<BigInt*> x);
static BigInt* lsh(js::ExclusiveContext* cx, Handle<BigInt*> x, Handle<BigInt*> y);
static BigInt* rsh(js::ExclusiveContext* cx, Handle<BigInt*> x, Handle<BigInt*> y);
static BigInt* bitAnd(js::ExclusiveContext* cx, Handle<BigInt*> x, Handle<BigInt*> y);
@@ -145,6 +149,10 @@ class BigInt final : public js::gc::TenuredCell {
MutableHandle<Value> res);
static bool neg(js::ExclusiveContext* cx, Handle<Value> operand,
MutableHandle<Value> res);
+ static bool inc(js::ExclusiveContext* cx, Handle<Value> operand,
+ MutableHandle<Value> res);
+ static bool dec(js::ExclusiveContext* cx, Handle<Value> operand,
+ MutableHandle<Value> res);
static bool lsh(js::ExclusiveContext* cx, Handle<Value> lhs, Handle<Value> rhs,
MutableHandle<Value> res);
static bool rsh(js::ExclusiveContext* cx, Handle<Value> lhs, Handle<Value> rhs,
@@ -288,7 +296,7 @@ class BigInt final : public js::gc::TenuredCell {
// Return `(|x| - 1) * (resultNegative ? -1 : +1)`, with the precondition that
// |x| != 0.
static BigInt* absoluteSubOne(js::ExclusiveContext* cx, Handle<BigInt*> x,
- unsigned resultLength);
+ bool resultNegative = false);
// Return `a + b`, incrementing `*carry` if the addition overflows.
static inline Digit digitAdd(Digit a, Digit b, Digit* carry) {
diff --git a/js/src/vm/Interpreter-inl.h b/js/src/vm/Interpreter-inl.h
index 93d2672b0f..cbf3113b50 100644
--- a/js/src/vm/Interpreter-inl.h
+++ b/js/src/vm/Interpreter-inl.h
@@ -421,16 +421,19 @@ IncOperation(JSContext* cx,
MutableHandleValue val,
MutableHandleValue res)
{
- MOZ_ASSERT(val.isNumber(), "+1 only callable on result of JSOP_TONUMERIC");
-
int32_t i;
if (val.isInt32() && (i = val.toInt32()) != INT32_MAX) {
res.setInt32(i + 1);
return true;
}
- res.setNumber(val.toNumber() + 1);
- return true;
+ if (val.isNumber()) {
+ res.setNumber(val.toNumber() + 1);
+ return true;
+ }
+
+ MOZ_ASSERT(val.isBigInt(), "+1 only callable on result of JSOP_TONUMERIC");
+ return BigInt::inc(cx, val, res);
}
static MOZ_ALWAYS_INLINE bool
@@ -438,16 +441,19 @@ DecOperation(JSContext* cx,
MutableHandleValue val,
MutableHandleValue res)
{
- MOZ_ASSERT(val.isNumber(), "-1 only callable on result of JSOP_TONUMERIC");
-
int32_t i;
if (val.isInt32() && (i = val.toInt32()) != INT32_MIN) {
res.setInt32(i - 1);
return true;
}
- res.setNumber(val.toNumber() - 1);
- return true;
+ if (val.isNumber()) {
+ res.setNumber(val.toNumber() - 1);
+ return true;
+ }
+
+ MOZ_ASSERT(val.isBigInt(), "-1 only callable on result of JSOP_TONUMERIC");
+ return BigInt::dec(cx, val, res);
}
static MOZ_ALWAYS_INLINE bool