50 lines
2.5 KiB
Markdown
50 lines
2.5 KiB
Markdown
# Handoff — the two unreached guards in `lib/x86.ml`
|
|
|
|
Branch `dev-loop`, worktree `agent-ab6daf83988bee428`, on top of `b1cc67b`. This lane is items 4 and 5 of
|
|
`HANDOFF-x86-rt.md`'s "What remains": the `flan_transfer_fail` branch in the transfer exit, and the
|
|
`"defers on a transfer path nothing reaches"` refusal beside it. Both were described as guards that exist so that
|
|
if the reasoning behind them is wrong they say so, and that no program in the corpus reaches. The question this
|
|
lane was given is whether they are correct or merely untested.
|
|
|
|
**Status: in progress.** Item 5 is answered and the answer is that the guard is wrong. Item 4 is being probed.
|
|
|
|
## Item 5 — `"%s has defers on a transfer path nothing reaches"` is reachable, and it is a real hole
|
|
|
|
`lib/x86.ml:2234`. It fires when a function has function-level defers (`fn.Tast.fdefers <> []`) and `f.unwound` is
|
|
false — that is, when nothing in the body ever named the function's own transfer exit as a landing pad. `f.unwound`
|
|
is set in exactly one place, `current_pad` (`lib/x86.ml:757`), when the pad stack is empty, and `current_pad` has
|
|
exactly one caller that matters here: `guard`, the two-load-and-branch check emitted after every Flan call. So the
|
|
condition reads, in source terms: **a function that has a defer and makes no guarded call at all** — neither in the
|
|
body nor in the defers, which are spliced onto the normal exit path and so are lowered as part of the body.
|
|
|
|
That is not an exotic shape. `spike/x86/g5.flan` is ten lines and reaches it:
|
|
|
|
```
|
|
x86: quiet has defers on a transfer path nothing reaches
|
|
```
|
|
|
|
exit status 3, from
|
|
|
|
```flan
|
|
(defn quiet [n i32] i32
|
|
(defer (set log (+ log 1)))
|
|
(* n 2))
|
|
```
|
|
|
|
The same program compiles and runs under LLVM. `lib/emit.ml`'s `emit_fn` (~line 2283) has no counterpart to this
|
|
refusal: it writes the whole transfer exit under `if f.unwound then`, so when nothing can unwind the block and the
|
|
defers on it are simply not emitted. The two backends agree about the *reasoning* — the defers on that path are
|
|
dead — and disagree only about what to do with the observation. `x86.ml` refuses; `emit.ml` drops them.
|
|
|
|
**The correct lowering is to drop them,** matching `emit.ml`. The reasoning behind the guard is sound; the guard
|
|
itself is not, because "nothing reaches that path" is the ordinary case for any leaf function with a defer, not a
|
|
sign that something has gone wrong.
|
|
|
|
## Item 4 — the `flan_transfer_fail` branch
|
|
|
|
(being probed; see below)
|
|
|
|
## Files
|
|
|
|
(to be filled in)
|