From a58b9699aa269e231fd885a45aece4915fb25dbc Mon Sep 17 00:00:00 2001 From: Tom Schuster Date: Wed, 16 Sep 2020 14:38:06 +0000 Subject: [PATCH] Bug 1664617 - Support calling DOM getters in CacheIR r=jandem Differential Revision: https://phabricator.services.mozilla.com/D90011 --- js/public/experimental/JitInfo.h | 3 ++ js/src/jit/BaselineCacheIRCompiler.cpp | 28 ++++++++++++ js/src/jit/CacheIR.cpp | 62 ++++++++++++++++++++++++-- js/src/jit/CacheIROps.yaml | 8 ++++ js/src/jit/IonCacheIRCompiler.cpp | 24 ++++++++++ js/src/jit/VMFunctionList-inl.h | 1 + js/src/jit/VMFunctions.cpp | 18 ++++++++ js/src/jit/VMFunctions.h | 3 ++ 8 files changed, 144 insertions(+), 3 deletions(-) diff --git a/js/public/experimental/JitInfo.h b/js/public/experimental/JitInfo.h index 39a4f192fcd6..52e89c65c37e 100644 --- a/js/public/experimental/JitInfo.h +++ b/js/public/experimental/JitInfo.h @@ -38,6 +38,9 @@ class JSJitGetterCallArgs : protected JS::MutableHandle { explicit JSJitGetterCallArgs(JS::Rooted* rooted) : JS::MutableHandle(rooted) {} + explicit JSJitGetterCallArgs(JS::MutableHandle handle) + : JS::MutableHandle(handle) {} + JS::MutableHandle rval() { return *this; } }; diff --git a/js/src/jit/BaselineCacheIRCompiler.cpp b/js/src/jit/BaselineCacheIRCompiler.cpp index 74d5318fab1d..7941fe101c69 100644 --- a/js/src/jit/BaselineCacheIRCompiler.cpp +++ b/js/src/jit/BaselineCacheIRCompiler.cpp @@ -609,6 +609,34 @@ bool BaselineCacheIRCompiler::emitCallNativeGetterResult( return true; } +bool BaselineCacheIRCompiler::emitCallDOMGetterResult(ObjOperandId objId, + uint32_t jitInfoOffset) { + JitSpew(JitSpew_Codegen, "%s", __FUNCTION__); + + Register obj = allocator.useRegister(masm, objId); + Address jitInfoAddr(stubAddress(jitInfoOffset)); + + AutoScratchRegister scratch(allocator, masm); + + allocator.discardStack(masm); + + AutoStubFrame stubFrame(*this); + stubFrame.enter(masm, scratch); + + // Load the JSJitInfo in the scratch register. + masm.loadPtr(jitInfoAddr, scratch); + + masm.Push(obj); + masm.Push(scratch); + + using Fn = + bool (*)(JSContext*, const JSJitInfo*, HandleObject, MutableHandleValue); + callVM(masm); + + stubFrame.leave(masm); + return true; +} + bool BaselineCacheIRCompiler::emitProxyGetResult(ObjOperandId objId, uint32_t idOffset) { JitSpew(JitSpew_Codegen, "%s", __FUNCTION__); diff --git a/js/src/jit/CacheIR.cpp b/js/src/jit/CacheIR.cpp index fd26f7117018..e8b76b6fd4a9 100644 --- a/js/src/jit/CacheIR.cpp +++ b/js/src/jit/CacheIR.cpp @@ -225,15 +225,15 @@ GetPropIRGenerator::GetPropIRGenerator(JSContext* cx, HandleScript script, resultFlags_(resultFlags), preliminaryObjectAction_(PreliminaryObjectAction::None) {} -static void EmitLoadSlotResult(CacheIRWriter& writer, ObjOperandId holderOp, +static void EmitLoadSlotResult(CacheIRWriter& writer, ObjOperandId holderId, NativeObject* holder, Shape* shape) { if (holder->isFixedSlot(shape->slot())) { - writer.loadFixedSlotResult(holderOp, + writer.loadFixedSlotResult(holderId, NativeObject::getFixedSlotOffset(shape->slot())); } else { size_t dynamicSlotOffset = holder->dynamicSlotIndex(shape->slot()) * sizeof(Value); - writer.loadDynamicSlotResult(holderOp, dynamicSlotOffset); + writer.loadDynamicSlotResult(holderId, dynamicSlotOffset); } } @@ -1065,6 +1065,54 @@ static void EmitCallGetterResult(JSContext* cx, CacheIRWriter& writer, EmitCallGetterResultNoGuards(cx, writer, obj, holder, shape, receiverId); } +static bool CanAttachDOMGetter(JSContext* cx, HandleObject obj, + HandleShape shape, ICState::Mode mode) { + if (!JitOptions.warpBuilder) { + return false; + } + + if (mode != ICState::Mode::Specialized) { + return false; + } + + JSFunction* getter = &shape->getterValue().toObject().as(); + if (!getter->hasJitInfo()) { + return false; + } + + if (cx->realm() != getter->realm()) { + return false; + } + + const JSJitInfo* jitInfo = getter->jitInfo(); + if (jitInfo->type() != JSJitInfo::Getter) { + return false; + } + + const JSClass* clasp = obj->getClass(); + if (!clasp->isDOMClass() || clasp->isProxy()) { + return false; + } + + DOMInstanceClassHasProtoAtDepth instanceChecker = + cx->runtime()->DOMcallbacks->instanceClassMatchesProto; + return instanceChecker(clasp, jitInfo->protoID, jitInfo->depth); +} + +static void EmitCallDOMGetterResult(JSContext* cx, CacheIRWriter& writer, + JSObject* obj, JSObject* holder, + Shape* shape, ObjOperandId objId) { + // Note: this relies on EmitCallGetterResultGuards emitting a shape guard + // for specialized stubs. + // The shape guard ensures the receiver's Class is valid for this DOM getter. + EmitCallGetterResultGuards(writer, obj, holder, shape, objId, + ICState::Mode::Specialized); + + JSFunction* getter = &shape->getterValue().toObject().as(); + writer.callDOMGetterResult(objId, getter->jitInfo()); + writer.typeMonitorResult(); +} + void GetPropIRGenerator::attachMegamorphicNativeSlot(ObjOperandId objId, jsid id, bool handleMissing) { @@ -1130,6 +1178,14 @@ AttachDecision GetPropIRGenerator::tryAttachNative(HandleObject obj, case CanAttachNativeGetter: { MOZ_ASSERT(!idempotent()); maybeEmitIdGuard(id); + + if (!isSuper() && CanAttachDOMGetter(cx_, obj, shape, mode_)) { + EmitCallDOMGetterResult(cx_, writer, obj, holder, shape, objId); + + trackAttached("DOMGetter"); + return AttachDecision::Attach; + } + EmitCallGetterResult(cx_, writer, obj, holder, shape, objId, receiverId, mode_); diff --git a/js/src/jit/CacheIROps.yaml b/js/src/jit/CacheIROps.yaml index d84026760da6..bb27131cebee 100644 --- a/js/src/jit/CacheIROps.yaml +++ b/js/src/jit/CacheIROps.yaml @@ -1860,6 +1860,14 @@ sameRealm: BoolImm nargsAndFlags: RawWordField +- name: CallDOMGetterResult + shared: false + transpile: false + cost_estimate: 4 + args: + obj: ObjId + jitInfo: RawPointerField + - name: ProxyGetResult shared: false transpile: true diff --git a/js/src/jit/IonCacheIRCompiler.cpp b/js/src/jit/IonCacheIRCompiler.cpp index d2be1f7be32c..8f31274c8667 100644 --- a/js/src/jit/IonCacheIRCompiler.cpp +++ b/js/src/jit/IonCacheIRCompiler.cpp @@ -1069,6 +1069,30 @@ bool IonCacheIRCompiler::emitCallNativeGetterResult( return true; } +bool IonCacheIRCompiler::emitCallDOMGetterResult(ObjOperandId objId, + uint32_t jitInfoOffset) { + JitSpew(JitSpew_Codegen, "%s", __FUNCTION__); + AutoSaveLiveRegisters save(*this); + AutoOutputRegister output(*this); + + Register obj = allocator.useRegister(masm, objId); + + const JSJitInfo* info = rawWordStubField(jitInfoOffset); + + allocator.discardStack(masm); + prepareVMCall(masm, save); + + masm.Push(obj); + masm.Push(ImmPtr(info)); + + using Fn = + bool (*)(JSContext*, const JSJitInfo*, HandleObject, MutableHandleValue); + callVM(masm); + + masm.storeCallResultValue(output); + return true; +} + bool IonCacheIRCompiler::emitProxyGetResult(ObjOperandId objId, uint32_t idOffset) { JitSpew(JitSpew_Codegen, "%s", __FUNCTION__); diff --git a/js/src/jit/VMFunctionList-inl.h b/js/src/jit/VMFunctionList-inl.h index b6bf26917020..b15a658ed0b5 100644 --- a/js/src/jit/VMFunctionList-inl.h +++ b/js/src/jit/VMFunctionList-inl.h @@ -76,6 +76,7 @@ namespace jit { _(BoxBoxableValue, js::wasm::BoxBoxableValue) \ _(BoxNonStrictThis, js::BoxNonStrictThis) \ _(BuiltinObjectOperation, js::BuiltinObjectOperation) \ + _(CallDOMGetter, js::jit::CallDOMGetter) \ _(CallNativeGetter, js::jit::CallNativeGetter) \ _(CallNativeSetter, js::jit::CallNativeSetter) \ _(CharCodeAt, js::jit::CharCodeAt) \ diff --git a/js/src/jit/VMFunctions.cpp b/js/src/jit/VMFunctions.cpp index 680124632045..01939c2e33c1 100644 --- a/js/src/jit/VMFunctions.cpp +++ b/js/src/jit/VMFunctions.cpp @@ -1498,6 +1498,24 @@ bool CallNativeGetter(JSContext* cx, HandleFunction callee, return true; } +bool CallDOMGetter(JSContext* cx, const JSJitInfo* info, HandleObject obj, + MutableHandleValue result) { + MOZ_ASSERT(info->type() == JSJitInfo::Getter); + MOZ_ASSERT(obj->isNative()); + MOZ_ASSERT(obj->getClass()->isDOMClass()); + +#ifdef DEBUG + DOMInstanceClassHasProtoAtDepth instanceChecker = + cx->runtime()->DOMcallbacks->instanceClassMatchesProto; + MOZ_ASSERT(instanceChecker(obj->getClass(), info->protoID, info->depth)); +#endif + + // Loading DOM_OBJECT_SLOT, which must be the first slot. + JS::Value val = JS::GetReservedSlot(obj, 0); + JSJitGetterOp getter = info->getter; + return getter(cx, obj, val.toPrivate(), JSJitGetterCallArgs(result)); +} + bool CallNativeSetter(JSContext* cx, HandleFunction callee, HandleObject obj, HandleValue rhs) { AutoRealm ar(cx, callee); diff --git a/js/src/jit/VMFunctions.h b/js/src/jit/VMFunctions.h index a44d658b2351..a5d6ef499fe7 100644 --- a/js/src/jit/VMFunctions.h +++ b/js/src/jit/VMFunctions.h @@ -1078,6 +1078,9 @@ MOZ_MUST_USE bool CallNativeGetter(JSContext* cx, HandleFunction callee, HandleValue receiver, MutableHandleValue result); +bool CallDOMGetter(JSContext* cx, const JSJitInfo* jitInfo, HandleObject obj, + MutableHandleValue result); + MOZ_MUST_USE bool CallNativeSetter(JSContext* cx, HandleFunction callee, HandleObject obj, HandleValue rhs);