Bug 1357468 - More Object.hasOwnProperty optimizations for Speedometer. r=jandem
authorTom Schuster <evilpies@gmail.com>
Tue, 18 Apr 2017 18:56:25 +0200
changeset 565176 d555db01bb95f98b1822fdb9a7cabcb4067289cb
parent 565175 8b9f406319daae303d8e3959e0702b1cee87f435
child 565177 b825bae141ebd5bed3572c474320539d2558a460
push id54802
push userdgottwald@mozilla.com
push dateWed, 19 Apr 2017 15:26:27 +0000
reviewersjandem
bugs1357468
milestone55.0a1
Bug 1357468 - More Object.hasOwnProperty optimizations for Speedometer. r=jandem - IC support dense elements, like [0].hasOwnProperty("0") - IC support unboxed objects - IC support proxies, i.e. NodeList or other DOM proxies mostly
js/src/jit/BaselineCacheIRCompiler.cpp
js/src/jit/CacheIR.cpp
js/src/jit/CacheIR.h
js/src/jit/CacheIRCompiler.cpp
js/src/jit/IonCacheIRCompiler.cpp
js/src/jit/VMFunctions.cpp
js/src/proxy/Proxy.cpp
js/src/proxy/Proxy.h
--- a/js/src/jit/BaselineCacheIRCompiler.cpp
+++ b/js/src/jit/BaselineCacheIRCompiler.cpp
@@ -667,16 +667,42 @@ BaselineCacheIRCompiler::emitCallProxyGe
 
     if (!callVM(masm, ProxyGetPropertyByValueInfo))
         return false;
 
     stubFrame.leave(masm);
     return true;
 }
 
