optvirt.pas 42 KB

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