[llvm] [SPIR-V] Add support for OpFMod intrinsic (PR #193172)

Arseniy Obolenskiy via llvm-commits llvm-commits at lists.llvm.org
Thu May 7 09:00:11 PDT 2026


https://github.com/aobolensk updated https://github.com/llvm/llvm-project/pull/193172

>From 7ec8b3bd3eb374c2528a134f55465006b66f2857 Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Tue, 21 Apr 2026 11:54:26 +0200
Subject: [PATCH 1/2] [SPIR-V] Add support for OpFMod intrinsic

Add the spv.fmod intrinsic and lower it directly to SPIRV::OpFMod covering scalar and vector cases
---
 llvm/include/llvm/IR/IntrinsicsSPIRV.td         |  1 +
 .../Target/SPIRV/SPIRVInstructionSelector.cpp   |  5 +++++
 .../scalar-floating-point-arithmetic.ll         | 16 +++++++++++++++-
 .../vector-floating-point-arithmetic.ll         | 17 ++++++++++++++++-
 4 files changed, 37 insertions(+), 2 deletions(-)

diff --git a/llvm/include/llvm/IR/IntrinsicsSPIRV.td b/llvm/include/llvm/IR/IntrinsicsSPIRV.td
index d2a5fa1f08724..67f890d1d6dac 100644
--- a/llvm/include/llvm/IR/IntrinsicsSPIRV.td
+++ b/llvm/include/llvm/IR/IntrinsicsSPIRV.td
@@ -98,6 +98,7 @@ let TargetPrefix = "spv" in {
   def int_spv_degrees : DefaultAttrsIntrinsic<[LLVMMatchType<0>], [llvm_anyfloat_ty], [IntrNoMem]>;
   def int_spv_distance : DefaultAttrsIntrinsic<[LLVMVectorElementType<0>], [llvm_anyfloat_ty, LLVMMatchType<0>], [IntrNoMem]>;
   def int_spv_faceforward : DefaultAttrsIntrinsic<[LLVMMatchType<0>], [llvm_anyfloat_ty, LLVMMatchType<0>, LLVMMatchType<0>], [IntrNoMem]>;
+  def int_spv_fmod : DefaultAttrsIntrinsic<[LLVMMatchType<0>], [llvm_anyfloat_ty, LLVMMatchType<0>], [IntrNoMem]>;
   def int_spv_frac : DefaultAttrsIntrinsic<[LLVMMatchType<0>], [llvm_anyfloat_ty], [IntrNoMem]>;
   def int_spv_isinf : DefaultAttrsIntrinsic<[LLVMScalarOrSameVectorWidth<0, llvm_i1_ty>],
     [llvm_anyfloat_ty], [IntrNoMem]>;
diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index f1e0450bb20f9..8a4efd631e851 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -677,6 +677,7 @@ static bool intrinsicHasSideEffects(Intrinsic::ID ID) {
   case Intrinsic::spv_firstbitlow:
   case Intrinsic::spv_firstbitshigh:
   case Intrinsic::spv_firstbituhigh:
+  case Intrinsic::spv_fmod:
   case Intrinsic::spv_frac:
   case Intrinsic::spv_gep:
   case Intrinsic::spv_global_offset:
@@ -4763,6 +4764,10 @@ bool SPIRVInstructionSelector::selectIntrinsic(Register ResVReg,
     return selectExtInst(ResVReg, ResType, I, CL::degrees, GL::Degrees);
   case Intrinsic::spv_faceforward:
     return selectExtInst(ResVReg, ResType, I, GL::FaceForward);
+  case Intrinsic::spv_fmod:
+    return selectOpWithSrcs(
+        ResVReg, ResType, I,
+        {I.getOperand(2).getReg(), I.getOperand(3).getReg()}, SPIRV::OpFMod);
   case Intrinsic::spv_frac:
     return selectExtInst(ResVReg, ResType, I, CL::fract, GL::Fract);
   case Intrinsic::spv_isinf:
diff --git a/llvm/test/CodeGen/SPIRV/instructions/scalar-floating-point-arithmetic.ll b/llvm/test/CodeGen/SPIRV/instructions/scalar-floating-point-arithmetic.ll
index b04db946f36d0..0856a33ac3bb8 100644
--- a/llvm/test/CodeGen/SPIRV/instructions/scalar-floating-point-arithmetic.ll
+++ b/llvm/test/CodeGen/SPIRV/instructions/scalar-floating-point-arithmetic.ll
@@ -8,7 +8,7 @@
 ; CHECK-DAG: OpName [[SCALAR_FDIV:%.+]] "scalar_fdiv"
 ; CHECK-DAG: OpName [[SCALAR_FREM:%.+]] "scalar_frem"
 ; CHECK-DAG: OpName [[SCALAR_FMA:%.+]] "scalar_fma"
-;; FIXME: add test for OpFMod
+; CHECK-DAG: OpName [[SCALAR_FMOD:%.+]] "scalar_fmod"
 
 ; CHECK-NOT: DAG-FENCE
 
@@ -109,6 +109,20 @@ define float @scalar_frem(float %a, float %b) {
 ; CHECK:      OpReturnValue [[C]]
 ; CHECK-NEXT: OpFunctionEnd
 
+;; Test fmod on scalar:
+define float @scalar_fmod(float %a, float %b) {
+    %c = call float @llvm.spv.fmod.f32(float %a, float %b)
+    ret float %c
+}
+
+; CHECK:      [[SCALAR_FMOD]] = OpFunction [[SCALAR]] None [[SCALAR_FN]]
+; CHECK-NEXT: [[A:%.+]] = OpFunctionParameter [[SCALAR]]
+; CHECK-NEXT: [[B:%.+]] = OpFunctionParameter [[SCALAR]]
+; CHECK:      OpLabel
+; CHECK:      [[C:%.+]] = OpFMod [[SCALAR]] [[A]] [[B]]
+; CHECK:      OpReturnValue [[C]]
+; CHECK-NEXT: OpFunctionEnd
+
 declare float @llvm.fma.f32(float, float, float)
 
 ;; Test fma on scalar:
diff --git a/llvm/test/CodeGen/SPIRV/instructions/vector-floating-point-arithmetic.ll b/llvm/test/CodeGen/SPIRV/instructions/vector-floating-point-arithmetic.ll
index 0b0e505829ed1..67f427c52aeae 100644
--- a/llvm/test/CodeGen/SPIRV/instructions/vector-floating-point-arithmetic.ll
+++ b/llvm/test/CodeGen/SPIRV/instructions/vector-floating-point-arithmetic.ll
@@ -7,7 +7,7 @@
 ; CHECK-DAG: OpName [[VECTOR_FMUL:%.+]] "vector_fmul"
 ; CHECK-DAG: OpName [[VECTOR_FDIV:%.+]] "vector_fdiv"
 ; CHECK-DAG: OpName [[VECTOR_FREM:%.+]] "vector_frem"
-;; TODO: add test for OpFMod
+; CHECK-DAG: OpName [[VECTOR_FMOD:%.+]] "vector_fmod"
 
 ; CHECK-NOT: DAG-FENCE
 
@@ -106,3 +106,18 @@ define <2 x half> @vector_frem(<2 x half> %a, <2 x half> %b) {
 ; CHECK:      [[C:%.+]] = OpFRem [[VECTOR]] [[A]] [[B]]
 ; CHECK:      OpReturnValue [[C]]
 ; CHECK-NEXT: OpFunctionEnd
+
+
+;; Test fmod on vector:
+define <2 x half> @vector_fmod(<2 x half> %a, <2 x half> %b) {
+    %c = call <2 x half> @llvm.spv.fmod.v2f16(<2 x half> %a, <2 x half> %b)
+    ret <2 x half> %c
+}
+
+; CHECK:      [[VECTOR_FMOD]] = OpFunction [[VECTOR]] None [[VECTOR_FN]]
+; CHECK-NEXT: [[A:%.+]] = OpFunctionParameter [[VECTOR]]
+; CHECK-NEXT: [[B:%.+]] = OpFunctionParameter [[VECTOR]]
+; CHECK:      OpLabel
+; CHECK:      [[C:%.+]] = OpFMod [[VECTOR]] [[A]] [[B]]
+; CHECK:      OpReturnValue [[C]]
+; CHECK-NEXT: OpFunctionEnd

>From 0666b30b503e84ad84ba59ac235d6c3b37be951c Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Thu, 7 May 2026 17:59:58 +0200
Subject: [PATCH 2/2] Address review comment

---
 llvm/include/llvm/IR/IntrinsicsSPIRV.td        |  1 -
 llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp        | 18 ++++++++++++++++++
 llvm/lib/Target/SPIRV/SPIRVBuiltins.td         |  4 ++++
 .../Target/SPIRV/SPIRVInstructionSelector.cpp  |  5 -----
 .../scalar-floating-point-arithmetic.ll        |  6 ++++--
 .../vector-floating-point-arithmetic.ll        |  6 ++++--
 6 files changed, 30 insertions(+), 10 deletions(-)

diff --git a/llvm/include/llvm/IR/IntrinsicsSPIRV.td b/llvm/include/llvm/IR/IntrinsicsSPIRV.td
index 67f890d1d6dac..d2a5fa1f08724 100644
--- a/llvm/include/llvm/IR/IntrinsicsSPIRV.td
+++ b/llvm/include/llvm/IR/IntrinsicsSPIRV.td
@@ -98,7 +98,6 @@ let TargetPrefix = "spv" in {
   def int_spv_degrees : DefaultAttrsIntrinsic<[LLVMMatchType<0>], [llvm_anyfloat_ty], [IntrNoMem]>;
   def int_spv_distance : DefaultAttrsIntrinsic<[LLVMVectorElementType<0>], [llvm_anyfloat_ty, LLVMMatchType<0>], [IntrNoMem]>;
   def int_spv_faceforward : DefaultAttrsIntrinsic<[LLVMMatchType<0>], [llvm_anyfloat_ty, LLVMMatchType<0>, LLVMMatchType<0>], [IntrNoMem]>;
-  def int_spv_fmod : DefaultAttrsIntrinsic<[LLVMMatchType<0>], [llvm_anyfloat_ty, LLVMMatchType<0>], [IntrNoMem]>;
   def int_spv_frac : DefaultAttrsIntrinsic<[LLVMMatchType<0>], [llvm_anyfloat_ty], [IntrNoMem]>;
   def int_spv_isinf : DefaultAttrsIntrinsic<[LLVMScalarOrSameVectorWidth<0, llvm_i1_ty>],
     [llvm_anyfloat_ty], [IntrNoMem]>;
diff --git a/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp b/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp
index 1e6d3c52990f7..b0e345dcc2c59 100644
--- a/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp
@@ -2135,6 +2135,21 @@ static bool generateMulExtendedInst(const SPIRV::IncomingCall *Call,
   return true;
 }
 
+static bool generateArithmeticInst(const SPIRV::IncomingCall *Call,
+                                   MachineIRBuilder &MIRBuilder,
+                                   SPIRVGlobalRegistry *GR) {
+  const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
+  unsigned Opcode =
+      SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode;
+
+  auto MIB = MIRBuilder.buildInstr(Opcode)
+                 .addDef(Call->ReturnRegister)
+                 .addUse(GR->getSPIRVTypeID(Call->ReturnType));
+  for (Register Arg : Call->Arguments)
+    MIB.addUse(Arg);
+  return true;
+}
+
 static bool generateGetQueryInst(const SPIRV::IncomingCall *Call,
                                  MachineIRBuilder &MIRBuilder,
                                  SPIRVGlobalRegistry *GR) {
@@ -3342,6 +3357,7 @@ mapBuiltinToOpcode(const StringRef DemangledCall,
   case SPIRV::AsyncCopy:
   case SPIRV::LoadStore:
   case SPIRV::CoopMatr:
+  case SPIRV::Arithmetic:
     if (const auto *R =
             SPIRV::lookupNativeBuiltin(Call->Builtin->Name, Call->Builtin->Set))
       return std::make_tuple(Call->Builtin->Group, R->Opcode, 0);
@@ -3455,6 +3471,8 @@ std::optional<bool> lowerBuiltin(const StringRef DemangledCall,
     return generateICarryBorrowInst(Call.get(), MIRBuilder, GR);
   case SPIRV::MulExtended:
     return generateMulExtendedInst(Call.get(), MIRBuilder, GR);
+  case SPIRV::Arithmetic:
+    return generateArithmeticInst(Call.get(), MIRBuilder, GR);
   case SPIRV::GetQuery:
     return generateGetQueryInst(Call.get(), MIRBuilder, GR);
   case SPIRV::ImageSizeQuery:
diff --git a/llvm/lib/Target/SPIRV/SPIRVBuiltins.td b/llvm/lib/Target/SPIRV/SPIRVBuiltins.td
index 2bebf95940033..f83f98038a55a 100644
--- a/llvm/lib/Target/SPIRV/SPIRVBuiltins.td
+++ b/llvm/lib/Target/SPIRV/SPIRVBuiltins.td
@@ -72,6 +72,7 @@ def TernaryBitwiseINTEL : BuiltinGroup;
 def Block2DLoadStore : BuiltinGroup;
 def Pipe : BuiltinGroup;
 def PredicatedLoadStore : BuiltinGroup;
+def Arithmetic : BuiltinGroup;
 def ArbitraryPrecisionFixedPoint : BuiltinGroup;
 def BlockingPipes : BuiltinGroup;
 def ImageChannelDataTypes : BuiltinGroup;
@@ -681,6 +682,9 @@ defm : DemangledNativeBuiltin<"__spirv_ISubBorrow", OpenCL_std, ICarryBorrow, 3,
 defm : DemangledNativeBuiltin<"__spirv_UMulExtended", OpenCL_std, MulExtended, 2, 3, OpUMulExtended>;
 defm : DemangledNativeBuiltin<"__spirv_SMulExtended", OpenCL_std, MulExtended, 2, 3, OpSMulExtended>;
 
+// Arithmetic builtin records:
+defm : DemangledNativeBuiltin<"__spirv_FMod", OpenCL_std, Arithmetic, 2, 2, OpFMod>;
+
 // cl_intel_split_work_group_barrier
 defm : DemangledNativeBuiltin<"intel_work_group_barrier_arrive", OpenCL_std, Barrier, 1, 2, OpControlBarrierArriveINTEL>;
 defm : DemangledNativeBuiltin<"__spirv_ControlBarrierArriveINTEL", OpenCL_std, Barrier, 3, 3, OpControlBarrierArriveINTEL>;
diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index 8a4efd631e851..f1e0450bb20f9 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -677,7 +677,6 @@ static bool intrinsicHasSideEffects(Intrinsic::ID ID) {
   case Intrinsic::spv_firstbitlow:
   case Intrinsic::spv_firstbitshigh:
   case Intrinsic::spv_firstbituhigh:
-  case Intrinsic::spv_fmod:
   case Intrinsic::spv_frac:
   case Intrinsic::spv_gep:
   case Intrinsic::spv_global_offset:
@@ -4764,10 +4763,6 @@ bool SPIRVInstructionSelector::selectIntrinsic(Register ResVReg,
     return selectExtInst(ResVReg, ResType, I, CL::degrees, GL::Degrees);
   case Intrinsic::spv_faceforward:
     return selectExtInst(ResVReg, ResType, I, GL::FaceForward);
-  case Intrinsic::spv_fmod:
-    return selectOpWithSrcs(
-        ResVReg, ResType, I,
-        {I.getOperand(2).getReg(), I.getOperand(3).getReg()}, SPIRV::OpFMod);
   case Intrinsic::spv_frac:
     return selectExtInst(ResVReg, ResType, I, CL::fract, GL::Fract);
   case Intrinsic::spv_isinf:
diff --git a/llvm/test/CodeGen/SPIRV/instructions/scalar-floating-point-arithmetic.ll b/llvm/test/CodeGen/SPIRV/instructions/scalar-floating-point-arithmetic.ll
index 0856a33ac3bb8..bdad95a1615bc 100644
--- a/llvm/test/CodeGen/SPIRV/instructions/scalar-floating-point-arithmetic.ll
+++ b/llvm/test/CodeGen/SPIRV/instructions/scalar-floating-point-arithmetic.ll
@@ -110,11 +110,13 @@ define float @scalar_frem(float %a, float %b) {
 ; CHECK-NEXT: OpFunctionEnd
 
 ;; Test fmod on scalar:
-define float @scalar_fmod(float %a, float %b) {
-    %c = call float @llvm.spv.fmod.f32(float %a, float %b)
+define spir_func float @scalar_fmod(float %a, float %b) {
+    %c = call spir_func float @_Z12__spirv_FModff(float %a, float %b)
     ret float %c
 }
 
+declare spir_func float @_Z12__spirv_FModff(float, float)
+
 ; CHECK:      [[SCALAR_FMOD]] = OpFunction [[SCALAR]] None [[SCALAR_FN]]
 ; CHECK-NEXT: [[A:%.+]] = OpFunctionParameter [[SCALAR]]
 ; CHECK-NEXT: [[B:%.+]] = OpFunctionParameter [[SCALAR]]
diff --git a/llvm/test/CodeGen/SPIRV/instructions/vector-floating-point-arithmetic.ll b/llvm/test/CodeGen/SPIRV/instructions/vector-floating-point-arithmetic.ll
index 67f427c52aeae..e79b3a6595a0f 100644
--- a/llvm/test/CodeGen/SPIRV/instructions/vector-floating-point-arithmetic.ll
+++ b/llvm/test/CodeGen/SPIRV/instructions/vector-floating-point-arithmetic.ll
@@ -109,11 +109,13 @@ define <2 x half> @vector_frem(<2 x half> %a, <2 x half> %b) {
 
 
 ;; Test fmod on vector:
-define <2 x half> @vector_fmod(<2 x half> %a, <2 x half> %b) {
-    %c = call <2 x half> @llvm.spv.fmod.v2f16(<2 x half> %a, <2 x half> %b)
+define spir_func <2 x half> @vector_fmod(<2 x half> %a, <2 x half> %b) {
+    %c = call spir_func <2 x half> @_Z12__spirv_FModDv2_DhS_(<2 x half> %a, <2 x half> %b)
     ret <2 x half> %c
 }
 
+declare spir_func <2 x half> @_Z12__spirv_FModDv2_DhS_(<2 x half>, <2 x half>)
+
 ; CHECK:      [[VECTOR_FMOD]] = OpFunction [[VECTOR]] None [[VECTOR_FN]]
 ; CHECK-NEXT: [[A:%.+]] = OpFunctionParameter [[VECTOR]]
 ; CHECK-NEXT: [[B:%.+]] = OpFunctionParameter [[VECTOR]]



More information about the llvm-commits mailing list