optconstprop.pas 13 KB

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