Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-pascal / 2009-04

Remove unused functions in a class

7 messages · 2 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

JoshyFun

Hello FPC-Pascal,

If a virtual method of a class is not used at all in the programm will the compiler remove it from the executable ?

I think that unused constants and functions will be removed by the compiler or the linker but the methdos of a class... :-?

Best regards, JoshyFun
Jonas Maebe
to JoshyFun
JoshyFun wrote
If a virtual method of a class is not used at all in the programm will the compiler remove it from the executable ?

If you use FPC 2.3.1 with whole-program optimization and the compiler can prove that it is never callable: yes. See http://wiki.freepascal.org/Whole_Program_Optimization for more information. In case this is in relation to the thread on the Lazarus list about the big executables: note that it has only a limited effect on Lazarus programs, because almost all linked LCL code can potentially be executed (due to the way the LCL is constructed). In fact, I think most savings there come from making a number of virtual method calls non-virtual, rather than from throwing away unreachable code.

The internal linker can also do it (only throwing away virtual method calls, not turning virtual method calls into static ones), but only on Windows platforms. It is however not currently enabled in the compiler, because the changes break the external linker. It should therefore be turned into a command line option (along with a check that produces an error if you try to link a unit compiled with the option using the external linker), but that hasn't been done yet.

Jonas

JoshyFun
to Jonas Maebe

Hello Jonas,

Sunday, April 5, 2009, 11:20:59 AM, you wrote:

JM> If you use FPC 2.3.1 with whole-program optimization and the compiler JM> can prove that it is never callable: yes. See
JM> http://wiki.freepascal.org/Whole_Program_Optimization JM> for more information. In case this is in relation to the thread on JM> the Lazarus list about the big executables: note that it has only a

Yes and no :) The thread raises the question in my brain :) but not to reduce the LCL final size, it was only a conceptual question, how can the compiler determine that a virtual method is unused at all, and even if it is removed the VMT entry should be present do not ?

JM> limited effect on Lazarus programs, because almost all linked LCL code JM> can potentially be executed (due to the way the LCL is constructed). JM> In fact, I think most savings there come from making a number of JM> virtual method calls non-virtual, rather than from throwing away JM> unreachable code.

I think that LCL should be reestructured to get the compiler and linker performing a good dead code clean, but that task is a big task.

JM> The internal linker can also do it (only throwing away virtual method [...]
JM> that produces an error if you try to link a unit compiled with the JM> option using the external linker), but that hasn't been done yet.

Just for curiosity which is the parameter set ?

Thank you for the answer.

Best regards, JoshyFun
Jonas Maebe
to JoshyFun
JoshyFun wrote 13 lines
JM> If you use FPC 2.3.1 with whole-program optimization and the compiler JM> can prove that it is never callable: yes. See JM> http://wiki.freepascal.org/Whole_Program_Optimization JM> for more information. In case this is in relation to the thread on JM> the Lazarus list about the big executables: note that it has only a Yes and no :) The thread raises the question in my brain :) but not to reduce the LCL final size, it was only a conceptual question, how can the compiler determine that a virtual method is unused at all,

If you have a class hierarchy tbase->tderived1->tderived2 with a virtual method called "vmethod", and nowhere in the program there is a call to vmethod, then it is unused. Or if it is only called using tderived2 instances, then if the linker does not find any direct references to tderived1.vmethod or tbase.vmethod (e.g., via "inherited" calls from tderived2 methods), it knows that the VMT entries for vmethod in tderived1 and tbase can be set to nil.

and even if it is removed the VMT entry should be present do not ?

The easiest is to keep it, but simply set it to nil or to FPC_ABSTRACTERROR. Then you don't have to start juggling with changing offsets everywhere.

Show quoted text 7 lines
JM> The internal linker can also do it (only throwing away virtual method [...] JM> that produces an error if you try to link a unit compiled with the JM> option using the external linker), but that hasn't been done yet. Just for curiosity which is the parameter set ?

Compile the compiler with -dvtentry. It's not been tested for a while though, as far as I know.

I also made a mistake in my previous explanation: currently, the WPO does not yet perform the optimization the -dvtentry does. -dvtentry looks at which VMT entries of a class hierarchy are never accessed, and replaces those by nil. As a result, if those methods are not referred directly, then they can be removed by the linker.

The WPO looks at which class types can be instantiated in the program and at the inheritance tree, and based on this determines which virtual method calls can be replaced by non-virtual method calls (and for those methods, it replaces the VMT entry with FPC_ABSTRACTERROR). It also replaces the VMT entries for (non-class) virtual methods in all classes that cannot be instantiated in the program with FPC_ABSTRACTERROR. As a result, again if such methods are not referenced directly, then they can be removed by the linker (and the ones that are referenced can be turned into normal function calls).

