[llvm] [RISCV][P-ext] Add mvd alias for padd.dw rd, zero, rs. Use for copy idiom. (PR #205223)

Craig Topper via llvm-commits llvm-commits at lists.llvm.org
Mon Jun 22 23:12:53 PDT 2026


https://github.com/topperc updated https://github.com/llvm/llvm-project/pull/205223

>From 49bb144054efd0f7bf745b5ace3ea637e7b1280f Mon Sep 17 00:00:00 2001
From: Craig Topper <craig.topper at sifive.com>
Date: Mon, 22 Jun 2026 15:02:58 -0700
Subject: [PATCH 1/2] [RISCV][P-ext] Add mvd alias for padd.dw rd, zero, rs.
 Use for copy idiom.

See https://github.com/riscv/riscv-p-spec/pull/304

I've refactored the MoveMerge code to use a different BuildMI call
for the 2 cases rather than try to manage variables to share the call.
While I fixed the code to not set the kill flag on X0_Pair and to
only set it on one of the operands for the FSGNJ_D_IN32X case. This
matches RISCVInstrInfo::copyPhysReg.

I wonder if we can just use copyPhysReg here?
---
 llvm/lib/Target/RISCV/RISCVInstrInfo.cpp      |  4 +-
 llvm/lib/Target/RISCV/RISCVInstrInfoP.td      |  2 +
 llvm/lib/Target/RISCV/RISCVMoveMerger.cpp     | 40 ++++++++-----------
 .../RISCV/calling-conv-p-ext-vector.ll        |  2 +-
 .../CodeGen/RISCV/make-compressible-zilsd.mir |  2 +-
 .../CodeGen/RISCV/rv32-merge-non-arg-reg.mir  |  4 +-
 .../CodeGen/RISCV/rv32-move-merge-crash.ll    |  2 +-
 llvm/test/CodeGen/RISCV/rv32-move-merge.ll    |  2 +-
 llvm/test/CodeGen/RISCV/rv32p.ll              | 14 +++----
 llvm/test/CodeGen/RISCV/rvp-simd-64.ll        |  2 +-
 llvm/test/MC/RISCV/rv32p-aliases-valid.s      |  4 ++
 11 files changed, 39 insertions(+), 39 deletions(-)

diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp b/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp
index 3636325cead24..62091cd734f54 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp
+++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp
@@ -548,8 +548,8 @@ void RISCVInstrInfo::copyPhysReg(MachineBasicBlock &MBB,
       if (STI.hasStdExtP()) {
         // On RV32P, `padd.dw` is a GPR Pair Add
         BuildMI(MBB, MBBI, DL, get(RISCV::PADD_DW), DstReg)
-            .addReg(SrcReg, KillFlag | getRenamableRegState(RenamableSrc))
-            .addReg(RISCV::X0_Pair);
+            .addReg(RISCV::X0_Pair)
+            .addReg(SrcReg, KillFlag | getRenamableRegState(RenamableSrc));
         return;
       }
     }
diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfoP.td b/llvm/lib/Target/RISCV/RISCVInstrInfoP.td
index e63eb9ce50e83..df983408004c9 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrInfoP.td
+++ b/llvm/lib/Target/RISCV/RISCVInstrInfoP.td
@@ -1730,6 +1730,8 @@ let append Predicates = [IsRV32] in {
   def : InstAlias<"pncvt.h $rd, $rs", (PNSRLI_H GPR:$rd, GPRPairRV32:$rs, 0)>;
   def : InstAlias<"pncvth.b $rd, $rs", (PNSRLI_B GPR:$rd, GPRPairRV32:$rs, 8)>;
   def : InstAlias<"pncvth.h $rd, $rs", (PNSRLI_H GPR:$rd, GPRPairRV32:$rs, 16)>;
+
+  def : InstAlias<"mvd $rd, $rs", (PADD_DW GPRPairRV32:$rd, X0_Pair, GPRPairRV32:$rs)>;
 } // append Predicates = [IsRV32]
 } // Predicates = [HasStdExtP]
 
diff --git a/llvm/lib/Target/RISCV/RISCVMoveMerger.cpp b/llvm/lib/Target/RISCV/RISCVMoveMerger.cpp
index a242ec3594bd8..a72642aad60ab 100644
--- a/llvm/lib/Target/RISCV/RISCVMoveMerger.cpp
+++ b/llvm/lib/Target/RISCV/RISCVMoveMerger.cpp
@@ -75,16 +75,6 @@ char RISCVMoveMerge::ID = 0;
 INITIALIZE_PASS(RISCVMoveMerge, "riscv-move-merge", RISCV_MOVE_MERGE_NAME,
                 false, false)
 
