Bug 1171945: IonMonkey - Part 3: Move BinaryArith from Baseline to Shared stubs, r=jandem

This commit is contained in:
Hannes Verschore 2015-08-19 15:15:49 +02:00
Родитель bfb75abb29
Коммит cf1a076929
4 изменённых файлов: 844 добавлений и 839 удалений

Просмотреть файл

@ -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<ICBinaryArith_Fallback*> 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 <BITOP> Int32 or Int32 <BITOP> 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<DoBinaryArithFallbackFn>(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<CanGC>(cx, lhs, rhs);
if (!result)
return false;
res.setString(result);
return true;
}
typedef bool (*DoConcatStringsFn)(JSContext*, HandleString, HandleString, MutableHandleValue);
static const VMFunction DoConcatStringsInfo = FunctionInfo<DoConcatStringsFn>(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<CanGC>(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<NoGC>(cx, lstr, rstr);
if (!str) {
RootedString nlstr(cx, lstr), nrstr(cx, rstr);
str = ConcatStrings<CanGC>(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<DoConcatStringObjectFn>(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<void*, int32_t(*)(double)>(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
//

Просмотреть файл

@ -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<ICBinaryArith_Fallback>(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<int32_t>(engine_) |
(static_cast<int32_t>(kind) << 1) |
(static_cast<int32_t>(op_) << 17) |
(static_cast<int32_t>(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<ICBinaryArith_Int32>(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<ICBinaryArith_StringConcat>(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<int32_t>(engine_) |
(static_cast<int32_t>(kind) << 1) |
(static_cast<int32_t>(lhsIsString_) << 17);
}
public:
Compiler(JSContext* cx, bool lhsIsString)
: ICStubCompiler(cx, ICStub::BinaryArith_StringObjectConcat, Engine::Baseline),
lhsIsString_(lhsIsString)
{}
ICStub* getStub(ICStubSpace* space) {
return newStub<ICBinaryArith_StringObjectConcat>(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<ICBinaryArith_Double>(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<int32_t>(engine_) |
(static_cast<int32_t>(kind) << 1) |
(static_cast<int32_t>(op_) << 17) |
(static_cast<int32_t>(lhsIsBool_) << 25) |
(static_cast<int32_t>(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<ICBinaryArith_BooleanWithInt32>(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<int32_t>(engine_) |
(static_cast<int32_t>(kind) << 1) |
(static_cast<int32_t>(op) << 17) |
(static_cast<int32_t>(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<ICBinaryArith_DoubleWithInt32>(space, getStubCode(),
lhsIsDouble_);
}
};
};
// UnaryArith
// JSOP_BITNOT
// JSOP_NEG

Просмотреть файл

@ -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<ICBinaryArith_Fallback*> 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 <BITOP> Int32 or Int32 <BITOP> 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<DoBinaryArithFallbackFn>(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<CanGC>(cx, lhs, rhs);
if (!result)
return false;
res.setString(result);
return true;
}
typedef bool (*DoConcatStringsFn)(JSContext*, HandleString, HandleString, MutableHandleValue);
static const VMFunction DoConcatStringsInfo = FunctionInfo<DoConcatStringsFn>(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<CanGC>(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<NoGC>(cx, lstr, rstr);
if (!str) {
RootedString nlstr(cx, lstr), nrstr(cx, rstr);
str = ConcatStrings<CanGC>(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<DoConcatStringObjectFn>(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<void*, int32_t(*)(double)>(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

Просмотреть файл

@ -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<ICBinaryArith_Fallback>(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<int32_t>(engine_) |
(static_cast<int32_t>(kind) << 1) |
(static_cast<int32_t>(op_) << 17) |
(static_cast<int32_t>(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<ICBinaryArith_Int32>(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<ICBinaryArith_StringConcat>(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<int32_t>(engine_) |
(static_cast<int32_t>(kind) << 1) |
(static_cast<int32_t>(lhsIsString_) << 17);
}
public:
Compiler(JSContext* cx, bool lhsIsString)
: ICStubCompiler(cx, ICStub::BinaryArith_StringObjectConcat, Engine::Baseline),
lhsIsString_(lhsIsString)
{}
ICStub* getStub(ICStubSpace* space) {
return newStub<ICBinaryArith_StringObjectConcat>(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<ICBinaryArith_Double>(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<int32_t>(engine_) |
(static_cast<int32_t>(kind) << 1) |
(static_cast<int32_t>(op_) << 17) |
(static_cast<int32_t>(lhsIsBool_) << 25) |
(static_cast<int32_t>(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<ICBinaryArith_BooleanWithInt32>(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<int32_t>(engine_) |
(static_cast<int32_t>(kind) << 1) |
(static_cast<int32_t>(op) << 17) |
(static_cast<int32_t>(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<ICBinaryArith_DoubleWithInt32>(space, getStubCode(),
lhsIsDouble_);
}
};
};
} // namespace jit
} // namespace js