flan/HANDOFF-x86-aggregates.md

2.9 KiB

Handoff — the aggregate case across the reload boundary

Branch dev-loop, from 957ba07. This closes item 4 of HANDOFF-x86-redef.md: a redefined function that takes and returns a struct, which is the one case the two backends' conventions disagree about and the reason X86.redefinition exists at all rather than reusing Emit.redefinition.

Status: in progress. This file is written early and on purpose, so that a lane that picks the work up mid-way has the plan and not just the diff.

The plan

  1. A fixture pair, test/programs/reload-agg.flan and reload-agg-v2.flan, beside the existing reload.flan rather than inside it — reload.flan's transcript is shared with the LLVM path and must not move. No main; the host is test/reload_host.c unchanged, which wants outer : () -> i64 and a counter global. Everything aggregate happens inside Flan, between the host's compiled-once call site and the redefined body, because the C boundary is where the two conventions are required to agree and is not what is under test.
  2. Four struct shapes, because SysV treats them in four different ways and x86.ml treats all four the same:
    • ≤16 bytes, all integer — SysV passes in rdi:rsi and returns in rax:rdx; x86.ml passes a pointer and returns through a hidden sret. Diverges in both directions.
    • >16 bytes — SysV copies the argument onto the stack, which x86.ml does not; the return, though, is a hidden pointer in the first integer register for SysV too, so that half may well coincide. Worth knowing, and worth not assuming either way.
    • {f64, f64} — SysV classifies both eightbytes SSE and uses xmm0:xmm1.
    • {i64, f64} — one INTEGER and one SSE eightbyte, so rax and xmm0, which is a third register pattern again.
  3. Every field weighted by position in the printed answer (a + 100*b + 10000*c + …), so that a scrambled field order, a wrong base pointer or a half-loaded eightbyte prints a visibly wrong integer rather than the right one by symmetry. A test whose answer is a symmetric sum of the fields passes under a convention mismatch that merely permutes them.
  4. Run it on both backends: the LLVM Emit.redefinition path as the control, and the X86.redefinition path as the claim. The same transcript from both is the measurement.

Open questions recorded rather than asked

  • Whether the MEMORY-return half of the matrix genuinely coincides between the two conventions, or only looks as though it should. Measured by the mismatch spike rather than argued.

Baseline

before
spike/x86/survey.sh 99 MATCH / 0 DIFFER / 0 refused
spike/x86/cells.sh 4/4 ok
dune test --root . exit 0

Note in advance: survey.sh globs test/programs/*.flan, so each new no-main fixture adds one to its no-main skip count. That number moving is the fixtures existing and is not a regression; the MATCH count is what must not move.