Skip to content

Pin --target-cpu: the release crashed with SIGILL on a machine that did not build it - #112

Merged
codetalcott merged 2 commits into
mainfrom
claude/target-cpu-baseline
Aug 25, 2026
Merged

Pin --target-cpu: the release crashed with SIGILL on a machine that did not build it#112
codetalcott merged 2 commits into
mainfrom
claude/target-cpu-baseline

Conversation

@codetalcott

Copy link
Copy Markdown
Owner

The v0.10.0rc1 release run failed, and it caught something worth the whole exercise. Nothing was published — publish and publish-pypi skipped, no GitHub release, no PyPI upload.

What happened

Both wheels built. smoke-wheel passed inside both build jobs. wheel-consume-macos passed. Then wheel-consume-linux ran m0serve --version in a clean container:

0  libKGENCompilerRTShared.so 0x00007f39a1540f8e
...
4  m0serve                    0x000056203c1260e9
Illegal instruction (core dumped)

Why

--target-cpu <CPU>
    Sets the compilation target CPU. Defaults to the host CPU.

mojo build is -march=native by default — for every artifact this repository has ever produced. Asked directly on a developer machine:

--target-cpu apple-m4
--target-features ...,+sme,+sme-f64f64,+sme-i16i64,+sme2

+sme/+sme2 are the Scalable Matrix Extension. No M1, M2 or M3 has them. The wheels I dogfooded against three Django projects — all on that same M4 — would very likely have crashed on any other Apple Silicon Mac.

The fix

platform baseline dropped
macOS arm64 apple-m1 +sme, +sme2, +i8mm, +bf16
Linux / macOS x86_64 x86-64-v2 AVX, AVX2, BMI, FMA
Linux aarch64 generic

x86-64-v2 is SSE4.2/POPCNT — 2009 hardware, and RHEL 9's own baseline. apple-m1 is the oldest Apple Silicon. Verified: the rebuilt binary runs, and otool -tv finds zero SME instructions where the m4 build emitted them.

Why this one is worse than the rpath defect it resembles

rpath target CPU
passes where built yes yes
fails elsewhere yes yes
detectable on the build machine statically no

The rpath version was visible to static inspection — that's what ffi_portability_check.py is for. This one isn't: the binary is correct, it just needs instructions the consumer's CPU doesn't implement. Only running it on different silicon can find it.

wheel-consume-linux caught it purely because it runs somewhere else. wheel-consume-macos did not catch it and cannot — it builds and consumes on macos-14, so they share a CPU. The macOS side is protected by the pin, not by that job. Worth saying plainly rather than treating a green tick as reassurance.

The guard, and how its first version was wrong

check_docs.py asserts both tasks pass the flag. The first version searched the whole task body — which contains a comment explaining --target-cpu — so it passed with the flag deleted. A guard satisfied by its own documentation. It now parses the mojo build command with continuations joined; both tasks were sabotaged to confirm it fails.

Cost

bench/results/ numbers were measured with host-native tuning and a baseline build won't reproduce them exactly. Correctness first — an artifact that crashes has no throughput.

Test

839 tests, warnings unchanged at 68, smoke-ffi/smoke-serve/smoke-wheel green.

After this

The v0.10.0rc1 tag exists but produced no release and no upload, so it can be deleted and re-pushed once this lands.

🤖 Generated with Claude Code

codetalcott and others added 2 commits August 25, 2026 17:55
…id not build it

The v0.10.0rc1 run built both wheels, passed smoke-wheel inside both build
jobs, passed wheel-consume-macos -- and then wheel-consume-linux ran
`m0serve --version` in a clean container and got:

    0  libKGENCompilerRTShared.so 0x00007f39a1540f8e
    ...
    Illegal instruction (core dumped)

`mojo build` defaults --target-cpu to the HOST CPU. That is -march=native,
silently, for every artifact this repository has ever produced. Asking the
compiler directly on this machine:

    --target-cpu apple-m4
    --target-features ...,+sme,+sme-f64f64,+sme-i16i64,+sme2

+sme/+sme2 are the Scalable Matrix Extension. No M1, M2 or M3 has them, so
the wheels dogfooded against three Django projects -- all on the same M4 --
would very likely have crashed on any other Apple Silicon Mac.

build-ffi and build-serve now pin the oldest CPU each platform must support:
apple-m1, x86-64-v2 (SSE4.2/POPCNT, hardware from 2009, and RHEL 9's own
baseline), generic on aarch64. Verified: the rebuilt binary still runs, and
`otool -tv` finds zero SME instructions where the m4 build emitted them.

This is the rpath defect one level up, and worse in one specific way: the
rpath version was at least visible to static inspection, which is what
ffi_portability_check.py exists for. This one is not -- the binary is
correct, it just needs instructions the consumer's CPU does not implement.
Nothing on the build machine can tell. wheel-consume-linux caught it purely
because it runs somewhere else, which is the property those jobs were built
for. wheel-consume-macos did NOT catch it and cannot: it builds and consumes
on macos-14, so they share a CPU. The macOS side is protected by the pin.

check_docs.py asserts both tasks pass the flag -- and the first version of
that check was itself broken in an instructive way: it searched the whole
task body, which includes a comment explaining what --target-cpu is for, so
it passed with the flag deleted. A guard satisfied by its own documentation.
It now parses the mojo build command with continuations joined, and both
tasks were sabotaged to confirm it fails.

Cost, stated rather than buried: bench/results/ numbers were measured with
host-native tuning and a baseline build will not reproduce them exactly.

839 tests, warnings at 68, smoke-ffi/serve/wheel green.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…d build to offer

I claimed pinning --target-cpu costs performance and that bench numbers would
not reproduce. That was an assumption, and it is wrong. Compiling the same
sources at apple-m1 and apple-m4 and diffing the disassembly:

    run_benchmarks   41,248 lines   0 differing
    m0serve         257,735 lines   0 differing

Byte-identical machine code. Stronger than a timing comparison, since
identical code cannot differ in speed, and unsurprising once stated: what
apple-m4 adds over apple-m1 is +sme, +sme2, +i8mm, +bf16 -- matrix and ML
extensions -- and an HTTP server's hot paths are syscalls, byte scanning,
hashing and memcpy. Nothing there auto-vectorizes into SME.

So the answer to "should we offer an m4+ build" is no, twice over. There is
nothing to ship: it would be the same bytes under a different name. And there
would be nowhere to put it if there were -- a wheel's platform tag has no
microarchitecture field, macosx_13_0_arm64 is the only arm64 macOS tag, so
two tuned wheels for one version collide on filename and pip cannot choose.
It would take a separate distribution name or an off-PyPI download.

Scoped honestly: this is true of this code at this commit. Add SIMD-heavy
work and it could change, and the way to find out is to re-run the diff.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@codetalcott
codetalcott merged commit 7f19119 into main Aug 25, 2026
4 checks passed
@codetalcott
codetalcott deleted the claude/target-cpu-baseline branch August 25, 2026 22:29
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.

1 participant