Bug 1718102 - Abstract CompilationInput lazy fields behind accessors. r=arai

This change adds accessors to reach the `BaseScript* lazy` field as well as all
information taken from it. The intent being that most information used from the
BaseScript are taken out of the ScriptStencil and ScriptStencilExtra structures,
and that we can later use these in-place where the lazy field is used.

Differential Revision: https://phabricator.services.mozilla.com/D119106
This commit is contained in:
Nicolas B. Pierron 2021-07-07 18:01:05 +00:00
Родитель 27213e21b9
Коммит 7e30d9ee03
7 изменённых файлов: 97 добавлений и 69 удалений

Просмотреть файл

@ -1007,22 +1007,15 @@ static bool CompileLazyFunction(JSContext* cx, CompilationInput& input,
const Unit* units, size_t length) { const Unit* units, size_t length) {
MOZ_ASSERT(input.source); MOZ_ASSERT(input.source);
MOZ_ASSERT(cx->compartment() == input.lazy->compartment());
// We can only compile functions whose parents have previously been
// compiled, because compilation requires full information about the
// function's immediately enclosing scope.
MOZ_ASSERT(input.lazy->isReadyForDelazification());
AutoAssertReportedException assertException(cx); AutoAssertReportedException assertException(cx);
Rooted<JSFunction*> fun(cx, input.lazy->function()); Rooted<JSFunction*> fun(cx, input.function());
InheritThis inheritThis = fun->isArrow() ? InheritThis::Yes : InheritThis::No; InheritThis inheritThis = fun->isArrow() ? InheritThis::Yes : InheritThis::No;
LifoAllocScope allocScope(&cx->tempLifoAlloc()); LifoAllocScope allocScope(&cx->tempLifoAlloc());
CompilationState compilationState(cx, allocScope, input); CompilationState compilationState(cx, allocScope, input);
compilationState.setFunctionKey(input.lazy); compilationState.setFunctionKey(input.extent());
MOZ_ASSERT(!compilationState.isInitialStencil()); MOZ_ASSERT(!compilationState.isInitialStencil());
if (!compilationState.init(cx, inheritThis)) { if (!compilationState.init(cx, inheritThis)) {
return false; return false;
@ -1037,8 +1030,8 @@ static bool CompileLazyFunction(JSContext* cx, CompilationInput& input,
} }
FunctionNode* pn = parser.standaloneLazyFunction( FunctionNode* pn = parser.standaloneLazyFunction(
fun, input.lazy->toStringStart(), input.lazy->strict(), fun, input.extent().toStringStart, input.strict(), input.generatorKind(),
input.lazy->generatorKind(), input.lazy->asyncKind()); input.asyncKind());
if (!pn) { if (!pn) {
return false; return false;
} }
@ -1055,15 +1048,15 @@ static bool CompileLazyFunction(JSContext* cx, CompilationInput& input,
// NOTE: Only allow relazification if there was no lazy PrivateScriptData. // NOTE: Only allow relazification if there was no lazy PrivateScriptData.
// This excludes non-leaf functions and all script class constructors. // This excludes non-leaf functions and all script class constructors.
bool hadLazyScriptData = input.lazy->hasPrivateScriptData(); bool hadLazyScriptData = input.hasPrivateScriptData();
bool isRelazifiableAfterDelazify = input.lazy->isRelazifiableAfterDelazify(); bool isRelazifiableAfterDelazify = input.isRelazifiable();
if (isRelazifiableAfterDelazify && !hadLazyScriptData) { if (isRelazifiableAfterDelazify && !hadLazyScriptData) {
compilationState.scriptData[CompilationStencil::TopLevelIndex] compilationState.scriptData[CompilationStencil::TopLevelIndex]
.setAllowRelazify(); .setAllowRelazify();
} }
mozilla::DebugOnly<uint32_t> lazyFlags = mozilla::DebugOnly<uint32_t> lazyFlags =
static_cast<uint32_t>(input.lazy->immutableFlags()); static_cast<uint32_t>(input.immutableFlags());
Rooted<CompilationGCOutput> gcOutput(cx); Rooted<CompilationGCOutput> gcOutput(cx);
{ {
@ -1129,7 +1122,7 @@ static bool DelazifyCanonicalScriptedFunctionImpl(JSContext* cx,
.setSelfHostingMode(false); .setSelfHostingMode(false);
Rooted<CompilationInput> input(cx, CompilationInput(options)); Rooted<CompilationInput> input(cx, CompilationInput(options));
input.get().initFromLazy(lazy, ss); input.get().initFromLazy(cx, lazy, ss);
return CompileLazyFunction(cx, input.get(), units.get(), sourceLength); return CompileLazyFunction(cx, input.get(), units.get(), sourceLength);
} }

Просмотреть файл

@ -210,7 +210,8 @@ struct CompilationAtomCache {
}; };
// Input of the compilation, including source and enclosing context. // Input of the compilation, including source and enclosing context.
struct CompilationInput { struct CompilationInput
: public ImmutableScriptFlagsAccessors<CompilationInput> {
enum class CompilationTarget { enum class CompilationTarget {
Global, Global,
SelfHosting, SelfHosting,
@ -226,8 +227,10 @@ struct CompilationInput {
CompilationAtomCache atomCache; CompilationAtomCache atomCache;
BaseScript* lazy = nullptr; private:
BaseScript* lazy_ = nullptr;
public:
RefPtr<ScriptSource> source; RefPtr<ScriptSource> source;
// * If the target is Global, null. // * If the target is Global, null.
@ -303,11 +306,17 @@ struct CompilationInput {
return true; return true;
} }
void initFromLazy(BaseScript* lazyScript, ScriptSource* ss) { void initFromLazy(JSContext* cx, BaseScript* lazyScript, ScriptSource* ss) {
MOZ_ASSERT(cx->compartment() == lazyScript->compartment());
// We can only compile functions whose parents have previously been
// compiled, because compilation requires full information about the
// function's immediately enclosing scope.
MOZ_ASSERT(lazyScript->isReadyForDelazification());
target = CompilationTarget::Delazification; target = CompilationTarget::Delazification;
lazy = lazyScript; lazy_ = lazyScript;
source = ss; source = ss;
enclosingScope = lazy->function()->enclosingScope(); enclosingScope = lazy_->function()->enclosingScope();
} }
// Returns true if enclosingScope field is provided to init* function, // Returns true if enclosingScope field is provided to init* function,
@ -327,6 +336,37 @@ struct CompilationInput {
return nullptr; return nullptr;
} }
// FullParseHandler needs a BaseScript to find the closed-over-binding index,
// as well as to walk over the inner functions references with
// skipLazyInnerFunctions.
BaseScript* lazyOuterScript() {
MOZ_ASSERT(isInitialStencil() == !lazy_);
return lazy_;
}
// When compiling a lazy function, this is needed to initialize the
// FunctionBox as well as the CompilationState.
JSFunction* function() { return lazy_->function(); }
// When compiling an inner function, we want to know the unique identifier
// which identify a function. This is computed from the source extend.
const SourceExtent& extent() const { return lazy_->extent(); }
// See `BaseScript::immutableFlags_`.
ImmutableScriptFlags immutableFlags() const {
return lazy_->immutableFlags();
}
bool hasPrivateScriptData() const {
// This is equivalent to: ngcthings != 0 || useMemberInitializers()
// See BaseScript::CreateRawLazy.
return lazy_->hasPrivateScriptData();
}
// Whether this CompilationInput is parsing the top-level of a script, or
// false if we are parsing an inner function.
bool isInitialStencil() { return !lazy_; }
void trace(JSTracer* trc); void trace(JSTracer* trc);
// Size of dynamic data. Note that GC data is counted by GC and not here. We // Size of dynamic data. Note that GC data is counted by GC and not here. We
@ -735,8 +775,8 @@ struct ExtensibleCompilationStencil {
return *this; return *this;
} }
void setFunctionKey(BaseScript* lazy) { void setFunctionKey(const SourceExtent& extent) {
functionKey = CompilationStencil::toFunctionKey(lazy->extent()); functionKey = CompilationStencil::toFunctionKey(extent);
} }
bool isInitialStencil() const { bool isInitialStencil() const {

Просмотреть файл

@ -174,7 +174,8 @@ NameLocation EmitterScope::searchAndCache(BytecodeEmitter* bce,
// If the name is not found in the current compilation, walk the Scope // If the name is not found in the current compilation, walk the Scope
// chain encompassing the compilation. // chain encompassing the compilation.
if (!loc) { if (!loc) {
MOZ_ASSERT(bce->compilationState.input.lazy || MOZ_ASSERT(bce->compilationState.input.target ==
CompilationInput::CompilationTarget::Delazification ||
bce->compilationState.input.target == bce->compilationState.input.target ==
CompilationInput::CompilationTarget::Eval); CompilationInput::CompilationTarget::Eval);
inCurrentScript = false; inCurrentScript = false;

Просмотреть файл

@ -199,10 +199,10 @@ PerHandlerParser<ParseHandler>::PerHandlerParser(
CompilationState& compilationState, void* internalSyntaxParser) CompilationState& compilationState, void* internalSyntaxParser)
: ParserBase(cx, options, foldConstants, compilationState), : ParserBase(cx, options, foldConstants, compilationState),
handler_(cx, compilationState.allocScope.alloc(), handler_(cx, compilationState.allocScope.alloc(),
compilationState.input.lazy), compilationState.input.lazyOuterScript()),
internalSyntaxParser_(internalSyntaxParser) { internalSyntaxParser_(internalSyntaxParser) {
MOZ_ASSERT(compilationState.isInitialStencil() == MOZ_ASSERT(compilationState.isInitialStencil() ==
!compilationState.input.lazy); compilationState.input.isInitialStencil());
} }
template <class ParseHandler, typename Unit> template <class ParseHandler, typename Unit>

Просмотреть файл

@ -642,7 +642,7 @@ bool CompilationInput::initForStandaloneFunctionInNonSyntacticScope(
void CompilationInput::trace(JSTracer* trc) { void CompilationInput::trace(JSTracer* trc) {
atomCache.trace(trc); atomCache.trace(trc);
TraceNullableRoot(trc, &lazy, "compilation-input-lazy"); TraceNullableRoot(trc, &lazy_, "compilation-input-lazy");
TraceNullableRoot(trc, &enclosingScope, "compilation-input-enclosing-scope"); TraceNullableRoot(trc, &enclosingScope, "compilation-input-enclosing-scope");
} }
@ -1131,8 +1131,8 @@ static bool InstantiateTopLevel(JSContext* cx, CompilationInput& input,
MOZ_ASSERT(stencil.sharedData.get(CompilationStencil::TopLevelIndex)); MOZ_ASSERT(stencil.sharedData.get(CompilationStencil::TopLevelIndex));
if (!stencil.isInitialStencil()) { if (!stencil.isInitialStencil()) {
MOZ_ASSERT(input.lazy); MOZ_ASSERT(input.lazyOuterScript());
RootedScript script(cx, JSScript::CastFromLazy(input.lazy)); RootedScript script(cx, JSScript::CastFromLazy(input.lazyOuterScript()));
if (!JSScript::fullyInitFromStencil(cx, input, stencil, gcOutput, script, if (!JSScript::fullyInitFromStencil(cx, input, stencil, gcOutput, script,
CompilationStencil::TopLevelIndex)) { CompilationStencil::TopLevelIndex)) {
return false; return false;
@ -1325,9 +1325,9 @@ static void AssertDelazificationFieldsMatch(const CompilationStencil& stencil,
static void FunctionsFromExistingLazy(CompilationInput& input, static void FunctionsFromExistingLazy(CompilationInput& input,
CompilationGCOutput& gcOutput) { CompilationGCOutput& gcOutput) {
MOZ_ASSERT(gcOutput.functions.empty()); MOZ_ASSERT(gcOutput.functions.empty());
gcOutput.functions.infallibleAppend(input.lazy->function()); gcOutput.functions.infallibleAppend(input.function());
for (JS::GCCellPtr elem : input.lazy->gcthings()) { for (JS::GCCellPtr elem : input.lazyOuterScript()->gcthings()) {
if (!elem.is<JSObject>()) { if (!elem.is<JSObject>()) {
continue; continue;
} }
@ -1355,7 +1355,7 @@ bool CompilationStencil::instantiateStencilAfterPreparation(
// Distinguish between the initial (possibly lazy) compile and any subsequent // Distinguish between the initial (possibly lazy) compile and any subsequent
// delazification compiles. Delazification will update existing GC things. // delazification compiles. Delazification will update existing GC things.
bool isInitialParse = stencil.isInitialStencil(); bool isInitialParse = stencil.isInitialStencil();
MOZ_ASSERT(stencil.isInitialStencil() == !input.lazy); MOZ_ASSERT(stencil.isInitialStencil() == input.isInitialStencil());
// Phase 1: Instantate JSAtoms. // Phase 1: Instantate JSAtoms.
if (!InstantiateAtoms(cx, input, stencil)) { if (!InstantiateAtoms(cx, input, stencil)) {
@ -1392,7 +1392,7 @@ bool CompilationStencil::instantiateStencilAfterPreparation(
// specific lazy script. It is not used by instantiation, but we should // specific lazy script. It is not used by instantiation, but we should
// ensure it is correctly defined. // ensure it is correctly defined.
MOZ_ASSERT(stencil.functionKey == MOZ_ASSERT(stencil.functionKey ==
CompilationStencil::toFunctionKey(input.lazy->extent())); CompilationStencil::toFunctionKey(input.extent()));
FunctionsFromExistingLazy(input, gcOutput); FunctionsFromExistingLazy(input, gcOutput);
MOZ_ASSERT(gcOutput.functions.length() == stencil.scriptData.size()); MOZ_ASSERT(gcOutput.functions.length() == stencil.scriptData.size());

Просмотреть файл

@ -39,7 +39,6 @@
#include "vm/BytecodeIterator.h" #include "vm/BytecodeIterator.h"
#include "vm/BytecodeLocation.h" #include "vm/BytecodeLocation.h"
#include "vm/BytecodeUtil.h" #include "vm/BytecodeUtil.h"
#include "vm/GeneratorAndAsyncKind.h" // GeneratorKind, FunctionAsyncKind
#include "vm/JSAtom.h" #include "vm/JSAtom.h"
#include "vm/NativeObject.h" #include "vm/NativeObject.h"
#include "vm/ScopeKind.h" // ScopeKind #include "vm/ScopeKind.h" // ScopeKind
@ -1568,7 +1567,7 @@ class BaseScript : public gc::TenuredCellWithNonGCPointer<uint8_t>,
} }
uint32_t toStringStart() const { return extent_.toStringStart; } uint32_t toStringStart() const { return extent_.toStringStart; }
uint32_t toStringEnd() const { return extent_.toStringEnd; } uint32_t toStringEnd() const { return extent_.toStringEnd; }
SourceExtent extent() const { return extent_; } const SourceExtent& extent() const { return extent_; }
[[nodiscard]] bool appendSourceDataForToString(JSContext* cx, [[nodiscard]] bool appendSourceDataForToString(JSContext* cx,
js::StringBuffer& buf); js::StringBuffer& buf);
@ -1581,16 +1580,6 @@ class BaseScript : public gc::TenuredCellWithNonGCPointer<uint8_t>,
const MutableScriptFlags& mutableFlags() const { return mutableFlags_; } const MutableScriptFlags& mutableFlags() const { return mutableFlags_; }
MutableScriptFlags& mutableFlags() { return mutableFlags_; } MutableScriptFlags& mutableFlags() { return mutableFlags_; }
GeneratorKind generatorKind() const {
return isGenerator() ? GeneratorKind::Generator
: GeneratorKind::NotGenerator;
}
FunctionAsyncKind asyncKind() const {
return isAsync() ? FunctionAsyncKind::AsyncFunction
: FunctionAsyncKind::SyncFunction;
}
bool hasEnclosingScript() const { return warmUpData_.isEnclosingScript(); } bool hasEnclosingScript() const { return warmUpData_.isEnclosingScript(); }
BaseScript* enclosingScript() const { BaseScript* enclosingScript() const {
return warmUpData_.toEnclosingScript(); return warmUpData_.toEnclosingScript();
@ -1698,24 +1687,6 @@ class BaseScript : public gc::TenuredCellWithNonGCPointer<uint8_t>,
static constexpr size_t offsetOfWarmUpData() { static constexpr size_t offsetOfWarmUpData() {
return offsetof(BaseScript, warmUpData_); return offsetof(BaseScript, warmUpData_);
} }
protected:
bool isRelazifiableImpl() const {
// A script may not be relazifiable if parts of it can be entrained in
// interesting ways:
// - Scripts with inner-functions or direct-eval (which can add
// inner-functions) should not be relazified as their Scopes may be part
// of another scope-chain.
// - Generators and async functions may be re-entered in complex ways so
// don't discard bytecode. The JIT resume code assumes this.
// - Functions with template literals must always return the same object
// instance so must not discard it by relazifying.
return !hasInnerFunctions() && !hasDirectEval() && !isGenerator() &&
!isAsync() && !hasCallSiteObj();
}
public:
bool isRelazifiableAfterDelazify() const { return isRelazifiableImpl(); }
}; };
/* /*
@ -1941,8 +1912,6 @@ class JSScript : public js::BaseScript {
void updateJitCodeRaw(JSRuntime* rt); void updateJitCodeRaw(JSRuntime* rt);
bool isRelazifiable() const { return isRelazifiableImpl(); }
js::ModuleObject* module() const; js::ModuleObject* module() const;
bool isGlobalCode() const; bool isGlobalCode() const;

Просмотреть файл

@ -22,11 +22,12 @@
#include "frontend/SourceNotes.h" // js::SrcNote #include "frontend/SourceNotes.h" // js::SrcNote
#include "frontend/TypedIndex.h" // js::frontend::TypedIndex #include "frontend/TypedIndex.h" // js::frontend::TypedIndex
#include "js/AllocPolicy.h" // js::SystemAllocPolicy #include "js/AllocPolicy.h" // js::SystemAllocPolicy
#include "js/TypeDecls.h" // JSContext,jsbytecode #include "js/TypeDecls.h" // JSContext,jsbytecode
#include "js/UniquePtr.h" // js::UniquePtr #include "js/UniquePtr.h" // js::UniquePtr
#include "util/EnumFlags.h" // js::EnumFlags #include "util/EnumFlags.h" // js::EnumFlags
#include "util/TrailingArray.h" // js::TrailingArray #include "util/TrailingArray.h" // js::TrailingArray
#include "vm/GeneratorAndAsyncKind.h" // GeneratorKind, FunctionAsyncKind
#include "vm/StencilEnums.h" // js::{TryNoteKind,ImmutableScriptFlagsEnum,MutableScriptFlagsEnum} #include "vm/StencilEnums.h" // js::{TryNoteKind,ImmutableScriptFlagsEnum,MutableScriptFlagsEnum}
// //
@ -290,6 +291,30 @@ struct ImmutableScriptFlagsAccessors {
#undef IMMUTABLE_FLAG_GETTER #undef IMMUTABLE_FLAG_GETTER
#undef FLAG_GETTER #undef FLAG_GETTER
GeneratorKind generatorKind() const {
return isGenerator() ? GeneratorKind::Generator
: GeneratorKind::NotGenerator;
}
FunctionAsyncKind asyncKind() const {
return isAsync() ? FunctionAsyncKind::AsyncFunction
: FunctionAsyncKind::SyncFunction;
}
bool isRelazifiable() const {
// A script may not be relazifiable if parts of it can be entrained in
// interesting ways:
// - Scripts with inner-functions or direct-eval (which can add
// inner-functions) should not be relazified as their Scopes may be part
// of another scope-chain.
// - Generators and async functions may be re-entered in complex ways so
// don't discard bytecode. The JIT resume code assumes this.
// - Functions with template literals must always return the same object
// instance so must not discard it by relazifying.
return !hasInnerFunctions() && !hasDirectEval() && !isGenerator() &&
!isAsync() && !hasCallSiteObj();
}
}; };
template <typename Holder> template <typename Holder>