diff --git a/js/src/frontend/BytecodeControlStructures.cpp b/js/src/frontend/BytecodeControlStructures.cpp new file mode 100644 index 0000000000..281f1e9cfd --- /dev/null +++ b/js/src/frontend/BytecodeControlStructures.cpp @@ -0,0 +1,84 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "frontend/BytecodeControlStructures.h" + +#include "frontend/BytecodeEmitter.h" +#include "frontend/EmitterScope.h" + +using namespace js; +using namespace js::frontend; + +NestableControl::NestableControl(BytecodeEmitter* bce, StatementKind kind) + : Nestable(&bce->innermostNestableControl), + kind_(kind), + emitterScope_(bce->innermostEmitterScopeNoCheck()) +{} + +BreakableControl::BreakableControl(BytecodeEmitter* bce, StatementKind kind) + : NestableControl(bce, kind) +{ + MOZ_ASSERT(is()); +} + +bool +BreakableControl::patchBreaks(BytecodeEmitter* bce) +{ + return bce->emitJumpTargetAndPatch(breaks); +} + +LabelControl::LabelControl(BytecodeEmitter* bce, JSAtom* label, ptrdiff_t startOffset) + : BreakableControl(bce, StatementKind::Label), + label_(bce->cx, label), + startOffset_(startOffset) +{} + +LoopControl::LoopControl(BytecodeEmitter* bce, StatementKind loopKind) + : BreakableControl(bce, loopKind), + tdzCache_(bce), + continueTarget({ -1 }) +{ + MOZ_ASSERT(is()); + + LoopControl* enclosingLoop = findNearest(enclosing()); + + stackDepth_ = bce->stackDepth; + loopDepth_ = enclosingLoop ? enclosingLoop->loopDepth_ + 1 : 1; + + int loopSlots; + if (loopKind == StatementKind::Spread || loopKind == StatementKind::ForOfLoop) + loopSlots = 3; + else if (loopKind == StatementKind::ForInLoop) + loopSlots = 2; + else + loopSlots = 0; + + MOZ_ASSERT(loopSlots <= stackDepth_); + + if (enclosingLoop) { + canIonOsr_ = (enclosingLoop->canIonOsr_ && + stackDepth_ == enclosingLoop->stackDepth_ + loopSlots); + } else { + canIonOsr_ = stackDepth_ == loopSlots; + } +} + +bool +LoopControl::patchBreaksAndContinues(BytecodeEmitter* bce) +{ + MOZ_ASSERT(continueTarget.offset != -1); + if (!patchBreaks(bce)) + return false; + bce->patchJumpsToTarget(continues, continueTarget); + return true; +} + +TryFinallyControl::TryFinallyControl(BytecodeEmitter* bce, StatementKind kind) + : NestableControl(bce, kind), + emittingSubroutine_(false) +{ + MOZ_ASSERT(is()); +} diff --git a/js/src/frontend/BytecodeControlStructures.h b/js/src/frontend/BytecodeControlStructures.h new file mode 100644 index 0000000000..37681c8837 --- /dev/null +++ b/js/src/frontend/BytecodeControlStructures.h @@ -0,0 +1,175 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef frontend_BytecodeControlStructures_h +#define frontend_BytecodeControlStructures_h + +#include "mozilla/Attributes.h" + +#include +#include + +#include "ds/Nestable.h" +#include "frontend/JumpList.h" +#include "frontend/SharedContext.h" +#include "frontend/TDZCheckCache.h" +#include "gc/Rooting.h" +#include "vm/String.h" + +namespace js { +namespace frontend { + +struct BytecodeEmitter; +class EmitterScope; + +class NestableControl : public Nestable +{ + StatementKind kind_; + + // The innermost scope when this was pushed. + EmitterScope* emitterScope_; + + protected: + NestableControl(BytecodeEmitter* bce, StatementKind kind); + + public: + using Nestable::enclosing; + using Nestable::findNearest; + + StatementKind kind() const { + return kind_; + } + + EmitterScope* emitterScope() const { + return emitterScope_; + } + + template + bool is() const; + + template + T& as() { + MOZ_ASSERT(this->is()); + return static_cast(*this); + } +}; + +class BreakableControl : public NestableControl +{ + public: + // Offset of the last break. + JumpList breaks; + + BreakableControl(BytecodeEmitter* bce, StatementKind kind); + + MOZ_MUST_USE bool patchBreaks(BytecodeEmitter* bce); +}; +template <> +inline bool +NestableControl::is() const +{ + return StatementKindIsUnlabeledBreakTarget(kind_) || kind_ == StatementKind::Label; +} + +class LabelControl : public BreakableControl +{ + RootedAtom label_; + + // The code offset when this was pushed. Used for effectfulness checking. + ptrdiff_t startOffset_; + + public: + LabelControl(BytecodeEmitter* bce, JSAtom* label, ptrdiff_t startOffset); + + HandleAtom label() const { + return label_; + } + + ptrdiff_t startOffset() const { + return startOffset_; + } +}; +template <> +inline bool +NestableControl::is() const +{ + return kind_ == StatementKind::Label; +} + +class LoopControl : public BreakableControl +{ + // Loops' children are emitted in dominance order, so they can always + // have a TDZCheckCache. + TDZCheckCache tdzCache_; + + // Stack depth when this loop was pushed on the control stack. + int32_t stackDepth_; + + // The loop nesting depth. Used as a hint to Ion. + uint32_t loopDepth_; + + // Can we OSR into Ion from here? True unless there is non-loop state on the stack. + bool canIonOsr_; + + public: + // The target of continue statement jumps, e.g., the update portion of a + // for(;;) loop. + JumpTarget continueTarget; + + // Offset of the last continue in the loop. + JumpList continues; + + LoopControl(BytecodeEmitter* bce, StatementKind loopKind); + + uint32_t loopDepth() const { + return loopDepth_; + } + + bool canIonOsr() const { + return canIonOsr_; + } + + MOZ_MUST_USE bool patchBreaksAndContinues(BytecodeEmitter* bce); +}; +template <> +inline bool +NestableControl::is() const +{ + return StatementKindIsLoop(kind_); +} + +class TryFinallyControl : public NestableControl +{ + bool emittingSubroutine_; + + public: + // The subroutine when emitting a finally block. + JumpList gosubs; + + // Offset of the last catch guard, if any. + JumpList guardJump; + + TryFinallyControl(BytecodeEmitter* bce, StatementKind kind); + + void setEmittingSubroutine() { + emittingSubroutine_ = true; + } + + bool emittingSubroutine() const { + return emittingSubroutine_; + } +}; +template <> +inline bool +NestableControl::is() const +{ + return kind_ == StatementKind::Try || kind_ == StatementKind::Finally; +} + +} /* namespace frontend */ +} /* namespace js */ + +#endif /* frontend_BytecodeControlStructures_h */ diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index d309b4d6b2..85e123b7c4 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -28,6 +28,7 @@ #include "jsutil.h" #include "ds/Nestable.h" +#include "frontend/BytecodeControlStructures.h" #include "frontend/CallOrNewEmitter.h" #include "frontend/ElemOpEmitter.h" #include "frontend/EmitterScope.h" @@ -62,11 +63,6 @@ using mozilla::NumberIsInt32; using mozilla::PodCopy; using mozilla::Some; -class BreakableControl; -class LabelControl; -class LoopControl; -class ForOfLoopControl; -class TryFinallyControl; class OptionalEmitter; static bool @@ -89,222 +85,6 @@ GetCallArgsAndCount(ParseNode* callNode, ParseNode** argumentNode) return callNode->pn_count - 1; } -class BytecodeEmitter::NestableControl : public Nestable -{ - StatementKind kind_; - - // The innermost scope when this was pushed. - EmitterScope* emitterScope_; - - protected: - NestableControl(BytecodeEmitter* bce, StatementKind kind) - : Nestable(&bce->innermostNestableControl), - kind_(kind), - emitterScope_(bce->innermostEmitterScopeNoCheck()) - { } - - public: - using Nestable::enclosing; - using Nestable::findNearest; - - StatementKind kind() const { - return kind_; - } - - EmitterScope* emitterScope() const { - return emitterScope_; - } - - template - bool is() const; - - template - T& as() { - MOZ_ASSERT(this->is()); - return static_cast(*this); - } -}; - -// Template specializations are disallowed in different namespaces; specialize -// all the NestableControl subtypes up front. -namespace js { -namespace frontend { - -template <> -bool -BytecodeEmitter::NestableControl::is() const -{ - return StatementKindIsUnlabeledBreakTarget(kind_) || kind_ == StatementKind::Label; -} - -template <> -bool -BytecodeEmitter::NestableControl::is() const -{ - return kind_ == StatementKind::Label; -} - -template <> -bool -BytecodeEmitter::NestableControl::is() const -{ - return StatementKindIsLoop(kind_); -} - -template <> -bool -BytecodeEmitter::NestableControl::is() const -{ - return kind_ == StatementKind::ForOfLoop; -} - -template <> -bool -BytecodeEmitter::NestableControl::is() const -{ - return kind_ == StatementKind::Try || kind_ == StatementKind::Finally; -} - -} // namespace frontend -} // namespace js - -class BreakableControl : public BytecodeEmitter::NestableControl -{ - public: - // Offset of the last break. - JumpList breaks; - - BreakableControl(BytecodeEmitter* bce, StatementKind kind) - : NestableControl(bce, kind) - { - MOZ_ASSERT(is()); - } - - MOZ_MUST_USE bool patchBreaks(BytecodeEmitter* bce) { - return bce->emitJumpTargetAndPatch(breaks); - } -}; - -class LabelControl : public BreakableControl -{ - RootedAtom label_; - - // The code offset when this was pushed. Used for effectfulness checking. - ptrdiff_t startOffset_; - - public: - LabelControl(BytecodeEmitter* bce, JSAtom* label, ptrdiff_t startOffset) - : BreakableControl(bce, StatementKind::Label), - label_(bce->cx, label), - startOffset_(startOffset) - { } - - HandleAtom label() const { - return label_; - } - - ptrdiff_t startOffset() const { - return startOffset_; - } -}; - -class LoopControl : public BreakableControl -{ - // Loops' children are emitted in dominance order, so they can always - // have a TDZCheckCache. - TDZCheckCache tdzCache_; - - // Stack depth when this loop was pushed on the control stack. - int32_t stackDepth_; - - // The loop nesting depth. Used as a hint to Ion. - uint32_t loopDepth_; - - // Can we OSR into Ion from here? True unless there is non-loop state on the stack. - bool canIonOsr_; - - public: - // The target of continue statement jumps, e.g., the update portion of a - // for(;;) loop. - JumpTarget continueTarget; - - // Offset of the last continue in the loop. - JumpList continues; - - LoopControl(BytecodeEmitter* bce, StatementKind loopKind) - : BreakableControl(bce, loopKind), - tdzCache_(bce), - continueTarget({ -1 }) - { - MOZ_ASSERT(is()); - - LoopControl* enclosingLoop = findNearest(enclosing()); - - stackDepth_ = bce->stackDepth; - loopDepth_ = enclosingLoop ? enclosingLoop->loopDepth_ + 1 : 1; - - int loopSlots; - if (loopKind == StatementKind::Spread || loopKind == StatementKind::ForOfLoop) - loopSlots = 3; - else if (loopKind == StatementKind::ForInLoop) - loopSlots = 2; - else - loopSlots = 0; - - MOZ_ASSERT(loopSlots <= stackDepth_); - - if (enclosingLoop) { - canIonOsr_ = (enclosingLoop->canIonOsr_ && - stackDepth_ == enclosingLoop->stackDepth_ + loopSlots); - } else { - canIonOsr_ = stackDepth_ == loopSlots; - } - } - - uint32_t loopDepth() const { - return loopDepth_; - } - - bool canIonOsr() const { - return canIonOsr_; - } - - MOZ_MUST_USE bool patchBreaksAndContinues(BytecodeEmitter* bce) { - MOZ_ASSERT(continueTarget.offset != -1); - if (!patchBreaks(bce)) - return false; - bce->patchJumpsToTarget(continues, continueTarget); - return true; - } -}; - -class TryFinallyControl : public BytecodeEmitter::NestableControl -{ - bool emittingSubroutine_; - - public: - // The subroutine when emitting a finally block. - JumpList gosubs; - - // Offset of the last catch guard, if any. - JumpList guardJump; - - TryFinallyControl(BytecodeEmitter* bce, StatementKind kind) - : NestableControl(bce, kind), - emittingSubroutine_(false) - { - MOZ_ASSERT(is()); - } - - void setEmittingSubroutine() { - emittingSubroutine_ = true; - } - - bool emittingSubroutine() const { - return emittingSubroutine_; - } -}; - class MOZ_STACK_CLASS TryEmitter { public: @@ -898,6 +678,19 @@ class ForOfLoopControl : public LoopControl } }; +namespace js { +namespace frontend { + +template <> +bool +NestableControl::is() const +{ + return kind_ == StatementKind::ForOfLoop; +} + +} // namespace frontend +} // namespace js + BytecodeEmitter::BytecodeEmitter(BytecodeEmitter* parent, Parser* parser, SharedContext* sc, HandleScript script, Handle lazyScript, @@ -958,13 +751,6 @@ BytecodeEmitter::init() return atomIndices.acquire(cx); } -template bool */> -BytecodeEmitter::NestableControl* -BytecodeEmitter::findInnermostNestableControl(Predicate predicate) const -{ - return NestableControl::findNearest(innermostNestableControl, predicate); -} - template T* BytecodeEmitter::findInnermostNestableControl() const @@ -1463,7 +1249,7 @@ class NonLocalExitControl bce_->stackDepth = savedDepth_; } - MOZ_MUST_USE bool prepareForNonLocalJump(BytecodeEmitter::NestableControl* target); + MOZ_MUST_USE bool prepareForNonLocalJump(NestableControl* target); MOZ_MUST_USE bool prepareForNonLocalJumpToOutermost() { return prepareForNonLocalJump(nullptr); @@ -1494,10 +1280,8 @@ NonLocalExitControl::leaveScope(EmitterScope* es) * Emit additional bytecode(s) for non-local jumps. */ bool -NonLocalExitControl::prepareForNonLocalJump(BytecodeEmitter::NestableControl* target) +NonLocalExitControl::prepareForNonLocalJump(NestableControl* target) { - using NestableControl = BytecodeEmitter::NestableControl; - EmitterScope* es = bce_->innermostEmitterScope(); int npops = 0; diff --git a/js/src/frontend/BytecodeEmitter.h b/js/src/frontend/BytecodeEmitter.h index eae080b2b5..bdbf2e6de2 100644 --- a/js/src/frontend/BytecodeEmitter.h +++ b/js/src/frontend/BytecodeEmitter.h @@ -123,13 +123,12 @@ typedef Vector SrcNotesVector; class CallOrNewEmitter; class ElemOpEmitter; class EmitterScope; +class NestableControl; class PropOpEmitter; class TDZCheckCache; struct MOZ_STACK_CLASS BytecodeEmitter { - class NestableControl; - SharedContext* const sc; /* context shared between parsing and bytecode generation */ ExclusiveContext* const cx; @@ -257,9 +256,6 @@ struct MOZ_STACK_CLASS BytecodeEmitter MOZ_MUST_USE bool init(); - template bool */> - NestableControl* findInnermostNestableControl(Predicate predicate) const; - template T* findInnermostNestableControl() const; diff --git a/js/src/moz.build b/js/src/moz.build index 59038c7230..709d94b9a1 100644 --- a/js/src/moz.build +++ b/js/src/moz.build @@ -139,6 +139,7 @@ UNIFIED_SOURCES += [ 'ds/LifoAlloc.cpp', 'ds/MemoryProtectionExceptionHandler.cpp', 'frontend/BytecodeCompiler.cpp', + 'frontend/BytecodeControlStructures.cpp', 'frontend/BytecodeEmitter.cpp', 'frontend/CallOrNewEmitter.cpp', 'frontend/ElemOpEmitter.cpp',