Skip to content

Windows and -O0 in CI; std.math's sqrt; SIMD widths compared where the AVX2 copy is plain - #185

Merged
nicolas-maman merged 6 commits into
mainfrom
fix/windows-o0
Oct 20, 2026
Merged

nicolas-maman merged 6 commits into
mainfrom
fix/windows-o0

Conversation

@nicolas-maman

@nicolas-maman nicolas-maman commented Oct 20, 2026 •

Copy link
Copy Markdown
Collaborator

Closes #184.

What changes

  • The engine's double square root is std.math's. At -O0 every sqrt is a library call. MinGW's library sqrt is the x87 one, at the 64-bit precision a MinGW main thread runs with, and it rounds twice: one ulp off for 253 of 1,000,000 inputs. From -O1 up GCC inlines sqrtsd and hides it. Proof: -O3 with -fno-builtin-sqrt gives exactly the -O0 bits.
    • math.sqrt_float calls std.math's sqrt, which runs in Aether's optimized runtime as the instruction. Every module, test and bench calls math.sqrt_float.
    • The per-module extern sqrt lines are gone, so a bare sqrt no longer compiles.
    • libm sqrtf stays: no float of 304 million differs from sqrtss.
  • test_simd_widths no longer requires a native AVX2 copy. GCC on Windows at -O0 (aether#3023) and the AETHER_GCC_COMPAT=0 path compile the copy as plain code. There native8() is false, and the copy still has to give the same bits. The widths are compared in every build, and the test says when the copy is plain. That leaves 29 checks.
  • A Windows leg (windows-latest, MSYS2 UCRT64). It runs the suite, then test_determinism at -O0 against the golden trace. The release is fetched as its .zip, because aeb's get.sh looks for a .tar.gz and would build the compiler from source. diffutils is installed for the golden diff.
  • .gitattributes keeps the golden trace and the scripts LF. The Windows runner checks text out as CRLF, and the golden diff then failed on every line, although the bits agree.
  • The pins move to one workflow-level env, which get.sh reads, so a bump is one line.
  • Docs: the README names the Windows leg and the libm calls, and says five scenes, not four. bench/RESULTS.md has the sqrt measurement, and drops the slice indexing from what is left in the SAT (aether#3020).

No Aether release needed

This was first meant to wait for aether#3023 and aether#3027. It does not:

  • aether#3023 (crash): ae build passes -fstack-protector-strong, and with it GCC on Win64 compiles the AVX2 copies at -O0. Measured on the release's C: plain -O0 crashes, and that flag alone makes it compile. The crash shows only in builds that call gcc themselves without the flag, as ae3d's do, so this leg cannot see it.
  • aether#3027 (thread x87 state): with std.math's sqrt, nothing in a step depends on the x87 precision, so the -O0 scenes agree over four workers on 0.835.0 as they are.

Measured (Windows)

build test_determinism golden trace
CI: -O3 and -O0, Aether 0.835.0 29 checks pass, four workers included bit for bit, 1,250 steps
local: -O3, Aether 0.835.0, UCRT64 and MINGW64 every test passes bit for bit
local: -O0, Aether at 677034aa (#3023 + #3027) 29 checks pass bit for bit

std.math's sqrt is a call where libm's became one sqrtsd. Three bench scenes, alternated runs: within their own spread, with the same bits (bench/RESULTS.md).

test_simd_widths checked contact_solver_wide8.lanes_native() before
comparing the widths. Aether does not promise a native copy: GCC on
Windows at -O0 (aether#3023) and the AETHER_GCC_COMPAT=0 path compile it
as plain code, where native8() is false and the copy still has to give
the same bits. The check failed those builds although they are correct.

The scenes are now compared at both widths in every build, and the test
says when the copy is plain. Measured with Aether at bd667cb1 on Windows:
29 checks pass at -O3; at -O0 the widths agree bit for bit and the note
prints.
ae3d and Siltreach run on Windows, and ae3d builds its suites at -O0, but
CI here built only Linux and macOS at -O3. Two bugs reached ae3d first:
GCC on Win64 crashing on the AVX2 copies at -O0 (aether#3023), and four
workers parting at -O0, where every sqrt is MinGW's library call and that
rounds twice on the main thread's x87 precision.

The new job runs the suite on windows-latest under MSYS2 UCRT64, with
the release fetched as its .zip (get.sh looks for a .tar.gz), then
test_determinism at -O0 against the golden trace. The pins move to one
workflow-level env, read by get.sh, so a bump is one line.

The README names the leg, and says five scenes, not four: the rolling
mix joined the trace and the count stayed.
At -O0 every sqrt is a library call, and MinGW's library sqrt is the x87
one at the 64-bit precision a MinGW main thread runs with: it rounds
twice, one ulp off for 253 of 1,000,000 inputs. From -O1 up GCC inlines
sqrtsd and the library is never reached, so only unoptimized builds on
Windows parted from the golden trace (wave pile from step 10). -O3 with
-fno-builtin-sqrt gives exactly the -O0 bits, which is the proof.

math.sqrt_float now calls std.math's sqrt, which runs in Aether's
optimized runtime as the instruction, and every module, test and bench
calls math.sqrt_float: the per-module `extern sqrt` lines are gone, so a
bare sqrt no longer compiles. libm's sqrtf stays: its 64-bit x87 result
rounds to a float once, and no float of 304 million differs from sqrtss.

The call costs nothing measurable (bench/RESULTS.md: three scenes,
alternated runs, within their spread) and the bits are the same: the
suite and the golden trace pass at -O3 with Aether 0.835.0.

bench/RESULTS.md also drops the slice indexing from what is left in the
SAT: the Aether session measured every bounds test folded and the
samples on aether_slice.h as skid from the loads (aether#3020).
MSYS2's base install has no diff, and the first run of the leg failed
there after every test had passed.
@nicolas-maman nicolas-maman changed the title Windows and -O0 in CI; SIMD widths compared where the AVX2 copy is plain Windows and -O0 in CI; std.math's sqrt; SIMD widths compared where the AVX2 copy is plain Oct 20, 2026
The Windows runner's git checks text out as CRLF, so the golden trace
differed from the build's on every line (1,1250c1,1250) although the
bits agree: a UCRT64 build here matches it bit for bit. .gitattributes
keeps tests/golden/*.txt and the shell scripts LF on every checkout.
ae build passes -fstack-protector-strong, and with it GCC on Win64 does
not crash on the AVX2 copies at -O0 (measured on the release's C for
test_contact_solver_wide: plain -O0 crashes, adding that flag alone
compiles). So the leg runs -O0 on Aether 0.835.0 and cannot see
aether#3023, which shows in builds that call gcc themselves without it,
as ae3d's do. With std.math's sqrt nothing in a step depends on the x87
precision, so the -O0 scenes keep their bits without aether#3027.
@nicolas-maman
nicolas-maman marked this pull request as ready for review October 20, 2026 18:16
@nicolas-maman
nicolas-maman merged commit 48065aa into main Oct 20, 2026
3 checks passed
@nicolas-maman
nicolas-maman deleted the fix/windows-o0 branch October 20, 2026 18:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Windows and -O0 untested: a wrong lanes_native check, and four-worker bits that drift at -O0

1 participant