optvirt.pas 42 KB

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