Bug 1344469 - Part 4 - Megamorphic stub. r=jandem
authorTom Schuster <evilpies@gmail.com>
Thu, 13 Apr 2017 22:17:57 +0200
changeset 352999 5e1a2ab034aee990e3a13c84eabb4d7cfcfc8791
parent 352998 07b10177f7084e753b95af68edc5f9f620dd8f6c
child 353000 a9d036ef05e83abbf6aa6370a4ef36bbc4d01706
push id31655
push userihsiao@mozilla.com
push dateFri, 14 Apr 2017 09:07:09 +0000
treeherdermozilla-central@8f806306fb83 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs1344469
milestone55.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 1344469 - Part 4 - Megamorphic stub. r=jandem
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
--- a/js/src/jit/CacheIR.cpp
+++ b/js/src/jit/CacheIR.cpp
@@ -2033,16 +2033,23 @@ HasOwnIRGenerator::tryAttachNativeHasOwn
 {
     PropertyResult prop;
     if (!LookupOwnPropertyPure(cx_, obj, key, &prop))
         return false;
 
     if (!prop.isNativeProperty())
         return false;
 
+    if (mode_ == ICState::Mode::Megamorphic) {
+        writer.megamorphicHasOwnResult(objId, keyId);
+        writer.returnFromIC();
+        trackAttached("MegamorphicHasOwn");
+        return true;
+    }
+
     Maybe<ObjOperandId> holderId;
     emitIdGuard(keyId, key);
     EmitReadSlotGuard(writer, obj, obj, prop.shape(), objId, &holderId);
     writer.loadBooleanResult(true);
     writer.returnFromIC();
 
     trackAttached("NativeHasOwn");
     return true;
@@ -2050,16 +2057,23 @@ HasOwnIRGenerator::tryAttachNativeHasOwn
 
 bool
 HasOwnIRGenerator::tryAttachNativeHasOwnDoesNotExist(HandleId key, ValOperandId keyId,
                                                      HandleObject obj, ObjOperandId objId)
 {
     if (!CheckHasNoSuchOwnProperty(cx_, obj, key))
         return false;
 
+    if (mode_ == ICState::Mode::Megamorphic) {
+        writer.megamorphicHasOwnResult(objId, keyId);
+        writer.returnFromIC();
+        trackAttached("MegamorphicHasOwn");
+        return true;
+    }
+
     Maybe<ObjOperandId> expandoId;
     emitIdGuard(keyId, key);
     TestMatchingReceiver(writer, obj, nullptr, objId, &expandoId);
     writer.loadBooleanResult(false);
     writer.returnFromIC();
 
     trackAttached("NativeHasOwnDoesNotExist");
     return true;
--- a/js/src/jit/CacheIR.h
+++ b/js/src/jit/CacheIR.h
@@ -181,16 +181,17 @@ extern const char* CacheKindNames[];
     _(LoadObject)                         \
     _(LoadProto)                          \
     _(LoadEnclosingEnvironment)           \
     _(LoadWrapperTarget)                  \
                                           \
     _(MegamorphicLoadSlotResult)          \
     _(MegamorphicLoadSlotByValueResult)   \
     _(MegamorphicStoreSlot)               \
+    _(MegamorphicHasOwnResult)            \
                                           \
     /* See CacheIR.cpp 'DOM proxies' comment. */ \
     _(LoadDOMExpandoValue)                \
     _(LoadDOMExpandoValueGuardGeneration) \
     _(LoadDOMExpandoValueIgnoreGeneration)\
     _(GuardDOMExpandoMissingOrGuardShape) \
                                           \
     _(StoreFixedSlot)                     \
@@ -776,16 +777,20 @@ class MOZ_RAII CacheIRWriter : public JS
     }
     void megamorphicStoreSlot(ObjOperandId obj, PropertyName* name, ValOperandId rhs,
                               bool needsTypeBarrier) {
         writeOpWithOperandId(CacheOp::MegamorphicStoreSlot, obj);
         addStubField(uintptr_t(name), StubField::Type::String);
         writeOperandId(rhs);
         buffer_.writeByte(needsTypeBarrier);
     }
+    void megamorphicHasOwnResult(ObjOperandId obj, ValOperandId id) {
+        writeOpWithOperandId(CacheOp::MegamorphicHasOwnResult, obj);
+        writeOperandId(id);
+    }
 
     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
@@ -2274,8 +2274,56 @@ CacheIRCompiler::emitMegamorphicLoadSlot
     masm.jump(failure->label());
 
     masm.bind(&ok);
     masm.setFramePushed(framePushed);
     masm.loadTypedOrValue(Address(masm.getStackPointer(), 0), output);
     masm.adjustStack(sizeof(Value));
     return true;
 }
