This is the second patch now. The "tuple/multi-vector-reg -> vector"
functionality depends on REGMODE_NATURAL_SIZE now but works as before apart
from that. Unfortunately, the diff is a bit garbled now, as I moved things
around. Sorry about that.
Bootstrapped and regtested on x86, power10, and aarch64.
Regtested on riscv64.
Regards
Robin
[PATCH] expand: 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.
I couldn't resist refactoring GET_MODE (op0) to outermode throughout the
functions. If that obstructs reviewing, I'm happy to revert that part.
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: New test.
---
gcc/expmed.cc | 212 +++++++++++++-----
.../gcc.target/riscv/rvv/autovec/pr125390.c | 36 +++
2 files changed, 194 insertions(+), 54 deletions(-)
create mode 100644 gcc/testsuite/gcc.target/riscv/rvv/autovec/pr125390.c
diff --git a/gcc/expmed.cc b/gcc/expmed.cc
index c4db5ef10bd..ae6b0fd0ecf 100644
--- a/gcc/expmed.cc
+++ b/gcc/expmed.cc
@@ -1680,65 +1680,34 @@ 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 sub-vector of mode TMODE (and same BITSIZE)
+ from the vector OP0 at position BITNUM. Lowpart the result of
+ mode MODE with TMODE if necessary. Return the extracted value
+ or NULL_RTX if we failed. */
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 op0, rtx target, machine_mode mode,
+ machine_mode tmode, poly_uint64 bitsize,
+ poly_uint64 bitnum, 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. */
- if (VECTOR_MODE_P (GET_MODE (op0))
+ machine_mode outermode = GET_MODE (op0);
+ if (VECTOR_MODE_P (outermode)
&& !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);
+ machine_mode new_mode = outermode;
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)),
+ if (!multiple_p (GET_MODE_BITSIZE (outermode),
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))))
+ GET_MODE_SIZE (outermode)))
new_mode = VOIDmode;
}
poly_uint64 pos;
@@ -1753,11 +1722,12 @@ extract_bit_field_1 (rtx str_rtx, poly_uint64 bitsize,
poly_uint64 bitnum,
enum insn_code icode
= convert_optab_handler (vec_extract_optab, outermode, innermode);
- if (new_mode != GET_MODE (op0))
- op0 = gen_lowpart (new_mode, op0);
+ rtx from = op0;
+ if (new_mode != GET_MODE (from))
+ from = gen_lowpart (new_mode, op0);
create_output_operand (&ops[0], target, innermode);
ops[0].target = 1;
- create_input_operand (&ops[1], op0, outermode);
+ create_input_operand (&ops[1], from, GET_MODE (from));
create_integer_operand (&ops[2], pos);
if (maybe_expand_insn (icode, 3, ops))
{
@@ -1771,10 +1741,23 @@ extract_bit_field_1 (rtx str_rtx, poly_uint64 bitsize,
poly_uint64 bitnum,
}
}
+ 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;
@@ -1792,21 +1775,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);
@@ -1848,6 +1828,130 @@ extract_bit_field_1 (rtx str_rtx, poly_uint64 bitsize,
poly_uint64 bitnum,
}
}
+ return NULL_RTX;
+}
+
+/* Get a vector from a multi-vector-register mode (like a tuple mode).
+ If OP0 has such a mode and TMODE as well as BITSIZE span a full
+ sub-vector, force it into a register of an appropriately-sized
+ vector mode. Return the vector if successful or 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,
+ directly force it into a register. The subreg should be
+ gone by then. */
+ 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))))
+ {
+ op0 = force_reg (vmode_onevec, subreg);
+ *bitnum = off;
+ return 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. */
+
+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 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
new file mode 100644
index 00000000000..536c71c174a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/pr125390.c
@@ -0,0 +1,36 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64gcv_zvl256b -mabi=lp64d -mrvv-vector-bits=zvl -O2
--param=riscv-autovec-mode=RVVMF8QI -fno-vect-cost-model -fdump-rtl-expand" } */
+
+int a, b[64], c[64];
+
+void
+foo (void)
+{
+ for (unsigned e = 0; e < 8; e += 2)
+ {
+ a = c[e] ^ c[e + 1];
+ b[e] = 0;
+ }
+}
+
+void
+bar (void)
+{
+ for (unsigned e = 0; e < 16; e += 2)
+ {
+ a = c[e] ^ c[e + 1];
+ b[e] = 0;
+ }
+}
+
+void
+baz (void)
+{
+ for (unsigned e = 0; e < 32; e += 4)
+ {
+ a = c[e] ^ c[e + 1] ^ c[e + 2] ^ c[e + 3];
+ b[e] = 0;
+ }
+}
+
+/* { dg-final { scan-rtl-dump-not "virtual-stack-vars" "expand" } } */
--
2.54.0