Browse Source

+ initial implementation of whole-program optimisation framework
+ implementation of whole-program devirtualisation
o use:
a) generate whole-program optimisation information (no need
to completely compile the program and all of its units
with -OW/-FW, only the main program is sufficient)
fpc -OWdevirtcalls -FWmyprog.wpo myprog
b) use it to optimise the program
fpc -B -Owdevirtcalls -Fwmyprog.wpo myprog
(the -B is not required, but only sources recompiled during
the second pass will actually be optimised -- if you want,
you can even rebuild the rtl devirtualised for a particular
program; and these options can obviously also be used
together with regular optimisation switches)
o warning:
- there are no checks yet to ensure that you do not use
units optimised for a particular program with another
program (or with a changed version of the same program)

git-svn-id: branches/wpo@11878 -

Jonas Maebe 17 years ago
parent
commit
fbac599784

+ 4 - 0
.gitattributes

@@ -315,6 +315,7 @@ compiler/options.pas svneol=native#text/plain
 compiler/optloop.pas svneol=native#text/plain
 compiler/opttail.pas svneol=native#text/plain
 compiler/optutils.pas svneol=native#text/plain
+compiler/optvirt.pas svneol=native#text/plain
 compiler/owar.pas svneol=native#text/plain
 compiler/owbase.pas svneol=native#text/plain
 compiler/parabase.pas svneol=native#text/plain
@@ -576,6 +577,9 @@ compiler/vis/cpuinfo.pas svneol=native#text/plain
 compiler/vis/cpunode.pas svneol=native#text/plain
 compiler/vis/cpupara.pas svneol=native#text/plain
 compiler/widestr.pas svneol=native#text/plain
+compiler/wpo.pas svneol=native#text/plain
+compiler/wpobase.pas svneol=native#text/plain
+compiler/wpoinfo.pas svneol=native#text/plain
 compiler/x86/aasmcpu.pas svneol=native#text/plain
 compiler/x86/agx86att.pas svneol=native#text/plain
 compiler/x86/agx86int.pas svneol=native#text/plain

+ 6 - 2
compiler/compiler.pas

@@ -40,7 +40,7 @@ uses
 {$ENDIF}
   verbose,comphook,systems,
   cutils,cfileutl,cclasses,globals,options,fmodule,parser,symtable,
-  assemble,link,dbgbase,import,export,tokens,pass_1
+  assemble,link,dbgbase,import,export,tokens,pass_1,wpobase,wpoinfo
   { cpu parameter handling }
   ,cpupara
   { procinfo stuff }
@@ -117,7 +117,8 @@ function Compile(const cmd:string):longint;
 implementation
 
 uses
-  aasmcpu;
+  aasmcpu,
+  wpo;
 
 {$if defined(EXTDEBUG) or defined(MEMDEBUG)}
   {$define SHOWUSEDMEM}
@@ -145,6 +146,7 @@ begin
      DoneExport;
      DoneLinker;
      DoneAsm;
+     DoneWpo;
    end;
 { Free memory for the others }
   CompilerInited:=false;
@@ -184,6 +186,8 @@ begin
   InitExport;
   InitLinker;
   InitAsm;
+  InitWpo;
+
   CompilerInitedAfterArgs:=true;
 end;
 

+ 20 - 29
compiler/fmodule.pas

@@ -44,7 +44,9 @@ interface
     uses
        cutils,cclasses,cfileutl,
        globtype,finput,ogbase,
-       symbase,symsym,aasmbase,aasmtai,aasmdata;
+       symbase,symsym,
+       wpobase,
+       aasmbase,aasmtai,aasmdata;
 
 
     const
@@ -127,6 +129,7 @@ interface
         derefdata     : tdynamicarray;
         deflist,
         symlist       : TFPObjectList;
+        wpoinfo       : tunitwpoinfobase; { whole program optimization-related information that is generated during the current run for this unit }
         globalsymtable,           { pointer to the global symtable of this unit }
         localsymtable : TSymtable;{ pointer to the local symtable of this unit }
         globalmacrosymtable,           { pointer to the global macro symtable of this unit }
@@ -487,6 +490,7 @@ implementation
         derefdataintflen:=0;
         deflist:=TFPObjectList.Create(false);
         symlist:=TFPObjectList.Create(false);
+        wpoinfo:=nil;
         globalsymtable:=nil;
         localsymtable:=nil;
         globalmacrosymtable:=nil;
@@ -593,14 +597,11 @@ implementation
         derefdata.free;
         deflist.free;
         symlist.free;
-        if assigned(globalsymtable) then
-          globalsymtable.free;
-        if assigned(localsymtable) then
-          localsymtable.free;
-        if assigned(globalmacrosymtable) then
-          globalmacrosymtable.free;
-        if assigned(localmacrosymtable) then
-          localmacrosymtable.free;
+        wpoinfo.free;
+        globalsymtable.free;
+        localsymtable.free;
+        globalmacrosymtable.free;
+        localmacrosymtable.free;
 {$ifdef MEMDEBUG}
         memsymtable.stop;
 {$endif}
@@ -643,30 +644,20 @@ implementation
             asmdata:=nil;
           end;
         DoneDebugInfo(self);
-        if assigned(globalsymtable) then
-          begin
-            globalsymtable.free;
-            globalsymtable:=nil;
-          end;
-        if assigned(localsymtable) then
-          begin
-            localsymtable.free;
-            localsymtable:=nil;
-          end;
-        if assigned(globalmacrosymtable) then
-          begin
-            globalmacrosymtable.free;
-            globalmacrosymtable:=nil;
-          end;
-        if assigned(localmacrosymtable) then
-          begin
-            localmacrosymtable.free;
-            localmacrosymtable:=nil;
-          end;
+        globalsymtable.free;
+        globalsymtable:=nil;
+        localsymtable.free;
+        localsymtable:=nil;
+        globalmacrosymtable.free;
+        globalmacrosymtable:=nil;
+        localmacrosymtable.free;
+        localmacrosymtable:=nil;
         deflist.free;
         deflist:=TFPObjectList.Create(false);
         symlist.free;
         symlist:=TFPObjectList.Create(false);
+        wpoinfo.free;
+        wpoinfo:=nil;
         derefdata.free;
         derefdata:=TDynamicArray.Create(1024);
         if assigned(unitmap) then

+ 2 - 0
compiler/fpcdefs.inc

@@ -3,6 +3,8 @@
 {$H-}
 {$goto on}
 {$inline on}
+{$interfaces corba}
+{$static on}
 
 {$ifdef win32}
   { 256 MB stack }

+ 14 - 1
compiler/fppu.pas

@@ -97,6 +97,7 @@ uses
   cfileutl,
   verbose,systems,version,
   symtable, symsym,
+  wpoinfo,
   scanner,
   aasmbase,ogbase,
   parser,
@@ -1064,6 +1065,8 @@ uses
              tstoredsymtable(localsymtable).buildderef;
              tstoredsymtable(localsymtable).buildderefimpl;
            end;
+         tunitwpoinfo(wpoinfo).buildderef;
+         tunitwpoinfo(wpoinfo).buildderefimpl;
          writederefmap;
          writederefdata;
 
@@ -1098,6 +1101,9 @@ uses
          if (flags and uf_local_symtable)<>0 then
            tstoredsymtable(localsymtable).ppuwrite(ppufile);
 
+         { write whole program optimisation-related information }
+         tunitwpoinfo(wpoinfo).ppuwrite(ppufile);
+
          { the last entry ibend is written automaticly }
 
          { flush to be sure }
@@ -1301,11 +1307,16 @@ uses
             localsymtable:=tstaticsymtable.create(modulename^,moduleid);
             tstaticsymtable(localsymtable).ppuload(ppufile);
           end;
-
+          
         { we can now derefence all pointers to the implementation parts }
         tstoredsymtable(globalsymtable).derefimpl;
         if assigned(localsymtable) then
           tstoredsymtable(localsymtable).derefimpl;
+
+         { read whole program optimisation-related information }
+         wpoinfo:=tunitwpoinfo.ppuload(ppufile);
+         tunitwpoinfo(wpoinfo).deref;
+         tunitwpoinfo(wpoinfo).derefimpl;
       end;
 
 
@@ -1383,6 +1394,8 @@ uses
                       tstoredsymtable(localsymtable).deref;
                       tstoredsymtable(localsymtable).derefimpl;
                     end;
+                   tunitwpoinfo(wpoinfo).deref;
+                   tunitwpoinfo(wpoinfo).derefimpl;
                  end
                else
                  Message1(unit_u_skipping_reresolving_unit,modulename^);

+ 63 - 0
compiler/globals.pas

@@ -110,6 +110,10 @@ interface
          localswitches   : tlocalswitches;
          modeswitches    : tmodeswitches;
          optimizerswitches : toptimizerswitches;
