Add type specialized paths for instanceof to Ion, bug 814861. r=pierron
authorBrian Hackett <bhackett1024@gmail.com>
Sun, 25 Nov 2012 11:04:21 -0500
changeset 114101 e390f459239ef1f2309ca414665739faf83ccacd
parent 114100 3c67034ba39c687b8da87d6ca26c86e3b0c3f3ac
child 114102 8849063cf05951e9e0b9e30357276419f1ab5b7b
push id23904
push useremorley@mozilla.com
push dateMon, 26 Nov 2012 11:00:15 +0000
treeherdermozilla-central@541ccce39563 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspierron
bugs814861
milestone20.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
Add type specialized paths for instanceof to Ion, bug 814861. r=pierron
js/src/ion/CodeGenerator.cpp
js/src/ion/CodeGenerator.h
js/src/ion/IonBuilder.cpp
js/src/ion/LIR-Common.h
js/src/ion/LOpcodes.h
js/src/ion/Lowering.cpp
js/src/ion/Lowering.h
js/src/ion/MIR.h
js/src/ion/MOpcodes.h
js/src/ion/TypePolicy.cpp
js/src/ion/TypePolicy.h
--- a/js/src/ion/CodeGenerator.cpp
+++ b/js/src/ion/CodeGenerator.cpp
@@ -4209,16 +4209,132 @@ CodeGenerator::visitInArray(LInArray *li
 
     masm.bind(&falseBranch);
     masm.move32(Imm32(0), output);
     masm.bind(&done);
     return true;
 }
 
 bool
