Bug 1508962 part 1 - Add a BaselineCodeGen<Handler> base class to BaselineCompiler and use it for JSOp compilation. r=djvj
authorJan de Mooij <jdemooij@mozilla.com>
Thu, 29 Nov 2018 22:43:00 +0000
changeset 505305 77764799fba3b6f29fb7b5ce15e4ba73f263832f
parent 505304 7a24e1e35a7e4a372f719463ca367882bb6de5dd
child 505306 e0b75c94bc06140a53af4e9bc1a6c738b62d5793
push id10290
push userffxbld-merge
push dateMon, 03 Dec 2018 16:23:23 +0000
treeherdermozilla-beta@700bed2445e6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdjvj
bugs1508962
milestone65.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 1508962 part 1 - Add a BaselineCodeGen<Handler> base class to BaselineCompiler and use it for JSOp compilation. r=djvj This is just the first step: the Handler classes are still empty and BaselineCodeGen contains fields/methods that should eventually move out of there. That will happen in follow-up patches. Rooting analysis now reported a hazard in ObjectGroup::getOrFixupCopyOnWriteObject, this patch has the trivial fix for that too. Differential Revision: https://phabricator.services.mozilla.com/D12522
js/src/jit/BaselineCompiler.cpp
js/src/jit/BaselineCompiler.h
js/src/vm/ObjectGroup.cpp
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -36,37 +36,44 @@
 #include "vm/NativeObject-inl.h"
 #include "vm/TypeInference-inl.h"
 
 using namespace js;
 using namespace js::jit;
 
 using mozilla::AssertedCast;
 
-BaselineCompiler::BaselineCompiler(JSContext* cx, TempAllocator& alloc, JSScript* script)
-  : cx(cx),
+template <typename Handler>
+BaselineCodeGen<Handler>::BaselineCodeGen(JSContext* cx, TempAllocator& alloc, JSScript* script)
+  : handler(),
+    cx(cx),
     script(script),
     pc(script->code()),
     ionCompileable_(jit::IsIonEnabled(cx) && CanIonCompileScript(cx, script)),
     compileDebugInstrumentation_(script->isDebuggee()),
     alloc_(alloc),
     analysis_(alloc, script),
     frame(script, masm),
-    pcMappingEntries_(),
+    traceLoggerToggleOffsets_(cx),
     icEntryIndex_(0),
     pushedBeforeCall_(0),
 #ifdef DEBUG
     inCall_(false),
 #endif
+    modifiesArguments_(false)
+{
+}
+
+BaselineCompiler::BaselineCompiler(JSContext* cx, TempAllocator& alloc, JSScript* script)
+  : BaselineCodeGen(cx, alloc, script),
+    pcMappingEntries_(),
     profilerPushToggleOffset_(),
     profilerEnterFrameToggleOffset_(),
     profilerExitFrameToggleOffset_(),
-    traceLoggerToggleOffsets_(cx),
-    traceLoggerScriptTextIdOffset_(),
-    modifiesArguments_(false)
+    traceLoggerScriptTextIdOffset_()
 {
 #ifdef JS_CODEGEN_NONE
     MOZ_CRASH();
 #endif
 }
 
 bool
 BaselineCompiler::init()
@@ -560,18 +567,19 @@ BaselineCompiler::emitOutOfLinePostBarri
     masm.passABIArg(objReg);
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, PostWriteBarrier));
 
     masm.popValue(R0);
     masm.ret();
     return true;
 }
 
-bool
-BaselineCompiler::emitNextIC()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::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);
 
     // We don't use every ICEntry and we can skip unreachable ops, so we have
@@ -594,33 +602,35 @@ BaselineCompiler::emitNextIC()
     if (!retAddrEntries_.emplaceBack(script->pcToOffset(pc), kind, callOffset)) {
         ReportOutOfMemory(cx);
         return false;
     }
 
     return true;
 }
 
+template <typename Handler>
 void
-BaselineCompiler::prepareVMCall()
+BaselineCodeGen<Handler>::prepareVMCall()
 {
     pushedBeforeCall_ = masm.framePushed();
 #ifdef DEBUG
     inCall_ = true;
 #endif
 
     // Ensure everything is synced.
     frame.syncStack(0);
 
     // Save the frame pointer.
     masm.Push(BaselineFrameReg);
 }
 
-bool
-BaselineCompiler::callVM(const VMFunction& fun, CallVMPhase phase)
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::callVM(const VMFunction& fun, CallVMPhase phase)
 {
     TrampolinePtr code = cx->runtime()->jitRuntime()->getVMWrapper(fun);
 
 #ifdef DEBUG
     // Assert prepareVMCall() has been called.
     MOZ_ASSERT(inCall_);
     inCall_ = false;
 
@@ -853,18 +863,19 @@ BaselineCompiler::initEnvironmentChain()
 
     return true;
 }
 
 typedef bool (*InterruptCheckFn)(JSContext*);
 static const VMFunction InterruptCheckInfo =
     FunctionInfo<InterruptCheckFn>(InterruptCheck, "InterruptCheck");
 
-bool
-BaselineCompiler::emitInterruptCheck()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emitInterruptCheck()
 {
     frame.syncStack(0);
 
     Label done;
     masm.branch32(Assembler::Equal,
                   AbsoluteAddress(cx->addressOfInterruptBits()), Imm32(0),
                   &done);
 
@@ -877,18 +888,19 @@ BaselineCompiler::emitInterruptCheck()
     return true;
 }
 
 typedef bool (*IonCompileScriptForBaselineFn)(JSContext*, BaselineFrame*, jsbytecode*);
 static const VMFunction IonCompileScriptForBaselineInfo =
     FunctionInfo<IonCompileScriptForBaselineFn>(IonCompileScriptForBaseline,
                                                 "IonCompileScriptForBaseline");
 
-bool
-BaselineCompiler::emitWarmUpCounterIncrement(bool allowOsr)
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emitWarmUpCounterIncrement(bool allowOsr)
 {
     // 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;
     }
 
@@ -1068,18 +1080,20 @@ BaselineCompiler::emitTraceLoggerExit()
 
     masm.Pop(loggerReg);
 
     masm.bind(&noTraceLogger);
 
     return true;
 }
 
-bool
-BaselineCompiler::emitTraceLoggerResume(Register baselineScript, AllocatableGeneralRegisterSet& regs)
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emitTraceLoggerResume(Register baselineScript,
+                                                 AllocatableGeneralRegisterSet& regs)
 {
     Register scriptId = regs.takeAny();
     Register loggerReg = regs.takeAny();
 
     Label noTraceLogger;
     if (!traceLoggerToggleOffsets_.append(masm.toggledJump(&noTraceLogger))) {
         return false;
     }
@@ -1238,116 +1252,128 @@ OPCODE_LIST(EMIT_OP)
         prevpc = pc;
 #endif
     }
 
     MOZ_ASSERT(JSOp(*prevpc) == JSOP_RETRVAL);
     return Method_Compiled;
 }
 
