Bug 1519792 part 2 - Move pc field from BaselineCodeGen to BaselineCompilerHandler. r=djvj
authorJan de Mooij <jdemooij@mozilla.com>
Thu, 17 Jan 2019 09:20:15 +0000
changeset 511352 cbe224190304b678b8ca0c9647cd5989e3a93609
parent 511351 1e52ffae5eb49e06edc3ec9701e3365ef7649c78
child 511353 6b7a9a7afa56c0cc0f2399bf378cb6d12b73b86a
push id10547
push userffxbld-merge
push dateMon, 21 Jan 2019 13:03:58 +0000
treeherdermozilla-beta@24ec1916bffe [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdjvj
bugs1519792
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 1519792 part 2 - Move pc field from BaselineCodeGen to BaselineCompilerHandler. r=djvj JSOPs like JSOP_INT8 that depend on the pc need to be specialized now for the interpreter. I added MOZ_CRASH versions of these that we can implement later. This split is a bit pessimistic: we should actually (partially) share codegen for some of these ops like JSOP_RESUME. However it's easier to revisit this later when we need to implement these ops for the interpreter. Differential Revision: https://phabricator.services.mozilla.com/D16442
js/src/jit/BaselineCompiler.cpp
js/src/jit/BaselineCompiler.h
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -44,29 +44,29 @@ using mozilla::Maybe;
 
 namespace js {
 namespace jit {
 
 BaselineCompilerHandler::BaselineCompilerHandler(TempAllocator& alloc,
                                                  JSScript* script)
     : alloc_(alloc),
       script_(script),
+      pc_(script->code()),
       compileDebugInstrumentation_(script->isDebuggee()) {}
 
 BaselineInterpreterHandler::BaselineInterpreterHandler() {}
 
 template <typename Handler>
 template <typename... HandlerArgs>
 BaselineCodeGen<Handler>::BaselineCodeGen(JSContext* cx, TempAllocator& alloc,
                                           JSScript* script,
                                           HandlerArgs&&... args)
     : handler(std::forward<HandlerArgs>(args)...),
       cx(cx),
       script(script),
-      pc(script->code()),
       ionCompileable_(jit::IsIonEnabled(cx) && CanIonCompileScript(cx, script)),
       alloc_(alloc),
       analysis_(alloc, script),
       frame(script, masm),
       traceLoggerToggleOffsets_(cx),
       icEntryIndex_(0),
       pushedBeforeCall_(0),
 #ifdef DEBUG
@@ -117,23 +117,23 @@ bool BaselineCompiler::init() {
   }
 
   return true;
 }
 
 bool BaselineCompiler::addPCMappingEntry(bool addIndexEntry) {
   // Don't add multiple entries for a single pc.
   size_t nentries = pcMappingEntries_.length();
-  if (nentries > 0 &&
-      pcMappingEntries_[nentries - 1].pcOffset == script->pcToOffset(pc)) {
+  uint32_t pcOffset = script->pcToOffset(handler.pc());
+  if (nentries > 0 && pcMappingEntries_[nentries - 1].pcOffset == pcOffset) {
     return true;
   }
 
   PCMappingEntry entry;
-  entry.pcOffset = script->pcToOffset(pc);
+  entry.pcOffset = pcOffset;
   entry.nativeOffset = masm.currentOffset();
   entry.slotInfo = getStackTopSlotInfo();
   entry.addIndexEntry = addIndexEntry;
 
   return pcMappingEntries_.append(entry);
 }
 
 MethodStatus BaselineCompiler::compile() {
@@ -266,19 +266,19 @@ MethodStatus BaselineCompiler::compile()
   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(), retAddrEntries_.length(),
-          pcMappingIndexEntries.length(), pcEntries.length(),
-          bytecodeTypeMapEntries, resumeEntries,
+          profilerExitFrameToggleOffset_.offset(),
+          handler.retAddrEntries().length(), pcMappingIndexEntries.length(),
+          pcEntries.length(), bytecodeTypeMapEntries, resumeEntries,
           traceLoggerToggleOffsets_.length()),
       JS::DeletePolicy<BaselineScript>(cx->runtime()));
   if (!baselineScript) {
     ReportOutOfMemory(cx);
     return Method_Error;
   }
 
   baselineScript->setMethod(code);
@@ -291,18 +291,19 @@ MethodStatus BaselineCompiler::compile()
 
   MOZ_ASSERT(pcMappingIndexEntries.length() > 0);
   baselineScript->copyPCMappingIndexEntries(&pcMappingIndexEntries[0]);
 
   MOZ_ASSERT(pcEntries.length() > 0);
   baselineScript->copyPCMappingEntries(pcEntries);
 
   // Copy RetAddrEntries.
-  if (retAddrEntries_.length() > 0) {
-    baselineScript->copyRetAddrEntries(script, &retAddrEntries_[0]);
+  if (handler.retAddrEntries().length() > 0) {
+    baselineScript->copyRetAddrEntries(script,
+                                       handler.retAddrEntries().begin());
   }
 
   // If profiler instrumentation is enabled, toggle instrumentation on.
   if (cx->runtime()->jitRuntime()->isProfilerInstrumentationEnabled(
           cx->runtime())) {
     baselineScript->toggleProfilerInstrumentation(true);
   }
 
@@ -444,49 +445,54 @@ bool BaselineCompiler::emitOutOfLinePost
   masm.passABIArg(objReg);
   masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, PostWriteBarrier));
 
   masm.popValue(R0);
   masm.ret();
   return true;
 }
 
-template <typename Handler>
-bool BaselineCodeGen<Handler>::emitNextIC() {
+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.
 
-  uint32_t pcOffset = script->pcToOffset(pc);
+  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_++;
   } while (entry->pcOffset() < pcOffset);
 
   MOZ_RELEASE_ASSERT(entry->pcOffset() == pcOffset);
-  MOZ_ASSERT_IF(entry->isForOp(), BytecodeOpHasIC(JSOp(*pc)));
+  MOZ_ASSERT_IF(entry->isForOp(), BytecodeOpHasIC(JSOp(*handler.pc())));
 
   CodeOffset callOffset;
   EmitCallIC(masm, entry, &callOffset);
 
   RetAddrEntry::Kind kind =
       entry->isForOp() ? RetAddrEntry::Kind::IC : RetAddrEntry::Kind::NonOpIC;
 
-  if (!retAddrEntries_.emplaceBack(script->pcToOffset(pc), kind, callOffset)) {
+  if (!handler.retAddrEntries().emplaceBack(pcOffset, kind, callOffset)) {
     ReportOutOfMemory(cx);
     return false;
   }
 
   return true;
 }
 
