https://gcc.gnu.org/g:f5b3ed79efe40bae658954edd2e94f1a7dd29ec1

commit f5b3ed79efe40bae658954edd2e94f1a7dd29ec1
Author: Alexandre Oliva <ol...@adacore.com>
Date:   Sat Jun 21 05:58:22 2025 -0300

    [lra] propagate fp2sp elimination offset after disabling it [PR120424]
    
    Deactivating the fp2sp elimination early causes __do_global_ctors_aux
    on arm-linux-gnueabihf to be miscompiled (with -fnon-call-exceptions
    -fstack-clash-protection) because, after fp2sp is disabled, we'd
    choose another elimination and use its -1 prev_offset.
    
    But keeping it active causes e.g. pr103973-18.c to be miscompiled on
    x86_64-linux-gnu (with ix86_frame_pointer_required modified to return
    true when ix86_get_frame_size () > 0, to exercise the fp2sp
    elimination deactivation), because two adjacent pushes of two adjacent
    temporaries end up pushing the same slot, because one of them gets
    offset by the sp variation.
    
    The solution is to keep the early disabling of the elimination, which
    fixes x86_64, but retain the code to propagate the offset to the next
    FP elimination, which fixes arm.
    
    
    (I was concerned that disabling the fp2sp elimination too soon could
    drop offsets already applied to fp, that would have to be reverted or
    passed on to the next fp elimination, but we know the elimination was
    not used, and if there were any insns or offsets to propagate, the
    previous round of lra_eliminate, in which update_reg_eliminations
    would have selected the fp2sp elimination, would also have set
    elimination_fp2sp_occured if any insn used the fp2sp elimination's
    offset.  Since we know there weren't any, because
    elimination_fp2sp_occured is clear, we know other fp eliminations can
    safely use their initial offsets if needed.)

Diff:
---
 gcc/lra-eliminations.cc | 19 +++++++++++++++++--
 1 file changed, 17 insertions(+), 2 deletions(-)

diff --git a/gcc/lra-eliminations.cc b/gcc/lra-eliminations.cc
index f8a761e6eeaa..400857587b3a 100644
--- a/gcc/lra-eliminations.cc
+++ b/gcc/lra-eliminations.cc
@@ -312,6 +312,11 @@ move_plus_up (rtx x)
 /* Flag that we already did frame pointer to stack pointer elimination.  */
 static bool elimination_fp2sp_occured_p = false;
 
+/* Hold the fp2sp elimination that was disabled and inactivated, but
+   whose offsets we wish to propagate to any subsequently-chosen fp
+   elimination.  */
+static class lra_elim_table *disabled_fp2sp_elimination;
+
 /* Scan X and replace any eliminable registers (such as fp) with a
    replacement (such as sp) if SUBST_P, plus an offset.  The offset is
    a change in the offset between the eliminable register and its
@@ -1185,7 +1190,9 @@ update_reg_eliminate (bitmap insns_with_changed_offsets)
          setup_can_eliminate (ep, false);
          continue;
        }
-      if (!ep->can_eliminate && elimination_map[ep->from] == ep)
+      if (!ep->can_eliminate
+         && (elimination_map[ep->from] == ep
+             || disabled_fp2sp_elimination == ep))
        {
          /* We cannot use this elimination anymore -- find another
             one.  */
@@ -1233,6 +1240,7 @@ update_reg_eliminate (bitmap insns_with_changed_offsets)
 
       INITIAL_ELIMINATION_OFFSET (ep->from, ep->to, ep->offset);
     }
+  disabled_fp2sp_elimination = NULL;
   setup_elimination_map ();
   result = false;
   CLEAR_HARD_REG_SET (temp_hard_reg_set);
@@ -1319,6 +1327,7 @@ init_elimination (void)
   rtx_insn *insn;
   class lra_elim_table *ep;
 
+  disabled_fp2sp_elimination = NULL;
   init_elim_table ();
   FOR_EACH_BB_FN (bb, cfun)
     {
@@ -1432,7 +1441,13 @@ lra_update_fp2sp_elimination (void)
      during update_reg_eliminate.  */
   ep = elimination_map[FRAME_POINTER_REGNUM];
   if (ep->to == STACK_POINTER_REGNUM)
-    setup_can_eliminate (ep, false);
+    {
+      /* Avoid using this known-unused elimination when removing
+        spilled pseudos.  */
+      elimination_map[FRAME_POINTER_REGNUM] = NULL;
+      disabled_fp2sp_elimination = ep;
+      setup_can_eliminate (ep, false);
+    }
   else
     for (ep = reg_eliminate; ep < &reg_eliminate[NUM_ELIMINABLE_REGS]; ep++)
       if (ep->from == FRAME_POINTER_REGNUM && ep->to == STACK_POINTER_REGNUM)

Reply via email to