* Prevent spilling of spill-helper registers which contain the value of a

previously spilled register. These helper registers must never be spilled.
  This fixes failures of the register allocator in rare corner cases.
This commit is contained in:
Yuriy Sydorov 2021-09-10 18:06:12 +03:00
parent 37bb10e893
commit b4df9dbe1d

View File

@ -95,7 +95,7 @@ unit rgobj;
Treginfoflag=( Treginfoflag=(
ri_coalesced, { the register is coalesced with other register } ri_coalesced, { the register is coalesced with other register }
ri_selected, { the register is put to selectstack } ri_selected, { the register is put to selectstack }
ri_spill_read, { the register contains a value loaded from a spilled register } ri_spill_helper, { the register contains a value of a previously spilled register }
ri_has_initial_loc { the register has the initial memory location (e.g. a parameter in the stack) } ri_has_initial_loc { the register has the initial memory location (e.g. a parameter in the stack) }
); );
Treginfoflagset=set of Treginfoflag; Treginfoflagset=set of Treginfoflag;
@ -1621,9 +1621,9 @@ unit rgobj;
to get too much conflicts with the result that the spilling code to get too much conflicts with the result that the spilling code
will never converge (PFV) will never converge (PFV)
We need a special processing for nodes with the ri_spill_read flag set. We need a special processing for nodes with the ri_spill_helper flag set.
These nodes contain a value loaded from a previously spilled node. These nodes contain a value of a previously spilled node.
We need to avoid another spilling of ri_spill_read nodes, since it will We need to avoid another spilling of ri_spill_helper nodes, since it will
likely lead to an endless loop and the register allocation will fail. likely lead to an endless loop and the register allocation will fail.
} }
maxlength:=0; maxlength:=0;
@ -1632,9 +1632,9 @@ unit rgobj;
with spillworklist do with spillworklist do
begin begin
{Safe: This procedure is only called if length<>0} {Safe: This procedure is only called if length<>0}
{ Search for a candidate to be spilled, ignoring nodes with the ri_spill_read flag set. } { Search for a candidate to be spilled, ignoring nodes with the ri_spill_helper flag set. }
for i:=0 to length-1 do for i:=0 to length-1 do
if not(ri_spill_read in reginfo[buf^[i]].flags) then if not(ri_spill_helper in reginfo[buf^[i]].flags) then
begin begin
adj:=reginfo[buf^[i]].adjlist; adj:=reginfo[buf^[i]].adjlist;
if assigned(adj) and if assigned(adj) and
@ -1651,10 +1651,10 @@ unit rgobj;
if p=high(p) then if p=high(p) then
begin begin
{ If no normal nodes found, then only ri_spill_read nodes are present { If no normal nodes found, then only ri_spill_helper nodes are present
in the list. Finding the node with the least interferences and in the list. Finding the node with the least interferences and
the least weight. the least weight.
This allows us to put the most restricted ri_spill_read nodes This allows us to put the most restricted ri_spill_helper nodes
to the top of selectstack so they will be the first to get to the top of selectstack so they will be the first to get
a color assigned. a color assigned.
} }
@ -1688,63 +1688,115 @@ unit rgobj;
{Assign_colours assigns the actual colours to the registers.} {Assign_colours assigns the actual colours to the registers.}
var adj : Psuperregisterworklist; var
i,j,k : cardinal; colourednodes : Tsuperregisterset;
n,a,c : Tsuperregister;
colourednodes : Tsuperregisterset; procedure reset_colours;
var
n : Tsuperregister;
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);
end;
function colour_regitser(n : Tsuperregister) : boolean;
var
j,k : cardinal;
adj : Psuperregisterworklist;
adj_colours:set of 0..255; adj_colours:set of 0..255;
found : boolean; a,c : Tsuperregister;
{$if declared(RS_STACK_POINTER_REG) and (RS_STACK_POINTER_REG<>RS_INVALID)} {$if declared(RS_STACK_POINTER_REG) and (RS_STACK_POINTER_REG<>RS_INVALID)}
tmpr: tregister; tmpr: tregister;
{$endif} {$endif}
begin
{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;
{ e.g. AVR does not have a stack pointer register }
{$if declared(RS_STACK_POINTER_REG) and (RS_STACK_POINTER_REG<>RS_INVALID)}
{ FIXME: temp variable r is needed here to avoid Internal error 20060521 }
{ while compiling the compiler. }
tmpr:=NR_STACK_POINTER_REG;
if (regtype=getregtype(tmpr)) then
include(adj_colours,RS_STACK_POINTER_REG);
{$ifend}
{Assume a spill by default...}
result:=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;
result:=true;
supregset_include(colourednodes,n);
break;
end;
end;
if not result then
spillednodes.add(n);
end;
var
i,k : cardinal;
n : Tsuperregister;
spill_loop : boolean;
begin begin
spillednodes.clear; reset_colours;
{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.} {Now colour the imaginary registers on the select-stack.}
spill_loop:=false;
for i:=selectstack.length downto 1 do for i:=selectstack.length downto 1 do
begin begin
n:=selectstack.buf^[i-1]; n:=selectstack.buf^[i-1];
{Create a list of colours that we cannot assign to n.} if not colour_regitser(n) and
adj_colours:=[]; (ri_spill_helper in reginfo[n].flags) then
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;
{ e.g. AVR does not have a stack pointer register }
{$if declared(RS_STACK_POINTER_REG) and (RS_STACK_POINTER_REG<>RS_INVALID)}
{ FIXME: temp variable r is needed here to avoid Internal error 20060521 }
{ while compiling the compiler. }
tmpr:=NR_STACK_POINTER_REG;
if (regtype=getregtype(tmpr)) then
include(adj_colours,RS_STACK_POINTER_REG);
{$ifend}
{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 begin
c:=usable_registers[k]; { Register n is a helper register which holds the value
if not(c in adj_colours) then of a previously spilled register. Register n must never
begin be spilled. Report the spilling loop and break. }
reginfo[n].colour:=c; spill_loop:=true;
found:=true; break;
supregset_include(colourednodes,n);
break;
end;
end; end;
if not found then
spillednodes.add(n);
end; end;
if spill_loop then
begin
{ Spilling loop is detected when colouring registers using the select-stack order.
Trying to eliminte this by using a different colouring order. }
reset_colours;
{ To prevent spilling of helper registers it is needed to assign colours to them first. }
for i:=selectstack.length downto 1 do
begin
n:=selectstack.buf^[i-1];
if ri_spill_helper in reginfo[n].flags then
if not colour_regitser(n) then
{ Can't colour the spill helper register n.
This can happen only when the code generator produces invalid code. }
internalerror(2021091001);
end;
{ Assign colours for the rest of the registers }
for i:=selectstack.length downto 1 do
begin
n:=selectstack.buf^[i-1];
if not (ri_spill_helper in reginfo[n].flags) then
colour_regitser(n);
end;
end;
{Finally colour the nodes that were coalesced.} {Finally colour the nodes that were coalesced.}
for i:=1 to coalescednodes.length do for i:=1 to coalescednodes.length do
begin begin
@ -2833,7 +2885,7 @@ unit rgobj;
begin begin
loadreg:=getregisterinline(list,regs.reginfo[counter].spillregconstraints); loadreg:=getregisterinline(list,regs.reginfo[counter].spillregconstraints);
do_spill_read(list,tai(loadpos.previous),spilltemplist[orgreg],loadreg,orgreg); do_spill_read(list,tai(loadpos.previous),spilltemplist[orgreg],loadreg,orgreg);
include(reginfo[getsupreg(loadreg)].flags,ri_spill_read); include(reginfo[getsupreg(loadreg)].flags,ri_spill_helper);
end; end;
end; end;
@ -2871,6 +2923,7 @@ unit rgobj;
ssa_safe then ssa_safe then
begin begin
storereg:=getregisterinline(list,regs.reginfo[counter].spillregconstraints); storereg:=getregisterinline(list,regs.reginfo[counter].spillregconstraints);
include(reginfo[getsupreg(storereg)].flags,ri_spill_helper);
{ we also use loadreg for store replacements in case we { we also use loadreg for store replacements in case we
don't have ensure ssa -> initialise loadreg even if don't have ensure ssa -> initialise loadreg even if
there are no reads } there are no reads }