Bug 1357759 - Support megamorphic IC for JSOP_IN draft
authorTed Campbell <tcampbell@mozilla.com>
Tue, 26 Sep 2017 17:40:57 -0400
changeset 672655 27a484fb86e171b129a99abcd8bf780c45b9b50b
parent 672654 945135f3ffb793c4433808c8ced4b19d452186a0
child 733872 a511640deb9bc41e489f071a9c34742405308f7a
push id82322
push userbmo:tcampbell@mozilla.com
push dateFri, 29 Sep 2017 15:16:57 +0000
bugs1357759
milestone58.0a1
Bug 1357759 - Support megamorphic IC for JSOP_IN MozReview-Commit-ID: Ll94KKuN5FH
js/src/jit-test/tests/cacheir/has.js
js/src/jit/CacheIR.cpp
js/src/jit/CacheIR.h
js/src/jit/CacheIRCompiler.cpp
js/src/jit/CacheIRCompiler.h
js/src/jit/VMFunctions.cpp
js/src/jit/VMFunctions.h
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/has.js
@@ -0,0 +1,49 @@
+var max = 40;
+setJitCompilerOption("ion.warmup.trigger", max - 10);
+
+function simple() {
+    var array = [{a: 1}, {b: 1, a: 1}, {c: 1, a: 1}];
+    for (var i = 0; i < array.length; i++) {
+        var x = array[i];
+        assertEq("a" in x, true);
+        assertEq("d" in x, false);
+    }
+}
+
+function megamorphic() {
+    var array = [{a: 1}, {b: 1, a: 1}, {c: 1, a: 1},
+                 {a: 1, b: 1}, {c: 1, e: 1, a: 1},
+                 {__proto__:{e: 1, f: 1, a: 1, g: 1}},
+                 {__proto__:{e: 1, f: 1, a: 1, g: 1, h: 1}}];
+    for (var i = 0; i < array.length; i++) {
+        var x = array[i];
+        assertEq("a" in x, true);
+        assertEq("d" in x, false);
+    }
+}
+
+function proto() {
+    var base = {a: 1};
+    var array = [{__proto__: base},
+                 {__proto__: base, b: 1, a: 1},
+                 {__proto__: base, c: 1, a: 1}];
+    for (var j = 0; j < 2; j++) {
+        for (var i = 0; i < array.length; i++) {
+            var x = array[i];
+            assertEq("a" in x, true);
+            assertEq("d" in x, (j > 0));
+        }
+        base.d = 1; // Define property on prototype
+    }
+}
+
+function test() {
+    for (var i = 0; i < max; i++) {
+        simple();
+        megamorphic();
+        proto();
+    }
+}
+
+test();
+test();
--- a/js/src/jit/CacheIR.cpp
+++ b/js/src/jit/CacheIR.cpp
@@ -2414,23 +2414,22 @@ HasPropIRGenerator::tryAttachNamedProp(H
         return true;
 
     return false;
 }
 
 bool
 HasPropIRGenerator::tryAttachMegamorphic(ObjOperandId objId, ValOperandId keyId)
 {
+    bool hasOwn = (cacheKind_ == CacheKind::HasOwn);
+
     if (mode_ != ICState::Mode::Megamorphic)
         return false;
 
-    if (cacheKind_ != CacheKind::HasOwn)
-        return false;
-
-    writer.megamorphicHasOwnResult(objId, keyId);
+    writer.megamorphicHasPropResult(objId, keyId, hasOwn);
     writer.returnFromIC();
     trackAttached("MegamorphicHasProp");
     return true;
 }
 
 bool
 HasPropIRGenerator::tryAttachNative(JSObject* obj, ObjOperandId objId, jsid key,
                                     ValOperandId keyId, PropertyResult prop, JSObject* holder)
--- a/js/src/jit/CacheIR.h
+++ b/js/src/jit/CacheIR.h
@@ -199,17 +199,17 @@ extern const char* CacheKindNames[];
     _(LoadProto)                          \
     _(LoadEnclosingEnvironment)           \
     _(LoadWrapperTarget)                  \
                                           \
     _(MegamorphicLoadSlotResult)          \
     _(MegamorphicLoadSlotByValueResult)   \
     _(MegamorphicStoreSlot)               \
     _(MegamorphicSetElement)              \
