https://gcc.gnu.org/g:caab3df20accebf99913287c148ed64ea809fb9b
commit r17-5215-gcaab3df20accebf99913287c148ed64ea809fb9b Author: Igor Shevlyakov <[email protected]> Date: Tue Oct 6 17:24:40 2026 -0700 optabs: Reduce const permute selectors by the input length [PR127860] expand_vec_perm_const builds its vec_perm_indices with the element count of the result mode. Since GCC 13 the inputs of a VEC_PERM_EXPR with a constant selector may have a different number of elements than the result, and the selector indexes the concatenation of the inputs. The vec_perm_builder only carries the length of the selector, which is the length of the result, so for inputs with more elements than the result every index at or above twice the result length is wrapped into the first input before the target hook sees it. That is silent wrong code: a V4SI permute of two V8SI inputs with selector { 0, 8, 1, 9 } reaches the hook as { 0, 0, 1, 1 }, although can_vec_perm_const_p asked the hook about { 0, 8, 1, 9 } (tree_to_vec_perm_indices uses the input length). Build the indices with the input mode's element count. As in can_vec_perm_const_p, only try vec_shr and vec_shl when the inputs have the result mode and a constant number of elements. The fallbacks after the target hook permute in the result mode, so give up before them for other inputs. No in-tree target currently accepts a narrowing permute in its vec_perm_const hook, so this is latent upstream; aarch64 accepts mixed widths only for SVE dupq, which widens and only uses indices below the input length, so it is unaffected. Assisted-by: Claude Opus 5.5 gcc/ChangeLog: PR middle-end/127860 * optabs.cc (expand_vec_perm_const): Reduce the selector by twice the input length. Only use vec_shr and vec_shl for constant-length inputs of the result mode, and the fallbacks for inputs of the result mode. Check earlier that the inputs have the same mode. gcc/testsuite/ChangeLog: PR middle-end/127860 * gcc.dg/torture/vec-perm-wide-input-1.c: New test. Signed-off-by: Igor Shevlyakov <[email protected]> Diff: --- gcc/optabs.cc | 30 ++-- .../gcc.dg/torture/vec-perm-wide-input-1.c | 163 +++++++++++++++++++++ 2 files changed, 184 insertions(+), 9 deletions(-) diff --git a/gcc/optabs.cc b/gcc/optabs.cc index 628500f8c879..3b7cf55a81bd 100644 --- a/gcc/optabs.cc +++ b/gcc/optabs.cc @@ -6812,9 +6812,10 @@ vec_perm_and_mask (machine_mode mode, const vec_perm_indices &sel, vector in SEL and return the result. Use TARGET to hold the result if nonnull and convenient. - MODE is the mode of the vectors being permuted (V0 and V1). SEL_MODE - is the TYPE_MODE associated with SEL, or BLKmode if SEL isn't known - to have a particular mode. */ + MODE is the mode of the result. V0 and V1 have the same mode, which + has the same element mode as MODE but may have a different number of + elements. SEL_MODE is the TYPE_MODE associated with SEL, or BLKmode + if SEL isn't known to have a particular mode. */ rtx expand_vec_perm_const (machine_mode mode, rtx v0, rtx v1, @@ -6833,11 +6834,15 @@ expand_vec_perm_const (machine_mode mode, rtx v0, rtx v1, rtx_insn *last = get_last_insn (); bool single_arg_p = rtx_equal_p (v0, v1); + /* The selector indexes the concatenation of the inputs, which may have + a different number of elements than the result. */ + machine_mode op_mode = GET_MODE (v0); + gcc_checking_assert (op_mode == GET_MODE (v1)); /* Always specify two input vectors here and leave the target to handle cases in which the inputs are equal. Not all backends can cope with the single-input representation when testing for a double-input target instruction. */ - vec_perm_indices indices (sel, 2, GET_MODE_NUNITS (mode)); + vec_perm_indices indices (sel, 2, GET_MODE_NUNITS (op_mode)); /* See if this can be handled with a vec_shr or vec_shl. We only do this if the second (for vec_shr) or first (for vec_shl) vector is all @@ -6846,14 +6851,16 @@ expand_vec_perm_const (machine_mode mode, rtx v0, rtx v1, insn_code shift_code_qi = CODE_FOR_nothing; optab shift_optab = unknown_optab; rtx v2 = v0; - if (v1 == CONST0_RTX (GET_MODE (v1))) + if (v1 == CONST0_RTX (op_mode)) shift_optab = vec_shr_optab; - else if (v0 == CONST0_RTX (GET_MODE (v0))) + else if (v0 == CONST0_RTX (op_mode)) { shift_optab = vec_shl_optab; v2 = v1; } - if (shift_optab != unknown_optab) + if (mode == op_mode + && GET_MODE_NUNITS (mode).is_constant () + && shift_optab != unknown_optab) { shift_code = optab_handler (shift_optab, mode); shift_code_qi = ((qimode != VOIDmode && qimode != mode) @@ -6893,13 +6900,18 @@ expand_vec_perm_const (machine_mode mode, rtx v0, rtx v1, if (single_arg_p) v1 = v0; - gcc_checking_assert (GET_MODE (v0) == GET_MODE (v1)); - machine_mode op_mode = GET_MODE (v0); if (targetm.vectorize.vec_perm_const (mode, op_mode, target, v0, v1, indices)) return target; } + /* The fallbacks below need the inputs to have the result mode. */ + if (mode != op_mode) + { + delete_insns_since (last); + return NULL_RTX; + } + /* Fall back to a constant byte-based permutation. */ vec_perm_indices qimode_indices; rtx target_qi = NULL_RTX, v0_qi = NULL_RTX, v1_qi = NULL_RTX; diff --git a/gcc/testsuite/gcc.dg/torture/vec-perm-wide-input-1.c b/gcc/testsuite/gcc.dg/torture/vec-perm-wide-input-1.c new file mode 100644 index 000000000000..32611ac5e252 --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/vec-perm-wide-input-1.c @@ -0,0 +1,163 @@ +/* Constant VEC_PERM_EXPRs with inputs wider than the result. */ +/* { dg-do run } */ +/* { dg-additional-options "-fgimple -Wno-psabi" } */ + +typedef int v8si __attribute__((vector_size(32))); +typedef int v4si __attribute__((vector_size(16))); +typedef int v16si __attribute__((vector_size(64))); +typedef unsigned char v16qi __attribute__((vector_size(16))); +typedef unsigned char v8qi __attribute__((vector_size(8))); +typedef double v4df __attribute__((vector_size(32))); +typedef double v2df __attribute__((vector_size(16))); +typedef long long v2di __attribute__((vector_size(16))); + +v8si a, b; +v4si r; +v16si c; +v8si s; +v16qi qa, qb; +v8qi qr; +v4df da, db; +v2df dr; + +void __attribute__((noipa)) __GIMPLE() f1 () +{ + v8si a_; v8si b_; v4si r_; + a_ = a; b_ = b; + r_ = __VEC_PERM (a_, b_, _Literal (v4si) { 0, 8, 1, 9 }); + r = r_; +} + +void __attribute__((noipa)) __GIMPLE() f2 () +{ + v8si a_; v8si b_; v4si r_; + a_ = a; b_ = b; + r_ = __VEC_PERM (a_, b_, _Literal (v4si) { 7, 15, 6, 14 }); + r = r_; +} + +void __attribute__((noipa)) __GIMPLE() f3 () +{ + v16qi a_; v16qi b_; v8qi r_; + a_ = qa; b_ = qb; + r_ = __VEC_PERM (a_, b_, _Literal (v8qi) { 31, 0, 17, 30, 15, 16, 2, 24 }); + qr = r_; +} + +void __attribute__((noipa)) __GIMPLE() f4 () +{ + v4df a_; v4df b_; v2df r_; + a_ = da; b_ = db; + r_ = __VEC_PERM (a_, b_, _Literal (v2di) { 6, 3 }); + dr = r_; +} + +void __attribute__((noipa)) __GIMPLE() f5 () +{ + v16si c_; v8si s_; + c_ = c; + s_ = __VEC_PERM (c_, _Literal (v16si) { 0 }, + _Literal (v8si) { 2, 3, 4, 5, 6, 7, 8, 9 }); + s = s_; +} + +void __attribute__((noipa)) __GIMPLE() f6 () +{ + v16si c_; v8si s_; + c_ = c; + s_ = __VEC_PERM (c_, _Literal (v16si) { 0 }, + _Literal (v8si) { 15, 16, 14, 17, 9, 31, 0, 20 }); + s = s_; +} + +void __attribute__((noipa)) __GIMPLE() f7 () +{ + v8si b_; v4si r_; + b_ = b; + r_ = __VEC_PERM (b_, _Literal (v8si) { 0 }, _Literal (v4si) { 2, 3, 4, 5 }); + r = r_; +} + +void __attribute__((noipa)) __GIMPLE() f8 () +{ + v8si b_; v4si r_; + b_ = b; + r_ = __VEC_PERM (_Literal (v8si) { 0 }, b_, _Literal (v4si) { 0, 4, 5, 6 }); + r = r_; +} + +void __attribute__((noipa)) __GIMPLE() f9 () +{ + v8si b_; v4si r_; + b_ = b; + r_ = __VEC_PERM (_Literal (v8si) { 0 }, b_, _Literal (v4si) { 0, 8, 9, 10 }); + r = r_; +} + +int +main () +{ + for (int i = 0; i < 8; i++) + { + a[i] = i; + b[i] = 100 + i; + } + for (int i = 0; i < 16; i++) + { + c[i] = i + 1; + qa[i] = i; + qb[i] = 100 + i; + } + for (int i = 0; i < 4; i++) + { + da[i] = i; + db[i] = 100 + i; + } + + f1 (); + if (r[0] != 0 || r[1] != 100 || r[2] != 1 || r[3] != 101) + __builtin_abort (); + + f2 (); + if (r[0] != 7 || r[1] != 107 || r[2] != 6 || r[3] != 106) + __builtin_abort (); + + f3 (); + { + static const unsigned char e[8] = { 115, 0, 101, 114, 15, 100, 2, 108 }; + for (int i = 0; i < 8; i++) + if (qr[i] != e[i]) + __builtin_abort (); + } + + f4 (); + if (dr[0] != 102 || dr[1] != 3) + __builtin_abort (); + + f5 (); + for (int i = 0; i < 8; i++) + if (s[i] != i + 3) + __builtin_abort (); + + f6 (); + { + static const int e[8] = { 16, 0, 15, 0, 10, 0, 1, 0 }; + for (int i = 0; i < 8; i++) + if (s[i] != e[i]) + __builtin_abort (); + } + + f7 (); + if (r[0] != 102 || r[1] != 103 || r[2] != 104 || r[3] != 105) + __builtin_abort (); + + f8 (); + if (r[0] != 0 || r[1] != 0 || r[2] != 0 || r[3] != 0) + __builtin_abort (); + + f9 (); + if (r[0] != 0 || r[1] != 100 || r[2] != 101 || r[3] != 102) + __builtin_abort (); + + return 0; +}
