Bug 1705819 - Resolve _SetIsInlinableLargeFunction during BCE. r=arai

Use the existing intrinsic function call processing of self-hosting, the BCE
can also resolve _SetIsInlinableLargeFunction. To do this, we also track the
latest top-level FunctionBox that was processed to allow easily updating
flags without a dedicated hashmap. This change lets us make the flag
immutable now.

Differential Revision: https://phabricator.services.mozilla.com/D112411
This commit is contained in:
Ted Campbell 2021-04-19 18:48:08 +00:00
parent 9e4dc813a7
commit 840a19df94
9 changed files with 95 additions and 39 deletions

View File

@ -616,6 +616,7 @@ MSG_DEF(JSMSG_DUPLICATE_CAPTURE_NAME, 0, JSEXN_SYNTAXERR, "duplicate captur
MSG_DEF(JSMSG_INVALID_NAMED_REF, 0, JSEXN_SYNTAXERR, "invalid named reference in regular expression")
MSG_DEF(JSMSG_INVALID_NAMED_CAPTURE_REF, 0, JSEXN_SYNTAXERR, "invalid named capture reference in regular expression")
MSG_DEF(JSMSG_INCOMPATIBLE_REGEXP_GETTER, 2, JSEXN_TYPEERR, "RegExp.prototype.{0} getter called on non-RegExp object: {1}")
MSG_DEF(JSMSG_SELFHOST_DECORATOR_MUST_FOLLOW, 0, JSEXN_TYPEERR, "selfhost decorator must immediately follow target function")
// Self-hosting
MSG_DEF(JSMSG_DEFAULT_LOCALE_ERROR, 0, JSEXN_ERR, "internal error getting the default locale")

View File

@ -5860,10 +5860,7 @@ MOZ_NEVER_INLINE bool BytecodeEmitter::emitFunction(
return false;
}
MOZ_ASSERT(funNode->functionIsHoisted());
return true;
}
if (funbox->isInterpreted()) {
} else if (funbox->isInterpreted()) {
if (!funbox->emitBytecode) {
return fe.emitLazy();
// [stack] FUN?
@ -5888,13 +5885,19 @@ MOZ_NEVER_INLINE bool BytecodeEmitter::emitFunction(
// [stack] FUN?
return false;
}
return true;
} else {
if (!fe.emitAsmJSModule()) {
// [stack]
return false;
}
}
if (!fe.emitAsmJSModule()) {
// [stack]
return false;
// Track the last emitted top-level self-hosted function, so that intrinsics
// can adjust attributes at parse time.
if (emitterMode == EmitterMode::SelfHosting) {
if (sc->isTopLevelContext() && funbox->explicitName()) {
prevSelfHostedTopLevelFunction = funbox;
}
}
return true;
@ -7511,6 +7514,57 @@ bool BytecodeEmitter::emitSelfHostedGetBuiltinPrototype(BinaryNode* callNode) {
callNode, /* isConstructor = */ false);
}
#ifdef DEBUG
bool BytecodeEmitter::checkSelfHostedExpectedTopLevel(BinaryNode* callNode,
ParseNode* node) {
// The function argument is expected to be a simple binding/function name.
// Eg. `function foo() { }; SpecialIntrinsic(foo)`
if (!node->isKind(ParseNodeKind::Name)) {
reportError(callNode, JSMSG_UNEXPECTED_TYPE, "function argument",
"not a binding name");
return false;
}
TaggedParserAtomIndex targetName = node->as<NameNode>().name();
// The special intrinsics must follow the target functions definition. A
// simple assert is fine here since any hoisted function will cause a non-null
// value to be set here.
MOZ_ASSERT(prevSelfHostedTopLevelFunction);
// The target function must match the most recently defined top-level
// self-hosted function.
if (prevSelfHostedTopLevelFunction->explicitName() != targetName) {
reportError(callNode, JSMSG_SELFHOST_DECORATOR_MUST_FOLLOW);
return false;
}
return true;
}
#endif
bool BytecodeEmitter::emitSelfHostedSetIsInlinableLargeFunction(
BinaryNode* callNode) {
ListNode* argsList = &callNode->right()->as<ListNode>();
if (argsList->count() != 1) {
reportNeedMoreArgsError(callNode, "_SetIsInlinableLargeFunction", "1", "",
argsList);
return false;
}
#ifdef DEBUG
if (!checkSelfHostedExpectedTopLevel(callNode, argsList->head())) {
return false;
}
#endif
MOZ_ASSERT(prevSelfHostedTopLevelFunction->isInitialCompilation);
prevSelfHostedTopLevelFunction->setIsInlinableLargeFunction();
// This is still a call node, so we must generate a stack value.
return emit1(JSOp::Undefined);
}
#ifdef DEBUG
bool BytecodeEmitter::checkSelfHostedUnsafeGetReservedSlot(
BinaryNode* callNode) {
@ -8017,6 +8071,10 @@ bool BytecodeEmitter::emitCallOrNew(
if (calleeName == TaggedParserAtomIndex::WellKnown::GetBuiltinPrototype()) {
return emitSelfHostedGetBuiltinPrototype(callNode);
}
if (calleeName ==
TaggedParserAtomIndex::WellKnown::SetIsInlinableLargeFunction()) {
return emitSelfHostedSetIsInlinableLargeFunction(callNode);
}
#ifdef DEBUG
if (calleeName ==
TaggedParserAtomIndex::WellKnown::UnsafeGetReservedSlot() ||

View File

@ -119,6 +119,12 @@ struct MOZ_STACK_CLASS BytecodeEmitter {
EmitterScope* innermostEmitterScope_ = nullptr;
TDZCheckCache* innermostTDZCheckCache = nullptr;
// When compiling in self-hosted mode, we have special intrinsics that act as
// decorators for exported functions. To keeps things simple, we only allow
// these to target the last top-level function emitted. This field tracks that
// function.
FunctionBox* prevSelfHostedTopLevelFunction = nullptr;
#ifdef DEBUG
bool unstableEmitterScope = false;
@ -790,7 +796,11 @@ struct MOZ_STACK_CLASS BytecodeEmitter {
[[nodiscard]] bool emitSelfHostedToString(BinaryNode* callNode);
[[nodiscard]] bool emitSelfHostedGetBuiltinConstructor(BinaryNode* callNode);
[[nodiscard]] bool emitSelfHostedGetBuiltinPrototype(BinaryNode* callNode);
[[nodiscard]] bool emitSelfHostedSetIsInlinableLargeFunction(
BinaryNode* callNode);
#ifdef DEBUG
[[nodiscard]] bool checkSelfHostedExpectedTopLevel(BinaryNode* callNode,
ParseNode* node);
[[nodiscard]] bool checkSelfHostedUnsafeGetReservedSlot(BinaryNode* callNode);
[[nodiscard]] bool checkSelfHostedUnsafeSetReservedSlot(BinaryNode* callNode);
#endif

View File

@ -649,6 +649,13 @@ class FunctionBox : public SuspendableContext {
}
}
void setIsInlinableLargeFunction() {
immutableFlags_.setFlag(ImmutableFlags::IsInlinableLargeFunction, true);
if (isScriptExtraFieldCopiedToStencil) {
copyUpdatedImmutableFlags();
}
}
uint16_t length() { return length_; }
void setLength(uint16_t length) { length_ = length; }
@ -680,6 +687,8 @@ class FunctionBox : public SuspendableContext {
// * setCtorFunctionHasThisBinding can be called to a class constructor
// with a lazy function, while parsing enclosing class
// * setIsInlinableLargeFunction can be called by BCE to update flags of the
// previous top-level function, but only in self-hosted mode.
void copyUpdatedImmutableFlags();
// * setCtorToStringEnd bcan be called to a class constructor with a lazy

View File

@ -411,6 +411,8 @@
MACRO_(setBigInt64, setBigInt64, "setBigInt64") \
MACRO_(setBigUint64, setBigUint64, "setBigUint64") \
MACRO_(SetConstructorInit, SetConstructorInit, "SetConstructorInit") \
MACRO_(SetIsInlinableLargeFunction, SetIsInlinableLargeFunction, \
"_SetIsInlinableLargeFunction") \
MACRO_(SetIterator, SetIterator, "Set Iterator") \
MACRO_(setPrototypeOf, setPrototypeOf, "setPrototypeOf") \
MACRO_(shape, shape, "shape") \

View File

@ -4395,11 +4395,6 @@ static JSScript* CopyScriptImpl(JSContext* cx, HandleScript src,
return nullptr;
}
// Maintain this flag when cloning self-hosted functions.
if (src->isInlinableLargeFunction()) {
dst->setIsInlinableLargeFunction();
}
// Clone the PrivateScriptData into dst
if (!PrivateScriptData::Clone(cx, src, dst, scopes)) {
return nullptr;

View File

@ -1628,6 +1628,7 @@ class BaseScript : public gc::TenuredCellWithNonGCPointer<uint8_t> {
IMMUTABLE_FLAG_GETTER(argumentsHasVarBinding, ArgumentsHasVarBinding)
IMMUTABLE_FLAG_GETTER(alwaysNeedsArgsObj, AlwaysNeedsArgsObj)
IMMUTABLE_FLAG_GETTER(hasMappedArgsObj, HasMappedArgsObj)
IMMUTABLE_FLAG_GETTER(isInlinableLargeFunction, IsInlinableLargeFunction)
MUTABLE_FLAG_GETTER_SETTER(hasRunOnce, HasRunOnce)
MUTABLE_FLAG_GETTER_SETTER(hasScriptCounts, HasScriptCounts)
@ -1646,7 +1647,6 @@ class BaseScript : public gc::TenuredCellWithNonGCPointer<uint8_t> {
MUTABLE_FLAG_GETTER_SETTER(uninlineable, Uninlineable)
MUTABLE_FLAG_GETTER_SETTER(failedLexicalCheck, FailedLexicalCheck)
MUTABLE_FLAG_GETTER_SETTER(hadSpeculativePhiBailout, HadSpeculativePhiBailout)
MUTABLE_FLAG_GETTER_SETTER(isInlinableLargeFunction, IsInlinableLargeFunction)
#undef IMMUTABLE_FLAG_GETTER
#undef MUTABLE_FLAG_GETTER_SETTER

View File

@ -1004,25 +1004,6 @@ static bool intrinsic_SetCanonicalName(JSContext* cx, unsigned argc,
return true;
}
static bool intrinsic_SetIsInlinableLargeFunction(JSContext* cx, unsigned argc,
Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 1);
RootedFunction fun(cx, &args[0].toObject().as<JSFunction>());
MOZ_ASSERT(fun->isSelfHostedBuiltin());
// _SetIsInlinableLargeFunction can only be called on top-level function
// declarations.
MOZ_ASSERT(fun->kind() == FunctionFlags::NormalFunction);
MOZ_ASSERT(!fun->isLambda());
fun->baseScript()->setIsInlinableLargeFunction();
args.rval().setUndefined();
return true;
}
static bool intrinsic_GeneratorObjectIsClosed(JSContext* cx, unsigned argc,
Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
@ -2343,8 +2324,6 @@ static const JSFunctionSpec intrinsic_functions[] = {
CallNonGenericSelfhostedMethod<Is<ArrayIteratorObject>>, 2, 0),
JS_FN("_SetCanonicalName", intrinsic_SetCanonicalName, 2, 0),
JS_FN("_SetIsInlinableLargeFunction", intrinsic_SetIsInlinableLargeFunction,
1, 0),
JS_INLINABLE_FN("GuardToArrayIterator",
intrinsic_GuardToBuiltin<ArrayIteratorObject>, 1, 0,

View File

@ -250,6 +250,10 @@ enum class ImmutableScriptFlagsEnum : uint32_t {
// is set independently of whether we actually use an `arguments` binding. The
// conditions are specified in the ECMAScript spec.
HasMappedArgsObj = 1 << 28,
// Large self-hosted methods that should be inlined anyway by the JIT for
// performance reasons can be marked with this flag.
IsInlinableLargeFunction = 1 << 29,
};
enum class MutableScriptFlagsEnum : uint32_t {
@ -300,9 +304,7 @@ enum class MutableScriptFlagsEnum : uint32_t {
// has failed.
Uninlineable = 1 << 19,
// Large self-hosted methods that should be inlined anyway by the JIT for
// performance reasons can be marked with this flag.
IsInlinableLargeFunction = 1 << 20,
// (1 << 20) is unused.
// *****************************************************************
// The flags below are set when we bail out and invalidate a script.