Skip to content

hir: anon-shape literals mint Any-typed fields, so {v: number, w: number} is declared to the GC as two POINTER slots #7544

Description

@proggeramlug

Found while landing #7532 (typed-shape layout declared at allocation).

The finding

HIR rewrites a closed-shape object literal into new __AnonShape_<hash>(…), and that synthesized constructor is a qualifying prologue for #7532's declaration gate. It still does not qualify — because the minted class's field types come out as Any, not Number.

Any is pointer-bearing, so class_layout_declarable_at_allocation rejects it, correctly.

But the consequence is bigger than one missed optimization:

{v: number, w: number} is currently declared to the collector as two POINTER slots.

Every closed-shape numeric literal — the single most common allocation shape in the churn family and in most real JS — tells the GC it holds two pointers when it provably holds two doubles. That costs:

  • scanning: minor and full collections trace two slots per object that can never contain a reference;
  • the raw-f64 store path: there is no raw-f64 store to unlock at all, because the descriptor never says raw-f64;
  • layout bookkeeping: pointer slots take the mask-maintaining path rather than the pointer-free one.

Why this is a different lever from #7532

#7532 fixed an ordering problem — the layout was installed after the constructor, so stores inside it could not pass the intact-bit guard. That is now closed for declared classes: push_cls went 1.553×, and its promotion fell 210,488 → 64 bytes, matching what the equivalent literal already achieved.

This one is a type-propagation gap. The literal's element types are known at the site ({v: 1, w: 2} or {v: someNumber, w: otherNumber}) and are simply not carried into the minted class's ClassField::ty. Nothing about the ordering work reaches it.

Where to look

mint_anon_shape_class in perry-hir's lower/context.rs — what it writes into each synthesized ClassField::ty, and whether the literal's per-property inferred type is available at that point (the value-type analysis in analysis/value_types.rs already answers this for other consumers). The acceptance question is narrow: does a {v: number, w: number} literal end up with Number-typed fields and therefore a raw-f64 descriptor?

Acceptance

{v: 1, w: 2} constructs with a raw-f64 descriptor and a pointer-free layout, verified by the same measurements #7532 used: an IR census showing the stores no longer route through js_put_value_set, and PERRY_GC_DIAG promotion/copied bytes at or below the current numbers. Guard against the #6377 failure mode — more type visibility un-gates latent fast paths — with a full gap-suite run against a same-session main baseline.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions