Bug 1530937 part 16 - Convert tailCallVMs. r=tcampbell
authorJan de Mooij <jdemooij@mozilla.com>
Tue, 12 Mar 2019 14:06:22 +0000
changeset 521531 6382f22140dd664135ef6a9142ec98fadb33c183
parent 521530 5aee215799365ba80bbede0bf44edf24c9ff365a
child 521532 488d49d434f8d0e7c40faf8971a93e6205a4a098
push id10867
push userdvarga@mozilla.com
push dateThu, 14 Mar 2019 15:20:45 +0000
treeherdermozilla-beta@abad13547875 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstcampbell
bugs1530937
milestone67.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 1530937 part 16 - Convert tailCallVMs. r=tcampbell Tail calls have their own list/array/enum to improve type safety. Differential Revision: https://phabricator.services.mozilla.com/D23112
js/src/jit/BaselineCacheIRCompiler.cpp
js/src/jit/BaselineCompiler.cpp
js/src/jit/BaselineIC.cpp
js/src/jit/BaselineIC.h
js/src/jit/JitRealm.h
js/src/jit/VMFunctionList-inl.h
js/src/jit/VMFunctions.cpp
js/src/jit/VMFunctions.h
--- a/js/src/jit/BaselineCacheIRCompiler.cpp
+++ b/js/src/jit/BaselineCacheIRCompiler.cpp
@@ -35,24 +35,31 @@ Address CacheRegisterAllocator::addressO
 
 // BaselineCacheIRCompiler compiles CacheIR to BaselineIC native code.
 class MOZ_RAII BaselineCacheIRCompiler : public CacheIRCompiler {
   bool inStubFrame_;
   bool makesGCCalls_;
   BaselineCacheIRStubKind kind_;
 
   void callVMInternal(MacroAssembler& masm, VMFunctionId id);
-  void tailCallVM(MacroAssembler& masm, const VMFunction& fun);
 
   template <typename Fn, Fn fn>
   void callVM(MacroAssembler& masm) {
     VMFunctionId id = VMFunctionToId<Fn, fn>::id;
     callVMInternal(masm, id);
   }
 
+  void tailCallVMInternal(MacroAssembler& masm, TailCallVMFunctionId id);
+
+  template <typename Fn, Fn fn>
+  void tailCallVM(MacroAssembler& masm) {
+    TailCallVMFunctionId id = TailCallVMFunctionToId<Fn, fn>::id;
+    tailCallVMInternal(masm, id);
+  }
+
   MOZ_MUST_USE bool callTypeUpdateIC(Register obj, ValueOperand val,
                                      Register scratch,
                                      LiveGeneralRegisterSet saveRegs);
 
   MOZ_MUST_USE bool emitStoreSlotShared(bool isFixed);
   MOZ_MUST_USE bool emitAddAndStoreSlotShared(CacheOp op);
 
  public:
@@ -153,21 +160,22 @@ void BaselineCacheIRCompiler::callVMInte
   MOZ_ASSERT(inStubFrame_);
 
   TrampolinePtr code = cx_->runtime()->jitRuntime()->getVMWrapper(id);
   MOZ_ASSERT(GetVMFunction(id).expectTailCall == NonTailCall);
 
   EmitBaselineCallVM(code, masm);
 }
 
