123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363 |
- {
- Constant propagation across statements
- Copyright (c) 2005-2012 by Jeppe Johansen and 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 optconstprop;
- {$i fpcdefs.inc}
- { $define DEBUG_CONSTPROP}
- interface
- uses
- node;
- { does constant propagation for rootnode
- The approach is simple: It search for constant assignment statements. As soon as such
- a statement is found, the following statements are search if they contain
- a usage of the assigned variable. If this is a the case, the variable is
- replaced by the constant. This does not work across points where the
- program flow joins so e.g.
- if ... then
- ...
- a:=1;
- ...
- else
- ...
- a:=1;
- ...
- writeln(a);
- will not result in any constant propagation.
- }
- function do_optconstpropagate(var rootnode : tnode) : tnode;
- implementation
- uses
- pass_1,procinfo,compinnr,
- symsym, symconst,
- nutils, nbas, ncnv, nld, nflw, ncal, ninl;
- function check_written(var n: tnode; arg: pointer): foreachnoderesult;
- begin
- result:=fen_false;
- if n.isequal(tnode(arg)) and
- ((n.flags*[nf_write,nf_modify])<>[]) then
- begin
- result:=fen_norecurse_true;
- end;
- end;
- { propagates the constant assignment passed in arg into n }
- function replaceBasicAssign(var n: tnode; arg: tnode; var tree_modified: boolean): boolean;
- var
- st2, oldnode: tnode;
- old: pnode;
- changed, tree_modified2,tree_modified3: boolean;
- written : Boolean;
- begin
- result:=true;
- if n = nil then
- exit;
- tree_modified:=false;
- tree_modified2:=false;
- tree_modified3:=false;
- { while it might be usefull, to use foreach to iterate all nodes, it is safer to
- iterate manually here so we have full controll how all nodes are processed }
- { We cannot analyze beyond those nodes, so we terminate to be on the safe side }
- if (n.nodetype in [addrn,derefn,asmn,casen,whilerepeatn,labeln,continuen,breakn,
- tryexceptn,raisen,tryfinallyn,onn,loadparentfpn,loadvmtaddrn,guidconstn,rttin,addoptn,asn,goton,
- objcselectorn,objcprotocoln]) then
- exit(false)
- else if n.nodetype=assignn then
- begin
- tree_modified:=false;
- { we can propage the constant in both branches because the evaluation order is not defined }
- result:=replaceBasicAssign(tassignmentnode(n).right, arg, tree_modified);
- { do not use the intuitive way result:=result and replace... because this would prevent
- replaceBasicAssign being called if the result is already false }
- result:=replaceBasicAssign(tassignmentnode(n).left, arg, tree_modified2) and result;
- tree_modified:=tree_modified or tree_modified2;
- { but we have to check if left writes to the currently searched variable ... }
- written:=foreachnodestatic(pm_postprocess, tassignmentnode(n).left, @check_written, tassignmentnode(arg).left);
- { ... if this is the case, we have to stop searching }
- result:=result and not(written);
- end
- else if n.isequal(tassignmentnode(arg).left) and ((n.flags*[nf_write,nf_modify])=[]) then
- begin
- n.Free;
- n:=tassignmentnode(arg).right.getcopy;
- inserttypeconv_internal(n, tassignmentnode(arg).left.resultdef);
- tree_modified:=true;
- end
- else if n.nodetype=statementn then
- result:=replaceBasicAssign(tstatementnode(n).left, arg, tree_modified)
- else if n.nodetype=forn then
- begin
- result:=replaceBasicAssign(tfornode(n).right, arg, tree_modified);
- if result then
- replaceBasicAssign(tfornode(n).t1, arg, tree_modified2);
- tree_modified:=tree_modified or tree_modified2;
- { after a for node we cannot continue with our simple approach }
- result:=false;
- end
- else if n.nodetype=blockn then
- begin
- changed:=false;
- st2:=tstatementnode(tblocknode(n).statements);
- old:=@tblocknode(n).statements;
- while assigned(st2) do
- begin
- repeat
- oldnode:=st2;
- tree_modified2:=false;
- if not replaceBasicAssign(st2, arg, tree_modified2) then
- begin
- old^:=st2;
- oldnode:=nil;
- changed:=changed or tree_modified2;
- result:=false;
- break;
- end
- else
- old^:=st2;
- changed:=changed or tree_modified2;
- until oldnode=st2;
- if oldnode = nil then
- break;
- old:=@tstatementnode(st2).next;
- st2:=tstatementnode(st2).next;
- end;
- tree_modified:=changed;
- end
- else if n.nodetype=ifn then
- begin
- result:=replaceBasicAssign(tifnode(n).left, arg, tree_modified);
- if result then
- begin
- if assigned(tifnode(n).t1) then
- begin
- { we can propagate the constant in both branches of an if statement
- because even if the the branch writes to it, the else branch gets the
- unmodified value }
- result:=replaceBasicAssign(tifnode(n).right, arg, tree_modified2);
- { do not use the intuitive way result:=result and replace... because this would prevent
- replaceBasicAssign being called if the result is already false }
- result:=replaceBasicAssign(tifnode(n).t1, arg, tree_modified3) and result;
- tree_modified:=tree_modified or tree_modified2 or tree_modified3;
- end
- else
- begin
- result:=replaceBasicAssign(tifnode(n).right, arg, tree_modified2);
- tree_modified:=tree_modified or tree_modified2;
- end;
- end;
- end
- else if n.nodetype=inlinen then
- begin
- { constant inc'ed/dec'ed? }
- if (tinlinenode(n).inlinenumber=in_dec_x) or (tinlinenode(n).inlinenumber=in_inc_x) then
- begin
- if tnode(tassignmentnode(arg).left).isequal(tcallparanode(tinlinenode(n).left).left) and
- (not(assigned(tcallparanode(tinlinenode(n).left).right)) or
- (tcallparanode(tcallparanode(tinlinenode(n).left).right).left.nodetype=ordconstn)) then
- begin
- { if the node just being searched is inc'ed/dec'ed then replace the inc/dec
- by add/sub and force a second replacement pass }
- oldnode:=n;
- n:=tinlinenode(n).getaddsub_for_incdec;
- oldnode.free;
- tree_modified:=true;
- { do not continue, value changed, if further const. propagations are possible, this is done
- by the next pass }
- result:=false;
- exit;
- end;
- end
- else if might_have_sideeffects(n) then
- exit(false);
- replaceBasicAssign(tunarynode(n).left, arg, tree_modified);
- result:=false;
- end
- else if n.nodetype=calln then
- exit(false)
- else if n.InheritsFrom(tbinarynode) then
- begin
- result:=replaceBasicAssign(tbinarynode(n).left, arg, tree_modified);
- if result then
- result:=replaceBasicAssign(tbinarynode(n).right, arg, tree_modified2);
- tree_modified:=tree_modified or tree_modified2;
- end
- else if n.InheritsFrom(tunarynode) then
- begin
- result:=replaceBasicAssign(tunarynode(n).left, arg, tree_modified);
- end;
- if n.nodetype<>callparan then
- begin
- if tree_modified then
- exclude(n.flags,nf_pass1_done);
- do_firstpass(n);
- end;
- end;
- function propagate(var n: tnode; arg: pointer): foreachnoderesult;
- var
- l,
- st, st2, oldnode: tnode;
- old: pnode;
- a: tassignmentnode;
- tree_mod, changed: boolean;
- begin
- result:=fen_true;
- changed:=false;
- PBoolean(arg)^:=false;
- if not assigned(n) then
- exit;
- if n.nodetype in [calln] then
- exit(fen_norecurse_true);
- if n.nodetype=blockn then
- begin
- st:=tblocknode(n).statements;
- while assigned(st) and
- (st.nodetype=statementn) and
- assigned(tstatementnode(st).statement) do
- begin
- if tstatementnode(st).statement.nodetype=assignn then
- begin
- a:=tassignmentnode(tstatementnode(st).statement);
- l:=a.left;
- if ((((l.nodetype=loadn) and
- { its address cannot have escaped the current routine }
- not(tabstractvarsym(tloadnode(l).symtableentry).addr_taken)) and
- ((
- (tloadnode(l).symtableentry.typ=localvarsym) and
- (tloadnode(l).symtable=current_procinfo.procdef.localst)
- ) or
- ((tloadnode(l).symtableentry.typ=paravarsym) and
- (tloadnode(l).symtable=current_procinfo.procdef.parast)
- ) or
- ((tloadnode(l).symtableentry.typ=staticvarsym) and
- (tloadnode(l).symtable.symtabletype=staticsymtable)
- )
- )) or
- (l.nodetype = temprefn)) and
- (is_constintnode(a.right) or
- is_constboolnode(a.right) or
- is_constcharnode(a.right) or
- is_constenumnode(a.right) or
- is_conststringnode(a.right)) then
- begin
- st2:=tstatementnode(tstatementnode(st).right);
- old:=@tstatementnode(st).right;
- while assigned(st2) do
- begin
- repeat
- oldnode:=st2;
- { Simple assignment of constant found }
- tree_mod:=false;
- if not replaceBasicAssign(st2, a, tree_mod) then
- begin
- old^:=st2;
- oldnode:=nil;
- changed:=changed or tree_mod;
- break;
- end
- else
- old^:=st2;
- changed:=changed or tree_mod;
- until oldnode=st2;
- if oldnode = nil then
- break;
- old:=@tstatementnode(st2).next;
- st2:=tstatementnode(st2).next;
- end;
- end;
- end;
- st:=tstatementnode(st).next;
- end;
- end;
- PBoolean(arg)^:=changed;
- end;
- function do_optconstpropagate(var rootnode: tnode): tnode;
- var
- changed: boolean;
- runsimplify : Boolean;
- begin
- {$ifdef DEBUG_CONSTPROP}
- writeln('************************ before constant propagation ***************************');
- printnode(rootnode);
- {$endif DEBUG_CONSTPROP}
- runsimplify:=false;
- repeat
- changed:=false;
- foreachnodestatic(pm_postandagain, rootnode, @propagate, @changed);
- runsimplify:=runsimplify or changed;
- until changed=false;
- if runsimplify then
- doinlinesimplify(rootnode);
- {$ifdef DEBUG_CONSTPROP}
- writeln('************************ after constant propagation ***************************');
- printnode(rootnode);
- writeln('*******************************************************************************');
- {$endif DEBUG_CONSTPROP}
- result:=rootnode;
- end;
- end.
|