Bug 826148 - Part 1: JSFunction flag and interpreter changes (r=luke)

This commit is contained in:
Shu-yu Guo 2013-01-10 13:04:04 -08:00
parent 5023298814
commit 99db0d9ca5
9 changed files with 172 additions and 27 deletions

View File

@ -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)
{

View File

@ -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
{

View File

@ -650,6 +650,7 @@ JSCompartment::sweep(FreeOp *fop, bool releaseTypes)
sweepNewTypeObjectTable(newTypeObjects);
sweepNewTypeObjectTable(lazyTypeObjects);
sweepBreakpoints(fop);
sweepCallsiteClones();
if (global_ && IsObjectAboutToBeFinalized(global_.unsafeGet()))
global_ = NULL;

View File

@ -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

View File

@ -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)
if (clone->isInterpreted() && !CloneFunctionScript(cx, fun, clone))
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;
}

View File

@ -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;

View File

@ -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;

View File

@ -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()
{

View File

@ -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