[INFER] Floating point register allocation, bug 609898.

This commit is contained in:
Brian Hackett 2010-11-12 06:25:52 -08:00
parent 0c537779e7
commit fb68b676af
19 changed files with 1108 additions and 590 deletions

View File

@ -254,6 +254,7 @@ private:
OP2_SQRTSD_VsdWsd = 0x51,
OP2_XORPD_VpdWpd = 0x57,
OP2_MOVD_VdEd = 0x6E,
OP2_PSRLDQ_Vd = 0x73,
OP2_MOVD_EdVd = 0x7E,
OP2_JCC_rel32 = 0x80,
OP_SETCC = 0x90,
@ -1862,6 +1863,27 @@ public:
m_formatter.twoByteOp(OP2_MOVD_VdEd, (RegisterID)dst, src);
}
/* :FIXME: borrowed from patch in bug 594247 */
void psrldq_rr(XMMRegisterID dest, int shift)
{
js::JaegerSpew(js::JSpew_Insns,
IPFX "pslldq %s, %d\n", MAYBE_PAD,
nameFPReg(dest), shift);
m_formatter.prefix(PRE_SSE_66);
m_formatter.twoByteOp(OP2_PSRLDQ_Vd, (RegisterID)3, (RegisterID)dest);
m_formatter.immediate8(shift);
}
void movd_rr(XMMRegisterID src, RegisterID dst)
{
js::JaegerSpew(js::JSpew_Insns,
IPFX "movd %s, %s\n", MAYBE_PAD,
nameFPReg(src), nameIReg(dst));
m_formatter.prefix(PRE_SSE_66);
m_formatter.twoByteOp(OP2_MOVD_EdVd, (RegisterID)src, dst);
}
#if WTF_CPU_X86_64
void movq_rr(XMMRegisterID src, RegisterID dst)
{

View File

@ -745,6 +745,12 @@ Script::analyze(JSContext *cx)
defineArray, defineCount)) {
return;
}
/* Treat the fallthrough of a branch instruction as a jump target. */
if (type == JOF_JUMP || type == JOF_JUMPX)
nextcode->jumpTarget = true;
else
nextcode->fallthrough = true;
}
}

View File

@ -59,7 +59,10 @@ struct Bytecode
friend class Script;
/* Whether there are any incoming jumps to this instruction. */
bool jumpTarget : 1;
bool jumpTarget : 1;
/* Whether there is fallthrough to this instruction from a non-branching instruction. */
bool fallthrough : 1;
/* Whether this instruction has been analyzed to get its output defines and stack. */
bool analyzed : 1;

View File

@ -936,6 +936,8 @@ GetValueTypeFromTypeFlags(TypeFlags flags)
return JSVAL_TYPE_BOOLEAN;
case TYPE_FLAG_INT32:
return JSVAL_TYPE_INT32;
case (TYPE_FLAG_INT32 | TYPE_FLAG_DOUBLE):
return JSVAL_TYPE_DOUBLE;
case TYPE_FLAG_STRING:
return JSVAL_TYPE_STRING;
case TYPE_FLAG_OBJECT:
@ -2119,6 +2121,8 @@ Script::analyzeTypes(JSContext *cx, Bytecode *code, TypeState &state)
case JSOP_BITNOT:
case JSOP_RSH:
case JSOP_LSH:
case JSOP_URSH:
/* :TODO: Add heuristics for guessing URSH which can overflow. */
code->setFixed(cx, 0, TYPE_INT32);
break;
case JSOP_FALSE:
@ -2138,7 +2142,6 @@ Script::analyzeTypes(JSContext *cx, Bytecode *code, TypeState &state)
code->setFixed(cx, 0, TYPE_BOOLEAN);
break;
case JSOP_DOUBLE:
case JSOP_DIV:
code->setFixed(cx, 0, TYPE_DOUBLE);
break;
case JSOP_STRING:
@ -2600,7 +2603,8 @@ Script::analyzeTypes(JSContext *cx, Bytecode *code, TypeState &state)
case JSOP_SUB:
case JSOP_MUL:
case JSOP_MOD:
case JSOP_URSH:
case JSOP_DIV:
/* :TODO: Add heuristics for guessing when dividing two ints produces a double. */
code->popped(0)->addArith(cx, pool, code, code->pushed(0));
code->popped(1)->addArith(cx, pool, code, code->pushed(0));
break;

View File

@ -3718,7 +3718,7 @@ END_CASE(JSOP_ADD)
double d = d1 OP d2; \
regs.sp--; \
regs.sp[-1].setNumber(d); \
TYPE_MONITOR_RESULT(cx, 0, regs.sp[-1], !regs.sp[-1].isInt32()); \
TYPE_MONITOR_RESULT(cx, 0, regs.sp[-1], !regs.sp[-1].isInt32()); \
JS_END_MACRO
BEGIN_CASE(JSOP_SUB)
@ -3758,7 +3758,7 @@ BEGIN_CASE(JSOP_DIV)
d1 /= d2;
regs.sp[-1].setNumber(d1);
}
TYPE_MONITOR_RESULT(cx, 0, regs.sp[-1], false);
TYPE_MONITOR_RESULT(cx, 0, regs.sp[-1], !regs.sp[-1].isInt32());
}
END_CASE(JSOP_DIV)

View File

@ -191,12 +191,62 @@ static const JSC::MacroAssembler::RegisterID JSParamReg_Argc = JSC::ARMRegiste
m_assembler.pinsrd_rr(hi, fpReg);
} else {
m_assembler.movd_rr(lo, fpReg);
m_assembler.movd_rr(hi, FPRegisters::Temp0);
m_assembler.unpcklps_rr(FPRegisters::Temp0, fpReg);
m_assembler.movd_rr(hi, FPRegisters::ConversionTemp);
m_assembler.unpcklps_rr(FPRegisters::ConversionTemp, fpReg);
}
}
#endif
/*
* Move a register pair which may indicate either an int32 or double into fpreg,
* converting to double in the int32 case.
*/
void moveInt32OrDouble(RegisterID data, RegisterID type, Address address, FPRegisterID fpreg)
{
#ifdef JS_CPU_X86
fastLoadDouble(data, type, fpreg);
Jump notInteger = testInt32(Assembler::NotEqual, type);
convertInt32ToDouble(data, fpreg);
notInteger.linkTo(label(), this);
#else
Jump notInteger = testInt32(Assembler::NotEqual, type);
convertInt32ToDouble(data, fpreg);
Jump fallthrough = jump();
notInteger.linkTo(label(), this);
/* Store the components, then read it back out as a double. */
storeValueFromComponents(type, data, address);
loadDouble(address, fpreg);
fallthrough.linkTo(label(), this);
#endif
}
/*
* Move a memory address which contains either an int32 or double into fpreg,
* converting to double in the int32 case.
*/
void moveInt32OrDouble(Address address, FPRegisterID fpreg)
{
Jump notInteger = testInt32(Assembler::NotEqual, address);
convertInt32ToDouble(payloadOf(address), fpreg);
Jump fallthrough = jump();
notInteger.linkTo(label(), this);
loadDouble(address, fpreg);
fallthrough.linkTo(label(), this);
}
void negateDouble(FPRegisterID fpreg)
{
#if defined JS_CPU_X86 || defined JS_CPU_X64
static const uint64 DoubleNegMask = 0x8000000000000000ULL;
loadDouble(&DoubleNegMask, FPRegisters::ConversionTemp);
xorDouble(FPRegisters::ConversionTemp, fpreg);
#elif defined JS_CPU_ARM
negDouble(fpreg, fpreg);
#endif
}
/*
* Prepares for a stub call.
*/

View File

