Bug 1543714 - Move BytecodeSection,PerScriptData and related classes to BytecodeSection.{cpp,h}. r=jorendorff
authorTooru Fujisawa <arai_a@mac.com>
Wed, 08 May 2019 09:00:02 +0000
changeset 534914 9928e1d5bc06081359d16e04226e989d912484ee
parent 534913 d81d08e2b3a115f803bfb2c033b885470a3e6a9d
child 534915 37bf1a4ee6bcaa9e0142db6eabd9cd5d6b0f03c4
push id2082
push userffxbld-merge
push dateMon, 01 Jul 2019 08:34:18 +0000
treeherdermozilla-release@2fb19d0466d2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjorendorff
bugs1543714
milestone68.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1543714 - Move BytecodeSection,PerScriptData and related classes to BytecodeSection.{cpp,h}. r=jorendorff Differential Revision: https://phabricator.services.mozilla.com/D27098
js/src/frontend/BytecodeEmitter.cpp
js/src/frontend/BytecodeEmitter.h
js/src/frontend/BytecodeSection.cpp
js/src/frontend/BytecodeSection.h
js/src/frontend/moz.build
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -10,17 +10,16 @@
 
 #include "frontend/BytecodeEmitter.h"
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/PodOperations.h"
-#include "mozilla/ReverseIterator.h"
 #include "mozilla/Sprintf.h"
 #include "mozilla/Variant.h"
 
 #include <string.h>
 
 #include "jsnum.h"
 #include "jstypes.h"
 #include "jsutil.h"
@@ -86,34 +85,16 @@ static bool ParseNodeRequiresSpecialLine
   // handling to avoid strange stepping behavior.
   // Functions usually shouldn't have location information (bug 1431202).
 
   ParseNodeKind kind = pn->getKind();
   return kind == ParseNodeKind::WhileStmt || kind == ParseNodeKind::ForStmt ||
          kind == ParseNodeKind::Function;
 }
 
-BytecodeEmitter::BytecodeSection::BytecodeSection(JSContext* cx,
-                                                  uint32_t lineNum)
-    : code_(cx),
-      notes_(cx),
-      tryNoteList_(cx),
-      scopeNoteList_(cx),
-      resumeOffsetList_(cx),
-      currentLine_(lineNum) {}
-
-BytecodeEmitter::PerScriptData::PerScriptData(JSContext* cx)
-    : scopeList_(cx),
-      numberList_(cx),
-      atomIndices_(cx->frontendCollectionPool()) {}
-
-bool BytecodeEmitter::PerScriptData::init(JSContext* cx) {
-  return atomIndices_.acquire(cx);
-}
-
 BytecodeEmitter::BytecodeEmitter(
     BytecodeEmitter* parent, SharedContext* sc, HandleScript script,
     Handle<LazyScript*> lazyScript, uint32_t lineNum, EmitterMode emitterMode,
     FieldInitializers fieldInitializers /* = FieldInitializers::Invalid() */)
     : sc(sc),
       cx(sc->cx_),
       parent(parent),
       script(cx, script),
@@ -255,31 +236,16 @@ bool BytecodeEmitter::emitCheck(JSOp op,
     static_assert(MaxBytecodeLength + 1 /* this */ + ARGC_LIMIT <= UINT32_MAX,
                   "numICEntries must not overflow");
     bytecodeSection().incrementNumICEntries();
   }
 
   return true;
 }
 
