Skip to content

[SPIRV] Add missing OpenCL atomic_fetch_min/max builtin mappings#186971

Closed
pvelesko wants to merge 1 commit intollvm:mainfrom
CHIP-SPV:spirv-atomic-fetch-minmax
Closed

[SPIRV] Add missing OpenCL atomic_fetch_min/max builtin mappings#186971
pvelesko wants to merge 1 commit intollvm:mainfrom
CHIP-SPV:spirv-atomic-fetch-minmax

Conversation

@pvelesko
Copy link
Copy Markdown
Contributor

Summary

  • Add missing atomic_fetch_min/max, atomic_fetch_min/max_explicit, and legacy atom_min/max OpenCL builtin-to-SPIR-V opcode mappings in SPIRVBuiltins.td
  • Add OpAtomicSMax/SMin/UMax/UMin cases to the atomic RMW dispatch in generateAtomicInst
  • Handle signed/unsigned opcode selection in buildAtomicRMWInst based on the OpTypeInt signedness operand

The backend already had mappings for atomic_fetch_add/sub/or/xor/and and their _explicit variants, but was missing min/max. This caused OpenCL programs using these atomics to emit unresolved function calls instead of the correct OpAtomicSMin/OpAtomicSMax/OpAtomicUMin/OpAtomicUMax instructions.

Test plan

  • Verified with chipStar test suite: 10 atomicMin/Max tests and cuda-simpleAtomicIntrinsics now pass on the in-tree SPIR-V backend (previously failed with "Missing definition for atomic_fetch_max_explicit")
  • Need to add a lit test for atomic_fetch_min/max_explicitOpAtomicSMin/OpAtomicSMax/OpAtomicUMin/OpAtomicUMax lowering

@llvmbot
Copy link
Copy Markdown
Member

llvmbot commented Mar 17, 2026

@llvm/pr-subscribers-backend-spir-v

Author: Paulius Velesko (pvelesko)

Changes

Summary

  • Add missing atomic_fetch_min/max, atomic_fetch_min/max_explicit, and legacy atom_min/max OpenCL builtin-to-SPIR-V opcode mappings in SPIRVBuiltins.td
  • Add OpAtomicSMax/SMin/UMax/UMin cases to the atomic RMW dispatch in generateAtomicInst
  • Handle signed/unsigned opcode selection in buildAtomicRMWInst based on the OpTypeInt signedness operand

The backend already had mappings for atomic_fetch_add/sub/or/xor/and and their _explicit variants, but was missing min/max. This caused OpenCL programs using these atomics to emit unresolved function calls instead of the correct OpAtomicSMin/OpAtomicSMax/OpAtomicUMin/OpAtomicUMax instructions.

Test plan

  • Verified with chipStar test suite: 10 atomicMin/Max tests and cuda-simpleAtomicIntrinsics now pass on the in-tree SPIR-V backend (previously failed with "Missing definition for atomic_fetch_max_explicit")
  • Need to add a lit test for atomic_fetch_min/max_explicitOpAtomicSMin/OpAtomicSMax/OpAtomicUMin/OpAtomicUMax lowering

Full diff: https://github.com/llvm/llvm-project/pull/186971.diff

2 Files Affected:

  • (modified) llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp (+15)
  • (modified) llvm/lib/Target/SPIRV/SPIRVBuiltins.td (+6)
diff --git a/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp b/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp
index f9a9127446013..95adf05b91c8d 100644
--- a/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp
@@ -840,6 +840,17 @@ static bool buildAtomicRMWInst(const SPIRV::IncomingCall *Call, unsigned Opcode,
                                          Semantics, MIRBuilder, GR);
   Register ValueReg = Call->Arguments[1];
   Register ValueTypeReg = GR->getSPIRVTypeID(Call->ReturnType);
