Bug 1561925 - Implement remaining interpreter-specific BaselineCodeGen methods. r=tcampbell
authorJan de Mooij <jdemooij@mozilla.com>
Sat, 06 Jul 2019 10:34:06 +0000
changeset 481537 01f4651b1ca8010dd2b900fe8236ed9f449359bd
parent 481536 9f93598c61f531672033a592879521ae4f2fcd08
child 481538 50b6e4480ea0cdc62c9d28d40b277dad6e2878e5
push id36249
push userrmaries@mozilla.com
push dateSat, 06 Jul 2019 21:46:28 +0000
treeherdermozilla-central@88f4a687f039 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstcampbell
bugs1561925
milestone69.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 1561925 - Implement remaining interpreter-specific BaselineCodeGen methods. r=tcampbell After the recent script data changes we can now read objects/scopes/bigints and resume offsets from JIT code. Differential Revision: https://phabricator.services.mozilla.com/D36201
js/src/jit/BaselineCompiler.cpp
js/src/jit/BaselineCompiler.h
js/src/vm/JSScript.h
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -36,16 +36,18 @@
 #include "vm/Interpreter-inl.h"
 #include "vm/JSScript-inl.h"
 #include "vm/NativeObject-inl.h"
 #include "vm/TypeInference-inl.h"
 
 using namespace js;
 using namespace js::jit;
 
