fix(native): vtable dispatch for virtual method calls in expression context
EmitMethodCallExpr was always emitting a direct `callq` for method calls, ignoring VTableSlot. Virtual calls like B.GetVal() (where B's declared type is an abstract base) produced a direct call to the base's method symbol, which is never emitted for abstract methods — causing link errors. Add vtable dispatch (load vtable ptr from Self, index by VTableSlot, indirect call) in both the <=6 and >6 arg paths of EmitMethodCallExpr, matching what EmitMethodCallStmt already does. This resolves the last undefined references (Streams_TInputStream_Read, Streams_TOutputStream_Write) when native-compiling the compiler itself. The native-compiled compiler now links and runs.
This commit is contained in:
parent
367a8c0a79
commit
75ab341a2d
|
|
@ -5282,7 +5282,14 @@ begin
|
|||
for I := UserSlots - 1 downto 0 do
|
||||
Self.Emit(#9'popq ' + SysVArgRegs64[I + 1]);
|
||||
Self.Emit(#9'movq %r10, %rdi');
|
||||
Self.Emit(#9'callq ' + Sym);
|
||||
if MD.VTableSlot >= 0 then
|
||||
begin
|
||||
Self.Emit(#9'movq (%rdi), %rax');
|
||||
Self.Emit(Format(#9'movq %d(%%rax), %%rax', [(MD.VTableSlot + 1) * 8]));
|
||||
Self.Emit(#9'callq *%rax');
|
||||
end
|
||||
else
|
||||
Self.Emit(#9'callq ' + Sym);
|
||||
end
|
||||
else
|
||||
begin
|
||||
|
|
@ -5343,7 +5350,14 @@ begin
|
|||
Self.Emit(Format(#9'movq %d(%%rsp), %s', [I * 8, SysVArgRegs64[I]]));
|
||||
|
||||
Self.Emit(Format(#9'addq $%d, %%rsp', [6 * 8]));
|
||||
Self.Emit(#9'callq ' + Sym);
|
||||
if MD.VTableSlot >= 0 then
|
||||
begin
|
||||
Self.Emit(#9'movq (%rdi), %rax');
|
||||
Self.Emit(Format(#9'movq %d(%%rax), %%rax', [(MD.VTableSlot + 1) * 8]));
|
||||
Self.Emit(#9'callq *%rax');
|
||||
end
|
||||
else
|
||||
Self.Emit(#9'callq ' + Sym);
|
||||
Self.Emit(Format(#9'addq $%d, %%rsp', [CleanUp]));
|
||||
end;
|
||||
end;
|
||||
|
|
|
|||
|
|
@ -287,6 +287,9 @@ type
|
|||
|
||||
{ Const array data emission + ConstArraySymbol reference }
|
||||
procedure TestRun_Native_ConstArray_StringElements;
|
||||
|
||||
{ Virtual method dispatch in expression context }
|
||||
procedure TestRun_Native_VirtualDispatch_Expr;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
|
@ -4330,6 +4333,28 @@ begin
|
|||
'ax' + LE + 'bx' + LE + 'cx' + LE, 0);
|
||||
end;
|
||||
|
||||
procedure TE2ENativeTests.TestRun_Native_VirtualDispatch_Expr;
|
||||
begin
|
||||
AssertRunsOnBoth(
|
||||
'program T;'
|
||||
+ 'type '
|
||||
+ ' TBase = class '
|
||||
+ ' function GetVal(): Integer; virtual; abstract; '
|
||||
+ ' end; '
|
||||
+ ' TChild = class(TBase) '
|
||||
+ ' function GetVal(): Integer; override; '
|
||||
+ ' end; '
|
||||
+ 'function TChild.GetVal(): Integer; '
|
||||
+ 'begin Result := 42 end; '
|
||||
+ 'var B: TBase; '
|
||||
+ 'begin '
|
||||
+ ' B := TChild.Create; '
|
||||
+ ' WriteLn(B.GetVal()); '
|
||||
+ ' B.Free '
|
||||
+ 'end.',
|
||||
'42' + LE, 0);
|
||||
end;
|
||||
|
||||
initialization
|
||||
RegisterTest(TE2ENativeTests);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue