Skip to content

Conversation

lenary
Copy link
Member

@lenary lenary commented Aug 12, 2025

The original patterns for the Xqci select-like instructions used select, and marked that ISD node as legal. This is not the usual way that select is dealt with in the RISC-V backend.

Usually on RISC-V, we expand select to riscv_select_cc which holds references to the operands of the comparison and the possible values depending on the comparison. In retrospect, this is a much better fit for our instructions, as most of them correspond to specific condition codes, rather than more generic select with a truthy/falsey value.

This PR moves the Xqci select-like patterns to use riscv_select_cc nodes. This applies to the Xqcicm, Xqcics and Xqcicli instruction patterns.

In order to match the existing codegen, minor additions had to be made to translateSetCCForBranch to ensure that comparisons against specific immediate values are left in a form that can be matched more closely by the instructions. This prevents having to insert additional li instructions and use the register forms.

There are a few slight regressions:

  • There are sometimes more mv instructions than entirely necessary. I believe these would not be seen with larger examples where the register allocator has more leeway.
  • In some tests where just one of the three extensions is enabled, codegen falls back to using a branch over a move. With all three extensions enabled (the configuration we most care about), these are not seen.
  • The generated patterns are very similar to each other - they have similar complexity (7 or 8) and there are still overlaps. Sometimes the choice between two instructions can be affected by the order of the patterns in the tablegen file.

One other change is that Xqcicm instructions are prioritised over Xqcics instructions where they have identical patterns. This is done because one of the the Xqcicm instructions is compressible (qc.mveqi), while none of the Xqcics instructions are.

Copy link

github-actions bot commented Aug 12, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

@lenary lenary changed the title [WIP][RISCV] Move patterns to riscv_selectcc [RISCV] Move Xqci Select-likes to use riscv_selectcc Aug 15, 2025
@lenary lenary marked this pull request as ready for review August 15, 2025 01:50
@llvmbot
Copy link
Member

llvmbot commented Aug 15, 2025

@llvm/pr-subscribers-backend-risc-v

Author: Sam Elliott (lenary)

Changes

The original patterns for the Xqci select-like instructions used select, and marked that ISD node as legal. This is not the usual way that select is dealt with in the RISC-V backend.

Usually on RISC-V, we expand select to riscv_select_cc which holds references to the operands of the comparison and the possible values depending on the comparison. In retrospect, this is a much better fit for our instructions, as most of them correspond to specific condition codes, rather than more generic select with a truthy/falsey value.

This PR moves the Xqci select-like patterns to use riscv_select_cc nodes. This applies to the Xqcicm, Xqcics and Xqcicli instruction patterns.

In order to match the existing codegen, minor additions had to be made to translateSetCCForBranch to ensure that comparisons against specific immediate values are left in a form that can be matched more closely by the instructions. This prevents having to insert additional li instructions and use the register forms.

There are a few slight regressions:

  • There are sometimes more mv instructions than entirely necessary. I believe these would not be seen with larger examples where the register allocator has more leeway.
  • In some tests where just one of the three extensions is enabled, codegen falls back to using a branch over a move. With all three extensions enabled (the configuration we most care about), these are not seen.
  • The generated patterns are very similar to each other - they have similar complexity (7 or 8) and there are still overlaps. Sometimes the choice between two instructions can be affected by the order of the patterns in the tablegen file.

Patch is 128.95 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/153147.diff

11 Files Affected:

  • (modified) llvm/lib/Target/RISCV/RISCVFeatures.td (-3)
  • (modified) llvm/lib/Target/RISCV/RISCVISelLowering.cpp (+17-2)
  • (modified) llvm/lib/Target/RISCV/RISCVInstrInfoXqci.td (+82-68)
  • (modified) llvm/test/CodeGen/RISCV/select-bare.ll (+14)
  • (modified) llvm/test/CodeGen/RISCV/select-cc.ll (+76)
  • (modified) llvm/test/CodeGen/RISCV/select-cond.ll (+353-110)
  • (modified) llvm/test/CodeGen/RISCV/select-const.ll (+138)
  • (modified) llvm/test/CodeGen/RISCV/select.ll (+418)
  • (modified) llvm/test/CodeGen/RISCV/xqcicli.ll (+183-3)
  • (modified) llvm/test/CodeGen/RISCV/xqcicm.ll (+217-26)
  • (modified) llvm/test/CodeGen/RISCV/xqcics.ll (+123)
diff --git a/llvm/lib/Target/RISCV/RISCVFeatures.td b/llvm/lib/Target/RISCV/RISCVFeatures.td
index a7329d201f880..0f8956e524c1b 100644
--- a/llvm/lib/Target/RISCV/RISCVFeatures.td
+++ b/llvm/lib/Target/RISCV/RISCVFeatures.td
@@ -1488,9 +1488,6 @@ def HasVendorXqcics
 def NoVendorXqcics
     : Predicate<"!Subtarget->hasVendorXqcics()">;
 
