[llvm] [RISCV] Fold shxadd into Xqcisls scaled load/store in RISCVMergeBaseOffset (PR #182221)

Sudharsan Veeravalli via llvm-commits llvm-commits at lists.llvm.org
Thu Feb 19 18:01:15 PST 2026


https://github.com/svs-quic updated https://github.com/llvm/llvm-project/pull/182221

>From de48777a1bdf7ea84f2868d398bc4d02feb4d892 Mon Sep 17 00:00:00 2001
From: Sudharsan Veeravalli <svs at qti.qualcomm.com>
Date: Thu, 19 Feb 2026 10:38:05 +0530
Subject: [PATCH 1/4] Pre-commit test

---
 .../xqcisls-merge-base-offset-muliadd.ll      | 118 ++++++++++++++++++
 1 file changed, 118 insertions(+)
 create mode 100644 llvm/test/CodeGen/RISCV/xqcisls-merge-base-offset-muliadd.ll

diff --git a/llvm/test/CodeGen/RISCV/xqcisls-merge-base-offset-muliadd.ll b/llvm/test/CodeGen/RISCV/xqcisls-merge-base-offset-muliadd.ll
new file mode 100644
index 0000000000000..d1e82643c3df0
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/xqcisls-merge-base-offset-muliadd.ll
@@ -0,0 +1,118 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc -mtriple=riscv32 -mattr=+xqcili,+xqcisls,+zba -verify-machineinstrs %s -o - | FileCheck %s
+
+ at sym = external global i8, align 1
+
+define i32 @load_scaled(i32 %idx) nounwind {
+; CHECK-LABEL: load_scaled:
+; CHECK:       # %bb.0: # %entry
+; CHECK-NEXT:    qc.e.li a1, sym
+; CHECK-NEXT:    sh2add a0, a0, a1
+; CHECK-NEXT:    lw a0, 16(a0)
+; CHECK-NEXT:    ret
+entry:
+  %baseptr = getelementptr i8, i8* @sym, i32 0
+  %baseint = ptrtoint i8* %baseptr to i32
+  %idxsh   = shl i32 %idx, 2
+  %sum     = add i32 %baseint, %idxsh
+  %sum_ptr = inttoptr i32 %sum to i8*
+  %addr_plus_imm = getelementptr i8, i8* %sum_ptr, i32 16
+  %addr_i32p = bitcast i8* %addr_plus_imm to i32*
+  %val = load i32, i32* %addr_i32p, align 4
+  ret i32 %val
+}
+
+define void @store_scaled(i32 %idx, i32 %val) nounwind {
+; CHECK-LABEL: store_scaled:
+; CHECK:       # %bb.0: # %entry
+; CHECK-NEXT:    qc.e.li a2, sym
+; CHECK-NEXT:    sh3add a0, a0, a2
+; CHECK-NEXT:    sw a1, 12(a0)
+; CHECK-NEXT:    ret
+entry:
+  %baseptr = getelementptr i8, i8* @sym, i32 0
+  %baseint = ptrtoint i8* %baseptr to i32
+  %idxsh   = shl i32 %idx, 3
+  %sum     = add i32 %baseint, %idxsh
+  %sum_ptr = inttoptr i32 %sum to i8*
+  %addr_plus_imm = getelementptr i8, i8* %sum_ptr, i32 12
+  %addr_i32p = bitcast i8* %addr_plus_imm to i32*
+  store i32 %val, i32* %addr_i32p, align 4
+  ret void
+}
+
+define i32 @load_scaled_i8(i32 %idx) nounwind {
+; CHECK-LABEL: load_scaled_i8:
+; CHECK:       # %bb.0: # %entry
+; CHECK-NEXT:    qc.e.li a1, sym
+; CHECK-NEXT:    sh2add a0, a0, a1
+; CHECK-NEXT:    lb a0, 5(a0)
+; CHECK-NEXT:    ret
+entry:
+  %baseptr = getelementptr i8, i8* @sym, i32 0
+  %baseint = ptrtoint i8* %baseptr to i32
+  %idxsh   = shl i32 %idx, 2
+  %sum     = add i32 %baseint, %idxsh
+  %sum_ptr = inttoptr i32 %sum to i8*
+  %addr_plus_imm = getelementptr i8, i8* %sum_ptr, i32 5
+  %val8 = load i8, i8* %addr_plus_imm, align 1
+  %val = sext i8 %val8 to i32
+  ret i32 %val
+}
+
+define void @store_scaled_i8(i32 %idx, i32 %val) nounwind {
+; CHECK-LABEL: store_scaled_i8:
+; CHECK:       # %bb.0: # %entry
+; CHECK-NEXT:    qc.e.li a2, sym
+; CHECK-NEXT:    sh3add a0, a0, a2
+; CHECK-NEXT:    sb a1, 7(a0)
+; CHECK-NEXT:    ret
+entry:
+  %baseptr = getelementptr i8, i8* @sym, i32 0
+  %baseint = ptrtoint i8* %baseptr to i32
+  %idxsh   = shl i32 %idx, 3
+  %sum     = add i32 %baseint, %idxsh
+  %sum_ptr = inttoptr i32 %sum to i8*
+  %addr_plus_imm = getelementptr i8, i8* %sum_ptr, i32 7
+  %val8 = trunc i32 %val to i8
+  store i8 %val8, i8* %addr_plus_imm, align 1
+  ret void
+}
+
+define i32 @load_scaled_i16(i32 %idx) nounwind {
+; CHECK-LABEL: load_scaled_i16:
+; CHECK:       # %bb.0: # %entry
+; CHECK-NEXT:    qc.e.li a1, sym
+; CHECK-NEXT:    sh1add a0, a0, a1
+; CHECK-NEXT:    lh a0, 6(a0)
+; CHECK-NEXT:    ret
+entry:
+  %baseptr = getelementptr i8, i8* @sym, i32 0
+  %baseint = ptrtoint i8* %baseptr to i32
+  %idxsh   = shl i32 %idx, 1
+  %sum     = add i32 %baseint, %idxsh
+  %sum_ptr = inttoptr i32 %sum to i16*
+  %addr_plus_imm = getelementptr i16, i16* %sum_ptr, i32 3
+  %val16 = load i16, i16* %addr_plus_imm, align 2
+  %val = sext i16 %val16 to i32
+  ret i32 %val
+}
+
+define void @store_scaled_i16(i32 %idx, i32 %val) nounwind {
+; CHECK-LABEL: store_scaled_i16:
+; CHECK:       # %bb.0: # %entry
+; CHECK-NEXT:    qc.e.li a2, sym
+; CHECK-NEXT:    sh2add a0, a0, a2
+; CHECK-NEXT:    sh a1, 10(a0)
+; CHECK-NEXT:    ret
+entry:
+  %baseptr = getelementptr i8, i8* @sym, i32 0
+  %baseint = ptrtoint i8* %baseptr to i32
+  %idxsh   = shl i32 %idx, 2
+  %sum     = add i32 %baseint, %idxsh
+  %sum_ptr = inttoptr i32 %sum to i16*
+  %addr_plus_imm = getelementptr i16, i16* %sum_ptr, i32 5
+  %val16 = trunc i32 %val to i16
+  store i16 %val16, i16* %addr_plus_imm, align 2
+  ret void
+}

>From eb9b174b7bac561f0b95362d25dd3220e77a0cc9 Mon Sep 17 00:00:00 2001
From: Sudharsan Veeravalli <svs at qti.qualcomm.com>
Date: Thu, 19 Feb 2026 10:39:31 +0530
Subject: [PATCH 2/4] Merge shxadd into scaled load/store

---
 .../lib/Target/RISCV/RISCVMergeBaseOffset.cpp | 110 ++++++++++++++++++
 .../xqcisls-merge-base-offset-muliadd.ll      |  30 ++---
 2 files changed, 122 insertions(+), 18 deletions(-)

diff --git a/llvm/lib/Target/RISCV/RISCVMergeBaseOffset.cpp b/llvm/lib/Target/RISCV/RISCVMergeBaseOffset.cpp
index be15f17e84298..2ebd14bbe60d3 100644
--- a/llvm/lib/Target/RISCV/RISCVMergeBaseOffset.cpp
+++ b/llvm/lib/Target/RISCV/RISCVMergeBaseOffset.cpp
@@ -43,6 +43,7 @@ class RISCVMergeBaseOffsetOpt : public MachineFunctionPass {
                          MachineInstr &TailShXAdd, Register GSReg);
 
   bool foldIntoMemoryOps(MachineInstr &Hi, MachineInstr &Lo);
+  bool foldShxaddIntoScaledMemory(MachineInstr &Hi, MachineInstr &Lo);
 
   RISCVMergeBaseOffsetOpt() : MachineFunctionPass(ID) {}
 
@@ -575,6 +576,114 @@ bool RISCVMergeBaseOffsetOpt::foldIntoMemoryOps(MachineInstr &Hi,
   return true;
 }
 
+// Try to fold sequences of the form:
+//   Hi/lo:    qc.e.li vreg1, s           -> qc.e.lo vreg1, s+imm
+//   TailAdd:  shxadd vreg2, vreg3, vreg1 -> deleted
+//   Tail:     lx vreg4, imm(vreg2)       -> qc.lrx vreg4, vreg1, vreg3, (1/2/3)
+bool RISCVMergeBaseOffsetOpt::foldShxaddIntoScaledMemory(MachineInstr &Hi,
+                                                         MachineInstr &Lo) {
+  if (!ST->hasVendorXqcisls())
+    return false;
+
+  if (Hi.getOpcode() != RISCV::QC_E_LI)
+    return false;
+
+  Register BaseReg = Hi.getOperand(0).getReg();
+  if (!BaseReg.isVirtual() || !MRI->hasOneUse(BaseReg))
+    return false;
+
+  MachineInstr &ShxAdd = *MRI->use_instr_begin(BaseReg);
+  unsigned ShxOpc = ShxAdd.getOpcode();
+  if (ShxOpc != RISCV::SH1ADD && ShxOpc != RISCV::SH2ADD &&
+      ShxOpc != RISCV::SH3ADD)
+    return false;
+
+  // shxadd Rd, Rs1, Rs2
+  Register Rd = ShxAdd.getOperand(0).getReg();
+  Register Rs1 = ShxAdd.getOperand(1).getReg();
+  Register Rs2 = ShxAdd.getOperand(2).getReg();
+
+  if (Rs2 != BaseReg)
+    return false;
+
+  if (!Rd.isVirtual() || !MRI->hasOneUse(Rd))
+    return false;
+
+  MachineInstr &TailMem = *MRI->use_instr_begin(Rd);
+  unsigned Opc = TailMem.getOpcode();
+
+  if (!TailMem.getOperand(1).isReg() || TailMem.getOperand(1).getReg() != Rd)
+    return false;
+  if (!TailMem.getOperand(2).isImm())
+    return false;
+  int64_t Imm = TailMem.getOperand(2).getImm();
+
+  // Update QC_E_LI offset.
+  int64_t NewOffset = SignExtend64<32>(Hi.getOperand(1).getOffset() + Imm);
+
+  if (!isInt<32>(NewOffset))
+    return false;
+
+  unsigned NewOpc = 0;
+  switch (Opc) {
+  case RISCV::LB:
+    NewOpc = RISCV::QC_LRB;
+    break;
+  case RISCV::LBU:
+    NewOpc = RISCV::QC_LRBU;
+    break;
+  case RISCV::LH:
+    NewOpc = RISCV::QC_LRH;
+    break;
+  case RISCV::LHU:
+    NewOpc = RISCV::QC_LRHU;
+    break;
+  case RISCV::LW:
+    NewOpc = RISCV::QC_LRW;
+    break;
+  case RISCV::SB:
+    NewOpc = RISCV::QC_SRB;
+    break;
+  case RISCV::SH:
+    NewOpc = RISCV::QC_SRH;
+    break;
+  case RISCV::SW:
+    NewOpc = RISCV::QC_SRW;
+    break;
+  default:
+    return false;
+  }
+
+  Hi.getOperand(1).setOffset(NewOffset);
+  TailMem.getOperand(2).ChangeToImmediate(0);
+
+  // Build scaled load/store.
+  auto *TII = ST->getInstrInfo();
+  auto *MBB = TailMem.getParent();
+  RegState DestRegState;
+  DestRegState = TailMem.mayLoad()
+                     ? RegState::Define
+                     : getKillRegState(TailMem.getOperand(0).isKill());
+  unsigned ShAmt = (ShxOpc == RISCV::SH1ADD)   ? 1
+                   : (ShxOpc == RISCV::SH2ADD) ? 2
+                                               : 3;
+
+  // Ensure index register satisfies GPRNoX0 class required by QC_LR*/QC_SR*.
+  MRI->constrainRegClass(Rs1, &RISCV::GPRNoX0RegClass);
+
+  BuildMI(*MBB, TailMem, TailMem.getDebugLoc(), TII->get(NewOpc))
+      .addReg(TailMem.getOperand(0).getReg(), DestRegState)
+      .addReg(Rs2, getKillRegState(ShxAdd.getOperand(2).isKill()))
+      .addReg(Rs1, getKillRegState(ShxAdd.getOperand(1).isKill()))
+      .addImm(ShAmt)
+      .cloneMemRefs(TailMem);
+
+  MRI->replaceRegWith(Rd, Rs2);
+  TailMem.eraseFromParent();
+  ShxAdd.eraseFromParent();
+  return true;
+}
+
 bool RISCVMergeBaseOffsetOpt::runOnMachineFunction(MachineFunction &Fn) {
   if (skipFunction(Fn.getFunction()))
     return false;
@@ -591,6 +700,7 @@ bool RISCVMergeBaseOffsetOpt::runOnMachineFunction(MachineFunction &Fn) {
         continue;
       MadeChange |= detectAndFoldOffset(Hi, *Lo);
       MadeChange |= foldIntoMemoryOps(Hi, *Lo);
+      MadeChange |= foldShxaddIntoScaledMemory(Hi, *Lo);
     }
   }
 
diff --git a/llvm/test/CodeGen/RISCV/xqcisls-merge-base-offset-muliadd.ll b/llvm/test/CodeGen/RISCV/xqcisls-merge-base-offset-muliadd.ll
index d1e82643c3df0..f30961bdb0add 100644
--- a/llvm/test/CodeGen/RISCV/xqcisls-merge-base-offset-muliadd.ll
+++ b/llvm/test/CodeGen/RISCV/xqcisls-merge-base-offset-muliadd.ll
@@ -6,9 +6,8 @@
 define i32 @load_scaled(i32 %idx) nounwind {
 ; CHECK-LABEL: load_scaled:
 ; CHECK:       # %bb.0: # %entry
-; CHECK-NEXT:    qc.e.li a1, sym
-; CHECK-NEXT:    sh2add a0, a0, a1
-; CHECK-NEXT:    lw a0, 16(a0)
+; CHECK-NEXT:    qc.e.li a1, sym+16
+; CHECK-NEXT:    qc.lrw a0, a1, a0, 2
 ; CHECK-NEXT:    ret
 entry:
   %baseptr = getelementptr i8, i8* @sym, i32 0
@@ -25,9 +24,8 @@ entry:
 define void @store_scaled(i32 %idx, i32 %val) nounwind {
 ; CHECK-LABEL: store_scaled:
 ; CHECK:       # %bb.0: # %entry
-; CHECK-NEXT:    qc.e.li a2, sym
-; CHECK-NEXT:    sh3add a0, a0, a2
-; CHECK-NEXT:    sw a1, 12(a0)
+; CHECK-NEXT:    qc.e.li a2, sym+12
+; CHECK-NEXT:    qc.srw a1, a2, a0, 3
 ; CHECK-NEXT:    ret
 entry:
   %baseptr = getelementptr i8, i8* @sym, i32 0
@@ -44,9 +42,8 @@ entry:
 define i32 @load_scaled_i8(i32 %idx) nounwind {
 ; CHECK-LABEL: load_scaled_i8:
 ; CHECK:       # %bb.0: # %entry
-; CHECK-NEXT:    qc.e.li a1, sym
-; CHECK-NEXT:    sh2add a0, a0, a1
-; CHECK-NEXT:    lb a0, 5(a0)
+; CHECK-NEXT:    qc.e.li a1, sym+5
+; CHECK-NEXT:    qc.lrb a0, a1, a0, 2
 ; CHECK-NEXT:    ret
 entry:
   %baseptr = getelementptr i8, i8* @sym, i32 0
@@ -63,9 +60,8 @@ entry:
 define void @store_scaled_i8(i32 %idx, i32 %val) nounwind {
 ; CHECK-LABEL: store_scaled_i8:
 ; CHECK:       # %bb.0: # %entry
-; CHECK-NEXT:    qc.e.li a2, sym
-; CHECK-NEXT:    sh3add a0, a0, a2
-; CHECK-NEXT:    sb a1, 7(a0)
+; CHECK-NEXT:    qc.e.li a2, sym+7
+; CHECK-NEXT:    qc.srb a1, a2, a0, 3
 ; CHECK-NEXT:    ret
 entry:
   %baseptr = getelementptr i8, i8* @sym, i32 0
@@ -82,9 +78,8 @@ entry:
 define i32 @load_scaled_i16(i32 %idx) nounwind {
 ; CHECK-LABEL: load_scaled_i16:
 ; CHECK:       # %bb.0: # %entry
-; CHECK-NEXT:    qc.e.li a1, sym
-; CHECK-NEXT:    sh1add a0, a0, a1
-; CHECK-NEXT:    lh a0, 6(a0)
+; CHECK-NEXT:    qc.e.li a1, sym+6
+; CHECK-NEXT:    qc.lrh a0, a1, a0, 1
 ; CHECK-NEXT:    ret
 entry:
   %baseptr = getelementptr i8, i8* @sym, i32 0
@@ -101,9 +96,8 @@ entry:
 define void @store_scaled_i16(i32 %idx, i32 %val) nounwind {
 ; CHECK-LABEL: store_scaled_i16:
 ; CHECK:       # %bb.0: # %entry
-; CHECK-NEXT:    qc.e.li a2, sym
-; CHECK-NEXT:    sh2add a0, a0, a2
-; CHECK-NEXT:    sh a1, 10(a0)
+; CHECK-NEXT:    qc.e.li a2, sym+10
+; CHECK-NEXT:    qc.srh a1, a2, a0, 2
 ; CHECK-NEXT:    ret
 entry:
   %baseptr = getelementptr i8, i8* @sym, i32 0

>From 545d944e4c5658f56f979e893c5f90afe10f298f Mon Sep 17 00:00:00 2001
From: Sudharsan Veeravalli <svs at qti.qualcomm.com>
Date: Thu, 19 Feb 2026 13:01:42 +0530
Subject: [PATCH 3/4] Fix typo

---
 llvm/lib/Target/RISCV/RISCVMergeBaseOffset.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/lib/Target/RISCV/RISCVMergeBaseOffset.cpp b/llvm/lib/Target/RISCV/RISCVMergeBaseOffset.cpp
index 2ebd14bbe60d3..903e2e96e863c 100644
--- a/llvm/lib/Target/RISCV/RISCVMergeBaseOffset.cpp
+++ b/llvm/lib/Target/RISCV/RISCVMergeBaseOffset.cpp
@@ -577,7 +577,7 @@ bool RISCVMergeBaseOffsetOpt::foldIntoMemoryOps(MachineInstr &Hi,
 }
 
 // Try to fold sequences of the form:
-//   Hi/lo:    qc.e.li vreg1, s           -> qc.e.lo vreg1, s+imm
+//   Hi/lo:    qc.e.li vreg1, s           -> qc.e.li vreg1, s+imm
 //   TailAdd:  shxadd vreg2, vreg3, vreg1 -> deleted
 //   Tail:     lx vreg4, imm(vreg2)       -> qc.lrx vreg4, vreg1, vreg3, (1/2/3)
 bool RISCVMergeBaseOffsetOpt::foldShxaddIntoScaledMemory(MachineInstr &Hi,

>From 58459b5056f43300b5eb1e01b793f01c136dbc73 Mon Sep 17 00:00:00 2001
From: Sudharsan Veeravalli <svs at qti.qualcomm.com>
Date: Fri, 20 Feb 2026 07:30:46 +0530
Subject: [PATCH 4/4] Address comments and also add more tests

---
 .../lib/Target/RISCV/RISCVMergeBaseOffset.cpp | 24 ++++---
 .../xqcisls-merge-base-offset-muliadd.ll      | 65 ++++++++++++++++++-
 2 files changed, 75 insertions(+), 14 deletions(-)

diff --git a/llvm/lib/Target/RISCV/RISCVMergeBaseOffset.cpp b/llvm/lib/Target/RISCV/RISCVMergeBaseOffset.cpp
index 903e2e96e863c..95b16d75e38da 100644
--- a/llvm/lib/Target/RISCV/RISCVMergeBaseOffset.cpp
+++ b/llvm/lib/Target/RISCV/RISCVMergeBaseOffset.cpp
@@ -594,32 +594,32 @@ bool RISCVMergeBaseOffsetOpt::foldShxaddIntoScaledMemory(MachineInstr &Hi,
 
   MachineInstr &ShxAdd = *MRI->use_instr_begin(BaseReg);
   unsigned ShxOpc = ShxAdd.getOpcode();
+  // TODO: Add support for QC.SHLADD
   if (ShxOpc != RISCV::SH1ADD && ShxOpc != RISCV::SH2ADD &&
       ShxOpc != RISCV::SH3ADD)
     return false;
 
   // shxadd Rd, Rs1, Rs2
-  Register Rd = ShxAdd.getOperand(0).getReg();
-  Register Rs1 = ShxAdd.getOperand(1).getReg();
-  Register Rs2 = ShxAdd.getOperand(2).getReg();
+  Register ScaledReg = ShxAdd.getOperand(0).getReg();
+  Register IndexReg = ShxAdd.getOperand(1).getReg();
 
-  if (Rs2 != BaseReg)
+  if (ShxAdd.getOperand(2).getReg() != BaseReg)
     return false;
 
-  if (!Rd.isVirtual() || !MRI->hasOneUse(Rd))
+  if (!ScaledReg.isVirtual() || !MRI->hasOneUse(ScaledReg))
     return false;
 
-  MachineInstr &TailMem = *MRI->use_instr_begin(Rd);
+  MachineInstr &TailMem = *MRI->use_instr_begin(ScaledReg);
   unsigned Opc = TailMem.getOpcode();
 
-  if (!TailMem.getOperand(1).isReg() || TailMem.getOperand(1).getReg() != Rd)
+  if (!TailMem.getOperand(1).isReg() || TailMem.getOperand(1).getReg() != ScaledReg)
     return false;
   if (!TailMem.getOperand(2).isImm())
     return false;
   int64_t Imm = TailMem.getOperand(2).getImm();
 
   // Update QC_E_LI offset.
-  int64_t NewOffset = SignExtend64<32>(Hi.getOperand(1).getOffset() + Imm);
+  int64_t NewOffset = Hi.getOperand(1).getOffset() + Imm;
 
   if (!isInt<32>(NewOffset))
     return false;
@@ -655,7 +655,6 @@ bool RISCVMergeBaseOffsetOpt::foldShxaddIntoScaledMemory(MachineInstr &Hi,
   }
 
   Hi.getOperand(1).setOffset(NewOffset);
-  TailMem.getOperand(2).ChangeToImmediate(0);
 
   // Build scaled load/store.
   auto *TII = ST->getInstrInfo();
@@ -669,16 +668,15 @@ bool RISCVMergeBaseOffsetOpt::foldShxaddIntoScaledMemory(MachineInstr &Hi,
                                                : 3;
 
   // Ensure index register satisfies GPRNoX0 class required by QC_LR*/QC_SR*.
-  MRI->constrainRegClass(Rs1, &RISCV::GPRNoX0RegClass);
+  MRI->constrainRegClass(IndexReg, &RISCV::GPRNoX0RegClass);
 
   BuildMI(*MBB, TailMem, TailMem.getDebugLoc(), TII->get(NewOpc))
       .addReg(TailMem.getOperand(0).getReg(), DestRegState)
-      .addReg(Rs2, getKillRegState(ShxAdd.getOperand(2).isKill()))
-      .addReg(Rs1, getKillRegState(ShxAdd.getOperand(1).isKill()))
+      .addReg(BaseReg, getKillRegState(ShxAdd.getOperand(2).isKill()))
+      .addReg(IndexReg, getKillRegState(ShxAdd.getOperand(1).isKill()))
       .addImm(ShAmt)
       .cloneMemRefs(TailMem);
 
-  MRI->replaceRegWith(Rd, Rs2);
   TailMem.eraseFromParent();
   ShxAdd.eraseFromParent();
   return true;
diff --git a/llvm/test/CodeGen/RISCV/xqcisls-merge-base-offset-muliadd.ll b/llvm/test/CodeGen/RISCV/xqcisls-merge-base-offset-muliadd.ll
index f30961bdb0add..61260494fcfdc 100644
--- a/llvm/test/CodeGen/RISCV/xqcisls-merge-base-offset-muliadd.ll
+++ b/llvm/test/CodeGen/RISCV/xqcisls-merge-base-offset-muliadd.ll
@@ -1,5 +1,5 @@
 ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
-; RUN: llc -mtriple=riscv32 -mattr=+xqcili,+xqcisls,+zba -verify-machineinstrs %s -o - | FileCheck %s
+; RUN: llc -mtriple=riscv32 -mattr=+xqcili,+xqcisls,+zba,+xqciac -verify-machineinstrs %s -o - | FileCheck %s
 
 @sym = external global i8, align 1
 
@@ -110,3 +110,66 @@ entry:
   store i16 %val16, i16* %addr_plus_imm, align 2
   ret void
 }
+
+define void @no_store_scaled_i16_multiuse_base_reg(i32 %idx, i32 %val) nounwind {
+; CHECK-LABEL: no_store_scaled_i16_multiuse_base_reg:
+; CHECK:       # %bb.0: # %entry
+; CHECK-NEXT:    qc.e.li a2, sym
+; CHECK-NEXT:    sh2add a0, a0, a2
+; CHECK-NEXT:    sh a1, 10(a0)
+; CHECK-NEXT:    sh a1, 14(a0)
+; CHECK-NEXT:    ret
+entry:
+  %baseptr = getelementptr i8, i8* @sym, i32 0
+  %baseint = ptrtoint i8* %baseptr to i32
+  %idxsh   = shl i32 %idx, 2
+  %sum     = add i32 %baseint, %idxsh
+  %sum_ptr = inttoptr i32 %sum to i16*
+  %addr_plus_imm = getelementptr i16, i16* %sum_ptr, i32 5
+  %addr_plus_imm2 = getelementptr i16, i16* %sum_ptr, i32 7
+  %val16 = trunc i32 %val to i16
+  store i16 %val16, i16* %addr_plus_imm, align 2
+  store i16 %val16, i16* %addr_plus_imm2, align 2
+  ret void
+}
+
+define void @no_store_scaled_i8_multiuse_scaled_reg(i32 %idx, i32 %val) nounwind {
+; CHECK-LABEL: no_store_scaled_i8_multiuse_scaled_reg:
+; CHECK:       # %bb.0: # %entry
+; CHECK-NEXT:    qc.e.li a2, sym
+; CHECK-NEXT:    sh3add a0, a0, a2
+; CHECK-NEXT:    add a0, a0, a1
+; CHECK-NEXT:    sb a1, 7(a0)
+; CHECK-NEXT:    ret
+entry:
+  %baseptr = getelementptr i8, i8* @sym, i32 0
+  %baseint = ptrtoint i8* %baseptr to i32
+  %idxsh   = shl i32 %idx, 3
+  %sum     = add i32 %baseint, %idxsh
+  %sum2    = add i32 %sum, %val
+  %sum_ptr = inttoptr i32 %sum2 to i8*
+  %addr_plus_imm = getelementptr i8, i8* %sum_ptr, i32 7
+  %val8 = trunc i32 %val to i8
+  store i8 %val8, i8* %addr_plus_imm, align 1
+  ret void
+}
+
+; FIXME: This should be folded once we add support for QC.SHLADD in RISCVMergeBaseOffset
+define i32 @no_load_scaled_i16_shift_gt_3(i32 %idx) nounwind {
+; CHECK-LABEL: no_load_scaled_i16_shift_gt_3:
+; CHECK:       # %bb.0: # %entry
+; CHECK-NEXT:    qc.e.li a1, sym
+; CHECK-NEXT:    qc.shladd a0, a0, a1, 5
+; CHECK-NEXT:    lh a0, 6(a0)
+; CHECK-NEXT:    ret
+entry:
+  %baseptr = getelementptr i8, i8* @sym, i32 0
+  %baseint = ptrtoint i8* %baseptr to i32
+  %idxsh   = shl i32 %idx, 5
+  %sum     = add i32 %baseint, %idxsh
+  %sum_ptr = inttoptr i32 %sum to i16*
+  %addr_plus_imm = getelementptr i16, i16* %sum_ptr, i32 3
+  %val16 = load i16, i16* %addr_plus_imm, align 2
+  %val = sext i16 %val16 to i32
+  ret i32 %val
+}



More information about the llvm-commits mailing list