-    _(MegamorphicHasOwnResult)            \
+    _(MegamorphicHasPropResult)           \
                                           \
     /* See CacheIR.cpp 'DOM proxies' comment. */ \
     _(LoadDOMExpandoValue)                \
     _(LoadDOMExpandoValueGuardGeneration) \
     _(LoadDOMExpandoValueIgnoreGeneration)\
     _(GuardDOMExpandoMissingOrGuardShape) \
                                           \
     _(StoreFixedSlot)                     \
@@ -882,19 +882,20 @@ class MOZ_RAII CacheIRWriter : public JS
         buffer_.writeByte(needsTypeBarrier);
     }
     void megamorphicSetElement(ObjOperandId obj, ValOperandId id, ValOperandId rhs, bool strict) {
         writeOpWithOperandId(CacheOp::MegamorphicSetElement, obj);
         writeOperandId(id);
         writeOperandId(rhs);
         buffer_.writeByte(uint32_t(strict));
     }
-    void megamorphicHasOwnResult(ObjOperandId obj, ValOperandId id) {
-        writeOpWithOperandId(CacheOp::MegamorphicHasOwnResult, obj);
+    void megamorphicHasPropResult(ObjOperandId obj, ValOperandId id, bool hasOwn) {
+        writeOpWithOperandId(CacheOp::MegamorphicHasPropResult, obj);
         writeOperandId(id);
+        buffer_.writeByte(uint32_t(hasOwn));
     }
 
     void loadBooleanResult(bool val) {
         writeOp(CacheOp::LoadBooleanResult);
         buffer_.writeByte(uint32_t(val));
     }
     void loadUndefinedResult() {
         writeOp(CacheOp::LoadUndefinedResult);
--- a/js/src/jit/CacheIRCompiler.cpp
+++ b/js/src/jit/CacheIRCompiler.cpp
@@ -2536,22 +2536,23 @@ CacheIRCompiler::emitMegamorphicLoadSlot
     masm.bind(&ok);
     masm.setFramePushed(framePushed);
     masm.loadTypedOrValue(Address(masm.getStackPointer(), 0), output);
     masm.adjustStack(sizeof(Value));
     return true;
 }
 
 bool
-CacheIRCompiler::emitMegamorphicHasOwnResult()
+CacheIRCompiler::emitMegamorphicHasPropResult()
 {
     AutoOutputRegister output(*this);
 
     Register obj = allocator.useRegister(masm, reader.objOperandId());
     ValueOperand idVal = allocator.useValueRegister(masm, reader.valOperandId());
+    bool hasOwn = reader.readBool();
 
     AutoScratchRegisterMaybeOutput scratch(allocator, masm, output);
 
     FailurePath* failure;
     if (!addFailurePath(&failure))
         return false;
 
     // idVal will be in vp[0], result will be stored in vp[1].
@@ -2564,17 +2565,20 @@ CacheIRCompiler::emitMegamorphicHasOwnRe
     volatileRegs.takeUnchecked(idVal);
     masm.PushRegsInMask(volatileRegs);
 
     masm.setupUnalignedABICall(scratch);
     masm.loadJSContext(scratch);
     masm.passABIArg(scratch);
     masm.passABIArg(obj);
     masm.passABIArg(idVal.scratchReg());
-    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, HasOwnNativeDataProperty));
+    if (hasOwn)
+        masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, HasNativeDataProperty<true>));
+    else
+        masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, HasNativeDataProperty<false>));
     masm.mov(ReturnReg, scratch);
     masm.PopRegsInMask(volatileRegs);
 
     masm.Pop(idVal);
 
     Label ok;
     uint32_t framePushed = masm.framePushed();
     masm.branchIfTrueBool(scratch, &ok);
