[llvm] [RISCV] Enhance RISCVMoveMerger for GPRPair Moves on RV32 #180831 (PR #182416)

Kavin Gnanapandithan via llvm-commits llvm-commits at lists.llvm.org
Fri Feb 20 11:14:42 PST 2026


https://github.com/KavinTheG updated https://github.com/llvm/llvm-project/pull/182416

>From 1ca361a656f4116372b577c779c51f596cf4aaae Mon Sep 17 00:00:00 2001
From: Kavin Gnanapandithan <kavin.balag at gmail.com>
Date: Thu, 19 Feb 2026 18:42:33 -0500
Subject: [PATCH 1/3] [RISCV] Enhance RISCVMoveMerger for GPRPair Moves on RV32
 #180831

Extends RISCVMoveMerger to identify adjacent 32-bit moves that can be
combined into a single 64-bit move instruction.
---
 llvm/lib/Target/RISCV/RISCVMoveMerger.cpp     | 142 +++++++++++++++---
 .../RISCV/calling-conv-p-ext-vector.ll        |   3 +-
 llvm/test/CodeGen/RISCV/double-imm.ll         |   3 +-
 .../CodeGen/RISCV/double-intrinsics-strict.ll |   3 +-
 llvm/test/CodeGen/RISCV/double-intrinsics.ll  |   3 +-
 .../CodeGen/RISCV/double-round-conv-sat.ll    |  36 ++---
 llvm/test/CodeGen/RISCV/double-select-fcmp.ll |   3 +-
 llvm/test/CodeGen/RISCV/double-select-icmp.ll |  30 ++--
 .../RISCV/double-stack-spill-restore.ll       |   6 +-
 .../RISCV/fold-addi-loadstore-zilsd.ll        |   3 +-
 .../RISCV/inline-asm-zdinx-constraint-r.ll    |   3 +-
 llvm/test/CodeGen/RISCV/rv32-move-merge.ll    |  46 ++++++
 llvm/test/CodeGen/RISCV/rv32p.ll              |  21 +--
 .../CodeGen/RISCV/zicond-fp-select-zfinx.ll   |  12 +-
 14 files changed, 207 insertions(+), 107 deletions(-)
 create mode 100644 llvm/test/CodeGen/RISCV/rv32-move-merge.ll