-void BytecodeEmitter::BytecodeSection::updateDepth(ptrdiff_t target) {
-  jsbytecode* pc = code(target);
-
-  int nuses = StackUses(pc);
-  int ndefs = StackDefs(pc);
-
-  stackDepth_ -= nuses;
-  MOZ_ASSERT(stackDepth_ >= 0);
-  stackDepth_ += ndefs;
-
-  if ((uint32_t)stackDepth_ > maxStackDepth_) {
-    maxStackDepth_ = stackDepth_;
-  }
-}
-
 #ifdef DEBUG
 bool BytecodeEmitter::checkStrictOrSloppy(JSOp op) {
   if (IsCheckStrictOp(op) && !sc->strict()) {
     return false;
   }
   if (IsCheckSloppyOp(op) && sc->strict()) {
     return false;
   }
@@ -9557,137 +9523,16 @@ bool BytecodeEmitter::setSrcNoteOffset(u
 void BytecodeEmitter::copySrcNotes(jssrcnote* destination, uint32_t nsrcnotes) {
   unsigned count = bytecodeSection().notes().length();
   // nsrcnotes includes SN_MAKE_TERMINATOR in addition to the srcnotes.
   MOZ_ASSERT(nsrcnotes == count + 1);
   PodCopy(destination, bytecodeSection().notes().begin(), count);
   SN_MAKE_TERMINATOR(&destination[count]);
 }
 
-void CGNumberList::finish(mozilla::Span<GCPtrValue> array) {
-  MOZ_ASSERT(length() == array.size());
-
-  for (unsigned i = 0; i < length(); i++) {
-    array[i].init(vector[i]);
-  }
-}
-
-/*
- * Find the index of the given object for code generator.
- *
- * Since the emitter refers to each parsed object only once, for the index we
- * use the number of already indexed objects. We also add the object to a list
- * to convert the list to a fixed-size array when we complete code generation,
- * see js::CGObjectList::finish below.
- */
-unsigned CGObjectList::add(ObjectBox* objbox) {
-  MOZ_ASSERT(objbox->isObjectBox());
-  MOZ_ASSERT(!objbox->emitLink);
-  objbox->emitLink = lastbox;
-  lastbox = objbox;
-  return length++;
-}
-
-void CGObjectList::finish(mozilla::Span<GCPtrObject> array) {
-  MOZ_ASSERT(length <= INDEX_LIMIT);
-  MOZ_ASSERT(length == array.size());
-
-  ObjectBox* objbox = lastbox;
-  for (GCPtrObject& obj : mozilla::Reversed(array)) {
-    MOZ_ASSERT(obj == nullptr);
-    MOZ_ASSERT(objbox->object()->isTenured());
-    obj.init(objbox->object());
-    objbox = objbox->emitLink;
-  }
-}
-
-void CGObjectList::finishInnerFunctions() {
-  ObjectBox* objbox = lastbox;
-  while (objbox) {
-    if (objbox->isFunctionBox()) {
-      objbox->asFunctionBox()->finish();
-    }
-    objbox = objbox->emitLink;
-  }
-}
-
-void CGScopeList::finish(mozilla::Span<GCPtrScope> array) {
-  MOZ_ASSERT(length() <= INDEX_LIMIT);
-  MOZ_ASSERT(length() == array.size());
-
-  for (uint32_t i = 0; i < length(); i++) {
-    array[i].init(vector[i]);
-  }
-}
-
-bool CGTryNoteList::append(JSTryNoteKind kind, uint32_t stackDepth,
-                           size_t start, size_t end) {
-  MOZ_ASSERT(start <= end);
-  MOZ_ASSERT(size_t(uint32_t(start)) == start);
-  MOZ_ASSERT(size_t(uint32_t(end)) == end);
-
-  // Offsets are given relative to sections, but we only expect main-section
-  // to have TryNotes. In finish() we will fixup base offset.
-
-  JSTryNote note;
-  note.kind = kind;
-  note.stackDepth = stackDepth;
-  note.start = uint32_t(start);
-  note.length = uint32_t(end - start);
-
-  return list.append(note);
-}
-
-void CGTryNoteList::finish(mozilla::Span<JSTryNote> array) {
-  MOZ_ASSERT(length() == array.size());
-
-  for (unsigned i = 0; i < length(); i++) {
-    array[i] = list[i];
-  }
-}
-
-bool CGScopeNoteList::append(uint32_t scopeIndex, uint32_t offset,
-                             uint32_t parent) {
-  CGScopeNote note;
-  mozilla::PodZero(&note);
-
-  // Offsets are given relative to sections. In finish() we will fixup base
-  // offset if needed.
-
-  note.index = scopeIndex;
-  note.start = offset;
-  note.parent = parent;
-
-  return list.append(note);
-}
-
-void CGScopeNoteList::recordEnd(uint32_t index, uint32_t offset) {
-  MOZ_ASSERT(index < length());
-  MOZ_ASSERT(list[index].length == 0);
-  list[index].end = offset;
-}
-
-void CGScopeNoteList::finish(mozilla::Span<ScopeNote> array) {
-  MOZ_ASSERT(length() == array.size());
-
-  for (unsigned i = 0; i < length(); i++) {
-    MOZ_ASSERT(list[i].end >= list[i].start);
-    list[i].length = list[i].end - list[i].start;
-    array[i] = list[i];
-  }
-}
-
-void CGResumeOffsetList::finish(mozilla::Span<uint32_t> array) {
-  MOZ_ASSERT(length() == array.size());
-
-  for (unsigned i = 0; i < length(); i++) {
-    array[i] = list[i];
-  }
-}
-
 const JSSrcNoteSpec js_SrcNoteSpec[] = {
 #define DEFINE_SRC_NOTE_SPEC(sym, name, arity) {name, arity},
     FOR_EACH_SRC_NOTE_TYPE(DEFINE_SRC_NOTE_SPEC)
 #undef DEFINE_SRC_NOTE_SPEC
 };
 
 static int SrcNoteArity(jssrcnote* sn) {
   MOZ_ASSERT(SN_TYPE(sn) < SRC_LAST);
--- a/js/src/frontend/BytecodeEmitter.h
+++ b/js/src/frontend/BytecodeEmitter.h
@@ -9,105 +9,32 @@
 #ifndef frontend_BytecodeEmitter_h
 #define frontend_BytecodeEmitter_h
 
 #include "mozilla/Attributes.h"
 #include "mozilla/Span.h"
 
 #include "ds/InlineTable.h"
 #include "frontend/BCEParserHandle.h"
+#include "frontend/BytecodeSection.h"  // BytecodeSection, PerScriptData
 #include "frontend/DestructuringFlavor.h"
 #include "frontend/EitherParser.h"
 #include "frontend/JumpList.h"
 #include "frontend/NameFunctions.h"
 #include "frontend/ParseNode.h"
 #include "frontend/SharedContext.h"
 #include "frontend/SourceNotes.h"
 #include "frontend/ValueUsage.h"
 #include "vm/BytecodeUtil.h"
 #include "vm/Interpreter.h"
 #include "vm/Iteration.h"
 
 namespace js {
 namespace frontend {
 
-class CGNumberList {
-  Rooted<ValueVector> vector;
-
- public:
-  explicit CGNumberList(JSContext* cx) : vector(cx, ValueVector(cx)) {}
-  MOZ_MUST_USE bool append(const Value& v) { return vector.append(v); }
-  size_t length() const { return vector.length(); }
-  void finish(mozilla::Span<GCPtrValue> array);
-};
-
-struct CGObjectList {
-  uint32_t length;    /* number of emitted so far objects */
-  ObjectBox* lastbox; /* last emitted object */
-
-  CGObjectList() : length(0), lastbox(nullptr) {}
-
-  unsigned add(ObjectBox* objbox);
-  void finish(mozilla::Span<GCPtrObject> array);
-  void finishInnerFunctions();
-};
-
-struct MOZ_STACK_CLASS CGScopeList {
-  Rooted<GCVector<Scope*>> vector;
-
-  explicit CGScopeList(JSContext* cx) : vector(cx, GCVector<Scope*>(cx)) {}
-
-  bool append(Scope* scope) { return vector.append(scope); }
-  uint32_t length() const { return vector.length(); }
-  void finish(mozilla::Span<GCPtrScope> array);
-};
-
-struct CGTryNoteList {
-  Vector<JSTryNote> list;
-  explicit CGTryNoteList(JSContext* cx) : list(cx) {}
-
-  MOZ_MUST_USE bool append(JSTryNoteKind kind, uint32_t stackDepth,
-                           size_t start, size_t end);
-  size_t length() const { return list.length(); }
-  void finish(mozilla::Span<JSTryNote> array);
-};
-
-struct CGScopeNote : public ScopeNote {
-  // The end offset. Used to compute the length.
-  uint32_t end;
-};
-
-struct CGScopeNoteList {
-  Vector<CGScopeNote> list;
-  explicit CGScopeNoteList(JSContext* cx) : list(cx) {}
-
-  MOZ_MUST_USE bool append(uint32_t scopeIndex, uint32_t offset,
-                           uint32_t parent);
-  void recordEnd(uint32_t index, uint32_t offse);
-  size_t length() const { return list.length(); }
-  void finish(mozilla::Span<ScopeNote> array);
-};
-
-struct CGResumeOffsetList {
-  Vector<uint32_t> list;
-  explicit CGResumeOffsetList(JSContext* cx) : list(cx) {}
-
-  MOZ_MUST_USE bool append(uint32_t offset) { return list.append(offset); }
-  size_t length() const { return list.length(); }
-  void finish(mozilla::Span<uint32_t> array);
-};
-
-static constexpr size_t MaxBytecodeLength = INT32_MAX;
-static constexpr size_t MaxSrcNotesLength = INT32_MAX;
-
-// Have a few inline elements, so as to avoid heap allocation for tiny
-// sequences.  See bug 1390526.
-typedef Vector<jsbytecode, 64> BytecodeVector;
-typedef Vector<jssrcnote, 64> SrcNotesVector;
-
 class CallOrNewEmitter;
 class ElemOpEmitter;
 class EmitterScope;
 class NestableControl;
 class PropertyEmitter;
 class TDZCheckCache;
 class TryEmitter;
 
@@ -122,259 +49,23 @@ struct MOZ_STACK_CLASS BytecodeEmitter {
 
   // The JSScript we're ultimately producing.
   Rooted<JSScript*> script;
 
   // The lazy script if mode is LazyFunction, nullptr otherwise.
   Rooted<LazyScript*> lazyScript;
 
  private:
-  // Bytecode and all data directly associated with specific opcode/index inside
-  // bytecode is stored in this class.
-  class BytecodeSection {
-   public:
-    BytecodeSection(JSContext* cx, uint32_t lineNum);
-
-    // ---- Bytecode ----
-
-    BytecodeVector& code() { return code_; }
-    const BytecodeVector& code() const { return code_; }
-
-    jsbytecode* code(ptrdiff_t offset) { return code_.begin() + offset; }
-    ptrdiff_t offset() const { return code_.end() - code_.begin(); }
-
-    // ---- Source notes ----
-
-    SrcNotesVector& notes() { return notes_; }
-    const SrcNotesVector& notes() const { return notes_; }
-
-    ptrdiff_t lastNoteOffset() const { return lastNoteOffset_; }
-    void setLastNoteOffset(ptrdiff_t offset) { lastNoteOffset_ = offset; }
-
-    // ---- Jump ----
-
-    ptrdiff_t lastTargetOffset() const { return lastTarget_.offset; }
-    void setLastTargetOffset(ptrdiff_t offset) { lastTarget_.offset = offset; }
-
-    // Check if the last emitted opcode is a jump target.
-    bool lastOpcodeIsJumpTarget() const {
-      return offset() - lastTarget_.offset == ptrdiff_t(JSOP_JUMPTARGET_LENGTH);
-    }
-
-    // JumpTarget should not be part of the emitted statement, as they can be
-    // aliased by multiple statements. If we included the jump target as part of
-    // the statement we might have issues where the enclosing statement might
-    // not contain all the opcodes of the enclosed statements.
-    ptrdiff_t lastNonJumpTargetOffset() const {
-      return lastOpcodeIsJumpTarget() ? lastTarget_.offset : offset();
-    }
-
-    // ---- Stack ----
-
-    int32_t stackDepth() const { return stackDepth_; }
-    void setStackDepth(int32_t depth) { stackDepth_ = depth; }
-
-    uint32_t maxStackDepth() const { return maxStackDepth_; }
-
-    void updateDepth(ptrdiff_t target);
-
-    // ---- Try notes ----
-
-    CGTryNoteList& tryNoteList() { return tryNoteList_; };
-    const CGTryNoteList& tryNoteList() const { return tryNoteList_; };
-
-    // ---- Scope ----
-
-    CGScopeNoteList& scopeNoteList() { return scopeNoteList_; };
-    const CGScopeNoteList& scopeNoteList() const { return scopeNoteList_; };
-
-    // ---- Generator ----
-
-    CGResumeOffsetList& resumeOffsetList() { return resumeOffsetList_; }
-    const CGResumeOffsetList& resumeOffsetList() const {
-      return resumeOffsetList_;
-    }
-
-    uint32_t numYields() const { return numYields_; }
-    void addNumYields() { numYields_++; }
-
-    // ---- Line and column ----
-
-    uint32_t currentLine() const { return currentLine_; }
-    uint32_t lastColumn() const { return lastColumn_; }
-    void setCurrentLine(uint32_t line) {
-      currentLine_ = line;
-      lastColumn_ = 0;
-    }
-    void setLastColumn(uint32_t column) { lastColumn_ = column; }
-
-    void updateSeparatorPosition() {
-      lastSeparatorOffet_ = code().length();
-      lastSeparatorLine_ = currentLine_;
-      lastSeparatorColumn_ = lastColumn_;
-    }
-
-    void updateSeparatorPositionIfPresent() {
-      if (lastSeparatorOffet_ == code().length()) {
-        lastSeparatorLine_ = currentLine_;
-        lastSeparatorColumn_ = lastColumn_;
-      }
-    }
-
-    bool isDuplicateLocation() const {
-      return lastSeparatorLine_ == currentLine_ &&
-             lastSeparatorColumn_ == lastColumn_;
-    }
-
-    // ---- JIT ----
-
-    uint32_t numICEntries() const { return numICEntries_; }
-    void incrementNumICEntries() {
-      MOZ_ASSERT(numICEntries_ != UINT32_MAX, "Shouldn't overflow");
-      numICEntries_++;
-    }
-    void setNumICEntries(uint32_t entries) { numICEntries_ = entries; }
-
-    uint32_t numTypeSets() const { return numTypeSets_; }
-    void incrementNumTypeSets() {
-      MOZ_ASSERT(numTypeSets_ != UINT32_MAX, "Shouldn't overflow");
-      numTypeSets_++;
-    }
-
-   private:
-    // ---- Bytecode ----
-
-    // Bytecode.
-    BytecodeVector code_;
-
-    // ---- Source notes ----
-
-    // Source notes
-    SrcNotesVector notes_;
-
-    // Code offset for last source note
-    ptrdiff_t lastNoteOffset_ = 0;
-
-    // ---- Jump ----
-
-    // Last jump target emitted.
-    JumpTarget lastTarget_ = {-1 - ptrdiff_t(JSOP_JUMPTARGET_LENGTH)};
-
-    // ---- Stack ----
-
-    // Maximum number of expression stack slots so far.
-    uint32_t maxStackDepth_ = 0;
-
-    // Current stack depth in script frame.
-    int32_t stackDepth_ = 0;
-
-    // ---- Try notes ----
-
-    // List of emitted try notes.
-    CGTryNoteList tryNoteList_;
-
-    // ---- Scope ----
-
-    // List of emitted block scope notes.
-    CGScopeNoteList scopeNoteList_;
-
-    // ---- Generator ----
-
-    // Certain ops (yield, await, gosub) have an entry in the script's
-    // resumeOffsets list. This can be used to map from the op's resumeIndex to
-    // the bytecode offset of the next pc. This indirection makes it easy to
-    // resume in the JIT (because BaselineScript stores a resumeIndex => native
-    // code array).
-    CGResumeOffsetList resumeOffsetList_;
-
-    // Number of yield instructions emitted. Does not include JSOP_AWAIT.
-    uint32_t numYields_ = 0;
-
-    // ---- Line and column ----
-
-    // Line number for srcnotes.
-    //
-    // WARNING: If this becomes out of sync with already-emitted srcnotes,
-    // we can get undefined behavior.
-    uint32_t currentLine_;
-
-    // Zero-based column index on currentLine_ of last SRC_COLSPAN-annotated
-    // opcode.
-    //
-    // WARNING: If this becomes out of sync with already-emitted srcnotes,
-    // we can get undefined behavior.
-    uint32_t lastColumn_ = 0;
-
-    // The offset, line and column numbers of the last opcode for the
-    // breakpoint for step execution.
-    uint32_t lastSeparatorOffet_ = 0;
-    uint32_t lastSeparatorLine_ = 0;
-    uint32_t lastSeparatorColumn_ = 0;
-
-    // ---- JIT ----
-
-    // Number of ICEntries in the script. There's one ICEntry for each JOF_IC op
-    // and, if the script is a function, for |this| and each formal argument.
-    uint32_t numICEntries_ = 0;
-
-    // Number of JOF_TYPESET opcodes generated.
-    uint32_t numTypeSets_ = 0;
-  };
-
   BytecodeSection bytecodeSection_;
 
  public:
   BytecodeSection& bytecodeSection() { return bytecodeSection_; }
   const BytecodeSection& bytecodeSection() const { return bytecodeSection_; }
 
  private:
-  // Data that is not directly associated with specific opcode/index inside
-  // bytecode, but referred from bytecode is stored in this class.
-  class PerScriptData {
-   public:
-    explicit PerScriptData(JSContext* cx);
-
-    MOZ_MUST_USE bool init(JSContext* cx);
-
-    // ---- Scope ----
-
-    CGScopeList& scopeList() { return scopeList_; }
-    const CGScopeList& scopeList() const { return scopeList_; }
-
-    // ---- Literals ----
-
-    CGNumberList& numberList() { return numberList_; }
-    const CGNumberList& numberList() const { return numberList_; }
-
-    CGObjectList& objectList() { return objectList_; }
-    const CGObjectList& objectList() const { return objectList_; }
-
-    PooledMapPtr<AtomIndexMap>& atomIndices() { return atomIndices_; }
-    const PooledMapPtr<AtomIndexMap>& atomIndices() const {
-      return atomIndices_;
-    }
-
-   private:
-    // ---- Scope ----
-
-    // List of emitted scopes.
-    CGScopeList scopeList_;
-
-    // ---- Literals ----
-
-    // List of double and bigint values used by script.
-    CGNumberList numberList_;
-
-    // List of emitted objects.
-    CGObjectList objectList_;
-
-    // Map from atom to index.
-    PooledMapPtr<AtomIndexMap> atomIndices_;
-  };
-
   PerScriptData perScriptData_;
 
  public:
   PerScriptData& perScriptData() { return perScriptData_; }
   const PerScriptData& perScriptData() const { return perScriptData_; }
 
  private:
   // switchToMain sets this to the bytecode offset of the main section.
new file mode 100644
--- /dev/null
+++ b/js/src/frontend/BytecodeSection.cpp
@@ -0,0 +1,170 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ * 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/BytecodeSection.h"
+
+#include "mozilla/Assertions.h"       // MOZ_ASSERT
+#include "mozilla/PodOperations.h"    // PodZero
+#include "mozilla/ReverseIterator.h"  // mozilla::Reversed
+
+#include "frontend/ParseNode.h"      // ObjectBox
+#include "frontend/SharedContext.h"  // FunctionBox
+#include "vm/BytecodeUtil.h"         // INDEX_LIMIT, StackUses, StackDefs
+#include "vm/JSContext.h"            // JSContext
+
+using namespace js;
+using namespace js::frontend;
+
+void CGNumberList::finish(mozilla::Span<GCPtrValue> array) {
+  MOZ_ASSERT(length() == array.size());
+
+  for (unsigned i = 0; i < length(); i++) {
+    array[i].init(vector[i]);
+  }
+}
+
+/*
+ * Find the index of the given object for code generator.
+ *
+ * Since the emitter refers to each parsed object only once, for the index we
+ * use the number of already indexed objects. We also add the object to a list
+ * to convert the list to a fixed-size array when we complete code generation,
+ * see js::CGObjectList::finish below.
+ */
+unsigned CGObjectList::add(ObjectBox* objbox) {
+  MOZ_ASSERT(objbox->isObjectBox());
+  MOZ_ASSERT(!objbox->emitLink);
+  objbox->emitLink = lastbox;
+  lastbox = objbox;
+  return length++;
+}
+
+void CGObjectList::finish(mozilla::Span<GCPtrObject> array) {
+  MOZ_ASSERT(length <= INDEX_LIMIT);
+  MOZ_ASSERT(length == array.size());
+
+  ObjectBox* objbox = lastbox;
+  for (GCPtrObject& obj : mozilla::Reversed(array)) {
+    MOZ_ASSERT(obj == nullptr);
+    MOZ_ASSERT(objbox->object()->isTenured());
+    obj.init(objbox->object());
+    objbox = objbox->emitLink;
+  }
+}
+
+void CGObjectList::finishInnerFunctions() {
+  ObjectBox* objbox = lastbox;
+  while (objbox) {
+    if (objbox->isFunctionBox()) {
+      objbox->asFunctionBox()->finish();
+    }
+    objbox = objbox->emitLink;
+  }
+}
+
+void CGScopeList::finish(mozilla::Span<GCPtrScope> array) {
+  MOZ_ASSERT(length() <= INDEX_LIMIT);
+  MOZ_ASSERT(length() == array.size());
+
+  for (uint32_t i = 0; i < length(); i++) {
+    array[i].init(vector[i]);
+  }
+}
+
+bool CGTryNoteList::append(JSTryNoteKind kind, uint32_t stackDepth,
+                           size_t start, size_t end) {
+  MOZ_ASSERT(start <= end);
+  MOZ_ASSERT(size_t(uint32_t(start)) == start);
+  MOZ_ASSERT(size_t(uint32_t(end)) == end);
+
+  // Offsets are given relative to sections, but we only expect main-section
+  // to have TryNotes. In finish() we will fixup base offset.
+
+  JSTryNote note;
+  note.kind = kind;
+  note.stackDepth = stackDepth;
+  note.start = uint32_t(start);
+  note.length = uint32_t(end - start);
+
+  return list.append(note);
+}
+
+void CGTryNoteList::finish(mozilla::Span<JSTryNote> array) {
+  MOZ_ASSERT(length() == array.size());
+
+  for (unsigned i = 0; i < length(); i++) {
+    array[i] = list[i];
+  }
+}
+
+bool CGScopeNoteList::append(uint32_t scopeIndex, uint32_t offset,
+                             uint32_t parent) {
+  CGScopeNote note;
+  mozilla::PodZero(&note);
+
+  // Offsets are given relative to sections. In finish() we will fixup base
+  // offset if needed.
+
+  note.index = scopeIndex;
+  note.start = offset;
+  note.parent = parent;
+
+  return list.append(note);
+}
+
+void CGScopeNoteList::recordEnd(uint32_t index, uint32_t offset) {
+  MOZ_ASSERT(index < length());
+  MOZ_ASSERT(list[index].length == 0);
+  list[index].end = offset;
+}
+
+void CGScopeNoteList::finish(mozilla::Span<ScopeNote> array) {
+  MOZ_ASSERT(length() == array.size());
+
+  for (unsigned i = 0; i < length(); i++) {
+    MOZ_ASSERT(list[i].end >= list[i].start);
+    list[i].length = list[i].end - list[i].start;
+    array[i] = list[i];
+  }
+}
+
+void CGResumeOffsetList::finish(mozilla::Span<uint32_t> array) {
+  MOZ_ASSERT(length() == array.size());
+
+  for (unsigned i = 0; i < length(); i++) {
+    array[i] = list[i];
+  }
+}
+
+BytecodeSection::BytecodeSection(JSContext* cx, uint32_t lineNum)
+    : code_(cx),
+      notes_(cx),
+      tryNoteList_(cx),
+      scopeNoteList_(cx),
+      resumeOffsetList_(cx),
+      currentLine_(lineNum) {}
+
+void BytecodeSection::updateDepth(ptrdiff_t target) {
+  jsbytecode* pc = code(target);
+
+  int nuses = StackUses(pc);
+  int ndefs = StackDefs(pc);
+
+  stackDepth_ -= nuses;
+  MOZ_ASSERT(stackDepth_ >= 0);
+  stackDepth_ += ndefs;
+
+  if (uint32_t(stackDepth_) > maxStackDepth_) {
+    maxStackDepth_ = stackDepth_;
+  }
+}
+
+PerScriptData::PerScriptData(JSContext* cx)
+    : scopeList_(cx),
+      numberList_(cx),
+      atomIndices_(cx->frontendCollectionPool()) {}
+
+bool PerScriptData::init(JSContext* cx) { return atomIndices_.acquire(cx); }
new file mode 100644
--- /dev/null
+++ b/js/src/frontend/BytecodeSection.h
@@ -0,0 +1,353 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ * 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_BytecodeSection_h
+#define frontend_BytecodeSection_h
+
+#include "mozilla/Attributes.h"  // MOZ_MUST_USE, MOZ_STACK_CLASS
+#include "mozilla/Span.h"        // mozilla::Span
+
+#include <stddef.h>  // ptrdiff_t, size_t
+#include <stdint.h>  // uint16_t, int32_t, uint32_t
+
+#include "NamespaceImports.h"          // ValueVector
+#include "frontend/JumpList.h"         // JumpTarget
+#include "frontend/NameCollections.h"  // AtomIndexMap, PooledMapPtr
+#include "frontend/SourceNotes.h"      // jssrcnote
+#include "gc/Barrier.h"                // GCPtrObject, GCPtrScope, GCPtrValue
+#include "gc/Rooting.h"                // JS::Rooted
+#include "js/GCVector.h"               // GCVector
+#include "js/TypeDecls.h"              // jsbytecode
+#include "js/Value.h"                  // JS::Vector
+#include "js/Vector.h"                 // Vector
+#include "vm/JSScript.h"               // JSTryNote, JSTryNoteKind, ScopeNote
+#include "vm/Opcodes.h"                // JSOP_*
+
+struct JSContext;
+
+namespace js {
+
+class Scope;
+
+namespace frontend {
+
+class ObjectBox;
+
+class CGNumberList {
+  JS::Rooted<ValueVector> vector;
+
+ public:
+  explicit CGNumberList(JSContext* cx) : vector(cx, ValueVector(cx)) {}
+  MOZ_MUST_USE bool append(const JS::Value& v) { return vector.append(v); }
+  size_t length() const { return vector.length(); }
+  void finish(mozilla::Span<GCPtrValue> array);
+};
+
+struct CGObjectList {
+  // Number of emitted so far objects.
+  uint32_t length;
+  // Last emitted object.
+  ObjectBox* lastbox;
+
+  CGObjectList() : length(0), lastbox(nullptr) {}
+
+  unsigned add(ObjectBox* objbox);
+  void finish(mozilla::Span<GCPtrObject> array);
+  void finishInnerFunctions();
+};
+
+struct MOZ_STACK_CLASS CGScopeList {
+  JS::Rooted<GCVector<Scope*>> vector;
+
+  explicit CGScopeList(JSContext* cx) : vector(cx, GCVector<Scope*>(cx)) {}
+
+  bool append(Scope* scope) { return vector.append(scope); }
+  uint32_t length() const { return vector.length(); }
+  void finish(mozilla::Span<GCPtrScope> array);
+};
+
+struct CGTryNoteList {
+  Vector<JSTryNote> list;
+  explicit CGTryNoteList(JSContext* cx) : list(cx) {}
+
+  MOZ_MUST_USE bool append(JSTryNoteKind kind, uint32_t stackDepth,
+                           size_t start, size_t end);
+  size_t length() const { return list.length(); }
+  void finish(mozilla::Span<JSTryNote> array);
+};
+
+struct CGScopeNote : public ScopeNote {
+  // The end offset. Used to compute the length.
+  uint32_t end;
+};
+
+struct CGScopeNoteList {
+  Vector<CGScopeNote> list;
+  explicit CGScopeNoteList(JSContext* cx) : list(cx) {}
+
+  MOZ_MUST_USE bool append(uint32_t scopeIndex, uint32_t offset,
+                           uint32_t parent);
+  void recordEnd(uint32_t index, uint32_t offse);
+  size_t length() const { return list.length(); }
+  void finish(mozilla::Span<ScopeNote> array);
+};
+
+struct CGResumeOffsetList {
+  Vector<uint32_t> list;
+  explicit CGResumeOffsetList(JSContext* cx) : list(cx) {}
+
+  MOZ_MUST_USE bool append(uint32_t offset) { return list.append(offset); }
+  size_t length() const { return list.length(); }
+  void finish(mozilla::Span<uint32_t> array);
+};
+
+
+static constexpr size_t MaxBytecodeLength = INT32_MAX;
+static constexpr size_t MaxSrcNotesLength = INT32_MAX;
+
+// Have a few inline elements, so as to avoid heap allocation for tiny
+// sequences.  See bug 1390526.
+typedef Vector<jsbytecode, 64> BytecodeVector;
+typedef Vector<jssrcnote, 64> SrcNotesVector;
+
+// Bytecode and all data directly associated with specific opcode/index inside
+// bytecode is stored in this class.
+class BytecodeSection {
+ public:
+  BytecodeSection(JSContext* cx, uint32_t lineNum);
+
+  // ---- Bytecode ----
+
+  BytecodeVector& code() { return code_; }
+  const BytecodeVector& code() const { return code_; }
+
+  jsbytecode* code(ptrdiff_t offset) { return code_.begin() + offset; }
+  ptrdiff_t offset() const { return code_.end() - code_.begin(); }
+
+  // ---- Source notes ----
+
+  SrcNotesVector& notes() { return notes_; }
+  const SrcNotesVector& notes() const { return notes_; }
+
+  ptrdiff_t lastNoteOffset() const { return lastNoteOffset_; }
+  void setLastNoteOffset(ptrdiff_t offset) { lastNoteOffset_ = offset; }
+
+  // ---- Jump ----
+
+  ptrdiff_t lastTargetOffset() const { return lastTarget_.offset; }
+  void setLastTargetOffset(ptrdiff_t offset) { lastTarget_.offset = offset; }
+
+  // Check if the last emitted opcode is a jump target.
+  bool lastOpcodeIsJumpTarget() const {
+    return offset() - lastTarget_.offset == ptrdiff_t(JSOP_JUMPTARGET_LENGTH);
+  }
+
+  // JumpTarget should not be part of the emitted statement, as they can be
+  // aliased by multiple statements. If we included the jump target as part of
+  // the statement we might have issues where the enclosing statement might
+  // not contain all the opcodes of the enclosed statements.
+  ptrdiff_t lastNonJumpTargetOffset() const {
+    return lastOpcodeIsJumpTarget() ? lastTarget_.offset : offset();
+  }
+
+  // ---- Stack ----
+
+  int32_t stackDepth() const { return stackDepth_; }
+  void setStackDepth(int32_t depth) { stackDepth_ = depth; }
+
+  uint32_t maxStackDepth() const { return maxStackDepth_; }
+
+  void updateDepth(ptrdiff_t target);
+
+  // ---- Try notes ----
+
+  CGTryNoteList& tryNoteList() { return tryNoteList_; };
+  const CGTryNoteList& tryNoteList() const { return tryNoteList_; };
+
+  // ---- Scope ----
+
+  CGScopeNoteList& scopeNoteList() { return scopeNoteList_; };
+  const CGScopeNoteList& scopeNoteList() const { return scopeNoteList_; };
+
+  // ---- Generator ----
+
+  CGResumeOffsetList& resumeOffsetList() { return resumeOffsetList_; }
+  const CGResumeOffsetList& resumeOffsetList() const {
+    return resumeOffsetList_;
+  }
+
+  uint32_t numYields() const { return numYields_; }
+  void addNumYields() { numYields_++; }
+
+  // ---- Line and column ----
+
+  uint32_t currentLine() const { return currentLine_; }
+  uint32_t lastColumn() const { return lastColumn_; }
+  void setCurrentLine(uint32_t line) {
+    currentLine_ = line;
+    lastColumn_ = 0;
+  }
+  void setLastColumn(uint32_t column) { lastColumn_ = column; }
+
+  void updateSeparatorPosition() {
+    lastSeparatorOffet_ = code().length();
+    lastSeparatorLine_ = currentLine_;
+    lastSeparatorColumn_ = lastColumn_;
+  }
+
+  void updateSeparatorPositionIfPresent() {
+    if (lastSeparatorOffet_ == code().length()) {
+      lastSeparatorLine_ = currentLine_;
+      lastSeparatorColumn_ = lastColumn_;
+    }
+  }
+
+  bool isDuplicateLocation() const {
+    return lastSeparatorLine_ == currentLine_ &&
+           lastSeparatorColumn_ == lastColumn_;
+  }
+
+  // ---- JIT ----
+
+  uint32_t numICEntries() const { return numICEntries_; }
+  void incrementNumICEntries() {
+    MOZ_ASSERT(numICEntries_ != UINT32_MAX, "Shouldn't overflow");
+    numICEntries_++;
+  }
+  void setNumICEntries(uint32_t entries) { numICEntries_ = entries; }
+
+  uint32_t numTypeSets() const { return numTypeSets_; }
+  void incrementNumTypeSets() {
+    MOZ_ASSERT(numTypeSets_ != UINT32_MAX, "Shouldn't overflow");
+    numTypeSets_++;
+  }
+
+ private:
+  // ---- Bytecode ----
+
+  // Bytecode.
+  BytecodeVector code_;
+
+  // ---- Source notes ----
+
+  // Source notes
+  SrcNotesVector notes_;
+
+  // Code offset for last source note
+  ptrdiff_t lastNoteOffset_ = 0;
+
+  // ---- Jump ----
+
+  // Last jump target emitted.
+  JumpTarget lastTarget_ = {-1 - ptrdiff_t(JSOP_JUMPTARGET_LENGTH)};
+
+  // ---- Stack ----
+
+  // Maximum number of expression stack slots so far.
+  uint32_t maxStackDepth_ = 0;
+
+  // Current stack depth in script frame.
+  int32_t stackDepth_ = 0;
+
+  // ---- Try notes ----
+
+  // List of emitted try notes.
+  CGTryNoteList tryNoteList_;
+
+  // ---- Scope ----
+
+  // List of emitted block scope notes.
+  CGScopeNoteList scopeNoteList_;
+
+  // ---- Generator ----
+
+  // Certain ops (yield, await, gosub) have an entry in the script's
+  // resumeOffsets list. This can be used to map from the op's resumeIndex to
+  // the bytecode offset of the next pc. This indirection makes it easy to
+  // resume in the JIT (because BaselineScript stores a resumeIndex => native
+  // code array).
+  CGResumeOffsetList resumeOffsetList_;
+
+  // Number of yield instructions emitted. Does not include JSOP_AWAIT.
+  uint32_t numYields_ = 0;
+
+  // ---- Line and column ----
+
+  // Line number for srcnotes.
+  //
+  // WARNING: If this becomes out of sync with already-emitted srcnotes,
+  // we can get undefined behavior.
+  uint32_t currentLine_;
+
+  // Zero-based column index on currentLine_ of last SRC_COLSPAN-annotated
+  // opcode.
+  //
+  // WARNING: If this becomes out of sync with already-emitted srcnotes,
+  // we can get undefined behavior.
+  uint32_t lastColumn_ = 0;
+
+  // The offset, line and column numbers of the last opcode for the
+  // breakpoint for step execution.
+  uint32_t lastSeparatorOffet_ = 0;
+  uint32_t lastSeparatorLine_ = 0;
+  uint32_t lastSeparatorColumn_ = 0;
+
+  // ---- JIT ----
+
+  // Number of ICEntries in the script. There's one ICEntry for each JOF_IC op
+  // and, if the script is a function, for |this| and each formal argument.
+  uint32_t numICEntries_ = 0;
+
+  // Number of JOF_TYPESET opcodes generated.
+  uint32_t numTypeSets_ = 0;
+};
+
+// Data that is not directly associated with specific opcode/index inside
+// bytecode, but referred from bytecode is stored in this class.
+class PerScriptData {
+ public:
+  explicit PerScriptData(JSContext* cx);
+
+  MOZ_MUST_USE bool init(JSContext* cx);
+
+  // ---- Scope ----
+
+  CGScopeList& scopeList() { return scopeList_; }
+  const CGScopeList& scopeList() const { return scopeList_; }
+
+  // ---- Literals ----
+
+  CGNumberList& numberList() { return numberList_; }
+  const CGNumberList& numberList() const { return numberList_; }
+
+  CGObjectList& objectList() { return objectList_; }
+  const CGObjectList& objectList() const { return objectList_; }
+
+  PooledMapPtr<AtomIndexMap>& atomIndices() { return atomIndices_; }
+  const PooledMapPtr<AtomIndexMap>& atomIndices() const { return atomIndices_; }
+
+ private:
+  // ---- Scope ----
+
+  // List of emitted scopes.
+  CGScopeList scopeList_;
+
+  // ---- Literals ----
+
+  // List of double and bigint values used by script.
+  CGNumberList numberList_;
+
+  // List of emitted objects.
+  CGObjectList objectList_;
+
+  // Map from atom to index.
+  PooledMapPtr<AtomIndexMap> atomIndices_;
+};
+
+} /* namespace frontend */
+} /* namespace js */
+
+#endif /* frontend_BytecodeSection_h */
--- a/js/src/frontend/moz.build
+++ b/js/src/frontend/moz.build
@@ -22,16 +22,17 @@ ReservedWordsGenerated = GENERATED_FILES
 ReservedWordsGenerated.script = 'GenerateReservedWords.py'
 ReservedWordsGenerated.inputs += ['ReservedWords.h']
 
 
 UNIFIED_SOURCES += [
     'BytecodeCompiler.cpp',
     'BytecodeControlStructures.cpp',
     'BytecodeEmitter.cpp',
+    'BytecodeSection.cpp',
     'CallOrNewEmitter.cpp',
     'CForEmitter.cpp',
     'DefaultEmitter.cpp',
     'DoWhileEmitter.cpp',
     'ElemOpEmitter.cpp',
     'EmitterScope.cpp',
     'ExpressionStatementEmitter.cpp',
     'FoldConstants.cpp',