1 { 2 Copyright (c) 2008 by Jonas Maebe 3 4 Virtual methods optimizations (devirtualization) 5 6 This program is free software; you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published by 8 the Free Software Foundation; either version 2 of the License, or 9 (at your option) any later version. 10 11 This program is distributed in the hope that it will be useful, 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 GNU General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with this program; if not, write to the Free Software 18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 19 20 **************************************************************************** 21 } 22 unit optvirt; 23 24 {$i fpcdefs.inc} 25 26 interface 27 28 uses 29 globtype, 30 cclasses, 31 symtype,symdef, 32 wpobase; 33 34 type 35 { node in an inheritance tree, contains a link to the parent type (if any) and to all 36 child types 37 } 38 tinheritancetreenode = class 39 private 40 fdef: tobjectdef; 41 fparent: tinheritancetreenode; 42 fchilds: tfpobjectlist; 43 fcalledvmtmethods: tbitset; 44 finstantiated: boolean; 45 getchildnull46 function getchild(index: longint): tinheritancetreenode; 47 public 48 constructor create(_parent: tinheritancetreenode; _def: tobjectdef; _instantiated: boolean); 49 { destroys both this node and all of its siblings } 50 destructor destroy; override; childcountnull51 function childcount: longint; haschildsnull52 function haschilds: boolean; 53 property childs[index: longint]: tinheritancetreenode read getchild; 54 property parent: tinheritancetreenode read fparent; 55 property def: tobjectdef read fdef; 56 property instantiated: boolean read finstantiated write finstantiated; 57 { if def is not yet a child of this node, add it. In all cases, return node containing 58 this def (either new or existing one 59 } maybeaddchildnull60 function maybeaddchild(_def: tobjectdef; _instantiated: boolean): tinheritancetreenode; findchildnull61 function findchild(_def: tobjectdef): tinheritancetreenode; 62 end; 63 64 65 tinheritancetreecallback = procedure(node: tinheritancetreenode; arg: pointer) of object; 66 67 tinheritancetree = class 68 private 69 { just a regular node with parent = nil } 70 froots: tinheritancetreenode; 71 72 classrefdefs: tfpobjectlist; 73 74 procedure foreachnodefromroot(root: tinheritancetreenode; proctocall: tinheritancetreecallback; arg: pointer); registerinstantiatedobjectdefrecursivenull75 function registerinstantiatedobjectdefrecursive(def: tobjectdef; instantiated: boolean): tinheritancetreenode; 76 procedure markvmethods(node: tinheritancetreenode; p: pointer); 77 procedure printobjectvmtinfo(node: tinheritancetreenode; arg: pointer); 78 procedure addcalledvmtentries(node: tinheritancetreenode; arg: pointer); 79 getnodefordefnull80 function getnodefordef(def: tobjectdef): tinheritancetreenode; 81 public 82 constructor create; 83 destructor destroy; override; 84 { adds an objectdef (the def itself, and all of its parents that do not yet exist) to 85 the tree, and returns the leaf node 86 } 87 procedure registerinstantiatedobjdef(def: tdef); 88 procedure registerinstantiatedclassrefdef(def: tdef); 89 procedure registercalledvmtentries(entries: tcalledvmtentries); 90 procedure checkforclassrefinheritance(def: tdef); 91 procedure foreachnode(proctocall: tinheritancetreecallback; arg: pointer); 92 procedure foreachleafnode(proctocall: tinheritancetreecallback; arg: pointer); 93 procedure optimizevirtualmethods; 94 procedure printvmtinfo; 95 end; 96 97 98 { devirtualisation information for a class } 99 100 tclassdevirtinfo = class(tfphashobject) 101 private 102 { array (indexed by vmt entry nr) of replacement statically callable method names } 103 fstaticmethodnames: tfplist; 104 { is this class instantiated by the program? } 105 finstantiated: boolean; isstaticvmtentrynull106 function isstaticvmtentry(vmtindex: longint; out replacementname: pshortstring): boolean; 107 public 108 constructor create(hashobjectlist:tfphashobjectlist;const n: shortstring; instantiated: boolean); 109 destructor destroy; override; 110 111 property instantiated: boolean read finstantiated; 112 113 procedure addstaticmethod(vmtindex: longint; const replacementname: shortstring); 114 end; 115 116 117 { devirtualisation information for all classes in a unit } 118 119 tunitdevirtinfo = class(tfphashobject) 120 private 121 { hashtable of classes } 122 fclasses: tfphashobjectlist; 123 public 124 constructor create(hashobjectlist:tfphashobjectlist;const n: shortstring);reintroduce; 125 destructor destroy; override; 126 addclassnull127 function addclass(const n: shortstring; instantiated: boolean): tclassdevirtinfo; findclassnull128 function findclass(const n: shortstring): tclassdevirtinfo; 129 end; 130 131 { devirtualisation information for all units in a program } 132 133 { tprogdevirtinfo } 134 135 tprogdevirtinfo = class(twpodevirtualisationhandler) 136 private 137 { hashtable of tunitdevirtinfo (which contain tclassdevirtinfo) } 138 funits: tfphashobjectlist; 139 140 procedure converttreenode(node: tinheritancetreenode; arg: pointer); addunitifnewnull141 function addunitifnew(const n: shortstring): tunitdevirtinfo; findunitnull142 function findunit(const n: shortstring): tunitdevirtinfo; getstaticnamenull143 function getstaticname(forvmtentry: boolean; objdef, procdef: tdef; out staticname: TSymStr): boolean; 144 procedure documentformat(writer: twposectionwriterintf); 145 public 146 constructor create; override; 147 destructor destroy; override; 148 getwpotypenull149 class function getwpotype: twpotype; override; generatesinfoforwposwitchesnull150 class function generatesinfoforwposwitches: twpoptimizerswitches; override; performswpoforswitchesnull151 class function performswpoforswitches: twpoptimizerswitches; override; sectionnamenull152 class function sectionname: shortstring; override; 153 154 { information collection } 155 procedure constructfromcompilerstate; override; 156 procedure storewpofilesection(writer: twposectionwriterintf); override; 157 158 { information providing } 159 procedure loadfromwpofilesection(reader: twposectionreaderintf); override; staticnameforcallingvirtualmethodnull160 function staticnameforcallingvirtualmethod(objdef, procdef: tdef; out staticname: TSymStr): boolean; override; staticnameforvmtentrynull161 function staticnameforvmtentry(objdef, procdef: tdef; out staticname: TSymStr): boolean; override; 162 163 end; 164 165 166 implementation 167 168 uses 169 cutils, 170 fmodule, 171 symconst, 172 symbase, 173 defcmp, 174 verbose; 175 176 const 177 DEVIRT_SECTION_NAME = 'contextinsensitive_devirtualization'; 178 179 { *************************** tinheritancetreenode ************************* } 180 181 constructor tinheritancetreenode.create(_parent: tinheritancetreenode; _def: tobjectdef; _instantiated: boolean); 182 begin 183 fparent:=_parent; 184 fdef:=_def; 185 finstantiated:=_instantiated; 186 if assigned(_def) then 187 fcalledvmtmethods:=tbitset.create(_def.vmtentries.count); 188 end; 189 190 191 destructor tinheritancetreenode.destroy; 192 begin 193 { fchilds owns its members, so it will free them too } 194 fchilds.free; 195 fcalledvmtmethods.free; 196 inherited destroy; 197 end; 198 199 tinheritancetreenode.childcountnull200 function tinheritancetreenode.childcount: longint; 201 begin 202 if assigned(fchilds) then 203 result:=fchilds.count 204 else 205 result:=0; 206 end; 207 208 tinheritancetreenode.haschildsnull209 function tinheritancetreenode.haschilds: boolean; 210 begin 211 result:=assigned(fchilds) 212 end; 213 214 tinheritancetreenode.getchildnull215 function tinheritancetreenode.getchild(index: longint): tinheritancetreenode; 216 begin 217 result:=tinheritancetreenode(fchilds[index]); 218 end; 219 220 tinheritancetreenode.maybeaddchildnull221 function tinheritancetreenode.maybeaddchild(_def: tobjectdef; _instantiated: boolean): tinheritancetreenode; 222 begin 223 { sanity check } 224 if assigned(_def.childof) then 225 begin 226 if (_def.childof<>def) then 227 internalerror(2008092201); 228 end 229 else if assigned(fparent) then 230 internalerror(2008092202); 231 232 if not assigned(fchilds) then 233 fchilds:=tfpobjectlist.create(true); 234 { def already a child -> return } 235 result:=findchild(_def); 236 if assigned(result) then 237 result.finstantiated:=result.finstantiated or _instantiated 238 else 239 begin 240 { not found, add new child } 241 result:=tinheritancetreenode.create(self,_def,_instantiated); 242 fchilds.add(result); 243 end; 244 end; 245 246 tinheritancetreenode.findchildnull247 function tinheritancetreenode.findchild(_def: tobjectdef): tinheritancetreenode; 248 var 249 i: longint; 250 begin 251 result:=nil; 252 if assigned(fchilds) then 253 for i := 0 to fchilds.count-1 do 254 if (tinheritancetreenode(fchilds[i]).def=_def) then 255 begin 256 result:=tinheritancetreenode(fchilds[i]); 257 break; 258 end; 259 end; 260 261 { *************************** tinheritancetree ************************* } 262 263 constructor tinheritancetree.create; 264 begin 265 froots:=tinheritancetreenode.create(nil,nil,false); 266 classrefdefs:=tfpobjectlist.create(false); 267 end; 268 269 270 destructor tinheritancetree.destroy; 271 begin 272 froots.free; 273 classrefdefs.free; 274 inherited destroy; 275 end; 276 277 tinheritancetree.registerinstantiatedobjectdefrecursivenull278 function tinheritancetree.registerinstantiatedobjectdefrecursive(def: tobjectdef; instantiated: boolean): tinheritancetreenode; 279 begin 280 if assigned(def.childof) then 281 begin 282 { recursively add parent, of which we have no info about whether or not it is 283 instantiated at this point -> default to false (will be overridden by "true" 284 if this class is instantioted, since then registerinstantiatedobjdef() will 285 be called for this class as well) 286 } 287 result:=registerinstantiatedobjectdefrecursive(def.childof,false); 288 { and add ourselves to the parent } 289 result:=result.maybeaddchild(def,instantiated); 290 end 291 else 292 { add ourselves to the roots } 293 result:=froots.maybeaddchild(def,instantiated); 294 end; 295 296 297 procedure tinheritancetree.registerinstantiatedobjdef(def: tdef); 298 begin 299 { add the def } 300 if (def.typ=objectdef) then 301 registerinstantiatedobjectdefrecursive(tobjectdef(def),true) 302 else 303 internalerror(2008092401); 304 end; 305 306 307 procedure tinheritancetree.registerinstantiatedclassrefdef(def: tdef); 308 begin 309 { queue for later checking (these are the objectdefs 310 to which the classrefdefs point) } 311 if (def.typ=objectdef) then 312 classrefdefs.add(def) 313 else 314 internalerror(2008101401); 315 end; 316 317 tinheritancetree.getnodefordefnull318 function tinheritancetree.getnodefordef(def: tobjectdef): tinheritancetreenode; 319 begin 320 if assigned(def.childof) then 321 begin 322 result:=getnodefordef(def.childof); 323 if assigned(result) then 324 result:=result.findchild(def); 325 end 326 else 327 result:=froots.findchild(def); 328 end; 329 330 331 procedure tinheritancetree.registercalledvmtentries(entries: tcalledvmtentries); 332 var 333 node: tinheritancetreenode; 334 begin 335 node:=getnodefordef(tobjectdef(entries.objdef)); 336 { it's possible that no instance of this class or its descendants are 337 instantiated 338 } 339 if not assigned(node) then 340 exit; 341 { now mark these methods as (potentially) called for this type and for 342 all of its descendants 343 } 344 addcalledvmtentries(node,entries.calledentries); 345 foreachnodefromroot(node,@addcalledvmtentries,entries.calledentries); 346 end; 347 348 349 procedure tinheritancetree.checkforclassrefinheritance(def: tdef); 350 var 351 i: longint; 352 begin 353 if (def.typ=objectdef) then 354 begin 355 {$ifdef debug_devirt} 356 write(' Checking for classrefdef inheritance of ',def.typename); 357 {$endif debug_devirt} 358 for i:=0 to classrefdefs.count-1 do 359 if def_is_related(tobjectdef(def),tobjectdef(classrefdefs[i])) then 360 begin 361 {$ifdef debug_devirt} 362 writeln('... Found: inherits from Class Of ',tobjectdef(classrefdefs[i]).typename); 363 {$endif debug_devirt} 364 registerinstantiatedobjdef(def); 365 exit; 366 end; 367 {$ifdef debug_devirt} 368 writeln('... Not found!'); 369 {$endif debug_devirt} 370 end; 371 end; 372 373 374 procedure tinheritancetree.foreachnodefromroot(root: tinheritancetreenode; proctocall: tinheritancetreecallback; arg: pointer); 375 376 procedure process(const node: tinheritancetreenode); 377 var 378 i: longint; 379 begin 380 for i:=0 to node.childcount-1 do 381 if node.childs[i].haschilds then 382 begin 383 proctocall(node.childs[i],arg); 384 process(node.childs[i]) 385 end 386 else 387 proctocall(node.childs[i],arg); 388 end; 389 390 begin 391 process(root); 392 end; 393 394 395 procedure tinheritancetree.foreachnode(proctocall: tinheritancetreecallback; arg: pointer); 396 begin 397 foreachnodefromroot(froots,proctocall,arg); 398 end; 399 400 401 procedure tinheritancetree.foreachleafnode(proctocall: tinheritancetreecallback; arg: pointer); 402 403 procedure process(const node: tinheritancetreenode); 404 var 405 i: longint; 406 begin 407 for i:=0 to node.childcount-1 do 408 if node.childs[i].haschilds then 409 process(node.childs[i]) 410 else 411 proctocall(node.childs[i],arg); 412 end; 413 414 begin 415 process(froots); 416 end; 417 418 419 procedure tinheritancetree.markvmethods(node: tinheritancetreenode; p: pointer); 420 var 421 currnode: tinheritancetreenode; 422 pd: tprocdef; 423 i: longint; 424 makeallvirtual: boolean; 425 begin 426 {$IFDEF DEBUG_DEVIRT} 427 writeln('processing leaf node ',node.def.typename); 428 {$ENDIF} 429 { todo: also process interfaces (ImplementedInterfaces) } 430 if (node.def.vmtentries.count=0) then 431 exit; 432 { process all vmt entries for this class/object } 433 for i:=0 to node.def.vmtentries.count-1 do 434 begin 435 currnode:=node; 436 { extra tprocdef(tobject(..)) typecasts so that -CR can catch 437 errors in case the vmtentries are not properly (re)deref'd } 438 pd:=tprocdef(tobject(pvmtentry(currnode.def.vmtentries[i])^.procdef)); 439 { abstract methods cannot be called directly } 440 if (po_abstractmethod in pd.procoptions) then 441 continue; 442 {$IFDEF DEBUG_DEVIRT} 443 writeln(' method ',pd.typename); 444 {$ENDIF} 445 { Now mark all virtual methods static that are the same in parent 446 classes as in this instantiated child class (only instantiated 447 classes can be leaf nodes, since only instantiated classes were 448 added to the tree). 449 If a first child does not override a parent method while a 450 a second one does, the first will mark it as statically 451 callable, but the second will set it to not statically callable. 452 In the opposite situation, the first will mark it as not 453 statically callable and the second will leave it alone. 454 } 455 makeallvirtual:=false; 456 repeat 457 if { stop when this method does not exist in a parent } 458 (currnode.def.vmtentries.count<=i) then 459 break; 460 461 if not assigned(currnode.def.vmcallstaticinfo) then 462 currnode.def.vmcallstaticinfo:=allocmem(currnode.def.vmtentries.count*sizeof(tvmcallstatic)); 463 { if this method cannot be called, we can just mark it as 464 unreachable. This will cause its static name to be set to 465 FPC_ABSTRACTERROR later on. Exception: published methods are 466 always reachable (via RTTI). 467 } 468 if (pd.visibility<>vis_published) and 469 not(currnode.fcalledvmtmethods.isset(i)) then 470 begin 471 currnode.def.vmcallstaticinfo^[i]:=vmcs_unreachable; 472 currnode:=currnode.parent; 473 end 474 { same procdef as in all instantiated childs? (yes or don't know) } 475 else if (currnode.def.vmcallstaticinfo^[i] in [vmcs_default,vmcs_yes]) then 476 begin 477 { methods in uninstantiated classes can be made static if 478 they are the same in all instantiated derived classes 479 } 480 if ((pvmtentry(currnode.def.vmtentries[i])^.procdef=pd) or 481 (not currnode.instantiated and 482 (currnode.def.vmcallstaticinfo^[i]=vmcs_default))) and 483 not makeallvirtual then 484 begin 485 {$IFDEF DEBUG_DEVIRT} 486 writeln(' marking as static for ',currnode.def.typename); 487 {$ENDIF} 488 currnode.def.vmcallstaticinfo^[i]:=vmcs_yes; 489 { this is in case of a non-instantiated parent of an instantiated child: 490 the method declared in the child will always be called here 491 } 492 pvmtentry(currnode.def.vmtentries[i])^.procdef:=pd; 493 end 494 else 495 begin 496 {$IFDEF DEBUG_DEVIRT} 497 writeln(' marking as non-static for ',currnode.def.typename); 498 {$ENDIF} 499 { this vmt entry must also remain virtual for all parents } 500 makeallvirtual:=true; 501 currnode.def.vmcallstaticinfo^[i]:=vmcs_no; 502 end; 503 currnode:=currnode.parent; 504 end 505 else if (currnode.def.vmcallstaticinfo^[i]=vmcs_no) then 506 begin 507 {$IFDEF DEBUG_DEVIRT} 508 writeln(' not processing parents, already non-static for ',currnode.def.typename); 509 {$ENDIF} 510 { parents are already set to vmcs_no, so no need to continue } 511 currnode:=nil; 512 end 513 else 514 currnode:=currnode.parent; 515 until not assigned(currnode) or 516 not assigned(currnode.def); 517 end; 518 end; 519 520 521 procedure tinheritancetree.optimizevirtualmethods; 522 begin 523 foreachleafnode(@markvmethods,nil); 524 end; 525 526 527 procedure tinheritancetree.printobjectvmtinfo(node: tinheritancetreenode; arg: pointer); 528 var 529 i, 530 totaldevirtualised, 531 totalvirtual, 532 totalunreachable: ptrint; 533 begin 534 totaldevirtualised:=0; 535 totalvirtual:=0; 536 totalunreachable:=0; 537 writeln(node.def.typename); 538 if (node.def.vmtentries.count=0) then 539 begin 540 writeln(' No virtual methods!'); 541 exit; 542 end; 543 for i:=0 to node.def.vmtentries.count-1 do 544 if (po_virtualmethod in pvmtentry(node.def.vmtentries[i])^.procdef.procoptions) then 545 begin 546 inc(totalvirtual); 547 if (node.def.vmcallstaticinfo^[i]=vmcs_yes) then 548 begin 549 inc(totaldevirtualised); 550 writeln(' Devirtualised: ',pvmtentry(node.def.vmtentries[i])^.procdef.typename); 551 end 552 else if (node.def.vmcallstaticinfo^[i]=vmcs_unreachable) then 553 begin 554 inc(totalunreachable); 555 writeln(' Unreachable: ',pvmtentry(node.def.vmtentries[i])^.procdef.typename); 556 end; 557 end; 558 writeln('Total devirtualised/unreachable/all: ',totaldevirtualised,'/',totalunreachable,'/',totalvirtual); 559 writeln; 560 end; 561 562 563 procedure tinheritancetree.addcalledvmtentries(node: tinheritancetreenode; arg: pointer); 564 var 565 vmtentries: tbitset absolute arg; 566 begin 567 node.fcalledvmtmethods.addset(vmtentries); 568 end; 569 570 571 procedure tinheritancetree.printvmtinfo; 572 begin 573 foreachnode(@printobjectvmtinfo,nil); 574 end; 575 576 577 { helper routines: decompose an object & procdef combo into a unitname, class name and vmtentry number 578 (unit name where the objectdef is declared, class name of the objectdef, vmtentry number of the 579 procdef -- procdef does not necessarily belong to objectdef, it may also belong to a descendant 580 or parent). classprefix is set in case of nested classes. 581 } 582 583 procedure defunitclassname(objdef: tobjectdef; out unitname, classname: pshortstring; out classprefix: shortstring); 584 const 585 mainprogname: string[2] = 'P$'; 586 var 587 mainsymtab, 588 objparentsymtab : tsymtable; 589 begin 590 objparentsymtab:=objdef.symtable; 591 mainsymtab:=objparentsymtab.defowner.owner; 592 classprefix:=''; 593 while mainsymtab.symtabletype in [recordsymtable,objectsymtable] do 594 begin 595 classprefix:=mainsymtab.name^+'.'+classprefix; 596 mainsymtab:=mainsymtab.defowner.owner; 597 end; 598 { main symtable must be static or global } 599 if not(mainsymtab.symtabletype in [staticsymtable,globalsymtable]) then 600 internalerror(200204177); 601 if (TSymtable(main_module.localsymtable)=mainsymtab) and 602 (not main_module.is_unit) then 603 { same convention as for mangled names } 604 unitname:=@mainprogname 605 else 606 unitname:=mainsymtab.name; 607 classname:=tobjectdef(objparentsymtab.defowner).objname; 608 end; 609 610 611 procedure defsdecompose(objdef: tobjectdef; procdef: tprocdef; out unitname, classname: pshortstring; out classprefix: shortstring; out vmtentry: longint); 612 begin 613 defunitclassname(objdef,unitname,classname,classprefix); 614 vmtentry:=procdef.extnumber; 615 { if it's $ffff, this is not a valid virtual method } 616 if (vmtentry=$ffff) then 617 internalerror(2008100509); 618 end; 619 620 621 { tclassdevirtinfo } 622 623 constructor tclassdevirtinfo.create(hashobjectlist:tfphashobjectlist;const n: shortstring; instantiated: boolean); 624 begin 625 inherited create(hashobjectlist,n); 626 finstantiated:=instantiated; 627 fstaticmethodnames:=tfplist.create; 628 end; 629 630 destructor tclassdevirtinfo.destroy; 631 var 632 i: longint; 633 begin 634 for i:=0 to fstaticmethodnames.count-1 do 635 if assigned(fstaticmethodnames[i]) then 636 freemem(fstaticmethodnames[i]); 637 fstaticmethodnames.free; 638 inherited destroy; 639 end; 640 641 procedure tclassdevirtinfo.addstaticmethod(vmtindex: longint; 642 const replacementname: shortstring); 643 begin 644 if (vmtindex>=fstaticmethodnames.count) then 645 fstaticmethodnames.Count:=vmtindex+10; 646 fstaticmethodnames[vmtindex]:=stringdup(replacementname); 647 end; 648 tclassdevirtinfo.isstaticvmtentrynull649 function tclassdevirtinfo.isstaticvmtentry(vmtindex: longint; out 650 replacementname: pshortstring): boolean; 651 begin 652 result:=false; 653 if (vmtindex>=fstaticmethodnames.count) then 654 exit; 655 656 replacementname:=fstaticmethodnames[vmtindex]; 657 result:=assigned(replacementname); 658 end; 659 660 { tunitdevirtinfo } 661 662 constructor tunitdevirtinfo.create(hashobjectlist:tfphashobjectlist;const n: shortstring); 663 begin 664 inherited create(hashobjectlist,n); 665 fclasses:=tfphashobjectlist.create(true); 666 end; 667 668 destructor tunitdevirtinfo.destroy; 669 begin 670 fclasses.free; 671 inherited destroy; 672 end; 673 tunitdevirtinfo.addclassnull674 function tunitdevirtinfo.addclass(const n: shortstring; instantiated: boolean): tclassdevirtinfo; 675 begin 676 result:=findclass(n); 677 { can't have two classes with the same name in a single unit } 678 if assigned(result) then 679 internalerror(2008100501); 680 result:=tclassdevirtinfo.create(fclasses,n,instantiated); 681 end; 682 tunitdevirtinfo.findclassnull683 function tunitdevirtinfo.findclass(const n: shortstring): tclassdevirtinfo; 684 begin 685 result:=tclassdevirtinfo(fclasses.find(n)); 686 end; 687 688 689 { tprogdevirtinfo } 690 691 procedure tprogdevirtinfo.converttreenode(node: tinheritancetreenode; arg: pointer); 692 var 693 i: longint; 694 classprefix: shortstring; 695 unitid, classid: pshortstring; 696 unitdevirtinfo: tunitdevirtinfo; 697 classdevirtinfo: tclassdevirtinfo; 698 begin 699 if (not node.instantiated) and 700 (node.def.vmtentries.count=0) then 701 exit; 702 { always add a class entry for an instantiated class, so we can 703 fill the vmt's of non-instantiated classes with calls to 704 FPC_ABSTRACTERROR during the optimisation phase 705 } 706 defunitclassname(node.def,unitid,classid,classprefix); 707 unitdevirtinfo:=addunitifnew(unitid^); 708 classdevirtinfo:=unitdevirtinfo.addclass(classprefix+classid^,node.instantiated); 709 if (node.def.vmtentries.count=0) then 710 exit; 711 for i:=0 to node.def.vmtentries.count-1 do 712 if (po_virtualmethod in pvmtentry(node.def.vmtentries[i])^.procdef.procoptions) then 713 case node.def.vmcallstaticinfo^[i] of 714 vmcs_yes: 715 begin 716 { add info about devirtualised vmt entry } 717 classdevirtinfo.addstaticmethod(i,pvmtentry(node.def.vmtentries[i])^.procdef.mangledname); 718 end; 719 vmcs_unreachable: 720 begin 721 { static reference to FPC_ABSTRACTERROR } 722 classdevirtinfo.addstaticmethod(i,'FPC_ABSTRACTERROR'); 723 end; 724 end; 725 end; 726 727 728 constructor tprogdevirtinfo.create; 729 begin 730 inherited create; 731 end; 732 733 734 destructor tprogdevirtinfo.destroy; 735 begin 736 funits.free; 737 inherited destroy; 738 end; 739 740 tprogdevirtinfo.getwpotypenull741 class function tprogdevirtinfo.getwpotype: twpotype; 742 begin 743 result:=wpo_devirtualization_context_insensitive; 744 end; 745 746 tprogdevirtinfo.generatesinfoforwposwitchesnull747 class function tprogdevirtinfo.generatesinfoforwposwitches: twpoptimizerswitches; 748 begin 749 result:=[cs_wpo_devirtualize_calls,cs_wpo_optimize_vmts]; 750 end; 751 752 tprogdevirtinfo.performswpoforswitchesnull753 class function tprogdevirtinfo.performswpoforswitches: twpoptimizerswitches; 754 begin 755 result:=[cs_wpo_devirtualize_calls,cs_wpo_optimize_vmts]; 756 end; 757 758 tprogdevirtinfo.sectionnamenull759 class function tprogdevirtinfo.sectionname: shortstring; 760 begin 761 result:=DEVIRT_SECTION_NAME; 762 end; 763 764 765 procedure tprogdevirtinfo.constructfromcompilerstate; 766 var 767 hp: tmodule; 768 i: longint; 769 inheritancetree: tinheritancetree; 770 begin 771 { register all instantiated class/object types } 772 hp:=tmodule(loaded_units.first); 773 while assigned(hp) do 774 begin 775 if assigned(hp.wpoinfo.createdobjtypes) then 776 for i:=0 to hp.wpoinfo.createdobjtypes.count-1 do 777 tdef(hp.wpoinfo.createdobjtypes[i]).register_created_object_type; 778 if assigned(hp.wpoinfo.createdclassrefobjtypes) then 779 for i:=0 to hp.wpoinfo.createdclassrefobjtypes.count-1 do 780 tobjectdef(hp.wpoinfo.createdclassrefobjtypes[i]).register_created_classref_type; 781 if assigned(hp.wpoinfo.maybecreatedbyclassrefdeftypes) then 782 for i:=0 to hp.wpoinfo.maybecreatedbyclassrefdeftypes.count-1 do 783 tobjectdef(hp.wpoinfo.maybecreatedbyclassrefdeftypes[i]).register_maybe_created_object_type; 784 hp:=tmodule(hp.next); 785 end; 786 inheritancetree:=tinheritancetree.create; 787 788 { add all constructed class/object types to the tree } 789 {$IFDEF DEBUG_DEVIRT} 790 writeln('constructed object/class/classreftypes in ',current_module.realmodulename^); 791 {$ENDIF} 792 for i := 0 to current_module.wpoinfo.createdobjtypes.count-1 do 793 begin 794 inheritancetree.registerinstantiatedobjdef(tdef(current_module.wpoinfo.createdobjtypes[i])); 795 {$IFDEF DEBUG_DEVIRT} 796 write(' ',tdef(current_module.wpoinfo.createdobjtypes[i]).GetTypeName); 797 {$ENDIF} 798 case tdef(current_module.wpoinfo.createdobjtypes[i]).typ of 799 objectdef: 800 case tobjectdef(current_module.wpoinfo.createdobjtypes[i]).objecttype of 801 odt_object: 802 {$IFDEF DEBUG_DEVIRT} 803 writeln(' (object)') 804 {$ENDIF} 805 ; 806 odt_class: 807 {$IFDEF DEBUG_DEVIRT} 808 writeln(' (class)') 809 {$ENDIF} 810 ; 811 else 812 internalerror(2008092101); 813 end; 814 else 815 internalerror(2008092102); 816 end; 817 end; 818 819 { register all instantiated classrefdefs with the tree } 820 for i := 0 to current_module.wpoinfo.createdclassrefobjtypes.count-1 do 821 begin 822 inheritancetree.registerinstantiatedclassrefdef(tdef(current_module.wpoinfo.createdclassrefobjtypes[i])); 823 {$IFDEF DEBUG_DEVIRT} 824 write(' Class Of ',tdef(current_module.wpoinfo.createdclassrefobjtypes[i]).GetTypeName); 825 {$ENDIF} 826 case tdef(current_module.wpoinfo.createdclassrefobjtypes[i]).typ of 827 objectdef: 828 {$IFDEF DEBUG_DEVIRT} 829 writeln(' (classrefdef)') 830 {$ENDIF} 831 ; 832 else 833 internalerror(2008101101); 834 end; 835 end; 836 837 838 { now add all objectdefs that are referred somewhere (via a 839 loadvmtaddr node) and that are derived from an instantiated 840 classrefdef to the tree (as they can, in theory, all 841 be instantiated as well) 842 } 843 for i := 0 to current_module.wpoinfo.maybecreatedbyclassrefdeftypes.count-1 do 844 begin 845 inheritancetree.checkforclassrefinheritance(tdef(current_module.wpoinfo.maybecreatedbyclassrefdeftypes[i])); 846 {$IFDEF DEBUG_DEVIRT} 847 write(' Class Of ',tdef(current_module.wpoinfo.maybecreatedbyclassrefdeftypes[i]).GetTypeName); 848 {$ENDIF} 849 case tdef(current_module.wpoinfo.maybecreatedbyclassrefdeftypes[i]).typ of 850 objectdef: 851 {$IFDEF DEBUG_DEVIRT} 852 writeln(' (classrefdef)') 853 {$ENDIF} 854 ; 855 else 856 internalerror(2008101101); 857 end; 858 end; 859 860 { add info about called virtual methods } 861 hp:=tmodule(loaded_units.first); 862 while assigned(hp) do 863 begin 864 if assigned(hp.wpoinfo.calledvmtentries) then 865 for i:=0 to hp.wpoinfo.calledvmtentries.count-1 do 866 inheritancetree.registercalledvmtentries(tcalledvmtentries(hp.wpoinfo.calledvmtentries[i])); 867 hp:=tmodule(hp.next); 868 end; 869 870 871 inheritancetree.optimizevirtualmethods; 872 {$ifdef DEBUG_DEVIRT} 873 inheritancetree.printvmtinfo; 874 {$endif DEBUG_DEVIRT} 875 inheritancetree.foreachnode(@converttreenode,nil); 876 inheritancetree.free; 877 end; 878 879 tprogdevirtinfo.addunitifnewnull880 function tprogdevirtinfo.addunitifnew(const n: shortstring): tunitdevirtinfo; 881 begin 882 if assigned(funits) then 883 result:=findunit(n) 884 else 885 begin 886 funits:=tfphashobjectlist.create; 887 result:=nil; 888 end; 889 if not assigned(result) then 890 begin 891 result:=tunitdevirtinfo.create(funits,n); 892 end; 893 end; 894 895 tprogdevirtinfo.findunitnull896 function tprogdevirtinfo.findunit(const n: shortstring): tunitdevirtinfo; 897 begin 898 result:=tunitdevirtinfo(funits.find(n)); 899 end; 900 901 902 procedure tprogdevirtinfo.loadfromwpofilesection(reader: twposectionreaderintf); 903 var 904 unitid, 905 classid, 906 vmtentryname: string; 907 vmttype: string[15]; 908 vmtentrynrstr: string[7]; 909 classinstantiated: string[1]; 910 vmtentry, error: longint; 911 unitdevirtinfo: tunitdevirtinfo; 912 classdevirtinfo: tclassdevirtinfo; 913 instantiated: boolean; 914 begin 915 { format: 916 # unitname^ 917 unit1^ 918 # classname& 919 class1& 920 # instantiated? 921 1 922 # vmt type (base or some interface) 923 basevmt 924 # vmt entry nr 925 0 926 # name of routine to call instead 927 staticvmtentryforslot0 928 5 929 staticvmtentryforslot5 930 intfvmt1 931 0 932 staticvmtentryforslot0 933 934 # non-instantiated class (but if we encounter a variable of this 935 # type, we can optimise class to vmtentry 1) 936 class2& 937 0 938 basevmt 939 1 940 staticvmtentryforslot1 941 942 # instantiated class without optimisable virtual methods 943 class3& 944 1 945 946 unit2^ 947 1 948 class3& 949 ... 950 951 currently, only basevmt is supported (no interfaces yet) 952 } 953 { could be empty if no classes or so } 954 if not reader.sectiongetnextline(unitid) then 955 exit; 956 repeat 957 if (unitid='') or 958 (unitid[length(unitid)]<>'^') then 959 internalerror(2008100502); 960 { cut off the trailing ^ } 961 setlength(unitid,length(unitid)-1); 962 unitdevirtinfo:=addunitifnew(unitid); 963 { now read classes } 964 if not reader.sectiongetnextline(classid) then 965 internalerror(2008100505); 966 repeat 967 if (classid='') or 968 (classid[length(classid)]<>'&') then 969 internalerror(2008100503); 970 { instantiated? } 971 if not reader.sectiongetnextline(classinstantiated) then 972 internalerror(2008101901); 973 instantiated:=classinstantiated='1'; 974 { cut off the trailing & } 975 setlength(classid,length(classid)-1); 976 classdevirtinfo:=unitdevirtinfo.addclass(classid,instantiated); 977 { last class could be an instantiated class without any 978 optimisable methods. } 979 if not reader.sectiongetnextline(vmttype) then 980 exit; 981 { any optimisable virtual methods? } 982 if (vmttype<>'') then 983 begin 984 { interface info is not yet supported } 985 if (vmttype<>'basevmt') then 986 internalerror(2008100507); 987 { read all vmt entries for this class } 988 while reader.sectiongetnextline(vmtentrynrstr) and 989 (vmtentrynrstr<>'') do 990 begin 991 val(vmtentrynrstr,vmtentry,error); 992 if (error<>0) then 993 internalerror(2008100504); 994 if not reader.sectiongetnextline(vmtentryname) or 995 (vmtentryname='') then 996 internalerror(2008100508); 997 classdevirtinfo.addstaticmethod(vmtentry,vmtentryname); 998 end; 999 end; 1000 { end of section -> exit } 1001 if not(reader.sectiongetnextline(classid)) then 1002 exit; 1003 until (classid='') or 1004 (classid[length(classid)]='^'); 1005 { next unit, or error } 1006 unitid:=classid; 1007 until false; 1008 end; 1009 1010 1011 procedure tprogdevirtinfo.documentformat(writer: twposectionwriterintf); 1012 begin 1013 writer.sectionputline('# section format:'); 1014 writer.sectionputline('# unit1^'); 1015 writer.sectionputline('# class1& ; classname&'); 1016 writer.sectionputline('# 1 ; instantiated or not'); 1017 writer.sectionputline('# basevmt ; vmt type (base or some interface)'); 1018 writer.sectionputline('# # vmt entry nr'); 1019 writer.sectionputline('# 0 ; vmt entry nr'); 1020 writer.sectionputline('# staticvmtentryforslot0 ; name or routine to call instead'); 1021 writer.sectionputline('# 5'); 1022 writer.sectionputline('# staticvmtentryforslot5'); 1023 writer.sectionputline('# intfvmt1'); 1024 writer.sectionputline('# 0'); 1025 writer.sectionputline('# staticvmtentryforslot0'); 1026 writer.sectionputline('#'); 1027 writer.sectionputline('# class2&'); 1028 writer.sectionputline('# 0 ; non-instantiated class (can be variables of this type, e.g. TObject)'); 1029 writer.sectionputline('# basevmt'); 1030 writer.sectionputline('# 1'); 1031 writer.sectionputline('# staticvmtentryforslot1'); 1032 writer.sectionputline('#'); 1033 writer.sectionputline('# class3& ; instantiated class without optimisable virtual methods'); 1034 writer.sectionputline('# 1'); 1035 writer.sectionputline('#'); 1036 writer.sectionputline('# unit2^'); 1037 writer.sectionputline('# 1'); 1038 writer.sectionputline('# class3&'); 1039 writer.sectionputline('# ...'); 1040 writer.sectionputline('#'); 1041 writer.sectionputline('# currently, only basevmt is supported (no interfaces yet)'); 1042 writer.sectionputline('#'); 1043 end; 1044 1045 1046 procedure tprogdevirtinfo.storewpofilesection(writer: twposectionwriterintf); 1047 var 1048 unitcount, 1049 classcount, 1050 vmtentrycount: longint; 1051 unitdevirtinfo: tunitdevirtinfo; 1052 classdevirtinfo: tclassdevirtinfo; 1053 first: boolean; 1054 begin 1055 writer.startsection(DEVIRT_SECTION_NAME); 1056 { if there are no optimised virtual methods, we have stored no info } 1057 if not assigned(funits) then 1058 exit; 1059 documentformat(writer); 1060 for unitcount:=0 to funits.count-1 do 1061 begin 1062 unitdevirtinfo:=tunitdevirtinfo(funits[unitcount]); 1063 writer.sectionputline(unitdevirtinfo.name+'^'); 1064 for classcount:=0 to unitdevirtinfo.fclasses.count-1 do 1065 begin 1066 classdevirtinfo:=tclassdevirtinfo(tunitdevirtinfo(funits[unitcount]).fclasses[classcount]); 1067 writer.sectionputline(classdevirtinfo.name+'&'); 1068 writer.sectionputline(tostr(ord(classdevirtinfo.instantiated))); 1069 first:=true; 1070 for vmtentrycount:=0 to classdevirtinfo.fstaticmethodnames.count-1 do 1071 if assigned(classdevirtinfo.fstaticmethodnames[vmtentrycount]) then 1072 begin 1073 if first then 1074 begin 1075 writer.sectionputline('basevmt'); 1076 first:=false; 1077 end; 1078 writer.sectionputline(tostr(vmtentrycount)); 1079 writer.sectionputline(pshortstring(classdevirtinfo.fstaticmethodnames[vmtentrycount])^); 1080 end; 1081 writer.sectionputline(''); 1082 end; 1083 end; 1084 end; 1085 1086 tprogdevirtinfo.getstaticnamenull1087 function tprogdevirtinfo.getstaticname(forvmtentry: boolean; objdef, procdef: tdef; out staticname: TSymStr): boolean; 1088 var 1089 unitid, 1090 classid, 1091 newname: pshortstring; 1092 unitdevirtinfo: tunitdevirtinfo; 1093 classdevirtinfo: tclassdevirtinfo; 1094 vmtentry: longint; 1095 realobjdef: tobjectdef; 1096 classprefix: shortstring; 1097 begin 1098 { if we don't have any devirtualisation info, exit } 1099 if not assigned(funits) then 1100 begin 1101 result:=false; 1102 exit 1103 end; 1104 { class methods are in the regular vmt, so we can handle classrefs 1105 the same way as plain objectdefs 1106 } 1107 if (objdef.typ=classrefdef) then 1108 realobjdef:=tobjectdef(tclassrefdef(objdef).pointeddef) 1109 else if (objdef.typ=objectdef) and 1110 (tobjectdef(objdef).objecttype in [odt_class,odt_object]) then 1111 realobjdef:=tobjectdef(objdef) 1112 else 1113 begin 1114 { we don't support interfaces yet } 1115 result:=false; 1116 exit; 1117 end; 1118 1119 { if it's for a vmtentry of an objdef and the objdef is 1120 not instantiated, then we can fill the vmt with pointers 1121 to FPC_ABSTRACTERROR, except for published methods 1122 (these can be called via rtti, so always have to point 1123 to the original method) 1124 } 1125 if forvmtentry and 1126 (tprocdef(procdef).visibility=vis_published) then 1127 begin 1128 result:=false; 1129 exit; 1130 end; 1131 1132 { get the component names for the class/procdef combo } 1133 defsdecompose(realobjdef,tprocdef(procdef),unitid,classid,classprefix,vmtentry); 1134 1135 { If we don't have information about a particular unit/class/method, 1136 it means that such class cannot be instantiated. So if we are 1137 looking up information for a vmt entry, we can always safely return 1138 FPC_ABSTRACTERROR if we do not find anything, unless it's a 1139 published method (but those are handled already above) or a 1140 class method (can be called even if the class is not instantiated). 1141 } 1142 result:= 1143 forvmtentry and 1144 not(po_classmethod in tprocdef(procdef).procoptions); 1145 staticname:='FPC_ABSTRACTERROR'; 1146 1147 { do we have any info for this unit? } 1148 unitdevirtinfo:=findunit(unitid^); 1149 if not assigned(unitdevirtinfo) then 1150 exit; 1151 { and for this class? } 1152 classdevirtinfo:=unitdevirtinfo.findclass(classprefix+classid^); 1153 if not assigned(classdevirtinfo) then 1154 exit; 1155 if forvmtentry and 1156 (objdef.typ=objectdef) and 1157 not classdevirtinfo.instantiated and 1158 { virtual class methods can be called even if the class is not instantiated } 1159 not(po_classmethod in tprocdef(procdef).procoptions) then 1160 begin 1161 { already set above 1162 staticname:='FPC_ABSTRACTERROR'; 1163 } 1164 result:=true; 1165 end 1166 else 1167 begin 1168 { now check whether it can be devirtualised, and if so to what } 1169 result:=classdevirtinfo.isstaticvmtentry(vmtentry,newname); 1170 if result then 1171 staticname:=newname^; 1172 end; 1173 end; 1174 1175 1176 tprogdevirtinfo.staticnameforcallingvirtualmethodnull1177 function tprogdevirtinfo.staticnameforcallingvirtualmethod(objdef, procdef: tdef; out staticname: TSymStr): boolean; 1178 begin 1179 result:=getstaticname(false,objdef,procdef,staticname); 1180 end; 1181 1182 tprogdevirtinfo.staticnameforvmtentrynull1183 function tprogdevirtinfo.staticnameforvmtentry(objdef, procdef: tdef; out staticname: TSymStr): boolean; 1184 begin 1185 result:=getstaticname(true,objdef,procdef,staticname); 1186 end; 1187 1188 end. 1189