mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-04-11 01:18:30 +02:00
2033 lines
66 KiB
ObjectPascal
2033 lines
66 KiB
ObjectPascal
{
|
|
$Id$
|
|
Copyright (c) 1998-2002 by Florian Klaempfl
|
|
|
|
This unit implements the base class for the register allocator
|
|
|
|
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.
|
|
****************************************************************************
|
|
}
|
|
|
|
{$i fpcdefs.inc}
|
|
|
|
{ Allow duplicate allocations, can be used to get the .s file written }
|
|
{ $define ALLOWDUPREG}
|
|
|
|
|
|
unit rgobj;
|
|
|
|
interface
|
|
|
|
uses
|
|
cutils, cpubase,
|
|
aasmbase,aasmtai,aasmcpu,
|
|
cclasses,globtype,cgbase,cgutils,
|
|
cpuinfo
|
|
;
|
|
|
|
type
|
|
{
|
|
The interference bitmap contains of 2 layers:
|
|
layer 1 - 256*256 blocks with pointers to layer 2 blocks
|
|
layer 2 - blocks of 32*256 (32 bytes = 256 bits)
|
|
}
|
|
Tinterferencebitmap2 = array[byte] of set of byte;
|
|
Pinterferencebitmap2 = ^Tinterferencebitmap2;
|
|
Tinterferencebitmap1 = array[byte] of Pinterferencebitmap2;
|
|
pinterferencebitmap1 = ^tinterferencebitmap1;
|
|
|
|
Tinterferencebitmap=class
|
|
private
|
|
maxx1,
|
|
maxy1 : byte;
|
|
fbitmap : pinterferencebitmap1;
|
|
function getbitmap(x,y:tsuperregister):boolean;
|
|
procedure setbitmap(x,y:tsuperregister;b:boolean);
|
|
public
|
|
constructor create;
|
|
destructor destroy;override;
|
|
property bitmap[x,y:tsuperregister]:boolean read getbitmap write setbitmap;default;
|
|
end;
|
|
|
|
Tmovelistheader=record
|
|
count,
|
|
maxcount,
|
|
sorted_until : cardinal;
|
|
end;
|
|
|
|
Tmovelist=record
|
|
header : Tmovelistheader;
|
|
data : array[tsuperregister] of Tlinkedlistitem;
|
|
end;
|
|
Pmovelist=^Tmovelist;
|
|
|
|
{In the register allocator we keep track of move instructions.
|
|
These instructions are moved between five linked lists. There
|
|
is also a linked list per register to keep track about the moves
|
|
it is associated with. Because we need to determine quickly in
|
|
which of the five lists it is we add anu enumeradtion to each
|
|
move instruction.}
|
|
|
|
Tmoveset=(ms_coalesced_moves,ms_constrained_moves,ms_frozen_moves,
|
|
ms_worklist_moves,ms_active_moves);
|
|
Tmoveins=class(Tlinkedlistitem)
|
|
moveset:Tmoveset;
|
|
x,y:Tsuperregister;
|
|
end;
|
|
|
|
Treginfoflag=(ri_coalesced,ri_selected);
|
|
Treginfoflagset=set of Treginfoflag;
|
|
|
|
Treginfo=record
|
|
live_start,
|
|
live_end : Tai;
|
|
subreg : tsubregister;
|
|
alias : Tsuperregister;
|
|
{ The register allocator assigns each register a colour }
|
|
colour : Tsuperregister;
|
|
movelist : Pmovelist;
|
|
adjlist : Psuperregisterworklist;
|
|
degree : TSuperregister;
|
|
flags : Treginfoflagset;
|
|
end;
|
|
Preginfo=^TReginfo;
|
|
|
|
tspillreginfo = record
|
|
spillreg : tregister;
|
|
orgreg : tsuperregister;
|
|
tempreg : tregister;
|
|
regread,regwritten, mustbespilled: boolean;
|
|
end;
|
|
tspillregsinfo = array[0..2] of tspillreginfo;
|
|
|
|
{#------------------------------------------------------------------
|
|
|
|
This class implements the default register allocator. It is used by the
|
|
code generator to allocate and free registers which might be valid
|
|
across nodes. It also contains utility routines related to registers.
|
|
|
|
Some of the methods in this class should be overriden
|
|
by cpu-specific implementations.
|
|
|
|
--------------------------------------------------------------------}
|
|
trgobj=class
|
|
preserved_by_proc : tcpuregisterset;
|
|
used_in_proc : tcpuregisterset;
|
|
|
|
constructor create(Aregtype:Tregistertype;
|
|
Adefaultsub:Tsubregister;
|
|
const Ausable:array of tsuperregister;
|
|
Afirst_imaginary:Tsuperregister;
|
|
Apreserved_by_proc:Tcpuregisterset);
|
|
destructor destroy;override;
|
|
|
|
{# Allocate a register. An internalerror will be generated if there is
|
|
no more free registers which can be allocated.}
|
|
function getregister(list:Taasmoutput;subreg:Tsubregister):Tregister;virtual;
|
|
{# Get the register specified.}
|
|
procedure getcpuregister(list:Taasmoutput;r:Tregister);virtual;
|
|
procedure ungetcpuregister(list:Taasmoutput;r:Tregister);virtual;
|
|
{# Get multiple registers specified.}
|
|
procedure alloccpuregisters(list:Taasmoutput;r:Tcpuregisterset);virtual;
|
|
{# Free multiple registers specified.}
|
|
procedure dealloccpuregisters(list:Taasmoutput;r:Tcpuregisterset);virtual;
|
|
function uses_registers:boolean;virtual;
|
|
procedure add_reg_instruction(instr:Tai;r:tregister);
|
|
procedure add_move_instruction(instr:Taicpu);
|
|
{# Do the register allocation.}
|
|
procedure do_register_allocation(list:Taasmoutput;headertai:tai);virtual;
|
|
{ Adds an interference edge.
|
|
don't move this to the protected section, the arm cg requires to access this (FK) }
|
|
procedure add_edge(u,v:Tsuperregister);
|
|
protected
|
|
regtype : Tregistertype;
|
|
{ default subregister used }
|
|
defaultsub : tsubregister;
|
|
live_registers:Tsuperregisterworklist;
|
|
{ can be overriden to add cpu specific interferences }
|
|
procedure add_cpu_interferences(p : tai);virtual;
|
|
procedure add_constraints(reg:Tregister);virtual;
|
|
function getregisterinline(list:Taasmoutput;subreg:Tsubregister):Tregister;
|
|
procedure ungetregisterinline(list:Taasmoutput;r:Tregister);
|
|
function get_spill_subreg(r : tregister) : tsubregister;virtual;
|
|
function do_spill_replace(list:Taasmoutput;instr:taicpu;orgreg:tsuperregister;const spilltemp:treference):boolean;virtual;
|
|
procedure do_spill_read(list:Taasmoutput;pos:tai;const spilltemp:treference;tempreg:tregister);virtual;
|
|
procedure do_spill_written(list:Taasmoutput;pos:tai;const spilltemp:treference;tempreg:tregister);virtual;
|
|
|
|
function instr_spill_register(list:Taasmoutput;
|
|
instr:taicpu;
|
|
const r:Tsuperregisterset;
|
|
const spilltemplist:Tspill_temp_list): boolean;virtual;
|
|
private
|
|
{# First imaginary register.}
|
|
first_imaginary : Tsuperregister;
|
|
{# Highest register allocated until now.}
|
|
reginfo : PReginfo;
|
|
maxreginfo,
|
|
maxreginfoinc,
|
|
maxreg : Tsuperregister;
|
|
usable_registers_cnt : word;
|
|
usable_registers : array[0..maxcpuregister-1] of tsuperregister;
|
|
ibitmap : Tinterferencebitmap;
|
|
spillednodes,
|
|
simplifyworklist,
|
|
freezeworklist,
|
|
spillworklist,
|
|
coalescednodes,
|
|
selectstack : tsuperregisterworklist;
|
|
worklist_moves,
|
|
active_moves,
|
|
frozen_moves,
|
|
coalesced_moves,
|
|
constrained_moves : Tlinkedlist;
|
|
{$ifdef EXTDEBUG}
|
|
procedure writegraph(loopidx:longint);
|
|
{$endif EXTDEBUG}
|
|
{# Disposes of the reginfo array.}
|
|
procedure dispose_reginfo;
|
|
{# Prepare the register colouring.}
|
|
procedure prepare_colouring;
|
|
{# Clean up after register colouring.}
|
|
procedure epilogue_colouring;
|
|
{# Colour the registers; that is do the register allocation.}
|
|
procedure colour_registers;
|
|
procedure insert_regalloc_info(list:Taasmoutput;u:tsuperregister);
|
|
procedure insert_regalloc_info_all(list:Taasmoutput);
|
|
procedure generate_interference_graph(list:Taasmoutput;headertai:tai);
|
|
procedure translate_registers(list:Taasmoutput);
|
|
function spill_registers(list:Taasmoutput;headertai:tai):boolean;virtual;
|
|
function getnewreg(subreg:tsubregister):tsuperregister;
|
|
procedure add_edges_used(u:Tsuperregister);
|
|
procedure add_to_movelist(u:Tsuperregister;data:Tlinkedlistitem);
|
|
function move_related(n:Tsuperregister):boolean;
|
|
procedure make_work_list;
|
|
procedure sort_simplify_worklist;
|
|
procedure enable_moves(n:Tsuperregister);
|
|
procedure decrement_degree(m:Tsuperregister);
|
|
procedure simplify;
|
|
function get_alias(n:Tsuperregister):Tsuperregister;
|
|
procedure add_worklist(u:Tsuperregister);
|
|
function adjacent_ok(u,v:Tsuperregister):boolean;
|
|
function conservative(u,v:Tsuperregister):boolean;
|
|
procedure combine(u,v:Tsuperregister);
|
|
procedure coalesce;
|
|
procedure freeze_moves(u:Tsuperregister);
|
|
procedure freeze;
|
|
procedure select_spill;
|
|
procedure assign_colours;
|
|
procedure clear_interferences(u:Tsuperregister);
|
|
end;
|
|
|
|
const
|
|
first_reg = 0;
|
|
last_reg = high(tsuperregister)-1;
|
|
maxspillingcounter = 20;
|
|
|
|
|
|
implementation
|
|
|
|
uses
|
|
systems,
|
|
globals,verbose,tgobj,procinfo;
|
|
|
|
|
|
procedure sort_movelist(ml:Pmovelist);
|
|
|
|
{Ok, sorting pointers is silly, but it does the job to make Trgobj.combine
|
|
faster.}
|
|
|
|
var h,i,p:word;
|
|
t:Tlinkedlistitem;
|
|
|
|
begin
|
|
with ml^ do
|
|
begin
|
|
if header.count<2 then
|
|
exit;
|
|
p:=1;
|
|
while 2*p<header.count do
|
|
p:=2*p;
|
|
while p<>0 do
|
|
begin
|
|
for h:=p to header.count-1 do
|
|
begin
|
|
i:=h;
|
|
t:=data[i];
|
|
repeat
|
|
if ptrint(data[i-p])<=ptrint(t) then
|
|
break;
|
|
data[i]:=data[i-p];
|
|
dec(i,p);
|
|
until i<p;
|
|
data[i]:=t;
|
|
end;
|
|
p:=p shr 1;
|
|
end;
|
|
header.sorted_until:=header.count-1;
|
|
end;
|
|
end;
|
|
|
|
{******************************************************************************
|
|
tinterferencebitmap
|
|
******************************************************************************}
|
|
|
|
constructor tinterferencebitmap.create;
|
|
begin
|
|
inherited create;
|
|
maxx1:=1;
|
|
getmem(fbitmap,sizeof(tinterferencebitmap1)*2);
|
|
fillchar(fbitmap^,sizeof(tinterferencebitmap1)*2,0);
|
|
end;
|
|
|
|
|
|
destructor tinterferencebitmap.destroy;
|
|
|
|
var i,j:byte;
|
|
|
|
begin
|
|
for i:=0 to maxx1 do
|
|
for j:=0 to maxy1 do
|
|
if assigned(fbitmap[i,j]) then
|
|
dispose(fbitmap[i,j]);
|
|
freemem(fbitmap);
|
|
end;
|
|
|
|
|
|
function tinterferencebitmap.getbitmap(x,y:tsuperregister):boolean;
|
|
var
|
|
page : pinterferencebitmap2;
|
|
begin
|
|
result:=false;
|
|
if (x shr 8>maxx1) then
|
|
exit;
|
|
page:=fbitmap[x shr 8,y shr 8];
|
|
result:=assigned(page) and
|
|
((x and $ff) in page^[y and $ff]);
|
|
end;
|
|
|
|
|
|
procedure tinterferencebitmap.setbitmap(x,y:tsuperregister;b:boolean);
|
|
var
|
|
x1,y1 : byte;
|
|
begin
|
|
x1:=x shr 8;
|
|
y1:=y shr 8;
|
|
if x1>maxx1 then
|
|
begin
|
|
reallocmem(fbitmap,sizeof(tinterferencebitmap1)*(x1+1));
|
|
fillchar(fbitmap[maxx1+1],sizeof(tinterferencebitmap1)*(x1-maxx1),0);
|
|
maxx1:=x1;
|
|
end;
|
|
if not assigned(fbitmap[x1,y1]) then
|
|
begin
|
|
if y1>maxy1 then
|
|
maxy1:=y1;
|
|
new(fbitmap[x1,y1]);
|
|
fillchar(fbitmap[x1,y1]^,sizeof(tinterferencebitmap2),0);
|
|
end;
|
|
if b then
|
|
include(fbitmap[x1,y1]^[y and $ff],(x and $ff))
|
|
else
|
|
exclude(fbitmap[x1,y1]^[y and $ff],(x and $ff));
|
|
end;
|
|
|
|
|
|
{******************************************************************************
|
|
trgobj
|
|
******************************************************************************}
|
|
|
|
constructor trgobj.create(Aregtype:Tregistertype;
|
|
Adefaultsub:Tsubregister;
|
|
const Ausable:array of tsuperregister;
|
|
Afirst_imaginary:Tsuperregister;
|
|
Apreserved_by_proc:Tcpuregisterset);
|
|
var
|
|
i : Tsuperregister;
|
|
begin
|
|
{ empty super register sets can cause very strange problems }
|
|
if high(Ausable)=0 then
|
|
internalerror(200210181);
|
|
first_imaginary:=Afirst_imaginary;
|
|
maxreg:=Afirst_imaginary;
|
|
regtype:=Aregtype;
|
|
defaultsub:=Adefaultsub;
|
|
preserved_by_proc:=Apreserved_by_proc;
|
|
used_in_proc:=[];
|
|
live_registers.init;
|
|
{ Get reginfo for CPU registers }
|
|
maxreginfo:=first_imaginary;
|
|
maxreginfoinc:=16;
|
|
worklist_moves:=Tlinkedlist.create;
|
|
reginfo:=allocmem(first_imaginary*sizeof(treginfo));
|
|
for i:=0 to first_imaginary-1 do
|
|
begin
|
|
reginfo[i].degree:=high(tsuperregister);
|
|
reginfo[i].alias:=RS_INVALID;
|
|
end;
|
|
{ Usable registers }
|
|
fillchar(usable_registers,sizeof(usable_registers),0);
|
|
for i:=low(Ausable) to high(Ausable) do
|
|
usable_registers[i]:=Ausable[i];
|
|
usable_registers_cnt:=high(Ausable)+1;
|
|
{ Initialize Worklists }
|
|
spillednodes.init;
|
|
simplifyworklist.init;
|
|
freezeworklist.init;
|
|
spillworklist.init;
|
|
coalescednodes.init;
|
|
selectstack.init;
|
|
end;
|
|
|
|
destructor trgobj.destroy;
|
|
|
|
begin
|
|
spillednodes.done;
|
|
simplifyworklist.done;
|
|
freezeworklist.done;
|
|
spillworklist.done;
|
|
coalescednodes.done;
|
|
selectstack.done;
|
|
live_registers.done;
|
|
worklist_moves.free;
|
|
dispose_reginfo;
|
|
end;
|
|
|
|
procedure Trgobj.dispose_reginfo;
|
|
|
|
var i:Tsuperregister;
|
|
|
|
begin
|
|
if reginfo<>nil then
|
|
begin
|
|
for i:=0 to maxreg-1 do
|
|
with reginfo[i] do
|
|
begin
|
|
if adjlist<>nil then
|
|
dispose(adjlist,done);
|
|
if movelist<>nil then
|
|
dispose(movelist);
|
|
end;
|
|
freemem(reginfo);
|
|
reginfo:=nil;
|
|
end;
|
|
end;
|
|
|
|
function trgobj.getnewreg(subreg:tsubregister):tsuperregister;
|
|
var
|
|
oldmaxreginfo : tsuperregister;
|
|
begin
|
|
result:=maxreg;
|
|
inc(maxreg);
|
|
if maxreg>=last_reg then
|
|
Message(parser_f_too_complex_proc);
|
|
if maxreg>=maxreginfo then
|
|
begin
|
|
oldmaxreginfo:=maxreginfo;
|
|
{ Prevent overflow }
|
|
if maxreginfoinc>last_reg-maxreginfo then
|
|
maxreginfo:=last_reg
|
|
else
|
|
begin
|
|
inc(maxreginfo,maxreginfoinc);
|
|
if maxreginfoinc<256 then
|
|
maxreginfoinc:=maxreginfoinc*2;
|
|
end;
|
|
reallocmem(reginfo,maxreginfo*sizeof(treginfo));
|
|
{ Do we really need it to clear it ? At least for 1.0.x (PFV) }
|
|
fillchar(reginfo[oldmaxreginfo],(maxreginfo-oldmaxreginfo)*sizeof(treginfo),0);
|
|
end;
|
|
reginfo[result].subreg:=subreg;
|
|
end;
|
|
|
|
|
|
function trgobj.getregister(list:Taasmoutput;subreg:Tsubregister):Tregister;
|
|
begin
|
|
{$ifdef EXTDEBUG}
|
|
if reginfo=nil then
|
|
InternalError(2004020901);
|
|
{$endif EXTDEBUG}
|
|
if defaultsub=R_SUBNONE then
|
|
result:=newreg(regtype,getnewreg(R_SUBNONE),R_SUBNONE)
|
|
else
|
|
result:=newreg(regtype,getnewreg(subreg),subreg);
|
|
end;
|
|
|
|
|
|
function trgobj.uses_registers:boolean;
|
|
begin
|
|
result:=(maxreg>first_imaginary);
|
|
end;
|
|
|
|
|
|
procedure trgobj.ungetcpuregister(list:Taasmoutput;r:Tregister);
|
|
begin
|
|
if (getsupreg(r)>=first_imaginary) then
|
|
InternalError(2004020901);
|
|
list.concat(Tai_regalloc.dealloc(r,nil));
|
|
end;
|
|
|
|
|
|
procedure trgobj.getcpuregister(list:Taasmoutput;r:Tregister);
|
|
var
|
|
supreg:Tsuperregister;
|
|
begin
|
|
supreg:=getsupreg(r);
|
|
if supreg>=first_imaginary then
|
|
internalerror(2003121503);
|
|
include(used_in_proc,supreg);
|
|
list.concat(Tai_regalloc.alloc(r,nil));
|
|
end;
|
|
|
|
|
|
procedure trgobj.alloccpuregisters(list:Taasmoutput;r:Tcpuregisterset);
|
|
|
|
var i:Tsuperregister;
|
|
|
|
begin
|
|
for i:=0 to first_imaginary-1 do
|
|
if i in r then
|
|
getcpuregister(list,newreg(regtype,i,defaultsub));
|
|
end;
|
|
|
|
|
|
procedure trgobj.dealloccpuregisters(list:Taasmoutput;r:Tcpuregisterset);
|
|
|
|
var i:Tsuperregister;
|
|
|
|
begin
|
|
for i:=0 to first_imaginary-1 do
|
|
if i in r then
|
|
ungetcpuregister(list,newreg(regtype,i,defaultsub));
|
|
end;
|
|
|
|
|
|
procedure trgobj.do_register_allocation(list:Taasmoutput;headertai:tai);
|
|
var
|
|
spillingcounter:byte;
|
|
endspill:boolean;
|
|
begin
|
|
{ Insert regalloc info for imaginary registers }
|
|
insert_regalloc_info_all(list);
|
|
ibitmap:=tinterferencebitmap.create;
|
|
generate_interference_graph(list,headertai);
|
|
{ Don't do the real allocation when -sr is passed }
|
|
if (cs_no_regalloc in aktglobalswitches) then
|
|
exit;
|
|
{Do register allocation.}
|
|
spillingcounter:=0;
|
|
repeat
|
|
prepare_colouring;
|
|
colour_registers;
|
|
epilogue_colouring;
|
|
endspill:=true;
|
|
if spillednodes.length<>0 then
|
|
begin
|
|
inc(spillingcounter);
|
|
if spillingcounter>maxspillingcounter then
|
|
exit;
|
|
if spillingcounter>maxspillingcounter then
|
|
internalerror(200309041);
|
|
endspill:=not spill_registers(list,headertai);
|
|
end;
|
|
until endspill;
|
|
ibitmap.free;
|
|
translate_registers(list);
|
|
dispose_reginfo;
|
|
end;
|
|
|
|
|
|
procedure trgobj.add_constraints(reg:Tregister);
|
|
|
|
begin
|
|
end;
|
|
|
|
|
|
procedure trgobj.add_edge(u,v:Tsuperregister);
|
|
|
|
{This procedure will add an edge to the virtual interference graph.}
|
|
|
|
procedure addadj(u,v:Tsuperregister);
|
|
|
|
begin
|
|
with reginfo[u] do
|
|
begin
|
|
if adjlist=nil then
|
|
new(adjlist,init);
|
|
adjlist^.add(v);
|
|
end;
|
|
end;
|
|
|
|
begin
|
|
if (u<>v) and not(ibitmap[v,u]) then
|
|
begin
|
|
ibitmap[v,u]:=true;
|
|
ibitmap[u,v]:=true;
|
|
{Precoloured nodes are not stored in the interference graph.}
|
|
if (u>=first_imaginary) then
|
|
addadj(u,v);
|
|
if (v>=first_imaginary) then
|
|
addadj(v,u);
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure trgobj.add_edges_used(u:Tsuperregister);
|
|
|
|
var i:word;
|
|
|
|
begin
|
|
with live_registers do
|
|
if length>0 then
|
|
for i:=0 to length-1 do
|
|
add_edge(u,get_alias(buf^[i]));
|
|
end;
|
|
|
|
{$ifdef EXTDEBUG}
|
|
procedure trgobj.writegraph(loopidx:longint);
|
|
|
|
{This procedure writes out the current interference graph in the
|
|
register allocator.}
|
|
|
|
|
|
var f:text;
|
|
i,j:Tsuperregister;
|
|
|
|
begin
|
|
assign(f,'igraph'+tostr(loopidx));
|
|
rewrite(f);
|
|
writeln(f,'Interference graph');
|
|
writeln(f);
|
|
write(f,' ');
|
|
for i:=0 to 15 do
|
|
for j:=0 to 15 do
|
|
write(f,hexstr(i,1));
|
|
writeln(f);
|
|
write(f,' ');
|
|
for i:=0 to 15 do
|
|
write(f,'0123456789ABCDEF');
|
|
writeln(f);
|
|
for i:=0 to maxreg-1 do
|
|
begin
|
|
write(f,hexstr(i,2):4);
|
|
for j:=0 to maxreg-1 do
|
|
if ibitmap[i,j] then
|
|
write(f,'*')
|
|
else
|
|
write(f,'-');
|
|
writeln(f);
|
|
end;
|
|
close(f);
|
|
end;
|
|
{$endif EXTDEBUG}
|
|
|
|
procedure trgobj.add_to_movelist(u:Tsuperregister;data:Tlinkedlistitem);
|
|
begin
|
|
with reginfo[u] do
|
|
begin
|
|
if movelist=nil then
|
|
begin
|
|
getmem(movelist,sizeof(tmovelistheader)+60*sizeof(pointer));
|
|
movelist^.header.maxcount:=60;
|
|
movelist^.header.count:=0;
|
|
movelist^.header.sorted_until:=0;
|
|
end
|
|
else
|
|
begin
|
|
if movelist^.header.count>=movelist^.header.maxcount then
|
|
begin
|
|
movelist^.header.maxcount:=movelist^.header.maxcount*2;
|
|
reallocmem(movelist,sizeof(tmovelistheader)+movelist^.header.maxcount*sizeof(pointer));
|
|
end;
|
|
end;
|
|
movelist^.data[movelist^.header.count]:=data;
|
|
inc(movelist^.header.count);
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure trgobj.add_reg_instruction(instr:Tai;r:tregister);
|
|
var
|
|
supreg : tsuperregister;
|
|
begin
|
|
supreg:=getsupreg(r);
|
|
{$ifdef extdebug}
|
|
if supreg>=maxreginfo then
|
|
internalerror(200411061);
|
|
{$endif extdebug}
|
|
if supreg>=first_imaginary then
|
|
with reginfo[supreg] do
|
|
begin
|
|
if not assigned(live_start) then
|
|
live_start:=instr;
|
|
live_end:=instr;
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure trgobj.add_move_instruction(instr:Taicpu);
|
|
|
|
{This procedure notifies a certain as a move instruction so the
|
|
register allocator can try to eliminate it.}
|
|
|
|
var i:Tmoveins;
|
|
ssupreg,dsupreg:Tsuperregister;
|
|
|
|
begin
|
|
{$ifdef extdebug}
|
|
if (instr.oper[O_MOV_SOURCE]^.typ<>top_reg) or
|
|
(instr.oper[O_MOV_DEST]^.typ<>top_reg) then
|
|
internalerror(200311291);
|
|
{$endif}
|
|
i:=Tmoveins.create;
|
|
i.moveset:=ms_worklist_moves;
|
|
worklist_moves.insert(i);
|
|
ssupreg:=getsupreg(instr.oper[O_MOV_SOURCE]^.reg);
|
|
add_to_movelist(ssupreg,i);
|
|
dsupreg:=getsupreg(instr.oper[O_MOV_DEST]^.reg);
|
|
if ssupreg<>dsupreg then
|
|
{Avoid adding the same move instruction twice to a single register.}
|
|
add_to_movelist(dsupreg,i);
|
|
i.x:=ssupreg;
|
|
i.y:=dsupreg;
|
|
end;
|
|
|
|
function trgobj.move_related(n:Tsuperregister):boolean;
|
|
|
|
var i:cardinal;
|
|
|
|
begin
|
|
move_related:=false;
|
|
with reginfo[n] do
|
|
if movelist<>nil then
|
|
with movelist^ do
|
|
for i:=0 to header.count-1 do
|
|
if Tmoveins(data[i]).moveset in [ms_worklist_moves,ms_active_moves] then
|
|
begin
|
|
move_related:=true;
|
|
break;
|
|
end;
|
|
end;
|
|
|
|
procedure Trgobj.sort_simplify_worklist;
|
|
|
|
{Sorts the simplifyworklist by the number of interferences the
|
|
registers in it cause. This allows simplify to execute in
|
|
constant time.}
|
|
|
|
var p,h,i,leni,lent:word;
|
|
t:Tsuperregister;
|
|
adji,adjt:Psuperregisterworklist;
|
|
|
|
begin
|
|
with simplifyworklist do
|
|
begin
|
|
if length<2 then
|
|
exit;
|
|
p:=1;
|
|
while 2*p<length do
|
|
p:=2*p;
|
|
while p<>0 do
|
|
begin
|
|
for h:=p to length-1 do
|
|
begin
|
|
i:=h;
|
|
t:=buf^[i];
|
|
adjt:=reginfo[buf^[i]].adjlist;
|
|
lent:=0;
|
|
if adjt<>nil then
|
|
lent:=adjt^.length;
|
|
repeat
|
|
adji:=reginfo[buf^[i-p]].adjlist;
|
|
leni:=0;
|
|
if adji<>nil then
|
|
leni:=adji^.length;
|
|
if leni<=lent then
|
|
break;
|
|
buf^[i]:=buf^[i-p];
|
|
dec(i,p)
|
|
until i<p;
|
|
buf^[i]:=t;
|
|
end;
|
|
p:=p shr 1;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure trgobj.make_work_list;
|
|
|
|
var n:Tsuperregister;
|
|
|
|
begin
|
|
{If we have 7 cpu registers, and the degree of a node is 7, we cannot
|
|
assign it to any of the registers, thus it is significant.}
|
|
for n:=first_imaginary to maxreg-1 do
|
|
with reginfo[n] do
|
|
begin
|
|
if adjlist=nil then
|
|
degree:=0
|
|
else
|
|
degree:=adjlist^.length;
|
|
if degree>=usable_registers_cnt then
|
|
spillworklist.add(n)
|
|
else if move_related(n) then
|
|
freezeworklist.add(n)
|
|
else
|
|
simplifyworklist.add(n);
|
|
end;
|
|
sort_simplify_worklist;
|
|
end;
|
|
|
|
|
|
procedure trgobj.prepare_colouring;
|
|
begin
|
|
make_work_list;
|
|
active_moves:=Tlinkedlist.create;
|
|
frozen_moves:=Tlinkedlist.create;
|
|
coalesced_moves:=Tlinkedlist.create;
|
|
constrained_moves:=Tlinkedlist.create;
|
|
selectstack.clear;
|
|
end;
|
|
|
|
procedure trgobj.enable_moves(n:Tsuperregister);
|
|
|
|
var m:Tlinkedlistitem;
|
|
i:cardinal;
|
|
|
|
begin
|
|
with reginfo[n] do
|
|
if movelist<>nil then
|
|
for i:=0 to movelist^.header.count-1 do
|
|
begin
|
|
m:=movelist^.data[i];
|
|
if Tmoveins(m).moveset in [ms_worklist_moves,ms_active_moves] then
|
|
if Tmoveins(m).moveset=ms_active_moves then
|
|
begin
|
|
{Move m from the set active_moves to the set worklist_moves.}
|
|
active_moves.remove(m);
|
|
Tmoveins(m).moveset:=ms_worklist_moves;
|
|
worklist_moves.concat(m);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure Trgobj.decrement_degree(m:Tsuperregister);
|
|
|
|
var adj : Psuperregisterworklist;
|
|
n : tsuperregister;
|
|
d,i : word;
|
|
|
|
begin
|
|
with reginfo[m] do
|
|
begin
|
|
d:=degree;
|
|
if d=0 then
|
|
internalerror(200312151);
|
|
dec(degree);
|
|
if d=usable_registers_cnt then
|
|
begin
|
|
{Enable moves for m.}
|
|
enable_moves(m);
|
|
{Enable moves for adjacent.}
|
|
adj:=adjlist;
|
|
if adj<>nil then
|
|
for i:=1 to adj^.length do
|
|
begin
|
|
n:=adj^.buf^[i-1];
|
|
if reginfo[n].flags*[ri_selected,ri_coalesced]<>[] then
|
|
enable_moves(n);
|
|
end;
|
|
{Remove the node from the spillworklist.}
|
|
if not spillworklist.delete(m) then
|
|
internalerror(200310145);
|
|
|
|
if move_related(m) then
|
|
freezeworklist.add(m)
|
|
else
|
|
simplifyworklist.add(m);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure trgobj.simplify;
|
|
|
|
var adj : Psuperregisterworklist;
|
|
m,n : Tsuperregister;
|
|
i : word;
|
|
begin
|
|
{We take the element with the least interferences out of the
|
|
simplifyworklist. Since the simplifyworklist is now sorted, we
|
|
no longer need to search, but we can simply take the first element.}
|
|
m:=simplifyworklist.get;
|
|
|
|
{Push it on the selectstack.}
|
|
selectstack.add(m);
|
|
with reginfo[m] do
|
|
begin
|
|
include(flags,ri_selected);
|
|
adj:=adjlist;
|
|
end;
|
|
if adj<>nil then
|
|
for i:=1 to adj^.length do
|
|
begin
|
|
n:=adj^.buf^[i-1];
|
|
if (n>=first_imaginary) and
|
|
(reginfo[n].flags*[ri_selected,ri_coalesced]=[]) then
|
|
decrement_degree(n);
|
|
end;
|
|
end;
|
|
|
|
function trgobj.get_alias(n:Tsuperregister):Tsuperregister;
|
|
|
|
begin
|
|
while ri_coalesced in reginfo[n].flags do
|
|
n:=reginfo[n].alias;
|
|
get_alias:=n;
|
|
end;
|
|
|
|
procedure trgobj.add_worklist(u:Tsuperregister);
|
|
begin
|
|
if (u>=first_imaginary) and
|
|
(not move_related(u)) and
|
|
(reginfo[u].degree<usable_registers_cnt) then
|
|
begin
|
|
if not freezeworklist.delete(u) then
|
|
internalerror(200308161); {must be found}
|
|
simplifyworklist.add(u);
|
|
end;
|
|
end;
|
|
|
|
|
|
function trgobj.adjacent_ok(u,v:Tsuperregister):boolean;
|
|
|
|
{Check wether u and v should be coalesced. u is precoloured.}
|
|
|
|
function ok(t,r:Tsuperregister):boolean;
|
|
|
|
begin
|
|
ok:=(t<first_imaginary) or
|
|
(reginfo[t].degree<usable_registers_cnt) or
|
|
ibitmap[r,t];
|
|
end;
|
|
|
|
var adj : Psuperregisterworklist;
|
|
i : word;
|
|
n : tsuperregister;
|
|
|
|
begin
|
|
with reginfo[v] do
|
|
begin
|
|
adjacent_ok:=true;
|
|
adj:=adjlist;
|
|
if adj<>nil then
|
|
for i:=1 to adj^.length do
|
|
begin
|
|
n:=adj^.buf^[i-1];
|
|
if (flags*[ri_coalesced,ri_selected]=[]) and not ok(n,u) then
|
|
begin
|
|
adjacent_ok:=false;
|
|
break;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function trgobj.conservative(u,v:Tsuperregister):boolean;
|
|
|
|
var adj : Psuperregisterworklist;
|
|
done : Tsuperregisterset; {To prevent that we count nodes twice.}
|
|
i,k:word;
|
|
n : tsuperregister;
|
|
|
|
begin
|
|
k:=0;
|
|
supregset_reset(done,false,maxreg);
|
|
with reginfo[u] do
|
|
begin
|
|
adj:=adjlist;
|
|
if adj<>nil then
|
|
for i:=1 to adj^.length do
|
|
begin
|
|
n:=adj^.buf^[i-1];
|
|
if flags*[ri_coalesced,ri_selected]=[] then
|
|
begin
|
|
supregset_include(done,n);
|
|
if reginfo[n].degree>=usable_registers_cnt then
|
|
inc(k);
|
|
end;
|
|
end;
|
|
end;
|
|
adj:=reginfo[v].adjlist;
|
|
if adj<>nil then
|
|
for i:=1 to adj^.length do
|
|
begin
|
|
n:=adj^.buf^[i-1];
|
|
if not supregset_in(done,n) and
|
|
(reginfo[n].degree>=usable_registers_cnt) and
|
|
(reginfo[u].flags*[ri_coalesced,ri_selected]=[]) then
|
|
inc(k);
|
|
end;
|
|
conservative:=(k<usable_registers_cnt);
|
|
end;
|
|
|
|
|
|
procedure trgobj.combine(u,v:Tsuperregister);
|
|
|
|
var adj : Psuperregisterworklist;
|
|
i,n,p,q:cardinal;
|
|
t : tsuperregister;
|
|
searched:Tlinkedlistitem;
|
|
|
|
label l1;
|
|
|
|
begin
|
|
if not freezeworklist.delete(v) then
|
|
spillworklist.delete(v);
|
|
coalescednodes.add(v);
|
|
include(reginfo[v].flags,ri_coalesced);
|
|
reginfo[v].alias:=u;
|
|
|
|
{Combine both movelists. Since the movelists are sets, only add
|
|
elements that are not already present. The movelists cannot be
|
|
empty by definition; nodes are only coalesced if there is a move
|
|
between them. To prevent quadratic time blowup (movelists of
|
|
especially machine registers can get very large because of moves
|
|
generated during calls) we need to go into disgusting complexity.
|
|
|
|
(See webtbs/tw2242 for an example that stresses this.)
|
|
|
|
We want to sort the movelist to be able to search logarithmically.
|
|
Unfortunately, sorting the movelist every time before searching
|
|
is counter-productive, since the movelist usually grows with a few
|
|
items at a time. Therefore, we split the movelist into a sorted
|
|
and an unsorted part and search through both. If the unsorted part
|
|
becomes too large, we sort.}
|
|
if assigned(reginfo[u].movelist) then
|
|
begin
|
|
{We have to weigh the cost of sorting the list against searching
|
|
the cost of the unsorted part. I use factor of 8 here; if the
|
|
number of items is less than 8 times the numer of unsorted items,
|
|
we'll sort the list.}
|
|
with reginfo[u].movelist^ do
|
|
if header.count<8*(header.count-header.sorted_until) then
|
|
sort_movelist(reginfo[u].movelist);
|
|
|
|
if assigned(reginfo[v].movelist) then
|
|
begin
|
|
for n:=0 to reginfo[v].movelist^.header.count-1 do
|
|
begin
|
|
{Binary search the sorted part of the list.}
|
|
searched:=reginfo[v].movelist^.data[n];
|
|
p:=0;
|
|
q:=reginfo[u].movelist^.header.sorted_until;
|
|
i:=0;
|
|
if q<>0 then
|
|
repeat
|
|
i:=(p+q) shr 1;
|
|
if ptrint(searched)>ptrint(reginfo[u].movelist^.data[i]) then
|
|
p:=i+1
|
|
else
|
|
q:=i;
|
|
until p=q;
|
|
with reginfo[u].movelist^ do
|
|
if searched<>data[i] then
|
|
begin
|
|
{Linear search the unsorted part of the list.}
|
|
for i:=header.sorted_until+1 to header.count-1 do
|
|
if searched=data[i] then
|
|
goto l1;
|
|
{Not found -> add}
|
|
add_to_movelist(u,searched);
|
|
l1:
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
enable_moves(v);
|
|
|
|
adj:=reginfo[v].adjlist;
|
|
if adj<>nil then
|
|
for i:=1 to adj^.length do
|
|
begin
|
|
t:=adj^.buf^[i-1];
|
|
with reginfo[t] do
|
|
if not(ri_coalesced in flags) then
|
|
begin
|
|
{t has a connection to v. Since we are adding v to u, we
|
|
need to connect t to u. However, beware if t was already
|
|
connected to u...}
|
|
if (ibitmap[t,u]) and not (ri_selected in flags) then
|
|
{... because in that case, we are actually removing an edge
|
|
and the degree of t decreases.}
|
|
decrement_degree(t)
|
|
else
|
|
begin
|
|
add_edge(t,u);
|
|
{We have added an edge to t and u. So their degree increases.
|
|
However, v is added to u. That means its neighbours will
|
|
no longer point to v, but to u instead. Therefore, only the
|
|
degree of u increases.}
|
|
if (u>=first_imaginary) and not (ri_selected in flags) then
|
|
inc(reginfo[u].degree);
|
|
end;
|
|
end;
|
|
end;
|
|
if (reginfo[u].degree>=usable_registers_cnt) and freezeworklist.delete(u) then
|
|
spillworklist.add(u);
|
|
end;
|
|
|
|
|
|
procedure trgobj.coalesce;
|
|
|
|
var m:Tmoveins;
|
|
x,y,u,v:Tsuperregister;
|
|
|
|
begin
|
|
m:=Tmoveins(worklist_moves.getfirst);
|
|
x:=get_alias(m.x);
|
|
y:=get_alias(m.y);
|
|
if (y<first_imaginary) then
|
|
begin
|
|
u:=y;
|
|
v:=x;
|
|
end
|
|
else
|
|
begin
|
|
u:=x;
|
|
v:=y;
|
|
end;
|
|
if (u=v) then
|
|
begin
|
|
m.moveset:=ms_coalesced_moves; {Already coalesced.}
|
|
coalesced_moves.insert(m);
|
|
add_worklist(u);
|
|
end
|
|
{Do u and v interfere? In that case the move is constrained. Two
|
|
precoloured nodes interfere allways. If v is precoloured, by the above
|
|
code u is precoloured, thus interference...}
|
|
else if (v<first_imaginary) or ibitmap[u,v] then
|
|
begin
|
|
m.moveset:=ms_constrained_moves; {Cannot coalesce yet...}
|
|
constrained_moves.insert(m);
|
|
add_worklist(u);
|
|
add_worklist(v);
|
|
end
|
|
{Next test: is it possible and a good idea to coalesce??}
|
|
else if ((u<first_imaginary) and adjacent_ok(u,v)) or
|
|
((u>=first_imaginary) and conservative(u,v)) then
|
|
begin
|
|
m.moveset:=ms_coalesced_moves; {Move coalesced!}
|
|
coalesced_moves.insert(m);
|
|
combine(u,v);
|
|
add_worklist(u);
|
|
end
|
|
else
|
|
begin
|
|
m.moveset:=ms_active_moves;
|
|
active_moves.insert(m);
|
|
end;
|
|
end;
|
|
|
|
procedure trgobj.freeze_moves(u:Tsuperregister);
|
|
|
|
var i:cardinal;
|
|
m:Tlinkedlistitem;
|
|
v,x,y:Tsuperregister;
|
|
|
|
begin
|
|
if reginfo[u].movelist<>nil then
|
|
for i:=0 to reginfo[u].movelist^.header.count-1 do
|
|
begin
|
|
m:=reginfo[u].movelist^.data[i];
|
|
if Tmoveins(m).moveset in [ms_worklist_moves,ms_active_moves] then
|
|
begin
|
|
x:=Tmoveins(m).x;
|
|
y:=Tmoveins(m).y;
|
|
if get_alias(y)=get_alias(u) then
|
|
v:=get_alias(x)
|
|
else
|
|
v:=get_alias(y);
|
|
{Move m from active_moves/worklist_moves to frozen_moves.}
|
|
if Tmoveins(m).moveset=ms_active_moves then
|
|
active_moves.remove(m)
|
|
else
|
|
worklist_moves.remove(m);
|
|
Tmoveins(m).moveset:=ms_frozen_moves;
|
|
frozen_moves.insert(m);
|
|
|
|
if (v>=first_imaginary) and not(move_related(v)) and
|
|
(reginfo[v].degree<usable_registers_cnt) then
|
|
begin
|
|
freezeworklist.delete(v);
|
|
simplifyworklist.add(v);
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure trgobj.freeze;
|
|
|
|
var n:Tsuperregister;
|
|
|
|
begin
|
|
{ We need to take a random element out of the freezeworklist. We take
|
|
the last element. Dirty code! }
|
|
n:=freezeworklist.get;
|
|
{Add it to the simplifyworklist.}
|
|
simplifyworklist.add(n);
|
|
freeze_moves(n);
|
|
end;
|
|
|
|
procedure trgobj.select_spill;
|
|
|
|
var
|
|
n : tsuperregister;
|
|
adj : psuperregisterworklist;
|
|
max,p,i:word;
|
|
|
|
begin
|
|
{ We must look for the element with the most interferences in the
|
|
spillworklist. This is required because those registers are creating
|
|
the most conflicts and keeping them in a register will not reduce the
|
|
complexity and even can cause the help registers for the spilling code
|
|
to get too much conflicts with the result that the spilling code
|
|
will never converge (PFV) }
|
|
max:=0;
|
|
p:=0;
|
|
with spillworklist do
|
|
begin
|
|
{Safe: This procedure is only called if length<>0}
|
|
for i:=0 to length-1 do
|
|
begin
|
|
adj:=reginfo[buf^[i]].adjlist;
|
|
if assigned(adj) and (adj^.length>max) then
|
|
begin
|
|
p:=i;
|
|
max:=adj^.length;
|
|
end;
|
|
end;
|
|
n:=buf^[p];
|
|
deleteidx(p);
|
|
end;
|
|
|
|
simplifyworklist.add(n);
|
|
freeze_moves(n);
|
|
end;
|
|
|
|
procedure trgobj.assign_colours;
|
|
|
|
{Assign_colours assigns the actual colours to the registers.}
|
|
|
|
var adj : Psuperregisterworklist;
|
|
i,j,k : word;
|
|
n,a,c : Tsuperregister;
|
|
colourednodes : Tsuperregisterset;
|
|
adj_colours:set of 0..255;
|
|
found : boolean;
|
|
|
|
begin
|
|
spillednodes.clear;
|
|
{Reset colours}
|
|
for n:=0 to maxreg-1 do
|
|
reginfo[n].colour:=n;
|
|
{Colour the cpu registers...}
|
|
supregset_reset(colourednodes,false,maxreg);
|
|
for n:=0 to first_imaginary-1 do
|
|
supregset_include(colourednodes,n);
|
|
{Now colour the imaginary registers on the select-stack.}
|
|
for i:=selectstack.length downto 1 do
|
|
begin
|
|
n:=selectstack.buf^[i-1];
|
|
{Create a list of colours that we cannot assign to n.}
|
|
adj_colours:=[];
|
|
adj:=reginfo[n].adjlist;
|
|
if adj<>nil then
|
|
for j:=0 to adj^.length-1 do
|
|
begin
|
|
a:=get_alias(adj^.buf^[j]);
|
|
if supregset_in(colourednodes,a) and (reginfo[a].colour<=255) then
|
|
include(adj_colours,reginfo[a].colour);
|
|
end;
|
|
if regtype=R_INTREGISTER then
|
|
include(adj_colours,RS_STACK_POINTER_REG);
|
|
{Assume a spill by default...}
|
|
found:=false;
|
|
{Search for a colour not in this list.}
|
|
for k:=0 to usable_registers_cnt-1 do
|
|
begin
|
|
c:=usable_registers[k];
|
|
if not(c in adj_colours) then
|
|
begin
|
|
reginfo[n].colour:=c;
|
|
found:=true;
|
|
supregset_include(colourednodes,n);
|
|
include(used_in_proc,c);
|
|
break;
|
|
end;
|
|
end;
|
|
if not found then
|
|
spillednodes.add(n);
|
|
end;
|
|
{Finally colour the nodes that were coalesced.}
|
|
for i:=1 to coalescednodes.length do
|
|
begin
|
|
n:=coalescednodes.buf^[i-1];
|
|
k:=get_alias(n);
|
|
reginfo[n].colour:=reginfo[k].colour;
|
|
if reginfo[k].colour<maxcpuregister then
|
|
include(used_in_proc,reginfo[k].colour);
|
|
end;
|
|
end;
|
|
|
|
procedure trgobj.colour_registers;
|
|
|
|
begin
|
|
repeat
|
|
if simplifyworklist.length<>0 then
|
|
simplify
|
|
else if not(worklist_moves.empty) then
|
|
coalesce
|
|
else if freezeworklist.length<>0 then
|
|
freeze
|
|
else if spillworklist.length<>0 then
|
|
select_spill;
|
|
until (simplifyworklist.length=0) and
|
|
worklist_moves.empty and
|
|
(freezeworklist.length=0) and
|
|
(spillworklist.length=0);
|
|
assign_colours;
|
|
end;
|
|
|
|
procedure trgobj.epilogue_colouring;
|
|
var
|
|
i : Tsuperregister;
|
|
begin
|
|
worklist_moves.clear;
|
|
active_moves.destroy;
|
|
active_moves:=nil;
|
|
frozen_moves.destroy;
|
|
frozen_moves:=nil;
|
|
coalesced_moves.destroy;
|
|
coalesced_moves:=nil;
|
|
constrained_moves.destroy;
|
|
constrained_moves:=nil;
|
|
for i:=0 to maxreg-1 do
|
|
with reginfo[i] do
|
|
if movelist<>nil then
|
|
begin
|
|
dispose(movelist);
|
|
movelist:=nil;
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure trgobj.clear_interferences(u:Tsuperregister);
|
|
|
|
{Remove node u from the interference graph and remove all collected
|
|
move instructions it is associated with.}
|
|
|
|
var i : word;
|
|
v : Tsuperregister;
|
|
adj,adj2 : Psuperregisterworklist;
|
|
|
|
begin
|
|
adj:=reginfo[u].adjlist;
|
|
if adj<>nil then
|
|
begin
|
|
for i:=1 to adj^.length do
|
|
begin
|
|
v:=adj^.buf^[i-1];
|
|
{Remove (u,v) and (v,u) from bitmap.}
|
|
ibitmap[u,v]:=false;
|
|
ibitmap[v,u]:=false;
|
|
{Remove (v,u) from adjacency list.}
|
|
adj2:=reginfo[v].adjlist;
|
|
if adj2<>nil then
|
|
begin
|
|
adj2^.delete(u);
|
|
if adj2^.length=0 then
|
|
begin
|
|
dispose(adj2,done);
|
|
reginfo[v].adjlist:=nil;
|
|
end;
|
|
end;
|
|
end;
|
|
{Remove ( u,* ) from adjacency list.}
|
|
dispose(adj,done);
|
|
reginfo[u].adjlist:=nil;
|
|
end;
|
|
end;
|
|
|
|
|
|
function trgobj.getregisterinline(list:Taasmoutput;subreg:Tsubregister):Tregister;
|
|
var
|
|
p : Tsuperregister;
|
|
begin
|
|
p:=getnewreg(subreg);
|
|
live_registers.add(p);
|
|
result:=newreg(regtype,p,subreg);
|
|
add_edges_used(p);
|
|
add_constraints(result);
|
|
end;
|
|
|
|
|
|
procedure trgobj.ungetregisterinline(list:Taasmoutput;r:Tregister);
|
|
var
|
|
supreg:Tsuperregister;
|
|
begin
|
|
supreg:=getsupreg(r);
|
|
live_registers.delete(supreg);
|
|
insert_regalloc_info(list,supreg);
|
|
end;
|
|
|
|
|
|
procedure trgobj.insert_regalloc_info(list:Taasmoutput;u:tsuperregister);
|
|
var
|
|
p : tai;
|
|
r : tregister;
|
|
palloc,
|
|
pdealloc : tai_regalloc;
|
|
begin
|
|
{ Insert regallocs for all imaginary registers }
|
|
with reginfo[u] do
|
|
begin
|
|
r:=newreg(regtype,u,subreg);
|
|
if assigned(live_start) then
|
|
begin
|
|
{ Generate regalloc and bind it to an instruction, this
|
|
is needed to find all live registers belonging to an
|
|
instruction during the spilling }
|
|
if live_start.typ=ait_instruction then
|
|
palloc:=tai_regalloc.alloc(r,live_start)
|
|
else
|
|
palloc:=tai_regalloc.alloc(r,nil);
|
|
if live_end.typ=ait_instruction then
|
|
pdealloc:=tai_regalloc.dealloc(r,live_end)
|
|
else
|
|
pdealloc:=tai_regalloc.dealloc(r,nil);
|
|
{ Insert live start allocation before the instruction/reg_a_sync }
|
|
list.insertbefore(palloc,live_start);
|
|
{ Insert live end deallocation before reg allocations
|
|
to reduce conflicts }
|
|
p:=live_end;
|
|
while assigned(p) and
|
|
assigned(p.previous) and
|
|
(tai(p.previous).typ=ait_regalloc) and
|
|
(tai_regalloc(p.previous).ratype=ra_alloc) and
|
|
(tai_regalloc(p.previous).reg<>r) do
|
|
p:=tai(p.previous);
|
|
{ , but add release after a reg_a_sync }
|
|
if assigned(p) and
|
|
(p.typ=ait_regalloc) and
|
|
(tai_regalloc(p).ratype=ra_sync) then
|
|
p:=tai(p.next);
|
|
if assigned(p) then
|
|
list.insertbefore(pdealloc,p)
|
|
else
|
|
list.concat(pdealloc);
|
|
end
|
|
{$ifdef EXTDEBUG}
|
|
else
|
|
Comment(V_Warning,'Register '+std_regname(r)+' not used');
|
|
{$endif EXTDEBUG}
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure trgobj.insert_regalloc_info_all(list:Taasmoutput);
|
|
var
|
|
supreg : tsuperregister;
|
|
begin
|
|
{ Insert regallocs for all imaginary registers }
|
|
for supreg:=first_imaginary to maxreg-1 do
|
|
insert_regalloc_info(list,supreg);
|
|
end;
|
|
|
|
|
|
procedure trgobj.add_cpu_interferences(p : tai);
|
|
begin
|
|
end;
|
|
|
|
|
|
procedure trgobj.generate_interference_graph(list:Taasmoutput;headertai:tai);
|
|
var
|
|
p : tai;
|
|
{$ifdef EXTDEBUG}
|
|
i : integer;
|
|
{$endif EXTDEBUG}
|
|
supreg : tsuperregister;
|
|
begin
|
|
{ All allocations are available. Now we can generate the
|
|
interference graph. Walk through all instructions, we can
|
|
start with the headertai, because before the header tai is
|
|
only symbols. }
|
|
live_registers.clear;
|
|
p:=headertai;
|
|
while assigned(p) do
|
|
begin
|
|
if p.typ=ait_regalloc then
|
|
with Tai_regalloc(p) do
|
|
begin
|
|
if (getregtype(reg)=regtype) then
|
|
begin
|
|
supreg:=getsupreg(reg);
|
|
case ratype of
|
|
ra_alloc :
|
|
begin
|
|
live_registers.add(supreg);
|
|
add_edges_used(supreg);
|
|
end;
|
|
ra_dealloc :
|
|
begin
|
|
live_registers.delete(supreg);
|
|
add_edges_used(supreg);
|
|
end;
|
|
end;
|
|
{ constraints needs always to be updated }
|
|
add_constraints(reg);
|
|
end;
|
|
end;
|
|
add_cpu_interferences(p);
|
|
p:=Tai(p.next);
|
|
end;
|
|
|
|
{$ifdef EXTDEBUG}
|
|
if live_registers.length>0 then
|
|
begin
|
|
for i:=0 to live_registers.length-1 do
|
|
begin
|
|
{ Only report for imaginary registers }
|
|
if live_registers.buf^[i]>=first_imaginary then
|
|
Comment(V_Warning,'Register '+std_regname(newreg(R_INTREGISTER,live_registers.buf^[i],defaultsub))+' not released');
|
|
end;
|
|
end;
|
|
{$endif}
|
|
end;
|
|
|
|
|
|
procedure Trgobj.translate_registers(list:taasmoutput);
|
|
var
|
|
hp,p,q:Tai;
|
|
i:shortint;
|
|
{$ifdef arm}
|
|
so:pshifterop;
|
|
{$endif arm}
|
|
|
|
|
|
begin
|
|
{ Leave when no imaginary registers are used }
|
|
if maxreg<=first_imaginary then
|
|
exit;
|
|
p:=Tai(list.first);
|
|
while assigned(p) do
|
|
begin
|
|
case p.typ of
|
|
ait_regalloc:
|
|
with Tai_regalloc(p) do
|
|
begin
|
|
if (getregtype(reg)=regtype) then
|
|
begin
|
|
{ Only alloc/dealloc is needed for the optimizer, remove
|
|
other regalloc }
|
|
if not(ratype in [ra_alloc,ra_dealloc]) then
|
|
begin
|
|
q:=Tai(next);
|
|
list.remove(p);
|
|
p.free;
|
|
p:=q;
|
|
continue;
|
|
end
|
|
else
|
|
begin
|
|
setsupreg(reg,reginfo[getsupreg(reg)].colour);
|
|
{
|
|
Remove sequences of release and
|
|
allocation of the same register like. Other combinations
|
|
of release/allocate need to stay in the list.
|
|
|
|
# Register X released
|
|
# Register X allocated
|
|
}
|
|
if assigned(previous) and
|
|
(ratype=ra_alloc) and
|
|
(Tai(previous).typ=ait_regalloc) and
|
|
(Tai_regalloc(previous).reg=reg) and
|
|
(Tai_regalloc(previous).ratype=ra_dealloc) then
|
|
begin
|
|
q:=Tai(next);
|
|
hp:=tai(previous);
|
|
list.remove(hp);
|
|
hp.free;
|
|
list.remove(p);
|
|
p.free;
|
|
p:=q;
|
|
continue;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
ait_instruction:
|
|
with Taicpu(p) do
|
|
begin
|
|
aktfilepos:=fileinfo;
|
|
for i:=0 to ops-1 do
|
|
with oper[i]^ do
|
|
case typ of
|
|
Top_reg:
|
|
if (getregtype(reg)=regtype) then
|
|
setsupreg(reg,reginfo[getsupreg(reg)].colour);
|
|
Top_ref:
|
|
begin
|
|
if regtype=R_INTREGISTER then
|
|
with ref^ do
|
|
begin
|
|
if base<>NR_NO then
|
|
setsupreg(base,reginfo[getsupreg(base)].colour);
|
|
if index<>NR_NO then
|
|
setsupreg(index,reginfo[getsupreg(index)].colour);
|
|
end;
|
|
end;
|
|
{$ifdef arm}
|
|
Top_shifterop:
|
|
begin
|
|
if regtype=R_INTREGISTER then
|
|
begin
|
|
so:=shifterop;
|
|
if so^.rs<>NR_NO then
|
|
setsupreg(so^.rs,reginfo[getsupreg(so^.rs)].colour);
|
|
end;
|
|
end;
|
|
{$endif arm}
|
|
end;
|
|
|
|
{ Maybe the operation can be removed when
|
|
it is a move and both arguments are the same }
|
|
if is_same_reg_move(regtype) then
|
|
begin
|
|
q:=Tai(p.next);
|
|
list.remove(p);
|
|
p.free;
|
|
p:=q;
|
|
continue;
|
|
end;
|
|
end;
|
|
end;
|
|
p:=Tai(p.next);
|
|
end;
|
|
aktfilepos:=current_procinfo.exitpos;
|
|
end;
|
|
|
|
|
|
function trgobj.spill_registers(list:Taasmoutput;headertai:tai):boolean;
|
|
{ Returns true if any help registers have been used }
|
|
var
|
|
i : word;
|
|
t : tsuperregister;
|
|
p,q : Tai;
|
|
regs_to_spill_set:Tsuperregisterset;
|
|
spill_temps : ^Tspill_temp_list;
|
|
supreg : tsuperregister;
|
|
templist : taasmoutput;
|
|
begin
|
|
spill_registers:=false;
|
|
live_registers.clear;
|
|
for i:=first_imaginary to maxreg-1 do
|
|
exclude(reginfo[i].flags,ri_selected);
|
|
spill_temps:=allocmem(sizeof(treference)*maxreg);
|
|
supregset_reset(regs_to_spill_set,false,$ffff);
|
|
{ Allocate temps and insert in front of the list }
|
|
templist:=taasmoutput.create;
|
|
{Safe: this procedure is only called if there are spilled nodes.}
|
|
with spillednodes do
|
|
for i:=0 to length-1 do
|
|
begin
|
|
t:=buf^[i];
|
|
{Alternative representation.}
|
|
supregset_include(regs_to_spill_set,t);
|
|
{Clear all interferences of the spilled register.}
|
|
clear_interferences(t);
|
|
{Get a temp for the spilled register, the size must at least equal a complete register,
|
|
take also care of the fact that subreg can be larger than a single register like doubles
|
|
that occupy 2 registers }
|
|
tg.gettemp(templist,
|
|
max(tcgsize2size[reg_cgsize(newreg(regtype,t,R_SUBWHOLE))],
|
|
tcgsize2size[reg_cgsize(newreg(regtype,t,reginfo[t].subreg))]),
|
|
tt_noreuse,spill_temps^[t]);
|
|
end;
|
|
list.insertlistafter(headertai,templist);
|
|
templist.free;
|
|
{ Walk through all instructions, we can start with the headertai,
|
|
because before the header tai is only symbols }
|
|
p:=headertai;
|
|
while assigned(p) do
|
|
begin
|
|
case p.typ of
|
|
ait_regalloc:
|
|
with Tai_regalloc(p) do
|
|
begin
|
|
if (getregtype(reg)=regtype) then
|
|
begin
|
|
{A register allocation of a spilled register can be removed.}
|
|
supreg:=getsupreg(reg);
|
|
if supregset_in(regs_to_spill_set,supreg) then
|
|
begin
|
|
q:=Tai(p.next);
|
|
list.remove(p);
|
|
p.free;
|
|
p:=q;
|
|
continue;
|
|
end
|
|
else
|
|
begin
|
|
case ratype of
|
|
ra_alloc :
|
|
live_registers.add(supreg);
|
|
ra_dealloc :
|
|
live_registers.delete(supreg);
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
ait_instruction:
|
|
with Taicpu(p) do
|
|
begin
|
|
aktfilepos:=fileinfo;
|
|
if instr_spill_register(list,taicpu(p),regs_to_spill_set,spill_temps^) then
|
|
spill_registers:=true;
|
|
end;
|
|
end;
|
|
p:=Tai(p.next);
|
|
end;
|
|
aktfilepos:=current_procinfo.exitpos;
|
|
{Safe: this procedure is only called if there are spilled nodes.}
|
|
with spillednodes do
|
|
for i:=0 to length-1 do
|
|
tg.ungettemp(list,spill_temps^[buf^[i]]);
|
|
freemem(spill_temps);
|
|
end;
|
|
|
|
|
|
function trgobj.do_spill_replace(list:Taasmoutput;instr:taicpu;orgreg:tsuperregister;const spilltemp:treference):boolean;
|
|
begin
|
|
result:=false;
|
|
end;
|
|
|
|
|
|
procedure Trgobj.do_spill_read(list:Taasmoutput;pos:tai;const spilltemp:treference;tempreg:tregister);
|
|
begin
|
|
list.insertafter(spilling_create_load(spilltemp,tempreg),pos);
|
|
end;
|
|
|
|
|
|
procedure Trgobj.do_spill_written(list:Taasmoutput;pos:tai;const spilltemp:treference;tempreg:tregister);
|
|
begin
|
|
list.insertafter(spilling_create_store(tempreg,spilltemp),pos);
|
|
end;
|
|
|
|
|
|
function trgobj.get_spill_subreg(r : tregister) : tsubregister;
|
|
begin
|
|
result:=defaultsub;
|
|
end;
|
|
|
|
|
|
function trgobj.instr_spill_register(list:Taasmoutput;
|
|
instr:taicpu;
|
|
const r:Tsuperregisterset;
|
|
const spilltemplist:Tspill_temp_list): boolean;
|
|
var
|
|
counter, regindex: longint;
|
|
regs: tspillregsinfo;
|
|
spilled: boolean;
|
|
|
|
procedure addreginfo(reg: tregister; operation: topertype);
|
|
var
|
|
i, tmpindex: longint;
|
|
supreg : tsuperregister;
|
|
begin
|
|
tmpindex := regindex;
|
|
supreg:=getsupreg(reg);
|
|
{ did we already encounter this register? }
|
|
for i := 0 to pred(regindex) do
|
|
if (regs[i].orgreg = supreg) then
|
|
begin
|
|
tmpindex := i;
|
|
break;
|
|
end;
|
|
if tmpindex > high(regs) then
|
|
internalerror(2003120301);
|
|
regs[tmpindex].orgreg := supreg;
|
|
regs[tmpindex].spillreg:=reg;
|
|
if supregset_in(r,supreg) then
|
|
begin
|
|
{ add/update info on this register }
|
|
regs[tmpindex].mustbespilled := true;
|
|
case operation of
|
|
operand_read:
|
|
regs[tmpindex].regread := true;
|
|
operand_write:
|
|
regs[tmpindex].regwritten := true;
|
|
operand_readwrite:
|
|
begin
|
|
regs[tmpindex].regread := true;
|
|
regs[tmpindex].regwritten := true;
|
|
end;
|
|
end;
|
|
spilled := true;
|
|
end;
|
|
inc(regindex,ord(regindex=tmpindex));
|
|
end;
|
|
|
|
|
|
procedure tryreplacereg(var reg: tregister);
|
|
var
|
|
i: longint;
|
|
supreg: tsuperregister;
|
|
begin
|
|
supreg:=getsupreg(reg);
|
|
for i:=0 to pred(regindex) do
|
|
if (regs[i].mustbespilled) and
|
|
(regs[i].orgreg=supreg) then
|
|
begin
|
|
{ Only replace supreg }
|
|
setsupreg(reg,getsupreg(regs[i].tempreg));
|
|
break;
|
|
end;
|
|
end;
|
|
|
|
var
|
|
loadpos,
|
|
storepos : tai;
|
|
oldlive_registers : tsuperregisterworklist;
|
|
begin
|
|
result := false;
|
|
fillchar(regs,sizeof(regs),0);
|
|
for counter := low(regs) to high(regs) do
|
|
regs[counter].orgreg := RS_INVALID;
|
|
spilled := false;
|
|
regindex := 0;
|
|
|
|
{ check whether and if so which and how (read/written) this instructions contains
|
|
registers that must be spilled }
|
|
for counter := 0 to instr.ops-1 do
|
|
with instr.oper[counter]^ do
|
|
begin
|
|
case typ of
|
|
top_reg:
|
|
begin
|
|
if (getregtype(reg) = regtype) then
|
|
addreginfo(reg,instr.spilling_get_operation_type(counter));
|
|
end;
|
|
top_ref:
|
|
begin
|
|
if regtype in [R_INTREGISTER,R_ADDRESSREGISTER] then
|
|
with ref^ do
|
|
begin
|
|
if (base <> NR_NO) then
|
|
addreginfo(base,instr.spilling_get_operation_type_ref(counter,base));
|
|
if (index <> NR_NO) then
|
|
addreginfo(index,instr.spilling_get_operation_type_ref(counter,index));
|
|
end;
|
|
end;
|
|
{$ifdef ARM}
|
|
top_shifterop:
|
|
begin
|
|
if regtype in [R_INTREGISTER,R_ADDRESSREGISTER] then
|
|
if shifterop^.rs<>NR_NO then
|
|
addreginfo(shifterop^.rs,operand_read);
|
|
end;
|
|
{$endif ARM}
|
|
end;
|
|
end;
|
|
|
|
{ if no spilling for this instruction we can leave }
|
|
if not spilled then
|
|
exit;
|
|
|
|
{$ifdef x86}
|
|
{ Try replacing the register with the spilltemp. This is usefull only
|
|
for the i386,x86_64 that support memory locations for several instructions }
|
|
for counter := 0 to pred(regindex) do
|
|
with regs[counter] do
|
|
begin
|
|
if mustbespilled then
|
|
begin
|
|
if do_spill_replace(list,instr,orgreg,spilltemplist[orgreg]) then
|
|
mustbespilled:=false;
|
|
end;
|
|
end;
|
|
{$endif x86}
|
|
|
|
{
|
|
There are registers that need are spilled. We generate the
|
|
following code for it. The used positions where code need
|
|
to be inserted are marked using #. Note that code is always inserted
|
|
before the positions using pos.previous. This way the position is always
|
|
the same since pos doesn't change, but pos.previous is modified everytime
|
|
new code is inserted.
|
|
|
|
[
|
|
- reg_allocs load spills
|
|
- load spills
|
|
]
|
|
[#loadpos
|
|
- reg_deallocs
|
|
- reg_allocs
|
|
]
|
|
[
|
|
- reg_deallocs for load-only spills
|
|
- reg_allocs for store-only spills
|
|
]
|
|
[#instr
|
|
- original instruction
|
|
]
|
|
[
|
|
- store spills
|
|
- reg_deallocs store spills
|
|
]
|
|
[#storepos
|
|
]
|
|
}
|
|
|
|
result := true;
|
|
oldlive_registers.copyfrom(live_registers);
|
|
|
|
{ Process all tai_regallocs belonging to this instruction, ignore explicit
|
|
inserted regallocs. These can happend for example in i386:
|
|
mov ref,ireg26
|
|
<regdealloc ireg26, instr=taicpu of lea>
|
|
<regalloc edi, insrt=nil>
|
|
lea [ireg26+ireg17],edi
|
|
All released registers are also added to the live_registers because
|
|
they can't be used during the spilling }
|
|
loadpos:=tai(instr.previous);
|
|
while assigned(loadpos) and
|
|
(loadpos.typ=ait_regalloc) and
|
|
((tai_regalloc(loadpos).instr=nil) or
|
|
(tai_regalloc(loadpos).instr=instr)) do
|
|
begin
|
|
if tai_regalloc(loadpos).ratype=ra_dealloc then
|
|
live_registers.add(getsupreg(tai_regalloc(loadpos).reg));
|
|
loadpos:=tai(loadpos.previous);
|
|
end;
|
|
loadpos:=tai(loadpos.next);
|
|
|
|
{ Load the spilled registers }
|
|
for counter := 0 to pred(regindex) do
|
|
with regs[counter] do
|
|
begin
|
|
if mustbespilled and regread then
|
|
begin
|
|
tempreg:=getregisterinline(list,get_spill_subreg(regs[counter].spillreg));
|
|
do_spill_read(list,tai(loadpos.previous),spilltemplist[orgreg],tempreg);
|
|
end;
|
|
end;
|
|
|
|
{ Release temp registers of read-only registers, and add reference of the instruction
|
|
to the reginfo }
|
|
for counter := 0 to pred(regindex) do
|
|
with regs[counter] do
|
|
begin
|
|
if mustbespilled and regread and (not regwritten) then
|
|
begin
|
|
{ The original instruction will be the next that uses this register }
|
|
add_reg_instruction(instr,tempreg);
|
|
ungetregisterinline(list,tempreg);
|
|
end;
|
|
end;
|
|
|
|
{ Allocate temp registers of write-only registers, and add reference of the instruction
|
|
to the reginfo }
|
|
for counter := 0 to pred(regindex) do
|
|
with regs[counter] do
|
|
begin
|
|
if mustbespilled and regwritten then
|
|
begin
|
|
{ When the register is also loaded there is already a register assigned }
|
|
if (not regread) then
|
|
tempreg:=getregisterinline(list,get_spill_subreg(regs[counter].spillreg));
|
|
{ The original instruction will be the next that uses this register, this
|
|
also needs to be done for read-write registers }
|
|
add_reg_instruction(instr,tempreg);
|
|
end;
|
|
end;
|
|
|
|
{ store the spilled registers }
|
|
storepos:=tai(instr.next);
|
|
for counter := 0 to pred(regindex) do
|
|
with regs[counter] do
|
|
begin
|
|
if mustbespilled and regwritten then
|
|
begin
|
|
do_spill_written(list,tai(storepos.previous),spilltemplist[orgreg],tempreg);
|
|
ungetregisterinline(list,tempreg);
|
|
end;
|
|
end;
|
|
|
|
{ now all spilling code is generated we can restore the live registers. This
|
|
must be done after the store because the store can need an extra register
|
|
that also needs to conflict with the registers of the instruction }
|
|
live_registers.done;
|
|
live_registers:=oldlive_registers;
|
|
|
|
{ substitute registers }
|
|
for counter:=0 to instr.ops-1 do
|
|
with instr.oper[counter]^ do
|
|
begin
|
|
case typ of
|
|
top_reg:
|
|
begin
|
|
if (getregtype(reg) = regtype) then
|
|
tryreplacereg(reg);
|
|
end;
|
|
top_ref:
|
|
begin
|
|
if regtype in [R_INTREGISTER,R_ADDRESSREGISTER] then
|
|
begin
|
|
tryreplacereg(ref^.base);
|
|
tryreplacereg(ref^.index);
|
|
end;
|
|
end;
|
|
{$ifdef ARM}
|
|
top_shifterop:
|
|
begin
|
|
if regtype in [R_INTREGISTER,R_ADDRESSREGISTER] then
|
|
tryreplacereg(shifterop^.rs);
|
|
end;
|
|
{$endif ARM}
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
end.
|
|
{
|
|
$Log$
|
|
Revision 1.156 2005-03-25 21:55:43 jonas
|
|
* removed some unused variables
|
|
|
|
Revision 1.155 2005/03/20 19:47:46 peter
|
|
* fix spilling code when explicit cpu registers are used in an
|
|
instruction
|
|
|
|
Revision 1.154 2005/02/18 23:37:51 jonas
|
|
* fixed spilling for several ppc instructions which only read registers
|
|
+ added support for registers in references that get changed (load/store
|
|
with update)
|
|
|
|
Revision 1.153 2005/02/14 17:13:07 peter
|
|
* truncate log
|
|
|
|
}
|