Bug 1342553 - Part 0.2: Support JSOP_CHECKISCALLABLE in JIT. r=shu, a=lizzard
authorTooru Fujisawa <arai_a@mac.com>
Sun, 26 Feb 2017 14:02:37 +0900
changeset 379380 e82d8ab7e1614f1126ce2497314fb89d70c7b821
parent 379379 7dfe8ded92454117333087599534f1926614f081
child 379381 4c6f403862d369eb0f3f9eff423c6458f36ec4bf
push id1419
push userjlund@mozilla.com
push dateMon, 10 Apr 2017 20:44:07 +0000
treeherdermozilla-release@5e6801b73ef6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersshu, lizzard
bugs1342553
milestone53.0
Bug 1342553 - Part 0.2: Support JSOP_CHECKISCALLABLE in JIT. r=shu, a=lizzard
js/src/jit/BaselineCompiler.cpp
js/src/jit/BaselineCompiler.h
js/src/jit/CodeGenerator.cpp
js/src/jit/CodeGenerator.h
js/src/jit/IonBuilder.cpp
js/src/jit/IonBuilder.h
js/src/jit/Lowering.cpp
js/src/jit/Lowering.h
js/src/jit/MIR.h
js/src/jit/MOpcodes.h
js/src/jit/VMFunctions.cpp
js/src/jit/VMFunctions.h
js/src/jit/shared/LIR-shared.h
js/src/jit/shared/LOpcodes-shared.h
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -1376,16 +1376,36 @@ BaselineCompiler::emit_JSOP_CHECKISOBJ()
     pushArg(Imm32(GET_UINT8(pc)));
     if (!callVM(ThrowCheckIsObjectInfo))
         return false;
 
     masm.bind(&ok);
     return true;
 }
 
