[llvm] [RISCV] Add combines to form WSUBAU on RV32 with P. (PR #181604)

Craig Topper via llvm-commits llvm-commits at lists.llvm.org
Sun Feb 15 22:51:44 PST 2026


https://github.com/topperc created https://github.com/llvm/llvm-project/pull/181604

None

>From 44e97ea46d85f4eaab7c65d129323c4af3327e82 Mon Sep 17 00:00:00 2001
From: Craig Topper <craig.topper at sifive.com>
Date: Sun, 15 Feb 2026 22:11:49 -0800
Subject: [PATCH 1/2] Pre-commit tests

---
 llvm/test/CodeGen/RISCV/rv32p.ll | 44 ++++++++++++++++++++++++++++++++
 1 file changed, 44 insertions(+)

diff --git a/llvm/test/CodeGen/RISCV/rv32p.ll b/llvm/test/CodeGen/RISCV/rv32p.ll
index 9f04646b54d75..8a8e5d6bd20fc 100644
--- a/llvm/test/CodeGen/RISCV/rv32p.ll
+++ b/llvm/test/CodeGen/RISCV/rv32p.ll
@@ -1109,3 +1109,47 @@ define i64 @waddau_zext_chain(i64 %acc, i32 %a, i32 %b) nounwind {
   %sum2 = add i64 %sum1, %ext_b
   ret i64 %sum2
 }
+
+; acc - zext(a) -> wsubau acc, 0, a
+define i64 @wsubau_zext(i64 %acc, i32 %a) nounwind {
+; CHECK-LABEL: wsubau_zext:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    li a3, 0
+; CHECK-NEXT:    subd a0, a0, a2
+; CHECK-NEXT:    ret
+  %ext_a = zext i32 %a to i64
+  %sub = sub i64 %acc, %ext_a
+  ret i64 %sub
+}
+
+; (acc + zext(a)) - zext(b) -> wsubau acc, a, b
+define i64 @wsubau_zext_chain(i64 %acc, i32 %a, i32 %b) nounwind {
+; CHECK-LABEL: wsubau_zext_chain:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    mv a4, a3
+; CHECK-NEXT:    waddau a0, a2, zero
+; CHECK-NEXT:    li a5, 0
+; CHECK-NEXT:    subd a0, a0, a4
+; CHECK-NEXT:    ret
+  %ext_a = zext i32 %a to i64
+  %ext_b = zext i32 %b to i64
+  %sum = add i64 %acc, %ext_a
+  %sub = sub i64 %sum, %ext_b
+  ret i64 %sub
+}
+
+; (acc - zext(a)) + zext(b) -> wsubau acc, b, a
+define i64 @wsubau_zext_chain_rev(i64 %acc, i32 %a, i32 %b) nounwind {
+; CHECK-LABEL: wsubau_zext_chain_rev:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    mv a4, a3
+; CHECK-NEXT:    li a3, 0
+; CHECK-NEXT:    subd a0, a0, a2
+; CHECK-NEXT:    waddau a0, a4, zero
+; CHECK-NEXT:    ret
+  %ext_a = zext i32 %a to i64
+  %ext_b = zext i32 %b to i64
+  %sub = sub i64 %acc, %ext_a
+  %sum = add i64 %sub, %ext_b
+  ret i64 %sum
+}

>From 13d63cf68c207d3f13b21261154d843c16882828 Mon Sep 17 00:00:00 2001
From: Craig Topper <craig.topper at sifive.com>
Date: Sun, 15 Feb 2026 22:46:26 -0800
Subject: [PATCH 2/2] [RISCV] Add combines to form WSUBAU on RV32 with P.

---
 llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp | 16 +++---
 llvm/lib/Target/RISCV/RISCVISelLowering.cpp | 59 ++++++++++++++++++---
 llvm/lib/Target/RISCV/RISCVInstrInfoP.td    |  2 +
 llvm/test/CodeGen/RISCV/rv32p.ll            | 13 ++---
 4 files changed, 66 insertions(+), 24 deletions(-)

diff --git a/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp b/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp
index 7d61ea6332138..bf5057bdc50fb 100644
--- a/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp
@@ -1990,7 +1990,8 @@ void RISCVDAGToDAGISel::Select(SDNode *Node) {
     [[fallthrough]];
   case RISCVISD::SUBD:
   case RISCVISD::PPAIRE_DB:
-  case RISCVISD::WADDAU: {
+  case RISCVISD::WADDAU:
+  case RISCVISD::WSUBAU: {
     assert(!Subtarget->is64Bit() && "Unexpected opcode");
     assert((Node->getOpcode() != RISCVISD::PPAIRE_DB ||
             Subtarget->enablePExtSIMDCodeGen()) &&
@@ -2010,17 +2011,16 @@ void RISCVDAGToDAGISel::Select(SDNode *Node) {
     SDValue Op1Hi = Node->getOperand(3);
 
     MachineSDNode *New;
-    if (Node->getOpcode() == RISCVISD::WADDAU) {
-      // WADDAU: rd = rd + zext(rs1) + zext(rs2)
-      // Op0 is the accumulator (GPRPair), Op1Lo and Op1Hi are the two 32-bit
-      // values to add.
-      New = CurDAG->getMachineNode(RISCV::WADDAU, DL, MVT::Untyped, Op0, Op1Lo,
-                                   Op1Hi);
+    if (Opcode == RISCVISD::WADDAU || Opcode == RISCVISD::WSUBAU) {
+      // WADDAU/WSUBAU: Op0 is the accumulator (GPRPair), Op1Lo and Op1Hi are
+      // the two 32-bit values.
+      unsigned Opc = Opcode == RISCVISD::WADDAU ? RISCV::WADDAU : RISCV::WSUBAU;
+      New = CurDAG->getMachineNode(Opc, DL, MVT::Untyped, Op0, Op1Lo, Op1Hi);
     } else {
       SDValue Op1 = buildGPRPair(CurDAG, DL, MVT::Untyped, Op1Lo, Op1Hi);
 
       unsigned Opc;
-      switch (Node->getOpcode()) {
+      switch (Opcode) {
       default:
         llvm_unreachable("Unexpected opcode");
       case RISCVISD::ADDD:
diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
index 4d38ee354d86f..221a56427c173 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -21375,25 +21375,72 @@ SDValue RISCVTargetLowering::PerformDAGCombine(SDNode *N,
     }
     break;
   }
+  case RISCVISD::SUBD: {
+    assert(!Subtarget.is64Bit() && Subtarget.hasStdExtP() &&
+           "SUBD is only for RV32 with P extension");
+
+    SDValue Op0Lo = N->getOperand(0);
+    SDValue Op0Hi = N->getOperand(1);
+    SDValue Op1Lo = N->getOperand(2);
+    SDValue Op1Hi = N->getOperand(3);
+
+    // (SUBD lo, hi, x, 0) -> (WSUBAU lo, hi, 0, x)
+    // WSUBAU semantics: rd = rd + zext(rs1) - zext(rs2)
+    if (isNullConstant(Op1Hi)) {
+      SDValue Result =
+          DAG.getNode(RISCVISD::WSUBAU, DL, DAG.getVTList(MVT::i32, MVT::i32),
+                      Op0Lo, Op0Hi, DAG.getConstant(0, DL, MVT::i32), Op1Lo);
+      return DCI.CombineTo(N, Result.getValue(0), Result.getValue(1));
+    }
+    break;
+  }
   case RISCVISD::WADDAU: {
     assert(!Subtarget.is64Bit() && Subtarget.hasStdExtP() &&
            "WADDAU is only for RV32 with P extension");
-    // (WADDAU (WADDAU lo, hi, x, 0), y, 0) -> (WADDAU lo, hi, x, y)
     SDValue Op0Lo = N->getOperand(0);
     SDValue Op0Hi = N->getOperand(1);
     SDValue Op1 = N->getOperand(2);
     SDValue Op2 = N->getOperand(3);
 
-    // Check if this WADDAU has a zero second operand and the accumulator
-    // comes from another WADDAU with a zero second operand.
     // FIXME: Canonicalize zero Op1 to Op2.
-    if (isNullConstant(Op2) && Op0Lo.getOpcode() == RISCVISD::WADDAU &&
+    if (isNullConstant(Op2) && Op0Lo.getNode() == Op0Hi.getNode() &&
+        Op0Lo.getResNo() == 0 && Op0Hi.getResNo() == 1 && Op0Lo.hasOneUse() &&
+        Op0Hi.hasOneUse()) {
+      // (WADDAU (WADDAU lo, hi, x, 0), y, 0) -> (WADDAU lo, hi, x, y)
+      if (Op0Lo.getOpcode() == RISCVISD::WADDAU &&
+          isNullConstant(Op0Lo.getOperand(3))) {
+        SDValue Result = DAG.getNode(
+            RISCVISD::WADDAU, DL, DAG.getVTList(MVT::i32, MVT::i32),
+            Op0Lo.getOperand(0), Op0Lo.getOperand(1), Op0Lo.getOperand(2), Op1);
+        return DCI.CombineTo(N, Result.getValue(0), Result.getValue(1));
+      }
+      // (WADDAU (WSUBAU lo, hi, 0, a), b, 0) -> (WSUBAU lo, hi, b, a)
+      if (Op0Lo.getOpcode() == RISCVISD::WSUBAU &&
+          isNullConstant(Op0Lo.getOperand(2))) {
+        SDValue Result = DAG.getNode(
+            RISCVISD::WSUBAU, DL, DAG.getVTList(MVT::i32, MVT::i32),
+            Op0Lo.getOperand(0), Op0Lo.getOperand(1), Op1, Op0Lo.getOperand(3));
+        return DCI.CombineTo(N, Result.getValue(0), Result.getValue(1));
+      }
+    }
+    break;
+  }
+  case RISCVISD::WSUBAU: {
+    assert(!Subtarget.is64Bit() && Subtarget.hasStdExtP() &&
+           "WSUBAU is only for RV32 with P extension");
+    SDValue Op0Lo = N->getOperand(0);
+    SDValue Op0Hi = N->getOperand(1);
+    SDValue Op1 = N->getOperand(2);
+    SDValue Op2 = N->getOperand(3);
+
+    // (WSUBAU (WADDAU lo, hi, a, 0), 0, b) -> (WSUBAU lo, hi, a, b)
+    if (isNullConstant(Op1) && Op0Lo.getOpcode() == RISCVISD::WADDAU &&
         Op0Lo.getNode() == Op0Hi.getNode() && Op0Lo.getResNo() == 0 &&
         Op0Hi.getResNo() == 1 && Op0Lo.hasOneUse() && Op0Hi.hasOneUse() &&
         isNullConstant(Op0Lo.getOperand(3))) {
       SDValue Result = DAG.getNode(
-          RISCVISD::WADDAU, DL, DAG.getVTList(MVT::i32, MVT::i32),
-          Op0Lo.getOperand(0), Op0Lo.getOperand(1), Op0Lo.getOperand(2), Op1);
+          RISCVISD::WSUBAU, DL, DAG.getVTList(MVT::i32, MVT::i32),
+          Op0Lo.getOperand(0), Op0Lo.getOperand(1), Op0Lo.getOperand(2), Op2);
       return DCI.CombineTo(N, Result.getValue(0), Result.getValue(1));
     }
     break;
diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfoP.td b/llvm/lib/Target/RISCV/RISCVInstrInfoP.td
index 6de7309dc2deb..2df2d84d6933a 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrInfoP.td
+++ b/llvm/lib/Target/RISCV/RISCVInstrInfoP.td
@@ -1501,6 +1501,8 @@ def SDT_RISCVWideningAddSubAccumulate : SDTypeProfile<2, 4, [SDTCisVT<0, i32>,
                                                              SDTCisSameAs<0, 5>]>;
 // Widening add accumulate unsigned: rd = rd + zext(rs1) + zext(rs2)
 def riscv_waddau : RVSDNode<"WADDAU", SDT_RISCVWideningAddSubAccumulate>;
+// Widening sub accumulate unsigned: rd = rd + zext(rs1) - zext(rs2)
+def riscv_wsubau : RVSDNode<"WSUBAU", SDT_RISCVWideningAddSubAccumulate>;
 
 def riscv_wmulsu : RVSDNode<"WMULSU", SDTIntBinHiLoOp>;
 
diff --git a/llvm/test/CodeGen/RISCV/rv32p.ll b/llvm/test/CodeGen/RISCV/rv32p.ll
index 8a8e5d6bd20fc..1e0e6b5e9748a 100644
--- a/llvm/test/CodeGen/RISCV/rv32p.ll
+++ b/llvm/test/CodeGen/RISCV/rv32p.ll
@@ -1114,8 +1114,7 @@ define i64 @waddau_zext_chain(i64 %acc, i32 %a, i32 %b) nounwind {
 define i64 @wsubau_zext(i64 %acc, i32 %a) nounwind {
 ; CHECK-LABEL: wsubau_zext:
 ; CHECK:       # %bb.0:
-; CHECK-NEXT:    li a3, 0
-; CHECK-NEXT:    subd a0, a0, a2
+; CHECK-NEXT:    wsubau a0, zero, a2
 ; CHECK-NEXT:    ret
   %ext_a = zext i32 %a to i64
   %sub = sub i64 %acc, %ext_a
@@ -1126,10 +1125,7 @@ define i64 @wsubau_zext(i64 %acc, i32 %a) nounwind {
 define i64 @wsubau_zext_chain(i64 %acc, i32 %a, i32 %b) nounwind {
 ; CHECK-LABEL: wsubau_zext_chain:
 ; CHECK:       # %bb.0:
-; CHECK-NEXT:    mv a4, a3
-; CHECK-NEXT:    waddau a0, a2, zero
-; CHECK-NEXT:    li a5, 0
-; CHECK-NEXT:    subd a0, a0, a4
+; CHECK-NEXT:    wsubau a0, a2, a3
 ; CHECK-NEXT:    ret
   %ext_a = zext i32 %a to i64
   %ext_b = zext i32 %b to i64
@@ -1142,10 +1138,7 @@ define i64 @wsubau_zext_chain(i64 %acc, i32 %a, i32 %b) nounwind {
 define i64 @wsubau_zext_chain_rev(i64 %acc, i32 %a, i32 %b) nounwind {
 ; CHECK-LABEL: wsubau_zext_chain_rev:
 ; CHECK:       # %bb.0:
-; CHECK-NEXT:    mv a4, a3
-; CHECK-NEXT:    li a3, 0
-; CHECK-NEXT:    subd a0, a0, a2
-; CHECK-NEXT:    waddau a0, a4, zero
+; CHECK-NEXT:    wsubau a0, a3, a2
 ; CHECK-NEXT:    ret
   %ext_a = zext i32 %a to i64
   %ext_b = zext i32 %b to i64



More information about the llvm-commits mailing list