From 41acad1d116ce0b84a9307101636dbbefe1408da Mon Sep 17 00:00:00 2001 From: Jonas Maebe Date: Sat, 6 Jun 2009 08:24:36 +0000 Subject: [PATCH] + 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 - --- .gitattributes | 1 + compiler/cclasses.pas | 123 +++++++++++++++++++++++++++++++++++ compiler/ncgld.pas | 13 +++- compiler/ncgrtti.pas | 9 ++- compiler/optvirt.pas | 141 +++++++++++++++++++++++++++++++++------- compiler/ppu.pas | 2 +- compiler/symdef.pas | 12 +++- compiler/wpobase.pas | 119 +++++++++++++++++++++++++++++++++ compiler/wpoinfo.pas | 41 ++++++++++++ tests/test/opt/twpo7.pp | 74 +++++++++++++++++++++ 10 files changed, 509 insertions(+), 26 deletions(-) create mode 100644 tests/test/opt/twpo7.pp diff --git a/.gitattributes b/.gitattributes index c2064a2247..692a646620 100644 --- a/.gitattributes +++ b/.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/twpo4.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/packages/fcl-base/tascii85.pp svneol=native#text/plain tests/test/packages/fcl-base/tgettext1.pp svneol=native#text/plain diff --git a/compiler/cclasses.pas b/compiler/cclasses.pas index f31333d5de..35d3ba26bc 100644 --- a/compiler/cclasses.pas +++ b/compiler/cclasses.pas @@ -504,6 +504,35 @@ type 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(P: PChar; Len: Integer): LongWord; @@ -2757,4 +2786,98 @@ end; Result := False; 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:= + (dataindex0); + 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. diff --git a/compiler/ncgld.pas b/compiler/ncgld.pas index b1991786a5..7c90837444 100644 --- a/compiler/ncgld.pas +++ b/compiler/ncgld.pas @@ -64,7 +64,8 @@ implementation cpubase,parabase, tgobj,ncgutil, cgobj, - ncgbas,ncgflw; + ncgbas,ncgflw, + wpobase; {***************************************************************************** SSA (for memory temps) support @@ -481,6 +482,16 @@ implementation if (po_virtualmethod in procdef.procoptions) and not(nf_inherited in flags) then 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 } if (left.resultdef.typ<>classrefdef) then begin diff --git a/compiler/ncgrtti.pas b/compiler/ncgrtti.pas index 0c05298acd..ca39913631 100644 --- a/compiler/ncgrtti.pas +++ b/compiler/ncgrtti.pas @@ -67,7 +67,8 @@ implementation fmodule, symsym, aasmtai,aasmdata, - defutil + defutil, + wpobase ; @@ -311,6 +312,12 @@ implementation { virtual method, write vmt offset } current_asmdata.asmlists[al_rtti].concat(Tai_const.create(aitconst_ptr, 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; end; end; diff --git a/compiler/optvirt.pas b/compiler/optvirt.pas index 07fc446c20..0d79c7c362 100644 --- a/compiler/optvirt.pas +++ b/compiler/optvirt.pas @@ -40,6 +40,7 @@ unit optvirt; fdef: tobjectdef; fparent: tinheritancetreenode; fchilds: tfpobjectlist; + fcalledvmtmethods: tbitset; finstantiated: boolean; function getchild(index: longint): tinheritancetreenode; @@ -57,6 +58,7 @@ unit optvirt; this def (either new or existing one } function maybeaddchild(_def: tobjectdef; _instantiated: boolean): tinheritancetreenode; + function findchild(_def: tobjectdef): tinheritancetreenode; end; @@ -73,6 +75,9 @@ unit optvirt; function registerinstantiatedobjectdefrecursive(def: tobjectdef; instantiated: boolean): tinheritancetreenode; procedure markvmethods(node: tinheritancetreenode; p: pointer); procedure printobjectvmtinfo(node: tinheritancetreenode; arg: pointer); + procedure addcalledvmtentries(node: tinheritancetreenode; arg: pointer); + + function getnodefordef(def: tobjectdef): tinheritancetreenode; public constructor create; destructor destroy; override; @@ -81,6 +86,7 @@ unit optvirt; } procedure registerinstantiatedobjdef(def: tdef); procedure registerinstantiatedclassrefdef(def: tdef); + procedure registercalledvmtentries(entries: tcalledvmtentries); procedure checkforclassrefinheritance(def: tdef); procedure foreachnode(proctocall: tinheritancetreecallback; arg: pointer); procedure foreachleafnode(proctocall: tinheritancetreecallback; arg: pointer); @@ -178,6 +184,8 @@ unit optvirt; fparent:=_parent; fdef:=_def; finstantiated:=_instantiated; + if assigned(_def) then + fcalledvmtmethods:=tbitset.create(_def.vmtentries.count); end; @@ -185,6 +193,7 @@ unit optvirt; begin { fchilds owns its members, so it will free them too } fchilds.free; + fcalledvmtmethods.free; inherited destroy; end; @@ -211,8 +220,6 @@ unit optvirt; function tinheritancetreenode.maybeaddchild(_def: tobjectdef; _instantiated: boolean): tinheritancetreenode; - var - i: longint; begin { sanity check } if assigned(_def.childof) then @@ -226,19 +233,32 @@ unit optvirt; 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); + 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; + 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 ************************* } constructor tinheritancetree.create; @@ -296,6 +316,37 @@ unit optvirt; 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); var i: longint; @@ -408,8 +459,19 @@ unit optvirt; if not assigned(currnode.def.vmcallstaticinfo) then 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) } - if (currnode.def.vmcallstaticinfo^[i] in [vmcs_default,vmcs_yes]) then + else 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 @@ -439,14 +501,16 @@ unit optvirt; end; currnode:=currnode.parent; end - else + else if (currnode.def.vmcallstaticinfo^[i]=vmcs_no) then begin {$IFDEF DEBUG_DEVIRT} writeln(' not processing parents, already non-static for ',currnode.def.typename); {$ENDIF} { parents are already set to vmcs_no, so no need to continue } currnode:=nil; - end; + end + else + currnode:=currnode.parent; until not assigned(currnode) or not assigned(currnode.def); end; @@ -463,10 +527,12 @@ unit optvirt; var i, totaldevirtualised, - totalvirtual: ptrint; + totalvirtual, + totalunreachable: ptrint; begin totaldevirtualised:=0; totalvirtual:=0; + totalunreachable:=0; writeln(node.def.typename); if (node.def.vmtentries.count=0) then begin @@ -481,13 +547,26 @@ unit optvirt; begin inc(totaldevirtualised); 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; - writeln('Total devirtualised: ',totaldevirtualised,'/',totalvirtual); + writeln('Total devirtualised/unreachable/all: ',totaldevirtualised,'/',totalunreachable,'/',totalvirtual); writeln; end; + procedure tinheritancetree.addcalledvmtentries(node: tinheritancetreenode; arg: pointer); + var + vmtentries: tbitset absolute arg; + begin + node.fcalledvmtmethods.addset(vmtentries); + end; + + procedure tinheritancetree.printvmtinfo; begin foreachnode(@printobjectvmtinfo,nil); @@ -622,11 +701,18 @@ unit optvirt; if (node.def.vmtentries.count=0) then exit; 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; @@ -809,6 +895,17 @@ unit optvirt; 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; {$ifdef DEBUG_DEVIRT} inheritancetree.printvmtinfo; diff --git a/compiler/ppu.pas b/compiler/ppu.pas index ad12f19838..e99eda6af0 100644 --- a/compiler/ppu.pas +++ b/compiler/ppu.pas @@ -43,7 +43,7 @@ type {$endif Test_Double_checksum} const - CurrentPPUVersion = 97; + CurrentPPUVersion = 98; { buffer sizes } maxentrysize = 1024; diff --git a/compiler/symdef.pas b/compiler/symdef.pas index 9ef979ad1e..72dd81e843 100644 --- a/compiler/symdef.pas +++ b/compiler/symdef.pas @@ -231,7 +231,7 @@ interface { tobjectdef } - tvmcallstatic = (vmcs_default, vmcs_yes, vmcs_no); + tvmcallstatic = (vmcs_default, vmcs_yes, vmcs_no, vmcs_unreachable); pmvcallstaticinfo = ^tmvcallstaticinfo; tmvcallstaticinfo = array[0..1024*1024-1] of tvmcallstatic; tobjectdef = class(tabstractrecorddef) @@ -296,9 +296,11 @@ interface function FindDestructor : tprocdef; function implements_any_interfaces: boolean; procedure reset; override; + { WPO } procedure register_created_object_type;override; procedure register_maybe_created_object_type; procedure register_created_classref_type; + procedure register_vmt_call(index:longint); end; tclassrefdef = class(tabstractpointerdef) @@ -4286,6 +4288,14 @@ implementation 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 ****************************************************************************} diff --git a/compiler/wpobase.pas b/compiler/wpobase.pas index 25674eabe8..55e1e2f2ae 100644 --- a/compiler/wpobase.pas +++ b/compiler/wpobase.pas @@ -110,6 +110,31 @@ type { ** 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 generalised for different kinds of wpo information, currently specific to devirtualization. @@ -127,6 +152,12 @@ type so they can end up in a classrefdef var and be instantiated) } 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 constructor create; reintroduce; virtual; destructor destroy; override; @@ -134,10 +165,12 @@ type property createdobjtypes: tfpobjectlist read fcreatedobjtypes; property createdclassrefobjtypes: tfpobjectlist read fcreatedclassrefobjtypes; property maybecreatedbyclassrefdeftypes: tfpobjectlist read fmaybecreatedbyclassrefdeftypes; + property calledvmtentries: tfphashlist read fcalledvmtentries; procedure addcreatedobjtype(def: tdef); procedure addcreatedobjtypeforclassref(def: tdef); procedure addmaybecreatedbyclassref(def: tdef); + procedure addcalledvmtentry(def: tdef; index: longint); end; { ************************************************************************* } @@ -321,10 +354,13 @@ implementation fcreatedobjtypes:=tfpobjectlist.create(false); fcreatedclassrefobjtypes:=tfpobjectlist.create(false); fmaybecreatedbyclassrefdeftypes:=tfpobjectlist.create(false); + fcalledvmtentries:=tfphashlist.create; end; destructor tunitwpoinfobase.destroy; + var + i: longint; begin fcreatedobjtypes.free; fcreatedobjtypes:=nil; @@ -332,6 +368,12 @@ implementation fcreatedclassrefobjtypes:=nil; fmaybecreatedbyclassrefdeftypes.free; fmaybecreatedbyclassrefdeftypes:=nil; + + for i:=0 to fcalledvmtentries.count-1 do + tcalledvmtentries(fcalledvmtentries[i]).free; + fcalledvmtentries.free; + fcalledvmtentries:=nil; + inherited destroy; end; @@ -341,16 +383,35 @@ implementation fcreatedobjtypes.add(def); end; + procedure tunitwpoinfobase.addcreatedobjtypeforclassref(def: tdef); begin fcreatedclassrefobjtypes.add(def); end; + procedure tunitwpoinfobase.addmaybecreatedbyclassref(def: tdef); begin fmaybecreatedbyclassrefdeftypes.add(def); 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 } function twpofilereader.getnextnoncommentline(out s: string): @@ -677,4 +738,62 @@ implementation inherited destroy; 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. diff --git a/compiler/wpoinfo.pas b/compiler/wpoinfo.pas index a53b739860..e773616af7 100644 --- a/compiler/wpoinfo.pas +++ b/compiler/wpoinfo.pas @@ -41,6 +41,7 @@ type fcreatedobjtypesderefs: pderefarray; fcreatedclassrefobjtypesderefs: pderefarray; fmaybecreatedbyclassrefdeftypesderefs: pderefarray; + fcalledvmtentriestemplist: tfpobjectlist; { devirtualisation information -- end } public @@ -92,6 +93,13 @@ implementation freemem(fmaybecreatedbyclassrefdeftypesderefs); fmaybecreatedbyclassrefdeftypesderefs:=nil; end; + + if assigned(fcalledvmtentriestemplist) then + begin + fcalledvmtentriestemplist.free; + fcalledvmtentriestemplist:=nil; + end; + inherited destroy; end; @@ -113,6 +121,10 @@ implementation for i:=0 to fmaybecreatedbyclassrefdeftypes.count-1 do ppufile.putderef(fmaybecreatedbyclassrefdeftypesderefs^[i]); + ppufile.putlongint(fcalledvmtentries.count); + for i:=0 to fcalledvmtentries.count-1 do + tcalledvmtentries(fcalledvmtentries[i]).ppuwrite(ppufile); + ppufile.writeentry(ibcreatedobjtypes); { don't free deref arrays immediately after use, as the types may need @@ -149,6 +161,13 @@ implementation getmem(fmaybecreatedbyclassrefdeftypesderefs,len*sizeof(tderef)); for i:=0 to len-1 do 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; @@ -167,6 +186,9 @@ implementation getmem(fmaybecreatedbyclassrefdeftypesderefs,fmaybecreatedbyclassrefdeftypes.count*sizeof(tderef)); for i:=0 to fmaybecreatedbyclassrefdeftypes.count-1 do fmaybecreatedbyclassrefdeftypesderefs^[i].build(fmaybecreatedbyclassrefdeftypes[i]); + + for i:=0 to fcalledvmtentries.count-1 do + tcalledvmtentries(fcalledvmtentries[i]).objdefderef.build(tcalledvmtentries(fcalledvmtentries[i]).objdef); end; @@ -178,6 +200,8 @@ implementation procedure tunitwpoinfo.deref; var i: longint; + len: longint; + begin { don't free deref arrays immediately after use, as the types may need re-resolving in case a unit needs to be reloaded @@ -190,6 +214,23 @@ implementation for i:=0 to fmaybecreatedbyclassrefdeftypes.count-1 do 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; diff --git a/tests/test/opt/twpo7.pp b/tests/test/opt/twpo7.pp new file mode 100644 index 0000000000..41f6b3d733 --- /dev/null +++ b/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:=@u2.u1procaddrtaken; + p(); + u2.u1proccalled; + u2.u1proccalledinoverride; + u2.free; +end.