optvirt.pas 37 KB

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