EnzymeAD / EnzymeAD/Enzyme

Cannot select: 0x6fe5990: f32,ch = AtomicLoadFAdd for clang19+

Open
#2,796 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
LLVM
Stars
1.7k
Forks
188
Avg merge
1d 22h
Merged PRs (30d)
26

Description

fatal error: error in backend: Cannot select: 0x6fe5990: f32,ch = AtomicLoadFAdd<(load store monotonic (s32) on %ir., addrspace 101)> 0x5b671e0, 0x24330d0, 0x4003440 0x24330d0: i64,ch = CopyFromReg 0x3223500, Register:i64 %8 0x4003440: f32,ch = load<(load (s32) from %ir.23, !tbaa !36, !alias.scope !42, !noalias !43, addrspace 1)> 0x6fe5df0, 0x2433060, undef:i64 0x2433060: i64 = add 0x5b67800, 0x6fe5370 0x5b67800: i64 = addrspacecast[0 -> 1] 0x6fe57d0 0x6fe57d0: i64,ch = load<(invariant load (s64) from %ir.6, !tbaa !35, !alias.scope !33, !noalias !30, addrspace 101)> 0x3223500, 0x2433220, undef:i64 0x2433220: i64,ch = CopyFromReg 0x3223500, Register:i64 %0 0x6fe5370: i64 = NVPTXISD::MUL_WIDE_UNSIGNED 0x5b673a0, Constant:i32<4> 0x5b673a0: i32,ch = CopyFromReg 0x3223500, Register:i32 %2 In function: _ZN8Cromwell8Patterns28flux_map_kernel_cuda_reverseINS_11CommonModel27BoundaryConditionUDFLibrary13priv_BCScalar24OperationScalarPopulatorEjLi0ELi1EEEvjT_S6_NS_10Containers10CRSMapViewIT0_S9_EEj clang++: error: clang frontend command failed with exit code 70 (use -v to see invocation) clang version 21.1.8 (https://github.com/llvm/llvm-project.git 2078da43e25a4623cab2d0d60decddf709aaea28) Target: x86_64-pc-linux-gnu Thread model: posix InstalledDir: /home/mixu/software/build_enzyme/install/llvm-21.1.8/bin clang++: note: diagnostic msg: ******************** PLEASE ATTACH THE FOLLOWING FILES TO THE BUG REPORT: Preprocessed source(s) and associated run script(s) are located at: clang++: note: diagnostic msg: /tmp/Scalar-sm_80-942488.cu clang++: note: diagnostic msg: /tmp/Scalar-1d0c0d.cu clang++: note: diagnostic msg: /tmp/Scalar-sm_80-942488.sh clang++: note: diagnostic msg: ********************

This issue is occurs in clang21 but not in the clang 19,

A simple IR reproducer https://tinyurl.com/28qwe9ag

gives

"%1 = atomicrmw fadd ptr %"val_ptr'ipg_unwrap", float %0 monotonic, align 4"

Here is the investigation from chatgpt "what changed between Clang 19 and 21 in NVPTX lowering, and what Enzyme is generating around this reverse kernel so we can pin down a practical workaround.
I’ve narrowed this down to a backend regression area, not an Enzyme math rule issue: the failing node is an atomicrmw fadd that LLVM 21 still thinks lives in NVPTX .param space (addrspace(101)), which is a bad place for an atomic.
Thought for 6m 25s
This looks like an LLVM NVPTX backend regression / gap, not an Enzyme differentiation-rule bug.
Your crash says the selector is trying to lower:
AtomicLoadFAdd ... addrspace 101
On NVPTX, address space 101 is .param. LLVM documents that as ADDRESS_SPACE_PARAM = 101.
The problem is that .param is special: NVPTX says kernel/device parameters are accessed through ld.param, and if you need to write them or take an address that behaves like writable memory, LLVM must first make a local copy. The NVPTX lowering comments say device params are passed via param space, kernel params are read-only, and for device functions “taking the address” returns a pointer to a copy in local space; byval params may need copying into local memory.
At the same time, in LLVM 21.1.8, NVPTX’s shouldExpandAtomicRMWInIR returns None for atomicrmw fadd on float, meaning the backend expects to lower that atomic natively instead of expanding it away in IR.
So the failure mode is:
1. Enzyme emits a reverse accumulation as atomicrmw fadd float.
2. LLVM 21 keeps that as a native NVPTX atomic path.
3. But the pointer is still effectively in .param space (addrspace 101).
4. NVPTX has no legal selector pattern for atomic add in param space, so instruction selection dies with Cannot select: AtomicLoadFAdd ... addrspace 101."

The suggestions:
"
2. First quick test: enable early byval copies
• Add:
-mllvm -nvptx-early-byval-copy
• LLVM has a hidden NVPTX option exactly for this: nvptx-early-byval-copy, described as “Create a copy of byval function arguments early.” The surrounding comments explicitly say byval args are special and writable/address-taken uses need a local copy.
This is the first thing I would try in your exact build, because it may move the problematic object out of .param early enough that the reverse atomic becomes local/global instead of param.

4. Enzyme-side workaround: avoid atomic accumulation on param-space shadows
• I would not disable all atomic adds globally on NVPTX unless you are sure there is no race.
• But a targeted Enzyme workaround is reasonable: detect when the destination pointer for addToDiffe is in NVPTX param space / corresponds to a byval shadow, and do not emit atomicrmw there.
• Instead, force that shadow to live in local memory earlier, or avoid treating that byval aggregate as an atomic-updated destination.
"

I had confirmed that -mllvm -nvptx-early-byval-copy helps but it reduces the efficience. An fix to the LLVM code is
```
diff --git a/llvm/lib/Target/NVPTX/NVPTXLowerArgs.cpp b/llvm/lib/Target/NVPTX/NVPTXLowerArgs.cpp
index f4362fe8d905..5566a2b93cae 100644
--- a/llvm/lib/Target/NVPTX/NVPTXLowerArgs.cpp
+++ b/llvm/lib/Target/NVPTX/NVPTXLowerArgs.cpp
@@ -498,6 +498,18 @@ struct ArgUseChecker : PtrUseVisitor {
if (!IsGridConstant)
PI.setAborted(&II);
}
+
+ void visitAtomicRMWInst(AtomicRMWInst &AI) {
+ // atomicrmw writes to the pointer, needs a local copy.
+ if (!IsGridConstant)
+ PI.setAborted(&AI);
+ }
+
+ void visitAtomicCmpXchgInst(AtomicCmpXchgInst &AI) {
+ // cmpxchg writes to the pointer, needs a local copy.
+ if (!IsGridConstant)
+ PI.setAborted(&AI);
+ }
}; // struct ArgUseChecker

void copyByValParam(Function &F, Argument &Arg) {

```

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.