[JAEGER] Added JSOP_GLOBALINC, JSOP_GLOBALDEC, JSOP_INCGLOBAL, JSOP_DECGLOBAL.

This commit is contained in:
David Anderson 2010-05-30 19:44:29 -07:00
parent aad4b0b970
commit bf470f73e5
13 changed files with 321 additions and 42 deletions

View File

@ -349,6 +349,15 @@ mjit::Compiler::generateMethod()
jsop_setglobal(GET_SLOTNO(PC));
END_CASE(JSOP_SETGLOBAL)
BEGIN_CASE(JSOP_INCGLOBAL)
BEGIN_CASE(JSOP_DECGLOBAL)
BEGIN_CASE(JSOP_GLOBALINC)
BEGIN_CASE(JSOP_GLOBALDEC)
/* Advances PC automatically. */
jsop_globalinc(op, GET_SLOTNO(PC));
break;
END_CASE(JSOP_GLOBALINC)
default:
/* Sorry, this opcode isn't implemented yet. */
#ifdef JS_METHODJIT_SPEW

View File

@ -129,6 +129,7 @@ class Compiler
/* Fast opcodes. */
void jsop_bitop(JSOp op);
void jsop_globalinc(JSOp op, uint32 index);
#define STUB_CALL_TYPE(type) \
Call stubCall(type stub, Uses uses, Defs defs) { \

View File

@ -115,7 +115,8 @@ FrameState::pop()
inline void
FrameState::freeReg(RegisterID reg)
{
forgetReg(reg);
JS_ASSERT(regstate[reg].fe == NULL);
freeRegs.putReg(reg);
}
inline void
@ -215,6 +216,25 @@ FrameState::pushTypedPayload(uint32 tag, RegisterID payload)
regstate[payload] = RegisterState(fe, RematInfo::DATA, true);
}
inline void
FrameState::pushUntypedPayload(uint32 tag, RegisterID payload)
{
JS_ASSERT(!freeRegs.hasReg(payload));
FrameEntry *fe = rawPush();
/* The forceful type sync will assert otherwise. */
#ifdef DEBUG
fe->type.unsync();
#endif
masm.storeTypeTag(Imm32(tag), addressOf(fe));
fe->type.sync();
fe->data.unsync();
fe->data.setRegister(payload);
regstate[payload] = RegisterState(fe, RematInfo::DATA, true);
}
inline JSC::MacroAssembler::RegisterID
FrameState::tempRegForType(FrameEntry *fe)
{
@ -245,26 +265,10 @@ FrameState::tempRegForData(FrameEntry *fe)
return reg;
}
inline JSC::MacroAssembler::RegisterID
FrameState::ownRegForData(FrameEntry *fe)
inline bool
FrameState::shouldAvoidTypeRemat(FrameEntry *fe)
{
JS_ASSERT(!fe->data.isConstant());
if (fe->data.inRegister()) {
/* Remove ownership of this register. */
RegisterID reg = fe->data.reg();
JS_ASSERT(regstate[reg].fe == fe);
JS_ASSERT(regstate[reg].type == RematInfo::DATA);
regstate[reg].fe = NULL;
fe->data.invalidate();
return reg;
}
/* :XXX: X64 */
RegisterID reg = alloc(fe, RematInfo::DATA, true);
masm.loadData32(addressOf(fe), reg);
return reg;
return fe->type.inMemory();
}
inline bool
@ -306,6 +310,7 @@ FrameState::syncData(const FrameEntry *fe, Assembler &masm) const
inline void
FrameState::learnType(FrameEntry *fe, uint32 tag)
{
JS_ASSERT(!fe->type.inRegister());
fe->setTypeTag(tag);
}
@ -317,6 +322,14 @@ FrameState::addressOf(const FrameEntry *fe) const
return Address(Assembler::FpReg, sizeof(JSStackFrame) + sizeof(Value) * index);
}
inline JSC::MacroAssembler::Jump
FrameState::testInt32(Assembler::Condition cond, FrameEntry *fe)
{
if (shouldAvoidTypeRemat(fe))
return masm.testInt32(cond, addressOf(fe));
return masm.testInt32(cond, tempRegForType(fe));
}
} /* namspace mjit */
} /* namspace js */

View File

@ -124,11 +124,11 @@ FrameState::storeTo(FrameEntry *fe, Address address, bool popped)
JS_ASSERT(!freeRegs.hasReg(address.base));
if (fe->data.inRegister()) {
masm.storeData32(fe->data.reg(), addressOf(fe));
masm.storeData32(fe->data.reg(), address);
} else {
RegisterID reg = popped ? alloc() : alloc(fe, RematInfo::DATA, true);
masm.loadData32(addressOf(fe), reg);
masm.storeData32(reg, addressOf(fe));
masm.storeData32(reg, address);
if (popped)
freeReg(reg);
else
@ -136,13 +136,13 @@ FrameState::storeTo(FrameEntry *fe, Address address, bool popped)
}
if (fe->isTypeKnown()) {
masm.storeTypeTag(Imm32(fe->getTypeTag()), addressOf(fe));
masm.storeTypeTag(Imm32(fe->getTypeTag()), address);
} else if (fe->type.inRegister()) {
masm.storeTypeTag(fe->type.reg(), addressOf(fe));
masm.storeTypeTag(fe->type.reg(), address);
} else {
RegisterID reg = popped ? alloc() : alloc(fe, RematInfo::TYPE, true);
masm.loadTypeTag(addressOf(fe), reg);
masm.storeTypeTag(reg, addressOf(fe));
masm.storeTypeTag(reg, address);
if (popped)
freeReg(reg);
else
@ -252,3 +252,57 @@ FrameState::merge(Assembler &masm, uint32 iVD) const
}
}
JSC::MacroAssembler::RegisterID
FrameState::copyData(FrameEntry *fe)
{
JS_ASSERT(!fe->data.isConstant());
if (fe->data.inRegister()) {
RegisterID reg = fe->data.reg();
if (freeRegs.empty()) {
if (!fe->data.synced())
syncData(fe, masm);
fe->data.setMemory();
regstate[reg].fe = NULL;
} else {
RegisterID newReg = alloc();
masm.move(reg, newReg);
reg = newReg;
}
return reg;
}
RegisterID reg = alloc();
if (!freeRegs.empty())
masm.move(tempRegForData(fe), reg);
else
masm.loadData32(addressOf(fe),reg);
return reg;
}
JSC::MacroAssembler::RegisterID
FrameState::ownRegForData(FrameEntry *fe)
{
JS_ASSERT(!fe->data.isConstant());
/* :XXX: X64 */
if (fe->data.inRegister()) {
RegisterID reg = fe->data.reg();
/* Remove ownership of this register. */
JS_ASSERT(regstate[reg].fe == fe);
JS_ASSERT(regstate[reg].type == RematInfo::DATA);
regstate[reg].fe = NULL;
fe->data.invalidate();
return reg;
}
JS_ASSERT(fe->data.inMemory());
RegisterID reg = alloc();
masm.loadData32(addressOf(fe), reg);
return reg;
}

View File

