From ff674a32012d2a18fa13dfd7a385375a11a531e3 Mon Sep 17 00:00:00 2001 From: Subash B 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(I)) + if (auto *FPI = dyn_cast(I)) { useRoundingMode(FPI, B); + if (auto *FPCmpI = dyn_cast(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 &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(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(*F); + SmallVector EraseFromParent; for (BasicBlock &BB : *F) { for (Instruction &I : BB) { auto Call = dyn_cast(&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(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 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(I)) { + if (auto *FPI = dyn_cast(I)) useRoundingMode(FPI, B); - if (auto *FPCmpI = dyn_cast(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 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(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 &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(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)