flan/docs/handoffs/HANDOFF-x86-annotate.md

2.9 KiB

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.