Bug 1520452 part 1 - Move script field from BaselineCodeGen to BaselineCompilerHandler. r=djvj
authorJan de Mooij <jdemooij@mozilla.com>
Thu, 24 Jan 2019 12:59:04 +0000
changeset 515274 d42906b3c65e3009761774c8353a5104335ac4c6
parent 515273 8659b66f06743242f9b5ca5c6ef6d9e0750bd417
child 515275 54afac5c0bb51abc16131e672ce3049cdbd1620b
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)
reviewersdjvj
bugs1520452
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 1520452 part 1 - Move script field from BaselineCodeGen to BaselineCompilerHandler. r=djvj Differential Revision: https://phabricator.services.mozilla.com/D16689
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
@@ -59,17 +59,16 @@ BaselineInterpreterHandler::BaselineInte
 
 template <typename Handler>
 template <typename... HandlerArgs>
 BaselineCodeGen<Handler>::BaselineCodeGen(JSContext* cx, TempAllocator& alloc,
                                           JSScript* script,
                                           HandlerArgs&&... args)
     : handler(masm, std::forward<HandlerArgs>(args)...),
       cx(cx),
-      script(script),
       ionCompileable_(jit::IsIonEnabled(cx) && CanIonCompileScript(cx, script)),
       alloc_(alloc),
       analysis_(alloc, script),
       frame(handler.frame()),
       traceLoggerToggleOffsets_(cx),
       icEntryIndex_(0),
       pushedBeforeCall_(0),
 #ifdef DEBUG
@@ -125,31 +124,32 @@ bool BaselineCompiler::init() {
   }
 
   return true;
 }
 
 bool BaselineCompiler::addPCMappingEntry(bool addIndexEntry) {
   // Don't add multiple entries for a single pc.
   size_t nentries = pcMappingEntries_.length();
-  uint32_t pcOffset = script->pcToOffset(handler.pc());
+  uint32_t pcOffset = handler.script()->pcToOffset(handler.pc());
   if (nentries > 0 && pcMappingEntries_[nentries - 1].pcOffset == pcOffset) {
     return true;
   }
 
   PCMappingEntry entry;
   entry.pcOffset = pcOffset;
   entry.nativeOffset = masm.currentOffset();
   entry.slotInfo = getStackTopSlotInfo();
   entry.addIndexEntry = addIndexEntry;
 
   return pcMappingEntries_.append(entry);
 }
 
 MethodStatus BaselineCompiler::compile() {
+  JSScript* script = handler.script();
   JitSpew(JitSpew_BaselineScripts, "Baseline compiling script %s:%u:%u (%p)",
           script->filename(), script->lineno(), script->column(), script);
 
   JitSpew(JitSpew_Codegen, "# Emitting baseline code for script %s:%u:%u",
           script->filename(), script->lineno(), script->column());
 
   TraceLoggerThread* logger = TraceLoggerForCurrentThread(cx);
   TraceLoggerEvent scriptEvent(TraceLogger_AnnotateScripts, script);
@@ -459,16 +459,17 @@ bool BaselineCompiler::emitOutOfLinePost
 }
 
 template <>
 bool BaselineCompilerCodeGen::emitNextIC() {
   // Emit a call to an IC stored in ICScript. Calls to this must match the
   // ICEntry order in ICScript: first the non-op IC entries for |this| and
   // formal arguments, then the for-op IC entries for JOF_IC ops.
 
+  JSScript* script = handler.script();
   uint32_t pcOffset = script->pcToOffset(handler.pc());
 
   // We don't use every ICEntry and we can skip unreachable ops, so we have
   // to loop until we find an ICEntry for the current pc.
   const ICEntry* entry;
   do {
     entry = &script->icScript()->icEntry(icEntryIndex_);
     icEntryIndex_++;
@@ -676,18 +677,62 @@ void BaselineCompiler::emitIsDebuggeeChe
     masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
     masm.passABIArg(R0.scratchReg());
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, jit::FrameIsDebuggeeCheck));
     masm.Pop(BaselineFrameReg);
   }
 }
 
 template <>
+void BaselineCompilerCodeGen::loadScript(Register dest) {
+  masm.movePtr(ImmGCPtr(handler.script()), dest);
+}
+
+template <>
+void BaselineInterpreterCodeGen::loadScript(Register dest) {
+  MOZ_CRASH("NYI: interpreter loadScript");
+}
+
+template <>
+void BaselineCompilerCodeGen::loadGlobalLexicalEnvironment(Register dest) {
+  masm.movePtr(ImmGCPtr(&cx->global()->lexicalEnvironment()), dest);
+}
+
+template <>
+void BaselineInterpreterCodeGen::loadGlobalLexicalEnvironment(Register dest) {
+  MOZ_CRASH("NYI: interpreter loadGlobalLexicalEnvironment");
+}
+
+template <>
+void BaselineCompilerCodeGen::pushGlobalLexicalEnvironmentValue(
+    ValueOperand scratch) {
+  frame.push(ObjectValue(cx->global()->lexicalEnvironment()));
+}
+
+template <>
+void BaselineInterpreterCodeGen::pushGlobalLexicalEnvironmentValue(
+    ValueOperand scratch) {
+  loadGlobalLexicalEnvironment(scratch.scratchReg());
+  masm.tagValue(JSVAL_TYPE_OBJECT, scratch.scratchReg(), scratch);
+  frame.push(scratch);
+}
+
+template <>
+void BaselineCompilerCodeGen::loadGlobalThisValue(ValueOperand dest) {
+  masm.moveValue(cx->global()->lexicalEnvironment().thisValue(), dest);
+}
+
+template <>
+void BaselineInterpreterCodeGen::loadGlobalThisValue(ValueOperand dest) {
+  MOZ_CRASH("NYI: interpreter loadGlobalThisValue");
+}
+
+template <>
 void BaselineCompilerCodeGen::pushScriptArg() {
-  pushArg(ImmGCPtr(script));
+  pushArg(ImmGCPtr(handler.script()));
 }
 
 template <>
 void BaselineInterpreterCodeGen::pushScriptArg() {
   MOZ_CRASH("NYI: interpreter pushScriptArg");
 }
 
 template <>
