[2/2] middle-end: Lower variable-length vector division by a uniform constant [PR118377]

Message ID 20261002185018.1518807-2-gcc@gcc.dp11.uk
State New
Headers
Series [1/2] middle-end: Diagnose unsupported variable-length vector operations [PR118377] |

Commit Message

Dominic P Oct. 2, 2026, 6:50 p.m. UTC
  When the divisor is the same in every element, the analysis in
expand_vector_divmod needs only one element of it, and the shifts,
additions and highpart multiplications that it emits work for any
vector length.  Use it for variable-length vectors in that case, also
at -O0, where they have no piecewise fallback.  The testcase from the
PR (svint16_t x / 4) becomes a shift sequence instead of a sorry.
Divisors that differ between elements, and those that
expand_vector_divmod also rejects for fixed-length vectors, still get
the sorry.

Bootstrapped and regression tested on x86_64-pc-linux-gnu.  The SVE
and vect tests have no regressions on aarch64-linux-gnu (qemu, VL128
and VL2048).

Assisted-by: Claude Opus 5 (Anthropic)

	PR target/118377

gcc/ChangeLog:

	* tree-vect-generic.cc (add_shift): Handle variable-length vectors.
	(expand_vector_divmod): Handle variable-length vectors when the
	divisor is uniform.
	(expand_vector_operation): Try expand_vector_divmod for
	variable-length vectors at -O0 too.

gcc/testsuite/ChangeLog:

	* gcc.target/aarch64/sve/acle/general-c/pr118377.c: Expect no
	diagnostic for division and modulus by a uniform constant.
	* g++.target/aarch64/sve/acle/general-c++/pr118377.C: Likewise.
	* gcc.target/aarch64/sve/pr118377_run.c: New test.
	* gcc.target/aarch64/sve/pr118377_run_2.c: New test.

Signed-off-by: Dominic P <gcc@gcc.dp11.uk>
---
OK for trunk?

When optimizing, release builds of GCC 16 already compile these cases
correctly, because to_constant () returns the minimum length and the
equal elements fold to a duplicate, so without this patch 1/2 would
turn them into a sorry.  At -O0 they lower the first 128 bits only.

 .../aarch64/sve/acle/general-c++/pr118377.C   |  4 +-
 .../aarch64/sve/acle/general-c/pr118377.c     |  4 +-
 .../gcc.target/aarch64/sve/pr118377_run.c     | 39 +++++++++++++++++++
 .../gcc.target/aarch64/sve/pr118377_run_2.c   |  4 ++
 gcc/tree-vect-generic.cc                      | 14 +++++--
 5 files changed, 57 insertions(+), 8 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/aarch64/sve/pr118377_run.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/sve/pr118377_run_2.c
  

Patch

diff --git a/gcc/testsuite/g++.target/aarch64/sve/acle/general-c++/pr118377.C b/gcc/testsuite/g++.target/aarch64/sve/acle/general-c++/pr118377.C
index e614ded7c4b..07c81ac99d4 100644
--- a/gcc/testsuite/g++.target/aarch64/sve/acle/general-c++/pr118377.C
+++ b/gcc/testsuite/g++.target/aarch64/sve/acle/general-c++/pr118377.C
@@ -11,7 +11,7 @@  f1 (svint8_t x, svint8_t y)
 svint16_t
 f2 (svint16_t x)
 {
-  return x / 4; // { dg-message {unsupported operation on variable-length vectors} }
+  return x / 4;
 }
 
 svint32_t
@@ -23,7 +23,7 @@  f3 (svint32_t x, svint32_t y)
 svuint8_t
 f4 (svuint8_t x)
 {
-  return x % 3; // { dg-message {unsupported operation on variable-length vectors} }
+  return x % 3;
 }
 
 svbfloat16_t
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/acle/general-c/pr118377.c b/gcc/testsuite/gcc.target/aarch64/sve/acle/general-c/pr118377.c
index c26b36bb96e..e78784f3bcf 100644
--- a/gcc/testsuite/gcc.target/aarch64/sve/acle/general-c/pr118377.c
+++ b/gcc/testsuite/gcc.target/aarch64/sve/acle/general-c/pr118377.c
@@ -11,7 +11,7 @@  f1 (svint8_t x, svint8_t y)
 svint16_t
 f2 (svint16_t x)
 {
-  return x / 4; /* { dg-message {unsupported operation on variable-length vectors} } */
+  return x / 4;
 }
 
 svint32_t
