summaryrefslogtreecommitdiff
path: root/js/src
diff options
context:
space:
mode:
authorMoonchild <moonchild@palemoon.org>2023-09-20 11:56:31 +0200
committerMoonchild <moonchild@palemoon.org>2023-09-20 11:56:31 +0200
commitd2eb2ce9f4760ad2dd5aaa474013d42e5bc9d762 (patch)
tree9a7bca4b6417d8a11ac6f102e7d3b24fb8008fd7 /js/src
parentbb39e1e38f75d115bd1f0dc82d5b561a2da57d2e (diff)
parentdba1e366014e91a04823e047dc20c8d01d259702 (diff)
downloaduxp-d2eb2ce9f4760ad2dd5aaa474013d42e5bc9d762.tar.gz
Merge branch 'master' into simdjs-removal
Diffstat (limited to 'js/src')
-rw-r--r--js/src/frontend/BytecodeEmitter.cpp2
-rw-r--r--js/src/frontend/ElemOpEmitter.cpp9
-rw-r--r--js/src/frontend/NameOpEmitter.cpp9
-rw-r--r--js/src/frontend/PropOpEmitter.cpp9
-rw-r--r--js/src/jit/BaselineCompiler.cpp43
-rw-r--r--js/src/jit/BaselineCompiler.h9
-rw-r--r--js/src/jit/BaselineIC.cpp36
-rw-r--r--js/src/jit/BaselineIC.h26
-rw-r--r--js/src/jit/BaselineICList.h2
-rw-r--r--js/src/jit/BaselineInspector.cpp5
-rw-r--r--js/src/jit/CodeGenerator.cpp44
-rw-r--r--js/src/jit/CodeGenerator.h1
-rw-r--r--js/src/jit/IonBuilder.cpp238
-rw-r--r--js/src/jit/IonBuilder.h13
-rw-r--r--js/src/jit/IonTypes.h4
-rw-r--r--js/src/jit/Lowering.cpp9
-rw-r--r--js/src/jit/Lowering.h1
-rw-r--r--js/src/jit/MIR.cpp34
-rw-r--r--js/src/jit/MIR.h38
-rw-r--r--js/src/jit/MOpcodes.h1
-rw-r--r--js/src/jit/RangeAnalysis.cpp6
-rw-r--r--js/src/jit/SharedIC.cpp46
-rw-r--r--js/src/jit/SharedIC.h2
-rw-r--r--js/src/jit/TypePolicy.cpp2
-rw-r--r--js/src/jit/VMFunctions.cpp14
-rw-r--r--js/src/jit/VMFunctions.h3
-rw-r--r--js/src/jit/shared/LIR-shared.h15
-rw-r--r--js/src/jit/shared/LOpcodes-shared.h1
-rw-r--r--js/src/jit/x64/SharedIC-x64.cpp10
-rw-r--r--js/src/jit/x86-shared/BaseAssembler-x86-shared.h8
-rw-r--r--js/src/jit/x86/SharedIC-x86.cpp11
-rw-r--r--js/src/json.cpp11
-rw-r--r--js/src/vm/BigIntType.cpp143
-rw-r--r--js/src/vm/BigIntType.h10
-rw-r--r--js/src/vm/Interpreter-inl.h40
-rw-r--r--js/src/vm/Interpreter.cpp28
-rw-r--r--js/src/vm/Opcodes.h33
-rw-r--r--js/src/vm/TypedArrayObject.cpp4
38 files changed, 709 insertions, 211 deletions
diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp
index 462edfd91e..7680411f07 100644
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -2057,7 +2057,7 @@ BytecodeEmitter::emitCallIncDec(UnaryNode* incDec)
MOZ_ASSERT(call->isKind(PNK_CALL));
if (!emitTree(call)) // CALLRESULT
return false;
- if (!emit1(JSOP_POS)) // N
+ if (!emit1(JSOP_TONUMERIC)) // N
return false;
// The increment/decrement has no side effects, so proceed to throw for
diff --git a/js/src/frontend/ElemOpEmitter.cpp b/js/src/frontend/ElemOpEmitter.cpp
index f17b554e7a..39d0eca21c 100644
--- a/js/src/frontend/ElemOpEmitter.cpp
+++ b/js/src/frontend/ElemOpEmitter.cpp
@@ -233,8 +233,8 @@ ElemOpEmitter::emitIncDec()
MOZ_ASSERT(state_ == State::Get);
- JSOp binOp = isInc() ? JSOP_ADD : JSOP_SUB;
- if (!bce_->emit1(JSOP_POS)) { // ... N
+ JSOp incOp = isInc() ? JSOP_INC : JSOP_DEC;
+ if (!bce_->emit1(JSOP_TONUMERIC)) { // ... N
return false;
}
if (isPostIncDec()) {
@@ -242,10 +242,7 @@ ElemOpEmitter::emitIncDec()
return false;
}
}
- if (!bce_->emit1(JSOP_ONE)) { // ... N? N 1
- return false;
- }
- if (!bce_->emit1(binOp)) { // ... N? N+1
+ if (!bce_->emit1(incOp)) { // ... N? N+1
return false;
}
if (isPostIncDec()) {
diff --git a/js/src/frontend/NameOpEmitter.cpp b/js/src/frontend/NameOpEmitter.cpp
index 8fa7f9d966..48caac8de3 100644
--- a/js/src/frontend/NameOpEmitter.cpp
+++ b/js/src/frontend/NameOpEmitter.cpp
@@ -339,11 +339,11 @@ NameOpEmitter::emitIncDec()
{
MOZ_ASSERT(state_ == State::Start);
- JSOp binOp = isInc() ? JSOP_ADD : JSOP_SUB;
+ JSOp incOp = isInc() ? JSOP_INC : JSOP_DEC;
if (!prepareForRhs()) { // ENV? V
return false;
}
- if (!bce_->emit1(JSOP_POS)) { // ENV? N
+ if (!bce_->emit1(JSOP_TONUMERIC)) { // ENV? N
return false;
}
if (isPostIncDec()) {
@@ -351,10 +351,7 @@ NameOpEmitter::emitIncDec()
return false;
}
}
- if (!bce_->emit1(JSOP_ONE)) { // ENV? N? N 1
- return false;
- }
- if (!bce_->emit1(binOp)) { // ENV? N? N+1
+ if (!bce_->emit1(incOp)) { // ENV? N? N+1
return false;
}
if (isPostIncDec() && emittedBindOp()) {
diff --git a/js/src/frontend/PropOpEmitter.cpp b/js/src/frontend/PropOpEmitter.cpp
index bfbca00e1d..278bc0e0fd 100644
--- a/js/src/frontend/PropOpEmitter.cpp
+++ b/js/src/frontend/PropOpEmitter.cpp
@@ -217,9 +217,9 @@ PropOpEmitter::emitIncDec(JSAtom* prop)
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;
}
if (isPostIncDec()) {
@@ -227,10 +227,7 @@ PropOpEmitter::emitIncDec(JSAtom* prop)
return false;
}
}
- if (!bce_->emit1(JSOP_ONE)) { // ... N? N 1
- return false;
- }
- if (!bce_->emit1(binOp)) { // ... N? N+1
+ if (!bce_->emit1(incOp)) { // ... N? N+1
return false;
}
if (isPostIncDec()) {
diff --git a/js/src/jit/BaselineCompiler.cpp b/js/src/jit/BaselineCompiler.cpp
index 7dbd076c6b..ab38011be2 100644
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -1322,14 +1322,37 @@ BaselineCompiler::emit_JSOP_POS()
// Keep top stack value in R0.
frame.popRegsAndSync(1);
- // Inline path for int32 and double.
+ // Inline path for int32 and double; otherwise call VM.
Label done;
masm.branchTestNumber(Assembler::Equal, R0, &done);
- // Call IC.
- ICToNumber_Fallback::Compiler stubCompiler(cx);
- if (!emitOpIC(stubCompiler.getStub(&stubSpace_)))
+ prepareVMCall();
+ pushArg(R0);
+ if (!callVM(ToNumberInfo)) {
+ 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);
frame.push(R0);
@@ -1940,6 +1963,18 @@ BaselineCompiler::emit_JSOP_NEG()
}
bool
+BaselineCompiler::emit_JSOP_INC()
+{
+ return emitUnaryArith();
+}
+
+bool
+BaselineCompiler::emit_JSOP_DEC()
+{
+ return emitUnaryArith();
+}
+
+bool
BaselineCompiler::emit_JSOP_LT()
{
return emitCompare();
diff --git a/js/src/jit/BaselineCompiler.h b/js/src/jit/BaselineCompiler.h
index 8e9976b17f..30da13d9c1 100644
--- a/js/src/jit/BaselineCompiler.h
+++ b/js/src/jit/BaselineCompiler.h
@@ -50,6 +50,7 @@ namespace jit {
_(JSOP_OR) \
_(JSOP_NOT) \
_(JSOP_POS) \
+ _(JSOP_TONUMERIC) \
_(JSOP_LOOPHEAD) \
_(JSOP_LOOPENTRY) \
_(JSOP_VOID) \
@@ -242,8 +243,10 @@ namespace jit {
_(JSOP_JUMPTARGET) \
_(JSOP_IS_CONSTRUCTING) \
_(JSOP_TRY_DESTRUCTURING_ITERCLOSE) \
- _(JSOP_IMPORTMETA) \
- _(JSOP_DYNAMIC_IMPORT)
+ _(JSOP_IMPORTMETA) \
+ _(JSOP_DYNAMIC_IMPORT) \
+ _(JSOP_INC) \
+ _(JSOP_DEC)
class BaselineCompiler : public BaselineCompilerSpecific
{
@@ -327,7 +330,7 @@ class BaselineCompiler : public BaselineCompilerSpecific
OPCODE_LIST(EMIT_OP)
#undef EMIT_OP
- // JSOP_NEG, JSOP_BITNOT
+ // JSOP_NEG, JSOP_BITNOT, JSOP_INC, JSOP_DEC
MOZ_MUST_USE bool emitUnaryArith();
// JSOP_BITXOR, JSOP_LSH, JSOP_ADD etc.
diff --git a/js/src/jit/BaselineIC.cpp b/js/src/jit/BaselineIC.cpp
index fa02c24374..b657359d3e 100644
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -694,42 +694,6 @@ ICToBool_Object::Compiler::generateStubCode(MacroAssembler& masm)
}
//
-// 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
//
diff --git a/js/src/jit/BaselineIC.h b/js/src/jit/BaselineIC.h
index 895c8af6b4..bd30ec0369 100644
--- a/js/src/jit/BaselineIC.h
+++ b/js/src/jit/BaselineIC.h
@@ -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
// JSOP_GETELEM
diff --git a/js/src/jit/BaselineICList.h b/js/src/jit/BaselineICList.h
index 09b0db82ad..08e61a1872 100644
--- a/js/src/jit/BaselineICList.h
+++ b/js/src/jit/BaselineICList.h
@@ -34,8 +34,6 @@ namespace jit {
_(ToBool_Double) \
_(ToBool_Object) \
\
- _(ToNumber_Fallback) \
- \
_(Call_Fallback) \
_(Call_Scripted) \
_(Call_AnyScripted) \
diff --git a/js/src/jit/BaselineInspector.cpp b/js/src/jit/BaselineInspector.cpp
index 4c22a1839c..1b639d6b10 100644
--- a/js/src/jit/BaselineInspector.cpp
+++ b/js/src/jit/BaselineInspector.cpp
@@ -408,6 +408,11 @@ BaselineInspector::expectedBinaryArithSpecialization(jsbytecode* pc)
MIRType result;
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);
ICStub* stub = entry.fallbackStub();
if (stub->isBinaryArith_Fallback() &&
diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp
index d427f5f7f4..0c28e978c4 100644
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -2377,6 +2377,8 @@ CodeGenerator::visitUnarySharedStub(LUnarySharedStub* lir)
switch (jsop) {
case JSOP_BITNOT:
case JSOP_NEG:
+ case JSOP_INC:
+ case JSOP_DEC:
emitSharedStub(ICStub::Kind::UnaryArith_Fallback, lir);
break;
case JSOP_CALLPROP:
@@ -3413,6 +3415,48 @@ CodeGenerator::visitLoadUnboxedExpando(LLoadUnboxedExpando* lir)
}
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
CodeGenerator::visitTypeBarrierV(LTypeBarrierV* lir)
{
ValueOperand operand = ToValue(lir, LTypeBarrierV::Input);
diff --git a/js/src/jit/CodeGenerator.h b/js/src/jit/CodeGenerator.h
index 44804794a2..719f7ca1b1 100644
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -149,6 +149,7 @@ class CodeGenerator final : public CodeGeneratorSpecific
void visitGuardReceiverPolymorphic(LGuardReceiverPolymorphic* lir);
void visitGuardUnboxedExpando(LGuardUnboxedExpando* lir);
void visitLoadUnboxedExpando(LLoadUnboxedExpando* lir);
+ void visitToNumeric(LToNumeric* lir);
void visitTypeBarrierV(LTypeBarrierV* lir);
void visitTypeBarrierO(LTypeBarrierO* lir);
void visitMonitorTypes(LMonitorTypes* lir);
diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp
index ae3776c888..7eb69ec00f 100644
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -636,7 +636,7 @@ IonBuilder::analyzeNewLoopTypes(MBasicBlock* entry, jsbytecode* start, jsbytecod
MPhi* phi = entry->getSlot(slot)->toPhi();
- if (*last == JSOP_POS)
+ if (*last == JSOP_POS || *last == JSOP_TONUMERIC)
last = earlier;
if (CodeSpec[*last].format & JOF_TYPESET) {
@@ -719,6 +719,8 @@ IonBuilder::analyzeNewLoopTypes(MBasicBlock* entry, jsbytecode* start, jsbytecod
case JSOP_DIV:
case JSOP_MOD:
case JSOP_NEG:
+ case JSOP_INC:
+ case JSOP_DEC:
type = inspector->expectedResultType(last);
break;
case JSOP_BIGINT:
@@ -1584,6 +1586,7 @@ IonBuilder::traverseBytecode()
break;
case JSOP_POS:
+ case JSOP_TONUMERIC:
case JSOP_TOID:
case JSOP_TOSTRING:
// These ops may leave their input on the stack without setting
@@ -1737,9 +1740,16 @@ IonBuilder::inspectOpcode(JSOp op)
case JSOP_POS:
return jsop_pos();
+ case JSOP_TONUMERIC:
+ return jsop_tonumeric();
+
case JSOP_NEG:
return jsop_neg();
+ case JSOP_INC:
+ case JSOP_DEC:
+ return jsop_inc_or_dec(op);
+
case JSOP_TOSTRING:
return jsop_tostring();
@@ -4852,7 +4862,7 @@ IonBuilder::jsop_bitop(JSOp op)
}
MDefinition::Opcode
-JSOpToMDefinition(JSOp op)
+BinaryJSOpToMDefinition(JSOp op)
{
switch (op) {
case JSOP_ADD:
@@ -4953,6 +4963,40 @@ IonBuilder::powTrySpecialized(bool* emitted, MDefinition* base, MDefinition* pow
return true;
}
+MIRType
+IonBuilder::binaryArithNumberSpecialization(MDefinition* left, MDefinition* right)
+{
+ // Try to specialize as int32.
+ if (left->type() == MIRType::Int32 && right->type() == MIRType::Int32 &&
+ !inspector->hasSeenDoubleResult(pc)) {
+ return MIRType::Int32;
+ }
+ return MIRType::Double;
+}
+
+MBinaryArithInstruction*
+IonBuilder::binaryArithEmitSpecialized(MDefinition::Opcode op, MIRType specialization,
+ MDefinition* left, MDefinition* right)
+{
+ MBinaryArithInstruction* ins = MBinaryArithInstruction::New(alloc(), op, left, right);
+ ins->setSpecialization(specialization);
+
+ if (op == MDefinition::Op_Add || op == MDefinition::Op_Mul) {
+ ins->setCommutative();
+ }
+
+ current->add(ins);
+ current->push(ins);
+
+ MOZ_ASSERT(!ins->isEffectful());
+
+ if(!maybeInsertResume()) {
+ return nullptr;
+ }
+
+ return ins;
+}
+
static inline bool
SimpleArithOperand(MDefinition* op)
{
@@ -4986,19 +5030,20 @@ IonBuilder::binaryArithTrySpecialized(bool* emitted, JSOp op, MDefinition* left,
return true;
}
- MDefinition::Opcode defOp = JSOpToMDefinition(op);
- MBinaryArithInstruction* ins = MBinaryArithInstruction::New(alloc(), defOp, left, right);
- ins->setNumberSpecialization(alloc(), inspector, pc);
-
- if (op == JSOP_ADD || op == JSOP_MUL)
- ins->setCommutative();
-
- current->add(ins);
- current->push(ins);
+ MDefinition::Opcode defOp = BinaryJSOpToMDefinition(op);
+ MIRType specialization = binaryArithNumberSpecialization(left, right);
+ MBinaryArithInstruction* ins = binaryArithEmitSpecialized(defOp, specialization, left, right);
- MOZ_ASSERT(!ins->isEffectful());
- if (!maybeInsertResume())
+ if(!ins) {
return false;
+ }
+
+ // Relax int32 to double if, despite the fact that we have int32 operands and
+ // we've never seen a double result, we know the result may overflow or be a
+ // double.
+ if (specialization == MIRType::Int32 && ins->constantDoubleResult(alloc())) {
+ ins->setSpecialization(MIRType::Double);
+ }
trackOptimizationSuccess();
*emitted = true;
@@ -5022,16 +5067,10 @@ IonBuilder::binaryArithTrySpecializedOnBaselineInspector(bool* emitted, JSOp op,
return true;
}
- MDefinition::Opcode def_op = JSOpToMDefinition(op);
- MBinaryArithInstruction* ins = MBinaryArithInstruction::New(alloc(), def_op, left, right);
- ins->setSpecialization(specialization);
-
- current->add(ins);
- current->push(ins);
-
- MOZ_ASSERT(!ins->isEffectful());
- if (!maybeInsertResume())
+ MDefinition::Opcode defOp = BinaryJSOpToMDefinition(op);
+ if(!binaryArithEmitSpecialized(defOp, specialization, left, right)) {
return false;
+ }
trackOptimizationSuccess();
*emitted = true;
@@ -5124,8 +5163,8 @@ IonBuilder::jsop_binary_arith(JSOp op, MDefinition* left, MDefinition* right)
trackOptimizationAttempt(TrackedStrategy::BinaryArith_Call);
trackOptimizationSuccess();
- MDefinition::Opcode def_op = JSOpToMDefinition(op);
- MBinaryArithInstruction* ins = MBinaryArithInstruction::New(alloc(), def_op, left, right);
+ MDefinition::Opcode defOp = BinaryJSOpToMDefinition(op);
+ MBinaryArithInstruction* ins = MBinaryArithInstruction::New(alloc(), defOp, left, right);
// Decrease type from 'any type' to 'empty type' when one of the operands
// is 'empty typed'.
@@ -5202,6 +5241,157 @@ IonBuilder::jsop_neg()
return jsop_binary_arith(JSOP_MUL, negator, right);
}
+MDefinition*
+IonBuilder::unaryArithConvertToBinary(JSOp op, MDefinition::Opcode* defOp)
+{
+ switch (op) {
+ case JSOP_INC: {
+ *defOp = MDefinition::Op_Add;
+ MConstant* right = MConstant::New(alloc(), Int32Value(1));
+ current->add(right);
+ return right;
+ }
+ case JSOP_DEC: {
+ *defOp = MDefinition::Op_Sub;
+ MConstant* right = MConstant::New(alloc(), Int32Value(1));
+ current->add(right);
+ return right;
+ }
+ default:
+ MOZ_CRASH("unexpected unary opcode");
+ }
+}
+
+bool
+IonBuilder::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
IonBuilder::jsop_tostring()
{
diff --git a/js/src/jit/IonBuilder.h b/js/src/jit/IonBuilder.h
index 96cfd821ca..8f616b74eb 100644
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -497,8 +497,13 @@ class IonBuilder
// jsop_binary_arith helpers.
MBinaryArithInstruction* binaryArithInstruction(JSOp op, MDefinition* left, MDefinition* right);
+ MIRType binaryArithNumberSpecialization(MDefinition* left, MDefinition* right);
MOZ_MUST_USE bool binaryArithTryConcat(bool* emitted, JSOp op, MDefinition* left,
MDefinition* right);
+ MOZ_MUST_USE MBinaryArithInstruction* binaryArithEmitSpecialized(MDefinition::Opcode op,
+ MIRType specialization,
+ MDefinition* left,
+ MDefinition* right);
MOZ_MUST_USE bool binaryArithTrySpecialized(bool* emitted, JSOp op, MDefinition* left,
MDefinition* right);
MOZ_MUST_USE bool binaryArithTrySpecializedOnBaselineInspector(bool* emitted, JSOp op,
@@ -510,6 +515,12 @@ class IonBuilder
// jsop_bitnot helpers.
MOZ_MUST_USE bool bitnotTrySpecialized(bool* emitted, MDefinition* input);
+ // jsop_inc_or_dec helpers.
+ MDefinition* unaryArithConvertToBinary(JSOp op, MDefinition::Opcode* defOp);
+ MOZ_MUST_USE bool unaryArithTrySpecialized(bool* emitted, JSOp op, MDefinition* value);
+ MOZ_MUST_USE bool unaryArithTrySpecializedOnBaselineInspector(bool* emitted, JSOp op,
+ MDefinition* value);
+
// jsop_pow helpers.
MOZ_MUST_USE bool powTrySpecialized(bool* emitted, MDefinition* base, MDefinition* power,
MIRType outputType);
@@ -692,6 +703,8 @@ class IonBuilder
MOZ_MUST_USE bool jsop_pow();
MOZ_MUST_USE bool jsop_pos();
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_setarg(uint32_t arg);
MOZ_MUST_USE bool jsop_defvar(uint32_t index);
diff --git a/js/src/jit/IonTypes.h b/js/src/jit/IonTypes.h
index 50d612ac8e..95e6aaf459 100644
--- a/js/src/jit/IonTypes.h
+++ b/js/src/jit/IonTypes.h
@@ -619,6 +619,10 @@ IsNumberType(MIRType type)
type == MIRType::Int64;
}
+static inline bool IsNumericType(MIRType type) {
+ return IsNumberType(type) || type == MIRType::BigInt;
+}
+
static inline bool
IsTypeRepresentableAsDouble(MIRType type)
{
diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp
index 11e7348547..d2ce1596ff 100644
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -2166,6 +2166,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
LIRGenerator::visitTruncateToInt32(MTruncateToInt32* truncate)
{
MDefinition* opd = truncate->input();
diff --git a/js/src/jit/Lowering.h b/js/src/jit/Lowering.h
index 9217ed9dcd..6a59c1b2ea 100644
--- a/js/src/jit/Lowering.h
+++ b/js/src/jit/Lowering.h
@@ -325,6 +325,7 @@ class LIRGenerator : public LIRGeneratorSpecific
void visitDebugCheckSelfHosted(MDebugCheckSelfHosted* ins);
void visitModuleMetadata(MModuleMetadata* ins);
void visitDynamicImport(MDynamicImport* ins);
+ void visitToNumeric(MToNumeric* ins);
};
} // namespace jit
diff --git a/js/src/jit/MIR.cpp b/js/src/jit/MIR.cpp
index 34d9e07bf9..e30188cb48 100644
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -2586,23 +2586,6 @@ MBinaryArithInstruction::New(TempAllocator& alloc, Opcode op,
}
}
-void
-MBinaryArithInstruction::setNumberSpecialization(TempAllocator& alloc, BaselineInspector* inspector,
- jsbytecode* pc)
-{
- setSpecialization(MIRType::Double);
-
- // Try to specialize as int32.
- if (getOperand(0)->type() == MIRType::Int32 && getOperand(1)->type() == MIRType::Int32) {
- bool seenDouble = inspector->hasSeenDoubleResult(pc);
-
- // Use int32 specialization if the operation doesn't overflow on its
- // constant operands and if the operation has never overflowed.
- if (!seenDouble && !constantDoubleResult(alloc))
- setInt32Specialization();
- }
-}
-
bool
MBinaryArithInstruction::constantDoubleResult(TempAllocator& alloc)
{
@@ -3666,6 +3649,23 @@ MResumePoint::isRecoverableOperand(MUse* u) const
}
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*
MToInt32::foldsTo(TempAllocator& alloc)
{
MDefinition* input = getOperand(0);
diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h
index 532f404b51..23cce805c1 100644
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -4335,6 +4335,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
// 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.
@@ -5020,7 +5057,6 @@ class MBinaryArithInstruction
specialization_ = MIRType::Int32;
setResultType(MIRType::Int32);
}
- void setNumberSpecialization(TempAllocator& alloc, BaselineInspector* inspector, jsbytecode* pc);
virtual void trySpecializeFloat32(TempAllocator& alloc) override;
diff --git a/js/src/jit/MOpcodes.h b/js/src/jit/MOpcodes.h
index aa2dda77a2..857193e911 100644
--- a/js/src/jit/MOpcodes.h
+++ b/js/src/jit/MOpcodes.h
@@ -102,6 +102,7 @@ namespace jit {
_(ToDouble) \
_(ToFloat32) \
_(ToInt32) \
+ _(ToNumeric) \
_(TruncateToInt32) \
_(WrapInt64ToInt32) \
_(ExtendInt32ToInt64) \
diff --git a/js/src/jit/RangeAnalysis.cpp b/js/src/jit/RangeAnalysis.cpp
index 7b35349ab2..bf1ea72d2a 100644
--- a/js/src/jit/RangeAnalysis.cpp
+++ b/js/src/jit/RangeAnalysis.cpp
@@ -1742,6 +1742,12 @@ MTruncateToInt32::computeRange(TempAllocator& alloc)
}
void
+MToNumeric::computeRange(TempAllocator& alloc)
+{
+ setRange(new (alloc) Range(getOperand(0)));
+}
+
+void
MToInt32::computeRange(TempAllocator& alloc)
{
// No clamping since this computes the range *before* bailouts.
diff --git a/js/src/jit/SharedIC.cpp b/js/src/jit/SharedIC.cpp
index 375edb1400..d337534768 100644
--- a/js/src/jit/SharedIC.cpp
+++ b/js/src/jit/SharedIC.cpp
@@ -1469,21 +1469,33 @@ DoUnaryArithFallback(JSContext* cx, void* payload, ICUnaryArith_Fallback* stub_,
JSOp op = JSOp(*pc);
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) {
case JSOP_BITNOT: {
- RootedValue valCopy(cx, val);
if (!BitNot(cx, &valCopy, res)) {
return false;
}
break;
}
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;
break;
}
+ case JSOP_INC: {
+ if (!IncOperation(cx, &valCopy, res)) {
+ return false;
+ }
+ break;
+ }
+ case JSOP_DEC: {
+ if (!DecOperation(cx, &valCopy, res)) {
+ return false;
+ }
+ break;
+ }
default:
MOZ_CRASH("Unexpected op");
}
@@ -1558,12 +1570,8 @@ ICUnaryArith_Double::Compiler::generateStubCode(MacroAssembler& masm)
Label failure;
masm.ensureDouble(R0, FloatReg0, &failure);
- MOZ_ASSERT(op == JSOP_NEG || op == JSOP_BITNOT);
-
- if (op == JSOP_NEG) {
- masm.negateDouble(FloatReg0);
- masm.boxDouble(FloatReg0, R0);
- } else {
+ switch (op) {
+ case JSOP_BITNOT: {
// Truncate the double to an int32.
Register scratchReg = R1.scratchReg();
@@ -1581,6 +1589,24 @@ ICUnaryArith_Double::Compiler::generateStubCode(MacroAssembler& masm)
masm.bind(&doneTruncate);
masm.not32(scratchReg);
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);
diff --git a/js/src/jit/SharedIC.h b/js/src/jit/SharedIC.h
index d0038c9378..d259ebf0bc 100644
--- a/js/src/jit/SharedIC.h
+++ b/js/src/jit/SharedIC.h
@@ -1855,6 +1855,8 @@ class ICBinaryArith_DoubleWithInt32 : public ICStub
// UnaryArith
// JSOP_BITNOT
// JSOP_NEG
+// JSOP_INC
+// JSOP_DEC
class ICUnaryArith_Fallback : public ICFallbackStub
{
diff --git a/js/src/jit/TypePolicy.cpp b/js/src/jit/TypePolicy.cpp
index 59ac9e2c21..5023d00f77 100644
--- a/js/src/jit/TypePolicy.cpp
+++ b/js/src/jit/TypePolicy.cpp
@@ -1071,6 +1071,7 @@ FilterTypeSetPolicy::adjustInputs(TempAllocator& alloc, MInstruction* ins)
// Lists of all TypePolicy specializations which are used by MIR Instructions.
#define TYPE_POLICY_LIST(_) \
+ _(AllDoublePolicy) \
_(ArithPolicy) \
_(BitwisePolicy) \
_(BoxInputsPolicy) \
@@ -1086,7 +1087,6 @@ FilterTypeSetPolicy::adjustInputs(TempAllocator& alloc, MInstruction* ins)
_(StoreUnboxedScalarPolicy) \
_(StoreUnboxedObjectOrNullPolicy) \
_(TestPolicy) \
- _(AllDoublePolicy) \
_(ToDoublePolicy) \
_(ToInt32Policy) \
_(ToStringPolicy) \
diff --git a/js/src/jit/VMFunctions.cpp b/js/src/jit/VMFunctions.cpp
index 8802d3582b..68e4eb30e4 100644
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -1357,5 +1357,19 @@ CheckIsCallable(JSContext* cx, HandleValue v, CheckIsCallableKind kind)
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 js
diff --git a/js/src/jit/VMFunctions.h b/js/src/jit/VMFunctions.h
index f4280f5800..b134c5df05 100644
--- a/js/src/jit/VMFunctions.h
+++ b/js/src/jit/VMFunctions.h
@@ -803,6 +803,9 @@ BaselineGetFunctionThis(JSContext* cx, BaselineFrame* frame, MutableHandleValue
MOZ_MUST_USE bool
CheckIsCallable(JSContext* cx, HandleValue v, CheckIsCallableKind kind);
+extern const VMFunction ToNumberInfo;
+extern const VMFunction ToNumericInfo;
+
} // namespace jit
} // namespace js
diff --git a/js/src/jit/shared/LIR-shared.h b/js/src/jit/shared/LIR-shared.h
index 437c3fa7f3..7f8e77ea69 100644
--- a/js/src/jit/shared/LIR-shared.h
+++ b/js/src/jit/shared/LIR-shared.h
@@ -6797,6 +6797,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.
class LTypeBarrierV : public LInstructionHelper<0, BOX_PIECES, 1>
{
diff --git a/js/src/jit/shared/LOpcodes-shared.h b/js/src/jit/shared/LOpcodes-shared.h
index 10c6be0b36..a411d3f066 100644
--- a/js/src/jit/shared/LOpcodes-shared.h
+++ b/js/src/jit/shared/LOpcodes-shared.h
@@ -217,6 +217,7 @@
_(GuardClass) \
_(GuardUnboxedExpando) \
_(LoadUnboxedExpando) \
+ _(ToNumeric) \
_(TypeBarrierV) \
_(TypeBarrierO) \
_(MonitorTypes) \
diff --git a/js/src/jit/x64/SharedIC-x64.cpp b/js/src/jit/x64/SharedIC-x64.cpp
index 9e38cef6bf..ccad231d90 100644
--- a/js/src/jit/x64/SharedIC-x64.cpp
+++ b/js/src/jit/x64/SharedIC-x64.cpp
@@ -216,6 +216,16 @@ ICUnaryArith_Int32::Compiler::generateStubCode(MacroAssembler& masm)
masm.branchTest32(Assembler::Zero, R0.valueReg(), Imm32(0x7fffffff), &failure);
masm.negl(R0.valueReg());
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:
MOZ_CRASH("Unexpected op");
}
diff --git a/js/src/jit/x86-shared/BaseAssembler-x86-shared.h b/js/src/jit/x86-shared/BaseAssembler-x86-shared.h
index 399380ae53..31a8d1f65c 100644
--- a/js/src/jit/x86-shared/BaseAssembler-x86-shared.h
+++ b/js/src/jit/x86-shared/BaseAssembler-x86-shared.h
@@ -4942,7 +4942,7 @@ threeByteOpImmSimd("vblendps", VEX_PD, OP3_BLENDPS_VpsWpsIb, ESCAPE_3A, imm, off
void twoByteOp8(TwoByteOpcodeID opcode, RegisterID rm, RegisterID reg)
{
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(opcode);
registerModRM(rm, reg);
@@ -4951,7 +4951,7 @@ threeByteOpImmSimd("vblendps", VEX_PD, OP3_BLENDPS_VpsWpsIb, ESCAPE_3A, imm, off
void twoByteOp8(TwoByteOpcodeID opcode, int32_t offset, RegisterID base, RegisterID reg)
{
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(opcode);
memoryModRM(offset, base, reg);
@@ -4961,7 +4961,7 @@ threeByteOpImmSimd("vblendps", VEX_PD, OP3_BLENDPS_VpsWpsIb, ESCAPE_3A, imm, off
int scale, RegisterID reg)
{
m_buffer.ensureSpace(MaxInstructionSize);
- emitRexIf(byteRegRequiresRex(reg)|regRequiresRex(base)|regRequiresRex(index),
+ emitRexIf(byteRegRequiresRex(reg)||regRequiresRex(base)||regRequiresRex(index),
reg, index, base);
m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE);
m_buffer.putByteUnchecked(opcode);
@@ -4975,7 +4975,7 @@ threeByteOpImmSimd("vblendps", VEX_PD, OP3_BLENDPS_VpsWpsIb, ESCAPE_3A, imm, off
void twoByteOp8_movx(TwoByteOpcodeID opcode, RegisterID rm, RegisterID reg)
{
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(opcode);
registerModRM(rm, reg);
diff --git a/js/src/jit/x86/SharedIC-x86.cpp b/js/src/jit/x86/SharedIC-x86.cpp
index ee00e1bf0d..b293106ce3 100644
--- a/js/src/jit/x86/SharedIC-x86.cpp
+++ b/js/src/jit/x86/SharedIC-x86.cpp
@@ -226,6 +226,17 @@ ICUnaryArith_Int32::Compiler::generateStubCode(MacroAssembler& masm)
masm.branchTest32(Assembler::Zero, R0.payloadReg(), Imm32(0x7fffffff), &failure);
masm.negl(R0.payloadReg());
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:
MOZ_CRASH("Unexpected op");
}
diff --git a/js/src/json.cpp b/js/src/json.cpp
index d426fc721a..193fed1e0d 100644
--- a/js/src/json.cpp
+++ b/js/src/json.cpp
@@ -277,10 +277,15 @@ PreprocessValue(JSContext* cx, HandleObject holder, KeyType key, MutableHandleVa
RootedString keyStr(cx);
- /* Step 2. */
- if (vp.isObject()) {
+ // Step 2. Modified by BigInt spec 6.1 to check for a toJSON method on the
+ // BigInt prototype when the value is a BigInt.
+ if (vp.isObject() || vp.isBigInt()) {
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))
return false;
diff --git a/js/src/vm/BigIntType.cpp b/js/src/vm/BigIntType.cpp
index 8382c641fa..1f8b061e02 100644
--- a/js/src/vm/BigIntType.cpp
+++ b/js/src/vm/BigIntType.cpp
@@ -185,16 +185,21 @@ BigInt* BigInt::zero(ExclusiveContext* cx) {
return createUninitialized(cx, 0, false);
}
-BigInt* BigInt::one(ExclusiveContext* cx) {
- BigInt* ret = createUninitialized(cx, 1, false);
-
- if (!ret) {
+BigInt* BigInt::createFromDigit(ExclusiveContext* cx, Digit d, bool isNegative) {
+ MOZ_ASSERT(d != 0);
+ BigInt* res = createUninitialized(cx, 1, isNegative);
+ if (!res) {
return nullptr;
}
- ret->setDigit(0, 1);
+ res->setDigit(0, d);
+ return res;
+}
- return ret;
+BigInt* BigInt::one(ExclusiveContext* cx) { return createFromDigit(cx, 1, false); }
+
+BigInt* BigInt::negativeOne(ExclusiveContext* cx) {
+ return createFromDigit(cx, 1, true);
}
BigInt* BigInt::neg(ExclusiveContext* cx, HandleBigInt x) {
@@ -977,21 +982,26 @@ BigInt* BigInt::absoluteAddOne(ExclusiveContext* cx, HandleBigInt x,
return destructivelyTrimHighZeroDigits(cx, result);
}
-// Like the above, but you can specify that the allocated result should have
-// length `resultLength`, which must be at least as large as `x->digitLength()`.
-// The result will be unsigned.
BigInt* BigInt::absoluteSubOne(ExclusiveContext* cx, HandleBigInt x,
- unsigned resultLength) {
+ bool resultNegative) {
MOZ_ASSERT(!x->isZero());
- MOZ_ASSERT(resultLength >= x->digitLength());
- bool resultNegative = false;
- RootedBigInt result(cx,
- createUninitialized(cx, resultLength, resultNegative));
+
+ unsigned length = x->digitLength();
+
+ if (length == 1) {
+ Digit d = x->digit(0);
+ if (d == 1) {
+ // Ignore resultNegative.
+ return zero(cx);
+ }
+ return createFromDigit(cx, d - 1, resultNegative);
+ }
+
+ RootedBigInt result(cx, createUninitialized(cx, length, resultNegative));
if (!result) {
return nullptr;
}
- unsigned length = x->digitLength();
Digit borrow = 1;
for (unsigned i = 0; i < length; i++) {
Digit newBorrow = 0;
@@ -999,13 +1009,36 @@ BigInt* BigInt::absoluteSubOne(ExclusiveContext* cx, HandleBigInt x,
borrow = newBorrow;
}
MOZ_ASSERT(!borrow);
- for (unsigned i = length; i < resultLength; i++) {
- result->setDigit(i, 0);
- }
return destructivelyTrimHighZeroDigits(cx, result);
}
+BigInt* BigInt::inc(ExclusiveContext* cx, HandleBigInt x) {
+ if (x->isZero()) {
+ return one(cx);
+ }
+
+ bool isNegative = x->isNegative();
+ if (isNegative) {
+ return absoluteSubOne(cx, x, isNegative);
+ }
+
+ return absoluteAddOne(cx, x, isNegative);
+}
+
+BigInt* BigInt::dec(ExclusiveContext* cx, HandleBigInt x) {
+ if (x->isZero()) {
+ return negativeOne(cx);
+ }
+
+ bool isNegative = x->isNegative();
+ if (isNegative) {
+ return absoluteAddOne(cx, x, isNegative);
+ }
+
+ return absoluteSubOne(cx, x, isNegative);
+}
+
// Lookup table for the maximum number of bits required per character of a
// base-N string representation of a number. To increase accuracy, the array
// value is the actual value multiplied by 32. To generate this table:
@@ -1569,13 +1602,7 @@ BigInt* BigInt::createFromUint64(ExclusiveContext* cx, uint64_t n) {
return res;
}
- BigInt* res = createUninitialized(cx, 1, isNegative);
- if (!res) {
- return nullptr;
- }
-
- res->setDigit(0, n);
- return res;
+ return createFromDigit(cx, n, isNegative);
}
BigInt* BigInt::createFromInt64(ExclusiveContext* cx, int64_t n) {
@@ -1770,12 +1797,7 @@ BigInt* BigInt::mod(ExclusiveContext* cx, HandleBigInt x, HandleBigInt y) {
return zero(cx);
}
- BigInt* remainder = createUninitialized(cx, 1, x->isNegative());
- if (!remainder) {
- return nullptr;
- }
- remainder->setDigit(0, remainderDigit);
- return remainder;
+ return createFromDigit(cx, remainderDigit, x->isNegative());
} else {
RootedBigInt remainder(cx);
if (!absoluteDivWithBigIntDivisor(cx, x, y, Nothing(), Some(&remainder),
@@ -1930,15 +1952,7 @@ BigInt* BigInt::lshByAbsolute(ExclusiveContext* cx, HandleBigInt x, HandleBigInt
}
BigInt* BigInt::rshByMaximum(ExclusiveContext* cx, bool isNegative) {
- if (isNegative) {
- RootedBigInt negativeOne(cx, createUninitialized(cx, 1, isNegative));
- if (!negativeOne) {
- return nullptr;
- }
- negativeOne->setDigit(0, 1);
- return negativeOne;
- }
- return zero(cx);
+ return isNegative ? negativeOne(cx) : zero(cx);
}
BigInt* BigInt::rshByAbsolute(ExclusiveContext* cx, HandleBigInt x, HandleBigInt y) {
@@ -2049,14 +2063,13 @@ BigInt* BigInt::bitAnd(ExclusiveContext* cx, HandleBigInt x, HandleBigInt y) {
}
if (x->isNegative() && y->isNegative()) {
- int resultLength = std::max(x->digitLength(), y->digitLength()) + 1;
// (-x) & (-y) == ~(x-1) & ~(y-1) == ~((x-1) | (y-1))
// == -(((x-1) | (y-1)) + 1)
- RootedBigInt x1(cx, absoluteSubOne(cx, x, resultLength));
+ RootedBigInt x1(cx, absoluteSubOne(cx, x));
if (!x1) {
return nullptr;
}
- RootedBigInt y1(cx, absoluteSubOne(cx, y, y->digitLength()));
+ RootedBigInt y1(cx, absoluteSubOne(cx, y));
if (!y1) {
return nullptr;
}
@@ -2072,7 +2085,7 @@ BigInt* BigInt::bitAnd(ExclusiveContext* cx, HandleBigInt x, HandleBigInt y) {
HandleBigInt& pos = x->isNegative() ? y : x;
HandleBigInt& neg = x->isNegative() ? x : y;
- RootedBigInt neg1(cx, absoluteSubOne(cx, neg, neg->digitLength()));
+ RootedBigInt neg1(cx, absoluteSubOne(cx, neg));
if (!neg1) {
return nullptr;
}
@@ -2096,27 +2109,24 @@ BigInt* BigInt::bitXor(ExclusiveContext* cx, HandleBigInt x, HandleBigInt y) {
}
if (x->isNegative() && y->isNegative()) {
- int resultLength = std::max(x->digitLength(), y->digitLength());
-
// (-x) ^ (-y) == ~(x-1) ^ ~(y-1) == (x-1) ^ (y-1)
- RootedBigInt x1(cx, absoluteSubOne(cx, x, resultLength));
+ RootedBigInt x1(cx, absoluteSubOne(cx, x));
if (!x1) {
return nullptr;
}
- RootedBigInt y1(cx, absoluteSubOne(cx, y, y->digitLength()));
+ RootedBigInt y1(cx, absoluteSubOne(cx, y));
if (!y1) {
return nullptr;
}
return absoluteXor(cx, x1, y1);
}
MOZ_ASSERT(x->isNegative() != y->isNegative());
- int resultLength = std::max(x->digitLength(), y->digitLength()) + 1;
HandleBigInt& pos = x->isNegative() ? y : x;
HandleBigInt& neg = x->isNegative() ? x : y;
// x ^ (-y) == x ^ ~(y-1) == ~(x ^ (y-1)) == -((x ^ (y-1)) + 1)
- RootedBigInt result(cx, absoluteSubOne(cx, neg, resultLength));
+ RootedBigInt result(cx, absoluteSubOne(cx, neg));
if (!result) {
return nullptr;
}
@@ -2138,7 +2148,6 @@ BigInt* BigInt::bitOr(ExclusiveContext* cx, HandleBigInt x, HandleBigInt y) {
return x;
}
- unsigned resultLength = std::max(x->digitLength(), y->digitLength());
bool resultNegative = x->isNegative() || y->isNegative();
if (!resultNegative) {
@@ -2148,11 +2157,11 @@ BigInt* BigInt::bitOr(ExclusiveContext* cx, HandleBigInt x, HandleBigInt y) {
if (x->isNegative() && y->isNegative()) {
// (-x) | (-y) == ~(x-1) | ~(y-1) == ~((x-1) & (y-1))
// == -(((x-1) & (y-1)) + 1)
- RootedBigInt result(cx, absoluteSubOne(cx, x, resultLength));
+ RootedBigInt result(cx, absoluteSubOne(cx, x));
if (!result) {
return nullptr;
}
- RootedBigInt y1(cx, absoluteSubOne(cx, y, y->digitLength()));
+ RootedBigInt y1(cx, absoluteSubOne(cx, y));
if (!y1) {
return nullptr;
}
@@ -2168,7 +2177,7 @@ BigInt* BigInt::bitOr(ExclusiveContext* cx, HandleBigInt x, HandleBigInt y) {
HandleBigInt& neg = x->isNegative() ? x : y;
// x | (-y) == x | ~(y-1) == ~((y-1) &~ x) == -(((y-1) &~ x) + 1)
- RootedBigInt result(cx, absoluteSubOne(cx, neg, resultLength));
+ RootedBigInt result(cx, absoluteSubOne(cx, neg));
if (!result) {
return nullptr;
}
@@ -2183,7 +2192,7 @@ BigInt* BigInt::bitOr(ExclusiveContext* cx, HandleBigInt x, HandleBigInt y) {
BigInt* BigInt::bitNot(ExclusiveContext* cx, HandleBigInt x) {
if (x->isNegative()) {
// ~(-x) == ~(~(x-1)) == x-1
- return absoluteSubOne(cx, x, x->digitLength());
+ return absoluteSubOne(cx, x);
} else {
// ~x == -x-1 == -(x+1)
bool resultNegative = true;
@@ -2535,6 +2544,30 @@ bool BigInt::neg(ExclusiveContext* cx, HandleValue operand, MutableHandleValue r
return true;
}
+bool BigInt::inc(ExclusiveContext* cx, HandleValue operand, MutableHandleValue res) {
+ MOZ_ASSERT(operand.isBigInt());
+
+ RootedBigInt operandBigInt(cx, operand.toBigInt());
+ BigInt* resBigInt = BigInt::inc(cx, operandBigInt);
+ if (!resBigInt) {
+ return false;
+ }
+ res.setBigInt(resBigInt);
+ return true;
+}
+
+bool BigInt::dec(ExclusiveContext* cx, HandleValue operand, MutableHandleValue res) {
+ MOZ_ASSERT(operand.isBigInt());
+
+ RootedBigInt operandBigInt(cx, operand.toBigInt());
+ BigInt* resBigInt = BigInt::dec(cx, operandBigInt);
+ if (!resBigInt) {
+ return false;
+ }
+ res.setBigInt(resBigInt);
+ return true;
+}
+
bool BigInt::lsh(ExclusiveContext* cx, HandleValue lhs, HandleValue rhs,
MutableHandleValue res) {
if (!ValidBigIntOperands(cx, lhs, rhs)) {
diff --git a/js/src/vm/BigIntType.h b/js/src/vm/BigIntType.h
index ea0317fd9c..693f8bf36c 100644
--- a/js/src/vm/BigIntType.h
+++ b/js/src/vm/BigIntType.h
@@ -97,9 +97,11 @@ class BigInt final : public js::gc::TenuredCell {
static BigInt* createFromDouble(js::ExclusiveContext* cx, double d);
static BigInt* createFromUint64(js::ExclusiveContext* cx, uint64_t n);
static BigInt* createFromInt64(js::ExclusiveContext* cx, int64_t n);
+ static BigInt* createFromDigit(js::ExclusiveContext* cx, Digit d, bool isNegative);
// FIXME: Cache these values.
static BigInt* zero(js::ExclusiveContext* cx);
static BigInt* one(js::ExclusiveContext* cx);
+ static BigInt* negativeOne(js::ExclusiveContext* cx);
static BigInt* copy(js::ExclusiveContext* cx, Handle<BigInt*> x);
static BigInt* add(js::ExclusiveContext* cx, Handle<BigInt*> x, Handle<BigInt*> y);
@@ -109,6 +111,8 @@ class BigInt final : public js::gc::TenuredCell {
static BigInt* mod(js::ExclusiveContext* cx, Handle<BigInt*> x, Handle<BigInt*> y);
static BigInt* pow(js::ExclusiveContext* cx, Handle<BigInt*> x, Handle<BigInt*> y);
static BigInt* neg(js::ExclusiveContext* cx, Handle<BigInt*> x);
+ static BigInt* inc(js::ExclusiveContext* cx, Handle<BigInt*> x);
+ static BigInt* dec(js::ExclusiveContext* cx, Handle<BigInt*> x);
static BigInt* lsh(js::ExclusiveContext* cx, Handle<BigInt*> x, Handle<BigInt*> y);
static BigInt* rsh(js::ExclusiveContext* cx, Handle<BigInt*> x, Handle<BigInt*> y);
static BigInt* bitAnd(js::ExclusiveContext* cx, Handle<BigInt*> x, Handle<BigInt*> y);
@@ -145,6 +149,10 @@ class BigInt final : public js::gc::TenuredCell {
MutableHandle<Value> res);
static bool neg(js::ExclusiveContext* cx, Handle<Value> operand,
MutableHandle<Value> res);
+ static bool inc(js::ExclusiveContext* cx, Handle<Value> operand,
+ MutableHandle<Value> res);
+ static bool dec(js::ExclusiveContext* cx, Handle<Value> operand,
+ MutableHandle<Value> res);
static bool lsh(js::ExclusiveContext* cx, Handle<Value> lhs, Handle<Value> rhs,
MutableHandle<Value> res);
static bool rsh(js::ExclusiveContext* cx, Handle<Value> lhs, Handle<Value> rhs,
@@ -288,7 +296,7 @@ class BigInt final : public js::gc::TenuredCell {
// Return `(|x| - 1) * (resultNegative ? -1 : +1)`, with the precondition that
// |x| != 0.
static BigInt* absoluteSubOne(js::ExclusiveContext* cx, Handle<BigInt*> x,
- unsigned resultLength);
+ bool resultNegative = false);
// Return `a + b`, incrementing `*carry` if the addition overflows.
static inline Digit digitAdd(Digit a, Digit b, Digit* carry) {
diff --git a/js/src/vm/Interpreter-inl.h b/js/src/vm/Interpreter-inl.h
index a48c753f1d..cbf3113b50 100644
--- a/js/src/vm/Interpreter-inl.h
+++ b/js/src/vm/Interpreter-inl.h
@@ -417,6 +417,46 @@ NegOperation(JSContext* cx, HandleScript script, jsbytecode* pc, MutableHandleVa
}
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
ToIdOperation(JSContext* cx, HandleScript script, jsbytecode* pc, HandleValue idval,
MutableHandleValue res)
{
diff --git a/js/src/vm/Interpreter.cpp b/js/src/vm/Interpreter.cpp
index a03fa847f7..f97ba99272 100644
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -4176,6 +4176,34 @@ CASE(JSOP_IS_CONSTRUCTING)
PUSH_MAGIC(JS_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)
{
PUSH_COPY(script->getConst(GET_UINT32_INDEX(REGS.pc)));
diff --git a/js/src/vm/Opcodes.h b/js/src/vm/Opcodes.h
index ff707aac08..ad140ff7bc 100644
--- a/js/src/vm/Opcodes.h
+++ b/js/src/vm/Opcodes.h
@@ -2357,7 +2357,33 @@
* Operands:
* 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.
* Category: Literals
@@ -2365,15 +2391,12 @@
* Operands: uint32_t constIndex
* 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
* a power of two. Use this macro to do so.
*/
#define FOR_EACH_TRAILING_UNUSED_OPCODE(macro) \
- macro(236) \
- macro(237) \
- macro(238) \
macro(239) \
macro(240) \
macro(241) \
diff --git a/js/src/vm/TypedArrayObject.cpp b/js/src/vm/TypedArrayObject.cpp
index fc3d6c5df2..de7ce028eb 100644
--- a/js/src/vm/TypedArrayObject.cpp
+++ b/js/src/vm/TypedArrayObject.cpp
@@ -3233,6 +3233,10 @@ js::IsTypedArrayConstructor(HandleValue v, uint32_t type)
return IsNativeFunction(v, Int32Array::class_constructor);
case Scalar::Uint32:
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:
return IsNativeFunction(v, Float32Array::class_constructor);
case Scalar::Float64: