Bug 887016 - Part 8: Add ObjectHasPrototype. r=nbp
☠☠ backed out by 592fbf849342 ☠ ☠
authorTooru Fujisawa <arai_a@mac.com>
Wed, 27 Jan 2016 23:43:04 +0900
changeset 290626 4be734a1452428151831dbbc6115ac10b20083f7
parent 290625 dee348be668513677d339437e9fcf4d6b5b6ce01
child 290627 537d40121b6d4c80071e02aeaa95712fdfdcb107
push id19656
push usergwagner@mozilla.com
push dateMon, 04 Apr 2016 13:43:23 +0000
treeherderb2g-inbound@e99061fde28a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnbp
bugs887016
milestone48.0a1
Bug 887016 - Part 8: Add ObjectHasPrototype. r=nbp
js/src/jit-test/tests/ion/testObjectHasPrototype.js
js/src/jit/InlinableNatives.h
js/src/jit/IonBuilder.h
js/src/jit/MCallOptimize.cpp
js/src/vm/SelfHosting.cpp
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/testObjectHasPrototype.js
@@ -0,0 +1,63 @@
+setJitCompilerOption("ion.warmup.trigger", 4);
+
+var ObjectHasPrototype = getSelfHostedValue("ObjectHasPrototype");
+
+var StringProto = String.prototype;
+var ObjectProto = Object.prototype;
+
+function testBasic() {
+  var f = function() {
+    assertEq(ObjectHasPrototype(StringProto, ObjectProto), true);
+  };
+  for (var i = 0; i < 40; i++) {
+    f();
+  }
+}
+testBasic();
+
+function testProtoChange(proto) {
+  var f = function(expected) {
+    assertEq(ObjectHasPrototype(StringProto, ObjectProto), expected);
+  };
+  var expected = true;
+  for (var i = 0; i < 120; i++) {
+    f(expected);
+    if (i == 40) {
+      Object.setPrototypeOf(StringProto, proto);
+      expected = false;
+    }
+    if (i == 80) {
+      Object.setPrototypeOf(StringProto, ObjectProto);
+      expected = true;
+    }
+  }
+}
+testProtoChange(null);
+// Different singleton
+testProtoChange(Function.prototype);
+// native non-singleton
+testProtoChange(/a/);
+// non-native non-singleton
+testProtoChange({});
+
+var Int32ArrayProto = Int32Array.prototype;
+var TypedArrayProto = Object.getPrototypeOf(Int32ArrayProto);
+function testProtoProtoChange(proto) {
+  var f = function() {
+    assertEq(ObjectHasPrototype(Int32ArrayProto, TypedArrayProto), true);
+  };
+  for (var i = 0; i < 120; i++) {
+    f();
+    if (i == 40)
+      Object.setPrototypeOf(TypedArrayProto, proto);
+    if (i == 80)
+      Object.setPrototypeOf(TypedArrayProto, Object);
+  }
+}
+testProtoProtoChange(null);
+// Different singleton
+testProtoProtoChange(Function.prototype);
+// native non-singleton
+testProtoProtoChange(/a/);
+// non-native non-singleton
+testProtoProtoChange({});
--- a/js/src/jit/InlinableNatives.h
+++ b/js/src/jit/InlinableNatives.h
@@ -101,16 +101,17 @@
     _(IntrinsicToObject)            \
     _(IntrinsicIsObject)            \
     _(IntrinsicIsWrappedArrayConstructor) \
     _(IntrinsicToInteger)           \
     _(IntrinsicToString)            \
     _(IntrinsicIsConstructing)      \
     _(IntrinsicSubstringKernel)     \
     _(IntrinsicDefineDataProperty)  \
