[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