[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