Jonas

JoshyFun
to Jonas Maebe

Hello Jonas,

Sunday, April 5, 2009, 2:09:33 PM, you wrote:

JM> If you have a class hierarchy tbase->tderived1->tderived2 with a JM> virtual method called "vmethod", and nowhere in the program there is a JM> call to vmethod, then it is unused. Or if it is only called using JM> tderived2 instances, then if the linker does not find any direct JM> references to tderived1.vmethod or tbase.vmethod (e.g., via JM> "inherited" calls from tderived2 methods), it knows that the VMT JM> entries for vmethod in tderived1 and tbase can be set to nil.

In fact I was thinking in something like this:

TCustomFoo=class(TObject);
TDerivedFoo=class(TCustomFoo)
 procedure Myprocedure(); virtual;
end;
TDeDerivedFoo=class(TDerivedFoo)
 procedure MyProcedure(); override;
end;

And in the code:

var
  MyObj: TCustomFoo;
begin
  MyObj:=TDeDerivedFoo.Create();
  TDerivedFoo(MyObj).Myprocedure();
end;

Something like this could kill the dead code detection ? A simple yes/no is enoght :)

> JM> that produces an error if you try to link a unit compiled with the > JM> option using the external linker), but that hasn't been done yet. > > Just for curiosity which is the parameter set ?

JM> Compile the compiler with -dvtentry. It's not been tested for a while JM> though, as far as I know.

I'll play with it just for fun...

JM> I also made a mistake in my previous explanation: currently, the WPO JM> does not yet perform the optimization the -dvtentry does. -dvtentry JM> looks at which VMT entries of a class hierarchy are never accessed, JM> and replaces those by nil. As a result, if those methods are not JM> referred directly, then they can be removed by the linker.

I understand it in that way already.

JM> The WPO looks at which class types can be instantiated in the program JM> and at the inheritance tree, and based on this determines which [...]
JM> referenced directly, then they can be removed by the linker (and the JM> ones that are referenced can be turned into normal function calls).

More or less I understand the WPO way of work. Thank you again.

Best regards, JoshyFun
Jonas Maebe
to JoshyFun
JoshyFun wrote 19 lines
TCustomFoo=class(TObject); TDerivedFoo=class(TCustomFoo) procedure Myprocedure(); virtual; end; TDeDerivedFoo=class(TDerivedFoo) procedure MyProcedure(); override; end; And in the code: var MyObj: TCustomFoo; begin MyObj:=TDeDerivedFoo.Create(); TDerivedFoo(MyObj).Myprocedure(); end; Something like this could kill the dead code detection ? A simple yes/no is enoght :)

Not in case of whole-program optimization:

--- dead.sorg 2009-04-05 17:24:25.000000000 +0200 +++ dead.s 2009-04-05 17:24:32.000000000 +0200 @@ -91,10 +91,7 @@

  # [29] TDerivedFoo(MyObj).Myprocedure();
         movl    L_U_P$PROGRAM_MYOBJ$non_lazy_ptr-Lj2(%ebx),%eax
         movl    (%eax),%eax

- movl L_U_P$PROGRAM_MYOBJ$non_lazy_ptr-Lj2(%ebx),%edx
- movl (%edx),%edx
- movl (%edx),%edx
- call *80(%edx)
+ call L_P$PROGRAM_TDEDERIVEDFOO_$__MYPROCEDURE$stub

I'm not sure about -dvtentry.

Jonas

JoshyFun
to Jonas Maebe

Hello Jonas,

you wrote
> Something like this could kill the dead code detection ? A simple > yes/no is enoght :)

JM> Not in case of whole-program optimization:

JM> --- dead.sorg 2009-04-05 17:24:25.000000000 +0200 JM> +++ dead.s 2009-04-05 17:24:32.000000000 +0200 JM> @@ -91,10 +91,7 @@
JM> # [29] TDerivedFoo(MyObj).Myprocedure();
JM> movl L_U_P$PROGRAM_MYOBJ$non_lazy_ptr-Lj2(%ebx),%eax JM> movl (%eax),%eax
JM> - movl L_U_P$PROGRAM_MYOBJ$non_lazy_ptr-Lj2(%ebx),%edx JM> - movl (%edx),%edx
JM> - movl (%edx),%edx
JM> - call *80(%edx)
JM> + call L_P$PROGRAM_TDEDERIVEDFOO_$__MYPROCEDURE$stub

JM> I'm not sure about -dvtentry.

Nice to see that, that's for sure a non easy objetive :)

Best regards, JoshyFun