https://gcc.gnu.org/g:1445b105ff49c01c048cc204c205d39017da7874

commit 1445b105ff49c01c048cc204c205d39017da7874
Author: Alexandre Oliva <ol...@adacore.com>
Date:   Sat May 24 20:15:02 2025 -0300

    [lra] force reg update after spilling to memory

Diff:
---
 gcc/lra-eliminations.cc                 | 41 +++++++++++++++++++++------------
 gcc/lra-spills.cc                       |  2 +-
 gcc/testsuite/g++.target/arm/pr120424.C | 34 +++++++++++++++++++++++++++
 3 files changed, 61 insertions(+), 16 deletions(-)

diff --git a/gcc/lra-eliminations.cc b/gcc/lra-eliminations.cc
index d84a7d1ee99f..6b2c2b8e9b45 100644
--- a/gcc/lra-eliminations.cc
+++ b/gcc/lra-eliminations.cc
@@ -138,20 +138,6 @@ lra_debug_elim_table (void)
   print_elim_table (stderr);
 }
 
-/* Setup possibility of elimination in elimination table element EP to
-   VALUE.  Setup FRAME_POINTER_NEEDED if elimination from frame
-   pointer to stack pointer is not possible anymore.  */
-static void
-setup_can_eliminate (class lra_elim_table *ep, bool value)
-{
-  ep->can_eliminate = ep->prev_can_eliminate = value;
-  if (! value
-      && ep->from == FRAME_POINTER_REGNUM && ep->to == STACK_POINTER_REGNUM)
-    frame_pointer_needed = 1;
-  if (!frame_pointer_needed)
-    REGNO_POINTER_ALIGN (HARD_FRAME_POINTER_REGNUM) = 0;
-}
-
 /* Map: eliminable "from" register -> its current elimination,
    or NULL if none.  The elimination table may contain more than
    one elimination for the same hard register, but this map specifies
@@ -172,6 +158,28 @@ static poly_int64 self_elim_offsets[FIRST_PSEUDO_REGISTER];
    potentially eliminable hard registers are stored in the map.         */
 static rtx eliminable_reg_rtx[FIRST_PSEUDO_REGISTER];
 
+/* Setup possibility of elimination in elimination table element EP to
+   VALUE.  Setup FRAME_POINTER_NEEDED if elimination from frame
+   pointer to stack pointer is not possible anymore.  */
+static void
+setup_can_eliminate (class lra_elim_table *ep, bool value)
+{
+  /* If we're disabling the preferred elimination from the map, make
+     sure we keep prev_can_eliminate set, so that update_reg_eliminate
+     sets previous_offset for the new elimination, or
+     self_elim_offsets if no new elimination is available.  */
+  if (!value && elimination_map[ep->from] == ep)
+    ep->prev_can_eliminate = true;
+  else
+    ep->prev_can_eliminate = value;
+  ep->can_eliminate = value;
+  if (! value
+      && ep->from == FRAME_POINTER_REGNUM && ep->to == STACK_POINTER_REGNUM)
+    frame_pointer_needed = 1;
+  if (!frame_pointer_needed)
+    REGNO_POINTER_ALIGN (HARD_FRAME_POINTER_REGNUM) = 0;
+}
+
 /* Set up ELIMINATION_MAP of the currently used eliminations.  */
 static void
 setup_elimination_map (void)
@@ -263,7 +271,10 @@ get_elimination (rtx reg)
   if ((hard_regno = REGNO (reg)) < 0 || hard_regno >= FIRST_PSEUDO_REGISTER)
     return NULL;
   if ((ep = elimination_map[hard_regno]) != NULL)
-    return ep->from_rtx != reg ? NULL : ep;
+    {
+      gcc_assert (ep->can_eliminate);
+      return ep->from_rtx != reg ? NULL : ep;
+    }
   poly_int64 offset = self_elim_offsets[hard_regno];
   if (known_eq (offset, 0))
     return NULL;
diff --git a/gcc/lra-spills.cc b/gcc/lra-spills.cc
index fc912c43ce6e..4febc693d283 100644
--- a/gcc/lra-spills.cc
+++ b/gcc/lra-spills.cc
@@ -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);
            }
diff --git a/gcc/testsuite/g++.target/arm/pr120424.C 
b/gcc/testsuite/g++.target/arm/pr120424.C
new file mode 100644
index 000000000000..4d0e49013c04
--- /dev/null
+++ b/gcc/testsuite/g++.target/arm/pr120424.C
@@ -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);
+}

Reply via email to