[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