Bug 1501310 - Move Baseline IC fallback code from JitRealm to JitRuntime. r=tcampbell
authorJan de Mooij <jdemooij@mozilla.com>
Tue, 02 Apr 2019 09:16:43 +0000
changeset 467555 d6fb444fa553abf9864546515b795f9988bdaf3a
parent 467554 cb394c9bb56d3a5a9e214c1c97e6a63318754083
child 467556 8f80c03171c9ea34ca7c7ee4962647643f000cf0
push id112633
push usercbrindusan@mozilla.com
push dateTue, 02 Apr 2019 15:52:18 +0000
treeherdermozilla-inbound@a6c6359636f7 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstcampbell
bugs1501310
milestone68.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 1501310 - Move Baseline IC fallback code from JitRealm to JitRuntime. r=tcampbell Fallback code is now generated (as a single JitCode instance) when we create the JitRuntime. In ICScript::Create we can now allocate the fallback stubs directly (we no longer need a Compiler class for each fallback stub) because we no longer have to handle the compile-code case. Differential Revision: https://phabricator.services.mozilla.com/D24360
js/src/jit/BaselineBailouts.cpp
js/src/jit/BaselineIC.cpp
js/src/jit/BaselineIC.h
js/src/jit/BaselineICList.h
js/src/jit/Ion.cpp
js/src/jit/JitRealm.h
--- a/js/src/jit/BaselineBailouts.cpp
+++ b/js/src/jit/BaselineBailouts.cpp
@@ -437,34 +437,35 @@ struct BaselineStackBuilder {
 #ifdef DEBUG
 static inline bool IsInlinableFallback(ICFallbackStub* icEntry) {
   return icEntry->isCall_Fallback() || icEntry->isGetProp_Fallback() ||
          icEntry->isSetProp_Fallback() || icEntry->isGetElem_Fallback();
 }
 #endif
 
 static inline void* GetStubReturnAddress(JSContext* cx, jsbytecode* pc) {
-  JitRealm* jitRealm = cx->realm()->jitRealm();
+  const BaselineICFallbackCode& code =
+      cx->runtime()->jitRuntime()->baselineICFallbackCode();
 
   if (IsGetPropPC(pc)) {
-    return jitRealm->bailoutReturnAddr(BailoutReturnStub::GetProp);
+    return code.bailoutReturnAddr(BailoutReturnKind::GetProp);
   }
   if (IsSetPropPC(pc)) {
-    return jitRealm->bailoutReturnAddr(BailoutReturnStub::SetProp);
+    return code.bailoutReturnAddr(BailoutReturnKind::SetProp);
   }
   if (IsGetElemPC(pc)) {
-    return jitRealm->bailoutReturnAddr(BailoutReturnStub::GetElem);
+    return code.bailoutReturnAddr(BailoutReturnKind::GetElem);
   }
 
   // This should be a call op of some kind, now.
   MOZ_ASSERT(IsCallPC(pc) && !IsSpreadCallPC(pc));
   if (IsConstructorCallPC(pc)) {
-    return jitRealm->bailoutReturnAddr(BailoutReturnStub::New);
+    return code.bailoutReturnAddr(BailoutReturnKind::New);
   }
-  return jitRealm->bailoutReturnAddr(BailoutReturnStub::Call);
+  return code.bailoutReturnAddr(BailoutReturnKind::Call);
 }
 
 static inline jsbytecode* GetNextNonLoopEntryPc(jsbytecode* pc,
                                                 jsbytecode** skippedLoopEntry) {
   JSOp op = JSOp(*pc);
   if (op == JSOP_GOTO) {
     return pc + GET_JUMP_OFFSET(pc);
   }
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -31,16 +31,17 @@
 #include "jit/SharedICHelpers.h"
 #include "jit/VMFunctions.h"
 #include "js/Conversions.h"
 #include "js/GCVector.h"
 #include "vm/JSFunction.h"
 #include "vm/Opcodes.h"
 #include "vm/SelfHosting.h"
 #include "vm/TypedArrayObject.h"
+#include "vtune/VTuneWrapper.h"
 
 #include "builtin/Boolean-inl.h"
 
 #include "jit/JitFrames-inl.h"
 #include "jit/MacroAssembler-inl.h"
 #include "jit/shared/Lowering-shared-inl.h"
 #include "jit/SharedICHelpers-inl.h"
 #include "jit/VMFunctionList-inl.h"
@@ -49,16 +50,36 @@
 #include "vm/JSScript-inl.h"
 #include "vm/StringObject-inl.h"
 
 using mozilla::DebugOnly;
 
 namespace js {
 namespace jit {
 
+// Class used to emit all Baseline IC fallback code when initializing the
+// JitRuntime.
+class MOZ_RAII FallbackICCodeCompiler final : public ICStubCompilerBase {
+  BaselineICFallbackCode& code;
+  MacroAssembler& masm;
+
+  MOZ_MUST_USE bool emitCall(bool isSpread, bool isConstructing);
+  MOZ_MUST_USE bool emitGetElem(bool hasReceiver);
+  MOZ_MUST_USE bool emitGetProp(bool hasReceiver);
+
+ public:
+  FallbackICCodeCompiler(JSContext* cx, BaselineICFallbackCode& code,
+                         MacroAssembler& masm)
+      : ICStubCompilerBase(cx), code(code), masm(masm) {}
+
+#define DEF_METHOD(kind) MOZ_MUST_USE bool emit_##kind();
+  IC_BASELINE_FALLBACK_CODE_KIND_LIST(DEF_METHOD)
+#undef DEF_METHOD
+};
+
 #ifdef JS_JITSPEW
 void FallbackICSpew(JSContext* cx, ICFallbackStub* stub, const char* fmt, ...) {
   if (JitSpewEnabled(JitSpew_BaselineICFallback)) {
     RootedScript script(cx, GetTopJitJSScript(cx));
     jsbytecode* pc = stub->icEntry()->pc(script);
 
     char fmtbuf[100];
     va_list args;
@@ -103,24 +124,49 @@ ICFallbackStub* ICEntry::fallbackStub() 
 }
 
 void ICEntry::trace(JSTracer* trc) {
   for (ICStub* stub = firstStub(); stub; stub = stub->next()) {
     stub->trace(trc);
   }
 }
 
+// Allocator for Baseline IC fallback stubs. These stubs use trampoline code
+// stored in JitRuntime.
+class MOZ_RAII FallbackStubAllocator {
+  JSContext* cx_;
+  ICStubSpace& stubSpace_;
+  const BaselineICFallbackCode& code_;
+
+ public:
+  FallbackStubAllocator(JSContext* cx, ICStubSpace& stubSpace)
+      : cx_(cx),
+        stubSpace_(stubSpace),
+        code_(cx->runtime()->jitRuntime()->baselineICFallbackCode()) {}
+
+  template <typename T, typename... Args>
+  T* newStub(BaselineICFallbackKind kind, Args&&... args) {
+    TrampolinePtr addr = code_.addr(kind);
+    return ICStub::NewFallback<T>(cx_, &stubSpace_, addr,
+                                  std::forward<Args>(args)...);
+  }
+};
+
 /* static */
 UniquePtr<ICScript> ICScript::create(JSContext* cx, JSScript* script) {
   MOZ_ASSERT(cx->realm()->jitRealm());
   MOZ_ASSERT(jit::IsBaselineEnabled(cx));
 
   FallbackICStubSpace stubSpace;
+  FallbackStubAllocator alloc(cx, stubSpace);
+
   js::Vector<ICEntry, 16, SystemAllocPolicy> icEntries;
 
+  using Kind = BaselineICFallbackKind;
+
   auto addIC = [cx, &icEntries, script](jsbytecode* pc, ICStub* stub) {
     if (!stub) {
       MOZ_ASSERT(cx->isExceptionPending());
       return false;
     }
     uint32_t offset = pc ? script->pcToOffset(pc) : ICEntry::NonOpPCOffset;
     if (!icEntries.emplaceBack(stub, offset)) {
       ReportOutOfMemory(cx);
@@ -128,24 +174,26 @@ UniquePtr<ICScript> ICScript::create(JSC
     }
     return true;
   };
 
   // Add ICEntries and fallback stubs for this/argument type checks.
   // Note: we pass a nullptr pc to indicate this is a non-op IC.
   // See ICEntry::NonOpPCOffset.
   if (JSFunction* fun = script->functionNonDelazifying()) {
-    ICTypeMonitor_Fallback::Compiler compiler(cx, uint32_t(0));
-    if (!addIC(nullptr, compiler.getStub(&stubSpace))) {
+    ICStub* stub =
+        alloc.newStub<ICTypeMonitor_Fallback>(Kind::TypeMonitor, nullptr, 0);
+    if (!addIC(nullptr, stub)) {
       return nullptr;
     }
 
     for (size_t i = 0; i < fun->nargs(); i++) {
-      ICTypeMonitor_Fallback::Compiler compiler(cx, i + 1);
-      if (!addIC(nullptr, compiler.getStub(&stubSpace))) {
+      ICStub* stub = alloc.newStub<ICTypeMonitor_Fallback>(Kind::TypeMonitor,
+                                                           nullptr, i + 1);
+      if (!addIC(nullptr, stub)) {
         return nullptr;
       }
     }
   }
 
   jsbytecode const* pcEnd = script->codeEnd();
 
   // Add ICEntries and fallback stubs for JOF_IC bytecode ops.
@@ -161,250 +209,271 @@ UniquePtr<ICScript> ICScript::create(JSC
     }
 
     switch (op) {
       case JSOP_NOT:
       case JSOP_AND:
       case JSOP_OR:
       case JSOP_IFEQ:
       case JSOP_IFNE: {
-        ICToBool_Fallback::Compiler stubCompiler(cx);
-        if (!addIC(pc, stubCompiler.getStub(&stubSpace))) {
+        ICStub* stub = alloc.newStub<ICToBool_Fallback>(Kind::ToBool);
+        if (!addIC(pc, stub)) {
           return nullptr;
         }
         break;
       }
       case JSOP_BITNOT:
       case JSOP_NEG:
       case JSOP_INC:
       case JSOP_DEC: {
-        ICUnaryArith_Fallback::Compiler stubCompiler(cx);
-        if (!addIC(pc, stubCompiler.getStub(&stubSpace))) {
+        ICStub* stub = alloc.newStub<ICUnaryArith_Fallback>(Kind::UnaryArith);
+        if (!addIC(pc, stub)) {
           return nullptr;
         }
         break;
       }
       case JSOP_BITOR:
       case JSOP_BITXOR:
       case JSOP_BITAND:
       case JSOP_LSH:
       case JSOP_RSH:
       case JSOP_URSH:
       case JSOP_ADD:
       case JSOP_SUB:
       case JSOP_MUL:
       case JSOP_DIV:
       case JSOP_MOD:
       case JSOP_POW: {
-        ICBinaryArith_Fallback::Compiler stubCompiler(cx);
-        if (!addIC(pc, stubCompiler.getStub(&stubSpace))) {
+        ICStub* stub = alloc.newStub<ICBinaryArith_Fallback>(Kind::BinaryArith);
+        if (!addIC(pc, stub)) {
           return nullptr;
         }
         break;
       }
       case JSOP_EQ:
       case JSOP_NE:
       case JSOP_LT:
       case JSOP_LE:
       case JSOP_GT:
       case JSOP_GE:
       case JSOP_STRICTEQ:
       case JSOP_STRICTNE: {
-        ICCompare_Fallback::Compiler stubCompiler(cx);
-        if (!addIC(pc, stubCompiler.getStub(&stubSpace))) {
+        ICStub* stub = alloc.newStub<ICCompare_Fallback>(Kind::Compare);
+        if (!addIC(pc, stub)) {
           return nullptr;
         }
         break;
       }
       case JSOP_LOOPENTRY: {
-        ICWarmUpCounter_Fallback::Compiler stubCompiler(cx);
-        if (!addIC(pc, stubCompiler.getStub(&stubSpace))) {
+        ICStub* stub =
+            alloc.newStub<ICWarmUpCounter_Fallback>(Kind::WarmUpCounter);
+        if (!addIC(pc, stub)) {
           return nullptr;
         }
         break;
       }
       case JSOP_NEWARRAY: {
         ObjectGroup* group =
             ObjectGroup::allocationSiteGroup(cx, script, pc, JSProto_Array);
         if (!group) {
           return nullptr;
         }
-        ICNewArray_Fallback::Compiler stubCompiler(cx, group);
-        if (!addIC(pc, stubCompiler.getStub(&stubSpace))) {
+        ICStub* stub =
+            alloc.newStub<ICNewArray_Fallback>(Kind::NewArray, group);
+        if (!addIC(pc, stub)) {
           return nullptr;
         }
         break;
       }
       case JSOP_NEWOBJECT:
       case JSOP_NEWINIT: {
-        ICNewObject_Fallback::Compiler stubCompiler(cx);
-        if (!addIC(pc, stubCompiler.getStub(&stubSpace))) {
+        ICStub* stub = alloc.newStub<ICNewObject_Fallback>(Kind::NewObject);
+        if (!addIC(pc, stub)) {
           return nullptr;
         }
         break;
       }
       case JSOP_INITELEM:
       case JSOP_INITHIDDENELEM:
       case JSOP_INITELEM_ARRAY:
       case JSOP_INITELEM_INC:
       case JSOP_SETELEM:
       case JSOP_STRICTSETELEM: {
-        ICSetElem_Fallback::Compiler stubCompiler(cx);
-        if (!addIC(pc, stubCompiler.getStub(&stubSpace))) {
+        ICStub* stub = alloc.newStub<ICSetElem_Fallback>(Kind::SetElem);
+        if (!addIC(pc, stub)) {
           return nullptr;
         }
         break;
       }
       case JSOP_INITPROP:
       case JSOP_INITLOCKEDPROP:
       case JSOP_INITHIDDENPROP:
       case JSOP_INITGLEXICAL:
       case JSOP_SETPROP:
       case JSOP_STRICTSETPROP:
       case JSOP_SETNAME:
       case JSOP_STRICTSETNAME:
       case JSOP_SETGNAME:
       case JSOP_STRICTSETGNAME: {
-        ICSetProp_Fallback::Compiler compiler(cx);
-        if (!addIC(pc, compiler.getStub(&stubSpace))) {
+        ICStub* stub = alloc.newStub<ICSetProp_Fallback>(Kind::SetProp);
+        if (!addIC(pc, stub)) {
           return nullptr;
         }
         break;
       }
       case JSOP_GETPROP:
       case JSOP_CALLPROP:
       case JSOP_LENGTH:
-      case JSOP_GETPROP_SUPER:
       case JSOP_GETBOUNDNAME: {
-        bool hasReceiver = (op == JSOP_GETPROP_SUPER);
-        ICGetProp_Fallback::Compiler compiler(cx, hasReceiver);
-        if (!addIC(pc, compiler.getStub(&stubSpace))) {
+        ICStub* stub = alloc.newStub<ICGetProp_Fallback>(Kind::GetProp);
+        if (!addIC(pc, stub)) {
+          return nullptr;
+        }
+        break;
+      }
+      case JSOP_GETPROP_SUPER: {
+        ICStub* stub = alloc.newStub<ICGetProp_Fallback>(Kind::GetPropSuper);
+        if (!addIC(pc, stub)) {
           return nullptr;
         }
         break;
       }
       case JSOP_GETELEM:
-      case JSOP_CALLELEM:
+      case JSOP_CALLELEM: {
+        ICStub* stub = alloc.newStub<ICGetElem_Fallback>(Kind::GetElem);
+        if (!addIC(pc, stub)) {
+          return nullptr;
+        }
+        break;
+      }
       case JSOP_GETELEM_SUPER: {
-        bool hasReceiver = (op == JSOP_GETELEM_SUPER);
-        ICGetElem_Fallback::Compiler stubCompiler(cx, hasReceiver);
-        if (!addIC(pc, stubCompiler.getStub(&stubSpace))) {
+        ICStub* stub = alloc.newStub<ICGetElem_Fallback>(Kind::GetElemSuper);
+        if (!addIC(pc, stub)) {
           return nullptr;
         }
         break;
       }
       case JSOP_IN: {
-        ICIn_Fallback::Compiler stubCompiler(cx);
-        if (!addIC(pc, stubCompiler.getStub(&stubSpace))) {
+        ICStub* stub = alloc.newStub<ICIn_Fallback>(Kind::In);
+        if (!addIC(pc, stub)) {
           return nullptr;
         }
         break;
       }
       case JSOP_HASOWN: {
-        ICHasOwn_Fallback::Compiler stubCompiler(cx);
-        if (!addIC(pc, stubCompiler.getStub(&stubSpace))) {
+        ICStub* stub = alloc.newStub<ICHasOwn_Fallback>(Kind::HasOwn);
+        if (!addIC(pc, stub)) {
           return nullptr;
         }
         break;
       }
       case JSOP_GETNAME:
       case JSOP_GETGNAME: {
-        ICGetName_Fallback::Compiler stubCompiler(cx);
-        if (!addIC(pc, stubCompiler.getStub(&stubSpace))) {
+        ICStub* stub = alloc.newStub<ICGetName_Fallback>(Kind::GetName);
+        if (!addIC(pc, stub)) {
           return nullptr;
         }
         break;
       }
       case JSOP_BINDNAME:
       case JSOP_BINDGNAME: {
-        ICBindName_Fallback::Compiler stubCompiler(cx);
-        if (!addIC(pc, stubCompiler.getStub(&stubSpace))) {
+        ICStub* stub = alloc.newStub<ICBindName_Fallback>(Kind::BindName);
+        if (!addIC(pc, stub)) {
           return nullptr;
         }
         break;
       }
       case JSOP_GETALIASEDVAR:
       case JSOP_GETIMPORT: {
-        ICTypeMonitor_Fallback::Compiler compiler(cx, nullptr);
-        if (!addIC(pc, compiler.getStub(&stubSpace))) {
+        ICStub* stub =
+            alloc.newStub<ICTypeMonitor_Fallback>(Kind::TypeMonitor, nullptr);
+        if (!addIC(pc, stub)) {
           return nullptr;
         }
         break;
       }
       case JSOP_GETINTRINSIC: {
-        ICGetIntrinsic_Fallback::Compiler stubCompiler(cx);
-        if (!addIC(pc, stubCompiler.getStub(&stubSpace))) {
+        ICStub* stub =
+            alloc.newStub<ICGetIntrinsic_Fallback>(Kind::GetIntrinsic);
+        if (!addIC(pc, stub)) {
           return nullptr;
         }
         break;
       }
       case JSOP_CALL:
       case JSOP_CALL_IGNORES_RV:
       case JSOP_CALLITER:
-      case JSOP_SUPERCALL:
       case JSOP_FUNCALL:
       case JSOP_FUNAPPLY:
-      case JSOP_NEW:
       case JSOP_EVAL:
       case JSOP_STRICTEVAL: {
-        bool construct = JSOp(*pc) == JSOP_NEW || JSOp(*pc) == JSOP_SUPERCALL;
-        ICCall_Fallback::Compiler stubCompiler(cx,
-                                               /* isConstructing = */ construct,
-                                               /* isSpread = */ false);
-        if (!addIC(pc, stubCompiler.getStub(&stubSpace))) {
+        ICStub* stub = alloc.newStub<ICCall_Fallback>(Kind::Call);
+        if (!addIC(pc, stub)) {
+          return nullptr;
+        }
+        break;
+      }
+      case JSOP_SUPERCALL:
+      case JSOP_NEW: {
+        ICStub* stub = alloc.newStub<ICCall_Fallback>(Kind::CallConstructing);
+        if (!addIC(pc, stub)) {
           return nullptr;
         }
         break;
       }
       case JSOP_SPREADCALL:
-      case JSOP_SPREADSUPERCALL:
-      case JSOP_SPREADNEW:
       case JSOP_SPREADEVAL:
       case JSOP_STRICTSPREADEVAL: {
-        bool construct =
-            JSOp(*pc) == JSOP_SPREADNEW || JSOp(*pc) == JSOP_SPREADSUPERCALL;
-        ICCall_Fallback::Compiler stubCompiler(cx,
-                                               /* isConstructing = */ construct,
-                                               /* isSpread = */ true);
-        if (!addIC(pc, stubCompiler.getStub(&stubSpace))) {
+        ICStub* stub = alloc.newStub<ICCall_Fallback>(Kind::SpreadCall);
+        if (!addIC(pc, stub)) {
+          return nullptr;
+        }
+        break;
+      }
+      case JSOP_SPREADSUPERCALL:
+      case JSOP_SPREADNEW: {
+        ICStub* stub =
+            alloc.newStub<ICCall_Fallback>(Kind::SpreadCallConstructing);
+        if (!addIC(pc, stub)) {
           return nullptr;
         }
         break;
       }
       case JSOP_INSTANCEOF: {
-        ICInstanceOf_Fallback::Compiler stubCompiler(cx);
-        if (!addIC(pc, stubCompiler.getStub(&stubSpace))) {
+        ICStub* stub = alloc.newStub<ICInstanceOf_Fallback>(Kind::InstanceOf);
+        if (!addIC(pc, stub)) {
           return nullptr;
         }
         break;
       }
       case JSOP_TYPEOF:
       case JSOP_TYPEOFEXPR: {
-        ICTypeOf_Fallback::Compiler stubCompiler(cx);
-        if (!addIC(pc, stubCompiler.getStub(&stubSpace))) {
+        ICStub* stub = alloc.newStub<ICTypeOf_Fallback>(Kind::TypeOf);
+        if (!addIC(pc, stub)) {
           return nullptr;
         }
         break;
       }
       case JSOP_ITER: {
-        ICGetIterator_Fallback::Compiler compiler(cx);
-        if (!addIC(pc, compiler.getStub(&stubSpace))) {
+        ICStub* stub = alloc.newStub<ICGetIterator_Fallback>(Kind::GetIterator);
+        if (!addIC(pc, stub)) {
           return nullptr;
         }
         break;
       }
       case JSOP_REST: {
         ArrayObject* templateObject = ObjectGroup::newArrayObject(
             cx, nullptr, 0, TenuredObject,
             ObjectGroup::NewArrayKind::UnknownIndex);
         if (!templateObject) {
           return nullptr;
         }
-        ICRest_Fallback::Compiler compiler(cx, templateObject);
-        if (!addIC(pc, compiler.getStub(&stubSpace))) {
+        ICStub* stub =
+            alloc.newStub<ICRest_Fallback>(Kind::Rest, templateObject);
+        if (!addIC(pc, stub)) {
           return nullptr;
         }
         break;
       }
       default:
         MOZ_CRASH("JOF_IC op not handled");
     }
   }
@@ -499,30 +568,29 @@ bool ICStub::makesGCCalls() const {
       return toCacheIR_Monitored()->stubInfo()->makesGCCalls();
     case CacheIR_Updated:
       return toCacheIR_Updated()->stubInfo()->makesGCCalls();
     default:
       return NonCacheIRStubMakesGCCalls(kind());
   }
 }
 
-void ICStub::traceCode(JSTracer* trc, const char* name) {
-  JitCode* stubJitCode = jitCode();
-  TraceManuallyBarrieredEdge(trc, &stubJitCode, name);
-}
-
 void ICStub::updateCode(JitCode* code) {
   // Write barrier on the old code.
   JitCode::writeBarrierPre(jitCode());
   stubCode_ = code->raw();
 }
 
 /* static */
 void ICStub::trace(JSTracer* trc) {
-  traceCode(trc, "shared-stub-jitcode");
+  // Fallback stubs use runtime-wide trampoline code we don't need to trace.
+  if (!usesTrampolineCode()) {
+    JitCode* stubJitCode = jitCode();
+    TraceManuallyBarrieredEdge(trc, &stubJitCode, "baseline-ic-stub-code");
+  }
 
   // If the stub is a monitored fallback stub, then trace the monitor ICs
   // hanging off of that stub.  We don't need to worry about the regular
   // monitored stubs, because the regular monitored stubs will always have a
   // monitored fallback stub that references the same stub chain.
   if (isMonitoredFallback()) {
     ICTypeMonitor_Fallback* lastMonStub =
         toMonitoredFallbackStub()->maybeFallbackMonitorStub();
@@ -776,18 +844,17 @@ bool DoWarmUpCounterFallbackOSR(JSContex
   if (!info) {
     return false;
   }
   *infoPtr = info;
 
   return true;
 }
 
-bool ICWarmUpCounter_Fallback::Compiler::generateStubCode(
-    MacroAssembler& masm) {
+bool FallbackICCodeCompiler::emit_WarmUpCounter() {
   // Push a stub frame so that we can perform a non-tail call.
   enterStubFrame(masm, R1.scratchReg());
 
   Label noCompiledCode;
   // Call DoWarmUpCounterFallbackOSR to compile/check-for Ion-compiled function
   {
     // Push IonOsrTempData pointer storage
     masm.subFromStackPtr(Imm32(sizeof(void*)));
@@ -1000,22 +1067,24 @@ ICMonitoredStub::ICMonitoredStub(Kind ki
       firstMonitorStub_->toTypeMonitor_Fallback()->firstMonitorStub() ==
           firstMonitorStub_);
 }
 
 bool ICMonitoredFallbackStub::initMonitoringChain(JSContext* cx,
                                                   JSScript* script) {
   MOZ_ASSERT(fallbackMonitorStub_ == nullptr);
 
-  ICTypeMonitor_Fallback::Compiler compiler(cx, this);
   ICStubSpace* space = script->icScript()->fallbackStubSpace();
-  ICTypeMonitor_Fallback* stub = compiler.getStub(space);
+  FallbackStubAllocator alloc(cx, *space);
+  auto* stub = alloc.newStub<ICTypeMonitor_Fallback>(
+      BaselineICFallbackKind::TypeMonitor, this);
   if (!stub) {
     return false;
   }
+
   fallbackMonitorStub_ = stub;
   return true;
 }
 
 bool ICMonitoredFallbackStub::addMonitorStubForValue(JSContext* cx,
                                                      BaselineFrame* frame,
                                                      StackTypeSet* types,
                                                      HandleValue val) {
@@ -1025,18 +1094,19 @@ bool ICMonitoredFallbackStub::addMonitor
     return false;
   }
   return typeMonitorFallback->addMonitorStubForValue(cx, frame, types, val);
 }
 
 bool ICUpdatedStub::initUpdatingChain(JSContext* cx, ICStubSpace* space) {
   MOZ_ASSERT(firstUpdateStub_ == nullptr);
 
-  ICTypeUpdate_Fallback::Compiler compiler(cx);
-  ICTypeUpdate_Fallback* stub = compiler.getStub(space);
+  FallbackStubAllocator alloc(cx, *space);
+  auto* stub =
+      alloc.newStub<ICTypeUpdate_Fallback>(BaselineICFallbackKind::TypeUpdate);
   if (!stub) {
     return false;
   }
 
   firstUpdateStub_ = stub;
   return true;
 }
 
@@ -1044,148 +1114,152 @@ bool ICUpdatedStub::initUpdatingChain(JS
 ICStubSpace* ICStubCompiler::StubSpaceForStub(bool makesGCCalls,
                                               JSScript* script) {
   if (makesGCCalls) {
     return script->icScript()->fallbackStubSpace();
   }
   return script->zone()->jitZone()->optimizedStubSpace();
 }
 
+static void InitMacroAssemblerForICStub(StackMacroAssembler& masm) {
+#ifndef JS_USE_LINK_REGISTER
+  // The first value contains the return addres,
+  // which we pull into ICTailCallReg for tail calls.
+  masm.adjustFrame(sizeof(intptr_t));
+#endif
+#ifdef JS_CODEGEN_ARM
+  masm.setSecondScratchReg(BaselineSecondScratchReg);
+#endif
+}
+
 JitCode* ICStubCompiler::getStubCode() {
   JitRealm* realm = cx->realm()->jitRealm();
 
   // Check for existing cached stubcode.
   uint32_t stubKey = getKey();
   JitCode* stubCode = realm->getStubCode(stubKey);
   if (stubCode) {
     return stubCode;
   }
 
   // Compile new stubcode.
   JitContext jctx(cx, nullptr);
   StackMacroAssembler masm;
-#ifndef JS_USE_LINK_REGISTER
-  // The first value contains the return addres,
-  // which we pull into ICTailCallReg for tail calls.
-  masm.adjustFrame(sizeof(intptr_t));
-#endif
-#ifdef JS_CODEGEN_ARM
-  masm.setSecondScratchReg(BaselineSecondScratchReg);
-#endif
+  InitMacroAssemblerForICStub(masm);
 
   if (!generateStubCode(masm)) {
     return nullptr;
   }
   Linker linker(masm, "getStubCode");
   Rooted<JitCode*> newStubCode(cx, linker.newCode(cx, CodeKind::Baseline));
   if (!newStubCode) {
     return nullptr;
   }
 
   // Cache newly compiled stubcode.
   if (!realm->putStubCode(cx, stubKey, newStubCode)) {
     return nullptr;
   }
 
-  // After generating code, run postGenerateStubCode().  We must not fail
-  // after this point.
-  postGenerateStubCode(masm, newStubCode);
-
   MOZ_ASSERT(entersStubFrame_ == ICStub::NonCacheIRStubMakesGCCalls(kind));
   MOZ_ASSERT(!inStubFrame_);
 
 #ifdef JS_ION_PERF
   writePerfSpewerJitCodeProfile(newStubCode, "BaselineIC");
 #endif
 
   return newStubCode;
 }
 
-bool ICStubCompiler::tailCallVMInternal(MacroAssembler& masm,
-                                        TailCallVMFunctionId id) {
+bool ICStubCompilerBase::tailCallVMInternal(MacroAssembler& masm,
+                                            TailCallVMFunctionId id) {
   TrampolinePtr code = cx->runtime()->jitRuntime()->getVMWrapper(id);
   const VMFunctionData& fun = GetVMFunction(id);
   MOZ_ASSERT(fun.expectTailCall == TailCall);
   uint32_t argSize = fun.explicitStackSlots() * sizeof(void*);
   EmitBaselineTailCallVM(code, masm, argSize);
   return true;
 }
 
-bool ICStubCompiler::callVMInternal(MacroAssembler& masm, VMFunctionId id) {
+bool ICStubCompilerBase::callVMInternal(MacroAssembler& masm, VMFunctionId id) {
   MOZ_ASSERT(inStubFrame_);
 
   TrampolinePtr code = cx->runtime()->jitRuntime()->getVMWrapper(id);
   MOZ_ASSERT(GetVMFunction(id).expectTailCall == NonTailCall);
 
   EmitBaselineCallVM(code, masm);
   return true;
 }
 
 template <typename Fn, Fn fn>
-bool ICStubCompiler::callVM(MacroAssembler& masm) {
+bool ICStubCompilerBase::callVM(MacroAssembler& masm) {
   VMFunctionId id = VMFunctionToId<Fn, fn>::id;
   return callVMInternal(masm, id);
 }
 
 template <typename Fn, Fn fn>
-bool ICStubCompiler::tailCallVM(MacroAssembler& masm) {
+bool ICStubCompilerBase::tailCallVM(MacroAssembler& masm) {
   TailCallVMFunctionId id = TailCallVMFunctionToId<Fn, fn>::id;
   return tailCallVMInternal(masm, id);
 }
 
-void ICStubCompiler::enterStubFrame(MacroAssembler& masm, Register scratch) {
+void ICStubCompilerBase::enterStubFrame(MacroAssembler& masm,
+                                        Register scratch) {
   EmitBaselineEnterStubFrame(masm, scratch);
 #ifdef DEBUG
   framePushedAtEnterStubFrame_ = masm.framePushed();
 #endif
 
   MOZ_ASSERT(!inStubFrame_);
   inStubFrame_ = true;
 
 #ifdef DEBUG
   entersStubFrame_ = true;
 #endif
 }
 
-void ICStubCompiler::assumeStubFrame() {
+void ICStubCompilerBase::assumeStubFrame() {
   MOZ_ASSERT(!inStubFrame_);
   inStubFrame_ = true;
 
 #ifdef DEBUG
   entersStubFrame_ = true;
 
   // |framePushed| isn't tracked precisely in ICStubs, so simply assume it to
   // be STUB_FRAME_SIZE so that assertions don't fail in leaveStubFrame.
   framePushedAtEnterStubFrame_ = STUB_FRAME_SIZE;
 #endif
 }
 
-void ICStubCompiler::leaveStubFrame(MacroAssembler& masm, bool calledIntoIon) {
+void ICStubCompilerBase::leaveStubFrame(MacroAssembler& masm,
+                                        bool calledIntoIon) {
   MOZ_ASSERT(entersStubFrame_ && inStubFrame_);
   inStubFrame_ = false;
 
 #ifdef DEBUG
   masm.setFramePushed(framePushedAtEnterStubFrame_);
   if (calledIntoIon) {
     masm.adjustFrame(sizeof(intptr_t));  // Calls into ion have this extra.
   }
 #endif
   EmitBaselineLeaveStubFrame(masm, calledIntoIon);
 }
 
-void ICStubCompiler::pushStubPayload(MacroAssembler& masm, Register scratch) {
+void ICStubCompilerBase::pushStubPayload(MacroAssembler& masm,
+                                         Register scratch) {
   if (inStubFrame_) {
     masm.loadPtr(Address(BaselineFrameReg, 0), scratch);
     masm.pushBaselineFramePtr(scratch, scratch);
   } else {
     masm.pushBaselineFramePtr(BaselineFrameReg, scratch);
   }
 }
 
-void ICStubCompiler::PushStubPayload(MacroAssembler& masm, Register scratch) {
+void ICStubCompilerBase::PushStubPayload(MacroAssembler& masm,
+                                         Register scratch) {
   pushStubPayload(masm, scratch);
   masm.adjustFrame(sizeof(intptr_t));
 }
 
 void ICScript::noteAccessedGetter(uint32_t pcOffset) {
   ICEntry& entry = icEntryFromPCOffset(pcOffset);
   ICFallbackStub* stub = entry.fallbackStub();
 
@@ -1422,17 +1496,17 @@ bool DoTypeMonitorFallback(JSContext* cx
   } else {
     types = TypeScript::BytecodeTypes(script, pc);
     TypeScript::Monitor(cx, script, pc, types, value);
   }
 
   return stub->addMonitorStubForValue(cx, frame, types, value);
 }
 
-bool ICTypeMonitor_Fallback::Compiler::generateStubCode(MacroAssembler& masm) {
+bool FallbackICCodeCompiler::emit_TypeMonitor() {
   MOZ_ASSERT(R0 == JSReturnOperand);
 
   // Restore the tail call register.
   EmitRestoreTailCallReg(masm);
 
   masm.pushValue(R0);
   masm.push(ICStubReg);
   masm.pushBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
@@ -1761,17 +1835,17 @@ bool DoTypeUpdateFallback(JSContext* cx,
     // instance we may reallocate dynamic slots before calling this),
     // so ignore OOMs if we failed to attach a stub.
     cx->recoverFromOutOfMemory();
   }
 
   return true;
 }
 
-bool ICTypeUpdate_Fallback::Compiler::generateStubCode(MacroAssembler& masm) {
+bool FallbackICCodeCompiler::emit_TypeUpdate() {
   // Just store false into R1.scratchReg() and return.
   masm.move32(Imm32(0), R1.scratchReg());
   EmitReturnFromIC(masm);
   return true;
 }
 
 bool ICTypeUpdate_PrimitiveSet::Compiler::generateStubCode(
     MacroAssembler& masm) {
@@ -1887,17 +1961,17 @@ bool DoToBoolFallback(JSContext* cx, Bas
                                    BaselineCacheIRStubKind::Regular, arg);
 
   bool cond = ToBoolean(arg);
   ret.setBoolean(cond);
 
   return true;
 }
 
-bool ICToBool_Fallback::Compiler::generateStubCode(MacroAssembler& masm) {
+bool FallbackICCodeCompiler::emit_ToBool() {
   MOZ_ASSERT(R0 == JSReturnOperand);
 
   // Restore the tail call register.
   EmitRestoreTailCallReg(masm);
 
   // Push arguments.
   masm.pushValue(R0);
   masm.push(ICStubReg);
@@ -2101,24 +2175,24 @@ bool DoGetElemSuperFallback(JSContext* c
   if (rhs.isNumber() && rhs.isDouble() &&
       !mozilla::NumberEqualsInt32(rhs.toDouble(), &representable)) {
     stub->setSawNonIntegerIndex();
   }
 
   return true;
 }
 
-bool ICGetElem_Fallback::Compiler::generateStubCode(MacroAssembler& masm) {
+bool FallbackICCodeCompiler::emitGetElem(bool hasReceiver) {
   MOZ_ASSERT(R0 == JSReturnOperand);
 
   // Restore the tail call register.
   EmitRestoreTailCallReg(masm);
 
   // Super property getters use a |this| that differs from base object
-  if (hasReceiver_) {
+  if (hasReceiver) {
     // State: receiver in R0, index in R1, obj on the stack
 
     // Ensure stack is fully synced for the expression decompiler.
     // We need: receiver, index, obj
     masm.pushValue(R0);
     masm.pushValue(R1);
     masm.pushValue(Address(masm.getStackPointer(), sizeof(Value) * 2));
 
@@ -2152,17 +2226,23 @@ bool ICGetElem_Fallback::Compiler::gener
       return false;
     }
   }
 
   // This is the resume point used when bailout rewrites call stack to undo
   // Ion inlined frames. The return address pushed onto reconstructed stack
   // will point here.
   assumeStubFrame();
-  bailoutReturnOffset_.bind(masm.currentOffset());
+  if (hasReceiver) {
+    code.initBailoutReturnOffset(BailoutReturnKind::GetElemSuper,
+                                 masm.currentOffset());
+  } else {
+    code.initBailoutReturnOffset(BailoutReturnKind::GetElem,
+                                 masm.currentOffset());
+  }
 
   leaveStubFrame(masm, true);
 
   // When we get here, ICStubReg contains the ICGetElem_Fallback stub,
   // which we can't use to enter the TypeMonitor IC, because it's a
   // MonitoredFallbackStub instead of a MonitoredStub. So, we cheat. Note that
   // we must have a non-null fallbackMonitorStub here because InitFromBailout
   // delazifies.
@@ -2170,22 +2250,22 @@ bool ICGetElem_Fallback::Compiler::gener
                        ICMonitoredFallbackStub::offsetOfFallbackMonitorStub()),
                ICStubReg);
   EmitEnterTypeMonitorIC(masm,
                          ICTypeMonitor_Fallback::offsetOfFirstMonitorStub());
 
   return true;
 }
 
-void ICGetElem_Fallback::Compiler::postGenerateStubCode(MacroAssembler& masm,
-                                                        Handle<JitCode*> code) {
-  BailoutReturnStub kind = hasReceiver_ ? BailoutReturnStub::GetElemSuper
-                                        : BailoutReturnStub::GetElem;
-  void* address = code->raw() + bailoutReturnOffset_.offset();
-  cx->realm()->jitRealm()->initBailoutReturnAddr(address, getKey(), kind);
+bool FallbackICCodeCompiler::emit_GetElem() {
+  return emitGetElem(/* hasReceiver = */ false);
+}
+
+bool FallbackICCodeCompiler::emit_GetElemSuper() {
+  return emitGetElem(/* hasReceiver = */ true);
 }
 
 static void SetUpdateStubData(ICCacheIR_Updated* stub,
                               const PropertyTypeCheckInfo* info) {
   if (info->isSet()) {
     stub->updateStubGroup() = info->group();
     stub->updateStubId() = info->id();
   }
@@ -2321,17 +2401,17 @@ bool DoSetElemFallback(JSContext* cx, Ba
     if (!attached && !isTemporarilyUnoptimizable) {
       stub->state().trackNotAttached();
     }
   }
 
   return true;
 }
 
-bool ICSetElem_Fallback::Compiler::generateStubCode(MacroAssembler& masm) {
+bool FallbackICCodeCompiler::emit_SetElem() {
   MOZ_ASSERT(R0 == JSReturnOperand);
 
   EmitRestoreTailCallReg(masm);
 
   // State: R0: object, R1: index, stack: rhs.
   // For the decompiler, the stack has to be: object, index, rhs,
   // so we push the index, then overwrite the rhs Value with R0
   // and push the rhs value.
@@ -2462,17 +2542,17 @@ bool DoInFallback(JSContext* cx, Baselin
   if (!OperatorIn(cx, key, obj, &cond)) {
     return false;
   }
   res.setBoolean(cond);
 
   return true;
 }
 
-bool ICIn_Fallback::Compiler::generateStubCode(MacroAssembler& masm) {
+bool FallbackICCodeCompiler::emit_In() {
   EmitRestoreTailCallReg(masm);
 
   // Sync for the decompiler.
   masm.pushValue(R0);
   masm.pushValue(R1);
 
   // Push arguments.
   masm.pushValue(R1);
@@ -2504,17 +2584,17 @@ bool DoHasOwnFallback(JSContext* cx, Bas
   if (!HasOwnProperty(cx, objValue, keyValue, &found)) {
     return false;
   }
 
   res.setBoolean(found);
   return true;
 }
 
-bool ICHasOwn_Fallback::Compiler::generateStubCode(MacroAssembler& masm) {
+bool FallbackICCodeCompiler::emit_HasOwn() {
   EmitRestoreTailCallReg(masm);
 
   // Sync for the decompiler.
   masm.pushValue(R0);
   masm.pushValue(R1);
 
   // Push arguments.
   masm.pushValue(R1);
@@ -2567,17 +2647,17 @@ bool DoGetNameFallback(JSContext* cx, Ba
   // Add a type monitor stub for the resulting value.
   if (!stub->addMonitorStubForValue(cx, frame, types, res)) {
     return false;
   }
 
   return true;
 }
 
-bool ICGetName_Fallback::Compiler::generateStubCode(MacroAssembler& masm) {
+bool FallbackICCodeCompiler::emit_GetName() {
   MOZ_ASSERT(R0 == JSReturnOperand);
 
   EmitRestoreTailCallReg(masm);
 
   masm.push(R0.scratchReg());
   masm.push(ICStubReg);
   pushStubPayload(masm, R0.scratchReg());
 
@@ -2611,17 +2691,17 @@ bool DoBindNameFallback(JSContext* cx, B
   if (!LookupNameUnqualified(cx, name, envChain, &scope)) {
     return false;
   }
 
   res.setObject(*scope);
   return true;
 }
 
-bool ICBindName_Fallback::Compiler::generateStubCode(MacroAssembler& masm) {
+bool FallbackICCodeCompiler::emit_BindName() {
   MOZ_ASSERT(R0 == JSReturnOperand);
 
   EmitRestoreTailCallReg(masm);
 
   masm.push(R0.scratchReg());
   masm.push(ICStubReg);
   pushStubPayload(masm, R0.scratchReg());
 
@@ -2657,17 +2737,17 @@ bool DoGetIntrinsicFallback(JSContext* c
   TypeScript::Monitor(cx, script, pc, res);
 
   TryAttachStub<GetIntrinsicIRGenerator>("GetIntrinsic", cx, frame, stub,
                                          BaselineCacheIRStubKind::Regular, res);
 
   return true;
 }
 
-bool ICGetIntrinsic_Fallback::Compiler::generateStubCode(MacroAssembler& masm) {
+bool FallbackICCodeCompiler::emit_GetIntrinsic() {
   EmitRestoreTailCallReg(masm);
 
   masm.push(ICStubReg);
   pushStubPayload(masm, R0.scratchReg());
 
   using Fn = bool (*)(JSContext*, BaselineFrame*, ICGetIntrinsic_Fallback*,
                       MutableHandleValue);
   return tailCallVM<Fn, DoGetIntrinsicFallback>(masm);
@@ -2832,23 +2912,23 @@ bool DoGetPropSuperFallback(JSContext* c
   // Add a type monitor stub for the resulting value.
   if (!stub->addMonitorStubForValue(cx, frame, types, res)) {
     return false;
   }
 
   return true;
 }
 
-bool ICGetProp_Fallback::Compiler::generateStubCode(MacroAssembler& masm) {
+bool FallbackICCodeCompiler::emitGetProp(bool hasReceiver) {
   MOZ_ASSERT(R0 == JSReturnOperand);
 
   EmitRestoreTailCallReg(masm);
 
   // Super property getters use a |this| that differs from base object
-  if (hasReceiver_) {
+  if (hasReceiver) {
     // Push arguments.
     masm.pushValue(R0);
     masm.pushValue(R1);
     masm.push(ICStubReg);
     masm.pushBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
 
     using Fn = bool (*)(JSContext*, BaselineFrame*, ICGetProp_Fallback*,
                         HandleValue, MutableHandleValue, MutableHandleValue);
@@ -2870,17 +2950,23 @@ bool ICGetProp_Fallback::Compiler::gener
       return false;
     }
   }
 
   // This is the resume point used when bailout rewrites call stack to undo
   // Ion inlined frames. The return address pushed onto reconstructed stack
   // will point here.
   assumeStubFrame();
-  bailoutReturnOffset_.bind(masm.currentOffset());
+  if (hasReceiver) {
+    code.initBailoutReturnOffset(BailoutReturnKind::GetPropSuper,
+                                 masm.currentOffset());
+  } else {
+    code.initBailoutReturnOffset(BailoutReturnKind::GetProp,
+                                 masm.currentOffset());
+  }
 
   leaveStubFrame(masm, true);
 
   // When we get here, ICStubReg contains the ICGetProp_Fallback stub,
   // which we can't use to enter the TypeMonitor IC, because it's a
   // MonitoredFallbackStub instead of a MonitoredStub. So, we cheat. Note that
   // we must have a non-null fallbackMonitorStub here because InitFromBailout
   // delazifies.
@@ -2888,22 +2974,22 @@ bool ICGetProp_Fallback::Compiler::gener
                        ICMonitoredFallbackStub::offsetOfFallbackMonitorStub()),
                ICStubReg);
   EmitEnterTypeMonitorIC(masm,
                          ICTypeMonitor_Fallback::offsetOfFirstMonitorStub());
 
   return true;
 }
 
-void ICGetProp_Fallback::Compiler::postGenerateStubCode(MacroAssembler& masm,
-                                                        Handle<JitCode*> code) {
-  BailoutReturnStub kind = hasReceiver_ ? BailoutReturnStub::GetPropSuper
-                                        : BailoutReturnStub::GetProp;
-  void* address = code->raw() + bailoutReturnOffset_.offset();
-  cx->realm()->jitRealm()->initBailoutReturnAddr(address, getKey(), kind);
+bool FallbackICCodeCompiler::emit_GetProp() {
+  return emitGetProp(/* hasReceiver = */ false);
+}
+
+bool FallbackICCodeCompiler::emit_GetPropSuper() {
+  return emitGetProp(/* hasReceiver = */ true);
 }
 
 //
 // SetProp_Fallback
 //
 
 bool DoSetPropFallback(JSContext* cx, BaselineFrame* frame,
                        ICSetProp_Fallback* stub, Value* stack, HandleValue lhs,
@@ -3040,17 +3126,17 @@ bool DoSetPropFallback(JSContext* cx, Ba
     if (!attached && !isTemporarilyUnoptimizable) {
       stub->state().trackNotAttached();
     }
   }
 
   return true;
 }
 
-bool ICSetProp_Fallback::Compiler::generateStubCode(MacroAssembler& masm) {
+bool FallbackICCodeCompiler::emit_SetProp() {
   MOZ_ASSERT(R0 == JSReturnOperand);
 
   EmitRestoreTailCallReg(masm);
 
   // Ensure stack is fully synced for the expression decompiler.
   // Overwrite the RHS value on top of the stack with the object, then push
   // the RHS in R1 on top of that.
   masm.storeValue(R0, Address(masm.getStackPointer(), 0));
@@ -3074,31 +3160,25 @@ bool ICSetProp_Fallback::Compiler::gener
   if (!tailCallVM<Fn, DoSetPropFallback>(masm)) {
     return false;
   }
 
   // This is the resume point used when bailout rewrites call stack to undo
   // Ion inlined frames. The return address pushed onto reconstructed stack
   // will point here.
   assumeStubFrame();
-  bailoutReturnOffset_.bind(masm.currentOffset());
+  code.initBailoutReturnOffset(BailoutReturnKind::SetProp,
+                               masm.currentOffset());
 
   leaveStubFrame(masm, true);
   EmitReturnFromIC(masm);
 
   return true;
 }
 
-void ICSetProp_Fallback::Compiler::postGenerateStubCode(MacroAssembler& masm,
-                                                        Handle<JitCode*> code) {
-  BailoutReturnStub kind = BailoutReturnStub::SetProp;
-  void* address = code->raw() + bailoutReturnOffset_.offset();
-  cx->realm()->jitRealm()->initBailoutReturnAddr(address, getKey(), kind);
-}
-
 //
 // Call_Fallback
 //
 
 static bool TryAttachFunApplyStub(JSContext* cx, ICCall_Fallback* stub,
                                   HandleScript script, jsbytecode* pc,
                                   HandleValue thisv, uint32_t argc, Value* argv,
                                   ICTypeMonitor_Fallback* typeMonitorFallback,
@@ -3881,17 +3961,17 @@ bool DoSpreadCallFallback(JSContext* cx,
   StackTypeSet* types = TypeScript::BytecodeTypes(script, pc);
   if (!stub->addMonitorStubForValue(cx, frame, types, res)) {
     return false;
   }
 
   return true;
 }
 
-void ICCallStubCompiler::pushCallArguments(MacroAssembler& masm,
+void ICStubCompilerBase::pushCallArguments(MacroAssembler& masm,
                                            AllocatableGeneralRegisterSet regs,
                                            Register argcReg, bool isJitCall,
                                            bool isConstructing) {
   MOZ_ASSERT(!regs.has(argcReg));
 
   // Account for new.target
   Register count = regs.takeAny();
 
@@ -4190,50 +4270,54 @@ void ICCallStubCompiler::pushArrayArgume
   masm.bind(&copyStart);
   masm.branchPtr(Assembler::Equal, endReg, startReg, &copyDone);
   masm.subPtr(Imm32(sizeof(Value)), endReg);
   masm.pushValue(Address(endReg, 0));
   masm.jump(&copyStart);
   masm.bind(&copyDone);
 }
 
-bool ICCall_Fallback::Compiler::generateStubCode(MacroAssembler& masm) {
+bool FallbackICCodeCompiler::emitCall(bool isSpread, bool isConstructing) {
   MOZ_ASSERT(R0 == JSReturnOperand);
 
   // Values are on the stack left-to-right. Calling convention wants them
   // right-to-left so duplicate them on the stack in reverse order.
   // |this| and callee are pushed last.
 
   AllocatableGeneralRegisterSet regs(availableGeneralRegs(0));
 
-  if (MOZ_UNLIKELY(isSpread_)) {
+  if (MOZ_UNLIKELY(isSpread)) {
     // Push a stub frame so that we can perform a non-tail call.
     enterStubFrame(masm, R1.scratchReg());
 
     // Use BaselineFrameReg instead of BaselineStackReg, because
     // BaselineFrameReg and BaselineStackReg hold the same value just after
     // calling enterStubFrame.
 
     // newTarget
-    if (isConstructing_) {
+    uint32_t valueOffset = 0;
+    if (isConstructing) {
       masm.pushValue(Address(BaselineFrameReg, STUB_FRAME_SIZE));
+      valueOffset++;
     }
 
     // array
-    uint32_t valueOffset = isConstructing_;
     masm.pushValue(Address(BaselineFrameReg,
-                           valueOffset++ * sizeof(Value) + STUB_FRAME_SIZE));
+                           valueOffset * sizeof(Value) + STUB_FRAME_SIZE));
+    valueOffset++;
 
     // this
     masm.pushValue(Address(BaselineFrameReg,
-                           valueOffset++ * sizeof(Value) + STUB_FRAME_SIZE));
+                           valueOffset * sizeof(Value) + STUB_FRAME_SIZE));
+    valueOffset++;
 
     // callee
     masm.pushValue(Address(BaselineFrameReg,
-                           valueOffset++ * sizeof(Value) + STUB_FRAME_SIZE));
+                           valueOffset * sizeof(Value) + STUB_FRAME_SIZE));
+    valueOffset++;
 
     masm.push(masm.getStackPointer());
     masm.push(ICStubReg);
 
     PushStubPayload(masm, R0.scratchReg());
 
     using Fn = bool (*)(JSContext*, BaselineFrame*, ICCall_Fallback*, Value*,
                         MutableHandleValue);
@@ -4250,17 +4334,17 @@ bool ICCall_Fallback::Compiler::generate
   }
 
   // Push a stub frame so that we can perform a non-tail call.
   enterStubFrame(masm, R1.scratchReg());
 
   regs.take(R0.scratchReg());  // argc.
 
   pushCallArguments(masm, regs, R0.scratchReg(), /* isJitCall = */ false,
-                    isConstructing_);
+                    isConstructing);
 
   masm.push(masm.getStackPointer());
   masm.push(R0.scratchReg());
   masm.push(ICStubReg);
 
   PushStubPayload(masm, R0.scratchReg());
 
   using Fn = bool (*)(JSContext*, BaselineFrame*, ICCall_Fallback*, uint32_t,
@@ -4271,28 +4355,35 @@ bool ICCall_Fallback::Compiler::generate
 
   leaveStubFrame(masm);
   EmitReturnFromIC(masm);
 
   // This is the resume point used when bailout rewrites call stack to undo
   // Ion inlined frames. The return address pushed onto reconstructed stack
   // will point here.
   assumeStubFrame();
-  bailoutReturnOffset_.bind(masm.currentOffset());
+
+  MOZ_ASSERT(!isSpread);
+
+  if (isConstructing) {
+    code.initBailoutReturnOffset(BailoutReturnKind::New, masm.currentOffset());
+  } else {
+    code.initBailoutReturnOffset(BailoutReturnKind::Call, masm.currentOffset());
+  }
 
   // Load passed-in ThisV into R1 just in case it's needed.  Need to do this
   // before we leave the stub frame since that info will be lost.
   // Current stack:  [...., ThisV, ActualArgc, CalleeToken, Descriptor ]
   masm.loadValue(Address(masm.getStackPointer(), 3 * sizeof(size_t)), R1);
 
   leaveStubFrame(masm, true);
 
   // If this is a |constructing| call, if the callee returns a non-object, we
   // replace it with the |this| object passed in.
-  if (isConstructing_) {
+  if (isConstructing) {
     MOZ_ASSERT(JSReturnOperand == R0);
     Label skipThisReplace;
 
     masm.branchTestObject(Assembler::Equal, JSReturnOperand, &skipThisReplace);
     masm.moveValue(R1, R0);
 #ifdef DEBUG
     masm.branchTestObject(Assembler::Equal, JSReturnOperand, &skipThisReplace);
     masm.assumeUnreachable("Failed to return object in constructing call.");
@@ -4310,26 +4401,30 @@ bool ICCall_Fallback::Compiler::generate
                        ICMonitoredFallbackStub::offsetOfFallbackMonitorStub()),
                ICStubReg);
   EmitEnterTypeMonitorIC(masm,
                          ICTypeMonitor_Fallback::offsetOfFirstMonitorStub());
 
   return true;
 }
 
-void ICCall_Fallback::Compiler::postGenerateStubCode(MacroAssembler& masm,
-                                                     Handle<JitCode*> code) {
-  if (MOZ_UNLIKELY(isSpread_)) {
-    return;
-  }
-
-  void* address = code->raw() + bailoutReturnOffset_.offset();
-  BailoutReturnStub kind =
-      isConstructing_ ? BailoutReturnStub::New : BailoutReturnStub::Call;
-  cx->realm()->jitRealm()->initBailoutReturnAddr(address, getKey(), kind);
+bool FallbackICCodeCompiler::emit_Call() {
+  return emitCall(/* isSpread = */ false, /* isConstructing = */ false);
+}
+
+bool FallbackICCodeCompiler::emit_CallConstructing() {
+  return emitCall(/* isSpread = */ false, /* isConstructing = */ true);
+}
+
+bool FallbackICCodeCompiler::emit_SpreadCall() {
+  return emitCall(/* isSpread = */ true, /* isConstructing = */ false);
+}
+
+bool FallbackICCodeCompiler::emit_SpreadCallConstructing() {
+  return emitCall(/* isSpread = */ true, /* isConstructing = */ true);
 }
 
 bool ICCallScriptedCompiler::generateStubCode(MacroAssembler& masm) {
   Label failure;
   AllocatableGeneralRegisterSet regs(availableGeneralRegs(0));
   bool canUseTailCallReg = regs.has(ICTailCallReg);
 
   Register argcReg = R0.scratchReg();
@@ -5276,17 +5371,17 @@ bool DoGetIteratorFallback(JSContext* cx
   if (!iterobj) {
     return false;
   }
 
   res.setObject(*iterobj);
   return true;
 }
 
-bool ICGetIterator_Fallback::Compiler::generateStubCode(MacroAssembler& masm) {
+bool FallbackICCodeCompiler::emit_GetIterator() {
   EmitRestoreTailCallReg(masm);
 
   // Sync stack for the decompiler.
   masm.pushValue(R0);
 
   masm.pushValue(R0);
   masm.push(ICStubReg);
   pushStubPayload(masm, R0.scratchReg());
@@ -5334,17 +5429,17 @@ bool DoInstanceOfFallback(JSContext* cx,
   EnsureTrackPropertyTypes(cx, obj, NameToId(cx->names().prototype));
 
   TryAttachStub<InstanceOfIRGenerator>("InstanceOf", cx, frame, stub,
                                        BaselineCacheIRStubKind::Regular, lhs,
                                        obj);
   return true;
 }
 
-bool ICInstanceOf_Fallback::Compiler::generateStubCode(MacroAssembler& masm) {
+bool FallbackICCodeCompiler::emit_InstanceOf() {
   EmitRestoreTailCallReg(masm);
 
   // Sync stack for the decompiler.
   masm.pushValue(R0);
   masm.pushValue(R1);
 
   masm.pushValue(R1);
   masm.pushValue(R0);
@@ -5370,17 +5465,17 @@ bool DoTypeOfFallback(JSContext* cx, Bas
                                    BaselineCacheIRStubKind::Regular, val);
 
   JSType type = js::TypeOfValue(val);
   RootedString string(cx, TypeName(type, cx->names()));
   res.setString(string);
   return true;
 }
 
-bool ICTypeOf_Fallback::Compiler::generateStubCode(MacroAssembler& masm) {
+bool FallbackICCodeCompiler::emit_TypeOf() {
   EmitRestoreTailCallReg(masm);
 
   masm.pushValue(R0);
   masm.push(ICStubReg);
   pushStubPayload(masm, R0.scratchReg());
 
   using Fn = bool (*)(JSContext*, BaselineFrame*, ICTypeOf_Fallback*,
                       HandleValue, MutableHandleValue);
@@ -5459,17 +5554,17 @@ bool DoRestFallback(JSContext* cx, Basel
                                   ObjectGroup::NewArrayKind::UnknownIndex);
   if (!obj) {
     return false;
   }
   res.setObject(*obj);
   return true;
 }
 
-bool ICRest_Fallback::Compiler::generateStubCode(MacroAssembler& masm) {
+bool FallbackICCodeCompiler::emit_Rest() {
   EmitRestoreTailCallReg(masm);
 
   masm.push(ICStubReg);
   pushStubPayload(masm, R0.scratchReg());
 
   using Fn = bool (*)(JSContext*, BaselineFrame*, ICRest_Fallback*,
                       MutableHandleValue);
   return tailCallVM<Fn, DoRestFallback>(masm);
@@ -5526,17 +5621,17 @@ bool DoUnaryArithFallback(JSContext* cx,
   }
 
   TryAttachStub<UnaryArithIRGenerator>("UniaryArith", cx, frame, stub,
                                        BaselineCacheIRStubKind::Regular, op,
                                        val, res);
   return true;
 }
 
-bool ICUnaryArith_Fallback::Compiler::generateStubCode(MacroAssembler& masm) {
+bool FallbackICCodeCompiler::emit_UnaryArith() {
   MOZ_ASSERT(R0 == JSReturnOperand);
 
   // Restore the tail call register.
   EmitRestoreTailCallReg(masm);
 
   // Ensure stack is fully synced for the expression decompiler.
   masm.pushValue(R0);
 
@@ -5650,17 +5745,17 @@ bool DoBinaryArithFallback(JSContext* cx
   }
 
   TryAttachStub<BinaryArithIRGenerator>("BinaryArith", cx, frame, stub,
                                         BaselineCacheIRStubKind::Regular, op,
                                         lhs, rhs, ret);
   return true;
 }
 
-bool ICBinaryArith_Fallback::Compiler::generateStubCode(MacroAssembler& masm) {
+bool FallbackICCodeCompiler::emit_BinaryArith() {
   MOZ_ASSERT(R0 == JSReturnOperand);
 
   // Restore the tail call register.
   EmitRestoreTailCallReg(masm);
 
   // Ensure stack is fully synced for the expression decompiler.
   masm.pushValue(R0);
   masm.pushValue(R1);
@@ -5747,17 +5842,17 @@ bool DoCompareFallback(JSContext* cx, Ba
   ret.setBoolean(out);
 
   TryAttachStub<CompareIRGenerator>("Compare", cx, frame, stub,
                                     BaselineCacheIRStubKind::Regular, op, lhs,
                                     rhs);
   return true;
 }
 
-bool ICCompare_Fallback::Compiler::generateStubCode(MacroAssembler& masm) {
+bool FallbackICCodeCompiler::emit_Compare() {
   MOZ_ASSERT(R0 == JSReturnOperand);
 
   // Restore the tail call register.
   EmitRestoreTailCallReg(masm);
 
   // Ensure stack is fully synced for the expression decompiler.
   masm.pushValue(R0);
   masm.pushValue(R1);
@@ -5808,17 +5903,17 @@ bool DoNewArrayFallback(JSContext* cx, B
       stub->setTemplateObject(templateObject);
     }
   }
 
   res.setObject(*obj);
   return true;
 }
 
-bool ICNewArray_Fallback::Compiler::generateStubCode(MacroAssembler& masm) {
+bool FallbackICCodeCompiler::emit_NewArray() {
   EmitRestoreTailCallReg(masm);
 
   masm.push(R0.scratchReg());  // length
   masm.push(ICStubReg);        // stub.
   masm.pushBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
 
   using Fn = bool (*)(JSContext*, BaselineFrame*, ICNewArray_Fallback*,
                       uint32_t, MutableHandleValue);
@@ -5863,21 +5958,58 @@ bool DoNewObjectFallback(JSContext* cx, 
   if (!obj) {
     return false;
   }
 
   res.setObject(*obj);
   return true;
 }
 
-bool ICNewObject_Fallback::Compiler::generateStubCode(MacroAssembler& masm) {
+bool FallbackICCodeCompiler::emit_NewObject() {
   EmitRestoreTailCallReg(masm);
 
   masm.push(ICStubReg);  // stub.
   pushStubPayload(masm, R0.scratchReg());
 
   using Fn = bool (*)(JSContext*, BaselineFrame*, ICNewObject_Fallback*,
                       MutableHandleValue);
   return tailCallVM<Fn, DoNewObjectFallback>(masm);
 }
 
+bool JitRuntime::generateBaselineICFallbackCode(JSContext* cx) {
+  StackMacroAssembler masm;
+
+  BaselineICFallbackCode& fallbackCode = baselineICFallbackCode_.ref();
+  FallbackICCodeCompiler compiler(cx, fallbackCode, masm);
+
+  JitSpew(JitSpew_Codegen, "# Emitting Baseline IC fallback code");
+
+#define EMIT_CODE(kind)                                            \
+  {                                                                \
+    uint32_t offset = startTrampolineCode(masm);                   \
+    InitMacroAssemblerForICStub(masm);                             \
+    if (!compiler.emit_##kind()) {                                 \
+      return false;                                                \
+    }                                                              \
+    fallbackCode.initOffset(BaselineICFallbackKind::kind, offset); \
+  }
+  IC_BASELINE_FALLBACK_CODE_KIND_LIST(EMIT_CODE)
+#undef EMIT_CODE
+
+  Linker linker(masm, "BaselineICFallback");
+  JitCode* code = linker.newCode(cx, CodeKind::Other);
+  if (!code) {
+    return false;
+  }
+
+#ifdef JS_ION_PERF
+  writePerfSpewerJitCodeProfile(code, "BaselineICFallback");
+#endif
+#ifdef MOZ_VTUNE
+  vtune::MarkStub(code, "BaselineICFallback");
+#endif
+
+  fallbackCode.initCode(code);
+  return true;
+}
+
 }  // namespace jit
 }  // namespace js
--- a/js/src/jit/BaselineIC.h
+++ b/js/src/jit/BaselineIC.h
@@ -498,33 +498,42 @@ class ICStub {
   enum Trait {
     Regular = 0x0,
     Fallback = 0x1,
     Monitored = 0x2,
     MonitoredFallback = 0x3,
     Updated = 0x4
   };
 
-  void traceCode(JSTracer* trc, const char* name);
   void updateCode(JitCode* stubCode);
   void trace(JSTracer* trc);
 
   template <typename T, typename... Args>
   static T* New(JSContext* cx, ICStubSpace* space, JitCode* code,
                 Args&&... args) {
     if (!code) {
       return nullptr;
     }
     T* result = space->allocate<T>(code, std::forward<Args>(args)...);
     if (!result) {
       ReportOutOfMemory(cx);
     }
     return result;
   }
 
+  template <typename T, typename... Args>
+  static T* NewFallback(JSContext* cx, ICStubSpace* space, TrampolinePtr code,
+                        Args&&... args) {
+    T* result = space->allocate<T>(code, std::forward<Args>(args)...);
+    if (MOZ_UNLIKELY(!result)) {
+      ReportOutOfMemory(cx);
+    }
+    return result;
+  }
+
  protected:
   // The raw jitcode to call for this stub.
   uint8_t* stubCode_;
 
   // Pointer to next IC stub.  This is null for the last IC stub, which should
   // either be a fallback or inert IC stub.
   ICStub* next_;
 
@@ -532,33 +541,41 @@ class ICStub {
   uint16_t extra_;
 
   // The kind of the stub.
   //  High bit is 'isFallback' flag.
   //  Second high bit is 'isMonitored' flag.
   Trait trait_ : 3;
   Kind kind_ : 13;
 
-  inline ICStub(Kind kind, JitCode* stubCode)
-      : stubCode_(stubCode->raw()),
+  inline ICStub(Kind kind, uint8_t* stubCode)
+      : stubCode_(stubCode),
         next_(nullptr),
         extra_(0),
         trait_(Regular),
         kind_(kind) {
     MOZ_ASSERT(stubCode != nullptr);
   }
 
-  inline ICStub(Kind kind, Trait trait, JitCode* stubCode)
-      : stubCode_(stubCode->raw()),
+  inline ICStub(Kind kind, JitCode* stubCode) : ICStub(kind, stubCode->raw()) {
+    MOZ_ASSERT(stubCode != nullptr);
+  }
+
+  inline ICStub(Kind kind, Trait trait, uint8_t* stubCode)
+      : stubCode_(stubCode),
         next_(nullptr),
         extra_(0),
         trait_(trait),
         kind_(kind) {
     MOZ_ASSERT(stubCode != nullptr);
   }
+  inline ICStub(Kind kind, Trait trait, JitCode* stubCode)
+      : ICStub(kind, trait, stubCode->raw()) {
+    MOZ_ASSERT(stubCode != nullptr);
+  }
 
   inline Trait trait() const {
     // Workaround for MSVC reading trait_ as signed value.
     return (Trait)(trait_ & 0x7);
   }
 
  public:
   inline Kind kind() const { return static_cast<Kind>(kind_); }
@@ -635,17 +652,25 @@ class ICStub {
   inline void setNext(ICStub* stub) {
     // Note: next_ only needs to be changed under the compilation lock for
     // non-type-monitor/update ICs.
     next_ = stub;
   }
 
   inline ICStub** addressOfNext() { return &next_; }
 
-  inline JitCode* jitCode() { return JitCode::FromExecutable(stubCode_); }
+  bool usesTrampolineCode() const {
+    // All fallback code is stored in a single JitCode instance, so we can't
+    // call JitCode::FromExecutable on the raw pointer.
+    return isFallback() || isTypeMonitor_Fallback() || isTypeUpdate_Fallback();
+  }
+  JitCode* jitCode() {
+    MOZ_ASSERT(!usesTrampolineCode());
+    return JitCode::FromExecutable(stubCode_);
+  }
 
   inline uint8_t* rawStubCode() const { return stubCode_; }
 
   // This method is not valid on TypeUpdate stub chains!
   inline ICFallbackStub* getChainFallback() {
     ICStub* lastStub = this;
     while (lastStub->next_) {
       lastStub = lastStub->next_;
@@ -700,25 +725,25 @@ class ICFallbackStub : public ICStub {
 
   // A pointer to the location stub pointer that needs to be
   // changed to add a new "last" stub immediately before the fallback
   // stub.  This'll start out pointing to the icEntry's "firstStub_"
   // field, and as new stubs are added, it'll point to the current
   // last stub's "next_" field.
   ICStub** lastStubPtrAddr_;
 
-  ICFallbackStub(Kind kind, JitCode* stubCode)
-      : ICStub(kind, ICStub::Fallback, stubCode),
+  ICFallbackStub(Kind kind, TrampolinePtr stubCode)
+      : ICStub(kind, ICStub::Fallback, stubCode.value),
         icEntry_(nullptr),
         state_(),
         enteredCount_(0),
         lastStubPtrAddr_(nullptr) {}
 
-  ICFallbackStub(Kind kind, Trait trait, JitCode* stubCode)
-      : ICStub(kind, trait, stubCode),
+  ICFallbackStub(Kind kind, Trait trait, TrampolinePtr stubCode)
+      : ICStub(kind, trait, stubCode.value),
         icEntry_(nullptr),
         state_(),
         enteredCount_(0),
         lastStubPtrAddr_(nullptr) {
     MOZ_ASSERT(trait == ICStub::Fallback || trait == ICStub::MonitoredFallback);
   }
 
  public:
@@ -949,51 +974,31 @@ class ICCacheIR_Updated : public ICUpdat
 
   void notePreliminaryObject() { extra_ = 1; }
   bool hasPreliminaryObject() const { return extra_; }
 
   uint8_t* stubDataStart();
 };
 
 // Base class for stubcode compilers.
-class ICStubCompiler {
-  // Prevent GC in the middle of stub compilation.
-  js::gc::AutoSuppressGC suppressGC;
-
+class ICStubCompilerBase {
  protected:
   JSContext* cx;
-  ICStub::Kind kind;
-  bool inStubFrame_;
+  bool inStubFrame_ = false;
 
 #ifdef DEBUG
-  bool entersStubFrame_;
-  uint32_t framePushedAtEnterStubFrame_;
+  bool entersStubFrame_ = false;
+  uint32_t framePushedAtEnterStubFrame_ = 0;
 #endif
 
-  // By default the stubcode key is just the kind.
-  virtual int32_t getKey() const { return static_cast<int32_t>(kind); }
-
-  virtual MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm) = 0;
-  virtual void postGenerateStubCode(MacroAssembler& masm,
-                                    Handle<JitCode*> genCode) {}
-
-  JitCode* getStubCode();
-
-  ICStubCompiler(JSContext* cx, ICStub::Kind kind)
-      : suppressGC(cx),
-        cx(cx),
-        kind(kind),
-        inStubFrame_(false)
-#ifdef DEBUG
-        ,
-        entersStubFrame_(false),
-        framePushedAtEnterStubFrame_(0)
-#endif
-  {
-  }
+  explicit ICStubCompilerBase(JSContext* cx) : cx(cx) {}
+
+  void pushCallArguments(MacroAssembler& masm,
+                         AllocatableGeneralRegisterSet regs, Register argcReg,
+                         bool isJitCall, bool isConstructing = false);
 
   // Push a payload specialized per compiler needed to execute stubs.
   void PushStubPayload(MacroAssembler& masm, Register scratch);
   void pushStubPayload(MacroAssembler& masm, Register scratch);
 
   // Emits a tail call to a VMFunction wrapper.
   MOZ_MUST_USE bool tailCallVMInternal(MacroAssembler& masm,
                                        TailCallVMFunctionId id);
@@ -1051,16 +1056,34 @@ class ICStubCompiler {
         regs.take(R1);
         break;
       default:
         MOZ_CRASH("Invalid numInputs");
     }
 
     return regs;
   }
+};
+
+class ICStubCompiler : public ICStubCompilerBase {
+  // Prevent GC in the middle of stub compilation.
+  js::gc::AutoSuppressGC suppressGC;
+
+ protected:
+  ICStub::Kind kind;
+
+  // By default the stubcode key is just the kind.
+  virtual int32_t getKey() const { return static_cast<int32_t>(kind); }
+
+  virtual MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm) = 0;
+
+  JitCode* getStubCode();
+
+  ICStubCompiler(JSContext* cx, ICStub::Kind kind)
+      : ICStubCompilerBase(cx), suppressGC(cx), kind(kind) {}
 
  protected:
   template <typename T, typename... Args>
   T* newStub(Args&&... args) {
     return ICStub::New<T>(cx, std::forward<Args>(args)...);
   }
 
  public:
@@ -1075,43 +1098,28 @@ class ICStubCompiler {
 };
 
 // WarmUpCounter_Fallback
 
 // A WarmUpCounter IC chain has only the fallback stub.
 class ICWarmUpCounter_Fallback : public ICFallbackStub {
   friend class ICStubSpace;
 
-  explicit ICWarmUpCounter_Fallback(JitCode* stubCode)
+  explicit ICWarmUpCounter_Fallback(TrampolinePtr stubCode)
       : ICFallbackStub(ICStub::WarmUpCounter_Fallback, stubCode) {}
-
- public:
-  // Compiler for this stub kind.
-  class Compiler : public ICStubCompiler {
-   protected:
-    MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm) override;
-
-   public:
-    explicit Compiler(JSContext* cx)
-        : ICStubCompiler(cx, ICStub::WarmUpCounter_Fallback) {}
-
-    ICWarmUpCounter_Fallback* getStub(ICStubSpace* space) override {
-      return newStub<ICWarmUpCounter_Fallback>(space, getStubCode());
-    }
-  };
 };
 
 // Monitored fallback stubs - as the name implies.
 class ICMonitoredFallbackStub : public ICFallbackStub {
  protected:
   // Pointer to the fallback monitor stub. Created lazily by
   // getFallbackMonitorStub if needed.
   ICTypeMonitor_Fallback* fallbackMonitorStub_;
 
-  ICMonitoredFallbackStub(Kind kind, JitCode* stubCode)
+  ICMonitoredFallbackStub(Kind kind, TrampolinePtr stubCode)
       : ICFallbackStub(kind, ICStub::MonitoredFallback, stubCode),
         fallbackMonitorStub_(nullptr) {}
 
  public:
   MOZ_MUST_USE bool initMonitoringChain(JSContext* cx, JSScript* script);
   MOZ_MUST_USE bool addMonitorStubForValue(JSContext* cx, BaselineFrame* frame,
                                            StackTypeSet* types,
                                            HandleValue val);
@@ -1246,20 +1254,20 @@ class ICTypeMonitor_Fallback : public IC
   bool hasFallbackStub_ : 1;
 
   // Index of 'this' or argument which is being monitored, or BYTECODE_INDEX
   // if this is monitoring the types of values pushed at some bytecode.
   uint32_t argumentIndex_ : 23;
 
   static const uint32_t BYTECODE_INDEX = (1 << 23) - 1;
 
-  ICTypeMonitor_Fallback(JitCode* stubCode,
+  ICTypeMonitor_Fallback(TrampolinePtr stubCode,
                          ICMonitoredFallbackStub* mainFallbackStub,
-                         uint32_t argumentIndex)
-      : ICStub(ICStub::TypeMonitor_Fallback, stubCode),
+                         uint32_t argumentIndex = BYTECODE_INDEX)
+      : ICStub(ICStub::TypeMonitor_Fallback, stubCode.value),
         mainFallbackStub_(mainFallbackStub),
         firstMonitorStub_(thisFromCtor()),
         lastMonitorStubPtrAddr_(nullptr),
         numOptimizedMonitorStubs_(0),
         hasFallbackStub_(mainFallbackStub != nullptr),
         argumentIndex_(argumentIndex) {}
 
   ICTypeMonitor_Fallback* thisFromCtor() { return this; }
@@ -1343,41 +1351,16 @@ class ICTypeMonitor_Fallback : public IC
 
   // Create a new monitor stub for the type of the given value, and
   // add it to this chain.
   MOZ_MUST_USE bool addMonitorStubForValue(JSContext* cx, BaselineFrame* frame,
                                            StackTypeSet* types,
                                            HandleValue val);
 
   void resetMonitorStubChain(Zone* zone);
-
-  // Compiler for this stub kind.
-  class Compiler : public ICStubCompiler {
-    ICMonitoredFallbackStub* mainFallbackStub_;
-    uint32_t argumentIndex_;
-
-   protected:
-    MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm) override;
-
-   public:
-    Compiler(JSContext* cx, ICMonitoredFallbackStub* mainFallbackStub)
-        : ICStubCompiler(cx, ICStub::TypeMonitor_Fallback),
-          mainFallbackStub_(mainFallbackStub),
-          argumentIndex_(BYTECODE_INDEX) {}
-
-    Compiler(JSContext* cx, uint32_t argumentIndex)
-        : ICStubCompiler(cx, ICStub::TypeMonitor_Fallback),
-          mainFallbackStub_(nullptr),
-          argumentIndex_(argumentIndex) {}
-
-    ICTypeMonitor_Fallback* getStub(ICStubSpace* space) override {
-      return newStub<ICTypeMonitor_Fallback>(space, getStubCode(),
-                                             mainFallbackStub_, argumentIndex_);
-    }
-  };
 };
 
 class ICTypeMonitor_PrimitiveSet : public TypeCheckPrimitiveSetStub {
   friend class ICStubSpace;
 
   ICTypeMonitor_PrimitiveSet(JitCode* stubCode, uint16_t flags)
       : TypeCheckPrimitiveSetStub(TypeMonitor_PrimitiveSet, stubCode, flags) {}
 
@@ -1489,33 +1472,18 @@ class ICTypeMonitor_AnyValue : public IC
 
 // TypeUpdate
 
 // The TypeUpdate fallback is not a regular fallback, since it just
 // forwards to a different entry point in the main fallback stub.
 class ICTypeUpdate_Fallback : public ICStub {
   friend class ICStubSpace;
 
-  explicit ICTypeUpdate_Fallback(JitCode* stubCode)
-      : ICStub(ICStub::TypeUpdate_Fallback, stubCode) {}
-
- public:
-  // Compiler for this stub kind.
-  class Compiler : public ICStubCompiler {
-   protected:
-    MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm) override;
-
-   public:
-    explicit Compiler(JSContext* cx)
-        : ICStubCompiler(cx, ICStub::TypeUpdate_Fallback) {}
-
-    ICTypeUpdate_Fallback* getStub(ICStubSpace* space) override {
-      return newStub<ICTypeUpdate_Fallback>(space, getStubCode());
-    }
-  };
+  explicit ICTypeUpdate_Fallback(TrampolinePtr stubCode)
+      : ICStub(ICStub::TypeUpdate_Fallback, stubCode.value) {}
 };
 
 class ICTypeUpdate_PrimitiveSet : public TypeCheckPrimitiveSetStub {
   friend class ICStubSpace;
 
   ICTypeUpdate_PrimitiveSet(JitCode* stubCode, uint16_t flags)
       : TypeCheckPrimitiveSetStub(TypeUpdate_PrimitiveSet, stubCode, flags) {}
 
@@ -1628,303 +1596,141 @@ class ICTypeUpdate_AnyValue : public ICS
 };
 
 // ToBool
 //      JSOP_IFNE
 
 class ICToBool_Fallback : public ICFallbackStub {
   friend class ICStubSpace;
 
-  explicit ICToBool_Fallback(JitCode* stubCode)
+  explicit ICToBool_Fallback(TrampolinePtr stubCode)
       : ICFallbackStub(ICStub::ToBool_Fallback, stubCode) {}
 
  public:
   static const uint32_t MAX_OPTIMIZED_STUBS = 8;
-
-  // Compiler for this stub kind.
-  class Compiler : public ICStubCompiler {
-   protected:
-    MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm) override;
-
-   public:
-    explicit Compiler(JSContext* cx)
-        : ICStubCompiler(cx, ICStub::ToBool_Fallback) {}
-
-    ICStub* getStub(ICStubSpace* space) override {
-      return newStub<ICToBool_Fallback>(space, getStubCode());
-    }
-  };
 };
 
 // GetElem
 //      JSOP_GETELEM
 //      JSOP_GETELEM_SUPER
 
 class ICGetElem_Fallback : public ICMonitoredFallbackStub {
   friend class ICStubSpace;
 
-  explicit ICGetElem_Fallback(JitCode* stubCode)
+  explicit ICGetElem_Fallback(TrampolinePtr stubCode)
       : ICMonitoredFallbackStub(ICStub::GetElem_Fallback, stubCode) {}
 
   static const uint16_t EXTRA_NEGATIVE_INDEX = 0x1;
   static const uint16_t SAW_NON_INTEGER_INDEX_BIT = 0x2;
 
  public:
   void noteNegativeIndex() { extra_ |= EXTRA_NEGATIVE_INDEX; }
   bool hasNegativeIndex() const { return extra_ & EXTRA_NEGATIVE_INDEX; }
 
   void setSawNonIntegerIndex() { extra_ |= SAW_NON_INTEGER_INDEX_BIT; }
   bool sawNonIntegerIndex() const { return extra_ & SAW_NON_INTEGER_INDEX_BIT; }
-
-  // Compiler for this stub kind.
-  class Compiler : public ICStubCompiler {
-   protected:
-    CodeOffset bailoutReturnOffset_;
-    bool hasReceiver_;
-    MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm) override;
-    void postGenerateStubCode(MacroAssembler& masm,
-                              Handle<JitCode*> code) override;
-
-    virtual int32_t getKey() const override {
-      return static_cast<int32_t>(kind) |
-             (static_cast<int32_t>(hasReceiver_) << 16);
-    }
-
-   public:
-    explicit Compiler(JSContext* cx, bool hasReceiver = false)
-        : ICStubCompiler(cx, ICStub::GetElem_Fallback),
-          hasReceiver_(hasReceiver) {}
-
-    ICStub* getStub(ICStubSpace* space) override {
-      return newStub<ICGetElem_Fallback>(space, getStubCode());
-    }
-  };
 };
 
 // SetElem
 //      JSOP_SETELEM
 //      JSOP_INITELEM
 
 class ICSetElem_Fallback : public ICFallbackStub {
   friend class ICStubSpace;
 
-  explicit ICSetElem_Fallback(JitCode* stubCode)
+  explicit ICSetElem_Fallback(TrampolinePtr stubCode)
       : ICFallbackStub(ICStub::SetElem_Fallback, stubCode) {}
 
   static const size_t HasDenseAddFlag = 0x1;
   static const size_t HasTypedArrayOOBFlag = 0x2;
 
  public:
   void noteHasDenseAdd() { extra_ |= HasDenseAddFlag; }
   bool hasDenseAdd() const { return extra_ & HasDenseAddFlag; }
 
   void noteHasTypedArrayOOB() { extra_ |= HasTypedArrayOOBFlag; }
   bool hasTypedArrayOOB() const { return extra_ & HasTypedArrayOOBFlag; }
-
-  // Compiler for this stub kind.
-  class Compiler : public ICStubCompiler {
-   protected:
-    MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm) override;
-
-   public:
-    explicit Compiler(JSContext* cx)
-        : ICStubCompiler(cx, ICStub::SetElem_Fallback) {}
-
-    ICStub* getStub(ICStubSpace* space) override {
-      return newStub<ICSetElem_Fallback>(space, getStubCode());
-    }
-  };
 };
 
 // In
 //      JSOP_IN
 class ICIn_Fallback : public ICFallbackStub {
   friend class ICStubSpace;
 
-  explicit ICIn_Fallback(JitCode* stubCode)
+  explicit ICIn_Fallback(TrampolinePtr stubCode)
       : ICFallbackStub(ICStub::In_Fallback, stubCode) {}
-
- public:
-  class Compiler : public ICStubCompiler {
-   protected:
-    MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm) override;
-
-   public:
-    explicit Compiler(JSContext* cx)
-        : ICStubCompiler(cx, ICStub::In_Fallback) {}
-
-    ICStub* getStub(ICStubSpace* space) override {
-      return newStub<ICIn_Fallback>(space, getStubCode());
-    }
-  };
 };
 
 // HasOwn
 //      JSOP_HASOWN
 class ICHasOwn_Fallback : public ICFallbackStub {
   friend class ICStubSpace;
 
-  explicit ICHasOwn_Fallback(JitCode* stubCode)
+  explicit ICHasOwn_Fallback(TrampolinePtr stubCode)
       : ICFallbackStub(ICStub::HasOwn_Fallback, stubCode) {}
-
- public:
-  class Compiler : public ICStubCompiler {
-   protected:
-    MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm) override;
-
-   public:
-    explicit Compiler(JSContext* cx)
-        : ICStubCompiler(cx, ICStub::HasOwn_Fallback) {}
-
-    ICStub* getStub(ICStubSpace* space) override {
-      return newStub<ICHasOwn_Fallback>(space, getStubCode());
-    }
-  };
 };
 
 // GetName
 //      JSOP_GETNAME
 //      JSOP_GETGNAME
 class ICGetName_Fallback : public ICMonitoredFallbackStub {
   friend class ICStubSpace;
 
-  explicit ICGetName_Fallback(JitCode* stubCode)
+  explicit ICGetName_Fallback(TrampolinePtr stubCode)
       : ICMonitoredFallbackStub(ICStub::GetName_Fallback, stubCode) {}
-
- public:
-  class Compiler : public ICStubCompiler {
-   protected:
-    MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm) override;
-
-   public:
-    explicit Compiler(JSContext* cx)
-        : ICStubCompiler(cx, ICStub::GetName_Fallback) {}
-
-    ICStub* getStub(ICStubSpace* space) override {
-      return newStub<ICGetName_Fallback>(space, getStubCode());
-    }
-  };
 };
 
 // BindName
 //      JSOP_BINDNAME
 class ICBindName_Fallback : public ICFallbackStub {
   friend class ICStubSpace;
 
-  explicit ICBindName_Fallback(JitCode* stubCode)
+  explicit ICBindName_Fallback(TrampolinePtr stubCode)
       : ICFallbackStub(ICStub::BindName_Fallback, stubCode) {}
-
- public:
-  class Compiler : public ICStubCompiler {
-   protected:
-    MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm) override;
-
-   public:
-    explicit Compiler(JSContext* cx)
-        : ICStubCompiler(cx, ICStub::BindName_Fallback) {}
-
-    ICStub* getStub(ICStubSpace* space) override {
-      return newStub<ICBindName_Fallback>(space, getStubCode());
-    }
-  };
 };
 
 // GetIntrinsic
 //      JSOP_GETINTRINSIC
 class ICGetIntrinsic_Fallback : public ICMonitoredFallbackStub {
   friend class ICStubSpace;
 
-  explicit ICGetIntrinsic_Fallback(JitCode* stubCode)
+  explicit ICGetIntrinsic_Fallback(TrampolinePtr stubCode)
       : ICMonitoredFallbackStub(ICStub::GetIntrinsic_Fallback, stubCode) {}
-
- public:
-  class Compiler : public ICStubCompiler {
-   protected:
-    MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm) override;
-
-   public:
-    explicit Compiler(JSContext* cx)
-        : ICStubCompiler(cx, ICStub::GetIntrinsic_Fallback) {}
-
-    ICStub* getStub(ICStubSpace* space) override {
-      return newStub<ICGetIntrinsic_Fallback>(space, getStubCode());
-    }
-  };
 };
 
 // GetProp
 //     JSOP_GETPROP
 //     JSOP_GETPROP_SUPER
 
 class ICGetProp_Fallback : public ICMonitoredFallbackStub {
   friend class ICStubSpace;
 
-  explicit ICGetProp_Fallback(JitCode* stubCode)
+  explicit ICGetProp_Fallback(TrampolinePtr stubCode)
       : ICMonitoredFallbackStub(ICStub::GetProp_Fallback, stubCode) {}
 
  public:
   static const size_t ACCESSED_GETTER_BIT = 1;
 
   void noteAccessedGetter() { extra_ |= (1u << ACCESSED_GETTER_BIT); }
   bool hasAccessedGetter() const {
     return extra_ & (1u << ACCESSED_GETTER_BIT);
   }
-
-  class Compiler : public ICStubCompiler {
-   protected:
-    CodeOffset bailoutReturnOffset_;
-    bool hasReceiver_;
-    MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm) override;
-    void postGenerateStubCode(MacroAssembler& masm,
-                              Handle<JitCode*> code) override;
-
-    virtual int32_t getKey() const override {
-      return static_cast<int32_t>(kind) |
-             (static_cast<int32_t>(hasReceiver_) << 16);
-    }
-
-   public:
-    explicit Compiler(JSContext* cx, bool hasReceiver = false)
-        : ICStubCompiler(cx, ICStub::GetProp_Fallback),
-          hasReceiver_(hasReceiver) {}
-
-    ICStub* getStub(ICStubSpace* space) override {
-      return newStub<ICGetProp_Fallback>(space, getStubCode());
-    }
-  };
 };
 
 // SetProp
 //     JSOP_SETPROP
 //     JSOP_SETNAME
 //     JSOP_SETGNAME
 //     JSOP_INITPROP
 
 class ICSetProp_Fallback : public ICFallbackStub {
   friend class ICStubSpace;
 
-  explicit ICSetProp_Fallback(JitCode* stubCode)
+  explicit ICSetProp_Fallback(TrampolinePtr stubCode)
       : ICFallbackStub(ICStub::SetProp_Fallback, stubCode) {}
-
- public:
-  class Compiler : public ICStubCompiler {
-   protected:
-    CodeOffset bailoutReturnOffset_;
-    MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm) override;
-    void postGenerateStubCode(MacroAssembler& masm,
-                              Handle<JitCode*> code) override;
-
-   public:
-    explicit Compiler(JSContext* cx)
-        : ICStubCompiler(cx, ICStub::SetProp_Fallback) {}
-
-    ICStub* getStub(ICStubSpace* space) override {
-      return newStub<ICSetProp_Fallback>(space, getStubCode());
-    }
-  };
 };
 
 // Call
 //      JSOP_CALL
 //      JSOP_CALL_IGNORES_RV
 //      JSOP_FUNAPPLY
 //      JSOP_FUNCALL
 //      JSOP_NEW
@@ -1934,19 +1740,16 @@ class ICSetProp_Fallback : public ICFall
 
 class ICCallStubCompiler : public ICStubCompiler {
  protected:
   ICCallStubCompiler(JSContext* cx, ICStub::Kind kind)
       : ICStubCompiler(cx, kind) {}
 
   enum FunApplyThing { FunApply_MagicArgs, FunApply_Array };
 
-  void pushCallArguments(MacroAssembler& masm,
-                         AllocatableGeneralRegisterSet regs, Register argcReg,
-                         bool isJitCall, bool isConstructing = false);
   void pushSpreadCallArguments(MacroAssembler& masm,
                                AllocatableGeneralRegisterSet regs,
                                Register argcReg, bool isJitCall,
                                bool isConstructing);
   void guardSpreadCall(MacroAssembler& masm, Register argcReg, Label* failure,
                        bool isConstructing);
   Register guardFunApply(MacroAssembler& masm,
                          AllocatableGeneralRegisterSet regs, Register argcReg,
@@ -1959,52 +1762,25 @@ class ICCallStubCompiler : public ICStub
 
 class ICCall_Fallback : public ICMonitoredFallbackStub {
   friend class ICStubSpace;
 
  public:
   static const uint32_t MAX_OPTIMIZED_STUBS = 16;
 
  private:
-  explicit ICCall_Fallback(JitCode* stubCode)
+  explicit ICCall_Fallback(TrampolinePtr stubCode)
       : ICMonitoredFallbackStub(ICStub::Call_Fallback, stubCode) {}
 
  public:
   bool scriptedStubsAreGeneralized() const { return hasStub(Call_AnyScripted); }
   bool nativeStubsAreGeneralized() const {
     // Return hasStub(Call_AnyNative) after Call_AnyNative stub is added.
     return false;
   }
-
-  // Compiler for this stub kind.
-  class Compiler : public ICCallStubCompiler {
-   protected:
-    bool isConstructing_;
-    bool isSpread_;
-    CodeOffset bailoutReturnOffset_;
-    MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm) override;
-    void postGenerateStubCode(MacroAssembler& masm,
-                              Handle<JitCode*> code) override;
-
-    virtual int32_t getKey() const override {
-      return static_cast<int32_t>(kind) |
-             (static_cast<int32_t>(isSpread_) << 16) |
-             (static_cast<int32_t>(isConstructing_) << 17);
-    }
-
-   public:
-    Compiler(JSContext* cx, bool isConstructing, bool isSpread)
-        : ICCallStubCompiler(cx, ICStub::Call_Fallback),
-          isConstructing_(isConstructing),
-          isSpread_(isSpread) {}
-
-    ICStub* getStub(ICStubSpace* space) override {
-      return newStub<ICCall_Fallback>(space, getStubCode());
-    }
-  };
 };
 
 class ICCall_Scripted : public ICMonitoredStub {
   friend class ICStubSpace;
 
  public:
   // The maximum number of inlineable spread call arguments. Keep this small
   // to avoid controllable stack overflows by attackers passing large arrays
@@ -2416,245 +2192,132 @@ class ICCall_ConstStringSplit : public I
     }
   };
 };
 
 // IC for constructing an iterator from an input value.
 class ICGetIterator_Fallback : public ICFallbackStub {
   friend class ICStubSpace;
 
-  explicit ICGetIterator_Fallback(JitCode* stubCode)
+  explicit ICGetIterator_Fallback(TrampolinePtr stubCode)
       : ICFallbackStub(ICStub::GetIterator_Fallback, stubCode) {}
-
- public:
-  class Compiler : public ICStubCompiler {
-   protected:
-    MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm) override;
-
-   public:
-    explicit Compiler(JSContext* cx)
-        : ICStubCompiler(cx, ICStub::GetIterator_Fallback) {}
-
-    ICStub* getStub(ICStubSpace* space) override {
-      return newStub<ICGetIterator_Fallback>(space, getStubCode());
-    }
-  };
 };
 
 // InstanceOf
 //      JSOP_INSTANCEOF
 class ICInstanceOf_Fallback : public ICFallbackStub {
   friend class ICStubSpace;
 
-  explicit ICInstanceOf_Fallback(JitCode* stubCode)
+  explicit ICInstanceOf_Fallback(TrampolinePtr stubCode)
       : ICFallbackStub(ICStub::InstanceOf_Fallback, stubCode) {}
-
- public:
-  class Compiler : public ICStubCompiler {
-   protected:
-    MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm) override;
-
-   public:
-    explicit Compiler(JSContext* cx)
-        : ICStubCompiler(cx, ICStub::InstanceOf_Fallback) {}
-
-    ICStub* getStub(ICStubSpace* space) override {
-      return newStub<ICInstanceOf_Fallback>(space, getStubCode());
-    }
-  };
 };
 
 // TypeOf
 //      JSOP_TYPEOF
 //      JSOP_TYPEOFEXPR
 class ICTypeOf_Fallback : public ICFallbackStub {
   friend class ICStubSpace;
 
-  explicit ICTypeOf_Fallback(JitCode* stubCode)
+  explicit ICTypeOf_Fallback(TrampolinePtr stubCode)
       : ICFallbackStub(ICStub::TypeOf_Fallback, stubCode) {}
 
  public:
   static const uint32_t MAX_OPTIMIZED_STUBS = 6;
-
-  class Compiler : public ICStubCompiler {
-   protected:
-    MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm) override;
-
-   public:
-    explicit Compiler(JSContext* cx)
-        : ICStubCompiler(cx, ICStub::TypeOf_Fallback) {}
-
-    ICStub* getStub(ICStubSpace* space) override {
-      return newStub<ICTypeOf_Fallback>(space, getStubCode());
-    }
-  };
 };
 
 class ICRest_Fallback : public ICFallbackStub {
   friend class ICStubSpace;
 
   GCPtrArrayObject templateObject_;
 
-  ICRest_Fallback(JitCode* stubCode, ArrayObject* templateObject)
+  ICRest_Fallback(TrampolinePtr stubCode, ArrayObject* templateObject)
       : ICFallbackStub(ICStub::Rest_Fallback, stubCode),
         templateObject_(templateObject) {}
 
  public:
   static const uint32_t MAX_OPTIMIZED_STUBS = 8;
 
   GCPtrArrayObject& templateObject() { return templateObject_; }
-
-  class Compiler : public ICStubCompiler {
-   protected:
-    RootedArrayObject templateObject;
-    MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm) override;
-
-   public:
-    Compiler(JSContext* cx, ArrayObject* templateObject)
-        : ICStubCompiler(cx, ICStub::Rest_Fallback),
-          templateObject(cx, templateObject) {}
-
-    ICStub* getStub(ICStubSpace* space) override {
-      return newStub<ICRest_Fallback>(space, getStubCode(), templateObject);
-    }
-  };
 };
 
 // UnaryArith
 //     JSOP_BITNOT
 //     JSOP_NEG
 //     JSOP_INC
 //     JSOP_DEC
 
 class ICUnaryArith_Fallback : public ICFallbackStub {
   friend class ICStubSpace;
 
-  explicit ICUnaryArith_Fallback(JitCode* stubCode)
+  explicit ICUnaryArith_Fallback(TrampolinePtr stubCode)
       : ICFallbackStub(UnaryArith_Fallback, stubCode) {
     extra_ = 0;
   }
 
  public:
   bool sawDoubleResult() { return extra_; }
   void setSawDoubleResult() { extra_ = 1; }
-
-  // Compiler for this stub kind.
-  class Compiler : public ICStubCompiler {
-   protected:
-    MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm) override;
-
-   public:
-    explicit Compiler(JSContext* cx)
-        : ICStubCompiler(cx, ICStub::UnaryArith_Fallback) {}
-
-    ICStub* getStub(ICStubSpace* space) override {
-      return newStub<ICUnaryArith_Fallback>(space, getStubCode());
-    }
-  };
 };
 
 // Compare
 //      JSOP_LT
 //      JSOP_LE
 //      JSOP_GT
 //      JSOP_GE
 //      JSOP_EQ
 //      JSOP_NE
 //      JSOP_STRICTEQ
 //      JSOP_STRICTNE
 
 class ICCompare_Fallback : public ICFallbackStub {
   friend class ICStubSpace;
 
-  explicit ICCompare_Fallback(JitCode* stubCode)
+  explicit ICCompare_Fallback(TrampolinePtr stubCode)
       : ICFallbackStub(ICStub::Compare_Fallback, stubCode) {}
-
- public:
-  // Compiler for this stub kind.
-  class Compiler : public ICStubCompiler {
-   protected:
-    MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm) override;
-
-   public:
-    explicit Compiler(JSContext* cx)
-        : ICStubCompiler(cx, ICStub::Compare_Fallback) {}
-
-    ICStub* getStub(ICStubSpace* space) override {
-      return newStub<ICCompare_Fallback>(space, getStubCode());
-    }
-  };
 };
 
 // BinaryArith
 //      JSOP_ADD, JSOP_SUB, JSOP_MUL, JOP_DIV, JSOP_MOD
 //      JSOP_BITAND, JSOP_BITXOR, JSOP_BITOR
 //      JSOP_LSH, JSOP_RSH, JSOP_URSH
 
 class ICBinaryArith_Fallback : public ICFallbackStub {
   friend class ICStubSpace;
 
-  explicit ICBinaryArith_Fallback(JitCode* stubCode)
+  explicit ICBinaryArith_Fallback(TrampolinePtr stubCode)
       : ICFallbackStub(BinaryArith_Fallback, stubCode) {
     extra_ = 0;
   }
 
   static const uint16_t SAW_DOUBLE_RESULT_BIT = 0x1;
 
  public:
   static const uint32_t MAX_OPTIMIZED_STUBS = 8;
 
   bool sawDoubleResult() const { return extra_ & SAW_DOUBLE_RESULT_BIT; }
   void setSawDoubleResult() { extra_ |= SAW_DOUBLE_RESULT_BIT; }
-
-  // Compiler for this stub kind.
-  class Compiler : public ICStubCompiler {
-   protected:
-    MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm) override;
-
-   public:
-    explicit Compiler(JSContext* cx)
-        : ICStubCompiler(cx, ICStub::BinaryArith_Fallback) {}
-
-    ICStub* getStub(ICStubSpace* space) override {
-      return newStub<ICBinaryArith_Fallback>(space, getStubCode());
-    }
-  };
 };
 
 // JSOP_NEWARRAY
 
 class ICNewArray_Fallback : public ICFallbackStub {
   friend class ICStubSpace;
 
   GCPtrObject templateObject_;
 
   // The group used for objects created here is always available, even if the
   // template object itself is not.
   GCPtrObjectGroup templateGroup_;
 
-  ICNewArray_Fallback(JitCode* stubCode, ObjectGroup* templateGroup)
+  ICNewArray_Fallback(TrampolinePtr stubCode, ObjectGroup* templateGroup)
       : ICFallbackStub(ICStub::NewArray_Fallback, stubCode),
         templateObject_(nullptr),
         templateGroup_(templateGroup) {}
 
  public:
-  class Compiler : public ICStubCompiler {
-    RootedObjectGroup templateGroup;
-    MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm) override;
-
-   public:
-    Compiler(JSContext* cx, ObjectGroup* templateGroup)
-        : ICStubCompiler(cx, ICStub::NewArray_Fallback),
-          templateGroup(cx, templateGroup) {}
-
-    ICStub* getStub(ICStubSpace* space) override {
-      return newStub<ICNewArray_Fallback>(space, getStubCode(), templateGroup);
-    }
-  };
-
   GCPtrObject& templateObject() { return templateObject_; }
 
   void setTemplateObject(JSObject* obj) {
     MOZ_ASSERT(obj->group() == templateGroup());
     templateObject_ = obj;
   }
 
   GCPtrObjectGroup& templateGroup() { return templateGroup_; }
@@ -2667,33 +2330,21 @@ class ICNewArray_Fallback : public ICFal
 
 // JSOP_NEWOBJECT
 
 class ICNewObject_Fallback : public ICFallbackStub {
   friend class ICStubSpace;
 
   GCPtrObject templateObject_;
 
-  explicit ICNewObject_Fallback(JitCode* stubCode)
+  explicit ICNewObject_Fallback(TrampolinePtr stubCode)
       : ICFallbackStub(ICStub::NewObject_Fallback, stubCode),
         templateObject_(nullptr) {}
 
  public:
-  class Compiler : public ICStubCompiler {
-    MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm) override;
-
-   public:
-    explicit Compiler(JSContext* cx)
-        : ICStubCompiler(cx, ICStub::NewObject_Fallback) {}
-
-    ICStub* getStub(ICStubSpace* space) override {
-      return newStub<ICNewObject_Fallback>(space, getStubCode());
-    }
-  };
-
   GCPtrObject& templateObject() { return templateObject_; }
 
   void setTemplateObject(JSObject* obj) { templateObject_ = obj; }
 };
 
 inline bool IsCacheableDOMProxy(JSObject* obj) {
   if (!obj->is<ProxyObject>()) {
     return false;
--- a/js/src/jit/BaselineICList.h
+++ b/js/src/jit/BaselineICList.h
@@ -5,17 +5,18 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef jit_BaselineICList_h
 #define jit_BaselineICList_h
 
 namespace js {
 namespace jit {
 
-// List of IC stub kinds that can only run in Baseline.
+// List of Baseline IC stub kinds. The stub kind determines the structure of the
+// ICStub data.
 #define IC_BASELINE_STUB_KIND_LIST(_) \
   _(WarmUpCounter_Fallback)           \
                                       \
   _(TypeMonitor_Fallback)             \
   _(TypeMonitor_SingleObject)         \
   _(TypeMonitor_ObjectGroup)          \
   _(TypeMonitor_PrimitiveSet)         \
   _(TypeMonitor_AnyValue)             \
@@ -55,28 +56,62 @@ namespace jit {
                                       \
   _(GetIntrinsic_Fallback)            \
                                       \
   _(SetProp_Fallback)                 \
                                       \
   _(GetIterator_Fallback)             \
                                       \
   _(InstanceOf_Fallback)              \
-  _(InstanceOf_Function)              \
                                       \
   _(TypeOf_Fallback)                  \
                                       \
   _(Rest_Fallback)                    \
                                       \
   _(BinaryArith_Fallback)             \
                                       \
   _(Compare_Fallback)                 \
                                       \
   _(GetProp_Fallback)                 \
                                       \
   _(CacheIR_Regular)                  \
   _(CacheIR_Monitored)                \
   _(CacheIR_Updated)
 
+// List of fallback trampolines. Each of these fallback trampolines exists as
+// part of the JitRuntime. Note that some fallback stubs in previous list may
+// have multiple trampolines in this list. For example, Call_Fallback has
+// constructing/spread variants here with different calling conventions needing
+// different trampolines.
+#define IC_BASELINE_FALLBACK_CODE_KIND_LIST(_) \
+  _(WarmUpCounter)                             \
+  _(TypeMonitor)                               \
+  _(TypeUpdate)                                \
+  _(NewArray)                                  \
+  _(NewObject)                                 \
+  _(ToBool)                                    \
+  _(UnaryArith)                                \
+  _(Call)                                      \
+  _(CallConstructing)                          \
+  _(SpreadCall)                                \
+  _(SpreadCallConstructing)                    \
+  _(GetElem)                                   \
+  _(GetElemSuper)                              \
+  _(SetElem)                                   \
+  _(In)                                        \
+  _(HasOwn)                                    \
+  _(GetName)                                   \
+  _(BindName)                                  \
+  _(GetIntrinsic)                              \
+  _(SetProp)                                   \
+  _(GetIterator)                               \
+  _(InstanceOf)                                \
+  _(TypeOf)                                    \
+  _(Rest)                                      \
+  _(BinaryArith)                               \
+  _(Compare)                                   \
+  _(GetProp)                                   \
+  _(GetPropSuper)
+
 }  // namespace jit
 }  // namespace js
 
 #endif /* jit_BaselineICList_h */
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -195,16 +195,33 @@ uint32_t JitRuntime::startTrampolineCode
 
 bool JitRuntime::initialize(JSContext* cx) {
   MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
 
   AutoAllocInAtomsZone az(cx);
 
   JitContext jctx(cx, nullptr);
 
+  if (!generateTrampolines(cx)) {
+    return false;
+  }
+
+  if (!generateBaselineICFallbackCode(cx)) {
+    return false;
+  }
+
+  jitcodeGlobalTable_ = cx->new_<JitcodeGlobalTable>();
+  if (!jitcodeGlobalTable_) {
+    return false;
+  }
+
+  return true;
+}
+
+bool JitRuntime::generateTrampolines(JSContext* cx) {
   StackMacroAssembler masm;
 
   Label bailoutTail;
   JitSpew(JitSpew_Codegen, "# Emitting bailout tail stub");
   generateBailoutTailStub(masm, &bailoutTail);
 
   if (cx->runtime()->jitSupportsFloatingPoint) {
     JitSpew(JitSpew_Codegen, "# Emitting bailout tables");
@@ -300,21 +317,16 @@ bool JitRuntime::initialize(JSContext* c
 
 #ifdef JS_ION_PERF
   writePerfSpewerJitCodeProfile(trampolineCode_, "Trampolines");
 #endif
 #ifdef MOZ_VTUNE
   vtune::MarkStub(trampolineCode_, "Trampolines");
 #endif
 
-  jitcodeGlobalTable_ = cx->new_<JitcodeGlobalTable>();
-  if (!jitcodeGlobalTable_) {
-    return false;
-  }
-
   return true;
 }
 
 JitCode* JitRuntime::debugTrapHandler(JSContext* cx) {
   if (!debugTrapHandler_) {
     // JitRuntime code stubs are shared across compartments and have to
     // be allocated in the atoms zone.
     AutoAllocInAtomsZone az(cx);
@@ -556,24 +568,16 @@ void JitRuntime::SweepJitcodeGlobalTable
 }
 
 void JitRealm::sweep(JS::Realm* realm) {
   // Any outstanding compilations should have been cancelled by the GC.
   MOZ_ASSERT(!HasOffThreadIonCompile(realm));
 
   stubCodes_->sweep();
 
-  // If the sweep removed a bailout Fallback stub, nullptr the corresponding
-  // return addr.
-  for (auto& it : bailoutReturnStubInfo_) {
-    if (!stubCodes_->lookup(it.key)) {
-      it = BailoutReturnStubInfo();
-    }
-  }
-
   for (ReadBarrieredJitCode& stub : stubs_) {
     if (stub && IsAboutToBeFinalized(&stub)) {
       stub.set(nullptr);
     }
   }
 }
 
 void JitZone::sweep() { baselineCacheIRStubCodes_.sweep(); }
--- a/js/src/jit/JitRealm.h
+++ b/js/src/jit/JitRealm.h
@@ -4,21 +4,23 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef jit_JitRealm_h
 #define jit_JitRealm_h
 
 #include "mozilla/Array.h"
 #include "mozilla/DebugOnly.h"
+#include "mozilla/EnumeratedArray.h"
 #include "mozilla/MemoryReporting.h"
 
 #include <utility>
 
 #include "builtin/TypedObject.h"
+#include "jit/BaselineICList.h"
 #include "jit/CompileInfo.h"
 #include "jit/ICStubSpace.h"
 #include "jit/IonCode.h"
 #include "jit/IonControlFlow.h"
 #include "jit/JitFrames.h"
 #include "jit/shared/Assembler-shared.h"
 #include "js/GCHashTable.h"
 #include "js/Value.h"
@@ -57,16 +59,70 @@ struct EnterJitData {
   unsigned osrNumStackValues;
 
   RootedObject envChain;
   RootedValue result;
 
   bool constructing;
 };
 
+enum class BaselineICFallbackKind {
+#define DEF_ENUM_KIND(kind) kind,
+  IC_BASELINE_FALLBACK_CODE_KIND_LIST(DEF_ENUM_KIND)
+#undef DEF_ENUM_KIND
+      Count
+};
+
+enum class BailoutReturnKind {
+  GetProp,
+  GetPropSuper,
+  SetProp,
+  GetElem,
+  GetElemSuper,
+  Call,
+  New,
+  Count
+};
+
+// Class storing code and offsets for all Baseline IC fallback trampolines. This
+// is stored in JitRuntime and generated when creating the JitRuntime.
+class BaselineICFallbackCode {
+  JitCode* code_ = nullptr;
+  using OffsetArray =
+      mozilla::EnumeratedArray<BaselineICFallbackKind,
+                               BaselineICFallbackKind::Count, uint32_t>;
+  OffsetArray offsets_ = {};
+
+  // Keep track of offset into various baseline stubs' code at return
+  // point from called script.
+  using BailoutReturnArray =
+      mozilla::EnumeratedArray<BailoutReturnKind, BailoutReturnKind::Count,
+                               uint32_t>;
+  BailoutReturnArray bailoutReturnOffsets_ = {};
+
+ public:
+  BaselineICFallbackCode() = default;
+  BaselineICFallbackCode(const BaselineICFallbackCode&) = delete;
+  void operator=(const BaselineICFallbackCode&) = delete;
+
+  void initOffset(BaselineICFallbackKind kind, uint32_t offset) {
+    offsets_[kind] = offset;
+  }
+  void initCode(JitCode* code) { code_ = code; }
+  void initBailoutReturnOffset(BailoutReturnKind kind, uint32_t offset) {
+    bailoutReturnOffsets_[kind] = offset;
+  }
+  TrampolinePtr addr(BaselineICFallbackKind kind) const {
+    return TrampolinePtr(code_->raw() + offsets_[kind]);
+  }
+  uint8_t* bailoutReturnAddr(BailoutReturnKind kind) const {
+    return code_->raw() + bailoutReturnOffsets_[kind];
+  }
+};
+
 typedef void (*EnterJitCode)(void* code, unsigned argc, Value* argv,
                              InterpreterFrame* fp, CalleeToken calleeToken,
                              JSObject* envChain, size_t numStackValues,
                              Value* vp);
 
 class JitcodeGlobalTable;
 
 class JitRuntime {
@@ -150,16 +206,18 @@ class JitRuntime {
   // Maps VMFunctionId to the offset of the wrapper code in trampolineCode_.
   using VMWrapperOffsets = Vector<uint32_t, 0, SystemAllocPolicy>;
   VMWrapperOffsets functionWrapperOffsets_;
 
   // Maps TailCallVMFunctionId to the offset of the wrapper code in
   // trampolineCode_.
   VMWrapperOffsets tailCallFunctionWrapperOffsets_;
 
+  MainThreadData<BaselineICFallbackCode> baselineICFallbackCode_;
+
   // Global table of jitcode native address => bytecode address mappings.
   UnprotectedData<JitcodeGlobalTable*> jitcodeGlobalTable_;
 
 #ifdef DEBUG
   // The number of possible bailing places encounters before forcefully bailing
   // in that place. Zero means inactive.
   MainThreadData<uint32_t> ionBailAfter_;
 #endif
@@ -176,16 +234,19 @@ class JitRuntime {
   using IonBuilderList = mozilla::LinkedList<js::jit::IonBuilder>;
   MainThreadData<IonBuilderList> ionLazyLinkList_;
   MainThreadData<size_t> ionLazyLinkListSize_;
 
   // Counter used to help dismbiguate stubs in CacheIR
   MainThreadData<uint64_t> disambiguationId_;
 
  private:
+  bool generateTrampolines(JSContext* cx);
+  bool generateBaselineICFallbackCode(JSContext* cx);
+
   void generateLazyLinkStub(MacroAssembler& masm);
   void generateInterpreterStub(MacroAssembler& masm);
   void generateDoubleToInt32ValueStub(MacroAssembler& masm);
   void generateProfilerExitFrameTailStub(MacroAssembler& masm,
                                          Label* profilerExitTail);
   void generateExceptionTailStub(MacroAssembler& masm, void* handler,
                                  Label* profilerExitTail);
   void generateBailoutTailStub(MacroAssembler& masm, Label* bailoutTail);
@@ -237,16 +298,20 @@ class JitRuntime {
 
   static void Trace(JSTracer* trc, const js::AutoAccessAtomsZone& access);
   static void TraceJitcodeGlobalTableForMinorGC(JSTracer* trc);
   static MOZ_MUST_USE bool MarkJitcodeGlobalTableIteratively(GCMarker* marker);
   static void SweepJitcodeGlobalTable(JSRuntime* rt);
 
   ExecutableAllocator& execAlloc() { return execAlloc_.ref(); }
 
+  const BaselineICFallbackCode& baselineICFallbackCode() const {
+    return baselineICFallbackCode_.ref();
+  }
+
   IonCompilationId nextCompilationId() {
     return IonCompilationId(nextCompilationId_++);
   }
 
   TrampolinePtr getVMWrapper(const VMFunction& f) const;
 
   TrampolinePtr getVMWrapper(VMFunctionId funId) const {
     MOZ_ASSERT(trampolineCode_);
@@ -466,50 +531,25 @@ class JitZone {
     IonCacheIRStubInfoSet::AddPtr p =
         ionCacheIRStubInfoSet_.lookupForAdd(lookup);
     MOZ_ASSERT(!p);
     return ionCacheIRStubInfoSet_.add(p, std::move(key));
   }
   void purgeIonCacheIRStubInfo() { ionCacheIRStubInfoSet_.clearAndCompact(); }
 };
 
-enum class BailoutReturnStub {
-  GetProp,
-  GetPropSuper,
-  SetProp,
-  GetElem,
-  GetElemSuper,
-  Call,
-  New,
-  Count
-};
-
 class JitRealm {
   friend class JitActivation;
 
   // Map ICStub keys to ICStub shared code objects.
   using ICStubCodeMap =
       GCHashMap<uint32_t, ReadBarrieredJitCode, DefaultHasher<uint32_t>,
                 ZoneAllocPolicy, IcStubCodeMapGCPolicy<uint32_t>>;
   ICStubCodeMap* stubCodes_;
 
-  // Keep track of offset into various baseline stubs' code at return
-  // point from called script.
-  struct BailoutReturnStubInfo {
-    void* addr;
-    uint32_t key;
-
-    BailoutReturnStubInfo() : addr(nullptr), key(0) {}
-    BailoutReturnStubInfo(void* addr_, uint32_t key_)
-        : addr(addr_), key(key_) {}
-  };
-  mozilla::EnumeratedArray<BailoutReturnStub, BailoutReturnStub::Count,
-                           BailoutReturnStubInfo>
-      bailoutReturnStubInfo_;
-
   // The JitRealm stores stubs to concatenate strings inline and perform RegExp
   // calls inline. These bake in zone and realm specific pointers and can't be
   // stored in JitRuntime. They also are dependent on the value of
   // 'stringsCanBeInNursery' and must be flushed when its value changes.
   //
   // These are weak pointers, but they can by accessed during off-thread Ion
   // compilation and therefore can't use the usual read barrier. Instead, we
   // record which stubs have been read and perform the appropriate barriers in
@@ -552,24 +592,16 @@ class JitRealm {
                                 Handle<JitCode*> stubCode) {
     MOZ_ASSERT(stubCode);
     if (!stubCodes_->putNew(key, stubCode.get())) {
       ReportOutOfMemory(cx);
       return false;
     }
     return true;
   }
-  void initBailoutReturnAddr(void* addr, uint32_t key, BailoutReturnStub kind) {
-    MOZ_ASSERT(bailoutReturnStubInfo_[kind].addr == nullptr);
-    bailoutReturnStubInfo_[kind] = BailoutReturnStubInfo{addr, key};
-  }
-  void* bailoutReturnAddr(BailoutReturnStub kind) {
-    MOZ_ASSERT(bailoutReturnStubInfo_[kind].addr);
-    return bailoutReturnStubInfo_[kind].addr;
-  }
 
   JitRealm();
   ~JitRealm();
 
   MOZ_MUST_USE bool initialize(JSContext* cx, bool zoneHasNurseryStrings);
 
   // Initialize code stubs only used by Ion, not Baseline.
   MOZ_MUST_USE bool ensureIonStubsExist(JSContext* cx) {