[v2] cselib: lookup mem addr during invalidation

Message ID orfr9n82r7.fsf_-_@lxoliva.fsfla.org
State New
Headers
Series [v2] cselib: lookup mem addr during invalidation |

Commit Message

Alexandre Oliva Dec. 7, 2025, 2:06 a.m. UTC
  On Dec  5, 2025, Eric Botcazou <botcazou@adacore.com> wrote:

>> * cselib.cc (cselib_invalidate_mem): Lookup the address as
>> part of canonicalizing it.

> The cselib_invalidate_mem change looks a bit risky at this stage

... and here's that part split out for the next stage1.  Ok then?


When processing stores for e.g. parameters passed on the stack, as in
gcc.dg/pr117239.c, each store invalidates other stores pertaining to
the same argument, because we can tell they refer to the same object,
but not that the offsets don't overlap.  The reason for that is that
the mem_rtx being invalidated is canonicalized to an SP offset, while
those in the cselib table have canonical values as addresses, and
alias.cc can't resolve SP to values to compare the offsets.

With this change, pr117239.c doesn't require -fschedule-insns to fail,
with the PR117239 fixes reverted.


for  gcc/ChangeLog

	* cselib.cc (cselib_invalidate_mem): Lookup the address as
	part of canonicalizing it.
---
 gcc/cselib.cc |    8 ++++++++
 1 file changed, 8 insertions(+)
  

Comments

Eric Botcazou Dec. 7, 2025, 9:35 a.m. UTC | #1
> ... and here's that part split out for the next stage1.  Ok then?

Sure.
  
Alexandre Oliva Dec. 12, 2025, 6:33 a.m. UTC | #2
On Dec  6, 2025, Alexandre Oliva <oliva@adacore.com> wrote:

> When processing stores for e.g. parameters passed on the stack, as in
> gcc.dg/pr117239.c, each store invalidates other stores pertaining to
> the same argument, because we can tell they refer to the same object,
> but not that the offsets don't overlap.  The reason for that is that
> the mem_rtx being invalidated is canonicalized to an SP offset, while
> those in the cselib table have canonical values as addresses, and
> alias.cc can't resolve SP to values to compare the offsets.

I withdraw the patch:

> 	* cselib.cc (cselib_invalidate_mem): Lookup the address as
> 	part of canonicalizing it.

because base terms aren't preserved in cselib, and even with WIP code to
preserve them, when stack and frame pointer differ by a constant, they
end up in the same cluster of VALUEs, but alias.cc wants them to have
different base terms.

I suppose one way to improve on this is for the frame pointer after
reload to share the base term with the stack pointer.  This would also
prevent the sp restore from bp to cause sp's reg_value to be reset, and
avoid the need for special-casing of the bp set up from sp.  Would such
a change be desirable/acceptable?

Is there interest in code to preserve base terms in cselib tables?
  
Alexandre Oliva Dec. 17, 2025, 2:04 a.m. UTC | #3
On Dec 12, 2025, Alexandre Oliva <oliva@adacore.com> wrote:

> because base terms aren't preserved in cselib, and even with WIP code to
> preserve them, when stack and frame pointer differ by a constant, they
> end up in the same cluster of VALUEs, but alias.cc wants them to have
> different base terms.

> I suppose one way to improve on this is for the frame pointer after
> reload to share the base term with the stack pointer.  This would also
> prevent the sp restore from bp to cause sp's reg_value to be reset, and
> avoid the need for special-casing of the bp set up from sp.  Would such
> a change be desirable/acceptable?

> Is there interest in code to preserve base terms in cselib tables?

I'm posting this WIP patch FTR as reference for
https://gcc.gnu.org/pipermail/gcc-patches/2025-December/703846.html and
to figure out whether there's interest in such an approach to retain
ADDRESSes in cselib tables.  This regstraps on x86_64-linux-gnu, but it
would require some further polishing to replace local declarations of
external functions with global ones, drop #if0 and whatnot.

diff --git a/gcc/alias.cc b/gcc/alias.cc
index a23396eaa35e0..f6f59c8e13119 100644
--- a/gcc/alias.cc
+++ b/gcc/alias.cc
@@ -1659,6 +1659,16 @@ get_reg_base_value (unsigned int regno)
   return (*reg_base_value)[regno];
 }
 
+rtx
+get_reg_base_value_checked (unsigned int regno)
+{
+  if (!reg_base_value)
+    return NULL_RTX;
+  if (regno >= reg_base_value->length ())
+    return NULL_RTX;
+  return get_reg_base_value (regno);
+}
+
 /* If a value is known for REGNO, return it.  */
 
 rtx
@@ -1964,6 +1974,12 @@ find_base_term (rtx x, vec<std::pair<cselib_val *,
 	return temp;
       }
 
+    case VALUE_ADDRESS:
+      x = XEXP (x, 0);
+      if (GET_CODE (x) != VALUE)
+	return x;
+      /* Fall through.  */
+
     case VALUE:
       val = CSELIB_VAL_PTR (x);
       ret = NULL_RTX;
@@ -2070,6 +2086,13 @@ find_base_term (rtx x)
   return res;
 }
 
+rtx
+find_value_base_term (rtx x)
+{
+  gcc_checking_assert (GET_CODE (x) == VALUE);
+  return find_base_term (x);
+}
+
 /* Return true if accesses to address X may alias accesses based
    on the stack pointer.  */
 
@@ -2315,8 +2338,16 @@ get_addr (rtx x)
       for (l = v->locs; l; l = l->next)
 	if (CONSTANT_P (l->loc))
 	  return l->loc;
+#if 0
+	else if (GET_CODE (l->loc) == VALUE_ADDRESS)
+	  return x;
+#endif
       for (l = v->locs; l; l = l->next)
 	if (!REG_P (l->loc) && !MEM_P (l->loc)
+	    /* ENTRY_VALUEs are not useful addresses, we can't get aliasing
+	       information from them.  */
+	    && GET_CODE (l->loc) != ENTRY_VALUE
+	    && GET_CODE (l->loc) != VALUE_ADDRESS
 	    /* Avoid infinite recursion when potentially dealing with
 	       var-tracking artificial equivalences, by skipping the
 	       equivalences themselves, and not choosing expressions
@@ -2330,6 +2361,8 @@ get_addr (rtx x)
 	  for (l = v->locs; l; l = l->next)
 	    if (REG_P (l->loc)
 		|| (GET_CODE (l->loc) != VALUE
+		    && GET_CODE (l->loc) != ENTRY_VALUE
+		    && GET_CODE (l->loc) != VALUE_ADDRESS
 		    && !refs_newer_value_p (l->loc, x)))
 	      return l->loc;
 	  /* Return the canonical value.  */
@@ -3080,6 +3113,38 @@ canon_true_dependence (const_rtx mem, machine_mode mem_mode, rtx mem_addr,
 			    x, x_addr, /*mem_canonicalized=*/true);
 }
 
+/* Return true iff x and m are addresses with the same base term, and that are
+   equivalent memrefs.  Both are presumed canonicalized.  */
+bool
+same_base_term_known_overlap_p (rtx x, rtx m, poly_int64 size)
+{
+  rtx tx = get_addr (x);
+  rtx tm = get_addr (m);
+
+  rtx bx = find_base_term (tx);
+  rtx bm = find_base_term (tm);
+
+  if (bx != bm && bx && bm
+      && !(GET_CODE (bx) == ADDRESS
+	   && GET_CODE (bm) == ADDRESS
+	   && bx->u.fld[0].rt_int <= 0
+	   && bm->u.fld[0].rt_int <= 0)
+      /* libgo runtime may different decls for the same symbol used within the
+	 same function, because they come from different units compiled
+	 together.  */
+      && !(GET_CODE (bx) == SYMBOL_REF
+	   && GET_CODE (bm) == SYMBOL_REF
+	   && XSTR (bx, 0) == XSTR (bm, 0)))
+    return false;
+
+#if 0
+  x = canon_rtx (tx);
+  m = canon_rtx (tm);
+#endif
+
+  return memrefs_conflict_p (size, x, size, m, 0) != 0;
+}
+
 /* Returns true if a write to X might alias a previous read from
    (or, if WRITEP is true, a write to) MEM.
    If X_CANONCALIZED is true, then X_ADDR is the canonicalized address of X,
@@ -3290,16 +3355,25 @@ init_alias_target (void)
 	&& targetm.hard_regno_mode_ok (i, Pmode))
       static_reg_base_value[i] = arg_base_value;
 
-  /* RTL code is required to be consistent about whether it uses the
-     stack pointer, the frame pointer or the argument pointer to
-     access a given area of the frame.  We can therefore use the
-     base address to distinguish between the different areas.  */
+  /* RTL code is required to be consistent about whether it uses the stack
+     pointer, the frame pointer or the argument pointer to access a given area
+     of the frame.  We can therefore use the base address to distinguish
+     between the different areas.  However, after register allocation and
+     especially prologue generation, cselib makes all these areas part of the
+     same cluster of VALUEs, making it hard to distinguish the areas, so make
+     them all share the same base term then.  */
   static_reg_base_value[STACK_POINTER_REGNUM]
-    = unique_base_value (UNIQUE_BASE_VALUE_SP);
+    = (reload_completed
+       ? arg_base_value
+       : unique_base_value (UNIQUE_BASE_VALUE_SP));
   static_reg_base_value[ARG_POINTER_REGNUM]
-    = unique_base_value (UNIQUE_BASE_VALUE_ARGP);
+    = (reload_completed
+       ? arg_base_value
+       : unique_base_value (UNIQUE_BASE_VALUE_ARGP));
   static_reg_base_value[FRAME_POINTER_REGNUM]
-    = unique_base_value (UNIQUE_BASE_VALUE_FP);
+    = (reload_completed
+       ? arg_base_value
+       : unique_base_value (UNIQUE_BASE_VALUE_FP));
 
   /* The above rules extend post-reload, with eliminations applying
      consistently to each of the three pointers.  Cope with cases in
@@ -3307,7 +3381,9 @@ init_alias_target (void)
      rather than the stack pointer.  */
   if (!HARD_FRAME_POINTER_IS_FRAME_POINTER)
     static_reg_base_value[HARD_FRAME_POINTER_REGNUM]
