Bug 965992 part 2 - Add inline caches for getting DOM expando properties. r=bz,evilpie
authorJan de Mooij <jdemooij@mozilla.com>
Sat, 07 Jan 2017 10:21:43 +0100
changeset 356451 744e3371701b602e027faec7994df5c064879136
parent 356450 1f5fe007fc9c9820c7894ca4428b7df20264ec65
child 356452 f004cbe6f011a9d20132e57b9030bd70b4236f42
push id10621
push userjlund@mozilla.com
push dateMon, 23 Jan 2017 16:02:43 +0000
treeherdermozilla-aurora@dca7b42e6c67 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz, evilpie
bugs965992
milestone53.0a1
Bug 965992 part 2 - Add inline caches for getting DOM expando properties. r=bz,evilpie
js/src/jit/CacheIR.cpp
js/src/jit/CacheIR.h
js/src/jit/CacheIRCompiler.cpp
js/src/jit/CacheIRCompiler.h
--- a/js/src/jit/CacheIR.cpp
+++ b/js/src/jit/CacheIR.cpp
@@ -65,21 +65,26 @@ EmitLoadSlotResult(CacheIRWriter& writer
 //   returns either an UndefinedValue (no expando), ObjectValue (the expando
 //   object), or PrivateValue(ExpandoAndGeneration*).
 //
 // * LoadDOMExpandoValueGuardGeneration: guards the Value in the proxy's expando
 //   slot is the same PrivateValue(ExpandoAndGeneration*), then guards on its
 //   generation, then returns expandoAndGeneration->expando. This Value is
 //   either an UndefinedValue or ObjectValue.
 //
+// * LoadDOMExpandoValueIgnoreGeneration: assumes the Value in the proxy's
+//   expando slot is a PrivateValue(ExpandoAndGeneration*), unboxes it, and
+//   returns the expandoAndGeneration->expando Value.
+//
 // * GuardDOMExpandoMissingOrGuardShape: takes an expando Value as input, then
 //   guards it's either UndefinedValue or an object with the expected shape.
 
 enum class ProxyStubType {
     None,
+    DOMExpando,
     DOMShadowed,
     DOMUnshadowed,
     Generic
 };
 
 static ProxyStubType
 GetProxyStubType(JSContext* cx, HandleObject obj, HandleId id)
 {
@@ -90,18 +95,21 @@ GetProxyStubType(JSContext* cx, HandleOb
         return ProxyStubType::Generic;
 
     DOMProxyShadowsResult shadows = GetDOMProxyShadowsCheck()(cx, obj, id);
     if (shadows == ShadowCheckFailed) {
         cx->clearPendingException();
         return ProxyStubType::None;
     }
 
-    if (DOMProxyIsShadowing(shadows))
+    if (DOMProxyIsShadowing(shadows)) {
+        if (shadows == ShadowsViaDirectExpando || shadows == ShadowsViaIndirectExpando)
+            return ProxyStubType::DOMExpando;
         return ProxyStubType::DOMShadowed;
+    }
 
     MOZ_ASSERT(shadows == DoesntShadow || shadows == DoesntShadowUnique);
     return ProxyStubType::DOMUnshadowed;
 }
 
 bool
 GetPropIRGenerator::tryAttachStub()
 {
@@ -552,16 +560,76 @@ GetPropIRGenerator::tryAttachGenericProx
         writer.callProxyGetByValueResult(objId, getElemKeyValueId());
     }
 
     writer.typeMonitorResult();
     return true;
 }
 
 bool
+GetPropIRGenerator::tryAttachDOMProxyExpando(HandleObject obj, ObjOperandId objId, HandleId id)
+{
+    MOZ_ASSERT(IsCacheableDOMProxy(obj));
+
+    RootedValue expandoVal(cx_, GetProxyExtra(obj, GetDOMProxyExpandoSlot()));
+    RootedObject expandoObj(cx_);
+    ExpandoAndGeneration* expandoAndGeneration = nullptr;
+    if (expandoVal.isObject()) {
+        expandoObj = &expandoVal.toObject();
+    } else {
+        MOZ_ASSERT(!expandoVal.isUndefined(),
+                   "How did a missing expando manage to shadow things?");
+        expandoAndGeneration = static_cast<ExpandoAndGeneration*>(expandoVal.toPrivate());
+        MOZ_ASSERT(expandoAndGeneration);
+        expandoObj = &expandoAndGeneration->expando.toObject();
+    }
+
+    // Try to do the lookup on the expando object.
+    RootedNativeObject holder(cx_);
+    RootedShape propShape(cx_);
+    NativeGetPropCacheability canCache =
+        CanAttachNativeGetProp(cx_, expandoObj, id, &holder, &propShape, pc_, engine_,
+                               canAttachGetter_, isTemporarilyUnoptimizable_);
+    if (canCache != CanAttachReadSlot && canCache != CanAttachCallGetter)
+        return false;
+    if (!holder)
+        return false;
+
+    MOZ_ASSERT(holder == expandoObj);
+
+    maybeEmitIdGuard(id);
+    writer.guardShape(objId, obj->maybeShape());
+
+    // Shape determines Class, so now it must be a DOM proxy.
+    ValOperandId expandoValId;
+    if (expandoVal.isObject()) {
+        expandoValId = writer.loadDOMExpandoValue(objId);
+    } else {
+        MOZ_ASSERT(expandoAndGeneration);
+        expandoValId = writer.loadDOMExpandoValueIgnoreGeneration(objId);
+    }
+
+    // Guard the expando is an object and shape guard.
+    ObjOperandId expandoObjId = writer.guardIsObject(expandoValId);
+    writer.guardShape(expandoObjId, expandoObj->as<NativeObject>().shape());
+
+    if (canCache == CanAttachReadSlot) {
+        // Load from the expando's slots.
+        EmitLoadSlotResult(writer, expandoObjId, &expandoObj->as<NativeObject>(), propShape);
+        writer.typeMonitorResult();
+    } else {
+        // Call the getter. Note that we pass objId, the DOM proxy, as |this|
+        // and not the expando object.
+        MOZ_ASSERT(canCache == CanAttachCallGetter);
+        EmitCallGetterResultNoGuards(writer, expandoObj, expandoObj, propShape, objId);
+    }
+    return true;
+}
+
+bool
 GetPropIRGenerator::tryAttachDOMProxyShadowed(HandleObject obj, ObjOperandId objId, HandleId id)
 {
     MOZ_ASSERT(IsCacheableDOMProxy(obj));
 
     maybeEmitIdGuard(id);
     writer.guardShape(objId, obj->maybeShape());
 
     // No need for more guards: we know this is a DOM proxy, since the shape
@@ -579,19 +647,18 @@ CheckDOMProxyExpandoDoesNotShadow(CacheI
                                   ObjOperandId objId)
 {
     MOZ_ASSERT(IsCacheableDOMProxy(obj));
 
     Value expandoVal = GetProxyExtra(obj, GetDOMProxyExpandoSlot());
 
     ValOperandId expandoId;
     if (!expandoVal.isObject() && !expandoVal.isUndefined()) {
-        ExpandoAndGeneration* expandoAndGeneration = (ExpandoAndGeneration*)expandoVal.toPrivate();
-        expandoId = writer.loadDOMExpandoValueGuardGeneration(objId, expandoAndGeneration,
-                                                              expandoAndGeneration->generation);
+        auto expandoAndGeneration = static_cast<ExpandoAndGeneration*>(expandoVal.toPrivate());
+        expandoId = writer.loadDOMExpandoValueGuardGeneration(objId, expandoAndGeneration);
         expandoVal = expandoAndGeneration->expando;
     } else {
         expandoId = writer.loadDOMExpandoValue(objId);
     }
 
     if (expandoVal.isUndefined()) {
         // Guard there's no expando object.
         writer.guardType(expandoId, JSVAL_TYPE_UNDEFINED);
@@ -660,16 +727,24 @@ GetPropIRGenerator::tryAttachDOMProxyUns
 }
 
 bool
 GetPropIRGenerator::tryAttachProxy(HandleObject obj, ObjOperandId objId, HandleId id)
 {
     switch (GetProxyStubType(cx_, obj, id)) {
       case ProxyStubType::None:
         return false;
+      case ProxyStubType::DOMExpando:
+        if (tryAttachDOMProxyExpando(obj, objId, id))
+            return true;
+        if (*isTemporarilyUnoptimizable_) {
+            // Scripted getter without JIT code. Just wait.
+            return false;
+        }
+        MOZ_FALLTHROUGH; // Fall through to the generic shadowed case.
       case ProxyStubType::DOMShadowed:
         return tryAttachDOMProxyShadowed(obj, objId, id);
       case ProxyStubType::DOMUnshadowed:
         return tryAttachDOMProxyUnshadowed(obj, objId, id);
       case ProxyStubType::Generic:
         return tryAttachGenericProxy(obj, objId, id);
     }
 
--- a/js/src/jit/CacheIR.h
+++ b/js/src/jit/CacheIR.h
@@ -160,16 +160,17 @@ enum class CacheKind : uint8_t
     _(GuardAndGetIndexFromString)         \
     _(LoadObject)                         \
     _(LoadProto)                          \
     _(LoadEnclosingEnvironment)           \
                                           \
     /* See CacheIR.cpp 'DOM proxies' comment. */ \
     _(LoadDOMExpandoValue)                \
     _(LoadDOMExpandoValueGuardGeneration) \
+    _(LoadDOMExpandoValueIgnoreGeneration)\
     _(GuardDOMExpandoMissingOrGuardShape) \
                                           \
     /* The *Result ops load a value into the cache's result register. */ \
     _(LoadFixedSlotResult)                \
     _(LoadDynamicSlotResult)              \
     _(LoadUnboxedPropertyResult)          \
     _(LoadTypedObjectResult)              \
     _(LoadDenseElementResult)             \
@@ -523,23 +524,28 @@ class MOZ_RAII CacheIRWriter : public JS
         writeOperandId(res);
         return res;
     }
     void guardDOMExpandoMissingOrGuardShape(ValOperandId expando, Shape* shape) {
         writeOpWithOperandId(CacheOp::GuardDOMExpandoMissingOrGuardShape, expando);
         addStubField(uintptr_t(shape), StubField::Type::Shape);
     }
     ValOperandId loadDOMExpandoValueGuardGeneration(ObjOperandId obj,
-                                                    ExpandoAndGeneration* expandoAndGeneration,
-                                                    uint64_t generation)
+                                                    ExpandoAndGeneration* expandoAndGeneration)
     {
         ValOperandId res(nextOperandId_++);
         writeOpWithOperandId(CacheOp::LoadDOMExpandoValueGuardGeneration, obj);
         addStubField(uintptr_t(expandoAndGeneration), StubField::Type::RawWord);
-        addStubField(generation, StubField::Type::RawInt64);
+        addStubField(expandoAndGeneration->generation, StubField::Type::RawInt64);
+        writeOperandId(res);
+        return res;
+    }
+    ValOperandId loadDOMExpandoValueIgnoreGeneration(ObjOperandId obj) {
+        ValOperandId res(nextOperandId_++);
+        writeOpWithOperandId(CacheOp::LoadDOMExpandoValueIgnoreGeneration, obj);
         writeOperandId(res);
         return res;
     }
 
     void loadUndefinedResult() {
         writeOp(CacheOp::LoadUndefinedResult);
     }
     void loadFixedSlotResult(ObjOperandId obj, size_t offset) {
@@ -739,16 +745,17 @@ class MOZ_RAII GetPropIRGenerator : publ
     bool tryAttachUnboxed(HandleObject obj, ObjOperandId objId, HandleId id);
     bool tryAttachUnboxedExpando(HandleObject obj, ObjOperandId objId, HandleId id);
     bool tryAttachTypedObject(HandleObject obj, ObjOperandId objId, HandleId id);
     bool tryAttachObjectLength(HandleObject obj, ObjOperandId objId, HandleId id);
     bool tryAttachModuleNamespace(HandleObject obj, ObjOperandId objId, HandleId id);
     bool tryAttachWindowProxy(HandleObject obj, ObjOperandId objId, HandleId id);
 
     bool tryAttachGenericProxy(HandleObject obj, ObjOperandId objId, HandleId id);
+    bool tryAttachDOMProxyExpando(HandleObject obj, ObjOperandId objId, HandleId id);
     bool tryAttachDOMProxyShadowed(HandleObject obj, ObjOperandId objId, HandleId id);
     bool tryAttachDOMProxyUnshadowed(HandleObject obj, ObjOperandId objId, HandleId id);
     bool tryAttachProxy(HandleObject obj, ObjOperandId objId, HandleId id);
 
     bool tryAttachPrimitive(ValOperandId valId, HandleId id);
     bool tryAttachStringChar(ValOperandId valId, ValOperandId indexId);
     bool tryAttachStringLength(ValOperandId valId, HandleId id);
     bool tryAttachMagicArgumentsName(ValOperandId valId, HandleId id);
--- a/js/src/jit/CacheIRCompiler.cpp
+++ b/js/src/jit/CacheIRCompiler.cpp
@@ -1316,16 +1316,43 @@ CacheIRCompiler::emitLoadDOMExpandoValue
     masm.loadPtr(Address(obj, ProxyObject::offsetOfValues()), val.scratchReg());
     masm.loadValue(Address(val.scratchReg(),
                            ProxyObject::offsetOfExtraSlotInValues(GetDOMProxyExpandoSlot())),
                    val);
     return true;
 }
 
 bool
+CacheIRCompiler::emitLoadDOMExpandoValueIgnoreGeneration()
+{
+    Register obj = allocator.useRegister(masm, reader.objOperandId());
+    ValueOperand output = allocator.defineValueRegister(masm, reader.valOperandId());
+
+    // Determine the expando's Address.
+    Register scratch = output.scratchReg();
+    masm.loadPtr(Address(obj, ProxyObject::offsetOfValues()), scratch);
+    Address expandoAddr(scratch, ProxyObject::offsetOfExtraSlotInValues(GetDOMProxyExpandoSlot()));
+
+#ifdef DEBUG
+    // Private values are stored as doubles, so assert we have a double.
+    Label ok;
+    masm.branchTestDouble(Assembler::Equal, expandoAddr, &ok);
+    masm.assumeUnreachable("DOM expando is not a PrivateValue!");
+    masm.bind(&ok);
+#endif
+
+    // Load the ExpandoAndGeneration* from the PrivateValue.
+    masm.loadPrivate(expandoAddr, scratch);
+
+    // Load expandoAndGeneration->expando into the output Value register.
+    masm.loadValue(Address(scratch, ExpandoAndGeneration::offsetOfExpando()), output);
+    return true;
+}
+
+bool
 CacheIRCompiler::emitLoadUndefinedResult()
 {
     AutoOutputRegister output(*this);
     if (output.hasValue())
         masm.moveValue(UndefinedValue(), output.valueReg());
     else
         masm.assumeUnreachable("Should have monitored undefined result");
     return true;
--- a/js/src/jit/CacheIRCompiler.h
+++ b/js/src/jit/CacheIRCompiler.h
@@ -27,16 +27,17 @@ namespace jit {
     _(GuardNoUnboxedExpando)              \
     _(GuardAndLoadUnboxedExpando)         \
     _(GuardNoDetachedTypedObjects)        \
     _(GuardNoDenseElements)               \
     _(GuardAndGetIndexFromString)         \
     _(LoadProto)                          \
     _(LoadEnclosingEnvironment)           \
     _(LoadDOMExpandoValue)                \
+    _(LoadDOMExpandoValueIgnoreGeneration)\
     _(LoadUndefinedResult)                \
     _(LoadInt32ArrayLengthResult)         \
     _(LoadUnboxedArrayLengthResult)       \
     _(LoadArgumentsObjectLengthResult)    \
     _(LoadStringLengthResult)             \
     _(LoadStringCharResult)               \
     _(LoadArgumentsObjectArgResult)       \
     _(LoadDenseElementResult)             \