wpobase.pas 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680
  1. {
  2. Copyright (c) 2008 by Jonas Maebe
  3. Whole program optimisation information collection base class
  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 wpobase;
  18. {$i fpcdefs.inc}
  19. interface
  20. uses
  21. globtype,
  22. cclasses,
  23. symtype;
  24. type
  25. { the types of available whole program optimization }
  26. twpotype = (wpo_devirtualization_context_insensitive,wpo_live_symbol_information);
  27. const
  28. wpo2str: array[twpotype] of string[16] = ('devirtualization','symbol liveness');
  29. type
  30. { ************************************************************************* }
  31. { ******************** General base classes/interfaces ******************** }
  32. { ************************************************************************* }
  33. { interface to reading a section from a file with wpo info }
  34. twposectionreaderintf = interface
  35. ['{51BE3F89-C9C5-4965-9C83-AE7490C92E3E}']
  36. function sectiongetnextline(out s: string): boolean;
  37. end;
  38. { interface to writing sections to a file with wpoinfo }
  39. twposectionwriterintf = interface
  40. ['{C056F0DD-62B1-4612-86C7-2D39944C4437}']
  41. procedure startsection(const name: string);
  42. procedure sectionputline(const s: string);
  43. end;
  44. { base class for wpo information stores }
  45. { twpocomponentbase }
  46. twpocomponentbase = class
  47. public
  48. constructor create; reintroduce; virtual;
  49. { type of whole program optimization information collected/provided by
  50. this class
  51. }
  52. class function getwpotype: twpotype; virtual; abstract;
  53. { whole program optimizations for which this class generates information }
  54. class function generatesinfoforwposwitches: twpoptimizerswitches; virtual; abstract;
  55. { whole program optimizations performed by this class }
  56. class function performswpoforswitches: twpoptimizerswitches; virtual; abstract;
  57. { returns the name of the section parsed by this class }
  58. class function sectionname: shortstring; virtual; abstract;
  59. { checks whether the compiler options are compatible with this
  60. optimization (default: don't check anything)
  61. }
  62. class procedure checkoptions; virtual;
  63. { loads the information pertinent to this whole program optimization from
  64. the current section being processed by reader
  65. }
  66. procedure loadfromwpofilesection(reader: twposectionreaderintf); virtual; abstract;
  67. { stores the information of this component to a file in a format that can
  68. be loaded again using loadfromwpofilesection()
  69. }
  70. procedure storewpofilesection(writer: twposectionwriterintf); virtual; abstract;
  71. { extracts the information pertinent to this whole program optimization
  72. from the current compiler state (loaded units, ...)
  73. }
  74. procedure constructfromcompilerstate; virtual; abstract;
  75. end;
  76. twpocomponentbaseclass = class of twpocomponentbase;
  77. { forward declaration of overall wpo info manager class }
  78. twpoinfomanagerbase = class;
  79. { ************************************************************************* }
  80. { ** Information created per unit for use during subsequent compilation *** }
  81. { ************************************************************************* }
  82. { base class of information collected per unit. Still needs to be
  83. generalised for different kinds of wpo information, currently specific
  84. to devirtualization.
  85. }
  86. tunitwpoinfobase = class
  87. protected
  88. { created object types }
  89. fcreatedobjtypes: tfpobjectlist;
  90. { objectdefs pointed to by created classrefdefs }
  91. fcreatedclassrefobjtypes: tfpobjectlist;
  92. { objtypes potentially instantiated by fcreatedclassrefobjtypes
  93. (objdectdefs pointed to by classrefdefs that are
  94. passed as a regular parameter, loaded in a variable, ...
  95. so they can end up in a classrefdef var and be instantiated)
  96. }
  97. fmaybecreatedbyclassrefdeftypes: tfpobjectlist;
  98. public
  99. constructor create; reintroduce; virtual;
  100. destructor destroy; override;
  101. property createdobjtypes: tfpobjectlist read fcreatedobjtypes;
  102. property createdclassrefobjtypes: tfpobjectlist read fcreatedclassrefobjtypes;
  103. property maybecreatedbyclassrefdeftypes: tfpobjectlist read fmaybecreatedbyclassrefdeftypes;
  104. procedure addcreatedobjtype(def: tdef);
  105. procedure addcreatedobjtypeforclassref(def: tdef);
  106. procedure addmaybecreatedbyclassref(def: tdef);
  107. end;
  108. { ************************************************************************* }
  109. { **** Total information created for use during subsequent compilation **** }
  110. { ************************************************************************* }
  111. { class to create a file with wpo information }
  112. { tavailablewpofilewriter }
  113. twpofilewriter = class(tobject,twposectionwriterintf)
  114. private
  115. { array of class *instances* that wish to be written out to the
  116. whole program optimization feedback file
  117. }
  118. fsectioncontents: tfpobjectlist;
  119. ffilename: tcmdstr;
  120. foutputfile: text;
  121. public
  122. constructor create(const fn: tcmdstr);
  123. destructor destroy; override;
  124. procedure writefile;
  125. { starts a new section with name "name" }
  126. procedure startsection(const name: string);
  127. { writes s to the wpo file }
  128. procedure sectionputline(const s: string);
  129. { register a component instance that needs to be written
  130. to the wpo feedback file
  131. }
  132. procedure registerwpocomponent(component: twpocomponentbase);
  133. end;
  134. { ************************************************************************* }
  135. { ************ Information for use during current compilation ************* }
  136. { ************************************************************************* }
  137. { class to read a file with wpo information }
  138. twpofilereader = class(tobject,twposectionreaderintf)
  139. private
  140. ffilename: tcmdstr;
  141. flinenr: longint;
  142. finputfile: text;
  143. fcurline: string;
  144. fusecurline: boolean;
  145. { destination for the read information }
  146. fdest: twpoinfomanagerbase;
  147. function getnextnoncommentline(out s: string): boolean;
  148. public
  149. constructor create(const fn: tcmdstr; dest: twpoinfomanagerbase);
  150. destructor destroy; override;
  151. { processes the wpo info in the file }
  152. procedure processfile;
  153. { returns next line of the current section in s, and false if no more
  154. lines in the current section
  155. }
  156. function sectiongetnextline(out s: string): boolean;
  157. end;
  158. { ************************************************************************* }
  159. { ******* Specific kinds of whole program optimization components ********* }
  160. { ************************************************************************* }
  161. { method devirtualisation }
  162. twpodevirtualisationhandler = class(twpocomponentbase)
  163. { checks whether procdef (a procdef for a virtual method) can be replaced with
  164. a static call when it's called as objdef.procdef, and if so returns the
  165. mangled name in staticname.
  166. }
  167. function staticnameforcallingvirtualmethod(objdef, procdef: tdef; out staticname: string): boolean; virtual; abstract;
  168. { checks whether procdef (a procdef for a virtual method) can be replaced with
  169. a different procname in the vmt of objdef, and if so returns the new
  170. mangledname in staticname
  171. }
  172. function staticnameforvmtentry(objdef, procdef: tdef; out staticname: string): boolean; virtual; abstract;
  173. end;
  174. twpodeadcodehandler = class(twpocomponentbase)
  175. { checks whether a mangledname was removed as dead code from the final
  176. binary (WARNING: must *not* be called for functions marked as inline,
  177. since if all call sites are inlined, it won't appear in the final
  178. binary but nevertheless is still necessary!)
  179. }
  180. function symbolinfinalbinary(const s: shortstring): boolean; virtual; abstract;
  181. end;
  182. { ************************************************************************* }
  183. { ************ Collection of all instances of wpo components ************** }
  184. { ************************************************************************* }
  185. { class doing all the bookkeeping for everything }
  186. twpoinfomanagerbase = class
  187. private
  188. { array of classrefs of handler classes for the various kinds of whole
  189. program optimizations that we support
  190. }
  191. fwpocomponents: tfphashlist;
  192. freader: twpofilereader;
  193. fwriter: twpofilewriter;
  194. public
  195. { instances of the various optimizers/information collectors (for
  196. information used during this compilation)
  197. }
  198. wpoinfouse: array[twpotype] of twpocomponentbase;
  199. { register a whole program optimization class type }
  200. procedure registerwpocomponentclass(wpocomponent: twpocomponentbaseclass);
  201. { get the program optimization class type that can parse the contents
  202. of the section with name "secname" in the wpo feedback file
  203. }
  204. function gethandlerforsection(const secname: string): twpocomponentbaseclass;
  205. { tell all instantiated wpo component classes to collect the information
  206. from the global compiler state that they need (done at the very end of
  207. the compilation process)
  208. }
  209. procedure extractwpoinfofromprogram;
  210. { set the name of the feedback file from which all whole-program information
  211. to be used during the current compilation will be read
  212. }
  213. procedure setwpoinputfile(const fn: tcmdstr);
  214. { set the name of the feedback file to which all whole-program information
  215. collected during the current compilation will be written
  216. }
  217. procedure setwpooutputfile(const fn: tcmdstr);
  218. { check whether the specified wpo options (-FW/-Fw/-OW/-Ow) are complete
  219. and sensical, and parse the wpo feedback file specified with
  220. setwpoinputfile
  221. }
  222. procedure parseandcheckwpoinfo;
  223. { routines accessing the optimizer information }
  224. { 1) devirtualization at the symbol name level }
  225. function can_be_devirtualized(objdef, procdef: tdef; out name: shortstring): boolean; virtual; abstract;
  226. { 2) optimal replacement method name in vmt }
  227. function optimized_name_for_vmt(objdef, procdef: tdef; out name: shortstring): boolean; virtual; abstract;
  228. { 3) does a symbol appear in the final binary (i.e., not removed by dead code stripping/smart linking).
  229. WARNING: do *not* call for inline functions/procedures/methods/...
  230. }
  231. function symbol_live(const name: shortstring): boolean; virtual; abstract;
  232. constructor create; reintroduce;
  233. destructor destroy; override;
  234. end;
  235. var
  236. wpoinfomanager: twpoinfomanagerbase;
  237. implementation
  238. uses
  239. globals,
  240. cutils,
  241. sysutils,
  242. symdef,
  243. verbose;
  244. { tcreatedwpoinfobase }
  245. constructor tunitwpoinfobase.create;
  246. begin
  247. fcreatedobjtypes:=tfpobjectlist.create(false);
  248. fcreatedclassrefobjtypes:=tfpobjectlist.create(false);
  249. fmaybecreatedbyclassrefdeftypes:=tfpobjectlist.create(false);
  250. end;
  251. destructor tunitwpoinfobase.destroy;
  252. begin
  253. fcreatedobjtypes.free;
  254. fcreatedobjtypes:=nil;
  255. fcreatedclassrefobjtypes.free;
  256. fcreatedclassrefobjtypes:=nil;
  257. fmaybecreatedbyclassrefdeftypes.free;
  258. fmaybecreatedbyclassrefdeftypes:=nil;
  259. inherited destroy;
  260. end;
  261. procedure tunitwpoinfobase.addcreatedobjtype(def: tdef);
  262. begin
  263. fcreatedobjtypes.add(def);
  264. end;
  265. procedure tunitwpoinfobase.addcreatedobjtypeforclassref(def: tdef);
  266. begin
  267. fcreatedclassrefobjtypes.add(def);
  268. end;
  269. procedure tunitwpoinfobase.addmaybecreatedbyclassref(def: tdef);
  270. begin
  271. fmaybecreatedbyclassrefdeftypes.add(def);
  272. end;
  273. { twpofilereader }
  274. function twpofilereader.getnextnoncommentline(out s: string):
  275. boolean;
  276. begin
  277. if (fusecurline) then
  278. begin
  279. s:=fcurline;
  280. fusecurline:=false;
  281. result:=true;
  282. exit;
  283. end;
  284. repeat
  285. readln(finputfile,s);
  286. if (s='') and
  287. eof(finputfile) then
  288. begin
  289. result:=false;
  290. exit;
  291. end;
  292. inc(flinenr);
  293. until (s='') or
  294. (s[1]<>'#');
  295. result:=true;
  296. end;
  297. constructor twpofilereader.create(const fn: tcmdstr; dest: twpoinfomanagerbase);
  298. begin
  299. if not FileExists(fn) then
  300. begin
  301. cgmessage1(wpo_cant_find_file,fn);
  302. exit;
  303. end;
  304. assign(finputfile,fn);
  305. ffilename:=fn;
  306. fdest:=dest;
  307. end;
  308. destructor twpofilereader.destroy;
  309. begin
  310. inherited destroy;
  311. end;
  312. procedure twpofilereader.processfile;
  313. var
  314. sectionhandler: twpocomponentbaseclass;
  315. i: longint;
  316. wpotype: twpotype;
  317. s,
  318. sectionname: string;
  319. begin
  320. cgmessage1(wpo_begin_processing,ffilename);
  321. reset(finputfile);
  322. flinenr:=0;
  323. while getnextnoncommentline(s) do
  324. begin
  325. if (s='') then
  326. continue;
  327. { format: "% sectionname" }
  328. if (s[1]<>'%') then
  329. begin
  330. cgmessage2(wpo_expected_section,tostr(flinenr),s);
  331. break;
  332. end;
  333. for i:=2 to length(s) do
  334. if (s[i]<>' ') then
  335. break;
  336. sectionname:=copy(s,i,255);
  337. { find handler for section and process }
  338. sectionhandler:=fdest.gethandlerforsection(sectionname);
  339. if assigned(sectionhandler) then
  340. begin
  341. wpotype:=sectionhandler.getwpotype;
  342. cgmessage2(wpo_found_section,sectionname,wpo2str[wpotype]);
  343. { do we need this information? }
  344. if ((sectionhandler.performswpoforswitches * init_settings.dowpoptimizerswitches) <> []) then
  345. begin
  346. { did some other section already generate this type of information? }
  347. if assigned(fdest.wpoinfouse[wpotype]) then
  348. begin
  349. cgmessage2(wpo_duplicate_wpotype,wpo2str[wpotype],sectionname);
  350. fdest.wpoinfouse[wpotype].free;
  351. end;
  352. { process the section }
  353. fdest.wpoinfouse[wpotype]:=sectionhandler.create;
  354. twpocomponentbase(fdest.wpoinfouse[wpotype]).loadfromwpofilesection(self);
  355. end
  356. else
  357. begin
  358. cgmessage1(wpo_skipping_unnecessary_section,sectionname);
  359. { skip the current section }
  360. while sectiongetnextline(s) do
  361. ;
  362. end;
  363. end
  364. else
  365. begin
  366. cgmessage1(wpo_no_section_handler,sectionname);
  367. { skip the current section }
  368. while sectiongetnextline(s) do
  369. ;
  370. end;
  371. end;
  372. close(finputfile);
  373. cgmessage1(wpo_end_processing,ffilename);
  374. end;
  375. function twpofilereader.sectiongetnextline(out s: string): boolean;
  376. begin
  377. result:=getnextnoncommentline(s);
  378. if not result then
  379. exit;
  380. { start of new section? }
  381. if (s<>'') and
  382. (s[1]='%') then
  383. begin
  384. { keep read line for next call to getnextnoncommentline() }
  385. fcurline:=s;
  386. fusecurline:=true;
  387. result:=false;
  388. end;
  389. end;
  390. { twpocomponentbase }
  391. constructor twpocomponentbase.create;
  392. begin
  393. { do nothing }
  394. end;
  395. class procedure twpocomponentbase.checkoptions;
  396. begin
  397. { do nothing }
  398. end;
  399. { twpofilewriter }
  400. constructor twpofilewriter.create(const fn: tcmdstr);
  401. begin
  402. assign(foutputfile,fn);
  403. ffilename:=fn;
  404. fsectioncontents:=tfpobjectlist.create(true);
  405. end;
  406. destructor twpofilewriter.destroy;
  407. begin
  408. fsectioncontents.free;
  409. inherited destroy;
  410. end;
  411. procedure twpofilewriter.writefile;
  412. var
  413. i: longint;
  414. begin
  415. {$i-}
  416. rewrite(foutputfile);
  417. {$i+}
  418. if (ioresult <> 0) then
  419. begin
  420. cgmessage1(wpo_cant_create_feedback_file,ffilename);
  421. exit;
  422. end;
  423. for i:=0 to fsectioncontents.count-1 do
  424. twpocomponentbase(fsectioncontents[i]).storewpofilesection(self);
  425. close(foutputfile);
  426. end;
  427. procedure twpofilewriter.startsection(const name: string);
  428. begin
  429. writeln(foutputfile,'% ',name);
  430. end;
  431. procedure twpofilewriter.sectionputline(const s: string);
  432. begin
  433. writeln(foutputfile,s);
  434. end;
  435. procedure twpofilewriter.registerwpocomponent(
  436. component: twpocomponentbase);
  437. begin
  438. fsectioncontents.add(component);
  439. end;
  440. { twpoinfomanagerbase }
  441. procedure twpoinfomanagerbase.registerwpocomponentclass(wpocomponent: twpocomponentbaseclass);
  442. begin
  443. fwpocomponents.add(wpocomponent.sectionname,wpocomponent);
  444. end;
  445. function twpoinfomanagerbase.gethandlerforsection(const secname: string
  446. ): twpocomponentbaseclass;
  447. begin
  448. result:=twpocomponentbaseclass(fwpocomponents.find(secname));
  449. end;
  450. procedure twpoinfomanagerbase.setwpoinputfile(const fn: tcmdstr);
  451. begin
  452. freader:=twpofilereader.create(fn,self);
  453. end;
  454. procedure twpoinfomanagerbase.setwpooutputfile(const fn: tcmdstr);
  455. begin
  456. fwriter:=twpofilewriter.create(fn);
  457. end;
  458. procedure twpoinfomanagerbase.parseandcheckwpoinfo;
  459. var
  460. i: longint;
  461. begin
  462. { error if we don't have to optimize yet have an input feedback file }
  463. if (init_settings.dowpoptimizerswitches=[]) and
  464. assigned(freader) then
  465. begin
  466. cgmessage(wpo_input_without_info_use);
  467. exit;
  468. end;
  469. { error if we have to optimize yet don't have an input feedback file }
  470. if (init_settings.dowpoptimizerswitches<>[]) and
  471. not assigned(freader) then
  472. begin
  473. cgmessage(wpo_no_input_specified);
  474. exit;
  475. end;
  476. { if we have to generate wpo information, check that a file has been
  477. specified and that we have something to write to it
  478. }
  479. if (init_settings.genwpoptimizerswitches<>[]) and
  480. not assigned(fwriter) then
  481. begin
  482. cgmessage(wpo_no_output_specified);
  483. exit;
  484. end;
  485. if (init_settings.genwpoptimizerswitches=[]) and
  486. assigned(fwriter) then
  487. begin
  488. cgmessage(wpo_output_without_info_gen);
  489. exit;
  490. end;
  491. { now read the input feedback file }
  492. if assigned(freader) then
  493. begin
  494. freader.processfile;
  495. freader.free;
  496. freader:=nil;
  497. end;
  498. { and for each specified optimization check whether the input feedback
  499. file contained the necessary information
  500. }
  501. if (([cs_wpo_devirtualize_calls,cs_wpo_optimize_vmts] * init_settings.dowpoptimizerswitches) <> []) and
  502. not assigned(wpoinfouse[wpo_devirtualization_context_insensitive]) then
  503. begin
  504. cgmessage1(wpo_not_enough_info,wpo2str[wpo_devirtualization_context_insensitive]);
  505. exit;
  506. end;
  507. if (cs_wpo_symbol_liveness in init_settings.dowpoptimizerswitches) and
  508. not assigned(wpoinfouse[wpo_live_symbol_information]) then
  509. begin
  510. cgmessage1(wpo_not_enough_info,wpo2str[wpo_live_symbol_information]);
  511. exit;
  512. end;
  513. { perform pre-checking to ensure there are no known incompatibilities between
  514. the selected optimizations and other switches
  515. }
  516. for i:=0 to fwpocomponents.count-1 do
  517. if (twpocomponentbaseclass(fwpocomponents[i]).generatesinfoforwposwitches*init_settings.genwpoptimizerswitches)<>[] then
  518. twpocomponentbaseclass(fwpocomponents[i]).checkoptions
  519. end;
  520. procedure twpoinfomanagerbase.extractwpoinfofromprogram;
  521. var
  522. i: longint;
  523. info: twpocomponentbase;
  524. begin
  525. { if don't have to write anything, fwriter has not been created }
  526. if not assigned(fwriter) then
  527. exit;
  528. { let all wpo components gather the necessary info from the compiler state }
  529. for i:=0 to fwpocomponents.count-1 do
  530. if (twpocomponentbaseclass(fwpocomponents[i]).generatesinfoforwposwitches*current_settings.genwpoptimizerswitches)<>[] then
  531. begin
  532. info:=twpocomponentbaseclass(fwpocomponents[i]).create;
  533. info.constructfromcompilerstate;
  534. fwriter.registerwpocomponent(info);
  535. end;
  536. { and write their info to disk }
  537. fwriter.writefile;
  538. fwriter.free;
  539. fwriter:=nil;
  540. end;
  541. constructor twpoinfomanagerbase.create;
  542. begin
  543. inherited create;
  544. fwpocomponents:=tfphashlist.create;
  545. end;
  546. destructor twpoinfomanagerbase.destroy;
  547. var
  548. i: twpotype;
  549. begin
  550. freader.free;
  551. freader:=nil;
  552. fwriter.free;
  553. fwriter:=nil;
  554. fwpocomponents.free;
  555. fwpocomponents:=nil;
  556. for i:=low(wpoinfouse) to high(wpoinfouse) do
  557. if assigned(wpoinfouse[i]) then
  558. wpoinfouse[i].free;
  559. inherited destroy;
  560. end;
  561. end.