optvirt.pas 42 KB

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