aarch64: Use sbfx/sbfiz for sign_extend of an SImode shift [PR118648]

Message ID 20261002193324.2642574-1-gcc@gcc.dp11.uk
State New
Headers
Series aarch64: Use sbfx/sbfiz for sign_extend of an SImode shift [PR118648] |

Commit Message

DominicP Oct. 2, 2026, 7:33 p.m. UTC
  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
  

Patch

diff --git a/gcc/config/aarch64/aarch64.md b/gcc/config/aarch64/aarch64.md
index 7c8915ec389..2e952bd9d72 100644
--- a/gcc/config/aarch64/aarch64.md
+++ b/gcc/config/aarch64/aarch64.md
@@ -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")]
diff --git a/gcc/testsuite/gcc.target/aarch64/pr118648.c b/gcc/testsuite/gcc.target/aarch64/pr118648.c
new file mode 100644
index 00000000000..d40767437c6
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/pr118648.c
@@ -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;
+}