Joseph Ferano 81f7e464ec The indirection cell on the x86 backend, and --x86 --dev with it
FnAddr (Fnval n) emitted the symbol, which is right for a whole-program build
and wrong the instant anything is redefined into it. It now reads the cell,
and so does every direct call, which is what emit.ml's body_of does and is the
half that matters: a redefinition is one store, and it has to reach call sites
that already exist.

What is emitted, all of it behind dev:

  - one cell per function in .data, .globl, initialised to the body this build
    compiled. Spelled exactly as Emit.cellname spells it, because the point of
    having one here is that an LLVM-built module binds
    @"flan.cell.<n>" = external global ptr against it. nm -D over the two
    builds of the same program gives identical sets of 68 cell symbols.
  - the cell load placed after the arguments, which emit.ml has as a
    load-bearing comment: a redefinition landing between two calls must not
    land in the middle of one. CallPtr stays the other way round.
  - the flan_dev_reg_enable constructor, which arms the allocation registry.

Not emitted: Emit.cellptr, the deeper spelling for a name the host was never
built with. It cannot arise in a whole-program build and belongs with the
redefinition module that would introduce one.

The --x86 --dev refusal is relaxed, and the argument is that flan dev never
reaches this fork: --x86 is read only by flan build, and the daemon builds
host and modules through Build.executable / Build.shared without it. So the
flag means a host whose call sites are redefinable, and nothing claims the
module that would redefine through them exists.

Two things were needed to believe any of that. First, the corpus with --dev on
both sides: 97 MATCH, 0 DIFFER, same as without it. Before the constructor was
added that read 96/1 — registry.flan asks (live? ...) and got four zeroes,
which is the whole of what a dev host does differently besides the cells.

Second, and the corpus cannot do this one: a dev build starts with every cell
pointing at the body this build compiled, so it prints what a release build
prints whether anything reads the cell or not. spike/x86/cells.sh preloads a
shared object whose constructor dlsyms flan.cell.twice and stores a different
body there -- the one store a redefinition ends in, done from outside, no
compiler involved. Both dev builds then print the new answer for a direct call
and for a function value, and both release builds are unchanged, which is what
says the change came from the indirection and not from symbol interposition.

One thing the later lane inherits, now written in both headers rather than
left to be discovered. x86.ml licenses its own calling convention on the
grounds that a dev build is compiled entirely here and a release build
entirely by LLVM, so the two never meet in one process. A cell an LLVM-built
module can store into is the first thing that could make that false: the
conventions agree on scalars and disagree on every aggregate, so an
Emit.redefinition module dlopened into an --x86 host would be right until the
first redefined function took or returned a struct. The answer is a
redefinition emitter here, not a classifier.
2026-09-13 21:17:39 +07:00
2024-07-09 21:01:55 +10:00
2026-09-10 14:56:35 +07:00
2026-09-10 14:40:34 +07:00
2026-09-10 14:40:34 +07:00
Description
No description provided
MIT 7.5 MiB
Languages
OCaml 67.2%
Emacs Lisp 15.2%
C 10.4%
HTML 2.9%
Standard ML 2.8%
Other 1.5%