+using JS::TraceKind;
+
 using mozilla::AssertedCast;
 using mozilla::Maybe;
 
 namespace js {
 namespace jit {
 
 // When generating the Baseline Interpreter, this register is guaranteed to hold
 // the bytecode pc at the start of a bytecode instruction. Instructions are free
@@ -921,43 +923,99 @@ void BaselineInterpreterCodeGen::pushScr
 
   masm.loadPtr(frame.addressOfInterpreterPC(), scratch1);
   LoadInt32Operand(masm, scratch1, scratch1);
 
   loadScriptAtom(scratch1, scratch2);
   pushArg(scratch2);
 }
 
-template <>
-void BaselineCompilerCodeGen::pushScriptObjectArg(ScriptObjectType type) {
-  JSScript* script = handler.script();
+static gc::Cell* GetScriptGCThing(JSScript* script, jsbytecode* pc,
+                                  ScriptGCThingType type) {
+  switch (type) {
+    case ScriptGCThingType::RegExp:
+      return script->getRegExp(pc);
+    case ScriptGCThingType::Function:
+      return script->getFunction(pc);
+    case ScriptGCThingType::Scope:
+      return script->getScope(pc);
+    case ScriptGCThingType::BigInt:
+      return script->getBigInt(pc);
+  }
+  MOZ_CRASH("Unexpected GCThing type");
+}
+
+template <>
+void BaselineCompilerCodeGen::loadScriptGCThing(ScriptGCThingType type,
+                                                Register dest,
+                                                Register scratch) {
+  gc::Cell* thing = GetScriptGCThing(handler.script(), handler.pc(), type);
+  masm.movePtr(ImmGCPtr(thing), dest);
+}
+
+template <>
+void BaselineInterpreterCodeGen::loadScriptGCThing(ScriptGCThingType type,
+                                                   Register dest,
+                                                   Register scratch) {
+  MOZ_ASSERT(dest != scratch);
+
+  // Load the index in |scratch|.
+  masm.loadPtr(frame.addressOfInterpreterPC(), scratch);
+  LoadInt32Operand(masm, scratch, scratch);
+
+  // Load the GCCellPtr.
+  loadScript(dest);
+  masm.loadPtr(Address(dest, JSScript::offsetOfPrivateScriptData()), dest);
+  masm.loadPtr(BaseIndex(dest, scratch, ScalePointer,
+                         PrivateScriptData::offsetOfGCThings()),
+               dest);
+
+  // Clear the tag bits.
   switch (type) {
-    case ScriptObjectType::RegExp:
-      pushArg(ImmGCPtr(script->getRegExp(handler.pc())));
-      return;
-    case ScriptObjectType::Function:
-      pushArg(ImmGCPtr(script->getFunction(handler.pc())));
-      return;
-  }
-  MOZ_CRASH("Unexpected object type");
-}
-
-template <>
-void BaselineInterpreterCodeGen::pushScriptObjectArg(ScriptObjectType type) {
-  MOZ_CRASH("NYI: interpreter pushScriptObjectArg");
-}
-
-template <>
-void BaselineCompilerCodeGen::pushScriptScopeArg() {
-  pushArg(ImmGCPtr(handler.script()->getScope(handler.pc())));
-}
-
-template <>
-void BaselineInterpreterCodeGen::pushScriptScopeArg() {
-  MOZ_CRASH("NYI: interpreter pushScriptScopeArg");
+    case ScriptGCThingType::RegExp:
+    case ScriptGCThingType::Function:
+      // No-op because GCCellPtr tag bits are zero for objects.
+      static_assert(uintptr_t(TraceKind::Object) == 0,
+                    "Unexpected tag bits for object GCCellPtr");
+      break;
+    case ScriptGCThingType::Scope:
+    case ScriptGCThingType::BigInt:
+      // Use xorPtr with a 32-bit immediate because it's more efficient than
+      // andPtr on 64-bit.
+      static_assert(uintptr_t(TraceKind::Scope) >= JS::OutOfLineTraceKindMask,
+                    "Expected Scopes to have OutOfLineTraceKindMask tag");
+      static_assert(uintptr_t(TraceKind::BigInt) >= JS::OutOfLineTraceKindMask,
+                    "Expected BigInts to have OutOfLineTraceKindMask tag");
+      masm.xorPtr(Imm32(JS::OutOfLineTraceKindMask), dest);
+      break;
+  }
+
+#ifdef DEBUG
+  // Assert low bits are not set.
+  Label ok;
+  masm.branchTestPtr(Assembler::Zero, dest, Imm32(0b111), &ok);
+  masm.assumeUnreachable("GC pointer with tag bits set");
+  masm.bind(&ok);
+#endif
+}
+
+template <>
+void BaselineCompilerCodeGen::pushScriptGCThingArg(ScriptGCThingType type,
+                                                   Register scratch1,
+                                                   Register scratch2) {
+  gc::Cell* thing = GetScriptGCThing(handler.script(), handler.pc(), type);
+  pushArg(ImmGCPtr(thing));
+}
+
+template <>
+void BaselineInterpreterCodeGen::pushScriptGCThingArg(ScriptGCThingType type,
+                                                      Register scratch1,
+                                                      Register scratch2) {
+  loadScriptGCThing(type, scratch1, scratch2);
+  pushArg(scratch1);
 }
 
 template <>
 void BaselineCompilerCodeGen::pushUint8BytecodeOperandArg(Register) {
   MOZ_ASSERT(JOF_OPTYPE(JSOp(*handler.pc())) == JOF_UINT8);
   pushArg(Imm32(GET_UINT8(handler.pc())));
 }
 
@@ -1525,17 +1583,20 @@ bool BaselineCompilerCodeGen::emitTraceL
 
   masm.bind(&noTraceLogger);
 
   return true;
 }
 
 template <>
 bool BaselineInterpreterCodeGen::emitTraceLoggerEnter() {
-  MOZ_CRASH("NYI: interpreter emitTraceLoggerEnter");
+  if (JS::TraceLoggerSupported()) {
+    MOZ_CRASH("NYI: interpreter emitTraceLoggerEnter");
+  }
+  return true;
 }
 
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emitTraceLoggerExit() {
   AllocatableRegisterSet regs(RegisterSet::Volatile());
   Register loggerReg = regs.takeAnyGeneral();
 
   Label noTraceLogger;
@@ -2461,17 +2522,22 @@ template <>
 bool BaselineCompilerCodeGen::emit_JSOP_BIGINT() {
   BigInt* bi = handler.script()->getBigInt(handler.pc());
   frame.push(BigIntValue(bi));
   return true;
 }
 
 template <>
 bool BaselineInterpreterCodeGen::emit_JSOP_BIGINT() {
-  MOZ_CRASH("NYI: interpreter JSOP_BIGINT");
+  Register scratch1 = R0.scratchReg();
+  Register scratch2 = R1.scratchReg();
+  loadScriptGCThing(ScriptGCThingType::BigInt, scratch1, scratch2);
+  masm.tagValue(JSVAL_TYPE_BIGINT, scratch1, R0);
+  frame.push(R0);
+  return true;
 }
 
 template <>
 bool BaselineCompilerCodeGen::emit_JSOP_STRING() {
   frame.push(StringValue(handler.script()->getAtom(handler.pc())));
   return true;
 }
 
@@ -2574,17 +2640,18 @@ bool BaselineInterpreterCodeGen::emit_JS
   masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, R0);
   frame.push(R0);
   return true;
 }
 
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_REGEXP() {
   prepareVMCall();
-  pushScriptObjectArg(ScriptObjectType::RegExp);
+  pushScriptGCThingArg(ScriptGCThingType::RegExp, R0.scratchReg(),
+                       R1.scratchReg());
 
   using Fn = JSObject* (*)(JSContext*, Handle<RegExpObject*>);
   if (!callVM<Fn, CloneRegExpObject>()) {
     return false;
   }
 
   // Box and push return value.
   masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, R0);
