Bug 659577 - emit ScopeCoordinate::hops (r=waldo)

--HG--
extra : rebase_source : 3698a6411ffcae90bbcfaa7a965d8de648b3e509
This commit is contained in:
Luke Wagner 2012-04-11 18:09:20 -07:00
parent a53f1cb215
commit 731780b34b
11 changed files with 189 additions and 71 deletions

View File

@ -833,43 +833,85 @@ EmitUnaliasedVarOp(JSContext *cx, JSOp op, uint16_t slot, BytecodeEmitter *bce)
}
static bool
EmitAliasedVarOp(JSContext *cx, JSOp op, uint16_t binding, JSAtom *atom, BytecodeEmitter *bce)
EmitAliasedVarOp(JSContext *cx, JSOp op, ScopeCoordinate sc, JSAtom *atom, BytecodeEmitter *bce)
{
JS_ASSERT(JOF_OPTYPE(op) == JOF_SCOPECOORD);
/*
* XXX This is temporary: bug 659577 will need to compute the number of
* cloned block objects to hop over.
*/
uint16_t hops = 0;
jsatomid atomIndex;
if (!bce->makeAtomIndex(atom, &atomIndex))
return false;
bool decomposed = js_CodeSpec[op].format & JOF_DECOMPOSE;
unsigned n = 2 * sizeof(uint16_t) + sizeof(uint32_t) + (decomposed ? 1 : 0);
unsigned n = 2 * sizeof(uint16_t) + sizeof(uint32_t) + sizeof(uint16_t) + (decomposed ? 1 : 0);
JS_ASSERT(int(n) + 1 /* op */ == js_CodeSpec[op].length);
ptrdiff_t off = EmitN(cx, bce, op, n);
if (off < 0)
return false;
jsbytecode *pc = bce->code(off);
SET_UINT16(pc, hops);
SET_UINT16(pc, sc.hops);
pc += sizeof(uint16_t);
SET_UINT16(pc, binding);
SET_UINT16(pc, sc.binding);
pc += sizeof(uint16_t);
SET_UINT32_INDEX(pc, atomIndex);
pc += sizeof(uint32_t);
SET_UINT16(pc, sc.frameBinding);
return true;
}
static unsigned
ClonedBlockDepth(BytecodeEmitter *bce)
{
unsigned clonedBlockDepth = 0;
for (StaticBlockObject *b = bce->sc->blockChain; b; b = b->enclosingBlock()) {
if (b->needsClone())
++clonedBlockDepth;
}
return clonedBlockDepth;
}
static bool
EmitAliasedVarOp(JSContext *cx, JSOp op, ParseNode *pn, BytecodeEmitter *bce)
{
uint16_t binding = JOF_OPTYPE(pn->getOp()) == JOF_QARG
? bce->sc->bindings.argToBinding(pn->pn_cookie.slot())
: bce->sc->bindings.localToBinding(pn->pn_cookie.slot());
return EmitAliasedVarOp(cx, op, binding, pn->atom(), bce);
/*
* The contents of the dynamic scope chain (fp->scopeChain) exactly reflect
* the needsClone-subset of the block chain. Use this to determine the
* number of ClonedBlockObjects on fp->scopeChain to skip to find the scope
* object containing the var to which pn is bound. ALIASEDVAR ops cannot
* reach across with scopes so ClonedBlockObjects is the only NestedScope
* on the scope chain.
*/
ScopeCoordinate sc;
if (JOF_OPTYPE(pn->getOp()) == JOF_QARG) {
JS_ASSERT(bce->sc->funIsHeavyweight());
sc.hops = ClonedBlockDepth(bce);
sc.binding = bce->sc->bindings.argToBinding(pn->pn_cookie.slot());
sc.frameBinding = sc.binding;
} else {
JS_ASSERT(JOF_OPTYPE(pn->getOp()) == JOF_LOCAL || pn->isKind(PNK_FUNCTION));
unsigned local = pn->pn_cookie.slot();
sc.frameBinding = bce->sc->bindings.localToBinding(local);
if (local < bce->sc->bindings.numVars()) {
JS_ASSERT(bce->sc->funIsHeavyweight());
sc.hops = ClonedBlockDepth(bce);
sc.binding = sc.frameBinding;
} else {
unsigned depth = local - bce->sc->bindings.numVars();
unsigned hops = 0;
StaticBlockObject *b = bce->sc->blockChain;
while (!b->containsVarAtDepth(depth)) {
if (b->needsClone())
hops++;
b = b->enclosingBlock();
}
sc.hops = hops;
sc.binding = depth - b->stackDepth();
}
}
return EmitAliasedVarOp(cx, op, sc, pn->atom(), bce);
}
static bool
@ -1031,11 +1073,9 @@ EmitEnterBlock(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, JSOp op)
Definition *dn = blockObj->maybeDefinitionParseNode(i);
/* Beware the empty destructuring dummy. */
if (!dn) {
JS_ASSERT(i + 1 <= blockObj->slotCount());
if (!dn)
blockObj->setAliased(i, bce->sc->bindingsAccessedDynamically());
continue;
}
JS_ASSERT(dn->isDefn());
JS_ASSERT(unsigned(dn->frameSlot() + depthPlusFixed) < JS_BIT(16));
@ -2591,9 +2631,12 @@ frontend::EmitFunctionScript(JSContext *cx, BytecodeEmitter *bce, ParseNode *bod
if (Emit1(cx, bce, JSOP_ARGUMENTS) < 0)
return false;
if (bce->sc->bindingsAccessedDynamically()) {
ScopeCoordinate sc;
sc.hops = 0;
sc.binding = bce->sc->bindings.localToBinding(bce->sc->argumentsLocalSlot());
sc.frameBinding = sc.binding;
JSAtom *atom = cx->runtime->atomState.argumentsAtom;
uint16_t binding = bce->sc->bindings.localToBinding(bce->sc->argumentsLocalSlot());
if (!EmitAliasedVarOp(cx, JSOP_SETALIASEDVAR, binding, atom, bce))
if (!EmitAliasedVarOp(cx, JSOP_SETALIASEDVAR, sc, atom, bce))
return false;
} else {
if (!EmitUnaliasedVarOp(cx, JSOP_SETLOCAL, bce->sc->argumentsLocalSlot(), bce))

View File

@ -3,7 +3,7 @@ setDebug(true);
x = "notset";
function main() {
/* The JSOP_STOP in main. */
a = { valueOf: function () { trap(main, 95, "success()"); } };
a = { valueOf: function () { trap(main, 97, "success()"); } };
b = "";
eval();
a + b;

View File

@ -320,13 +320,19 @@ ScriptAnalysis::analyzeBytecode(JSContext *cx)
case JSOP_GETALIASEDVAR:
case JSOP_CALLALIASEDVAR:
case JSOP_SETALIASEDVAR:
case JSOP_SETALIASEDVAR: {
JS_ASSERT(!isInlineable);
usesScopeChain_ = true;
/* XXX: this can be removed after bug 659577. */
if (ScopeCoordinate(pc).binding >= script->nfixed)
ScopeCoordinate sc(pc);
if (script->bindings.bindingIsLocal(sc.frameBinding) &&
script->bindings.bindingToLocal(sc.frameBinding) >= script->nfixed)
{
localsAliasStack_ = true;
}
break;
}
case JSOP_DEFFUN:
case JSOP_DEFVAR:

View File

@ -364,9 +364,9 @@ static inline uint32_t GetBytecodeSlot(JSScript *script, jsbytecode *pc)
case JSOP_SETALIASEDVAR:
{
ScopeCoordinate sc = ScopeCoordinate(pc);
return script->bindings.bindingIsArg(sc.binding)
? ArgSlot(script->bindings.bindingToArg(sc.binding))
: LocalSlot(script, script->bindings.bindingToLocal(sc.binding));
return script->bindings.bindingIsArg(sc.frameBinding)
? ArgSlot(script->bindings.bindingToArg(sc.frameBinding))
: LocalSlot(script, script->bindings.bindingToLocal(sc.frameBinding));
}

View File

@ -880,26 +880,6 @@ CheckArgAccess(StackFrame *fp, unsigned index)
fp->script()->argsObjAliasesFormals());
}
/*
* This function is temporary. Bug 659577 will change all ALIASEDVAR
* access to use the scope chain instead.
*/
static inline Value &
AliasedVar(StackFrame *fp, ScopeCoordinate sc)
{
JSScript *script = fp->script();
#ifdef DEBUG
JS_ASSERT(sc.hops == 0); /* Temporary */
if (script->bindings.bindingIsArg(sc.binding))
JS_ASSERT(script->formalLivesInCallObject(script->bindings.bindingToArg(sc.binding)));
else
CheckLocalAccess(fp, script->bindings.bindingToLocal(sc.binding), true);
#endif
return script->bindings.bindingIsArg(sc.binding)
? fp->formalArg(script->bindings.bindingToArg(sc.binding))
: fp->localSlot(script->bindings.bindingToLocal(sc.binding));
}
#define PUSH_COPY(v) do { *regs.sp++ = v; assertSameCompartment(cx, regs.sp[-1]); } while (0)
#define PUSH_COPY_SKIP_CHECK(v) *regs.sp++ = v
#define PUSH_NULL() regs.sp++->setNull()
@ -1178,6 +1158,8 @@ js::Interpret(JSContext *cx, StackFrame *entryFrame, InterpMode interpMode)
# define END_CASE_LEN8 len = 8; goto advance_pc;
# define END_CASE_LEN9 len = 9; goto advance_pc;
# define END_CASE_LEN10 len = 10; goto advance_pc;
# define END_CASE_LEN11 len = 11; goto advance_pc;
# define END_CASE_LEN12 len = 12; goto advance_pc;
# define END_VARLEN_CASE goto advance_pc;
# define ADD_EMPTY_CASE(OP) BEGIN_CASE(OP)
# define END_EMPTY_CASES goto advance_pc_by_one;
@ -2846,16 +2828,14 @@ BEGIN_CASE(JSOP_CALLALIASEDVAR)
BEGIN_CASE(JSOP_GETALIASEDVAR)
{
ScopeCoordinate sc = ScopeCoordinate(regs.pc);
Value &var = AliasedVar(regs.fp(), sc);
PUSH_COPY(var);
PUSH_COPY(regs.fp()->aliasedVarScope(sc).aliasedVar(sc));
}
END_CASE(JSOP_GETALIASEDVAR)
BEGIN_CASE(JSOP_SETALIASEDVAR)
{
ScopeCoordinate sc = ScopeCoordinate(regs.pc);
Value &var = AliasedVar(regs.fp(), sc);
var = regs.sp[-1];
regs.fp()->aliasedVarScope(sc).setAliasedVar(sc, regs.sp[-1]);
}
END_CASE(JSOP_SETALIASEDVAR)

View File

@ -333,14 +333,18 @@ OPDEF(JSOP_FINALLY, 135,"finally", NULL, 1, 0, 2, 0, JOF_BYTE)
* function, function statements that are conditionally executed, 'eval',
* 'with', 'arguments' and E4X filters. All of these cases require creating a
* CallObject to own the aliased variable.
*
* XXX: there is also a temporary 2-byte index (indicating the frame slot
* aliased by the scope chain) which will be removed with the last patch of bug
* 659577.
*/
OPDEF(JSOP_GETALIASEDVAR, 136,"getaliasedvar",NULL, 9, 0, 1, 19, JOF_SCOPECOORD|JOF_NAME)
OPDEF(JSOP_CALLALIASEDVAR,137,"callaliasedvar",NULL, 9, 0, 1, 19, JOF_SCOPECOORD|JOF_NAME)
OPDEF(JSOP_SETALIASEDVAR, 138,"setaliasedvar",NULL, 9, 1, 1, 3, JOF_SCOPECOORD|JOF_NAME|JOF_SET|JOF_DETECTING)
OPDEF(JSOP_INCALIASEDVAR, 139,"incaliasedvar",NULL, 10, 0, 1, 15, JOF_SCOPECOORD|JOF_NAME|JOF_INC|JOF_TMPSLOT3|JOF_DECOMPOSE)
OPDEF(JSOP_DECALIASEDVAR, 140,"decaliasedvar",NULL, 10, 0, 1, 15, JOF_SCOPECOORD|JOF_NAME|JOF_DEC|JOF_TMPSLOT3|JOF_DECOMPOSE)
OPDEF(JSOP_ALIASEDVARINC, 141,"aliasedvarinc",NULL, 10, 0, 1, 15, JOF_SCOPECOORD|JOF_NAME|JOF_INC|JOF_POST|JOF_TMPSLOT3|JOF_DECOMPOSE)
OPDEF(JSOP_ALIASEDVARDEC, 142,"aliasedvardec",NULL, 10, 0, 1, 15, JOF_SCOPECOORD|JOF_NAME|JOF_DEC|JOF_POST|JOF_TMPSLOT3|JOF_DECOMPOSE)
OPDEF(JSOP_GETALIASEDVAR, 136,"getaliasedvar",NULL, 11, 0, 1, 19, JOF_SCOPECOORD|JOF_NAME)
OPDEF(JSOP_CALLALIASEDVAR,137,"callaliasedvar",NULL, 11, 0, 1, 19, JOF_SCOPECOORD|JOF_NAME)
OPDEF(JSOP_SETALIASEDVAR, 138,"setaliasedvar",NULL, 11, 1, 1, 3, JOF_SCOPECOORD|JOF_NAME|JOF_SET|JOF_DETECTING)
OPDEF(JSOP_INCALIASEDVAR, 139,"incaliasedvar",NULL, 12, 0, 1, 15, JOF_SCOPECOORD|JOF_NAME|JOF_INC|JOF_TMPSLOT3|JOF_DECOMPOSE)
OPDEF(JSOP_DECALIASEDVAR, 140,"decaliasedvar",NULL, 12, 0, 1, 15, JOF_SCOPECOORD|JOF_NAME|JOF_DEC|JOF_TMPSLOT3|JOF_DECOMPOSE)
OPDEF(JSOP_ALIASEDVARINC, 141,"aliasedvarinc",NULL, 12, 0, 1, 15, JOF_SCOPECOORD|JOF_NAME|JOF_INC|JOF_POST|JOF_TMPSLOT3|JOF_DECOMPOSE)
OPDEF(JSOP_ALIASEDVARDEC, 142,"aliasedvardec",NULL, 12, 0, 1, 15, JOF_SCOPECOORD|JOF_NAME|JOF_DEC|JOF_POST|JOF_TMPSLOT3|JOF_DECOMPOSE)
/* Unused. */
OPDEF(JSOP_UNUSED8, 143,"unused8", NULL, 1, 0, 0, 0, JOF_BYTE)

View File

@ -2816,10 +2816,10 @@ mjit::Compiler::generateMethod()
frame.push(ObjectValue(*singleton));
} else {
ScopeCoordinate sc = ScopeCoordinate(PC);
if (script->bindings.bindingIsArg(sc.binding))
frame.pushArg(script->bindings.bindingToArg(sc.binding));
if (script->bindings.bindingIsArg(sc.frameBinding))
frame.pushArg(script->bindings.bindingToArg(sc.frameBinding));
else
frame.pushLocal(script->bindings.bindingToLocal(sc.binding));
frame.pushLocal(script->bindings.bindingToLocal(sc.frameBinding));
}
}
END_CASE(JSOP_GETALIASEDVAR)
@ -2850,10 +2850,10 @@ mjit::Compiler::generateMethod()
jsbytecode *next = &PC[JSOP_SETALIASEDVAR_LENGTH];
bool pop = JSOp(*next) == JSOP_POP && !analysis->jumpTarget(next);
ScopeCoordinate sc = ScopeCoordinate(PC);
if (script->bindings.bindingIsArg(sc.binding))
frame.storeArg(script->bindings.bindingToArg(sc.binding), pop);
if (script->bindings.bindingIsArg(sc.frameBinding))
frame.storeArg(script->bindings.bindingToArg(sc.frameBinding), pop);
else
frame.storeLocal(script->bindings.bindingToLocal(sc.binding), pop);
frame.storeLocal(script->bindings.bindingToLocal(sc.frameBinding), pop);
updateVarType();
if (pop) {

View File

@ -14,7 +14,8 @@ namespace js {
inline
ScopeCoordinate::ScopeCoordinate(jsbytecode *pc)
: hops(GET_UINT16(pc)), binding(GET_UINT16(pc + 2))
: hops(GET_UINT16(pc)), binding(GET_UINT16(pc + 2)),
frameBinding(GET_UINT16(pc + 8))
{
JS_ASSERT(JOF_OPTYPE(*pc) == JOF_SCOPECOORD);
}
@ -55,6 +56,58 @@ ScopeObject::setStackFrame(StackFrame *frame)
return setPrivate(frame);
}
inline const Value &
ScopeObject::aliasedVar(ScopeCoordinate sc)
{
/* XXX: all this is temporary until the last patch of 659577 */
StackFrame *fp = maybeStackFrame();
Bindings &bindings = fp->script()->bindings;
if (isCall()) {
JS_ASSERT(sc.binding == sc.frameBinding);
if (bindings.bindingIsArg(sc.binding)) {
unsigned arg = bindings.bindingToArg(sc.binding);
JS_ASSERT(fp->script()->formalLivesInCallObject(arg));
return fp->formalArg(arg);
}
unsigned var = bindings.bindingToLocal(sc.binding);
JS_ASSERT(fp->script()->varIsAliased(var));
return fp->localSlot(var);
}
unsigned var = bindings.bindingToLocal(sc.frameBinding);
fp = js_LiveFrameIfGenerator(fp);
JS_ASSERT(var == sc.binding + asClonedBlock().staticBlock().stackDepth() + fp->numFixed());
JS_ASSERT(asClonedBlock().staticBlock().isAliased(sc.binding));
return fp->localSlot(var);
}
inline void
ScopeObject::setAliasedVar(ScopeCoordinate sc, const Value &v)
{
/* XXX: all this is temporary until the last patch of 659577 */
StackFrame *fp = maybeStackFrame();
Bindings &bindings = fp->script()->bindings;
if (isCall()) {
JS_ASSERT(sc.binding == sc.frameBinding);
if (bindings.bindingIsArg(sc.binding)) {
unsigned arg = bindings.bindingToArg(sc.binding);
JS_ASSERT(fp->script()->formalLivesInCallObject(arg));
fp->formalArg(arg) = v;
} else {
unsigned var = bindings.bindingToLocal(sc.binding);
JS_ASSERT(fp->script()->varIsAliased(var));
fp->localSlot(var) = v;
}
} else {
unsigned var = bindings.bindingToLocal(sc.frameBinding);
fp = js_LiveFrameIfGenerator(fp);
JS_ASSERT(var == sc.binding + asClonedBlock().staticBlock().stackDepth() + fp->numFixed());
JS_ASSERT(asClonedBlock().staticBlock().isAliased(sc.binding));
fp->localSlot(var) = v;
}
}
/*static*/ inline size_t
ScopeObject::offsetOfEnclosingScope()
{

View File

@ -9,6 +9,7 @@
#define ScopeObject_h___
#include "jscntxt.h"
#include "jsiter.h"
#include "jsobj.h"
#include "jsweakmap.h"
@ -21,20 +22,17 @@ namespace js {
* given lexically-enclosing variable. A scope coordinate has two dimensions:
* - hops: the number of scope objects on the scope chain to skip
* - binding: which binding on the scope object
*
* XXX: Until bug 659577 lands, this is all for show and all ScopeCoordinates
* have hops fixed at 0 and 'binding' is just the js::Bindings binding for args
* and vars and the stack depth for let bindings. Thus, aliased-var access
* touches the StackFrame like it always did and 'binding' must be first
* converted to either an arg or local slot (using Bindings::bindingToLocal or
* bindingToArg). With bug 659577, ScopeObject will have a 'var' function that
* takes a ScopeCoordinate.
*/
struct ScopeCoordinate
{
uint16_t hops;
uint16_t binding;
/* XXX this will be removed with the last patch of bug 659577. */
uint16_t frameBinding;
inline ScopeCoordinate(jsbytecode *pc);
inline ScopeCoordinate() {}
};
inline JSAtom *
@ -97,6 +95,15 @@ class ScopeObject : public JSObject
inline JSObject &enclosingScope() const;
inline bool setEnclosingScope(JSContext *cx, HandleObject obj);
/*
* Get or set an aliased variable contained in this scope. Unaliased
* variables should instead access the StackFrame. Aliased variable access
* is primarily made through JOF_SCOPECOORD ops which is why these members
* take a ScopeCoordinate instead of just the slot index.
*/
inline const Value &aliasedVar(ScopeCoordinate sc);
inline void setAliasedVar(ScopeCoordinate sc, const Value &v);
/*
* The stack frame for this scope object, if the frame is still active.
* Note: these members may not be called for a StaticBlockObject or

View File

@ -286,6 +286,28 @@ StackFrame::actualArgsEnd() const
return formalArgs() + numActualArgs();
}
inline ScopeObject &
StackFrame::aliasedVarScope(ScopeCoordinate sc) const
{
JSObject *scope = &scopeChain()->asScope();
for (unsigned i = sc.hops; i; i--)
scope = &scope->asScope().enclosingScope();
#ifdef DEBUG
if (scope->isCall()) {
JS_ASSERT(scope->asCall() == callObj());
JS_ASSERT(scope->asCall().maybeStackFrame() == this);
} else {
StaticBlockObject &target = scope->asClonedBlock().staticBlock();
StaticBlockObject *b = &blockChain();
while (b != &target)
b = b->enclosingBlock();
}
#endif
return scope->asScope();
}
inline void
StackFrame::setScopeChain(JSObject &obj)
{

View File

@ -43,6 +43,8 @@ class ScriptFrameIter;
class AllFramesIter;
class ArgumentsObject;
class ScopeCoordinate;
class ScopeObject;
class StaticBlockObject;
#ifdef JS_METHODJIT
@ -847,6 +849,7 @@ class StackFrame
*/
inline HandleObject scopeChain() const;
inline ScopeObject &aliasedVarScope(ScopeCoordinate sc) const;
inline GlobalObject &global() const;
bool hasCallObj() const {