A float (Double/Single) by-value argument passed to a function or method that
returns a record with a managed field (dynamic array / string, i.e. the true
sret return path) was mis-handled by the native backend: every argument was
materialised via EmitExprToEax, the integer-only emitter. A float LITERAL hit
"unsupported expression form TFloatLiteral", and a float VARIABLE would have
been routed through %rax into an integer argument register instead of an %xmm
register — wrong per the SysV ABI. QBE compiled these correctly, so it was a
native/QBE parity divergence.
This blocked DecFromFloat / DecFromFloatExact (TDecimal has a dynamic-array
field) and any MoneyFromFloat-style constructor in Numerics.Money.
Fix: both EmitSretCall (free functions) and EmitMethodSretCall (methods) now
detect a float-typed by-value argument and, when present, evaluate arguments
into stack slots and load them into registers per the SysV ABI — integer args
into the integer argument registers (after %rdi=sret and, for methods,
%rsi=Self), float args into %xmm0.. with an independent counter, and %al set to
the vector-register count. The pure-integer path is unchanged, so the common
case (and native self-compile) is byte-for-byte identical.
Adds three e2e regression tests (cp.test.e2e.recordret.pas), run on both
backends via AssertRunsOnAll: single float arg to a managed-record-returning
function, two float args, and a single float arg to a managed-record-returning
method.
Verified: FIXPOINT_OK, NATIVE_FIXPOINT_OK, NATIVE_INTERNAL_OK; the new tests
pass on both the QBE and native arms.