refactor(ast): replace TIdentExpr.IsVarParam with TParamMode enum

Introduce TParamMode (pmNone, pmVar, pmRecordValue, pmStaticArrayValue)
on TIdentExpr so the semantic pass records the precise param-slot
classification once instead of merging three cases into a Boolean.

The QBE backend treats all three modes identically (ParamMode <> pmNone),
preserving existing semantics.  The native x86-64 and LLVM backends can
now dispatch on the enum directly instead of re-deriving the distinction
from Sym.Kind and TypeDesc.Kind at every reader site.

Other IsVarParam fields (TFieldAccessExpr, TMethodCallExpr, TAssignment,
TMethodParam, TMethodCallStmt) are unchanged.

Based on Andrew Haines' proposal and patch (blaise_llvm d16f877).
This commit is contained in:
Graeme Geldenhuys 2026-06-10 02:06:43 +01:00
parent d00323e0b2
commit 40755b987a
6 changed files with 63 additions and 50 deletions

View file

@ -2846,7 +2846,7 @@ begin
if TIdentExpr(AExpr).IsConstant then
Self.Emit(Format(#9'movabsq $%s, %%rax',
[IntToStr(TIdentExpr(AExpr).ConstValue)]))
else if TIdentExpr(AExpr).IsVarParam then
else if TIdentExpr(AExpr).ParamMode <> pmNone then
begin
Self.Emit(Format(#9'movq %s, %%rcx',
[Self.VarOperand(TIdentExpr(AExpr).Name)]));
@ -4327,7 +4327,7 @@ begin
{ @VarParam — the variable's slot holds a pointer to the caller's data;
load that pointer value (not the slot address). }
if (AOE.Expr is TIdentExpr) and TIdentExpr(AOE.Expr).IsVarParam then
if (AOE.Expr is TIdentExpr) and (TIdentExpr(AOE.Expr).ParamMode <> pmNone) then
begin
Self.Emit(Format(#9'movq %s, %%rax',
[Self.VarOperand(TIdentExpr(AOE.Expr).Name)]));
@ -4770,7 +4770,7 @@ begin
begin
{ Pass the argument's address. If the arg is itself a var param the slot
already holds a pointer; otherwise take the address of the local/global. }
if (AArg is TIdentExpr) and TIdentExpr(AArg).IsVarParam then
if (AArg is TIdentExpr) and (TIdentExpr(AArg).ParamMode <> pmNone) then
Self.Emit(Format(#9'movq %s, %%rax',
[Self.VarOperand(TIdentExpr(AArg).Name)]))
else if (AArg is TIdentExpr) and Self.IsLocal(TIdentExpr(AArg).Name) then
@ -7589,7 +7589,7 @@ begin
end
else if IsVar then
begin
if (Arg is TIdentExpr) and TIdentExpr(Arg).IsVarParam then
if (Arg is TIdentExpr) and (TIdentExpr(Arg).ParamMode <> pmNone) then
Self.Emit(Format(#9'movq %s, %%rax',
[Self.VarOperand(TIdentExpr(Arg).Name)]))
else if (Arg is TIdentExpr) and Self.IsLocal(TIdentExpr(Arg).Name) then
@ -7692,7 +7692,7 @@ begin
end
else if IsVar then
begin
if (Arg is TIdentExpr) and TIdentExpr(Arg).IsVarParam then
if (Arg is TIdentExpr) and (TIdentExpr(Arg).ParamMode <> pmNone) then
Self.Emit(Format(#9'movq %s, %%rax',
[Self.VarOperand(TIdentExpr(Arg).Name)]))
else if (Arg is TIdentExpr) and Self.IsLocal(TIdentExpr(Arg).Name) then
@ -7798,7 +7798,7 @@ begin
TProcParamInfo(AProcType.Params.Items[I]).IsVarParam;
if IsVar then
begin
if (Arg is TIdentExpr) and TIdentExpr(Arg).IsVarParam then
if (Arg is TIdentExpr) and (TIdentExpr(Arg).ParamMode <> pmNone) then
Self.Emit(Format(#9'movq %s, %%rax',
[Self.VarOperand(TIdentExpr(Arg).Name)]))
else if (Arg is TIdentExpr) and Self.IsLocal(TIdentExpr(Arg).Name) then

