Skip to content

Commit e9d29df

Browse files
committed
docs: home the tail-call walkthrough in the trace playground
The interactive tail-call-optimization walkthrough belongs with the other step-through examples in the Explore trace playground, not in the tracing reference. Move the two tail-recursive examples and the O0/O2 call-stack comparison into a "Watching the optimizer" section under the trace playground, and leave the tracing page with a reference-style summary plus the back-edge transform-context schema example, linking to the playground for the interactive version. Restore the optimizer mention to the Explore hub.
1 parent 2fdd8f2 commit e9d29df

3 files changed

Lines changed: 51 additions & 54 deletions

File tree

‎packages/web/docs/core-schemas/programs/tracing.mdx‎

Lines changed: 19 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,6 @@ sidebar_position: 4
44

55
import SpecLink from "@site/src/components/SpecLink";
66
import SchemaExample from "@site/src/components/SchemaExample";
7-
import { TraceExample } from "@theme/ProgramExample";
8-
import {
9-
tailRecursiveSum,
10-
tailRecursiveFactorial,
11-
} from "./tracing-examples";
127

138
# Tracing execution
149

@@ -178,44 +173,14 @@ instead of (or alongside) a reason pointer:
178173
}`}
179174
</SchemaExample>
180175

181-
## Tail-call optimization
182-
183-
The recursion examples above push a new frame for every call — step
184-
through them and watch the call stack grow. A compiler can often avoid
185-
that. When a recursive call sits in **tail position** — its result is
186-
returned directly, with no further work after it — the compiler can
187-
reuse the current frame instead of pushing a new one. This is
188-
**tail-call optimization** (TCO), and it turns recursion into a loop.
189-
190-
The two programs below are written so bugc's optimizer folds them. Each
191-
accumulates its result in an `acc` parameter and hands it to the next
192-
call in tail position, so no work is left pending on the stack.
193-
194-
Use the **Opt** selector in the trace drawer to set the optimization
195-
level. Compile at **O0** (no optimization) and again at **O2**
196-
(optimizations on, including TCO), then step through and compare the
197-
call stack. TCO kicks in at **O2**.
198-
199-
<TraceExample
200-
title="Tail-recursive sum"
201-
description="Sums 1..5 with an accumulator; TCO folds it into a loop"
202-
source={tailRecursiveSum}
203-
/>
204-
205-
<TraceExample
206-
title="Tail-recursive factorial"
207-
description="Computes 5! with an accumulator; TCO folds it into a loop"
208-
source={tailRecursiveFactorial}
209-
/>
210-
211-
At **O0**, each `sum`/`fact` call is a real invoke/return pair and the
212-
call stack grows one frame per iteration. At **O2**, the recursive
213-
call becomes a **back-edge**: a single JUMP that ends one iteration and
214-
begins the next without pushing a frame. The call stack stays flat.
215-
216-
That one JUMP carries three facts at once, composed as sibling keys on a
217-
single context — the flat form described on the
218-
[transform context](/spec/program/context/transform) page:
176+
## Optimized code: the tailcall transform
177+
178+
When the compiler optimizes tail-recursive calls, it turns the recursion
179+
into a loop: the recursive call becomes a **back-edge** — a single JUMP that
180+
ends one iteration and begins the next without pushing a frame. That one
181+
JUMP carries three facts at once, composed as sibling keys on a single
182+
context (the flat form described on the
183+
[transform context](/spec/program/context/transform) page):
219184

220185
<SchemaExample
221186
schema="program/context/transform"
@@ -239,14 +204,17 @@ single context — the flat form described on the
239204

240205
The `return` and `invoke` state the source-level facts — the previous
241206
iteration returned, the next was invoked — and `transform: ["tailcall"]`
242-
explains how the compiler realized that pair as one JUMP. Because no
243-
value crosses a frame boundary here, the `return` carries no `data` and
244-
the `invoke` no `arguments`: the accumulator is threaded through the
245-
loop directly, and the invoke `target` points at the loop header the
246-
JUMP re-enters. A debugger that ignores the transform still reads a
247-
coherent invoke/return sequence; one that understands it can show that
248-
the call stack isn't really growing, and present the recursion as the
249-
loop it compiled to.
207+
explains how the compiler realized that pair as one JUMP. Because no value
208+
crosses a frame boundary here, the `return` carries no `data` and the
209+
`invoke` no `arguments`: the accumulator is threaded through the loop
210+
directly, and the invoke `target` points at the loop header the JUMP
211+
re-enters. A debugger that ignores the transform still reads a coherent
212+
invoke/return sequence; one that understands it can show that the call stack
213+
isn't really growing.
214+
215+
To watch this happen — flipping the optimizer between **O0** and **O2** and
216+
stepping through the back-edge — see the tail-call optimization examples in
217+
the [Trace playground](/docs/explore/trace-playground).
250218

251219
## Trace data structure
252220

‎packages/web/docs/explore/index.mdx‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ your browser; no setup required.
1616
storage slot, a struct, and a computed location each select their bytes.
1717
- [**Trace playground**](/docs/explore/trace-playground) — Compile a BUG
1818
program and step through its execution trace: source location, in-scope
19-
variables, the call stack, and instruction context at every step.
19+
variables, the call stack, and instruction context at every step —
20+
including the optimizer's tail-call transform.
2021
- [**BUG playground**](/docs/explore/bug-playground) — Compile the BUG
2122
language end to end and inspect each stage: AST, IR, control-flow graph,
2223
and bytecode.

‎packages/web/docs/explore/trace-playground.mdx‎

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import { TracePlayground, TraceExample } from "@theme/ProgramExample";
88
import {
99
counterIncrement,
1010
functionCallAndReturn,
11+
tailRecursiveSum,
12+
tailRecursiveFactorial,
1113
} from "../core-schemas/programs/tracing-examples";
1214

1315
# Trace playground
@@ -18,8 +20,8 @@ source line, in-scope variables, call stack, and instruction context
1820
light up at every step.
1921

2022
Click **"Try it"** on an example to open the trace drawer, then use the
21-
step controls to walk through execution. The two examples build on each
22-
other: start with the storage write, then follow a function call.
23+
step controls to walk through execution. The examples build on each
24+
other: start at the top and work down, picking up one idea at a time.
2325

2426
<TracePlayground>
2527

@@ -51,4 +53,30 @@ For the exact shape of invoke, return, and revert contexts, see the
5153
[function call spec](/spec/program/context/function) and the
5254
[tracing reference](/docs/core-schemas/programs/tracing).
5355

56+
## Watching the optimizer
57+
58+
Compilers rewrite code as they optimize, and **transform** contexts
59+
record what they did. Set the **Opt** selector to **O2** and step through
60+
these tail-recursive programs: the recursive call folds into a loop, and
61+
the back-edge JUMP carries `transform: ["tailcall"]` next to its invoke
62+
and return. The call stack stays flat instead of growing one frame per
63+
iteration.
64+
65+
<TraceExample
66+
title="Tail-recursive sum"
67+
description="Sums 1..5 with an accumulator; TCO folds it into a loop"
68+
source={tailRecursiveSum}
69+
/>
70+
71+
<TraceExample
72+
title="Tail-recursive factorial"
73+
description="Computes 5! with an accumulator; TCO folds it into a loop"
74+
source={tailRecursiveFactorial}
75+
/>
76+
77+
Flip the **Opt** selector between **O0** and **O2** to see the call stack
78+
change. For how the `tailcall` transform composes with invoke/return, see
79+
the [transform context spec](/spec/program/context/transform) and the
80+
[tail-call optimization walkthrough](/docs/core-schemas/programs/tracing).
81+
5482
</TracePlayground>

0 commit comments

Comments
 (0)