mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-22 18:32:00 +00:00
Bug 599214 - JM: Add a fast path for object equality (r=dvander,nnethercote)
This commit is contained in:
parent
234bccd8ef
commit
af25c84c93
@ -88,6 +88,7 @@ namespace js {
|
||||
* - ACCSET_STACKFRAME: All JSStackFrame objects.
|
||||
* - ACCSET_RUNTIME: The JSRuntime object.
|
||||
* - ACCSET_OBJ_CLASP: The 'clasp' field of all JSObjects.
|
||||
* - ACCSET_OBJ_FLAGS: The 'flags' field of all JSObjects.
|
||||
* - ACCSET_OBJ_SHAPE: The 'shape' field of all JSObjects.
|
||||
* - ACCSET_OBJ_PROTO: The 'proto' field of all JSObjects.
|
||||
* - ACCSET_OBJ_PARENT: The 'parent' field of all JSObjects.
|
||||
@ -115,28 +116,29 @@ static const nanojit::AccSet ACCSET_FRAMEREGS = (1 << 6);
|
||||
static const nanojit::AccSet ACCSET_STACKFRAME = (1 << 7);
|
||||
static const nanojit::AccSet ACCSET_RUNTIME = (1 << 8);
|
||||
|
||||
// Nb: JSObject::{lastProp,map,flags} don't have an AccSet because they are never accessed on trace
|
||||
// Nb: JSObject::{lastProp,map} don't have an AccSet because they are never accessed on trace
|
||||
static const nanojit::AccSet ACCSET_OBJ_CLASP = (1 << 9);
|
||||
static const nanojit::AccSet ACCSET_OBJ_SHAPE = (1 << 10);
|
||||
static const nanojit::AccSet ACCSET_OBJ_PROTO = (1 << 11);
|
||||
static const nanojit::AccSet ACCSET_OBJ_PARENT = (1 << 12);
|
||||
static const nanojit::AccSet ACCSET_OBJ_PRIVATE = (1 << 13);
|
||||
static const nanojit::AccSet ACCSET_OBJ_CAPACITY = (1 << 14);
|
||||
static const nanojit::AccSet ACCSET_OBJ_SLOTS = (1 << 15); // the pointer to the slots
|
||||
static const nanojit::AccSet ACCSET_OBJ_FLAGS = (1 << 10);
|
||||
static const nanojit::AccSet ACCSET_OBJ_SHAPE = (1 << 11);
|
||||
static const nanojit::AccSet ACCSET_OBJ_PROTO = (1 << 12);
|
||||
static const nanojit::AccSet ACCSET_OBJ_PARENT = (1 << 13);
|
||||
static const nanojit::AccSet ACCSET_OBJ_PRIVATE = (1 << 14);
|
||||
static const nanojit::AccSet ACCSET_OBJ_CAPACITY = (1 << 15);
|
||||
static const nanojit::AccSet ACCSET_OBJ_SLOTS = (1 << 16); // the pointer to the slots
|
||||
|
||||
static const nanojit::AccSet ACCSET_SLOTS = (1 << 16); // the slots themselves
|
||||
static const nanojit::AccSet ACCSET_TARRAY = (1 << 17);
|
||||
static const nanojit::AccSet ACCSET_TARRAY_DATA = (1 << 18);
|
||||
static const nanojit::AccSet ACCSET_ITER = (1 << 19);
|
||||
static const nanojit::AccSet ACCSET_ITER_PROPS = (1 << 20);
|
||||
static const nanojit::AccSet ACCSET_STRING = (1 << 21);
|
||||
static const nanojit::AccSet ACCSET_STRING_MCHARS = (1 << 22);
|
||||
static const nanojit::AccSet ACCSET_TYPEMAP = (1 << 23);
|
||||
static const nanojit::AccSet ACCSET_FCSLOTS = (1 << 24);
|
||||
static const nanojit::AccSet ACCSET_ARGS_DATA = (1 << 25);
|
||||
static const nanojit::AccSet ACCSET_SLOTS = (1 << 17); // the slots themselves
|
||||
static const nanojit::AccSet ACCSET_TARRAY = (1 << 18);
|
||||
static const nanojit::AccSet ACCSET_TARRAY_DATA = (1 << 19);
|
||||
static const nanojit::AccSet ACCSET_ITER = (1 << 20);
|
||||
static const nanojit::AccSet ACCSET_ITER_PROPS = (1 << 21);
|
||||
static const nanojit::AccSet ACCSET_STRING = (1 << 22);
|
||||
static const nanojit::AccSet ACCSET_STRING_MCHARS = (1 << 23);
|
||||
static const nanojit::AccSet ACCSET_TYPEMAP = (1 << 24);
|
||||
static const nanojit::AccSet ACCSET_FCSLOTS = (1 << 25);
|
||||
static const nanojit::AccSet ACCSET_ARGS_DATA = (1 << 26);
|
||||
}
|
||||
|
||||
static const uint8_t TM_NUM_USED_ACCS = 26; // number of access regions used by TraceMonkey
|
||||
static const uint8_t TM_NUM_USED_ACCS = 27; // number of access regions used by TraceMonkey
|
||||
|
||||
enum JSTNErrType { INFALLIBLE, FAIL_STATUS, FAIL_NULL, FAIL_NEG, FAIL_NEITHER };
|
||||
enum {
|
||||
|
@ -6570,6 +6570,7 @@ js_DumpObject(JSObject *obj)
|
||||
if (flags & JSObject::METHOD_BARRIER) fprintf(stderr, " method_barrier");
|
||||
if (flags & JSObject::INDEXED) fprintf(stderr, " indexed");
|
||||
if (flags & JSObject::OWN_SHAPE) fprintf(stderr, " own_shape");
|
||||
if (flags & JSObject::HAS_EQUALITY) fprintf(stderr, " has_equality");
|
||||
|
||||
bool anyFlags = flags != 0;
|
||||
if (obj->isNative()) {
|
||||
|
@ -331,9 +331,10 @@ struct JSObject : js::gc::Cell {
|
||||
BRANDED = 0x08,
|
||||
GENERIC = 0x10,
|
||||
METHOD_BARRIER = 0x20,
|
||||
INDEXED = 0x40,
|
||||
OWN_SHAPE = 0x80,
|
||||
BOUND_FUNCTION = 0x100
|
||||
INDEXED = 0x40,
|
||||
OWN_SHAPE = 0x80,
|
||||
BOUND_FUNCTION = 0x100,
|
||||
HAS_EQUALITY = 0x200
|
||||
};
|
||||
|
||||
/*
|
||||
@ -419,6 +420,8 @@ struct JSObject : js::gc::Cell {
|
||||
bool generic() { return !!(flags & GENERIC); }
|
||||
void setGeneric() { flags |= GENERIC; }
|
||||
|
||||
bool hasSpecialEquality() { return !!(flags & HAS_EQUALITY); }
|
||||
|
||||
private:
|
||||
void generateOwnShape(JSContext *cx);
|
||||
|
||||
|
@ -193,23 +193,24 @@ nanojit::LInsPrinter::accNames[] = {
|
||||
"rt", // (1 << 8) == ACCSET_RUNTIME
|
||||
|
||||
"objclasp", // (1 << 9) == ACCSET_OBJ_CLASP
|
||||
"objshape", // (1 << 10) == ACCSET_OBJ_SHAPE
|
||||
"objproto", // (1 << 11) == ACCSET_OBJ_PROTO
|
||||
"objparent", // (1 << 12) == ACCSET_OBJ_PARENT
|
||||
"objprivate", // (1 << 13) == ACCSET_OBJ_PRIVATE
|
||||
"objcapacity", // (1 << 14) == ACCSET_OBJ_CAPACITY
|
||||
"objslots", // (1 << 15) == ACCSET_OBJ_SLOTS
|
||||
"objflags", // (1 << 10) == ACCSET_OBJ_FLAGS
|
||||
"objshape", // (1 << 11) == ACCSET_OBJ_SHAPE
|
||||
"objproto", // (1 << 12) == ACCSET_OBJ_PROTO
|
||||
"objparent", // (1 << 13) == ACCSET_OBJ_PARENT
|
||||
"objprivate", // (1 << 14) == ACCSET_OBJ_PRIVATE
|
||||
"objcapacity", // (1 << 15) == ACCSET_OBJ_CAPACITY
|
||||
"objslots", // (1 << 16) == ACCSET_OBJ_SLOTS
|
||||
|
||||
"slots", // (1 << 16) == ACCSET_SLOTS
|
||||
"tarray", // (1 << 17) == ACCSET_TARRAY
|
||||
"tdata", // (1 << 18) == ACCSET_TARRAY_DATA
|
||||
"iter", // (1 << 19) == ACCSET_ITER
|
||||
"iterprops", // (1 << 20) == ACCSET_ITER_PROPS
|
||||
"str", // (1 << 21) == ACCSET_STRING
|
||||
"strmchars", // (1 << 22) == ACCSET_STRING_MCHARS
|
||||
"typemap", // (1 << 23) == ACCSET_TYPEMAP
|
||||
"fcslots", // (1 << 24) == ACCSET_FCSLOTS
|
||||
"argsdata", // (1 << 25) == ACCSET_ARGS_DATA
|
||||
"slots", // (1 << 17) == ACCSET_SLOTS
|
||||
"tarray", // (1 << 18) == ACCSET_TARRAY
|
||||
"tdata", // (1 << 19) == ACCSET_TARRAY_DATA
|
||||
"iter", // (1 << 20) == ACCSET_ITER
|
||||
"iterprops", // (1 << 21) == ACCSET_ITER_PROPS
|
||||
"str", // (1 << 22) == ACCSET_STRING
|
||||
"strmchars", // (1 << 23) == ACCSET_STRING_MCHARS
|
||||
"typemap", // (1 << 24) == ACCSET_TYPEMAP
|
||||
"fcslots", // (1 << 25) == ACCSET_FCSLOTS
|
||||
"argsdata", // (1 << 26) == ACCSET_ARGS_DATA
|
||||
|
||||
"?!" // this entry should never be used, have it just in case
|
||||
};
|
||||
@ -9067,6 +9068,11 @@ TraceRecorder::equalityHelper(Value& l, Value& r, LIns* l_ins, LIns* r_ins,
|
||||
} else if (l.isObject()) {
|
||||
if (l.toObject().getClass()->ext.equality)
|
||||
RETURN_STOP_A("Can't trace extended class equality operator");
|
||||
LIns* flags_ins = lir->insLoad(LIR_ldi, l_ins, offsetof(JSObject, flags),
|
||||
ACCSET_OBJ_FLAGS);
|
||||
LIns* flag_ins = lir->ins2(LIR_andi, flags_ins, INS_CONSTU(JSObject::HAS_EQUALITY));
|
||||
guard(true, lir->insEqI_0(flag_ins), BRANCH_EXIT);
|
||||
|
||||
op = LIR_eqp;
|
||||
cond = (l == r);
|
||||
} else if (l.isBoolean()) {
|
||||
@ -16908,6 +16914,10 @@ void ValidateWriter::checkAccSet(LOpcode op, LIns* base, int32_t disp, AccSet ac
|
||||
ok = OK_OBJ_FIELD(LIR_ldp, clasp);
|
||||
break;
|
||||
|
||||
case ACCSET_OBJ_FLAGS:
|
||||
ok = OK_OBJ_FIELD(LIR_ldi, flags);
|
||||
break;
|
||||
|
||||
case ACCSET_OBJ_SHAPE:
|
||||
ok = OK_OBJ_FIELD(LIR_ldi, objShape);
|
||||
break;
|
||||
|
@ -213,6 +213,20 @@ AppendString(JSCharBuffer &cb, JSString *str)
|
||||
return cb.append(chars, end);
|
||||
}
|
||||
|
||||
/*
|
||||
* This wrapper is needed because NewBuiltinClassInstance doesn't
|
||||
* call the constructor, and we need a place to set the
|
||||
* HAS_EQUALITY bit.
|
||||
*/
|
||||
static inline JSObject *
|
||||
NewBuiltinClassInstanceXML(JSContext *cx, Class *clasp)
|
||||
{
|
||||
JSObject *obj = NewBuiltinClassInstance(cx, clasp);
|
||||
if (obj && clasp->ext.equality)
|
||||
obj->flags |= JSObject::HAS_EQUALITY;
|
||||
return obj;
|
||||
}
|
||||
|
||||
#define DEFINE_GETTER(name,code) \
|
||||
static JSBool \
|
||||
name(JSContext *cx, JSObject *obj, jsid id, jsval *vp) \
|
||||
@ -309,7 +323,7 @@ NewXMLNamespace(JSContext *cx, JSString *prefix, JSString *uri, JSBool declared)
|
||||
{
|
||||
JSObject *obj;
|
||||
|
||||
obj = NewBuiltinClassInstance(cx, &js_NamespaceClass);
|
||||
obj = NewBuiltinClassInstanceXML(cx, &js_NamespaceClass);
|
||||
if (!obj)
|
||||
return JS_FALSE;
|
||||
JS_ASSERT(JSVAL_IS_VOID(obj->getNamePrefix()));
|
||||
@ -520,7 +534,7 @@ static JSObject *
|
||||
NewXMLQName(JSContext *cx, JSString *uri, JSString *prefix, JSString *localName,
|
||||
Class *clasp = &js_QNameClass)
|
||||
{
|
||||
JSObject *obj = NewBuiltinClassInstance(cx, clasp);
|
||||
JSObject *obj = NewBuiltinClassInstanceXML(cx, clasp);
|
||||
if (!obj)
|
||||
return NULL;
|
||||
JS_ASSERT(obj->isQName());
|
||||
@ -633,7 +647,7 @@ NamespaceHelper(JSContext *cx, JSObject *obj, intN argc, jsval *argv,
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
obj = NewBuiltinClassInstance(cx, &js_NamespaceClass);
|
||||
obj = NewBuiltinClassInstanceXML(cx, &js_NamespaceClass);
|
||||
if (!obj)
|
||||
return JS_FALSE;
|
||||
}
|
||||
@ -698,6 +712,8 @@ Namespace(JSContext *cx, uintN argc, Value *vp)
|
||||
{
|
||||
JSObject *thisobj = NULL;
|
||||
(void)IsConstructing_PossiblyWithGivenThisObject(vp, &thisobj);
|
||||
if (thisobj)
|
||||
thisobj->flags |= JSObject::HAS_EQUALITY;
|
||||
return NamespaceHelper(cx, thisobj, argc, Jsvalify(vp + 2), Jsvalify(vp));
|
||||
}
|
||||
|
||||
@ -739,7 +755,7 @@ QNameHelper(JSContext *cx, JSObject *obj, Class *clasp, intN argc,
|
||||
* Create and return a new QName or AttributeName object exactly as if
|
||||
* constructed.
|
||||
*/
|
||||
obj = NewBuiltinClassInstance(cx, clasp);
|
||||
obj = NewBuiltinClassInstanceXML(cx, clasp);
|
||||
if (!obj)
|
||||
return JS_FALSE;
|
||||
}
|
||||
@ -833,6 +849,8 @@ QName(JSContext *cx, uintN argc, Value *vp)
|
||||
{
|
||||
JSObject *thisobj = NULL;
|
||||
(void)IsConstructing_PossiblyWithGivenThisObject(vp, &thisobj);
|
||||
if (thisobj)
|
||||
thisobj->flags |= JSObject::HAS_EQUALITY;
|
||||
return QNameHelper(cx, thisobj, &js_QNameClass, argc, Jsvalify(vp + 2), Jsvalify(vp));
|
||||
}
|
||||
|
||||
|
@ -98,6 +98,7 @@ mjit::Compiler::Compiler(JSContext *cx, JSStackFrame *fp)
|
||||
#if defined JS_MONOIC
|
||||
mics(ContextAllocPolicy(cx)),
|
||||
callICs(ContextAllocPolicy(cx)),
|
||||
equalityICs(ContextAllocPolicy(cx)),
|
||||
#endif
|
||||
#if defined JS_POLYIC
|
||||
pics(ContextAllocPolicy(cx)),
|
||||
@ -361,6 +362,7 @@ mjit::Compiler::finishThisUp(JITScript **jitp)
|
||||
#if defined JS_MONOIC
|
||||
sizeof(ic::MICInfo) * mics.length() +
|
||||
sizeof(ic::CallICInfo) * callICs.length() +
|
||||
sizeof(ic::EqualityICInfo) * equalityICs.length() +
|
||||
#endif
|
||||
#if defined JS_POLYIC
|
||||
sizeof(ic::PICInfo) * pics.length() +
|
||||
@ -428,12 +430,9 @@ mjit::Compiler::finishThisUp(JITScript **jitp)
|
||||
JS_ASSERT(jumpMap[offs].isValid());
|
||||
scriptMICs[i].traceHint = fullCode.locationOf(mics[i].traceHint);
|
||||
scriptMICs[i].load = fullCode.locationOf(jumpMap[offs]);
|
||||
scriptMICs[i].u.hints.hasSlowTraceHintOne = mics[i].slowTraceHintOne.isSet();
|
||||
if (mics[i].slowTraceHintOne.isSet())
|
||||
scriptMICs[i].slowTraceHintOne = stubCode.locationOf(mics[i].slowTraceHintOne.get());
|
||||
scriptMICs[i].u.hints.hasSlowTraceHintTwo = mics[i].slowTraceHintTwo.isSet();
|
||||
if (mics[i].slowTraceHintTwo.isSet())
|
||||
scriptMICs[i].slowTraceHintTwo = stubCode.locationOf(mics[i].slowTraceHintTwo.get());
|
||||
scriptMICs[i].u.hints.hasSlowTraceHint = mics[i].slowTraceHint.isSet();
|
||||
if (mics[i].slowTraceHint.isSet())
|
||||
scriptMICs[i].slowTraceHint = stubCode.locationOf(mics[i].slowTraceHint.get());
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@ -503,6 +502,34 @@ mjit::Compiler::finishThisUp(JITScript **jitp)
|
||||
stubCode.patch(callICs[i].addrLabel2, &cics[i]);
|
||||
}
|
||||
}
|
||||
|
||||
jit->nEqualityICs = equalityICs.length();
|
||||
if (equalityICs.length()) {
|
||||
jit->equalityICs = (ic::EqualityICInfo *)cursor;
|
||||
cursor += sizeof(ic::EqualityICInfo) * equalityICs.length();
|
||||
} else {
|
||||
jit->equalityICs = NULL;
|
||||
}
|
||||
|
||||
if (ic::EqualityICInfo *scriptEICs = jit->equalityICs) {
|
||||
for (size_t i = 0; i < equalityICs.length(); i++) {
|
||||
uint32 offs = uint32(equalityICs[i].jumpTarget - script->code);
|
||||
JS_ASSERT(jumpMap[offs].isValid());
|
||||
scriptEICs[i].target = fullCode.locationOf(jumpMap[offs]);
|
||||
scriptEICs[i].stubEntry = stubCode.locationOf(equalityICs[i].stubEntry);
|
||||
scriptEICs[i].stubCall = stubCode.locationOf(equalityICs[i].stubCall);
|
||||
scriptEICs[i].stub = equalityICs[i].stub;
|
||||
scriptEICs[i].lvr = equalityICs[i].lvr;
|
||||
scriptEICs[i].rvr = equalityICs[i].rvr;
|
||||
scriptEICs[i].tempReg = equalityICs[i].tempReg;
|
||||
scriptEICs[i].cond = equalityICs[i].cond;
|
||||
if (equalityICs[i].jumpToStub.isSet())
|
||||
scriptEICs[i].jumpToStub = fullCode.locationOf(equalityICs[i].jumpToStub.get());
|
||||
scriptEICs[i].fallThrough = fullCode.locationOf(equalityICs[i].fallThrough);
|
||||
|
||||
stubCode.patch(equalityICs[i].addrLabel, &scriptEICs[i]);
|
||||
}
|
||||
}
|
||||
#endif /* JS_MONOIC */
|
||||
|
||||
for (size_t i = 0; i < callPatches.length(); i++) {
|
||||
@ -4277,21 +4304,17 @@ mjit::Compiler::jsop_instanceof()
|
||||
* after rejoin()s.
|
||||
*/
|
||||
void
|
||||
mjit::Compiler::jumpAndTrace(Jump j, jsbytecode *target, Jump *slowOne, Jump *slowTwo)
|
||||
mjit::Compiler::jumpAndTrace(Jump j, jsbytecode *target, Jump *slow)
|
||||
{
|
||||
#ifndef JS_TRACER
|
||||
jumpInScript(j, target);
|
||||
if (slowOne)
|
||||
stubcc.jumpInScript(*slowOne, target);
|
||||
if (slowTwo)
|
||||
stubcc.jumpInScript(*slowTwo, target);
|
||||
if (slow)
|
||||
stubcc.jumpInScript(*slow, target);
|
||||
#else
|
||||
if (!addTraceHints || target >= PC || JSOp(*target) != JSOP_TRACE) {
|
||||
jumpInScript(j, target);
|
||||
if (slowOne)
|
||||
stubcc.jumpInScript(*slowOne, target);
|
||||
if (slowTwo)
|
||||
stubcc.jumpInScript(*slowTwo, target);
|
||||
if (slow)
|
||||
stubcc.jumpInScript(*slow, target);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -4301,19 +4324,15 @@ mjit::Compiler::jumpAndTrace(Jump j, jsbytecode *target, Jump *slowOne, Jump *sl
|
||||
mic.entry = masm.label();
|
||||
mic.jumpTarget = target;
|
||||
mic.traceHint = j;
|
||||
if (slowOne)
|
||||
mic.slowTraceHintOne = *slowOne;
|
||||
if (slowTwo)
|
||||
mic.slowTraceHintTwo = *slowTwo;
|
||||
if (slow)
|
||||
mic.slowTraceHint = *slow;
|
||||
# endif
|
||||
|
||||
Label traceStart = stubcc.masm.label();
|
||||
|
||||
stubcc.linkExitDirect(j, traceStart);
|
||||
if (slowOne)
|
||||
slowOne->linkTo(traceStart, &stubcc.masm);
|
||||
if (slowTwo)
|
||||
slowTwo->linkTo(traceStart, &stubcc.masm);
|
||||
if (slow)
|
||||
slow->linkTo(traceStart, &stubcc.masm);
|
||||
# if JS_MONOIC
|
||||
passMICAddress(mic);
|
||||
# endif
|
||||
|
@ -81,8 +81,7 @@ class Compiler : public BaseCompiler
|
||||
ic::MICInfo::Kind kind;
|
||||
jsbytecode *jumpTarget;
|
||||
Jump traceHint;
|
||||
MaybeJump slowTraceHintOne;
|
||||
MaybeJump slowTraceHintTwo;
|
||||
MaybeJump slowTraceHint;
|
||||
union {
|
||||
struct {
|
||||
bool typeConst;
|
||||
@ -94,6 +93,19 @@ class Compiler : public BaseCompiler
|
||||
} u;
|
||||
};
|
||||
|
||||
struct EqualityGenInfo {
|
||||
DataLabelPtr addrLabel;
|
||||
Label stubEntry;
|
||||
Call stubCall;
|
||||
BoolStub stub;
|
||||
MaybeJump jumpToStub;
|
||||
Label fallThrough;
|
||||
jsbytecode *jumpTarget;
|
||||
ValueRemat lvr, rvr;
|
||||
Assembler::Condition cond;
|
||||
JSC::MacroAssembler::RegisterID tempReg;
|
||||
};
|
||||
|
||||
/* InlineFrameAssembler wants to see this. */
|
||||
public:
|
||||
struct CallGenInfo {
|
||||
@ -215,6 +227,7 @@ class Compiler : public BaseCompiler
|
||||
#if defined JS_MONOIC
|
||||
js::Vector<MICGenInfo, 64> mics;
|
||||
js::Vector<CallGenInfo, 64> callICs;
|
||||
js::Vector<EqualityGenInfo, 64> equalityICs;
|
||||
#endif
|
||||
#if defined JS_POLYIC
|
||||
js::Vector<PICGenInfo, 16> pics;
|
||||
@ -274,7 +287,7 @@ class Compiler : public BaseCompiler
|
||||
bool constructThis();
|
||||
|
||||
/* Opcode handlers. */
|
||||
void jumpAndTrace(Jump j, jsbytecode *target, Jump *slowOne = NULL, Jump *slowTwo = NULL);
|
||||
void jumpAndTrace(Jump j, jsbytecode *target, Jump *slow = NULL);
|
||||
void jsop_bindname(uint32 index);
|
||||
void jsop_setglobal(uint32 index);
|
||||
void jsop_getglobal(uint32 index);
|
||||
|
@ -957,8 +957,6 @@ mjit::Compiler::jsop_equality_int_string(JSOp op, BoolStub stub, jsbytecode *tar
|
||||
|
||||
bool lhsInt = lhs->isType(JSVAL_TYPE_INT32);
|
||||
bool rhsInt = rhs->isType(JSVAL_TYPE_INT32);
|
||||
bool lhsString = lhs->isType(JSVAL_TYPE_STRING);
|
||||
bool rhsString = rhs->isType(JSVAL_TYPE_STRING);
|
||||
|
||||
/* Invert the condition if fusing with an IFEQ branch. */
|
||||
bool flipCondition = (target && fused == JSOP_IFEQ);
|
||||
@ -995,15 +993,14 @@ mjit::Compiler::jsop_equality_int_string(JSOp op, BoolStub stub, jsbytecode *tar
|
||||
*/
|
||||
frame.syncAndKill(Registers(Registers::AvailRegs), Uses(frame.frameDepth()), Uses(2));
|
||||
|
||||
/* Temporary for OOL string path. */
|
||||
RegisterID T1 = frame.allocReg();
|
||||
RegisterID tempReg = frame.allocReg();
|
||||
|
||||
frame.pop();
|
||||
frame.pop();
|
||||
frame.discardFrame();
|
||||
|
||||
/* Start of the slow path for equality stub call. */
|
||||
Label stubCall = stubcc.masm.label();
|
||||
Label stubEntry = stubcc.masm.label();
|
||||
|
||||
JaegerSpew(JSpew_Insns, " ---- BEGIN STUB CALL CODE ---- \n");
|
||||
|
||||
@ -1011,8 +1008,31 @@ mjit::Compiler::jsop_equality_int_string(JSOp op, BoolStub stub, jsbytecode *tar
|
||||
frame.ensureValueSynced(stubcc.masm, lhs, lvr);
|
||||
frame.ensureValueSynced(stubcc.masm, rhs, rvr);
|
||||
|
||||
/* Call the stub, adjusting for the two values just pushed. */
|
||||
stubcc.call(stub, frame.stackDepth() + script->nfixed + 2);
|
||||
bool needStub = true;
|
||||
|
||||
#ifdef JS_MONOIC
|
||||
EqualityGenInfo ic;
|
||||
|
||||
ic.cond = cond;
|
||||
ic.tempReg = tempReg;
|
||||
ic.lvr = lvr;
|
||||
ic.rvr = rvr;
|
||||
ic.stubEntry = stubEntry;
|
||||
ic.stub = stub;
|
||||
|
||||
bool useIC = !addTraceHints || target >= PC;
|
||||
|
||||
/* Call the IC stub, which may generate a fast path. */
|
||||
if (useIC) {
|
||||
/* Adjust for the two values just pushed. */
|
||||
ic.addrLabel = stubcc.masm.moveWithPatch(ImmPtr(NULL), Registers::ArgReg1);
|
||||
ic.stubCall = stubcc.call(ic::Equality, frame.stackDepth() + script->nfixed + 2);
|
||||
needStub = false;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (needStub)
|
||||
stubcc.call(stub, frame.stackDepth() + script->nfixed + 2);
|
||||
|
||||
/*
|
||||
* The stub call has no need to rejoin, since state is synced.
|
||||
@ -1027,94 +1047,59 @@ mjit::Compiler::jsop_equality_int_string(JSOp op, BoolStub stub, jsbytecode *tar
|
||||
|
||||
JaegerSpew(JSpew_Insns, " ---- END STUB CALL CODE ---- \n");
|
||||
|
||||
/* Emit an OOL string path if both sides might be strings. */
|
||||
bool stringPath = !(lhsInt || rhsInt);
|
||||
Label missedInt = stubCall;
|
||||
Jump stringFallthrough;
|
||||
Jump stringMatched;
|
||||
|
||||
if (stringPath) {
|
||||
missedInt = stubcc.masm.label();
|
||||
|
||||
if (!lhsString) {
|
||||
Jump lhsFail = stubcc.masm.testString(Assembler::NotEqual, lvr.typeReg());
|
||||
lhsFail.linkTo(stubCall, &stubcc.masm);
|
||||
}
|
||||
if (!rhsString) {
|
||||
JS_ASSERT(!rhsConst);
|
||||
Jump rhsFail = stubcc.masm.testString(Assembler::NotEqual, rvr.typeReg());
|
||||
rhsFail.linkTo(stubCall, &stubcc.masm);
|
||||
}
|
||||
|
||||
/* Test if lhs/rhs are atomized. */
|
||||
Imm32 atomizedFlags(JSString::FLAT | JSString::ATOMIZED);
|
||||
|
||||
stubcc.masm.load32(Address(lvr.dataReg(), offsetof(JSString, mLengthAndFlags)), T1);
|
||||
stubcc.masm.and32(Imm32(JSString::TYPE_FLAGS_MASK), T1);
|
||||
Jump lhsNotAtomized = stubcc.masm.branch32(Assembler::NotEqual, T1, atomizedFlags);
|
||||
lhsNotAtomized.linkTo(stubCall, &stubcc.masm);
|
||||
|
||||
if (!rhsConst) {
|
||||
stubcc.masm.load32(Address(rvr.dataReg(), offsetof(JSString, mLengthAndFlags)), T1);
|
||||
stubcc.masm.and32(Imm32(JSString::TYPE_FLAGS_MASK), T1);
|
||||
Jump rhsNotAtomized = stubcc.masm.branch32(Assembler::NotEqual, T1, atomizedFlags);
|
||||
rhsNotAtomized.linkTo(stubCall, &stubcc.masm);
|
||||
}
|
||||
|
||||
if (rhsConst) {
|
||||
JSString *str = rval.toString();
|
||||
JS_ASSERT(str->isAtomized());
|
||||
stringMatched = stubcc.masm.branchPtr(cond, lvr.dataReg(), ImmPtr(str));
|
||||
} else {
|
||||
stringMatched = stubcc.masm.branchPtr(cond, lvr.dataReg(), rvr.dataReg());
|
||||
}
|
||||
|
||||
stringFallthrough = stubcc.masm.jump();
|
||||
}
|
||||
|
||||
Jump fast;
|
||||
if (lhsString || rhsString) {
|
||||
/* Jump straight to the OOL string path. */
|
||||
Jump jump = masm.jump();
|
||||
stubcc.linkExitDirect(jump, missedInt);
|
||||
fast = masm.jump();
|
||||
} else {
|
||||
/* Emit inline integer path. */
|
||||
MaybeJump firstStubJump;
|
||||
|
||||
if (lhsInt || rhsInt || (!lhs->isTypeKnown() && !rhs->isTypeKnown())) {
|
||||
if (!lhsInt) {
|
||||
Jump lhsFail = masm.testInt32(Assembler::NotEqual, lvr.typeReg());
|
||||
stubcc.linkExitDirect(lhsFail, missedInt);
|
||||
stubcc.linkExitDirect(lhsFail, stubEntry);
|
||||
firstStubJump = lhsFail;
|
||||
}
|
||||
if (!rhsInt) {
|
||||
if (rhsConst) {
|
||||
Jump rhsFail = masm.jump();
|
||||
stubcc.linkExitDirect(rhsFail, missedInt);
|
||||
} else {
|
||||
Jump rhsFail = masm.testInt32(Assembler::NotEqual, rvr.typeReg());
|
||||
stubcc.linkExitDirect(rhsFail, missedInt);
|
||||
}
|
||||
Jump rhsFail = masm.testInt32(Assembler::NotEqual, rvr.typeReg());
|
||||
stubcc.linkExitDirect(rhsFail, stubEntry);
|
||||
if (!firstStubJump.isSet())
|
||||
firstStubJump = rhsFail;
|
||||
}
|
||||
|
||||
if (rhsConst)
|
||||
fast = masm.branch32(cond, lvr.dataReg(), Imm32(rval.toInt32()));
|
||||
else
|
||||
fast = masm.branch32(cond, lvr.dataReg(), rvr.dataReg());
|
||||
|
||||
jumpInScript(fast, target);
|
||||
} else {
|
||||
Jump j = masm.jump();
|
||||
stubcc.linkExitDirect(j, stubEntry);
|
||||
firstStubJump = j;
|
||||
|
||||
/* This is just a dummy jump. */
|
||||
fast = masm.jump();
|
||||
}
|
||||
|
||||
/* Jump from the stub call and string path fallthroughs to here. */
|
||||
#ifdef JS_MONOIC
|
||||
ic.jumpToStub = firstStubJump;
|
||||
if (useIC) {
|
||||
ic.fallThrough = masm.label();
|
||||
ic.jumpTarget = target;
|
||||
equalityICs.append(ic);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Jump from the stub call fallthrough to here. */
|
||||
stubcc.crossJump(stubFallthrough, masm.label());
|
||||
if (stringPath)
|
||||
stubcc.crossJump(stringFallthrough, masm.label());
|
||||
|
||||
/*
|
||||
* NB: jumpAndTrace emits to the OOL path, so make sure not to use it
|
||||
* in the middle of an in-progress slow path.
|
||||
*/
|
||||
jumpAndTrace(fast, target, &stubBranch, stringPath ? &stringMatched : NULL);
|
||||
jumpAndTrace(fast, target, &stubBranch);
|
||||
} else {
|
||||
/* No fusing. Compare, set, and push a boolean. */
|
||||
|
||||
/* Should have filtered these out in the caller. */
|
||||
JS_ASSERT(!lhsString && !rhsString);
|
||||
JS_ASSERT(!lhs->isType(JSVAL_TYPE_STRING) && !rhs->isType(JSVAL_TYPE_STRING));
|
||||
|
||||
/* Test the types. */
|
||||
if (!lhsInt) {
|
||||
|
@ -781,11 +781,8 @@ DisableTraceHint(VMFrame &f, ic::MICInfo &mic)
|
||||
|
||||
DisableTraceHintSingle(mic.traceHint, mic.load);
|
||||
|
||||
if (mic.u.hints.hasSlowTraceHintOne)
|
||||
DisableTraceHintSingle(mic.slowTraceHintOne, mic.load);
|
||||
|
||||
if (mic.u.hints.hasSlowTraceHintTwo)
|
||||
DisableTraceHintSingle(mic.slowTraceHintTwo, mic.load);
|
||||
if (mic.u.hints.hasSlowTraceHint)
|
||||
DisableTraceHintSingle(mic.slowTraceHint, mic.load);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -818,6 +818,13 @@ mjit::JITScript::release()
|
||||
#endif
|
||||
|
||||
#if defined JS_MONOIC
|
||||
for (JSC::ExecutablePool **pExecPool = execPools.begin();
|
||||
pExecPool != execPools.end();
|
||||
++pExecPool)
|
||||
{
|
||||
(*pExecPool)->release();
|
||||
}
|
||||
|
||||
for (uint32 i = 0; i < nCallICs; i++)
|
||||
callICs[i].releasePools();
|
||||
#endif
|
||||
|
@ -146,6 +146,7 @@ namespace ic {
|
||||
# endif
|
||||
# if defined JS_MONOIC
|
||||
struct MICInfo;
|
||||
struct EqualityICInfo;
|
||||
struct CallICInfo;
|
||||
# endif
|
||||
}
|
||||
@ -175,6 +176,7 @@ typedef void (JS_FASTCALL *VoidStubCallIC)(VMFrame &, js::mjit::ic::CallICInfo *
|
||||
typedef void * (JS_FASTCALL *VoidPtrStubCallIC)(VMFrame &, js::mjit::ic::CallICInfo *);
|
||||
typedef void (JS_FASTCALL *VoidStubMIC)(VMFrame &, js::mjit::ic::MICInfo *);
|
||||
typedef void * (JS_FASTCALL *VoidPtrStubMIC)(VMFrame &, js::mjit::ic::MICInfo *);
|
||||
typedef JSBool (JS_FASTCALL *BoolStubEqualityIC)(VMFrame &, js::mjit::ic::EqualityICInfo *);
|
||||
#endif
|
||||
#ifdef JS_POLYIC
|
||||
typedef void (JS_FASTCALL *VoidStubPIC)(VMFrame &, js::mjit::ic::PICInfo *);
|
||||
@ -196,6 +198,12 @@ struct JITScript {
|
||||
uint32 nMICs; /* number of MonoICs */
|
||||
ic::CallICInfo *callICs; /* CallICs in this script. */
|
||||
uint32 nCallICs; /* number of call ICs */
|
||||
ic::EqualityICInfo *equalityICs;
|
||||
uint32 nEqualityICs;
|
||||
|
||||
// Additional ExecutablePools that IC stubs were generated into.
|
||||
typedef Vector<JSC::ExecutablePool *, 0, SystemAllocPolicy> ExecPoolVector;
|
||||
ExecPoolVector execPools;
|
||||
#endif
|
||||
#ifdef JS_POLYIC
|
||||
ic::PICInfo *pics; /* PICs in this script */
|
||||
|
@ -211,6 +211,206 @@ ic::SetGlobalName(VMFrame &f, ic::MICInfo *ic)
|
||||
GetStubForSetGlobalName(f)(f, atom);
|
||||
}
|
||||
|
||||
class EqualityICLinker : public LinkerHelper
|
||||
{
|
||||
VMFrame &f;
|
||||
|
||||
public:
|
||||
EqualityICLinker(JSContext *cx, VMFrame &f)
|
||||
: LinkerHelper(cx), f(f)
|
||||
{ }
|
||||
|
||||
bool init(Assembler &masm) {
|
||||
JSC::ExecutablePool *pool = LinkerHelper::init(masm);
|
||||
if (!pool)
|
||||
return false;
|
||||
JSScript *script = f.fp()->script();
|
||||
JITScript *jit = script->getJIT(f.fp()->isConstructing());
|
||||
if (!jit->execPools.append(pool)) {
|
||||
pool->release();
|
||||
js_ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
/* Rough over-estimate of how much memory we need to unprotect. */
|
||||
static const uint32 INLINE_PATH_LENGTH = 64;
|
||||
|
||||
class EqualityCompiler : public BaseCompiler
|
||||
{
|
||||
VMFrame &f;
|
||||
EqualityICInfo ⁣
|
||||
|
||||
Vector<Jump, 4, SystemAllocPolicy> jumpList;
|
||||
Jump trueJump;
|
||||
Jump falseJump;
|
||||
|
||||
public:
|
||||
EqualityCompiler(VMFrame &f, EqualityICInfo &ic)
|
||||
: BaseCompiler(f.cx), f(f), ic(ic), jumpList(SystemAllocPolicy())
|
||||
{
|
||||
}
|
||||
|
||||
void linkToStub(Jump j)
|
||||
{
|
||||
jumpList.append(j);
|
||||
}
|
||||
|
||||
void linkTrue(Jump j)
|
||||
{
|
||||
trueJump = j;
|
||||
}
|
||||
|
||||
void linkFalse(Jump j)
|
||||
{
|
||||
falseJump = j;
|
||||
}
|
||||
|
||||
void generateStringPath(Assembler &masm)
|
||||
{
|
||||
ValueRemat &lvr = ic.lvr;
|
||||
ValueRemat &rvr = ic.rvr;
|
||||
|
||||
if (!lvr.isConstant && !lvr.isType(JSVAL_TYPE_STRING)) {
|
||||
Jump lhsFail = masm.testString(Assembler::NotEqual, lvr.typeReg());
|
||||
linkToStub(lhsFail);
|
||||
}
|
||||
|
||||
if (!rvr.isConstant && !rvr.isType(JSVAL_TYPE_STRING)) {
|
||||
Jump rhsFail = masm.testString(Assembler::NotEqual, rvr.typeReg());
|
||||
linkToStub(rhsFail);
|
||||
}
|
||||
|
||||
RegisterID tmp = ic.tempReg;
|
||||
|
||||
/* Test if lhs/rhs are atomized. */
|
||||
Imm32 atomizedFlags(JSString::FLAT | JSString::ATOMIZED);
|
||||
|
||||
masm.load32(Address(lvr.dataReg(), offsetof(JSString, mLengthAndFlags)), tmp);
|
||||
masm.and32(Imm32(JSString::TYPE_FLAGS_MASK), tmp);
|
||||
Jump lhsNotAtomized = masm.branch32(Assembler::NotEqual, tmp, atomizedFlags);
|
||||
linkToStub(lhsNotAtomized);
|
||||
|
||||
if (!rvr.isConstant) {
|
||||
masm.load32(Address(rvr.dataReg(), offsetof(JSString, mLengthAndFlags)), tmp);
|
||||
masm.and32(Imm32(JSString::TYPE_FLAGS_MASK), tmp);
|
||||
Jump rhsNotAtomized = masm.branch32(Assembler::NotEqual, tmp, atomizedFlags);
|
||||
linkToStub(rhsNotAtomized);
|
||||
}
|
||||
|
||||
if (rvr.isConstant) {
|
||||
JSString *str = Valueify(rvr.u.v).toString();
|
||||
JS_ASSERT(str->isAtomized());
|
||||
Jump test = masm.branchPtr(ic.cond, lvr.dataReg(), ImmPtr(str));
|
||||
linkTrue(test);
|
||||
} else {
|
||||
Jump test = masm.branchPtr(ic.cond, lvr.dataReg(), rvr.dataReg());
|
||||
linkTrue(test);
|
||||
}
|
||||
|
||||
Jump fallthrough = masm.jump();
|
||||
linkFalse(fallthrough);
|
||||
}
|
||||
|
||||
void generateObjectPath(Assembler &masm)
|
||||
{
|
||||
ValueRemat &lvr = ic.lvr;
|
||||
ValueRemat &rvr = ic.rvr;
|
||||
|
||||
if (!lvr.isConstant && !lvr.isType(JSVAL_TYPE_OBJECT)) {
|
||||
Jump lhsFail = masm.testObject(Assembler::NotEqual, lvr.typeReg());
|
||||
linkToStub(lhsFail);
|
||||
}
|
||||
|
||||
if (!rvr.isConstant && !rvr.isType(JSVAL_TYPE_OBJECT)) {
|
||||
Jump rhsFail = masm.testObject(Assembler::NotEqual, rvr.typeReg());
|
||||
linkToStub(rhsFail);
|
||||
}
|
||||
|
||||
Jump lhsHasEq = masm.branchTest32(Assembler::NonZero,
|
||||
Address(lvr.dataReg(),
|
||||
offsetof(JSObject, flags)),
|
||||
Imm32(JSObject::HAS_EQUALITY));
|
||||
linkToStub(lhsHasEq);
|
||||
|
||||
if (rvr.isConstant) {
|
||||
JSObject *obj = &Valueify(rvr.u.v).toObject();
|
||||
Jump test = masm.branchPtr(ic.cond, lvr.dataReg(), ImmPtr(obj));
|
||||
linkTrue(test);
|
||||
} else {
|
||||
Jump test = masm.branchPtr(ic.cond, lvr.dataReg(), rvr.dataReg());
|
||||
linkTrue(test);
|
||||
}
|
||||
|
||||
Jump fallthrough = masm.jump();
|
||||
linkFalse(fallthrough);
|
||||
}
|
||||
|
||||
bool linkForIC(Assembler &masm)
|
||||
{
|
||||
EqualityICLinker buffer(cx, f);
|
||||
if (!buffer.init(masm))
|
||||
return false;
|
||||
|
||||
/* Set the targets of all type test failures to go to the stub. */
|
||||
for (size_t i = 0; i < jumpList.length(); i++)
|
||||
buffer.link(jumpList[i], ic.stubEntry);
|
||||
jumpList.clear();
|
||||
|
||||
/* Set the targets for the the success and failure of the actual equality test. */
|
||||
buffer.link(trueJump, ic.target);
|
||||
buffer.link(falseJump, ic.fallThrough);
|
||||
|
||||
CodeLocationLabel cs = buffer.finalizeCodeAddendum();
|
||||
|
||||
/* Jump to the newly generated code instead of to the IC. */
|
||||
JSC::RepatchBuffer jumpRepatcher(ic.jumpToStub.executableAddress(), INLINE_PATH_LENGTH);
|
||||
jumpRepatcher.relink(ic.jumpToStub, cs);
|
||||
|
||||
/* Overwrite the call to the IC with a call to the stub. */
|
||||
JSC::RepatchBuffer stubRepatcher(ic.stubCall.executableAddress(), INLINE_PATH_LENGTH);
|
||||
JSC::FunctionPtr fptr(JS_FUNC_TO_DATA_PTR(void *, ic.stub));
|
||||
stubRepatcher.relink(ic.stubCall, fptr);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool update()
|
||||
{
|
||||
if (!ic.generated) {
|
||||
Assembler masm;
|
||||
Value rval = f.regs.sp[-1];
|
||||
Value lval = f.regs.sp[-2];
|
||||
|
||||
if (rval.isObject() && lval.isObject()) {
|
||||
generateObjectPath(masm);
|
||||
ic.generated = true;
|
||||
} else if (rval.isString() && lval.isString()) {
|
||||
generateStringPath(masm);
|
||||
ic.generated = true;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
|
||||
return linkForIC(masm);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
JSBool JS_FASTCALL
|
||||
ic::Equality(VMFrame &f, ic::EqualityICInfo *ic)
|
||||
{
|
||||
EqualityCompiler cc(f, *ic);
|
||||
if (!cc.update())
|
||||
THROWV(JS_FALSE);
|
||||
|
||||
return ic->stub(f);
|
||||
}
|
||||
|
||||
static void * JS_FASTCALL
|
||||
SlowCallFromIC(VMFrame &f, ic::CallICInfo *ic)
|
||||
{
|
||||
|
@ -89,8 +89,7 @@ struct MICInfo {
|
||||
|
||||
/* Used by TRACER. */
|
||||
JSC::CodeLocationJump traceHint;
|
||||
JSC::CodeLocationJump slowTraceHintOne;
|
||||
JSC::CodeLocationJump slowTraceHintTwo;
|
||||
JSC::CodeLocationJump slowTraceHint;
|
||||
|
||||
/* Used by all MICs. */
|
||||
Kind kind : 3;
|
||||
@ -103,8 +102,7 @@ struct MICInfo {
|
||||
} name;
|
||||
/* Used by TRACER. */
|
||||
struct {
|
||||
bool hasSlowTraceHintOne : 1;
|
||||
bool hasSlowTraceHintTwo : 1;
|
||||
bool hasSlowTraceHint : 1;
|
||||
} hints;
|
||||
} u;
|
||||
};
|
||||
@ -112,6 +110,25 @@ struct MICInfo {
|
||||
void JS_FASTCALL GetGlobalName(VMFrame &f, ic::MICInfo *ic);
|
||||
void JS_FASTCALL SetGlobalName(VMFrame &f, ic::MICInfo *ic);
|
||||
|
||||
struct EqualityICInfo {
|
||||
typedef JSC::MacroAssembler::RegisterID RegisterID;
|
||||
|
||||
JSC::CodeLocationLabel stubEntry;
|
||||
JSC::CodeLocationCall stubCall;
|
||||
BoolStub stub;
|
||||
JSC::CodeLocationLabel target;
|
||||
JSC::CodeLocationLabel fallThrough;
|
||||
JSC::CodeLocationJump jumpToStub;
|
||||
|
||||
ValueRemat lvr, rvr;
|
||||
|
||||
bool generated : 1;
|
||||
JSC::MacroAssembler::RegisterID tempReg : 5;
|
||||
Assembler::Condition cond : 6;
|
||||
};
|
||||
|
||||
JSBool JS_FASTCALL Equality(VMFrame &f, ic::EqualityICInfo *ic);
|
||||
|
||||
/* See MonoIC.cpp, CallCompiler for more information on call ICs. */
|
||||
struct CallICInfo {
|
||||
typedef JSC::MacroAssembler::RegisterID RegisterID;
|
||||
|
@ -70,6 +70,11 @@ struct ValueRemat {
|
||||
JS_ASSERT(!isConstant && !u.s.isTypeKnown);
|
||||
return u.s.type.reg;
|
||||
}
|
||||
|
||||
bool isType(JSValueType type_) const {
|
||||
JS_ASSERT(!isConstant);
|
||||
return u.s.isTypeKnown && u.s.type.knownType == type_;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -133,6 +133,7 @@ class StubCompiler
|
||||
STUB_CALL_TYPE(VoidPtrStubMIC);
|
||||
STUB_CALL_TYPE(VoidStubCallIC);
|
||||
STUB_CALL_TYPE(VoidPtrStubCallIC);
|
||||
STUB_CALL_TYPE(BoolStubEqualityIC);
|
||||
#endif
|
||||
|
||||
#undef STUB_CALL_TYPE
|
||||
|
Loading…
x
Reference in New Issue
Block a user