Bug 1507066 part 1 - Move bytecode map from BaselineScript to TypeScript. r=tcampbell
authorJan de Mooij <jdemooij@mozilla.com>
Fri, 25 Jan 2019 07:16:18 +0000
changeset 515400 f745bbf9f347e1946210d1559ea93693dca68b30
parent 515399 9bab87de8e3c8be666f0bc459519b4f26d8483a3
child 515401 ec1aa436aeff3fe194c7077dcbb18950b0b3c5d0
push id1953
push userffxbld-merge
push dateMon, 11 Mar 2019 12:10:20 +0000
treeherdermozilla-release@9c35dcbaa899 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstcampbell
bugs1507066
milestone66.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 1507066 part 1 - Move bytecode map from BaselineScript to TypeScript. r=tcampbell We need this for the interpreter so our Baseline ICs work without a BaselineScript. Also these days TypeScript is a more logical place for it anyway. This also cleans up the TypeScript allocation code to stop relying on zero initialization (malloc instead of calloc) and we now use the constructor. Differential Revision: https://phabricator.services.mozilla.com/D17228
js/src/jit/BaselineCompiler.cpp
js/src/jit/BaselineJIT.cpp
js/src/jit/BaselineJIT.h
js/src/jit/IonBuilder.cpp
js/src/vm/JSFunction.h
js/src/vm/JSScript.h
js/src/vm/TypeInference-inl.h
js/src/vm/TypeInference.cpp
js/src/vm/TypeInference.h
js/src/vm/TypeSet.h
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -264,30 +264,27 @@ MethodStatus BaselineCompiler::compile()
     previousOffset = entry.nativeOffset;
   }
 
   if (pcEntries.oom()) {
     ReportOutOfMemory(cx);
     return Method_Error;
   }
 
-  // Note: There is an extra entry in the bytecode type map for the search
-  // hint, see below.
-  size_t bytecodeTypeMapEntries = script->nTypeSets() + 1;
   size_t resumeEntries =
       script->hasResumeOffsets() ? script->resumeOffsets().size() : 0;
   UniquePtr<BaselineScript> baselineScript(
-      BaselineScript::New(
-          script, bailoutPrologueOffset_.offset(),
-          debugOsrPrologueOffset_.offset(), debugOsrEpilogueOffset_.offset(),
-          profilerEnterFrameToggleOffset_.offset(),
-          profilerExitFrameToggleOffset_.offset(),
-          handler.retAddrEntries().length(), pcMappingIndexEntries.length(),
-          pcEntries.length(), bytecodeTypeMapEntries, resumeEntries,
-          traceLoggerToggleOffsets_.length()),
+      BaselineScript::New(script, bailoutPrologueOffset_.offset(),
+                          debugOsrPrologueOffset_.offset(),
+                          debugOsrEpilogueOffset_.offset(),
+                          profilerEnterFrameToggleOffset_.offset(),
+                          profilerExitFrameToggleOffset_.offset(),
+                          handler.retAddrEntries().length(),
+                          pcMappingIndexEntries.length(), pcEntries.length(),
+                          resumeEntries, traceLoggerToggleOffsets_.length()),
       JS::DeletePolicy<BaselineScript>(cx->runtime()));
   if (!baselineScript) {
     ReportOutOfMemory(cx);
     return Method_Error;
   }
 
   baselineScript->setMethod(code);
   baselineScript->setTemplateEnvironment(templateEnv);
@@ -322,23 +319,16 @@ MethodStatus BaselineCompiler::compile()
     baselineScript->setUsesEnvironmentChain();
   }
 
 #ifdef JS_TRACE_LOGGING
   // Initialize the tracelogger instrumentation.
   baselineScript->initTraceLogger(script, traceLoggerToggleOffsets_);
 #endif
 
