[llvm] [SPIRV] Added support for the constrained comparison intrinsics (PR #157439)

Subash B via llvm-commits llvm-commits at lists.llvm.org
Mon Sep 8 05:31:50 PDT 2025


https://github.com/SubashBoopathi created https://github.com/llvm/llvm-project/pull/157439

Added SPIR-V  support for constrained floating-point comparison intrinsics (fcmp, fcmps) with lowering and tests.

>From ff674a32012d2a18fa13dfd7a385375a11a531e3 Mon Sep 17 00:00:00 2001
From: Subash B <subash.boopathi at multicorewareinc.com>
Date: Wed, 6 Aug 2025 12:04:54 +0530
Subject: [PATCH 1/3] Added support for experimental_constrained_fcmp in llvm
 project

---
 llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp | 19 ++++++-
 .../Target/SPIRV/SPIRVPrepareFunctions.cpp    | 29 ++++++++++
 .../llvm-intrinsics/constrained-comparison.ll | 57 +++++++++++++++++++
 3 files changed, 103 insertions(+), 2 deletions(-)
 create mode 100644 llvm/test/CodeGen/SPIRV/llvm-intrinsics/constrained-comparison.ll

diff --git a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
index b90e1aadbb5a1..e34bde565899f 100644
--- a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
@@ -2432,9 +2432,24 @@ bool SPIRVEmitIntrinsics::runOnFunction(Function &Func) {
     // already, and force it to be i8 if not
     if (Postpone && !GR->findAssignPtrTypeInstr(I))
       insertAssignPtrTypeIntrs(I, B, true);
-
-    if (auto *FPI = dyn_cast<ConstrainedFPIntrinsic>(I))
+    if (auto *FPI = dyn_cast<ConstrainedFPIntrinsic>(I)) {
       useRoundingMode(FPI, B);
+      if (auto *FPCmpI = dyn_cast<ConstrainedFPCmpIntrinsic>(I)) {
+        unsigned int ID = FPCmpI->getIntrinsicID();
+        switch (ID) {
+        case Intrinsic::experimental_constrained_fcmp:
+        case Intrinsic::experimental_constrained_fcmps: {
+          Value *LHS = FPCmpI->getArgOperand(0);
+          Value *RHS = FPCmpI->getArgOperand(1);
+          FCmpInst::Predicate Pred = FPCmpI->getPredicate();
+          Value *NewInst = B.CreateFCmp(Pred, LHS, RHS);
+          I->replaceAllUsesWith(NewInst);
+          I->eraseFromParent();
+          break;
+        }
+        }
+      }
+    }
   }
 
   // Pass backward: use instructions results to specify/update/cast operands
diff --git a/llvm/lib/Target/SPIRV/SPIRVPrepareFunctions.cpp b/llvm/lib/Target/SPIRV/SPIRVPrepareFunctions.cpp
index 2bffbf73b574a..23788121048c8 100644
--- a/llvm/lib/Target/SPIRV/SPIRVPrepareFunctions.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVPrepareFunctions.cpp
@@ -335,6 +335,25 @@ static void lowerFunnelShifts(IntrinsicInst *FSHIntrinsic) {
   FSHIntrinsic->setCalledFunction(FSHFunc);
 }
 
+static void lowerConstrainedFPCmpIntrinsic(
+    ConstrainedFPCmpIntrinsic *ConstrainedCmpIntrinsic,
+    SmallVector<Instruction *> &EraseFromParent) {
+  if (!ConstrainedCmpIntrinsic)
+    return;
+
+  // Extract the floating-point values being compared
+  Value *LHS = ConstrainedCmpIntrinsic->getArgOperand(0);
+  Value *RHS = ConstrainedCmpIntrinsic->getArgOperand(1);
+  FCmpInst::Predicate Pred = ConstrainedCmpIntrinsic->getPredicate();
+
+  // Insert the replacement fcmp instruction
+  IRBuilder<> Builder(ConstrainedCmpIntrinsic);
+  Value *FCmp = Builder.CreateFCmp(Pred, LHS, RHS);
+
+  ConstrainedCmpIntrinsic->replaceAllUsesWith(FCmp);
+  EraseFromParent.push_back(dyn_cast<Instruction>(ConstrainedCmpIntrinsic));
+}
+
 static void lowerExpectAssume(IntrinsicInst *II) {
   // If we cannot use the SPV_KHR_expect_assume extension, then we need to
   // ignore the intrinsic and move on. It should be removed later on by LLVM.
@@ -379,6 +398,7 @@ static bool toSpvOverloadedIntrinsic(IntrinsicInst *II, Intrinsic::ID NewID,
 bool SPIRVPrepareFunctions::substituteIntrinsicCalls(Function *F) {
   bool Changed = false;
   const SPIRVSubtarget &STI = TM.getSubtarget<SPIRVSubtarget>(*F);
+  SmallVector<Instruction *> EraseFromParent;
   for (BasicBlock &BB : *F) {
     for (Instruction &I : BB) {
       auto Call = dyn_cast<CallInst>(&I);
@@ -420,9 +440,18 @@ bool SPIRVPrepareFunctions::substituteIntrinsicCalls(Function *F) {
         lowerPtrAnnotation(II);
         Changed = true;
         break;
+      case Intrinsic::experimental_constrained_fcmp:
+      case Intrinsic::experimental_constrained_fcmps:
+        lowerConstrainedFPCmpIntrinsic(dyn_cast<ConstrainedFPCmpIntrinsic>(II),
+                                       EraseFromParent);
+        Changed = true;
+        break;
       }
     }
   }
+  for (auto *I : EraseFromParent) {
+    I->eraseFromParent();
+  }
   return Changed;
 }
 
diff --git a/llvm/test/CodeGen/SPIRV/llvm-intrinsics/constrained-comparison.ll b/llvm/test/CodeGen/SPIRV/llvm-intrinsics/constrained-comparison.ll
new file mode 100644
index 0000000000000..b5e3c82af1b10
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/llvm-intrinsics/constrained-comparison.ll
@@ -0,0 +1,57 @@
+; RUN: llc -O0 -mtriple=spirv32-unknown-unknown %s -o - | FileCheck %s --check-prefix=CHECK
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv32-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+
+; CHECK-DAG: OpFOrdEqual
+; CHECK-DAG: OpFOrdGreaterThan
+; CHECK-DAG: OpFOrdGreaterThanEqual
+; CHECK-DAG: OpFOrdLessThan
+; CHECK-DAG: OpFOrdLessThanEqual
+; CHECK-DAG: OpFOrdNotEqual
+; CHECK-DAG: OpOrdered
+; CHECK-DAG: OpFUnordEqual
+; CHECK-DAG: OpFUnordGreaterThan
+; CHECK-DAG: OpFUnordGreaterThanEqual
+; CHECK-DAG: OpFUnordLessThan
+; CHECK-DAG: OpFUnordLessThanEqual
+; CHECK-DAG: OpFUnordNotEqual
+; CHECK-DAG: OpUnordered
+
+define dso_local spir_kernel void @test(float %a){
+entry:
+  %cmp = tail call i1 @llvm.experimental.constrained.fcmps.f32(float %a, float %a, metadata !"oeq", metadata !"fpexcept.strict") #2
+  %cmp1 = tail call i1 @llvm.experimental.constrained.fcmps.f32(float %a, float %a, metadata !"ogt", metadata !"fpexcept.strict") #2
+  %cmp2 = tail call i1 @llvm.experimental.constrained.fcmps.f32(float %a, float %a, metadata !"oge", metadata !"fpexcept.strict") #2
+  %cmp3 = tail call i1 @llvm.experimental.constrained.fcmp.f32(float %a, float %a, metadata !"olt", metadata !"fpexcept.strict") #2
+  %cmp4 = tail call i1 @llvm.experimental.constrained.fcmp.f32(float %a, float %a, metadata !"ole", metadata !"fpexcept.strict") #2
+  %cmp5 = tail call i1 @llvm.experimental.constrained.fcmp.f32(float %a, float %a, metadata !"one", metadata !"fpexcept.strict") #2
+  %cmp6 = tail call i1 @llvm.experimental.constrained.fcmp.f32(float %a, float %a, metadata !"ord", metadata !"fpexcept.strict") #2
+  %cmp7 = tail call i1 @llvm.experimental.constrained.fcmp.f32(float %a, float %a, metadata !"ueq", metadata !"fpexcept.strict") #2
+  %cmp8 = tail call i1 @llvm.experimental.constrained.fcmp.f32(float %a, float %a, metadata !"ugt", metadata !"fpexcept.strict") #2
+  %cmp9 = tail call i1 @llvm.experimental.constrained.fcmp.f32(float %a, float %a, metadata !"uge", metadata !"fpexcept.strict") #2
+  %cmp10 = tail call i1 @llvm.experimental.constrained.fcmp.f32(float %a, float %a, metadata !"ult", metadata !"fpexcept.strict") #2
+  %cmp11 = tail call i1 @llvm.experimental.constrained.fcmp.f32(float %a, float %a, metadata !"ule", metadata !"fpexcept.strict") #2
+  %cmp12 = tail call i1 @llvm.experimental.constrained.fcmp.f32(float %a, float %a, metadata !"une", metadata !"fpexcept.strict") #2
+  %cmp13 = tail call i1 @llvm.experimental.constrained.fcmp.f32(float %a, float %a, metadata !"uno", metadata !"fpexcept.strict") #2
+
+  ; Chain all comparisons using 'or'
+  %or1 = or i1 %cmp, %cmp1
+  %or2 = or i1 %or1, %cmp2
+  %or3 = or i1 %or2, %cmp3
+  %or4 = or i1 %or3, %cmp4
+  %or5 = or i1 %or4, %cmp5
+  %or6 = or i1 %or5, %cmp6
+  %or7 = or i1 %or6, %cmp7
+  %or8 = or i1 %or7, %cmp8
+  %or9 = or i1 %or8, %cmp9
+  %or10 = or i1 %or9, %cmp10
+  %or11 = or i1 %or10, %cmp11
+  %or12 = or i1 %or11, %cmp12
+  %or13 = or i1 %or12, %cmp13
+  br i1 %or13, label %true_block, label %false_block
+true_block:
+  ret void
+false_block:
+  ret void
+}
+declare i1 @llvm.experimental.constrained.fcmps.f32(float, float, metadata, metadata) #1
+declare i1 @llvm.experimental.constrained.fcmp.f32(float, float, metadata, metadata) #1

>From 60398c79bda2b1d0f582dcd157d5c45e3975f55e Mon Sep 17 00:00:00 2001
From: Subash B <subash.boopathi at multicorewareinc.com>
Date: Wed, 6 Aug 2025 15:17:33 +0530
Subject: [PATCH 2/3] Added support for experimental_constrained_fcmp in llvm
 project

---
 llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp | 18 +-----------------
 1 file changed, 1 insertion(+), 17 deletions(-)

diff --git a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
index e34bde565899f..b8b4ca4d475c3 100644
--- a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
@@ -2432,24 +2432,8 @@ bool SPIRVEmitIntrinsics::runOnFunction(Function &Func) {
     // already, and force it to be i8 if not
     if (Postpone && !GR->findAssignPtrTypeInstr(I))
       insertAssignPtrTypeIntrs(I, B, true);
-    if (auto *FPI = dyn_cast<ConstrainedFPIntrinsic>(I)) {
+    if (auto *FPI = dyn_cast<ConstrainedFPIntrinsic>(I))
       useRoundingMode(FPI, B);
-      if (auto *FPCmpI = dyn_cast<ConstrainedFPCmpIntrinsic>(I)) {
-        unsigned int ID = FPCmpI->getIntrinsicID();
-        switch (ID) {
-        case Intrinsic::experimental_constrained_fcmp:
-        case Intrinsic::experimental_constrained_fcmps: {
-          Value *LHS = FPCmpI->getArgOperand(0);
-          Value *RHS = FPCmpI->getArgOperand(1);
-          FCmpInst::Predicate Pred = FPCmpI->getPredicate();
-          Value *NewInst = B.CreateFCmp(Pred, LHS, RHS);
-          I->replaceAllUsesWith(NewInst);
-          I->eraseFromParent();
-          break;
-        }
-        }
-      }
-    }
   }
 
   // Pass backward: use instructions results to specify/update/cast operands

>From 35f4cd770b085b21fd8c35ed17de4cb99948f7b7 Mon Sep 17 00:00:00 2001
From: Subash B <subash.boopathi at multicorewareinc.com>
Date: Wed, 6 Aug 2025 15:19:39 +0530
Subject: [PATCH 3/3] Added support for experimental_constrained_fcmp in llvm
 project

---
 llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp |  1 +
 .../Target/SPIRV/SPIRVPrepareFunctions.cpp    |  7 +---
 .../llvm-intrinsics/constrained-comparison.ll | 37 +++++++++----------
 3 files changed, 20 insertions(+), 25 deletions(-)

diff --git a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
index b8b4ca4d475c3..b90e1aadbb5a1 100644
--- a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
@@ -2432,6 +2432,7 @@ bool SPIRVEmitIntrinsics::runOnFunction(Function &Func) {
     // already, and force it to be i8 if not
     if (Postpone && !GR->findAssignPtrTypeInstr(I))
       insertAssignPtrTypeIntrs(I, B, true);
+
     if (auto *FPI = dyn_cast<ConstrainedFPIntrinsic>(I))
       useRoundingMode(FPI, B);
   }
diff --git a/llvm/lib/Target/SPIRV/SPIRVPrepareFunctions.cpp b/llvm/lib/Target/SPIRV/SPIRVPrepareFunctions.cpp
index 23788121048c8..c10bfc66fdcb8 100644
--- a/llvm/lib/Target/SPIRV/SPIRVPrepareFunctions.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVPrepareFunctions.cpp
@@ -340,16 +340,12 @@ static void lowerConstrainedFPCmpIntrinsic(
     SmallVector<Instruction *> &EraseFromParent) {
   if (!ConstrainedCmpIntrinsic)
     return;
-
   // Extract the floating-point values being compared
   Value *LHS = ConstrainedCmpIntrinsic->getArgOperand(0);
   Value *RHS = ConstrainedCmpIntrinsic->getArgOperand(1);
   FCmpInst::Predicate Pred = ConstrainedCmpIntrinsic->getPredicate();
-
-  // Insert the replacement fcmp instruction
   IRBuilder<> Builder(ConstrainedCmpIntrinsic);
   Value *FCmp = Builder.CreateFCmp(Pred, LHS, RHS);
-
   ConstrainedCmpIntrinsic->replaceAllUsesWith(FCmp);
   EraseFromParent.push_back(dyn_cast<Instruction>(ConstrainedCmpIntrinsic));
 }
@@ -449,9 +445,8 @@ bool SPIRVPrepareFunctions::substituteIntrinsicCalls(Function *F) {
       }
     }
   }
-  for (auto *I : EraseFromParent) {
+  for (auto *I : EraseFromParent)
     I->eraseFromParent();
-  }
   return Changed;
 }
 
