Merge remote-tracking branch 'origin/tracking' into custom

This commit is contained in:
roytam1 2023-09-20 09:31:36 +08:00
commit 30236b0f05
40 changed files with 722 additions and 213 deletions

View file

@ -664,7 +664,8 @@ public:
NS_IMETHOD Run() override NS_IMETHOD Run() override
{ {
MOZ_ASSERT(mPerformance); MOZ_ASSERT(mPerformance);
mPerformance->NotifyObservers(); RefPtr<Performance> performance(mPerformance);
performance->NotifyObservers();
return NS_OK; return NS_OK;
} }
@ -688,7 +689,12 @@ Performance::RunNotificationObserversTask()
{ {
mPendingNotificationObserversTask = true; mPendingNotificationObserversTask = true;
nsCOMPtr<nsIRunnable> task = new NotifyObserversTask(this); nsCOMPtr<nsIRunnable> task = new NotifyObserversTask(this);
nsresult rv = NS_DispatchToCurrentThread(task); nsresult rv;
if (NS_IsMainThread()) {
rv = NS_DispatchToCurrentThread(task);
} else {
rv = NS_DispatchToMainThread(task);
}
if (NS_WARN_IF(NS_FAILED(rv))) { if (NS_WARN_IF(NS_FAILED(rv))) {
mPendingNotificationObserversTask = false; mPendingNotificationObserversTask = false;
} }

View file

@ -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) \

View file

@ -2057,7 +2057,7 @@ BytecodeEmitter::emitCallIncDec(UnaryNode* incDec)
MOZ_ASSERT(call->isKind(PNK_CALL)); MOZ_ASSERT(call->isKind(PNK_CALL));
if (!emitTree(call)) // CALLRESULT if (!emitTree(call)) // CALLRESULT
return false; return false;
if (!emit1(JSOP_POS)) // N if (!emit1(JSOP_TONUMERIC)) // N
return false; return false;
// The increment/decrement has no side effects, so proceed to throw for // The increment/decrement has no side effects, so proceed to throw for

View file

@ -233,8 +233,8 @@ ElemOpEmitter::emitIncDec()
MOZ_ASSERT(state_ == State::Get); MOZ_ASSERT(state_ == State::Get);
JSOp binOp = isInc() ? JSOP_ADD : JSOP_SUB; JSOp incOp = isInc() ? JSOP_INC : JSOP_DEC;
if (!bce_->emit1(JSOP_POS)) { // ... N if (!bce_->emit1(JSOP_TONUMERIC)) { // ... N
return false; return false;
} }
if (isPostIncDec()) { if (isPostIncDec()) {
@ -242,10 +242,7 @@ ElemOpEmitter::emitIncDec()
return false; return false;
} }
} }
if (!bce_->emit1(JSOP_ONE)) { // ... N? N 1 if (!bce_->emit1(incOp)) { // ... N? N+1
return false;
}
if (!bce_->emit1(binOp)) { // ... N? N+1
return false; return false;
} }
if (isPostIncDec()) { if (isPostIncDec()) {

View file

@ -339,11 +339,11 @@ NameOpEmitter::emitIncDec()
{ {
MOZ_ASSERT(state_ == State::Start); MOZ_ASSERT(state_ == State::Start);
JSOp binOp = isInc() ? JSOP_ADD : JSOP_SUB; JSOp incOp = isInc() ? JSOP_INC : JSOP_DEC;
if (!prepareForRhs()) { // ENV? V if (!prepareForRhs()) { // ENV? V
return false; return false;
} }
if (!bce_->emit1(JSOP_POS)) { // ENV? N if (!bce_->emit1(JSOP_TONUMERIC)) { // ENV? N
return false; return false;
} }
if (isPostIncDec()) { if (isPostIncDec()) {
@ -351,10 +351,7 @@ NameOpEmitter::emitIncDec()
return false; return false;
} }
} }
if (!bce_->emit1(JSOP_ONE)) { // ENV? N? N 1 if (!bce_->emit1(incOp)) { // ENV? N? N+1
return false;
}
if (!bce_->emit1(binOp)) { // ENV? N? N+1
return false; return false;
} }
if (isPostIncDec() && emittedBindOp()) { if (isPostIncDec() && emittedBindOp()) {

View file

@ -217,9 +217,9 @@ PropOpEmitter::emitIncDec(JSAtom* prop)
MOZ_ASSERT(state_ == State::Get); MOZ_ASSERT(state_ == State::Get);
JSOp binOp = isInc() ? JSOP_ADD : JSOP_SUB; JSOp incOp = isInc() ? JSOP_INC : JSOP_DEC;
if (!bce_->emit1(JSOP_POS)) { // ... N if (!bce_->emit1(JSOP_TONUMERIC)) { // ... N
return false; return false;
} }
if (isPostIncDec()) { if (isPostIncDec()) {
@ -227,10 +227,7 @@ PropOpEmitter::emitIncDec(JSAtom* prop)
return false; return false;
} }
} }
if (!bce_->emit1(JSOP_ONE)) { // ... N? N 1 if (!bce_->emit1(incOp)) { // ... N? N+1
return false;
}
if (!bce_->emit1(binOp)) { // ... N? N+1
return false; return false;
} }
if (isPostIncDec()) { if (isPostIncDec()) {

View file

@ -1322,14 +1322,37 @@ BaselineCompiler::emit_JSOP_POS()
// Keep top stack value in R0. // Keep top stack value in R0.
frame.popRegsAndSync(1); frame.popRegsAndSync(1);
// Inline path for int32 and double. // Inline path for int32 and double; otherwise call VM.
Label done; Label done;
masm.branchTestNumber(Assembler::Equal, R0, &done); masm.branchTestNumber(Assembler::Equal, R0, &done);
// Call IC. prepareVMCall();
ICToNumber_Fallback::Compiler stubCompiler(cx); pushArg(R0);
if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) if (!callVM(ToNumberInfo)) {
return false; return false;
}
masm.bind(&done);
frame.push(R0);
return true;
}
bool
BaselineCompiler::emit_JSOP_TONUMERIC()
{
// Keep top stack value in R0.
frame.popRegsAndSync(1);
// Inline path for int32 and double; otherwise call VM.
Label done;
masm.branchTestNumber(Assembler::Equal, R0, &done);
prepareVMCall();
pushArg(R0);
if (!callVM(ToNumericInfo)) {
return false;
}
masm.bind(&done); masm.bind(&done);
frame.push(R0); frame.push(R0);
@ -1939,6 +1962,18 @@ BaselineCompiler::emit_JSOP_NEG()
return emitUnaryArith(); return emitUnaryArith();
} }
bool
BaselineCompiler::emit_JSOP_INC()
{
return emitUnaryArith();
}
bool
BaselineCompiler::emit_JSOP_DEC()
{
return emitUnaryArith();
}
bool bool
BaselineCompiler::emit_JSOP_LT() BaselineCompiler::emit_JSOP_LT()
{ {

View file

@ -50,6 +50,7 @@ namespace jit {
_(JSOP_OR) \ _(JSOP_OR) \
_(JSOP_NOT) \ _(JSOP_NOT) \
_(JSOP_POS) \ _(JSOP_POS) \
_(JSOP_TONUMERIC) \
_(JSOP_LOOPHEAD) \ _(JSOP_LOOPHEAD) \
_(JSOP_LOOPENTRY) \ _(JSOP_LOOPENTRY) \
_(JSOP_VOID) \ _(JSOP_VOID) \
@ -243,7 +244,9 @@ namespace jit {
_(JSOP_IS_CONSTRUCTING) \ _(JSOP_IS_CONSTRUCTING) \
_(JSOP_TRY_DESTRUCTURING_ITERCLOSE) \ _(JSOP_TRY_DESTRUCTURING_ITERCLOSE) \
_(JSOP_IMPORTMETA) \ _(JSOP_IMPORTMETA) \
_(JSOP_DYNAMIC_IMPORT) _(JSOP_DYNAMIC_IMPORT) \
_(JSOP_INC) \
_(JSOP_DEC)
class BaselineCompiler : public BaselineCompilerSpecific class BaselineCompiler : public BaselineCompilerSpecific
{ {
@ -327,7 +330,7 @@ class BaselineCompiler : public BaselineCompilerSpecific
OPCODE_LIST(EMIT_OP) OPCODE_LIST(EMIT_OP)
#undef EMIT_OP #undef EMIT_OP
// JSOP_NEG, JSOP_BITNOT // JSOP_NEG, JSOP_BITNOT, JSOP_INC, JSOP_DEC
MOZ_MUST_USE bool emitUnaryArith(); MOZ_MUST_USE bool emitUnaryArith();
// JSOP_BITXOR, JSOP_LSH, JSOP_ADD etc. // JSOP_BITXOR, JSOP_LSH, JSOP_ADD etc.

View file

@ -694,42 +694,6 @@ ICToBool_Object::Compiler::generateStubCode(MacroAssembler& masm)
return true; return true;
} }
//
// ToNumber_Fallback
//
static bool
DoToNumberFallback(JSContext* cx, ICToNumber_Fallback* stub, HandleValue arg, MutableHandleValue ret)
{
FallbackICSpew(cx, stub, "ToNumber");
ret.set(arg);
return ToNumber(cx, ret);
}
typedef bool (*DoToNumberFallbackFn)(JSContext*, ICToNumber_Fallback*, HandleValue, MutableHandleValue);
static const VMFunction DoToNumberFallbackInfo =
FunctionInfo<DoToNumberFallbackFn>(DoToNumberFallback, "DoToNumberFallback", TailCall,
PopValues(1));
bool
ICToNumber_Fallback::Compiler::generateStubCode(MacroAssembler& masm)
{
MOZ_ASSERT(engine_ == Engine::Baseline);
MOZ_ASSERT(R0 == JSReturnOperand);
// Restore the tail call register.
EmitRestoreTailCallReg(masm);
// Ensure stack is fully synced for the expression decompiler.
masm.pushValue(R0);
// Push arguments.
masm.pushValue(R0);
masm.push(ICStubReg);
return tailCallVM(DoToNumberFallbackInfo, masm);
}
// //
// GetElem_Fallback // GetElem_Fallback
// //

View file

@ -333,32 +333,6 @@ class ICToBool_Object : public ICStub
}; };
}; };
// ToNumber
// JSOP_POS
class ICToNumber_Fallback : public ICFallbackStub
{
friend class ICStubSpace;
explicit ICToNumber_Fallback(JitCode* stubCode)
: ICFallbackStub(ICStub::ToNumber_Fallback, stubCode) {}
public:
// Compiler for this stub kind.
class Compiler : public ICStubCompiler {
protected:
MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm);
public:
explicit Compiler(JSContext* cx)
: ICStubCompiler(cx, ICStub::ToNumber_Fallback, Engine::Baseline) {}
ICStub* getStub(ICStubSpace* space) {
return newStub<ICToNumber_Fallback>(space, getStubCode());
}
};
};
// GetElem // GetElem
// JSOP_GETELEM // JSOP_GETELEM

