https://gcc.gnu.org/g:e97550a7d0e1a8b31a76b0877c0e90a0163da7ee

commit r16-5631-ge97550a7d0e1a8b31a76b0877c0e90a0163da7ee
Author: Richard Earnshaw <[email protected]>
Date:   Tue Nov 25 15:47:05 2025 +0000

    arm: handle long-range CBZ/CBNZ patterns [PR122867]
    
    The CBN?Z instructions have a very small range (just 128 bytes
    forwards).  The compiler knows how to handle cases where we
    exceed that, but only if the range remains within that which
    a condition branch can support.  When compiling some machine
    generated code it is not too difficult to exceed this limit,
    so arrange to fall back to a conditional branch over an
    unconditional one in this extreme case.
    
    gcc/ChangeLog:
            PR target/122867
            * config/arm/arm.cc (arm_print_operand): Use %- to
            emit LOCAL_LABEL_PREFIX.
            (arm_print_operand_punct_valid_p): Allow %- for punct
            and make %_ valid for all compilation variants.
            * config/arm/thumb2.md (*thumb2_cbz): Handle very
            large branch ranges that exceed the limit of b<cond>.
            (*thumb2_cbnz): Likewise.
    
    gcc/testsuite/ChangeLog:
            PR target/122867
            * gcc.target/arm/cbz-range.c: New test.

Diff:
---
 gcc/config/arm/arm.cc                    |  11 ++-
 gcc/config/arm/thumb2.md                 |  38 +++++++----
 gcc/testsuite/gcc.target/arm/cbz-range.c | 114 +++++++++++++++++++++++++++++++
 3 files changed, 146 insertions(+), 17 deletions(-)

