[llvm] [SPIRV] Evaluate G_ZEXT fewerElementsIf before vector size legality rules (PR #219017)

Farzon Lotfi via llvm-commits llvm-commits at lists.llvm.org
Wed Aug 26 22:03:53 PDT 2026


https://github.com/farzonl updated https://github.com/llvm/llvm-project/pull/219017

>From 7b13c89266dd868bdea8691f84284d4a4b567495 Mon Sep 17 00:00:00 2001
From: Farzon Lotfi <farzon at farzon.org>
Date: Wed, 26 Aug 2026 14:44:15 -0400
Subject: [PATCH 1/2] [SPIRV] Evaluate G_ZEXT fewerElementsIf before vector
 size legality rules

fixes #218444 for power of 2 vectors

So what is going on here is after we legalized the FCMP and ICMP
instructions the SPIRV legalizer has two legalization paths. One path
for power of 2 based vectors and one for non power of 2 based vectors.
The previous fix only exercised the non power of 2 based vectors
which is why we saw 3x3 and 4x3 based matrix types light up. The
power of 2 based case did not light up because it hit Zext since
HLSL bools are converted from i1s to i32s. the Zext instruction
needs to legalize the size befor looking at legal vector sizes because
those legal vector sizes include opencl vector sizes. The fix was
to change the ordering.
---
 llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp  |  6 +--
 .../legalization/matrix-wide-vector-shader.ll | 48 +++++++++++++++++++
 2 files changed, 51 insertions(+), 3 deletions(-)

diff --git a/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp b/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp
index e32d720aed8f7..fde1cc02900d1 100644
--- a/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp
@@ -360,12 +360,12 @@ SPIRVLegalizerInfo::SPIRVLegalizerInfo(const SPIRVSubtarget &ST) {
       .legalIf(ExtendedScalarsAndVectorsProduct);
 
   getActionDefinitionsBuilder({G_TRUNC, G_ZEXT, G_SEXT, G_ANYEXT})
-      .legalForCartesianProduct(allScalarsAndVectors)
-      .legalIf(ExtendedScalarsAndVectorsProduct)
       .moreElementsToNextPow2(0)
       .fewerElementsIf(vectorElementCountIsGreaterThan(0, MaxVectorSize),
                        LegalizeMutations::changeElementCountTo(
-                           0, ElementCount::getFixed(MaxVectorSize)));
+                           0, ElementCount::getFixed(MaxVectorSize)))
+      .legalForCartesianProduct(allScalarsAndVectors)
+      .legalIf(ExtendedScalarsAndVectorsProduct);
 
   getActionDefinitionsBuilder(G_SEXT_INREG)
       .moreElementsToNextPow2(0)
diff --git a/llvm/test/CodeGen/SPIRV/legalization/matrix-wide-vector-shader.ll b/llvm/test/CodeGen/SPIRV/legalization/matrix-wide-vector-shader.ll
index 0d054b6ef3338..8ffc963e77238 100644
--- a/llvm/test/CodeGen/SPIRV/legalization/matrix-wide-vector-shader.ll
+++ b/llvm/test/CodeGen/SPIRV/legalization/matrix-wide-vector-shader.ll
@@ -4,6 +4,9 @@
 @Ints9    = internal addrspace(10) global [9 x i32] poison
 @Ints9B   = internal addrspace(10) global [9 x i32] poison
 @Bools9   = internal addrspace(10) global [9 x i32] poison
+ at Ints8    = internal addrspace(10) global [8 x i32] poison
+ at Ints8B   = internal addrspace(10) global [8 x i32] poison
+ at Bools8   = internal addrspace(10) global [8 x i32] poison
 @Floats9  = internal addrspace(10) global [9 x float] poison
 @Floats9B = internal addrspace(10) global [9 x float] poison
 @Ints12   = internal addrspace(10) global [12 x i32] poison
@@ -12,7 +15,11 @@
 @Floats12 = internal addrspace(10) global [12 x float] poison
 @Floats12B = internal addrspace(10) global [12 x float] poison
 @Ints16   = internal addrspace(10) global [16 x i32] poison
+ at Ints16B  = internal addrspace(10) global [16 x i32] poison
 @Bools16  = internal addrspace(10) global [16 x i32] poison
+ at Floats16 = internal addrspace(10) global [16 x float] poison
+ at Floats16B = internal addrspace(10) global [16 x float] poison
+ at BoolBits16 = internal addrspace(10) global [16 x i1] poison
 
 ; CHECK-DAG: %[[Bool:[0-9]+]] = OpTypeBool
 ; CHECK-DAG: %[[Int32:[0-9]+]] = OpTypeInt 32 0
@@ -196,6 +203,18 @@ define internal void @narrow_12elem_zext() {
   ret void
 }
 
+; Standalone G_ZEXT regression: the wide result must be split before the
+; Cartesian-product legality rule accepts it.
+; CHECK-LABEL: ; -- Begin function zext_16elem
+; CHECK-COUNT-16: OpSelect %[[Int32]]
+; CHECK-NOT: OpSelect %[[Int32]]
+define internal void @zext_16elem() {
+  %bits = load <16 x i1>, ptr addrspace(10) @BoolBits16
+  %ext = zext <16 x i1> %bits to <16 x i32>
+  store <16 x i32> %ext, ptr addrspace(10) @Bools16
+  ret void
+}
+
 ; CHECK-LABEL: ; -- Begin function narrow_16elem_sext
 ; CHECK: %[[Shl0:[0-9]+]] = OpShiftLeftLogical %[[Vec4Int32]]
 ; CHECK-NEXT: %{{[0-9]+}} = OpShiftRightArithmetic %[[Vec4Int32]] %[[Shl0]]