-static unsigned getGPRPairCopyOpcode(const RISCVSubtarget &ST) {
-  if (ST.hasStdExtZdinx())
-    return RISCV::FSGNJ_D_IN32X;
-
-  if (ST.hasStdExtP())
-    return RISCV::PADD_DW;
-
-  llvm_unreachable("Unhandled subtarget with paired move.");
-}
-
 static unsigned getCM_MVOpcode(const RISCVSubtarget &ST, bool MoveFromSToA) {
   if (ST.hasStdExtZcmp())
     return MoveFromSToA ? RISCV::CM_MVA01S : RISCV::CM_MVSA01;
@@ -186,25 +176,29 @@ RISCVMoveMerge::mergeGPRPairInsns(MachineBasicBlock::iterator I,
   // flag.
   MachineOperand PairedSource = *SecondPair.Source;
 
-  unsigned Opcode = getGPRPairCopyOpcode(*ST);
   for (auto It = std::next(I); It != Paired && PairedSource.isKill(); ++It)
     if (It->readsRegister(PairedSource.getReg(), TRI))
       PairedSource.setIsKill(false);
 
-  Register SrcReg1, SrcReg2, DestReg;
   unsigned GPRPairIdx =
       RegPairIsEven ? RISCV::sub_gpr_even : RISCV::sub_gpr_odd;
-  SrcReg1 = TRI->getMatchingSuperReg(FirstPair.Source->getReg(), GPRPairIdx,
-                                     &RISCV::GPRPairRegClass);
-  SrcReg2 = ST->hasStdExtZdinx() ? SrcReg1 : Register(RISCV::X0_Pair);
-  DestReg = TRI->getMatchingSuperReg(FirstPair.Destination->getReg(),
-                                     GPRPairIdx, &RISCV::GPRPairRegClass);
-
-  BuildMI(*I->getParent(), I, DL, TII->get(Opcode), DestReg)
-      .addReg(SrcReg1, getKillRegState(PairedSource.isKill() &&
-                                       FirstPair.Source->isKill()))
-      .addReg(SrcReg2, getKillRegState(PairedSource.isKill() &&
-                                       FirstPair.Source->isKill()));
+  MCRegister SrcReg = TRI->getMatchingSuperReg(
+      FirstPair.Source->getReg(), GPRPairIdx, &RISCV::GPRPairRegClass);
+  MCRegister DestReg = TRI->getMatchingSuperReg(
+      FirstPair.Destination->getReg(), GPRPairIdx, &RISCV::GPRPairRegClass);
+  bool SrcKill = PairedSource.isKill() && FirstPair.Source->isKill();
+
+  if (ST->hasStdExtZdinx()) {
+    BuildMI(*I->getParent(), I, DL, TII->get(RISCV::FSGNJ_D_IN32X), DestReg)
+        .addReg(SrcReg)
+        .addReg(SrcReg, getKillRegState(SrcKill));
+  } else if (ST->hasStdExtP()) {
+    BuildMI(*I->getParent(), I, DL, TII->get(RISCV::PADD_DW), DestReg)
+        .addReg(RISCV::X0_Pair)
+        .addReg(SrcReg, getKillRegState(SrcKill));
+  } else {
+    llvm_unreachable("Unhandled subtarget with paired move.");
+  }
 
   I->eraseFromParent();
   Paired->eraseFromParent();
diff --git a/llvm/test/CodeGen/RISCV/calling-conv-p-ext-vector.ll b/llvm/test/CodeGen/RISCV/calling-conv-p-ext-vector.ll
index 30aca1db3ed9c..be096e0b95ce4 100644
--- a/llvm/test/CodeGen/RISCV/calling-conv-p-ext-vector.ll
+++ b/llvm/test/CodeGen/RISCV/calling-conv-p-ext-vector.ll
@@ -206,7 +206,7 @@ define <8 x i8> @test_call_v8i8(<8 x i8> %a, <8 x i8> %b) {
 ; RV32-NEXT:    .cfi_offset ra, -4
 ; RV32-NEXT:    mv a4, a1
 ; RV32-NEXT:    mv a5, a0
-; RV32-NEXT:    padd.dw a0, a2, zero
+; RV32-NEXT:    mvd a0, a2
 ; RV32-NEXT:    mv a2, a5
 ; RV32-NEXT:    mv a3, a4
 ; RV32-NEXT:    call external_v8i8
diff --git a/llvm/test/CodeGen/RISCV/make-compressible-zilsd.mir b/llvm/test/CodeGen/RISCV/make-compressible-zilsd.mir
index 0c22db20d4a68..9123093ca0128 100644
--- a/llvm/test/CodeGen/RISCV/make-compressible-zilsd.mir
+++ b/llvm/test/CodeGen/RISCV/make-compressible-zilsd.mir
@@ -130,7 +130,7 @@ body:             |
     ; RV32_P-LABEL: name: store_common_value_double
     ; RV32_P: liveins: $x10, $x11, $x12, $x16, $x17
     ; RV32_P-NEXT: {{  $}}
-    ; RV32_P-NEXT: $x14_x15 = PADD_DW $x16_x17, $x0_pair
+    ; RV32_P-NEXT: $x14_x15 = PADD_DW $x0_pair, $x16_x17
     ; RV32_P-NEXT: SD_RV32 $x14_x15, killed renamable $x10, 0 :: (store (s64) into %ir.a)
     ; RV32_P-NEXT: SD_RV32 $x14_x15, killed renamable $x11, 0 :: (store (s64) into %ir.b)
     ; RV32_P-NEXT: SD_RV32 killed $x14_x15, killed renamable $x12, 0 :: (store (s64) into %ir.c)
diff --git a/llvm/test/CodeGen/RISCV/rv32-merge-non-arg-reg.mir b/llvm/test/CodeGen/RISCV/rv32-merge-non-arg-reg.mir
index ab3de8c8918fa..094c77461a920 100644
--- a/llvm/test/CodeGen/RISCV/rv32-merge-non-arg-reg.mir
+++ b/llvm/test/CodeGen/RISCV/rv32-merge-non-arg-reg.mir
@@ -18,7 +18,7 @@ body:             |
     ; P-EXT-LABEL: name: merge_copy_non_arg_reg
     ; P-EXT: liveins: $x28, $x29
     ; P-EXT-NEXT: {{  $}}
-    ; P-EXT-NEXT: $x6_x7 = PADD_DW $x28_x29, $x0_pair
+    ; P-EXT-NEXT: $x6_x7 = PADD_DW $x0_pair, $x28_x29
     ; P-EXT-NEXT: PseudoRET implicit $x6
     $x6 = COPY $x28
     $x7 = COPY $x29
@@ -40,7 +40,7 @@ body:             |
     ; P-EXT-LABEL: name: merge_copy_non_arg_reg_with_intervening_unrelated_copy
     ; P-EXT: liveins: $x12, $x28, $x29
     ; P-EXT-NEXT: {{  $}}
-    ; P-EXT-NEXT: $x6_x7 = PADD_DW $x28_x29, $x0_pair
+    ; P-EXT-NEXT: $x6_x7 = PADD_DW $x0_pair, $x28_x29
     ; P-EXT-NEXT: $x18 = ADDI $x12, 0
     ; P-EXT-NEXT: PseudoRET implicit $x6
     $x6 = COPY $x28
diff --git a/llvm/test/CodeGen/RISCV/rv32-move-merge-crash.ll b/llvm/test/CodeGen/RISCV/rv32-move-merge-crash.ll
index 801de62ce1f54..e25ac02cfad51 100644
--- a/llvm/test/CodeGen/RISCV/rv32-move-merge-crash.ll
+++ b/llvm/test/CodeGen/RISCV/rv32-move-merge-crash.ll
@@ -48,7 +48,7 @@ define void @test(i32 %arg0, i32 %arg1) nounwind {
 ; ZCMP-P-NEXT:    #NO_APP
 ; ZCMP-P-NEXT:    #APP
 ; ZCMP-P-NEXT:    #NO_APP
-; ZCMP-P-NEXT:    padd.dw a0, a4, zero
+; ZCMP-P-NEXT:    mvd a0, a4
 ; ZCMP-P-NEXT:    tail foo
 ;
 ; ZCMP-P64-LABEL: test:
diff --git a/llvm/test/CodeGen/RISCV/rv32-move-merge.ll b/llvm/test/CodeGen/RISCV/rv32-move-merge.ll
index 646cee53d4bcf..2dcda981d8641 100644
--- a/llvm/test/CodeGen/RISCV/rv32-move-merge.ll
+++ b/llvm/test/CodeGen/RISCV/rv32-move-merge.ll
@@ -34,7 +34,7 @@ define i64 @mv_to_fmv(i64 %a, i64 %b) nounwind {
 ; CHECK32P-NEXT:    sw s1, 4(sp) # 4-byte Folded Spill
 ; CHECK32P-NEXT:    addd s0, a0, a2
 ; CHECK32P-NEXT:    call foo
-; CHECK32P-NEXT:    padd.dw a0, s0, zero
+; CHECK32P-NEXT:    mvd a0, s0
 ; CHECK32P-NEXT:    lw ra, 12(sp) # 4-byte Folded Reload
 ; CHECK32P-NEXT:    lw s0, 8(sp) # 4-byte Folded Reload
 ; CHECK32P-NEXT:    lw s1, 4(sp) # 4-byte Folded Reload
diff --git a/llvm/test/CodeGen/RISCV/rv32p.ll b/llvm/test/CodeGen/RISCV/rv32p.ll
index 0cd07672532dd..aca224c397ad2 100644
--- a/llvm/test/CodeGen/RISCV/rv32p.ll
+++ b/llvm/test/CodeGen/RISCV/rv32p.ll
@@ -1122,7 +1122,7 @@ define i64 @wmaccu(i32 %a, i32 %b, i64 %c) nounwind {
 ; CHECK-LABEL: wmaccu:
 ; CHECK:       # %bb.0:
 ; CHECK-NEXT:    wmaccu a2, a0, a1
-; CHECK-NEXT:    padd.dw a0, a2, zero
+; CHECK-NEXT:    mvd a0, a2
 ; CHECK-NEXT:    ret
   %aext = zext i32 %a to i64
   %bext = zext i32 %b to i64
@@ -1135,7 +1135,7 @@ define i64 @wmaccu_commute(i32 %a, i32 %b, i64 %c) nounwind {
 ; CHECK-LABEL: wmaccu_commute:
 ; CHECK:       # %bb.0:
 ; CHECK-NEXT:    wmaccu a2, a0, a1
-; CHECK-NEXT:    padd.dw a0, a2, zero
+; CHECK-NEXT:    mvd a0, a2
 ; CHECK-NEXT:    ret
   %aext = zext i32 %a to i64
   %bext = zext i32 %b to i64
@@ -1148,7 +1148,7 @@ define i64 @wmacc(i32 %a, i32 %b, i64 %c) nounwind {
 ; CHECK-LABEL: wmacc:
 ; CHECK:       # %bb.0:
 ; CHECK-NEXT:    wmacc a2, a0, a1
-; CHECK-NEXT:    padd.dw a0, a2, zero
+; CHECK-NEXT:    mvd a0, a2
 ; CHECK-NEXT:    ret
   %aext = sext i32 %a to i64
   %bext = sext i32 %b to i64
@@ -1161,7 +1161,7 @@ define i64 @wmacc_commute(i32 %a, i32 %b, i64 %c) nounwind {
 ; CHECK-LABEL: wmacc_commute:
 ; CHECK:       # %bb.0:
 ; CHECK-NEXT:    wmacc a2, a0, a1
-; CHECK-NEXT:    padd.dw a0, a2, zero
+; CHECK-NEXT:    mvd a0, a2
 ; CHECK-NEXT:    ret
   %aext = sext i32 %a to i64
   %bext = sext i32 %b to i64
@@ -1174,7 +1174,7 @@ define i64 @wmaccsu(i32 %a, i32 %b, i64 %c) nounwind {
 ; CHECK-LABEL: wmaccsu:
 ; CHECK:       # %bb.0:
 ; CHECK-NEXT:    wmaccsu a2, a0, a1
-; CHECK-NEXT:    padd.dw a0, a2, zero
+; CHECK-NEXT:    mvd a0, a2
 ; CHECK-NEXT:    ret
   %aext = sext i32 %a to i64
   %bext = zext i32 %b to i64
@@ -1187,7 +1187,7 @@ define i64 @wmaccsu_commute(i32 %a, i32 %b, i64 %c) nounwind {
 ; CHECK-LABEL: wmaccsu_commute:
 ; CHECK:       # %bb.0:
 ; CHECK-NEXT:    wmaccsu a2, a0, a1
-; CHECK-NEXT:    padd.dw a0, a2, zero
+; CHECK-NEXT:    mvd a0, a2
 ; CHECK-NEXT:    ret
   %aext = sext i32 %a to i64
   %bext = zext i32 %b to i64
@@ -1467,7 +1467,7 @@ define i64 @wmacc_first_mul_multiple_uses(i32 %a, i32 %b, i32 %c, i32 %d, ptr %o
 ; CHECK-NEXT:    mv a5, a3
 ; CHECK-NEXT:    mv a6, a2
 ; CHECK-NEXT:    wmacc a2, a0, a1
-; CHECK-NEXT:    padd.dw a0, a2, zero
+; CHECK-NEXT:    mvd a0, a2
 ; CHECK-NEXT:    sw a6, 0(a4)
 ; CHECK-NEXT:    sw a5, 4(a4)
 ; CHECK-NEXT:    ret
diff --git a/llvm/test/CodeGen/RISCV/rvp-simd-64.ll b/llvm/test/CodeGen/RISCV/rvp-simd-64.ll
index 363b1dd33534e..9fa73c77ff864 100644
--- a/llvm/test/CodeGen/RISCV/rvp-simd-64.ll
+++ b/llvm/test/CodeGen/RISCV/rvp-simd-64.ll
@@ -1863,7 +1863,7 @@ define <4 x i16> @test_pssla_h(<4 x i16> %a, <4 x i16> %b) {
 ; RV32-NEXT:    pack a1, a7, t1
 ; RV32-NEXT:    merge a0, a4, t0
 ; RV32-NEXT:    merge a2, a0, a1
-; RV32-NEXT:    padd.dw a0, a2, zero
+; RV32-NEXT:    mvd a0, a2
 ; RV32-NEXT:    ret
 ;
 ; RV64-LABEL: test_pssla_h:
diff --git a/llvm/test/MC/RISCV/rv32p-aliases-valid.s b/llvm/test/MC/RISCV/rv32p-aliases-valid.s
index c8e517caacc54..a15cb37935df9 100644
--- a/llvm/test/MC/RISCV/rv32p-aliases-valid.s
+++ b/llvm/test/MC/RISCV/rv32p-aliases-valid.s
@@ -286,3 +286,7 @@ li t3, 0x81008100
 # CHECK-S-OBJ-NOALIAS: lui t4, 524296
 # CHECK-S-OBJ: lui t4, 524296
 li t4, 0x80008000
+
+# CHECK-S-OBJ-NOALIAS: padd.dw a0, zero, s0
+# CHECK-S-OBJ: mvd a0, s0
+mvd a0, s0

>From 4777b3cfd979a8e2f1489476ed4ffa5dca202376 Mon Sep 17 00:00:00 2001
From: Craig Topper <craig.topper at sifive.com>
Date: Mon, 22 Jun 2026 23:12:32 -0700
Subject: [PATCH 2/2] fixup! Use copyPhysReg.

---
 llvm/lib/Target/RISCV/RISCVMoveMerger.cpp | 16 +++-------------
 1 file changed, 3 insertions(+), 13 deletions(-)

diff --git a/llvm/lib/Target/RISCV/RISCVMoveMerger.cpp b/llvm/lib/Target/RISCV/RISCVMoveMerger.cpp
index a72642aad60ab..ecd991f9ced0f 100644
--- a/llvm/lib/Target/RISCV/RISCVMoveMerger.cpp
+++ b/llvm/lib/Target/RISCV/RISCVMoveMerger.cpp
@@ -186,19 +186,9 @@ RISCVMoveMerge::mergeGPRPairInsns(MachineBasicBlock::iterator I,
       FirstPair.Source->getReg(), GPRPairIdx, &RISCV::GPRPairRegClass);
   MCRegister DestReg = TRI->getMatchingSuperReg(
       FirstPair.Destination->getReg(), GPRPairIdx, &RISCV::GPRPairRegClass);
-  bool SrcKill = PairedSource.isKill() && FirstPair.Source->isKill();
-
-  if (ST->hasStdExtZdinx()) {
-    BuildMI(*I->getParent(), I, DL, TII->get(RISCV::FSGNJ_D_IN32X), DestReg)
-        .addReg(SrcReg)
-        .addReg(SrcReg, getKillRegState(SrcKill));
-  } else if (ST->hasStdExtP()) {
-    BuildMI(*I->getParent(), I, DL, TII->get(RISCV::PADD_DW), DestReg)
-        .addReg(RISCV::X0_Pair)
-        .addReg(SrcReg, getKillRegState(SrcKill));
-  } else {
-    llvm_unreachable("Unhandled subtarget with paired move.");
-  }
+  bool KillSrc = PairedSource.isKill() && FirstPair.Source->isKill();
+
+  TII->copyPhysReg(*I->getParent(), I, DL, DestReg, SrcReg, KillSrc);
 
   I->eraseFromParent();
   Paired->eraseFromParent();



More information about the llvm-commits mailing list