Generic Visitors

This commit is contained in:
Michael Schimmel
2026-01-03 19:14:18 +01:00
parent db74b83e11
commit 22674b962b
27 changed files with 3573 additions and 3547 deletions
+34 -28
View File
@@ -16,7 +16,7 @@ uses
Myc.Ast.Compiler.Binder;
type
// Exception specific to specialization errors (e.g. internal compilation failures)
// Exception specific to specialization errors
ESpecializerException = class(EAstException);
IAstSpecializer = interface(IAstVisitor)
@@ -29,7 +29,7 @@ type
public
Address: TResolvedAddress;
ArgTypes: TArray<IStaticType>;
Func: TDataValue.TFunc;
Func: TDataValue.TFunc; // Usually nil in key, but part of structure if needed
constructor Create(const AAddress: TResolvedAddress; const AArgTypes: TArray<IStaticType>);
end;
@@ -39,10 +39,9 @@ type
end;
// This transformer runs *after* TypeChecker.
// It specializes all statically resolvable function calls (RTL and user-defined)
// by replacing them with nodes that have a direct StaticTarget.
// It propagates Purity information but does NOT perform Constant Folding yet.
// It specializes all statically resolvable function calls.
TStaticSpecializer = class(TAstTransformer, IAstSpecializer)
public
type
TCompileFunc = reference to function(const Node: IFunctionDefinition; const ArgTypes: TArray<IStaticType>): TCompiledFunction;
private
@@ -52,8 +51,12 @@ type
function GetStaticRtlFunction(const AName: string; const AArgTypes: TArray<IStaticType>): TSpecializedMethod;
strict private
// Specialization Handler (IAstNode signature)
function VisitFunctionCall(const Node: IAstNode): IAstNode;
protected
function VisitFunctionCall(const Node: IFunctionCallNode): IAstNode; override;
procedure SetupHandlers; override;
public
constructor Create(
@@ -89,13 +92,19 @@ begin
raise ESpecializerException.Create('MonomorphCache cannot be nil.');
if not Assigned(AFunctionRegistry) then
raise ESpecializerException.Create('FunctionRegistry cannot be nil.');
// CompileFunc can be nil if no recursion is supported, but usually required.
FMonomorphCache := AMonomorphCache;
FFunctionRegistry := AFunctionRegistry;
FCompileFunc := ACompileFunc;
end;
procedure TStaticSpecializer.SetupHandlers;
begin
inherited SetupHandlers; // Load default transformations
// Override FunctionCall logic
Register(akFunctionCall, VisitFunctionCall);
end;
class function TStaticSpecializer.Specialize(
const RootNode: IAstNode;
const AMonomorphCache: IMonomorphCache;
@@ -119,8 +128,10 @@ begin
Result := TRtlRegistry.GetStaticSpecialization(AName, AArgTypes);
end;
function TStaticSpecializer.VisitFunctionCall(const Node: IFunctionCallNode): IAstNode;
function TStaticSpecializer.VisitFunctionCall(const Node: IAstNode): IAstNode;
var
C: IFunctionCallNode;
newCall: IFunctionCallNode;
newCallee: IAstNode;
newArgsList: IArgumentList;
i: Integer;
@@ -132,18 +143,18 @@ var
specializedMethod: TSpecializedMethod;
funcDef: IFunctionDefinition;
begin
// 1. Specialize children first (bottom-up)
newCallee := Accept(Node.Callee);
// Arguments are now visited as a List, returning an IArgumentList
newArgsList := Accept(Node.Arguments).AsArgumentList;
C := Node.AsFunctionCall;
// 1. Specialize children first (bottom-up) by calling inherited
// inherited VisitFunctionCall returns an IAstNode (which is a new IFunctionCallNode if changed)
newCall := inherited VisitFunctionCall(Node).AsFunctionCall;
newCallee := newCall.Callee;
newArgsList := newCall.Arguments;
// 2. Check if this call is a candidate for specialization
if newCallee.Kind <> akIdentifier then
begin
if (newCallee = Node.Callee) and (newArgsList = Node.Arguments) then
Result := Node
else
Result := TAst.FunctionCall(Node.Identity, newCallee, newArgsList, Node.StaticType, Node.IsTailCall, nil);
Result := newCall;
exit;
end;
@@ -165,10 +176,7 @@ begin
if not allTypesKnown then
begin
if (newCallee = Node.Callee) and (newArgsList = Node.Arguments) then
Result := Node
else
Result := TAst.FunctionCall(Node.Identity, newCallee, newArgsList, Node.StaticType, Node.IsTailCall, nil);
Result := newCall;
exit;
end;
@@ -186,7 +194,7 @@ begin
newCallee,
newArgsList,
specializedMethod.ReturnType,
Node.IsTailCall,
C.IsTailCall,
specializedMethod.Target,
specializedMethod.IsPure
);
@@ -206,7 +214,7 @@ begin
newCallee,
newArgsList,
specializedMethod.ReturnType,
Node.IsTailCall,
C.IsTailCall,
specializedMethod.Target,
specializedMethod.IsPure
);
@@ -224,7 +232,7 @@ begin
var lambdaDef := funcDef.AsLambdaExpression;
if (Length(lambdaDef.Upvalues) > 0) or (lambdaDef.HasNestedLambdas) then
begin
Result := TAst.FunctionCall(Node.Identity, newCallee, newArgsList, Node.StaticType, Node.IsTailCall, nil);
Result := newCall;
exit;
end;
end;
@@ -245,15 +253,12 @@ begin
FMonomorphCache.Add(key, specializedMethod);
// 6c. Return the new node
Result := TAst.FunctionCall(Node.Identity, newCallee, newArgsList, returnType, Node.IsTailCall, compiled.Func, compiled.IsPure);
Result := TAst.FunctionCall(Node.Identity, newCallee, newArgsList, returnType, C.IsTailCall, compiled.Func, compiled.IsPure);
exit;
end;
// 7. Fallback: Not RTL, Not User-Code -> Dynamic
if (newCallee = Node.Callee) and (newArgsList = Node.Arguments) then
Result := Node
else
Result := TAst.FunctionCall(Node.Identity, newCallee, newArgsList, Node.StaticType, Node.IsTailCall, nil);
Result := newCall;
end;
{ TMonoCacheKey }
@@ -262,6 +267,7 @@ constructor TMonoCacheKey.Create(const AAddress: TResolvedAddress; const AArgTyp
begin
Address := AAddress;
ArgTypes := AArgTypes;
Func := nil;
end;
end.