-bool
-BaselineCompiler::emit_JSOP_NOP()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_NOP()
 {
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_ITERNEXT()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_ITERNEXT()
 {
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_NOP_DESTRUCTURING()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_NOP_DESTRUCTURING()
 {
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_TRY_DESTRUCTURING_ITERCLOSE()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_TRY_DESTRUCTURING_ITERCLOSE()
 {
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_LABEL()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_LABEL()
 {
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_POP()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_POP()
 {
     frame.pop();
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_POPN()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_POPN()
 {
     frame.popn(GET_UINT16(pc));
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_DUPAT()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::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);
     masm.loadValue(frame.addressOfStackValue(frame.peek(depth)), R0);
     frame.push(R0);
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_DUP()
+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);
 
     // inc/dec ops use DUP followed by ONE, ADD. Push R0 last to avoid a move.
     frame.push(R1);
     frame.push(R0);
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_DUP2()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_DUP2()
 {
     frame.syncStack(0);
 
     masm.loadValue(frame.addressOfStackValue(frame.peek(-2)), R0);
     masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R1);
 
     frame.push(R0);
     frame.push(R1);
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_SWAP()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_SWAP()
 {
     // Keep top stack values in R0 and R1.
     frame.popRegsAndSync(2);
 
     frame.push(R1);
     frame.push(R0);
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_PICK()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::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
 
@@ -1365,18 +1391,19 @@ BaselineCompiler::emit_JSOP_PICK()
     }
 
     // Push R0.
     frame.pop();
     frame.push(R0);
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_UNPICK()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::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
 
@@ -1393,119 +1420,129 @@ BaselineCompiler::emit_JSOP_UNPICK()
     }
 
     // Store R0 under the nth value.
     Address dest = frame.addressOfStackValue(frame.peek(depth));
     masm.storeValue(R0, dest);
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_GOTO()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_GOTO()
 {
     frame.syncStack(0);
 
     jsbytecode* target = pc + GET_JUMP_OFFSET(pc);
     masm.jump(labelOf(target));
     return true;
 }
 
-bool
-BaselineCompiler::emitToBoolean()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emitToBoolean()
 {
     Label skipIC;
     masm.branchTestBoolean(Assembler::Equal, R0, &skipIC);
 
     // Call IC
     if (!emitNextIC()) {
         return false;
     }
 
     masm.bind(&skipIC);
     return true;
 }
 
-bool
-BaselineCompiler::emitTest(bool branchIfTrue)
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emitTest(bool branchIfTrue)
 {
     bool knownBoolean = frame.peek(-1)->isKnownBoolean();
 
     // Keep top stack value in R0.
     frame.popRegsAndSync(1);
 
     if (!knownBoolean && !emitToBoolean()) {
         return false;
     }
 
     // IC will leave a BooleanValue in R0, just need to branch on it.
     masm.branchTestBooleanTruthy(branchIfTrue, R0, labelOf(pc + GET_JUMP_OFFSET(pc)));
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_IFEQ()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_IFEQ()
 {
     return emitTest(false);
 }
 
-bool
-BaselineCompiler::emit_JSOP_IFNE()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_IFNE()
 {
     return emitTest(true);
 }
 
-bool
-BaselineCompiler::emitAndOr(bool branchIfTrue)
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emitAndOr(bool branchIfTrue)
 {
     bool knownBoolean = frame.peek(-1)->isKnownBoolean();
 
     // AND and OR leave the original value on the stack.
     frame.syncStack(0);
 
     masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R0);
     if (!knownBoolean && !emitToBoolean()) {
         return false;
     }
 
     masm.branchTestBooleanTruthy(branchIfTrue, R0, labelOf(pc + GET_JUMP_OFFSET(pc)));
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_AND()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_AND()
 {
     return emitAndOr(false);
 }
 
-bool
-BaselineCompiler::emit_JSOP_OR()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_OR()
 {
     return emitAndOr(true);
 }
 
-bool
-BaselineCompiler::emit_JSOP_NOT()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_NOT()
 {
     bool knownBoolean = frame.peek(-1)->isKnownBoolean();
 
     // Keep top stack value in R0.
     frame.popRegsAndSync(1);
 
     if (!knownBoolean && !emitToBoolean()) {
         return false;
     }
 
     masm.notBoolean(R0);
 
     frame.push(R0, JSVAL_TYPE_BOOLEAN);
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_POS()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_POS()
 {
     // Keep top stack value in R0.
     frame.popRegsAndSync(1);
 
     // Inline path for int32 and double.
     Label done;
     masm.branchTestNumber(Assembler::Equal, R0, &done);
 
@@ -1514,77 +1551,84 @@ BaselineCompiler::emit_JSOP_POS()
         return false;
     }
 
     masm.bind(&done);
     frame.push(R0);
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_LOOPHEAD()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_LOOPHEAD()
 {
     if (!emit_JSOP_JUMPTARGET()) {
         return false;
     }
     return emitInterruptCheck();
 }
 
-bool
-BaselineCompiler::emit_JSOP_LOOPENTRY()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_LOOPENTRY()
 {
     if (!emit_JSOP_JUMPTARGET()) {
         return false;
     }
     frame.syncStack(0);
     if (!emitWarmUpCounterIncrement(LoopEntryCanIonOsr(pc))) {
         return false;
     }
     if (script->trackRecordReplayProgress()) {
         masm.inc64(AbsoluteAddress(mozilla::recordreplay::ExecutionProgressCounter()));
     }
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_VOID()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_VOID()
 {
     frame.pop();
     frame.push(UndefinedValue());
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_UNDEFINED()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_UNDEFINED()
 {
     // If this ever changes, change what JSOP_GIMPLICITTHIS does too.
     frame.push(UndefinedValue());
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_HOLE()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_HOLE()
 {
     frame.push(MagicValue(JS_ELEMENTS_HOLE));
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_NULL()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_NULL()
 {
     frame.push(NullValue());
     return true;
 }
 
 typedef bool (*ThrowCheckIsObjectFn)(JSContext*, CheckIsObjectKind);
 static const VMFunction ThrowCheckIsObjectInfo =
     FunctionInfo<ThrowCheckIsObjectFn>(ThrowCheckIsObject, "ThrowCheckIsObject");
 
-bool
-BaselineCompiler::emit_JSOP_CHECKISOBJ()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_CHECKISOBJ()
 {
     frame.syncStack(0);
     masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R0);
 
     Label ok;
     masm.branchTestObject(Assembler::Equal, R0, &ok);
 
     prepareVMCall();
@@ -1597,18 +1641,19 @@ BaselineCompiler::emit_JSOP_CHECKISOBJ()
     masm.bind(&ok);
     return true;
 }
 
 typedef bool (*CheckIsCallableFn)(JSContext*, HandleValue, CheckIsCallableKind);
 static const VMFunction CheckIsCallableInfo =
     FunctionInfo<CheckIsCallableFn>(CheckIsCallable, "CheckIsCallable");
 
-bool
-BaselineCompiler::emit_JSOP_CHECKISCALLABLE()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_CHECKISCALLABLE()
 {
     frame.syncStack(0);
     masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R0);
 
     prepareVMCall();
 
     pushArg(Imm32(GET_UINT8(pc)));
     pushArg(R0);
@@ -1624,36 +1669,39 @@ static const VMFunction ThrowUninitializ
     FunctionInfo<ThrowUninitializedThisFn>(BaselineThrowUninitializedThis,
                                            "BaselineThrowUninitializedThis");
 
 typedef bool (*ThrowInitializedThisFn)(JSContext*);
 static const VMFunction ThrowInitializedThisInfo =
     FunctionInfo<ThrowInitializedThisFn>(BaselineThrowInitializedThis,
                                          "BaselineThrowInitializedThis");
 
-bool
-BaselineCompiler::emit_JSOP_CHECKTHIS()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_CHECKTHIS()
 {
     frame.syncStack(0);
     masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R0);
 
     return emitCheckThis(R0);
 }
 
-bool
-BaselineCompiler::emit_JSOP_CHECKTHISREINIT()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_CHECKTHISREINIT()
 {
     frame.syncStack(0);
     masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R0);
 
     return emitCheckThis(R0, /* reinit = */true);
 }
 
-bool
-BaselineCompiler::emitCheckThis(ValueOperand val, bool reinit)
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emitCheckThis(ValueOperand val, bool reinit)
 {
     Label thisOK;
     if (reinit) {
         masm.branchTestMagic(Assembler::Equal, val, &thisOK);
     } else {
         masm.branchTestMagic(Assembler::NotEqual, val, &thisOK);
     }
 
@@ -1675,18 +1723,19 @@ BaselineCompiler::emitCheckThis(ValueOpe
     masm.bind(&thisOK);
     return true;
 }
 
 typedef bool (*ThrowBadDerivedReturnFn)(JSContext*, HandleValue);
 static const VMFunction ThrowBadDerivedReturnInfo =
     FunctionInfo<ThrowBadDerivedReturnFn>(jit::ThrowBadDerivedReturn, "ThrowBadDerivedReturn");
 
-bool
-BaselineCompiler::emit_JSOP_CHECKRETURN()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_CHECKRETURN()
 {
     MOZ_ASSERT(script->isDerivedClassConstructor());
 
     // Load |this| in R0, return value in R1.
     frame.popRegsAndSync(1);
     emitLoadReturnValue(R1);
 
     Label done, returnOK;
@@ -1713,18 +1762,19 @@ BaselineCompiler::emit_JSOP_CHECKRETURN(
     masm.bind(&done);
     return true;
 }
 
 typedef bool (*GetFunctionThisFn)(JSContext*, BaselineFrame*, MutableHandleValue);
 static const VMFunction GetFunctionThisInfo =
     FunctionInfo<GetFunctionThisFn>(jit::BaselineGetFunctionThis, "BaselineGetFunctionThis");
 
-bool
-BaselineCompiler::emit_JSOP_FUNCTIONTHIS()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_FUNCTIONTHIS()
 {
     MOZ_ASSERT(function());
     MOZ_ASSERT(!function()->isArrow());
 
     frame.pushThis();
 
     // In strict mode code or self-hosted functions, |this| is left alone.
     if (script->strict() || (function() && function()->isSelfHostedBuiltin())) {
@@ -1750,18 +1800,19 @@ BaselineCompiler::emit_JSOP_FUNCTIONTHIS
     return true;
 }
 
 typedef void (*GetNonSyntacticGlobalThisFn)(JSContext*, HandleObject, MutableHandleValue);
 static const VMFunction GetNonSyntacticGlobalThisInfo =
     FunctionInfo<GetNonSyntacticGlobalThisFn>(js::GetNonSyntacticGlobalThis,
                                               "GetNonSyntacticGlobalThis");
 
-bool
-BaselineCompiler::emit_JSOP_GLOBALTHIS()
+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);
         frame.push(R0);
         return true;
@@ -1775,116 +1826,130 @@ BaselineCompiler::emit_JSOP_GLOBALTHIS()
     if (!callVM(GetNonSyntacticGlobalThisInfo)) {
         return false;
     }
 
     frame.push(R0);
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_TRUE()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_TRUE()
 {
     frame.push(BooleanValue(true));
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_FALSE()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_FALSE()
 {
     frame.push(BooleanValue(false));
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_ZERO()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_ZERO()
 {
     frame.push(Int32Value(0));
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_ONE()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_ONE()
 {
     frame.push(Int32Value(1));
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_INT8()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_INT8()
 {
     frame.push(Int32Value(GET_INT8(pc)));
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_INT32()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_INT32()
 {
     frame.push(Int32Value(GET_INT32(pc)));
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_UINT16()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_UINT16()
 {
     frame.push(Int32Value(GET_UINT16(pc)));
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_UINT24()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_UINT24()
 {
     frame.push(Int32Value(GET_UINT24(pc)));
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_RESUMEINDEX()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_RESUMEINDEX()
 {
     return emit_JSOP_UINT24();
 }
 
-bool
-BaselineCompiler::emit_JSOP_DOUBLE()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_DOUBLE()
 {
     frame.push(script->getConst(GET_UINT32_INDEX(pc)));
     return true;
 }
 
 #ifdef ENABLE_BIGINT
-bool
-BaselineCompiler::emit_JSOP_BIGINT()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_BIGINT()
 {
     frame.push(script->getConst(GET_UINT32_INDEX(pc)));
     return true;
 }
 #endif
 
-bool
-BaselineCompiler::emit_JSOP_STRING()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_STRING()
 {
     frame.push(StringValue(script->getAtom(pc)));
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_SYMBOL()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_SYMBOL()
 {
     unsigned which = GET_UINT8(pc);
     JS::Symbol* sym = cx->runtime()->wellKnownSymbols->get(which);
     frame.push(SymbolValue(sym));
     return true;
 }
 
 typedef JSObject* (*DeepCloneObjectLiteralFn)(JSContext*, HandleObject, NewObjectKind);
 static const VMFunction DeepCloneObjectLiteralInfo =
     FunctionInfo<DeepCloneObjectLiteralFn>(DeepCloneObjectLiteral, "DeepCloneObjectLiteral");
 
-bool
-BaselineCompiler::emit_JSOP_OBJECT()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_OBJECT()
 {
     if (cx->realm()->creationOptions().cloneSingletons()) {
         RootedObject obj(cx, script->getObject(GET_UINT32_INDEX(pc)));
         if (!obj) {
             return false;
         }
 
         prepareVMCall();
@@ -1902,18 +1967,19 @@ BaselineCompiler::emit_JSOP_OBJECT()
         return true;
     }
 
     cx->realm()->behaviors().setSingletonsAsValues();
     frame.push(ObjectValue(*script->getObject(pc)));
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_CALLSITEOBJ()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_CALLSITEOBJ()
 {
     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)) {
@@ -1923,18 +1989,19 @@ BaselineCompiler::emit_JSOP_CALLSITEOBJ(
     frame.push(ObjectValue(*cso));
     return true;
 }
 
 typedef JSObject* (*CloneRegExpObjectFn)(JSContext*, Handle<RegExpObject*>);
 static const VMFunction CloneRegExpObjectInfo =
     FunctionInfo<CloneRegExpObjectFn>(CloneRegExpObject, "CloneRegExpObject");
 
-bool
-BaselineCompiler::emit_JSOP_REGEXP()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_REGEXP()
 {
     RootedObject reObj(cx, script->getRegExp(pc));
 
     prepareVMCall();
     pushArg(ImmGCPtr(reObj));
     if (!callVM(CloneRegExpObjectInfo)) {
         return false;
     }
@@ -1943,18 +2010,19 @@ BaselineCompiler::emit_JSOP_REGEXP()
     masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, R0);
     frame.push(R0);
     return true;
 }
 
 typedef JSObject* (*LambdaFn)(JSContext*, HandleFunction, HandleObject);
 static const VMFunction LambdaInfo = FunctionInfo<LambdaFn>(js::Lambda, "Lambda");
 
-bool
-BaselineCompiler::emit_JSOP_LAMBDA()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_LAMBDA()
 {
     RootedFunction fun(cx, script->getFunction(GET_UINT32_INDEX(pc)));
 
     prepareVMCall();
     masm.loadPtr(frame.addressOfEnvironmentChain(), R0.scratchReg());
 
     pushArg(R0.scratchReg());
     pushArg(ImmGCPtr(fun));
@@ -1968,18 +2036,19 @@ BaselineCompiler::emit_JSOP_LAMBDA()
     frame.push(R0);
     return true;
 }
 
 typedef JSObject* (*LambdaArrowFn)(JSContext*, HandleFunction, HandleObject, HandleValue);
 static const VMFunction LambdaArrowInfo =
     FunctionInfo<LambdaArrowFn>(js::LambdaArrow, "LambdaArrow");
 
-bool
-BaselineCompiler::emit_JSOP_LAMBDA_ARROW()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_LAMBDA_ARROW()
 {
     // Keep pushed newTarget in R0.
     frame.popRegsAndSync(1);
 
     RootedFunction fun(cx, script->getFunction(GET_UINT32_INDEX(pc)));
 
     prepareVMCall();
     masm.loadPtr(frame.addressOfEnvironmentChain(), R2.scratchReg());
@@ -1997,18 +2066,19 @@ BaselineCompiler::emit_JSOP_LAMBDA_ARROW
     frame.push(R0);
     return true;
 }
 
 typedef bool (*SetFunNameFn)(JSContext*, HandleFunction, HandleValue, FunctionPrefixKind);
 static const VMFunction SetFunNameInfo =
     FunctionInfo<SetFunNameFn>(js::SetFunctionNameIfNoOwnName, "SetFunName");
 
-bool
-BaselineCompiler::emit_JSOP_SETFUNNAME()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_SETFUNNAME()
 {
     frame.popRegsAndSync(2);
 
     frame.push(R0);
     frame.syncStack(0);
 
     FunctionPrefixKind prefixKind = FunctionPrefixKind(GET_UINT8(pc));
     masm.unboxObject(R0, R0.scratchReg());
@@ -2016,19 +2086,20 @@ BaselineCompiler::emit_JSOP_SETFUNNAME()
     prepareVMCall();
 
     pushArg(Imm32(int32_t(prefixKind)));
     pushArg(R1);
     pushArg(R0.scratchReg());
     return callVM(SetFunNameInfo);
 }
 
+template <typename Handler>
 void
-BaselineCompiler::storeValue(const StackValue* source, const Address& dest,
-                             const ValueOperand& scratch)
+BaselineCodeGen<Handler>::storeValue(const StackValue* source, const Address& dest,
+                                     const ValueOperand& scratch)
 {
     switch (source->kind()) {
       case StackValue::Constant:
         masm.storeValue(source->constant(), dest);
         break;
       case StackValue::Register:
         masm.storeValue(source->reg(), dest);
         break;
@@ -2053,236 +2124,266 @@ BaselineCompiler::storeValue(const Stack
         masm.loadValue(frame.addressOfStackValue(source), scratch);
         masm.storeValue(scratch, dest);
         break;
       default:
         MOZ_CRASH("Invalid kind");
     }
 }
 
-bool
-BaselineCompiler::emit_JSOP_BITOR()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_BITOR()
 {
     return emitBinaryArith();
 }
 
-bool
-BaselineCompiler::emit_JSOP_BITXOR()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_BITXOR()
 {
     return emitBinaryArith();
 }
 
-bool
-BaselineCompiler::emit_JSOP_BITAND()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_BITAND()
 {
     return emitBinaryArith();
 }
 
-bool
-BaselineCompiler::emit_JSOP_LSH()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_LSH()
 {
     return emitBinaryArith();
 }
 
-bool
-BaselineCompiler::emit_JSOP_RSH()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_RSH()
 {
     return emitBinaryArith();
 }
 
-bool
-BaselineCompiler::emit_JSOP_URSH()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_URSH()
 {
     return emitBinaryArith();
 }
 
-bool
-BaselineCompiler::emit_JSOP_ADD()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_ADD()
 {
     return emitBinaryArith();
 }
 
-bool
-BaselineCompiler::emit_JSOP_SUB()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_SUB()
 {
     return emitBinaryArith();
 }
 
-bool
-BaselineCompiler::emit_JSOP_MUL()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_MUL()
 {
     return emitBinaryArith();
 }
 
-bool
-BaselineCompiler::emit_JSOP_DIV()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_DIV()
 {
     return emitBinaryArith();
 }
 
-bool
-BaselineCompiler::emit_JSOP_MOD()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_MOD()
 {
     return emitBinaryArith();
 }
 
-bool
-BaselineCompiler::emit_JSOP_POW()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_POW()
 {
     return emitBinaryArith();
 }
 
-bool
-BaselineCompiler::emitBinaryArith()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emitBinaryArith()
 {
     // Keep top JSStack value in R0 and R2
     frame.popRegsAndSync(2);
 
     // Call IC
     if (!emitNextIC()) {
         return false;
     }
 
     // Mark R0 as pushed stack value.
     frame.push(R0);
     return true;
 }
 
-bool
-BaselineCompiler::emitUnaryArith()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emitUnaryArith()
 {
     // Keep top stack value in R0.
     frame.popRegsAndSync(1);
 
     // Call IC
     if (!emitNextIC()) {
         return false;
     }
 
     // Mark R0 as pushed stack value.
     frame.push(R0);
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_BITNOT()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_BITNOT()
 {
     return emitUnaryArith();
 }
 
-bool
-BaselineCompiler::emit_JSOP_NEG()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_NEG()
 {
     return emitUnaryArith();
 }
 
-bool
-BaselineCompiler::emit_JSOP_LT()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_LT()
 {
     return emitCompare();
 }
 
-bool
-BaselineCompiler::emit_JSOP_LE()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_LE()
 {
     return emitCompare();
 }
 
-bool
-BaselineCompiler::emit_JSOP_GT()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_GT()
 {
     return emitCompare();
 }
 
-bool
-BaselineCompiler::emit_JSOP_GE()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_GE()
 {
     return emitCompare();
 }
 
-bool
-BaselineCompiler::emit_JSOP_EQ()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_EQ()
 {
     return emitCompare();
 }
 
-bool
-BaselineCompiler::emit_JSOP_NE()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_NE()
 {
     return emitCompare();
 }
 
-bool
-BaselineCompiler::emitCompare()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emitCompare()
 {
     // CODEGEN
 
     // Keep top JSStack value in R0 and R1.
     frame.popRegsAndSync(2);
 
     // Call IC.
     if (!emitNextIC()) {
         return false;
     }
 
     // Mark R0 as pushed stack value.
     frame.push(R0, JSVAL_TYPE_BOOLEAN);
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_STRICTEQ()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_STRICTEQ()
 {
     return emitCompare();
 }
 
-bool
-BaselineCompiler::emit_JSOP_STRICTNE()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_STRICTNE()
 {
     return emitCompare();
 }
 
-bool
-BaselineCompiler::emit_JSOP_CONDSWITCH()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_CONDSWITCH()
 {
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_CASE()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_CASE()
 {
     frame.popRegsAndSync(1);
 
     Label done;
     jsbytecode* target = pc + GET_JUMP_OFFSET(pc);
     masm.branchTestBooleanTruthy(/* branchIfTrue */ false, R0, &done);
     {
         // Pop the switch value if the case matches.
         masm.addToStackPtr(Imm32(sizeof(Value)));
         masm.jump(labelOf(target));
     }
     masm.bind(&done);
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_DEFAULT()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_DEFAULT()
 {
     frame.pop();
     return emit_JSOP_GOTO();
 }
 
-bool
-BaselineCompiler::emit_JSOP_LINENO()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_LINENO()
 {
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_NEWARRAY()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_NEWARRAY()
 {
     frame.syncStack(0);
 
     uint32_t length = GET_UINT32(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");
 
@@ -2296,18 +2397,19 @@ BaselineCompiler::emit_JSOP_NEWARRAY()
     frame.push(R0);
     return true;
 }
 
 typedef ArrayObject* (*NewArrayCopyOnWriteFn)(JSContext*, HandleArrayObject, gc::InitialHeap);
 const VMFunction jit::NewArrayCopyOnWriteInfo =
     FunctionInfo<NewArrayCopyOnWriteFn>(js::NewDenseCopyOnWriteArray, "NewDenseCopyOnWriteArray");
 
-bool
-BaselineCompiler::emit_JSOP_NEWARRAY_COPYONWRITE()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_NEWARRAY_COPYONWRITE()
 {
     RootedScript scriptRoot(cx, script);
     JSObject* obj = ObjectGroup::getOrFixupCopyOnWriteObject(cx, scriptRoot, pc);
     if (!obj) {
         return false;
     }
 
     prepareVMCall();
@@ -2320,18 +2422,19 @@ BaselineCompiler::emit_JSOP_NEWARRAY_COP
     }
 
     // Box and push return value.
     masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, R0);
     frame.push(R0);
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_INITELEM_ARRAY()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::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);
     MOZ_ASSERT(index <= INT32_MAX,
@@ -2345,44 +2448,47 @@ BaselineCompiler::emit_JSOP_INITELEM_ARR
         return false;
     }
 
     // Pop the rhs, so that the object is on the top of the stack.
     frame.pop();
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_NEWOBJECT()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_NEWOBJECT()
 {
     frame.syncStack(0);
 
     if (!emitNextIC()) {
         return false;
     }
 
     frame.push(R0);
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_NEWINIT()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_NEWINIT()
 {
     frame.syncStack(0);
 
     if (!emitNextIC()) {
         return false;
     }
 
     frame.push(R0);
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_INITELEM()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_INITELEM()
 {
     // Store RHS in the scratch slot.
     storeValue(frame.peek(-1), frame.addressOfScratchValue(), R2);
     frame.pop();
 
     // Keep object and index in R0 and R1.
     frame.popRegsAndSync(2);
 
@@ -2398,28 +2504,30 @@ BaselineCompiler::emit_JSOP_INITELEM()
         return false;
     }
 
     // Pop the rhs, so that the object is on the top of the stack.
     frame.pop();
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_INITHIDDENELEM()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_INITHIDDENELEM()
 {
     return emit_JSOP_INITELEM();
 }
 
 typedef bool (*MutateProtoFn)(JSContext* cx, HandlePlainObject obj, HandleValue newProto);
 static const VMFunction MutateProtoInfo =
     FunctionInfo<MutateProtoFn>(MutatePrototype, "MutatePrototype");
 
-bool
-BaselineCompiler::emit_JSOP_MUTATEPROTO()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_MUTATEPROTO()
 {
     // Keep values on the stack for the decompiler.
     frame.syncStack(0);
 
     masm.unboxObject(frame.addressOfStackValue(frame.peek(-2)), R0.scratchReg());
     masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R1);
 
     prepareVMCall();
@@ -2430,64 +2538,69 @@ BaselineCompiler::emit_JSOP_MUTATEPROTO(
     if (!callVM(MutateProtoInfo)) {
         return false;
     }
 
     frame.pop();
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_INITPROP()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_INITPROP()
 {
     // Load lhs in R0, rhs in R1.
     frame.syncStack(0);
     masm.loadValue(frame.addressOfStackValue(frame.peek(-2)), R0);
     masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R1);
 
     // Call IC.
     if (!emitNextIC()) {
         return false;
     }
 
     // Leave the object on the stack.
     frame.pop();
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_INITLOCKEDPROP()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_INITLOCKEDPROP()
 {
     return emit_JSOP_INITPROP();
 }
 
-bool
-BaselineCompiler::emit_JSOP_INITHIDDENPROP()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_INITHIDDENPROP()
 {
     return emit_JSOP_INITPROP();
 }
 
-bool
-BaselineCompiler::emit_JSOP_GETELEM()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_GETELEM()
 {
     // Keep top two stack values in R0 and R1.
     frame.popRegsAndSync(2);
 
     // Call IC.
     if (!emitNextIC()) {
         return false;
     }
 
     // Mark R0 as pushed stack value.
     frame.push(R0);
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_GETELEM_SUPER()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_GETELEM_SUPER()
 {
     // Store obj in the scratch slot.
     storeValue(frame.peek(-1), frame.addressOfScratchValue(), R2);
     frame.pop();
 
     // Keep receiver and index in R0 and R1.
     frame.popRegsAndSync(2);
 
@@ -2498,24 +2611,26 @@ BaselineCompiler::emit_JSOP_GETELEM_SUPE
         return false;
     }
 
     frame.pop(); // This value is also popped in InitFromBailout.
     frame.push(R0);
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_CALLELEM()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_CALLELEM()
 {
     return emit_JSOP_GETELEM();
 }
 
-bool
-BaselineCompiler::emit_JSOP_SETELEM()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_SETELEM()
 {
     // Store RHS in the scratch slot.
     storeValue(frame.peek(-1), frame.addressOfScratchValue(), R2);
     frame.pop();
 
     // Keep object and index in R0 and R1.
     frame.popRegsAndSync(2);
 
@@ -2525,24 +2640,26 @@ BaselineCompiler::emit_JSOP_SETELEM()
     // Call IC.
     if (!emitNextIC()) {
         return false;
     }
 
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_STRICTSETELEM()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_STRICTSETELEM()
 {
     return emit_JSOP_SETELEM();
 }
 
-bool
-BaselineCompiler::emit_JSOP_SETELEM_SUPER()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_SETELEM_SUPER()
 {
     bool strict = IsCheckStrictOp(JSOp(*pc));
 
     // Incoming stack is |receiver, propval, obj, rval|. We need to shuffle
     // stack to leave rval when operation is complete.
 
     // Pop rval into R0, then load receiver into R1 and replace with rval.
     frame.popRegsAndSync(1);
@@ -2562,30 +2679,32 @@ BaselineCompiler::emit_JSOP_SETELEM_SUPE
     if (!callVM(SetObjectElementInfo)) {
         return false;
     }
 
     frame.popn(2);
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_STRICTSETELEM_SUPER()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_STRICTSETELEM_SUPER()
 {
     return emit_JSOP_SETELEM_SUPER();
 }
 
 typedef bool (*DeleteElementFn)(JSContext*, HandleValue, HandleValue, bool*);
 static const VMFunction DeleteElementStrictInfo
     = FunctionInfo<DeleteElementFn>(DeleteElementJit<true>, "DeleteElementStrict");
 static const VMFunction DeleteElementNonStrictInfo
     = FunctionInfo<DeleteElementFn>(DeleteElementJit<false>, "DeleteElementNonStrict");
 
-bool
-BaselineCompiler::emit_JSOP_DELELEM()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_DELELEM()
 {
     // Keep values on the stack for the decompiler.
     frame.syncStack(0);
     masm.loadValue(frame.addressOfStackValue(frame.peek(-2)), R0);
     masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R1);
 
     prepareVMCall();
 
@@ -2598,50 +2717,54 @@ BaselineCompiler::emit_JSOP_DELELEM()
     }
 
     masm.boxNonDouble(JSVAL_TYPE_BOOLEAN, ReturnReg, R1);
     frame.popn(2);
     frame.push(R1);
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_STRICTDELELEM()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_STRICTDELELEM()
 {
     return emit_JSOP_DELELEM();
 }
 
-bool
-BaselineCompiler::emit_JSOP_IN()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_IN()
 {
     frame.popRegsAndSync(2);
 
     if (!emitNextIC()) {
         return false;
     }
 
     frame.push(R0);
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_HASOWN()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_HASOWN()
 {
     frame.popRegsAndSync(2);
 
     if (!emitNextIC()) {
         return false;
     }
 
     frame.push(R0);
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_GETGNAME()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_GETGNAME()
 {
     if (script->hasNonSyntacticScope()) {
         return emit_JSOP_GETNAME();
     }
 
     RootedPropertyName name(cx, script->getName(pc));
 
     // These names are non-configurable on the global and cannot be shadowed.
@@ -2667,18 +2790,19 @@ BaselineCompiler::emit_JSOP_GETGNAME()
         return false;
     }
 
     // Mark R0 as pushed stack value.
     frame.push(R0);
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_BINDGNAME()
+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)) {
@@ -2703,89 +2827,97 @@ BaselineCompiler::emit_JSOP_BINDGNAME()
     }
 
     return emit_JSOP_BINDNAME();
 }
 
 typedef JSObject* (*BindVarFn)(JSContext*, HandleObject);
 static const VMFunction BindVarInfo = FunctionInfo<BindVarFn>(jit::BindVar, "BindVar");
 
-bool
-BaselineCompiler::emit_JSOP_BINDVAR()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_BINDVAR()
 {
     frame.syncStack(0);
     masm.loadPtr(frame.addressOfEnvironmentChain(), R0.scratchReg());
 
     prepareVMCall();
     pushArg(R0.scratchReg());
 
     if (!callVM(BindVarInfo)) {
         return false;
     }
 
     masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, R0);
     frame.push(R0);
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_SETPROP()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_SETPROP()
 {
     // Keep lhs in R0, rhs in R1.
     frame.popRegsAndSync(2);
 
     // Keep RHS on the stack.
     frame.push(R1);
     frame.syncStack(0);
 
     // Call IC.
     if (!emitNextIC()) {
         return false;
     }
 
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_STRICTSETPROP()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_STRICTSETPROP()
 {
     return emit_JSOP_SETPROP();
 }
 
-bool
-BaselineCompiler::emit_JSOP_SETNAME()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_SETNAME()
 {
     return emit_JSOP_SETPROP();
 }
 
-bool
-BaselineCompiler::emit_JSOP_STRICTSETNAME()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_STRICTSETNAME()
 {
     return emit_JSOP_SETPROP();
 }
 
-bool
-BaselineCompiler::emit_JSOP_SETGNAME()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_SETGNAME()
 {
     return emit_JSOP_SETPROP();
 }
 
-bool
-BaselineCompiler::emit_JSOP_STRICTSETGNAME()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_STRICTSETGNAME()
 {
     return emit_JSOP_SETPROP();
 }
 
 typedef bool (*SetPropertySuperFn)(JSContext*, HandleObject, HandleValue,
                                    HandlePropertyName, HandleValue, bool);
 static const VMFunction SetPropertySuperInfo =
     FunctionInfo<SetPropertySuperFn>(js::SetPropertySuper, "SetPropertySuper");
 
-bool
-BaselineCompiler::emit_JSOP_SETPROP_SUPER()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_SETPROP_SUPER()
 {
     bool strict = IsCheckStrictOp(JSOp(*pc));
 
     // Incoming stack is |receiver, obj, rval|. We need to shuffle stack to
     // leave rval when operation is complete.
 
     // Pop rval into R0, then load receiver into R1 and replace with rval.
     frame.popRegsAndSync(1);
@@ -2804,58 +2936,64 @@ BaselineCompiler::emit_JSOP_SETPROP_SUPE
     if (!callVM(SetPropertySuperInfo)) {
         return false;
     }
 
     frame.pop();
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_STRICTSETPROP_SUPER()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_STRICTSETPROP_SUPER()
 {
     return emit_JSOP_SETPROP_SUPER();
 }
 
-bool
-BaselineCompiler::emit_JSOP_GETPROP()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_GETPROP()
 {
     // Keep object in R0.
     frame.popRegsAndSync(1);
 
     // Call IC.
     if (!emitNextIC()) {
         return false;
     }
 
     // Mark R0 as pushed stack value.
     frame.push(R0);
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_CALLPROP()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_CALLPROP()
 {
     return emit_JSOP_GETPROP();
 }
 
-bool
-BaselineCompiler::emit_JSOP_LENGTH()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_LENGTH()
 {
     return emit_JSOP_GETPROP();
 }
 
-bool
-BaselineCompiler::emit_JSOP_GETBOUNDNAME()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_GETBOUNDNAME()
 {
     return emit_JSOP_GETPROP();
 }
 
-bool
-BaselineCompiler::emit_JSOP_GETPROP_SUPER()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_GETPROP_SUPER()
 {
     // Receiver -> R1, Object -> R0
     frame.popRegsAndSync(1);
     masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R1);
     frame.pop();
 
     if (!emitNextIC()) {
         return false;
@@ -2867,18 +3005,19 @@ BaselineCompiler::emit_JSOP_GETPROP_SUPE
 
 
 typedef bool (*DeletePropertyFn)(JSContext*, HandleValue, HandlePropertyName, bool*);
 static const VMFunction DeletePropertyStrictInfo =
     FunctionInfo<DeletePropertyFn>(DeletePropertyJit<true>, "DeletePropertyStrict");
 static const VMFunction DeletePropertyNonStrictInfo =
     FunctionInfo<DeletePropertyFn>(DeletePropertyJit<false>, "DeletePropertyNonStrict");
 
-bool
-BaselineCompiler::emit_JSOP_DELPROP()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_DELPROP()
 {
     // Keep value on the stack for the decompiler.
     frame.syncStack(0);
     masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R0);
 
     prepareVMCall();
 
     pushArg(ImmGCPtr(script->getName(pc)));
@@ -2890,56 +3029,61 @@ BaselineCompiler::emit_JSOP_DELPROP()
     }
 
     masm.boxNonDouble(JSVAL_TYPE_BOOLEAN, ReturnReg, R1);
     frame.pop();
     frame.push(R1);
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_STRICTDELPROP()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_STRICTDELPROP()
 {
     return emit_JSOP_DELPROP();
 }
 
+template <typename Handler>
 void
-BaselineCompiler::getEnvironmentCoordinateObject(Register reg)
+BaselineCodeGen<Handler>::getEnvironmentCoordinateObject(Register reg)
 {
     EnvironmentCoordinate ec(pc);
 
     masm.loadPtr(frame.addressOfEnvironmentChain(), reg);
     for (unsigned i = ec.hops(); i; i--) {
         masm.unboxObject(Address(reg, EnvironmentObject::offsetOfEnclosingEnvironment()), reg);
     }
 }
 
+template <typename Handler>
 Address
-BaselineCompiler::getEnvironmentCoordinateAddressFromObject(Register objReg, Register reg)
+BaselineCodeGen<Handler>::getEnvironmentCoordinateAddressFromObject(Register objReg, Register reg)
 {
     EnvironmentCoordinate ec(pc);
     Shape* shape = EnvironmentCoordinateToEnvironmentShape(script, 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 <typename Handler>
 Address
-BaselineCompiler::getEnvironmentCoordinateAddress(Register reg)
+BaselineCodeGen<Handler>::getEnvironmentCoordinateAddress(Register reg)
 {
     getEnvironmentCoordinateObject(reg);
     return getEnvironmentCoordinateAddressFromObject(reg, reg);
 }
 
-bool
-BaselineCompiler::emit_JSOP_GETALIASEDVAR()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_GETALIASEDVAR()
 {
     frame.syncStack(0);
 
     Address address = getEnvironmentCoordinateAddress(R0.scratchReg());
     masm.loadValue(address, R0);
 
     if (ionCompileable_) {
         // No need to monitor types if we know Ion can't compile this script.
@@ -2947,18 +3091,19 @@ BaselineCompiler::emit_JSOP_GETALIASEDVA
             return false;
         }
     }
 
     frame.push(R0);
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_SETALIASEDVAR()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_SETALIASEDVAR()
 {
     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);
@@ -2995,35 +3140,37 @@ BaselineCompiler::emit_JSOP_SETALIASEDVA
     masm.branchValueIsNurseryCell(Assembler::NotEqual, R0, temp, &skipBarrier);
 
     masm.call(&postBarrierSlot_); // Won't clobber R0
 
     masm.bind(&skipBarrier);
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_GETNAME()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_GETNAME()
 {
     frame.syncStack(0);
 
     masm.loadPtr(frame.addressOfEnvironmentChain(), R0.scratchReg());
 
     // Call IC.
     if (!emitNextIC()) {
         return false;
     }
 
     // Mark R0 as pushed stack value.
     frame.push(R0);
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_BINDNAME()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_BINDNAME()
 {
     frame.syncStack(0);
 
     if (*pc == JSOP_BINDGNAME && !script->hasNonSyntacticScope()) {
         masm.movePtr(ImmGCPtr(&script->global().lexicalEnvironment()), R0.scratchReg());
     } else {
         masm.loadPtr(frame.addressOfEnvironmentChain(), R0.scratchReg());
     }
@@ -3038,18 +3185,19 @@ BaselineCompiler::emit_JSOP_BINDNAME()
     return true;
 }
 
 typedef bool (*DeleteNameFn)(JSContext*, HandlePropertyName, HandleObject,
                              MutableHandleValue);
 static const VMFunction DeleteNameInfo =
     FunctionInfo<DeleteNameFn>(DeleteNameOperation, "DeleteNameOperation");
 
-bool
-BaselineCompiler::emit_JSOP_DELNAME()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_DELNAME()
 {
     frame.syncStack(0);
     masm.loadPtr(frame.addressOfEnvironmentChain(), R0.scratchReg());
 
     prepareVMCall();
 
     pushArg(R0.scratchReg());
     pushArg(ImmGCPtr(script->getName(pc)));
@@ -3057,18 +3205,19 @@ BaselineCompiler::emit_JSOP_DELNAME()
     if (!callVM(DeleteNameInfo)) {
         return false;
     }
 
     frame.push(R0);
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_GETIMPORT()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_GETIMPORT()
 {
     ModuleEnvironmentObject* env = GetModuleEnvironmentForScript(script);
     MOZ_ASSERT(env);
 
     ModuleEnvironmentObject* targetEnv;
     Shape* shape;
     MOZ_ALWAYS_TRUE(env->lookupImport(NameToId(script->getName(pc)), &targetEnv, &shape));
 
@@ -3100,34 +3249,36 @@ BaselineCompiler::emit_JSOP_GETIMPORT()
             return false;
         }
     }
 
     frame.push(R0);
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_GETINTRINSIC()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_GETINTRINSIC()
 {
     frame.syncStack(0);
 
     if (!emitNextIC()) {
         return false;
     }
 
     frame.push(R0);
     return true;
 }
 
 typedef bool (*DefVarFn)(JSContext*, HandlePropertyName, unsigned, HandleObject);
 static const VMFunction DefVarInfo = FunctionInfo<DefVarFn>(DefVar, "DefVar");
 
-bool
-BaselineCompiler::emit_JSOP_DEFVAR()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_DEFVAR()
 {
     frame.syncStack(0);
 
     unsigned attrs = JSPROP_ENUMERATE;
     if (!script->isForEval()) {
         attrs |= JSPROP_PERMANENT;
     }
     MOZ_ASSERT(attrs <= UINT32_MAX);
@@ -3141,24 +3292,26 @@ BaselineCompiler::emit_JSOP_DEFVAR()
     pushArg(ImmGCPtr(script->getName(pc)));
 
     return callVM(DefVarInfo);
 }
 
 typedef bool (*DefLexicalFn)(JSContext*, HandlePropertyName, unsigned, HandleObject);
 static const VMFunction DefLexicalInfo = FunctionInfo<DefLexicalFn>(DefLexical, "DefLexical");
 
-bool
-BaselineCompiler::emit_JSOP_DEFCONST()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_DEFCONST()
 {
     return emit_JSOP_DEFLET();
 }
 
-bool
-BaselineCompiler::emit_JSOP_DEFLET()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_DEFLET()
 {
     frame.syncStack(0);
 
     unsigned attrs = JSPROP_ENUMERATE | JSPROP_PERMANENT;
     if (*pc == JSOP_DEFCONST) {
         attrs |= JSPROP_READONLY;
     }
     MOZ_ASSERT(attrs <= UINT32_MAX);
@@ -3173,18 +3326,19 @@ BaselineCompiler::emit_JSOP_DEFLET()
 
     return callVM(DefLexicalInfo);
 }
 
 typedef bool (*DefFunOperationFn)(JSContext*, HandleScript, HandleObject, HandleFunction);
 static const VMFunction DefFunOperationInfo =
     FunctionInfo<DefFunOperationFn>(DefFunOperation, "DefFunOperation");
 
-bool
-BaselineCompiler::emit_JSOP_DEFFUN()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_DEFFUN()
 {
     frame.popRegsAndSync(1);
     masm.unboxObject(R0, R0.scratchReg());
     masm.loadPtr(frame.addressOfEnvironmentChain(), R1.scratchReg());
 
     prepareVMCall();
 
     pushArg(R0.scratchReg());
@@ -3195,18 +3349,19 @@ BaselineCompiler::emit_JSOP_DEFFUN()
 }
 
 typedef bool (*InitPropGetterSetterFn)(JSContext*, jsbytecode*, HandleObject, HandlePropertyName,
                                        HandleObject);
 static const VMFunction InitPropGetterSetterInfo =
     FunctionInfo<InitPropGetterSetterFn>(InitGetterSetterOperation,
                                          "InitPropGetterSetterOperation");
 
-bool
-BaselineCompiler::emitInitPropGetterSetter()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emitInitPropGetterSetter()
 {
     MOZ_ASSERT(JSOp(*pc) == JSOP_INITPROP_GETTER ||
                JSOp(*pc) == JSOP_INITHIDDENPROP_GETTER ||
                JSOp(*pc) == JSOP_INITPROP_SETTER ||
                JSOp(*pc) == JSOP_INITHIDDENPROP_SETTER);
 
     // Keep values on the stack for the decompiler.
     frame.syncStack(0);
@@ -3224,48 +3379,53 @@ BaselineCompiler::emitInitPropGetterSett
     if (!callVM(InitPropGetterSetterInfo)) {
         return false;
     }
 
     frame.pop();
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_INITPROP_GETTER()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_INITPROP_GETTER()
 {
     return emitInitPropGetterSetter();
 }
 
-bool
-BaselineCompiler::emit_JSOP_INITHIDDENPROP_GETTER()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_INITHIDDENPROP_GETTER()
 {
     return emitInitPropGetterSetter();
 }
 
-bool
-BaselineCompiler::emit_JSOP_INITPROP_SETTER()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_INITPROP_SETTER()
 {
     return emitInitPropGetterSetter();
 }
 
-bool
-BaselineCompiler::emit_JSOP_INITHIDDENPROP_SETTER()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_INITHIDDENPROP_SETTER()
 {
     return emitInitPropGetterSetter();
 }
 
 typedef bool (*InitElemGetterSetterFn)(JSContext*, jsbytecode*, HandleObject, HandleValue,
                                        HandleObject);
 static const VMFunction InitElemGetterSetterInfo =
     FunctionInfo<InitElemGetterSetterFn>(InitGetterSetterOperation,
                                          "InitElemGetterSetterOperation");
 
-bool
-BaselineCompiler::emitInitElemGetterSetter()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emitInitElemGetterSetter()
 {
     MOZ_ASSERT(JSOp(*pc) == JSOP_INITELEM_GETTER ||
                JSOp(*pc) == JSOP_INITHIDDENELEM_GETTER ||
                JSOp(*pc) == JSOP_INITELEM_SETTER ||
                JSOp(*pc) == JSOP_INITHIDDENELEM_SETTER);
 
     // Load index and value in R0 and R1, but keep values on the stack for the
     // decompiler.
@@ -3284,42 +3444,47 @@ BaselineCompiler::emitInitElemGetterSett
     if (!callVM(InitElemGetterSetterInfo)) {
         return false;
     }
 
     frame.popn(2);
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_INITELEM_GETTER()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_INITELEM_GETTER()
 {
     return emitInitElemGetterSetter();
 }
 
-bool
-BaselineCompiler::emit_JSOP_INITHIDDENELEM_GETTER()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_INITHIDDENELEM_GETTER()
 {
     return emitInitElemGetterSetter();
 }
 
-bool
-BaselineCompiler::emit_JSOP_INITELEM_SETTER()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_INITELEM_SETTER()
 {
     return emitInitElemGetterSetter();
 }
 
-bool
-BaselineCompiler::emit_JSOP_INITHIDDENELEM_SETTER()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_INITHIDDENELEM_SETTER()
 {
     return emitInitElemGetterSetter();
 }
 
-bool
-BaselineCompiler::emit_JSOP_INITELEM_INC()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_INITELEM_INC()
 {
     // Keep the object and rhs on the stack.
     frame.syncStack(0);
 
     // Load object in R0, index in R1.
     masm.loadValue(frame.addressOfStackValue(frame.peek(-3)), R0);
     masm.loadValue(frame.addressOfStackValue(frame.peek(-2)), R1);
 
@@ -3338,37 +3503,40 @@ BaselineCompiler::emit_JSOP_INITELEM_INC
     masm.branchTestInt32(Assembler::Equal, indexAddr, &isInt32);
     masm.assumeUnreachable("INITELEM_INC index must be Int32");
     masm.bind(&isInt32);
 #endif
     masm.incrementInt32Value(indexAddr);
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_GETLOCAL()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_GETLOCAL()
 {
     frame.pushLocal(GET_LOCALNO(pc));
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_SETLOCAL()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::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);
     storeValue(frame.peek(-1), frame.addressOfLocal(local), R0);
     return true;
 }
 
-bool
-BaselineCompiler::emitFormalArgAccess(uint32_t arg, bool get)
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emitFormalArgAccess(uint32_t arg, bool get)
 {
     // Fast path: the script does not use |arguments| or formals don't
     // alias the arguments object.
     if (!script->argumentsAliasesFormals()) {
         if (get) {
             frame.pushArg(arg);
         } else {
             // See the comment in emit_JSOP_SETLOCAL.
@@ -3431,39 +3599,42 @@ BaselineCompiler::emitFormalArgAccess(ui
 
         masm.bind(&skipBarrier);
     }
 
     masm.bind(&done);
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_GETARG()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_GETARG()
 {
     uint32_t arg = GET_ARGNO(pc);
     return emitFormalArgAccess(arg, /* get = */ true);
 }
 
-bool
-BaselineCompiler::emit_JSOP_SETARG()
+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);
 }
 
-bool
-BaselineCompiler::emit_JSOP_NEWTARGET()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_NEWTARGET()
 {
     if (script->isForEval()) {
         frame.pushEvalNewTarget();
         return true;
     }
 
     MOZ_ASSERT(function());
     frame.syncStack(0);
@@ -3518,104 +3689,116 @@ BaselineCompiler::emit_JSOP_NEWTARGET()
     return true;
 }
 
 typedef bool (*ThrowRuntimeLexicalErrorFn)(JSContext* cx, unsigned);
 static const VMFunction ThrowRuntimeLexicalErrorInfo =
     FunctionInfo<ThrowRuntimeLexicalErrorFn>(jit::ThrowRuntimeLexicalError,
                                              "ThrowRuntimeLexicalError");
 
-bool
-BaselineCompiler::emitThrowConstAssignment()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emitThrowConstAssignment()
 {
     prepareVMCall();
     pushArg(Imm32(JSMSG_BAD_CONST_ASSIGN));
     return callVM(ThrowRuntimeLexicalErrorInfo);
 }
 
-bool
-BaselineCompiler::emit_JSOP_THROWSETCONST()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_THROWSETCONST()
 {
     return emitThrowConstAssignment();
 }
 
-bool
-BaselineCompiler::emit_JSOP_THROWSETALIASEDCONST()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_THROWSETALIASEDCONST()
 {
     return emitThrowConstAssignment();
 }
 
-bool
-BaselineCompiler::emit_JSOP_THROWSETCALLEE()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_THROWSETCALLEE()
 {
     return emitThrowConstAssignment();
 }
 
-bool
-BaselineCompiler::emitUninitializedLexicalCheck(const ValueOperand& val)
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emitUninitializedLexicalCheck(const ValueOperand& val)
 {
     Label done;
     masm.branchTestMagicValue(Assembler::NotEqual, val, JS_UNINITIALIZED_LEXICAL, &done);
 
     prepareVMCall();
     pushArg(Imm32(JSMSG_UNINITIALIZED_LEXICAL));
     if (!callVM(ThrowRuntimeLexicalErrorInfo)) {
         return false;
     }
 
     masm.bind(&done);
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_CHECKLEXICAL()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_CHECKLEXICAL()
 {
     frame.syncStack(0);
     masm.loadValue(frame.addressOfLocal(GET_LOCALNO(pc)), R0);
     return emitUninitializedLexicalCheck(R0);
 }
 
-bool
-BaselineCompiler::emit_JSOP_INITLEXICAL()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_INITLEXICAL()
 {
     return emit_JSOP_SETLOCAL();
 }
 
-bool
-BaselineCompiler::emit_JSOP_INITGLEXICAL()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_INITGLEXICAL()
 {
     frame.popRegsAndSync(1);
     frame.push(ObjectValue(script->global().lexicalEnvironment()));
     frame.push(R0);
     return emit_JSOP_SETPROP();
 }
 
-bool
-BaselineCompiler::emit_JSOP_CHECKALIASEDLEXICAL()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_CHECKALIASEDLEXICAL()
 {
     frame.syncStack(0);
     masm.loadValue(getEnvironmentCoordinateAddress(R0.scratchReg()), R0);
     return emitUninitializedLexicalCheck(R0);
 }
 
-bool
-BaselineCompiler::emit_JSOP_INITALIASEDLEXICAL()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_INITALIASEDLEXICAL()
 {
     return emit_JSOP_SETALIASEDVAR();
 }
 
-bool
-BaselineCompiler::emit_JSOP_UNINITIALIZED()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_UNINITIALIZED()
 {
     frame.push(MagicValue(JS_UNINITIALIZED_LEXICAL));
     return true;
 }
 
-bool
-BaselineCompiler::emitCall()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emitCall()
 {
     MOZ_ASSERT(IsCallPC(pc));
 
     bool construct = JSOp(*pc) == JSOP_NEW || JSOp(*pc) == JSOP_SUPERCALL;
     uint32_t argc = GET_ARGC(pc);
 
     frame.syncStack(0);
     masm.move32(Imm32(argc), R0.scratchReg());
@@ -3626,18 +3809,19 @@ BaselineCompiler::emitCall()
     }
 
     // Update FrameInfo.
     frame.popn(2 + argc + construct);
     frame.push(R0);
     return true;
 }
 
-bool
-BaselineCompiler::emitSpreadCall()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emitSpreadCall()
 {
     MOZ_ASSERT(IsCallPC(pc));
 
     frame.syncStack(0);
     masm.move32(Imm32(1), R0.scratchReg());
 
     // Call IC
     bool construct = JSOp(*pc) == JSOP_SPREADNEW || JSOp(*pc) == JSOP_SPREADSUPERCALL;
@@ -3646,106 +3830,121 @@ BaselineCompiler::emitSpreadCall()
     }
 
     // Update FrameInfo.
     frame.popn(3 + construct);
     frame.push(R0);
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_CALL()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_CALL()
 {
     return emitCall();
 }
 
-bool
-BaselineCompiler::emit_JSOP_CALL_IGNORES_RV()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_CALL_IGNORES_RV()
 {
     return emitCall();
 }
 
-bool
-BaselineCompiler::emit_JSOP_CALLITER()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_CALLITER()
 {
     return emitCall();
 }
 
-bool
-BaselineCompiler::emit_JSOP_NEW()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_NEW()
 {
     return emitCall();
 }
 
-bool
-BaselineCompiler::emit_JSOP_SUPERCALL()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_SUPERCALL()
 {
     return emitCall();
 }
 
-bool
-BaselineCompiler::emit_JSOP_FUNCALL()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_FUNCALL()
 {
     return emitCall();
 }
 
-bool
-BaselineCompiler::emit_JSOP_FUNAPPLY()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_FUNAPPLY()
 {
     return emitCall();
 }
 
-bool
-BaselineCompiler::emit_JSOP_EVAL()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_EVAL()
 {
     return emitCall();
 }
 
-bool
-BaselineCompiler::emit_JSOP_STRICTEVAL()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_STRICTEVAL()
 {
     return emitCall();
 }
 
-bool
-BaselineCompiler::emit_JSOP_SPREADCALL()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_SPREADCALL()
 {
     return emitSpreadCall();
 }
 
-bool
-BaselineCompiler::emit_JSOP_SPREADNEW()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_SPREADNEW()
 {
     return emitSpreadCall();
 }
 
-bool
-BaselineCompiler::emit_JSOP_SPREADSUPERCALL()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_SPREADSUPERCALL()
 {
     return emitSpreadCall();
 }
 
-bool
-BaselineCompiler::emit_JSOP_SPREADEVAL()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_SPREADEVAL()
 {
     return emitSpreadCall();
 }
 
-bool
-BaselineCompiler::emit_JSOP_STRICTSPREADEVAL()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_STRICTSPREADEVAL()
 {
     return emitSpreadCall();
 }
 
 typedef bool (*OptimizeSpreadCallFn)(JSContext*, HandleValue, bool*);
 static const VMFunction OptimizeSpreadCallInfo =
     FunctionInfo<OptimizeSpreadCallFn>(OptimizeSpreadCall, "OptimizeSpreadCall");
 
-bool
-BaselineCompiler::emit_JSOP_OPTIMIZE_SPREADCALL()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_OPTIMIZE_SPREADCALL()
 {
     frame.syncStack(0);
     masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R0);
 
     prepareVMCall();
     pushArg(R0);
 
     if (!callVM(OptimizeSpreadCallInfo)) {
@@ -3757,18 +3956,19 @@ BaselineCompiler::emit_JSOP_OPTIMIZE_SPR
     return true;
 }
 
 typedef bool (*ImplicitThisFn)(JSContext*, HandleObject, HandlePropertyName,
                                MutableHandleValue);
 const VMFunction jit::ImplicitThisInfo =
     FunctionInfo<ImplicitThisFn>(ImplicitThisOperation, "ImplicitThisOperation");
 
-bool
-BaselineCompiler::emit_JSOP_IMPLICITTHIS()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_IMPLICITTHIS()
 {
     frame.syncStack(0);
     masm.loadPtr(frame.addressOfEnvironmentChain(), R0.scratchReg());
 
     prepareVMCall();
 
     pushArg(ImmGCPtr(script->getName(pc)));
     pushArg(R0.scratchReg());
@@ -3776,112 +3976,121 @@ BaselineCompiler::emit_JSOP_IMPLICITTHIS
     if (!callVM(ImplicitThisInfo)) {
         return false;
     }
 
     frame.push(R0);
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_GIMPLICITTHIS()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_GIMPLICITTHIS()
 {
     if (!script->hasNonSyntacticScope()) {
         frame.push(UndefinedValue());
         return true;
     }
 
     return emit_JSOP_IMPLICITTHIS();
 }
 
-bool
-BaselineCompiler::emit_JSOP_INSTANCEOF()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_INSTANCEOF()
 {
     frame.popRegsAndSync(2);
 
     if (!emitNextIC()) {
         return false;
     }
 
     frame.push(R0);
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_TYPEOF()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_TYPEOF()
 {
     frame.popRegsAndSync(1);
 
     if (!emitNextIC()) {
         return false;
     }
 
     frame.push(R0);
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_TYPEOFEXPR()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_TYPEOFEXPR()
 {
     return emit_JSOP_TYPEOF();
 }
 
 typedef bool (*ThrowMsgFn)(JSContext*, const unsigned);
 static const VMFunction ThrowMsgInfo =
     FunctionInfo<ThrowMsgFn>(js::ThrowMsgOperation, "ThrowMsgOperation");
 
-bool
-BaselineCompiler::emit_JSOP_THROWMSG()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_THROWMSG()
 {
     prepareVMCall();
     pushArg(Imm32(GET_UINT16(pc)));
     return callVM(ThrowMsgInfo);
 }
 
 typedef bool (*ThrowFn)(JSContext*, HandleValue);
 static const VMFunction ThrowInfo = FunctionInfo<ThrowFn>(js::Throw, "Throw");
 
-bool
-BaselineCompiler::emit_JSOP_THROW()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_THROW()
 {
     // Keep value to throw in R0.
     frame.popRegsAndSync(1);
 
     prepareVMCall();
     pushArg(R0);
 
     return callVM(ThrowInfo);
 }
 
-bool
-BaselineCompiler::emit_JSOP_TRY()
+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();
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_FINALLY()
+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.setStackDepth(frame.stackDepth() + 2);
 
     // To match the interpreter, emit an interrupt check at the start of the
     // finally block.
     return emitInterruptCheck();
 }
 
-bool
-BaselineCompiler::emit_JSOP_GOSUB()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_GOSUB()
 {
     // Jump to the finally block.
     frame.syncStack(0);
     jsbytecode* target = pc + GET_JUMP_OFFSET(pc);
     masm.jump(labelOf(target));
     return true;
 }
 
@@ -3892,18 +4101,19 @@ LoadBaselineScriptResumeEntries(MacroAss
     MOZ_ASSERT(dest != scratch);
 
     masm.movePtr(ImmGCPtr(script), dest);
     masm.loadPtr(Address(dest, JSScript::offsetOfBaselineScript()), dest);
     masm.load32(Address(dest, BaselineScript::offsetOfResumeEntriesOffset()), scratch);
     masm.addPtr(scratch, dest);
 }
 
-bool
-BaselineCompiler::emit_JSOP_RETSUB()
+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.
     prepareVMCall();
@@ -3923,20 +4133,21 @@ BaselineCompiler::emit_JSOP_RETSUB()
     masm.jump(scratch1);
     return true;
 }
 
 typedef bool (*PushLexicalEnvFn)(JSContext*, BaselineFrame*, Handle<LexicalScope*>);
 static const VMFunction PushLexicalEnvInfo =
     FunctionInfo<PushLexicalEnvFn>(jit::PushLexicalEnv, "PushLexicalEnv");
 
-bool
-BaselineCompiler::emit_JSOP_PUSHLEXICALENV()
-{
-    LexicalScope& scope = script->getScope(pc)->as<LexicalScope>();
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_PUSHLEXICALENV()
+{
+    LexicalScope& scope = script->getScope(pc)->template as<LexicalScope>();
 
     // Call a stub to push the block on the block chain.
     prepareVMCall();
     masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
 
     pushArg(ImmGCPtr(&scope));
     pushArg(R0.scratchReg());
 
@@ -3947,18 +4158,19 @@ typedef bool (*PopLexicalEnvFn)(JSContex
 static const VMFunction PopLexicalEnvInfo =
     FunctionInfo<PopLexicalEnvFn>(jit::PopLexicalEnv, "PopLexicalEnv");
 
 typedef bool (*DebugLeaveThenPopLexicalEnvFn)(JSContext*, BaselineFrame*, jsbytecode*);
 static const VMFunction DebugLeaveThenPopLexicalEnvInfo =
     FunctionInfo<DebugLeaveThenPopLexicalEnvFn>(jit::DebugLeaveThenPopLexicalEnv,
                                                 "DebugLeaveThenPopLexicalEnv");
 
-bool
-BaselineCompiler::emit_JSOP_POPLEXICALENV()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_POPLEXICALENV()
 {
     prepareVMCall();
 
     masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
 
     if (compileDebugInstrumentation_) {
         pushArg(ImmPtr(pc));
         pushArg(R0.scratchReg());
@@ -3973,18 +4185,19 @@ typedef bool (*FreshenLexicalEnvFn)(JSCo
 static const VMFunction FreshenLexicalEnvInfo =
     FunctionInfo<FreshenLexicalEnvFn>(jit::FreshenLexicalEnv, "FreshenLexicalEnv");
 
 typedef bool (*DebugLeaveThenFreshenLexicalEnvFn)(JSContext*, BaselineFrame*, jsbytecode*);
 static const VMFunction DebugLeaveThenFreshenLexicalEnvInfo =
     FunctionInfo<DebugLeaveThenFreshenLexicalEnvFn>(jit::DebugLeaveThenFreshenLexicalEnv,
                                                     "DebugLeaveThenFreshenLexicalEnv");
 
-bool
-BaselineCompiler::emit_JSOP_FRESHENLEXICALENV()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_FRESHENLEXICALENV()
 {
     prepareVMCall();
 
     masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
 
     if (compileDebugInstrumentation_) {
         pushArg(ImmPtr(pc));
         pushArg(R0.scratchReg());
@@ -4000,18 +4213,19 @@ typedef bool (*RecreateLexicalEnvFn)(JSC
 static const VMFunction RecreateLexicalEnvInfo =
     FunctionInfo<RecreateLexicalEnvFn>(jit::RecreateLexicalEnv, "RecreateLexicalEnv");
 
 typedef bool (*DebugLeaveThenRecreateLexicalEnvFn)(JSContext*, BaselineFrame*, jsbytecode*);
 static const VMFunction DebugLeaveThenRecreateLexicalEnvInfo =
     FunctionInfo<DebugLeaveThenRecreateLexicalEnvFn>(jit::DebugLeaveThenRecreateLexicalEnv,
                                                      "DebugLeaveThenRecreateLexicalEnv");
 
-bool
-BaselineCompiler::emit_JSOP_RECREATELEXICALENV()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_RECREATELEXICALENV()
 {
     prepareVMCall();
 
     masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
 
     if (compileDebugInstrumentation_) {
         pushArg(ImmPtr(pc));
         pushArg(R0.scratchReg());
@@ -4021,18 +4235,19 @@ BaselineCompiler::emit_JSOP_RECREATELEXI
     pushArg(R0.scratchReg());
     return callVM(RecreateLexicalEnvInfo);
 }
 
 typedef bool (*DebugLeaveLexicalEnvFn)(JSContext*, BaselineFrame*, jsbytecode*);
 static const VMFunction DebugLeaveLexicalEnvInfo =
     FunctionInfo<DebugLeaveLexicalEnvFn>(jit::DebugLeaveLexicalEnv, "DebugLeaveLexicalEnv");
 
-bool
-BaselineCompiler::emit_JSOP_DEBUGLEAVELEXICALENV()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_DEBUGLEAVELEXICALENV()
 {
     if (!compileDebugInstrumentation_) {
         return true;
     }
 
     prepareVMCall();
     masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
     pushArg(ImmPtr(pc));
@@ -4040,49 +4255,52 @@ BaselineCompiler::emit_JSOP_DEBUGLEAVELE
 
     return callVM(DebugLeaveLexicalEnvInfo);
 }
 
 typedef bool (*PushVarEnvFn)(JSContext*, BaselineFrame*, HandleScope);
 static const VMFunction PushVarEnvInfo =
     FunctionInfo<PushVarEnvFn>(jit::PushVarEnv, "PushVarEnv");
 
-bool
-BaselineCompiler::emit_JSOP_PUSHVARENV()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_PUSHVARENV()
 {
     prepareVMCall();
     masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
     pushArg(ImmGCPtr(script->getScope(pc)));
     pushArg(R0.scratchReg());
 
     return callVM(PushVarEnvInfo);
 }
 
 typedef bool (*PopVarEnvFn)(JSContext*, BaselineFrame*);
 static const VMFunction PopVarEnvInfo =
     FunctionInfo<PopVarEnvFn>(jit::PopVarEnv, "PopVarEnv");
 
-bool
-BaselineCompiler::emit_JSOP_POPVARENV()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_POPVARENV()
 {
     prepareVMCall();
     masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
     pushArg(R0.scratchReg());
 
     return callVM(PopVarEnvInfo);
 }
 
 typedef bool (*EnterWithFn)(JSContext*, BaselineFrame*, HandleValue, Handle<WithScope*>);
 static const VMFunction EnterWithInfo =
     FunctionInfo<EnterWithFn>(jit::EnterWith, "EnterWith");
 
-bool
-BaselineCompiler::emit_JSOP_ENTERWITH()
-{
-    WithScope& withScope = script->getScope(pc)->as<WithScope>();
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_ENTERWITH()
+{
+    WithScope& withScope = script->getScope(pc)->template as<WithScope>();
 
     // Pop "with" object to R0.
     frame.popRegsAndSync(1);
 
     // Call a stub to push the object onto the environment chain.
     prepareVMCall();
     masm.loadBaselineFramePtr(BaselineFrameReg, R1.scratchReg());
 
@@ -4092,51 +4310,54 @@ BaselineCompiler::emit_JSOP_ENTERWITH()
 
     return callVM(EnterWithInfo);
 }
 
 typedef bool (*LeaveWithFn)(JSContext*, BaselineFrame*);
 static const VMFunction LeaveWithInfo =
     FunctionInfo<LeaveWithFn>(jit::LeaveWith, "LeaveWith");
 
-bool
-BaselineCompiler::emit_JSOP_LEAVEWITH()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_LEAVEWITH()
 {
     // Call a stub to pop the with object from the environment chain.
     prepareVMCall();
 
     masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
     pushArg(R0.scratchReg());
 
     return callVM(LeaveWithInfo);
 }
 
 typedef bool (*GetAndClearExceptionFn)(JSContext*, MutableHandleValue);
 static const VMFunction GetAndClearExceptionInfo =
     FunctionInfo<GetAndClearExceptionFn>(GetAndClearException, "GetAndClearException");
 
-bool
-BaselineCompiler::emit_JSOP_EXCEPTION()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_EXCEPTION()
 {
     prepareVMCall();
 
     if (!callVM(GetAndClearExceptionInfo)) {
         return false;
     }
 
     frame.push(R0);
     return true;
 }
 
 typedef bool (*OnDebuggerStatementFn)(JSContext*, BaselineFrame*, jsbytecode* pc, bool*);
 static const VMFunction OnDebuggerStatementInfo =
     FunctionInfo<OnDebuggerStatementFn>(jit::OnDebuggerStatement, "OnDebuggerStatement");
 
-bool
-BaselineCompiler::emit_JSOP_DEBUGGER()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_DEBUGGER()
 {
     prepareVMCall();
     pushArg(ImmPtr(pc));
 
     frame.assertSyncedStack();
     masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
     pushArg(R0.scratchReg());
 
@@ -4155,18 +4376,19 @@ BaselineCompiler::emit_JSOP_DEBUGGER()
     return true;
 }
 
 typedef bool (*DebugEpilogueFn)(JSContext*, BaselineFrame*, jsbytecode*);
 static const VMFunction DebugEpilogueInfo =
     FunctionInfo<DebugEpilogueFn>(jit::DebugEpilogueOnBaselineReturn,
                                   "DebugEpilogueOnBaselineReturn");
 
-bool
-BaselineCompiler::emitReturn()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emitReturn()
 {
     if (compileDebugInstrumentation_) {
         // Move return value into the frame's rval slot.
         masm.storeValue(JSReturnOperand, frame.addressOfReturnValue());
         masm.or32(Imm32(BaselineFrame::HAS_RVAL), frame.addressOfFlags());
 
         // Load BaselineFrame pointer in R0.
         frame.syncStack(0);
@@ -4190,42 +4412,45 @@ BaselineCompiler::emitReturn()
     // into return label.
     if (pc + GetBytecodeLength(pc) < script->codeEnd()) {
         masm.jump(&return_);
     }
 
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_RETURN()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_RETURN()
 {
     MOZ_ASSERT(frame.stackDepth() == 1);
 
     frame.popValue(JSReturnOperand);
     return emitReturn();
 }
 
+template <typename Handler>
 void
-BaselineCompiler::emitLoadReturnValue(ValueOperand val)
+BaselineCodeGen<Handler>::emitLoadReturnValue(ValueOperand val)
 {
     Label done, noRval;
     masm.branchTest32(Assembler::Zero, frame.addressOfFlags(),
                       Imm32(BaselineFrame::HAS_RVAL), &noRval);
     masm.loadValue(frame.addressOfReturnValue(), val);
     masm.jump(&done);
 
     masm.bind(&noRval);
     masm.moveValue(UndefinedValue(), val);
 
     masm.bind(&done);
 }
 
-bool
-BaselineCompiler::emit_JSOP_RETRVAL()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_RETRVAL()
 {
     MOZ_ASSERT(frame.stackDepth() == 0);
 
     masm.moveValue(UndefinedValue(), JSReturnOperand);
 
     if (!script->noScriptRval()) {
         // Return the value in the return value slot, if any.
         Label done;
@@ -4236,18 +4461,19 @@ BaselineCompiler::emit_JSOP_RETRVAL()
     }
 
     return emitReturn();
 }
 
 typedef bool (*ToIdFn)(JSContext*, HandleValue, MutableHandleValue);
 static const VMFunction ToIdInfo = FunctionInfo<ToIdFn>(js::ToIdOperation, "ToIdOperation");
 
-bool
-BaselineCompiler::emit_JSOP_TOID()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_TOID()
 {
     // Load index in R0, but keep values on the stack for the decompiler.
     frame.syncStack(0);
     masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R0);
 
     // No-op if the index is trivally convertable to an id.
     Label done;
     masm.branchTestInt32(Assembler::Equal, R0, &done);
@@ -4266,18 +4492,19 @@ BaselineCompiler::emit_JSOP_TOID()
     frame.pop(); // Pop index.
     frame.push(R0);
     return true;
 }
 
 typedef JSObject* (*ToAsyncFn)(JSContext*, HandleFunction);
 static const VMFunction ToAsyncInfo = FunctionInfo<ToAsyncFn>(js::WrapAsyncFunction, "ToAsync");
 
-bool
-BaselineCompiler::emit_JSOP_TOASYNC()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_TOASYNC()
 {
     frame.syncStack(0);
     masm.unboxObject(frame.addressOfStackValue(frame.peek(-1)), R0.scratchReg());
 
     prepareVMCall();
     pushArg(R0.scratchReg());
 
     if (!callVM(ToAsyncInfo)) {
@@ -4289,18 +4516,19 @@ BaselineCompiler::emit_JSOP_TOASYNC()
     frame.push(R0);
     return true;
 }
 
 typedef JSObject* (*ToAsyncGenFn)(JSContext*, HandleFunction);
 static const VMFunction ToAsyncGenInfo =
     FunctionInfo<ToAsyncGenFn>(js::WrapAsyncGenerator, "ToAsyncGen");
 
-bool
-BaselineCompiler::emit_JSOP_TOASYNCGEN()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_TOASYNCGEN()
 {
     frame.syncStack(0);
     masm.unboxObject(frame.addressOfStackValue(frame.peek(-1)), R0.scratchReg());
 
     prepareVMCall();
     pushArg(R0.scratchReg());
 
     if (!callVM(ToAsyncGenInfo)) {
@@ -4312,18 +4540,19 @@ BaselineCompiler::emit_JSOP_TOASYNCGEN()
     frame.push(R0);
     return true;
 }
 
 typedef JSObject* (*ToAsyncIterFn)(JSContext*, HandleObject, HandleValue);
 static const VMFunction ToAsyncIterInfo =
     FunctionInfo<ToAsyncIterFn>(js::CreateAsyncFromSyncIterator, "ToAsyncIter");
 
-bool
-BaselineCompiler::emit_JSOP_TOASYNCITER()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_TOASYNCITER()
 {
     frame.syncStack(0);
     masm.unboxObject(frame.addressOfStackValue(frame.peek(-2)), R0.scratchReg());
     masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R1);
 
     prepareVMCall();
     pushArg(R1);
     pushArg(R0.scratchReg());
@@ -4336,18 +4565,19 @@ BaselineCompiler::emit_JSOP_TOASYNCITER(
     frame.popn(2);
     frame.push(R0);
     return true;
 }
 
 typedef bool (*TrySkipAwaitFn)(JSContext*, HandleValue, MutableHandleValue);
 static const VMFunction TrySkipAwaitInfo = FunctionInfo<TrySkipAwaitFn>(jit::TrySkipAwait, "TrySkipAwait");
 
-bool
-BaselineCompiler::emit_JSOP_TRYSKIPAWAIT()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_TRYSKIPAWAIT()
 {
     frame.syncStack(0);
     masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R0);
 
     prepareVMCall();
     pushArg(R0);
 
     if (!callVM(TrySkipAwaitInfo)) {
@@ -4370,18 +4600,19 @@ BaselineCompiler::emit_JSOP_TRYSKIPAWAIT
     frame.push(R1);
     return true;
 }
 
 typedef bool (*ThrowObjectCoercibleFn)(JSContext*, HandleValue);
 static const VMFunction ThrowObjectCoercibleInfo =
     FunctionInfo<ThrowObjectCoercibleFn>(ThrowObjectCoercible, "ThrowObjectCoercible");
 
-bool
-BaselineCompiler::emit_JSOP_CHECKOBJCOERCIBLE()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_CHECKOBJCOERCIBLE()
 {
     frame.syncStack(0);
     masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R0);
 
     Label fail, done;
 
     masm.branchTestUndefined(Assembler::Equal, R0, &fail);
     masm.branchTestNull(Assembler::NotEqual, R0, &done);
@@ -4397,18 +4628,19 @@ BaselineCompiler::emit_JSOP_CHECKOBJCOER
 
     masm.bind(&done);
     return true;
 }
 
 typedef JSString* (*ToStringFn)(JSContext*, HandleValue);
 static const VMFunction ToStringInfo = FunctionInfo<ToStringFn>(ToStringSlow, "ToStringSlow");
 
-bool
-BaselineCompiler::emit_JSOP_TOSTRING()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_TOSTRING()
 {
     // Keep top stack value in R0.
     frame.popRegsAndSync(1);
 
     // Inline path for string.
     Label done;
     masm.branchTestString(Assembler::Equal, R0, &done);
 
@@ -4423,18 +4655,19 @@ BaselineCompiler::emit_JSOP_TOSTRING()
 
     masm.tagValue(JSVAL_TYPE_STRING, ReturnReg, R0);
 
     masm.bind(&done);
     frame.push(R0);
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_TABLESWITCH()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_TABLESWITCH()
 {
     frame.popRegsAndSync(1);
 
     jsbytecode* defaultpc = pc + GET_JUMP_OFFSET(pc);
     Label* defaultLabel = labelOf(defaultpc);
 
     int32_t low = GET_JUMP_OFFSET(pc + 1 * JUMP_OFFSET_LEN);
     int32_t high = GET_JUMP_OFFSET(pc + 2 * JUMP_OFFSET_LEN);
@@ -4468,45 +4701,48 @@ BaselineCompiler::emit_JSOP_TABLESWITCH(
     LoadBaselineScriptResumeEntries(masm, script, scratch1, scratch2);
     masm.loadPtr(BaseIndex(scratch1, key, ScaleFromElemWidth(sizeof(uintptr_t)),
                            firstResumeIndex * sizeof(uintptr_t)), scratch1);
     masm.jump(scratch1);
 
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_ITER()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_ITER()
 {
     frame.popRegsAndSync(1);
 
     if (!emitNextIC()) {
         return false;
     }
 
     frame.push(R0);
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_MOREITER()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_MOREITER()
 {
     frame.syncStack(0);
     masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R0);
 
     if (!emitNextIC()) {
         return false;
     }
 
     frame.push(R0);
     return true;
 }
 
-bool
-BaselineCompiler::emitIsMagicValue()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emitIsMagicValue()
 {
     frame.syncStack(0);
 
     Label isMagic, done;
     masm.branchTestMagic(Assembler::Equal, frame.addressOfStackValue(frame.peek(-1)),
                          &isMagic);
     masm.moveValue(BooleanValue(false), R0);
     masm.jump(&done);
@@ -4514,77 +4750,84 @@ BaselineCompiler::emitIsMagicValue()
     masm.bind(&isMagic);
     masm.moveValue(BooleanValue(true), R0);
 
     masm.bind(&done);
     frame.push(R0, JSVAL_TYPE_BOOLEAN);
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_ISNOITER()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_ISNOITER()
 {
     return emitIsMagicValue();
 }
 
-bool
-BaselineCompiler::emit_JSOP_ENDITER()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_ENDITER()
 {
     if (!emit_JSOP_JUMPTARGET()) {
         return false;
     }
     frame.popRegsAndSync(1);
 
     if (!emitNextIC()) {
         return false;
     }
 
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_ISGENCLOSING()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_ISGENCLOSING()
 {
     return emitIsMagicValue();
 }
 
-bool
-BaselineCompiler::emit_JSOP_GETRVAL()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_GETRVAL()
 {
     frame.syncStack(0);
 
     emitLoadReturnValue(R0);
 
     frame.push(R0);
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_SETRVAL()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_SETRVAL()
 {
     // Store to the frame's return value slot.
     storeValue(frame.peek(-1), frame.addressOfReturnValue(), R2);
     masm.or32(Imm32(BaselineFrame::HAS_RVAL), frame.addressOfFlags());
     frame.pop();
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_CALLEE()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_CALLEE()
 {
     MOZ_ASSERT(function());
     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>
 void
-BaselineCompiler::getThisEnvironmentCallee(Register reg)
+BaselineCodeGen<Handler>::getThisEnvironmentCallee(Register reg)
 {
     // Directly load callee from frame if we have a HomeObject
     if (function() && function()->allowSuperProperty()) {
         masm.loadFunctionFromCalleeToken(frame.addressOfCalleeToken(), reg);
         return;
     }
 
     // Locate environment chain
@@ -4612,18 +4855,19 @@ BaselineCompiler::getThisEnvironmentCall
     // Load callee
     masm.unboxObject(Address(reg, CallObject::offsetOfCallee()), reg);
 }
 
 typedef JSObject* (*HomeObjectSuperBaseFn)(JSContext*, HandleObject);
 static const VMFunction HomeObjectSuperBaseInfo =
     FunctionInfo<HomeObjectSuperBaseFn>(HomeObjectSuperBase, "HomeObjectSuperBase");
 
-bool
-BaselineCompiler::emit_JSOP_SUPERBASE()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_SUPERBASE()
 {
     frame.syncStack(0);
 
     Register scratch = R0.scratchReg();
     Register proto = R1.scratchReg();
 
     // Lookup callee object of environment containing [[ThisValue]]
     getThisEnvironmentCallee(scratch);
@@ -4659,18 +4903,19 @@ BaselineCompiler::emit_JSOP_SUPERBASE()
     frame.push(R1);
     return true;
 }
 
 typedef JSObject* (*SuperFunOperationFn)(JSContext*, HandleObject);
 static const VMFunction SuperFunOperationInfo =
     FunctionInfo<SuperFunOperationFn>(SuperFunOperation, "SuperFunOperation");
 
-bool
-BaselineCompiler::emit_JSOP_SUPERFUN()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_SUPERFUN()
 {
     frame.syncStack(0);
 
     Register callee = R0.scratchReg();
     Register proto = R1.scratchReg();
     Register scratch = R2.scratchReg();
 
     // Lookup callee object of environment containing [[ThisValue]]
@@ -4711,18 +4956,19 @@ BaselineCompiler::emit_JSOP_SUPERFUN()
     frame.push(R1);
     return true;
 }
 
 typedef bool (*NewArgumentsObjectFn)(JSContext*, BaselineFrame*, MutableHandleValue);
 static const VMFunction NewArgumentsObjectInfo =
     FunctionInfo<NewArgumentsObjectFn>(jit::NewArgumentsObject, "NewArgumentsObject");
 
-bool
-BaselineCompiler::emit_JSOP_ARGUMENTS()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_ARGUMENTS()
 {
     frame.syncStack(0);
 
     MOZ_ASSERT(script->argumentsHasVarBinding());
 
     Label done;
     if (!script->needsArgsObj()) {
         // We assume the script does not need an arguments object. However, this
@@ -4750,67 +4996,71 @@ BaselineCompiler::emit_JSOP_ARGUMENTS()
     frame.push(R0);
     return true;
 }
 
 typedef bool (*RunOnceScriptPrologueFn)(JSContext*, HandleScript);
 static const VMFunction RunOnceScriptPrologueInfo =
     FunctionInfo<RunOnceScriptPrologueFn>(js::RunOnceScriptPrologue, "RunOnceScriptPrologue");
 
-bool
-BaselineCompiler::emit_JSOP_RUNONCE()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_RUNONCE()
 {
     frame.syncStack(0);
 
     prepareVMCall();
 
     masm.movePtr(ImmGCPtr(script), R0.scratchReg());
     pushArg(R0.scratchReg());
 
     return callVM(RunOnceScriptPrologueInfo);
 }
 
-bool
-BaselineCompiler::emit_JSOP_REST()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_REST()
 {
     frame.syncStack(0);
 
     if (!emitNextIC()) {
         return false;
     }
 
     // Mark R0 as pushed stack value.
     frame.push(R0);
     return true;
 }
 
 typedef JSObject* (*CreateGeneratorFn)(JSContext*, BaselineFrame*);
 static const VMFunction CreateGeneratorInfo =
     FunctionInfo<CreateGeneratorFn>(jit::CreateGenerator, "CreateGenerator");
 
-bool
-BaselineCompiler::emit_JSOP_GENERATOR()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_GENERATOR()
 {
     MOZ_ASSERT(frame.stackDepth() == 0);
 
     masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
 
     prepareVMCall();
     pushArg(R0.scratchReg());
     if (!callVM(CreateGeneratorInfo)) {
         return false;
     }
 
     masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, R0);
     frame.push(R0);
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_INITIALYIELD()
+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);
@@ -4836,18 +5086,19 @@ BaselineCompiler::emit_JSOP_INITIALYIELD
     masm.tagValue(JSVAL_TYPE_OBJECT, genObj, JSReturnOperand);
     return emitReturn();
 }
 
 typedef bool (*NormalSuspendFn)(JSContext*, HandleObject, BaselineFrame*, jsbytecode*, uint32_t);
 static const VMFunction NormalSuspendInfo =
     FunctionInfo<NormalSuspendFn>(jit::NormalSuspend, "NormalSuspend");
 
-bool
-BaselineCompiler::emit_JSOP_YIELD()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_YIELD()
 {
     // Store generator in R0.
     frame.popRegsAndSync(1);
 
     Register genObj = R2.scratchReg();
     masm.unboxObject(R0, genObj);
 
     MOZ_ASSERT(frame.stackDepth() >= 1);
@@ -4884,28 +5135,30 @@ BaselineCompiler::emit_JSOP_YIELD()
             return false;
         }
     }
 
     masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), JSReturnOperand);
     return emitReturn();
 }
 
-bool
-BaselineCompiler::emit_JSOP_AWAIT()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_AWAIT()
 {
     return emit_JSOP_YIELD();
 }
 
 typedef bool (*DebugAfterYieldFn)(JSContext*, BaselineFrame*, jsbytecode*, bool*);
 static const VMFunction DebugAfterYieldInfo =
     FunctionInfo<DebugAfterYieldFn>(jit::DebugAfterYield, "DebugAfterYield");
 
-bool
-BaselineCompiler::emit_JSOP_DEBUGAFTERYIELD()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_DEBUGAFTERYIELD()
 {
     if (!compileDebugInstrumentation_) {
         return true;
     }
 
     frame.assertSyncedStack();
     masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
     prepareVMCall();
@@ -4926,18 +5179,19 @@ BaselineCompiler::emit_JSOP_DEBUGAFTERYI
     masm.bind(&done);
     return true;
 }
 
 typedef bool (*FinalSuspendFn)(JSContext*, HandleObject, jsbytecode*);
 static const VMFunction FinalSuspendInfo =
     FunctionInfo<FinalSuspendFn>(jit::FinalSuspend, "FinalSuspend");
 
-bool
-BaselineCompiler::emit_JSOP_FINALYIELDRVAL()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_FINALYIELDRVAL()
 {
     // Store generator in R0.
     frame.popRegsAndSync(1);
     masm.unboxObject(R0, R0.scratchReg());
 
     prepareVMCall();
     pushArg(ImmPtr(pc));
     pushArg(R0.scratchReg());
@@ -4955,18 +5209,19 @@ typedef bool (*InterpretResumeFn)(JSCont
 static const VMFunction InterpretResumeInfo =
     FunctionInfo<InterpretResumeFn>(jit::InterpretResume, "InterpretResume");
 
 typedef bool (*GeneratorThrowFn)(JSContext*, BaselineFrame*, Handle<GeneratorObject*>,
                                  HandleValue, uint32_t);
 static const VMFunction GeneratorThrowOrReturnInfo =
     FunctionInfo<GeneratorThrowFn>(jit::GeneratorThrowOrReturn, "GeneratorThrowOrReturn", TailCall);
 
-bool
-BaselineCompiler::emit_JSOP_RESUME()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_RESUME()
 {
     GeneratorObject::ResumeKind resumeKind = GeneratorObject::getResumeKind(pc);
 
     frame.syncStack(0);
     masm.assertStackAlignment(sizeof(Value), 0);
 
     AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
     regs.take(BaselineFrameReg);
@@ -5199,73 +5454,78 @@ BaselineCompiler::emit_JSOP_RESUME()
     frame.push(R0);
     return true;
 }
 
 typedef bool (*CheckSelfHostedFn)(JSContext*, HandleValue);
 static const VMFunction CheckSelfHostedInfo =
     FunctionInfo<CheckSelfHostedFn>(js::Debug_CheckSelfHosted, "Debug_CheckSelfHosted");
 
-bool
-BaselineCompiler::emit_JSOP_DEBUGCHECKSELFHOSTED()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_DEBUGCHECKSELFHOSTED()
 {
 #ifdef DEBUG
     frame.syncStack(0);
 
     masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R0);
 
     prepareVMCall();
     pushArg(R0);
     if (!callVM(CheckSelfHostedInfo)) {
         return false;
     }
 #endif
     return true;
 
 }
 
-bool
-BaselineCompiler::emit_JSOP_IS_CONSTRUCTING()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_IS_CONSTRUCTING()
 {
     frame.push(MagicValue(JS_IS_CONSTRUCTING));
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_JUMPTARGET()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_JUMPTARGET()
 {
     if (!script->hasScriptCounts()) {
         return true;
     }
     PCCounts* counts = script->maybeGetPCCounts(pc);
     uint64_t* counterAddr = &counts->numExec();
     masm.inc64(AbsoluteAddress(counterAddr));
     return true;
 }
 
 typedef bool (*CheckClassHeritageOperationFn)(JSContext*, HandleValue);
 static const VMFunction CheckClassHeritageOperationInfo =
     FunctionInfo<CheckClassHeritageOperationFn>(js::CheckClassHeritageOperation,
                                                 "CheckClassHeritageOperation");
 
-bool
-BaselineCompiler::emit_JSOP_CHECKCLASSHERITAGE()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_CHECKCLASSHERITAGE()
 {
     frame.syncStack(0);
 
     // Leave the heritage value on the stack.
     masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R0);
 
     prepareVMCall();
     pushArg(R0);
     return callVM(CheckClassHeritageOperationInfo);
 }
 
-bool
-BaselineCompiler::emit_JSOP_INITHOMEOBJECT()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_INITHOMEOBJECT()
 {
     // Load HomeObject in R0.
     frame.popRegsAndSync(1);
 
     // Load function off stack
     Register func = R2.scratchReg();
     masm.unboxObject(frame.addressOfStackValue(frame.peek(-1)), func);
 
@@ -5279,18 +5539,19 @@ BaselineCompiler::emit_JSOP_INITHOMEOBJE
     masm.branchPtrInNurseryChunk(Assembler::Equal, func, temp, &skipBarrier);
     masm.branchValueIsNurseryObject(Assembler::NotEqual, R0, temp, &skipBarrier);
     masm.call(&postBarrierSlot_);
     masm.bind(&skipBarrier);
 
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_BUILTINPROTO()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_BUILTINPROTO()
 {
     // The builtin prototype is a constant for a given global.
     JSProtoKey key = static_cast<JSProtoKey>(GET_UINT8(pc));
     MOZ_ASSERT(key < JSProto_LIMIT);
     JSObject* builtin = GlobalObject::getOrCreatePrototype(cx, key);
     if (!builtin) {
         return false;
     }
@@ -5298,18 +5559,19 @@ BaselineCompiler::emit_JSOP_BUILTINPROTO
     return true;
 }
 
 typedef JSObject* (*ObjectWithProtoOperationFn)(JSContext*, HandleValue);
 static const VMFunction ObjectWithProtoOperationInfo =
     FunctionInfo<ObjectWithProtoOperationFn>(js::ObjectWithProtoOperation,
                                              "ObjectWithProtoOperationInfo");
 
-bool
-BaselineCompiler::emit_JSOP_OBJWITHPROTO()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_OBJWITHPROTO()
 {
     frame.syncStack(0);
 
     // Leave the proto value on the stack for the decompiler
     masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R0);
 
     prepareVMCall();
     pushArg(R0);
@@ -5322,18 +5584,19 @@ BaselineCompiler::emit_JSOP_OBJWITHPROTO
     frame.push(R0);
     return true;
 }
 
 typedef JSObject* (*FunWithProtoFn)(JSContext*, HandleFunction, HandleObject, HandleObject);
 static const VMFunction FunWithProtoInfo =
     FunctionInfo<FunWithProtoFn>(js::FunWithProtoOperation, "FunWithProtoOperation");
 
-bool
-BaselineCompiler::emit_JSOP_FUNWITHPROTO()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_FUNWITHPROTO()
 {
     frame.popRegsAndSync(1);
 
     masm.unboxObject(R0, R0.scratchReg());
     masm.loadPtr(frame.addressOfEnvironmentChain(), R1.scratchReg());
 
     prepareVMCall();
     pushArg(R0.scratchReg());
@@ -5349,18 +5612,19 @@ BaselineCompiler::emit_JSOP_FUNWITHPROTO
 }
 
 typedef JSFunction* (*MakeDefaultConstructorFn)(JSContext*, HandleScript,
                                                 jsbytecode*, HandleObject);
 static const VMFunction MakeDefaultConstructorInfo =
     FunctionInfo<MakeDefaultConstructorFn>(js::MakeDefaultConstructor,
                                            "MakeDefaultConstructor");
 
-bool
-BaselineCompiler::emit_JSOP_CLASSCONSTRUCTOR()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_CLASSCONSTRUCTOR()
 {
     frame.syncStack(0);
 
     // Pass nullptr as prototype to MakeDefaultConstructor
     prepareVMCall();
     pushArg(ImmPtr(nullptr));
     pushArg(ImmPtr(pc));
     pushArg(ImmGCPtr(script));
@@ -5368,18 +5632,19 @@ BaselineCompiler::emit_JSOP_CLASSCONSTRU
         return false;
     }
 
     masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, R0);
     frame.push(R0);
     return true;
 }
 
-bool
-BaselineCompiler::emit_JSOP_DERIVEDCONSTRUCTOR()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_DERIVEDCONSTRUCTOR()
 {
     frame.popRegsAndSync(1);
 
     masm.unboxObject(R0, R0.scratchReg());
 
     prepareVMCall();
     pushArg(R0.scratchReg());
     pushArg(ImmPtr(pc));
@@ -5393,18 +5658,19 @@ BaselineCompiler::emit_JSOP_DERIVEDCONST
     return true;
 }
 
 typedef JSObject* (*GetOrCreateModuleMetaObjectFn)(JSContext*, HandleObject);
 static const VMFunction GetOrCreateModuleMetaObjectInfo =
     FunctionInfo<GetOrCreateModuleMetaObjectFn>(js::GetOrCreateModuleMetaObject,
                                                 "GetOrCreateModuleMetaObject");
 
-bool
-BaselineCompiler::emit_JSOP_IMPORTMETA()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_IMPORTMETA()
 {
     RootedModuleObject module(cx, GetModuleObjectForScript(script));
     MOZ_ASSERT(module);
 
     frame.syncStack(0);
 
     prepareVMCall();
     pushArg(ImmGCPtr(module));
@@ -5417,18 +5683,19 @@ BaselineCompiler::emit_JSOP_IMPORTMETA()
     return true;
 }
 
 typedef JSObject* (*StartDynamicModuleImportFn)(JSContext*, HandleValue, HandleValue);
 static const VMFunction StartDynamicModuleImportInfo =
     FunctionInfo<StartDynamicModuleImportFn>(js::StartDynamicModuleImport,
                                                 "StartDynamicModuleImport");
 
-bool
-BaselineCompiler::emit_JSOP_DYNAMIC_IMPORT()
+template <typename Handler>
+bool
+BaselineCodeGen<Handler>::emit_JSOP_DYNAMIC_IMPORT()
 {
     RootedValue referencingPrivate(cx, FindScriptOrModulePrivateForScript(script));
 
     // Put specifier value in R0.
     frame.popRegsAndSync(1);
 
     prepareVMCall();
     pushArg(R0);
@@ -5436,8 +5703,11 @@ BaselineCompiler::emit_JSOP_DYNAMIC_IMPO
     if (!callVM(StartDynamicModuleImportInfo)) {
         return false;
     }
 
     masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, R0);
     frame.push(R0);
     return true;
 }
+
+// Instantiate explicitly for now to make sure it compiles.
+template class jit::BaselineCodeGen<BaselineInterpreterHandler>;
--- a/js/src/jit/BaselineCompiler.h
+++ b/js/src/jit/BaselineCompiler.h
@@ -244,101 +244,61 @@ namespace jit {
     _(JSOP_BUILTINPROTO)       \
     _(JSOP_OBJWITHPROTO)       \
     _(JSOP_FUNWITHPROTO)       \
     _(JSOP_CLASSCONSTRUCTOR)   \
     _(JSOP_DERIVEDCONSTRUCTOR) \
     _(JSOP_IMPORTMETA)         \
     _(JSOP_DYNAMIC_IMPORT)
 
-class BaselineCompiler final
+// Base class for BaselineCompiler and BaselineInterpreterGenerator. The Handler
+// template is a class storing fields/methods that are interpreter or compiler
+// specific. This can be combined with template specialization of methods in
+// this class to specialize behavior.
+template <typename Handler>
+class BaselineCodeGen
 {
+  protected:
+    Handler handler;
+
     JSContext* cx;
     JSScript* script;
     jsbytecode* pc;
     StackMacroAssembler masm;
     bool ionCompileable_;
     bool compileDebugInstrumentation_;
 
     TempAllocator& alloc_;
     BytecodeAnalysis analysis_;
     FrameInfo frame;
 
     js::Vector<RetAddrEntry, 16, SystemAllocPolicy> retAddrEntries_;
+    js::Vector<CodeOffset> traceLoggerToggleOffsets_;
 
-    // Stores the native code offset for a bytecode pc.
-    struct PCMappingEntry
-    {
-        uint32_t pcOffset;
-        uint32_t nativeOffset;
-        PCMappingSlotInfo slotInfo;
-
-        // If set, insert a PCMappingIndexEntry before encoding the
-        // current entry.
-        bool addIndexEntry;
-    };
-
-    js::Vector<PCMappingEntry, 16, SystemAllocPolicy> pcMappingEntries_;
+    FixedList<Label> labels_;
+    NonAssertingLabel return_;
+    NonAssertingLabel postBarrierSlot_;
 
     // Index of the current ICEntry in the script's ICScript.
     uint32_t icEntryIndex_;
 
     uint32_t pushedBeforeCall_;
 #ifdef DEBUG
     bool inCall_;
 #endif
 
-    CodeOffset profilerPushToggleOffset_;
-    CodeOffset profilerEnterFrameToggleOffset_;
-    CodeOffset profilerExitFrameToggleOffset_;
-
-    Vector<CodeOffset> traceLoggerToggleOffsets_;
-    CodeOffset traceLoggerScriptTextIdOffset_;
-
-    FixedList<Label> labels_;
-    NonAssertingLabel return_;
-    NonAssertingLabel postBarrierSlot_;
-
-    // Early Ion bailouts will enter at this address. This is after frame
-    // construction and before environment chain is initialized.
-    CodeOffset bailoutPrologueOffset_;
-
-    // Baseline Debug OSR during prologue will enter at this address. This is
-    // right after where a debug prologue VM call would have returned.
-    CodeOffset debugOsrPrologueOffset_;
-
-    // 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_;
-
     // Whether any on stack arguments are modified.
     bool modifiesArguments_;
 
+    BaselineCodeGen(JSContext* cx, TempAllocator& alloc, JSScript* script);
+
     Label* labelOf(jsbytecode* pc) {
         return &labels_[script->pcToOffset(pc)];
     }
 
-    // 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;
-    }
-
-  public:
-    BaselineCompiler(JSContext* cx, TempAllocator& alloc, JSScript* script);
-    MOZ_MUST_USE bool init();
-
-    MethodStatus compile();
-
-    void setCompileDebugInstrumentation() {
-        compileDebugInstrumentation_ = true;
-    }
-
-  private:
     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;
     }
 
@@ -347,30 +307,16 @@ class BaselineCompiler final
         // been delazified before compilation started.
         return script->functionNonDelazifying();
     }
 
     ModuleObject* module() const {
         return script->module();
     }
 
-    PCMappingSlotInfo getStackTopSlotInfo() {
-        MOZ_ASSERT(frame.numUnsyncedSlots() <= 2);
-        switch (frame.numUnsyncedSlots()) {
-          case 0:
-            return PCMappingSlotInfo::MakeSlotInfo();
-          case 1:
-            return PCMappingSlotInfo::MakeSlotInfo(PCMappingSlotInfo::ToSlotLocation(frame.peek(-1)));
-          case 2:
-          default:
-            return PCMappingSlotInfo::MakeSlotInfo(PCMappingSlotInfo::ToSlotLocation(frame.peek(-1)),
-                                                   PCMappingSlotInfo::ToSlotLocation(frame.peek(-2)));
-        }
-    }
-
     template <typename T>
     void pushArg(const T& t) {
         masm.Push(t);
     }
     void prepareVMCall();
 
     enum CallVMPhase {
         POST_INITIALIZE,
@@ -381,46 +327,24 @@ class BaselineCompiler final
     bool callVMNonOp(const VMFunction& fun, CallVMPhase phase=POST_INITIALIZE) {
         if (!callVM(fun, phase)) {
             return false;
         }
         retAddrEntries_.back().setKind(RetAddrEntry::Kind::NonOpCallVM);
         return true;
     }
 
-    BytecodeAnalysis& analysis() {
-        return analysis_;
-    }
-
-    MethodStatus emitBody();
-
     MOZ_MUST_USE bool emitCheckThis(ValueOperand val, bool reinit=false);
     void emitLoadReturnValue(ValueOperand val);
 
-    void emitInitializeLocals();
-    MOZ_MUST_USE bool emitPrologue();
-    MOZ_MUST_USE bool emitEpilogue();
-    MOZ_MUST_USE bool emitOutOfLinePostBarrierSlot();
     MOZ_MUST_USE bool emitNextIC();
-    MOZ_MUST_USE bool emitStackCheck();
     MOZ_MUST_USE bool emitInterruptCheck();
     MOZ_MUST_USE bool emitWarmUpCounterIncrement(bool allowOsr=true);
-    MOZ_MUST_USE bool emitArgumentTypeChecks();
-    void emitIsDebuggeeCheck();
-    MOZ_MUST_USE bool emitDebugPrologue();
-    MOZ_MUST_USE bool emitDebugTrap();
-    MOZ_MUST_USE bool emitTraceLoggerEnter();
-    MOZ_MUST_USE bool emitTraceLoggerExit();
     MOZ_MUST_USE bool emitTraceLoggerResume(Register script, AllocatableGeneralRegisterSet& regs);
 
-    void emitProfilerEnterFrame();
-    void emitProfilerExitFrame();
-
-    MOZ_MUST_USE bool initEnvironmentChain();
-
     void storeValue(const StackValue* source, const Address& dest,
                     const ValueOperand& scratch);
 
 #define EMIT_OP(op) bool emit_##op();
     OPCODE_LIST(EMIT_OP)
 #undef EMIT_OP
 
     // JSOP_NEG, JSOP_BITNOT
@@ -445,24 +369,131 @@ class BaselineCompiler final
 
     MOZ_MUST_USE bool emitFormalArgAccess(uint32_t arg, bool get);
 
     MOZ_MUST_USE bool emitThrowConstAssignment();
     MOZ_MUST_USE bool emitUninitializedLexicalCheck(const ValueOperand& val);
 
     MOZ_MUST_USE bool emitIsMagicValue();
 
-    MOZ_MUST_USE bool addPCMappingEntry(bool addIndexEntry);
-
     void getEnvironmentCoordinateObject(Register reg);
     Address getEnvironmentCoordinateAddressFromObject(Register objReg, Register reg);
     Address getEnvironmentCoordinateAddress(Register reg);
 
     void getThisEnvironmentCallee(Register reg);
 };
 
+// Interface used by BaselineCodeGen for BaselineCompiler.
+class BaselineCompilerHandler
+{
+  public:
+};
+
+class BaselineCompiler final : private BaselineCodeGen<BaselineCompilerHandler>
+{
+    // Stores the native code offset for a bytecode pc.
+    struct PCMappingEntry
+    {
+        uint32_t pcOffset;
+        uint32_t nativeOffset;
+        PCMappingSlotInfo slotInfo;
+
+        // If set, insert a PCMappingIndexEntry before encoding the
+        // current entry.
+        bool addIndexEntry;
+    };
+
+    js::Vector<PCMappingEntry, 16, SystemAllocPolicy> pcMappingEntries_;
+
+    CodeOffset profilerPushToggleOffset_;
+    CodeOffset profilerEnterFrameToggleOffset_;
+    CodeOffset profilerExitFrameToggleOffset_;
+
+    CodeOffset traceLoggerScriptTextIdOffset_;
+
+    // Early Ion bailouts will enter at this address. This is after frame
+    // construction and before environment chain is initialized.
+    CodeOffset bailoutPrologueOffset_;
+
+    // Baseline Debug OSR during prologue will enter at this address. This is
+    // right after where a debug prologue VM call would have returned.
+    CodeOffset debugOsrPrologueOffset_;
+
+    // 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;
+    }
+
+  public:
+    BaselineCompiler(JSContext* cx, TempAllocator& alloc, JSScript* script);
+    MOZ_MUST_USE bool init();
+
+    MethodStatus compile();
+
+    void setCompileDebugInstrumentation() {
+        compileDebugInstrumentation_ = true;
+    }
+
+  private:
+    PCMappingSlotInfo getStackTopSlotInfo() {
+        MOZ_ASSERT(frame.numUnsyncedSlots() <= 2);
+        switch (frame.numUnsyncedSlots()) {
+          case 0:
+            return PCMappingSlotInfo::MakeSlotInfo();
+          case 1:
+            return PCMappingSlotInfo::MakeSlotInfo(PCMappingSlotInfo::ToSlotLocation(frame.peek(-1)));
+          case 2:
+          default:
+            return PCMappingSlotInfo::MakeSlotInfo(PCMappingSlotInfo::ToSlotLocation(frame.peek(-1)),
+                                                   PCMappingSlotInfo::ToSlotLocation(frame.peek(-2)));
+        }
+    }
+
+    BytecodeAnalysis& analysis() {
+        return analysis_;
+    }
+
+    MethodStatus emitBody();
+
+    void emitInitializeLocals();
+    MOZ_MUST_USE bool emitPrologue();
+    MOZ_MUST_USE bool emitEpilogue();
+    MOZ_MUST_USE bool emitOutOfLinePostBarrierSlot();
+    MOZ_MUST_USE bool emitStackCheck();
+    MOZ_MUST_USE bool emitArgumentTypeChecks();
+    void emitIsDebuggeeCheck();
+    MOZ_MUST_USE bool emitDebugPrologue();
+    MOZ_MUST_USE bool emitDebugTrap();
+    MOZ_MUST_USE bool emitTraceLoggerEnter();
+    MOZ_MUST_USE bool emitTraceLoggerExit();
+
+    void emitProfilerEnterFrame();
+    void emitProfilerExitFrame();
+
+    MOZ_MUST_USE bool initEnvironmentChain();
+
+    MOZ_MUST_USE bool addPCMappingEntry(bool addIndexEntry);
+};
+
+// Interface used by BaselineCodeGen for BaselineInterpreterGenerator.
+class BaselineInterpreterHandler
+{
+  public:
+};
+
+class BaselineInterpreterGenerator final : private BaselineCodeGen<BaselineInterpreterHandler>
+{
+  public:
+};
+
 extern const VMFunction NewArrayCopyOnWriteInfo;
 extern const VMFunction ImplicitThisInfo;
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_BaselineCompiler_h */
--- a/js/src/vm/ObjectGroup.cpp
+++ b/js/src/vm/ObjectGroup.cpp
@@ -1639,20 +1639,22 @@ ObjectGroup::setAllocationSiteObjectGrou
 /* static */ ArrayObject*
 ObjectGroup::getOrFixupCopyOnWriteObject(JSContext* cx, HandleScript script, jsbytecode* pc)
 {
     // Make sure that the template object for script/pc has a type indicating
     // that the object and its copies have copy on write elements.
     RootedArrayObject obj(cx, &script->getObject(GET_UINT32_INDEX(pc))->as<ArrayObject>());
     MOZ_ASSERT(obj->denseElementsAreCopyOnWrite());
 
-    AutoSweepObjectGroup sweepObjGroup(obj->group());
-    if (obj->group()->fromAllocationSite(sweepObjGroup)) {
-        MOZ_ASSERT(obj->group()->hasAnyFlags(sweepObjGroup, OBJECT_FLAG_COPY_ON_WRITE));
-        return obj;
+    {
+        AutoSweepObjectGroup sweepObjGroup(obj->group());
+        if (obj->group()->fromAllocationSite(sweepObjGroup)) {
+            MOZ_ASSERT(obj->group()->hasAnyFlags(sweepObjGroup, OBJECT_FLAG_COPY_ON_WRITE));
+            return obj;
+        }
     }
 
     RootedObjectGroup group(cx, allocationSiteGroup(cx, script, pc, JSProto_Array));
     if (!group) {
         return nullptr;
     }
 
     AutoSweepObjectGroup sweepGroup(group);