[llvm] [RISCV] Add ComplexPatterns for matching xor/vmnot_vl+vmset_vl. NFC (PR #182071)
Craig Topper via llvm-commits
llvm-commits at lists.llvm.org
Wed Feb 18 09:31:03 PST 2026
https://github.com/topperc created https://github.com/llvm/llvm-project/pull/182071
Xor is commutable and we don't guarantee which operand will be the vmset_vl. Tablegen will generate all possible permutations when creating RISCVGenDAGISel.inc. These xor/vmnot_vl are used by other commutable nodes leading to quite a few patterns being generated by tablegen.
By using a ComplexPattern we can handle both cases with one piece of C++ code. This reduces the isel table by 2-3k.
>From 17f15124575df15c886fb60ff566c80f888d2bd4 Mon Sep 17 00:00:00 2001
From: Craig Topper <craig.topper at sifive.com>
Date: Tue, 17 Feb 2026 22:31:24 -0800
Subject: [PATCH] [RISCV] Add ComplexPatterns for matching
xor/vmnot_vl+vmset_vl. NFC
Xor is commutable and we don't guarantee which operand will be the vmset_vl.
Tablegen will generate all possible permutations when creating
RISCVGenDAGISel.inc. These xor/vmnot_vl are used by other commutable
nodes leading to quite a few patterns being generated by tablegen.
By using a ComplexPattern we can handle both cases with one piece
of C++ code. This reduces the isel table by 2-3k.
---
llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp | 48 +++++++++++++++++++
llvm/lib/Target/RISCV/RISCVISelDAGToDAG.h | 3 ++
.../Target/RISCV/RISCVInstrInfoVSDPatterns.td | 11 +++--
.../Target/RISCV/RISCVInstrInfoVVLPatterns.td | 18 ++++---
4 files changed, 70 insertions(+), 10 deletions(-)
diff --git a/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp b/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp
index bf5057bdc50fb..f25c4b5648dac 100644
--- a/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp
@@ -4565,6 +4565,54 @@ bool RISCVDAGToDAGISel::selectRVVSimm5(SDValue N, unsigned Width,
return false;
}
+// Match XOR with a VMSET_VL operand. REturn the other operand.
+bool RISCVDAGToDAGISel::selectVMNOTOp(SDValue N, SDValue &Res) {
+ if (N.getOpcode() != ISD::XOR)
+ return false;
+
+ if (N.getOperand(0).getOpcode() == RISCVISD::VMSET_VL) {
+ Res = N.getOperand(1);
+ return true;
+ }
+
+ if (N.getOperand(1).getOpcode() == RISCVISD::VMSET_VL) {
+ Res = N.getOperand(0);
+ return true;
+ }
+
+ return false;
+}
+
+// Match VMXOR_VL with a VMSET_VL operand. Making sure that that VL operand
+// matches the parent's VL. Return the other operand of the VMXOR_VL.
+bool RISCVDAGToDAGISel::selectVMNOT_VLOp(SDNode *Parent, SDValue N,
+ SDValue &Res) {
+ if (N.getOpcode() != RISCVISD::VMXOR_VL)
+ return false;
+
+ assert(Parent &&
+ (Parent->getOpcode() == RISCVISD::VMAND_VL ||
+ Parent->getOpcode() == RISCVISD::VMOR_VL ||
+ Parent->getOpcode() == RISCVISD::VMXOR_VL) &&
+ "Unexpected parent");
+
+ // The VL should match the parent.
+ if (Parent->getOperand(2) != N->getOperand(2))
+ return false;
+
+ if (N.getOperand(0).getOpcode() == RISCVISD::VMSET_VL) {
+ Res = N.getOperand(1);
+ return true;
+ }
+
+ if (N.getOperand(1).getOpcode() == RISCVISD::VMSET_VL) {
+ Res = N.getOperand(0);
+ return true;
+ }
+
+ return false;
+}
+
// Try to remove sext.w if the input is a W instruction or can be made into
// a W instruction cheaply.
bool RISCVDAGToDAGISel::doPeepholeSExtW(SDNode *N) {
diff --git a/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.h b/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.h
index 1d59dc57c4135..8cb4effef582c 100644
--- a/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.h
+++ b/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.h
@@ -156,6 +156,9 @@ class RISCVDAGToDAGISel : public SelectionDAGISel {
return selectRVVSimm5(N, Width, Imm);
}
+ bool selectVMNOTOp(SDValue N, SDValue &Res);
+ bool selectVMNOT_VLOp(SDNode *Parent, SDValue N, SDValue &Res);
+
void addVectorLoadStoreOperands(SDNode *Node, unsigned SEWImm,
const SDLoc &DL, unsigned CurOp,
bool IsMasked, bool IsStridedOrIndexed,
diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfoVSDPatterns.td b/llvm/lib/Target/RISCV/RISCVInstrInfoVSDPatterns.td
index a469d7a04ec36..23951fa4c6d19 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrInfoVSDPatterns.td
+++ b/llvm/lib/Target/RISCV/RISCVInstrInfoVSDPatterns.td
@@ -24,6 +24,11 @@
def rvv_vnot : PatFrag<(ops node:$in),
(xor node:$in, (riscv_vmset_vl (XLenVT srcvalue)))>;
+// Match xor+riscv_vmset_vl as an operand. By handling as a ComplexPattern we
+// can check both operands of the xor without needing tablegen to emit
+// multiple isel patterns.
+def rvv_vnot_op : ComplexPattern<vAny, 1, "selectVMNOTOp", [xor], [], 3>;
+
multiclass VPatUSLoadStoreSDNode<ValueType type,
RegisterClass regclass,
int log2sew,
@@ -1203,15 +1208,15 @@ foreach mti = AllMasks in {
(!cast<Instruction>("PseudoVMXNOR_MM_"#mti.BX)
VR:$rs1, VR:$rs2, mti.AVL, mti.Log2SEW)>;
- def : Pat<(mti.Mask (and VR:$rs1, (rvv_vnot VR:$rs2))),
+ def : Pat<(mti.Mask (and VR:$rs1, rvv_vnot_op:$rs2)),
(!cast<Instruction>("PseudoVMANDN_MM_"#mti.BX)
VR:$rs1, VR:$rs2, mti.AVL, mti.Log2SEW)>;
- def : Pat<(mti.Mask (or VR:$rs1, (rvv_vnot VR:$rs2))),
+ def : Pat<(mti.Mask (or VR:$rs1, rvv_vnot_op:$rs2)),
(!cast<Instruction>("PseudoVMORN_MM_"#mti.BX)
VR:$rs1, VR:$rs2, mti.AVL, mti.Log2SEW)>;
// Handle rvv_vnot the same as the vmnot.m pseudoinstruction.
- def : Pat<(mti.Mask (rvv_vnot VR:$rs)),
+ def : Pat<(mti.Mask rvv_vnot_op:$rs),
(!cast<Instruction>("PseudoVMNAND_MM_"#mti.BX)
VR:$rs, VR:$rs, mti.AVL, mti.Log2SEW)>;
}
diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfoVVLPatterns.td b/llvm/lib/Target/RISCV/RISCVInstrInfoVVLPatterns.td
index d1bcaffdeac5b..0d5ba476cc88a 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrInfoVVLPatterns.td
+++ b/llvm/lib/Target/RISCV/RISCVInstrInfoVVLPatterns.td
@@ -493,6 +493,13 @@ def true_mask : PatLeaf<(riscv_vmset_vl (XLenVT srcvalue))>;
def riscv_vmnot_vl : PatFrag<(ops node:$rs, node:$vl),
(riscv_vmxor_vl node:$rs, true_mask, node:$vl)>;
+// Match riscv_vmxor_vl+riscv_vmset_vl as an operand. By handling as a
+// ComplexPattern we can check both operands of the vmxor_vl without needing
+// tablegen to emit multiple isel patterns.
+def riscv_vmnot_vl_op : ComplexPattern<vAny, 1, "selectVMNOT_VLOp", [], [], 9> {
+ let WantsParent = true;
+}
+
let HasMaskOp = true in {
// vcpop.m with additional mask and VL operands.
def riscv_vcpop_vl : RVSDNode<"VCPOP_VL",
@@ -2828,20 +2835,17 @@ foreach mti = AllMasks in {
(!cast<Instruction>("PseudoVMXOR_MM_" # mti.BX)
VR:$rs1, VR:$rs2, GPR:$vl, mti.Log2SEW)>;
- def : Pat<(mti.Mask (riscv_vmand_vl VR:$rs1,
- (riscv_vmnot_vl VR:$rs2, VLOpFrag),
+ def : Pat<(mti.Mask (riscv_vmand_vl VR:$rs1, riscv_vmnot_vl_op:$rs2,
VLOpFrag)),
(!cast<Instruction>("PseudoVMANDN_MM_" # mti.BX)
VR:$rs1, VR:$rs2, GPR:$vl, mti.Log2SEW)>;
- def : Pat<(mti.Mask (riscv_vmor_vl VR:$rs1,
- (riscv_vmnot_vl VR:$rs2, VLOpFrag),
+ def : Pat<(mti.Mask (riscv_vmor_vl VR:$rs1, riscv_vmnot_vl_op:$rs2,
VLOpFrag)),
(!cast<Instruction>("PseudoVMORN_MM_" # mti.BX)
VR:$rs1, VR:$rs2, GPR:$vl, mti.Log2SEW)>;
// XOR is associative so we need 2 patterns for VMXNOR.
- def : Pat<(mti.Mask (riscv_vmxor_vl (riscv_vmnot_vl VR:$rs1,
- VLOpFrag),
- VR:$rs2, VLOpFrag)),
+ def : Pat<(mti.Mask (riscv_vmxor_vl riscv_vmnot_vl_op:$rs1,
+ VR:$rs2, VLOpFrag)),
(!cast<Instruction>("PseudoVMXNOR_MM_" # mti.BX)
VR:$rs1, VR:$rs2, GPR:$vl, mti.Log2SEW)>;
More information about the llvm-commits
mailing list