Bug 1488786 - Add an IC for sparse array element access r=jandem
authorMatthew Gaudet <mgaudet@mozilla.com>
Mon, 24 Sep 2018 15:22:57 +0000
changeset 494985 0e43215b1975317557dff4c9a8878b1189d3e14e
parent 494984 f939ffbce06c53888ae512b74af25117278d4a67
child 494986 fd2a0cc4b40afeb46ac88965237bfefaa58702ae
push id9984
push userffxbld-merge
push dateMon, 15 Oct 2018 21:07:35 +0000
treeherdermozilla-beta@183d27ea8570 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs1488786
milestone64.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 1488786 - Add an IC for sparse array element access r=jandem Differential Revision: https://phabricator.services.mozilla.com/D5706
js/src/jit-test/tests/cacheir/bug1488786.js
js/src/jit/BaselineCacheIRCompiler.cpp
js/src/jit/CacheIR.cpp
js/src/jit/CacheIR.h
js/src/jit/IonCacheIRCompiler.cpp
js/src/jit/VMFunctions.cpp
js/src/jit/VMFunctions.h
js/src/vm/NativeObject.cpp
js/src/vm/NativeObject.h
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bug1488786.js
@@ -0,0 +1,38 @@
+setJitCompilerOption('ion.forceinlineCaches', 1);
+
+var A = Array(2**18);
+A[0] = "A";
+A[1] = "B";
+A[2**14] = "C";
+A[2**31-1] = "D";
+A[-1] = "E";
+
+function get_thee(a,x) {
+    return a[x];
+}
+
+// Warmup IC
+for (var i = 0; i < 30; i++) {
+    get_thee(A, i % A.length);
+}
+
+// Math.hypot currently always returns a Number, so helps
+// us ensure we're accessing the array with a Number index.
+var y = Math.hypot(1,0);
+var z = 2**31-1;
+// Ensure we handle negative indices.
+var a = -1;
+
+function test() {
+    for (var i = 0; i < 30; i++) {
+        assertEq(get_thee(A,y), "B");
+        assertEq(get_thee(A,z), "D");
+        assertEq(get_thee(A,a), "E");
+    }
+}
+test();
+
+if (!('oomTest' in this))
+    quit();
+
+oomTest(test);
--- a/js/src/jit/BaselineCacheIRCompiler.cpp
+++ b/js/src/jit/BaselineCacheIRCompiler.cpp
@@ -682,16 +682,41 @@ BaselineCacheIRCompiler::emitCallProxyHa
         }
     }
 
     stubFrame.leave(masm);
     return true;
 }
 
 bool
+BaselineCacheIRCompiler::emitCallNativeGetElementResult()
+{
+    Register obj = allocator.useRegister(masm, reader.objOperandId());
+    Register index = allocator.useRegister(masm, reader.int32OperandId());
+
+    AutoScratchRegister scratch(allocator, masm);
+
+    allocator.discardStack(masm);
+
+    AutoStubFrame stubFrame(*this);
+    stubFrame.enter(masm, scratch);
+
+    masm.Push(index);
+    masm.Push(TypedOrValueRegister(MIRType::Object, AnyRegister(obj)));
+    masm.Push(obj);
+
+    if (!callVM(masm, NativeGetElementInfo)) {
+        return false;
+    }
+
+    stubFrame.leave(masm);
+    return true;
+}
+
+bool
 BaselineCacheIRCompiler::emitLoadUnboxedPropertyResult()
 {
     AutoOutputRegister output(*this);
     Register obj = allocator.useRegister(masm, reader.objOperandId());
     AutoScratchRegisterMaybeOutput scratch(allocator, masm, output);
 
     JSValueType fieldType = reader.valueType();
     Address fieldOffset(stubAddress(reader.stubOffset()));
--- a/js/src/jit/CacheIR.cpp
+++ b/js/src/jit/CacheIR.cpp
@@ -285,16 +285,19 @@ GetPropIRGenerator::tryAttachStub()
                 return true;
             }
             if (tryAttachUnboxedElementHole(obj, objId, index, indexId)) {
                 return true;
             }
             if (tryAttachArgumentsObjectArg(obj, objId, indexId)) {
                 return true;
             }
+            if (tryAttachGenericElement(obj, objId, index, indexId)) {
+                return true;
+            }
 
             trackAttached(IRGenerator::NotAttached);
             return false;
         }
 
         trackAttached(IRGenerator::NotAttached);
         return false;
     }
@@ -2338,16 +2341,41 @@ GetPropIRGenerator::tryAttachUnboxedElem
     // No monitor: We know undefined must be in the typeset already.
     writer.returnFromIC();
 
     trackAttached("UnboxedElementHole");
     return true;
 }
 
 bool