@@ -216,6 +235,32 @@ define internal void @narrow_16elem_sext() {
 
 ;--- G_ICMP/G_FCMP: split the flattened matrix into 4-lane comparisons ---
 
+; CHECK-LABEL: ; -- Begin function icmp_8elem
+; CHECK-COUNT-2: OpIEqual %[[Vec4Bool]]
+; CHECK-COUNT-2: OpSelect %[[Vec4Int32]]
+; CHECK-NOT: OpIEqual
+define internal void @icmp_8elem() {
+  %a = load <8 x i32>, ptr addrspace(10) @Ints8
+  %b = load <8 x i32>, ptr addrspace(10) @Ints8B
+  %cmp = icmp eq <8 x i32> %a, %b
+  %ext = zext <8 x i1> %cmp to <8 x i32>
+  store <8 x i32> %ext, ptr addrspace(10) @Bools8
+  ret void
+}
+
+; CHECK-LABEL: ; -- Begin function fcmp_16elem
+; CHECK-COUNT-4: OpFOrdEqual %[[Vec4Bool]]
+; CHECK-COUNT-4: OpSelect %[[Vec4Int32]]
+; CHECK-NOT: OpFOrdEqual
+define internal void @fcmp_16elem() {
+  %a = load <16 x float>, ptr addrspace(10) @Floats16
+  %b = load <16 x float>, ptr addrspace(10) @Floats16B
+  %cmp = fcmp oeq <16 x float> %a, %b
+  %ext = zext <16 x i1> %cmp to <16 x i32>
+  store <16 x i32> %ext, ptr addrspace(10) @Bools16
+  ret void
+}
+
 ; CHECK-LABEL: ; -- Begin function icmp_3x3
 ; CHECK-COUNT-8: OpLoad %[[Int32]]
 ; CHECK: %[[ICMP_AS:[0-9]+]] = OpLoad %[[Int32]]
@@ -312,6 +357,9 @@ define void @main() #0 {
   call void @bool4x4_sext()
   call void @narrow_12elem_zext()
   call void @narrow_16elem_sext()
+  call void @zext_16elem()
+  call void @icmp_8elem()
+  call void @fcmp_16elem()
   call void @icmp_3x3()
   call void @icmp_12elem()
   call void @fcmp_3x3()

>From 26c5fe50ca21df58ec0c2d8cccb6a8b75959dab2 Mon Sep 17 00:00:00 2001
From: Farzon Lotfi <farzonlotfi at microsoft.com>
Date: Thu, 27 Aug 2026 01:01:46 -0400
Subject: [PATCH 2/2] the previous change impacts vec3 by promoting them to
 vec4s via moreElementsToNextPow2. It also has broader impact on openCL code
 than I would have liked. instead making an allowedScalarsAndVectors we can
 ST.isShader() to enforce legal shader vectors and then do spliting behaviors

---
 llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp | 13 ++++++++++---
 1 file changed, 10 insertions(+), 3 deletions(-)

diff --git a/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp b/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp
index fde1cc02900d1..96e68ff1e2aae 100644
--- a/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp
@@ -124,6 +124,13 @@ SPIRVLegalizerInfo::SPIRVLegalizerInfo(const SPIRVSubtarget &ST) {
       v4s1,  v4s8,  v4s16, v4s32, v4s64,  v8s1,   v8s8,  v8s16,
       v8s32, v8s64, v16s1, v16s8, v16s16, v16s32, v16s64};
 
+  auto allShaderScalarsAndVectors = {
+      s1,   s8,   s16,   s32,   s64,   s128, v2s1, v2s8,  v2s16, v2s32, v2s64,
+      v3s1, v3s8, v3s16, v3s32, v3s64, v4s1, v4s8, v4s16, v4s32, v4s64};
+
+  auto &allowedScalarsAndVectors =
+      ST.isShader() ? allShaderScalarsAndVectors : allScalarsAndVectors;
+
   auto allIntScalarsAndVectors = {
       s8,    s16,   s32,   s64,   s128,   v2s8,   v2s16, v2s32, v2s64,
       v3s8,  v3s16, v3s32, v3s64, v4s8,   v4s16,  v4s32, v4s64, v8s8,
@@ -360,12 +367,12 @@ SPIRVLegalizerInfo::SPIRVLegalizerInfo(const SPIRVSubtarget &ST) {
       .legalIf(ExtendedScalarsAndVectorsProduct);
 
   getActionDefinitionsBuilder({G_TRUNC, G_ZEXT, G_SEXT, G_ANYEXT})
+      .legalForCartesianProduct(allowedScalarsAndVectors)
+      .legalIf(ExtendedScalarsAndVectorsProduct)
       .moreElementsToNextPow2(0)
       .fewerElementsIf(vectorElementCountIsGreaterThan(0, MaxVectorSize),
                        LegalizeMutations::changeElementCountTo(
-                           0, ElementCount::getFixed(MaxVectorSize)))
-      .legalForCartesianProduct(allScalarsAndVectors)
-      .legalIf(ExtendedScalarsAndVectorsProduct);
+                           0, ElementCount::getFixed(MaxVectorSize)));
 
   getActionDefinitionsBuilder(G_SEXT_INREG)
       .moreElementsToNextPow2(0)



More information about the llvm-commits mailing list