-      = unique_base_value (UNIQUE_BASE_VALUE_HFP);
+      = (reload_completed
+	 ? arg_base_value
+	 : unique_base_value (UNIQUE_BASE_VALUE_HFP));
 }
 
 /* Set MEMORY_MODIFIED when X modifies DATA (that is assumed
diff --git a/gcc/cselib.cc b/gcc/cselib.cc
index c6628abf2a989..a6cae62bb2fee 100644
--- a/gcc/cselib.cc
+++ b/gcc/cselib.cc
@@ -2505,6 +2505,8 @@ cselib_lookup (rtx x, machine_mode mode,
 static void
 cselib_invalidate_regno_val (unsigned int regno, struct elt_list **l)
 {
+  extern rtx get_reg_base_value_checked (unsigned);
+  rtx value_address = get_reg_base_value_checked (regno);
   cselib_val *v = (*l)->elt;
   if (*l == REG_VALUES (regno))
     {
@@ -2517,7 +2519,12 @@ cselib_invalidate_regno_val (unsigned int regno, struct elt_list **l)
       l = &(*l)->next;
     }
   else
-    unchain_one_elt_list (l);
+    {
+      if (value_address
+	  && GET_MODE (value_address) != GET_MODE (v->val_rtx))
+	value_address = NULL_RTX;
+      unchain_one_elt_list (l);
+    }
 
   v = canonical_cselib_val (v);
 
@@ -2533,6 +2540,56 @@ cselib_invalidate_regno_val (unsigned int regno, struct elt_list **l)
       if (REG_P (x) && REGNO (x) == regno)
 	{
 	  unchain_one_elt_loc_list (p);
+
+	  extern rtx find_value_base_term (rtx);
+	  /* Preserve the VALUE_ADDRESS associated with the regno previously
+	     held in v, if we can't identify it any longer.  */
+	  rtx found_value_address = find_value_base_term (v->val_rtx);
+	  cselib_val *add_address_to = NULL;
+	  if (value_address && found_value_address != value_address)
+	    add_address_to = v;
+
+	  rtx wanted_value_address
+	    = value_address ? value_address : found_value_address;
+
+	  if (wanted_value_address)
+	    {
+	      cselib_val *addrv = v;
+	      for (;;)
+		{
+		  rtx addr = canon_rtx (get_addr (addrv->val_rtx));
+		  if (addr == addrv->val_rtx
+		      || GET_CODE (addr) != PLUS
+		      || GET_CODE (XEXP (addr, 0)) != VALUE
+		      || !CONSTANT_P (XEXP (addr, 1)))
+		    break;
+
+		  rtx addr_value_address
+		    = find_value_base_term (XEXP (addr, 0));
+		  if (addr_value_address)
+		    break;
+		  else
+		    {
+		      gcc_checking_assert (addr_value_address
+					   != wanted_value_address);
+		      addrv = add_address_to
+			= canonical_cselib_val (CSELIB_VAL_PTR (XEXP (addr, 0)));
+		    }
+		}
+	    }
+
+	  if (add_address_to)
+	    {
+	      if (add_address_to != v)
+		p = &add_address_to->locs;
+
+	      elt_loc_list *n = *p;
+	      *p = elt_loc_list_pool.allocate ();
+	      (*p)->loc = gen_rtx_VALUE_ADDRESS (GET_MODE (v->val_rtx),
+						 wanted_value_address);
+	      (*p)->setting_insn = setting_insn;
+	      (*p)->next = n;
+	    }
 	  break;
 	}
     }
@@ -2628,6 +2685,10 @@ cselib_invalidate_mem (rtx mem_rtx)
   if ((v = cselib_lookup (mem_addr, GET_MODE (mem_addr),
 			  0, GET_MODE (mem_rtx))))
     {
+      extern bool same_base_term_known_overlap_p (rtx, rtx, poly_int64);
+      gcc_checking_assert (same_base_term_known_overlap_p
+			   (mem_addr, v->val_rtx,
+			    GET_MODE_SIZE (GET_MODE (mem_rtx))));
       mem_addr = v->val_rtx;
       mem_rtx = replace_equiv_address_nv (mem_rtx, mem_addr);
     }
diff --git a/gcc/rtl.def b/gcc/rtl.def
index 15ae7d10fcc10..b1f68fb00d25a 100644
--- a/gcc/rtl.def
+++ b/gcc/rtl.def
@@ -128,6 +128,9 @@ DEF_RTL_EXPR(SEQUENCE, "sequence", "E", RTX_EXTRA)
 /* Represents a non-global base address.  This is only used in alias.cc.  */
 DEF_RTL_EXPR(ADDRESS, "address", "i", RTX_EXTRA)
 
