https://gcc.gnu.org/g:80bef0cb12be0c93b54af2c084b6e728626377ff
commit r17-5245-g80bef0cb12be0c93b54af2c084b6e728626377ff Author: Soumya AR <[email protected]> Date: Wed Sep 23 12:30:18 2026 +0000 aarch64: Use SVE ASRD with vector division using division operator The SVE ASRD instruction performs signed division by a power of two (an arithmetic shift right that rounds towards zero). The vectorizer already uses it for such divisions through the sdiv_pow2 optab. This patch additionally enables ASRD when dividing vectors using the GNU C division operator. For example: int32x4_t foo (int32x4_t x) { return x / 4; } and svint32_t bar (svint32_t x) { return x / 4; } currently generate an SVE SDIV, but can be done using a single ASRD. This patch extends aarch64_instruction_selection to rewrite a signed vector TRUNC_DIV_EXPR by a positive power-of-two constant into a call to .DIV_POW2, which then expands through the sdiv_pow2 optab. Bootstrapped and regtested on aarch64-linux-gnu, no regressions. OK for trunk? Signed-off-by: Soumya AR <[email protected]> gcc/ChangeLog: * config/aarch64/aarch64.cc: Include internal-fn.h. (aarch64_try_sdiv_pow2): New function. (aarch64_instruction_selection): Call it for TRUNC_DIV_EXPR. gcc/testsuite/ChangeLog: * gcc.target/aarch64/sve/sve-asrd-2.c: New test. Diff: --- gcc/config/aarch64/aarch64.cc | 39 +++++++++++++ gcc/testsuite/gcc.target/aarch64/sve/sve-asrd-2.c | 70 +++++++++++++++++++++++ 2 files changed, 109 insertions(+) diff --git a/gcc/config/aarch64/aarch64.cc b/gcc/config/aarch64/aarch64.cc index 5f0556fd5169..5d58491434f6 100644 --- a/gcc/config/aarch64/aarch64.cc +++ b/gcc/config/aarch64/aarch64.cc @@ -32,6 +32,7 @@ #include "tree.h" #include "memmodel.h" #include "gimple.h" +#include "internal-fn.h" #include "cfghooks.h" #include "cfgloop.h" #include "df.h" @@ -2332,6 +2333,40 @@ aarch64_try_widen_mult_by_pow2 (const gassign *convert, return true; } +/* Try to use the sdiv_pow2 optab for signed vector division by a constant + power of two. */ +static bool +aarch64_try_sdiv_pow2 (const gassign *assign, gimple_stmt_iterator *gsi) +{ + tree lhs = gimple_assign_lhs (assign); + tree type = TREE_TYPE (lhs); + + if (!VECTOR_INTEGER_TYPE_P (type) + || TYPE_UNSIGNED (type) + || !direct_internal_fn_supported_p (IFN_DIV_POW2, type, + OPTIMIZE_FOR_BOTH)) + return false; + + tree dividend = gimple_assign_rhs1 (assign); + tree divisor = uniform_integer_cst_p (gimple_assign_rhs2 (assign)); + + /* Check that the divisor is a constant positive power of two. */ + if (!divisor + || tree_int_cst_sgn (divisor) != 1 + || !integer_pow2p (divisor)) + return false; + + tree shift = build_int_cst (TREE_TYPE (type), tree_log2 (divisor)); + shift = build_vector_from_val (type, shift); + + gcall *call = gimple_build_call_internal (IFN_DIV_POW2, 2, + dividend, shift); + gimple_call_set_lhs (call, lhs); + gimple_set_location (call, gimple_location (assign)); + gsi_replace (gsi, call, false); + return true; +} + /* Implement TARGET_MAX_NOCE_IFCVT_SEQ_COST. If an explicit max was set then honor it, otherwise apply a tuning specific scale to branch costs. */ @@ -4552,6 +4587,10 @@ aarch64_instruction_selection (function *fun, gimple_stmt_iterator *gsi) if (!assign) return false; + if (gimple_assign_rhs_code (assign) == TRUNC_DIV_EXPR + && aarch64_try_sdiv_pow2 (assign, gsi)) + return true; + if (CONVERT_EXPR_CODE_P (gimple_assign_rhs_code (assign)) && aarch64_try_widen_mult_by_pow2 (assign, gsi)) return true; diff --git a/gcc/testsuite/gcc.target/aarch64/sve/sve-asrd-2.c b/gcc/testsuite/gcc.target/aarch64/sve/sve-asrd-2.c new file mode 100644 index 000000000000..d4e29ab186ac --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/sve/sve-asrd-2.c @@ -0,0 +1,70 @@ +/* { dg-do compile } */ +/* { dg-options "-O3 -fdump-tree-isel" } */ +/* { dg-final { check-function-bodies "**" "" "" } } */ +/* { dg-final { scan-tree-dump-times {\.DIV_POW2} 4 "isel" } } */ + +#include <arm_sve.h> + +#define FOO(TYPE) \ +TYPE foo_##TYPE (TYPE x) \ +{ \ + return x / 8; \ +} + +/* +** foo_int32x4_t: +** ptrue (p[0-7]).b, vl16 +** asrd z[0-9]+\.s, \1/m, z[0-9]+\.s, #3 +** ret +*/ +FOO (int32x4_t) + +/* +** foo_int64x2_t: +** ptrue (p[0-7]).b, vl16 +** asrd z[0-9]+\.d, \1/m, z[0-9]+\.d, #3 +** ret +*/ +FOO (int64x2_t) + +/* +** foo_svint32_t: +** ptrue (p[0-7]).b, all +** asrd z[0-9]+\.s, \1/m, z[0-9]+\.s, #3 +** ret +*/ +FOO (svint32_t) + +/* +** foo_svint64_t: +** ptrue (p[0-7]).b, all +** asrd z[0-9]+\.d, \1/m, z[0-9]+\.d, #3 +** ret +*/ +FOO (svint64_t) + +/* +** foo_nonpow2: +** mov (z[0-9]+\.s), #7 +** ptrue (p[0-7]).b, all +** sdiv (z[0-9]+\.s), \2/m, \3, \1 +** ret +*/ +svint32_t +foo_nonpow2 (svint32_t x) +{ + return x / 7; +} + +/* +** foo_unsigned: +** mov (z[0-9]+\.s), #8 +** ptrue (p[0-7]).b, all +** udiv (z[0-9]+\.s), \2/m, \3, \1 +** ret +*/ +svuint32_t +foo_unsigned (svuint32_t x) +{ + return x / 8; +}
