refactor(codegen-native): extract BuildArgSlotClasses (item 10)

The per-argument SysV slot classification — open-array and by-value
interface occupy two integer slots, a by-value Double/Single occupies one
xmm slot, everything else one integer slot — was duplicated verbatim in
EmitPopMethodArgsToRegs and EmitMethodOverflowLoad (the <=6-slot pop path
and the >6-slot overflow-load path). Hoist the classification loop into
BuildArgSlotClasses(AParams, AArgs, AList); each caller still prepends its
own Self slot (overflow-load adds slot 0, the pop path handles Self via
AIntBase) and consumes the list as before.

Pure dedup: byte-identical native .s verified on a method-call program
exercising float args, integer overflow (>6 slots), open-array, and
interface params. All four fixpoints and the full suite (QBE-built and
native-built test runners) unchanged.
This commit is contained in:
Graeme Geldenhuys 2026-06-27 11:51:08 +01:00
parent 5232ea7bc0
commit aa52ab6bef

View file

@ -709,6 +709,15 @@ type
its xmm register instead of being mis-passed in an integer register. }
procedure EmitPopMethodArgsToRegs(AParams, AArgs: TObjectList;
AIntBase: Integer);
{ Append one SysV slot-class entry per logical argument to AList (1 = float
eightbyte xmm, 0 = integer/pointer eightbyte integer register). Open
arrays and by-value interfaces occupy two integer slots; a by-value
Double/Single occupies one xmm slot; everything else is one integer slot.
The receiver/Self slot is NOT added here callers prepend it as needed.
Shared by EmitPopMethodArgsToRegs and EmitMethodOverflowLoad so the
classification stays single-sourced. }
procedure BuildArgSlotClasses(AParams, AArgs: TObjectList;
AList: TList<Integer>);
{ Emit the call for a method whose receiver (Self) is already in %rdi:
a vtable dispatch when AMD is virtual (VTableSlot >= 0) so the call
respects polymorphism, otherwise a static callq to AStaticSym. Used by
@ -8830,49 +8839,57 @@ begin
Self.Emit(#9'callq ' + AStaticSym);
end;
procedure TX86_64Backend.EmitPopMethodArgsToRegs(AParams, AArgs: TObjectList;
AIntBase: Integer);
procedure TX86_64Backend.BuildArgSlotClasses(AParams, AArgs: TObjectList;
AList: TList<Integer>);
var
I, NParams, IntIdx, XmmIdx: Integer;
I, NParams: Integer;
P: TMethodParam;
PT: TTypeDesc;
IsFloatSlot: TList<Integer>; { 1 = float (xmm) slot, 0 = integer slot,
in stack-slot order (slot 0 pushed first) }
Dest: TStringList; { target register per slot, parallel to above }
Slots: Integer;
begin
{ Determine the param backing each logical arg. AParams may be nil (no
declared params): then every arg is a plain integer slot. }
NParams := 0;
if AParams <> nil then NParams := AParams.Count;
for I := 0 to AArgs.Count - 1 do
begin
P := nil;
if I < NParams then P := TMethodParam(AParams.Items[I]);
PT := nil;
if P <> nil then PT := P.ResolvedType;
if (PT = nil) and (TASTExpr(AArgs.Items[I]).ResolvedType <> nil) then
PT := TASTExpr(AArgs.Items[I]).ResolvedType;
if (P <> nil) and P.IsOpenArray then
begin
AList.Add(0); AList.Add(0); { ptr + high }
end
else if (PT <> nil) and (PT.Kind = tyInterface) and
((P = nil) or not P.IsVarParam) then
begin
AList.Add(0); AList.Add(0); { obj + itab }
end
else if IsFloatFamily(PT) and ((P = nil) or
(not P.IsVarParam and not P.IsOpenArray)) then
AList.Add(1) { float scalar -> xmm }
else
AList.Add(0); { integer/ptr scalar }
end;
end;
procedure TX86_64Backend.EmitPopMethodArgsToRegs(AParams, AArgs: TObjectList;
AIntBase: Integer);
var
I, IntIdx, XmmIdx: Integer;
IsFloatSlot: TList<Integer>; { 1 = float (xmm) slot, 0 = integer slot,
in stack-slot order (slot 0 pushed first) }
Dest: TStringList; { target register per slot, parallel to above }
Slots: Integer;
begin
IsFloatSlot := TList<Integer>.Create();
Dest := TStringList.Create();
try
{ Forward pass: one entry per stack slot, in push order (slot 0 first). }
for I := 0 to AArgs.Count - 1 do
begin
P := nil;
if I < NParams then P := TMethodParam(AParams.Items[I]);
PT := nil;
if P <> nil then PT := P.ResolvedType;
if (PT = nil) and (TASTExpr(AArgs.Items[I]).ResolvedType <> nil) then
PT := TASTExpr(AArgs.Items[I]).ResolvedType;
if (P <> nil) and P.IsOpenArray then
begin
IsFloatSlot.Add(0); IsFloatSlot.Add(0); { ptr + high }
end
else if (PT <> nil) and (PT.Kind = tyInterface) and
((P = nil) or not P.IsVarParam) then
begin
IsFloatSlot.Add(0); IsFloatSlot.Add(0); { obj + itab }
end
else if IsFloatFamily(PT) and ((P = nil) or
(not P.IsVarParam and not P.IsOpenArray)) then
IsFloatSlot.Add(1) { float scalar -> xmm }
else
IsFloatSlot.Add(0); { integer/ptr scalar }
end;
Self.BuildArgSlotClasses(AParams, AArgs, IsFloatSlot);
{ Assign a register to each slot in forward order: integers consume the
SysV integer registers from AIntBase, floats consume xmm0.. the two
@ -8966,40 +8983,17 @@ end;
function TX86_64Backend.EmitMethodOverflowLoad(AParams, AArgs: TObjectList;
AAllocSz: Integer): Integer;
var
I, NParams, IntIdx, XmmIdx, SlotOff: Integer;
P: TMethodParam;
PT: TTypeDesc;
I, IntIdx, XmmIdx, SlotOff: Integer;
IsFloatSlot: TList<Integer>; { per flat slot (slot 0 = Self): 1 = xmm }
OverflowOffs: TList<Integer>; { source offsets of integer-overflow slots }
RK, RSrc, RDst, OverflowBytes: Integer;
begin
NParams := 0;
if AParams <> nil then NParams := AParams.Count;
IsFloatSlot := TList<Integer>.Create();
OverflowOffs := TList<Integer>.Create();
try
{ Slot 0 = Self/receiver — always integer. }
{ Slot 0 = Self/receiver — always integer. The per-arg slots follow. }
IsFloatSlot.Add(0);
for I := 0 to AArgs.Count - 1 do
begin
P := nil;
if I < NParams then P := TMethodParam(AParams.Items[I]);
PT := nil;
if P <> nil then PT := P.ResolvedType;
if (PT = nil) and (TASTExpr(AArgs.Items[I]).ResolvedType <> nil) then
PT := TASTExpr(AArgs.Items[I]).ResolvedType;
if (P <> nil) and P.IsOpenArray then
begin IsFloatSlot.Add(0); IsFloatSlot.Add(0); end
else if (PT <> nil) and (PT.Kind = tyInterface) and
((P = nil) or not P.IsVarParam) then
begin IsFloatSlot.Add(0); IsFloatSlot.Add(0); end
else if IsFloatFamily(PT) and ((P = nil) or
(not P.IsVarParam and not P.IsOpenArray)) then
IsFloatSlot.Add(1)
else
IsFloatSlot.Add(0);
end;
Self.BuildArgSlotClasses(AParams, AArgs, IsFloatSlot);
{ Load register-bound slots; record integer-overflow slot offsets. Self is
integer register 0 (%rdi); arg integers continue from index 1. }