@ -115,6 +115,8 @@ mjit::Compiler::Compiler(JSContext *cx, JSStackFrame *fp)
,addTraceHints(cx->traceJitEnabled)
#endif
#if defined JS_TYPE_INFERENCE
,hasThisType(false)
,thisType(JSVAL_TYPE_UNKNOWN)
,argumentTypes(ContextAllocPolicy(cx))
,localTypes(ContextAllocPolicy(cx))
#endif
@ -386,8 +388,11 @@ mjit::Compiler::generatePrologue()
JSValueType type = knownLocalType(i);
if (type != JSVAL_TYPE_UNKNOWN) {
JS_ASSERT(!analysis->localHasUseBeforeDef(i));
Address local(JSFrameReg, sizeof(JSStackFrame) + i * sizeof(Value));
masm.storeTypeTag(ImmType(type), local);
/* Doubles must will be written entirely when syncing. */
if (type != JSVAL_TYPE_DOUBLE) {
Address local(JSFrameReg, sizeof(JSStackFrame) + i * sizeof(Value));
masm.storeTypeTag(ImmType(type), local);
}
frame.learnType(frame.getLocal(i), type, false);
}
}
@ -853,14 +858,16 @@ mjit::Compiler::generateMethod()
frame.setInTryBlock(opinfo->inTryBlock);
if (opinfo->jumpTarget || trap) {
if (opinfo->fallthrough)
fixDoubleTypes(Uses(0));
frame.syncAndForgetEverything(opinfo->stackDepth);
opinfo->safePoint = true;
if (!trap)
restoreAnalysisTypes(opinfo->stackDepth);
}
jumpMap[uint32(PC - script->code)] = masm.label();
if (opinfo->jumpTarget)
restoreAnalysisTypes(opinfo->stackDepth);
SPEW_OPCODE();
JS_ASSERT(frame.stackDepth() == opinfo->stackDepth);
@ -922,6 +929,7 @@ mjit::Compiler::generateMethod()
BEGIN_CASE(JSOP_GOTO)
{
/* :XXX: this isn't really necessary if we follow the branch. */
fixDoubleTypes(Uses(0));
frame.syncAndForgetEverything();
Jump j = masm.jump();
if (!jumpAndTrace(j, PC + GET_JUMP_OFFSET(PC)))
@ -931,6 +939,7 @@ mjit::Compiler::generateMethod()
BEGIN_CASE(JSOP_IFEQ)
BEGIN_CASE(JSOP_IFNE)
fixDoubleTypes(Uses(1));
if (!jsop_ifneq(op, PC + GET_JUMP_OFFSET(PC)))
return Compile_Error;
END_CASE(JSOP_IFNE)
@ -938,7 +947,7 @@ mjit::Compiler::generateMethod()
BEGIN_CASE(JSOP_ARGUMENTS)
prepareStubCall(Uses(0));
stubCall(stubs::Arguments);
frame.pushSynced();
frame.pushSynced(knownPushedType(0));
END_CASE(JSOP_ARGUMENTS)
BEGIN_CASE(JSOP_FORARG)
@ -982,8 +991,10 @@ mjit::Compiler::generateMethod()
/* Get jump target, if any. */
jsbytecode *target = NULL;
if (fused != JSOP_NOP)
if (fused != JSOP_NOP) {
target = next + GET_JUMP_OFFSET(next);
fixDoubleTypes(Uses(2));
}
BoolStub stub = NULL;
switch (op) {
@ -1143,7 +1154,7 @@ mjit::Compiler::generateMethod()
prepareStubCall(Uses(0));
masm.move(ImmPtr(atom), Registers::ArgReg1);
stubCall(stubs::DelName);
frame.pushSynced();
frame.pushSynced(knownPushedType(0));
}
END_CASE(JSOP_DELNAME)
@ -1156,7 +1167,7 @@ mjit::Compiler::generateMethod()
masm.move(ImmPtr(atom), Registers::ArgReg1);
stubCall(STRICT_VARIANT(stubs::DelProp));
frame.pop();
frame.pushSynced();
frame.pushSynced(knownPushedType(0));
}
END_CASE(JSOP_DELPROP)
@ -1164,7 +1175,7 @@ mjit::Compiler::generateMethod()
prepareStubCall(Uses(2));
stubCall(STRICT_VARIANT(stubs::DelElem));
frame.popn(2);
frame.pushSynced();
frame.pushSynced(knownPushedType(0));
END_CASE(JSOP_DELELEM)
BEGIN_CASE(JSOP_TYPEOF)
@ -1264,25 +1275,28 @@ mjit::Compiler::generateMethod()
BEGIN_CASE(JSOP_GETTHISPROP)
/* Push thisv onto stack. */
jsop_this();
if (!jsop_getprop(script->getAtom(fullAtomIndex(PC))))
if (!jsop_getprop(script->getAtom(fullAtomIndex(PC)), knownPushedType(0)))
return Compile_Error;
END_CASE(JSOP_GETTHISPROP);
BEGIN_CASE(JSOP_GETARGPROP)
/* Push arg onto stack. */
jsop_getarg(GET_SLOTNO(PC));
if (!jsop_getprop(script->getAtom(fullAtomIndex(&PC[ARGNO_LEN]))))
if (!jsop_getprop(script->getAtom(fullAtomIndex(&PC[ARGNO_LEN])), knownPushedType(0)))
return Compile_Error;
END_CASE(JSOP_GETARGPROP)
BEGIN_CASE(JSOP_GETLOCALPROP)
frame.pushLocal(GET_SLOTNO(PC));
if (!jsop_getprop(script->getAtom(fullAtomIndex(&PC[SLOTNO_LEN]))))
{
uint32 local = GET_SLOTNO(PC);
frame.pushLocal(local, knownLocalType(local));
if (!jsop_getprop(script->getAtom(fullAtomIndex(&PC[SLOTNO_LEN])), knownPushedType(0)))
return Compile_Error;
}
END_CASE(JSOP_GETLOCALPROP)
BEGIN_CASE(JSOP_GETPROP)
if (!jsop_getprop(script->getAtom(fullAtomIndex(PC))))
if (!jsop_getprop(script->getAtom(fullAtomIndex(PC)), knownPushedType(0)))
return Compile_Error;
END_CASE(JSOP_GETPROP)
@ -1305,8 +1319,8 @@ mjit::Compiler::generateMethod()
prepareStubCall(Uses(0));
masm.move(Imm32(fullAtomIndex(PC)), Registers::ArgReg1);
stubCall(stubs::CallName);
frame.pushSynced();
frame.pushSynced();
frame.pushSynced(knownPushedType(0));
frame.pushSynced(knownPushedType(1));
END_CASE(JSOP_CALLNAME)
BEGIN_CASE(JSOP_EVAL)
@ -1372,11 +1386,13 @@ mjit::Compiler::generateMethod()
BEGIN_CASE(JSOP_OR)
BEGIN_CASE(JSOP_AND)
fixDoubleTypes(Uses(0));
if (!jsop_andor(op, PC + GET_JUMP_OFFSET(PC)))
return Compile_Error;
END_CASE(JSOP_AND)
BEGIN_CASE(JSOP_TABLESWITCH)
fixDoubleTypes(Uses(1));
frame.syncAndForgetEverything();
masm.move(ImmPtr(PC), Registers::ArgReg1);
@ -1390,6 +1406,7 @@ mjit::Compiler::generateMethod()
END_CASE(JSOP_TABLESWITCH)
BEGIN_CASE(JSOP_LOOKUPSWITCH)
fixDoubleTypes(Uses(1));
frame.syncAndForgetEverything();
masm.move(ImmPtr(PC), Registers::ArgReg1);
@ -1416,7 +1433,7 @@ mjit::Compiler::generateMethod()
masm.move(Imm32(PC[1]), Registers::ArgReg1);
stubCall(stubs::Iter);
frame.pop();
frame.pushSynced();
frame.pushSynced(JSVAL_TYPE_UNKNOWN);
#else
iter(PC[1]);
#endif
@ -1470,7 +1487,7 @@ mjit::Compiler::generateMethod()
BEGIN_CASE(JSOP_GETLOCAL)
{
uint32 slot = GET_SLOTNO(PC);
frame.pushLocal(slot);
frame.pushLocal(slot, knownPushedType(0));
}
END_CASE(JSOP_GETLOCAL)
@ -1479,7 +1496,7 @@ mjit::Compiler::generateMethod()
jsbytecode *next = &PC[JSOP_SETLOCAL_LENGTH];
bool pop = JSOp(*next) == JSOP_POP && !analysis->jumpTarget(next);
JSValueType type = knownLocalType(GET_SLOTNO(PC));
frame.storeLocal(GET_SLOTNO(PC), pop, type == JSVAL_TYPE_UNKNOWN);
frame.storeLocal(GET_SLOTNO(PC), pop, type);
if (pop) {
frame.pop();
PC += JSOP_SETLOCAL_LENGTH + JSOP_POP_LENGTH;
@ -1653,7 +1670,7 @@ mjit::Compiler::generateMethod()
Address excn(reg, offsetof(JSContext, exception));
frame.freeReg(reg);
frame.push(excn);
frame.push(excn, knownPushedType(0));
}
END_CASE(JSOP_EXCEPTION)
@ -1794,7 +1811,7 @@ mjit::Compiler::generateMethod()
masm.loadPrivate(upvarAddress, reg);
// push ((Value *) reg)[index]
frame.freeReg(reg);
frame.push(Address(reg, index * sizeof(Value)));
frame.push(Address(reg, index * sizeof(Value)), knownPushedType(0));
if (op == JSOP_CALLFCSLOT)
frame.push(UndefinedValue());
}
@ -1804,13 +1821,13 @@ mjit::Compiler::generateMethod()
prepareStubCall(Uses(0));
masm.move(Imm32(GET_ARGNO(PC)), Registers::ArgReg1);
stubCall(stubs::ArgSub);
frame.pushSynced();
frame.pushSynced(knownPushedType(0));
END_CASE(JSOP_ARGSUB)
BEGIN_CASE(JSOP_ARGCNT)
prepareStubCall(Uses(0));
stubCall(stubs::ArgCnt);
frame.pushSynced();
frame.pushSynced(knownPushedType(0));
END_CASE(JSOP_ARGCNT)
BEGIN_CASE(JSOP_DEFLOCALFUN)
@ -1868,7 +1885,7 @@ mjit::Compiler::generateMethod()
prepareStubCall(Uses(0));
masm.move(Imm32(uva->vector[index].asInteger()), Registers::ArgReg1);
stubCall(stubs::GetUpvar);
frame.pushSynced();
frame.pushSynced(knownPushedType(0));
if (op == JSOP_CALLUPVAR)
frame.push(UndefinedValue());
}
@ -1902,7 +1919,7 @@ mjit::Compiler::generateMethod()
END_CASE(JSOP_LEAVEBLOCK)
BEGIN_CASE(JSOP_CALLLOCAL)
frame.pushLocal(GET_SLOTNO(PC));
frame.pushLocal(GET_SLOTNO(PC), knownPushedType(0));
frame.push(UndefinedValue());
END_CASE(JSOP_CALLLOCAL)
@ -2114,7 +2131,7 @@ mjit::Compiler::jsop_getglobal(uint32 index)
RegisterID reg = frame.allocReg();
Address address = masm.objSlotRef(globalObj, reg, slot);
frame.freeReg(reg);
frame.push(address);
frame.push(address, knownPushedType(0));
}
void
@ -2150,7 +2167,7 @@ mjit::Compiler::loadReturnValue(Assembler *masm, FrameEntry *fe)
stubcc.masm.loadValueAsComponents(fe->getValue(), typeReg, dataReg);
} else {
Address rval(frame.addressOf(fe));
if (fe->isTypeKnown()) {
if (fe->isTypeKnown() && !fe->isType(JSVAL_TYPE_DOUBLE)) {
stubcc.masm.loadPayload(rval, dataReg);
stubcc.masm.move(ImmType(fe->getKnownType()), typeReg);
} else {
@ -2369,18 +2386,12 @@ mjit::Compiler::emitUncachedCall(uint32 argc, bool callingNew)
frame.popn(argc + 2);
JSValueType type = knownPushedType(0);
if (type != JSVAL_TYPE_UNKNOWN) {
frame.takeReg(JSReturnReg_Data);
frame.pushTypedPayload(type, JSReturnReg_Data);
} else {
frame.takeReg(JSReturnReg_Type);
frame.takeReg(JSReturnReg_Data);
frame.pushRegs(JSReturnReg_Type, JSReturnReg_Data);
}
frame.takeReg(JSReturnReg_Type);
frame.takeReg(JSReturnReg_Data);
frame.pushRegs(JSReturnReg_Type, JSReturnReg_Data, knownPushedType(0));
stubcc.linkExitDirect(notCompiled, stubcc.masm.label());
stubcc.rejoin(Changes(0));
stubcc.rejoin(Changes(1));
callPatches.append(callPatch);
}
@ -2533,19 +2544,15 @@ mjit::Compiler::inlineCallHelper(uint32 argc, bool callingNew)
frame.popn(argc + 2);
JSValueType type = knownPushedType(0);
if (type != JSVAL_TYPE_UNKNOWN) {
frame.takeReg(JSReturnReg_Data);
frame.pushTypedPayload(type, JSReturnReg_Data);
} else {
frame.takeReg(JSReturnReg_Type);
frame.takeReg(JSReturnReg_Data);
frame.pushRegs(JSReturnReg_Type, JSReturnReg_Data);
}
frame.takeReg(JSReturnReg_Type);
frame.takeReg(JSReturnReg_Data);
frame.pushRegs(JSReturnReg_Type, JSReturnReg_Data, type);
callIC.slowJoinPoint = stubcc.masm.label();
rejoin1.linkTo(callIC.slowJoinPoint, &stubcc.masm);
rejoin2.linkTo(callIC.slowJoinPoint, &stubcc.masm);
stubcc.rejoin(Changes(0));
stubcc.rejoin(Changes(1));
callICs.append(callIC);
callPatches.append(callPatch);
@ -2690,7 +2697,7 @@ mjit::Compiler::jsop_getprop_slow(JSAtom *atom, bool usePropCache)
stubCall(stubs::GetPropNoCache);
}
frame.pop();
frame.pushSynced();
frame.pushSynced(JSVAL_TYPE_UNKNOWN);
}
bool
@ -2700,8 +2707,8 @@ mjit::Compiler::jsop_callprop_slow(JSAtom *atom)
masm.move(ImmPtr(atom), Registers::ArgReg1);
stubCall(stubs::CallProp);
frame.pop();
frame.pushSynced();
frame.pushSynced();
frame.pushSynced(knownPushedType(0));
frame.pushSynced(knownPushedType(1));
return true;
}
@ -2728,12 +2735,12 @@ mjit::Compiler::jsop_length()
}
#if defined JS_POLYIC
return jsop_getprop(cx->runtime->atomState.lengthAtom);
return jsop_getprop(cx->runtime->atomState.lengthAtom, knownPushedType(0));
#else
prepareStubCall(Uses(1));
stubCall(stubs::Length);
frame.pop();
frame.pushSynced();
frame.pushSynced(knownPushedType(0));
return true;
#endif
}
@ -2754,7 +2761,7 @@ mjit::Compiler::passICAddress(BaseICInfo *ic)
}
bool
mjit::Compiler::jsop_getprop(JSAtom *atom, bool doTypeCheck, bool usePropCache)
mjit::Compiler::jsop_getprop(JSAtom *atom, JSValueType knownType, bool doTypeCheck, bool usePropCache)
{
FrameEntry *top = frame.peek(-1);
@ -2873,7 +2880,7 @@ mjit::Compiler::jsop_getprop(JSAtom *atom, bool doTypeCheck, bool usePropCache)
/* GETPROP_INLINE_TYPE_GUARD's validity is asserted above. */
pic.objReg = objReg;
frame.pushRegs(shapeReg, objReg);
frame.pushRegs(shapeReg, objReg, knownType);
stubcc.rejoin(Changes(1));
@ -2952,8 +2959,8 @@ mjit::Compiler::jsop_callprop_generic(JSAtom *atom)
/* Adjust the frame. None of this will generate code. */
frame.pop();
frame.pushRegs(shapeReg, objReg);
frame.pushSynced();
frame.pushRegs(shapeReg, objReg, knownPushedType(0));
frame.pushSynced(knownPushedType(1));
/* Load dslots. */
#if defined JS_NUNBOX32
@ -3026,7 +3033,7 @@ mjit::Compiler::jsop_callprop_str(JSAtom *atom)
frame.pushTypedPayload(JSVAL_TYPE_OBJECT, reg);
/* Get the property. */
if (!jsop_getprop(atom))
if (!jsop_getprop(atom, knownPushedType(0)))
return false;
/* Perform a swap. */
@ -3034,12 +3041,6 @@ mjit::Compiler::jsop_callprop_str(JSAtom *atom)
frame.shift(-3);
frame.shift(-1);
/* 4) Test if the function can take a primitive. */
#ifdef DEBUG
FrameEntry *funFe = frame.peek(-2);
#endif
JS_ASSERT(!funFe->isTypeKnown());
/*
* See bug 584579 - need to forget string type, since wrapping could
* create an object. forgetType() alone is not valid because it cannot be
@ -3132,7 +3133,7 @@ mjit::Compiler::jsop_callprop_obj(JSAtom *atom)
* is the resulting vp[1].
*/
frame.dup();
frame.pushRegs(shapeReg, objReg);
frame.pushRegs(shapeReg, objReg, knownPushedType(0));
frame.shift(-2);
/*
@ -3343,7 +3344,7 @@ mjit::Compiler::jsop_name(JSAtom *atom)
}
pic.fastPathRejoin = masm.label();
frame.pushRegs(pic.shapeReg, pic.objReg);
frame.pushRegs(pic.shapeReg, pic.objReg, knownPushedType(0));
JS_ASSERT(masm.differenceBetween(pic.fastPathStart, dbgJumpOffset) == SCOPENAME_JUMP_OFFSET);
@ -3359,7 +3360,7 @@ mjit::Compiler::jsop_xname(JSAtom *atom)
FrameEntry *fe = frame.peek(-1);
if (fe->isNotType(JSVAL_TYPE_OBJECT)) {
return jsop_getprop(atom);
return jsop_getprop(atom, knownPushedType(0));
}
if (!fe->isTypeKnown()) {
@ -3386,7 +3387,7 @@ mjit::Compiler::jsop_xname(JSAtom *atom)
pic.fastPathRejoin = masm.label();
frame.pop();
frame.pushRegs(pic.shapeReg, pic.objReg);
frame.pushRegs(pic.shapeReg, pic.objReg, knownPushedType(0));
JS_ASSERT(masm.differenceBetween(pic.fastPathStart, dbgJumpOffset) == SCOPENAME_JUMP_OFFSET);
@ -3456,17 +3457,17 @@ mjit::Compiler::jsop_name(JSAtom *atom)
{
prepareStubCall(Uses(0));
stubCall(stubs::Name);
frame.pushSynced();
frame.pushSynced(knownPushedType(0));
}
bool
mjit::Compiler::jsop_xname(JSAtom *atom)
{
return jsop_getprop(atom);
return jsop_getprop(atom, knownPushedType(0));
}
bool
mjit::Compiler::jsop_getprop(JSAtom *atom, bool typecheck, bool usePropCache)
mjit::Compiler::jsop_getprop(JSAtom *atom, JSValueType knownType, bool typecheck, bool usePropCache)
{
jsop_getprop_slow(atom, usePropCache);
return true;
@ -3515,15 +3516,7 @@ void
mjit::Compiler::jsop_getarg(uint32 slot)
{
Address argument(JSFrameReg, JSStackFrame::offsetOfFormalArg(fun, slot));
JSValueType type = knownArgumentType(slot);
if (type != JSVAL_TYPE_UNKNOWN) {
RegisterID dataReg = frame.allocReg();
masm.loadPayload(argument, dataReg);
frame.pushTypedPayload(type, dataReg);
} else {
frame.push(argument);
}
frame.push(argument, knownArgumentType(slot));
}
void
@ -3540,19 +3533,25 @@ void
mjit::Compiler::jsop_this()
{
Address thisvAddr(JSFrameReg, JSStackFrame::offsetOfThis(fun));
frame.push(thisvAddr);
frame.push(thisvAddr, JSVAL_TYPE_UNKNOWN);
/*
* In strict mode code, we don't wrap 'this'.
* In direct-call eval code, we wrapped 'this' before entering the eval.
* In global code, 'this' is always an object.
*/
if (fun && !script->strictModeCode) {
JSValueType type = knownThisType();
if (fun && !script->strictModeCode && type != JSVAL_TYPE_OBJECT) {
Jump notObj = frame.testObject(Assembler::NotEqual, frame.peek(-1));
stubcc.linkExit(notObj, Uses(1));
stubcc.leave();
stubcc.call(stubs::This);
stubcc.rejoin(Changes(1));
}
if (type == JSVAL_TYPE_OBJECT)
frame.learnType(frame.peek(-1), type, false);
}
void
@ -3637,7 +3636,7 @@ mjit::Compiler::jsop_gnameinc(JSOp op, VoidStubAtom stub, uint32 index)
prepareStubCall(Uses(0));
masm.move(ImmPtr(atom), Registers::ArgReg1);
stubCall(stub);
frame.pushSynced();
frame.pushSynced(knownPushedType(0));
#endif
PC += JSOP_GNAMEINC_LENGTH;
@ -3727,7 +3726,7 @@ mjit::Compiler::jsop_nameinc(JSOp op, VoidStubAtom stub, uint32 index)
prepareStubCall(Uses(0));
masm.move(ImmPtr(atom), Registers::ArgReg1);
stubCall(stub);
frame.pushSynced();
frame.pushSynced(knownPushedType(0));
#endif
PC += JSOP_NAMEINC_LENGTH;
@ -3751,7 +3750,7 @@ mjit::Compiler::jsop_propinc(JSOp op, VoidStubAtom stub, uint32 index)
frame.dup();
// OBJ OBJ
if (!jsop_getprop(atom))
if (!jsop_getprop(atom, JSVAL_TYPE_UNKNOWN))
return false;
// OBJ V
@ -3774,7 +3773,7 @@ mjit::Compiler::jsop_propinc(JSOp op, VoidStubAtom stub, uint32 index)
frame.dup();
// OBJ OBJ
if (!jsop_getprop(atom))
if (!jsop_getprop(atom, JSVAL_TYPE_UNKNOWN))
return false;
// OBJ V
@ -3815,7 +3814,7 @@ mjit::Compiler::jsop_propinc(JSOp op, VoidStubAtom stub, uint32 index)
masm.move(ImmPtr(atom), Registers::ArgReg1);
stubCall(stub);
frame.pop();
frame.pushSynced();
frame.pushSynced(knownPushedType(0));
}
PC += JSOP_PROPINC_LENGTH;
@ -3836,7 +3835,7 @@ mjit::Compiler::iter(uintN flags)
masm.move(Imm32(flags), Registers::ArgReg1);
stubCall(stubs::Iter);
frame.pop();
frame.pushSynced();
frame.pushSynced(JSVAL_TYPE_UNKNOWN);
return;
}
@ -4096,7 +4095,7 @@ mjit::Compiler::jsop_eleminc(JSOp op, VoidStub stub)
prepareStubCall(Uses(2));
stubCall(stub);
frame.popn(2);
frame.pushSynced();
frame.pushSynced(knownPushedType(0));
}
void
@ -4104,7 +4103,7 @@ mjit::Compiler::jsop_getgname_slow(uint32 index)
{
prepareStubCall(Uses(0));
stubCall(stubs::GetGlobalName);
frame.pushSynced();
frame.pushSynced(knownPushedType(0));
}
void
@ -4202,7 +4201,7 @@ mjit::Compiler::jsop_getgname(uint32 index)
JS_ASSERT(mic.patchValueOffset == masm.differenceBetween(mic.load, inlineValueLoadLabel));
# endif
frame.pushRegs(treg, dreg);
frame.pushRegs(treg, dreg, knownPushedType(0));
stubcc.rejoin(Changes(1));
mics.append(mic);
@ -4220,7 +4219,7 @@ mjit::Compiler::jsop_setgname_slow(uint32 index)
masm.move(ImmPtr(atom), Registers::ArgReg1);
stubCall(STRICT_VARIANT(stubs::SetGlobalName));
frame.popn(2);
frame.pushSynced();
frame.pushSynced(knownPushedType(0));
}
void
@ -4274,6 +4273,9 @@ mjit::Compiler::jsop_setgname(uint32 index)
RegisterID dataReg = Registers::ReturnReg;
JSValueType typeTag = JSVAL_TYPE_INT32;
if (!fe->isConstant() && fe->isType(JSVAL_TYPE_DOUBLE))
frame.forgetKnownDouble(fe);
mic.u.name.typeConst = fe->isTypeKnown();
mic.u.name.dataConst = fe->isConstant();
@ -4335,7 +4337,7 @@ mjit::Compiler::jsop_setgname(uint32 index)
if (mic.u.name.typeConst)
frame.pushTypedPayload(typeTag, dataReg);
else
frame.pushRegs(typeReg, dataReg);
frame.pushRegs(typeReg, dataReg, knownPushedType(0));
}
stubcc.rejoin(Changes(1));
@ -4352,7 +4354,7 @@ mjit::Compiler::jsop_setelem_slow()
prepareStubCall(Uses(3));
stubCall(STRICT_VARIANT(stubs::SetElem));
frame.popn(3);
frame.pushSynced();
frame.pushSynced(JSVAL_TYPE_UNKNOWN);
}
void
@ -4361,7 +4363,7 @@ mjit::Compiler::jsop_getelem_slow()
prepareStubCall(Uses(2));
stubCall(stubs::GetElem);
frame.popn(2);
frame.pushSynced();
frame.pushSynced(knownPushedType(0));
}
void
@ -4411,7 +4413,7 @@ mjit::Compiler::jsop_instanceof()
/* This is sadly necessary because the error case needs the object. */
frame.dup();
if (!jsop_getprop(cx->runtime->atomState.classPrototypeAtom, false))
if (!jsop_getprop(cx->runtime->atomState.classPrototypeAtom, JSVAL_TYPE_UNKNOWN, false))
return false;
/* Primitive prototypes are invalid. */
@ -4471,7 +4473,7 @@ mjit::Compiler::emitEval(uint32 argc)
masm.move(Imm32(argc), Registers::ArgReg1);
stubCall(stubs::Eval);
frame.popn(argc + 2);
frame.pushSynced();
frame.pushSynced(knownPushedType(0));
}
/*
@ -4618,7 +4620,7 @@ mjit::Compiler::constructThis()
frame.pushTypedPayload(JSVAL_TYPE_OBJECT, calleeReg);
// Get callee.prototype.
if (!jsop_getprop(cx->runtime->atomState.classPrototypeAtom, false, false))
if (!jsop_getprop(cx->runtime->atomState.classPrototypeAtom, JSVAL_TYPE_UNKNOWN, false, false))
return false;
// Reach into the proto Value and grab a register for its data.
@ -4626,10 +4628,13 @@ mjit::Compiler::constructThis()
RegisterID protoReg = frame.ownRegForData(protoFe);
// Now, get the type. If it's not an object, set protoReg to NULL.
Jump isNotObject = frame.testObject(Assembler::NotEqual, protoFe);
stubcc.linkExitDirect(isNotObject, stubcc.masm.label());
stubcc.masm.move(ImmPtr(NULL), protoReg);
stubcc.crossJump(stubcc.masm.jump(), masm.label());
JS_ASSERT_IF(protoFe->isTypeKnown(), protoFe->isType(JSVAL_TYPE_OBJECT));
if (!protoFe->isType(JSVAL_TYPE_OBJECT)) {
Jump isNotObject = frame.testObject(Assembler::NotEqual, protoFe);
stubcc.linkExitDirect(isNotObject, stubcc.masm.label());
stubcc.masm.move(ImmPtr(NULL), protoReg);
stubcc.crossJump(stubcc.masm.jump(), masm.label());
}
// Done with the protoFe.
frame.pop();
@ -4648,19 +4653,52 @@ mjit::Compiler::jsop_callelem_slow()
prepareStubCall(Uses(2));
stubCall(stubs::CallElem);
frame.popn(2);
frame.pushSynced();
frame.pushSynced();
frame.pushSynced(knownPushedType(0));
frame.pushSynced(knownPushedType(1));
}
void
mjit::Compiler::fixDoubleTypes(Uses uses)
{
#ifdef JS_TYPE_INFERENCE
/*
* For any locals or stack values which we know to be integers but are treated as
* doubles by the type inference, convert to double. These will be assumed to be
* doubles at control flow join points. Skip this for the top 'uses' values on the
* stack, which will be popped before branching.
*/
for (uint32 i = 0; i < script->nfixed; i++) {
JSValueType type = knownLocalType(i);
if (type == JSVAL_TYPE_DOUBLE) {
FrameEntry *fe = frame.getLocal(i);
if (!fe->isType(JSVAL_TYPE_DOUBLE))
frame.ensureDouble(fe);
}
}
analyze::Bytecode &opinfo = analysis->getCode(PC);
for (uint32 i = 0; i < opinfo.stackDepth - uses.nuses; i++) {
types::TypeStack *stack = opinfo.inStack;
types::TypeSet *types = analysis->getStackTypes(script->nfixed + i, stack);
JSValueType type = types->getKnownTypeTag(cx, script, isConstructing);
if (type == JSVAL_TYPE_DOUBLE) {
FrameEntry *fe = frame.getLocal(script->nfixed + i);
if (!fe->isType(JSVAL_TYPE_DOUBLE))
frame.ensureDouble(fe);
}
}
#endif
}
void
mjit::Compiler::restoreAnalysisTypes(uint32 stackDepth)
{
#ifdef JS_TYPE_INFERENCE
/* Restore known types of locals. */
/* Restore known types of locals, for join points or after forgetting everything. */
for (uint32 i = 0; i < script->nfixed; i++) {
JSValueType type = knownLocalType(i);
if (type != JSVAL_TYPE_UNKNOWN) {
FrameEntry *fe = frame.getLocal(i);
JS_ASSERT(!fe->isTypeKnown());
frame.learnType(fe, type, false);
}
}
@ -4670,12 +4708,26 @@ mjit::Compiler::restoreAnalysisTypes(uint32 stackDepth)
JSValueType type = types->getKnownTypeTag(cx, script, isConstructing);
if (type != JSVAL_TYPE_UNKNOWN) {
FrameEntry *fe = frame.getLocal(script->nfixed + i);
frame.learnType(fe, type, true);
JS_ASSERT(!fe->isTypeKnown());
frame.learnType(fe, type, type != JSVAL_TYPE_DOUBLE);
}
}
#endif
}
JSValueType
mjit::Compiler::knownThisType()
{
#ifdef JS_TYPE_INFERENCE
if (hasThisType)
return thisType;
hasThisType = true;
thisType = analysis->thisTypes.getKnownTypeTag(cx, script, isConstructing);
return thisType;
#endif
return JSVAL_TYPE_UNKNOWN;
}
JSValueType
mjit::Compiler::knownArgumentType(uint32 arg)
{

View File

@ -291,6 +291,8 @@ class Compiler : public BaseCompiler
bool addTraceHints;
#ifdef JS_TYPE_INFERENCE
bool hasThisType;
JSValueType thisType;
js::Vector<JSValueType, 16> argumentTypes;
js::Vector<JSValueType, 16> localTypes;
#endif
@ -320,7 +322,9 @@ class Compiler : public BaseCompiler
CompileStatus finishThisUp(JITScript **jitp);
/* Analysis helpers. */
void fixDoubleTypes(Uses uses);
void restoreAnalysisTypes(uint32 stackDepth);
JSValueType knownThisType();
JSValueType knownArgumentType(uint32 arg);
JSValueType knownLocalType(uint32 local);
JSValueType knownPushedType(uint32 pushed);
@ -340,7 +344,7 @@ class Compiler : public BaseCompiler
void iterNext();
bool iterMore();
void iterEnd();
MaybeJump loadDouble(FrameEntry *fe, FPRegisterID fpReg);
MaybeJump loadDouble(FrameEntry *fe, FPRegisterID *fpReg, bool *allocated);
#ifdef JS_POLYIC
void passICAddress(BaseICInfo *ic);
#endif
@ -348,6 +352,16 @@ class Compiler : public BaseCompiler
void passMICAddress(MICGenInfo &mic);
#endif
bool constructThis();
void ensureDouble(FrameEntry *fe);
/*
* Ensure fe is an integer, truncating from double if necessary, or jump to
* the slow path per uses.
*/
void ensureInteger(FrameEntry *fe, Uses uses);
/* Convert fe from a double to integer (per ValueToECMAInt32) in place. */
void truncateDoubleToInt32(FrameEntry *fe, Uses uses);
/* Opcode handlers. */
bool jumpAndTrace(Jump j, jsbytecode *target, Jump *slow = NULL);
@ -380,7 +394,7 @@ class Compiler : public BaseCompiler
void jsop_getelem_slow();
void jsop_callelem_slow();
void jsop_unbrand();
bool jsop_getprop(JSAtom *atom, bool typeCheck = true, bool usePropCache = true);
bool jsop_getprop(JSAtom *atom, JSValueType type, bool typeCheck = true, bool usePropCache = true);
bool jsop_length();
bool jsop_setprop(JSAtom *atom, bool usePropCache = true);
void jsop_setprop_slow(JSAtom *atom, bool usePropCache = true);

View File

@ -226,10 +226,7 @@ mjit::Compiler::jsop_binary(JSOp op, VoidStub stub)
prepareStubCall(Uses(2));
stubCall(stub);
frame.popn(2);
if (isStringResult)
frame.pushSyncedType(JSVAL_TYPE_STRING);
else
frame.pushSynced();
frame.pushSynced(isStringResult ? JSVAL_TYPE_STRING : knownPushedType(0));
return;
}
@ -270,28 +267,35 @@ EmitDoubleOp(JSOp op, FPRegisterID fpRight, FPRegisterID fpLeft, Assembler &masm
}
mjit::MaybeJump
mjit::Compiler::loadDouble(FrameEntry *fe, FPRegisterID fpReg)
mjit::Compiler::loadDouble(FrameEntry *fe, FPRegisterID *fpReg, bool *allocated)
{
MaybeJump notNumber;
if (!fe->isConstant() && fe->isType(JSVAL_TYPE_DOUBLE)) {
*fpReg = frame.tempFPRegForData(fe);
*allocated = false;
return notNumber;
}
*fpReg = frame.allocFPReg();
*allocated = true;
if (fe->isConstant()) {
slowLoadConstantDouble(masm, fe, fpReg);
slowLoadConstantDouble(masm, fe, *fpReg);
} else if (!fe->isTypeKnown()) {
frame.tempRegForType(fe);
Jump j = frame.testDouble(Assembler::Equal, fe);
notNumber = frame.testInt32(Assembler::NotEqual, fe);
frame.convertInt32ToDouble(masm, fe, fpReg);
frame.convertInt32ToDouble(masm, fe, *fpReg);
Jump converted = masm.jump();
j.linkTo(masm.label(), &masm);
// CANDIDATE
frame.loadDouble(fe, fpReg, masm);
frame.loadDouble(fe, *fpReg, masm);
converted.linkTo(masm.label(), &masm);
} else if (fe->getKnownType() == JSVAL_TYPE_INT32) {
frame.tempRegForData(fe);
frame.convertInt32ToDouble(masm, fe, fpReg);
} else {
JS_ASSERT(fe->getKnownType() == JSVAL_TYPE_DOUBLE);
frame.loadDouble(fe, fpReg, masm);
JS_ASSERT(fe->isType(JSVAL_TYPE_INT32));
frame.tempRegForData(fe);
frame.convertInt32ToDouble(masm, fe, *fpReg);
}
return notNumber;
@ -304,18 +308,27 @@ mjit::Compiler::loadDouble(FrameEntry *fe, FPRegisterID fpReg)
void
mjit::Compiler::jsop_binary_double(FrameEntry *lhs, FrameEntry *rhs, JSOp op, VoidStub stub)
{
FPRegisterID fpLeft = FPRegisters::First;
FPRegisterID fpRight = FPRegisters::Second;
FPRegisterID fpLeft, fpRight;
bool allocateLeft, allocateRight;
MaybeJump lhsNotNumber = loadDouble(lhs, fpLeft);
MaybeJump lhsNotNumber = loadDouble(lhs, &fpLeft, &allocateLeft);
if (lhsNotNumber.isSet())
stubcc.linkExit(lhsNotNumber.get(), Uses(2));
/* The left register holds the result, and needs to be mutable. */
if (!allocateLeft) {
FPRegisterID res = frame.allocFPReg();
masm.moveDouble(fpLeft, res);
fpLeft = res;
allocateLeft = true;
}
MaybeJump rhsNotNumber;
if (frame.haveSameBacking(lhs, rhs)) {
masm.moveDouble(fpLeft, fpRight);
fpRight = fpLeft;
allocateRight = false;
} else {
rhsNotNumber = loadDouble(rhs, fpRight);
rhsNotNumber = loadDouble(rhs, &fpRight, &allocateRight);
if (rhsNotNumber.isSet())
stubcc.linkExit(rhsNotNumber.get(), Uses(2));
}
@ -323,13 +336,17 @@ mjit::Compiler::jsop_binary_double(FrameEntry *lhs, FrameEntry *rhs, JSOp op, Vo
EmitDoubleOp(op, fpRight, fpLeft, masm);
MaybeJump done;
JSValueType type = knownPushedType(0);
/*
* Try to convert result to integer. Skip this for 1/x or -1/x, as the
* result is unlikely to fit in an int.
* Try to convert result to integer, if the result has unknown or integer type.
* Skip this for 1/x or -1/x, as the result is unlikely to fit in an int.
*/
if (op == JSOP_DIV && !(lhs->isConstant() && lhs->isType(JSVAL_TYPE_INT32) &&
abs(lhs->getValue().toInt32()) == 1)) {
if (op == JSOP_DIV &&
(type == JSVAL_TYPE_INT32 ||
(type == JSVAL_TYPE_UNKNOWN &&
!(lhs->isConstant() && lhs->isType(JSVAL_TYPE_INT32) &&
abs(lhs->getValue().toInt32()) == 1)))) {
RegisterID reg = frame.allocReg();
JumpList isDouble;
masm.branchConvertDoubleToInt32(fpLeft, reg, isDouble, fpRight);
@ -339,10 +356,21 @@ mjit::Compiler::jsop_binary_double(FrameEntry *lhs, FrameEntry *rhs, JSOp op, Vo
frame.freeReg(reg);
done.setJump(masm.jump());
isDouble.linkTo(masm.label(), &masm);
}
masm.storeDouble(fpLeft, frame.addressOf(lhs));
if (type == JSVAL_TYPE_UNKNOWN) {
masm.storeDouble(fpLeft, frame.addressOf(lhs));
} else if (type == JSVAL_TYPE_INT32) {
/*
* Integer conversion failed, but the result is expected to be an integer.
* Call a stub and try harder to convert to int32, or failing that trigger
* recompilation of this script.
*/
JS_ASSERT(op == JSOP_DIV);
stubcc.linkExit(masm.jump(), Uses(2));
}
if (done.isSet())
done.getJump().linkTo(masm.label(), &masm);
@ -352,8 +380,21 @@ mjit::Compiler::jsop_binary_double(FrameEntry *lhs, FrameEntry *rhs, JSOp op, Vo
stubcc.call(stub);
}
if (allocateRight)
frame.freeFPReg(fpRight);
frame.popn(2);
frame.pushNumber(MaybeRegisterID());
if (type == JSVAL_TYPE_UNKNOWN) {
frame.freeFPReg(fpLeft);
frame.pushSynced(type);
} else if (type == JSVAL_TYPE_DOUBLE) {
frame.pushDouble(fpLeft);
} else {
JS_ASSERT(op == JSOP_DIV && type == JSVAL_TYPE_INT32);
frame.freeFPReg(fpLeft);
frame.pushSynced(type);
}
if (lhsNotNumber.isSet() || rhsNotNumber.isSet())
stubcc.rejoin(Changes(1));
@ -368,11 +409,15 @@ mjit::Compiler::jsop_binary_full_simple(FrameEntry *fe, JSOp op, VoidStub stub)
FrameEntry *lhs = frame.peek(-2);
/* Easiest case: known double. Don't bother conversion back yet? */
if (fe->isTypeKnown() && fe->getKnownType() == JSVAL_TYPE_DOUBLE) {
loadDouble(fe, FPRegisters::First);
EmitDoubleOp(op, FPRegisters::First, FPRegisters::First, masm);
if (fe->isType(JSVAL_TYPE_DOUBLE)) {
FPRegisterID fpreg = frame.allocFPReg();
FPRegisterID lhs = frame.tempFPRegForData(fe);
masm.moveDouble(lhs, fpreg);
EmitDoubleOp(op, fpreg, fpreg, masm);
frame.popn(2);
frame.pushNumber(MaybeRegisterID());
JS_ASSERT(knownPushedType(0) == JSVAL_TYPE_DOUBLE);
frame.pushDouble(fpreg);
return;
}
@ -387,12 +432,12 @@ mjit::Compiler::jsop_binary_full_simple(FrameEntry *fe, JSOp op, VoidStub stub)
stubcc.linkExitDirect(notInt, stubcc.masm.label());
notNumber = stubcc.masm.testDouble(Assembler::NotEqual, regs.lhsType.reg());
frame.loadDouble(fe, FPRegisters::First, stubcc.masm);
EmitDoubleOp(op, FPRegisters::First, FPRegisters::First, stubcc.masm);
frame.loadDouble(fe, regs.lhsFP, stubcc.masm);
EmitDoubleOp(op, regs.lhsFP, regs.lhsFP, stubcc.masm);
/* Force the double back to memory. */
Address result = frame.addressOf(lhs);
stubcc.masm.storeDouble(FPRegisters::First, result);
stubcc.masm.storeDouble(regs.lhsFP, result);
/* Load the payload into the result reg so the rejoin is safe. */
stubcc.masm.loadPayload(result, regs.result);
@ -432,17 +477,17 @@ mjit::Compiler::jsop_binary_full_simple(FrameEntry *fe, JSOp op, VoidStub stub)
{
if (regs.lhsNeedsRemat) {
Address address = masm.payloadOf(frame.addressForDataRemat(lhs));
stubcc.masm.convertInt32ToDouble(address, FPRegisters::First);
stubcc.masm.convertInt32ToDouble(address, regs.lhsFP);
} else if (!lhs->isConstant()) {
stubcc.masm.convertInt32ToDouble(regs.lhsData.reg(), FPRegisters::First);
stubcc.masm.convertInt32ToDouble(regs.lhsData.reg(), regs.lhsFP);
} else {
slowLoadConstantDouble(stubcc.masm, lhs, FPRegisters::First);
slowLoadConstantDouble(stubcc.masm, lhs, regs.lhsFP);
}
EmitDoubleOp(op, FPRegisters::First, FPRegisters::First, stubcc.masm);
EmitDoubleOp(op, regs.lhsFP, regs.lhsFP, stubcc.masm);
Address address = frame.addressOf(lhs);
stubcc.masm.storeDouble(FPRegisters::First, address);
stubcc.masm.storeDouble(regs.lhsFP, address);
stubcc.masm.loadPayload(address, regs.result);
overflowDone = stubcc.masm.jump();
@ -462,11 +507,13 @@ mjit::Compiler::jsop_binary_full_simple(FrameEntry *fe, JSOp op, VoidStub stub)
frame.popn(2);
JSValueType type = knownPushedType(0);
if (type != JSVAL_TYPE_UNKNOWN)
if (type == JSVAL_TYPE_INT32)
frame.pushTypedPayload(type, regs.result);
else
frame.pushNumber(regs.result, true);
frame.freeFPReg(regs.lhsFP);
/* Merge back OOL double paths. */
if (doublePathDone.isSet())
stubcc.linkRejoin(doublePathDone.get());
@ -527,9 +574,6 @@ mjit::Compiler::jsop_binary_full(FrameEntry *lhs, FrameEntry *rhs, JSOp op, Void
JS_ASSERT_IF(lhs->isTypeKnown(), lhs->getKnownType() == JSVAL_TYPE_INT32);
JS_ASSERT_IF(rhs->isTypeKnown(), rhs->getKnownType() == JSVAL_TYPE_INT32);
FPRegisterID fpLeft = FPRegisters::First;
FPRegisterID fpRight = FPRegisters::Second;
MaybeJump lhsNotDouble, rhsNotNumber, lhsUnknownDone;
if (!lhs->isTypeKnown())
emitLeftDoublePath(lhs, rhs, regs, lhsNotDouble, rhsNotNumber, lhsUnknownDone);
@ -546,11 +590,11 @@ mjit::Compiler::jsop_binary_full(FrameEntry *lhs, FrameEntry *rhs, JSOp op, Void
lhsUnknownDone.get().linkTo(stubcc.masm.label(), &stubcc.masm);
/* Perform the double operation. */
EmitDoubleOp(op, fpRight, fpLeft, stubcc.masm);
EmitDoubleOp(op, regs.rhsFP, regs.lhsFP, stubcc.masm);
/* Force the double back to memory. */
Address result = frame.addressOf(lhs);
stubcc.masm.storeDouble(fpLeft, result);
stubcc.masm.storeDouble(regs.lhsFP, result);
/* Load the payload into the result reg so the rejoin is safe. */
stubcc.masm.loadPayload(result, regs.result);
@ -667,26 +711,26 @@ mjit::Compiler::jsop_binary_full(FrameEntry *lhs, FrameEntry *rhs, JSOp op, Void
{
if (regs.lhsNeedsRemat) {
Address address = masm.payloadOf(frame.addressForDataRemat(lhs));
stubcc.masm.convertInt32ToDouble(address, fpLeft);
stubcc.masm.convertInt32ToDouble(address, regs.lhsFP);
} else if (!lhs->isConstant()) {
stubcc.masm.convertInt32ToDouble(regs.lhsData.reg(), fpLeft);
stubcc.masm.convertInt32ToDouble(regs.lhsData.reg(), regs.lhsFP);
} else {
slowLoadConstantDouble(stubcc.masm, lhs, fpLeft);
slowLoadConstantDouble(stubcc.masm, lhs, regs.lhsFP);
}
if (regs.rhsNeedsRemat) {
Address address = masm.payloadOf(frame.addressForDataRemat(rhs));
stubcc.masm.convertInt32ToDouble(address, fpRight);
stubcc.masm.convertInt32ToDouble(address, regs.rhsFP);
} else if (!rhs->isConstant()) {
stubcc.masm.convertInt32ToDouble(regs.rhsData.reg(), fpRight);
stubcc.masm.convertInt32ToDouble(regs.rhsData.reg(), regs.rhsFP);
} else {
slowLoadConstantDouble(stubcc.masm, rhs, fpRight);
slowLoadConstantDouble(stubcc.masm, rhs, regs.rhsFP);
}
EmitDoubleOp(op, fpRight, fpLeft, stubcc.masm);
EmitDoubleOp(op, regs.rhsFP, regs.lhsFP, stubcc.masm);
Address address = frame.addressOf(lhs);
stubcc.masm.storeDouble(fpLeft, address);
stubcc.masm.storeDouble(regs.lhsFP, address);
stubcc.masm.loadPayload(address, regs.result);
overflowDone = stubcc.masm.jump();
@ -714,11 +758,14 @@ mjit::Compiler::jsop_binary_full(FrameEntry *lhs, FrameEntry *rhs, JSOp op, Void
frame.popn(2);
JSValueType type = knownPushedType(0);
if (type != JSVAL_TYPE_UNKNOWN)
if (type == JSVAL_TYPE_INT32)
frame.pushTypedPayload(type, regs.result);
else
frame.pushNumber(regs.result, true);
frame.freeFPReg(regs.lhsFP);
frame.freeFPReg(regs.rhsFP);
/* Merge back OOL double paths. */
if (doublePathDone.isSet())
stubcc.linkRejoin(doublePathDone.get());
@ -729,8 +776,6 @@ mjit::Compiler::jsop_binary_full(FrameEntry *lhs, FrameEntry *rhs, JSOp op, Void
stubcc.rejoin(Changes(1));
}
static const uint64 DoubleNegMask = 0x8000000000000000ULL;
void
mjit::Compiler::jsop_neg()
{
@ -740,12 +785,23 @@ mjit::Compiler::jsop_neg()
prepareStubCall(Uses(1));
stubCall(stubs::Neg);
frame.pop();
frame.pushSynced();
frame.pushSynced(knownPushedType(0));
return;
}
JS_ASSERT(!fe->isConstant());
if (fe->isType(JSVAL_TYPE_DOUBLE)) {
FPRegisterID fpreg = frame.tempFPRegForData(fe);
FPRegisterID res = frame.allocFPReg();
masm.moveDouble(fpreg, res);
masm.negateDouble(res);
frame.pop();
frame.pushDouble(res);
return;
}
/* Load type information into register. */
MaybeRegisterID feTypeReg;
if (!fe->isTypeKnown() && !frame.shouldAvoidTypeRemat(fe)) {
@ -764,17 +820,13 @@ mjit::Compiler::jsop_neg()
{
maybeJumpIfNotDouble(masm, jmpNotDbl, fe, feTypeReg);
FPRegisterID fpreg = frame.copyEntryIntoFPReg(fe, FPRegisters::First);
#if defined JS_CPU_X86 || defined JS_CPU_X64
masm.loadDouble(&DoubleNegMask, FPRegisters::Second);
masm.xorDouble(FPRegisters::Second, fpreg);
#elif defined JS_CPU_ARM
masm.negDouble(fpreg, fpreg);
#endif
FPRegisterID fpreg = frame.allocFPReg();
frame.loadDouble(fe, fpreg, masm);
masm.negateDouble(fpreg);
/* Overwrite pushed frame's memory (before push). */
masm.storeDouble(fpreg, frame.addressOf(fe));
frame.freeFPReg(fpreg);
}
/* Try an integer path (out-of-line). */
@ -808,7 +860,7 @@ mjit::Compiler::jsop_neg()
stubcc.call(stubs::Neg);
frame.pop();
frame.pushSynced();
frame.pushSynced(knownPushedType(0));
/* Link jumps. */
if (jmpNotDbl.isSet())
@ -839,7 +891,7 @@ mjit::Compiler::jsop_mod()
prepareStubCall(Uses(2));
stubCall(stubs::Mod);
frame.popn(2);
frame.pushSynced();
frame.pushSynced(knownPushedType(0));
return;
}
@ -946,7 +998,7 @@ mjit::Compiler::jsop_mod()
frame.popn(2);
JSValueType type = knownPushedType(0);
if (type != JSVAL_TYPE_UNKNOWN)
if (type == JSVAL_TYPE_INT32)
frame.pushTypedPayload(type, X86Registers::edx);
else
frame.pushNumber(X86Registers::edx);
@ -1172,9 +1224,6 @@ mjit::Compiler::emitLeftDoublePath(FrameEntry *lhs, FrameEntry *rhs, FrameState:
MaybeJump &lhsNotDouble, MaybeJump &rhsNotNumber,
MaybeJump &lhsUnknownDone)
{
FPRegisterID fpLeft = FPRegisters::First;
FPRegisterID fpRight = FPRegisters::Second;
/* If the LHS is not a 32-bit integer, take OOL path. */
Jump lhsNotInt32 = masm.testInt32(Assembler::NotEqual, regs.lhsType.reg());
stubcc.linkExitDirect(lhsNotInt32, stubcc.masm.label());
@ -1191,9 +1240,9 @@ mjit::Compiler::emitLeftDoublePath(FrameEntry *lhs, FrameEntry *rhs, FrameState:
/* If RHS is constant, convert now. */
if (rhs->isConstant())
slowLoadConstantDouble(stubcc.masm, rhs, fpRight);
slowLoadConstantDouble(stubcc.masm, rhs, regs.rhsFP);
else
stubcc.masm.convertInt32ToDouble(regs.rhsData.reg(), fpRight);
stubcc.masm.convertInt32ToDouble(regs.rhsData.reg(), regs.rhsFP);
if (!rhs->isTypeKnown()) {
/* Jump past double load, bind double type check. */
@ -1202,14 +1251,14 @@ mjit::Compiler::emitLeftDoublePath(FrameEntry *lhs, FrameEntry *rhs, FrameState:
/* Load the double. */
frame.loadDouble(regs.rhsType.reg(), regs.rhsData.reg(),
rhs, fpRight, stubcc.masm);
rhs, regs.rhsFP, stubcc.masm);
converted.linkTo(stubcc.masm.label(), &stubcc.masm);
}
/* Load the LHS. */
frame.loadDouble(regs.lhsType.reg(), regs.lhsData.reg(),
lhs, fpLeft, stubcc.masm);
lhs, regs.lhsFP, stubcc.masm);
lhsUnknownDone = stubcc.masm.jump();
}
@ -1220,9 +1269,6 @@ void
mjit::Compiler::emitRightDoublePath(FrameEntry *lhs, FrameEntry *rhs, FrameState::BinaryAlloc &regs,
MaybeJump &rhsNotNumber2)
{
FPRegisterID fpLeft = FPRegisters::First;
FPRegisterID fpRight = FPRegisters::Second;
/* If the RHS is not a double, take OOL path. */
Jump notInt32 = masm.testInt32(Assembler::NotEqual, regs.rhsType.reg());
stubcc.linkExitDirect(notInt32, stubcc.masm.label());
@ -1232,13 +1278,13 @@ mjit::Compiler::emitRightDoublePath(FrameEntry *lhs, FrameEntry *rhs, FrameState
/* We know LHS is an integer. */
if (lhs->isConstant())
slowLoadConstantDouble(stubcc.masm, lhs, fpLeft);
slowLoadConstantDouble(stubcc.masm, lhs, regs.lhsFP);
else
stubcc.masm.convertInt32ToDouble(regs.lhsData.reg(), fpLeft);
stubcc.masm.convertInt32ToDouble(regs.lhsData.reg(), regs.lhsFP);
/* Load the RHS. */
frame.loadDouble(regs.rhsType.reg(), regs.rhsData.reg(),
rhs, fpRight, stubcc.masm);
rhs, regs.rhsFP, stubcc.masm);
}
static inline Assembler::DoubleCondition
@ -1274,13 +1320,18 @@ mjit::Compiler::jsop_relational_double(JSOp op, BoolStub stub, jsbytecode *targe
FrameEntry *rhs = frame.peek(-1);
FrameEntry *lhs = frame.peek(-2);
FPRegisterID fpLeft = FPRegisters::First;
FPRegisterID fpRight = FPRegisters::Second;
JS_ASSERT_IF(!target, fused != JSOP_IFEQ);
MaybeJump lhsNotNumber = loadDouble(lhs, fpLeft);
MaybeJump rhsNotNumber = loadDouble(rhs, fpRight);
FPRegisterID fpLeft, fpRight;
bool allocateLeft, allocateRight;
MaybeJump lhsNotNumber = loadDouble(lhs, &fpLeft, &allocateLeft);
if (!allocateLeft)
frame.pinFPReg(fpLeft);
MaybeJump rhsNotNumber = loadDouble(rhs, &fpRight, &allocateRight);
if (!allocateLeft)
frame.unpinFPReg(fpLeft);
Assembler::DoubleCondition dblCond = DoubleCondForOp(op, fused);
@ -1337,7 +1388,13 @@ mjit::Compiler::jsop_relational_double(JSOp op, BoolStub stub, jsbytecode *targe
frame.pushTypedPayload(JSVAL_TYPE_BOOLEAN, reg);
stubcc.rejoin(Changes(1));
if (allocateLeft)
frame.freeFPReg(fpLeft);
if (allocateRight)
frame.freeFPReg(fpRight);
}
return true;
}
@ -1366,9 +1423,6 @@ mjit::Compiler::jsop_relational_full(JSOp op, BoolStub stub, jsbytecode *target,
FrameState::BinaryAlloc regs;
frame.allocForBinary(lhs, rhs, op, regs, !target);
FPRegisterID fpLeft = FPRegisters::First;
FPRegisterID fpRight = FPRegisters::Second;
MaybeJump lhsNotDouble, rhsNotNumber, lhsUnknownDone;
if (!lhs->isTypeKnown())
emitLeftDoublePath(lhs, rhs, regs, lhsNotDouble, rhsNotNumber, lhsUnknownDone);
@ -1434,7 +1488,7 @@ mjit::Compiler::jsop_relational_full(JSOp op, BoolStub stub, jsbytecode *target,
if (lhsUnknownDone.isSet())
lhsUnknownDone.get().linkTo(stubcc.masm.label(), &stubcc.masm);
frame.sync(stubcc.masm, Uses(frame.frameDepth()));
doubleTest = stubcc.masm.branchDouble(dblCond, fpLeft, fpRight);
doubleTest = stubcc.masm.branchDouble(dblCond, regs.lhsFP, regs.rhsFP);
doubleFall = stubcc.masm.jump();
/* Link all incoming slow paths to here. */
@ -1538,7 +1592,7 @@ mjit::Compiler::jsop_relational_full(JSOp op, BoolStub stub, jsbytecode *target,
if (lhsUnknownDone.isSet())
lhsUnknownDone.get().linkTo(stubcc.masm.label(), &stubcc.masm);
/* :FIXME: Use SET if we can? */
Jump test = stubcc.masm.branchDouble(dblCond, fpLeft, fpRight);
Jump test = stubcc.masm.branchDouble(dblCond, regs.lhsFP, regs.rhsFP);
stubcc.masm.move(Imm32(0), regs.result);
Jump skip = stubcc.masm.jump();
test.linkTo(stubcc.masm.label(), &stubcc.masm);
@ -1606,7 +1660,11 @@ mjit::Compiler::jsop_relational_full(JSOp op, BoolStub stub, jsbytecode *target,
if (hasDoublePath)
stubcc.crossJump(doubleDone.get(), masm.label());
stubcc.rejoin(Changes(1));
frame.freeFPReg(regs.lhsFP);
frame.freeFPReg(regs.rhsFP);
}
return true;
}

View File

@ -115,136 +115,45 @@ mjit::Compiler::jsop_rsh_int_const(FrameEntry *lhs, FrameEntry *rhs)
}
void
mjit::Compiler::jsop_rsh_unknown_const(FrameEntry *lhs, FrameEntry *rhs)
mjit::Compiler::ensureInteger(FrameEntry *fe, Uses uses)
{
int32 shiftAmount = rhs->getValue().toInt32();
if (fe->isConstant()) {
if (!fe->isType(JSVAL_TYPE_INT32)) {
JS_ASSERT(fe->isType(JSVAL_TYPE_DOUBLE));
fe->convertConstantDoubleToInt32(cx);
}
} else if (fe->isType(JSVAL_TYPE_DOUBLE)) {
FPRegisterID fpreg = frame.tempFPRegForData(fe);
RegisterID lhsType = frame.tempRegForType(lhs);
frame.pinReg(lhsType);
RegisterID lhsData = frame.copyDataIntoReg(lhs);
frame.unpinReg(lhsType);
RegisterID data = frame.allocReg();
Jump truncateGuard = masm.branchTruncateDoubleToInt32(fpreg, data);
stubcc.linkExit(truncateGuard, uses);
Jump lhsIntGuard = masm.testInt32(Assembler::NotEqual, lhsType);
stubcc.linkExitDirect(lhsIntGuard, stubcc.masm.label());
frame.learnType(fe, JSVAL_TYPE_INT32, data);
} else if (!fe->isType(JSVAL_TYPE_INT32)) {
RegisterID typeReg = frame.tempRegForType(fe);
frame.pinReg(typeReg);
RegisterID dataReg = frame.tempRegForData(fe);
frame.pinReg(dataReg);
RegisterID scratchReg = frame.allocReg();
frame.unpinReg(dataReg);
frame.unpinReg(typeReg);
Jump lhsDoubleGuard = stubcc.masm.testDouble(Assembler::NotEqual, lhsType);
frame.loadDouble(lhs, FPRegisters::First, stubcc.masm);
Jump lhsTruncateGuard = stubcc.masm.branchTruncateDoubleToInt32(FPRegisters::First, lhsData);
stubcc.crossJump(stubcc.masm.jump(), masm.label());
Jump intGuard = masm.testInt32(Assembler::Equal, typeReg);
Jump doubleGuard = masm.testDouble(Assembler::NotEqual, typeReg);
stubcc.linkExit(doubleGuard, uses);
lhsDoubleGuard.linkTo(stubcc.masm.label(), &stubcc.masm);
lhsTruncateGuard.linkTo(stubcc.masm.label(), &stubcc.masm);
FPRegisterID fpreg = frame.allocFPReg();
frame.loadDouble(fe, fpreg, masm);
Jump truncateGuard = masm.branchTruncateDoubleToInt32(fpreg, scratchReg);
stubcc.linkExit(truncateGuard, uses);
masm.move(scratchReg, dataReg);
intGuard.linkTo(masm.label(), &masm);
frame.sync(stubcc.masm, Uses(2));
stubcc.call(stubs::Rsh);
if (shiftAmount)
masm.rshift32(Imm32(shiftAmount), lhsData);
frame.popn(2);
frame.pushTypedPayload(JSVAL_TYPE_INT32, lhsData);
stubcc.rejoin(Changes(1));
}
void
mjit::Compiler::jsop_rsh_const_unknown(FrameEntry *lhs, FrameEntry *rhs)
{
RegisterID rhsData = rightRegForShift(rhs);
RegisterID rhsType = frame.tempRegForType(rhs);
frame.pinReg(rhsType);
RegisterID result = frame.allocReg();
frame.unpinReg(rhsType);
Jump rhsIntGuard = masm.testInt32(Assembler::NotEqual, rhsType);
stubcc.linkExit(rhsIntGuard, Uses(2));
stubcc.leave();
stubcc.call(stubs::Rsh);
masm.move(Imm32(lhs->getValue().toInt32()), result);
masm.rshift32(rhsData, result);
frame.freeReg(rhsData);
frame.popn(2);
frame.pushTypedPayload(JSVAL_TYPE_INT32, result);
stubcc.rejoin(Changes(1));
}
void
mjit::Compiler::jsop_rsh_int_unknown(FrameEntry *lhs, FrameEntry *rhs)
{
RegisterID rhsData = rightRegForShift(rhs);
RegisterID rhsType = frame.tempRegForType(rhs);
frame.pinReg(rhsType);
RegisterID lhsData = frame.copyDataIntoReg(lhs);
frame.unpinReg(rhsType);
Jump rhsIntGuard = masm.testInt32(Assembler::NotEqual, rhsType);
stubcc.linkExit(rhsIntGuard, Uses(2));
stubcc.leave();
stubcc.call(stubs::Rsh);
masm.rshift32(rhsData, lhsData);
frame.freeReg(rhsData);
frame.popn(2);
frame.pushTypedPayload(JSVAL_TYPE_INT32, lhsData);
stubcc.rejoin(Changes(1));
}
void
mjit::Compiler::jsop_rsh_unknown_any(FrameEntry *lhs, FrameEntry *rhs)
{
JS_ASSERT(!lhs->isTypeKnown());
JS_ASSERT(!rhs->isNotType(JSVAL_TYPE_INT32));
/* Allocate registers. */
RegisterID rhsData = rightRegForShift(rhs);
MaybeRegisterID rhsType;
if (!rhs->isTypeKnown()) {
rhsType.setReg(frame.tempRegForType(rhs));
frame.pinReg(rhsType.reg());
frame.freeFPReg(fpreg);
frame.freeReg(scratchReg);
frame.learnType(fe, JSVAL_TYPE_INT32);
}
RegisterID lhsData = frame.copyDataIntoReg(lhs);
MaybeRegisterID lhsType;
if (rhsType.isSet() && frame.haveSameBacking(lhs, rhs))
lhsType = rhsType;
else
lhsType = frame.tempRegForType(lhs);
/* Non-integer rhs jumps to stub. */
MaybeJump rhsIntGuard;
if (rhsType.isSet()) {
rhsIntGuard.setJump(masm.testInt32(Assembler::NotEqual, rhsType.reg()));
frame.unpinReg(rhsType.reg());
}
/* Non-integer lhs jumps to double guard. */
Jump lhsIntGuard = masm.testInt32(Assembler::NotEqual, lhsType.reg());
stubcc.linkExitDirect(lhsIntGuard, stubcc.masm.label());
/* Attempt to convert lhs double to int32. */
Jump lhsDoubleGuard = stubcc.masm.testDouble(Assembler::NotEqual, lhsType.reg());
frame.loadDouble(lhs, FPRegisters::First, stubcc.masm);
Jump lhsTruncateGuard = stubcc.masm.branchTruncateDoubleToInt32(FPRegisters::First, lhsData);
stubcc.crossJump(stubcc.masm.jump(), masm.label());
lhsDoubleGuard.linkTo(stubcc.masm.label(), &stubcc.masm);
lhsTruncateGuard.linkTo(stubcc.masm.label(), &stubcc.masm);
if (rhsIntGuard.isSet())
stubcc.linkExitDirect(rhsIntGuard.getJump(), stubcc.masm.label());
frame.sync(stubcc.masm, Uses(2));
stubcc.call(stubs::Rsh);
masm.rshift32(rhsData, lhsData);
frame.freeReg(rhsData);
frame.popn(2);
frame.pushTypedPayload(JSVAL_TYPE_INT32, lhsData);
stubcc.rejoin(Changes(1));
}
void
@ -256,33 +165,31 @@ mjit::Compiler::jsop_rsh()
if (tryBinaryConstantFold(cx, frame, JSOP_RSH, lhs, rhs))
return;
if (lhs->isNotType(JSVAL_TYPE_INT32) || rhs->isNotType(JSVAL_TYPE_INT32)) {
if ((lhs->isNotType(JSVAL_TYPE_INT32) && lhs->isNotType(JSVAL_TYPE_DOUBLE)) ||
(rhs->isNotType(JSVAL_TYPE_INT32) && rhs->isNotType(JSVAL_TYPE_DOUBLE))) {
prepareStubCall(Uses(2));
stubCall(stubs::Rsh);
frame.popn(2);
frame.pushSyncedType(JSVAL_TYPE_INT32);
frame.pushSynced(JSVAL_TYPE_INT32);
return;
}
ensureInteger(lhs, Uses(2));
ensureInteger(rhs, Uses(2));
stubcc.leave();
stubcc.call(stubs::Rsh);
JS_ASSERT(!(lhs->isConstant() && rhs->isConstant()));
if (lhs->isConstant()) {
if (rhs->isType(JSVAL_TYPE_INT32))
jsop_rsh_const_int(lhs, rhs);
else
jsop_rsh_const_unknown(lhs, rhs);
jsop_rsh_const_int(lhs, rhs);
} else if (rhs->isConstant()) {
if (lhs->isType(JSVAL_TYPE_INT32))
jsop_rsh_int_const(lhs, rhs);
else
jsop_rsh_unknown_const(lhs, rhs);
jsop_rsh_int_const(lhs, rhs);
} else {
if (lhs->isType(JSVAL_TYPE_INT32) && rhs->isType(JSVAL_TYPE_INT32))
jsop_rsh_int_int(lhs, rhs);
else if (lhs->isType(JSVAL_TYPE_INT32))
jsop_rsh_int_unknown(lhs, rhs);
else
jsop_rsh_unknown_any(lhs, rhs);
jsop_rsh_int_int(lhs, rhs);
}
stubcc.rejoin(Changes(1));
}
void
@ -295,7 +202,7 @@ mjit::Compiler::jsop_bitnot()
prepareStubCall(Uses(1));
stubCall(stubs::BitNot);
frame.pop();
frame.pushSyncedType(JSVAL_TYPE_INT32);
frame.pushSynced(JSVAL_TYPE_INT32);
return;
}
@ -357,60 +264,32 @@ mjit::Compiler::jsop_bitop(JSOp op)
if (!lhs->isConstant() && rhs->isConstant() && lhsIntOrDouble &&
rhs->isType(JSVAL_TYPE_INT32) && rhs->getValue().toInt32() == 0 &&
(op == JSOP_BITOR || op == JSOP_LSH)) {
ensureInteger(lhs, Uses(2));
RegisterID reg = frame.copyDataIntoReg(lhs);
if (lhs->isType(JSVAL_TYPE_INT32)) {
frame.popn(2);
frame.pushTypedPayload(JSVAL_TYPE_INT32, reg);
return;
}
MaybeJump isInt;
if (!lhs->isType(JSVAL_TYPE_DOUBLE)) {
RegisterID typeReg = frame.tempRegForType(lhs);
isInt = masm.testInt32(Assembler::Equal, typeReg);
Jump notDouble = masm.testDouble(Assembler::NotEqual, typeReg);
stubcc.linkExit(notDouble, Uses(2));
}
frame.loadDouble(lhs, FPRegisters::First, masm);
Jump truncateGuard = masm.branchTruncateDoubleToInt32(FPRegisters::First, reg);
stubcc.linkExit(truncateGuard, Uses(2));
stubcc.leave();
stubcc.call(stub);
if (isInt.isSet())
isInt.get().linkTo(masm.label(), &masm);
frame.popn(2);
frame.pushTypedPayload(JSVAL_TYPE_INT32, reg);
stubcc.rejoin(Changes(1));
return;
}
/* We only want to handle integers here. */
if (rhs->isNotType(JSVAL_TYPE_INT32) || lhs->isNotType(JSVAL_TYPE_INT32) ||
if ((lhs->isNotType(JSVAL_TYPE_INT32) && lhs->isNotType(JSVAL_TYPE_DOUBLE)) ||
(rhs->isNotType(JSVAL_TYPE_INT32) && rhs->isNotType(JSVAL_TYPE_DOUBLE)) ||
(op == JSOP_URSH && rhs->isConstant() && rhs->getValue().toInt32() % 32 == 0)) {
prepareStubCall(Uses(2));
stubCall(stub);
frame.popn(2);
if (op == JSOP_URSH)
frame.pushSynced();
else
frame.pushSyncedType(JSVAL_TYPE_INT32);
frame.pushSynced(op != JSOP_URSH ? JSVAL_TYPE_INT32 : knownPushedType(0));
return;
}
/* Test the types. */
bool stubNeeded = false;
if (!rhs->isTypeKnown()) {
Jump rhsFail = frame.testInt32(Assembler::NotEqual, rhs);
stubcc.linkExit(rhsFail, Uses(2));
frame.learnType(rhs, JSVAL_TYPE_INT32);
stubNeeded = true;
}
if (!lhs->isTypeKnown() && !frame.haveSameBacking(lhs, rhs)) {
Jump lhsFail = frame.testInt32(Assembler::NotEqual, lhs);
stubcc.linkExit(lhsFail, Uses(2));
stubNeeded = true;
}
ensureInteger(lhs, Uses(2));
ensureInteger(rhs, Uses(2));
if (lhs->isConstant() && rhs->isConstant()) {
int32 L = lhs->getValue().toInt32();
@ -495,25 +374,22 @@ mjit::Compiler::jsop_bitop(JSOp op)
RegisterID reg = frame.ownRegForData(lhs);
int shift = rhs->getValue().toInt32() & 0x1F;
stubcc.leave();
stubcc.call(stub);
if (shift) {
if (op == JSOP_LSH)
masm.lshift32(Imm32(shift), reg);
else
masm.urshift32(Imm32(shift), reg);
}
if (stubNeeded) {
stubcc.leave();
stubcc.call(stub);
}
frame.popn(2);
/* x >>> 0 may result in a double, handled above. */
JS_ASSERT_IF(op == JSOP_URSH, shift >= 1);
frame.pushTypedPayload(JSVAL_TYPE_INT32, reg);
if (stubNeeded)
stubcc.rejoin(Changes(1));
stubcc.rejoin(Changes(1));
return;
}
#if defined(JS_CPU_X86) || defined(JS_CPU_X64)
@ -548,7 +424,6 @@ mjit::Compiler::jsop_bitop(JSOp op)
Jump isNegative = masm.branch32(Assembler::LessThan, reg, Imm32(0));
stubcc.linkExit(isNegative, Uses(2));
stubNeeded = true;
}
break;
}
@ -558,31 +433,29 @@ mjit::Compiler::jsop_bitop(JSOp op)
return;
}
if (stubNeeded) {
stubcc.leave();
stubcc.call(stub);
}
stubcc.leave();
stubcc.call(stub);
frame.pop();
frame.pop();
JSValueType type = knownPushedType(0);
if (type != JSVAL_TYPE_UNKNOWN)
if (type != JSVAL_TYPE_UNKNOWN && type != JSVAL_TYPE_DOUBLE)
frame.pushTypedPayload(type, reg);
else if (op == JSOP_URSH)
frame.pushNumber(reg, true);
else
frame.pushTypedPayload(JSVAL_TYPE_INT32, reg);
if (stubNeeded)
stubcc.rejoin(Changes(1));
stubcc.rejoin(Changes(1));
}
void
mjit::Compiler::jsop_globalinc(JSOp op, uint32 index)
{
uint32 slot = script->getGlobalSlot(index);
JSValueType type = knownPushedType(0);
bool popped = false;
PC += JSOP_GLOBALINC_LENGTH;
@ -599,15 +472,22 @@ mjit::Compiler::jsop_globalinc(JSOp op, uint32 index)
Address addr = masm.objSlotRef(globalObj, reg, slot);
uint32 depth = frame.stackDepth();
if (post && !popped) {
frame.push(addr);
if (type != JSVAL_TYPE_UNKNOWN && type != JSVAL_TYPE_INT32) {
data = frame.allocReg();
stubcc.linkExit(masm.jump(), Uses(0));
} else if (post && !popped) {
frame.push(addr, type);
FrameEntry *fe = frame.peek(-1);
Jump notInt = frame.testInt32(Assembler::NotEqual, fe);
stubcc.linkExit(notInt, Uses(0));
if (type == JSVAL_TYPE_UNKNOWN) {
Jump notInt = frame.testInt32(Assembler::NotEqual, fe);
stubcc.linkExit(notInt, Uses(0));
}
data = frame.copyDataIntoReg(fe);
} else {
Jump notInt = masm.testInt32(Assembler::NotEqual, addr);
stubcc.linkExit(notInt, Uses(0));
if (type == JSVAL_TYPE_UNKNOWN) {
Jump notInt = masm.testInt32(Assembler::NotEqual, addr);
stubcc.linkExit(notInt, Uses(0));
}
data = frame.allocReg();
masm.loadPayload(addr, data);
}
@ -933,7 +813,8 @@ mjit::Compiler::booleanJumpScript(JSOp op, jsbytecode *target)
if (!fe->isTypeKnown() && !frame.shouldAvoidTypeRemat(fe))
type.setReg(frame.copyTypeIntoReg(fe));
data.setReg(frame.copyDataIntoReg(fe));
if (!fe->isType(JSVAL_TYPE_DOUBLE))
data.setReg(frame.copyDataIntoReg(fe));
frame.syncAndForgetEverything();
@ -963,7 +844,8 @@ mjit::Compiler::booleanJumpScript(JSOp op, jsbytecode *target)
* TODO: We don't need the second jump if
* jumpInScript() can go from ool path to inline path.
*/
jmpNotExecScript.setJump(masm.branchTest32(ncond, data.reg(), data.reg()));
if (!fe->isType(JSVAL_TYPE_DOUBLE))
jmpNotExecScript.setJump(masm.branchTest32(ncond, data.reg(), data.reg()));
Label lblExecScript = masm.label();
Jump j = masm.jump();
@ -1061,7 +943,7 @@ mjit::Compiler::jsop_localinc(JSOp op, uint32 slot, bool popped)
bool post = (op == JSOP_LOCALINC || op == JSOP_LOCALDEC);
int32 amt = (op == JSOP_INCLOCAL || op == JSOP_LOCALINC) ? 1 : -1;
frame.pushLocal(slot);
frame.pushLocal(slot, knownPushedType(0));
FrameEntry *fe = frame.peek(-1);
@ -1140,14 +1022,18 @@ mjit::Compiler::jsop_localinc(JSOp op, uint32 slot, bool popped)
else
stubcc.call(stubs::DecLocal);
JSValueType type = knownLocalType(slot);
frame.pop();
frame.pushTypedPayload(JSVAL_TYPE_INT32, reg);
frame.storeLocal(slot, popped, false);
if (type == JSVAL_TYPE_INT32)
frame.pushTypedPayload(JSVAL_TYPE_INT32, reg);
else
frame.pushNumber(reg, true);
frame.storeLocal(slot, popped, type);
if (popped)
frame.pop();
else
frame.forgetType(frame.peek(-1));
stubcc.rejoin(Changes(0));
}
@ -1534,7 +1420,7 @@ mjit::Compiler::jsop_getelem_dense(bool isPacked)
RegisterID dataReg = frame.allocReg();
MaybeRegisterID typeReg;
if (!isPacked || type == JSVAL_TYPE_UNKNOWN)
if (!isPacked || type == JSVAL_TYPE_UNKNOWN || type == JSVAL_TYPE_DOUBLE)
typeReg = frame.allocReg();
frame.unpinReg(objReg);
@ -1561,7 +1447,7 @@ mjit::Compiler::jsop_getelem_dense(bool isPacked)
if (!isPacked) {
stubcc.linkExit(holeCheck, Uses(2));
if (type != JSVAL_TYPE_UNKNOWN)
if (type != JSVAL_TYPE_UNKNOWN && type != JSVAL_TYPE_DOUBLE)
frame.freeReg(typeReg.reg());
}
@ -1570,10 +1456,10 @@ mjit::Compiler::jsop_getelem_dense(bool isPacked)
frame.popn(2);
if (type != JSVAL_TYPE_UNKNOWN)
frame.pushTypedPayload(type, dataReg);
if (type == JSVAL_TYPE_UNKNOWN || type == JSVAL_TYPE_DOUBLE)
frame.pushRegs(typeReg.reg(), dataReg, type);
else
frame.pushRegs(typeReg.reg(), dataReg);
frame.pushTypedPayload(type, dataReg);
stubcc.rejoin(Changes(2));
}
@ -1714,9 +1600,9 @@ mjit::Compiler::jsop_getelem(bool isCall)
ic.fastPathRejoin = masm.label();
frame.popn(2);
frame.pushRegs(ic.typeReg, ic.objReg);
frame.pushRegs(ic.typeReg, ic.objReg, knownPushedType(0));
if (isCall)
frame.pushSynced();
frame.pushSynced(knownPushedType(1));
stubcc.rejoin(Changes(2));
@ -1887,7 +1773,7 @@ mjit::Compiler::jsop_stricteq(JSOp op)
stubCall(stubs::StrictNe);
frame.popn(2);
frame.pushSyncedType(JSVAL_TYPE_BOOLEAN);
frame.pushSynced(JSVAL_TYPE_BOOLEAN);
return;
}
@ -1972,7 +1858,7 @@ mjit::Compiler::jsop_pos()
prepareStubCall(Uses(1));
stubCall(stubs::Pos);
frame.pop();
frame.pushSynced();
frame.pushSynced(knownPushedType(0));
return;
}

View File

@ -41,6 +41,7 @@
#define jsjaeger_valueinfo_h__
#include "jsapi.h"
#include "jsnum.h"
#include "methodjit/MachineRegs.h"
#include "methodjit/RematInfo.h"
#include "assembler/assembler/MacroAssembler.h"
@ -72,6 +73,10 @@ class FrameEntry
return type.isConstant();
}
/*
* The known type should not be used in generated code if it is JSVAL_TYPE_DOUBLE.
* In such cases either the value is constant, in memory or in a floating point register.
*/
JSValueType getKnownType() const {
JS_ASSERT(isTypeKnown());
return knownType;
@ -79,6 +84,7 @@ class FrameEntry
#if defined JS_NUNBOX32
JSValueTag getKnownTag() const {
JS_ASSERT(v_.s.tag != JSVAL_TAG_CLEAR);
return v_.s.tag;
}
#elif defined JS_PUNBOX64
@ -114,14 +120,20 @@ class FrameEntry
}
#endif
bool isCachedNumber() const {
return isNumber;
}
bool hasSameBacking(const FrameEntry *other) const {
return backing() == other->backing();
}
/* For a constant double FrameEntry, truncate to an int32. */
void convertConstantDoubleToInt32(JSContext *cx) {
JS_ASSERT(isType(JSVAL_TYPE_DOUBLE) && isConstant());
int32 value;
ValueToECMAInt32(cx, getValue(), &value);
Value newValue = Int32Value(value);
setConstant(Jsvalify(newValue));
}
private:
void setType(JSValueType type_) {
type.setConstant();
@ -132,7 +144,6 @@ class FrameEntry
v_.asBits |= JSVAL_TYPE_TO_SHIFTED_TAG(type_);
#endif
knownType = type_;
JS_ASSERT(!isNumber);
}
void track(uint32 index) {
@ -144,7 +155,6 @@ class FrameEntry
void clear() {
copied = false;
copy = NULL;
isNumber = false;
}
uint32 trackerIndex() {
@ -242,9 +252,8 @@ class FrameEntry
uint32 index_;
FrameEntry *copy;
bool copied;
bool isNumber;
bool tracked;
char padding[1];
char padding[2];
};
} /* namespace mjit */

View File

@ -126,6 +126,20 @@ FrameState::allocReg(FrameEntry *fe, RematInfo::RematType type)
return reg;
}
inline JSC::MacroAssembler::FPRegisterID
FrameState::allocFPReg()
{
FPRegisterID reg;
if (!freeFPRegs.empty()) {
reg = freeFPRegs.takeAnyReg();
} else {
reg = evictSomeFPReg();
fpregstate[reg].forget();
}
return reg;
}
inline void
FrameState::convertInt32ToDouble(Assembler &masm, FrameEntry *fe, FPRegisterID fpreg) const
{
@ -168,6 +182,14 @@ FrameState::freeReg(RegisterID reg)
freeRegs.putReg(reg);
}
inline void
FrameState::freeFPReg(FPRegisterID reg)
{
JS_ASSERT(!fpregstate[reg].usedBy());
freeFPRegs.putReg(reg);
}
inline void
FrameState::forgetReg(RegisterID reg)
{
@ -183,6 +205,16 @@ FrameState::forgetReg(RegisterID reg)
}
}
inline void
FrameState::forgetFPReg(FPRegisterID reg)
{
JS_ASSERT_IF(fpregstate[reg].fe(), !fpregstate[reg].fe()->isCopy());
JS_ASSERT(!fpregstate[reg].isPinned());
fpregstate[reg].forget();
freeFPRegs.putReg(reg);
}
inline void
FrameState::syncAndForgetEverything(uint32 newStackDepth)
{
@ -209,20 +241,16 @@ FrameState::push(const Value &v)
}
inline void
FrameState::pushSynced()
{
if (sp->isTracked())
sp->resetSynced();
sp++;
}
inline void
FrameState::pushSyncedType(JSValueType type)
FrameState::pushSynced(JSValueType type)
{
FrameEntry *fe = rawPush();
fe->resetSynced();
fe->setType(type);
if (type != JSVAL_TYPE_UNKNOWN) {
fe->setType(type);
if (type == JSVAL_TYPE_DOUBLE)
ensureInMemoryDouble(fe, masm);
}
}
inline void
@ -239,8 +267,15 @@ FrameState::pushSynced(JSValueType type, RegisterID reg)
}
inline void
FrameState::push(Address address)
FrameState::push(Address address, JSValueType knownType)
{
if (knownType == JSVAL_TYPE_DOUBLE) {
FPRegisterID fpreg = allocFPReg();
masm.moveInt32OrDouble(address, fpreg);
pushDouble(fpreg);
return;
}
#ifdef JS_PUNBOX64
// It's okay if either of these clobbers address.base, since we guarantee
// eviction will not physically clobber. It's also safe, on x64, for
@ -268,26 +303,37 @@ FrameState::push(Address address)
masm.loadPayload(address, dataReg);
#endif
pushRegs(typeReg, dataReg);
pushRegs(typeReg, dataReg, knownType);
}
inline void
FrameState::pushRegs(RegisterID type, RegisterID data)
FrameState::pushRegs(RegisterID type, RegisterID data, JSValueType knownType)
{
JS_ASSERT(!freeRegs.hasReg(type) && !freeRegs.hasReg(data));
FrameEntry *fe = rawPush();
fe->resetUnsynced();
fe->type.setRegister(type);
fe->data.setRegister(data);
regstate[type].associate(fe, RematInfo::TYPE);
regstate[data].associate(fe, RematInfo::DATA);
if (knownType == JSVAL_TYPE_UNKNOWN) {
FrameEntry *fe = rawPush();
fe->resetUnsynced();
fe->type.setRegister(type);
fe->data.setRegister(data);
regstate[type].associate(fe, RematInfo::TYPE);
regstate[data].associate(fe, RematInfo::DATA);
} else if (knownType == JSVAL_TYPE_DOUBLE) {
FPRegisterID fpreg = allocFPReg();
masm.moveInt32OrDouble(data, type, addressOf(sp), fpreg);
pushDouble(fpreg);
freeReg(type);
freeReg(data);
} else {
freeReg(type);
pushTypedPayload(knownType, data);
}
}
inline void
FrameState::pushTypedPayload(JSValueType type, RegisterID payload)
{
JS_ASSERT(type != JSVAL_TYPE_DOUBLE);
JS_ASSERT(!freeRegs.hasReg(payload));
FrameEntry *fe = rawPush();
@ -299,15 +345,13 @@ FrameState::pushTypedPayload(JSValueType type, RegisterID payload)
}
inline void
FrameState::pushNumber(MaybeRegisterID payload, bool asInt32)
FrameState::pushNumber(RegisterID payload, bool asInt32)
{
JS_ASSERT_IF(payload.isSet(), !freeRegs.hasReg(payload.reg()));
JS_ASSERT(!freeRegs.hasReg(payload));
FrameEntry *fe = rawPush();
fe->clear();
JS_ASSERT(!fe->isNumber);
if (asInt32) {
if (!fe->type.synced())
masm.storeTypeTag(ImmType(JSVAL_TYPE_INT32), addressOf(fe));
@ -316,14 +360,9 @@ FrameState::pushNumber(MaybeRegisterID payload, bool asInt32)
fe->type.setMemory();
}
fe->isNumber = true;
if (payload.isSet()) {
fe->data.unsync();
fe->data.setRegister(payload.reg());
regstate[payload.reg()].associate(fe, RematInfo::DATA);
} else {
fe->data.setMemory();
}
fe->data.unsync();
fe->data.setRegister(payload);
regstate[payload].associate(fe, RematInfo::DATA);
}
inline void
@ -331,12 +370,10 @@ FrameState::pushInt32(RegisterID payload)
{
FrameEntry *fe = rawPush();
fe->clear();
JS_ASSERT(!fe->isNumber);
masm.storeTypeTag(ImmType(JSVAL_TYPE_INT32), addressOf(fe));
fe->type.setMemory();
fe->isNumber = true;
fe->data.unsync();
fe->data.setRegister(payload);
regstate[payload].associate(fe, RematInfo::DATA);
@ -407,6 +444,7 @@ inline JSC::MacroAssembler::RegisterID
FrameState::tempRegForData(FrameEntry *fe)
{
JS_ASSERT(!fe->data.isConstant());
JS_ASSERT(!fe->isType(JSVAL_TYPE_DOUBLE));
if (fe->isCopy())
fe = fe->copyOf();
@ -420,10 +458,31 @@ FrameState::tempRegForData(FrameEntry *fe)
return reg;
}
inline JSC::MacroAssembler::FPRegisterID
FrameState::tempFPRegForData(FrameEntry *fe)
{
JS_ASSERT(!fe->data.isConstant());
JS_ASSERT(fe->isType(JSVAL_TYPE_DOUBLE));
if (fe->isCopy())
fe = fe->copyOf();
if (fe->data.inFPRegister())
return fe->data.fpreg();
FPRegisterID reg = allocFPReg();
masm.loadDouble(addressOf(fe), reg);
setFPRegister(fe, reg);
return reg;
}
inline JSC::MacroAssembler::RegisterID
FrameState::tempRegInMaskForData(FrameEntry *fe, uint32 mask)
{
JS_ASSERT(!fe->data.isConstant());
JS_ASSERT(!fe->isType(JSVAL_TYPE_DOUBLE));
if (fe->isCopy())
fe = fe->copyOf();
@ -485,6 +544,25 @@ FrameState::ensureFeSynced(const FrameEntry *fe, Assembler &masm) const
if (fe->isCopy())
backing = fe->copyOf();
if (backing->isType(JSVAL_TYPE_DOUBLE)) {
if (fe->data.synced()) {
/* Entries representing known doubles can't be partially synced. */
JS_ASSERT(fe->type.synced());
return;
}
if (backing->isConstant()) {
masm.storeValue(backing->getValue(), to);
} else if (backing->data.inFPRegister()) {
masm.storeDouble(backing->data.fpreg(), to);
} else {
/* Use a temporary so the entry can be synced without allocating a register. */
JS_ASSERT(backing->data.inMemory() && backing != fe);
masm.loadDouble(addressOf(backing), FPRegisters::ConversionTemp);
masm.storeDouble(FPRegisters::ConversionTemp, to);
}
return;
}
#if defined JS_PUNBOX64
/* If we can, sync the type and data in one go. */
if (!fe->data.synced() && !fe->type.synced()) {
@ -574,10 +652,25 @@ FrameState::ensureDataSynced(const FrameEntry *fe, Assembler &masm) const
inline void
FrameState::syncFe(FrameEntry *fe)
{
if (fe->type.synced() && fe->data.synced())
return;
FrameEntry *backing = fe;
if (fe->isCopy())
backing = fe->copyOf();
if (backing->isType(JSVAL_TYPE_DOUBLE)) {
if (!backing->isConstant())
tempFPRegForData(backing);
ensureFeSynced(fe, masm);
if (!fe->type.synced())
fe->type.sync();
if (!fe->data.synced())
fe->data.sync();
return;
}
bool needTypeReg = !fe->type.synced() && backing->type.inMemory();
bool needDataReg = !fe->data.synced() && backing->data.inMemory();
@ -633,6 +726,8 @@ FrameState::syncFe(FrameEntry *fe)
inline void
FrameState::syncType(FrameEntry *fe)
{
JS_ASSERT(!fe->isType(JSVAL_TYPE_DOUBLE));
FrameEntry *backing = fe;
if (fe->isCopy())
backing = fe->copyOf();
@ -649,6 +744,8 @@ FrameState::syncType(FrameEntry *fe)
inline void
FrameState::syncData(FrameEntry *fe)
{
JS_ASSERT(!fe->isType(JSVAL_TYPE_DOUBLE));
FrameEntry *backing = fe;
if (fe->isCopy())
backing = fe->copyOf();
@ -690,16 +787,30 @@ FrameState::forgetType(FrameEntry *fe)
inline void
FrameState::learnType(FrameEntry *fe, JSValueType type, bool unsync)
{
JS_ASSERT(!fe->isType(JSVAL_TYPE_DOUBLE));
if (fe->type.inRegister())
forgetReg(fe->type.reg());
#ifdef DEBUG
fe->isNumber = false;
#endif
fe->setType(type);
if (unsync)
fe->type.unsync();
}
inline void
FrameState::learnType(FrameEntry *fe, JSValueType type, RegisterID data)
{
forgetAllRegs(fe);
fe->setCopyOf(NULL);
fe->type.setConstant();
fe->knownType = type;
fe->data.setRegister(data);
regstate[data].associate(fe, RematInfo::DATA);
fe->data.unsync();
fe->type.unsync();
}
inline JSC::MacroAssembler::Address
FrameState::addressOf(const FrameEntry *fe) const
{
@ -802,18 +913,6 @@ FrameState::getLocal(uint32 slot)
return fe;
}
inline void
FrameState::pinReg(RegisterID reg)
{
regstate[reg].pin();
}
inline void
FrameState::unpinReg(RegisterID reg)
{
regstate[reg].unpin();
}
inline void
FrameState::unpinKilledReg(RegisterID reg)
{
@ -828,6 +927,8 @@ FrameState::forgetAllRegs(FrameEntry *fe)
forgetReg(fe->type.reg());
if (fe->data.inRegister())
forgetReg(fe->data.reg());
if (fe->data.inFPRegister())
forgetFPReg(fe->data.fpreg());
}
inline void
@ -873,7 +974,7 @@ FrameState::dupAt(int32 n)
}
inline void
FrameState::pushLocal(uint32 n)
FrameState::pushLocal(uint32 n, JSValueType knownType)
{
if (!eval && !isClosedVar(n)) {
pushCopyOf(indexOfFe(getLocal(n)));
@ -890,7 +991,7 @@ FrameState::pushLocal(uint32 n)
JS_ASSERT(fe->data.inMemory());
}
#endif
push(Address(JSFrameReg, sizeof(JSStackFrame) + n * sizeof(Value)));
push(Address(JSFrameReg, sizeof(JSStackFrame) + n * sizeof(Value)), knownType);
}
}
@ -959,18 +1060,18 @@ FrameState::giveOwnRegs(FrameEntry *fe)
} else {
RegisterID type = copyTypeIntoReg(fe);
pop();
pushRegs(type, data);
pushRegs(type, data, JSVAL_TYPE_UNKNOWN);
}
}
inline void
FrameState::loadDouble(RegisterID t, RegisterID d, FrameEntry *fe, FPRegisterID fpReg,
FrameState::loadDouble(RegisterID t, RegisterID d, FrameEntry *fe, FPRegisterID fpreg,
Assembler &masm) const
{
#ifdef JS_CPU_X86
masm.fastLoadDouble(d, t, fpReg);
masm.fastLoadDouble(d, t, fpreg);
#else
loadDouble(fe, fpReg, masm);
loadDouble(fe, fpreg, masm);
#endif
}
@ -993,25 +1094,28 @@ FrameState::loadDouble(FrameEntry *fe, FPRegisterID fpReg, Assembler &masm) cons
FrameEntry *backing = fe->copyOf();
if (tryFastDoubleLoad(fe, fpReg, masm))
return;
if (backing->isCachedNumber() || (backing->type.synced() && backing->data.synced())) {
masm.loadDouble(addressOf(backing), fpReg);
return;
}
fe = backing;
}
if (tryFastDoubleLoad(fe, fpReg, masm))
return;
if ((fe->type.synced() && fe->data.synced()) || fe->isCachedNumber()) {
masm.loadDouble(addressOf(fe), fpReg);
return;
}
ensureFeSynced(fe, masm);
masm.loadDouble(addressOf(fe), fpReg);
}
void
FrameState::setFPRegister(FrameEntry *fe, FPRegisterID fpreg, bool reassociate)
{
fe->type.setConstant();
fe->knownType = JSVAL_TYPE_DOUBLE;
fe->data.setFPRegister(fpreg);
if (reassociate)
fpregstate[fpreg].reassociate(fe);
else
fpregstate[fpreg].associate(fe, RematInfo::DATA);
}
inline bool
FrameState::isClosedVar(uint32 slot)
{

View File

@ -176,6 +176,41 @@ FrameState::evictSomeReg(uint32 mask)
return fallback;
}
JSC::MacroAssembler::FPRegisterID
FrameState::evictSomeFPReg()
{
#ifdef DEBUG
bool fallbackSet = false;
#endif
FPRegisterID fallback = FPRegisterID(0);
for (uint32 i = 0; i < FPRegisters::TotalFPRegisters; i++) {
FPRegisterID reg = FPRegisterID(i);
FrameEntry *fe = fpregstate[i].fe();
if (!fe)
continue;
JS_ASSERT(fe->isType(JSVAL_TYPE_DOUBLE));
#ifdef DEBUG
fallbackSet = true;
#endif
fallback = reg;
if (fe->data.synced()) {
fe->data.setMemory();
return fallback;
}
}
JS_ASSERT(fallbackSet);
FrameEntry *fe = fpregstate[fallback].fe();
syncFe(fe);
fe->data.setMemory();
return fallback;
}
void
FrameState::syncAndForgetEverything()
@ -192,6 +227,7 @@ FrameState::resetInternalState()
tracker.reset();
freeRegs.reset();
freeFPRegs.reset();
}
void
@ -200,6 +236,7 @@ FrameState::discardFrame()
resetInternalState();
memset(regstate, 0, sizeof(regstate));
memset(fpregstate, 0, sizeof(fpregstate));
}
void
@ -208,9 +245,10 @@ FrameState::forgetEverything()
resetInternalState();
#ifdef DEBUG
for (uint32 i = 0; i < JSC::MacroAssembler::TotalRegisters; i++) {
for (uint32 i = 0; i < JSC::MacroAssembler::TotalRegisters; i++)
JS_ASSERT(!regstate[i].usedBy());
}
for (uint32 i = 0; i < FPRegisters::TotalFPRegisters; i++)
JS_ASSERT(!fpregstate[i].usedBy());
#endif
}
@ -233,6 +271,18 @@ FrameState::storeTo(FrameEntry *fe, Address address, bool popped)
addressOf(fe).base != address.base ||
addressOf(fe).offset != address.offset);
if (fe->data.inFPRegister()) {
masm.storeDouble(fe->data.fpreg(), address);
return;
}
if (fe->isType(JSVAL_TYPE_DOUBLE)) {
JS_ASSERT(fe->data.inMemory());
masm.loadDouble(addressOf(fe), FPRegisters::ConversionTemp);
masm.storeDouble(FPRegisters::ConversionTemp, address);
return;
}
#if defined JS_PUNBOX64
if (fe->type.inMemory() && fe->data.inMemory()) {
/* Future optimization: track that the Value is in a register. */
@ -242,6 +292,8 @@ FrameState::storeTo(FrameEntry *fe, Address address, bool popped)
return;
}
JS_ASSERT(!fe->isType(JSVAL_TYPE_DOUBLE));
/*
* If dreg is obtained via allocReg(), then calling
* pinReg() trips an assertion. But in all other cases,
@ -335,6 +387,12 @@ void FrameState::loadForReturn(FrameEntry *fe, RegisterID typeReg, RegisterID da
return;
}
if (fe->isType(JSVAL_TYPE_DOUBLE)) {
FPRegisterID fpreg = tempFPRegForData(fe);
masm.breakDouble(fpreg, typeReg, dataReg);
return;
}
if (fe->isCopy())
fe = fe->copyOf();
@ -409,6 +467,7 @@ void
FrameState::assertValidRegisterState() const
{
Registers checkedFreeRegs;
FPRegisters checkedFreeFPRegs;
for (uint32 i = 0; i < tracker.nentries; i++) {
FrameEntry *fe = tracker[i];
@ -419,7 +478,8 @@ FrameState::assertValidRegisterState() const
JS_ASSERT_IF(fe->isCopy(),
fe->trackerIndex() > fe->copyOf()->trackerIndex());
JS_ASSERT_IF(fe->isCopy(), fe > fe->copyOf());
JS_ASSERT_IF(fe->isCopy(), !fe->type.inRegister() && !fe->data.inRegister());
JS_ASSERT_IF(fe->isCopy(),
!fe->type.inRegister() && !fe->data.inRegister() && !fe->data.inFPRegister());
JS_ASSERT_IF(fe->isCopy(), fe->copyOf() < sp);
JS_ASSERT_IF(fe->isCopy(), fe->copyOf()->isCopied());
@ -433,15 +493,28 @@ FrameState::assertValidRegisterState() const
checkedFreeRegs.takeReg(fe->data.reg());
JS_ASSERT(regstate[fe->data.reg()].fe() == fe);
}
if (fe->data.inFPRegister()) {
JS_ASSERT(fe->isType(JSVAL_TYPE_DOUBLE));
checkedFreeFPRegs.takeReg(fe->data.fpreg());
JS_ASSERT(fpregstate[fe->data.fpreg()].fe() == fe);
}
}
JS_ASSERT(checkedFreeRegs == freeRegs);
JS_ASSERT(checkedFreeFPRegs == freeFPRegs);
for (uint32 i = 0; i < JSC::MacroAssembler::TotalRegisters; i++) {
JS_ASSERT(!regstate[i].isPinned());
JS_ASSERT_IF(regstate[i].fe(), !freeRegs.hasReg(RegisterID(i)));
JS_ASSERT_IF(regstate[i].fe(), regstate[i].fe()->isTracked());
}
for (uint32 i = 0; i < FPRegisters::TotalFPRegisters; i++) {
JS_ASSERT(!fpregstate[i].isPinned());
JS_ASSERT_IF(fpregstate[i].fe(), !freeFPRegs.hasReg(FPRegisterID(i)));
JS_ASSERT_IF(fpregstate[i].fe(), fpregstate[i].fe()->isTracked());
JS_ASSERT_IF(fpregstate[i].fe(), fpregstate[i].type() == RematInfo::DATA);
}
}
#endif
@ -498,6 +571,21 @@ FrameState::sync(Assembler &masm, Uses uses) const
#endif
}
/* Floating point registers are all volatile, so they are always synced for calls. */
for (unsigned i = 0; i < FPRegisters::TotalFPRegisters; i++) {
if (freeFPRegs.hasReg(FPRegisterID(i)))
continue;
FrameEntry *fe = fpregstate[i].fe();
if (!fe)
continue;
JS_ASSERT(fe->isType(JSVAL_TYPE_DOUBLE));
JS_ASSERT(fpregstate[i].type() == RematInfo::DATA);
ensureFeSynced(fe, masm);
}
/*
* Keep track of free registers using a bitmask. If we have to drop into
* syncFancy(), then this mask will help avoid eviction.
@ -511,6 +599,12 @@ FrameState::sync(Assembler &masm, Uses uses) const
if (!fe->isTracked())
continue;
if (fe->isType(JSVAL_TYPE_DOUBLE)) {
/* Copies of in-memory doubles can be synced without spilling. */
ensureFeSynced(fe, masm);
continue;
}
FrameEntry *backing = fe;
if (!fe->isCopy()) {
@ -669,11 +763,56 @@ FrameState::syncAndKill(Registers kill, Uses uses, Uses ignore)
forgetReg(reg);
}
/* Floating point registers are all volatile, so they are always synced for calls. */
for (unsigned i = 0; i < FPRegisters::TotalFPRegisters; i++) {
if (freeFPRegs.hasReg(FPRegisterID(i)))
continue;
FrameEntry *fe = fpregstate[i].fe();
if (!fe || fe >= spStop)
continue;
JS_ASSERT(fe && fe->isType(JSVAL_TYPE_DOUBLE));
JS_ASSERT(fpregstate[i].type() == RematInfo::DATA);
syncFe(fe);
JS_ASSERT(fe->data.synced() && fe->type.synced());
fe->data.setMemory();
forgetFPReg(FPRegisterID(i));
}
}
void
FrameState::merge(Assembler &masm, Changes changes) const
{
/*
* For any changed values we are merging back which we consider to be doubles,
* ensure they actually are doubles. They must be doubles or ints, but we
* do not require stub paths to always generate a double when needed.
*/
for (unsigned i = 0; i < changes.nchanges; i++) {
FrameEntry *fe = sp - 1 - i;
if (fe->isType(JSVAL_TYPE_DOUBLE))
ensureInMemoryDouble(fe, masm);
}
for (unsigned i = 0; i < FPRegisters::TotalFPRegisters; i++) {
if (freeFPRegs.hasReg(FPRegisterID(i)))
continue;
FrameEntry *fe = fpregstate[i].fe();
if (!fe)
continue;
JS_ASSERT(fe->isType(JSVAL_TYPE_DOUBLE));
JS_ASSERT(fpregstate[i].type() == RematInfo::DATA);
masm.loadDouble(addressOf(fe), FPRegisterID(i));
}
Registers search(Registers::AvailRegs & ~freeRegs.freeMask);
while (!search.empty()) {
@ -712,6 +851,7 @@ void
FrameState::copyDataIntoReg(FrameEntry *fe, RegisterID hint)
{
JS_ASSERT(!fe->data.isConstant());
JS_ASSERT(!fe->isType(JSVAL_TYPE_DOUBLE));
if (fe->isCopy())
fe = fe->copyOf();
@ -822,24 +962,6 @@ FrameState::copyInt32ConstantIntoReg(Assembler &masm, FrameEntry *fe)
return reg;
}
JSC::MacroAssembler::FPRegisterID
FrameState::copyEntryIntoFPReg(FrameEntry *fe, FPRegisterID fpreg)
{
return copyEntryIntoFPReg(this->masm, fe, fpreg);
}
JSC::MacroAssembler::FPRegisterID
FrameState::copyEntryIntoFPReg(Assembler &masm, FrameEntry *fe, FPRegisterID fpreg)
{
if (fe->isCopy())
fe = fe->copyOf();
ensureFeSynced(fe, masm);
masm.loadDouble(addressOf(fe), fpreg);
return fpreg;
}
JSC::MacroAssembler::RegisterID
FrameState::ownRegForType(FrameEntry *fe)
{
@ -887,6 +1009,7 @@ JSC::MacroAssembler::RegisterID
FrameState::ownRegForData(FrameEntry *fe)
{
JS_ASSERT(!fe->data.isConstant());
JS_ASSERT(!fe->isType(JSVAL_TYPE_DOUBLE));
RegisterID reg;
if (fe->isCopy()) {
@ -942,6 +1065,77 @@ FrameState::discardFe(FrameEntry *fe)
fe->data.setMemory();
}
void
FrameState::pushDouble(FPRegisterID fpreg)
{
FrameEntry *fe = rawPush();
fe->resetUnsynced();
setFPRegister(fe, fpreg);
}
void
FrameState::pushDouble(Address address)
{
FPRegisterID fpreg = allocFPReg();
masm.loadDouble(address, fpreg);
FrameEntry *fe = rawPush();
fe->resetUnsynced();
setFPRegister(fe, fpreg);
}
void
FrameState::ensureInMemoryDouble(FrameEntry *fe, Assembler &masm) const
{
Address address = addressOf(fe);
Jump notInteger = masm.testInt32(Assembler::NotEqual, address);
masm.convertInt32ToDouble(masm.payloadOf(address), FPRegisters::ConversionTemp);
masm.storeDouble(FPRegisters::ConversionTemp, address);
notInteger.linkTo(masm.label(), &masm);
}
void
FrameState::ensureDouble(FrameEntry *fe)
{
if (fe->isConstant()) {
JS_ASSERT(fe->getValue().isInt32());
Value newValue = DoubleValue(double(fe->getValue().toInt32()));
fe->setConstant(Jsvalify(newValue));
return;
}
if (fe->isCopy()) {
/* If this is a copy of another slot, that slot should already have been converted to double. */
JS_ASSERT(fe->copyOf()->isType(JSVAL_TYPE_DOUBLE));
fe->type.setConstant();
fe->knownType = JSVAL_TYPE_DOUBLE;
return;
}
if (fe->isType(JSVAL_TYPE_DOUBLE))
return;
if (fe->isType(JSVAL_TYPE_INT32)) {
RegisterID data = tempRegForData(fe);
FPRegisterID fpreg = allocFPReg();
masm.convertInt32ToDouble(data, fpreg);
forgetAllRegs(fe);
setFPRegister(fe, fpreg);
return;
}
if (fe->data.inMemory()) {
FPRegisterID fpreg = allocFPReg();
masm.moveInt32OrDouble(addressOf(fe), fpreg);
setFPRegister(fe, fpreg);
return;
}
JS_NOT_REACHED("FIXME");
}
void
FrameState::pushCopyOf(uint32 index)
{
@ -955,7 +1149,6 @@ FrameState::pushCopyOf(uint32 index)
fe->setType(backing->getKnownType());
else
fe->type.invalidate();
fe->isNumber = backing->isNumber;
fe->data.invalidate();
if (backing->isCopy()) {
backing = backing->copyOf();
@ -1139,11 +1332,11 @@ FrameState::uncopy(FrameEntry *original)
}
void
FrameState::storeLocal(uint32 n, bool popGuaranteed, bool typeChange)
FrameState::storeLocal(uint32 n, bool popGuaranteed, JSValueType type)
{
FrameEntry *local = getLocal(n);
storeTop(local, popGuaranteed, typeChange);
storeTop(local, popGuaranteed, type);
bool closed = eval || isClosedVar(n);
if (!closed && !inTryBlock)
@ -1173,9 +1366,8 @@ FrameState::forgetEntry(FrameEntry *fe)
}
void
FrameState::storeTop(FrameEntry *target, bool popGuaranteed, bool typeChange)
FrameState::storeTop(FrameEntry *target, bool popGuaranteed, JSValueType type)
{
bool wasSynced = target->type.synced();
/* Detect something like (x = x) which is a no-op. */
FrameEntry *top = peek(-1);
if (top->isCopy() && top->copyOf() == target) {
@ -1192,8 +1384,12 @@ FrameState::storeTop(FrameEntry *target, bool popGuaranteed, bool typeChange)
target->setCopyOf(NULL);
target->setNotCopied();
target->setConstant(Jsvalify(top->getValue()));
if (!typeChange)
/* Types of local variables are always in sync if known. */
if (indexOfFe(target) < localIndex(script->nfixed) &&
type != JSVAL_TYPE_UNKNOWN && type != JSVAL_TYPE_DOUBLE) {
target->type.sync();
}
return;
}
@ -1227,7 +1423,6 @@ FrameState::storeTop(FrameEntry *target, bool popGuaranteed, bool typeChange)
else
target->type.invalidate();
target->data.invalidate();
target->isNumber = backing->isNumber;
return;
}
@ -1269,40 +1464,60 @@ FrameState::storeTop(FrameEntry *target, bool popGuaranteed, bool typeChange)
if (backing->trackerIndex() < target->trackerIndex())
swapInTracker(backing, target);
/*
* Move the backing store down - we spill registers here, but we could be
* smarter and re-use the type reg.
*/
RegisterID reg = tempRegForData(backing);
target->data.setRegister(reg);
regstate[reg].reassociate(target);
if (backing->isType(JSVAL_TYPE_DOUBLE)) {
FPRegisterID fpreg = tempFPRegForData(backing);
if (type == JSVAL_TYPE_UNKNOWN) {
masm.storeDouble(fpreg, addressOf(target));
target->resetSynced();
if (typeChange) {
if (backing->isTypeKnown()) {
target->setType(backing->getKnownType());
/* We're about to invalidate the backing, so forget the FP register. */
forgetFPReg(fpreg);
} else {
RegisterID reg = tempRegForType(backing);
target->type.setRegister(reg);
regstate[reg].reassociate(target);
JS_ASSERT(type == JSVAL_TYPE_DOUBLE);
target->resetUnsynced();
setFPRegister(target, fpreg, true);
}
} else {
/*
* :FIXME: Should eventually assert this, but can't yet as registers are still
* used to hold things with known types in some places.
* Move the backing store down - we spill registers here, but we could be
* smarter and re-use the type reg.
*/
// JS_ASSERT(backing->isTypeKnown());
if (!backing->isTypeKnown() && backing->type.inRegister())
forgetReg(backing->type.reg());
if (!wasSynced)
masm.storeTypeTag(ImmType(backing->getKnownType()), addressOf(target));
target->type.setMemory();
RegisterID reg = tempRegForData(backing);
target->data.setRegister(reg);
regstate[reg].reassociate(target);
if (type == JSVAL_TYPE_UNKNOWN) {
if (backing->isTypeKnown()) {
target->setType(backing->getKnownType());
} else {
RegisterID reg = tempRegForType(backing);
target->type.setRegister(reg);
regstate[reg].reassociate(target);
}
} else if (type == JSVAL_TYPE_DOUBLE) {
JS_ASSERT(backing->isType(JSVAL_TYPE_INT32));
FPRegisterID fpreg = allocFPReg();
masm.convertInt32ToDouble(reg, fpreg);
setFPRegister(target, fpreg);
forgetReg(reg);
} else {
/*
* The backing should normally already be the type we are storing. However,
* we do not always keep track of the type in fused opcodes like GETTHISPROP.
*/
JS_ASSERT_IF(backing->isTypeKnown(), backing->isType(type));
if (!backing->isTypeKnown())
learnType(backing, type);
target->type.setConstant();
target->knownType = type;
}
}
if (!backing->isTypeKnown())
backing->type.invalidate();
backing->data.invalidate();
backing->setCopyOf(target);
backing->isNumber = target->isNumber;
JS_ASSERT(top->copyOf() == target);
@ -1337,12 +1552,37 @@ FrameState::shift(int32 n)
pop();
}
void
FrameState::forgetKnownDouble(FrameEntry *fe)
{
/*
* Forget all information indicating fe is a double, so we can use GPRs for its
* contents. We currently need to do this in order to use the entry in MICs/PICs
* or to construct its ValueRemat. :FIXME: this needs to get fixed.
*/
JS_ASSERT(!fe->isConstant() && fe->isType(JSVAL_TYPE_DOUBLE));
FPRegisterID fpreg = tempFPRegForData(fe);
forgetAllRegs(fe);
fe->resetUnsynced();
RegisterID typeReg = allocReg(fe, RematInfo::TYPE);
pinReg(typeReg);
RegisterID dataReg = allocReg(fe, RematInfo::DATA);
unpinReg(typeReg);
masm.breakDouble(fpreg, typeReg, dataReg);
fe->type.setRegister(typeReg);
fe->data.setRegister(dataReg);
}
void
FrameState::pinEntry(FrameEntry *fe, ValueRemat &vr)
{
if (fe->isConstant()) {
vr = ValueRemat::FromConstant(fe->getValue());
} else {
if (fe->isType(JSVAL_TYPE_DOUBLE))
forgetKnownDouble(fe);
// Pin the type register so it can't spill.
MaybeRegisterID maybePinnedType = maybePinType(fe);
@ -1431,6 +1671,8 @@ FrameState::allocForSameBinary(FrameEntry *fe, JSOp op, BinaryAlloc &alloc)
if (alloc.lhsType.isSet())
unpinReg(alloc.lhsType.reg());
alloc.lhsFP = alloc.rhsFP = allocFPReg();
}
void
@ -1510,6 +1752,16 @@ FrameState::allocForBinary(FrameEntry *lhs, FrameEntry *rhs, JSOp op, BinaryAllo
pinReg(alloc.rhsType.reg());
}
/*
* Allocate floating point registers. These are temporaries with no pre-existing data;
* floating point registers are only allocated for known doubles, and BinaryAlloc is not
* used for such operations.
*/
JS_ASSERT(!backingLeft->isType(JSVAL_TYPE_DOUBLE));
JS_ASSERT(!backingRight->isType(JSVAL_TYPE_DOUBLE));
alloc.lhsFP = allocFPReg();
alloc.rhsFP = allocFPReg();
bool commu;
switch (op) {
case JSOP_EQ:

View File

@ -242,19 +242,14 @@ class FrameState
bool init(uint32 nargs);
/*
* Pushes a synced slot.
* Pushes a synced slot that may have a known type.
*/
inline void pushSynced();
inline void pushSynced(JSValueType knownType);
/*
* Pushes a slot that has a known, synced type and payload.
*/
inline void pushSyncedType(JSValueType type);
/*
* Pushes a slot that has a known, synced type and payload.
*/
inline void pushSynced(JSValueType type, RegisterID reg);
inline void pushSynced(JSValueType knownType, RegisterID reg);
/*
* Pushes a constant value.
@ -264,7 +259,7 @@ class FrameState
/*
* Loads a value from memory and pushes it.
*/
inline void push(Address address);
inline void push(Address address, JSValueType knownType);
/*
* Pushes a known type and allocated payload onto the operation stack.
@ -272,9 +267,23 @@ class FrameState
inline void pushTypedPayload(JSValueType type, RegisterID payload);
/*
* Pushes a type register and data register pair.
* Pushes a type register and data register pair, converting to the specified
* known type if necessary.
*/
inline void pushRegs(RegisterID type, RegisterID data);
inline void pushRegs(RegisterID type, RegisterID data, JSValueType knownType);
/* Push a value which is definitely a double. */
void pushDouble(FPRegisterID fpreg);
void pushDouble(Address address);
/* Ensure that fe is definitely a double. It must already be either int or double. */
void ensureDouble(FrameEntry *fe);
/* Ensure that in-memory fe is definitely a double. */
void ensureInMemoryDouble(FrameEntry *fe, Assembler &masm) const;
/* Forget that fe is definitely a double. */
void forgetKnownDouble(FrameEntry *fe);
/*
* Pushes a known type and allocated payload onto the operation stack.
@ -298,7 +307,7 @@ class FrameState
* was synced, then popping both and pushing a maybe-int32 does not need
* to be synced.
*/
inline void pushNumber(MaybeRegisterID payload, bool asInt32 = false);
inline void pushNumber(RegisterID payload, bool asInt32 = false);
/*
* Pushes an int32 onto the operation stack. This is a specialized version
@ -333,7 +342,7 @@ class FrameState
/*
* Pushes a copy of a local variable.
*/
void pushLocal(uint32 n);
void pushLocal(uint32 n, JSValueType knownType);
inline FrameEntry *getLocal(uint32 slot);
@ -359,6 +368,7 @@ class FrameState
* The compiler should NOT explicitly free it.
*/
inline RegisterID tempRegForData(FrameEntry *fe);
inline FPRegisterID tempFPRegForData(FrameEntry *fe);
/*
* Same as above, except register must match identically.
@ -413,14 +423,6 @@ class FrameState
void copyDataIntoReg(FrameEntry *fe, RegisterID exact);
RegisterID copyDataIntoReg(Assembler &masm, FrameEntry *fe);
/*
* Allocates a FPRegister for a FrameEntry, such that the compiler
* can modify it in-place. The FrameState is not modified.
*/
FPRegisterID copyEntryIntoFPReg(FrameEntry *fe, FPRegisterID fpreg);
FPRegisterID copyEntryIntoFPReg(Assembler &masm, FrameEntry *fe,
FPRegisterID fpreg);
/*
* Allocates a register for a FrameEntry's type, such that the compiler
* can modify it in-place. The actual FE is not modified.
@ -455,6 +457,8 @@ class FrameState
MaybeRegisterID rhsData;
MaybeRegisterID extraFree;
RegisterID result; // mutable result reg
FPRegisterID lhsFP; // mutable scratch floating point reg
FPRegisterID rhsFP; // mutable scratch floating point reg
bool resultHasRhs; // whether the result has the RHS instead of the LHS
bool lhsNeedsRemat; // whether LHS needs memory remat
bool rhsNeedsRemat; // whether RHS needs memory remat
@ -513,6 +517,7 @@ class FrameState
* is not spilled; the backing data becomes invalidated!
*/
inline void freeReg(RegisterID reg);
inline void freeFPReg(FPRegisterID reg);
/*
* Allocates a register. If none are free, one may be spilled from the
@ -520,6 +525,7 @@ class FrameState
* then this is considered a compiler bug and an assert will fire.
*/
inline RegisterID allocReg();
inline FPRegisterID allocFPReg();
/*
* Allocates a register, except using a mask.
@ -549,10 +555,13 @@ class FrameState
void loadForReturn(FrameEntry *fe, RegisterID typeReg, RegisterID dataReg, RegisterID tempReg);
/*
* Stores the top stack slot back to a slot.
* Stores the top stack slot back to a local or slot. type indicates any known
* type for the local/slot.
*/
void storeLocal(uint32 n, bool popGuaranteed = false, bool typeChange = true);
void storeTop(FrameEntry *target, bool popGuaranteed = false, bool typeChange = true);
void storeLocal(uint32 n, bool popGuaranteed = false,
JSValueType type = JSVAL_TYPE_UNKNOWN);
void storeTop(FrameEntry *target, bool popGuaranteed = false,
JSValueType type = JSVAL_TYPE_UNKNOWN);
/*
* Restores state from a slow path.
@ -601,9 +610,10 @@ class FrameState
void discardFrame();
/*
* Mark an existing slot with a type.
* Mark an existing slot with a type. unsync indicates whether type is already synced.
*/
inline void learnType(FrameEntry *fe, JSValueType type, bool unsync = true);
inline void learnType(FrameEntry *fe, JSValueType type, RegisterID payload);
/*
* Forget a type, syncing in the process.
@ -669,12 +679,14 @@ class FrameState
* no matter what. In addition, pinReg() can only be used on registers
* which are associated with FrameEntries.
*/
inline void pinReg(RegisterID reg);
inline void pinReg(RegisterID reg) { regstate[reg].pin(); }
inline void pinFPReg(FPRegisterID reg) { fpregstate[reg].pin(); }
/*
* Unpins a previously pinned register.
*/
inline void unpinReg(RegisterID reg);
inline void unpinReg(RegisterID reg) { regstate[reg].unpin(); }
inline void unpinFPReg(FPRegisterID reg) { fpregstate[reg].unpin(); }
/*
* Same as unpinReg(), but does not restore the FrameEntry.
@ -755,10 +767,13 @@ class FrameState
private:
inline RegisterID allocReg(FrameEntry *fe, RematInfo::RematType type);
inline void forgetReg(RegisterID reg);
inline void forgetFPReg(FPRegisterID reg);
RegisterID evictSomeReg(uint32 mask);
FPRegisterID evictSomeFPReg();
void evictReg(RegisterID reg);
inline FrameEntry *rawPush();
inline void addToTracker(FrameEntry *fe);
inline void setFPRegister(FrameEntry *fe, FPRegisterID fpreg, bool reassociate = false);
/* Guarantee sync, but do not set any sync flag. */
inline void ensureFeSynced(const FrameEntry *fe, Assembler &masm) const;
@ -826,6 +841,7 @@ class FrameState
/* All allocated registers. */
Registers freeRegs;
FPRegisters freeFPRegs;
/* Cache of FrameEntry objects. */
FrameEntry *entries;
@ -850,6 +866,7 @@ class FrameState
* entry is active, you must check the allocated registers.
*/
RegisterState regstate[Assembler::TotalRegisters];
RegisterState fpregstate[FPRegisters::TotalFPRegisters];
#if defined JS_NUNBOX32
mutable ImmutableSync reifier;

View File

@ -215,7 +215,7 @@ ImmutableSync::syncCopy(FrameEntry *fe)
Address addr = frame.addressOf(fe);
if (fe->isTypeKnown() && !e.learnedType) {
if (fe->isTypeKnown() && !fe->isType(JSVAL_TYPE_DOUBLE) && !e.learnedType) {
e.learnedType = true;
e.type = fe->getKnownType();
}
@ -238,7 +238,7 @@ ImmutableSync::syncNormal(FrameEntry *fe)
Address addr = frame.addressOf(fe);
if (fe->isTypeKnown()) {
if (fe->isTypeKnown() && !fe->isType(JSVAL_TYPE_DOUBLE)) {
e.learnedType = true;
e.type = fe->getKnownType();
}

View File

@ -254,7 +254,7 @@ struct FPRegisters {
typedef JSC::MacroAssembler::FPRegisterID FPRegisterID;
#if defined(JS_CPU_X86) || defined(JS_CPU_X64)
static const uint32 TotalFPRegisters = 8;
static const uint32 TotalFPRegisters = 7;
static const uint32 TempFPRegs =
(1 << JSC::X86Registers::xmm0)
| (1 << JSC::X86Registers::xmm1)
@ -262,25 +262,16 @@ struct FPRegisters {
| (1 << JSC::X86Registers::xmm3)
| (1 << JSC::X86Registers::xmm4)
| (1 << JSC::X86Registers::xmm5)
| (1 << JSC::X86Registers::xmm6)
| (1 << JSC::X86Registers::xmm7);
/* FIXME: Temporary hack until FPRegister allocation exists. */
static const FPRegisterID First = JSC::X86Registers::xmm0;
static const FPRegisterID Second = JSC::X86Registers::xmm1;
static const FPRegisterID Temp0 = JSC::X86Registers::xmm2;
static const FPRegisterID Temp1 = JSC::X86Registers::xmm3;
| (1 << JSC::X86Registers::xmm6);
/* For shuffling FP values around, or loading GPRs into a FP reg. */
static const FPRegisterID ConversionTemp = JSC::X86Registers::xmm7;
#elif defined(JS_CPU_ARM)
static const uint32 TotalFPRegisters = 4;
static const uint32 TotalFPRegisters = 3;
static const uint32 TempFPRegs =
(1 << JSC::ARMRegisters::d0)
| (1 << JSC::ARMRegisters::d1)
| (1 << JSC::ARMRegisters::d2)
| (1 << JSC::ARMRegisters::d3);
/* FIXME: Temporary hack until FPRegister allocation exists. */
static const FPRegisterID First = JSC::ARMRegisters::d0;
static const FPRegisterID Second = JSC::ARMRegisters::d1;
static const FPRegisterID Temp0 = JSC::ARMRegisters::d2;
static const FPRegisterID Temp1 = JSC::ARMRegisters::d3;
| (1 << JSC::ARMRegisters::d2);
static const FPRegisterID ConversionTemp = JSC::ARMRegisters::d3;
#else
# error "Unsupported platform"
#endif

View File

@ -59,7 +59,9 @@ struct ImmType : ImmTag
{
ImmType(JSValueType type)
: ImmTag(JSVAL_TYPE_TO_TAG(type))
{ }
{
JS_ASSERT(type > JSVAL_TYPE_DOUBLE);
}
};
struct ImmPayload : JSC::MacroAssembler::Imm32
@ -313,6 +315,21 @@ class NunboxAssembler : public JSC::MacroAssembler
loadPayload(address, dataReg);
return notHole;
}
/* :FIXME: borrowed from patch in bug 594247 */
void breakDouble(FPRegisterID srcDest, RegisterID typeReg, RegisterID dataReg) {
#ifdef JS_CPU_X86
// Move the low 32-bits of the 128-bit XMM register into dataReg.
// Then, right shift the 128-bit XMM register by 4 bytes.
// Finally, move the new low 32-bits of the 128-bit XMM register into typeReg.
m_assembler.movd_rr(srcDest, dataReg);
m_assembler.psrldq_rr(srcDest, 4);
m_assembler.movd_rr(srcDest, typeReg);
#else
JS_STATIC_ASSERT(0);
JS_NOT_REACHED("implement this - push double, pop pop is easiest");
#endif
}
};
typedef NunboxAssembler ValueAssembler;

View File

@ -66,7 +66,9 @@ struct ImmType : ImmTag
{
ImmType(JSValueType type)
: ImmTag(JSValueShiftedTag(JSVAL_TYPE_TO_SHIFTED_TAG(type)))
{ }
{
JS_ASSERT(type > JSVAL_TYPE_DOUBLE);
}
};
struct ImmPayload : Imm64
@ -326,6 +328,14 @@ class PunboxAssembler : public JSC::MacroAssembler
}
return notHole;
}
/* :FIXME: borrowed from patch in bug 594247 */
void breakDouble(FPRegisterID srcDest, RegisterID typeReg, RegisterID dataReg) {
m_assembler.movq_rr(srcDest, typeReg);
move(Registers::PayloadMaskReg, dataReg);
andPtr(typeReg, dataReg);
xorPtr(dataReg, typeReg);
}
};
typedef PunboxAssembler ValueAssembler;

View File

@ -200,6 +200,7 @@ struct ValueRemat {
*/
struct RematInfo {
typedef JSC::MacroAssembler::RegisterID RegisterID;
typedef JSC::MacroAssembler::FPRegisterID FPRegisterID;
enum SyncState {
SYNCED,
@ -221,9 +222,12 @@ struct RematInfo {
/* Backing bits are known at compile time. */
PhysLoc_Constant,
/* Backing bits are in a register. */
/* Backing bits are in a general purpose register. */
PhysLoc_Register,
/* Backing bits are part of a floating point register. */
PhysLoc_FPRegister,
/* Backing bits are invalid/unknown. */
PhysLoc_Invalid
};
@ -238,6 +242,16 @@ struct RematInfo {
return reg_;
}
void setFPRegister(FPRegisterID reg) {
fpreg_ = reg;
location_ = PhysLoc_FPRegister;
}
FPRegisterID fpreg() const {
JS_ASSERT(inFPRegister());
return fpreg_;
}
void setMemory() {
location_ = PhysLoc_Memory;
sync_ = SYNCED;
@ -251,6 +265,7 @@ struct RematInfo {
bool isConstant() const { return location_ == PhysLoc_Constant; }
bool inRegister() const { return location_ == PhysLoc_Register; }
bool inFPRegister() const { return location_ == PhysLoc_FPRegister; }
bool inMemory() const { return location_ == PhysLoc_Memory; }
bool synced() const { return sync_ == SYNCED; }
void sync() {
@ -267,8 +282,16 @@ struct RematInfo {
}
private:
/* Set if location is PhysLoc_Register. */
RegisterID reg_;
union {
/* Set if location is PhysLoc_Register. */
RegisterID reg_;
/*
* Set if location is PhysLoc_FPRegister. This must be the data for a FE,
* and the known type is JSVAL_TYPE_DOUBLE.
*/
FPRegisterID fpreg_;
};
/* Remat source. */
PhysLoc location_;