optvirt.pas 43 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193
  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,localsymtable] 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. { node.def.vmcallstaticinfo can be nil if the object only has abstract
  601. virtual methods }
  602. if (node.def.vmtentries.count=0) or
  603. not assigned(node.def.vmcallstaticinfo) then
  604. exit;
  605. for i:=0 to node.def.vmtentries.count-1 do
  606. if (po_virtualmethod in pvmtentry(node.def.vmtentries[i])^.procdef.procoptions) then
  607. case node.def.vmcallstaticinfo^[i] of
  608. vmcs_yes:
  609. begin
  610. { add info about devirtualised vmt entry }
  611. classdevirtinfo.addstaticmethod(i,pvmtentry(node.def.vmtentries[i])^.procdef.mangledname);
  612. end;
  613. vmcs_unreachable:
  614. begin
  615. { static reference to FPC_ABSTRACTERROR }
  616. classdevirtinfo.addstaticmethod(i,'FPC_ABSTRACTERROR');
  617. end;
  618. else
  619. ;
  620. end;
  621. end;
  622. constructor tprogdevirtinfo.create;
  623. begin
  624. inherited create;
  625. end;
  626. destructor tprogdevirtinfo.destroy;
  627. begin
  628. funits.free;
  629. inherited destroy;
  630. end;
  631. class function tprogdevirtinfo.getwpotype: twpotype;
  632. begin
  633. result:=wpo_devirtualization_context_insensitive;
  634. end;
  635. class function tprogdevirtinfo.generatesinfoforwposwitches: twpoptimizerswitches;
  636. begin
  637. result:=[cs_wpo_devirtualize_calls,cs_wpo_optimize_vmts];
  638. end;
  639. class function tprogdevirtinfo.performswpoforswitches: twpoptimizerswitches;
  640. begin
  641. result:=[cs_wpo_devirtualize_calls,cs_wpo_optimize_vmts];
  642. end;
  643. class function tprogdevirtinfo.sectionname: shortstring;
  644. begin
  645. result:=DEVIRT_SECTION_NAME;
  646. end;
  647. procedure tprogdevirtinfo.constructfromcompilerstate;
  648. var
  649. hp: tmodule;
  650. i: longint;
  651. inheritancetree: tinheritancetree;
  652. begin
  653. { register all instantiated class/object types }
  654. hp:=tmodule(loaded_units.first);
  655. while assigned(hp) do
  656. begin
  657. if assigned(hp.wpoinfo.createdobjtypes) then
  658. for i:=0 to hp.wpoinfo.createdobjtypes.count-1 do
  659. tdef(hp.wpoinfo.createdobjtypes[i]).register_created_object_type;
  660. if assigned(hp.wpoinfo.createdclassrefobjtypes) then
  661. for i:=0 to hp.wpoinfo.createdclassrefobjtypes.count-1 do
  662. tobjectdef(hp.wpoinfo.createdclassrefobjtypes[i]).register_created_classref_type;
  663. if assigned(hp.wpoinfo.maybecreatedbyclassrefdeftypes) then
  664. for i:=0 to hp.wpoinfo.maybecreatedbyclassrefdeftypes.count-1 do
  665. tobjectdef(hp.wpoinfo.maybecreatedbyclassrefdeftypes[i]).register_maybe_created_object_type;
  666. hp:=tmodule(hp.next);
  667. end;
  668. inheritancetree:=tinheritancetree.create;
  669. { add all constructed class/object types to the tree }
  670. {$IFDEF DEBUG_DEVIRT}
  671. writeln('constructed object/class/classreftypes in ',current_module.realmodulename^);
  672. {$ENDIF}
  673. for i := 0 to current_module.wpoinfo.createdobjtypes.count-1 do
  674. begin
  675. inheritancetree.registerinstantiatedobjdef(tdef(current_module.wpoinfo.createdobjtypes[i]));
  676. {$IFDEF DEBUG_DEVIRT}
  677. write(' ',tdef(current_module.wpoinfo.createdobjtypes[i]).GetTypeName);
  678. {$ENDIF}
  679. case tdef(current_module.wpoinfo.createdobjtypes[i]).typ of
  680. objectdef:
  681. case tobjectdef(current_module.wpoinfo.createdobjtypes[i]).objecttype of
  682. odt_object:
  683. {$IFDEF DEBUG_DEVIRT}
  684. writeln(' (object)')
  685. {$ENDIF}
  686. ;
  687. odt_class:
  688. {$IFDEF DEBUG_DEVIRT}
  689. writeln(' (class)')
  690. {$ENDIF}
  691. ;
  692. else
  693. internalerror(2008092101);
  694. end;
  695. else
  696. internalerror(2008092102);
  697. end;
  698. end;
  699. { register all instantiated classrefdefs with the tree }
  700. for i := 0 to current_module.wpoinfo.createdclassrefobjtypes.count-1 do
  701. begin
  702. inheritancetree.registerinstantiatedclassrefdef(tdef(current_module.wpoinfo.createdclassrefobjtypes[i]));
  703. {$IFDEF DEBUG_DEVIRT}
  704. write(' Class Of ',tdef(current_module.wpoinfo.createdclassrefobjtypes[i]).GetTypeName);
  705. {$ENDIF}
  706. case tdef(current_module.wpoinfo.createdclassrefobjtypes[i]).typ of
  707. objectdef:
  708. {$IFDEF DEBUG_DEVIRT}
  709. writeln(' (classrefdef)')
  710. {$ENDIF}
  711. ;
  712. else
  713. internalerror(2008101101);
  714. end;
  715. end;
  716. { now add all objectdefs that are referred somewhere (via a
  717. loadvmtaddr node) and that are derived from an instantiated
  718. classrefdef to the tree (as they can, in theory, all
  719. be instantiated as well)
  720. }
  721. for i := 0 to current_module.wpoinfo.maybecreatedbyclassrefdeftypes.count-1 do
  722. begin
  723. inheritancetree.checkforclassrefinheritance(tdef(current_module.wpoinfo.maybecreatedbyclassrefdeftypes[i]));
  724. {$IFDEF DEBUG_DEVIRT}
  725. write(' Class Of ',tdef(current_module.wpoinfo.maybecreatedbyclassrefdeftypes[i]).GetTypeName);
  726. {$ENDIF}
  727. case tdef(current_module.wpoinfo.maybecreatedbyclassrefdeftypes[i]).typ of
  728. objectdef:
  729. {$IFDEF DEBUG_DEVIRT}
  730. writeln(' (classrefdef)')
  731. {$ENDIF}
  732. ;
  733. else
  734. internalerror(2008101106);
  735. end;
  736. end;
  737. { add info about called virtual methods }
  738. hp:=tmodule(loaded_units.first);
  739. while assigned(hp) do
  740. begin
  741. if assigned(hp.wpoinfo.calledvmtentries) then
  742. for i:=0 to hp.wpoinfo.calledvmtentries.count-1 do
  743. inheritancetree.registercalledvmtentries(tcalledvmtentries(hp.wpoinfo.calledvmtentries[i]));
  744. hp:=tmodule(hp.next);
  745. end;
  746. inheritancetree.optimizevirtualmethods;
  747. {$ifdef DEBUG_DEVIRT}
  748. inheritancetree.printvmtinfo;
  749. {$endif DEBUG_DEVIRT}
  750. inheritancetree.foreachnode(@converttreenode,nil);
  751. inheritancetree.free;
  752. end;
  753. function tprogdevirtinfo.addunitifnew(const n: shortstring): tunitdevirtinfo;
  754. begin
  755. if assigned(funits) then
  756. result:=findunit(n)
  757. else
  758. begin
  759. funits:=tfphashobjectlist.create;
  760. result:=nil;
  761. end;
  762. if not assigned(result) then
  763. begin
  764. result:=tunitdevirtinfo.create(funits,n);
  765. end;
  766. end;
  767. function tprogdevirtinfo.findunit(const n: shortstring): tunitdevirtinfo;
  768. begin
  769. result:=tunitdevirtinfo(funits.find(n));
  770. end;
  771. procedure tprogdevirtinfo.loadfromwpofilesection(reader: twposectionreaderintf);
  772. var
  773. unitid,
  774. classid,
  775. vmtentryname: string;
  776. vmttype: string[15];
  777. vmtentrynrstr: string[7];
  778. classinstantiated: string[1];
  779. vmtentry, error: longint;
  780. unitdevirtinfo: tunitdevirtinfo;
  781. classdevirtinfo: tclassdevirtinfo;
  782. instantiated: boolean;
  783. begin
  784. { format:
  785. # unitname^
  786. unit1^
  787. # classname&
  788. class1&
  789. # instantiated?
  790. 1
  791. # vmt type (base or some interface)
  792. basevmt
  793. # vmt entry nr
  794. 0
  795. # name of routine to call instead
  796. staticvmtentryforslot0
  797. 5
  798. staticvmtentryforslot5
  799. intfvmt1
  800. 0
  801. staticvmtentryforslot0
  802. # non-instantiated class (but if we encounter a variable of this
  803. # type, we can optimise class to vmtentry 1)
  804. class2&
  805. 0
  806. basevmt
  807. 1
  808. staticvmtentryforslot1
  809. # instantiated class without optimisable virtual methods
  810. class3&
  811. 1
  812. unit2^
  813. 1
  814. class3&
  815. ...
  816. currently, only basevmt is supported (no interfaces yet)
  817. }
  818. { could be empty if no classes or so }
  819. if not reader.sectiongetnextline(unitid) then
  820. exit;
  821. repeat
  822. if (unitid='') or
  823. (unitid[length(unitid)]<>'^') then
  824. internalerror(2008100502);
  825. { cut off the trailing ^ }
  826. setlength(unitid,length(unitid)-1);
  827. unitdevirtinfo:=addunitifnew(unitid);
  828. { now read classes }
  829. if not reader.sectiongetnextline(classid) then
  830. internalerror(2008100505);
  831. repeat
  832. if (classid='') or
  833. (classid[length(classid)]<>'&') then
  834. internalerror(2008100503);
  835. { instantiated? }
  836. if not reader.sectiongetnextline(classinstantiated) then
  837. internalerror(2008101901);
  838. instantiated:=classinstantiated='1';
  839. { cut off the trailing & }
  840. setlength(classid,length(classid)-1);
  841. classdevirtinfo:=unitdevirtinfo.addclass(classid,instantiated);
  842. { last class could be an instantiated class without any
  843. optimisable methods. }
  844. if not reader.sectiongetnextline(vmttype) then
  845. exit;
  846. { any optimisable virtual methods? }
  847. if (vmttype<>'') then
  848. begin
  849. { interface info is not yet supported }
  850. if (vmttype<>'basevmt') then
  851. internalerror(2008100507);
  852. { read all vmt entries for this class }
  853. while reader.sectiongetnextline(vmtentrynrstr) and
  854. (vmtentrynrstr<>'') do
  855. begin
  856. val(vmtentrynrstr,vmtentry,error);
  857. if (error<>0) then
  858. internalerror(2008100504);
  859. if not reader.sectiongetnextline(vmtentryname) or
  860. (vmtentryname='') then
  861. internalerror(2008100508);
  862. classdevirtinfo.addstaticmethod(vmtentry,vmtentryname);
  863. end;
  864. end;
  865. { end of section -> exit }
  866. if not(reader.sectiongetnextline(classid)) then
  867. exit;
  868. until (classid='') or
  869. (classid[length(classid)]='^');
  870. { next unit, or error }
  871. unitid:=classid;
  872. until false;
  873. end;
  874. procedure tprogdevirtinfo.documentformat(writer: twposectionwriterintf);
  875. begin
  876. writer.sectionputline('# section format:');
  877. writer.sectionputline('# unit1^');
  878. writer.sectionputline('# class1& ; classname&');
  879. writer.sectionputline('# 1 ; instantiated or not');
  880. writer.sectionputline('# basevmt ; vmt type (base or some interface)');
  881. writer.sectionputline('# # vmt entry nr');
  882. writer.sectionputline('# 0 ; vmt entry nr');
  883. writer.sectionputline('# staticvmtentryforslot0 ; name or routine to call instead');
  884. writer.sectionputline('# 5');
  885. writer.sectionputline('# staticvmtentryforslot5');
  886. writer.sectionputline('# intfvmt1');
  887. writer.sectionputline('# 0');
  888. writer.sectionputline('# staticvmtentryforslot0');
  889. writer.sectionputline('#');
  890. writer.sectionputline('# class2&');
  891. writer.sectionputline('# 0 ; non-instantiated class (can be variables of this type, e.g. TObject)');
  892. writer.sectionputline('# basevmt');
  893. writer.sectionputline('# 1');
  894. writer.sectionputline('# staticvmtentryforslot1');
  895. writer.sectionputline('#');
  896. writer.sectionputline('# class3& ; instantiated class without optimisable virtual methods');
  897. writer.sectionputline('# 1');
  898. writer.sectionputline('#');
  899. writer.sectionputline('# unit2^');
  900. writer.sectionputline('# 1');
  901. writer.sectionputline('# class3&');
  902. writer.sectionputline('# ...');
  903. writer.sectionputline('#');
  904. writer.sectionputline('# currently, only basevmt is supported (no interfaces yet)');
  905. writer.sectionputline('#');
  906. end;
  907. procedure tprogdevirtinfo.storewpofilesection(writer: twposectionwriterintf);
  908. var
  909. unitcount,
  910. classcount,
  911. vmtentrycount: longint;
  912. unitdevirtinfo: tunitdevirtinfo;
  913. classdevirtinfo: tclassdevirtinfo;
  914. first: boolean;
  915. begin
  916. writer.startsection(DEVIRT_SECTION_NAME);
  917. { if there are no optimised virtual methods, we have stored no info }
  918. if not assigned(funits) then
  919. exit;
  920. documentformat(writer);
  921. for unitcount:=0 to funits.count-1 do
  922. begin
  923. unitdevirtinfo:=tunitdevirtinfo(funits[unitcount]);
  924. writer.sectionputline(unitdevirtinfo.name+'^');
  925. for classcount:=0 to unitdevirtinfo.fclasses.count-1 do
  926. begin
  927. classdevirtinfo:=tclassdevirtinfo(tunitdevirtinfo(funits[unitcount]).fclasses[classcount]);
  928. writer.sectionputline(classdevirtinfo.name+'&');
  929. writer.sectionputline(tostr(ord(classdevirtinfo.instantiated)));
  930. first:=true;
  931. for vmtentrycount:=0 to classdevirtinfo.fstaticmethodnames.count-1 do
  932. if assigned(classdevirtinfo.fstaticmethodnames[vmtentrycount]) then
  933. begin
  934. if first then
  935. begin
  936. writer.sectionputline('basevmt');
  937. first:=false;
  938. end;
  939. writer.sectionputline(tostr(vmtentrycount));
  940. writer.sectionputline(pshortstring(classdevirtinfo.fstaticmethodnames[vmtentrycount])^);
  941. end;
  942. writer.sectionputline('');
  943. end;
  944. end;
  945. end;
  946. function tprogdevirtinfo.getstaticname(forvmtentry: boolean; objdef, procdef: tdef; out staticname: TSymStr): boolean;
  947. var
  948. unitid,
  949. classid,
  950. newname: pshortstring;
  951. unitdevirtinfo: tunitdevirtinfo;
  952. classdevirtinfo: tclassdevirtinfo;
  953. vmtentry: longint;
  954. realobjdef: tobjectdef;
  955. classprefix: shortstring;
  956. begin
  957. { if we don't have any devirtualisation info, exit }
  958. if not assigned(funits) then
  959. begin
  960. result:=false;
  961. exit
  962. end;
  963. { class methods are in the regular vmt, so we can handle classrefs
  964. the same way as plain objectdefs
  965. }
  966. if (objdef.typ=classrefdef) then
  967. realobjdef:=tobjectdef(tclassrefdef(objdef).pointeddef)
  968. else if (objdef.typ=objectdef) and
  969. (tobjectdef(objdef).objecttype in [odt_class,odt_object]) then
  970. realobjdef:=tobjectdef(objdef)
  971. else
  972. begin
  973. { we don't support interfaces yet }
  974. result:=false;
  975. exit;
  976. end;
  977. { if it's for a vmtentry of an objdef and the objdef is
  978. not instantiated, then we can fill the vmt with pointers
  979. to FPC_ABSTRACTERROR, except for published methods
  980. (these can be called via rtti, so always have to point
  981. to the original method)
  982. }
  983. if forvmtentry and
  984. (tprocdef(procdef).visibility=vis_published) then
  985. begin
  986. result:=false;
  987. exit;
  988. end;
  989. { get the component names for the class/procdef combo }
  990. defsdecompose(realobjdef,tprocdef(procdef),unitid,classid,classprefix,vmtentry);
  991. { If we don't have information about a particular unit/class/method,
  992. it means that such class cannot be instantiated. So if we are
  993. looking up information for a vmt entry, we can always safely return
  994. FPC_ABSTRACTERROR if we do not find anything, unless it's a
  995. published method (but those are handled already above) or a
  996. class method (can be called even if the class is not instantiated).
  997. }
  998. result:=
  999. forvmtentry and
  1000. not(po_classmethod in tprocdef(procdef).procoptions);
  1001. staticname:='FPC_ABSTRACTERROR';
  1002. { do we have any info for this unit? }
  1003. unitdevirtinfo:=findunit(unitid^);
  1004. if not assigned(unitdevirtinfo) then
  1005. exit;
  1006. { and for this class? }
  1007. classdevirtinfo:=unitdevirtinfo.findclass(classprefix+classid^);
  1008. if not assigned(classdevirtinfo) then
  1009. exit;
  1010. if forvmtentry and
  1011. (objdef.typ=objectdef) and
  1012. not classdevirtinfo.instantiated and
  1013. { virtual class methods can be called even if the class is not instantiated }
  1014. not(po_classmethod in tprocdef(procdef).procoptions) then
  1015. begin
  1016. { already set above
  1017. staticname:='FPC_ABSTRACTERROR';
  1018. }
  1019. result:=true;
  1020. end
  1021. else
  1022. begin
  1023. { now check whether it can be devirtualised, and if so to what }
  1024. result:=classdevirtinfo.isstaticvmtentry(vmtentry,newname);
  1025. if result then
  1026. staticname:=newname^;
  1027. end;
  1028. end;
  1029. function tprogdevirtinfo.staticnameforcallingvirtualmethod(objdef, procdef: tdef; out staticname: TSymStr): boolean;
  1030. begin
  1031. result:=getstaticname(false,objdef,procdef,staticname);
  1032. end;
  1033. function tprogdevirtinfo.staticnameforvmtentry(objdef, procdef: tdef; out staticname: TSymStr): boolean;
  1034. begin
  1035. result:=getstaticname(true,objdef,procdef,staticname);
  1036. end;
  1037. end.