@@ -23,7 +23,7 @@  f3 (svint32_t x, svint32_t y)
 svuint8_t
 f4 (svuint8_t x)
 {
-  return x % 3; /* { dg-message {unsupported operation on variable-length vectors} } */
+  return x % 3;
 }
 
 svbfloat16_t
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/pr118377_run.c b/gcc/testsuite/gcc.target/aarch64/sve/pr118377_run.c
new file mode 100644
index 00000000000..045b13b7b52
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/sve/pr118377_run.c
@@ -0,0 +1,39 @@ 
+/* { dg-do run { target aarch64_sve_hw } } */
+/* { dg-options "-O2" } */
+
+#include <arm_sve.h>
+
+#define TEST(NAME, VT, ST, PTRUE, OP, D)				\
+  __attribute__((noipa)) VT NAME (VT x) { return x OP (D); }		\
+  void check_##NAME (void)						\
+  {									\
+    ST a[256], r[256];							\
+    int n = svcntp_b8 (svptrue_b8 (), PTRUE ());			\
+    for (int i = 0; i < n; i++)						\
+      a[i] = (ST) (i * 2654435761u + 12345u);				\
+    svst1 (PTRUE (), r, NAME (svld1 (PTRUE (), a)));			\
+    for (int i = 0; i < n; i++)						\
+      if (r[i] != (ST) (a[i] OP (ST) (D)))				\
+	__builtin_abort ();						\
+  }
+
+TEST (s8_div_7, svint8_t, signed char, svptrue_b8, /, 7)
+TEST (u8_mod_3, svuint8_t, unsigned char, svptrue_b8, %, 3)
+TEST (s16_div_4, svint16_t, short, svptrue_b16, /, 4)
+TEST (s16_mod_m10, svint16_t, short, svptrue_b16, %, -10)
+TEST (u16_div_10, svuint16_t, unsigned short, svptrue_b16, /, 10)
+TEST (s32_mod_8, svint32_t, int, svptrue_b32, %, 8)
+TEST (u64_mod_10, svuint64_t, unsigned long, svptrue_b64, %, 10)
+
+int
+main (void)
+{
+  check_s8_div_7 ();
+  check_u8_mod_3 ();
+  check_s16_div_4 ();
+  check_s16_mod_m10 ();
+  check_u16_div_10 ();
+  check_s32_mod_8 ();
+  check_u64_mod_10 ();
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/pr118377_run_2.c b/gcc/testsuite/gcc.target/aarch64/sve/pr118377_run_2.c
new file mode 100644
index 00000000000..01fbfcb04bd
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/sve/pr118377_run_2.c
@@ -0,0 +1,4 @@ 
+/* { dg-do run { target aarch64_sve_hw } } */
+/* { dg-options "-O0" } */
+
+#include "pr118377_run.c"
diff --git a/gcc/tree-vect-generic.cc b/gcc/tree-vect-generic.cc
index 5681a92454f..37e9645f191 100644
--- a/gcc/tree-vect-generic.cc
+++ b/gcc/tree-vect-generic.cc
@@ -470,7 +470,9 @@  add_shift (gimple_stmt_iterator *gsi, tree type, tree op0, int *shiftcnts,
 	   enum tree_code code)
 {
   optab op;
-  unsigned int i, nunits = nunits_for_known_piecewise_op (type);
+  unsigned HOST_WIDE_INT i, nunits;
+  if (!TYPE_VECTOR_SUBPARTS (type).is_constant (&nunits))
+    nunits = 1;
   bool scalar_shift = true;
 
   for (i = 1; i < nunits; i++)
@@ -712,7 +714,11 @@  expand_vector_divmod (gimple_stmt_iterator *gsi, tree type, tree op0,
   int pre_shift = -1, post_shift;
   unsigned HOST_WIDE_INT nunits;
   if (!TYPE_VECTOR_SUBPARTS (type).is_constant (&nunits))
-    return NULL_TREE;
+    {
+      if (!uniform_vector_p (op1))
+	return NULL_TREE;
+      nunits = 1;
+    }
   int *shifts = XALLOCAVEC (int, nunits * 4);
   int *pre_shifts = shifts + nunits;
   int *post_shifts = pre_shifts + nunits;
@@ -904,7 +910,7 @@  expand_vector_divmod (gimple_stmt_iterator *gsi, tree type, tree op0,
 	      break;
 	  uns_type
 	    = build_vector_type (build_nonstandard_integer_type (prec, 1),
-				 nunits);
+				 TYPE_VECTOR_SUBPARTS (type));
 	  if (i == nunits && TYPE_MODE (uns_type) == TYPE_MODE (type))
 	    {
 	      for (i = 0; i < nunits; i++)
@@ -1340,7 +1346,7 @@  expand_vector_operation (gimple_stmt_iterator *gsi, tree type, tree compute_type
 	  tree rhs2 = gimple_assign_rhs2 (assign);
 	  tree ret;
 
-	  if (!optimize
+	  if ((!optimize && TYPE_VECTOR_SUBPARTS (type).is_constant ())
 	      || !VECTOR_INTEGER_TYPE_P (type)
 	      || TREE_CODE (rhs2) != VECTOR_CST
 	      || !VECTOR_MODE_P (TYPE_MODE (type)))