Bug 1344469 - Part 3 - CacheIR for HasOwn with baseline support. r=jandem
authorTom Schuster <evilpies@gmail.com>
Thu, 13 Apr 2017 22:17:57 +0200
changeset 352834 07b10177f7084e753b95af68edc5f9f620dd8f6c
parent 352833 50e8be7ee74875005ac2835e8fb21749ee8e1bc3
child 352835 5e1a2ab034aee990e3a13c84eabb4d7cfcfc8791
push id89167
push userevilpies@gmail.com
push dateThu, 13 Apr 2017 20:18:48 +0000
treeherdermozilla-inbound@56e3c896e6cc [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 3 - CacheIR for HasOwn with baseline support. r=jandem
js/src/jit/BaselineCacheIRCompiler.cpp
js/src/jit/BaselineIC.cpp
js/src/jit/CacheIR.cpp
js/src/jit/CacheIR.h
js/src/jit/SharedIC.cpp
js/src/jit/SharedIC.h
--- a/js/src/jit/BaselineCacheIRCompiler.cpp
+++ b/js/src/jit/BaselineCacheIRCompiler.cpp
@@ -1838,16 +1838,17 @@ BaselineCacheIRCompiler::init(CacheKind 
     switch (kind) {
       case CacheKind::GetProp:
         MOZ_ASSERT(numInputs == 1);
         allocator.initInputLocation(0, R0);
         break;
       case CacheKind::GetElem:
       case CacheKind::SetProp:
       case CacheKind::In:
+      case CacheKind::HasOwn:
         MOZ_ASSERT(numInputs == 2);
         allocator.initInputLocation(0, R0);
         allocator.initInputLocation(1, R1);
         break;
       case CacheKind::SetElem:
         MOZ_ASSERT(numInputs == 3);
         allocator.initInputLocation(0, R0);
         allocator.initInputLocation(1, R1);
@@ -1893,16 +1894,17 @@ jit::AttachBaselineCacheIRStub(JSContext
     MOZ_ASSERT(stub->numOptimizedStubs() < MaxOptimizedCacheIRStubs);
 
     enum class CacheIRStubKind { Regular, Monitored, Updated };
 
     uint32_t stubDataOffset;
     CacheIRStubKind stubKind;
     switch (kind) {
       case CacheKind::In:
+      case CacheKind::HasOwn:
         stubDataOffset = sizeof(ICCacheIR_Regular);
         stubKind = CacheIRStubKind::Regular;
         break;
       case CacheKind::GetProp:
       case CacheKind::GetElem:
       case CacheKind::GetName:
         stubDataOffset = sizeof(ICCacheIR_Monitored);
         stubKind = CacheIRStubKind::Monitored;
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -1266,16 +1266,36 @@ static bool
 DoHasOwnFallback(JSContext* cx, BaselineFrame* frame, ICHasOwn_Fallback* stub_,
                  HandleValue keyValue, HandleValue objValue, MutableHandleValue res)
 {
     // This fallback stub may trigger debug mode toggling.
     DebugModeOSRVolatileStub<ICIn_Fallback*> stub(frame, stub_);
 
     FallbackICSpew(cx, stub, "HasOwn");
 
+    if (stub->state().maybeTransition())
+        stub->discardStubs(cx);
+
+    if (stub->state().canAttachStub()) {
+        RootedScript script(cx, frame->script());
+        jsbytecode* pc = stub->icEntry()->pc(script);
+
+        ICStubEngine engine = ICStubEngine::Baseline;
+        HasOwnIRGenerator gen(cx, script, pc, stub->state().mode(), keyValue, objValue);
+        bool attached = false;
+        if (gen.tryAttachStub()) {
+            ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
+                                                        engine, script, stub, &attached);
+            if (newStub)
+                JitSpew(JitSpew_BaselineIC, "  Attached CacheIR stub");
+        }
+        if (!attached)
+            stub->state().trackNotAttached();
+    }
+
     bool found;
     if (!HasOwnProperty(cx, objValue, keyValue, &found))
         return false;
 
     res.setBoolean(found);
     return true;
 }
 
--- a/js/src/jit/CacheIR.cpp
+++ b/js/src/jit/CacheIR.cpp
@@ -2015,16 +2015,131 @@ InIRGenerator::trackNotAttached()
         RootedValue objV(cx_, ObjectValue(*obj_));
         sp.valueProperty(guard, "base", objV);
         sp.valueProperty(guard, "property", key_);
         sp.endCache(guard);
     }
 #endif
 }
 
+HasOwnIRGenerator::HasOwnIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc,
+                                     ICState::Mode mode, HandleValue key, HandleValue value)
+  : IRGenerator(cx, script, pc, CacheKind::HasOwn, mode),
+    key_(key), val_(value)
+{ }
+
+
+bool
+HasOwnIRGenerator::tryAttachNativeHasOwn(HandleId key, ValOperandId keyId,
+                                         HandleObject obj, ObjOperandId objId)
+{
+    PropertyResult prop;
+    if (!LookupOwnPropertyPure(cx_, obj, key, &prop))
+        return false;
+
+    if (!prop.isNativeProperty())
+        return false;
+
+    Maybe<ObjOperandId> holderId;
+    emitIdGuard(keyId, key);
+    EmitReadSlotGuard(writer, obj, obj, prop.shape(), objId, &holderId);
+    writer.loadBooleanResult(true);
+    writer.returnFromIC();
+
+    trackAttached("NativeHasOwn");
+    return true;
+}
+
+bool
+HasOwnIRGenerator::tryAttachNativeHasOwnDoesNotExist(HandleId key, ValOperandId keyId,
+                                                     HandleObject obj, ObjOperandId objId)
+{
+    if (!CheckHasNoSuchOwnProperty(cx_, obj, key))
+        return false;
+
+    Maybe<ObjOperandId> expandoId;
+    emitIdGuard(keyId, key);
+    TestMatchingReceiver(writer, obj, nullptr, objId, &expandoId);
+    writer.loadBooleanResult(false);
+    writer.returnFromIC();
+
+    trackAttached("NativeHasOwnDoesNotExist");
+    return true;
+}
+
+bool
+HasOwnIRGenerator::tryAttachStub()
+{
+    MOZ_ASSERT(cacheKind_ == CacheKind::HasOwn);
+
+    AutoAssertNoPendingException aanpe(cx_);
+
+    ValOperandId keyId(writer.setInputOperandId(0));
+    ValOperandId valId(writer.setInputOperandId(1));
+
+    if (!val_.isObject()) {
+        trackNotAttached();
+        return false;
+    }
+    RootedObject obj(cx_, &val_.toObject());
+
+    ObjOperandId objId = writer.guardIsObject(valId);
+
+    RootedId id(cx_);
+    bool nameOrSymbol;
+    if (!ValueToNameOrSymbolId(cx_, key_, &id, &nameOrSymbol)) {
+        cx_->clearPendingException();
+        return false;
+    }
+
+    if (nameOrSymbol) {
+        if (tryAttachNativeHasOwn(id, keyId, obj, objId))
+            return true;
+        if (tryAttachNativeHasOwnDoesNotExist(id, keyId, obj, objId))
+            return true;
+
+        trackNotAttached();
+        return false;
+    }
+
+    trackNotAttached();
+    return false;
+}
+
+void
+HasOwnIRGenerator::trackAttached(const char* name)
+{
+#ifdef JS_JITSPEW
+    CacheIRSpewer& sp = GetCacheIRSpewerSingleton();
+    if (sp.enabled()) {
+        LockGuard<Mutex> guard(sp.lock());
+        sp.beginCache(guard, *this);
+        sp.valueProperty(guard, "base", val_);
+        sp.valueProperty(guard, "property", key_);
+        sp.attached(guard, name);
+        sp.endCache(guard);
+    }
+#endif
+}
+
+void
+HasOwnIRGenerator::trackNotAttached()
+{
+#ifdef JS_JITSPEW
+    CacheIRSpewer& sp = GetCacheIRSpewerSingleton();
+    if (sp.enabled()) {
+        LockGuard<Mutex> guard(sp.lock());
+        sp.beginCache(guard, *this);
+        sp.valueProperty(guard, "base", val_);
+        sp.valueProperty(guard, "property", key_);
+        sp.endCache(guard);
+    }
+#endif
+}
+
 bool
 IRGenerator::maybeGuardInt32Index(const Value& index, ValOperandId indexId,
                                   uint32_t* int32Index, Int32OperandId* int32IndexId)
 {
     if (index.isNumber()) {
         int32_t indexSigned;
         if (index.isInt32()) {
             indexSigned = index.toInt32();
--- a/js/src/jit/CacheIR.h
+++ b/js/src/jit/CacheIR.h
@@ -134,17 +134,18 @@ class TypedOperandId : public OperandId
 };
 
 #define CACHE_IR_KINDS(_)   \
     _(GetProp)              \
     _(GetElem)              \
     _(GetName)              \
     _(SetProp)              \
     _(SetElem)              \
-    _(In)
+    _(In)                   \
+    _(HasOwn)
 
 enum class CacheKind : uint8_t
 {
 #define DEFINE_KIND(kind) kind,
     CACHE_IR_KINDS(DEFINE_KIND)
 #undef DEFINE_KIND
 };
 
@@ -1248,12 +1249,33 @@ class MOZ_RAII InIRGenerator : public IR
 
   public:
     InIRGenerator(JSContext* cx, HandleScript, jsbytecode* pc, ICState::Mode mode, HandleValue key,
                   HandleObject obj);
 
     bool tryAttachStub();
 };
 
+// HasOwnIRGenerator generates CacheIR for a HasOwn IC.
+class MOZ_RAII HasOwnIRGenerator : public IRGenerator
+{
+    HandleValue key_;
+    HandleValue val_;
+
+    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();
+
+  public:
+    HasOwnIRGenerator(JSContext* cx, HandleScript, jsbytecode* pc, ICState::Mode mode, HandleValue key,
+                      HandleValue value);
+
+    bool tryAttachStub();
+};
+
 } // namespace jit
 } // namespace js
 
 #endif /* jit_CacheIR_h */
