Skip to content

Commit 5100381

Browse files
quic-garvguptdyung
authored andcommitted
[RISCV] Fold 26-bit frame-index offsets into Xqcilo/Xqcilia load/store/addi (#209315)
Extend frame-index addressing such that the sp-relative offset resolved for a stack access that fits within 26-bit and not 12-bits; folds directly into the wide Xqcilo/Xqcilia instructions. This is achieved by adding the support for the following: - Adding support for stack accesses to complex pattern `SelectAddrRegImm26` for 26-bit loads and stores - Adding support for frame index operand to qc.e.addi instruction - Allowing 26-bit offsets to fold into the loads and store instructions from qcilo/qcilia extension (cherry picked from commit 79d1db7)
1 parent b9c1e15 commit 5100381

5 files changed

Lines changed: 154 additions & 0 deletions

File tree

llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3579,6 +3579,10 @@ bool RISCVDAGToDAGISel::SelectAddrRegImm(SDValue Addr, SDValue &Base,
35793579
/// compressible) standard load/store instructions.
35803580
bool RISCVDAGToDAGISel::SelectAddrRegImm26(SDValue Addr, SDValue &Base,
35813581
SDValue &Offset) {
3582+
3583+
if (SelectAddrFrameIndex(Addr, Base, Offset))
3584+
return true;
3585+
35823586
SDLoc DL(Addr);
35833587
MVT VT = Addr.getSimpleValueType();
35843588

@@ -3588,6 +3592,8 @@ bool RISCVDAGToDAGISel::SelectAddrRegImm26(SDValue Addr, SDValue &Base,
35883592
// load/store.
35893593
if (isInt<26>(CVal) && !isInt<12>(CVal)) {
35903594
Base = Addr.getOperand(0);
3595+
if (auto *FIN = dyn_cast<FrameIndexSDNode>(Base))
3596+
Base = CurDAG->getTargetFrameIndex(FIN->getIndex(), VT);
35913597
Offset = CurDAG->getSignedTargetConstant(CVal, DL, VT);
35923598
return true;
35933599
}

llvm/lib/Target/RISCV/RISCVInstrInfoXqci.td

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1635,6 +1635,13 @@ def : PatGprNoX0Simm26NoSimm12<or, QC_E_ORI>;
16351635
def : PatGprNoX0Simm26NoSimm12<xor, QC_E_XORI>;
16361636
} // Predicates = [HasVendorXqcilia, IsRV32]
16371637

1638+
/// FrameIndex calculations
1639+
1640+
let Predicates = [HasVendorXqcilia, IsRV32] in {
1641+
def : Pat<(riscv_add_like frameindex:$fi, simm26_nosimm12:$offset),
1642+
(QC_E_ADDI (iPTR (to_tframeindex $fi)), simm26_nosimm12:$offset)>;
1643+
} // Predicates = [HasVendorXqcilia, IsRV32]
1644+
16381645
/// Load/Store operations
16391646

16401647
let Predicates = [HasVendorXqcilo, IsRV32], AddedComplexity = 2 in {

llvm/lib/Target/RISCV/RISCVInstrPredicates.td

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,3 +242,25 @@ def isBaseStore
242242
SH,
243243
SB,
244244
]>>>;
245+
246+
// Xqcilo 26-bit offset Loads (qc.e.lb, qc.e.lbu, qc.e.lh, qc.e.lhu, qc.e.lw)
247+
def isBaseQCLoad
248+
: TIIPredicate<"isBaseQCLoad",
249+
MCReturnStatement<
250+
CheckOpcode<[
251+
QC_E_LB,
252+
QC_E_LBU,
253+
QC_E_LH,
254+
QC_E_LHU,
255+
QC_E_LW
256+
]>>>;
257+
258+
// Xqcilo 26-bit offset Stores (qc.e.sb, qc.e.sh, qc.e.sw)
259+
def isBaseQCStore
260+
: TIIPredicate<"isBaseQCStore",
261+
MCReturnStatement<
262+
CheckOpcode<[
263+
QC_E_SB,
264+
QC_E_SH,
265+
QC_E_SW
266+
]>>>;

llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -588,6 +588,7 @@ bool RISCVRegisterInfo::eliminateFrameIndex(MachineBasicBlock::iterator II,
588588
if (!IsRVVSpill) {
589589
int64_t Val = Offset.getFixed();
590590
int64_t Lo12 = SignExtend64<12>(Val);
591+
int64_t Lo26 = SignExtend64<26>(Val);
591592
unsigned Opc = MI.getOpcode();
592593

593594
if (Opc == RISCV::ADDI && !isInt<12>(Val)) {
@@ -614,6 +615,11 @@ bool RISCVRegisterInfo::eliminateFrameIndex(MachineBasicBlock::iterator II,
614615
// instruction will add 4 to the immediate. If that would overflow 12
615616
// bits, we can't fold the offset.
616617
MI.getOperand(FIOperandNum + 1).ChangeToImmediate(0);
618+
} else if (Opc == RISCV::QC_E_ADDI || RISCVInstrInfo::isBaseQCLoad(MI) ||
619+
RISCVInstrInfo::isBaseQCStore(MI)) {
620+
MI.getOperand(FIOperandNum + 1).ChangeToImmediate(Lo26);
621+
Offset = StackOffset::get((uint64_t)Val - (uint64_t)Lo26,
622+
Offset.getScalable());
617623
} else {
618624
// We can encode an add with 12 bit signed immediate in the immediate
619625
// operand of our user instruction. As a result, the remaining
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5
2+
; Frame-index addressing for the Qualcomm Xqcilo/Xqcilia large-offset
3+
; instructions (qc.e.lw/qc.e.sw/qc.e.addi). Stack accesses whose resolved
4+
; sp-relative offset is a 26-bit (but not 12-bit) signed immediate fold the
5+
; offset directly into the wide instruction instead of materialising a base.
6+
;
7+
; RUN: llc < %s -mtriple=riscv32 -mattr=+xqcilo,+xqcilia -riscv-no-aliases \
8+
; RUN: | FileCheck %s
9+
10+
; Load variant: A load at a large (simm26, not simm12) offset off a stack slot
11+
; folds the resolved frame offset into a single qc.e.lw. The offset-0 access
12+
; keeps the alloca live.
13+
define i32 @stack_fi_load(i32 %x) nounwind {
14+
; CHECK-LABEL: stack_fi_load:
15+
; CHECK: # %bb.0: # %entry
16+
; CHECK-NEXT: qc.e.addi sp, sp, -4000016
17+
; CHECK-NEXT: qc.e.lw a1, 3000016(sp)
18+
; CHECK-NEXT: c.mv a2, a0
19+
; CHECK-NEXT: c.mv a0, a1
20+
; CHECK-NEXT: c.swsp a2, 16(sp)
21+
; CHECK-NEXT: qc.e.addi sp, sp, 4000016
22+
; CHECK-NEXT: c.jr ra
23+
entry:
24+
%arr = alloca [4000000 x i8], align 4
25+
%p0 = getelementptr inbounds [4000000 x i8], ptr %arr, i32 0, i32 0
26+
store i32 %x, ptr %p0, align 4
27+
%p1 = getelementptr inbounds [4000000 x i8], ptr %arr, i32 0, i32 3000000
28+
%v = load i32, ptr %p1, align 4
29+
ret i32 %v
30+
}
31+
32+
; Store variant: a store at a large (simm26, not simm12) offset off a stack slot
33+
; folds into a single qc.e.sw.
34+
define void @stack_fi_store(i32 %x) nounwind {
35+
; CHECK-LABEL: stack_fi_store:
36+
; CHECK: # %bb.0: # %entry
37+
; CHECK-NEXT: qc.e.addi sp, sp, -4000016
38+
; CHECK-NEXT: qc.e.sw a0, 3000016(sp)
39+
; CHECK-NEXT: c.swsp a0, 16(sp)
40+
; CHECK-NEXT: qc.e.addi sp, sp, 4000016
41+
; CHECK-NEXT: c.jr ra
42+
entry:
43+
%arr = alloca [4000000 x i8], align 4
44+
%p1 = getelementptr inbounds [4000000 x i8], ptr %arr, i32 0, i32 3000000
45+
store i32 %x, ptr %p1, align 4
46+
%p0 = getelementptr inbounds [4000000 x i8], ptr %arr, i32 0, i32 0
47+
store i32 %x, ptr %p0, align 4
48+
ret void
49+
}
50+
51+
; The address of a large-offset stack element escapes via a call, so it must be
52+
; materialised as a value. The (frameindex + simm26) computation selects
53+
; QC_E_ADDI and folds the resolved 26-bit frame offset into it.
54+
declare void @use(ptr)
55+
define void @fi_addr_escapes(i32 %x) nounwind {
56+
; CHECK-LABEL: fi_addr_escapes:
57+
; CHECK: # %bb.0:
58+
; CHECK-NEXT: c.addi16sp sp, -256
59+
; CHECK-NEXT: c.swsp ra, 252(sp) # 4-byte Folded Spill
60+
; CHECK-NEXT: qc.e.addi sp, sp, -3999760
61+
; CHECK-NEXT: qc.e.addi a0, sp, 3000012
62+
; CHECK-NEXT: call use
63+
; CHECK-NEXT: c.addi4spn a0, sp, 12
64+
; CHECK-NEXT: call use
65+
; CHECK-NEXT: qc.e.addi sp, sp, 3999760
66+
; CHECK-NEXT: c.lwsp ra, 252(sp) # 4-byte Folded Reload
67+
; CHECK-NEXT: c.addi16sp sp, 256
68+
; CHECK-NEXT: c.jr ra
69+
%arr = alloca [4000000 x i8], align 4
70+
%p1 = getelementptr inbounds [4000000 x i8], ptr %arr, i32 0, i32 3000000
71+
call void @use(ptr %p1)
72+
%p0 = getelementptr inbounds [4000000 x i8], ptr %arr, i32 0, i32 0
73+
call void @use(ptr %p0)
74+
ret void
75+
}
76+
77+
; A bare frame index (offset 0) whose slot resolves to a large (simm26, not
78+
; simm12) sp-relative offset. The small alloca is placed first so the allocator
79+
; lays it out high in the frame (sp + ~8204).
80+
define i32 @bare_fi_high_frame() nounwind {
81+
; CHECK-LABEL: bare_fi_high_frame:
82+
; CHECK: # %bb.0:
83+
; CHECK-NEXT: qc.e.addi sp, sp, -8208
84+
; CHECK-NEXT: c.li a0, 1
85+
; CHECK-NEXT: c.swsp a0, 12(sp)
86+
; CHECK-NEXT: qc.e.lw a0, 8204(sp)
87+
; CHECK-NEXT: qc.e.addi sp, sp, 8208
88+
; CHECK-NEXT: c.jr ra
89+
%small = alloca i32
90+
%big = alloca [8192 x i8], align 4
91+
%pb = getelementptr [8192 x i8], ptr %big, i32 0, i32 0
92+
store volatile i32 1, ptr %pb
93+
%v = load i32, ptr %small
94+
ret i32 %v
95+
}
96+
97+
; Store variant of the bare-frame-index case.
98+
define void @bare_fi_high_frame_store(i32 %x) nounwind {
99+
; CHECK-LABEL: bare_fi_high_frame_store:
100+
; CHECK: # %bb.0:
101+
; CHECK-NEXT: qc.e.addi sp, sp, -8208
102+
; CHECK-NEXT: c.li a1, 1
103+
; CHECK-NEXT: c.swsp a1, 12(sp)
104+
; CHECK-NEXT: qc.e.sw a0, 8204(sp)
105+
; CHECK-NEXT: qc.e.addi sp, sp, 8208
106+
; CHECK-NEXT: c.jr ra
107+
%small = alloca i32
108+
%big = alloca [8192 x i8], align 4
109+
%pb = getelementptr [8192 x i8], ptr %big, i32 0, i32 0
110+
store volatile i32 1, ptr %pb
111+
store i32 %x, ptr %small
112+
ret void
113+
}

0 commit comments

Comments
 (0)