Add type specialized paths for instanceof to Ion, bug 814861. r=pierron a=lsblakk
authorBrian Hackett <bhackett1024@gmail.com>
Thu, 06 Dec 2012 15:38:46 -0700
changeset 117363 1a8a0c1e29b6400b8e85d78a6729e8b2748696b7
parent 117362 2aff22c4bacad2fcc2bbdf2f6391f483a2217c92
child 117364 d306f97c1da2c54af79f94f92b1c7001d354628d
push id1861
push userbhackett@mozilla.com
push dateThu, 06 Dec 2012 22:39:13 +0000
treeherdermozilla-beta@1a8a0c1e29b6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspierron, lsblakk
bugs814861
milestone18.0
Add type specialized paths for instanceof to Ion, bug 814861. r=pierron a=lsblakk
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
@@ -3932,16 +3932,132 @@ CodeGenerator::visitIn(LIn *ins)
     static const VMFunction OperatorInInfo = FunctionInfo<pf>(OperatorIn);
 
     pushArg(ToRegister(ins->rhs()));
     pushArg(ToValue(ins, LIn::LHS));
 
     return callVM(OperatorInInfo, ins);
 }
 
+bool
+CodeGenerator::visitInstanceOfO(LInstanceOfO *ins)
+{
+    return emitInstanceOf(ins, ins->mir()->prototypeObject());
+}
+
+bool
+CodeGenerator::visitInstanceOfV(LInstanceOfV *ins)
+{
+    return emitInstanceOf(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::emitInstanceOf(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->isInstanceOfV()) {
+        Label isObject;
+        ValueOperand lhsValue = ToValue(ins, LInstanceOfV::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->toInstanceOfO()->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->isInstanceOfV()) {
+            ValueOperand lhsValue = ToValue(ins, LInstanceOfV::LHS);
+            objReg = masm.extractObject(lhsValue, output);
+        } else {
+            objReg = ToRegister(ins->toInstanceOfO()->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;
+}
+
 typedef bool (*HasInstanceFn)(JSContext *, HandleObject, HandleValue, JSBool *);
 static const VMFunction HasInstanceInfo = FunctionInfo<HasInstanceFn>(js::HasInstance);
 
 bool
 CodeGenerator::visitCallInstanceOf(LCallInstanceOf *ins)
 {
     ValueOperand lhs = ToValue(ins, LCallInstanceOf::LHS);
     Register rhs = ToRegister(ins->rhs());
--- a/js/src/ion/CodeGenerator.h
+++ b/js/src/ion/CodeGenerator.h
@@ -161,16 +161,19 @@ class CodeGenerator : public CodeGenerat
     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 visitIn(LIn *ins);
+    bool emitInstanceOf(LInstruction *ins, RawObject prototypeObject);
+    bool visitInstanceOfO(LInstanceOfO *ins);
+    bool visitInstanceOfV(LInstanceOfV *ins);
     bool visitCallInstanceOf(LCallInstanceOf *ins);
     bool visitFunctionBoundary(LFunctionBoundary *lir);
     bool visitGetDOMProperty(LGetDOMProperty *lir);
     bool visitSetDOMProperty(LSetDOMProperty *lir);
     bool visitCallDOMNative(LCallDOMNative *lir);
 
     bool visitCheckOverRecursed(LCheckOverRecursed *lir);
     bool visitCheckOverRecursedFailure(CheckOverRecursedFailure *ool);
--- a/js/src/ion/IonBuilder.cpp
+++ b/js/src/ion/IonBuilder.cpp
@@ -6352,19 +6352,47 @@ IonBuilder::jsop_in()
     current->push(ins);
 
     return resumeAfter(ins);
 }
 
 bool
 IonBuilder::jsop_instanceof()
 {
-    MDefinition *proto = current->pop();
+    MDefinition *rhs = current->pop();
     MDefinition *obj = current->pop();
-    MCallInstanceOf *ins = new MCallInstanceOf(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;
+
+        MInstanceOf *ins = new MInstanceOf(obj, protoObject);
+
+        current->add(ins);
+        current->push(ins);
+
+        return resumeAfter(ins);
+    } while (false);
+
+    MCallInstanceOf *ins = new MCallInstanceOf(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
@@ -3092,16 +3092,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 LInstanceOfO : public LInstructionHelper<1, 1, 0>
+{
+  public:
+    LIR_HEADER(InstanceOfO);
+    LInstanceOfO(const LAllocation &lhs) {
+        setOperand(0, lhs);
+    }
+
+    MInstanceOf *mir() const {
+        return mir_->toInstanceOf();
+    }
+
+    const LAllocation *lhs() {
+        return getOperand(0);
+    }
+};
+
+class LInstanceOfV : public LInstructionHelper<1, BOX_PIECES, 0>
+{
+  public:
+    LIR_HEADER(InstanceOfV);
+    LInstanceOfV() {
+    }
+
+    MInstanceOf *mir() const {
+        return mir_->toInstanceOf();
+    }
+
+    const LAllocation *lhs() {
+        return getOperand(LHS);
+    }
+
+    static const size_t LHS = 0;
+};
+
 class LCallInstanceOf : public LCallInstructionHelper<1, BOX_PIECES+1, 0>
 {
   public:
     LIR_HEADER(CallInstanceOf);
     LCallInstanceOf(const LAllocation &rhs) {
         setOperand(RHS, rhs);
     }
 
--- a/js/src/ion/LOpcodes.h
+++ b/js/src/ion/LOpcodes.h
@@ -160,16 +160,18 @@
     _(StringLength)                 \
     _(ArgumentsLength)              \
     _(GetArgument)                  \
     _(TypeOfV)                      \
     _(ToIdV)                        \
     _(Floor)                        \
     _(Round)                        \
     _(In)                           \
+    _(InstanceOfO)                  \
+    _(InstanceOfV)                  \
     _(CallInstanceOf)               \
     _(InterruptCheck)               \
     _(FunctionBoundary)             \
     _(GetDOMProperty)               \
     _(SetDOMProperty)               \
     _(CallDOMNative)
 
 #if defined(JS_CPU_X86)
--- a/js/src/ion/Lowering.cpp
+++ b/js/src/ion/Lowering.cpp
@@ -1840,16 +1840,32 @@ 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::visitInstanceOf(MInstanceOf *ins)
+{
+    MDefinition *lhs = ins->getOperand(0);
+
+    JS_ASSERT(lhs->type() == MIRType_Value || lhs->type() == MIRType_Object);
+
+    if (lhs->type() == MIRType_Object) {
+        LInstanceOfO *lir = new LInstanceOfO(useRegister(lhs));
+        return define(lir, ins) && assignSafepoint(lir, ins);
+    }
+
+    LInstanceOfV *lir = new LInstanceOfV();
+    return useBox(lir, LInstanceOfV::LHS, lhs) && define(lir, ins) && assignSafepoint(lir, ins);
+}
+
+bool
 LIRGenerator::visitCallInstanceOf(MCallInstanceOf *ins)
 {
     MDefinition *lhs = ins->lhs();
     MDefinition *rhs = ins->rhs();
 
     JS_ASSERT(lhs->type() == MIRType_Value);
     JS_ASSERT(rhs->type() == MIRType_Object);
 
--- a/js/src/ion/Lowering.h
+++ b/js/src/ion/Lowering.h
@@ -175,16 +175,17 @@ class LIRGenerator : public LIRGenerator
     bool visitIteratorNext(MIteratorNext *ins);
     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 visitInstanceOf(MInstanceOf *ins);
     bool visitCallInstanceOf(MCallInstanceOf *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
@@ -5192,16 +5192,42 @@ class MIn
 
     INSTRUCTION_HEADER(In);
 
     TypePolicy *typePolicy() {
         return this;
     }
 };
 
+// Implementation for instanceof operator with specific rhs.
+class MInstanceOf
+  : public MUnaryInstruction,
+    public InstanceOfPolicy
+{
+    CompilerRootObject protoObj_;
+
+  public:
+    MInstanceOf(MDefinition *obj, RawObject proto)
+      : MUnaryInstruction(obj),
+        protoObj_(proto)
+    {
+        setResultType(MIRType_Boolean);
+    }
+
+    INSTRUCTION_HEADER(InstanceOf);
+
+    TypePolicy *typePolicy() {
+        return this;
+    }
+
+    RawObject prototypeObject() {
+        return protoObj_;
+    }
+};
+
 // Implementation for instanceof operator.
 class MCallInstanceOf
   : public MBinaryInstruction,
     public MixPolicy<BoxPolicy<0>, ObjectPolicy<1> >
 {
   public:
     MCallInstanceOf(MDefinition *obj, MDefinition *proto)
       : MBinaryInstruction(obj, proto)
--- a/js/src/ion/MOpcodes.h
+++ b/js/src/ion/MOpcodes.h
@@ -125,16 +125,17 @@ namespace ion {
     _(IteratorMore)                                                         \
     _(IteratorEnd)                                                          \
     _(StringLength)                                                         \
     _(ArgumentsLength)                                                      \
     _(GetArgument)                                                          \
     _(Floor)                                                                \
     _(Round)                                                                \
     _(In)                                                                   \
+    _(InstanceOf)                                                           \
     _(CallInstanceOf)                                                       \
     _(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
@@ -403,19 +403,16 @@ 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);
     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
@@ -191,18 +191,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
 {