[llvm] [SPIRV] Support additional comparison predicates for i1 types (PR #174585)
Manuel Carrasco via llvm-commits
llvm-commits at lists.llvm.org
Wed Jan 7 07:07:29 PST 2026
https://github.com/mgcarrasco updated https://github.com/llvm/llvm-project/pull/174585
>From 82ba5f6538d36f60703f06c2f89f298ee3ae7325 Mon Sep 17 00:00:00 2001
From: Manuel Carrasco <Manuel.Carrasco at amd.com>
Date: Tue, 6 Jan 2026 07:11:38 -0600
Subject: [PATCH 1/4] [SPIRV] Lower i1 comparisons to logical operations in
regularizer pass.
UGT, UGE, ULT, ULE, SGT, SGE, SLT, SLE predicates for i1 types are now
lowered to equivalent logical operations (AND, OR, NOT) to ensure
valid SPIR-V, since SPIR-V boolean types only support logical operations.
---
llvm/lib/Target/SPIRV/SPIRVRegularizer.cpp | 73 ++++++++++++
llvm/test/CodeGen/SPIRV/icmp-i1.ll | 132 +++++++++++++++++++++
2 files changed, 205 insertions(+)
create mode 100644 llvm/test/CodeGen/SPIRV/icmp-i1.ll
diff --git a/llvm/lib/Target/SPIRV/SPIRVRegularizer.cpp b/llvm/lib/Target/SPIRV/SPIRVRegularizer.cpp
index 653c9ad53e888..20b7783d3e5b1 100644
--- a/llvm/lib/Target/SPIRV/SPIRVRegularizer.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVRegularizer.cpp
@@ -37,6 +37,7 @@ struct SPIRVRegularizer : public FunctionPass {
private:
void runLowerConstExpr(Function &F);
+ void runLowerI1Comparisons(Function &F);
};
} // namespace
@@ -151,7 +152,79 @@ void SPIRVRegularizer::runLowerConstExpr(Function &F) {
}
}
+static Instruction *createLogicalOp(Value *Op1, Value *Op2, bool UseAnd,
+ Constant *TrueVal,
+ BasicBlock::iterator InsertPt) {
+ auto *NotOp2 =
+ BinaryOperator::Create(Instruction::Xor, Op2, TrueVal, "", InsertPt);
+ return BinaryOperator::Create(UseAnd ? Instruction::And : Instruction::Or,
+ Op1, NotOp2, "", InsertPt);
+}
+
+// Lower i1 comparisons with certain predicates to logical operations.
+// The backend treats i1 as boolean values, and SPIR-V only allows logical
+// operations for boolean values. This function lowers i1 comparisons with
+// certain predicates to logical operations to generate valid SPIR-V.
+void SPIRVRegularizer::runLowerI1Comparisons(Function &F) {
+ LLVMContext &Ctx = F.getContext();
+
+ for (auto &I : make_early_inc_range(instructions(F))) {
+ auto *Cmp = dyn_cast<ICmpInst>(&I);
+ if (!Cmp)
+ continue;
+
+ bool IsI1 = Cmp->getOperand(0)->getType()->isIntegerTy(1);
+ if (!IsI1)
+ continue;
+
+ auto Pred = Cmp->getPredicate();
+ bool IsTargetPred =
+ Pred >= ICmpInst::ICMP_UGT && Pred <= ICmpInst::ICMP_SLE;
+ if (!IsTargetPred)
+ continue;
+
+ Value *P = Cmp->getOperand(0);
+ Value *Q = Cmp->getOperand(1);
+ auto *TrueVal = ConstantInt::getTrue(Ctx);
+
+ Instruction *Result = nullptr;
+ switch (Pred) {
+ case ICmpInst::ICMP_UGT:
+ case ICmpInst::ICMP_SLT:
+ // Result = p & !q
+ Result =
+ createLogicalOp(P, Q, /*UseAnd=*/true, TrueVal, Cmp->getIterator());
+ break;
+ case ICmpInst::ICMP_ULT:
+ case ICmpInst::ICMP_SGT:
+ // Result = q & !p
+ Result =
+ createLogicalOp(Q, P, /*UseAnd=*/true, TrueVal, Cmp->getIterator());
+ break;
+ case ICmpInst::ICMP_ULE:
+ case ICmpInst::ICMP_SGE:
+ // Result = q | !p
+ Result =
+ createLogicalOp(Q, P, /*UseAnd=*/false, TrueVal, Cmp->getIterator());
+ break;
+ case ICmpInst::ICMP_UGE:
+ case ICmpInst::ICMP_SLE:
+ // Result = p | !q
+ Result =
+ createLogicalOp(P, Q, /*UseAnd=*/false, TrueVal, Cmp->getIterator());
+ break;
+ default:
+ llvm_unreachable("Unexpected predicate");
+ }
+
+ // Replace all uses and erase the old instruction
+ Cmp->replaceAllUsesWith(Result);
+ Cmp->eraseFromParent();
+ }
+}
+
bool SPIRVRegularizer::runOnFunction(Function &F) {
+ runLowerI1Comparisons(F);
runLowerConstExpr(F);
return true;
}
diff --git a/llvm/test/CodeGen/SPIRV/icmp-i1.ll b/llvm/test/CodeGen/SPIRV/icmp-i1.ll
new file mode 100644
index 0000000000000..258f59d28f2ad
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/icmp-i1.ll
@@ -0,0 +1,132 @@
+; RUN: llc -O0 -mtriple=spirv64-unknown-unknown < %s | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown -filetype=obj < %s | spirv-val %}
+
+target triple = "spirv64-amd-amdhsa"
+
+define spir_func i1 @ugt(i1 %p, i1 %q) addrspace(4) {
+ ; CHECK: Begin function ugt
+ ; CHECK: OpLogicalNotEqual
+ ; CHECK: OpLogicalAnd
+ %r = icmp ugt i1 %p, %q
+ ret i1 %r
+}
+
+define spir_func i1 @ugt_same_sign(i1 %p, i1 %q) addrspace(4) {
+ ; CHECK: Begin function ugt_same_sign
+ ; CHECK: OpLogicalNotEqual
+ ; CHECK: OpLogicalAnd
+ %r = icmp samesign ugt i1 %p, %q
+ ret i1 %r
+}
+
+define spir_func i1 @uge(i1 %p, i1 %q) addrspace(4) {
+ ; CHECK: Begin function uge
+ ; CHECK: OpLogicalNotEqual
+ ; CHECK: OpLogicalOr
+ %r = icmp uge i1 %p, %q
+ ret i1 %r
+}
+
+define spir_func i1 @uge_same_sign(i1 %p, i1 %q) addrspace(4) {
+ ; CHECK: Begin function uge_same_sign
+ ; CHECK: OpLogicalNotEqual
+ ; CHECK: OpLogicalOr
+ %r = icmp samesign uge i1 %p, %q
+ ret i1 %r
+}
+
+define spir_func i1 @ult(i1 %p, i1 %q) addrspace(4) {
+ ; CHECK: Begin function ult
+ ; CHECK: OpLogicalNotEqual
+ ; CHECK: OpLogicalAnd
+ %r = icmp ult i1 %p, %q
+ ret i1 %r
+}
+
+define spir_func i1 @ult_same_sign(i1 %p, i1 %q) addrspace(4) {
+ ; CHECK: Begin function ult_same_sign
+ ; CHECK: OpLogicalNotEqual
+ ; CHECK: OpLogicalAnd
+ %r = icmp samesign ult i1 %p, %q
+ ret i1 %r
+}
+
+define spir_func i1 @ule(i1 %p, i1 %q) addrspace(4) {
+ ; CHECK: Begin function ule
+ ; CHECK: OpLogicalNotEqual
+ ; CHECK: OpLogicalOr
+ %r = icmp ule i1 %p, %q
+ ret i1 %r
+}
+
+define spir_func i1 @ule_same_sign(i1 %p, i1 %q) addrspace(4) {
+ ; CHECK: Begin function ule_same_sign
+ ; CHECK: OpLogicalNotEqual
+ ; CHECK: OpLogicalOr
+ %r = icmp samesign ule i1 %p, %q
+ ret i1 %r
+}
+
+define spir_func i1 @sgt(i1 %p, i1 %q) addrspace(4) {
+ ; CHECK: Begin function sgt
+ ; CHECK: OpLogicalNotEqual
+ ; CHECK: OpLogicalAnd
+ %r = icmp sgt i1 %p, %q
+ ret i1 %r
+}
+
+define spir_func i1 @sgt_same_sign(i1 %p, i1 %q) addrspace(4) {
+ ; CHECK: Begin function sgt_same_sign
+ ; CHECK: OpLogicalNotEqual
+ ; CHECK: OpLogicalAnd
+ %r = icmp samesign sgt i1 %p, %q
+ ret i1 %r
+}
+
+define spir_func i1 @sge(i1 %p, i1 %q) addrspace(4) {
+ ; CHECK: Begin function sge
+ ; CHECK: OpLogicalNotEqual
+ ; CHECK: OpLogicalOr
+ %r = icmp sge i1 %p, %q
+ ret i1 %r
+}
+
+define spir_func i1 @sge_same_sign(i1 %p, i1 %q) addrspace(4) {
+ ; CHECK: Begin function sge_same_sign
+ ; CHECK: OpLogicalNotEqual
+ ; CHECK: OpLogicalOr
+ %r = icmp samesign sge i1 %p, %q
+ ret i1 %r
+}
+
+define spir_func i1 @slt(i1 %p, i1 %q) addrspace(4) {
+ ; CHECK: Begin function slt
+ ; CHECK: OpLogicalNotEqual
+ ; CHECK: OpLogicalAnd
+ %r = icmp slt i1 %p, %q
+ ret i1 %r
+}
+
+define spir_func i1 @slt_same_sign(i1 %p, i1 %q) addrspace(4) {
+ ; CHECK: Begin function slt_same_sign
+ ; CHECK: OpLogicalNotEqual
+ ; CHECK: OpLogicalAnd
+ %r = icmp samesign slt i1 %p, %q
+ ret i1 %r
+}
+
+define spir_func i1 @sle(i1 %p, i1 %q) addrspace(4) {
+ ; CHECK: Begin function sle
+ ; CHECK: OpLogicalNotEqual
+ ; CHECK: OpLogicalOr
+ %r = icmp sle i1 %p, %q
+ ret i1 %r
+}
+
+define spir_func i1 @sle_same_sign(i1 %p, i1 %q) addrspace(4) {
+ ; CHECK: Begin function sle_same_sign
+ ; CHECK: OpLogicalNotEqual
+ ; CHECK: OpLogicalOr
+ %r = icmp samesign sle i1 %p, %q
+ ret i1 %r
+}
>From 632d077aac6368acad8225a58e4cd5cfa5494f15 Mon Sep 17 00:00:00 2001
From: Manuel Carrasco <Manuel.Carrasco at amd.com>
Date: Wed, 7 Jan 2026 08:27:21 -0600
Subject: [PATCH 2/4] [review] Use IRBuilder and inline call.
---
llvm/lib/Target/SPIRV/SPIRVRegularizer.cpp | 28 ++++++----------------
1 file changed, 7 insertions(+), 21 deletions(-)
diff --git a/llvm/lib/Target/SPIRV/SPIRVRegularizer.cpp b/llvm/lib/Target/SPIRV/SPIRVRegularizer.cpp
index 20b7783d3e5b1..6df8f326e11c5 100644
--- a/llvm/lib/Target/SPIRV/SPIRVRegularizer.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVRegularizer.cpp
@@ -13,6 +13,7 @@
#include "SPIRV.h"
#include "llvm/IR/Constants.h"
+#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/InstIterator.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/PassManager.h"
@@ -152,22 +153,11 @@ void SPIRVRegularizer::runLowerConstExpr(Function &F) {
}
}
-static Instruction *createLogicalOp(Value *Op1, Value *Op2, bool UseAnd,
- Constant *TrueVal,
- BasicBlock::iterator InsertPt) {
- auto *NotOp2 =
- BinaryOperator::Create(Instruction::Xor, Op2, TrueVal, "", InsertPt);
- return BinaryOperator::Create(UseAnd ? Instruction::And : Instruction::Or,
- Op1, NotOp2, "", InsertPt);
-}
-
// Lower i1 comparisons with certain predicates to logical operations.
// The backend treats i1 as boolean values, and SPIR-V only allows logical
// operations for boolean values. This function lowers i1 comparisons with
// certain predicates to logical operations to generate valid SPIR-V.
void SPIRVRegularizer::runLowerI1Comparisons(Function &F) {
- LLVMContext &Ctx = F.getContext();
-
for (auto &I : make_early_inc_range(instructions(F))) {
auto *Cmp = dyn_cast<ICmpInst>(&I);
if (!Cmp)
@@ -185,33 +175,29 @@ void SPIRVRegularizer::runLowerI1Comparisons(Function &F) {
Value *P = Cmp->getOperand(0);
Value *Q = Cmp->getOperand(1);
- auto *TrueVal = ConstantInt::getTrue(Ctx);
- Instruction *Result = nullptr;
+ IRBuilder<> Builder(Cmp);
+ Value *Result = nullptr;
switch (Pred) {
case ICmpInst::ICMP_UGT:
case ICmpInst::ICMP_SLT:
// Result = p & !q
- Result =
- createLogicalOp(P, Q, /*UseAnd=*/true, TrueVal, Cmp->getIterator());
+ Result = Builder.CreateAnd(P, Builder.CreateNot(Q));
break;
case ICmpInst::ICMP_ULT:
case ICmpInst::ICMP_SGT:
// Result = q & !p
- Result =
- createLogicalOp(Q, P, /*UseAnd=*/true, TrueVal, Cmp->getIterator());
+ Result = Builder.CreateAnd(Q, Builder.CreateNot(P));
break;
case ICmpInst::ICMP_ULE:
case ICmpInst::ICMP_SGE:
// Result = q | !p
- Result =
- createLogicalOp(Q, P, /*UseAnd=*/false, TrueVal, Cmp->getIterator());
+ Result = Builder.CreateOr(Q, Builder.CreateNot(P));
break;
case ICmpInst::ICMP_UGE:
case ICmpInst::ICMP_SLE:
// Result = p | !q
- Result =
- createLogicalOp(P, Q, /*UseAnd=*/false, TrueVal, Cmp->getIterator());
+ Result = Builder.CreateOr(P, Builder.CreateNot(Q));
break;
default:
llvm_unreachable("Unexpected predicate");
>From 2e7c3da171511e3a00c70b5a006986968a4fcd73 Mon Sep 17 00:00:00 2001
From: Manuel Carrasco <Manuel.Carrasco at amd.com>
Date: Wed, 7 Jan 2026 08:27:42 -0600
Subject: [PATCH 3/4] [review] Take name.
---
llvm/lib/Target/SPIRV/SPIRVRegularizer.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/lib/Target/SPIRV/SPIRVRegularizer.cpp b/llvm/lib/Target/SPIRV/SPIRVRegularizer.cpp
index 6df8f326e11c5..c864556783a2a 100644
--- a/llvm/lib/Target/SPIRV/SPIRVRegularizer.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVRegularizer.cpp
@@ -203,7 +203,7 @@ void SPIRVRegularizer::runLowerI1Comparisons(Function &F) {
llvm_unreachable("Unexpected predicate");
}
- // Replace all uses and erase the old instruction
+ Result->takeName(Cmp);
Cmp->replaceAllUsesWith(Result);
Cmp->eraseFromParent();
}
>From 926405fa472c0334efa9083a2ac9d678ada5d7c7 Mon Sep 17 00:00:00 2001
From: Manuel Carrasco <Manuel.Carrasco at amd.com>
Date: Wed, 7 Jan 2026 09:07:03 -0600
Subject: [PATCH 4/4] [review] remove triple.
---
llvm/test/CodeGen/SPIRV/icmp-i1.ll | 2 --
1 file changed, 2 deletions(-)
diff --git a/llvm/test/CodeGen/SPIRV/icmp-i1.ll b/llvm/test/CodeGen/SPIRV/icmp-i1.ll
index 258f59d28f2ad..576bafebd6124 100644
--- a/llvm/test/CodeGen/SPIRV/icmp-i1.ll
+++ b/llvm/test/CodeGen/SPIRV/icmp-i1.ll
@@ -1,8 +1,6 @@
; RUN: llc -O0 -mtriple=spirv64-unknown-unknown < %s | FileCheck %s
; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown -filetype=obj < %s | spirv-val %}
-target triple = "spirv64-amd-amdhsa"
-
define spir_func i1 @ugt(i1 %p, i1 %q) addrspace(4) {
; CHECK: Begin function ugt
; CHECK: OpLogicalNotEqual
More information about the llvm-commits
mailing list