https://github.com/dyung updated https://github.com/llvm/llvm-project/pull/209574
>From 5100381e4719316bb4f00419bb52e260dc32f2e6 Mon Sep 17 00:00:00 2001 From: Garvit Gupta <[email protected]> Date: Tue, 14 Jul 2026 23:11:07 +0530 Subject: [PATCH] [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 79d1db7e00932407c4aa1028632f2c188d14cac8) --- llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp | 6 + llvm/lib/Target/RISCV/RISCVInstrInfoXqci.td | 7 ++ llvm/lib/Target/RISCV/RISCVInstrPredicates.td | 22 ++++ llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp | 6 + .../RISCV/xqcilo-xqcilia-frame-index.ll | 113 ++++++++++++++++++ 5 files changed, 154 insertions(+) create mode 100644 llvm/test/CodeGen/RISCV/xqcilo-xqcilia-frame-index.ll diff --git a/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp b/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp index c86926e075c99..8d9c2d519274c 100644 --- a/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp +++ b/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp @@ -3579,6 +3579,10 @@ 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(); @@ -3588,6 +3592,8 @@ bool RISCVDAGToDAGISel::SelectAddrRegImm26(SDValue Addr, SDValue &Base, // load/store. if (isInt<26>(CVal) && !isInt<12>(CVal)) { Base = Addr.getOperand(0); + if (auto *FIN = dyn_cast<FrameIndexSDNode>(Base)) + Base = CurDAG->getTargetFrameIndex(FIN->getIndex(), VT); Offset = CurDAG->getSignedTargetConstant(CVal, DL, VT); return true; } diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfoXqci.td b/llvm/lib/Target/RISCV/RISCVInstrInfoXqci.td index bb7b2bf08644e..ea3c5927f1da8 100644 --- a/llvm/lib/Target/RISCV/RISCVInstrInfoXqci.td +++ b/llvm/lib/Target/RISCV/RISCVInstrInfoXqci.td @@ -1635,6 +1635,13 @@ def : PatGprNoX0Simm26NoSimm12<or, QC_E_ORI>; def : PatGprNoX0Simm26NoSimm12<xor, QC_E_XORI>; } // Predicates = [HasVendorXqcilia, IsRV32] +/// FrameIndex calculations + +let Predicates = [HasVendorXqcilia, IsRV32] in { + def : Pat<(riscv_add_like frameindex:$fi, simm26_nosimm12:$offset), + (QC_E_ADDI (iPTR (to_tframeindex $fi)), simm26_nosimm12:$offset)>; +} // Predicates = [HasVendorXqcilia, IsRV32] + /// Load/Store operations let Predicates = [HasVendorXqcilo, IsRV32], AddedComplexity = 2 in { diff --git a/llvm/lib/Target/RISCV/RISCVInstrPredicates.td b/llvm/lib/Target/RISCV/RISCVInstrPredicates.td index 367f91bb9902f..cff834150cfeb 100644 --- a/llvm/lib/Target/RISCV/RISCVInstrPredicates.td +++ b/llvm/lib/Target/RISCV/RISCVInstrPredicates.td @@ -242,3 +242,25 @@ def isBaseStore SH, SB, ]>>>; + +// Xqcilo 26-bit offset Loads (qc.e.lb, qc.e.lbu, qc.e.lh, qc.e.lhu, qc.e.lw) +def isBaseQCLoad + : TIIPredicate<"isBaseQCLoad", + MCReturnStatement< + CheckOpcode<[ + QC_E_LB, + QC_E_LBU, + QC_E_LH, + QC_E_LHU, + QC_E_LW + ]>>>; + +// Xqcilo 26-bit offset Stores (qc.e.sb, qc.e.sh, qc.e.sw) +def isBaseQCStore + : TIIPredicate<"isBaseQCStore", + MCReturnStatement< + CheckOpcode<[ + QC_E_SB, + QC_E_SH, + QC_E_SW + ]>>>; diff --git a/llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp b/llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp index 7dbdc881ea6c0..3311841505685 100644 --- a/llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp +++ b/llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp @@ -588,6 +588,7 @@ bool RISCVRegisterInfo::eliminateFrameIndex(MachineBasicBlock::iterator II, if (!IsRVVSpill) { int64_t Val = Offset.getFixed(); int64_t Lo12 = SignExtend64<12>(Val); + int64_t Lo26 = SignExtend64<26>(Val); unsigned Opc = MI.getOpcode(); if (Opc == RISCV::ADDI && !isInt<12>(Val)) { @@ -614,6 +615,11 @@ 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 (Opc == RISCV::QC_E_ADDI || RISCVInstrInfo::isBaseQCLoad(MI) || + RISCVInstrInfo::isBaseQCStore(MI)) { + MI.getOperand(FIOperandNum + 1).ChangeToImmediate(Lo26); + Offset = StackOffset::get((uint64_t)Val - (uint64_t)Lo26, + Offset.getScalable()); } else { // We can encode an add with 12 bit signed immediate in the immediate // operand of our user instruction. As a result, the remaining diff --git a/llvm/test/CodeGen/RISCV/xqcilo-xqcilia-frame-index.ll b/llvm/test/CodeGen/RISCV/xqcilo-xqcilia-frame-index.ll new file mode 100644 index 0000000000000..eaf5b78f30ff2 --- /dev/null +++ b/llvm/test/CodeGen/RISCV/xqcilo-xqcilia-frame-index.ll @@ -0,0 +1,113 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5 +; Frame-index addressing for the Qualcomm Xqcilo/Xqcilia large-offset +; instructions (qc.e.lw/qc.e.sw/qc.e.addi). Stack accesses whose resolved +; sp-relative offset is a 26-bit (but not 12-bit) signed immediate fold the +; offset directly into the wide instruction instead of materialising a base. +; +; RUN: llc < %s -mtriple=riscv32 -mattr=+xqcilo,+xqcilia -riscv-no-aliases \ +; RUN: | FileCheck %s + +; Load variant: A load at a large (simm26, not simm12) offset off a stack slot +; folds the resolved frame offset into a single qc.e.lw. The offset-0 access +; keeps the alloca live. +define i32 @stack_fi_load(i32 %x) nounwind { +; CHECK-LABEL: stack_fi_load: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: qc.e.addi sp, sp, -4000016 +; CHECK-NEXT: qc.e.lw a1, 3000016(sp) +; CHECK-NEXT: c.mv a2, a0 +; CHECK-NEXT: c.mv a0, a1 +; CHECK-NEXT: c.swsp a2, 16(sp) +; CHECK-NEXT: qc.e.addi sp, sp, 4000016 +; CHECK-NEXT: c.jr ra +entry: + %arr = alloca [4000000 x i8], align 4 + %p0 = getelementptr inbounds [4000000 x i8], ptr %arr, i32 0, i32 0 + store i32 %x, ptr %p0, align 4 + %p1 = getelementptr inbounds [4000000 x i8], ptr %arr, i32 0, i32 3000000 + %v = load i32, ptr %p1, align 4 + ret i32 %v +} + +; Store variant: a store at a large (simm26, not simm12) offset off a stack slot +; folds into a single qc.e.sw. +define void @stack_fi_store(i32 %x) nounwind { +; CHECK-LABEL: stack_fi_store: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: qc.e.addi sp, sp, -4000016 +; CHECK-NEXT: qc.e.sw a0, 3000016(sp) +; CHECK-NEXT: c.swsp a0, 16(sp) +; CHECK-NEXT: qc.e.addi sp, sp, 4000016 +; CHECK-NEXT: c.jr ra +entry: + %arr = alloca [4000000 x i8], align 4 + %p1 = getelementptr inbounds [4000000 x i8], ptr %arr, i32 0, i32 3000000 + store i32 %x, ptr %p1, align 4 + %p0 = getelementptr inbounds [4000000 x i8], ptr %arr, i32 0, i32 0 + store i32 %x, ptr %p0, align 4 + ret void +} + +; The address of a large-offset stack element escapes via a call, so it must be +; materialised as a value. The (frameindex + simm26) computation selects +; QC_E_ADDI and folds the resolved 26-bit frame offset into it. +declare void @use(ptr) +define void @fi_addr_escapes(i32 %x) nounwind { +; CHECK-LABEL: fi_addr_escapes: +; CHECK: # %bb.0: +; CHECK-NEXT: c.addi16sp sp, -256 +; CHECK-NEXT: c.swsp ra, 252(sp) # 4-byte Folded Spill +; CHECK-NEXT: qc.e.addi sp, sp, -3999760 +; CHECK-NEXT: qc.e.addi a0, sp, 3000012 +; CHECK-NEXT: call use +; CHECK-NEXT: c.addi4spn a0, sp, 12 +; CHECK-NEXT: call use +; CHECK-NEXT: qc.e.addi sp, sp, 3999760 +; CHECK-NEXT: c.lwsp ra, 252(sp) # 4-byte Folded Reload +; CHECK-NEXT: c.addi16sp sp, 256 +; CHECK-NEXT: c.jr ra + %arr = alloca [4000000 x i8], align 4 + %p1 = getelementptr inbounds [4000000 x i8], ptr %arr, i32 0, i32 3000000 + call void @use(ptr %p1) + %p0 = getelementptr inbounds [4000000 x i8], ptr %arr, i32 0, i32 0 + call void @use(ptr %p0) + ret void +} + +; A bare frame index (offset 0) whose slot resolves to a large (simm26, not +; simm12) sp-relative offset. The small alloca is placed first so the allocator +; lays it out high in the frame (sp + ~8204). +define i32 @bare_fi_high_frame() nounwind { +; CHECK-LABEL: bare_fi_high_frame: +; CHECK: # %bb.0: +; CHECK-NEXT: qc.e.addi sp, sp, -8208 +; CHECK-NEXT: c.li a0, 1 +; CHECK-NEXT: c.swsp a0, 12(sp) +; CHECK-NEXT: qc.e.lw a0, 8204(sp) +; CHECK-NEXT: qc.e.addi sp, sp, 8208 +; CHECK-NEXT: c.jr ra + %small = alloca i32 + %big = alloca [8192 x i8], align 4 + %pb = getelementptr [8192 x i8], ptr %big, i32 0, i32 0 + store volatile i32 1, ptr %pb + %v = load i32, ptr %small + ret i32 %v +} + +; Store variant of the bare-frame-index case. +define void @bare_fi_high_frame_store(i32 %x) nounwind { +; CHECK-LABEL: bare_fi_high_frame_store: +; CHECK: # %bb.0: +; CHECK-NEXT: qc.e.addi sp, sp, -8208 +; CHECK-NEXT: c.li a1, 1 +; CHECK-NEXT: c.swsp a1, 12(sp) +; CHECK-NEXT: qc.e.sw a0, 8204(sp) +; CHECK-NEXT: qc.e.addi sp, sp, 8208 +; CHECK-NEXT: c.jr ra + %small = alloca i32 + %big = alloca [8192 x i8], align 4 + %pb = getelementptr [8192 x i8], ptr %big, i32 0, i32 0 + store volatile i32 1, ptr %pb + store i32 %x, ptr %small + ret void +} _______________________________________________ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