diff --git a/llvm/lib/Target/RISCV/RISCVMoveMerger.cpp b/llvm/lib/Target/RISCV/RISCVMoveMerger.cpp
index efea1b422d582..2d5e65fa61de6 100644
--- a/llvm/lib/Target/RISCV/RISCVMoveMerger.cpp
+++ b/llvm/lib/Target/RISCV/RISCVMoveMerger.cpp
@@ -33,6 +33,8 @@ struct RISCVMoveMerge : public MachineFunctionPass {
   // Track which register units have been modified and used.
   LiveRegUnits ModifiedRegUnits, UsedRegUnits;
 
+  bool isRegisterEven(const DestSourcePair &RegPair);
+
   bool isCandidateToMergeMVA01S(const DestSourcePair &RegPair);
   bool isCandidateToMergeMVSA01(const DestSourcePair &RegPair);
   // Merge the two instructions indicated into a single pair instruction.
@@ -40,6 +42,9 @@ struct RISCVMoveMerge : public MachineFunctionPass {
   mergePairedInsns(MachineBasicBlock::iterator I,
                    MachineBasicBlock::iterator Paired, bool MoveFromSToA);
 
+  MachineBasicBlock::iterator
+  findMatchingInstPair(MachineBasicBlock::iterator &MBBI,
+                       const DestSourcePair &RegPair);
   // Look for C.MV instruction that can be combined with
   // the given instruction into CM.MVA01S or CM.MVSA01. Return the matching
   // instruction if one exists.
@@ -59,24 +64,38 @@ char RISCVMoveMerge::ID = 0;
 INITIALIZE_PASS(RISCVMoveMerge, "riscv-move-merge", RISCV_MOVE_MERGE_NAME,
                 false, false)
 
-static unsigned getMoveFromSToAOpcode(const RISCVSubtarget &ST) {
+static unsigned getMoveFromOpcode(const RISCVSubtarget &ST, bool MoveFromSToA) {
   if (ST.hasStdExtZcmp())
-    return RISCV::CM_MVA01S;
+    return MoveFromSToA ? RISCV::CM_MVA01S : RISCV::CM_MVSA01;
 
   if (ST.hasVendorXqccmp())
-    return RISCV::QC_CM_MVA01S;
+    return MoveFromSToA ? RISCV::QC_CM_MVA01S : RISCV::QC_CM_MVSA01;
+
+  if (ST.hasStdExtZdinx()) {
+    if (ST.is64Bit())
+      return RISCV::FSGNJ_D;
+    return RISCV::FSGNJ_D_IN32X;
+  }
+
+  if (ST.hasStdExtP())
+    return RISCV::ADDD;
 
   llvm_unreachable("Unhandled subtarget with paired A to S move.");
 }
 
-static unsigned getMoveFromAToSOpcode(const RISCVSubtarget &ST) {
-  if (ST.hasStdExtZcmp())
-    return RISCV::CM_MVSA01;
+bool RISCVMoveMerge::isRegisterEven(const DestSourcePair &RegPair) {
+  Register Destination = RegPair.Destination->getReg();
+  Register Source = RegPair.Source->getReg();
 
-  if (ST.hasVendorXqccmp())
-    return RISCV::QC_CM_MVSA01;
+  if (Source == Destination)
+    return false;
+
+  Register SrcPair = TRI->getMatchingSuperReg(Source, RISCV::sub_gpr_even,
+                                              &RISCV::GPRPairRegClass);
+  Register DestPair = TRI->getMatchingSuperReg(Destination, RISCV::sub_gpr_even,
+                                               &RISCV::GPRPairRegClass);
 
-  llvm_unreachable("Unhandled subtarget with paired S to A move");
+  return SrcPair.isValid() && DestPair.isValid();
 }
 
 // Check if registers meet CM.MVA01S constraints.
@@ -84,7 +103,8 @@ bool RISCVMoveMerge::isCandidateToMergeMVA01S(const DestSourcePair &RegPair) {
   Register Destination = RegPair.Destination->getReg();
   Register Source = RegPair.Source->getReg();
   // If destination is not a0 or a1.
-  if ((Destination == RISCV::X10 || Destination == RISCV::X11) &&
+  if ((ST->hasStdExtZcmp() || ST->hasVendorXqccmp()) &&
+      (Destination == RISCV::X10 || Destination == RISCV::X11) &&
       RISCV::SR07RegClass.contains(Source))
     return true;
   return false;
@@ -95,7 +115,8 @@ bool RISCVMoveMerge::isCandidateToMergeMVSA01(const DestSourcePair &RegPair) {
   Register Destination = RegPair.Destination->getReg();
   Register Source = RegPair.Source->getReg();
   // If Source is s0 - s7.
-  if ((Source == RISCV::X10 || Source == RISCV::X11) &&
+  if ((ST->hasStdExtZcmp() || ST->hasVendorXqccmp()) &&
+      (Source == RISCV::X10 || Source == RISCV::X11) &&
       RISCV::SR07RegClass.contains(Destination))
     return true;
   return false;
@@ -128,35 +149,102 @@ RISCVMoveMerge::mergePairedInsns(MachineBasicBlock::iterator I,
   //
   //   mv a0, s2
   //   mv a1, s1    =>  cm.mva01s s2,s1
-  unsigned Opcode;
-  if (MoveFromSToA) {
-    // We are moving one of the copies earlier so its kill flag may become
-    // invalid. Clear the copied kill flag if there are any reads of the
-    // register between the new location and the old location.
+  unsigned Opcode = getMoveFromOpcode(*ST, MoveFromSToA);
+  // We are moving one of the copies earlier so its kill flag may become
+  // invalid. Clear the copied kill flag if there are any reads of the
+  // register between the new location and the old location.
+  if (MoveFromSToA || (!ST->hasStdExtZcmp() && !ST->hasVendorXqccmp()))
     for (auto It = std::next(I); It != Paired && PairedSource.isKill(); ++It)
       if (It->readsRegister(PairedSource.getReg(), TRI))
         PairedSource.setIsKill(false);
 
-    Opcode = getMoveFromSToAOpcode(*ST);
+  if (MoveFromSToA) {
     Sreg1 = FirstPair.Source;
     Sreg2 = &PairedSource;
     if (FirstPair.Destination->getReg() != RISCV::X10)
       std::swap(Sreg1, Sreg2);
   } else {
-    Opcode = getMoveFromAToSOpcode(*ST);
     Sreg1 = FirstPair.Destination;
     Sreg2 = PairedRegs.Destination;
     if (FirstPair.Source->getReg() != RISCV::X10)
       std::swap(Sreg1, Sreg2);
   }
 
-  BuildMI(*I->getParent(), I, DL, TII->get(Opcode)).add(*Sreg1).add(*Sreg2);
+  if (!ST->hasStdExtZcmp() && !ST->hasVendorXqccmp()) {
+    Register SrcReg1, SrcReg2, DestReg;
+
+    SrcReg1 =
+        TRI->getMatchingSuperReg(FirstPair.Source->getReg(),
+                                 RISCV::sub_gpr_even, &RISCV::GPRPairRegClass);
+    SrcReg2 =
+        ST->hasStdExtZdinx()
+            ? SrcReg1
+            : Register(TRI->getMatchingSuperReg(RISCV::X0, RISCV::sub_gpr_even,
+                                                &RISCV::GPRPairRegClass));
+    DestReg =
+        TRI->getMatchingSuperReg(FirstPair.Destination->getReg(),
+                                 RISCV::sub_gpr_even, &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()));
+  } else
+    BuildMI(*I->getParent(), I, DL, TII->get(Opcode)).add(*Sreg1).add(*Sreg2);
 
   I->eraseFromParent();
   Paired->eraseFromParent();
   return NextI;
 }
 
+MachineBasicBlock::iterator
+RISCVMoveMerge::findMatchingInstPair(MachineBasicBlock::iterator &MBBI,
+                                     const DestSourcePair &RegPair) {
+  MachineBasicBlock::iterator E = MBBI->getParent()->end();
+  ModifiedRegUnits.clear();
+  UsedRegUnits.clear();
+
+  for (MachineBasicBlock::iterator I = next_nodbg(MBBI, E); I != E;
+       I = next_nodbg(I, E)) {
+
+    MachineInstr &MI = *I;
+
+    if (auto SecondPair = TII->isCopyInstrImpl(MI)) {
+      Register SourceReg = SecondPair->Source->getReg();
+      Register DestReg = SecondPair->Destination->getReg();
+
+      if (RegPair.Destination->getReg() == DestReg ||
+          RegPair.Source->getReg() == SourceReg)
+        return E;
+
+      // Get the register pair from the even half
+      Register SrcGPRPair = TRI->getMatchingSuperReg(RegPair.Source->getReg(),
+                                                     RISCV::sub_gpr_even,
+                                                     &RISCV::GPRPairRegClass);
+
+      Register DestGPRPair = TRI->getMatchingSuperReg(
+          RegPair.Destination->getReg(), RISCV::sub_gpr_even,
+          &RISCV::GPRPairRegClass);
+
+      // Check if the second pair match the odd registers of the GPR pair.
+      if (SourceReg != TRI->getSubReg(SrcGPRPair, RISCV::sub_gpr_odd) ||
+          DestReg != TRI->getSubReg(DestGPRPair, RISCV::sub_gpr_odd))
+        return E;
+
+      if (!ModifiedRegUnits.available(DestReg) ||
+          !UsedRegUnits.available(DestReg) ||
+          !ModifiedRegUnits.available(SourceReg))
+        return E;
+
+      return I;
+    }
+    // Update modified / used register units.
+    LiveRegUnits::accumulateUsedDefed(MI, ModifiedRegUnits, UsedRegUnits, TRI);
+  }
+  return E;
+}
+
 MachineBasicBlock::iterator
 RISCVMoveMerge::findMatchingInst(MachineBasicBlock::iterator &MBBI,
                                  bool MoveFromSToA,
@@ -217,13 +305,17 @@ bool RISCVMoveMerge::mergeMoveSARegPair(MachineBasicBlock &MBB) {
     auto RegPair = TII->isCopyInstrImpl(*MBBI);
     if (RegPair.has_value()) {
       bool MoveFromSToA = isCandidateToMergeMVA01S(*RegPair);
-      if (!MoveFromSToA && !isCandidateToMergeMVSA01(*RegPair)) {
+      bool IsEven = isRegisterEven(*RegPair);
+      if (!MoveFromSToA && !isCandidateToMergeMVSA01(*RegPair) && !IsEven) {
         ++MBBI;
         continue;
       }
 
-      MachineBasicBlock::iterator Paired =
-          findMatchingInst(MBBI, MoveFromSToA, RegPair.value());
+      MachineBasicBlock::iterator Paired = E;
+      if (ST->hasStdExtZcmp() || ST->hasVendorXqccmp())
+        Paired = findMatchingInst(MBBI, MoveFromSToA, RegPair.value());
+      else if (IsEven)
+        Paired = findMatchingInstPair(MBBI, RegPair.value());
       // If matching instruction can be found merge them.
       if (Paired != E) {
         MBBI = mergePairedInsns(MBBI, Paired, MoveFromSToA);
@@ -241,7 +333,11 @@ bool RISCVMoveMerge::runOnMachineFunction(MachineFunction &Fn) {
     return false;
 
   ST = &Fn.getSubtarget<RISCVSubtarget>();
-  if (!ST->hasStdExtZcmp() && !ST->hasVendorXqccmp())
+  if (!ST->hasStdExtZcmp() && !ST->hasVendorXqccmp() && !ST->hasStdExtZdinx() &&
+      !ST->hasStdExtP())
+    return false;
+
+  if (ST->hasStdExtP() && ST->is64Bit())
     return false;
 
   TII = ST->getInstrInfo();
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 a17bd34dff1c9..a7f7a44bc2013 100644
--- a/llvm/test/CodeGen/RISCV/calling-conv-p-ext-vector.ll
+++ b/llvm/test/CodeGen/RISCV/calling-conv-p-ext-vector.ll
@@ -211,8 +211,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:    mv a0, a2
-; RV32-NEXT:    mv a1, a3
+; RV32-NEXT:    addd a0, a2, zero
 ; RV32-NEXT:    mv a2, a5
 ; RV32-NEXT:    mv a3, a4
 ; RV32-NEXT:    call external_v8i8
diff --git a/llvm/test/CodeGen/RISCV/double-imm.ll b/llvm/test/CodeGen/RISCV/double-imm.ll
index 6f7c30edba3ea..9f31c95f64dc3 100644
--- a/llvm/test/CodeGen/RISCV/double-imm.ll
+++ b/llvm/test/CodeGen/RISCV/double-imm.ll
@@ -153,8 +153,7 @@ define dso_local double @negzero_sel(i16 noundef %a, double noundef %d) nounwind
 ; CHECKRV32ZDINX-NEXT:    mv a3, a2
 ; CHECKRV32ZDINX-NEXT:    mv a2, a1
 ; CHECKRV32ZDINX-NEXT:  .LBB4_3: # %entry
-; CHECKRV32ZDINX-NEXT:    mv a0, a2
-; CHECKRV32ZDINX-NEXT:    mv a1, a3
+; CHECKRV32ZDINX-NEXT:    fmv.d a0, a2
 ; CHECKRV32ZDINX-NEXT:    ret
 ;
 ; CHECKRV64ZDINX-LABEL: negzero_sel:
diff --git a/llvm/test/CodeGen/RISCV/double-intrinsics-strict.ll b/llvm/test/CodeGen/RISCV/double-intrinsics-strict.ll
index 117a00dce4b10..53fcfa19725db 100644
--- a/llvm/test/CodeGen/RISCV/double-intrinsics-strict.ll
+++ b/llvm/test/CodeGen/RISCV/double-intrinsics-strict.ll
@@ -281,8 +281,7 @@ define double @sincos_f64(double %a) nounwind strictfp {
 ; RV32IZFINXZDINX-NEXT:    mv s0, a1
 ; RV32IZFINXZDINX-NEXT:    mv s1, a0
 ; RV32IZFINXZDINX-NEXT:    call sin
-; RV32IZFINXZDINX-NEXT:    mv s2, a0
-; RV32IZFINXZDINX-NEXT:    mv s3, a1
+; RV32IZFINXZDINX-NEXT:    fmv.d s2, a0
 ; RV32IZFINXZDINX-NEXT:    mv a0, s1
 ; RV32IZFINXZDINX-NEXT:    mv a1, s0
 ; RV32IZFINXZDINX-NEXT:    call cos
diff --git a/llvm/test/CodeGen/RISCV/double-intrinsics.ll b/llvm/test/CodeGen/RISCV/double-intrinsics.ll
index 81e6d84af17cb..a1be9bce43a4c 100644
--- a/llvm/test/CodeGen/RISCV/double-intrinsics.ll
+++ b/llvm/test/CodeGen/RISCV/double-intrinsics.ll
@@ -236,8 +236,7 @@ define double @sincos_f64(double %a) nounwind {
 ; RV32IZFINXZDINX-NEXT:    mv s0, a1
 ; RV32IZFINXZDINX-NEXT:    mv s1, a0
 ; RV32IZFINXZDINX-NEXT:    call sin
-; RV32IZFINXZDINX-NEXT:    mv s2, a0
-; RV32IZFINXZDINX-NEXT:    mv s3, a1
+; RV32IZFINXZDINX-NEXT:    fmv.d s2, a0
 ; RV32IZFINXZDINX-NEXT:    mv a0, s1
 ; RV32IZFINXZDINX-NEXT:    mv a1, s0
 ; RV32IZFINXZDINX-NEXT:    call cos
diff --git a/llvm/test/CodeGen/RISCV/double-round-conv-sat.ll b/llvm/test/CodeGen/RISCV/double-round-conv-sat.ll
index 1fb3d34907caa..c40043da12723 100644
--- a/llvm/test/CodeGen/RISCV/double-round-conv-sat.ll
+++ b/llvm/test/CodeGen/RISCV/double-round-conv-sat.ll
@@ -96,8 +96,7 @@ define i64 @test_floor_si64(double %x) nounwind {
 ; RV32IZFINXZDINX-NEXT:    sw s0, 8(sp) # 4-byte Folded Spill
 ; RV32IZFINXZDINX-NEXT:    sw s1, 4(sp) # 4-byte Folded Spill
 ; RV32IZFINXZDINX-NEXT:    call floor
-; RV32IZFINXZDINX-NEXT:    mv s0, a0
-; RV32IZFINXZDINX-NEXT:    mv s1, a1
+; RV32IZFINXZDINX-NEXT:    fmv.d s0, a0
 ; RV32IZFINXZDINX-NEXT:    call __fixdfdi
 ; RV32IZFINXZDINX-NEXT:    lui a2, %hi(.LCPI1_0)
 ; RV32IZFINXZDINX-NEXT:    lw a4, %lo(.LCPI1_0)(a2)
@@ -225,8 +224,7 @@ define i64 @test_floor_ui64(double %x) nounwind {
 ; RV32IZFINXZDINX-NEXT:    lw a4, %lo(.LCPI3_0)(a2)
 ; RV32IZFINXZDINX-NEXT:    addi a2, a2, %lo(.LCPI3_0)
 ; RV32IZFINXZDINX-NEXT:    lw a5, 4(a2)
-; RV32IZFINXZDINX-NEXT:    mv s0, a0
-; RV32IZFINXZDINX-NEXT:    mv s1, a1
+; RV32IZFINXZDINX-NEXT:    fmv.d s0, a0
 ; RV32IZFINXZDINX-NEXT:    flt.d a0, a4, s0
 ; RV32IZFINXZDINX-NEXT:    neg s2, a0
 ; RV32IZFINXZDINX-NEXT:    mv a0, s0
@@ -345,8 +343,7 @@ define i64 @test_ceil_si64(double %x) nounwind {
 ; RV32IZFINXZDINX-NEXT:    sw s0, 8(sp) # 4-byte Folded Spill
 ; RV32IZFINXZDINX-NEXT:    sw s1, 4(sp) # 4-byte Folded Spill
 ; RV32IZFINXZDINX-NEXT:    call ceil
-; RV32IZFINXZDINX-NEXT:    mv s0, a0
-; RV32IZFINXZDINX-NEXT:    mv s1, a1
+; RV32IZFINXZDINX-NEXT:    fmv.d s0, a0
 ; RV32IZFINXZDINX-NEXT:    call __fixdfdi
 ; RV32IZFINXZDINX-NEXT:    lui a2, %hi(.LCPI5_0)
 ; RV32IZFINXZDINX-NEXT:    lw a4, %lo(.LCPI5_0)(a2)
@@ -474,8 +471,7 @@ define i64 @test_ceil_ui64(double %x) nounwind {
 ; RV32IZFINXZDINX-NEXT:    lw a4, %lo(.LCPI7_0)(a2)
 ; RV32IZFINXZDINX-NEXT:    addi a2, a2, %lo(.LCPI7_0)
 ; RV32IZFINXZDINX-NEXT:    lw a5, 4(a2)
-; RV32IZFINXZDINX-NEXT:    mv s0, a0
-; RV32IZFINXZDINX-NEXT:    mv s1, a1
+; RV32IZFINXZDINX-NEXT:    fmv.d s0, a0
 ; RV32IZFINXZDINX-NEXT:    flt.d a0, a4, s0
 ; RV32IZFINXZDINX-NEXT:    neg s2, a0
 ; RV32IZFINXZDINX-NEXT:    mv a0, s0
@@ -594,8 +590,7 @@ define i64 @test_trunc_si64(double %x) nounwind {
 ; RV32IZFINXZDINX-NEXT:    sw s0, 8(sp) # 4-byte Folded Spill
 ; RV32IZFINXZDINX-NEXT:    sw s1, 4(sp) # 4-byte Folded Spill
 ; RV32IZFINXZDINX-NEXT:    call trunc
-; RV32IZFINXZDINX-NEXT:    mv s0, a0
-; RV32IZFINXZDINX-NEXT:    mv s1, a1
+; RV32IZFINXZDINX-NEXT:    fmv.d s0, a0
 ; RV32IZFINXZDINX-NEXT:    call __fixdfdi
 ; RV32IZFINXZDINX-NEXT:    lui a2, %hi(.LCPI9_0)
 ; RV32IZFINXZDINX-NEXT:    lw a4, %lo(.LCPI9_0)(a2)
@@ -723,8 +718,7 @@ define i64 @test_trunc_ui64(double %x) nounwind {
 ; RV32IZFINXZDINX-NEXT:    lw a4, %lo(.LCPI11_0)(a2)
 ; RV32IZFINXZDINX-NEXT:    addi a2, a2, %lo(.LCPI11_0)
 ; RV32IZFINXZDINX-NEXT:    lw a5, 4(a2)
-; RV32IZFINXZDINX-NEXT:    mv s0, a0
-; RV32IZFINXZDINX-NEXT:    mv s1, a1
+; RV32IZFINXZDINX-NEXT:    fmv.d s0, a0
 ; RV32IZFINXZDINX-NEXT:    flt.d a0, a4, s0
 ; RV32IZFINXZDINX-NEXT:    neg s2, a0
 ; RV32IZFINXZDINX-NEXT:    mv a0, s0
@@ -843,8 +837,7 @@ define i64 @test_round_si64(double %x) nounwind {
 ; RV32IZFINXZDINX-NEXT:    sw s0, 8(sp) # 4-byte Folded Spill
 ; RV32IZFINXZDINX-NEXT:    sw s1, 4(sp) # 4-byte Folded Spill
 ; RV32IZFINXZDINX-NEXT:    call round
-; RV32IZFINXZDINX-NEXT:    mv s0, a0
-; RV32IZFINXZDINX-NEXT:    mv s1, a1
+; RV32IZFINXZDINX-NEXT:    fmv.d s0, a0
 ; RV32IZFINXZDINX-NEXT:    call __fixdfdi
 ; RV32IZFINXZDINX-NEXT:    lui a2, %hi(.LCPI13_0)
 ; RV32IZFINXZDINX-NEXT:    lw a4, %lo(.LCPI13_0)(a2)
@@ -972,8 +965,7 @@ define i64 @test_round_ui64(double %x) nounwind {
 ; RV32IZFINXZDINX-NEXT:    lw a4, %lo(.LCPI15_0)(a2)
 ; RV32IZFINXZDINX-NEXT:    addi a2, a2, %lo(.LCPI15_0)
 ; RV32IZFINXZDINX-NEXT:    lw a5, 4(a2)
-; RV32IZFINXZDINX-NEXT:    mv s0, a0
-; RV32IZFINXZDINX-NEXT:    mv s1, a1
+; RV32IZFINXZDINX-NEXT:    fmv.d s0, a0
 ; RV32IZFINXZDINX-NEXT:    flt.d a0, a4, s0
 ; RV32IZFINXZDINX-NEXT:    neg s2, a0
 ; RV32IZFINXZDINX-NEXT:    mv a0, s0
@@ -1092,8 +1084,7 @@ define i64 @test_roundeven_si64(double %x) nounwind {
 ; RV32IZFINXZDINX-NEXT:    sw s0, 8(sp) # 4-byte Folded Spill
 ; RV32IZFINXZDINX-NEXT:    sw s1, 4(sp) # 4-byte Folded Spill
 ; RV32IZFINXZDINX-NEXT:    call roundeven
-; RV32IZFINXZDINX-NEXT:    mv s0, a0
-; RV32IZFINXZDINX-NEXT:    mv s1, a1
+; RV32IZFINXZDINX-NEXT:    fmv.d s0, a0
 ; RV32IZFINXZDINX-NEXT:    call __fixdfdi
 ; RV32IZFINXZDINX-NEXT:    lui a2, %hi(.LCPI17_0)
 ; RV32IZFINXZDINX-NEXT:    lw a4, %lo(.LCPI17_0)(a2)
@@ -1221,8 +1212,7 @@ define i64 @test_roundeven_ui64(double %x) nounwind {
 ; RV32IZFINXZDINX-NEXT:    lw a4, %lo(.LCPI19_0)(a2)
 ; RV32IZFINXZDINX-NEXT:    addi a2, a2, %lo(.LCPI19_0)
 ; RV32IZFINXZDINX-NEXT:    lw a5, 4(a2)
-; RV32IZFINXZDINX-NEXT:    mv s0, a0
-; RV32IZFINXZDINX-NEXT:    mv s1, a1
+; RV32IZFINXZDINX-NEXT:    fmv.d s0, a0
 ; RV32IZFINXZDINX-NEXT:    flt.d a0, a4, s0
 ; RV32IZFINXZDINX-NEXT:    neg s2, a0
 ; RV32IZFINXZDINX-NEXT:    mv a0, s0
@@ -1341,8 +1331,7 @@ define i64 @test_rint_si64(double %x) nounwind {
 ; RV32IZFINXZDINX-NEXT:    sw s0, 8(sp) # 4-byte Folded Spill
 ; RV32IZFINXZDINX-NEXT:    sw s1, 4(sp) # 4-byte Folded Spill
 ; RV32IZFINXZDINX-NEXT:    call rint
-; RV32IZFINXZDINX-NEXT:    mv s0, a0
-; RV32IZFINXZDINX-NEXT:    mv s1, a1
+; RV32IZFINXZDINX-NEXT:    fmv.d s0, a0
 ; RV32IZFINXZDINX-NEXT:    call __fixdfdi
 ; RV32IZFINXZDINX-NEXT:    lui a2, %hi(.LCPI21_0)
 ; RV32IZFINXZDINX-NEXT:    lw a4, %lo(.LCPI21_0)(a2)
@@ -1470,8 +1459,7 @@ define i64 @test_rint_ui64(double %x) nounwind {
 ; RV32IZFINXZDINX-NEXT:    lw a4, %lo(.LCPI23_0)(a2)
 ; RV32IZFINXZDINX-NEXT:    addi a2, a2, %lo(.LCPI23_0)
 ; RV32IZFINXZDINX-NEXT:    lw a5, 4(a2)
-; RV32IZFINXZDINX-NEXT:    mv s0, a0
-; RV32IZFINXZDINX-NEXT:    mv s1, a1
+; RV32IZFINXZDINX-NEXT:    fmv.d s0, a0
 ; RV32IZFINXZDINX-NEXT:    flt.d a0, a4, s0
 ; RV32IZFINXZDINX-NEXT:    neg s2, a0
 ; RV32IZFINXZDINX-NEXT:    mv a0, s0
diff --git a/llvm/test/CodeGen/RISCV/double-select-fcmp.ll b/llvm/test/CodeGen/RISCV/double-select-fcmp.ll
index 7fd559c05764a..4ef425dc500a2 100644
--- a/llvm/test/CodeGen/RISCV/double-select-fcmp.ll
+++ b/llvm/test/CodeGen/RISCV/double-select-fcmp.ll
@@ -674,8 +674,7 @@ define double @CascadedSelect(double noundef %a) {
 ; CHECKRV32ZDINX-NEXT:  # %bb.2: # %entry
 ; CHECKRV32ZDINX-NEXT:    fmv.d a2, a0
 ; CHECKRV32ZDINX-NEXT:  .LBB20_3: # %entry
-; CHECKRV32ZDINX-NEXT:    mv a0, a2
-; CHECKRV32ZDINX-NEXT:    mv a1, a3
+; CHECKRV32ZDINX-NEXT:    fmv.d a0, a2
 ; CHECKRV32ZDINX-NEXT:    ret
 ;
 ; CHECKRV64ZDINX-LABEL: CascadedSelect:
diff --git a/llvm/test/CodeGen/RISCV/double-select-icmp.ll b/llvm/test/CodeGen/RISCV/double-select-icmp.ll
index 75fa8e513026d..486768aab1f30 100644
--- a/llvm/test/CodeGen/RISCV/double-select-icmp.ll
+++ b/llvm/test/CodeGen/RISCV/double-select-icmp.ll
@@ -24,8 +24,7 @@ define double @select_icmp_eq(i32 signext %a, i32 signext %b, double %c, double
 ; RV32ZDINX-NEXT:  # %bb.1:
 ; RV32ZDINX-NEXT:    fmv.d a2, a4
 ; RV32ZDINX-NEXT:  .LBB0_2:
-; RV32ZDINX-NEXT:    mv a0, a2
-; RV32ZDINX-NEXT:    mv a1, a3
+; RV32ZDINX-NEXT:    fmv.d a0, a2
 ; RV32ZDINX-NEXT:    ret
 ;
 ; RV64ZDINX-LABEL: select_icmp_eq:
@@ -56,8 +55,7 @@ define double @select_icmp_ne(i32 signext %a, i32 signext %b, double %c, double
 ; RV32ZDINX-NEXT:  # %bb.1:
 ; RV32ZDINX-NEXT:    fmv.d a2, a4
 ; RV32ZDINX-NEXT:  .LBB1_2:
-; RV32ZDINX-NEXT:    mv a0, a2
-; RV32ZDINX-NEXT:    mv a1, a3
+; RV32ZDINX-NEXT:    fmv.d a0, a2
 ; RV32ZDINX-NEXT:    ret
 ;
 ; RV64ZDINX-LABEL: select_icmp_ne:
@@ -88,8 +86,7 @@ define double @select_icmp_ugt(i32 signext %a, i32 signext %b, double %c, double
 ; RV32ZDINX-NEXT:  # %bb.1:
 ; RV32ZDINX-NEXT:    fmv.d a2, a4
 ; RV32ZDINX-NEXT:  .LBB2_2:
-; RV32ZDINX-NEXT:    mv a0, a2
-; RV32ZDINX-NEXT:    mv a1, a3
+; RV32ZDINX-NEXT:    fmv.d a0, a2
 ; RV32ZDINX-NEXT:    ret
 ;
 ; RV64ZDINX-LABEL: select_icmp_ugt:
@@ -120,8 +117,7 @@ define double @select_icmp_uge(i32 signext %a, i32 signext %b, double %c, double
 ; RV32ZDINX-NEXT:  # %bb.1:
 ; RV32ZDINX-NEXT:    fmv.d a2, a4
 ; RV32ZDINX-NEXT:  .LBB3_2:
-; RV32ZDINX-NEXT:    mv a0, a2
-; RV32ZDINX-NEXT:    mv a1, a3
+; RV32ZDINX-NEXT:    fmv.d a0, a2
 ; RV32ZDINX-NEXT:    ret
 ;
 ; RV64ZDINX-LABEL: select_icmp_uge:
@@ -152,8 +148,7 @@ define double @select_icmp_ult(i32 signext %a, i32 signext %b, double %c, double
 ; RV32ZDINX-NEXT:  # %bb.1:
 ; RV32ZDINX-NEXT:    fmv.d a2, a4
 ; RV32ZDINX-NEXT:  .LBB4_2:
-; RV32ZDINX-NEXT:    mv a0, a2
-; RV32ZDINX-NEXT:    mv a1, a3
+; RV32ZDINX-NEXT:    fmv.d a0, a2
 ; RV32ZDINX-NEXT:    ret
 ;
 ; RV64ZDINX-LABEL: select_icmp_ult:
@@ -184,8 +179,7 @@ define double @select_icmp_ule(i32 signext %a, i32 signext %b, double %c, double
 ; RV32ZDINX-NEXT:  # %bb.1:
 ; RV32ZDINX-NEXT:    fmv.d a2, a4
 ; RV32ZDINX-NEXT:  .LBB5_2:
-; RV32ZDINX-NEXT:    mv a0, a2
-; RV32ZDINX-NEXT:    mv a1, a3
+; RV32ZDINX-NEXT:    fmv.d a0, a2
 ; RV32ZDINX-NEXT:    ret
 ;
 ; RV64ZDINX-LABEL: select_icmp_ule:
@@ -216,8 +210,7 @@ define double @select_icmp_sgt(i32 signext %a, i32 signext %b, double %c, double
 ; RV32ZDINX-NEXT:  # %bb.1:
 ; RV32ZDINX-NEXT:    fmv.d a2, a4
 ; RV32ZDINX-NEXT:  .LBB6_2:
-; RV32ZDINX-NEXT:    mv a0, a2
-; RV32ZDINX-NEXT:    mv a1, a3
+; RV32ZDINX-NEXT:    fmv.d a0, a2
 ; RV32ZDINX-NEXT:    ret
 ;
 ; RV64ZDINX-LABEL: select_icmp_sgt:
@@ -248,8 +241,7 @@ define double @select_icmp_sge(i32 signext %a, i32 signext %b, double %c, double
 ; RV32ZDINX-NEXT:  # %bb.1:
 ; RV32ZDINX-NEXT:    fmv.d a2, a4
 ; RV32ZDINX-NEXT:  .LBB7_2:
-; RV32ZDINX-NEXT:    mv a0, a2
-; RV32ZDINX-NEXT:    mv a1, a3
+; RV32ZDINX-NEXT:    fmv.d a0, a2
 ; RV32ZDINX-NEXT:    ret
 ;
 ; RV64ZDINX-LABEL: select_icmp_sge:
@@ -280,8 +272,7 @@ define double @select_icmp_slt(i32 signext %a, i32 signext %b, double %c, double
 ; RV32ZDINX-NEXT:  # %bb.1:
 ; RV32ZDINX-NEXT:    fmv.d a2, a4
 ; RV32ZDINX-NEXT:  .LBB8_2:
-; RV32ZDINX-NEXT:    mv a0, a2
-; RV32ZDINX-NEXT:    mv a1, a3
+; RV32ZDINX-NEXT:    fmv.d a0, a2
 ; RV32ZDINX-NEXT:    ret
 ;
 ; RV64ZDINX-LABEL: select_icmp_slt:
@@ -312,8 +303,7 @@ define double @select_icmp_sle(i32 signext %a, i32 signext %b, double %c, double
 ; RV32ZDINX-NEXT:  # %bb.1:
 ; RV32ZDINX-NEXT:    fmv.d a2, a4
 ; RV32ZDINX-NEXT:  .LBB9_2:
-; RV32ZDINX-NEXT:    mv a0, a2
-; RV32ZDINX-NEXT:    mv a1, a3
+; RV32ZDINX-NEXT:    fmv.d a0, a2
 ; RV32ZDINX-NEXT:    ret
 ;
 ; RV64ZDINX-LABEL: select_icmp_sle:
diff --git a/llvm/test/CodeGen/RISCV/double-stack-spill-restore.ll b/llvm/test/CodeGen/RISCV/double-stack-spill-restore.ll
index 4ae912a34d337..1e039e1e88eee 100644
--- a/llvm/test/CodeGen/RISCV/double-stack-spill-restore.ll
+++ b/llvm/test/CodeGen/RISCV/double-stack-spill-restore.ll
@@ -71,14 +71,12 @@ define double @func(double %d, i32 %n) nounwind {
 ; RV32IZFINXZDINX-NEXT:    beqz a2, .LBB0_2
 ; RV32IZFINXZDINX-NEXT:  # %bb.1: # %if.else
 ; RV32IZFINXZDINX-NEXT:    addi a2, a2, -1
-; RV32IZFINXZDINX-NEXT:    mv a0, s0
-; RV32IZFINXZDINX-NEXT:    mv a1, s1
+; RV32IZFINXZDINX-NEXT:    fmv.d a0, s0
 ; RV32IZFINXZDINX-NEXT:    call func
 ; RV32IZFINXZDINX-NEXT:    fadd.d a0, a0, s0
 ; RV32IZFINXZDINX-NEXT:    j .LBB0_3
 ; RV32IZFINXZDINX-NEXT:  .LBB0_2: # %return
-; RV32IZFINXZDINX-NEXT:    mv a0, s0
-; RV32IZFINXZDINX-NEXT:    mv a1, s1
+; RV32IZFINXZDINX-NEXT:    fmv.d a0, s0
 ; RV32IZFINXZDINX-NEXT:  .LBB0_3: # %return
 ; RV32IZFINXZDINX-NEXT:    lw ra, 12(sp) # 4-byte Folded Reload
 ; RV32IZFINXZDINX-NEXT:    lw s0, 8(sp) # 4-byte Folded Reload
diff --git a/llvm/test/CodeGen/RISCV/fold-addi-loadstore-zilsd.ll b/llvm/test/CodeGen/RISCV/fold-addi-loadstore-zilsd.ll
index c01248fa42480..d1cf8a13a9a88 100644
--- a/llvm/test/CodeGen/RISCV/fold-addi-loadstore-zilsd.ll
+++ b/llvm/test/CodeGen/RISCV/fold-addi-loadstore-zilsd.ll
@@ -59,8 +59,7 @@ define double @fold_addi_from_different_bb(i32 %k, i32 %n, ptr %a) nounwind {
 ; CHECK-NEXT:  .LBB2_3:
 ; CHECK-NEXT:    fmv.d s0, zero
 ; CHECK-NEXT:  .LBB2_4: # %for.cond.cleanup
-; CHECK-NEXT:    mv a0, s0
-; CHECK-NEXT:    mv a1, s1
+; CHECK-NEXT:    fmv.d a0, s0
 ; CHECK-NEXT:    lw ra, 28(sp) # 4-byte Folded Reload
 ; CHECK-NEXT:    lw s0, 24(sp) # 4-byte Folded Reload
 ; CHECK-NEXT:    lw s1, 20(sp) # 4-byte Folded Reload
diff --git a/llvm/test/CodeGen/RISCV/inline-asm-zdinx-constraint-r.ll b/llvm/test/CodeGen/RISCV/inline-asm-zdinx-constraint-r.ll
index 57be0e5e4199a..1194fbe1a81ac 100644
--- a/llvm/test/CodeGen/RISCV/inline-asm-zdinx-constraint-r.ll
+++ b/llvm/test/CodeGen/RISCV/inline-asm-zdinx-constraint-r.ll
@@ -66,8 +66,7 @@ define double @constraint_double_abi_name(double %a) nounwind {
 ; RV32FINX-NEXT:    #APP
 ; RV32FINX-NEXT:    fadd.d t1, a0, s0
 ; RV32FINX-NEXT:    #NO_APP
-; RV32FINX-NEXT:    mv a0, t1
-; RV32FINX-NEXT:    mv a1, t2
+; RV32FINX-NEXT:    fmv.d a0, t1
 ; RV32FINX-NEXT:    lw s0, 12(sp) # 4-byte Folded Reload
 ; RV32FINX-NEXT:    lw s1, 8(sp) # 4-byte Folded Reload
 ; RV32FINX-NEXT:    addi sp, sp, 16
diff --git a/llvm/test/CodeGen/RISCV/rv32-move-merge.ll b/llvm/test/CodeGen/RISCV/rv32-move-merge.ll
new file mode 100644
index 0000000000000..5bc0471cb02cd
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/rv32-move-merge.ll
@@ -0,0 +1,46 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc -mtriple=riscv32 -mattr=+zdinx -verify-machineinstrs < %s \
+; RUN:   | FileCheck -check-prefixes=CHECK32ZDINX %s
+; RUN: llc -mtriple=riscv32 -mattr=+experimental-p -verify-machineinstrs < %s \
+; RUN:   | FileCheck -check-prefixes=CHECK32P %s
+
+
+declare void @foo()
+
+define i64 @mv_to_fmv(i64 %a, i64 %b) nounwind {
+; CHECK32ZDINX-LABEL: mv_to_fmv:
+; CHECK32ZDINX:       # %bb.0:
+; CHECK32ZDINX-NEXT:    addi sp, sp, -16
+; CHECK32ZDINX-NEXT:    sw ra, 12(sp) # 4-byte Folded Spill
+; CHECK32ZDINX-NEXT:    sw s0, 8(sp) # 4-byte Folded Spill
+; CHECK32ZDINX-NEXT:    sw s1, 4(sp) # 4-byte Folded Spill
+; CHECK32ZDINX-NEXT:    add a1, a1, a3
+; CHECK32ZDINX-NEXT:    add s0, a0, a2
+; CHECK32ZDINX-NEXT:    sltu s1, s0, a0
+; CHECK32ZDINX-NEXT:    add s1, a1, s1
+; CHECK32ZDINX-NEXT:    call foo
+; CHECK32ZDINX-NEXT:    fmv.d a0, s0
+; CHECK32ZDINX-NEXT:    lw ra, 12(sp) # 4-byte Folded Reload
+; CHECK32ZDINX-NEXT:    lw s0, 8(sp) # 4-byte Folded Reload
+; CHECK32ZDINX-NEXT:    lw s1, 4(sp) # 4-byte Folded Reload
+; CHECK32ZDINX-NEXT:    addi sp, sp, 16
+; CHECK32ZDINX-NEXT:    ret
+;
+; CHECK32P-LABEL: mv_to_fmv:
+; CHECK32P:       # %bb.0:
+; CHECK32P-NEXT:    addi sp, sp, -16
+; CHECK32P-NEXT:    sw ra, 12(sp) # 4-byte Folded Spill
+; CHECK32P-NEXT:    sw s0, 8(sp) # 4-byte Folded Spill
+; CHECK32P-NEXT:    sw s1, 4(sp) # 4-byte Folded Spill
+; CHECK32P-NEXT:    addd s0, a0, a2
+; CHECK32P-NEXT:    call foo
+; CHECK32P-NEXT:    addd a0, s0, zero
+; 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
+; CHECK32P-NEXT:    addi sp, sp, 16
+; CHECK32P-NEXT:    ret
+  %1 = add i64 %a, %b
+  call void @foo()
+  ret i64 %1
+}
diff --git a/llvm/test/CodeGen/RISCV/rv32p.ll b/llvm/test/CodeGen/RISCV/rv32p.ll
index 9f04646b54d75..b3ce9c610a166 100644
--- a/llvm/test/CodeGen/RISCV/rv32p.ll
+++ b/llvm/test/CodeGen/RISCV/rv32p.ll
@@ -814,8 +814,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:    mv a0, a2
-; CHECK-NEXT:    mv a1, a3
+; CHECK-NEXT:    addd a0, a2, zero
 ; CHECK-NEXT:    ret
   %aext = zext i32 %a to i64
   %bext = zext i32 %b to i64
@@ -828,8 +827,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:    mv a0, a2
-; CHECK-NEXT:    mv a1, a3
+; CHECK-NEXT:    addd a0, a2, zero
 ; CHECK-NEXT:    ret
   %aext = zext i32 %a to i64
   %bext = zext i32 %b to i64
@@ -842,8 +840,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:    mv a0, a2
-; CHECK-NEXT:    mv a1, a3
+; CHECK-NEXT:    addd a0, a2, zero
 ; CHECK-NEXT:    ret
   %aext = sext i32 %a to i64
   %bext = sext i32 %b to i64
@@ -856,8 +853,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:    mv a0, a2
-; CHECK-NEXT:    mv a1, a3
+; CHECK-NEXT:    addd a0, a2, zero
 ; CHECK-NEXT:    ret
   %aext = sext i32 %a to i64
   %bext = sext i32 %b to i64
@@ -870,8 +866,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:    mv a0, a2
-; CHECK-NEXT:    mv a1, a3
+; CHECK-NEXT:    addd a0, a2, zero
 ; CHECK-NEXT:    ret
   %aext = sext i32 %a to i64
   %bext = zext i32 %b to i64
@@ -884,8 +879,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:    mv a0, a2
-; CHECK-NEXT:    mv a1, a3
+; CHECK-NEXT:    addd a0, a2, zero
 ; CHECK-NEXT:    ret
   %aext = sext i32 %a to i64
   %bext = zext i32 %b to i64
@@ -925,8 +919,7 @@ define i64 @wmacc_first_mul_multiple_uses(i32 %a, i32 %b, i32 %c, i32 %d, ptr %o
 ; CHECK-NEXT:    wmacc a2, a0, a1
 ; CHECK-NEXT:    sw a6, 0(a4)
 ; CHECK-NEXT:    sw a5, 4(a4)
-; CHECK-NEXT:    mv a0, a2
-; CHECK-NEXT:    mv a1, a3
+; CHECK-NEXT:    addd a0, a2, zero
 ; CHECK-NEXT:    ret
   %aext = sext i32 %a to i64
   %bext = sext i32 %b to i64
diff --git a/llvm/test/CodeGen/RISCV/zicond-fp-select-zfinx.ll b/llvm/test/CodeGen/RISCV/zicond-fp-select-zfinx.ll
index 0e8a0c704207d..3cb3d25beb86c 100644
--- a/llvm/test/CodeGen/RISCV/zicond-fp-select-zfinx.ll
+++ b/llvm/test/CodeGen/RISCV/zicond-fp-select-zfinx.ll
@@ -156,8 +156,7 @@ define double @select_f64_i1(i1 %cond, double %t, double %f) nounwind {
 ; RV32ZDINX_ZICOND-NEXT:    mv a5, a2
 ; RV32ZDINX_ZICOND-NEXT:    mv a4, a1
 ; RV32ZDINX_ZICOND-NEXT:  .LBB1_3: # %entry
-; RV32ZDINX_ZICOND-NEXT:    mv a0, a4
-; RV32ZDINX_ZICOND-NEXT:    mv a1, a5
+; RV32ZDINX_ZICOND-NEXT:    fmv.d a0, a4
 ; RV32ZDINX_ZICOND-NEXT:    ret
 ;
 ; RV32ZDINX_NOZICOND-LABEL: select_f64_i1:
@@ -173,8 +172,7 @@ define double @select_f64_i1(i1 %cond, double %t, double %f) nounwind {
 ; RV32ZDINX_NOZICOND-NEXT:    mv a5, a2
 ; RV32ZDINX_NOZICOND-NEXT:    mv a4, a1
 ; RV32ZDINX_NOZICOND-NEXT:  .LBB1_3: # %entry
-; RV32ZDINX_NOZICOND-NEXT:    mv a0, a4
-; RV32ZDINX_NOZICOND-NEXT:    mv a1, a5
+; RV32ZDINX_NOZICOND-NEXT:    fmv.d a0, a4
 ; RV32ZDINX_NOZICOND-NEXT:    ret
 entry:
   %sel = select i1 %cond, double %t, double %f
@@ -296,8 +294,7 @@ define double @select_f64_fcmp(double %a, double %b, double %c, double %d) nounw
 ; RV32ZDINX_ZICOND-NEXT:  # %bb.1: # %entry
 ; RV32ZDINX_ZICOND-NEXT:    fmv.d a4, a6
 ; RV32ZDINX_ZICOND-NEXT:  .LBB2_2: # %entry
-; RV32ZDINX_ZICOND-NEXT:    mv a0, a4
-; RV32ZDINX_ZICOND-NEXT:    mv a1, a5
+; RV32ZDINX_ZICOND-NEXT:    fmv.d a0, a4
 ; RV32ZDINX_ZICOND-NEXT:    ret
 ;
 ; RV32ZDINX_NOZICOND-LABEL: select_f64_fcmp:
@@ -307,8 +304,7 @@ define double @select_f64_fcmp(double %a, double %b, double %c, double %d) nounw
 ; RV32ZDINX_NOZICOND-NEXT:  # %bb.1: # %entry
 ; RV32ZDINX_NOZICOND-NEXT:    fmv.d a4, a6
 ; RV32ZDINX_NOZICOND-NEXT:  .LBB2_2: # %entry
-; RV32ZDINX_NOZICOND-NEXT:    mv a0, a4
-; RV32ZDINX_NOZICOND-NEXT:    mv a1, a5
+; RV32ZDINX_NOZICOND-NEXT:    fmv.d a0, a4
 ; RV32ZDINX_NOZICOND-NEXT:    ret
 entry:
   %cmp = fcmp ogt double %a, %b

>From 34895d02bd39b7affd8fb910027b7f6ce8487add Mon Sep 17 00:00:00 2001
From: Kavin Gnanapandithan <kavin.balag at gmail.com>
Date: Fri, 20 Feb 2026 12:42:04 -0500
Subject: [PATCH 2/3] Adds a mir test case to test RISCVMoveMerge on registers
 aside from a0/a1, and returns early if subtarget is 64-bit and zdinx.

---
 llvm/lib/Target/RISCV/RISCVMoveMerger.cpp     | 15 +++--------
 .../RISCV/calling-conv-p-ext-vector.ll        |  2 +-
 .../CodeGen/RISCV/rv32-merge-non-arg-reg.mir  | 26 +++++++++++++++++++
 llvm/test/CodeGen/RISCV/rv32-move-merge.ll    |  2 +-
 llvm/test/CodeGen/RISCV/rv32p.ll              | 14 +++++-----
 5 files changed, 39 insertions(+), 20 deletions(-)
 create mode 100644 llvm/test/CodeGen/RISCV/rv32-merge-non-arg-reg.mir

diff --git a/llvm/lib/Target/RISCV/RISCVMoveMerger.cpp b/llvm/lib/Target/RISCV/RISCVMoveMerger.cpp
index 2d5e65fa61de6..b6c707f506ec6 100644
--- a/llvm/lib/Target/RISCV/RISCVMoveMerger.cpp
+++ b/llvm/lib/Target/RISCV/RISCVMoveMerger.cpp
@@ -71,14 +71,11 @@ static unsigned getMoveFromOpcode(const RISCVSubtarget &ST, bool MoveFromSToA) {
   if (ST.hasVendorXqccmp())
     return MoveFromSToA ? RISCV::QC_CM_MVA01S : RISCV::QC_CM_MVSA01;
 
-  if (ST.hasStdExtZdinx()) {
-    if (ST.is64Bit())
-      return RISCV::FSGNJ_D;
+  if (ST.hasStdExtZdinx())
     return RISCV::FSGNJ_D_IN32X;
-  }
 
   if (ST.hasStdExtP())
-    return RISCV::ADDD;
+    return RISCV::PADD_DW;
 
   llvm_unreachable("Unhandled subtarget with paired A to S move.");
 }
@@ -176,11 +173,7 @@ RISCVMoveMerge::mergePairedInsns(MachineBasicBlock::iterator I,
     SrcReg1 =
         TRI->getMatchingSuperReg(FirstPair.Source->getReg(),
                                  RISCV::sub_gpr_even, &RISCV::GPRPairRegClass);
-    SrcReg2 =
-        ST->hasStdExtZdinx()
-            ? SrcReg1
-            : Register(TRI->getMatchingSuperReg(RISCV::X0, RISCV::sub_gpr_even,
-                                                &RISCV::GPRPairRegClass));
+    SrcReg2 = ST->hasStdExtZdinx() ? SrcReg1 : Register(RISCV::X0_Pair);
     DestReg =
         TRI->getMatchingSuperReg(FirstPair.Destination->getReg(),
                                  RISCV::sub_gpr_even, &RISCV::GPRPairRegClass);
@@ -337,7 +330,7 @@ bool RISCVMoveMerge::runOnMachineFunction(MachineFunction &Fn) {
       !ST->hasStdExtP())
     return false;
 
-  if (ST->hasStdExtP() && ST->is64Bit())
+  if ((ST->hasStdExtP() || ST->hasStdExtZdinx()) && ST->is64Bit())
     return false;
 
   TII = ST->getInstrInfo();
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 a7f7a44bc2013..535a7fa168a9a 100644
--- a/llvm/test/CodeGen/RISCV/calling-conv-p-ext-vector.ll
+++ b/llvm/test/CodeGen/RISCV/calling-conv-p-ext-vector.ll
@@ -211,7 +211,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:    addd a0, a2, zero
+; RV32-NEXT:    padd.dw a0, a2, zero
 ; RV32-NEXT:    mv a2, a5
 ; RV32-NEXT:    mv a3, a4
 ; RV32-NEXT:    call external_v8i8
diff --git a/llvm/test/CodeGen/RISCV/rv32-merge-non-arg-reg.mir b/llvm/test/CodeGen/RISCV/rv32-merge-non-arg-reg.mir
new file mode 100644
index 0000000000000..596d3d1c62175
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/rv32-merge-non-arg-reg.mir
@@ -0,0 +1,26 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6
+# RUN: llc -mtriple=riscv32 -mattr=+zdinx -run-pass=postrapseudos,riscv-move-merge %s -o - | FileCheck %s --check-prefix=ZDINX
+# RUN: llc -mtriple=riscv32 -mattr=+experimental-p -run-pass=postrapseudos,riscv-move-merge %s -o - | FileCheck %s --check-prefix=P-EXT
+
+...
+---
+name:            merge_copy_non_arg_reg
+tracksRegLiveness: true
+body:             |
+  bb.0:
+    liveins: $x28, $x29
+    ; ZDINX-LABEL: name: merge_copy_non_arg_reg
+    ; ZDINX: liveins: $x28, $x29
+    ; ZDINX-NEXT: {{  $}}
+    ; ZDINX-NEXT: $x6_x7 = FSGNJ_D_IN32X $x28_x29, $x28_x29
+    ; ZDINX-NEXT: PseudoRET implicit $x6
+    ;
+    ; 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: PseudoRET implicit $x6
+    $x6 = COPY $x28
+    $x7 = COPY $x29
+    PseudoRET implicit $x6
+...
diff --git a/llvm/test/CodeGen/RISCV/rv32-move-merge.ll b/llvm/test/CodeGen/RISCV/rv32-move-merge.ll
index 5bc0471cb02cd..34652b5f0782b 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:    addd a0, s0, zero
+; CHECK32P-NEXT:    padd.dw a0, s0, zero
 ; 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 b3ce9c610a166..8b07e17e2bff6 100644
--- a/llvm/test/CodeGen/RISCV/rv32p.ll
+++ b/llvm/test/CodeGen/RISCV/rv32p.ll
@@ -814,7 +814,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:    addd a0, a2, zero
+; CHECK-NEXT:    padd.dw a0, a2, zero
 ; CHECK-NEXT:    ret
   %aext = zext i32 %a to i64
   %bext = zext i32 %b to i64
@@ -827,7 +827,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:    addd a0, a2, zero
+; CHECK-NEXT:    padd.dw a0, a2, zero
 ; CHECK-NEXT:    ret
   %aext = zext i32 %a to i64
   %bext = zext i32 %b to i64
@@ -840,7 +840,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:    addd a0, a2, zero
+; CHECK-NEXT:    padd.dw a0, a2, zero
 ; CHECK-NEXT:    ret
   %aext = sext i32 %a to i64
   %bext = sext i32 %b to i64
@@ -853,7 +853,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:    addd a0, a2, zero
+; CHECK-NEXT:    padd.dw a0, a2, zero
 ; CHECK-NEXT:    ret
   %aext = sext i32 %a to i64
   %bext = sext i32 %b to i64
@@ -866,7 +866,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:    addd a0, a2, zero
+; CHECK-NEXT:    padd.dw a0, a2, zero
 ; CHECK-NEXT:    ret
   %aext = sext i32 %a to i64
   %bext = zext i32 %b to i64
@@ -879,7 +879,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:    addd a0, a2, zero
+; CHECK-NEXT:    padd.dw a0, a2, zero
 ; CHECK-NEXT:    ret
   %aext = sext i32 %a to i64
   %bext = zext i32 %b to i64
@@ -919,7 +919,7 @@ define i64 @wmacc_first_mul_multiple_uses(i32 %a, i32 %b, i32 %c, i32 %d, ptr %o
 ; CHECK-NEXT:    wmacc a2, a0, a1
 ; CHECK-NEXT:    sw a6, 0(a4)
 ; CHECK-NEXT:    sw a5, 4(a4)
-; CHECK-NEXT:    addd a0, a2, zero
+; CHECK-NEXT:    padd.dw a0, a2, zero
 ; CHECK-NEXT:    ret
   %aext = sext i32 %a to i64
   %bext = sext i32 %b to i64

>From b59901998140c7c36f81ae5bfef0073ff5dab077 Mon Sep 17 00:00:00 2001
From: Kavin Gnanapandithan <kavin.balag at gmail.com>
Date: Fri, 20 Feb 2026 14:12:18 -0500
Subject: [PATCH 3/3] Adds curly brackets around else statement.

---
 llvm/lib/Target/RISCV/RISCVMoveMerger.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/llvm/lib/Target/RISCV/RISCVMoveMerger.cpp b/llvm/lib/Target/RISCV/RISCVMoveMerger.cpp
index b6c707f506ec6..b2194084632a0 100644
--- a/llvm/lib/Target/RISCV/RISCVMoveMerger.cpp
+++ b/llvm/lib/Target/RISCV/RISCVMoveMerger.cpp
@@ -183,8 +183,9 @@ RISCVMoveMerge::mergePairedInsns(MachineBasicBlock::iterator I,
                                          FirstPair.Source->isKill()))
         .addReg(SrcReg2, getKillRegState(PairedSource.isKill() &&
                                          FirstPair.Source->isKill()));
-  } else
+  } else {
     BuildMI(*I->getParent(), I, DL, TII->get(Opcode)).add(*Sreg1).add(*Sreg2);
+  }
 
   I->eraseFromParent();
   Paired->eraseFromParent();



More information about the llvm-commits mailing list