@@ -2593,17 +2660,18 @@ bool BaselineCodeGen<Handler>::emit_JSOP
 }
 
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_LAMBDA() {
   prepareVMCall();
   masm.loadPtr(frame.addressOfEnvironmentChain(), R0.scratchReg());
 
   pushArg(R0.scratchReg());
-  pushScriptObjectArg(ScriptObjectType::Function);
+  pushScriptGCThingArg(ScriptGCThingType::Function, R0.scratchReg(),
+                       R1.scratchReg());
 
   using Fn = JSObject* (*)(JSContext*, HandleFunction, HandleObject);
   if (!callVM<Fn, js::Lambda>()) {
     return false;
   }
 
   // Box and push return value.
   masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, R0);
@@ -2616,17 +2684,18 @@ bool BaselineCodeGen<Handler>::emit_JSOP
   // Keep pushed newTarget in R0.
   frame.popRegsAndSync(1);
 
   prepareVMCall();
   masm.loadPtr(frame.addressOfEnvironmentChain(), R2.scratchReg());
 
   pushArg(R0);
   pushArg(R2.scratchReg());
-  pushScriptObjectArg(ScriptObjectType::Function);
+  pushScriptGCThingArg(ScriptGCThingType::Function, R0.scratchReg(),
+                       R1.scratchReg());
 
   using Fn =
       JSObject* (*)(JSContext*, HandleFunction, HandleObject, HandleValue);
   if (!callVM<Fn, js::LambdaArrow>()) {
     return false;
   }
 
   // Box and push return value.
@@ -4733,32 +4802,59 @@ static void LoadBaselineScriptResumeEntr
 
   masm.movePtr(ImmGCPtr(script), dest);
   masm.loadPtr(Address(dest, JSScript::offsetOfBaselineScript()), dest);
   masm.load32(Address(dest, BaselineScript::offsetOfResumeEntriesOffset()),
               scratch);
   masm.addPtr(scratch, dest);
 }
 
