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
