Repository navigation
fix(rccl): replace __hip_atomic builtins with Clang scoped atomics - #11321
Conversation
mch
left a comment
There was a problem hiding this comment.
Code wise this looks good. I wonder if there is any potential performance risk here though, or risk of older compilers / versions of ROCm that don't support __scoped_atomic_* that RCCL is supposed to support.
I'll check with the compilers folks. This is a clang builtin. It's been there at least as far back as October of 2025. I saw @lfmeadow use it back then. I'll check with him. It looks like they were added in clang15. We're on clang20. Let me check which version of ROCm used clang14. Update: It looks like we moved to clang 15 for ROCm 5.4. I think this is fine. |
There was a problem hiding this comment.
ARGUS
Bottom line: Worth settling before approval: the poison probe exits 0 when no HIP compiler is found (test_poison_hip_atomics.py:117), so the guard can pass without ever probing. The 37 call-site conversions themselves check out, with no scope or memory-order drift.
Comment @argus review to re-run, or @argus re-review after pushing.
The poison probe could pass without ever probing: with no HIP compiler
found, _find_cxx() returned None, every test skipped, and unittest exited
0. Make that a failure instead, and move the checks that need no compiler
(header present, pragma contents) into their own class so they still gate
on a toolchain-less box. RCCL_POISON_TEST_ALLOW_SKIP=1 opts out locally.
Also in the probe:
- assert the pragma and the POISONED tuple are set-equal, so dropping a
name from either side fails rather than silently shrinking coverage
- compile every _call_expr() with the poison off, which is what keeps the
per-name arity table honest for all 12 names instead of just the one
the load tests use
- pin the __scoped_atomic_compare_exchange_n shape for both widths; it is
the only conversion in the port whose argument list changed
- reformat with black
do_guards ran the two guards back to back, so under set -e a kernel-count
failure aborted the phase before the poison probe. do_run had the same
short-circuit one level up. Collect both with || rc=1 and return rc, the
idiom do_host_tests already uses.
The CMake comment justified the joined --include= spelling by saying
rccl-device-compile would take the path for a source file, which the
-include/-imacros branch added in this same PR contradicts. State the
actual reason (single token, needs no cooperation from a flag forwarder)
and say why that branch is worth keeping.
Re-indent the continuation lines the rename left hanging: the callee grew
5 characters but the wrapped arguments kept their old column, and
prims_ll.h:325 ran to 121 against ColumnLimit 120. clang-format over the
changed ranges only; ce_reduce_impl.h.in included since the pre-commit
hook matches .h$ and not .h.in. Guard the poison pragma with clang-format
off/on -- clang-format has no notion of a line-continued #pragma and joins
it onto one ~300-column line.
Fix the header comment that said four headers were pulled in while three
were, and list RCCL_POISON_HIP_ATOMICS in the README and install.sh option
lists.
|
@amd-argus thanks — the bottom line was right, and worse than the comment says. Pushed f3e6526 addressing all 12 threads; replies are inline on each. The guard could pass without probingConfirmed: The fix has two parts. A missing HIP compiler is now a failure rather than a skip ( After the change, the same no-compiler run fails: and the gutted-header case now trips two tests, because the pragma check no longer depends on a compiler. One finding extendedThe Two notes back
On the Test evidence
|
|
@argus re-review |
…11321) - Retarget of the scoped-atomics work onto `rccl-develop` (see also - Device atomics now use Clang `__scoped_atomic_*` / `__atomic_*` instead of `__hip_atomic_*`, with explicit memory scopes. - Adds a compile-time poison (`RCCL_POISON_HIP_ATOMICS`, default ON) and a host-test CI probe that `__hip_atomic_load` and the rest of the `__hip_atomic_*` builtins fail to compile in RCCL sources. `src/algorithms/gin/sdma/all_reduce_gin_sdma.h` is gone on `rccl-develop`, so those five call-site edits from the develop PR are omitted here. There are **no remaining `__hip_atomic_*` call sites** in RCCL sources after this PR. The only remaining hits are the poison header, the CI probe, and comments. HIP wrapper APIs that still go through `__hip_atomic_*` **inside HIP headers** are still present. Those compile with the poison because HIP parses them **before** the pragma, so they are the remaining way `__hip_atomic_*` can still land in `librccl.so`: | API | Where | |---|---| | `atomicAdd` | `prims_ll.h`, `prims_simple.h`, `gin_anvil_sdma.h`, `gin_rocshmem_gda.h`, `llx_latency_bench.c` | | `atomicMax` | `prims_simple.h` | | `atomicCAS` | DOCA verbs headers under `src/transport/net_ib/gdaki/` | Closing that gap means porting those wrappers to `__scoped_atomic_*` with explicit scopes. I will do that as a separate PR. - [x] `./install.sh -l` still builds with poison enabled - [x] `python3 projects/rccl/src/include/test_poison_hip_atomics.py` (or `test/host/run_host_tests.sh guards`) - [ ] Confirm a deliberate `__hip_atomic_load` in a device TU fails with `attempt to use a poisoned identifier`
…11321) - Retarget of the scoped-atomics work onto `rccl-develop` (see also - Device atomics now use Clang `__scoped_atomic_*` / `__atomic_*` instead of `__hip_atomic_*`, with explicit memory scopes. - Adds a compile-time poison (`RCCL_POISON_HIP_ATOMICS`, default ON) and a host-test CI probe that `__hip_atomic_load` and the rest of the `__hip_atomic_*` builtins fail to compile in RCCL sources. `src/algorithms/gin/sdma/all_reduce_gin_sdma.h` is gone on `rccl-develop`, so those five call-site edits from the develop PR are omitted here. There are **no remaining `__hip_atomic_*` call sites** in RCCL sources after this PR. The only remaining hits are the poison header, the CI probe, and comments. HIP wrapper APIs that still go through `__hip_atomic_*` **inside HIP headers** are still present. Those compile with the poison because HIP parses them **before** the pragma, so they are the remaining way `__hip_atomic_*` can still land in `librccl.so`: | API | Where | |---|---| | `atomicAdd` | `prims_ll.h`, `prims_simple.h`, `gin_anvil_sdma.h`, `gin_rocshmem_gda.h`, `llx_latency_bench.c` | | `atomicMax` | `prims_simple.h` | | `atomicCAS` | DOCA verbs headers under `src/transport/net_ib/gdaki/` | Closing that gap means porting those wrappers to `__scoped_atomic_*` with explicit scopes. I will do that as a separate PR. - [x] `./install.sh -l` still builds with poison enabled - [x] `python3 projects/rccl/src/include/test_poison_hip_atomics.py` (or `test/host/run_host_tests.sh guards`) - [ ] Confirm a deliberate `__hip_atomic_load` in a device TU fails with `attempt to use a poisoned identifier`
…11321) - Retarget of the scoped-atomics work onto `rccl-develop` (see also - Device atomics now use Clang `__scoped_atomic_*` / `__atomic_*` instead of `__hip_atomic_*`, with explicit memory scopes. - Adds a compile-time poison (`RCCL_POISON_HIP_ATOMICS`, default ON) and a host-test CI probe that `__hip_atomic_load` and the rest of the `__hip_atomic_*` builtins fail to compile in RCCL sources. `src/algorithms/gin/sdma/all_reduce_gin_sdma.h` is gone on `rccl-develop`, so those five call-site edits from the develop PR are omitted here. There are **no remaining `__hip_atomic_*` call sites** in RCCL sources after this PR. The only remaining hits are the poison header, the CI probe, and comments. HIP wrapper APIs that still go through `__hip_atomic_*` **inside HIP headers** are still present. Those compile with the poison because HIP parses them **before** the pragma, so they are the remaining way `__hip_atomic_*` can still land in `librccl.so`: | API | Where | |---|---| | `atomicAdd` | `prims_ll.h`, `prims_simple.h`, `gin_anvil_sdma.h`, `gin_rocshmem_gda.h`, `llx_latency_bench.c` | | `atomicMax` | `prims_simple.h` | | `atomicCAS` | DOCA verbs headers under `src/transport/net_ib/gdaki/` | Closing that gap means porting those wrappers to `__scoped_atomic_*` with explicit scopes. I will do that as a separate PR. - [x] `./install.sh -l` still builds with poison enabled - [x] `python3 projects/rccl/src/include/test_poison_hip_atomics.py` (or `test/host/run_host_tests.sh guards`) - [ ] Confirm a deliberate `__hip_atomic_load` in a device TU fails with `attempt to use a poisoned identifier`
…11321) - Retarget of the scoped-atomics work onto `rccl-develop` (see also - Device atomics now use Clang `__scoped_atomic_*` / `__atomic_*` instead of `__hip_atomic_*`, with explicit memory scopes. - Adds a compile-time poison (`RCCL_POISON_HIP_ATOMICS`, default ON) and a host-test CI probe that `__hip_atomic_load` and the rest of the `__hip_atomic_*` builtins fail to compile in RCCL sources. `src/algorithms/gin/sdma/all_reduce_gin_sdma.h` is gone on `rccl-develop`, so those five call-site edits from the develop PR are omitted here. There are **no remaining `__hip_atomic_*` call sites** in RCCL sources after this PR. The only remaining hits are the poison header, the CI probe, and comments. HIP wrapper APIs that still go through `__hip_atomic_*` **inside HIP headers** are still present. Those compile with the poison because HIP parses them **before** the pragma, so they are the remaining way `__hip_atomic_*` can still land in `librccl.so`: | API | Where | |---|---| | `atomicAdd` | `prims_ll.h`, `prims_simple.h`, `gin_anvil_sdma.h`, `gin_rocshmem_gda.h`, `llx_latency_bench.c` | | `atomicMax` | `prims_simple.h` | | `atomicCAS` | DOCA verbs headers under `src/transport/net_ib/gdaki/` | Closing that gap means porting those wrappers to `__scoped_atomic_*` with explicit scopes. I will do that as a separate PR. - [x] `./install.sh -l` still builds with poison enabled - [x] `python3 projects/rccl/src/include/test_poison_hip_atomics.py` (or `test/host/run_host_tests.sh guards`) - [ ] Confirm a deliberate `__hip_atomic_load` in a device TU fails with `attempt to use a poisoned identifier`
…11321) - Retarget of the scoped-atomics work onto `rccl-develop` (see also - Device atomics now use Clang `__scoped_atomic_*` / `__atomic_*` instead of `__hip_atomic_*`, with explicit memory scopes. - Adds a compile-time poison (`RCCL_POISON_HIP_ATOMICS`, default ON) and a host-test CI probe that `__hip_atomic_load` and the rest of the `__hip_atomic_*` builtins fail to compile in RCCL sources. `src/algorithms/gin/sdma/all_reduce_gin_sdma.h` is gone on `rccl-develop`, so those five call-site edits from the develop PR are omitted here. There are **no remaining `__hip_atomic_*` call sites** in RCCL sources after this PR. The only remaining hits are the poison header, the CI probe, and comments. HIP wrapper APIs that still go through `__hip_atomic_*` **inside HIP headers** are still present. Those compile with the poison because HIP parses them **before** the pragma, so they are the remaining way `__hip_atomic_*` can still land in `librccl.so`: | API | Where | |---|---| | `atomicAdd` | `prims_ll.h`, `prims_simple.h`, `gin_anvil_sdma.h`, `gin_rocshmem_gda.h`, `llx_latency_bench.c` | | `atomicMax` | `prims_simple.h` | | `atomicCAS` | DOCA verbs headers under `src/transport/net_ib/gdaki/` | Closing that gap means porting those wrappers to `__scoped_atomic_*` with explicit scopes. I will do that as a separate PR. - [x] `./install.sh -l` still builds with poison enabled - [x] `python3 projects/rccl/src/include/test_poison_hip_atomics.py` (or `test/host/run_host_tests.sh guards`) - [ ] Confirm a deliberate `__hip_atomic_load` in a device TU fails with `attempt to use a poisoned identifier`
…OCm#11321) - Retarget of the scoped-atomics work onto `rccl-develop` (see also - Device atomics now use Clang `__scoped_atomic_*` / `__atomic_*` instead of `__hip_atomic_*`, with explicit memory scopes. - Adds a compile-time poison (`RCCL_POISON_HIP_ATOMICS`, default ON) and a host-test CI probe that `__hip_atomic_load` and the rest of the `__hip_atomic_*` builtins fail to compile in RCCL sources. `src/algorithms/gin/sdma/all_reduce_gin_sdma.h` is gone on `rccl-develop`, so those five call-site edits from the develop PR are omitted here. There are **no remaining `__hip_atomic_*` call sites** in RCCL sources after this PR. The only remaining hits are the poison header, the CI probe, and comments. HIP wrapper APIs that still go through `__hip_atomic_*` **inside HIP headers** are still present. Those compile with the poison because HIP parses them **before** the pragma, so they are the remaining way `__hip_atomic_*` can still land in `librccl.so`: | API | Where | |---|---| | `atomicAdd` | `prims_ll.h`, `prims_simple.h`, `gin_anvil_sdma.h`, `gin_rocshmem_gda.h`, `llx_latency_bench.c` | | `atomicMax` | `prims_simple.h` | | `atomicCAS` | DOCA verbs headers under `src/transport/net_ib/gdaki/` | Closing that gap means porting those wrappers to `__scoped_atomic_*` with explicit scopes. I will do that as a separate PR. - [x] `./install.sh -l` still builds with poison enabled - [x] `python3 projects/rccl/src/include/test_poison_hip_atomics.py` (or `test/host/run_host_tests.sh guards`) - [ ] Confirm a deliberate `__hip_atomic_load` in a device TU fails with `attempt to use a poisoned identifier`
## Summary Fixes a potentially serious semantic bug in the codebase that previously required the compiler to fuse two adjacent 8-byte accesses into a single 16-byte access per lane. Instead, explicitly use 16-byte operations for most datatypes. * Add `loadLLLine` / `storeLLLine` helpers in `prims_ll.h`, mirroring the LL128 `load128NT` / `store128Plain` approach * Replace paired 64-bit nontemporal loads and plain stores in LL FIFO access (`readLL`, `readLLBeginAll`, `readLLFinish`, `storeLL`) with single explicit `v4u` vector operations * Make `ncclLLFifoLine` always carry a `v4u` view so the helpers have one layout to work against * Preserve the GFX11 inline-asm b128 path, the gfx1250 system-scope path (`RCCL_LL_FIFO_SYS_SCOPE`, added to `develop` since this PR was opened), and the gfx1200/gfx1201 store fallback, which now uses `__scoped_atomic_store_n` to match `develop` after #11321 On the legacy (non-`RCCL_LL_FIFO_SYS_SCOPE`) paths, a single explicit 128-bit op removes the dependence on the backend fusing two independent 64-bit nontemporal ops into one 16-byte access, and keeps the data and flag words of a line in one memory transaction. ## Jira JIRA ID: ROCM-31751 ## Test plan * [x] Release build on gfx950, gfx1201 and gfx1100 with ROCm 7.0.2 (`ONLY_FUNCS="AllReduce RING LL Sum f32|SendRecv"`): `librccl.so` and `rcclras` link with no new diagnostics. These three targets cover the `RCCL_HAVE_GLOBAL_DWORDX4_BUILTINS` vector path, the gfx1200/gfx1201 scoped-atomic store fallback, and the `__GFX11__` inline-asm arm respectively. * [ ] gfx1250 build: not covered locally (needs ROCm 7.1.x). The `RCCL_LL_FIFO_SYS_SCOPE` arm that gfx1250 selects is unchanged by this PR. * [ ] LL protocol collectives on gfx942/gfx950/gfx1250 * [ ] Verify assembly emits `global_load/store_dwordx4` rather than paired flat/dword ops on the HIP paths Made with Cursor --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Praneeth Vallem <pvallem@amd.com>
…11321) - Retarget of the scoped-atomics work onto `rccl-develop` (see also - Device atomics now use Clang `__scoped_atomic_*` / `__atomic_*` instead of `__hip_atomic_*`, with explicit memory scopes. - Adds a compile-time poison (`RCCL_POISON_HIP_ATOMICS`, default ON) and a host-test CI probe that `__hip_atomic_load` and the rest of the `__hip_atomic_*` builtins fail to compile in RCCL sources. `src/algorithms/gin/sdma/all_reduce_gin_sdma.h` is gone on `rccl-develop`, so those five call-site edits from the develop PR are omitted here. There are **no remaining `__hip_atomic_*` call sites** in RCCL sources after this PR. The only remaining hits are the poison header, the CI probe, and comments. HIP wrapper APIs that still go through `__hip_atomic_*` **inside HIP headers** are still present. Those compile with the poison because HIP parses them **before** the pragma, so they are the remaining way `__hip_atomic_*` can still land in `librccl.so`: | API | Where | |---|---| | `atomicAdd` | `prims_ll.h`, `prims_simple.h`, `gin_anvil_sdma.h`, `gin_rocshmem_gda.h`, `llx_latency_bench.c` | | `atomicMax` | `prims_simple.h` | | `atomicCAS` | DOCA verbs headers under `src/transport/net_ib/gdaki/` | Closing that gap means porting those wrappers to `__scoped_atomic_*` with explicit scopes. I will do that as a separate PR. - [x] `./install.sh -l` still builds with poison enabled - [x] `python3 projects/rccl/src/include/test_poison_hip_atomics.py` (or `test/host/run_host_tests.sh guards`) - [ ] Confirm a deliberate `__hip_atomic_load` in a device TU fails with `attempt to use a poisoned identifier`
…11321) - Retarget of the scoped-atomics work onto `rccl-develop` (see also - Device atomics now use Clang `__scoped_atomic_*` / `__atomic_*` instead of `__hip_atomic_*`, with explicit memory scopes. - Adds a compile-time poison (`RCCL_POISON_HIP_ATOMICS`, default ON) and a host-test CI probe that `__hip_atomic_load` and the rest of the `__hip_atomic_*` builtins fail to compile in RCCL sources. `src/algorithms/gin/sdma/all_reduce_gin_sdma.h` is gone on `rccl-develop`, so those five call-site edits from the develop PR are omitted here. There are **no remaining `__hip_atomic_*` call sites** in RCCL sources after this PR. The only remaining hits are the poison header, the CI probe, and comments. HIP wrapper APIs that still go through `__hip_atomic_*` **inside HIP headers** are still present. Those compile with the poison because HIP parses them **before** the pragma, so they are the remaining way `__hip_atomic_*` can still land in `librccl.so`: | API | Where | |---|---| | `atomicAdd` | `prims_ll.h`, `prims_simple.h`, `gin_anvil_sdma.h`, `gin_rocshmem_gda.h`, `llx_latency_bench.c` | | `atomicMax` | `prims_simple.h` | | `atomicCAS` | DOCA verbs headers under `src/transport/net_ib/gdaki/` | Closing that gap means porting those wrappers to `__scoped_atomic_*` with explicit scopes. I will do that as a separate PR. - [x] `./install.sh -l` still builds with poison enabled - [x] `python3 projects/rccl/src/include/test_poison_hip_atomics.py` (or `test/host/run_host_tests.sh guards`) - [ ] Confirm a deliberate `__hip_atomic_load` in a device TU fails with `attempt to use a poisoned identifier`
…11321) - Retarget of the scoped-atomics work onto `rccl-develop` (see also - Device atomics now use Clang `__scoped_atomic_*` / `__atomic_*` instead of `__hip_atomic_*`, with explicit memory scopes. - Adds a compile-time poison (`RCCL_POISON_HIP_ATOMICS`, default ON) and a host-test CI probe that `__hip_atomic_load` and the rest of the `__hip_atomic_*` builtins fail to compile in RCCL sources. `src/algorithms/gin/sdma/all_reduce_gin_sdma.h` is gone on `rccl-develop`, so those five call-site edits from the develop PR are omitted here. There are **no remaining `__hip_atomic_*` call sites** in RCCL sources after this PR. The only remaining hits are the poison header, the CI probe, and comments. HIP wrapper APIs that still go through `__hip_atomic_*` **inside HIP headers** are still present. Those compile with the poison because HIP parses them **before** the pragma, so they are the remaining way `__hip_atomic_*` can still land in `librccl.so`: | API | Where | |---|---| | `atomicAdd` | `prims_ll.h`, `prims_simple.h`, `gin_anvil_sdma.h`, `gin_rocshmem_gda.h`, `llx_latency_bench.c` | | `atomicMax` | `prims_simple.h` | | `atomicCAS` | DOCA verbs headers under `src/transport/net_ib/gdaki/` | Closing that gap means porting those wrappers to `__scoped_atomic_*` with explicit scopes. I will do that as a separate PR. - [x] `./install.sh -l` still builds with poison enabled - [x] `python3 projects/rccl/src/include/test_poison_hip_atomics.py` (or `test/host/run_host_tests.sh guards`) - [ ] Confirm a deliberate `__hip_atomic_load` in a device TU fails with `attempt to use a poisoned identifier`
Summary
rccl-develop(see also fix(rccl): replace deprecated __hip_atomic builtins with __scoped_atomic #11316 againstdevelop).__scoped_atomic_*/__atomic_*instead of__hip_atomic_*, with explicit memory scopes.RCCL_POISON_HIP_ATOMICS, default ON) and a host-test CI probe that__hip_atomic_loadand the rest of the__hip_atomic_*builtins fail to compile in RCCL sources.src/algorithms/gin/sdma/all_reduce_gin_sdma.his gone onrccl-develop, so those five call-site edits from the develop PR are omitted here.Remaining
__hip_atomic_*coverageThere are no remaining
__hip_atomic_*call sites in RCCL sources after this PR. The only remaining hits are the poison header, the CI probe, and comments.HIP wrapper APIs that still go through
__hip_atomic_*inside HIP headers are still present. Those compile with the poison because HIP parses them before the pragma, so they are the remaining way__hip_atomic_*can still land inlibrccl.so:atomicAddprims_ll.h,prims_simple.h,gin_anvil_sdma.h,gin_rocshmem_gda.h,llx_latency_bench.catomicMaxprims_simple.hatomicCASsrc/transport/net_ib/gdaki/Closing that gap means porting those wrappers to
__scoped_atomic_*with explicit scopes. I will do that as a separate PR.Test plan
./install.sh -lstill builds with poison enabledpython3 projects/rccl/src/include/test_poison_hip_atomics.py(ortest/host/run_host_tests.sh guards)__hip_atomic_loadin a device TU fails withattempt to use a poisoned identifier