Bug 1248412 - inlineIsTypedArrayHelper: Check for TypedArray and Proxy classes when we allow wrapped TypedArray. r=Waldo
authorNicolas B. Pierron <nicolas.b.pierron@mozilla.com>
Mon, 29 Feb 2016 13:20:37 +0000
changeset 322422 1da938156283fc4fec76261e41efbd5abd894e24
parent 322421 460195af91020133b4d773ed044665f1df8cdfd2
child 322423 8a0a84198d1c80a0305be9f4b33c99ff7880ef34
push id5913
push userjlund@mozilla.com
push dateMon, 25 Apr 2016 16:57:49 +0000
treeherdermozilla-beta@dcaf0a6fa115 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersWaldo
bugs1248412
milestone47.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 1248412 - inlineIsTypedArrayHelper: Check for TypedArray and Proxy classes when we allow wrapped TypedArray. r=Waldo
js/src/jit-test/tests/self-hosting/is-possibly-wrapped-typed-array.js
js/src/jit/MCallOptimize.cpp
js/src/vm/ProxyObject.h
js/src/vm/TypeInference.cpp
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/self-hosting/is-possibly-wrapped-typed-array.js
@@ -0,0 +1,151 @@
+var IsPossiblyWrappedTypedArray = getSelfHostedValue("IsPossiblyWrappedTypedArray");
+
+var declareSamples = `
+    var allTypedArraySamples = [
+        { value: new Int8Array(1), expected: true },
+        { value: new Uint8Array(1), expected: true },
+        { value: new Int16Array(1), expected: true },
+        { value: new Uint16Array(1), expected: true },
+        { value: new Int32Array(1), expected: true },
+        { value: new Uint32Array(1), expected: true },
+        { value: new Float32Array(1), expected: true },
+        { value: new Float64Array(1), expected: true },
+        { value: new Uint8ClampedArray(1), expected: true }
+    ];
+
+    var allObjectSamples = [
+        { value: new Array(1), expected: false },
+        { value: {}, expected: false },
+        { value: { length: 1 }, expected: false }
+    ];
+
+    var allNonObjectSamples = [
+        { value: "a", expected: false },
+        { value: 1.2, expected: false },
+        { value: true, expected: false },
+        { value: Symbol("a"), expected: false }
+    ];
+`;
+
+// Create a new global to wrap with cross compartment wrappers.
+var g = newGlobal();
+evaluate(declareSamples)
+g.evaluate(declareSamples);
+
+var assertCode = `function (value, expected) {
+    assertEq(IsPossiblyWrappedTypedArray(value), expected);
+    return inIon();
+}`;
+
+function checkSamples(samples) {
+    // Create the assert function anew every run so as not to share JIT code,
+    // type information, etc.
+    var assert = new Function(`return (${assertCode})`)();
+
+    // Prevent Ion compilation of this function so that we don't freeze the
+    // sample array's type.  If we did, IonBuilder's typed-array-length inlining
+    // would always see a Mixed state, preventing IsPossiblyWrappedTypedArray
+    // from being inlined.
+    with ({}) {};
+
+    do {
+        // spinInJit is used to ensure that we at least test all elements in the
+        // sample vector while running a compiled version of the assert
+        // function.
+        var spinInJit = true;
+        for (var i = 0; i < samples.length; i++) {
+            var e = samples[i];
+            if (!e) continue;
+            spinInJit = spinInJit && assert(e.value, e.expected);
+        }
+    } while(!spinInJit);
+}
+
+// Check a mix of samples from each type.
+function test(a, b, c, d, e, f) {
+    var samples = [
+        a == -1 ? null : allTypedArraySamples[a],
+        b == -1 ? null : allObjectSamples[b],
+        c == -1 ? null : allNonObjectSamples[c],
+        d == -1 ? null : g.allTypedArraySamples[d],
+        e == -1 ? null : g.allObjectSamples[e],
+        f == -1 ? null : g.allNonObjectSamples[f]
+    ];
+
+    checkSamples(samples);
+}
+
+// Check all samples.
+checkSamples(allTypedArraySamples);
+checkSamples(allObjectSamples);
+checkSamples(allNonObjectSamples);
+checkSamples(g.allTypedArraySamples);
+checkSamples(g.allObjectSamples);
+checkSamples(g.allNonObjectSamples);
+
+// Check combinations mixing 2 elements from different types.
+test(-1, -1, -1, -1,  0,  0);
+test(-1, -1, -1,  0, -1,  0);
+test(-1, -1, -1,  0,  0, -1);
+test(-1, -1,  0, -1, -1,  0);
+test(-1, -1,  0, -1,  0, -1);
+test(-1, -1,  0,  0, -1, -1);
+test(-1,  0, -1, -1, -1,  0);
+test(-1,  0, -1, -1,  0, -1);
+test(-1,  0, -1,  0, -1, -1);
+test(-1,  0,  0, -1, -1, -1);
+test( 0, -1, -1, -1, -1,  0);
+test( 0, -1, -1, -1,  0, -1);
+test( 0, -1, -1,  0, -1, -1);
+test( 0, -1,  0, -1, -1, -1);
+test( 0,  0, -1, -1, -1, -1);
+
+// Check combinations mixing 3 elements from different types.
+test(-1, -1, -1,  0,  0,  0);
+test(-1, -1,  0, -1,  0,  0);
+test(-1, -1,  0,  0, -1,  0);
+test(-1, -1,  0,  0,  0, -1);
+test(-1,  0, -1, -1,  0,  0);
+test(-1,  0, -1,  0, -1,  0);
+test(-1,  0, -1,  0,  0, -1);
+test(-1,  0,  0, -1, -1,  0);
+test(-1,  0,  0, -1,  0, -1);
+test(-1,  0,  0,  0, -1, -1);
+test( 0, -1, -1, -1,  0,  0);
+test( 0, -1, -1,  0, -1,  0);
+test( 0, -1, -1,  0,  0, -1);
+test( 0, -1,  0, -1, -1,  0);
+test( 0, -1,  0, -1,  0, -1);
+test( 0, -1,  0,  0, -1, -1);
+test( 0,  0, -1, -1, -1,  0);
+test( 0,  0, -1, -1,  0, -1);
+test( 0,  0, -1,  0, -1, -1);
+test( 0,  0,  0, -1, -1, -1);
+
+// Check combinations mixing 4 elements from different types.
+test(-1, -1,  0,  0,  0,  0);
+test(-1,  0, -1,  0,  0,  0);
+test(-1,  0,  0, -1,  0,  0);
+test(-1,  0,  0,  0, -1,  0);
+test(-1,  0,  0,  0,  0, -1);
+test( 0, -1, -1,  0,  0,  0);
+test( 0, -1,  0, -1,  0,  0);
+test( 0, -1,  0,  0, -1,  0);
+test( 0, -1,  0,  0,  0, -1);
+test( 0,  0, -1, -1,  0,  0);
+test( 0,  0, -1,  0, -1,  0);
+test( 0,  0, -1,  0,  0, -1);
+test( 0,  0,  0, -1, -1,  0);
+test( 0,  0,  0, -1,  0, -1);
+test( 0,  0,  0,  0, -1, -1);
+
+// Check combinations mixing 5 elements from different types.
+test(-1,  0,  0,  0,  0,  0);
+test( 0, -1,  0,  0,  0,  0);
+test( 0,  0, -1,  0,  0,  0);
+test( 0,  0,  0, -1,  0,  0);
+test( 0,  0,  0,  0, -1,  0);
+test( 0,  0,  0,  0,  0, -1);
+
+// Check combinations mixing 6 elements from different types.
+test( 0,  0,  0,  0,  0,  0);
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -12,16 +12,17 @@
 #include "builtin/TypedObject.h"
 #include "jit/BaselineInspector.h"
 #include "jit/InlinableNatives.h"
 #include "jit/IonBuilder.h"
 #include "jit/Lowering.h"
 #include "jit/MIR.h"
 #include "jit/MIRGraph.h"
 #include "vm/ArgumentsObject.h"
+#include "vm/ProxyObject.h"
 
 #include "jsscriptinlines.h"
 
 #include "jit/shared/Lowering-shared-inl.h"
 #include "vm/NativeObject-inl.h"
 #include "vm/StringObject-inl.h"
 #include "vm/UnboxedObject-inl.h"
 
@@ -2205,27 +2206,36 @@ IonBuilder::inlineIsTypedArrayHelper(Cal
 
     TemporaryTypeSet* types = callInfo.getArg(0)->resultTypeSet();
     if (!types)
         return InliningStatus_NotInlined;
 
     bool result = false;
     switch (types->forAllClasses(constraints(), IsTypedArrayClass)) {
       case TemporaryTypeSet::ForAllResult::ALL_FALSE:
-      case TemporaryTypeSet::ForAllResult::EMPTY: {
         // Wrapped typed arrays won't appear to be typed arrays per a
         // |forAllClasses| query.  If wrapped typed arrays are to be considered
         // typed arrays, a negative answer is not conclusive.  Don't inline in
         // that case.
-        if (wrappingBehavior == AllowWrappedTypedArrays)
-            return InliningStatus_NotInlined;
-
+        if (wrappingBehavior == AllowWrappedTypedArrays) {
+            switch (types->forAllClasses(constraints(), IsProxyClass)) {
+              case TemporaryTypeSet::ForAllResult::ALL_FALSE:
+              case TemporaryTypeSet::ForAllResult::EMPTY:
+                break;
+              case TemporaryTypeSet::ForAllResult::ALL_TRUE:
+              case TemporaryTypeSet::ForAllResult::MIXED:
+                return InliningStatus_NotInlined;
+            }
+        }
+
+        MOZ_FALLTHROUGH;
+
+      case TemporaryTypeSet::ForAllResult::EMPTY:
         result = false;
         break;
-      }
 
       case TemporaryTypeSet::ForAllResult::ALL_TRUE:
         result = true;
         break;
 
       case TemporaryTypeSet::ForAllResult::MIXED:
         return InliningStatus_NotInlined;
     }
--- a/js/src/vm/ProxyObject.h
+++ b/js/src/vm/ProxyObject.h
@@ -105,16 +105,22 @@ class ProxyObject : public JSObject
     void nuke(const BaseProxyHandler* handler);
 
     // There is no class_ member to force specialization of JSObject::is<T>().
     // The implementation in JSObject is incorrect for proxies since it doesn't
     // take account of the handler type.
     static const Class proxyClass;
 };
 
+inline bool
+IsProxyClass(const Class* clasp)
+{
+    return clasp->isProxy();
+}
+
 bool IsDerivedProxyObject(const JSObject* obj, const js::BaseProxyHandler* handler);
 
 } // namespace js
 
 template<>
 inline bool
 JSObject::is<js::ProxyObject>() const
 {
--- a/js/src/vm/TypeInference.cpp
+++ b/js/src/vm/TypeInference.cpp
@@ -2302,18 +2302,17 @@ TemporaryTypeSet::forAllClasses(Compiler
         if (!clasp)
             continue;
         if (!getObject(i)->hasStableClassAndProto(constraints))
             return ForAllResult::MIXED;
         if (func(clasp)) {
             true_results = true;
             if (false_results)
                 return ForAllResult::MIXED;
-        }
-        else {
+        } else {
             false_results = true;
             if (true_results)
                 return ForAllResult::MIXED;
         }
     }
 
     MOZ_ASSERT(true_results != false_results);