[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
Mon Jul 20 00:40:14 PDT 2026
https://github.com/ritter-x2a updated https://github.com/llvm/llvm-project/pull/204329
>From d8f7ce063cf04133f446e078e6f02126ac8dc79f 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/5] [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 9526cd020d724..3f793364df384 100644
--- a/llvm/docs/LangRef.md
+++ b/llvm/docs/LangRef.md
@@ -4062,12 +4062,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 29a08b541c739bb671757d9312658f9209ac0573 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/5] "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 3f793364df384..d58eb4e39da9f 100644
--- a/llvm/docs/LangRef.md
+++ b/llvm/docs/LangRef.md
@@ -4069,8 +4069,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 dcb93429a67e45c971a0b1b21ac1636bb33bada4 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/5] 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 d58eb4e39da9f..f722cb72d2418 100644
--- a/llvm/docs/LangRef.md
+++ b/llvm/docs/LangRef.md
@@ -4069,10 +4069,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 222d484e6820cb4ee665f6012aeb61c83108068c 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/5] 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 f722cb72d2418..0089d4e47a3e6 100644
--- a/llvm/docs/LangRef.md
+++ b/llvm/docs/LangRef.md
@@ -4069,8 +4069,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
>From 56e13dc6b06516cad33d4c0dd389b94b517ec0e5 Mon Sep 17 00:00:00 2001
From: Fabian Ritter <fabian.ritter at amd.com>
Date: Mon, 20 Jul 2026 03:36:54 -0400
Subject: [PATCH 5/5] Explicitly restrict the atomic ordering constraints to
perfectly overlapping accesses.
---
llvm/docs/LangRef.md | 47 +++++++++++++++++++++++++++-----------------
1 file changed, 29 insertions(+), 18 deletions(-)
diff --git a/llvm/docs/LangRef.md b/llvm/docs/LangRef.md
index 0089d4e47a3e6..8dac361cc5176 100644
--- a/llvm/docs/LangRef.md
+++ b/llvm/docs/LangRef.md
@@ -4105,6 +4105,12 @@ address. See that instruction's documentation for details.
For a simpler introduction to the ordering constraints, see the
{doc}`Atomics`.
+For the following, we call two or more accesses *perfectly overlapping*
+if they all access the exact same set of bytes, i.e., they access the
+same address and have the same access size. By the constraints of the
+previous section, racing atomic accesses must be perfectly overlapping
+to act atomically.
+
`unordered`
: The set of values that can be read is governed by the happens-before
partial order. A value cannot be read unless some operation wrote
@@ -4114,22 +4120,24 @@ For a simpler introduction to the ordering constraints, see the
to make them atomic in any interesting way.
`monotonic`
-: In addition to the guarantees of `unordered`, there is a single
- total order for modifications by `monotonic` operations on each
- address. All modification orders must be compatible with the
+: In addition to the guarantees of `unordered`, there is a total order
+ of modifications for each set of perfectly overlapping `monotonic`
+ operations.
+ All modification orders must be compatible with the
happens-before order. There is no guarantee that the modification
orders can be combined to a global total order for the whole program
(and this often will not be possible). If the read in an atomic
read-modify-write operation M ({ref}`cmpxchg <i_cmpxchg>` and
- {ref}`atomicrmw <i_atomicrmw>`) reads from a `monotonic` (or
- stronger) write W, W must be immediately before M in the address's
- modification order. If one atomic read happens before another atomic
- read of the same address and both are at least `monotonic`, the
- later read must not see an earlier value in the address's
- modification order. This disallows reordering of `monotonic` (or
- stronger) operations on the same address. If an address is written
- `monotonic`-ally by one thread, and other threads `monotonic`-ally
- read that address repeatedly, the other threads must eventually see
+ {ref}`atomicrmw <i_atomicrmw>`) reads from a perfectly overlapping
+ `monotonic` (or stronger) write W, W must be immediately before M in
+ the relevant modification order. If one atomic read happens before
+ another perfectly overlapping atomic read and both are at least
+ `monotonic`, the later read must not see an earlier value in the
+ address's modification order. This disallows reordering of perfectly
+ overlapping `monotonic` (or stronger) operations. If an address is
+ written `monotonic`-ally by one thread, and other threads
+ `monotonic`-ally read that address repeatedly with perfectly
+ overlapping accesses, the other threads must eventually see
the write. This corresponds to the C/C++ `memory_order_relaxed`.
`acquire`
@@ -4139,10 +4147,11 @@ For a simpler introduction to the ordering constraints, see the
`release`
: In addition to the guarantees of `monotonic`, if this operation
- writes a value which is subsequently read by an `acquire`
- operation, it *synchronizes-with* that operation. Furthermore,
- this occurs even if the value written by a `release` operation
- has been modified by a read-modify-write operation before being
+ writes a value which is subsequently read by a perfectly overlapping
+ `acquire` operation, it *synchronizes-with* that operation.
+ Furthermore, this occurs even if the value written by a `release`
+ operation has been modified by a perfectly overlapping
+ read-modify-write operation before being
read. (Such a set of operations comprises a *release
sequence*). This corresponds to the C/C++
`memory_order_release`.
@@ -4156,7 +4165,8 @@ For a simpler introduction to the ordering constraints, see the
operation that only reads, `release` for an operation that only
writes), there is a global total order on all
sequentially-consistent operations on all addresses. If an address
- is only accessed through sequentially-consistent operations, each
+ is only accessed through perfectly overlapping
+ sequentially-consistent operations, each
sequentially-consistent read of that address sees the last preceding
write to the same address in this global order. This corresponds to
the C/C++ `memory_order_seq_cst` and Java `volatile`.
@@ -11967,7 +11977,8 @@ defines what *synchronizes-with* edges they add. They can only be given
A fence A which has (at least) `release` ordering semantics
*synchronizes with* a fence B with (at least) `acquire` ordering
semantics if and only if there exist atomic operations X and Y, both
-operating on some atomic object M, such that A is sequenced before X, X
+operating on some atomic object M with the same address and access size,
+such that A is sequenced before X, X
modifies M (either directly or through some side effect of a sequence
headed by X), Y is sequenced before B, and Y observes M. This provides a
*happens-before* dependency between A and B. Rather than an explicit
More information about the llvm-commits
mailing list