2
Tail recursion optimization
4
Copyright (c) 2006 by Florian Klaempfl
6
This program is free software; you can redistribute it and/or modify
7
it under the terms of the GNU General Public License as published by
8
the Free Software Foundation; either version 2 of the License, or
9
(at your option) any later version.
11
This program is distributed in the hope that it will be useful,
12
but WITHOUT ANY WARRANTY; without even the implied warranty of
13
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
GNU General Public License for more details.
16
You should have received a copy of the GNU General Public License
17
along with this program; if not, write to the Free Software
18
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20
****************************************************************************
31
procedure do_opttail(var n : tnode;p : tprocdef);
39
nutils,nbas,nflw,ncal,nld,ncnv,
43
procedure do_opttail(var n : tnode;p : tprocdef);
46
labelnode : tlabelnode;
48
function find_and_replace_tailcalls(var n : tnode) : boolean;
51
usedcallnode : tcallnode;
53
function is_recursivecall(n : tnode) : boolean;
55
result:=(n.nodetype=calln) and (tcallnode(n).procdefinition=p);
57
usedcallnode:=tcallnode(n)
59
{ obsolete type cast? }
60
result:=((n.nodetype=typeconvn) and (ttypeconvnode(n).convtype=tc_equal) and is_recursivecall(ttypeconvnode(n).left));
63
function is_resultassignment(n : tnode) : boolean;
65
result:=((n.nodetype=loadn) and (tloadnode(n).symtableentry=p.funcretsym)) or
66
((n.nodetype=typeconvn) and (ttypeconvnode(n).convtype=tc_equal) and is_resultassignment(ttypeconvnode(n).left));
75
copystatements : tstatementnode;
76
paranode : tcallparanode;
77
tempnode : ttempcreatenode;
81
{ no tail call found and replaced so far }
90
while assigned(tstatementnode(hp).right) do
91
hp:=tstatementnode(hp).right;
92
result:=find_and_replace_tailcalls(tstatementnode(hp).left);
96
result:=find_and_replace_tailcalls(tifnode(n).right);
97
{ avoid short bool eval here }
98
result:=find_and_replace_tailcalls(tifnode(n).t1) or result;
102
if is_resultassignment(tbinarynode(n).left) and
103
is_recursivecall(tbinarynode(n).right) then
107
writeln('tail recursion optimization for ',p.mangledname);
110
{ create assignments for all parameters }
112
{ this is hairy to do because one parameter could be used to calculate another one, so
113
assign them first to temps and then add them }
115
calcnodes:=internalstatements(calcstatements);
116
copynodes:=internalstatements(copystatements);
117
paranode:=tcallparanode(usedcallnode.left);
118
while assigned(paranode) do
120
tempnode:=ctempcreatenode.create(paranode.left.resultdef,paranode.left.resultdef.size,tt_persistent,true);
121
addstatement(calcstatements,tempnode);
122
addstatement(calcstatements,
123
cassignmentnode.create(
124
ctemprefnode.create(tempnode),
128
{ "cast" away const varspezs }
129
loadnode:=cloadnode.create(paranode.parasym,paranode.parasym.owner);
130
include(loadnode.flags,nf_isinternal_ignoreconst);
132
addstatement(copystatements,
133
cassignmentnode.create(
135
ctemprefnode.create(tempnode)
137
addstatement(copystatements,ctempdeletenode.create_normal_temp(tempnode));
141
paranode:=tcallparanode(paranode.right);
145
n:=internalstatements(nodes);
147
if assigned(usedcallnode.methodpointerinit) then
149
addstatement(nodes,usedcallnode.methodpointerinit);
150
usedcallnode.methodpointerinit:=nil;
153
addstatement(nodes,calcnodes);
154
addstatement(nodes,copynodes);
157
addstatement(nodes,cgotonode.create(labelnode));
159
if assigned(usedcallnode.methodpointerdone) then
161
{ methodpointerdone should contain only temp. node clean up }
162
checktreenodetypes(usedcallnode.methodpointerdone,
163
[tempdeleten,blockn,statementn,temprefn,nothingn]);
164
addstatement(nodes,usedcallnode.methodpointerdone);
165
usedcallnode.methodpointerdone:=nil;
175
result:=find_and_replace_tailcalls(tblocknode(n).left);
184
{ check if the parameters actually would support tail recursion elimination }
185
for i:=0 to p.paras.count-1 do
186
with tparavarsym(p.paras[i]) do
187
if (varspez in [vs_out,vs_var]) or
188
((varspez=vs_const) and
189
(paramanager.push_addr_param(varspez,vardef,p.proccalloption)) or
190
{ parameters requiring tables are too complicated to handle
191
and slow down things anyways so a tail recursion call
194
vardef.needs_inittable) then
197
labelnode:=clabelnode.create(cnothingnode.create);
198
if find_and_replace_tailcalls(n) then
201
n:=internalstatements(s);
202
addstatement(s,labelnode);
203
addstatement(s,oldnodes);