Incorrect number of arguments passed to called function! call void @llvm.nvvm.barrier.cta.sync.aligned.all()
- Dominant language
- LLVM
- Stars
- 1.7k
- Forks
- 188
- Avg merge
- 1d 22h
- Merged PRs (30d)
- 26
Description
https://tinyurl.com/2annfzyp
This code is fine in clang 19 but failed with clang 21. Here is the proposed fix by AI
```
Yes — this is an Enzyme bug in the LLVM>20 CUDA barrier handling, not a problem in your kernel.
The giveaway is in your IR:
the original barrier inside the cloned function is valid:
call void @llvm.nvvm.barrier.cta.sync.aligned.all(i32 0)
but Enzyme also emits invalid reverse-pass calls:
call void @llvm.nvvm.barrier.cta.sync.aligned.all()
For the newer NVVM intrinsic form, llvm.nvvm.barrier.cta.sync.aligned.all takes an i32 barrier id, and LLVM’s NVVM auto-upgrade maps the old barrier0 form to barrier.cta.sync.aligned.all(i32 0).
So why did Clang 19.1.7 work? Because on the old LLVM path, Enzyme used llvm.nvvm.barrier0() with zero arguments, which matched the old intrinsic shape. On the newer path, Enzyme switched to the new intrinsic name, but in AdjointGenerator.h it still builds the reverse barrier call with an empty argument list. That is exactly what triggers the verifier error “Incorrect number of arguments passed to called function!”. The “warning found shared memory” lines are just warnings; the real failure is the malformed barrier call.
The buggy code is in enzyme/Enzyme/AdjointGenerator.h, around the handleAdjointForIntrinsic(...) logic for:
nvvm_barrier_cta_sync_aligned_all
nvvm_barrier_cta_sync_aligned_count
the reduction barriers that Enzyme lowers back to a sync barrier in reverse mode
Right now it does this for LLVM>20:
SmallVector args = {};
auto cal = cast(Builder2.CreateCall(
getIntrinsicDeclaration(M, Intrinsic::nvvm_barrier_cta_sync_aligned_all),
args));
That should not be empty.
Use this fix.
diff --git a/enzyme/Enzyme/AdjointGenerator.h b/enzyme/Enzyme/AdjointGenerator.h
--- a/enzyme/Enzyme/AdjointGenerator.h
+++ b/enzyme/Enzyme/AdjointGenerator.h
@@ -3940,24 +3940,49 @@
#if LLVM_VERSION_MAJOR < 22
case Intrinsic::nvvm_barrier0_popc:
case Intrinsic::nvvm_barrier0_and:
case Intrinsic::nvvm_barrier0_or:
#else
case Intrinsic::nvvm_barrier_cta_red_and_aligned_all:
- case Intrinsic::nvvm_barrier_cta_red_and_aligned_count:
case Intrinsic::nvvm_barrier_cta_red_or_aligned_all:
- case Intrinsic::nvvm_barrier_cta_red_or_aligned_count:
case Intrinsic::nvvm_barrier_cta_red_popc_aligned_all:
- case Intrinsic::nvvm_barrier_cta_red_popc_aligned_count:
#endif
{
- SmallVector args = {};
+ SmallVector args = {};
#if LLVM_VERSION_MAJOR > 20
+ args.push_back(I.getOperand(0)); // barrier id
+ auto *Fn =
+ getIntrinsicDeclaration(M, Intrinsic::nvvm_barrier_cta_sync_aligned_all);
auto cal = cast(Builder2.CreateCall(
- getIntrinsicDeclaration(
- M, Intrinsic::nvvm_barrier_cta_sync_aligned_all),
+ Fn,
args));
- cal->setCallingConv(getIntrinsicDeclaration(
- M, Intrinsic::nvvm_barrier_cta_sync_aligned_all)
- ->getCallingConv());
+ cal->setCallingConv(Fn->getCallingConv());
#else
+ auto *Fn = getIntrinsicDeclaration(M, Intrinsic::nvvm_barrier0);
auto cal = cast(Builder2.CreateCall(
- getIntrinsicDeclaration(M, Intrinsic::nvvm_barrier0), args));
- cal->setCallingConv(getIntrinsicDeclaration(M, Intrinsic::nvvm_barrier0)
- ->getCallingConv());
+ Fn, args));
+ cal->setCallingConv(Fn->getCallingConv());
#endif
cal->setDebugLoc(gutils->getNewFromOriginal(I.getDebugLoc()));
return false;
}
+#if LLVM_VERSION_MAJOR >= 22
+ case Intrinsic::nvvm_barrier_cta_red_and_aligned_count:
+ case Intrinsic::nvvm_barrier_cta_red_or_aligned_count:
+ case Intrinsic::nvvm_barrier_cta_red_popc_aligned_count: {
+ SmallVector args = {
+ I.getOperand(0), // barrier id
+ I.getOperand(1) // thread count
+ };
+ auto *Fn =
+ getIntrinsicDeclaration(M, Intrinsic::nvvm_barrier_cta_sync_aligned_count);
+ auto cal = cast(Builder2.CreateCall(Fn, args));
+ cal->setCallingConv(Fn->getCallingConv());
+ cal->setDebugLoc(gutils->getNewFromOriginal(I.getDebugLoc()));
+ return false;
+ }
+#endif
+
#if LLVM_VERSION_MAJOR <= 20
case Intrinsic::nvvm_barrier0:
#else
- case Intrinsic::nvvm_barrier_cta_sync_aligned_all:
- case Intrinsic::nvvm_barrier_cta_sync_aligned_count:
+ case Intrinsic::nvvm_barrier_cta_sync_aligned_all: {
+ SmallVector args = {I.getOperand(0)};
+ auto *Fn = getIntrinsicDeclaration(M, ID);
+ auto cal = cast(Builder2.CreateCall(Fn, args));
+ cal->setCallingConv(Fn->getCallingConv());
+ cal->setDebugLoc(gutils->getNewFromOriginal(I.getDebugLoc()));
+ return false;
+ }
+ case Intrinsic::nvvm_barrier_cta_sync_aligned_count: {
+ SmallVector args = {I.getOperand(0), I.getOperand(1)};
+ auto *Fn = getIntrinsicDeclaration(M, ID);
+ auto cal = cast(Builder2.CreateCall(Fn, args));
+ cal->setCallingConv(Fn->getCallingConv());
+ cal->setDebugLoc(gutils->getNewFromOriginal(I.getDebugLoc()));
+ return false;
+ }
#endif
case Intrinsic::amdgcn_s_barrier:
case Intrinsic::nvvm_membar_cta:
case Intrinsic::nvvm_membar_gl:
case Intrinsic::nvvm_membar_sys: {
SmallVector args = {};
Two notes:
First, I.getOperand(0) is the right thing here. In the cloned/reverse function, that already is the current barrier id operand, so you do not need to remap through orig_ops.
Second, for LLVM 22+ the *_aligned_count reduction intrinsics should reverse to sync_aligned_count, not sync_aligned_all. Even if your current failure is on aligned_all, it is better to fix both now.
A quick workaround is to stay on Clang 19.1.7 / LLVM 20 for these kernels until you patch Enzyme, because that path still uses the old zero-arg llvm.nvvm.barrier0() form.
After this patch, the bad reverse IR should become:
call void @llvm.nvvm.barrier.cta.sync.aligned.all(i32 0)
instead of:
call void @llvm.nvvm.barrier.cta.sync.aligned.all()
and the verifier error should go away.
```
Contributor guide
Assessment
This issue has not been assessed yet.