From 5c766c60ef5c42f2eee3c75da6e172d3f4e95b79 Mon Sep 17 00:00:00 2001 From: Hannes Verschore Date: Wed, 19 Aug 2015 15:15:49 +0200 Subject: [PATCH] Bug 1171945: IonMonkey - Part 3: Move BinaryArith from Baseline to Shared stubs, r=jandem --- js/src/jit/BaselineIC.cpp | 565 ------------------------------------- js/src/jit/BaselineIC.h | 274 ------------------ js/src/jit/SharedIC.cpp | 570 ++++++++++++++++++++++++++++++++++++++ js/src/jit/SharedIC.h | 274 ++++++++++++++++++ 4 files changed, 844 insertions(+), 839 deletions(-) diff --git a/js/src/jit/BaselineIC.cpp b/js/src/jit/BaselineIC.cpp index cdcc778beb84..ae9ad0652d65 100644 --- a/js/src/jit/BaselineIC.cpp +++ b/js/src/jit/BaselineIC.cpp @@ -1861,571 +1861,6 @@ ICToNumber_Fallback::Compiler::generateStubCode(MacroAssembler& masm) return tailCallVM(DoToNumberFallbackInfo, masm); } -// -// BinaryArith_Fallback -// - -static bool -DoBinaryArithFallback(JSContext* cx, BaselineFrame* frame, ICBinaryArith_Fallback* stub_, - HandleValue lhs, HandleValue rhs, MutableHandleValue ret) -{ - // This fallback stub may trigger debug mode toggling. - DebugModeOSRVolatileStub stub(frame, stub_); - - RootedScript script(cx, frame->script()); - jsbytecode* pc = stub->icEntry()->pc(script); - JSOp op = JSOp(*pc); - FallbackICSpew(cx, stub, "BinaryArith(%s,%d,%d)", js_CodeName[op], - int(lhs.isDouble() ? JSVAL_TYPE_DOUBLE : lhs.extractNonDoubleType()), - int(rhs.isDouble() ? JSVAL_TYPE_DOUBLE : rhs.extractNonDoubleType())); - - // Don't pass lhs/rhs directly, we need the original values when - // generating stubs. - RootedValue lhsCopy(cx, lhs); - RootedValue rhsCopy(cx, rhs); - - // Perform the compare operation. - switch(op) { - case JSOP_ADD: - // Do an add. - if (!AddValues(cx, &lhsCopy, &rhsCopy, ret)) - return false; - break; - case JSOP_SUB: - if (!SubValues(cx, &lhsCopy, &rhsCopy, ret)) - return false; - break; - case JSOP_MUL: - if (!MulValues(cx, &lhsCopy, &rhsCopy, ret)) - return false; - break; - case JSOP_DIV: - if (!DivValues(cx, &lhsCopy, &rhsCopy, ret)) - return false; - break; - case JSOP_MOD: - if (!ModValues(cx, &lhsCopy, &rhsCopy, ret)) - return false; - break; - case JSOP_POW: - if (!math_pow_handle(cx, lhsCopy, rhsCopy, ret)) - return false; - break; - case JSOP_BITOR: { - int32_t result; - if (!BitOr(cx, lhs, rhs, &result)) - return false; - ret.setInt32(result); - break; - } - case JSOP_BITXOR: { - int32_t result; - if (!BitXor(cx, lhs, rhs, &result)) - return false; - ret.setInt32(result); - break; - } - case JSOP_BITAND: { - int32_t result; - if (!BitAnd(cx, lhs, rhs, &result)) - return false; - ret.setInt32(result); - break; - } - case JSOP_LSH: { - int32_t result; - if (!BitLsh(cx, lhs, rhs, &result)) - return false; - ret.setInt32(result); - break; - } - case JSOP_RSH: { - int32_t result; - if (!BitRsh(cx, lhs, rhs, &result)) - return false; - ret.setInt32(result); - break; - } - case JSOP_URSH: { - if (!UrshOperation(cx, lhs, rhs, ret)) - return false; - break; - } - default: - MOZ_CRASH("Unhandled baseline arith op"); - } - - // Check if debug mode toggling made the stub invalid. - if (stub.invalid()) - return true; - - if (ret.isDouble()) - stub->setSawDoubleResult(); - - // Check to see if a new stub should be generated. - if (stub->numOptimizedStubs() >= ICBinaryArith_Fallback::MAX_OPTIMIZED_STUBS) { - stub->noteUnoptimizableOperands(); - return true; - } - - // Handle string concat. - if (op == JSOP_ADD) { - if (lhs.isString() && rhs.isString()) { - JitSpew(JitSpew_BaselineIC, " Generating %s(String, String) stub", js_CodeName[op]); - MOZ_ASSERT(ret.isString()); - ICBinaryArith_StringConcat::Compiler compiler(cx); - ICStub* strcatStub = compiler.getStub(compiler.getStubSpace(script)); - if (!strcatStub) - return false; - stub->addNewStub(strcatStub); - return true; - } - - if ((lhs.isString() && rhs.isObject()) || (lhs.isObject() && rhs.isString())) { - JitSpew(JitSpew_BaselineIC, " Generating %s(%s, %s) stub", js_CodeName[op], - lhs.isString() ? "String" : "Object", - lhs.isString() ? "Object" : "String"); - MOZ_ASSERT(ret.isString()); - ICBinaryArith_StringObjectConcat::Compiler compiler(cx, lhs.isString()); - ICStub* strcatStub = compiler.getStub(compiler.getStubSpace(script)); - if (!strcatStub) - return false; - stub->addNewStub(strcatStub); - return true; - } - } - - if (((lhs.isBoolean() && (rhs.isBoolean() || rhs.isInt32())) || - (rhs.isBoolean() && (lhs.isBoolean() || lhs.isInt32()))) && - (op == JSOP_ADD || op == JSOP_SUB || op == JSOP_BITOR || op == JSOP_BITAND || - op == JSOP_BITXOR)) - { - JitSpew(JitSpew_BaselineIC, " Generating %s(%s, %s) stub", js_CodeName[op], - lhs.isBoolean() ? "Boolean" : "Int32", rhs.isBoolean() ? "Boolean" : "Int32"); - ICBinaryArith_BooleanWithInt32::Compiler compiler(cx, op, lhs.isBoolean(), rhs.isBoolean()); - ICStub* arithStub = compiler.getStub(compiler.getStubSpace(script)); - if (!arithStub) - return false; - stub->addNewStub(arithStub); - return true; - } - - // Handle only int32 or double. - if (!lhs.isNumber() || !rhs.isNumber()) { - stub->noteUnoptimizableOperands(); - return true; - } - - MOZ_ASSERT(ret.isNumber()); - - if (lhs.isDouble() || rhs.isDouble() || ret.isDouble()) { - if (!cx->runtime()->jitSupportsFloatingPoint) - return true; - - switch (op) { - case JSOP_ADD: - case JSOP_SUB: - case JSOP_MUL: - case JSOP_DIV: - case JSOP_MOD: { - // Unlink int32 stubs, it's faster to always use the double stub. - stub->unlinkStubsWithKind(cx, ICStub::BinaryArith_Int32); - JitSpew(JitSpew_BaselineIC, " Generating %s(Double, Double) stub", js_CodeName[op]); - - ICBinaryArith_Double::Compiler compiler(cx, op); - ICStub* doubleStub = compiler.getStub(compiler.getStubSpace(script)); - if (!doubleStub) - return false; - stub->addNewStub(doubleStub); - return true; - } - default: - break; - } - } - - if (lhs.isInt32() && rhs.isInt32() && op != JSOP_POW) { - bool allowDouble = ret.isDouble(); - if (allowDouble) - stub->unlinkStubsWithKind(cx, ICStub::BinaryArith_Int32); - JitSpew(JitSpew_BaselineIC, " Generating %s(Int32, Int32%s) stub", js_CodeName[op], - allowDouble ? " => Double" : ""); - ICBinaryArith_Int32::Compiler compilerInt32(cx, op, allowDouble); - ICStub* int32Stub = compilerInt32.getStub(compilerInt32.getStubSpace(script)); - if (!int32Stub) - return false; - stub->addNewStub(int32Stub); - return true; - } - - // Handle Double Int32 or Int32 Double case. - if (((lhs.isDouble() && rhs.isInt32()) || (lhs.isInt32() && rhs.isDouble())) && - ret.isInt32()) - { - switch(op) { - case JSOP_BITOR: - case JSOP_BITXOR: - case JSOP_BITAND: { - JitSpew(JitSpew_BaselineIC, " Generating %s(%s, %s) stub", js_CodeName[op], - lhs.isDouble() ? "Double" : "Int32", - lhs.isDouble() ? "Int32" : "Double"); - ICBinaryArith_DoubleWithInt32::Compiler compiler(cx, op, lhs.isDouble()); - ICStub* optStub = compiler.getStub(compiler.getStubSpace(script)); - if (!optStub) - return false; - stub->addNewStub(optStub); - return true; - } - default: - break; - } - } - - stub->noteUnoptimizableOperands(); - return true; -} - -typedef bool (*DoBinaryArithFallbackFn)(JSContext*, BaselineFrame*, ICBinaryArith_Fallback*, - HandleValue, HandleValue, MutableHandleValue); -static const VMFunction DoBinaryArithFallbackInfo = - FunctionInfo(DoBinaryArithFallback, TailCall, PopValues(2)); - -bool -ICBinaryArith_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); - masm.pushValue(R1); - - // Push arguments. - masm.pushValue(R1); - masm.pushValue(R0); - masm.push(ICStubReg); - pushFramePtr(masm, R0.scratchReg()); - - return tailCallVM(DoBinaryArithFallbackInfo, masm); -} - -static bool -DoConcatStrings(JSContext* cx, HandleString lhs, HandleString rhs, MutableHandleValue res) -{ - JSString* result = ConcatStrings(cx, lhs, rhs); - if (!result) - return false; - - res.setString(result); - return true; -} - -typedef bool (*DoConcatStringsFn)(JSContext*, HandleString, HandleString, MutableHandleValue); -static const VMFunction DoConcatStringsInfo = FunctionInfo(DoConcatStrings, TailCall); - -bool -ICBinaryArith_StringConcat::Compiler::generateStubCode(MacroAssembler& masm) -{ - MOZ_ASSERT(engine_ == Engine::Baseline); - - Label failure; - masm.branchTestString(Assembler::NotEqual, R0, &failure); - masm.branchTestString(Assembler::NotEqual, R1, &failure); - - // Restore the tail call register. - EmitRestoreTailCallReg(masm); - - masm.unboxString(R0, R0.scratchReg()); - masm.unboxString(R1, R1.scratchReg()); - - masm.push(R1.scratchReg()); - masm.push(R0.scratchReg()); - if (!tailCallVM(DoConcatStringsInfo, masm)) - return false; - - // Failure case - jump to next stub - masm.bind(&failure); - EmitStubGuardFailure(masm); - return true; -} - -static JSString* -ConvertObjectToStringForConcat(JSContext* cx, HandleValue obj) -{ - MOZ_ASSERT(obj.isObject()); - RootedValue rootedObj(cx, obj); - if (!ToPrimitive(cx, &rootedObj)) - return nullptr; - return ToString(cx, rootedObj); -} - -static bool -DoConcatStringObject(JSContext* cx, bool lhsIsString, HandleValue lhs, HandleValue rhs, - MutableHandleValue res) -{ - JSString* lstr = nullptr; - JSString* rstr = nullptr; - if (lhsIsString) { - // Convert rhs first. - MOZ_ASSERT(lhs.isString() && rhs.isObject()); - rstr = ConvertObjectToStringForConcat(cx, rhs); - if (!rstr) - return false; - - // lhs is already string. - lstr = lhs.toString(); - } else { - MOZ_ASSERT(rhs.isString() && lhs.isObject()); - // Convert lhs first. - lstr = ConvertObjectToStringForConcat(cx, lhs); - if (!lstr) - return false; - - // rhs is already string. - rstr = rhs.toString(); - } - - JSString* str = ConcatStrings(cx, lstr, rstr); - if (!str) { - RootedString nlstr(cx, lstr), nrstr(cx, rstr); - str = ConcatStrings(cx, nlstr, nrstr); - if (!str) - return false; - } - - // Technically, we need to call TypeScript::MonitorString for this PC, however - // it was called when this stub was attached so it's OK. - - res.setString(str); - return true; -} - -typedef bool (*DoConcatStringObjectFn)(JSContext*, bool lhsIsString, HandleValue, HandleValue, - MutableHandleValue); -static const VMFunction DoConcatStringObjectInfo = - FunctionInfo(DoConcatStringObject, TailCall, PopValues(2)); - -bool -ICBinaryArith_StringObjectConcat::Compiler::generateStubCode(MacroAssembler& masm) -{ - MOZ_ASSERT(engine_ == Engine::Baseline); - - Label failure; - if (lhsIsString_) { - masm.branchTestString(Assembler::NotEqual, R0, &failure); - masm.branchTestObject(Assembler::NotEqual, R1, &failure); - } else { - masm.branchTestObject(Assembler::NotEqual, R0, &failure); - masm.branchTestString(Assembler::NotEqual, R1, &failure); - } - - // Restore the tail call register. - EmitRestoreTailCallReg(masm); - - // Sync for the decompiler. - masm.pushValue(R0); - masm.pushValue(R1); - - // Push arguments. - masm.pushValue(R1); - masm.pushValue(R0); - masm.push(Imm32(lhsIsString_)); - if (!tailCallVM(DoConcatStringObjectInfo, masm)) - return false; - - // Failure case - jump to next stub - masm.bind(&failure); - EmitStubGuardFailure(masm); - return true; -} - -bool -ICBinaryArith_Double::Compiler::generateStubCode(MacroAssembler& masm) -{ - MOZ_ASSERT(engine_ == Engine::Baseline); - - Label failure; - masm.ensureDouble(R0, FloatReg0, &failure); - masm.ensureDouble(R1, FloatReg1, &failure); - - switch (op) { - case JSOP_ADD: - masm.addDouble(FloatReg1, FloatReg0); - break; - case JSOP_SUB: - masm.subDouble(FloatReg1, FloatReg0); - break; - case JSOP_MUL: - masm.mulDouble(FloatReg1, FloatReg0); - break; - case JSOP_DIV: - masm.divDouble(FloatReg1, FloatReg0); - break; - case JSOP_MOD: - masm.setupUnalignedABICall(R0.scratchReg()); - masm.passABIArg(FloatReg0, MoveOp::DOUBLE); - masm.passABIArg(FloatReg1, MoveOp::DOUBLE); - masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, NumberMod), MoveOp::DOUBLE); - MOZ_ASSERT(ReturnDoubleReg == FloatReg0); - break; - default: - MOZ_CRASH("Unexpected op"); - } - - masm.boxDouble(FloatReg0, R0); - EmitReturnFromIC(masm); - - // Failure case - jump to next stub - masm.bind(&failure); - EmitStubGuardFailure(masm); - return true; -} - -bool -ICBinaryArith_BooleanWithInt32::Compiler::generateStubCode(MacroAssembler& masm) -{ - MOZ_ASSERT(engine_ == Engine::Baseline); - - Label failure; - if (lhsIsBool_) - masm.branchTestBoolean(Assembler::NotEqual, R0, &failure); - else - masm.branchTestInt32(Assembler::NotEqual, R0, &failure); - - if (rhsIsBool_) - masm.branchTestBoolean(Assembler::NotEqual, R1, &failure); - else - masm.branchTestInt32(Assembler::NotEqual, R1, &failure); - - Register lhsReg = lhsIsBool_ ? masm.extractBoolean(R0, ExtractTemp0) - : masm.extractInt32(R0, ExtractTemp0); - Register rhsReg = rhsIsBool_ ? masm.extractBoolean(R1, ExtractTemp1) - : masm.extractInt32(R1, ExtractTemp1); - - MOZ_ASSERT(op_ == JSOP_ADD || op_ == JSOP_SUB || - op_ == JSOP_BITOR || op_ == JSOP_BITXOR || op_ == JSOP_BITAND); - - switch(op_) { - case JSOP_ADD: { - Label fixOverflow; - - masm.branchAdd32(Assembler::Overflow, rhsReg, lhsReg, &fixOverflow); - masm.tagValue(JSVAL_TYPE_INT32, lhsReg, R0); - EmitReturnFromIC(masm); - - masm.bind(&fixOverflow); - masm.sub32(rhsReg, lhsReg); - // Proceed to failure below. - break; - } - case JSOP_SUB: { - Label fixOverflow; - - masm.branchSub32(Assembler::Overflow, rhsReg, lhsReg, &fixOverflow); - masm.tagValue(JSVAL_TYPE_INT32, lhsReg, R0); - EmitReturnFromIC(masm); - - masm.bind(&fixOverflow); - masm.add32(rhsReg, lhsReg); - // Proceed to failure below. - break; - } - case JSOP_BITOR: { - masm.orPtr(rhsReg, lhsReg); - masm.tagValue(JSVAL_TYPE_INT32, lhsReg, R0); - EmitReturnFromIC(masm); - break; - } - case JSOP_BITXOR: { - masm.xorPtr(rhsReg, lhsReg); - masm.tagValue(JSVAL_TYPE_INT32, lhsReg, R0); - EmitReturnFromIC(masm); - break; - } - case JSOP_BITAND: { - masm.andPtr(rhsReg, lhsReg); - masm.tagValue(JSVAL_TYPE_INT32, lhsReg, R0); - EmitReturnFromIC(masm); - break; - } - default: - MOZ_CRASH("Unhandled op for BinaryArith_BooleanWithInt32."); - } - - // Failure case - jump to next stub - masm.bind(&failure); - EmitStubGuardFailure(masm); - return true; -} - -bool -ICBinaryArith_DoubleWithInt32::Compiler::generateStubCode(MacroAssembler& masm) -{ - MOZ_ASSERT(engine_ == Engine::Baseline); - MOZ_ASSERT(op == JSOP_BITOR || op == JSOP_BITAND || op == JSOP_BITXOR); - - Label failure; - Register intReg; - Register scratchReg; - if (lhsIsDouble_) { - masm.branchTestDouble(Assembler::NotEqual, R0, &failure); - masm.branchTestInt32(Assembler::NotEqual, R1, &failure); - intReg = masm.extractInt32(R1, ExtractTemp0); - masm.unboxDouble(R0, FloatReg0); - scratchReg = R0.scratchReg(); - } else { - masm.branchTestInt32(Assembler::NotEqual, R0, &failure); - masm.branchTestDouble(Assembler::NotEqual, R1, &failure); - intReg = masm.extractInt32(R0, ExtractTemp0); - masm.unboxDouble(R1, FloatReg0); - scratchReg = R1.scratchReg(); - } - - // Truncate the double to an int32. - { - Label doneTruncate; - Label truncateABICall; - masm.branchTruncateDouble(FloatReg0, scratchReg, &truncateABICall); - masm.jump(&doneTruncate); - - masm.bind(&truncateABICall); - masm.push(intReg); - masm.setupUnalignedABICall(scratchReg); - masm.passABIArg(FloatReg0, MoveOp::DOUBLE); - masm.callWithABI(mozilla::BitwiseCast(JS::ToInt32)); - masm.storeCallResult(scratchReg); - masm.pop(intReg); - - masm.bind(&doneTruncate); - } - - Register intReg2 = scratchReg; - // All handled ops commute, so no need to worry about ordering. - switch(op) { - case JSOP_BITOR: - masm.orPtr(intReg, intReg2); - break; - case JSOP_BITXOR: - masm.xorPtr(intReg, intReg2); - break; - case JSOP_BITAND: - masm.andPtr(intReg, intReg2); - break; - default: - MOZ_CRASH("Unhandled op for BinaryArith_DoubleWithInt32."); - } - masm.tagValue(JSVAL_TYPE_INT32, intReg2, R0); - EmitReturnFromIC(masm); - - // Failure case - jump to next stub - masm.bind(&failure); - EmitStubGuardFailure(masm); - return true; -} - // // UnaryArith_Fallback // diff --git a/js/src/jit/BaselineIC.h b/js/src/jit/BaselineIC.h index 4cbd4de69dc6..04f7b9e51dbb 100644 --- a/js/src/jit/BaselineIC.h +++ b/js/src/jit/BaselineIC.h @@ -1104,280 +1104,6 @@ class ICToNumber_Fallback : public ICFallbackStub }; }; -// BinaryArith -// JSOP_ADD -// JSOP_BITAND, JSOP_BITXOR, JSOP_BITOR -// JSOP_LSH, JSOP_RSH, JSOP_URSH - -class ICBinaryArith_Fallback : public ICFallbackStub -{ - friend class ICStubSpace; - - explicit ICBinaryArith_Fallback(JitCode* stubCode) - : ICFallbackStub(BinaryArith_Fallback, stubCode) - { - extra_ = 0; - } - - static const uint16_t SAW_DOUBLE_RESULT_BIT = 0x1; - static const uint16_t UNOPTIMIZABLE_OPERANDS_BIT = 0x2; - - public: - static const uint32_t MAX_OPTIMIZED_STUBS = 8; - - bool sawDoubleResult() const { - return extra_ & SAW_DOUBLE_RESULT_BIT; - } - void setSawDoubleResult() { - extra_ |= SAW_DOUBLE_RESULT_BIT; - } - bool hadUnoptimizableOperands() const { - return extra_ & UNOPTIMIZABLE_OPERANDS_BIT; - } - void noteUnoptimizableOperands() { - extra_ |= UNOPTIMIZABLE_OPERANDS_BIT; - } - - // Compiler for this stub kind. - class Compiler : public ICStubCompiler { - protected: - bool generateStubCode(MacroAssembler& masm); - - public: - explicit Compiler(JSContext* cx) - : ICStubCompiler(cx, ICStub::BinaryArith_Fallback, Engine::Baseline) {} - - ICStub* getStub(ICStubSpace* space) { - return newStub(space, getStubCode()); - } - }; -}; - -class ICBinaryArith_Int32 : public ICStub -{ - friend class ICStubSpace; - - ICBinaryArith_Int32(JitCode* stubCode, bool allowDouble) - : ICStub(BinaryArith_Int32, stubCode) - { - extra_ = allowDouble; - } - - public: - bool allowDouble() const { - return extra_; - } - - // Compiler for this stub kind. - class Compiler : public ICStubCompiler { - protected: - JSOp op_; - bool allowDouble_; - - bool generateStubCode(MacroAssembler& masm); - - // Stub keys shift-stubs need to encode the kind, the JSOp and if we allow doubles. - virtual int32_t getKey() const { - return static_cast(engine_) | - (static_cast(kind) << 1) | - (static_cast(op_) << 17) | - (static_cast(allowDouble_) << 25); - } - - public: - Compiler(JSContext* cx, JSOp op, bool allowDouble) - : ICStubCompiler(cx, ICStub::BinaryArith_Int32, Engine::Baseline), - op_(op), allowDouble_(allowDouble) {} - - ICStub* getStub(ICStubSpace* space) { - return newStub(space, getStubCode(), allowDouble_); - } - }; -}; - -class ICBinaryArith_StringConcat : public ICStub -{ - friend class ICStubSpace; - - explicit ICBinaryArith_StringConcat(JitCode* stubCode) - : ICStub(BinaryArith_StringConcat, stubCode) - {} - - public: - class Compiler : public ICStubCompiler { - protected: - bool generateStubCode(MacroAssembler& masm); - - public: - explicit Compiler(JSContext* cx) - : ICStubCompiler(cx, ICStub::BinaryArith_StringConcat, Engine::Baseline) - {} - - ICStub* getStub(ICStubSpace* space) { - return newStub(space, getStubCode()); - } - }; -}; - -class ICBinaryArith_StringObjectConcat : public ICStub -{ - friend class ICStubSpace; - - ICBinaryArith_StringObjectConcat(JitCode* stubCode, bool lhsIsString) - : ICStub(BinaryArith_StringObjectConcat, stubCode) - { - extra_ = lhsIsString; - } - - public: - bool lhsIsString() const { - return extra_; - } - - class Compiler : public ICStubCompiler { - protected: - bool lhsIsString_; - bool generateStubCode(MacroAssembler& masm); - - virtual int32_t getKey() const { - return static_cast(engine_) | - (static_cast(kind) << 1) | - (static_cast(lhsIsString_) << 17); - } - - public: - Compiler(JSContext* cx, bool lhsIsString) - : ICStubCompiler(cx, ICStub::BinaryArith_StringObjectConcat, Engine::Baseline), - lhsIsString_(lhsIsString) - {} - - ICStub* getStub(ICStubSpace* space) { - return newStub(space, getStubCode(), - lhsIsString_); - } - }; -}; - -class ICBinaryArith_Double : public ICStub -{ - friend class ICStubSpace; - - explicit ICBinaryArith_Double(JitCode* stubCode) - : ICStub(BinaryArith_Double, stubCode) - {} - - public: - class Compiler : public ICMultiStubCompiler { - protected: - bool generateStubCode(MacroAssembler& masm); - - public: - Compiler(JSContext* cx, JSOp op) - : ICMultiStubCompiler(cx, ICStub::BinaryArith_Double, op, Engine::Baseline) - {} - - ICStub* getStub(ICStubSpace* space) { - return newStub(space, getStubCode()); - } - }; -}; - -class ICBinaryArith_BooleanWithInt32 : public ICStub -{ - friend class ICStubSpace; - - ICBinaryArith_BooleanWithInt32(JitCode* stubCode, bool lhsIsBool, bool rhsIsBool) - : ICStub(BinaryArith_BooleanWithInt32, stubCode) - { - MOZ_ASSERT(lhsIsBool || rhsIsBool); - extra_ = 0; - if (lhsIsBool) - extra_ |= 1; - if (rhsIsBool) - extra_ |= 2; - } - - public: - bool lhsIsBoolean() const { - return extra_ & 1; - } - - bool rhsIsBoolean() const { - return extra_ & 2; - } - - class Compiler : public ICStubCompiler { - protected: - JSOp op_; - bool lhsIsBool_; - bool rhsIsBool_; - bool generateStubCode(MacroAssembler& masm); - - virtual int32_t getKey() const { - return static_cast(engine_) | - (static_cast(kind) << 1) | - (static_cast(op_) << 17) | - (static_cast(lhsIsBool_) << 25) | - (static_cast(rhsIsBool_) << 26); - } - - public: - Compiler(JSContext* cx, JSOp op, bool lhsIsBool, bool rhsIsBool) - : ICStubCompiler(cx, ICStub::BinaryArith_BooleanWithInt32, Engine::Baseline), - op_(op), lhsIsBool_(lhsIsBool), rhsIsBool_(rhsIsBool) - { - MOZ_ASSERT(op_ == JSOP_ADD || op_ == JSOP_SUB || op_ == JSOP_BITOR || - op_ == JSOP_BITAND || op_ == JSOP_BITXOR); - MOZ_ASSERT(lhsIsBool_ || rhsIsBool_); - } - - ICStub* getStub(ICStubSpace* space) { - return newStub(space, getStubCode(), - lhsIsBool_, rhsIsBool_); - } - }; -}; - -class ICBinaryArith_DoubleWithInt32 : public ICStub -{ - friend class ICStubSpace; - - ICBinaryArith_DoubleWithInt32(JitCode* stubCode, bool lhsIsDouble) - : ICStub(BinaryArith_DoubleWithInt32, stubCode) - { - extra_ = lhsIsDouble; - } - - public: - bool lhsIsDouble() const { - return extra_; - } - - class Compiler : public ICMultiStubCompiler { - protected: - bool lhsIsDouble_; - bool generateStubCode(MacroAssembler& masm); - - virtual int32_t getKey() const { - return static_cast(engine_) | - (static_cast(kind) << 1) | - (static_cast(op) << 17) | - (static_cast(lhsIsDouble_) << 25); - } - - public: - Compiler(JSContext* cx, JSOp op, bool lhsIsDouble) - : ICMultiStubCompiler(cx, ICStub::BinaryArith_DoubleWithInt32, op, Engine::Baseline), - lhsIsDouble_(lhsIsDouble) - {} - - ICStub* getStub(ICStubSpace* space) { - return newStub(space, getStubCode(), - lhsIsDouble_); - } - }; -}; - // UnaryArith // JSOP_BITNOT // JSOP_NEG diff --git a/js/src/jit/SharedIC.cpp b/js/src/jit/SharedIC.cpp index 5c2420ee62e9..7b9beff7a025 100644 --- a/js/src/jit/SharedIC.cpp +++ b/js/src/jit/SharedIC.cpp @@ -7,8 +7,10 @@ #include "jit/SharedIC.h" #include "mozilla/SizePrintfMacros.h" +#include "jslibmath.h" #include "jstypes.h" +#include "jit/BaselineDebugModeOSR.h" #include "jit/BaselineIC.h" #include "jit/JitSpewer.h" #include "jit/Linker.h" @@ -17,8 +19,10 @@ # include "jit/PerfSpewer.h" #endif #include "jit/VMFunctions.h" +#include "vm/Interpreter.h" #include "jit/MacroAssembler-inl.h" +#include "vm/Interpreter-inl.h" namespace js { namespace jit { @@ -828,5 +832,571 @@ ICStubCompiler::emitPostWriteBarrierSlot(MacroAssembler& masm, Register obj, Val return true; } +// +// BinaryArith_Fallback +// + +static bool +DoBinaryArithFallback(JSContext* cx, BaselineFrame* frame, ICBinaryArith_Fallback* stub_, + HandleValue lhs, HandleValue rhs, MutableHandleValue ret) +{ + // This fallback stub may trigger debug mode toggling. + DebugModeOSRVolatileStub stub(frame, stub_); + + RootedScript script(cx, frame->script()); + jsbytecode* pc = stub->icEntry()->pc(script); + JSOp op = JSOp(*pc); + FallbackICSpew(cx, stub, "BinaryArith(%s,%d,%d)", js_CodeName[op], + int(lhs.isDouble() ? JSVAL_TYPE_DOUBLE : lhs.extractNonDoubleType()), + int(rhs.isDouble() ? JSVAL_TYPE_DOUBLE : rhs.extractNonDoubleType())); + + // Don't pass lhs/rhs directly, we need the original values when + // generating stubs. + RootedValue lhsCopy(cx, lhs); + RootedValue rhsCopy(cx, rhs); + + // Perform the compare operation. + switch(op) { + case JSOP_ADD: + // Do an add. + if (!AddValues(cx, &lhsCopy, &rhsCopy, ret)) + return false; + break; + case JSOP_SUB: + if (!SubValues(cx, &lhsCopy, &rhsCopy, ret)) + return false; + break; + case JSOP_MUL: + if (!MulValues(cx, &lhsCopy, &rhsCopy, ret)) + return false; + break; + case JSOP_DIV: + if (!DivValues(cx, &lhsCopy, &rhsCopy, ret)) + return false; + break; + case JSOP_MOD: + if (!ModValues(cx, &lhsCopy, &rhsCopy, ret)) + return false; + break; + case JSOP_POW: + if (!math_pow_handle(cx, lhsCopy, rhsCopy, ret)) + return false; + break; + case JSOP_BITOR: { + int32_t result; + if (!BitOr(cx, lhs, rhs, &result)) + return false; + ret.setInt32(result); + break; + } + case JSOP_BITXOR: { + int32_t result; + if (!BitXor(cx, lhs, rhs, &result)) + return false; + ret.setInt32(result); + break; + } + case JSOP_BITAND: { + int32_t result; + if (!BitAnd(cx, lhs, rhs, &result)) + return false; + ret.setInt32(result); + break; + } + case JSOP_LSH: { + int32_t result; + if (!BitLsh(cx, lhs, rhs, &result)) + return false; + ret.setInt32(result); + break; + } + case JSOP_RSH: { + int32_t result; + if (!BitRsh(cx, lhs, rhs, &result)) + return false; + ret.setInt32(result); + break; + } + case JSOP_URSH: { + if (!UrshOperation(cx, lhs, rhs, ret)) + return false; + break; + } + default: + MOZ_CRASH("Unhandled baseline arith op"); + } + + // Check if debug mode toggling made the stub invalid. + if (stub.invalid()) + return true; + + if (ret.isDouble()) + stub->setSawDoubleResult(); + + // Check to see if a new stub should be generated. + if (stub->numOptimizedStubs() >= ICBinaryArith_Fallback::MAX_OPTIMIZED_STUBS) { + stub->noteUnoptimizableOperands(); + return true; + } + + // Handle string concat. + if (op == JSOP_ADD) { + if (lhs.isString() && rhs.isString()) { + JitSpew(JitSpew_BaselineIC, " Generating %s(String, String) stub", js_CodeName[op]); + MOZ_ASSERT(ret.isString()); + ICBinaryArith_StringConcat::Compiler compiler(cx); + ICStub* strcatStub = compiler.getStub(compiler.getStubSpace(script)); + if (!strcatStub) + return false; + stub->addNewStub(strcatStub); + return true; + } + + if ((lhs.isString() && rhs.isObject()) || (lhs.isObject() && rhs.isString())) { + JitSpew(JitSpew_BaselineIC, " Generating %s(%s, %s) stub", js_CodeName[op], + lhs.isString() ? "String" : "Object", + lhs.isString() ? "Object" : "String"); + MOZ_ASSERT(ret.isString()); + ICBinaryArith_StringObjectConcat::Compiler compiler(cx, lhs.isString()); + ICStub* strcatStub = compiler.getStub(compiler.getStubSpace(script)); + if (!strcatStub) + return false; + stub->addNewStub(strcatStub); + return true; + } + } + + if (((lhs.isBoolean() && (rhs.isBoolean() || rhs.isInt32())) || + (rhs.isBoolean() && (lhs.isBoolean() || lhs.isInt32()))) && + (op == JSOP_ADD || op == JSOP_SUB || op == JSOP_BITOR || op == JSOP_BITAND || + op == JSOP_BITXOR)) + { + JitSpew(JitSpew_BaselineIC, " Generating %s(%s, %s) stub", js_CodeName[op], + lhs.isBoolean() ? "Boolean" : "Int32", rhs.isBoolean() ? "Boolean" : "Int32"); + ICBinaryArith_BooleanWithInt32::Compiler compiler(cx, op, lhs.isBoolean(), rhs.isBoolean()); + ICStub* arithStub = compiler.getStub(compiler.getStubSpace(script)); + if (!arithStub) + return false; + stub->addNewStub(arithStub); + return true; + } + + // Handle only int32 or double. + if (!lhs.isNumber() || !rhs.isNumber()) { + stub->noteUnoptimizableOperands(); + return true; + } + + MOZ_ASSERT(ret.isNumber()); + + if (lhs.isDouble() || rhs.isDouble() || ret.isDouble()) { + if (!cx->runtime()->jitSupportsFloatingPoint) + return true; + + switch (op) { + case JSOP_ADD: + case JSOP_SUB: + case JSOP_MUL: + case JSOP_DIV: + case JSOP_MOD: { + // Unlink int32 stubs, it's faster to always use the double stub. + stub->unlinkStubsWithKind(cx, ICStub::BinaryArith_Int32); + JitSpew(JitSpew_BaselineIC, " Generating %s(Double, Double) stub", js_CodeName[op]); + + ICBinaryArith_Double::Compiler compiler(cx, op); + ICStub* doubleStub = compiler.getStub(compiler.getStubSpace(script)); + if (!doubleStub) + return false; + stub->addNewStub(doubleStub); + return true; + } + default: + break; + } + } + + if (lhs.isInt32() && rhs.isInt32() && op != JSOP_POW) { + bool allowDouble = ret.isDouble(); + if (allowDouble) + stub->unlinkStubsWithKind(cx, ICStub::BinaryArith_Int32); + JitSpew(JitSpew_BaselineIC, " Generating %s(Int32, Int32%s) stub", js_CodeName[op], + allowDouble ? " => Double" : ""); + ICBinaryArith_Int32::Compiler compilerInt32(cx, op, allowDouble); + ICStub* int32Stub = compilerInt32.getStub(compilerInt32.getStubSpace(script)); + if (!int32Stub) + return false; + stub->addNewStub(int32Stub); + return true; + } + + // Handle Double Int32 or Int32 Double case. + if (((lhs.isDouble() && rhs.isInt32()) || (lhs.isInt32() && rhs.isDouble())) && + ret.isInt32()) + { + switch(op) { + case JSOP_BITOR: + case JSOP_BITXOR: + case JSOP_BITAND: { + JitSpew(JitSpew_BaselineIC, " Generating %s(%s, %s) stub", js_CodeName[op], + lhs.isDouble() ? "Double" : "Int32", + lhs.isDouble() ? "Int32" : "Double"); + ICBinaryArith_DoubleWithInt32::Compiler compiler(cx, op, lhs.isDouble()); + ICStub* optStub = compiler.getStub(compiler.getStubSpace(script)); + if (!optStub) + return false; + stub->addNewStub(optStub); + return true; + } + default: + break; + } + } + + stub->noteUnoptimizableOperands(); + return true; +} + +typedef bool (*DoBinaryArithFallbackFn)(JSContext*, BaselineFrame*, ICBinaryArith_Fallback*, + HandleValue, HandleValue, MutableHandleValue); +static const VMFunction DoBinaryArithFallbackInfo = + FunctionInfo(DoBinaryArithFallback, TailCall, PopValues(2)); + +bool +ICBinaryArith_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); + masm.pushValue(R1); + + // Push arguments. + masm.pushValue(R1); + masm.pushValue(R0); + masm.push(ICStubReg); + pushFramePtr(masm, R0.scratchReg()); + + return tailCallVM(DoBinaryArithFallbackInfo, masm); +} + +static bool +DoConcatStrings(JSContext* cx, HandleString lhs, HandleString rhs, MutableHandleValue res) +{ + JSString* result = ConcatStrings(cx, lhs, rhs); + if (!result) + return false; + + res.setString(result); + return true; +} + +typedef bool (*DoConcatStringsFn)(JSContext*, HandleString, HandleString, MutableHandleValue); +static const VMFunction DoConcatStringsInfo = FunctionInfo(DoConcatStrings, TailCall); + +bool +ICBinaryArith_StringConcat::Compiler::generateStubCode(MacroAssembler& masm) +{ + MOZ_ASSERT(engine_ == Engine::Baseline); + + Label failure; + masm.branchTestString(Assembler::NotEqual, R0, &failure); + masm.branchTestString(Assembler::NotEqual, R1, &failure); + + // Restore the tail call register. + EmitRestoreTailCallReg(masm); + + masm.unboxString(R0, R0.scratchReg()); + masm.unboxString(R1, R1.scratchReg()); + + masm.push(R1.scratchReg()); + masm.push(R0.scratchReg()); + if (!tailCallVM(DoConcatStringsInfo, masm)) + return false; + + // Failure case - jump to next stub + masm.bind(&failure); + EmitStubGuardFailure(masm); + return true; +} + +static JSString* +ConvertObjectToStringForConcat(JSContext* cx, HandleValue obj) +{ + MOZ_ASSERT(obj.isObject()); + RootedValue rootedObj(cx, obj); + if (!ToPrimitive(cx, &rootedObj)) + return nullptr; + return ToString(cx, rootedObj); +} + +static bool +DoConcatStringObject(JSContext* cx, bool lhsIsString, HandleValue lhs, HandleValue rhs, + MutableHandleValue res) +{ + JSString* lstr = nullptr; + JSString* rstr = nullptr; + if (lhsIsString) { + // Convert rhs first. + MOZ_ASSERT(lhs.isString() && rhs.isObject()); + rstr = ConvertObjectToStringForConcat(cx, rhs); + if (!rstr) + return false; + + // lhs is already string. + lstr = lhs.toString(); + } else { + MOZ_ASSERT(rhs.isString() && lhs.isObject()); + // Convert lhs first. + lstr = ConvertObjectToStringForConcat(cx, lhs); + if (!lstr) + return false; + + // rhs is already string. + rstr = rhs.toString(); + } + + JSString* str = ConcatStrings(cx, lstr, rstr); + if (!str) { + RootedString nlstr(cx, lstr), nrstr(cx, rstr); + str = ConcatStrings(cx, nlstr, nrstr); + if (!str) + return false; + } + + // Technically, we need to call TypeScript::MonitorString for this PC, however + // it was called when this stub was attached so it's OK. + + res.setString(str); + return true; +} + +typedef bool (*DoConcatStringObjectFn)(JSContext*, bool lhsIsString, HandleValue, HandleValue, + MutableHandleValue); +static const VMFunction DoConcatStringObjectInfo = + FunctionInfo(DoConcatStringObject, TailCall, PopValues(2)); + +bool +ICBinaryArith_StringObjectConcat::Compiler::generateStubCode(MacroAssembler& masm) +{ + MOZ_ASSERT(engine_ == Engine::Baseline); + + Label failure; + if (lhsIsString_) { + masm.branchTestString(Assembler::NotEqual, R0, &failure); + masm.branchTestObject(Assembler::NotEqual, R1, &failure); + } else { + masm.branchTestObject(Assembler::NotEqual, R0, &failure); + masm.branchTestString(Assembler::NotEqual, R1, &failure); + } + + // Restore the tail call register. + EmitRestoreTailCallReg(masm); + + // Sync for the decompiler. + masm.pushValue(R0); + masm.pushValue(R1); + + // Push arguments. + masm.pushValue(R1); + masm.pushValue(R0); + masm.push(Imm32(lhsIsString_)); + if (!tailCallVM(DoConcatStringObjectInfo, masm)) + return false; + + // Failure case - jump to next stub + masm.bind(&failure); + EmitStubGuardFailure(masm); + return true; +} + +bool +ICBinaryArith_Double::Compiler::generateStubCode(MacroAssembler& masm) +{ + MOZ_ASSERT(engine_ == Engine::Baseline); + + Label failure; + masm.ensureDouble(R0, FloatReg0, &failure); + masm.ensureDouble(R1, FloatReg1, &failure); + + switch (op) { + case JSOP_ADD: + masm.addDouble(FloatReg1, FloatReg0); + break; + case JSOP_SUB: + masm.subDouble(FloatReg1, FloatReg0); + break; + case JSOP_MUL: + masm.mulDouble(FloatReg1, FloatReg0); + break; + case JSOP_DIV: + masm.divDouble(FloatReg1, FloatReg0); + break; + case JSOP_MOD: + masm.setupUnalignedABICall(R0.scratchReg()); + masm.passABIArg(FloatReg0, MoveOp::DOUBLE); + masm.passABIArg(FloatReg1, MoveOp::DOUBLE); + masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, NumberMod), MoveOp::DOUBLE); + MOZ_ASSERT(ReturnDoubleReg == FloatReg0); + break; + default: + MOZ_CRASH("Unexpected op"); + } + + masm.boxDouble(FloatReg0, R0); + EmitReturnFromIC(masm); + + // Failure case - jump to next stub + masm.bind(&failure); + EmitStubGuardFailure(masm); + return true; +} + +bool +ICBinaryArith_BooleanWithInt32::Compiler::generateStubCode(MacroAssembler& masm) +{ + MOZ_ASSERT(engine_ == Engine::Baseline); + + Label failure; + if (lhsIsBool_) + masm.branchTestBoolean(Assembler::NotEqual, R0, &failure); + else + masm.branchTestInt32(Assembler::NotEqual, R0, &failure); + + if (rhsIsBool_) + masm.branchTestBoolean(Assembler::NotEqual, R1, &failure); + else + masm.branchTestInt32(Assembler::NotEqual, R1, &failure); + + Register lhsReg = lhsIsBool_ ? masm.extractBoolean(R0, ExtractTemp0) + : masm.extractInt32(R0, ExtractTemp0); + Register rhsReg = rhsIsBool_ ? masm.extractBoolean(R1, ExtractTemp1) + : masm.extractInt32(R1, ExtractTemp1); + + MOZ_ASSERT(op_ == JSOP_ADD || op_ == JSOP_SUB || + op_ == JSOP_BITOR || op_ == JSOP_BITXOR || op_ == JSOP_BITAND); + + switch(op_) { + case JSOP_ADD: { + Label fixOverflow; + + masm.branchAdd32(Assembler::Overflow, rhsReg, lhsReg, &fixOverflow); + masm.tagValue(JSVAL_TYPE_INT32, lhsReg, R0); + EmitReturnFromIC(masm); + + masm.bind(&fixOverflow); + masm.sub32(rhsReg, lhsReg); + // Proceed to failure below. + break; + } + case JSOP_SUB: { + Label fixOverflow; + + masm.branchSub32(Assembler::Overflow, rhsReg, lhsReg, &fixOverflow); + masm.tagValue(JSVAL_TYPE_INT32, lhsReg, R0); + EmitReturnFromIC(masm); + + masm.bind(&fixOverflow); + masm.add32(rhsReg, lhsReg); + // Proceed to failure below. + break; + } + case JSOP_BITOR: { + masm.orPtr(rhsReg, lhsReg); + masm.tagValue(JSVAL_TYPE_INT32, lhsReg, R0); + EmitReturnFromIC(masm); + break; + } + case JSOP_BITXOR: { + masm.xorPtr(rhsReg, lhsReg); + masm.tagValue(JSVAL_TYPE_INT32, lhsReg, R0); + EmitReturnFromIC(masm); + break; + } + case JSOP_BITAND: { + masm.andPtr(rhsReg, lhsReg); + masm.tagValue(JSVAL_TYPE_INT32, lhsReg, R0); + EmitReturnFromIC(masm); + break; + } + default: + MOZ_CRASH("Unhandled op for BinaryArith_BooleanWithInt32."); + } + + // Failure case - jump to next stub + masm.bind(&failure); + EmitStubGuardFailure(masm); + return true; +} + +bool +ICBinaryArith_DoubleWithInt32::Compiler::generateStubCode(MacroAssembler& masm) +{ + MOZ_ASSERT(engine_ == Engine::Baseline); + MOZ_ASSERT(op == JSOP_BITOR || op == JSOP_BITAND || op == JSOP_BITXOR); + + Label failure; + Register intReg; + Register scratchReg; + if (lhsIsDouble_) { + masm.branchTestDouble(Assembler::NotEqual, R0, &failure); + masm.branchTestInt32(Assembler::NotEqual, R1, &failure); + intReg = masm.extractInt32(R1, ExtractTemp0); + masm.unboxDouble(R0, FloatReg0); + scratchReg = R0.scratchReg(); + } else { + masm.branchTestInt32(Assembler::NotEqual, R0, &failure); + masm.branchTestDouble(Assembler::NotEqual, R1, &failure); + intReg = masm.extractInt32(R0, ExtractTemp0); + masm.unboxDouble(R1, FloatReg0); + scratchReg = R1.scratchReg(); + } + + // Truncate the double to an int32. + { + Label doneTruncate; + Label truncateABICall; + masm.branchTruncateDouble(FloatReg0, scratchReg, &truncateABICall); + masm.jump(&doneTruncate); + + masm.bind(&truncateABICall); + masm.push(intReg); + masm.setupUnalignedABICall(scratchReg); + masm.passABIArg(FloatReg0, MoveOp::DOUBLE); + masm.callWithABI(mozilla::BitwiseCast(JS::ToInt32)); + masm.storeCallResult(scratchReg); + masm.pop(intReg); + + masm.bind(&doneTruncate); + } + + Register intReg2 = scratchReg; + // All handled ops commute, so no need to worry about ordering. + switch(op) { + case JSOP_BITOR: + masm.orPtr(intReg, intReg2); + break; + case JSOP_BITXOR: + masm.xorPtr(intReg, intReg2); + break; + case JSOP_BITAND: + masm.andPtr(intReg, intReg2); + break; + default: + MOZ_CRASH("Unhandled op for BinaryArith_DoubleWithInt32."); + } + masm.tagValue(JSVAL_TYPE_INT32, intReg2, R0); + EmitReturnFromIC(masm); + + // Failure case - jump to next stub + masm.bind(&failure); + EmitStubGuardFailure(masm); + return true; +} + + } // namespace jit } // namespace js diff --git a/js/src/jit/SharedIC.h b/js/src/jit/SharedIC.h index f6ad3b95ec4a..392c2a961ec2 100644 --- a/js/src/jit/SharedIC.h +++ b/js/src/jit/SharedIC.h @@ -1115,6 +1115,280 @@ class ICMultiStubCompiler : public ICStubCompiler : ICStubCompiler(cx, kind, engine), op(op) {} }; +// BinaryArith +// JSOP_ADD +// JSOP_BITAND, JSOP_BITXOR, JSOP_BITOR +// JSOP_LSH, JSOP_RSH, JSOP_URSH + +class ICBinaryArith_Fallback : public ICFallbackStub +{ + friend class ICStubSpace; + + explicit ICBinaryArith_Fallback(JitCode* stubCode) + : ICFallbackStub(BinaryArith_Fallback, stubCode) + { + extra_ = 0; + } + + static const uint16_t SAW_DOUBLE_RESULT_BIT = 0x1; + static const uint16_t UNOPTIMIZABLE_OPERANDS_BIT = 0x2; + + public: + static const uint32_t MAX_OPTIMIZED_STUBS = 8; + + bool sawDoubleResult() const { + return extra_ & SAW_DOUBLE_RESULT_BIT; + } + void setSawDoubleResult() { + extra_ |= SAW_DOUBLE_RESULT_BIT; + } + bool hadUnoptimizableOperands() const { + return extra_ & UNOPTIMIZABLE_OPERANDS_BIT; + } + void noteUnoptimizableOperands() { + extra_ |= UNOPTIMIZABLE_OPERANDS_BIT; + } + + // Compiler for this stub kind. + class Compiler : public ICStubCompiler { + protected: + bool generateStubCode(MacroAssembler& masm); + + public: + explicit Compiler(JSContext* cx) + : ICStubCompiler(cx, ICStub::BinaryArith_Fallback, Engine::Baseline) {} + + ICStub* getStub(ICStubSpace* space) { + return newStub(space, getStubCode()); + } + }; +}; + +class ICBinaryArith_Int32 : public ICStub +{ + friend class ICStubSpace; + + ICBinaryArith_Int32(JitCode* stubCode, bool allowDouble) + : ICStub(BinaryArith_Int32, stubCode) + { + extra_ = allowDouble; + } + + public: + bool allowDouble() const { + return extra_; + } + + // Compiler for this stub kind. + class Compiler : public ICStubCompiler { + protected: + JSOp op_; + bool allowDouble_; + + bool generateStubCode(MacroAssembler& masm); + + // Stub keys shift-stubs need to encode the kind, the JSOp and if we allow doubles. + virtual int32_t getKey() const { + return static_cast(engine_) | + (static_cast(kind) << 1) | + (static_cast(op_) << 17) | + (static_cast(allowDouble_) << 25); + } + + public: + Compiler(JSContext* cx, JSOp op, bool allowDouble) + : ICStubCompiler(cx, ICStub::BinaryArith_Int32, Engine::Baseline), + op_(op), allowDouble_(allowDouble) {} + + ICStub* getStub(ICStubSpace* space) { + return newStub(space, getStubCode(), allowDouble_); + } + }; +}; + +class ICBinaryArith_StringConcat : public ICStub +{ + friend class ICStubSpace; + + explicit ICBinaryArith_StringConcat(JitCode* stubCode) + : ICStub(BinaryArith_StringConcat, stubCode) + {} + + public: + class Compiler : public ICStubCompiler { + protected: + bool generateStubCode(MacroAssembler& masm); + + public: + explicit Compiler(JSContext* cx) + : ICStubCompiler(cx, ICStub::BinaryArith_StringConcat, Engine::Baseline) + {} + + ICStub* getStub(ICStubSpace* space) { + return newStub(space, getStubCode()); + } + }; +}; + +class ICBinaryArith_StringObjectConcat : public ICStub +{ + friend class ICStubSpace; + + ICBinaryArith_StringObjectConcat(JitCode* stubCode, bool lhsIsString) + : ICStub(BinaryArith_StringObjectConcat, stubCode) + { + extra_ = lhsIsString; + } + + public: + bool lhsIsString() const { + return extra_; + } + + class Compiler : public ICStubCompiler { + protected: + bool lhsIsString_; + bool generateStubCode(MacroAssembler& masm); + + virtual int32_t getKey() const { + return static_cast(engine_) | + (static_cast(kind) << 1) | + (static_cast(lhsIsString_) << 17); + } + + public: + Compiler(JSContext* cx, bool lhsIsString) + : ICStubCompiler(cx, ICStub::BinaryArith_StringObjectConcat, Engine::Baseline), + lhsIsString_(lhsIsString) + {} + + ICStub* getStub(ICStubSpace* space) { + return newStub(space, getStubCode(), + lhsIsString_); + } + }; +}; + +class ICBinaryArith_Double : public ICStub +{ + friend class ICStubSpace; + + explicit ICBinaryArith_Double(JitCode* stubCode) + : ICStub(BinaryArith_Double, stubCode) + {} + + public: + class Compiler : public ICMultiStubCompiler { + protected: + bool generateStubCode(MacroAssembler& masm); + + public: + Compiler(JSContext* cx, JSOp op) + : ICMultiStubCompiler(cx, ICStub::BinaryArith_Double, op, Engine::Baseline) + {} + + ICStub* getStub(ICStubSpace* space) { + return newStub(space, getStubCode()); + } + }; +}; + +class ICBinaryArith_BooleanWithInt32 : public ICStub +{ + friend class ICStubSpace; + + ICBinaryArith_BooleanWithInt32(JitCode* stubCode, bool lhsIsBool, bool rhsIsBool) + : ICStub(BinaryArith_BooleanWithInt32, stubCode) + { + MOZ_ASSERT(lhsIsBool || rhsIsBool); + extra_ = 0; + if (lhsIsBool) + extra_ |= 1; + if (rhsIsBool) + extra_ |= 2; + } + + public: + bool lhsIsBoolean() const { + return extra_ & 1; + } + + bool rhsIsBoolean() const { + return extra_ & 2; + } + + class Compiler : public ICStubCompiler { + protected: + JSOp op_; + bool lhsIsBool_; + bool rhsIsBool_; + bool generateStubCode(MacroAssembler& masm); + + virtual int32_t getKey() const { + return static_cast(engine_) | + (static_cast(kind) << 1) | + (static_cast(op_) << 17) | + (static_cast(lhsIsBool_) << 25) | + (static_cast(rhsIsBool_) << 26); + } + + public: + Compiler(JSContext* cx, JSOp op, bool lhsIsBool, bool rhsIsBool) + : ICStubCompiler(cx, ICStub::BinaryArith_BooleanWithInt32, Engine::Baseline), + op_(op), lhsIsBool_(lhsIsBool), rhsIsBool_(rhsIsBool) + { + MOZ_ASSERT(op_ == JSOP_ADD || op_ == JSOP_SUB || op_ == JSOP_BITOR || + op_ == JSOP_BITAND || op_ == JSOP_BITXOR); + MOZ_ASSERT(lhsIsBool_ || rhsIsBool_); + } + + ICStub* getStub(ICStubSpace* space) { + return newStub(space, getStubCode(), + lhsIsBool_, rhsIsBool_); + } + }; +}; + +class ICBinaryArith_DoubleWithInt32 : public ICStub +{ + friend class ICStubSpace; + + ICBinaryArith_DoubleWithInt32(JitCode* stubCode, bool lhsIsDouble) + : ICStub(BinaryArith_DoubleWithInt32, stubCode) + { + extra_ = lhsIsDouble; + } + + public: + bool lhsIsDouble() const { + return extra_; + } + + class Compiler : public ICMultiStubCompiler { + protected: + bool lhsIsDouble_; + bool generateStubCode(MacroAssembler& masm); + + virtual int32_t getKey() const { + return static_cast(engine_) | + (static_cast(kind) << 1) | + (static_cast(op) << 17) | + (static_cast(lhsIsDouble_) << 25); + } + + public: + Compiler(JSContext* cx, JSOp op, bool lhsIsDouble) + : ICMultiStubCompiler(cx, ICStub::BinaryArith_DoubleWithInt32, op, Engine::Baseline), + lhsIsDouble_(lhsIsDouble) + {} + + ICStub* getStub(ICStubSpace* space) { + return newStub(space, getStubCode(), + lhsIsDouble_); + } + }; +}; + } // namespace jit } // namespace js