arm_final_prescan_insn gives up on a region that ends in a call on any
BLX-capable core, and does not recognise a return there at all.  The
AAPCS concern is only the direct BL, which the linker rewrites to BLX
in place.  An indirect call is BLX<cond> Rm with no relocation, a
sibling call gets R_ARM_JUMP24 conditional or not, and the return
templates already emit %?.  Predicate those three.

This needs "arm: add TARGET_CALLEE_SAVE_COST and
TARGET_FRAME_ALLOCATION_COST"
<[email protected]>; without it no call site in
the kernel is predicated.

.text of vmlinux, this patch and the next together:

  bcmrpi_defconfig  ARMv6 -O2  10683484 -> 10674880  -8604 B (-0.081 %)
  bcmrpi_defconfig  ARMv6 -Os   8709508 ->  8701308  -8200 B (-0.094 %)
  bcm2709_defconfig ARMv7 -O2  11183812 -> 11175128  -8684 B (-0.078 %)

2087 sites in the ARMv6 -O2 image lose their branch: 704 indirect calls,
749 sibling calls and 634 returns.  No gcc.c-torture/compile file grows
at -O2 or -Os on armv6zk or armv7-a.

Bootstrapped on x86_64-pc-linux-gnu.  Regression tested on arm-none-eabi
(arm, simd, mve, advsimd-intrinsics, acle and cmse) on an Arm and a
Thumb-2 board, with and without -mpredicate-calls.

Assisted-by: Claude Opus 5 (Anthropic)

gcc/ChangeLog:

        * config/arm/arm.cc (arm_final_prescan_insn): Restrict the AAPCS
        conditional-call bail to a linking direct call, so that a guarded
        indirect or sibling call is predicated.  Predicate a return
        reached in the skipped region.

gcc/testsuite/ChangeLog:

        * gcc.target/arm/cond-exec-call-indirect-1.c: New test.
        * gcc.target/arm/cond-exec-return-1.c: New test.
        * gcc.target/arm/cond-exec-run-1.c: New test.
        * gcc.target/arm/cond-exec-sibcall-1.c: New test.
        * gcc.target/arm/cond-exec-thumb-1.c: New test.

Signed-off-by: Dominic P <[email protected]>
---
 gcc/config/arm/arm.cc                         | 44 +++++++--
 .../arm/cond-exec-call-indirect-1.c           | 23 +++++
 .../gcc.target/arm/cond-exec-return-1.c       | 37 +++++++
 .../gcc.target/arm/cond-exec-run-1.c          | 97 +++++++++++++++++++
 .../gcc.target/arm/cond-exec-sibcall-1.c      | 21 ++++
 .../gcc.target/arm/cond-exec-thumb-1.c        | 34 +++++++
 6 files changed, 250 insertions(+), 6 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/arm/cond-exec-call-indirect-1.c
 create mode 100644 gcc/testsuite/gcc.target/arm/cond-exec-return-1.c
 create mode 100644 gcc/testsuite/gcc.target/arm/cond-exec-run-1.c
 create mode 100644 gcc/testsuite/gcc.target/arm/cond-exec-sibcall-1.c
 create mode 100644 gcc/testsuite/gcc.target/arm/cond-exec-thumb-1.c

