[llvm] [SPIRV] Legalize i1 min/max before selection (PR #204231)

Tim Besard via llvm-commits llvm-commits at lists.llvm.org
Fri Jul 24 03:18:15 PDT 2026


https://github.com/maleadt updated https://github.com/llvm/llvm-project/pull/204231

>From 2a27667b18809b3e1a1b3ac2e841f2bad3bf86a4 Mon Sep 17 00:00:00 2001
From: Tim Besard <tim.besard at gmail.com>
Date: Tue, 16 Jun 2026 20:57:30 +0200
Subject: [PATCH] [SPIRV] Legalize i1 min/max before selection

LLVM i1 maps to OpTypeBool, while the SPIR-V extended min/max instructions require integer scalar or vector types. Keep s1 out of the extended-integer legality rule, scalarize boolean vectors, and widen scalar i1 lanes before instruction selection.

Add coverage for scalar signed/unsigned i1 min/max and a vector boolean case, including the arbitrary-precision integer extension mode that previously selected an invalid boolean OpExtInst.

Co-authored-by: Codex <noreply at openai.com>
---
 llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp  | 11 +++-
 .../SPIRV/llvm-intrinsics/minmax-i1.ll        | 64 +++++++++++++++++++
 2 files changed, 72 insertions(+), 3 deletions(-)
 create mode 100644 llvm/test/CodeGen/SPIRV/llvm-intrinsics/minmax-i1.ll

diff --git a/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp b/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp
index 21586e242404a..74cc84373e748 100644
--- a/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp
@@ -130,6 +130,7 @@ SPIRVLegalizerInfo::SPIRVLegalizerInfo(const SPIRVSubtarget &ST) {
       v8s16, v8s32, v8s64, v16s8, v16s16, v16s32, v16s64};
 
   auto allBoolScalarsAndVectors = {s1, v2s1, v3s1, v4s1, v8s1, v16s1};
+  auto allBoolVectors = {v2s1, v3s1, v4s1, v8s1, v16s1};
 
   auto allIntScalars = {s8, s16, s32, s64, s128};
 
@@ -165,10 +166,11 @@ SPIRVLegalizerInfo::SPIRVLegalizerInfo(const SPIRVSubtarget &ST) {
       HasArbitraryPrecisionInts ||
       ST.canUseExtension(SPIRV::Extension::SPV_KHR_bit_instructions) ||
       ST.canUseExtension(SPIRV::Extension::SPV_INTEL_int4);
-  auto extendedScalarsAndVectors =
+  auto extendedIntScalarsAndVectors =
       [IsExtendedInts](const LegalityQuery &Query) {
         const LLT Ty = Query.Types[0];
-        return IsExtendedInts && Ty.isValid() && !Ty.isPointerOrPointerVector();
+        return IsExtendedInts && Ty.isValid() &&
+               !Ty.isPointerOrPointerVector() && Ty.getScalarSizeInBits() > 1;
       };
   auto extendedScalarsAndVectorsProduct = [IsExtendedInts](
                                               const LegalityQuery &Query) {
@@ -328,7 +330,10 @@ SPIRVLegalizerInfo::SPIRVLegalizerInfo(const SPIRVSubtarget &ST) {
                                G_BITREVERSE, G_SADDSAT, G_UADDSAT, G_SSUBSAT,
                                G_USUBSAT, G_SCMP, G_UCMP})
       .legalFor(allIntScalarsAndVectors)
-      .legalIf(extendedScalarsAndVectors);
+      .legalIf(extendedIntScalarsAndVectors)
+      // LLVM i1 maps to OpTypeBool, not OpTypeInt.
+      .scalarizeIf(typeInSet(0, allBoolVectors), 0)
+      .minScalar(0, s32);
 
   getActionDefinitionsBuilder({G_SSHLSAT, G_USHLSAT}).lower();
 
diff --git a/llvm/test/CodeGen/SPIRV/llvm-intrinsics/minmax-i1.ll b/llvm/test/CodeGen/SPIRV/llvm-intrinsics/minmax-i1.ll
new file mode 100644
index 0000000000000..012d787d96356
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/llvm-intrinsics/minmax-i1.ll
@@ -0,0 +1,64 @@
+; RUN: llc -O0 -verify-machineinstrs -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s
+; RUN: llc -O0 -verify-machineinstrs -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_ALTERA_arbitrary_precision_integers %s -o - | FileCheck %s
+
+; CHECK-DAG: %[[#BOOL:]] = OpTypeBool
+; CHECK-DAG: %[[#INT:]] = OpTypeInt 32 0
+; CHECK-DAG: %[[#VBOOL:]] = OpTypeVector %[[#BOOL]] 2
+; CHECK-DAG: %[[#ZERO:]] = OpConstantNull %[[#INT]]
+; CHECK-DAG: %[[#ONE:]] = OpConstant %[[#INT]] 1
+
+define spir_func i1 @test_umin_i1(i1 %a, i1 %b) {
+entry:
+; CHECK: OpSelect %[[#INT]] %[[#]] %[[#ONE]] %[[#ZERO]]
+; CHECK: OpSelect %[[#INT]] %[[#]] %[[#ONE]] %[[#ZERO]]
+; CHECK: OpExtInst %[[#INT]] %[[#]] u_min %[[#]] %[[#]]
+; CHECK: OpBitwiseAnd %[[#INT]] %[[#]] %[[#ONE]]
+; CHECK: OpINotEqual %[[#BOOL]] %[[#]] %[[#ZERO]]
+  %r = call i1 @llvm.umin.i1(i1 %a, i1 %b)
+  ret i1 %r
+}
+
+define spir_func i1 @test_umax_i1(i1 %a, i1 %b) {
+entry:
+; CHECK: OpExtInst %[[#INT]] %[[#]] u_max %[[#]] %[[#]]
+; CHECK: OpINotEqual %[[#BOOL]] %[[#]] %[[#ZERO]]
+  %r = call i1 @llvm.umax.i1(i1 %a, i1 %b)
+  ret i1 %r
+}
+
+define spir_func i1 @test_smin_i1(i1 %a, i1 %b) {
+entry:
+; CHECK: OpExtInst %[[#INT]] %[[#]] s_min %[[#]] %[[#]]
+; CHECK: OpINotEqual %[[#BOOL]] %[[#]] %[[#ZERO]]
+  %r = call i1 @llvm.smin.i1(i1 %a, i1 %b)
+  ret i1 %r
+}
+
+define spir_func i1 @test_smax_i1(i1 %a, i1 %b) {
+entry:
+; CHECK: OpExtInst %[[#INT]] %[[#]] s_max %[[#]] %[[#]]
+; CHECK: OpINotEqual %[[#BOOL]] %[[#]] %[[#ZERO]]
+  %r = call i1 @llvm.smax.i1(i1 %a, i1 %b)
+  ret i1 %r
+}
+
+define spir_func <2 x i1> @test_umin_v2i1(<2 x i1> %a, <2 x i1> %b) {
+entry:
+; CHECK: OpCompositeExtract %[[#BOOL]] %[[#]] 0
+; CHECK: OpCompositeExtract %[[#BOOL]] %[[#]] 1
+; CHECK: OpCompositeExtract %[[#BOOL]] %[[#]] 0
+; CHECK: OpCompositeExtract %[[#BOOL]] %[[#]] 1
+; CHECK: OpExtInst %[[#INT]] %[[#]] u_min %[[#]] %[[#]]
+; CHECK: OpINotEqual %[[#BOOL]] %[[#]] %[[#ZERO]]
+; CHECK: OpExtInst %[[#INT]] %[[#]] u_min %[[#]] %[[#]]
+; CHECK: OpINotEqual %[[#BOOL]] %[[#]] %[[#ZERO]]
+; CHECK: OpCompositeConstruct %[[#VBOOL]] %[[#]] %[[#]]
+  %r = call <2 x i1> @llvm.umin.v2i1(<2 x i1> %a, <2 x i1> %b)
+  ret <2 x i1> %r
+}
+
+declare i1 @llvm.umin.i1(i1, i1)
+declare i1 @llvm.umax.i1(i1, i1)
+declare i1 @llvm.smin.i1(i1, i1)
+declare i1 @llvm.smax.i1(i1, i1)
+declare <2 x i1> @llvm.umin.v2i1(<2 x i1>, <2 x i1>)



More information about the llvm-commits mailing list