1 {
2     Copyright (c) 2008 by Jonas Maebe
3 
4     Virtual methods optimizations (devirtualization)
5 
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10 
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15 
16     You should have received a copy of the GNU General Public License
17     along with this program; if not, write to the Free Software
18     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 
20  ****************************************************************************
21 }
22 unit optvirt;
23 
24 {$i fpcdefs.inc}
25 
26   interface
27 
28     uses
29       globtype,
30       cclasses,
31       symtype,symdef,
32       wpobase;
33 
34     type
35        { node in an inheritance tree, contains a link to the parent type (if any) and to all
36         child types
37       }
38       tinheritancetreenode = class
39        private
40         fdef: tobjectdef;
41         fparent: tinheritancetreenode;
42         fchilds: tfpobjectlist;
43         fcalledvmtmethods: tbitset;
44         finstantiated: boolean;
45 
getchildnull46         function getchild(index: longint): tinheritancetreenode;
47        public
48         constructor create(_parent: tinheritancetreenode; _def: tobjectdef; _instantiated: boolean);
49         { destroys both this node and all of its siblings }
50         destructor destroy; override;
childcountnull51         function  childcount: longint;
haschildsnull52         function  haschilds: boolean;
53         property  childs[index: longint]: tinheritancetreenode read getchild;
54         property  parent: tinheritancetreenode read fparent;
55         property  def: tobjectdef read fdef;
56         property  instantiated: boolean read finstantiated write finstantiated;
57         { if def is not yet a child of this node, add it. In all cases, return node containing
58           this def (either new or existing one
59         }
maybeaddchildnull60         function  maybeaddchild(_def: tobjectdef; _instantiated: boolean): tinheritancetreenode;
findchildnull61         function  findchild(_def: tobjectdef): tinheritancetreenode;
62       end;
63 
64 
65       tinheritancetreecallback = procedure(node: tinheritancetreenode; arg: pointer) of object;
66 
67       tinheritancetree = class
68        private
69         { just a regular node with parent = nil }
70         froots: tinheritancetreenode;
71 
72         classrefdefs: tfpobjectlist;
73 
74         procedure foreachnodefromroot(root: tinheritancetreenode; proctocall: tinheritancetreecallback; arg: pointer);
registerinstantiatedobjectdefrecursivenull75         function registerinstantiatedobjectdefrecursive(def: tobjectdef; instantiated: boolean): tinheritancetreenode;
76         procedure markvmethods(node: tinheritancetreenode; p: pointer);
77         procedure printobjectvmtinfo(node: tinheritancetreenode; arg: pointer);
78         procedure addcalledvmtentries(node: tinheritancetreenode; arg: pointer);
79 
getnodefordefnull80         function  getnodefordef(def: tobjectdef): tinheritancetreenode;
81        public
82         constructor create;
83         destructor destroy; override;
84         { adds an objectdef (the def itself, and all of its parents that do not yet exist) to
85           the tree, and returns the leaf node
86         }
87         procedure registerinstantiatedobjdef(def: tdef);
88         procedure registerinstantiatedclassrefdef(def: tdef);
89         procedure registercalledvmtentries(entries: tcalledvmtentries);
90         procedure checkforclassrefinheritance(def: tdef);
91         procedure foreachnode(proctocall: tinheritancetreecallback; arg: pointer);
92         procedure foreachleafnode(proctocall: tinheritancetreecallback; arg: pointer);
93         procedure optimizevirtualmethods;
94         procedure printvmtinfo;
95       end;
96 
97 
98       { devirtualisation information for a class }
99 
100       tclassdevirtinfo = class(tfphashobject)
101        private
102         { array (indexed by vmt entry nr) of replacement statically callable method names }
103         fstaticmethodnames: tfplist;
104         { is this class instantiated by the program? }
105         finstantiated: boolean;
isstaticvmtentrynull106         function isstaticvmtentry(vmtindex: longint; out replacementname: pshortstring): boolean;
107        public
108         constructor create(hashobjectlist:tfphashobjectlist;const n: shortstring; instantiated: boolean);
109         destructor destroy; override;
110 
111         property instantiated: boolean read finstantiated;
112 
113         procedure addstaticmethod(vmtindex: longint; const replacementname: shortstring);
114       end;
115 
116 
117       { devirtualisation information for all classes in a unit }
118 
119       tunitdevirtinfo = class(tfphashobject)
120        private
121         { hashtable of classes }
122         fclasses: tfphashobjectlist;
123        public
124         constructor create(hashobjectlist:tfphashobjectlist;const n: shortstring);reintroduce;
125         destructor destroy; override;
126 
addclassnull127         function addclass(const n: shortstring; instantiated: boolean): tclassdevirtinfo;
findclassnull128         function findclass(const n: shortstring): tclassdevirtinfo;
129       end;
130 
131       { devirtualisation information for all units in a program }
132 
133       { tprogdevirtinfo }
134 
135       tprogdevirtinfo = class(twpodevirtualisationhandler)
136        private
137         { hashtable of tunitdevirtinfo (which contain tclassdevirtinfo) }
138         funits: tfphashobjectlist;
139 
140         procedure converttreenode(node: tinheritancetreenode; arg: pointer);
addunitifnewnull141         function addunitifnew(const n: shortstring): tunitdevirtinfo;
findunitnull142         function findunit(const n: shortstring): tunitdevirtinfo;
getstaticnamenull143         function getstaticname(forvmtentry: boolean; objdef, procdef: tdef; out staticname: TSymStr): boolean;
144         procedure documentformat(writer: twposectionwriterintf);
145        public
146         constructor create; override;
147         destructor destroy; override;
148 
getwpotypenull149         class function getwpotype: twpotype; override;
generatesinfoforwposwitchesnull150         class function generatesinfoforwposwitches: twpoptimizerswitches; override;
performswpoforswitchesnull151         class function performswpoforswitches: twpoptimizerswitches; override;
sectionnamenull152         class function sectionname: shortstring; override;
153 
154         { information collection }
155         procedure constructfromcompilerstate; override;
156         procedure storewpofilesection(writer: twposectionwriterintf); override;
157 
158         { information providing }
159         procedure loadfromwpofilesection(reader: twposectionreaderintf); override;
staticnameforcallingvirtualmethodnull160         function staticnameforcallingvirtualmethod(objdef, procdef: tdef; out staticname: TSymStr): boolean; override;
staticnameforvmtentrynull161         function staticnameforvmtentry(objdef, procdef: tdef; out staticname: TSymStr): boolean; override;
162 
163       end;
164 
165 
166   implementation
167 
168     uses
169       cutils,
170       fmodule,
171       symconst,
172       symbase,
173       defcmp,
174       verbose;
175 
176     const
177       DEVIRT_SECTION_NAME = 'contextinsensitive_devirtualization';
178 
179    { *************************** tinheritancetreenode ************************* }
180 
181     constructor tinheritancetreenode.create(_parent: tinheritancetreenode; _def: tobjectdef; _instantiated: boolean);
182       begin
183         fparent:=_parent;
184         fdef:=_def;
185         finstantiated:=_instantiated;
186         if assigned(_def) then
187           fcalledvmtmethods:=tbitset.create(_def.vmtentries.count);
188       end;
189 
190 
191     destructor tinheritancetreenode.destroy;
192       begin
193         { fchilds owns its members, so it will free them too }
194         fchilds.free;
195         fcalledvmtmethods.free;
196         inherited destroy;
197       end;
198 
199 
tinheritancetreenode.childcountnull200     function tinheritancetreenode.childcount: longint;
201       begin
202         if assigned(fchilds) then
203           result:=fchilds.count
204         else
205           result:=0;
206       end;
207 
208 
tinheritancetreenode.haschildsnull209     function tinheritancetreenode.haschilds: boolean;
210       begin
211         result:=assigned(fchilds)
212       end;
213 
214 
tinheritancetreenode.getchildnull215     function tinheritancetreenode.getchild(index: longint): tinheritancetreenode;
216       begin
217         result:=tinheritancetreenode(fchilds[index]);
218       end;
219 
220 
tinheritancetreenode.maybeaddchildnull221     function tinheritancetreenode.maybeaddchild(_def: tobjectdef; _instantiated: boolean): tinheritancetreenode;
222       begin
223         { sanity check }
224         if assigned(_def.childof) then
225           begin
226             if (_def.childof<>def) then
227               internalerror(2008092201);
228           end
229         else if assigned(fparent) then
230           internalerror(2008092202);
231 
232         if not assigned(fchilds) then
233           fchilds:=tfpobjectlist.create(true);
234         { def already a child -> return }
235         result:=findchild(_def);
236         if assigned(result) then
237           result.finstantiated:=result.finstantiated or _instantiated
238         else
239           begin
240             { not found, add new child }
241             result:=tinheritancetreenode.create(self,_def,_instantiated);
242             fchilds.add(result);
243           end;
244       end;
245 
246 
tinheritancetreenode.findchildnull247     function tinheritancetreenode.findchild(_def: tobjectdef): tinheritancetreenode;
248       var
249         i: longint;
250       begin
251         result:=nil;
252         if assigned(fchilds) then
253           for i := 0 to fchilds.count-1 do
254             if (tinheritancetreenode(fchilds[i]).def=_def) then
255               begin
256                 result:=tinheritancetreenode(fchilds[i]);
257                 break;
258               end;
259       end;
260 
261     { *************************** tinheritancetree ************************* }
262 
263     constructor tinheritancetree.create;
264       begin
265         froots:=tinheritancetreenode.create(nil,nil,false);
266         classrefdefs:=tfpobjectlist.create(false);
267       end;
268 
269 
270     destructor tinheritancetree.destroy;
271       begin
272         froots.free;
273         classrefdefs.free;
274         inherited destroy;
275       end;
276 
277 
tinheritancetree.registerinstantiatedobjectdefrecursivenull278     function tinheritancetree.registerinstantiatedobjectdefrecursive(def: tobjectdef; instantiated: boolean): tinheritancetreenode;
279       begin
280         if assigned(def.childof) then
281           begin
282             { recursively add parent, of which we have no info about whether or not it is
283               instantiated at this point -> default to false (will be overridden by "true"
284               if this class is instantioted, since then registerinstantiatedobjdef() will
285               be called for this class as well)
286             }
287             result:=registerinstantiatedobjectdefrecursive(def.childof,false);
288             { and add ourselves to the parent }
289             result:=result.maybeaddchild(def,instantiated);
290           end
291         else
292           { add ourselves to the roots }
293           result:=froots.maybeaddchild(def,instantiated);
294       end;
295 
296 
297     procedure tinheritancetree.registerinstantiatedobjdef(def: tdef);
298       begin
299         { add the def }
300         if (def.typ=objectdef) then
301           registerinstantiatedobjectdefrecursive(tobjectdef(def),true)
302         else
303           internalerror(2008092401);
304       end;
305 
306 
307     procedure tinheritancetree.registerinstantiatedclassrefdef(def: tdef);
308       begin
309         { queue for later checking (these are the objectdefs
310           to which the classrefdefs point) }
311         if (def.typ=objectdef) then
312           classrefdefs.add(def)
313         else
314           internalerror(2008101401);
315       end;
316 
317 
tinheritancetree.getnodefordefnull318     function tinheritancetree.getnodefordef(def: tobjectdef): tinheritancetreenode;
319       begin
320         if assigned(def.childof) then
321           begin
322             result:=getnodefordef(def.childof);
323             if assigned(result) then
324               result:=result.findchild(def);
325           end
326         else
327           result:=froots.findchild(def);
328       end;
329 
330 
331     procedure tinheritancetree.registercalledvmtentries(entries: tcalledvmtentries);
332       var
333         node: tinheritancetreenode;
334       begin
335         node:=getnodefordef(tobjectdef(entries.objdef));
336         { it's possible that no instance of this class or its descendants are
337           instantiated
338         }
339         if not assigned(node) then
340           exit;
341         { now mark these methods as (potentially) called for this type and for
342           all of its descendants
343         }
344         addcalledvmtentries(node,entries.calledentries);
345         foreachnodefromroot(node,@addcalledvmtentries,entries.calledentries);
346       end;
347 
348 
349    procedure tinheritancetree.checkforclassrefinheritance(def: tdef);
350      var
351        i: longint;
352      begin
353        if (def.typ=objectdef) then
354          begin
355 {$ifdef debug_devirt}
356            write('   Checking for classrefdef inheritance of ',def.typename);
357 {$endif debug_devirt}
358            for i:=0 to classrefdefs.count-1 do
359              if def_is_related(tobjectdef(def),tobjectdef(classrefdefs[i])) then
360                begin
361 {$ifdef debug_devirt}
362                  writeln('... Found: inherits from Class Of ',tobjectdef(classrefdefs[i]).typename);
363 {$endif debug_devirt}
364                  registerinstantiatedobjdef(def);
365                  exit;
366                end;
367 {$ifdef debug_devirt}
368            writeln('... Not found!');
369 {$endif debug_devirt}
370          end;
371      end;
372 
373 
374     procedure tinheritancetree.foreachnodefromroot(root: tinheritancetreenode; proctocall: tinheritancetreecallback; arg: pointer);
375 
376       procedure process(const node: tinheritancetreenode);
377         var
378          i: longint;
379         begin
380           for i:=0 to node.childcount-1 do
381             if node.childs[i].haschilds then
382               begin
383                 proctocall(node.childs[i],arg);
384                 process(node.childs[i])
385               end
386             else
387               proctocall(node.childs[i],arg);
388         end;
389 
390       begin
391         process(root);
392       end;
393 
394 
395     procedure tinheritancetree.foreachnode(proctocall: tinheritancetreecallback; arg: pointer);
396       begin
397         foreachnodefromroot(froots,proctocall,arg);
398       end;
399 
400 
401     procedure tinheritancetree.foreachleafnode(proctocall: tinheritancetreecallback; arg: pointer);
402 
403       procedure process(const node: tinheritancetreenode);
404         var
405          i: longint;
406         begin
407           for i:=0 to node.childcount-1 do
408             if node.childs[i].haschilds then
409               process(node.childs[i])
410             else
411               proctocall(node.childs[i],arg);
412         end;
413 
414       begin
415         process(froots);
416       end;
417 
418 
419     procedure tinheritancetree.markvmethods(node: tinheritancetreenode; p: pointer);
420       var
421         currnode: tinheritancetreenode;
422         pd: tprocdef;
423         i: longint;
424         makeallvirtual: boolean;
425       begin
426         {$IFDEF DEBUG_DEVIRT}
427         writeln('processing leaf node ',node.def.typename);
428         {$ENDIF}
429         { todo: also process interfaces (ImplementedInterfaces) }
430         if (node.def.vmtentries.count=0) then
431           exit;
432         { process all vmt entries for this class/object }
433         for i:=0 to node.def.vmtentries.count-1 do
434           begin
435             currnode:=node;
436             { extra tprocdef(tobject(..)) typecasts so that -CR can catch
437               errors in case the vmtentries are not properly (re)deref'd }
438             pd:=tprocdef(tobject(pvmtentry(currnode.def.vmtentries[i])^.procdef));
439             { abstract methods cannot be called directly }
440             if (po_abstractmethod in pd.procoptions) then
441               continue;
442             {$IFDEF DEBUG_DEVIRT}
443             writeln('  method ',pd.typename);
444             {$ENDIF}
445             { Now mark all virtual methods static that are the same in parent
446               classes as in this instantiated child class (only instantiated
447               classes can be leaf nodes, since only instantiated classes were
448               added to the tree).
449               If a first child does not override a parent method while a
450               a second one does, the first will mark it as statically
451               callable, but the second will set it to not statically callable.
452               In the opposite situation, the first will mark it as not
453               statically callable and the second will leave it alone.
454             }
455             makeallvirtual:=false;
456             repeat
457               if { stop when this method does not exist in a parent }
458                  (currnode.def.vmtentries.count<=i) then
459                 break;
460 
461               if not assigned(currnode.def.vmcallstaticinfo) then
462                 currnode.def.vmcallstaticinfo:=allocmem(currnode.def.vmtentries.count*sizeof(tvmcallstatic));
463               { if this method cannot be called, we can just mark it as
464                 unreachable. This will cause its static name to be set to
465                 FPC_ABSTRACTERROR later on. Exception: published methods are
466                 always reachable (via RTTI).
467               }
468               if (pd.visibility<>vis_published) and
469                  not(currnode.fcalledvmtmethods.isset(i)) then
470                 begin
471                   currnode.def.vmcallstaticinfo^[i]:=vmcs_unreachable;
472                   currnode:=currnode.parent;
473                 end
474               { same procdef as in all instantiated childs? (yes or don't know) }
475               else if (currnode.def.vmcallstaticinfo^[i] in [vmcs_default,vmcs_yes]) then
476                 begin
477                   { methods in uninstantiated classes can be made static if
478                     they are the same in all instantiated derived classes
479                   }
480                   if ((pvmtentry(currnode.def.vmtentries[i])^.procdef=pd) or
481                       (not currnode.instantiated and
482                        (currnode.def.vmcallstaticinfo^[i]=vmcs_default))) and
483                       not makeallvirtual then
484                     begin
485                       {$IFDEF DEBUG_DEVIRT}
486                       writeln('    marking as static for ',currnode.def.typename);
487                       {$ENDIF}
488                       currnode.def.vmcallstaticinfo^[i]:=vmcs_yes;
489                       { this is in case of a non-instantiated parent of an instantiated child:
490                         the method declared in the child will always be called here
491                       }
492                       pvmtentry(currnode.def.vmtentries[i])^.procdef:=pd;
493                     end
494                   else
495                     begin
496                       {$IFDEF DEBUG_DEVIRT}
497                       writeln('    marking as non-static for ',currnode.def.typename);
498                       {$ENDIF}
499                       { this vmt entry must also remain virtual for all parents }
500                       makeallvirtual:=true;
501                       currnode.def.vmcallstaticinfo^[i]:=vmcs_no;
502                     end;
503                   currnode:=currnode.parent;
504                 end
505               else if (currnode.def.vmcallstaticinfo^[i]=vmcs_no) then
506                 begin
507                   {$IFDEF DEBUG_DEVIRT}
508                   writeln('    not processing parents, already non-static for ',currnode.def.typename);
509                   {$ENDIF}
510                   { parents are already set to vmcs_no, so no need to continue }
511                   currnode:=nil;
512                 end
513               else
514                 currnode:=currnode.parent;
515             until not assigned(currnode) or
516                   not assigned(currnode.def);
517           end;
518       end;
519 
520 
521     procedure tinheritancetree.optimizevirtualmethods;
522       begin
523         foreachleafnode(@markvmethods,nil);
524       end;
525 
526 
527     procedure tinheritancetree.printobjectvmtinfo(node: tinheritancetreenode; arg: pointer);
528       var
529         i,
530         totaldevirtualised,
531         totalvirtual,
532         totalunreachable: ptrint;
533       begin
534         totaldevirtualised:=0;
535         totalvirtual:=0;
536         totalunreachable:=0;
537         writeln(node.def.typename);
538         if (node.def.vmtentries.count=0) then
539           begin
540             writeln('  No virtual methods!');
541             exit;
542           end;
543         for i:=0 to node.def.vmtentries.count-1 do
544           if (po_virtualmethod in pvmtentry(node.def.vmtentries[i])^.procdef.procoptions) then
545             begin
546               inc(totalvirtual);
547               if (node.def.vmcallstaticinfo^[i]=vmcs_yes) then
548                 begin
549                   inc(totaldevirtualised);
550                   writeln('  Devirtualised: ',pvmtentry(node.def.vmtentries[i])^.procdef.typename);
551                 end
552               else if (node.def.vmcallstaticinfo^[i]=vmcs_unreachable) then
553                 begin
554                   inc(totalunreachable);
555                   writeln('   Unreachable: ',pvmtentry(node.def.vmtentries[i])^.procdef.typename);
556                 end;
557             end;
558         writeln('Total devirtualised/unreachable/all: ',totaldevirtualised,'/',totalunreachable,'/',totalvirtual);
559         writeln;
560       end;
561 
562 
563     procedure tinheritancetree.addcalledvmtentries(node: tinheritancetreenode; arg: pointer);
564       var
565         vmtentries: tbitset absolute arg;
566       begin
567         node.fcalledvmtmethods.addset(vmtentries);
568       end;
569 
570 
571     procedure tinheritancetree.printvmtinfo;
572       begin
573         foreachnode(@printobjectvmtinfo,nil);
574       end;
575 
576 
577     { helper routines: decompose an object & procdef combo into a unitname, class name and vmtentry number
578       (unit name where the objectdef is declared, class name of the objectdef, vmtentry number of the
579        procdef -- procdef does not necessarily belong to objectdef, it may also belong to a descendant
580        or parent). classprefix is set in case of nested classes.
581     }
582 
583     procedure defunitclassname(objdef: tobjectdef; out unitname, classname: pshortstring; out classprefix: shortstring);
584       const
585         mainprogname: string[2] = 'P$';
586       var
587         mainsymtab,
588         objparentsymtab : tsymtable;
589       begin
590         objparentsymtab:=objdef.symtable;
591         mainsymtab:=objparentsymtab.defowner.owner;
592         classprefix:='';
593         while mainsymtab.symtabletype in [recordsymtable,objectsymtable] do
594           begin
595             classprefix:=mainsymtab.name^+'.'+classprefix;
596             mainsymtab:=mainsymtab.defowner.owner;
597           end;
598         { main symtable must be static or global }
599         if not(mainsymtab.symtabletype in [staticsymtable,globalsymtable]) then
600          internalerror(200204177);
601         if (TSymtable(main_module.localsymtable)=mainsymtab) and
602             (not main_module.is_unit) then
603            { same convention as for mangled names }
604           unitname:=@mainprogname
605         else
606           unitname:=mainsymtab.name;
607         classname:=tobjectdef(objparentsymtab.defowner).objname;
608       end;
609 
610 
611     procedure defsdecompose(objdef: tobjectdef; procdef: tprocdef; out unitname, classname: pshortstring; out classprefix: shortstring; out vmtentry: longint);
612       begin
613         defunitclassname(objdef,unitname,classname,classprefix);
614         vmtentry:=procdef.extnumber;
615         { if it's $ffff, this is not a valid virtual method }
616         if (vmtentry=$ffff) then
617           internalerror(2008100509);
618       end;
619 
620 
621    { tclassdevirtinfo }
622 
623     constructor tclassdevirtinfo.create(hashobjectlist:tfphashobjectlist;const n: shortstring; instantiated: boolean);
624       begin
625         inherited create(hashobjectlist,n);
626         finstantiated:=instantiated;
627         fstaticmethodnames:=tfplist.create;
628       end;
629 
630     destructor tclassdevirtinfo.destroy;
631       var
632         i: longint;
633       begin
634         for i:=0 to fstaticmethodnames.count-1 do
635           if assigned(fstaticmethodnames[i]) then
636             freemem(fstaticmethodnames[i]);
637         fstaticmethodnames.free;
638         inherited destroy;
639       end;
640 
641     procedure tclassdevirtinfo.addstaticmethod(vmtindex: longint;
642       const replacementname: shortstring);
643       begin
644         if (vmtindex>=fstaticmethodnames.count) then
645           fstaticmethodnames.Count:=vmtindex+10;
646         fstaticmethodnames[vmtindex]:=stringdup(replacementname);
647       end;
648 
tclassdevirtinfo.isstaticvmtentrynull649     function tclassdevirtinfo.isstaticvmtentry(vmtindex: longint; out
650       replacementname: pshortstring): boolean;
651       begin
652          result:=false;
653          if (vmtindex>=fstaticmethodnames.count) then
654            exit;
655 
656          replacementname:=fstaticmethodnames[vmtindex];
657          result:=assigned(replacementname);
658       end;
659 
660     { tunitdevirtinfo }
661 
662     constructor tunitdevirtinfo.create(hashobjectlist:tfphashobjectlist;const n: shortstring);
663       begin
664         inherited create(hashobjectlist,n);
665         fclasses:=tfphashobjectlist.create(true);
666       end;
667 
668     destructor tunitdevirtinfo.destroy;
669       begin
670         fclasses.free;
671         inherited destroy;
672       end;
673 
tunitdevirtinfo.addclassnull674     function tunitdevirtinfo.addclass(const n: shortstring; instantiated: boolean): tclassdevirtinfo;
675       begin
676         result:=findclass(n);
677         { can't have two classes with the same name in a single unit }
678         if assigned(result) then
679           internalerror(2008100501);
680         result:=tclassdevirtinfo.create(fclasses,n,instantiated);
681       end;
682 
tunitdevirtinfo.findclassnull683     function tunitdevirtinfo.findclass(const n: shortstring): tclassdevirtinfo;
684       begin
685         result:=tclassdevirtinfo(fclasses.find(n));
686       end;
687 
688 
689     { tprogdevirtinfo }
690 
691     procedure tprogdevirtinfo.converttreenode(node: tinheritancetreenode; arg: pointer);
692       var
693         i: longint;
694         classprefix: shortstring;
695         unitid, classid: pshortstring;
696         unitdevirtinfo: tunitdevirtinfo;
697         classdevirtinfo: tclassdevirtinfo;
698       begin
699         if (not node.instantiated) and
700            (node.def.vmtentries.count=0) then
701           exit;
702         { always add a class entry for an instantiated class, so we can
703           fill the vmt's of non-instantiated classes with calls to
704           FPC_ABSTRACTERROR during the optimisation phase
705         }
706         defunitclassname(node.def,unitid,classid,classprefix);
707         unitdevirtinfo:=addunitifnew(unitid^);
708         classdevirtinfo:=unitdevirtinfo.addclass(classprefix+classid^,node.instantiated);
709         if (node.def.vmtentries.count=0) then
710           exit;
711         for i:=0 to node.def.vmtentries.count-1 do
712           if (po_virtualmethod in pvmtentry(node.def.vmtentries[i])^.procdef.procoptions) then
713             case node.def.vmcallstaticinfo^[i] of
714               vmcs_yes:
715                 begin
716                   { add info about devirtualised vmt entry }
717                   classdevirtinfo.addstaticmethod(i,pvmtentry(node.def.vmtentries[i])^.procdef.mangledname);
718                 end;
719               vmcs_unreachable:
720                 begin
721                   { static reference to FPC_ABSTRACTERROR }
722                   classdevirtinfo.addstaticmethod(i,'FPC_ABSTRACTERROR');
723                 end;
724             end;
725       end;
726 
727 
728     constructor tprogdevirtinfo.create;
729       begin
730         inherited create;
731       end;
732 
733 
734     destructor tprogdevirtinfo.destroy;
735       begin
736         funits.free;
737         inherited destroy;
738       end;
739 
740 
tprogdevirtinfo.getwpotypenull741     class function tprogdevirtinfo.getwpotype: twpotype;
742       begin
743         result:=wpo_devirtualization_context_insensitive;
744       end;
745 
746 
tprogdevirtinfo.generatesinfoforwposwitchesnull747     class function tprogdevirtinfo.generatesinfoforwposwitches: twpoptimizerswitches;
748       begin
749         result:=[cs_wpo_devirtualize_calls,cs_wpo_optimize_vmts];
750       end;
751 
752 
tprogdevirtinfo.performswpoforswitchesnull753     class function tprogdevirtinfo.performswpoforswitches: twpoptimizerswitches;
754       begin
755         result:=[cs_wpo_devirtualize_calls,cs_wpo_optimize_vmts];
756       end;
757 
758 
tprogdevirtinfo.sectionnamenull759     class function tprogdevirtinfo.sectionname: shortstring;
760       begin
761         result:=DEVIRT_SECTION_NAME;
762       end;
763 
764 
765     procedure tprogdevirtinfo.constructfromcompilerstate;
766       var
767         hp: tmodule;
768         i: longint;
769         inheritancetree: tinheritancetree;
770       begin
771          { register all instantiated class/object types }
772          hp:=tmodule(loaded_units.first);
773          while assigned(hp) do
774           begin
775             if assigned(hp.wpoinfo.createdobjtypes) then
776               for i:=0 to hp.wpoinfo.createdobjtypes.count-1 do
777                 tdef(hp.wpoinfo.createdobjtypes[i]).register_created_object_type;
778             if assigned(hp.wpoinfo.createdclassrefobjtypes) then
779               for i:=0 to hp.wpoinfo.createdclassrefobjtypes.count-1 do
780                 tobjectdef(hp.wpoinfo.createdclassrefobjtypes[i]).register_created_classref_type;
781             if assigned(hp.wpoinfo.maybecreatedbyclassrefdeftypes) then
782               for i:=0 to hp.wpoinfo.maybecreatedbyclassrefdeftypes.count-1 do
783                 tobjectdef(hp.wpoinfo.maybecreatedbyclassrefdeftypes[i]).register_maybe_created_object_type;
784             hp:=tmodule(hp.next);
785           end;
786          inheritancetree:=tinheritancetree.create;
787 
788          { add all constructed class/object types to the tree }
789 {$IFDEF DEBUG_DEVIRT}
790          writeln('constructed object/class/classreftypes in ',current_module.realmodulename^);
791 {$ENDIF}
792          for i := 0 to current_module.wpoinfo.createdobjtypes.count-1 do
793            begin
794              inheritancetree.registerinstantiatedobjdef(tdef(current_module.wpoinfo.createdobjtypes[i]));
795 {$IFDEF DEBUG_DEVIRT}
796              write('  ',tdef(current_module.wpoinfo.createdobjtypes[i]).GetTypeName);
797 {$ENDIF}
798              case tdef(current_module.wpoinfo.createdobjtypes[i]).typ of
799                objectdef:
800                  case tobjectdef(current_module.wpoinfo.createdobjtypes[i]).objecttype of
801                    odt_object:
802 {$IFDEF DEBUG_DEVIRT}
803                      writeln(' (object)')
804 {$ENDIF}
805                      ;
806                    odt_class:
807 {$IFDEF DEBUG_DEVIRT}
808                      writeln(' (class)')
809 {$ENDIF}
810                      ;
811                    else
812                      internalerror(2008092101);
813                  end;
814                else
815                  internalerror(2008092102);
816              end;
817            end;
818 
819          { register all instantiated classrefdefs with the tree }
820          for i := 0 to current_module.wpoinfo.createdclassrefobjtypes.count-1 do
821            begin
822              inheritancetree.registerinstantiatedclassrefdef(tdef(current_module.wpoinfo.createdclassrefobjtypes[i]));
823 {$IFDEF DEBUG_DEVIRT}
824              write('  Class Of ',tdef(current_module.wpoinfo.createdclassrefobjtypes[i]).GetTypeName);
825 {$ENDIF}
826              case tdef(current_module.wpoinfo.createdclassrefobjtypes[i]).typ of
827                objectdef:
828 {$IFDEF DEBUG_DEVIRT}
829                  writeln(' (classrefdef)')
830 {$ENDIF}
831                  ;
832                else
833                  internalerror(2008101101);
834              end;
835            end;
836 
837 
838          { now add all objectdefs that are referred somewhere (via a
839            loadvmtaddr node) and that are derived from an instantiated
840            classrefdef to the tree (as they can, in theory, all
841            be instantiated as well)
842          }
843          for i := 0 to current_module.wpoinfo.maybecreatedbyclassrefdeftypes.count-1 do
844            begin
845              inheritancetree.checkforclassrefinheritance(tdef(current_module.wpoinfo.maybecreatedbyclassrefdeftypes[i]));
846 {$IFDEF DEBUG_DEVIRT}
847              write('  Class Of ',tdef(current_module.wpoinfo.maybecreatedbyclassrefdeftypes[i]).GetTypeName);
848 {$ENDIF}
849              case tdef(current_module.wpoinfo.maybecreatedbyclassrefdeftypes[i]).typ of
850                objectdef:
851 {$IFDEF DEBUG_DEVIRT}
852                  writeln(' (classrefdef)')
853 {$ENDIF}
854                  ;
855                else
856                  internalerror(2008101101);
857              end;
858            end;
859 
860          { add info about called virtual methods }
861          hp:=tmodule(loaded_units.first);
862          while assigned(hp) do
863           begin
864             if assigned(hp.wpoinfo.calledvmtentries) then
865               for i:=0 to hp.wpoinfo.calledvmtentries.count-1 do
866                 inheritancetree.registercalledvmtentries(tcalledvmtentries(hp.wpoinfo.calledvmtentries[i]));
867             hp:=tmodule(hp.next);
868           end;
869 
870 
871          inheritancetree.optimizevirtualmethods;
872 {$ifdef DEBUG_DEVIRT}
873          inheritancetree.printvmtinfo;
874 {$endif DEBUG_DEVIRT}
875          inheritancetree.foreachnode(@converttreenode,nil);
876          inheritancetree.free;
877       end;
878 
879 
tprogdevirtinfo.addunitifnewnull880     function tprogdevirtinfo.addunitifnew(const n: shortstring): tunitdevirtinfo;
881       begin
882         if assigned(funits) then
883           result:=findunit(n)
884         else
885           begin
886             funits:=tfphashobjectlist.create;
887             result:=nil;
888           end;
889         if not assigned(result) then
890           begin
891             result:=tunitdevirtinfo.create(funits,n);
892           end;
893       end;
894 
895 
tprogdevirtinfo.findunitnull896     function tprogdevirtinfo.findunit(const n: shortstring): tunitdevirtinfo;
897       begin
898         result:=tunitdevirtinfo(funits.find(n));
899       end;
900 
901 
902     procedure tprogdevirtinfo.loadfromwpofilesection(reader: twposectionreaderintf);
903       var
904         unitid,
905         classid,
906         vmtentryname: string;
907         vmttype: string[15];
908         vmtentrynrstr: string[7];
909         classinstantiated: string[1];
910         vmtentry, error: longint;
911         unitdevirtinfo: tunitdevirtinfo;
912         classdevirtinfo: tclassdevirtinfo;
913         instantiated: boolean;
914       begin
915         { format:
916             # unitname^
917             unit1^
918             # classname&
919             class1&
920             # instantiated?
921             1
922             # vmt type (base or some interface)
923             basevmt
924             # vmt entry nr
925             0
926             # name of routine to call instead
927             staticvmtentryforslot0
928             5
929             staticvmtentryforslot5
930             intfvmt1
931             0
932             staticvmtentryforslot0
933 
934             # non-instantiated class (but if we encounter a variable of this
935             # type, we can optimise class to vmtentry 1)
936             class2&
937             0
938             basevmt
939             1
940             staticvmtentryforslot1
941 
942             # instantiated class without optimisable virtual methods
943             class3&
944             1
945 
946             unit2^
947             1
948             class3&
949             ...
950 
951             currently, only basevmt is supported (no interfaces yet)
952         }
953         { could be empty if no classes or so }
954         if not reader.sectiongetnextline(unitid) then
955           exit;
956         repeat
957           if (unitid='') or
958              (unitid[length(unitid)]<>'^') then
959             internalerror(2008100502);
960           { cut off the trailing ^ }
961           setlength(unitid,length(unitid)-1);
962           unitdevirtinfo:=addunitifnew(unitid);
963           { now read classes }
964           if not reader.sectiongetnextline(classid) then
965             internalerror(2008100505);
966           repeat
967             if (classid='') or
968                (classid[length(classid)]<>'&') then
969               internalerror(2008100503);
970             { instantiated? }
971             if not reader.sectiongetnextline(classinstantiated) then
972               internalerror(2008101901);
973             instantiated:=classinstantiated='1';
974             { cut off the trailing & }
975             setlength(classid,length(classid)-1);
976             classdevirtinfo:=unitdevirtinfo.addclass(classid,instantiated);
977             { last class could be an instantiated class without any
978                optimisable methods. }
979             if not reader.sectiongetnextline(vmttype) then
980               exit;
981             { any optimisable virtual methods? }
982             if (vmttype<>'') then
983               begin
984                 { interface info is not yet supported }
985                 if (vmttype<>'basevmt') then
986                   internalerror(2008100507);
987                 { read all vmt entries for this class }
988                 while reader.sectiongetnextline(vmtentrynrstr) and
989                       (vmtentrynrstr<>'') do
990                   begin
991                     val(vmtentrynrstr,vmtentry,error);
992                     if (error<>0) then
993                       internalerror(2008100504);
994                     if not reader.sectiongetnextline(vmtentryname) or
995                        (vmtentryname='') then
996                       internalerror(2008100508);
997                     classdevirtinfo.addstaticmethod(vmtentry,vmtentryname);
998                   end;
999               end;
1000             { end of section -> exit }
1001             if not(reader.sectiongetnextline(classid)) then
1002               exit;
1003           until (classid='') or
1004                 (classid[length(classid)]='^');
1005           { next unit, or error }
1006           unitid:=classid;
1007         until false;
1008       end;
1009 
1010 
1011     procedure tprogdevirtinfo.documentformat(writer: twposectionwriterintf);
1012       begin
1013         writer.sectionputline('# section format:');
1014         writer.sectionputline('# unit1^');
1015         writer.sectionputline('# class1&                ; classname&');
1016         writer.sectionputline('# 1                      ; instantiated or not');
1017         writer.sectionputline('# basevmt                ; vmt type (base or some interface)');
1018         writer.sectionputline('# # vmt entry nr');
1019         writer.sectionputline('# 0                      ; vmt entry nr');
1020         writer.sectionputline('# staticvmtentryforslot0 ; name or routine to call instead');
1021         writer.sectionputline('# 5');
1022         writer.sectionputline('# staticvmtentryforslot5');
1023         writer.sectionputline('# intfvmt1');
1024         writer.sectionputline('# 0');
1025         writer.sectionputline('# staticvmtentryforslot0');
1026         writer.sectionputline('#');
1027         writer.sectionputline('# class2&');
1028         writer.sectionputline('# 0                      ; non-instantiated class (can be variables of this type, e.g. TObject)');
1029         writer.sectionputline('# basevmt');
1030         writer.sectionputline('# 1');
1031         writer.sectionputline('# staticvmtentryforslot1');
1032         writer.sectionputline('#');
1033         writer.sectionputline('# class3&                ; instantiated class without optimisable virtual methods');
1034         writer.sectionputline('# 1');
1035         writer.sectionputline('#');
1036         writer.sectionputline('# unit2^');
1037         writer.sectionputline('# 1');
1038         writer.sectionputline('# class3&');
1039         writer.sectionputline('# ...');
1040         writer.sectionputline('#');
1041         writer.sectionputline('# currently, only basevmt is supported (no interfaces yet)');
1042         writer.sectionputline('#');
1043       end;
1044 
1045 
1046     procedure tprogdevirtinfo.storewpofilesection(writer: twposectionwriterintf);
1047       var
1048         unitcount,
1049         classcount,
1050         vmtentrycount: longint;
1051         unitdevirtinfo: tunitdevirtinfo;
1052         classdevirtinfo: tclassdevirtinfo;
1053         first: boolean;
1054       begin
1055         writer.startsection(DEVIRT_SECTION_NAME);
1056         { if there are no optimised virtual methods, we have stored no info }
1057         if not assigned(funits) then
1058           exit;
1059         documentformat(writer);
1060         for unitcount:=0 to funits.count-1 do
1061           begin
1062             unitdevirtinfo:=tunitdevirtinfo(funits[unitcount]);
1063             writer.sectionputline(unitdevirtinfo.name+'^');
1064             for classcount:=0 to unitdevirtinfo.fclasses.count-1 do
1065               begin
1066                 classdevirtinfo:=tclassdevirtinfo(tunitdevirtinfo(funits[unitcount]).fclasses[classcount]);
1067                 writer.sectionputline(classdevirtinfo.name+'&');
1068                 writer.sectionputline(tostr(ord(classdevirtinfo.instantiated)));
1069                 first:=true;
1070                 for vmtentrycount:=0 to classdevirtinfo.fstaticmethodnames.count-1 do
1071                   if assigned(classdevirtinfo.fstaticmethodnames[vmtentrycount]) then
1072                     begin
1073                       if first then
1074                         begin
1075                           writer.sectionputline('basevmt');
1076                           first:=false;
1077                         end;
1078                       writer.sectionputline(tostr(vmtentrycount));
1079                       writer.sectionputline(pshortstring(classdevirtinfo.fstaticmethodnames[vmtentrycount])^);
1080                     end;
1081                 writer.sectionputline('');
1082               end;
1083           end;
1084       end;
1085 
1086 
tprogdevirtinfo.getstaticnamenull1087     function tprogdevirtinfo.getstaticname(forvmtentry: boolean; objdef, procdef: tdef; out staticname: TSymStr): boolean;
1088       var
1089         unitid,
1090         classid,
1091         newname: pshortstring;
1092         unitdevirtinfo: tunitdevirtinfo;
1093         classdevirtinfo: tclassdevirtinfo;
1094         vmtentry: longint;
1095         realobjdef: tobjectdef;
1096         classprefix: shortstring;
1097       begin
1098          { if we don't have any devirtualisation info, exit }
1099          if not assigned(funits) then
1100            begin
1101              result:=false;
1102              exit
1103            end;
1104          { class methods are in the regular vmt, so we can handle classrefs
1105            the same way as plain objectdefs
1106          }
1107          if (objdef.typ=classrefdef) then
1108            realobjdef:=tobjectdef(tclassrefdef(objdef).pointeddef)
1109          else if (objdef.typ=objectdef) and
1110             (tobjectdef(objdef).objecttype in [odt_class,odt_object]) then
1111            realobjdef:=tobjectdef(objdef)
1112          else
1113            begin
1114              { we don't support interfaces yet }
1115              result:=false;
1116              exit;
1117            end;
1118 
1119          { if it's for a vmtentry of an objdef and the objdef is
1120            not instantiated, then we can fill the vmt with pointers
1121            to FPC_ABSTRACTERROR, except for published methods
1122            (these can be called via rtti, so always have to point
1123             to the original method)
1124          }
1125          if forvmtentry and
1126             (tprocdef(procdef).visibility=vis_published) then
1127            begin
1128              result:=false;
1129              exit;
1130            end;
1131 
1132          { get the component names for the class/procdef combo }
1133          defsdecompose(realobjdef,tprocdef(procdef),unitid,classid,classprefix,vmtentry);
1134 
1135          { If we don't have information about a particular unit/class/method,
1136            it means that such class cannot be instantiated. So if we are
1137            looking up information for a vmt entry, we can always safely return
1138            FPC_ABSTRACTERROR if we do not find anything, unless it's a
1139            published method (but those are handled already above) or a
1140            class method (can be called even if the class is not instantiated).
1141          }
1142          result:=
1143            forvmtentry and
1144            not(po_classmethod in tprocdef(procdef).procoptions);
1145          staticname:='FPC_ABSTRACTERROR';
1146 
1147          { do we have any info for this unit? }
1148          unitdevirtinfo:=findunit(unitid^);
1149          if not assigned(unitdevirtinfo) then
1150            exit;
1151          { and for this class? }
1152          classdevirtinfo:=unitdevirtinfo.findclass(classprefix+classid^);
1153          if not assigned(classdevirtinfo) then
1154            exit;
1155          if forvmtentry and
1156             (objdef.typ=objectdef) and
1157             not classdevirtinfo.instantiated and
1158             { virtual class methods can be called even if the class is not instantiated }
1159             not(po_classmethod in tprocdef(procdef).procoptions) then
1160            begin
1161              { already set above
1162                staticname:='FPC_ABSTRACTERROR';
1163              }
1164              result:=true;
1165            end
1166          else
1167            begin
1168              { now check whether it can be devirtualised, and if so to what }
1169              result:=classdevirtinfo.isstaticvmtentry(vmtentry,newname);
1170              if result then
1171                staticname:=newname^;
1172            end;
1173       end;
1174 
1175 
1176 
tprogdevirtinfo.staticnameforcallingvirtualmethodnull1177     function tprogdevirtinfo.staticnameforcallingvirtualmethod(objdef, procdef: tdef; out staticname: TSymStr): boolean;
1178       begin
1179         result:=getstaticname(false,objdef,procdef,staticname);
1180       end;
1181 
1182 
tprogdevirtinfo.staticnameforvmtentrynull1183     function tprogdevirtinfo.staticnameforvmtentry(objdef, procdef: tdef; out staticname: TSymStr): boolean;
1184       begin
1185         result:=getstaticname(true,objdef,procdef,staticname);
1186       end;
1187 
1188 end.
1189