flan/lib/mangle.ml
Joseph Ferano 75177a5a18 Review follow-ups: the constructor took a name a program can write
The wrapper was called flan.dev.ctor, and that is a name Flan can reach:
(defn dev.ctor [] i32 7) mangles onto exactly it. The program compiles
and runs as a release build and fails a dev build with a redefinition
clang refuses -- loud, and only under LLVM with --dev, but mangle.ml's
own comment exists to make it impossible rather than loud. The name is
[Mangle.dev_ctor] now and starts with the dot no reader token can
produce, beside .init-globals and .init-data. The colliding program
builds and prints 7.

Two comments in survey.sh, both of them reasoning rather than
behaviour. The counts argument against a per-name -O0 list was no
argument: excluding moves the counts just as much. What actually
carries it is that dies_segv builds both programs at -O0 on both
backends and asserts more than this sweep would. And dev-segv's park
under --dev is a forever-list reason that happens to land on a program
this list already covers, not a second reason for this list.

FIX.org takes the sweep, and one thing the sweep cannot say: the two
heap cases of bytes-copy.flan leak 24 bytes through flan_bytes_dup,
measured with --leak-check=full, and both corpora are blind to it by
policy -- detect_leaks=0 on one side and --leak-check=no on the other.
The row proves the copy is in bounds and written. It says nothing about
who frees it, and a green sweep should not be read as though it did.
2026-09-21 10:12:55 +07:00

63 lines
3.3 KiB
OCaml

(* The symbol names a Flan build puts into an object, spelled once.
Both backends emit the same names — that is not a nicety, it is the link:
an [--x86] host and a redefinition module built by LLVM bind against each
other, so [@"flan.cell.<n>"] has to be byte-for-byte the same string on
both sides or the dlopen fails and the piece served nothing. The macro
loader and the daemon's disassembler look up the same names from outside
the backends entirely.
So the prefixes live here, unquoted and without a sigil. Quoting is each
backend's own — LLVM writes [@"..."], the assembler writes ["..."] — and a
Flan name holds -, ?, > and /, which is why every emitted name is quoted
at all. This module only decides *which string* is quoted.
Not here: the ABI marker itself. [Emit.abi_marker] is ["flan.abi.llvm"] and
[X86.abi_marker] is ["flan.abi.x86"], and the two must stay distinct —
a crossed pair is refused at [dlopen] precisely because the marker one
image defines is not the one the other references. Sharing that string
would delete the mechanism. The *label* a module hangs its requirement on
is the other half of that mechanism and is shared, so it is here: see
[abi_require] below. *)
(* The prefix itself. It keeps the Flan [main] from colliding with C's, and
it is what makes every Flan symbol recognisable in a disassembly. *)
let prefix = "flan."
(* A function or a global. One namespace, because the language has one: a
[defn] and a [defonce] cannot share a name, so nothing here has to keep
them apart. The compiler's own names go through this too — [.init-globals]
and [.init-data] start with a dot no reader token can produce. *)
let sym n = prefix ^ n
(* The single constructor a dev build emits, which calls the runtime's two
enable entry points — the allocation registry and the crash handler — in
that order. It goes through [sym] with a leading dot for exactly the reason
that comment gives, and the reason is not decoration: the first spelling of
this was a plain [flan.dev.ctor], which is what [(defn dev.ctor [] i32 7)]
mangles to. That program compiles and runs as a release build and fails a
dev build with a redefinition clang refuses. A dot is what no reader token
can produce, so there is no Flan name that reaches this one. *)
let dev_ctor = prefix ^ ".dev-ctor"
(* A dev build's indirection cell: a mutable global holding the address of the
function that is currently this name's body. *)
let cell n = prefix ^ "cell." ^ n
(* The two module-local caches a name the host was never built with is reached
through — one for a function, one for a global. *)
let cellptr n = prefix ^ "cellp." ^ n
let globalptr n = prefix ^ "gp." ^ n
(* A compiled macro's entry point, which the expander dlsyms by this name out
of the module [Build.macro_module] wrote. *)
let macro n = prefix ^ "macro." ^ n
(* The datum a redefinition module puts its host's ABI marker into. Nothing
outside the module names it — the relocation against the marker is the
whole of what it does — but both backends emit it, so the string is spelled
once here rather than twice, beside every other name that has to read the
same on both sides. What it *holds* is each backend's own marker, and that
pair stays distinct; see the paragraph at the top. *)
let abi_require = prefix ^ "abi.require"