optvirt.pas 34 KB

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