Refactor and improve GETELEM IC (bug 602641, r=dmandelin).

This commit is contained in:
David Anderson 2010-10-27 21:04:13 -07:00
parent 3a40c011d1
commit 41328585a9
13 changed files with 955 additions and 1085 deletions

View File

@ -50,6 +50,8 @@
#include "methodjit/MethodJIT.h"
#include "methodjit/MachineRegs.h"
#include "CodeGenIncludes.h"
#include "jsobjinlines.h"
#include "jsscopeinlines.h"
namespace js {
namespace mjit {
@ -209,9 +211,9 @@ static const JSC::MacroAssembler::RegisterID JSParamReg_Argc = JSC::ARMRegiste
load32(Address(obj, offsetof(JSObject, objShape)), shape);
}
Jump guardShape(RegisterID obj, uint32 shape) {
return branch32(NotEqual, Address(obj, offsetof(JSObject, objShape)),
Imm32(shape));
Jump guardShape(RegisterID objReg, JSObject *obj) {
return branch32(NotEqual, Address(objReg, offsetof(JSObject, objShape)),
Imm32(obj->shape()));
}
Jump testFunction(Condition cond, RegisterID fun) {
@ -427,6 +429,24 @@ static const JSC::MacroAssembler::RegisterID JSParamReg_Argc = JSC::ARMRegiste
else
move(remat.reg(), reg);
}
void loadDynamicSlot(RegisterID objReg, uint32 slot,
RegisterID typeReg, RegisterID dataReg) {
loadPtr(Address(objReg, offsetof(JSObject, slots)), dataReg);
loadValueAsComponents(Address(dataReg, slot * sizeof(Value)), typeReg, dataReg);
}
void loadObjProp(JSObject *obj, RegisterID objReg,
const js::Shape *shape,
RegisterID typeReg, RegisterID dataReg)
{
if (shape->isMethod())
loadValueAsComponents(ObjectValue(shape->methodObject()), typeReg, dataReg);
else if (obj->hasSlotsArray())
loadDynamicSlot(objReg, shape->slot, typeReg, dataReg);
else
loadInlineSlot(objReg, shape->slot, typeReg, dataReg);
}
};
/* Return f<true> if the script is strict mode code, f<false> otherwise. */

View File

@ -48,19 +48,7 @@
namespace js {
namespace mjit {
class BaseCompiler
{
protected:
JSContext *cx;
public:
BaseCompiler() : cx(NULL)
{ }
BaseCompiler(JSContext *cx) : cx(cx)
{ }
protected:
struct MacroAssemblerTypedefs {
typedef JSC::MacroAssembler::Label Label;
typedef JSC::MacroAssembler::Imm32 Imm32;
typedef JSC::MacroAssembler::ImmPtr ImmPtr;
@ -77,11 +65,25 @@ class BaseCompiler
typedef JSC::MacroAssembler::DataLabel32 DataLabel32;
typedef JSC::FunctionPtr FunctionPtr;
typedef JSC::RepatchBuffer RepatchBuffer;
typedef JSC::CodeBlock CodeBlock;
typedef JSC::CodeLocationLabel CodeLocationLabel;
typedef JSC::JITCode JITCode;
typedef JSC::CodeLocationCall CodeLocationCall;
typedef JSC::ReturnAddressPtr ReturnAddressPtr;
typedef JSC::MacroAssemblerCodePtr MacroAssemblerCodePtr;
};
class BaseCompiler : public MacroAssemblerTypedefs
{
protected:
JSContext *cx;
public:
BaseCompiler() : cx(NULL)
{ }
BaseCompiler(JSContext *cx) : cx(cx)
{ }
protected:
JSC::ExecutablePool *
getExecPool(size_t size) {
@ -127,6 +129,12 @@ class LinkerHelper : public JSC::LinkBuffer
}
return ep;
}
void maybeLink(MaybeJump jump, JSC::CodeLocationLabel label) {
if (!jump.isSet())
return;
link(jump.get(), label);
}
};
} /* namespace js */

View File

@ -103,6 +103,7 @@ mjit::Compiler::Compiler(JSContext *cx, JSStackFrame *fp)
#endif
#if defined JS_POLYIC
pics(ContextAllocPolicy(cx)),
getElemICs(ContextAllocPolicy(cx)),
#endif
callPatches(ContextAllocPolicy(cx)),
callSites(ContextAllocPolicy(cx)),
@ -390,6 +391,7 @@ mjit::Compiler::finishThisUp(JITScript **jitp)
#endif
#if defined JS_POLYIC
sizeof(ic::PICInfo) * pics.length() +
sizeof(ic::GetElementIC) * getElemICs.length() +
#endif
sizeof(CallSite) * callSites.length();
@ -586,6 +588,38 @@ mjit::Compiler::finishThisUp(JITScript **jitp)
}
#if defined JS_POLYIC
jit->nGetElems = getElemICs.length();
if (getElemICs.length()) {
jit->getElems = (ic::GetElementIC *)cursor;
cursor += sizeof(ic::GetElementIC) * getElemICs.length();
} else {
jit->getElems = NULL;
}
for (size_t i = 0; i < getElemICs.length(); i++) {
ic::GetElementIC &to = jit->getElems[i];
GetElementICInfo &from = getElemICs[i];
to.init();
from.copyTo(to, fullCode, stubCode);
to.typeReg = from.typeReg;
to.objReg = from.objReg;
to.idRemat = from.id;
if (from.typeGuard.isSet()) {
int inlineTypeGuard = fullCode.locationOf(from.typeGuard.get()) -
fullCode.locationOf(from.fastPathStart);
to.inlineTypeGuard = inlineTypeGuard;
JS_ASSERT(to.inlineTypeGuard == inlineTypeGuard);
}
int inlineClaspGuard = fullCode.locationOf(from.claspGuard) -
fullCode.locationOf(from.fastPathStart);
to.inlineClaspGuard = inlineClaspGuard;
JS_ASSERT(to.inlineClaspGuard == inlineClaspGuard);
stubCode.patch(from.paramAddr, &to);
}
jit->nPICs = pics.length();
if (pics.length()) {
jit->pics = (ic::PICInfo *)cursor;
@ -596,11 +630,9 @@ mjit::Compiler::finishThisUp(JITScript **jitp)
if (ic::PICInfo *scriptPICs = jit->pics) {
for (size_t i = 0; i < pics.length(); i++) {
scriptPICs[i].init();
pics[i].copyTo(scriptPICs[i], fullCode, stubCode);
pics[i].copySimpleMembersTo(scriptPICs[i]);
scriptPICs[i].fastPathStart = fullCode.locationOf(pics[i].fastPathStart);
scriptPICs[i].fastPathRejoin = fullCode.locationOf(pics[i].fastPathRejoin);
scriptPICs[i].slowPathStart = stubCode.locationOf(pics[i].slowPathStart);
scriptPICs[i].slowPathCall = stubCode.locationOf(pics[i].slowPathCall);
scriptPICs[i].shapeGuard = masm.distanceOf(pics[i].shapeGuard) -
masm.distanceOf(pics[i].fastPathStart);
JS_ASSERT(scriptPICs[i].shapeGuard == masm.distanceOf(pics[i].shapeGuard) -
@ -623,8 +655,6 @@ mjit::Compiler::finishThisUp(JITScript **jitp)
scriptPICs[i].u.get.typeCheckOffset = distance;
}
}
new (&scriptPICs[i].execPools) ic::PICInfo::ExecPoolVector(SystemAllocPolicy());
scriptPICs[i].reset();
stubCode.patch(pics[i].paramAddr, &scriptPICs[i]);
}
}
@ -2593,9 +2623,9 @@ mjit::Compiler::passMICAddress(MICGenInfo &mic)
#if defined JS_POLYIC
void
mjit::Compiler::passPICAddress(PICGenInfo &pic)
mjit::Compiler::passICAddress(BaseICInfo *ic)
{
pic.paramAddr = stubcc.masm.moveWithPatch(ImmPtr(NULL), Registers::ArgReg1);
ic->paramAddr = stubcc.masm.moveWithPatch(ImmPtr(NULL), Registers::ArgReg1);
}
bool
@ -2623,7 +2653,7 @@ mjit::Compiler::jsop_getprop(JSAtom *atom, bool doTypeCheck, bool usePropCache)
shapeReg = frame.allocReg();
}
PICGenInfo pic(ic::PICInfo::GET, usePropCache);
PICGenInfo pic(ic::PICInfo::GET, JSOp(*PC), usePropCache);
/* Guard that the type is an object. */
Jump typeCheck;
@ -2654,7 +2684,6 @@ mjit::Compiler::jsop_getprop(JSAtom *atom, bool doTypeCheck, bool usePropCache)
pic.shapeReg = shapeReg;
pic.atom = atom;
pic.objRemat = frame.dataRematInfo(top);
/* Guard on shape. */
masm.loadShape(objReg, shapeReg);
@ -2669,7 +2698,7 @@ mjit::Compiler::jsop_getprop(JSAtom *atom, bool doTypeCheck, bool usePropCache)
pic.slowPathStart = stubcc.linkExit(j, Uses(1));
stubcc.leave();
passPICAddress(pic);
passICAddress(&pic);
pic.slowPathCall = stubcc.call(ic::GetProp);
/* Load dslots. */
@ -2727,113 +2756,6 @@ mjit::Compiler::jsop_getprop(JSAtom *atom, bool doTypeCheck, bool usePropCache)
return true;
}
#ifdef JS_POLYIC
bool
mjit::Compiler::jsop_getelem_pic(FrameEntry *obj, FrameEntry *id, RegisterID objReg,
RegisterID idReg, RegisterID shapeReg)
{
PICGenInfo pic(ic::PICInfo::GETELEM, true);
pic.objRemat = frame.dataRematInfo(obj);
pic.idRemat = frame.dataRematInfo(id);
pic.shapeReg = shapeReg;
pic.hasTypeCheck = false;
pic.fastPathStart = masm.label();
/* Guard on shape. */
masm.loadShape(objReg, shapeReg);
pic.shapeGuard = masm.label();
DataLabel32 inlineShapeOffsetLabel;
Jump jmpShapeGuard = masm.branch32WithPatch(Assembler::NotEqual, shapeReg,
Imm32(int32(JSObjectMap::INVALID_SHAPE)),
inlineShapeOffsetLabel);
DBGLABEL(dbgInlineShapeJump);
/* Guard on id identity. */
#if defined JS_NUNBOX32
static const void *BOGUS_ATOM = (void *)0xdeadbeef;
#elif defined JS_PUNBOX64
static const void *BOGUS_ATOM = (void *)0xfeedfacedeadbeef;
#endif
DataLabelPtr inlineAtomOffsetLabel;
Jump idGuard = masm.branchPtrWithPatch(Assembler::NotEqual, idReg,
inlineAtomOffsetLabel, ImmPtr(BOGUS_ATOM));
DBGLABEL(dbgInlineAtomJump);
/*
* The state between these two exits is identical, so this safe. The
* GETELEM PIC repatches both jumps to the slowPathStart on reset.
*/
stubcc.linkExit(idGuard, Uses(2));
pic.slowPathStart = stubcc.linkExit(jmpShapeGuard, Uses(2));
stubcc.leave();
passPICAddress(pic);
pic.slowPathCall = stubcc.call(ic::GetElem);
/* Load dslots. */
#if defined JS_NUNBOX32
DBGLABEL(dbgDslotsLoad);
#elif defined JS_PUNBOX64
Label dslotsLoadLabel = masm.label();
#endif
masm.loadPtr(Address(objReg, offsetof(JSObject, slots)), objReg);
/* Copy the slot value to the expression stack. */
Address slot(objReg, 1 << 24);
#if defined JS_NUNBOX32
masm.loadTypeTag(slot, shapeReg);
DBGLABEL(dbgTypeLoad);
masm.loadPayload(slot, objReg);
DBGLABEL(dbgDataLoad);
#elif defined JS_PUNBOX64
Label inlineValueOffsetLabel =
masm.loadValueAsComponents(slot, shapeReg, objReg);
#endif
pic.fastPathRejoin = masm.label();
pic.objReg = objReg;
pic.idReg = idReg;
RETURN_IF_OOM(false);
#if defined JS_NUNBOX32
JS_ASSERT(masm.differenceBetween(pic.fastPathRejoin, dbgDslotsLoad) == GETPROP_DSLOTS_LOAD);
JS_ASSERT(masm.differenceBetween(pic.fastPathRejoin, dbgTypeLoad) == GETPROP_TYPE_LOAD);
JS_ASSERT(masm.differenceBetween(pic.fastPathRejoin, dbgDataLoad) == GETPROP_DATA_LOAD);
JS_ASSERT(masm.differenceBetween(pic.shapeGuard, inlineAtomOffsetLabel) == GETELEM_INLINE_ATOM_OFFSET);
JS_ASSERT(masm.differenceBetween(pic.shapeGuard, dbgInlineAtomJump) == GETELEM_INLINE_ATOM_JUMP);
JS_ASSERT(masm.differenceBetween(pic.shapeGuard, inlineShapeOffsetLabel) == GETELEM_INLINE_SHAPE_OFFSET);
JS_ASSERT(masm.differenceBetween(pic.shapeGuard, dbgInlineShapeJump) == GETELEM_INLINE_SHAPE_JUMP);
#elif defined JS_PUNBOX64
pic.labels.getprop.dslotsLoadOffset = masm.differenceBetween(pic.fastPathRejoin, dslotsLoadLabel);
JS_ASSERT(pic.labels.getprop.dslotsLoadOffset == masm.differenceBetween(pic.fastPathRejoin, dslotsLoadLabel));
pic.labels.getelem.inlineShapeOffset = masm.differenceBetween(pic.shapeGuard, inlineShapeOffsetLabel);
JS_ASSERT(pic.labels.getelem.inlineShapeOffset == masm.differenceBetween(pic.shapeGuard, inlineShapeOffsetLabel));
pic.labels.getelem.inlineAtomOffset = masm.differenceBetween(pic.shapeGuard, inlineAtomOffsetLabel);
JS_ASSERT(pic.labels.getelem.inlineAtomOffset == masm.differenceBetween(pic.shapeGuard, inlineAtomOffsetLabel));
pic.labels.getelem.inlineValueOffset = masm.differenceBetween(pic.fastPathRejoin, inlineValueOffsetLabel);
JS_ASSERT(pic.labels.getelem.inlineValueOffset == masm.differenceBetween(pic.fastPathRejoin, inlineValueOffsetLabel));
JS_ASSERT(masm.differenceBetween(inlineShapeOffsetLabel, dbgInlineShapeJump) == GETELEM_INLINE_SHAPE_JUMP);
JS_ASSERT(masm.differenceBetween(pic.shapeGuard, dbgInlineAtomJump) ==
pic.labels.getelem.inlineAtomOffset + GETELEM_INLINE_ATOM_JUMP);
#endif
JS_ASSERT(pic.idReg != pic.objReg);
JS_ASSERT(pic.idReg != pic.shapeReg);
JS_ASSERT(pic.objReg != pic.shapeReg);
pics.append(pic);
return true;
}
#endif
bool
mjit::Compiler::jsop_callprop_generic(JSAtom *atom)
{
@ -2847,7 +2769,7 @@ mjit::Compiler::jsop_callprop_generic(JSAtom *atom)
RegisterID objReg = frame.copyDataIntoReg(top);
RegisterID shapeReg = frame.allocReg();
PICGenInfo pic(ic::PICInfo::CALL, true);
PICGenInfo pic(ic::PICInfo::CALL, JSOp(*PC), true);
pic.pc = PC;
@ -2871,7 +2793,6 @@ mjit::Compiler::jsop_callprop_generic(JSAtom *atom)
pic.objReg = objReg;
pic.shapeReg = shapeReg;
pic.atom = atom;
pic.objRemat = frame.dataRematInfo(top);
/*
* Store the type and object back. Don't bother keeping them in registers,
@ -2901,7 +2822,7 @@ mjit::Compiler::jsop_callprop_generic(JSAtom *atom)
/* Slow path. */
stubcc.leave();
passPICAddress(pic);
passICAddress(&pic);
pic.slowPathCall = stubcc.call(ic::CallProp);
/* Adjust the frame. None of this will generate code. */
@ -3019,7 +2940,7 @@ mjit::Compiler::jsop_callprop_obj(JSAtom *atom)
{
FrameEntry *top = frame.peek(-1);
PICGenInfo pic(ic::PICInfo::CALL, true);
PICGenInfo pic(ic::PICInfo::CALL, JSOp(*PC), true);
JS_ASSERT(top->isTypeKnown());
JS_ASSERT(top->getKnownType() == JSVAL_TYPE_OBJECT);
@ -3034,7 +2955,6 @@ mjit::Compiler::jsop_callprop_obj(JSAtom *atom)
pic.shapeReg = shapeReg;
pic.atom = atom;
pic.objRemat = frame.dataRematInfo(top);
/* Guard on shape. */
masm.loadShape(objReg, shapeReg);
@ -3049,7 +2969,7 @@ mjit::Compiler::jsop_callprop_obj(JSAtom *atom)
pic.slowPathStart = stubcc.linkExit(j, Uses(1));
stubcc.leave();
passPICAddress(pic);
passICAddress(&pic);
pic.slowPathCall = stubcc.call(ic::CallProp);
/* Load dslots. */
@ -3151,7 +3071,10 @@ mjit::Compiler::jsop_setprop(JSAtom *atom, bool usePropCache)
JSOp op = JSOp(*PC);
PICGenInfo pic(op == JSOP_SETMETHOD ? ic::PICInfo::SETMETHOD : ic::PICInfo::SET, usePropCache);
ic::PICInfo::Kind kind = (op == JSOP_SETMETHOD)
? ic::PICInfo::SETMETHOD
: ic::PICInfo::SET;
PICGenInfo pic(kind, op, usePropCache);
pic.atom = atom;
/* Guard that the type is an object. */
@ -3191,7 +3114,6 @@ mjit::Compiler::jsop_setprop(JSAtom *atom, bool usePropCache)
RegisterID shapeReg = frame.allocReg();
pic.shapeReg = shapeReg;
pic.objRemat = frame.dataRematInfo(lhs);
frame.unpinEntry(vr);
@ -3209,7 +3131,7 @@ mjit::Compiler::jsop_setprop(JSAtom *atom, bool usePropCache)
pic.slowPathStart = stubcc.linkExit(j, Uses(2));
stubcc.leave();
passPICAddress(pic);
passICAddress(&pic);
pic.slowPathCall = stubcc.call(ic::SetProp);
}
@ -3276,7 +3198,7 @@ mjit::Compiler::jsop_setprop(JSAtom *atom, bool usePropCache)
void
mjit::Compiler::jsop_name(JSAtom *atom)
{
PICGenInfo pic(ic::PICInfo::NAME, true);
PICGenInfo pic(ic::PICInfo::NAME, JSOp(*PC), true);
pic.shapeReg = frame.allocReg();
pic.objReg = frame.allocReg();
@ -3291,7 +3213,7 @@ mjit::Compiler::jsop_name(JSAtom *atom)
{
pic.slowPathStart = stubcc.linkExit(j, Uses(0));
stubcc.leave();
passPICAddress(pic);
passICAddress(&pic);
pic.slowPathCall = stubcc.call(ic::Name);
}
@ -3308,7 +3230,7 @@ mjit::Compiler::jsop_name(JSAtom *atom)
bool
mjit::Compiler::jsop_xname(JSAtom *atom)
{
PICGenInfo pic(ic::PICInfo::XNAME, true);
PICGenInfo pic(ic::PICInfo::XNAME, JSOp(*PC), true);
FrameEntry *fe = frame.peek(-1);
if (fe->isNotType(JSVAL_TYPE_OBJECT)) {
@ -3333,7 +3255,7 @@ mjit::Compiler::jsop_xname(JSAtom *atom)
{
pic.slowPathStart = stubcc.linkExit(j, Uses(1));
stubcc.leave();
passPICAddress(pic);
passICAddress(&pic);
pic.slowPathCall = stubcc.call(ic::XName);
}
@ -3352,7 +3274,7 @@ mjit::Compiler::jsop_xname(JSAtom *atom)
void
mjit::Compiler::jsop_bindname(uint32 index, bool usePropCache)
{
PICGenInfo pic(ic::PICInfo::BIND, usePropCache);
PICGenInfo pic(ic::PICInfo::BIND, JSOp(*PC), usePropCache);
// This code does not check the frame flags to see if scopeChain has been
// set. Rather, it relies on the up-front analysis statically determining
@ -3382,7 +3304,7 @@ mjit::Compiler::jsop_bindname(uint32 index, bool usePropCache)
{
pic.slowPathStart = stubcc.linkExit(j, Uses(0));
stubcc.leave();
passPICAddress(pic);
passICAddress(&pic);
pic.slowPathCall = stubcc.call(ic::BindName);
}

View File

@ -160,18 +160,39 @@ class Compiler : public BaseCompiler
bool hasSlowNcode;
};
#if defined JS_POLYIC
struct BaseICInfo {
BaseICInfo(JSOp op) : op(op)
{ }
Label fastPathStart;
Label fastPathRejoin;
Label slowPathStart;
Call slowPathCall;
DataLabelPtr paramAddr;
JSOp op;
void copyTo(ic::BaseIC &to, JSC::LinkBuffer &full, JSC::LinkBuffer &stub) {
to.fastPathStart = full.locationOf(fastPathStart);
to.fastPathRejoin = full.locationOf(fastPathRejoin);
to.slowPathStart = stub.locationOf(slowPathStart);
to.slowPathCall = stub.locationOf(slowPathCall);
to.op = op;
JS_ASSERT(to.op == op);
}
};
struct GetElementICInfo : public BaseICInfo {
GetElementICInfo(JSOp op) : BaseICInfo(op)
{ }
RegisterID typeReg;
RegisterID objReg;
ValueRemat id;
MaybeJump typeGuard;
Jump claspGuard;
};
struct PICGenInfo : public BaseICInfo {
PICGenInfo(ic::PICInfo::Kind kind, bool usePropCache)
: kind(kind), usePropCache(usePropCache)
PICGenInfo(ic::PICInfo::Kind kind, JSOp op, bool usePropCache)
: BaseICInfo(op), kind(kind), usePropCache(usePropCache)
{ }
ic::PICInfo::Kind kind;
Label typeCheck;
@ -183,8 +204,6 @@ class Compiler : public BaseCompiler
Label shapeGuard;
jsbytecode *pc;
JSAtom *atom;
StateRemat objRemat;
StateRemat idRemat;
bool hasTypeCheck;
ValueRemat vr;
# if defined JS_CPU_X64
@ -200,15 +219,12 @@ class Compiler : public BaseCompiler
if (ic.isSet()) {
ic.u.vr = vr;
} else if (ic.isGet()) {
ic.u.get.idReg = idReg;
ic.u.get.typeReg = typeReg;
ic.u.get.hasTypeCheck = hasTypeCheck;
ic.setObjRemat(objRemat);
}
}
};
#endif
struct Defs {
Defs(uint32 ndefs)
@ -250,6 +266,7 @@ class Compiler : public BaseCompiler
#endif
#if defined JS_POLYIC
js::Vector<PICGenInfo, 16> pics;
js::Vector<GetElementICInfo> getElemICs;
#endif
js::Vector<CallPatchInfo, 64> callPatches;
js::Vector<InternalCallSite, 64> callSites;
@ -298,7 +315,7 @@ class Compiler : public BaseCompiler
void iterEnd();
MaybeJump loadDouble(FrameEntry *fe, FPRegisterID fpReg);
#ifdef JS_POLYIC
void passPICAddress(PICGenInfo &pic);
void passICAddress(BaseICInfo *ic);
#endif
#ifdef JS_MONOIC
void passMICAddress(MICGenInfo &mic);
@ -400,13 +417,6 @@ class Compiler : public BaseCompiler
void jsop_localinc(JSOp op, uint32 slot, bool popped);
void jsop_setelem();
bool jsop_getelem();
bool jsop_getelem_known_type(FrameEntry *obj, FrameEntry *id, RegisterID tmpReg);
bool jsop_getelem_with_pic(FrameEntry *obj, FrameEntry *id, RegisterID tmpReg);
void jsop_getelem_nopic(FrameEntry *obj, FrameEntry *id, RegisterID tmpReg);
bool jsop_getelem_pic(FrameEntry *obj, FrameEntry *id, RegisterID objReg, RegisterID idReg,
RegisterID shapeReg);
void jsop_getelem_dense(FrameEntry *obj, FrameEntry *id, RegisterID objReg,
MaybeRegisterID &idReg, RegisterID shapeReg);
void jsop_stricteq(JSOp op);
void jsop_equality(JSOp op, BoolStub stub, jsbytecode *target, JSOp fused);
void jsop_equality_int_string(JSOp op, BoolStub stub, jsbytecode *target, JSOp fused);

View File

@ -1360,135 +1360,6 @@ mjit::Compiler::jsop_setelem()
stubcc.rejoin(Changes(0));
}
void
mjit::Compiler::jsop_getelem_dense(FrameEntry *obj, FrameEntry *id, RegisterID objReg,
MaybeRegisterID &idReg, RegisterID tmpReg)
{
/* Note: idReg is only valid if id is not a constant. */
Jump guardDense = masm.testObjClass(Assembler::NotEqual, objReg, &js_ArrayClass);
stubcc.linkExit(guardDense, Uses(2));
Int32Key key = idReg.isSet()
? Int32Key::FromRegister(idReg.reg())
: Int32Key::FromConstant(id->getValue().toInt32());
Assembler::FastArrayLoadFails fails =
masm.fastArrayLoad(objReg, key, tmpReg, objReg);
stubcc.linkExit(fails.rangeCheck, Uses(2));
stubcc.linkExit(fails.holeCheck, Uses(2));
}
bool
mjit::Compiler::jsop_getelem_known_type(FrameEntry *obj, FrameEntry *id, RegisterID tmpReg)
{
switch (id->getKnownType()) {
case JSVAL_TYPE_INT32:
{
/* Prologue. */
RegisterID objReg = frame.copyDataIntoReg(obj);
MaybeRegisterID idReg;
if (!id->isConstant())
idReg.setReg(frame.copyDataIntoReg(id));
/* Meat. */
jsop_getelem_dense(obj, id, objReg, idReg, tmpReg);
stubcc.leave();
stubcc.call(stubs::GetElem);
/* Epilogue. */
if (idReg.isSet())
frame.freeReg(idReg.reg());
frame.popn(2);
frame.pushRegs(tmpReg, objReg);
stubcc.rejoin(Changes(1));
break;
}
#ifdef JS_POLYIC
case JSVAL_TYPE_STRING:
{
/* Prologue. */
RegisterID objReg = frame.copyDataIntoReg(obj);
RegisterID idReg = frame.copyDataIntoReg(id);
/* Meat. */
if (!jsop_getelem_pic(obj, id, objReg, idReg, tmpReg))
return false;
/* Epilogue. */
frame.popn(2);
frame.pushRegs(tmpReg, objReg);
frame.freeReg(idReg);
stubcc.rejoin(Changes(1));
break;
}
#endif
default:
JS_NOT_REACHED("Invalid known id type.");
}
return true;
}
#ifdef JS_POLYIC
bool
mjit::Compiler::jsop_getelem_with_pic(FrameEntry *obj, FrameEntry *id, RegisterID tmpReg)
{
JS_ASSERT(!id->isTypeKnown());
RegisterID objReg = frame.copyDataIntoReg(obj);
MaybeRegisterID idReg(frame.copyDataIntoReg(id));
RegisterID typeReg = frame.tempRegForType(id, tmpReg);
Jump intGuard = masm.testInt32(Assembler::NotEqual, typeReg);
JaegerSpew(JSpew_Insns, " ==== BEGIN DENSE ARRAY CODE ==== \n");
jsop_getelem_dense(obj, id, objReg, idReg, tmpReg);
Jump performedDense = masm.jump();
JaegerSpew(JSpew_Insns, " ==== END DENSE ARRAY CODE ==== \n");
intGuard.linkTo(masm.label(), &masm);
Jump stringGuard = masm.testString(Assembler::NotEqual, typeReg);
stubcc.linkExit(stringGuard, Uses(2)); /* Neither int nor string at this point. */
stubcc.leave();
stubcc.call(stubs::GetElem);
Jump toFinalMerge = stubcc.masm.jump();
if (!jsop_getelem_pic(obj, id, objReg, idReg.reg(), tmpReg))
return false;
performedDense.linkTo(masm.label(), &masm);
frame.popn(2);
frame.pushRegs(tmpReg, objReg);
frame.freeReg(idReg.reg());
toFinalMerge.linkTo(stubcc.masm.label(), &stubcc.masm);
stubcc.rejoin(Changes(1));
return true;
}
#endif
void
mjit::Compiler::jsop_getelem_nopic(FrameEntry *obj, FrameEntry *id, RegisterID tmpReg)
{
/* Only handle the int32 case. */
RegisterID objReg = frame.copyDataIntoReg(obj);
MaybeRegisterID idReg(frame.copyDataIntoReg(id));
RegisterID typeReg = frame.tempRegForType(id, tmpReg);
Jump intGuard = masm.testInt32(Assembler::NotEqual, typeReg);
stubcc.linkExit(intGuard, Uses(2));
/* Meat. */
jsop_getelem_dense(obj, id, objReg, idReg, tmpReg);
stubcc.leave();
stubcc.call(stubs::GetElem);
/* Epilogue. */
frame.freeReg(idReg.reg());
frame.popn(2);
frame.pushRegs(tmpReg, objReg);
stubcc.rejoin(Changes(1));
}
bool
mjit::Compiler::jsop_getelem()
{
@ -1516,30 +1387,107 @@ mjit::Compiler::jsop_getelem()
return true;
}
if (id->isTypeKnown() && id->getKnownType() == JSVAL_TYPE_STRING && id->isConstant()) {
/* Never happens, or I'd optimize it. */
jsop_getelem_slow();
return true;
GetElementICInfo ic = GetElementICInfo(JSOp(*PC));
// Pin the top of the stack to avoid spills, before allocating registers.
MaybeRegisterID pinnedIdData = frame.maybePinData(id);
MaybeRegisterID pinnedIdType = frame.maybePinType(id);
MaybeJump objTypeGuard;
if (!obj->isTypeKnown()) {
// Test the type of the object without spilling the payload.
MaybeRegisterID pinnedObjData = frame.maybePinData(obj);
Jump guard = frame.testObject(Assembler::NotEqual, obj);
frame.maybeUnpinReg(pinnedObjData);
// Create a sync path, which we'll rejoin manually later. This is safe
// as long as the IC does not build a stub; it won't, because |obj|
// won't be an object. If we extend this IC to support strings, all
// that needs to change is a little code movement.
stubcc.linkExit(guard, Uses(2));
objTypeGuard = stubcc.masm.jump();
}
RegisterID tmpReg;
if (obj->isTypeKnown()) {
tmpReg = frame.allocReg();
// Get a mutable register for the object. This will be the data reg.
ic.objReg = frame.copyDataIntoReg(obj);
// Get a mutable register for pushing the result type. We kill two birds
// with one stone by making sure, if the key type is not known, to be loaded
// into this register. In this case it is both an input and an output.
frame.maybeUnpinReg(pinnedIdType);
if (id->isConstant() || id->isTypeKnown())
ic.typeReg = frame.allocReg();
else
ic.typeReg = frame.copyTypeIntoReg(id);
// Fill in the id value.
frame.maybeUnpinReg(pinnedIdData);
if (id->isConstant()) {
ic.id = ValueRemat::FromConstant(id->getValue());
} else {
tmpReg = frame.copyTypeIntoReg(obj);
Jump objGuard = masm.testObject(Assembler::NotEqual, tmpReg);
stubcc.linkExit(objGuard, Uses(2));
RegisterID dataReg = frame.tempRegForData(id);
if (id->isTypeKnown())
ic.id = ValueRemat::FromKnownType(id->getKnownType(), dataReg);
else
ic.id = ValueRemat::FromRegisters(ic.typeReg, dataReg);
}
if (id->isTypeKnown())
return jsop_getelem_known_type(obj, id, tmpReg);
ic.fastPathStart = masm.label();
// Note: slow path here is safe, since the frame will not be modified.
ic.slowPathStart = stubcc.masm.label();
frame.sync(stubcc.masm, Uses(2));
if (id->mightBeType(JSVAL_TYPE_INT32)) {
// Always test the type first (see comment in PolyIC.h).
if (!id->isTypeKnown()) {
ic.typeGuard = masm.testInt32(Assembler::NotEqual, ic.typeReg);
stubcc.linkExitDirect(ic.typeGuard.get(), ic.slowPathStart);
}
// Guard on the clasp.
ic.claspGuard = masm.testObjClass(Assembler::NotEqual, ic.objReg, &js_ArrayClass);
stubcc.linkExitDirect(ic.claspGuard, ic.slowPathStart);
Int32Key key = id->isConstant()
? Int32Key::FromConstant(id->getValue().toInt32())
: Int32Key::FromRegister(ic.id.dataReg());
Assembler::FastArrayLoadFails fails =
masm.fastArrayLoad(ic.objReg, key, ic.typeReg, ic.objReg);
stubcc.linkExitDirect(fails.rangeCheck, ic.slowPathStart);
stubcc.linkExitDirect(fails.holeCheck, ic.slowPathStart);
} else {
// The type is known to not be dense-friendly ahead of time, so always
// fall back to a slow path.
ic.claspGuard = masm.jump();
stubcc.linkExitDirect(ic.claspGuard, ic.slowPathStart);
}
stubcc.leave();
if (objTypeGuard.isSet())
objTypeGuard.get().linkTo(stubcc.masm.label(), &stubcc.masm);
#ifdef JS_POLYIC
passICAddress(&ic);
ic.slowPathCall = stubcc.call(ic::GetElement);
#else
ic.slowPathCall = stubcc.call(stubs::GetElem);
#endif
ic.fastPathRejoin = masm.label();
frame.popn(2);
frame.pushRegs(ic.typeReg, ic.objReg);
stubcc.rejoin(Changes(2));
#ifdef JS_POLYIC
return jsop_getelem_with_pic(obj, id, tmpReg);
#else
jsop_getelem_nopic(obj, id, tmpReg);
return true;
if (!getElemICs.append(ic))
return false;
#endif
return true;
}
static inline bool

View File

@ -97,6 +97,12 @@ class FrameEntry
return isTypeKnown() && getKnownType() != type_;
}
// Return true if the type of this value is definitely type_, or is unknown
// and thus potentially type_ at runtime.
bool mightBeType(JSValueType type_) const {
return !isNotType(type_);
}
#if defined JS_NUNBOX32
uint32 getPayload() const {
//JS_ASSERT(!Valueify(v_.asBits).isDouble() || type.synced());

View File

@ -811,10 +811,10 @@ mjit::JITScript::release()
code.m_executablePool->release();
#if defined JS_POLYIC
for (uint32 i = 0; i < nPICs; i++) {
pics[i].releasePools();
Destroy(pics[i].execPools);
}
for (uint32 i = 0; i < nPICs; i++)
pics[i].finish();
for (uint32 i = 0; i < nGetElems; i++)
getElems[i].finish();
#endif
#if defined JS_MONOIC

View File

@ -143,6 +143,7 @@ namespace mjit {
namespace ic {
# if defined JS_POLYIC
struct PICInfo;
struct GetElementIC;
# endif
# if defined JS_MONOIC
struct MICInfo;
@ -182,6 +183,7 @@ typedef void * (JS_FASTCALL *VoidPtrStubTraceIC)(VMFrame &, js::mjit::ic::TraceI
#endif
#ifdef JS_POLYIC
typedef void (JS_FASTCALL *VoidStubPIC)(VMFrame &, js::mjit::ic::PICInfo *);
typedef void (JS_FASTCALL *VoidStubGetElemIC)(VMFrame &, js::mjit::ic::GetElementIC *);
#endif
namespace mjit {
@ -212,6 +214,8 @@ struct JITScript {
#ifdef JS_POLYIC
ic::PICInfo *pics; /* PICs in this script */
uint32 nPICs; /* number of PolyICs */
ic::GetElementIC *getElems;
uint32 nGetElems;
#endif
void *invokeEntry; /* invoke address */
void *fastEntry; /* cached entry, fastest */

View File

@ -90,22 +90,15 @@ class NunboxAssembler : public JSC::MacroAssembler
return BaseIndex(address.base, address.index, address.scale, address.offset + TAG_OFFSET);
}
void loadSlot(RegisterID obj, RegisterID clobber, uint32 slot, bool inlineAccess,
RegisterID type, RegisterID data) {
JS_ASSERT(type != data);
Address address(obj, JSObject::getFixedSlotOffset(slot));
RegisterID activeAddressReg = obj;
if (!inlineAccess) {
loadPtr(Address(obj, offsetof(JSObject, slots)), clobber);
address = Address(clobber, slot * sizeof(Value));
activeAddressReg = clobber;
}
if (activeAddressReg == type) {
loadPayload(address, data);
loadTypeTag(address, type);
void loadInlineSlot(RegisterID objReg, uint32 slot,
RegisterID typeReg, RegisterID dataReg) {
Address address(objReg, JSObject::getFixedSlotOffset(slot));
if (objReg == typeReg) {
loadPayload(address, dataReg);
loadTypeTag(address, typeReg);
} else {
loadTypeTag(address, type);
loadPayload(address, data);
loadTypeTag(address, typeReg);
loadPayload(address, dataReg);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -37,7 +37,7 @@
*
* ***** END LICENSE BLOCK ***** */
#if !defined jsjaeger_poly_ic_h__ && defined JS_METHODJIT && defined JS_POLYIC
#if !defined jsjaeger_poly_ic_h__ && defined JS_METHODJIT
#define jsjaeger_poly_ic_h__
#include "jscntxt.h"
@ -46,7 +46,9 @@
#include "assembler/assembler/MacroAssembler.h"
#include "assembler/assembler/CodeLocation.h"
#include "methodjit/MethodJIT.h"
#include "BaseAssembler.h"
#include "RematInfo.h"
#include "BaseCompiler.h"
namespace js {
namespace mjit {
@ -54,6 +56,7 @@ namespace ic {
/* Maximum number of stubs for a given callsite. */
static const uint32 MAX_PIC_STUBS = 16;
static const uint32 MAX_GETELEM_IC_STUBS = 17;
/* SetPropCompiler */
#if defined JS_CPU_X86
@ -159,26 +162,6 @@ union PICLabels {
int32 stubShapeJump : 8;
} getprop;
/* GetElemCompiler */
struct {
/* Offset from storeBack to beginning of 'mov dslots, addr' */
int32 dslotsLoadOffset : 8;
/* Offset from shapeGuard to end of shape comparison. */
int32 inlineShapeOffset : 8;
/* Offset from shapeGuard to end of atom comparison. */
int32 inlineAtomOffset : 8;
/* Offset from storeBack to end of value load. */
int32 inlineValueOffset : 8;
/* Offset from lastStubStart to end of shape jump. */
// TODO: We can redefine the location of lastStubStart to be
// after the jump -- at which point this is always 0.
int32 stubShapeJump : 8;
} getelem;
/* BindNameCompiler */
struct {
/* Offset from shapeGuard to end of shape jump. */
@ -187,21 +170,27 @@ union PICLabels {
};
#endif
struct BaseIC {
enum LookupStatus {
Lookup_Error = 0,
Lookup_Uncacheable,
Lookup_Cacheable
};
struct BaseIC : public MacroAssemblerTypedefs {
// Address of inline fast-path.
JSC::CodeLocationLabel fastPathStart;
CodeLocationLabel fastPathStart;
// Address to rejoin to the fast-path.
JSC::CodeLocationLabel fastPathRejoin;
CodeLocationLabel fastPathRejoin;
// Start of the slow path.
JSC::CodeLocationLabel slowPathStart;
CodeLocationLabel slowPathStart;
// Slow path stub call.
JSC::CodeLocationCall slowPathCall;
CodeLocationCall slowPathCall;
// Address of the start of the last generated stub, if any.
JSC::CodeLocationLabel lastStubStart;
CodeLocationLabel lastStubStart;
typedef Vector<JSC::ExecutablePool *, 0, SystemAllocPolicy> ExecPoolVector;
@ -210,16 +199,25 @@ struct BaseIC {
// Return the start address of the last path in this PIC, which is the
// inline path if no stubs have been generated yet.
JSC::CodeLocationLabel lastPathStart() {
CodeLocationLabel lastPathStart() {
return stubsGenerated > 0 ? lastStubStart : fastPathStart;
}
// Whether or not the callsite has been hit at least once.
bool hit : 1;
bool slowCallPatched : 1;
// Number of stubs generated.
uint32 stubsGenerated : 5;
// Offset from start of stub to jump target of second shape guard as Nitro
// asm data location. This is 0 if there is only one shape guard in the
// last stub.
int secondShapeGuard : 11;
// Opcode this was compiled for.
JSOp op : 8;
// Release ExecutablePools referred to by this PIC.
void releasePools() {
for (JSC::ExecutablePool **pExecPool = execPools.begin();
@ -229,17 +227,122 @@ struct BaseIC {
}
}
void init() {
new (&execPools) ExecPoolVector(SystemAllocPolicy());
}
void finish() {
releasePools();
this->~BaseIC();
}
void reset() {
hit = false;
slowCallPatched = false;
stubsGenerated = 0;
secondShapeGuard = 0;
releasePools();
execPools.clear();
}
bool shouldUpdate(JSContext *cx);
void spew(JSContext *cx, const char *event, const char *reason);
LookupStatus disable(JSContext *cx, const char *reason, void *stub);
bool isCallOp();
};
struct GetElementIC : public BaseIC {
// On stub entry:
// If hasInlineTypeCheck() is true, and inlineTypeCheckPatched is false,
// - typeReg contains the type of the |id| parameter.
// If hasInlineTypeCheck() is true, and inlineTypeCheckPatched is true,
// - typeReg contains the shape of |objReg| iff typeRegHasBaseShape
// is true.
// Otherwise, typeReg is garbage.
//
// On stub exit, typeReg must contain the type of the result value.
RegisterID typeReg : 5;
// On stub entry, objReg contains the object pointer for the |obj| parameter.
// On stub exit, objReg must contain the payload of the result value.
RegisterID objReg : 5;
// Offset from the fast path to the inline type check.
// This is only set if hasInlineTypeCheck() is true.
unsigned inlineTypeGuard : 8;
// Offset from the fast path to the inline clasp guard. This is always
// set; if |id| is known to not be int32, then it's an unconditional
// jump to the slow path.
unsigned inlineClaspGuard : 8;
// This is usable if hasInlineTypeGuard() returns true, which implies
// that a dense array fast path exists. The inline type guard serves as
// the head of the chain of all string-based element stubs.
bool inlineTypeGuardPatched : 1;
// This is always usable, and specifies whether the inline clasp guard
// has been patched. If hasInlineTypeGuard() is true, it guards against
// a dense array, and guarantees the inline type guard has passed.
// Otherwise, there is no inline type guard, and the clasp guard is just
// an unconditional jump.
bool inlineClaspGuardPatched : 1;
////////////////////////////////////////////
// State for string-based property stubs. //
////////////////////////////////////////////
// True if typeReg is guaranteed to have the shape of objReg.
bool typeRegHasBaseShape : 1;
// These offsets are used for string-key dependent stubs, such as named
// property accesses. They are separated from the int-key dependent stubs,
// in order to guarantee that the id type needs only one guard per type.
int atomGuard : 8; // optional, non-zero if present
int firstShapeGuard : 8; // always set
int secondShapeGuard : 8; // optional, non-zero if present
bool hasLastStringStub : 1;
CodeLocationLabel lastStringStub;
// A limited ValueRemat instance. It may contains either:
// 1) A constant, or
// 2) A known type and data reg, or
// 3) A data reg.
// The sync bits are not set, and the type reg is never set and should not
// be used, as it is encapsulated more accurately in |typeReg|. Also, note
// carefully that the data reg is immutable.
ValueRemat idRemat;
bool hasInlineTypeGuard() const {
return !idRemat.isTypeKnown();
}
bool shouldPatchInlineTypeGuard() {
return hasInlineTypeGuard() && !inlineTypeGuardPatched;
}
bool shouldPatchUnconditionalClaspGuard() {
return !hasInlineTypeGuard() && !inlineClaspGuardPatched;
}
void init() {
BaseIC::init();
reset();
}
void reset() {
BaseIC::reset();
inlineTypeGuardPatched = false;
inlineClaspGuardPatched = false;
typeRegHasBaseShape = false;
hasLastStringStub = false;
}
void purge();
LookupStatus update(JSContext *cx, JSObject *obj, const Value &v, jsid id, Value *vp);
LookupStatus attachGetProp(JSContext *cx, JSObject *obj, const Value &v, jsid id,
Value *vp);
LookupStatus disable(JSContext *cx, const char *reason);
LookupStatus error(JSContext *cx);
bool shouldUpdate(JSContext *cx);
};
struct PICInfo : public BaseIC {
typedef JSC::MacroAssembler::RegisterID RegisterID;
// Operation this is a PIC for.
enum Kind
#ifdef _MSC_VER
@ -252,7 +355,6 @@ struct PICInfo : public BaseIC {
SETMETHOD, // JSOP_SETMETHOD
NAME, // JSOP_NAME
BIND, // JSOP_BINDNAME
GETELEM, // JSOP_GETELEM
XNAME // JSOP_GETXPROP
};
@ -263,20 +365,10 @@ struct PICInfo : public BaseIC {
// Reverse offset from slowPathStart to the type check slow path.
int32 typeCheckOffset;
// Remat info for the object reg.
int32 objRemat : MIN_STATE_REMAT_BITS;
bool objNeedsRemat : 1;
RegisterID idReg : 5; // only used in GETELEM PICs.
} get;
ValueRemat vr;
} u;
// Offset from start of stub to jump target of second shape guard as Nitro
// asm data location. This is 0 if there is only one shape guard in the
// last stub.
int secondShapeGuard : 11;
Kind kind : 3;
// True if register R holds the base object shape along exits from the
@ -299,7 +391,7 @@ struct PICInfo : public BaseIC {
return kind == SET || kind == SETMETHOD;
}
inline bool isGet() const {
return kind == GET || kind == CALL || kind == GETELEM;
return kind == GET || kind == CALL;
}
inline RegisterID typeReg() {
JS_ASSERT(isGet());
@ -309,14 +401,6 @@ struct PICInfo : public BaseIC {
JS_ASSERT(isGet());
return u.get.hasTypeCheck;
}
inline const StateRemat objRemat() const {
JS_ASSERT(isGet());
return StateRemat::FromInt32(u.get.objRemat);
}
inline bool objNeedsRemat() {
JS_ASSERT(isGet());
return u.get.objNeedsRemat;
}
inline bool shapeNeedsRemat() {
return !shapeRegHasBaseShape;
}
@ -325,12 +409,6 @@ struct PICInfo : public BaseIC {
return !hasTypeCheck();
}
inline void setObjRemat(const StateRemat &sr) {
JS_ASSERT(isGet());
u.get.objRemat = sr.toInt32();
JS_ASSERT(u.get.objRemat == sr.toInt32());
}
#if defined JS_CPU_X64
// Required labels for platform-specific patching.
PICLabels labels;
@ -342,30 +420,30 @@ struct PICInfo : public BaseIC {
// Index into the script's atom table.
JSAtom *atom;
bool shouldGenerate() {
return stubsGenerated < MAX_PIC_STUBS || !inlinePathPatched;
void init() {
BaseIC::init();
reset();
}
// Reset the data members to the state of a fresh PIC before any patching
// or stub generation was done.
void reset() {
inlinePathPatched = false;
if (kind == GET || kind == CALL || kind == GETELEM)
u.get.objNeedsRemat = false;
secondShapeGuard = 0;
shapeRegHasBaseShape = true;
BaseIC::reset();
}
};
#ifdef JS_POLYIC
void PurgePICs(JSContext *cx, JSScript *script);
void JS_FASTCALL GetProp(VMFrame &f, ic::PICInfo *);
void JS_FASTCALL GetElem(VMFrame &f, ic::PICInfo *);
void JS_FASTCALL SetProp(VMFrame &f, ic::PICInfo *);
void JS_FASTCALL CallProp(VMFrame &f, ic::PICInfo *);
void JS_FASTCALL Name(VMFrame &f, ic::PICInfo *);
void JS_FASTCALL XName(VMFrame &f, ic::PICInfo *);
void JS_FASTCALL BindName(VMFrame &f, ic::PICInfo *);
void JS_FASTCALL GetElement(VMFrame &f, ic::GetElementIC *);
#endif
} /* namespace ic */
} /* namespace mjit */

View File

@ -93,16 +93,10 @@ class PunboxAssembler : public JSC::MacroAssembler
return address;
}
void loadSlot(RegisterID obj, RegisterID clobber, uint32 slot, bool inlineAccess,
RegisterID type, RegisterID data) {
JS_ASSERT(type != data);
Address address(obj, JSObject::getFixedSlotOffset(slot));
if (!inlineAccess) {
loadPtr(Address(obj, offsetof(JSObject, slots)), clobber);
address = Address(clobber, slot * sizeof(Value));
}
loadValueAsComponents(address, type, data);
void loadInlineSlot(RegisterID objReg, uint32 slot,
RegisterID typeReg, RegisterID dataReg) {
Address address(objReg, JSObject::getFixedSlotOffset(slot));
loadValueAsComponents(address, typeReg, dataReg);
}
template <typename T>

View File

@ -127,6 +127,7 @@ class StubCompiler
STUB_CALL_TYPE(VoidStubPC);
#ifdef JS_POLYIC
STUB_CALL_TYPE(VoidStubPIC);
STUB_CALL_TYPE(VoidStubGetElemIC);
#endif
#ifdef JS_MONOIC
STUB_CALL_TYPE(VoidStubMIC);