[llvm] [RISCV] Avoid VMNOT by swapping VMERGE operands for mask extensions (PR #126751)

Piotr Fusik via llvm-commits llvm-commits at lists.llvm.org
Wed Feb 19 23:19:50 PST 2025


https://github.com/pfusik updated https://github.com/llvm/llvm-project/pull/126751

>From 6ce68443da480ede43aa5134c6a5b1f302aa5580 Mon Sep 17 00:00:00 2001
From: Piotr Fusik <p.fusik at samsung.com>
Date: Mon, 17 Feb 2025 12:08:24 +0100
Subject: [PATCH 1/2] [RISCV][test] Add tests for extending negated mask

---
 llvm/test/CodeGen/RISCV/rvv/mask-exts-not.ll | 111 +++++++++++++++++++
 1 file changed, 111 insertions(+)
 create mode 100644 llvm/test/CodeGen/RISCV/rvv/mask-exts-not.ll

diff --git a/llvm/test/CodeGen/RISCV/rvv/mask-exts-not.ll b/llvm/test/CodeGen/RISCV/rvv/mask-exts-not.ll
new file mode 100644
index 0000000000000..1a00c84a022f3
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/rvv/mask-exts-not.ll
@@ -0,0 +1,111 @@
+; 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 <vscale x 8 x i8> @mask_sext_not_nxv8i8(<vscale x 8 x i1> %m) {
+; CHECK-LABEL: mask_sext_not_nxv8i8:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    vsetvli a0, zero, e8, m1, ta, ma
+; CHECK-NEXT:    vmnot.m v0, v0
+; CHECK-NEXT:    vmv.v.i v8, 0
+; CHECK-NEXT:    vmerge.vim v8, v8, -1, v0
+; CHECK-NEXT:    ret
+  %not = xor <vscale x 8 x i1> %m, splat (i1 true)
+  %ext = sext <vscale x 8 x i1> %not to <vscale x 8 x i8>
+  ret <vscale x 8 x i8> %ext
+}
+
+define <vscale x 8 x i8> @mask_zext_not_nxv8i8(<vscale x 8 x i1> %m) {
+; CHECK-LABEL: mask_zext_not_nxv8i8:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    vsetvli a0, zero, e8, m1, ta, ma
+; CHECK-NEXT:    vmnot.m v0, v0
+; CHECK-NEXT:    vmv.v.i v8, 0
+; CHECK-NEXT:    vmerge.vim v8, v8, 1, v0
+; CHECK-NEXT:    ret
+  %not = xor <vscale x 8 x i1> %m, splat (i1 true)
+  %ext = zext <vscale x 8 x i1> %not to <vscale x 8 x i8>
+  ret <vscale x 8 x i8> %ext
+}
+
+define <8 x i8> @mask_sext_not_v8i8(<8 x i1> %m) {
+; CHECK-LABEL: mask_sext_not_v8i8:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    vsetivli zero, 8, e8, mf2, ta, ma
+; CHECK-NEXT:    vmnot.m v0, v0
+; CHECK-NEXT:    vmv.v.i v8, 0
+; CHECK-NEXT:    vmerge.vim v8, v8, -1, v0
+; CHECK-NEXT:    ret
+  %not = xor <8 x i1> %m, splat (i1 true)
+  %ext = sext <8 x i1> %not to <8 x i8>
+  ret <8 x i8> %ext
+}
+
+define <8 x i8> @mask_zext_not_v8i8(<8 x i1> %m) {
+; CHECK-LABEL: mask_zext_not_v8i8:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    vsetivli zero, 8, e8, mf2, ta, ma
+; CHECK-NEXT:    vmnot.m v0, v0
+; CHECK-NEXT:    vmv.v.i v8, 0
+; CHECK-NEXT:    vmerge.vim v8, v8, 1, v0
+; CHECK-NEXT:    ret
+  %not = xor <8 x i1> %m, splat (i1 true)
+  %ext = zext <8 x i1> %not to <8 x i8>
+  ret <8 x i8> %ext
+}
+
+define <vscale x 8 x i8> @mask_sext_xor_nxv8i8(<vscale x 8 x i1> %m, <vscale x 8 x i1> %x) {
+; CHECK-LABEL: mask_sext_xor_nxv8i8:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    vsetvli a0, zero, e8, m1, ta, ma
+; CHECK-NEXT:    vmxor.mm v0, v0, v8
+; CHECK-NEXT:    vmv.v.i v8, 0
+; CHECK-NEXT:    vmerge.vim v8, v8, -1, v0
+; CHECK-NEXT:    ret
+  %xor = xor <vscale x 8 x i1> %m, %x
+  %ext = sext <vscale x 8 x i1> %xor to <vscale x 8 x i8>
+  ret <vscale x 8 x i8> %ext
+}
+
+define <vscale x 8 x i8> @mask_zext_xor_nxv8i8(<vscale x 8 x i1> %m, <vscale x 8 x i1> %x) {
+; CHECK-LABEL: mask_zext_xor_nxv8i8:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    vsetvli a0, zero, e8, m1, ta, ma
+; CHECK-NEXT:    vmxor.mm v0, v0, v8
+; CHECK-NEXT:    vmv.v.i v8, 0
+; CHECK-NEXT:    vmerge.vim v8, v8, 1, v0
+; CHECK-NEXT:    ret
+  %xor = xor <vscale x 8 x i1> %m, %x
+  %ext = zext <vscale x 8 x i1> %xor to <vscale x 8 x i8>
+  ret <vscale x 8 x i8> %ext
+}
+
+define <8 x i8> @mask_sext_xor_v8i8(<8 x i1> %m) {
+; CHECK-LABEL: mask_sext_xor_v8i8:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    li a0, 85
+; CHECK-NEXT:    vsetivli zero, 8, e8, mf2, ta, ma
+; CHECK-NEXT:    vmv.s.x v8, a0
+; CHECK-NEXT:    vmxor.mm v0, v0, v8
+; CHECK-NEXT:    vmv.v.i v8, 0
+; CHECK-NEXT:    vmerge.vim v8, v8, -1, v0
+; CHECK-NEXT:    ret
+  %xor = xor <8 x i1> %m, <i1 1, i1 0, i1 1, i1 0, i1 1, i1 0, i1 1, i1 0>
+  %ext = sext <8 x i1> %xor to <8 x i8>
+  ret <8 x i8> %ext
+}
+
+define <8 x i8> @mask_zext_xor_v8i8(<8 x i1> %m) {
+; CHECK-LABEL: mask_zext_xor_v8i8:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    li a0, 85
+; CHECK-NEXT:    vsetivli zero, 8, e8, mf2, ta, ma
+; CHECK-NEXT:    vmv.s.x v8, a0
+; CHECK-NEXT:    vmxor.mm v0, v0, v8
+; CHECK-NEXT:    vmv.v.i v8, 0
+; CHECK-NEXT:    vmerge.vim v8, v8, 1, v0
+; CHECK-NEXT:    ret
+  %xor = xor <8 x i1> %m, <i1 1, i1 0, i1 1, i1 0, i1 1, i1 0, i1 1, i1 0>
+  %ext = zext <8 x i1> %xor to <8 x i8>
+  ret <8 x i8> %ext
+}

>From e8f700e88f209601f8150b822e48fd5cca52d828 Mon Sep 17 00:00:00 2001
From: Piotr Fusik <p.fusik at samsung.com>
Date: Mon, 17 Feb 2025 12:31:02 +0100
Subject: [PATCH 2/2] [RISCV] Avoid VMNOT by swapping VMERGE operands for mask
 extensions

Fold:

    (select (not m),  1, 0) -> (select m, 0,  1)
    (select (not m), -1, 0) -> (select m, 0, -1)
---
 llvm/lib/Target/RISCV/RISCVISelLowering.cpp  | 18 ++++++++++++++++++
 llvm/test/CodeGen/RISCV/rvv/mask-exts-not.ll | 20 ++++++++------------
 2 files changed, 26 insertions(+), 12 deletions(-)

diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
index 0f5e7bd254f68..cc2478b581741 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -8961,6 +8961,10 @@ SDValue RISCVTargetLowering::lowerVectorMaskExt(SDValue Op, SelectionDAG &DAG,
   if (VecVT.isScalableVector()) {
     SDValue SplatZero = DAG.getConstant(0, DL, VecVT);
     SDValue SplatTrueVal = DAG.getSignedConstant(ExtTrueVal, DL, VecVT);
+    if (Src.getOpcode() == ISD::XOR &&
+        ISD::isConstantSplatVectorAllOnes(Src.getOperand(1).getNode()))
+      return DAG.getNode(ISD::VSELECT, DL, VecVT, Src.getOperand(0), SplatZero,
+                         SplatTrueVal);
     return DAG.getNode(ISD::VSELECT, DL, VecVT, Src, SplatTrueVal, SplatZero);
   }
 
@@ -8976,6 +8980,20 @@ SDValue RISCVTargetLowering::lowerVectorMaskExt(SDValue Op, SelectionDAG &DAG,
   SDValue SplatZero = DAG.getConstant(0, DL, XLenVT);
   SDValue SplatTrueVal = DAG.getSignedConstant(ExtTrueVal, DL, XLenVT);
 
+  if (Src.getOpcode() == ISD::EXTRACT_SUBVECTOR) {
+    SDValue Xor = Src.getOperand(0);
+    if (Xor.getOpcode() == RISCVISD::VMXOR_VL) {
+      SDValue ScalableOnes = Xor.getOperand(1);
+      if (ScalableOnes.getOpcode() == ISD::INSERT_SUBVECTOR &&
+          ScalableOnes.getOperand(0).isUndef() &&
+          ISD::isConstantSplatVectorAllOnes(
+              ScalableOnes.getOperand(1).getNode())) {
+        CC = Xor.getOperand(0);
+        std::swap(SplatZero, SplatTrueVal);
+      }
+    }
+  }
+
   SplatZero = DAG.getNode(RISCVISD::VMV_V_X_VL, DL, ContainerVT,
                           DAG.getUNDEF(ContainerVT), SplatZero, VL);
   SplatTrueVal = DAG.getNode(RISCVISD::VMV_V_X_VL, DL, ContainerVT,
diff --git a/llvm/test/CodeGen/RISCV/rvv/mask-exts-not.ll b/llvm/test/CodeGen/RISCV/rvv/mask-exts-not.ll
index 1a00c84a022f3..ce33a3b5e197e 100644
--- a/llvm/test/CodeGen/RISCV/rvv/mask-exts-not.ll
+++ b/llvm/test/CodeGen/RISCV/rvv/mask-exts-not.ll
@@ -6,9 +6,8 @@ define <vscale x 8 x i8> @mask_sext_not_nxv8i8(<vscale x 8 x i1> %m) {
 ; CHECK-LABEL: mask_sext_not_nxv8i8:
 ; CHECK:       # %bb.0:
 ; CHECK-NEXT:    vsetvli a0, zero, e8, m1, ta, ma
-; CHECK-NEXT:    vmnot.m v0, v0
-; CHECK-NEXT:    vmv.v.i v8, 0
-; CHECK-NEXT:    vmerge.vim v8, v8, -1, v0
+; CHECK-NEXT:    vmv.v.i v8, -1
+; CHECK-NEXT:    vmerge.vim v8, v8, 0, v0
 ; CHECK-NEXT:    ret
   %not = xor <vscale x 8 x i1> %m, splat (i1 true)
   %ext = sext <vscale x 8 x i1> %not to <vscale x 8 x i8>
@@ -19,9 +18,8 @@ define <vscale x 8 x i8> @mask_zext_not_nxv8i8(<vscale x 8 x i1> %m) {
 ; CHECK-LABEL: mask_zext_not_nxv8i8:
 ; CHECK:       # %bb.0:
 ; CHECK-NEXT:    vsetvli a0, zero, e8, m1, ta, ma
-; CHECK-NEXT:    vmnot.m v0, v0
-; CHECK-NEXT:    vmv.v.i v8, 0
-; CHECK-NEXT:    vmerge.vim v8, v8, 1, v0
+; CHECK-NEXT:    vmv.v.i v8, 1
+; CHECK-NEXT:    vmerge.vim v8, v8, 0, v0
 ; CHECK-NEXT:    ret
   %not = xor <vscale x 8 x i1> %m, splat (i1 true)
   %ext = zext <vscale x 8 x i1> %not to <vscale x 8 x i8>
@@ -32,9 +30,8 @@ define <8 x i8> @mask_sext_not_v8i8(<8 x i1> %m) {
 ; CHECK-LABEL: mask_sext_not_v8i8:
 ; CHECK:       # %bb.0:
 ; CHECK-NEXT:    vsetivli zero, 8, e8, mf2, ta, ma
-; CHECK-NEXT:    vmnot.m v0, v0
-; CHECK-NEXT:    vmv.v.i v8, 0
-; CHECK-NEXT:    vmerge.vim v8, v8, -1, v0
+; CHECK-NEXT:    vmv.v.i v8, -1
+; CHECK-NEXT:    vmerge.vim v8, v8, 0, v0
 ; CHECK-NEXT:    ret
   %not = xor <8 x i1> %m, splat (i1 true)
   %ext = sext <8 x i1> %not to <8 x i8>
@@ -45,9 +42,8 @@ define <8 x i8> @mask_zext_not_v8i8(<8 x i1> %m) {
 ; CHECK-LABEL: mask_zext_not_v8i8:
 ; CHECK:       # %bb.0:
 ; CHECK-NEXT:    vsetivli zero, 8, e8, mf2, ta, ma
-; CHECK-NEXT:    vmnot.m v0, v0
-; CHECK-NEXT:    vmv.v.i v8, 0
-; CHECK-NEXT:    vmerge.vim v8, v8, 1, v0
+; CHECK-NEXT:    vmv.v.i v8, 1
+; CHECK-NEXT:    vmerge.vim v8, v8, 0, v0
 ; CHECK-NEXT:    ret
   %not = xor <8 x i1> %m, splat (i1 true)
   %ext = zext <8 x i1> %not to <8 x i8>



More information about the llvm-commits mailing list