View file

@ -69,10 +69,17 @@ type
TNilLiteral = class(TASTExpr); { nil keyword — type is tyNil }
TParamMode = (
pmNone,
pmVar,
pmRecordValue,
pmStaticArrayValue
);
TIdentExpr = class(TASTExpr)
public
Name: string;
IsVarParam: Boolean; { set by uSemantic — True if this ident is a var parameter }
ParamMode: TParamMode; { set by uSemantic — how the param slot is laid out }
IsConstant: Boolean; { set by uSemantic — True if this ident is a skConstant symbol }
ConstValue: Int64; { valid when IsConstant = True; integer/bool/enum value }
ConstString: string; { valid when IsConstant = True and type is tyString }
@ -1690,7 +1697,7 @@ begin
begin
IE := TIdentExpr.Create();
IE.Name := TIdentExpr(AExpr).Name;
{ Note: leave IsConstant/IsVarParam/IsImplicitSelf/etc. nil/false —
{ Note: leave IsConstant/ParamMode/IsImplicitSelf/etc. nil/false —
semantic analyser must re-resolve in the new instance's scope. }
Result := IE;
end

View file

@ -144,7 +144,7 @@ begin
end;
if TIdentExpr(AExpr).IsImplicitSelfMethod then
Flags := Flags + ' ImplicitSelfMethod';
if TIdentExpr(AExpr).IsVarParam then
if TIdentExpr(AExpr).ParamMode <> pmNone then
Flags := Flags + ' VarParam';
if TIdentExpr(AExpr).IsGlobal then
Flags := Flags + ' Global';

View file

@ -4322,7 +4322,7 @@ begin
EmitLine(Format(' %s =l loadl %s', [Loaded, VarRef(Id.Name, Id.IsGlobal)]));
Result := Loaded;
end
else if Id.IsVarParam then
else if Id.ParamMode <> pmNone then
begin
{ Var-record param: dereference the param slot to get the actual record
address. }
@ -4449,7 +4449,7 @@ var
SelfT: string;
ImplFld: TFieldInfo;
begin
if AIdent.IsVarParam then
if AIdent.ParamMode <> pmNone then
begin
// The local slot holds the caller's pointer — load it so we pass the
// original address, not the address of the local slot.
@ -10216,7 +10216,7 @@ begin
EmitLine(Format(' %s =w loadw %%_cap_%s', [T, TIdentExpr(AExpr).Name]));
end;
end
else if TIdentExpr(AExpr).IsVarParam and
else if (TIdentExpr(AExpr).ParamMode <> pmNone) and
(AExpr.ResolvedType <> nil) and
(AExpr.ResolvedType.Kind in [tyRecord, tyStaticArray]) then
begin
@ -10225,7 +10225,7 @@ begin
EmitLine(Format(' %s =l loadl %%_var_%s', [T, TIdentExpr(AExpr).Name]));
Exit(T);
end
else if TIdentExpr(AExpr).IsVarParam then
else if TIdentExpr(AExpr).ParamMode <> pmNone then
begin
{ Var param of scalar type: load pointer, then dereference to get value }
Ptr := AllocTemp();

View file

@ -7938,10 +7938,16 @@ begin
the QBE ABI level: the local slot holds a pointer, not the aggregate
bytes. Codegen must dereference the slot before reading fields. }
TIdentExpr(AExpr).Name := Sym.Name; { normalise to declared casing }
TIdentExpr(AExpr).IsVarParam :=
(Sym.Kind = skVarParameter) or
((Sym.Kind = skParameter) and (Sym.TypeDesc <> nil) and
(Sym.TypeDesc.Kind in [tyRecord, tyStaticArray]));
if Sym.Kind = skVarParameter then
TIdentExpr(AExpr).ParamMode := pmVar
else if (Sym.Kind = skParameter) and (Sym.TypeDesc <> nil) and
(Sym.TypeDesc.Kind = tyRecord) then
TIdentExpr(AExpr).ParamMode := pmRecordValue
else if (Sym.Kind = skParameter) and (Sym.TypeDesc <> nil) and
(Sym.TypeDesc.Kind = tyStaticArray) then
TIdentExpr(AExpr).ParamMode := pmStaticArrayValue
else
TIdentExpr(AExpr).ParamMode := pmNone;
TIdentExpr(AExpr).IsGlobal := Sym.IsGlobal;
TIdentExpr(AExpr).IsThreadVar := Sym.IsThreadVar;
if Sym.Kind = skConstant then

View file

@ -25,9 +25,9 @@ TArrayLiteralExpr.Elements serialise
# TNilLiteral (uAST.pas:70)
# TIdentExpr (uAST.pas:72)
# TIdentExpr (uAST.pas:79)
TIdentExpr.Name serialise
TIdentExpr.IsVarParam safe
TIdentExpr.ParamMode safe
TIdentExpr.IsConstant safe
TIdentExpr.ConstValue safe
TIdentExpr.ConstString safe
@ -38,7 +38,7 @@ TIdentExpr.IsImplicitSelfMethod safe
TIdentExpr.IsMetaclassRef safe
TIdentExpr.ConstArraySymbol safe
# TFieldAccessExpr (uAST.pas:93)
# TFieldAccessExpr (uAST.pas:100)
TFieldAccessExpr.RecordName serialise
TFieldAccessExpr.FieldName serialise
TFieldAccessExpr.Base serialise
@ -63,29 +63,29 @@ TFieldAccessExpr.PropIndexExpr serialise
TFieldAccessExpr.IsCharAccess safe
TFieldAccessExpr.IsArrayAccess safe
# TIsExpr (uAST.pas:125)
# TIsExpr (uAST.pas:132)
TIsExpr.Obj serialise
TIsExpr.TypeName serialise
# TAsExpr (uAST.pas:133)
# TAsExpr (uAST.pas:140)
TAsExpr.Obj serialise
TAsExpr.TypeName serialise
# TSupportsExpr (uAST.pas:142)
# TSupportsExpr (uAST.pas:149)
TSupportsExpr.Obj serialise
TSupportsExpr.IntfTypeName serialise
TSupportsExpr.OutVarName serialise
TSupportsExpr.OutVarIsGlobal safe
# TBinaryExpr (uAST.pas:158)
# TBinaryExpr (uAST.pas:165)
TBinaryExpr.Op serialise
TBinaryExpr.Left serialise
TBinaryExpr.Right serialise
# TNotExpr (uAST.pas:166)
# TNotExpr (uAST.pas:173)
TNotExpr.Expr serialise
# TAssignment (uAST.pas:178)
# TAssignment (uAST.pas:185)
TAssignment.Name serialise
TAssignment.Expr serialise
TAssignment.IsVarParam safe
@ -94,23 +94,23 @@ TAssignment.IsThreadVar safe
TAssignment.IsWeakLhs safe
TAssignment.ImplicitSelfField safe
# TIfStmt (uAST.pas:194)
# TIfStmt (uAST.pas:201)
TIfStmt.Condition serialise
TIfStmt.ThenStmt serialise
TIfStmt.ElseStmt serialise
# TCompoundStmt (uAST.pas:202)
# TCompoundStmt (uAST.pas:209)
TCompoundStmt.Stmts serialise
# TWhileStmt (uAST.pas:209)
# TWhileStmt (uAST.pas:216)
TWhileStmt.Condition serialise
TWhileStmt.Body serialise
# TRepeatStmt (uAST.pas:216)
# TRepeatStmt (uAST.pas:223)
TRepeatStmt.Body serialise
TRepeatStmt.Condition serialise
# TForStmt (uAST.pas:223)
# TForStmt (uAST.pas:230)
TForStmt.VarName serialise
TForStmt.IsGlobal safe
TForStmt.StartExpr serialise
@ -118,7 +118,7 @@ TForStmt.EndExpr serialise
TForStmt.IsDownTo serialise
TForStmt.Body serialise
# TForInStmt (uAST.pas:234)
# TForInStmt (uAST.pas:241)
TForInStmt.VarName serialise
TForInStmt.VarIsGlobal safe
TForInStmt.CollExpr serialise
@ -137,34 +137,34 @@ TForInStmt.IsSetIter safe
TForInStmt.SetBitCount safe
TForInStmt.SetMaskVarName safe
# TTryFinallyStmt (uAST.pas:267)
# TTryFinallyStmt (uAST.pas:274)
TTryFinallyStmt.TryBody serialise
TTryFinallyStmt.FinallyBody serialise
# TTryExceptStmt (uAST.pas:284)
# TTryExceptStmt (uAST.pas:291)
TTryExceptStmt.TryBody serialise
TTryExceptStmt.Handlers serialise
TTryExceptStmt.ElseBody serialise
TTryExceptStmt.ExceptBody serialise
# TRaiseStmt (uAST.pas:297)
# TRaiseStmt (uAST.pas:304)
TRaiseStmt.Expr serialise
# TExitStmt (uAST.pas:308)
# TExitStmt (uAST.pas:315)
TExitStmt.Value safe
TExitStmt.ResultAssign safe
# TBreakStmt (uAST.pas:316)
# TBreakStmt (uAST.pas:323)
# TContinueStmt (uAST.pas:319)
# TContinueStmt (uAST.pas:326)
# TCaseStmt (uAST.pas:330)
# TCaseStmt (uAST.pas:337)
TCaseStmt.Selector serialise
TCaseStmt.Branches serialise
TCaseStmt.ElseStmt serialise
TCaseStmt.IsStringCase safe
# TFieldAssignment (uAST.pas:341)
# TFieldAssignment (uAST.pas:348)
TFieldAssignment.RecordName serialise
TFieldAssignment.FieldName serialise
TFieldAssignment.Expr serialise
@ -177,24 +177,24 @@ TFieldAssignment.IsVarParam safe
TFieldAssignment.PropIndexExpr serialise
TFieldAssignment.PropOwnerType safe
# TStaticSubscriptAssign (uAST.pas:360)
# TStaticSubscriptAssign (uAST.pas:367)
TStaticSubscriptAssign.ArrayName serialise
TStaticSubscriptAssign.IndexExpr serialise
TStaticSubscriptAssign.ValueExpr serialise
TStaticSubscriptAssign.IsGlobal safe
# TPointerWriteStmt (uAST.pas:371)
# TPointerWriteStmt (uAST.pas:378)
TPointerWriteStmt.PtrExpr serialise
TPointerWriteStmt.ValExpr serialise
# TProcCall (uAST.pas:379)
# TProcCall (uAST.pas:386)
TProcCall.Name serialise
TProcCall.Args serialise
TProcCall.IsImplicitSelfMethod safe
TProcCall.IsIndirectCall safe
TProcCall.IndirectCallIsGlobal safe
# TFuncCallExpr (uAST.pas:392)
# TFuncCallExpr (uAST.pas:399)
TFuncCallExpr.Name serialise
TFuncCallExpr.Args serialise
TFuncCallExpr.IsImplicitSelfMethod safe
@ -204,18 +204,18 @@ TFuncCallExpr.IsBuiltinHasClassAttr safe
TFuncCallExpr.HasClassAttrClass safe
TFuncCallExpr.HasClassAttrAttr safe
# TIndirectFuncCallExpr (uAST.pas:418)
# TIndirectFuncCallExpr (uAST.pas:425)
TIndirectFuncCallExpr.CalleeExpr serialise
TIndirectFuncCallExpr.Args serialise
# TDerefExpr (uAST.pas:428)
# TDerefExpr (uAST.pas:435)
TDerefExpr.Expr serialise
# TAddrOfExpr (uAST.pas:435)
# TAddrOfExpr (uAST.pas:442)
TAddrOfExpr.Expr serialise
TAddrOfExpr.ResolvedFreeRoutine safe
# TMethodCallStmt (uAST.pas:447)
# TMethodCallStmt (uAST.pas:454)
TMethodCallStmt.ObjectName serialise
TMethodCallStmt.Name serialise
TMethodCallStmt.Args serialise
@ -226,11 +226,11 @@ TMethodCallStmt.IsVarParam safe
TMethodCallStmt.IsBuiltinToString safe
TMethodCallStmt.IsProcFieldCall safe
# TInheritedCallStmt (uAST.pas:474)
# TInheritedCallStmt (uAST.pas:481)
TInheritedCallStmt.Name serialise
TInheritedCallStmt.Args serialise
# TMethodCallExpr (uAST.pas:664)
# TMethodCallExpr (uAST.pas:671)
TMethodCallExpr.ObjectName serialise
TMethodCallExpr.Name serialise
TMethodCallExpr.Args serialise