+template <typename Handler>
+void BaselineCodeGen<Handler>::emitInterpJumpToResumeEntry(Register script,
+                                                           Register resumeIndex,
+                                                           Register scratch) {
+  // Load JSScript::scriptData() into |script|.
+  masm.loadPtr(Address(script, JSScript::offsetOfScriptData()), script);
+
+  // Load the resume pcOffset in |resumeIndex|.
+  masm.load32(Address(script, SharedScriptData::offsetOfResumeOffsetsOffset()),
+              scratch);
+  masm.computeEffectiveAddress(BaseIndex(scratch, resumeIndex, TimesFour),
+                               scratch);
+  masm.load32(BaseIndex(script, scratch, TimesOne), resumeIndex);
+
+  // Load pc* in |script|.
+  masm.load32(Address(script, SharedScriptData::offsetOfCodeOffset()), scratch);
+  masm.addPtr(scratch, script);
+
+  // Add resume offset to PC, jump to it.
+  masm.addPtr(resumeIndex, script);
+  Address pcAddr(BaselineFrameReg,
+                 BaselineFrame::reverseOffsetOfInterpreterPC());
+  masm.storePtr(script, pcAddr);
+  emitJumpToInterpretOpLabel();
+}
+
 template <>
 void BaselineCompilerCodeGen::jumpToResumeEntry(Register resumeIndex,
                                                 Register scratch1,
                                                 Register scratch2) {
   LoadBaselineScriptResumeEntries(masm, handler.script(), scratch1, scratch2);
   masm.loadPtr(
       BaseIndex(scratch1, resumeIndex, ScaleFromElemWidth(sizeof(uintptr_t))),
       scratch1);
   masm.jump(scratch1);
 }
 
 template <>
 void BaselineInterpreterCodeGen::jumpToResumeEntry(Register resumeIndex,
                                                    Register scratch1,
                                                    Register scratch2) {
-  MOZ_CRASH("NYI: interpreter jumpToResumeEntry");
+  loadScript(scratch1);
+  emitInterpJumpToResumeEntry(scratch1, resumeIndex, scratch2);
 }
 
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_RETSUB() {
   frame.popRegsAndSync(2);
 
   Label isReturn;
   masm.branchTestBooleanTruthy(/* branchIfTrue = */ false, R0, &isReturn);
@@ -4832,17 +4928,18 @@ MOZ_MUST_USE bool BaselineInterpreterCod
 }
 
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_PUSHLEXICALENV() {
   // Call a stub to push the block on the block chain.
   prepareVMCall();
   masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
 
-  pushScriptScopeArg();
+  pushScriptGCThingArg(ScriptGCThingType::Scope, R1.scratchReg(),
+                       R2.scratchReg());
   pushArg(R0.scratchReg());
 
   using Fn = bool (*)(JSContext*, BaselineFrame*, Handle<LexicalScope*>);
   return callVM<Fn, jit::PushLexicalEnv>();
 }
 
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_POPLEXICALENV() {
@@ -4929,17 +5026,18 @@ bool BaselineCodeGen<Handler>::emit_JSOP
   };
   return emitDebugInstrumentation(ifDebuggee);
 }
 
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_PUSHVARENV() {
   prepareVMCall();
   masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
-  pushScriptScopeArg();
+  pushScriptGCThingArg(ScriptGCThingType::Scope, R1.scratchReg(),
+                       R2.scratchReg());
   pushArg(R0.scratchReg());
 
   using Fn = bool (*)(JSContext*, BaselineFrame*, HandleScope);
   return callVM<Fn, jit::PushVarEnv>();
 }
 
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_POPVARENV() {
@@ -4953,20 +5051,21 @@ bool BaselineCodeGen<Handler>::emit_JSOP
 
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_ENTERWITH() {
   // Pop "with" object to R0.
   frame.popRegsAndSync(1);
 
   // Call a stub to push the object onto the environment chain.
   prepareVMCall();
+
+  pushScriptGCThingArg(ScriptGCThingType::Scope, R1.scratchReg(),
+                       R2.scratchReg());
+  pushArg(R0);
   masm.loadBaselineFramePtr(BaselineFrameReg, R1.scratchReg());
-
-  pushScriptScopeArg();
-  pushArg(R0);
   pushArg(R1.scratchReg());
 
   using Fn =
       bool (*)(JSContext*, BaselineFrame*, HandleValue, Handle<WithScope*>);
   return callVM<Fn, jit::EnterWith>();
 }
 
 template <typename Handler>
@@ -5269,84 +5368,124 @@ bool BaselineCodeGen<Handler>::emit_JSOP
 
   masm.tagValue(JSVAL_TYPE_STRING, ReturnReg, R0);
 
   masm.bind(&done);
   frame.push(R0);
   return true;
 }
 
