Bug 887016 - Part 8: Add ObjectHasPrototype. r=nbp
authorTooru Fujisawa <arai_a@mac.com>
Wed, 27 Jan 2016 23:43:04 +0900
changeset 292174 ac74c5fe92172d8dbb363eb713e1512570c51af1
parent 292173 741c4be20f025d1b24b5fcab5b7987bd649bdd0b
child 292175 b5b06959919ad3a0150c7ca1dfe0de7d2d9df7e1
push id74764
push userarai_a@mac.com
push dateThu, 07 Apr 2016 10:49:15 +0000
treeherdermozilla-inbound@4d0f975a2311 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnbp
bugs887016
milestone48.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 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
@@ -100,16 +100,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
@@ -916,16 +916,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"
@@ -259,16 +260,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);
@@ -1607,16 +1610,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),