Browse Source

+ keep track of called virtual methods per unit. -Owoptvtms will now replace
vmt entries of virtual methods that can never be called with references
to FPC_ABSTRACTERROR. Some virtual methods are always considered to be
reachable: published methods, and methods used as getter/setter for a
published property.

git-svn-id: trunk@13238 -

Jonas Maebe 16 years ago
parent
commit
41acad1d11
10 changed files with 509 additions and 26 deletions
  1. 1 0
      .gitattributes
  2. 123 0
      compiler/cclasses.pas
  3. 12 1
      compiler/ncgld.pas
  4. 8 1
      compiler/ncgrtti.pas
  5. 119 22
      compiler/optvirt.pas
  6. 1 1
      compiler/ppu.pas
  7. 11 1
      compiler/symdef.pas
  8. 119 0
      compiler/wpobase.pas
  9. 41 0
      compiler/wpoinfo.pas
  10. 74 0
      tests/test/opt/twpo7.pp

+ 1 - 0
.gitattributes

@@ -8009,6 +8009,7 @@ tests/test/opt/twpo2.pp svneol=native#text/plain
 tests/test/opt/twpo3.pp svneol=native#text/plain
 tests/test/opt/twpo3.pp svneol=native#text/plain
 tests/test/opt/twpo4.pp svneol=native#text/plain
 tests/test/opt/twpo4.pp svneol=native#text/plain
 tests/test/opt/twpo5.pp svneol=native#text/plain
 tests/test/opt/twpo5.pp svneol=native#text/plain
+tests/test/opt/twpo7.pp svneol=native#text/plain
 tests/test/opt/uwpo2.pp svneol=native#text/plain
 tests/test/opt/uwpo2.pp svneol=native#text/plain
 tests/test/packages/fcl-base/tascii85.pp svneol=native#text/plain
 tests/test/packages/fcl-base/tascii85.pp svneol=native#text/plain
 tests/test/packages/fcl-base/tgettext1.pp svneol=native#text/plain
 tests/test/packages/fcl-base/tgettext1.pp svneol=native#text/plain

+ 123 - 0
compiler/cclasses.pas

@@ -504,6 +504,35 @@ type
       end;
       end;
 
 
 
 
+{******************************************************************
+                             tbitset
+*******************************************************************}
+
+       tbitset = class
+       private
+         fdata: pbyte;
+         fdatasize: longint;
+       public
+         constructor create(initsize: longint);
+         constructor create_bytesize(bytesize: longint);
+         destructor destroy; override;
+         procedure clear;
+         procedure grow(nsize: longint);
+         { sets a bit }
+         procedure include(index: longint);
+         { clears a bit }
+         procedure exclude(index: longint);
+         { finds an entry, creates one if not exists }
+         function isset(index: longint): boolean;
+
+         procedure addset(aset: tbitset);
+         procedure subset(aset: tbitset);
+
+         property data: pbyte read fdata;
+         property datasize: longint read fdatasize;
+      end;
+
+
     function FPHash(const s:shortstring):LongWord;
     function FPHash(const s:shortstring):LongWord;
     function FPHash(P: PChar; Len: Integer): LongWord;
     function FPHash(P: PChar; Len: Integer): LongWord;
 
 
@@ -2757,4 +2786,98 @@ end;
         Result := False;
         Result := False;
       end;
       end;
 
 
