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.

_______________________________________________
fpc-devel maillist  -  [email protected]
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-devel

Reply via email to