[llvm] [LLVM][SelectionDAG] Reduce chances of a split VSETCC being rewidened. (PR #191438)

Paul Walker via llvm-commits llvm-commits at lists.llvm.org
Tue Apr 14 10:46:57 PDT 2026


https://github.com/paulwalker-arm updated https://github.com/llvm/llvm-project/pull/191438

>From dd5b23d73d469aa933ee3ee5c24162d407884068 Mon Sep 17 00:00:00 2001
From: Paul Walker <paul.walker at arm.com>
Date: Fri, 10 Apr 2026 14:13:43 +0100
Subject: [PATCH 1/2] [LLVM][SelectionDAG] Reduce chances of a split VSETCC
 being rewidened.

This is a reimplementation of https://github.com/llvm/llvm-project/pull/139295
but without introducing a potential infinite type legalisation loop
whereby splitting a SETCC requires the result to be rewidened.

Fixes https://github.com/llvm/llvm-project/issues/190773
---
 .../SelectionDAG/LegalizeVectorTypes.cpp      | 20 +++++--
 .../test/CodeGen/WebAssembly/simd-pr190773.ll | 52 +++++++++++++++++++
 2 files changed, 69 insertions(+), 3 deletions(-)
 create mode 100644 llvm/test/CodeGen/WebAssembly/simd-pr190773.ll

diff --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp b/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp
index a96c77bc6a4e9..b1190abdb34f2 100644
--- a/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp
@@ -4796,9 +4796,15 @@ SDValue DAGTypeLegalizer::SplitVecOp_VSETCC(SDNode *N) {
   GetSplitVector(N->getOperand(isStrict ? 1 : 0), Lo0, Hi0);
   GetSplitVector(N->getOperand(isStrict ? 2 : 1), Lo1, Hi1);
 
+  LLVMContext &Context = *DAG.getContext();
   EVT VT = N->getValueType(0);
-  EVT PartResVT = Lo0.getValueType().changeElementType(*DAG.getContext(),
-                                                       VT.getScalarType());
+  EVT PartResVT =
+      Lo0.getValueType().changeElementType(Context, VT.getScalarType());
+
+  // If splitting the operand is going to cause the result to be widened, we're
+  // better of using an i1 vector that can be promoted instead.
+  if (getTypeAction(PartResVT) == TargetLowering::TypeWidenVector)
+    PartResVT = PartResVT.changeElementType(Context, MVT::i1);
 
   if (Opc == ISD::SETCC) {
     LoRes = DAG.getNode(ISD::SETCC, DL, PartResVT, Lo0, Lo1, N->getOperand(2));
@@ -4823,7 +4829,15 @@ SDValue DAGTypeLegalizer::SplitVecOp_VSETCC(SDNode *N) {
                         N->getOperand(2), MaskHi, EVLHi);
   }
 
-  return DAG.getNode(ISD::CONCAT_VECTORS, DL, VT, LoRes, HiRes);
+  EVT ConcatVT = PartResVT.getDoubleNumVectorElementsVT(Context);
+  SDValue Con = DAG.getNode(ISD::CONCAT_VECTORS, DL, ConcatVT, LoRes, HiRes);
+  if (ConcatVT == VT)
+    return Con;
+
+  EVT OpVT = N->getOperand(0).getValueType();
+  ISD::NodeType ExtendCode =
+      TargetLowering::getExtendForContent(TLI.getBooleanContents(OpVT));
+  return DAG.getNode(ExtendCode, DL, VT, Con);
 }
 
 
diff --git a/llvm/test/CodeGen/WebAssembly/simd-pr190773.ll b/llvm/test/CodeGen/WebAssembly/simd-pr190773.ll
new file mode 100644
index 0000000000000..4c7fa2d92726c
--- /dev/null
+++ b/llvm/test/CodeGen/WebAssembly/simd-pr190773.ll
@@ -0,0 +1,52 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc < %s -mtriple=wasm32 -mattr=+simd128 | FileCheck %s
+
+define <4 x i1>  @f(ptr %out, <4 x half> %v) {
+; CHECK-LABEL: f:
+; CHECK:         .functype f (i32, i32, i32, i32, i32) -> (v128)
+; CHECK-NEXT:    .local f32, f32, f32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    local.get 4
+; CHECK-NEXT:    call __extendhfsf2
+; CHECK-NEXT:    local.set 5
+; CHECK-NEXT:    local.get 3
+; CHECK-NEXT:    call __extendhfsf2
+; CHECK-NEXT:    local.set 6
+; CHECK-NEXT:    local.get 2
+; CHECK-NEXT:    call __extendhfsf2
+; CHECK-NEXT:    local.set 7
+; CHECK-NEXT:    i32.const 0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call __extendhfsf2
+; CHECK-NEXT:    f32.const 0x0p0
+; CHECK-NEXT:    f32.ne
+; CHECK-NEXT:    i32.sub
+; CHECK-NEXT:    i16x8.splat
+; CHECK-NEXT:    i32.const 0
+; CHECK-NEXT:    local.get 7
+; CHECK-NEXT:    f32.const 0x0p0
+; CHECK-NEXT:    f32.ne
+; CHECK-NEXT:    i32.sub
+; CHECK-NEXT:    i16x8.replace_lane 1
+; CHECK-NEXT:    i32x4.extend_low_i16x8_s
+; CHECK-NEXT:    i64x2.extend_low_i32x4_s
+; CHECK-NEXT:    i32.const 0
+; CHECK-NEXT:    local.get 6
+; CHECK-NEXT:    f32.const 0x0p0
+; CHECK-NEXT:    f32.ne
+; CHECK-NEXT:    i32.sub
+; CHECK-NEXT:    i16x8.splat
+; CHECK-NEXT:    i32.const 0
+; CHECK-NEXT:    local.get 5
+; CHECK-NEXT:    f32.const 0x0p0
+; CHECK-NEXT:    f32.ne
+; CHECK-NEXT:    i32.sub
+; CHECK-NEXT:    i16x8.replace_lane 1
+; CHECK-NEXT:    i32x4.extend_low_i16x8_s
+; CHECK-NEXT:    i64x2.extend_low_i32x4_s
+; CHECK-NEXT:    i8x16.shuffle 0, 1, 8, 9, 16, 17, 24, 25, 0, 1, 0, 1, 0, 1, 0, 1
+; CHECK-NEXT:    i32x4.extend_low_i16x8_u
+; CHECK-NEXT:    # fallthrough-return
+  %cmp = fcmp une <4 x half> %v, zeroinitializer
+  ret <4 x i1> %cmp
+}

>From 1f81c8413a87685151fc19c0a7ca2407cab898b6 Mon Sep 17 00:00:00 2001
From: Paul Walker <paul.walker at arm.com>
Date: Tue, 14 Apr 2026 15:35:50 +0100
Subject: [PATCH 2/2] Let getSetCCResultType decide the result type of split
 VSETCC operations.

---
 .../SelectionDAG/LegalizeVectorTypes.cpp      | 15 +++------
 llvm/lib/Target/ARM/ARMISelLowering.cpp       | 12 +++----
 .../WebAssembly/WebAssemblyISelLowering.cpp   |  6 +++-
 .../test/CodeGen/WebAssembly/simd-pr190773.ll | 31 ++++++++-----------
 4 files changed, 27 insertions(+), 37 deletions(-)

diff --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp b/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp
index b1190abdb34f2..3ddc403bc222d 100644
--- a/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp
@@ -4796,15 +4796,8 @@ SDValue DAGTypeLegalizer::SplitVecOp_VSETCC(SDNode *N) {
   GetSplitVector(N->getOperand(isStrict ? 1 : 0), Lo0, Hi0);
   GetSplitVector(N->getOperand(isStrict ? 2 : 1), Lo1, Hi1);
 
-  LLVMContext &Context = *DAG.getContext();
   EVT VT = N->getValueType(0);
-  EVT PartResVT =
-      Lo0.getValueType().changeElementType(Context, VT.getScalarType());
-
-  // If splitting the operand is going to cause the result to be widened, we're
-  // better of using an i1 vector that can be promoted instead.
-  if (getTypeAction(PartResVT) == TargetLowering::TypeWidenVector)
-    PartResVT = PartResVT.changeElementType(Context, MVT::i1);
+  EVT PartResVT = getSetCCResultType(Lo0.getValueType());
 
   if (Opc == ISD::SETCC) {
     LoRes = DAG.getNode(ISD::SETCC, DL, PartResVT, Lo0, Lo1, N->getOperand(2));
@@ -4829,15 +4822,15 @@ SDValue DAGTypeLegalizer::SplitVecOp_VSETCC(SDNode *N) {
                         N->getOperand(2), MaskHi, EVLHi);
   }
 
-  EVT ConcatVT = PartResVT.getDoubleNumVectorElementsVT(Context);
+  EVT ConcatVT = PartResVT.getDoubleNumVectorElementsVT(*DAG.getContext());
   SDValue Con = DAG.getNode(ISD::CONCAT_VECTORS, DL, ConcatVT, LoRes, HiRes);
-  if (ConcatVT == VT)
+  if (VT == ConcatVT)
     return Con;
 
   EVT OpVT = N->getOperand(0).getValueType();
   ISD::NodeType ExtendCode =
       TargetLowering::getExtendForContent(TLI.getBooleanContents(OpVT));
-  return DAG.getNode(ExtendCode, DL, VT, Con);
+  return DAG.getExtOrTrunc(Con, DL, VT, ExtendCode);
 }
 
 
diff --git a/llvm/lib/Target/ARM/ARMISelLowering.cpp b/llvm/lib/Target/ARM/ARMISelLowering.cpp
index 0ad6c07f36868..03af94bab92a1 100644
--- a/llvm/lib/Target/ARM/ARMISelLowering.cpp
+++ b/llvm/lib/Target/ARM/ARMISelLowering.cpp
@@ -1502,18 +1502,16 @@ ARMTargetLowering::findRepresentativeClass(const TargetRegisterInfo *TRI,
   return std::make_pair(RRC, Cost);
 }
 
-EVT ARMTargetLowering::getSetCCResultType(const DataLayout &DL, LLVMContext &,
+EVT ARMTargetLowering::getSetCCResultType(const DataLayout &DL, LLVMContext &C,
                                           EVT VT) const {
   if (!VT.isVector())
     return getPointerTy(DL);
 
   // MVE has a predicate register.
-  if ((Subtarget->hasMVEIntegerOps() &&
-       (VT == MVT::v2i64 || VT == MVT::v4i32 || VT == MVT::v8i16 ||
-        VT == MVT::v16i8)) ||
-      (Subtarget->hasMVEFloatOps() &&
-       (VT == MVT::v2f64 || VT == MVT::v4f32 || VT == MVT::v8f16)))
-    return MVT::getVectorVT(MVT::i1, VT.getVectorElementCount());
+  if ((Subtarget->hasMVEIntegerOps() && VT.isInteger()) ||
+      (Subtarget->hasMVEFloatOps() && VT.isFloatingPoint()))
+    return VT.changeElementType(C, MVT::i1);
+
   return VT.changeVectorElementTypeToInteger();
 }
 
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
index efa6a61f82e9d..a5db924a79fd9 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
@@ -1067,8 +1067,12 @@ bool WebAssemblyTargetLowering::isOffsetFoldingLegal(
 EVT WebAssemblyTargetLowering::getSetCCResultType(const DataLayout &DL,
                                                   LLVMContext &C,
                                                   EVT VT) const {
-  if (VT.isVector())
+  if (VT.isVector()) {
+    if (VT.getVectorElementType() == MVT::f16 && !Subtarget->hasFP16())
+      return VT.changeElementType(C, MVT::i1);
+
     return VT.changeVectorElementTypeToInteger();
+  }
 
   // So far, all branch instructions in Wasm take an I32 condition.
   // The default TargetLowering::getSetCCResultType returns the pointer size,
diff --git a/llvm/test/CodeGen/WebAssembly/simd-pr190773.ll b/llvm/test/CodeGen/WebAssembly/simd-pr190773.ll
index 4c7fa2d92726c..e68ed6eaddbeb 100644
--- a/llvm/test/CodeGen/WebAssembly/simd-pr190773.ll
+++ b/llvm/test/CodeGen/WebAssembly/simd-pr190773.ll
@@ -15,37 +15,32 @@ define <4 x i1>  @f(ptr %out, <4 x half> %v) {
 ; CHECK-NEXT:    local.get 2
 ; CHECK-NEXT:    call __extendhfsf2
 ; CHECK-NEXT:    local.set 7
-; CHECK-NEXT:    i32.const 0
 ; CHECK-NEXT:    local.get 1
 ; CHECK-NEXT:    call __extendhfsf2
 ; CHECK-NEXT:    f32.const 0x0p0
 ; CHECK-NEXT:    f32.ne
-; CHECK-NEXT:    i32.sub
-; CHECK-NEXT:    i16x8.splat
-; CHECK-NEXT:    i32.const 0
+; CHECK-NEXT:    i32x4.splat
 ; CHECK-NEXT:    local.get 7
 ; CHECK-NEXT:    f32.const 0x0p0
 ; CHECK-NEXT:    f32.ne
-; CHECK-NEXT:    i32.sub
-; CHECK-NEXT:    i16x8.replace_lane 1
-; CHECK-NEXT:    i32x4.extend_low_i16x8_s
-; CHECK-NEXT:    i64x2.extend_low_i32x4_s
-; CHECK-NEXT:    i32.const 0
+; CHECK-NEXT:    i32x4.replace_lane 2
+; CHECK-NEXT:    i32.const 63
+; CHECK-NEXT:    i64x2.shl
+; CHECK-NEXT:    i32.const 63
+; CHECK-NEXT:    i64x2.shr_s
 ; CHECK-NEXT:    local.get 6
 ; CHECK-NEXT:    f32.const 0x0p0
 ; CHECK-NEXT:    f32.ne
-; CHECK-NEXT:    i32.sub
-; CHECK-NEXT:    i16x8.splat
-; CHECK-NEXT:    i32.const 0
+; CHECK-NEXT:    i32x4.splat
 ; CHECK-NEXT:    local.get 5
 ; CHECK-NEXT:    f32.const 0x0p0
 ; CHECK-NEXT:    f32.ne
-; CHECK-NEXT:    i32.sub
-; CHECK-NEXT:    i16x8.replace_lane 1
-; CHECK-NEXT:    i32x4.extend_low_i16x8_s
-; CHECK-NEXT:    i64x2.extend_low_i32x4_s
-; CHECK-NEXT:    i8x16.shuffle 0, 1, 8, 9, 16, 17, 24, 25, 0, 1, 0, 1, 0, 1, 0, 1
-; CHECK-NEXT:    i32x4.extend_low_i16x8_u
+; CHECK-NEXT:    i32x4.replace_lane 2
+; CHECK-NEXT:    i32.const 63
+; CHECK-NEXT:    i64x2.shl
+; CHECK-NEXT:    i32.const 63
+; CHECK-NEXT:    i64x2.shr_s
+; CHECK-NEXT:    i8x16.shuffle 0, 1, 2, 3, 8, 9, 10, 11, 16, 17, 18, 19, 24, 25, 26, 27
 ; CHECK-NEXT:    # fallthrough-return
   %cmp = fcmp une <4 x half> %v, zeroinitializer
   ret <4 x i1> %cmp



More information about the llvm-commits mailing list