aarch64: Use sbfx/sbfiz for sign_extend of an SImode shift [PR118648]
Commit Message
combine fails to match (sign_extend:DI (ashiftrt:SI x c)) and
(sign_extend:DI (ashift:SI x c)), so we emit asr/lsl followed by sxtw
where a single sbfx/sbfiz would do. The QImode and HImode versions of
these patterns already exist; extend them to SImode sources. The
zero_extend:DI of an SImode left shift that the ashl iterator now also
covers is still matched by the earlier *<optab>si3_insn_uxtw pattern.
aarch64_extend_bitfield_pattern_p is deliberately left alone: costing
the SImode forms as one instruction lets combine keep an asr that has
another use and add an sbfx, which then stops the sxtw from folding into
an extended-register add or address (x + a + p[a] with a = n >> 3 grows
from 4 to 5 instructions).
Assisted-by: Claude Opus 5 (Anthropic)
PR rtl-optimization/118648
PR target/127288
gcc/ChangeLog:
* config/aarch64/aarch64.md (*<ANY_EXTEND:optab><GPI:mode>_ashl<SHORT:mode>):
Rename to...
(*<ANY_EXTEND:optab><GPI:mode>_ashl<ALLX:mode>): ...this and
iterate over ALLX.
(*extend<GPI:mode>_ashr<SHORT:mode>): Rename to...
(*extend<GPI:mode>_ashr<ALLX:mode>): ...this and iterate over ALLX.
gcc/testsuite/ChangeLog:
* gcc.target/aarch64/pr118648.c: New test.
Signed-off-by: Dominic P <gcc@gcc.dp11.uk>
---
This also fixes PR127288. I did not canonicalise to sign_extract in
combine because riscv, loongarch, mips and rs6000 match
(sign_extend:DI (shift:SI ...)) directly.
Effect at -O2 on an arm64 Linux 6.12 kernel (bcm2712_defconfig): 22
objects shrink, none grow; 62 fewer instructions excluding alignment
nops, .text -600 bytes. On a Cortex-A72 (Pi 400), loop-carried chains
add; asr; sxtw and asr; add x, x, w, sxtw run at 2 instead of 3 cycles
per iteration (add; sbfx and sbfx; add); likewise lsl; sxtw -> sbfiz.
Regression-tested with an aarch64-linux-gnu cross compiler (C testsuite under
qemu user mode), together with the aarch64 patches for PR118666 and
PR125640; no regressions.
OK for trunk?
gcc/config/aarch64/aarch64.md | 20 ++---
gcc/testsuite/gcc.target/aarch64/pr118648.c | 86 +++++++++++++++++++++
2 files changed, 96 insertions(+), 10 deletions(-)
create mode 100644 gcc/testsuite/gcc.target/aarch64/pr118648.c
@@ -6577,14 +6577,14 @@
[(set_attr "type" "rotate_imm")]
)
-(define_insn "*<ANY_EXTEND:optab><GPI:mode>_ashl<SHORT:mode>"
+(define_insn "*<ANY_EXTEND:optab><GPI:mode>_ashl<ALLX:mode>"
[(set (match_operand:GPI 0 "register_operand" "=r")
(ANY_EXTEND:GPI
- (ashift:SHORT (match_operand:SHORT 1 "register_operand" "r")
- (match_operand 2 "const_int_operand" "n"))))]
- "UINTVAL (operands[2]) < GET_MODE_BITSIZE (<SHORT:MODE>mode)"
+ (ashift:ALLX (match_operand:ALLX 1 "register_operand" "r")
+ (match_operand 2 "const_int_operand" "n"))))]
+ "UINTVAL (operands[2]) < GET_MODE_BITSIZE (<ALLX:MODE>mode)"
{
- operands[3] = GEN_INT (<SHORT:sizen> - UINTVAL (operands[2]));
+ operands[3] = GEN_INT (<ALLX:sizen> - UINTVAL (operands[2]));
return "<su>bfiz\t%<GPI:w>0, %<GPI:w>1, %2, %3";
}
[(set_attr "type" "bfx")]
@@ -6603,14 +6603,14 @@
[(set_attr "type" "bfx")]
)
-(define_insn "*extend<GPI:mode>_ashr<SHORT:mode>"
+(define_insn "*extend<GPI:mode>_ashr<ALLX:mode>"
[(set (match_operand:GPI 0 "register_operand" "=r")
(sign_extend:GPI
- (ashiftrt:SHORT (match_operand:SHORT 1 "register_operand" "r")
- (match_operand 2 "const_int_operand" "n"))))]
- "UINTVAL (operands[2]) < GET_MODE_BITSIZE (<SHORT:MODE>mode)"
+ (ashiftrt:ALLX (match_operand:ALLX 1 "register_operand" "r")
+ (match_operand 2 "const_int_operand" "n"))))]
+ "UINTVAL (operands[2]) < GET_MODE_BITSIZE (<ALLX:MODE>mode)"
{
- operands[3] = GEN_INT (<SHORT:sizen> - UINTVAL (operands[2]));
+ operands[3] = GEN_INT (<ALLX:sizen> - UINTVAL (operands[2]));
return "sbfx\\t%<GPI:w>0, %<GPI:w>1, %2, %3";
}
[(set_attr "type" "bfx")]
new file mode 100644
@@ -0,0 +1,86 @@
+/* PR rtl-optimization/118648 */
+/* PR target/127288 */
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+/* { dg-add-options check_function_bodies } */
+/* { dg-final { check-function-bodies "**" "" "" } } */
+
+/*
+** asr11:
+** sbfx x0, x0, 11, 21
+** ret
+*/
+long
+asr11 (int t)
+{
+ return t >> 11;
+}
+
+/*
+** asr31:
+** sbfx x0, x0, 31, 1
+** ret
+*/
+long
+asr31 (int t)
+{
+ return t >> 31;
+}
+
+/*
+** lsl11:
+** sbfiz x0, x0, 11, 21
+** ret
+*/
+long
+lsl11 (int t)
+{
+ return (int) ((unsigned) t << 11);
+}
+
+/*
+** lsl31:
+** sbfiz x0, x0, 31, 1
+** ret
+*/
+long
+lsl31 (int t)
+{
+ return (int) ((unsigned) t << 31);
+}
+
+/*
+** zlsl11:
+** lsl w0, w0, 11
+** ret
+*/
+unsigned long
+zlsl11 (unsigned t)
+{
+ return t << 11;
+}
+
+/*
+** store:
+** sbfx (x[0-9]+), x1, 11, 21
+** str \1, \[x0, 8\]
+** ret
+*/
+void
+store (long *a, int t)
+{
+ a[1] = t >> 11;
+}
+
+/*
+** load:
+** ldr w([0-9]+), \[x1\]
+** sbfx x([0-9]+), x\1, 2, 30
+** str x\2, \[x0\]
+** ret
+*/
+void
+load (long long *a, int *b)
+{
+ *a = *b >> 2;
+}