The ж-box lowering, in its own emitted code — A2 exemplars
Companion to
DESIGN-zh-box-reduction.md(the arc’s design: §1 the measured bill, §3.3 the call-site row table, §3.4 the worked example, §11 the adversarial panel). Every snippet below is verbatim from the merged behavioral goldens — before =c33b3a67e(master before the A2 merge), after =c28c40960(the merge). The rebank commitf7bf4dda2classifies all 49 moved goldens into the six families shown here. Reproduce any pair withgit diff c33b3a67e c28c40960 -- src/tests/Behavioral/<name>.Headline numbers, from the A2 lane report (laptop R, pinned go1.23.1): fiat P224 Mul/Add/Sub/Square/Select at 0 B/op (was 528–1,344); the P256
TestAllocationsmirror at 10,105 obj/run vs 241,077 (−95.8 %; A3’s pinned pipeline measure owns the final verdict); the ж-bound A/B benchmark at 652.9 ms lowered vs 6,084 ms boxed (9.32×), taking lowered C# to 2.7× native Go from 24.9× boxed.
The arc in one sentence: a Go pointer parameter whose address provably never outlives the call —
not stored, not compared to nil, not captured, not handed to defer/go — is emitted as a native
CLR ref T parameter instead of a ж<T> heap box, and every uncovered shape falls back to the
boxed emission, so coverage is a win-counter, not a soundness premise.
1 · The signature itself — including generics
GenericReceiverFieldAddress. Go:
func setT[T any](p *T, val T) { *p = val }
func (b *Box[T]) Set(val T) { setT(&b.v, val) }
Boxed emission (before):
internal static void setT<T>(ж<T> Ꮡp, T val) {
ref var p = ref Ꮡp.DerefOrNull();
p = val;
}
public static void Set<T>(this ж<Box<T>> Ꮡb, T val) {
setT(Ꮡb.of(Box<T>.Ꮡv), val);
}
Lowered emission (after):
internal static void setT<T>(ref T p, T val) {
p = val;
}
public static void Set<T>(this ж<Box<T>> Ꮡb, T val) {
ref var b = ref Ꮡb.DerefOrNull();
setT(ref nonnil(ref b).v, val);
}
The parameter is the alias: the ж<T> Ꮡp box and its DerefOrNull() preamble vanish, and the
lowering survives generic instantiation. The caller’s row-1 field address Ꮡb.of(Box<T>.Ꮡv) — a
fresh 128-byte box per evaluation — becomes the allocation-free ref nonnil(ref b).v.
2 · A loop variable comes home from the heap
ForVariants. Go:
i := 0
for i < 10 {
f(&i)
i++
}
func f(y *int) { fmt.Print(*y) }
Before → after:
// before
ref var i = ref heap<nint>(out var Ꮡi);
i = 0;
while (i < 10) {
f(Ꮡi);
i++;
}
internal static void f(ж<nint> Ꮡy) {
ref var y = ref Ꮡy.DerefOrNull();
fmt.Print(y);
}
// after
nint i = 0;
while (i < 10) {
f(ref i);
i++;
}
internal static void f(ref nint y) {
fmt.Print(y);
}
An address-taken local was 2 objects (the ж box plus its eager T[1] pinnable slot); now i stays
a stack local. The same project’s labeled-loop variant used to mint a fresh box per iteration
for distinct addresses; A2 proves the requirement unnecessary there and collapses it to one plain
loop variable. Sibling exemplar in AddressOfParamWrite: a [3]int value parameter keeps Go’s
copy semantics as a plain a = a.Clone(); — no box — and bump(&a[1]) becomes row 4’s
bump(ref a[1]).
3 · Callee lowers, call site unwraps
PointerFieldArrayElementAddress. Go:
func bump(c *cycle) { c.n++ }
func viaParam(p *rec, i int) {
c := &p.future[i]
bump(c)
}
// before
internal static void bump(ж<cycle> Ꮡc) {
ref var c = ref Ꮡc.DerefOrNull();
c.n++;
}
internal static void viaParam(ж<rec> Ꮡp, nint i) {
var c = Ꮡp.at(rec.Ꮡfuture, i);
bump(c);
}
// after
internal static void bump(ref cycle c) {
c.n++;
}
internal static void viaParam(ж<rec> Ꮡp, nint i) {
var c = Ꮡp.at(rec.Ꮡfuture, i);
bump(ref (c).DerefOrNull());
}
The seam between covered and uncovered shapes in one hunk: bump’s parameter lowers, but this
caller’s c is still a box (it comes from .at(…) indexing), so the call unwraps it. Each
function makes its own deal; the convention change composes across the boundary.
4 · The pointer that gets reassigned
PointerParamNilWalk. Go:
func advance(p *node) (*node, int) {
return p.next, p.val
}
// before
internal static (ж<node>, nint) advance(ж<node> Ꮡp) {
ref var p = ref Ꮡp.DerefOrNull();
return (p.next, p.val);
}
// after
internal static (ж<node>, nint) advance(ref node p) {
return (p.next, p.val);
}
What does not lower is as telling: the returned p.next stays ж<node> (a pointer escaping
through a return keeps its box identity), and the caller’s walk loop emits
advance(ref (Ꮡp).DerefOrNull()) — dereferenced at each call, not bound once — so a pointer
variable reassigned between iterations always hands the callee its current target (row 3’s
reassigned-pointer safety).
5 · Two boxes become one temp
NamedArrayWrapper. Go:
var sm scal
fromBytes((*[4]uint64)(&sm.s), 7) // Named→underlying reinterpret
double(&sm.s, (*nonMont)(&sm.s)) // sibling reinterpret, same storage
// before
ref var sm = ref heap(new scal(), out var Ꮡsm);
fromBytes(Ꮡ((Ꮡsm.of(scal.Ꮡs)).Value.Value), 7);
@double(Ꮡsm.of(scal.Ꮡs),
Ꮡ((nonMont)((Ꮡsm.of(scal.Ꮡs)).Value.Value)));
// after
scal sm = new();
var ᴛ1 = sm.s.Value;
fromBytes(ref ᴛ1, 7);
var ᴛ2 = (nonMont)((sm.s).Value);
@double(ref sm.s, ref ᴛ2);
The double-box shape (an of() box plus a second box over a copied array header — class 3a of the
bill, ~17,280 of P256’s field-ref boxes) drops to a hoisted temp passed by ref. Parity argument:
the copied wrapper’s header shares its T[] backing with the original, so writes through the temp
land in the storage Go’s reinterpret would alias. The pairing is type-gated to the
identical-underlying-array family only — a string or numeric wrapper’s value is a plain copy, so
those sites keep the identity box end to end. One purely type-level predicate,
refConvPairingSupported, decides both the census and the emission (the output gate caught the two
sides disagreeing once — NamedNumericPointerReinterpret’s lost write — and the shared predicate
closed that class structurally; see 171e48b5f).
6 · nil keeps its panic timing
GuardedNilPointerParamDeref. Go:
func digits(base int, invalid *int) int {
n := 0
for i := 0; i < 5; i++ {
if i >= base && *invalid == 0 { // deref only behind the guard
*invalid = i
}
n++
}
return n
}
c2 := digits(10, nil) // legal: the guard never fires
// before
internal static nint digits(nint @base, ж<nint> Ꮡinvalid) {
ref var invalid = ref Ꮡinvalid.DerefOrNull();
...
}
nint c2 = digits(10, nil);
// after
internal static nint digits(nint @base, ref nint invalid) {
...
}
nint c2 = digits(10, ref ((ж<nint>)default!).DerefOrNull());
A lowered parameter still has to accept Go’s nil: the synthesized argument binds a null box and
defers the fault to the first actual use inside the callee — Go’s “a nil pointer only panics when
dereferenced” timing, exactly. The RefLoweredNilTiming guard pins this timing against go run.
7 · The A/B benchmark
src/tests/Performance/PerfRefLower — new in A2, the fiat shape distilled: pointer params feeding
pointer params, address-taken locals, field addresses, twenty million iterations. Go:
func cmov(out *uint64, cond, a, b uint64) {
m := uint64(0) - (cond & 1)
*out = (m & b) | (^m & a)
}
func mix(e, t *felem, k uint64) {
norm(&e.x, k)
norm(&t.x, k+1)
for i := 0; i < 4; i++ {
var s uint64
cmov(&s, t.x[i]&1, e.x[i]+t.x[i], e.x[i]^t.x[i])
e.x[i] = s
}
}
Lowered emission (the file contains zero ж, Ꮡ, or heap( occurrences — the loop allocates
nothing):
internal static void cmov(ref uint64 @out, uint64 cond, uint64 a, uint64 b) {
var m = (uint64)0 - ((uint64)(cond & 1));
@out = (uint64)(((uint64)(m & b)) | ((uint64)(~m & a)));
}
internal static void mix(ref felem e, ref felem t, uint64 k) {
norm(ref nonnil(ref e).x, k);
norm(ref nonnil(ref t).x, k + 1);
for (nint i = 0; i < 4; i++) {
uint64 s = default!;
cmov(ref s, (uint64)(t.x[i] & 1), e.x[i] + t.x[i], (uint64)(e.x[i] ^ t.x[i]));
e.x[i] = s;
}
}
8 · The receiver comes home too (B′ §4.2, 2026-08-26)
A2’s reversion asked only where an explicit &x went. A pointer-receiver call takes an address
too — implicitly — and the census charged every one of them a kept-reason, so a local that also fed
a lowered position kept a box the emitted body never touched. Whether the box is really consumed
turns on what the CALLEE’s receiver is emitted as: a [GoRecv] this ref T binds an addressable
receiver directly and needs none.
ClosureWriteVisibility, from the behavioral corpus — a local written through a closure and
mutated by a pointer-receiver method, which is the shape that makes the question interesting:
t := Tally{5, "s"}
bump := func() { t.total += 100 }
bump()
t.Add(3) // ptr-receiver method, [GoRecv] ref
// before // after
ref var t = ref heap<Tally>(out var Ꮡt); var t = new Tally(5, "s"u8);
t = new Tally(5, "s"u8); void bump() {
void bump() { t.total += 100;
Ꮡt.Value.total += 100; }
}
Nothing is lost by dropping the box, because C# hoists a captured local into a display class and
rewrites every reference to it — the closure’s write and the method’s ref t bind one storage,
exactly as the box used to guarantee. The test’s own stdout comparison against go run is the
proof, and it is unchanged.
What deliberately stays boxed
The RefLoweredParams guard keeps counter-examples beside the lowered ones, so the boundary is
itself under test:
| kept boxed | why |
|---|---|
guarded(ж<uint64> Ꮡp) |
compares the pointer to nil — its identity is observed, not just its target |
boxedBump(ж<uint64> Ꮡout) |
used as a func value — a method group cannot close over a ref |
defer(ᴛ1 => bump(ref ᴛ1.DerefOrNull()), Ꮡx, …) |
the defer/go carve-out: the address must survive past the frame, so the box crosses and the deref happens at invocation |
| in-lambda call sites | uniformly boxed-fallback wrapped .DerefOrNull() — identity-box aliasing preserved through captures |
| string/numeric wrapper reinterprets | outside row 5’s array-wrapper pairing family; a value copy would lose the write |
blank/unnamed pointer params (func setup(_ *note)) |
nothing to gain — excluded after the vacuous lowering emitted an empty parameter name |
receiver of a direct-ж callee (Ꮡz.FieldAddr()) |
the box IS that method’s receiver — it takes &recv.field, so there is nothing for a ref to bind |
receiver of a method VALUE (var g = Ꮡz.Square;, runIt(Ꮡz.Square)) |
a method group cannot close over a ref — the same reason boxedBump stays boxed above, one level up |
receiver under defer/go (defer(Ꮡz.Square, …), goǃ(Ꮡo.of(Outer.Ꮡt).Square)) |
the defer/go carve-out again: the delegate outlives the statement, so the box crosses |
The receiver rows are guarded by refLoweringAnalysis_test.go’s keptBoxMethod / keptMethodValue /
keptDeferRecv / keptGoRecv fixtures beside revertsRefMethod, and the guard was proven in both
directions — restoring the blanket keep reds the revert row, dropping the carve-outs reds exactly
these four. Note what is NOT on this list: a closure-captured receiver, and a call inside a
deferred closure. Both revert, because C#’s own capture provides the frame lifetime the carve-out
exists for — the box would be redundant, not protective.
Every exclusion is the same statement: the lowering never guesses. A shape either carries its escape proof or it keeps the box that made it correct yesterday.