[lra] force reg update after spilling to memory [PR120424]

In the added C++ testcase, a stack slot at a negative sp offset is
used to hold a value across a call.

There are a couple of causes that directly lead to this outcome:

- the -fstack-clash-protection and -fnon-call-exception options, that
cause arm_frame_pointer_required to flip from false to true when the
first pseudo gets spilled to memory;

- when the affected pseudo is spilled to memory, we fail to update lra
regno info, because the insns that reference it are already on the
lra_constraint_insn_stack;

There is another potentially-related issue:

- when we notice that the frame pointer can no longer be eliminated to
the stack pointer, we immediately clear can_eliminate, and also
prev_can_eliminate, but update_reg_eliminate relied on the latter to
tell that it needs to propagate a previous_offset to the
newly-selected elimination, or restore the original offsets.

This patch ensures that we update insn register info after spilling a
pseudo to memory, and enables update_reg_eliminate to recognize the
case in which a previously-preferred elimination is disabled
regardless of prev_can_eliminate.


for  gcc/ChangeLog

	PR rtl-optimization/120424
	PR middle-end/118939
	* lra-spills.cc (spill_pseudos): Update insn regno info.
	* lra-eliminations.cc (update_reg_eliminate): Recognize
	disabling of active elimination regardless of
	prev_can_eliminate.

for  gcc/testsuite/ChangeLog

	PR rtl-optimization/120424
	PR middle-end/118939
	* g++.target/arm/pr120424.C: New.
	* gnat.dg/controlled9.adb: New.
	* gnat.dg/controlled9_pkg.ads: New.
This commit is contained in:
Alexandre Oliva
2025-06-02 20:21:45 -03:00
committed by Alexandre Oliva
parent 8fc9e03a70
commit 2edb50a310
5 changed files with 51 additions and 2 deletions

View File

@@ -1185,7 +1185,7 @@ update_reg_eliminate (bitmap insns_with_changed_offsets)
setup_can_eliminate (ep, false);
continue;
}
if (ep->can_eliminate != prev && elimination_map[ep->from] == ep)
if (!ep->can_eliminate && elimination_map[ep->from] == ep)
{
/* We cannot use this elimination anymore -- find another
one. */

View File

@@ -556,7 +556,7 @@ spill_pseudos (void)
fprintf (lra_dump_file,
"Changing spilled pseudos to memory in insn #%u\n",
INSN_UID (insn));
lra_push_insn (insn);
lra_push_insn_and_update_insn_regno_info (insn);
if (lra_reg_spill_p || targetm.different_addr_displacement_p ())
lra_set_used_insn_alternative (insn, LRA_UNKNOWN_ALT);
}

View File

@@ -0,0 +1,34 @@
/* { dg-do compile } */
/* { dg-options "-march=armv7 -O2 -fstack-clash-protection -fnon-call-exceptions" } */
/* { dg-final { scan-assembler-not {#-8} } } */
/* LRA register elimination gets confused when register spilling
causes arm_frame_pointer_required to switch from false to true, and
ends up using a stack slot below sp. */
void f() {
int i = 0, j = 0;
asm ("" : : "m" (i), "m" (j));
}
void g(void (*fn[])(), int i)
{
auto fn0 = fn[i+0];
auto fn1 = fn[i+1];
auto fn2 = fn[i+2];
auto fn3 = fn[i+3];
fn0();
fn1();
if (!fn2)
throw i+2;
fn2();
fn3();
fn0();
fn1();
}
int
main()
{
void (*fn[4])() = { f, f, f, f };
g (fn, 0);
}

View File

@@ -0,0 +1,10 @@
-- { dg-do run }
-- { dg-options "-O1 -fstack-check" }
-- from PR middle-end/118939
with Controlled9_Pkg;
procedure Controlled9 is
S : constant Controlled9_Pkg.T_Access := new Controlled9_Pkg.T;
begin
null;
end Controlled9;

View File

@@ -0,0 +1,5 @@
with Ada.Finalization;
package Controlled9_Pkg is
type T is new Ada.Finalization.Controlled with null record;
type T_Access is access all T;
end Controlled9_Pkg;