+
+bool
+CacheIRCompiler::emitMegamorphicHasOwnResult()
+{
+    AutoOutputRegister output(*this);
+
+    Register obj = allocator.useRegister(masm, reader.objOperandId());
+    ValueOperand idVal = allocator.useValueRegister(masm, reader.valOperandId());
+
+    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].
+    masm.reserveStack(sizeof(Value));
+    masm.Push(idVal);
+    masm.moveStackPtrTo(idVal.scratchReg());
+
+    LiveRegisterSet volatileRegs(GeneralRegisterSet::Volatile(), liveVolatileFloatRegs());
+    volatileRegs.takeUnchecked(scratch);
+    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));
+    masm.mov(ReturnReg, scratch);
+    masm.PopRegsInMask(volatileRegs);
+
+    masm.Pop(idVal);
+
+    Label ok;
+    uint32_t framePushed = masm.framePushed();
+    masm.branchIfTrueBool(scratch, &ok);
+    masm.adjustStack(sizeof(Value));
+    masm.jump(failure->label());
+
+    masm.bind(&ok);
+    masm.setFramePushed(framePushed);
+    masm.loadTypedOrValue(Address(masm.getStackPointer(), 0), output);
+    masm.adjustStack(sizeof(Value));
+    return true;
+}
--- a/js/src/jit/CacheIRCompiler.h
+++ b/js/src/jit/CacheIRCompiler.h
@@ -47,16 +47,17 @@ namespace jit {
     _(LoadArgumentsObjectArgResult)       \
     _(LoadDenseElementResult)             \
     _(LoadDenseElementHoleResult)         \
     _(LoadDenseElementExistsResult)       \
     _(LoadDenseElementHoleExistsResult)   \
     _(LoadUnboxedArrayElementResult)      \
     _(LoadTypedElementResult)             \
     _(MegamorphicLoadSlotByValueResult)   \
+    _(MegamorphicHasOwnResult)            \
     _(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
@@ -1594,51 +1594,61 @@ GetNativeDataProperty(JSContext* cx, JSO
 }
 
 template bool
 GetNativeDataProperty<true>(JSContext* cx, JSObject* obj, PropertyName* name, Value* vp);
 
 template bool
 GetNativeDataProperty<false>(JSContext* cx, JSObject* obj, PropertyName* name, Value* vp);
 
+static MOZ_ALWAYS_INLINE bool
+ValueToAtomOrSymbol(JSContext* cx, Value& idVal, jsid* id)
+{
+    JS::AutoCheckCannotGC nogc;
+
+    if (MOZ_LIKELY(idVal.isString())) {
+        JSString* s = idVal.toString();
+        JSAtom* atom;
+        if (s->isAtom()) {
+            atom = &s->asAtom();
+        } else {
+            atom = AtomizeString(cx, s);
+            if (!atom)
+                return false;
+        }
+        *id = AtomToId(atom);
+    } else if (idVal.isSymbol()) {
+        *id = SYMBOL_TO_JSID(idVal.toSymbol());
+    } else {
+        if (!ValueToIdPure(idVal, id))
+            return false;
+    }
+
+    // Watch out for ids that may be stored in dense elements.
+    static_assert(NativeObject::MAX_DENSE_ELEMENTS_COUNT < JSID_INT_MAX,
+                  "All dense elements must have integer jsids");
+    if (MOZ_UNLIKELY(JSID_IS_INT(*id)))
+        return false;
+
+    return true;
+}
+
 template <bool HandleMissing>
 bool
 GetNativeDataPropertyByValue(JSContext* cx, JSObject* obj, Value* vp)
 {
     JS::AutoCheckCannotGC nogc;
 
     if (MOZ_UNLIKELY(!obj->isNative()))
         return false;
 
     // vp[0] contains the id, result will be stored in vp[1].
     Value idVal = vp[0];
-
     jsid id;
-    if (MOZ_LIKELY(idVal.isString())) {
-        JSString* s = idVal.toString();
-        JSAtom* atom;
-        if (s->isAtom()) {
-            atom = &s->asAtom();
-        } else {
-            atom = AtomizeString(cx, s);
-            if (!atom)
-                return false;
-        }
-        id = AtomToId(atom);
-    } else if (idVal.isSymbol()) {
-        id = SYMBOL_TO_JSID(idVal.toSymbol());
-    } else {
-        if (!ValueToIdPure(idVal, &id))
-            return false;
-    }
-
-    // Watch out for ids that may be stored in dense elements.
-    static_assert(NativeObject::MAX_DENSE_ELEMENTS_COUNT < JSID_INT_MAX,
-                  "All dense elements must have integer jsids");
-    if (MOZ_UNLIKELY(JSID_IS_INT(id)))
+    if (!ValueToAtomOrSymbol(cx, idVal, &id))
         return false;
 
     Value* res = vp + 1;
     return GetNativeDataProperty<HandleMissing>(cx, &obj->as<NativeObject>(), id, res);
 }
 
 template bool
 GetNativeDataPropertyByValue<true>(JSContext* cx, JSObject* obj, Value* vp);
@@ -1720,10 +1730,41 @@ ObjectHasGetterSetter(JSContext* cx, JSO
             return false;
 
         if (!proto->isNative())
             return false;
         nobj = &proto->as<NativeObject>();
     }
 }
 
+bool
+HasOwnNativeDataProperty(JSContext* cx, JSObject* obj, Value* vp)
+{
+    JS::AutoCheckCannotGC nogc;
+
+    if (MOZ_UNLIKELY(!obj->isNative()))
+        return false;
+
+    // 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;
+
+    NativeObject* nobj = &obj->as<NativeObject>();
+    if (nobj->lastProperty()->search(cx, id)) {
+        vp[1].setBoolean(true);
+        return true;
+    }
+
+    // Property not found. Watch out for Class hooks.
+    if (MOZ_UNLIKELY(!nobj->is<PlainObject>())) {
+        if (ClassMayResolveId(cx->names(), nobj->getClass(), id, nobj))
+            return false;
+    }
+
+    // Missing property.
+    vp[1].setBoolean(false);
+    return true;
+}
+
 } // namespace jit
 } // namespace js
--- a/js/src/jit/VMFunctions.h
+++ b/js/src/jit/VMFunctions.h
@@ -851,16 +851,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);
 
+bool
+HasOwnNativeDataProperty(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);
 
 } // namespace jit