From aa52ab6bef4322394fba9810228916717c5eff1d Mon Sep 17 00:00:00 2001 From: Graeme Geldenhuys Date: Sat, 27 Jun 2026 11:51:08 +0100 Subject: [PATCH] refactor(codegen-native): extract BuildArgSlotClasses (item 10) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- .../pascal/blaise.codegen.native.x86_64.pas | 106 +++++++++--------- 1 file changed, 50 insertions(+), 56 deletions(-) diff --git a/compiler/src/main/pascal/blaise.codegen.native.x86_64.pas b/compiler/src/main/pascal/blaise.codegen.native.x86_64.pas index f3758d2..7c9374a 100644 --- a/compiler/src/main/pascal/blaise.codegen.native.x86_64.pas +++ b/compiler/src/main/pascal/blaise.codegen.native.x86_64.pas @@ -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); { 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); var - I, NParams, IntIdx, XmmIdx: Integer; + I, NParams: Integer; P: TMethodParam; PT: TTypeDesc; - IsFloatSlot: TList; { 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; { 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.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; { per flat slot (slot 0 = Self): 1 = xmm } OverflowOffs: TList; { source offsets of integer-overflow slots } RK, RSrc, RDst, OverflowBytes: Integer; begin - NParams := 0; - if AParams <> nil then NParams := AParams.Count; - IsFloatSlot := TList.Create(); OverflowOffs := TList.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. }