diff --git a/docs/handoffs/HANDOFF-x86-annotate.md b/docs/handoffs/HANDOFF-x86-annotate.md new file mode 100644 index 0000000..84f1bbc --- /dev/null +++ b/docs/handoffs/HANDOFF-x86-annotate.md @@ -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.