diff options
author | Moonchild <moonchild@palemoon.org> | 2023-09-20 11:56:31 +0200 |
---|---|---|
committer | Moonchild <moonchild@palemoon.org> | 2023-09-20 11:56:31 +0200 |
commit | d2eb2ce9f4760ad2dd5aaa474013d42e5bc9d762 (patch) | |
tree | 9a7bca4b6417d8a11ac6f102e7d3b24fb8008fd7 /js/src/jit | |
parent | bb39e1e38f75d115bd1f0dc82d5b561a2da57d2e (diff) | |
parent | dba1e366014e91a04823e047dc20c8d01d259702 (diff) | |
download | uxp-d2eb2ce9f4760ad2dd5aaa474013d42e5bc9d762.tar.gz |
Merge branch 'master' into simdjs-removal
Diffstat (limited to 'js/src/jit')
27 files changed, 494 insertions, 128 deletions
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"); } |