[fpc-devel] Optimization breaks check for overridden function / Is there a better way to check?
David Jenkins
david at scootersoftware.com
Mon Oct 20 23:52:45 CEST 2025
On 10/20/25 3:29 PM, Martin Frb via fpc-devel wrote:
> 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.
>
> _______________________________________________
> fpc-devel maillist - fpc-devel at lists.freepascal.org
> https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-devel
To me it looks like TPrinter.DoNewPage is not updated to
System.EmptyMethod as part of the optimization. Which is why the check
fails.
My local fix for now is to use
if (TMethod(@Self.DoNewPage).Code = Pointer(@TPrinter.DoNewPage)) or
(TMethod(@Self.DoNewPage).Code = System.EmptyMethod) then
begin
....
end;
David
More information about the fpc-devel
mailing list