From 9d47f2ea8d00d21e101d899880307595e853a1a2 Mon Sep 17 00:00:00 2001 From: Tom Schuster Date: Tue, 18 Apr 2017 18:56:25 +0200 Subject: [PATCH] Bug 1357468 - More Object.hasOwnProperty optimizations for Speedometer. r=jandem - IC support dense elements, like [0].hasOwnProperty("0") - IC support unboxed objects - IC support proxies, i.e. NodeList or other DOM proxies mostly --- js/src/jit/BaselineCacheIRCompiler.cpp | 26 +++++++++++++ js/src/jit/CacheIR.cpp | 53 ++++++++++++++++++++++++-- js/src/jit/CacheIR.h | 8 ++++ js/src/jit/IonCacheIRCompiler.cpp | 27 +++++++++++++ js/src/jit/VMFunctions.cpp | 12 ++++-- js/src/proxy/Proxy.cpp | 15 ++++++++ js/src/proxy/Proxy.h | 3 ++ 7 files changed, 138 insertions(+), 6 deletions(-) diff --git a/js/src/jit/BaselineCacheIRCompiler.cpp b/js/src/jit/BaselineCacheIRCompiler.cpp index b97b6bcfd9dd..078eac28738d 100644 --- a/js/src/jit/BaselineCacheIRCompiler.cpp +++ b/js/src/jit/BaselineCacheIRCompiler.cpp @@ -672,6 +672,32 @@ BaselineCacheIRCompiler::emitCallProxyGetByValueResult() return true; } +typedef bool (*ProxyHasOwnFn)(JSContext*, HandleObject, HandleValue, MutableHandleValue); +static const VMFunction ProxyHasOwnInfo = FunctionInfo(ProxyHasOwn, "ProxyHasOwn"); + +bool +BaselineCacheIRCompiler::emitCallProxyHasOwnResult() +{ + Register obj = allocator.useRegister(masm, reader.objOperandId()); + ValueOperand idVal = allocator.useValueRegister(masm, reader.valOperandId()); + + AutoScratchRegister scratch(allocator, masm); + + allocator.discardStack(masm); + + AutoStubFrame stubFrame(*this); + stubFrame.enter(masm, scratch); + + masm.Push(idVal); + masm.Push(obj); + + if (!callVM(masm, ProxyHasOwnInfo)) + return false; + + stubFrame.leave(masm); + return true; +} + bool BaselineCacheIRCompiler::emitLoadUnboxedPropertyResult() { diff --git a/js/src/jit/CacheIR.cpp b/js/src/jit/CacheIR.cpp index 2862f79bb90d..d524d970f00f 100644 --- a/js/src/jit/CacheIR.cpp +++ b/js/src/jit/CacheIR.cpp @@ -2159,7 +2159,10 @@ HasOwnIRGenerator::tryAttachNativeHasOwn(HandleId key, ValOperandId keyId, if (!LookupOwnPropertyPure(cx_, obj, key, &prop)) return false; - if (!prop.isNativeProperty()) + if (!prop.isFound()) + return false; + + if (!obj->isNative() && !obj->is()) return false; if (mode_ == ICState::Mode::Megamorphic) { @@ -2169,9 +2172,9 @@ HasOwnIRGenerator::tryAttachNativeHasOwn(HandleId key, ValOperandId keyId, return true; } - Maybe holderId; + Maybe expandoId; emitIdGuard(keyId, key); - EmitReadSlotGuard(writer, obj, obj, prop.shape(), objId, &holderId); + TestMatchingReceiver(writer, obj, nullptr, objId, &expandoId); writer.loadBooleanResult(true); writer.returnFromIC(); @@ -2203,6 +2206,37 @@ HasOwnIRGenerator::tryAttachNativeHasOwnDoesNotExist(HandleId key, ValOperandId return true; } +bool +HasOwnIRGenerator::tryAttachProxyElement(ValOperandId keyId, HandleObject obj, ObjOperandId objId) +{ + if (!obj->is()) + return false; + + writer.guardIsProxy(objId); + writer.callProxyHasOwnResult(objId, keyId); + writer.returnFromIC(); + + trackAttached("ProxyHasOwn"); + return true; +} + +bool +HasOwnIRGenerator::tryAttachDenseHasOwn(uint32_t index, Int32OperandId indexId, + HandleObject obj, ObjOperandId objId) +{ + if (!obj->isNative()) + return false; + if (!obj->as().containsDenseElement(index)) + return false; + + writer.guardShape(objId, obj->as().lastProperty()); + writer.loadDenseElementExistsResult(objId, indexId); + writer.returnFromIC(); + + trackAttached("DenseHasOwn"); + return true; +} + bool HasOwnIRGenerator::tryAttachStub() { @@ -2221,6 +2255,9 @@ HasOwnIRGenerator::tryAttachStub() ObjOperandId objId = writer.guardIsObject(valId); + if (tryAttachProxyElement(keyId, obj, objId)) + return true; + RootedId id(cx_); bool nameOrSymbol; if (!ValueToNameOrSymbolId(cx_, key_, &id, &nameOrSymbol)) { @@ -2238,6 +2275,16 @@ HasOwnIRGenerator::tryAttachStub() return false; } + uint32_t index; + Int32OperandId indexId; + if (maybeGuardInt32Index(key_, keyId, &index, &indexId)) { + if (tryAttachDenseHasOwn(index, indexId, obj, objId)) + return true; + + trackNotAttached(); + return false; + } + trackNotAttached(); return false; } diff --git a/js/src/jit/CacheIR.h b/js/src/jit/CacheIR.h index 4a2243959bdc..63236bbaab71 100644 --- a/js/src/jit/CacheIR.h +++ b/js/src/jit/CacheIR.h @@ -242,6 +242,7 @@ extern const char* CacheKindNames[]; _(CallNativeGetterResult) \ _(CallProxyGetResult) \ _(CallProxyGetByValueResult) \ + _(CallProxyHasOwnResult) \ _(LoadUndefinedResult) \ _(LoadBooleanResult) \ \ @@ -885,6 +886,10 @@ class MOZ_RAII CacheIRWriter : public JS::CustomAutoRooter writeOpWithOperandId(CacheOp::CallProxyGetByValueResult, obj); writeOperandId(idVal); } + void callProxyHasOwnResult(ObjOperandId obj, ValOperandId idVal) { + writeOpWithOperandId(CacheOp::CallProxyHasOwnResult, obj); + writeOperandId(idVal); + } void loadEnvironmentFixedSlotResult(ObjOperandId obj, size_t offset) { writeOpWithOperandId(CacheOp::LoadEnvironmentFixedSlotResult, obj); addStubField(offset, StubField::Type::RawWord); @@ -1286,6 +1291,9 @@ class MOZ_RAII HasOwnIRGenerator : public IRGenerator HandleValue key_; HandleValue val_; + bool tryAttachProxyElement(ValOperandId keyId, HandleObject obj, ObjOperandId objId); + bool tryAttachDenseHasOwn(uint32_t index, Int32OperandId indexId, + HandleObject obj, ObjOperandId objId); bool tryAttachNativeHasOwn(HandleId key, ValOperandId keyId, HandleObject obj, ObjOperandId objId); bool tryAttachNativeHasOwnDoesNotExist(HandleId key, ValOperandId keyId, diff --git a/js/src/jit/IonCacheIRCompiler.cpp b/js/src/jit/IonCacheIRCompiler.cpp index b7db57f95435..ab1a7c040d1b 100644 --- a/js/src/jit/IonCacheIRCompiler.cpp +++ b/js/src/jit/IonCacheIRCompiler.cpp @@ -1040,6 +1040,33 @@ IonCacheIRCompiler::emitCallProxyGetByValueResult() return true; } +typedef bool (*ProxyHasOwnFn)(JSContext*, HandleObject, HandleValue, MutableHandleValue); +static const VMFunction ProxyHasOwnInfo = FunctionInfo(ProxyHasOwn, "ProxyHasOwn"); + +bool +IonCacheIRCompiler::emitCallProxyHasOwnResult() +{ + AutoSaveLiveRegisters save(*this); + AutoOutputRegister output(*this); + + Register obj = allocator.useRegister(masm, reader.objOperandId()); + ValueOperand idVal = allocator.useValueRegister(masm, reader.valOperandId()); + + allocator.discardStack(masm); + + prepareVMCall(masm); + + masm.Push(idVal); + masm.Push(obj); + + if (!callVM(masm, ProxyHasOwnInfo)) + return false; + + masm.storeCallResultValue(output); + return true; +} + + bool IonCacheIRCompiler::emitLoadUnboxedPropertyResult() { diff --git a/js/src/jit/VMFunctions.cpp b/js/src/jit/VMFunctions.cpp index 758fabbd499e..d0c03fdf9cf2 100644 --- a/js/src/jit/VMFunctions.cpp +++ b/js/src/jit/VMFunctions.cpp @@ -1740,15 +1740,21 @@ HasOwnNativeDataProperty(JSContext* cx, JSObject* obj, Value* vp) { JS::AutoCheckCannotGC nogc; - if (MOZ_UNLIKELY(!obj->isNative())) - return false; - // vp[0] contains the id, result will be stored in vp[1]. Value idVal = vp[0]; jsid id; if (!ValueToAtomOrSymbol(cx, idVal, &id)) return false; + if (!obj->isNative()) { + if (obj->is()) { + bool res = obj->as().containsUnboxedOrExpandoProperty(cx, id); + vp[1].setBoolean(res); + return true; + } + return false; + } + NativeObject* nobj = &obj->as(); if (nobj->lastProperty()->search(cx, id)) { vp[1].setBoolean(true); diff --git a/js/src/proxy/Proxy.cpp b/js/src/proxy/Proxy.cpp index a5f640b7b078..3c42a1611e3a 100644 --- a/js/src/proxy/Proxy.cpp +++ b/js/src/proxy/Proxy.cpp @@ -284,6 +284,21 @@ Proxy::hasOwn(JSContext* cx, HandleObject proxy, HandleId id, bool* bp) return handler->hasOwn(cx, proxy, id, bp); } +bool +js::ProxyHasOwn(JSContext* cx, HandleObject proxy, HandleValue idVal, MutableHandleValue result) +{ + RootedId id(cx); + if (!ValueToId(cx, idVal, &id)) + return false; + + bool hasOwn; + if (!Proxy::hasOwn(cx, proxy, id, &hasOwn)) + return false; + + result.setBoolean(hasOwn); + return true; +} + static Value ValueToWindowProxyIfWindow(const Value& v) { diff --git a/js/src/proxy/Proxy.h b/js/src/proxy/Proxy.h index aa95a7f7de4b..bfb7ad7be36f 100644 --- a/js/src/proxy/Proxy.h +++ b/js/src/proxy/Proxy.h @@ -80,6 +80,9 @@ proxy_Construct(JSContext* cx, unsigned argc, Value* vp); // These functions are used by JIT code +bool +ProxyHasOwn(JSContext* cx, HandleObject proxy, HandleValue idVal, MutableHandleValue result); + bool ProxyGetProperty(JSContext* cx, HandleObject proxy, HandleId id, MutableHandleValue vp);