optvirt.pas 43 KB

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