Skip to content

[CLEANUP] Remove dead opcodes#21510

Closed
NullVoxPopuli-ai-agent wants to merge 1 commit into
emberjs:mainfrom
NullVoxPopuli-ai-agent:remove-dead-opcodes
Closed

[CLEANUP] Remove dead opcodes#21510
NullVoxPopuli-ai-agent wants to merge 1 commit into
emberjs:mainfrom
NullVoxPopuli-ai-agent:remove-dead-opcodes

Conversation

@NullVoxPopuli-ai-agent

Copy link
Copy Markdown
Contributor

Removes six opcodes that are vestigial on both sides of the compile → execute pipeline. Audit method: for every VM syscall op and wire format op, check (a) does the opcode compiler ever emit it, (b) does the runtime ever handle it, (c) is it referenced anywhere beyond its own definition.

VM opcodes

  • VM_POP_ARGS_OP (84) — never emitted by the opcode compiler and no runtime handler is registered for it; a program containing it would crash. Existed only as the constant, the VmPopArgs type, and a DEBUG metadata entry.
  • VM_JUMP_IF_OP (65) — a runtime handler was registered, but the compiler never emits it: all conditionals compile through JumpUnless/JumpEq (with ToBoolean). Removing it drops a live, non-tree-shakeable handler from the append-opcodes table.

Wire format opcodes

  • StrictModifier (5), StrictBlock (7), DynamicArg (20), StaticArg (21) — never produced by the wire format encoder (modifiers/blocks encode as plain Modifier/Block in both strict and loose mode; component args encode inline as hashes) and never consumed by the opcode compiler (no STATEMENTS/EXPRESSIONS handler exists — if one ever appeared in a program, compilation would fail). Their only references were the opcode table, the interface types (Statements.Argument, isArgument), and the wire-format debug printer. Since the wire format is version-locked — templates must be compiled by the matching compiler — removing never-emitted opcodes cannot affect anything in the wild.

Opcode numbers are not reused; the freed numbers become gaps alongside the existing ones (93–94, 101–102, 104), so no renumbering churn.

Not touched here: WithDynamicVars/GetDynamicVar and their VM counterparts are live code whose removal is already proposed separately in #21485.

Net −71 lines. Verified with tsc --noEmit, eslint on all touched packages, and browser test sweeps (jit 1690 tests, component 1689 tests — all passing).

🤖 Generated with Claude Code

Removes opcodes that are vestigial on both sides of the pipeline:

* `VM_POP_ARGS_OP` (84): never emitted by the opcode compiler and has
  no runtime handler registered — a program containing it would crash.

* `VM_JUMP_IF_OP` (65): a runtime handler was registered, but the
  compiler never emits it; all conditionals compile through
  `JumpUnless`/`JumpEq`. Removing it drops a live (non-tree-shakeable)
  handler from the append-opcodes table.

* Wire format `StrictModifier` (5), `StrictBlock` (7), `DynamicArg`
  (20), `StaticArg` (21): never produced by the wire format encoder
  and never consumed by the opcode compiler; modifiers and blocks
  encode as plain `Modifier`/`Block` in both modes, and component args
  are encoded inline as hashes. Their only references were the opcode
  table itself, the interface types, and the wire-format debug
  printer. The wire format is version-locked (templates must be
  compiled by the matching compiler), so removing never-emitted
  opcodes is safe.

Opcode numbers are not reused; the freed numbers just become gaps like
the existing ones (93-94, 101-102, 104).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@NullVoxPopuli-ai-agent

Copy link
Copy Markdown
Contributor Author

Some archaeology on how these six opcodes became dead, from glimmer-vm history (where these packages lived before vendoring):

VM_JUMP_IF_OP (65)

Dates back to the original {{#if}} inlining (glimmerjs/glimmer-vm@5fcd6da23, Nov 2015). Its last real job was compiling {{#unless}}: evaluate the condition, ToBoolean, then jumpIf('ELSE') to skip the main block when truthy ({{#if}} always used the mirror op, JumpUnless). It stopped being emitted in glimmerjs/glimmer-vm@8f5c02152 (Feb 2018), which introduced the structured tryIf builder helper — emitting only JumpUnless — and reformulated unless as an if with swapped branches. The runtime handler has been registered-but-unreachable for eight years since.

VM_POP_ARGS_OP (84)

Lived four days. Added in glimmerjs/glimmer-vm@c1ff46597 (Sep 8, 2017) to pop + clear the Arguments object after static component invocations; removed — builder method, handler, and metadata — in glimmerjs/glimmer-vm@19340c9e8 (Sep 12, 2017) when frames took over argument cleanup. The enum entry was left behind, and the Dec 2018 opcode-TOML metadata generator (glimmerjs/glimmer-vm@b442d4e75) then faithfully cataloged [syscall.argspop] and began regenerating DEBUG metadata for an op with no emitter and no handler, which every later refactor carried along.

StaticArg (21) / DynamicArg (20)

Never actually wire format, even when alive: from 2016 on they were an intermediate representation inside the compiler. The JS compiler pushed ['static-arg', name, value] while walking a component's opening tag, and ComponentBlock partitioned them via isArgument and flattened them into the component's args: [keys, values] hash at serialization time — so they never appeared in final wire output, and no opcode-compiler or runtime consumer for the integer form ever existed in the repo's history. The producing code was deleted in the strict-mode precompiler rewrite (glimmerjs/glimmer-vm@8e11b9116, Sep 2020), which encodes component args directly as hashes; the types, isArgument, and debug-printer cases have lingered since.

StrictModifier (5) / StrictBlock (7)

Born dead. Reserved in glimmerjs/glimmer-vm@4c56c2164 (Jul 2019, "Lay groundwork for lexical scope") as anticipated strict-mode variants of Modifier/Block. git log -S 'SexpOpcodes.StrictModifier' returns zero commits — nothing ever emitted or consumed them. When strict mode shipped in the 2020 precompiler it reused plain Modifier/Block for both modes, and the reserved slots were forgotten.


The common pattern: each op died during a compiler-architecture rewrite (2017 frames, 2018 builder helpers, 2020 strict-mode precompiler) that replaced the mechanism but left the opcode registry — enums, types, TOML metadata — intact, and later tooling preserved the corpses. This PR is the first pass auditing the registry against actual emit/handle sites.

🤖 Generated with Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants