Bug 1419372 - Optimize in Operator for typed arrays. r=jandem
authorMatthew Gaudet <mgaudet@mozilla.com>
Thu, 30 Nov 2017 11:21:36 -0500
changeset 394665 87fdf9ff3843
parent 394664 f28a10b116e0
child 394666 4418485f0512
push id97962
push userryanvm@gmail.com
push dateMon, 04 Dec 2017 16:50:39 +0000
treeherdermozilla-inbound@87fdf9ff3843 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs1419372
milestone59.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 1419372 - Optimize in Operator for typed arrays. r=jandem Add inline cache entry for TypedArray exists checks. Requires adding a new CacheIR opcode LoadTypedElementExistsResult, as well as a macro assembler implementation for that opcode.
js/src/jit-test/tests/typedarray/typed-array-inline-cache.js
js/src/jit/CacheIR.cpp
js/src/jit/CacheIR.h
js/src/jit/CacheIRCompiler.cpp
js/src/jit/CacheIRCompiler.h
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/typedarray/typed-array-inline-cache.js
@@ -0,0 +1,53 @@
+// ECMA262 9.4.5.2 [[HasProperty]]
+function check_in(x, a) {
+    return (x in a);
+}
+
+function check_has_own(x, a) {
+    return a.hasOwnProperty(x);
+}
+
+//make sure baseline gets compiled
+function warmup(a) {
+    for (var i = 0; i < 1001; i++) {
+        check_in(i, a);
+        check_has_own(i, a);
+    }
+}
+
+function check_assertions(a) {
+    assertEq(check_in(1, a),      true);
+    assertEq(check_in("-0",a),    false); // -0 access
+    assertEq(check_in(-10,a),     false); // Negative access
+    assertEq(check_in(1012,a),    false); // OOB access
+
+
+    assertEq(check_has_own(1, a),      true);
+    assertEq(check_has_own("-0",a),    false); // -0 access
+    assertEq(check_has_own(-10,a),     false); // Negative access
+    assertEq(check_has_own(1012,a),    false); // OOB access
+}
+
+function test_with_no_protochain(a) {
+    var a = new Int32Array(1000).fill(1);
+    warmup(a);
+    check_assertions(a);
+}
+
+// Attempting to validate against this comment:
+// https://bugzilla.mozilla.org/show_bug.cgi?id=1419372#c3
+//
+// "Out of bounds "in" or "hasOwnProperty" should always
+// return false, and not consider the prototype chain at all"
+function test_with_protochain(a) {
+    var a = new Int32Array(1000).fill(1);
+    // try to force the behaviour of 9.4.5.2
+    a[1012] = "1012";
+    a["-0"]   = "-0";
+    a[-10]  = "-10";
+    warmup(a);
+    check_assertions(a);
+}
+
+test_with_no_protochain();
+test_with_protochain();
--- a/js/src/jit/CacheIR.cpp
+++ b/js/src/jit/CacheIR.cpp
@@ -2560,16 +2560,39 @@ HasPropIRGenerator::tryAttachUnboxedExpa
     writer.loadBooleanResult(true);
     writer.returnFromIC();
 
     trackAttached("UnboxedExpandoHasProp");
     return true;
 }
 
 bool
+HasPropIRGenerator::tryAttachTypedArray(HandleObject obj, ObjOperandId objId,
+                                        uint32_t index, Int32OperandId indexId)
+{
+    if (!obj->is<TypedArrayObject>() && !IsPrimitiveArrayTypedObject(obj))
+        return false;
+
+    // Don't attach typed object stubs if the underlying storage could be
+    // detached, as the stub will always bail out.
+    if (IsPrimitiveArrayTypedObject(obj) && cx_->compartment()->detachedTypedObjects)
+        return false;
+
+    TypedThingLayout layout = GetTypedThingLayout(obj->getClass());
+    writer.guardShape(objId, obj->as<ShapedObject>().shape());
+
+    writer.loadTypedElementExistsResult(objId, indexId, layout);
+
+    writer.returnFromIC();
+
+    trackAttached("TypedArrayObject");
+    return true;
+}
+
+bool
 HasPropIRGenerator::tryAttachTypedObject(JSObject* obj, ObjOperandId objId,
                                          jsid key, ValOperandId keyId)
 {
     if (!obj->is<TypedObject>())
         return false;
 
     if (!obj->as<TypedObject>().typeDescr().hasProperty(cx_->names(), key))
         return false;
@@ -2688,16 +2711,18 @@ HasPropIRGenerator::tryAttachStub()
 
     uint32_t index;
     Int32OperandId indexId;
     if (maybeGuardInt32Index(idVal_, keyId, &index, &indexId)) {
         if (tryAttachDense(obj, objId, index, indexId))
             return true;
         if (tryAttachDenseHole(obj, objId, index, indexId))
             return true;
+        if (tryAttachTypedArray(obj, objId, index, indexId))
+            return true;
 
         trackNotAttached();
         return false;
     }
 
     trackNotAttached();
     return false;
 }
--- a/js/src/jit/CacheIR.h
+++ b/js/src/jit/CacheIR.h
@@ -234,16 +234,17 @@ extern const char* CacheKindNames[];
     /* The *Result ops load a value into the cache's result register. */ \
     _(LoadFixedSlotResult)                \
     _(LoadDynamicSlotResult)              \
     _(LoadUnboxedPropertyResult)          \
     _(LoadTypedObjectResult)              \
     _(LoadDenseElementResult)             \
     _(LoadDenseElementHoleResult)         \
     _(LoadDenseElementExistsResult)       \
