From e81bd0d0ab726553a9500f841933aee40d4f06c6 Mon Sep 17 00:00:00 2001 From: Jan de Mooij Date: Thu, 9 Jul 2020 07:02:25 +0000 Subject: [PATCH] Bug 1651037 part 5 - Change some MIR instructions to prepare for MNurseryObject use. r=iain,evilpie WrappedFunction only needs the JSFunction* for natives. This way we can still optimize scripted calls to nursery-allocated functions. Depends on D82670 Differential Revision: https://phabricator.services.mozilla.com/D82671 --- js/src/jit/CodeGenerator.cpp | 8 ++-- js/src/jit/IonBuilder.cpp | 2 +- js/src/jit/MIR.cpp | 62 +++++++++++++++++++--------- js/src/jit/MIR.h | 32 ++++++++------ js/src/jit/WarpCacheIRTranspiler.cpp | 25 +++++++---- 5 files changed, 84 insertions(+), 45 deletions(-) diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index 552dec0e2b55..d255c101d6aa 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -5098,7 +5098,7 @@ void CodeGenerator::visitCallNative(LCallNative* call) { // Push a Value containing the callee object: natives are allowed to access // their callee before setting the return value. The StackPointer is moved // to &vp[0]. - masm.Push(ObjectValue(*target->rawJSFunction())); + masm.Push(ObjectValue(*target->rawNativeJSFunction())); // Preload arguments into registers. masm.loadJSContext(argContextReg); @@ -5108,7 +5108,7 @@ void CodeGenerator::visitCallNative(LCallNative* call) { masm.Push(argUintNReg); if (call->mir()->maybeCrossRealm()) { - masm.movePtr(ImmGCPtr(target->rawJSFunction()), tempReg); + masm.movePtr(ImmGCPtr(target->rawNativeJSFunction()), tempReg); masm.switchToObjectRealm(tempReg, tempReg); } @@ -5247,7 +5247,7 @@ void CodeGenerator::visitCallDOMNative(LCallDOMNative* call) { // Push a Value containing the callee object: natives are allowed to access // their callee before setting the return value. After this the StackPointer // points to &vp[0]. - masm.Push(ObjectValue(*target->rawJSFunction())); + masm.Push(ObjectValue(*target->rawNativeJSFunction())); // Now compute the argv value. Since StackPointer is pointing to &vp[0] and // argv is &vp[2] we just need to add 2*sizeof(Value) to the current @@ -5277,7 +5277,7 @@ void CodeGenerator::visitCallDOMNative(LCallDOMNative* call) { if (call->mir()->maybeCrossRealm()) { // We use argJSContext as scratch register here. - masm.movePtr(ImmGCPtr(target->rawJSFunction()), argJSContext); + masm.movePtr(ImmGCPtr(target->rawNativeJSFunction()), argJSContext); masm.switchToObjectRealm(argJSContext, argJSContext); } diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp index d55fcdce48c5..65e4ab3363df 100644 --- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -6263,7 +6263,7 @@ AbortReasonOr IonBuilder::makeCall(const Maybe& targets, if (call->isCallDOMNative()) { return pushDOMTypeBarrier(call, types, - call->getSingleTarget()->rawJSFunction()); + call->getSingleTarget()->rawNativeJSFunction()); } return pushTypeBarrier(call, types, BarrierKind::TypeSet); diff --git a/js/src/jit/MIR.cpp b/js/src/jit/MIR.cpp index c738f90733a2..ac14c9ae206e 100644 --- a/js/src/jit/MIR.cpp +++ b/js/src/jit/MIR.cpp @@ -1481,21 +1481,28 @@ bool MParameter::congruentTo(const MDefinition* ins) const { } WrappedFunction::WrappedFunction(JSFunction* fun) - : fun_(fun), nargs_(fun->nargs()), flags_(fun->flags()) {} + : nativeFun_(fun->isNativeWithoutJitEntry() ? fun : nullptr), + nargs_(fun->nargs()), + flags_(fun->flags()) { + MOZ_ASSERT(!JitOptions.warpBuilder); +} -WrappedFunction::WrappedFunction(JSFunction* fun, uint16_t nargs, +WrappedFunction::WrappedFunction(JSFunction* nativeFun, uint16_t nargs, FunctionFlags flags) - : fun_(fun), nargs_(nargs), flags_(flags) { + : nativeFun_(nativeFun), nargs_(nargs), flags_(flags) { + MOZ_ASSERT_IF(nativeFun, isNativeWithoutJitEntry()); + #ifdef DEBUG // If we are not running off-main thread we can assert that the // metadata is consistent. - if (!CanUseExtraThreads()) { - MOZ_ASSERT(fun->nargs() == nargs); + if (!CanUseExtraThreads() && nativeFun) { + MOZ_ASSERT(nativeFun->nargs() == nargs); - MOZ_ASSERT(fun->isNativeWithoutJitEntry() == isNativeWithoutJitEntry()); - MOZ_ASSERT(fun->hasJitEntry() == hasJitEntry()); - MOZ_ASSERT(fun->isConstructor() == isConstructor()); - MOZ_ASSERT(fun->isClassConstructor() == isClassConstructor()); + MOZ_ASSERT(nativeFun->isNativeWithoutJitEntry() == + isNativeWithoutJitEntry()); + MOZ_ASSERT(nativeFun->hasJitEntry() == hasJitEntry()); + MOZ_ASSERT(nativeFun->isConstructor() == isConstructor()); + MOZ_ASSERT(nativeFun->isClassConstructor() == isClassConstructor()); } #endif } @@ -5847,18 +5854,25 @@ MDefinition* MGuardNullOrUndefined::foldsTo(TempAllocator& alloc) { } MDefinition* MGuardObjectIdentity::foldsTo(TempAllocator& alloc) { - if (!object()->isConstant()) { - return this; + if (object()->isConstant() && expected()->isConstant()) { + JSObject* obj = &object()->toConstant()->toObject(); + JSObject* other = &expected()->toConstant()->toObject(); + if (!bailOnEquality()) { + if (obj == other) { + return object(); + } + } else { + if (obj != other) { + return object(); + } + } } - JSObject* obj = &object()->toConstant()->toObject(); - JSObject* other = &expected()->toConstant()->toObject(); - if (!bailOnEquality()) { - if (obj == other) { - return object(); - } - } else { - if (obj != other) { + if (!bailOnEquality() && object()->isNurseryObject() && + expected()->isNurseryObject()) { + uint32_t objIndex = object()->toNurseryObject()->nurseryIndex(); + uint32_t otherIndex = expected()->toNurseryObject()->nurseryIndex(); + if (objIndex == otherIndex) { return object(); } } @@ -5867,7 +5881,7 @@ MDefinition* MGuardObjectIdentity::foldsTo(TempAllocator& alloc) { } MDefinition* MGuardSpecificFunction::foldsTo(TempAllocator& alloc) { - if (function()->isConstant()) { + if (function()->isConstant() && expected()->isConstant()) { JSObject* fun = &function()->toConstant()->toObject(); JSObject* other = &expected()->toConstant()->toObject(); if (fun == other) { @@ -5875,6 +5889,14 @@ MDefinition* MGuardSpecificFunction::foldsTo(TempAllocator& alloc) { } } + if (function()->isNurseryObject() && expected()->isNurseryObject()) { + uint32_t funIndex = function()->toNurseryObject()->nurseryIndex(); + uint32_t otherIndex = expected()->toNurseryObject()->nurseryIndex(); + if (funIndex == otherIndex) { + return function(); + } + } + return this; } diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index c07a8ffb8e10..c7a32b53bd08 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -2606,18 +2606,19 @@ class MInitElemGetterSetter NAMED_OPERANDS((0, object), (1, idValue), (2, value)) }; -// WrappedFunction wraps a JSFunction so it can safely be used off-thread. -// In particular, a function's flags can be modified on the main thread as -// functions are relazified and delazified, so we must be careful not to access -// these flags off-thread. +// WrappedFunction stores information about a function that can safely be used +// off-thread. In particular, a function's flags can be modified on the main +// thread as functions are relazified and delazified, so we must be careful not +// to access these flags off-thread. class WrappedFunction : public TempObject { - CompilerFunction fun_; + // If this is a native function without a JitEntry, the JSFunction*. + CompilerFunction nativeFun_; uint16_t nargs_; js::FunctionFlags flags_; public: explicit WrappedFunction(JSFunction* fun); - WrappedFunction(JSFunction* fun, uint16_t nargs, FunctionFlags flags); + WrappedFunction(JSFunction* nativeFun, uint16_t nargs, FunctionFlags flags); // Note: When adding new accessors be sure to add consistency asserts // to the constructor. @@ -2634,19 +2635,24 @@ class WrappedFunction : public TempObject { // These fields never change, they can be accessed off-main thread. JSNative native() const { MOZ_ASSERT(isNativeWithoutJitEntry()); - return fun_->nativeUnchecked(); + return nativeFun_->nativeUnchecked(); } bool hasJitInfo() const { - return flags_.isBuiltinNative() && fun_->jitInfoUnchecked(); + return flags_.isBuiltinNative() && nativeFun_->jitInfoUnchecked(); } const JSJitInfo* jitInfo() const { MOZ_ASSERT(hasJitInfo()); - return fun_->jitInfoUnchecked(); + return nativeFun_->jitInfoUnchecked(); } - JSFunction* rawJSFunction() const { return fun_; } + JSFunction* rawNativeJSFunction() const { return nativeFun_; } - bool appendRoots(MRootList& roots) const { return roots.append(fun_); } + bool appendRoots(MRootList& roots) const { + if (nativeFun_) { + return roots.append(nativeFun_); + } + return true; + } }; class MCall : public MVariadicInstruction, public CallPolicy::Data { @@ -9238,7 +9244,7 @@ class MGuardObjectIdentity : public MBinaryInstruction, bool bailOnEquality) : MBinaryInstruction(classOpcode, obj, expected), bailOnEquality_(bailOnEquality) { - MOZ_ASSERT(expected->isConstant()); + MOZ_ASSERT(expected->isConstant() || expected->isNurseryObject()); setGuard(); setMovable(); setResultType(MIRType::Object); @@ -9275,7 +9281,7 @@ class MGuardSpecificFunction : public MBinaryInstruction, : MBinaryInstruction(classOpcode, obj, expected), nargs_(nargs), flags_(flags) { - MOZ_ASSERT(expected->isConstant()); + MOZ_ASSERT(expected->isConstant() || expected->isNurseryObject()); setGuard(); setMovable(); setResultType(MIRType::Object); diff --git a/js/src/jit/WarpCacheIRTranspiler.cpp b/js/src/jit/WarpCacheIRTranspiler.cpp index a0877ce170a5..644b6422354d 100644 --- a/js/src/jit/WarpCacheIRTranspiler.cpp +++ b/js/src/jit/WarpCacheIRTranspiler.cpp @@ -1643,15 +1643,26 @@ bool WarpCacheIRTranspiler::emitCallFunction(ObjOperandId calleeId, WrappedFunction* wrappedTarget = nullptr; if (callee->isGuardSpecificFunction()) { auto* guard = callee->toGuardSpecificFunction(); - JSFunction* target = - &guard->expected()->toConstant()->toObject().as(); - wrappedTarget = - new (alloc()) WrappedFunction(target, guard->nargs(), guard->flags()); + MDefinition* expectedDef = guard->expected(); + MOZ_ASSERT(expectedDef->isConstant() || expectedDef->isNurseryObject()); - MOZ_ASSERT_IF(kind == CallKind::Native, - wrappedTarget->isNativeWithoutJitEntry()); - MOZ_ASSERT_IF(kind == CallKind::Scripted, wrappedTarget->hasJitEntry()); + // If this is a native without a JitEntry, WrappedFunction needs to know the + // target JSFunction. + // TODO: support nursery-allocated natives with WrappedFunction, maybe by + // storing the JSNative in the Baseline stub like flags/nargs. + bool isNative = guard->flags().isNativeWithoutJitEntry(); + if (!isNative || expectedDef->isConstant()) { + JSFunction* nativeTarget = nullptr; + if (isNative) { + nativeTarget = &expectedDef->toConstant()->toObject().as(); + } + wrappedTarget = new (alloc()) + WrappedFunction(nativeTarget, guard->nargs(), guard->flags()); + MOZ_ASSERT_IF(kind == CallKind::Native, + wrappedTarget->isNativeWithoutJitEntry()); + MOZ_ASSERT_IF(kind == CallKind::Scripted, wrappedTarget->hasJitEntry()); + } } bool needsThisCheck = false;