mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-04-05 22:08:11 +02:00
503 lines
15 KiB
ObjectPascal
503 lines
15 KiB
ObjectPascal
{
|
|
Helper routines for the optimizer
|
|
|
|
Copyright (c) 2007 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 optutils;
|
|
|
|
{$i fpcdefs.inc}
|
|
|
|
interface
|
|
|
|
uses
|
|
cclasses,
|
|
node,
|
|
globtype;
|
|
|
|
type
|
|
{ this implementation should be really improved,
|
|
its purpose is to find equal nodes }
|
|
TIndexedNodeSet = class(TFPList)
|
|
function Add(node : tnode) : boolean;
|
|
function Includes(node : tnode) : tnode;
|
|
function Remove(node : tnode) : boolean;
|
|
end;
|
|
|
|
procedure SetNodeSucessors(p,last : tnode);
|
|
procedure PrintDFAInfo(var f : text;p : tnode);
|
|
procedure PrintIndexedNodeSet(var f : text;s : TIndexedNodeSet);
|
|
|
|
{ determines the optinfo.defsum field for the given node
|
|
this field contains a sum of all expressions defined by
|
|
all child expressions reachable through p
|
|
}
|
|
procedure CalcDefSum(p : tnode);
|
|
|
|
{ calculates/estimates the field execution weight of optinfo }
|
|
procedure CalcExecutionWeights(p : tnode;Initial : longint = 100);
|
|
|
|
{ determines the optinfo.defsum field for the given node
|
|
this field contains a sum of all expressions defined by
|
|
all child expressions reachable through p
|
|
}
|
|
procedure CalcUseSum(p : tnode);
|
|
|
|
{ returns true, if n is a valid node and has life info }
|
|
function has_life_info(n : tnode) : boolean;
|
|
|
|
implementation
|
|
|
|
uses
|
|
cutils,
|
|
verbose,
|
|
optbase,
|
|
ncal,nbas,nflw,nutils,nset,ncon;
|
|
|
|
function TIndexedNodeSet.Add(node : tnode) : boolean;
|
|
var
|
|
i : Integer;
|
|
p : tnode;
|
|
begin
|
|
node.allocoptinfo;
|
|
p:=Includes(node);
|
|
if assigned(p) then
|
|
begin
|
|
result:=false;
|
|
node.optinfo^.index:=p.optinfo^.index;
|
|
end
|
|
else
|
|
begin
|
|
i:=inherited Add(node);
|
|
node.optinfo^.index:=i;
|
|
result:=true;
|
|
end
|
|
end;
|
|
|
|
|
|
function TIndexedNodeSet.Includes(node : tnode) : tnode;
|
|
var
|
|
i : longint;
|
|
begin
|
|
for i:=0 to Count-1 do
|
|
if tnode(List[i]).isequal(node) then
|
|
begin
|
|
result:=tnode(List[i]);
|
|
exit;
|
|
end;
|
|
result:=nil;
|
|
end;
|
|
|
|
|
|
function TIndexedNodeSet.Remove(node : tnode) : boolean;
|
|
var
|
|
p : tnode;
|
|
begin
|
|
result:=false;
|
|
p:=Includes(node);
|
|
if assigned(p) then
|
|
begin
|
|
if inherited Remove(p)<>-1 then
|
|
result:=true;
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure PrintIndexedNodeSet(var f : text;s : TIndexedNodeSet);
|
|
var
|
|
i : integer;
|
|
begin
|
|
for i:=0 to s.count-1 do
|
|
begin
|
|
writeln(f,'=============================== Node ',i,' ===============================');
|
|
printnode(f,tnode(s[i]));
|
|
writeln(f);
|
|
end;
|
|
end;
|
|
|
|
|
|
function PrintNodeDFA(var n: tnode; arg: pointer): foreachnoderesult;
|
|
begin
|
|
if assigned(n.optinfo) and ((n.optinfo^.life<>nil) or (n.optinfo^.use<>nil) or (n.optinfo^.def<>nil)) then
|
|
begin
|
|
write(text(arg^),nodetype2str[n.nodetype],'(',n.fileinfo.line,',',n.fileinfo.column,') Life: ');
|
|
PrintDFASet(text(arg^),n.optinfo^.life);
|
|
write(text(arg^),' Def: ');
|
|
PrintDFASet(text(arg^),n.optinfo^.def);
|
|
write(text(arg^),' Use: ');
|
|
PrintDFASet(text(arg^),n.optinfo^.use);
|
|
if assigned(n.successor) then
|
|
write(text(arg^),' Successor: ',nodetype2str[n.successor.nodetype],'(',n.successor.fileinfo.line,',',n.successor.fileinfo.column,')')
|
|
else
|
|
write(text(arg^),' Successor: nil');
|
|
write(text(arg^),' DefSum: ');
|
|
PrintDFASet(text(arg^),n.optinfo^.defsum);
|
|
writeln(text(arg^));
|
|
end;
|
|
result:=fen_false;
|
|
end;
|
|
|
|
|
|
procedure PrintDFAInfo(var f : text;p : tnode);
|
|
begin
|
|
foreachnodestatic(pm_postprocess,p,@PrintNodeDFA,@f);
|
|
end;
|
|
|
|
|
|
type
|
|
PBreakContinueStackNode = ^TBreakContinueStackNode;
|
|
TBreakContinueStackNode = record
|
|
{ successor node for a break statement in the current loop }
|
|
brk,
|
|
{ successor node for a continue statement in the current loop }
|
|
cont : tnode;
|
|
next : PBreakContinueStackNode;
|
|
end;
|
|
|
|
{ implements a stack to track successor nodes for break and continue
|
|
statements }
|
|
TBreakContinueStack = object
|
|
top: PBreakContinueStackNode;
|
|
constructor Init;
|
|
destructor Done;
|
|
procedure Push(brk,cont : tnode);
|
|
procedure Pop;
|
|
end;
|
|
|
|
const
|
|
NullBreakContinueStackNode : TBreakContinueStackNode = (brk: nil; cont: nil; next: nil);
|
|
|
|
|
|
constructor TBreakContinueStack.Init;
|
|
begin
|
|
top:=@NullBreakContinueStackNode;
|
|
end;
|
|
|
|
|
|
destructor TBreakContinueStack.Done;
|
|
begin
|
|
while top<>@NullBreakContinueStackNode do
|
|
Pop;
|
|
end;
|
|
|
|
|
|
procedure TBreakContinueStack.Push(brk,cont : tnode);
|
|
var
|
|
n : PBreakContinueStackNode;
|
|
begin
|
|
new(n);
|
|
n^.brk:=brk;
|
|
n^.cont:=cont;
|
|
n^.next:=top;
|
|
top:=n;
|
|
end;
|
|
|
|
|
|
procedure TBreakContinueStack.Pop;
|
|
var
|
|
n : PBreakContinueStackNode;
|
|
begin
|
|
n:=top;
|
|
top:=n^.next;
|
|
Dispose(n);
|
|
end;
|
|
|
|
|
|
procedure SetNodeSucessors(p,last : tnode);
|
|
var
|
|
BreakContinueStack : TBreakContinueStack;
|
|
Exitsuccessor: TNode;
|
|
{ sets the successor nodes of a node tree block
|
|
returns the first node of the tree if it's a controll flow node }
|
|
function DoSet(p : tnode;succ : tnode) : tnode;
|
|
var
|
|
hp1,hp2, oldexitsuccessor: tnode;
|
|
i : longint;
|
|
begin
|
|
result:=nil;
|
|
if p=nil then
|
|
exit;
|
|
case p.nodetype of
|
|
statementn:
|
|
begin
|
|
hp1:=p;
|
|
result:=p;
|
|
while assigned(hp1) do
|
|
begin
|
|
{ does another statement follow? }
|
|
if assigned(tstatementnode(hp1).next) then
|
|
begin
|
|
hp2:=DoSet(tstatementnode(hp1).statement,tstatementnode(hp1).next);
|
|
if assigned(hp2) then
|
|
tstatementnode(hp1).successor:=hp2
|
|
else
|
|
tstatementnode(hp1).successor:=tstatementnode(hp1).next;
|
|
end
|
|
else
|
|
begin
|
|
hp2:=DoSet(tstatementnode(hp1).statement,succ);
|
|
if assigned(hp2) then
|
|
tstatementnode(hp1).successor:=hp2
|
|
else
|
|
tstatementnode(hp1).successor:=succ;
|
|
end;
|
|
hp1:=tstatementnode(hp1).next;
|
|
end;
|
|
end;
|
|
blockn:
|
|
begin
|
|
result:=p;
|
|
oldexitsuccessor:=Exitsuccessor;
|
|
if nf_block_with_exit in p.flags then
|
|
Exitsuccessor:=succ;
|
|
DoSet(tblocknode(p).statements,succ);
|
|
if assigned(tblocknode(p).statements) then
|
|
p.successor:=tblocknode(p).statements
|
|
else
|
|
p.successor:=succ;
|
|
Exitsuccessor:=oldexitsuccessor;
|
|
end;
|
|
forn:
|
|
begin
|
|
BreakContinueStack.Push(succ,p);
|
|
result:=p;
|
|
{ the successor of the last node of the for body is the dummy loop iteration node
|
|
it allows the dfa to inject needed life information into the loop }
|
|
tfornode(p).loopiteration:=cnothingnode.create;
|
|
|
|
DoSet(tfornode(p).t2,tfornode(p).loopiteration);
|
|
p.successor:=succ;
|
|
BreakContinueStack.Pop;
|
|
end;
|
|
breakn:
|
|
begin
|
|
result:=p;
|
|
p.successor:=BreakContinueStack.top^.brk;
|
|
end;
|
|
continuen:
|
|
begin
|
|
result:=p;
|
|
p.successor:=BreakContinueStack.top^.cont;
|
|
end;
|
|
whilerepeatn:
|
|
begin
|
|
BreakContinueStack.Push(succ,p);
|
|
result:=p;
|
|
{ the successor of the last node of the while/repeat body is the while node itself }
|
|
DoSet(twhilerepeatnode(p).right,p);
|
|
|
|
p.successor:=succ;
|
|
|
|
{ special case: we do not do a dyn. dfa, but we should handle endless loops }
|
|
if is_constboolnode(twhilerepeatnode(p).left) then
|
|
begin
|
|
if ((lnf_testatbegin in twhilerepeatnode(p).loopflags) and
|
|
getbooleanvalue(twhilerepeatnode(p).left)) or (not(lnf_testatbegin in twhilerepeatnode(p).loopflags) and
|
|
not(getbooleanvalue(twhilerepeatnode(p).left))) then
|
|
p.successor:=nil;
|
|
end;
|
|
|
|
BreakContinueStack.Pop;
|
|
end;
|
|
ifn:
|
|
begin
|
|
result:=p;
|
|
DoSet(tifnode(p).right,succ);
|
|
DoSet(tifnode(p).t1,succ);
|
|
p.successor:=succ;
|
|
end;
|
|
labeln:
|
|
begin
|
|
result:=p;
|
|
p.successor:=succ;
|
|
end;
|
|
assignn:
|
|
begin
|
|
result:=p;
|
|
p.successor:=succ;
|
|
end;
|
|
goton:
|
|
begin
|
|
result:=p;
|
|
if not(assigned(tgotonode(p).labelnode)) then
|
|
internalerror(2007050701);
|
|
p.successor:=tgotonode(p).labelnode;
|
|
end;
|
|
exitn:
|
|
begin
|
|
result:=p;
|
|
p.successor:=Exitsuccessor;
|
|
end;
|
|
casen:
|
|
begin
|
|
result:=p;
|
|
DoSet(tcasenode(p).elseblock,succ);
|
|
for i:=0 to tcasenode(p).blocks.count-1 do
|
|
DoSet(pcaseblock(tcasenode(p).blocks[i])^.statement,succ);
|
|
p.successor:=succ;
|
|
end;
|
|
calln:
|
|
begin
|
|
{ not sure if this is enough (FK) }
|
|
result:=p;
|
|
if cnf_call_never_returns in tcallnode(p).callnodeflags then
|
|
p.successor:=nil
|
|
else
|
|
p.successor:=succ;
|
|
end;
|
|
inlinen:
|
|
begin
|
|
{ not sure if this is enough (FK) }
|
|
result:=p;
|
|
p.successor:=succ;
|
|
end;
|
|
loadn,
|
|
tempcreaten,
|
|
tempdeleten,
|
|
nothingn:
|
|
begin
|
|
result:=p;
|
|
p.successor:=succ;
|
|
end;
|
|
raisen:
|
|
begin
|
|
result:=p;
|
|
{ raise never returns }
|
|
p.successor:=nil;
|
|
end;
|
|
tryexceptn,
|
|
tryfinallyn,
|
|
onn:
|
|
internalerror(2007050501);
|
|
else
|
|
;
|
|
end;
|
|
end;
|
|
|
|
begin
|
|
BreakContinueStack.Init;
|
|
Exitsuccessor:=nil;
|
|
DoSet(p,last);
|
|
BreakContinueStack.Done;
|
|
end;
|
|
|
|
|
|
function adddef(var n: tnode; arg: pointer): foreachnoderesult;
|
|
var
|
|
defsum : PDFASet absolute arg;
|
|
begin
|
|
if assigned(n.optinfo) then
|
|
begin
|
|
DFASetIncludeSet(defsum^,n.optinfo^.def);
|
|
{ for nodes itself do not necessarily expose the definition of the counter as
|
|
the counter might be undefined after the for loop, so include here the counter
|
|
explicitly }
|
|
if (n.nodetype=forn) and assigned(tfornode(n).left.optinfo) then
|
|
DFASetInclude(defsum^,tfornode(n).left.optinfo^.index);
|
|
end;
|
|
Result:=fen_false;
|
|
end;
|
|
|
|
|
|
procedure CalcDefSum(p : tnode);
|
|
var
|
|
defsum : PDFASet;
|
|
begin
|
|
p.allocoptinfo;
|
|
defsum:=@p.optinfo^.defsum;
|
|
if not assigned(defsum^) then
|
|
foreachnodestatic(pm_postprocess,p,@adddef,defsum);
|
|
end;
|
|
|
|
|
|
function SetExecutionWeight(var n: tnode; arg: pointer): foreachnoderesult;
|
|
var
|
|
Weight, CaseWeight : longint;
|
|
i : Integer;
|
|
begin
|
|
Result:=fen_false;
|
|
n.allocoptinfo;
|
|
Weight:=max(plongint(arg)^,1);
|
|
case n.nodetype of
|
|
casen:
|
|
begin
|
|
CalcExecutionWeights(tcasenode(n).left,Weight);
|
|
CaseWeight:=max(Weight div tcasenode(n).labelcnt,1);
|
|
for i:=0 to tcasenode(n).blocks.count-1 do
|
|
CalcExecutionWeights(pcaseblock(tcasenode(n).blocks[i])^.statement,CaseWeight);
|
|
|
|
CalcExecutionWeights(tcasenode(n).elseblock,CaseWeight);
|
|
Result:=fen_norecurse_false;
|
|
end;
|
|
whilerepeatn:
|
|
begin
|
|
CalcExecutionWeights(twhilerepeatnode(n).right,Weight*8);
|
|
CalcExecutionWeights(twhilerepeatnode(n).left,Weight*8);
|
|
Result:=fen_norecurse_false;
|
|
end;
|
|
ifn:
|
|
begin
|
|
CalcExecutionWeights(tifnode(n).left,Weight);
|
|
CalcExecutionWeights(tifnode(n).right,Weight div 2);
|
|
CalcExecutionWeights(tifnode(n).t1,Weight div 2);
|
|
Result:=fen_norecurse_false;
|
|
end;
|
|
else
|
|
;
|
|
end;
|
|
n.optinfo^.executionweight:=Weight;
|
|
end;
|
|
|
|
|
|
procedure CalcExecutionWeights(p : tnode;Initial : longint = 100);
|
|
begin
|
|
if assigned(p) then
|
|
foreachnodestatic(pm_postprocess,p,@SetExecutionWeight,Pointer(@Initial));
|
|
end;
|
|
|
|
|
|
function adduse(var n: tnode; arg: pointer): foreachnoderesult;
|
|
var
|
|
usesum : PDFASet absolute arg;
|
|
begin
|
|
if assigned(n.optinfo) then
|
|
DFASetIncludeSet(usesum^,n.optinfo^.use);
|
|
Result:=fen_false;
|
|
end;
|
|
|
|
|
|
procedure CalcUseSum(p : tnode);
|
|
var
|
|
usesum : PDFASet;
|
|
begin
|
|
p.allocoptinfo;
|
|
usesum:=@p.optinfo^.usesum;
|
|
if not assigned(usesum^) then
|
|
foreachnodestatic(pm_postprocess,p,@adduse,usesum);
|
|
end;
|
|
|
|
|
|
function has_life_info(n : tnode) : boolean;
|
|
begin
|
|
result:=assigned(n) and assigned(n.optinfo) and
|
|
assigned(n.optinfo^.life);
|
|
end;
|
|
|
|
end.
|
|
|