[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
Thu Sep 10 01:29:10 PDT 2026


https://github.com/martonmoro created https://github.com/llvm/llvm-project/pull/222553

Fold a mask NOT of an integer vector compare into the compare by inverting
the condition code, so `vmsne.vi` + `vmnot.m` becomes `vmseq.vi`:

    (vmxor_vl (setcc_vl a, b, cc), vmset_vl) -> (setcc_vl a, b, !cc)

Only `SETCC_VL` with integer operands, one use, undef passthru and an
all-ones mask is handled. FP compares are excluded since the inverse of an
ordered compare is unordered.

Fixes #222158

>From 5fab21f6d66eeee83376e8b7cd087814658e7842 Mon Sep 17 00:00:00 2001
From: Marton Moro <moro.marton at gmail.com>
Date: Thu, 10 Sep 2026 10:26:41 +0200
Subject: [PATCH] [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   | 24 +++++++
 .../CodeGen/RISCV/rvv/combine-vmnot-setcc.ll  | 66 +++++++++++++++++++
 2 files changed, 90 insertions(+)
 create mode 100644 llvm/test/CodeGen/RISCV/rvv/combine-vmnot-setcc.ll

diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
index 5f8ad5da42da1..cb7a9b917d4e5 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -19253,6 +19253,28 @@ 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.
+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() ||
+      Cmp.getOperand(4).getOpcode() != RISCVISD::VMSET_VL)
+    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 +25282,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
new file mode 100644
index 0000000000000..6bef503bf6f1c
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/rvv/combine-vmnot-setcc.ll
@@ -0,0 +1,66 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc -mtriple=riscv32 -mattr=+v -verify-machineinstrs < %s | FileCheck %s
+; RUN: llc -mtriple=riscv64 -mattr=+v -verify-machineinstrs < %s | FileCheck %s
+
+define i1 @reduce_and_trunc_v8i8(ptr %p) {
+; CHECK-LABEL: reduce_and_trunc_v8i8:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    vsetivli zero, 8, e8, mf2, ta, ma
+; CHECK-NEXT:    vle8.v v8, (a0)
+; CHECK-NEXT:    vand.vi v8, v8, 1
+; CHECK-NEXT:    vmseq.vi v8, v8, 0
+; CHECK-NEXT:    vcpop.m a0, v8
+; CHECK-NEXT:    seqz a0, a0
+; CHECK-NEXT:    ret
+  %v = load <8 x i8>, ptr %p
+  %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(ptr %p) {
+; CHECK-LABEL: reduce_and_icmp_slt_v8i8:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    vsetivli zero, 8, e8, mf2, ta, ma
+; CHECK-NEXT:    vle8.v v8, (a0)
+; CHECK-NEXT:    vmsgt.vi v8, v8, 4
+; CHECK-NEXT:    vcpop.m a0, v8
+; CHECK-NEXT:    seqz a0, a0
+; CHECK-NEXT:    ret
+  %v = load <8 x i8>, ptr %p
+  %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(ptr %p) {
+; CHECK-LABEL: not_trunc_v8i8:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    vsetivli zero, 8, e8, mf2, ta, ma
+; CHECK-NEXT:    vle8.v v8, (a0)
+; CHECK-NEXT:    vand.vi v8, v8, 1
+; CHECK-NEXT:    vmseq.vi v0, v8, 0
+; CHECK-NEXT:    ret
+  %v = load <8 x i8>, ptr %p
+  %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(ptr %p) {
+; 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:    vle32.v v8, (a0)
+; 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
+  %v = load <8 x float>, ptr %p
+  %c = fcmp olt <8 x float> %v, zeroinitializer
+  %r = call i1 @llvm.vector.reduce.and.v8i1(<8 x i1> %c)
+  ret i1 %r
+}



More information about the llvm-commits mailing list