Say what the objdump actually showed, and where it showed nothing

The first version of both notes claimed a call to fmod in any build with a
typed float % in it. A literal pair is folded before any call exists, which
is the same fact two paragraphs further down explaining why the corpus block
uses globals. Both now say the measurement: the calls are in the build whose
operands come through globals.
This commit is contained in:
Joseph Ferano 2026-09-20 11:25:49 +07:00
parent 58d5ccda94
commit 6896128407
2 changed files with 25 additions and 17 deletions

29
FIX.org
View File

@ -675,9 +675,11 @@ the dev loop's default backend, which is what turned a backend gap into a
thing the author hit while writing ordinary code.
Fixed by calling the same function LLVM calls. There is no SSE remainder
instruction and LLVM does not invent one: at -O0 it lowers frem to fmod or
fmodf, which objdump shows as a call to the PLT stub in any build of a program
with a typed float % in it. x86.ml now loads the two operands into xmm0 and
instruction and LLVM does not invent one: a frem that reaches the code
generator becomes a call to fmod or fmodf, which objdump shows as a call to
the PLT stub — fourteen of them in a build of the probe whose operands come
through globals, and none at all in one written with float literals, where the
pair is folded to its answer before any call exists. x86.ml now loads the two operands into xmm0 and
xmm1 — already the SysV argument registers — and calls fmod or fmodf by width.
Agreement is then by construction rather than by a second hand-written
identity that would have to get every rounding, every signed zero and every
@ -702,13 +704,14 @@ build. So a corpus program that exercises a construct is already a test that
the two backends agree about it, and the float % cases added to math3.flan are
in that set by being in test/programs.
What @x86 does not do is fix the LLVM side at -O0. It builds both sides with
whatever the default optimisation is, so a construct LLVM folds at compile
time — a % over two float literals is one; the operator never survives to the
IR — is compared as a constant against the x86 backend's actual lowering. The
float % block in math3.flan goes through globals for that reason, the same
reason arith.flan gives for its own. Two things would close the rest of the
gap: a SURVEY_FLAGS=-O0 pass, so the LLVM side emits the calls and branches
rather than the answers, and something that walks the two prim match arms
mechanically rather than relying on somebody reading them side by side, which
is how this gap survived. Neither is queued.
What @x86 does not do is pin the LLVM side at -O0. It builds both sides at the
default -O2, so a construct LLVM folds at compile time — a % over two float
literals is one: that build contains no fmod call — is compared as a constant
against the x86 backend's actual lowering. The float % block in math3.flan
goes through globals for that reason, the same reason arith.flan gives for its
own. Two things would close the rest of the gap: an -O0 pass of the sweep, so
the LLVM side emits the calls and branches rather than the answers — the
script already has SURVEY_FLAGS, which hands the same extra flags to both
sides, and both sides do accept -O0 — and something that walks the two prim
match arms mechanically rather than relying on somebody reading them side by
side, which is how this gap survived. Neither is queued.

View File

@ -2762,10 +2762,15 @@ and prim f (e : Tast.expr) (p : Tast.prim) (args : Tast.expr list) dst =
fload f.b ~dst:1 ~mm:(lmem f lb ~scratch:r11) ~f64;
(match p with
(* There is no SSE remainder instruction, and LLVM does not pretend
otherwise: at -O0 it lowers [frem] to a call to C's [fmod] or
[fmodf], which `objdump -d` on any build of a program containing a
typed float [%] shows as a [call fmod@plt]. Calling the same
function is what makes the two backends agree by construction
otherwise: it lowers a [frem] that reaches the code generator to a
call to C's [fmod] or [fmodf], which is what `objdump -d` shows as a
[call fmod@plt] in a build whose operands come through globals. A
[%] over two float literals is folded on the way and contains no
call at all, which is why the corpus block that covers this uses
globals a folded answer is evidence about the folder.
Calling the same function is what makes the two backends agree by
construction
rather than by a hand-written identity that would have to get every
rounding, every sign of zero and every infinity right on its own.
The prelude already declares both symbols ([fmod-f32],