+
+{****************************************************************************
+                                tbitset
+****************************************************************************}
+
+    constructor tbitset.create(initsize: longint);
+      begin
+        create_bytesize((initsize+7) div 8);
+      end;
+
+
+    constructor tbitset.create_bytesize(bytesize: longint);
+      begin
+        fdatasize:=bytesize;
+        getmem(fdata,fdataSize);
+        clear;
+      end;
+
+
+    destructor tbitset.destroy;
+      begin
+        freemem(fdata,fdatasize);
+        inherited destroy;
+      end;
+
+
+    procedure tbitset.clear;
+      begin
+        fillchar(fdata^,fdatasize,0);
+      end;
+
+
+    procedure tbitset.grow(nsize: longint);
+      begin
+        reallocmem(fdata,nsize);
+        fillchar(fdata[fdatasize],nsize-fdatasize,0);
+        fdatasize:=nsize;
+      end;
+
+
+    procedure tbitset.include(index: longint);
+      var
+        dataindex: longint;
+      begin
+        { don't use bitpacked array, not endian-safe }
+        dataindex:=index shr 3;
+        if (dataindex>=datasize) then
+          grow(dataindex);
+        fdata[dataindex]:=fdata[dataindex] or (1 shl (index and 7));
+      end;
+
+
+    procedure tbitset.exclude(index: longint);
+      var
+        dataindex: longint;
+      begin
+        dataindex:=index shr 3;
+        if (dataindex>=datasize) then
+          exit;
+        fdata[dataindex]:=fdata[dataindex] and not(1 shl (index and 7));
+      end;
+
+
+    function tbitset.isset(index: longint): boolean;
+      var
+        dataindex: longint;
+      begin
+        dataindex:=index shr 3;
+        result:=
+          (dataindex<datasize) and
+          (((fdata[index shr 3] shr (index and 7)) and 1)<>0);
+      end;
+
+
+    procedure tbitset.addset(aset: tbitset);
+      var
+        i: longint;
+      begin
+        if (aset.datasize>datasize) then
+          grow(aset.datasize);
+        for i:=0 to aset.datasize-1 do
+          fdata[i]:=fdata[i] or aset.data[i];
+      end;
+
+
+    procedure tbitset.subset(aset: tbitset);
+      var
+        i: longint;
+      begin
+        for i:=0 to min(datasize,aset.datasize)-1 do
+          fdata[i]:=fdata[i] and not(aset.data[i]);
+      end;
+
+
 end.
 end.

+ 12 - 1
compiler/ncgld.pas

@@ -64,7 +64,8 @@ implementation
       cpubase,parabase,
       cpubase,parabase,
       tgobj,ncgutil,
       tgobj,ncgutil,
       cgobj,
       cgobj,
-      ncgbas,ncgflw;
+      ncgbas,ncgflw,
+      wpobase;
 
 
 {*****************************************************************************
 {*****************************************************************************
                    SSA (for memory temps) support
                    SSA (for memory temps) support
@@ -481,6 +482,16 @@ implementation
                      if (po_virtualmethod in procdef.procoptions) and
                      if (po_virtualmethod in procdef.procoptions) and
                         not(nf_inherited in flags) then
                         not(nf_inherited in flags) then
                        begin
                        begin
+                         if (not assigned(current_procinfo) or
+                             wpoinfomanager.symbol_live(current_procinfo.procdef.mangledname)) then
+                           procdef._class.register_vmt_call(procdef.extnumber);
+            {$ifdef vtentry}
+                         if not is_interface(procdef._class) then
+                           begin
+                             inc(current_asmdata.NextVTEntryNr);
+                             current_asmdata.CurrAsmList.Concat(tai_symbol.CreateName('VTREF'+tostr(current_asmdata.NextVTEntryNr)+'_'+procdef._class.vmt_mangledname+'$$'+tostr(vmtoffset div sizeof(pint)),AT_FUNCTION,0));
+                           end;
+            {$endif vtentry}
                          { a classrefdef already points to the VMT }
                          { a classrefdef already points to the VMT }
                          if (left.resultdef.typ<>classrefdef) then
                          if (left.resultdef.typ<>classrefdef) then
                            begin
                            begin

+ 8 - 1
compiler/ncgrtti.pas

@@ -67,7 +67,8 @@ implementation
        fmodule,
        fmodule,
        symsym,
        symsym,
        aasmtai,aasmdata,
        aasmtai,aasmdata,
-       defutil
+       defutil,
+       wpobase
        ;
        ;
 
 
 
 
@@ -311,6 +312,12 @@ implementation
                      { virtual method, write vmt offset }
                      { virtual method, write vmt offset }
                      current_asmdata.asmlists[al_rtti].concat(Tai_const.create(aitconst_ptr,
                      current_asmdata.asmlists[al_rtti].concat(Tai_const.create(aitconst_ptr,
                        tprocdef(propaccesslist.procdef)._class.vmtmethodoffset(tprocdef(propaccesslist.procdef).extnumber)));
                        tprocdef(propaccesslist.procdef)._class.vmtmethodoffset(tprocdef(propaccesslist.procdef).extnumber)));
+                     { register for wpo }
+                     tprocdef(propaccesslist.procdef)._class.register_vmt_call(tprocdef(propaccesslist.procdef).extnumber);
+                     {$ifdef vtentry}
+                     { not sure if we can insert those vtentry symbols safely here }
+                     {$error register methods used for published properties}
+                     {$endif vtentry}
                      typvalue:=2;
                      typvalue:=2;
                   end;
                   end;
              end;
              end;

+ 119 - 22
compiler/optvirt.pas

@@ -40,6 +40,7 @@ unit optvirt;
         fdef: tobjectdef;
         fdef: tobjectdef;
         fparent: tinheritancetreenode;
         fparent: tinheritancetreenode;
         fchilds: tfpobjectlist;
         fchilds: tfpobjectlist;
+        fcalledvmtmethods: tbitset;
         finstantiated: boolean;
         finstantiated: boolean;
 
 
         function getchild(index: longint): tinheritancetreenode;
         function getchild(index: longint): tinheritancetreenode;
@@ -57,6 +58,7 @@ unit optvirt;
           this def (either new or existing one
           this def (either new or existing one
         }
         }
         function  maybeaddchild(_def: tobjectdef; _instantiated: boolean): tinheritancetreenode;
         function  maybeaddchild(_def: tobjectdef; _instantiated: boolean): tinheritancetreenode;
+        function  findchild(_def: tobjectdef): tinheritancetreenode;
       end;
       end;
 
 
 
 
@@ -73,6 +75,9 @@ unit optvirt;
         function registerinstantiatedobjectdefrecursive(def: tobjectdef; instantiated: boolean): tinheritancetreenode;
         function registerinstantiatedobjectdefrecursive(def: tobjectdef; instantiated: boolean): tinheritancetreenode;
         procedure markvmethods(node: tinheritancetreenode; p: pointer);
         procedure markvmethods(node: tinheritancetreenode; p: pointer);
         procedure printobjectvmtinfo(node: tinheritancetreenode; arg: pointer);
         procedure printobjectvmtinfo(node: tinheritancetreenode; arg: pointer);
+        procedure addcalledvmtentries(node: tinheritancetreenode; arg: pointer);
+
+        function  getnodefordef(def: tobjectdef): tinheritancetreenode;
        public
        public
         constructor create;
         constructor create;
         destructor destroy; override;
         destructor destroy; override;
@@ -81,6 +86,7 @@ unit optvirt;
         }
         }
         procedure registerinstantiatedobjdef(def: tdef);
         procedure registerinstantiatedobjdef(def: tdef);
         procedure registerinstantiatedclassrefdef(def: tdef);
         procedure registerinstantiatedclassrefdef(def: tdef);
+        procedure registercalledvmtentries(entries: tcalledvmtentries);
         procedure checkforclassrefinheritance(def: tdef);
         procedure checkforclassrefinheritance(def: tdef);
         procedure foreachnode(proctocall: tinheritancetreecallback; arg: pointer);
         procedure foreachnode(proctocall: tinheritancetreecallback; arg: pointer);
         procedure foreachleafnode(proctocall: tinheritancetreecallback; arg: pointer);
         procedure foreachleafnode(proctocall: tinheritancetreecallback; arg: pointer);
@@ -178,6 +184,8 @@ unit optvirt;
         fparent:=_parent;
         fparent:=_parent;
         fdef:=_def;
         fdef:=_def;
         finstantiated:=_instantiated;
         finstantiated:=_instantiated;
+        if assigned(_def) then
+          fcalledvmtmethods:=tbitset.create(_def.vmtentries.count);
       end;
       end;
 
 
 
 
@@ -185,6 +193,7 @@ unit optvirt;
       begin
       begin
         { fchilds owns its members, so it will free them too }
         { fchilds owns its members, so it will free them too }
         fchilds.free;
         fchilds.free;
+        fcalledvmtmethods.free;
         inherited destroy;
         inherited destroy;
       end;
       end;
 
 
@@ -211,8 +220,6 @@ unit optvirt;
 
 
 
 
     function tinheritancetreenode.maybeaddchild(_def: tobjectdef; _instantiated: boolean): tinheritancetreenode;
     function tinheritancetreenode.maybeaddchild(_def: tobjectdef; _instantiated: boolean): tinheritancetreenode;
-      var
-        i: longint;
       begin
       begin
         { sanity check }
         { sanity check }
         if assigned(_def.childof) then 
         if assigned(_def.childof) then 
@@ -226,19 +233,32 @@ unit optvirt;
         if not assigned(fchilds) then
         if not assigned(fchilds) then
           fchilds:=tfpobjectlist.create(true);
           fchilds:=tfpobjectlist.create(true);
         { def already a child -> return }
         { 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);
+        result:=findchild(_def);
+        if assigned(result) then
+          result.finstantiated:=result.finstantiated or _instantiated
+        else
+          begin
+            { not found, add new child }
+            result:=tinheritancetreenode.create(self,_def,_instantiated);
+            fchilds.add(result);
+          end;
       end;
       end;
 
 
 
 
+    function tinheritancetreenode.findchild(_def: tobjectdef): tinheritancetreenode;
+      var
+        i: longint;
+      begin
+        result:=nil;
+        if assigned(fchilds) then
+          for i := 0 to fchilds.count-1 do
+            if (tinheritancetreenode(fchilds[i]).def=_def) then
+              begin
+                result:=tinheritancetreenode(fchilds[i]);
+                break;
+              end;
+      end;
+
     { *************************** tinheritancetree ************************* }
     { *************************** tinheritancetree ************************* }
 
 
     constructor tinheritancetree.create;
     constructor tinheritancetree.create;
@@ -296,6 +316,37 @@ unit optvirt;
       end;
       end;
 
 
 
 
+    function tinheritancetree.getnodefordef(def: tobjectdef): tinheritancetreenode;
+      begin
+        if assigned(def.childof) then
+          begin
+            result:=getnodefordef(def.childof);
+            if assigned(result) then
+              result:=result.findchild(def);
+          end
+        else
+          result:=froots.findchild(def);
+      end;
+
+
+    procedure tinheritancetree.registercalledvmtentries(entries: tcalledvmtentries);
+      var
+        node: tinheritancetreenode;
+      begin
+        node:=getnodefordef(tobjectdef(entries.objdef));
+        { it's possible that no instance of this class or its descendants are
+          instantiated
+        }
+        if not assigned(node) then
+          exit;
+        { now mark these methods as (potentially) called for this type and for
+          all of its descendants
+        }
+        addcalledvmtentries(node,entries.calledentries);
+        foreachnodefromroot(node,@addcalledvmtentries,entries.calledentries);
+      end;
+
+
    procedure tinheritancetree.checkforclassrefinheritance(def: tdef);
    procedure tinheritancetree.checkforclassrefinheritance(def: tdef);
      var
      var
        i: longint;
        i: longint;
@@ -408,8 +459,19 @@ unit optvirt;
               
               
               if not assigned(currnode.def.vmcallstaticinfo) then
               if not assigned(currnode.def.vmcallstaticinfo) then
                 currnode.def.vmcallstaticinfo:=allocmem(currnode.def.vmtentries.count*sizeof(tvmcallstatic));
                 currnode.def.vmcallstaticinfo:=allocmem(currnode.def.vmtentries.count*sizeof(tvmcallstatic));
+              { if this method cannot be called, we can just mark it as
+                unreachable. This will cause its static name to be set to
+                FPC_ABSTRACTERROR later on. Exception: published methods are
+                always reachable (via RTTI).
+              }
+              if (pd.visibility<>vis_published) and
+                 not(currnode.fcalledvmtmethods.isset(i)) then
+                begin
+                  currnode.def.vmcallstaticinfo^[i]:=vmcs_unreachable;
+                  currnode:=currnode.parent;
+                end
               { same procdef as in all instantiated childs? (yes or don't know) }
               { same procdef as in all instantiated childs? (yes or don't know) }
-              if (currnode.def.vmcallstaticinfo^[i] in [vmcs_default,vmcs_yes]) then
+              else if (currnode.def.vmcallstaticinfo^[i] in [vmcs_default,vmcs_yes]) then
                 begin
                 begin
                   { methods in uninstantiated classes can be made static if
                   { methods in uninstantiated classes can be made static if
                     they are the same in all instantiated derived classes
                     they are the same in all instantiated derived classes
@@ -439,14 +501,16 @@ unit optvirt;
                     end;
                     end;
                   currnode:=currnode.parent;
                   currnode:=currnode.parent;
                 end
                 end
-              else
+              else if (currnode.def.vmcallstaticinfo^[i]=vmcs_no) then
                 begin
                 begin
                   {$IFDEF DEBUG_DEVIRT}
                   {$IFDEF DEBUG_DEVIRT}
                   writeln('    not processing parents, already non-static for ',currnode.def.typename);
                   writeln('    not processing parents, already non-static for ',currnode.def.typename);
                   {$ENDIF}
                   {$ENDIF}
                   { parents are already set to vmcs_no, so no need to continue }
                   { parents are already set to vmcs_no, so no need to continue }
                   currnode:=nil;
                   currnode:=nil;
-                end;
+                end
+              else
+                currnode:=currnode.parent;
             until not assigned(currnode) or
             until not assigned(currnode) or
                   not assigned(currnode.def);
                   not assigned(currnode.def);
           end;
           end;
@@ -463,10 +527,12 @@ unit optvirt;
       var
       var
         i,
         i,
         totaldevirtualised,
         totaldevirtualised,
-        totalvirtual: ptrint;
+        totalvirtual,
+        totalunreachable: ptrint;
       begin
       begin
         totaldevirtualised:=0;
         totaldevirtualised:=0;
         totalvirtual:=0;
         totalvirtual:=0;
+        totalunreachable:=0;
         writeln(node.def.typename);
         writeln(node.def.typename);
         if (node.def.vmtentries.count=0) then
         if (node.def.vmtentries.count=0) then
           begin
           begin
@@ -481,13 +547,26 @@ unit optvirt;
                 begin
                 begin
                   inc(totaldevirtualised);
                   inc(totaldevirtualised);
                   writeln('  Devirtualised: ',pvmtentry(node.def.vmtentries[i])^.procdef.typename);
                   writeln('  Devirtualised: ',pvmtentry(node.def.vmtentries[i])^.procdef.typename);
+                end
+              else if (node.def.vmcallstaticinfo^[i]=vmcs_unreachable) then
+                begin
+                  inc(totalunreachable);
+                  writeln('   Unreachable: ',pvmtentry(node.def.vmtentries[i])^.procdef.typename);
                 end;
                 end;
             end;
             end;
-        writeln('Total devirtualised: ',totaldevirtualised,'/',totalvirtual);
+        writeln('Total devirtualised/unreachable/all: ',totaldevirtualised,'/',totalunreachable,'/',totalvirtual);
         writeln;
         writeln;
       end;
       end;
 
 
 
 
+    procedure tinheritancetree.addcalledvmtentries(node: tinheritancetreenode; arg: pointer);
+      var
+        vmtentries: tbitset absolute arg;
+      begin
+        node.fcalledvmtmethods.addset(vmtentries);
+      end;
+
+
     procedure tinheritancetree.printvmtinfo;
     procedure tinheritancetree.printvmtinfo;
       begin
       begin
         foreachnode(@printobjectvmtinfo,nil);
         foreachnode(@printobjectvmtinfo,nil);
@@ -622,11 +701,18 @@ unit optvirt;
         if (node.def.vmtentries.count=0) then
         if (node.def.vmtentries.count=0) then
           exit;
           exit;
         for i:=0 to node.def.vmtentries.count-1 do
         for i:=0 to node.def.vmtentries.count-1 do
-          if (po_virtualmethod in pvmtentry(node.def.vmtentries[i])^.procdef.procoptions) and
-             (node.def.vmcallstaticinfo^[i]=vmcs_yes) then
-            begin
-              { add info about devirtualised vmt entry }
-              classdevirtinfo.addstaticmethod(i,pvmtentry(node.def.vmtentries[i])^.procdef.mangledname);
+          if (po_virtualmethod in pvmtentry(node.def.vmtentries[i])^.procdef.procoptions) then
+            case node.def.vmcallstaticinfo^[i] of
+              vmcs_yes:
+                begin
+                  { add info about devirtualised vmt entry }
+                  classdevirtinfo.addstaticmethod(i,pvmtentry(node.def.vmtentries[i])^.procdef.mangledname);
+                end;
+              vmcs_unreachable:
+                begin
+                  { static reference to FPC_ABSTRACTERROR }
+                  classdevirtinfo.addstaticmethod(i,'FPC_ABSTRACTERROR');
+                end;
             end;
             end;
       end;
       end;
 
 
@@ -809,6 +895,17 @@ unit optvirt;
              end;
              end;
            end;
            end;
 
 
+         { add info about called virtual methods }
+         hp:=tmodule(loaded_units.first);
+         while assigned(hp) do
+          begin
+            if assigned(hp.wpoinfo.calledvmtentries) then
+              for i:=0 to hp.wpoinfo.calledvmtentries.count-1 do
+                inheritancetree.registercalledvmtentries(tcalledvmtentries(hp.wpoinfo.calledvmtentries[i]));
+            hp:=tmodule(hp.next);
+          end;
+
+
          inheritancetree.optimizevirtualmethods;
          inheritancetree.optimizevirtualmethods;
 {$ifdef DEBUG_DEVIRT}
 {$ifdef DEBUG_DEVIRT}
          inheritancetree.printvmtinfo;
          inheritancetree.printvmtinfo;

+ 1 - 1
compiler/ppu.pas

@@ -43,7 +43,7 @@ type
 {$endif Test_Double_checksum}
 {$endif Test_Double_checksum}
 
 
 const
 const
-  CurrentPPUVersion = 97;
+  CurrentPPUVersion = 98;
 
 
 { buffer sizes }
 { buffer sizes }
   maxentrysize = 1024;
   maxentrysize = 1024;

+ 11 - 1
compiler/symdef.pas

@@ -231,7 +231,7 @@ interface
 
 
        { tobjectdef }
        { tobjectdef }
 
 
-       tvmcallstatic = (vmcs_default, vmcs_yes, vmcs_no);
+       tvmcallstatic = (vmcs_default, vmcs_yes, vmcs_no, vmcs_unreachable);
        pmvcallstaticinfo = ^tmvcallstaticinfo;
        pmvcallstaticinfo = ^tmvcallstaticinfo;
        tmvcallstaticinfo = array[0..1024*1024-1] of tvmcallstatic;
        tmvcallstaticinfo = array[0..1024*1024-1] of tvmcallstatic;
        tobjectdef = class(tabstractrecorddef)
        tobjectdef = class(tabstractrecorddef)
@@ -296,9 +296,11 @@ interface
           function FindDestructor : tprocdef;
           function FindDestructor : tprocdef;
           function implements_any_interfaces: boolean;
           function implements_any_interfaces: boolean;
           procedure reset; override;
           procedure reset; override;
+          { WPO }
           procedure register_created_object_type;override;
           procedure register_created_object_type;override;
           procedure register_maybe_created_object_type;
           procedure register_maybe_created_object_type;
           procedure register_created_classref_type;
           procedure register_created_classref_type;
+          procedure register_vmt_call(index:longint);
        end;
        end;
 
 
        tclassrefdef = class(tabstractpointerdef)
        tclassrefdef = class(tabstractpointerdef)
@@ -4286,6 +4288,14 @@ implementation
           end;
           end;
       end;
       end;
 
 
+
+    procedure tobjectdef.register_vmt_call(index: longint);
+      begin
+        if (is_object(self) or is_class(self)) then
+          current_module.wpoinfo.addcalledvmtentry(self,index);
+      end;
+
+
 {****************************************************************************
 {****************************************************************************
                              TImplementedInterface
                              TImplementedInterface
 ****************************************************************************}
 ****************************************************************************}

+ 119 - 0
compiler/wpobase.pas

@@ -110,6 +110,31 @@ type
   { ** Information created per unit for use during subsequent compilation *** }
   { ** Information created per unit for use during subsequent compilation *** }
   { ************************************************************************* }
   { ************************************************************************* }
 
 
+  { information about called vmt entries for a class }
+  tcalledvmtentries = class
+   protected
+    { the class }
+    fobjdef: tdef;
+    fobjdefderef: tderef;
+    { the vmt entries }
+    fcalledentries: tbitset;
+   public
+    constructor create(_objdef: tdef; nentries: longint);
+    constructor ppuload(ppufile: tcompilerppufile);
+    destructor destroy; override;
+    procedure ppuwrite(ppufile: tcompilerppufile);
+
+    procedure buildderef;
+    procedure buildderefimpl;
+    procedure deref;
+    procedure derefimpl;
+
+    property objdef: tdef read fobjdef write fobjdef;
+    property objdefderef: tderef read fobjdefderef write fobjdefderef;
+    property calledentries: tbitset read fcalledentries write fcalledentries;
+  end;
+
+
   { base class of information collected per unit. Still needs to be
   { base class of information collected per unit. Still needs to be
     generalised for different kinds of wpo information, currently specific
     generalised for different kinds of wpo information, currently specific
     to devirtualization.
     to devirtualization.
@@ -127,6 +152,12 @@ type
        so they can end up in a classrefdef var and be instantiated)
        so they can end up in a classrefdef var and be instantiated)
     }
     }
     fmaybecreatedbyclassrefdeftypes: tfpobjectlist;
     fmaybecreatedbyclassrefdeftypes: tfpobjectlist;
+
+    { called virtual methods for all classes (hashed by mangled classname,
+      entries bitmaps indicating which vmt entries per class are called --
+      tcalledvmtentries)
+    }
+    fcalledvmtentries: tfphashlist;
    public
    public
     constructor create; reintroduce; virtual;
     constructor create; reintroduce; virtual;
     destructor destroy; override;
     destructor destroy; override;
@@ -134,10 +165,12 @@ type
     property createdobjtypes: tfpobjectlist read fcreatedobjtypes;
     property createdobjtypes: tfpobjectlist read fcreatedobjtypes;
     property createdclassrefobjtypes: tfpobjectlist read fcreatedclassrefobjtypes;
     property createdclassrefobjtypes: tfpobjectlist read fcreatedclassrefobjtypes;
     property maybecreatedbyclassrefdeftypes: tfpobjectlist read fmaybecreatedbyclassrefdeftypes;
     property maybecreatedbyclassrefdeftypes: tfpobjectlist read fmaybecreatedbyclassrefdeftypes;
+    property calledvmtentries: tfphashlist read fcalledvmtentries;
 
 
     procedure addcreatedobjtype(def: tdef);
     procedure addcreatedobjtype(def: tdef);
     procedure addcreatedobjtypeforclassref(def: tdef);
     procedure addcreatedobjtypeforclassref(def: tdef);
     procedure addmaybecreatedbyclassref(def: tdef);
     procedure addmaybecreatedbyclassref(def: tdef);
+    procedure addcalledvmtentry(def: tdef; index: longint);
   end;
   end;
 
 
   { ************************************************************************* }
   { ************************************************************************* }
@@ -321,10 +354,13 @@ implementation
       fcreatedobjtypes:=tfpobjectlist.create(false);
       fcreatedobjtypes:=tfpobjectlist.create(false);
       fcreatedclassrefobjtypes:=tfpobjectlist.create(false);
       fcreatedclassrefobjtypes:=tfpobjectlist.create(false);
       fmaybecreatedbyclassrefdeftypes:=tfpobjectlist.create(false);
       fmaybecreatedbyclassrefdeftypes:=tfpobjectlist.create(false);
+      fcalledvmtentries:=tfphashlist.create;
     end;
     end;
 
 
 
 
   destructor tunitwpoinfobase.destroy;
   destructor tunitwpoinfobase.destroy;
+    var
+      i: longint;
     begin
     begin
       fcreatedobjtypes.free;
       fcreatedobjtypes.free;
       fcreatedobjtypes:=nil;
       fcreatedobjtypes:=nil;
@@ -332,6 +368,12 @@ implementation
       fcreatedclassrefobjtypes:=nil;
       fcreatedclassrefobjtypes:=nil;
       fmaybecreatedbyclassrefdeftypes.free;
       fmaybecreatedbyclassrefdeftypes.free;
       fmaybecreatedbyclassrefdeftypes:=nil;
       fmaybecreatedbyclassrefdeftypes:=nil;
+
+      for i:=0 to fcalledvmtentries.count-1 do
+        tcalledvmtentries(fcalledvmtentries[i]).free;
+      fcalledvmtentries.free;
+      fcalledvmtentries:=nil;
+
       inherited destroy;
       inherited destroy;
     end;
     end;
     
     
@@ -341,16 +383,35 @@ implementation
       fcreatedobjtypes.add(def);
       fcreatedobjtypes.add(def);
     end;
     end;
 
 
+
   procedure tunitwpoinfobase.addcreatedobjtypeforclassref(def: tdef);
   procedure tunitwpoinfobase.addcreatedobjtypeforclassref(def: tdef);
     begin
     begin
       fcreatedclassrefobjtypes.add(def);
       fcreatedclassrefobjtypes.add(def);
     end;
     end;
 
 
+
   procedure tunitwpoinfobase.addmaybecreatedbyclassref(def: tdef);
   procedure tunitwpoinfobase.addmaybecreatedbyclassref(def: tdef);
     begin
     begin
       fmaybecreatedbyclassrefdeftypes.add(def);
       fmaybecreatedbyclassrefdeftypes.add(def);
     end;
     end;
 
 
+
+  procedure tunitwpoinfobase.addcalledvmtentry(def: tdef; index: longint);
+    var
+      entries: tcalledvmtentries;
+      key: shortstring;
+    begin
+      key:=tobjectdef(def).vmt_mangledname;
+      entries:=tcalledvmtentries(fcalledvmtentries.find(key));
+      if not assigned(entries) then
+        begin
+          entries:=tcalledvmtentries.create(def,tobjectdef(def).vmtentries.count);
+          fcalledvmtentries.add(key,entries);
+        end;
+      entries.calledentries.include(index);
+    end;
+
+
   { twpofilereader }
   { twpofilereader }
 
 
   function twpofilereader.getnextnoncommentline(out s: string):
   function twpofilereader.getnextnoncommentline(out s: string):
@@ -677,4 +738,62 @@ implementation
       inherited destroy;
       inherited destroy;
     end;
     end;
 
 
+  { tcalledvmtentries }
+
+  constructor tcalledvmtentries.create(_objdef: tdef; nentries: longint);
+    begin
+      objdef:=_objdef;
+      calledentries:=tbitset.create(nentries);
+    end;
+
+
+  constructor tcalledvmtentries.ppuload(ppufile: tcompilerppufile);
+    var
+      len: longint;
+    begin
+      ppufile.getderef(fobjdefderef);
+      len:=ppufile.getlongint;
+      calledentries:=tbitset.create_bytesize(len);
+      if (len <> calledentries.datasize) then
+        internalerror(2009060301);
+      ppufile.readdata(calledentries.data^,len);
+    end;
+
+
+  destructor tcalledvmtentries.destroy;
+    begin
+      fcalledentries.free;
+      inherited destroy;
+    end;
+
+
+  procedure tcalledvmtentries.ppuwrite(ppufile: tcompilerppufile);
+    begin
+      ppufile.putderef(objdefderef);
+      ppufile.putlongint(calledentries.datasize);
+      ppufile.putdata(calledentries.data^,calledentries.datasize);
+    end;
+
+
+  procedure tcalledvmtentries.buildderef;
+    begin
+      objdefderef.build(objdef);
+    end;
+
+
+  procedure tcalledvmtentries.buildderefimpl;
+    begin
+    end;
+
+
+  procedure tcalledvmtentries.deref;
+    begin
+      objdef:=tdef(objdefderef.resolve);
+    end;
+
+
+  procedure tcalledvmtentries.derefimpl;
+    begin
+    end;
+
 end.
 end.

+ 41 - 0
compiler/wpoinfo.pas

@@ -41,6 +41,7 @@ type
     fcreatedobjtypesderefs: pderefarray;
     fcreatedobjtypesderefs: pderefarray;
     fcreatedclassrefobjtypesderefs: pderefarray;
     fcreatedclassrefobjtypesderefs: pderefarray;
     fmaybecreatedbyclassrefdeftypesderefs: pderefarray;
     fmaybecreatedbyclassrefdeftypesderefs: pderefarray;
+    fcalledvmtentriestemplist: tfpobjectlist;
    { devirtualisation information -- end }
    { devirtualisation information -- end }
 
 
    public
    public
@@ -92,6 +93,13 @@ implementation
           freemem(fmaybecreatedbyclassrefdeftypesderefs);
           freemem(fmaybecreatedbyclassrefdeftypesderefs);
           fmaybecreatedbyclassrefdeftypesderefs:=nil;
           fmaybecreatedbyclassrefdeftypesderefs:=nil;
         end;
         end;
+
+      if assigned(fcalledvmtentriestemplist) then
+        begin
+          fcalledvmtentriestemplist.free;
+          fcalledvmtentriestemplist:=nil;
+        end;
+
       inherited destroy;
       inherited destroy;
     end;
     end;
     
     
@@ -113,6 +121,10 @@ implementation
       for i:=0 to fmaybecreatedbyclassrefdeftypes.count-1 do
       for i:=0 to fmaybecreatedbyclassrefdeftypes.count-1 do
         ppufile.putderef(fmaybecreatedbyclassrefdeftypesderefs^[i]);
         ppufile.putderef(fmaybecreatedbyclassrefdeftypesderefs^[i]);
 
 
+      ppufile.putlongint(fcalledvmtentries.count);
+      for i:=0 to fcalledvmtentries.count-1 do
+        tcalledvmtentries(fcalledvmtentries[i]).ppuwrite(ppufile);
+
       ppufile.writeentry(ibcreatedobjtypes);
       ppufile.writeentry(ibcreatedobjtypes);
 
 
       { don't free deref arrays immediately after use, as the types may need
       { don't free deref arrays immediately after use, as the types may need
@@ -149,6 +161,13 @@ implementation
       getmem(fmaybecreatedbyclassrefdeftypesderefs,len*sizeof(tderef));
       getmem(fmaybecreatedbyclassrefdeftypesderefs,len*sizeof(tderef));
       for i:=0 to len-1 do
       for i:=0 to len-1 do
         ppufile.getderef(fmaybecreatedbyclassrefdeftypesderefs^[i]);
         ppufile.getderef(fmaybecreatedbyclassrefdeftypesderefs^[i]);
+
+      len:=ppufile.getlongint;
+      fcalledvmtentriestemplist:=tfpobjectlist.create(false);
+      fcalledvmtentriestemplist.count:=len;
+      fcalledvmtentries:=tfphashlist.create;
+      for i:=0 to len-1 do
+        fcalledvmtentriestemplist[i]:=tcalledvmtentries.ppuload(ppufile);
     end;
     end;
 
 
 
 
@@ -167,6 +186,9 @@ implementation
       getmem(fmaybecreatedbyclassrefdeftypesderefs,fmaybecreatedbyclassrefdeftypes.count*sizeof(tderef));
       getmem(fmaybecreatedbyclassrefdeftypesderefs,fmaybecreatedbyclassrefdeftypes.count*sizeof(tderef));
       for i:=0 to fmaybecreatedbyclassrefdeftypes.count-1 do
       for i:=0 to fmaybecreatedbyclassrefdeftypes.count-1 do
         fmaybecreatedbyclassrefdeftypesderefs^[i].build(fmaybecreatedbyclassrefdeftypes[i]);
         fmaybecreatedbyclassrefdeftypesderefs^[i].build(fmaybecreatedbyclassrefdeftypes[i]);
+
+      for i:=0 to fcalledvmtentries.count-1 do
+        tcalledvmtentries(fcalledvmtentries[i]).objdefderef.build(tcalledvmtentries(fcalledvmtentries[i]).objdef);
     end;
     end;
 
 
 
 
@@ -178,6 +200,8 @@ implementation
   procedure tunitwpoinfo.deref;
   procedure tunitwpoinfo.deref;
     var
     var
       i: longint;
       i: longint;
+      len: longint;
+
     begin
     begin
       { don't free deref arrays immediately after use, as the types may need
       { don't free deref arrays immediately after use, as the types may need
         re-resolving in case a unit needs to be reloaded
         re-resolving in case a unit needs to be reloaded
@@ -190,6 +214,23 @@ implementation
 
 
       for i:=0 to fmaybecreatedbyclassrefdeftypes.count-1 do
       for i:=0 to fmaybecreatedbyclassrefdeftypes.count-1 do
         fmaybecreatedbyclassrefdeftypes[i]:=fmaybecreatedbyclassrefdeftypesderefs^[i].resolve;
         fmaybecreatedbyclassrefdeftypes[i]:=fmaybecreatedbyclassrefdeftypesderefs^[i].resolve;
+
+      { in case we are re-resolving, free previous batch }
+      if (fcalledvmtentries.count<>0) then
+        { don't just re-deref, in case the name might have changed (?) }
+        fcalledvmtentries.clear;
+      { allocate enough internal memory in one go }
+      fcalledvmtentries.capacity:=fcalledvmtentriestemplist.count;
+      { now resolve all items in the list and add them to the hash table }
+      for i:=0 to fcalledvmtentriestemplist.count-1 do
+        begin
+          with tcalledvmtentries(fcalledvmtentriestemplist[i]) do
+            begin
+              objdef:=tdef(objdefderef.resolve);
+              fcalledvmtentries.add(tobjectdef(objdef).vmt_mangledname,
+                fcalledvmtentriestemplist[i]);
+            end;
+        end;
     end;
     end;
 
 
 
 

+ 74 - 0
tests/test/opt/twpo7.pp

@@ -0,0 +1,74 @@
+{ %wpoparas=devirtcalls,optvmts }
+{ %wpopasses=1 }
+
+
+{$mode objfpc}
+
+type
+  tu1 = class
+    procedure u1proccalled; virtual;
+    procedure u1proccalledinoverride; virtual;
+    procedure u1proccallednotoverridden; virtual;
+    procedure u1procnotcalled; virtual;
+    procedure u1procaddrtaken; virtual;
+  end;
+
+  tu2 = class(tu1)
+    procedure u1proccalledinoverride; override;
+  end;
+
+
+  procedure tu1.u1proccalled;
+    begin
+      writeln('u1proccalled in u1');
+    end;
+
+  procedure tu1.u1proccalledinoverride;
+    begin
+      writeln('u1proccalledinoverride in u1');
+      if (self.classtype=tu1) then
+        halt(3);
+    end;
+
+  procedure tu1.u1proccallednotoverridden;
+    begin
+      writeln('u1proccallednotoverridden in u1');
+      if not(self.classtype = tu1) then
+        halt(4);
+    end;
+
+  procedure tu1.u1procnotcalled;
+    begin
+      writeln('u1procnotcalled in u1');
+      halt(1);
+    end;
+
+  procedure tu1.u1procaddrtaken;
+    begin
+      writeln('procvar called');
+    end;
+
+
+  procedure tu2.u1proccalledinoverride;
+    begin
+      writeln('u1proccalledinoverride in u2');
+      if (self.classtype <> tu2) then
+        halt(10);
+    end;
+
+var
+  u1: tu1;
+  u2: tu2;
+  p: procedure of object;
+begin
+  u1:=tu1.create;
+  u1.u1proccalled;
+  u1.u1proccallednotoverridden;
+  u1.free;
+  u2:=tu2.create;
+  p:[email protected];
+  p();
+  u2.u1proccalled;
+  u2.u1proccalledinoverride;
+  u2.free;
+end.