-  uint32_t* bytecodeMap = baselineScript->bytecodeTypeMap();
-  FillBytecodeTypeMap(script, bytecodeMap);
-
-  // The last entry in the last index found, and is used to avoid binary
-  // searches for the sought entry when queries are in linear order.
-  bytecodeMap[script->nTypeSets()] = 0;
-
   // Compute yield/await native resume addresses.
   baselineScript->computeResumeNativeOffsets(script);
 
   if (compileDebugInstrumentation()) {
     baselineScript->setHasDebugInstrumentation();
   }
 
   // Always register a native => bytecode mapping entry, since profiler can be
--- a/js/src/jit/BaselineJIT.cpp
+++ b/js/src/jit/BaselineJIT.cpp
@@ -326,41 +326,36 @@ MethodStatus jit::CanEnterBaselineMethod
   return CanEnterBaselineJIT(cx, script, /* osrFrame = */ nullptr);
 };
 
 BaselineScript* BaselineScript::New(
     JSScript* jsscript, uint32_t bailoutPrologueOffset,
     uint32_t debugOsrPrologueOffset, uint32_t debugOsrEpilogueOffset,
     uint32_t profilerEnterToggleOffset, uint32_t profilerExitToggleOffset,
     size_t retAddrEntries, size_t pcMappingIndexEntries, size_t pcMappingSize,
-    size_t bytecodeTypeMapEntries, size_t resumeEntries,
-    size_t traceLoggerToggleOffsetEntries) {
+    size_t resumeEntries, size_t traceLoggerToggleOffsetEntries) {
   static const unsigned DataAlignment = sizeof(uintptr_t);
 
   size_t retAddrEntriesSize = retAddrEntries * sizeof(RetAddrEntry);
   size_t pcMappingIndexEntriesSize =
       pcMappingIndexEntries * sizeof(PCMappingIndexEntry);
-  size_t bytecodeTypeMapSize = bytecodeTypeMapEntries * sizeof(uint32_t);
   size_t resumeEntriesSize = resumeEntries * sizeof(uintptr_t);
   size_t tlEntriesSize = traceLoggerToggleOffsetEntries * sizeof(uint32_t);
 
   size_t paddedRetAddrEntriesSize =
       AlignBytes(retAddrEntriesSize, DataAlignment);
   size_t paddedPCMappingIndexEntriesSize =
       AlignBytes(pcMappingIndexEntriesSize, DataAlignment);
   size_t paddedPCMappingSize = AlignBytes(pcMappingSize, DataAlignment);
-  size_t paddedBytecodeTypesMapSize =
-      AlignBytes(bytecodeTypeMapSize, DataAlignment);
   size_t paddedResumeEntriesSize = AlignBytes(resumeEntriesSize, DataAlignment);
   size_t paddedTLEntriesSize = AlignBytes(tlEntriesSize, DataAlignment);
 
   size_t allocBytes = paddedRetAddrEntriesSize +
                       paddedPCMappingIndexEntriesSize + paddedPCMappingSize +
-                      paddedBytecodeTypesMapSize + paddedResumeEntriesSize +
-                      paddedTLEntriesSize;
+                      paddedResumeEntriesSize + paddedTLEntriesSize;
 
   BaselineScript* script =
       jsscript->zone()->pod_malloc_with_extra<BaselineScript, uint8_t>(
           allocBytes);
   if (!script) {
     return nullptr;
   }
   new (script) BaselineScript(bailoutPrologueOffset, debugOsrPrologueOffset,
@@ -377,19 +372,16 @@ BaselineScript* BaselineScript::New(
   script->pcMappingIndexOffset_ = offsetCursor;
   script->pcMappingIndexEntries_ = pcMappingIndexEntries;
   offsetCursor += paddedPCMappingIndexEntriesSize;
 
   script->pcMappingOffset_ = offsetCursor;
   script->pcMappingSize_ = pcMappingSize;
   offsetCursor += paddedPCMappingSize;
 
-  script->bytecodeTypeMapOffset_ = bytecodeTypeMapEntries ? offsetCursor : 0;
-  offsetCursor += paddedBytecodeTypesMapSize;
-
   script->resumeEntriesOffset_ = resumeEntries ? offsetCursor : 0;
   offsetCursor += paddedResumeEntriesSize;
 
   script->traceLoggerToggleOffsetsOffset_ = tlEntriesSize ? offsetCursor : 0;
   script->numTraceLoggerToggleOffsets_ = traceLoggerToggleOffsetEntries;
   offsetCursor += paddedTLEntriesSize;
 
   MOZ_ASSERT(offsetCursor == sizeof(BaselineScript) + allocBytes);
--- a/js/src/jit/BaselineJIT.h
+++ b/js/src/jit/BaselineJIT.h
@@ -290,20 +290,16 @@ struct BaselineScript final {
   uint32_t retAddrEntries_ = 0;
 
   uint32_t pcMappingIndexOffset_ = 0;
   uint32_t pcMappingIndexEntries_ = 0;
 
   uint32_t pcMappingOffset_ = 0;
   uint32_t pcMappingSize_ = 0;
 
-  // List mapping indexes of bytecode type sets to the offset of the opcode
-  // they correspond to, for use by TypeScript::BytecodeTypes.
-  uint32_t bytecodeTypeMapOffset_ = 0;
-
   // We store the native code address corresponding to each bytecode offset in
   // the script's resumeOffsets list.
   uint32_t resumeEntriesOffset_ = 0;
 
   // By default tracelogger is disabled. Therefore we disable the logging code
   // by default. We store the offsets we must patch to enable the logging.
   uint32_t traceLoggerToggleOffsetsOffset_ = 0;
   uint32_t numTraceLoggerToggleOffsets_ = 0;
@@ -339,18 +335,17 @@ struct BaselineScript final {
         profilerExitToggleOffset_(profilerExitToggleOffset) {}
 
  public:
   static BaselineScript* New(
       JSScript* jsscript, uint32_t bailoutPrologueOffset,
       uint32_t debugOsrPrologueOffset, uint32_t debugOsrEpilogueOffset,
       uint32_t profilerEnterToggleOffset, uint32_t profilerExitToggleOffset,
       size_t retAddrEntries, size_t pcMappingIndexEntries, size_t pcMappingSize,
-      size_t bytecodeTypeMapEntries, size_t resumeEntries,
-      size_t traceLoggerToggleOffsetEntries);
+      size_t resumeEntries, size_t traceLoggerToggleOffsetEntries);
 
   static void Trace(JSTracer* trc, BaselineScript* script);
   static void Destroy(FreeOp* fop, BaselineScript* script);
 
   static inline size_t offsetOfMethod() {
     return offsetof(BaselineScript, method_);
   }
 
@@ -497,22 +492,16 @@ struct BaselineScript final {
 
   static size_t offsetOfFlags() { return offsetof(BaselineScript, flags_); }
   static size_t offsetOfResumeEntriesOffset() {
     return offsetof(BaselineScript, resumeEntriesOffset_);
   }
 
   static void writeBarrierPre(Zone* zone, BaselineScript* script);
 
-  uint32_t* bytecodeTypeMap() {
-    MOZ_ASSERT(bytecodeTypeMapOffset_);
-    return reinterpret_cast<uint32_t*>(reinterpret_cast<uint8_t*>(this) +
-                                       bytecodeTypeMapOffset_);
-  }
-
   uint8_t maxInliningDepth() const { return maxInliningDepth_; }
   void setMaxInliningDepth(uint32_t depth) {
     MOZ_ASSERT(depth <= UINT8_MAX);
     maxInliningDepth_ = depth;
   }
   void resetMaxInliningDepth() { maxInliningDepth_ = UINT8_MAX; }
 
   uint16_t inlinedBytecodeLength() const { return inlinedBytecodeLength_; }
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -746,28 +746,18 @@ AbortReasonOr<Ok> IonBuilder::init() {
   if (inlineCallInfo_) {
     // If we're inlining, the actual this/argument types are not necessarily
     // a subset of the script's observed types. |argTypes| is never accessed
     // for inlined scripts, so we just null it.
     thisTypes = inlineCallInfo_->thisArg()->resultTypeSet();
     argTypes = nullptr;
   }
 
-  // The baseline script normally has the bytecode type map, but compute
-  // it ourselves if we do not have a baseline script.
-  if (script()->hasBaselineScript()) {
-    bytecodeTypeMap = script()->baselineScript()->bytecodeTypeMap();
-  } else {
-    bytecodeTypeMap = alloc_->lifoAlloc()->newArrayUninitialized<uint32_t>(
-        script()->nTypeSets());
-    if (!bytecodeTypeMap) {
-      return abort(AbortReason::Alloc);
-    }
-    FillBytecodeTypeMap(script(), bytecodeTypeMap);
-  }
+  AutoSweepTypeScript sweep(script());
+  bytecodeTypeMap = script()->types(sweep)->bytecodeTypeMap();
 
   return Ok();
 }
 
 AbortReasonOr<Ok> IonBuilder::build() {
   // Spew IC info for inlined script, but only when actually compiling,
   // not when analyzing it.
 #ifdef JS_STRUCTURED_SPEW
--- a/js/src/vm/JSFunction.h
+++ b/js/src/vm/JSFunction.h
@@ -226,16 +226,17 @@ class JSFunction : public js::NativeObje
   bool needsFunctionEnvironmentObjects() const {
     return needsCallObject() || needsNamedLambdaEnvironment();
   }
 
   bool needsSomeEnvironmentObject() const {
     return needsFunctionEnvironmentObjects() || needsExtraBodyVarEnvironment();
   }
 
+  static constexpr size_t NArgsBits = sizeof(nargs_) * CHAR_BIT;
   size_t nargs() const { return nargs_; }
 
   uint16_t flags() const { return flags_; }
 
   FunctionKind kind() const {
     return static_cast<FunctionKind>((flags_ & FUNCTION_KIND_MASK) >>
                                      FUNCTION_KIND_SHIFT);
   }
--- a/js/src/vm/JSScript.h
+++ b/js/src/vm/JSScript.h
@@ -1981,16 +1981,17 @@ class JSScript : public js::gc::TenuredC
     // Only functions have parameters.
     js::Scope* scope = bodyScope();
     if (!scope->is<js::FunctionScope>()) {
       return false;
     }
     return scope->as<js::FunctionScope>().hasParameterExprs();
   }
 
+  static constexpr size_t NumTypeSetsBits = sizeof(nTypeSets_) * CHAR_BIT;
   size_t nTypeSets() const { return nTypeSets_; }
 
   size_t funLength() const { return funLength_; }
 
   static size_t offsetOfFunLength() { return offsetof(JSScript, funLength_); }
 
   uint32_t sourceStart() const { return sourceStart_; }
 
--- a/js/src/vm/TypeInference-inl.h
+++ b/js/src/vm/TypeInference-inl.h
@@ -622,24 +622,16 @@ extern void TypeMonitorResult(JSContext*
                               StackTypeSet* types, TypeSet::Type type);
 extern void TypeMonitorResult(JSContext* cx, JSScript* script, jsbytecode* pc,
                               const Value& rval);
 
 /////////////////////////////////////////////////////////////////////
 // Script interface functions
 /////////////////////////////////////////////////////////////////////
 
-/* static */ inline unsigned TypeScript::NumTypeSets(JSScript* script) {
-  size_t num = script->nTypeSets() + 1 /* this */;
-  if (JSFunction* fun = script->functionNonDelazifying()) {
-    num += fun->nargs();
-  }
-  return num;
-}
-
 /* static */ inline StackTypeSet* TypeScript::ThisTypes(JSScript* script) {
   AutoSweepTypeScript sweep(script);
   TypeScript* types = script->types(sweep);
   return types ? types->typeArray() + script->nTypeSets() : nullptr;
 }
 
 /*
  * Note: for non-escaping arguments, argTypes reflect only the initial type of
@@ -693,20 +685,19 @@ template <typename TYPESET>
 /* static */ inline StackTypeSet* TypeScript::BytecodeTypes(JSScript* script,
                                                             jsbytecode* pc) {
   MOZ_ASSERT(CurrentThreadCanAccessZone(script->zone()));
   AutoSweepTypeScript sweep(script);
   TypeScript* types = script->types(sweep);
   if (!types) {
     return nullptr;
   }
-  uint32_t* hint =
-      script->baselineScript()->bytecodeTypeMap() + script->nTypeSets();
-  return BytecodeTypes(script, pc, script->baselineScript()->bytecodeTypeMap(),
-                       hint, types->typeArray());
+  uint32_t* hint = types->bytecodeTypeMap() + script->nTypeSets();
+  return BytecodeTypes(script, pc, types->bytecodeTypeMap(), hint,
+                       types->typeArray());
 }
 
 /* static */ inline void TypeScript::Monitor(JSContext* cx, JSScript* script,
                                              jsbytecode* pc,
                                              const js::Value& rval) {
   TypeMonitorResult(cx, script, pc, rval);
 }
 
--- a/js/src/vm/TypeInference.cpp
+++ b/js/src/vm/TypeInference.cpp
@@ -1186,19 +1186,20 @@ CompilerConstraintList* js::NewCompilerC
 }
 
 /* static */ bool TypeScript::FreezeTypeSets(
     CompilerConstraintList* constraints, JSScript* script,
     TemporaryTypeSet** pThisTypes, TemporaryTypeSet** pArgTypes,
     TemporaryTypeSet** pBytecodeTypes) {
   LifoAlloc* alloc = constraints->alloc();
   AutoSweepTypeScript sweep(script);
-  StackTypeSet* existing = script->types(sweep)->typeArray();
-
-  size_t count = NumTypeSets(script);
+  TypeScript* typeScript = script->types(sweep);
+  StackTypeSet* existing = typeScript->typeArray();
+
+  size_t count = typeScript->numTypeSets();
   TemporaryTypeSet* types =
       alloc->newArrayUninitialized<TemporaryTypeSet>(count);
   if (!types) {
     return false;
   }
 
   for (size_t i = 0; i < count; i++) {
     if (!existing[i].cloneIntoUninitialized(alloc, &types[i])) {
@@ -1545,18 +1546,17 @@ bool js::FinishCompilation(JSContext* cx
     }
 
     // If necessary, add constraints to trigger invalidation on the script
     // after any future changes to the stack type sets.
     if (entry.script->hasFreezeConstraints()) {
       continue;
     }
 
-    size_t count = TypeScript::NumTypeSets(entry.script);
-
+    size_t count = types->numTypeSets();
     StackTypeSet* array = types->typeArray();
     for (size_t i = 0; i < count; i++) {
       if (!array[i].addConstraint(
               cx,
               cx->typeLifoAlloc().new_<TypeConstraintFreezeStack>(entry.script),
               false)) {
         succeeded = false;
       }
@@ -3431,18 +3431,19 @@ bool js::AddClearDefiniteFunctionUsesInS
   // analysis was done is stable. We only need to look at type sets which
   // contain a single object, as IonBuilder does not inline polymorphic sites
   // during the definite properties analysis.
 
   TypeSet::ObjectKey* calleeKey =
       TypeSet::ObjectType(calleeScript->functionNonDelazifying()).objectKey();
 
   AutoSweepTypeScript sweep(script);
-  unsigned count = TypeScript::NumTypeSets(script);
-  StackTypeSet* typeArray = script->types(sweep)->typeArray();
+  TypeScript* typeScript = script->types(sweep);
+  unsigned count = typeScript->numTypeSets();
+  StackTypeSet* typeArray = typeScript->typeArray();
 
   for (unsigned i = 0; i < count; i++) {
     StackTypeSet* types = &typeArray[i];
     if (!types->unknownObject() && types->getObjectCount() == 1) {
       if (calleeKey != types->getObject(0)) {
         // Also check if the object is the Function.call or
         // Function.apply native. IonBuilder uses the presence of these
         // functions during inlining.
@@ -3495,29 +3496,33 @@ void js::TypeMonitorCallSlow(JSContext* 
   }
 
   /* Watch for fewer actuals than formals to the call. */
   for (; arg < nargs; arg++) {
     TypeScript::SetArgument(cx, script, arg, UndefinedValue());
   }
 }
 
-void js::FillBytecodeTypeMap(JSScript* script, uint32_t* bytecodeMap) {
+static void FillBytecodeTypeMap(JSScript* script, uint32_t* bytecodeMap) {
   uint32_t added = 0;
   for (jsbytecode* pc = script->code(); pc < script->codeEnd();
        pc += GetBytecodeLength(pc)) {
     JSOp op = JSOp(*pc);
     if (CodeSpec[op].format & JOF_TYPESET) {
       bytecodeMap[added++] = script->pcToOffset(pc);
       if (added == script->nTypeSets()) {
         break;
       }
     }
   }
   MOZ_ASSERT(added == script->nTypeSets());
+
+  // The last entry in the last index found, and is used to avoid binary
+  // searches for the sought entry when queries are in linear order.
+  bytecodeMap[script->nTypeSets()] = 0;
 }
 
 void js::TypeMonitorResult(JSContext* cx, JSScript* script, jsbytecode* pc,
                            TypeSet::Type type) {
   cx->check(script, type);
 
   AutoEnterAnalysis enter(cx);
 
@@ -3562,53 +3567,82 @@ void js::TypeMonitorResult(JSContext* cx
 
   TypeMonitorResult(cx, script, pc, TypeSet::GetValueType(rval));
 }
 
 /////////////////////////////////////////////////////////////////////
 // TypeScript
 /////////////////////////////////////////////////////////////////////
 
+static size_t NumTypeSets(JSScript* script) {
+  size_t num = script->nTypeSets() + 1 /* this */;
+  if (JSFunction* fun = script->functionNonDelazifying()) {
+    num += fun->nargs();
+  }
+
+  // We rely on numTypeSets being in a safe range to prevent overflow when
+  // allocating TypeScript.
+  static_assert(JSScript::NumTypeSetsBits == 16,
+                "JSScript typesets should have safe range to avoid overflow");
+  static_assert(JSFunction::NArgsBits == 16,
+                "JSFunction nargs should have safe range to avoid overflow");
+
+  return num;
+}
+
+TypeScript::TypeScript(JSScript* script, ICScriptPtr&& icScript,
+                       uint32_t numTypeSets)
+    : icScript_(std::move(icScript)), numTypeSets_(numTypeSets) {
+  StackTypeSet* array = typeArray();
+  for (unsigned i = 0; i < numTypeSets; i++) {
+    new (&array[i]) StackTypeSet();
+  }
+
+  FillBytecodeTypeMap(script, bytecodeTypeMap());
+}
+
 bool JSScript::makeTypes(JSContext* cx) {
   MOZ_ASSERT(!types_);
   cx->check(this);
 
   AutoEnterAnalysis enter(cx);
 
   UniquePtr<jit::ICScript> icScript(jit::ICScript::create(cx, this));
   if (!icScript) {
     return false;
   }
 
   // We need to call prepareForDestruction on ICScript before we |delete| it.
   auto prepareForDestruction = mozilla::MakeScopeExit(
       [&] { icScript->prepareForDestruction(cx->zone()); });
 
-  unsigned count = TypeScript::NumTypeSets(this);
-
-  size_t size = TypeScript::SizeIncludingTypeArray(count);
+  size_t numTypeSets = NumTypeSets(this);
+
+  // Note: There is an extra entry in the bytecode type map for the search
+  // hint, see FillBytecodeTypeMap.
+  size_t bytecodeTypeMapEntries = nTypeSets() + 1;
+
+  // Calculate allocation size. This cannot overflow, see comment in
+  // NumTypeSets.
+  static_assert(sizeof(TypeScript) ==
+                    sizeof(StackTypeSet) + offsetof(TypeScript, typeArray_),
+                "typeArray_ must be last member of TypeScript");
+  size_t allocSize =
+      (offsetof(TypeScript, typeArray_) + numTypeSets * sizeof(StackTypeSet) +
+       bytecodeTypeMapEntries * sizeof(uint32_t));
+
   auto typeScript =
-      reinterpret_cast<TypeScript*>(cx->pod_calloc<uint8_t>(size));
+      reinterpret_cast<TypeScript*>(cx->pod_malloc<uint8_t>(allocSize));
   if (!typeScript) {
     return false;
   }
 
   prepareForDestruction.release();
-  typeScript->icScript_ = std::move(icScript);
-
-#ifdef JS_CRASH_DIAGNOSTICS
-  {
-    StackTypeSet* typeArray = typeScript->typeArray();
-    for (unsigned i = 0; i < count; i++) {
-      typeArray[i].initMagic();
-    }
-  }
-#endif
-
-  types_ = typeScript;
+
+  types_ = new (typeScript) TypeScript(this, std::move(icScript), numTypeSets);
   setTypesGeneration(cx->zone()->types.generation);
 
 #ifdef DEBUG
   StackTypeSet* typeArray = typeScript->typeArray();
   for (unsigned i = 0; i < nTypeSets(); i++) {
     InferSpew(ISpewOps, "typeSet: %sT%p%s bytecode%u %p",
               InferSpewColor(&typeArray[i]), &typeArray[i],
               InferSpewColorReset(), i, this);
@@ -4685,17 +4719,17 @@ void ObjectGroup::sweep(const AutoSweepO
         continue;
       }
       inlinedCompilations[dest] = inlinedCompilations[i];
       dest++;
     }
     inlinedCompilations.shrinkTo(dest);
   }
 
-  unsigned num = TypeScript::NumTypeSets(this);
+  unsigned num = types_->numTypeSets();
   StackTypeSet* typeArray = types_->typeArray();
 
   // Remove constraints and references to dead objects from stack type sets.
   for (unsigned i = 0; i < num; i++) {
     typeArray[i].sweep(sweep, zone());
   }
 
   if (zone()->types.hadOOMSweepingTypes()) {
--- a/js/src/vm/TypeInference.h
+++ b/js/src/vm/TypeInference.h
@@ -205,52 +205,56 @@ class TypeScript {
   // The freeze constraints added to stack type sets will only directly
   // invalidate the script containing those stack type sets. This Vector
   // contains compilations that inlined this script, so we can invalidate
   // them as well.
   RecompileInfoVector inlinedCompilations_;
 
   // ICScript and TypeScript have the same lifetimes, so we store a pointer to
   // ICScript here to not increase sizeof(JSScript).
-  js::UniquePtr<js::jit::ICScript> icScript_;
+  using ICScriptPtr = js::UniquePtr<js::jit::ICScript>;
+  ICScriptPtr icScript_;
 
-  // Variable-size array
+  // Number of TypeSets in typeArray_.
+  uint32_t numTypeSets_;
+
+  // Variable-size array. This is followed by the bytecode type map.
   StackTypeSet typeArray_[1];
 
  public:
+  TypeScript(JSScript* script, ICScriptPtr&& icScript, uint32_t numTypeSets);
+
   RecompileInfoVector& inlinedCompilations() { return inlinedCompilations_; }
   MOZ_MUST_USE bool addInlinedCompilation(RecompileInfo info) {
     if (!inlinedCompilations_.empty() && inlinedCompilations_.back() == info) {
       return true;
     }
     return inlinedCompilations_.append(info);
   }
 
+  uint32_t numTypeSets() const { return numTypeSets_; }
+
   jit::ICScript* icScript() const {
     MOZ_ASSERT(icScript_);
     return icScript_.get();
   }
 
   /* Array of type sets for variables and JOF_TYPESET ops. */
   StackTypeSet* typeArray() const {
     // Ensure typeArray_ is the last data member of TypeScript.
     JS_STATIC_ASSERT(sizeof(TypeScript) ==
                      sizeof(typeArray_) + offsetof(TypeScript, typeArray_));
     return const_cast<StackTypeSet*>(typeArray_);
   }
 
-  static inline size_t SizeIncludingTypeArray(size_t arraySize) {
-    // Ensure typeArray_ is the last data member of TypeScript.
-    JS_STATIC_ASSERT(sizeof(TypeScript) ==
-                     sizeof(StackTypeSet) + offsetof(TypeScript, typeArray_));
-    return offsetof(TypeScript, typeArray_) + arraySize * sizeof(StackTypeSet);
+  uint32_t* bytecodeTypeMap() {
+    MOZ_ASSERT(numTypeSets_ > 0);
+    return reinterpret_cast<uint32_t*>(typeArray_ + numTypeSets_);
   }
 
-  static inline unsigned NumTypeSets(JSScript* script);
-
   static inline StackTypeSet* ThisTypes(JSScript* script);
   static inline StackTypeSet* ArgTypes(JSScript* script, unsigned i);
 
   /* Get the type set for values observed at an opcode. */
   static inline StackTypeSet* BytecodeTypes(JSScript* script, jsbytecode* pc);
 
   template <typename TYPESET>
   static inline TYPESET* BytecodeTypes(JSScript* script, jsbytecode* pc,
@@ -318,18 +322,16 @@ class MOZ_RAII AutoKeepTypeScripts {
   AutoKeepTypeScripts(const AutoKeepTypeScripts&) = delete;
   void operator=(const AutoKeepTypeScripts&) = delete;
 
  public:
   explicit inline AutoKeepTypeScripts(JSContext* cx);
   inline ~AutoKeepTypeScripts();
 };
 
-void FillBytecodeTypeMap(JSScript* script, uint32_t* bytecodeMap);
-
 class RecompileInfo;
 
 // Generate the type constraints for the compilation. Sets |isValidOut| based on
 // whether the type constraints still hold.
 bool FinishCompilation(JSContext* cx, HandleScript script,
                        CompilerConstraintList* constraints,
                        IonCompilationId compilationId, bool* isValidOut);
 
--- a/js/src/vm/TypeSet.h
+++ b/js/src/vm/TypeSet.h
@@ -670,23 +670,16 @@ class ConstraintTypeSet : public TypeSet
 
  protected:
   /* Chain of constraints which propagate changes out from this type set. */
   TypeConstraint* constraintList_ = nullptr;
 
  public:
   ConstraintTypeSet() = default;
 
-#ifdef JS_CRASH_DIAGNOSTICS
-  void initMagic() {
-    MOZ_ASSERT(!magic_);
-    magic_ = ConstraintTypeSetMagic;
-  }
-#endif
-
   void checkMagic() const {
 #ifdef JS_CRASH_DIAGNOSTICS
     if (MOZ_UNLIKELY(magic_ != ConstraintTypeSetMagic)) {
       ReportMagicWordFailure(magic_, ConstraintTypeSetMagic, uintptr_t(flags),
                              uintptr_t(objectSet));
     }
 #endif
   }