optvirt.pas 34 KB

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