]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
arm: handle long-range CBZ/CBNZ patterns [PR122867]
authorRichard Earnshaw <rearnsha@arm.com>
Tue, 25 Nov 2025 15:47:05 +0000 (15:47 +0000)
committerRichard Earnshaw <rearnsha@arm.com>
Wed, 26 Nov 2025 14:31:16 +0000 (14:31 +0000)
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.

gcc/config/arm/arm.cc
gcc/config/arm/thumb2.md
gcc/testsuite/gcc.target/arm/cbz-range.c [new file with mode: 0644]

index 07d24d1f67ed6ac310eadd5c202585f18f43ad6a..20d3f1f4578b63b1b948f635c01bd7812548e5c8 100644 (file)
@@ -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 == '!')));
 }
 \f
 /* Target hook for assembling integer objects.  The ARM version needs to
index 40c0e052946c05e541132779c7526dd3c466bc67..c3539958f8a3a998286772eb6f418da80c07ecc5 100644 (file)
              (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")]
 )
 
              (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 (file)
index 0000000..3b23888
--- /dev/null
@@ -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;
+}