mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 821361 - Optimize type information in closures that only run once, r=luke.
This commit is contained in:
parent
c66997672e
commit
ecff87a86d
@ -119,6 +119,7 @@ BytecodeEmitter::BytecodeEmitter(BytecodeEmitter *parent, Parser *parser, Shared
|
||||
typesetCount(0),
|
||||
hasSingletons(false),
|
||||
emittingForInit(false),
|
||||
emittingRunOnceLambda(false),
|
||||
hasGlobalScope(hasGlobalScope),
|
||||
selfHostingMode(selfHostingMode)
|
||||
{
|
||||
@ -1655,14 +1656,20 @@ CheckSideEffects(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, bool *answe
|
||||
}
|
||||
|
||||
bool
|
||||
BytecodeEmitter::checkSingletonContext()
|
||||
BytecodeEmitter::isInLoop()
|
||||
{
|
||||
if (!script->compileAndGo || sc->isFunctionBox())
|
||||
return false;
|
||||
for (StmtInfoBCE *stmt = topStmt; stmt; stmt = stmt->down) {
|
||||
if (stmt->isLoop())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
BytecodeEmitter::checkSingletonContext()
|
||||
{
|
||||
if (!script->compileAndGo || sc->isFunctionBox() || isInLoop())
|
||||
return false;
|
||||
hasSingletons = true;
|
||||
return true;
|
||||
}
|
||||
@ -2593,12 +2600,27 @@ frontend::EmitFunctionScript(JSContext *cx, BytecodeEmitter *bce, ParseNode *bod
|
||||
if (!JSScript::fullyInitFromEmitter(cx, bce->script, bce))
|
||||
return false;
|
||||
|
||||
/*
|
||||
* If this function is only expected to run once, mark the script so that
|
||||
* initializers created within it may be given more precise types.
|
||||
*/
|
||||
if (bce->parent && bce->parent->emittingRunOnceLambda)
|
||||
bce->script->treatAsRunOnce = true;
|
||||
|
||||
/* Mark functions which will only be executed once as singletons. */
|
||||
/*
|
||||
* Mark as singletons any function which will only be executed once, or
|
||||
* which is inner to a lambda we only expect to run once. In the latter
|
||||
* case, if the lambda runs multiple times then CloneFunctionObject will
|
||||
* make a deep clone of its contents.
|
||||
*/
|
||||
bool singleton =
|
||||
cx->typeInferenceEnabled() &&
|
||||
bce->script->compileAndGo &&
|
||||
bce->parent &&
|
||||
bce->parent->checkSingletonContext();
|
||||
(bce->parent->checkSingletonContext() ||
|
||||
(!bce->parent->isInLoop() &&
|
||||
bce->parent->parent &&
|
||||
bce->parent->parent->emittingRunOnceLambda));
|
||||
|
||||
/* Initialize fun->script() so that the debugger has a valid fun->script(). */
|
||||
RootedFunction fun(cx, bce->script->function());
|
||||
@ -5360,6 +5382,24 @@ EmitCallOrNew(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top)
|
||||
callop = true; /* suppress JSOP_UNDEFINED after */
|
||||
break;
|
||||
#endif
|
||||
case PNK_FUNCTION:
|
||||
/*
|
||||
* Top level lambdas which are immediately invoked should be
|
||||
* treated as only running once. Every time they execute we will
|
||||
* create new types and scripts for their contents, to increase
|
||||
* the quality of type information within them and enable more
|
||||
* backend optimizations. Note that this does not depend on the
|
||||
* lambda being invoked at most once (it may be named or be
|
||||
* accessed via foo.caller indirection), as multiple executions
|
||||
* will just cause the inner scripts to be repeatedly cloned.
|
||||
*/
|
||||
JS_ASSERT(!bce->emittingRunOnceLambda);
|
||||
bce->emittingRunOnceLambda = true;
|
||||
if (!EmitTree(cx, bce, pn2))
|
||||
return false;
|
||||
bce->emittingRunOnceLambda = false;
|
||||
callop = false;
|
||||
break;
|
||||
default:
|
||||
if (!EmitTree(cx, bce, pn2))
|
||||
return false;
|
||||
|
@ -114,6 +114,9 @@ struct BytecodeEmitter
|
||||
|
||||
bool emittingForInit:1; /* true while emitting init expr of for; exclude 'in' */
|
||||
|
||||
bool emittingRunOnceLambda:1; /* true while emitting a lambda which is only
|
||||
expected to run once. */
|
||||
|
||||
const bool hasGlobalScope:1; /* frontend::CompileScript's scope chain is the
|
||||
global object */
|
||||
|
||||
@ -153,6 +156,7 @@ struct BytecodeEmitter
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isInLoop();
|
||||
bool checkSingletonContext();
|
||||
|
||||
bool needsImplicitThis();
|
||||
|
11
js/src/jit-test/tests/basic/runOnceClosures.js
Normal file
11
js/src/jit-test/tests/basic/runOnceClosures.js
Normal file
@ -0,0 +1,11 @@
|
||||
|
||||
compare = (function() {
|
||||
function inner() { return inner.caller; };
|
||||
globalInner = inner;
|
||||
globalClosure = inner();
|
||||
return function(f) { return f === inner; }
|
||||
})();
|
||||
|
||||
assertEq(compare(globalInner), true);
|
||||
globalClosure();
|
||||
assertEq(compare(globalInner), false);
|
@ -1501,15 +1501,15 @@ js_CloneFunctionObject(JSContext *cx, HandleFunction fun, HandleObject parent,
|
||||
clone->initializeExtended();
|
||||
}
|
||||
|
||||
if (cx->compartment == fun->compartment() && !types::UseNewTypeForClone(fun)) {
|
||||
if (cx->compartment == fun->compartment() &&
|
||||
!fun->hasSingletonType() &&
|
||||
!types::UseNewTypeForClone(fun))
|
||||
{
|
||||
/*
|
||||
* We can use the same type as the original function provided that (a)
|
||||
* its prototype is correct, and (b) its type is not a singleton. The
|
||||
* first case will hold in all compileAndGo code, and the second case
|
||||
* will have been caught by CloneFunctionObject coming from function
|
||||
* definitions or read barriers, so will not get here.
|
||||
* Clone the function, reusing its script. We can use the same type as
|
||||
* the original function provided that its prototype is correct.
|
||||
*/
|
||||
if (fun->getProto() == proto && !fun->hasSingletonType())
|
||||
if (fun->getProto() == proto)
|
||||
clone->setType(fun->type());
|
||||
} else {
|
||||
if (!JSObject::setSingletonType(cx, clone))
|
||||
|
@ -243,13 +243,22 @@ CloneFunctionObjectIfNotSingleton(JSContext *cx, HandleFunction fun, HandleObjec
|
||||
* was called pessimistically, and we need to preserve the type's
|
||||
* property that if it is singleton there is only a single object
|
||||
* with its type in existence.
|
||||
*
|
||||
* For functions inner to run once lambda, it may be possible that
|
||||
* the lambda runs multiple times and we repeatedly clone it. In these
|
||||
* cases, fall through to CloneFunctionObject, which will deep clone
|
||||
* the function's script.
|
||||
*/
|
||||
if (fun->hasSingletonType()) {
|
||||
Rooted<JSObject*> obj(cx, SkipScopeParent(parent));
|
||||
if (!JSObject::setParent(cx, fun, obj))
|
||||
return NULL;
|
||||
fun->setEnvironment(parent);
|
||||
return fun;
|
||||
RootedScript script(cx, JSFunction::getOrCreateScript(cx, fun));
|
||||
if (!script->hasBeenCloned) {
|
||||
script->hasBeenCloned = true;
|
||||
Rooted<JSObject*> obj(cx, SkipScopeParent(parent));
|
||||
if (!JSObject::setParent(cx, fun, obj))
|
||||
return NULL;
|
||||
fun->setEnvironment(parent);
|
||||
return fun;
|
||||
}
|
||||
}
|
||||
|
||||
return CloneFunctionObject(cx, fun, parent);
|
||||
|
@ -2477,7 +2477,7 @@ types::UseNewTypeForInitializer(JSContext *cx, HandleScript script, jsbytecode *
|
||||
* arrays, but not normal arrays.
|
||||
*/
|
||||
|
||||
if (!cx->typeInferenceEnabled() || script->function())
|
||||
if (!cx->typeInferenceEnabled() || (script->function() && !script->treatAsRunOnce))
|
||||
return false;
|
||||
|
||||
if (key != JSProto_Object && !(key >= JSProto_Int8Array && key <= JSProto_Uint8ClampedArray))
|
||||
@ -4211,25 +4211,25 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset, TypeInferen
|
||||
poppedTypes(pc, 0)->addArith(cx, script, pc, &pushed[0]);
|
||||
break;
|
||||
|
||||
case JSOP_LAMBDA:
|
||||
case JSOP_DEFFUN: {
|
||||
case JSOP_LAMBDA: {
|
||||
RootedObject obj(cx, script_->getObject(GET_UINT32_INDEX(pc)));
|
||||
TypeSet *res = &pushed[0];
|
||||
|
||||
TypeSet *res = NULL;
|
||||
if (op == JSOP_LAMBDA)
|
||||
res = &pushed[0];
|
||||
|
||||
if (res) {
|
||||
if (script_->compileAndGo && !UseNewTypeForClone(obj->toFunction()))
|
||||
res->addType(cx, Type::ObjectType(obj));
|
||||
else
|
||||
res->addType(cx, Type::UnknownType());
|
||||
} else {
|
||||
cx->compartment->types.monitorBytecode(cx, script, offset);
|
||||
}
|
||||
// If the lambda may produce values with different types than the
|
||||
// original function, despecialize the type produced here. This includes
|
||||
// functions that are deep cloned at each lambda, as well as inner
|
||||
// functions to run-once lambdas which may actually execute multiple times.
|
||||
if (script_->compileAndGo && !script_->treatAsRunOnce && !UseNewTypeForClone(obj->toFunction()))
|
||||
res->addType(cx, Type::ObjectType(obj));
|
||||
else
|
||||
res->addType(cx, Type::AnyObjectType());
|
||||
break;
|
||||
}
|
||||
|
||||
case JSOP_DEFFUN:
|
||||
cx->compartment->types.monitorBytecode(cx, script, offset);
|
||||
break;
|
||||
|
||||
case JSOP_DEFVAR:
|
||||
break;
|
||||
|
||||
@ -4536,14 +4536,9 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset, TypeInferen
|
||||
pushed[0].addType(cx, Type::UnknownType());
|
||||
break;
|
||||
|
||||
case JSOP_CALLEE: {
|
||||
JSFunction *fun = script_->function();
|
||||
if (script_->compileAndGo && !UseNewTypeForClone(fun))
|
||||
pushed[0].addType(cx, Type::ObjectType(fun));
|
||||
else
|
||||
pushed[0].addType(cx, Type::UnknownType());
|
||||
case JSOP_CALLEE:
|
||||
pushed[0].addType(cx, Type::AnyObjectType());
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
/* Display fine-grained debug information first */
|
||||
|
@ -474,6 +474,8 @@ class JSScript : public js::gc::Cell
|
||||
undefined properties in this
|
||||
script */
|
||||
bool hasSingletons:1; /* script has singleton objects */
|
||||
bool treatAsRunOnce:1; /* script is a lambda to treat as running once. */
|
||||
bool hasBeenCloned:1; /* script has been reused for a clone. */
|
||||
bool isActiveEval:1; /* script came from eval(), and is still active */
|
||||
bool isCachedEval:1; /* script came from eval(), and is in eval cache */
|
||||
bool uninlineable:1; /* script is considered uninlineable by analysis */
|
||||
|
Loading…
Reference in New Issue
Block a user