[PATCH v3] PR target/48609: RTL simplifications for x86 complex arg passing.

"Roger Sayle" <[email protected]>
Newsgroups gmane.comp.gcc.patches
Message-ID <[email protected]>
This is a ping/revision of my patch to address PR target/48609 incorporating
the latest round of feedback from Richard Sandiford.

The original patch (and motivation) was posted here:
https://gcc.gnu.org/pipermail/gcc-patches/2026-June/721864.html
with fantastic feedback from both Richards, version 2 was posted here:
https://gcc.gnu.org/pipermail/gcc-patches/2026-July/723533.html
This latest version includes the final tweaks suggested here:
https://gcc.gnu.org/pipermail/gcc-patches/2026-July/723545.html
which concluded with Richard Sandiford's words:
> LGTM otherwise, but I think someone else should give the final ok.


This patch has been tested on x86_64-pc-linux-gnu with make bootstrap
and make -k check, both with and without --target_board=unix{-m32}
with no new failures.  OK for mainline?


2026-08-10  Roger Sayle  <[email protected]>
            Richard Sandiford  <[email protected]>

gcc/ChangeLog
        PR target/48609
        * rtl.cc (rtvec_series_p): Enhance to allow START to be a
        poly_int64 instead of just an int.
        * rtl.h (rtvec_series_p): Update function prototype.
        * rtlanal.cc (vec_series_highpart_p): Now that rtxvec_series_p
        can handle poly_int64, we handle modes that aren't constant size.
        (vec_series_lowpart_p): Likewise.
        (get_ref_base_and_offset): New function to determine the base RTX
        and byte offset of an arbitrary expression, typically a SUBREG.
        * rtlanal.h (get_ref_base_and_offset): Prototype here.
        * simplify-rtx.cc (simplify_binary_operation_1) <case VEC_CONCAT>:
        Generalize the existing (vec_concat (first_half) (second_half))
        optimization using the new get_ref_base_and_offset function.

gcc/testsuite/ChangeLog
        PR target/48609
        * gcc.target/i386/pr48609.c: New test case.


Thanks again (especially to Richard Sandiford).
Roger
--
patchsr3.txt (text/plain, 7.1 KB)
diff --git a/gcc/rtl.cc b/gcc/rtl.cc
index 3839c364571..dac230b2159 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 e0311189db4..0edea426783 100644
--- a/gcc/rtl.h
+++ b/gcc/rtl.h
@@ -3092,7 +3092,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..d897dc400ea 100644
--- a/gcc/rtlanal.cc
+++ b/gcc/rtlanal.cc
@@ -7057,14 +7057,12 @@ register_asm_p (const_rtx x)
 bool
 vec_series_highpart_p (machine_mode result_mode, machine_mode op_mode, rtx sel)
 {
-  int nunits;
-  if (GET_MODE_NUNITS (op_mode).is_constant (&nunits)
-      && targetm.can_change_mode_class (op_mode, result_mode, ALL_REGS))
-    {
-      int offset = BYTES_BIG_ENDIAN ? 0 : nunits - XVECLEN (sel, 0);
-      return rtvec_series_p (XVEC (sel, 0), offset);
-    }
-  return false;
+  if (!targetm.can_change_mode_class (op_mode, result_mode, ALL_REGS))
+    return false;
+  if (BYTES_BIG_ENDIAN)
+    return rtvec_series_p (XVEC (sel, 0), 0);
+  poly_int64 offset = GET_MODE_NUNITS (op_mode) - XVECLEN (sel, 0);
+  return rtvec_series_p (XVEC (sel, 0), offset);
 }
 
 /* Return true if, for all OP of mode OP_MODE:
@@ -7076,14 +7074,12 @@ vec_series_highpart_p (machine_mode result_mode, machine_mode op_mode, rtx sel)
 bool
 vec_series_lowpart_p (machine_mode result_mode, machine_mode op_mode, rtx sel)
 {
-  int nunits;
-  if (GET_MODE_NUNITS (op_mode).is_constant (&nunits)
-      && targetm.can_change_mode_class (op_mode, result_mode, ALL_REGS))
-    {
-      int offset = BYTES_BIG_ENDIAN ? nunits - XVECLEN (sel, 0) : 0;
-      return rtvec_series_p (XVEC (sel, 0), offset);
-    }
-  return false;
+  if (!targetm.can_change_mode_class (op_mode, result_mode, ALL_REGS))
+    return false;
+  if (!BYTES_BIG_ENDIAN)
+    return rtvec_series_p (XVEC (sel, 0), 0);
+  poly_int64 offset = GET_MODE_NUNITS (op_mode) - XVECLEN (sel, 0);
+  return rtvec_series_p (XVEC (sel, 0), offset);
 }
 
 /* Return true if X contains a paradoxical subreg.  */