diff --git a/gcc/config/arm/arm.cc b/gcc/config/arm/arm.cc
index 07d24d1f67ed..20d3f1f4578b 100644
--- a/gcc/config/arm/arm.cc
+++ b/gcc/config/arm/arm.cc
@@ -24064,7 +24064,7 @@ arm_print_condition (FILE *stream)
 
 
 /* Globally reserved letters: acln
-   Puncutation letters currently used: @_|?().!#
+   Puncutation letters currently used: @_-|?().!#
    Lower case letters currently used: bcdefhimpqtvwxyz
    Upper case letters currently used: ABCDEFGHIJKLMOPQRSTUV
    Letters previously used, but now deprecated/obsolete: sNWXYZ.
@@ -24097,6 +24097,11 @@ arm_print_operand (FILE *stream, rtx x, int code)
     case '_':
       fputs (user_label_prefix, stream);
       return;
+    case '-':
+#ifdef LOCAL_LABEL_PREFIX
+      fputs (LOCAL_LABEL_PREFIX, stream);
+#endif
+      return;
 
     case '|':
       fputs (REGISTER_PREFIX, stream);
@@ -24913,9 +24918,9 @@ arm_print_operand_punct_valid_p (unsigned char code)
 {
   return (code == '@' || code == '|' || code == '.'
          || code == '(' || code == ')' || code == '#'
+         || code == '-' || code == '_'
          || (TARGET_32BIT && (code == '?'))
-         || (TARGET_THUMB2 && (code == '!'))
-         || (TARGET_THUMB && (code == '_')));
+         || (TARGET_THUMB2 && (code == '!')));
 }
 
 /* Target hook for assembling integer objects.  The ARM version needs to
diff --git a/gcc/config/arm/thumb2.md b/gcc/config/arm/thumb2.md
index 40c0e052946c..c3539958f8a3 100644
--- a/gcc/config/arm/thumb2.md
+++ b/gcc/config/arm/thumb2.md
@@ -1464,19 +1464,24 @@
              (pc)))
    (clobber (reg:CC CC_REGNUM))]
   "TARGET_THUMB2"
-  "*
-  if (get_attr_length (insn) == 2)
-    return \"cbz\\t%0, %l1\";
-  else
-    return \"cmp\\t%0, #0\;beq\\t%l1\";
-  "
+  {
+    int offset = (INSN_ADDRESSES (INSN_UID (operands[1]))
+                 - INSN_ADDRESSES (INSN_UID (insn)));
+    if (get_attr_length (insn) == 2)
+      return "cbz\t%0, %l1";
+    else if (offset >= -1048564 && offset <= 1048576)
+      return "cmp\t%0, #0\;beq\t%l1";
+    else if (which_alternative == 0)
+      return "cbnz\t%0, %-LCB%=\;b\t%l1\n%-LCB%=:";
+    return "cmp\t%0, #0\;bne\t%-LCB%=\;b\t%l1\n%-LCB%=:";
+  }
   [(set (attr "length")
         (if_then_else
            (and (ge (minus (match_dup 1) (pc)) (const_int 2))
                 (le (minus (match_dup 1) (pc)) (const_int 128))
                 (not (match_test "which_alternative")))
            (const_int 2)
-           (const_int 8)))
+           (const_int 10)))
    (set_attr "type" "branch,multiple")]
 )
 
@@ -1488,19 +1493,24 @@
              (pc)))
    (clobber (reg:CC CC_REGNUM))]
   "TARGET_THUMB2"
-  "*
-  if (get_attr_length (insn) == 2)
-    return \"cbnz\\t%0, %l1\";
-  else
-    return \"cmp\\t%0, #0\;bne\\t%l1\";
-  "
+  {
+    int offset = (INSN_ADDRESSES (INSN_UID (operands[1]))
+                 - INSN_ADDRESSES (INSN_UID (insn)));
+    if (get_attr_length (insn) == 2)
+      return "cbnz\t%0, %l1";
+    else if (offset >= -1048564 && offset <= 1048576)
+      return "cmp\t%0, #0\;bne\t%l1";
+    else if (which_alternative == 0)
+      return "cbz\t%0, %-LCB%=\;b\t%l1\n%-LCB%=:";
+    return "cmp\t%0, #0\;beq\t%-LCB%=\;b\t%l1\n%-LCB%=:";
+  }
   [(set (attr "length")
         (if_then_else
            (and (ge (minus (match_dup 1) (pc)) (const_int 2))
                 (le (minus (match_dup 1) (pc)) (const_int 128))
                 (not (match_test "which_alternative")))
            (const_int 2)
-           (const_int 8)))
+           (const_int 10)))
    (set_attr "type" "branch,multiple")]
 )
 
diff --git a/gcc/testsuite/gcc.target/arm/cbz-range.c 
b/gcc/testsuite/gcc.target/arm/cbz-range.c
new file mode 100644
index 000000000000..3b23888a42a9
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/cbz-range.c
@@ -0,0 +1,114 @@
+/* { dg-do assemble } */
+/* { dg-require-effective-target arm_arch_v7a_ok } */
+/* { dg-options "-O -mthumb" } */
+/* { dg-add-options arm_arch_v7a } */
+
+#define f "movw r0, #0;movw r0, #0;movw r0, #0;"
+#define f2 f f
+#define f4 f2 f2
+#define f8 f4 f4
+#define f16 f8 f8
+#define f32 f16 f16
+#define f64 f32 f32
+#define f128 f64 f64
+#define f256 f128 f128
+#define f512 f256 f256
+#define f1024 f512 f512
+#define f2048 f1024 f1024
+#define f4096 f2048 f2048
+#define f8192 f4096 f4096
+#define f16384 f8192 f8192
+#define f32768 f16384 f16384
+#define f65536 f32768 f32768
+#define f131072 f65536 f65536
+int a;
+
+int cbz1(int g)
+{
+  if (g)
+    asm(f8);
+  return a;
+}
+
+int cbz2(int g)
+{
+  asm ("": "+h"(g));
+  if (g)
+    asm(f8);
+  return a;
+}
+
+int cbz3(int g)
+{
+  if (g)
+    asm(f16);
+  return a;
+}
+
+int cbz4(int g)
+{
+  asm ("": "+h"(g));
+  if (g)
+    asm(f16);
+  return a;
+}
+
+int cbz5(int g)
+{
+  if (g)
+    asm(f131072);
+  return a;
+}
+
+int cbz6(int g)
+{
+  asm ("": "+h"(g));
+  if (g)
+    asm(f131072);
+  return a;
+}
+
+int cbnz1(int g)
+{
+  if (!g)
+    asm(f8);
+  return a;
+}
+
+int cbnz2(int g)
+{
+  asm ("": "+h"(g));
+  if (!g)
+    asm(f8);
+  return a;
+}
+
+int cbnz3(int g)
+{
+  if (!g)
+    asm(f16);
+  return a;
+}
+
+int cbnz4(int g)
+{
+  asm ("": "+h"(g));
+  if (!g)
+    asm(f16);
+  return a;
+}
+
+int cbnz5(int g)
+{
+  if (!g)
+    asm(f131072);
+  return a;
+}
+
+int cbnz6(int g)
+{
+  asm ("": "+h"(g));
+  if (!g)
+    asm(f131072);
+  return a;
+}

Reply via email to