2
0

optvirt.pas 43 KB

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