+static constexpr uint32_t TableSwitchOpLowOffset = 1 * JUMP_OFFSET_LEN;
+static constexpr uint32_t TableSwitchOpHighOffset = 2 * JUMP_OFFSET_LEN;
+static constexpr uint32_t TableSwitchOpFirstResumeIndexOffset =
+    3 * JUMP_OFFSET_LEN;
+
 template <>
 void BaselineCompilerCodeGen::emitGetTableSwitchIndex(ValueOperand val,
-                                                      Register dest) {
+                                                      Register dest,
+                                                      Register scratch1,
+                                                      Register scratch2) {
   jsbytecode* pc = handler.pc();
   jsbytecode* defaultpc = pc + GET_JUMP_OFFSET(pc);
   Label* defaultLabel = handler.labelOf(defaultpc);
 
-  int32_t low = GET_JUMP_OFFSET(pc + 1 * JUMP_OFFSET_LEN);
-  int32_t high = GET_JUMP_OFFSET(pc + 2 * JUMP_OFFSET_LEN);
+  int32_t low = GET_JUMP_OFFSET(pc + TableSwitchOpLowOffset);
+  int32_t high = GET_JUMP_OFFSET(pc + TableSwitchOpHighOffset);
   int32_t length = high - low + 1;
 
   // Jump to the 'default' pc if not int32 (tableswitch is only used when
   // all cases are int32).
   masm.branchTestInt32(Assembler::NotEqual, val, defaultLabel);
   masm.unboxInt32(val, dest);
 
   // Subtract 'low'. Bounds check.
   if (low != 0) {
     masm.sub32(Imm32(low), dest);
   }
   masm.branch32(Assembler::AboveOrEqual, dest, Imm32(length), defaultLabel);
 }
 
 template <>
 void BaselineInterpreterCodeGen::emitGetTableSwitchIndex(ValueOperand val,
-                                                         Register dest) {
-  MOZ_CRASH("NYI: interpreter emitTableSwitchJumpTableIndex");
+                                                         Register dest,
+                                                         Register scratch1,
+                                                         Register scratch2) {
+  // Jump to the 'default' pc if not int32 (tableswitch is only used when
+  // all cases are int32).
+  Label done, jumpToDefault;
+  masm.branchTestInt32(Assembler::NotEqual, val, &jumpToDefault);
+  masm.unboxInt32(val, dest);
+
+  masm.loadPtr(frame.addressOfInterpreterPC(), scratch1);
+
+  Address lowAddr(scratch1, sizeof(jsbytecode) + TableSwitchOpLowOffset);
+  Address highAddr(scratch1, sizeof(jsbytecode) + TableSwitchOpHighOffset);
+
+  // Jump to default if val > high.
+  masm.branch32(Assembler::LessThan, highAddr, dest, &jumpToDefault);
+
+  // Jump to default if val < low.
+  masm.load32(lowAddr, scratch2);
+  masm.branch32(Assembler::GreaterThan, scratch2, dest, &jumpToDefault);
+
+  // index := val - low.
+  masm.sub32(scratch2, dest);
+  masm.jump(&done);
+
+  masm.bind(&jumpToDefault);
+  emitJump();
+
+  masm.bind(&done);
 }
 
 template <>
 void BaselineCompilerCodeGen::emitTableSwitchJump(Register key,
                                                   Register scratch1,
                                                   Register scratch2) {
   // Jump to resumeEntries[firstResumeIndex + key].
 
   // Note: BytecodeEmitter::allocateResumeIndex static_asserts
   // |firstResumeIndex * sizeof(uintptr_t)| fits in int32_t.
   uint32_t firstResumeIndex =
-      GET_RESUMEINDEX(handler.pc() + 3 * JUMP_OFFSET_LEN);
+      GET_RESUMEINDEX(handler.pc() + TableSwitchOpFirstResumeIndexOffset);
   LoadBaselineScriptResumeEntries(masm, handler.script(), scratch1, scratch2);
   masm.loadPtr(BaseIndex(scratch1, key, ScaleFromElemWidth(sizeof(uintptr_t)),
                          firstResumeIndex * sizeof(uintptr_t)),
                scratch1);
   masm.jump(scratch1);
 }
 
 template <>
 void BaselineInterpreterCodeGen::emitTableSwitchJump(Register key,
                                                      Register scratch1,
                                                      Register scratch2) {
-  MOZ_CRASH("NYI: interpreter emitTableSwitchJump");
+  // Load the op's firstResumeIndex in scratch1.
+  masm.loadPtr(frame.addressOfInterpreterPC(), scratch1);
+  LoadUint24Operand(masm, scratch1, TableSwitchOpFirstResumeIndexOffset,
+                    scratch1);
+
+  masm.add32(key, scratch1);
+  jumpToResumeEntry(scratch1, key, scratch2);
 }
 
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_TABLESWITCH() {
   frame.popRegsAndSync(1);
 
   Register key = R0.scratchReg();
   Register scratch1 = R1.scratchReg();
   Register scratch2 = R2.scratchReg();
 
   // Call a stub to convert R0 from double to int32 if needed.
   // Note: this stub may clobber scratch1.
   masm.call(cx->runtime()->jitRuntime()->getDoubleToInt32ValueStub());
 
   // Load the index in the jump table in |key|, or branch to default pc if not
   // int32 or out-of-range.
-  emitGetTableSwitchIndex(R0, key);
+  emitGetTableSwitchIndex(R0, key, scratch1, scratch2);
 
   // Jump to the target pc.
   emitTableSwitchJump(key, scratch1, scratch2);
   return true;
 }
 
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_ITER() {
@@ -5867,17 +6006,27 @@ bool BaselineCodeGen<Handler>::emitEnter
   // If the Baseline Interpreter is enabled we resume in either the
   // BaselineScript (if present) or Baseline Interpreter.
   Label noBaselineScript;
   masm.branchPtr(Assembler::BelowOrEqual, baselineAddr,
                  ImmPtr(BASELINE_DISABLED_SCRIPT), &noBaselineScript);
   emitEnterBaseline();
 
   masm.bind(&noBaselineScript);
-  MOZ_CRASH("NYI: enter interpreted generator");
+
+  // Initialize interpreter frame fields.
+  Address flagsAddr(BaselineFrameReg, BaselineFrame::reverseOffsetOfFlags());
+  Address scriptAddr(BaselineFrameReg,
+                     BaselineFrame::reverseOffsetOfInterpreterScript());
+  masm.or32(Imm32(BaselineFrame::RUNNING_IN_INTERPRETER), flagsAddr);
+  masm.storePtr(script, scriptAddr);
+
+  // Initialize pc and jump to it.
+  emitInterpJumpToResumeEntry(script, resumeIndex, scratch);
+  return true;
 }
 
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emitGeneratorResume(
     GeneratorResumeKind resumeKind) {
   frame.syncStack(0);
   masm.assertStackAlignment(sizeof(Value), 0);
 
@@ -6364,17 +6513,18 @@ bool BaselineCodeGen<Handler>::emit_JSOP
   frame.popRegsAndSync(1);
 
   masm.unboxObject(R0, R0.scratchReg());
   masm.loadPtr(frame.addressOfEnvironmentChain(), R1.scratchReg());
 
   prepareVMCall();
   pushArg(R0.scratchReg());
   pushArg(R1.scratchReg());
-  pushScriptObjectArg(ScriptObjectType::Function);
+  pushScriptGCThingArg(ScriptGCThingType::Function, R0.scratchReg(),
+                       R1.scratchReg());
 
   using Fn =
       JSObject* (*)(JSContext*, HandleFunction, HandleObject, HandleObject);
   if (!callVM<Fn, js::FunWithProtoOperation>()) {
     return false;
   }
 
   masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, R0);
