"Roger Sayle" <[email protected]> writes:
> Hi Richard and Richard,
> Many thanks for the reviews.  Thanks for explaining that VEC_CONCAT
> is in memory order, so that the first field needs to be SUBREG_BYTE (op) == 0
> rather than subreg_lowpart_p (op) [I'm fortunate enough that for x86_64
> both these conditions are equivalent].
>
> I'm currently tweaking my patch to avoid subreg_lowpart_p (except
> where the inner mode and the outer mode have the same size, in which
> case I believe subreg_lowpart_p is equivalent to SUBREG_BYTE == 0).
>
> However, to confirm that I now have this straight, do you agree that the
> existing VEC_CONCAT transformation in simplify-rtx.cc (immediately before
> my change) is broken/non-portable:
>
> simplify_rtx.cc line 5589
>         /* (vec_concat:
>              (subreg_lowpart:N OP)
>              (vec_select:N OP P))  -->  OP when P selects the high half
>             of the OP.  */
>         if (GET_CODE (trueop0) == SUBREG
>             && subreg_lowpart_p (trueop0)
>             && GET_CODE (trueop1) == VEC_SELECT
>             && SUBREG_REG (trueop0) == XEXP (trueop1, 0)
>             && !side_effects_p (XEXP (trueop1, 0))
>             && vec_series_highpart_p (op1_mode, mode, XEXP (trueop1, 1)))
>           return XEXP (trueop1, 0);
>
> Notice the use of "subreg_lowpart_p (trueop0)" is the same idiom that
> you're asking me to avoid/correct.

Yeah, I agree that that looks wrong.

Since that fold and yours are really variants of the same thing,
I wonder whether we should have an rtx equivalent of get_ref_base_and_extent.
I've attached a proof of concept below.  It's completely untested, so likely
wrong in several ways, but I think it would be better than testing for
specific combinations like doubly nested subregs, subregs of shifts,
or vec_selects.

We could later add support for other rtx codes, such as SIGN_EXTRACT,
ZERO_EXTRACT, and TRUNCATE.

Thanks,
Richard


diff --git a/gcc/rtl.cc b/gcc/rtl.cc
index 3839c364571..9e7390082c9 100644
--- a/gcc/rtl.cc
+++ b/gcc/rtl.cc
@@ -594,12 +594,13 @@ rtvec_all_equal_p (const_rtvec vec)
    { START, START+1, START+2, ... }.  */
 
 bool
-rtvec_series_p (rtvec vec, int start)
+rtvec_series_p (rtvec vec, poly_int64 start)
 {
   for (int i = 0; i < GET_NUM_ELEM (vec); i++)
     {
-      rtx x = RTVEC_ELT (vec, i);
-      if (!CONST_INT_P (x) || INTVAL (x) != i + start)
+      poly_int64 elt;
+      if (!poly_int_rtx_p (RTVEC_ELT (vec, i), &elt)
+         || maybe_ne (elt, i + start))
        return false;
     }
   return true;
diff --git a/gcc/rtl.h b/gcc/rtl.h
index 747d917ecf2..55a7548e31f 100644
--- a/gcc/rtl.h
+++ b/gcc/rtl.h
@@ -3085,7 +3085,7 @@ extern bool rtx_equal_p (const_rtx, const_rtx,
                         rtx_equal_p_callback_function = NULL);
 
 extern bool rtvec_all_equal_p (const_rtvec);
-extern bool rtvec_series_p (rtvec, int);
+extern bool rtvec_series_p (rtvec, poly_int64);
 
 /* Return true if X is a vector constant with a duplicated element value.  */
 
diff --git a/gcc/rtlanal.cc b/gcc/rtlanal.cc
index 5274a5c59cf..94a339b9a8a 100644
--- a/gcc/rtlanal.cc
+++ b/gcc/rtlanal.cc
@@ -7100,3 +7100,76 @@ contains_paradoxical_subreg_p (rtx x)
     }
   return false;
 }
+
+/* Analyze X as accessing a consecutive sequence of bytes in some base rtx that
+   is no smaller than X.  Return the base value and set *OFFSET_PTR to the byte
+   offset of X from the start of the base.  Like SUBREG_BYTE, this byte offset
+   follows memory order.  */
+
+rtx
+get_ref_base_and_offset (rtx x, poly_uint64 *offset_ptr)
+{
+  poly_uint64 outer_bytes = GET_MODE_SIZE (GET_MODE (x));
+  poly_uint64 offset = 0;
+  for (;;)
+    {
+      switch (GET_CODE (x))
+       {
+       case SUBREG:
+         if (!paradoxical_subreg_p (x))
+           {
+             offset += SUBREG_BYTE (x);
+             x = SUBREG_REG (x);
+             continue;
+           }
+         break;
+
+       case ASHIFT:
+       case LSHIFTRT:
+       case ASHIFTRT:
+         if (SCALAR_INT_MODE_P (GET_MODE (x))
+             && CONST_INT_P (XEXP (x, 1))
+             && UINTVAL (XEXP (x, 1)) % BITS_PER_UNIT == 0)
+           {
+             auto inner_bytes = GET_MODE_SIZE (GET_MODE (x));
+             /* Reanalyze the current extraction as a shift right of X.  */
+             poly_uint64 lsb = subreg_size_lsb (outer_bytes, inner_bytes,
+                                                offset);
+             /* Convert it to a shift right of XEXP (x, 0).  This might
+                wrap.  */
+             if (GET_CODE (x) == ASHIFT)
+               lsb -= UINTVAL (XEXP (x, 1));
+             else
+               lsb += UINTVAL (XEXP (x, 1));
+             if (known_le (lsb, (inner_bytes - outer_bytes) * BITS_PER_UNIT))
+               {
+                 x = XEXP (x, 0);
+                 offset = subreg_size_offset_from_lsb (outer_bytes,
+                                                       inner_bytes, lsb);
+                 continue;
+               }
+           }
+         break;
+
+       case VEC_SELECT:
+         {
+           rtx sel = XEXP (x, 1);
+           poly_int64 start;
+           if (poly_int_rtx_p (XVECEXP (sel, 0, 0), &start)
+               && rtvec_series_p (XVEC (sel, 0), start))
+             {
+               x = XEXP (x, 0);
+               offset += start * GET_MODE_UNIT_SIZE (GET_MODE (x));
+               continue;
+             }
+           break;
+         }
+
+       default:
+         break;
+       }
+
+      *offset_ptr = offset;
+      return x;
+    }
+}

Reply via email to