+GetPropIRGenerator::tryAttachGenericElement(HandleObject obj, ObjOperandId objId,
+                                            uint32_t index, Int32OperandId indexId)
+{
+    if (!obj->isNative()) {
+        return false;
+    }
+
+    // To allow other types to attach in the non-megamorphic case we test the specific
+    // matching native reciever; however, once megamorphic we can attach for any native
+    if (mode_ == ICState::Mode::Megamorphic) {
+        writer.guardIsNativeObject(objId);
+    } else {
+        NativeObject* nobj = &obj->as<NativeObject>();
+        TestMatchingNativeReceiver(writer, nobj, objId);
+    }
+    writer.callNativeGetElementResult(objId, indexId);
+    writer.typeMonitorResult();
+
+    trackAttached(mode_ == ICState::Mode::Megamorphic
+                  ? "GenericElementMegamorphic": "GenericElement");
+    return true;
+}
+
+
+bool
 GetPropIRGenerator::tryAttachProxyElement(HandleObject obj, ObjOperandId objId)
 {
     if (!obj->is<ProxyObject>()) {
         return false;
     }
 
     // The proxy stubs don't currently support |super| access.
     if (isSuper()) {
--- a/js/src/jit/CacheIR.h
+++ b/js/src/jit/CacheIR.h
@@ -293,16 +293,17 @@ extern const char* const CacheKindNames[
     _(LoadEnvironmentDynamicSlotResult)   \
     _(LoadObjectResult)                   \
     _(CallScriptedGetterResult)           \
     _(CallNativeGetterResult)             \
     _(CallProxyGetResult)                 \
     _(CallProxyGetByValueResult)          \
     _(CallProxyHasPropResult)             \
     _(CallObjectHasSparseElementResult)   \
+    _(CallNativeGetElementResult)        \
     _(LoadUndefinedResult)                \
     _(LoadBooleanResult)                  \
     _(LoadStringResult)                   \
     _(LoadInstanceOfObjectResult)         \
     _(LoadTypeOfObjectResult)             \
     _(DoubleAddResult)                    \
     _(DoubleSubResult)                    \
     _(DoubleMulResult)                    \
@@ -1254,16 +1255,20 @@ class MOZ_RAII CacheIRWriter : public JS
         writeOpWithOperandId(CacheOp::CallProxyHasPropResult, obj);
         writeOperandId(idVal);
         buffer_.writeByte(uint32_t(hasOwn));
     }
     void callObjectHasSparseElementResult(ObjOperandId obj, Int32OperandId index) {
         writeOpWithOperandId(CacheOp::CallObjectHasSparseElementResult, obj);
         writeOperandId(index);
     }
+    void callNativeGetElementResult(ObjOperandId obj, Int32OperandId index) {
+        writeOpWithOperandId(CacheOp::CallNativeGetElementResult, obj);
+        writeOperandId(index);
+    }
     void loadEnvironmentFixedSlotResult(ObjOperandId obj, size_t offset) {
         writeOpWithOperandId(CacheOp::LoadEnvironmentFixedSlotResult, obj);
         addStubField(offset, StubField::Type::RawWord);
     }
     void loadEnvironmentDynamicSlotResult(ObjOperandId obj, size_t offset) {
         writeOpWithOperandId(CacheOp::LoadEnvironmentDynamicSlotResult, obj);
         addStubField(offset, StubField::Type::RawWord);
     }
@@ -1564,16 +1569,19 @@ class MOZ_RAII GetPropIRGenerator : publ
                                uint32_t index, Int32OperandId indexId);
     bool tryAttachDenseElementHole(HandleObject obj, ObjOperandId objId,
                                    uint32_t index, Int32OperandId indexId);
     bool tryAttachTypedElement(HandleObject obj, ObjOperandId objId,
                                uint32_t index, Int32OperandId indexId);
     bool tryAttachUnboxedElementHole(HandleObject obj, ObjOperandId objId,
                                      uint32_t index, Int32OperandId indexId);
 
+    bool tryAttachGenericElement(HandleObject obj, ObjOperandId objId,
+                                 uint32_t index, Int32OperandId indexId);
+
     bool tryAttachProxyElement(HandleObject obj, ObjOperandId objId);
 
     void attachMegamorphicNativeSlot(ObjOperandId objId, jsid id, bool handleMissing);
 
     ValOperandId getElemKeyValueId() const {
         MOZ_ASSERT(cacheKind_ == CacheKind::GetElem || cacheKind_ == CacheKind::GetElemSuper);
         return ValOperandId(1);
     }
--- a/js/src/jit/IonCacheIRCompiler.cpp
+++ b/js/src/jit/IonCacheIRCompiler.cpp
@@ -1184,16 +1184,43 @@ IonCacheIRCompiler::emitCallProxyHasProp
             return false;
         }
     }
 
     masm.storeCallResultValue(output);
     return true;
 }
 
