arai-a / smoosh-sync

Automation to make jsparagus and SpiderMonkey bytecode in sync
2 stars 0 forks source link

/js/src/vm/Opcodes.h and 4 more files have been updated (65de67ce) #331

Open github-actions[bot] opened 1 year ago

github-actions[bot] commented 1 year ago

Files

Changesets

Diffs

/js/src/frontend/CompilationStencil.h

--- 20edf5ff2283dfc74fdf23f9b6e2f5100c7354d4/js/src/frontend/CompilationStencil.h
+++ 65de67ceb4f8f8551175cf981eceeb13bdbcbb29/js/src/frontend/CompilationStencil.h
@@ -3,68 +3,83 @@
  * 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_CompilationStencil_h
 #define frontend_CompilationStencil_h

 #include "mozilla/AlreadyAddRefed.h"  // already_AddRefed
-#include "mozilla/Assertions.h"       // MOZ_ASSERT
-#include "mozilla/Atomics.h"          // mozilla::Atomic
-#include "mozilla/Attributes.h"       // MOZ_RAII
-#include "mozilla/HashTable.h"        // mozilla::HashMap
-#include "mozilla/Maybe.h"            // mozilla::Maybe
+#include "mozilla/Assertions.h"  // MOZ_ASSERT, MOZ_RELEASE_ASSERT, MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE
+#include "mozilla/Atomics.h"     // mozilla::Atomic
+#include "mozilla/Attributes.h"  // MOZ_RAII, MOZ_STACK_CLASS
+#include "mozilla/HashTable.h"   // mozilla::HashMap, mozilla::DefaultHasher
+#include "mozilla/Maybe.h"       // mozilla::Maybe
 #include "mozilla/MemoryReporting.h"  // mozilla::MallocSizeOf
 #include "mozilla/RefPtr.h"           // RefPtr
-#include "mozilla/Span.h"
-#include "mozilla/Variant.h"  // mozilla::Variant
+#include "mozilla/Span.h"             // mozilla::Span
+#include "mozilla/Variant.h"          // mozilla::Variant

-#include "ds/LifoAlloc.h"
-#include "frontend/FrontendContext.h"    // AutoReportFrontendContext
-#include "frontend/NameAnalysisTypes.h"  // EnvironmentCoordinate
-#include "frontend/ParserAtom.h"   // ParserAtomsTable, TaggedParserAtomIndex
-#include "frontend/ScriptIndex.h"  // ScriptIndex
-#include "frontend/SharedContext.h"
-#include "frontend/Stencil.h"
+#include <algorithm>    // std::swap
+#include <stddef.h>     // size_t
+#include <stdint.h>     // uint32_t, uintptr_t
+#include <type_traits>  // std::is_pointer_v
+#include <utility>      // std::forward, std::move
+
+#include "ds/LifoAlloc.h"                 // LifoAlloc, LifoAllocScope
+#include "frontend/FrontendContext.h"     // FrontendContext
+#include "frontend/FunctionSyntaxKind.h"  // FunctionSyntaxKind
+#include "frontend/NameAnalysisTypes.h"   // NameLocation
+#include "frontend/ParserAtom.h"  // ParserAtomsTable, ParserAtomIndex, TaggedParserAtomIndex, ParserAtomSpan
+#include "frontend/ScopeIndex.h"     // ScopeIndex
+#include "frontend/ScriptIndex.h"    // ScriptIndex
+#include "frontend/SharedContext.h"  // ThisBinding, InheritThis, Directives
+#include "frontend/Stencil.h"  // ScriptStencil, ScriptStencilExtra, ScopeStencil, RegExpStencil, BigIntStencil, ObjLiteralStencil, BaseParserScopeData, StencilModuleMetadata
 #include "frontend/TaggedParserAtomIndexHasher.h"  // TaggedParserAtomIndexHasher
-#include "frontend/UsedNameTracker.h"
-#include "js/GCVector.h"
-#include "js/HashTable.h"
-#include "js/RefCounted.h"  // AtomicRefCounted
-#include "js/Transcoding.h"
-#include "js/UniquePtr.h"  // js::UniquePtr
-#include "js/Vector.h"
-#include "js/WasmModule.h"
-#include "vm/GlobalObject.h"  // GlobalObject
-#include "vm/JSContext.h"
-#include "vm/JSFunction.h"  // JSFunction
-#include "vm/JSScript.h"    // SourceExtent
-#include "vm/Realm.h"
+#include "frontend/UsedNameTracker.h"              // UsedNameTracker
+#include "js/AllocPolicy.h"    // SystemAllocPolicy, ReportOutOfMemory
+#include "js/GCVector.h"       // JS::GCVector
+#include "js/RefCounted.h"     // AtomicRefCounted
+#include "js/RootingAPI.h"     // JS::Handle
+#include "js/Transcoding.h"    // JS::TranscodeBuffer, JS::TranscodeRange
+#include "js/UniquePtr.h"      // js::UniquePtr
+#include "js/Vector.h"         // Vector
+#include "js/WasmModule.h"     // JS::WasmModule
+#include "vm/FunctionFlags.h"  // FunctionFlags
+#include "vm/GlobalObject.h"   // GlobalObject
+#include "vm/JSContext.h"      // JSContext
+#include "vm/JSFunction.h"     // JSFunction
+#include "vm/JSScript.h"       // BaseScript, ScriptSource, SourceExtent
+#include "vm/Realm.h"          // JSContext::global
+#include "vm/Scope.h"          // Scope, ModuleScope
 #include "vm/ScopeKind.h"      // ScopeKind