+  // For min/max atomics, select signed or unsigned opcode based on the
+  // integer return type's signedness.
+  if (Call->ReturnType->getOpcode() == SPIRV::OpTypeInt) {
+    bool IsUnsigned = Call->ReturnType->getOperand(2).getImm() == 0;
+    if (IsUnsigned) {
+      if (Opcode == SPIRV::OpAtomicSMax)
+        Opcode = SPIRV::OpAtomicUMax;
+      else if (Opcode == SPIRV::OpAtomicSMin)
+        Opcode = SPIRV::OpAtomicUMin;
+    }
+  }
   // support cl_ext_float_atomics
   if (Call->ReturnType->getOpcode() == SPIRV::OpTypeFloat) {
     if (Opcode == SPIRV::OpAtomicIAdd) {
@@ -1775,6 +1786,10 @@ static bool generateAtomicInst(const SPIRV::IncomingCall *Call,
   case SPIRV::OpAtomicXor:
   case SPIRV::OpAtomicAnd:
   case SPIRV::OpAtomicExchange:
+  case SPIRV::OpAtomicSMax:
+  case SPIRV::OpAtomicSMin:
+  case SPIRV::OpAtomicUMax:
+  case SPIRV::OpAtomicUMin:
     return buildAtomicRMWInst(Call, Opcode, MIRBuilder, GR);
   case SPIRV::OpMemoryBarrier:
     return buildBarrierInst(Call, SPIRV::OpMemoryBarrier, MIRBuilder, GR);
diff --git a/llvm/lib/Target/SPIRV/SPIRVBuiltins.td b/llvm/lib/Target/SPIRV/SPIRVBuiltins.td
index eb1b6c3185e7d..630fb762c6eb2 100644
--- a/llvm/lib/Target/SPIRV/SPIRVBuiltins.td
+++ b/llvm/lib/Target/SPIRV/SPIRVBuiltins.td
@@ -633,6 +633,12 @@ defm : DemangledNativeBuiltin<"atomic_fetch_sub_explicit", OpenCL_std, Atomic, 3
 defm : DemangledNativeBuiltin<"atomic_fetch_or_explicit", OpenCL_std, Atomic, 3, 4, OpAtomicOr>;
 defm : DemangledNativeBuiltin<"atomic_fetch_xor_explicit", OpenCL_std, Atomic, 3, 4, OpAtomicXor>;
 defm : DemangledNativeBuiltin<"atomic_fetch_and_explicit", OpenCL_std, Atomic, 3, 4, OpAtomicAnd>;
+defm : DemangledNativeBuiltin<"atomic_fetch_min", OpenCL_std, Atomic, 2, 4, OpAtomicSMin>;
+defm : DemangledNativeBuiltin<"atomic_fetch_max", OpenCL_std, Atomic, 2, 4, OpAtomicSMax>;
+defm : DemangledNativeBuiltin<"atomic_fetch_min_explicit", OpenCL_std, Atomic, 3, 4, OpAtomicSMin>;
+defm : DemangledNativeBuiltin<"atomic_fetch_max_explicit", OpenCL_std, Atomic, 3, 4, OpAtomicSMax>;
+defm : DemangledNativeBuiltin<"atom_min", OpenCL_std, Atomic, 2, 2, OpAtomicSMin>;
+defm : DemangledNativeBuiltin<"atom_max", OpenCL_std, Atomic, 2, 2, OpAtomicSMax>;
 defm : DemangledNativeBuiltin<"atomic_flag_test_and_set", OpenCL_std, Atomic, 1, 1, OpAtomicFlagTestAndSet>;
 defm : DemangledNativeBuiltin<"__spirv_AtomicFlagTestAndSet", OpenCL_std, Atomic, 3, 3, OpAtomicFlagTestAndSet>;
 defm : DemangledNativeBuiltin<"atomic_flag_test_and_set_explicit", OpenCL_std, Atomic, 2, 3, OpAtomicFlagTestAndSet>;

@pvelesko
Copy link
Copy Markdown
Contributor Author

The Test SPIR-V CI failure is pre-existing on main — confirmed by #186992 (no-op baseline PR on main that shows the same 5 pointer/SpecConstantOp test failures).

The SPIR-V backend had mappings for atomic_fetch_add/sub/or/xor/and
and their _explicit variants, but was missing atomic_fetch_min/max,
atomic_fetch_min/max_explicit, and the legacy atom_min/max builtins.

This caused OpenCL programs using these atomics to emit unresolved
function calls instead of the correct OpAtomicSMin/OpAtomicSMax/
OpAtomicUMin/OpAtomicUMax instructions.

Add the missing builtin-to-opcode mappings in SPIRVBuiltins.td and
handle the signed/unsigned opcode selection in buildAtomicRMWInst
based on the integer return type's signedness (OpTypeInt operand 2).
@pvelesko pvelesko force-pushed the spirv-atomic-fetch-minmax branch from 9c9500e to 2192ffe Compare April 1, 2026 05:47
@pvelesko pvelesko closed this Apr 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants