From 8ab67f850c66c0880f69ead218f1c9ad200c2133 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Mon, 4 Oct 2010 14:13:33 -0700 Subject: [PATCH] ICs for scripted new (bug 589398, r=luke,dmandelin). --- js/src/jsapi.cpp | 2 +- js/src/jsbuiltins.h | 2 +- js/src/jscompartment.cpp | 4 +- js/src/jsdbgapi.cpp | 11 +- js/src/jsemit.cpp | 9 +- js/src/jsinterp.cpp | 158 +++-- js/src/jsinterp.h | 2 +- js/src/jsiter.cpp | 2 +- js/src/jsobj.cpp | 25 +- js/src/jsobj.h | 12 +- js/src/jsopcode.tbl | 3 + js/src/jsproxy.cpp | 2 +- js/src/jsscript.cpp | 12 +- js/src/jsscript.h | 71 +- js/src/jstracer.cpp | 37 +- js/src/jstracer.h | 2 +- js/src/methodjit/BaseAssembler.h | 5 + js/src/methodjit/Compiler.cpp | 609 ++++++++++-------- js/src/methodjit/Compiler.h | 23 +- js/src/methodjit/FrameState.cpp | 19 +- js/src/methodjit/FrameState.h | 7 +- js/src/methodjit/InvokeHelpers.cpp | 88 ++- js/src/methodjit/MethodJIT.cpp | 56 +- js/src/methodjit/MethodJIT.h | 85 ++- js/src/methodjit/MonoIC.cpp | 157 ++--- js/src/methodjit/MonoIC.h | 12 +- js/src/methodjit/PolyIC.cpp | 142 ++-- js/src/methodjit/PolyIC.h | 15 +- js/src/methodjit/Retcon.cpp | 65 +- js/src/methodjit/Retcon.h | 3 +- js/src/methodjit/StubCalls-inl.h | 1 - js/src/methodjit/StubCalls.cpp | 42 +- js/src/methodjit/StubCalls.h | 10 +- js/src/methodjit/StubCompiler.h | 5 + js/src/shell/js.cpp | 2 +- .../tests/jaeger/bug563000/simple-trap-2.js | 2 +- .../jaeger/bug563000/trap-force-return-1.js | 2 +- .../jaeger/bug563000/trap-own-callsite.js | 2 +- .../jaeger/bug563000/trap-self-as-parent.js | 2 +- .../jaeger/bug563000/trap-self-from-trap.js | 4 +- 40 files changed, 995 insertions(+), 717 deletions(-) diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 5b6917660ea0..0527dc85185d 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -2990,7 +2990,7 @@ JS_NewObjectForConstructor(JSContext *cx, const jsval *vp) CHECK_REQUEST(cx); assertSameCompartment(cx, *vp); - return js_NewInstance(cx, JSVAL_TO_OBJECT(*vp)); + return js_CreateThis(cx, JSVAL_TO_OBJECT(*vp)); } JS_PUBLIC_API(JSBool) diff --git a/js/src/jsbuiltins.h b/js/src/jsbuiltins.h index 275663e05028..e884098dcd78 100644 --- a/js/src/jsbuiltins.h +++ b/js/src/jsbuiltins.h @@ -612,7 +612,7 @@ JS_DECLARE_CALLINFO(js_NumberToString) /* Defined in jsobj.cpp. */ JS_DECLARE_CALLINFO(js_Object_tn) -JS_DECLARE_CALLINFO(js_NewInstanceFromTrace) +JS_DECLARE_CALLINFO(js_CreateThisFromTrace) JS_DECLARE_CALLINFO(js_NonEmptyObject) /* Defined in jsregexp.cpp. */ diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index 96b4ad80dd0e..b6ee679d48c2 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -318,7 +318,7 @@ JSCompartment::sweep(JSContext *cx) #if defined JS_METHODJIT && defined JS_MONOIC for (JSCList *cursor = scripts.next; cursor != &scripts; cursor = cursor->next) { JSScript *script = reinterpret_cast(cursor); - if (script->jit) + if (script->hasJITCode()) mjit::ic::SweepCallICs(script); } #endif @@ -333,7 +333,7 @@ JSCompartment::purge(JSContext *cx) for (JSScript *script = (JSScript *)scripts.next; &script->links != &scripts; script = (JSScript *)script->links.next) { - if (script->jit) { + if (script->hasJITCode()) { # if defined JS_POLYIC mjit::ic::PurgePICs(cx, script); # endif diff --git a/js/src/jsdbgapi.cpp b/js/src/jsdbgapi.cpp index 0b92a81b6218..e6fc2e91fec5 100644 --- a/js/src/jsdbgapi.cpp +++ b/js/src/jsdbgapi.cpp @@ -116,8 +116,7 @@ js_SetDebugMode(JSContext *cx, JSBool debug) &script->links != &cx->compartment->scripts; script = (JSScript *)script->links.next) { if (script->debugMode != debug && - script->ncode && - script->ncode != JS_UNJITTABLE_METHOD && + script->hasJITCode() && !IsScriptLive(cx, script)) { /* * In the event that this fails, debug mode is left partially on, @@ -236,6 +235,10 @@ JS_SetTrap(JSContext *cx, JSScript *script, jsbytecode *pc, return JS_FALSE; } + // Do not trap BEGIN, it's a special prologue opcode. + if (JSOp(*pc) == JSOP_BEGIN) + pc += JSOP_BEGIN_LENGTH; + JS_ASSERT((JSOp) *pc != JSOP_TRAP); junk = NULL; rt = cx->runtime; @@ -274,7 +277,7 @@ JS_SetTrap(JSContext *cx, JSScript *script, jsbytecode *pc, cx->free(junk); #ifdef JS_METHODJIT - if (script->ncode != NULL && script->ncode != JS_UNJITTABLE_METHOD) { + if (script->hasJITCode()) { mjit::Recompiler recompiler(cx, script); if (!recompiler.recompile()) return JS_FALSE; @@ -327,7 +330,7 @@ JS_ClearTrap(JSContext *cx, JSScript *script, jsbytecode *pc, DBG_UNLOCK(cx->runtime); #ifdef JS_METHODJIT - if (script->ncode != NULL && script->ncode != JS_UNJITTABLE_METHOD) { + if (script->hasJITCode()) { mjit::Recompiler recompiler(cx, script); recompiler.recompile(); } diff --git a/js/src/jsemit.cpp b/js/src/jsemit.cpp index 7d4db58b24f0..d857d1859a1d 100644 --- a/js/src/jsemit.cpp +++ b/js/src/jsemit.cpp @@ -3678,10 +3678,15 @@ bad: JSBool js_EmitFunctionScript(JSContext *cx, JSCodeGenerator *cg, JSParseNode *body) { + CG_SWITCH_TO_PROLOG(cg); + JS_ASSERT(CG_NEXT(cg) == CG_BASE(cg)); + if (js_Emit1(cx, cg, JSOP_BEGIN) < 0) + return false; + CG_SWITCH_TO_MAIN(cg); + if (cg->flags & TCF_FUN_IS_GENERATOR) { - /* JSOP_GENERATOR must be the first instruction. */ + /* JSOP_GENERATOR must be the first real instruction. */ CG_SWITCH_TO_PROLOG(cg); - JS_ASSERT(CG_NEXT(cg) == CG_BASE(cg)); if (js_Emit1(cx, cg, JSOP_GENERATOR) < 0) return false; CG_SWITCH_TO_MAIN(cg); diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index 2879e8094d0c..ea33bbade29e 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -134,12 +134,13 @@ JSStackFrame::pc(JSContext *cx, JSStackFrame *next) #if defined(JS_METHODJIT) && defined(JS_MONOIC) JSScript *script = this->script(); + js::mjit::JITScript *jit = script->getJIT(isConstructing()); size_t low = 0; - size_t high = script->jit->nCallICs; + size_t high = jit->nCallICs; while (high > low + 1) { /* Could overflow here on a script with 2 billion calls. Oh well. */ size_t mid = (high + low) / 2; - void *entry = script->callICs[mid].funGuard.executableAddress(); + void *entry = jit->callICs[mid].funGuard.executableAddress(); /* * Use >= here as the return address of the call is likely to be @@ -151,7 +152,7 @@ JSStackFrame::pc(JSContext *cx, JSStackFrame *next) low = mid; } - js::mjit::ic::CallICInfo &callIC = script->callICs[low]; + js::mjit::ic::CallICInfo &callIC = jit->callICs[low]; JS_ASSERT((uint8*)callIC.funGuard.executableAddress() + callIC.joinPointOffset == next->ncode_); return callIC.pc; @@ -616,7 +617,7 @@ struct AutoInterpPreparer { }; JS_REQUIRES_STACK bool -RunScript(JSContext *cx, JSScript *script, JSFunction *fun, JSObject &scopeChain) +RunScript(JSContext *cx, JSScript *script, JSStackFrame *fp) { JS_ASSERT(script); @@ -626,8 +627,11 @@ RunScript(JSContext *cx, JSScript *script, JSFunction *fun, JSObject &scopeChain AutoInterpPreparer prepareInterp(cx, script); + JS_ASSERT(fp == cx->fp()); + JS_ASSERT(fp->script() == script); + #ifdef JS_METHODJIT - mjit::CompileStatus status = mjit::CanMethodJIT(cx, script, fun, &scopeChain); + mjit::CompileStatus status = mjit::CanMethodJIT(cx, script, fp); if (status == mjit::Compile_Error) return JS_FALSE; @@ -635,7 +639,7 @@ RunScript(JSContext *cx, JSScript *script, JSFunction *fun, JSObject &scopeChain return mjit::JaegerShot(cx); #endif - return Interpret(cx, cx->fp()); + return Interpret(cx, fp); } /* @@ -686,8 +690,10 @@ Invoke(JSContext *cx, const CallArgs &argsRef, uint32 flags) /* Handle the empty-script special case. */ if (JS_UNLIKELY(script->isEmpty())) { if (flags & JSINVOKE_CONSTRUCT) { - JS_ASSERT(args.thisv().isObject()); - args.rval() = args.thisv(); + JSObject *obj = js_CreateThisForFunction(cx, &callee); + if (!obj) + return false; + args.rval().setObject(*obj); } else { args.rval().setUndefined(); } @@ -718,19 +724,20 @@ Invoke(JSContext *cx, const CallArgs &argsRef, uint32 flags) * and fp->scopeChain is correct because the thisObject hook may call * JS_GetScopeChain. */ - Value &thisv = fp->functionThis(); - JS_ASSERT_IF(flags & JSINVOKE_CONSTRUCT, !thisv.isPrimitive()); - if (thisv.isObject() && !(flags & JSINVOKE_CONSTRUCT)) { - /* - * We must call the thisObject hook in case we are not called from the - * interpreter, where a prior bytecode has computed an appropriate - * |this| already. - */ - JSObject *thisp = thisv.toObject().thisObject(cx); - if (!thisp) - return false; - JS_ASSERT(IsSaneThisObject(*thisp)); - thisv.setObject(*thisp); + if (!(flags & JSINVOKE_CONSTRUCT)) { + Value &thisv = fp->functionThis(); + if (thisv.isObject()) { + /* + * We must call the thisObject hook in case we are not called from the + * interpreter, where a prior bytecode has computed an appropriate + * |this| already. + */ + JSObject *thisp = thisv.toObject().thisObject(cx); + if (!thisp) + return false; + JS_ASSERT(IsSaneThisObject(*thisp)); + thisv.setObject(*thisp); + } } JSInterpreterHook hook = cx->debugHooks->callHook; @@ -743,7 +750,7 @@ Invoke(JSContext *cx, const CallArgs &argsRef, uint32 flags) { AutoPreserveEnumerators preserve(cx); Probes::enterJSFun(cx, fun); - ok = RunScript(cx, script, fun, fp->scopeChain()); + ok = RunScript(cx, script, fp); Probes::exitJSFun(cx, fun); } @@ -756,6 +763,8 @@ Invoke(JSContext *cx, const CallArgs &argsRef, uint32 flags) PutActivationObjects(cx, fp); args.rval() = fp->returnValue(); + JS_ASSERT_IF(flags & JSINVOKE_CONSTRUCT, !args.rval().isPrimitive()); + return ok; } @@ -900,7 +909,7 @@ Execute(JSContext *cx, JSObject *chain, JSScript *script, /* Run script until JSOP_STOP or error. */ AutoPreserveEnumerators preserve(cx); - JSBool ok = RunScript(cx, script, NULL, frame.fp()->scopeChain()); + JSBool ok = RunScript(cx, script, frame.fp()); if (result) *result = frame.fp()->returnValue(); @@ -1153,8 +1162,9 @@ InvokeConstructor(JSContext *cx, const CallArgs &argsRef) /* Handle the fast-constructors cases before falling into the general case . */ Class *clasp = callee->getClass(); + JSFunction *fun = NULL; if (clasp == &js_FunctionClass) { - JSFunction *fun = callee->getFunctionPrivate(); + fun = callee->getFunctionPrivate(); if (fun->isConstructor()) { args.thisv().setMagicWithObjectOrNullPayload(NULL); return CallJSNativeConstructor(cx, fun->u.n.native, args.argc(), args.base()); @@ -1164,25 +1174,30 @@ InvokeConstructor(JSContext *cx, const CallArgs &argsRef) return CallJSNativeConstructor(cx, clasp->construct, args.argc(), args.base()); } - /* Construct 'this'. */ - JSObject *obj = js_NewInstance(cx, callee); - if (!obj) - return false; - args.thisv().setObject(*obj); + /* Scripts create their own |this| in JSOP_BEGIN */ + if (!fun || !fun->isInterpreted()) { + JSObject *obj = js_CreateThis(cx, callee); + if (!obj) + return false; + args.thisv().setObject(*obj); + } if (!Invoke(cx, args, JSINVOKE_CONSTRUCT)) return false; - /* Check the return value and if it's primitive, force it to be obj. */ if (args.rval().isPrimitive()) { - if (callee->getClass() != &js_FunctionClass) { + if (clasp != &js_FunctionClass) { /* native [[Construct]] returning primitive is error */ JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_NEW_RESULT, js_ValueToPrintableString(cx, args.rval())); return false; } - args.rval().setObject(*obj); + + /* The interpreter fixes rval for us. */ + JS_ASSERT(!fun->isInterpreted()); + + args.rval() = args.thisv(); } JS_RUNTIME_METER(cx->runtime, constructs); @@ -2290,7 +2305,7 @@ Interpret(JSContext *cx, JSStackFrame *entryFrame, uintN inlineCallCount, uintN do { \ JS_ASSERT_IF(leaveOnSafePoint, !TRACE_RECORDER(cx)); \ if (leaveOnSafePoint && !regs.fp->hasImacropc() && \ - script->nmap && script->nmap[regs.pc - script->code]) { \ + script->hasNativeCodeForPC(regs.fp->isConstructing(), regs.pc)) { \ JS_ASSERT(!TRACE_RECORDER(cx)); \ interpReturnOK = true; \ goto stop_recording; \ @@ -4477,6 +4492,41 @@ BEGIN_CASE(JSOP_ENUMELEM) } END_CASE(JSOP_ENUMELEM) +BEGIN_CASE(JSOP_BEGIN) +{ + if (regs.fp->isConstructing()) { + JSObject *obj2 = js_CreateThisForFunction(cx, ®s.fp->callee()); + if (!obj2) + goto error; + regs.fp->functionThis().setObject(*obj2); + } + + /* Call the debugger hook if present. */ + if (JSInterpreterHook hook = cx->debugHooks->callHook) { + regs.fp->setHookData(hook(cx, regs.fp, JS_TRUE, 0, + cx->debugHooks->callHookData)); + CHECK_INTERRUPT_HANDLER(); + } + + JS_RUNTIME_METER(rt, inlineCalls); + + Probes::enterJSFun(cx, regs.fp->fun()); + +#ifdef JS_METHODJIT + /* Try to ensure methods are method JIT'd. */ + mjit::CompileStatus status = mjit::CanMethodJIT(cx, script, regs.fp); + if (status == mjit::Compile_Error) + goto error; + if (!TRACE_RECORDER(cx) && status == mjit::Compile_Okay) { + if (!mjit::JaegerShot(cx)) + goto error; + interpReturnOK = true; + goto inline_return; + } +#endif +} +END_CASE(JSOP_BEGIN) + { JSFunction *newfun; JSObject *callee; @@ -4498,24 +4548,15 @@ BEGIN_CASE(JSOP_NEW) if (IsFunctionObject(vp[0], &callee)) { newfun = callee->getFunctionPrivate(); if (newfun->isInterpreted()) { - /* Root as we go using vp[1]. */ - if (!callee->getProperty(cx, - ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom), - &vp[1])) { - goto error; - } - JSObject *proto = vp[1].isObject() ? &vp[1].toObject() : NULL; - JSObject *obj2 = NewNonFunction(cx, &js_ObjectClass, proto, callee->getParent()); - if (!obj2) - goto error; - if (newfun->u.i.script->isEmpty()) { + JSObject *obj2 = js_CreateThisForFunction(cx, callee); + if (!obj2) + goto error; vp[0].setObject(*obj2); regs.sp = vp + 1; goto end_new; } - vp[1].setObject(*obj2); flags = JSFRAME_CONSTRUCTING; goto inline_call; } @@ -4584,38 +4625,13 @@ BEGIN_CASE(JSOP_APPLY) if (newfun->isHeavyweight() && !js_GetCallObject(cx, regs.fp)) goto error; - /* Call the debugger hook if present. */ - if (JSInterpreterHook hook = cx->debugHooks->callHook) { - regs.fp->setHookData(hook(cx, regs.fp, JS_TRUE, 0, - cx->debugHooks->callHookData)); - CHECK_INTERRUPT_HANDLER(); - } - inlineCallCount++; - JS_RUNTIME_METER(rt, inlineCalls); - - Probes::enterJSFun(cx, newfun); TRACE_0(EnterFrame); -#ifdef JS_METHODJIT - /* Try to ensure methods are method JIT'd. */ - { - JSObject *scope = ®s.fp->scopeChain(); - mjit::CompileStatus status = mjit::CanMethodJIT(cx, newscript, newfun, scope); - if (status == mjit::Compile_Error) - goto error; - if (!TRACE_RECORDER(cx) && status == mjit::Compile_Okay) { - if (!mjit::JaegerShot(cx)) - goto error; - interpReturnOK = true; - goto inline_return; - } - } -#endif - /* Load first op and dispatch it (safe since JSOP_STOP). */ op = (JSOp) *regs.pc; + JS_ASSERT(op == JSOP_BEGIN); DO_OP(); } diff --git a/js/src/jsinterp.h b/js/src/jsinterp.h index 04708a494bdd..9b09ddaa1f40 100644 --- a/js/src/jsinterp.h +++ b/js/src/jsinterp.h @@ -968,7 +968,7 @@ extern JS_REQUIRES_STACK JS_NEVER_INLINE bool Interpret(JSContext *cx, JSStackFrame *stopFp, uintN inlineCallCount = 0, uintN interpFlags = 0); extern JS_REQUIRES_STACK bool -RunScript(JSContext *cx, JSScript *script, JSFunction *fun, JSObject &scopeChain); +RunScript(JSContext *cx, JSScript *script, JSStackFrame *fp); #define JSPROP_INITIALIZER 0x100 /* NB: Not a valid property attribute. */ diff --git a/js/src/jsiter.cpp b/js/src/jsiter.cpp index 33e569b508b6..a5d18f38abcf 100644 --- a/js/src/jsiter.cpp +++ b/js/src/jsiter.cpp @@ -1283,7 +1283,7 @@ SendToGenerator(JSContext *cx, JSGeneratorOp op, JSObject *obj, JSObject *enumerators = cx->enumerators; cx->enumerators = gen->enumerators; - ok = RunScript(cx, stackfp->script(), stackfp->fun(), stackfp->scopeChain()); + ok = RunScript(cx, stackfp->script(), stackfp); gen->enumerators = cx->enumerators; cx->enumerators = enumerators; diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index 32a581e9d271..1ba744e371d1 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -2770,7 +2770,7 @@ js_Object(JSContext *cx, uintN argc, Value *vp) } JSObject* -js_NewInstance(JSContext *cx, JSObject *callee) +js_CreateThis(JSContext *cx, JSObject *callee) { Class *clasp = callee->getClass(); @@ -2790,6 +2790,25 @@ js_NewInstance(JSContext *cx, JSObject *callee) return NewObject(cx, newclasp, proto, parent); } +JSObject * +js_CreateThisForFunctionWithProto(JSContext *cx, JSObject *callee, JSObject *proto) +{ + return NewNonFunction(cx, &js_ObjectClass, proto, callee->getParent()); +} + +JSObject * +js_CreateThisForFunction(JSContext *cx, JSObject *callee) +{ + Value protov; + if (!callee->getProperty(cx, + ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom), + &protov)) { + return NULL; + } + JSObject *proto = protov.isObject() ? &protov.toObject() : NULL; + return js_CreateThisForFunctionWithProto(cx, callee, proto); +} + #ifdef JS_TRACER static JS_ALWAYS_INLINE JSObject* @@ -2840,7 +2859,7 @@ JS_DEFINE_CALLINFO_3(extern, OBJECT, js_String_tn, CONTEXT, CALLEE_PROTOTYPE, ST nanojit::ACCSET_STORE_ANY) JSObject* FASTCALL -js_NewInstanceFromTrace(JSContext *cx, Class *clasp, JSObject *ctor) +js_CreateThisFromTrace(JSContext *cx, Class *clasp, JSObject *ctor) { JS_ASSERT(JS_ON_TRACE(cx)); JS_ASSERT(ctor->isFunction()); @@ -2891,7 +2910,7 @@ js_NewInstanceFromTrace(JSContext *cx, Class *clasp, JSObject *ctor) return NewNonFunction(cx, clasp, proto, parent); } -JS_DEFINE_CALLINFO_3(extern, CONSTRUCTOR_RETRY, js_NewInstanceFromTrace, CONTEXT, CLASS, OBJECT, 0, +JS_DEFINE_CALLINFO_3(extern, CONSTRUCTOR_RETRY, js_CreateThisFromTrace, CONTEXT, CLASS, OBJECT, 0, nanojit::ACCSET_STORE_ANY) #else /* !JS_TRACER */ diff --git a/js/src/jsobj.h b/js/src/jsobj.h index 5b4b66b66cd2..c76af07c3ed3 100644 --- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -1407,8 +1407,18 @@ extern JSObject * js_ConstructObject(JSContext *cx, js::Class *clasp, JSObject *proto, JSObject *parent, uintN argc, js::Value *argv); +// Specialized call for constructing |this| with a known function callee, +// and a known prototype. extern JSObject * -js_NewInstance(JSContext *cx, JSObject *callee); +js_CreateThisForFunctionWithProto(JSContext *cx, JSObject *callee, JSObject *proto); + +// Specialized call for constructing |this| with a known function callee. +extern JSObject * +js_CreateThisForFunction(JSContext *cx, JSObject *callee); + +// Generic call for constructing |this|. +extern JSObject * +js_CreateThis(JSContext *cx, JSObject *callee); extern jsid js_CheckForStringIndex(jsid id); diff --git a/js/src/jsopcode.tbl b/js/src/jsopcode.tbl index 89c895c84fd5..5707ea478dc8 100644 --- a/js/src/jsopcode.tbl +++ b/js/src/jsopcode.tbl @@ -620,3 +620,6 @@ OPDEF(JSOP_FORGLOBAL, 246,"forglobal", NULL, 3, 1, 1, 19, JOF_GLOBAL */ OPDEF(JSOP_BLOCKCHAIN, 247,"blockchain", NULL, 3, 0, 0, 0, JOF_OBJECT) OPDEF(JSOP_NULLBLOCKCHAIN,248,"nullblockchain",NULL, 1, 0, 0, 0, JOF_BYTE) + +OPDEF(JSOP_BEGIN, 249,"begin", NULL, 1, 0, 0, 0, JOF_BYTE|JOF_TMPSLOT) + diff --git a/js/src/jsproxy.cpp b/js/src/jsproxy.cpp index 4857ba2ee508..937b07515e7b 100644 --- a/js/src/jsproxy.cpp +++ b/js/src/jsproxy.cpp @@ -1235,7 +1235,7 @@ callable_Call(JSContext *cx, uintN argc, Value *vp) static JSBool callable_Construct(JSContext *cx, uintN argc, Value *vp) { - JSObject *thisobj = js_NewInstance(cx, &JS_CALLEE(cx, vp).toObject()); + JSObject *thisobj = js_CreateThis(cx, &JS_CALLEE(cx, vp).toObject()); if (!thisobj) return false; diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index ef2649c5ebc7..db8af79d5ed3 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -118,7 +118,7 @@ js_XDRScript(JSXDRState *xdr, JSScript **scriptp, bool needMutableScript, uint32 length, lineno, nslots, magic; uint32 natoms, nsrcnotes, ntrynotes, nobjects, nupvars, nregexps, nconsts, i; uint32 prologLength, version, encodedClosedCount; - uint16 nClosedArgs, nClosedVars; + uint16 nClosedArgs = 0, nClosedVars = 0; JSPrincipals *principals; uint32 encodeable; JSBool filenameWasSaved; @@ -1641,16 +1641,6 @@ js_GetScriptLineExtent(JSScript *script) return 1 + lineno - script->lineno; } -#ifdef JS_METHODJIT -bool -JSScript::isValidJitCode(void *jcode) -{ - return (char*)jcode >= (char*)jit->invoke && - (char*)jcode < (char*)jit->invoke + - jit->inlineLength + jit->outOfLineLength; -} -#endif - void JSScript::copyClosedSlotsTo(JSScript *other) { diff --git a/js/src/jsscript.h b/js/src/jsscript.h index 88321350d88a..b95dc9e5f337 100644 --- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -169,21 +169,20 @@ struct GlobalSlotArray { namespace JSC { class ExecutablePool; } + +#define JS_UNJITTABLE_SCRIPT (reinterpret_cast(1)) + +enum JITScriptStatus { + JITScript_None, + JITScript_Invalid, + JITScript_Valid +}; + namespace js { namespace mjit { struct JITScript; -namespace ic { -# if defined JS_POLYIC - struct PICInfo; -# endif -# if defined JS_MONOIC - struct MICInfo; - struct CallICInfo; -# endif -} -struct CallSite; } } #endif @@ -285,20 +284,35 @@ struct JSScript { public: #ifdef JS_METHODJIT - // Note: the other pointers in this group may be non-NULL only if - // |execPool| is non-NULL. - void *ncode; /* native code compiled by the method JIT */ - void **nmap; /* maps PCs to native code */ - js::mjit::JITScript *jit; /* Extra JIT info */ -# if defined JS_POLYIC - js::mjit::ic::PICInfo *pics; /* PICs in this script */ -# endif -# if defined JS_MONOIC - js::mjit::ic::MICInfo *mics; /* MICs in this script. */ - js::mjit::ic::CallICInfo *callICs; /* CallICs in this script. */ -# endif + // Fast-cached pointers to make calls faster. These are also used to + // quickly test whether there is JIT code; a NULL value means no + // compilation has been attempted. A JS_UNJITTABLE_SCRIPT value means + // compilation failed. Any value is the arity-check entry point. + void *jitArityCheckNormal; + void *jitArityCheckCtor; - bool isValidJitCode(void *jcode); + js::mjit::JITScript *jitNormal; /* Extra JIT info for normal scripts */ + js::mjit::JITScript *jitCtor; /* Extra JIT info for constructors */ + + bool hasJITCode() { + return jitNormal || jitCtor; + } + + inline void **maybeNativeMap(bool constructing); + inline bool hasNativeCodeForPC(bool constructing, jsbytecode *pc); + + js::mjit::JITScript *getJIT(bool constructing) { + return constructing ? jitCtor : jitNormal; + } + + JITScriptStatus getJITStatus(bool constructing) { + void *addr = constructing ? jitArityCheckCtor : jitArityCheckNormal; + if (addr == NULL) + return JITScript_None; + if (addr == JS_UNJITTABLE_SCRIPT) + return JITScript_Invalid; + return JITScript_Valid; + } #endif /* Script notes are allocated right after the code. */ @@ -393,17 +407,6 @@ struct JSScript { return const_cast(&emptyScriptConst); } -#ifdef JS_METHODJIT - /* - * Map the given PC to the corresponding native code address. - */ - void *pcToNative(jsbytecode *pc) { - JS_ASSERT(nmap); - JS_ASSERT(nmap[pc - code]); - return nmap[pc - code]; - } -#endif - uint32 getClosedArg(uint32 index) { JS_ASSERT(index < nClosedArgs); return closedSlots[index]; diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index 81cdb7e15213..9cb913a775ab 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -11051,6 +11051,20 @@ TraceRecorder::emitNativePropertyOp(const Shape* shape, LIns* obj_ins, guard(true, lir->insEqI_0(status_ins), STATUS_EXIT); } +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_BEGIN() +{ + JSStackFrame* fp = cx->fp(); + if (fp->isConstructing()) { + LIns* callee_ins = get(&cx->fp()->calleeValue()); + LIns* args[] = { callee_ins, INS_CONSTPTR(&js_ObjectClass), cx_ins }; + LIns* tv_ins = lir->insCall(&js_CreateThisFromTrace_ci, args); + guard(false, lir->insEqP_0(tv_ins), OOM_EXIT); + set(&fp->thisValue(), tv_ins); + } + return ARECORD_CONTINUE; +} + JS_REQUIRES_STACK RecordingStatus TraceRecorder::emitNativeCall(JSSpecializedNative* sn, uintN argc, LIns* args[], bool rooted) { @@ -11360,7 +11374,7 @@ TraceRecorder::callNative(uintN argc, JSOp mode) clasp = &js_ObjectClass; JS_ASSERT(((jsuword) clasp & 3) == 0); - // Abort on |new Function|. js_NewInstance would allocate a regular- + // Abort on |new Function|. js_CreateThis would allocate a regular- // sized JSObject, not a Function-sized one. (The Function ctor would // deep-bail anyway but let's not go there.) if (clasp == &js_FunctionClass) @@ -11379,7 +11393,7 @@ TraceRecorder::callNative(uintN argc, JSOp mode) args[0] = INS_CONSTOBJ(funobj); args[1] = INS_CONSTPTR(clasp); args[2] = cx_ins; - newobj_ins = lir->insCall(&js_NewInstanceFromTrace_ci, args); + newobj_ins = lir->insCall(&js_CreateThisFromTrace_ci, args); guard(false, lir->insEqP_0(newobj_ins), OOM_EXIT); /* @@ -11504,15 +11518,8 @@ TraceRecorder::functionCall(uintN argc, JSOp mode) } #endif - if (FUN_INTERPRETED(fun)) { - if (mode == JSOP_NEW) { - LIns* args[] = { get(&fval), INS_CONSTPTR(&js_ObjectClass), cx_ins }; - LIns* tv_ins = lir->insCall(&js_NewInstanceFromTrace_ci, args); - guard(false, lir->insEqP_0(tv_ins), OOM_EXIT); - set(&tval, tv_ins); - } + if (FUN_INTERPRETED(fun)) return interpretedFunctionCall(fval, fun, argc, mode == JSOP_NEW); - } Native native = fun->maybeNative(); Value* argv = &tval + 1; @@ -13306,7 +13313,15 @@ TraceRecorder::interpretedFunctionCall(Value& fval, JSFunction* fun, uintN argc, * and does not call any TR::record_*CallComplete hook. */ if (fun->u.i.script->isEmpty()) { - LIns* rval_ins = constructing ? stack(-1 - argc) : INS_UNDEFINED(); + LIns* rval_ins; + if (constructing) { + LIns* args[] = { get(&fval), INS_CONSTPTR(&js_ObjectClass), cx_ins }; + LIns* tv_ins = lir->insCall(&js_CreateThisFromTrace_ci, args); + guard(false, lir->insEqP_0(tv_ins), OOM_EXIT); + rval_ins = tv_ins; + } else { + rval_ins = INS_UNDEFINED(); + } stack(-2 - argc, rval_ins); return RECORD_CONTINUE; } diff --git a/js/src/jstracer.h b/js/src/jstracer.h index 25e291e3b76a..cf5280b24d03 100644 --- a/js/src/jstracer.h +++ b/js/src/jstracer.h @@ -949,7 +949,7 @@ class TraceRecorder /* Carry the return value from a native call to the record_NativeCallComplete. */ nanojit::LIns* native_rval_ins; - /* Carry the return value of js_NewInstance to record_NativeCallComplete. */ + /* Carry the return value of js_CreateThis to record_NativeCallComplete. */ nanojit::LIns* newobj_ins; /* Carry the JSSpecializedNative used to generate a call to record_NativeCallComplete. */ diff --git a/js/src/methodjit/BaseAssembler.h b/js/src/methodjit/BaseAssembler.h index 110c53b57c83..5339d00eee1b 100644 --- a/js/src/methodjit/BaseAssembler.h +++ b/js/src/methodjit/BaseAssembler.h @@ -342,6 +342,11 @@ static const JSC::MacroAssembler::RegisterID JSParamReg_Argc = JSC::ARMRegiste } }; +/* Return f if the script is strict mode code, f otherwise. */ +#define STRICT_VARIANT(f) \ + (FunctionTemplateConditional(script->strictModeCode, \ + f, f)) + /* Save some typing. */ static const JSC::MacroAssembler::RegisterID JSFrameReg = BaseAssembler::JSFrameReg; static const JSC::MacroAssembler::RegisterID JSReturnReg_Type = BaseAssembler::JSReturnReg_Type; diff --git a/js/src/methodjit/Compiler.cpp b/js/src/methodjit/Compiler.cpp index 83f70832be29..53040f027dfb 100644 --- a/js/src/methodjit/Compiler.cpp +++ b/js/src/methodjit/Compiler.cpp @@ -74,12 +74,16 @@ static const char *OpcodeNames[] = { }; #endif -mjit::Compiler::Compiler(JSContext *cx, JSScript *script, JSFunction *fun, JSObject *scopeChain) +mjit::Compiler::Compiler(JSContext *cx, JSStackFrame *fp) : BaseCompiler(cx), - script(script), - scopeChain(scopeChain), + fp(fp), + script(fp->script()), + scopeChain(&fp->scopeChain()), globalObj(scopeChain->getGlobal()), - fun(fun), + fun(fp->isFunctionFrame() && !fp->isEvalFrame() + ? fp->fun() + : NULL), + isConstructing(fp->isConstructing()), analysis(cx, script), jumpMap(NULL), frame(cx, script, masm), branchPatches(ContextAllocPolicy(cx)), #if defined JS_MONOIC @@ -99,6 +103,34 @@ mjit::Compiler::Compiler(JSContext *cx, JSScript *script, JSFunction *fun, JSObj { } +CompileStatus +mjit::Compiler::compile() +{ + JS_ASSERT(!script->isEmpty()); + JS_ASSERT_IF(isConstructing, !script->jitCtor); + JS_ASSERT_IF(!isConstructing, !script->jitNormal); + + JITScript **jit = isConstructing ? &script->jitCtor : &script->jitNormal; + void **checkAddr = isConstructing + ? &script->jitArityCheckCtor + : &script->jitArityCheckNormal; + + CompileStatus status = performCompilation(jit); + if (status == Compile_Okay) { + // Global scripts don't have an arity check entry. That's okay, we + // just need a pointer so the VM can quickly decide whether this + // method can be JIT'd or not. Global scripts cannot be IC'd, since + // they have no functions, so there is no danger. + *checkAddr = (*jit)->arityCheckEntry + ? (*jit)->arityCheckEntry + : (*jit)->invokeEntry; + } else { + *checkAddr = JS_UNJITTABLE_SCRIPT; + } + + return status; +} + #define CHECK_STATUS(expr) \ JS_BEGIN_MACRO \ CompileStatus status_ = (expr); \ @@ -107,10 +139,8 @@ mjit::Compiler::Compiler(JSContext *cx, JSScript *script, JSFunction *fun, JSObj JS_END_MACRO CompileStatus -mjit::Compiler::Compile() +mjit::Compiler::performCompilation(JITScript **jitp) { - JS_ASSERT(!script->ncode); - JaegerSpew(JSpew_Scripts, "compiling script (file \"%s\") (line \"%d\") (length \"%d\")\n", script->filename, script->lineno, script->length); @@ -152,7 +182,7 @@ mjit::Compiler::Compile() CHECK_STATUS(generatePrologue()); CHECK_STATUS(generateMethod()); CHECK_STATUS(generateEpilogue()); - CHECK_STATUS(finishThisUp()); + CHECK_STATUS(finishThisUp(jitp)); #ifdef JS_METHODJIT_SPEW prof.stop(); @@ -160,7 +190,7 @@ mjit::Compiler::Compile() #endif JaegerSpew(JSpew_Scripts, "successfully compiled (code \"%p\") (size \"%ld\")\n", - (void*)script->ncode, masm.size() + stubcc.size()); + (*jitp)->code.m_code.executableAddress(), (*jitp)->code.m_size); return Compile_Okay; } @@ -173,18 +203,13 @@ mjit::Compiler::~Compiler() } CompileStatus JS_NEVER_INLINE -mjit::TryCompile(JSContext *cx, JSScript *script, JSFunction *fun, JSObject *scopeChain) +mjit::TryCompile(JSContext *cx, JSStackFrame *fp) { - Compiler cc(cx, script, fun, scopeChain); + JS_ASSERT(cx->fp() == fp); - JS_ASSERT(!script->ncode); - JS_ASSERT(!script->isEmpty()); + Compiler cc(cx, fp); - CompileStatus status = cc.Compile(); - if (status != Compile_Okay) - script->ncode = JS_UNJITTABLE_METHOD; - - return status; + return cc.compile(); } CompileStatus @@ -290,7 +315,7 @@ mjit::Compiler::generateEpilogue() } CompileStatus -mjit::Compiler::finishThisUp() +mjit::Compiler::finishThisUp(JITScript **jitp) { for (size_t i = 0; i < branchPatches.length(); i++) { Label label = labelOf(branchPatches[i].pc); @@ -336,18 +361,16 @@ mjit::Compiler::finishThisUp() return Compile_Error; } - script->jit = (JITScript *)cursor; + JITScript *jit = (JITScript *)cursor; cursor += sizeof(JITScript); - script->jit->execPool = execPool; - script->jit->inlineLength = masm.size(); - script->jit->outOfLineLength = stubcc.size(); - script->jit->nCallSites = callSites.length(); - script->jit->invoke = result; + jit->code = JSC::MacroAssemblerCodeRef(result, execPool, masm.size() + stubcc.size()); + jit->nCallSites = callSites.length(); + jit->invokeEntry = result; /* Build the pc -> ncode mapping. */ void **nmap = (void **)cursor; - script->nmap = nmap; + jit->nmap = nmap; cursor += sizeof(void *) * script->length; for (size_t i = 0; i < script->length; i++) { @@ -358,107 +381,116 @@ mjit::Compiler::finishThisUp() } } - if (fun) - script->jit->arityCheck = stubCode.locationOf(arityLabel).executableAddress(); - -#if defined JS_MONOIC - script->jit->nMICs = mics.length(); - if (mics.length()) { - script->mics = (ic::MICInfo *)cursor; - cursor += sizeof(ic::MICInfo) * mics.length(); - } else { - script->mics = NULL; + if (fun) { + jit->arityCheckEntry = stubCode.locationOf(arityLabel).executableAddress(); + jit->fastEntry = fullCode.locationOf(invokeLabel).executableAddress(); } - for (size_t i = 0; i < mics.length(); i++) { - script->mics[i].kind = mics[i].kind; - script->mics[i].entry = fullCode.locationOf(mics[i].entry); - switch (mics[i].kind) { - case ic::MICInfo::GET: - case ic::MICInfo::SET: - script->mics[i].load = fullCode.locationOf(mics[i].load); - script->mics[i].shape = fullCode.locationOf(mics[i].shape); - script->mics[i].stubCall = stubCode.locationOf(mics[i].call); - script->mics[i].stubEntry = stubCode.locationOf(mics[i].stubEntry); - script->mics[i].u.name.typeConst = mics[i].u.name.typeConst; - script->mics[i].u.name.dataConst = mics[i].u.name.dataConst; +#if defined JS_MONOIC + jit->nMICs = mics.length(); + if (mics.length()) { + jit->mics = (ic::MICInfo *)cursor; + cursor += sizeof(ic::MICInfo) * mics.length(); + } else { + jit->mics = NULL; + } + + if (ic::MICInfo *scriptMICs = jit->mics) { + for (size_t i = 0; i < mics.length(); i++) { + scriptMICs[i].kind = mics[i].kind; + scriptMICs[i].entry = fullCode.locationOf(mics[i].entry); + switch (mics[i].kind) { + case ic::MICInfo::GET: + case ic::MICInfo::SET: + scriptMICs[i].load = fullCode.locationOf(mics[i].load); + scriptMICs[i].shape = fullCode.locationOf(mics[i].shape); + scriptMICs[i].stubCall = stubCode.locationOf(mics[i].call); + scriptMICs[i].stubEntry = stubCode.locationOf(mics[i].stubEntry); + scriptMICs[i].u.name.typeConst = mics[i].u.name.typeConst; + scriptMICs[i].u.name.dataConst = mics[i].u.name.dataConst; #if defined JS_PUNBOX64 - script->mics[i].patchValueOffset = mics[i].patchValueOffset; + scriptMICs[i].patchValueOffset = mics[i].patchValueOffset; #endif - break; - case ic::MICInfo::TRACER: { - uint32 offs = uint32(mics[i].jumpTarget - script->code); - JS_ASSERT(jumpMap[offs].isValid()); - script->mics[i].traceHint = fullCode.locationOf(mics[i].traceHint); - script->mics[i].load = fullCode.locationOf(jumpMap[offs]); - script->mics[i].u.hints.hasSlowTraceHintOne = mics[i].slowTraceHintOne.isSet(); - if (mics[i].slowTraceHintOne.isSet()) - script->mics[i].slowTraceHintOne = stubCode.locationOf(mics[i].slowTraceHintOne.get()); - script->mics[i].u.hints.hasSlowTraceHintTwo = mics[i].slowTraceHintTwo.isSet(); - if (mics[i].slowTraceHintTwo.isSet()) - script->mics[i].slowTraceHintTwo = stubCode.locationOf(mics[i].slowTraceHintTwo.get()); - break; - } - default: - JS_NOT_REACHED("Bad MIC kind"); + break; + case ic::MICInfo::TRACER: { + uint32 offs = uint32(mics[i].jumpTarget - script->code); + 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()); + break; + } + default: + JS_NOT_REACHED("Bad MIC kind"); + } + stubCode.patch(mics[i].addrLabel, &scriptMICs[i]); } } - script->jit->nCallICs = callICs.length(); + jit->nCallICs = callICs.length(); if (callICs.length()) { - script->callICs = (ic::CallICInfo *)cursor; + jit->callICs = (ic::CallICInfo *)cursor; cursor += sizeof(ic::CallICInfo) * callICs.length(); } else { - script->callICs = NULL; + jit->callICs = NULL; } - for (size_t i = 0; i < callICs.length(); i++) { - script->callICs[i].reset(); - script->callICs[i].funGuard = fullCode.locationOf(callICs[i].funGuard); - script->callICs[i].funJump = fullCode.locationOf(callICs[i].funJump); - script->callICs[i].slowPathStart = stubCode.locationOf(callICs[i].slowPathStart); + if (ic::CallICInfo *cics = jit->callICs) { + for (size_t i = 0; i < callICs.length(); i++) { + cics[i].reset(); + cics[i].funGuard = fullCode.locationOf(callICs[i].funGuard); + cics[i].funJump = fullCode.locationOf(callICs[i].funJump); + cics[i].slowPathStart = stubCode.locationOf(callICs[i].slowPathStart); - /* Compute the hot call offset. */ - uint32 offset = fullCode.locationOf(callICs[i].hotJump) - - fullCode.locationOf(callICs[i].funGuard); - script->callICs[i].hotJumpOffset = offset; - JS_ASSERT(script->callICs[i].hotJumpOffset == offset); + /* Compute the hot call offset. */ + uint32 offset = fullCode.locationOf(callICs[i].hotJump) - + fullCode.locationOf(callICs[i].funGuard); + cics[i].hotJumpOffset = offset; + JS_ASSERT(cics[i].hotJumpOffset == offset); - /* Compute the join point offset. */ - offset = fullCode.locationOf(callICs[i].joinPoint) - - fullCode.locationOf(callICs[i].funGuard); - script->callICs[i].joinPointOffset = offset; - JS_ASSERT(script->callICs[i].joinPointOffset == offset); - - /* Compute the OOL call offset. */ - offset = stubCode.locationOf(callICs[i].oolCall) - - stubCode.locationOf(callICs[i].slowPathStart); - script->callICs[i].oolCallOffset = offset; - JS_ASSERT(script->callICs[i].oolCallOffset == offset); + /* Compute the join point offset. */ + offset = fullCode.locationOf(callICs[i].joinPoint) - + fullCode.locationOf(callICs[i].funGuard); + cics[i].joinPointOffset = offset; + JS_ASSERT(cics[i].joinPointOffset == offset); + + /* Compute the OOL call offset. */ + offset = stubCode.locationOf(callICs[i].oolCall) - + stubCode.locationOf(callICs[i].slowPathStart); + cics[i].oolCallOffset = offset; + JS_ASSERT(cics[i].oolCallOffset == offset); - /* Compute the OOL jump offset. */ - offset = stubCode.locationOf(callICs[i].oolJump) - - stubCode.locationOf(callICs[i].slowPathStart); - script->callICs[i].oolJumpOffset = offset; - JS_ASSERT(script->callICs[i].oolJumpOffset == offset); + /* Compute the OOL jump offset. */ + offset = stubCode.locationOf(callICs[i].oolJump) - + stubCode.locationOf(callICs[i].slowPathStart); + cics[i].oolJumpOffset = offset; + JS_ASSERT(cics[i].oolJumpOffset == offset); - /* Compute the slow join point offset. */ - offset = stubCode.locationOf(callICs[i].slowJoinPoint) - - stubCode.locationOf(callICs[i].slowPathStart); - script->callICs[i].slowJoinOffset = offset; - JS_ASSERT(script->callICs[i].slowJoinOffset == offset); + /* Compute the slow join point offset. */ + offset = stubCode.locationOf(callICs[i].slowJoinPoint) - + stubCode.locationOf(callICs[i].slowPathStart); + cics[i].slowJoinOffset = offset; + JS_ASSERT(cics[i].slowJoinOffset == offset); - /* Compute the join point offset for continuing on the hot path. */ - offset = stubCode.locationOf(callICs[i].hotPathLabel) - - stubCode.locationOf(callICs[i].funGuard); - script->callICs[i].hotPathOffset = offset; - JS_ASSERT(script->callICs[i].hotPathOffset == offset); + /* Compute the join point offset for continuing on the hot path. */ + offset = stubCode.locationOf(callICs[i].hotPathLabel) - + stubCode.locationOf(callICs[i].funGuard); + cics[i].hotPathOffset = offset; + JS_ASSERT(cics[i].hotPathOffset == offset); - script->callICs[i].pc = callICs[i].pc; - script->callICs[i].argc = callICs[i].argc; - script->callICs[i].funObjReg = callICs[i].funObjReg; - script->callICs[i].funPtrReg = callICs[i].funPtrReg; - script->callICs[i].frameDepth = callICs[i].frameDepth; + cics[i].pc = callICs[i].pc; + cics[i].argc = callICs[i].argc; + cics[i].funObjReg = callICs[i].funObjReg; + cics[i].funPtrReg = callICs[i].funPtrReg; + cics[i].frameDepth = callICs[i].frameDepth; + stubCode.patch(callICs[i].addrLabel1, &cics[i]); + stubCode.patch(callICs[i].addrLabel2, &cics[i]); + } } #endif /* JS_MONOIC */ @@ -471,44 +503,47 @@ mjit::Compiler::finishThisUp() } #if defined JS_POLYIC - script->jit->nPICs = pics.length(); + jit->nPICs = pics.length(); if (pics.length()) { - script->pics = (ic::PICInfo *)cursor; + jit->pics = (ic::PICInfo *)cursor; cursor += sizeof(ic::PICInfo) * pics.length(); } else { - script->pics = NULL; + jit->pics = NULL; } - for (size_t i = 0; i < pics.length(); i++) { - pics[i].copySimpleMembersTo(script->pics[i]); - script->pics[i].fastPathStart = fullCode.locationOf(pics[i].fastPathStart); - script->pics[i].storeBack = fullCode.locationOf(pics[i].storeBack); - script->pics[i].slowPathStart = stubCode.locationOf(pics[i].slowPathStart); - script->pics[i].callReturn = uint16((uint8*)stubCode.locationOf(pics[i].callReturn).executableAddress() - - (uint8*)script->pics[i].slowPathStart.executableAddress()); - script->pics[i].shapeGuard = masm.distanceOf(pics[i].shapeGuard) - - masm.distanceOf(pics[i].fastPathStart); - JS_ASSERT(script->pics[i].shapeGuard == masm.distanceOf(pics[i].shapeGuard) - - masm.distanceOf(pics[i].fastPathStart)); - script->pics[i].shapeRegHasBaseShape = true; + if (ic::PICInfo *scriptPICs = jit->pics) { + for (size_t i = 0; i < pics.length(); i++) { + pics[i].copySimpleMembersTo(scriptPICs[i]); + scriptPICs[i].fastPathStart = fullCode.locationOf(pics[i].fastPathStart); + scriptPICs[i].storeBack = fullCode.locationOf(pics[i].storeBack); + scriptPICs[i].slowPathStart = stubCode.locationOf(pics[i].slowPathStart); + scriptPICs[i].callReturn = uint16((uint8*)stubCode.locationOf(pics[i].callReturn).executableAddress() - + (uint8*)scriptPICs[i].slowPathStart.executableAddress()); + scriptPICs[i].shapeGuard = masm.distanceOf(pics[i].shapeGuard) - + masm.distanceOf(pics[i].fastPathStart); + JS_ASSERT(scriptPICs[i].shapeGuard == masm.distanceOf(pics[i].shapeGuard) - + masm.distanceOf(pics[i].fastPathStart)); + scriptPICs[i].shapeRegHasBaseShape = true; # if defined JS_CPU_X64 - memcpy(&script->pics[i].labels, &pics[i].labels, sizeof(PICLabels)); + memcpy(&scriptPICs[i].labels, &pics[i].labels, sizeof(PICLabels)); # endif - if (pics[i].kind == ic::PICInfo::SET || - pics[i].kind == ic::PICInfo::SETMETHOD) { - script->pics[i].u.vr = pics[i].vr; - } else if (pics[i].kind != ic::PICInfo::NAME) { - if (pics[i].hasTypeCheck) { - int32 distance = stubcc.masm.distanceOf(pics[i].typeCheck) - - stubcc.masm.distanceOf(pics[i].slowPathStart); - JS_ASSERT(distance <= 0); - script->pics[i].u.get.typeCheckOffset = distance; + if (pics[i].kind == ic::PICInfo::SET || + pics[i].kind == ic::PICInfo::SETMETHOD) { + scriptPICs[i].u.vr = pics[i].vr; + } else if (pics[i].kind != ic::PICInfo::NAME) { + if (pics[i].hasTypeCheck) { + int32 distance = stubcc.masm.distanceOf(pics[i].typeCheck) - + stubcc.masm.distanceOf(pics[i].slowPathStart); + JS_ASSERT(distance <= 0); + scriptPICs[i].u.get.typeCheckOffset = distance; + } } + new (&scriptPICs[i].execPools) ic::PICInfo::ExecPoolVector(SystemAllocPolicy()); + scriptPICs[i].reset(); + stubCode.patch(pics[i].addrLabel, &scriptPICs[i]); } - new (&script->pics[i].execPools) ic::PICInfo::ExecPoolVector(SystemAllocPolicy()); - script->pics[i].reset(); } #endif /* JS_POLYIC */ @@ -534,10 +569,8 @@ mjit::Compiler::finishThisUp() JSC::ExecutableAllocator::makeExecutable(result, masm.size() + stubcc.size()); JSC::ExecutableAllocator::cacheFlush(result, masm.size() + stubcc.size()); - script->ncode = (uint8 *)(result + masm.distanceOf(invokeLabel)); - /* Build the table of call sites. */ - script->jit->nCallSites = callSites.length(); + jit->nCallSites = callSites.length(); if (callSites.length()) { CallSite *callSiteList = (CallSite *)cursor; cursor += sizeof(CallSite) * callSites.length(); @@ -550,12 +583,14 @@ mjit::Compiler::finishThisUp() callSiteList[i].pcOffset = callSites[i].pc - script->code; callSiteList[i].id = callSites[i].id; } - script->jit->callSites = callSiteList; + jit->callSites = callSiteList; } else { - script->jit->callSites = NULL; + jit->callSites = NULL; } - JS_ASSERT(size_t(cursor - (uint8*)script->jit) == totalBytes); + JS_ASSERT(size_t(cursor - (uint8*)jit) == totalBytes); + + *jitp = jit; return Compile_Okay; } @@ -1662,6 +1697,11 @@ mjit::Compiler::generateMethod() break; END_CASE(JSOP_GLOBALINC) + BEGIN_CASE(JSOP_BEGIN) + if (isConstructing) + constructThis(); + END_CASE(JSOP_BEGIN) + default: /* Sorry, this opcode isn't implemented yet. */ #ifdef JS_METHODJIT_SPEW @@ -1717,15 +1757,17 @@ mjit::Compiler::findCallSite(const CallSite &callSite) { JS_ASSERT(callSite.pcOffset < script->length); + JITScript *jit = script->getJIT(fp->isConstructing()); + uint8* ilPath = (uint8 *)jit->code.m_code.executableAddress(); + uint8* oolPath = ilPath + masm.size(); + for (uint32 i = 0; i < callSites.length(); i++) { if (callSites[i].pc == script->code + callSite.pcOffset && callSites[i].id == callSite.id) { if (callSites[i].stub) { - return (uint8*)script->jit->invoke + masm.size() + - stubcc.masm.distanceOf(callSites[i].location); + return oolPath + stubcc.masm.distanceOf(callSites[i].location); } - return (uint8*)script->jit->invoke + - stubcc.masm.distanceOf(callSites[i].location); + return ilPath + masm.distanceOf(callSites[i].location); } } @@ -1781,24 +1823,100 @@ mjit::Compiler::emitFinalReturn(Assembler &masm) masm.jump(Registers::ReturnReg); } +// Emits code to load a return value of the frame into the scripted-ABI +// type & data register pair. If the return value is in fp->rval, then |fe| +// is NULL. Otherwise, |fe| contains the return value. +// +// If reading from fp->rval, |undefined| is loaded optimistically, before +// checking if fp->rval is set in the frame flags and loading that instead. +// +// Otherwise, if |masm| is the inline path, it is loaded as efficiently as +// the FrameState can manage. If |masm| is the OOL path, the value is simply +// loaded from its slot in the frame, since the caller has guaranteed it's +// been synced. +// void -mjit::Compiler::loadReturnValue(Assembler &masm) +mjit::Compiler::loadReturnValue(Assembler *masm, FrameEntry *fe) { - /* - * Load a return value from POPV or SETRVAL into the return registers, - * otherwise return undefined. - */ - masm.loadValueAsComponents(UndefinedValue(), JSReturnReg_Type, JSReturnReg_Data); - if (analysis.usesReturnValue()) { - Jump rvalClear = masm.branchTest32(Assembler::Zero, - FrameFlagsAddress(), Imm32(JSFRAME_HAS_RVAL)); - Address rvalAddress(JSFrameReg, JSStackFrame::offsetOfReturnValue()); - masm.loadValueAsComponents(rvalAddress, - JSReturnReg_Type, JSReturnReg_Data); - rvalClear.linkTo(masm.label(), &masm); + RegisterID typeReg = JSReturnReg_Type; + RegisterID dataReg = JSReturnReg_Data; + + if (fe) { + // If using the OOL assembler, the caller signifies that the |fe| is + // synced, but not to rely on its register state. + if (masm != &this->masm) { + if (fe->isConstant()) { + stubcc.masm.loadValueAsComponents(fe->getValue(), typeReg, dataReg); + } else { + Address rval(frame.addressOf(fe)); + if (fe->isTypeKnown()) { + stubcc.masm.loadPayload(rval, dataReg); + stubcc.masm.move(ImmType(fe->getKnownType()), typeReg); + } else { + stubcc.masm.loadValueAsComponents(rval, typeReg, dataReg); + } + } + } else { + frame.loadTo(fe, typeReg, dataReg, Registers::ReturnReg); + } + } else { + // Load a return value from POPV or SETRVAL into the return registers, + // otherwise return undefined. + masm->loadValueAsComponents(UndefinedValue(), typeReg, dataReg); + if (analysis.usesReturnValue()) { + Jump rvalClear = masm->branchTest32(Assembler::Zero, + FrameFlagsAddress(), + Imm32(JSFRAME_HAS_RVAL)); + Address rvalAddress(JSFrameReg, JSStackFrame::offsetOfReturnValue()); + masm->loadValueAsComponents(rvalAddress, typeReg, dataReg); + rvalClear.linkTo(masm->label(), masm); + } } } +// This ensures that constructor return values are an object. If a non-object +// is returned, either explicitly or implicitly, the newly created object is +// loaded out of the frame. Otherwise, the explicitly returned object is kept. +// +void +mjit::Compiler::fixPrimitiveReturn(Assembler *masm, FrameEntry *fe) +{ + JS_ASSERT(isConstructing); + + Address thisv(JSFrameReg, JSStackFrame::offsetOfThis(fun)); + + // Easy cases - no return value, or known primitive, so just return thisv. + if (!fe || (fe->isTypeKnown() && fe->getKnownType() != JSVAL_TYPE_OBJECT)) { + masm->loadValueAsComponents(thisv, JSReturnReg_Type, JSReturnReg_Data); + return; + } + + // If the type is known to be an object, just load the return value as normal. + if (fe->isTypeKnown() && fe->getKnownType() == JSVAL_TYPE_OBJECT) { + loadReturnValue(masm, fe); + return; + } + + // There's a return value, and its type is unknown. Test the type and load + // |thisv| if necessary. + loadReturnValue(masm, fe); + Jump j = masm->testObject(Assembler::Equal, JSReturnReg_Type); + masm->loadValueAsComponents(thisv, JSReturnReg_Type, JSReturnReg_Data); + j.linkTo(masm->label(), masm); +} + +// Loads the return value into the scripted ABI register pair, such that JS +// semantics in constructors are preserved. +// +void +mjit::Compiler::emitReturnValue(Assembler *masm, FrameEntry *fe) +{ + if (isConstructing) + fixPrimitiveReturn(masm, fe); + else + loadReturnValue(masm, fe); +} + void mjit::Compiler::emitReturn(FrameEntry *fe) { @@ -1823,8 +1941,7 @@ mjit::Compiler::emitReturn(FrameEntry *fe) stubCall(stubs::PutActivationObjects); if (fe) { - masm.loadValueAsComponents(frame.addressOf(fe), - JSReturnReg_Type, JSReturnReg_Data); + emitReturnValue(&masm, fe); emitFinalReturn(masm); frame.discardFrame(); return; @@ -1839,22 +1956,12 @@ mjit::Compiler::emitReturn(FrameEntry *fe) stubcc.leave(); stubcc.call(stubs::PutActivationObjects); - if (fe) { - stubcc.masm.loadValueAsComponents(frame.addressOf(fe), - JSReturnReg_Type, JSReturnReg_Data); - } else { - loadReturnValue(stubcc.masm); - } - + emitReturnValue(&stubcc.masm, fe); emitFinalReturn(stubcc.masm); } } - if (fe) - frame.storeTo(fe, JSReturnReg_Data, JSReturnReg_Type, Registers::ReturnReg); - else - loadReturnValue(masm); - + emitReturnValue(&masm, fe); emitFinalReturn(masm); frame.discardFrame(); } @@ -1929,18 +2036,6 @@ mjit::Compiler::interruptCheckHelper() #endif } -void -mjit::Compiler::emitPrimitiveTestForNew(uint32 argc) -{ - Jump primitive = masm.testPrimitive(Assembler::Equal, JSReturnReg_Type); - stubcc.linkExitDirect(primitive, stubcc.masm.label()); - FrameEntry *fe = frame.peek(-int(argc + 1)); - Address thisv(frame.addressOf(fe)); - stubcc.masm.loadValueAsComponents(thisv, JSReturnReg_Type, JSReturnReg_Data); - Jump primFix = stubcc.masm.jump(); - stubcc.crossJump(primFix, masm.label()); -} - void mjit::Compiler::emitUncachedCall(uint32 argc, bool callingNew) { @@ -1973,9 +2068,6 @@ mjit::Compiler::emitUncachedCall(uint32 argc, bool callingNew) callPatch.joinPoint = masm.label(); masm.loadPtr(Address(JSFrameReg, JSStackFrame::offsetOfPrev()), JSFrameReg); - if (callingNew) - emitPrimitiveTestForNew(argc); - frame.popn(argc + 2); frame.takeReg(JSReturnReg_Type); frame.takeReg(JSReturnReg_Data); @@ -1993,6 +2085,10 @@ mjit::Compiler::inlineCallHelper(uint32 argc, bool callingNew) /* Check for interrupts on function call */ interruptCheckHelper(); + // |thisv| does not need to be synced for constructing. + if (callingNew) + frame.discardFe(frame.peek(-int(argc + 1))); + FrameEntry *fe = frame.peek(-int(argc + 2)); /* Currently, we don't support constant functions. */ @@ -2002,12 +2098,7 @@ mjit::Compiler::inlineCallHelper(uint32 argc, bool callingNew) } #ifdef JS_MONOIC - FrameEntry *thisvFe = frame.peek(-int(argc + 1)); - Address thisvAddr = frame.addressOf(thisvFe); - CallGenInfo callIC(argc); - uint32 callICIndex = callICs.length(); - CallPatchInfo callPatch; /* @@ -2044,16 +2135,6 @@ mjit::Compiler::inlineCallHelper(uint32 argc, bool callingNew) if (typeReg.isSet()) notObjectJump = masm.testObject(Assembler::NotEqual, typeReg.reg()); - /* - * Ensure that dataReg stays in a register which won't be clobbered - * by the intervening call to NewObject. - */ - if (callingNew && !(Registers::maskReg(dataReg) & Registers::SavedRegs)) { - RegisterID reg = Registers(Registers::SavedRegs).takeAnyReg(); - masm.move(dataReg, reg); - dataReg = reg; - } - tempRegs.takeReg(dataReg); RegisterID t0 = tempRegs.takeAnyReg(); RegisterID t1 = tempRegs.takeAnyReg(); @@ -2083,28 +2164,6 @@ mjit::Compiler::inlineCallHelper(uint32 argc, bool callingNew) stubcc.masm.and32(Imm32(JSFUN_KINDMASK), t1); Jump isNative = stubcc.masm.branch32(Assembler::Below, t1, Imm32(JSFUN_INTERPRETED)); - /* Create the new object. This requires some fiddling to save the two values. */ - if (callingNew) { - void *pfun = stubcc.masm.getCallTarget(JS_FUNC_TO_DATA_PTR(void *, stubs::NewObject)); - stubcc.masm.storePtr(ImmPtr(PC), - FrameAddress(offsetof(VMFrame, regs) + offsetof(JSFrameRegs, pc))); - stubcc.masm.fixScriptStack(frame.frameDepth()); - stubcc.masm.setupVMFrame(); -#if defined(JS_CPU_X86) - /* Need to stay 16-byte aligned on x86. */ - stubcc.masm.subPtr(Imm32(8), JSC::MacroAssembler::stackPointerRegister); -#endif - stubcc.masm.push(dataReg); - stubcc.masm.push(t0); - stubcc.masm.move(Imm32(argc), Registers::ArgReg1); - stubcc.masm.wrapCall(pfun); - stubcc.masm.pop(t0); - stubcc.masm.pop(dataReg); -#if defined(JS_CPU_X86) - stubcc.masm.addPtr(Imm32(8), JSC::MacroAssembler::stackPointerRegister); -#endif - } - /* * No-op jump that gets re-patched. This is so ArgReg1 won't be * clobbered, with the added bonus that the generated stub doesn't @@ -2115,7 +2174,7 @@ mjit::Compiler::inlineCallHelper(uint32 argc, bool callingNew) callIC.oolJump = toPatch; /* At this point the function is definitely scripted. Call the link routine. */ - stubcc.masm.move(Imm32(callICIndex), Registers::ArgReg1); + callIC.addrLabel1 = stubcc.masm.moveWithPatch(ImmPtr(NULL), Registers::ArgReg1); callIC.oolCall = stubcc.call(callingNew ? ic::New : ic::Call); callIC.funObjReg = dataReg; @@ -2145,7 +2204,7 @@ mjit::Compiler::inlineCallHelper(uint32 argc, bool callingNew) notFunction.linkTo(stubcc.masm.label(), &stubcc.masm); isNative.linkTo(stubcc.masm.label(), &stubcc.masm); - stubcc.masm.move(Imm32(callICIndex), Registers::ArgReg1); + callIC.addrLabel2 = stubcc.masm.moveWithPatch(ImmPtr(NULL), Registers::ArgReg1); stubcc.call(callingNew ? ic::NativeNew : ic::NativeCall); rejoin2 = stubcc.masm.jump(); @@ -2157,13 +2216,6 @@ mjit::Compiler::inlineCallHelper(uint32 argc, bool callingNew) */ callIC.hotPathLabel = masm.label(); - /* If calling |new|, make sure to allocate a new object. */ - if (callingNew) { - prepareStubCall(Uses(argc + 2)); - masm.move(Imm32(argc), Registers::ArgReg1); - stubCall(stubs::NewObject); - } - uint32 flags = 0; if (callingNew) flags |= JSFRAME_CONSTRUCTING; @@ -2175,13 +2227,6 @@ mjit::Compiler::inlineCallHelper(uint32 argc, bool callingNew) callIC.joinPoint = callPatch.joinPoint = masm.label(); masm.loadPtr(Address(JSFrameReg, JSStackFrame::offsetOfPrev()), JSFrameReg); - /* - * Functions invoked with |new| can return primitive values. - * Just deal with this here. - */ - if (callingNew) - emitPrimitiveTestForNew(argc); - frame.popn(argc + 2); frame.takeReg(JSReturnReg_Type); frame.takeReg(JSReturnReg_Data); @@ -2375,6 +2420,19 @@ mjit::Compiler::jsop_length() } #if defined JS_POLYIC + +void +mjit::Compiler::passPICAddress(PICGenInfo &pic) +{ + pic.addrLabel = stubcc.masm.moveWithPatch(ImmPtr(NULL), Registers::ArgReg1); +} + +void +mjit::Compiler::passMICAddress(MICGenInfo &mic) +{ + mic.addrLabel = stubcc.masm.moveWithPatch(ImmPtr(NULL), Registers::ArgReg1); +} + void mjit::Compiler::jsop_getprop(JSAtom *atom, bool doTypeCheck) { @@ -2445,7 +2503,7 @@ mjit::Compiler::jsop_getprop(JSAtom *atom, bool doTypeCheck) pic.slowPathStart = stubcc.linkExit(j, Uses(1)); stubcc.leave(); - stubcc.masm.move(Imm32(pics.length()), Registers::ArgReg1); + passPICAddress(pic); pic.callReturn = stubcc.call(ic::GetProp); /* Load dslots. */ @@ -2546,7 +2604,7 @@ mjit::Compiler::jsop_getelem_pic(FrameEntry *obj, FrameEntry *id, RegisterID obj pic.slowPathStart = stubcc.linkExit(jmpShapeGuard, Uses(2)); stubcc.leave(); - stubcc.masm.move(Imm32(pics.length()), Registers::ArgReg1); + passPICAddress(pic); pic.callReturn = stubcc.call(ic::GetElem); /* Load dslots. */ @@ -2672,7 +2730,7 @@ mjit::Compiler::jsop_callprop_generic(JSAtom *atom) /* Slow path. */ stubcc.leave(); - stubcc.masm.move(Imm32(pics.length()), Registers::ArgReg1); + passPICAddress(pic); pic.callReturn = stubcc.call(ic::CallProp); /* Adjust the frame. None of this will generate code. */ @@ -2836,7 +2894,7 @@ mjit::Compiler::jsop_callprop_obj(JSAtom *atom) pic.slowPathStart = stubcc.linkExit(j, Uses(1)); stubcc.leave(); - stubcc.masm.move(Imm32(pics.length()), Registers::ArgReg1); + passPICAddress(pic); pic.callReturn = stubcc.call(ic::CallProp); /* Load dslots. */ @@ -2958,13 +3016,12 @@ mjit::Compiler::jsop_setprop(JSAtom *atom) * the normal SETNAME property cache logic. */ JSOp op = JSOp(*PC); + stubcc.masm.move(ImmPtr(atom), Registers::ArgReg1); if (op == JSOP_SETNAME || op == JSOP_SETPROP || op == JSOP_SETGNAME || op == JSOP_SETMETHOD) { - stubcc.masm.move(ImmPtr(atom), Registers::ArgReg1); stubcc.call(STRICT_VARIANT(stubs::SetName)); } else { - stubcc.masm.move(Imm32(pics.length()), Registers::ArgReg1); - stubcc.call(ic::SetPropDumb); + stubcc.call(STRICT_VARIANT(stubs::SetPropNoCache)); } typeCheck = stubcc.masm.jump(); @@ -3004,7 +3061,7 @@ mjit::Compiler::jsop_setprop(JSAtom *atom) pic.slowPathStart = stubcc.linkExit(j, Uses(2)); stubcc.leave(); - stubcc.masm.move(Imm32(pics.length()), Registers::ArgReg1); + passPICAddress(pic); pic.callReturn = stubcc.call(ic::SetProp); } @@ -3084,7 +3141,7 @@ mjit::Compiler::jsop_name(JSAtom *atom) { pic.slowPathStart = stubcc.linkExit(j, Uses(0)); stubcc.leave(); - stubcc.masm.move(Imm32(pics.length()), Registers::ArgReg1); + passPICAddress(pic); pic.callReturn = stubcc.call(ic::Name); } @@ -3127,7 +3184,7 @@ mjit::Compiler::jsop_xname(JSAtom *atom) { pic.slowPathStart = stubcc.linkExit(j, Uses(1)); stubcc.leave(); - stubcc.masm.move(Imm32(pics.length()), Registers::ArgReg1); + passPICAddress(pic); pic.callReturn = stubcc.call(ic::XName); } @@ -3169,7 +3226,7 @@ mjit::Compiler::jsop_bindname(uint32 index) { pic.slowPathStart = stubcc.linkExit(j, Uses(0)); stubcc.leave(); - stubcc.masm.move(Imm32(pics.length()), Registers::ArgReg1); + passPICAddress(pic); pic.callReturn = stubcc.call(ic::BindName); } @@ -3876,7 +3933,7 @@ mjit::Compiler::jsop_getgname(uint32 index) stubcc.linkExit(shapeGuard, Uses(0)); stubcc.leave(); - stubcc.masm.move(Imm32(mics.length()), Registers::ArgReg1); + passMICAddress(mic); mic.stubEntry = stubcc.masm.label(); mic.call = stubcc.call(ic::GetGlobalName); @@ -3975,7 +4032,7 @@ mjit::Compiler::jsop_setgname(uint32 index) stubcc.linkExit(shapeGuard, Uses(2)); stubcc.leave(); - stubcc.masm.move(Imm32(mics.length()), Registers::ArgReg1); + passMICAddress(mic); mic.stubEntry = stubcc.masm.label(); mic.call = stubcc.call(ic::SetGlobalName); @@ -4226,7 +4283,7 @@ mjit::Compiler::jumpAndTrace(Jump j, jsbytecode *target, Jump *slowOne, Jump *sl if (slowTwo) slowTwo->linkTo(traceStart, &stubcc.masm); # if JS_MONOIC - stubcc.masm.move(Imm32(mics.length()), Registers::ArgReg1); + passMICAddress(mic); # endif /* Save and restore compiler-tracked PC, so cx->regs is right in InvokeTracer. */ @@ -4296,3 +4353,45 @@ mjit::Compiler::leaveBlock() frame.leaveBlock(n); } +// Creates the new object expected for constructors, and places it in |thisv|. +// It is broken down into the following operations: +// CALLEE +// GETPROP "prototype" +// IFPRIMTOP: +// NULL +// call js_CreateThisFromFunctionWithProto(...) +// +void +mjit::Compiler::constructThis() +{ + JS_ASSERT(isConstructing); + + // Load the callee. + Address callee(JSFrameReg, JSStackFrame::offsetOfCallee(fun)); + RegisterID calleeReg = frame.allocReg(); + masm.loadPayload(callee, calleeReg); + frame.pushTypedPayload(JSVAL_TYPE_OBJECT, calleeReg); + + // Get callee.prototype. + jsop_getprop(cx->runtime->atomState.classPrototypeAtom); + + // Reach into the proto Value and grab a register for its data. + FrameEntry *protoFe = frame.peek(-1); + RegisterID protoReg = frame.ownRegForData(protoFe); + + // Now, get the type. If it's not an object, set protoReg to NULL. + Jump isNotObject = frame.testObject(Assembler::NotEqual, protoFe); + stubcc.linkExitDirect(isNotObject, stubcc.masm.label()); + stubcc.masm.move(ImmPtr(NULL), protoReg); + stubcc.crossJump(stubcc.masm.jump(), masm.label()); + + // Done with the protoFe. + frame.pop(); + + prepareStubCall(Uses(0)); + if (protoReg != Registers::ArgReg1) + masm.move(protoReg, Registers::ArgReg1); + stubCall(stubs::CreateThis); + frame.freeReg(protoReg); +} + diff --git a/js/src/methodjit/Compiler.h b/js/src/methodjit/Compiler.h index be20c21df323..4cab7bca831b 100644 --- a/js/src/methodjit/Compiler.h +++ b/js/src/methodjit/Compiler.h @@ -72,6 +72,7 @@ class Compiler : public BaseCompiler Label entry; Label stubEntry; DataLabel32 shape; + DataLabelPtr addrLabel; #if defined JS_PUNBOX64 uint32 patchValueOffset; #endif @@ -114,6 +115,8 @@ class Compiler : public BaseCompiler Label slowJoinPoint; Label slowPathStart; Label hotPathLabel; + DataLabelPtr addrLabel1; + DataLabelPtr addrLabel2; Jump oolJump; RegisterID funObjReg; RegisterID funPtrReg; @@ -143,6 +146,7 @@ class Compiler : public BaseCompiler Label storeBack; Label typeCheck; Label slowPathStart; + DataLabelPtr addrLabel; RegisterID shapeReg; RegisterID objReg; RegisterID idReg; @@ -196,10 +200,12 @@ class Compiler : public BaseCompiler bool ool; }; + JSStackFrame *fp; JSScript *script; JSObject *scopeChain; JSObject *globalObj; JSFunction *fun; + bool isConstructing; BytecodeAnalyzer analysis; Label *jumpMap; jsbytecode *PC; @@ -211,7 +217,7 @@ class Compiler : public BaseCompiler js::Vector callICs; #endif #if defined JS_POLYIC - js::Vector pics; + js::Vector pics; #endif js::Vector callPatches; js::Vector callSites; @@ -226,10 +232,10 @@ class Compiler : public BaseCompiler // follows interpreter usage in JSOP_LENGTH. enum { LengthAtomIndex = uint32(-2) }; - Compiler(JSContext *cx, JSScript *script, JSFunction *fun, JSObject *scopeChain); + Compiler(JSContext *cx, JSStackFrame *fp); ~Compiler(); - CompileStatus Compile(); + CompileStatus compile(); jsbytecode *getPC() { return PC; } Label getLabel() { return masm.label(); } @@ -238,10 +244,11 @@ class Compiler : public BaseCompiler void *findCallSite(const CallSite &callSite); private: + CompileStatus performCompilation(JITScript **jitp); CompileStatus generatePrologue(); CompileStatus generateMethod(); CompileStatus generateEpilogue(); - CompileStatus finishThisUp(); + CompileStatus finishThisUp(JITScript **jitp); /* Non-emitting helpers. */ uint32 fullAtomIndex(jsbytecode *pc); @@ -257,6 +264,9 @@ class Compiler : public BaseCompiler void iterMore(); void iterEnd(); MaybeJump loadDouble(FrameEntry *fe, FPRegisterID fpReg); + void passPICAddress(PICGenInfo &pic); + void passMICAddress(MICGenInfo &mic); + void constructThis(); /* Opcode handlers. */ void jumpAndTrace(Jump j, jsbytecode *target, Jump *slowOne = NULL, Jump *slowTwo = NULL); @@ -268,12 +278,13 @@ class Compiler : public BaseCompiler void jsop_this(); void emitReturn(FrameEntry *fe); void emitFinalReturn(Assembler &masm); - void loadReturnValue(Assembler &masm); + void loadReturnValue(Assembler *masm, FrameEntry *fe); + void emitReturnValue(Assembler *masm, FrameEntry *fe); void dispatchCall(VoidPtrStubUInt32 stub, uint32 argc); void interruptCheckHelper(); void emitUncachedCall(uint32 argc, bool callingNew); - void emitPrimitiveTestForNew(uint32 argc); void inlineCallHelper(uint32 argc, bool callingNew); + void fixPrimitiveReturn(Assembler *masm, FrameEntry *fe); void jsop_gnameinc(JSOp op, VoidStubAtom stub, uint32 index); void jsop_nameinc(JSOp op, VoidStubAtom stub, uint32 index); void jsop_propinc(JSOp op, VoidStubAtom stub, uint32 index); diff --git a/js/src/methodjit/FrameState.cpp b/js/src/methodjit/FrameState.cpp index 4b26b8929463..838f638fc510 100644 --- a/js/src/methodjit/FrameState.cpp +++ b/js/src/methodjit/FrameState.cpp @@ -327,7 +327,7 @@ FrameState::storeTo(FrameEntry *fe, Address address, bool popped) #endif } -void FrameState::storeTo(FrameEntry *fe, RegisterID dataReg, RegisterID typeReg, RegisterID tempReg) +void FrameState::loadTo(FrameEntry *fe, RegisterID typeReg, RegisterID dataReg, RegisterID tempReg) { JS_ASSERT(dataReg != typeReg && dataReg != tempReg && typeReg != tempReg); @@ -347,6 +347,15 @@ void FrameState::storeTo(FrameEntry *fe, RegisterID dataReg, RegisterID typeReg, return; } +#ifdef JS_PUNBOX64 + // If the value is synced, and requires at least one load, we can do + // better on x64. + if (fe->type.inMemory() && fe->data.inMemory()) { + masm.loadValueAsComponents(addressOf(fe), typeReg, dataReg); + return; + } +#endif + RegisterID data = tempRegForData(fe); RegisterID type = tempRegForType(fe); if (data == typeReg && type == dataReg) { @@ -893,6 +902,14 @@ FrameState::ownRegForData(FrameEntry *fe) return reg; } +void +FrameState::discardFe(FrameEntry *fe) +{ + forgetEntry(fe); + fe->type.setMemory(); + fe->data.setMemory(); +} + void FrameState::pushCopyOf(uint32 index) { diff --git a/js/src/methodjit/FrameState.h b/js/src/methodjit/FrameState.h index d66415ba6ee5..bca9568d3b7d 100644 --- a/js/src/methodjit/FrameState.h +++ b/js/src/methodjit/FrameState.h @@ -590,7 +590,7 @@ class FrameState * Fully stores a FrameEntry into two arbitrary registers. tempReg may be * used as a temporary. */ - void storeTo(FrameEntry *fe, RegisterID dataReg, RegisterID typeReg, RegisterID tempReg); + void loadTo(FrameEntry *fe, RegisterID typeReg, RegisterID dataReg, RegisterID tempReg); /* * Stores the top stack slot back to a slot. @@ -654,6 +654,11 @@ class FrameState */ inline void forgetType(FrameEntry *fe); + /* + * Discards a FrameEntry, tricking the FS into thinking it's synced. + */ + void discardFe(FrameEntry *fe); + /* * Helper function. Tests if a slot's type is null. Condition should * be Equal or NotEqual. diff --git a/js/src/methodjit/InvokeHelpers.cpp b/js/src/methodjit/InvokeHelpers.cpp index a7e3eadf9b38..a9af7e6abda1 100644 --- a/js/src/methodjit/InvokeHelpers.cpp +++ b/js/src/methodjit/InvokeHelpers.cpp @@ -219,29 +219,6 @@ InlineReturn(VMFrame &f, JSBool ok, JSBool popFrame) return ok; } -JSBool JS_FASTCALL -stubs::NewObject(VMFrame &f, uint32 argc) -{ - JSContext *cx = f.cx; - Value *vp = f.regs.sp - (argc + 2); - - JSObject *funobj = &vp[0].toObject(); - JS_ASSERT(funobj->isFunction()); - - jsid id = ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom); - if (!funobj->getProperty(cx, id, &vp[1])) - THROWV(JS_FALSE); - - JSObject *proto = vp[1].isObject() ? &vp[1].toObject() : NULL; - JSObject *obj = NewNonFunction(cx, &js_ObjectClass, proto, funobj->getParent()); - if (!obj) - THROWV(JS_FALSE); - - vp[1].setObject(*obj); - - return JS_TRUE; -} - void JS_FASTCALL stubs::SlowCall(VMFrame &f, uint32 argc) { @@ -361,8 +338,8 @@ stubs::CompileFunction(VMFrame &f, uint32 nactual) fp->initCallFrameEarlyPrologue(fun, fp->nativeReturnAddress()); /* Empty script does nothing. */ + bool callingNew = fp->isConstructing(); if (script->isEmpty()) { - bool callingNew = fp->isConstructing(); RemovePartialFrame(cx, fp); Value *vp = f.regs.sp - (nactual + 2); if (callingNew) @@ -389,9 +366,9 @@ stubs::CompileFunction(VMFrame &f, uint32 nactual) if (fun->isHeavyweight() && !js_GetCallObject(cx, fp)) THROWV(NULL); - CompileStatus status = CanMethodJIT(cx, script, fun, &fp->scopeChain()); + CompileStatus status = CanMethodJIT(cx, script, fp); if (status == Compile_Okay) - return script->jit->invoke; + return script->getJIT(callingNew)->invokeEntry; /* Function did not compile... interpret it. */ JSBool ok = Interpret(cx, fp); @@ -441,8 +418,8 @@ UncachedInlineCall(VMFrame &f, uint32 flags, void **pret, uint32 argc) } /* Try to compile if not already compiled. */ - if (!newscript->ncode) { - if (mjit::TryCompile(cx, newscript, newfp->fun(), &newfp->scopeChain()) == Compile_Error) { + if (newscript->getJITStatus(newfp->isConstructing()) == JITScript_None) { + if (mjit::TryCompile(cx, newfp) == Compile_Error) { /* A runtime exception was thrown, get out. */ InlineReturn(f, JS_FALSE); return false; @@ -450,9 +427,8 @@ UncachedInlineCall(VMFrame &f, uint32 flags, void **pret, uint32 argc) } /* If newscript was successfully compiled, run it. */ - JS_ASSERT(newscript->ncode); - if (newscript->ncode != JS_UNJITTABLE_METHOD) { - *pret = newscript->jit->invoke; + if (JITScript *jit = newscript->getJIT(newfp->isConstructing())) { + *pret = jit->invokeEntry; return true; } @@ -484,9 +460,6 @@ stubs::UncachedNewHelper(VMFrame &f, uint32 argc, UncachedCallResult *ucr) if (IsFunctionObject(*vp, &ucr->fun) && ucr->fun->isInterpreted() && !ucr->fun->script()->isEmpty()) { - if (!stubs::NewObject(f, argc)) - return; - ucr->callee = &vp->toObject(); if (!UncachedInlineCall(f, JSFRAME_CONSTRUCTING, &ucr->codeAddr, argc)) THROW(); @@ -612,7 +585,10 @@ js_InternalThrow(VMFrame &f) if (!pc) return NULL; - return cx->fp()->script()->pcToNative(pc); + JSStackFrame *fp = cx->fp(); + JSScript *script = fp->script(); + JITScript *jit = script->getJIT(fp->isConstructing()); + return jit->nmap[pc - script->code]; } void JS_FASTCALL @@ -623,6 +599,18 @@ stubs::GetCallObject(VMFrame &f) THROW(); } +void JS_FASTCALL +stubs::CreateThis(VMFrame &f, JSObject *proto) +{ + JSContext *cx = f.cx; + JSStackFrame *fp = f.fp(); + JSObject *callee = &fp->callee(); + JSObject *obj = js_CreateThisForFunctionWithProto(cx, callee, proto); + if (!obj) + THROW(); + fp->formalArgs()[-1].setObject(*obj); +} + static inline void AdvanceReturnPC(JSContext *cx) { @@ -696,11 +684,12 @@ AtSafePoint(JSContext *cx) return false; JSScript *script = fp->script(); - if (!script->nmap) + JITScript *jit = script->getJIT(fp->isConstructing()); + if (!jit->nmap) return false; JS_ASSERT(cx->regs->pc >= script->code && cx->regs->pc < script->code + script->length); - return !!script->nmap[cx->regs->pc - script->code]; + return !!jit->nmap[cx->regs->pc - script->code]; } static inline JSBool @@ -709,8 +698,11 @@ PartialInterpret(VMFrame &f) JSContext *cx = f.cx; JSStackFrame *fp = cx->fp(); - JS_ASSERT(fp->hasImacropc() || !fp->script()->nmap || - !fp->script()->nmap[cx->regs->pc - fp->script()->code]); +#ifdef DEBUG + JITScript *jit = fp->script()->getJIT(fp->isConstructing()); + JS_ASSERT(fp->hasImacropc() || !jit->nmap || + !jit->nmap[cx->regs->pc - fp->script()->code]); +#endif JSBool ok = JS_TRUE; ok = Interpret(cx, fp, 0, JSINTERP_SAFEPOINT); @@ -740,7 +732,8 @@ FinishExcessFrames(VMFrame &f, JSStackFrame *entryFrame) if (AtSafePoint(cx)) { JSScript *script = fp->script(); - if (!JaegerShotAtSafePoint(cx, script->nmap[cx->regs->pc - script->code])) { + JITScript *jit = script->getJIT(fp->isConstructing()); + if (!JaegerShotAtSafePoint(cx, jit->nmap[cx->regs->pc - script->code])) { if (!HandleErrorInExcessFrames(f, entryFrame)) return false; @@ -894,9 +887,10 @@ RunTracer(VMFrame &f) /* Step 2. If entryFrame is at a safe point, just leave. */ if (AtSafePoint(cx)) { + JITScript *jit = entryFrame->script()->getJIT(entryFrame->isConstructing()); uint32 offs = uint32(cx->regs->pc - entryFrame->script()->code); - JS_ASSERT(entryFrame->script()->nmap[offs]); - return entryFrame->script()->nmap[offs]; + JS_ASSERT(jit->nmap[offs]); + return jit->nmap[offs]; } /* Step 3. If entryFrame is at a RETURN, then leave slightly differently. */ @@ -930,14 +924,10 @@ RunTracer(VMFrame &f) #if defined JS_TRACER # if defined JS_MONOIC void *JS_FASTCALL -stubs::InvokeTracer(VMFrame &f, uint32 index) +stubs::InvokeTracer(VMFrame &f, ic::MICInfo *mic) { - JSScript *script = f.fp()->script(); - ic::MICInfo &mic = script->mics[index]; - - JS_ASSERT(mic.kind == ic::MICInfo::TRACER); - - return RunTracer(f, mic); + JS_ASSERT(mic->kind == ic::MICInfo::TRACER); + return RunTracer(f, *mic); } # else diff --git a/js/src/methodjit/MethodJIT.cpp b/js/src/methodjit/MethodJIT.cpp index 191c9c549604..c457f6f995db 100644 --- a/js/src/methodjit/MethodJIT.cpp +++ b/js/src/methodjit/MethodJIT.cpp @@ -764,9 +764,9 @@ EnterMethodJIT(JSContext *cx, JSStackFrame *fp, void *code) JSBool mjit::JaegerShot(JSContext *cx) { - JSScript *script = cx->fp()->script(); - - JS_ASSERT(script->ncode && script->ncode != JS_UNJITTABLE_METHOD); + JSStackFrame *fp = cx->fp(); + JSScript *script = fp->script(); + JITScript *jit = script->getJIT(fp->isConstructing()); #ifdef JS_TRACER if (TRACE_RECORDER(cx)) @@ -775,7 +775,7 @@ mjit::JaegerShot(JSContext *cx) JS_ASSERT(cx->regs->pc == script->code); - return EnterMethodJIT(cx, cx->fp(), script->jit->invoke); + return EnterMethodJIT(cx, cx->fp(), jit->invokeEntry); } JSBool @@ -795,37 +795,47 @@ static inline void Destroy(T &t) } void -mjit::ReleaseScriptCode(JSContext *cx, JSScript *script) +mjit::JITScript::release() { - if (script->jit) { #if defined DEBUG && (defined JS_CPU_X86 || defined JS_CPU_X64) - memset(script->jit->invoke, 0xcc, script->jit->inlineLength + - script->jit->outOfLineLength); + void *addr = code.m_code.executableAddress(); + memset(addr, 0xcc, code.m_size); #endif - script->jit->execPool->release(); - script->jit->execPool = NULL; - // Releasing the execPool takes care of releasing the code. - script->ncode = NULL; + code.m_executablePool->release(); #if defined JS_POLYIC - for (uint32 i = 0; i < script->jit->nPICs; i++) { - script->pics[i].releasePools(); - Destroy(script->pics[i].execPools); - } + for (uint32 i = 0; i < nPICs; i++) { + pics[i].releasePools(); + Destroy(pics[i].execPools); + } #endif #if defined JS_MONOIC - for (uint32 i = 0; i < script->jit->nCallICs; i++) - script->callICs[i].releasePools(); + for (uint32 i = 0; i < nCallICs; i++) + callICs[i].releasePools(); #endif +} - cx->free(script->jit); +void +mjit::ReleaseScriptCode(JSContext *cx, JSScript *script) +{ + // NB: The recompiler may call ReleaseScriptCode, in which case it + // will get called again when the script is destroyed, so we + // must protect against calling ReleaseScriptCode twice. - // The recompiler may call ReleaseScriptCode, in which case it - // will get called again when the script is destroyed, so we - // must protect against calling ReleaseScriptCode twice. - script->jit = NULL; + if (script->jitNormal) { + script->jitNormal->release(); + script->jitArityCheckNormal = NULL; + cx->free(script->jitNormal); + script->jitNormal = NULL; + } + + if (script->jitCtor) { + script->jitCtor->release(); + script->jitArityCheckCtor = NULL; + cx->free(script->jitCtor); + script->jitCtor = NULL; } } diff --git a/js/src/methodjit/MethodJIT.h b/js/src/methodjit/MethodJIT.h index 68d6dd0f0c31..850b6a3dfa74 100644 --- a/js/src/methodjit/MethodJIT.h +++ b/js/src/methodjit/MethodJIT.h @@ -139,6 +139,18 @@ struct VMFrame extern "C" void JaegerStubVeneer(void); #endif +namespace mjit { +namespace ic { +# if defined JS_POLYIC + struct PICInfo; +# endif +# if defined JS_MONOIC + struct MICInfo; + struct CallICInfo; +# endif +} +} + typedef void (JS_FASTCALL *VoidStub)(VMFrame &); typedef void (JS_FASTCALL *VoidVpStub)(VMFrame &, Value *); typedef void (JS_FASTCALL *VoidStubUInt32)(VMFrame &, uint32); @@ -158,26 +170,47 @@ typedef JSString * (JS_FASTCALL *JSStrStubUInt32)(VMFrame &, uint32); typedef void (JS_FASTCALL *VoidStubJSObj)(VMFrame &, JSObject *); typedef void (JS_FASTCALL *VoidStubPC)(VMFrame &, jsbytecode *); typedef JSBool (JS_FASTCALL *BoolStubUInt32)(VMFrame &f, uint32); - -#define JS_UNJITTABLE_METHOD (reinterpret_cast(1)) +typedef void (JS_FASTCALL *VoidStubMIC)(VMFrame &, js::mjit::ic::MICInfo *); +typedef void * (JS_FASTCALL *VoidPtrStubMIC)(VMFrame &, js::mjit::ic::MICInfo *); +typedef void (JS_FASTCALL *VoidStubPIC)(VMFrame &, js::mjit::ic::PICInfo *); +typedef void (JS_FASTCALL *VoidStubCallIC)(VMFrame &, js::mjit::ic::CallICInfo *); +typedef void * (JS_FASTCALL *VoidPtrStubCallIC)(VMFrame &, js::mjit::ic::CallICInfo *); namespace mjit { +struct CallSite; + struct JITScript { - JSC::ExecutablePool *execPool; /* pool that contains |ncode|; script owns the pool */ - uint32 inlineLength; /* length of inline JIT'd code */ - uint32 outOfLineLength; /* length of out of line JIT'd code */ + typedef JSC::MacroAssemblerCodeRef CodeRef; + CodeRef code; /* pool & code addresses */ + js::mjit::CallSite *callSites; uint32 nCallSites; + void **nmap; /* scripted pc to native code map. */ #ifdef JS_MONOIC - uint32 nMICs; /* number of MonoICs */ - uint32 nCallICs; /* number of call ICs */ + ic::MICInfo *mics; /* MICs in this script. */ + uint32 nMICs; /* number of MonoICs */ + ic::CallICInfo *callICs; /* CallICs in this script. */ + uint32 nCallICs; /* number of call ICs */ #endif #ifdef JS_POLYIC - uint32 nPICs; /* number of PolyICs */ + ic::PICInfo *pics; /* PICs in this script */ + uint32 nPICs; /* number of PolyICs */ #endif - void *invoke; /* invoke address */ - void *arityCheck; /* arity check address */ + void *invokeEntry; /* invoke address */ + void *fastEntry; /* cached entry, fastest */ + void *arityCheckEntry; /* arity check address */ + + bool isValidCode(void *ptr) { + char *jitcode = (char *)code.m_code.executableAddress(); + char *jcheck = (char *)ptr; + return jcheck >= jitcode && jcheck < jitcode + code.m_size; + } + + void sweepCallICs(); + void purgeMICs(); + void purgePICs(); + void release(); }; /* Execute a method that has been JIT compiled. */ @@ -197,18 +230,21 @@ void JS_FASTCALL ProfileStubCall(VMFrame &f); CompileStatus JS_NEVER_INLINE -TryCompile(JSContext *cx, JSScript *script, JSFunction *fun, JSObject *scopeChain); +TryCompile(JSContext *cx, JSStackFrame *fp); void ReleaseScriptCode(JSContext *cx, JSScript *script); static inline CompileStatus -CanMethodJIT(JSContext *cx, JSScript *script, JSFunction *fun, JSObject *scopeChain) +CanMethodJIT(JSContext *cx, JSScript *script, JSStackFrame *fp) { - if (!cx->methodJitEnabled || script->ncode == JS_UNJITTABLE_METHOD) + if (!cx->methodJitEnabled) return Compile_Abort; - if (script->ncode == NULL) - return TryCompile(cx, script, fun, scopeChain); + JITScriptStatus status = script->getJITStatus(fp->isConstructing()); + if (status == JITScript_Invalid) + return Compile_Abort; + if (status == JITScript_None) + return TryCompile(cx, fp); return Compile_Okay; } @@ -223,6 +259,25 @@ struct CallSite } /* namespace js */ +inline void ** +JSScript::maybeNativeMap(bool constructing) +{ + js::mjit::JITScript *jit = constructing ? jitCtor : jitNormal; + if (!jit) + return NULL; + return jit->nmap; +} + +inline bool +JSScript::hasNativeCodeForPC(bool constructing, jsbytecode *pc) +{ + js::mjit::JITScript *jit = getJIT(constructing); + if (!jit) + return false; + JS_ASSERT(pc >= code && pc < code + length); + return !!jit->nmap[pc - code]; +} + #ifdef _MSC_VER extern "C" void *JaegerThrowpoline(js::VMFrame *vmFrame); #else diff --git a/js/src/methodjit/MonoIC.cpp b/js/src/methodjit/MonoIC.cpp index ce12f4a9f3d2..eada7e730948 100644 --- a/js/src/methodjit/MonoIC.cpp +++ b/js/src/methodjit/MonoIC.cpp @@ -70,22 +70,21 @@ typedef JSC::MacroAssembler::Call Call; #if defined JS_MONOIC static void -PatchGetFallback(VMFrame &f, ic::MICInfo &mic) +PatchGetFallback(VMFrame &f, ic::MICInfo *ic) { - JSC::RepatchBuffer repatch(mic.stubEntry.executableAddress(), 64); + JSC::RepatchBuffer repatch(ic->stubEntry.executableAddress(), 64); JSC::FunctionPtr fptr(JS_FUNC_TO_DATA_PTR(void *, stubs::GetGlobalName)); - repatch.relink(mic.stubCall, fptr); + repatch.relink(ic->stubCall, fptr); } void JS_FASTCALL -ic::GetGlobalName(VMFrame &f, uint32 index) +ic::GetGlobalName(VMFrame &f, ic::MICInfo *ic) { JSObject *obj = f.fp()->scopeChain().getGlobal(); - ic::MICInfo &mic = f.fp()->script()->mics[index]; JSAtom *atom = f.fp()->script()->getAtom(GET_INDEX(f.regs.pc)); jsid id = ATOM_TO_JSID(atom); - JS_ASSERT(mic.kind == ic::MICInfo::GET); + JS_ASSERT(ic->kind == ic::MICInfo::GET); JS_LOCK_OBJ(f.cx, obj); const Shape *shape = obj->nativeLookup(id); @@ -95,33 +94,33 @@ ic::GetGlobalName(VMFrame &f, uint32 index) { JS_UNLOCK_OBJ(f.cx, obj); if (shape) - PatchGetFallback(f, mic); + PatchGetFallback(f, ic); stubs::GetGlobalName(f); return; } uint32 slot = shape->slot; JS_UNLOCK_OBJ(f.cx, obj); - mic.u.name.touched = true; + ic->u.name.touched = true; /* Patch shape guard. */ - JSC::RepatchBuffer repatch(mic.entry.executableAddress(), 50); - repatch.repatch(mic.shape, obj->shape()); + JSC::RepatchBuffer repatch(ic->entry.executableAddress(), 50); + repatch.repatch(ic->shape, obj->shape()); /* Patch loads. */ JS_ASSERT(slot >= JS_INITIAL_NSLOTS); slot -= JS_INITIAL_NSLOTS; slot *= sizeof(Value); - JSC::RepatchBuffer loads(mic.load.executableAddress(), 32, false); + JSC::RepatchBuffer loads(ic->load.executableAddress(), 32, false); #if defined JS_CPU_X86 - loads.repatch(mic.load.dataLabel32AtOffset(MICInfo::GET_DATA_OFFSET), slot); - loads.repatch(mic.load.dataLabel32AtOffset(MICInfo::GET_TYPE_OFFSET), slot + 4); + loads.repatch(ic->load.dataLabel32AtOffset(MICInfo::GET_DATA_OFFSET), slot); + loads.repatch(ic->load.dataLabel32AtOffset(MICInfo::GET_TYPE_OFFSET), slot + 4); #elif defined JS_CPU_ARM - // mic.load actually points to the LDR instruction which fetches the offset, but 'repatch' + // ic->load actually points to the LDR instruction which fetches the offset, but 'repatch' // knows how to dereference it to find the integer value. - loads.repatch(mic.load.dataLabel32AtOffset(0), slot); + loads.repatch(ic->load.dataLabel32AtOffset(0), slot); #elif defined JS_PUNBOX64 - loads.repatch(mic.load.dataLabel32AtOffset(mic.patchValueOffset), slot); + loads.repatch(ic->load.dataLabel32AtOffset(ic->patchValueOffset), slot); #endif /* Do load anyway... this time. */ @@ -140,11 +139,11 @@ SetGlobalNameSlow(VMFrame &f, uint32 index) } static void -PatchSetFallback(VMFrame &f, ic::MICInfo &mic) +PatchSetFallback(VMFrame &f, ic::MICInfo *ic) { - JSC::RepatchBuffer repatch(mic.stubEntry.executableAddress(), 64); + JSC::RepatchBuffer repatch(ic->stubEntry.executableAddress(), 64); JSC::FunctionPtr fptr(JS_FUNC_TO_DATA_PTR(void *, SetGlobalNameSlow)); - repatch.relink(mic.stubCall, fptr); + repatch.relink(ic->stubCall, fptr); } static VoidStubAtom @@ -159,14 +158,13 @@ GetStubForSetGlobalName(VMFrame &f) } void JS_FASTCALL -ic::SetGlobalName(VMFrame &f, uint32 index) +ic::SetGlobalName(VMFrame &f, ic::MICInfo *ic) { JSObject *obj = f.fp()->scopeChain().getGlobal(); - ic::MICInfo &mic = f.fp()->script()->mics[index]; JSAtom *atom = f.fp()->script()->getAtom(GET_INDEX(f.regs.pc)); jsid id = ATOM_TO_JSID(atom); - JS_ASSERT(mic.kind == ic::MICInfo::SET); + JS_ASSERT(ic->kind == ic::MICInfo::SET); JS_LOCK_OBJ(f.cx, obj); const Shape *shape = obj->nativeLookup(id); @@ -177,40 +175,40 @@ ic::SetGlobalName(VMFrame &f, uint32 index) { JS_UNLOCK_OBJ(f.cx, obj); if (shape) - PatchSetFallback(f, mic); + PatchSetFallback(f, ic); GetStubForSetGlobalName(f)(f, atom); return; } uint32 slot = shape->slot; JS_UNLOCK_OBJ(f.cx, obj); - mic.u.name.touched = true; + ic->u.name.touched = true; /* Patch shape guard. */ - JSC::RepatchBuffer repatch(mic.entry.executableAddress(), 50); - repatch.repatch(mic.shape, obj->shape()); + JSC::RepatchBuffer repatch(ic->entry.executableAddress(), 50); + repatch.repatch(ic->shape, obj->shape()); /* Patch loads. */ JS_ASSERT(slot >= JS_INITIAL_NSLOTS); slot -= JS_INITIAL_NSLOTS; slot *= sizeof(Value); - JSC::RepatchBuffer stores(mic.load.executableAddress(), 32, false); + JSC::RepatchBuffer stores(ic->load.executableAddress(), 32, false); #if defined JS_CPU_X86 - stores.repatch(mic.load.dataLabel32AtOffset(MICInfo::SET_TYPE_OFFSET), slot + 4); + stores.repatch(ic->load.dataLabel32AtOffset(MICInfo::SET_TYPE_OFFSET), slot + 4); uint32 dataOffset; - if (mic.u.name.typeConst) + if (ic->u.name.typeConst) dataOffset = MICInfo::SET_DATA_CONST_TYPE_OFFSET; else dataOffset = MICInfo::SET_DATA_TYPE_OFFSET; - stores.repatch(mic.load.dataLabel32AtOffset(dataOffset), slot); + stores.repatch(ic->load.dataLabel32AtOffset(dataOffset), slot); #elif defined JS_CPU_ARM - // mic.load actually points to the LDR instruction which fetches the offset, but 'repatch' + // ic->load actually points to the LDR instruction which fetches the offset, but 'repatch' // knows how to dereference it to find the integer value. - stores.repatch(mic.load.dataLabel32AtOffset(0), slot); + stores.repatch(ic->load.dataLabel32AtOffset(0), slot); #elif defined JS_PUNBOX64 - stores.repatch(mic.load.dataLabel32AtOffset(mic.patchValueOffset), slot); + stores.repatch(ic->load.dataLabel32AtOffset(ic->patchValueOffset), slot); #endif // Actually implement the op the slow way. @@ -218,24 +216,16 @@ ic::SetGlobalName(VMFrame &f, uint32 index) } static void * JS_FASTCALL -SlowCallFromIC(VMFrame &f, uint32 index) +SlowCallFromIC(VMFrame &f, ic::CallICInfo *ic) { - JSScript *oldscript = f.fp()->script(); - CallICInfo &ic= oldscript->callICs[index]; - - stubs::SlowCall(f, ic.argc); - + stubs::SlowCall(f, ic->argc); return NULL; } static void * JS_FASTCALL -SlowNewFromIC(VMFrame &f, uint32 index) +SlowNewFromIC(VMFrame &f, ic::CallICInfo *ic) { - JSScript *oldscript = f.fp()->script(); - CallICInfo &ic = oldscript->callICs[index]; - - stubs::SlowNew(f, ic.argc); - + stubs::SlowNew(f, ic->argc); return NULL; } @@ -325,8 +315,11 @@ class CallCompiler : public BaseCompiler * here since ncode has two failure modes and we need to load out of * nmap anyway. */ - masm.loadPtr(Address(t0, offsetof(JSScript, jit)), t0); - Jump hasCode = masm.branchTestPtr(Assembler::NonZero, t0, t0); + size_t offset = callingNew + ? offsetof(JSScript, jitArityCheckCtor) + : offsetof(JSScript, jitArityCheckNormal); + masm.loadPtr(Address(t0, offset), t0); + Jump hasCode = masm.branchPtr(Assembler::Above, t0, ImmPtr(JS_UNJITTABLE_SCRIPT)); /* Try and compile. On success we get back the nmap pointer. */ masm.storePtr(JSFrameReg, FrameAddress(offsetof(VMFrame, regs.fp))); @@ -345,7 +338,6 @@ class CallCompiler : public BaseCompiler /* Get nmap[ARITY], set argc, call. */ masm.move(Imm32(ic.argc), JSParamReg_Argc); - masm.loadPtr(Address(t0, offsetof(JITScript, arityCheck)), t0); masm.jump(t0); JSC::ExecutablePool *ep = poolForSize(masm.size(), CallICInfo::Pool_ScriptStub); @@ -377,9 +369,11 @@ class CallCompiler : public BaseCompiler ic.fastGuardedObject = obj; + JITScript *jit = script->getJIT(callingNew); + repatch.repatch(ic.funGuard, obj); repatch.relink(ic.funGuard.jumpAtOffset(ic.hotJumpOffset), - JSC::CodeLocationLabel(script->ncode)); + JSC::CodeLocationLabel(jit->fastEntry)); JaegerSpew(JSpew_PICs, "patched CALL path %p (obj: %p)\n", start, ic.fastGuardedObject); } @@ -649,52 +643,40 @@ class CallCompiler : public BaseCompiler }; void * JS_FASTCALL -ic::Call(VMFrame &f, uint32 index) +ic::Call(VMFrame &f, CallICInfo *ic) { - JSScript *oldscript = f.fp()->script(); - CallICInfo &ic = oldscript->callICs[index]; - CallCompiler cc(f, ic, false); + CallCompiler cc(f, *ic, false); return cc.update(); } void * JS_FASTCALL -ic::New(VMFrame &f, uint32 index) +ic::New(VMFrame &f, CallICInfo *ic) { - JSScript *oldscript = f.fp()->script(); - CallICInfo &ic = oldscript->callICs[index]; - CallCompiler cc(f, ic, true); + CallCompiler cc(f, *ic, true); return cc.update(); } void JS_FASTCALL -ic::NativeCall(VMFrame &f, uint32 index) +ic::NativeCall(VMFrame &f, CallICInfo *ic) { - JSScript *oldscript = f.fp()->script(); - CallICInfo &ic = oldscript->callICs[index]; - CallCompiler cc(f, ic, false); + CallCompiler cc(f, *ic, false); if (!cc.generateNativeStub()) - stubs::SlowCall(f, ic.argc); + stubs::SlowCall(f, ic->argc); } void JS_FASTCALL -ic::NativeNew(VMFrame &f, uint32 index) +ic::NativeNew(VMFrame &f, CallICInfo *ic) { - JSScript *oldscript = f.fp()->script(); - CallICInfo &ic = oldscript->callICs[index]; - CallCompiler cc(f, ic, true); + CallCompiler cc(f, *ic, true); if (!cc.generateNativeStub()) - stubs::SlowNew(f, ic.argc); + stubs::SlowNew(f, ic->argc); } void -ic::PurgeMICs(JSContext *cx, JSScript *script) +JITScript::purgeMICs() { - /* MICs are purged during GC to handle changing shapes. */ - JS_ASSERT(cx->runtime->gcRegenShapes); - - uint32 nmics = script->jit->nMICs; - for (uint32 i = 0; i < nmics; i++) { - ic::MICInfo &mic = script->mics[i]; + for (uint32 i = 0; i < nMICs; i++) { + ic::MICInfo &mic = mics[i]; switch (mic.kind) { case ic::MICInfo::SET: case ic::MICInfo::GET: @@ -720,10 +702,22 @@ ic::PurgeMICs(JSContext *cx, JSScript *script) } void -ic::SweepCallICs(JSScript *script) +ic::PurgeMICs(JSContext *cx, JSScript *script) { - for (uint32 i = 0; i < script->jit->nCallICs; i++) { - ic::CallICInfo &ic = script->callICs[i]; + /* MICs are purged during GC to handle changing shapes. */ + JS_ASSERT(cx->runtime->gcRegenShapes); + + if (script->jitNormal) + script->jitNormal->purgeMICs(); + if (script->jitCtor) + script->jitCtor->purgeMICs(); +} + +void +JITScript::sweepCallICs() +{ + for (uint32 i = 0; i < nCallICs; i++) { + ic::CallICInfo &ic = callICs[i]; /* * If the object is unreachable, we're guaranteed not to be currently @@ -757,5 +751,14 @@ ic::SweepCallICs(JSScript *script) } } +void +ic::SweepCallICs(JSScript *script) +{ + if (script->jitNormal) + script->jitNormal->sweepCallICs(); + if (script->jitCtor) + script->jitCtor->sweepCallICs(); +} + #endif /* JS_MONOIC */ diff --git a/js/src/methodjit/MonoIC.h b/js/src/methodjit/MonoIC.h index 485e21aab9ba..9df110558e92 100644 --- a/js/src/methodjit/MonoIC.h +++ b/js/src/methodjit/MonoIC.h @@ -109,8 +109,8 @@ struct MICInfo { } u; }; -void JS_FASTCALL GetGlobalName(VMFrame &f, uint32 index); -void JS_FASTCALL SetGlobalName(VMFrame &f, uint32 index); +void JS_FASTCALL GetGlobalName(VMFrame &f, ic::MICInfo *ic); +void JS_FASTCALL SetGlobalName(VMFrame &f, ic::MICInfo *ic); /* See MonoIC.cpp, CallCompiler for more information on call ICs. */ struct CallICInfo { @@ -187,10 +187,10 @@ struct CallICInfo { } }; -void * JS_FASTCALL New(VMFrame &f, uint32 index); -void * JS_FASTCALL Call(VMFrame &f, uint32 index); -void JS_FASTCALL NativeNew(VMFrame &f, uint32 index); -void JS_FASTCALL NativeCall(VMFrame &f, uint32 index); +void * JS_FASTCALL New(VMFrame &f, ic::CallICInfo *ic); +void * JS_FASTCALL Call(VMFrame &f, ic::CallICInfo *ic); +void JS_FASTCALL NativeNew(VMFrame &f, ic::CallICInfo *ic); +void JS_FASTCALL NativeCall(VMFrame &f, ic::CallICInfo *ic); void PurgeMICs(JSContext *cx, JSScript *script); void SweepCallICs(JSScript *script); diff --git a/js/src/methodjit/PolyIC.cpp b/js/src/methodjit/PolyIC.cpp index 0709f6857138..f869437c20f7 100644 --- a/js/src/methodjit/PolyIC.cpp +++ b/js/src/methodjit/PolyIC.cpp @@ -111,8 +111,7 @@ class PICStubCompiler : public BaseCompiler return disable(reason, JS_FUNC_TO_DATA_PTR(void *, stub)); } - bool disable(const char *reason, VoidStubUInt32 stub) - { + bool disable(const char *reason, VoidStubPIC stub) { return disable(reason, JS_FUNC_TO_DATA_PTR(void *, stub)); } @@ -159,7 +158,7 @@ class SetPropCompiler : public PICStubCompiler { JSObject *obj; JSAtom *atom; - VoidStubUInt32 stub; + VoidStubPIC stub; int lastStubSecondShapeGuard; static int32 dslotsLoadOffset(ic::PICInfo &pic) { @@ -224,7 +223,7 @@ class SetPropCompiler : public PICStubCompiler public: SetPropCompiler(VMFrame &f, JSScript *script, JSObject *obj, ic::PICInfo &pic, JSAtom *atom, - VoidStubUInt32 stub) + VoidStubPIC stub) : PICStubCompiler("setprop", f, script, pic), obj(obj), atom(atom), stub(stub), lastStubSecondShapeGuard(pic.secondShapeGuard) { } @@ -746,7 +745,7 @@ class GetPropCompiler : public PICStubCompiler { } GetPropCompiler(VMFrame &f, JSScript *script, JSObject *obj, ic::PICInfo &pic, JSAtom *atom, - VoidStubUInt32 stub) + VoidStubPIC stub) : PICStubCompiler("callprop", f, script, pic), obj(obj), atom(atom), stub(JS_FUNC_TO_DATA_PTR(void *, stub)), lastStubSecondShapeGuard(pic.secondShapeGuard) @@ -770,7 +769,7 @@ class GetPropCompiler : public PICStubCompiler RepatchBuffer repatcher2(pic.slowPathStart.executableAddress(), INLINE_PATH_LENGTH); ReturnAddressPtr retPtr(pic.slowPathStart.callAtOffset(pic.callReturn).executableAddress()); - VoidStubUInt32 stub; + VoidStubPIC stub; switch (pic.kind) { case ic::PICInfo::GET: stub = ic::GetProp; @@ -1573,7 +1572,7 @@ class ScopeNameCompiler : public PICStubCompiler public: ScopeNameCompiler(VMFrame &f, JSScript *script, JSObject *scopeChain, ic::PICInfo &pic, - JSAtom *atom, VoidStubUInt32 stub) + JSAtom *atom, VoidStubPIC stub) : PICStubCompiler("name", f, script, pic), scopeChain(scopeChain), atom(atom), stub(JS_FUNC_TO_DATA_PTR(void *, stub)), obj(NULL), holder(NULL), prop(NULL) { } @@ -1591,7 +1590,7 @@ class ScopeNameCompiler : public PICStubCompiler RepatchBuffer repatcher2(pic.slowPathStart.executableAddress(), INLINE_PATH_LENGTH); ReturnAddressPtr retPtr(pic.slowPathStart.callAtOffset(pic.callReturn).executableAddress()); - VoidStubUInt32 stub = (pic.kind == ic::PICInfo::NAME) ? ic::Name : ic::XName; + VoidStubPIC stub = (pic.kind == ic::PICInfo::NAME) ? ic::Name : ic::XName; MacroAssemblerCodePtr target(JS_FUNC_TO_DATA_PTR(void *, stub)); repatcher.relinkCallerToTrampoline(retPtr, target); } @@ -1912,7 +1911,7 @@ class BindNameCompiler : public PICStubCompiler public: BindNameCompiler(VMFrame &f, JSScript *script, JSObject *scopeChain, ic::PICInfo &pic, - JSAtom *atom, VoidStubUInt32 stub) + JSAtom *atom, VoidStubPIC stub) : PICStubCompiler("bind", f, script, pic), scopeChain(scopeChain), atom(atom), stub(JS_FUNC_TO_DATA_PTR(void *, stub)) { } @@ -2020,15 +2019,14 @@ class BindNameCompiler : public PICStubCompiler }; void JS_FASTCALL -ic::GetProp(VMFrame &f, uint32 index) +ic::GetProp(VMFrame &f, ic::PICInfo *pic) { JSScript *script = f.fp()->script(); - PICInfo &pic = script->pics[index]; - JSAtom *atom = pic.atom; + JSAtom *atom = pic->atom; if (atom == f.cx->runtime->atomState.lengthAtom) { if (f.regs.sp[-1].isString()) { - GetPropCompiler cc(f, script, NULL, pic, NULL, stubs::Length); + GetPropCompiler cc(f, script, NULL, *pic, NULL, stubs::Length); if (!cc.generateStringLengthStub()) { cc.disable("error"); THROW(); @@ -2039,7 +2037,7 @@ ic::GetProp(VMFrame &f, uint32 index) } else if (!f.regs.sp[-1].isPrimitive()) { JSObject *obj = &f.regs.sp[-1].toObject(); if (obj->isArray() || (obj->isArguments() && !obj->isArgsLengthOverridden())) { - GetPropCompiler cc(f, script, obj, pic, NULL, stubs::Length); + GetPropCompiler cc(f, script, obj, *pic, NULL, stubs::Length); if (obj->isArray()) { if (!cc.generateArrayLengthStub()) { cc.disable("error"); @@ -2063,8 +2061,8 @@ ic::GetProp(VMFrame &f, uint32 index) if (!obj) THROW(); - if (pic.shouldGenerate()) { - GetPropCompiler cc(f, script, obj, pic, atom, stubs::GetProp); + if (pic->shouldGenerate()) { + GetPropCompiler cc(f, script, obj, *pic, atom, stubs::GetProp); if (!cc.update()) { cc.disable("error"); THROW(); @@ -2078,10 +2076,9 @@ ic::GetProp(VMFrame &f, uint32 index) } void JS_FASTCALL -ic::GetElem(VMFrame &f, uint32 picIndex) +ic::GetElem(VMFrame &f, ic::PICInfo *pic) { JSScript *script = f.fp()->script(); - PICInfo &pic = script->pics[picIndex]; JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-2]); if (!obj) @@ -2090,8 +2087,8 @@ ic::GetElem(VMFrame &f, uint32 picIndex) Value idval = f.regs.sp[-1]; JS_ASSERT(idval.isString()); JSString *id = idval.toString(); - if (pic.shouldGenerate()) { - GetElemCompiler cc(f, script, obj, pic, id, stubs::GetElem); + if (pic->shouldGenerate()) { + GetElemCompiler cc(f, script, obj, *pic, id, stubs::GetElem); if (!cc.update()) { cc.disable("error"); THROW(); @@ -2107,46 +2104,29 @@ ic::GetElem(VMFrame &f, uint32 picIndex) f.regs.sp[-2] = v; } -void JS_FASTCALL -ic::SetPropDumb(VMFrame &f, uint32 index) -{ - JSScript *script = f.fp()->script(); - ic::PICInfo &pic = script->pics[index]; - JS_ASSERT(pic.isSet()); - JSAtom *atom = pic.atom; - - JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-2]); - if (!obj) - THROW(); - Value rval = f.regs.sp[-1]; - if (!obj->setProperty(f.cx, ATOM_TO_JSID(atom), &f.regs.sp[-1], - script->strictModeCode)) - THROW(); - f.regs.sp[-2] = rval; -} - +template static void JS_FASTCALL -SetPropSlow(VMFrame &f, uint32 index) +SetPropDumb(VMFrame &f, ic::PICInfo *pic) { - JSScript *script = f.fp()->script(); - ic::PICInfo &pic = script->pics[index]; - JS_ASSERT(pic.isSet()); + stubs::SetPropNoCache(f, pic->atom); +} - JSAtom *atom = pic.atom; - STRICT_VARIANT(stubs::SetName)(f, atom); +template +static void JS_FASTCALL +SetPropSlow(VMFrame &f, ic::PICInfo *pic) +{ + stubs::SetName(f, pic->atom); } void JS_FASTCALL -ic::SetProp(VMFrame &f, uint32 index) +ic::SetProp(VMFrame &f, ic::PICInfo *pic) { JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-2]); if (!obj) THROW(); JSScript *script = f.fp()->script(); - ic::PICInfo &pic = script->pics[index]; - JSAtom *atom = pic.atom; - JS_ASSERT(pic.isSet()); + JS_ASSERT(pic->isSet()); // // Important: We update the PIC before looking up the property so that the @@ -2157,7 +2137,7 @@ ic::SetProp(VMFrame &f, uint32 index) // cache can't handle a GET and SET from the same scripted PC. // - VoidStubUInt32 stub; + VoidStubPIC stub; switch (JSOp(*f.regs.pc)) { case JSOP_PROPINC: case JSOP_PROPDEC: @@ -2167,40 +2147,36 @@ ic::SetProp(VMFrame &f, uint32 index) case JSOP_NAMEDEC: case JSOP_INCNAME: case JSOP_DECNAME: - stub = SetPropDumb; + stub = STRICT_VARIANT(SetPropDumb); break; default: - stub = SetPropSlow; + stub = STRICT_VARIANT(SetPropSlow); break; } - SetPropCompiler cc(f, script, obj, pic, atom, stub); + SetPropCompiler cc(f, script, obj, *pic, pic->atom, stub); if (!cc.update()) { cc.disable("error"); THROW(); } Value rval = f.regs.sp[-1]; - stub(f, index); + stub(f, pic); } static void JS_FASTCALL -CallPropSlow(VMFrame &f, uint32 index) +CallPropSlow(VMFrame &f, ic::PICInfo *pic) { - JSScript *script = f.fp()->script(); - ic::PICInfo &pic = script->pics[index]; - stubs::CallProp(f, pic.atom); + stubs::CallProp(f, pic->atom); } void JS_FASTCALL -ic::CallProp(VMFrame &f, uint32 index) +ic::CallProp(VMFrame &f, ic::PICInfo *pic) { JSContext *cx = f.cx; JSFrameRegs ®s = f.regs; JSScript *script = f.fp()->script(); - ic::PICInfo &pic = script->pics[index]; - JSAtom *origAtom = pic.atom; Value lval; lval = regs.sp[-1]; @@ -2260,7 +2236,7 @@ ic::CallProp(VMFrame &f, uint32 index) * PropertyCache::test. */ jsid id; - id = ATOM_TO_JSID(origAtom); + id = ATOM_TO_JSID(pic->atom); regs.sp++; regs.sp[-1].setNull(); @@ -2298,7 +2274,7 @@ ic::CallProp(VMFrame &f, uint32 index) } } - GetPropCompiler cc(f, script, &objv.toObject(), pic, origAtom, CallPropSlow); + GetPropCompiler cc(f, script, &objv.toObject(), *pic, pic->atom, CallPropSlow); if (usePIC) { if (lval.isObject()) { if (!cc.update()) { @@ -2319,7 +2295,7 @@ ic::CallProp(VMFrame &f, uint32 index) #if JS_HAS_NO_SUCH_METHOD if (JS_UNLIKELY(rval.isUndefined())) { - regs.sp[-2].setString(ATOM_TO_STRING(origAtom)); + regs.sp[-2].setString(ATOM_TO_STRING(pic->atom)); if (!js_OnUnknownMethod(cx, regs.sp - 2)) THROW(); } @@ -2327,28 +2303,26 @@ ic::CallProp(VMFrame &f, uint32 index) } static void JS_FASTCALL -SlowName(VMFrame &f, uint32 index) +SlowName(VMFrame &f, ic::PICInfo *pic) { stubs::Name(f); } static void JS_FASTCALL -SlowXName(VMFrame &f, uint32 index) +SlowXName(VMFrame &f, ic::PICInfo *pic) { stubs::GetProp(f); } void JS_FASTCALL -ic::XName(VMFrame &f, uint32 index) +ic::XName(VMFrame &f, ic::PICInfo *pic) { JSScript *script = f.fp()->script(); - ic::PICInfo &pic = script->pics[index]; - JSAtom *atom = pic.atom; /* GETXPROP is guaranteed to have an object. */ JSObject *obj = &f.regs.sp[-1].toObject(); - ScopeNameCompiler cc(f, script, obj, pic, atom, SlowXName); + ScopeNameCompiler cc(f, script, obj, *pic, pic->atom, SlowXName); if (!cc.updateForXName()) { cc.disable("error"); @@ -2362,13 +2336,11 @@ ic::XName(VMFrame &f, uint32 index) } void JS_FASTCALL -ic::Name(VMFrame &f, uint32 index) +ic::Name(VMFrame &f, ic::PICInfo *pic) { JSScript *script = f.fp()->script(); - ic::PICInfo &pic = script->pics[index]; - JSAtom *atom = pic.atom; - ScopeNameCompiler cc(f, script, &f.fp()->scopeChain(), pic, atom, SlowName); + ScopeNameCompiler cc(f, script, &f.fp()->scopeChain(), *pic, pic->atom, SlowName); if (!cc.updateForName()) { cc.disable("error"); @@ -2382,19 +2354,17 @@ ic::Name(VMFrame &f, uint32 index) } static void JS_FASTCALL -SlowBindName(VMFrame &f, uint32 index) +SlowBindName(VMFrame &f, ic::PICInfo *pic) { stubs::BindName(f); } void JS_FASTCALL -ic::BindName(VMFrame &f, uint32 index) +ic::BindName(VMFrame &f, ic::PICInfo *pic) { JSScript *script = f.fp()->script(); - ic::PICInfo &pic = script->pics[index]; - JSAtom *atom = pic.atom; - BindNameCompiler cc(f, script, &f.fp()->scopeChain(), pic, atom, SlowBindName); + BindNameCompiler cc(f, script, &f.fp()->scopeChain(), *pic, pic->atom, SlowBindName); JSObject *obj = cc.update(); if (!obj) { @@ -2406,11 +2376,10 @@ ic::BindName(VMFrame &f, uint32 index) } void -ic::PurgePICs(JSContext *cx, JSScript *script) +JITScript::purgePICs() { - uint32 npics = script->jit->nPICs; - for (uint32 i = 0; i < npics; i++) { - ic::PICInfo &pic = script->pics[i]; + for (uint32 i = 0; i < nPICs; i++) { + ic::PICInfo &pic = pics[i]; switch (pic.kind) { case ic::PICInfo::SET: case ic::PICInfo::SETMETHOD: @@ -2438,5 +2407,14 @@ ic::PurgePICs(JSContext *cx, JSScript *script) } } +void +ic::PurgePICs(JSContext *cx, JSScript *script) +{ + if (script->jitNormal) + script->jitNormal->purgePICs(); + if (script->jitCtor) + script->jitCtor->purgePICs(); +} + #endif /* JS_POLYIC */ diff --git a/js/src/methodjit/PolyIC.h b/js/src/methodjit/PolyIC.h index 0edbe26321c8..9e3d63e03bef 100644 --- a/js/src/methodjit/PolyIC.h +++ b/js/src/methodjit/PolyIC.h @@ -352,14 +352,13 @@ struct PICInfo { }; void PurgePICs(JSContext *cx, JSScript *script); -void JS_FASTCALL GetProp(VMFrame &f, uint32 index); -void JS_FASTCALL GetElem(VMFrame &f, uint32 index); -void JS_FASTCALL SetProp(VMFrame &f, uint32 index); -void JS_FASTCALL CallProp(VMFrame &f, uint32 index); -void JS_FASTCALL Name(VMFrame &f, uint32 index); -void JS_FASTCALL XName(VMFrame &f, uint32 index); -void JS_FASTCALL BindName(VMFrame &f, uint32 index); -void JS_FASTCALL SetPropDumb(VMFrame &f, uint32 index); +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 *); } /* namespace ic */ } /* namespace mjit */ diff --git a/js/src/methodjit/Retcon.cpp b/js/src/methodjit/Retcon.cpp index 99526fb01f49..f5069e1f9cb1 100644 --- a/js/src/methodjit/Retcon.cpp +++ b/js/src/methodjit/Retcon.cpp @@ -73,13 +73,14 @@ AutoScriptRetrapper::untrap(jsbytecode *pc) } Recompiler::PatchableAddress -Recompiler::findPatch(void **location) +Recompiler::findPatch(JITScript *jit, void **location) { - for (uint32 i = 0; i < script->jit->nCallSites; i++) { - if (script->jit->callSites[i].codeOffset + (uint8*)script->jit->invoke == *location) { + uint8* codeStart = (uint8 *)jit->code.m_code.executableAddress(); + for (uint32 i = 0; i < jit->nCallSites; i++) { + if (jit->callSites[i].codeOffset + codeStart == *location) { PatchableAddress result; result.location = location; - result.callSite = script->jit->callSites[i]; + result.callSite = jit->callSites[i]; return result; } } @@ -116,17 +117,27 @@ Recompiler::Recompiler(JSContext *cx, JSScript *script) bool Recompiler::recompile() { - JS_ASSERT(script->ncode && script->ncode != JS_UNJITTABLE_METHOD); + JS_ASSERT(script->hasJITCode()); - Vector toPatch(cx); + Vector normalPatches(cx); + Vector ctorPatches(cx); /* Scan the stack, saving the ncode elements of the frames. */ - JSStackFrame *firstFrame = NULL; + JSStackFrame *firstCtorFrame = NULL; + JSStackFrame *firstNormalFrame = NULL; for (AllFramesIter i(cx); !i.done(); ++i) { - if (!firstFrame && i.fp()->maybeScript() == script) - firstFrame = i.fp(); - if (script->isValidJitCode(i.fp()->nativeReturnAddress())) { - if (!toPatch.append(findPatch(i.fp()->addressOfNativeReturnAddress()))) + if (!firstCtorFrame && i.fp()->maybeScript() == script && i.fp()->isConstructing()) + firstCtorFrame = i.fp(); + else if (!firstNormalFrame && i.fp()->maybeScript() == script && !i.fp()->isConstructing()) + firstNormalFrame = i.fp(); + void **addr = i.fp()->addressOfNativeReturnAddress(); + if (!*addr) + continue; + if (script->jitCtor && script->jitCtor->isValidCode(*addr)) { + if (!ctorPatches.append(findPatch(script->jitCtor, addr))) + return false; + } else if (script->jitNormal && script->jitNormal->isValidCode(*addr)) { + if (!normalPatches.append(findPatch(script->jitNormal, addr))) return false; } } @@ -136,29 +147,41 @@ Recompiler::recompile() f != NULL; f = f->previous) { - void **machineReturn = f->returnAddressLocation(); - if (script->isValidJitCode(*machineReturn)) { - if (!toPatch.append(findPatch(machineReturn))) + void **addr = f->returnAddressLocation(); + if (script->jitCtor && script->jitCtor->isValidCode(*addr)) { + if (!ctorPatches.append(findPatch(script->jitCtor, addr))) + return false; + } else if (script->jitNormal && script->jitNormal->isValidCode(*addr)) { + if (!normalPatches.append(findPatch(script->jitNormal, addr))) return false; } } ReleaseScriptCode(cx, script); - /* No need to actually compile or fixup if no frames on the stack */ - if (!firstFrame) - return true; + if (normalPatches.length() && !recompile(firstNormalFrame, normalPatches)) + return false; + if (ctorPatches.length() && !recompile(firstCtorFrame, ctorPatches)) + return false; + + return true; +} + +bool +Recompiler::recompile(JSStackFrame *fp, Vector &patches) +{ /* If we get this far, the script is live, and we better be safe to re-jit. */ JS_ASSERT(cx->compartment->debugMode); + JS_ASSERT(fp); - Compiler c(cx, script, firstFrame->maybeFun(), &firstFrame->scopeChain()); - if (c.Compile() != Compile_Okay) + Compiler c(cx, fp); + if (c.compile() != Compile_Okay) return false; /* Perform the earlier scanned patches */ - for (uint32 i = 0; i < toPatch.length(); i++) - applyPatch(c, toPatch[i]); + for (uint32 i = 0; i < patches.length(); i++) + applyPatch(c, patches[i]); return true; } diff --git a/js/src/methodjit/Retcon.h b/js/src/methodjit/Retcon.h index 8280329a8fe0..99050c0e5eed 100644 --- a/js/src/methodjit/Retcon.h +++ b/js/src/methodjit/Retcon.h @@ -97,8 +97,9 @@ private: JSContext *cx; JSScript *script; - PatchableAddress findPatch(void **location); + PatchableAddress findPatch(JITScript *jit, void **location); void applyPatch(Compiler& c, PatchableAddress& toPatch); + bool recompile(JSStackFrame *fp, Vector &patches); }; } /* namespace mjit */ diff --git a/js/src/methodjit/StubCalls-inl.h b/js/src/methodjit/StubCalls-inl.h index 55b3513ca9ce..2b2844a2ea9f 100644 --- a/js/src/methodjit/StubCalls-inl.h +++ b/js/src/methodjit/StubCalls-inl.h @@ -99,7 +99,6 @@ ReportAtomNotDefined(JSContext *cx, JSAtom *atom) } \ JS_END_MACRO - }} #endif /* jslogic_h__ */ diff --git a/js/src/methodjit/StubCalls.cpp b/js/src/methodjit/StubCalls.cpp index 5850c36e46a1..a0231f1ed997 100644 --- a/js/src/methodjit/StubCalls.cpp +++ b/js/src/methodjit/StubCalls.cpp @@ -281,6 +281,22 @@ stubs::SetName(VMFrame &f, JSAtom *origAtom) template void JS_FASTCALL stubs::SetName(VMFrame &f, JSAtom *origAtom); template void JS_FASTCALL stubs::SetName(VMFrame &f, JSAtom *origAtom); +template +void JS_FASTCALL +stubs::SetPropNoCache(VMFrame &f, JSAtom *atom) +{ + JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-2]); + if (!obj) + THROW(); + Value rval = f.regs.sp[-1]; + if (!obj->setProperty(f.cx, ATOM_TO_JSID(atom), &f.regs.sp[-1], strict)) + THROW(); + f.regs.sp[-2] = rval; +} + +template void JS_FASTCALL stubs::SetPropNoCache(VMFrame &f, JSAtom *origAtom); +template void JS_FASTCALL stubs::SetPropNoCache(VMFrame &f, JSAtom *origAtom); + template void JS_FASTCALL stubs::SetGlobalNameDumb(VMFrame &f, JSAtom *atom) @@ -2517,14 +2533,15 @@ stubs::LookupSwitch(VMFrame &f, jsbytecode *pc) { jsbytecode *jpc = pc; JSScript *script = f.fp()->script(); + JITScript *jit = script->getJIT(f.fp()->isConstructing()); /* This is correct because the compiler adjusts the stack beforehand. */ Value lval = f.regs.sp[-1]; if (!lval.isPrimitive()) { ptrdiff_t offs = (pc + GET_JUMP_OFFSET(pc)) - script->code; - JS_ASSERT(script->nmap[offs]); - return script->nmap[offs]; + JS_ASSERT(jit->nmap[offs]); + return jit->nmap[offs]; } JS_ASSERT(pc[0] == JSOP_LOOKUPSWITCH); @@ -2544,8 +2561,8 @@ stubs::LookupSwitch(VMFrame &f, jsbytecode *pc) JSString *rhs = rval.toString(); if (rhs == str || js_EqualStrings(str, rhs)) { ptrdiff_t offs = (jpc + GET_JUMP_OFFSET(pc)) - script->code; - JS_ASSERT(script->nmap[offs]); - return script->nmap[offs]; + JS_ASSERT(jit->nmap[offs]); + return jit->nmap[offs]; } } pc += JUMP_OFFSET_LEN; @@ -2557,8 +2574,8 @@ stubs::LookupSwitch(VMFrame &f, jsbytecode *pc) pc += INDEX_LEN; if (rval.isNumber() && d == rval.toNumber()) { ptrdiff_t offs = (jpc + GET_JUMP_OFFSET(pc)) - script->code; - JS_ASSERT(script->nmap[offs]); - return script->nmap[offs]; + JS_ASSERT(jit->nmap[offs]); + return jit->nmap[offs]; } pc += JUMP_OFFSET_LEN; } @@ -2568,16 +2585,16 @@ stubs::LookupSwitch(VMFrame &f, jsbytecode *pc) pc += INDEX_LEN; if (lval == rval) { ptrdiff_t offs = (jpc + GET_JUMP_OFFSET(pc)) - script->code; - JS_ASSERT(script->nmap[offs]); - return script->nmap[offs]; + JS_ASSERT(jit->nmap[offs]); + return jit->nmap[offs]; } pc += JUMP_OFFSET_LEN; } } ptrdiff_t offs = (jpc + GET_JUMP_OFFSET(jpc)) - script->code; - JS_ASSERT(script->nmap[offs]); - return script->nmap[offs]; + JS_ASSERT(jit->nmap[offs]); + return jit->nmap[offs]; } void * JS_FASTCALL @@ -2586,6 +2603,7 @@ stubs::TableSwitch(VMFrame &f, jsbytecode *origPc) jsbytecode * const originalPC = origPc; jsbytecode *pc = originalPC; JSScript *script = f.fp()->script(); + JITScript *jit = script->getJIT(f.fp()->isConstructing()); uint32 jumpOffset = GET_JUMP_OFFSET(pc); pc += JUMP_OFFSET_LEN; @@ -2625,8 +2643,8 @@ stubs::TableSwitch(VMFrame &f, jsbytecode *origPc) finally: /* Provide the native address. */ ptrdiff_t offset = (originalPC + jumpOffset) - script->code; - JS_ASSERT(script->nmap[offset]); - return script->nmap[offset]; + JS_ASSERT(jit->nmap[offset]); + return jit->nmap[offset]; } void JS_FASTCALL diff --git a/js/src/methodjit/StubCalls.h b/js/src/methodjit/StubCalls.h index fe49e1ba1106..da104367cbcb 100644 --- a/js/src/methodjit/StubCalls.h +++ b/js/src/methodjit/StubCalls.h @@ -98,14 +98,14 @@ struct UncachedCallResult { void UncachedCallHelper(VMFrame &f, uint32 argc, UncachedCallResult *ucr); void UncachedNewHelper(VMFrame &f, uint32 argc, UncachedCallResult *ucr); -JSBool JS_FASTCALL NewObject(VMFrame &f, uint32 argc); +void JS_FASTCALL CreateThis(VMFrame &f, JSObject *proto); void JS_FASTCALL Throw(VMFrame &f); void JS_FASTCALL PutCallObject(VMFrame &f); void JS_FASTCALL PutActivationObjects(VMFrame &f); void JS_FASTCALL GetCallObject(VMFrame &f); void JS_FASTCALL WrapPrimitiveThis(VMFrame &f); #if JS_MONOIC -void * JS_FASTCALL InvokeTracer(VMFrame &f, uint32 index); +void * JS_FASTCALL InvokeTracer(VMFrame &f, ic::MICInfo *mic); #else void * JS_FASTCALL InvokeTracer(VMFrame &f); #endif @@ -116,6 +116,7 @@ void * JS_FASTCALL TableSwitch(VMFrame &f, jsbytecode *origPc); void JS_FASTCALL BindName(VMFrame &f); JSObject * JS_FASTCALL BindGlobalName(VMFrame &f); template void JS_FASTCALL SetName(VMFrame &f, JSAtom *atom); +template void JS_FASTCALL SetPropNoCache(VMFrame &f, JSAtom *atom); template void JS_FASTCALL SetGlobalName(VMFrame &f, JSAtom *atom); template void JS_FASTCALL SetGlobalNameDumb(VMFrame &f, JSAtom *atom); void JS_FASTCALL Name(VMFrame &f); @@ -226,11 +227,6 @@ inline FuncPtr FunctionTemplateConditional(bool cond, FuncPtr a, FuncPtr b) { return cond ? a : b; } -/* Return f if the script is strict mode code, f otherwise. */ -#define STRICT_VARIANT(f) \ - (FunctionTemplateConditional(script->strictModeCode, \ - f, f)) - }} /* namespace stubs,mjit,js */ extern "C" void * diff --git a/js/src/methodjit/StubCompiler.h b/js/src/methodjit/StubCompiler.h index bdfaff63bd70..6d945312c984 100644 --- a/js/src/methodjit/StubCompiler.h +++ b/js/src/methodjit/StubCompiler.h @@ -125,6 +125,11 @@ class StubCompiler STUB_CALL_TYPE(BoolStub); STUB_CALL_TYPE(VoidStubAtom); STUB_CALL_TYPE(VoidStubPC); + STUB_CALL_TYPE(VoidStubMIC); + STUB_CALL_TYPE(VoidPtrStubMIC); + STUB_CALL_TYPE(VoidStubPIC); + STUB_CALL_TYPE(VoidStubCallIC); + STUB_CALL_TYPE(VoidPtrStubCallIC); #undef STUB_CALL_TYPE diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index dea6d8c0c79d..005675639ba8 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -1196,7 +1196,7 @@ AssertJit(JSContext *cx, uintN argc, jsval *vp) { #ifdef JS_METHODJIT if (JS_GetOptions(cx) & JSOPTION_METHODJIT) { - if (cx->fp()->script()->nmap == NULL) { + if (!cx->fp()->script()->getJIT(cx->fp()->isConstructing())) { JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_ASSERT_JIT_FAILED); return JS_FALSE; } diff --git a/js/src/trace-test/tests/jaeger/bug563000/simple-trap-2.js b/js/src/trace-test/tests/jaeger/bug563000/simple-trap-2.js index a2b8d32d76de..db7e4b82b2a0 100644 --- a/js/src/trace-test/tests/jaeger/bug563000/simple-trap-2.js +++ b/js/src/trace-test/tests/jaeger/bug563000/simple-trap-2.js @@ -4,7 +4,7 @@ function main() { x = "failure"; } function success() { x = "success"; } /* The JSOP_STOP in a. */ -trap(main, 6, "success()"); +trap(main, 7, "success()"); main(); assertEq(x, "success"); diff --git a/js/src/trace-test/tests/jaeger/bug563000/trap-force-return-1.js b/js/src/trace-test/tests/jaeger/bug563000/trap-force-return-1.js index 1d19aeebeb11..fcb2eab3ef1b 100644 --- a/js/src/trace-test/tests/jaeger/bug563000/trap-force-return-1.js +++ b/js/src/trace-test/tests/jaeger/bug563000/trap-force-return-1.js @@ -3,5 +3,5 @@ function main() { return "failure"; } /* JSOP_RETURN in main. */ -trap(main, 3, "'success'"); +trap(main, 4, "'success'"); assertEq(main(), "success"); diff --git a/js/src/trace-test/tests/jaeger/bug563000/trap-own-callsite.js b/js/src/trace-test/tests/jaeger/bug563000/trap-own-callsite.js index 18561be47267..16a5a11c445e 100644 --- a/js/src/trace-test/tests/jaeger/bug563000/trap-own-callsite.js +++ b/js/src/trace-test/tests/jaeger/bug563000/trap-own-callsite.js @@ -3,7 +3,7 @@ x = "notset"; function myparent(nested) { if (nested) { /* myparent call in myparent. */ - trap(myparent, 37, "failure()"); + trap(myparent, 38, "failure()"); } else { x = "success"; myparent(true); diff --git a/js/src/trace-test/tests/jaeger/bug563000/trap-self-as-parent.js b/js/src/trace-test/tests/jaeger/bug563000/trap-self-as-parent.js index eefc0bb86005..6c302163e197 100644 --- a/js/src/trace-test/tests/jaeger/bug563000/trap-self-as-parent.js +++ b/js/src/trace-test/tests/jaeger/bug563000/trap-self-as-parent.js @@ -4,7 +4,7 @@ x = "notset"; function myparent(nested) { if (nested) { /* noop call in myparent */ - trap(myparent, 48, "success()"); + trap(myparent, 49, "success()"); } else { myparent(true); x = "failure"; diff --git a/js/src/trace-test/tests/jaeger/bug563000/trap-self-from-trap.js b/js/src/trace-test/tests/jaeger/bug563000/trap-self-from-trap.js index 5cecf58cd5ad..b01d5ac3b12c 100644 --- a/js/src/trace-test/tests/jaeger/bug563000/trap-self-from-trap.js +++ b/js/src/trace-test/tests/jaeger/bug563000/trap-self-from-trap.js @@ -6,14 +6,14 @@ function doNothing() { } function myparent(nested) { if (nested) { /* JSOP_CALL to doNothing in myparent with nested = true. */ - trap(myparent, 24, "success()"); + trap(myparent, 25, "success()"); doNothing(); } else { doNothing(); } } /* JSOP_CALL to doNothing in myparent with nested = false. */ -trap(myparent, 34, "myparent(true)"); +trap(myparent, 35, "myparent(true)"); function success() { x = "success";