https://gcc.gnu.org/g:40b1a100861b81e07c7a67b1fa0e5c73ab082cf6
commit r17-2030-g40b1a100861b81e07c7a67b1fa0e5c73ab082cf6 Author: Abhishek Kaushik <[email protected]> Date: Mon Jun 29 15:26:53 2026 +0000 aarch64: Use SBFIZ for widening signed pow2 multiplies For a widening conversion of the result of a signed multiply by a positive power of two, rewrite the sequence to widen the multiplicand before multiplying. This exposes the form that AArch64 can emit as SBFIZ and avoids a separate sign extension. The rewrite is valid because overflow in the original signed multiply is undefined. Bootstrapped and regression tested on aarch64-linux-gnu. gcc/ * config/aarch64/aarch64.cc: Include gimple-fold.h. (aarch64_try_widen_mult_by_pow2): New function. (aarch64_instruction_selection): Call it for conversion assignments. gcc/testsuite/ * gcc.target/aarch64/sbfiz-widen-mult-1.c: New test. Diff: --- gcc/config/aarch64/aarch64.cc | 86 ++++++++++++++++++++++ .../gcc.target/aarch64/sbfiz-widen-mult-1.c | 49 ++++++++++++ 2 files changed, 135 insertions(+) diff --git a/gcc/config/aarch64/aarch64.cc b/gcc/config/aarch64/aarch64.cc index bf16cad72c17..72822ca621f5 100644 --- a/gcc/config/aarch64/aarch64.cc +++ b/gcc/config/aarch64/aarch64.cc @@ -61,6 +61,7 @@ #include "dwarf2.h" #include "dwarf2out.h" #include "gimple-iterator.h" +#include "gimple-fold.h" #include "tree-vectorizer.h" #include "aarch64-cost-tables.h" #include "dumpfile.h" @@ -2229,6 +2230,87 @@ aarch64_preferred_else_value (unsigned, tree, unsigned int nops, tree *ops) return nops == 3 ? ops[2] : ops[0]; } +/* Try to widen a signed, overflow-undefined multiply by a power of two before + converting it to a wider integral type. + + This helps AArch64 instruction selection expose a form that can be emitted + as SBFIZ, avoiding an otherwise separate sign-extension around the + shift/bitfield operation. + + For example, rewrite: + + _2 = _1 * 2; + _3 = (long int) _2; + + into: + + _6 = (long int) _1; + _3 = _6 * 2; + + This is valid because overflow in the original narrow signed multiply is + undefined. For all defined executions, widening the multiplicand before the + multiply produces the same value as multiplying in the narrow type and then + converting the result. + + The original narrow multiply is removed immediately. There is no DCE pass + after AArch64 instruction selection, so leaving it behind would keep dead + multiplications in the final optimized GIMPLE dump. */ +static bool +aarch64_try_widen_mult_by_pow2 (const gassign *convert, + gimple_stmt_iterator *gsi) +{ + tree type = TREE_TYPE (gimple_assign_lhs (convert)); + tree inner = gimple_assign_rhs1 (convert); + tree inner_type = TREE_TYPE (inner); + + if (!INTEGRAL_TYPE_P (type) + || !INTEGRAL_TYPE_P (inner_type) + || !TYPE_OVERFLOW_UNDEFINED (inner_type) + || TYPE_PRECISION (type) <= TYPE_PRECISION (inner_type) + || TYPE_PRECISION (type) > BITS_PER_WORD + || TREE_CODE (inner) != SSA_NAME + || !has_single_use (inner)) + return false; + + gimple *stmt = SSA_NAME_DEF_STMT (inner); + if (!is_gimple_assign (stmt) + || gimple_assign_rhs_code (stmt) != MULT_EXPR) + return false; + + tree multiplicand = gimple_assign_rhs1 (stmt); + tree pow2const = gimple_assign_rhs2 (stmt); + if (!integer_pow2p (pow2const) + || tree_int_cst_sgn (pow2const) <= 0) + return false; + + gimple_stmt_iterator stmt_gsi = gsi_for_stmt (stmt); + + gimple_seq stmts = NULL; + tree widened_multiplicand = gimple_convert (&stmts, + gimple_location (convert), + type, multiplicand); + + gsi_insert_seq_before (gsi, stmts, GSI_SAME_STMT); + + tree widened_pow2const = fold_convert (type, pow2const); + + tree mul_lhs = gimple_assign_lhs (convert); + gassign *mul_stmt + = gimple_build_assign (mul_lhs, MULT_EXPR, + widened_multiplicand, + widened_pow2const); + + gsi_replace (gsi, mul_stmt, true); + + /* INNER was used only by CONVERT, which we just replaced. The defining + multiply is therefore dead, so remove it. */ + gcc_checking_assert (has_zero_uses (inner)); + gsi_remove (&stmt_gsi, true); + release_defs (stmt); + + return true; +} + /* Implement TARGET_INSTRUCTION_SELECTION. The target hook is used to change generic sequences to a form AArch64 has an easier time expanding instructions for. It's not supposed to be used for generic rewriting that @@ -2243,6 +2325,10 @@ aarch64_instruction_selection (function * /* fun */, gimple_stmt_iterator *gsi) if (!assign) return false; + if (CONVERT_EXPR_CODE_P (gimple_assign_rhs_code (assign)) + && aarch64_try_widen_mult_by_pow2 (assign, gsi)) + return true; + /* Convert p == q ? s1 : s2; to diff --git a/gcc/testsuite/gcc.target/aarch64/sbfiz-widen-mult-1.c b/gcc/testsuite/gcc.target/aarch64/sbfiz-widen-mult-1.c new file mode 100644 index 000000000000..801016672e14 --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/sbfiz-widen-mult-1.c @@ -0,0 +1,49 @@ +/* { dg-do compile } */ +/* { dg-options "-O2" } */ +/* { dg-final { check-function-bodies "**" "" } } */ + +#include <stdint.h> + +/* +** extend_8to32_by2: +** sbfiz w0, w0, 1, 8 +** ret +*/ +int32_t extend_8to32_by2 (int8_t x) { return x * 2; } + +/* +** extend_16to32_by2: +** sbfiz w0, w0, 1, 16 +** ret +*/ +int32_t extend_16to32_by2 (int16_t x) { return x * 2; } + +/* +** extend_8to64_by2: +** sbfiz x0, x0, 1, 8 +** ret +*/ +int64_t extend_8to64_by2 (int8_t x) { return x * 2; } + +/* +** extend_16to64_by2: +** sbfiz x0, x0, 1, 16 +** ret +*/ +int64_t extend_16to64_by2 (int16_t x) { return x * 2; } + +/* +** extend_32to64_by2: +** sbfiz x0, x0, 1, 32 +** ret +*/ +int64_t extend_32to64_by2 (int32_t x) { return 2 * x; } + +/* +** extend_64to128_by2: +** mov x1, x0 +** lsl x0, x0, 1 +** sbfx x1, x1, 62, 1 +** ret +*/ +__int128_t extend_64to128_by2 (long long x) { return x * 2; }
