← board

EmitLoadVarA64 needs a destination register — DUPLICATE

Closed as a duplicate of [[feature-opt-emitloadvara64-needs-a-destination-register-parameter]], which the coordinator filed for the same work within minutes of this one. That ticket is the survivor: it is already ranked in backlog/ and now carries the hazard analysis (the x1-scratch self-clobber) that this one predates.

Kept rather than deleted because 1185b3489 and the umbrella ticket both cite this slug. Everything below is the original text.


EmitLoadVarA64 needs a destination register

1185b3489 collapsed aarch64's per-binop push / eval / mov / pop dance for a constant right operand. The measured population it left behind:

program integer binops CONST (done) LEAFSYM (this ticket)
compiler.pas 54056 80.9% 12.6%
mandelbrot 4953 77.7% 16.3%
chess 4917 77.7% 16.0%
jsondemo 8016 77.9% 16.0%

Measured with PXXDBG=a.a64binop, which is already in the tree.

Why it was not done in the port

x86-64's arm loads the right operand straight into rcx while the left sits in raxEmitLoadVarRcx is a real second entry point. aarch64 has no equivalent: EmitLoadVarA64 hardcodes x0, and it is not a thin function. It carries

Duplicating that into an EmitLoadVarX1A64 twin would create exactly the second path that devdocs/dev/normalise-dont-special-case.md exists to prevent, and the dyn-array comment sitting inside the function is the proof that this particular helper is one where the arms do drift apart.

The cheap-looking alternative — evaluate left, mov x1, x0, load right into x0, and let the op read the operands reversed — is only valid for commutative ops, so it would either exclude sub/div/shifts or need reversed encodings per arm. That is a second path in a different costume.

What to do

Give EmitLoadVarA64 a destination-register parameter and thread it through every arm — residency, float residency, dyn-array, extension. Keep the existing name as a one-line wrapper passing 0 so the ~dozens of existing call sites are untouched, then add the LEAFSYM arm to the binop path alongside the CONST one it already has.

The extension arms are the risk, not the residency ones: several encode the destination implicitly in the opcode constants ($93401C00 style), so a register parameter has to reach into each encoding rather than be OR-ed in uniformly. Budget the review for that, not for the plumbing.

Gate

Track A: make compiler/pascal26 (self-host fixedpoint) plus the aarch64 differential against a pre-change compiler — 10 programs x -O0/-O2/-O3 under qemu-aarch64, which is the harness 1185b3489 used, and it must report the count of pairs it compared. Land behind -O3 and promote per the campaign rule.

Note on measuring it

Do not expect a trustworthy timing number on this box: qemu-aarch64 does not model store-to-load forwarding, which is most of what removing two stack ops per binop is worth, and there is no valgrind or TCG plugin here for a deterministic count. Quote code size and instruction count from the artefact, as the port did.