-def HasVendorXqcicsOrXqcicm
-    : Predicate<"Subtarget->hasVendorXqcics() || Subtarget->hasVendorXqcicm()">;
-
 def FeatureVendorXqcicsr
     : RISCVExperimentalExtension<0, 4, "Qualcomm uC CSR Extension">;
 def HasVendorXqcicsr
diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
index 57ca687df5f6c..0e0193326d94a 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -436,8 +436,7 @@ RISCVTargetLowering::RISCVTargetLowering(const TargetMachine &TM,
     setOperationAction(ISD::ABS, MVT::i32, Custom);
   }
 
-  if (!Subtarget.useCCMovInsn() && !Subtarget.hasVendorXTHeadCondMov() &&
-      !Subtarget.hasVendorXqcicm() && !Subtarget.hasVendorXqcics())
+  if (!Subtarget.useCCMovInsn() && !Subtarget.hasVendorXTHeadCondMov())
     setOperationAction(ISD::SELECT, XLenVT, Custom);
 
   if (Subtarget.hasVendorXqcia() && !Subtarget.is64Bit()) {
@@ -2505,6 +2504,14 @@ static void translateSetCCForBranch(const SDLoc &DL, SDValue &LHS, SDValue &RHS,
         CC = ISD::SETGE;
         return;
       }
+      if ((Subtarget.hasVendorXqcicm() || Subtarget.hasVendorXqcicli()) &&
+          C != INT64_MAX && isInt<5>(C + 1)) {
+        // We have a conditional move instruction for SETGE but not SETGT.
+        // Convert X > C to X >= C + 1, if (C + 1) is a 5-bit signed immediate.
+        RHS = DAG.getSignedConstant(C + 1, DL, RHS.getValueType());
+        CC = ISD::SETGE;
+        return;
+      }
       if (Subtarget.hasVendorXqcibi() && C != INT64_MAX && isInt<16>(C + 1)) {
         // We have a branch immediate instruction for SETGE but not SETGT.
         // Convert X > C to X >= C + 1, if (C + 1) is a 16-bit signed immediate.
@@ -2523,6 +2530,14 @@ static void translateSetCCForBranch(const SDLoc &DL, SDValue &LHS, SDValue &RHS,
       }
       break;
     case ISD::SETUGT:
+      if ((Subtarget.hasVendorXqcicm() || Subtarget.hasVendorXqcicli()) &&
+          C != INT64_MAX && isUInt<5>(C + 1)) {
+        // We have a conditional move instruction for SETUGE but not SETUGT.
+        // Convert X > C to X >= C + 1, if (C + 1) is a 5-bit signed immediate.
+        RHS = DAG.getConstant(C + 1, DL, RHS.getValueType());
+        CC = ISD::SETUGE;
+        return;
+      }
       if (Subtarget.hasVendorXqcibi() && C != INT64_MAX && isUInt<16>(C + 1)) {
         // We have a branch immediate instruction for SETUGE but not SETUGT.
         // Convert X > C to X >= C + 1, if (C + 1) is a 16-bit unsigned
diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfoXqci.td b/llvm/lib/Target/RISCV/RISCVInstrInfoXqci.td
index 2c64b0c220fba..f3f98ae333028 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrInfoXqci.td
+++ b/llvm/lib/Target/RISCV/RISCVInstrInfoXqci.td
@@ -1334,51 +1334,51 @@ class QCScaledStPat<PatFrag StoreOp, RVInst Inst>
           (Inst GPR:$rd, GPRMem:$rs1, GPRNoX0:$rs2, uimm3:$shamt)>;
 
 class QCIMVCCPat<CondCode Cond, QCIMVCC Inst>
-    : Pat<(select (i32 (setcc (i32 GPRNoX0:$rs1), (i32 GPRNoX0:$rs2), Cond)), (i32 GPRNoX0:$rs3), (i32 GPRNoX0:$rd)),
+    : Pat<(i32 (riscv_selectcc (i32 GPRNoX0:$rs1), (i32 GPRNoX0:$rs2), Cond, (i32 GPRNoX0:$rs3), (i32 GPRNoX0:$rd))),
           (Inst GPRNoX0:$rd, GPRNoX0:$rs1, GPRNoX0:$rs2, GPRNoX0:$rs3)>;
 
 class QCIMVCCIPat<CondCode Cond, QCIMVCCI Inst, DAGOperand InTyImm>
-    : Pat<(select (i32 (setcc (i32 GPRNoX0:$rs1), InTyImm:$imm, Cond)), (i32 GPRNoX0:$rs3), (i32 GPRNoX0:$rd)),
+    : Pat<(i32 (riscv_selectcc (i32 GPRNoX0:$rs1), InTyImm:$imm, Cond, (i32 GPRNoX0:$rs3), (i32 GPRNoX0:$rd))),
           (Inst GPRNoX0:$rd, GPRNoX0:$rs1, InTyImm:$imm, GPRNoX0:$rs3)>;
 
 class QCISELECTCCIPat<CondCode Cond, QCISELECTCCI Inst>
-    : Pat<(select (i32 (setcc (i32 GPRNoX0:$rd), simm5:$imm, Cond)), (i32 GPRNoX0:$rs2), (i32 GPRNoX0:$rs3)),
+    : Pat<(i32 (riscv_selectcc (i32 GPRNoX0:$rd), simm5:$imm, Cond, (i32 GPRNoX0:$rs2), (i32 GPRNoX0:$rs3))),
           (Inst GPRNoX0:$rd, simm5:$imm, GPRNoX0:$rs2, GPRNoX0:$rs3)>;
 
 class QCISELECTICCIPat<CondCode Cond, QCISELECTICCI Inst>
-    : Pat<(select (i32 (setcc (i32 GPRNoX0:$rd), simm5:$imm, Cond)), (i32 GPRNoX0:$rs2), simm5:$simm2),
+    : Pat<(i32 (riscv_selectcc (i32 GPRNoX0:$rd), simm5:$imm, Cond, (i32 GPRNoX0:$rs2), simm5:$simm2)),
           (Inst GPRNoX0:$rd, simm5:$imm, GPRNoX0:$rs2, simm5:$simm2)>;
 
 class QCISELECTICCIPatInv<CondCode Cond, QCISELECTICCI Inst>
-    : Pat<(select (i32 (setcc (i32 GPRNoX0:$rd), simm5:$imm, Cond)), simm5:$simm2, (i32 GPRNoX0:$rs2)),
+    : Pat<(i32 (riscv_selectcc (i32 GPRNoX0:$rd), simm5:$imm, Cond, simm5:$simm2, (i32 GPRNoX0:$rs2))),
           (Inst GPRNoX0:$rd, simm5:$imm, GPRNoX0:$rs2, simm5:$simm2)>;
 
 class QCISELECTICCPat<CondCode Cond, QCISELECTICC Inst>
-    : Pat<(select (i32 (setcc (i32 GPRNoX0:$rd), (i32 GPRNoX0:$rs1), Cond)), (i32 GPRNoX0:$rs2), simm5:$simm2),
+    : Pat<(i32 (riscv_selectcc (i32 GPRNoX0:$rd), (i32 GPRNoX0:$rs1), Cond, (i32 GPRNoX0:$rs2), simm5:$simm2)),
           (Inst GPRNoX0:$rd, GPRNoX0:$rs1, GPRNoX0:$rs2, simm5:$simm2)>;
 
 class QCISELECTICCPatInv<CondCode Cond, QCISELECTICC Inst>
-    : Pat<(select (i32 (setcc (i32 GPRNoX0:$rd), (i32 GPRNoX0:$rs1), Cond)), simm5:$simm2, (i32 GPRNoX0:$rs2)),
+    : Pat<(i32 (riscv_selectcc (i32 GPRNoX0:$rd), (i32 GPRNoX0:$rs1), Cond, simm5:$simm2, (i32 GPRNoX0:$rs2))),
           (Inst GPRNoX0:$rd, GPRNoX0:$rs1, GPRNoX0:$rs2, simm5:$simm2)>;
 
 class QCISELECTIICCPat<CondCode Cond, QCISELECTIICC Inst>
-    : Pat<(select (i32 (setcc (i32 GPRNoX0:$rd), (i32 GPRNoX0:$rs1), Cond)), simm5:$simm1, simm5:$simm2),
+    : Pat<(i32 (riscv_selectcc (i32 GPRNoX0:$rd), (i32 GPRNoX0:$rs1), Cond, simm5:$simm1, simm5:$simm2)),
           (Inst GPRNoX0:$rd, GPRNoX0:$rs1, simm5:$simm1, simm5:$simm2)>;
 
 class QCILICCPat<CondCode Cond, QCILICC Inst>
-    : Pat<(select (XLenVT (setcc (XLenVT GPRNoX0:$rs1), (XLenVT GPRNoX0:$rs2), Cond)), simm5:$simm, (XLenVT GPRNoX0:$rd)),
+    : Pat<(i32 (riscv_selectcc (i32 GPRNoX0:$rs1), (i32 GPRNoX0:$rs2), Cond, simm5:$simm, (i32 GPRNoX0:$rd))),
           (Inst GPRNoX0:$rd, GPRNoX0:$rs1, GPRNoX0:$rs2, simm5:$simm)>;
 
 class QCILICCPatInv<CondCode Cond, QCILICC Inst>
-    : Pat<(select (XLenVT (setcc (XLenVT GPRNoX0:$rs1), (XLenVT GPRNoX0:$rs2), Cond)), (XLenVT GPRNoX0:$rd), simm5:$simm),
+    : Pat<(i32 (riscv_selectcc (i32 GPRNoX0:$rs1), (i32 GPRNoX0:$rs2), Cond, (i32 GPRNoX0:$rd), simm5:$simm)),
           (Inst GPRNoX0:$rd, GPRNoX0:$rs1, GPRNoX0:$rs2, simm5:$simm)>;
 
 class QCILICCIPat<CondCode Cond, QCILICC Inst, DAGOperand InTyImm>
-    : Pat<(select (XLenVT (setcc (XLenVT GPRNoX0:$rs1), InTyImm:$imm, Cond)), simm5:$simm, (XLenVT GPRNoX0:$rd)),
+    : Pat<(i32 (riscv_selectcc (i32 GPRNoX0:$rs1), InTyImm:$imm, Cond, simm5:$simm, (i32 GPRNoX0:$rd))),
           (Inst GPRNoX0:$rd, GPRNoX0:$rs1, InTyImm:$imm, simm5:$simm)>;
 
 class QCILICCIPatInv<CondCode Cond, QCILICC Inst, DAGOperand InTyImm>
-    : Pat<(select (XLenVT (setcc (XLenVT GPRNoX0:$rs1), InTyImm:$imm, Cond)), (XLenVT GPRNoX0:$rd), simm5:$simm),
+    : Pat<(i32 (riscv_selectcc (i32 GPRNoX0:$rs1), InTyImm:$imm, Cond, (i32 GPRNoX0:$rd), simm5:$simm)),
           (Inst GPRNoX0:$rd, GPRNoX0:$rs1, InTyImm:$imm, simm5:$simm)>;
 
 // Match `riscv_brcc` and lower to the appropriate XQCIBI branch instruction.
@@ -1525,79 +1525,93 @@ let Predicates = [HasVendorXqciint, IsRV32] in
 def : Pat<(riscv_mileaveret_glue), (QC_C_MILEAVERET)>;
 
 let Predicates = [HasVendorXqcicm, IsRV32] in {
-def : Pat<(select (i32 GPRNoX0:$rs1), (i32 GPRNoX0:$rd),(i32 GPRNoX0:$rs3)),
-          (QC_MVEQI GPRNoX0:$rd, GPRNoX0:$rs1, (i32 0), GPRNoX0:$rs3)>;
-
-def : QCIMVCCPat <SETEQ,  QC_MVEQ>;
-def : QCIMVCCPat <SETNE,  QC_MVNE>;
-def : QCIMVCCPat <SETLT,  QC_MVLT>;
-def : QCIMVCCPat <SETULT, QC_MVLTU>;
-
-def : QCIMVCCIPat <SETLT,  QC_MVLTI, simm5>;
-def : QCIMVCCIPat <SETULT, QC_MVLTUI, uimm5>;
+def : QCIMVCCPat<SETEQ,  QC_MVEQ>;
+def : QCIMVCCPat<SETNE,  QC_MVNE>;
+def : QCIMVCCPat<SETLT,  QC_MVLT>;
+def : QCIMVCCPat<SETULT, QC_MVLTU>;
+def : QCIMVCCPat<SETGE,  QC_MVGE>;
+def : QCIMVCCPat<SETUGE, QC_MVGEU>;
+
+def : QCIMVCCIPat<SETLT,  QC_MVLTI,  simm5>;
+def : QCIMVCCIPat<SETULT, QC_MVLTUI, uimm5>;
+def : QCIMVCCIPat<SETGE,  QC_MVGEI,  simm5>;
+def : QCIMVCCIPat<SETUGE, QC_MVGEUI, uimm5>;
 }
 
 // Prioritize Xqcics over these patterns.
 let Predicates = [HasVendorXqcicm, NoVendorXqcics, IsRV32] in {
-def : QCIMVCCIPat <SETEQ,  QC_MVEQI, simm5>;
-def : QCIMVCCIPat <SETNE,  QC_MVNEI, simm5>;
+// (SELECT X, Y, Z) is canonicalised to `(riscv_selectcc x, 0, NE, y, z)`.
+// This exists to prioritise over the `Select_GPR_Using_CC_GPR` pattern.
+def : Pat<(i32 (riscv_selectcc (i32 GPRNoX0:$rs1), (i32 0), SETNE, (i32 GPRNoX0:$rs3), (i32 GPRNoX0:$rd))),
+          (QC_MVNEI GPRNoX0:$rd, GPRNoX0:$rs1, 0, GPRNoX0:$rs3)>;
+def : Pat<(i32 (riscv_selectcc (i32 GPRNoX0:$rs1), (i32 0), SETEQ, (i32 GPRNoX0:$rs3), (i32 GPRNoX0:$rd))),
+          (QC_MVEQI GPRNoX0:$rd, GPRNoX0:$rs1, 0, GPRNoX0:$rs3)>;
+
+def : QCIMVCCIPat<SETEQ,  QC_MVEQI, simm5>;
+def : QCIMVCCIPat<SETNE,  QC_MVNEI, simm5>;
 }
 
-let Predicates = [HasVendorXqcicli, HasVendorXqcicsOrXqcicm, IsRV32] in {
-def : QCILICCPat <SETEQ,  QC_LIEQ>;
-def : QCILICCPat <SETNE,  QC_LINE>;
-def : QCILICCPat <SETLT,  QC_LILT>;
-def : QCILICCPat <SETGE,  QC_LIGE>;
-def : QCILICCPat <SETULT, QC_LILTU>;
-def : QCILICCPat <SETUGE, QC_LIGEU>;
-
-def : QCILICCIPat <SETEQ,  QC_LIEQI, simm5>;
-def : QCILICCIPat <SETNE,  QC_LINEI, simm5>;
-def : QCILICCIPat <SETLT,  QC_LILTI, simm5>;
-def : QCILICCIPat <SETGE,  QC_LIGEI, simm5>;
-def : QCILICCIPat <SETULT, QC_LILTUI, uimm5>;
-def : QCILICCIPat <SETUGE, QC_LIGEUI, uimm5>;
-
-def : QCILICCPatInv <SETNE,  QC_LIEQ>;
-def : QCILICCPatInv <SETEQ,  QC_LINE>;
-def : QCILICCPatInv <SETGE,  QC_LILT>;
-def : QCILICCPatInv <SETLT,  QC_LIGE>;
-def : QCILICCPatInv <SETUGE, QC_LILTU>;
-def : QCILICCPatInv <SETULT, QC_LIGEU>;
-
-def : QCILICCIPatInv <SETNE,  QC_LIEQI, simm5>;
-def : QCILICCIPatInv <SETEQ,  QC_LINEI, simm5>;
-def : QCILICCIPatInv <SETGE,  QC_LILTI, simm5>;
-def : QCILICCIPatInv <SETLT,  QC_LIGEI, simm5>;
-def : QCILICCIPatInv <SETUGE, QC_LILTUI, uimm5>;
-def : QCILICCIPatInv <SETULT, QC_LIGEUI, uimm5>;
-}
+let Predicates = [HasVendorXqcicli, IsRV32] in {
+def : QCILICCPat<SETEQ,  QC_LIEQ>;
+def : QCILICCPat<SETNE,  QC_LINE>;
+def : QCILICCPat<SETLT,  QC_LILT>;
+def : QCILICCPat<SETGE,  QC_LIGE>;
+def : QCILICCPat<SETULT, QC_LILTU>;
+def : QCILICCPat<SETUGE, QC_LIGEU>;
+
+def : QCILICCIPat<SETEQ,  QC_LIEQI, simm5>;
+def : QCILICCIPat<SETNE,  QC_LINEI, simm5>;
+def : QCILICCIPat<SETLT,  QC_LILTI, simm5>;
+def : QCILICCIPat<SETGE,  QC_LIGEI, simm5>;
+def : QCILICCIPat<SETULT, QC_LILTUI, uimm5>;
+def : QCILICCIPat<SETUGE, QC_LIGEUI, uimm5>;
+
+def : QCILICCPatInv<SETNE,  QC_LIEQ>;
+def : QCILICCPatInv<SETEQ,  QC_LINE>;
+def : QCILICCPatInv<SETGE,  QC_LILT>;
+def : QCILICCPatInv<SETLT,  QC_LIGE>;
+def : QCILICCPatInv<SETUGE, QC_LILTU>;
+def : QCILICCPatInv<SETULT, QC_LIGEU>;
+
+def : QCILICCIPatInv<SETNE,  QC_LIEQI, simm5>;
+def : QCILICCIPatInv<SETEQ,  QC_LINEI, simm5>;
+def : QCILICCIPatInv<SETGE,  QC_LILTI, simm5>;
+def : QCILICCIPatInv<SETLT,  QC_LIGEI, simm5>;
+def : QCILICCIPatInv<SETUGE, QC_LILTUI, uimm5>;
+def : QCILICCIPatInv<SETULT, QC_LIGEUI, uimm5>;
+} // Predicates = [HasVendorXqcicli, IsRV32]
 
 let Predicates = [HasVendorXqcics, IsRV32] in {
-def : Pat<(select (i32 GPRNoX0:$rd), (i32 GPRNoX0:$rs2),(i32 GPRNoX0:$rs3)),
+// (SELECT X, Y, Z) is canonicalised to `(riscv_selectcc x, 0, NE, y, z)`.
+// These exist to prioritise over the `Select_GPR_Using_CC_GPR` pattern.
+
+def : Pat<(i32 (riscv_selectcc (i32 GPRNoX0:$rd), (i32 0), SETNE, (i32 GPRNoX0:$rs2), (i32 GPRNoX0:$rs3))),
           (QC_SELECTNEI GPRNoX0:$rd, (i32 0), GPRNoX0:$rs2, GPRNoX0:$rs3)>;
-def : Pat<(select (i32 GPRNoX0:$rd), (i32 GPRNoX0:$rs2), simm5:$simm2),
+def : Pat<(i32 (riscv_selectcc (i32 GPRNoX0:$rd), (i32 0), SETEQ, (i32 GPRNoX0:$rs2), (i32 GPRNoX0:$rs3))),
+          (QC_SELECTEQI GPRNoX0:$rd, (i32 0), GPRNoX0:$rs2, GPRNoX0:$rs3)>;
+
+def : Pat<(i32 (riscv_selectcc (i32 GPRNoX0:$rd), (i32 0), SETNE, (i32 GPRNoX0:$rs2), simm5:$simm2)),
           (QC_SELECTINEI GPRNoX0:$rd, (i32 0), GPRNoX0:$rs2, simm5:$simm2)>;
-def : Pat<(select (i32 GPRNoX0:$rd), simm5:$simm2,(i32 GPRNoX0:$rs2)),
+def : Pat<(i32 (riscv_selectcc (i32 GPRNoX0:$rd), (i32 0), SETNE, simm5:$simm2, (i32 GPRNoX0:$rs2))),
           (QC_SELECTIEQI GPRNoX0:$rd, (i32 0), GPRNoX0:$rs2, simm5:$simm2)>;
 
-def : QCISELECTCCIPat <SETEQ,  QC_SELECTEQI>;
-def : QCISELECTCCIPat <SETNE,  QC_SELECTNEI>;
+def : QCISELECTCCIPat<SETEQ,  QC_SELECTEQI>;
+def : QCISELECTCCIPat<SETNE,  QC_SELECTNEI>;
 
-def : QCISELECTICCIPat <SETEQ,  QC_SELECTIEQI>;
-def : QCISELECTICCIPat <SETNE,  QC_SELECTINEI>;
+def : QCISELECTICCIPat<SETEQ,  QC_SELECTIEQI>;
+def : QCISELECTICCIPat<SETNE,  QC_SELECTINEI>;
 
-def : QCISELECTICCIPatInv <SETEQ,  QC_SELECTINEI>;
-def : QCISELECTICCIPatInv <SETNE,  QC_SELECTIEQI>;
+def : QCISELECTICCIPatInv<SETEQ,  QC_SELECTINEI>;
+def : QCISELECTICCIPatInv<SETNE,  QC_SELECTIEQI>;
 
-def : QCISELECTICCPat <SETEQ,  QC_SELECTIEQ>;
-def : QCISELECTICCPat <SETNE,  QC_SELECTINE>;
+def : QCISELECTICCPat<SETEQ,  QC_SELECTIEQ>;
+def : QCISELECTICCPat<SETNE,  QC_SELECTINE>;
 
-def : QCISELECTICCPatInv <SETEQ,  QC_SELECTINE>;
-def : QCISELECTICCPatInv <SETNE,  QC_SELECTIEQ>;
+def : QCISELECTICCPatInv<SETEQ,  QC_SELECTINE>;
+def : QCISELECTICCPatInv<SETNE,  QC_SELECTIEQ>;
 
-def : QCISELECTIICCPat <SETEQ,  QC_SELECTIIEQ>;
-def : QCISELECTIICCPat <SETNE,  QC_SELECTIINE>;
+def : QCISELECTIICCPat<SETEQ,  QC_SELECTIIEQ>;
+def : QCISELECTIICCPat<SETNE,  QC_SELECTIINE>;
 } // Predicates = [HasVendorXqcics, IsRV32]
 
 let Predicates = [HasVendorXqcilsm, IsRV32] in {
diff --git a/llvm/test/CodeGen/RISCV/select-bare.ll b/llvm/test/CodeGen/RISCV/select-bare.ll
index fc8eaa480b116..e6b742375df57 100644
--- a/llvm/test/CodeGen/RISCV/select-bare.ll
+++ b/llvm/test/CodeGen/RISCV/select-bare.ll
@@ -3,6 +3,8 @@
 ; RUN:   | FileCheck %s -check-prefix=RV32I
 ; RUN: llc -mtriple=riscv64 -mattr=+xmipscmov -verify-machineinstrs < %s \
 ; RUN:   | FileCheck -check-prefix=RV64I-CCMOV %s
+; RUN: llc -mtriple=riscv32 -mattr=+experimental-xqcicm,+experimental-xqcics,+experimental-xqcicli -verify-machineinstrs < %s \
+; RUN:   | FileCheck %s --check-prefixes=RV32IXQCI
 
 define i32 @bare_select(i1 %a, i32 %b, i32 %c) nounwind {
 ; RV32I-LABEL: bare_select:
@@ -20,6 +22,12 @@ define i32 @bare_select(i1 %a, i32 %b, i32 %c) nounwind {
 ; RV64I-CCMOV-NEXT:    andi a0, a0, 1
 ; RV64I-CCMOV-NEXT:    mips.ccmov a0, a0, a1, a2
 ; RV64I-CCMOV-NEXT:    ret
+;
+; RV32IXQCI-LABEL: bare_select:
+; RV32IXQCI:       # %bb.0:
+; RV32IXQCI-NEXT:    andi a0, a0, 1
+; RV32IXQCI-NEXT:    qc.selectnei a0, 0, a1, a2
+; RV32IXQCI-NEXT:    ret
   %1 = select i1 %a, i32 %b, i32 %c
   ret i32 %1
 }
@@ -40,6 +48,12 @@ define float @bare_select_float(i1 %a, float %b, float %c) nounwind {
 ; RV64I-CCMOV-NEXT:    andi a0, a0, 1
 ; RV64I-CCMOV-NEXT:    mips.ccmov a0, a0, a1, a2
 ; RV64I-CCMOV-NEXT:    ret
+;
+; RV32IXQCI-LABEL: bare_select_float:
+; RV32IXQCI:       # %bb.0:
+; RV32IXQCI-NEXT:    andi a0, a0, 1
+; RV32IXQCI-NEXT:    qc.selectnei a0, 0, a1, a2
+; RV32IXQCI-NEXT:    ret
   %1 = select i1 %a, float %b, float %c
   ret float %1
 }
diff --git a/llvm/test/CodeGen/RISCV/select-cc.ll b/llvm/test/CodeGen/RISCV/select-cc.ll
index ec1f8aeddcaaf..02b562c130657 100644
--- a/llvm/test/CodeGen/RISCV/select-cc.ll
+++ b/llvm/test/CodeGen/RISCV/select-cc.ll
@@ -1,6 +1,8 @@
 ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
 ; RUN: llc -mtriple=riscv32 -disable-block-placement -verify-machineinstrs < %s \
 ; RUN:   | FileCheck -check-prefixes=RV32I %s
+; RUN: llc -mtriple=riscv32 -mattr=+experimental-xqcicm,+experimental-xqcics,+experimental-xqcicli -verify-machineinstrs < %s \
+; RUN:   | FileCheck %s --check-prefixes=RV32IXQCI
 ; RUN: llc -mtriple=riscv64 -disable-block-placement -verify-machineinstrs < %s \
 ; RUN:   | FileCheck -check-prefixes=RV64I %s
 ; RUN: llc -mtriple=riscv64 -mattr=+xmipscmov -verify-machineinstrs < %s \
@@ -83,6 +85,44 @@ define signext i32 @foo(i32 signext %a, ptr %b) nounwind {
 ; RV32I-NEXT:  .LBB0_28:
 ; RV32I-NEXT:    ret
 ;
+; RV32IXQCI-LABEL: foo:
+; RV32IXQCI:       # %bb.0:
+; RV32IXQCI-NEXT:    lw a5, 0(a1)
+; RV32IXQCI-NEXT:    lw a2, 0(a1)
+; RV32IXQCI-NEXT:    lw a4, 0(a1)
+; RV32IXQCI-NEXT:    lw t5, 0(a1)
+; RV32IXQCI-NEXT:    lw t4, 0(a1)
+; RV32IXQCI-NEXT:    lw t2, 0(a1)
+; RV32IXQCI-NEXT:    lw t1, 0(a1)
+; RV32IXQCI-NEXT:    lw t0, 0(a1)
+; RV32IXQCI-NEXT:    lw a7, 0(a1)
+; RV32IXQCI-NEXT:    lw a6, 0(a1)
+; RV32IXQCI-NEXT:    lw t3, 0(a1)
+; RV32IXQCI-NEXT:    lw a3, 0(a1)
+; RV32IXQCI-NEXT:    bltz t3, .LBB0_2
+; RV32IXQCI-NEXT:  # %bb.1:
+; RV32IXQCI-NEXT:    li t6, 0
+; RV32IXQCI-NEXT:    qc.mveq a5, a0, a5, a0
+; RV32IXQCI-NEXT:    qc.mvne a2, a5, a2, a5
+; RV32IXQCI-NEXT:    qc.mvltu a4, a4, a2, a2
+; RV32IXQCI-NEXT:    qc.mvgeu t5, a4, t5, a4
+; RV32IXQCI-NEXT:    qc.mvltu t4, t5, t4, t5
+; RV32IXQCI-NEXT:    qc.mvgeu t2, t2, t4, t4
+; RV32IXQCI-NEXT:    qc.mvlt t1, t1, t2, t2
+; RV32IXQCI-NEXT:    qc.mvge t0, t1, t0, t1
+; RV32IXQCI-NEXT:    qc.mvlt a7, t0, a7, t0
+; RV32IXQCI-NEXT:    qc.mvge a6, a6, a7, a7
+; RV32IXQCI-NEXT:    mv a3, t3
+; RV32IXQCI-NEXT:    qc.mvge a3, t6, t3, a6
+; RV32IXQCI-NEXT:  .LBB0_2:
+; RV32IXQCI-NEXT:    lw a2, 0(a1)
+; RV32IXQCI-NEXT:    lw a0, 0(a1)
+; RV32IXQCI-NEXT:    li a1, 1024
+; RV32IXQCI-NEXT:    qc.mvlt a2, a1, a2, a3
+; RV32IXQCI-NEXT:    li a1, 2046
+; RV32IXQCI-NEXT:    qc.mvltu a0, a1, t3, a2
+; RV32IXQCI-NEXT:    ret
+;
 ; RV64I-LABEL: foo:
 ; RV64I:       # %bb.0:
 ; RV64I-NEXT:    lw a2, 0(a1)
@@ -291,6 +331,23 @@ define signext i16 @numsignbits(i16 signext %0, i16 signext %1, i16 signext %2,
 ; RV32I-NEXT:    addi sp, sp, 16
 ; RV32I-NEXT:    ret
 ;
+; RV32IXQCI-LABEL: numsignbits:
+; RV32IXQCI:       # %bb.0:
+; RV32IXQCI-NEXT:    qc.selecteqi a0, 0, a3, a2
+; RV32IXQCI-NEXT:    beqz a1, .LBB1_2
+; RV32IXQCI-NEXT:  # %bb.1:
+; RV32IXQCI-NEXT:    addi sp, sp, -16
+; RV32IXQCI-NEXT:    sw ra, 12(sp) # 4-byte Folded Spill
+; RV32IXQCI-NEXT:    sw s0, 8(sp) # 4-byte Folded Spill
+; RV32IXQCI-NEXT:    mv s0, a0
+; RV32IXQCI-NEXT:    call bar
+; RV32IXQCI-NEXT:    mv a0, s0
+; RV32IXQCI-NEXT:    lw ra, 12(sp) # 4-byte Folded Reload
+; RV32IXQCI-NEXT:    lw s0, 8(sp) # 4-byte Folded Reload
+; RV32IXQCI-NEXT:    addi sp, sp, 16
+; RV32IXQCI-NEXT:  .LBB1_2:
+; RV32IXQCI-NEXT:    ret
+;
 ; RV64I-LABEL: numsignbits:
 ; RV64I:       # %bb.0:
 ; RV64I-NEXT:    addi sp, sp, -16
@@ -355,6 +412,14 @@ define i32 @select_sge_int16min(i32 signext %x, i32 signext %y, i32 signext %z)
 ; RV32I-NEXT:    mv a0, a1
 ; RV32I-NEXT:    ret
 ;
+; RV32IXQCI-LABEL: select_sge_int16min:
+; RV32IXQCI:       # %bb.0:
+; RV32IXQCI-NEXT:    lui a3, 1048560
+; RV32IXQCI-NEXT:    addi a3, a3, -1
+; RV32IXQCI-NEXT:    qc.mvlt a2, a3, a0, a1
+; RV32IXQCI-NEXT:    mv a0, a2
+; RV32IXQCI-NEXT:    ret
+;
 ; RV64I-LABEL: select_sge_int16min:
 ; RV64I:       # %bb.0:
 ; RV64I-NEXT:    lui a3, 1048560
@@ -399,6 +464,17 @@ define i64 @select_sge_int32min(i64 %x, i64 %y, i64 %z) {
 ; RV32I-NEXT:    mv a1, a3
 ; RV32I-NEXT:    ret
 ;
+; RV32IXQCI-LABEL: select_sge_int32min:
+; RV32IXQCI:       # %bb.0:
+; RV32IXQCI-NEXT:    slti a6, a0, 0
+; RV32IXQCI-NEXT:    slti a0, a1, 0
+; RV32IXQCI-NEXT:    xori a0, a0, 1
+; RV32IXQCI-NEXT:    qc.selecteqi a1, -1, a6, a0
+; RV32IXQCI-NEXT:    mv a0, a1
+; RV32IXQCI-NEXT:    qc.selectnei a0, 0, a2, a4
+; RV32IXQCI-NEXT:    qc.selectnei a1, 0, a3, a5
+; RV32IXQCI-NEXT:    ret
+;
 ; RV64I-LABEL: select_sge_int32min:
 ; RV64I:       # %bb.0:
 ; RV64I-NEXT:    lui a3, 524288
diff --git a/llvm/test/CodeGen/RISCV/select-cond.ll b/llvm/test/CodeGen/RISCV/select-cond.ll
index 59f4d95f45acc..a2a0a4e9177f6 100644
--- a/llvm/test/CodeGen/RISCV/select-cond.ll
+++ b/llvm/test/CodeGen/RISCV/select-cond.ll
@@ -7,6 +7,8 @@
 ; RUN:   | FileCheck %s --check-prefixes=RV32-XQCICM
 ; RUN: llc -mtriple=riscv32 -mattr=+experimental-xqcics -verify-machineinstrs < %s \
 ; RUN:  ...
[truncated]

@lenary lenary requested review from svs-quic and hchandel August 16, 2025 22:47
@lenary
Copy link
Member Author

lenary commented Aug 26, 2025

Ping?

@lenary
Copy link
Member Author

lenary commented Sep 3, 2025

I think the choice to prioritise Xqcics over Xqcicm where the patterns overlap is wrong - as Xqcicm has compressible instructions.

I am updating this PR and will make that change. Sorry that it will make the PR even larger.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants