mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-18 07:45:30 +00:00
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:
parent
9e4dc813a7
commit
840a19df94
@ -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")
|
||||
|
@ -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() ||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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") \
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user