+typedef bool (*ProxyHasOwnFn)(JSContext*, HandleObject, HandleValue, MutableHandleValue);
+static const VMFunction ProxyHasOwnInfo = FunctionInfo<ProxyHasOwnFn>(ProxyHasOwn, "ProxyHasOwn");
+
+bool
+BaselineCacheIRCompiler::emitCallProxyHasOwnResult()
+{
+    Register obj = allocator.useRegister(masm, reader.objOperandId());
+    ValueOperand idVal = allocator.useValueRegister(masm, reader.valOperandId());
+
+    AutoScratchRegister scratch(allocator, masm);
+
+    allocator.discardStack(masm);
+
+    AutoStubFrame stubFrame(*this);
+    stubFrame.enter(masm, scratch);
+
+    masm.Push(idVal);
+    masm.Push(obj);
+
+    if (!callVM(masm, ProxyHasOwnInfo))
+        return false;
+
+    stubFrame.leave(masm);
+    return true;
+}
+
 bool
 BaselineCacheIRCompiler::emitLoadUnboxedPropertyResult()
 {
     AutoOutputRegister output(*this);
     Register obj = allocator.useRegister(masm, reader.objOperandId());
     AutoScratchRegisterMaybeOutput scratch(allocator, masm, output);
 
     JSValueType fieldType = reader.valueType();
--- a/js/src/jit/CacheIR.cpp
+++ b/js/src/jit/CacheIR.cpp
@@ -2154,29 +2154,32 @@ HasOwnIRGenerator::HasOwnIRGenerator(JSC
 bool
 HasOwnIRGenerator::tryAttachNativeHasOwn(HandleId key, ValOperandId keyId,
                                          HandleObject obj, ObjOperandId objId)
 {
     PropertyResult prop;
     if (!LookupOwnPropertyPure(cx_, obj, key, &prop))
         return false;
 
-    if (!prop.isNativeProperty())
+    if (!prop.isFound())
+        return false;
+
+    if (!obj->isNative() && !obj->is<UnboxedPlainObject>())
         return false;
 
     if (mode_ == ICState::Mode::Megamorphic) {
         writer.megamorphicHasOwnResult(objId, keyId);
         writer.returnFromIC();
         trackAttached("MegamorphicHasOwn");
         return true;
     }
 
-    Maybe<ObjOperandId> holderId;
+    Maybe<ObjOperandId> expandoId;
     emitIdGuard(keyId, key);
-    EmitReadSlotGuard(writer, obj, obj, prop.shape(), objId, &holderId);
+    TestMatchingReceiver(writer, obj, nullptr, objId, &expandoId);
     writer.loadBooleanResult(true);
     writer.returnFromIC();
 
     trackAttached("NativeHasOwn");
     return true;
 }
 
 bool
@@ -2199,16 +2202,47 @@ HasOwnIRGenerator::tryAttachNativeHasOwn
     writer.loadBooleanResult(false);
     writer.returnFromIC();
 
     trackAttached("NativeHasOwnDoesNotExist");
     return true;
 }
 
 bool
+HasOwnIRGenerator::tryAttachProxyElement(ValOperandId keyId, HandleObject obj, ObjOperandId objId)
+{
+    if (!obj->is<ProxyObject>())
+        return false;
+
+    writer.guardIsProxy(objId);
+    writer.callProxyHasOwnResult(objId, keyId);
+    writer.returnFromIC();
+
+    trackAttached("ProxyHasOwn");
+    return true;
+}
+
+bool
+HasOwnIRGenerator::tryAttachDenseHasOwn(uint32_t index, Int32OperandId indexId,
+                                        HandleObject obj, ObjOperandId objId)
+{
+    if (!obj->isNative())
+        return false;
+    if (!obj->as<NativeObject>().containsDenseElement(index))
+        return false;
+
+    writer.guardShape(objId, obj->as<NativeObject>().lastProperty());
+    writer.loadDenseElementExistsResult(objId, indexId);
+    writer.returnFromIC();
+
+    trackAttached("DenseHasOwn");
+    return true;
+}
+
+bool
 HasOwnIRGenerator::tryAttachStub()
 {
     MOZ_ASSERT(cacheKind_ == CacheKind::HasOwn);
 
     AutoAssertNoPendingException aanpe(cx_);
 
     ValOperandId keyId(writer.setInputOperandId(0));
     ValOperandId valId(writer.setInputOperandId(1));
@@ -2216,16 +2250,19 @@ HasOwnIRGenerator::tryAttachStub()
     if (!val_.isObject()) {
         trackNotAttached();
         return false;
     }
     RootedObject obj(cx_, &val_.toObject());
 
     ObjOperandId objId = writer.guardIsObject(valId);
 
+    if (tryAttachProxyElement(keyId, obj, objId))
+        return true;
+
     RootedId id(cx_);
     bool nameOrSymbol;
     if (!ValueToNameOrSymbolId(cx_, key_, &id, &nameOrSymbol)) {
         cx_->clearPendingException();
         return false;
     }
 
     if (nameOrSymbol) {
@@ -2233,16 +2270,26 @@ HasOwnIRGenerator::tryAttachStub()
             return true;
         if (tryAttachNativeHasOwnDoesNotExist(id, keyId, obj, objId))
             return true;
 
         trackNotAttached();
         return false;
     }
 
+    uint32_t index;
+    Int32OperandId indexId;
+    if (maybeGuardInt32Index(key_, keyId, &index, &indexId)) {
+        if (tryAttachDenseHasOwn(index, indexId, obj, objId))
+            return true;
+
+        trackNotAttached();
+        return false;
+    }
+
     trackNotAttached();
     return false;
 }
 
 void
 HasOwnIRGenerator::trackAttached(const char* name)
 {
 #ifdef JS_JITSPEW
--- a/js/src/jit/CacheIR.h
+++ b/js/src/jit/CacheIR.h
@@ -237,16 +237,17 @@ extern const char* CacheKindNames[];
     _(LoadFrameArgumentResult)            \
     _(LoadEnvironmentFixedSlotResult)     \
     _(LoadEnvironmentDynamicSlotResult)   \
     _(LoadObjectResult)                   \
     _(CallScriptedGetterResult)           \
     _(CallNativeGetterResult)             \
     _(CallProxyGetResult)                 \
     _(CallProxyGetByValueResult)          \
+    _(CallProxyHasOwnResult)              \
     _(LoadUndefinedResult)                \
     _(LoadBooleanResult)                  \
                                           \
     _(TypeMonitorResult)                  \
     _(ReturnFromIC)                       \
     _(WrapResult)
 
 enum class CacheOp {
@@ -880,16 +881,20 @@ class MOZ_RAII CacheIRWriter : public JS
     void callProxyGetResult(ObjOperandId obj, jsid id) {
         writeOpWithOperandId(CacheOp::CallProxyGetResult, obj);
         addStubField(uintptr_t(JSID_BITS(id)), StubField::Type::Id);
     }
     void callProxyGetByValueResult(ObjOperandId obj, ValOperandId idVal) {
         writeOpWithOperandId(CacheOp::CallProxyGetByValueResult, obj);
         writeOperandId(idVal);
     }
+    void callProxyHasOwnResult(ObjOperandId obj, ValOperandId idVal) {
+        writeOpWithOperandId(CacheOp::CallProxyHasOwnResult, obj);
+        writeOperandId(idVal);
+    }
     void loadEnvironmentFixedSlotResult(ObjOperandId obj, size_t offset) {
         writeOpWithOperandId(CacheOp::LoadEnvironmentFixedSlotResult, obj);
         addStubField(offset, StubField::Type::RawWord);
     }
     void loadEnvironmentDynamicSlotResult(ObjOperandId obj, size_t offset) {
         writeOpWithOperandId(CacheOp::LoadEnvironmentDynamicSlotResult, obj);
         addStubField(offset, StubField::Type::RawWord);
     }
@@ -1281,16 +1286,19 @@ class MOZ_RAII InIRGenerator : public IR
 };
 
 // HasOwnIRGenerator generates CacheIR for a HasOwn IC.
 class MOZ_RAII HasOwnIRGenerator : public IRGenerator
 {
     HandleValue key_;
     HandleValue val_;
 
+    bool tryAttachProxyElement(ValOperandId keyId, HandleObject obj, ObjOperandId objId);
+    bool tryAttachDenseHasOwn(uint32_t index, Int32OperandId indexId,
+                              HandleObject obj, ObjOperandId objId);
     bool tryAttachNativeHasOwn(HandleId key, ValOperandId keyId,
                            HandleObject obj, ObjOperandId objId);
     bool tryAttachNativeHasOwnDoesNotExist(HandleId key, ValOperandId keyId,
                                            HandleObject obj, ObjOperandId objId);
 
     void trackAttached(const char* name);
     void trackNotAttached();
 
--- a/js/src/jit/CacheIRCompiler.cpp
+++ b/js/src/jit/CacheIRCompiler.cpp
@@ -1928,17 +1928,23 @@ CacheIRCompiler::emitLoadDenseElementExi
 
     // Bounds check. Unsigned compare sends negative indices to next IC.
     Address initLength(scratch, ObjectElements::offsetOfInitializedLength());
     masm.branch32(Assembler::BelowOrEqual, initLength, index, failure->label());
 
     // Hole check.
     BaseObjectElementIndex element(scratch, index);
     masm.branchTestMagic(Assembler::Equal, element, failure->label());
-    masm.moveValue(BooleanValue(true), output.valueReg());
+
+    if (output.hasValue()) {
+        masm.moveValue(BooleanValue(true), output.valueReg());
+    } else {
+        MOZ_ASSERT(output.type() == JSVAL_TYPE_BOOLEAN);
+        masm.movePtr(ImmWord(true), output.typedReg().gpr());
+    }
     return true;
 }
 
 bool
 CacheIRCompiler::emitLoadDenseElementHoleExistsResult()
 {
     AutoOutputRegister output(*this);
     Register obj = allocator.useRegister(masm, reader.objOperandId());
--- a/js/src/jit/IonCacheIRCompiler.cpp
+++ b/js/src/jit/IonCacheIRCompiler.cpp
@@ -1035,16 +1035,43 @@ IonCacheIRCompiler::emitCallProxyGetByVa
 
     if (!callVM(masm, ProxyGetPropertyByValueInfo))
         return false;
 
     masm.storeCallResultValue(output);
     return true;
 }
 
+typedef bool (*ProxyHasOwnFn)(JSContext*, HandleObject, HandleValue, MutableHandleValue);
+static const VMFunction ProxyHasOwnInfo = FunctionInfo<ProxyHasOwnFn>(ProxyHasOwn, "ProxyHasOwn");
+
+bool
+IonCacheIRCompiler::emitCallProxyHasOwnResult()
+{
+    AutoSaveLiveRegisters save(*this);
+    AutoOutputRegister output(*this);
+
+    Register obj = allocator.useRegister(masm, reader.objOperandId());
+    ValueOperand idVal = allocator.useValueRegister(masm, reader.valOperandId());
+
+    allocator.discardStack(masm);
+
+    prepareVMCall(masm);
+
+    masm.Push(idVal);
+    masm.Push(obj);
+
+    if (!callVM(masm, ProxyHasOwnInfo))
+        return false;
+
+    masm.storeCallResultValue(output);
+    return true;
+}
+
+
 bool
 IonCacheIRCompiler::emitLoadUnboxedPropertyResult()
 {
     AutoOutputRegister output(*this);
     Register obj = allocator.useRegister(masm, reader.objOperandId());
 
     JSValueType fieldType = reader.valueType();
     int32_t fieldOffset = int32StubField(reader.stubOffset());
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -1735,25 +1735,31 @@ ObjectHasGetterSetter(JSContext* cx, JSO
     }
 }
 
 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;
 
+    if (!obj->isNative()) {
+        if (obj->is<UnboxedPlainObject>()) {
+            bool res = obj->as<UnboxedPlainObject>().containsUnboxedOrExpandoProperty(cx, id);
+            vp[1].setBoolean(res);
+            return true;
+        }
+        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>())) {
--- a/js/src/proxy/Proxy.cpp
+++ b/js/src/proxy/Proxy.cpp
@@ -279,16 +279,31 @@ Proxy::hasOwn(JSContext* cx, HandleObjec
     const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
     *bp = false; // default result if we refuse to perform this action
     AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::GET, true);
     if (!policy.allowed())
         return policy.returnValue();
     return handler->hasOwn(cx, proxy, id, bp);
 }
 
+bool
+js::ProxyHasOwn(JSContext* cx, HandleObject proxy, HandleValue idVal, MutableHandleValue result)
+{
+    RootedId id(cx);
+    if (!ValueToId<CanGC>(cx, idVal, &id))
+        return false;
+
+    bool hasOwn;
+    if (!Proxy::hasOwn(cx, proxy, id, &hasOwn))
+        return false;
+
+    result.setBoolean(hasOwn);
+    return true;
+}
+
 static Value
 ValueToWindowProxyIfWindow(const Value& v)
 {
     if (v.isObject())
         return ObjectValue(*ToWindowProxyIfWindow(&v.toObject()));
     return v;
 }
 
--- a/js/src/proxy/Proxy.h
+++ b/js/src/proxy/Proxy.h
@@ -76,16 +76,19 @@ class Proxy
 bool
 proxy_Call(JSContext* cx, unsigned argc, Value* vp);
 bool
 proxy_Construct(JSContext* cx, unsigned argc, Value* vp);
 
 // These functions are used by JIT code
 
 bool
+ProxyHasOwn(JSContext* cx, HandleObject proxy, HandleValue idVal, MutableHandleValue result);
+
+bool
 ProxyGetProperty(JSContext* cx, HandleObject proxy, HandleId id, MutableHandleValue vp);
 
 bool
 ProxyGetPropertyByValue(JSContext* cx, HandleObject proxy, HandleValue idVal,
                         MutableHandleValue vp);
 
 bool
 ProxySetProperty(JSContext* cx, HandleObject proxy, HandleId id, HandleValue val, bool strict);