opttail.pas 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. {
  2. Tail recursion optimization
  3. Copyright (c) 2006 by 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 opttail;
  18. {$i fpcdefs.inc}
  19. interface
  20. uses
  21. symdef,node;
  22. procedure do_opttail(var n : tnode;p : tprocdef);
  23. implementation
  24. uses
  25. globtype,
  26. symconst,symsym,
  27. defcmp,
  28. nutils,nbas,nflw,ncal,nld,ncnv,
  29. pass_1,
  30. paramgr;
  31. procedure do_opttail(var n : tnode;p : tprocdef);
  32. var
  33. labelnode : tlabelnode;
  34. function find_and_replace_tailcalls(var n : tnode) : boolean;
  35. var
  36. usedcallnode : tcallnode;
  37. function is_recursivecall(n : tnode) : boolean;
  38. begin
  39. result:=(n.nodetype=calln) and (tcallnode(n).procdefinition=p);
  40. if result then
  41. usedcallnode:=tcallnode(n)
  42. else
  43. { obsolete type cast? }
  44. result:=((n.nodetype=typeconvn) and (ttypeconvnode(n).convtype=tc_equal) and is_recursivecall(ttypeconvnode(n).left));
  45. end;
  46. function is_resultassignment(n : tnode) : boolean;
  47. begin
  48. result:=((n.nodetype=loadn) and (tloadnode(n).symtableentry=p.funcretsym)) or
  49. ((n.nodetype=typeconvn) and (ttypeconvnode(n).convtype=tc_equal) and is_resultassignment(ttypeconvnode(n).left));
  50. end;
  51. var
  52. calcnodes,
  53. copynodes,
  54. hp : tnode;
  55. nodes,
  56. calcstatements,
  57. copystatements : tstatementnode;
  58. paranode : tcallparanode;
  59. tempnode : ttempcreatenode;
  60. loadnode : tloadnode;
  61. oldnodetree : tnode;
  62. begin
  63. { no tail call found and replaced so far }
  64. result:=false;
  65. if n=nil then
  66. exit;
  67. case n.nodetype of
  68. statementn:
  69. begin
  70. hp:=n;
  71. { search last node }
  72. while assigned(tstatementnode(hp).right) do
  73. hp:=tstatementnode(hp).right;
  74. result:=find_and_replace_tailcalls(tstatementnode(hp).left);
  75. end;
  76. ifn:
  77. begin
  78. result:=find_and_replace_tailcalls(tifnode(n).right);
  79. { avoid short bool eval here }
  80. result:=find_and_replace_tailcalls(tifnode(n).t1) or result;
  81. end;
  82. assignn:
  83. begin
  84. if is_resultassignment(tbinarynode(n).left) and
  85. is_recursivecall(tbinarynode(n).right) then
  86. begin
  87. { found one! }
  88. {
  89. writeln('tail recursion optimization for ',p.mangledname);
  90. printnode(output,n);
  91. }
  92. { create assignments for all parameters }
  93. { this is hairy to do because one parameter could be used to calculate another one, so
  94. assign them first to temps and then add them }
  95. calcnodes:=internalstatements(calcstatements);
  96. copynodes:=internalstatements(copystatements);
  97. paranode:=tcallparanode(usedcallnode.left);
  98. while assigned(paranode) do
  99. begin
  100. tempnode:=ctempcreatenode.create(paranode.left.resultdef,paranode.left.resultdef.size,tt_persistent,true);
  101. addstatement(calcstatements,tempnode);
  102. addstatement(calcstatements,
  103. cassignmentnode.create(
  104. ctemprefnode.create(tempnode),
  105. paranode.left
  106. ));
  107. { "cast" away const varspezs }
  108. loadnode:=cloadnode.create(paranode.parasym,paranode.parasym.owner);
  109. include(loadnode.flags,nf_isinternal_ignoreconst);
  110. addstatement(copystatements,
  111. cassignmentnode.create(
  112. loadnode,
  113. ctemprefnode.create(tempnode)
  114. ));
  115. addstatement(copystatements,ctempdeletenode.create_normal_temp(tempnode));
  116. { reused }
  117. paranode.left:=nil;
  118. paranode:=tcallparanode(paranode.right);
  119. end;
  120. oldnodetree:=n;
  121. n:=internalstatements(nodes);
  122. if assigned(usedcallnode.methodpointerinit) then
  123. begin
  124. addstatement(nodes,usedcallnode.methodpointerinit);
  125. usedcallnode.methodpointerinit:=nil;
  126. end;
  127. addstatement(nodes,calcnodes);
  128. addstatement(nodes,copynodes);
  129. { create goto }
  130. addstatement(nodes,cgotonode.create(labelnode));
  131. if assigned(usedcallnode.methodpointerdone) then
  132. begin
  133. { methodpointerdone should contain only temp. node clean up }
  134. checktreenodetypes(usedcallnode.methodpointerdone,
  135. [tempdeleten,blockn,statementn,temprefn,nothingn]);
  136. addstatement(nodes,usedcallnode.methodpointerdone);
  137. usedcallnode.methodpointerdone:=nil;
  138. end;
  139. oldnodetree.free;
  140. do_firstpass(n);
  141. result:=true;
  142. end;
  143. end;
  144. blockn:
  145. result:=find_and_replace_tailcalls(tblocknode(n).left);
  146. end;
  147. end;
  148. var
  149. s : tstatementnode;
  150. oldnodes : tnode;
  151. i : longint;
  152. begin
  153. { check if the parameters actually would support tail recursion elimination }
  154. for i:=0 to p.paras.count-1 do
  155. with tparavarsym(p.paras[i]) do
  156. if (varspez in [vs_out,vs_var]) or
  157. ((varspez=vs_const) and
  158. (paramanager.push_addr_param(varspez,vardef,p.proccalloption)) or
  159. { parameters requiring tables are too complicated to handle
  160. and slow down things anyways so a tail recursion call
  161. makes no sense
  162. }
  163. vardef.needs_inittable) then
  164. exit;
  165. labelnode:=clabelnode.create(cnothingnode.create);
  166. if find_and_replace_tailcalls(n) then
  167. begin
  168. oldnodes:=n;
  169. n:=internalstatements(s);
  170. addstatement(s,labelnode);
  171. addstatement(s,oldnodes);
  172. end
  173. else
  174. labelnode.free;
  175. end;
  176. end.