From f6ade302c621d27ccc41e8c552c9f608998a988f Mon Sep 17 00:00:00 2001 From: Jan de Mooij Date: Wed, 29 Oct 2014 13:49:20 +0100 Subject: [PATCH] Bug 1090491 - Don't allocate stack slots for aliased locals. r=luke --- js/src/frontend/BytecodeCompiler.cpp | 5 +- js/src/frontend/BytecodeEmitter.cpp | 67 ++++++++++++++++++-- js/src/frontend/BytecodeEmitter.h | 8 +++ js/src/frontend/Parser.cpp | 13 +++- js/src/jit-test/tests/debug/Frame-eval-24.js | 24 +++++++ js/src/jit/BaselineFrame.cpp | 2 +- js/src/jit/BaselineFrame.h | 5 +- js/src/jit/RematerializedFrame.h | 5 +- js/src/jsscript.cpp | 45 ++++++++++--- js/src/jsscript.h | 54 ++++++++++++++-- js/src/jsscriptinlines.h | 4 +- js/src/vm/ForkJoin.cpp | 2 +- js/src/vm/ScopeObject.cpp | 6 +- js/src/vm/Stack-inl.h | 13 ++-- js/src/vm/Stack.cpp | 19 +----- js/src/vm/Stack.h | 9 +-- 16 files changed, 210 insertions(+), 71 deletions(-) create mode 100644 js/src/jit-test/tests/debug/Frame-eval-24.js diff --git a/js/src/frontend/BytecodeCompiler.cpp b/js/src/frontend/BytecodeCompiler.cpp index 30452b87ae5..d3d359ecf8c 100644 --- a/js/src/frontend/BytecodeCompiler.cpp +++ b/js/src/frontend/BytecodeCompiler.cpp @@ -386,6 +386,9 @@ frontend::CompileScript(ExclusiveContext *cx, LifoAlloc *alloc, HandleObject sco if (!NameFunctions(cx, pn)) return nullptr; + if (!bce.updateLocalsToFrameSlots()) + return nullptr; + if (!EmitTree(cx, &bce, pn)) return nullptr; @@ -423,7 +426,7 @@ frontend::CompileScript(ExclusiveContext *cx, LifoAlloc *alloc, HandleObject sco // frame. InternalHandle bindings(script, &script->bindings); if (!Bindings::initWithTemporaryStorage(cx, bindings, 0, 0, 0, - pc->blockScopeDepth, nullptr)) + pc->blockScopeDepth, 0, 0, nullptr)) { return nullptr; } diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index b5567014c10..e130c8d63c2 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -131,6 +131,7 @@ BytecodeEmitter::BytecodeEmitter(BytecodeEmitter *parent, staticScope(sc->context), atomIndices(sc->context), firstLine(lineNum), + localsToFrameSlots_(sc->context), stackDepth(0), maxStackDepth(0), arrayCompDepth(0), emitLevel(0), @@ -155,6 +156,41 @@ BytecodeEmitter::init() return atomIndices.ensureMap(sc->context); } +bool +BytecodeEmitter::updateLocalsToFrameSlots() +{ + // Assign stack slots to unaliased locals (aliased locals are stored in the + // call object and don't need their own stack slots). We do this by filling + // a Vector that can be used to map a local to its stack slot. + + if (localsToFrameSlots_.length() == script->bindings.numLocals()) { + // CompileScript calls updateNumBlockScoped to update the block scope + // depth. Do nothing if the depth didn't change. + return true; + } + + localsToFrameSlots_.clear(); + + if (!localsToFrameSlots_.reserve(script->bindings.numLocals())) + return false; + + uint32_t slot = 0; + for (BindingIter bi(script); !bi.done(); bi++) { + if (bi->kind() == Binding::ARGUMENT) + continue; + + if (bi->aliased()) + localsToFrameSlots_.infallibleAppend(UINT32_MAX); + else + localsToFrameSlots_.infallibleAppend(slot++); + } + + for (size_t i = 0; i < script->bindings.numBlockScoped(); i++) + localsToFrameSlots_.infallibleAppend(slot++); + + return true; +} + static ptrdiff_t EmitCheck(ExclusiveContext *cx, BytecodeEmitter *bce, ptrdiff_t delta) { @@ -771,12 +807,18 @@ AllLocalsAliased(StaticBlockObject &obj) static bool ComputeAliasedSlots(ExclusiveContext *cx, BytecodeEmitter *bce, Handle blockObj) { + uint32_t numAliased = bce->script->bindings.numAliasedBodyLevelLocals(); + for (unsigned i = 0; i < blockObj->numVariables(); i++) { Definition *dn = blockObj->definitionParseNode(i); MOZ_ASSERT(dn->isDefn()); + + // blockIndexToLocalIndex returns the frame slot following the unaliased + // locals. We add numAliased so that the cookie's slot value comes after + // all (aliased and unaliased) body level locals. if (!dn->pn_cookie.set(bce->parser->tokenStream, dn->pn_cookie.level(), - blockObj->blockIndexToLocalIndex(dn->frameSlot()))) + numAliased + blockObj->blockIndexToLocalIndex(dn->frameSlot()))) { return false; } @@ -807,7 +849,9 @@ EmitInternedObjectOp(ExclusiveContext *cx, uint32_t index, JSOp op, BytecodeEmit static void ComputeLocalOffset(ExclusiveContext *cx, BytecodeEmitter *bce, Handle blockObj) { - unsigned nbodyfixed = bce->sc->isFunctionBox() ? bce->script->bindings.numBodyLevelLocals() : 0; + unsigned nbodyfixed = bce->sc->isFunctionBox() + ? bce->script->bindings.numUnaliasedBodyLevelLocals() + : 0; unsigned localOffset = nbodyfixed; if (bce->staticScope) { @@ -1093,6 +1137,12 @@ EmitUnaliasedVarOp(ExclusiveContext *cx, JSOp op, uint32_t slot, MaybeCheckLexic MOZ_ASSERT(JOF_OPTYPE(op) != JOF_SCOPECOORD); if (IsLocalOp(op)) { + // Only unaliased locals have stack slots assigned to them. Convert the + // var index (which includes unaliased and aliased locals) to the stack + // slot index. + MOZ_ASSERT(bce->localsToFrameSlots_[slot] <= slot); + slot = bce->localsToFrameSlots_[slot]; + if (checkLexical) { MOZ_ASSERT(op != JSOP_INITLEXICAL); if (!EmitLocalOp(cx, bce, JSOP_CHECKLEXICAL, slot)) @@ -1300,6 +1350,7 @@ EmitAliasedVarOp(ExclusiveContext *cx, JSOp op, ParseNode *pn, BytecodeEmitter * MOZ_ASSERT_IF(bce->sc->isFunctionBox(), local <= bceOfDef->script->bindings.numLocals()); MOZ_ASSERT(bceOfDef->staticScope->is()); Rooted b(cx, &bceOfDef->staticScope->as()); + local = bceOfDef->localsToFrameSlots_[local]; while (local < b->localOffset()) { if (b->needsClone()) skippedScopes++; @@ -2569,7 +2620,12 @@ InitializeBlockScopedLocalsFromStack(ExclusiveContext *cx, BytecodeEmitter *bce, if (!EmitAliasedVarOp(cx, JSOP_INITALIASEDLEXICAL, sc, DontCheckLexical, bce)) return false; } else { - unsigned local = blockObj->blockIndexToLocalIndex(i - 1); + // blockIndexToLocalIndex returns the slot index after the unaliased + // locals stored in the frame. EmitUnaliasedVarOp expects the slot index + // to include both unaliased and aliased locals, so we have to add the + // number of aliased locals. + uint32_t numAliased = bce->script->bindings.numAliasedBodyLevelLocals(); + unsigned local = blockObj->blockIndexToLocalIndex(i - 1) + numAliased; if (!EmitUnaliasedVarOp(cx, JSOP_INITLEXICAL, local, DontCheckLexical, bce)) return false; } @@ -2949,6 +3005,9 @@ BytecodeEmitter::isRunOnceLambda() bool frontend::EmitFunctionScript(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *body) { + if (!bce->updateLocalsToFrameSlots()) + return false; + /* * IonBuilder has assumptions about what may occur immediately after * script->main (e.g., in the case of destructuring params). Thus, put the @@ -5263,7 +5322,7 @@ EmitFunc(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn) bi++; MOZ_ASSERT(bi->kind() == Binding::VARIABLE || bi->kind() == Binding::CONSTANT || bi->kind() == Binding::ARGUMENT); - MOZ_ASSERT(bi.frameIndex() < JS_BIT(20)); + MOZ_ASSERT(bi.argOrLocalIndex() < JS_BIT(20)); #endif pn->pn_index = index; if (!EmitIndexOp(cx, JSOP_LAMBDA, index, bce)) diff --git a/js/src/frontend/BytecodeEmitter.h b/js/src/frontend/BytecodeEmitter.h index 55b22ab0c43..ccc775404b8 100644 --- a/js/src/frontend/BytecodeEmitter.h +++ b/js/src/frontend/BytecodeEmitter.h @@ -116,6 +116,13 @@ struct BytecodeEmitter OwnedAtomIndexMapPtr atomIndices; /* literals indexed for mapping */ unsigned firstLine; /* first line, for JSScript::initFromEmitter */ + /* + * Only unaliased locals have stack slots assigned to them. This vector is + * used to map a local index (which includes unaliased and aliased locals) + * to its stack slot index. + */ + Vector localsToFrameSlots_; + int32_t stackDepth; /* current stack depth in script frame */ uint32_t maxStackDepth; /* maximum stack depth so far */ @@ -178,6 +185,7 @@ struct BytecodeEmitter bool insideEval, HandleScript evalCaller, bool hasGlobalScope, uint32_t lineNum, EmitterMode emitterMode = Normal); bool init(); + bool updateLocalsToFrameSlots(); bool isAliasedName(ParseNode *pn); diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 4c90e2eb8dc..f884e872013 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -320,7 +320,8 @@ ParseContext::popLetDecl(JSAtom *atom) template static void -AppendPackedBindings(const ParseContext *pc, const DeclVector &vec, Binding *dst) +AppendPackedBindings(const ParseContext *pc, const DeclVector &vec, Binding *dst, + uint32_t *numUnaliased = nullptr) { for (size_t i = 0; i < vec.length(); ++i, ++dst) { Definition *dn = vec[i]; @@ -357,6 +358,8 @@ AppendPackedBindings(const ParseContext *pc, const DeclVector &vec pc->decls().lookupFirst(name) == dn); *dst = Binding(name, kind, aliased); + if (!aliased && numUnaliased) + ++*numUnaliased; } } @@ -392,13 +395,17 @@ ParseContext::generateFunctionBindings(ExclusiveContext *cx, Token return false; } + uint32_t numUnaliasedVars = 0; + uint32_t numUnaliasedBodyLevelLexicals = 0; + AppendPackedBindings(this, args_, packedBindings); - AppendPackedBindings(this, vars_, packedBindings + args_.length()); + AppendPackedBindings(this, vars_, packedBindings + args_.length(), &numUnaliasedVars); AppendPackedBindings(this, bodyLevelLexicals_, - packedBindings + args_.length() + vars_.length()); + packedBindings + args_.length() + vars_.length(), &numUnaliasedBodyLevelLexicals); return Bindings::initWithTemporaryStorage(cx, bindings, args_.length(), vars_.length(), bodyLevelLexicals_.length(), blockScopeDepth, + numUnaliasedVars, numUnaliasedBodyLevelLexicals, packedBindings); } diff --git a/js/src/jit-test/tests/debug/Frame-eval-24.js b/js/src/jit-test/tests/debug/Frame-eval-24.js new file mode 100644 index 00000000000..b731771c64b --- /dev/null +++ b/js/src/jit-test/tests/debug/Frame-eval-24.js @@ -0,0 +1,24 @@ +// Make sure the getVariable/setVariable/eval functions work correctly with +// unaliased locals. +var g = newGlobal(); +g.eval('\ +function g() { debugger; };\ +function f(arg) {\ + var y = arg - 3;\ + var a1 = 1;\ + var a2 = 1;\ + var b = arg + 9;\ + var z = function() { return a1 + a2; };\ + g();\ +};'); + +var dbg = new Debugger(g); + +dbg.onDebuggerStatement = function handleDebugger(frame) { + assertEq(frame.older.eval("y + b").return, 26); + assertEq(frame.older.environment.getVariable("y"), 7); + frame.older.environment.setVariable("b", 4); + assertEq(frame.older.eval("y + b").return, 11); +}; + +g.f(10); diff --git a/js/src/jit/BaselineFrame.cpp b/js/src/jit/BaselineFrame.cpp index a7cc1622491..18eef4d364c 100644 --- a/js/src/jit/BaselineFrame.cpp +++ b/js/src/jit/BaselineFrame.cpp @@ -93,7 +93,7 @@ BaselineFrame::trace(JSTracer *trc, JitFrameIterator &frameIterator) // Clear dead block-scoped locals. while (nfixed > nlivefixed) - unaliasedLocal(--nfixed, DONT_CHECK_ALIASING).setMagic(JS_UNINITIALIZED_LEXICAL); + unaliasedLocal(--nfixed).setMagic(JS_UNINITIALIZED_LEXICAL); // Mark live locals. MarkLocals(this, trc, 0, nlivefixed); diff --git a/js/src/jit/BaselineFrame.h b/js/src/jit/BaselineFrame.h index 501feb5cd3f..3cbfab62135 100644 --- a/js/src/jit/BaselineFrame.h +++ b/js/src/jit/BaselineFrame.h @@ -188,11 +188,8 @@ class BaselineFrame return argv()[i]; } - Value &unaliasedLocal(uint32_t i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING) const { + Value &unaliasedLocal(uint32_t i) const { MOZ_ASSERT(i < script()->nfixed()); -#ifdef DEBUG - CheckLocalUnaliased(checkAliasing, script(), i); -#endif return *valueSlot(i); } diff --git a/js/src/jit/RematerializedFrame.h b/js/src/jit/RematerializedFrame.h index 783ccf12f07..46a8df4240d 100644 --- a/js/src/jit/RematerializedFrame.h +++ b/js/src/jit/RematerializedFrame.h @@ -149,11 +149,8 @@ class RematerializedFrame MOZ_ASSERT(i < script()->nfixed()); return locals()[i]; } - Value &unaliasedLocal(unsigned i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING) { + Value &unaliasedLocal(unsigned i) { MOZ_ASSERT(i < script()->nfixed()); -#ifdef DEBUG - CheckLocalUnaliased(checkAliasing, script(), i); -#endif return locals()[i]; } Value &unaliasedFormal(unsigned i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING) { diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index 2d4c12037ea..9c64f062e1d 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -64,19 +64,25 @@ using mozilla::RotateLeft; typedef Rooted RootedGlobalObject; /* static */ uint32_t -Bindings::argumentsVarIndex(ExclusiveContext *cx, InternalBindingsHandle bindings) +Bindings::argumentsVarIndex(ExclusiveContext *cx, InternalBindingsHandle bindings, + uint32_t *unaliasedSlot) { HandlePropertyName arguments = cx->names().arguments; BindingIter bi(bindings); while (bi->name() != arguments) bi++; - return bi.frameIndex(); + + if (unaliasedSlot) + *unaliasedSlot = bi->aliased() ? UINT32_MAX : bi.frameIndex(); + + return bi.localIndex(); } bool Bindings::initWithTemporaryStorage(ExclusiveContext *cx, InternalBindingsHandle self, uint32_t numArgs, uint32_t numVars, uint32_t numBodyLevelLexicals, uint32_t numBlockScoped, + uint32_t numUnaliasedVars, uint32_t numUnaliasedBodyLevelLexicals, Binding *bindingArray) { MOZ_ASSERT(!self->callObjShape_); @@ -92,11 +98,16 @@ Bindings::initWithTemporaryStorage(ExclusiveContext *cx, InternalBindingsHandle MOZ_ASSERT(totalSlots <= LOCALNO_LIMIT); MOZ_ASSERT(UINT32_MAX - numArgs >= totalSlots); + MOZ_ASSERT(numUnaliasedVars <= numVars); + MOZ_ASSERT(numUnaliasedBodyLevelLexicals <= numBodyLevelLexicals); + self->bindingArrayAndFlag_ = uintptr_t(bindingArray) | TEMPORARY_STORAGE_BIT; self->numArgs_ = numArgs; self->numVars_ = numVars; self->numBodyLevelLexicals_ = numBodyLevelLexicals; self->numBlockScoped_ = numBlockScoped; + self->numUnaliasedVars_ = numUnaliasedVars; + self->numUnaliasedBodyLevelLexicals_ = numUnaliasedBodyLevelLexicals; // Get the initial shape to use when creating CallObjects for this script. // After creation, a CallObject's shape may change completely (via direct eval() or @@ -123,7 +134,7 @@ Bindings::initWithTemporaryStorage(ExclusiveContext *cx, InternalBindingsHandle if (numBodyLevelLexicals > 0 && nslots < aliasedBodyLevelLexicalBegin && bi->kind() == Binding::VARIABLE && - bi.frameIndex() >= numVars) + bi.localIndex() >= numVars) { aliasedBodyLevelLexicalBegin = nslots; } @@ -215,7 +226,10 @@ Bindings::clone(JSContext *cx, InternalBindingsHandle self, * the source's bindingArray directly. */ if (!initWithTemporaryStorage(cx, self, src.numArgs(), src.numVars(), - src.numBodyLevelLexicals(), src.numBlockScoped(), + src.numBodyLevelLexicals(), + src.numBlockScoped(), + src.numUnaliasedVars(), + src.numUnaliasedBodyLevelLexicals(), src.bindingArray())) { return false; @@ -234,7 +248,9 @@ GCMethods::initial() template static bool XDRScriptBindings(XDRState *xdr, LifoAllocScope &las, uint16_t numArgs, uint32_t numVars, - uint16_t numBodyLevelLexicals, uint16_t numBlockScoped, HandleScript script) + uint16_t numBodyLevelLexicals, uint16_t numBlockScoped, + uint32_t numUnaliasedVars, uint16_t numUnaliasedBodyLevelLexicals, + HandleScript script) { JSContext *cx = xdr->cx(); @@ -281,6 +297,7 @@ XDRScriptBindings(XDRState *xdr, LifoAllocScope &las, uint16_t numArgs, ui InternalBindingsHandle bindings(script, &script->bindings); if (!Bindings::initWithTemporaryStorage(cx, bindings, numArgs, numVars, numBodyLevelLexicals, numBlockScoped, + numUnaliasedVars, numUnaliasedBodyLevelLexicals, bindingArray)) { return false; @@ -599,6 +616,8 @@ js::XDRScript(XDRState *xdr, HandleObject enclosingScope, HandleScript enc uint16_t nblocklocals = 0; uint16_t nbodylevellexicals = 0; uint32_t nvars = 0; + uint32_t nunaliasedvars = 0; + uint16_t nunaliasedbodylevellexicals = 0; if (mode == XDR_ENCODE) { script = scriptp.get(); MOZ_ASSERT_IF(enclosingScript, enclosingScript->compartment() == script->compartment()); @@ -607,6 +626,8 @@ js::XDRScript(XDRState *xdr, HandleObject enclosingScope, HandleScript enc nblocklocals = script->bindings.numBlockScoped(); nbodylevellexicals = script->bindings.numBodyLevelLexicals(); nvars = script->bindings.numVars(); + nunaliasedvars = script->bindings.numUnaliasedVars(); + nunaliasedbodylevellexicals = script->bindings.numUnaliasedBodyLevelLexicals(); } if (!xdr->codeUint16(&nargs)) return false; @@ -616,6 +637,10 @@ js::XDRScript(XDRState *xdr, HandleObject enclosingScope, HandleScript enc return false; if (!xdr->codeUint32(&nvars)) return false; + if (!xdr->codeUint32(&nunaliasedvars)) + return false; + if (!xdr->codeUint16(&nunaliasedbodylevellexicals)) + return false; if (mode == XDR_ENCODE) length = script->length(); @@ -759,7 +784,8 @@ js::XDRScript(XDRState *xdr, HandleObject enclosingScope, HandleScript enc /* JSScript::partiallyInit assumes script->bindings is fully initialized. */ LifoAllocScope las(&cx->tempLifoAlloc()); - if (!XDRScriptBindings(xdr, las, nargs, nvars, nbodylevellexicals, nblocklocals, script)) + if (!XDRScriptBindings(xdr, las, nargs, nvars, nbodylevellexicals, nblocklocals, + nunaliasedvars, nunaliasedbodylevellexicals, script)) return false; if (mode == XDR_DECODE) { @@ -3518,7 +3544,8 @@ js::SetFrameArgumentsObject(JSContext *cx, AbstractFramePtr frame, */ InternalBindingsHandle bindings(script, &script->bindings); - const uint32_t var = Bindings::argumentsVarIndex(cx, bindings); + uint32_t unaliasedSlot; + const uint32_t var = Bindings::argumentsVarIndex(cx, bindings, &unaliasedSlot); if (script->varIsAliased(var)) { /* @@ -3537,8 +3564,8 @@ js::SetFrameArgumentsObject(JSContext *cx, AbstractFramePtr frame, if (IsOptimizedPlaceholderMagicValue(frame.callObj().as().aliasedVar(ScopeCoordinate(pc)))) frame.callObj().as().setAliasedVar(cx, ScopeCoordinate(pc), cx->names().arguments, ObjectValue(*argsobj)); } else { - if (IsOptimizedPlaceholderMagicValue(frame.unaliasedLocal(var))) - frame.unaliasedLocal(var) = ObjectValue(*argsobj); + if (IsOptimizedPlaceholderMagicValue(frame.unaliasedLocal(unaliasedSlot))) + frame.unaliasedLocal(unaliasedSlot) = ObjectValue(*argsobj); } } diff --git a/js/src/jsscript.h b/js/src/jsscript.h index 8a1175f97a7..e1aa95fb76a 100644 --- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -187,7 +187,9 @@ class Bindings uint16_t numBlockScoped_; uint16_t numBodyLevelLexicals_; uint16_t aliasedBodyLevelLexicalBegin_; + uint16_t numUnaliasedBodyLevelLexicals_; uint32_t numVars_; + uint32_t numUnaliasedVars_; #if JS_BITS_PER_WORD == 32 // Bindings is allocated inline inside JSScript, which needs to be @@ -227,6 +229,7 @@ class Bindings static bool initWithTemporaryStorage(ExclusiveContext *cx, InternalBindingsHandle self, uint32_t numArgs, uint32_t numVars, uint32_t numBodyLevelLexicals, uint32_t numBlockScoped, + uint32_t numUnaliasedVars, uint32_t numUnaliasedBodyLevelLexicals, Binding *bindingArray); // CompileScript parses and compiles one statement at a time, but the result @@ -257,10 +260,16 @@ class Bindings uint32_t numBodyLevelLexicals() const { return numBodyLevelLexicals_; } uint32_t numBlockScoped() const { return numBlockScoped_; } uint32_t numBodyLevelLocals() const { return numVars_ + numBodyLevelLexicals_; } + uint32_t numUnaliasedBodyLevelLocals() const { return numUnaliasedVars_ + numUnaliasedBodyLevelLexicals_; } + uint32_t numAliasedBodyLevelLocals() const { return numBodyLevelLocals() - numUnaliasedBodyLevelLocals(); } uint32_t numLocals() const { return numVars() + numBodyLevelLexicals() + numBlockScoped(); } + uint32_t numUnaliasedLocals() const { return numUnaliasedVars() + numUnaliasedBodyLevelLexicals() + numBlockScoped(); } uint32_t lexicalBegin() const { return numArgs() + numVars(); } uint32_t aliasedBodyLevelLexicalBegin() const { return aliasedBodyLevelLexicalBegin_; } + uint32_t numUnaliasedVars() const { return numUnaliasedVars_; } + uint32_t numUnaliasedBodyLevelLexicals() const { return numUnaliasedBodyLevelLexicals_; } + // Return the size of the bindingArray. uint32_t count() const { return numArgs() + numVars() + numBodyLevelLexicals(); } @@ -268,7 +277,8 @@ class Bindings Shape *callObjShape() const { return callObjShape_; } /* Convenience method to get the var index of 'arguments'. */ - static uint32_t argumentsVarIndex(ExclusiveContext *cx, InternalBindingsHandle); + static uint32_t argumentsVarIndex(ExclusiveContext *cx, InternalBindingsHandle, + uint32_t *unaliasedSlot = nullptr); /* Return whether the binding at bindingIndex is aliased. */ bool bindingIsAliased(uint32_t bindingIndex); @@ -1053,20 +1063,20 @@ class JSScript : public js::gc::TenuredCell // The fixed part of a stack frame is comprised of vars (in function code) // and block-scoped locals (in all kinds of code). size_t nfixed() const { - return function_ ? bindings.numLocals() : bindings.numBlockScoped(); + return function_ ? bindings.numUnaliasedLocals() : bindings.numBlockScoped(); } // Number of fixed slots reserved for vars. Only nonzero for function // code. size_t nfixedvars() const { - return function_ ? bindings.numVars() : 0; + return function_ ? bindings.numUnaliasedVars() : 0; } // Number of fixed slots reserved for body-level lexicals and vars. This // value minus nfixedvars() is the number of body-level lexicals. Only // nonzero for function code. size_t nbodyfixed() const { - return function_ ? bindings.numBodyLevelLocals() : 0; + return function_ ? bindings.numUnaliasedBodyLevelLocals() : 0; } // Aliases for clarity when dealing with lexical slots. @@ -1684,22 +1694,52 @@ class BindingIter { const InternalBindingsHandle bindings_; uint32_t i_; + uint32_t unaliasedLocal_; friend class Bindings; public: - explicit BindingIter(const InternalBindingsHandle &bindings) : bindings_(bindings), i_(0) {} - explicit BindingIter(const HandleScript &script) : bindings_(script, &script->bindings), i_(0) {} + explicit BindingIter(const InternalBindingsHandle &bindings) + : bindings_(bindings), i_(0), unaliasedLocal_(0) {} + explicit BindingIter(const HandleScript &script) + : bindings_(script, &script->bindings), i_(0), unaliasedLocal_(0) {} bool done() const { return i_ == bindings_->count(); } operator bool() const { return !done(); } - void operator++(int) { MOZ_ASSERT(!done()); i_++; } BindingIter &operator++() { (*this)++; return *this; } + void operator++(int) { + MOZ_ASSERT(!done()); + const Binding &binding = **this; + if (binding.kind() != Binding::ARGUMENT && !binding.aliased()) + unaliasedLocal_++; + i_++; + } + + // Stack slots are assigned to arguments and unaliased locals. frameIndex() + // returns the slot index. It's invalid to call this method when the + // iterator is stopped on an aliased local, as it has no stack slot. uint32_t frameIndex() const { + MOZ_ASSERT(!done()); + if (i_ < bindings_->numArgs()) + return i_; + MOZ_ASSERT(!(*this)->aliased()); + return unaliasedLocal_; + } + uint32_t argIndex() const { + MOZ_ASSERT(!done()); + MOZ_ASSERT(i_ < bindings_->numArgs()); + return i_; + } + uint32_t argOrLocalIndex() const { MOZ_ASSERT(!done()); return i_ < bindings_->numArgs() ? i_ : i_ - bindings_->numArgs(); } + uint32_t localIndex() const { + MOZ_ASSERT(!done()); + MOZ_ASSERT(i_ >= bindings_->numArgs()); + return i_ - bindings_->numArgs(); + } const Binding &operator*() const { MOZ_ASSERT(!done()); return bindings_->bindingArray()[i_]; } const Binding *operator->() const { MOZ_ASSERT(!done()); return &bindings_->bindingArray()[i_]; } diff --git a/js/src/jsscriptinlines.h b/js/src/jsscriptinlines.h index bfecfc3c26b..574556c2eb3 100644 --- a/js/src/jsscriptinlines.h +++ b/js/src/jsscriptinlines.h @@ -23,7 +23,9 @@ namespace js { inline Bindings::Bindings() : callObjShape_(nullptr), bindingArrayAndFlag_(TEMPORARY_STORAGE_BIT), - numArgs_(0), numBlockScoped_(0), numVars_(0) + numArgs_(0), numBlockScoped_(0), + numBodyLevelLexicals_(0), numUnaliasedBodyLevelLexicals_(0), + numVars_(0), numUnaliasedVars_(0) {} inline diff --git a/js/src/vm/ForkJoin.cpp b/js/src/vm/ForkJoin.cpp index e057b6b3450..dd828fd771a 100644 --- a/js/src/vm/ForkJoin.cpp +++ b/js/src/vm/ForkJoin.cpp @@ -1130,7 +1130,7 @@ ForkJoinOperation::reportBailoutWarnings() else arg = frame->unaliasedActual(i, DONT_CHECK_ALIASING); } else { - arg = frame->unaliasedLocal(i - frame->numFormalArgs(), DONT_CHECK_ALIASING); + arg = frame->unaliasedLocal(i - frame->numFormalArgs()); } JSAutoByteString valueBytes; diff --git a/js/src/vm/ScopeObject.cpp b/js/src/vm/ScopeObject.cpp index 150a546ef97..d1bd651eeed 100644 --- a/js/src/vm/ScopeObject.cpp +++ b/js/src/vm/ScopeObject.cpp @@ -1349,10 +1349,10 @@ class DebugScopeProxy : public BaseProxyHandler return true; if (bi->kind() == Binding::VARIABLE || bi->kind() == Binding::CONSTANT) { - uint32_t i = bi.frameIndex(); - if (script->bodyLevelLocalIsAliased(i)) + if (script->bodyLevelLocalIsAliased(bi.localIndex())) return true; + uint32_t i = bi.frameIndex(); if (maybeLiveScope) { AbstractFramePtr frame = maybeLiveScope->frame(); if (action == GET) @@ -1373,7 +1373,7 @@ class DebugScopeProxy : public BaseProxyHandler } } else { MOZ_ASSERT(bi->kind() == Binding::ARGUMENT); - unsigned i = bi.frameIndex(); + unsigned i = bi.argIndex(); if (script->formalIsAliased(i)) return true; diff --git a/js/src/vm/Stack-inl.h b/js/src/vm/Stack-inl.h index 78e51ef7195..35615b8b514 100644 --- a/js/src/vm/Stack-inl.h +++ b/js/src/vm/Stack-inl.h @@ -118,12 +118,9 @@ InterpreterFrame::unaliasedVar(uint32_t i, MaybeCheckAliasing checkAliasing) } inline Value & -InterpreterFrame::unaliasedLocal(uint32_t i, MaybeCheckAliasing checkAliasing) +InterpreterFrame::unaliasedLocal(uint32_t i) { MOZ_ASSERT(i < script()->nfixed()); -#ifdef DEBUG - CheckLocalUnaliased(checkAliasing, script(), i); -#endif return slots()[i]; } @@ -502,13 +499,13 @@ AbstractFramePtr::unaliasedVar(uint32_t i, MaybeCheckAliasing checkAliasing) } inline Value & -AbstractFramePtr::unaliasedLocal(uint32_t i, MaybeCheckAliasing checkAliasing) +AbstractFramePtr::unaliasedLocal(uint32_t i) { if (isInterpreterFrame()) - return asInterpreterFrame()->unaliasedLocal(i, checkAliasing); + return asInterpreterFrame()->unaliasedLocal(i); if (isBaselineFrame()) - return asBaselineFrame()->unaliasedLocal(i, checkAliasing); - return asRematerializedFrame()->unaliasedLocal(i, checkAliasing); + return asBaselineFrame()->unaliasedLocal(i); + return asRematerializedFrame()->unaliasedLocal(i); } inline Value & diff --git a/js/src/vm/Stack.cpp b/js/src/vm/Stack.cpp index afd634c6760..13abb0e9dc8 100644 --- a/js/src/vm/Stack.cpp +++ b/js/src/vm/Stack.cpp @@ -368,7 +368,7 @@ InterpreterFrame::markValues(JSTracer *trc, Value *sp, jsbytecode *pc) // Clear dead block-scoped locals. while (nfixed > nlivefixed) - unaliasedLocal(--nfixed, DONT_CHECK_ALIASING).setMagic(JS_UNINITIALIZED_LEXICAL); + unaliasedLocal(--nfixed).setMagic(JS_UNINITIALIZED_LEXICAL); // Mark live locals. markValues(trc, 0, nlivefixed); @@ -1348,23 +1348,6 @@ AbstractFramePtr::hasPushedSPSFrame() const return asBaselineFrame()->hasPushedSPSFrame(); } -#ifdef DEBUG -void -js::CheckLocalUnaliased(MaybeCheckAliasing checkAliasing, JSScript *script, uint32_t i) -{ - if (!checkAliasing) - return; - - MOZ_ASSERT(i < script->nfixed()); - if (i < script->bindings.numVars()) { - MOZ_ASSERT(!script->varIsAliased(i)); - } else { - // FIXME: The callers of this function do not easily have the PC of the - // current frame, and so they do not know the block scope. - } -} -#endif - jit::JitActivation::JitActivation(JSContext *cx, bool active) : Activation(cx, Jit), active_(active), diff --git a/js/src/vm/Stack.h b/js/src/vm/Stack.h index 9ef20fea422..e58aeba5ea5 100644 --- a/js/src/vm/Stack.h +++ b/js/src/vm/Stack.h @@ -68,11 +68,6 @@ enum MaybeCheckLexical { CheckLexical = true, DontCheckLexical = false }; /*****************************************************************************/ -#ifdef DEBUG -extern void -CheckLocalUnaliased(MaybeCheckAliasing checkAliasing, JSScript *script, uint32_t i); -#endif - namespace jit { class BaselineFrame; class RematerializedFrame; @@ -217,7 +212,7 @@ class AbstractFramePtr inline bool copyRawFrameSlots(AutoValueVector *vec) const; inline Value &unaliasedVar(uint32_t i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING); - inline Value &unaliasedLocal(uint32_t i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING); + inline Value &unaliasedLocal(uint32_t i); inline Value &unaliasedFormal(unsigned i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING); inline Value &unaliasedActual(unsigned i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING); template inline void unaliasedForEachActual(JSContext *cx, Op op); @@ -517,7 +512,7 @@ class InterpreterFrame */ inline Value &unaliasedVar(uint32_t i, MaybeCheckAliasing = CHECK_ALIASING); - inline Value &unaliasedLocal(uint32_t i, MaybeCheckAliasing = CHECK_ALIASING); + inline Value &unaliasedLocal(uint32_t i); bool hasArgs() const { return isNonEvalFunctionFrame(); } inline Value &unaliasedFormal(unsigned i, MaybeCheckAliasing = CHECK_ALIASING);