2
0

optvirt.pas 42 KB

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