Skip to content

Remove dead opcodes#21515

Merged
NullVoxPopuli merged 1 commit into
mainfrom
remove-dead-opcodes
Jul 24, 2026
Merged

Remove dead opcodes#21515
NullVoxPopuli merged 1 commit into
mainfrom
remove-dead-opcodes

Conversation

@NullVoxPopuli

@NullVoxPopuli NullVoxPopuli commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Human notes

From bot exploration here:

It does, indeed, look like this stuff is unused. Is good to drop code we aren't using, as all code is liability.


AI Generated PR Summary and History

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).


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

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>
@github-actions

Copy link
Copy Markdown
Contributor

📊 Size report

Tarball size1.2 MB1.2 MB

dist/dev   -0.03%↓

File Before (Size / Brotli) After (Size / Brotli)
./packages/@glimmer/wire-format/index.js 1.5 kB / 519 B -7.77%↓1.3 kB / -5.59%↓490 B
./packages/shared-chunks/arguments-{hash}.js 62.6 kB / 12.2 kB -0.6%↓62.2 kB / -0.22%↓12.2 kB
Total (Includes all files) 2.1 MB / 498 kB -0.03%↓2.1 MB / -0.01%↓498 kB

dist/prod   -0.03%↓

File Before (Size / Brotli) After (Size / Brotli)
./packages/@glimmer/wire-format/index.js 1.5 kB / 519 B -7.77%↓1.3 kB / -5.59%↓490 B
./packages/shared-chunks/arguments-{hash}.js 58.4 kB / 11.3 kB -0.64%↓58 kB / 11.3 kB
Total (Includes all files) 1.9 MB / 455.4 kB -0.03%↓1.9 MB / 0%↑455.4 kB

smoke-tests/v2-app-template/dist   -0.04%↓

File Before (Size / Brotli) After (Size / Brotli)
./assets/api-{hash}.js 287.9 kB / 77.5 kB -0.05%↓287.8 kB / 0.07%↑77.5 kB
Total (Includes all files) 351.2 kB / 97.8 kB -0.04%↓351.1 kB / 0.07%↑97.9 kB

smoke-tests/v2-app-hello-world-template/dist   -0.11%↓

File Before (Size / Brotli) After (Size / Brotli)
./assets/main-{hash}.js 132.2 kB / 36.9 kB -0.11%↓132.1 kB / 0.2%↑37 kB
Total (Includes all files) 132.6 kB / 37 kB -0.11%↓132.4 kB / 0.3%↑37.1 kB

🤖 This report was automatically generated by wyvox/pkg-size

@NullVoxPopuli

Copy link
Copy Markdown
Contributor Author

no difference in perf in running pnpm bench

@NullVoxPopuli
NullVoxPopuli merged commit 0da7476 into main Jul 24, 2026
96 checks passed
@NullVoxPopuli
NullVoxPopuli deleted the remove-dead-opcodes branch July 24, 2026 02:49
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.

3 participants