An interface (itab-dispatched) method returning a RECORD was broken on both
backends, both when the result was assigned and when discarded in statement
position, for memory-class (sret) and register-class records alike. The itab
dispatch had no record-return ABI: it emitted a scalar call, so for a
memory-class return the first argument register doubled as the sret pointer
(the callee received a garbage argument and wrote the record over caller
memory — "_StringRelease corrupted header"), and a register-class return was
dereferenced as a pointer. Assignment was equally broken because
IsRecordCall / IsNativeRecordCall keyed off ResolvedMethod, which is nil for
itab dispatch.
QBE:
* IsRecordCall recognises an itab record call (ResolvedClassType = interface +
ResolvedType in [record, static-array]) for both the args form
(TMethodCallExpr) and the zero-arg form (TFieldAccessExpr.IsInterfaceCall).
* EmitIntfSretDispatch builds the visible args (Self, method args) and delegates
to EmitRecordReturnCallSite, which applies the correct ABI from
ClassifyRecordReturn (sret pointer vs register capture) — instead of always
passing an sret buffer.
* The discarded-statement path (EmitMethodCall) gained a record/static-array
branch: a throwaway zeroed buffer, the classified call, then
EmitRecordReleaseFields on the buffer.
Native:
* IsNativeRecordCall recognises the itab record case.
* New EmitIntfRecordSretDispatch performs itab dispatch with the full
record-return ABI: an sret buffer in %rdi for a memory-class record, or
EmitRecordRegReturnCapture (a new helper covering rcInt1/rcInt2/rcSSE1/
rcSSE2/rcIntSSE/rcSSEInt) storing the register result into the destination.
Wired into EmitRecordCallSretAt, both record-assignment branches (implicit-Self
field and plain local/var/global), and the discarded-statement path.
E2E regressions (both backends, via AssertRunsOnAll):
TE2EInterfaceTests.TestRun_InterfaceMethod_ReturnsSretRecord and
_ReturnsRegisterRecord — each exercises an assigned and a discarded call.