[fpc-devel] Optimization breaks check for overridden function / Is there a better way to check?

Martin Frb lazarus at mfriebe.de
Tue Oct 21 09:46:07 CEST 2025


On 21/10/2025 00:18, Sven Barth via fpc-devel wrote:
>
> If one is compiled with -O3, but the other is compiled with -O2. The 
> VMT is created for each type, so even if a method isn't overridden it 
> can be replaced in one VMT, but not in the other.
>

I just did a test, one single program, no units
Compiled with -O3 -al

The VMT contains FPC_EMPTYMETHOD
But the code (asm) still contains P$PROJECT1$_$TFOO_$__$$_BAR

If I enable the "inherited Bar" then it calls the "BAR" method, NOT the 
FPC_EMPTYMETHOD

If I do
   x := @TFoo.Bar;
I get
    leaq    P$PROJECT1$_$TFOO_$__$$_BAR(%rip),%rax


So the optimizer seems broken to me. What is the point of replacing it 
in the VMT, if the code is kept non-the less?
It only makes sense if the method is NOT called at all, because then the 
linker can remove the unused code (which would otherwise be referred too 
by the VMT).

Sort of what WPO would do.... Maybe even needed for WPO, as otherwise 
WPO can never know that this method is never called.
But, then "x := @TFoo.Bar;" blocks it from being removed, but "x := 
foo.Bar;" allows its removal.

And what argument could there be that the first must return the correct 
pointer? If the 2nd can replace it, then the optimizer is based on "the 
code must be able to work with the replaced pointer", and in that case 
the same should be allowed to say for the first?

-------------------------------------

Btw, the "code must work with wrong pointer" may be questionable.... 
Albeit not sure. (maybe a directive to prevent).

Most often I have seen this used to avoid calls to the empty base class. 
In that case, if it returns true for any empty inherited method => well 
that is fine too. It just avoids the call to an empty method.

Another use case, I recently found, is in generics.

if _X_.foo <> @TKnowGenParam.foo then begin
    // preparation
   _X_variable.foo;
end;

But here again, if TKnowGenParam is empty, then the above skips the 
preparation (and even can trigger dead code removal).
This of course currently works, because _X_ is the gen param, and that 
is the CLASS.
And it would work too if "foo" is empty, and the @CLASS.meth was 
optimised too.

Of course if other classes also had an empty foo, yet would want the 
preparation to run.... But then; why compare the method instead of the 
class.
In any case that I personally used this, I compared the method, because 
I want to compare it to the known empty method. (and catch inherited 
classes too)





program project1;{$Mode objfpc}
type

   TFoo = class
   public
     procedure Bar; virtual;
   end;

   TFoo1 = class(TFoo)
     procedure Bar; override;
   end;

procedure TFoo.Bar;
begin
   //
end;

procedure TFoo1.Bar;
begin
   //inherited Bar;  // calls
end;


var
   f: Tfoo;
   f1: Tfoo1;
   x: Pointer;
begin
   f := TFoo.Create;
   f1 := TFoo1.Create;

   //x := @TFoo.Bar;
   //x := f.Bar;

   f.Bar;
   f1.Bar;
end.



More information about the fpc-devel mailing list