@ -89,6 +89,7 @@ class FrameState
{
typedef JSC::MacroAssembler::RegisterID RegisterID;
typedef JSC::MacroAssembler::Address Address;
typedef JSC::MacroAssembler::Jump Jump;
typedef JSC::MacroAssembler::Imm32 Imm32;
struct Tracker {
@ -160,6 +161,13 @@ class FrameState
*/
inline void pushTypedPayload(uint32 tag, RegisterID payload);
/*
* Pushes a known type and allocated payload onto the operation stack.
* This must be used when the type is known, but cannot be propagated
* because it is not known to be correct at a slow-path merge point.
*/
inline void pushUntypedPayload(uint32 tag, RegisterID payload);
/*
* Pops a value off the operation stack, freeing any of its resources.
*/
@ -172,21 +180,42 @@ class FrameState
inline void popn(uint32 n);
/*
* Allocates a temporary register for a FrameEntry's type.
* Allocates a temporary register for a FrameEntry's type. The register
* can be spilled or clobbered by the frame. The compiler may only operate
* on it temporarily, and must take care not to clobber it.
*/
inline RegisterID tempRegForType(FrameEntry *fe);
/*
* Allocates a data register for a FrameEntry's type.
* Returns a register that is guaranteed to contain the frame entry's
* data payload. The compiler may not modify the contents of the register,
* though it may explicitly free it.
*/
inline RegisterID tempRegForData(FrameEntry *fe);
/*
* Allocates a register for a FrameEntry's data, such that the compiler
* can modify it in-place. If the slot already has a temporary register,
* it is cleared, and thus the entry is invalidated!
* can modify it in-place.
*
* The caller guarantees the FrameEntry will not be observed again. This
* allows the compiler to avoid spilling. Only call this if the FE is
* going to be popped before stubcc joins/guards or the end of the current
* opcode.
*/
inline RegisterID ownRegForData(FrameEntry *fe);
RegisterID ownRegForData(FrameEntry *fe);
/*
* Allocates a register for a FrameEntry's data, such that the compiler
* can modify it in-place. The actual FE is not modified.
*/
RegisterID copyData(FrameEntry *fe);
/*
* Types don't always have to be in registers, sometimes the compiler
* can use addresses and avoid spilling. If this FrameEntry has a synced
* address and no register, this returns true.
*/
inline bool shouldAvoidTypeRemat(FrameEntry *fe);
/*
* Payloads don't always have to be in registers, sometimes the compiler
@ -253,6 +282,12 @@ class FrameState
*/
inline void learnType(FrameEntry *fe, uint32 tag);
/*
* Helper function. Tests if a slot's type is an integer. Condition should
* be Equal or NotEqual.
*/
inline Jump testInt32(Assembler::Condition cond, FrameEntry *fe);
/*
* Returns the current stack depth of the frame.
*/

View File

@ -180,7 +180,7 @@ extern "C" void JaegerStubVeneer(void);
#endif
typedef void (JS_FASTCALL *VoidStub)(VMFrame &);
typedef void (JS_FASTCALL *VoidVpStub)(VMFrame &, jsval *);
typedef void (JS_FASTCALL *VoidVpStub)(VMFrame &, Value *);
typedef void (JS_FASTCALL *VoidStubUInt32)(VMFrame &, uint32);
typedef void (JS_FASTCALL *VoidStubInt32)(VMFrame &, int32);
typedef JSBool (JS_FASTCALL *BoolStub)(VMFrame &);

View File

@ -72,12 +72,10 @@ struct RematInfo {
PhysLoc_Constant,
/* Backing bits are in a register. */
PhysLoc_Register
PhysLoc_Register,
#ifdef DEBUG
/* Backing bits are invalid/unknown. */
, PhysLoc_Invalid
#endif
PhysLoc_Invalid
};
void setRegister(RegisterID reg) {
@ -96,9 +94,7 @@ struct RematInfo {
}
void invalidate() {
#ifdef DEBUG
location_ = PhysLoc_Invalid;
#endif
}
void setConstant() { location_ = PhysLoc_Constant; }

View File

@ -624,3 +624,59 @@ mjit::stubs::BitAnd(VMFrame &f)
f.regs.sp[-2].setInt32(i);
}
template <int32 N>
static inline bool
PostInc(VMFrame &f, Value *vp)
{
double d;
if (!ValueToNumber(f.cx, *vp, &d))
return false;
f.regs.sp++;
f.regs.sp[-1].setDouble(d);
d += N;
vp->setDouble(d);
return true;
}
template <int32 N>
static inline bool
PreInc(VMFrame &f, Value *vp)
{
double d;
if (!ValueToNumber(f.cx, *vp, &d))
return false;
d += N;
vp->setDouble(d);
f.regs.sp++;
f.regs.sp[-1].setDouble(d);
return true;
}
void JS_FASTCALL
stubs::VpInc(VMFrame &f, Value *vp)
{
if (!PostInc<1>(f, vp))
THROW();
}
void JS_FASTCALL
stubs::VpDec(VMFrame &f, Value *vp)
{
if (!PostInc<-1>(f, vp))
THROW();
}
void JS_FASTCALL
stubs::DecVp(VMFrame &f, Value *vp)
{
if (!PreInc<-1>(f, vp))
THROW();
}
void JS_FASTCALL
stubs::IncVp(VMFrame &f, Value *vp)
{
if (!PreInc<1>(f, vp))
THROW();
}

View File

@ -53,6 +53,11 @@ JSObject * JS_FASTCALL BindName(VMFrame &f);
void JS_FASTCALL SetName(VMFrame &f, uint32 index);
void JS_FASTCALL Name(VMFrame &f, uint32 index);
void JS_FASTCALL VpInc(VMFrame &f, Value *vp);
void JS_FASTCALL VpDec(VMFrame &f, Value *vp);
void JS_FASTCALL DecVp(VMFrame &f, Value *vp);
void JS_FASTCALL IncVp(VMFrame &f, Value *vp);
void JS_FASTCALL BitAnd(VMFrame &f);
}}} /* namespace stubs,mjit,js */

View File

@ -38,6 +38,7 @@
*
* ***** END LICENSE BLOCK ***** */
#include "StubCalls.h"
#include "StubCompiler.h"
#include "Compiler.h"
#include "assembler/assembler/LinkBuffer.h"
@ -106,10 +107,16 @@ typedef JSC::MacroAssembler::Imm32 Imm32;
JSC::MacroAssembler::Call
StubCompiler::stubCall(void *ptr)
{
return stubCall(ptr, frame.stackDepth() + script->nfixed);
}
JSC::MacroAssembler::Call
StubCompiler::stubCall(void *ptr, uint32 slots)
{
generation++;
JaegerSpew(JSpew_Insns, " ---- BEGIN SLOW CALL CODE ---- \n");
Call cl = masm.stubCall(ptr, cc.getPC(), frame.stackDepth() + script->nfixed);
Call cl = masm.stubCall(ptr, cc.getPC(), slots);
JaegerSpew(JSpew_Insns, " ---- END SLOW CALL CODE ---- \n");
return cl;
}
@ -133,3 +140,38 @@ StubCompiler::finalize(uint8 *ncode)
masm.finalize(ncode);
}
JSC::MacroAssembler::Call
StubCompiler::vpInc(JSOp op, bool pushed)
{
uint32 slots = frame.stackDepth() + script->nfixed;
if (pushed) {
JS_ASSERT(frame.stackDepth());
slots--;
}
VoidVpStub stub = NULL;
switch (op) {
case JSOP_GLOBALINC:
stub = stubs::VpInc;
break;
case JSOP_GLOBALDEC:
stub = stubs::VpDec;
break;
case JSOP_INCGLOBAL:
stub = stubs::IncVp;
break;
case JSOP_DECGLOBAL:
stub = stubs::DecVp;
break;
default:
JS_NOT_REACHED("unknown incdec op");
break;
}
return stubCall(JS_FUNC_TO_DATA_PTR(void *, stub), slots);
}

