fpc/compiler/opttail.pas
joost 07bf44517c * Merged XPCom branch into trunk, added support for constref and changed
the IInterface implementation to be XPCom-compatible
--- Merging r15997 through r16179 into '.':
U    rtl/inc/variants.pp
U    rtl/inc/objpash.inc
U    rtl/inc/objpas.inc
U    rtl/objpas/classes/persist.inc
U    rtl/objpas/classes/compon.inc
U    rtl/objpas/classes/classesh.inc
A    tests/test/tconstref1.pp
A    tests/test/tconstref2.pp
A    tests/test/tconstref3.pp
U    tests/test/tinterface4.pp
A    tests/test/tconstref4.pp
U    tests/webtbs/tw10897.pp
U    tests/webtbs/tw4086.pp
U    tests/webtbs/tw15363.pp
U    tests/webtbs/tw2177.pp
U    tests/webtbs/tw16592.pp
U    tests/tbs/tb0546.pp
U    compiler/sparc/cpupara.pas
U    compiler/i386/cpupara.pas
U    compiler/pdecsub.pas
U    compiler/symdef.pas
U    compiler/powerpc/cpupara.pas
U    compiler/avr/cpupara.pas
U    compiler/browcol.pas
U    compiler/defcmp.pas
U    compiler/powerpc64/cpupara.pas
U    compiler/ncgrtti.pas
U    compiler/x86_64/cpupara.pas
U    compiler/opttail.pas
U    compiler/htypechk.pas
U    compiler/tokens.pas
U    compiler/objcutil.pas
U    compiler/ncal.pas
U    compiler/symtable.pas
U    compiler/symsym.pas
U    compiler/m68k/cpupara.pas
U    compiler/regvars.pas
U    compiler/arm/cpupara.pas
U    compiler/symconst.pas
U    compiler/mips/cpupara.pas
U    compiler/paramgr.pas
U    compiler/psub.pas
U    compiler/pdecvar.pas
U    compiler/dbgstabs.pas
U    compiler/options.pas
U    packages/fcl-fpcunit/src/testutils.pp

git-svn-id: trunk@16180 -
2010-10-17 20:58:22 +00:00

213 lines
7.5 KiB
ObjectPascal

{
Tail recursion optimization
Copyright (c) 2006 by Florian Klaempfl
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
****************************************************************************
}
unit opttail;
{$i fpcdefs.inc}
interface
uses
symdef,node;
procedure do_opttail(var n : tnode;p : tprocdef);
implementation
uses
globtype,
symconst,symsym,
defcmp,defutil,
nutils,nbas,nflw,ncal,nld,ncnv,
pass_1,
paramgr;
procedure do_opttail(var n : tnode;p : tprocdef);
var
labelnode : tlabelnode;
function find_and_replace_tailcalls(var n : tnode) : boolean;
var
usedcallnode : tcallnode;
function is_recursivecall(n : tnode) : boolean;
begin
result:=(n.nodetype=calln) and (tcallnode(n).procdefinition=p) and not(assigned(tcallnode(n).methodpointer));
if result then
usedcallnode:=tcallnode(n)
else
{ obsolete type cast? }
result:=((n.nodetype=typeconvn) and (ttypeconvnode(n).convtype=tc_equal) and is_recursivecall(ttypeconvnode(n).left));
end;
function is_resultassignment(n : tnode) : boolean;
begin
result:=((n.nodetype=loadn) and (tloadnode(n).symtableentry=p.funcretsym)) or
((n.nodetype=typeconvn) and (ttypeconvnode(n).convtype=tc_equal) and is_resultassignment(ttypeconvnode(n).left));
end;
var
calcnodes,
copynodes,
hp : tnode;
nodes,
calcstatements,
copystatements : tstatementnode;
paranode : tcallparanode;
tempnode : ttempcreatenode;
loadnode : tloadnode;
oldnodetree : tnode;
begin
{ no tail call found and replaced so far }
result:=false;
if n=nil then
exit;
case n.nodetype of
statementn:
begin
hp:=n;
{ search last node }
while assigned(tstatementnode(hp).right) do
hp:=tstatementnode(hp).right;
result:=find_and_replace_tailcalls(tstatementnode(hp).left);
end;
ifn:
begin
result:=find_and_replace_tailcalls(tifnode(n).right);
{ avoid short bool eval here }
result:=find_and_replace_tailcalls(tifnode(n).t1) or result;
end;
assignn:
begin
if is_resultassignment(tbinarynode(n).left) and
is_recursivecall(tbinarynode(n).right) then
begin
{ found one! }
{
writeln('tail recursion optimization for ',p.mangledname);
printnode(output,n);
}
{ create assignments for all parameters }
{ this is hairy to do because one parameter could be used to calculate another one, so
assign them first to temps and then add them }
calcnodes:=internalstatements(calcstatements);
copynodes:=internalstatements(copystatements);
paranode:=tcallparanode(usedcallnode.left);
while assigned(paranode) do
begin
tempnode:=ctempcreatenode.create(paranode.left.resultdef,paranode.left.resultdef.size,tt_persistent,true);
addstatement(calcstatements,tempnode);
addstatement(calcstatements,
cassignmentnode.create(
ctemprefnode.create(tempnode),
paranode.left
));
{ "cast" away const varspezs }
loadnode:=cloadnode.create(paranode.parasym,paranode.parasym.owner);
include(loadnode.flags,nf_isinternal_ignoreconst);
addstatement(copystatements,
cassignmentnode.create(
loadnode,
ctemprefnode.create(tempnode)
));
addstatement(copystatements,ctempdeletenode.create_normal_temp(tempnode));
{ reused }
paranode.left:=nil;
paranode:=tcallparanode(paranode.right);
end;
oldnodetree:=n;
n:=internalstatements(nodes);
if assigned(usedcallnode.callinitblock) then
begin
addstatement(nodes,usedcallnode.callinitblock);
usedcallnode.callinitblock:=nil;
end;
addstatement(nodes,calcnodes);
addstatement(nodes,copynodes);
{ create goto }
addstatement(nodes,cgotonode.create(labelnode.labsym));
if assigned(usedcallnode.callcleanupblock) then
begin
{ callcleanupblock should contain only temp. node clean up }
checktreenodetypes(usedcallnode.callcleanupblock,
[tempdeleten,blockn,statementn,temprefn,nothingn]);
addstatement(nodes,usedcallnode.callcleanupblock);
usedcallnode.callcleanupblock:=nil;
end;
oldnodetree.free;
do_firstpass(n);
result:=true;
end;
end;
blockn:
result:=find_and_replace_tailcalls(tblocknode(n).left);
end;
end;
var
s : tstatementnode;
oldnodes : tnode;
i : longint;
labelsym : tlabelsym;
begin
{ check if the parameters actually would support tail recursion elimination }
for i:=0 to p.paras.count-1 do
with tparavarsym(p.paras[i]) do
if (varspez in [vs_out,vs_var,vs_constref]) or
((varspez=vs_const) and
(paramanager.push_addr_param(varspez,vardef,p.proccalloption)) or
{ parameters requiring tables are too complicated to handle
and slow down things anyways so a tail recursion call
makes no sense
}
is_managed_type(vardef)) then
exit;
labelsym:=tlabelsym.create('$opttail');
labelnode:=clabelnode.create(cnothingnode.create,labelsym);
if find_and_replace_tailcalls(n) then
begin
oldnodes:=n;
n:=internalstatements(s);
addstatement(s,labelnode);
addstatement(s,oldnodes);
end
else
labelnode.free;
end;
end.