@@ -698,26 +743,27 @@ void BaselineCompilerCodeGen::pushByteco
 template <>
 void BaselineInterpreterCodeGen::pushBytecodePCArg() {
   // This will be something like pushArg(Address(...));
   MOZ_CRASH("NYI: interpreter pushBytecodePCArg");
 }
 
 template <>
 void BaselineCompilerCodeGen::pushScriptNameArg() {
-  pushArg(ImmGCPtr(script->getName(handler.pc())));
+  pushArg(ImmGCPtr(handler.script()->getName(handler.pc())));
 }
 
 template <>
 void BaselineInterpreterCodeGen::pushScriptNameArg() {
   MOZ_CRASH("NYI: interpreter pushScriptNameArg");
 }
 
 template <>
 void BaselineCompilerCodeGen::pushScriptObjectArg(ScriptObjectType type) {
+  JSScript* script = handler.script();
   switch (type) {
     case ScriptObjectType::RegExp:
       pushArg(ImmGCPtr(script->getRegExp(handler.pc())));
       return;
     case ScriptObjectType::Function:
       pushArg(ImmGCPtr(script->getFunction(handler.pc())));
       return;
     case ScriptObjectType::ObjectLiteral:
@@ -729,17 +775,17 @@ void BaselineCompilerCodeGen::pushScript
 
 template <>
 void BaselineInterpreterCodeGen::pushScriptObjectArg(ScriptObjectType type) {
   MOZ_CRASH("NYI: interpreter pushScriptObjectArg");
 }
 
 template <>
 void BaselineCompilerCodeGen::pushScriptScopeArg() {
-  pushArg(ImmGCPtr(script->getScope(handler.pc())));
+  pushArg(ImmGCPtr(handler.script()->getScope(handler.pc())));
 }
 
 template <>
 void BaselineInterpreterCodeGen::pushScriptScopeArg() {
   MOZ_CRASH("NYI: interpreter pushScriptScopeArg");
 }
 
 template <>
@@ -823,17 +869,17 @@ static const VMFunction InitFunctionEnvi
         jit::InitFunctionEnvironmentObjects, "InitFunctionEnvironmentObjects");
 
 bool BaselineCompiler::initEnvironmentChain() {
   CallVMPhase phase = POST_INITIALIZE;
   if (needsEarlyStackCheck()) {
     phase = CHECK_OVER_RECURSED;
   }
 
-  RootedFunction fun(cx, function());
+  RootedFunction fun(cx, handler.function());
   if (fun) {
     // Use callee->environment as env chain. Note that we do this also
     // for needsSomeEnvironmentObject functions, so that the env chain
     // slot is properly initialized if the call triggers GC.
     Register callee = R0.scratchReg();
     Register scope = R1.scratchReg();
     masm.loadFunctionFromCalleeToken(frame.addressOfCalleeToken(), callee);
     masm.loadPtr(Address(callee, JSFunction::offsetOfEnvironment()), scope);
@@ -845,20 +891,20 @@ bool BaselineCompiler::initEnvironmentCh
 
       masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
       pushArg(R0.scratchReg());
 
       if (!callVMNonOp(InitFunctionEnvironmentObjectsInfo, phase)) {
         return false;
       }
     }
-  } else if (module()) {
+  } else if (handler.module()) {
     // Modules use a pre-created scope object.
     Register scope = R1.scratchReg();
-    masm.movePtr(ImmGCPtr(&module()->initialEnvironment()), scope);
+    masm.movePtr(ImmGCPtr(&handler.module()->initialEnvironment()), scope);
     masm.storePtr(scope, frame.addressOfEnvironmentChain());
   } else {
     // EnvironmentChain pointer in BaselineFrame has already been initialized
     // in prologue, but we need to check for redeclaration errors.
 
     prepareVMCall();
 
     pushScriptArg();
@@ -910,16 +956,17 @@ bool BaselineCompilerCodeGen::emitWarmUp
   }
 
   frame.assertSyncedStack();
 
   Register scriptReg = R2.scratchReg();
   Register countReg = R0.scratchReg();
   Address warmUpCounterAddr(scriptReg, JSScript::offsetOfWarmUpCounter());
 
+  JSScript* script = handler.script();
   masm.movePtr(ImmGCPtr(script), scriptReg);
   masm.load32(warmUpCounterAddr, countReg);
   masm.add32(Imm32(1), countReg);
   masm.store32(countReg, warmUpCounterAddr);
 
   jsbytecode* pc = handler.pc();
   if (JSOp(*pc) == JSOP_LOOPENTRY) {
     // If this is a loop inside a catch or finally block, increment the warmup
@@ -975,43 +1022,46 @@ bool BaselineCompilerCodeGen::emitWarmUp
 }
 
 template <>
 bool BaselineInterpreterCodeGen::emitWarmUpCounterIncrement() {
   MOZ_CRASH("NYI: interpreter emitWarmUpCounterIncrement");
 }
 
 bool BaselineCompiler::emitArgumentTypeChecks() {
-  if (!function()) {
+  if (!handler.function()) {
     return true;
   }
 
   frame.pushThis();
   frame.popRegsAndSync(1);
 
   if (!emitNextIC()) {
     return false;
   }
 
-  for (size_t i = 0; i < function()->nargs(); i++) {
+  size_t nargs = handler.function()->nargs();
+
+  for (size_t i = 0; i < nargs; i++) {
     frame.pushArg(i);
     frame.popRegsAndSync(1);
 
     if (!emitNextIC()) {
       return false;
     }
   }
 
   return true;
 }
 
 bool BaselineCompiler::emitDebugTrap() {
   MOZ_ASSERT(compileDebugInstrumentation());
   MOZ_ASSERT(frame.numUnsyncedSlots() == 0);
 
+  JSScript* script = handler.script();
   bool enabled =
       script->stepModeEnabled() || script->hasBreakpointsAt(handler.pc());
 
 #if defined(JS_CODEGEN_ARM64)
   // Flush any pending constant pools to prevent incorrect
   // PCMappingEntry offsets. See Bug 1446819.
   masm.flush();
   // Fix up the PCMappingEntry to avoid any constant pool.
@@ -1049,17 +1099,17 @@ bool BaselineCompiler::emitTraceLoggerEn
   }
 
   masm.Push(loggerReg);
   masm.Push(scriptReg);
 
   masm.loadTraceLogger(loggerReg);
 
   // Script start.
-  masm.movePtr(ImmGCPtr(script), scriptReg);
+  masm.movePtr(ImmGCPtr(handler.script()), scriptReg);
   masm.loadPtr(Address(scriptReg, JSScript::offsetOfBaselineScript()),
                scriptReg);
   Address scriptEvent(scriptReg,
                       BaselineScript::offsetOfTraceLoggerScriptEvent());
   masm.computeEffectiveAddress(scriptEvent, scriptReg);
   masm.tracelogStartEvent(loggerReg, scriptReg);
 
   // Engine start.
@@ -1342,16 +1392,82 @@ template <>
 void BaselineInterpreterCodeGen::emitTestBooleanTruthy(bool branchIfTrue,
                                                        ValueOperand val) {
   Label done;
   masm.branchTestBooleanTruthy(!branchIfTrue, val, &done);
   emitJump();
   masm.bind(&done);
 }
 
+template <>
+template <typename F1, typename F2>
+MOZ_MUST_USE bool BaselineCompilerCodeGen::emitTestScriptFlag(
+    JSScript::ImmutableFlags flag, const F1& ifSet, const F2& ifNotSet,
+    Register scratch) {
+  return handler.script()->hasFlag(flag) ? ifSet() : ifNotSet();
+}
+
+template <>
+template <typename F1, typename F2>
+MOZ_MUST_USE bool BaselineInterpreterCodeGen::emitTestScriptFlag(
+    JSScript::ImmutableFlags flag, const F1& ifSet, const F2& ifNotSet,
+    Register scratch) {
+  Label flagNotSet, done;
+  loadScript(scratch);
+  masm.branchTest32(Assembler::Zero,
+                    Address(scratch, JSScript::offsetOfImmutableFlags()),
+                    Imm32(uint32_t(flag)), &flagNotSet);
+  {
+    if (!ifSet()) {
+      return false;
+    }
+    masm.jump(&done);
+  }
+  masm.bind(&flagNotSet);
+  {
+    if (!ifNotSet()) {
+      return false;
+    }
+  }
+
+  masm.bind(&done);
+  return true;
+}
+
+template <>
+template <typename F>
+MOZ_MUST_USE bool BaselineCompilerCodeGen::emitTestScriptFlag(
+    JSScript::ImmutableFlags flag, bool value, const F& emit,
+    Register scratch) {
+  if (handler.script()->hasFlag(flag) == value) {
+    return emit();
+  }
+  return true;
+}
+
+template <>
+template <typename F>
+MOZ_MUST_USE bool BaselineInterpreterCodeGen::emitTestScriptFlag(
+    JSScript::ImmutableFlags flag, bool value, const F& emit,
+    Register scratch) {
+  Label done;
+  loadScript(scratch);
+  masm.branchTest32(value ? Assembler::Zero : Assembler::NonZero,
+                    Address(scratch, JSScript::offsetOfImmutableFlags()),
+                    Imm32(uint32_t(flag)), &done);
+  {
+    if (!emit()) {
+      return false;
+    }
+  }
+
+  masm.bind(&done);
+  return true;
+}
+
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_GOTO() {
   frame.syncStack(0);
   emitJump();
   return true;
 }
 
 template <typename Handler>
@@ -1468,21 +1584,24 @@ template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_LOOPENTRY() {
   if (!emit_JSOP_JUMPTARGET()) {
     return false;
   }
   frame.syncStack(0);
   if (!emitWarmUpCounterIncrement()) {
     return false;
   }
-  if (script->trackRecordReplayProgress()) {
+
+  auto incCounter = [this]() {
     masm.inc64(
         AbsoluteAddress(mozilla::recordreplay::ExecutionProgressCounter()));
-  }
-  return true;
+    return true;
+  };
+  return emitTestScriptFlag(JSScript::ImmutableFlags::TrackRecordReplayProgress,
+                            true, incCounter, R2.scratchReg());
 }
 
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_VOID() {
   frame.pop();
   frame.push(UndefinedValue());
   return true;
 }
@@ -1606,17 +1725,18 @@ bool BaselineCodeGen<Handler>::emitCheck
 
 typedef bool (*ThrowBadDerivedReturnFn)(JSContext*, HandleValue);
 static const VMFunction ThrowBadDerivedReturnInfo =
     FunctionInfo<ThrowBadDerivedReturnFn>(jit::ThrowBadDerivedReturn,
                                           "ThrowBadDerivedReturn");
 
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_CHECKRETURN() {
-  MOZ_ASSERT(script->isDerivedClassConstructor());
+  MOZ_ASSERT_IF(handler.maybeScript(),
+                handler.maybeScript()->isDerivedClassConstructor());
 
   // Load |this| in R0, return value in R1.
   frame.popRegsAndSync(1);
   emitLoadReturnValue(R1);
 
   Label done, returnOK;
   masm.branchTestObject(Assembler::Equal, R1, &done);
   masm.branchTestUndefined(Assembler::Equal, R1, &returnOK);
@@ -1644,74 +1764,76 @@ bool BaselineCodeGen<Handler>::emit_JSOP
 
 typedef bool (*GetFunctionThisFn)(JSContext*, BaselineFrame*,
                                   MutableHandleValue);
 static const VMFunction GetFunctionThisInfo = FunctionInfo<GetFunctionThisFn>(
     jit::BaselineGetFunctionThis, "BaselineGetFunctionThis");
 
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_FUNCTIONTHIS() {
-  MOZ_ASSERT(function());
-  MOZ_ASSERT(!function()->isArrow());
+  MOZ_ASSERT_IF(handler.maybeFunction(), !handler.maybeFunction()->isArrow());
 
   frame.pushThis();
 
+  auto boxThis = [this]() {
+    // Load |thisv| in R0. Skip the call if it's already an object.
+    Label skipCall;
+    frame.popRegsAndSync(1);
+    masm.branchTestObject(Assembler::Equal, R0, &skipCall);
+
+    prepareVMCall();
+    masm.loadBaselineFramePtr(BaselineFrameReg, R1.scratchReg());
+
+    pushArg(R1.scratchReg());
+
+    if (!callVM(GetFunctionThisInfo)) {
+      return false;
+    }
+
+    masm.bind(&skipCall);
+    frame.push(R0);
+    return true;
+  };
+
   // In strict mode code, |this| is left alone.
-  if (script->strict()) {
-    return true;
-  }
-
-  // Load |thisv| in R0. Skip the call if it's already an object.
-  Label skipCall;
-  frame.popRegsAndSync(1);
-  masm.branchTestObject(Assembler::Equal, R0, &skipCall);
-
-  prepareVMCall();
-  masm.loadBaselineFramePtr(BaselineFrameReg, R1.scratchReg());
-
-  pushArg(R1.scratchReg());
-
-  if (!callVM(GetFunctionThisInfo)) {
-    return false;
-  }
-
-  masm.bind(&skipCall);
-  frame.push(R0);
-  return true;
+  return emitTestScriptFlag(JSScript::ImmutableFlags::Strict, false, boxThis,
+                            R2.scratchReg());
 }
 
 typedef void (*GetNonSyntacticGlobalThisFn)(JSContext*, HandleObject,
                                             MutableHandleValue);
 static const VMFunction GetNonSyntacticGlobalThisInfo =
     FunctionInfo<GetNonSyntacticGlobalThisFn>(js::GetNonSyntacticGlobalThis,
                                               "GetNonSyntacticGlobalThis");
 
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_GLOBALTHIS() {
   frame.syncStack(0);
 
-  if (!script->hasNonSyntacticScope()) {
-    LexicalEnvironmentObject* globalLexical =
-        &script->global().lexicalEnvironment();
-    masm.moveValue(globalLexical->thisValue(), R0);
+  auto getNonSyntacticThis = [this]() {
+    prepareVMCall();
+
+    masm.loadPtr(frame.addressOfEnvironmentChain(), R0.scratchReg());
+    pushArg(R0.scratchReg());
+
+    if (!callVM(GetNonSyntacticGlobalThisInfo)) {
+      return false;
+    }
+
     frame.push(R0);
     return true;
-  }
-
-  prepareVMCall();
-
-  masm.loadPtr(frame.addressOfEnvironmentChain(), R0.scratchReg());
-  pushArg(R0.scratchReg());
-
-  if (!callVM(GetNonSyntacticGlobalThisInfo)) {
-    return false;
-  }
-
-  frame.push(R0);
-  return true;
+  };
+  auto getGlobalThis = [this]() {
+    loadGlobalThisValue(R0);
+    frame.push(R0);
+    return true;
+  };
+  return emitTestScriptFlag(JSScript::ImmutableFlags::HasNonSyntacticScope,
+                            getNonSyntacticThis, getGlobalThis,
+                            R2.scratchReg());
 }
 
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_TRUE() {
   frame.push(BooleanValue(true));
   return true;
 }
 
@@ -1779,17 +1901,17 @@ bool BaselineInterpreterCodeGen::emit_JS
 
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_RESUMEINDEX() {
   return emit_JSOP_UINT24();
 }
 
 template <>
 bool BaselineCompilerCodeGen::emit_JSOP_DOUBLE() {
-  frame.push(script->getConst(GET_UINT32_INDEX(handler.pc())));
+  frame.push(handler.script()->getConst(GET_UINT32_INDEX(handler.pc())));
   return true;
 }
 
 template <>
 bool BaselineInterpreterCodeGen::emit_JSOP_DOUBLE() {
   MOZ_CRASH("NYI: interpreter JSOP_DOUBLE");
 }
 
@@ -1797,17 +1919,17 @@ bool BaselineInterpreterCodeGen::emit_JS
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_BIGINT() {
   return emit_JSOP_DOUBLE();
 }
 #endif
 
 template <>
 bool BaselineCompilerCodeGen::emit_JSOP_STRING() {
-  frame.push(StringValue(script->getAtom(handler.pc())));
+  frame.push(StringValue(handler.script()->getAtom(handler.pc())));
   return true;
 }
 
 template <>
 bool BaselineInterpreterCodeGen::emit_JSOP_STRING() {
   MOZ_CRASH("NYI: interpreter JSOP_STRING");
 }
 
@@ -1844,27 +1966,28 @@ bool BaselineCompilerCodeGen::emit_JSOP_
 
     // Box and push return value.
     masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, R0);
     frame.push(R0);
     return true;
   }
 
   cx->realm()->behaviors().setSingletonsAsValues();
-  frame.push(ObjectValue(*script->getObject(handler.pc())));
+  frame.push(ObjectValue(*handler.script()->getObject(handler.pc())));
   return true;
 }
 
 template <>
 bool BaselineInterpreterCodeGen::emit_JSOP_OBJECT() {
   MOZ_CRASH("NYI: interpreter JSOP_OBJECT");
 }
 
 template <>
 bool BaselineCompilerCodeGen::emit_JSOP_CALLSITEOBJ() {
+  JSScript* script = handler.script();
   jsbytecode* pc = handler.pc();
   RootedObject cso(cx, script->getObject(pc));
   RootedObject raw(cx, script->getObject(GET_UINT32_INDEX(pc) + 1));
   if (!cso || !raw) {
     return false;
   }
 
   if (!ProcessCallSiteObjOperation(cx, cso, raw)) {
@@ -2194,17 +2317,17 @@ bool BaselineInterpreterCodeGen::emit_JS
 
 typedef ArrayObject* (*NewArrayCopyOnWriteFn)(JSContext*, HandleArrayObject,
                                               gc::InitialHeap);
 const VMFunction NewArrayCopyOnWriteInfo = FunctionInfo<NewArrayCopyOnWriteFn>(
     js::NewDenseCopyOnWriteArray, "NewDenseCopyOnWriteArray");
 
 template <>
 bool BaselineCompilerCodeGen::emit_JSOP_NEWARRAY_COPYONWRITE() {
-  RootedScript scriptRoot(cx, script);
+  RootedScript scriptRoot(cx, handler.script());
   JSObject* obj =
       ObjectGroup::getOrFixupCopyOnWriteObject(cx, scriptRoot, handler.pc());
   if (!obj) {
     return false;
   }
 
   prepareVMCall();
 
@@ -2526,17 +2649,17 @@ bool BaselineCodeGen<Handler>::emit_JSOP
   }
 
   frame.push(R0);
   return true;
 }
 
 template <>
 bool BaselineCompilerCodeGen::tryOptimizeGetGlobalName() {
-  PropertyName* name = script->getName(handler.pc());
+  PropertyName* name = handler.script()->getName(handler.pc());
 
   // These names are non-configurable on the global and cannot be shadowed.
   if (name == cx->names().undefined) {
     frame.push(UndefinedValue());
     return true;
   }
   if (name == cx->names().NaN) {
     frame.push(cx->runtime()->NaNValue);
@@ -2553,41 +2676,43 @@ bool BaselineCompilerCodeGen::tryOptimiz
 template <>
 bool BaselineInterpreterCodeGen::tryOptimizeGetGlobalName() {
   // Interpreter doesn't optimize simple GETGNAMEs.
   return false;
 }
 
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_GETGNAME() {
-  if (script->hasNonSyntacticScope()) {
-    return emit_JSOP_GETNAME();
-  }
-
-  if (tryOptimizeGetGlobalName()) {
+  auto getName = [this]() { return emit_JSOP_GETNAME(); };
+
+  auto getGlobalName = [this]() {
+    if (tryOptimizeGetGlobalName()) {
+      return true;
+    }
+
+    frame.syncStack(0);
+
+    loadGlobalLexicalEnvironment(R0.scratchReg());
+
+    // Call IC.
+    if (!emitNextIC()) {
+      return false;
+    }
+
+    // Mark R0 as pushed stack value.
+    frame.push(R0);
     return true;
-  }
-
-  frame.syncStack(0);
-
-  masm.movePtr(ImmGCPtr(&script->global().lexicalEnvironment()),
-               R0.scratchReg());
-
-  // Call IC.
-  if (!emitNextIC()) {
-    return false;
-  }
-
-  // Mark R0 as pushed stack value.
-  frame.push(R0);
-  return true;
+  };
+  return emitTestScriptFlag(JSScript::ImmutableFlags::HasNonSyntacticScope,
+                            getName, getGlobalName, R2.scratchReg());
 }
 
 template <>
 bool BaselineCompilerCodeGen::tryOptimizeBindGlobalName() {
+  JSScript* script = handler.script();
   if (script->hasNonSyntacticScope()) {
     return false;
   }
 
   // We can bind name to the global lexical scope if the binding already
   // exists, is initialized, and is writable (i.e., an initialized
   // 'let') at compile time.
   RootedPropertyName name(cx, script->getName(handler.pc()));
@@ -2835,16 +2960,17 @@ void BaselineCompilerCodeGen::getEnviron
 template <>
 void BaselineInterpreterCodeGen::getEnvironmentCoordinateObject(Register reg) {
   MOZ_CRASH("NYI: interpreter getEnvironmentCoordinateObject");
 }
 
 template <>
 Address BaselineCompilerCodeGen::getEnvironmentCoordinateAddressFromObject(
     Register objReg, Register reg) {
+  JSScript* script = handler.script();
   EnvironmentCoordinate ec(handler.pc());
   Shape* shape = EnvironmentCoordinateToEnvironmentShape(script, handler.pc());
 
   if (shape->numFixedSlots() <= ec.slot()) {
     masm.loadPtr(Address(objReg, NativeObject::offsetOfSlots()), reg);
     return Address(reg, (ec.slot() - shape->numFixedSlots()) * sizeof(Value));
   }
 
@@ -2927,25 +3053,40 @@ bool BaselineCodeGen<Handler>::emit_JSOP
 
   // Mark R0 as pushed stack value.
   frame.push(R0);
   return true;
 }
 
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emitBindName(JSOp op) {
-  MOZ_ASSERT(op == JSOP_BINDNAME || op == JSOP_BINDGNAME);
+  // If we have a BINDGNAME without a non-syntactic scope, we pass the global
+  // lexical environment to the IC instead of the frame's environment.
 
   frame.syncStack(0);
 
-  if (op == JSOP_BINDGNAME && !script->hasNonSyntacticScope()) {
-    masm.movePtr(ImmGCPtr(&script->global().lexicalEnvironment()),
-                 R0.scratchReg());
+  auto loadGlobalLexical = [this]() {
+    loadGlobalLexicalEnvironment(R0.scratchReg());
+    return true;
+  };
+  auto loadFrameEnv = [this]() {
+    masm.loadPtr(frame.addressOfEnvironmentChain(), R0.scratchReg());
+    return true;
+  };
+
+  if (op == JSOP_BINDNAME) {
+    if (!loadFrameEnv()) {
+      return false;
+    }
   } else {
-    masm.loadPtr(frame.addressOfEnvironmentChain(), R0.scratchReg());
+    MOZ_ASSERT(op == JSOP_BINDGNAME);
+    if (!emitTestScriptFlag(JSScript::ImmutableFlags::HasNonSyntacticScope,
+                            loadFrameEnv, loadGlobalLexical, R2.scratchReg())) {
+      return false;
+    }
   }
 
   // Call IC.
   if (!emitNextIC()) {
     return false;
   }
 
   // Mark R0 as pushed stack value.
@@ -2978,16 +3119,17 @@ bool BaselineCodeGen<Handler>::emit_JSOP
   }
 
   frame.push(R0);
   return true;
 }
 
 template <>
 bool BaselineCompilerCodeGen::emit_JSOP_GETIMPORT() {
+  JSScript* script = handler.script();
   ModuleEnvironmentObject* env = GetModuleEnvironmentForScript(script);
   MOZ_ASSERT(env);
 
   jsid id = NameToId(script->getName(handler.pc()));
   ModuleEnvironmentObject* targetEnv;
   Shape* shape;
   MOZ_ALWAYS_TRUE(env->lookupImport(id, &targetEnv, &shape));
 
@@ -3273,17 +3415,17 @@ bool BaselineInterpreterCodeGen::emit_JS
 template <>
 bool BaselineCompilerCodeGen::emitFormalArgAccess(JSOp op) {
   MOZ_ASSERT(op == JSOP_GETARG || op == JSOP_SETARG);
 
   uint32_t arg = GET_ARGNO(handler.pc());
 
   // Fast path: the script does not use |arguments| or formals don't
   // alias the arguments object.
-  if (!script->argumentsAliasesFormals()) {
+  if (!handler.script()->argumentsAliasesFormals()) {
     if (op == JSOP_GETARG) {
       frame.pushArg(arg);
     } else {
       // See the comment in emit_JSOP_SETLOCAL.
       frame.syncStack(1);
       frame.storeStackValue(-1, frame.addressOfArg(arg), R0);
     }
 
@@ -3292,17 +3434,17 @@ bool BaselineCompilerCodeGen::emitFormal
 
   // Sync so that we can use R0.
   frame.syncStack(0);
 
   // If the script is known to have an arguments object, we can just use it.
   // Else, we *may* have an arguments object (because we can't invalidate
   // when needsArgsObj becomes |true|), so we have to test HAS_ARGS_OBJ.
   Label done;
-  if (!script->needsArgsObj()) {
+  if (!handler.script()->needsArgsObj()) {
     Label hasArgsObj;
     masm.branchTest32(Assembler::NonZero, frame.addressOfFlags(),
                       Imm32(BaselineFrame::HAS_ARGS_OBJ), &hasArgsObj);
     if (op == JSOP_GETARG) {
       masm.loadValue(frame.addressOfArg(arg), R0);
     } else {
       frame.storeStackValue(-1, frame.addressOfArg(arg), R0);
     }
@@ -3358,36 +3500,38 @@ bool BaselineInterpreterCodeGen::emitFor
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_GETARG() {
   return emitFormalArgAccess(JSOP_GETARG);
 }
 
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_SETARG() {
   // Ionmonkey can't inline functions with SETARG with magic arguments.
-  if (!script->argsObjAliasesFormals() && script->argumentsAliasesFormals()) {
-    script->setUninlineable();
+  if (JSScript* script = handler.maybeScript()) {
+    if (!script->argsObjAliasesFormals() && script->argumentsAliasesFormals()) {
+      script->setUninlineable();
+    }
   }
 
   modifiesArguments_ = true;
 
   return emitFormalArgAccess(JSOP_SETARG);
 }
 
-template <typename Handler>
-bool BaselineCodeGen<Handler>::emit_JSOP_NEWTARGET() {
-  if (script->isForEval()) {
+template <>
+bool BaselineCompilerCodeGen::emit_JSOP_NEWTARGET() {
+  if (handler.script()->isForEval()) {
     frame.pushEvalNewTarget();
     return true;
   }
 
-  MOZ_ASSERT(function());
+  MOZ_ASSERT(handler.function());
   frame.syncStack(0);
 
-  if (function()->isArrow()) {
+  if (handler.function()->isArrow()) {
     // Arrow functions store their |new.target| value in an
     // extended slot.
     Register scratch = R0.scratchReg();
     masm.loadFunctionFromCalleeToken(frame.addressOfCalleeToken(), scratch);
     masm.loadValue(
         Address(scratch, FunctionExtended::offsetOfArrowNewTargetSlot()), R0);
     frame.push(R0);
     return true;
@@ -3400,46 +3544,50 @@ bool BaselineCodeGen<Handler>::emit_JSOP
 
   Register argvLen = R0.scratchReg();
 
   Address actualArgs(BaselineFrameReg, BaselineFrame::offsetOfNumActualArgs());
   masm.loadPtr(actualArgs, argvLen);
 
   Label useNFormals;
 
-  masm.branchPtr(Assembler::Below, argvLen, Imm32(function()->nargs()),
-                 &useNFormals);
+  uint32_t nargs = handler.function()->nargs();
+  masm.branchPtr(Assembler::Below, argvLen, Imm32(nargs), &useNFormals);
 
   {
     BaseValueIndex newTarget(BaselineFrameReg, argvLen,
                              BaselineFrame::offsetOfArg(0));
     masm.loadValue(newTarget, R0);
     masm.jump(&done);
   }
 
   masm.bind(&useNFormals);
 
   {
-    Address newTarget(
-        BaselineFrameReg,
-        BaselineFrame::offsetOfArg(0) + (function()->nargs() * sizeof(Value)));
+    Address newTarget(BaselineFrameReg,
+                      BaselineFrame::offsetOfArg(0) + (nargs * sizeof(Value)));
     masm.loadValue(newTarget, R0);
     masm.jump(&done);
   }
 
   // else push(undefined)
   masm.bind(&notConstructing);
   masm.moveValue(UndefinedValue(), R0);
 
   masm.bind(&done);
   frame.push(R0);
 
   return true;
 }
 
+template <>
+bool BaselineInterpreterCodeGen::emit_JSOP_NEWTARGET() {
+  MOZ_CRASH("NYI: interpreter JSOP_NEWTARGET");
+}
+
 typedef bool (*ThrowRuntimeLexicalErrorFn)(JSContext* cx, unsigned);
 static const VMFunction ThrowRuntimeLexicalErrorInfo =
     FunctionInfo<ThrowRuntimeLexicalErrorFn>(jit::ThrowRuntimeLexicalError,
                                              "ThrowRuntimeLexicalError");
 
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emitThrowConstAssignment() {
   prepareVMCall();
@@ -3494,17 +3642,17 @@ bool BaselineInterpreterCodeGen::emit_JS
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_INITLEXICAL() {
   return emit_JSOP_SETLOCAL();
 }
 
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_INITGLEXICAL() {
   frame.popRegsAndSync(1);
-  frame.push(ObjectValue(script->global().lexicalEnvironment()));
+  pushGlobalLexicalEnvironmentValue(R1);
   frame.push(R0);
   return emit_JSOP_SETPROP();
 }
 
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_CHECKALIASEDLEXICAL() {
   frame.syncStack(0);
   masm.loadValue(getEnvironmentCoordinateAddress(R0.scratchReg()), R0);
@@ -3679,22 +3827,24 @@ bool BaselineCodeGen<Handler>::emit_JSOP
   }
 
   frame.push(R0);
   return true;
 }
 
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_GIMPLICITTHIS() {
-  if (!script->hasNonSyntacticScope()) {
+  auto pushUndefined = [this]() {
     frame.push(UndefinedValue());
     return true;
-  }
-
-  return emit_JSOP_IMPLICITTHIS();
+  };
+  auto emitImplicitThis = [this]() { return emit_JSOP_IMPLICITTHIS(); };
+
+  return emitTestScriptFlag(JSScript::ImmutableFlags::HasNonSyntacticScope,
+                            emitImplicitThis, pushUndefined, R2.scratchReg());
 }
 
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_INSTANCEOF() {
   frame.popRegsAndSync(2);
 
   if (!emitNextIC()) {
     return false;
@@ -3748,17 +3898,20 @@ bool BaselineCodeGen<Handler>::emit_JSOP
 
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_TRY() {
   if (!emit_JSOP_JUMPTARGET()) {
     return false;
   }
 
   // Ionmonkey can't inline function with JSOP_TRY.
-  script->setUninlineable();
+  if (JSScript* script = handler.maybeScript()) {
+    script->setUninlineable();
+  }
+
   return true;
 }
 
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_FINALLY() {
   // JSOP_FINALLY has a def count of 2, but these values are already on the
   // stack (they're pushed by JSOP_GOSUB). Update the compiler's stack state.
   frame.incStackDepth(2);
@@ -3783,16 +3936,34 @@ 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 <>
+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");
+}
+
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_RETSUB() {
   frame.popRegsAndSync(2);
 
   Label isReturn;
   masm.branchTestBooleanTruthy(/* branchIfTrue = */ false, R0, &isReturn);
 
   // R0 is |true|. We need to throw R1.
@@ -3800,24 +3971,22 @@ bool BaselineCodeGen<Handler>::emit_JSOP
   pushArg(R1);
   if (!callVM(ThrowInfo)) {
     return false;
   }
 
   masm.bind(&isReturn);
 
   // R0 is |false|. R1 contains the resumeIndex to jump to.
+  Register resumeIndexReg = R1.scratchReg();
+  masm.unboxInt32(R1, resumeIndexReg);
+
   Register scratch1 = R2.scratchReg();
   Register scratch2 = R0.scratchReg();
-  LoadBaselineScriptResumeEntries(masm, script, scratch1, scratch2);
-  masm.unboxInt32(R1, scratch2);
-  masm.loadPtr(
-      BaseIndex(scratch1, scratch2, ScaleFromElemWidth(sizeof(uintptr_t))),
-      scratch1);
-  masm.jump(scratch1);
+  jumpToResumeEntry(resumeIndexReg, scratch1, scratch2);
   return true;
 }
 
 template <>
 template <typename F1, typename F2>
 MOZ_MUST_USE bool BaselineCompilerCodeGen::emitDebugInstrumentation(
     const F1& ifDebuggee, const Maybe<F2>& ifNotDebuggee) {
   // The JIT calls either ifDebuggee or (if present) ifNotDebuggee, because it
@@ -4160,17 +4329,17 @@ void BaselineCodeGen<Handler>::emitLoadR
 }
 
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_RETRVAL() {
   frame.assertStackDepth(0);
 
   masm.moveValue(UndefinedValue(), JSReturnOperand);
 
-  if (!script->noScriptRval()) {
+  if (!handler.maybeScript() || !handler.maybeScript()->noScriptRval()) {
     // Return the value in the return value slot, if any.
     Label done;
     Address flags = frame.addressOfFlags();
     masm.branchTest32(Assembler::Zero, flags, Imm32(BaselineFrame::HAS_RVAL),
                       &done);
     masm.loadValue(frame.addressOfReturnValue(), JSReturnOperand);
     masm.bind(&done);
   }
@@ -4401,17 +4570,17 @@ void BaselineCompilerCodeGen::emitTableS
                                                   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);
-  LoadBaselineScriptResumeEntries(masm, script, scratch1, scratch2);
+  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,
@@ -4524,17 +4693,18 @@ bool BaselineCodeGen<Handler>::emit_JSOP
   frame.storeStackValue(-1, frame.addressOfReturnValue(), R2);
   masm.or32(Imm32(BaselineFrame::HAS_RVAL), frame.addressOfFlags());
   frame.pop();
   return true;
 }
 
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_CALLEE() {
-  MOZ_ASSERT(function());
+  MOZ_ASSERT_IF(handler.maybeScript(),
+                handler.maybeScript()->functionNonDelazifying());
   frame.syncStack(0);
   masm.loadFunctionFromCalleeToken(frame.addressOfCalleeToken(),
                                    R0.scratchReg());
   masm.tagValue(JSVAL_TYPE_OBJECT, R0.scratchReg(), R0);
   frame.push(R0);
   return true;
 }
 
@@ -4666,28 +4836,29 @@ typedef bool (*NewArgumentsObjectFn)(JSC
 static const VMFunction NewArgumentsObjectInfo =
     FunctionInfo<NewArgumentsObjectFn>(jit::NewArgumentsObject,
                                        "NewArgumentsObject");
 
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_ARGUMENTS() {
   frame.syncStack(0);
 
-  MOZ_ASSERT(script->argumentsHasVarBinding());
+  MOZ_ASSERT_IF(handler.maybeScript(),
+                handler.maybeScript()->argumentsHasVarBinding());
 
   Label done;
-  if (!script->needsArgsObj()) {
+  if (!handler.maybeScript() || !handler.maybeScript()->needsArgsObj()) {
     // We assume the script does not need an arguments object. However, this
     // assumption can be invalidated later, see argumentsOptimizationFailed
     // in JSScript. Guard on the script's NeedsArgsObj flag.
     masm.moveValue(MagicValue(JS_OPTIMIZED_ARGUMENTS), R0);
 
     // If we don't need an arguments object, skip the VM call.
     Register scratch = R1.scratchReg();
-    masm.movePtr(ImmGCPtr(script), scratch);
+    loadScript(scratch);
     masm.branchTest32(
         Assembler::Zero, Address(scratch, JSScript::offsetOfMutableFlags()),
         Imm32(uint32_t(JSScript::MutableFlags::NeedsArgsObj)), &done);
   }
 
   prepareVMCall();
 
   masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
@@ -5142,17 +5313,17 @@ bool BaselineCompilerCodeGen::emit_JSOP_
     return false;
   }
 
   // After the generator returns, we restore the stack pointer, switch back to
   // the current realm, push the return value, and we're done.
   masm.bind(&returnTarget);
   masm.computeEffectiveAddress(frame.addressOfStackValue(-1),
                                masm.getStackPointer());
-  masm.switchToRealm(script->realm(), R2.scratchReg());
+  masm.switchToRealm(handler.script()->realm(), R2.scratchReg());
   frame.popn(2);
   frame.push(R0);
   return true;
 }
 
 template <>
 bool BaselineInterpreterCodeGen::emit_JSOP_RESUME() {
   MOZ_CRASH("NYI: interpreter JSOP_RESUME");
@@ -5181,16 +5352,17 @@ bool BaselineCodeGen<Handler>::emit_JSOP
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_IS_CONSTRUCTING() {
   frame.push(MagicValue(JS_IS_CONSTRUCTING));
   return true;
 }
 
 template <>
 bool BaselineCompilerCodeGen::emit_JSOP_JUMPTARGET() {
+  JSScript* script = handler.script();
   if (!script->hasScriptCounts()) {
     return true;
   }
   PCCounts* counts = script->maybeGetPCCounts(handler.pc());
   uint64_t* counterAddr = &counts->numExec();
   masm.inc64(AbsoluteAddress(counterAddr));
   return true;
 }
@@ -5351,42 +5523,48 @@ bool BaselineCodeGen<Handler>::emit_JSOP
   return true;
 }
 
 typedef JSObject* (*GetOrCreateModuleMetaObjectFn)(JSContext*, HandleObject);
 static const VMFunction GetOrCreateModuleMetaObjectInfo =
     FunctionInfo<GetOrCreateModuleMetaObjectFn>(js::GetOrCreateModuleMetaObject,
                                                 "GetOrCreateModuleMetaObject");
 
-template <typename Handler>
-bool BaselineCodeGen<Handler>::emit_JSOP_IMPORTMETA() {
-  RootedModuleObject module(cx, GetModuleObjectForScript(script));
+template <>
+bool BaselineCompilerCodeGen::emit_JSOP_IMPORTMETA() {
+  RootedModuleObject module(cx, GetModuleObjectForScript(handler.script()));
   MOZ_ASSERT(module);
 
   frame.syncStack(0);
 
   prepareVMCall();
   pushArg(ImmGCPtr(module));
   if (!callVM(GetOrCreateModuleMetaObjectInfo)) {
     return false;
   }
 
   masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, R0);
   frame.push(R0);
   return true;
 }
 
+template <>
+bool BaselineInterpreterCodeGen::emit_JSOP_IMPORTMETA() {
+  MOZ_CRASH("NYI: interpreter JSOP_IMPORTMETA");
+}
+
 typedef JSObject* (*StartDynamicModuleImportFn)(JSContext*, HandleObject,
                                                 HandleValue);
 static const VMFunction StartDynamicModuleImportInfo =
     FunctionInfo<StartDynamicModuleImportFn>(js::StartDynamicModuleImport,
                                              "StartDynamicModuleImport");
 
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_DYNAMIC_IMPORT() {
+  JSScript* script = handler.script();
   RootedObject referencingScriptSource(cx, script->sourceObject());
 
   // Put specifier value in R0.
   frame.popRegsAndSync(1);
 
   prepareVMCall();
   pushArg(R0);
   pushArg(ImmGCPtr(referencingScriptSource));
@@ -5394,24 +5572,30 @@ bool BaselineCodeGen<Handler>::emit_JSOP
     return false;
   }
 
   masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, R0);
   frame.push(R0);
   return true;
 }
 
+template <>
+bool BaselineInterpreterCodeGen::emit_JSOP_DYNAMIC_IMPORT() {
+  MOZ_CRASH("NYI: interpreter JSOP_DYNAMIC_IMPORT");
+}
+
 bool BaselineCompiler::emitPrologue() {
 #ifdef JS_USE_LINK_REGISTER
   // Push link register from generateEnterJIT()'s BLR.
   masm.pushReturnAddress();
   masm.checkStackAlignment();
 #endif
   emitProfilerEnterFrame();
 
+  JSScript* script = handler.script();
   if (script->trackRecordReplayProgress()) {
     masm.inc64(
         AbsoluteAddress(mozilla::recordreplay::ExecutionProgressCounter()));
   }
 
   masm.push(BaselineFrameReg);
   masm.moveStackPtrTo(BaselineFrameReg);
   masm.subFromStackPtr(Imm32(BaselineFrame::Size()));
@@ -5422,17 +5606,17 @@ bool BaselineCompiler::emitPrologue() {
   // Initialize BaselineFrame::flags.
   masm.store32(Imm32(0), frame.addressOfFlags());
 
   // Handle env chain pre-initialization (in case GC gets run
   // during stack check).  For global and eval scripts, the env
   // chain is in R1.  For function scripts, the env chain is in
   // the callee, nullptr is stored for now so that GC doesn't choke
   // on a bogus EnvironmentChain value in the frame.
-  if (function()) {
+  if (handler.function()) {
     masm.storePtr(ImmPtr(nullptr), frame.addressOfEnvironmentChain());
   } else {
     masm.storePtr(R1.scratchReg(), frame.addressOfEnvironmentChain());
   }
 
   // Functions with a large number of locals require two stack checks.
   // The VMCall for a fallible stack check can only occur after the
   // env chain has been initialized, as that is required for proper
@@ -5535,16 +5719,17 @@ bool BaselineCompiler::emitEpilogue() {
 
   emitProfilerExitFrame();
 
   masm.ret();
   return true;
 }
 
 MethodStatus BaselineCompiler::emitBody() {
+  JSScript* script = handler.script();
   MOZ_ASSERT(handler.pc() == script->code());
 
   bool lastOpUnreachable = false;
   uint32_t emittedOps = 0;
   mozilla::DebugOnly<jsbytecode*> prevpc = handler.pc();
 
   while (true) {
     JSOp op = JSOp(*handler.pc());
--- a/js/src/jit/BaselineCompiler.h
+++ b/js/src/jit/BaselineCompiler.h
@@ -256,17 +256,16 @@ namespace jit {
 // specific. This can be combined with template specialization of methods in
 // this class to specialize behavior.
 template <typename Handler>
 class BaselineCodeGen {
  protected:
   Handler handler;
 
   JSContext* cx;
-  JSScript* script;
   StackMacroAssembler masm;
   bool ionCompileable_;
 
   TempAllocator& alloc_;
   BytecodeAnalysis analysis_;
   typename Handler::FrameInfoT& frame;
 
   js::Vector<CodeOffset> traceLoggerToggleOffsets_;
@@ -284,24 +283,16 @@ class BaselineCodeGen {
 
   // Whether any on stack arguments are modified.
   bool modifiesArguments_;
 
   template <typename... HandlerArgs>
   BaselineCodeGen(JSContext* cx, TempAllocator& alloc, JSScript* script,
                   HandlerArgs&&... args);
 
-  JSFunction* function() const {
-    // Not delazifying here is ok as the function is guaranteed to have
-    // been delazified before compilation started.
-    return script->functionNonDelazifying();
-  }
-
-  ModuleObject* module() const { return script->module(); }
-
   template <typename T>
   void pushArg(const T& t) {
     masm.Push(t);
   }
 
   // Pushes the current script as argument for a VM function.
   void pushScriptArg();
 
@@ -316,16 +307,30 @@ class BaselineCodeGen {
   void pushScriptScopeArg();
 
   // Pushes a bytecode operand as argument for a VM function.
   void pushUint8BytecodeOperandArg();
   void pushUint16BytecodeOperandArg();
 
   void loadResumeIndexBytecodeOperand(Register dest);
 
+  // Loads the current JSScript* in dest.
+  void loadScript(Register dest);
+
+  // Jump to the script's resume entry indicated by resumeIndex.
+  void jumpToResumeEntry(Register resumeIndex, Register scratch1,
+                         Register scratch2);
+
+  // Load the global's lexical environment.
+  void loadGlobalLexicalEnvironment(Register dest);
+  void pushGlobalLexicalEnvironmentValue(ValueOperand scratch);
+
+  // Load the |this|-value from the global's lexical environment.
+  void loadGlobalThisValue(ValueOperand dest);
+
   void prepareVMCall();
 
   void storeFrameSizeAndPushDescriptor(uint32_t frameBaseSize, uint32_t argSize,
                                        const Address& frameSizeAddr);
   void computeFullFrameSize(uint32_t frameBaseSize, Register dest);
 
   enum CallVMPhase { POST_INITIALIZE, CHECK_OVER_RECURSED };
   bool callVM(const VMFunction& fun, CallVMPhase phase = POST_INITIALIZE);
@@ -344,16 +349,29 @@ class BaselineCodeGen {
   template <typename F1, typename F2>
   MOZ_MUST_USE bool emitDebugInstrumentation(
       const F1& ifDebuggee, const mozilla::Maybe<F2>& ifNotDebuggee);
   template <typename F>
   MOZ_MUST_USE bool emitDebugInstrumentation(const F& ifDebuggee) {
     return emitDebugInstrumentation(ifDebuggee, mozilla::Maybe<F>());
   }
 
+  // ifSet should be a function emitting code for when the script has |flag|
+  // set. ifNotSet emits code for when the flag isn't set.
+  template <typename F1, typename F2>
+  MOZ_MUST_USE bool emitTestScriptFlag(JSScript::ImmutableFlags flag,
+                                       const F1& ifSet, const F2& ifNotSet,
+                                       Register scratch);
+
+  // If |script->hasFlag(flag) == value|, execute the code emitted by |emit|.
+  template <typename F>
+  MOZ_MUST_USE bool emitTestScriptFlag(JSScript::ImmutableFlags flag,
+                                       bool value, const F& emit,
+                                       Register scratch);
+
   MOZ_MUST_USE bool emitCheckThis(ValueOperand val, bool reinit = false);
   void emitLoadReturnValue(ValueOperand val);
 
   MOZ_MUST_USE bool emitNextIC();
   MOZ_MUST_USE bool emitInterruptCheck();
   MOZ_MUST_USE bool emitWarmUpCounterIncrement();
   MOZ_MUST_USE bool emitTraceLoggerResume(Register script,
                                           AllocatableGeneralRegisterSet& regs);
@@ -449,16 +467,28 @@ class BaselineCompilerHandler {
   jsbytecode* pc() const { return pc_; }
   jsbytecode* maybePC() const { return pc_; }
 
   void moveToNextPC() { pc_ += GetBytecodeLength(pc_); }
   Label* labelOf(jsbytecode* pc) { return &labels_[script_->pcToOffset(pc)]; }
 
   bool isDefinitelyLastOp() const { return pc_ == script_->lastPC(); }
 
+  JSScript* script() const { return script_; }
+  JSScript* maybeScript() const { return script_; }
+
+  JSFunction* function() const {
+    // Not delazifying here is ok as the function is guaranteed to have
+    // been delazified before compilation started.
+    return script_->functionNonDelazifying();
+  }
+  JSFunction* maybeFunction() const { return function(); }
+
+  ModuleObject* module() const { return script_->module(); }
+
   void setCompileDebugInstrumentation() { compileDebugInstrumentation_ = true; }
   bool compileDebugInstrumentation() const {
     return compileDebugInstrumentation_;
   }
 
   RetAddrEntryVector& retAddrEntries() { return retAddrEntries_; }
 
   MOZ_MUST_USE bool appendRetAddrEntry(JSContext* cx, RetAddrEntry::Kind kind,
@@ -508,17 +538,17 @@ class BaselineCompiler final : private B
   // Baseline Debug OSR during epilogue will enter at this address. This is
   // right after where a debug epilogue VM call would have returned.
   CodeOffset debugOsrEpilogueOffset_;
 
   // If a script has more |nslots| than this, then emit code to do an
   // early stack check.
   static const unsigned EARLY_STACK_CHECK_SLOT_COUNT = 128;
   bool needsEarlyStackCheck() const {
-    return script->nslots() > EARLY_STACK_CHECK_SLOT_COUNT;
+    return handler.script()->nslots() > EARLY_STACK_CHECK_SLOT_COUNT;
   }
 
  public:
   BaselineCompiler(JSContext* cx, TempAllocator& alloc, JSScript* script);
   MOZ_MUST_USE bool init();
 
   MethodStatus compile();
 
@@ -578,19 +608,21 @@ class BaselineInterpreterHandler {
 
  public:
   using FrameInfoT = InterpreterFrameInfo;
 
   explicit BaselineInterpreterHandler(MacroAssembler& masm);
 
   InterpreterFrameInfo& frame() { return frame_; }
 
-  // Interpreter doesn't know the pc statically.
+  // Interpreter doesn't know the script and pc statically.
   jsbytecode* maybePC() const { return nullptr; }
   bool isDefinitelyLastOp() const { return false; }
+  JSScript* maybeScript() const { return nullptr; }
+  JSFunction* maybeFunction() const { return nullptr; }
 
   // Interpreter doesn't need to keep track of RetAddrEntries, so these methods
   // are no-ops.
   MOZ_MUST_USE bool appendRetAddrEntry(JSContext* cx, RetAddrEntry::Kind kind,
                                        uint32_t retOffset) {
     return true;
   }
   void markLastRetAddrEntryKind(RetAddrEntry::Kind) {}
--- a/js/src/vm/JSScript.h
+++ b/js/src/vm/JSScript.h
@@ -1609,17 +1609,19 @@ class JSScript : public js::gc::TenuredC
   // When running in ion, also increased for any inlined scripts. Reset if
   // the script's JIT code is forcibly discarded.
   mozilla::Atomic<uint32_t, mozilla::Relaxed,
                   mozilla::recordreplay::Behavior::DontPreserve>
       warmUpCount = {};
 
   // Immutable flags should not be modified after this script has been
   // initialized. These flags should likely be preserved when serializing
-  // (XDR) or copying (CopyScript) this script.
+  // (XDR) or copying (CopyScript) this script. This is only public for the
+  // JITs.
+ public:
   enum class ImmutableFlags : uint32_t {
     // No need for result value of last expression statement.
     NoScriptRval = 1 << 0,
 
     // Code is in strict mode.
     Strict = 1 << 1,
 
     // Code has "use strict"; explicitly.
@@ -1682,16 +1684,18 @@ class JSScript : public js::gc::TenuredC
 
     // Script came from eval().
     IsForEval = 1 << 22,
 
     // Whether the record/replay execution progress counter (see RecordReplay.h)
     // should be updated as this script runs.
     TrackRecordReplayProgress = 1 << 23,
   };
+
+ private:
   // Note: don't make this a bitfield! It makes it hard to read these flags
   // from JIT code.
   uint32_t immutableFlags_ = 0;
 
   // Mutable flags typically store information about runtime or deoptimization
   // behavior of this script. This is only public for the JITs.
  public:
   enum class MutableFlags : uint32_t {
@@ -1850,19 +1854,22 @@ class JSScript : public js::gc::TenuredC
     } else {
       clearFlag(flag);
     }
   }
   void clearFlag(MutableFlags flag) { mutableFlags_ &= ~uint32_t(flag); }
 
   // ImmutableFlags accessors.
 
+ public:
   MOZ_MUST_USE bool hasFlag(ImmutableFlags flag) const {
     return immutableFlags_ & uint32_t(flag);
   }
+
+ private:
   void setFlag(ImmutableFlags flag) { immutableFlags_ |= uint32_t(flag); }
   void setFlag(ImmutableFlags flag, bool b) {
     if (b) {
       setFlag(flag);
     } else {
       clearFlag(flag);
     }
   }
@@ -2197,16 +2204,19 @@ class JSScript : public js::gc::TenuredC
 
   bool hasInnerFunctions() const {
     return hasFlag(ImmutableFlags::HasInnerFunctions);
   }
 
   static constexpr size_t offsetOfMutableFlags() {
     return offsetof(JSScript, mutableFlags_);
   }
+  static size_t offsetOfImmutableFlags() {
+    return offsetof(JSScript, immutableFlags_);
+  }
 
   bool hasAnyIonScript() const { return hasIonScript(); }
 
   bool hasIonScript() const {
     bool res = ion && ion != ION_DISABLED_SCRIPT &&
                ion != ION_COMPILING_SCRIPT && ion != ION_PENDING_SCRIPT;
     MOZ_ASSERT_IF(res, baseline);
     return res;