[RISCV] Reduce spill/reload pairs when Xqcilo extension is enabled (#212807)

[RISCV] Reduce spill/reload pairs when Xqcilo extension is enabled

Currently, `SelectAddrRegImm26` calls `SelectAddrFrameIndex` first,
causing bare frame-index loads (offset 0) to select 48-bit loads/stores at
ISel. Due to `AddedComplexity=2` on the QC48LdPat patterns, the wide
opcode won over the standard LW/SW even though the resolved frame offset
typically fits simm12.

This led to more spills and reloads in functions which are under high
register pressure because 48-bit loads and stores are not marked easily
rematerializable. Also, simply adding 48-bit loads and stores to
`isLoadFromStackSlot/isStoreToStackSlot` doesn't solve the regression
for the multi call case and only by making Isel produce the plain
32/64-bit loads and store opcodes as the baseline does RA behave
identically.

Therefor this PR fixes the issue by:

-Remove the `SelectAddrFrameIndex` call from SelectAddrRegImm26. Bare frame
indices now fall through to standard LW/SW selection at ISel, where RA
recognizes them as rematerializable stack loads.

-Add post-RA promotion in `eliminateFrameIndex`: when a plain LW/SW has a
resolved frame offset that exceeds simm12, promote the opcode to
the corresponding 48-bit load/store opcode and fold the 26-bit offset
directly. This preserves the intended large-offset optimization
without affecting RA decisions.

This solves the code size regression in high register pressure function
introduced by PR #209315

Assisted by Claude

(cherry picked from commit 8b18aa0b1ec2de76d47748a4d13c4de02b4d8580)
This commit is contained in:
Garvit Gupta authored and Douglas Yung committed 2026-08-04 12:34:04 +00:00
1 parent 2629bf96a7
commit fa0232af8b
3 files changed
+139 -5

No files matched your search

@@ -3589,10 +3589,6 @@ bool RISCVDAGToDAGISel::SelectAddrRegImm(SDValue Addr, SDValue &Base,
/// compressible) standard load/store instructions.
bool RISCVDAGToDAGISel::SelectAddrRegImm26(SDValue Addr, SDValue &Base,
SDValue &Offset) {
if (SelectAddrFrameIndex(Addr, Base, Offset))
return true;
SDLoc DL(Addr);
MVT VT = Addr.getSimpleValueType();
+37 -1
View File
@@ -561,6 +561,29 @@ void RISCVRegisterInfo::lowerSegmentSpillReload(MachineBasicBlock::iterator II,
II->eraseFromParent();
}
static unsigned getXqciloWideOpcode(unsigned Opc) {
switch (Opc) {
case RISCV::LW:
return RISCV::QC_E_LW;
case RISCV::SW:
return RISCV::QC_E_SW;
case RISCV::LB:
return RISCV::QC_E_LB;
case RISCV::LBU:
return RISCV::QC_E_LBU;
case RISCV::LH:
return RISCV::QC_E_LH;
case RISCV::LHU:
return RISCV::QC_E_LHU;
case RISCV::SB:
return RISCV::QC_E_SB;
case RISCV::SH:
return RISCV::QC_E_SH;
default:
return 0;
}
}
bool RISCVRegisterInfo::eliminateFrameIndex(MachineBasicBlock::iterator II,
int SPAdj, unsigned FIOperandNum,
RegScavenger *RS) const {
@@ -569,7 +592,9 @@ bool RISCVRegisterInfo::eliminateFrameIndex(MachineBasicBlock::iterator II,
MachineInstr &MI = *II;
MachineFunction &MF = *MI.getParent()->getParent();
MachineRegisterInfo &MRI = MF.getRegInfo();
bool Is64Bit = MF.getSubtarget<RISCVSubtarget>().is64Bit();
const RISCVSubtarget &ST = MF.getSubtarget<RISCVSubtarget>();
const RISCVInstrInfo *TII = ST.getInstrInfo();
bool Is64Bit = ST.is64Bit();
DebugLoc DL = MI.getDebugLoc();
int FrameIndex = MI.getOperand(FIOperandNum).getIndex();
@@ -615,6 +640,17 @@ bool RISCVRegisterInfo::eliminateFrameIndex(MachineBasicBlock::iterator II,
// instruction will add 4 to the immediate. If that would overflow 12
// bits, we can't fold the offset.
MI.getOperand(FIOperandNum + 1).ChangeToImmediate(0);
} else if (unsigned WideOpc = getXqciloWideOpcode(Opc);
!isInt<12>(Val) && ST.hasVendorXqcilo() && WideOpc) {
// The resolved frame offset exceeds simm12 but the instruction is a
// standard load/store (LW/SW/etc). Promote to the wide Xqcilo equivalent
// so the full 26-bit offset folds directly, avoiding a separate
// base-adjust instruction. This runs post-RA and does not affect
// register allocation decisions.
MI.setDesc(TII->get(WideOpc));
MI.getOperand(FIOperandNum + 1).ChangeToImmediate(Lo26);
Offset = StackOffset::get((uint64_t)Val - (uint64_t)Lo26,
Offset.getScalable());
} else if (Opc == RISCV::QC_E_ADDI || RISCVInstrInfo::isBaseQCLoad(MI) ||
RISCVInstrInfo::isBaseQCStore(MI)) {
MI.getOperand(FIOperandNum + 1).ChangeToImmediate(Lo26);
@@ -111,3 +111,105 @@ define void @bare_fi_high_frame_store(i32 %x) nounwind {
store i32 %x, ptr %small
ret void
}
; Register-pressure regression test: bare frame-index loads from fixed stack
; slots (incoming args on stack) must select standard LW at ISel — not QC_E_LW —
; so that the register allocator can rematerialize them. With QC_E_LW selected,
; isLoadFromStackSlot does not recognize it, RA cannot rematerialize, and under
; high pressure it spills excessively.
declare dso_local i32 @sink(i32 noundef) local_unnamed_addr
define dso_local i32 @regpressure(i32 noundef %a0, i32 noundef %a1, i32 noundef %a2, i32 noundef %a3, i32 noundef %a4, i32 noundef %a5, i32 noundef %a6, i32 noundef %a7, i32 noundef %s0, i32 noundef %s1, i32 noundef %s2, i32 noundef %s3, i32 noundef %s4, i32 noundef %s5, i32 noundef %s6, i32 noundef %s7, i32 noundef %s8, i32 noundef %s9, i32 noundef %s10, i32 noundef %s11) local_unnamed_addr nounwind {
; CHECK-LABEL: regpressure:
; CHECK: # %bb.0: # %entry
; CHECK-NEXT: c.addi16sp sp, -64
; CHECK-NEXT: c.swsp ra, 60(sp) # 4-byte Folded Spill
; CHECK-NEXT: c.swsp s0, 56(sp) # 4-byte Folded Spill
; CHECK-NEXT: c.swsp s1, 52(sp) # 4-byte Folded Spill
; CHECK-NEXT: c.swsp s2, 48(sp) # 4-byte Folded Spill
; CHECK-NEXT: c.swsp s3, 44(sp) # 4-byte Folded Spill
; CHECK-NEXT: c.swsp s4, 40(sp) # 4-byte Folded Spill
; CHECK-NEXT: c.swsp s5, 36(sp) # 4-byte Folded Spill
; CHECK-NEXT: c.swsp s6, 32(sp) # 4-byte Folded Spill
; CHECK-NEXT: c.swsp s7, 28(sp) # 4-byte Folded Spill
; CHECK-NEXT: c.swsp s8, 24(sp) # 4-byte Folded Spill
; CHECK-NEXT: c.swsp s9, 20(sp) # 4-byte Folded Spill
; CHECK-NEXT: c.swsp s10, 16(sp) # 4-byte Folded Spill
; CHECK-NEXT: c.swsp s11, 12(sp) # 4-byte Folded Spill
; CHECK-NEXT: c.mv s0, a7
; CHECK-NEXT: c.mv s1, a6
; CHECK-NEXT: c.mv s2, a5
; CHECK-NEXT: c.mv s3, a4
; CHECK-NEXT: c.mv s4, a3
; CHECK-NEXT: c.mv s5, a2
; CHECK-NEXT: c.mv s6, a1
; CHECK-NEXT: c.lwsp s8, 80(sp)
; CHECK-NEXT: c.lwsp s10, 76(sp)
; CHECK-NEXT: c.lwsp s11, 72(sp)
; CHECK-NEXT: c.lwsp s9, 68(sp)
; CHECK-NEXT: c.lwsp s7, 64(sp)
; CHECK-NEXT: call sink
; CHECK-NEXT: c.add s2, s3
; CHECK-NEXT: c.add s0, s1
; CHECK-NEXT: c.add s0, s2
; CHECK-NEXT: c.add s9, s11
; CHECK-NEXT: c.add s0, s7
; CHECK-NEXT: c.add s8, s10
; CHECK-NEXT: c.add s0, s9
; CHECK-NEXT: c.lwsp a1, 84(sp)
; CHECK-NEXT: c.add s8, a1
; CHECK-NEXT: c.add s0, s8
; CHECK-NEXT: c.lwsp a1, 92(sp)
; CHECK-NEXT: c.lwsp a2, 88(sp)
; CHECK-NEXT: c.add a1, a2
; CHECK-NEXT: c.lwsp a2, 108(sp)
; CHECK-NEXT: c.lwsp a3, 104(sp)
; CHECK-NEXT: c.add a2, a3
; CHECK-NEXT: c.lwsp a3, 96(sp)
; CHECK-NEXT: c.add a1, a3
; CHECK-NEXT: c.add a0, a2
; CHECK-NEXT: c.lwsp a2, 100(sp)
; CHECK-NEXT: c.add a1, a2
; CHECK-NEXT: c.add a0, s6
; CHECK-NEXT: c.add a1, s0
; CHECK-NEXT: c.add a0, s5
; CHECK-NEXT: c.add a0, a1
; CHECK-NEXT: c.add a0, s4
; CHECK-NEXT: c.lwsp ra, 60(sp) # 4-byte Folded Reload
; CHECK-NEXT: c.lwsp s0, 56(sp) # 4-byte Folded Reload
; CHECK-NEXT: c.lwsp s1, 52(sp) # 4-byte Folded Reload
; CHECK-NEXT: c.lwsp s2, 48(sp) # 4-byte Folded Reload
; CHECK-NEXT: c.lwsp s3, 44(sp) # 4-byte Folded Reload
; CHECK-NEXT: c.lwsp s4, 40(sp) # 4-byte Folded Reload
; CHECK-NEXT: c.lwsp s5, 36(sp) # 4-byte Folded Reload
; CHECK-NEXT: c.lwsp s6, 32(sp) # 4-byte Folded Reload
; CHECK-NEXT: c.lwsp s7, 28(sp) # 4-byte Folded Reload
; CHECK-NEXT: c.lwsp s8, 24(sp) # 4-byte Folded Reload
; CHECK-NEXT: c.lwsp s9, 20(sp) # 4-byte Folded Reload
; CHECK-NEXT: c.lwsp s10, 16(sp) # 4-byte Folded Reload
; CHECK-NEXT: c.lwsp s11, 12(sp) # 4-byte Folded Reload
; CHECK-NEXT: c.addi16sp sp, 64
; CHECK-NEXT: c.jr ra
entry:
%call = tail call i32 @sink(i32 noundef %a0)
%add2 = add i32 %a5, %a4
%add4 = add i32 %add2, %a6
%add6 = add i32 %add4, %a7
%add7 = add i32 %add6, %s0
%add8 = add i32 %add7, %s1
%add9 = add i32 %add8, %s2
%add10 = add i32 %add9, %s3
%add11 = add i32 %add10, %s4
%add12 = add i32 %add11, %s5
%add13 = add i32 %add12, %s6
%add14 = add i32 %add13, %s7
%add15 = add i32 %add14, %s8
%add16 = add i32 %add15, %s9
%add17 = add i32 %add16, %s10
%add18 = add i32 %add17, %s11
%add19 = add i32 %add18, %call
%add20 = add i32 %add19, %a1
%add21 = add i32 %add20, %a2
%add22 = add i32 %add21, %a3
ret i32 %add22
}