--- a/js/src/jit/BaselineCompiler.h
+++ b/js/src/jit/BaselineCompiler.h
@@ -251,16 +251,18 @@ namespace jit {
   _(JSOP_FUNWITHPROTO)          \
   _(JSOP_CLASSCONSTRUCTOR)      \
   _(JSOP_DERIVEDCONSTRUCTOR)    \
   _(JSOP_IMPORTMETA)            \
   _(JSOP_DYNAMIC_IMPORT)        \
   _(JSOP_INC)                   \
   _(JSOP_DEC)
 
+enum class ScriptGCThingType { RegExp, Function, Scope, BigInt };
+
 // Base class for BaselineCompiler and BaselineInterpreterGenerator. The Handler
 // template is a class storing fields/methods that are interpreter or compiler
 // specific. This can be combined with template specialization of methods in
 // this class to specialize behavior.
 template <typename Handler>
 class BaselineCodeGen {
  protected:
   Handler handler;
@@ -313,20 +315,21 @@ class BaselineCodeGen {
   // Pushes the current script as argument for a VM function.
   void pushScriptArg();
 
   // Pushes the bytecode pc as argument for a VM function.
   void pushBytecodePCArg();
 
   // Pushes a name/object/scope associated with the current bytecode op (and
   // stored in the script) as argument for a VM function.
-  enum class ScriptObjectType { RegExp, Function };
-  void pushScriptObjectArg(ScriptObjectType type);
+  void loadScriptGCThing(ScriptGCThingType type, Register dest,
+                         Register scratch);
+  void pushScriptGCThingArg(ScriptGCThingType type, Register scratch1,
+                            Register scratch2);
   void pushScriptNameArg(Register scratch1, Register scratch2);
-  void pushScriptScopeArg();
 
   // Pushes a bytecode operand as argument for a VM function.
   void pushUint8BytecodeOperandArg(Register scratch);
   void pushUint16BytecodeOperandArg(Register scratch);
 
   void loadInt32LengthBytecodeOperand(Register dest);
   void loadInt32IndexBytecodeOperand(ValueOperand dest);
   void loadNumFormalArguments(Register dest);
@@ -402,16 +405,18 @@ class BaselineCodeGen {
   template <typename F>
   MOZ_MUST_USE bool emitTestScriptFlag(JSScript::MutableFlags flag, bool value,
                                        const F& emit, Register scratch);
 
   MOZ_MUST_USE bool emitGeneratorResume(GeneratorResumeKind resumeKind);
   MOZ_MUST_USE bool emitEnterGeneratorCode(Register script,
                                            Register resumeIndex,
                                            Register scratch);
+  void emitInterpJumpToResumeEntry(Register script, Register resumeIndex,
+                                   Register scratch);
   void emitJumpToInterpretOpLabel();
 
   MOZ_MUST_USE bool emitIncExecutionProgressCounter(Register scratch);
 
   MOZ_MUST_USE bool emitCheckThis(ValueOperand val, bool reinit = false);
   void emitLoadReturnValue(ValueOperand val);
   void emitPushNonArrowFunctionNewTarget();
   void emitGetAliasedVar(ValueOperand dest);
@@ -439,17 +444,18 @@ class BaselineCodeGen {
   void emitJump();
 
   // For a JOF_JUMP op, jumps to the op's jump target depending on the Value
   // in |val|.
   void emitTestBooleanTruthy(bool branchIfTrue, ValueOperand val);
 
   // Converts |val| to an index in the jump table and stores this in |dest|
   // or branches to the default pc if not int32 or out-of-range.
-  void emitGetTableSwitchIndex(ValueOperand val, Register dest);
+  void emitGetTableSwitchIndex(ValueOperand val, Register dest,
+                               Register scratch1, Register scratch2);
 
   // Jumps to the target of a table switch based on |key| and the
   // firstResumeIndex stored in JSOP_TABLESWITCH.
   void emitTableSwitchJump(Register key, Register scratch1, Register scratch2);
 
   MOZ_MUST_USE bool emitReturn();
 
   MOZ_MUST_USE bool emitToBoolean();
--- a/js/src/vm/JSScript.h
+++ b/js/src/vm/JSScript.h
@@ -1469,19 +1469,23 @@ class alignas(uintptr_t) PrivateScriptDa
 
   // Size to allocate
   static size_t AllocationSize(uint32_t ngcthings);
 
   // Initialize header and PackedSpans
   explicit PrivateScriptData(uint32_t ngcthings);
 
  public:
+  static constexpr size_t offsetOfGCThings() {
+    return sizeof(PrivateScriptData);
+  }
+
   // Accessors for typed array spans.
   mozilla::Span<JS::GCCellPtr> gcthings() {
-    size_t offset = sizeof(PrivateScriptData);
+    size_t offset = offsetOfGCThings();
     return mozilla::MakeSpan(offsetToPointer<JS::GCCellPtr>(offset), ngcthings);
   }
 
   void setFieldInitializers(FieldInitializers fieldInitializers) {
     fieldInitializers_ = fieldInitializers;
   }
   const FieldInitializers& getFieldInitializers() { return fieldInitializers_; }
 
@@ -1773,16 +1777,21 @@ class alignas(uintptr_t) SharedScriptDat
   mozilla::Span<JSTryNote> tryNotes() {
     return mozilla::MakeSpan(offsetToPointer<JSTryNote>(tryNotesOffset()),
                              offsetToPointer<JSTryNote>(endOffset()));
   }
 
   static constexpr size_t offsetOfCodeOffset() {
     return offsetof(SharedScriptData, codeOffset_);
   }
+  static constexpr size_t offsetOfResumeOffsetsOffset() {
+    // Resume-offsets are the first optional array if they exist. Locate the
+    // array with the 'optArrayOffset_' field.
+    return offsetof(SharedScriptData, optArrayOffset_);
+  }
   static constexpr size_t offsetOfNfixed() {
     return offsetof(SharedScriptData, nfixed);
   }
   static constexpr size_t offsetOfNslots() {
     return offsetof(SharedScriptData, nslots);
   }
   static constexpr size_t offsetOfFunLength() {
     return offsetof(SharedScriptData, funLength);
@@ -2547,16 +2556,19 @@ class JSScript : public js::gc::TenuredC
     return offsetof(JSScript, mutableFlags_);
   }
   static size_t offsetOfImmutableFlags() {
     return offsetof(JSScript, immutableFlags_);
   }
   static constexpr size_t offsetOfScriptData() {
     return offsetof(JSScript, scriptData_);
   }
+  static constexpr size_t offsetOfPrivateScriptData() {
+    return offsetof(JSScript, data_);
+  }
   static constexpr size_t offsetOfJitScript() {
     return offsetof(JSScript, jitScript_);
   }
 
   bool hasAnyIonScript() const { return hasIonScript(); }
 
   bool hasIonScript() const {
     bool res = ion && ion != ION_DISABLED_SCRIPT &&