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. symtable,
  138. defcmp,
  139. nobj,
  140. verbose;
  141. const
  142. DEVIRT_SECTION_NAME = 'contextinsensitive_devirtualization';
  143. { *************************** tinheritancetreenode ************************* }
  144. constructor tinheritancetreenode.create(_parent: tinheritancetreenode; _def: tobjectdef; _instantiated: boolean);
  145. begin
  146. fparent:=_parent;
  147. fdef:=_def;
  148. finstantiated:=_instantiated;
  149. if assigned(_def) then
  150. fcalledvmtmethods:=tbitset.create(_def.vmtentries.count);
  151. end;
  152. destructor tinheritancetreenode.destroy;
  153. begin
  154. { fchilds owns its members, so it will free them too }
  155. fchilds.free;
  156. fcalledvmtmethods.free;
  157. inherited destroy;
  158. end;
  159. function tinheritancetreenode.childcount: longint;
  160. begin
  161. if assigned(fchilds) then
  162. result:=fchilds.count
  163. else
  164. result:=0;
  165. end;
  166. function tinheritancetreenode.haschilds: boolean;
  167. begin
  168. result:=assigned(fchilds)
  169. end;
  170. function tinheritancetreenode.getchild(index: longint): tinheritancetreenode;
  171. begin
  172. result:=tinheritancetreenode(fchilds[index]);
  173. end;
  174. function tinheritancetreenode.maybeaddchild(_def: tobjectdef; _instantiated: boolean): tinheritancetreenode;
  175. begin
  176. { sanity check }
  177. if assigned(_def.childof) then
  178. begin
  179. if (_def.childof<>def) then
  180. internalerror(2008092201);
  181. end
  182. else if assigned(fparent) then
  183. internalerror(2008092202);
  184. if not assigned(fchilds) then
  185. fchilds:=tfpobjectlist.create(true);
  186. { def already a child -> return }
  187. result:=findchild(_def);
  188. if assigned(result) then
  189. result.finstantiated:=result.finstantiated or _instantiated
  190. else
  191. begin
  192. { not found, add new child }
  193. result:=tinheritancetreenode.create(self,_def,_instantiated);
  194. fchilds.add(result);
  195. end;
  196. end;
  197. function tinheritancetreenode.findchild(_def: tobjectdef): tinheritancetreenode;
  198. var
  199. i: longint;
  200. begin
  201. result:=nil;
  202. if assigned(fchilds) then
  203. for i := 0 to fchilds.count-1 do
  204. if (tinheritancetreenode(fchilds[i]).def=_def) then
  205. begin
  206. result:=tinheritancetreenode(fchilds[i]);
  207. break;
  208. end;
  209. end;
  210. { *************************** tinheritancetree ************************* }
  211. constructor tinheritancetree.create;
  212. begin
  213. froots:=tinheritancetreenode.create(nil,nil,false);
  214. classrefdefs:=tfpobjectlist.create(false);
  215. end;
  216. destructor tinheritancetree.destroy;
  217. begin
  218. froots.free;
  219. classrefdefs.free;
  220. inherited destroy;
  221. end;
  222. function tinheritancetree.registerinstantiatedobjectdefrecursive(def: tobjectdef; instantiated: boolean): tinheritancetreenode;
  223. begin
  224. if assigned(def.childof) then
  225. begin
  226. { recursively add parent, of which we have no info about whether or not it is
  227. instantiated at this point -> default to false (will be overridden by "true"
  228. if this class is instantioted, since then registerinstantiatedobjdef() will
  229. be called for this class as well)
  230. }
  231. result:=registerinstantiatedobjectdefrecursive(def.childof,false);
  232. { and add ourselves to the parent }
  233. result:=result.maybeaddchild(def,instantiated);
  234. end
  235. else
  236. { add ourselves to the roots }
  237. result:=froots.maybeaddchild(def,instantiated);
  238. end;
  239. procedure tinheritancetree.registerinstantiatedobjdef(def: tdef);
  240. begin
  241. { add the def }
  242. if (def.typ=objectdef) then
  243. registerinstantiatedobjectdefrecursive(tobjectdef(def),true)
  244. else
  245. internalerror(2008092401);
  246. end;
  247. procedure tinheritancetree.registerinstantiatedclassrefdef(def: tdef);
  248. begin
  249. { queue for later checking (these are the objectdefs
  250. to which the classrefdefs point) }
  251. if (def.typ=objectdef) then
  252. classrefdefs.add(def)
  253. else
  254. internalerror(2008101401);
  255. end;
  256. function tinheritancetree.getnodefordef(def: tobjectdef): tinheritancetreenode;
  257. begin
  258. if assigned(def.childof) then
  259. begin
  260. result:=getnodefordef(def.childof);
  261. if assigned(result) then
  262. result:=result.findchild(def);
  263. end
  264. else
  265. result:=froots.findchild(def);
  266. end;
  267. procedure tinheritancetree.registercalledvmtentries(entries: tcalledvmtentries);
  268. var
  269. node: tinheritancetreenode;
  270. begin
  271. node:=getnodefordef(tobjectdef(entries.objdef));
  272. { it's possible that no instance of this class or its descendants are
  273. instantiated
  274. }
  275. if not assigned(node) then
  276. exit;
  277. { now mark these methods as (potentially) called for this type and for
  278. all of its descendants
  279. }
  280. addcalledvmtentries(node,entries.calledentries);
  281. foreachnodefromroot(node,@addcalledvmtentries,entries.calledentries);
  282. end;
  283. procedure tinheritancetree.checkforclassrefinheritance(def: tdef);
  284. var
  285. i: longint;
  286. begin
  287. if (def.typ=objectdef) then
  288. begin
  289. {$ifdef debug_devirt}
  290. write(' Checking for classrefdef inheritance of ',def.typename);
  291. {$endif debug_devirt}
  292. for i:=0 to classrefdefs.count-1 do
  293. if def_is_related(tobjectdef(def),tobjectdef(classrefdefs[i])) then
  294. begin
  295. {$ifdef debug_devirt}
  296. writeln('... Found: inherits from Class Of ',tobjectdef(classrefdefs[i]).typename);
  297. {$endif debug_devirt}
  298. registerinstantiatedobjdef(def);
  299. exit;
  300. end;
  301. {$ifdef debug_devirt}
  302. writeln('... Not found!');
  303. {$endif debug_devirt}
  304. end;
  305. end;
  306. procedure tinheritancetree.foreachnodefromroot(root: tinheritancetreenode; proctocall: tinheritancetreecallback; arg: pointer);
  307. procedure process(const node: tinheritancetreenode);
  308. var
  309. i: longint;
  310. begin
  311. for i:=0 to node.childcount-1 do
  312. if node.childs[i].haschilds then
  313. begin
  314. proctocall(node.childs[i],arg);
  315. process(node.childs[i])
  316. end
  317. else
  318. proctocall(node.childs[i],arg);
  319. end;
  320. begin
  321. process(root);
  322. end;
  323. procedure tinheritancetree.foreachnode(proctocall: tinheritancetreecallback; arg: pointer);
  324. begin
  325. foreachnodefromroot(froots,proctocall,arg);
  326. end;
  327. procedure tinheritancetree.foreachleafnode(proctocall: tinheritancetreecallback; arg: pointer);
  328. procedure process(const node: tinheritancetreenode);
  329. var
  330. i: longint;
  331. begin
  332. for i:=0 to node.childcount-1 do
  333. if node.childs[i].haschilds then
  334. process(node.childs[i])
  335. else
  336. proctocall(node.childs[i],arg);
  337. end;
  338. begin
  339. process(froots);
  340. end;
  341. procedure tinheritancetree.markvmethods(node: tinheritancetreenode; p: pointer);
  342. var
  343. currnode: tinheritancetreenode;
  344. pd: tprocdef;
  345. i: longint;
  346. makeallvirtual: boolean;
  347. begin
  348. {$IFDEF DEBUG_DEVIRT}
  349. writeln('processing leaf node ',node.def.typename);
  350. {$ENDIF}
  351. { todo: also process interfaces (ImplementedInterfaces) }
  352. if (node.def.vmtentries.count=0) then
  353. exit;
  354. { process all vmt entries for this class/object }
  355. for i:=0 to node.def.vmtentries.count-1 do
  356. begin
  357. currnode:=node;
  358. { extra tprocdef(tobject(..)) typecasts so that -CR can catch
  359. errors in case the vmtentries are not properly (re)deref'd }
  360. pd:=tprocdef(tobject(pvmtentry(currnode.def.vmtentries[i])^.procdef));
  361. { abstract methods cannot be called directly }
  362. if (po_abstractmethod in pd.procoptions) then
  363. continue;
  364. {$IFDEF DEBUG_DEVIRT}
  365. writeln(' method ',pd.typename);
  366. {$ENDIF}
  367. { Now mark all virtual methods static that are the same in parent
  368. classes as in this instantiated child class (only instantiated
  369. classes can be leaf nodes, since only instantiated classes were
  370. added to the tree).
  371. If a first child does not override a parent method while a
  372. a second one does, the first will mark it as statically
  373. callable, but the second will set it to not statically callable.
  374. In the opposite situation, the first will mark it as not
  375. statically callable and the second will leave it alone.
  376. }
  377. makeallvirtual:=false;
  378. repeat
  379. if { stop when this method does not exist in a parent }
  380. (currnode.def.vmtentries.count<=i) then
  381. break;
  382. if not assigned(currnode.def.vmcallstaticinfo) then
  383. currnode.def.vmcallstaticinfo:=allocmem(currnode.def.vmtentries.count*sizeof(tvmcallstatic));
  384. { if this method cannot be called, we can just mark it as
  385. unreachable. This will cause its static name to be set to
  386. FPC_ABSTRACTERROR later on. Exception: published methods are
  387. always reachable (via RTTI).
  388. }
  389. if (pd.visibility<>vis_published) and
  390. not(currnode.fcalledvmtmethods.isset(i)) then
  391. begin
  392. currnode.def.vmcallstaticinfo^[i]:=vmcs_unreachable;
  393. currnode:=currnode.parent;
  394. end
  395. { same procdef as in all instantiated childs? (yes or don't know) }
  396. else if (currnode.def.vmcallstaticinfo^[i] in [vmcs_default,vmcs_yes]) then
  397. begin
  398. { methods in uninstantiated classes can be made static if
  399. they are the same in all instantiated derived classes
  400. }
  401. if ((pvmtentry(currnode.def.vmtentries[i])^.procdef=pd) or
  402. (not currnode.instantiated and
  403. (currnode.def.vmcallstaticinfo^[i]=vmcs_default))) and
  404. not makeallvirtual then
  405. begin
  406. {$IFDEF DEBUG_DEVIRT}
  407. writeln(' marking as static for ',currnode.def.typename);
  408. {$ENDIF}
  409. currnode.def.vmcallstaticinfo^[i]:=vmcs_yes;
  410. { this is in case of a non-instantiated parent of an instantiated child:
  411. the method declared in the child will always be called here
  412. }
  413. pvmtentry(currnode.def.vmtentries[i])^.procdef:=pd;
  414. end
  415. else
  416. begin
  417. {$IFDEF DEBUG_DEVIRT}
  418. writeln(' marking as non-static for ',currnode.def.typename);
  419. {$ENDIF}
  420. { this vmt entry must also remain virtual for all parents }
  421. makeallvirtual:=true;
  422. currnode.def.vmcallstaticinfo^[i]:=vmcs_no;
  423. end;
  424. currnode:=currnode.parent;
  425. end
  426. else if (currnode.def.vmcallstaticinfo^[i]=vmcs_no) then
  427. begin
  428. {$IFDEF DEBUG_DEVIRT}
  429. writeln(' not processing parents, already non-static for ',currnode.def.typename);
  430. {$ENDIF}
  431. { parents are already set to vmcs_no, so no need to continue }
  432. currnode:=nil;
  433. end
  434. else
  435. currnode:=currnode.parent;
  436. until not assigned(currnode) or
  437. not assigned(currnode.def);
  438. end;
  439. end;
  440. procedure tinheritancetree.optimizevirtualmethods;
  441. begin
  442. foreachleafnode(@markvmethods,nil);
  443. end;
  444. procedure tinheritancetree.printobjectvmtinfo(node: tinheritancetreenode; arg: pointer);
  445. var
  446. i,
  447. totaldevirtualised,
  448. totalvirtual,
  449. totalunreachable: ptrint;
  450. begin
  451. totaldevirtualised:=0;
  452. totalvirtual:=0;
  453. totalunreachable:=0;
  454. writeln(node.def.typename);
  455. if (node.def.vmtentries.count=0) then
  456. begin
  457. writeln(' No virtual methods!');
  458. exit;
  459. end;
  460. for i:=0 to node.def.vmtentries.count-1 do
  461. if (po_virtualmethod in pvmtentry(node.def.vmtentries[i])^.procdef.procoptions) then
  462. begin
  463. inc(totalvirtual);
  464. if (node.def.vmcallstaticinfo^[i]=vmcs_yes) then
  465. begin
  466. inc(totaldevirtualised);
  467. writeln(' Devirtualised: ',pvmtentry(node.def.vmtentries[i])^.procdef.typename);
  468. end
  469. else if (node.def.vmcallstaticinfo^[i]=vmcs_unreachable) then
  470. begin
  471. inc(totalunreachable);
  472. writeln(' Unreachable: ',pvmtentry(node.def.vmtentries[i])^.procdef.typename);
  473. end;
  474. end;
  475. writeln('Total devirtualised/unreachable/all: ',totaldevirtualised,'/',totalunreachable,'/',totalvirtual);
  476. writeln;
  477. end;
  478. procedure tinheritancetree.addcalledvmtentries(node: tinheritancetreenode; arg: pointer);
  479. var
  480. vmtentries: tbitset absolute arg;
  481. begin
  482. node.fcalledvmtmethods.addset(vmtentries);
  483. end;
  484. procedure tinheritancetree.printvmtinfo;
  485. begin
  486. foreachnode(@printobjectvmtinfo,nil);
  487. end;
  488. { helper routines: decompose an object & procdef combo into a unitname, class name and vmtentry number
  489. (unit name where the objectdef is declared, class name of the objectdef, vmtentry number of the
  490. procdef -- procdef does not necessarily belong to objectdef, it may also belong to a descendant
  491. or parent). classprefix is set in case of nested classes.
  492. }
  493. procedure defunitclassname(objdef: tobjectdef; out unitname, classname: pshortstring; out classprefix: shortstring);
  494. const
  495. mainprogname: string[2] = 'P$';
  496. var
  497. mainsymtab,
  498. objparentsymtab : tsymtable;
  499. begin
  500. objparentsymtab:=objdef.symtable;
  501. mainsymtab:=objparentsymtab.defowner.owner;
  502. classprefix:='';
  503. while mainsymtab.symtabletype in [recordsymtable,objectsymtable] do
  504. begin
  505. classprefix:=mainsymtab.name^+'.'+classprefix;
  506. mainsymtab:=mainsymtab.defowner.owner;
  507. end;
  508. { main symtable must be static or global }
  509. if not(mainsymtab.symtabletype in [staticsymtable,globalsymtable]) then
  510. internalerror(200204177);
  511. if (TSymtable(main_module.localsymtable)=mainsymtab) and
  512. (not main_module.is_unit) then
  513. { same convention as for mangled names }
  514. unitname:=@mainprogname
  515. else
  516. unitname:=mainsymtab.name;
  517. classname:=tobjectdef(objparentsymtab.defowner).objname;
  518. end;
  519. procedure defsdecompose(objdef: tobjectdef; procdef: tprocdef; out unitname, classname: pshortstring; out classprefix: shortstring; out vmtentry: longint);
  520. begin
  521. defunitclassname(objdef,unitname,classname,classprefix);
  522. vmtentry:=procdef.extnumber;
  523. { if it's $ffff, this is not a valid virtual method }
  524. if (vmtentry=$ffff) then
  525. internalerror(2008100509);
  526. end;
  527. { tclassdevirtinfo }
  528. constructor tclassdevirtinfo.create(hashobjectlist:tfphashobjectlist;const n: shortstring; instantiated: boolean);
  529. begin
  530. inherited create(hashobjectlist,n);
  531. finstantiated:=instantiated;
  532. fstaticmethodnames:=tfplist.create;
  533. end;
  534. destructor tclassdevirtinfo.destroy;
  535. var
  536. i: longint;
  537. begin
  538. for i:=0 to fstaticmethodnames.count-1 do
  539. if assigned(fstaticmethodnames[i]) then
  540. freemem(fstaticmethodnames[i]);
  541. fstaticmethodnames.free;
  542. inherited destroy;
  543. end;
  544. procedure tclassdevirtinfo.addstaticmethod(vmtindex: longint;
  545. const replacementname: shortstring);
  546. begin
  547. if (vmtindex>=fstaticmethodnames.count) then
  548. fstaticmethodnames.Count:=vmtindex+10;
  549. fstaticmethodnames[vmtindex]:=stringdup(replacementname);
  550. end;
  551. function tclassdevirtinfo.isstaticvmtentry(vmtindex: longint; out
  552. replacementname: pshortstring): boolean;
  553. begin
  554. result:=false;
  555. if (vmtindex>=fstaticmethodnames.count) then
  556. exit;
  557. replacementname:=fstaticmethodnames[vmtindex];
  558. result:=assigned(replacementname);
  559. end;
  560. { tunitdevirtinfo }
  561. constructor tunitdevirtinfo.create(hashobjectlist:tfphashobjectlist;const n: shortstring);
  562. begin
  563. inherited create(hashobjectlist,n);
  564. fclasses:=tfphashobjectlist.create(true);
  565. end;
  566. destructor tunitdevirtinfo.destroy;
  567. begin
  568. fclasses.free;
  569. inherited destroy;
  570. end;
  571. function tunitdevirtinfo.addclass(const n: shortstring; instantiated: boolean): tclassdevirtinfo;
  572. begin
  573. result:=findclass(n);
  574. { can't have two classes with the same name in a single unit }
  575. if assigned(result) then
  576. internalerror(2008100501);
  577. result:=tclassdevirtinfo.create(fclasses,n,instantiated);
  578. end;
  579. function tunitdevirtinfo.findclass(const n: shortstring): tclassdevirtinfo;
  580. begin
  581. result:=tclassdevirtinfo(fclasses.find(n));
  582. end;
  583. { tprogdevirtinfo }
  584. procedure tprogdevirtinfo.converttreenode(node: tinheritancetreenode; arg: pointer);
  585. var
  586. i: longint;
  587. classprefix: shortstring;
  588. unitid, classid: pshortstring;
  589. unitdevirtinfo: tunitdevirtinfo;
  590. classdevirtinfo: tclassdevirtinfo;
  591. begin
  592. if (not node.instantiated) and
  593. (node.def.vmtentries.count=0) then
  594. exit;
  595. { always add a class entry for an instantiated class, so we can
  596. fill the vmt's of non-instantiated classes with calls to
  597. FPC_ABSTRACTERROR during the optimisation phase
  598. }
  599. defunitclassname(node.def,unitid,classid,classprefix);
  600. unitdevirtinfo:=addunitifnew(unitid^);
  601. classdevirtinfo:=unitdevirtinfo.addclass(classprefix+classid^,node.instantiated);
  602. if (node.def.vmtentries.count=0) then
  603. exit;
  604. for i:=0 to node.def.vmtentries.count-1 do
  605. if (po_virtualmethod in pvmtentry(node.def.vmtentries[i])^.procdef.procoptions) then
  606. case node.def.vmcallstaticinfo^[i] of
  607. vmcs_yes:
  608. begin
  609. { add info about devirtualised vmt entry }
  610. classdevirtinfo.addstaticmethod(i,pvmtentry(node.def.vmtentries[i])^.procdef.mangledname);
  611. end;
  612. vmcs_unreachable:
  613. begin
  614. { static reference to FPC_ABSTRACTERROR }
  615. classdevirtinfo.addstaticmethod(i,'FPC_ABSTRACTERROR');
  616. end;
  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.