+/* Represents a base address within a cselib table.  */
+DEF_RTL_EXPR(VALUE_ADDRESS, "value_address", "e", RTX_OBJ)
+
 /* ----------------------------------------------------------------------
    Expression types used for things in the instruction chain.
 
diff --git a/gcc/var-tracking.cc b/gcc/var-tracking.cc
index 8732c3ba62aa5..21cde6b0a7b6c 100644
--- a/gcc/var-tracking.cc
+++ b/gcc/var-tracking.cc
@@ -2442,6 +2442,7 @@ unsuitable_loc (rtx loc)
     case SCRATCH:
     case ASM_INPUT:
     case ASM_OPERANDS:
+    case VALUE_ADDRESS:
       return true;
 
     default:
  
Richard Biener Dec. 17, 2025, 12:36 p.m. UTC | #4
On Wed, Dec 17, 2025 at 3:06 AM Alexandre Oliva <oliva@adacore.com> wrote:
>
> On Dec 12, 2025, Alexandre Oliva <oliva@adacore.com> wrote:
>
> > because base terms aren't preserved in cselib, and even with WIP code to
> > preserve them, when stack and frame pointer differ by a constant, they
> > end up in the same cluster of VALUEs, but alias.cc wants them to have
> > different base terms.
>
> > I suppose one way to improve on this is for the frame pointer after
> > reload to share the base term with the stack pointer.  This would also
> > prevent the sp restore from bp to cause sp's reg_value to be reset, and
> > avoid the need for special-casing of the bp set up from sp.  Would such
> > a change be desirable/acceptable?
>
> > Is there interest in code to preserve base terms in cselib tables?

IMO "base terms" (aka RTLs "points-to analysis") needs to be re-architected.

> I'm posting this WIP patch FTR as reference for
> https://gcc.gnu.org/pipermail/gcc-patches/2025-December/703846.html and
> to figure out whether there's interest in such an approach to retain
> ADDRESSes in cselib tables.  This regstraps on x86_64-linux-gnu, but it
> would require some further polishing to replace local declarations of
> external functions with global ones, drop #if0 and whatnot.
>
> diff --git a/gcc/alias.cc b/gcc/alias.cc
> index a23396eaa35e0..f6f59c8e13119 100644
> --- a/gcc/alias.cc
> +++ b/gcc/alias.cc
> @@ -1659,6 +1659,16 @@ get_reg_base_value (unsigned int regno)
>    return (*reg_base_value)[regno];
>  }
>
> +rtx
> +get_reg_base_value_checked (unsigned int regno)
> +{
> +  if (!reg_base_value)
> +    return NULL_RTX;
> +  if (regno >= reg_base_value->length ())
> +    return NULL_RTX;
> +  return get_reg_base_value (regno);
> +}
> +
>  /* If a value is known for REGNO, return it.  */
>
>  rtx
> @@ -1964,6 +1974,12 @@ find_base_term (rtx x, vec<std::pair<cselib_val *,
>         return temp;
>        }
>
> +    case VALUE_ADDRESS:
> +      x = XEXP (x, 0);
> +      if (GET_CODE (x) != VALUE)
> +       return x;
> +      /* Fall through.  */
> +
>      case VALUE:
>        val = CSELIB_VAL_PTR (x);
>        ret = NULL_RTX;
> @@ -2070,6 +2086,13 @@ find_base_term (rtx x)
>    return res;
>  }
>
> +rtx
> +find_value_base_term (rtx x)
> +{
> +  gcc_checking_assert (GET_CODE (x) == VALUE);
> +  return find_base_term (x);
> +}
> +
>  /* Return true if accesses to address X may alias accesses based
>     on the stack pointer.  */
>
> @@ -2315,8 +2338,16 @@ get_addr (rtx x)
>        for (l = v->locs; l; l = l->next)
>         if (CONSTANT_P (l->loc))
>           return l->loc;
> +#if 0
> +       else if (GET_CODE (l->loc) == VALUE_ADDRESS)
> +         return x;
> +#endif
>        for (l = v->locs; l; l = l->next)
>         if (!REG_P (l->loc) && !MEM_P (l->loc)
> +           /* ENTRY_VALUEs are not useful addresses, we can't get aliasing
> +              information from them.  */
> +           && GET_CODE (l->loc) != ENTRY_VALUE
> +           && GET_CODE (l->loc) != VALUE_ADDRESS
>             /* Avoid infinite recursion when potentially dealing with
>                var-tracking artificial equivalences, by skipping the
>                equivalences themselves, and not choosing expressions
> @@ -2330,6 +2361,8 @@ get_addr (rtx x)
>           for (l = v->locs; l; l = l->next)
>             if (REG_P (l->loc)
>                 || (GET_CODE (l->loc) != VALUE
> +                   && GET_CODE (l->loc) != ENTRY_VALUE
> +                   && GET_CODE (l->loc) != VALUE_ADDRESS
>                     && !refs_newer_value_p (l->loc, x)))
>               return l->loc;
>           /* Return the canonical value.  */
> @@ -3080,6 +3113,38 @@ canon_true_dependence (const_rtx mem, machine_mode mem_mode, rtx mem_addr,
>                             x, x_addr, /*mem_canonicalized=*/true);
>  }
>
> +/* Return true iff x and m are addresses with the same base term, and that are
> +   equivalent memrefs.  Both are presumed canonicalized.  */
> +bool
> +same_base_term_known_overlap_p (rtx x, rtx m, poly_int64 size)
> +{
> +  rtx tx = get_addr (x);
> +  rtx tm = get_addr (m);
> +
> +  rtx bx = find_base_term (tx);
> +  rtx bm = find_base_term (tm);
> +
> +  if (bx != bm && bx && bm
> +      && !(GET_CODE (bx) == ADDRESS
> +          && GET_CODE (bm) == ADDRESS
> +          && bx->u.fld[0].rt_int <= 0
> +          && bm->u.fld[0].rt_int <= 0)
> +      /* libgo runtime may different decls for the same symbol used within the
> +        same function, because they come from different units compiled
> +        together.  */
> +      && !(GET_CODE (bx) == SYMBOL_REF
> +          && GET_CODE (bm) == SYMBOL_REF
> +          && XSTR (bx, 0) == XSTR (bm, 0)))
> +    return false;
> +
> +#if 0
> +  x = canon_rtx (tx);
> +  m = canon_rtx (tm);
> +#endif
> +
> +  return memrefs_conflict_p (size, x, size, m, 0) != 0;
> +}
> +
>  /* Returns true if a write to X might alias a previous read from
>     (or, if WRITEP is true, a write to) MEM.
>     If X_CANONCALIZED is true, then X_ADDR is the canonicalized address of X,
> @@ -3290,16 +3355,25 @@ init_alias_target (void)
>         && targetm.hard_regno_mode_ok (i, Pmode))
>        static_reg_base_value[i] = arg_base_value;
>
> -  /* RTL code is required to be consistent about whether it uses the
> -     stack pointer, the frame pointer or the argument pointer to
> -     access a given area of the frame.  We can therefore use the
> -     base address to distinguish between the different areas.  */
> +  /* RTL code is required to be consistent about whether it uses the stack
> +     pointer, the frame pointer or the argument pointer to access a given area
> +     of the frame.  We can therefore use the base address to distinguish
> +     between the different areas.  However, after register allocation and
> +     especially prologue generation, cselib makes all these areas part of the
> +     same cluster of VALUEs, making it hard to distinguish the areas, so make
> +     them all share the same base term then.  */
>    static_reg_base_value[STACK_POINTER_REGNUM]
> -    = unique_base_value (UNIQUE_BASE_VALUE_SP);
> +    = (reload_completed
> +       ? arg_base_value
> +       : unique_base_value (UNIQUE_BASE_VALUE_SP));
>    static_reg_base_value[ARG_POINTER_REGNUM]
> -    = unique_base_value (UNIQUE_BASE_VALUE_ARGP);
> +    = (reload_completed
> +       ? arg_base_value
> +       : unique_base_value (UNIQUE_BASE_VALUE_ARGP));
>    static_reg_base_value[FRAME_POINTER_REGNUM]
> -    = unique_base_value (UNIQUE_BASE_VALUE_FP);
> +    = (reload_completed
> +       ? arg_base_value
> +       : unique_base_value (UNIQUE_BASE_VALUE_FP));
>
>    /* The above rules extend post-reload, with eliminations applying
>       consistently to each of the three pointers.  Cope with cases in
> @@ -3307,7 +3381,9 @@ init_alias_target (void)
>       rather than the stack pointer.  */
>    if (!HARD_FRAME_POINTER_IS_FRAME_POINTER)
>      static_reg_base_value[HARD_FRAME_POINTER_REGNUM]
> -      = unique_base_value (UNIQUE_BASE_VALUE_HFP);
> +      = (reload_completed
> +        ? arg_base_value
> +        : unique_base_value (UNIQUE_BASE_VALUE_HFP));
>  }
>
>  /* Set MEMORY_MODIFIED when X modifies DATA (that is assumed
> diff --git a/gcc/cselib.cc b/gcc/cselib.cc
> index c6628abf2a989..a6cae62bb2fee 100644
> --- a/gcc/cselib.cc
> +++ b/gcc/cselib.cc
> @@ -2505,6 +2505,8 @@ cselib_lookup (rtx x, machine_mode mode,
>  static void
>  cselib_invalidate_regno_val (unsigned int regno, struct elt_list **l)
>  {
> +  extern rtx get_reg_base_value_checked (unsigned);
> +  rtx value_address = get_reg_base_value_checked (regno);
>    cselib_val *v = (*l)->elt;
>    if (*l == REG_VALUES (regno))
>      {
> @@ -2517,7 +2519,12 @@ cselib_invalidate_regno_val (unsigned int regno, struct elt_list **l)
>        l = &(*l)->next;
>      }
>    else
> -    unchain_one_elt_list (l);
> +    {
> +      if (value_address
> +         && GET_MODE (value_address) != GET_MODE (v->val_rtx))
> +       value_address = NULL_RTX;
> +      unchain_one_elt_list (l);
> +    }
>
>    v = canonical_cselib_val (v);
>
> @@ -2533,6 +2540,56 @@ cselib_invalidate_regno_val (unsigned int regno, struct elt_list **l)
>        if (REG_P (x) && REGNO (x) == regno)
>         {
>           unchain_one_elt_loc_list (p);
> +
> +         extern rtx find_value_base_term (rtx);
> +         /* Preserve the VALUE_ADDRESS associated with the regno previously
> +            held in v, if we can't identify it any longer.  */
> +         rtx found_value_address = find_value_base_term (v->val_rtx);
> +         cselib_val *add_address_to = NULL;
> +         if (value_address && found_value_address != value_address)
> +           add_address_to = v;
> +
> +         rtx wanted_value_address
> +           = value_address ? value_address : found_value_address;
> +
> +         if (wanted_value_address)
> +           {
> +             cselib_val *addrv = v;
> +             for (;;)
> +               {
> +                 rtx addr = canon_rtx (get_addr (addrv->val_rtx));
> +                 if (addr == addrv->val_rtx
> +                     || GET_CODE (addr) != PLUS
> +                     || GET_CODE (XEXP (addr, 0)) != VALUE
> +                     || !CONSTANT_P (XEXP (addr, 1)))
> +                   break;
> +
> +                 rtx addr_value_address
> +                   = find_value_base_term (XEXP (addr, 0));
> +                 if (addr_value_address)
> +                   break;
> +                 else
> +                   {
> +                     gcc_checking_assert (addr_value_address
> +                                          != wanted_value_address);
> +                     addrv = add_address_to
> +                       = canonical_cselib_val (CSELIB_VAL_PTR (XEXP (addr, 0)));
> +                   }
> +               }
> +           }
> +
> +         if (add_address_to)
> +           {
> +             if (add_address_to != v)
> +               p = &add_address_to->locs;
> +
> +             elt_loc_list *n = *p;
> +             *p = elt_loc_list_pool.allocate ();
> +             (*p)->loc = gen_rtx_VALUE_ADDRESS (GET_MODE (v->val_rtx),
> +                                                wanted_value_address);
> +             (*p)->setting_insn = setting_insn;
> +             (*p)->next = n;
> +           }
>           break;
>         }
>      }
> @@ -2628,6 +2685,10 @@ cselib_invalidate_mem (rtx mem_rtx)
>    if ((v = cselib_lookup (mem_addr, GET_MODE (mem_addr),
>                           0, GET_MODE (mem_rtx))))
>      {
> +      extern bool same_base_term_known_overlap_p (rtx, rtx, poly_int64);
> +      gcc_checking_assert (same_base_term_known_overlap_p
> +                          (mem_addr, v->val_rtx,
> +                           GET_MODE_SIZE (GET_MODE (mem_rtx))));
>        mem_addr = v->val_rtx;
>        mem_rtx = replace_equiv_address_nv (mem_rtx, mem_addr);
>      }
> diff --git a/gcc/rtl.def b/gcc/rtl.def
> index 15ae7d10fcc10..b1f68fb00d25a 100644
> --- a/gcc/rtl.def
> +++ b/gcc/rtl.def
> @@ -128,6 +128,9 @@ DEF_RTL_EXPR(SEQUENCE, "sequence", "E", RTX_EXTRA)
>  /* Represents a non-global base address.  This is only used in alias.cc.  */
>  DEF_RTL_EXPR(ADDRESS, "address", "i", RTX_EXTRA)
>
> +/* Represents a base address within a cselib table.  */
> +DEF_RTL_EXPR(VALUE_ADDRESS, "value_address", "e", RTX_OBJ)
> +
>  /* ----------------------------------------------------------------------
>     Expression types used for things in the instruction chain.
>
> diff --git a/gcc/var-tracking.cc b/gcc/var-tracking.cc
> index 8732c3ba62aa5..21cde6b0a7b6c 100644
> --- a/gcc/var-tracking.cc
> +++ b/gcc/var-tracking.cc
> @@ -2442,6 +2442,7 @@ unsuitable_loc (rtx loc)
>      case SCRATCH:
>      case ASM_INPUT:
>      case ASM_OPERANDS:
> +    case VALUE_ADDRESS:
>        return true;
>
>      default:
>
>
> --
> Alexandre Oliva, happy hacker            https://blog.lx.oliva.nom.br/
> Free Software Activist     FSFLA co-founder     GNU Toolchain Engineer
> More tolerance and less prejudice are key for inclusion and diversity.
> Excluding neuro-others for not behaving ""normal"" is *not* inclusive!
  

Patch

diff --git a/gcc/cselib.cc b/gcc/cselib.cc
index 930357409bc54..c6628abf2a989 100644
--- a/gcc/cselib.cc
+++ b/gcc/cselib.cc
@@ -2623,6 +2623,14 @@  cselib_invalidate_mem (rtx mem_rtx)
   rtx mem_addr;
 
   mem_addr = canon_rtx (get_addr (XEXP (mem_rtx, 0)));
+  /* Resolve MEM_ADDR to a VALUE_RTX, so that canon_anti_dependence can compare
+     offsets from the same base, even for SP-based addresses.  */
+  if ((v = cselib_lookup (mem_addr, GET_MODE (mem_addr),
+			  0, GET_MODE (mem_rtx))))
+    {
+      mem_addr = v->val_rtx;
+      mem_rtx = replace_equiv_address_nv (mem_rtx, mem_addr);
+    }
   mem_rtx = canon_rtx (mem_rtx);
 
   vp = &first_containing_mem;