On Tue, 13 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 (view-converted to d's type as before), 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.
This was LLM assisted? > 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.target/aarch64/sve/pr127862.c: New test. > * gcc.target/aarch64/sve/pr127862_run.c: New test. > > Signed-off-by: Igor Shevlyakov <[email protected]> > --- > Bootstrapped and regtested on x86_64-pc-linux-gnu (-m32/-m64), no new > regressions. > > gcc/match.pd | 61 ++++-- > .../torture/vec-perm-merge-wide-input-1.c | 193 ++++++++++++++++++ > .../torture/vec-perm-merge-wide-input-2.c | 64 ++++++ > .../gcc.target/aarch64/sve/pr127862.c | 53 +++++ > .../gcc.target/aarch64/sve/pr127862_run.c | 46 +++++ > gcc/tree-ssa-forwprop.cc | 9 + > 6 files changed, 411 insertions(+), 15 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.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 6984af44582..11c1c6e6765 100644 > --- a/gcc/match.pd > +++ b/gcc/match.pd > @@ -12868,16 +12868,22 @@ and, > c = VEC_PERM_EXPR <a, b, VCST0>; > d = VEC_PERM_EXPR <c, c, VCST1>; > to > - d = VEC_PERM_EXPR <a, b, NEW_VCST>; */ > + d = VEC_PERM_EXPR <a, b, NEW_VCST>; > + a, b, c and d may differ in their number of elements, 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); > machine_mode op_mode = TYPE_MODE (TREE_TYPE (@2)); > int nelts = TYPE_VECTOR_SUBPARTS (type).to_constant (); > + int c_nelts = TYPE_VECTOR_SUBPARTS (TREE_TYPE (@0)).to_constant (); > + int op_nelts = TYPE_VECTOR_SUBPARTS (TREE_TYPE (@2)).to_constant (); > vec_perm_indices sel0, sel1; > } > (if (tree_to_vec_perm_indices (&sel0, @2, @3, @4) > @@ -12890,23 +12896,38 @@ and, > for (int i = 0; i < nelts; i++) > builder2.quick_push (sel0[sel1[i].to_constant ()]); I'm not sure [] on sel0 will work OK if sel1 has < sel1.ninputs () elements in its encoding? > - vec_perm_indices sel2 (builder2, 2, nelts); > + vec_perm_indices sel2 (builder2, 2, op_nelts); use sel0.ninputs () > + > + /* The new permute selects elements of a and b, so it has their > + element type; the VIEW_CONVERT_EXPR below gives it d's type. */ Huh, but the element modes are going to be the same (hopefully). I suppose you are considering the case of mixing FP and int modes in the permute? I suppose the correct thing to do is to adjust result_mode (move it's declaration down) in the first place given we'll V_C_E the result. This seems to be also a separate bug, worth splitting out. > + tree perm_type = TREE_TYPE (@2); > + if (nelts != op_nelts) > + perm_type = build_vector_type (TREE_TYPE (perm_type), nelts); > > tree op0 = NULL_TREE; > + if (nelts != op_nelts || c_nelts != op_nelts) > + { > + /* Only merge permutes of different widths if the target > + handles the result. */ > + if (can_vec_perm_const_p (TYPE_MODE (perm_type), op_mode, sel2, > + false)) > + op0 = vec_perm_indices_to_tree (TREE_TYPE (@5), sel2); > + } > /* 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. */ > - 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 (result_mode, op_mode, sel1, true)) > - : !can_vec_perm_const_p (result_mode, op_mode, sel1, true))) > + else 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 (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 (@5), sel2); > } > (if (op0) > - (view_convert (vec_perm @2 @3 { op0; })))))))) > + (view_convert (vec_perm:perm_type @2 @3 { op0; })))))))) > > /* Merge > c = VEC_PERM_EXPR <a, b, VCST0>; > @@ -12918,11 +12939,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; > } > @@ -12955,7 +12981,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. > @@ -12964,7 +12990,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); > @@ -12980,11 +13006,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; > } > @@ -13016,7 +13047,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. > @@ -13025,7 +13056,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.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)
