2
0

optconstprop.pas 17 KB

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