Bug 1520744 - Move prologue/epilogue methods from BaselineCompiler to BaselineCodeGen. r=djvj
authorJan de Mooij <jdemooij@mozilla.com>
Sun, 27 Jan 2019 08:55:18 +0000
changeset 515585 dd40395ed807a09a53b06be1176c6000870f6e68
parent 515584 d743672a092cac271f2a0ca0a7976161fb2c19f7
child 515586 4fbf196208cc9c28be1b9f884c1022f58ac62ba2
push id1953
push userffxbld-merge
push dateMon, 11 Mar 2019 12:10:20 +0000
treeherdermozilla-release@9c35dcbaa899 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdjvj
bugs1520744
milestone66.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1520744 - Move prologue/epilogue methods from BaselineCompiler to BaselineCodeGen. r=djvj This will change a bit in the future but I think this is a reasonable initial version. Differential Revision: https://phabricator.services.mozilla.com/D16801
js/src/jit/BaselineCompiler.cpp
js/src/jit/BaselineCompiler.h
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -65,30 +65,30 @@ BaselineInterpreterHandler::BaselineInte
 
 template <typename Handler>
 template <typename... HandlerArgs>
 BaselineCodeGen<Handler>::BaselineCodeGen(JSContext* cx, HandlerArgs&&... args)
     : handler(cx, masm, std::forward<HandlerArgs>(args)...),
       cx(cx),
       frame(handler.frame()),
       traceLoggerToggleOffsets_(cx),
+      profilerEnterFrameToggleOffset_(),
+      profilerExitFrameToggleOffset_(),
       pushedBeforeCall_(0),
 #ifdef DEBUG
       inCall_(false),
 #endif
       modifiesArguments_(false) {
 }
 
 BaselineCompiler::BaselineCompiler(JSContext* cx, TempAllocator& alloc,
                                    JSScript* script)
     : BaselineCodeGen(cx, /* HandlerArgs = */ alloc, script),
       pcMappingEntries_(),
       profilerPushToggleOffset_(),
-      profilerEnterFrameToggleOffset_(),
-      profilerExitFrameToggleOffset_(),
       traceLoggerScriptTextIdOffset_() {
 #ifdef JS_CODEGEN_NONE
   MOZ_CRASH();
 #endif
 }
 
 BaselineInterpreterGenerator::BaselineInterpreterGenerator(JSContext* cx)
     : BaselineCodeGen(cx /* no handlerArgs */) {}
@@ -366,17 +366,18 @@ MethodStatus BaselineCompiler::compile()
 
 #ifdef MOZ_VTUNE
   vtune::MarkScript(code, script, "baseline");
 #endif
 
   return Method_Compiled;
 }
 
-void BaselineCompiler::emitInitializeLocals() {
+template <>
+void BaselineCompilerCodeGen::emitInitializeLocals() {
   // Initialize all locals to |undefined|. Lexical bindings are temporal
   // dead zoned in bytecode.
 
   size_t n = frame.nlocals();
   if (n == 0) {
     return;
   }
 
@@ -405,23 +406,29 @@ void BaselineCompiler::emitInitializeLoc
     for (size_t i = 0; i < LOOP_UNROLL_FACTOR; i++) {
       masm.pushValue(R0);
     }
     masm.branchSub32(Assembler::NonZero, Imm32(LOOP_UNROLL_FACTOR),
                      R1.scratchReg(), &pushLoop);
   }
 }
 
+template <>
+void BaselineInterpreterCodeGen::emitInitializeLocals() {
+  MOZ_CRASH("NYI: interpreter emitInitializeLocals");
+}
+
 // On input:
 //  R2.scratchReg() contains object being written to.
 //  Called with the baseline stack synced, except for R0 which is preserved.
 //  All other registers are usable as scratch.
 // This calls:
 //    void PostWriteBarrier(JSRuntime* rt, JSObject* obj);