--- a/js/src/jit/SharedIC.cpp
+++ b/js/src/jit/SharedIC.cpp
@@ -1914,44 +1914,56 @@ StripPreliminaryObjectStubs(JSContext* c
         else if (iter->isCacheIR_Monitored() && iter->toCacheIR_Monitored()->hasPreliminaryObject())
             iter.unlink(cx);
         else if (iter->isCacheIR_Updated() && iter->toCacheIR_Updated()->hasPreliminaryObject())
             iter.unlink(cx);
     }
 }
 
 bool
+CheckHasNoSuchOwnProperty(JSContext* cx, JSObject* obj, jsid id)
+{
+    if (obj->isNative()) {
+        // Don't handle proto chains with resolve hooks.
+        if (ClassMayResolveId(cx->names(), obj->getClass(), id, obj))
+            return false;
+        if (obj->as<NativeObject>().contains(cx, id))
+            return false;
+        if (obj->getClass()->getGetProperty())
+            return false;
+    } else if (obj->is<UnboxedPlainObject>()) {
+        if (obj->as<UnboxedPlainObject>().containsUnboxedOrExpandoProperty(cx, id))
+            return false;
+    } else if (obj->is<UnboxedArrayObject>()) {
+        if (JSID_IS_ATOM(id, cx->names().length))
+            return false;
+    } else if (obj->is<TypedObject>()) {
+        if (obj->as<TypedObject>().typeDescr().hasProperty(cx->names(), id))
+            return false;
+    } else {
+        return false;
+    }
+
+    return true;
+}
+
+bool
 CheckHasNoSuchProperty(JSContext* cx, JSObject* obj, jsid id,
                        JSObject** lastProto, size_t* protoChainDepthOut)
 {
     size_t depth = 0;
     JSObject* curObj = obj;
     while (curObj) {
-        if (curObj->isNative()) {
-            // Don't handle proto chains with resolve hooks.
-            if (ClassMayResolveId(cx->names(), curObj->getClass(), id, curObj))
-                return false;
-            if (curObj->as<NativeObject>().contains(cx, id))
-                return false;
-            if (curObj->getClass()->getGetProperty())
-                return false;
-        } else if (curObj != obj) {
-            // Non-native objects are only handled as the original receiver.
+        if (!CheckHasNoSuchOwnProperty(cx, curObj, id))
             return false;
-        } else if (curObj->is<UnboxedPlainObject>()) {
-            if (curObj->as<UnboxedPlainObject>().containsUnboxedOrExpandoProperty(cx, id))
-                return false;
-        } else if (curObj->is<UnboxedArrayObject>()) {
-            if (JSID_IS_ATOM(id, cx->names().length))
+
+        if (!curObj->isNative()) {
+            // Non-native objects are only handled as the original receiver.
+            if (curObj != obj)
                 return false;
-        } else if (curObj->is<TypedObject>()) {
-            if (curObj->as<TypedObject>().typeDescr().hasProperty(cx->names(), id))
-                return false;
-        } else {
-            return false;
         }
 
         JSObject* proto = curObj->staticPrototype();
         if (!proto)
             break;
 
         curObj = proto;
         depth++;
--- a/js/src/jit/SharedIC.h
+++ b/js/src/jit/SharedIC.h
@@ -2294,16 +2294,19 @@ GetTypedThingLayout(const Class* clasp)
 
 bool
 IsPreliminaryObject(JSObject* obj);
 
 void
 StripPreliminaryObjectStubs(JSContext* cx, ICFallbackStub* stub);
 
 MOZ_MUST_USE bool
+CheckHasNoSuchOwnProperty(JSContext* cx, JSObject* obj, jsid id);
+
+MOZ_MUST_USE bool
 CheckHasNoSuchProperty(JSContext* cx, JSObject* obj, jsid id,
                        JSObject** lastProto = nullptr, size_t* protoChainDepthOut = nullptr);
 
 void
 CheckForTypedObjectWithDetachedStorage(JSContext* cx, MacroAssembler& masm, Label* failure);
 
 void
 LoadTypedThingData(MacroAssembler& masm, TypedThingLayout layout, Register obj, Register result);