Open Bug 1529456 Opened 1 year ago Updated 2 days ago

[meta] Unify JSScript and LazyScript types


(Core :: JavaScript Engine, enhancement, P2)





(Reporter: tcampbell, Assigned: tcampbell)


(Depends on 9 open bugs, Blocks 2 open bugs)


(Keywords: meta)

Currently LazyScripts and JSScripts form a messy sort of partial bijection of pointers. Each LazyScript delazifies to a specific JSScript -- which may already exist. Some JSScripts can relazify back to a specific LazyScript that was our previous lazy form.

These types are two forms that a single abstract interpreted-script morphs between. I propose we unify them into a single type with a single pointer identity.

Looking at the contents of these structs, we see that 6 x uint32_t and 1 x GCPtr are directly duplicates when both scripts exists (non-lazy leaf functions). There is also a GCPtr in each type pointing to each other that would be shared. This would give some amount of memory savings on pages with lots of JS.

One concern of sharing types is that we might end up with the maximum size of both types. We can mitigate this by realizing that every JSScript has a required variable-length allocation for PrivateScriptData into which we would more many JSScript fields. A number of JSScript fields are also planned to move to SharedScriptData in Bug 1471062. In a LazyScript we have an optional |void* table_| for non-trivial cases where data could be stored. This brings us down to roughly three fields that may be candidates for a simple union.

The primary motivation for this change is to simplify the engines handling of script representation. The following are a few areas that simplify.

A JSFunction uses flags to decide if its current target is a LazyScript or JSScript and requires de-/re-lazification to update those bits. One very large wrinkle here is that function cloning can lead to many JSFunctions having a different (transient) notion of the current status. Currently there is a lot of complexity to keep this all working. When JSScript / LazyScript become fused, we can transfer the flag out of the JSFunction and confirm lazy-status to script itself. This should also allow non-leaf functions to safely lazify.

One big area of difficulty is that we have many script flags for various reasons -- parsing, embedding, runtime, optimization -- that exist in different forms and different subsets on LazyScript and JSScript. This is further compounded heavily by the multiple ways we create scripts: Parsing, BinAST, Cloning, XDR. This is even further exacerbated by GC/debugger/code-coverage using CellIters and accessing this constructs in partial states. Unsurprisingly, there have been many frustrating bugs around these flags and continue to be (I've found three more this week..). In the merged data type, JSScript::ImmutableFlags/MutableFlags are shared by both lazy and non-lazy states.

We will also be able to easily move jitCodeRaw_ to the shared structure and add an automatic delazification trampoline (similar to the interpreter one) for the JITs to use instead of de-optimizing.

Feedback welcome. Many of the patches I'm working on are worth landing regardless because they fix bugs and simplify existing code.

We are making good progress in this direction and I think the end result will shape up nicely. Here is the proposed unified JSScript data type. I leave in BaseScript the things that are trivially the same, and focus on the unions here.

class JSScript : BaseScript {
  // Maintain fast access to bytecode.
  RefPtr<js::RuntimeScriptData> scriptData_ = {};

  // The PrivateScriptData will maintain a pointer to the LazyScriptData on compile.
  union {
    js::LazyScriptData* lazyData_;
    js::PrivateScriptData* data_;

  // The script progresses through the following state changes:
  // 1) Cold. Enclosing script is still lazy.
  // 2) Lazy. Enclosing script is compiled, but we are still lazy.
  // 3) Warm-up. Running in C++ interpreter without ICs.
  // 4) JIT. Have a JitScript and run from BaselineInterpreter to BaselineCompiler to IonMonkey
  union {
    JSScript* enclosingScript_;
    js::Scope* enclosingScope_;
    uintptr_t warmUpCount_;
    js::jit::JitScript* jitScript_;
See Also: → 1568759
Depends on: 1505689
Depends on: 1571446
No longer blocks: 1529991
Depends on: 1529991
Depends on: 1583816
Depends on: 1587943
Depends on: 1587950
Depends on: 1587955
No longer blocks: 1589767
Depends on: 1589767
Depends on: 1589904
Summary: Consider merging JSScript and LazyScript types → [meta] Unify JSScript and LazyScript types
Depends on: 1591209
Depends on: 1591405
Depends on: 1591515
Depends on: 1591575
Depends on: 1591596
Depends on: 1591598
Depends on: 1591600
Depends on: 1591747
Depends on: 1592356
Depends on: 1599104

Tests are finally green when using a combined JSScript/LazyScript. There are some unexpected regressions to investigate and a couple JIT optimizations (around fun.length) where disabled temporarily. The end may finally be in sight.

Depends on: 1600439
Depends on: 1600705
Depends on: 1567157
Depends on: 1601963
Depends on: 1602222
Depends on: 1602480
Depends on: 1602878
Depends on: 1603218
Depends on: 1604064
Depends on: 1604069
Depends on: 1604179
Depends on: 1604215
Depends on: 1604288
See Also: → 1604766
Depends on: 1605591
Depends on: 1605648
Depends on: 1606113
You need to log in before you can comment on or make changes to this bug.