зеркало из 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
|
#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 *
|
JSContext *
|
||||||
js::NewContext(JSRuntime *rt, size_t stackChunkSize)
|
js::NewContext(JSRuntime *rt, size_t stackChunkSize)
|
||||||
{
|
{
|
||||||
|
|
|
@ -58,6 +58,39 @@ namespace js {
|
||||||
|
|
||||||
typedef HashSet<JSObject *> ObjectSet;
|
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. */
|
/* Detects cycles when traversing an object graph. */
|
||||||
class AutoCycleDetector
|
class AutoCycleDetector
|
||||||
{
|
{
|
||||||
|
|
|
@ -650,6 +650,7 @@ JSCompartment::sweep(FreeOp *fop, bool releaseTypes)
|
||||||
sweepNewTypeObjectTable(newTypeObjects);
|
sweepNewTypeObjectTable(newTypeObjects);
|
||||||
sweepNewTypeObjectTable(lazyTypeObjects);
|
sweepNewTypeObjectTable(lazyTypeObjects);
|
||||||
sweepBreakpoints(fop);
|
sweepBreakpoints(fop);
|
||||||
|
sweepCallsiteClones();
|
||||||
|
|
||||||
if (global_ && IsObjectAboutToBeFinalized(global_.unsafeGet()))
|
if (global_ && IsObjectAboutToBeFinalized(global_.unsafeGet()))
|
||||||
global_ = NULL;
|
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);
|
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
|
* Keeps track of the total number of malloc bytes connected to a
|
||||||
* compartment's GC things. This counter should be used in preference to
|
* 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());
|
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
|
* 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
|
* 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
|
* (JS_CloneFunctionObject) which dynamically ensures that 'script' has
|
||||||
* no enclosing lexical scope (only the global scope).
|
* no enclosing lexical scope (only the global scope).
|
||||||
*/
|
*/
|
||||||
if (clone->isInterpreted()) {
|
if (clone->isInterpreted() && !CloneFunctionScript(cx, fun, clone))
|
||||||
RootedScript script(cx, clone->nonLazyScript());
|
return NULL;
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return clone;
|
return clone;
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,6 +44,13 @@ class JSFunction : public JSObject
|
||||||
HAS_DEFAULTS = 0x0800, /* function has at least one default parameter */
|
HAS_DEFAULTS = 0x0800, /* function has at least one default parameter */
|
||||||
INTERPRETED_LAZY = 0x1000, /* function is interpreted but doesn't have a script yet */
|
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: */
|
/* Derived Flags values for convenience: */
|
||||||
NATIVE_FUN = 0,
|
NATIVE_FUN = 0,
|
||||||
INTERPRETED_LAMBDA = INTERPRETED | LAMBDA
|
INTERPRETED_LAMBDA = INTERPRETED | LAMBDA
|
||||||
|
@ -100,6 +107,11 @@ class JSFunction : public JSObject
|
||||||
bool hasRest() const { return flags & HAS_REST; }
|
bool hasRest() const { return flags & HAS_REST; }
|
||||||
bool hasDefaults() const { return flags & HAS_DEFAULTS; }
|
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: */
|
/* Compound attributes: */
|
||||||
bool isBuiltin() const {
|
bool isBuiltin() const {
|
||||||
return isNative() || isSelfHostedBuiltin();
|
return isNative() || isSelfHostedBuiltin();
|
||||||
|
@ -141,6 +153,10 @@ class JSFunction : public JSObject
|
||||||
flags |= SELF_HOSTED_CTOR;
|
flags |= SELF_HOSTED_CTOR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setIsCloneAtCallsite() {
|
||||||
|
flags |= CALLSITE_CLONE;
|
||||||
|
}
|
||||||
|
|
||||||
void setIsFunctionPrototype() {
|
void setIsFunctionPrototype() {
|
||||||
JS_ASSERT(!isFunctionPrototype());
|
JS_ASSERT(!isFunctionPrototype());
|
||||||
flags |= IS_FUN_PROTO;
|
flags |= IS_FUN_PROTO;
|
||||||
|
|
|
@ -2359,8 +2359,25 @@ BEGIN_CASE(JSOP_FUNCALL)
|
||||||
bool construct = (*regs.pc == JSOP_NEW);
|
bool construct = (*regs.pc == JSOP_NEW);
|
||||||
|
|
||||||
RootedFunction &fun = rootFunction0;
|
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. */
|
/* 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 (construct) {
|
||||||
if (!InvokeConstructorKernel(cx, args))
|
if (!InvokeConstructorKernel(cx, args))
|
||||||
goto error;
|
goto error;
|
||||||
|
@ -2380,9 +2397,7 @@ BEGIN_CASE(JSOP_FUNCALL)
|
||||||
|
|
||||||
InitialFrameFlags initial = construct ? INITIAL_CONSTRUCT : INITIAL_NONE;
|
InitialFrameFlags initial = construct ? INITIAL_CONSTRUCT : INITIAL_NONE;
|
||||||
bool newType = cx->typeInferenceEnabled() && UseNewType(cx, script, regs.pc);
|
bool newType = cx->typeInferenceEnabled() && UseNewType(cx, script, regs.pc);
|
||||||
RootedScript funScript(cx, JSFunction::getOrCreateScript(cx, fun));
|
RootedScript funScript(cx, fun->nonLazyScript());
|
||||||
if (!funScript)
|
|
||||||
goto error;
|
|
||||||
if (!cx->stack.pushInlineFrame(cx, regs, args, *fun, funScript, initial))
|
if (!cx->stack.pushInlineFrame(cx, regs, args, *fun, funScript, initial))
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
|
|
|
@ -2343,6 +2343,37 @@ js::CloneScript(JSContext *cx, HandleObject enclosingScope, HandleFunction fun,
|
||||||
return dst;
|
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 *
|
DebugScript *
|
||||||
JSScript::debugScript()
|
JSScript::debugScript()
|
||||||
{
|
{
|
||||||
|
|
|
@ -1300,6 +1300,9 @@ CurrentScriptFileLineOrigin(JSContext *cx, unsigned *linenop, LineOption = NOT_C
|
||||||
extern UnrootedScript
|
extern UnrootedScript
|
||||||
CloneScript(JSContext *cx, HandleObject enclosingScope, HandleFunction fun, HandleScript script);
|
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
|
* NB: after a successful XDR_DECODE, XDRScript callers must do any required
|
||||||
* subsequent set-up of owning function or script object and then call
|
* subsequent set-up of owning function or script object and then call
|
||||||
|
|
Загрузка…
Ссылка в новой задаче