☠☠ backed out by 6cda85d6e4f6 ☠ ☠ | |
author | Shu-yu Guo <shu@rfrn.org> |
Thu, 10 Jan 2013 13:04:04 -0800 | |
changeset 118502 | b659dc17b1646aa6ecb3275afab6829b6080c7f1 |
parent 118501 | cf76976f7316d13a00ed9a5c3ce038cc6f758069 |
child 118503 | 57bf735f3e186a1501fe2fe01b46bc1ef41d0ca2 |
push id | 24166 |
push user | Ms2ger@gmail.com |
push date | Fri, 11 Jan 2013 13:57:41 +0000 |
treeherder | mozilla-central@63c4b0f66a0c [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | luke |
bugs | 826148 |
milestone | 21.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
js/src/jscntxt.cpp | file | annotate | diff | comparison | revisions | |
js/src/jscntxt.h | file | annotate | diff | comparison | revisions | |
js/src/jscompartment.cpp | file | annotate | diff | comparison | revisions | |
js/src/jscompartment.h | file | annotate | diff | comparison | revisions | |
js/src/jsfun.cpp | file | annotate | diff | comparison | revisions | |
js/src/jsfun.h | file | annotate | diff | comparison | revisions | |
js/src/jsinterp.cpp | file | annotate | diff | comparison | revisions | |
js/src/jsscript.cpp | file | annotate | diff | comparison | revisions | |
js/src/jsscript.h | file | annotate | diff | comparison | revisions |
--- a/js/src/jscntxt.cpp +++ b/js/src/jscntxt.cpp @@ -237,16 +237,68 @@ JSRuntime::createJaegerRuntime(JSContext return NULL; } jaegerRuntime_ = jr; return jaegerRuntime_; } #endif +void +JSCompartment::sweepCallsiteClones() +{ + if (callsiteClones.initialized()) { + for (CallsiteCloneTable::Enum e(callsiteClones); !e.empty(); e.popFront()) { + CallsiteCloneKey key = e.front().key; + JSFunction *fun = e.front().value; + if (!key.script->isMarked() || !fun->isMarked()) + e.removeFront(); + } + } +} + +RawFunction +js::CloneFunctionAtCallsite(JSContext *cx, HandleFunction fun, HandleScript script, jsbytecode *pc) +{ + JS_ASSERT(cx->typeInferenceEnabled()); + JS_ASSERT(fun->isCloneAtCallsite()); + JS_ASSERT(types::UseNewTypeForClone(fun)); + JS_ASSERT(!fun->nonLazyScript()->enclosingStaticScope()); + + typedef CallsiteCloneKey Key; + typedef CallsiteCloneTable Table; + + Table &table = cx->compartment->callsiteClones; + if (!table.initialized() && !table.init()) + return NULL; + + Key key; + key.script = script; + key.offset = pc - script->code; + key.original = fun; + + Table::AddPtr p = table.lookupForAdd(key); + if (p) + return p->value; + + RootedObject parent(cx, fun->environment()); + RootedFunction clone(cx, CloneFunctionObject(cx, fun, parent, + JSFunction::ExtendedFinalizeKind)); + if (!clone) + return NULL; + + // Store a link back to the original for function.caller. + clone->setExtendedSlot(0, ObjectValue(*fun)); + + if (!table.add(p, key, clone.get())) + return NULL; + + return clone; +} + JSContext * js::NewContext(JSRuntime *rt, size_t stackChunkSize) { JS_AbortIfWrongThread(rt); JSContext *cx = js_new<JSContext>(rt); if (!cx) return NULL;
--- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -53,16 +53,49 @@ js_ReportOutOfMemory(JSContext *cx); extern void js_ReportAllocationOverflow(JSContext *cx); namespace js { typedef HashSet<JSObject *> ObjectSet; +struct CallsiteCloneKey { + /* The original function that we are cloning. */ + JSFunction *original; + + /* The script of the call. */ + JSScript *script; + + /* The offset of the call. */ + uint32_t offset; + + CallsiteCloneKey() { PodZero(this); } + + typedef CallsiteCloneKey Lookup; + + static inline uint32_t hash(CallsiteCloneKey key) { + return uint32_t(size_t(key.script->code + key.offset) ^ size_t(key.original)); + } + + static inline bool match(const CallsiteCloneKey &a, const CallsiteCloneKey &b) { + return a.script == b.script && a.offset == b.offset && a.original == b.original; + } +}; + +typedef HashMap<CallsiteCloneKey, + ReadBarriered<JSFunction>, + CallsiteCloneKey, + SystemAllocPolicy> CallsiteCloneTable; + +RawFunction CloneFunctionAtCallsite(JSContext *cx, HandleFunction fun, + HandleScript script, jsbytecode *pc); + +typedef HashSet<JSObject *> ObjectSet; + /* Detects cycles when traversing an object graph. */ class AutoCycleDetector { JSContext *cx; JSObject *obj; bool cyclic; uint32_t hashsetGenerationAtInit; ObjectSet::AddPtr hashsetAddPointer;
--- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -645,16 +645,17 @@ JSCompartment::sweep(FreeOp *fop, bool r /* Remove dead references held weakly by the compartment. */ sweepBaseShapeTable(); sweepInitialShapeTable(); sweepNewTypeObjectTable(newTypeObjects); sweepNewTypeObjectTable(lazyTypeObjects); sweepBreakpoints(fop); + sweepCallsiteClones(); if (global_ && IsObjectAboutToBeFinalized(global_.unsafeGet())) global_ = NULL; #ifdef JS_ION if (ionCompartment_) ionCompartment_->sweep(fop); #endif
--- a/js/src/jscompartment.h +++ b/js/src/jscompartment.h @@ -345,16 +345,24 @@ struct JSCompartment : private JS::shado void sweepNewTypeObjectTable(js::types::TypeObjectSet &table); js::types::TypeObject *getNewType(JSContext *cx, js::TaggedProto proto, JSFunction *fun = NULL, bool isDOM = false); js::types::TypeObject *getLazyType(JSContext *cx, js::Handle<js::TaggedProto> proto); /* + * Hash table of all manually call site-cloned functions from within + * self-hosted code. Cloning according to call site provides extra + * sensitivity for type specialization and inlining. + */ + js::CallsiteCloneTable callsiteClones; + void sweepCallsiteClones(); + + /* * Keeps track of the total number of malloc bytes connected to a * compartment's GC things. This counter should be used in preference to * gcMallocBytes. These counters affect collection in the same way as * gcBytes and gcTriggerBytes. */ size_t gcMallocAndFreeBytes; size_t gcTriggerMallocAndFreeBytes;
--- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -80,16 +80,23 @@ fun_getProperty(JSContext *cx, HandleObj if (!JSObject::getProto(cx, obj, &obj)) return false; if (!obj) return true; } RootedFunction fun(cx, obj->toFunction()); /* + * Callsite clones should never escape to script, so get the original + * function. + */ + if (fun->isCallsiteClone()) + fun = fun->getExtendedSlot(0).toObject().toFunction(); + + /* * Mark the function's script as uninlineable, to expand any of its * frames on the stack before we go looking for them. This allows the * below walk to only check each explicit frame rather than needing to * check any calls that were inlined. */ if (fun->isInterpreted()) { JSFunction::getOrCreateScript(cx, fun)->uninlineable = true; MarkTypeObjectFlags(cx, fun, OBJECT_FLAG_UNINLINEABLE); @@ -1509,39 +1516,18 @@ js_CloneFunctionObject(JSContext *cx, Ha return NULL; /* * Across compartments we have to clone the script for interpreted * functions. Cross-compartment cloning only happens via JSAPI * (JS_CloneFunctionObject) which dynamically ensures that 'script' has * no enclosing lexical scope (only the global scope). */ - if (clone->isInterpreted()) { - RootedScript script(cx, clone->nonLazyScript()); - JS_ASSERT(script->compartment() == fun->compartment()); - JS_ASSERT_IF(script->compartment() != cx->compartment, - !script->enclosingStaticScope()); - - RootedObject scope(cx, script->enclosingStaticScope()); - - clone->mutableScript().init(NULL); - - RootedScript cscript(cx, CloneScript(cx, scope, clone, script)); - if (!cscript) - return NULL; - - clone->setScript(cscript); - cscript->setFunction(clone); - - GlobalObject *global = script->compileAndGo ? &script->global() : NULL; - - script = clone->nonLazyScript(); - CallNewScriptHook(cx, script, clone); - Debugger::onNewScript(cx, script, global); - } + if (clone->isInterpreted() && !CloneFunctionScript(cx, fun, clone)) + return NULL; } return clone; } JSFunction * js_DefineFunction(JSContext *cx, HandleObject obj, HandleId id, Native native, unsigned nargs, unsigned flags, AllocKind kind) {
--- a/js/src/jsfun.h +++ b/js/src/jsfun.h @@ -39,16 +39,23 @@ class JSFunction : public JSObject SELF_HOSTED = 0x0100, /* function is self-hosted builtin and must not be decompilable nor constructible. */ SELF_HOSTED_CTOR = 0x0200, /* function is self-hosted builtin constructor and must be constructible but not decompilable. */ HAS_REST = 0x0400, /* function has a rest (...) parameter */ HAS_DEFAULTS = 0x0800, /* function has at least one default parameter */ INTERPRETED_LAZY = 0x1000, /* function is interpreted but doesn't have a script yet */ + /* + * Function is cloned anew at each callsite. This is temporarily + * needed for ParallelArray selfhosted code until type information can + * be made context sensitive. See discussion in bug 826148. + */ + CALLSITE_CLONE = 0x2000, + /* Derived Flags values for convenience: */ NATIVE_FUN = 0, INTERPRETED_LAMBDA = INTERPRETED | LAMBDA }; static void staticAsserts() { JS_STATIC_ASSERT(INTERPRETED == JS_FUNCTION_INTERPRETED_BIT); MOZ_STATIC_ASSERT(sizeof(JSFunction) == sizeof(js::shadow::Function), @@ -95,16 +102,21 @@ class JSFunction : public JSObject bool isExprClosure() const { return flags & EXPR_CLOSURE; } bool hasGuessedAtom() const { return flags & HAS_GUESSED_ATOM; } bool isLambda() const { return flags & LAMBDA; } bool isSelfHostedBuiltin() const { return flags & SELF_HOSTED; } bool isSelfHostedConstructor() const { return flags & SELF_HOSTED_CTOR; } bool hasRest() const { return flags & HAS_REST; } bool hasDefaults() const { return flags & HAS_DEFAULTS; } + /* Original functions that should be cloned are not extended. */ + bool isCloneAtCallsite() const { return (flags & CALLSITE_CLONE) && !isExtended(); } + /* Cloned functions keep a backlink to the original in extended slot 0. */ + bool isCallsiteClone() const { return (flags & CALLSITE_CLONE) && isExtended(); } + /* Compound attributes: */ bool isBuiltin() const { return isNative() || isSelfHostedBuiltin(); } bool isInterpretedConstructor() const { return isInterpreted() && !isFunctionPrototype() && (!isSelfHostedBuiltin() || isSelfHostedConstructor()); } @@ -136,16 +148,20 @@ class JSFunction : public JSObject flags |= SELF_HOSTED; } void setIsSelfHostedConstructor() { JS_ASSERT(!isSelfHostedConstructor()); flags |= SELF_HOSTED_CTOR; } + void setIsCloneAtCallsite() { + flags |= CALLSITE_CLONE; + } + void setIsFunctionPrototype() { JS_ASSERT(!isFunctionPrototype()); flags |= IS_FUN_PROTO; } void setIsHeavyweight() { flags |= HEAVYWEIGHT; }
--- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -2354,18 +2354,35 @@ BEGIN_CASE(JSOP_FUNCALL) if (regs.fp()->hasPushedSPSFrame()) cx->runtime->spsProfiler.updatePC(script, regs.pc); JS_ASSERT(regs.stackDepth() >= 2 + GET_ARGC(regs.pc)); CallArgs args = CallArgsFromSp(GET_ARGC(regs.pc), regs.sp); bool construct = (*regs.pc == JSOP_NEW); RootedFunction &fun = rootFunction0; + bool isFunction = IsFunctionObject(args.calleev(), fun.address()); + + /* + * Some builtins are marked as clone-at-callsite to increase precision of + * TI and JITs. + */ + if (isFunction) { + if (fun->isInterpretedLazy() && !JSFunction::getOrCreateScript(cx, fun)) + goto error; + if (cx->typeInferenceEnabled() && fun->isCloneAtCallsite()) { + fun = CloneFunctionAtCallsite(cx, fun, script, regs.pc); + if (!fun) + goto error; + args.setCallee(ObjectValue(*fun)); + } + } + /* Don't bother trying to fast-path calls to scripted non-constructors. */ - if (!IsFunctionObject(args.calleev(), fun.address()) || !fun->isInterpretedConstructor()) { + if (!isFunction || !fun->isInterpretedConstructor()) { if (construct) { if (!InvokeConstructorKernel(cx, args)) goto error; } else { if (!InvokeKernel(cx, args)) goto error; } Value *newsp = args.spAfterCall(); @@ -2375,19 +2392,17 @@ BEGIN_CASE(JSOP_FUNCALL) DO_NEXT_OP(len); } if (!TypeMonitorCall(cx, args, construct)) goto error; InitialFrameFlags initial = construct ? INITIAL_CONSTRUCT : INITIAL_NONE; bool newType = cx->typeInferenceEnabled() && UseNewType(cx, script, regs.pc); - RootedScript funScript(cx, JSFunction::getOrCreateScript(cx, fun)); - if (!funScript) - goto error; + RootedScript funScript(cx, fun->nonLazyScript()); if (!cx->stack.pushInlineFrame(cx, regs, args, *fun, funScript, initial)) goto error; SET_SCRIPT(regs.fp()->script()); #ifdef JS_METHODJIT script->resetLoopCount(); #endif
--- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -2338,16 +2338,47 @@ js::CloneScript(JSContext *cx, HandleObj vector[i].init(regexps[i]); } if (ntrynotes != 0) dst->trynotes()->vector = Rebase<JSTryNote>(dst, src, src->trynotes()->vector); return dst; } +bool +js::CloneFunctionScript(JSContext *cx, HandleFunction original, HandleFunction clone) +{ + JS_ASSERT(clone->isInterpreted()); + + RootedScript script(cx, clone->nonLazyScript()); + JS_ASSERT(script); + JS_ASSERT(script->compartment() == original->compartment()); + JS_ASSERT_IF(script->compartment() != cx->compartment, + !script->enclosingStaticScope()); + + RootedObject scope(cx, script->enclosingStaticScope()); + + clone->mutableScript().init(NULL); + + RawScript cscript = CloneScript(cx, scope, clone, script); + if (!cscript) + return false; + + clone->setScript(cscript); + cscript->setFunction(clone); + + GlobalObject *global = script->compileAndGo ? &script->global() : NULL; + + script = clone->nonLazyScript(); + CallNewScriptHook(cx, script, clone); + Debugger::onNewScript(cx, script, global); + + return true; +} + DebugScript * JSScript::debugScript() { JS_ASSERT(hasDebugScript); DebugScriptMap *map = compartment()->debugScriptMap; JS_ASSERT(map); DebugScriptMap::Ptr p = map->lookup(this); JS_ASSERT(p);
--- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -1295,16 +1295,19 @@ enum LineOption { }; inline void CurrentScriptFileLineOrigin(JSContext *cx, unsigned *linenop, LineOption = NOT_CALLED_FROM_JSOP_EVAL); extern UnrootedScript CloneScript(JSContext *cx, HandleObject enclosingScope, HandleFunction fun, HandleScript script); +bool +CloneFunctionScript(JSContext *cx, HandleFunction original, HandleFunction clone); + /* * NB: after a successful XDR_DECODE, XDRScript callers must do any required * subsequent set-up of owning function or script object and then call * js_CallNewScriptHook. */ template<XDRMode mode> bool XDRScript(XDRState<mode> *xdr, HandleObject enclosingScope, HandleScript enclosingScript,