[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