Four lowerings want a buffer, and the buffer wants to remember

This commit is contained in:
Joseph Ferano 2026-09-14 11:11:13 +07:00
parent f77216212e
commit 5a2f627842

View File

@ -0,0 +1,73 @@
# Handoff — one buffer for every lowering
Branch `dev-loop`, worktree `agent-a9d4028fa5752ba39`, from `f772162`.
`spike/x86/dump.sh` landed at `f772162` and prints four lowerings of one function side by side: the LLVM IR
the frontend emits, what `llc` makes of it at `-O0` and at `-O2`, and what the hand-written x86 backend emits.
Reading one against another is the only way to check a lowering by eye. A shell script that prints all four
into a scrollback is not how anyone reads them, though: three of the four are noise on any given day, and the
one that is not is the one you are elbow-deep in. So the same four go into an Emacs buffer with collapsible
sections, and the buffer remembers which of them you had open.
`M-x flan-lowering`, `C-c C-l` in `flan-mode`.
## The three decisions
### 1. It compiles the file; it does not ask the daemon
`flan-disassemble` (`C-c C-a`) asks the running program what the body it is calling for a name actually came
out as. This asks a different question — what does this source compile to, through four different lowerings —
and the two cannot be merged, for a concrete reason rather than a stylistic one: **the daemon has the
installed body's `.ll` and its `.so`, and neither `llc -O2` nor the hand-written backend has ever been run
over it.** There is no answer in the daemon to give. `llc -O2` on the installed IR would be a fifth thing,
built here and never run anywhere, and the backend's output for an installed body does not exist at all
unless the daemon was started `--x86`.
So the two commands sit beside each other and each says which question it answers. `flan-disassemble` keeps
its header, whose `showing` line is the daemon's own account of how much its answer claims. This buffer's
header says the opposite thing just as plainly: it names the file and the compiler flags, and says the
listing is what the source on disk compiles to and not what the program is running. Both docstrings
cross-reference the other.
### 2. `outline-minor-mode`, not `magit-section` and not overlays of my own
Nothing under `emacs/` requires a package that is not in Emacs — the whole client is `subr-x`, `seq`,
`pcase`, `cl-lib`, `xref`, `eldoc`, `comint`, `imenu` and its own files. Taking a dependency on Magit for a
four-section buffer would be the first, and it would be for folding, which Emacs has had since before Magit
existed.
`outline-minor-mode` on GNU Emacs 30.2.50 is the whole of what was asked for: `outline-cycle` is `TAB` on a
heading, `outline-hide-body` and `outline-show-all` collapse and expand everything, and
`outline-next-visible-heading` / `outline-previous-visible-heading` are `n` and `p`. Hand-rolling it would be
a hundred lines re-implementing that badly.
Headings are found by `outline-search-function` keyed to a text property this file puts on the four heading
lines, **not** by a regexp over the text. That is not fussiness: LLVM IR has `;` comments, assembler output
has `#` and `;` comments and lines that start with `.`, and `objdump` prints `<flan.step>:` label lines. Any
regexp loose enough to match four section headers matches inside three of the four bodies, and folding then
fragments in the middle of a listing.
### 3. The open/closed state is a global alist keyed by section, and nothing else
One `defvar`, four entries, alive for the Emacs session and no longer. Not persisted to disk: it is four
booleans, a preference file for it would one day have to be migrated, and nobody has ever wanted yesterday's
fold state back.
Keyed to the **section**, not to the function. The workflow is "I am working on the backend today", which is
a fact about the section and holds across every function the author looks at while he is doing it. Keyed to
the function it would forget on every `M-x flan-lowering` of a different name, which is exactly the thing he
asked not to happen.
The alist is the single source of truth. A toggle mutates it and then applies it to the buffer; a refresh
applies it to the newly drawn buffer. Visibility is never read back out — that would couple this to whether
outline folds with overlays or with text properties, which is a thing that has changed between Emacs
versions and is none of this file's business.
## Status
- [x] Handoff stub, decisions fixed
- [ ] `emacs/flan-lower.el`
- [ ] Key in `flan-mode`, autoloads
- [ ] Tests in `emacs/test-flan-dev.el`
- [ ] `emacs/MANUAL.md`
- [ ] `dune test`, `@page`, `@cells`