View file

@ -34,8 +34,6 @@ namespace jit {
_(ToBool_Double) \ _(ToBool_Double) \
_(ToBool_Object) \ _(ToBool_Object) \
\ \
_(ToNumber_Fallback) \
\
_(Call_Fallback) \ _(Call_Fallback) \
_(Call_Scripted) \ _(Call_Scripted) \
_(Call_AnyScripted) \ _(Call_AnyScripted) \

View file

@ -408,6 +408,11 @@ BaselineInspector::expectedBinaryArithSpecialization(jsbytecode* pc)
MIRType result; MIRType result;
ICStub* stubs[2]; ICStub* stubs[2];
if (JSOp(*pc) == JSOP_POS) {
// +x expanding to x*1, but no corresponding IC.
return MIRType::None;
}
const ICEntry& entry = icEntryFromPC(pc); const ICEntry& entry = icEntryFromPC(pc);
ICStub* stub = entry.fallbackStub(); ICStub* stub = entry.fallbackStub();
if (stub->isBinaryArith_Fallback() && if (stub->isBinaryArith_Fallback() &&

View file

@ -2378,6 +2378,8 @@ CodeGenerator::visitUnarySharedStub(LUnarySharedStub* lir)
switch (jsop) { switch (jsop) {
case JSOP_BITNOT: case JSOP_BITNOT:
case JSOP_NEG: case JSOP_NEG:
case JSOP_INC:
case JSOP_DEC:
emitSharedStub(ICStub::Kind::UnaryArith_Fallback, lir); emitSharedStub(ICStub::Kind::UnaryArith_Fallback, lir);
break; break;
case JSOP_CALLPROP: case JSOP_CALLPROP:
@ -3413,6 +3415,48 @@ CodeGenerator::visitLoadUnboxedExpando(LLoadUnboxedExpando* lir)
masm.loadPtr(Address(obj, UnboxedPlainObject::offsetOfExpando()), result); masm.loadPtr(Address(obj, UnboxedPlainObject::offsetOfExpando()), result);
} }
void
CodeGenerator::visitToNumeric(LToNumeric* lir)
{
ValueOperand operand = ToValue(lir, LToNumeric::Input);
ValueOperand output = ToOutValue(lir);
bool maybeInt32 = lir->mir()->mightBeType(MIRType::Int32);
bool maybeDouble = lir->mir()->mightBeType(MIRType::Double);
bool maybeNumber = maybeInt32 || maybeDouble;
bool maybeBigInt = lir->mir()->mightBeType(MIRType::BigInt);
int checks = int(maybeNumber) + int(maybeBigInt);
OutOfLineCode* ool = oolCallVM(ToNumericInfo, lir, ArgList(operand), StoreValueTo(output));
if (checks == 0) {
masm.jump(ool->entry());
} else {
Label done;
using Condition = Assembler::Condition;
constexpr Condition Equal = Assembler::Equal;
constexpr Condition NotEqual = Assembler::NotEqual;
if (maybeNumber) {
checks--;
Condition cond = checks ? Equal : NotEqual;
Label* target = checks ? &done : ool->entry();
masm.branchTestNumber(cond, operand, target);
}
if (maybeBigInt) {
checks--;
Condition cond = checks ? Equal : NotEqual;
Label* target = checks ? &done : ool->entry();
masm.branchTestBigInt(cond, operand, target);
}
MOZ_ASSERT(checks == 0);
masm.bind(&done);
masm.moveValue(operand, output);
}
masm.bind(ool->rejoin());
}
void void
CodeGenerator::visitTypeBarrierV(LTypeBarrierV* lir) CodeGenerator::visitTypeBarrierV(LTypeBarrierV* lir)
{ {

View file

@ -149,6 +149,7 @@ class CodeGenerator final : public CodeGeneratorSpecific
void visitGuardReceiverPolymorphic(LGuardReceiverPolymorphic* lir); void visitGuardReceiverPolymorphic(LGuardReceiverPolymorphic* lir);
void visitGuardUnboxedExpando(LGuardUnboxedExpando* lir); void visitGuardUnboxedExpando(LGuardUnboxedExpando* lir);
void visitLoadUnboxedExpando(LLoadUnboxedExpando* lir); void visitLoadUnboxedExpando(LLoadUnboxedExpando* lir);
void visitToNumeric(LToNumeric* lir);
void visitTypeBarrierV(LTypeBarrierV* lir); void visitTypeBarrierV(LTypeBarrierV* lir);
void visitTypeBarrierO(LTypeBarrierO* lir); void visitTypeBarrierO(LTypeBarrierO* lir);
void visitMonitorTypes(LMonitorTypes* lir); void visitMonitorTypes(LMonitorTypes* lir);

View file

@ -636,7 +636,7 @@ IonBuilder::analyzeNewLoopTypes(MBasicBlock* entry, jsbytecode* start, jsbytecod
MPhi* phi = entry->getSlot(slot)->toPhi(); MPhi* phi = entry->getSlot(slot)->toPhi();
if (*last == JSOP_POS) if (*last == JSOP_POS || *last == JSOP_TONUMERIC)
last = earlier; last = earlier;
if (CodeSpec[*last].format & JOF_TYPESET) { if (CodeSpec[*last].format & JOF_TYPESET) {
@ -719,6 +719,8 @@ IonBuilder::analyzeNewLoopTypes(MBasicBlock* entry, jsbytecode* start, jsbytecod
case JSOP_DIV: case JSOP_DIV:
case JSOP_MOD: case JSOP_MOD:
case JSOP_NEG: case JSOP_NEG:
case JSOP_INC:
case JSOP_DEC:
type = inspector->expectedResultType(last); type = inspector->expectedResultType(last);
break; break;
case JSOP_BIGINT: case JSOP_BIGINT:
@ -1584,6 +1586,7 @@ IonBuilder::traverseBytecode()
break; break;
case JSOP_POS: case JSOP_POS:
case JSOP_TONUMERIC:
case JSOP_TOID: case JSOP_TOID:
case JSOP_TOSTRING: case JSOP_TOSTRING:
// These ops may leave their input on the stack without setting // These ops may leave their input on the stack without setting
@ -1737,9 +1740,16 @@ IonBuilder::inspectOpcode(JSOp op)
case JSOP_POS: case JSOP_POS:
return jsop_pos(); return jsop_pos();
case JSOP_TONUMERIC:
return jsop_tonumeric();
case JSOP_NEG: case JSOP_NEG:
return jsop_neg(); return jsop_neg();
case JSOP_INC:
case JSOP_DEC:
return jsop_inc_or_dec(op);
case JSOP_TOSTRING: case JSOP_TOSTRING:
return jsop_tostring(); return jsop_tostring();
@ -4852,7 +4862,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:
@ -4953,6 +4963,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)
{ {
@ -4986,19 +5030,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;
@ -5022,16 +5067,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;
@ -5124,8 +5163,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'.
@ -5202,6 +5241,157 @@ 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
IonBuilder::jsop_tonumeric()
{
MDefinition* peeked = current->peek(-1);
if (IsNumericType(peeked->type())) {
// Elide the ToNumeric as we already unboxed the value.
peeked->setImplicitlyUsedUnchecked();
return true;
}
LifoAlloc* lifoAlloc = alloc().lifoAlloc();
TemporaryTypeSet* types = lifoAlloc->new_<TemporaryTypeSet>();
if (!types) {
return false;
}
types->addType(TypeSet::Int32Type(), lifoAlloc);
types->addType(TypeSet::DoubleType(), lifoAlloc);
types->addType(TypeSet::BigIntType(), lifoAlloc);
if (peeked->type() == MIRType::Value && peeked->resultTypeSet() &&
peeked->resultTypeSet()->isSubset(types)) {
// Elide the ToNumeric because the arg is already a boxed numeric.
peeked->setImplicitlyUsedUnchecked();
return true;
}
// Otherwise, pop the value and add an MToNumeric.
MDefinition* popped = current->pop();
MToNumeric* ins = MToNumeric::New(alloc(), popped, types);
current->add(ins);
current->push(ins);
// toValue() is effectful, so add a resume point.
return resumeAfter(ins);
}
bool
IonBuilder::jsop_inc_or_dec(JSOp op)
{
bool emitted = false;
MDefinition* value = current->pop();
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 resumeAfter(stub);
}
bool bool
IonBuilder::jsop_tostring() IonBuilder::jsop_tostring()
{ {

View file

@ -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);
@ -692,6 +703,8 @@ class IonBuilder
MOZ_MUST_USE bool jsop_pow(); MOZ_MUST_USE bool jsop_pow();
MOZ_MUST_USE bool jsop_pos(); MOZ_MUST_USE bool jsop_pos();
MOZ_MUST_USE bool jsop_neg(); MOZ_MUST_USE bool jsop_neg();
MOZ_MUST_USE bool jsop_tonumeric();
MOZ_MUST_USE bool jsop_inc_or_dec(JSOp op);
MOZ_MUST_USE bool jsop_tostring(); MOZ_MUST_USE bool jsop_tostring();
MOZ_MUST_USE bool jsop_setarg(uint32_t arg); MOZ_MUST_USE bool jsop_setarg(uint32_t arg);
MOZ_MUST_USE bool jsop_defvar(uint32_t index); MOZ_MUST_USE bool jsop_defvar(uint32_t index);

View file

@ -644,6 +644,10 @@ IsNumberType(MIRType type)
type == MIRType::Int64; type == MIRType::Int64;
} }
static inline bool IsNumericType(MIRType type) {
return IsNumberType(type) || type == MIRType::BigInt;
}
static inline bool static inline bool
IsTypeRepresentableAsDouble(MIRType type) IsTypeRepresentableAsDouble(MIRType type)
{ {

View file

@ -2165,6 +2165,15 @@ LIRGenerator::visitToInt32(MToInt32* convert)
} }
} }
void
LIRGenerator::visitToNumeric(MToNumeric* ins)
{
MOZ_ASSERT(ins->input()->type() == MIRType::Value);
LToNumeric* lir = new (alloc()) LToNumeric(useBoxAtStart(ins->input()));
defineBox(lir, ins);
assignSafepoint(lir, ins);
}
void void
LIRGenerator::visitTruncateToInt32(MTruncateToInt32* truncate) LIRGenerator::visitTruncateToInt32(MTruncateToInt32* truncate)
{ {

View file

@ -336,6 +336,7 @@ class LIRGenerator : public LIRGeneratorSpecific
void visitDebugCheckSelfHosted(MDebugCheckSelfHosted* ins); void visitDebugCheckSelfHosted(MDebugCheckSelfHosted* ins);
void visitModuleMetadata(MModuleMetadata* ins); void visitModuleMetadata(MModuleMetadata* ins);
void visitDynamicImport(MDynamicImport* ins); void visitDynamicImport(MDynamicImport* ins);
void visitToNumeric(MToNumeric* ins);
}; };
} // namespace jit } // namespace jit

View file

@ -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)
{ {
@ -4189,6 +4172,23 @@ MResumePoint::isRecoverableOperand(MUse* u) const
return block()->info().isRecoverableOperand(indexOf(u)); return block()->info().isRecoverableOperand(indexOf(u));
} }
MDefinition*
MToNumeric::foldsTo(TempAllocator& alloc)
{
MDefinition* input = getOperand(0);
if (input->isBox()) {
MDefinition* unboxed = input->getOperand(0);
if (IsNumericType(unboxed->type())) {
// If the argument is an MBox and we can see that it boxes a numeric
// value, ToNumeric can be elided.
return input;
}
}
return this;
}
MDefinition* MDefinition*
MToInt32::foldsTo(TempAllocator& alloc) MToInt32::foldsTo(TempAllocator& alloc)
{ {

View file

@ -5534,6 +5534,43 @@ class MInt64ToFloatingPoint
} }
}; };
// Takes a boxed Value and returns a Value containing either a Number or a
// BigInt. Usually this will be the value itself, but it may be an object that
// has a @@toPrimitive, valueOf, or toString method.
class MToNumeric : public MUnaryInstruction, public BoxInputsPolicy::Data
{
MToNumeric(MDefinition* arg, TemporaryTypeSet* types)
: MUnaryInstruction(arg)
{
MOZ_ASSERT(!IsNumericType(arg->type()),
"Unboxable definitions don't need ToNumeric");
setResultType(MIRType::Value);
// Although `types' is always Int32|Double|BigInt, we have to compute it in
// IonBuilder to know whether emitting an MToNumeric is needed, so we just
// pass it through as an argument instead of recomputing it here.
setResultTypeSet(types);
setGuard();
setMovable();
}
public:
INSTRUCTION_HEADER(ToNumeric)
TRIVIAL_NEW_WRAPPERS
static MToNumeric* New(TempAllocator& alloc, MDefinition* arg,
TemporaryTypeSet* types) {
return new (alloc) MToNumeric(arg, types);
}
void computeRange(TempAllocator& alloc) override;
bool congruentTo(const MDefinition* ins) const override {
return congruentIfOperandsEqual(ins);
}
MDefinition* foldsTo(TempAllocator& alloc) override;
ALLOW_CLONE(MToNumeric)
};
// Converts a primitive (either typed or untyped) to an int32. If the input is // Converts a primitive (either typed or untyped) to an int32. If the input is
// not primitive at runtime, a bailout occurs. If the input cannot be converted // not primitive at runtime, a bailout occurs. If the input cannot be converted
// to an int32 without loss (i.e. "5.5" or undefined) then a bailout occurs. // to an int32 without loss (i.e. "5.5" or undefined) then a bailout occurs.
@ -6219,7 +6256,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;

View file

@ -123,6 +123,7 @@ namespace jit {
_(ToDouble) \ _(ToDouble) \
_(ToFloat32) \ _(ToFloat32) \
_(ToInt32) \ _(ToInt32) \
_(ToNumeric) \
_(TruncateToInt32) \ _(TruncateToInt32) \
_(WrapInt64ToInt32) \ _(WrapInt64ToInt32) \
_(ExtendInt32ToInt64) \ _(ExtendInt32ToInt64) \

View file

@ -1741,6 +1741,12 @@ MTruncateToInt32::computeRange(TempAllocator& alloc)
setRange(output); setRange(output);
} }
void
MToNumeric::computeRange(TempAllocator& alloc)
{
setRange(new (alloc) Range(getOperand(0)));
}
void void
MToInt32::computeRange(TempAllocator& alloc) MToInt32::computeRange(TempAllocator& alloc)
{ {

View file

@ -1469,21 +1469,33 @@ DoUnaryArithFallback(JSContext* cx, void* payload, ICUnaryArith_Fallback* stub_,
JSOp op = JSOp(*pc); JSOp op = JSOp(*pc);
FallbackICSpew(cx, stub, "UnaryArith(%s)", CodeName[op]); FallbackICSpew(cx, stub, "UnaryArith(%s)", CodeName[op]);
// The unary operations take a copied val because the original value is needed
// below.
RootedValue valCopy(cx, val);
switch (op) { switch (op) {
case JSOP_BITNOT: { case JSOP_BITNOT: {
RootedValue valCopy(cx, val);
if (!BitNot(cx, &valCopy, res)) { if (!BitNot(cx, &valCopy, res)) {
return false; return false;
} }
break; break;
} }
case JSOP_NEG: { case JSOP_NEG: {
// We copy val here because the original value is needed below.
RootedValue valCopy(cx, val);
if (!NegOperation(cx, script, pc, &valCopy, res)) if (!NegOperation(cx, script, pc, &valCopy, res))
return false; return false;
break; break;
} }
case JSOP_INC: {
if (!IncOperation(cx, &valCopy, res)) {
return false;
}
break;
}
case JSOP_DEC: {
if (!DecOperation(cx, &valCopy, res)) {
return false;
}
break;
}
default: default:
MOZ_CRASH("Unexpected op"); MOZ_CRASH("Unexpected op");
} }
@ -1558,12 +1570,8 @@ ICUnaryArith_Double::Compiler::generateStubCode(MacroAssembler& masm)
Label failure; Label failure;
masm.ensureDouble(R0, FloatReg0, &failure); masm.ensureDouble(R0, FloatReg0, &failure);
MOZ_ASSERT(op == JSOP_NEG || op == JSOP_BITNOT); switch (op) {
case JSOP_BITNOT: {
if (op == JSOP_NEG) {
masm.negateDouble(FloatReg0);
masm.boxDouble(FloatReg0, R0);
} else {
// Truncate the double to an int32. // Truncate the double to an int32.
Register scratchReg = R1.scratchReg(); Register scratchReg = R1.scratchReg();
@ -1581,6 +1589,24 @@ ICUnaryArith_Double::Compiler::generateStubCode(MacroAssembler& masm)
masm.bind(&doneTruncate); masm.bind(&doneTruncate);
masm.not32(scratchReg); masm.not32(scratchReg);
masm.tagValue(JSVAL_TYPE_INT32, scratchReg, R0); masm.tagValue(JSVAL_TYPE_INT32, scratchReg, R0);
break;
}
case JSOP_NEG:
masm.negateDouble(FloatReg0);
masm.boxDouble(FloatReg0, R0);
break;
case JSOP_INC:
case JSOP_DEC:
masm.loadConstantDouble(1.0, ScratchDoubleReg);
if (op == JSOP_INC) {
masm.addDouble(ScratchDoubleReg, FloatReg0);
} else {
masm.subDouble(ScratchDoubleReg, FloatReg0);
}
masm.boxDouble(FloatReg0, R0);
break;
default:
MOZ_CRASH("Unexpected op");
} }
EmitReturnFromIC(masm); EmitReturnFromIC(masm);

View file

@ -1855,6 +1855,8 @@ class ICBinaryArith_DoubleWithInt32 : public ICStub
// UnaryArith // UnaryArith
// JSOP_BITNOT // JSOP_BITNOT
// JSOP_NEG // JSOP_NEG
// JSOP_INC
// JSOP_DEC
class ICUnaryArith_Fallback : public ICFallbackStub class ICUnaryArith_Fallback : public ICFallbackStub
{ {

View file

@ -1186,6 +1186,7 @@ FilterTypeSetPolicy::adjustInputs(TempAllocator& alloc, MInstruction* ins)
// Lists of all TypePolicy specializations which are used by MIR Instructions. // Lists of all TypePolicy specializations which are used by MIR Instructions.
#define TYPE_POLICY_LIST(_) \ #define TYPE_POLICY_LIST(_) \
_(AllDoublePolicy) \
_(ArithPolicy) \ _(ArithPolicy) \
_(BitwisePolicy) \ _(BitwisePolicy) \
_(BoxInputsPolicy) \ _(BoxInputsPolicy) \
@ -1204,7 +1205,6 @@ FilterTypeSetPolicy::adjustInputs(TempAllocator& alloc, MInstruction* ins)
_(StoreUnboxedScalarPolicy) \ _(StoreUnboxedScalarPolicy) \
_(StoreUnboxedObjectOrNullPolicy) \ _(StoreUnboxedObjectOrNullPolicy) \
_(TestPolicy) \ _(TestPolicy) \
_(AllDoublePolicy) \
_(ToDoublePolicy) \ _(ToDoublePolicy) \
_(ToInt32Policy) \ _(ToInt32Policy) \
_(ToStringPolicy) \ _(ToStringPolicy) \

View file

@ -1357,5 +1357,19 @@ CheckIsCallable(JSContext* cx, HandleValue v, CheckIsCallableKind kind)
return true; return true;
} }
template <bool allowBigInt = false>
static bool DoToNumeric(JSContext* cx, HandleValue arg, MutableHandleValue ret)
{
ret.set(arg);
if (allowBigInt) {
return ToNumeric(cx, ret);
}
return ToNumber(cx, ret);
}
typedef bool (*ToNumericFn)(JSContext*, HandleValue, MutableHandleValue);
const VMFunction ToNumberInfo = FunctionInfo<ToNumericFn>(DoToNumeric, "ToNumber");
const VMFunction ToNumericInfo = FunctionInfo<ToNumericFn>(DoToNumeric<true>, "ToNumeric");
} // namespace jit } // namespace jit
} // namespace js } // namespace js

View file

@ -803,6 +803,9 @@ BaselineGetFunctionThis(JSContext* cx, BaselineFrame* frame, MutableHandleValue
MOZ_MUST_USE bool MOZ_MUST_USE bool
CheckIsCallable(JSContext* cx, HandleValue v, CheckIsCallableKind kind); CheckIsCallable(JSContext* cx, HandleValue v, CheckIsCallableKind kind);
extern const VMFunction ToNumberInfo;
extern const VMFunction ToNumericInfo;
} // namespace jit } // namespace jit
} // namespace js } // namespace js

View file

@ -7494,6 +7494,21 @@ class LLoadUnboxedExpando : public LInstructionHelper<1, 1, 0>
} }
}; };
// Ensure that a value is numeric, possibly via a VM call-out that invokes
// valueOf().
class LToNumeric : public LInstructionHelper<BOX_PIECES, BOX_PIECES, 0> {
public:
LIR_HEADER(ToNumeric)
explicit LToNumeric(const LBoxAllocation& input) {
setBoxOperand(Input, input);
}
static const size_t Input = 0;
const MToNumeric* mir() const { return mir_->toToNumeric(); }
};
// Guard that a value is in a TypeSet. // Guard that a value is in a TypeSet.
class LTypeBarrierV : public LInstructionHelper<0, BOX_PIECES, 1> class LTypeBarrierV : public LInstructionHelper<0, BOX_PIECES, 1>
{ {

View file

@ -258,6 +258,7 @@
_(GuardClass) \ _(GuardClass) \
_(GuardUnboxedExpando) \ _(GuardUnboxedExpando) \
_(LoadUnboxedExpando) \ _(LoadUnboxedExpando) \
_(ToNumeric) \
_(TypeBarrierV) \ _(TypeBarrierV) \
_(TypeBarrierO) \ _(TypeBarrierO) \
_(MonitorTypes) \ _(MonitorTypes) \

View file

@ -216,6 +216,16 @@ ICUnaryArith_Int32::Compiler::generateStubCode(MacroAssembler& masm)
masm.branchTest32(Assembler::Zero, R0.valueReg(), Imm32(0x7fffffff), &failure); masm.branchTest32(Assembler::Zero, R0.valueReg(), Imm32(0x7fffffff), &failure);
masm.negl(R0.valueReg()); masm.negl(R0.valueReg());
break; break;
case JSOP_INC: {
RegisterOrInt32Constant rval = RegisterOrInt32Constant(R0.valueReg());
masm.inc32(&rval);
break;
}
case JSOP_DEC: {
RegisterOrInt32Constant rval = RegisterOrInt32Constant(R0.valueReg());
masm.dec32(&rval);
break;
}
default: default:
MOZ_CRASH("Unexpected op"); MOZ_CRASH("Unexpected op");
} }