-void BaselineCacheIRCompiler::tailCallVM(MacroAssembler& masm,
-                                         const VMFunction& fun) {
+void BaselineCacheIRCompiler::tailCallVMInternal(MacroAssembler& masm,
+                                                 TailCallVMFunctionId id) {
   MOZ_ASSERT(!inStubFrame_);
 
-  TrampolinePtr code = cx_->runtime()->jitRuntime()->getVMWrapper(fun);
+  TrampolinePtr code = cx_->runtime()->jitRuntime()->getVMWrapper(id);
+  const VMFunctionData& fun = GetVMFunction(id);
   MOZ_ASSERT(fun.expectTailCall == TailCall);
   size_t argSize = fun.explicitStackSlots() * sizeof(void*);
 
   EmitBaselineTailCallVM(code, masm, argSize);
 }
 
 static size_t GetEnteredOffset(BaselineCacheIRStubKind kind) {
   switch (kind) {
@@ -2409,12 +2417,13 @@ bool BaselineCacheIRCompiler::emitCallSt
   // For the expression decompiler
   EmitRestoreTailCallReg(masm);
   masm.pushValue(lhs);
   masm.pushValue(rhs);
 
   masm.pushValue(rhs);
   masm.pushValue(lhs);
 
-  tailCallVM(masm, DoConcatStringObjectInfo);
+  using Fn = bool (*)(JSContext*, HandleValue, HandleValue, MutableHandleValue);
+  tailCallVM<Fn, DoConcatStringObject>(masm);
 
   return true;
 }
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -5297,23 +5297,16 @@ bool BaselineCodeGen<Handler>::emit_JSOP
   if (!callVM<Fn, jit::FinalSuspend>()) {
     return false;
   }
 
   masm.loadValue(frame.addressOfReturnValue(), JSReturnOperand);
   return emitReturn();
 }
 
-typedef bool (*GeneratorThrowFn)(JSContext*, BaselineFrame*,
-                                 Handle<AbstractGeneratorObject*>, HandleValue,
-                                 uint32_t);
-static const VMFunction GeneratorThrowOrReturnInfo =
-    FunctionInfo<GeneratorThrowFn>(jit::GeneratorThrowOrReturn,
-                                   "GeneratorThrowOrReturn", TailCall);
-
 template <>
 bool BaselineCompilerCodeGen::emit_JSOP_RESUME() {
   auto resumeKind = AbstractGeneratorObject::getResumeKind(handler.pc());
 
   frame.syncStack(0);
   masm.assertStackAlignment(sizeof(Value), 0);
 
   AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
@@ -5513,18 +5506,23 @@ bool BaselineCompilerCodeGen::emit_JSOP_
     masm.loadBaselineFramePtr(BaselineFrameReg, scratch2);
 
     prepareVMCall();
     pushArg(Imm32(resumeKind));
     pushArg(retVal);
     pushArg(genObj);
     pushArg(scratch2);
 
-    const VMFunction& fun = GeneratorThrowOrReturnInfo;
-    TrampolinePtr code = cx->runtime()->jitRuntime()->getVMWrapper(fun);
+    using Fn =
+        bool (*)(JSContext*, BaselineFrame*, Handle<AbstractGeneratorObject*>,
+                 HandleValue, uint32_t);
+    TailCallVMFunctionId id =
+        TailCallVMFunctionToId<Fn, jit::GeneratorThrowOrReturn>::id;
+    TrampolinePtr code = cx->runtime()->jitRuntime()->getVMWrapper(id);
+    const VMFunctionData& fun = GetVMFunction(id);
 
     // Create and push the frame descriptor.
     masm.subStackPtrFrom(scratch1);
     masm.makeFrameDescriptor(scratch1, FrameType::BaselineJS,
                              ExitFrameLayout::Size());
     masm.push(scratch1);
 
     // We have created a baseline frame as if we were the
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -1095,18 +1095,20 @@ JitCode* ICStubCompiler::getStubCode() {
 
 #ifdef JS_ION_PERF
   writePerfSpewerJitCodeProfile(newStubCode, "BaselineIC");
 #endif
 
   return newStubCode;
 }
 
-bool ICStubCompiler::tailCallVM(const VMFunction& fun, MacroAssembler& masm) {
-  TrampolinePtr code = cx->runtime()->jitRuntime()->getVMWrapper(fun);
+bool ICStubCompiler::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) {
   MOZ_ASSERT(inStubFrame_);
@@ -1119,16 +1121,22 @@ bool ICStubCompiler::callVMInternal(Macr
 }
 
 template <typename Fn, Fn fn>
 bool ICStubCompiler::callVM(MacroAssembler& masm) {
   VMFunctionId id = VMFunctionToId<Fn, fn>::id;
   return callVMInternal(masm, id);
 }
 
+template <typename Fn, Fn fn>
+bool ICStubCompiler::tailCallVM(MacroAssembler& masm) {
+  TailCallVMFunctionId id = TailCallVMFunctionToId<Fn, fn>::id;
+  return tailCallVMInternal(masm, id);
+}
+
 void ICStubCompiler::enterStubFrame(MacroAssembler& masm, Register scratch) {
   EmitBaselineEnterStubFrame(masm, scratch);
 #ifdef DEBUG
   framePushedAtEnterStubFrame_ = masm.framePushed();
 #endif
 
   MOZ_ASSERT(!inStubFrame_);
   inStubFrame_ = true;
@@ -1361,19 +1369,19 @@ bool ICTypeMonitor_Fallback::addMonitorS
       MOZ_ASSERT(iter->toMonitoredStub()->firstMonitorStub() == this);
       iter->toMonitoredStub()->updateFirstMonitorStub(firstMonitorStub_);
     }
   }
 
   return true;
 }
 
-static bool DoTypeMonitorFallback(JSContext* cx, BaselineFrame* frame,
-                                  ICTypeMonitor_Fallback* stub,
-                                  HandleValue value, MutableHandleValue res) {
+bool DoTypeMonitorFallback(JSContext* cx, BaselineFrame* frame,
+                           ICTypeMonitor_Fallback* stub, HandleValue value,
+                           MutableHandleValue res) {
   JSScript* script = frame->script();
   jsbytecode* pc = stub->icEntry()->pc(script);
   TypeFallbackICSpew(cx, stub, "TypeMonitor");
 
   // Copy input value to res.
   res.set(value);
 
   if (MOZ_UNLIKELY(value.isMagic())) {
@@ -1415,34 +1423,29 @@ static bool DoTypeMonitorFallback(JSCont
   } else {
     types = TypeScript::BytecodeTypes(script, pc);
     TypeScript::Monitor(cx, script, pc, types, value);
   }
 
   return stub->addMonitorStubForValue(cx, frame, types, value);
 }
 
-typedef bool (*DoTypeMonitorFallbackFn)(JSContext*, BaselineFrame*,
-                                        ICTypeMonitor_Fallback*, HandleValue,
-                                        MutableHandleValue);
-static const VMFunction DoTypeMonitorFallbackInfo =
-    FunctionInfo<DoTypeMonitorFallbackFn>(DoTypeMonitorFallback,
-                                          "DoTypeMonitorFallback", TailCall);
-
 bool ICTypeMonitor_Fallback::Compiler::generateStubCode(MacroAssembler& masm) {
   MOZ_ASSERT(R0 == JSReturnOperand);
 
   // Restore the tail call register.
   EmitRestoreTailCallReg(masm);
 
   masm.pushValue(R0);
   masm.push(ICStubReg);
   masm.pushBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
 
-  return tailCallVM(DoTypeMonitorFallbackInfo, masm);
+  using Fn = bool (*)(JSContext*, BaselineFrame*, ICTypeMonitor_Fallback*,
+                      HandleValue, MutableHandleValue);
+  return tailCallVM<Fn, DoTypeMonitorFallback>(masm);
 }
 
 bool ICTypeMonitor_PrimitiveSet::Compiler::generateStubCode(
     MacroAssembler& masm) {
   Label success;
   if ((flags_ & TypeToFlag(ValueType::Int32)) &&
       !(flags_ & TypeToFlag(ValueType::Double))) {
     masm.branchTestInt32(Assembler::Equal, R0, &success);
@@ -1881,50 +1884,47 @@ bool ICTypeUpdate_AnyValue::Compiler::ge
   EmitReturnFromIC(masm);
   return true;
 }
 
 //
 // ToBool_Fallback
 //
 
-static bool DoToBoolFallback(JSContext* cx, BaselineFrame* frame,
-                             ICToBool_Fallback* stub, HandleValue arg,
-                             MutableHandleValue ret) {
+bool DoToBoolFallback(JSContext* cx, BaselineFrame* frame,
+                      ICToBool_Fallback* stub, HandleValue arg,
+                      MutableHandleValue ret) {
   stub->incrementEnteredCount();
   FallbackICSpew(cx, stub, "ToBool");
 
   MOZ_ASSERT(!arg.isBoolean());
 
   TryAttachStub<ToBoolIRGenerator>("ToBool", cx, frame, stub,
                                    BaselineCacheIRStubKind::Regular, arg);
 
   bool cond = ToBoolean(arg);
   ret.setBoolean(cond);
 
   return true;
 }
 
-typedef bool (*pf)(JSContext*, BaselineFrame*, ICToBool_Fallback*, HandleValue,
-                   MutableHandleValue);
-static const VMFunction fun =
-    FunctionInfo<pf>(DoToBoolFallback, "DoToBoolFallback", TailCall);
-
 bool ICToBool_Fallback::Compiler::generateStubCode(MacroAssembler& masm) {
   MOZ_ASSERT(R0 == JSReturnOperand);
 
   // Restore the tail call register.
   EmitRestoreTailCallReg(masm);
 
   // Push arguments.
   masm.pushValue(R0);
   masm.push(ICStubReg);
   pushStubPayload(masm, R0.scratchReg());
 
-  return tailCallVM(fun, masm);
+  using Fn = bool (*)(JSContext*, BaselineFrame*, ICToBool_Fallback*,
+                      HandleValue, MutableHandleValue);
+  return tailCallVM<Fn, DoToBoolFallback>(masm);
 }
 
 static void StripPreliminaryObjectStubs(JSContext* cx, ICFallbackStub* stub) {
   // Before the new script properties analysis has been performed on a type,
   // all instances of that type have the maximum number of fixed slots.
   // Afterwards, the objects (even the preliminary ones) might be changed
   // to reduce the number of fixed slots they have. If we generate stubs for
   // both the old and new number of fixed slots, the stub will look
@@ -1945,19 +1945,19 @@ static void StripPreliminaryObjectStubs(
     }
   }
 }
 
 //
 // GetElem_Fallback
 //
 
-static bool DoGetElemFallback(JSContext* cx, BaselineFrame* frame,
-                              ICGetElem_Fallback* stub, HandleValue lhs,
-                              HandleValue rhs, MutableHandleValue res) {
+bool DoGetElemFallback(JSContext* cx, BaselineFrame* frame,
+                       ICGetElem_Fallback* stub, HandleValue lhs,
+                       HandleValue rhs, MutableHandleValue res) {
   stub->incrementEnteredCount();
 
   RootedScript script(cx, frame->script());
   jsbytecode* pc = stub->icEntry()->pc(frame->script());
   StackTypeSet* types = TypeScript::BytecodeTypes(script, pc);
 
   JSOp op = JSOp(*pc);
   FallbackICSpew(cx, stub, "GetElem(%s)", CodeName[op]);
@@ -2037,20 +2037,20 @@ static bool DoGetElemFallback(JSContext*
   if (rhs.isNumber() && rhs.isDouble() &&
       !mozilla::NumberEqualsInt32(rhs.toDouble(), &representable)) {
     stub->setSawNonIntegerIndex();
   }
 
   return true;
 }
 
-static bool DoGetElemSuperFallback(JSContext* cx, BaselineFrame* frame,
-                                   ICGetElem_Fallback* stub, HandleValue lhs,
-                                   HandleValue rhs, HandleValue receiver,
-                                   MutableHandleValue res) {
+bool DoGetElemSuperFallback(JSContext* cx, BaselineFrame* frame,
+                            ICGetElem_Fallback* stub, HandleValue lhs,
+                            HandleValue rhs, HandleValue receiver,
+                            MutableHandleValue res) {
   stub->incrementEnteredCount();
 
   RootedScript script(cx, frame->script());
   jsbytecode* pc = stub->icEntry()->pc(frame->script());
   StackTypeSet* types = TypeScript::BytecodeTypes(script, pc);
 
   JSOp op = JSOp(*pc);
   FallbackICSpew(cx, stub, "GetElemSuper(%s)", CodeName[op]);
@@ -2115,32 +2115,16 @@ static bool DoGetElemSuperFallback(JSCon
   if (rhs.isNumber() && rhs.isDouble() &&
       !mozilla::NumberEqualsInt32(rhs.toDouble(), &representable)) {
     stub->setSawNonIntegerIndex();
   }
 
   return true;
 }
 
-typedef bool (*DoGetElemFallbackFn)(JSContext*, BaselineFrame*,
-                                    ICGetElem_Fallback*, HandleValue,
-                                    HandleValue, MutableHandleValue);
-static const VMFunction DoGetElemFallbackInfo =
-    FunctionInfo<DoGetElemFallbackFn>(DoGetElemFallback, "DoGetElemFallback",
-                                      TailCall, PopValues(2));
-
-typedef bool (*DoGetElemSuperFallbackFn)(JSContext*, BaselineFrame*,
-                                         ICGetElem_Fallback*, HandleValue,
-                                         HandleValue, HandleValue,
-                                         MutableHandleValue);
-static const VMFunction DoGetElemSuperFallbackInfo =
-    FunctionInfo<DoGetElemSuperFallbackFn>(DoGetElemSuperFallback,
-                                           "DoGetElemSuperFallback", TailCall,
-                                           PopValues(3));
-
 bool ICGetElem_Fallback::Compiler::generateStubCode(MacroAssembler& masm) {
   MOZ_ASSERT(R0 == JSReturnOperand);
 
   // Restore the tail call register.
   EmitRestoreTailCallReg(masm);
 
   // Super property getters use a |this| that differs from base object
   if (hasReceiver_) {
@@ -2154,31 +2138,36 @@ bool ICGetElem_Fallback::Compiler::gener
 
     // Push arguments.
     masm.pushValue(R0);  // Receiver
     masm.pushValue(R1);  // Index
     masm.pushValue(Address(masm.getStackPointer(), sizeof(Value) * 5));  // Obj
     masm.push(ICStubReg);
     masm.pushBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
 
-    if (!tailCallVM(DoGetElemSuperFallbackInfo, masm)) {
+    using Fn =
+        bool (*)(JSContext*, BaselineFrame*, ICGetElem_Fallback*, HandleValue,
+                 HandleValue, HandleValue, MutableHandleValue);
+    if (!tailCallVM<Fn, DoGetElemSuperFallback>(masm)) {
       return false;
     }
   } else {
     // Ensure stack is fully synced for the expression decompiler.
     masm.pushValue(R0);
     masm.pushValue(R1);
 
     // Push arguments.
     masm.pushValue(R1);
     masm.pushValue(R0);
     masm.push(ICStubReg);
     masm.pushBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
 
-    if (!tailCallVM(DoGetElemFallbackInfo, masm)) {
+    using Fn = bool (*)(JSContext*, BaselineFrame*, ICGetElem_Fallback*,
+                        HandleValue, HandleValue, MutableHandleValue);
+    if (!tailCallVM<Fn, DoGetElemFallback>(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();
@@ -2211,20 +2200,19 @@ void ICGetElem_Fallback::Compiler::postG
 static void SetUpdateStubData(ICCacheIR_Updated* stub,
                               const PropertyTypeCheckInfo* info) {
   if (info->isSet()) {
     stub->updateStubGroup() = info->group();
     stub->updateStubId() = info->id();
   }
 }
 
-static bool DoSetElemFallback(JSContext* cx, BaselineFrame* frame,
-                              ICSetElem_Fallback* stub, Value* stack,
-                              HandleValue objv, HandleValue index,
-                              HandleValue rhs) {
+bool DoSetElemFallback(JSContext* cx, BaselineFrame* frame,
+                       ICSetElem_Fallback* stub, Value* stack, HandleValue objv,
+                       HandleValue index, HandleValue rhs) {
   stub->incrementEnteredCount();
 
   RootedScript script(cx, frame->script());
   RootedScript outerScript(cx, script);
   jsbytecode* pc = stub->icEntry()->pc(script);
   JSOp op = JSOp(*pc);
   FallbackICSpew(cx, stub, "SetElem(%s)", CodeName[JSOp(*pc)]);
 
@@ -2355,23 +2343,16 @@ static bool DoSetElemFallback(JSContext*
     if (!attached && !isTemporarilyUnoptimizable) {
       stub->state().trackNotAttached();
     }
   }
 
   return true;
 }
 
-typedef bool (*DoSetElemFallbackFn)(JSContext*, BaselineFrame*,
-                                    ICSetElem_Fallback*, Value*, HandleValue,
-                                    HandleValue, HandleValue);
-static const VMFunction DoSetElemFallbackInfo =
-    FunctionInfo<DoSetElemFallbackFn>(DoSetElemFallback, "DoSetElemFallback",
-                                      TailCall, PopValues(2));
-
 bool ICSetElem_Fallback::Compiler::generateStubCode(MacroAssembler& masm) {
   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
@@ -2394,17 +2375,19 @@ bool ICSetElem_Fallback::Compiler::gener
   // (pushed for the decompiler) with the rhs.
   masm.computeEffectiveAddress(
       Address(masm.getStackPointer(), 3 * sizeof(Value)), R0.scratchReg());
   masm.push(R0.scratchReg());
 
   masm.push(ICStubReg);
   pushStubPayload(masm, R0.scratchReg());
 
-  return tailCallVM(DoSetElemFallbackInfo, masm);
+  using Fn = bool (*)(JSContext*, BaselineFrame*, ICSetElem_Fallback*, Value*,
+                      HandleValue, HandleValue, HandleValue);
+  return tailCallVM<Fn, DoSetElemFallback>(masm);
 }
 
 void ICScript::noteHasDenseAdd(uint32_t pcOffset) {
   ICEntry& entry = icEntryFromPCOffset(pcOffset);
   ICFallbackStub* stub = entry.fallbackStub();
 
   if (stub->isSetElem_Fallback()) {
     stub->toSetElem_Fallback()->noteHasDenseAdd();
@@ -2494,19 +2477,19 @@ template void StoreToTypedArray(JSContex
                                 Scalar::Type type, const ValueOperand& value,
                                 const BaseIndex& dest, Register scratch,
                                 Label* failure);
 
 //
 // In_Fallback
 //
 
-static bool DoInFallback(JSContext* cx, BaselineFrame* frame,
-                         ICIn_Fallback* stub, HandleValue key,
-                         HandleValue objValue, MutableHandleValue res) {
+bool DoInFallback(JSContext* cx, BaselineFrame* frame, ICIn_Fallback* stub,
+                  HandleValue key, HandleValue objValue,
+                  MutableHandleValue res) {
   stub->incrementEnteredCount();
 
   FallbackICSpew(cx, stub, "In");
 
   if (!objValue.isObject()) {
     ReportInNotObjectError(cx, key, -2, objValue, -1);
     return false;
   }
@@ -2520,44 +2503,41 @@ static bool DoInFallback(JSContext* cx, 
   if (!OperatorIn(cx, key, obj, &cond)) {
     return false;
   }
   res.setBoolean(cond);
 
   return true;
 }
 
-typedef bool (*DoInFallbackFn)(JSContext*, BaselineFrame*, ICIn_Fallback*,
-                               HandleValue, HandleValue, MutableHandleValue);
-static const VMFunction DoInFallbackInfo = FunctionInfo<DoInFallbackFn>(
-    DoInFallback, "DoInFallback", TailCall, PopValues(2));
-
 bool ICIn_Fallback::Compiler::generateStubCode(MacroAssembler& masm) {
   EmitRestoreTailCallReg(masm);
 
   // Sync for the decompiler.
   masm.pushValue(R0);
   masm.pushValue(R1);
 
   // Push arguments.
   masm.pushValue(R1);
   masm.pushValue(R0);
   masm.push(ICStubReg);
   pushStubPayload(masm, R0.scratchReg());
 
-  return tailCallVM(DoInFallbackInfo, masm);
+  using Fn = bool (*)(JSContext*, BaselineFrame*, ICIn_Fallback*, HandleValue,
+                      HandleValue, MutableHandleValue);
+  return tailCallVM<Fn, DoInFallback>(masm);
 }
 
 //
 // HasOwn_Fallback
 //
 
-static bool DoHasOwnFallback(JSContext* cx, BaselineFrame* frame,
-                             ICHasOwn_Fallback* stub, HandleValue keyValue,
-                             HandleValue objValue, MutableHandleValue res) {
+bool DoHasOwnFallback(JSContext* cx, BaselineFrame* frame,
+                      ICHasOwn_Fallback* stub, HandleValue keyValue,
+                      HandleValue objValue, MutableHandleValue res) {
   stub->incrementEnteredCount();
 
   FallbackICSpew(cx, stub, "HasOwn");
 
   TryAttachStub<HasPropIRGenerator>("HasOwn", cx, frame, stub,
                                     BaselineCacheIRStubKind::Regular,
                                     CacheKind::HasOwn, keyValue, objValue);
 
@@ -2565,45 +2545,41 @@ static bool DoHasOwnFallback(JSContext* 
   if (!HasOwnProperty(cx, objValue, keyValue, &found)) {
     return false;
   }
 
   res.setBoolean(found);
   return true;
 }
 
-typedef bool (*DoHasOwnFallbackFn)(JSContext*, BaselineFrame*,
-                                   ICHasOwn_Fallback*, HandleValue, HandleValue,
-                                   MutableHandleValue);
-static const VMFunction DoHasOwnFallbackInfo = FunctionInfo<DoHasOwnFallbackFn>(
-    DoHasOwnFallback, "DoHasOwnFallback", TailCall, PopValues(2));
-
 bool ICHasOwn_Fallback::Compiler::generateStubCode(MacroAssembler& masm) {
   EmitRestoreTailCallReg(masm);
 
   // Sync for the decompiler.
   masm.pushValue(R0);
   masm.pushValue(R1);
 
   // Push arguments.
   masm.pushValue(R1);
   masm.pushValue(R0);
   masm.push(ICStubReg);
   pushStubPayload(masm, R0.scratchReg());
 
-  return tailCallVM(DoHasOwnFallbackInfo, masm);
+  using Fn = bool (*)(JSContext*, BaselineFrame*, ICHasOwn_Fallback*,
+                      HandleValue, HandleValue, MutableHandleValue);
+  return tailCallVM<Fn, DoHasOwnFallback>(masm);
 }
 
 //
 // GetName_Fallback
 //
 
-static bool DoGetNameFallback(JSContext* cx, BaselineFrame* frame,
-                              ICGetName_Fallback* stub, HandleObject envChain,
-                              MutableHandleValue res) {
+bool DoGetNameFallback(JSContext* cx, BaselineFrame* frame,
+                       ICGetName_Fallback* stub, HandleObject envChain,
+                       MutableHandleValue res) {
   stub->incrementEnteredCount();
 
   RootedScript script(cx, frame->script());
   jsbytecode* pc = stub->icEntry()->pc(script);
   mozilla::DebugOnly<JSOp> op = JSOp(*pc);
   FallbackICSpew(cx, stub, "GetName(%s)", CodeName[JSOp(*pc)]);
 
   MOZ_ASSERT(op == JSOP_GETNAME || op == JSOP_GETGNAME);
@@ -2632,42 +2608,37 @@ static bool DoGetNameFallback(JSContext*
   // Add a type monitor stub for the resulting value.
   if (!stub->addMonitorStubForValue(cx, frame, types, res)) {
     return false;
   }
 
   return true;
 }
 
-typedef bool (*DoGetNameFallbackFn)(JSContext*, BaselineFrame*,
-                                    ICGetName_Fallback*, HandleObject,
-                                    MutableHandleValue);
-static const VMFunction DoGetNameFallbackInfo =
-    FunctionInfo<DoGetNameFallbackFn>(DoGetNameFallback, "DoGetNameFallback",
-                                      TailCall);
-
 bool ICGetName_Fallback::Compiler::generateStubCode(MacroAssembler& masm) {
   MOZ_ASSERT(R0 == JSReturnOperand);
 
   EmitRestoreTailCallReg(masm);
 
   masm.push(R0.scratchReg());
   masm.push(ICStubReg);
   pushStubPayload(masm, R0.scratchReg());
 
-  return tailCallVM(DoGetNameFallbackInfo, masm);
+  using Fn = bool (*)(JSContext*, BaselineFrame*, ICGetName_Fallback*,
+                      HandleObject, MutableHandleValue);
+  return tailCallVM<Fn, DoGetNameFallback>(masm);
 }
 
 //
 // BindName_Fallback
 //
 
-static bool DoBindNameFallback(JSContext* cx, BaselineFrame* frame,
-                               ICBindName_Fallback* stub, HandleObject envChain,
-                               MutableHandleValue res) {
+bool DoBindNameFallback(JSContext* cx, BaselineFrame* frame,
+                        ICBindName_Fallback* stub, HandleObject envChain,
+                        MutableHandleValue res) {
   stub->incrementEnteredCount();
 
   jsbytecode* pc = stub->icEntry()->pc(frame->script());
   mozilla::DebugOnly<JSOp> op = JSOp(*pc);
   FallbackICSpew(cx, stub, "BindName(%s)", CodeName[JSOp(*pc)]);
 
   MOZ_ASSERT(op == JSOP_BINDNAME || op == JSOP_BINDGNAME);
 
@@ -2681,42 +2652,37 @@ static bool DoBindNameFallback(JSContext
   if (!LookupNameUnqualified(cx, name, envChain, &scope)) {
     return false;
   }
 
   res.setObject(*scope);
   return true;
 }
 
-typedef bool (*DoBindNameFallbackFn)(JSContext*, BaselineFrame*,
-                                     ICBindName_Fallback*, HandleObject,
-                                     MutableHandleValue);
-static const VMFunction DoBindNameFallbackInfo =
-    FunctionInfo<DoBindNameFallbackFn>(DoBindNameFallback, "DoBindNameFallback",
-                                       TailCall);
-
 bool ICBindName_Fallback::Compiler::generateStubCode(MacroAssembler& masm) {
   MOZ_ASSERT(R0 == JSReturnOperand);
 
   EmitRestoreTailCallReg(masm);
 
   masm.push(R0.scratchReg());
   masm.push(ICStubReg);
   pushStubPayload(masm, R0.scratchReg());
 
-  return tailCallVM(DoBindNameFallbackInfo, masm);
+  using Fn = bool (*)(JSContext*, BaselineFrame*, ICBindName_Fallback*,
+                      HandleObject, MutableHandleValue);
+  return tailCallVM<Fn, DoBindNameFallback>(masm);
 }
 
 //
 // GetIntrinsic_Fallback
 //
 
-static bool DoGetIntrinsicFallback(JSContext* cx, BaselineFrame* frame,
-                                   ICGetIntrinsic_Fallback* stub,
-                                   MutableHandleValue res) {
+bool DoGetIntrinsicFallback(JSContext* cx, BaselineFrame* frame,
+                            ICGetIntrinsic_Fallback* stub,
+                            MutableHandleValue res) {
   stub->incrementEnteredCount();
 
   RootedScript script(cx, frame->script());
   jsbytecode* pc = stub->icEntry()->pc(script);
   mozilla::DebugOnly<JSOp> op = JSOp(*pc);
   FallbackICSpew(cx, stub, "GetIntrinsic(%s)", CodeName[JSOp(*pc)]);
 
   MOZ_ASSERT(op == JSOP_GETINTRINSIC);
@@ -2732,30 +2698,25 @@ static bool DoGetIntrinsicFallback(JSCon
   TypeScript::Monitor(cx, script, pc, res);
 
   TryAttachStub<GetIntrinsicIRGenerator>("GetIntrinsic", cx, frame, stub,
                                          BaselineCacheIRStubKind::Regular, res);
 
   return true;
 }
 
-typedef bool (*DoGetIntrinsicFallbackFn)(JSContext*, BaselineFrame*,
-                                         ICGetIntrinsic_Fallback*,
-                                         MutableHandleValue);
-static const VMFunction DoGetIntrinsicFallbackInfo =
-    FunctionInfo<DoGetIntrinsicFallbackFn>(DoGetIntrinsicFallback,
-                                           "DoGetIntrinsicFallback", TailCall);
-
 bool ICGetIntrinsic_Fallback::Compiler::generateStubCode(MacroAssembler& masm) {
   EmitRestoreTailCallReg(masm);
 
   masm.push(ICStubReg);
   pushStubPayload(masm, R0.scratchReg());
 
-  return tailCallVM(DoGetIntrinsicFallbackInfo, masm);
+  using Fn = bool (*)(JSContext*, BaselineFrame*, ICGetIntrinsic_Fallback*,
+                      MutableHandleValue);
+  return tailCallVM<Fn, DoGetIntrinsicFallback>(masm);
 }
 
 //
 // GetProp_Fallback
 //
 
 static bool ComputeGetPropResult(JSContext* cx, BaselineFrame* frame, JSOp op,
                                  HandlePropertyName name,
@@ -2785,19 +2746,19 @@ static bool ComputeGetPropResult(JSConte
         return false;
       }
     }
   }
 
   return true;
 }
 
-static bool DoGetPropFallback(JSContext* cx, BaselineFrame* frame,
-                              ICGetProp_Fallback* stub, MutableHandleValue val,
-                              MutableHandleValue res) {
+bool DoGetPropFallback(JSContext* cx, BaselineFrame* frame,
+                       ICGetProp_Fallback* stub, MutableHandleValue val,
+                       MutableHandleValue res) {
   stub->incrementEnteredCount();
 
   RootedScript script(cx, frame->script());
   jsbytecode* pc = stub->icEntry()->pc(script);
   JSOp op = JSOp(*pc);
   FallbackICSpew(cx, stub, "GetProp(%s)", CodeName[op]);
 
   MOZ_ASSERT(op == JSOP_GETPROP || op == JSOP_CALLPROP || op == JSOP_LENGTH ||
@@ -2848,20 +2809,19 @@ static bool DoGetPropFallback(JSContext*
 
   // Add a type monitor stub for the resulting value.
   if (!stub->addMonitorStubForValue(cx, frame, types, res)) {
     return false;
   }
   return true;
 }
 
-static bool DoGetPropSuperFallback(JSContext* cx, BaselineFrame* frame,
-                                   ICGetProp_Fallback* stub,
-                                   HandleValue receiver, MutableHandleValue val,
-                                   MutableHandleValue res) {
+bool DoGetPropSuperFallback(JSContext* cx, BaselineFrame* frame,
+                            ICGetProp_Fallback* stub, HandleValue receiver,
+                            MutableHandleValue val, MutableHandleValue res) {
   stub->incrementEnteredCount();
 
   RootedScript script(cx, frame->script());
   jsbytecode* pc = stub->icEntry()->pc(script);
   FallbackICSpew(cx, stub, "GetPropSuper(%s)", CodeName[JSOp(*pc)]);
 
   MOZ_ASSERT(JSOp(*pc) == JSOP_GETPROP_SUPER);
 
@@ -2913,57 +2873,46 @@ static bool DoGetPropSuperFallback(JSCon
   // Add a type monitor stub for the resulting value.
   if (!stub->addMonitorStubForValue(cx, frame, types, res)) {
     return false;
   }
 
   return true;
 }
 
-typedef bool (*DoGetPropFallbackFn)(JSContext*, BaselineFrame*,
-                                    ICGetProp_Fallback*, MutableHandleValue,
-                                    MutableHandleValue);
-static const VMFunction DoGetPropFallbackInfo =
-    FunctionInfo<DoGetPropFallbackFn>(DoGetPropFallback, "DoGetPropFallback",
-                                      TailCall, PopValues(1));
-
-typedef bool (*DoGetPropSuperFallbackFn)(JSContext*, BaselineFrame*,
-                                         ICGetProp_Fallback*, HandleValue,
-                                         MutableHandleValue,
-                                         MutableHandleValue);
-static const VMFunction DoGetPropSuperFallbackInfo =
-    FunctionInfo<DoGetPropSuperFallbackFn>(DoGetPropSuperFallback,
-                                           "DoGetPropSuperFallback", TailCall);
-
 bool ICGetProp_Fallback::Compiler::generateStubCode(MacroAssembler& masm) {
   MOZ_ASSERT(R0 == JSReturnOperand);
 
   EmitRestoreTailCallReg(masm);
 
   // Super property getters use a |this| that differs from base object
   if (hasReceiver_) {
     // Push arguments.
     masm.pushValue(R0);
     masm.pushValue(R1);
     masm.push(ICStubReg);
     masm.pushBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
 
-    if (!tailCallVM(DoGetPropSuperFallbackInfo, masm)) {
+    using Fn = bool (*)(JSContext*, BaselineFrame*, ICGetProp_Fallback*,
+                        HandleValue, MutableHandleValue, MutableHandleValue);
+    if (!tailCallVM<Fn, DoGetPropSuperFallback>(masm)) {
       return false;
     }
   } else {
     // Ensure stack is fully synced for the expression decompiler.
     masm.pushValue(R0);
 
     // Push arguments.
     masm.pushValue(R0);
     masm.push(ICStubReg);
     masm.pushBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
 
-    if (!tailCallVM(DoGetPropFallbackInfo, masm)) {
+    using Fn = bool (*)(JSContext*, BaselineFrame*, ICGetProp_Fallback*,
+                        MutableHandleValue, MutableHandleValue);
+    if (!tailCallVM<Fn, DoGetPropFallback>(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();
@@ -2992,19 +2941,19 @@ void ICGetProp_Fallback::Compiler::postG
   void* address = code->raw() + bailoutReturnOffset_.offset();
   cx->realm()->jitRealm()->initBailoutReturnAddr(address, getKey(), kind);
 }
 
 //
 // SetProp_Fallback
 //
 
-static bool DoSetPropFallback(JSContext* cx, BaselineFrame* frame,
-                              ICSetProp_Fallback* stub, Value* stack,
-                              HandleValue lhs, HandleValue rhs) {
+bool DoSetPropFallback(JSContext* cx, BaselineFrame* frame,
+                       ICSetProp_Fallback* stub, Value* stack, HandleValue lhs,
+                       HandleValue rhs) {
   stub->incrementEnteredCount();
 
   RootedScript script(cx, frame->script());
   jsbytecode* pc = stub->icEntry()->pc(script);
   JSOp op = JSOp(*pc);
   FallbackICSpew(cx, stub, "SetProp(%s)", CodeName[op]);
 
   MOZ_ASSERT(op == JSOP_SETPROP || op == JSOP_STRICTSETPROP ||
@@ -3140,23 +3089,16 @@ static bool DoSetPropFallback(JSContext*
     if (!attached && !isTemporarilyUnoptimizable) {
       stub->state().trackNotAttached();
     }
   }
 
   return true;
 }
 
-typedef bool (*DoSetPropFallbackFn)(JSContext*, BaselineFrame*,
-                                    ICSetProp_Fallback*, Value*, HandleValue,
-                                    HandleValue);
-static const VMFunction DoSetPropFallbackInfo =
-    FunctionInfo<DoSetPropFallbackFn>(DoSetPropFallback, "DoSetPropFallback",
-                                      TailCall, PopValues(1));
-
 bool ICSetProp_Fallback::Compiler::generateStubCode(MacroAssembler& masm) {
   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.
@@ -3171,17 +3113,19 @@ bool ICSetProp_Fallback::Compiler::gener
   // (pushed for the decompiler) with the RHS.
   masm.computeEffectiveAddress(
       Address(masm.getStackPointer(), 2 * sizeof(Value)), R0.scratchReg());
   masm.push(R0.scratchReg());
 
   masm.push(ICStubReg);
   pushStubPayload(masm, R0.scratchReg());
 
-  if (!tailCallVM(DoSetPropFallbackInfo, masm)) {
+  using Fn = bool (*)(JSContext*, BaselineFrame*, ICSetProp_Fallback*, Value*,
+                      HandleValue, HandleValue);
+  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());
@@ -5329,61 +5273,56 @@ bool ICCall_ScriptedFunCall::Compiler::g
   EmitStubGuardFailure(masm);
   return true;
 }
 
 //
 // GetIterator_Fallback
 //
 
-static bool DoGetIteratorFallback(JSContext* cx, BaselineFrame* frame,
-                                  ICGetIterator_Fallback* stub,
-                                  HandleValue value, MutableHandleValue res) {
+bool DoGetIteratorFallback(JSContext* cx, BaselineFrame* frame,
+                           ICGetIterator_Fallback* stub, HandleValue value,
+                           MutableHandleValue res) {
   stub->incrementEnteredCount();
   FallbackICSpew(cx, stub, "GetIterator");
 
   TryAttachStub<GetIteratorIRGenerator>(
       "GetIterator", cx, frame, stub, BaselineCacheIRStubKind::Regular, value);
 
   JSObject* iterobj = ValueToIterator(cx, value);
   if (!iterobj) {
     return false;
   }
 
   res.setObject(*iterobj);
   return true;
 }
 
-typedef bool (*DoGetIteratorFallbackFn)(JSContext*, BaselineFrame*,
-                                        ICGetIterator_Fallback*, HandleValue,
-                                        MutableHandleValue);
-static const VMFunction DoGetIteratorFallbackInfo =
-    FunctionInfo<DoGetIteratorFallbackFn>(
-        DoGetIteratorFallback, "DoGetIteratorFallback", TailCall, PopValues(1));
-
 bool ICGetIterator_Fallback::Compiler::generateStubCode(MacroAssembler& masm) {
   EmitRestoreTailCallReg(masm);
 
   // Sync stack for the decompiler.
   masm.pushValue(R0);
 
   masm.pushValue(R0);
   masm.push(ICStubReg);
   pushStubPayload(masm, R0.scratchReg());
 
-  return tailCallVM(DoGetIteratorFallbackInfo, masm);
+  using Fn = bool (*)(JSContext*, BaselineFrame*, ICGetIterator_Fallback*,
+                      HandleValue, MutableHandleValue);
+  return tailCallVM<Fn, DoGetIteratorFallback>(masm);
 }
 
 //
 // InstanceOf_Fallback
 //
 
-static bool DoInstanceOfFallback(JSContext* cx, BaselineFrame* frame,
-                                 ICInstanceOf_Fallback* stub, HandleValue lhs,
-                                 HandleValue rhs, MutableHandleValue res) {
+bool DoInstanceOfFallback(JSContext* cx, BaselineFrame* frame,
+                          ICInstanceOf_Fallback* stub, HandleValue lhs,
+                          HandleValue rhs, MutableHandleValue res) {
   stub->incrementEnteredCount();
 
   FallbackICSpew(cx, stub, "InstanceOf");
 
   if (!rhs.isObject()) {
     ReportValueError(cx, JSMSG_BAD_INSTANCEOF_RHS, -1, rhs, nullptr);
     return false;
   }
@@ -5410,71 +5349,62 @@ static bool DoInstanceOfFallback(JSConte
   EnsureTrackPropertyTypes(cx, obj, NameToId(cx->names().prototype));
 
   TryAttachStub<InstanceOfIRGenerator>("InstanceOf", cx, frame, stub,
                                        BaselineCacheIRStubKind::Regular, lhs,
                                        obj);
   return true;
 }
 
-typedef bool (*DoInstanceOfFallbackFn)(JSContext*, BaselineFrame*,
-                                       ICInstanceOf_Fallback*, HandleValue,
-                                       HandleValue, MutableHandleValue);
-static const VMFunction DoInstanceOfFallbackInfo =
-    FunctionInfo<DoInstanceOfFallbackFn>(
-        DoInstanceOfFallback, "DoInstanceOfFallback", TailCall, PopValues(2));
-
 bool ICInstanceOf_Fallback::Compiler::generateStubCode(MacroAssembler& masm) {
   EmitRestoreTailCallReg(masm);
 
   // Sync stack for the decompiler.
   masm.pushValue(R0);
   masm.pushValue(R1);
 
   masm.pushValue(R1);
   masm.pushValue(R0);
   masm.push(ICStubReg);
   pushStubPayload(masm, R0.scratchReg());
 
-  return tailCallVM(DoInstanceOfFallbackInfo, masm);
+  using Fn = bool (*)(JSContext*, BaselineFrame*, ICInstanceOf_Fallback*,
+                      HandleValue, HandleValue, MutableHandleValue);
+  return tailCallVM<Fn, DoInstanceOfFallback>(masm);
 }
 
 //
 // TypeOf_Fallback
 //
 
-static bool DoTypeOfFallback(JSContext* cx, BaselineFrame* frame,
-                             ICTypeOf_Fallback* stub, HandleValue val,
-                             MutableHandleValue res) {
+bool DoTypeOfFallback(JSContext* cx, BaselineFrame* frame,
+                      ICTypeOf_Fallback* stub, HandleValue val,
+                      MutableHandleValue res) {
   stub->incrementEnteredCount();
   FallbackICSpew(cx, stub, "TypeOf");
 
   TryAttachStub<TypeOfIRGenerator>("TypeOf", cx, frame, stub,
                                    BaselineCacheIRStubKind::Regular, val);
 
   JSType type = js::TypeOfValue(val);
   RootedString string(cx, TypeName(type, cx->names()));
   res.setString(string);
   return true;
 }
 
-typedef bool (*DoTypeOfFallbackFn)(JSContext*, BaselineFrame* frame,
-                                   ICTypeOf_Fallback*, HandleValue,
-                                   MutableHandleValue);
-static const VMFunction DoTypeOfFallbackInfo = FunctionInfo<DoTypeOfFallbackFn>(
-    DoTypeOfFallback, "DoTypeOfFallback", TailCall);
-
 bool ICTypeOf_Fallback::Compiler::generateStubCode(MacroAssembler& masm) {
   EmitRestoreTailCallReg(masm);
 
   masm.pushValue(R0);
   masm.push(ICStubReg);
   pushStubPayload(masm, R0.scratchReg());
 
-  return tailCallVM(DoTypeOfFallbackInfo, masm);
+  using Fn = bool (*)(JSContext*, BaselineFrame*, ICTypeOf_Fallback*,
+                      HandleValue, MutableHandleValue);
+  return tailCallVM<Fn, DoTypeOfFallback>(masm);
 }
 
 ICTypeMonitor_SingleObject::ICTypeMonitor_SingleObject(JitCode* stubCode,
                                                        JSObject* obj)
     : ICStub(TypeMonitor_SingleObject, stubCode), obj_(obj) {}
 
 ICTypeMonitor_ObjectGroup::ICTypeMonitor_ObjectGroup(JitCode* stubCode,
                                                      ObjectGroup* group)
@@ -5527,54 +5457,51 @@ ICCall_ClassHook::ICCall_ClassHook(JitCo
   native_ = Simulator::RedirectNativeFunction(native_, Args_General3);
 #endif
 }
 
 //
 // Rest_Fallback
 //
 
-static bool DoRestFallback(JSContext* cx, BaselineFrame* frame,
-                           ICRest_Fallback* stub, MutableHandleValue res) {
+bool DoRestFallback(JSContext* cx, BaselineFrame* frame, ICRest_Fallback* stub,
+                    MutableHandleValue res) {
   unsigned numFormals = frame->numFormalArgs() - 1;
   unsigned numActuals = frame->numActualArgs();
   unsigned numRest = numActuals > numFormals ? numActuals - numFormals : 0;
   Value* rest = frame->argv() + numFormals;
 
   ArrayObject* obj =
       ObjectGroup::newArrayObject(cx, rest, numRest, GenericObject,
                                   ObjectGroup::NewArrayKind::UnknownIndex);
   if (!obj) {
     return false;
   }
   res.setObject(*obj);
   return true;
 }
 
-typedef bool (*DoRestFallbackFn)(JSContext*, BaselineFrame*, ICRest_Fallback*,
-                                 MutableHandleValue);
-static const VMFunction DoRestFallbackInfo =
-    FunctionInfo<DoRestFallbackFn>(DoRestFallback, "DoRestFallback", TailCall);
-
 bool ICRest_Fallback::Compiler::generateStubCode(MacroAssembler& masm) {
   EmitRestoreTailCallReg(masm);
 
   masm.push(ICStubReg);
   pushStubPayload(masm, R0.scratchReg());
 
-  return tailCallVM(DoRestFallbackInfo, masm);
+  using Fn = bool (*)(JSContext*, BaselineFrame*, ICRest_Fallback*,
+                      MutableHandleValue);
+  return tailCallVM<Fn, DoRestFallback>(masm);
 }
 
 //
 // UnaryArith_Fallback
 //
 
-static bool DoUnaryArithFallback(JSContext* cx, BaselineFrame* frame,
-                                 ICUnaryArith_Fallback* stub, HandleValue val,
-                                 MutableHandleValue res) {
+bool DoUnaryArithFallback(JSContext* cx, BaselineFrame* frame,
+                          ICUnaryArith_Fallback* stub, HandleValue val,
+                          MutableHandleValue res) {
   stub->incrementEnteredCount();
 
   RootedScript script(cx, frame->script());
   jsbytecode* pc = stub->icEntry()->pc(script);
   JSOp op = JSOp(*pc);
   FallbackICSpew(cx, stub, "UnaryArith(%s)", CodeName[op]);
 
   // The unary operations take a copied val because the original value is needed
@@ -5614,47 +5541,42 @@ static bool DoUnaryArithFallback(JSConte
   }
 
   TryAttachStub<UnaryArithIRGenerator>("UniaryArith", cx, frame, stub,
                                        BaselineCacheIRStubKind::Regular, op,
                                        val, res);
   return true;
 }
 
-typedef bool (*DoUnaryArithFallbackFn)(JSContext*, BaselineFrame*,
-                                       ICUnaryArith_Fallback*, HandleValue,
-                                       MutableHandleValue);
-static const VMFunction DoUnaryArithFallbackInfo =
-    FunctionInfo<DoUnaryArithFallbackFn>(
-        DoUnaryArithFallback, "DoUnaryArithFallback", TailCall, PopValues(1));
-
 bool ICUnaryArith_Fallback::Compiler::generateStubCode(MacroAssembler& masm) {
   MOZ_ASSERT(R0 == JSReturnOperand);
 
   // Restore the tail call register.
   EmitRestoreTailCallReg(masm);
 
   // Ensure stack is fully synced for the expression decompiler.
   masm.pushValue(R0);
 
   // Push arguments.
   masm.pushValue(R0);
   masm.push(ICStubReg);
   pushStubPayload(masm, R0.scratchReg());
 
-  return tailCallVM(DoUnaryArithFallbackInfo, masm);
+  using Fn = bool (*)(JSContext*, BaselineFrame*, ICUnaryArith_Fallback*,
+                      HandleValue, MutableHandleValue);
+  return tailCallVM<Fn, DoUnaryArithFallback>(masm);
 }
 
 //
 // BinaryArith_Fallback
 //
 
-static bool DoBinaryArithFallback(JSContext* cx, BaselineFrame* frame,
-                                  ICBinaryArith_Fallback* stub, HandleValue lhs,
-                                  HandleValue rhs, MutableHandleValue ret) {
+bool DoBinaryArithFallback(JSContext* cx, BaselineFrame* frame,
+                           ICBinaryArith_Fallback* stub, HandleValue lhs,
+                           HandleValue rhs, MutableHandleValue ret) {
   stub->incrementEnteredCount();
 
   RootedScript script(cx, frame->script());
   jsbytecode* pc = stub->icEntry()->pc(script);
   JSOp op = JSOp(*pc);
   FallbackICSpew(
       cx, stub, "CacheIRBinaryArith(%s,%d,%d)", CodeName[op],
       int(lhs.isDouble() ? JSVAL_TYPE_DOUBLE : lhs.extractNonDoubleType()),
@@ -5743,48 +5665,43 @@ static bool DoBinaryArithFallback(JSCont
   }
 
   TryAttachStub<BinaryArithIRGenerator>("BinaryArith", cx, frame, stub,
                                         BaselineCacheIRStubKind::Regular, op,
                                         lhs, rhs, ret);
   return true;
 }
 
-typedef bool (*DoBinaryArithFallbackFn)(JSContext*, BaselineFrame*,
-                                        ICBinaryArith_Fallback*, HandleValue,
-                                        HandleValue, MutableHandleValue);
-static const VMFunction DoBinaryArithFallbackInfo =
-    FunctionInfo<DoBinaryArithFallbackFn>(
-        DoBinaryArithFallback, "DoBinaryArithFallback", TailCall, PopValues(2));
-
 bool ICBinaryArith_Fallback::Compiler::generateStubCode(MacroAssembler& masm) {
   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);
 
   // Push arguments.
   masm.pushValue(R1);
   masm.pushValue(R0);
   masm.push(ICStubReg);
   pushStubPayload(masm, R0.scratchReg());
 
-  return tailCallVM(DoBinaryArithFallbackInfo, masm);
+  using Fn = bool (*)(JSContext*, BaselineFrame*, ICBinaryArith_Fallback*,
+                      HandleValue, HandleValue, MutableHandleValue);
+  return tailCallVM<Fn, DoBinaryArithFallback>(masm);
 }
 
 //
 // Compare_Fallback
 //
-static bool DoCompareFallback(JSContext* cx, BaselineFrame* frame,
-                              ICCompare_Fallback* stub, HandleValue lhs,
-                              HandleValue rhs, MutableHandleValue ret) {
+bool DoCompareFallback(JSContext* cx, BaselineFrame* frame,
+                       ICCompare_Fallback* stub, HandleValue lhs,
+                       HandleValue rhs, MutableHandleValue ret) {
   stub->incrementEnteredCount();
 
   RootedScript script(cx, frame->script());
   jsbytecode* pc = stub->icEntry()->pc(script);
   JSOp op = JSOp(*pc);
 
   FallbackICSpew(cx, stub, "Compare(%s)", CodeName[op]);
 
@@ -5844,48 +5761,43 @@ static bool DoCompareFallback(JSContext*
   ret.setBoolean(out);
 
   TryAttachStub<CompareIRGenerator>("Compare", cx, frame, stub,
                                     BaselineCacheIRStubKind::Regular, op, lhs,
                                     rhs);
   return true;
 }
 
-typedef bool (*DoCompareFallbackFn)(JSContext*, BaselineFrame*,
-                                    ICCompare_Fallback*, HandleValue,
-                                    HandleValue, MutableHandleValue);
-static const VMFunction DoCompareFallbackInfo =
-    FunctionInfo<DoCompareFallbackFn>(DoCompareFallback, "DoCompareFallback",
-                                      TailCall, PopValues(2));
-
 bool ICCompare_Fallback::Compiler::generateStubCode(MacroAssembler& masm) {
   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);
 
   // Push arguments.
   masm.pushValue(R1);
   masm.pushValue(R0);
   masm.push(ICStubReg);
   pushStubPayload(masm, R0.scratchReg());
-  return tailCallVM(DoCompareFallbackInfo, masm);
+
+  using Fn = bool (*)(JSContext*, BaselineFrame*, ICCompare_Fallback*,
+                      HandleValue, HandleValue, MutableHandleValue);
+  return tailCallVM<Fn, DoCompareFallback>(masm);
 }
 
 //
 // NewArray_Fallback
 //
 
-static bool DoNewArray(JSContext* cx, BaselineFrame* frame,
-                       ICNewArray_Fallback* stub, uint32_t length,
-                       MutableHandleValue res) {
+bool DoNewArray(JSContext* cx, BaselineFrame* frame, ICNewArray_Fallback* stub,
+                uint32_t length, MutableHandleValue res) {
   stub->incrementEnteredCount();
   FallbackICSpew(cx, stub, "NewArray");
 
   RootedObject obj(cx);
   if (stub->templateObject()) {
     RootedObject templateObject(cx, stub->templateObject());
     obj = NewArrayOperationWithTemplate(cx, templateObject);
     if (!obj) {
@@ -5909,36 +5821,33 @@ static bool DoNewArray(JSContext* cx, Ba
       stub->setTemplateObject(templateObject);
     }
   }
 
   res.setObject(*obj);
   return true;
 }
 
-typedef bool (*DoNewArrayFn)(JSContext*, BaselineFrame*, ICNewArray_Fallback*,
-                             uint32_t, MutableHandleValue);
-static const VMFunction DoNewArrayInfo =
-    FunctionInfo<DoNewArrayFn>(DoNewArray, "DoNewArray", TailCall);
-
 bool ICNewArray_Fallback::Compiler::generateStubCode(MacroAssembler& masm) {
   EmitRestoreTailCallReg(masm);
 
   masm.push(R0.scratchReg());  // length
   masm.push(ICStubReg);        // stub.
   masm.pushBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
 
-  return tailCallVM(DoNewArrayInfo, masm);
+  using Fn = bool (*)(JSContext*, BaselineFrame*, ICNewArray_Fallback*,
+                      uint32_t, MutableHandleValue);
+  return tailCallVM<Fn, DoNewArray>(masm);
 }
 
 //
 // NewObject_Fallback
 //
-static bool DoNewObject(JSContext* cx, BaselineFrame* frame,
-                        ICNewObject_Fallback* stub, MutableHandleValue res) {
+bool DoNewObject(JSContext* cx, BaselineFrame* frame,
+                 ICNewObject_Fallback* stub, MutableHandleValue res) {
   stub->incrementEnteredCount();
   FallbackICSpew(cx, stub, "NewObject");
 
   RootedObject obj(cx);
 
   RootedObject templateObject(cx, stub->templateObject());
   if (templateObject) {
     MOZ_ASSERT(
@@ -5967,24 +5876,21 @@ static bool DoNewObject(JSContext* cx, B
   if (!obj) {
     return false;
   }
 
   res.setObject(*obj);
   return true;
 }
 
-typedef bool (*DoNewObjectFn)(JSContext*, BaselineFrame*, ICNewObject_Fallback*,
-                              MutableHandleValue);
-static const VMFunction DoNewObjectInfo =
-    FunctionInfo<DoNewObjectFn>(DoNewObject, "DoNewObject", TailCall);
-
 bool ICNewObject_Fallback::Compiler::generateStubCode(MacroAssembler& masm) {
   EmitRestoreTailCallReg(masm);
 
   masm.push(ICStubReg);  // stub.
   pushStubPayload(masm, R0.scratchReg());
 
-  return tailCallVM(DoNewObjectInfo, masm);
+  using Fn = bool (*)(JSContext*, BaselineFrame*, ICNewObject_Fallback*,
+                      MutableHandleValue);
+  return tailCallVM<Fn, DoNewObject>(masm);
 }
 
 }  // namespace jit
 }  // namespace js
--- a/js/src/jit/BaselineIC.h
+++ b/js/src/jit/BaselineIC.h
@@ -991,17 +991,21 @@ class ICStubCompiler {
   {
   }
 
   // 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 tailCallVM(const VMFunction& fun, MacroAssembler& masm);
+  MOZ_MUST_USE bool tailCallVMInternal(MacroAssembler& masm,
+                                       TailCallVMFunctionId id);
+
+  template <typename Fn, Fn fn>
+  MOZ_MUST_USE bool tailCallVM(MacroAssembler& masm);
 
   // Emits a normal (non-tail) call to a VMFunction wrapper.
   MOZ_MUST_USE bool callVMInternal(MacroAssembler& masm, VMFunctionId id);
 
   template <typename Fn, Fn fn>
   MOZ_MUST_USE bool callVM(MacroAssembler& masm);
 
   // A stub frame is used when a stub wants to call into the VM without
@@ -2733,12 +2737,101 @@ extern bool DoWarmUpCounterFallbackOSR(J
 extern bool DoCallFallback(JSContext* cx, BaselineFrame* frame,
                            ICCall_Fallback* stub, uint32_t argc, Value* vp,
                            MutableHandleValue res);
 
 extern bool DoSpreadCallFallback(JSContext* cx, BaselineFrame* frame,
                                  ICCall_Fallback* stub, Value* vp,
                                  MutableHandleValue res);
 
+extern bool DoTypeMonitorFallback(JSContext* cx, BaselineFrame* frame,
+                                  ICTypeMonitor_Fallback* stub,
+                                  HandleValue value, MutableHandleValue res);
+
+extern bool DoToBoolFallback(JSContext* cx, BaselineFrame* frame,
+                             ICToBool_Fallback* stub, HandleValue arg,
+                             MutableHandleValue ret);
+
+extern bool DoGetElemSuperFallback(JSContext* cx, BaselineFrame* frame,
+                                   ICGetElem_Fallback* stub, HandleValue lhs,
+                                   HandleValue rhs, HandleValue receiver,
+                                   MutableHandleValue res);
+
+extern bool DoGetElemFallback(JSContext* cx, BaselineFrame* frame,
+                              ICGetElem_Fallback* stub, HandleValue lhs,
+                              HandleValue rhs, MutableHandleValue res);
+
+extern bool DoSetElemFallback(JSContext* cx, BaselineFrame* frame,
+                              ICSetElem_Fallback* stub, Value* stack,
+                              HandleValue objv, HandleValue index,
+                              HandleValue rhs);
+
+extern bool DoInFallback(JSContext* cx, BaselineFrame* frame,
+                         ICIn_Fallback* stub, HandleValue key,
+                         HandleValue objValue, MutableHandleValue res);
+
+extern bool DoHasOwnFallback(JSContext* cx, BaselineFrame* frame,
+                             ICHasOwn_Fallback* stub, HandleValue keyValue,
+                             HandleValue objValue, MutableHandleValue res);
+
+extern bool DoGetNameFallback(JSContext* cx, BaselineFrame* frame,
+                              ICGetName_Fallback* stub, HandleObject envChain,
+                              MutableHandleValue res);
+
+extern bool DoBindNameFallback(JSContext* cx, BaselineFrame* frame,
+                               ICBindName_Fallback* stub, HandleObject envChain,
+                               MutableHandleValue res);
+
+extern bool DoGetIntrinsicFallback(JSContext* cx, BaselineFrame* frame,
+                                   ICGetIntrinsic_Fallback* stub,
+                                   MutableHandleValue res);
+
+extern bool DoGetPropFallback(JSContext* cx, BaselineFrame* frame,
+                              ICGetProp_Fallback* stub, MutableHandleValue val,
+                              MutableHandleValue res);
+
+extern bool DoGetPropSuperFallback(JSContext* cx, BaselineFrame* frame,
+                                   ICGetProp_Fallback* stub,
+                                   HandleValue receiver, MutableHandleValue val,
+                                   MutableHandleValue res);
+
+extern bool DoSetPropFallback(JSContext* cx, BaselineFrame* frame,
+                              ICSetProp_Fallback* stub, Value* stack,
+                              HandleValue lhs, HandleValue rhs);
+
+extern bool DoGetIteratorFallback(JSContext* cx, BaselineFrame* frame,
+                                  ICGetIterator_Fallback* stub,
+                                  HandleValue value, MutableHandleValue res);
+
+extern bool DoInstanceOfFallback(JSContext* cx, BaselineFrame* frame,
+                                 ICInstanceOf_Fallback* stub, HandleValue lhs,
+                                 HandleValue rhs, MutableHandleValue res);
+
+extern bool DoTypeOfFallback(JSContext* cx, BaselineFrame* frame,
+                             ICTypeOf_Fallback* stub, HandleValue val,
+                             MutableHandleValue res);
+
+extern bool DoRestFallback(JSContext* cx, BaselineFrame* frame,
+                           ICRest_Fallback* stub, MutableHandleValue res);
+
+extern bool DoUnaryArithFallback(JSContext* cx, BaselineFrame* frame,
+                                 ICUnaryArith_Fallback* stub, HandleValue val,
+                                 MutableHandleValue res);
+
+extern bool DoBinaryArithFallback(JSContext* cx, BaselineFrame* frame,
+                                  ICBinaryArith_Fallback* stub, HandleValue lhs,
+                                  HandleValue rhs, MutableHandleValue ret);
+
+extern bool DoNewArray(JSContext* cx, BaselineFrame* frame,
+                       ICNewArray_Fallback* stub, uint32_t length,
+                       MutableHandleValue res);
+
+extern bool DoNewObject(JSContext* cx, BaselineFrame* frame,
+                        ICNewObject_Fallback* stub, MutableHandleValue res);
+
+extern bool DoCompareFallback(JSContext* cx, BaselineFrame* frame,
+                              ICCompare_Fallback* stub, HandleValue lhs,
+                              HandleValue rhs, MutableHandleValue ret);
+
 }  // namespace jit
 }  // namespace js
 
 #endif /* jit_BaselineIC_h */
--- a/js/src/jit/JitRealm.h
+++ b/js/src/jit/JitRealm.h
@@ -24,16 +24,18 @@
 #include "js/Value.h"
 #include "vm/Stack.h"
 
 namespace js {
 namespace jit {
 
 class FrameSizeClass;
 struct VMFunctionData;
+
+enum class TailCallVMFunctionId;
 enum class VMFunctionId;
 
 struct EnterJitData {
   explicit EnterJitData(JSContext* cx)
       : jitcode(nullptr),
         osrFrame(nullptr),
         calleeToken(nullptr),
         maxArgv(nullptr),
@@ -144,16 +146,20 @@ class JitRuntime {
   // trampolineCode_.
   using VMWrapperMap = HashMap<const VMFunction*, uint32_t, VMFunction>;
   WriteOnceData<VMWrapperMap*> functionWrappers_;
 
   // 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_;
+
   // 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
@@ -195,16 +201,20 @@ class JitRuntime {
   void generateFreeStub(MacroAssembler& masm);
   JitCode* generateDebugTrapHandler(JSContext* cx);
   JitCode* generateBaselineDebugModeOSRHandler(
       JSContext* cx, uint32_t* noFrameRegPopOffsetOut);
 
   bool generateVMWrapper(JSContext* cx, MacroAssembler& masm,
                          const VMFunctionData& f, void* nativeFun,
                          uint32_t* wrapperOffset);
+
+  template <typename IdT>
+  bool generateVMWrappers(JSContext* cx, MacroAssembler& masm,
+                          VMWrapperOffsets& offsets);
   bool generateVMWrappers(JSContext* cx, MacroAssembler& masm);
 
   bool generateTLEventVM(MacroAssembler& masm, const VMFunctionData& f,
                          bool enter);
 
   inline bool generateTLEnterVM(MacroAssembler& masm, const VMFunctionData& f) {
     return generateTLEventVM(masm, f, /* enter = */ true);
   }
@@ -233,20 +243,24 @@ class JitRuntime {
   ExecutableAllocator& execAlloc() { return execAlloc_.ref(); }
 
   IonCompilationId nextCompilationId() {
     return IonCompilationId(nextCompilationId_++);
   }
 
   TrampolinePtr getVMWrapper(const VMFunction& f) const;
 
-  TrampolinePtr getVMWrapper(const VMFunctionId funId) const {
+  TrampolinePtr getVMWrapper(VMFunctionId funId) const {
     MOZ_ASSERT(trampolineCode_);
     return trampolineCode(functionWrapperOffsets_[size_t(funId)]);
   }
+  TrampolinePtr getVMWrapper(TailCallVMFunctionId funId) const {
+    MOZ_ASSERT(trampolineCode_);
+    return trampolineCode(tailCallFunctionWrapperOffsets_[size_t(funId)]);
+  }
 
   JitCode* debugTrapHandler(JSContext* cx);
   JitCode* getBaselineDebugModeOSRHandler(JSContext* cx);
   void* getBaselineDebugModeOSRHandlerAddress(JSContext* cx, bool popFrameReg);
 
   TrampolinePtr getGenericBailoutHandler() const {
     return trampolineCode(bailoutHandlerOffset_);
   }
--- a/js/src/jit/VMFunctionList-inl.h
+++ b/js/src/jit/VMFunctionList-inl.h
@@ -242,28 +242,61 @@ namespace jit {
   _(ToIdOperation, js::ToIdOperation)                                          \
   _(ToObjectSlow, js::ToObjectSlow)                                            \
   _(ToStringSlow, js::ToStringSlow<CanGC>)                                     \
   _(TrySkipAwait, js::jit::TrySkipAwait)                                       \
   _(UnboxedPlainObjectConvertToNative,                                         \
     js::UnboxedPlainObject::convertToNative)                                   \
   _(UrshValues, js::UrshValues)
 
+// The list below is for tail calls. The third argument specifies the number of
+// non-argument Values the VM wrapper should pop from the stack. This is used
+// for Baseline ICs.
+#define TAIL_CALL_VMFUNCTION_LIST(_)                            \
+  _(DoBinaryArithFallback, js::jit::DoBinaryArithFallback, 2)   \
+  _(DoBindNameFallback, js::jit::DoBindNameFallback, 0)         \
+  _(DoCompareFallback, js::jit::DoCompareFallback, 2)           \
+  _(DoConcatStringObject, js::jit::DoConcatStringObject, 2)     \
+  _(DoGetElemFallback, js::jit::DoGetElemFallback, 2)           \
+  _(DoGetElemSuperFallback, js::jit::DoGetElemSuperFallback, 3) \
+  _(DoGetIntrinsicFallback, js::jit::DoGetIntrinsicFallback, 0) \
+  _(DoGetIteratorFallback, js::jit::DoGetIteratorFallback, 1)   \
+  _(DoGetNameFallback, js::jit::DoGetNameFallback, 0)           \
+  _(DoGetPropFallback, js::jit::DoGetPropFallback, 1)           \
+  _(DoGetPropSuperFallback, js::jit::DoGetPropSuperFallback, 0) \
+  _(DoHasOwnFallback, js::jit::DoHasOwnFallback, 2)             \
+  _(DoInFallback, js::jit::DoInFallback, 2)                     \
+  _(DoInstanceOfFallback, js::jit::DoInstanceOfFallback, 2)     \
+  _(DoNewArray, js::jit::DoNewArray, 0)                         \
+  _(DoNewObject, js::jit::DoNewObject, 0)                       \
+  _(DoRestFallback, js::jit::DoRestFallback, 0)                 \
+  _(DoSetElemFallback, js::jit::DoSetElemFallback, 2)           \
+  _(DoSetPropFallback, js::jit::DoSetPropFallback, 1)           \
+  _(DoToBoolFallback, js::jit::DoToBoolFallback, 0)             \
+  _(DoTypeMonitorFallback, js::jit::DoTypeMonitorFallback, 0)   \
+  _(DoTypeOfFallback, js::jit::DoTypeOfFallback, 0)             \
+  _(DoUnaryArithFallback, js::jit::DoUnaryArithFallback, 1)     \
+  _(GeneratorThrowOrReturn, js::jit::GeneratorThrowOrReturn, 0)
+
+#define DEF_ID(name, ...) name,
 enum class VMFunctionId {
-#define DEF_ID(name, fp) name,
   VMFUNCTION_LIST(DEF_ID)
-#undef DEF_ID
       Count
 };
+enum class TailCallVMFunctionId { TAIL_CALL_VMFUNCTION_LIST(DEF_ID) Count };
+#undef DEF_ID
 
 // Define the VMFunctionToId template to map from signature + function to
 // the VMFunctionId. This lets us verify the consumer/codegen code matches
 // the C++ signature.
 template <typename Function, Function fun>
-struct VMFunctionToId;  // Error on this line? Forgot to update VMFUNCTION_LIST?
+struct VMFunctionToId;  // Error here? Update VMFUNCTION_LIST?
+
+template <typename Function, Function fun>
+struct TailCallVMFunctionToId;  // Error here? Update TAIL_CALL_VMFUNCTION_LIST?
 
 // GCC warns when the signature does not have matching attributes (for example
 // MOZ_MUST_USE). Squelch this warning to avoid a GCC-only footgun.
 #if MOZ_IS_GCC
 #  pragma GCC diagnostic push
 #  pragma GCC diagnostic ignored "-Wignored-attributes"
 #endif
 
@@ -272,16 +305,24 @@ struct VMFunctionToId;  // Error on this
 #define DEF_TEMPLATE(name, fp)                             \
   template <>                                              \
   struct VMFunctionToId<decltype(&(::fp)), ::fp> {         \
     static constexpr VMFunctionId id = VMFunctionId::name; \
   };
 VMFUNCTION_LIST(DEF_TEMPLATE)
 #undef DEF_TEMPLATE
 
+#define DEF_TEMPLATE(name, fp, valuesToPop)                                \
+  template <>                                                              \
+  struct TailCallVMFunctionToId<decltype(&(::fp)), ::fp> {                 \
+    static constexpr TailCallVMFunctionId id = TailCallVMFunctionId::name; \
+  };
+TAIL_CALL_VMFUNCTION_LIST(DEF_TEMPLATE)
+#undef DEF_TEMPLATE
+
 #if MOZ_IS_GCC
 #  pragma GCC diagnostic pop
 #endif
 
 }  // namespace jit
 }  // namespace js
 
 #endif // jit_VMFunctionList_inl_h
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -68,19 +68,19 @@ struct VMFunctionDataHelper<R (*)(JSCont
     return BitMask<TypeToRootType, uint64_t, 3, Args...>::result;
   }
   constexpr explicit VMFunctionDataHelper(
       const char* name, PopValues extraValuesToPop = PopValues(0))
       : VMFunctionData(name, explicitArgs(), argumentProperties(),
                        argumentPassedInFloatRegs(), argumentRootTypes(),
                        outParam(), outParamRootType(), returnType(),
                        extraValuesToPop.numValues, NonTailCall) {}
-  constexpr explicit VMFunctionDataHelper(
-      const char* name, MaybeTailCall expectTailCall,
-      PopValues extraValuesToPop = PopValues(0))
+  constexpr explicit VMFunctionDataHelper(const char* name,
+                                          MaybeTailCall expectTailCall,
+                                          PopValues extraValuesToPop)
       : VMFunctionData(name, explicitArgs(), argumentProperties(),
                        argumentPassedInFloatRegs(), argumentRootTypes(),
                        outParam(), outParamRootType(), returnType(),
                        extraValuesToPop.numValues, expectTailCall) {}
 };
 
 // GCC warns when the signature does not have matching attributes (for example
 // MOZ_MUST_USE). Squelch this warning to avoid a GCC-only footgun.
@@ -90,70 +90,103 @@ struct VMFunctionDataHelper<R (*)(JSCont
 #endif
 
 // Generate VMFunctionData array.
 static constexpr VMFunctionData vmFunctions[] = {
 #define DEF_VMFUNCTION(name, fp) VMFunctionDataHelper<decltype(&(::fp))>(#name),
     VMFUNCTION_LIST(DEF_VMFUNCTION)
 #undef DEF_VMFUNCTION
 };
+static constexpr VMFunctionData tailCallVMFunctions[] = {
+#define DEF_VMFUNCTION(name, fp, valuesToPop)              \
+  VMFunctionDataHelper<decltype(&(::fp))>(#name, TailCall, \
+                                          PopValues(valuesToPop)),
+    TAIL_CALL_VMFUNCTION_LIST(DEF_VMFUNCTION)
+#undef DEF_VMFUNCTION
+};
 
 #if MOZ_IS_GCC
 #  pragma GCC diagnostic pop
 #endif
 
-// Generate array storing C++ function pointers. These pointers are not stored
+// Generate arrays storing C++ function pointers. These pointers are not stored
 // in VMFunctionData because there's no good way to cast them to void* in
 // constexpr code. Compilers are smart enough to treat the const array below as
 // constexpr.
-static void* const vmFunctionTargets[] = {
-#define DEF_VMFUNCTION(name, fp) (void*)(::fp),
-    VMFUNCTION_LIST(DEF_VMFUNCTION)
+#define DEF_VMFUNCTION(name, fp, ...) (void*)(::fp),
+static void* const vmFunctionTargets[] = {VMFUNCTION_LIST(DEF_VMFUNCTION)};
+static void* const tailCallVMFunctionTargets[] = {
+    TAIL_CALL_VMFUNCTION_LIST(DEF_VMFUNCTION)};
 #undef DEF_VMFUNCTION
-};
 
 const VMFunctionData& GetVMFunction(VMFunctionId id) {
   return vmFunctions[size_t(id)];
 }
+const VMFunctionData& GetVMFunction(TailCallVMFunctionId id) {
+  return tailCallVMFunctions[size_t(id)];
+}
 
-bool JitRuntime::generateVMWrappers(JSContext* cx, MacroAssembler& masm) {
+static void* GetVMFunctionTarget(VMFunctionId id) {
+  return vmFunctionTargets[size_t(id)];
+}
+
+static void* GetVMFunctionTarget(TailCallVMFunctionId id) {
+  return tailCallVMFunctionTargets[size_t(id)];
+}
+
+template <typename IdT>
+bool JitRuntime::generateVMWrappers(JSContext* cx, MacroAssembler& masm,
+                                    VMWrapperOffsets& offsets) {
   // Generate all VM function wrappers.
 
-  static constexpr size_t NumVMFunctions = size_t(VMFunctionId::Count);
+  static constexpr size_t NumVMFunctions = size_t(IdT::Count);
 
-  if (!functionWrapperOffsets_.reserve(NumVMFunctions)) {
+  if (!offsets.reserve(NumVMFunctions)) {
     return false;
   }
 
 #ifdef DEBUG
   const char* lastName = nullptr;
 #endif
 
   for (size_t i = 0; i < NumVMFunctions; i++) {
-    VMFunctionId id = VMFunctionId(i);
+    IdT id = IdT(i);
     const VMFunctionData& fun = GetVMFunction(id);
 
 #ifdef DEBUG
     // Assert the list is sorted by name.
     if (lastName) {
       MOZ_ASSERT(strcmp(lastName, fun.name()) < 0,
                  "VM function list must be sorted by name");
     }
     lastName = fun.name();
 #endif
 
     JitSpew(JitSpew_Codegen, "# VM function wrapper (%s)", fun.name());
 
     uint32_t offset;
-    if (!generateVMWrapper(cx, masm, fun, vmFunctionTargets[i], &offset)) {
+    if (!generateVMWrapper(cx, masm, fun, GetVMFunctionTarget(id), &offset)) {
       return false;
     }
 
-    MOZ_ASSERT(functionWrapperOffsets_.length() == size_t(id));
-    functionWrapperOffsets_.infallibleAppend(offset);
+    MOZ_ASSERT(offsets.length() == size_t(id));
+    offsets.infallibleAppend(offset);
+  }
+
+  return true;
+};
+
+bool JitRuntime::generateVMWrappers(JSContext* cx, MacroAssembler& masm) {
+  if (!generateVMWrappers<VMFunctionId>(cx, masm, functionWrapperOffsets_)) {
+    return false;
+  }
+
+  if (!generateVMWrappers<TailCallVMFunctionId>(
+          cx, masm, tailCallFunctionWrapperOffsets_)) {
+    return false;
   }
 
   return true;
 }
 
 // Statics are initialized to null.
 /* static */
 VMFunction* VMFunction::functions;
@@ -1877,22 +1910,16 @@ bool DoConcatStringObject(JSContext* cx,
 
   // Technically, we need to call TypeScript::MonitorString for this PC, however
   // it was called when this stub was attached so it's OK.
 
   res.setString(str);
   return true;
 }
 
-typedef bool (*DoConcatStringObjectFn)(JSContext*, HandleValue, HandleValue,
-                                       MutableHandleValue);
-const VMFunction DoConcatStringObjectInfo =
-    FunctionInfo<DoConcatStringObjectFn>(
-        DoConcatStringObject, "DoConcatStringObject", TailCall, PopValues(2));
-
 MOZ_MUST_USE bool TrySkipAwait(JSContext* cx, HandleValue val,
                                MutableHandleValue resolved) {
   bool canSkip;
   if (!TrySkipAwait(cx, val, &canSkip, resolved)) {
     return false;
   }
 
   if (!canSkip) {
--- a/js/src/jit/VMFunctions.h
+++ b/js/src/jit/VMFunctions.h
@@ -1220,19 +1220,18 @@ MOZ_MUST_USE bool TrySkipAwait(JSContext
 bool IsPossiblyWrappedTypedArray(JSContext* cx, JSObject* obj, bool* result);
 
 bool DoToNumber(JSContext* cx, HandleValue arg, MutableHandleValue ret);
 bool DoToNumeric(JSContext* cx, HandleValue arg, MutableHandleValue ret);
 
 bool CopyStringSplitArray(JSContext* cx, HandleArrayObject arr,
                           MutableHandleValue result);
 
-// TailCall VMFunctions
-extern const VMFunction DoConcatStringObjectInfo;
-
+enum class TailCallVMFunctionId;
 enum class VMFunctionId;
 
 extern const VMFunctionData& GetVMFunction(VMFunctionId id);
+extern const VMFunctionData& GetVMFunction(TailCallVMFunctionId id);
 
 }  // namespace jit
 }  // namespace js
 
 #endif /* jit_VMFunctions_h */