+         { generate information necessary to perform these wpo's during a subsequent compilation }
+         genwpoptimizerswitches: twpoptimizerswitches;
+         { perform these wpo's using information generated during a previous compilation }
+         dowpoptimizerswitches: twpoptimizerswitches;
          debugswitches   : tdebugswitches;
          { 0: old behaviour for sets <=256 elements
            >0: round to this size }
@@ -172,6 +176,9 @@ interface
        { specified with -FE or -FU }
        outputexedir      : TPathStr;
        outputunitdir     : TPathStr;
+       { specified with -FW and -Fw }
+       wpofeedbackinput,
+       wpofeedbackoutput : TPathStr;
 
        { things specified with parameters }
        paratarget        : tsystem;
@@ -313,6 +320,8 @@ interface
         localswitches : [cs_check_io,cs_typed_const_writable];
         modeswitches : fpcmodeswitches;
         optimizerswitches : [];
+        genwpoptimizerswitches : [];
+        dowpoptimizerswitches : [];
         debugswitches : [];
         setalloc : 0;
         packenum : 4;
@@ -409,6 +418,7 @@ interface
     function SetFpuType(const s:string;var a:tfputype):boolean;
     function UpdateAlignmentStr(s:string;var a:talignmentinfo):boolean;
     function UpdateOptimizerStr(s:string;var a:toptimizerswitches):boolean;
+    function UpdateWpoStr(s: string; var a: twpoptimizerswitches): boolean;
     function UpdateDebugStr(s:string;var a:tdebugswitches):boolean;
     function IncludeFeature(const s : string) : boolean;
     function SetMinFPConstPrec(const s: string; var a: tfloattype) : boolean;
@@ -1069,6 +1079,59 @@ implementation
       end;
 
 
+    function UpdateWpoStr(s: string; var a: twpoptimizerswitches): boolean;
+      var
+        tok   : string;
+        doset,
+        found : boolean;
+        opt   : twpoptimizerswitch;
+      begin
+        result:=true;
+        uppervar(s);
+        repeat
+          tok:=GetToken(s,',');
+          if tok='' then
+           break;
+          if Copy(tok,1,2)='NO' then
+            begin
+              delete(tok,1,2);
+              doset:=false;
+            end
+          else
+            doset:=true;
+          found:=false;
+          if (tok = 'ALL') then
+            begin
+              for opt:=low(twpoptimizerswitch) to high(twpoptimizerswitch) do
+                if doset then
+                  include(a,opt)
+                else
+                  exclude(a,opt);
+            end
+          else
+            begin
+              for opt:=low(twpoptimizerswitch) to high(twpoptimizerswitch) do
+                begin
+                  if WPOptimizerSwitchStr[opt]=tok then
+                    begin
+                      found:=true;
+                      break;
+                    end;
+                end;
+              if found then
+                begin
+                  if doset then
+                    include(a,opt)
+                  else
+                    exclude(a,opt);
+                end
+              else
+                result:=false;
+            end;
+        until false;
+      end;
+
+
     function UpdateDebugStr(s:string;var a:tdebugswitches):boolean;
       var
         tok   : string;

+ 14 - 0
compiler/globtype.pas

@@ -187,12 +187,21 @@ interface
        );
        toptimizerswitches = set of toptimizerswitch;
 
+       { whole program optimizer }
+       twpoptimizerswitch = (cs_wpo_devirtualize_calls
+       );
+       twpoptimizerswitches = set of twpoptimizerswitch;
+
+
     const
        OptimizerSwitchStr : array[toptimizerswitch] of string[10] = ('',
          'LEVEL1','LEVEL2','LEVEL3',
          'REGVAR','UNCERTAIN','SIZE','STACKFRAME',
          'PEEPHOLE','ASMCSE','LOOPUNROLL','TAILREC','CSE','DFA','STRENGTH'
        );
+       WPOptimizerSwitchStr : array [twpoptimizerswitch] of string[11] = (
+         'DEVIRTCALLS'
+       );
 
        DebugSwitchStr : array[tdebugswitch] of string[9] = ('',
          'DWARFSETS');
@@ -202,6 +211,11 @@ interface
        genericlevel2optimizerswitches = [cs_opt_level2];
        genericlevel3optimizerswitches = [cs_opt_level3];
 
+       { whole program optimizations whose information generation requires
+         information from all loaded units
+       }
+       WPOptimizationsNeedingAllUnitInfo = [cs_wpo_devirtualize_calls];
+
        featurestr : array[tfeature] of string[12] = (
          'HEAP','INITFINAL','RTTI','CLASSES','EXCEPTIONS','EXITCODE',
          'ANSISTRINGS','WIDESTRINGS','TEXTIO','CONSOLEIO','FILEIO',

+ 60 - 0
compiler/msg/errore.msg

@@ -2494,6 +2494,62 @@ option_ppc386_deprecated=11042_W_Use of ppc386.cfg is deprecated, please use fpc
 %\end{description}
 # EndOfTeX
 
+#
+#  Whole program optimization
+#
+# 12004 is the last used one
+#
+# BeginOfTeX
+%
+% \section{Whole program optimisation messages}
+% This section lists errors that occur when the compiler is performing
+% whole program optimization.
+wpo_cant_find_file=12000_E_Cannot open whole program optimization feedback file $1
+% The compiler cannot open the specified feedback file with whole program optimization information.
+wpo_begin_processing=12001_D_Processing whole program optimization information in wpo feedback file $1
+% The compiler starts processing whole program optimization information found in the named file.
+wpo_end_processing=12002_D_Finished processing the whole program optimization information in wpo feedback file $1
+% The compiler has finished processing the whole program optimization information found in the named file.
+wpo_expected_section=12003_E_Expected section header, but got "$2" at line $1 of wpo feedback file
+% The compiler expected a section header in the whole program optimization file (starting with \%),
+% but did not find it.
+wpo_no_section_handler=12004_W_No handler registered for whole program optimization section "$2" at line $1 of wpo feedback file, ignoring
+% The compiler has no handler to deal with the mentioned whole program optimization information
+% section, and will therefore ignore it and skip to the next section.
+wpo_found_section=12005_D_Found whole program optimization section "$1" with information about "$2"
+% The compiler encountered a section with whole program optimization information, and according
+% to its handler this section contains information usable for the mentioned purpose.
+wpo_no_input_specified=12006_E_The selected whole program optimizations require a previously generated feedback file (use -Fw to specify)
+% The compiler needs information gathered during a previous compilation run to perform the selected
+% whole program optimizations. You can specify the location of the feedback file containing this
+% information using the -Fw switch.
+wpo_not_enough_info=12007_E_No collected information necessary to perform "$1" whole program optimization found
+% While you pointed the compiler to a file containing whole program optimization feedback, it
+% did not contain the information necessary to perform the selected optimizations. You most likely
+% have to recompile the program using the appropate -OWxxx switch.
+wpo_no_output_specified=12008_E_Specify a whole program optimization feedback file to store the generated info in (using -FW)
+% You have to specify the feedback file in which the compiler has to store the whole program optimization
+% information that is generated during the compilation run. This can be done using the -FW switch.
+wpo_output_without_info_gen=12009_E_Not generating any whole program optimization information, yet a feedback file was specified (using -FW)
+% The compiler was instructed to store whole program optimization feedback into a file specified using -FW,
+% but not to actually generated any whole program optimization feedback. The classes of to be
+% generated information can be speciied using -OWxxx
+wpo_input_without_info_use=12010_E_Not performing any whole program optimizations, yet an input feedback file was specified (using -Fw)
+% The compiler was not instructed to perform any whole program optimizations (no -Owxxx parameters),
+% but nevertheless an input file with such feedback was specified (using -Fwyyy). Since this can
+% indicate that you forgot to specify an -Owxxx parameter, the compiler generates an error in this case.
+wpo_skipping_unnecessary_section=12011_D_Skipping whole program optimization section "$1", because not needed by the requested optimizations
+% The whole program optimization feedback file contains a section with information that is not
+% required by the selected whole program optimizations
+wpo_duplicate_wpotype=12012_W_Overriding previously read information for "$1" from feedback input file using information in section "$2"
+% The feedback file contains multiple sections that provide the same class of information (e.g.,
+% information about which virtual methods can be devirtualized). In this case, the information in last encountered
+% seciont is used. Turn on debugging output (-vd) to see for each section what class of information
+% it provides.
+%\end{description}
+# EndOfTeX
+
+
 #
 # Logo (option -l)
 #
@@ -2526,6 +2582,10 @@ Supported ABI targets:
 Supported Optimizations:
   $OPTIMIZATIONS
 
+Supported Whole Program Optimizations:
+  All
+  $WPOPTIMIZATIONS
+
 This program comes under the GNU General Public Licence
 For more information read COPYING.FPC
 

+ 15 - 2
compiler/msgidx.inc

@@ -750,13 +750,26 @@ const
   option_config_is_dir=11040;
   option_confict_asm_debug=11041;
   option_ppc386_deprecated=11042;
+  wpo_cant_find_file=12000;
+  wpo_begin_processing=12001;
+  wpo_end_processing=12002;
+  wpo_expected_section=12003;
+  wpo_no_section_handler=12004;
+  wpo_found_section=12005;
+  wpo_no_input_specified=12006;
+  wpo_not_enough_info=12007;
+  wpo_no_output_specified=12008;
+  wpo_output_without_info_gen=12009;
+  wpo_input_without_info_use=12010;
+  wpo_skipping_unnecessary_section=12011;
+  wpo_duplicate_wpotype=12012;
   option_logo=11023;
   option_info=11024;
   option_help_pages=11025;
 
-  MsgTxtSize = 47219;
+  MsgTxtSize = 48555;
 
   MsgIdxMax : array[1..20] of longint=(
     24,87,248,84,65,50,108,22,201,61,
-    43,1,1,1,1,1,1,1,1,1
+    43,13,1,1,1,1,1,1,1,1
   );

+ 133 - 104
compiler/msgtxt.inc

@@ -1,7 +1,7 @@
 {$ifdef Delphi}
-const msgtxt : array[0..000196] of string[240]=(
+const msgtxt : array[0..000202] of string[240]=(
 {$else Delphi}
-const msgtxt : array[0..000196,1..240] of char=(
+const msgtxt : array[0..000202,1..240] of char=(
 {$endif Delphi}
   '01000_T_Compiler: $1'#000+
   '01001_D_Compiler OS: $1'#000+
@@ -836,9 +836,34 @@ const msgtxt : array[0..000196,1..240] of char=(
   '11041_W_Ass','embler output selected "$1" cannot generate debug info, d'+
   'ebugging disabled'#000+
   '11042_W_Use of ppc386.cfg is deprecated, please use fpc.cfg instead'#000+
-  '11023_Free Pascal Compiler version $FPCFULLVERSION [$FPCDATE] for $FPC'+
-  'CPU'#010+
-  'Copyright (c) 1993-2008',' by Florian Klaempfl'#000+
+  '12000_E_Cannot open whole program optimization feedback file $1'#000+
+  '12001_D_Processing whole program ','optimization information in wpo fee'+
+  'dback file $1'#000+
+  '12002_D_Finished processing the whole program optimization information'+
+  ' in wpo feedback file $1'#000+
+  '12003_E_Expected section header, but got "$2" at line $1 of wpo feedba'+
+  'ck file'#000+
+  '12004_W_No handler',' registered for whole program optimization section'+
+  ' "$2" at line $1 of wpo feedback file, ignoring'#000+
+  '12005_D_Found whole program optimization section "$1" with information'+
+  ' about "$2"'#000+
+  '12006_E_The selected whole program optimizations require a p','reviousl'+
+  'y generated feedback file (use -Fw to specify)'#000+
+  '12007_E_No collected information necessary to perform "$1" whole progr'+
+  'am optimization found'#000+
+  '12008_E_Specify a whole program optimization feedback file to store th'+
+  'e generated info in (us','ing -FW)'#000+
+  '12009_E_Not generating any whole program optimization information, yet'+
+  ' a feedback file was specified (using -FW)'#000+
+  '12010_E_Not performing any whole program optimizations, yet an input f'+
+  'eedback file was specified (using -Fw)'#000+
+  '12011_D_S','kipping whole program optimization section "$1", because no'+
+  't needed by the requested optimizations'#000+
+  '12012_W_Overriding previously read information for "$1" from feedback '+
+  'input file using information in section "$2"'#000+
+  '11023_Free Pascal Compiler',' version $FPCFULLVERSION [$FPCDATE] for $F'+
+  'PCCPU'#010+
+  'Copyright (c) 1993-2008 by Florian Klaempfl'#000+
   '11024_Free Pascal Compiler version $FPCVERSION'#010+
   #010+
   'Compiler Date      : $FPCDATE'#010+
@@ -847,10 +872,10 @@ const msgtxt : array[0..000196,1..240] of char=(
   'Supported targets:'#010+
   '  $OSTARGETS'#010+
   #010+
-  'Supported CPU instruction sets:'#010+
+  'Support','ed CPU instruction sets:'#010+
   '  $INSTRUCTIONSETS'#010+
   #010+
-  'Supported FPU instruction ','sets:'#010+
+  'Supported FPU instruction sets:'#010+
   '  $FPUINSTRUCTIONSETS'#010+
   #010+
   'Supported ABI targets:'#010+
@@ -859,271 +884,275 @@ const msgtxt : array[0..000196,1..240] of char=(
   'Supported Optimizations:'#010+
   '  $OPTIMIZATIONS'#010+
   #010+
+  'Supported Whole Program Optimizations:'#010+
+  '  All'#010+
+  '  $WPOPTIMIZAT','IONS'#010+
+  #010+
   'This program comes under the GNU General Public Licence'#010+
   'For more information read COPYING.FPC'#010+
   #010+
   'Report bugs,suggestions etc to:'#010+
-  '   ','              [email protected]'#000+
-  '11025_**0*_Put + after a boolean switch option to enable it, - to disa'+
+  '                 [email protected]'#000+
+  '11025_**0*_Put + after a boolean switch option to enable it, - to disa',
   'ble it'#010+
   '**1a_The compiler doesn'#039't delete the generated assembler file'#010+
   '**2al_List sourcecode lines in assembler file'#010+
-  '**2an_List node info ','in assembler file'#010+
+  '**2an_List node info in assembler file'#010+
   '*L2ap_Use pipes instead of creating temporary assembler files'#010+
-  '**2ar_List register allocation/release info in assembler file'#010+
+  '**2ar_List register allo','cation/release info in assembler file'#010+
   '**2at_List temp allocation/release info in assembler file'#010+
   '**1A<x>_Output format:'#010+
-  '**2Adefault_Use d','efault assembler'#010+
+  '**2Adefault_Use default assembler'#010+
   '3*2Aas_Assemble using GNU AS'#010+
   '3*2Anasmcoff_COFF (Go32v2) file using Nasm'#010+
-  '3*2Anasmelf_ELF32 (Linux) file using Nasm'#010+
+  '3*2Anasmelf_ELF','32 (Linux) file using Nasm'#010+
   '3*2Anasmwin32_Win32 object file using Nasm'#010+
   '3*2Anasmwdosx_Win32/WDOSX object file using Nasm'#010+
-  '3*2Awasm_Obj file',' using Wasm (Watcom)'#010+
+  '3*2Awasm_Obj file using Wasm (Watcom)'#010+
   '3*2Anasmobj_Obj file using Nasm'#010+
   '3*2Amasm_Obj file using Masm (Microsoft)'#010+
-  '3*2Atasm_Obj file using Tasm (Borland)'#010+
+  '3*2Atasm_O','bj file using Tasm (Borland)'#010+
   '3*2Aelf_ELF (Linux) using internal writer'#010+
   '3*2Acoff_COFF (Go32v2) using internal writer'#010+
-  '3*2Apecoff_PE-COFF (','Win32) using internal writer'#010+
+  '3*2Apecoff_PE-COFF (Win32) using internal writer'#010+
   '4*2Aas_Assemble using GNU AS'#010+
   '6*2Aas_Unix o-file using GNU AS'#010+
-  '6*2Agas_GNU Motorola assembler'#010+
+  '6*2Agas_GNU Mo','torola assembler'#010+
   '6*2Amit_MIT Syntax (old GAS)'#010+
   '6*2Amot_Standard Motorola assembler'#010+
   'A*2Aas_Assemble using GNU AS'#010+
-  'P*2Aas_Assemble using GNU',' AS'#010+
+  'P*2Aas_Assemble using GNU AS'#010+
   'S*2Aas_Assemble using GNU AS'#010+
   '**1b_Generate browser info'#010+
   '**2bl_Generate local symbol info'#010+
-  '**1B_Build all modules'#010+
+  '**1B_Build ','all modules'#010+
   '**1C<x>_Code generation options:'#010+
   '**2Ca<x>_Select ABI, see fpc -i for possible values'#010+
   '**2Cb_Generate big-endian code'#010+
-  '**2Cc<x>','_Set default calling convention to <x>'#010+
+  '**2Cc<x>_Set default calling convention to <x>'#010+
   '**2CD_Create also dynamic library (not supported)'#010+
-  '**2Ce_Compilation with emulated floating point opcodes'#010+
+  '**2Ce_Compilati','on with emulated floating point opcodes'#010+
   '**2Cf<x>_Select fpu instruction set to use, see fpc -i for possible va'+
   'lues'#010+
-  '**2CF<x>_Minimal floa','ting point constant precision (default, 32, 64)'+
-  #010+
+  '**2CF<x>_Minimal floating point constant precision (default, 32, 64)'#010+
   '**2Cg_Generate PIC code'#010+
-  '**2Ch<n>_<n> bytes heap (between 1023 and 67107840)'#010+
+  '**2Ch<n>_<n> bytes heap (between',' 1023 and 67107840)'#010+
   '**2Ci_IO-checking'#010+
   '**2Cn_Omit linking stage'#010+
   '**2Co_Check overflow of integer operations'#010+
-  '**2CO_Check for possible overf','low of integer operations'#010+
+  '**2CO_Check for possible overflow of integer operations'#010+
   '**2Cp<x>_Select instruction set, see fpc -i for possible values'#010+
-  '**2CP<x>=<y>_ packing settings'#010+
+  '**2CP<x>=<y>_ ','packing settings'#010+
   '**3CPPACKSET=<y>_ <y> set allocation: 0, 1 or DEFAULT or NORMAL, 2, 4 '+
   'and 8'#010+
   '**2Cr_Range checking'#010+
-  '**2CR_Verify object me','thod call validity'#010+
+  '**2CR_Verify object method call validity'#010+
   '**2Cs<n>_Set stack size to <n>'#010+
   '**2Ct_Stack checking'#010+
-  '**2CX_Create also smartlinked library'#010+
+  '**2CX_Create also smartlinked lib','rary'#010+
   '**1d<x>_Defines the symbol <x>'#010+
   '**1D_Generate a DEF file'#010+
   '**2Dd<x>_Set description to <x>'#010+
   '**2Dv<x>_Set DLL version to <x>'#010+
-  '*O2Dw_PM ap','plication'#010+
+  '*O2Dw_PM application'#010+
   '**1e<x>_Set path to executable'#010+
   '**1E_Same as -Cn'#010+
   '**1fPIC_Same as -Cg'#010+
-  '**1F<x>_Set file names and paths:'#010+
+  '**1F<x>_Set file names and',' paths:'#010+
   '**2Fa<x>[,y]_(for a program) load units <x> and [y] before uses is par'+
   'sed'#010+
   '**2Fc<x>_Set input codepage to <x>'#010+
-  '**2FC<x>_Set RC com','piler binary name to <x>'#010+
+  '**2FC<x>_Set RC compiler binary name to <x>'#010+
   '**2FD<x>_Set the directory where to search for compiler utilities'#010+
-  '**2Fe<x>_Redirect error output to <x>'#010+
+  '**2Fe<x>_Redi','rect error output to <x>'#010+
   '**2Ff<x>_Add <x> to framework path (Darwin only)'#010+
   '**2FE<x>_Set exe/unit output path to <x>'#010+
-  '**2Fi<x>_Add <x> to i','nclude path'#010+
+  '**2Fi<x>_Add <x> to include path'#010+
   '**2Fl<x>_Add <x> to library path'#010+
   '**2FL<x>_Use <x> as dynamic linker'#010+
-  '**2Fm<x>_Load unicode conversion table from <x>.txt in the compiler di'+
-  'r'#010+
+  '**2Fm<x>_Load unicode co','nversion table from <x>.txt in the compiler '+
+  'dir'#010+
   '**2Fo<x>_Add <x> to object path'#010+
   '**2Fr<x>_Load error message file <x>'#010+
-  '**2FR<x>_Set resour','ce (.res) linker to <x>'#010+
+  '**2FR<x>_Set resource (.res) linker to <x>'#010+
   '**2Fu<x>_Add <x> to unit path'#010+
-  '**2FU<x>_Set unit output path to <x>, overrides -FE'#010+
+  '**2FU<x>_Set unit output path to <x>, overrides -F','E'#010+
   '*g1g_Generate debug information (default format for target)'#010+
   '*g2gc_Generate checks for pointers'#010+
-  '*g2gh_Use heaptrace unit (for memory le','ak/corruption debugging)'#010+
+  '*g2gh_Use heaptrace unit (for memory leak/corruption debugging)'#010+
   '*g2gl_Use line info unit (show more info with backtraces)'#010+
-  '*g2go<x>_Set debug information options'#010+
+  '*g2go<x>_Set debug in','formation options'#010+
   '*g3godwarfsets_ Enable Dwarf set debug information (breaks gdb < 6.5)'#010+
   '*g2gp_Preserve case in stabs symbol names'#010+
-  '*g2gs_','Generate stabs debug information'#010+
+  '*g2gs_Generate stabs debug information'#010+
   '*g2gt_Trash local variables (to detect uninitialized uses)'#010+
-  '*g2gv_Generates programs traceable with valgrind'#010+
+  '*g2gv_Genera','tes programs traceable with valgrind'#010+
   '*g2gw_Generate dwarf-2 debug information (same as -gw2)'#010+
   '*g2gw2_Generate dwarf-2 debug information'#010+
-  '*','g2gw3_Generate dwarf-3 debug information'#010+
+  '*g2gw3_Generate dwarf-3 debug information'#010+
   '**1i_Information'#010+
   '**2iD_Return compiler date'#010+
-  '**2iV_Return short compiler version'#010+
+  '**2iV_Return short ','compiler version'#010+
   '**2iW_Return full compiler version'#010+
   '**2iSO_Return compiler OS'#010+
   '**2iSP_Return compiler host processor'#010+
-  '**2iTO_Return target',' OS'#010+
+  '**2iTO_Return target OS'#010+
   '**2iTP_Return target processor'#010+
   '**1I<x>_Add <x> to include path'#010+
   '**1k<x>_Pass <x> to the linker'#010+
-  '**1l_Write logo'#010+
+  '**1l_W','rite logo'#010+
   '**1M<x>_Set language mode to <x>'#010+
   '**2Mfpc_Free Pascal dialect (default)'#010+
   '**2Mobjfpc_FPC mode with Object Pascal support'#010+
-  '**2Mdelp','hi_Delphi 7 compatibility mode'#010+
+  '**2Mdelphi_Delphi 7 compatibility mode'#010+
   '**2Mtp_TP/BP 7.0 compatibility mode'#010+
-  '**2Mmacpas_Macintosh Pascal dialects compatibility mode'#010+
+  '**2Mmacpas_Macintosh Pascal dialects ','compatibility mode'#010+
   '**1n_Do not read the default config files'#010+
   '**1N<x>_Node tree optimizations'#010+
   '**2Nu_Unroll loops'#010+
-  '**1o<x>_Change the name ','of the executable produced to <x>'#010+
+  '**1o<x>_Change the name of the executable produced to <x>'#010+
   '**1O<x>_Optimizations:'#010+
   '**2O-_Disable optimizations'#010+
-  '**2O1_Level 1 optimizations (quick and debugger friendly)'#010+
+  '**2O1_Level 1 optim','izations (quick and debugger friendly)'#010+
   '**2O2_Level 2 optimizations (-O1 + quick optimizations)'#010+
-  '**2O3_Level 3 optimizations (-O2 + slow o','ptimizations)'#010+
+  '**2O3_Level 3 optimizations (-O2 + slow optimizations)'#010+
   '**2Oa<x>=<y>_Set alignment'#010+
-  '**2Oo[NO]<x>_Enable or disable optimizations, see fpc -i for possible '+
-  'values'#010+
+  '**2Oo[NO]<x>_Enable or disable optimizations, see fpc -i for po','ssibl'+
+  'e values'#010+
   '**2Op<x>_Set target cpu for optimizing, see fpc -i for possible values'+
   #010+
   '**2Os_Optimize for size rather than speed'#010+
-  '**1pg_Gen','erate profile code for gprof (defines FPC_PROFILE)'#010+
+  '**1pg_Generate profile code for gprof (defines FPC_PROFILE)'#010+
   '**1R<x>_Assembler reading style:'#010+
-  '**2Rdefault_Use default assembler for target'#010+
+  '**2Rdefault_Use defa','ult assembler for target'#010+
   '3*2Ratt_Read AT&T style assembler'#010+
   '3*2Rintel_Read Intel style assembler'#010+
   '6*2RMOT_Read motorola style assembler'#010+
-  '**','1S<x>_Syntax options:'#010+
+  '**1S<x>_Syntax options:'#010+
   '**2S2_Same as -Mobjfpc'#010+
   '**2Sc_Support operators like C (*=,+=,/= and -=)'#010+
-  '**2Sa_Turn on assertions'#010+
+  '**2Sa_Turn',' on assertions'#010+
   '**2Sd_Same as -Mdelphi'#010+
   '**2Se<x>_Error options. <x> is a combination of the following:'#010+
-  '**3*_<n> : Compiler halts after the',' <n> errors (default is 1)'#010+
+  '**3*_<n> : Compiler halts after the <n> errors (default is 1)'#010+
   '**3*_w : Compiler also halts after warnings'#010+
-  '**3*_n : Compiler also halts after notes'#010+
+  '**3*_n : Compiler also halts afte','r notes'#010+
   '**3*_h : Compiler also halts after hints'#010+
   '**2Sg_Enable LABEL and GOTO (default in -Mtp and -Mdelphi)'#010+
-  '**2Sh_Use ansistrings by def','ault instead of shortstrings'#010+
+  '**2Sh_Use ansistrings by default instead of shortstrings'#010+
   '**2Si_Turn on inlining of procedures/functions declared as "inline"'#010+
-  '**2Sk_Load fpcylix unit'#010+
+  '**2Sk_L','oad fpcylix unit'#010+
   '**2SI<x>_Set interface style to <x>'#010+
   '**3SIcom_COM compatible interface (default)'#010+
   '**3SIcorba_CORBA compatible interface'#010+
-  '*','*2Sm_Support macros like C (global)'#010+
+  '**2Sm_Support macros like C (global)'#010+
   '**2So_Same as -Mtp'#010+
-  '**2Ss_Constructor name must be init (destructor must be done)'#010+
+  '**2Ss_Constructor name must be init (destructor m','ust be done)'#010+
   '**2St_Allow static keyword in objects'#010+
   '**2Sx_Enable exception keywords (default in Delphi/ObjFPC modes)'#010+
-  '**1s_Do not call ass','embler and linker'#010+
+  '**1s_Do not call assembler and linker'#010+
   '**2sh_Generate script to link on host'#010+
   '**2st_Generate script to link on target'#010+
-  '**2sr_Skip register allocation phase (use with -alr)'#010+
+  '**2sr_Sk','ip register allocation phase (use with -alr)'#010+
   '**1T<x>_Target operating system:'#010+
   '3*2Temx_OS/2 via EMX (including EMX/RSX extender)'#010+
-  '3*2Tfree','bsd_FreeBSD'#010+
+  '3*2Tfreebsd_FreeBSD'#010+
   '3*2Tgo32v2_Version 2 of DJ Delorie DOS extender'#010+
   '3*2Tlinux_Linux'#010+
   '3*2Tnetbsd_NetBSD'#010+
-  '3*2Tnetware_Novell Netware Module (clib)'#010+
+  '3*2Tnetwar','e_Novell Netware Module (clib)'#010+
   '3*2Tnetwlibc_Novell Netware Module (libc)'#010+
   '3*2Topenbsd_OpenBSD'#010+
   '3*2Tos2_OS/2 / eComStation'#010+
-  '3*2Tsunos_SunOS/','Solaris'#010+
+  '3*2Tsunos_SunOS/Solaris'#010+
   '3*2Tsymbian_Symbian OS'#010+
   '3*2Twatcom_Watcom compatible DOS extender'#010+
   '3*2Twdosx_WDOSX DOS extender'#010+
-  '3*2Twin32_Windows 32 Bit'#010+
+  '3*','2Twin32_Windows 32 Bit'#010+
   '3*2Twince_Windows CE'#010+
   '4*2Tlinux_Linux'#010+
   '6*2Tamiga_Commodore Amiga'#010+
   '6*2Tatari_Atari ST/STe/TT'#010+
   '6*2Tlinux_Linux/m68k'#010+
-  '6*2','Tmacos_Macintosh m68k (not supported)'#010+
+  '6*2Tmacos_Macintosh m68k (not supported)'#010+
   '6*2Tpalmos_PalmOS'#010+
   'A*2Tlinux_Linux'#010+
   'A*2Twince_Windows CE'#010+
-  'P*2Tamiga_AmigaOS on PowerPC'#010+
+  'P*2Tamiga_A','migaOS on PowerPC'#010+
   'P*2Tdarwin_Darwin and Mac OS X on PowerPC'#010+
   'P*2Tlinux_Linux on PowerPC'#010+
   'P*2Tmacos_Mac OS (classic) on PowerPC'#010+
-  'P*2Tmorphos','_MorphOS'#010+
+  'P*2Tmorphos_MorphOS'#010+
   'S*2Tlinux_Linux'#010+
   '**1u<x>_Undefines the symbol <x>'#010+
   '**1U_Unit options:'#010+
-  '**2Un_Do not check where the unit name matches the file name'#010+
+  '**2Un_Do not check where th','e unit name matches the file name'#010+
   '**2Ur_Generate release unit files (never automatically recompiled)'#010+
   '**2Us_Compile a system unit'#010+
-  '**1v<x>','_Be verbose. <x> is a combination of the following letters:'#010+
-  '**2*_e : Show errors (default)       0 : Show nothing (except errors)'#010+
+  '**1v<x>_Be verbose. <x> is a combination of the following letters:'#010+
+  '**2*_e : Show errors (default)       0 : Sho','w nothing (except errors'+
+  ')'#010+
   '**2*_w : Show warnings               u : Show unit info'#010+
-  '**2*_n : Show notes                  t : Show tried/us','ed files'#010+
+  '**2*_n : Show notes                  t : Show tried/used files'#010+
   '**2*_h : Show hints                  c : Show conditionals'#010+
-  '**2*_i : Show general info           d : Show debug info'#010+
+  '**2*_i : Show general info          ',' d : Show debug info'#010+
   '**2*_l : Show linenumbers            r : Rhide/GCC compatibility mode'#010+
-  '**2*_a : Show everything             x : Exec','utable info (Win32 only'+
-  ')'#010+
+  '**2*_a : Show everything             x : Executable info (Win32 only)'#010+
   '**2*_b : Write file names messages with full path'#010+
-  '**2*_v : Write fpcdebug.txt with     p : Write tree.log with parse tre'+
-  'e'#010+
+  '**2*_v : Write fpcdebug.txt w','ith     p : Write tree.log with parse t'+
+  'ree'#010+
   '**2*_    lots of debugging info'#010+
   '3*1W<x>_Target-specific options (targets)'#010+
-  'A*1W<x>_Target-spec','ific options (targets)'#010+
+  'A*1W<x>_Target-specific options (targets)'#010+
   'P*1W<x>_Target-specific options (targets)'#010+
-  '3*2Wb_Create a bundle instead of a library (Darwin)'#010+
+  '3*2Wb_Create a bundle instead of a libr','ary (Darwin)'#010+
   'P*2Wb_Create a bundle instead of a library (Darwin)'#010+
   'p*2Wb_Create a bundle instead of a library (Darwin)'#010+
-  '3*2WB_Create a relo','catable image (Windows)'#010+
+  '3*2WB_Create a relocatable image (Windows)'#010+
   'A*2WB_Create a relocatable image (Windows, Symbian)'#010+
-  '3*2WC_Specify console type application (EMX, OS/2, Windows)'#010+
+  '3*2WC_Specify console type a','pplication (EMX, OS/2, Windows)'#010+
   'A*2WC_Specify console type application (Windows)'#010+
-  'P*2WC_Specify console type application (Classic Mac OS)',#010+
+  'P*2WC_Specify console type application (Classic Mac OS)'#010+
   '3*2WD_Use DEFFILE to export functions of DLL or EXE (Windows)'#010+
-  'A*2WD_Use DEFFILE to export functions of DLL or EXE (Windows)'#010+
+  'A*2WD_Use DEFFILE to export functions of ','DLL or EXE (Windows)'#010+
   '3*2We_Use external resources (Darwin)'#010+
   'P*2We_Use external resources (Darwin)'#010+
   'p*2We_Use external resources (Darwin)'#010+
-  '3','*2WF_Specify full-screen type application (EMX, OS/2)'#010+
-  '3*2WG_Specify graphic type application (EMX, OS/2, Windows)'#010+
+  '3*2WF_Specify full-screen type application (EMX, OS/2)'#010+
+  '3*2WG_Specify graphic type application (EMX, OS/2,',' Windows)'#010+
   'A*2WG_Specify graphic type application (Windows)'#010+
   'P*2WG_Specify graphic type application (Classic Mac OS)'#010+
-  '3*2Wi_Use internal re','sources (Darwin)'#010+
+  '3*2Wi_Use internal resources (Darwin)'#010+
   'P*2Wi_Use internal resources (Darwin)'#010+
   'p*2Wi_Use internal resources (Darwin)'#010+
-  '3*2WN_Do not generate relocation code, needed for debugging (Windows)'#010+
+  '3*2WN_Do no','t generate relocation code, needed for debugging (Windows'+
+  ')'#010+
   'A*2WN_Do not generate relocation code, needed for debugging (Windows)'#010+
-  '3*2WR_G','enerate relocation code (Windows)'#010+
+  '3*2WR_Generate relocation code (Windows)'#010+
   'A*2WR_Generate relocation code (Windows)'#010+
-  'P*2WT_Specify MPW tool type application (Classic Mac OS)'#010+
+  'P*2WT_Specify MPW tool type a','pplication (Classic Mac OS)'#010+
   '**1X_Executable options:'#010+
   '**2Xc_Pass --shared/-dynamic to the linker (BeOS, Darwin, FreeBSD, Lin'+
   'ux)'#010+
-  '**2Xd_Do ','not use standard library search path (needed for cross comp'+
-  'ile)'#010+
+  '**2Xd_Do not use standard library search path (needed for cross compil'+
+  'e)'#010+
   '**2Xe_Use external linker'#010+
-  '**2Xg_Create debuginfo in a separate file and add a debuglink section '+
-  'to executable'#010+
-  '**2XD_Try to link units dynamically      (defines FPC_LINK_DYNAMIC',')'#010+
+  '**2Xg_Create d','ebuginfo in a separate file and add a debuglink sectio'+
+  'n to executable'#010+
+  '**2XD_Try to link units dynamically      (defines FPC_LINK_DYNAMIC)'#010+
   '**2Xi_Use internal linker'#010+
   '**2Xm_Generate link map'#010+
-  '**2XM<x>_Set the name of the '#039'main'#039' program routine (default i'+
-  's '#039'main'#039')'#010+
+  '**2XM<x>_Set the name of the '#039'main'#039' program routine ','(default'+
+  ' is '#039'main'#039')'#010+
   '**2XP<x>_Prepend the binutils names with the prefix <x>'#010+
-  '**2Xr<x>_Set library search path to <x> (needed for cross co','mpile) ('+
-  'BeOS, Linux)'#010+
+  '**2Xr<x>_Set library search path to <x> (needed for cross compile) (Be'+
+  'OS, Linux)'#010+
   '**2XR<x>_Prepend <x> to all linker search paths (BeOS, Darwin, FreeBSD'+
-  ', Linux, Mac OS, Solaris)'#010+
+  ', Linux, Mac ','OS, Solaris)'#010+
   '**2Xs_Strip all symbols from executable'#010+
   '**2XS_Try to link units statically (default, defines FPC_LINK_STATIC)'#010+
-  '**2Xt_Link wi','th static libraries (-static is passed to linker)'#010+
-  '**2XX_Try to smartlink units             (defines FPC_LINK_SMART)'#010+
+  '**2Xt_Link with static libraries (-static is passed to linker)'#010+
+  '**2XX_Try to smartlink units             (defines FPC_','LINK_SMART)'#010+
   '**1*_'#010+
   '**1?_Show this help'#010+
   '**1h_Shows this help without waiting'#000

+ 34 - 7
compiler/ncal.pas

@@ -1540,7 +1540,21 @@ implementation
                 vmttree:=methodpointer.getcopy;
                 { Only a typenode can be passed when it is called with <class of xx>.create }
                 if vmttree.nodetype=typen then
-                  vmttree:=cloadvmtaddrnode.create(vmttree);
+                  begin
+                    { we know the exact class type being created }
+                    tclassrefdef(methodpointer.resultdef).pointeddef.register_created_object_type;
+                    vmttree:=cloadvmtaddrnode.create(vmttree);
+                  end
+                else
+                  begin
+                    { the loadvmtaddrnode is already created in case of classtype.create }
+                    if (vmttree.nodetype=loadvmtaddrn) and
+                       (tloadvmtaddrnode(vmttree).left.nodetype = typen) then
+                      tclassrefdef(methodpointer.resultdef).pointeddef.register_created_object_type
+                    else
+                      { the created class can be any child class as well -> register classrefdef }
+                      methodpointer.resultdef.register_created_object_type;
+                  end;
               end
             else
               begin
@@ -1575,11 +1589,15 @@ implementation
                       vmttree:=cpointerconstnode.create(1,voidpointertype)
                     else
                       vmttree:=cpointerconstnode.create(0,voidpointertype)
-                  else if (current_procinfo.procdef.proctypeoption=potype_constructor) and
-                          (procdefinition.proctypeoption=potype_constructor) then
+                  { else, if we are calling a constructor }
+                  else if (current_procinfo.procdef.proctypeoption=potype_constructor) then
                     vmttree:=cpointerconstnode.create(0,voidpointertype)
                   else
-                    vmttree:=cpointerconstnode.create(1,voidpointertype);
+                    begin
+                      { created a new class instance of this type }
+                      methodpointer.resultdef.register_created_object_type;
+                      vmttree:=cpointerconstnode.create(1,voidpointertype);
+                    end;
                 end
             else
             { normal call to method like cl1.proc }
@@ -1602,11 +1620,14 @@ implementation
                 else
                   begin
                     if (current_procinfo.procdef.proctypeoption=potype_constructor) and
-                       (procdefinition.proctypeoption=potype_constructor) and
                        (nf_is_self in methodpointer.flags) then
                       vmttree:=cpointerconstnode.create(0,voidpointertype)
                     else
-                      vmttree:=cpointerconstnode.create(1,voidpointertype);
+                      begin
+                        { created a new class instance of this type }
+                        methodpointer.resultdef.register_created_object_type;
+                        vmttree:=cpointerconstnode.create(1,voidpointertype);
+                      end;
                   end;
               end;
           end
@@ -1615,7 +1636,10 @@ implementation
           begin
             { constructor with extended syntax called from new }
             if (cnf_new_call in callnodeflags) then
+              begin
+                methodpointer.resultdef.register_created_object_type;
                 vmttree:=cloadvmtaddrnode.create(ctypenode.create(methodpointer.resultdef))
+              end
             else
               { destructor with extended syntax called from dispose }
               if (cnf_dispose_call in callnodeflags) then
@@ -1644,7 +1668,10 @@ implementation
                    if (methodpointer.nodetype=typen) then
                      vmttree:=cpointerconstnode.create(0,voidpointertype)
                    else
-                     vmttree:=cloadvmtaddrnode.create(ctypenode.create(methodpointer.resultdef))
+                     begin
+                       methodpointer.resultdef.register_created_object_type;
+                       vmttree:=cloadvmtaddrnode.create(ctypenode.create(methodpointer.resultdef))
+                     end;
                  end
                else
                  vmttree:=cpointerconstnode.create(0,voidpointertype);

+ 10 - 3
compiler/ncgcal.pas

@@ -86,7 +86,8 @@ implementation
 {$endif x86}
       ncgutil,
       cgobj,tgobj,
-      procinfo;
+      procinfo,
+      wpobase;
 
 
 {*****************************************************************************
@@ -863,6 +864,7 @@ implementation
 
     procedure tcgcallnode.pass_generate_code;
       var
+        name_to_call: shortstring;
         regs_to_save_int,
         regs_to_save_fpu,
         regs_to_save_mm   : Tcpuregisterset;
@@ -923,11 +925,13 @@ implementation
          { procedure variable or normal function call ? }
          if (right=nil) then
            begin
+             name_to_call:='';
              { When methodpointer is typen we don't need (and can't) load
                a pointer. We can directly call the correct procdef (PFV) }
              if (po_virtualmethod in procdefinition.procoptions) and
                 assigned(methodpointer) and
-                (methodpointer.nodetype<>typen) then
+                (methodpointer.nodetype<>typen) and
+                not wpoinfomanager.can_be_devirtualized(methodpointer.resultdef,procdefinition,name_to_call) then
                begin
                  { virtual methods require an index }
                  if tprocdef(procdefinition).extnumber=$ffff then
@@ -1018,7 +1022,10 @@ implementation
                       if (po_interrupt in procdefinition.procoptions) then
                         extra_interrupt_code;
                       extra_call_code;
-                      cg.a_call_name(current_asmdata.CurrAsmList,tprocdef(procdefinition).mangledname);
+                      if (name_to_call='') then
+                        cg.a_call_name(current_asmdata.CurrAsmList,tprocdef(procdefinition).mangledname)
+                      else
+                        cg.a_call_name(current_asmdata.CurrAsmList,name_to_call);
                       extra_post_call_code;
                     end;
                end;

+ 44 - 39
compiler/nobj.pas

@@ -55,9 +55,9 @@ interface
         VMTSymEntryList : TFPHashObjectList;
         has_constructor,
         has_virtual_method : boolean;
-        function is_new_vmt_entry(VMTSymEntry:TVMTSymEntry;pd:tprocdef):boolean;
+        function is_new_vmt_entry(VMTSymEntry:TVMTSymEntry;pd:tprocdef; check_visibility: boolean):boolean;
         procedure add_new_vmt_entry(VMTSymEntry:TVMTSymEntry;pd:tprocdef);
-        procedure add_vmt_entries(objdef:tobjectdef);
+        procedure add_vmt_entries(objdef:tobjectdef; check_visibility: boolean);
         function  intf_search_procdef_by_name(proc: tprocdef;const name: string): tprocdef;
         procedure intf_get_procdefs(ImplIntf:TImplementedInterface;IntfDef:TObjectDef);
         procedure intf_get_procdefs_recursive(ImplIntf:TImplementedInterface;IntfDef:TObjectDef);
@@ -66,7 +66,7 @@ interface
       public
         constructor create(c:tobjectdef);
         destructor  destroy;override;
-        procedure generate_vmt;
+        procedure generate_vmt(check_visibility: boolean);
       end;
 
     type
@@ -234,7 +234,7 @@ implementation
       end;
 
 
-    function TVMTBuilder.is_new_vmt_entry(VMTSymEntry:TVMTSymEntry;pd:tprocdef):boolean;
+    function TVMTBuilder.is_new_vmt_entry(VMTSymEntry:TVMTSymEntry;pd:tprocdef; check_visibility: boolean):boolean;
       const
         po_comp = [po_classmethod,po_virtualmethod,po_staticmethod,po_interrupt,po_iocheck,po_msgstr,po_msgint,
                    po_exports,po_varargs,po_explicitparaloc,po_nostackframe];
@@ -282,10 +282,11 @@ implementation
                       begin
                         if is_visible then
                           procdefcoll^.hidden:=true;
-                        if (pd._class=procdefcoll^.data._class) then
-                           MessagePos(pd.fileinfo,parser_e_overloaded_have_same_parameters)
-                        else if (_class=pd._class) and not(po_reintroduce in pd.procoptions) then
-                          MessagePos1(pd.fileinfo,parser_w_should_use_override,pd.fullprocname(false));
+                        if check_visibility then
+                          if (pd._class=procdefcoll^.data._class) then
+                             MessagePos(pd.fileinfo,parser_e_overloaded_have_same_parameters)
+                          else if (_class=pd._class) and not(po_reintroduce in pd.procoptions) then
+                            MessagePos1(pd.fileinfo,parser_w_should_use_override,pd.fullprocname(false));
                       end;
                   end
                 { if both are virtual we check the header }
@@ -303,10 +304,11 @@ implementation
                           begin
                             if is_visible then
                               procdefcoll^.hidden:=true;
-                            if (pd._class=procdefcoll^.data._class) then
-                              MessagePos(pd.fileinfo,parser_e_overloaded_have_same_parameters)
-                            else if (_class=pd._class) and not(po_reintroduce in pd.procoptions) then
-                              MessagePos1(pd.fileinfo,parser_w_should_use_override,pd.fullprocname(false));
+                            if check_visibility then
+                              if (pd._class=procdefcoll^.data._class) then
+                                MessagePos(pd.fileinfo,parser_e_overloaded_have_same_parameters)
+                              else if (_class=pd._class) and not(po_reintroduce in pd.procoptions) then
+                                MessagePos1(pd.fileinfo,parser_w_should_use_override,pd.fullprocname(false));
                           end;
                       end
                     { same parameter and return types (parameter specifiers will be checked below) }
@@ -341,7 +343,8 @@ implementation
                           for the current parsed class. Parent classes are already validated and
                           need to include all virtual methods including the ones not visible in the
                           current class }
-                        if (_class=pd._class) and
+                        if check_visibility and
+                           (_class=pd._class) and
                            (po_overridingmethod in pd.procoptions) and
                            (not procdefcoll^.visible) then
                           MessagePos1(pd.fileinfo,parser_e_nothing_to_be_overridden,pd.fullprocname(false));
@@ -369,14 +372,15 @@ implementation
                         begin
                           if is_visible then
                             procdefcoll^.hidden:=true;
-                          if (pd._class=procdefcoll^.data._class) then
-                            MessagePos(pd.fileinfo,parser_e_overloaded_have_same_parameters)
-                          else if (_class=pd._class) and not(po_reintroduce in pd.procoptions) then
-                            if not is_object(_class) then
-                              MessagePos1(pd.fileinfo,parser_w_should_use_override,pd.fullprocname(false))
-                            else
-                              { objects don't allow starting a new virtual tree }
-                              MessagePos1(pd.fileinfo,parser_e_header_dont_match_forward,procdefcoll^.data.fullprocname(false));
+                          if check_visibility then
+                            if (pd._class=procdefcoll^.data._class) then
+                              MessagePos(pd.fileinfo,parser_e_overloaded_have_same_parameters)
+                            else if (_class=pd._class) and not(po_reintroduce in pd.procoptions) then
+                              if not is_object(_class) then
+                                MessagePos1(pd.fileinfo,parser_w_should_use_override,pd.fullprocname(false))
+                              else
+                                { objects don't allow starting a new virtual tree }
+                                MessagePos1(pd.fileinfo,parser_e_header_dont_match_forward,procdefcoll^.data.fullprocname(false));
                         end;
                      end;
                   end
@@ -409,7 +413,7 @@ implementation
       end;
 
 
-    procedure TVMTBuilder.add_vmt_entries(objdef:tobjectdef);
+    procedure TVMTBuilder.add_vmt_entries(objdef:tobjectdef; check_visibility: boolean);
       var
          def : tdef;
          pd  : tprocdef;
@@ -418,7 +422,7 @@ implementation
       begin
         { start with the base class }
         if assigned(objdef.childof) then
-          add_vmt_entries(objdef.childof);
+          add_vmt_entries(objdef.childof,check_visibility);
         { process all procdefs, we must process the defs to
           keep the same order as that is written in the source
           to be compatible with the indexes in the interface vtable (PFV) }
@@ -433,7 +437,7 @@ implementation
                 if not assigned(VMTSymEntry) then
                   VMTSymEntry:=TVMTSymEntry.Create(VMTSymEntryList,pd.procsym.name);
                 { VMT entry }
-                if is_new_vmt_entry(VMTSymEntry,pd) then
+                if is_new_vmt_entry(VMTSymEntry,pd,check_visibility) then
                   add_new_vmt_entry(VMTSymEntry,pd);
               end;
           end;
@@ -663,7 +667,7 @@ implementation
       end;
 
 
-    procedure TVMTBuilder.generate_vmt;
+    procedure TVMTBuilder.generate_vmt(check_visibility: boolean);
       var
         i : longint;
         ImplIntf : TImplementedInterface;
@@ -671,26 +675,27 @@ implementation
         { Find VMT entries }
         has_constructor:=false;
         has_virtual_method:=false;
-        add_vmt_entries(_class);
+        add_vmt_entries(_class,check_visibility);
         if not(is_interface(_class)) and
            has_virtual_method and
            not(has_constructor) then
           Message1(parser_w_virtual_without_constructor,_class.objrealname^);
 
         { Find Procdefs implementing the interfaces }
-        if assigned(_class.ImplementedInterfaces) then
-          begin
-            { Collect implementor functions into the tImplementedInterface.procdefs }
-            for i:=0 to _class.ImplementedInterfaces.count-1 do
-              begin
-                ImplIntf:=TImplementedInterface(_class.ImplementedInterfaces[i]);
-                intf_get_procdefs_recursive(ImplIntf,ImplIntf.IntfDef);
-              end;
-            { Optimize interface tables to reuse wrappers }
-            intf_optimize_vtbls;
-            { Allocate interface tables }
-            intf_allocate_vtbls;
-          end;
+        if check_visibility then
+          if assigned(_class.ImplementedInterfaces) then
+            begin
+              { Collect implementor functions into the tImplementedInterface.procdefs }
+              for i:=0 to _class.ImplementedInterfaces.count-1 do
+                begin
+                  ImplIntf:=TImplementedInterface(_class.ImplementedInterfaces[i]);
+                  intf_get_procdefs_recursive(ImplIntf,ImplIntf.IntfDef);
+                end;
+              { Optimize interface tables to reuse wrappers }
+              intf_optimize_vtbls;
+              { Allocate interface tables }
+              intf_allocate_vtbls;
+            end;
       end;
 
 

+ 50 - 0
compiler/options.pas

@@ -77,6 +77,7 @@ uses
   cutils,cmsgs,
   comphook,
   symtable,scanner,rabase,
+  wpobase,
   i_bsd
   ;
 
@@ -142,6 +143,7 @@ var
   cpu : tcputype;
   fpu : tfputype;
   opt : toptimizerswitch;
+  wpopt: twpoptimizerswitch;
   abi : tabi;
 begin
   p:=MessagePchar(option_info);
@@ -217,6 +219,24 @@ begin
               end;
           end;
       end
+     else if pos('$WPOPTIMIZATIONS',s)>0 then
+      begin
+        for wpopt:=low(twpoptimizerswitch) to high(twpoptimizerswitch) do
+          begin
+{           currently all whole program optimizations are platform-independent
+            if opt in supported_wpoptimizerswitches then
+}
+              begin
+                hs:=s;
+                hs1:=WPOptimizerSwitchStr[wpopt];
+                if hs1<>'' then
+                  begin
+                    Replace(hs,'$WPOPTIMIZATIONS',hs1);
+                    Comment(V_Normal,hs);
+                  end;
+              end;
+          end;
+      end
      else
       Comment(V_Normal,s);
    end;
@@ -825,6 +845,24 @@ begin
                    end;
                  'U' :
                    OutputUnitDir:=FixPath(More,true);
+                 'W',
+                 'w':
+                   begin
+                     if More<>'' then
+                       begin
+                         DefaultReplacements(More);
+                         D:=ExtractFilePath(More);
+                         if (D<>'') then
+                           D:=FixPath(D,True);
+                         D:=D+ExtractFileName(More);
+                         if (c='W') then
+                           WpoFeedbackOutput:=D
+                         else
+                           WpoFeedbackInput:=D;
+                       end
+                     else
+                       IllegalPara(opt);
+                   end;
                  else
                    IllegalPara(opt);
                end;
@@ -1043,6 +1081,18 @@ begin
                       Message2(option_obsolete_switch_use_new,'-Or','-O2 or -Ooregvar');
                     'u' :
                       Message2(option_obsolete_switch_use_new,'-Ou','-Oouncertain');
+                    'w' :
+                      begin
+                        if not UpdateWpoStr(copy(more,j+1,length(more)),init_settings.dowpoptimizerswitches) then
+                          IllegalPara(opt);
+                        break;
+                      end;
+                    'W' :
+                      begin
+                        if not UpdateWpoStr(copy(more,j+1,length(more)),init_settings.genwpoptimizerswitches) then
+                          IllegalPara(opt);
+                        break;
+                      end;
                     else
                       IllegalPara(opt);
                   end;

+ 951 - 0
compiler/optvirt.pas

@@ -0,0 +1,951 @@
+{
+    Virtual methods optimizations (devirtualization)
+
+    Copyright (c) 2008 by Jonas Maebe
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ ****************************************************************************
+}
+unit optvirt;
+
+{$i fpcdefs.inc}
+
+  interface
+
+    uses
+      globtype,
+      cclasses,
+      symtype,symdef,
+      wpobase;
+
+    type
+       { node in an inheritance tree, contains a link to the parent type (if any) and to all
+        child types
+      }
+      tinheritancetreenode = class
+       private
+        fdef: tobjectdef;
+        fparent: tinheritancetreenode;
+        fchilds: tfpobjectlist;
+        finstantiated: boolean;
+
+        function getchild(index: longint): tinheritancetreenode;
+       public
+        constructor create(_parent: tinheritancetreenode; _def: tobjectdef; _instantiated: boolean);
+        { destroys both this node and all of its siblings }
+        destructor destroy; override;
+        function  childcount: longint;
+        function  haschilds: boolean;
+        property  childs[index: longint]: tinheritancetreenode read getchild;
+        property  parent: tinheritancetreenode read fparent;
+        property  def: tobjectdef read fdef;
+        property  instantiated: boolean read finstantiated write finstantiated;
+        { if def is not yet a child of this node, add it. In all cases, return node containing
+          this def (either new or existing one
+        }
+        function  maybeaddchild(_def: tobjectdef; _instantiated: boolean): tinheritancetreenode;
+      end;
+
+
+      tinheritancetreecallback = procedure(node: tinheritancetreenode; arg: pointer) of object;
+
+      tinheritancetree = class
+       private
+        { just a regular node with parent = nil }
+        froots: tinheritancetreenode;
+
+        classrefdefs: tfpobjectlist;
+
+        procedure foreachnodefromroot(root: tinheritancetreenode; proctocall: tinheritancetreecallback; arg: pointer);
+        function registerinstantiatedobjectdefrecursive(def: tobjectdef; instantiated: boolean): tinheritancetreenode;
+        procedure markvmethods(node: tinheritancetreenode; p: pointer);
+        procedure printobjectvmtinfo(node: tinheritancetreenode; arg: pointer);
+        procedure setinstantiated(node: tinheritancetreenode; arg: pointer);
+       public
+        constructor create;
+        destructor destroy; override;
+        { adds an objectdef (the def itself, and all of its parents that do not yet exist) to
+          the tree, and returns the leaf node
+        }
+        procedure registerinstantiateddef(def: tdef);
+        procedure checkforclassrefinheritance(def: tdef);
+        procedure foreachnode(proctocall: tinheritancetreecallback; arg: pointer);
+        procedure foreachleafnode(proctocall: tinheritancetreecallback; arg: pointer);
+        procedure optimizevirtualmethods;
+        procedure printvmtinfo;
+      end;
+
+
+      { devirtualisation information for a class }
+
+      tclassdevirtinfo = class(tfphashobject)
+       private
+        { array (indexed by vmt entry nr) of replacement statically callable method names }
+        fstaticmethodnames: tfplist;
+        function isstaticvmtentry(vmtindex: longint; out replacementname: pshortstring): boolean;
+       public
+        constructor create(hashobjectlist:tfphashobjectlist;const n: shortstring);reintroduce;
+        destructor destroy; override;
+
+        procedure addstaticmethod(vmtindex: longint; const replacementname: shortstring);
+      end;
+
+
+      { devirtualisation information for all classes in a unit }
+
+      tunitdevirtinfo = class(tfphashobject)
+       private
+        { hashtable of classes }
+        fclasses: tfphashobjectlist;
+       public
+        constructor create(hashobjectlist:tfphashobjectlist;const n: shortstring);reintroduce;
+        destructor destroy; override;
+
+        function addclass(const n: shortstring): tclassdevirtinfo;
+        function findclass(const n: shortstring): tclassdevirtinfo;
+      end;
+
+      { defvirtualisation information for all units in a program }
+
+      { tprogdevirtinfo }
+
+      tprogdevirtinfo = class(twpodevirtualisationhandler)
+       private
+        { hashtable of tunitdevirtinfo (which contain tclassdevirtinfo) }
+        funits: tfphashobjectlist;
+
+        procedure converttreenode(node: tinheritancetreenode; arg: pointer);
+        function addunitifnew(const n: shortstring): tunitdevirtinfo;
+        function findunit(const n: shortstring): tunitdevirtinfo;
+       public
+        constructor create; override;
+        destructor destroy; override;
+
+        class function getwpotype: twpotype; override;
+        class function generatesinfoforwposwitches: twpoptimizerswitches; override;
+        class function performswpoforswitches: twpoptimizerswitches; override;
+
+        { information collection }
+        procedure constructfromcompilerstate; override;
+        procedure storewpofilesection(writer: twposectionwriterintf); override;
+
+        { infromation providing }
+        procedure loadfromwpofilesection(reader: twposectionreaderintf); override;
+        function staticnameforvirtualmethod(objdef, procdef: tdef; out staticname: string): boolean; override;
+
+      end;
+
+
+  implementation
+
+    uses
+      cutils,
+      fmodule,
+      symconst,
+      symbase,
+      symtable,
+      nobj,
+      verbose;
+
+    const
+      DEVIRT_SECTION_NAME = 'contextinsensitive_devirtualization';
+
+   { *************************** tinheritancetreenode ************************* }
+    
+    constructor tinheritancetreenode.create(_parent: tinheritancetreenode; _def: tobjectdef; _instantiated: boolean);
+      begin
+        fparent:=_parent;
+        fdef:=_def;
+        finstantiated:=_instantiated;
+      end;
+
+
+    destructor tinheritancetreenode.destroy;
+      begin
+        { fchilds owns its members, so it will free them too }
+        fchilds.free;
+        inherited destroy;
+      end;
+
+
+    function tinheritancetreenode.childcount: longint;
+      begin
+        if assigned(fchilds) then
+          result:=fchilds.count
+        else
+          result:=0;
+      end;
+
+
+    function tinheritancetreenode.haschilds: boolean;
+      begin
+        result:=assigned(fchilds)
+      end;
+
+
+    function tinheritancetreenode.getchild(index: longint): tinheritancetreenode;
+      begin
+        result:=tinheritancetreenode(fchilds[index]);
+      end;
+
+
+    function tinheritancetreenode.maybeaddchild(_def: tobjectdef; _instantiated: boolean): tinheritancetreenode;
+      var
+        i: longint;
+      begin
+        { sanity check }
+        if assigned(_def.childof) then 
+          begin
+            if (_def.childof<>def) then
+              internalerror(2008092201);
+          end
+        else if assigned(fparent) then
+          internalerror(2008092202);
+
+        if not assigned(fchilds) then
+          fchilds:=tfpobjectlist.create(true);
+        { def already a child -> return }
+        for i := 0 to fchilds.count-1 do
+          if (tinheritancetreenode(fchilds[i]).def=_def) then
+            begin
+              result:=tinheritancetreenode(fchilds[i]);
+              result.finstantiated:=result.finstantiated or _instantiated;
+              exit;
+            end;
+        { not found, add new child }
+        result:=tinheritancetreenode.create(self,_def,_instantiated);
+        fchilds.add(result);
+      end;
+
+
+    { *************************** tinheritancetree ************************* }
+
+    constructor tinheritancetree.create;
+      begin
+        froots:=tinheritancetreenode.create(nil,nil,false);
+        classrefdefs:=tfpobjectlist.create(false);
+      end;
+
+
+    destructor tinheritancetree.destroy;
+      begin
+        froots.free;
+        classrefdefs.free;
+        inherited destroy;
+      end;
+      
+
+    function tinheritancetree.registerinstantiatedobjectdefrecursive(def: tobjectdef; instantiated: boolean): tinheritancetreenode;
+      begin
+        if assigned(def.childof) then
+          begin
+            { recursively add parent, of which we have no info about whether or not it is
+              instantiated at this point -> default to false (will be overridden by "true"
+              if necessary)
+            }
+            result:=registerinstantiatedobjectdefrecursive(def.childof,false);
+            { and add ourselves to the parent }
+            result:=result.maybeaddchild(def,instantiated);
+          end
+        else
+          { add ourselves to the roots }
+          result:=froots.maybeaddchild(def,instantiated);
+      end;
+
+
+    procedure tinheritancetree.registerinstantiateddef(def: tdef);
+      begin
+        { add the def }
+        if (def.typ=objectdef) then
+          registerinstantiatedobjectdefrecursive(tobjectdef(def),true)
+        else if (def.typ=classrefdef) then
+          classrefdefs.add(def)
+        else
+          internalerror(2008092401);
+      end;
+
+
+   procedure tinheritancetree.checkforclassrefinheritance(def: tdef);
+     var
+       i: longint;
+     begin
+       if (def.typ=objectdef) then
+         begin
+           for i:=0 to classrefdefs.count-1 do
+             if tobjectdef(def).is_related(tclassrefdef(classrefdefs[i]).pointeddef) then
+               begin
+                 registerinstantiateddef(def);
+                 exit;
+               end;
+         end;
+     end;
+
+
+   procedure tinheritancetree.setinstantiated(node: tinheritancetreenode; arg: pointer);
+      var
+        classrefdef: tclassrefdef absolute arg;
+      begin
+        if not(node.instantiated) then
+          begin
+            node.instantiated:=true;
+            {$IFDEF DEBUG_DEVIRT}
+            writeln('Marked ',node.def.typename,' as instantiated because instantiated ',classrefdef.typename);
+            {$ENDIF}
+          end;
+      end;
+
+
+    procedure tinheritancetree.foreachnodefromroot(root: tinheritancetreenode; proctocall: tinheritancetreecallback; arg: pointer);
+        
+      procedure process(const node: tinheritancetreenode);
+        var
+         i: longint;
+        begin
+          for i:=0 to node.childcount-1 do
+            if node.childs[i].haschilds then
+              begin
+                proctocall(node.childs[i],arg);
+                process(node.childs[i])
+              end
+            else
+              proctocall(node.childs[i],arg);
+        end;
+        
+      begin
+        process(root);
+      end;
+
+
+    procedure tinheritancetree.foreachnode(proctocall: tinheritancetreecallback; arg: pointer);
+      begin
+        foreachnodefromroot(froots,proctocall,arg);
+      end;
+
+
+    procedure tinheritancetree.foreachleafnode(proctocall: tinheritancetreecallback; arg: pointer);
+
+      procedure process(const node: tinheritancetreenode);
+        var
+         i: longint;
+        begin
+          for i:=0 to node.childcount-1 do
+            if node.childs[i].haschilds then
+              process(node.childs[i])
+            else
+              proctocall(node.childs[i],arg);
+        end;
+        
+      begin
+        process(froots);
+      end;
+
+
+    procedure tinheritancetree.markvmethods(node: tinheritancetreenode; p: pointer);
+      var
+        currnode: tinheritancetreenode;
+        vmtbuilder: tvmtbuilder;
+        pd: tobject;
+        i: longint;
+        makeallvirtual: boolean;
+      begin
+        {$IFDEF DEBUG_DEVIRT}
+        writeln('processing leaf node ',node.def.typename);
+        {$ENDIF}
+        { todo: also process interfaces (ImplementedInterfaces) }
+        if not assigned(node.def.vmtentries) then
+          begin
+            vmtbuilder:=tvmtbuilder.create(node.def);
+            vmtbuilder.generate_vmt(false);
+            vmtbuilder.free;
+            { may not have any vmtentries }
+            if not assigned(node.def.vmtentries) then
+              exit;
+          end;
+        { process all vmt entries for this class/object }
+        for i:=0 to node.def.vmtentries.count-1 do
+          begin
+            currnode:=node;
+            pd:=currnode.def.vmtentries[i];
+            { abstract methods cannot be called directly }
+            if (po_abstractmethod in tprocdef(pd).procoptions) then
+              continue;
+            {$IFDEF DEBUG_DEVIRT}
+            writeln('  method ',tprocdef(pd).typename);
+            {$ENDIF}
+            { Now mark all virtual methods static that are the same in parent
+              classes as in this instantiated child class (only instantiated
+              classes can be leaf nodes, since only instantiated classes were
+              added to the tree) as statically callable.
+              If a first child does not override a parent method while a
+              a second one does, the first will mark it as statically
+              callable, but the second will set it to not statically callable.
+              In the opposite situation, the first will mark it as not
+              statically callable and the second will leave it alone.
+            }
+            makeallvirtual:=false;
+            repeat
+              if not assigned(currnode.def.vmtentries) then
+                begin
+                  vmtbuilder:=tvmtbuilder.create(currnode.def);
+                  vmtbuilder.generate_vmt(false);
+                  vmtbuilder.free;
+                  { may not have any vmtentries }
+                  if not assigned(currnode.def.vmtentries) then
+                    break;
+                end;
+              { stop when this method is not yet implemented in a parent }
+              if (currnode.def.vmtentries.count<=i) then
+                break;
+              
+              if not assigned(currnode.def.vmcallstaticinfo) then
+                currnode.def.vmcallstaticinfo:=allocmem(currnode.def.vmtentries.count*sizeof(tvmcallstatic));
+              { same procdef as in all instantiated childs? }
+              if (currnode.def.vmcallstaticinfo^[i] in [vmcs_default,vmcs_yes]) then
+                begin
+                  { methods in uninstantiated classes can be made static if
+                    they are the same in all instantiated derived classes
+                  }
+                  if ((currnode.def.vmtentries[i]=pd) or
+                      (not currnode.instantiated and
+                       (currnode.def.vmcallstaticinfo^[i]=vmcs_default))) and
+                      not makeallvirtual then
+                    begin
+                      {$IFDEF DEBUG_DEVIRT}
+                      writeln('    marking as static for ',currnode.def.typename);
+                      {$ENDIF}
+                      currnode.def.vmcallstaticinfo^[i]:=vmcs_yes;
+                      { this is in case of a non-instantiated parent of an instantiated child:
+                        the method declared in the child will always be called here
+                      }
+                      currnode.def.vmtentries[i]:=pd;
+                    end
+                  else
+                    begin
+                      {$IFDEF DEBUG_DEVIRT}
+                      writeln('    marking as non-static for ',currnode.def.typename);
+                      {$ENDIF}
+                      makeallvirtual:=true;
+                      currnode.def.vmcallstaticinfo^[i]:=vmcs_no;
+                    end;
+                  currnode:=currnode.parent;
+                end
+              else
+                begin
+                  {$IFDEF DEBUG_DEVIRT}
+                  writeln('    not processing parents, already non-static for ',currnode.def.typename);
+                  {$ENDIF}
+                  { parents are also already set to vmcs_no, so no need to continue }
+                  currnode:=nil;
+                end;
+            until not assigned(currnode) or
+                  not assigned(currnode.def);
+          end;
+      end;
+
+
+    procedure tinheritancetree.optimizevirtualmethods;
+      begin
+//        finalisetree;
+        foreachleafnode(@markvmethods,nil);
+      end;
+
+
+    procedure tinheritancetree.printobjectvmtinfo(node: tinheritancetreenode; arg: pointer);
+      var
+        i,
+        totaldevirtualised,
+        totalvirtual: ptrint;
+      begin
+        totaldevirtualised:=0;
+        totalvirtual:=0;
+        writeln(node.def.typename);
+        if not assigned(node.def.vmtentries) then
+          begin
+            writeln('  No virtual methods!');
+            exit;
+          end;
+        for i:=0 to node.def.vmtentries.count-1 do
+          if (po_virtualmethod in tabstractprocdef(node.def.vmtentries[i]).procoptions) then
+            begin
+              inc(totalvirtual);
+              if (node.def.vmcallstaticinfo^[i]=vmcs_yes) then
+                begin
+                  inc(totaldevirtualised);
+                  writeln('  Devirtualised: ',tabstractprocdef(node.def.vmtentries[i]).typename);
+                end;
+            end;
+        writeln('Total devirtualised: ',totaldevirtualised,'/',totalvirtual);
+        writeln;
+      end;
+
+
+    procedure tinheritancetree.printvmtinfo;
+      begin
+        foreachnode(@printobjectvmtinfo,nil);
+      end;
+
+
+    { helper routine: decompose a class/procdef combo into a unitname, class name and vmtentry number }
+
+    procedure defsdecompose(objdef: tobjectdef; procdef: tprocdef; out unitname, classname: pshortstring; out vmtentry: longint);
+      const
+        mainprogname: string[2] = 'P$';
+      var
+        mainsymtab,
+        objparentsymtab: tsymtable;
+      begin
+        objparentsymtab:=objdef.symtable;
+        mainsymtab:=objparentsymtab.defowner.owner;
+        { main symtable must be static or global }
+        if not(mainsymtab.symtabletype in [staticsymtable,globalsymtable]) then
+         internalerror(200204175);
+        if (TSymtable(main_module.localsymtable)=mainsymtab) and
+            (not main_module.is_unit) then
+           { same convention as for mangled names }
+          unitname:=@mainprogname
+        else
+          unitname:=mainsymtab.name;
+        classname:=tobjectdef(objparentsymtab.defowner).objname;
+        vmtentry:=procdef.extnumber;
+        { if it's $ffff, this is not a valid virtual method }
+        if (vmtentry=$ffff) then
+          internalerror(2008100509);
+      end;
+
+
+
+   { tclassdevirtinfo }
+
+    constructor tclassdevirtinfo.create(hashobjectlist:tfphashobjectlist;const n: shortstring);
+      begin
+        inherited create(hashobjectlist,n);
+        fstaticmethodnames:=tfplist.create;
+      end;
+
+    destructor tclassdevirtinfo.destroy;
+      var
+        i: longint;
+      begin
+        for i:=0 to fstaticmethodnames.count-1 do
+          if assigned(fstaticmethodnames[i]) then
+            freemem(fstaticmethodnames[i]);
+        fstaticmethodnames.free;
+        inherited destroy;
+      end;
+
+    procedure tclassdevirtinfo.addstaticmethod(vmtindex: longint;
+      const replacementname: shortstring);
+      begin
+        if (vmtindex>=fstaticmethodnames.count) then
+          fstaticmethodnames.Count:=vmtindex+10;
+        fstaticmethodnames[vmtindex]:=stringdup(replacementname);
+      end;
+
+    function tclassdevirtinfo.isstaticvmtentry(vmtindex: longint; out
+      replacementname: pshortstring): boolean;
+      begin
+         result:=false;
+         if (vmtindex>=fstaticmethodnames.count) then
+           exit;
+
+         replacementname:=fstaticmethodnames[vmtindex];
+         result:=assigned(replacementname);
+      end;
+
+    { tunitdevirtinfo }
+
+    constructor tunitdevirtinfo.create(hashobjectlist:tfphashobjectlist;const n: shortstring);
+      begin
+        inherited create(hashobjectlist,n);
+        fclasses:=tfphashobjectlist.create(true);
+      end;
+
+    destructor tunitdevirtinfo.destroy;
+      begin
+        fclasses.free;
+        inherited destroy;
+      end;
+
+    function tunitdevirtinfo.addclass(const n: shortstring): tclassdevirtinfo;
+      begin
+        result:=findclass(n);
+        { can't have two classes with the same name in a single unit }
+        if assigned(result) then
+          internalerror(2008100501);
+        result:=tclassdevirtinfo.create(fclasses,n);
+      end;
+
+    function tunitdevirtinfo.findclass(const n: shortstring): tclassdevirtinfo;
+      begin
+        result:=tclassdevirtinfo(fclasses.find(n));
+      end;
+
+
+    { tprogdevirtinfo }
+
+    procedure tprogdevirtinfo.converttreenode(node: tinheritancetreenode; arg: pointer);
+      var
+        i,
+        vmtentry: longint;
+        unitid, classid: pshortstring;
+        unitdevirtinfo: tunitdevirtinfo;
+        classdevirtinfo: tclassdevirtinfo;
+        first : boolean;
+      begin
+        if not assigned(node.def.vmtentries) then
+          exit;
+        first:=true;
+        for i:=0 to node.def.vmtentries.count-1 do
+          if (po_virtualmethod in tabstractprocdef(node.def.vmtentries[i]).procoptions) and
+             (node.def.vmcallstaticinfo^[i]=vmcs_yes) then
+            begin
+              if first then
+                begin
+                  { add necessary entries for the unit and the class }
+                  defsdecompose(node.def,tprocdef(node.def.vmtentries[i]),unitid,classid,vmtentry);
+                  unitdevirtinfo:=addunitifnew(unitid^);
+                  classdevirtinfo:=unitdevirtinfo.addclass(classid^);
+                  first:=false;
+                end;
+              { add info about devirtualised vmt entry }
+              classdevirtinfo.addstaticmethod(i,tprocdef(node.def.vmtentries[i]).mangledname);
+            end;
+      end;
+
+    constructor tprogdevirtinfo.create;
+      begin
+        inherited create;
+      end;
+
+    destructor tprogdevirtinfo.destroy;
+      begin
+        funits.free;
+        inherited destroy;
+      end;
+
+    class function tprogdevirtinfo.getwpotype: twpotype;
+      begin
+        result:=wpo_devirtualization_context_insensitive;
+      end;
+
+    class function tprogdevirtinfo.generatesinfoforwposwitches: twpoptimizerswitches;
+      begin
+        result:=[cs_wpo_devirtualize_calls];
+      end;
+
+    class function tprogdevirtinfo.performswpoforswitches: twpoptimizerswitches;
+      begin
+        result:=[cs_wpo_devirtualize_calls];
+      end;
+
+
+    procedure reset_all_impl_defs;
+
+      procedure reset_used_unit_impl_defs(hp:tmodule);
+        var
+          pu : tused_unit;
+        begin
+          pu:=tused_unit(hp.used_units.first);
+          while assigned(pu) do
+            begin
+              if not pu.u.is_reset then
+                begin
+                  { prevent infinte loop for circular dependencies }
+                  pu.u.is_reset:=true;
+                  if assigned(pu.u.localsymtable) then
+                    begin
+                      tstaticsymtable(pu.u.localsymtable).reset_all_defs;
+                      reset_used_unit_impl_defs(pu.u);
+                    end;
+                end;
+              pu:=tused_unit(pu.next);
+            end;
+        end;
+
+      var
+        hp2 : tmodule;
+      begin
+        hp2:=tmodule(loaded_units.first);
+        while assigned(hp2) do
+          begin
+            hp2.is_reset:=false;
+            hp2:=tmodule(hp2.next);
+          end;
+        reset_used_unit_impl_defs(current_module);
+      end;
+
+
+    procedure tprogdevirtinfo.constructfromcompilerstate;
+      var
+        hp: tmodule;
+        i: longint;
+        inheritancetree: tinheritancetree;
+      begin
+         { the compiler already resets all interface defs after every unit
+           compilation, but not the implementation defs (because this is only
+           done for the purpose of writing debug info, and you can never see
+           a type defined in the implementation of one unit in another unit).
+
+           Here, we want to record all classes constructed anywhere in the
+           program, also if those class(refdef) types are defined in the
+           implementation of a unit. So reset the state of all defs in
+           implementation sections before starting the collection process. }
+         reset_all_impl_defs;
+         { register all instantiated class/object types }
+         hp:=tmodule(loaded_units.first);
+         while assigned(hp) do
+          begin
+            if assigned(hp.wpoinfo.createdobjtypes) then
+              for i:=0 to hp.wpoinfo.createdobjtypes.count-1 do
+                tdef(hp.wpoinfo.createdobjtypes[i]).register_created_object_type;
+            hp:=tmodule(hp.next);
+          end;
+         inheritancetree:=tinheritancetree.create;
+{$IFDEF DEBUG_DEVIRT}
+         writeln('constructed object/class/classreftypes in ',current_module.realmodulename^);
+{$ENDIF}
+         for i := 0 to current_module.wpoinfo.createdobjtypes.count-1 do
+           begin
+             inheritancetree.registerinstantiateddef(tdef(current_module.wpoinfo.createdobjtypes[i]));
+{$IFDEF DEBUG_DEVIRT}
+             write('  ',tdef(current_module.wpoinfo.createdobjtypes[i]).GetTypeName);
+{$ENDIF}
+             case tdef(current_module.wpoinfo.createdobjtypes[i]).typ of
+               objectdef:
+                 case tobjectdef(current_module.wpoinfo.createdobjtypes[i]).objecttype of
+                   odt_object:
+{$IFDEF DEBUG_DEVIRT}
+                     writeln(' (object)')
+{$ENDIF}
+                     ;
+                   odt_class:
+{$IFDEF DEBUG_DEVIRT}
+                     writeln(' (class)')
+{$ENDIF}
+                     ;
+                   else
+                     internalerror(2008092101);
+                 end;
+               classrefdef:
+{$IFDEF DEBUG_DEVIRT}
+                 writeln(' (classrefdef)')
+{$ENDIF}
+                 ;
+               else
+                 internalerror(2008092102);
+             end;
+           end;
+         { now add all objectdefs derived from the instantiated
+           classrefdefs to the tree (as they can, in theory, all
+           be instantiated as well)
+         }
+         hp:=tmodule(loaded_units.first);
+         while assigned(hp) do
+          begin
+            { we cannot just walk over the module's deflist, because a bunch of
+              the defs in there don't exist anymore (when destroyed, they're
+              removed from their symtable but not from the module's deflist)
+
+              procedure-local (or class-local) class definitions do not (yet) exist
+            }
+            { globalsymtable (interface), is nil for main program itself }
+            if assigned(hp.globalsymtable) then
+              for i:=0 to hp.globalsymtable.deflist.count-1 do
+                inheritancetree.checkforclassrefinheritance(tdef(hp.globalsymtable.deflist[i]));
+            { staticsymtable (implementation) }
+            if assigned(hp.localsymtable) then
+              for i:=0 to hp.localsymtable.deflist.count-1 do
+                inheritancetree.checkforclassrefinheritance(tdef(hp.localsymtable.deflist[i]));
+            hp:=tmodule(hp.next);
+          end;
+         inheritancetree.optimizevirtualmethods;
+{$ifdef DEBUG_DEVIRT}
+         inheritancetree.printvmtinfo;
+{$endif DEBUG_DEVIRT}
+         inheritancetree.foreachnode(@converttreenode,nil);
+         inheritancetree.free;
+      end;
+
+    function tprogdevirtinfo.addunitifnew(const n: shortstring): tunitdevirtinfo;
+      begin
+        if assigned(funits) then
+          result:=findunit(n)
+        else
+          begin
+            funits:=tfphashobjectlist.create;
+            result:=nil;
+          end;
+        if not assigned(result) then
+          begin
+            result:=tunitdevirtinfo.create(funits,n);
+          end;
+      end;
+
+    function tprogdevirtinfo.findunit(const n: shortstring): tunitdevirtinfo;
+      begin
+        result:=tunitdevirtinfo(funits.find(n));
+      end;
+
+    procedure tprogdevirtinfo.loadfromwpofilesection(reader: twposectionreaderintf);
+      var
+        unitid,
+        classid,
+        vmtentryname: string;
+        vmttype: string[15];
+        vmtentrynrstr: string[7];
+        vmtentry, error: longint;
+        unitdevirtinfo: tunitdevirtinfo;
+        classdevirtinfo: tclassdevirtinfo;
+      begin
+        { format:
+            unit1^
+            class1&
+            basevmt
+            0
+            staticvmtentryforslot0
+            5
+            staticvmtentryforslot5
+            intfvmt1
+            0
+            staticvmtentryforslot0
+
+            class2&
+            basevmt
+            1
+            staticvmtentryforslot1
+
+            unit2^
+            class3&
+            ...
+
+            currently, only basevmt is supported (no interfaces yet)
+        }
+        { could be empty if no classes or so }
+        if not reader.sectiongetnextline(unitid) then
+          exit;
+        repeat
+          if (unitid='') or
+             (unitid[length(unitid)]<>'^') then
+            internalerror(2008100502);
+          { cut off the trailing ^ }
+          setlength(unitid,length(unitid)-1);
+          unitdevirtinfo:=addunitifnew(unitid);
+          { now read classes }
+          if not reader.sectiongetnextline(classid) then
+            internalerror(2008100505);
+          repeat
+            if (classid='') or
+               (classid[length(classid)]<>'&') then
+              internalerror(2008100503);
+            { cut off the trailing & }
+            setlength(classid,length(classid)-1);
+            classdevirtinfo:=unitdevirtinfo.addclass(classid);
+            if not reader.sectiongetnextline(vmttype) then
+              internalerror(2008100506);
+            { interface info is not yet supported }
+            if (vmttype<>'basevmt') then
+              internalerror(2008100507);
+            { read all vmt entries for this class }
+            while reader.sectiongetnextline(vmtentrynrstr) and
+                  (vmtentrynrstr<>'') do
+              begin
+                val(vmtentrynrstr,vmtentry,error);
+                if (error<>0) then
+                  internalerror(2008100504);
+                if not reader.sectiongetnextline(vmtentryname) or
+                   (vmtentryname='') then
+                  internalerror(2008100508);
+                classdevirtinfo.addstaticmethod(vmtentry,vmtentryname);
+              end;
+            { end of section -> exit }
+            if not(reader.sectiongetnextline(classid)) then
+              exit;
+          until (classid='') or
+                (classid[length(classid)]='^');
+          { next unit, or error }
+          unitid:=classid;
+        until false;
+      end;
+
+    procedure tprogdevirtinfo.storewpofilesection(writer: twposectionwriterintf);
+      var
+        unitcount,
+        classcount,
+        vmtentrycount: longint;
+        unitdevirtinfo: tunitdevirtinfo;
+        classdevirtinfo: tclassdevirtinfo;
+      begin
+        if (funits.count=0) then
+          exit;
+        writer.startsection(DEVIRT_SECTION_NAME);
+        for unitcount:=0 to funits.count-1 do
+          begin
+            unitdevirtinfo:=tunitdevirtinfo(funits[unitcount]);
+            writer.sectionputline(unitdevirtinfo.name+'^');
+            for classcount:=0 to unitdevirtinfo.fclasses.count-1 do
+              begin
+                classdevirtinfo:=tclassdevirtinfo(tunitdevirtinfo(funits[unitcount]).fclasses[classcount]);
+                writer.sectionputline(classdevirtinfo.name+'&');
+                writer.sectionputline('basevmt');
+                for vmtentrycount:=0 to classdevirtinfo.fstaticmethodnames.count-1 do
+                  if assigned(classdevirtinfo.fstaticmethodnames[vmtentrycount]) then
+                    begin
+                      writer.sectionputline(tostr(vmtentrycount));
+                      writer.sectionputline(pshortstring(classdevirtinfo.fstaticmethodnames[vmtentrycount])^);
+                    end;
+                writer.sectionputline('');
+              end;
+          end;
+      end;
+
+    function tprogdevirtinfo.staticnameforvirtualmethod(objdef, procdef: tdef; out staticname: string): boolean;
+      var
+        unitid,
+        classid,
+        newname: pshortstring;
+        unitdevirtinfo: tunitdevirtinfo;
+        classdevirtinfo: tclassdevirtinfo;
+        vmtentry: longint;
+      begin
+         { we don't support classrefs yet, nor interfaces }
+         if (objdef.typ<>objectdef) or
+            not(tobjectdef(objdef).objecttype in [odt_class,odt_object]) then
+           begin
+             result:=false;
+             exit;
+           end;
+
+         { get the component names for the class/procdef combo }
+         defsdecompose(tobjectdef(objdef), tprocdef(procdef),unitid,classid,vmtentry);
+
+         { do we have any info for this unit? }
+         unitdevirtinfo:=findunit(unitid^);
+         result:=false;
+         if not assigned(unitdevirtinfo) then
+           exit;
+         { and for this class? }
+         classdevirtinfo:=unitdevirtinfo.findclass(classid^);
+         if not assigned(classdevirtinfo) then
+           exit;
+         { now check whether it can be devirtualised, and if so to what }
+         result:=classdevirtinfo.isstaticvmtentry(vmtentry,newname);
+         if result then
+           staticname:=newname^;
+      end;
+
+initialization
+  twpoinfomanagerbase.registersectionreader(DEVIRT_SECTION_NAME,tprogdevirtinfo);
+end.

+ 1 - 1
compiler/pdecl.pas

@@ -465,7 +465,7 @@ implementation
                        not(df_generic in hdef.defoptions) then
                       begin
                         vmtbuilder:=TVMTBuilder.Create(tobjectdef(hdef));
-                        vmtbuilder.generate_vmt;
+                        vmtbuilder.generate_vmt(true);
                         vmtbuilder.free;
                       end;
                     try_consume_hintdirective(newtype.symoptions);

+ 48 - 3
compiler/pmodules.pas

@@ -38,12 +38,14 @@ implementation
        cutils,cfileutl,cclasses,comphook,
        globals,verbose,fmodule,finput,fppu,
        symconst,symbase,symtype,symdef,symsym,symtable,
+       wpoinfo,
        aasmtai,aasmdata,aasmcpu,aasmbase,
        cgbase,cgobj,
        nbas,ncgutil,
        link,assemble,import,export,gendef,ppu,comprsrc,dbgbase,
        cresstr,procinfo,
        pexports,
+       wpobase,
        scanner,pbase,pexpr,psystem,psub,pdecsub,ptype
 {$ifdef i386}
        { fix me! }
@@ -891,6 +893,9 @@ implementation
 {$ifdef i386}
          gotvarsym : tstaticvarsym;
 {$endif i386}
+{$ifdef debug_devirt}
+         i: longint;
+{$endif debug_devirt}
       begin
          init_procinfo:=nil;
          finalize_procinfo:=nil;
@@ -1029,7 +1034,7 @@ implementation
          current_module.interface_compiled:=true;
 
          { First reload all units depending on our interface, we need to do this
-           in the implementation part to prevent errorneous circular references }
+           in the implementation part to prevent erroneous circular references }
          reload_flagged_units;
 
          { Parse the implementation section }
@@ -1040,7 +1045,7 @@ implementation
 
          parse_only:=false;
 
-         { generates static symbol table }
+         { create static symbol table }
          current_module.localsymtable:=tstaticsymtable.create(current_module.modulename^,current_module.moduleid);
 
 {$ifdef i386}
@@ -1076,6 +1081,9 @@ implementation
          symtablestack.push(current_module.globalsymtable);
          symtablestack.push(current_module.localsymtable);
 
+         { create whole program optimisation information }
+         current_module.wpoinfo:=tunitwpoinfo.create;
+
          if not current_module.interface_only then
            begin
              Message1(parser_u_parsing_implementation,current_module.modulename^);
@@ -1251,6 +1259,30 @@ implementation
             exit;
           end;
 
+{$ifdef debug_devirt}
+         { print out all instantiated class/object types }
+         writeln('constructed object/class/classreftypes in ',current_module.realmodulename^);
+         for i := 0 to current_module.wpoinfo.createdobjtypes.count-1 do
+           begin
+             write('  ',tdef(current_module.wpoinfo.createdobjtypes[i]).GetTypeName);
+             case tdef(current_module.wpoinfo.createdobjtypes[i]).typ of
+               objectdef:
+                 case tobjectdef(current_module.wpoinfo.createdobjtypes[i]).objecttype of
+                   odt_object:
+                     writeln(' (object)');
+                   odt_class:
+                     writeln(' (class)');
+                   else
+                     internalerror(2008092101);
+                 end;
+               classrefdef:
+                 writeln(' (classrefdef)');
+               else
+                 internalerror(2008092102);
+             end;
+           end;
+{$endif debug_devirt}
+
         Message1(unit_u_finished_compiling,current_module.modulename^);
       end;
 
@@ -1642,6 +1674,9 @@ implementation
 
          symtablestack.push(current_module.localsymtable);
 
+         { create whole program optimisation information }
+         current_module.wpoinfo:=tunitwpoinfo.create;
+
          { should we force unit initialization? }
          force_init_final:=tstaticsymtable(current_module.localsymtable).needs_init_final;
          if force_init_final then
@@ -1947,6 +1982,9 @@ implementation
 
          symtablestack.push(current_module.localsymtable);
 
+         { create whole program optimisation information }
+         current_module.wpoinfo:=tunitwpoinfo.create;
+
          { The program intialization needs an alias, so it can be called
            from the bootstrap code.}
          if islibrary then
@@ -2137,7 +2175,10 @@ implementation
          { We might need the symbols info if not using
            the default do_extractsymbolinfo
            which is a dummy function PM }
-         needsymbolinfo:=do_extractsymbolinfo<>@def_extractsymbolinfo;
+         needsymbolinfo:=
+           (do_extractsymbolinfo<>@def_extractsymbolinfo) or
+           ((current_settings.genwpoptimizerswitches*WPOptimizationsNeedingAllUnitInfo)<>[]);
+
          { release all local symtables that are not needed anymore }
          if (not needsymbolinfo) then
            free_localsymtables(current_module.localsymtable);
@@ -2184,8 +2225,12 @@ implementation
                    linker.MakeSharedLibrary
                  else
                    linker.MakeExecutable;
+
+                 { collect all necessary information for whole-program optimization }
+                 wpoinfomanager.extractwpoinfofromprogram;
                end;
 
+
              { Give Fatal with error count for linker errors }
              if (Errorcount>0) and not status.skip_error then
               begin

+ 2 - 1
compiler/ppu.pas

@@ -43,7 +43,7 @@ type
 {$endif Test_Double_checksum}
 
 const
-  CurrentPPUVersion = 92;
+  CurrentPPUVersion = 93;
 
 { buffer sizes }
   maxentrysize = 1024;
@@ -126,6 +126,7 @@ const
   ibnodetree       = 80;
   ibasmsymbols     = 81;
   ibresources      = 82;
+  ibcreatedobjtypes = 83;
 
   ibmainname       = 90;
   { target-specific things }

+ 1 - 1
compiler/ptype.pas

@@ -259,7 +259,7 @@ implementation
                 if (tt.typ=objectdef) then
                   begin
                     vmtbuilder:=TVMTBuilder.Create(tobjectdef(tt));
-                    vmtbuilder.generate_vmt;
+                    vmtbuilder.generate_vmt(true);
                     vmtbuilder.free;
                   end;
               end;

+ 49 - 2
compiler/symdef.pas

@@ -223,6 +223,9 @@ interface
 
        { tobjectdef }
 
+       tvmcallstatic = (vmcs_default, vmcs_yes, vmcs_no);
+       pmvcallstaticinfo = ^tmvcallstaticinfo;
+       tmvcallstaticinfo = array[0..1024*1024-1] of tvmcallstatic;
        tobjectdef = class(tabstractrecorddef)
        public
           dwarf_struct_lab : tasmsymbol;
@@ -235,11 +238,13 @@ interface
           { to be able to have a variable vmt position }
           { and no vmt field for objects without virtuals }
           vmtentries     : TFPObjectList;
+          vmcallstaticinfo : pmvcallstaticinfo;
           vmt_offset     : longint;
-          writing_class_record_dbginfo : boolean;
           objecttype     : tobjecttyp;
           iidguid        : pguid;
           iidstr         : pshortstring;
+          writing_class_record_dbginfo,
+          created_in_current_module     : boolean;
           { store implemented interfaces defs and name mappings }
           ImplementedInterfaces : TFPObjectList;
           constructor create(ot : tobjecttyp;const n : string;c : tobjectdef);
@@ -269,14 +274,19 @@ interface
           procedure set_parent(c : tobjectdef);
           function FindDestructor : tprocdef;
           function implements_any_interfaces: boolean;
+          procedure reset; override;
+          procedure register_created_object_type;override;
        end;
 
        tclassrefdef = class(tabstractpointerdef)
+          created_in_current_module : boolean;
           constructor create(def:tdef);
           constructor ppuload(ppufile:tcompilerppufile);
           procedure ppuwrite(ppufile:tcompilerppufile);override;
-          function GetTypeName:string;override;
+          function  GetTypeName:string;override;
           function  is_publishable : boolean;override;
+          procedure reset; override;
+          procedure register_created_object_type;override;
        end;
 
        tarraydef = class(tstoreddef)
@@ -2032,8 +2042,24 @@ implementation
       begin
          result:=true;
       end;
+      
+      
+    procedure tclassrefdef.reset;
+      begin
+        inherited reset;
+        created_in_current_module:=false;
+      end;
 
 
+    procedure tclassrefdef.register_created_object_type;
+      begin
+        if not created_in_current_module then
+          begin
+            created_in_current_module:=true;
+            current_module.wpoinfo.addcreatedobjtype(self);
+          end;
+      end;
+
 {***************************************************************************
                                    TSETDEF
 ***************************************************************************}
@@ -3782,6 +3808,11 @@ implementation
              vmtentries.free;
              vmtentries:=nil;
            end;
+         if assigned(vmcallstaticinfo) then
+           begin
+             freemem(vmcallstaticinfo);
+             vmcallstaticinfo:=nil;
+           end;
          inherited destroy;
       end;
 
@@ -4187,6 +4218,22 @@ implementation
       end;
 
 
+    procedure tobjectdef.reset;
+      begin
+        inherited reset;
+        created_in_current_module:=false;
+      end;
+
+
+    procedure tobjectdef.register_created_object_type;
+      begin
+        if not created_in_current_module then
+          begin
+            created_in_current_module:=true;
+            current_module.wpoinfo.addcreatedobjtype(self);
+          end;
+      end;
+
 {****************************************************************************
                              TImplementedInterface
 ****************************************************************************}

+ 5 - 0
compiler/symtype.pas

@@ -82,6 +82,7 @@ interface
          function  needs_inittable:boolean;virtual;abstract;
          function  is_related(def:tdef):boolean;virtual;
          procedure ChangeOwner(st:TSymtable);
+         procedure register_created_object_type;virtual;
       end;
 
 {************************************************
@@ -321,6 +322,10 @@ implementation
       end;
 
 
+    procedure tdef.register_created_object_type;
+      begin
+      end;
+
 {****************************************************************************
                           TSYM (base for all symtypes)
 ****************************************************************************}

+ 35 - 0
compiler/wpo.pas

@@ -0,0 +1,35 @@
+{
+    Copyright (c) 2008 by Jonas Maebe
+
+    Collects all whole program optimization plugin untits
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ ****************************************************************************
+}
+
+unit wpo;
+
+{$i fpcdefs.inc}
+
+interface
+
+uses
+  optvirt;
+
+implementation
+
+end.
+

+ 566 - 0
compiler/wpobase.pas

@@ -0,0 +1,566 @@
+{
+    Copyright (c) 2008 by Jonas Maebe
+
+    Whole program optimisation information collection base class
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ ****************************************************************************
+}
+
+unit wpobase;
+
+{$i fpcdefs.inc}
+
+interface
+
+uses
+  globtype,
+  cclasses,
+  symtype;
+
+type
+  { the types of available whole program optimization }
+  twpotype = (wpo_devirtualization_context_insensitive);
+const
+  wpo2str: array[twpotype] of string[16] = ('devirtualization');
+
+type
+  { ************************************************************************* }
+  { ******************** General base classes/interfaces ******************** }
+  { ************************************************************************* }
+
+  { interface to reading a section from a file with wpo info }
+  twposectionreaderintf = interface
+    ['{51BE3F89-C9C5-4965-9C83-AE7490C92E3E}']
+    function sectiongetnextline(out s: string): boolean;
+  end;
+
+
+  { interface to writing sections to a file with wpoinfo }
+  twposectionwriterintf = interface
+    ['{C056F0DD-62B1-4612-86C7-2D39944C4437}']
+    procedure startsection(const name: string);
+    procedure sectionputline(const s: string);
+  end;
+
+
+  { base class for wpo information stores }
+  twpocomponentbase = class
+   public
+    constructor create; reintroduce; virtual;
+
+    { type of whole program optimization information collected/provided by
+      this class
+    }
+    class function getwpotype: twpotype; virtual; abstract;
+
+    { whole program optimizations for which this class generated information }
+    class function generatesinfoforwposwitches: twpoptimizerswitches; virtual; abstract;
+
+    { whole program optimizations performed by this class }
+    class function performswpoforswitches: twpoptimizerswitches; virtual; abstract;
+
+    { loads the information pertinent to this whole program optimization from
+      the current section being processed by reader
+    }
+    procedure loadfromwpofilesection(reader: twposectionreaderintf); virtual; abstract;
+
+    { stores the information of this component to a file in a format that can
+      be loaded again using loadfromwpofilesection()
+    }
+    procedure storewpofilesection(writer: twposectionwriterintf); virtual; abstract;
+
+    { extracts the informations pertinent to this whole program optimization
+      from the current compiler state (loaded units, ...)
+    }
+    procedure constructfromcompilerstate; virtual; abstract;
+  end;
+
+  twpocomponentbaseclass = class of twpocomponentbase;
+
+
+  { forward declaration of overall wpo info manager class }
+
+  twpoinfomanagerbase = class;
+
+  { ************************************************************************* }
+  { ** Information created per unit for use during subsequent compilation *** }
+  { ************************************************************************* }
+
+  { base class of information collected per unit. Still needs to be
+    generalised for different kinds of wpo information, currently specific
+    to devirtualization.
+  }
+
+  tunitwpoinfobase = class
+   protected
+    { created object types }
+    fcreatedobjtypes: tfpobjectlist;
+   public
+    constructor create; reintroduce; virtual;
+    destructor destroy; override;
+
+    property createdobjtypes: tfpobjectlist read fcreatedobjtypes;
+
+    procedure addcreatedobjtype(def: tdef);
+  end;
+
+  { ************************************************************************* }
+  { **** Total information created for use during subsequent compilation **** }
+  { ************************************************************************* }
+
+  { class to create a file with wpo information }
+
+  { tavailablewpofilewriter }
+
+  twpofilewriter = class(tobject,twposectionwriterintf)
+   private
+    { array of class *instances* that wish to be written out to the
+      whole program optimization
+    }
+    fsectioncontents: tfpobjectlist;
+
+    ffilename: tcmdstr;
+    foutputfile: text;
+
+   public
+    constructor create(const fn: tcmdstr);
+    destructor destroy; override;
+
+    procedure writefile;
+
+    { starts a new section with name "name" }
+    procedure startsection(const name: string);
+    { writes s to the wpo file }
+    procedure sectionputline(const s: string);
+
+    procedure registerwpocomponent(component: twpocomponentbase);
+  end;
+
+  { ************************************************************************* }
+  { ************ Information for use during current compilation ************* }
+  { ************************************************************************* }
+
+  { class to read a file with wpo information }
+  twpofilereader = class(tobject,twposectionreaderintf)
+   private
+    ffilename: tcmdstr;
+    flinenr: longint;
+    finputfile: text;
+    fcurline: string;
+    fusecurline: boolean;
+
+    { destination for the read information }
+    fdest: twpoinfomanagerbase;
+
+    function getnextnoncommentline(out s: string): boolean;
+   public
+
+     constructor create(const fn: tcmdstr; dest: twpoinfomanagerbase);
+     destructor destroy; override;
+
+     { processes the wpo info in the file }
+     procedure processfile;
+
+     { returns next line of the current section in s, and false if no more
+       lines in the current section
+     }
+     function sectiongetnextline(out s: string): boolean;
+  end;
+
+
+  { ************************************************************************* }
+  { ******* Specific kinds of whole program optimization components ********* }
+  { ************************************************************************* }
+
+  { method devirtualisation }
+  twpodevirtualisationhandler = class(twpocomponentbase)
+    { checks whether def (a procdef for a virtual method) can be replaced with
+      a static call, and if so returns the mangled name in staticname.
+    }
+    function staticnameforvirtualmethod(objdef, procdef: tdef; out staticname: string): boolean; virtual; abstract;
+  end;
+
+
+  { ************************************************************************* }
+  { ************ Collection of all instances of wpo components ************** }
+  { ************************************************************************* }
+
+  { class doing all the bookkeeping for everything  }
+
+  twpoinfomanagerbase = class
+   private
+    { array of classrefs of handler classes for the various kinds of whole
+      program optimization that we support
+    }
+    fsectionhandlers: tfphashlist; static;
+
+    freader: twpofilereader;
+    fwriter: twpofilewriter;
+   public
+    class procedure registersectionreader(const sectionname: string; sectionhandler: twpocomponentbaseclass);
+    function gethandlerforsection(const secname: string): twpocomponentbaseclass;
+
+    { instances of the various optimizers/information collectors (for
+      information used during this compilation)
+    }
+    wpoinfouse: array[twpotype] of twpocomponentbase;
+
+    procedure extractwpoinfofromprogram;
+
+    procedure setwpoinputfile(const fn: tcmdstr);
+    procedure setwpooutputfile(const fn: tcmdstr);
+    procedure parseandcheckwpoinfo;
+
+    { routines accessing the optimizer information }
+    { 1) devirtualization at the symbol name level }
+    function can_be_devirtualized(objdef, procdef: tdef; out name: shortstring): boolean; virtual; abstract;
+
+    constructor create; reintroduce;
+    destructor destroy; override;
+  end;
+
+
+  var
+    wpoinfomanager: twpoinfomanagerbase;
+
+implementation
+
+  uses
+    globals,
+    cutils,
+    sysutils,
+    symdef,
+    verbose;
+
+
+  { tcreatedwpoinfobase }
+
+  constructor tunitwpoinfobase.create;
+    begin
+      fcreatedobjtypes:=tfpobjectlist.create(false);
+    end;
+
+
+  destructor tunitwpoinfobase.destroy;
+    begin
+      fcreatedobjtypes.free;
+      fcreatedobjtypes:=nil;
+      inherited destroy;
+    end;
+    
+    
+  procedure tunitwpoinfobase.addcreatedobjtype(def: tdef);
+    begin
+      fcreatedobjtypes.add(def);
+    end;
+
+  { twpofilereader }
+
+  function twpofilereader.getnextnoncommentline(out s: string):
+    boolean;
+    begin
+      if (fusecurline) then
+        begin
+          s:=fcurline;
+          fusecurline:=false;
+          exit;
+        end;
+      repeat
+        readln(finputfile,s);
+        if (s='') and
+           eof(finputfile) then
+          begin
+            result:=false;
+            exit;
+          end;
+        inc(flinenr);
+      until (s='') or
+            (s[1]<>'#');
+      result:=true;
+    end;
+
+  constructor twpofilereader.create(const fn: tcmdstr; dest: twpoinfomanagerbase);
+    begin
+      if not FileExists(fn) then
+        begin
+          message1(wpo_cant_find_file,fn);
+          exit;
+        end;
+      assign(finputfile,fn);
+      ffilename:=fn;
+
+      fdest:=dest;
+    end;
+
+  destructor twpofilereader.destroy;
+    begin
+      inherited destroy;
+    end;
+
+  procedure twpofilereader.processfile;
+    var
+      sectionhandler: twpocomponentbaseclass;
+      i: longint;
+      wpotype: twpotype;
+      s,
+      sectionname: string;
+    begin
+      message1(wpo_begin_processing,ffilename);
+      reset(finputfile);
+      flinenr:=0;
+      while getnextnoncommentline(s) do
+        begin
+          if (s='') then
+            continue;
+          { format: "% sectionname" }
+          if (s[1]<>'%') then
+            begin
+              message2(wpo_expected_section,tostr(flinenr),s);
+              break;
+            end;
+          for i:=2 to length(s) do
+            if (s[i]<>' ') then
+              break;
+          sectionname:=copy(s,i,255);
+
+          { find handler for section and process }
+          sectionhandler:=fdest.gethandlerforsection(sectionname);
+          if assigned(sectionhandler) then
+            begin
+              wpotype:=sectionhandler.getwpotype;
+              message2(wpo_found_section,sectionname,wpo2str[wpotype]);
+              { do we need this information? }
+              if ((sectionhandler.performswpoforswitches * init_settings.dowpoptimizerswitches) <> []) then
+                begin
+                  { did some other section already generate this type of information? }
+                  if assigned(fdest.wpoinfouse[wpotype]) then
+                    begin
+                      message2(wpo_duplicate_wpotype,wpo2str[wpotype],sectionname);
+                      fdest.wpoinfouse[wpotype].free;
+                    end;
+                  { process the section }
+                  fdest.wpoinfouse[wpotype]:=sectionhandler.create;
+                  twpocomponentbase(fdest.wpoinfouse[wpotype]).loadfromwpofilesection(self);
+                end
+              else
+                message1(wpo_skipping_unnecessary_section,sectionname);
+              break;
+            end
+          else
+            begin
+              message1(wpo_no_section_handler,sectionname);
+              { skip the current section }
+              while sectiongetnextline(s) do
+                ;
+            end;
+        end;
+      close(finputfile);
+      message1(wpo_end_processing,ffilename);
+    end;
+
+  function twpofilereader.sectiongetnextline(out s: string): boolean;
+    begin
+      result:=getnextnoncommentline(s);
+      if not result then
+        exit;
+      { start of new section? }
+      if (s<>'') and
+         (s[1]='%') then
+        begin
+          { keep read line for next call to getnextnoncommentline() }
+          fcurline:=s;
+          fusecurline:=true;
+          result:=false;
+        end;
+    end;
+
+
+  { twpocomponentbase }
+
+  constructor twpocomponentbase.create;
+    begin
+      { do nothing }
+    end;
+
+  { twpofilewriter }
+
+  constructor twpofilewriter.create(const fn: tcmdstr);
+    begin
+      assign(foutputfile,fn);
+      ffilename:=fn;
+      fsectioncontents:=tfpobjectlist.create(true);
+    end;
+
+  destructor twpofilewriter.destroy;
+    begin
+      fsectioncontents.free;
+      inherited destroy;
+    end;
+
+  procedure twpofilewriter.writefile;
+    var
+      i: longint;
+    begin
+      rewrite(foutputfile);
+      for i:=0 to fsectioncontents.count-1 do
+        twpocomponentbase(fsectioncontents[i]).storewpofilesection(self);
+      close(foutputfile);
+    end;
+
+  procedure twpofilewriter.startsection(const name: string);
+    begin
+      writeln(foutputfile,'% ',name);
+    end;
+
+  procedure twpofilewriter.sectionputline(const s: string);
+    begin
+      writeln(foutputfile,s);
+    end;
+
+  procedure twpofilewriter.registerwpocomponent(
+    component: twpocomponentbase);
+    begin
+      fsectioncontents.add(component);
+    end;
+
+{ twpoinfomanagerbase }
+
+  class procedure twpoinfomanagerbase.registersectionreader(const sectionname: string; sectionhandler: twpocomponentbaseclass);
+    begin
+      { avoid having to check all the time whether it's assigned or not }
+      if not assigned(fsectionhandlers) then
+        fsectionhandlers:=tfphashlist.create;
+      fsectionhandlers.add(sectionname,sectionhandler);
+    end;
+
+
+  function twpoinfomanagerbase.gethandlerforsection(const secname: string
+      ): twpocomponentbaseclass;
+    begin
+      result:=twpocomponentbaseclass(fsectionhandlers.find(secname));
+    end;
+
+  procedure twpoinfomanagerbase.setwpoinputfile(const fn: tcmdstr);
+    begin
+      freader:=twpofilereader.create(fn,self);
+    end;
+
+  procedure twpoinfomanagerbase.setwpooutputfile(const fn: tcmdstr);
+    begin
+      fwriter:=twpofilewriter.create(fn);
+    end;
+
+  procedure twpoinfomanagerbase.parseandcheckwpoinfo;
+    begin
+      { error if we don't have to optimize yet have an input feedback file }
+      if (init_settings.dowpoptimizerswitches=[]) and
+         assigned(freader) then
+        begin
+          message(wpo_input_without_info_use);
+          exit;
+        end;
+
+      { error if we have to optimize yet don't have an input feedback file }
+      if (init_settings.dowpoptimizerswitches<>[]) and
+         not assigned(freader) then
+        begin
+          message(wpo_no_input_specified);
+          exit;
+        end;
+
+      { if we have to generate wpo information, check that a file has been
+        specified and that we have something to write to it
+      }
+      if (init_settings.genwpoptimizerswitches<>[]) and
+         not assigned(fwriter) then
+        begin
+          message(wpo_no_output_specified);
+          exit;
+        end;
+
+      if (init_settings.genwpoptimizerswitches=[]) and
+         assigned(fwriter) then
+        begin
+          message(wpo_output_without_info_gen);
+          exit;
+        end;
+
+      { now read the input feedback file }
+      if assigned(freader) then
+        begin
+          freader.processfile;
+          freader.free;
+          freader:=nil;
+        end;
+
+      { and for each specified optimization check whether the input feedback
+        file contained the necessary information
+      }
+      if (cs_wpo_devirtualize_calls in init_settings.dowpoptimizerswitches) and
+         not assigned(wpoinfouse[wpo_devirtualization_context_insensitive]) then
+        begin
+          message1(wpo_not_enough_info,wpo2str[wpo_devirtualization_context_insensitive]);
+          exit;
+        end;
+
+    end;
+
+  procedure twpoinfomanagerbase.extractwpoinfofromprogram;
+    var
+      i: longint;
+      info: twpocomponentbase;
+    begin
+      { if don't have to write anything, fwriter has not been created }
+      if not assigned(fwriter) then
+        exit;
+
+      { let all wpo components gather the necessary info from the compiler state }
+      for i:=0 to fsectionhandlers.count-1 do
+        if (twpocomponentbaseclass(fsectionhandlers[i]).generatesinfoforwposwitches*current_settings.genwpoptimizerswitches)<>[] then
+          begin
+            info:=twpocomponentbaseclass(fsectionhandlers[i]).create;
+            info.constructfromcompilerstate;
+            fwriter.registerwpocomponent(info);
+          end;
+      { and write their info to disk }
+      fwriter.writefile;
+      fwriter.free;
+      fwriter:=nil;
+    end;
+
+  constructor twpoinfomanagerbase.create;
+    begin
+      inherited create;
+    end;
+
+  destructor twpoinfomanagerbase.destroy;
+    var
+      i: twpotype;
+    begin
+      freader.free;
+      freader:=nil;
+      fwriter.free;
+      fwriter:=nil;
+      for i:=low(wpoinfouse) to high(wpoinfouse) do
+        if assigned(wpoinfouse[i]) then
+          wpoinfouse[i].free;
+      inherited destroy;
+    end;
+
+finalization
+  twpoinfomanagerbase.fsectionhandlers.free;
+  twpoinfomanagerbase.fsectionhandlers:=nil;
+end.

+ 188 - 0
compiler/wpoinfo.pas

@@ -0,0 +1,188 @@
+{
+    Copyright (c) 2008 by Jonas Maebe
+
+    Whole program optimisation information collection
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ ****************************************************************************
+}
+
+unit wpoinfo;
+
+{$i fpcdefs.inc}
+
+interface
+
+uses
+  cclasses,
+  symtype,
+  wpobase,
+  ppu;
+
+type
+  pderefarray = ^tderefarray;
+  tderefarray = array[0..1024*1024-1] of tderef;
+
+  tunitwpoinfo = class(tunitwpoinfobase)
+   { devirtualisation information -- begin }
+   private
+    fcreatedobjtypesderefs: pderefarray;
+   { devirtualisation information -- end }
+
+   public
+
+    destructor destroy; override;
+
+    procedure ppuwrite(ppufile:tcompilerppufile);
+    constructor ppuload(ppufile:tcompilerppufile);
+
+    procedure deref;
+    procedure derefimpl;
+    procedure buildderef;
+    procedure buildderefimpl;
+  end;
+
+
+  { twpoinfomanager }
+
+  twpoinfomanager = class(twpoinfomanagerbase)
+    function can_be_devirtualized(objdef, procdef: tdef; out name: shortstring): boolean; override;
+  end;
+
+
+  procedure InitWpo;
+  procedure DoneWpo;
+
+implementation
+
+  uses
+    globtype,
+    globals,
+    symdef,
+    verbose;
+
+
+  destructor tunitwpoinfo.destroy;
+    begin
+      if assigned(fcreatedobjtypesderefs) then
+        begin
+          freemem(fcreatedobjtypesderefs);
+          fcreatedobjtypesderefs:=nil;
+        end;
+      if assigned(fcreatedobjtypes) then
+        begin
+          fcreatedobjtypes.free;
+          fcreatedobjtypes:=nil;
+        end;
+      inherited destroy;
+    end;
+    
+    
+  procedure tunitwpoinfo.ppuwrite(ppufile:tcompilerppufile);
+    var
+      i: longint;
+    begin
+      { write the number of instantiated object types in this module,
+        followed by the derefs of those types
+      }
+      ppufile.putlongint(fcreatedobjtypes.count);
+      for i:=0 to fcreatedobjtypes.count-1 do
+        ppufile.putderef(fcreatedobjtypesderefs^[i]);
+      ppufile.writeentry(ibcreatedobjtypes);
+      freemem(fcreatedobjtypesderefs);
+      fcreatedobjtypesderefs:=nil;
+    end;
+
+
+  constructor tunitwpoinfo.ppuload(ppufile:tcompilerppufile);
+    var
+      i, len: longint;
+    begin
+      { load start of definition section, which holds the amount of defs }
+      if ppufile.readentry<>ibcreatedobjtypes then
+        Message(unit_f_ppu_read_error);
+      len:=ppufile.getlongint;
+      fcreatedobjtypes:=tfpobjectlist.create(false);
+      fcreatedobjtypes.count:=len;
+      getmem(fcreatedobjtypesderefs,len*sizeof(tderef));
+      for i:=0 to len-1 do
+        ppufile.getderef(fcreatedobjtypesderefs^[i]);
+    end;
+
+
+  procedure tunitwpoinfo.buildderef;
+    var
+      i: longint;
+    begin
+      getmem(fcreatedobjtypesderefs,fcreatedobjtypes.count*sizeof(tderef));
+      for i:=0 to fcreatedobjtypes.count-1 do
+        fcreatedobjtypesderefs^[i].build(fcreatedobjtypes[i]);
+    end;
+
+
+  procedure tunitwpoinfo.buildderefimpl;
+    begin
+    end;
+
+
+  procedure tunitwpoinfo.deref;
+    var
+      i: longint;
+    begin
+      for i:=0 to fcreatedobjtypes.count-1 do
+        fcreatedobjtypes[i]:=fcreatedobjtypesderefs^[i].resolve;
+      freemem(fcreatedobjtypesderefs);
+      fcreatedobjtypesderefs:=nil;
+    end;
+
+
+  procedure tunitwpoinfo.derefimpl;
+    begin
+    end;
+
+
+  { twpoinfomanager }
+
+  function twpoinfomanager.can_be_devirtualized(objdef, procdef: tdef; out name: shortstring): boolean;
+    begin
+      if not assigned(wpoinfouse[wpo_devirtualization_context_insensitive]) or
+         not(cs_wpo_devirtualize_calls in current_settings.dowpoptimizerswitches) then
+        begin
+          result:=false;
+          exit;
+        end;
+      result:=twpodevirtualisationhandler(wpoinfouse[wpo_devirtualization_context_insensitive]).staticnameforvirtualmethod(objdef,procdef,name);
+    end;
+
+
+  procedure InitWpo;
+    begin
+      { always create so we don't have to litter the source with if-tests }
+      wpoinfomanager:=twpoinfomanager.create;
+      if (wpofeedbackinput<>'') then
+        wpoinfomanager.setwpoinputfile(wpofeedbackinput);
+      if (wpofeedbackoutput<>'') then
+        wpoinfomanager.setwpooutputfile(wpofeedbackoutput);
+      wpoinfomanager.parseandcheckwpoinfo;
+    end;
+
+
+  procedure DoneWpo;
+    begin
+      wpoinfomanager.free;
+      wpoinfomanager:=nil;
+    end;
+
+end.