Skip to content

bugc: emit tailcall transform context on TCO back-edge#217

Merged
gnidan merged 1 commit into
transform-contextfrom
compiler-emit-tailcall-transform
Jul 2, 2026
Merged

bugc: emit tailcall transform context on TCO back-edge#217
gnidan merged 1 commit into
transform-contextfrom
compiler-emit-tailcall-transform

Conversation

@gnidan

@gnidan gnidan commented Jul 2, 2026

Copy link
Copy Markdown
Member

Emits a transform: ["tailcall"] context on the TCO back-edge JUMP, using the transform schema added in #212.

The TCO back-edge JUMP already carries a flat context with both invoke (the new iteration's call) and return (the previous iteration's return). This adds a third sibling key, transform: ["tailcall"], marking the instruction as a tail-call-optimized back-edge.

Per #212's design, this is an additive annotation — it does not replace the invoke/return pair (which state the source-level facts), but tells debuggers the pair was realized as a TCO back-edge rather than a real frame push/pop, so they can avoid inventing a spurious frame. Consumers that ignore transform contexts still get a sound source-level view from invoke/return alone. Flat sibling-key composition (no gather) follows the convention #212 established.

Changes

  • evmgen/generation/control-flow/terminator.tsbuildTailCallJumpOptions widens the emitted context type to Return & Invoke & Transform and adds transform: ["tailcall"].
  • evmgen/optimizer-contexts.test.ts — asserts the back-edge JUMP carries transform containing "tailcall" at optimizer levels 2 and 3.

Verification

  • yarn build + full bugc suite green (406 passed, 22 pre-existing skips).
  • Drove an actual level-2 compile of the tail-recursive count program: runtime program emits exactly one back-edge JUMP with transform: ["tailcall"] + invoke (id count, code target patched to 0x78) + return.

The TCO back-edge JUMP already carries a flat context with both
invoke (the new iteration's call) and return (the previous
iteration's return). Add a third sibling key,
transform: ["tailcall"], marking the instruction as a
tail-call-optimized back-edge.

This is an additive annotation: it does not replace the
invoke/return pair (which state the source-level facts) but
tells debuggers the pair was realized as a TCO back-edge rather
than a real frame push/pop, so they can avoid inventing a
spurious frame. Consumers that ignore transform contexts still
get a sound source-level view from invoke/return alone.

Widens the emitted context type to Return & Invoke & Transform
and extends the optimizer-contexts test to assert the back-edge
JUMP carries transform containing "tailcall".
@github-actions

github-actions Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor
PR Preview Action v1.8.1
Preview removed because the pull request was closed.
2026-07-02 03:04 UTC

@gnidan gnidan merged commit 314b42c into transform-context Jul 2, 2026
4 checks passed
@gnidan gnidan deleted the compiler-emit-tailcall-transform branch July 2, 2026 03:00
gnidan added a commit that referenced this pull request Jul 2, 2026
bugc #217 emits the TCO back-edge as a single flat context object
(return + invoke + transform keys together), but the existing tests
only exercised the gather shape. Add flat-shape variants for transform
extraction, the isTailCall flag, and frame replacement, plus a guard
that stripping the marker (the #10 failure mode) drops tail-call
handling.
gnidan added a commit that referenced this pull request Jul 2, 2026
…n panels (#222)

* web: fix tracer-drawer opcodes/state panels not filling height

The trace panels live in a flex:1 grid whose implicit row was auto-
sized to content, so dragging the drawer taller left dead space below
the panels instead of growing them. Give the grid an explicit 1fr row
and min-height:0 (on the grid and its items) so both panels absorb the
added vertical space and scroll internally.

* web: add optimizer-level selector (O0/O2) to tracer drawer

The drawer hardcoded optimizer level 0. Add an O0/O2 toggle in the
drawer header that recompiles + retraces at the chosen level, so
readers can flip to level 2 and watch optimizer transforms (e.g. the
tailcall annotation on TCO back-edges) appear. compileAndTrace now
takes the level explicitly; a ref mirrors the state so the example-
load effect reads the current level without re-running on toggle.

* programs-react: cover the flat (production) TCO back-edge shape

bugc #217 emits the TCO back-edge as a single flat context object
(return + invoke + transform keys together), but the existing tests
only exercised the gather shape. Add flat-shape variants for transform
extraction, the isTailCall flag, and frame replacement, plus a guard
that stripping the marker (the #10 failure mode) drops tail-call
handling.

* web: dedupe call stack + tailcall render + right-column panels

- Reuse the shared, tailcall-aware buildCallStack / extractCallInfo /
  extractTransform from @ethdebug/programs-react via a thin adapter
  (bugc .debug.context -> ethdebug format shape); drop the drawer's
  inline call-stack builder and local extractCallInfo.
- Render the tail-call chip on the reused call-stack frame and a
  tail-call variant on the call-info banner.
- Right column (gnidan's picks 1/2/4): resolved variable values
  (name: value via pointer resolution), gas remaining + per-step delta,
  and a transform annotations panel with per-tag glosses. Sections are
  now collapsible.

* web: widen optimizer selector to O0/O1/O2/O3

gnidan's call: expose all four bugc optimizer levels (each distinct —
L1 fold/prop/DCE, L2 +CSE/TCO/jump-opt, L3 +merging) rather than a
two-state O0/O2 toggle, future-proofing for other transforms. The
recompile+retrace already took the level; just widen the control from
two buttons to four (mapped over OPT_LEVELS, per-level tooltips). The
tailcall demo still lands on O2.
gnidan added a commit that referenced this pull request Jul 2, 2026
Refactor buildTailCallJumpOptions to emit its transform:["tailcall"]
marker via Ir.Utils.addTransform instead of a hand-written key, so all
transform emission (fold/tailcall/coalesce/...) routes through the one
helper. Behavior is identical — the back-edge JUMP still carries
return + invoke + transform:["tailcall"] — but composition stays
consistent (e.g. a folded-then-tailcall instruction would accumulate
the multiset) and there's no divergent hand-rolled site.

Existing #217 tailcall tests pass unchanged.
gnidan added a commit that referenced this pull request Jul 2, 2026
* bugc: emit fold transform on constant folding

Marks constant-folded values with transform:["fold"] so debuggers
can show that a value is a compile-time-evaluated constant rather
than source the user wrote.

Adds Ir.Utils.addTransform(debug, ...ids), which composes transform
markers as a flat sibling key on a debug context (per the flat-
composition convention) and appends to any existing transform array
— so an instruction touched by multiple passes accumulates the
multiset (e.g. ["fold","coalesce"]).

ConstantFoldingStep applies it to folded binary and hash results.
Although a folded const instruction is typically dissolved by
constant propagation + DCE, ConstantPropagationStep already carries
the folded const's operationDebug into the consuming instruction, so
the marker survives to the emitted bytecode's runtimeInstructions
(the tracer widget's path) at levels 1-3.

Adds an end-to-end test asserting the fold marker reaches the
bytecode at levels 1/2/3 and is absent at level 0.

* bugc: route tailcall transform through addTransform helper

Refactor buildTailCallJumpOptions to emit its transform:["tailcall"]
marker via Ir.Utils.addTransform instead of a hand-written key, so all
transform emission (fold/tailcall/coalesce/...) routes through the one
helper. Behavior is identical — the back-edge JUMP still carries
return + invoke + transform:["tailcall"] — but composition stays
consistent (e.g. a folded-then-tailcall instruction would accumulate
the multiset) and there's no divergent hand-rolled site.

Existing #217 tailcall tests pass unchanged.
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.

1 participant