Bug 1265748 - Enable non-Object path in IonBuilder::inlineIsCallable. r=shu
authorTooru Fujisawa <arai_a@mac.com>
Thu, 21 Apr 2016 08:45:40 +0900
changeset 294192 0b609cab107795bd79520e1674ddfdee80e46a8a
parent 294191 e92704fae381eb6b3363a87372663c5a6466e6fd
child 294193 4226c06dcf1ffa0d965bf6f8c77adf992a8b7d6b
push id30201
push userkwierso@gmail.com
push dateThu, 21 Apr 2016 21:41:56 +0000
treeherdermozilla-central@0891f0fa044c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersshu
bugs1265748
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 1265748 - Enable non-Object path in IonBuilder::inlineIsCallable. r=shu
js/src/jit-test/tests/ion/testIsCallable.js
js/src/jit/IonTypes.h
js/src/jit/MCallOptimize.cpp
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/testIsCallable.js
@@ -0,0 +1,134 @@
+var IsCallable = getSelfHostedValue("IsCallable");
+
+setJitCompilerOption("ion.warmup.trigger", 50);
+
+function testSinglePrimitive() {
+  var f1 = function() { assertEq(IsCallable(undefined), false); };
+  do { f1(); } while (!inIon());
+
+  var f2 = function() { assertEq(IsCallable(null), false); };
+  do { f2(); } while (!inIon());
+
+  var f3 = function() { assertEq(IsCallable(true), false); };
+  do { f3(); } while (!inIon());
+
+  var f4 = function() { assertEq(IsCallable(1), false); };
+  do { f4(); } while (!inIon());
+
+  var f5 = function() { assertEq(IsCallable(1.2), false); };
+  do { f5(); } while (!inIon());
+
+  var f6 = function() { assertEq(IsCallable("foo"), false); };
+  do { f6(); } while (!inIon());
+
+  var f7 = function() { assertEq(IsCallable(Symbol.iterator), false); };
+  do { f7(); } while (!inIon());
+}
+testSinglePrimitive();
+
+function testMixedPrimitive() {
+  var list = [
+    undefined,
+    null,
+    true,
+    1,
+    1.2,
+    "foo",
+    Symbol.iterator,
+  ];
+
+  var f1 = function() {
+    for (let x of list) {
+      assertEq(IsCallable(x), false);
+    }
+  };
+  do { f1(); } while (!inIon());
+}
+testMixedPrimitive();
+
+function testSingleObject() {
+  var obj = [];
+  var arr = [];
+
+  var f1 = function() { assertEq(IsCallable(obj), false); };
+  do { f1(); } while (!inIon());
+
+  var f2 = function() { assertEq(IsCallable(arr), false); };
+  do { f2(); } while (!inIon());
+}
+testSingleObject();
+
+function testMixedPrimitiveAndObject() {
+  var list = [
+    undefined,
+    null,
+    true,
+    1,
+    1.2,
+    "foo",
+    Symbol.iterator,
+
+    {},
+    [],
+  ];
+
+  var f1 = function() {
+    for (let x of list) {
+      assertEq(IsCallable(x), false);
+    }
+  };
+  do { f1(); } while (!inIon());
+}
+testMixedPrimitiveAndObject();
+
+function testFunction() {
+  var f1 = function() { assertEq(IsCallable(Function), true); };
+  do { f1(); } while (!inIon());
+
+  var f2 = function() { assertEq(IsCallable(parseInt), true); };
+  do { f2(); } while (!inIon());
+}
+testFunction();
+
+function testProxy() {
+  var p1 = new Proxy({}, {});
+  var f1 = function() { assertEq(IsCallable(p1), false); };
+  do { f1(); } while (!inIon());
+
+  var p2 = new Proxy(function() {}, {});
+  var f2 = function() { assertEq(IsCallable(p2), true); };
+  do { f2(); } while (!inIon());
+}
+testProxy();
+
+function testMixed() {
+  var p1 = new Proxy({}, {});
+  var p2 = new Proxy(function() {}, {});
+
+  var list = [
+    [undefined, false],
+    [null, false],
+    [true, false],
+    [1, false],
+    [1.2, false],
+    ["foo", false],
+    [Symbol.iterator, false],
+
+    [{}, false],
+    [[], false],
+
+    [Function, true],
+    [parseInt, true],
+
+    [p1, false],
+    [p2, true],
+  ];
+
+  var f1 = function() {
+    for (let [x, expected] of list) {
+      assertEq(IsCallable(x), expected);
+    }
+  };
+  do { f1(); } while (!inIon());
+}
+testMixed();
--- a/js/src/jit/IonTypes.h
+++ b/js/src/jit/IonTypes.h
@@ -392,24 +392,27 @@ enum MIRType
 {
     MIRType_Undefined,
     MIRType_Null,
     MIRType_Boolean,
     MIRType_Int32,
     MIRType_Int64,
     MIRType_Double,
     MIRType_Float32,
+    // Types above have trivial conversion to a number.
     MIRType_String,
     MIRType_Symbol,
+    // Types above are primitive (including undefined and null).
     MIRType_Object,
     MIRType_MagicOptimizedArguments,   // JS_OPTIMIZED_ARGUMENTS magic value.
     MIRType_MagicOptimizedOut,         // JS_OPTIMIZED_OUT magic value.
     MIRType_MagicHole,                 // JS_ELEMENTS_HOLE magic value.
     MIRType_MagicIsConstructing,       // JS_IS_CONSTRUCTING magic value.
     MIRType_MagicUninitializedLexical, // JS_UNINITIALIZED_LEXICAL magic value.
+    // Types above are specialized.
     MIRType_Value,
     MIRType_SinCosDouble,              // Optimizing a sin/cos to sincos.
     MIRType_ObjectOrNull,
     MIRType_None,                      // Invalid, used as a placeholder.
     MIRType_Slots,                     // A slots vector
     MIRType_Elements,                  // An elements vector
     MIRType_Pointer,                   // An opaque pointer that receives no special treatment
     MIRType_Shape,                     // A Shape pointer.
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -2556,45 +2556,49 @@ IonBuilder::inlineIsCallable(CallInfo& c
 {
     if (callInfo.argc() != 1 || callInfo.constructing()) {
         trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
         return InliningStatus_NotInlined;
     }
 
     if (getInlineReturnType() != MIRType_Boolean)
         return InliningStatus_NotInlined;
-    if (callInfo.getArg(0)->type() != MIRType_Object)
+
+    MDefinition* arg = callInfo.getArg(0);
+    // Do not inline if the type of arg is neither primitive nor object.
+    if (arg->type() > MIRType_Object)
         return InliningStatus_NotInlined;
 
     // Try inlining with constant true/false: only objects may be callable at
     // all, and if we know the class check if it is callable.
     bool isCallableKnown = false;
     bool isCallableConstant;
-    if (callInfo.getArg(0)->type() != MIRType_Object) {
+    if (arg->type() != MIRType_Object) {
+        // Primitive (including undefined and null).
         isCallableKnown = true;
         isCallableConstant = false;
     } else {
-        TemporaryTypeSet* types = callInfo.getArg(0)->resultTypeSet();
+        TemporaryTypeSet* types = arg->resultTypeSet();
         const Class* clasp = types ? types->getKnownClass(constraints()) : nullptr;
         if (clasp && !clasp->isProxy()) {
             isCallableKnown = true;
             isCallableConstant = clasp->nonProxyCallable();
         }
     }
 
     callInfo.setImplicitlyUsedUnchecked();
 
     if (isCallableKnown) {
         MConstant* constant = MConstant::New(alloc(), BooleanValue(isCallableConstant));
         current->add(constant);
         current->push(constant);
         return InliningStatus_Inlined;
     }
 
-    MIsCallable* isCallable = MIsCallable::New(alloc(), callInfo.getArg(0));
+    MIsCallable* isCallable = MIsCallable::New(alloc(), arg);
     current->add(isCallable);
     current->push(isCallable);
 
     return InliningStatus_Inlined;
 }
 
 IonBuilder::InliningStatus
 IonBuilder::inlineIsConstructor(CallInfo& callInfo)