+template <>
+bool BaselineInterpreterCodeGen::emitNextIC() {
+  MOZ_CRASH("NYI: interpreter emitNextIC");
+}
+
 template <typename Handler>
 void BaselineCodeGen<Handler>::prepareVMCall() {
   pushedBeforeCall_ = masm.framePushed();
 #ifdef DEBUG
   inCall_ = true;
 #endif
 
   // Ensure everything is synced.
@@ -573,17 +579,17 @@ bool BaselineCodeGen<Handler>::callVM(co
     Label ok;
     masm.branchTest32(Assembler::Zero, frame.addressOfFlags(),
                       Imm32(BaselineFrame::HAS_OVERRIDE_PC), &ok);
     masm.assumeUnreachable("BaselineFrame shouldn't override pc after VM call");
     masm.bind(&ok);
   }
 #endif
 
-  return appendRetAddrEntry(RetAddrEntry::Kind::CallVM, callOffset);
+  return handler.appendRetAddrEntry(cx, RetAddrEntry::Kind::CallVM, callOffset);
 }
 
 typedef bool (*CheckOverRecursedBaselineFn)(JSContext*, BaselineFrame*);
 static const VMFunction CheckOverRecursedBaselineInfo =
     FunctionInfo<CheckOverRecursedBaselineFn>(CheckOverRecursedBaseline,
                                               "CheckOverRecursedBaseline");
 
 bool BaselineCompiler::emitStackCheck() {
@@ -617,17 +623,17 @@ bool BaselineCompiler::emitStackCheck() 
   if (needsEarlyStackCheck()) {
     phase = CHECK_OVER_RECURSED;
   }
 
   if (!callVMNonOp(CheckOverRecursedBaselineInfo, phase)) {
     return false;
   }
 
-  retAddrEntries_.back().setKind(RetAddrEntry::Kind::StackCheck);
+  handler.markLastRetAddrEntryKind(RetAddrEntry::Kind::StackCheck);
 
   masm.bind(&skipCall);
   return true;
 }
 
 void BaselineCompiler::emitIsDebuggeeCheck() {
   if (compileDebugInstrumentation()) {
     masm.Push(BaselineFrameReg);
@@ -646,88 +652,98 @@ void BaselineCompilerCodeGen::pushScript
 
 template <>
 void BaselineInterpreterCodeGen::pushScriptArg() {
   MOZ_CRASH("NYI: interpreter pushScriptArg");
 }
 
 template <>
 void BaselineCompilerCodeGen::pushBytecodePCArg() {
-  pushArg(ImmPtr(pc));
+  pushArg(ImmPtr(handler.pc()));
 }
 
 template <>
 void BaselineInterpreterCodeGen::pushBytecodePCArg() {
   // This will be something like pushArg(Address(...));
   MOZ_CRASH("NYI: interpreter pushBytecodePCArg");
 }
 
 template <>
 void BaselineCompilerCodeGen::pushScriptNameArg() {
-  pushArg(ImmGCPtr(script->getName(pc)));
+  pushArg(ImmGCPtr(script->getName(handler.pc())));
 }
 
 template <>
 void BaselineInterpreterCodeGen::pushScriptNameArg() {
   MOZ_CRASH("NYI: interpreter pushScriptNameArg");
 }
 
 template <>
 void BaselineCompilerCodeGen::pushScriptObjectArg(ScriptObjectType type) {
   switch (type) {
     case ScriptObjectType::RegExp:
-      pushArg(ImmGCPtr(script->getRegExp(pc)));
+      pushArg(ImmGCPtr(script->getRegExp(handler.pc())));
       return;
     case ScriptObjectType::Function:
-      pushArg(ImmGCPtr(script->getFunction(pc)));
+      pushArg(ImmGCPtr(script->getFunction(handler.pc())));
       return;
     case ScriptObjectType::ObjectLiteral:
-      pushArg(ImmGCPtr(script->getObject(pc)));
+      pushArg(ImmGCPtr(script->getObject(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(script->getScope(pc)));
+  pushArg(ImmGCPtr(script->getScope(handler.pc())));
 }
 
 template <>
 void BaselineInterpreterCodeGen::pushScriptScopeArg() {
   MOZ_CRASH("NYI: interpreter pushScriptScopeArg");
 }
 
 template <>
 void BaselineCompilerCodeGen::pushUint8BytecodeOperandArg() {
-  MOZ_ASSERT(JOF_OPTYPE(JSOp(*pc)) == JOF_UINT8);
-  pushArg(Imm32(GET_UINT8(pc)));
+  MOZ_ASSERT(JOF_OPTYPE(JSOp(*handler.pc())) == JOF_UINT8);
+  pushArg(Imm32(GET_UINT8(handler.pc())));
 }
 
 template <>
 void BaselineInterpreterCodeGen::pushUint8BytecodeOperandArg() {
   MOZ_CRASH("NYI: interpreter pushUint8BytecodeOperandArg");
 }
 
 template <>
 void BaselineCompilerCodeGen::pushUint16BytecodeOperandArg() {
-  MOZ_ASSERT(JOF_OPTYPE(JSOp(*pc)) == JOF_UINT16);
-  pushArg(Imm32(GET_UINT16(pc)));
+  MOZ_ASSERT(JOF_OPTYPE(JSOp(*handler.pc())) == JOF_UINT16);
+  pushArg(Imm32(GET_UINT16(handler.pc())));
 }
 
 template <>
 void BaselineInterpreterCodeGen::pushUint16BytecodeOperandArg() {
   MOZ_CRASH("NYI: interpreter pushUint16BytecodeOperandArg");
 }
 
+template <>
+void BaselineCompilerCodeGen::loadResumeIndexBytecodeOperand(Register dest) {
+  masm.move32(Imm32(GET_RESUMEINDEX(handler.pc())), dest);
+}
+
+template <>
+void BaselineInterpreterCodeGen::loadResumeIndexBytecodeOperand(Register dest) {
+  MOZ_CRASH("NYI: interpreter loadResumeIndexBytecodeOperand");
+}
+
 typedef bool (*DebugPrologueFn)(JSContext*, BaselineFrame*, jsbytecode*, bool*);
 static const VMFunction DebugPrologueInfo =
     FunctionInfo<DebugPrologueFn>(jit::DebugPrologue, "DebugPrologue");
 
 bool BaselineCompiler::emitDebugPrologue() {
   if (compileDebugInstrumentation()) {
     // Load pointer to BaselineFrame in R0.
     masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
@@ -735,17 +751,17 @@ bool BaselineCompiler::emitDebugPrologue
     prepareVMCall();
     pushBytecodePCArg();
     pushArg(R0.scratchReg());
     if (!callVM(DebugPrologueInfo)) {
       return false;
     }
 
     // Fix up the RetAddrEntry appended by callVM for on-stack recompilation.
-    retAddrEntries_.back().setKind(RetAddrEntry::Kind::DebugPrologue);
+    handler.markLastRetAddrEntryKind(RetAddrEntry::Kind::DebugPrologue);
 
     // If the stub returns |true|, we have to return the value stored in the
     // frame's return value slot.
     Label done;
     masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, &done);
     {
       masm.loadValue(frame.addressOfReturnValue(), JSReturnOperand);
       masm.jump(&return_);
@@ -844,18 +860,18 @@ bool BaselineCodeGen<Handler>::emitInter
 }
 
 typedef bool (*IonCompileScriptForBaselineFn)(JSContext*, BaselineFrame*,
                                               jsbytecode*);
 static const VMFunction IonCompileScriptForBaselineInfo =
     FunctionInfo<IonCompileScriptForBaselineFn>(IonCompileScriptForBaseline,
                                                 "IonCompileScriptForBaseline");
 
-template <typename Handler>
-bool BaselineCodeGen<Handler>::emitWarmUpCounterIncrement(bool allowOsr) {
+template <>
+bool BaselineCompilerCodeGen::emitWarmUpCounterIncrement() {
   // Emit no warm-up counter increments or bailouts if Ion is not
   // enabled, or if the script will never be Ion-compileable
 
   if (!ionCompileable_) {
     return true;
   }
 
   frame.assertSyncedStack();
@@ -864,27 +880,28 @@ bool BaselineCodeGen<Handler>::emitWarmU
   Register countReg = R0.scratchReg();
   Address warmUpCounterAddr(scriptReg, JSScript::offsetOfWarmUpCounter());
 
   masm.movePtr(ImmGCPtr(script), scriptReg);
   masm.load32(warmUpCounterAddr, countReg);
   masm.add32(Imm32(1), countReg);
   masm.store32(countReg, warmUpCounterAddr);
 
-  // If this is a loop inside a catch or finally block, increment the warmup
-  // counter but don't attempt OSR (Ion only compiles the try block).
-  if (analysis_.info(pc).loopEntryInCatchOrFinally) {
-    MOZ_ASSERT(JSOp(*pc) == JSOP_LOOPENTRY);
-    return true;
-  }
-
-  // OSR not possible at this loop entry.
-  if (!allowOsr) {
-    MOZ_ASSERT(JSOp(*pc) == JSOP_LOOPENTRY);
-    return true;
+  jsbytecode* pc = handler.pc();
+  if (JSOp(*pc) == JSOP_LOOPENTRY) {
+    // If this is a loop inside a catch or finally block, increment the warmup
+    // counter but don't attempt OSR (Ion only compiles the try block).
+    if (analysis_.info(pc).loopEntryInCatchOrFinally) {
+      return true;
+    }
+
+    if (!LoopEntryCanIonOsr(pc)) {
+      // OSR into Ion not possible at this loop entry.
+      return true;
+    }
   }
 
   Label skipCall;
 
   const OptimizationInfo* info =
       IonOptimizations.get(IonOptimizations.firstLevel());
   uint32_t warmUpThreshold = info->compilerWarmUpThreshold(script, pc);
   masm.branch32(Assembler::LessThan, countReg, Imm32(warmUpThreshold),
@@ -902,31 +919,36 @@ bool BaselineCodeGen<Handler>::emitWarmU
       return false;
     }
   } else {
     // To call stubs we need to have an opcode. This code handles the
     // prologue and there is no dedicatd opcode present. Therefore use an
     // annotated vm call.
     prepareVMCall();
 
-    masm.Push(ImmPtr(pc));
+    pushBytecodePCArg();
     masm.PushBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
 
     if (!callVM(IonCompileScriptForBaselineInfo)) {
       return false;
     }
 
     // Annotate the RetAddrEntry as warmup counter.
-    retAddrEntries_.back().setKind(RetAddrEntry::Kind::WarmupCounter);
+    handler.markLastRetAddrEntryKind(RetAddrEntry::Kind::WarmupCounter);
   }
   masm.bind(&skipCall);
 
   return true;
 }
 
+template <>
+bool BaselineInterpreterCodeGen::emitWarmUpCounterIncrement() {
+  MOZ_CRASH("NYI: interpreter emitWarmUpCounterIncrement");
+}
+
 bool BaselineCompiler::emitArgumentTypeChecks() {
   if (!function()) {
     return true;
   }
 
   frame.pushThis();
   frame.popRegsAndSync(1);
 
@@ -945,42 +967,44 @@ bool BaselineCompiler::emitArgumentTypeC
 
   return true;
 }
 
 bool BaselineCompiler::emitDebugTrap() {
   MOZ_ASSERT(compileDebugInstrumentation());
   MOZ_ASSERT(frame.numUnsyncedSlots() == 0);
 
-  bool enabled = script->stepModeEnabled() || script->hasBreakpointsAt(pc);
+  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.
   pcMappingEntries_.back().nativeOffset = masm.currentOffset();
 #endif
 
   // Emit patchable call to debug trap handler.
-  JitCode* handler = cx->runtime()->jitRuntime()->debugTrapHandler(cx);
-  if (!handler) {
+  JitCode* handlerCode = cx->runtime()->jitRuntime()->debugTrapHandler(cx);
+  if (!handlerCode) {
     return false;
   }
-  mozilla::DebugOnly<CodeOffset> offset = masm.toggledCall(handler, enabled);
+  mozilla::DebugOnly<CodeOffset> offset =
+      masm.toggledCall(handlerCode, enabled);
 
 #ifdef DEBUG
   // Patchable call offset has to match the pc mapping offset.
   PCMappingEntry& entry = pcMappingEntries_.back();
   MOZ_ASSERT((&offset)->offset() == entry.nativeOffset);
 #endif
 
   // Add a RetAddrEntry for the return offset -> pc mapping.
-  return appendRetAddrEntry(RetAddrEntry::Kind::DebugTrap,
-                            masm.currentOffset());
+  return handler.appendRetAddrEntry(cx, RetAddrEntry::Kind::DebugTrap,
+                                    masm.currentOffset());
 }
 
 #ifdef JS_TRACE_LOGGING
 bool BaselineCompiler::emitTraceLoggerEnter() {
   AllocatableRegisterSet regs(RegisterSet::Volatile());
   Register loggerReg = regs.takeAnyGeneral();
   Register scriptReg = regs.takeAnyGeneral();
 
@@ -1116,36 +1140,46 @@ bool BaselineCodeGen<Handler>::emit_JSOP
 }
 
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_POP() {
   frame.pop();
   return true;
 }
 
-template <typename Handler>
-bool BaselineCodeGen<Handler>::emit_JSOP_POPN() {
-  frame.popn(GET_UINT16(pc));
+template <>
+bool BaselineCompilerCodeGen::emit_JSOP_POPN() {
+  frame.popn(GET_UINT16(handler.pc()));
   return true;
 }
 
-template <typename Handler>
-bool BaselineCodeGen<Handler>::emit_JSOP_DUPAT() {
+template <>
+bool BaselineInterpreterCodeGen::emit_JSOP_POPN() {
+  MOZ_CRASH("NYI: interpreter JSOP_POPN");
+}
+
+template <>
+bool BaselineCompilerCodeGen::emit_JSOP_DUPAT() {
   frame.syncStack(0);
 
   // DUPAT takes a value on the stack and re-pushes it on top.  It's like
   // GETLOCAL but it addresses from the top of the stack instead of from the
   // stack frame.
 
-  int depth = -(GET_UINT24(pc) + 1);
+  int depth = -(GET_UINT24(handler.pc()) + 1);
   masm.loadValue(frame.addressOfStackValue(frame.peek(depth)), R0);
   frame.push(R0);
   return true;
 }
 
+template <>
+bool BaselineInterpreterCodeGen::emit_JSOP_DUPAT() {
+  MOZ_CRASH("NYI: interpreter JSOP_DUPAT");
+}
+
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_DUP() {
   // Keep top stack value in R0, sync the rest so that we can use R1. We use
   // separate registers because every register can be used by at most one
   // StackValue.
   frame.popRegsAndSync(1);
   masm.moveValue(R0, R1);
 
@@ -1172,27 +1206,27 @@ bool BaselineCodeGen<Handler>::emit_JSOP
   // Keep top stack values in R0 and R1.
   frame.popRegsAndSync(2);
 
   frame.push(R1);
   frame.push(R0);
   return true;
 }
 
-template <typename Handler>
-bool BaselineCodeGen<Handler>::emit_JSOP_PICK() {
+template <>
+bool BaselineCompilerCodeGen::emit_JSOP_PICK() {
   frame.syncStack(0);
 
   // Pick takes a value on the stack and moves it to the top.
   // For instance, pick 2:
   //     before: A B C D E
   //     after : A B D E C
 
   // First, move value at -(amount + 1) into R0.
-  int32_t depth = -(GET_INT8(pc) + 1);
+  int32_t depth = -(GET_INT8(handler.pc()) + 1);
   masm.loadValue(frame.addressOfStackValue(frame.peek(depth)), R0);
 
   // Move the other values down.
   depth++;
   for (; depth < 0; depth++) {
     Address source = frame.addressOfStackValue(frame.peek(depth));
     Address dest = frame.addressOfStackValue(frame.peek(depth - 1));
     masm.loadValue(source, R1);
@@ -1200,61 +1234,73 @@ bool BaselineCodeGen<Handler>::emit_JSOP
   }
 
   // Push R0.
   frame.pop();
   frame.push(R0);
   return true;
 }
 
-template <typename Handler>
-bool BaselineCodeGen<Handler>::emit_JSOP_UNPICK() {
+template <>
+bool BaselineInterpreterCodeGen::emit_JSOP_PICK() {
+  MOZ_CRASH("NYI: interpreter JSOP_PICK");
+}
+
+template <>
+bool BaselineCompilerCodeGen::emit_JSOP_UNPICK() {
   frame.syncStack(0);
 
   // Pick takes the top of the stack value and moves it under the nth value.
   // For instance, unpick 2:
   //     before: A B C D E
   //     after : A B E C D
 
   // First, move value at -1 into R0.
   masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R0);
 
   // Move the other values up.
-  int32_t depth = -(GET_INT8(pc) + 1);
+  int32_t depth = -(GET_INT8(handler.pc()) + 1);
   for (int32_t i = -1; i > depth; i--) {
     Address source = frame.addressOfStackValue(frame.peek(i - 1));
     Address dest = frame.addressOfStackValue(frame.peek(i));
     masm.loadValue(source, R1);
     masm.storeValue(R1, dest);
   }
 
   // Store R0 under the nth value.
   Address dest = frame.addressOfStackValue(frame.peek(depth));
   masm.storeValue(R0, dest);
   return true;
 }
 
 template <>
+bool BaselineInterpreterCodeGen::emit_JSOP_UNPICK() {
+  MOZ_CRASH("NYI: interpreter JSOP_UNPICK");
+}
+
+template <>
 void BaselineCompilerCodeGen::emitJump() {
+  jsbytecode* pc = handler.pc();
   MOZ_ASSERT(IsJumpOpcode(JSOp(*pc)));
   frame.assertSyncedStack();
 
   jsbytecode* target = pc + GET_JUMP_OFFSET(pc);
   masm.jump(handler.labelOf(target));
 }
 
 template <>
 void BaselineInterpreterCodeGen::emitJump() {
   // We have to add the current pc's jump offset to the frame's pc.
   MOZ_CRASH("NYI: interpreter emitJump");
 }
 
 template <>
 void BaselineCompilerCodeGen::emitTestBooleanTruthy(bool branchIfTrue,
                                                     ValueOperand val) {
+  jsbytecode* pc = handler.pc();
   MOZ_ASSERT(IsJumpOpcode(JSOp(*pc)));
   frame.assertSyncedStack();
 
   jsbytecode* target = pc + GET_JUMP_OFFSET(pc);
   masm.branchTestBooleanTruthy(branchIfTrue, val, handler.labelOf(target));
 }
 
 template <>
@@ -1384,17 +1430,17 @@ bool BaselineCodeGen<Handler>::emit_JSOP
 }
 
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_LOOPENTRY() {
   if (!emit_JSOP_JUMPTARGET()) {
     return false;
   }
   frame.syncStack(0);
-  if (!emitWarmUpCounterIncrement(LoopEntryCanIonOsr(pc))) {
+  if (!emitWarmUpCounterIncrement()) {
     return false;
   }
   if (script->trackRecordReplayProgress()) {
     masm.inc64(
         AbsoluteAddress(mozilla::recordreplay::ExecutionProgressCounter()));
   }
   return true;
 }
@@ -1647,81 +1693,115 @@ bool BaselineCodeGen<Handler>::emit_JSOP
 }
 
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_ONE() {
   frame.push(Int32Value(1));
   return true;
 }
 
-template <typename Handler>
-bool BaselineCodeGen<Handler>::emit_JSOP_INT8() {
-  frame.push(Int32Value(GET_INT8(pc)));
+template <>
+bool BaselineCompilerCodeGen::emit_JSOP_INT8() {
+  frame.push(Int32Value(GET_INT8(handler.pc())));
   return true;
 }
 
-template <typename Handler>
-bool BaselineCodeGen<Handler>::emit_JSOP_INT32() {
-  frame.push(Int32Value(GET_INT32(pc)));
+template <>
+bool BaselineInterpreterCodeGen::emit_JSOP_INT8() {
+  MOZ_CRASH("NYI: interpreter JSOP_INT8");
+}
+
+template <>
+bool BaselineCompilerCodeGen::emit_JSOP_INT32() {
+  frame.push(Int32Value(GET_INT32(handler.pc())));
   return true;
 }
 
-template <typename Handler>
-bool BaselineCodeGen<Handler>::emit_JSOP_UINT16() {
-  frame.push(Int32Value(GET_UINT16(pc)));
+template <>
+bool BaselineInterpreterCodeGen::emit_JSOP_INT32() {
+  MOZ_CRASH("NYI: interpreter JSOP_INT32");
+}
+
+template <>
+bool BaselineCompilerCodeGen::emit_JSOP_UINT16() {
+  frame.push(Int32Value(GET_UINT16(handler.pc())));
   return true;
 }
 
-template <typename Handler>
-bool BaselineCodeGen<Handler>::emit_JSOP_UINT24() {
-  frame.push(Int32Value(GET_UINT24(pc)));
+template <>
+bool BaselineInterpreterCodeGen::emit_JSOP_UINT16() {
+  MOZ_CRASH("NYI: interpreter JSOP_UINT16");
+}
+
+template <>
+bool BaselineCompilerCodeGen::emit_JSOP_UINT24() {
+  frame.push(Int32Value(GET_UINT24(handler.pc())));
   return true;
 }
 
+template <>
+bool BaselineInterpreterCodeGen::emit_JSOP_UINT24() {
+  MOZ_CRASH("NYI: interpreter JSOP_UINT24");
+}
+
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_RESUMEINDEX() {
   return emit_JSOP_UINT24();
 }
 
-template <typename Handler>
-bool BaselineCodeGen<Handler>::emit_JSOP_DOUBLE() {
-  frame.push(script->getConst(GET_UINT32_INDEX(pc)));
+template <>
+bool BaselineCompilerCodeGen::emit_JSOP_DOUBLE() {
+  frame.push(script->getConst(GET_UINT32_INDEX(handler.pc())));
   return true;
 }
 
+template <>
+bool BaselineInterpreterCodeGen::emit_JSOP_DOUBLE() {
+  MOZ_CRASH("NYI: interpreter JSOP_DOUBLE");
+}
+
 #ifdef ENABLE_BIGINT
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_BIGINT() {
-  frame.push(script->getConst(GET_UINT32_INDEX(pc)));
-  return true;
+  return emit_JSOP_DOUBLE();
 }
 #endif
 
-template <typename Handler>
-bool BaselineCodeGen<Handler>::emit_JSOP_STRING() {
-  frame.push(StringValue(script->getAtom(pc)));
+template <>
+bool BaselineCompilerCodeGen::emit_JSOP_STRING() {
+  frame.push(StringValue(script->getAtom(handler.pc())));
   return true;
 }
 
-template <typename Handler>
-bool BaselineCodeGen<Handler>::emit_JSOP_SYMBOL() {
-  unsigned which = GET_UINT8(pc);
+template <>
+bool BaselineInterpreterCodeGen::emit_JSOP_STRING() {
+  MOZ_CRASH("NYI: interpreter JSOP_STRING");
+}
+
+template <>
+bool BaselineCompilerCodeGen::emit_JSOP_SYMBOL() {
+  unsigned which = GET_UINT8(handler.pc());
   JS::Symbol* sym = cx->runtime()->wellKnownSymbols->get(which);
   frame.push(SymbolValue(sym));
   return true;
 }
 
+template <>
+bool BaselineInterpreterCodeGen::emit_JSOP_SYMBOL() {
+  MOZ_CRASH("NYI: interpreter JSOP_SYMBOL");
+}
+
 typedef JSObject* (*DeepCloneObjectLiteralFn)(JSContext*, HandleObject,
                                               NewObjectKind);
 static const VMFunction DeepCloneObjectLiteralInfo =
     FunctionInfo<DeepCloneObjectLiteralFn>(DeepCloneObjectLiteral,
                                            "DeepCloneObjectLiteral");
 
-template <typename Handler>
-bool BaselineCodeGen<Handler>::emit_JSOP_OBJECT() {
+template <>
+bool BaselineCompilerCodeGen::emit_JSOP_OBJECT() {
   if (cx->realm()->creationOptions().cloneSingletons()) {
     prepareVMCall();
 
     pushArg(ImmWord(TenuredObject));
     pushScriptObjectArg(ScriptObjectType::ObjectLiteral);
 
     if (!callVM(DeepCloneObjectLiteralInfo)) {
       return false;
@@ -1729,36 +1809,47 @@ bool BaselineCodeGen<Handler>::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(pc)));
+  frame.push(ObjectValue(*script->getObject(handler.pc())));
   return true;
 }
 
-template <typename Handler>
-bool BaselineCodeGen<Handler>::emit_JSOP_CALLSITEOBJ() {
+template <>
+bool BaselineInterpreterCodeGen::emit_JSOP_OBJECT() {
+  MOZ_CRASH("NYI: interpreter JSOP_OBJECT");
+}
+
+template <>
+bool BaselineCompilerCodeGen::emit_JSOP_CALLSITEOBJ() {
+  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)) {
     return false;
   }
 
   frame.push(ObjectValue(*cso));
   return true;
 }
 
+template <>
+bool BaselineInterpreterCodeGen::emit_JSOP_CALLSITEOBJ() {
+  MOZ_CRASH("NYI: interpreter JSOP_CALLSITEOBJ");
+}
+
 typedef JSObject* (*CloneRegExpObjectFn)(JSContext*, Handle<RegExpObject*>);
 static const VMFunction CloneRegExpObjectInfo =
     FunctionInfo<CloneRegExpObjectFn>(CloneRegExpObject, "CloneRegExpObject");
 
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_REGEXP() {
   prepareVMCall();
   pushScriptObjectArg(ScriptObjectType::RegExp);
@@ -2063,45 +2154,51 @@ bool BaselineCodeGen<Handler>::emit_JSOP
   return emit_JSOP_GOTO();
 }
 
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_LINENO() {
   return true;
 }
 
-template <typename Handler>
-bool BaselineCodeGen<Handler>::emit_JSOP_NEWARRAY() {
+template <>
+bool BaselineCompilerCodeGen::emit_JSOP_NEWARRAY() {
   frame.syncStack(0);
 
-  uint32_t length = GET_UINT32(pc);
+  uint32_t length = GET_UINT32(handler.pc());
   MOZ_ASSERT(length <= INT32_MAX,
              "the bytecode emitter must fail to compile code that would "
              "produce JSOP_NEWARRAY with a length exceeding int32_t range");
 
   // Pass length in R0.
   masm.move32(Imm32(AssertedCast<int32_t>(length)), R0.scratchReg());
 
   if (!emitNextIC()) {
     return false;
   }
 
   frame.push(R0);
   return true;
 }
 
+template <>
+bool BaselineInterpreterCodeGen::emit_JSOP_NEWARRAY() {
+  MOZ_CRASH("NYI: interpreter JSOP_NEWARRAY");
+}
+
 typedef ArrayObject* (*NewArrayCopyOnWriteFn)(JSContext*, HandleArrayObject,
                                               gc::InitialHeap);
 const VMFunction NewArrayCopyOnWriteInfo = FunctionInfo<NewArrayCopyOnWriteFn>(
     js::NewDenseCopyOnWriteArray, "NewDenseCopyOnWriteArray");
 
-template <typename Handler>
-bool BaselineCodeGen<Handler>::emit_JSOP_NEWARRAY_COPYONWRITE() {
+template <>
+bool BaselineCompilerCodeGen::emit_JSOP_NEWARRAY_COPYONWRITE() {
   RootedScript scriptRoot(cx, script);
-  JSObject* obj = ObjectGroup::getOrFixupCopyOnWriteObject(cx, scriptRoot, pc);
+  JSObject* obj =
+      ObjectGroup::getOrFixupCopyOnWriteObject(cx, scriptRoot, handler.pc());
   if (!obj) {
     return false;
   }
 
   prepareVMCall();
 
   pushArg(Imm32(gc::DefaultHeap));
   pushArg(ImmGCPtr(obj));
@@ -2111,40 +2208,50 @@ bool BaselineCodeGen<Handler>::emit_JSOP
   }
 
   // Box and push return value.
   masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, R0);
   frame.push(R0);
   return true;
 }
 
-template <typename Handler>
-bool BaselineCodeGen<Handler>::emit_JSOP_INITELEM_ARRAY() {
+template <>
+bool BaselineInterpreterCodeGen::emit_JSOP_NEWARRAY_COPYONWRITE() {
+  MOZ_CRASH("NYI: interpreter JSOP_NEWARRAY_COPYONWRITE");
+}
+
+template <>
+bool BaselineCompilerCodeGen::emit_JSOP_INITELEM_ARRAY() {
   // Keep the object and rhs on the stack.
   frame.syncStack(0);
 
   // Load object in R0, index in R1.
   masm.loadValue(frame.addressOfStackValue(frame.peek(-2)), R0);
-  uint32_t index = GET_UINT32(pc);
+  uint32_t index = GET_UINT32(handler.pc());
   MOZ_ASSERT(index <= INT32_MAX,
              "the bytecode emitter must fail to compile code that would "
              "produce JSOP_INITELEM_ARRAY with a length exceeding "
              "int32_t range");
   masm.moveValue(Int32Value(AssertedCast<int32_t>(index)), R1);
 
   // Call IC.
   if (!emitNextIC()) {
     return false;
   }
 
   // Pop the rhs, so that the object is on the top of the stack.
   frame.pop();
   return true;
 }
 
+template <>
+bool BaselineInterpreterCodeGen::emit_JSOP_INITELEM_ARRAY() {
+  MOZ_CRASH("NYI: interpreter JSOP_INITELEM_ARRAY");
+}
+
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_NEWOBJECT() {
   frame.syncStack(0);
 
   if (!emitNextIC()) {
     return false;
   }
 
@@ -2409,82 +2516,114 @@ bool BaselineCodeGen<Handler>::emit_JSOP
   if (!emitNextIC()) {
     return false;
   }
 
   frame.push(R0);
   return true;
 }
 
-template <typename Handler>
-bool BaselineCodeGen<Handler>::emit_JSOP_GETGNAME() {
-  if (script->hasNonSyntacticScope()) {
-    return emit_JSOP_GETNAME();
-  }
-
-  RootedPropertyName name(cx, script->getName(pc));
+template <>
+bool BaselineCompilerCodeGen::tryOptimizeGetGlobalName() {
+  PropertyName* name = 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);
     return true;
   }
   if (name == cx->names().Infinity) {
     frame.push(cx->runtime()->positiveInfinityValue);
     return true;
   }
 
+  return false;
+}
+
+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()) {
+    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;
 }
 
+template <>
+bool BaselineCompilerCodeGen::tryOptimizeBindGlobalName() {
+  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()));
+  Rooted<LexicalEnvironmentObject*> env(cx,
+                                        &script->global().lexicalEnvironment());
+  if (Shape* shape = env->lookup(cx, name)) {
+    if (shape->writable() &&
+        !env->getSlot(shape->slot()).isMagic(JS_UNINITIALIZED_LEXICAL)) {
+      frame.push(ObjectValue(*env));
+      return true;
+    }
+    return false;
+  }
+
+  if (Shape* shape = script->global().lookup(cx, name)) {
+    // If the property does not currently exist on the global lexical
+    // scope, we can bind name to the global object if the property
+    // exists on the global and is non-configurable, as then it cannot
+    // be shadowed.
+    if (!shape->configurable()) {
+      frame.push(ObjectValue(script->global()));
+      return true;
+    }
+  }
+
+  return false;
+}
+
+template <>
+bool BaselineInterpreterCodeGen::tryOptimizeBindGlobalName() {
+  // Interpreter doesn't optimize simple BINDGNAMEs.
+  return false;
+}
+
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_BINDGNAME() {
-  if (!script->hasNonSyntacticScope()) {
-    // 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(pc));
-    Rooted<LexicalEnvironmentObject*> env(
-        cx, &script->global().lexicalEnvironment());
-    if (Shape* shape = env->lookup(cx, name)) {
-      if (shape->writable() &&
-          !env->getSlot(shape->slot()).isMagic(JS_UNINITIALIZED_LEXICAL)) {
-        frame.push(ObjectValue(*env));
-        return true;
-      }
-    } else if (Shape* shape = script->global().lookup(cx, name)) {
-      // If the property does not currently exist on the global lexical
-      // scope, we can bind name to the global object if the property
-      // exists on the global and is non-configurable, as then it cannot
-      // be shadowed.
-      if (!shape->configurable()) {
-        frame.push(ObjectValue(script->global()));
-        return true;
-      }
-    }
-
-    // Otherwise we have to use the environment chain.
-  }
-
+  if (tryOptimizeBindGlobalName()) {
+    return true;
+  }
   return emitBindName(JSOP_BINDGNAME);
 }
 
 typedef JSObject* (*BindVarFn)(JSContext*, JSObject*);
 static const VMFunction BindVarInfo =
     FunctionInfo<BindVarFn>(BindVarOperation, "BindVarOperation");
 
 template <typename Handler>
@@ -2669,41 +2808,52 @@ bool BaselineCodeGen<Handler>::emit_JSOP
   return emitDelProp(/* strict = */ false);
 }
 
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_STRICTDELPROP() {
   return emitDelProp(/* strict = */ true);
 }
 
-template <typename Handler>
-void BaselineCodeGen<Handler>::getEnvironmentCoordinateObject(Register reg) {
-  EnvironmentCoordinate ec(pc);
+template <>
+void BaselineCompilerCodeGen::getEnvironmentCoordinateObject(Register reg) {
+  EnvironmentCoordinate ec(handler.pc());
 
   masm.loadPtr(frame.addressOfEnvironmentChain(), reg);
   for (unsigned i = ec.hops(); i; i--) {
     masm.unboxObject(
         Address(reg, EnvironmentObject::offsetOfEnclosingEnvironment()), reg);
   }
 }
 
-template <typename Handler>
-Address BaselineCodeGen<Handler>::getEnvironmentCoordinateAddressFromObject(
+template <>
+void BaselineInterpreterCodeGen::getEnvironmentCoordinateObject(Register reg) {
+  MOZ_CRASH("NYI: interpreter getEnvironmentCoordinateObject");
+}
+
+template <>
+Address BaselineCompilerCodeGen::getEnvironmentCoordinateAddressFromObject(
     Register objReg, Register reg) {
-  EnvironmentCoordinate ec(pc);
-  Shape* shape = EnvironmentCoordinateToEnvironmentShape(script, pc);
+  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));
   }
 
   return Address(objReg, NativeObject::getFixedSlotOffset(ec.slot()));
 }
 
+template <>
+Address BaselineInterpreterCodeGen::getEnvironmentCoordinateAddressFromObject(
+    Register objReg, Register reg) {
+  MOZ_CRASH("NYI: interpreter getEnvironmentCoordinateAddressFromObject");
+}
+
 template <typename Handler>
 Address BaselineCodeGen<Handler>::getEnvironmentCoordinateAddress(
     Register reg) {
   getEnvironmentCoordinateObject(reg);
   return getEnvironmentCoordinateAddressFromObject(reg, reg);
 }
 
 template <typename Handler>
@@ -2719,18 +2869,19 @@ bool BaselineCodeGen<Handler>::emit_JSOP
       return false;
     }
   }
 
   frame.push(R0);
   return true;
 }
 
-template <typename Handler>
-bool BaselineCodeGen<Handler>::emit_JSOP_SETALIASEDVAR() {
+template <>
+bool BaselineCompilerCodeGen::emit_JSOP_SETALIASEDVAR() {
+  jsbytecode* pc = handler.pc();
   JSScript* outerScript = EnvironmentCoordinateFunctionScript(script, pc);
   if (outerScript && outerScript->treatAsRunOnce()) {
     // Type updates for this operation might need to be tracked, so treat
     // this as a SETPROP.
 
     // Load rhs into R1.
     frame.syncStack(0);
     masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R1);
@@ -2767,16 +2918,21 @@ bool BaselineCodeGen<Handler>::emit_JSOP
   masm.branchValueIsNurseryCell(Assembler::NotEqual, R0, temp, &skipBarrier);
 
   masm.call(&postBarrierSlot_);  // Won't clobber R0
 
   masm.bind(&skipBarrier);
   return true;
 }
 
+template <>
+bool BaselineInterpreterCodeGen::emit_JSOP_SETALIASEDVAR() {
+  MOZ_CRASH("NYI: interpreter JSOP_SETALIASEDVAR");
+}
+
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_GETNAME() {
   frame.syncStack(0);
 
   masm.loadPtr(frame.addressOfEnvironmentChain(), R0.scratchReg());
 
   // Call IC.
   if (!emitNextIC()) {
@@ -2834,25 +2990,25 @@ bool BaselineCodeGen<Handler>::emit_JSOP
   if (!callVM(DeleteNameInfo)) {
     return false;
   }
 
   frame.push(R0);
   return true;
 }
 
-template <typename Handler>
-bool BaselineCodeGen<Handler>::emit_JSOP_GETIMPORT() {
+template <>
+bool BaselineCompilerCodeGen::emit_JSOP_GETIMPORT() {
   ModuleEnvironmentObject* env = GetModuleEnvironmentForScript(script);
   MOZ_ASSERT(env);
 
+  jsid id = NameToId(script->getName(handler.pc()));
   ModuleEnvironmentObject* targetEnv;
   Shape* shape;
-  MOZ_ALWAYS_TRUE(
-      env->lookupImport(NameToId(script->getName(pc)), &targetEnv, &shape));
+  MOZ_ALWAYS_TRUE(env->lookupImport(id, &targetEnv, &shape));
 
   EnsureTrackPropertyTypes(cx, targetEnv, shape->propid());
 
   frame.syncStack(0);
 
   uint32_t slot = shape->slot();
   Register scratch = R0.scratchReg();
   masm.movePtr(ImmGCPtr(targetEnv), scratch);
@@ -2880,16 +3036,21 @@ bool BaselineCodeGen<Handler>::emit_JSOP
       return false;
     }
   }
 
   frame.push(R0);
   return true;
 }
 
+template <>
+bool BaselineInterpreterCodeGen::emit_JSOP_GETIMPORT() {
+  MOZ_CRASH("NYI: interpreter JSOP_GETIMPORT");
+}
+
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_GETINTRINSIC() {
   frame.syncStack(0);
 
   if (!emitNextIC()) {
     return false;
   }
 
@@ -3091,39 +3252,53 @@ bool BaselineCodeGen<Handler>::emit_JSOP
   masm.branchTestInt32(Assembler::Equal, indexAddr, &isInt32);
   masm.assumeUnreachable("INITELEM_INC index must be Int32");
   masm.bind(&isInt32);
 #endif
   masm.incrementInt32Value(indexAddr);
   return true;
 }
 
-template <typename Handler>
-bool BaselineCodeGen<Handler>::emit_JSOP_GETLOCAL() {
-  frame.pushLocal(GET_LOCALNO(pc));
+template <>
+bool BaselineCompilerCodeGen::emit_JSOP_GETLOCAL() {
+  frame.pushLocal(GET_LOCALNO(handler.pc()));
   return true;
 }
 
-template <typename Handler>
-bool BaselineCodeGen<Handler>::emit_JSOP_SETLOCAL() {
+template <>
+bool BaselineInterpreterCodeGen::emit_JSOP_GETLOCAL() {
+  MOZ_CRASH("NYI: interpreter JSOP_GETLOCAL");
+}
+
+template <>
+bool BaselineCompilerCodeGen::emit_JSOP_SETLOCAL() {
   // Ensure no other StackValue refers to the old value, for instance i + (i =
   // 3). This also allows us to use R0 as scratch below.
   frame.syncStack(1);
 
-  uint32_t local = GET_LOCALNO(pc);
+  uint32_t local = GET_LOCALNO(handler.pc());
   storeValue(frame.peek(-1), frame.addressOfLocal(local), R0);
   return true;
 }
 
-template <typename Handler>
-bool BaselineCodeGen<Handler>::emitFormalArgAccess(uint32_t arg, bool get) {
+template <>
+bool BaselineInterpreterCodeGen::emit_JSOP_SETLOCAL() {
+  MOZ_CRASH("NYI: interpreter JSOP_SETLOCAL");
+}
+
+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 (get) {
+    if (op == JSOP_GETARG) {
       frame.pushArg(arg);
     } else {
       // See the comment in emit_JSOP_SETLOCAL.
       frame.syncStack(1);
       storeValue(frame.peek(-1), frame.addressOfArg(arg), R0);
     }
 
     return true;
@@ -3135,34 +3310,34 @@ bool BaselineCodeGen<Handler>::emitForma
   // 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()) {
     Label hasArgsObj;
     masm.branchTest32(Assembler::NonZero, frame.addressOfFlags(),
                       Imm32(BaselineFrame::HAS_ARGS_OBJ), &hasArgsObj);
-    if (get) {
+    if (op == JSOP_GETARG) {
       masm.loadValue(frame.addressOfArg(arg), R0);
     } else {
       storeValue(frame.peek(-1), frame.addressOfArg(arg), R0);
     }
     masm.jump(&done);
     masm.bind(&hasArgsObj);
   }
 
   // Load the arguments object data vector.
   Register reg = R2.scratchReg();
   masm.loadPtr(
       Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfArgsObj()), reg);
   masm.loadPrivate(Address(reg, ArgumentsObject::getDataSlotOffset()), reg);
 
   // Load/store the argument.
   Address argAddr(reg, ArgumentsData::offsetOfArgs() + arg * sizeof(Value));
-  if (get) {
+  if (op == JSOP_GETARG) {
     masm.loadValue(argAddr, R0);
     frame.push(R0);
   } else {
     masm.guardedCallPreBarrier(argAddr, MIRType::Value);
     masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R0);
     masm.storeValue(R0, argAddr);
 
     MOZ_ASSERT(frame.numUnsyncedSlots() == 0);
@@ -3184,33 +3359,36 @@ bool BaselineCodeGen<Handler>::emitForma
 
     masm.bind(&skipBarrier);
   }
 
   masm.bind(&done);
   return true;
 }
 
+template <>
+bool BaselineInterpreterCodeGen::emitFormalArgAccess(JSOp op) {
+  MOZ_CRASH("NYI: interpreter emitFormalArgAccess");
+}
+
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_GETARG() {
-  uint32_t arg = GET_ARGNO(pc);
-  return emitFormalArgAccess(arg, /* get = */ true);
+  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();
   }
 
   modifiesArguments_ = true;
 
-  uint32_t arg = GET_ARGNO(pc);
-  return emitFormalArgAccess(arg, /* get = */ false);
+  return emitFormalArgAccess(JSOP_SETARG);
 }
 
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_NEWTARGET() {
   if (script->isForEval()) {
     frame.pushEvalNewTarget();
     return true;
   }
@@ -3310,23 +3488,28 @@ bool BaselineCodeGen<Handler>::emitUnini
   if (!callVM(ThrowRuntimeLexicalErrorInfo)) {
     return false;
   }
 
   masm.bind(&done);
   return true;
 }
 
-template <typename Handler>
-bool BaselineCodeGen<Handler>::emit_JSOP_CHECKLEXICAL() {
+template <>
+bool BaselineCompilerCodeGen::emit_JSOP_CHECKLEXICAL() {
   frame.syncStack(0);
-  masm.loadValue(frame.addressOfLocal(GET_LOCALNO(pc)), R0);
+  masm.loadValue(frame.addressOfLocal(GET_LOCALNO(handler.pc())), R0);
   return emitUninitializedLexicalCheck(R0);
 }
 
+template <>
+bool BaselineInterpreterCodeGen::emit_JSOP_CHECKLEXICAL() {
+  MOZ_CRASH("NYI: interpreter JSOP_CHECKLEXICAL");
+}
+
 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);
@@ -3348,37 +3531,42 @@ bool BaselineCodeGen<Handler>::emit_JSOP
 }
 
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_UNINITIALIZED() {
   frame.push(MagicValue(JS_UNINITIALIZED_LEXICAL));
   return true;
 }
 
-template <typename Handler>
-bool BaselineCodeGen<Handler>::emitCall(JSOp op) {
+template <>
+bool BaselineCompilerCodeGen::emitCall(JSOp op) {
   MOZ_ASSERT(IsCallOp(op));
 
   frame.syncStack(0);
 
-  uint32_t argc = GET_ARGC(pc);
+  uint32_t argc = GET_ARGC(handler.pc());
   masm.move32(Imm32(argc), R0.scratchReg());
 
   // Call IC
   if (!emitNextIC()) {
     return false;
   }
 
   // Update FrameInfo.
   bool construct = op == JSOP_NEW || op == JSOP_SUPERCALL;
   frame.popn(2 + argc + construct);
   frame.push(R0);
   return true;
 }
 
+template <>
+bool BaselineInterpreterCodeGen::emitCall(JSOp op) {
+  MOZ_CRASH("NYI: interpreter emitCall");
+}
+
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emitSpreadCall(JSOp op) {
   MOZ_ASSERT(IsCallOp(op));
 
   frame.syncStack(0);
   masm.move32(Imm32(1), R0.scratchReg());
 
   // Call IC
@@ -3939,29 +4127,29 @@ bool BaselineCodeGen<Handler>::emitRetur
     prepareVMCall();
     pushBytecodePCArg();
     pushArg(R0.scratchReg());
     if (!callVM(DebugEpilogueInfo)) {
       return false;
     }
 
     // Fix up the RetAddrEntry appended by callVM for on-stack recompilation.
-    retAddrEntries_.back().setKind(RetAddrEntry::Kind::DebugEpilogue);
+    handler.markLastRetAddrEntryKind(RetAddrEntry::Kind::DebugEpilogue);
 
     masm.loadValue(frame.addressOfReturnValue(), JSReturnOperand);
     return true;
   };
   if (!emitDebugInstrumentation(ifDebuggee)) {
     return false;
   }
 
   // Only emit the jump if this JSOP_RETRVAL is not the last instruction.
   // Not needed for last instruction, because last instruction flows
   // into return label.
-  if (pc + GetBytecodeLength(pc) < script->codeEnd()) {
+  if (!handler.isDefinitelyLastOp()) {
     masm.jump(&return_);
   }
 
   return true;
 }
 
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_RETURN() {
@@ -4191,16 +4379,17 @@ bool BaselineCodeGen<Handler>::emit_JSOP
   masm.bind(&done);
   frame.push(R0);
   return true;
 }
 
 template <>
 void BaselineCompilerCodeGen::emitGetTableSwitchIndex(ValueOperand val,
                                                       Register dest) {
+  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 length = high - low + 1;
 
   // Jump to the 'default' pc if not int32 (tableswitch is only used when
@@ -4216,45 +4405,58 @@ void BaselineCompilerCodeGen::emitGetTab
 }
 
 template <>
 void BaselineInterpreterCodeGen::emitGetTableSwitchIndex(ValueOperand val,
                                                          Register dest) {
   MOZ_CRASH("NYI: interpreter emitTableSwitchJumpTableIndex");
 }
 
+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);
+  LoadBaselineScriptResumeEntries(masm, 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");
+}
+
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_TABLESWITCH() {
   frame.popRegsAndSync(1);
 
-  uint32_t firstResumeIndex = GET_RESUMEINDEX(pc + 3 * JUMP_OFFSET_LEN);
-
   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);
 
-  // Jump to resumeEntries[firstResumeIndex + key].
-  //
-  // Note: BytecodeEmitter::allocateResumeIndex static_asserts
-  // |firstResumeIndex * sizeof(uintptr_t)| fits in int32_t.
-
-  LoadBaselineScriptResumeEntries(masm, script, scratch1, scratch2);
-  masm.loadPtr(BaseIndex(scratch1, key, ScaleFromElemWidth(sizeof(uintptr_t)),
-                         firstResumeIndex * sizeof(uintptr_t)),
-               scratch1);
-  masm.jump(scratch1);
-
+  // Jump to the target pc.
+  emitTableSwitchJump(key, scratch1, scratch2);
   return true;
 }
 
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_ITER() {
   frame.popRegsAndSync(1);
 
   if (!emitNextIC()) {
@@ -4345,34 +4547,39 @@ bool BaselineCodeGen<Handler>::emit_JSOP
   frame.syncStack(0);
   masm.loadFunctionFromCalleeToken(frame.addressOfCalleeToken(),
                                    R0.scratchReg());
   masm.tagValue(JSVAL_TYPE_OBJECT, R0.scratchReg(), R0);
   frame.push(R0);
   return true;
 }
 
-template <typename Handler>
-bool BaselineCodeGen<Handler>::emit_JSOP_ENVCALLEE() {
+template <>
+bool BaselineCompilerCodeGen::emit_JSOP_ENVCALLEE() {
   frame.syncStack(0);
-  uint8_t numHops = GET_UINT8(pc);
+  uint8_t numHops = GET_UINT8(handler.pc());
   Register scratch = R0.scratchReg();
 
   masm.loadPtr(frame.addressOfEnvironmentChain(), scratch);
   for (unsigned i = 0; i < numHops; i++) {
     Address nextAddr(scratch,
                      EnvironmentObject::offsetOfEnclosingEnvironment());
     masm.unboxObject(nextAddr, scratch);
   }
 
   masm.loadValue(Address(scratch, CallObject::offsetOfCallee()), R0);
   frame.push(R0);
   return true;
 }
 
+template <>
+bool BaselineInterpreterCodeGen::emit_JSOP_ENVCALLEE() {
+  MOZ_CRASH("NYI: interpreter JSOP_ENVCALLEE");
+}
+
 typedef JSObject* (*HomeObjectSuperBaseFn)(JSContext*, HandleObject);
 static const VMFunction HomeObjectSuperBaseInfo =
     FunctionInfo<HomeObjectSuperBaseFn>(HomeObjectSuperBase,
                                         "HomeObjectSuperBase");
 
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_SUPERBASE() {
   frame.popRegsAndSync(1);
@@ -4563,17 +4770,17 @@ bool BaselineCodeGen<Handler>::emit_JSOP
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_INITIALYIELD() {
   frame.syncStack(0);
   MOZ_ASSERT(frame.stackDepth() == 1);
 
   Register genObj = R2.scratchReg();
   masm.unboxObject(frame.addressOfStackValue(frame.peek(-1)), genObj);
 
-  MOZ_ASSERT(GET_RESUMEINDEX(pc) == 0);
+  MOZ_ASSERT_IF(handler.maybePC(), GET_RESUMEINDEX(handler.maybePC()) == 0);
   masm.storeValue(Int32Value(0),
                   Address(genObj, GeneratorObject::offsetOfResumeIndexSlot()));
 
   Register envObj = R0.scratchReg();
   Address envChainSlot(genObj, GeneratorObject::offsetOfEnvironmentChainSlot());
   masm.loadPtr(frame.addressOfEnvironmentChain(), envObj);
   masm.guardedCallPreBarrier(envChainSlot, MIRType::Value);
   masm.storeValue(JSVAL_TYPE_OBJECT, envObj, envChainSlot);
@@ -4605,28 +4812,28 @@ bool BaselineCodeGen<Handler>::emit_JSOP
   Register genObj = R2.scratchReg();
   masm.unboxObject(R0, genObj);
 
   MOZ_ASSERT(frame.stackDepth() >= 1);
 
   if (frame.stackDepth() == 1) {
     // If the expression stack is empty, we can inline the YIELD.
 
-    masm.storeValue(
-        Int32Value(GET_RESUMEINDEX(pc)),
-        Address(genObj, GeneratorObject::offsetOfResumeIndexSlot()));
+    Register temp = R1.scratchReg();
+    Address resumeIndexSlot(genObj, GeneratorObject::offsetOfResumeIndexSlot());
+    loadResumeIndexBytecodeOperand(temp);
+    masm.storeValue(JSVAL_TYPE_INT32, temp, resumeIndexSlot);
 
     Register envObj = R0.scratchReg();
     Address envChainSlot(genObj,
                          GeneratorObject::offsetOfEnvironmentChainSlot());
     masm.loadPtr(frame.addressOfEnvironmentChain(), envObj);
     masm.guardedCallPreBarrier(envChainSlot, MIRType::Value);
     masm.storeValue(JSVAL_TYPE_OBJECT, envObj, envChainSlot);
 
-    Register temp = R1.scratchReg();
     Label skipBarrier;
     masm.branchPtrInNurseryChunk(Assembler::Equal, genObj, temp, &skipBarrier);
     masm.branchPtrInNurseryChunk(Assembler::NotEqual, envObj, temp,
                                  &skipBarrier);
     MOZ_ASSERT(genObj == R2.scratchReg());
     masm.call(&postBarrierSlot_);
     masm.bind(&skipBarrier);
   } else {
@@ -4664,17 +4871,17 @@ bool BaselineCodeGen<Handler>::emit_JSOP
     masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
     prepareVMCall();
     pushBytecodePCArg();
     pushArg(R0.scratchReg());
     if (!callVM(DebugAfterYieldInfo)) {
       return false;
     }
 
-    retAddrEntries_.back().setKind(RetAddrEntry::Kind::DebugAfterYield);
+    handler.markLastRetAddrEntryKind(RetAddrEntry::Kind::DebugAfterYield);
 
     Label done;
     masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, &done);
     {
       masm.loadValue(frame.addressOfReturnValue(), JSReturnOperand);
       masm.jump(&return_);
     }
     masm.bind(&done);
@@ -4712,19 +4919,20 @@ static const VMFunction InterpretResumeI
 
 typedef bool (*GeneratorThrowFn)(JSContext*, BaselineFrame*,
                                  Handle<GeneratorObject*>, HandleValue,
                                  uint32_t);
 static const VMFunction GeneratorThrowOrReturnInfo =
     FunctionInfo<GeneratorThrowFn>(jit::GeneratorThrowOrReturn,
                                    "GeneratorThrowOrReturn", TailCall);
 
-template <typename Handler>
-bool BaselineCodeGen<Handler>::emit_JSOP_RESUME() {
-  GeneratorObject::ResumeKind resumeKind = GeneratorObject::getResumeKind(pc);
+template <>
+bool BaselineCompilerCodeGen::emit_JSOP_RESUME() {
+  GeneratorObject::ResumeKind resumeKind =
+      GeneratorObject::getResumeKind(handler.pc());
 
   frame.syncStack(0);
   masm.assertStackAlignment(sizeof(Value), 0);
 
   AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
   regs.take(BaselineFrameReg);
 
   // Load generator object.
@@ -4793,17 +5001,18 @@ bool BaselineCodeGen<Handler>::emit_JSOP
   Label genStart, returnTarget;
 #ifdef JS_USE_LINK_REGISTER
   masm.call(&genStart);
 #else
   masm.callAndPushReturnAddress(&genStart);
 #endif
 
   // Add a RetAddrEntry so the return offset -> pc mapping works.
-  if (!appendRetAddrEntry(RetAddrEntry::Kind::IC, masm.currentOffset())) {
+  if (!handler.appendRetAddrEntry(cx, RetAddrEntry::Kind::IC,
+                                  masm.currentOffset())) {
     return false;
   }
 
   masm.jump(&returnTarget);
   masm.bind(&genStart);
 #ifdef JS_USE_LINK_REGISTER
   masm.pushReturnAddress();
 #endif
@@ -4973,16 +5182,21 @@ bool BaselineCodeGen<Handler>::emit_JSOP
   masm.computeEffectiveAddress(frame.addressOfStackValue(frame.peek(-1)),
                                masm.getStackPointer());
   masm.switchToRealm(script->realm(), R2.scratchReg());
   frame.popn(2);
   frame.push(R0);
   return true;
 }
 
+template <>
+bool BaselineInterpreterCodeGen::emit_JSOP_RESUME() {
+  MOZ_CRASH("NYI: interpreter JSOP_RESUME");
+}
+
 typedef bool (*CheckSelfHostedFn)(JSContext*, HandleValue);
 static const VMFunction CheckSelfHostedInfo = FunctionInfo<CheckSelfHostedFn>(
     js::Debug_CheckSelfHosted, "Debug_CheckSelfHosted");
 
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_DEBUGCHECKSELFHOSTED() {
 #ifdef DEBUG
   frame.syncStack(0);
@@ -4999,27 +5213,32 @@ bool BaselineCodeGen<Handler>::emit_JSOP
 }
 
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_IS_CONSTRUCTING() {
   frame.push(MagicValue(JS_IS_CONSTRUCTING));
   return true;
 }
 
-template <typename Handler>
-bool BaselineCodeGen<Handler>::emit_JSOP_JUMPTARGET() {
+template <>
+bool BaselineCompilerCodeGen::emit_JSOP_JUMPTARGET() {
   if (!script->hasScriptCounts()) {
     return true;
   }
-  PCCounts* counts = script->maybeGetPCCounts(pc);
+  PCCounts* counts = script->maybeGetPCCounts(handler.pc());
   uint64_t* counterAddr = &counts->numExec();
   masm.inc64(AbsoluteAddress(counterAddr));
   return true;
 }
 
+template <>
+bool BaselineInterpreterCodeGen::emit_JSOP_JUMPTARGET() {
+  MOZ_CRASH("NYI: interpreter JSOP_JUMPTARGET");
+}
+
 typedef bool (*CheckClassHeritageOperationFn)(JSContext*, HandleValue);
 static const VMFunction CheckClassHeritageOperationInfo =
     FunctionInfo<CheckClassHeritageOperationFn>(js::CheckClassHeritageOperation,
                                                 "CheckClassHeritageOperation");
 
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_CHECKCLASSHERITAGE() {
   frame.syncStack(0);
@@ -5051,29 +5270,34 @@ bool BaselineCodeGen<Handler>::emit_JSOP
   masm.branchPtrInNurseryChunk(Assembler::Equal, func, temp, &skipBarrier);
   masm.branchValueIsNurseryObject(Assembler::NotEqual, R0, temp, &skipBarrier);
   masm.call(&postBarrierSlot_);
   masm.bind(&skipBarrier);
 
   return true;
 }
 
-template <typename Handler>
-bool BaselineCodeGen<Handler>::emit_JSOP_BUILTINPROTO() {
+template <>
+bool BaselineCompilerCodeGen::emit_JSOP_BUILTINPROTO() {
   // The builtin prototype is a constant for a given global.
-  JSProtoKey key = static_cast<JSProtoKey>(GET_UINT8(pc));
+  JSProtoKey key = static_cast<JSProtoKey>(GET_UINT8(handler.pc()));
   MOZ_ASSERT(key < JSProto_LIMIT);
   JSObject* builtin = GlobalObject::getOrCreatePrototype(cx, key);
   if (!builtin) {
     return false;
   }
   frame.push(ObjectValue(*builtin));
   return true;
 }
 
+template <>
+bool BaselineInterpreterCodeGen::emit_JSOP_BUILTINPROTO() {
+  MOZ_CRASH("NYI: interpreter JSOP_BUILTINPROTO");
+}
+
 typedef JSObject* (*ObjectWithProtoOperationFn)(JSContext*, HandleValue);
 static const VMFunction ObjectWithProtoOperationInfo =
     FunctionInfo<ObjectWithProtoOperationFn>(js::ObjectWithProtoOperation,
                                              "ObjectWithProtoOperationInfo");
 
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_OBJWITHPROTO() {
   frame.syncStack(0);
@@ -5346,47 +5570,47 @@ bool BaselineCompiler::emitEpilogue() {
 
   emitProfilerExitFrame();
 
   masm.ret();
   return true;
 }
 
 MethodStatus BaselineCompiler::emitBody() {
-  MOZ_ASSERT(pc == script->code());
+  MOZ_ASSERT(handler.pc() == script->code());
 
   bool lastOpUnreachable = false;
   uint32_t emittedOps = 0;
-  mozilla::DebugOnly<jsbytecode*> prevpc = pc;
+  mozilla::DebugOnly<jsbytecode*> prevpc = handler.pc();
 
   while (true) {
-    JSOp op = JSOp(*pc);
+    JSOp op = JSOp(*handler.pc());
     JitSpew(JitSpew_BaselineOp, "Compiling op @ %d: %s",
-            int(script->pcToOffset(pc)), CodeName[op]);
-
-    BytecodeInfo* info = analysis_.maybeInfo(pc);
+            int(script->pcToOffset(handler.pc())), CodeName[op]);
+
+    BytecodeInfo* info = analysis_.maybeInfo(handler.pc());
 
     // Skip unreachable ops.
     if (!info) {
       // Test if last instructions and stop emitting in that case.
-      pc += GetBytecodeLength(pc);
-      if (pc >= script->codeEnd()) {
+      handler.moveToNextPC();
+      if (handler.pc() >= script->codeEnd()) {
         break;
       }
 
       lastOpUnreachable = true;
-      prevpc = pc;
+      prevpc = handler.pc();
       continue;
     }
 
     if (info->jumpTarget) {
       // Fully sync the stack if there are incoming jumps.
       frame.syncStack(0);
       frame.setStackDepth(info->stackDepth);
-      masm.bind(handler.labelOf(pc));
+      masm.bind(handler.labelOf(handler.pc()));
     } else if (MOZ_UNLIKELY(compileDebugInstrumentation())) {
       // Also fully sync the stack if the debugger is enabled.
       frame.syncStack(0);
     } else {
       // At the beginning of any op, at most the top 2 stack-values are
       // unsynced.
       if (frame.stackDepth() > 2) {
         frame.syncStack(2);
@@ -5394,18 +5618,18 @@ MethodStatus BaselineCompiler::emitBody(
     }
 
     frame.assertValidState(*info);
 
     // Add a PC -> native mapping entry for the current op. These entries are
     // used when we need the native code address for a given pc, for instance
     // for bailouts from Ion, the debugger and exception handling. See
     // PCMappingIndexEntry for more information.
-    bool addIndexEntry =
-        (pc == script->code() || lastOpUnreachable || emittedOps > 100);
+    bool addIndexEntry = (handler.pc() == script->code() || lastOpUnreachable ||
+                          emittedOps > 100);
     if (addIndexEntry) {
       emittedOps = 0;
     }
     if (MOZ_UNLIKELY(!addPCMappingEntry(addIndexEntry))) {
       ReportOutOfMemory(cx);
       return Method_Error;
     }
 
@@ -5432,33 +5656,33 @@ MethodStatus BaselineCompiler::emitBody(
   case OP:                                                     \
     if (MOZ_UNLIKELY(!this->emit_##OP())) return Method_Error; \
     break;
         OPCODE_LIST(EMIT_OP)
 #undef EMIT_OP
     }
 
     // If the main instruction is not a jump target, then we emit the
-    //  corresponding code coverage counter.
-    if (pc == script->main() && !BytecodeIsJumpTarget(op)) {
+    // corresponding code coverage counter.
+    if (handler.pc() == script->main() && !BytecodeIsJumpTarget(op)) {
       if (!emit_JSOP_JUMPTARGET()) {
         return Method_Error;
       }
     }
 
     // Test if last instructions and stop emitting in that case.
-    pc += GetBytecodeLength(pc);
-    if (pc >= script->codeEnd()) {
+    handler.moveToNextPC();
+    if (handler.pc() >= script->codeEnd()) {
       break;
     }
 
     emittedOps++;
     lastOpUnreachable = false;
 #ifdef DEBUG
-    prevpc = pc;
+    prevpc = handler.pc();
 #endif
   }
 
   MOZ_ASSERT(JSOp(*prevpc) == JSOP_RETRVAL);
   return Method_Compiled;
 }
 
 // Instantiate explicitly for now to make sure it compiles.
--- a/js/src/jit/BaselineCompiler.h
+++ b/js/src/jit/BaselineCompiler.h
@@ -256,25 +256,23 @@ namespace jit {
 // this class to specialize behavior.
 template <typename Handler>
 class BaselineCodeGen {
  protected:
   Handler handler;
 
   JSContext* cx;
   JSScript* script;
-  jsbytecode* pc;
   StackMacroAssembler masm;
   bool ionCompileable_;
 
   TempAllocator& alloc_;
   BytecodeAnalysis analysis_;
   FrameInfo frame;
 
-  js::Vector<RetAddrEntry, 16, SystemAllocPolicy> retAddrEntries_;
   js::Vector<CodeOffset> traceLoggerToggleOffsets_;
 
   NonAssertingLabel return_;
   NonAssertingLabel postBarrierSlot_;
 
   // Index of the current ICEntry in the script's ICScript.
   uint32_t icEntryIndex_;
 
@@ -285,26 +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);
 
-  MOZ_MUST_USE bool appendRetAddrEntry(RetAddrEntry::Kind kind,
-                                       uint32_t retOffset) {
-    if (!retAddrEntries_.emplaceBack(script->pcToOffset(pc), kind,
-                                     CodeOffset(retOffset))) {
-      ReportOutOfMemory(cx);
-      return false;
-    }
-    return true;
-  }
-
   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(); }
 
@@ -325,26 +313,28 @@ class BaselineCodeGen {
   void pushScriptObjectArg(ScriptObjectType type);
   void pushScriptNameArg();
   void pushScriptScopeArg();
 
   // Pushes a bytecode operand as argument for a VM function.
   void pushUint8BytecodeOperandArg();
   void pushUint16BytecodeOperandArg();
 
+  void loadResumeIndexBytecodeOperand(Register dest);
+
   void prepareVMCall();
 
   enum CallVMPhase { POST_INITIALIZE, CHECK_OVER_RECURSED };
   bool callVM(const VMFunction& fun, CallVMPhase phase = POST_INITIALIZE);
 
   bool callVMNonOp(const VMFunction& fun, CallVMPhase phase = POST_INITIALIZE) {
     if (!callVM(fun, phase)) {
       return false;
     }
-    retAddrEntries_.back().setKind(RetAddrEntry::Kind::NonOpCallVM);
+    handler.markLastRetAddrEntryKind(RetAddrEntry::Kind::NonOpCallVM);
     return true;
   }
 
   // ifDebuggee should be a function emitting code for when the script is a
   // debuggee script. ifNotDebuggee (if present) is called to emit code for
   // non-debuggee scripts.
   template <typename F1, typename F2>
   MOZ_MUST_USE bool emitDebugInstrumentation(
@@ -354,17 +344,17 @@ class BaselineCodeGen {
     return emitDebugInstrumentation(ifDebuggee, mozilla::Maybe<F>());
   }
 
   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(bool allowOsr = true);
+  MOZ_MUST_USE bool emitWarmUpCounterIncrement();
   MOZ_MUST_USE bool emitTraceLoggerResume(Register script,
                                           AllocatableGeneralRegisterSet& regs);
 
   void storeValue(const StackValue* source, const Address& dest,
                   const ValueOperand& scratch);
 
 #define EMIT_OP(op) bool emit_##op();
   OPCODE_LIST(EMIT_OP)
@@ -385,16 +375,20 @@ class BaselineCodeGen {
   // 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);
 
+  // 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();
   MOZ_MUST_USE bool emitTest(bool branchIfTrue);
   MOZ_MUST_USE bool emitAndOr(bool branchIfTrue);
 
   MOZ_MUST_USE bool emitCall(JSOp op);
   MOZ_MUST_USE bool emitSpreadCall(JSOp op);
@@ -402,50 +396,80 @@ class BaselineCodeGen {
   MOZ_MUST_USE bool emitDelElem(bool strict);
   MOZ_MUST_USE bool emitDelProp(bool strict);
   MOZ_MUST_USE bool emitSetElemSuper(bool strict);
   MOZ_MUST_USE bool emitSetPropSuper(bool strict);
 
   MOZ_MUST_USE bool emitBindName(JSOp op);
   MOZ_MUST_USE bool emitDefLexical(JSOp op);
 
+  // Try to bake in the result of GETGNAME/BINDGNAME instead of using an IC.
+  // Return true if we managed to optimize the op.
+  bool tryOptimizeGetGlobalName();
+  bool tryOptimizeBindGlobalName();
+
   MOZ_MUST_USE bool emitInitPropGetterSetter();
   MOZ_MUST_USE bool emitInitElemGetterSetter();
 
-  MOZ_MUST_USE bool emitFormalArgAccess(uint32_t arg, bool get);
+  MOZ_MUST_USE bool emitFormalArgAccess(JSOp op);
 
   MOZ_MUST_USE bool emitThrowConstAssignment();
   MOZ_MUST_USE bool emitUninitializedLexicalCheck(const ValueOperand& val);
 
   MOZ_MUST_USE bool emitIsMagicValue();
 
   void getEnvironmentCoordinateObject(Register reg);
   Address getEnvironmentCoordinateAddressFromObject(Register objReg,
                                                     Register reg);
   Address getEnvironmentCoordinateAddress(Register reg);
 };
 
+using RetAddrEntryVector = js::Vector<RetAddrEntry, 16, SystemAllocPolicy>;
+
 // Interface used by BaselineCodeGen for BaselineCompiler.
 class BaselineCompilerHandler {
   TempAllocator& alloc_;
   FixedList<Label> labels_;
+  RetAddrEntryVector retAddrEntries_;
   JSScript* script_;
+  jsbytecode* pc_;
   bool compileDebugInstrumentation_;
 
  public:
   BaselineCompilerHandler(TempAllocator& alloc, JSScript* script);
 
   MOZ_MUST_USE bool init();
 
+  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(); }
+
   void setCompileDebugInstrumentation() { compileDebugInstrumentation_ = true; }
   bool compileDebugInstrumentation() const {
     return compileDebugInstrumentation_;
   }
+
+  RetAddrEntryVector& retAddrEntries() { return retAddrEntries_; }
+
+  MOZ_MUST_USE bool appendRetAddrEntry(JSContext* cx, RetAddrEntry::Kind kind,
+                                       uint32_t retOffset) {
+    if (!retAddrEntries_.emplaceBack(script_->pcToOffset(pc_), kind,
+                                     CodeOffset(retOffset))) {
+      ReportOutOfMemory(cx);
+      return false;
+    }
+    return true;
+  }
+  void markLastRetAddrEntryKind(RetAddrEntry::Kind kind) {
+    retAddrEntries_.back().setKind(kind);
+  }
 };
 
 using BaselineCompilerCodeGen = BaselineCodeGen<BaselineCompilerHandler>;
 
 class BaselineCompiler final : private BaselineCompilerCodeGen {
   // Stores the native code offset for a bytecode pc.
   struct PCMappingEntry {
     uint32_t pcOffset;
@@ -537,16 +561,28 @@ class BaselineCompiler final : private B
 
   MOZ_MUST_USE bool addPCMappingEntry(bool addIndexEntry);
 };
 
 // Interface used by BaselineCodeGen for BaselineInterpreterGenerator.
 class BaselineInterpreterHandler {
  public:
   explicit BaselineInterpreterHandler();
+
+  // Interpreter doesn't know the pc statically.
+  jsbytecode* maybePC() const { return nullptr; }
+  bool isDefinitelyLastOp() const { return false; }
+
+  // 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) {}
 };
 
 using BaselineInterpreterCodeGen = BaselineCodeGen<BaselineInterpreterHandler>;
 
 class BaselineInterpreterGenerator final : private BaselineInterpreterCodeGen {
  public:
 };