mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-29 20:07:30 +09:00
Issue #2308 & #1240 Follow-up - Add BigInt support to JSOP_INC and JSOP_DEC. https://bugzilla.mozilla.org/show_bug.cgi?id=1526309
This commit is contained in:
parent
5c8a6c5bb8
commit
e12e377e51
8 changed files with 283 additions and 127 deletions
|
|
@ -57,6 +57,10 @@ namespace JS {
|
||||||
_(BinaryArith_SharedCache) \
|
_(BinaryArith_SharedCache) \
|
||||||
_(BinaryArith_Call) \
|
_(BinaryArith_Call) \
|
||||||
\
|
\
|
||||||
|
_(UnaryArith_SpecializedTypes) \
|
||||||
|
_(UnaryArith_SpecializedOnBaselineTypes) \
|
||||||
|
_(UnaryArith_InlineCache) \
|
||||||
|
\
|
||||||
_(InlineCache_OptimizedStub) \
|
_(InlineCache_OptimizedStub) \
|
||||||
\
|
\
|
||||||
_(Call_Inline)
|
_(Call_Inline)
|
||||||
|
|
@ -112,6 +116,7 @@ namespace JS {
|
||||||
_(GetElemStringNotCached) \
|
_(GetElemStringNotCached) \
|
||||||
_(NonNativeReceiver) \
|
_(NonNativeReceiver) \
|
||||||
_(IndexType) \
|
_(IndexType) \
|
||||||
|
_(SpeculationOnInputTypesFailed) \
|
||||||
_(SetElemNonDenseNonTANotCached) \
|
_(SetElemNonDenseNonTANotCached) \
|
||||||
_(NoSimdJitSupport) \
|
_(NoSimdJitSupport) \
|
||||||
_(SimdTypeNotOptimized) \
|
_(SimdTypeNotOptimized) \
|
||||||
|
|
|
||||||
|
|
@ -4858,7 +4858,7 @@ IonBuilder::jsop_bitop(JSOp op)
|
||||||
}
|
}
|
||||||
|
|
||||||
MDefinition::Opcode
|
MDefinition::Opcode
|
||||||
JSOpToMDefinition(JSOp op)
|
BinaryJSOpToMDefinition(JSOp op)
|
||||||
{
|
{
|
||||||
switch (op) {
|
switch (op) {
|
||||||
case JSOP_ADD:
|
case JSOP_ADD:
|
||||||
|
|
@ -4959,6 +4959,40 @@ IonBuilder::powTrySpecialized(bool* emitted, MDefinition* base, MDefinition* pow
|
||||||
return true;
|
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
|
static inline bool
|
||||||
SimpleArithOperand(MDefinition* op)
|
SimpleArithOperand(MDefinition* op)
|
||||||
{
|
{
|
||||||
|
|
@ -4992,19 +5026,20 @@ IonBuilder::binaryArithTrySpecialized(bool* emitted, JSOp op, MDefinition* left,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
MDefinition::Opcode defOp = JSOpToMDefinition(op);
|
MDefinition::Opcode defOp = BinaryJSOpToMDefinition(op);
|
||||||
MBinaryArithInstruction* ins = MBinaryArithInstruction::New(alloc(), defOp, left, right);
|
MIRType specialization = binaryArithNumberSpecialization(left, right);
|
||||||
ins->setNumberSpecialization(alloc(), inspector, pc);
|
MBinaryArithInstruction* ins = binaryArithEmitSpecialized(defOp, specialization, left, right);
|
||||||
|
|
||||||
if (op == JSOP_ADD || op == JSOP_MUL)
|
if(!ins) {
|
||||||
ins->setCommutative();
|
|
||||||
|
|
||||||
current->add(ins);
|
|
||||||
current->push(ins);
|
|
||||||
|
|
||||||
MOZ_ASSERT(!ins->isEffectful());
|
|
||||||
if (!maybeInsertResume())
|
|
||||||
return false;
|
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();
|
trackOptimizationSuccess();
|
||||||
*emitted = true;
|
*emitted = true;
|
||||||
|
|
@ -5028,16 +5063,10 @@ IonBuilder::binaryArithTrySpecializedOnBaselineInspector(bool* emitted, JSOp op,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
MDefinition::Opcode def_op = JSOpToMDefinition(op);
|
MDefinition::Opcode defOp = BinaryJSOpToMDefinition(op);
|
||||||
MBinaryArithInstruction* ins = MBinaryArithInstruction::New(alloc(), def_op, left, right);
|
if(!binaryArithEmitSpecialized(defOp, specialization, left, right)) {
|
||||||
ins->setSpecialization(specialization);
|
|
||||||
|
|
||||||
current->add(ins);
|
|
||||||
current->push(ins);
|
|
||||||
|
|
||||||
MOZ_ASSERT(!ins->isEffectful());
|
|
||||||
if (!maybeInsertResume())
|
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
trackOptimizationSuccess();
|
trackOptimizationSuccess();
|
||||||
*emitted = true;
|
*emitted = true;
|
||||||
|
|
@ -5076,14 +5105,6 @@ IonBuilder::arithTrySharedStub(bool* emitted, JSOp op,
|
||||||
|
|
||||||
stub = MUnarySharedStub::New(alloc(), right);
|
stub = MUnarySharedStub::New(alloc(), right);
|
||||||
break;
|
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_ADD:
|
||||||
case JSOP_SUB:
|
case JSOP_SUB:
|
||||||
case JSOP_MUL:
|
case JSOP_MUL:
|
||||||
|
|
@ -5138,8 +5159,8 @@ IonBuilder::jsop_binary_arith(JSOp op, MDefinition* left, MDefinition* right)
|
||||||
trackOptimizationAttempt(TrackedStrategy::BinaryArith_Call);
|
trackOptimizationAttempt(TrackedStrategy::BinaryArith_Call);
|
||||||
trackOptimizationSuccess();
|
trackOptimizationSuccess();
|
||||||
|
|
||||||
MDefinition::Opcode def_op = JSOpToMDefinition(op);
|
MDefinition::Opcode defOp = BinaryJSOpToMDefinition(op);
|
||||||
MBinaryArithInstruction* ins = MBinaryArithInstruction::New(alloc(), def_op, left, right);
|
MBinaryArithInstruction* ins = MBinaryArithInstruction::New(alloc(), defOp, left, right);
|
||||||
|
|
||||||
// Decrease type from 'any type' to 'empty type' when one of the operands
|
// Decrease type from 'any type' to 'empty type' when one of the operands
|
||||||
// is 'empty typed'.
|
// is 'empty typed'.
|
||||||
|
|
@ -5216,27 +5237,117 @@ IonBuilder::jsop_neg()
|
||||||
return jsop_binary_arith(JSOP_MUL, negator, right);
|
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::unaryArithTrySpecialized(bool* emitted, JSOp op, MDefinition* value)
|
||||||
|
{
|
||||||
|
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
|
bool
|
||||||
IonBuilder::jsop_inc_or_dec(JSOp op)
|
IonBuilder::jsop_inc_or_dec(JSOp op)
|
||||||
{
|
{
|
||||||
// As above, pass constant without slot traffic.
|
bool emitted = false;
|
||||||
MConstant* one = MConstant::New(alloc(), Int32Value(1));
|
|
||||||
current->add(one);
|
|
||||||
|
|
||||||
MDefinition* value = current->pop();
|
MDefinition* value = current->pop();
|
||||||
|
|
||||||
switch (op) {
|
startTrackingOptimizations();
|
||||||
case JSOP_INC:
|
|
||||||
op = JSOP_ADD;
|
trackTypeInfo(TrackedTypeSite::Operand, value->type(), value->resultTypeSet());
|
||||||
break;
|
|
||||||
case JSOP_DEC:
|
if (!unaryArithTrySpecialized(&emitted, op, value)) {
|
||||||
op = JSOP_SUB;
|
return false;
|
||||||
break;
|
}
|
||||||
default:
|
if (emitted) {
|
||||||
MOZ_CRASH("jsop_inc_or_dec with bad op");
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return jsop_binary_arith(op, value, one);
|
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 resumeAfter(stub);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
|
|
||||||
|
|
@ -497,8 +497,13 @@ class IonBuilder
|
||||||
|
|
||||||
// jsop_binary_arith helpers.
|
// jsop_binary_arith helpers.
|
||||||
MBinaryArithInstruction* binaryArithInstruction(JSOp op, MDefinition* left, MDefinition* right);
|
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,
|
MOZ_MUST_USE bool binaryArithTryConcat(bool* emitted, JSOp op, MDefinition* left,
|
||||||
MDefinition* right);
|
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,
|
MOZ_MUST_USE bool binaryArithTrySpecialized(bool* emitted, JSOp op, MDefinition* left,
|
||||||
MDefinition* right);
|
MDefinition* right);
|
||||||
MOZ_MUST_USE bool binaryArithTrySpecializedOnBaselineInspector(bool* emitted, JSOp op,
|
MOZ_MUST_USE bool binaryArithTrySpecializedOnBaselineInspector(bool* emitted, JSOp op,
|
||||||
|
|
@ -510,6 +515,12 @@ class IonBuilder
|
||||||
// jsop_bitnot helpers.
|
// jsop_bitnot helpers.
|
||||||
MOZ_MUST_USE bool bitnotTrySpecialized(bool* emitted, MDefinition* input);
|
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.
|
// jsop_pow helpers.
|
||||||
MOZ_MUST_USE bool powTrySpecialized(bool* emitted, MDefinition* base, MDefinition* power,
|
MOZ_MUST_USE bool powTrySpecialized(bool* emitted, MDefinition* base, MDefinition* power,
|
||||||
MIRType outputType);
|
MIRType outputType);
|
||||||
|
|
|
||||||
|
|
@ -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
|
bool
|
||||||
MBinaryArithInstruction::constantDoubleResult(TempAllocator& alloc)
|
MBinaryArithInstruction::constantDoubleResult(TempAllocator& alloc)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -6219,7 +6219,6 @@ class MBinaryArithInstruction
|
||||||
specialization_ = MIRType::Int32;
|
specialization_ = MIRType::Int32;
|
||||||
setResultType(MIRType::Int32);
|
setResultType(MIRType::Int32);
|
||||||
}
|
}
|
||||||
void setNumberSpecialization(TempAllocator& alloc, BaselineInspector* inspector, jsbytecode* pc);
|
|
||||||
|
|
||||||
virtual void trySpecializeFloat32(TempAllocator& alloc) override;
|
virtual void trySpecializeFloat32(TempAllocator& alloc) override;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -185,16 +185,21 @@ BigInt* BigInt::zero(ExclusiveContext* cx) {
|
||||||
return createUninitialized(cx, 0, false);
|
return createUninitialized(cx, 0, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
BigInt* BigInt::one(ExclusiveContext* cx) {
|
BigInt* BigInt::createFromDigit(ExclusiveContext* cx, Digit d, bool isNegative) {
|
||||||
BigInt* ret = createUninitialized(cx, 1, false);
|
MOZ_ASSERT(d != 0);
|
||||||
|
BigInt* res = createUninitialized(cx, 1, isNegative);
|
||||||
if (!ret) {
|
if (!res) {
|
||||||
return nullptr;
|
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) {
|
BigInt* BigInt::neg(ExclusiveContext* cx, HandleBigInt x) {
|
||||||
|
|
@ -977,21 +982,26 @@ BigInt* BigInt::absoluteAddOne(ExclusiveContext* cx, HandleBigInt x,
|
||||||
return destructivelyTrimHighZeroDigits(cx, result);
|
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,
|
BigInt* BigInt::absoluteSubOne(ExclusiveContext* cx, HandleBigInt x,
|
||||||
unsigned resultLength) {
|
bool resultNegative) {
|
||||||
MOZ_ASSERT(!x->isZero());
|
MOZ_ASSERT(!x->isZero());
|
||||||
MOZ_ASSERT(resultLength >= x->digitLength());
|
|
||||||
bool resultNegative = false;
|
unsigned length = x->digitLength();
|
||||||
RootedBigInt result(cx,
|
|
||||||
createUninitialized(cx, resultLength, resultNegative));
|
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) {
|
if (!result) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned length = x->digitLength();
|
|
||||||
Digit borrow = 1;
|
Digit borrow = 1;
|
||||||
for (unsigned i = 0; i < length; i++) {
|
for (unsigned i = 0; i < length; i++) {
|
||||||
Digit newBorrow = 0;
|
Digit newBorrow = 0;
|
||||||
|
|
@ -999,13 +1009,36 @@ BigInt* BigInt::absoluteSubOne(ExclusiveContext* cx, HandleBigInt x,
|
||||||
borrow = newBorrow;
|
borrow = newBorrow;
|
||||||
}
|
}
|
||||||
MOZ_ASSERT(!borrow);
|
MOZ_ASSERT(!borrow);
|
||||||
for (unsigned i = length; i < resultLength; i++) {
|
|
||||||
result->setDigit(i, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
return destructivelyTrimHighZeroDigits(cx, result);
|
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
|
// 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
|
// base-N string representation of a number. To increase accuracy, the array
|
||||||
// value is the actual value multiplied by 32. To generate this table:
|
// 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;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
BigInt* res = createUninitialized(cx, 1, isNegative);
|
return createFromDigit(cx, n, isNegative);
|
||||||
if (!res) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
res->setDigit(0, n);
|
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BigInt* BigInt::createFromInt64(ExclusiveContext* cx, int64_t n) {
|
BigInt* BigInt::createFromInt64(ExclusiveContext* cx, int64_t n) {
|
||||||
|
|
@ -1770,12 +1797,7 @@ BigInt* BigInt::mod(ExclusiveContext* cx, HandleBigInt x, HandleBigInt y) {
|
||||||
return zero(cx);
|
return zero(cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
BigInt* remainder = createUninitialized(cx, 1, x->isNegative());
|
return createFromDigit(cx, remainderDigit, x->isNegative());
|
||||||
if (!remainder) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
remainder->setDigit(0, remainderDigit);
|
|
||||||
return remainder;
|
|
||||||
} else {
|
} else {
|
||||||
RootedBigInt remainder(cx);
|
RootedBigInt remainder(cx);
|
||||||
if (!absoluteDivWithBigIntDivisor(cx, x, y, Nothing(), Some(&remainder),
|
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) {
|
BigInt* BigInt::rshByMaximum(ExclusiveContext* cx, bool isNegative) {
|
||||||
if (isNegative) {
|
return isNegative ? negativeOne(cx) : zero(cx);
|
||||||
RootedBigInt negativeOne(cx, createUninitialized(cx, 1, isNegative));
|
|
||||||
if (!negativeOne) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
negativeOne->setDigit(0, 1);
|
|
||||||
return negativeOne;
|
|
||||||
}
|
|
||||||
return zero(cx);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BigInt* BigInt::rshByAbsolute(ExclusiveContext* cx, HandleBigInt x, HandleBigInt y) {
|
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()) {
|
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) & (-y) == ~(x-1) & ~(y-1) == ~((x-1) | (y-1))
|
||||||
// == -(((x-1) | (y-1)) + 1)
|
// == -(((x-1) | (y-1)) + 1)
|
||||||
RootedBigInt x1(cx, absoluteSubOne(cx, x, resultLength));
|
RootedBigInt x1(cx, absoluteSubOne(cx, x));
|
||||||
if (!x1) {
|
if (!x1) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
RootedBigInt y1(cx, absoluteSubOne(cx, y, y->digitLength()));
|
RootedBigInt y1(cx, absoluteSubOne(cx, y));
|
||||||
if (!y1) {
|
if (!y1) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
@ -2072,7 +2085,7 @@ BigInt* BigInt::bitAnd(ExclusiveContext* cx, HandleBigInt x, HandleBigInt y) {
|
||||||
HandleBigInt& pos = x->isNegative() ? y : x;
|
HandleBigInt& pos = x->isNegative() ? y : x;
|
||||||
HandleBigInt& neg = x->isNegative() ? x : y;
|
HandleBigInt& neg = x->isNegative() ? x : y;
|
||||||
|
|
||||||
RootedBigInt neg1(cx, absoluteSubOne(cx, neg, neg->digitLength()));
|
RootedBigInt neg1(cx, absoluteSubOne(cx, neg));
|
||||||
if (!neg1) {
|
if (!neg1) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
@ -2096,27 +2109,24 @@ BigInt* BigInt::bitXor(ExclusiveContext* cx, HandleBigInt x, HandleBigInt y) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (x->isNegative() && y->isNegative()) {
|
if (x->isNegative() && y->isNegative()) {
|
||||||
int resultLength = std::max(x->digitLength(), y->digitLength());
|
|
||||||
|
|
||||||
// (-x) ^ (-y) == ~(x-1) ^ ~(y-1) == (x-1) ^ (y-1)
|
// (-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) {
|
if (!x1) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
RootedBigInt y1(cx, absoluteSubOne(cx, y, y->digitLength()));
|
RootedBigInt y1(cx, absoluteSubOne(cx, y));
|
||||||
if (!y1) {
|
if (!y1) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
return absoluteXor(cx, x1, y1);
|
return absoluteXor(cx, x1, y1);
|
||||||
}
|
}
|
||||||
MOZ_ASSERT(x->isNegative() != y->isNegative());
|
MOZ_ASSERT(x->isNegative() != y->isNegative());
|
||||||
int resultLength = std::max(x->digitLength(), y->digitLength()) + 1;
|
|
||||||
|
|
||||||
HandleBigInt& pos = x->isNegative() ? y : x;
|
HandleBigInt& pos = x->isNegative() ? y : x;
|
||||||
HandleBigInt& neg = x->isNegative() ? x : y;
|
HandleBigInt& neg = x->isNegative() ? x : y;
|
||||||
|
|
||||||
// x ^ (-y) == x ^ ~(y-1) == ~(x ^ (y-1)) == -((x ^ (y-1)) + 1)
|
// 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) {
|
if (!result) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
@ -2138,7 +2148,6 @@ BigInt* BigInt::bitOr(ExclusiveContext* cx, HandleBigInt x, HandleBigInt y) {
|
||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned resultLength = std::max(x->digitLength(), y->digitLength());
|
|
||||||
bool resultNegative = x->isNegative() || y->isNegative();
|
bool resultNegative = x->isNegative() || y->isNegative();
|
||||||
|
|
||||||
if (!resultNegative) {
|
if (!resultNegative) {
|
||||||
|
|
@ -2148,11 +2157,11 @@ BigInt* BigInt::bitOr(ExclusiveContext* cx, HandleBigInt x, HandleBigInt y) {
|
||||||
if (x->isNegative() && y->isNegative()) {
|
if (x->isNegative() && y->isNegative()) {
|
||||||
// (-x) | (-y) == ~(x-1) | ~(y-1) == ~((x-1) & (y-1))
|
// (-x) | (-y) == ~(x-1) | ~(y-1) == ~((x-1) & (y-1))
|
||||||
// == -(((x-1) & (y-1)) + 1)
|
// == -(((x-1) & (y-1)) + 1)
|
||||||
RootedBigInt result(cx, absoluteSubOne(cx, x, resultLength));
|
RootedBigInt result(cx, absoluteSubOne(cx, x));
|
||||||
if (!result) {
|
if (!result) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
RootedBigInt y1(cx, absoluteSubOne(cx, y, y->digitLength()));
|
RootedBigInt y1(cx, absoluteSubOne(cx, y));
|
||||||
if (!y1) {
|
if (!y1) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
@ -2168,7 +2177,7 @@ BigInt* BigInt::bitOr(ExclusiveContext* cx, HandleBigInt x, HandleBigInt y) {
|
||||||
HandleBigInt& neg = x->isNegative() ? x : y;
|
HandleBigInt& neg = x->isNegative() ? x : y;
|
||||||
|
|
||||||
// x | (-y) == x | ~(y-1) == ~((y-1) &~ x) == -(((y-1) &~ x) + 1)
|
// 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) {
|
if (!result) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
@ -2183,7 +2192,7 @@ BigInt* BigInt::bitOr(ExclusiveContext* cx, HandleBigInt x, HandleBigInt y) {
|
||||||
BigInt* BigInt::bitNot(ExclusiveContext* cx, HandleBigInt x) {
|
BigInt* BigInt::bitNot(ExclusiveContext* cx, HandleBigInt x) {
|
||||||
if (x->isNegative()) {
|
if (x->isNegative()) {
|
||||||
// ~(-x) == ~(~(x-1)) == x-1
|
// ~(-x) == ~(~(x-1)) == x-1
|
||||||
return absoluteSubOne(cx, x, x->digitLength());
|
return absoluteSubOne(cx, x);
|
||||||
} else {
|
} else {
|
||||||
// ~x == -x-1 == -(x+1)
|
// ~x == -x-1 == -(x+1)
|
||||||
bool resultNegative = true;
|
bool resultNegative = true;
|
||||||
|
|
@ -2535,6 +2544,30 @@ bool BigInt::neg(ExclusiveContext* cx, HandleValue operand, MutableHandleValue r
|
||||||
return true;
|
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,
|
bool BigInt::lsh(ExclusiveContext* cx, HandleValue lhs, HandleValue rhs,
|
||||||
MutableHandleValue res) {
|
MutableHandleValue res) {
|
||||||
if (!ValidBigIntOperands(cx, lhs, rhs)) {
|
if (!ValidBigIntOperands(cx, lhs, rhs)) {
|
||||||
|
|
|
||||||
|
|
@ -97,9 +97,11 @@ class BigInt final : public js::gc::TenuredCell {
|
||||||
static BigInt* createFromDouble(js::ExclusiveContext* cx, double d);
|
static BigInt* createFromDouble(js::ExclusiveContext* cx, double d);
|
||||||
static BigInt* createFromUint64(js::ExclusiveContext* cx, uint64_t n);
|
static BigInt* createFromUint64(js::ExclusiveContext* cx, uint64_t n);
|
||||||
static BigInt* createFromInt64(js::ExclusiveContext* cx, int64_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.
|
// FIXME: Cache these values.
|
||||||
static BigInt* zero(js::ExclusiveContext* cx);
|
static BigInt* zero(js::ExclusiveContext* cx);
|
||||||
static BigInt* one(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* copy(js::ExclusiveContext* cx, Handle<BigInt*> x);
|
||||||
static BigInt* add(js::ExclusiveContext* cx, Handle<BigInt*> x, Handle<BigInt*> y);
|
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* mod(js::ExclusiveContext* cx, Handle<BigInt*> x, Handle<BigInt*> y);
|
||||||
static BigInt* pow(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* 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* lsh(js::ExclusiveContext* cx, Handle<BigInt*> x, Handle<BigInt*> y);
|
||||||
static BigInt* rsh(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);
|
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);
|
MutableHandle<Value> res);
|
||||||
static bool neg(js::ExclusiveContext* cx, Handle<Value> operand,
|
static bool neg(js::ExclusiveContext* cx, Handle<Value> operand,
|
||||||
MutableHandle<Value> res);
|
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,
|
static bool lsh(js::ExclusiveContext* cx, Handle<Value> lhs, Handle<Value> rhs,
|
||||||
MutableHandle<Value> res);
|
MutableHandle<Value> res);
|
||||||
static bool rsh(js::ExclusiveContext* cx, Handle<Value> lhs, Handle<Value> rhs,
|
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
|
// Return `(|x| - 1) * (resultNegative ? -1 : +1)`, with the precondition that
|
||||||
// |x| != 0.
|
// |x| != 0.
|
||||||
static BigInt* absoluteSubOne(js::ExclusiveContext* cx, Handle<BigInt*> x,
|
static BigInt* absoluteSubOne(js::ExclusiveContext* cx, Handle<BigInt*> x,
|
||||||
unsigned resultLength);
|
bool resultNegative = false);
|
||||||
|
|
||||||
// Return `a + b`, incrementing `*carry` if the addition overflows.
|
// Return `a + b`, incrementing `*carry` if the addition overflows.
|
||||||
static inline Digit digitAdd(Digit a, Digit b, Digit* carry) {
|
static inline Digit digitAdd(Digit a, Digit b, Digit* carry) {
|
||||||
|
|
|
||||||
|
|
@ -421,16 +421,19 @@ IncOperation(JSContext* cx,
|
||||||
MutableHandleValue val,
|
MutableHandleValue val,
|
||||||
MutableHandleValue res)
|
MutableHandleValue res)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(val.isNumber(), "+1 only callable on result of JSOP_TONUMERIC");
|
|
||||||
|
|
||||||
int32_t i;
|
int32_t i;
|
||||||
if (val.isInt32() && (i = val.toInt32()) != INT32_MAX) {
|
if (val.isInt32() && (i = val.toInt32()) != INT32_MAX) {
|
||||||
res.setInt32(i + 1);
|
res.setInt32(i + 1);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
res.setNumber(val.toNumber() + 1);
|
if (val.isNumber()) {
|
||||||
return true;
|
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
|
static MOZ_ALWAYS_INLINE bool
|
||||||
|
|
@ -438,16 +441,19 @@ DecOperation(JSContext* cx,
|
||||||
MutableHandleValue val,
|
MutableHandleValue val,
|
||||||
MutableHandleValue res)
|
MutableHandleValue res)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(val.isNumber(), "-1 only callable on result of JSOP_TONUMERIC");
|
|
||||||
|
|
||||||
int32_t i;
|
int32_t i;
|
||||||
if (val.isInt32() && (i = val.toInt32()) != INT32_MIN) {
|
if (val.isInt32() && (i = val.toInt32()) != INT32_MIN) {
|
||||||
res.setInt32(i - 1);
|
res.setInt32(i - 1);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
res.setNumber(val.toNumber() - 1);
|
if (val.isNumber()) {
|
||||||
return true;
|
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
|
static MOZ_ALWAYS_INLINE bool
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue