* 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=(
ri_coalesced, { the register is coalesced with other register }
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) }
);
Treginfoflagset=set of Treginfoflag;
@ -1621,9 +1621,9 @@ unit rgobj;
to get too much conflicts with the result that the spilling code
will never converge (PFV)
We need a special processing for nodes with the ri_spill_read flag set.
These nodes contain a value loaded from a previously spilled node.
We need to avoid another spilling of ri_spill_read nodes, since it will
We need a special processing for nodes with the ri_spill_helper flag set.
These nodes contain a value of a previously spilled node.
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.
}
maxlength:=0;
@ -1632,9 +1632,9 @@ unit rgobj;
with spillworklist do
begin
{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
if not(ri_spill_read in reginfo[buf^[i]].flags) then
if not(ri_spill_helper in reginfo[buf^[i]].flags) then
begin
adj:=reginfo[buf^[i]].adjlist;
if assigned(adj) and
@ -1651,10 +1651,10 @@ unit rgobj;
if p=high(p) then
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
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
a color assigned.
}
@ -1688,63 +1688,115 @@ unit rgobj;
{Assign_colours assigns the actual colours to the registers.}
var adj : Psuperregisterworklist;
i,j,k : cardinal;
n,a,c : Tsuperregister;
colourednodes : Tsuperregisterset;
var
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;
found : boolean;
a,c : Tsuperregister;
{$if declared(RS_STACK_POINTER_REG) and (RS_STACK_POINTER_REG<>RS_INVALID)}
tmpr: tregister;
{$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
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);
reset_colours;
{Now colour the imaginary registers on the select-stack.}
spill_loop:=false;
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;
{ 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
if not colour_regitser(n) and
(ri_spill_helper in reginfo[n].flags) then
begin
c:=usable_registers[k];
if not(c in adj_colours) then
begin
reginfo[n].colour:=c;
found:=true;
supregset_include(colourednodes,n);
break;
end;
{ Register n is a helper register which holds the value
of a previously spilled register. Register n must never
be spilled. Report the spilling loop and break. }
spill_loop:=true;
break;
end;
if not found then
spillednodes.add(n);
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.}
for i:=1 to coalescednodes.length do
begin
@ -2833,7 +2885,7 @@ unit rgobj;
begin
loadreg:=getregisterinline(list,regs.reginfo[counter].spillregconstraints);
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;
@ -2871,6 +2923,7 @@ unit rgobj;
ssa_safe then
begin
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
don't have ensure ssa -> initialise loadreg even if
there are no reads }