+    _(LoadTypedElementExistsResult)       \
     _(LoadDenseElementHoleExistsResult)   \
     _(LoadTypedElementResult)             \
     _(LoadInt32ArrayLengthResult)         \
     _(LoadArgumentsObjectArgResult)       \
     _(LoadArgumentsObjectLengthResult)    \
     _(LoadFunctionLengthResult)           \
     _(LoadStringCharResult)               \
     _(LoadStringLengthResult)             \
@@ -925,16 +926,21 @@ class MOZ_RAII CacheIRWriter : public JS
     void loadDenseElementHoleResult(ObjOperandId obj, Int32OperandId index) {
         writeOpWithOperandId(CacheOp::LoadDenseElementHoleResult, obj);
         writeOperandId(index);
     }
     void loadDenseElementExistsResult(ObjOperandId obj, Int32OperandId index) {
         writeOpWithOperandId(CacheOp::LoadDenseElementExistsResult, obj);
         writeOperandId(index);
     }
+    void loadTypedElementExistsResult(ObjOperandId obj, Int32OperandId index, TypedThingLayout layout) {
+        writeOpWithOperandId(CacheOp::LoadTypedElementExistsResult, obj);
+        writeOperandId(index);
+        buffer_.writeByte(uint32_t(layout));
+    }
     void loadDenseElementHoleExistsResult(ObjOperandId obj, Int32OperandId index) {
         writeOpWithOperandId(CacheOp::LoadDenseElementHoleExistsResult, obj);
         writeOperandId(index);
     }
     void loadTypedElementResult(ObjOperandId obj, Int32OperandId index, TypedThingLayout layout,
                                 Scalar::Type elementType) {
         writeOpWithOperandId(CacheOp::LoadTypedElementResult, obj);
         writeOperandId(index);
@@ -1434,16 +1440,18 @@ class MOZ_RAII HasPropIRGenerator : publ
 {
     HandleValue val_;
     HandleValue idVal_;
 
     bool tryAttachDense(HandleObject obj, ObjOperandId objId,
                         uint32_t index, Int32OperandId indexId);
     bool tryAttachDenseHole(HandleObject obj, ObjOperandId objId,
                             uint32_t index, Int32OperandId indexId);
+    bool tryAttachTypedArray(HandleObject obj, ObjOperandId objId,
+                             uint32_t index, Int32OperandId indexId);
     bool tryAttachNamedProp(HandleObject obj, ObjOperandId objId,
                             HandleId key, ValOperandId keyId);
     bool tryAttachMegamorphic(ObjOperandId objId, ValOperandId keyId);
     bool tryAttachNative(JSObject* obj, ObjOperandId objId,
                          jsid key, ValOperandId keyId,
                          PropertyResult prop, JSObject* holder);
     bool tryAttachUnboxed(JSObject* obj, ObjOperandId objId,
                           jsid key, ValOperandId keyId);
--- a/js/src/jit/CacheIRCompiler.cpp
+++ b/js/src/jit/CacheIRCompiler.cpp
@@ -1949,16 +1949,40 @@ CacheIRCompiler::emitLoadDenseElementHol
     masm.bind(&hole);
     masm.moveValue(UndefinedValue(), output.valueReg());
 
     masm.bind(&done);
     return true;
 }
 
 bool
+CacheIRCompiler::emitLoadTypedElementExistsResult()
+{
+    AutoOutputRegister output(*this);
+    Register obj = allocator.useRegister(masm, reader.objOperandId());
+    Register index = allocator.useRegister(masm, reader.int32OperandId());
+    TypedThingLayout layout = reader.typedThingLayout();
+    AutoScratchRegisterMaybeOutput scratch(allocator, masm, output);
+
+    Label outOfBounds, done;
+
+    // Bound check.
+    LoadTypedThingLength(masm, layout, obj, scratch);
+    masm.branch32(Assembler::BelowOrEqual, scratch, index, &outOfBounds);
+    EmitStoreBoolean(masm, true, output);
+    masm.jump(&done);
+
+    masm.bind(&outOfBounds);
+    EmitStoreBoolean(masm, false, output);
+
+    masm.bind(&done);
+    return true;
+}
+
+bool
 CacheIRCompiler::emitLoadDenseElementExistsResult()
 {
     AutoOutputRegister output(*this);
     Register obj = allocator.useRegister(masm, reader.objOperandId());
     Register index = allocator.useRegister(masm, reader.int32OperandId());
     AutoScratchRegisterMaybeOutput scratch(allocator, masm, output);
 
     FailurePath* failure;
--- a/js/src/jit/CacheIRCompiler.h
+++ b/js/src/jit/CacheIRCompiler.h
@@ -45,16 +45,17 @@ namespace jit {
     _(LoadFunctionLengthResult)           \
     _(LoadStringLengthResult)             \
     _(LoadStringCharResult)               \
     _(LoadArgumentsObjectArgResult)       \
     _(LoadDenseElementResult)             \
     _(LoadDenseElementHoleResult)         \
     _(LoadDenseElementExistsResult)       \
     _(LoadDenseElementHoleExistsResult)   \
+    _(LoadTypedElementExistsResult)       \
     _(LoadTypedElementResult)             \
     _(LoadObjectResult)                   \
     _(LoadTypeOfObjectResult)             \
     _(CompareStringResult)                \
     _(CompareObjectResult)                \
     _(CompareSymbolResult)                \
     _(ArrayJoinResult)                    \
     _(CallPrintString)                    \