The plan for an annotated listing, and the two decisions it rests on

This commit is contained in:
Joseph Ferano 2026-09-14 11:28:30 +07:00
parent f77216212e
commit 1961c7cc9d

View File

@ -0,0 +1,48 @@
# Handoff — `flan emit --x86` annotated, so its output can be read
Branch `dev-loop`, from `f772162`. **Stub: written before the code, per the working rules. Kept updated as
the work lands.**
The x86 backend exists so that a dev build is ours end to end and so that the editor can be told *why* each
instruction is there. Until now `flan emit --x86` printed a three-line file header and then nothing but
`.byte` blobs: no source form, no frame key, no statement of the calling convention, nothing naming the
bookkeeping. Reading it meant `as` plus `objdump`, and even that only answers *what*, never *why*.
## What is being built
1. **A comment per Flan form**, carrying the form's own source text and its `file:line:col`, above the run of
bytes that form produced. `lower`'s existing `dwline` hook is the annotation point — every expression
already passes through it carrying `e.Tast.loc`.
2. **A frame map per function.** `-0x20(%rbp)` means nothing without a key, and the key exists: `fn.snames`
says what the source called each slot, `f.slots` says where each one landed, and `xfer_off` / `sret_off` /
`retval` name the three the compiler adds. This is the single biggest difference from an LLVM listing.
3. **The calling convention, stated per function** — which register each argument arrived in, where the
transfer channel is, whether there is a hidden `sret`.
4. **The bookkeeping named**: the post-call guard, the bounds-check triple, the indirection cell load in a
`--dev` build, `rep movsb` block copies, the prologue and the epilogue.
## The two decisions
**Annotation is always on for `flan emit --x86` and always off for a `--x86` build.** `flan emit` exists to be
read by a person; there is no reason to make a reader ask for the thing the command is for. A build's `.s` is
a temporary file handed straight to clang and read by nobody, so leaving it exactly as it was keeps
`survey.sh`'s 103 MATCH a statement about the same text it has always been. `--no-annotate` is accepted by
`emit --x86` for the one consumer that wants the old spelling, which is the byte-identity check below.
**None of this belongs to the LLVM path.** `flan emit` already prints IR that names its values, carries
`!dbg` on every instruction and `!DILocalVariable` on every slot. The problem being fixed here is one this
backend has and LLVM does not.
## Byte identity
Annotation must not change one byte of emitted code. Comments and the splitting of one `.byte` directive into
several are both invisible to the assembler, but "invisible" is a claim to be measured rather than asserted:
for every program in the corpus, emit both ways, assemble both, and compare each section's bytes.
`survey.sh` at 103 MATCH / 0 DIFFER / 0 REFUSED and `dune test` are the regression check on the edits to
`buf` and `lower` — they say the backend still compiles what it compiled — and the section comparison is the
check on annotation itself.
## Status
Stub. Nothing below this line has landed yet.