View file

@ -4958,7 +4958,7 @@ threeByteOpImmSimd("vblendps", VEX_PD, OP3_BLENDPS_VpsWpsIb, ESCAPE_3A, imm, off
void twoByteOp8(TwoByteOpcodeID opcode, RegisterID rm, RegisterID reg) void twoByteOp8(TwoByteOpcodeID opcode, RegisterID rm, RegisterID reg)
{ {
m_buffer.ensureSpace(MaxInstructionSize); m_buffer.ensureSpace(MaxInstructionSize);
emitRexIf(byteRegRequiresRex(reg)|byteRegRequiresRex(rm), reg, 0, rm); emitRexIf(byteRegRequiresRex(reg)||byteRegRequiresRex(rm), reg, 0, rm);
m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE); m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE);
m_buffer.putByteUnchecked(opcode); m_buffer.putByteUnchecked(opcode);
registerModRM(rm, reg); registerModRM(rm, reg);
@ -4967,7 +4967,7 @@ threeByteOpImmSimd("vblendps", VEX_PD, OP3_BLENDPS_VpsWpsIb, ESCAPE_3A, imm, off
void twoByteOp8(TwoByteOpcodeID opcode, int32_t offset, RegisterID base, RegisterID reg) void twoByteOp8(TwoByteOpcodeID opcode, int32_t offset, RegisterID base, RegisterID reg)
{ {
m_buffer.ensureSpace(MaxInstructionSize); m_buffer.ensureSpace(MaxInstructionSize);
emitRexIf(byteRegRequiresRex(reg)|regRequiresRex(base), reg, 0, base); emitRexIf(byteRegRequiresRex(reg)||regRequiresRex(base), reg, 0, base);
m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE); m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE);
m_buffer.putByteUnchecked(opcode); m_buffer.putByteUnchecked(opcode);
memoryModRM(offset, base, reg); memoryModRM(offset, base, reg);
@ -4977,7 +4977,7 @@ threeByteOpImmSimd("vblendps", VEX_PD, OP3_BLENDPS_VpsWpsIb, ESCAPE_3A, imm, off
int scale, RegisterID reg) int scale, RegisterID reg)
{ {
m_buffer.ensureSpace(MaxInstructionSize); m_buffer.ensureSpace(MaxInstructionSize);
emitRexIf(byteRegRequiresRex(reg)|regRequiresRex(base)|regRequiresRex(index), emitRexIf(byteRegRequiresRex(reg)||regRequiresRex(base)||regRequiresRex(index),
reg, index, base); reg, index, base);
m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE); m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE);
m_buffer.putByteUnchecked(opcode); m_buffer.putByteUnchecked(opcode);
@ -4991,7 +4991,7 @@ threeByteOpImmSimd("vblendps", VEX_PD, OP3_BLENDPS_VpsWpsIb, ESCAPE_3A, imm, off
void twoByteOp8_movx(TwoByteOpcodeID opcode, RegisterID rm, RegisterID reg) void twoByteOp8_movx(TwoByteOpcodeID opcode, RegisterID rm, RegisterID reg)
{ {
m_buffer.ensureSpace(MaxInstructionSize); m_buffer.ensureSpace(MaxInstructionSize);
emitRexIf(regRequiresRex(reg)|byteRegRequiresRex(rm), reg, 0, rm); emitRexIf(regRequiresRex(reg)||byteRegRequiresRex(rm), reg, 0, rm);
m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE); m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE);
m_buffer.putByteUnchecked(opcode); m_buffer.putByteUnchecked(opcode);
registerModRM(rm, reg); registerModRM(rm, reg);

View file

@ -226,6 +226,17 @@ ICUnaryArith_Int32::Compiler::generateStubCode(MacroAssembler& masm)
masm.branchTest32(Assembler::Zero, R0.payloadReg(), Imm32(0x7fffffff), &failure); masm.branchTest32(Assembler::Zero, R0.payloadReg(), Imm32(0x7fffffff), &failure);
masm.negl(R0.payloadReg()); masm.negl(R0.payloadReg());
break; break;
case JSOP_INC: {
RegisterOrInt32Constant rval = RegisterOrInt32Constant(R0.payloadReg());
masm.inc32(&rval);
break;
}
case JSOP_DEC: {
RegisterOrInt32Constant rval = RegisterOrInt32Constant(R0.payloadReg());
masm.dec32(&rval);
break;
}
default: default:
MOZ_CRASH("Unexpected op"); MOZ_CRASH("Unexpected op");
} }

View file

@ -277,10 +277,15 @@ PreprocessValue(JSContext* cx, HandleObject holder, KeyType key, MutableHandleVa
RootedString keyStr(cx); RootedString keyStr(cx);
/* Step 2. */ // Step 2. Modified by BigInt spec 6.1 to check for a toJSON method on the
if (vp.isObject()) { // BigInt prototype when the value is a BigInt.
if (vp.isObject() || vp.isBigInt()) {
RootedValue toJSON(cx); RootedValue toJSON(cx);
RootedObject obj(cx, &vp.toObject()); RootedObject obj(cx, JS::ToObject(cx, vp));
if (!obj) {
return false;
}
if (!GetProperty(cx, obj, obj, cx->names().toJSON, &toJSON)) if (!GetProperty(cx, obj, obj, cx->names().toJSON, &toJSON))
return false; return false;

View file

@ -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)) {

View file

@ -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) {

View file

@ -416,6 +416,46 @@ NegOperation(JSContext* cx, HandleScript script, jsbytecode* pc, MutableHandleVa
return true; return true;
} }
static MOZ_ALWAYS_INLINE bool
IncOperation(JSContext* cx,
MutableHandleValue val,
MutableHandleValue res)
{
int32_t i;
if (val.isInt32() && (i = val.toInt32()) != INT32_MAX) {
res.setInt32(i + 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
DecOperation(JSContext* cx,
MutableHandleValue val,
MutableHandleValue res)
{
int32_t i;
if (val.isInt32() && (i = val.toInt32()) != INT32_MIN) {
res.setInt32(i - 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 static MOZ_ALWAYS_INLINE bool
ToIdOperation(JSContext* cx, HandleScript script, jsbytecode* pc, HandleValue idval, ToIdOperation(JSContext* cx, HandleScript script, jsbytecode* pc, HandleValue idval,
MutableHandleValue res) MutableHandleValue res)

View file

@ -4176,6 +4176,34 @@ CASE(JSOP_IS_CONSTRUCTING)
PUSH_MAGIC(JS_IS_CONSTRUCTING); PUSH_MAGIC(JS_IS_CONSTRUCTING);
END_CASE(JSOP_IS_CONSTRUCTING) END_CASE(JSOP_IS_CONSTRUCTING)
CASE(JSOP_INC)
{
ReservedRooted<Value> val(&rootValue0, REGS.sp[-1]);
MutableHandleValue res = REGS.stackHandleAt(-1);
if (!IncOperation(cx, &val, res)) {
goto error;
}
}
END_CASE(JSOP_INC)
CASE(JSOP_DEC)
{
ReservedRooted<Value> val(&rootValue0, REGS.sp[-1]);
MutableHandleValue res = REGS.stackHandleAt(-1);
if (!DecOperation(cx, &val, res)) {
goto error;
}
}
END_CASE(JSOP_DEC)
CASE(JSOP_TONUMERIC)
{
if (!ToNumeric(cx, REGS.stackHandleAt(-1))) {
goto error;
}
}
END_CASE(JSOP_TONUMERIC)
CASE(JSOP_BIGINT) CASE(JSOP_BIGINT)
{ {
PUSH_COPY(script->getConst(GET_UINT32_INDEX(REGS.pc))); PUSH_COPY(script->getConst(GET_UINT32_INDEX(REGS.pc)));

View file

@ -2358,6 +2358,32 @@
* Stack: arg => rval * Stack: arg => rval
*/ \ */ \
macro(JSOP_DYNAMIC_IMPORT, 234, "call-import", NULL, 1, 1, 1, JOF_BYTE) \ macro(JSOP_DYNAMIC_IMPORT, 234, "call-import", NULL, 1, 1, 1, JOF_BYTE) \
/*
* Pops the numeric value 'val' from the stack, then pushes 'val + 1'.
*
* Category: Operators
* Type: Arithmetic Operators
* Operands:
* Stack: val => (val + 1)
*/ \
macro(JSOP_INC, 235, "inc", NULL, 1, 1, 1, JOF_BYTE) \
/*
* Pops the numeric value 'val' from the stack, then pushes 'val - 1'.
*
* Category: Operators
* Type: Arithmetic Operators
* Operands:
* Stack: val => (val - 1)
*/ \
macro(JSOP_DEC, 236, "dec", NULL, 1, 1, 1, JOF_BYTE) \
/*
* Pop 'val' from the stack, then push the result of 'ToNumeric(val)'.
* Category: Operators
* Type: Arithmetic Operators
* Operands:
* Stack: val => ToNumeric(val)
*/ \
macro(JSOP_TONUMERIC, 237, "tonumeric", NULL, 1, 1, 1, JOF_BYTE) \
/* /*
* Pushes a BigInt constant onto the stack. * Pushes a BigInt constant onto the stack.
* Category: Literals * Category: Literals
@ -2365,15 +2391,12 @@
* Operands: uint32_t constIndex * Operands: uint32_t constIndex
* Stack: => val * Stack: => val
*/ \ */ \
macro(JSOP_BIGINT, 235, "bigint", NULL, 5, 0, 1, JOF_BIGINT) macro(JSOP_BIGINT, 238, "bigint", NULL, 5, 0, 1, JOF_BIGINT)
/* /*
* In certain circumstances it may be useful to "pad out" the opcode space to * In certain circumstances it may be useful to "pad out" the opcode space to
* a power of two. Use this macro to do so. * a power of two. Use this macro to do so.
*/ */
#define FOR_EACH_TRAILING_UNUSED_OPCODE(macro) \ #define FOR_EACH_TRAILING_UNUSED_OPCODE(macro) \
macro(236) \
macro(237) \
macro(238) \
macro(239) \ macro(239) \
macro(240) \ macro(240) \
macro(241) \ macro(241) \

View file

@ -3245,6 +3245,10 @@ js::IsTypedArrayConstructor(HandleValue v, uint32_t type)
return IsNativeFunction(v, Int32Array::class_constructor); return IsNativeFunction(v, Int32Array::class_constructor);
case Scalar::Uint32: case Scalar::Uint32:
return IsNativeFunction(v, Uint32Array::class_constructor); return IsNativeFunction(v, Uint32Array::class_constructor);
case Scalar::BigInt64:
return IsNativeFunction(v, BigInt64Array::class_constructor);
case Scalar::BigUint64:
return IsNativeFunction(v, BigUint64Array::class_constructor);
case Scalar::Float32: case Scalar::Float32:
return IsNativeFunction(v, Float32Array::class_constructor); return IsNativeFunction(v, Float32Array::class_constructor);
case Scalar::Float64: case Scalar::Float64: