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