[PATCH 2/6] arm: Fix missing acquire barrier for compare_exchange release/acquire on ARMv6/v7 [PR96056]
Dominic P <[email protected]> Sun, 2 Aug 2026 11:56:04 +0100
| Newsgroups | gmane.comp.gcc.patches |
|---|---|
| Message-ID | <[email protected]> |
An __atomic_compare_exchange with a success memory order of RELEASE and a failure memory order of ACQUIRE requires release ordering on the store path and acquire ordering on the (load-only) fail path. This is a well-formed combination: the two orders govern different sub-operations, and since C++17 the failure order need only avoid RELEASE/ACQ_REL and is otherwise unconstrained relative to the success order. arm_expand_compare_and_swap promotes the success model to ACQ_REL for this case so that both the release and the acquire orderings are preserved, but the promotion was gated on TARGET_HAVE_LDACQ. On ARMv6/ARMv7, which lack load-acquire/store-release instructions, the ordering is instead provided by explicit DMB barriers derived solely from the success memory model in arm_split_compare_and_swap. Without the promotion the success model stayed RELEASE, so need_atomic_barrier_p emitted only the pre (release) barrier and dropped the post (acquire) barrier. The fail path was therefore left with no acquire barrier, allowing later memory accesses to be reordered before the failed CAS load and violating the requested acquire semantics. For armv7-a the wrong sequence was: dmb ish .L2: ldrex r2, [r3] cmp r2, r0 bne .L3 strex ip, r1, [r3] cmp ip, #0 bne .L2 .L3: <- fall-through, no acquire barrier Remove the TARGET_HAVE_LDACQ guard so the promotion, and hence the trailing acquire barrier, is applied on all targets. ARMv8 LDACQ targets are unaffected: they already promoted and continue to emit ldaex/stlex with no DMB. Assisted-by: Claude Opus 5 (Anthropic) gcc/ChangeLog: PR target/96056 * config/arm/arm.cc (arm_expand_compare_and_swap): Promote the success memory model to ACQ_REL for a RELEASE/ACQUIRE compare_exchange on all targets, not only TARGET_HAVE_LDACQ ones, so that the acquire barrier is not dropped on ARMv6/ARMv7. gcc/testsuite/ChangeLog: PR target/96056 * gcc.target/arm/atomic-comp-swap-release-acquire-4.c: New test. Signed-off-by: Dominic P <[email protected]> --- gcc/config/arm/arm.cc | 12 +++++++++--- .../arm/atomic-comp-swap-release-acquire-4.c | 16 ++++++++++++++++ 2 files changed, 25 insertions(+), 3 deletions(-) create mode 100644 gcc/testsuite/gcc.target/arm/atomic-comp-swap-release-acquire-4.c diff --git a/gcc/config/arm/arm.cc b/gcc/config/arm/arm.cc index 4597b6bc0..62534baca 100644 --- a/gcc/config/arm/arm.cc +++ b/gcc/config/arm/arm.cc @@ -31367,10 +31367,16 @@ arm_expand_compare_and_swap (rtx operands[]) /* Normally the succ memory model must be stronger than fail, but in the unlikely event of fail being ACQUIRE and succ being RELEASE we need to - promote succ to ACQ_REL so that we don't lose the acquire semantics. */ + promote succ to ACQ_REL so that we don't lose the acquire semantics. - if (TARGET_HAVE_LDACQ - && is_mm_acquire (memmodel_from_int (INTVAL (mod_f))) + This must be done for all targets, not just those with load-acquire / + store-release (TARGET_HAVE_LDACQ) instructions: on ARMv6/ARMv7 the + ordering is provided by an explicit barrier (DMB), and without the + promotion the acquire barrier that the fail path requires would be + dropped, since the barriers are derived from the succ memory model + alone. [PR96056] */ + + if (is_mm_acquire (memmodel_from_int (INTVAL (mod_f))) && is_mm_release (memmodel_from_int (INTVAL (mod_s)))) mod_s = GEN_INT (MEMMODEL_ACQ_REL); diff --git a/gcc/testsuite/gcc.target/arm/atomic-comp-swap-release-acquire-4.c b/gcc/testsuite/gcc.target/arm/atomic-comp-swap-release-acquire-4.c new file mode 100644 index 000000000..b87130cf9 --- /dev/null +++ b/gcc/testsuite/gcc.target/arm/atomic-comp-swap-release-acquire-4.c @@ -0,0 +1,16 @@ +/* PR target/96056: on ARMv6/ARMv7 (no load-acquire/store-release), a + compare_exchange with success=RELEASE and failure=ACQUIRE must still + emit the acquire barrier required by the fail path. The ordering is + provided by explicit DMBs, so each of the 4 functions needs a barrier + before (release) and after (acquire) the ldrex/strex sequence. */ + +/* { dg-do compile } */ +/* { dg-require-effective-target arm_arch_v7a_ok } */ +/* { dg-options "-O2 -fno-ipa-icf" } */ +/* { dg-add-options arm_arch_v7a } */ + +#include "../aarch64/atomic-comp-swap-release-acquire.x" + +/* { dg-final { scan-assembler-not "ldaex" } } */ +/* { dg-final { scan-assembler-not "stlex" } } */ +/* { dg-final { scan-assembler-times "dmb\tish" 8 } } */ -- 2.55.0