@@ -7100,3 +7096,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;
+   }
+}
diff --git a/gcc/rtlanal.h b/gcc/rtlanal.h
index 04c3b1b9a59..99addaa80ff 100644
--- a/gcc/rtlanal.h
+++ b/gcc/rtlanal.h
@@ -336,8 +336,12 @@ vec_series_highpart_p (machine_mode result_mode, machine_mode op_mode,
 		       rtx sel);
 
 bool
-vec_series_lowpart_p (machine_mode result_mode, machine_mode op_mode, rtx sel);
+vec_series_lowpart_p (machine_mode result_mode, machine_mode op_mode,
+		      rtx sel);
 
 bool
 contains_paradoxical_subreg_p (rtx x);
+
+rtx
+get_ref_base_and_offset (rtx x, poly_uint64 *offset_ptr);
 #endif
diff --git a/gcc/simplify-rtx.cc b/gcc/simplify-rtx.cc
index 882a11c5760..1b36f2865ca 100644
--- a/gcc/simplify-rtx.cc
+++ b/gcc/simplify-rtx.cc
@@ -5586,17 +5586,26 @@ simplify_ashift:
 	    return simplify_gen_binary (VEC_SELECT, mode, XEXP (trueop0, 0),
 					gen_rtx_PARALLEL (VOIDmode, vec));
 	  }
-	/* (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);
+	/* (vec_concat:N
+	     (subreg:N/2 OP 0)
+	     (subreg:N/2 OP N/2)) --> OP
+	   i.e. where concatenating the first and second halves of the
+	   same object OP.  */
+	{
+	  poly_uint64 offset = 0u;
+	  rtx base0 = get_ref_base_and_offset (trueop0, &offset);
+	  if (known_eq (offset, 0u)
+	      && known_eq (GET_MODE_SIZE (GET_MODE (base0)),
+			   GET_MODE_SIZE (mode)))
+	    {
+	      rtx base1 = get_ref_base_and_offset (trueop1, &offset);
+	      if (rtx_equal_p (base0, base1)
+		  && known_eq (offset, GET_MODE_SIZE (op0_mode))
+		  && !side_effects_p (trueop0)
+		  && !side_effects_p (trueop1))
+		return gen_lowpart (mode, base0);
+	    }
+	}
       }
       return 0;
 
diff --git a/gcc/testsuite/gcc.target/i386/pr48609.c b/gcc/testsuite/gcc.target/i386/pr48609.c
new file mode 100644
index 00000000000..8af9d883761
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr48609.c
@@ -0,0 +1,13 @@
+/* PR target/48609 */
+/* { dg-do compile { target { ! ia32 } } } */
+/* { dg-options "-O2 -msse2" } */
+typedef _Complex float SCtype;
+extern SCtype bar;
+void foo (SCtype x)
+{
+  bar = x;
+}
+
+/* { dg-final { scan-assembler-not "movdqa" } } */
+/* { dg-final { scan-assembler-not "shufps" } } */
+/* { dg-final { scan-assembler-not "unpcklps" } } */
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.