From c7551dc24a0afe404819bb100c82aa5151ab0596 Mon Sep 17 00:00:00 2001
From: Odysseas Georgoudis <odygrd@hotmail.com>
Date: Wed, 15 Jul 2026 06:05:44 +0100
Subject: [PATCH] middle-end, i386: Fold averages with equal operands
 [PR122715]
To: gcc-patches@gcc.gnu.org
Cc: Andrew Pinski <andrew.pinski@oss.qualcomm.com>

IFN_AVG_FLOOR (x, x) and IFN_AVG_CEIL (x, x) both produce x.
Fold these cases in match.pd.

x86 PAVG intrinsics are represented by target builtins instead of
IFN_AVG_CEIL.  Fold calls with equal operands in
ix86_gimple_fold_builtin too.  For masked forms, apply the fold only
when every lane is enabled by the mask.

Bootstrapped on x86_64-pc-linux-gnu.  The new tests and the existing
PAVG-focused tests pass.  The target-independent match.pd fold was also
tested using an AArch64 cross-compiler.

	PR middle-end/122715

gcc/ChangeLog:

	* config/i386/i386.cc (ix86_gimple_fold_builtin): Fold PAVG
	builtins with equal operands.
	* match.pd: Fold IFN_AVG_FLOOR and IFN_AVG_CEIL with equal
	operands.

gcc/testsuite/ChangeLog:

	* gcc.dg/pr122715.c: New test.
	* gcc.target/i386/pr122715.c: New test.
---
 gcc/config/i386/i386.cc                  | 33 ++++++++++
 gcc/match.pd                             |  6 ++
 gcc/testsuite/gcc.dg/pr122715.c          | 22 +++++++
 gcc/testsuite/gcc.target/i386/pr122715.c | 76 ++++++++++++++++++++++++
 4 files changed, 137 insertions(+)
 create mode 100644 gcc/testsuite/gcc.dg/pr122715.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr122715.c

diff --git a/gcc/config/i386/i386.cc b/gcc/config/i386/i386.cc
index b137f5c22dc..696ff8cbdbe 100644
--- a/gcc/config/i386/i386.cc
+++ b/gcc/config/i386/i386.cc
@@ -19824,6 +19824,39 @@ ix86_gimple_fold_builtin (gimple_stmt_iterator *gsi)
 	}
       break;
 
+    case IX86_BUILTIN_PAVGUSB:
+    case IX86_BUILTIN_PAVGB:
+    case IX86_BUILTIN_PAVGW:
+    case IX86_BUILTIN_PAVGB128:
+    case IX86_BUILTIN_PAVGW128:
+    case IX86_BUILTIN_PAVGB256:
+    case IX86_BUILTIN_PAVGW256:
+    case IX86_BUILTIN_PAVGB128_MASK:
+    case IX86_BUILTIN_PAVGW128_MASK:
+    case IX86_BUILTIN_PAVGB256_MASK:
+    case IX86_BUILTIN_PAVGW256_MASK:
+    case IX86_BUILTIN_PAVGB512:
+    case IX86_BUILTIN_PAVGW512:
+      gcc_assert (n_args == 2 || n_args == 4);
+      arg0 = gimple_call_arg (stmt, 0);
+      arg1 = gimple_call_arg (stmt, 1);
+      /* For masked PAVG, only optimize if the mask is all ones.  */
+      if (n_args == 4)
+	{
+	  elems = TYPE_VECTOR_SUBPARTS (TREE_TYPE (arg0));
+	  if (!ix86_masked_all_ones (elems, gimple_call_arg (stmt, 3)))
+	    break;
+	}
+      if (operand_equal_p (arg0, arg1, 0) && gimple_call_lhs (stmt))
+	{
+	  loc = gimple_location (stmt);
+	  g = gimple_build_assign (gimple_call_lhs (stmt), arg0);
+	  gimple_set_location (g, loc);
+	  gsi_replace (gsi, g, false);
+	  return true;
+	}
+      break;
+
     case IX86_BUILTIN_PBLENDVB256:
     case IX86_BUILTIN_BLENDVPS256:
     case IX86_BUILTIN_BLENDVPD256:
diff --git a/gcc/match.pd b/gcc/match.pd
index d1a12c35ed3..c5f87fb6265 100644
--- a/gcc/match.pd
+++ b/gcc/match.pd
@@ -2320,7 +2320,13 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
     (view_convert (rshift (view_convert:ntype @0) @1))
     (convert (rshift (convert:ntype @0) @1))))))
 
+/* IFN_AVG_FLOOR (x, x) and IFN_AVG_CEIL (x, x) are both x.  */
 #if GIMPLE
+(for avg (IFN_AVG_FLOOR IFN_AVG_CEIL)
+ (simplify
+  (avg @0 @0)
+  @0))
+
  /* Fold ((x + y) >> 1 into IFN_AVG_FLOOR (x, y) if x and y are vectors in
     which each element is known to have at least one leading zero bit.  */
 (simplify
diff --git a/gcc/testsuite/gcc.dg/pr122715.c b/gcc/testsuite/gcc.dg/pr122715.c
new file mode 100644
index 00000000000..a767c1fe432
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pr122715.c
@@ -0,0 +1,22 @@
+/* PR middle-end/122715 */
+/* { dg-do compile } */
+/* { dg-options "-O2 -fgimple -fdump-tree-optimized" } */
+
+typedef unsigned int v4u32 __attribute__((vector_size(16)));
+
+v4u32 __GIMPLE() avg_floor(v4u32 x)
+{
+  v4u32 res;
+  res_1 = .AVG_FLOOR (x, x);
+  return res_1;
+}
+
+v4u32 __GIMPLE() avg_ceil(v4u32 x)
+{
+  v4u32 res;
+  res_1 = .AVG_CEIL (x, x);
+  return res_1;
+}
+
+/* { dg-final { scan-tree-dump-not {\.AVG_} "optimized" } } */
+/* { dg-final { scan-tree-dump-times {return x_} 2 "optimized" } } */
diff --git a/gcc/testsuite/gcc.target/i386/pr122715.c b/gcc/testsuite/gcc.target/i386/pr122715.c
new file mode 100644
index 00000000000..cdbb1a67104
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr122715.c
@@ -0,0 +1,76 @@
+/* PR middle-end/122715 */
+/* { dg-do compile } */
+/* { dg-options "-O2 -m3dnow -mavx2 -mavx512bw -mavx512vl" } */
+/* { dg-additional-options "-fdump-tree-optimized" } */
+
+#include <immintrin.h>
+
+__m64 avg_u8_3dnow(__m64 x)
+{
+  return (__m64) __builtin_ia32_pavgusb ((__v8qi) x, (__v8qi) x);
+}
+
+__m64 avg_u8_64(__m64 x)
+{
+  return _mm_avg_pu8 (x, x);
+}
+
+__m64 avg_u16_64(__m64 x)
+{
+  return _mm_avg_pu16 (x, x);
+}
+
+__m128i avg_u8_128(__m128i x)
+{
+  return _mm_avg_epu8 (x, x);
+}
+
+__m128i avg_u16_128(__m128i x)
+{
+  return _mm_avg_epu16 (x, x);
+}
+
+__m256i avg_u8_256(__m256i x)
+{
+  return _mm256_avg_epu8 (x, x);
+}
+
+__m256i avg_u16_256(__m256i x)
+{
+  return _mm256_avg_epu16 (x, x);
+}
+
+__m128i avg_u8_128_mask(__m128i x)
+{
+  return _mm_mask_avg_epu8 (_mm_setzero_si128 (), (__mmask16) -1, x, x);
+}
+
+__m128i avg_u16_128_mask(__m128i x)
+{
+  return _mm_mask_avg_epu16 (_mm_setzero_si128 (), (__mmask8) -1, x, x);
+}
+
+__m256i avg_u8_256_mask(__m256i x)
+{
+  return _mm256_mask_avg_epu8 (_mm256_setzero_si256 (),
+			       (__mmask32) -1, x, x);
+}
+
+__m256i avg_u16_256_mask(__m256i x)
+{
+  return _mm256_mask_avg_epu16 (_mm256_setzero_si256 (),
+				(__mmask16) -1, x, x);
+}
+
+__m512i avg_u8_512(__m512i x)
+{
+  return _mm512_avg_epu8 (x, x);
+}
+
+__m512i avg_u16_512(__m512i x)
+{
+  return _mm512_avg_epu16 (x, x);
+}
+
+/* { dg-final { scan-tree-dump-not {__builtin_ia32_pavg} "optimized" } } */
+/* { dg-final { scan-assembler-not {pavg} } } */
-- 
2.43.5

