Bug 554955: Revert fix (changesets 297b1312f534, 18a1effafe19, 26d40e1e80bf, f6117465a979): 14% performance hit as measured by Talos. r=talos

This commit is contained in:
Jim Blandy 2011-01-31 16:48:26 -08:00
Родитель a80aba6af2
Коммит 0ea68d51ff
20 изменённых файлов: 24 добавлений и 359 удалений

Просмотреть файл

@ -3666,30 +3666,9 @@ js_EmitFunctionScript(JSContext *cx, JSCodeGenerator *cg, JSParseNode *body)
CG_SWITCH_TO_MAIN(cg);
}
if (!js_EmitTree(cx, cg, body) ||
js_Emit1(cx, cg, JSOP_STOP) < 0 ||
!JSScript::NewScriptFromCG(cx, cg)) {
return false;
}
JSFunction *fun = cg->fun();
if (fun->script()->bindings.extensibleParents()) {
/*
* Since the function has extensible parents, its blocks need unique
* shapes. See the comments for js::Bindings::extensibleParents.
*/
JSScript *script = FUN_SCRIPT(fun);
if (JSScript::isValidOffset(script->objectsOffset)) {
JSObjectArray *objects = FUN_SCRIPT(fun)->objects();
for (uint32 i = 0; i < objects->length; i++) {
JSObject *obj = objects->vector[i];
if (obj->isBlock())
obj->setBlockOwnShape(cx);
}
}
}
return true;
return js_EmitTree(cx, cg, body) &&
js_Emit1(cx, cg, JSOP_STOP) >= 0 &&
JSScript::NewScriptFromCG(cx, cg);
}
/* A macro for inlining at the top of js_EmitTree (whence it came). */
@ -4593,7 +4572,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
}
#endif
fun = pn->pn_funbox->function();
fun = (JSFunction *) pn->pn_funbox->object;
JS_ASSERT(FUN_INTERPRETED(fun));
if (fun->u.i.script) {
/*

Просмотреть файл

@ -958,7 +958,8 @@ NewCallObject(JSContext *cx, Bindings *bindings, JSObject &scopeChain, JSObject
return NULL;
/* Init immediately to avoid GC seeing a half-init'ed object. */
callobj->initCall(cx, bindings, &scopeChain);
callobj->init(cx, &js_CallClass, NULL, &scopeChain, NULL, false);
callobj->setMap(bindings->lastShape());
/* This must come after callobj->lastProp has been set. */
if (!callobj->ensureInstanceReservedSlots(cx, argsVars))

Просмотреть файл

@ -96,7 +96,7 @@
JSFunctionSpec::call points to a
JSNativeTraceInfo. */
#define JSFUN_INTERPRETED 0x4000 /* use u.i if kind >= this value else u.n */
#define JSFUN_FLAT_CLOSURE 0x8000 /* flat (aka "display") closure */
#define JSFUN_FLAT_CLOSURE 0x8000 /* flag (aka "display") closure */
#define JSFUN_NULL_CLOSURE 0xc000 /* null closure entrains no scope chain */
#define JSFUN_KINDMASK 0xc000 /* encode interp vs. native and closure
optimization level -- see above */

Просмотреть файл

@ -2090,7 +2090,7 @@ AssertValidPropertyCacheHit(JSContext *cx, JSScript *script, JSFrameRegs& regs,
}
if (!ok)
return false;
if (cx->runtime->gcNumber != sample)
if (cx->runtime->gcNumber != sample || entry->vshape() != pobj->shape())
return true;
JS_ASSERT(prop);
JS_ASSERT(pobj == found);

Просмотреть файл

@ -3264,8 +3264,9 @@ js_CloneBlockObject(JSContext *cx, JSObject *proto, JSStackFrame *fp)
JSStackFrame *priv = js_FloatingFrameIfGenerator(cx, fp);
/* The caller sets parent on its own. */
clone->initClonedBlock(cx, proto, priv);
clone->init(cx, &js_BlockClass, proto, NULL, priv, false);
clone->setMap(proto->map);
if (!clone->ensureInstanceReservedSlots(cx, count + 1))
return NULL;

Просмотреть файл

@ -474,21 +474,16 @@ struct JSObject : js::gc::Cell {
bool hasOwnShape() const { return !!(flags & OWN_SHAPE); }
void setMap(JSObjectMap *amap) {
void setMap(const JSObjectMap *amap) {
JS_ASSERT(!hasOwnShape());
map = amap;
map = const_cast<JSObjectMap *>(amap);
objShape = map->shape;
}
void setSharedNonNativeMap() {
setMap(const_cast<JSObjectMap *>(&JSObjectMap::sharedNonNative));
setMap(&JSObjectMap::sharedNonNative);
}
/* Functions for setting up scope chain object maps and shapes. */
void initCall(JSContext *cx, const js::Bindings *bindings, JSObject *parent);
void initClonedBlock(JSContext *cx, JSObject *proto, JSStackFrame *priv);
void setBlockOwnShape(JSContext *cx);
void deletingShapeChange(JSContext *cx, const js::Shape &shape);
const js::Shape *methodShapeChange(JSContext *cx, const js::Shape &shape);
bool methodShapeChange(JSContext *cx, uint32 slot);

Просмотреть файл

@ -58,7 +58,6 @@
#include "jscntxt.h"
#include "jsnum.h"
#include "jsscopeinlines.h"
#include "jsscriptinlines.h"
#include "jsstr.h"
#include "jsgcinlines.h"
@ -140,59 +139,6 @@ JSObject::finalize(JSContext *cx)
finish(cx);
}
/*
* Initializer for Call objects for functions and eval frames. Set class,
* parent, map, and shape, and allocate slots.
*/
inline void
JSObject::initCall(JSContext *cx, const js::Bindings *bindings, JSObject *parent)
{
init(cx, &js_CallClass, NULL, parent, NULL, false);
map = bindings->lastShape();
/*
* If |bindings| is for a function that has extensible parents, that means
* its Call should have its own shape; see js::Bindings::extensibleParents.
*/
if (bindings->extensibleParents())
setOwnShape(js_GenerateShape(cx, false));
else
objShape = map->shape;
}
/*
* Initializer for cloned block objects. Set class, prototype, frame, map, and
* shape.
*/
inline void
JSObject::initClonedBlock(JSContext *cx, JSObject *proto, JSStackFrame *frame)
{
init(cx, &js_BlockClass, proto, NULL, frame, false);
/* Cloned blocks copy their prototype's map; it had better be shareable. */
JS_ASSERT(!proto->inDictionaryMode() || proto->lastProp->frozen());
map = proto->map;
/*
* If the prototype has its own shape, that means the clone should, too; see
* js::Bindings::extensibleParents.
*/
if (proto->hasOwnShape())
setOwnShape(js_GenerateShape(cx, false));
else
objShape = map->shape;
}
/*
* Mark a compile-time block as OWN_SHAPE, indicating that its run-time clones
* also need unique shapes. See js::Bindings::extensibleParents.
*/
inline void
JSObject::setBlockOwnShape(JSContext *cx) {
JS_ASSERT(isStaticBlock());
setOwnShape(js_GenerateShape(cx, false));
}
/*
* Property read barrier for deferred cloning of compiler-created function
* objects optimized as typically non-escaping, ad-hoc methods in obj.

Просмотреть файл

@ -230,7 +230,7 @@ OPDEF(JSOP_OBJECT, 80, "object", NULL, 3, 0, 1, 19, JOF_OBJECT
/* Pop value and discard it. */
OPDEF(JSOP_POP, 81, "pop", NULL, 1, 1, 0, 2, JOF_BYTE)
/* Call a function as a constructor; operand is argc. */
/* Convert value to number, for unary +. */
OPDEF(JSOP_NEW, 82, js_new_str, NULL, 3, -1, 1, 17, JOF_UINT16|JOF_INVOKE)
/* Trap into debugger for breakpoint, etc. */

Просмотреть файл

@ -308,7 +308,7 @@ Parser::newFunctionBox(JSObject *obj, JSParseNode *fn, JSTreeContext *tc)
bool
JSFunctionBox::joinable() const
{
return FUN_NULL_CLOSURE(function()) &&
return FUN_NULL_CLOSURE((JSFunction *) object) &&
!(tcflags & (TCF_FUN_USES_ARGUMENTS | TCF_FUN_USES_OWN_NAME));
}
@ -322,23 +322,6 @@ JSFunctionBox::inAnyDynamicScope() const
return false;
}
bool
JSFunctionBox::scopeIsExtensible() const
{
/* Direct eval calls can add bindings, but not in strict mode functions. */
if ((tcflags & TCF_FUN_CALLS_EVAL) && !(tcflags & TCF_STRICT_MODE_CODE))
return true;
/*
* Function statements also extend call objects. (We forbid function
* statements in strict mode code.)
*/
if (tcflags & TCF_HAS_FUNCTION_STMT)
return true;
return false;
}
bool
JSFunctionBox::shouldUnbrand(uintN methods, uintN slowMethods) const
{
@ -1192,7 +1175,7 @@ Compiler::defineGlobals(JSContext *cx, GlobalScope &globalScope, JSScript *scrip
Value rval;
if (def.funbox) {
JSFunction *fun = def.funbox->function();
JSFunction *fun = (JSFunction *)def.funbox->object;
/*
* No need to check for redeclarations or anything, global
@ -2044,7 +2027,6 @@ Parser::analyzeFunctions(JSTreeContext *tc)
return true;
if (!markFunArgs(tc->functionList))
return false;
markExtensibleScopeDescendants(tc->functionList, false);
setFunctionKinds(tc->functionList, &tc->flags);
return true;
}
@ -2087,7 +2069,7 @@ FindFunArgs(JSFunctionBox *funbox, int level, JSFunctionBoxQueue *queue)
do {
JSParseNode *fn = funbox->node;
JS_ASSERT(fn->pn_arity == PN_FUNC);
JSFunction *fun = funbox->function();
JSFunction *fun = (JSFunction *) funbox->object;
int fnlevel = level;
/*
@ -2346,7 +2328,7 @@ CanFlattenUpvar(JSDefinition *dn, JSFunctionBox *funbox, uint32 tcflags)
* function refers to its own name) or strictly after afunbox, we also
* defeat the flat closure optimization for this dn.
*/
JSFunction *afun = afunbox->function();
JSFunction *afun = (JSFunction *) afunbox->object;
if (!(afun->flags & JSFUN_LAMBDA)) {
if (dn->isBindingForm() || dn->pn_pos >= afunbox->node->pn_pos)
return false;
@ -2492,7 +2474,7 @@ Parser::setFunctionKinds(JSFunctionBox *funbox, uint32 *tcflags)
}
}
JSFunction *fun = funbox->function();
JSFunction *fun = (JSFunction *) funbox->object;
JS_ASSERT(FUN_KIND(fun) == JSFUN_INTERPRETED);
@ -2651,37 +2633,6 @@ Parser::setFunctionKinds(JSFunctionBox *funbox, uint32 *tcflags)
#undef FUN_METER
}
/*
* Walk the JSFunctionBox tree looking for functions whose call objects may
* acquire new bindings as they execute: non-strict functions that call eval,
* and functions that contain function statements (definitions not appearing
* within the top statement list, which don't take effect unless they are
* evaluated). Enclosed functions that could refer to bindings these extensible
* call objects can shadow must have their bindings' extensibleParents flags
* set, and such compiler-created blocks must have their OWN_SHAPE flags set;
* the comments for js::Bindings::extensibleParents explain why.
*/
void
Parser::markExtensibleScopeDescendants(JSFunctionBox *funbox, bool hasExtensibleParent)
{
for (; funbox; funbox = funbox->siblings) {
/*
* It would be nice to use FUN_KIND(fun) here to recognize functions
* that will never consult their parent chains, and thus don't need
* their 'extensible parents' flag set. Filed as bug 619750.
*/
JS_ASSERT(!funbox->bindings.extensibleParents());
if (hasExtensibleParent)
funbox->bindings.setExtensibleParents();
if (funbox->kids) {
markExtensibleScopeDescendants(funbox->kids,
hasExtensibleParent || funbox->scopeIsExtensible());
}
}
}
const char js_argument_str[] = "argument";
const char js_variable_str[] = "variable";
const char js_unknown_str[] = "unknown";
@ -3184,7 +3135,7 @@ Parser::functionDef(JSAtom *funAtom, FunctionType type, uintN lambda)
if (!funbox)
return NULL;
JSFunction *fun = funbox->function();
JSFunction *fun = (JSFunction *) funbox->object;
/* Now parse formal argument list and compute fun->nargs. */
JSParseNode *prelude = NULL;
@ -7515,7 +7466,7 @@ CheckForImmediatelyAppliedLambda(JSParseNode *pn)
JS_ASSERT(pn->pn_arity == PN_FUNC);
JSFunctionBox *funbox = pn->pn_funbox;
JS_ASSERT((funbox->function())->flags & JSFUN_LAMBDA);
JS_ASSERT(((JSFunction *) funbox->object)->flags & JSFUN_LAMBDA);
if (!(funbox->tcflags & (TCF_FUN_USES_ARGUMENTS | TCF_FUN_USES_OWN_NAME)))
pn->pn_dflags &= ~PND_FUNARG;
}

Просмотреть файл

@ -974,8 +974,6 @@ struct JSFunctionBox : public JSObjectBox
level:JSFB_LEVEL_BITS;
uint32 tcflags;
JSFunction *function() const { return (JSFunction *) object; }
bool joinable() const;
/*
@ -984,12 +982,6 @@ struct JSFunctionBox : public JSObjectBox
*/
bool inAnyDynamicScope() const;
/*
* Must this function's descendants be marked as having an extensible
* ancestor?
*/
bool scopeIsExtensible() const;
/*
* Unbrand an object being initialized or constructed if any method cannot
* be joined to one compiler-created null closure shared among N different
@ -1118,7 +1110,6 @@ struct Parser : private js::AutoGCRooter
bool analyzeFunctions(JSTreeContext *tc);
void cleanFunctionList(JSFunctionBox **funbox);
bool markFunArgs(JSFunctionBox *funbox);
void markExtensibleScopeDescendants(JSFunctionBox *funbox, bool hasExtensibleParent);
void setFunctionKinds(JSFunctionBox *funbox, uint32 *tcflags);
void trace(JSTracer *trc);

Просмотреть файл

@ -61,7 +61,6 @@ PropertyCache::fill(JSContext *cx, JSObject *obj, uintN scopeIndex, uintN protoI
JS_ASSERT(this == &JS_PROPERTY_CACHE(cx));
JS_ASSERT(!cx->runtime->gcRunning);
JS_ASSERT_IF(adding, !obj->isCall());
if (js_IsPropertyCacheDisabled(cx)) {
PCMETER(disfills++);

Просмотреть файл

@ -171,7 +171,6 @@ struct PropertyCacheEntry;
struct Shape;
struct EmptyShape;
class Bindings;
} /* namespace js */

Просмотреть файл

@ -173,7 +173,6 @@ class Bindings {
uint16 nargs;
uint16 nvars;
uint16 nupvars;
bool hasExtensibleParents;
public:
inline Bindings(JSContext *cx);
@ -204,7 +203,7 @@ class Bindings {
bool hasLocalNames() const { return countLocalNames() > 0; }
/* Returns the shape lineage generated for these bindings. */
inline js::Shape *lastShape() const;
inline const js::Shape *lastShape() const;
enum {
/*
@ -304,43 +303,6 @@ class Bindings {
*/
void makeImmutable();
/*
* Sometimes call objects and run-time block objects need unique shapes, but
* sometimes they don't.
*
* Property cache entries only record the shapes of the first and last
* objects along the search path, so if the search traverses more than those
* two objects, then those first and last shapes must determine the shapes
* of everything else along the path. The js_PurgeScopeChain stuff takes
* care of making this work, but that suffices only because we require that
* start points with the same shape have the same successor object in the
* search path --- if two start points have different successors, they must
* have different shapes.
*
* For call and run-time block objects, the "successor" is the scope chain
* parent. Unlike prototype objects (of which there are usually few), scope
* chain parents are created frequently (possibly on every call), so it's
* not too far from optimal to give every call and block its own shape.
*
* In many cases, however, it's not actually necessary to give call and
* block objects their own shapes, so we can do better. If the code will
* always be used with the same global object, and none of the enclosing
* call objects could have bindings added to them at runtime (by direct eval
* calls or function statements), then we can use a fixed set of shapes for
* those objects. You could think of the shapes in the functions' bindings
* and compile-time blocks as uniquely identifying the global object(s) at
* the end of the scope chain.
*
* (In fact, some JSScripts we do use against multiple global objects (see
* bug 618497), and using the fixed shapes isn't sound there.)
*
* If the hasExtensibleParents flag is set, then Call objects created for
* the function this Bindings describes need unique shapes. If the flag is
* clear, then we can use lastBinding's shape.
*/
void setExtensibleParents() { hasExtensibleParents = true; }
bool extensibleParents() const { return hasExtensibleParents; }
/*
* These methods provide direct access to the shape path normally
* encapsulated by js::Bindings. These methods may be used to make a

Просмотреть файл

@ -52,8 +52,7 @@ namespace js {
inline
Bindings::Bindings(JSContext *cx)
: lastBinding(cx->runtime->emptyCallShape), nargs(0), nvars(0), nupvars(0),
hasExtensibleParents(false)
: lastBinding(cx->runtime->emptyCallShape), nargs(0), nvars(0), nupvars(0)
{
}
@ -86,7 +85,7 @@ Bindings::clone(JSContext *cx, Bindings *bindings)
*this = *bindings;
}
Shape *
const Shape *
Bindings::lastShape() const
{
JS_ASSERT(lastBinding);

Просмотреть файл

@ -13,11 +13,6 @@ script regress-551763-1.js
script regress-551763-2.js
script regress-552432.js
script regress-553778.js
script regress-554955-1.js
script regress-554955-2.js
script regress-554955-3.js
script regress-554955-4.js
script regress-554955-5.js
script regress-555246-0.js
script regress-555246-1.js
script regress-559402-1.js

Просмотреть файл

@ -1,32 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/
*/
function f(s) {
eval(s);
return function(a) {
var d;
let (c = 3) {
d = function() { a; }; // force Block object to be cloned
with({}) {}; // repel JägerMonkey
return b; // lookup occurs in scope of Block
}
};
}
var b = 1;
var g1 = f("");
var g2 = f("var b = 2;");
/* Call the lambda once, caching a reference to the global b. */
g1(0);
/*
* If this call sees the above cache entry, then it will erroneously use the
* global b.
*/
assertEq(g2(0), 2);
reportCompare(true, true);

Просмотреть файл

@ -1,29 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/
*/
function f(s) {
eval(s);
return function(a) {
with({}) {}; // repel JägerMonkey
eval(a);
return b;
};
}
var b = 1;
var g1 = f("");
var g2 = f("var b = 2;");
/* Call the lambda once, caching a reference to the global b. */
g1('');
/*
* If this call sees the above cache entry, then it will erroneously use
* the global b.
*/
assertEq(g2(''), 2);
reportCompare(true, true);

Просмотреть файл

@ -1,31 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/
*/
function f(s) {
eval(s);
return function(a) {
with({}) {}; // repel JägerMonkey
eval(a);
let (c = 3) {
return b;
};
};
}
var b = 1;
var g1 = f("");
var g2 = f("var b = 2;");
/* Call the lambda once, caching a reference to the global b. */
g1('');
/*
* If this call sees the above cache entry, then it will erroneously use the
* global b.
*/
assertEq(g2(''), 2);
reportCompare(true, true);

Просмотреть файл

@ -1,31 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/
*/
function f(s) {
eval(s);
return function(a) {
eval(a);
let (c = 3) {
eval(s);
return b;
};
};
}
var b = 1;
var g1 = f('');
var g2 = f('');
/* Call the lambda once, caching a reference to the global b. */
g1('');
/*
* If this call sees the above cache entry, then it will erroneously use the
* global b.
*/
assertEq(g2('var b=2'), 2);
reportCompare(true, true);

Просмотреть файл

@ -1,30 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/
*/
function f(s) {
if (s) {
function b() { }
}
return function(a) {
eval(a);
return b;
};
}
var b = 1;
var g1 = f(false);
var g2 = f(true);
/* Call the lambda once, caching a reference to the global b. */
g1('');
/*
* If this call sees the above cache entry, then it will erroneously use the
* global b.
*/
assertEq(typeof g2(''), "function");
reportCompare(true, true);