-bool BaselineCompiler::emitOutOfLinePostBarrierSlot() {
+template <typename Handler>
+bool BaselineCodeGen<Handler>::emitOutOfLinePostBarrierSlot() {
   masm.bind(&postBarrierSlot_);
 
   Register objReg = R2.scratchReg();
   AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
   regs.take(R0);
   regs.take(objReg);
   regs.take(BaselineFrameReg);
   Register scratch = regs.takeAny();
@@ -610,80 +617,100 @@ bool BaselineCodeGen<Handler>::callVM(co
   return handler.appendRetAddrEntry(cx, RetAddrEntry::Kind::CallVM, callOffset);
 }
 
 typedef bool (*CheckOverRecursedBaselineFn)(JSContext*, BaselineFrame*);
 static const VMFunction CheckOverRecursedBaselineInfo =
     FunctionInfo<CheckOverRecursedBaselineFn>(CheckOverRecursedBaseline,
                                               "CheckOverRecursedBaseline");
 
-bool BaselineCompiler::emitStackCheck() {
+template <typename Handler>
+bool BaselineCodeGen<Handler>::emitStackCheck() {
   // If this is the late stack check for a frame which contains an early stack
   // check, then the early stack check might have failed and skipped past the
   // pushing of locals on the stack.
   //
   // If this is a possibility, then the OVER_RECURSED flag should be checked,
   // and the VMCall to CheckOverRecursedBaseline done unconditionally if it's
   // set.
   Label forceCall;
-  if (needsEarlyStackCheck()) {
+  if (handler.needsEarlyStackCheck()) {
     masm.branchTest32(Assembler::NonZero, frame.addressOfFlags(),
                       Imm32(BaselineFrame::OVER_RECURSED), &forceCall);
   }
 
   Label skipCall;
   masm.branchStackPtrRhs(Assembler::BelowOrEqual,
                          AbsoluteAddress(cx->addressOfJitStackLimit()),
                          &skipCall);
 
-  if (needsEarlyStackCheck()) {
+  if (handler.needsEarlyStackCheck()) {
     masm.bind(&forceCall);
   }
 
   prepareVMCall();
   masm.loadBaselineFramePtr(BaselineFrameReg, R1.scratchReg());
   pushArg(R1.scratchReg());
 
   CallVMPhase phase = POST_INITIALIZE;
-  if (needsEarlyStackCheck()) {
+  if (handler.needsEarlyStackCheck()) {
     phase = CHECK_OVER_RECURSED;
   }
 
   if (!callVMNonOp(CheckOverRecursedBaselineInfo, phase)) {
     return false;
   }
 
   handler.markLastRetAddrEntryKind(RetAddrEntry::Kind::StackCheck);
 
   masm.bind(&skipCall);
   return true;
 }
 
-void BaselineCompiler::emitIsDebuggeeCheck() {
-  if (compileDebugInstrumentation()) {
+template <>
+void BaselineCompilerCodeGen::emitIsDebuggeeCheck() {
+  if (handler.compileDebugInstrumentation()) {
     masm.Push(BaselineFrameReg);
     masm.setupUnalignedABICall(R0.scratchReg());
     masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
     masm.passABIArg(R0.scratchReg());
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, jit::FrameIsDebuggeeCheck));
     masm.Pop(BaselineFrameReg);
   }
 }
 
 template <>
+void BaselineInterpreterCodeGen::emitIsDebuggeeCheck() {
+  MOZ_CRASH("NYI: interpreter emitIsDebuggeeCheck");
+}
+
+template <>
 void BaselineCompilerCodeGen::loadScript(Register dest) {
   masm.movePtr(ImmGCPtr(handler.script()), dest);
 }
 
 template <>
 void BaselineInterpreterCodeGen::loadScript(Register dest) {
   MOZ_CRASH("NYI: interpreter loadScript");
 }
 
 template <>
+void BaselineCompilerCodeGen::subtractScriptSlotsSize(Register reg,
+                                                      Register scratch) {
+  uint32_t slotsSize = handler.script()->nslots() * sizeof(Value);
+  masm.subPtr(Imm32(slotsSize), reg);
+}
+
+template <>
+void BaselineInterpreterCodeGen::subtractScriptSlotsSize(Register reg,
+                                                         Register scratch) {
+  MOZ_CRASH("NYI: interpreter subtractScriptSlotsSize");
+}
+
+template <>
 void BaselineCompilerCodeGen::loadGlobalLexicalEnvironment(Register dest) {
   masm.movePtr(ImmGCPtr(&cx->global()->lexicalEnvironment()), dest);
 }
 
 template <>
 void BaselineInterpreterCodeGen::loadGlobalLexicalEnvironment(Register dest) {
   MOZ_CRASH("NYI: interpreter loadGlobalLexicalEnvironment");
 }
@@ -806,18 +833,19 @@ template <>
 void BaselineInterpreterCodeGen::loadResumeIndexBytecodeOperand(Register dest) {
   MOZ_CRASH("NYI: interpreter loadResumeIndexBytecodeOperand");
 }
 
 typedef bool (*DebugPrologueFn)(JSContext*, BaselineFrame*, jsbytecode*, bool*);
 static const VMFunction DebugPrologueInfo =
     FunctionInfo<DebugPrologueFn>(jit::DebugPrologue, "DebugPrologue");
 
-bool BaselineCompiler::emitDebugPrologue() {
-  if (compileDebugInstrumentation()) {
+template <typename Handler>
+bool BaselineCodeGen<Handler>::emitDebugPrologue() {
+  auto ifDebuggee = [this]() {
     // Load pointer to BaselineFrame in R0.
     masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
 
     prepareVMCall();
     pushBytecodePCArg();
     pushArg(R0.scratchReg());
     if (!callVM(DebugPrologueInfo)) {
       return false;
@@ -830,16 +858,20 @@ bool BaselineCompiler::emitDebugPrologue
     // frame's return value slot.
     Label done;
     masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, &done);
     {
       masm.loadValue(frame.addressOfReturnValue(), JSReturnOperand);
       masm.jump(&return_);
     }
     masm.bind(&done);
+    return true;
+  };
+  if (!emitDebugInstrumentation(ifDebuggee)) {
+    return false;
   }
 
   debugOsrPrologueOffset_ = CodeOffset(masm.currentOffset());
 
   return true;
 }
 
 typedef bool (*CheckGlobalOrEvalDeclarationConflictsFn)(JSContext*,
@@ -850,19 +882,36 @@ static const VMFunction CheckGlobalOrEva
         js::CheckGlobalOrEvalDeclarationConflicts,
         "CheckGlobalOrEvalDeclarationConflicts");
 
 typedef bool (*InitFunctionEnvironmentObjectsFn)(JSContext*, BaselineFrame*);
 static const VMFunction InitFunctionEnvironmentObjectsInfo =
     FunctionInfo<InitFunctionEnvironmentObjectsFn>(
         jit::InitFunctionEnvironmentObjects, "InitFunctionEnvironmentObjects");
 
-bool BaselineCompiler::initEnvironmentChain() {
+template <>
+void BaselineCompilerCodeGen::emitPreInitEnvironmentChain(
+    Register nonFunctionEnv) {
+  if (handler.function()) {
+    masm.storePtr(ImmPtr(nullptr), frame.addressOfEnvironmentChain());
+  } else {
+    masm.storePtr(nonFunctionEnv, frame.addressOfEnvironmentChain());
+  }
+}
+
+template <>
+void BaselineInterpreterCodeGen::emitPreInitEnvironmentChain(
+    Register nonFunctionEnv) {
+  MOZ_CRASH("NYI: interpreter emitPreInitEnvironmentChain");
+}
+
+template <>
+bool BaselineCompilerCodeGen::initEnvironmentChain() {
   CallVMPhase phase = POST_INITIALIZE;
-  if (needsEarlyStackCheck()) {
+  if (handler.needsEarlyStackCheck()) {
     phase = CHECK_OVER_RECURSED;
   }
 
   RootedFunction fun(cx, handler.function());
   if (fun) {
     // Use callee->environment as env chain. Note that we do this also
     // for needsSomeEnvironmentObject functions, so that the env chain
     // slot is properly initialized if the call triggers GC.
@@ -897,16 +946,21 @@ bool BaselineCompiler::initEnvironmentCh
     if (!callVMNonOp(CheckGlobalOrEvalDeclarationConflictsInfo, phase)) {
       return false;
     }
   }
 
   return true;
 }
 
+template <>
+bool BaselineInterpreterCodeGen::initEnvironmentChain() {
+  MOZ_CRASH("NYI: interpreter initEnvironmentChain");
+}
+
 typedef bool (*InterruptCheckFn)(JSContext*);
 static const VMFunction InterruptCheckInfo =
     FunctionInfo<InterruptCheckFn>(InterruptCheck, "InterruptCheck");
 
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emitInterruptCheck() {
   frame.syncStack(0);
 
@@ -1004,17 +1058,18 @@ bool BaselineCompilerCodeGen::emitWarmUp
   return true;
 }
 
 template <>
 bool BaselineInterpreterCodeGen::emitWarmUpCounterIncrement() {
   MOZ_CRASH("NYI: interpreter emitWarmUpCounterIncrement");
 }
 
-bool BaselineCompiler::emitArgumentTypeChecks() {
+template <>
+bool BaselineCompilerCodeGen::emitArgumentTypeChecks() {
   if (!handler.function()) {
     return true;
   }
 
   frame.pushThis();
   frame.popRegsAndSync(1);
 
   if (!emitNextIC()) {
@@ -1030,16 +1085,21 @@ bool BaselineCompiler::emitArgumentTypeC
     if (!emitNextIC()) {
       return false;
     }
   }
 
   return true;
 }
 
+template <>
+bool BaselineInterpreterCodeGen::emitArgumentTypeChecks() {
+  MOZ_CRASH("NYI: interpreter emitArgumentTypeChecks");
+}
+
 bool BaselineCompiler::emitDebugTrap() {
   MOZ_ASSERT(compileDebugInstrumentation());
   MOZ_ASSERT(frame.numUnsyncedSlots() == 0);
 
   JSScript* script = handler.script();
   bool enabled =
       script->stepModeEnabled() || script->hasBreakpointsAt(handler.pc());
 
@@ -1066,17 +1126,18 @@ bool BaselineCompiler::emitDebugTrap() {
 #endif
 
   // Add a RetAddrEntry for the return offset -> pc mapping.
   return handler.appendRetAddrEntry(cx, RetAddrEntry::Kind::DebugTrap,
                                     masm.currentOffset());
 }
 
 #ifdef JS_TRACE_LOGGING
-bool BaselineCompiler::emitTraceLoggerEnter() {
+template <>
+bool BaselineCompilerCodeGen::emitTraceLoggerEnter() {
   AllocatableRegisterSet regs(RegisterSet::Volatile());
   Register loggerReg = regs.takeAnyGeneral();
   Register scriptReg = regs.takeAnyGeneral();
 
   Label noTraceLogger;
   if (!traceLoggerToggleOffsets_.append(masm.toggledJump(&noTraceLogger))) {
     return false;
   }
@@ -1101,17 +1162,23 @@ bool BaselineCompiler::emitTraceLoggerEn
   masm.Pop(scriptReg);
   masm.Pop(loggerReg);
 
   masm.bind(&noTraceLogger);
 
   return true;
 }
 
-bool BaselineCompiler::emitTraceLoggerExit() {
+template <>
+bool BaselineInterpreterCodeGen::emitTraceLoggerEnter() {
+  MOZ_CRASH("NYI: interpreter emitTraceLoggerEnter");
+}
+
+template <typename Handler>
+bool BaselineCodeGen<Handler>::emitTraceLoggerExit() {
   AllocatableRegisterSet regs(RegisterSet::Volatile());
   Register loggerReg = regs.takeAnyGeneral();
 
   Label noTraceLogger;
   if (!traceLoggerToggleOffsets_.append(masm.toggledJump(&noTraceLogger))) {
     return false;
   }
 
@@ -1151,30 +1218,32 @@ bool BaselineCodeGen<Handler>::emitTrace
   regs.add(scriptId);
 
   masm.bind(&noTraceLogger);
 
   return true;
 }
 #endif
 
-void BaselineCompiler::emitProfilerEnterFrame() {
+template <typename Handler>
+void BaselineCodeGen<Handler>::emitProfilerEnterFrame() {
   // Store stack position to lastProfilingFrame variable, guarded by a toggled
   // jump. Starts off initially disabled.
   Label noInstrument;
   CodeOffset toggleOffset = masm.toggledJump(&noInstrument);
   masm.profilerEnterFrame(masm.getStackPointer(), R0.scratchReg());
   masm.bind(&noInstrument);
 
   // Store the start offset in the appropriate location.
   MOZ_ASSERT(!profilerEnterFrameToggleOffset_.bound());
   profilerEnterFrameToggleOffset_ = toggleOffset;
 }
 
-void BaselineCompiler::emitProfilerExitFrame() {
+template <typename Handler>
+void BaselineCodeGen<Handler>::emitProfilerExitFrame() {
   // Store previous frame to lastProfilingFrame variable, guarded by a toggled
   // jump. Starts off initially disabled.
   Label noInstrument;
   CodeOffset toggleOffset = masm.toggledJump(&noInstrument);
   masm.profilerExitFrame();
   masm.bind(&noInstrument);
 
   // Store the start offset in the appropriate location.
@@ -5575,71 +5644,71 @@ bool BaselineCodeGen<Handler>::emit_JSOP
     return false;
   }
 
   masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, R0);
   frame.push(R0);
   return true;
 }
 
-bool BaselineCompiler::emitPrologue() {
+template <typename Handler>
+bool BaselineCodeGen<Handler>::emitPrologue() {
 #ifdef JS_USE_LINK_REGISTER
   // Push link register from generateEnterJIT()'s BLR.
   masm.pushReturnAddress();
   masm.checkStackAlignment();
 #endif
   emitProfilerEnterFrame();
 
-  JSScript* script = handler.script();
-  if (script->trackRecordReplayProgress()) {
-    masm.inc64(
-        AbsoluteAddress(mozilla::recordreplay::ExecutionProgressCounter()));
-  }
-
   masm.push(BaselineFrameReg);
   masm.moveStackPtrTo(BaselineFrameReg);
   masm.subFromStackPtr(Imm32(BaselineFrame::Size()));
 
   // Initialize BaselineFrame. For eval scripts, the env chain
   // is passed in R1, so we have to be careful not to clobber it.
 
   // Initialize BaselineFrame::flags.
   masm.store32(Imm32(0), frame.addressOfFlags());
 
   // Handle env chain pre-initialization (in case GC gets run
   // during stack check).  For global and eval scripts, the env
   // chain is in R1.  For function scripts, the env chain is in
   // the callee, nullptr is stored for now so that GC doesn't choke
   // on a bogus EnvironmentChain value in the frame.
-  if (handler.function()) {
-    masm.storePtr(ImmPtr(nullptr), frame.addressOfEnvironmentChain());
-  } else {
-    masm.storePtr(R1.scratchReg(), frame.addressOfEnvironmentChain());
+  emitPreInitEnvironmentChain(R1.scratchReg());
+
+  auto incCounter = [this]() {
+    masm.inc64(
+        AbsoluteAddress(mozilla::recordreplay::ExecutionProgressCounter()));
+    return true;
+  };
+  if (!emitTestScriptFlag(JSScript::ImmutableFlags::TrackRecordReplayProgress,
+                          true, incCounter, R2.scratchReg())) {
+    return false;
   }
 
   // Functions with a large number of locals require two stack checks.
   // The VMCall for a fallible stack check can only occur after the
   // env chain has been initialized, as that is required for proper
   // exception handling if the VMCall returns false.  The env chain
   // initialization can only happen after the UndefinedValues for the
   // local slots have been pushed. However by that time, the stack might
   // have grown too much.
   //
   // In these cases, we emit an extra, early, infallible check before pushing
   // the locals. The early check just sets a flag on the frame if the stack
   // check fails. If the flag is set, then the jitcode skips past the pushing
   // of the locals, and directly to env chain initialization followed by the
   // actual stack check, which will throw the correct exception.
   Label earlyStackCheckFailed;
-  if (needsEarlyStackCheck()) {
+  if (handler.needsEarlyStackCheck()) {
     // Subtract the size of script->nslots() from the stack pointer.
-    uint32_t slotsSize = script->nslots() * sizeof(Value);
     Register scratch = R1.scratchReg();
     masm.moveStackPtrTo(scratch);
-    masm.subPtr(Imm32(slotsSize), scratch);
+    subtractScriptSlotsSize(scratch, R2.scratchReg());
 
     // Set the OVER_RECURSED flag on the frame if the computed stack pointer
     // overflows the stack limit. We have to use the actual (*NoInterrupt)
     // stack limit here because we don't want to set the flag and throw an
     // overrecursion exception later in the interrupt case.
     Label stackCheckOk;
     masm.branchPtr(Assembler::BelowOrEqual,
                    AbsoluteAddress(cx->addressOfJitStackLimitNoInterrupt()),
@@ -5648,17 +5717,17 @@ bool BaselineCompiler::emitPrologue() {
       masm.or32(Imm32(BaselineFrame::OVER_RECURSED), frame.addressOfFlags());
       masm.jump(&earlyStackCheckFailed);
     }
     masm.bind(&stackCheckOk);
   }
 
   emitInitializeLocals();
 
-  if (needsEarlyStackCheck()) {
+  if (handler.needsEarlyStackCheck()) {
     masm.bind(&earlyStackCheckFailed);
   }
 
 #ifdef JS_TRACE_LOGGING
   if (!emitTraceLoggerEnter()) {
     return false;
   }
 #endif
@@ -5673,17 +5742,20 @@ bool BaselineCompiler::emitPrologue() {
 
   // Initialize the env chain before any operation that may
   // call into the VM and trigger a GC.
   if (!initEnvironmentChain()) {
     return false;
   }
 
   frame.assertSyncedStack();
-  masm.debugAssertContextRealm(script->realm(), R1.scratchReg());
+
+  if (JSScript* script = handler.maybeScript()) {
+    masm.debugAssertContextRealm(script->realm(), R1.scratchReg());
+  }
 
   if (!emitStackCheck()) {
     return false;
   }
 
   if (!emitDebugPrologue()) {
     return false;
   }
@@ -5694,17 +5766,18 @@ bool BaselineCompiler::emitPrologue() {
 
   if (!emitArgumentTypeChecks()) {
     return false;
   }
 
   return true;
 }
 
-bool BaselineCompiler::emitEpilogue() {
+template <typename Handler>
+bool BaselineCodeGen<Handler>::emitEpilogue() {
   // Record the offset of the epilogue, so we can do early return from
   // Debugger handlers during on-stack recompile.
   debugOsrEpilogueOffset_ = CodeOffset(masm.currentOffset());
 
   masm.bind(&return_);
 
 #ifdef JS_TRACE_LOGGING
   if (!emitTraceLoggerExit()) {
--- a/js/src/jit/BaselineCompiler.h
+++ b/js/src/jit/BaselineCompiler.h
@@ -266,16 +266,31 @@ class BaselineCodeGen {
 
   typename Handler::FrameInfoT& frame;
 
   js::Vector<CodeOffset> traceLoggerToggleOffsets_;
 
   NonAssertingLabel return_;
   NonAssertingLabel postBarrierSlot_;
 
+  CodeOffset profilerEnterFrameToggleOffset_;
+  CodeOffset profilerExitFrameToggleOffset_;
+
+  // 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_;
+
   uint32_t pushedBeforeCall_;
 #ifdef DEBUG
   bool inCall_;
 #endif
 
   // Whether any on stack arguments are modified.
   bool modifiesArguments_;
 
@@ -304,16 +319,19 @@ class BaselineCodeGen {
   void pushUint8BytecodeOperandArg();
   void pushUint16BytecodeOperandArg();
 
   void loadResumeIndexBytecodeOperand(Register dest);
 
   // Loads the current JSScript* in dest.
   void loadScript(Register dest);
 
+  // Subtracts |script->nslots() * sizeof(Value)| from reg.
+  void subtractScriptSlotsSize(Register reg, Register scratch);
+
   // Jump to the script's resume entry indicated by resumeIndex.
   void jumpToResumeEntry(Register resumeIndex, Register scratch1,
                          Register scratch2);
 
   // Load the global's lexical environment.
   void loadGlobalLexicalEnvironment(Register dest);
   void pushGlobalLexicalEnvironmentValue(ValueOperand scratch);
 
@@ -429,16 +447,34 @@ class BaselineCodeGen {
   MOZ_MUST_USE bool emitUninitializedLexicalCheck(const ValueOperand& val);
 
   MOZ_MUST_USE bool emitIsMagicValue();
 
   void getEnvironmentCoordinateObject(Register reg);
   Address getEnvironmentCoordinateAddressFromObject(Register objReg,
                                                     Register reg);
   Address getEnvironmentCoordinateAddress(Register reg);
+
+  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();
+  MOZ_MUST_USE bool emitDebugPrologue();
+  MOZ_MUST_USE bool initEnvironmentChain();
+
+  MOZ_MUST_USE bool emitTraceLoggerEnter();
+  MOZ_MUST_USE bool emitTraceLoggerExit();
+
+  void emitIsDebuggeeCheck();
+  void emitInitializeLocals();
+  void emitPreInitEnvironmentChain(Register nonFunctionEnv);
+
+  void emitProfilerEnterFrame();
+  void emitProfilerExitFrame();
 };
 
 using RetAddrEntryVector = js::Vector<RetAddrEntry, 16, SystemAllocPolicy>;
 
 // Interface used by BaselineCodeGen for BaselineCompiler.
 class BaselineCompilerHandler {
   CompilerFrameInfo frame_;
   TempAllocator& alloc_;
@@ -505,16 +541,23 @@ class BaselineCompilerHandler {
       ReportOutOfMemory(cx);
       return false;
     }
     return true;
   }
   void markLastRetAddrEntryKind(RetAddrEntry::Kind kind) {
     retAddrEntries_.back().setKind(kind);
   }
+
+  // If a script has more |nslots| than this, then emit code to do an
+  // early stack check.
+  bool needsEarlyStackCheck() const {
+    static const unsigned EARLY_STACK_CHECK_SLOT_COUNT = 128;
+    return script()->nslots() > EARLY_STACK_CHECK_SLOT_COUNT;
+  }
 };
 
 using BaselineCompilerCodeGen = BaselineCodeGen<BaselineCompilerHandler>;
 
 class BaselineCompiler final : private BaselineCompilerCodeGen {
   // Stores the native code offset for a bytecode pc.
   struct PCMappingEntry {
     uint32_t pcOffset;
@@ -524,40 +567,19 @@ class BaselineCompiler final : private B
     // 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 handler.script()->nslots() > EARLY_STACK_CHECK_SLOT_COUNT;
-  }
-
  public:
   BaselineCompiler(JSContext* cx, TempAllocator& alloc, JSScript* script);
   MOZ_MUST_USE bool init();
 
   MethodStatus compile();
 
   bool compileDebugInstrumentation() const {
     return handler.compileDebugInstrumentation();
@@ -582,32 +604,17 @@ class BaselineCompiler final : private B
         PCMappingSlotInfo::SlotLocation loc2 = frame.stackValueSlotLocation(-2);
         return PCMappingSlotInfo::MakeSlotInfo(loc1, loc2);
       }
     }
   }
 
   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 {
   InterpreterFrameInfo frame_;
 
@@ -628,16 +635,20 @@ class BaselineInterpreterHandler {
   // are no-ops.
   MOZ_MUST_USE bool appendRetAddrEntry(JSContext* cx, RetAddrEntry::Kind kind,
                                        uint32_t retOffset) {
     return true;
   }
   void markLastRetAddrEntryKind(RetAddrEntry::Kind) {}
 
   bool maybeIonCompileable() const { return true; }
+
+  // The interpreter always does the early stack check because we don't know the
+  // frame size statically.
+  bool needsEarlyStackCheck() const { return true; }
 };
 
 using BaselineInterpreterCodeGen = BaselineCodeGen<BaselineInterpreterHandler>;
 
 class BaselineInterpreterGenerator final : private BaselineInterpreterCodeGen {
  public:
   explicit BaselineInterpreterGenerator(JSContext* cx);
 };