+typedef bool (*CheckIsCallableFn)(JSContext*, HandleValue, CheckIsCallableKind);
+static const VMFunction CheckIsCallableInfo =
+    FunctionInfo<CheckIsCallableFn>(CheckIsCallable, "CheckIsCallable");
+
+bool
+BaselineCompiler::emit_JSOP_CHECKISCALLABLE()
+{
+    frame.syncStack(0);
+    masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R0);
+
+    prepareVMCall();
+
+    pushArg(Imm32(GET_UINT8(pc)));
+    pushArg(R0);
+    if (!callVM(CheckIsCallableInfo))
+        return false;
+
+    return true;
+}
+
 typedef bool (*ThrowUninitializedThisFn)(JSContext*, BaselineFrame* frame);
 static const VMFunction ThrowUninitializedThisInfo =
     FunctionInfo<ThrowUninitializedThisFn>(BaselineThrowUninitializedThis,
                                            "BaselineThrowUninitializedThis");
 
 bool
 BaselineCompiler::emit_JSOP_CHECKTHIS()
 {
--- a/js/src/jit/BaselineCompiler.h
+++ b/js/src/jit/BaselineCompiler.h
@@ -213,16 +213,17 @@ namespace jit {
     _(JSOP_CALLEE)             \
     _(JSOP_GETRVAL)            \
     _(JSOP_SETRVAL)            \
     _(JSOP_RETRVAL)            \
     _(JSOP_RETURN)             \
     _(JSOP_FUNCTIONTHIS)       \
     _(JSOP_GLOBALTHIS)         \
     _(JSOP_CHECKISOBJ)         \
+    _(JSOP_CHECKISCALLABLE)    \
     _(JSOP_CHECKTHIS)          \
     _(JSOP_CHECKRETURN)        \
     _(JSOP_NEWTARGET)          \
     _(JSOP_SUPERCALL)          \
     _(JSOP_SPREADSUPERCALL)    \
     _(JSOP_THROWSETCONST)      \
     _(JSOP_THROWSETALIASEDCONST) \
     _(JSOP_THROWSETCALLEE) \
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -11512,49 +11512,75 @@ class OutOfLineIsCallable : public OutOf
     void accept(CodeGenerator* codegen) {
         codegen->visitOutOfLineIsCallable(this);
     }
     LIsCallable* ins() const {
         return ins_;
     }
 };
 
-void
-CodeGenerator::visitIsCallable(LIsCallable* ins)
-{
-    Register object = ToRegister(ins->object());
-    Register output = ToRegister(ins->output());
-
-    OutOfLineIsCallable* ool = new(alloc()) OutOfLineIsCallable(ins);
-    addOutOfLineCode(ool, ins->mir());
-
+template <CodeGenerator::CallableOrConstructor mode>
+void
+CodeGenerator::emitIsCallableOrConstructor(Register object, Register output, Label* failure)
+{
     Label notFunction, hasCOps, done;
     masm.loadObjClass(object, output);
 
-    // Just skim proxies off. Their notion of isCallable() is more complicated.
-    masm.branchTestClassIsProxy(true, output, ool->entry());
+    // Just skim proxies off. Their notion of isCallable()/isConstructor() is
+    // more complicated.
+    masm.branchTestClassIsProxy(true, output, failure);
 
     // An object is callable iff:
     //   is<JSFunction>() || (getClass()->cOps && getClass()->cOps->call).
+    // An object is constructor iff:
+    //  ((is<JSFunction>() && as<JSFunction>().isConstructor) ||
+    //   (getClass()->cOps && getClass()->cOps->construct)).
     masm.branchPtr(Assembler::NotEqual, output, ImmPtr(&JSFunction::class_), &notFunction);
-    masm.move32(Imm32(1), output);
+    if (mode == Callable) {
+        masm.move32(Imm32(1), output);
+    } else {
+        Label notConstructor;
+        masm.load16ZeroExtend(Address(object, JSFunction::offsetOfFlags()), output);
+        masm.and32(Imm32(JSFunction::CONSTRUCTOR), output);
+        masm.branchTest32(Assembler::Zero, output, output, &notConstructor);
+        masm.move32(Imm32(1), output);
+        masm.jump(&done);
+        masm.bind(&notConstructor);
+        masm.move32(Imm32(0), output);
+    }
     masm.jump(&done);
 
     masm.bind(&notFunction);
     masm.branchPtr(Assembler::NonZero, Address(output, offsetof(js::Class, cOps)),
                    ImmPtr(nullptr), &hasCOps);
     masm.move32(Imm32(0), output);
     masm.jump(&done);
 
     masm.bind(&hasCOps);
     masm.loadPtr(Address(output, offsetof(js::Class, cOps)), output);
-    masm.cmpPtrSet(Assembler::NonZero, Address(output, offsetof(js::ClassOps, call)),
+    size_t opsOffset = mode == Callable
+                       ? offsetof(js::ClassOps, call)
+                       : offsetof(js::ClassOps, construct);
+    masm.cmpPtrSet(Assembler::NonZero, Address(output, opsOffset),
                    ImmPtr(nullptr), output);
 
     masm.bind(&done);
+}
+
+void
+CodeGenerator::visitIsCallable(LIsCallable* ins)
+{
+    Register object = ToRegister(ins->object());
+    Register output = ToRegister(ins->output());
+
+    OutOfLineIsCallable* ool = new(alloc()) OutOfLineIsCallable(ins);
+    addOutOfLineCode(ool, ins->mir());
+
+    emitIsCallableOrConstructor<Callable>(object, output, ool->entry());
+
     masm.bind(ool->rejoin());
 }
 
 void
 CodeGenerator::visitOutOfLineIsCallable(OutOfLineIsCallable* ool)
 {
     LIsCallable* ins = ool->ins();
     Register object = ToRegister(ins->object());
@@ -11564,16 +11590,46 @@ CodeGenerator::visitOutOfLineIsCallable(
     masm.setupUnalignedABICall(output);
     masm.passABIArg(object);
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, ObjectIsCallable));
     masm.storeCallBoolResult(output);
     restoreVolatile(output);
     masm.jump(ool->rejoin());
 }
 
+typedef bool (*CheckIsCallableFn)(JSContext*, HandleValue, CheckIsCallableKind);
+static const VMFunction CheckIsCallableInfo =
+    FunctionInfo<CheckIsCallableFn>(CheckIsCallable, "CheckIsCallable");
+
+void
+CodeGenerator::visitCheckIsCallable(LCheckIsCallable* ins)
+{
+    ValueOperand checkValue = ToValue(ins, LCheckIsCallable::CheckValue);
+    Register temp = ToRegister(ins->temp());
+
+    // OOL code is used in the following 2 cases:
+    //   * checkValue is not callable
+    //   * checkValue is proxy and it's unknown whether it's callable or not
+    // CheckIsCallable checks if passed value is callable, regardless of the
+    // cases above.  IsCallable operation is not observable and checking it
+    // again doesn't matter.
+    OutOfLineCode* ool = oolCallVM(CheckIsCallableInfo, ins,
+                                   ArgList(checkValue, Imm32(ins->mir()->checkKind())),
+                                   StoreNothing());
+
+    masm.branchTestObject(Assembler::NotEqual, checkValue, ool->entry());
+
+    Register object = masm.extractObject(checkValue, temp);
+    emitIsCallableOrConstructor<Callable>(object, temp, ool->entry());
+
+    masm.branchTest32(Assembler::Zero, temp, temp, ool->entry());
+
+    masm.bind(ool->rejoin());
+}
+
 class OutOfLineIsConstructor : public OutOfLineCodeBase<CodeGenerator>
 {
     LIsConstructor* ins_;
 
   public:
     explicit OutOfLineIsConstructor(LIsConstructor* ins)
       : ins_(ins)
     { }
@@ -11590,47 +11646,18 @@ void
 CodeGenerator::visitIsConstructor(LIsConstructor* ins)
 {
     Register object = ToRegister(ins->object());
     Register output = ToRegister(ins->output());
 
     OutOfLineIsConstructor* ool = new(alloc()) OutOfLineIsConstructor(ins);
     addOutOfLineCode(ool, ins->mir());
 
-    Label notFunction, notConstructor, hasCOps, done;
-    masm.loadObjClass(object, output);
-
-    // Just skim proxies off. Their notion of isConstructor() is more complicated.
-    masm.branchTestClassIsProxy(true, output, ool->entry());
-
-    // An object is constructor iff
-    //  ((is<JSFunction>() && as<JSFunction>().isConstructor) ||
-    //   (getClass()->cOps && getClass()->cOps->construct)).
-    masm.branchPtr(Assembler::NotEqual, output, ImmPtr(&JSFunction::class_), &notFunction);
-    masm.load16ZeroExtend(Address(object, JSFunction::offsetOfFlags()), output);
-    masm.and32(Imm32(JSFunction::CONSTRUCTOR), output);
-    masm.branchTest32(Assembler::Zero, output, output, &notConstructor);
-    masm.move32(Imm32(1), output);
-    masm.jump(&done);
-    masm.bind(&notConstructor);
-    masm.move32(Imm32(0), output);
-    masm.jump(&done);
-
-    masm.bind(&notFunction);
-    masm.branchPtr(Assembler::NonZero, Address(output, offsetof(js::Class, cOps)),
-                   ImmPtr(nullptr), &hasCOps);
-    masm.move32(Imm32(0), output);
-    masm.jump(&done);
-
-    masm.bind(&hasCOps);
-    masm.loadPtr(Address(output, offsetof(js::Class, cOps)), output);
-    masm.cmpPtrSet(Assembler::NonZero, Address(output, offsetof(js::ClassOps, construct)),
-                   ImmPtr(nullptr), output);
-
-    masm.bind(&done);
+    emitIsCallableOrConstructor<Constructor>(object, output, ool->entry());
+
     masm.bind(ool->rejoin());
 }
 
 void
 CodeGenerator::visitOutOfLineIsConstructor(OutOfLineIsConstructor* ool)
 {
     LIsConstructor* ins = ool->ins();
     Register object = ToRegister(ins->object());
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -361,16 +361,22 @@ class CodeGenerator final : public CodeG
     void visitCallInstanceOf(LCallInstanceOf* ins);
     void visitGetDOMProperty(LGetDOMProperty* lir);
     void visitGetDOMMemberV(LGetDOMMemberV* lir);
     void visitGetDOMMemberT(LGetDOMMemberT* lir);
     void visitSetDOMProperty(LSetDOMProperty* lir);
     void visitCallDOMNative(LCallDOMNative* lir);
     void visitCallGetIntrinsicValue(LCallGetIntrinsicValue* lir);
     void visitCallBindVar(LCallBindVar* lir);
+    enum CallableOrConstructor {
+        Callable,
+        Constructor
+    };
+    template <CallableOrConstructor mode>
+    void emitIsCallableOrConstructor(Register object, Register output, Label* failure);
     void visitIsCallable(LIsCallable* lir);
     void visitOutOfLineIsCallable(OutOfLineIsCallable* ool);
     void visitIsConstructor(LIsConstructor* lir);
     void visitOutOfLineIsConstructor(OutOfLineIsConstructor* ool);
     void visitIsObject(LIsObject* lir);
     void visitIsObjectAndBranch(LIsObjectAndBranch* lir);
     void visitHasClass(LHasClass* lir);
     void visitWasmParameter(LWasmParameter* lir);
@@ -381,16 +387,17 @@ class CodeGenerator final : public CodeG
     void visitLexicalCheck(LLexicalCheck* ins);
     void visitThrowRuntimeLexicalError(LThrowRuntimeLexicalError* ins);
     void visitGlobalNameConflictsCheck(LGlobalNameConflictsCheck* ins);
     void visitDebugger(LDebugger* ins);
     void visitNewTarget(LNewTarget* ins);
     void visitArrowNewTarget(LArrowNewTarget* ins);
     void visitCheckReturn(LCheckReturn* ins);
     void visitCheckIsObj(LCheckIsObj* ins);
+    void visitCheckIsCallable(LCheckIsCallable* ins);
     void visitCheckObjCoercible(LCheckObjCoercible* ins);
     void visitDebugCheckSelfHosted(LDebugCheckSelfHosted* ins);
     void visitNaNToZero(LNaNToZero* ins);
     void visitOutOfLineNaNToZero(OutOfLineNaNToZero* ool);
 
     void visitCheckOverRecursed(LCheckOverRecursed* lir);
     void visitCheckOverRecursedFailure(CheckOverRecursedFailure* ool);
 
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -2246,16 +2246,19 @@ IonBuilder::inspectOpcode(JSOp op)
         break;
 
       case JSOP_NEWTARGET:
         return jsop_newtarget();
 
       case JSOP_CHECKISOBJ:
         return jsop_checkisobj(GET_UINT8(pc));
 
+      case JSOP_CHECKISCALLABLE:
+        return jsop_checkiscallable(GET_UINT8(pc));
+
       case JSOP_CHECKOBJCOERCIBLE:
         return jsop_checkobjcoercible();
 
       case JSOP_DEBUGCHECKSELFHOSTED:
       {
 #ifdef DEBUG
         MDebugCheckSelfHosted* check = MDebugCheckSelfHosted::New(alloc(), current->pop());
         current->add(check);
@@ -9407,16 +9410,25 @@ IonBuilder::jsop_checkisobj(uint8_t kind
 
     MCheckIsObj* check = MCheckIsObj::New(alloc(), current->pop(), kind);
     current->add(check);
     current->push(check);
     return Ok();
 }
 
 AbortReasonOr<Ok>
+IonBuilder::jsop_checkiscallable(uint8_t kind)
+{
+    MCheckIsCallable* check = MCheckIsCallable::New(alloc(), current->pop(), kind);
+    current->add(check);
+    current->push(check);
+    return Ok();
+}
+
+AbortReasonOr<Ok>
 IonBuilder::jsop_checkobjcoercible()
 {
     MDefinition* toCheck = current->peek(-1);
 
     if (!toCheck->mightBeType(MIRType::Undefined) &&
         !toCheck->mightBeType(MIRType::Null))
     {
         toCheck->setImplicitlyUsedUnchecked();
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -574,16 +574,17 @@ class IonBuilder
     AbortReasonOr<Ok> jsop_iterend();
     AbortReasonOr<Ok> jsop_in();
     AbortReasonOr<Ok> jsop_instanceof();
     AbortReasonOr<Ok> jsop_getaliasedvar(EnvironmentCoordinate ec);
     AbortReasonOr<Ok> jsop_setaliasedvar(EnvironmentCoordinate ec);
     AbortReasonOr<Ok> jsop_debugger();
     AbortReasonOr<Ok> jsop_newtarget();
     AbortReasonOr<Ok> jsop_checkisobj(uint8_t kind);
+    AbortReasonOr<Ok> jsop_checkiscallable(uint8_t kind);
     AbortReasonOr<Ok> jsop_checkobjcoercible();
     AbortReasonOr<Ok> jsop_pushcallobj();
 
     /* Inlining. */
 
     enum InliningStatus
     {
         InliningStatus_NotInlined,
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -4701,16 +4701,29 @@ LIRGenerator::visitCheckIsObj(MCheckIsOb
 
     LCheckIsObj* lir = new(alloc()) LCheckIsObj(useBoxAtStart(checkVal));
     redefine(ins, checkVal);
     add(lir, ins);
     assignSafepoint(lir, ins);
 }
 
 void
+LIRGenerator::visitCheckIsCallable(MCheckIsCallable* ins)
+{
+    MDefinition* checkVal = ins->checkValue();
+    MOZ_ASSERT(checkVal->type() == MIRType::Value);
+
+    LCheckIsCallable* lir = new(alloc()) LCheckIsCallable(useBox(checkVal),
+                                                          temp());
+    redefine(ins, checkVal);
+    add(lir, ins);
+    assignSafepoint(lir, ins);
+}
+
+void
 LIRGenerator::visitCheckObjCoercible(MCheckObjCoercible* ins)
 {
     MDefinition* checkVal = ins->checkValue();
     MOZ_ASSERT(checkVal->type() == MIRType::Value);
 
     LCheckObjCoercible* lir = new(alloc()) LCheckObjCoercible(useBoxAtStart(checkVal));
     redefine(ins, checkVal);
     add(lir, ins);
--- a/js/src/jit/Lowering.h
+++ b/js/src/jit/Lowering.h
@@ -324,16 +324,17 @@ class LIRGenerator : public LIRGenerator
     void visitDebugger(MDebugger* ins);
     void visitNewTarget(MNewTarget* ins);
     void visitArrowNewTarget(MArrowNewTarget* ins);
     void visitNaNToZero(MNaNToZero *ins);
     void visitAtomicIsLockFree(MAtomicIsLockFree* ins);
     void visitGuardSharedTypedArray(MGuardSharedTypedArray* ins);
     void visitCheckReturn(MCheckReturn* ins);
     void visitCheckIsObj(MCheckIsObj* ins);
+    void visitCheckIsCallable(MCheckIsCallable* ins);
     void visitCheckObjCoercible(MCheckObjCoercible* ins);
     void visitDebugCheckSelfHosted(MDebugCheckSelfHosted* ins);
 };
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_Lowering_h */
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -13432,18 +13432,19 @@ class MDebugger : public MNullaryInstruc
 };
 
 class MCheckIsObj
   : public MUnaryInstruction,
     public BoxInputsPolicy::Data
 {
     uint8_t checkKind_;
 
-    explicit MCheckIsObj(MDefinition* toCheck, uint8_t checkKind)
-      : MUnaryInstruction(toCheck), checkKind_(checkKind)
+    MCheckIsObj(MDefinition* toCheck, uint8_t checkKind)
+      : MUnaryInstruction(toCheck),
+        checkKind_(checkKind)
     {
         setResultType(MIRType::Value);
         setResultTypeSet(toCheck->resultTypeSet());
         setGuard();
     }
 
   public:
     INSTRUCTION_HEADER(CheckIsObj)
@@ -13452,16 +13453,43 @@ class MCheckIsObj
 
     uint8_t checkKind() const { return checkKind_; }
 
     AliasSet getAliasSet() const override {
         return AliasSet::None();
     }
 };
 
+class MCheckIsCallable
+  : public MUnaryInstruction,
+    public BoxInputsPolicy::Data
+{
+    uint8_t checkKind_;
+
+    MCheckIsCallable(MDefinition* toCheck, uint8_t checkKind)
+      : MUnaryInstruction(toCheck),
+        checkKind_(checkKind)
+    {
+        setResultType(MIRType::Value);
+        setResultTypeSet(toCheck->resultTypeSet());
+        setGuard();
+    }
+
+  public:
+    INSTRUCTION_HEADER(CheckIsCallable)
+    TRIVIAL_NEW_WRAPPERS
+    NAMED_OPERANDS((0, checkValue))
+
+    uint8_t checkKind() const { return checkKind_; }
+
+    AliasSet getAliasSet() const override {
+        return AliasSet::None();
+    }
+};
+
 class MCheckObjCoercible
   : public MUnaryInstruction,
     public BoxInputsPolicy::Data
 {
     explicit MCheckObjCoercible(MDefinition* toCheck)
       : MUnaryInstruction(toCheck)
     {
         setGuard();
--- a/js/src/jit/MOpcodes.h
+++ b/js/src/jit/MOpcodes.h
@@ -280,16 +280,17 @@ namespace jit {
     _(LexicalCheck)                                                         \
     _(ThrowRuntimeLexicalError)                                             \
     _(GlobalNameConflictsCheck)                                             \
     _(Debugger)                                                             \
     _(NewTarget)                                                            \
     _(ArrowNewTarget)                                                       \
     _(CheckReturn)                                                          \
     _(CheckIsObj)                                                           \
+    _(CheckIsCallable)                                                      \
     _(CheckObjCoercible)                                                    \
     _(DebugCheckSelfHosted)                                                 \
     _(AsmJSNeg)                                                             \
     _(AsmJSLoadHeap)                                                        \
     _(AsmJSStoreHeap)                                                       \
     _(AsmJSCompareExchangeHeap)                                             \
     _(AsmJSAtomicExchangeHeap)                                              \
     _(AsmJSAtomicBinopHeap)                                                 \
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -1393,10 +1393,19 @@ EqualStringsHelper(JSString* str1, JSStr
 
     JSLinearString* str2Linear = str2->ensureLinear(nullptr);
     if (!str2Linear)
         return false;
 
     return EqualChars(&str1->asLinear(), str2Linear);
 }
 
+bool
+CheckIsCallable(JSContext* cx, HandleValue v, CheckIsCallableKind kind)
+{
+    if (!IsCallable(v))
+        return ThrowCheckIsCallable(cx, kind);
+
+    return true;
+}
+
 } // namespace jit
 } // namespace js
--- a/js/src/jit/VMFunctions.h
+++ b/js/src/jit/VMFunctions.h
@@ -8,16 +8,17 @@
 #define jit_VMFunctions_h
 
 #include "mozilla/Attributes.h"
 
 #include "jspubtd.h"
 
 #include "jit/CompileInfo.h"
 #include "jit/JitFrames.h"
+#include "vm/Interpreter.h"
 
 namespace js {
 
 class NamedLambdaObject;
 class WithScope;
 class InlineTypedObject;
 class GeneratorObject;
 class TypedArrayObject;
@@ -814,12 +815,15 @@ BaselineGetFunctionThis(JSContext* cx, B
 MOZ_MUST_USE bool
 CallNativeGetter(JSContext* cx, HandleFunction callee, HandleObject obj,
                  MutableHandleValue result);
 
 
 MOZ_MUST_USE bool
 EqualStringsHelper(JSString* str1, JSString* str2);
 
+MOZ_MUST_USE bool
+CheckIsCallable(JSContext* cx, HandleValue v, CheckIsCallableKind kind);
+
 } // namespace jit
 } // namespace js
 
 #endif /* jit_VMFunctions_h */
--- a/js/src/jit/shared/LIR-shared.h
+++ b/js/src/jit/shared/LIR-shared.h
@@ -8912,16 +8912,37 @@ class LCheckIsObj : public LInstructionH
         setBoxOperand(CheckValue, value);
     }
 
     MCheckIsObj* mir() const {
         return mir_->toCheckIsObj();
     }
 };
 
+class LCheckIsCallable : public LInstructionHelper<BOX_PIECES, BOX_PIECES, 1>
+{
+  public:
+    LIR_HEADER(CheckIsCallable)
+
+    static const size_t CheckValue = 0;
+
+    LCheckIsCallable(const LBoxAllocation& value, const LDefinition& temp) {
+        setBoxOperand(CheckValue, value);
+        setTemp(0, temp);
+    }
+
+    const LDefinition* temp() {
+        return getTemp(0);
+    }
+
+    MCheckIsCallable* mir() const {
+        return mir_->toCheckIsCallable();
+    }
+};
+
 class LCheckObjCoercible : public LCallInstructionHelper<BOX_PIECES, BOX_PIECES, 0>
 {
   public:
     LIR_HEADER(CheckObjCoercible)
 
     static const size_t CheckValue = 0;
 
     explicit LCheckObjCoercible(const LBoxAllocation& value) {
--- a/js/src/jit/shared/LOpcodes-shared.h
+++ b/js/src/jit/shared/LOpcodes-shared.h
@@ -398,16 +398,17 @@
     _(LexicalCheck)                 \
     _(ThrowRuntimeLexicalError)     \
     _(GlobalNameConflictsCheck)     \
     _(Debugger)                     \
     _(NewTarget)                    \
     _(ArrowNewTarget)               \
     _(CheckReturn)                  \
     _(CheckIsObj)                   \
+    _(CheckIsCallable)              \
     _(CheckObjCoercible)            \
     _(DebugCheckSelfHosted)         \
     _(AsmJSLoadHeap)                \
     _(AsmJSStoreHeap)               \
     _(AsmJSCompareExchangeHeap)     \
     _(AsmJSAtomicExchangeHeap)      \
     _(AsmJSAtomicBinopHeap)         \
     _(AsmJSAtomicBinopHeapForEffect)\