+
+bool
+IonCacheIRCompiler::emitCallNativeGetElementResult()
+{
+    AutoSaveLiveRegisters save(*this);
+    AutoOutputRegister output(*this);
+
+    Register obj = allocator.useRegister(masm, reader.objOperandId());
+    Register index = allocator.useRegister(masm, reader.int32OperandId());
+
+    allocator.discardStack(masm);
+
+    prepareVMCall(masm, save);
+
+    masm.Push(index);
+    masm.Push(TypedOrValueRegister(MIRType::Object, AnyRegister(obj)));
+    masm.Push(obj);
+
+    if (!callVM(masm, NativeGetElementInfo)) {
+        return false;
+    }
+
+    masm.storeCallResultValue(output);
+    return true;
+}
+
+
 bool
 IonCacheIRCompiler::emitLoadUnboxedPropertyResult()
 {
     AutoOutputRegister output(*this);
     Register obj = allocator.useRegister(masm, reader.objOperandId());
 
     JSValueType fieldType = reader.valueType();
     int32_t fieldOffset = int32StubField(reader.stubOffset());
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -2062,10 +2062,15 @@ const VMFunction ProxySetPropertyByValue
     FunctionInfo<ProxySetPropertyByValueFn>(ProxySetPropertyByValue, "ProxySetPropertyByValue");
 
 typedef bool (*ProxyHasFn)(JSContext*, HandleObject, HandleValue, MutableHandleValue);
 const VMFunction ProxyHasInfo = FunctionInfo<ProxyHasFn>(ProxyHas, "ProxyHas");
 
 typedef bool (*ProxyHasOwnFn)(JSContext*, HandleObject, HandleValue, MutableHandleValue);
 const VMFunction ProxyHasOwnInfo = FunctionInfo<ProxyHasOwnFn>(ProxyHasOwn, "ProxyHasOwn");
 
+typedef bool (*NativeGetElementFn)(JSContext*, HandleNativeObject, HandleValue, int32_t,
+                                   MutableHandleValue);
+const VMFunction NativeGetElementInfo =
+    FunctionInfo<NativeGetElementFn>(NativeGetElement, "NativeGetProperty");
+
 } // namespace jit
 } // namespace js
--- a/js/src/jit/VMFunctions.h
+++ b/js/src/jit/VMFunctions.h
@@ -975,15 +975,17 @@ extern const VMFunction StringSplitHelpe
 
 extern const VMFunction ProxyGetPropertyInfo;
 extern const VMFunction ProxyGetPropertyByValueInfo;
 extern const VMFunction ProxySetPropertyInfo;
 extern const VMFunction ProxySetPropertyByValueInfo;
 extern const VMFunction ProxyHasInfo;
 extern const VMFunction ProxyHasOwnInfo;
 
+extern const VMFunction NativeGetElementInfo;
+
 // TailCall VMFunctions
 extern const VMFunction DoConcatStringObjectInfo;
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_VMFunctions_h */
--- a/js/src/vm/NativeObject.cpp
+++ b/js/src/vm/NativeObject.cpp
@@ -2565,16 +2565,35 @@ js::NativeGetProperty(JSContext* cx, Han
 bool
 js::NativeGetPropertyNoGC(JSContext* cx, NativeObject* obj, const Value& receiver, jsid id, Value* vp)
 {
     AutoAssertNoException noexc(cx);
     return NativeGetPropertyInline<NoGC>(cx, obj, receiver, id, NotNameLookup, vp);
 }
 
 bool
+js::NativeGetElement(JSContext* cx, HandleNativeObject obj, HandleValue receiver,
+                     int32_t index, MutableHandleValue vp)
+{
+    RootedId id(cx);
+
+    if (MOZ_LIKELY(index >=0)) {
+        if (!IndexToId(cx, index, &id)) {
+            return false;
+        }
+    } else {
+        RootedValue indexVal(cx, Int32Value(index));
+        if (!ValueToId<CanGC>(cx, indexVal, &id)) {
+            return false;
+        }
+    }
+    return NativeGetProperty(cx, obj, receiver, id, vp);
+}
+
+bool
 js::GetNameBoundInEnvironment(JSContext* cx, HandleObject envArg, HandleId id, MutableHandleValue vp)
 {
     // Manually unwrap 'with' environments to prevent looking up @@unscopables
     // twice.
     //
     // This is unfortunate because internally, the engine does not distinguish
     // HasProperty from HasBinding: both are implemented as a HasPropertyOp
     // hook on a WithEnvironmentObject.
--- a/js/src/vm/NativeObject.h
+++ b/js/src/vm/NativeObject.h
@@ -1605,16 +1605,20 @@ NativeGetPropertyNoGC(JSContext* cx, Nat
 
 inline bool
 NativeGetProperty(JSContext* cx, HandleNativeObject obj, HandleId id, MutableHandleValue vp)
 {
     RootedValue receiver(cx, ObjectValue(*obj));
     return NativeGetProperty(cx, obj, receiver, id, vp);
 }
 
+extern bool
+NativeGetElement(JSContext* cx, HandleNativeObject obj, HandleValue reciever, int32_t index,
+                 MutableHandleValue vp);
+
 bool
 SetPropertyByDefining(JSContext* cx, HandleId id, HandleValue v, HandleValue receiver,
                       ObjectOpResult& result);
 
 bool
 SetPropertyOnProto(JSContext* cx, HandleObject obj, HandleId id, HandleValue v,
                    HandleValue receiver, ObjectOpResult& result);