bench: add five synthetic union probes - #18
Merged
Conversation
macabeus
force-pushed
the
bench/synthetic-unions
branch
from
August 4, 2026 11:58
bda1c7d to
6039d35
Compare
macabeus
force-pushed
the
bench/synthetic-unions
branch
from
August 4, 2026 13:35
6039d35 to
c89a050
Compare
macabeus
force-pushed
the
bench/synthetic-unions
branch
from
August 4, 2026 13:45
c89a050 to
66929ee
Compare
The tier had no union spec at all, and the real tier only reaches a union in two rows — while "unions not modelled" is one of asmlift's most common decline reasons. Five probes, 20 rows: uhalf 32-bit store, two 16-bit reads of the same slot ubyte 32-bit store, variable-indexed byte read of the same slot upun float punned to u32 through a union utag tagged union in a struct, switched on the tag, read at three widths ustore array of unions: s32 write then a u16 write to the same element A union is only measurable when the SAME bytes are reached through members of different width or domain. A union whose members are never aliased compiles identically to a struct — that is the `sfield` problem, where the tag marks a property the bytes cannot falsify — so every probe here aliases. The aliasing probes take the union through a POINTER on purpose. Written as a local, `uhalf` and `ubyte` landed in a stack slot on the register-poor targets and asmlift declined on the stack-frame gap BEFORE reaching the overlap: the first version of `ubyte` declined on all four toolchains for sp-as-data and measured nothing about unions. `upun` is the deliberate exception — punning a float through memory IS the construct, so its FP store/load is the point. What they measure now: `uhalf` declines on all four with the union message; `utag` on three (mwcc declines earlier, on a cr0 compare); `upun` has m2c emitting its `M2C bitwise cast` marker on all four while asmlift matches on agbcc, where soft-float makes the pun a register move; `ubyte` and `ustore` both compile and score on both sides. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
asmlift 354 match (+4: upun on agbcc, ustore on agbcc/kmc/mwcc), m2c 342. No pre-existing row moved. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
macabeus
force-pushed
the
bench/synthetic-unions
branch
from
August 4, 2026 13:54
66929ee to
f23df28
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #16. Adds five synthetic union probes — 20 rows, 723 → 743.
Why
The synthetic tier had no union spec at all, and the real tier only reaches a union in two rows — while
unions not modelledis one of asmlift's most common decline reasons, and the audits behind #16 found two more real functions (sa3:OamMalloc,sa3:ProcessOamBuffers) that hit a union without anyone noticing. The construct was effectively unmeasured.The probes
uhalfu->w = v; return u->h[0] + u->h[1];ubyteu->w = v; return u->b[i];upunt.f = x; return t.u;u32through a unionutagswitch (t->kind) { … t->v.i / t->v.h / t->v.b }ustoree[i].a = v; e[i].b[0] = 0;s32write, then au16write to the same elementA union is only measurable when the same bytes are reached through members of different width or domain. A union whose members are never aliased compiles identically to a struct — that is the
sfieldproblem the audit flagged, where the tag marks a property the bytes cannot falsify. Every probe here aliases.What they measure
uhalfubyteupunutagustoreuhalfdeclines on all four with the union message — overlapping fields at offset 0 (widths 4 and 2) — unions not modelled. The cleanest isolate of the gap in the dataset.utaggives the same message on three; mwcc declines earlier, on an unrelatedcr0compare.upunis the sharpest head-to-head: m2c emits itsM2C bitwise castmarker on all four — its dedicated "cannot express this reinterpret in C" signal — while asmlift matches on agbcc, where soft-float turns the pun into a register move. On the FPU targets asmlift declines on theswc1/stfs/mfc1store-load.ubyteandustorecompile and score on both sides, so the set is not all-or-nothing: it moves gap size as well as exposing declines.Net: asmlift 350 → 354 (
upunon agbcc,ustoreon agbcc/kmc/mwcc), m2c unchanged at 342. No pre-existing row moved.A probe-design trap worth recording
The first version of
uhalfandubytedeclared the union as a local. On the register-poor targets it landed in a stack slot, and asmlift declined on the stack-frame gap before ever reaching the overlap:Four rows that looked like union coverage and measured frames. Taking the union through a pointer removed the masking:
ubytenow compiles and scores on all four, anduhalffails with the union message instead.upunis the deliberate exception — punning a float through memory is the construct, so its FP store/load is the point, not an artifact.This is the same standard the #16 audits applied to the existing tier (
ll2icompiling to a barebx lr,sfieldbeing byte-identical toloadoff): a probe has to isolate what it claims, and the only way to know is to look at what the compiler actually emitted.Verification
pnpm bench run+merge→ 743 rows,dirty: false,stale-checkgreen. 155 benchmark tests and 34 web tests pass;typecheck,format:check,lintclean. All 20 new rows build on all four toolchains.🤖 Generated with Claude Code