optvirt.pas 39 KB

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