Bug 1615143 - Move JSScript::lazyScript / LazyScript::script_ to BaseScript. r=jandem

This patch creates a union field in BaseScript to hold either form of
pointer. An IsLazyScript flag is added to ImmutableFlags to know which union
arm to trace. We are also able to use a single trace function for both.
IsLazyScript flag to disambiguate the union arms.

Note that this field will be removed entirely once the JSScript and
LazyScript instances are merged.

Differential Revision: https://phabricator.services.mozilla.com/D62680

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Ted Campbell 2020-02-13 14:32:39 +00:00
Родитель cd00e1a950
Коммит 432b2a7b18
5 изменённых файлов: 60 добавлений и 59 удалений

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

@ -937,10 +937,13 @@ static void CheckFlagsOnDelazification(uint32_t lazy, uint32_t nonLazy) {
// These flags are computed for lazy scripts and may have a different // These flags are computed for lazy scripts and may have a different
// definition for non-lazy scripts. // definition for non-lazy scripts.
// //
// IsLazyScript: This flag will be removed in Bug 1529456.
//
// TreatAsRunOnce: Some conditions depend on parent context and are // TreatAsRunOnce: Some conditions depend on parent context and are
// computed during lazy parsing, while other conditions // computed during lazy parsing, while other conditions
// need to full parse. // need to full parse.
constexpr uint32_t CustomFlagsMask = constexpr uint32_t CustomFlagsMask =
uint32_t(BaseScript::ImmutableFlags::IsLazyScript) |
uint32_t(BaseScript::ImmutableFlags::TreatAsRunOnce); uint32_t(BaseScript::ImmutableFlags::TreatAsRunOnce);
// These flags are expected to match between lazy and full parsing. // These flags are expected to match between lazy and full parsing.

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

@ -4918,7 +4918,7 @@ void js::gc::SweepLazyScripts(GCParallelTask* task) {
for (SweepGroupZonesIter zone(task->gc); !zone.done(); zone.next()) { for (SweepGroupZonesIter zone(task->gc); !zone.done(); zone.next()) {
AutoSetThreadIsSweeping threadIsSweeping(zone); AutoSetThreadIsSweeping threadIsSweeping(zone);
for (auto i = zone->cellIter<LazyScript>(); !i.done(); i.next()) { for (auto i = zone->cellIter<LazyScript>(); !i.done(); i.next()) {
WeakHeapPtrScript* edge = &i.unbarrieredGet()->script_; WeakHeapPtrScript* edge = &i.unbarrieredGet()->u.script_;
if (*edge && IsAboutToBeFinalized(edge)) { if (*edge && IsAboutToBeFinalized(edge)) {
*edge = nullptr; *edge = nullptr;
} }

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

@ -750,20 +750,15 @@ struct ImplicitEdgeHolderType {
// For now, we only handle JSObject* and JSScript* keys, but the linear time // For now, we only handle JSObject* and JSScript* keys, but the linear time
// algorithm can be easily extended by adding in more types here, then making // algorithm can be easily extended by adding in more types here, then making
// GCMarker::traverse<T> call markPotentialEphemeronKey. // GCMarker::traverse<T> call markImplicitEdges.
template <> template <>
struct ImplicitEdgeHolderType<JSObject*> { struct ImplicitEdgeHolderType<JSObject*> {
typedef JSObject* Type; typedef JSObject* Type;
}; };
template <> template <>
struct ImplicitEdgeHolderType<JSScript*> { struct ImplicitEdgeHolderType<BaseScript*> {
typedef JSScript* Type; typedef BaseScript* Type;
};
template <>
struct ImplicitEdgeHolderType<LazyScript*> {
typedef LazyScript* Type;
}; };
void GCMarker::markEphemeronValues(gc::Cell* markedCell, void GCMarker::markEphemeronValues(gc::Cell* markedCell,
@ -809,8 +804,7 @@ void GCMarker::markImplicitEdges(T* thing) {
} }
template void GCMarker::markImplicitEdges(JSObject*); template void GCMarker::markImplicitEdges(JSObject*);
template void GCMarker::markImplicitEdges(JSScript*); template void GCMarker::markImplicitEdges(BaseScript*);
template void GCMarker::markImplicitEdges(LazyScript*);
} // namespace js } // namespace js
@ -1135,19 +1129,24 @@ void BaseScript::traceChildren(JSTracer* trc) {
DebugAPI::traceDebugScript(trc, script); DebugAPI::traceDebugScript(trc, script);
} }
} }
}
void LazyScript::traceChildren(JSTracer* trc) {
BaseScript::traceChildren(trc);
// Trace the edge to our twin. Note that it will have the opposite type of
// current script.
if (isLazyScript()) {
if (trc->traceWeakEdges()) { if (trc->traceWeakEdges()) {
TraceNullableEdge(trc, &script_, "script"); TraceNullableEdge(trc, &u.script_, "script");
}
} else {
if (u.lazyScript) {
TraceManuallyBarrieredEdge(trc, &u.lazyScript, "lazyScript");
}
} }
if (trc->isMarkingTracer()) { if (trc->isMarkingTracer()) {
GCMarker::fromTracer(trc)->markImplicitEdges(this); GCMarker::fromTracer(trc)->markImplicitEdges(this);
} }
} }
inline void js::GCMarker::eagerlyMarkChildren(LazyScript* thing) { inline void js::GCMarker::eagerlyMarkChildren(LazyScript* thing) {
traverseEdge(thing, static_cast<JSObject*>(thing->functionOrGlobal_)); traverseEdge(thing, static_cast<JSObject*>(thing->functionOrGlobal_));
traverseEdge(thing, static_cast<JSObject*>(thing->sourceObject_)); traverseEdge(thing, static_cast<JSObject*>(thing->sourceObject_));

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

@ -5273,18 +5273,6 @@ void ScriptWarmUpData::trace(JSTracer* trc) {
} }
} }
void JSScript::traceChildren(JSTracer* trc) {
BaseScript::traceChildren(trc);
if (lazyScript) {
TraceManuallyBarrieredEdge(trc, &lazyScript, "lazyScript");
}
if (trc->isMarkingTracer()) {
GCMarker::fromTracer(trc)->markImplicitEdges(this);
}
}
size_t JSScript::calculateLiveFixed(jsbytecode* pc) { size_t JSScript::calculateLiveFixed(jsbytecode* pc) {
size_t nlivefixed = numAlwaysLiveFixedSlots(); size_t nlivefixed = numAlwaysLiveFixedSlots();
@ -5505,8 +5493,8 @@ bool JSScript::formalLivesInArgumentsObject(unsigned argSlot) {
void LazyScript::initScript(JSScript* script) { void LazyScript::initScript(JSScript* script) {
MOZ_ASSERT(script); MOZ_ASSERT(script);
MOZ_ASSERT(!script_.unbarrieredGet()); MOZ_ASSERT(!u.script_.unbarrieredGet());
script_.set(script); u.script_.set(script);
} }
/* static */ /* static */
@ -5536,6 +5524,11 @@ LazyScript* LazyScript::CreateRaw(JSContext* cx, uint32_t ngcthings,
return nullptr; return nullptr;
} }
// Mark this BaseScript as being a LazyScript and construct the appropriate
// union arm.
lazy->setFlag(ImmutableFlags::IsLazyScript);
new (&lazy->u.script_) WeakHeapPtrScript(nullptr);
// Allocate a PrivateScriptData if it will not be empty. Lazy class // Allocate a PrivateScriptData if it will not be empty. Lazy class
// constructors also need PrivateScriptData for field lists. // constructors also need PrivateScriptData for field lists.
if (ngcthings || fun->isClassConstructor()) { if (ngcthings || fun->isClassConstructor()) {

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

@ -2021,6 +2021,25 @@ class BaseScript : public gc::TenuredCell {
ScriptWarmUpData warmUpData_ = {}; ScriptWarmUpData warmUpData_ = {};
union TwinPointer {
// Information used to re-lazify a lazily-parsed interpreted function.
js::LazyScript* lazyScript;
// If non-nullptr, the script has been compiled and this is a forwarding
// pointer to the result. This is a weak pointer: after relazification, we
// can collect the script if there are no other pointers to it.
WeakHeapPtrScript script_;
// Default to the lazyScript union arm which is used by JSScripts. This
// corresponds to the default IsLazyScript flag being clear. Remember that a
// non-lazy script points *to* a LazyScript.
TwinPointer() : lazyScript(nullptr) {}
// The BaseScript uses a finalizer instead of a C++ destructor so this
// should never be run. We need to define to appease compiler though.
~TwinPointer() { MOZ_CRASH(); }
} u;
BaseScript(uint8_t* stubEntry, JSObject* functionOrGlobal, BaseScript(uint8_t* stubEntry, JSObject* functionOrGlobal,
ScriptSourceObject* sourceObject, uint32_t sourceStart, ScriptSourceObject* sourceObject, uint32_t sourceStart,
uint32_t sourceEnd, uint32_t toStringStart, uint32_t toStringEnd, uint32_t sourceEnd, uint32_t toStringStart, uint32_t toStringEnd,
@ -2131,6 +2150,10 @@ class BaseScript : public gc::TenuredCell {
// Whether this script contains a direct eval statement. // Whether this script contains a direct eval statement.
HasDirectEval = 1 << 27, HasDirectEval = 1 << 27,
// Whether this BaseScript is a LazyScript. This flag will be removed after
// LazyScript and JSScript are merged in Bug 1529456.
IsLazyScript = 1 << 28,
}; };
// Mutable flags typically store information about runtime or deoptimization // Mutable flags typically store information about runtime or deoptimization
@ -2376,6 +2399,7 @@ setterLevel: \
ShouldDeclareArguments) ShouldDeclareArguments)
IMMUTABLE_FLAG_GETTER(isFunction, IsFunction) IMMUTABLE_FLAG_GETTER(isFunction, IsFunction)
IMMUTABLE_FLAG_GETTER_SETTER_PUBLIC(hasDirectEval, HasDirectEval) IMMUTABLE_FLAG_GETTER_SETTER_PUBLIC(hasDirectEval, HasDirectEval)
IMMUTABLE_FLAG_GETTER(isLazyScript, IsLazyScript)
MUTABLE_FLAG_GETTER_SETTER(warnedAboutUndefinedProp, WarnedAboutUndefinedProp) MUTABLE_FLAG_GETTER_SETTER(warnedAboutUndefinedProp, WarnedAboutUndefinedProp)
MUTABLE_FLAG_GETTER_SETTER(hasRunOnce, HasRunOnce) MUTABLE_FLAG_GETTER_SETTER(hasRunOnce, HasRunOnce)
@ -2498,10 +2522,11 @@ setterLevel: \
return false; return false;
} }
protected:
void traceChildren(JSTracer* trc);
public: public:
friend class GCMarker;
friend void js::gc::SweepLazyScripts(GCParallelTask* task);
void traceChildren(JSTracer* trc);
void finalize(JSFreeOp* fop); void finalize(JSFreeOp* fop);
size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) { size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) {
@ -2565,14 +2590,6 @@ struct DeletePolicy<js::PrivateScriptData>
} /* namespace JS */ } /* namespace JS */
class JSScript : public js::BaseScript { class JSScript : public js::BaseScript {
private:
/* Information used to re-lazify a lazily-parsed interpreted function. */
js::LazyScript* lazyScript = nullptr;
//
// End of fields. Start methods.
//
private: private:
template <js::XDRMode mode> template <js::XDRMode mode>
friend js::XDRResult js::XDRScript(js::XDRState<mode>* xdr, friend js::XDRResult js::XDRScript(js::XDRState<mode>* xdr,
@ -2847,13 +2864,13 @@ class JSScript : public js::BaseScript {
// discard most JIT code before attempting relazification. // discard most JIT code before attempting relazification.
// - Specific subsystems (such as the Debugger) may disable scripts for // - Specific subsystems (such as the Debugger) may disable scripts for
// their own reasons. // their own reasons.
bool lazyAvailable = selfHosted() || lazyScript; bool lazyAvailable = selfHosted() || u.lazyScript;
return isRelazifiable() && lazyAvailable && !hasJitScript() && return isRelazifiable() && lazyAvailable && !hasJitScript() &&
!doNotRelazify(); !doNotRelazify();
} }
void setLazyScript(js::LazyScript* lazy) { lazyScript = lazy; } void setLazyScript(js::LazyScript* lazy) { u.lazyScript = lazy; }
js::LazyScript* maybeLazyScript() { return lazyScript; } js::LazyScript* maybeLazyScript() { return u.lazyScript; }
bool isModule() const { bool isModule() const {
MOZ_ASSERT(hasFlag(ImmutableFlags::IsModule) == MOZ_ASSERT(hasFlag(ImmutableFlags::IsModule) ==
@ -3187,8 +3204,6 @@ class JSScript : public js::BaseScript {
static const JS::TraceKind TraceKind = JS::TraceKind::Script; static const JS::TraceKind TraceKind = JS::TraceKind::Script;
void traceChildren(JSTracer* trc);
// A helper class to prevent relazification of the given function's script // A helper class to prevent relazification of the given function's script
// while it's holding on to it. This class automatically roots the script. // while it's holding on to it. This class automatically roots the script.
class AutoDelazify; class AutoDelazify;
@ -3231,12 +3246,6 @@ namespace js {
// Information about a script which may be (or has been) lazily compiled to // Information about a script which may be (or has been) lazily compiled to
// bytecode from its source. // bytecode from its source.
class LazyScript : public BaseScript { class LazyScript : public BaseScript {
// If non-nullptr, the script has been compiled and this is a forwarding
// pointer to the result. This is a weak pointer: after relazification, we
// can collect the script if there are no other pointers to it.
WeakHeapPtrScript script_ = nullptr;
friend void js::gc::SweepLazyScripts(GCParallelTask* task);
// The BaseScript::warmUpData_ field is used as follows: // The BaseScript::warmUpData_ field is used as follows:
// * LazyScript in which the script is nested. This case happens if the // * LazyScript in which the script is nested. This case happens if the
// enclosing script is lazily parsed and have never been compiled. // enclosing script is lazily parsed and have never been compiled.
@ -3370,11 +3379,11 @@ class LazyScript : public BaseScript {
void initScript(JSScript* script); void initScript(JSScript* script);
JSScript* maybeScript() { return script_; } JSScript* maybeScript() { return u.script_; }
const JSScript* maybeScriptUnbarriered() const { const JSScript* maybeScriptUnbarriered() const {
return script_.unbarrieredGet(); return u.script_.unbarrieredGet();
} }
bool hasScript() const { return bool(script_); } bool hasScript() const { return bool(u.script_); }
// Returns true if the enclosing script has ever been compiled. // Returns true if the enclosing script has ever been compiled.
// Once the enclosing script is compiled, the scope chain is created. // Once the enclosing script is compiled, the scope chain is created.
@ -3386,9 +3395,6 @@ class LazyScript : public BaseScript {
return warmUpData_.isEnclosingScope(); return warmUpData_.isEnclosingScope();
} }
friend class GCMarker;
void traceChildren(JSTracer* trc);
static const JS::TraceKind TraceKind = JS::TraceKind::LazyScript; static const JS::TraceKind TraceKind = JS::TraceKind::LazyScript;
}; };