optconstprop.pas 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384
  1. {
  2. Constant propagation across statements
  3. Copyright (c) 2005-2012 by Jeppe Johansen and Florian Klaempfl
  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 optconstprop;
  18. {$i fpcdefs.inc}
  19. { $define DEBUG_CONSTPROP}
  20. interface
  21. uses
  22. node;
  23. { does constant propagation for rootnode
  24. The approach is simple: It search for constant assignment statements. As soon as such
  25. a statement is found, the following statements are search if they contain
  26. a usage of the assigned variable. If this is a the case, the variable is
  27. replaced by the constant. This does not work across points where the
  28. program flow joins so e.g.
  29. if ... then
  30. ...
  31. a:=1;
  32. ...
  33. else
  34. ...
  35. a:=1;
  36. ...
  37. writeln(a);
  38. will not result in any constant propagation.
  39. }
  40. function do_optconstpropagate(var rootnode : tnode) : tnode;
  41. implementation
  42. uses
  43. pass_1,procinfo,compinnr,
  44. symsym, symconst,
  45. nutils, nbas, ncnv, nld, nflw, ncal, ninl;
  46. function check_written(var n: tnode; arg: pointer): foreachnoderesult;
  47. begin
  48. result:=fen_false;
  49. if n.isequal(tnode(arg)) and
  50. ((n.flags*[nf_write,nf_modify])<>[]) then
  51. begin
  52. result:=fen_norecurse_true;
  53. end;
  54. end;
  55. { propagates the constant assignment passed in arg into n }
  56. function replaceBasicAssign(var n: tnode; arg: tnode; var tree_modified: boolean): boolean;
  57. var
  58. st2, oldnode: tnode;
  59. old: pnode;
  60. changed, tree_modified2, tree_modified3: boolean;
  61. written, tree_modified4, tree_modified5: Boolean;
  62. begin
  63. result:=true;
  64. if n = nil then
  65. exit;
  66. tree_modified:=false;
  67. tree_modified2:=false;
  68. tree_modified3:=false;
  69. tree_modified4:=false;
  70. tree_modified5:=false;
  71. { while it might be usefull, to use foreach to iterate all nodes, it is safer to
  72. iterate manually here so we have full controll how all nodes are processed }
  73. { We cannot analyze beyond those nodes, so we terminate to be on the safe side }
  74. if (n.nodetype in [addrn,derefn,asmn,casen,whilerepeatn,labeln,continuen,breakn,
  75. tryexceptn,raisen,tryfinallyn,onn,loadparentfpn,loadvmtaddrn,guidconstn,rttin,addoptn,asn,goton,
  76. objcselectorn,objcprotocoln,
  77. arrayconstructorn,arrayconstructorrangen]) then
  78. exit(false)
  79. else if n.nodetype=assignn then
  80. begin
  81. tree_modified:=false;
  82. { we can propage the constant in both branches because the evaluation order is not defined }
  83. result:=replaceBasicAssign(tassignmentnode(n).right, arg, tree_modified);
  84. { do not use the intuitive way result:=result and replace... because this would prevent
  85. replaceBasicAssign being called if the result is already false }
  86. result:=replaceBasicAssign(tassignmentnode(n).left, arg, tree_modified2) and result;
  87. tree_modified:=tree_modified or tree_modified2;
  88. { but we have to check if left writes to the currently searched variable ... }
  89. written:=foreachnodestatic(pm_postprocess, tassignmentnode(n).left, @check_written, tassignmentnode(arg).left);
  90. { ... if this is the case, we have to stop searching }
  91. result:=result and not(written);
  92. end
  93. else if n.isequal(tassignmentnode(arg).left) and ((n.flags*[nf_write,nf_modify])=[]) then
  94. begin
  95. n.Free;
  96. n:=tassignmentnode(arg).right.getcopy;
  97. inserttypeconv_internal(n, tassignmentnode(arg).left.resultdef);
  98. tree_modified:=true;
  99. end
  100. else if n.nodetype=statementn then
  101. result:=replaceBasicAssign(tstatementnode(n).left, arg, tree_modified)
  102. else if n.nodetype=forn then
  103. begin
  104. result:=replaceBasicAssign(tfornode(n).right, arg, tree_modified);
  105. if result then
  106. replaceBasicAssign(tfornode(n).t1, arg, tree_modified2);
  107. tree_modified:=tree_modified or tree_modified2;
  108. { after a for node we cannot continue with our simple approach }
  109. result:=false;
  110. end
  111. else if n.nodetype=blockn then
  112. begin
  113. changed:=false;
  114. st2:=tstatementnode(tblocknode(n).statements);
  115. old:=@tblocknode(n).statements;
  116. while assigned(st2) do
  117. begin
  118. repeat
  119. oldnode:=st2;
  120. tree_modified2:=false;
  121. if not replaceBasicAssign(st2, arg, tree_modified2) then
  122. begin
  123. old^:=st2;
  124. oldnode:=nil;
  125. changed:=changed or tree_modified2;
  126. result:=false;
  127. break;
  128. end
  129. else
  130. old^:=st2;
  131. changed:=changed or tree_modified2;
  132. until oldnode=st2;
  133. if oldnode = nil then
  134. break;
  135. old:=@tstatementnode(st2).next;
  136. st2:=tstatementnode(st2).next;
  137. end;
  138. tree_modified:=changed;
  139. end
  140. else if n.nodetype=ifn then
  141. begin
  142. result:=replaceBasicAssign(tifnode(n).left, arg, tree_modified);
  143. if result then
  144. begin
  145. if assigned(tifnode(n).t1) then
  146. begin
  147. { we can propagate the constant in both branches of an if statement
  148. because even if the the branch writes to it, the else branch gets the
  149. unmodified value }
  150. result:=replaceBasicAssign(tifnode(n).right, arg, tree_modified2);
  151. { do not use the intuitive way result:=result and replace... because this would prevent
  152. replaceBasicAssign being called if the result is already false }
  153. result:=replaceBasicAssign(tifnode(n).t1, arg, tree_modified3) and result;
  154. tree_modified:=tree_modified or tree_modified2 or tree_modified3;
  155. end
  156. else
  157. begin
  158. result:=replaceBasicAssign(tifnode(n).right, arg, tree_modified2);
  159. tree_modified:=tree_modified or tree_modified2;
  160. end;
  161. end;
  162. end
  163. else if n.nodetype=inlinen then
  164. begin
  165. { constant inc'ed/dec'ed? }
  166. if (tinlinenode(n).inlinenumber=in_dec_x) or (tinlinenode(n).inlinenumber=in_inc_x) then
  167. begin
  168. if tnode(tassignmentnode(arg).left).isequal(tcallparanode(tinlinenode(n).left).left) and
  169. (not(assigned(tcallparanode(tinlinenode(n).left).right)) or
  170. (tcallparanode(tcallparanode(tinlinenode(n).left).right).left.nodetype=ordconstn)) then
  171. begin
  172. { if the node just being searched is inc'ed/dec'ed then replace the inc/dec
  173. by add/sub and force a second replacement pass }
  174. oldnode:=n;
  175. n:=tinlinenode(n).getaddsub_for_incdec;
  176. oldnode.free;
  177. tree_modified:=true;
  178. { do not continue, value changed, if further const. propagations are possible, this is done
  179. by the next pass }
  180. result:=false;
  181. exit;
  182. end;
  183. { inc/dec might have a side effect, so stop here for now }
  184. result:=false;
  185. exit;
  186. end
  187. else if might_have_sideeffects(n) then
  188. exit(false);
  189. result:=replaceBasicAssign(tunarynode(n).left, arg, tree_modified);
  190. end
  191. else if n.nodetype=calln then
  192. begin
  193. { only propagate simply variables which are regable, this means that their address is not
  194. taken }
  195. if (tassignmentnode(arg).left.nodetype=loadn) and
  196. (tabstractvarsym(tloadnode(tassignmentnode(arg).left).symtableentry).varregable in [vr_fpureg,vr_mmreg,vr_intreg]) then
  197. begin
  198. result:=replaceBasicAssign(tnode(tcallnode(n).callinitblock), arg, tree_modified);
  199. result:=result and replaceBasicAssign(tcallnode(n).left, arg, tree_modified2);
  200. result:=result and replaceBasicAssign(tcallnode(n).vmt_entry, arg, tree_modified3);
  201. result:=result and replaceBasicAssign(tcallnode(n).right, arg, tree_modified4);
  202. result:=result and replaceBasicAssign(tnode(tcallnode(n).callcleanupblock), arg, tree_modified5);
  203. tree_modified:=tree_modified or tree_modified2 or tree_modified3 or tree_modified4 or tree_modified5;
  204. end
  205. else
  206. result:=false;
  207. exit;
  208. end
  209. else if n.InheritsFrom(tbinarynode) then
  210. begin
  211. result:=replaceBasicAssign(tbinarynode(n).left, arg, tree_modified);
  212. if result then
  213. result:=replaceBasicAssign(tbinarynode(n).right, arg, tree_modified2);
  214. tree_modified:=tree_modified or tree_modified2;
  215. end
  216. else if n.InheritsFrom(tunarynode) then
  217. begin
  218. result:=replaceBasicAssign(tunarynode(n).left, arg, tree_modified);
  219. end;
  220. if n.nodetype<>callparan then
  221. begin
  222. if tree_modified then
  223. exclude(n.flags,nf_pass1_done);
  224. do_firstpass(n);
  225. end;
  226. end;
  227. function propagate(var n: tnode; arg: pointer): foreachnoderesult;
  228. var
  229. l,
  230. st, st2, oldnode: tnode;
  231. old: pnode;
  232. a: tassignmentnode;
  233. tree_mod, changed: boolean;
  234. begin
  235. result:=fen_true;
  236. changed:=false;
  237. PBoolean(arg)^:=false;
  238. if not assigned(n) then
  239. exit;
  240. if n.nodetype in [calln] then
  241. exit(fen_norecurse_true);
  242. if n.nodetype=blockn then
  243. begin
  244. st:=tblocknode(n).statements;
  245. while assigned(st) and
  246. (st.nodetype=statementn) and
  247. assigned(tstatementnode(st).statement) do
  248. begin
  249. if tstatementnode(st).statement.nodetype=assignn then
  250. begin
  251. a:=tassignmentnode(tstatementnode(st).statement);
  252. l:=a.left;
  253. if ((((l.nodetype=loadn) and
  254. { its address cannot have escaped the current routine }
  255. not(tabstractvarsym(tloadnode(l).symtableentry).addr_taken)) and
  256. ((
  257. (tloadnode(l).symtableentry.typ=localvarsym) and
  258. (tloadnode(l).symtable=current_procinfo.procdef.localst)
  259. ) or
  260. ((tloadnode(l).symtableentry.typ=paravarsym) and
  261. (tloadnode(l).symtable=current_procinfo.procdef.parast)
  262. ) or
  263. ((tloadnode(l).symtableentry.typ=staticvarsym) and
  264. (tloadnode(l).symtable.symtabletype=staticsymtable)
  265. )
  266. )) or
  267. (l.nodetype = temprefn)) and
  268. (is_constintnode(a.right) or
  269. is_constboolnode(a.right) or
  270. is_constcharnode(a.right) or
  271. is_constenumnode(a.right) or
  272. is_conststringnode(a.right)) then
  273. begin
  274. st2:=tstatementnode(tstatementnode(st).right);
  275. old:=@tstatementnode(st).right;
  276. while assigned(st2) do
  277. begin
  278. repeat
  279. oldnode:=st2;
  280. { Simple assignment of constant found }
  281. tree_mod:=false;
  282. if not replaceBasicAssign(st2, a, tree_mod) then
  283. begin
  284. old^:=st2;
  285. oldnode:=nil;
  286. changed:=changed or tree_mod;
  287. break;
  288. end
  289. else
  290. old^:=st2;
  291. changed:=changed or tree_mod;
  292. until oldnode=st2;
  293. if oldnode = nil then
  294. break;
  295. old:=@tstatementnode(st2).next;
  296. st2:=tstatementnode(st2).next;
  297. end;
  298. end;
  299. end;
  300. st:=tstatementnode(st).next;
  301. end;
  302. end;
  303. PBoolean(arg)^:=changed;
  304. end;
  305. function do_optconstpropagate(var rootnode: tnode): tnode;
  306. var
  307. changed: boolean;
  308. runsimplify : Boolean;
  309. begin
  310. {$ifdef DEBUG_CONSTPROP}
  311. writeln('************************ before constant propagation ***************************');
  312. printnode(rootnode);
  313. {$endif DEBUG_CONSTPROP}
  314. runsimplify:=false;
  315. repeat
  316. changed:=false;
  317. foreachnodestatic(pm_postandagain, rootnode, @propagate, @changed);
  318. runsimplify:=runsimplify or changed;
  319. until changed=false;
  320. if runsimplify then
  321. doinlinesimplify(rootnode);
  322. {$ifdef DEBUG_CONSTPROP}
  323. writeln('************************ after constant propagation ***************************');
  324. printnode(rootnode);
  325. writeln('*******************************************************************************');
  326. {$endif DEBUG_CONSTPROP}
  327. result:=rootnode;
  328. end;
  329. end.