-#include "vm/SharedStencil.h"  // SharedImmutableScriptData
+#include "vm/SharedStencil.h"  // ImmutableScriptFlags, MemberInitializers, SharedImmutableScriptData, RO_IMMUTABLE_SCRIPT_FLAGS

 class JSAtom;
+class JSFunction;
+class JSObject;
 class JSString;
+class JSTracer;

 namespace JS {
 class JS_PUBLIC_API ReadOnlyCompileOptions;
 }

 namespace js {

 class AtomSet;
 class JSONPrinter;
 class ModuleObject;

 namespace frontend {

 struct CompilationInput;
 struct CompilationStencil;
 struct CompilationGCOutput;
+struct PreallocatedCompilationGCOutput;
 class ScriptStencilIterable;
 struct InputName;
 class ScopeBindingCache;

 // When delazifying modules' inner functions, the actual global scope is used.
 // However, when doing a delazification the global scope is not available. We
 // use this dummy type to be a placeholder to be used as part of the InputScope
 // variants to mimic what the Global scope would be used for.
@@ -674,19 +689,19 @@ struct CompilationInput {
     if (!initScriptSource(fc)) {
       return false;
     }
     enclosingScope = InputScope(&cx->global()->emptyGlobalScope());
     return true;
   }

   bool initForStandaloneFunctionInNonSyntacticScope(
-      FrontendContext* fc, Handle<Scope*> functionEnclosingScope);
+      FrontendContext* fc, JS::Handle<Scope*> functionEnclosingScope);

-  bool initForEval(FrontendContext* fc, Handle<Scope*> evalEnclosingScope) {
+  bool initForEval(FrontendContext* fc, JS::Handle<Scope*> evalEnclosingScope) {
     target = CompilationTarget::Eval;
     if (!initScriptSource(fc)) {
       return false;
     }
     enclosingScope = InputScope(evalEnclosingScope);
     return true;
   }

@@ -904,18 +919,19 @@ class CompilationSyntaxParseCache {
                                             CompilationAtomCache& atomCache,
                                             const ScriptStencilRef& lazy);
 };

 // AsmJS scripts are very rare on-average, so we use a HashMap to associate
 // data with a ScriptStencil. The ScriptStencil has a flag to indicate if we
 // need to even do this lookup.
 using StencilAsmJSMap =
-    HashMap<ScriptIndex, RefPtr<const JS::WasmModule>,
-            mozilla::DefaultHasher<ScriptIndex>, js::SystemAllocPolicy>;
+    mozilla::HashMap<ScriptIndex, RefPtr<const JS::WasmModule>,
+                     mozilla::DefaultHasher<ScriptIndex>,
+                     js::SystemAllocPolicy>;

 struct StencilAsmJSContainer
     : public js::AtomicRefCounted<StencilAsmJSContainer> {
   StencilAsmJSMap moduleMap;

   StencilAsmJSContainer() = default;

   size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
@@ -932,18 +948,19 @@ struct SharedDataContainer {
   //       updating or clearing the pointer.
   using SingleSharedDataPtr = SharedImmutableScriptData*;

   using SharedDataVector =
       Vector<RefPtr<js::SharedImmutableScriptData>, 0, js::SystemAllocPolicy>;
   using SharedDataVectorPtr = SharedDataVector*;

   using SharedDataMap =
-      HashMap<ScriptIndex, RefPtr<js::SharedImmutableScriptData>,
-              mozilla::DefaultHasher<ScriptIndex>, js::SystemAllocPolicy>;
+      mozilla::HashMap<ScriptIndex, RefPtr<js::SharedImmutableScriptData>,
+                       mozilla::DefaultHasher<ScriptIndex>,
+                       js::SystemAllocPolicy>;
   using SharedDataMapPtr = SharedDataMap*;

  private:
   enum {
     SingleTag = 0,
     VectorTag = 1,
     MapTag = 2,
     BorrowTag = 3,
@@ -1192,33 +1209,37 @@ struct CompilationStencil {

   [[nodiscard]] static bool instantiateStencilAfterPreparation(
       JSContext* cx, CompilationInput& input, const CompilationStencil& stencil,
       CompilationGCOutput& gcOutput);

   [[nodiscard]] static bool prepareForInstantiate(
       FrontendContext* fc, CompilationAtomCache& atomCache,
       const CompilationStencil& stencil, CompilationGCOutput& gcOutput);
+  [[nodiscard]] static bool prepareForInstantiate(
+      FrontendContext* fc, CompilationAtomCache& atomCache,
+      const CompilationStencil& stencil,
+      PreallocatedCompilationGCOutput& gcOutput);

   [[nodiscard]] static bool instantiateStencils(
       JSContext* cx, CompilationInput& input, const CompilationStencil& stencil,
       CompilationGCOutput& gcOutput);

   // Decode the special self-hosted stencil
   [[nodiscard]] bool instantiateSelfHostedAtoms(
       JSContext* cx, AtomSet& atomSet, CompilationAtomCache& atomCache) const;
   [[nodiscard]] JSScript* instantiateSelfHostedTopLevelForRealm(
       JSContext* cx, CompilationInput& input);
   [[nodiscard]] JSFunction* instantiateSelfHostedLazyFunction(
       JSContext* cx, CompilationAtomCache& atomCache, ScriptIndex index,
-      Handle<JSAtom*> name);
+      JS::Handle<JSAtom*> name);
   [[nodiscard]] bool delazifySelfHostedFunction(JSContext* cx,
                                                 CompilationAtomCache& atomCache,
                                                 ScriptIndexRange range,
-                                                HandleFunction fun);
+                                                JS::Handle<JSFunction*> fun);

   [[nodiscard]] bool serializeStencils(JSContext* cx, CompilationInput& input,
                                        JS::TranscodeBuffer& buf,
                                        bool* succeededOut = nullptr) const;
   [[nodiscard]] bool deserializeStencils(
       FrontendContext* fc, const JS::ReadOnlyCompileOptions& options,
       const JS::TranscodeRange& range, bool* succeededOut = nullptr);

@@ -1548,108 +1569,240 @@ inline size_t ExtensibleCompilationStenc
          regExpData.sizeOfExcludingThis(mallocSizeOf) +
          bigIntData.sizeOfExcludingThis(mallocSizeOf) +
          objLiteralData.sizeOfExcludingThis(mallocSizeOf) +
          parserAtoms.sizeOfExcludingThis(mallocSizeOf) +
          sharedData.sizeOfExcludingThis(mallocSizeOf) + moduleMetadataSize +
          asmJSSize;
 }

+// A PreAllocateableGCArray is an array of GC thing pointers.
+//
+// The array's internal buffer can be allocated ahead of time, possibly off
+// main thread.
+template <typename T>
+struct PreAllocateableGCArray {
+ private:
+  size_t length_ = 0;
+
+  // Inline element for the case when length_ == 1.
+  T inlineElem_;
+
+  // Heap-allocated elements for the case when length_ > 1;
+  T* elems_ = nullptr;
+
+ public:
+  struct Preallocated {
+   private:
+    size_t length_ = 0;
+    uintptr_t* elems_ = nullptr;
+
+    friend struct PreAllocateableGCArray<T>;
+
+   public:
+    Preallocated() = default;
+    ~Preallocated();
+
+    bool empty() const { return length_ == 0; }
+
+    size_t length() const { return length_; }
+
+   private:
+    bool isInline() const { return length_ == 1; }
+
+   public:
+    bool allocate(size_t length);
+
+    size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
+      return sizeof(uintptr_t) * length_;
+    }
+  };
+
+  PreAllocateableGCArray() {
+    static_assert(std::is_pointer_v<T>,
+                  "PreAllocateableGCArray element must be a pointer");
+  }
+  ~PreAllocateableGCArray();
+
+  bool empty() const { return length_ == 0; }
+
+  size_t length() const { return length_; }
+
+ private:
+  bool isInline() const { return length_ == 1; }
+
+ public:
+  bool allocate(size_t length);
+  bool allocateWith(T init, size_t length);
+
+  // Steal pre-allocated buffer.
+  void steal(Preallocated&& buffer);
+
+  T& operator[](size_t index) {
+    MOZ_ASSERT(index < length_);
+
+    if (isInline()) {
+      return inlineElem_;
+    }
+
+    return elems_[index];
+  }
+  const T& operator[](size_t index) const {
+    MOZ_ASSERT(index < length_);
+
+    if (isInline()) {
+      return inlineElem_;
+    }
+
+    return elems_[index];
+  }
+
+  size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
+    if (!elems_) {
+      return 0;
+    }
+
+    return sizeof(T) * length_;
+  }
+
+  void trace(JSTracer* trc);
+};
+
+struct CompilationGCOutput;
+
+// Pre-allocated storage for CompilationGCOutput.
+struct PreallocatedCompilationGCOutput {
+ private:
+  PreAllocateableGCArray<JSFunction*>::Preallocated functions;
+  PreAllocateableGCArray<js::Scope*>::Preallocated scopes;
+
+  friend struct CompilationGCOutput;
+
+ public:
+  PreallocatedCompilationGCOutput() = default;
+
+  [[nodiscard]] bool allocate(FrontendContext* fc, size_t scriptDataLength,
+                              size_t scopeDataLength) {
+    if (!functions.allocate(scriptDataLength)) {
+      ReportOutOfMemory(fc);
+      return false;
+    }
+    if (!scopes.allocate(scopeDataLength)) {
+      ReportOutOfMemory(fc);
+      return false;
+    }
+    return true;
+  }
+
+  size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
+    return functions.sizeOfExcludingThis(mallocSizeOf) +
+           scopes.sizeOfExcludingThis(mallocSizeOf);
+  }
+};
+
 // The output of GC allocation from stencil.
 struct CompilationGCOutput {
   // The resulting outermost script for the compilation powered
   // by this CompilationStencil.
   JSScript* script = nullptr;

   // The resulting module object if there is one.
   ModuleObject* module = nullptr;

-  // A Rooted vector to handle tracing of JSFunction* and Atoms within.
+  // An array to handle tracing of JSFunction* and Atoms within.
   //
   // If the top level script isn't a function, the item at TopLevelIndex is
   // nullptr.
-  JS::GCVector<JSFunction*, 1, js::SystemAllocPolicy> functions;
+  PreAllocateableGCArray<JSFunction*> functions;

   // References to scopes are controlled via AbstractScopePtr, which holds onto
   // an index (and CompilationStencil reference).
-  JS::GCVector<js::Scope*, 1, js::SystemAllocPolicy> scopes;
+  PreAllocateableGCArray<js::Scope*> scopes;

   // The result ScriptSourceObject. This is unused in delazifying parses.
   ScriptSourceObject* sourceObject = nullptr;

  private:
   // If we are only instantiating part of a stencil, we can reduce allocations
-  // by setting a base index and reserving only the vector capacity we need.
+  // by setting a base index and allocating only the array elements we need.
   // This applies to both the `functions` and `scopes` arrays. These fields are
-  // initialized by `ensureReservedWithBaseIndex` which also reserves the vector
-  // sizes appropriately.
+  // initialized by `ensureAllocatedWithBaseIndex` which also allocates the
+  // array appropriately.
   //
   // Note: These are only used for self-hosted delazification currently.
   ScriptIndex functionsBaseIndex{};
   ScopeIndex scopesBaseIndex{};

   // End of fields.

  public:
   CompilationGCOutput() = default;

-  // Helper to access the `functions` vector. The NoBaseIndex version is used if
+  // Helper to access the `functions` array. The NoBaseIndex version is used if
   // the caller never uses a base index.
   JSFunction*& getFunction(ScriptIndex index) {
     return functions[index - functionsBaseIndex];
   }
   JSFunction*& getFunctionNoBaseIndex(ScriptIndex index) {
     MOZ_ASSERT(!functionsBaseIndex);
     return functions[index];
   }

-  // Helper accessors for the `scopes` vector.
+  // Helper accessors for the `scopes` array.
   js::Scope*& getScope(ScopeIndex index) {
     return scopes[index - scopesBaseIndex];
   }
   js::Scope*& getScopeNoBaseIndex(ScopeIndex index) {
     MOZ_ASSERT(!scopesBaseIndex);
     return scopes[index];
   }
   js::Scope* getScopeNoBaseIndex(ScopeIndex index) const {
     MOZ_ASSERT(!scopesBaseIndex);
     return scopes[index];
   }

-  // Reserve output vector capacity. This may be called before instantiate to do
-  // allocations ahead of time (off thread). The stencil instantiation code will
-  // also run this to ensure the vectors are ready.
-  [[nodiscard]] bool ensureReserved(FrontendContext* fc,
-                                    size_t scriptDataLength,
-                                    size_t scopeDataLength) {
-    if (!functions.reserve(scriptDataLength)) {
-      ReportOutOfMemory(fc);
-      return false;
+  // Allocate output arrays.
+  [[nodiscard]] bool ensureAllocated(FrontendContext* fc,
+                                     size_t scriptDataLength,
+                                     size_t scopeDataLength) {
+    if (functions.empty()) {
+      if (!functions.allocate(scriptDataLength)) {
+        ReportOutOfMemory(fc);
+        return false;
+      }
     }
-    if (!scopes.reserve(scopeDataLength)) {
-      ReportOutOfMemory(fc);
-      return false;
+    if (scopes.empty()) {
+      if (!scopes.allocate(scopeDataLength)) {
+        ReportOutOfMemory(fc);
+        return false;
+      }
     }
     return true;
   }

-  // A variant of `ensureReserved` that sets a base index for the function and
+  // Steal output arrays' buffer.
+  void steal(PreallocatedCompilationGCOutput&& pre) {
+    functions.steal(std::move(pre.functions));
+    scopes.steal(std::move(pre.scopes));
+  }
+
+  // A variant of `ensureAllocated` that sets a base index for the function and
   // scope arrays. This is used when instantiating only a subset of the stencil.
   // Currently this only applies to self-hosted delazification. The ranges
   // include the start index and exclude the limit index.
-  [[nodiscard]] bool ensureReservedWithBaseIndex(FrontendContext* fc,
-                                                 ScriptIndex scriptStart,
-                                                 ScriptIndex scriptLimit,
-                                                 ScopeIndex scopeStart,
-                                                 ScopeIndex scopeLimit) {
+  [[nodiscard]] bool ensureAllocatedWithBaseIndex(FrontendContext* fc,
+                                                  ScriptIndex scriptStart,
+                                                  ScriptIndex scriptLimit,
+                                                  ScopeIndex scopeStart,
+                                                  ScopeIndex scopeLimit) {
     this->functionsBaseIndex = scriptStart;
     this->scopesBaseIndex = scopeStart;

-    return ensureReserved(fc, scriptLimit - scriptStart,
-                          scopeLimit - scopeStart);
+    return ensureAllocated(fc, scriptLimit - scriptStart,
+                           scopeLimit - scopeStart);
   }

   // Size of dynamic data. Note that GC data is counted by GC and not here.
   size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
     return functions.sizeOfExcludingThis(mallocSizeOf) +
            scopes.sizeOfExcludingThis(mallocSizeOf);
   }

@@ -1771,18 +1924,19 @@ struct CompilationStencilMerger {
   // Delazifications are merged into this.
   //
   // If any failure happens during merge operation, this field is reset to
   // nullptr.
   UniquePtr<ExtensibleCompilationStencil> initial_;

   // A Map from function key to the ScriptIndex in the initial stencil.
   using FunctionKeyToScriptIndexMap =
-      HashMap<FunctionKey, ScriptIndex, mozilla::DefaultHasher<FunctionKey>,
-              js::SystemAllocPolicy>;
+      mozilla::HashMap<FunctionKey, ScriptIndex,
+                       mozilla::DefaultHasher<FunctionKey>,
+                       js::SystemAllocPolicy>;
   FunctionKeyToScriptIndexMap functionKeyToInitialScriptIndex_;

   [[nodiscard]] bool buildFunctionKeyToIndex(FrontendContext* fc);

   ScriptIndex getInitialScriptIndexFor(
       const CompilationStencil& delazification) const;

   // A map from delazification's ParserAtomIndex to

/js/src/frontend/Stencil.cpp

--- ff723d87099783cccd9b6e6def5d03e4effd3bf8/js/src/frontend/Stencil.cpp
+++ 65de67ceb4f8f8551175cf981eceeb13bdbcbb29/js/src/frontend/Stencil.cpp
@@ -10,16 +10,18 @@
 #include "mozilla/Assertions.h"             // MOZ_RELEASE_ASSERT
 #include "mozilla/Maybe.h"                  // mozilla::Maybe
 #include "mozilla/OperatorNewExtensions.h"  // mozilla::KnownNotNull
 #include "mozilla/PodOperations.h"          // mozilla::PodCopy
 #include "mozilla/RefPtr.h"                 // RefPtr
 #include "mozilla/ScopeExit.h"              // mozilla::ScopeExit
 #include "mozilla/Sprintf.h"                // SprintfLiteral

+#include <algorithm>  // std::fill
+
 #include "ds/LifoAlloc.h"               // LifoAlloc
 #include "frontend/AbstractScopePtr.h"  // ScopeIndex
 #include "frontend/BytecodeCompiler.h"  // CompileGlobalScriptToStencil, InstantiateStencils, CanLazilyParse, ParseModuleToStencil
 #include "frontend/BytecodeSection.h"   // EmitScriptThingsVector
 #include "frontend/CompilationStencil.h"  // CompilationStencil, CompilationState, ExtensibleCompilationStencil, CompilationGCOutput, CompilationStencilMerger
 #include "frontend/FrontendContext.h"
 #include "frontend/NameAnalysisTypes.h"  // EnvironmentCoordinate
 #include "frontend/ScopeBindingCache.h"  // ScopeBindingCache
@@ -29,16 +31,17 @@
 #include "gc/Tracer.h"                  // TraceNullableRoot
 #include "js/CallArgs.h"                // JSNative
 #include "js/CompileOptions.h"          // JS::DecodeOptions
 #include "js/experimental/JSStencil.h"  // JS::Stencil
 #include "js/GCAPI.h"                   // JS::AutoCheckCannotGC
 #include "js/Printer.h"                 // js::Fprinter
 #include "js/RootingAPI.h"              // Rooted
 #include "js/Transcoding.h"             // JS::TranscodeBuffer
+#include "js/Utility.h"                 // js_malloc, js_calloc, js_free
 #include "js/Value.h"                   // ObjectValue
 #include "js/WasmModule.h"              // JS::WasmModule
 #include "vm/BigIntType.h"   // ParseBigIntLiteral, BigIntLiteralIsZero
 #include "vm/BindingKind.h"  // BindingKind
 #include "vm/EnvironmentObject.h"
 #include "vm/GeneratorAndAsyncKind.h"  // GeneratorKind, FunctionAsyncKind
 #include "vm/JSContext.h"              // JSContext
 #include "vm/JSFunction.h"  // JSFunction, GetFunctionPrototype, NewFunctionWithProto
@@ -1682,32 +1685,140 @@ bool CompilationSyntaxParseCache::copyCl

     closedOverBindings[i] = parserAtom;
   }

   closedOverBindings_ = ClosedOverBindingsSpan(closedOverBindings, length);
   return true;
 }

+template <typename T>
+PreAllocateableGCArray<T>::~PreAllocateableGCArray() {
+  if (elems_) {
+    js_free(elems_);
+    elems_ = nullptr;
+  }
+}
+
+template <typename T>
+bool PreAllocateableGCArray<T>::allocate(size_t length) {
+  MOZ_ASSERT(empty());
+
+  length_ = length;
+
+  if (isInline()) {
+    inlineElem_ = nullptr;
+    return true;
+  }
+
+  elems_ = reinterpret_cast<T*>(js_calloc(sizeof(T) * length_));
+  if (!elems_) {
+    return false;
+  }
+
+  return true;
+}
+
+template <typename T>
+bool PreAllocateableGCArray<T>::allocateWith(T init, size_t length) {
+  MOZ_ASSERT(empty());
+
+  length_ = length;
+
+  if (isInline()) {
+    inlineElem_ = init;
+    return true;
+  }
+
+  elems_ = reinterpret_cast<T*>(js_malloc(sizeof(T) * length_));
+  if (!elems_) {
+    return false;
+  }
+
+  std::fill(elems_, elems_ + length_, init);
+  return true;
+}
+
+template <typename T>
+void PreAllocateableGCArray<T>::steal(Preallocated&& buffer) {
+  MOZ_ASSERT(empty());
+
+  length_ = buffer.length_;
+  buffer.length_ = 0;
+
+  if (isInline()) {
+    inlineElem_ = nullptr;
+    return;
+  }
+
+  elems_ = reinterpret_cast<T*>(buffer.elems_);
+  buffer.elems_ = nullptr;
+
+#ifdef DEBUG
+  for (size_t i = 0; i < length_; i++) {
+    MOZ_ASSERT(elems_[i] == nullptr);
+  }
+#endif
+}
+
+template <typename T>
+void PreAllocateableGCArray<T>::trace(JSTracer* trc) {
+  if (empty()) {
+    return;
+  }
+
+  if (isInline()) {
+    TraceNullableRoot(trc, &inlineElem_, "PreAllocateableGCArray::inlineElem_");
+    return;
+  }
+
+  for (size_t i = 0; i < length_; i++) {
+    TraceNullableRoot(trc, &elems_[i], "PreAllocateableGCArray::elems_");
+  }
+}
+
+template <typename T>
+PreAllocateableGCArray<T>::Preallocated::~Preallocated() {
+  if (elems_) {
+    js_free(elems_);
+    elems_ = nullptr;
+  }
+}
+
+template <typename T>
+bool PreAllocateableGCArray<T>::Preallocated::allocate(size_t length) {
+  MOZ_ASSERT(empty());
+
+  length_ = length;
+
+  if (isInline()) {
+    return true;
+  }
+
+  elems_ = reinterpret_cast<uintptr_t*>(js_calloc(sizeof(uintptr_t) * length_));
+  if (!elems_) {
+    return false;
+  }
+
+  return true;
+}
+
+template struct js::frontend::PreAllocateableGCArray<JSFunction*>;
+template struct js::frontend::PreAllocateableGCArray<js::Scope*>;
+
 void CompilationAtomCache::trace(JSTracer* trc) { atoms_.trace(trc); }

 void CompilationGCOutput::trace(JSTracer* trc) {
   TraceNullableRoot(trc, &script, "compilation-gc-output-script");
   TraceNullableRoot(trc, &module, "compilation-gc-output-module");
   TraceNullableRoot(trc, &sourceObject, "compilation-gc-output-source");
   functions.trace(trc);
   scopes.trace(trc);
 }

-void JS::InstantiationStorage::trace(JSTracer* trc) {
-  if (gcOutput_) {
-    gcOutput_->trace(trc);
-  }
-}
-
 RegExpObject* RegExpStencil::createRegExp(
     JSContext* cx, const CompilationAtomCache& atomCache) const {
   Rooted<JSAtom*> atom(cx, atomCache.getExistingAtomAt(cx, atom_));
   return RegExpObject::createSyntaxChecked(cx, atom, flags(), TenuredObject);
 }

 RegExpObject* RegExpStencil::createRegExpAndEnsureAtom(
     JSContext* cx, FrontendContext* fc, ParserAtomsTable& parserAtoms,
@@ -2011,20 +2122,17 @@ static bool InstantiateModuleObject(JSCo

 // Instantiate JSFunctions for each FunctionBox.
 static bool InstantiateFunctions(JSContext* cx, FrontendContext* fc,
                                  CompilationAtomCache& atomCache,
                                  const CompilationStencil& stencil,
                                  CompilationGCOutput& gcOutput) {
   using ImmutableFlags = ImmutableScriptFlagsEnum;

-  if (!gcOutput.functions.resize(stencil.scriptData.size())) {
-    ReportOutOfMemory(fc);
-    return false;
-  }
+  MOZ_ASSERT(gcOutput.functions.length() == stencil.scriptData.size());

   // Most JSFunctions will be have the same Shape so we can compute it now to
   // allow fast object creation. Generators / Async will use the slow path
   // instead.
   Rooted<SharedShape*> functionShape(
       cx, GlobalObject::getFunctionShapeWithDefaultProto(
               cx, /* extended = */ false));
   if (!functionShape) {
@@ -2093,29 +2201,29 @@ static bool InstantiateScopes(JSContext*
   // While allocating Scope object from ScopeStencil, Scope object for the
   // enclosing Scope should already be allocated.
   //
   // Enclosing scope of ScopeStencil can be either ScopeStencil or Scope*
   // pointer.
   //
   // If the enclosing scope is ScopeStencil, it's guaranteed to be earlier
   // element in stencil.scopeData, because enclosing_ field holds
-  // index into it, and newly created ScopeStencil is pushed back to the vector.
+  // index into it, and newly created ScopeStencil is pushed back to the array.
   //
   // If the enclosing scope is Scope*, it's CompilationInput.enclosingScope.

   MOZ_ASSERT(stencil.scopeData.size() == stencil.scopeNames.size());
   size_t scopeCount = stencil.scopeData.size();
   for (size_t i = 0; i < scopeCount; i++) {
     Scope* scope = stencil.scopeData[i].createScope(cx, input, gcOutput,
                                                     stencil.scopeNames[i]);
     if (!scope) {
       return false;
     }
-    gcOutput.scopes.infallibleAppend(scope);
+    gcOutput.scopes[i] = scope;
   }

   return true;
 }

 // Instantiate js::BaseScripts from ScriptStencils for inner functions of the
 // compilation. Note that standalone functions and functions being delazified
 // are handled below with other top-levels.
@@ -2371,25 +2479,27 @@ static void AssertDelazificationFieldsMa
 }
 #endif  // DEBUG

 // When delazifying, use the existing JSFunctions. The initial and delazifying
 // parse are required to generate the same sequence of functions for lazy
 // parsing to work at all.
 static void FunctionsFromExistingLazy(CompilationInput& input,
                                       CompilationGCOutput& gcOutput) {
-  MOZ_ASSERT(gcOutput.functions.empty());
-  gcOutput.functions.infallibleAppend(input.function());
+  MOZ_ASSERT(!gcOutput.functions[0]);
+
+  size_t instantiatedFunIndex = 0;
+  gcOutput.functions[instantiatedFunIndex++] = input.function();

   for (JS::GCCellPtr elem : input.lazyOuterBaseScript()->gcthings()) {
     if (!elem.is<JSObject>()) {
       continue;
     }
     JSFunction* fun = &elem.as<JSObject>().as<JSFunction>();
-    gcOutput.functions.infallibleAppend(fun);
+    gcOutput.functions[instantiatedFunIndex++] = fun;
   }
 }

 void CompilationStencil::borrowFromExtensibleCompilationStencil(
     ExtensibleCompilationStencil& extensibleStencil) {
   canLazilyParse = extensibleStencil.canLazilyParse;
   functionKey = extensibleStencil.functionKey;

@@ -2597,17 +2707,18 @@ JSScript* CompilationStencil::instantiat
   // we do not want to instantiate those functions here since they are instead
   // created on demand from the stencil. Create a dummy function and populate
   // the functions array of the CompilationGCOutput with references to it.
   RootedFunction dummy(
       cx, NewNativeFunction(cx, SelfHostedDummyFunction, 0, nullptr));
   if (!dummy) {
     return nullptr;
   }
-  if (!gcOutput.get().functions.appendN(dummy, scriptData.size())) {
+
+  if (!gcOutput.get().functions.allocateWith(dummy, scriptData.size())) {
     ReportOutOfMemory(cx);
     return nullptr;
   }

   if (!InstantiateTopLevel(cx, input, *this, gcOutput.get())) {
     return nullptr;
   }

@@ -2675,68 +2786,71 @@ bool CompilationStencil::delazifySelfHos
     auto gcthings = scriptData[scriptIndex].gcthings(*this);
     return gcthings[GCThingIndex::outermostScopeIndex()].toScope();
   };
   ScopeIndex scopeIndex = getOutermostScope(range.start);
   ScopeIndex scopeLimit = (range.limit < scriptData.size())
                               ? getOutermostScope(range.limit)
                               : ScopeIndex(scopeData.size());

-  // Prepare to instantiate by reserving the output vectors. We also set a base
+  // Prepare to instantiate by allocating the output arrays. We also set a base
   // index to avoid allocations in most cases.
   AutoReportFrontendContext fc(cx);
   Rooted<CompilationGCOutput> gcOutput(cx);
-  if (!gcOutput.get().ensureReservedWithBaseIndex(&fc, range.start, range.limit,
-                                                  scopeIndex, scopeLimit)) {
+  if (!gcOutput.get().ensureAllocatedWithBaseIndex(
+          &fc, range.start, range.limit, scopeIndex, scopeLimit)) {
     return false;
   }

   // Phase 1: Instantiate JSAtoms.
   //  NOTE: The self-hosted atoms are all "permanent" and the
   //        CompilationAtomCache is already stored on the JSRuntime.

   // Phase 2: Instantiate ScriptSourceObject, ModuleObject, JSFunctions.

   // Get the corresponding ScriptSourceObject to use in current realm.
   gcOutput.get().sourceObject = SelfHostingScriptSourceObject(cx);
   if (!gcOutput.get().sourceObject) {
     return false;
   }

+  size_t instantiatedFunIndex = 0;
+
   // Delazification target function.
-  gcOutput.get().functions.infallibleAppend(fun);
+  gcOutput.get().functions[instantiatedFunIndex++] = fun;

   // Allocate inner functions. Self-hosted functions do not allocate these with
   // the initial function.
   for (size_t i = range.start + 1; i < range.limit; i++) {
     JSFunction* innerFun = CreateFunction(cx, atomCache, *this, scriptData[i],
                                           scriptExtra[i], ScriptIndex(i));
     if (!innerFun) {
       return false;
     }
-    gcOutput.get().functions.infallibleAppend(innerFun);
+    gcOutput.get().functions[instantiatedFunIndex++] = innerFun;
   }

   // Phase 3: Instantiate js::Scopes.
   // NOTE: When the enclosing scope is not a stencil, directly use the
   //       `emptyGlobalScope` instead of reading from CompilationInput. This is
   //       a special case for self-hosted delazification that allows us to reuse
   //       the CompilationInput between different realms.
+  size_t instantiatedScopeIndex = 0;
   for (size_t i = scopeIndex; i < scopeLimit; i++) {
     ScopeStencil& data = scopeData[i];
     Rooted<Scope*> enclosingScope(
         cx, data.hasEnclosing() ? gcOutput.get().getScope(data.enclosing())
                                 : &cx->global()->emptyGlobalScope());

     js::Scope* scope =
         data.createScope(cx, atomCache, enclosingScope, scopeNames[i]);
     if (!scope) {
       return false;
     }
-    gcOutput.get().scopes.infallibleAppend(scope);
+    gcOutput.get().scopes[instantiatedScopeIndex++] = scope;
   }

   // Phase 4: Instantiate (inner) BaseScripts.
   ScriptIndex innerStart(range.start + 1);
   for (size_t i = innerStart; i < range.limit; i++) {
     if (!JSScript::fromStencil(cx, atomCache, *this, gcOutput.get(),
                                ScriptIndex(i))) {
       return false;
@@ -2758,19 +2872,32 @@ bool CompilationStencil::delazifySelfHos

   return true;
 }

 /* static */
 bool CompilationStencil::prepareForInstantiate(
     FrontendContext* fc, CompilationAtomCache& atomCache,
     const CompilationStencil& stencil, CompilationGCOutput& gcOutput) {
-  // Reserve the `gcOutput` vectors.
-  if (!gcOutput.ensureReserved(fc, stencil.scriptData.size(),
-                               stencil.scopeData.size())) {
+  // Allocate the `gcOutput` arrays.
+  if (!gcOutput.ensureAllocated(fc, stencil.scriptData.size(),
+                                stencil.scopeData.size())) {
+    return false;
+  }
+
+  return atomCache.allocate(fc, stencil.parserAtomData.size());
+}
+
+/* static */
+bool CompilationStencil::prepareForInstantiate(
+    FrontendContext* fc, CompilationAtomCache& atomCache,
+    const CompilationStencil& stencil,
+    PreallocatedCompilationGCOutput& gcOutput) {
+  if (!gcOutput.allocate(fc, stencil.scriptData.size(),
+                         stencil.scopeData.size())) {
     return false;
   }

   return atomCache.allocate(fc, stencil.parserAtomData.size());
 }

 bool CompilationStencil::serializeStencils(JSContext* cx,
                                            CompilationInput& input,
@@ -5342,44 +5469,48 @@ JS_PUBLIC_API JSScript* JS::InstantiateG
     JSContext* cx, const JS::InstantiateOptions& options, JS::Stencil* stencil,
     JS::InstantiationStorage* storage) {
   MOZ_ASSERT_IF(storage, storage->isValid());

   CompileOptions compileOptions(cx);
   options.copyTo(compileOptions);
   Rooted<CompilationInput> input(cx, CompilationInput(compileOptions));
   Rooted<CompilationGCOutput> gcOutput(cx);
-  CompilationGCOutput& output = storage ? *storage->gcOutput_ : gcOutput.get();
+  if (storage) {
+    gcOutput.get().steal(std::move(*storage->gcOutput_));
+  }

-  if (!InstantiateStencils(cx, input.get(), *stencil, output)) {
+  if (!InstantiateStencils(cx, input.get(), *stencil, gcOutput.get())) {
     return nullptr;
   }
-  return output.script;
+  return gcOutput.get().script;
 }

 JS_PUBLIC_API bool JS::StencilIsBorrowed(Stencil* stencil) {
   return stencil->storageType == CompilationStencil::StorageType::Borrowed;
 }

 JS_PUBLIC_API JSObject* JS::InstantiateModuleStencil(
     JSContext* cx, const JS::InstantiateOptions& options, JS::Stencil* stencil,
     JS::InstantiationStorage* storage) {
   MOZ_ASSERT_IF(storage, storage->isValid());

   CompileOptions compileOptions(cx);
   options.copyTo(compileOptions);
   compileOptions.setModule();
   Rooted<CompilationInput> input(cx, CompilationInput(compileOptions));
   Rooted<CompilationGCOutput> gcOutput(cx);
-  CompilationGCOutput& output = storage ? *storage->gcOutput_ : gcOutput.get();
+  if (storage) {
+    gcOutput.get().steal(std::move(*storage->gcOutput_));
+  }

-  if (!InstantiateStencils(cx, input.get(), *stencil, output)) {
+  if (!InstantiateStencils(cx, input.get(), *stencil, gcOutput.get())) {
     return nullptr;
   }
-  return output.module;
+  return gcOutput.get().module;
 }

 JS::TranscodeResult JS::EncodeStencil(JSContext* cx, JS::Stencil* stencil,
                                       TranscodeBuffer& buffer) {
   AutoReportFrontendContext fc(cx);
   XDRStencilEncoder encoder(&fc, buffer);
   XDRResult res = encoder.codeStencil(*stencil);
   if (res.isErr()) {

/js/src/vm/BytecodeUtil.cpp

/js/src/vm/BytecodeUtil.h

/js/src/vm/Opcodes.h