View File

@ -72,7 +72,11 @@ class StubCompiler
Compiler &cc;
FrameState &frame;
JSScript *script;
public:
Assembler masm;
private:
uint32 generation;
uint32 lastGeneration;
@ -94,6 +98,8 @@ class StubCompiler
return masm.buffer();
}
Call vpInc(JSOp op, bool pushed);
#define STUB_CALL_TYPE(type) \
Call call(type stub) { \
return stubCall(JS_FUNC_TO_DATA_PTR(void *, stub)); \
@ -122,6 +128,7 @@ class StubCompiler
private:
Call stubCall(void *ptr);
Call stubCall(void *ptr, uint32 slots);
};
} /* namepsace mjit */

View File

@ -91,6 +91,14 @@ class Assembler : public BaseAssembler
if (!v.isUndefined())
store32(Imm32(jv.s.payload.u32), payloadOf(address));
}
Jump testInt32(Assembler::Condition cond, RegisterID reg) {
return branch32(cond, reg, Imm32(JSVAL_MASK32_INT32));
}
Jump testInt32(Assembler::Condition cond, Address address) {
return branch32(cond, tagOf(address), Imm32(JSVAL_MASK32_INT32));
}
};
} /* namespace js */

View File

@ -85,16 +85,14 @@ mjit::Compiler::jsop_bitop(JSOp op)
/* Test the types. */
if (!rhs->isTypeKnown()) {
RegisterID reg = frame.tempRegForType(rhs);
Jump rhsFail = masm.branch32(Assembler::NotEqual, reg, Imm32(JSVAL_MASK32_INT32));
Jump rhsFail = masm.testInt32(Assembler::NotEqual, reg);
stubcc.linkExit(rhsFail);
frame.freeReg(reg);
frame.learnType(rhs, JSVAL_MASK32_INT32);
}
if (!lhs->isTypeKnown()) {
RegisterID reg = frame.tempRegForType(lhs);
Jump lhsFail = masm.branch32(Assembler::NotEqual, reg, Imm32(JSVAL_MASK32_INT32));
Jump lhsFail = masm.testInt32(Assembler::NotEqual, reg);
stubcc.linkExit(lhsFail);
frame.freeReg(reg);
}
stubcc.leave();
@ -153,3 +151,58 @@ mjit::Compiler::jsop_bitop(JSOp op)
stubcc.rejoin(2);
}
void
mjit::Compiler::jsop_globalinc(JSOp op, uint32 index)
{
uint32 slot = script->getGlobalSlot(index);
bool popped = false;
PC += JSOP_GLOBALINC_LENGTH;
if (JSOp(*PC) == JSOP_POP && !analysis[PC].nincoming) {
popped = true;
PC += JSOP_POP_LENGTH;
}
int amt = (js_CodeSpec[op].format & JOF_INC) ? 1 : -1;
bool post = !!(js_CodeSpec[op].format & JOF_POST);
RegisterID data;
RegisterID reg = frame.allocReg();
Address addr = masm.objSlotRef(globalObj, reg, slot);
if (post && !popped) {
frame.push(addr);
FrameEntry *fe = frame.peek(-1);
Jump notInt = frame.testInt32(Assembler::NotEqual, fe);
stubcc.linkExit(notInt);
data = frame.copyData(fe);
} else {
Jump notInt = masm.testInt32(Assembler::NotEqual, addr);
stubcc.linkExit(notInt);
data = frame.allocReg();
masm.loadData32(addr, data);
}
Jump ovf;
if (amt > 0)
ovf = masm.branchAdd32(Assembler::Overflow, Imm32(1), data);
else
ovf = masm.branchSub32(Assembler::Overflow, Imm32(1), data);
stubcc.linkExit(ovf);
stubcc.leave();
stubcc.masm.lea(addr, Registers::ArgReg1);
stubcc.vpInc(op, post && !popped);
masm.storeData32(data, addr);
if (!post && !popped)
frame.pushUntypedPayload(JSVAL_MASK32_INT32, data);
else
frame.freeReg(data);
frame.freeReg(reg);
stubcc.rejoin(1);
}