+CodeGenerator::visitInstanceOfTypedO(LInstanceOfTypedO *ins)
+{
+    return emitInstanceOfTyped(ins, ins->mir()->prototypeObject());
+}
+
+bool
+CodeGenerator::visitInstanceOfTypedV(LInstanceOfTypedV *ins)
+{
+    return emitInstanceOfTyped(ins, ins->mir()->prototypeObject());
+}
+
+// Wrap IsDelegate, which takes a Value for the lhs of an instanceof.
+static bool
+IsDelegateObject(JSContext *cx, HandleObject protoObj, HandleObject obj, JSBool *res)
+{
+    bool nres;
+    if (!IsDelegate(cx, protoObj, ObjectValue(*obj), &nres))
+        return false;
+    *res = nres;
+    return true;
+}
+
+typedef bool (*IsDelegateObjectFn)(JSContext *, HandleObject, HandleObject, JSBool *);
+static const VMFunction IsDelegateObjectInfo = FunctionInfo<IsDelegateObjectFn>(IsDelegateObject);
+
+bool
+CodeGenerator::emitInstanceOfTyped(LInstruction *ins, RawObject prototypeObject)
+{
+    // This path implements fun_hasInstance when the function's prototype is
+    // known to be prototypeObject.
+
+    Label done;
+    Register output = ToRegister(ins->getDef(0));
+
+    // If the lhs is a primitive, the result is false.
+    Register objReg;
+    if (ins->isInstanceOfTypedV()) {
+        Label isObject;
+        ValueOperand lhsValue = ToValue(ins, LInstanceOfTypedV::LHS);
+        masm.branchTestObject(Assembler::Equal, lhsValue, &isObject);
+        masm.mov(Imm32(0), output);
+        masm.jump(&done);
+        masm.bind(&isObject);
+        objReg = masm.extractObject(lhsValue, output);
+    } else {
+        objReg = ToRegister(ins->toInstanceOfTypedO()->lhs());
+    }
+
+    // Crawl the lhs's prototype chain in a loop to search for prototypeObject.
+    // This follows the main loop of js::IsDelegate, though additionally breaks
+    // out of the loop on Proxy::LazyProto.
+
+    // Load the lhs's prototype.
+    masm.loadPtr(Address(objReg, JSObject::offsetOfType()), output);
+    masm.loadPtr(Address(output, offsetof(types::TypeObject, proto)), output);
+
+    Label testLazy;
+    {
+        Label loopPrototypeChain;
+        masm.bind(&loopPrototypeChain);
+
+        // Test for the target prototype object.
+        Label notPrototypeObject;
+        masm.branchPtr(Assembler::NotEqual, output, ImmGCPtr(prototypeObject), &notPrototypeObject);
+        masm.mov(Imm32(1), output);
+        masm.jump(&done);
+        masm.bind(&notPrototypeObject);
+
+        JS_ASSERT(uintptr_t(Proxy::LazyProto) == 1);
+
+        // Test for NULL or Proxy::LazyProto
+        masm.branchPtr(Assembler::BelowOrEqual, output, ImmWord(1), &testLazy);
+
+        // Load the current object's prototype.
+        masm.loadPtr(Address(output, JSObject::offsetOfType()), output);
+        masm.loadPtr(Address(output, offsetof(types::TypeObject, proto)), output);
+
+        masm.jump(&loopPrototypeChain);
+    }
+
+    // Make a VM call if an object with a lazy proto was found on the prototype
+    // chain. This currently occurs only for cross compartment wrappers, which
+    // we do not expect to be compared with non-wrapper functions from this
+    // compartment. Otherwise, we stopped on a NULL prototype and the output
+    // register is already correct.
+
+    OutOfLineCode *ool = oolCallVM(IsDelegateObjectInfo, ins,
+                                   (ArgList(), ImmGCPtr(prototypeObject), objReg),
+                                   StoreRegisterTo(output));
+
+    // Regenerate the original lhs object for the VM call.
+    Label regenerate, *lazyEntry;
+    if (objReg != output) {
+        lazyEntry = ool->entry();
+    } else {
+        masm.bind(&regenerate);
+        lazyEntry = &regenerate;
+        if (ins->isInstanceOfTypedV()) {
+            ValueOperand lhsValue = ToValue(ins, LInstanceOfTypedV::LHS);
+            objReg = masm.extractObject(lhsValue, output);
+        } else {
+            objReg = ToRegister(ins->toInstanceOfTypedO()->lhs());
+        }
+        JS_ASSERT(objReg == output);
+        masm.jump(ool->entry());
+    }
+
+    masm.bind(&testLazy);
+    masm.branchPtr(Assembler::Equal, output, ImmWord(1), lazyEntry);
+
+    masm.bind(&done);
+    masm.bind(ool->rejoin());
+    return true;
+}
+
+bool
 CodeGenerator::visitInstanceOfO(LInstanceOfO *ins)
 {
     Register rhs = ToRegister(ins->getOperand(1));
     return emitInstanceOf(ins, rhs);
 }
 
 bool
 CodeGenerator::visitInstanceOfV(LInstanceOfV *ins)
--- a/js/src/ion/CodeGenerator.h
+++ b/js/src/ion/CodeGenerator.h
@@ -163,19 +163,22 @@ class CodeGenerator : public CodeGenerat
     bool visitIteratorMore(LIteratorMore *lir);
     bool visitIteratorEnd(LIteratorEnd *lir);
     bool visitArgumentsLength(LArgumentsLength *lir);
     bool visitGetArgument(LGetArgument *lir);
     bool visitCallSetProperty(LCallSetProperty *ins);
     bool visitCallDeleteProperty(LCallDeleteProperty *lir);
     bool visitBitNotV(LBitNotV *lir);
     bool visitBitOpV(LBitOpV *lir);
+    bool emitInstanceOfTyped(LInstruction *ins, RawObject prototypeObject);
     bool emitInstanceOf(LInstruction *ins, Register rhs);
     bool visitIn(LIn *ins);
     bool visitInArray(LInArray *ins);
