Skip to content

llvm_passes: lower 8 and 16 bit atomics onto their containing 32 bit word - #1512

Merged
pvelesko merged 2 commits into
mainfrom
2026-08-29-github-1497-subword-atomics
Aug 31, 2026
Merged

llvm_passes: lower 8 and 16 bit atomics onto their containing 32 bit word#1512
pvelesko merged 2 commits into
mainfrom
2026-08-29-github-1497-subword-atomics

Conversation

@pvelesko

Copy link
Copy Markdown
Collaborator

Clang lowers __hip_atomic_* on char and short to i8 and i16 atomic instructions, and both SPIR-V producers pass them through as OpAtomicLoad, OpAtomicIAdd, OpAtomicCompareExchange and friends on 8 and 16 bit integers, which OpenCL SPIR-V consumers do not implement: IGC fails the module build with undefined reference to _Z18__spirv_AtomicLoadPU3AS4cii and the Intel CPU OpenCL runtime with JIT session error: Symbols not found, taking every kernel in the module down with it. This adds HipLowerSubwordAtomicsPass, which rewrites each 8 and 16 bit atomic load, store, atomicrmw and cmpxchg onto the aligned 32 bit word that contains it (atomic load plus shift for loads, cmpxchg loops touching only the affected lane for the rest), keeping the ordering and syncscope. Found with Kokkos TestAtomicOperations on signed char on Aurora PVC. TestSubwordAtomics drives every form from all four byte lanes of a word concurrently, and subwordAtomics/subword-atomics.ll checks that no i8 or i16 atomic survives the post-link pipeline.

Fixes #1497

Clang lowers __hip_atomic_load / store / exchange / fetch_* /
compare_exchange_strong on char and short to load atomic i8, store atomic
i16, atomicrmw i8 and cmpxchg i16, and chipStar hands those to SPIR-V as
OpAtomicLoad and friends on OpTypeInt 8 / 16. OpenCL SPIR-V consumers only
implement 32 and 64 bit atomics: IGC fails the module build with

  error: undefined reference to `_Z18__spirv_AtomicLoadPU3AS4cii'
  error: backend compiler failed build.

and the Intel CPU OpenCL runtime with

  JIT session error: Symbols not found:
   [ _Z20atomic_load_explicitPU3AS1VU7_Atomicc12memory_order12memory_scope, ... ]

which takes every kernel in the module down with it (Kokkos
TestAtomicOperations on signed char, #1497).

TestSubwordAtomics.hip drives every 8 and 16 bit atomic form from all four
byte lanes of a word concurrently and checks the counts, so a lowering
that touches the neighbouring lanes non-atomically is caught as lost
updates rather than passing quietly. subwordAtomics/subword-atomics.ll
runs the post-link pass pipeline over every i8 / i16 atomic instruction
in the global, local and generic address spaces and fails while any of
them survives.

See #1497
…word

Clang lowers __hip_atomic_load / store / exchange / fetch_* /
compare_exchange on char and short to load atomic i8, store atomic i16,
atomicrmw i8 and cmpxchg i16, and both SPIR-V producers pass those through
as OpAtomicLoad, OpAtomicStore, OpAtomicIAdd, OpAtomicCompareExchange ...
on OpTypeInt 8 / 16. OpenCL SPIR-V consumers only implement 32 and 64 bit
atomics, so the module build fails inside the driver, past spirv-val:
IGC with

  error: undefined reference to `_Z18__spirv_AtomicLoadPU3AS4cii'
  error: backend compiler failed build.

and the Intel CPU runtime with

  JIT session error: Symbols not found:
   [ _Z20atomic_load_explicitPU3AS1VU7_Atomicc12memory_order12memory_scope, ... ]

Either failure takes every kernel in the module down with it, which is
how Kokkos' TestAtomicOperations on signed char sinks the whole
Kokkos_CoreUnitTest_HIP binary on Aurora.

Add HipLowerSubwordAtomicsPass, which does what LLVM's AtomicExpand does
for targets without narrow atomics: locate the aligned 32 bit word that
holds the value and the lane's bit offset in it, then

  load      -> 32 bit atomic load, shift, truncate
  store     -> cmpxchg loop replacing only the lane
  atomicrmw -> cmpxchg loop applying the operation to the lane only
               (every integer op, plus fadd / fsub / fmin / fmax on
               16 bit floats)
  cmpxchg   -> 32 bit cmpxchg with the expected and new values masked
               into the current word, retried while only the other
               lanes changed (strong) or not at all (weak)

Ordering and syncscope are carried over unchanged; the loops' initial
read is a monotonic atomic load in the same scope. The word address is a
GEP off the original pointer rather than an inttoptr so the address space
survives and InferAddressSpaces can still narrow a generic pointer. The
pass runs after HipLowerFPAtomicMinMax so the i16 cmpxchg that one emits
for half gets lowered as well. Values are assumed naturally aligned,
which is all clang emits; an under-aligned 16 bit atomic could straddle
two words and is left in place with a warning.

Fixes #1497
@pvelesko

Copy link
Copy Markdown
Collaborator Author

/run-aurora-ci

@pvelesko

Copy link
Copy Markdown
Collaborator Author

/run-aurora-ci

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8-bit atomics reach SPIR-V unlowered: IGC has no __spirv_AtomicLoad/Store/CompareExchange for char, module build fails

1 participant