summaryrefslogtreecommitdiff
path: root/compiler/rgobj.pas
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rgobj.pas')
-rw-r--r--compiler/rgobj.pas2022
1 files changed, 2022 insertions, 0 deletions
diff --git a/compiler/rgobj.pas b/compiler/rgobj.pas
new file mode 100644
index 0000000000..05be21b3c2
--- /dev/null
+++ b/compiler/rgobj.pas
@@ -0,0 +1,2022 @@
+{
+ 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
+ begin
+{$ifdef EXTDEBUG}
+ { Only exit here so the .s file is still generated. Assembling
+ the file will still trigger an error }
+ exit;
+{$else}
+ internalerror(200309041);
+{$endif}
+ end;
+ 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
+ { Only add deallocs belonging to the instruction. Explicit inserted deallocs
+ belong to the previous instruction and not the current instruction }
+ if (tai_regalloc(loadpos).instr=instr) and
+ (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.