refactor(codegen-native): extract EmitArgsToSlots (>6-slot call marshalling)

Phase 2 item 11 of the de-duplication plan — the largest single dedup.  The
">6-slot store-into-stack-slots" loop (write each logical argument to its
(I+1)*8(%rsp) slot, dispatching per kind: reload a hoisted akRecCall buffer
from its hoist depth / store a var-param address / store a width-adjusted xmm
bit pattern for a by-value float / store %rax for a plain scalar) was copy-
pasted verbatim at 6 call sites — the regular, metaclass-ctor, implicit-Self,
and proc-field >6-slot method paths.  Collapse into EmitArgsToSlots(AArgs,
AParams, AAllocSz, AHoistTotal, AHoistDepths, AHoistKinds); 7 call sites now
share it.

Risk gates preserved as the helper contract: all 6 sites used the same Self-at-
slot-0 base offset (Dest = (I+1)*8) and the same push-phase-free akRecCall
reload formula (AAllocSz + AHoistTotal - depth), so they parameterize cleanly —
no phase/index divergence to thread.

Behaviour-reconciling, but verified behaviour-preserving: native .s for a
>6-arg program (Self+8 int args, and a 7-slot mixed int/double/var-param call)
is byte-identical to pre-refactor HEAD; the native self-host fixpoint (which
compiles the whole compiler, exercising every >6-slot path including metaclass
and proc-field) is unchanged.  All four fixpoints pass; full suite OK (3853
tests) on both QBE-built and native-built test runners.
This commit is contained in:
Graeme Geldenhuys 2026-06-27 10:23:41 +01:00
parent 8e52f7769f
commit 180fbb62b4

View file