+    bool visitInstanceOfTypedO(LInstanceOfTypedO *ins);
+    bool visitInstanceOfTypedV(LInstanceOfTypedV *ins);
     bool visitInstanceOfO(LInstanceOfO *ins);
     bool visitInstanceOfV(LInstanceOfV *ins);
     bool visitFunctionBoundary(LFunctionBoundary *lir);
     bool visitGetDOMProperty(LGetDOMProperty *lir);
     bool visitSetDOMProperty(LSetDOMProperty *lir);
     bool visitCallDOMNative(LCallDOMNative *lir);
     bool visitCallGetIntrinsicValue(LCallGetIntrinsicValue *lir);
 
--- a/js/src/ion/IonBuilder.cpp
+++ b/js/src/ion/IonBuilder.cpp
@@ -6493,19 +6493,47 @@ IonBuilder::jsop_in_dense()
     current->push(ins);
 
     return true;
 }
 
 bool
 IonBuilder::jsop_instanceof()
 {
-    MDefinition *proto = current->pop();
+    MDefinition *rhs = current->pop();
     MDefinition *obj = current->pop();
-    MInstanceOf *ins = new MInstanceOf(obj, proto);
+
+    TypeOracle::BinaryTypes types = oracle->binaryTypes(script_, pc);
+
+    // If this is an 'x instanceof function' operation and we can determine the
+    // exact function and prototype object being tested for, use a typed path.
+    do {
+        RawObject rhsObject = types.rhsTypes ? types.rhsTypes->getSingleton() : NULL;
+        if (!rhsObject || !rhsObject->isFunction() || rhsObject->isBoundFunction())
+            break;
+
+        types::TypeObject *rhsType = rhsObject->getType(cx);
+        if (!rhsType || rhsType->unknownProperties())
+            break;
+
+        types::HeapTypeSet *protoTypes =
+            rhsType->getProperty(cx, NameToId(cx->names().classPrototype), false);
+        RawObject protoObject = protoTypes ? protoTypes->getSingleton(cx) : NULL;
+        if (!protoObject)
+            break;
+
+        MInstanceOfTyped *ins = new MInstanceOfTyped(obj, protoObject);
+
+        current->add(ins);
+        current->push(ins);
+
+        return resumeAfter(ins);
+    } while (false);
+
+    MInstanceOf *ins = new MInstanceOf(obj, rhs);
 
     current->add(ins);
     current->push(ins);
 
     return resumeAfter(ins);
 }
 
 MInstruction *
--- a/js/src/ion/LIR-Common.h
+++ b/js/src/ion/LIR-Common.h
@@ -3138,16 +3138,51 @@ class LIn : public LCallInstructionHelpe
     const LAllocation *rhs() {
         return getOperand(RHS);
     }
 
     static const size_t LHS = 0;
     static const size_t RHS = BOX_PIECES;
 };
 
