[fpc-devel] Optimization breaks check for overridden function / Is there a better way to check?
Martin Frb
lazarus at mfriebe.de
Mon Oct 20 22:29:50 CEST 2025
On 17/10/2025 21:15, Sven Barth via fpc-devel wrote:
>> What is the best way to check if a virtual function in a base class
>> has been overridden by a class that inherits the base class?
>>
>> In LCL TPrinter.pas the following check is used:
>>
>>
>> procedure TPrinter.NewPage;
>> begin
>> Inc(fPageNumber);
>> if TMethod(@Self.DoNewPage).Code = Pointer(@TPrinter.DoNewPage) then
>> begin
>> ..
>> end
>> ..
>> end
>>
>> We have run across an optimization problem with the TCocoaPrinter
>> class and that TPrinter code.
>>
>> TCocoaPrinter = class(TPrinter)
>>
>> with no override of DeNewPage
>
> David's problem is *not* due to a corruction or a bug, but due to an
> optimization that FPC performs that leads to different behavior,
> namely changing virtual methods that are empty to EmptyMethod to
> reduce the number of duplicate (empty) methods in the binary.
I am trying to figure out under which circumstances this would happen?
TPrinter = class...
procedure DoNewPage; virtual;
And the check really only makes sense for virtual methods. Well (in
generics that differs, but for non virtual, both classes are known at
compiletime).
So if it is a virtual method:
If the compiler optimizes @TPrinter.DoNewPage to EmptyMethod, then it
would put that into the class definition?
So that is into the VMT?
And there is only one VMT?
And self is a pointer (to pointer) to that one VMT?
So then how can one be replaced, but the other not?
He says "with no override of DeNewPage". So he expects them both to be
the same.
I could see the opposite fail. If there was on overwritten method, and
that was also empty, then the different overwritten empty method would
(falsely) report as the same...
But then reporting as different?
Or does the compiler only update the VMT, but does report
@TPrinter.DoNewPage to the original code?
If that was the case, would/should that not be considered a bug. If the
compiler replaced it, then it should report it for the original too.
If @TPrinter.DoNewPage still reports the original code (and NOT
emptyproc) then that forces the original code to be included too (even
though the whole optimization was to NOT include it).
Because you could then do:
MyMeth := @TPrinter.DoNewPage;
MyMeth();
and call the original code.
More information about the fpc-devel
mailing list