From bf470f73e56735c46f2224679a8cda37a26c2322 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Sun, 30 May 2010 19:44:29 -0700 Subject: [PATCH] [JAEGER] Added JSOP_GLOBALINC, JSOP_GLOBALDEC, JSOP_INCGLOBAL, JSOP_DECGLOBAL. --- js/src/methodjit/Compiler.cpp | 9 ++++ js/src/methodjit/Compiler.h | 1 + js/src/methodjit/FrameState-inl.h | 53 +++++++++++++++--------- js/src/methodjit/FrameState.cpp | 64 ++++++++++++++++++++++++++--- js/src/methodjit/FrameState.h | 45 +++++++++++++++++--- js/src/methodjit/MethodJIT.h | 2 +- js/src/methodjit/RematInfo.h | 8 +--- js/src/methodjit/StubCalls.cpp | 56 +++++++++++++++++++++++++ js/src/methodjit/StubCalls.h | 5 +++ js/src/methodjit/StubCompiler.cpp | 44 +++++++++++++++++++- js/src/methodjit/StubCompiler.h | 7 ++++ js/src/methodjit/nunbox/Assembler.h | 8 ++++ js/src/methodjit/nunbox/FastOps.cpp | 61 +++++++++++++++++++++++++-- 13 files changed, 321 insertions(+), 42 deletions(-) diff --git a/js/src/methodjit/Compiler.cpp b/js/src/methodjit/Compiler.cpp index f4be4c47c6b..a2f8c3d4a27 100644 --- a/js/src/methodjit/Compiler.cpp +++ b/js/src/methodjit/Compiler.cpp @@ -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 diff --git a/js/src/methodjit/Compiler.h b/js/src/methodjit/Compiler.h index a6bb594c619..485bb5c0173 100644 --- a/js/src/methodjit/Compiler.h +++ b/js/src/methodjit/Compiler.h @@ -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) { \ diff --git a/js/src/methodjit/FrameState-inl.h b/js/src/methodjit/FrameState-inl.h index 0db804b546f..308c49f40cb 100644 --- a/js/src/methodjit/FrameState-inl.h +++ b/js/src/methodjit/FrameState-inl.h @@ -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 */ diff --git a/js/src/methodjit/FrameState.cpp b/js/src/methodjit/FrameState.cpp index 2a4fd69c138..2546e457393 100644 --- a/js/src/methodjit/FrameState.cpp +++ b/js/src/methodjit/FrameState.cpp @@ -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; +} + diff --git a/js/src/methodjit/FrameState.h b/js/src/methodjit/FrameState.h index a0cfd967624..810c58beeea 100644 --- a/js/src/methodjit/FrameState.h +++ b/js/src/methodjit/FrameState.h @@ -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. */ diff --git a/js/src/methodjit/MethodJIT.h b/js/src/methodjit/MethodJIT.h index 14cc7630794..0352eeb55ee 100644 --- a/js/src/methodjit/MethodJIT.h +++ b/js/src/methodjit/MethodJIT.h @@ -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 &); diff --git a/js/src/methodjit/RematInfo.h b/js/src/methodjit/RematInfo.h index 86b6af9d8fe..a85d35b5dae 100644 --- a/js/src/methodjit/RematInfo.h +++ b/js/src/methodjit/RematInfo.h @@ -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; } diff --git a/js/src/methodjit/StubCalls.cpp b/js/src/methodjit/StubCalls.cpp index a2bd0404e2e..779ecbea110 100644 --- a/js/src/methodjit/StubCalls.cpp +++ b/js/src/methodjit/StubCalls.cpp @@ -624,3 +624,59 @@ mjit::stubs::BitAnd(VMFrame &f) f.regs.sp[-2].setInt32(i); } +template +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 +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(); +} + diff --git a/js/src/methodjit/StubCalls.h b/js/src/methodjit/StubCalls.h index 097f1238398..7a8d456e70c 100644 --- a/js/src/methodjit/StubCalls.h +++ b/js/src/methodjit/StubCalls.h @@ -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 */ diff --git a/js/src/methodjit/StubCompiler.cpp b/js/src/methodjit/StubCompiler.cpp index ec0e6776eb2..5c5530f7356 100644 --- a/js/src/methodjit/StubCompiler.cpp +++ b/js/src/methodjit/StubCompiler.cpp @@ -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); +} + diff --git a/js/src/methodjit/StubCompiler.h b/js/src/methodjit/StubCompiler.h index c4e5743d491..7e7dfcea82e 100644 --- a/js/src/methodjit/StubCompiler.h +++ b/js/src/methodjit/StubCompiler.h @@ -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 */ diff --git a/js/src/methodjit/nunbox/Assembler.h b/js/src/methodjit/nunbox/Assembler.h index 5c5b3c5f3f4..39f5b0ea1cb 100644 --- a/js/src/methodjit/nunbox/Assembler.h +++ b/js/src/methodjit/nunbox/Assembler.h @@ -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 */ diff --git a/js/src/methodjit/nunbox/FastOps.cpp b/js/src/methodjit/nunbox/FastOps.cpp index efbd07e49d3..7e4b26050ca 100644 --- a/js/src/methodjit/nunbox/FastOps.cpp +++ b/js/src/methodjit/nunbox/FastOps.cpp @@ -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); +} +