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); +}