@ -726,6 +726,18 @@ type
pattern (movsd %xmm0). }
function EmitMethodOverflowLoad(AParams, AArgs: TObjectList;
AAllocSz: Integer): Integer;
{ Store every logical argument into its 8-byte slot in the >6-slot call
frame already allocated at (%rsp): slot I goes to (I+1)*8(%rsp) (slot 0 is
reserved for Self). Dispatches per arg kind a hoisted akRecCall buffer
is reloaded from its hoist depth, a var/out param stores its address, a
by-value float stores the width-adjusted xmm bit pattern, and a plain
scalar stores %rax. AHoistDepths/AHoistKinds are the (depth, kind) vectors
EmitArgHoist produced; AAllocSz/AHoistTotal frame the akRecCall reload
offset. Shared verbatim by every >6-slot method/implicit-Self/metaclass
call path. }
procedure EmitArgsToSlots(AArgs, AParams: TObjectList;
AAllocSz, AHoistTotal: Integer;
AHoistDepths, AHoistKinds: TList<Integer>);
{ True when the parameter at AIndex is a by-value float-family param (the
caller must store its slot as an xmm bit pattern, not via %rax). }
function OverflowArgIsFloat(AParams: TObjectList; AIndex: Integer): Boolean;
@ -8153,34 +8165,7 @@ begin
OverflowSlots := TotalSlots - 6;
AllocSz := ((TotalSlots * 8 + 15) and (-16));
Self.Emit(Format(#9'subq $%d, %%rsp', [AllocSz]));
for I := 0 to ACall.Args.Count - 1 do
begin
Arg := TASTExpr(ACall.Args.Items[I]);
Dest := (I + 1) * 8;
if HK.Get(I) >= akRecCall then
begin
Self.Emit(Format(#9'movq %d(%%rsp), %%rax',
[AllocSz + HTotal - HD.Get(I)]));
Self.Emit(Format(#9'movq %%rax, %d(%%rsp)', [Dest]));
end
else if TMethodParam(MD.Params.Items[I]).IsVarParam then
begin
Self.EmitVarArgAddrToRax(Arg);
Self.Emit(Format(#9'movq %%rax, %d(%%rsp)', [Dest]));
end
else if Self.OverflowArgIsFloat(MD.Params, I) then
begin
Self.EmitExprToXmm0(Arg);
Self.EmitXmm0WidthAdjust(Arg.ResolvedType,
TMethodParam(MD.Params.Items[I]).ResolvedType.Kind = tySingle);
Self.Emit(Format(#9'movsd %%xmm0, %d(%%rsp)', [Dest]));
end
else
begin
Self.EmitExprToEax(Arg);
Self.Emit(Format(#9'movq %%rax, %d(%%rsp)', [Dest]));
end;
end;
Self.EmitArgsToSlots(ACall.Args, MD.Params, AllocSz, HTotal, HD, HK);
Self.Emit(Format(#9'movq %%rbx, 0(%%rsp)', []));
CleanUp := Self.EmitMethodOverflowLoad(MD.Params, ACall.Args, AllocSz);
if ACall.IsMetaclassDispatch and (MD.VTableSlot >= 0) then
@ -8354,34 +8339,7 @@ begin
HTotal := Self.EmitArgHoist(MD.Params, nil, True, '', ACall.Args, HD, HK);
Self.Emit(Format(#9'subq $%d, %%rsp', [AllocSz]));
for I := 0 to ACall.Args.Count - 1 do
begin
Arg := TASTExpr(ACall.Args.Items[I]);
Dest := (I + 1) * 8;
if HK.Get(I) >= akRecCall then
begin
Self.Emit(Format(#9'movq %d(%%rsp), %%rax',
[AllocSz + HTotal - HD.Get(I)]));
Self.Emit(Format(#9'movq %%rax, %d(%%rsp)', [Dest]));
end
else if TMethodParam(MD.Params.Items[I]).IsVarParam then
begin
Self.EmitVarArgAddrToRax(Arg);
Self.Emit(Format(#9'movq %%rax, %d(%%rsp)', [Dest]));
end
else if Self.OverflowArgIsFloat(MD.Params, I) then
begin
Self.EmitExprToXmm0(Arg);
Self.EmitXmm0WidthAdjust(Arg.ResolvedType,
TMethodParam(MD.Params.Items[I]).ResolvedType.Kind = tySingle);
Self.Emit(Format(#9'movsd %%xmm0, %d(%%rsp)', [Dest]));
end
else
begin
Self.EmitExprToEax(Arg);
Self.Emit(Format(#9'movq %%rax, %d(%%rsp)', [Dest]));
end;
end;
Self.EmitArgsToSlots(ACall.Args, MD.Params, AllocSz, HTotal, HD, HK);
if ACall.ObjectName <> '' then
begin
@ -8450,33 +8408,7 @@ begin
HK := TList<Integer>.Create();
HTotal := Self.EmitArgHoist(MD.Params, nil, True, '', AArgs, HD, HK);
Self.Emit(Format(#9'subq $%d, %%rsp', [AllocSz]));
for I := 0 to AArgs.Count - 1 do
begin
Arg := TASTExpr(AArgs.Items[I]);
Dest := (I + 1) * 8;
if HK.Get(I) >= akRecCall then
begin
Self.Emit(Format(#9'movq %d(%%rsp), %%rax', [AllocSz + HTotal - HD.Get(I)]));
Self.Emit(Format(#9'movq %%rax, %d(%%rsp)', [Dest]));
end
else if TMethodParam(MD.Params.Items[I]).IsVarParam then
begin
Self.EmitVarArgAddrToRax(Arg);
Self.Emit(Format(#9'movq %%rax, %d(%%rsp)', [Dest]));
end
else if Self.OverflowArgIsFloat(MD.Params, I) then
begin
Self.EmitExprToXmm0(Arg);
Self.EmitXmm0WidthAdjust(Arg.ResolvedType,
TMethodParam(MD.Params.Items[I]).ResolvedType.Kind = tySingle);
Self.Emit(Format(#9'movsd %%xmm0, %d(%%rsp)', [Dest]));
end
else
begin
Self.EmitExprToEax(Arg);
Self.Emit(Format(#9'movq %%rax, %d(%%rsp)', [Dest]));
end;
end;
Self.EmitArgsToSlots(AArgs, MD.Params, AllocSz, HTotal, HD, HK);
{ Self into slot 0. }
Self.Emit(Format(#9'movq %s, %%rax', [Self.VarOperand('Self')]));
Self.Emit(#9'movq %rax, 0(%rsp)');
@ -8992,6 +8924,43 @@ begin
not P.IsOpenArray;
end;
procedure TX86_64Backend.EmitArgsToSlots(AArgs, AParams: TObjectList;
AAllocSz, AHoistTotal: Integer;
AHoistDepths, AHoistKinds: TList<Integer>);
var
I, Dest: Integer;
Arg: TASTExpr;
begin
for I := 0 to AArgs.Count - 1 do
begin
Arg := TASTExpr(AArgs.Items[I]);
Dest := (I + 1) * 8;
if AHoistKinds.Get(I) >= akRecCall then
begin
Self.Emit(Format(#9'movq %d(%%rsp), %%rax',
[AAllocSz + AHoistTotal - AHoistDepths.Get(I)]));
Self.Emit(Format(#9'movq %%rax, %d(%%rsp)', [Dest]));
end
else if TMethodParam(AParams.Items[I]).IsVarParam then
begin
Self.EmitVarArgAddrToRax(Arg);
Self.Emit(Format(#9'movq %%rax, %d(%%rsp)', [Dest]));
end
else if Self.OverflowArgIsFloat(AParams, I) then
begin
Self.EmitExprToXmm0(Arg);
Self.EmitXmm0WidthAdjust(Arg.ResolvedType,
TMethodParam(AParams.Items[I]).ResolvedType.Kind = tySingle);
Self.Emit(Format(#9'movsd %%xmm0, %d(%%rsp)', [Dest]));
end
else
begin
Self.EmitExprToEax(Arg);
Self.Emit(Format(#9'movq %%rax, %d(%%rsp)', [Dest]));
end;
end;
end;
function TX86_64Backend.EmitMethodOverflowLoad(AParams, AArgs: TObjectList;
AAllocSz: Integer): Integer;
var
@ -9155,34 +9124,7 @@ begin
OverflowSlots := TotalSlots - 6;
AllocSz := ((TotalSlots * 8 + 15) and (-16));
Self.Emit(Format(#9'subq $%d, %%rsp', [AllocSz]));
for I := 0 to ACall.Args.Count - 1 do
begin
Arg := TASTExpr(ACall.Args.Items[I]);
Dest := (I + 1) * 8;
if HK.Get(I) >= akRecCall then
begin
Self.Emit(Format(#9'movq %d(%%rsp), %%rax',
[AllocSz + HTotal - HD.Get(I)]));
Self.Emit(Format(#9'movq %%rax, %d(%%rsp)', [Dest]));
end
else if TMethodParam(MD.Params.Items[I]).IsVarParam then
begin
Self.EmitVarArgAddrToRax(Arg);
Self.Emit(Format(#9'movq %%rax, %d(%%rsp)', [Dest]));
end
else if Self.OverflowArgIsFloat(MD.Params, I) then
begin
Self.EmitExprToXmm0(Arg);
Self.EmitXmm0WidthAdjust(Arg.ResolvedType,
TMethodParam(MD.Params.Items[I]).ResolvedType.Kind = tySingle);
Self.Emit(Format(#9'movsd %%xmm0, %d(%%rsp)', [Dest]));
end
else
begin
Self.EmitExprToEax(Arg);
Self.Emit(Format(#9'movq %%rax, %d(%%rsp)', [Dest]));
end;
end;
Self.EmitArgsToSlots(ACall.Args, MD.Params, AllocSz, HTotal, HD, HK);
Self.Emit(Format(#9'movq %%rbx, 0(%%rsp)', []));
CleanUp := Self.EmitMethodOverflowLoad(MD.Params, ACall.Args, AllocSz);
if MD.VTableSlot >= 0 then
@ -9399,34 +9341,7 @@ begin
HTotal := Self.EmitArgHoist(MD.Params, nil, True, '', ACall.Args, HD, HK);
Self.Emit(Format(#9'subq $%d, %%rsp', [AllocSz]));
for I := 0 to ACall.Args.Count - 1 do
begin
Arg := TASTExpr(ACall.Args.Items[I]);
Dest := (I + 1) * 8;
if HK.Get(I) >= akRecCall then
begin
Self.Emit(Format(#9'movq %d(%%rsp), %%rax',
[AllocSz + HTotal - HD.Get(I)]));
Self.Emit(Format(#9'movq %%rax, %d(%%rsp)', [Dest]));
end
else if TMethodParam(MD.Params.Items[I]).IsVarParam then
begin
Self.EmitVarArgAddrToRax(Arg);
Self.Emit(Format(#9'movq %%rax, %d(%%rsp)', [Dest]));
end
else if Self.OverflowArgIsFloat(MD.Params, I) then
begin
Self.EmitExprToXmm0(Arg);
Self.EmitXmm0WidthAdjust(Arg.ResolvedType,
TMethodParam(MD.Params.Items[I]).ResolvedType.Kind = tySingle);
Self.Emit(Format(#9'movsd %%xmm0, %d(%%rsp)', [Dest]));
end
else
begin
Self.EmitExprToEax(Arg);
Self.Emit(Format(#9'movq %%rax, %d(%%rsp)', [Dest]));
end;
end;
Self.EmitArgsToSlots(ACall.Args, MD.Params, AllocSz, HTotal, HD, HK);
if ACall.IsImplicitSelf and (ACall.ImplicitBaseInfo <> nil) then
begin
@ -9546,34 +9461,7 @@ begin
HK := TList<Integer>.Create();
HTotal := Self.EmitArgHoist(MD.Params, nil, True, '', AArgs, HD, HK);
Self.Emit(Format(#9'subq $%d, %%rsp', [AllocSz]));
for I := 0 to AArgs.Count - 1 do
begin
Arg := TASTExpr(AArgs.Items[I]);
Dest := (I + 1) * 8;
if HK.Get(I) >= akRecCall then
begin
Self.Emit(Format(#9'movq %d(%%rsp), %%rax',
[AllocSz + HTotal - HD.Get(I)]));
Self.Emit(Format(#9'movq %%rax, %d(%%rsp)', [Dest]));
end
else if TMethodParam(MD.Params.Items[I]).IsVarParam then
begin
Self.EmitVarArgAddrToRax(Arg);
Self.Emit(Format(#9'movq %%rax, %d(%%rsp)', [Dest]));
end
else if Self.OverflowArgIsFloat(MD.Params, I) then
begin
Self.EmitExprToXmm0(Arg);
Self.EmitXmm0WidthAdjust(Arg.ResolvedType,
TMethodParam(MD.Params.Items[I]).ResolvedType.Kind = tySingle);
Self.Emit(Format(#9'movsd %%xmm0, %d(%%rsp)', [Dest]));
end
else
begin
Self.EmitExprToEax(Arg);
Self.Emit(Format(#9'movq %%rax, %d(%%rsp)', [Dest]));
end;
end;
Self.EmitArgsToSlots(AArgs, MD.Params, AllocSz, HTotal, HD, HK);
{ Self into slot 0 (the implicit first integer arg). }
Self.Emit(Format(#9'movq %s, %%rax', [Self.VarOperand('Self')]));
Self.Emit(Format(#9'movq %%rax, 0(%%rsp)', []));
@ -11894,33 +11782,7 @@ begin
PCHK := TList<Integer>.Create();
PCHTotal := Self.EmitArgHoist(MD.Params, nil, True, '', PC.Args, PCHD, PCHK);
Self.Emit(Format(#9'subq $%d, %%rsp', [PCAllocSz]));
for I := 0 to PC.Args.Count - 1 do
begin
PCDest := (I + 1) * 8;
if PCHK.Get(I) >= akRecCall then
begin
Self.Emit(Format(#9'movq %d(%%rsp), %%rax',
[PCAllocSz + PCHTotal - PCHD.Get(I)]));
Self.Emit(Format(#9'movq %%rax, %d(%%rsp)', [PCDest]));
end
else if TMethodParam(MD.Params.Items[I]).IsVarParam then
begin
Self.EmitVarArgAddrToRax(TASTExpr(PC.Args.Items[I]));
Self.Emit(Format(#9'movq %%rax, %d(%%rsp)', [PCDest]));
end
else if Self.OverflowArgIsFloat(MD.Params, I) then
begin
Self.EmitExprToXmm0(TASTExpr(PC.Args.Items[I]));
Self.EmitXmm0WidthAdjust(TASTExpr(PC.Args.Items[I]).ResolvedType,
TMethodParam(MD.Params.Items[I]).ResolvedType.Kind = tySingle);
Self.Emit(Format(#9'movsd %%xmm0, %d(%%rsp)', [PCDest]));
end
else
begin
Self.EmitExprToEax(TASTExpr(PC.Args.Items[I]));
Self.Emit(Format(#9'movq %%rax, %d(%%rsp)', [PCDest]));
end;
end;
Self.EmitArgsToSlots(PC.Args, MD.Params, PCAllocSz, PCHTotal, PCHD, PCHK);
{ Self into slot 0 -> %rdi. }
Self.Emit(Format(#9'movq %s, %%rax', [Self.VarOperand('Self')]));
Self.Emit(#9'movq %rax, 0(%rsp)');