--- a/js/src/jit/CacheIRCompiler.h
+++ b/js/src/jit/CacheIRCompiler.h
@@ -57,17 +57,17 @@ namespace jit {
     _(LoadTypeOfObjectResult)             \
     _(CompareStringResult)                \
     _(CompareObjectResult)                \
     _(CompareSymbolResult)                \
     _(ArrayJoinResult)                    \
     _(CallPrintString)                    \
     _(Breakpoint)                         \
     _(MegamorphicLoadSlotByValueResult)   \
-    _(MegamorphicHasOwnResult)            \
+    _(MegamorphicHasPropResult)           \
     _(WrapResult)
 
 // Represents a Value on the Baseline frame's expression stack. Slot 0 is the
 // value on top of the stack (the most recently pushed value), slot 1 is the
 // value pushed before that, etc.
 class BaselineFrameSlot
 {
     uint32_t slot_;
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -1776,53 +1776,73 @@ ObjectHasGetterSetter(JSContext* cx, JSO
             return false;
 
         if (!proto->isNative())
             return false;
         nobj = &proto->as<NativeObject>();
     }
 }
 
+template <bool HasOwn>
 bool
-HasOwnNativeDataProperty(JSContext* cx, JSObject* obj, Value* vp)
+HasNativeDataProperty(JSContext* cx, JSObject* obj, Value* vp)
 {
     AutoUnsafeCallWithABI unsafe;
 
     // vp[0] contains the id, result will be stored in vp[1].
     Value idVal = vp[0];
     jsid id;
     if (!ValueToAtomOrSymbol(cx, idVal, &id))
         return false;
 
-    if (!obj->isNative()) {
-        if (obj->is<UnboxedPlainObject>()) {
-            bool res = obj->as<UnboxedPlainObject>().containsUnboxedOrExpandoProperty(cx, id);
-            vp[1].setBoolean(res);
-            return true;
-        }
-        return false;
-    }
+    do {
+        if (obj->isNative()) {
+            if (obj->as<NativeObject>().lastProperty()->search(cx, id)) {
+                vp[1].setBoolean(true);
+                return true;
+            }
 
-    NativeObject* nobj = &obj->as<NativeObject>();
-    if (nobj->lastProperty()->search(cx, id)) {
-        vp[1].setBoolean(true);
-        return true;
-    }
+            // Fail if there's a resolve hook, unless the mayResolve hook tells
+            // us the resolve hook won't define a property with this id.
+            if (MOZ_UNLIKELY(ClassMayResolveId(cx->names(), obj->getClass(), id, obj)))
+                return false;
+        } else if (obj->is<UnboxedPlainObject>()) {
+            if (obj->as<UnboxedPlainObject>().containsUnboxedOrExpandoProperty(cx, id)) {
+                vp[1].setBoolean(true);
+                return true;
+            }
+        } else if (obj->is<TypedObject>()) {
+            if (obj->as<TypedObject>().typeDescr().hasProperty(cx->names(), id)) {
+                vp[1].setBoolean(true);
+                return true;
+            }
+        } else {
+            return false;
+        }
 
-    // Property not found. Watch out for Class hooks.
-    if (MOZ_UNLIKELY(!nobj->is<PlainObject>())) {
-        if (ClassMayResolveId(cx->names(), nobj->getClass(), id, nobj))
-            return false;
-    }
+        // If implementing Object.hasOwnProperty, don't follow protochain.
+        if (HasOwn)
+            break;
+
+        // Get prototype. Objects that may allow dynamic prototypes are already
+        // filtered out above.
+        obj = obj->staticPrototype();
+    } while (obj);
 
     // Missing property.
     vp[1].setBoolean(false);
     return true;
 }
 
+template bool
+HasNativeDataProperty<true>(JSContext* cx, JSObject* obj, Value* vp);
+
+template bool
+HasNativeDataProperty<false>(JSContext* cx, JSObject* obj, Value* vp);
+
 JSString*
 TypeOfObject(JSObject* obj, JSRuntime* rt)
 {
     AutoUnsafeCallWithABI unsafe;
     JSType type = js::TypeOfObject(obj);
     return TypeName(type, *rt->commonNames);
 }
 
--- a/js/src/jit/VMFunctions.h
+++ b/js/src/jit/VMFunctions.h
@@ -871,18 +871,19 @@ CheckIsCallable(JSContext* cx, HandleVal
 template <bool HandleMissing>
 bool
 GetNativeDataProperty(JSContext* cx, JSObject* obj, PropertyName* name, Value* vp);
 
 template <bool HandleMissing>
 bool
 GetNativeDataPropertyByValue(JSContext* cx, JSObject* obj, Value* vp);
 
+template <bool HasOwn>
 bool
-HasOwnNativeDataProperty(JSContext* cx, JSObject* obj, Value* vp);
+HasNativeDataProperty(JSContext* cx, JSObject* obj, Value* vp);
 
 template <bool NeedsTypeBarrier>
 bool
 SetNativeDataProperty(JSContext* cx, JSObject* obj, PropertyName* name, Value* val);
 
 bool
 ObjectHasGetterSetter(JSContext* cx, JSObject* obj, Shape* propShape);