Thanks for having a look.

> Why do we need to force it into a register?  That can be 
> counterproductive,
> since it means that both the subvector and the tuple are live at the same
> time.  I realise we probably expect this to be optimised away later,
> but still, it feels a bit premature.
>
> I suppose most of extract_bit_field_1 is written assuming that:
>
>   while (GET_CODE (op0) == SUBREG)
>     {
>       bitnum += SUBREG_BYTE (op0) * BITS_PER_UNIT;
>       op0 = SUBREG_REG (op0);
>     }
>
> has dealt with the subreg case, but given this change, I think it
> would be better to make the code more robust against SUBREGs instead.
> The while loop above managed to deal with subregs without emitting code,
> but doing that here would undo the optimisation.
>
> Or is forcing to a REG necessary for precisely that reason?  Does keeping
> the SUBREG lead to inefficiencies later?

And once again we have a situation where an issue vanished :)
It definitely was necessary before but I tested without and everything 
seems to work now.  I don't recall the details but we would try subreg 
shenanigans on the non-reg-forced subreg.  I can only speculate but 
perhaps we don't do this anymore now that REGMODE_NATURAL_SIZE is 
defined properly.

I addressed the rest of your comments:
 - Refactored extract_vector_from_vector into a "return early" style and 
   renamed variables.
 - Adjusted comments.
 - Put another extract_vector_from_vector after the multireg extract.

v2 which is currently being bootstrapped and regtested is below.

Regards
 Robin

[PATCH v2] expmed: Improve tuple-extraction handling.

After the REGMODE_NATURAL_SIZE changes have landed, this is another small
improvement for PR125390 which also fixes aarch64's struct_3_256.c
regression.
Originally a riscv-specific issue and ICE, I noticed poor codegen when we
extract values from a tuple type.  In the optimized dump we have:

  vect_array.9 = .MASK_LEN_LOAD_LANES (&c, 32B, { -1, -1, -1, -1 }, _44(D), 4, 
0);
  _48 = BIT_FIELD_REF <vect_array.9[0], 32, 96>;
  _50 = BIT_FIELD_REF <vect_array.9[1], 32, 96>;
  f_13 = _48 ^ _50;
  a = f_13;

and _48 as well as _50 spill to memory instead of extracting directly.

We end up with

  (subreg:RVVMF2SI (reg:RVVMF2x2SI 138 [ vect_array.9 ]) 0)

which, as we pun the with a large integer mode, involves OImode
that we cannot "move".  We might be able to work around that in the
target in unintuitive ways :)  and we could also offer vec_extract for
all tuple modes.  However, I figured it would be much easier if expand
already handled the tuple -> vector part for us so just a vector extract
is left for the target to do.

The patch adds multi-vector-reg -> vector functionality as a separate
function and also moves vector-vector, as well as vector-element extract into
new functions.

        PR middle-end/125390

gcc/ChangeLog:

        * expmed.cc (extract_bit_field_1): Call new functions.
        (extract_vector_from_vector): New function.
        (extract_element_from_vector): Likewise.
        (extract_vector_from_multireg): Likewise.

gcc/testsuite/ChangeLog:

        * gcc.target/riscv/rvv/autovec/pr125390.c: Adjust test
        expectation.
---
 gcc/expmed.cc                                 | 286 ++++++++++++------
 .../gcc.target/riscv/rvv/autovec/pr125390.c   |   4 +-
 2 files changed, 202 insertions(+), 88 deletions(-)

diff --git a/gcc/expmed.cc b/gcc/expmed.cc
index c6494484251..7b6fcbe73cc 100644
--- a/gcc/expmed.cc
+++ b/gcc/expmed.cc
@@ -1698,101 +1698,83 @@ extract_bit_field_as_subreg (machine_mode mode, rtx 
op0,
   return NULL_RTX;
 }
 
