https://github.com/dyung updated 
https://github.com/llvm/llvm-project/pull/209574

>From f22200d45c8c0ce73153420ac44a838644cef785 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

Reply via email to