[llvm] [RISCV] Fold vmnot.m of an integer compare into the compare (PR #222553)
Marton Moro via llvm-commits
llvm-commits at lists.llvm.org
Tue Sep 15 00:13:51 PDT 2026
https://github.com/martonmoro updated https://github.com/llvm/llvm-project/pull/222553
>From 4c4da2a13f54d8a9111ec0e5eea6bd9be7ff90c3 Mon Sep 17 00:00:00 2001
From: Marton Moro <moro.marton at gmail.com>
Date: Thu, 10 Sep 2026 12:34:38 +0200
Subject: [PATCH 1/3] [RISCV] Add tests for mask NOT of integer vector
compares. NFC
---
.../CodeGen/RISCV/rvv/combine-vmnot-setcc.ll | 84 +++++++++++++++++++
1 file changed, 84 insertions(+)
create mode 100644 llvm/test/CodeGen/RISCV/rvv/combine-vmnot-setcc.ll
diff --git a/llvm/test/CodeGen/RISCV/rvv/combine-vmnot-setcc.ll b/llvm/test/CodeGen/RISCV/rvv/combine-vmnot-setcc.ll
new file mode 100644
index 0000000000000..b2ca2b8bacd08
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/rvv/combine-vmnot-setcc.ll
@@ -0,0 +1,84 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc -mtriple=riscv32 -mattr=+v < %s | FileCheck %s
+; RUN: llc -mtriple=riscv64 -mattr=+v < %s | FileCheck %s
+
+define i1 @reduce_and_trunc_v8i8(<8 x i8> %v) {
+; CHECK-LABEL: reduce_and_trunc_v8i8:
+; CHECK: # %bb.0:
+; CHECK-NEXT: vsetivli zero, 8, e8, mf2, ta, ma
+; CHECK-NEXT: vand.vi v8, v8, 1
+; CHECK-NEXT: vmsne.vi v8, v8, 0
+; CHECK-NEXT: vmnot.m v8, v8
+; CHECK-NEXT: vcpop.m a0, v8
+; CHECK-NEXT: seqz a0, a0
+; CHECK-NEXT: ret
+ %t = trunc <8 x i8> %v to <8 x i1>
+ %r = call i1 @llvm.vector.reduce.and.v8i1(<8 x i1> %t)
+ ret i1 %r
+}
+
+define i1 @reduce_and_icmp_slt_v8i8(<8 x i8> %v) {
+; CHECK-LABEL: reduce_and_icmp_slt_v8i8:
+; CHECK: # %bb.0:
+; CHECK-NEXT: vsetivli zero, 8, e8, mf2, ta, ma
+; CHECK-NEXT: vmsle.vi v8, v8, 4
+; CHECK-NEXT: vmnot.m v8, v8
+; CHECK-NEXT: vcpop.m a0, v8
+; CHECK-NEXT: seqz a0, a0
+; CHECK-NEXT: ret
+ %c = icmp slt <8 x i8> %v, splat (i8 5)
+ %r = call i1 @llvm.vector.reduce.and.v8i1(<8 x i1> %c)
+ ret i1 %r
+}
+
+define <8 x i1> @not_trunc_v8i8(<8 x i8> %v) {
+; CHECK-LABEL: not_trunc_v8i8:
+; CHECK: # %bb.0:
+; CHECK-NEXT: vsetivli zero, 8, e8, mf2, ta, ma
+; CHECK-NEXT: vand.vi v8, v8, 1
+; CHECK-NEXT: vmsne.vi v8, v8, 0
+; CHECK-NEXT: vmnot.m v0, v8
+; CHECK-NEXT: ret
+ %t = trunc <8 x i8> %v to <8 x i1>
+ %n = xor <8 x i1> %t, splat (i1 true)
+ ret <8 x i1> %n
+}
+
+; Floating-point compares must not be inverted
+define i1 @reduce_and_fcmp_olt_v8f32(<8 x float> %v) {
+; CHECK-LABEL: reduce_and_fcmp_olt_v8f32:
+; CHECK: # %bb.0:
+; CHECK-NEXT: fmv.w.x fa5, zero
+; CHECK-NEXT: vsetivli zero, 8, e32, m2, ta, ma
+; CHECK-NEXT: vmflt.vf v10, v8, fa5
+; CHECK-NEXT: vmnot.m v8, v10
+; CHECK-NEXT: vcpop.m a0, v8
+; CHECK-NEXT: seqz a0, a0
+; CHECK-NEXT: ret
+ %c = fcmp olt <8 x float> %v, zeroinitializer
+ %r = call i1 @llvm.vector.reduce.and.v8i1(<8 x i1> %c)
+ ret i1 %r
+}
+
+; The compare below has a non-trivial mask and no passthru.
+define <8 x i1> @not_vp_reverse_v8i1(<8 x i1> %v, <8 x i1> %m, i32 zeroext %evl) {
+; CHECK-LABEL: not_vp_reverse_v8i1:
+; CHECK: # %bb.0:
+; CHECK-NEXT: vsetvli zero, a0, e8, mf2, ta, ma
+; CHECK-NEXT: vmv.v.i v9, 0
+; CHECK-NEXT: vmerge.vim v9, v9, 1, v0
+; CHECK-NEXT: vmv1r.v v0, v8
+; CHECK-NEXT: vsetvli zero, zero, e16, m1, ta, ma
+; CHECK-NEXT: vid.v v10, v0.t
+; CHECK-NEXT: addi a0, a0, -1
+; CHECK-NEXT: vrsub.vx v10, v10, a0, v0.t
+; CHECK-NEXT: vsetvli zero, zero, e8, mf2, ta, ma
+; CHECK-NEXT: vrgatherei16.vv v11, v9, v10, v0.t
+; CHECK-NEXT: vmsne.vi v8, v11, 0, v0.t
+; CHECK-NEXT: vsetivli zero, 8, e8, mf2, ta, ma
+; CHECK-NEXT: vmnot.m v0, v8
+; CHECK-NEXT: ret
+ %r = call <8 x i1> @llvm.experimental.vp.reverse.v8i1(<8 x i1> %v, <8 x i1> %m, i32 %evl)
+ %n = xor <8 x i1> %r, splat (i1 true)
+ ret <8 x i1> %n
+}
>From 5e33947d986fdb30020695c8d833e9c18715f0b6 Mon Sep 17 00:00:00 2001
From: Marton Moro <moro.marton at gmail.com>
Date: Thu, 10 Sep 2026 13:31:44 +0200
Subject: [PATCH 2/3] [RISCV] Fold vmnot.m of an integer compare into the
compare
(vmxor_vl (setcc_vl a, b, cc), vmset_vl) -> (setcc_vl a, b, !cc)
Fixes #222158
---
llvm/lib/Target/RISCV/RISCVISelLowering.cpp | 25 +++++++++++++++++++
.../CodeGen/RISCV/rvv/combine-vmnot-setcc.ll | 13 +++-------
2 files changed, 29 insertions(+), 9 deletions(-)
diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
index 5f8ad5da42da1..6accbbbcaa9a5 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -19253,6 +19253,29 @@ static SDValue performORCombine(SDNode *N, TargetLowering::DAGCombinerInfo &DCI,
return combineSelectAndUseCommutative(N, DAG, /*AllOnes*/ false, Subtarget);
}
+// Fold (vmxor_vl (setcc_vl a, b, cc), vmset_vl) -> (setcc_vl a, b, !cc) for
+// integer compares. The NOT also flips the masked-off lanes, which the inverted
+// compare copies unchanged from the passthru. So the passthru must be undef,
+// but any mask is fine.
+static SDValue combineVMNOTOfSetCC(SDNode *N, SelectionDAG &DAG) {
+ SDValue Cmp = N->getOperand(0);
+ if (Cmp.getOpcode() == RISCVISD::VMSET_VL)
+ Cmp = N->getOperand(1);
+ else if (N->getOperand(1).getOpcode() != RISCVISD::VMSET_VL)
+ return SDValue();
+
+ if (Cmp.getOpcode() != RISCVISD::SETCC_VL || !Cmp.hasOneUse() ||
+ !Cmp.getOperand(0).getValueType().isInteger() ||
+ !Cmp.getOperand(3).isUndef())
+ return SDValue();
+
+ ISD::CondCode CC = cast<CondCodeSDNode>(Cmp.getOperand(2))->get();
+ CC = ISD::getSetCCInverse(CC, Cmp.getOperand(0).getValueType());
+ return DAG.getNode(RISCVISD::SETCC_VL, SDLoc(N), Cmp.getValueType(),
+ {Cmp.getOperand(0), Cmp.getOperand(1), DAG.getCondCode(CC),
+ Cmp.getOperand(3), Cmp.getOperand(4), Cmp.getOperand(5)});
+}
+
static SDValue performXORCombine(SDNode *N, SelectionDAG &DAG,
const RISCVSubtarget &Subtarget) {
SDValue N0 = N->getOperand(0);
@@ -25260,6 +25283,8 @@ SDValue RISCVTargetLowering::PerformDAGCombine(SDNode *N,
return N->getOperand(0);
break;
}
+ case RISCVISD::VMXOR_VL:
+ return combineVMNOTOfSetCC(N, DAG);
case RISCVISD::VMERGE_VL: {
// vmerge_vl allones, x, y, passthru, vl -> vmv_v_v passthru, x, vl
SDValue Mask = N->getOperand(0);
diff --git a/llvm/test/CodeGen/RISCV/rvv/combine-vmnot-setcc.ll b/llvm/test/CodeGen/RISCV/rvv/combine-vmnot-setcc.ll
index b2ca2b8bacd08..20959082c9167 100644
--- a/llvm/test/CodeGen/RISCV/rvv/combine-vmnot-setcc.ll
+++ b/llvm/test/CodeGen/RISCV/rvv/combine-vmnot-setcc.ll
@@ -7,8 +7,7 @@ define i1 @reduce_and_trunc_v8i8(<8 x i8> %v) {
; CHECK: # %bb.0:
; CHECK-NEXT: vsetivli zero, 8, e8, mf2, ta, ma
; CHECK-NEXT: vand.vi v8, v8, 1
-; CHECK-NEXT: vmsne.vi v8, v8, 0
-; CHECK-NEXT: vmnot.m v8, v8
+; CHECK-NEXT: vmseq.vi v8, v8, 0
; CHECK-NEXT: vcpop.m a0, v8
; CHECK-NEXT: seqz a0, a0
; CHECK-NEXT: ret
@@ -21,8 +20,7 @@ define i1 @reduce_and_icmp_slt_v8i8(<8 x i8> %v) {
; CHECK-LABEL: reduce_and_icmp_slt_v8i8:
; CHECK: # %bb.0:
; CHECK-NEXT: vsetivli zero, 8, e8, mf2, ta, ma
-; CHECK-NEXT: vmsle.vi v8, v8, 4
-; CHECK-NEXT: vmnot.m v8, v8
+; CHECK-NEXT: vmsgt.vi v8, v8, 4
; CHECK-NEXT: vcpop.m a0, v8
; CHECK-NEXT: seqz a0, a0
; CHECK-NEXT: ret
@@ -36,8 +34,7 @@ define <8 x i1> @not_trunc_v8i8(<8 x i8> %v) {
; CHECK: # %bb.0:
; CHECK-NEXT: vsetivli zero, 8, e8, mf2, ta, ma
; CHECK-NEXT: vand.vi v8, v8, 1
-; CHECK-NEXT: vmsne.vi v8, v8, 0
-; CHECK-NEXT: vmnot.m v0, v8
+; CHECK-NEXT: vmseq.vi v0, v8, 0
; CHECK-NEXT: ret
%t = trunc <8 x i8> %v to <8 x i1>
%n = xor <8 x i1> %t, splat (i1 true)
@@ -74,9 +71,7 @@ define <8 x i1> @not_vp_reverse_v8i1(<8 x i1> %v, <8 x i1> %m, i32 zeroext %evl)
; CHECK-NEXT: vrsub.vx v10, v10, a0, v0.t
; CHECK-NEXT: vsetvli zero, zero, e8, mf2, ta, ma
; CHECK-NEXT: vrgatherei16.vv v11, v9, v10, v0.t
-; CHECK-NEXT: vmsne.vi v8, v11, 0, v0.t
-; CHECK-NEXT: vsetivli zero, 8, e8, mf2, ta, ma
-; CHECK-NEXT: vmnot.m v0, v8
+; CHECK-NEXT: vmseq.vi v0, v11, 0, v0.t
; CHECK-NEXT: ret
%r = call <8 x i1> @llvm.experimental.vp.reverse.v8i1(<8 x i1> %v, <8 x i1> %m, i32 %evl)
%n = xor <8 x i1> %r, splat (i1 true)
>From 401e566d51bf43115764c18f8363973c0b23e6c1 Mon Sep 17 00:00:00 2001
From: Marton Moro <moro.marton at gmail.com>
Date: Tue, 15 Sep 2026 09:13:13 +0200
Subject: [PATCH 3/3] [RISCV] Return early when neither operand is VMSET_VL
---
llvm/lib/Target/RISCV/RISCVISelLowering.cpp | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
index 6accbbbcaa9a5..de35e6a1940b2 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -19259,10 +19259,12 @@ static SDValue performORCombine(SDNode *N, TargetLowering::DAGCombinerInfo &DCI,
// but any mask is fine.
static SDValue combineVMNOTOfSetCC(SDNode *N, SelectionDAG &DAG) {
SDValue Cmp = N->getOperand(0);
+ if (Cmp.getOpcode() != RISCVISD::VMSET_VL &&
+ N->getOperand(1).getOpcode() != RISCVISD::VMSET_VL)
+ return SDValue();
+
if (Cmp.getOpcode() == RISCVISD::VMSET_VL)
Cmp = N->getOperand(1);
- else if (N->getOperand(1).getOpcode() != RISCVISD::VMSET_VL)
- return SDValue();
if (Cmp.getOpcode() != RISCVISD::SETCC_VL || !Cmp.hasOneUse() ||
!Cmp.getOperand(0).getValueType().isInteger() ||
More information about the llvm-commits
mailing list