-/* A subroutine of extract_bit_field, with the same arguments.
-   If UNSIGNEDP is -1, the result need not be sign or zero extended.
-   If FALLBACK_P is true, fall back to extract_fixed_bit_field
-   if we can find no other means of implementing the operation.
-   if FALLBACK_P is false, return NULL instead.  */
+/* Try to extract a subvector of mode SUBVEC_MODE (which has BITSIZE bits)
+   from vector VEC, starting at bit BITNUM, then extract the MODE lowpart of
+   the subvector.  Return the result on success or NULL_RTX on failure.  */
 
 static rtx
-extract_bit_field_1 (rtx str_rtx, poly_uint64 bitsize, poly_uint64 bitnum,
-                    int unsignedp, rtx target, machine_mode mode,
-                    machine_mode tmode, bool reverse, bool fallback_p,
-                    rtx *alt_rtl)
+extract_vector_from_vector (rtx vec, rtx target, machine_mode mode,
+                           machine_mode subvec_mode, poly_uint64 bitsize,
+                           poly_uint64 bitnum, rtx *alt_rtl)
 {
-  rtx op0 = str_rtx;
-  machine_mode mode1;
-
-  if (tmode == VOIDmode)
-    tmode = mode;
+  machine_mode vec_mode = GET_MODE (vec);
+  if (!VECTOR_MODE_P (vec_mode)
+      || MEM_P (vec)
+      || !VECTOR_MODE_P (subvec_mode)
+      || maybe_ne (bitsize, GET_MODE_PRECISION (subvec_mode))
+      || known_le (GET_MODE_SIZE (GET_MODE (vec)),
+                  GET_MODE_SIZE (subvec_mode)))
+    return NULL_RTX;
 
-  while (GET_CODE (op0) == SUBREG)
-    {
-      bitnum += SUBREG_BYTE (op0) * BITS_PER_UNIT;
-      op0 = SUBREG_REG (op0);
+  /* Try to find a fitting mode for extracting.  */
+  machine_mode punned_vec_mode = vec_mode;
+  if (GET_MODE_INNER (vec_mode) != GET_MODE_INNER (subvec_mode))
+    {
+      scalar_mode inner_mode = GET_MODE_INNER (subvec_mode);
+      poly_uint64 nunits;
+      if (!multiple_p (GET_MODE_BITSIZE (vec_mode),
+                      GET_MODE_UNIT_BITSIZE (subvec_mode), &nunits)
+         || !related_vector_mode (subvec_mode, inner_mode,
+                                  nunits).exists (&punned_vec_mode)
+         || maybe_ne (GET_MODE_SIZE (punned_vec_mode),
+                      GET_MODE_SIZE (vec_mode)))
+       return NULL_RTX;
     }
+  vec_mode = punned_vec_mode;
 
-  /* If we have an out-of-bounds access to a register, just return an
-     uninitialized register of the required mode.  This can occur if the
-     source code contains an out-of-bounds access to a small array.  */
-  if (REG_P (op0) && known_ge (bitnum, GET_MODE_BITSIZE (GET_MODE (op0))))
-    return gen_reg_rtx (tmode);
-
-  if (REG_P (op0)
-      && mode == GET_MODE (op0)
-      && known_eq (bitnum, 0U)
-      && known_eq (bitsize, GET_MODE_BITSIZE (GET_MODE (op0))))
+  poly_uint64 pos;
+  enum insn_code icode;
+  if (((icode = convert_optab_handler
+       (vec_extract_optab, vec_mode, subvec_mode))
+       != CODE_FOR_nothing)
+      && multiple_p (bitnum, GET_MODE_BITSIZE (subvec_mode), &pos))
     {
-      if (reverse)
-       op0 = flip_storage_order (mode, op0);
-      /* We're trying to extract a full register from itself.  */
-      return op0;
-    }
+      if (vec_mode != GET_MODE (vec))
+       vec = gen_lowpart (vec_mode, vec);
 
-  /* First try to check for vector from vector extractions.  */
-  if (VECTOR_MODE_P (GET_MODE (op0))
-      && !MEM_P (op0)
-      && VECTOR_MODE_P (tmode)
-      && known_eq (bitsize, GET_MODE_PRECISION (tmode))
-      && maybe_gt (GET_MODE_SIZE (GET_MODE (op0)), GET_MODE_SIZE (tmode)))
-    {
-      machine_mode new_mode = GET_MODE (op0);
-      if (GET_MODE_INNER (new_mode) != GET_MODE_INNER (tmode))
-       {
-         scalar_mode inner_mode = GET_MODE_INNER (tmode);
-         poly_uint64 nunits;
-         if (!multiple_p (GET_MODE_BITSIZE (GET_MODE (op0)),
-                          GET_MODE_UNIT_BITSIZE (tmode), &nunits)
-             || !related_vector_mode (tmode, inner_mode,
-                                      nunits).exists (&new_mode)
-             || maybe_ne (GET_MODE_SIZE (new_mode),
-                          GET_MODE_SIZE (GET_MODE (op0))))
-           new_mode = VOIDmode;
-       }
-      poly_uint64 pos;
-      if (new_mode != VOIDmode
-         && (convert_optab_handler (vec_extract_optab, new_mode, tmode)
-             != CODE_FOR_nothing)
-         && multiple_p (bitnum, GET_MODE_BITSIZE (tmode), &pos))
+      class expand_operand ops[3];
+      create_output_operand (&ops[0], target, subvec_mode);
+      ops[0].target = 1;
+      create_input_operand (&ops[1], vec, GET_MODE (vec));
+      create_integer_operand (&ops[2], pos);
+      if (maybe_expand_insn (icode, 3, ops))
        {
-         class expand_operand ops[3];
-         machine_mode outermode = new_mode;
-         machine_mode innermode = tmode;
-         enum insn_code icode
-           = convert_optab_handler (vec_extract_optab, outermode, innermode);
-
-         if (new_mode != GET_MODE (op0))
-           op0 = gen_lowpart (new_mode, op0);
-         create_output_operand (&ops[0], target, innermode);
-         ops[0].target = 1;
-         create_input_operand (&ops[1], op0, outermode);
-         create_integer_operand (&ops[2], pos);
-         if (maybe_expand_insn (icode, 3, ops))
-           {
-             if (alt_rtl && ops[0].target)
-               *alt_rtl = target;
-             target = ops[0].value;
-             if (GET_MODE (target) != mode)
-               return gen_lowpart (tmode, target);
-             return target;
-           }
+         if (alt_rtl && ops[0].target)
+           *alt_rtl = target;
+         target = ops[0].value;
+         if (GET_MODE (target) != mode)
+           return gen_lowpart (subvec_mode, target);
+         return target;
        }
     }
 
+  return NULL_RTX;
+}
+
+/* Extract an element of OP0 of size BITSIZE, starting at bit position BITNUM.
+   Pun the result with mode TMODE if necessary.  Return the extracted element
+   if successful or NULL_RTX otherwise.  */
+
+static rtx
+extract_element_from_vector (rtx op0, rtx target, machine_mode mode,
+                            machine_mode tmode, poly_uint64 bitsize,
+                            poly_uint64 bitnum, rtx *alt_rtl)
+{
   /* See if we can get a better vector mode before extracting.  */
-  if (VECTOR_MODE_P (GET_MODE (op0))
+  machine_mode outermode = GET_MODE (op0);
+  if (VECTOR_MODE_P (outermode)
       && !MEM_P (op0)
-      && GET_MODE_INNER (GET_MODE (op0)) != tmode)
+      && GET_MODE_INNER (outermode) != tmode)
     {
       machine_mode new_mode;
 
@@ -1810,21 +1792,18 @@ extract_bit_field_1 (rtx str_rtx, poly_uint64 bitsize, 
poly_uint64 bitnum,
        new_mode = MIN_MODE_VECTOR_INT;
 
       FOR_EACH_MODE_FROM (new_mode, new_mode)
-       if (known_eq (GET_MODE_SIZE (new_mode), GET_MODE_SIZE (GET_MODE (op0)))
+       if (known_eq (GET_MODE_SIZE (new_mode), GET_MODE_SIZE (outermode))
            && known_eq (GET_MODE_UNIT_SIZE (new_mode), GET_MODE_SIZE (tmode))
            && known_eq (bitsize, GET_MODE_UNIT_PRECISION (new_mode))
            && multiple_p (bitnum, GET_MODE_UNIT_PRECISION (new_mode))
            && targetm.vector_mode_supported_p (new_mode)
-           && targetm.modes_tieable_p (GET_MODE (op0), new_mode))
+           && targetm.modes_tieable_p (outermode, new_mode))
          break;
       if (new_mode != VOIDmode)
        op0 = gen_lowpart (new_mode, op0);
     }
 
-  /* Use vec_extract patterns for extracting parts of vectors whenever
-     available.  If that fails, see whether the current modes and bitregion
-     give a natural subreg.  */
-  machine_mode outermode = GET_MODE (op0);
+  outermode = GET_MODE (op0);
   if (VECTOR_MODE_P (outermode) && !MEM_P (op0))
     {
       scalar_mode innermode = GET_MODE_INNER (outermode);
@@ -1866,6 +1845,139 @@ extract_bit_field_1 (rtx str_rtx, poly_uint64 bitsize, 
poly_uint64 bitnum,
        }
     }
 
+  return NULL_RTX;
+}
+
+/* Check whether:
+
+   (a) OP0 is a vector that spans multiple registers (which might
+       mean that it is a tuple mode) and
+
+   (b) a sequence of BITSIZE bits, starting at bit *BITNUM, fits within
+       a single vector register.
+
+   Return the vector register if so and update *BITNUM so that it
+   is relative to the returned register.  Return NULL_RTX otherwise.  */
+
+static rtx
+extract_vector_from_multireg (rtx op0, machine_mode tmode,
+                             poly_uint64 bitsize, poly_uint64 *bitnum)
+{
+  machine_mode outermode = GET_MODE (op0);
+  poly_uint64 outer_natural_sz = REGMODE_NATURAL_SIZE (outermode);
+  HOST_WIDE_INT vec_idx;
+  poly_uint64 off;
+  if (VECTOR_MODE_P (outermode)
+      && !MEM_P (op0)
+      && known_gt (GET_MODE_SIZE (outermode), outer_natural_sz)
+      && known_eq (bitsize, GET_MODE_PRECISION (tmode))
+      && known_le (GET_MODE_SIZE (tmode), outer_natural_sz)
+      && can_div_trunc_p (*bitnum, outer_natural_sz * BITS_PER_UNIT,
+                         &vec_idx, &off)
+      && known_le (off + bitsize, outer_natural_sz * BITS_PER_UNIT))
+    {
+      /* Get a single-vector mode from the outer multi-vector mode.
+        If it exists and we can pun the outer mode with a subreg,
+        do so and return it.  */
+      scalar_mode elmode = GET_MODE_INNER (outermode);
+      poly_uint64 nelts;
+      multiple_p (outer_natural_sz, GET_MODE_SIZE (elmode), &nelts);
+      opt_machine_mode vmode
+       = related_vector_mode (outermode, GET_MODE_INNER (outermode), nelts);
+
+      machine_mode vmode_onevec;
+      rtx subreg;
+      if (vmode.exists (&vmode_onevec)
+         && (subreg = simplify_gen_subreg
+             (vmode_onevec, op0, outermode,
+              vec_idx * GET_MODE_SIZE (vmode_onevec))))
+       {
+         *bitnum = off;
+         return subreg;
+       }
+    }
+
+  return NULL_RTX;
+}
+
+/* A subroutine of extract_bit_field, with the same arguments.
+   If UNSIGNEDP is -1, the result need not be sign or zero extended.
+   If FALLBACK_P is true, fall back to extract_fixed_bit_field
+   if we can find no other means of implementing the operation.
+   if FALLBACK_P is false, return NULL instead.  */
+
+static rtx
+extract_bit_field_1 (rtx str_rtx, poly_uint64 bitsize, poly_uint64 bitnum,
+                    int unsignedp, rtx target, machine_mode mode,
+                    machine_mode tmode, bool reverse, bool fallback_p,
+                    rtx *alt_rtl)
+{
+  rtx op0 = str_rtx;
+  machine_mode mode1;
+
+  if (tmode == VOIDmode)
+    tmode = mode;
+
+  while (GET_CODE (op0) == SUBREG)
+    {
+      bitnum += SUBREG_BYTE (op0) * BITS_PER_UNIT;
+      op0 = SUBREG_REG (op0);
+    }
+
+  /* If we have an out-of-bounds access to a register, just return an
+     uninitialized register of the required mode.  This can occur if the
+     source code contains an out-of-bounds access to a small array.  */
+  if (REG_P (op0) && known_ge (bitnum, GET_MODE_BITSIZE (GET_MODE (op0))))
+    return gen_reg_rtx (tmode);
+
+  if (REG_P (op0)
+      && mode == GET_MODE (op0)
+      && known_eq (bitnum, 0U)
+      && known_eq (bitsize, GET_MODE_BITSIZE (GET_MODE (op0))))
+    {
+      if (reverse)
+       op0 = flip_storage_order (mode, op0);
+      /* We're trying to extract a full register from itself.  */
+      return op0;
+    }
+
+  /* First try to check for vector from vector extractions.  */
+  rtx res = extract_vector_from_vector (op0, target, mode, tmode, bitsize,
+                                       bitnum, alt_rtl);
+  if (res)
+    return res;
+
+  /* Use vec_extract patterns for extracting parts of vectors whenever
+     available.  If that fails, see whether the current modes and bitregion
+     give a natural subreg.  */
+  res = extract_element_from_vector (op0, target, mode, tmode, bitsize, bitnum,
+                                    alt_rtl);
+  if (res)
+    return res;
+
+  /* If this mode occupies multiple registers and we're extracting
+     a single one, we can usually get to that vector directly
+     without actual extraction.
+     This helps targets see through multi-vector modes.  */
+  poly_uint64 bitnum_new = bitnum;
+  res = extract_vector_from_multireg (op0, tmode, bitsize, &bitnum_new);
+  if (res)
+    {
+      rtx vec = extract_vector_from_vector (res, target, mode, tmode, bitsize,
+                                           bitnum_new, alt_rtl);
+      if (vec)
+       return vec;
+
+      rtx el;
+      el = extract_element_from_vector (res, target, mode, tmode, bitsize,
+                                       bitnum_new, alt_rtl);
+      if (el)
+       return el;
+
+      op0 = res;
+      bitnum = bitnum_new;
+    }
+
   /* Make sure we are playing with integral modes.  Pun with subregs
      if we aren't.  */
   opt_scalar_int_mode op0_mode = int_mode_for_mode (GET_MODE (op0));
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/pr125390.c 
b/gcc/testsuite/gcc.target/riscv/rvv/autovec/pr125390.c
index 6ea9e509ba8..faa8eff767c 100644
--- a/gcc/testsuite/gcc.target/riscv/rvv/autovec/pr125390.c
+++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/pr125390.c
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-options "-march=rv64gcv_zvl256b -mabi=lp64d -mrvv-vector-bits=zvl -O2 
--param=riscv-autovec-mode=RVVMF8QI -fno-vect-cost-model" } */
+/* { dg-options "-march=rv64gcv_zvl256b -mabi=lp64d -mrvv-vector-bits=zvl -O2 
--param=riscv-autovec-mode=RVVMF8QI -fno-vect-cost-model 
-fdump-rtl-expand-details" } */
 
 int a, b[64], c[64];
 
@@ -32,3 +32,5 @@ baz (void)
       b[e] = 0;
     }
 }
+
+/* { dg-final { scan-rtl-dump-not "virtual-stack-vars" "expand" } } */
-- 
2.55.0

Reply via email to