+class LInstanceOfTypedO : public LInstructionHelper<1, 1, 0>
+{
+  public:
+    LIR_HEADER(InstanceOfTypedO);
+    LInstanceOfTypedO(const LAllocation &lhs) {
+        setOperand(0, lhs);
+    }
+
+    MInstanceOfTyped *mir() const {
+        return mir_->toInstanceOfTyped();
+    }
+
+    const LAllocation *lhs() {
+        return getOperand(0);
+    }
+};
+
+class LInstanceOfTypedV : public LInstructionHelper<1, BOX_PIECES, 0>
+{
+  public:
+    LIR_HEADER(InstanceOfTypedV);
+    LInstanceOfTypedV() {
+    }
+
+    MInstanceOfTyped *mir() const {
+        return mir_->toInstanceOfTyped();
+    }
+
+    const LAllocation *lhs() {
+        return getOperand(LHS);
+    }
+
+    static const size_t LHS = 0;
+};
+
 class LInstanceOfO : public LInstructionHelper<1, 2, 2>
 {
   public:
     LIR_HEADER(InstanceOfO);
     LInstanceOfO(const LAllocation &lhs, const LAllocation &rhs,
                  const LDefinition &temp, const LDefinition &temp2)
     {
         setOperand(0, lhs);
--- a/js/src/ion/LOpcodes.h
+++ b/js/src/ion/LOpcodes.h
@@ -164,16 +164,18 @@
     _(ArgumentsLength)              \
     _(GetArgument)                  \
     _(TypeOfV)                      \
     _(ToIdV)                        \
     _(Floor)                        \
     _(Round)                        \
     _(In)                           \
     _(InArray)                      \
+    _(InstanceOfTypedO)             \
+    _(InstanceOfTypedV)             \
     _(InstanceOfO)                  \
     _(InstanceOfV)                  \
     _(InterruptCheck)               \
     _(FunctionBoundary)             \
     _(GetDOMProperty)               \
     _(SetDOMProperty)               \
     _(CallDOMNative)
 
--- a/js/src/ion/Lowering.cpp
+++ b/js/src/ion/Lowering.cpp
@@ -1920,25 +1920,40 @@ LIRGenerator::visitIn(MIn *ins)
 
     LIn *lir = new LIn(useRegisterAtStart(rhs));
     if (!useBoxAtStart(lir, LIn::LHS, lhs))
         return false;
     return defineVMReturn(lir, ins) && assignSafepoint(lir, ins);
 }
 
 bool
+LIRGenerator::visitInstanceOfTyped(MInstanceOfTyped *ins)
+{
+    MDefinition *lhs = ins->getOperand(0);
+
+    JS_ASSERT(lhs->type() == MIRType_Value || lhs->type() == MIRType_Object);
+
+    if (lhs->type() == MIRType_Object) {
+        LInstanceOfTypedO *lir = new LInstanceOfTypedO(useRegister(lhs));
+        return define(lir, ins) && assignSafepoint(lir, ins);
+    }
+
+    LInstanceOfTypedV *lir = new LInstanceOfTypedV();
+    return useBox(lir, LInstanceOfTypedV::LHS, lhs) && define(lir, ins) && assignSafepoint(lir, ins);
+}
+
+bool
 LIRGenerator::visitInstanceOf(MInstanceOf *ins)
 {
     MDefinition *lhs = ins->lhs();
     MDefinition *rhs = ins->rhs();
 
     JS_ASSERT(lhs->type() == MIRType_Value || lhs->type() == MIRType_Object);
     JS_ASSERT(rhs->type() == MIRType_Object);
 
-    // InstanceOf with non-object will always return false
     if (lhs->type() == MIRType_Object) {
         LInstanceOfO *lir = new LInstanceOfO(useRegister(lhs), useRegister(rhs), temp(), temp());
         return define(lir, ins) && assignSafepoint(lir, ins);
     }
 
     LInstanceOfV *lir = new LInstanceOfV(useRegister(rhs), temp(), temp());
     return useBox(lir, LInstanceOfV::LHS, lhs) && define(lir, ins) && assignSafepoint(lir, ins);
 }
--- a/js/src/ion/Lowering.h
+++ b/js/src/ion/Lowering.h
@@ -179,16 +179,17 @@ class LIRGenerator : public LIRGenerator
     bool visitIteratorMore(MIteratorMore *ins);
     bool visitIteratorEnd(MIteratorEnd *ins);
     bool visitStringLength(MStringLength *ins);
     bool visitArgumentsLength(MArgumentsLength *ins);
     bool visitGetArgument(MGetArgument *ins);
     bool visitThrow(MThrow *ins);
     bool visitIn(MIn *ins);
     bool visitInArray(MInArray *ins);
+    bool visitInstanceOfTyped(MInstanceOfTyped *ins);
     bool visitInstanceOf(MInstanceOf *ins);
     bool visitFunctionBoundary(MFunctionBoundary *ins);
     bool visitSetDOMProperty(MSetDOMProperty *ins);
     bool visitGetDOMProperty(MGetDOMProperty *ins);
 };
 
 } // namespace ion
 } // namespace js
