[llvm] [RFC][LangRef] Specify that the accessed bytes of concurrent atomics must be either disjoint or the same (PR #204329)

Fabian Ritter via llvm-commits llvm-commits at lists.llvm.org
Tue Jul 14 23:53:04 PDT 2026


https://github.com/ritter-x2a updated https://github.com/llvm/llvm-project/pull/204329

>From eb2ae542690113d0db470b9e1d58725cef74dbfa Mon Sep 17 00:00:00 2001
From: Fabian Ritter <fabian.ritter at amd.com>
Date: Wed, 17 Jun 2026 05:59:01 -0400
Subject: [PATCH 1/4] [RFC][LangRef] Specify that the accessed bytes of
 concurrent atomics must be either disjoint or the same

So far, the LangRef hasn't been clear on the semantics of partially overlapping
concurrent atomics in LLVM IR (specifically: a set of accesses marked as
`atomic` that would be in a data race if they weren't `atomic` and not all of
them access the exact same set of bytes).

What loads read is defined in terms of individual bytes, but the memory
ordering constraints are formulated closely to the C/C++ (and Java for
`unordered`) memory model, where partially overlapping atomics are not
possible. It's not obvious how concepts like C/C++'s per-location total
modification order for `monotonic` accesses map to accesses that can partially
overlap. While C/C++ relies on the modification order to ensure that atomics
cannot tear (i.e., atomic reads return bytes from two or more atomic writes),
our IR semantics (as written) currently does not guarantee this in the presence
of partially overlapping accesses.

This PR proposes a solution to this problem: It specifies that concurrent
overlapping atomics must access the exact same set of bytes to act atomically.
If they don't, they form a data race (i.e., participating loads read `undef`
for affected bytes). This empowers the rest of the specification to imply that
`monotonic` (or stronger) accesses do not tear. The PR also adds a constraint
to ensure non-tearing for `unordered` atomic accesses.

This solution implies that transformations that merge adjacent atomic
loads/stores into wider atomic loads/stores are generally incorrect.
---
 llvm/docs/LangRef.md | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/llvm/docs/LangRef.md b/llvm/docs/LangRef.md
index f7e3ca8db80e1..c771e1e260167 100644
--- a/llvm/docs/LangRef.md
+++ b/llvm/docs/LangRef.md
@@ -4057,12 +4057,16 @@ Given that definition, R{sub}`byte` is defined as follows:
 -  Otherwise, if R{sub}`byte` may see exactly one write,
    R{sub}`byte` returns the value written by that write.
 -  Otherwise, if R is atomic, and all the writes R{sub}`byte` may
-   see are atomic, it chooses one of the values written. See the {ref}`Atomic
+   see are atomic, and R and the writes all access the exact same set of
+   bytes, it chooses one of the values written. See the {ref}`Atomic
    Memory Ordering Constraints <ordering>` section for additional
    constraints on how the choice is made. Targets may impose additional
    requirements on R and the writes it may see based on their `syncscope`.
 -  Otherwise R{sub}`byte` returns `undef`.
 
+Defined atomic accesses cannot tear: An atomic read cannot read bytes
+from more than one atomic write.
+
 R returns the value composed of the series of bytes it read. This
 implies that some bytes within the value may be `undef` **without**
 the entire value being `undef`. Note that this only defines the

>From e0dcab4ba2a4d6707bc6acc5764bea863e64d0f7 Mon Sep 17 00:00:00 2001
From: Fabian Ritter <fabian.ritter at amd.com>
Date: Thu, 18 Jun 2026 03:17:37 -0400
Subject: [PATCH 2/4] "un-simplify" the no-tearing constraint

---
 llvm/docs/LangRef.md | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/llvm/docs/LangRef.md b/llvm/docs/LangRef.md
index c771e1e260167..4591797708e60 100644
--- a/llvm/docs/LangRef.md
+++ b/llvm/docs/LangRef.md
@@ -4064,8 +4064,10 @@ Given that definition, R{sub}`byte` is defined as follows:
    requirements on R and the writes it may see based on their `syncscope`.
 -  Otherwise R{sub}`byte` returns `undef`.
 
-Defined atomic accesses cannot tear: An atomic read cannot read bytes
-from more than one atomic write.
+Defined atomic accesses cannot tear: Two byte subaccesses R{sub}`byte1`,
+R{sub}`byte2` of an atomic read R cannot read from different atomic
+writes W{sub}`1`, W{sub}`2` if both read subaccesses, R{sub}`byte1`,
+R{sub}`byte2`, may see both writes, W{sub}`1`, W{sub}`2`.
 
 R returns the value composed of the series of bytes it read. This
 implies that some bytes within the value may be `undef` **without**

>From 454bb24df08625be70b59d9a2e23c5f03e3812c1 Mon Sep 17 00:00:00 2001
From: Fabian Ritter <fabian.ritter at amd.com>
Date: Tue, 14 Jul 2026 03:42:08 -0400
Subject: [PATCH 3/4] Adapt Ralf's simpler no-tearing formulation.

---
 llvm/docs/LangRef.md | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/llvm/docs/LangRef.md b/llvm/docs/LangRef.md
index 4591797708e60..bb086d61a50cf 100644
--- a/llvm/docs/LangRef.md
+++ b/llvm/docs/LangRef.md
@@ -4064,10 +4064,9 @@ Given that definition, R{sub}`byte` is defined as follows:
    requirements on R and the writes it may see based on their `syncscope`.
 -  Otherwise R{sub}`byte` returns `undef`.
 
-Defined atomic accesses cannot tear: Two byte subaccesses R{sub}`byte1`,
-R{sub}`byte2` of an atomic read R cannot read from different atomic
-writes W{sub}`1`, W{sub}`2` if both read subaccesses, R{sub}`byte1`,
-R{sub}`byte2`, may see both writes, W{sub}`1`, W{sub}`2`.
+Defined atomic accesses cannot tear: If a byte subaccess R{sub}`byte1`
+of an atomic read R reads from an atomic write W, then all other byte
+subaccesses R{sub}`byte2` of R that may see W must also read from W.
 
 R returns the value composed of the series of bytes it read. This
 implies that some bytes within the value may be `undef` **without**

>From b64be89ff86ab4fbf03ebb389bbaf03c72d64919 Mon Sep 17 00:00:00 2001
From: Fabian Ritter <fabian.ritter at amd.com>
Date: Wed, 15 Jul 2026 02:50:05 -0400
Subject: [PATCH 4/4] Remove vague use of "defined"

---
 llvm/docs/LangRef.md | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/llvm/docs/LangRef.md b/llvm/docs/LangRef.md
index bb086d61a50cf..46e8eb04d4192 100644
--- a/llvm/docs/LangRef.md
+++ b/llvm/docs/LangRef.md
@@ -4064,8 +4064,8 @@ Given that definition, R{sub}`byte` is defined as follows:
    requirements on R and the writes it may see based on their `syncscope`.
 -  Otherwise R{sub}`byte` returns `undef`.
 
-Defined atomic accesses cannot tear: If a byte subaccess R{sub}`byte1`
-of an atomic read R reads from an atomic write W, then all other byte
+Atomic accesses cannot tear: If a byte subaccess R{sub}`byte1` of an
+atomic read R reads from an atomic write W, then all other byte
 subaccesses R{sub}`byte2` of R that may see W must also read from W.
 
 R returns the value composed of the series of bytes it read. This



More information about the llvm-commits mailing list