зеркало из https://github.com/mozilla/gecko-dev.git
Bug 826148 - Part 1: JSFunction flag and interpreter changes (r=luke)
This commit is contained in:
Родитель
f8ca69fdcc
Коммит
23f0319215
|
@ -242,6 +242,58 @@ JSRuntime::createJaegerRuntime(JSContext *cx)
|
|||
}
|
||||
#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)
|
||||
{
|
||||
|
|
|
@ -58,6 +58,39 @@ 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
|
||||
{
|
||||
|
|
|
@ -650,6 +650,7 @@ JSCompartment::sweep(FreeOp *fop, bool releaseTypes)
|
|||
sweepNewTypeObjectTable(newTypeObjects);
|
||||
sweepNewTypeObjectTable(lazyTypeObjects);
|
||||
sweepBreakpoints(fop);
|
||||
sweepCallsiteClones();
|
||||
|
||||
if (global_ && IsObjectAboutToBeFinalized(global_.unsafeGet()))
|
||||
global_ = NULL;
|
||||
|
|
|
@ -349,6 +349,14 @@ struct JSCompartment : private JS::shadow::Compartment, public js::gc::GraphNode
|
|||
|
||||
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
|
||||
|
|
|
@ -84,6 +84,13 @@ fun_getProperty(JSContext *cx, HandleObject obj_, HandleId id, MutableHandleValu
|
|||
}
|
||||
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
|
||||
|
@ -1514,29 +1521,8 @@ js_CloneFunctionObject(JSContext *cx, HandleFunction fun, HandleObject parent,
|
|||
* (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;
|
||||
}
|
||||
|
|
|
@ -44,6 +44,13 @@ class JSFunction : public JSObject
|
|||
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
|
||||
|
@ -100,6 +107,11 @@ class JSFunction : public JSObject
|
|||
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();
|
||||
|
@ -141,6 +153,10 @@ class JSFunction : public JSObject
|
|||
flags |= SELF_HOSTED_CTOR;
|
||||
}
|
||||
|
||||
void setIsCloneAtCallsite() {
|
||||
flags |= CALLSITE_CLONE;
|
||||
}
|
||||
|
||||
void setIsFunctionPrototype() {
|
||||
JS_ASSERT(!isFunctionPrototype());
|
||||
flags |= IS_FUN_PROTO;
|
||||
|
|
|
@ -2359,8 +2359,25 @@ BEGIN_CASE(JSOP_FUNCALL)
|
|||
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;
|
||||
|
@ -2380,9 +2397,7 @@ BEGIN_CASE(JSOP_FUNCALL)
|
|||
|
||||
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;
|
||||
|
||||
|
|
|
@ -2343,6 +2343,37 @@ js::CloneScript(JSContext *cx, HandleObject enclosingScope, HandleFunction fun,
|
|||
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()
|
||||
{
|
||||
|
|
|
@ -1300,6 +1300,9 @@ CurrentScriptFileLineOrigin(JSContext *cx, unsigned *linenop, LineOption = NOT_C
|
|||
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
|
||||
|
|
Загрузка…
Ссылка в новой задаче