--- a/js/src/ion/MIR.h
+++ b/js/src/ion/MIR.h
@@ -5240,17 +5240,43 @@ class MInArray
     bool needsHoleCheck() const {
         return needsHoleCheck_;
     }
     AliasSet getAliasSet() const {
         return AliasSet::Load(AliasSet::Element);
     }
 };
 
-// Implementation for instanceof operator.
+// Implementation for instanceof operator with specific rhs.
+class MInstanceOfTyped
+  : public MUnaryInstruction,
+    public InstanceOfPolicy
+{
+    CompilerRootObject protoObj_;
+
+  public:
+    MInstanceOfTyped(MDefinition *obj, RawObject proto)
+      : MUnaryInstruction(obj)
+    {
+        protoObj_ = proto;
+        setResultType(MIRType_Boolean);
+    }
+
+    INSTRUCTION_HEADER(InstanceOfTyped);
+
+    TypePolicy *typePolicy() {
+        return this;
+    }
+
+    RawObject prototypeObject() {
+        return protoObj_;
+    }
+};
+
+// Implementation for instanceof operator with unknown rhs.
 class MInstanceOf
   : public MBinaryInstruction,
     public InstanceOfPolicy
 {
   public:
     MInstanceOf(MDefinition *obj, MDefinition *proto)
       : MBinaryInstruction(obj, proto)
     {
--- a/js/src/ion/MOpcodes.h
+++ b/js/src/ion/MOpcodes.h
@@ -128,16 +128,17 @@ namespace ion {
     _(IteratorMore)                                                         \
     _(IteratorEnd)                                                          \
     _(StringLength)                                                         \
     _(ArgumentsLength)                                                      \
     _(GetArgument)                                                          \
     _(Floor)                                                                \
     _(Round)                                                                \
     _(In)                                                                   \
+    _(InstanceOfTyped)                                                      \
     _(InstanceOf)                                                           \
     _(InterruptCheck)                                                       \
     _(FunctionBoundary)                                                     \
     _(GetDOMProperty)                                                       \
     _(SetDOMProperty)
 
 // Forward declarations of MIR types.
 #define FORWARD_DECLARE(op) class M##op;
--- a/js/src/ion/TypePolicy.cpp
+++ b/js/src/ion/TypePolicy.cpp
@@ -381,19 +381,22 @@ CallSetElementPolicy::adjustInputs(MInst
 bool
 InstanceOfPolicy::adjustInputs(MInstruction *def)
 {
     // Box first operand if it isn't object
     if (def->getOperand(0)->type() != MIRType_Object) {
        BoxPolicy<0>::staticAdjustInputs(def);
     }
 
-    // Unbox second operand forcefully to an object, 
-    // so it bailouts with other types
-    ObjectPolicy<1>::staticAdjustInputs(def);
+    if (def->numOperands() == 2) {
+        // Unbox second operand forcefully to an object,
+        // so it bailouts with other types
+        ObjectPolicy<1>::staticAdjustInputs(def);
+    }
+
     return true;
 }
 
 bool
 StoreTypedArrayPolicy::adjustInputs(MInstruction *ins)
 {
     MStoreTypedArrayElement *store = ins->toStoreTypedArrayElement();
     JS_ASSERT(store->elements()->type() == MIRType_Elements);
--- a/js/src/ion/TypePolicy.h
+++ b/js/src/ion/TypePolicy.h
@@ -185,18 +185,18 @@ class MixPolicy : public TypePolicy
 };
 
 class CallSetElementPolicy : public SingleObjectPolicy
 {
   public:
     bool adjustInputs(MInstruction *def);
 };
 
-// First operand will be boxed to an Value (except for an object)
-// Second operand will forcefully be unboxed to an object
+// First operand will be boxed to a Value (except for an object)
+// Second operand (if specified) will forcefully be unboxed to an object
 class InstanceOfPolicy : public TypePolicy
 {
   public:
     bool adjustInputs(MInstruction *def);
 };
 
 class StoreTypedArrayPolicy : public BoxInputsPolicy
 {