optvirt.pas 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951
  1. {
  2. Virtual methods optimizations (devirtualization)
  3. Copyright (c) 2008 by Jonas Maebe
  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. finstantiated: boolean;
  35. function getchild(index: longint): tinheritancetreenode;
  36. public
  37. constructor create(_parent: tinheritancetreenode; _def: tobjectdef; _instantiated: boolean);
  38. { destroys both this node and all of its siblings }
  39. destructor destroy; override;
  40. function childcount: longint;
  41. function haschilds: boolean;
  42. property childs[index: longint]: tinheritancetreenode read getchild;
  43. property parent: tinheritancetreenode read fparent;
  44. property def: tobjectdef read fdef;
  45. property instantiated: boolean read finstantiated write finstantiated;
  46. { if def is not yet a child of this node, add it. In all cases, return node containing
  47. this def (either new or existing one
  48. }
  49. function maybeaddchild(_def: tobjectdef; _instantiated: boolean): tinheritancetreenode;
  50. end;
  51. tinheritancetreecallback = procedure(node: tinheritancetreenode; arg: pointer) of object;
  52. tinheritancetree = class
  53. private
  54. { just a regular node with parent = nil }
  55. froots: tinheritancetreenode;
  56. classrefdefs: tfpobjectlist;
  57. procedure foreachnodefromroot(root: tinheritancetreenode; proctocall: tinheritancetreecallback; arg: pointer);
  58. function registerinstantiatedobjectdefrecursive(def: tobjectdef; instantiated: boolean): tinheritancetreenode;
  59. procedure markvmethods(node: tinheritancetreenode; p: pointer);
  60. procedure printobjectvmtinfo(node: tinheritancetreenode; arg: pointer);
  61. procedure setinstantiated(node: tinheritancetreenode; arg: pointer);
  62. public
  63. constructor create;
  64. destructor destroy; override;
  65. { adds an objectdef (the def itself, and all of its parents that do not yet exist) to
  66. the tree, and returns the leaf node
  67. }
  68. procedure registerinstantiateddef(def: tdef);
  69. procedure checkforclassrefinheritance(def: tdef);
  70. procedure foreachnode(proctocall: tinheritancetreecallback; arg: pointer);
  71. procedure foreachleafnode(proctocall: tinheritancetreecallback; arg: pointer);
  72. procedure optimizevirtualmethods;
  73. procedure printvmtinfo;
  74. end;
  75. { devirtualisation information for a class }
  76. tclassdevirtinfo = class(tfphashobject)
  77. private
  78. { array (indexed by vmt entry nr) of replacement statically callable method names }
  79. fstaticmethodnames: tfplist;
  80. function isstaticvmtentry(vmtindex: longint; out replacementname: pshortstring): boolean;
  81. public
  82. constructor create(hashobjectlist:tfphashobjectlist;const n: shortstring);reintroduce;
  83. destructor destroy; override;
  84. procedure addstaticmethod(vmtindex: longint; const replacementname: shortstring);
  85. end;
  86. { devirtualisation information for all classes in a unit }
  87. tunitdevirtinfo = class(tfphashobject)
  88. private
  89. { hashtable of classes }
  90. fclasses: tfphashobjectlist;
  91. public
  92. constructor create(hashobjectlist:tfphashobjectlist;const n: shortstring);reintroduce;
  93. destructor destroy; override;
  94. function addclass(const n: shortstring): tclassdevirtinfo;
  95. function findclass(const n: shortstring): tclassdevirtinfo;
  96. end;
  97. { defvirtualisation information for all units in a program }
  98. { tprogdevirtinfo }
  99. tprogdevirtinfo = class(twpodevirtualisationhandler)
  100. private
  101. { hashtable of tunitdevirtinfo (which contain tclassdevirtinfo) }
  102. funits: tfphashobjectlist;
  103. procedure converttreenode(node: tinheritancetreenode; arg: pointer);
  104. function addunitifnew(const n: shortstring): tunitdevirtinfo;
  105. function findunit(const n: shortstring): tunitdevirtinfo;
  106. public
  107. constructor create; override;
  108. destructor destroy; override;
  109. class function getwpotype: twpotype; override;
  110. class function generatesinfoforwposwitches: twpoptimizerswitches; override;
  111. class function performswpoforswitches: twpoptimizerswitches; override;
  112. { information collection }
  113. procedure constructfromcompilerstate; override;
  114. procedure storewpofilesection(writer: twposectionwriterintf); override;
  115. { infromation providing }
  116. procedure loadfromwpofilesection(reader: twposectionreaderintf); override;
  117. function staticnameforvirtualmethod(objdef, procdef: tdef; out staticname: string): boolean; override;
  118. end;
  119. implementation
  120. uses
  121. cutils,
  122. fmodule,
  123. symconst,
  124. symbase,
  125. symtable,
  126. nobj,
  127. verbose;
  128. const
  129. DEVIRT_SECTION_NAME = 'contextinsensitive_devirtualization';
  130. { *************************** tinheritancetreenode ************************* }
  131. constructor tinheritancetreenode.create(_parent: tinheritancetreenode; _def: tobjectdef; _instantiated: boolean);
  132. begin
  133. fparent:=_parent;
  134. fdef:=_def;
  135. finstantiated:=_instantiated;
  136. end;
  137. destructor tinheritancetreenode.destroy;
  138. begin
  139. { fchilds owns its members, so it will free them too }
  140. fchilds.free;
  141. inherited destroy;
  142. end;
  143. function tinheritancetreenode.childcount: longint;
  144. begin
  145. if assigned(fchilds) then
  146. result:=fchilds.count
  147. else
  148. result:=0;
  149. end;
  150. function tinheritancetreenode.haschilds: boolean;
  151. begin
  152. result:=assigned(fchilds)
  153. end;
  154. function tinheritancetreenode.getchild(index: longint): tinheritancetreenode;
  155. begin
  156. result:=tinheritancetreenode(fchilds[index]);
  157. end;
  158. function tinheritancetreenode.maybeaddchild(_def: tobjectdef; _instantiated: boolean): tinheritancetreenode;
  159. var
  160. i: longint;
  161. begin
  162. { sanity check }
  163. if assigned(_def.childof) then
  164. begin
  165. if (_def.childof<>def) then
  166. internalerror(2008092201);
  167. end
  168. else if assigned(fparent) then
  169. internalerror(2008092202);
  170. if not assigned(fchilds) then
  171. fchilds:=tfpobjectlist.create(true);
  172. { def already a child -> return }
  173. for i := 0 to fchilds.count-1 do
  174. if (tinheritancetreenode(fchilds[i]).def=_def) then
  175. begin
  176. result:=tinheritancetreenode(fchilds[i]);
  177. result.finstantiated:=result.finstantiated or _instantiated;
  178. exit;
  179. end;
  180. { not found, add new child }
  181. result:=tinheritancetreenode.create(self,_def,_instantiated);
  182. fchilds.add(result);
  183. end;
  184. { *************************** tinheritancetree ************************* }
  185. constructor tinheritancetree.create;
  186. begin
  187. froots:=tinheritancetreenode.create(nil,nil,false);
  188. classrefdefs:=tfpobjectlist.create(false);
  189. end;
  190. destructor tinheritancetree.destroy;
  191. begin
  192. froots.free;
  193. classrefdefs.free;
  194. inherited destroy;
  195. end;
  196. function tinheritancetree.registerinstantiatedobjectdefrecursive(def: tobjectdef; instantiated: boolean): tinheritancetreenode;
  197. begin
  198. if assigned(def.childof) then
  199. begin
  200. { recursively add parent, of which we have no info about whether or not it is
  201. instantiated at this point -> default to false (will be overridden by "true"
  202. if necessary)
  203. }
  204. result:=registerinstantiatedobjectdefrecursive(def.childof,false);
  205. { and add ourselves to the parent }
  206. result:=result.maybeaddchild(def,instantiated);
  207. end
  208. else
  209. { add ourselves to the roots }
  210. result:=froots.maybeaddchild(def,instantiated);
  211. end;
  212. procedure tinheritancetree.registerinstantiateddef(def: tdef);
  213. begin
  214. { add the def }
  215. if (def.typ=objectdef) then
  216. registerinstantiatedobjectdefrecursive(tobjectdef(def),true)
  217. else if (def.typ=classrefdef) then
  218. classrefdefs.add(def)
  219. else
  220. internalerror(2008092401);
  221. end;
  222. procedure tinheritancetree.checkforclassrefinheritance(def: tdef);
  223. var
  224. i: longint;
  225. begin
  226. if (def.typ=objectdef) then
  227. begin
  228. for i:=0 to classrefdefs.count-1 do
  229. if tobjectdef(def).is_related(tclassrefdef(classrefdefs[i]).pointeddef) then
  230. begin
  231. registerinstantiateddef(def);
  232. exit;
  233. end;
  234. end;
  235. end;
  236. procedure tinheritancetree.setinstantiated(node: tinheritancetreenode; arg: pointer);
  237. var
  238. classrefdef: tclassrefdef absolute arg;
  239. begin
  240. if not(node.instantiated) then
  241. begin
  242. node.instantiated:=true;
  243. {$IFDEF DEBUG_DEVIRT}
  244. writeln('Marked ',node.def.typename,' as instantiated because instantiated ',classrefdef.typename);
  245. {$ENDIF}
  246. end;
  247. end;
  248. procedure tinheritancetree.foreachnodefromroot(root: tinheritancetreenode; proctocall: tinheritancetreecallback; arg: pointer);
  249. procedure process(const node: tinheritancetreenode);
  250. var
  251. i: longint;
  252. begin
  253. for i:=0 to node.childcount-1 do
  254. if node.childs[i].haschilds then
  255. begin
  256. proctocall(node.childs[i],arg);
  257. process(node.childs[i])
  258. end
  259. else
  260. proctocall(node.childs[i],arg);
  261. end;
  262. begin
  263. process(root);
  264. end;
  265. procedure tinheritancetree.foreachnode(proctocall: tinheritancetreecallback; arg: pointer);
  266. begin
  267. foreachnodefromroot(froots,proctocall,arg);
  268. end;
  269. procedure tinheritancetree.foreachleafnode(proctocall: tinheritancetreecallback; arg: pointer);
  270. procedure process(const node: tinheritancetreenode);
  271. var
  272. i: longint;
  273. begin
  274. for i:=0 to node.childcount-1 do
  275. if node.childs[i].haschilds then
  276. process(node.childs[i])
  277. else
  278. proctocall(node.childs[i],arg);
  279. end;
  280. begin
  281. process(froots);
  282. end;
  283. procedure tinheritancetree.markvmethods(node: tinheritancetreenode; p: pointer);
  284. var
  285. currnode: tinheritancetreenode;
  286. vmtbuilder: tvmtbuilder;
  287. pd: tobject;
  288. i: longint;
  289. makeallvirtual: boolean;
  290. begin
  291. {$IFDEF DEBUG_DEVIRT}
  292. writeln('processing leaf node ',node.def.typename);
  293. {$ENDIF}
  294. { todo: also process interfaces (ImplementedInterfaces) }
  295. if not assigned(node.def.vmtentries) then
  296. begin
  297. vmtbuilder:=tvmtbuilder.create(node.def);
  298. vmtbuilder.generate_vmt(false);
  299. vmtbuilder.free;
  300. { may not have any vmtentries }
  301. if not assigned(node.def.vmtentries) then
  302. exit;
  303. end;
  304. { process all vmt entries for this class/object }
  305. for i:=0 to node.def.vmtentries.count-1 do
  306. begin
  307. currnode:=node;
  308. pd:=currnode.def.vmtentries[i];
  309. { abstract methods cannot be called directly }
  310. if (po_abstractmethod in tprocdef(pd).procoptions) then
  311. continue;
  312. {$IFDEF DEBUG_DEVIRT}
  313. writeln(' method ',tprocdef(pd).typename);
  314. {$ENDIF}
  315. { Now mark all virtual methods static that are the same in parent
  316. classes as in this instantiated child class (only instantiated
  317. classes can be leaf nodes, since only instantiated classes were
  318. added to the tree) as statically callable.
  319. If a first child does not override a parent method while a
  320. a second one does, the first will mark it as statically
  321. callable, but the second will set it to not statically callable.
  322. In the opposite situation, the first will mark it as not
  323. statically callable and the second will leave it alone.
  324. }
  325. makeallvirtual:=false;
  326. repeat
  327. if not assigned(currnode.def.vmtentries) then
  328. begin
  329. vmtbuilder:=tvmtbuilder.create(currnode.def);
  330. vmtbuilder.generate_vmt(false);
  331. vmtbuilder.free;
  332. { may not have any vmtentries }
  333. if not assigned(currnode.def.vmtentries) then
  334. break;
  335. end;
  336. { stop when this method is not yet implemented in a parent }
  337. if (currnode.def.vmtentries.count<=i) then
  338. break;
  339. if not assigned(currnode.def.vmcallstaticinfo) then
  340. currnode.def.vmcallstaticinfo:=allocmem(currnode.def.vmtentries.count*sizeof(tvmcallstatic));
  341. { same procdef as in all instantiated childs? }
  342. if (currnode.def.vmcallstaticinfo^[i] in [vmcs_default,vmcs_yes]) then
  343. begin
  344. { methods in uninstantiated classes can be made static if
  345. they are the same in all instantiated derived classes
  346. }
  347. if ((currnode.def.vmtentries[i]=pd) or
  348. (not currnode.instantiated and
  349. (currnode.def.vmcallstaticinfo^[i]=vmcs_default))) and
  350. not makeallvirtual then
  351. begin
  352. {$IFDEF DEBUG_DEVIRT}
  353. writeln(' marking as static for ',currnode.def.typename);
  354. {$ENDIF}
  355. currnode.def.vmcallstaticinfo^[i]:=vmcs_yes;
  356. { this is in case of a non-instantiated parent of an instantiated child:
  357. the method declared in the child will always be called here
  358. }
  359. currnode.def.vmtentries[i]:=pd;
  360. end
  361. else
  362. begin
  363. {$IFDEF DEBUG_DEVIRT}
  364. writeln(' marking as non-static for ',currnode.def.typename);
  365. {$ENDIF}
  366. makeallvirtual:=true;
  367. currnode.def.vmcallstaticinfo^[i]:=vmcs_no;
  368. end;
  369. currnode:=currnode.parent;
  370. end
  371. else
  372. begin
  373. {$IFDEF DEBUG_DEVIRT}
  374. writeln(' not processing parents, already non-static for ',currnode.def.typename);
  375. {$ENDIF}
  376. { parents are also already set to vmcs_no, so no need to continue }
  377. currnode:=nil;
  378. end;
  379. until not assigned(currnode) or
  380. not assigned(currnode.def);
  381. end;
  382. end;
  383. procedure tinheritancetree.optimizevirtualmethods;
  384. begin
  385. // finalisetree;
  386. foreachleafnode(@markvmethods,nil);
  387. end;
  388. procedure tinheritancetree.printobjectvmtinfo(node: tinheritancetreenode; arg: pointer);
  389. var
  390. i,
  391. totaldevirtualised,
  392. totalvirtual: ptrint;
  393. begin
  394. totaldevirtualised:=0;
  395. totalvirtual:=0;
  396. writeln(node.def.typename);
  397. if not assigned(node.def.vmtentries) then
  398. begin
  399. writeln(' No virtual methods!');
  400. exit;
  401. end;
  402. for i:=0 to node.def.vmtentries.count-1 do
  403. if (po_virtualmethod in tabstractprocdef(node.def.vmtentries[i]).procoptions) then
  404. begin
  405. inc(totalvirtual);
  406. if (node.def.vmcallstaticinfo^[i]=vmcs_yes) then
  407. begin
  408. inc(totaldevirtualised);
  409. writeln(' Devirtualised: ',tabstractprocdef(node.def.vmtentries[i]).typename);
  410. end;
  411. end;
  412. writeln('Total devirtualised: ',totaldevirtualised,'/',totalvirtual);
  413. writeln;
  414. end;
  415. procedure tinheritancetree.printvmtinfo;
  416. begin
  417. foreachnode(@printobjectvmtinfo,nil);
  418. end;
  419. { helper routine: decompose a class/procdef combo into a unitname, class name and vmtentry number }
  420. procedure defsdecompose(objdef: tobjectdef; procdef: tprocdef; out unitname, classname: pshortstring; out vmtentry: longint);
  421. const
  422. mainprogname: string[2] = 'P$';
  423. var
  424. mainsymtab,
  425. objparentsymtab: tsymtable;
  426. begin
  427. objparentsymtab:=objdef.symtable;
  428. mainsymtab:=objparentsymtab.defowner.owner;
  429. { main symtable must be static or global }
  430. if not(mainsymtab.symtabletype in [staticsymtable,globalsymtable]) then
  431. internalerror(200204175);
  432. if (TSymtable(main_module.localsymtable)=mainsymtab) and
  433. (not main_module.is_unit) then
  434. { same convention as for mangled names }
  435. unitname:=@mainprogname
  436. else
  437. unitname:=mainsymtab.name;
  438. classname:=tobjectdef(objparentsymtab.defowner).objname;
  439. vmtentry:=procdef.extnumber;
  440. { if it's $ffff, this is not a valid virtual method }
  441. if (vmtentry=$ffff) then
  442. internalerror(2008100509);
  443. end;
  444. { tclassdevirtinfo }
  445. constructor tclassdevirtinfo.create(hashobjectlist:tfphashobjectlist;const n: shortstring);
  446. begin
  447. inherited create(hashobjectlist,n);
  448. fstaticmethodnames:=tfplist.create;
  449. end;
  450. destructor tclassdevirtinfo.destroy;
  451. var
  452. i: longint;
  453. begin
  454. for i:=0 to fstaticmethodnames.count-1 do
  455. if assigned(fstaticmethodnames[i]) then
  456. freemem(fstaticmethodnames[i]);
  457. fstaticmethodnames.free;
  458. inherited destroy;
  459. end;
  460. procedure tclassdevirtinfo.addstaticmethod(vmtindex: longint;
  461. const replacementname: shortstring);
  462. begin
  463. if (vmtindex>=fstaticmethodnames.count) then
  464. fstaticmethodnames.Count:=vmtindex+10;
  465. fstaticmethodnames[vmtindex]:=stringdup(replacementname);
  466. end;
  467. function tclassdevirtinfo.isstaticvmtentry(vmtindex: longint; out
  468. replacementname: pshortstring): boolean;
  469. begin
  470. result:=false;
  471. if (vmtindex>=fstaticmethodnames.count) then
  472. exit;
  473. replacementname:=fstaticmethodnames[vmtindex];
  474. result:=assigned(replacementname);
  475. end;
  476. { tunitdevirtinfo }
  477. constructor tunitdevirtinfo.create(hashobjectlist:tfphashobjectlist;const n: shortstring);
  478. begin
  479. inherited create(hashobjectlist,n);
  480. fclasses:=tfphashobjectlist.create(true);
  481. end;
  482. destructor tunitdevirtinfo.destroy;
  483. begin
  484. fclasses.free;
  485. inherited destroy;
  486. end;
  487. function tunitdevirtinfo.addclass(const n: shortstring): tclassdevirtinfo;
  488. begin
  489. result:=findclass(n);
  490. { can't have two classes with the same name in a single unit }
  491. if assigned(result) then
  492. internalerror(2008100501);
  493. result:=tclassdevirtinfo.create(fclasses,n);
  494. end;
  495. function tunitdevirtinfo.findclass(const n: shortstring): tclassdevirtinfo;
  496. begin
  497. result:=tclassdevirtinfo(fclasses.find(n));
  498. end;
  499. { tprogdevirtinfo }
  500. procedure tprogdevirtinfo.converttreenode(node: tinheritancetreenode; arg: pointer);
  501. var
  502. i,
  503. vmtentry: longint;
  504. unitid, classid: pshortstring;
  505. unitdevirtinfo: tunitdevirtinfo;
  506. classdevirtinfo: tclassdevirtinfo;
  507. first : boolean;
  508. begin
  509. if not assigned(node.def.vmtentries) then
  510. exit;
  511. first:=true;
  512. for i:=0 to node.def.vmtentries.count-1 do
  513. if (po_virtualmethod in tabstractprocdef(node.def.vmtentries[i]).procoptions) and
  514. (node.def.vmcallstaticinfo^[i]=vmcs_yes) then
  515. begin
  516. if first then
  517. begin
  518. { add necessary entries for the unit and the class }
  519. defsdecompose(node.def,tprocdef(node.def.vmtentries[i]),unitid,classid,vmtentry);
  520. unitdevirtinfo:=addunitifnew(unitid^);
  521. classdevirtinfo:=unitdevirtinfo.addclass(classid^);
  522. first:=false;
  523. end;
  524. { add info about devirtualised vmt entry }
  525. classdevirtinfo.addstaticmethod(i,tprocdef(node.def.vmtentries[i]).mangledname);
  526. end;
  527. end;
  528. constructor tprogdevirtinfo.create;
  529. begin
  530. inherited create;
  531. end;
  532. destructor tprogdevirtinfo.destroy;
  533. begin
  534. funits.free;
  535. inherited destroy;
  536. end;
  537. class function tprogdevirtinfo.getwpotype: twpotype;
  538. begin
  539. result:=wpo_devirtualization_context_insensitive;
  540. end;
  541. class function tprogdevirtinfo.generatesinfoforwposwitches: twpoptimizerswitches;
  542. begin
  543. result:=[cs_wpo_devirtualize_calls];
  544. end;
  545. class function tprogdevirtinfo.performswpoforswitches: twpoptimizerswitches;
  546. begin
  547. result:=[cs_wpo_devirtualize_calls];
  548. end;
  549. procedure reset_all_impl_defs;
  550. procedure reset_used_unit_impl_defs(hp:tmodule);
  551. var
  552. pu : tused_unit;
  553. begin
  554. pu:=tused_unit(hp.used_units.first);
  555. while assigned(pu) do
  556. begin
  557. if not pu.u.is_reset then
  558. begin
  559. { prevent infinte loop for circular dependencies }
  560. pu.u.is_reset:=true;
  561. if assigned(pu.u.localsymtable) then
  562. begin
  563. tstaticsymtable(pu.u.localsymtable).reset_all_defs;
  564. reset_used_unit_impl_defs(pu.u);
  565. end;
  566. end;
  567. pu:=tused_unit(pu.next);
  568. end;
  569. end;
  570. var
  571. hp2 : tmodule;
  572. begin
  573. hp2:=tmodule(loaded_units.first);
  574. while assigned(hp2) do
  575. begin
  576. hp2.is_reset:=false;
  577. hp2:=tmodule(hp2.next);
  578. end;
  579. reset_used_unit_impl_defs(current_module);
  580. end;
  581. procedure tprogdevirtinfo.constructfromcompilerstate;
  582. var
  583. hp: tmodule;
  584. i: longint;
  585. inheritancetree: tinheritancetree;
  586. begin
  587. { the compiler already resets all interface defs after every unit
  588. compilation, but not the implementation defs (because this is only
  589. done for the purpose of writing debug info, and you can never see
  590. a type defined in the implementation of one unit in another unit).
  591. Here, we want to record all classes constructed anywhere in the
  592. program, also if those class(refdef) types are defined in the
  593. implementation of a unit. So reset the state of all defs in
  594. implementation sections before starting the collection process. }
  595. reset_all_impl_defs;
  596. { register all instantiated class/object types }
  597. hp:=tmodule(loaded_units.first);
  598. while assigned(hp) do
  599. begin
  600. if assigned(hp.wpoinfo.createdobjtypes) then
  601. for i:=0 to hp.wpoinfo.createdobjtypes.count-1 do
  602. tdef(hp.wpoinfo.createdobjtypes[i]).register_created_object_type;
  603. hp:=tmodule(hp.next);
  604. end;
  605. inheritancetree:=tinheritancetree.create;
  606. {$IFDEF DEBUG_DEVIRT}
  607. writeln('constructed object/class/classreftypes in ',current_module.realmodulename^);
  608. {$ENDIF}
  609. for i := 0 to current_module.wpoinfo.createdobjtypes.count-1 do
  610. begin
  611. inheritancetree.registerinstantiateddef(tdef(current_module.wpoinfo.createdobjtypes[i]));
  612. {$IFDEF DEBUG_DEVIRT}
  613. write(' ',tdef(current_module.wpoinfo.createdobjtypes[i]).GetTypeName);
  614. {$ENDIF}
  615. case tdef(current_module.wpoinfo.createdobjtypes[i]).typ of
  616. objectdef:
  617. case tobjectdef(current_module.wpoinfo.createdobjtypes[i]).objecttype of
  618. odt_object:
  619. {$IFDEF DEBUG_DEVIRT}
  620. writeln(' (object)')
  621. {$ENDIF}
  622. ;
  623. odt_class:
  624. {$IFDEF DEBUG_DEVIRT}
  625. writeln(' (class)')
  626. {$ENDIF}
  627. ;
  628. else
  629. internalerror(2008092101);
  630. end;
  631. classrefdef:
  632. {$IFDEF DEBUG_DEVIRT}
  633. writeln(' (classrefdef)')
  634. {$ENDIF}
  635. ;
  636. else
  637. internalerror(2008092102);
  638. end;
  639. end;
  640. { now add all objectdefs derived from the instantiated
  641. classrefdefs to the tree (as they can, in theory, all
  642. be instantiated as well)
  643. }
  644. hp:=tmodule(loaded_units.first);
  645. while assigned(hp) do
  646. begin
  647. { we cannot just walk over the module's deflist, because a bunch of
  648. the defs in there don't exist anymore (when destroyed, they're
  649. removed from their symtable but not from the module's deflist)
  650. procedure-local (or class-local) class definitions do not (yet) exist
  651. }
  652. { globalsymtable (interface), is nil for main program itself }
  653. if assigned(hp.globalsymtable) then
  654. for i:=0 to hp.globalsymtable.deflist.count-1 do
  655. inheritancetree.checkforclassrefinheritance(tdef(hp.globalsymtable.deflist[i]));
  656. { staticsymtable (implementation) }
  657. if assigned(hp.localsymtable) then
  658. for i:=0 to hp.localsymtable.deflist.count-1 do
  659. inheritancetree.checkforclassrefinheritance(tdef(hp.localsymtable.deflist[i]));
  660. hp:=tmodule(hp.next);
  661. end;
  662. inheritancetree.optimizevirtualmethods;
  663. {$ifdef DEBUG_DEVIRT}
  664. inheritancetree.printvmtinfo;
  665. {$endif DEBUG_DEVIRT}
  666. inheritancetree.foreachnode(@converttreenode,nil);
  667. inheritancetree.free;
  668. end;
  669. function tprogdevirtinfo.addunitifnew(const n: shortstring): tunitdevirtinfo;
  670. begin
  671. if assigned(funits) then
  672. result:=findunit(n)
  673. else
  674. begin
  675. funits:=tfphashobjectlist.create;
  676. result:=nil;
  677. end;
  678. if not assigned(result) then
  679. begin
  680. result:=tunitdevirtinfo.create(funits,n);
  681. end;
  682. end;
  683. function tprogdevirtinfo.findunit(const n: shortstring): tunitdevirtinfo;
  684. begin
  685. result:=tunitdevirtinfo(funits.find(n));
  686. end;
  687. procedure tprogdevirtinfo.loadfromwpofilesection(reader: twposectionreaderintf);
  688. var
  689. unitid,
  690. classid,
  691. vmtentryname: string;
  692. vmttype: string[15];
  693. vmtentrynrstr: string[7];
  694. vmtentry, error: longint;
  695. unitdevirtinfo: tunitdevirtinfo;
  696. classdevirtinfo: tclassdevirtinfo;
  697. begin
  698. { format:
  699. unit1^
  700. class1&
  701. basevmt
  702. 0
  703. staticvmtentryforslot0
  704. 5
  705. staticvmtentryforslot5
  706. intfvmt1
  707. 0
  708. staticvmtentryforslot0
  709. class2&
  710. basevmt
  711. 1
  712. staticvmtentryforslot1
  713. unit2^
  714. class3&
  715. ...
  716. currently, only basevmt is supported (no interfaces yet)
  717. }
  718. { could be empty if no classes or so }
  719. if not reader.sectiongetnextline(unitid) then
  720. exit;
  721. repeat
  722. if (unitid='') or
  723. (unitid[length(unitid)]<>'^') then
  724. internalerror(2008100502);
  725. { cut off the trailing ^ }
  726. setlength(unitid,length(unitid)-1);
  727. unitdevirtinfo:=addunitifnew(unitid);
  728. { now read classes }
  729. if not reader.sectiongetnextline(classid) then
  730. internalerror(2008100505);
  731. repeat
  732. if (classid='') or
  733. (classid[length(classid)]<>'&') then
  734. internalerror(2008100503);
  735. { cut off the trailing & }
  736. setlength(classid,length(classid)-1);
  737. classdevirtinfo:=unitdevirtinfo.addclass(classid);
  738. if not reader.sectiongetnextline(vmttype) then
  739. internalerror(2008100506);
  740. { interface info is not yet supported }
  741. if (vmttype<>'basevmt') then
  742. internalerror(2008100507);
  743. { read all vmt entries for this class }
  744. while reader.sectiongetnextline(vmtentrynrstr) and
  745. (vmtentrynrstr<>'') do
  746. begin
  747. val(vmtentrynrstr,vmtentry,error);
  748. if (error<>0) then
  749. internalerror(2008100504);
  750. if not reader.sectiongetnextline(vmtentryname) or
  751. (vmtentryname='') then
  752. internalerror(2008100508);
  753. classdevirtinfo.addstaticmethod(vmtentry,vmtentryname);
  754. end;
  755. { end of section -> exit }
  756. if not(reader.sectiongetnextline(classid)) then
  757. exit;
  758. until (classid='') or
  759. (classid[length(classid)]='^');
  760. { next unit, or error }
  761. unitid:=classid;
  762. until false;
  763. end;
  764. procedure tprogdevirtinfo.storewpofilesection(writer: twposectionwriterintf);
  765. var
  766. unitcount,
  767. classcount,
  768. vmtentrycount: longint;
  769. unitdevirtinfo: tunitdevirtinfo;
  770. classdevirtinfo: tclassdevirtinfo;
  771. begin
  772. if (funits.count=0) then
  773. exit;
  774. writer.startsection(DEVIRT_SECTION_NAME);
  775. for unitcount:=0 to funits.count-1 do
  776. begin
  777. unitdevirtinfo:=tunitdevirtinfo(funits[unitcount]);
  778. writer.sectionputline(unitdevirtinfo.name+'^');
  779. for classcount:=0 to unitdevirtinfo.fclasses.count-1 do
  780. begin
  781. classdevirtinfo:=tclassdevirtinfo(tunitdevirtinfo(funits[unitcount]).fclasses[classcount]);
  782. writer.sectionputline(classdevirtinfo.name+'&');
  783. writer.sectionputline('basevmt');
  784. for vmtentrycount:=0 to classdevirtinfo.fstaticmethodnames.count-1 do
  785. if assigned(classdevirtinfo.fstaticmethodnames[vmtentrycount]) then
  786. begin
  787. writer.sectionputline(tostr(vmtentrycount));
  788. writer.sectionputline(pshortstring(classdevirtinfo.fstaticmethodnames[vmtentrycount])^);
  789. end;
  790. writer.sectionputline('');
  791. end;
  792. end;
  793. end;
  794. function tprogdevirtinfo.staticnameforvirtualmethod(objdef, procdef: tdef; out staticname: string): boolean;
  795. var
  796. unitid,
  797. classid,
  798. newname: pshortstring;
  799. unitdevirtinfo: tunitdevirtinfo;
  800. classdevirtinfo: tclassdevirtinfo;
  801. vmtentry: longint;
  802. begin
  803. { we don't support classrefs yet, nor interfaces }
  804. if (objdef.typ<>objectdef) or
  805. not(tobjectdef(objdef).objecttype in [odt_class,odt_object]) then
  806. begin
  807. result:=false;
  808. exit;
  809. end;
  810. { get the component names for the class/procdef combo }
  811. defsdecompose(tobjectdef(objdef), tprocdef(procdef),unitid,classid,vmtentry);
  812. { do we have any info for this unit? }
  813. unitdevirtinfo:=findunit(unitid^);
  814. result:=false;
  815. if not assigned(unitdevirtinfo) then
  816. exit;
  817. { and for this class? }
  818. classdevirtinfo:=unitdevirtinfo.findclass(classid^);
  819. if not assigned(classdevirtinfo) then
  820. exit;
  821. { now check whether it can be devirtualised, and if so to what }
  822. result:=classdevirtinfo.isstaticvmtentry(vmtentry,newname);
  823. if result then
  824. staticname:=newname^;
  825. end;
  826. initialization
  827. twpoinfomanagerbase.registersectionreader(DEVIRT_SECTION_NAME,tprogdevirtinfo);
  828. end.