+    _(IntrinsicObjectHasPrototype)  \
                                     \
     _(IntrinsicIsArrayIterator)     \
     _(IntrinsicIsMapIterator)       \
     _(IntrinsicIsStringIterator)    \
     _(IntrinsicIsListIterator)      \
                                     \
     _(IntrinsicGetNextMapEntryForIterator) \
                                     \
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -917,16 +917,17 @@ class IonBuilder
     InliningStatus inlineToString(CallInfo& callInfo);
     InliningStatus inlineDump(CallInfo& callInfo);
     InliningStatus inlineHasClass(CallInfo& callInfo, const Class* clasp,
                                   const Class* clasp2 = nullptr,
                                   const Class* clasp3 = nullptr,
                                   const Class* clasp4 = nullptr);
     InliningStatus inlineIsConstructing(CallInfo& callInfo);
     InliningStatus inlineSubstringKernel(CallInfo& callInfo);
+    InliningStatus inlineObjectHasPrototype(CallInfo& callInfo);
 
     // Testing functions.
     InliningStatus inlineBailout(CallInfo& callInfo);
     InliningStatus inlineAssertFloat32(CallInfo& callInfo);
     InliningStatus inlineAssertRecoveredOnBailout(CallInfo& callInfo);
 
     // Bind function.
     InliningStatus inlineBoundFunction(CallInfo& callInfo, JSFunction* target);
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -1,15 +1,16 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "jsmath.h"
+#include "jsobj.h"
 #include "jsstr.h"
 
 #include "builtin/AtomicsObject.h"
 #include "builtin/SIMD.h"
 #include "builtin/TestingFunctions.h"
 #include "builtin/TypedObject.h"
 #include "jit/BaselineInspector.h"
 #include "jit/InlinableNatives.h"
@@ -261,16 +262,18 @@ IonBuilder::inlineNativeCall(CallInfo& c
       case InlinableNative::IntrinsicIsMapIterator:
         return inlineHasClass(callInfo, &MapIteratorObject::class_);
       case InlinableNative::IntrinsicIsStringIterator:
         return inlineHasClass(callInfo, &StringIteratorObject::class_);
       case InlinableNative::IntrinsicIsListIterator:
         return inlineHasClass(callInfo, &ListIteratorObject::class_);
       case InlinableNative::IntrinsicDefineDataProperty:
         return inlineDefineDataProperty(callInfo);
+      case InlinableNative::IntrinsicObjectHasPrototype:
+        return inlineObjectHasPrototype(callInfo);
 
       // Map intrinsics.
       case InlinableNative::IntrinsicGetNextMapEntryForIterator:
         return inlineGetNextMapEntryForIterator(callInfo);
 
       // ArrayBuffer intrinsics.
       case InlinableNative::IntrinsicArrayBufferByteLength:
         return inlineArrayBufferByteLength(callInfo);
@@ -1609,16 +1612,68 @@ IonBuilder::inlineStringSplit(CallInfo& 
                                           callInfo.getArg(0), templateObjectDef);
     current->add(ins);
     current->push(ins);
 
     return InliningStatus_Inlined;
 }
 
 IonBuilder::InliningStatus
+IonBuilder::inlineObjectHasPrototype(CallInfo& callInfo)
+{
+    if (callInfo.argc() != 2 || callInfo.constructing()) {
+        trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
+        return InliningStatus_NotInlined;
+    }
+
+    MDefinition* objArg = callInfo.getArg(0);
+    MDefinition* protoArg = callInfo.getArg(1);
+
+    if (objArg->type() != MIRType_Object)
+        return InliningStatus_NotInlined;
+    if (protoArg->type() != MIRType_Object)
+        return InliningStatus_NotInlined;
+
+    // Inline only when both obj and proto are singleton objects and
+    // obj does not have uncacheable proto and obj.__proto__ is proto.
+    TemporaryTypeSet* objTypes = objArg->resultTypeSet();
+    if (!objTypes || objTypes->unknownObject() || objTypes->getObjectCount() != 1)
+        return InliningStatus_NotInlined;
+
+    TypeSet::ObjectKey* objKey = objTypes->getObject(0);
+    if (!objKey || !objKey->hasStableClassAndProto(constraints()))
+        return InliningStatus_NotInlined;
+    if (!objKey->isSingleton() || !objKey->singleton()->is<NativeObject>())
+        return InliningStatus_NotInlined;
+
+    JSObject* obj = &objKey->singleton()->as<NativeObject>();
+    if (obj->hasUncacheableProto())
+        return InliningStatus_NotInlined;
+
+    JSObject* actualProto = checkNurseryObject(objKey->proto().toObjectOrNull());
+    if (actualProto == nullptr)
+        return InliningStatus_NotInlined;
+
+    TemporaryTypeSet* protoTypes = protoArg->resultTypeSet();
+    if (!protoTypes || protoTypes->unknownObject() || protoTypes->getObjectCount() != 1)
+        return InliningStatus_NotInlined;
+
+    TypeSet::ObjectKey* protoKey = protoTypes->getObject(0);
+    if (!protoKey || !protoKey->hasStableClassAndProto(constraints()))
+        return InliningStatus_NotInlined;
+    if (!protoKey->isSingleton() || !protoKey->singleton()->is<NativeObject>())
+        return InliningStatus_NotInlined;
+
+    JSObject* proto = &protoKey->singleton()->as<NativeObject>();
+    pushConstant(BooleanValue(proto == actualProto));
+    callInfo.setImplicitlyUsedUnchecked();
+    return InliningStatus_Inlined;
+}
+
+IonBuilder::InliningStatus
 IonBuilder::inlineStrCharCodeAt(CallInfo& callInfo)
 {
     if (callInfo.argc() != 1 || callInfo.constructing()) {
         trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
         return InliningStatus_NotInlined;
     }
 
     if (getInlineReturnType() != MIRType_Int32)
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -581,16 +581,32 @@ intrinsic_DefineDataProperty(JSContext* 
     if (!DefineProperty(cx, obj, id, desc))
         return false;
 
     args.rval().setUndefined();
     return true;
 }
 
 static bool
+intrinsic_ObjectHasPrototype(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    MOZ_ASSERT(args.length() == 2);
+    RootedObject obj(cx, &args[0].toObject());
+    RootedObject proto(cx, &args[1].toObject());
+
+    RootedObject actualProto(cx);
+    if (!GetPrototype(cx, obj, &actualProto))
+        return false;
+
+    args.rval().setBoolean(actualProto == proto);
+    return true;
+}
+
+static bool
 intrinsic_UnsafeSetReservedSlot(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     MOZ_ASSERT(args.length() == 3);
     MOZ_ASSERT(args[0].isObject());
     MOZ_ASSERT(args[1].isInt32());
 
     args[0].toObject().as<NativeObject>().setReservedSlot(args[1].toPrivateUint32(), args[2]);
@@ -2170,16 +2186,18 @@ static const JSFunctionSpec intrinsic_fu
     JS_FN("AddContentTelemetry",     intrinsic_AddContentTelemetry,     2,0),
 
     JS_INLINABLE_FN("_IsConstructing", intrinsic_IsConstructing,        0,0,
                     IntrinsicIsConstructing),
     JS_INLINABLE_FN("SubstringKernel", intrinsic_SubstringKernel,       3,0,
                     IntrinsicSubstringKernel),
     JS_INLINABLE_FN("_DefineDataProperty",              intrinsic_DefineDataProperty,      4,0,
                     IntrinsicDefineDataProperty),
+    JS_INLINABLE_FN("ObjectHasPrototype",               intrinsic_ObjectHasPrototype,      2,0,
+                    IntrinsicObjectHasPrototype),
     JS_INLINABLE_FN("UnsafeSetReservedSlot",            intrinsic_UnsafeSetReservedSlot,   3,0,
                     IntrinsicUnsafeSetReservedSlot),
     JS_INLINABLE_FN("UnsafeGetReservedSlot",            intrinsic_UnsafeGetReservedSlot,   2,0,
                     IntrinsicUnsafeGetReservedSlot),
     JS_INLINABLE_FN("UnsafeGetObjectFromReservedSlot",  intrinsic_UnsafeGetObjectFromReservedSlot, 2,0,
                     IntrinsicUnsafeGetObjectFromReservedSlot),
     JS_INLINABLE_FN("UnsafeGetInt32FromReservedSlot",   intrinsic_UnsafeGetInt32FromReservedSlot,  2,0,
                     IntrinsicUnsafeGetInt32FromReservedSlot),