[llvm] [RISCV] Use ADDD for GPR Pair Move with P (PR #180671)
Sam Elliott via llvm-commits
llvm-commits at lists.llvm.org
Mon Feb 9 20:01:48 PST 2026
lenary wrote:
The testing on this one was quite annoying. It seems reasonable to prioritise using `fmv.d` when you have Zdinx, which didn't leave very many existing tests that would reasonably use `ADDD`.
There are lots of places where we get `mv a0, <reg>; mv a1, <reg>` - usually when returning a 64-bit value on rv32. Unfortunately we sort of get the wrong result here, due to register coalescing. For instance, `fold_addi_from_different_bb` in `llvm/test/CodeGen/RISCV/fold-addi-loadstore-zilsd.ll`:
Before Register Coalescer:
```
# Machine code for function fold_addi_from_different_bb: NoPHIs, TracksLiveness, TiedOpsRewritten
Function Live Ins: $x10 in %6, $x11 in %7, $x12 in %8
bb.0.entry:
successors: %bb.2(0x50000000), %bb.1(0x30000000); %bb.2(62.50%), %bb.1(37.50%)
liveins: $x10, $x11, $x12
%8:gpr = COPY $x12
%7:gpr = COPY $x11
%6:gpr = COPY $x10
%10:gpr = COPY $x0
BLT %10:gpr, %7:gpr, %bb.2
bb.1:
; predecessors: %bb.0
successors: %bb.3(0x80000000); %bb.3(100.00%)
%9:gprpair = COPY $x0_pair
%17:gprpair = COPY %9:gprpair
PseudoBR %bb.3
bb.2.for.body.lr.ph:
; predecessors: %bb.0
successors: %bb.4(0x80000000); %bb.4(100.00%)
%12:gpr = nsw SLLI %6:gpr, 4
%13:gpr = ADD %8:gpr, %12:gpr
%11:gprpair = COPY $x0_pair
%18:gpr = COPY %7:gpr
%19:gprpair = COPY %11:gprpair
PseudoBR %bb.4
bb.3.for.cond.cleanup:
; predecessors: %bb.4, %bb.1
%1:gprpair = COPY %17:gprpair
%15:gpr = COPY %1.sub_gpr_even:gprpair
%16:gpr = COPY %1.sub_gpr_odd:gprpair
$x10 = COPY %15:gpr
$x11 = COPY %16:gpr
PseudoRET implicit $x10, implicit $x11
bb.4.for.body:
; predecessors: %bb.2, %bb.4
successors: %bb.3(0x04000000), %bb.4(0x7c000000); %bb.3(3.12%), %bb.4(96.88%)
%3:gprpair = COPY %19:gprpair
%2:gpr = COPY %18:gpr
ADJCALLSTACKDOWN 0, 0, implicit-def dead $x2, implicit $x2
$x10 = COPY %8:gpr
PseudoCALL target-flags(riscv-call) @f, <regmask $vlenb $x0 $x1 $x8 $x9 $x18 $x19 $x20 $x21 $x22 $x23 $x24 $x25 $x26 $x27 $x0_h $x1_h $x8_h $x9_h $x18_h $x19_h $x20_h $x21_h $x22_h $x23_h $x24_h $x25_h $x26_h $x27_h $x0_w $x1_w $x8_w $x9_w and 17 more...>, implicit-def dead $x1, implicit $x10, implicit-def $x2
ADJCALLSTACKUP 0, 0, implicit-def dead $x2, implicit $x2
%14:gprpair = LD_RV32 %13:gpr, 8 :: (load (s64) from %ir.y)
%4:gprpair = nofpexcept FADD_D_IN32X %14:gprpair, %3:gprpair, 7, implicit $frm
%5:gpr = ADDI %2:gpr, -1
%17:gprpair = COPY %4:gprpair
%18:gpr = COPY %5:gpr
%19:gprpair = COPY %4:gprpair
BEQ %5:gpr, $x0, %bb.3
PseudoBR %bb.4
```
In `bb.3.for.cond.cleanup`, the two instructions before the `PseudoRET` become:
```
$x10 = COPY %19.sub_gpr_even:gprpairnox0
$x11 = COPY %19.sub_gpr_odd:gprpairnox0
```
Which means we definitely get a 2-instruction move, as seen in the final assembly for that function.
I'm not sure what the right thing to do here is. Obviously ADDD/FMV.D are only valid if the copies are coming from an even-odd register pair. Maybe we should be doing some manual coalescing of our own? One other thought I had is we could be assigning 64-bit values into `$x10_x11` during call lowering when they are in a well-aligned register pair, but I think that runs into problems when the two values aren't coming from even-odd pairs, and I think having some kind of COPY_PAIR_OPT instruction would probably be a bad idea for complexity (like we have for RV32_SD_OPT).
https://github.com/llvm/llvm-project/pull/180671
More information about the llvm-commits
mailing list