https://gcc.gnu.org/g:667a1b5285c2d5d075a324a29323804f93d43add
commit r17-1584-g667a1b5285c2d5d075a324a29323804f93d43add Author: Kyrylo Tkachov <[email protected]> Date: Tue Jun 16 00:58:28 2026 -0700 aarch64: Fix wrong code for high-64-zero Advanced SIMD constants [PR125794] r17-1491-gf152cf1734f808 (PR113926) taught aarch64_simd_valid_imm to materialize a 128-bit Advanced SIMD MOV constant whose high 64 bits are zero with a 64-bit MOVI/FMOV, which zeroes the upper half of the register. It records this with simd_immediate_info::width == 64 (output_width). However, when the low 64 bits are not themselves a valid Advanced SIMD (MOVI/MVNI/FMOV) immediate, the function fell through to the SVE immediate forms (aarch64_sve_valid_immediate). Those use a replicating "mov zN.<T>, #imm", which sets the whole vector, including the high 64 bits that were required to be zero, to the repeated low-64-bit value. For e.g. the V4SI constant { 0, 1, 0, 0 } this emitted mov z31.d, #4294967296 // 0x100000000, i.e. { 0, 1, 0, 1 } instead of the intended { 0, 1, 0, 0 }, producing wrong code. Fix it by not falling through to the SVE forms when output_width is set: such a constant must be formed by a 64-bit Advanced SIMD MOVI/FMOV (handled by the Advanced SIMD and floating-point paths just above) or not at all, in which case the caller materializes it some other way (e.g. a literal-pool load), which is the pre-r17-1491 behavior for these constants. The PR113926 optimization is unaffected: it only applies when the Advanced SIMD or floating-point path accepts the low 64 bits, and those still return true before the new check. Bootstrapped and regression-tested on aarch64-linux-gnu. Pushing to trunk. Signed-off-by: Kyrylo Tkachov <[email protected]> gcc/ChangeLog: PR target/125794 * config/aarch64/aarch64.cc (aarch64_simd_valid_imm): Do not fall through to the replicating SVE immediate forms for a 128-bit Advanced SIMD constant whose high 64 bits are zero (output_width != 0). gcc/testsuite/ChangeLog: PR target/125794 * gcc.target/aarch64/sve/pr125794.c: New test. Diff: --- gcc/config/aarch64/aarch64.cc | 6 ++++++ gcc/testsuite/gcc.target/aarch64/sve/pr125794.c | 22 ++++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/gcc/config/aarch64/aarch64.cc b/gcc/config/aarch64/aarch64.cc index 08746b657746..42e56512c616 100644 --- a/gcc/config/aarch64/aarch64.cc +++ b/gcc/config/aarch64/aarch64.cc @@ -24733,6 +24733,12 @@ aarch64_simd_valid_imm (rtx op, simd_immediate_info *info, return true; } + /* A constant with zero high 64 bits (output_width == 64) must be formed + by a 64-bit Advanced SIMD MOVI/FMOV; it must not fall through to the + SVE forms below, which replicate it across the whole vector. */ + if (output_width != 0) + return false; + if (TARGET_SVE) return aarch64_sve_valid_immediate (ival, imode, info, which); return false; diff --git a/gcc/testsuite/gcc.target/aarch64/sve/pr125794.c b/gcc/testsuite/gcc.target/aarch64/sve/pr125794.c new file mode 100644 index 000000000000..a4e198b94666 --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/sve/pr125794.c @@ -0,0 +1,22 @@ +/* { dg-do run { target aarch64_sve_hw } } */ +/* { dg-additional-options "-O2" } */ + +/* PR target/125794: a 128-bit Advanced SIMD integer constant whose high 64 + bits are zero (here { 0, 1, 0, 0 }) must be materialised with a 64-bit MOVI + that zeroes the upper half of the register. It was wrongly emitted as a + replicating SVE "mov zN.d, #imm", which set the high 64 bits to a non-zero + value and produced wrong code. */ + +typedef unsigned V __attribute__((__vector_size__ (32))); + +V x; + +int +main () +{ + x[5] = 1; + for (unsigned i = 0; i < 8; i++) + if (x[i] != (i == 5)) + __builtin_abort (); + return 0; +}
