optvirt.pas 34 KB

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