On Thu, 15 Oct 2026, Richard Biener wrote:
  > > +       if (nelts != op2_nelts || vc_nelts != op2_nelts)
  > > +    {
  > > +      /* Only merge permutes of different widths if the target
  > > +         handles the result.  */
  > > +      if (can_vec_perm_const_p (perm_mode, op2_mode, new_sel, false))
  > > +        new_vcst = vec_perm_indices_to_tree (TREE_TYPE (@5), new_sel);
  >
  > So what's the reason here?  Because vector lowering doesn't handle
  > them?  Because when we arrive here this means that at least one
  > of the original permutes had nelts != op_nelts, no?  So the check
  > below would eventually have one supported nelts == op_nelts
  > and one unsupported nelts != op_nelts - why would we not replace
  > this with a single unsupported nlets != op_nelts permute?

  There is no good reason; it was just caution.  Vector lowering does
  handle a constant permute whose inputs are wider than its result (it
  indexes by the input length), and after lowering every permute that is
  left is supported, so the general rule can only merge into a supported
  permute there anyway.

  v4 drops the special case: the general rule now applies whatever the
  widths, with the new permute queried in its own mode.  It is rebased
  onto trunk without 1/2, which you have pushed - thanks.  I'll post it
  as soon as I've sent this.

  Thanks,
  Igor


________________________________
From: Richard Biener <[email protected]>
Sent: Thursday, October 15, 2026 1:51:58 AM
To: Igor Shevlyakov
Cc: [email protected]
Subject: Re: [PATCH v3 2/2] middle-end: Fix VEC_PERM_EXPR merges of 
different-length vectors [PR127862]

[EXTERNAL] Caution: This email originated from outside of the organization. Do 
not click links or open attachments unless you recognize the sender and know 
the content is safe.

On Wed, 14 Oct 2026, Igor Shevlyakov wrote:

> Since GCC 14 a VEC_PERM_EXPR with a constant selector may have inputs
> with a different number of elements than its result.  The match.pd
> patterns that merge a VEC_PERM_EXPR into a VEC_PERM_EXPR using it, and
> forwprop's identity check for two consecutive permutes, still assume
> that every permute and input involved has the same number of elements.
>
> On aarch64 this happens for plain SVE intrinsics: svld1rq folds to a
> permute from a 128-bit vector to the SVE vector, and with
> -msve-vector-bits=256 a following svrev, svzip1, svuzp1 or svtrn2 of it
> is merged into an invalid permute.  That fails GIMPLE verification, and
> without checking it is miscompiled or ICEs in expand_vec_perm_const.
>
> - Merging c = <a, b> into d = <c, c> builds the new selector with d's
>   element count as the number of elements per input, and emits the new
>   permute with a's type but d's selector length.  That is invalid
>   GIMPLE: the verifier rejects it, and without checking it is
>   miscompiled or ICEs during expansion.
> - Merging c = <a, b> into d = <x, c> or <c, x> builds the new selector
>   with d's element count as the number of elements per input too, so
>   when d is narrower than c, lanes of c are taken from x: silent wrong
>   code.  When c and a differ in length, the new permute mixes inputs x
>   and a of different widths.
> - is_combined_permutation_identity folds the outer mask through the
>   inner one with the inner mask's type and reduces the result by its
>   length, so mixed widths ICE in fold_vec_perm or wrongly look like an
>   identity (a V2HI permute of a V2HI permute of two V4HI vectors gets
>   replaced by one of the V4HI inputs).
>
> Build each selector with the element count of its own inputs.  In the
> <c, c> merge, emit the new permute as a vector of a's element type with
> d's length, which is d's type unless c is view-converted to a different
> element type, and when the widths differ only merge if the target
> supports the result.  In the <x, c>
> and <c, x> merges, require a and b to have x's type and query the
> inner permute with its own result mode.  In forwprop, leave permutes of
> different widths to match.pd.
>
> Assisted-by: Claude Opus 5.5
>
>        PR middle-end/127862
>
> gcc/ChangeLog:
>
>        * match.pd (vec_perm (vec_perm) merges): Handle inputs and results
>        with different numbers of elements.
>        * tree-ssa-forwprop.cc (simplify_permutation): Don't check for an
>        identity when the permutes have different widths.
>
> gcc/testsuite/ChangeLog:
>
>        * gcc.dg/torture/vec-perm-merge-wide-input-1.c: New test.
>        * gcc.dg/torture/vec-perm-merge-wide-input-2.c: New test.
>        * gcc.dg/torture/vec-perm-merge-wide-input-3.c: New test.
>        * gcc.target/aarch64/sve/pr127862.c: New test.
>        * gcc.target/aarch64/sve/pr127862_run.c: New test.
>
> Signed-off-by: Igor Shevlyakov <[email protected]>
> ---
>  gcc/match.pd                                  |  67 ++-
>  .../torture/vec-perm-merge-wide-input-1.c     | 193 +++++++++
>  .../torture/vec-perm-merge-wide-input-2.c     |  64 +++
>  .../torture/vec-perm-merge-wide-input-3.c     | 382 ++++++++++++++++++
>  .../gcc.target/aarch64/sve/pr127862.c         |  53 +++
>  .../gcc.target/aarch64/sve/pr127862_run.c     |  46 +++
>  gcc/tree-ssa-forwprop.cc                      |   9 +
>  7 files changed, 797 insertions(+), 17 deletions(-)
>  create mode 100644 gcc/testsuite/gcc.dg/torture/vec-perm-merge-wide-input-1.c
>  create mode 100644 gcc/testsuite/gcc.dg/torture/vec-perm-merge-wide-input-2.c
>  create mode 100644 gcc/testsuite/gcc.dg/torture/vec-perm-merge-wide-input-3.c
>  create mode 100644 gcc/testsuite/gcc.target/aarch64/sve/pr127862.c
>  create mode 100644 gcc/testsuite/gcc.target/aarch64/sve/pr127862_run.c
>
> diff --git a/gcc/match.pd b/gcc/match.pd
> index 1cb710974f7..beafd894094 100644
> --- a/gcc/match.pd
> +++ b/gcc/match.pd
> @@ -12871,11 +12871,16 @@ and,
>     to
>       result = VIEW_CONVERT_EXPR <VEC_PERM_EXPR <op2, op3, new_sel>>;
>     The VIEW_CONVERT_EXPR may change the element type but not the element
> -   count.  */
> +   count.  op2 and op3 have the same number of elements, but inner may have
> +   a different number than op2 and op3, and result a different number
> +   than inner, so index each selector by the element count of its own
> +   inputs.  */
>
>  (simplify
>   (vec_perm (view_convert?@0 (vec_perm@1 @2 @3 VECTOR_CST@4)) @0 VECTOR_CST@5)
> - (if (TYPE_VECTOR_SUBPARTS (type).is_constant ())
> + (if (TYPE_VECTOR_SUBPARTS (type).is_constant ()
> +      && TYPE_VECTOR_SUBPARTS (TREE_TYPE (@0)).is_constant ()
> +      && TYPE_VECTOR_SUBPARTS (TREE_TYPE (@2)).is_constant ())
>    (with
>     {
>       machine_mode result_mode = TYPE_MODE (type);
> @@ -12883,6 +12888,8 @@ and,
>       machine_mode inner_mode = TYPE_MODE (TREE_TYPE (@1));
>       machine_mode op2_mode = TYPE_MODE (TREE_TYPE (@2));
>       int nelts = TYPE_VECTOR_SUBPARTS (type).to_constant ();
> +     int vc_nelts = TYPE_VECTOR_SUBPARTS (TREE_TYPE (@0)).to_constant ();
> +     int op2_nelts = TYPE_VECTOR_SUBPARTS (TREE_TYPE (@2)).to_constant ();
>       vec_perm_indices sel4, sel5;
>     }
>     (if (tree_to_vec_perm_indices (&sel4, @2, @3, @4)
> @@ -12895,24 +12902,40 @@ and,
>         for (int i = 0; i < nelts; i++)
>         new_builder.quick_push (sel4[sel5[i].to_constant ()]);
>
> -       vec_perm_indices new_sel (new_builder, 2, nelts);
> +       vec_perm_indices new_sel (new_builder, 2, sel4.nelts_per_input ());
> +
> +       /* The new permute has the element count of result and the element
> +       type of op2, so it has the type of result unless the
> +       VIEW_CONVERT_EXPR changes the element type.  */
> +       tree perm_type = type;
> +       if (!types_match (TREE_TYPE (type), TREE_TYPE (TREE_TYPE (@2))))
> +      perm_type = build_vector_type (TREE_TYPE (TREE_TYPE (@2)), nelts);
> +       machine_mode perm_mode = TYPE_MODE (perm_type);
>
>         tree new_vcst = NULL_TREE;
> +       if (nelts != op2_nelts || vc_nelts != op2_nelts)
> +      {
> +        /* Only merge permutes of different widths if the target
> +           handles the result.  */
> +        if (can_vec_perm_const_p (perm_mode, op2_mode, new_sel, false))
> +          new_vcst = vec_perm_indices_to_tree (TREE_TYPE (@5), new_sel);

So what's the reason here?  Because vector lowering doesn't handle
them?  Because when we arrive here this means that at least one
of the original permutes had nelts != op_nelts, no?  So the check
below would eventually have one supported nelts == op_nelts
and one unsupported nelts != op_nelts - why would we not replace
this with a single unsupported nlets != op_nelts permute?

The rest of the patch looks OK.

> +      }
>         /* If the new VEC_PERM_EXPR can't be handled but both
>          original VEC_PERM_EXPRs can, punt.
>          If one or both of the original VEC_PERM_EXPRs can't be
>          handled and the new one can't be either, don't increase
> -       number of VEC_PERM_EXPRs that can't be handled.
> -       The new VEC_PERM_EXPR has the type of op2.  */
> -       if (can_vec_perm_const_p (op2_mode, op2_mode, new_sel, false)
> -        || (single_use (@0)
> -            ? (!can_vec_perm_const_p (inner_mode, op2_mode, sel4, true)
> -               || !can_vec_perm_const_p (result_mode, vc_mode, sel5, true))
> -            : !can_vec_perm_const_p (result_mode, vc_mode, sel5, true)))
> +       number of VEC_PERM_EXPRs that can't be handled.  */
> +       else if (can_vec_perm_const_p (perm_mode, op2_mode, new_sel, false)
> +             || (single_use (@0)
> +                 ? (!can_vec_perm_const_p (inner_mode, op2_mode, sel4, true)
> +                    || !can_vec_perm_const_p (result_mode, vc_mode, sel5,
> +                                              true))
> +                 : !can_vec_perm_const_p (result_mode, vc_mode, sel5,
> +                                          true)))
>         new_vcst = vec_perm_indices_to_tree (TREE_TYPE (@5), new_sel);
>       }
>       (if (new_vcst)
> -      (view_convert (vec_perm @2 @3 { new_vcst; }))))))))
> +      (view_convert (vec_perm:perm_type @2 @3 { new_vcst; }))))))))
>
>  /* Merge
>       c = VEC_PERM_EXPR <a, b, VCST0>;
> @@ -12924,11 +12947,16 @@ and,
>
>  (simplify
>   (vec_perm @5 (vec_perm@0 @1 @2 VECTOR_CST@3) VECTOR_CST@4)
> - (if (TYPE_VECTOR_SUBPARTS (type).is_constant ())
> + /* The new permute replaces c by a or b, which therefore must have the
> +    type of x.  c may have more elements than d.  */
> + (if (TYPE_VECTOR_SUBPARTS (type).is_constant ()
> +      && TYPE_VECTOR_SUBPARTS (TREE_TYPE (@0)).is_constant ()
> +      && types_match (@1, @5))
>    (with
>     {
>       machine_mode result_mode = TYPE_MODE (type);
>       machine_mode op_mode = TYPE_MODE (TREE_TYPE (@1));
> +     machine_mode c_mode = TYPE_MODE (TREE_TYPE (@0));
>       int nelts = TYPE_VECTOR_SUBPARTS (type).to_constant ();
>       vec_perm_indices sel0, sel1;
>     }
> @@ -12961,7 +12989,7 @@ and,
>       (if (use_1 ^ use_2)
>        (with
>         {
> -      vec_perm_indices sel2 (builder2, 2, nelts);
> +      vec_perm_indices sel2 (builder2, 2, sel1.nelts_per_input ());
>         tree op0 = NULL_TREE;
>         /* If the new VEC_PERM_EXPR can't be handled but both
>            original VEC_PERM_EXPRs can, punt.
> @@ -12970,7 +12998,7 @@ and,
>            number of VEC_PERM_EXPRs that can't be handled.  */
>         if (can_vec_perm_const_p (result_mode, op_mode, sel2, false)
>             || (single_use (@0)
> -              ? (!can_vec_perm_const_p (result_mode, op_mode, sel0, true)
> +              ? (!can_vec_perm_const_p (c_mode, op_mode, sel0, true)
>                    || !can_vec_perm_const_p (result_mode, op_mode, sel1, 
> true))
>                 : !can_vec_perm_const_p (result_mode, op_mode, sel1, true)))
>           op0 = vec_perm_indices_to_tree (TREE_TYPE (@4), sel2);
> @@ -12986,11 +13014,16 @@ and,
>
>  (simplify
>   (vec_perm (vec_perm@0 @1 @2 VECTOR_CST@3) @5 VECTOR_CST@4)
> - (if (TYPE_VECTOR_SUBPARTS (type).is_constant ())
> + /* The new permute replaces c by a or b, which therefore must have the
> +    type of x.  c may have more elements than d.  */
> + (if (TYPE_VECTOR_SUBPARTS (type).is_constant ()
> +      && TYPE_VECTOR_SUBPARTS (TREE_TYPE (@0)).is_constant ()
> +      && types_match (@1, @5))
>    (with
>     {
>       machine_mode result_mode = TYPE_MODE (type);
>       machine_mode op_mode = TYPE_MODE (TREE_TYPE (@1));
> +     machine_mode c_mode = TYPE_MODE (TREE_TYPE (@0));
>       int nelts = TYPE_VECTOR_SUBPARTS (type).to_constant ();
>       vec_perm_indices sel0, sel1;
>     }
> @@ -13022,7 +13055,7 @@ and,
>       (if (use_1 ^ use_2)
>        (with
>         {
> -      vec_perm_indices sel2 (builder2, 2, nelts);
> +      vec_perm_indices sel2 (builder2, 2, sel1.nelts_per_input ());
>         tree op0 = NULL_TREE;
>         /* If the new VEC_PERM_EXPR can't be handled but both
>            original VEC_PERM_EXPRs can, punt.
> @@ -13031,7 +13064,7 @@ and,
>            number of VEC_PERM_EXPRs that can't be handled.  */
>         if (can_vec_perm_const_p (result_mode, op_mode, sel2, false)
>             || (single_use (@0)
> -              ? (!can_vec_perm_const_p (result_mode, op_mode, sel0, true)
> +              ? (!can_vec_perm_const_p (c_mode, op_mode, sel0, true)
>                    || !can_vec_perm_const_p (result_mode, op_mode, sel1, 
> true))
>                 : !can_vec_perm_const_p (result_mode, op_mode, sel1, true)))
>           op0 = vec_perm_indices_to_tree (TREE_TYPE (@4), sel2);
> diff --git a/gcc/testsuite/gcc.dg/torture/vec-perm-merge-wide-input-1.c 
> b/gcc/testsuite/gcc.dg/torture/vec-perm-merge-wide-input-1.c
> new file mode 100644
> index 00000000000..b047c05bc9a
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/torture/vec-perm-merge-wide-input-1.c
> @@ -0,0 +1,193 @@
> +/* VEC_PERM_EXPR merges with inputs wider than the result.  */
> +/* { dg-do run } */
> +/* { dg-additional-options "-fgimple -Wno-psabi" } */
> +
> +typedef int v4si __attribute__((vector_size(16)));
> +typedef int v8si __attribute__((vector_size(32)));
> +typedef int v16si __attribute__((vector_size(64)));
> +typedef float v4sf __attribute__((vector_size(16)));
> +typedef float v8sf __attribute__((vector_size(32)));
> +
> +v8si a8, b8, x8;
> +v16si a16, b16;
> +v4si r4;
> +v8si r8;
> +v4sf f4;
> +
> +/* c = <a, b> v8si; d = <c, c> v4si.  */
> +void __attribute__((noipa)) __GIMPLE() fa1 ()
> +{
> +  v8si a_; v8si b_; v8si c_; v4si d_;
> +  a_ = a8; b_ = b8;
> +  c_ = __VEC_PERM (a_, b_, _Literal (v8si) { 8, 0, 9, 1, 10, 2, 11, 3 });
> +  d_ = __VEC_PERM (c_, c_, _Literal (v4si) { 1, 2, 3, 4 });
> +  r4 = d_;
> +}
> +
> +/* c = <a, b> v8si from v16si; d = <c, c> v8si.  */
> +void __attribute__((noipa)) __GIMPLE() fa2 ()
> +{
> +  v16si a_; v16si b_; v8si c_; v8si d_;
> +  a_ = a16; b_ = b16;
> +  c_ = __VEC_PERM (a_, b_, _Literal (v8si) { 31, 0, 17, 14, 16, 15, 1, 30 });
> +  d_ = __VEC_PERM (c_, c_, _Literal (v8si) { 7, 6, 5, 4, 3, 2, 1, 0 });
> +  r8 = d_;
> +}
> +
> +/* As fa1, through a VIEW_CONVERT_EXPR.  */
> +void __attribute__((noipa)) __GIMPLE() fa3 ()
> +{
> +  v8si a_; v8si b_; v8si c_; v8sf e_; v4sf d_;
> +  a_ = a8; b_ = b8;
> +  c_ = __VEC_PERM (a_, b_, _Literal (v8si) { 8, 0, 9, 1, 10, 2, 11, 3 });
> +  e_ = __VIEW_CONVERT <v8sf> (c_);
> +  d_ = __VEC_PERM (e_, e_, _Literal (v4si) { 7, 0, 5, 2 });
> +  f4 = d_;
> +}
> +
> +/* c = <a, b> v8si; d = <x, c> v4si; only a used from c.  */
> +void __attribute__((noipa)) __GIMPLE() fb1 ()
> +{
> +  v8si a_; v8si b_; v8si x_; v8si c_; v4si d_;
> +  a_ = a8; b_ = b8; x_ = x8;
> +  c_ = __VEC_PERM (a_, b_, _Literal (v8si) { 7, 6, 5, 4, 8, 9, 10, 11 });
> +  d_ = __VEC_PERM (x_, c_, _Literal (v4si) { 0, 9, 7, 11 });
> +  r4 = d_;
> +}
> +
> +/* c = <a, b> v8si from v16si; d = <x, c> v8si; only b used from c.  */
> +void __attribute__((noipa)) __GIMPLE() fb2 ()
> +{
> +  v16si a_; v16si b_; v8si x_; v8si c_; v8si d_;
> +  a_ = a16; b_ = b16; x_ = x8;
> +  c_ = __VEC_PERM (a_, b_, _Literal (v8si) { 31, 1, 17, 2, 16, 3, 20, 4 });
> +  d_ = __VEC_PERM (x_, c_, _Literal (v8si) { 0, 8, 1, 10, 2, 12, 3, 14 });
> +  r8 = d_;
> +}
> +
> +/* c = <a, b> v8si; d = <c, x> v4si; only b used from c.  */
> +void __attribute__((noipa)) __GIMPLE() fc1 ()
> +{
> +  v8si a_; v8si b_; v8si x_; v8si c_; v4si d_;
> +  a_ = a8; b_ = b8; x_ = x8;
> +  c_ = __VEC_PERM (a_, b_, _Literal (v8si) { 0, 1, 2, 3, 15, 14, 13, 12 });
> +  d_ = __VEC_PERM (c_, x_, _Literal (v4si) { 4, 9, 6, 15 });
> +  r4 = d_;
> +}
> +
> +/* c = <a, b> v8si from v16si; d = <c, x> v8si; only a used from c.  */
> +void __attribute__((noipa)) __GIMPLE() fc2 ()
> +{
> +  v16si a_; v16si b_; v8si x_; v8si c_; v8si d_;
> +  a_ = a16; b_ = b16; x_ = x8;
> +  c_ = __VEC_PERM (a_, b_, _Literal (v8si) { 15, 0, 13, 2, 11, 4, 9, 6 });
> +  d_ = __VEC_PERM (c_, x_, _Literal (v8si) { 0, 8, 2, 9, 4, 10, 6, 11 });
> +  r8 = d_;
> +}
> +
> +/* Reference VEC_PERM_EXPR: OUT[i] = concat (X, Y)[SEL[i] % (2 * NX)].  */
> +static void
> +perm (int *out, const int *x, const int *y, int nx, const int *sel, int n)
> +{
> +  int tmp[16];
> +  for (int i = 0; i < n; i++)
> +    {
> +      int j = sel[i] % (2 * nx);
> +      tmp[i] = j < nx ? x[j] : y[j - nx];
> +    }
> +  for (int i = 0; i < n; i++)
> +    out[i] = tmp[i];
> +}
> +
> +static int fails;
> +
> +static void
> +check (const char *name, const int *got, const int *want, int n)
> +{
> +  for (int i = 0; i < n; i++)
> +    if (got[i] != want[i])
> +      {
> +     __builtin_printf ("%s: lane %d got %d want %d\n", name, i, got[i],
> +                       want[i]);
> +     fails++;
> +     return;
> +      }
> +}
> +
> +int
> +main ()
> +{
> +  int c[8], d[8];
> +  for (int i = 0; i < 8; i++)
> +    {
> +      a8[i] = i + 1;
> +      b8[i] = i + 101;
> +      x8[i] = i + 201;
> +    }
> +  for (int i = 0; i < 16; i++)
> +    {
> +      a16[i] = i + 301;
> +      b16[i] = i + 401;
> +    }
> +
> +  {
> +    static const int s0[8] = { 8, 0, 9, 1, 10, 2, 11, 3 };
> +    static const int s1[4] = { 1, 2, 3, 4 };
> +    perm (c, (int *) &a8, (int *) &b8, 8, s0, 8);
> +    perm (d, c, c, 8, s1, 4);
> +    fa1 ();
> +    check ("fa1", (int *) &r4, d, 4);
> +  }
> +  {
> +    static const int s0[8] = { 31, 0, 17, 14, 16, 15, 1, 30 };
> +    static const int s1[8] = { 7, 6, 5, 4, 3, 2, 1, 0 };
> +    perm (c, (int *) &a16, (int *) &b16, 16, s0, 8);
> +    perm (d, c, c, 8, s1, 8);
> +    fa2 ();
> +    check ("fa2", (int *) &r8, d, 8);
> +  }
> +  {
> +    static const int s0[8] = { 8, 0, 9, 1, 10, 2, 11, 3 };
> +    static const int s1[4] = { 7, 0, 5, 2 };
> +    perm (c, (int *) &a8, (int *) &b8, 8, s0, 8);
> +    perm (d, c, c, 8, s1, 4);
> +    fa3 ();
> +    check ("fa3", (int *) &f4, d, 4);
> +  }
> +  {
> +    static const int s0[8] = { 7, 6, 5, 4, 8, 9, 10, 11 };
> +    static const int s1[4] = { 0, 9, 7, 11 };
> +    perm (c, (int *) &a8, (int *) &b8, 8, s0, 8);
> +    perm (d, (int *) &x8, c, 8, s1, 4);
> +    fb1 ();
> +    check ("fb1", (int *) &r4, d, 4);
> +  }
> +  {
> +    static const int s0[8] = { 31, 1, 17, 2, 16, 3, 20, 4 };
> +    static const int s1[8] = { 0, 8, 1, 10, 2, 12, 3, 14 };
> +    perm (c, (int *) &a16, (int *) &b16, 16, s0, 8);
> +    perm (d, (int *) &x8, c, 8, s1, 8);
> +    fb2 ();
> +    check ("fb2", (int *) &r8, d, 8);
> +  }
> +  {
> +    static const int s0[8] = { 0, 1, 2, 3, 15, 14, 13, 12 };
> +    static const int s1[4] = { 4, 9, 6, 15 };
> +    perm (c, (int *) &a8, (int *) &b8, 8, s0, 8);
> +    perm (d, c, (int *) &x8, 8, s1, 4);
> +    fc1 ();
> +    check ("fc1", (int *) &r4, d, 4);
> +  }
> +  {
> +    static const int s0[8] = { 15, 0, 13, 2, 11, 4, 9, 6 };
> +    static const int s1[8] = { 0, 8, 2, 9, 4, 10, 6, 11 };
> +    perm (c, (int *) &a16, (int *) &b16, 16, s0, 8);
> +    perm (d, c, (int *) &x8, 8, s1, 8);
> +    fc2 ();
> +    check ("fc2", (int *) &r8, d, 8);
> +  }
> +
> +  if (fails)
> +    __builtin_abort ();
> +  return 0;
> +}
> diff --git a/gcc/testsuite/gcc.dg/torture/vec-perm-merge-wide-input-2.c 
> b/gcc/testsuite/gcc.dg/torture/vec-perm-merge-wide-input-2.c
> new file mode 100644
> index 00000000000..6df62d3347d
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/torture/vec-perm-merge-wide-input-2.c
> @@ -0,0 +1,64 @@
> +/* forwprop must not treat permutes of different widths as an identity.  */
> +/* { dg-do run } */
> +/* { dg-additional-options "-fgimple -Wno-psabi" } */
> +
> +typedef float v8sf __attribute__((vector_size(32)));
> +typedef float v4sf __attribute__((vector_size(16)));
> +typedef float v2sf __attribute__((vector_size(8)));
> +typedef int v4si __attribute__((vector_size(16)));
> +typedef int v2si __attribute__((vector_size(8)));
> +typedef short v4hi __attribute__((vector_size(8)));
> +typedef short v2hi __attribute__((vector_size(4)));
> +
> +v8sf fa, fb;
> +v2sf fr;
> +v4hi ha, hb;
> +v2hi hr;
> +
> +/* The composed mask { 3, 4 } has fewer elements than the inner one.  */
> +void __attribute__((noipa)) __GIMPLE() f1 ()
> +{
> +  v8sf a_; v8sf b_; v4sf c_; v2sf d_;
> +  a_ = fa; b_ = fb;
> +  c_ = __VEC_PERM (a_, b_, _Literal (v4si) { 4, 6, 12, 3 });
> +  d_ = __VEC_PERM (c_, c_, _Literal (v2si) { 6, 0 });
> +  fr = d_;
> +}
> +
> +/* { 1, 0 } of { 7, 6 } looks like an identity on b if the indices are
> +   reduced by the length of c rather than of b.  */
> +void __attribute__((noipa)) __GIMPLE() f2 ()
> +{
> +  v4hi a_; v4hi b_; v2hi c_; v2hi d_;
> +  a_ = ha; b_ = hb;
> +  c_ = __VEC_PERM (a_, b_, _Literal (v2hi) { 7, 6 });
> +  d_ = __VEC_PERM (c_, c_, _Literal (v2hi) { 1, 2 });
> +  hr = d_;
> +}
> +
> +int
> +main ()
> +{
> +  for (int i = 0; i < 8; i++)
> +    {
> +      fa[i] = i + 1;
> +      fb[i] = i + 101;
> +    }
> +  for (int i = 0; i < 4; i++)
> +    {
> +      ha[i] = i + 1;
> +      hb[i] = i + 101;
> +    }
> +
> +  f1 ();
> +  /* c = { a4, a6, b4, a3 }, d = { c2, c0 }.  */
> +  if (fr[0] != 105 || fr[1] != 5)
> +    __builtin_abort ();
> +
> +  f2 ();
> +  /* c = { b3, b2 }, d = { c1, c0 }.  */
> +  if (hr[0] != 103 || hr[1] != 104)
> +    __builtin_abort ();
> +
> +  return 0;
> +}
> diff --git a/gcc/testsuite/gcc.dg/torture/vec-perm-merge-wide-input-3.c 
> b/gcc/testsuite/gcc.dg/torture/vec-perm-merge-wide-input-3.c
> new file mode 100644
> index 00000000000..337328136ec
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/torture/vec-perm-merge-wide-input-3.c
> @@ -0,0 +1,382 @@
> +/* VEC_PERM_EXPR merges of c = <a, b> into d = <c, c> with c wider than, as
> +   wide as and narrower than a, and d wider than, as wide as and narrower
> +   than c.  */
> +/* { dg-do run } */
> +/* { dg-additional-options "-fgimple -Wno-psabi" } */
> +
> +typedef int v2si __attribute__((vector_size(8)));
> +typedef int v4si __attribute__((vector_size(16)));
> +typedef int v8si __attribute__((vector_size(32)));
> +typedef int v16si __attribute__((vector_size(64)));
> +typedef int v32si __attribute__((vector_size(128)));
> +
> +v8si a, b;
> +v2si r2;
> +v4si r4;
> +v8si r8;
> +v16si r16;
> +v32si r32;
> +
> +/* c wider than a (v8si -> v16si), d wider than c (-> v32si).  */
> +
> +void __attribute__((noipa)) __GIMPLE() f1 ()
> +{
> +  v8si a_; v8si b_; v16si c_; v32si d_;
> +  a_ = a; b_ = b;
> +  c_ = __VEC_PERM (a_, b_, _Literal (v16si) {
> +      14, 14, 14, 6, 5, 15, 5, 3, 14, 9, 4, 2, 1, 12, 14, 5 });
> +  d_ = __VEC_PERM (c_, c_, _Literal (v32si) {
> +      0, 4, 3, 2, 12, 15, 1, 29, 20, 28, 12, 14, 18, 31, 0, 5, 29, 17, 26, 5,
> +      16, 20, 14, 18, 1, 4, 6, 25, 6, 18, 24, 4 });
> +  r32 = d_;
> +}
> +
> +void __attribute__((noipa)) __GIMPLE() f2 ()
> +{
> +  v8si a_; v8si b_; v16si c_; v32si d_;
> +  a_ = a; b_ = b;
> +  c_ = __VEC_PERM (a_, b_, _Literal (v16si) {
> +      0, 0, 6, 6, 1, 15, 12, 12, 13, 2, 6, 8, 10, 2, 9, 10 });
> +  d_ = __VEC_PERM (c_, c_, _Literal (v32si) {
> +      0, 26, 7, 8, 15, 6, 0, 3, 29, 31, 11, 12, 28, 12, 8, 26, 24, 7, 25, 26,
> +      13, 0, 17, 19, 1, 13, 11, 25, 6, 2, 9, 13 });
> +  r32 = d_;
> +}
> +
> +/* c wider than a (v8si -> v16si), d as wide as c (-> v16si).  */
> +
> +void __attribute__((noipa)) __GIMPLE() f3 ()
> +{
> +  v8si a_; v8si b_; v16si c_; v16si d_;
> +  a_ = a; b_ = b;
> +  c_ = __VEC_PERM (a_, b_, _Literal (v16si) {
> +      14, 8, 0, 10, 9, 12, 2, 2, 2, 6, 7, 0, 11, 11, 14, 4 });
> +  d_ = __VEC_PERM (c_, c_, _Literal (v16si) {
> +      30, 8, 24, 11, 9, 19, 14, 15, 12, 10, 12, 24, 30, 5, 26, 3 });
> +  r16 = d_;
> +}
> +
> +void __attribute__((noipa)) __GIMPLE() f4 ()
> +{
> +  v8si a_; v8si b_; v16si c_; v16si d_;
> +  a_ = a; b_ = b;
> +  c_ = __VEC_PERM (a_, b_, _Literal (v16si) {
> +      3, 3, 1, 8, 7, 12, 8, 13, 15, 9, 5, 2, 4, 7, 15, 2 });
> +  d_ = __VEC_PERM (c_, c_, _Literal (v16si) {
> +      17, 13, 13, 1, 4, 17, 26, 28, 15, 3, 2, 11, 18, 23, 8, 5 });
> +  r16 = d_;
> +}
> +
> +/* c wider than a (v8si -> v16si), d narrower than c (-> v8si).  */
> +
> +void __attribute__((noipa)) __GIMPLE() f5 ()
> +{
> +  v8si a_; v8si b_; v16si c_; v8si d_;
> +  a_ = a; b_ = b;
> +  c_ = __VEC_PERM (a_, b_, _Literal (v16si) {
> +      11, 4, 14, 10, 4, 1, 0, 15, 11, 9, 1, 0, 2, 15, 2, 9 });
> +  d_ = __VEC_PERM (c_, c_, _Literal (v8si) { 20, 8, 4, 4, 28, 23, 2, 8 });
> +  r8 = d_;
> +}
> +
> +void __attribute__((noipa)) __GIMPLE() f6 ()
> +{
> +  v8si a_; v8si b_; v16si c_; v8si d_;
> +  a_ = a; b_ = b;
> +  c_ = __VEC_PERM (a_, b_, _Literal (v16si) {
> +      10, 11, 2, 15, 2, 13, 0, 15, 0, 12, 12, 0, 2, 2, 2, 3 });
> +  d_ = __VEC_PERM (c_, c_, _Literal (v8si) { 16, 26, 21, 24, 29, 28, 29, 5 
> });
> +  r8 = d_;
> +}
> +
> +/* c as wide as a (v8si -> v8si), d wider than c (-> v16si).  */
> +
> +void __attribute__((noipa)) __GIMPLE() f7 ()
> +{
> +  v8si a_; v8si b_; v8si c_; v16si d_;
> +  a_ = a; b_ = b;
> +  c_ = __VEC_PERM (a_, b_, _Literal (v8si) { 0, 9, 2, 15, 0, 7, 3, 15 });
> +  d_ = __VEC_PERM (c_, c_, _Literal (v16si) {
> +      15, 8, 0, 11, 9, 4, 6, 5, 10, 14, 15, 7, 10, 12, 8, 6 });
> +  r16 = d_;
> +}
> +
> +void __attribute__((noipa)) __GIMPLE() f8 ()
> +{
> +  v8si a_; v8si b_; v8si c_; v16si d_;
> +  a_ = a; b_ = b;
> +  c_ = __VEC_PERM (a_, b_, _Literal (v8si) { 13, 6, 6, 12, 7, 10, 6, 4 });
> +  d_ = __VEC_PERM (c_, c_, _Literal (v16si) {
> +      4, 15, 11, 1, 2, 8, 5, 3, 14, 15, 8, 6, 13, 12, 15, 10 });
> +  r16 = d_;
> +}
> +
> +/* c as wide as a (v8si -> v8si), d as wide as c (-> v8si).  */
> +
> +void __attribute__((noipa)) __GIMPLE() f9 ()
> +{
> +  v8si a_; v8si b_; v8si c_; v8si d_;
> +  a_ = a; b_ = b;
> +  c_ = __VEC_PERM (a_, b_, _Literal (v8si) { 14, 10, 2, 1, 8, 1, 8, 11 });
> +  d_ = __VEC_PERM (c_, c_, _Literal (v8si) { 9, 0, 4, 12, 14, 6, 0, 8 });
> +  r8 = d_;
> +}
> +
> +void __attribute__((noipa)) __GIMPLE() f10 ()
> +{
> +  v8si a_; v8si b_; v8si c_; v8si d_;
> +  a_ = a; b_ = b;
> +  c_ = __VEC_PERM (a_, b_, _Literal (v8si) { 7, 4, 1, 3, 14, 3, 11, 2 });
> +  d_ = __VEC_PERM (c_, c_, _Literal (v8si) { 6, 6, 15, 8, 5, 0, 15, 1 });
> +  r8 = d_;
> +}
> +
> +/* c as wide as a (v8si -> v8si), d narrower than c (-> v4si).  */
> +
> +void __attribute__((noipa)) __GIMPLE() f11 ()
> +{
> +  v8si a_; v8si b_; v8si c_; v4si d_;
> +  a_ = a; b_ = b;
> +  c_ = __VEC_PERM (a_, b_, _Literal (v8si) { 5, 7, 8, 11, 5, 12, 7, 2 });
> +  d_ = __VEC_PERM (c_, c_, _Literal (v4si) { 13, 12, 4, 14 });
> +  r4 = d_;
> +}
> +
> +void __attribute__((noipa)) __GIMPLE() f12 ()
> +{
> +  v8si a_; v8si b_; v8si c_; v4si d_;
> +  a_ = a; b_ = b;
> +  c_ = __VEC_PERM (a_, b_, _Literal (v8si) { 14, 6, 0, 12, 10, 14, 10, 6 });
> +  d_ = __VEC_PERM (c_, c_, _Literal (v4si) { 3, 3, 6, 7 });
> +  r4 = d_;
> +}
> +
> +/* c narrower than a (v8si -> v4si), d wider than c (-> v8si).  */
> +
> +void __attribute__((noipa)) __GIMPLE() f13 ()
> +{
> +  v8si a_; v8si b_; v4si c_; v8si d_;
> +  a_ = a; b_ = b;
> +  c_ = __VEC_PERM (a_, b_, _Literal (v4si) { 12, 2, 9, 10 });
> +  d_ = __VEC_PERM (c_, c_, _Literal (v8si) { 4, 0, 5, 1, 0, 7, 5, 6 });
> +  r8 = d_;
> +}
> +
> +void __attribute__((noipa)) __GIMPLE() f14 ()
> +{
> +  v8si a_; v8si b_; v4si c_; v8si d_;
> +  a_ = a; b_ = b;
> +  c_ = __VEC_PERM (a_, b_, _Literal (v4si) { 8, 15, 0, 6 });
> +  d_ = __VEC_PERM (c_, c_, _Literal (v8si) { 1, 6, 0, 2, 5, 2, 7, 2 });
> +  r8 = d_;
> +}
> +
> +/* c narrower than a (v8si -> v4si), d as wide as c (-> v4si).  */
> +
> +void __attribute__((noipa)) __GIMPLE() f15 ()
> +{
> +  v8si a_; v8si b_; v4si c_; v4si d_;
> +  a_ = a; b_ = b;
> +  c_ = __VEC_PERM (a_, b_, _Literal (v4si) { 14, 15, 2, 7 });
> +  d_ = __VEC_PERM (c_, c_, _Literal (v4si) { 7, 4, 2, 4 });
> +  r4 = d_;
> +}
> +
> +void __attribute__((noipa)) __GIMPLE() f16 ()
> +{
> +  v8si a_; v8si b_; v4si c_; v4si d_;
> +  a_ = a; b_ = b;
> +  c_ = __VEC_PERM (a_, b_, _Literal (v4si) { 9, 12, 6, 9 });
> +  d_ = __VEC_PERM (c_, c_, _Literal (v4si) { 2, 4, 7, 3 });
> +  r4 = d_;
> +}
> +
> +/* c narrower than a (v8si -> v4si), d narrower than c (-> v2si).  */
> +
> +void __attribute__((noipa)) __GIMPLE() f17 ()
> +{
> +  v8si a_; v8si b_; v4si c_; v2si d_;
> +  a_ = a; b_ = b;
> +  c_ = __VEC_PERM (a_, b_, _Literal (v4si) { 13, 3, 0, 12 });
> +  d_ = __VEC_PERM (c_, c_, _Literal (v2si) { 0, 0 });
> +  r2 = d_;
> +}
> +
> +void __attribute__((noipa)) __GIMPLE() f18 ()
> +{
> +  v8si a_; v8si b_; v4si c_; v2si d_;
> +  a_ = a; b_ = b;
> +  c_ = __VEC_PERM (a_, b_, _Literal (v4si) { 12, 3, 15, 2 });
> +  d_ = __VEC_PERM (c_, c_, _Literal (v2si) { 2, 1 });
> +  r2 = d_;
> +}
> +
> +/* Reference VEC_PERM_EXPR: OUT[i] = concat (X, Y)[SEL[i] % (2 * NX)].  */
> +static void
> +perm (int *out, const int *x, const int *y, int nx, const int *sel, int n)
> +{
> +  int tmp[32];
> +  for (int i = 0; i < n; i++)
> +    {
> +      int j = sel[i] % (2 * nx);
> +      tmp[i] = j < nx ? x[j] : y[j - nx];
> +    }
> +  for (int i = 0; i < n; i++)
> +    out[i] = tmp[i];
> +}
> +
> +static int fails;
> +
> +static void
> +check (const char *name, const int *got, const int *sel0, int nc,
> +       const int *sel1, int nd)
> +{
> +  int c[32], want[32];
> +  perm (c, (int *) &a, (int *) &b, 8, sel0, nc);
> +  perm (want, c, c, nc, sel1, nd);
> +  for (int i = 0; i < nd; i++)
> +    if (got[i] != want[i])
> +      {
> +     __builtin_printf ("%s: lane %d got %d want %d\n", name, i, got[i],
> +                       want[i]);
> +     fails++;
> +     return;
> +      }
> +}
> +
> +int
> +main ()
> +{
> +  for (int i = 0; i < 8; i++)
> +    {
> +      a[i] = i + 1;
> +      b[i] = i + 101;
> +    }
> +
> +  {
> +    static const int s0[] = {
> +        14, 14, 14, 6, 5, 15, 5, 3, 14, 9, 4, 2, 1, 12, 14, 5 };
> +    static const int s1[] = {
> +        0, 4, 3, 2, 12, 15, 1, 29, 20, 28, 12, 14, 18, 31, 0, 5, 29, 17, 26,
> +        5, 16, 20, 14, 18, 1, 4, 6, 25, 6, 18, 24, 4 };
> +    f1 ();
> +    check ("f1", (int *) &r32, s0, 16, s1, 32);
> +  }
> +  {
> +    static const int s0[] = {
> +        0, 0, 6, 6, 1, 15, 12, 12, 13, 2, 6, 8, 10, 2, 9, 10 };
> +    static const int s1[] = {
> +        0, 26, 7, 8, 15, 6, 0, 3, 29, 31, 11, 12, 28, 12, 8, 26, 24, 7, 25,
> +        26, 13, 0, 17, 19, 1, 13, 11, 25, 6, 2, 9, 13 };
> +    f2 ();
> +    check ("f2", (int *) &r32, s0, 16, s1, 32);
> +  }
> +  {
> +    static const int s0[] = {
> +        14, 8, 0, 10, 9, 12, 2, 2, 2, 6, 7, 0, 11, 11, 14, 4 };
> +    static const int s1[] = {
> +        30, 8, 24, 11, 9, 19, 14, 15, 12, 10, 12, 24, 30, 5, 26, 3 };
> +    f3 ();
> +    check ("f3", (int *) &r16, s0, 16, s1, 16);
> +  }
> +  {
> +    static const int s0[] = {
> +        3, 3, 1, 8, 7, 12, 8, 13, 15, 9, 5, 2, 4, 7, 15, 2 };
> +    static const int s1[] = {
> +        17, 13, 13, 1, 4, 17, 26, 28, 15, 3, 2, 11, 18, 23, 8, 5 };
> +    f4 ();
> +    check ("f4", (int *) &r16, s0, 16, s1, 16);
> +  }
> +  {
> +    static const int s0[] = {
> +        11, 4, 14, 10, 4, 1, 0, 15, 11, 9, 1, 0, 2, 15, 2, 9 };
> +    static const int s1[] = { 20, 8, 4, 4, 28, 23, 2, 8 };
> +    f5 ();
> +    check ("f5", (int *) &r8, s0, 16, s1, 8);
> +  }
> +  {
> +    static const int s0[] = {
> +        10, 11, 2, 15, 2, 13, 0, 15, 0, 12, 12, 0, 2, 2, 2, 3 };
> +    static const int s1[] = { 16, 26, 21, 24, 29, 28, 29, 5 };
> +    f6 ();
> +    check ("f6", (int *) &r8, s0, 16, s1, 8);
> +  }
> +  {
> +    static const int s0[] = { 0, 9, 2, 15, 0, 7, 3, 15 };
> +    static const int s1[] = {
> +        15, 8, 0, 11, 9, 4, 6, 5, 10, 14, 15, 7, 10, 12, 8, 6 };
> +    f7 ();
> +    check ("f7", (int *) &r16, s0, 8, s1, 16);
> +  }
> +  {
> +    static const int s0[] = { 13, 6, 6, 12, 7, 10, 6, 4 };
> +    static const int s1[] = {
> +        4, 15, 11, 1, 2, 8, 5, 3, 14, 15, 8, 6, 13, 12, 15, 10 };
> +    f8 ();
> +    check ("f8", (int *) &r16, s0, 8, s1, 16);
> +  }
> +  {
> +    static const int s0[] = { 14, 10, 2, 1, 8, 1, 8, 11 };
> +    static const int s1[] = { 9, 0, 4, 12, 14, 6, 0, 8 };
> +    f9 ();
> +    check ("f9", (int *) &r8, s0, 8, s1, 8);
> +  }
> +  {
> +    static const int s0[] = { 7, 4, 1, 3, 14, 3, 11, 2 };
> +    static const int s1[] = { 6, 6, 15, 8, 5, 0, 15, 1 };
> +    f10 ();
> +    check ("f10", (int *) &r8, s0, 8, s1, 8);
> +  }
> +  {
> +    static const int s0[] = { 5, 7, 8, 11, 5, 12, 7, 2 };
> +    static const int s1[] = { 13, 12, 4, 14 };
> +    f11 ();
> +    check ("f11", (int *) &r4, s0, 8, s1, 4);
> +  }
> +  {
> +    static const int s0[] = { 14, 6, 0, 12, 10, 14, 10, 6 };
> +    static const int s1[] = { 3, 3, 6, 7 };
> +    f12 ();
> +    check ("f12", (int *) &r4, s0, 8, s1, 4);
> +  }
> +  {
> +    static const int s0[] = { 12, 2, 9, 10 };
> +    static const int s1[] = { 4, 0, 5, 1, 0, 7, 5, 6 };
> +    f13 ();
> +    check ("f13", (int *) &r8, s0, 4, s1, 8);
> +  }
> +  {
> +    static const int s0[] = { 8, 15, 0, 6 };
> +    static const int s1[] = { 1, 6, 0, 2, 5, 2, 7, 2 };
> +    f14 ();
> +    check ("f14", (int *) &r8, s0, 4, s1, 8);
> +  }
> +  {
> +    static const int s0[] = { 14, 15, 2, 7 };
> +    static const int s1[] = { 7, 4, 2, 4 };
> +    f15 ();
> +    check ("f15", (int *) &r4, s0, 4, s1, 4);
> +  }
> +  {
> +    static const int s0[] = { 9, 12, 6, 9 };
> +    static const int s1[] = { 2, 4, 7, 3 };
> +    f16 ();
> +    check ("f16", (int *) &r4, s0, 4, s1, 4);
> +  }
> +  {
> +    static const int s0[] = { 13, 3, 0, 12 };
> +    static const int s1[] = { 0, 0 };
> +    f17 ();
> +    check ("f17", (int *) &r2, s0, 4, s1, 2);
> +  }
> +  {
> +    static const int s0[] = { 12, 3, 15, 2 };
> +    static const int s1[] = { 2, 1 };
> +    f18 ();
> +    check ("f18", (int *) &r2, s0, 4, s1, 2);
> +  }
> +
> +  if (fails)
> +    __builtin_abort ();
> +  return 0;
> +}
> diff --git a/gcc/testsuite/gcc.target/aarch64/sve/pr127862.c 
> b/gcc/testsuite/gcc.target/aarch64/sve/pr127862.c
> new file mode 100644
> index 00000000000..4b088391fa7
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/aarch64/sve/pr127862.c
> @@ -0,0 +1,53 @@
> +/* VEC_PERM_EXPR merges with a result wider than the inputs.  */
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -msve-vector-bits=256" } */
> +
> +#include <arm_sve.h>
> +
> +/* svld1rq is c = VEC_PERM <a, a> from a 128-bit vector; d = <c, c>.  */
> +
> +svint32_t
> +f1 (int32_t *p)
> +{
> +  svint32_t c = svld1rq_s32 (svptrue_b8 (), p);
> +  return svrev_s32 (c);
> +}
> +
> +svint32_t
> +f2 (int32_t *p)
> +{
> +  svint32_t c = svld1rq_s32 (svptrue_b8 (), p);
> +  return svzip1_s32 (c, c);
> +}
> +
> +svint32_t
> +f3 (int32_t *p)
> +{
> +  svint32_t c = svld1rq_s32 (svptrue_b8 (), p);
> +  return svuzp2_s32 (c, c);
> +}
> +
> +/* d = <x, c>.  */
> +
> +svint32_t
> +f4 (int32_t *p, svint32_t x)
> +{
> +  svint32_t c = svld1rq_s32 (svptrue_b8 (), p);
> +  return svzip1_s32 (x, c);
> +}
> +
> +svint32_t
> +f5 (int32_t *p, svint32_t x)
> +{
> +  svint32_t c = svld1rq_s32 (svptrue_b8 (), p);
> +  return svuzp1_s32 (x, c);
> +}
> +
> +/* d = <c, x>.  */
> +
> +svint32_t
> +f6 (int32_t *p, svint32_t x)
> +{
> +  svint32_t c = svld1rq_s32 (svptrue_b8 (), p);
> +  return svtrn2_s32 (c, x);
> +}
> diff --git a/gcc/testsuite/gcc.target/aarch64/sve/pr127862_run.c 
> b/gcc/testsuite/gcc.target/aarch64/sve/pr127862_run.c
> new file mode 100644
> index 00000000000..abbec284cb8
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/aarch64/sve/pr127862_run.c
> @@ -0,0 +1,46 @@
> +/* VEC_PERM_EXPR merges with a result wider than the inputs.  */
> +/* { dg-do run { target aarch64_sve256_hw } } */
> +/* { dg-options "-O2 -msve-vector-bits=256" } */
> +
> +#include <arm_sve.h>
> +
> +int32_t p[4] = { 1, 2, 3, 4 };
> +int32_t xs[8] = { 11, 12, 13, 14, 15, 16, 17, 18 };
> +int32_t r[8];
> +
> +/* c = VEC_PERM <a, a>, d = VEC_PERM <c, c>.  */
> +void __attribute__((noipa))
> +f1 (int32_t *p)
> +{
> +  svint32_t c = svld1rq_s32 (svptrue_b8 (), p);
> +  svst1_s32 (svptrue_b8 (), r, svrev_s32 (c));
> +}
> +
> +/* c = VEC_PERM <a, a>, d = VEC_PERM <c, x>.  */
> +void __attribute__((noipa))
> +f2 (int32_t *p, svint32_t x)
> +{
> +  svint32_t c = svld1rq_s32 (svptrue_b8 (), p);
> +  svst1_s32 (svptrue_b8 (), r, svtrn2_s32 (c, x));
> +}
> +
> +static void
> +check (const int32_t *want)
> +{
> +  for (int i = 0; i < 8; i++)
> +    if (r[i] != want[i])
> +      __builtin_abort ();
> +}
> +
> +int
> +main (void)
> +{
> +  static const int32_t want1[8] = { 4, 3, 2, 1, 4, 3, 2, 1 };
> +  static const int32_t want2[8] = { 2, 12, 4, 14, 2, 16, 4, 18 };
> +
> +  f1 (p);
> +  check (want1);
> +  f2 (p, svld1_s32 (svptrue_b8 (), xs));
> +  check (want2);
> +  return 0;
> +}
> diff --git a/gcc/tree-ssa-forwprop.cc b/gcc/tree-ssa-forwprop.cc
> index fba4387f503..942b5df666e 100644
> --- a/gcc/tree-ssa-forwprop.cc
> +++ b/gcc/tree-ssa-forwprop.cc
> @@ -5030,6 +5030,15 @@ simplify_permutation (gimple_stmt_iterator *gsi)
>        op3 = gimple_assign_rhs3 (def_stmt);
>        if (TREE_CODE (op3) != VECTOR_CST)
>        return false;
> +      /* is_combined_permutation_identity assumes that both permutes and
> +      their inputs have the same number of elements.  Mixed widths are
> +      left to the match.pd VEC_PERM merge patterns.  */
> +      if (maybe_ne (TYPE_VECTOR_SUBPARTS (TREE_TYPE (op2)),
> +                 TYPE_VECTOR_SUBPARTS (TREE_TYPE (op3)))
> +       || maybe_ne (TYPE_VECTOR_SUBPARTS (TREE_TYPE (op0)),
> +                    TYPE_VECTOR_SUBPARTS
> +                      (TREE_TYPE (gimple_assign_rhs1 (def_stmt)))))
> +     return false;
>        ident = is_combined_permutation_identity (op3, op2);
>        if (!ident)
>        return false;
>

--
Richard Biener <[email protected]>
SUSE Software Solutions Germany GmbH,
Frankenstrasse 146, 90461 Nuernberg, Germany;
GF: Stefan Gaiser, Jochen Jaser, Abhinav Puri; (HRB 36809, AG Nuernberg)


Reply via email to