diff --git a/llvm/test/CodeGen/SPIRV/llvm-intrinsics/constrained-comparison.ll b/llvm/test/CodeGen/SPIRV/llvm-intrinsics/constrained-comparison.ll
index b5e3c82af1b10..49bb8eac10be8 100644
--- a/llvm/test/CodeGen/SPIRV/llvm-intrinsics/constrained-comparison.ll
+++ b/llvm/test/CodeGen/SPIRV/llvm-intrinsics/constrained-comparison.ll
@@ -1,5 +1,5 @@
-; RUN: llc -O0 -mtriple=spirv32-unknown-unknown %s -o - | FileCheck %s --check-prefix=CHECK
-; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv32-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
 
 ; CHECK-DAG: OpFOrdEqual
 ; CHECK-DAG: OpFOrdGreaterThan
@@ -18,22 +18,21 @@
 
 define dso_local spir_kernel void @test(float %a){
 entry:
-  %cmp = tail call i1 @llvm.experimental.constrained.fcmps.f32(float %a, float %a, metadata !"oeq", metadata !"fpexcept.strict") #2
-  %cmp1 = tail call i1 @llvm.experimental.constrained.fcmps.f32(float %a, float %a, metadata !"ogt", metadata !"fpexcept.strict") #2
-  %cmp2 = tail call i1 @llvm.experimental.constrained.fcmps.f32(float %a, float %a, metadata !"oge", metadata !"fpexcept.strict") #2
-  %cmp3 = tail call i1 @llvm.experimental.constrained.fcmp.f32(float %a, float %a, metadata !"olt", metadata !"fpexcept.strict") #2
-  %cmp4 = tail call i1 @llvm.experimental.constrained.fcmp.f32(float %a, float %a, metadata !"ole", metadata !"fpexcept.strict") #2
-  %cmp5 = tail call i1 @llvm.experimental.constrained.fcmp.f32(float %a, float %a, metadata !"one", metadata !"fpexcept.strict") #2
-  %cmp6 = tail call i1 @llvm.experimental.constrained.fcmp.f32(float %a, float %a, metadata !"ord", metadata !"fpexcept.strict") #2
-  %cmp7 = tail call i1 @llvm.experimental.constrained.fcmp.f32(float %a, float %a, metadata !"ueq", metadata !"fpexcept.strict") #2
-  %cmp8 = tail call i1 @llvm.experimental.constrained.fcmp.f32(float %a, float %a, metadata !"ugt", metadata !"fpexcept.strict") #2
-  %cmp9 = tail call i1 @llvm.experimental.constrained.fcmp.f32(float %a, float %a, metadata !"uge", metadata !"fpexcept.strict") #2
-  %cmp10 = tail call i1 @llvm.experimental.constrained.fcmp.f32(float %a, float %a, metadata !"ult", metadata !"fpexcept.strict") #2
-  %cmp11 = tail call i1 @llvm.experimental.constrained.fcmp.f32(float %a, float %a, metadata !"ule", metadata !"fpexcept.strict") #2
-  %cmp12 = tail call i1 @llvm.experimental.constrained.fcmp.f32(float %a, float %a, metadata !"une", metadata !"fpexcept.strict") #2
-  %cmp13 = tail call i1 @llvm.experimental.constrained.fcmp.f32(float %a, float %a, metadata !"uno", metadata !"fpexcept.strict") #2
+  %cmp = tail call i1 @llvm.experimental.constrained.fcmps.f32(float %a, float %a, metadata !"oeq", metadata !"fpexcept.strict") 
+  %cmp1 = tail call i1 @llvm.experimental.constrained.fcmps.f32(float %a, float %a, metadata !"ogt", metadata !"fpexcept.strict") 
+  %cmp2 = tail call i1 @llvm.experimental.constrained.fcmps.f32(float %a, float %a, metadata !"oge", metadata !"fpexcept.strict") 
+  %cmp3 = tail call i1 @llvm.experimental.constrained.fcmp.f32(float %a, float %a, metadata !"olt", metadata !"fpexcept.strict") 
+  %cmp4 = tail call i1 @llvm.experimental.constrained.fcmp.f32(float %a, float %a, metadata !"ole", metadata !"fpexcept.strict") 
+  %cmp5 = tail call i1 @llvm.experimental.constrained.fcmp.f32(float %a, float %a, metadata !"one", metadata !"fpexcept.strict") 
+  %cmp6 = tail call i1 @llvm.experimental.constrained.fcmp.f32(float %a, float %a, metadata !"ord", metadata !"fpexcept.strict") 
+  %cmp7 = tail call i1 @llvm.experimental.constrained.fcmp.f32(float %a, float %a, metadata !"ueq", metadata !"fpexcept.strict") 
+  %cmp8 = tail call i1 @llvm.experimental.constrained.fcmp.f32(float %a, float %a, metadata !"ugt", metadata !"fpexcept.strict") 
+  %cmp9 = tail call i1 @llvm.experimental.constrained.fcmp.f32(float %a, float %a, metadata !"uge", metadata !"fpexcept.strict") 
+  %cmp10 = tail call i1 @llvm.experimental.constrained.fcmp.f32(float %a, float %a, metadata !"ult", metadata !"fpexcept.strict") 
+  %cmp11 = tail call i1 @llvm.experimental.constrained.fcmp.f32(float %a, float %a, metadata !"ule", metadata !"fpexcept.strict") 
+  %cmp12 = tail call i1 @llvm.experimental.constrained.fcmp.f32(float %a, float %a, metadata !"une", metadata !"fpexcept.strict") 
+  %cmp13 = tail call i1 @llvm.experimental.constrained.fcmp.f32(float %a, float %a, metadata !"uno", metadata !"fpexcept.strict") 
 
-  ; Chain all comparisons using 'or'
   %or1 = or i1 %cmp, %cmp1
   %or2 = or i1 %or1, %cmp2
   %or3 = or i1 %or2, %cmp3
@@ -53,5 +52,5 @@ true_block:
 false_block:
   ret void
 }
-declare i1 @llvm.experimental.constrained.fcmps.f32(float, float, metadata, metadata) #1
-declare i1 @llvm.experimental.constrained.fcmp.f32(float, float, metadata, metadata) #1
+declare i1 @llvm.experimental.constrained.fcmps.f32(float, float, metadata, metadata) 
+declare i1 @llvm.experimental.constrained.fcmp.f32(float, float, metadata, metadata) 



More information about the llvm-commits mailing list