diff --git a/gcc/config/arm/arm.cc b/gcc/config/arm/arm.cc
index 74f97b2cc2d..49fe75bcf7d 100644
--- a/gcc/config/arm/arm.cc
+++ b/gcc/config/arm/arm.cc
@@ -25882,12 +25882,19 @@ arm_final_prescan_insn (rtx_insn *insn)
              /* The AAPCS says that conditional calls should not be
                 used since they make interworking inefficient (the
                 linker can't transform BL<cond> into BLX).  That's
-                only a problem if the machine has BLX.  */
-             if (arm_arch5t)
-               {
-                 fail = TRUE;
-                 break;
-               }
+                only a problem if the machine has BLX, and only for a
+                linking direct call.  */
+             {
+               rtx addr = XEXP (XEXP (get_call_rtx_from (this_insn), 0), 0);
+
+               if (arm_arch5t
+                   && !SIBLING_CALL_P (this_insn)
+                   && SYMBOL_REF_P (addr))
+                 {
+                   fail = TRUE;
+                   break;
+                 }
+             }
 
              /* Succeed if the following insn is the target label, or
                 if the following two insns are a barrier and the
@@ -25926,6 +25933,31 @@ arm_final_prescan_insn (rtx_insn *insn)
                  else if (GET_CODE (SET_SRC (scanbody)) == IF_THEN_ELSE)
                    fail = TRUE;
                }
+             /* A return reached in the region: the return templates emit
+                the %? condition themselves.  */
+             else if (label && !reverse
+                      && (ANY_RETURN_P (scanbody)
+                          || (GET_CODE (scanbody) == PARALLEL
+                              && ANY_RETURN_P (XVECEXP (scanbody, 0, 0)))))
+               {
+                 if (!use_return_insn (TRUE, NULL) && !optimize_size)
+                   fail = TRUE;
+                 else
+                   {
+                     this_insn = next_nonnote_insn (this_insn);
+                     if (this_insn && BARRIER_P (this_insn))
+                       this_insn = next_nonnote_insn (this_insn);
+
+                     if (this_insn && this_insn == label
+                         && insns_skipped < max_insns_skipped)
+                       {
+                         arm_ccfsm_state = 1;
+                         succeed = TRUE;
+                       }
+                     else
+                       fail = TRUE;
+                   }
+               }
              /* Fail if a conditional return is undesirable (e.g. on a
                 StrongARM), but still allow this if optimizing for size.  */
              else if (GET_CODE (scanbody) == return_code
diff --git a/gcc/testsuite/gcc.target/arm/cond-exec-call-indirect-1.c 
b/gcc/testsuite/gcc.target/arm/cond-exec-call-indirect-1.c
new file mode 100644
index 00000000000..064ea182e37
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/cond-exec-call-indirect-1.c
@@ -0,0 +1,23 @@
+/* { dg-do compile } */
+/* { dg-require-effective-target arm_arm_ok } */
+/* { dg-options "-O2 -marm -march=armv6zk -mtune=arm1176jzf-s" } */
+/* { dg-add-options check_function_bodies } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+extern int bar (void);
+
+/*
+**ind:
+**     cmp     r0, #0
+**     push    {r4, lr}
+**     blxne   r0
+**     bl      bar(?:\(PLT\))?
+**...
+*/
+int
+ind (void (*fp) (void))
+{
+  if (fp)
+    fp ();
+  return bar () + 1;
+}
diff --git a/gcc/testsuite/gcc.target/arm/cond-exec-return-1.c 
b/gcc/testsuite/gcc.target/arm/cond-exec-return-1.c
new file mode 100644
index 00000000000..a5735b71624
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/cond-exec-return-1.c
@@ -0,0 +1,37 @@
+/* { dg-do compile } */
+/* { dg-require-effective-target arm_arm_ok } */
+/* { dg-options "-O2 -marm -march=armv6zk -mtune=arm1176jzf-s" } */
+/* { dg-add-options check_function_bodies } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+extern void die (void) __attribute__ ((noreturn));
+
+/*
+**setp:
+**     cmp     r1, #4096
+**     strcc   r1, \[r0\]
+**     bxcc    lr
+**...
+*/
+void
+setp (int *p, unsigned n)
+{
+  if (n >= 4096)
+    die ();
+  *p = n;
+}
+
+/*
+**trapp:
+**     cmp     r1, #4096
+**     strcc   r1, \[r0\]
+**     bxcc    lr
+**...
+*/
+void
+trapp (int *p, unsigned n)
+{
+  if (n >= 4096)
+    __builtin_trap ();
+  *p = n;
+}
diff --git a/gcc/testsuite/gcc.target/arm/cond-exec-run-1.c 
b/gcc/testsuite/gcc.target/arm/cond-exec-run-1.c
new file mode 100644
index 00000000000..2a264bc1eef
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/cond-exec-run-1.c
@@ -0,0 +1,97 @@
+/* { dg-do run } */
+/* { dg-require-effective-target arm_arm_ok } */
+/* { dg-options "-O2 -marm -march=armv6zk -mtune=arm1176jzf-s" } */
+
+int sum;
+int calls;
+
+__attribute__ ((noinline, noclone))
+void
+acc (void)
+{
+  calls++;
+}
+
+__attribute__ ((noinline, noclone))
+unsigned long
+one (void)
+{
+  calls++;
+  return 1;
+}
+
+__attribute__ ((noinline, noclone))
+int
+two (void)
+{
+  calls++;
+  return 2;
+}
+
+__attribute__ ((noinline, noclone))
+void
+die (void)
+{
+  __builtin_abort ();
+}
+
+/* Predicated indirect call.  */
+__attribute__ ((noinline, noclone))
+void
+ind (void (*fp) (void), int *p, int n)
+{
+  if (fp)
+    fp ();
+  *p = n;
+}
+
+/* Predicated sibling call.  */
+__attribute__ ((noinline, noclone))
+int
+pick (int x)
+{
+  return (int) (x ? one () : two ());
+}
+
+/* Predicated return.  */
+__attribute__ ((noinline, noclone))
+void
+setp (int *p, unsigned n)
+{
+  if (n >= 4096)
+    die ();
+  *p = n;
+}
+
+int
+main (void)
+{
+  int slot = 0;
+
+  ind (0, &slot, 5);
+  if (slot != 5 || calls != 0)
+    __builtin_abort ();
+
+  ind (acc, &slot, 7);
+  if (slot != 7 || calls != 1)
+    __builtin_abort ();
+
+  ind (acc, &slot, 9);
+  if (slot != 9 || calls != 2)
+    __builtin_abort ();
+
+  if (pick (1) != 1 || calls != 3)
+    __builtin_abort ();
+  if (pick (0) != 2 || calls != 4)
+    __builtin_abort ();
+
+  setp (&slot, 100);
+  if (slot != 100)
+    __builtin_abort ();
+  setp (&slot, 4095);
+  if (slot != 4095)
+    __builtin_abort ();
+
+  sum = slot;
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/arm/cond-exec-sibcall-1.c 
b/gcc/testsuite/gcc.target/arm/cond-exec-sibcall-1.c
new file mode 100644
index 00000000000..54d839950c2
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/cond-exec-sibcall-1.c
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+/* { dg-require-effective-target arm_arm_ok } */
+/* { dg-options "-O2 -marm -march=armv6zk -mtune=arm1176jzf-s" } */
+/* { dg-add-options check_function_bodies } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+extern unsigned long g1 (void);
+extern int g2 (void);
+
+/*
+**pick:
+**     cmp     r0, #0
+**     bne     g1(?:\(PLT\))?
+**...
+**     b       g2(?:\(PLT\))?
+*/
+int
+pick (int x)
+{
+  return (int) (x ? g1 () : g2 ());
+}
diff --git a/gcc/testsuite/gcc.target/arm/cond-exec-thumb-1.c 
b/gcc/testsuite/gcc.target/arm/cond-exec-thumb-1.c
new file mode 100644
index 00000000000..dbd488a687e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/cond-exec-thumb-1.c
@@ -0,0 +1,34 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mthumb" } */
+/* { dg-require-effective-target arm_arch_v7a_ok } */
+/* { dg-add-options arm_arch_v7a } */
+
+extern unsigned long g1 (void);
+extern int g2 (void);
+extern void die (void) __attribute__ ((noreturn));
+
+int
+pick (int x)
+{
+  return (int) (x ? g1 () : g2 ());
+}
+
+void
+setp (int *p, unsigned n)
+{
+  if (n >= 4096)
+    die ();
+  *p = n;
+}
+
+void
+ind (void (*fp) (void), int *p, int n)
+{
+  if (fp)
+    fp ();
+  *p = n;
+}
+
+/* The Arm-state condexec state machine does not run in Thumb state, so no
+   conditional branch-and-link is formed here.  */
+/* { dg-final { scan-assembler-not 
"\tblx?(eq|ne|cs|cc|mi|pl|vs|vc|hi|ls|ge|lt|gt|le)\[ \t\]" } } */
-- 
2.55.0

Reply via email to