[lld] [llvm] [RISCV] add new relocation type for global array accesses with non-constant indices (PR #207901)

via llvm-commits llvm-commits at lists.llvm.org
Tue Jul 28 23:09:51 PDT 2026


https://github.com/LiqinWeng updated https://github.com/llvm/llvm-project/pull/207901

>From 98cdf6d0e8189cd9b1b0cf2d516daf00a2fcd49b Mon Sep 17 00:00:00 2001
From: wengliqin <liqin.weng at spacemit.com>
Date: Tue, 7 Jul 2026 11:07:34 +0800
Subject: [PATCH 1/3] [RISCV] add new relocation type for global array accesses
 with non-constant indices

psABI: https://github.com/riscv-non-isa/riscv-elf-psabi-doc/pull/489
---
 lld/ELF/Arch/RISCV.cpp                        |  49 +++++++++
 lld/test/ELF/riscv-base-idx-add.s             |  71 ++++++++++++
 lld/test/ELF/riscv-base-idx-shxadd.s          |  69 ++++++++++++
 .../llvm/BinaryFormat/ELFRelocs/RISCV.def     |   3 +
 .../Target/RISCV/AsmParser/RISCVAsmParser.cpp |  14 ++-
 .../Target/RISCV/MCTargetDesc/RISCVBaseInfo.h |   2 +
 .../RISCV/MCTargetDesc/RISCVMCAsmInfo.h       |   1 +
 .../RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp | 101 +++++++++++++++++-
 .../Target/RISCV/MCTargetDesc/RISCVMCExpr.cpp |   6 ++
 llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp     |   6 ++
 llvm/lib/Target/RISCV/RISCVInstrFormats.td    |  11 ++
 llvm/lib/Target/RISCV/RISCVInstrInfo.cpp      |   2 +
 llvm/lib/Target/RISCV/RISCVInstrInfo.td       |  22 +++-
 llvm/lib/Target/RISCV/RISCVInstrInfoZb.td     |  17 +++
 llvm/test/MC/RISCV/Relocations/mc-dump.s      |   2 +-
 llvm/test/MC/RISCV/Relocations/relocations.s  |  54 +++++++++-
 llvm/test/MC/RISCV/rv32zba-invalid.s          |  16 +++
 llvm/test/MC/RISCV/rv64zba-invalid.s          |  36 +++++++
 18 files changed, 471 insertions(+), 11 deletions(-)
 create mode 100644 lld/test/ELF/riscv-base-idx-add.s
 create mode 100644 lld/test/ELF/riscv-base-idx-shxadd.s

diff --git a/lld/ELF/Arch/RISCV.cpp b/lld/ELF/Arch/RISCV.cpp
index 49c41ff9ebe84..cc233f357e2dc 100644
--- a/lld/ELF/Arch/RISCV.cpp
+++ b/lld/ELF/Arch/RISCV.cpp
@@ -74,6 +74,9 @@ class RISCV final : public TargetInfo {
 #define INTERNAL_R_RISCV_GPREL_S 257
 #define INTERNAL_R_RISCV_X0REL_I 258
 #define INTERNAL_R_RISCV_X0REL_S 259
+#define INTERNAL_R_RISCV_BASE_IDX_ADD 260
+#define INTERNAL_R_RISCV_BASE_IDX_LO12_I 261
+#define INTERNAL_R_RISCV_BASE_IDX_LO12_S 262
 
 const uint64_t dtpOffset = 0x800;
 
@@ -381,6 +384,9 @@ void RISCV::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels,
     case R_RISCV_HI20:
     case R_RISCV_LO12_I:
     case R_RISCV_LO12_S:
+    case R_RISCV_BASE_IDX_ADD:
+    case R_RISCV_BASE_IDX_LO12_I:
+    case R_RISCV_BASE_IDX_LO12_S:
       expr = R_ABS;
       break;
 
@@ -623,6 +629,7 @@ void RISCV::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const {
   case R_RISCV_TLSDESC_LOAD_LO12:
   case R_RISCV_TLSDESC_ADD_LO12:
   case R_RISCV_TPREL_LO12_I:
+  case R_RISCV_BASE_IDX_LO12_I:
   case R_RISCV_LO12_I: {
     uint64_t hi = (val + 0x800) >> 12;
     uint64_t lo = val - (hi << 12);
@@ -632,6 +639,7 @@ void RISCV::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const {
 
   case R_RISCV_PCREL_LO12_S:
   case R_RISCV_TPREL_LO12_S:
+  case R_RISCV_BASE_IDX_LO12_S:
   case R_RISCV_LO12_S: {
     uint64_t hi = (val + 0x800) >> 12;
     uint64_t lo = val - (hi << 12);
@@ -639,6 +647,32 @@ void RISCV::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const {
     return;
   }
 
+  case R_RISCV_BASE_IDX_ADD: {
+    write32le(loc, read32le(loc));
+    return;
+  }
+
+  case INTERNAL_R_RISCV_BASE_IDX_ADD: {
+    uint32_t insn = (read32le(loc) & ~(31 << 20)) | (X_GP << 20);
+    write32le(loc, insn);
+    return;
+  }
+
+  case INTERNAL_R_RISCV_BASE_IDX_LO12_I:
+  case INTERNAL_R_RISCV_BASE_IDX_LO12_S: {
+    Defined *gp = ctx.sym.riscvGlobalPointer;
+    int64_t displace = SignExtend64(val - gp->getVA(ctx), bits);
+    checkInt(ctx, loc, displace, 12, rel);
+    uint32_t insn = read32le(loc);
+    if (rel.type == INTERNAL_R_RISCV_BASE_IDX_LO12_I)
+      insn = setLO12_I(insn, displace);
+    else
+      insn = setLO12_S(insn, displace);
+
+    write32le(loc, insn);
+    return;
+  }
+
   case INTERNAL_R_RISCV_X0REL_I:
   case INTERNAL_R_RISCV_X0REL_S: {
     checkInt(ctx, loc, val, 12, rel);
@@ -1027,6 +1061,15 @@ static void relaxHi20Lo12(Ctx &ctx, const InputSection &sec, size_t i,
   case R_RISCV_LO12_S:
     sec.relaxAux->relocTypes[i] = INTERNAL_R_RISCV_GPREL_S;
     break;
+  case R_RISCV_BASE_IDX_ADD:
+    sec.relaxAux->relocTypes[i] = INTERNAL_R_RISCV_BASE_IDX_ADD;
+    break;
+  case R_RISCV_BASE_IDX_LO12_I:
+    sec.relaxAux->relocTypes[i] = INTERNAL_R_RISCV_BASE_IDX_LO12_I;
+    break;
+  case R_RISCV_BASE_IDX_LO12_S:
+    sec.relaxAux->relocTypes[i] = INTERNAL_R_RISCV_BASE_IDX_LO12_S;
+    break;
   }
 }
 
@@ -1082,6 +1125,9 @@ static bool relax(Ctx &ctx, int pass, InputSection &sec) {
     case R_RISCV_HI20:
     case R_RISCV_LO12_I:
     case R_RISCV_LO12_S:
+    case R_RISCV_BASE_IDX_ADD:
+    case R_RISCV_BASE_IDX_LO12_I:
+    case R_RISCV_BASE_IDX_LO12_S:
       if (relaxable(relocs, i))
         relaxHi20Lo12(ctx, sec, i, loc, r, remove);
       break;
@@ -1335,6 +1381,9 @@ void RISCV::finalizeRelax(int passes) const {
           case INTERNAL_R_RISCV_GPREL_S:
           case INTERNAL_R_RISCV_X0REL_I:
           case INTERNAL_R_RISCV_X0REL_S:
+          case INTERNAL_R_RISCV_BASE_IDX_ADD:
+          case INTERNAL_R_RISCV_BASE_IDX_LO12_I:
+          case INTERNAL_R_RISCV_BASE_IDX_LO12_S:
             break;
           case R_RISCV_RELAX:
             // Used by relaxTlsLe to indicate the relocation is ignored.
diff --git a/lld/test/ELF/riscv-base-idx-add.s b/lld/test/ELF/riscv-base-idx-add.s
new file mode 100644
index 0000000000000..17db2ce3e0a3a
--- /dev/null
+++ b/lld/test/ELF/riscv-base-idx-add.s
@@ -0,0 +1,71 @@
+# REQUIRES: riscv
+# RUN: rm -rf %t && split-file %s %t && cd %t
+
+# RUN: llvm-mc -filetype=obj -triple=riscv32-unknown-elf -mattr=+relax a.s -o rv32.o
+# RUN: llvm-mc -filetype=obj -triple=riscv64-unknown-elf -mattr=+relax a.s -o rv64.o
+
+# RUN: ld.lld --relax-gp --undefined=__global_pointer$ rv32.o lds -o rv32
+# RUN: ld.lld --relax-gp --undefined=__global_pointer$ rv64.o lds -o rv64
+# RUN: llvm-objdump -td -M no-aliases --no-show-raw-insn rv32 | FileCheck %s
+# RUN: llvm-objdump -td -M no-aliases --no-show-raw-insn rv64 | FileCheck %s
+
+# CHECK: 00000000 l       .text {{0*}}0 $x
+
+# CHECK-NOT:  lui
+# CHECK:      addi    a1, a1, -0x800
+# CHECK-NEXT: add     a0, a0, gp
+# CHECK-NEXT: lw      a0, -0x800(a0)
+# CHECK-NEXT: sw      a0, -0x800(a0)
+# CHECK-NOT:  lui
+# CHECK-NEXT: addi    a1, a1, 0x7fa
+# CHECK-NEXT: add     a0, a0, gp
+# CHECK-NEXT: lw      a0, 0x7fa(a0)
+# CHECK-NEXT: sw      a0, 0x7fa(a0)
+# CHECK-NEXT: lui     a1, 0x201
+# CHECK-NEXT: addi    a1, a1, 0xe
+# CHECK-NEXT: add     a0, a0, a1
+# CHECK-NEXT: lw      a0, 0xe(a0)
+# CHECK-NEXT: sw      a0, 0xe(a0)
+# CHECK-EMPTY:
+# CHECK-NEXT: <a>:
+# CHECK-NEXT: addi a0, a0, 0x1
+
+#--- a.s
+.global _start
+_start:
+  slli a0, a0, 2
+  lui  a1, %hi(array)
+  addi a1, a1, %base_idx_lo(array)
+  add  a0, a0, a1, %base_idx_add(array)
+  lw   a0, %base_idx_lo(array)(a0)
+  sw   a0, %base_idx_lo(array)(a0)
+  lui  a1, %hi(array1+10)
+  addi a1, a1, %base_idx_lo(array1+10)
+  add  a0, a0, a1, %base_idx_add(array1+10)
+  lw   a0, %base_idx_lo(array1+10)(a0)
+  sw   a0, %base_idx_lo(array1+10)(a0)
+  lui  a1, %hi(norelax+10)
+  addi a1, a1, %base_idx_lo(norelax+10)
+  add  a0, a0, a1, %base_idx_add(norelax+10)
+  lw   a0, %base_idx_lo(norelax+10)(a0)
+  sw   a0, %base_idx_lo(norelax+10)(a0)
+a:
+  addi a0, a0, 1
+
+.section .sdata,"aw"
+array:
+  .zero   4080
+  .size   array, 4080
+array1:
+  .zero   20
+  .size   array, 20
+norelax:
+  .zero   6
+  .size   array, 6
+
+#--- lds
+SECTIONS {
+  .text : {*(.text) }
+  .sdata 0x200000 : { }
+}
+
diff --git a/lld/test/ELF/riscv-base-idx-shxadd.s b/lld/test/ELF/riscv-base-idx-shxadd.s
new file mode 100644
index 0000000000000..32f2d97e668df
--- /dev/null
+++ b/lld/test/ELF/riscv-base-idx-shxadd.s
@@ -0,0 +1,69 @@
+# REQUIRES: riscv
+# RUN: rm -rf %t && split-file %s %t && cd %t
+
+# RUN: llvm-mc -filetype=obj -triple=riscv32-unknown-elf -mattr=+relax,+zba a.s -o rv32.o
+# RUN: llvm-mc -filetype=obj -triple=riscv64-unknown-elf -mattr=+relax,+zba a.s -o rv64.o
+
+# RUN: ld.lld --relax-gp --undefined=__global_pointer$ rv32.o lds -o rv32
+# RUN: ld.lld --relax-gp --undefined=__global_pointer$ rv64.o lds -o rv64
+# RUN: llvm-objdump --mattr=+zba -td -M no-aliases --no-show-raw-insn rv32 | FileCheck %s
+# RUN: llvm-objdump --mattr=+zba -td -M no-aliases --no-show-raw-insn rv64 | FileCheck %s
+
+# CHECK: 00000000 l       .text {{0*}}0 $x
+
+# CHECK-NOT:  lui
+# CHECK:      addi    a1, a1, -0x800
+# CHECK-NEXT: sh1add  a0, a0, gp
+# CHECK-NEXT: lw      a0, -0x800(a0)
+# CHECK-NEXT: sw      a0, -0x800(a0)
+# CHECK-NOT:  lui
+# CHECK-NEXT: addi    a1, a1, 0x7fa
+# CHECK-NEXT: sh1add  a0, a0, gp
+# CHECK-NEXT: lw      a0, 0x7fa(a0)
+# CHECK-NEXT: sw      a0, 0x7fa(a0)
+# CHECK-NEXT: lui     a1, 0x201
+# CHECK-NEXT: addi    a1, a1, 0xe
+# CHECK-NEXT: sh1add  a0, a0, a1
+# CHECK-NEXT: lw      a0, 0xe(a0)
+# CHECK-NEXT: sw      a0, 0xe(a0)
+# CHECK-EMPTY:
+# CHECK-NEXT: <a>:
+# CHECK-NEXT: addi a0, a0, 0x1
+
+#--- a.s
+.global _start
+_start:
+  lui  a1, %hi(array)
+  addi a1, a1, %base_idx_lo(array)
+  sh1add  a0, a0, a1, %base_idx_add(array)
+  lw   a0, %base_idx_lo(array)(a0)
+  sw   a0, %base_idx_lo(array)(a0)
+  lui  a1, %hi(array1+10)
+  addi a1, a1, %base_idx_lo(array1+10)
+  sh1add  a0, a0, a1, %base_idx_add(array1+10)
+  lw   a0, %base_idx_lo(array1+10)(a0)
+  sw   a0, %base_idx_lo(array1+10)(a0)
+  lui  a1, %hi(norelax+10)
+  addi a1, a1, %base_idx_lo(norelax+10)
+  sh1add  a0, a0, a1, %base_idx_add(norelax+10)
+  lw   a0, %base_idx_lo(norelax+10)(a0)
+  sw   a0, %base_idx_lo(norelax+10)(a0)
+a:
+  addi a0, a0, 1
+
+.section .sdata,"aw"
+array:
+  .zero   4080
+  .size   array, 4080
+array1:
+  .zero   20
+  .size   array, 20
+norelax:
+  .zero   6
+  .size   array, 6
+
+#--- lds
+SECTIONS {
+  .text : {*(.text) }
+  .sdata 0x200000 : { }
+}
diff --git a/llvm/include/llvm/BinaryFormat/ELFRelocs/RISCV.def b/llvm/include/llvm/BinaryFormat/ELFRelocs/RISCV.def
index ac9a089e853a6..1e9610ea1fe67 100644
--- a/llvm/include/llvm/BinaryFormat/ELFRelocs/RISCV.def
+++ b/llvm/include/llvm/BinaryFormat/ELFRelocs/RISCV.def
@@ -60,6 +60,9 @@ ELF_RELOC(R_RISCV_TLSDESC_HI20,      62)
 ELF_RELOC(R_RISCV_TLSDESC_LOAD_LO12, 63)
 ELF_RELOC(R_RISCV_TLSDESC_ADD_LO12,  64)
 ELF_RELOC(R_RISCV_TLSDESC_CALL,      65)
+ELF_RELOC(R_RISCV_BASE_IDX_LO12_I,   77)
+ELF_RELOC(R_RISCV_BASE_IDX_LO12_S,   78)
+ELF_RELOC(R_RISCV_BASE_IDX_ADD,      79)
 ELF_RELOC(R_RISCV_VENDOR,           191)
 ELF_RELOC(R_RISCV_CUSTOM192,        192)
 ELF_RELOC(R_RISCV_CUSTOM193,        193)
diff --git a/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp b/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp
index 8563f678464a6..428b08a4527f4 100644
--- a/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp
+++ b/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp
@@ -640,6 +640,17 @@ struct RISCVOperand final : public MCParsedAsmOperand {
            VK == ELF::R_RISCV_TPREL_ADD;
   }
 
+  bool isBaseIdxAddSymbol() const {
+    int64_t Imm;
+    // Must be of 'immediate' type but not a constant.
+    if (!isExpr() || evaluateConstantExpr(getExpr(), Imm))
+      return false;
+
+    RISCV::Specifier VK = RISCV::S_None;
+    return RISCVAsmParser::classifySymbolRef(getExpr(), VK) &&
+           (VK == ELF::R_RISCV_BASE_IDX_ADD);
+  }
+
   bool isTLSDESCCallSymbol() const {
     int64_t Imm;
     // Must be of 'immediate' type but not a constant.
@@ -952,7 +963,8 @@ struct RISCVOperand final : public MCParsedAsmOperand {
     RISCV::Specifier VK = RISCV::S_None;
     return RISCVAsmParser::classifySymbolRef(getExpr(), VK) &&
            (VK == RISCV::S_LO || VK == RISCV::S_PCREL_LO ||
-            VK == RISCV::S_TPREL_LO || VK == ELF::R_RISCV_TLSDESC_LOAD_LO12 ||
+            VK == RISCV::S_BASE_IDX_LO || VK == RISCV::S_TPREL_LO ||
+            VK == ELF::R_RISCV_TLSDESC_LOAD_LO12 ||
             VK == ELF::R_RISCV_TLSDESC_ADD_LO12);
   }
 
diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h
index e54d57d9f4451..b75ac8a6b3618 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h
@@ -483,6 +483,8 @@ enum {
   MO_TLSDESC_ADD_LO = 15,
   MO_TLSDESC_CALL = 16,
   MO_QC_ACCESS = 17,
+  MO_BASE_IDX_LO = 18,
+  MO_BASE_IDX_ADD = 19,
 
   // Used to differentiate between target-specific "direct" flags and "bitmask"
   // flags. A machine operand can only have one "direct" flag, but can have
diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCAsmInfo.h b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCAsmInfo.h
index cb966c56035a3..49c384d19b22b 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCAsmInfo.h
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCAsmInfo.h
@@ -41,6 +41,7 @@ enum {
   S_LO = FirstTargetFixupKind,
   S_PCREL_LO,
   S_PCREL_HI,
+  S_BASE_IDX_LO,
   S_TPREL_LO,
   S_CALL_PLT,
   S_GOT_HI,
diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp
index 23cbbf0954cd7..d53fcd9c3d6fc 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp
@@ -64,6 +64,10 @@ class RISCVMCCodeEmitter : public MCCodeEmitter {
                       SmallVectorImpl<MCFixup> &Fixups,
                       const MCSubtargetInfo &STI) const;
 
+  void expandAddBaseIdx(const MCInst &MI, SmallVectorImpl<char> &CB,
+                        SmallVectorImpl<MCFixup> &Fixups,
+                        const MCSubtargetInfo &STI) const;
+
   void expandLongCondBr(const MCInst &MI, SmallVectorImpl<char> &CB,
                         SmallVectorImpl<MCFixup> &Fixups,
                         const MCSubtargetInfo &STI) const;
@@ -321,6 +325,68 @@ void RISCVMCCodeEmitter::expandAddTPRel(const MCInst &MI,
   support::endian::write(CB, Binary, llvm::endianness::little);
 }
 
+static unsigned getAddOpAndFixups(unsigned AddOp) {
+  switch (AddOp) {
+  default:
+    llvm_unreachable("Unexpected ADD or SHXADD Opcode on GP-relative!");
+  case RISCV::PseudoAddBaseIdx:
+    return RISCV::ADD;
+  case RISCV::PseudoAddUWBaseIdx:
+    return RISCV::ADD_UW;
+  case RISCV::PseudoSh1AddBaseIdx:
+    return RISCV::SH1ADD;
+  case RISCV::PseudoSh2AddBaseIdx:
+    return RISCV::SH2ADD;
+  case RISCV::PseudoSh3AddBaseIdx:
+    return RISCV::SH3ADD;
+  case RISCV::PseudoSh1AddUWBaseIdx:
+    return RISCV::SH1ADD_UW;
+  case RISCV::PseudoSh2AddUWBaseIdx:
+    return RISCV::SH2ADD_UW;
+  case RISCV::PseudoSh3AddUWBaseIdx:
+    return RISCV::SH3ADD_UW;
+  }
+}
+
+// Pseudo ADD/SHXADD (base-index variant) to a simple ADD or SHXADD with the
+// correct relocation.
+void RISCVMCCodeEmitter::expandAddBaseIdx(const MCInst &MI,
+                                          SmallVectorImpl<char> &CB,
+                                          SmallVectorImpl<MCFixup> &Fixups,
+                                          const MCSubtargetInfo &STI) const {
+  MCOperand DestReg = MI.getOperand(0);
+  // At link time rs2 of the add/shXadd may be rewritten to gp (or zero for
+  // abs-near) when the symbol can be reached via gp-relative addressing.
+  MCOperand Src1 = MI.getOperand(1);
+  MCOperand Src2 = MI.getOperand(2);
+
+  MCOperand SrcSymbol = MI.getOperand(3);
+  assert(SrcSymbol.isExpr() &&
+         "Expected expression as third input to GP-relative add");
+
+  const auto *Expr = dyn_cast<MCSpecifierExpr>(SrcSymbol.getExpr());
+  assert(Expr && (Expr->getSpecifier() == ELF::R_RISCV_BASE_IDX_ADD) &&
+         "Expected expression as third input to base+index add");
+
+  unsigned BuildOpcode = getAddOpAndFixups(MI.getOpcode());
+
+  // Emit the correct base_idx_add relocation for the symbol.
+  addFixup(Fixups, 0, Expr, ELF::R_RISCV_BASE_IDX_ADD);
+
+  // Emit base_idx_add where the relax feature is enabled.
+  if (STI.hasFeature(RISCV::FeatureRelax)) {
+    Fixups.back().setLinkerRelaxable();
+  }
+
+  // Emit a normal ADD or SHXADD instruction with the given operands.
+  MCInst TmpInst = MCInstBuilder(BuildOpcode)
+                       .addOperand(DestReg)
+                       .addOperand(Src1)
+                       .addOperand(Src2);
+  uint32_t Binary = getBinaryCodeForInstr(TmpInst, Fixups, STI);
+  support::endian::write(CB, Binary, llvm::endianness::little);
+}
+
 static unsigned getInvertedBranchOp(unsigned BrOp) {
   switch (BrOp) {
   default:
@@ -589,6 +655,17 @@ void RISCVMCCodeEmitter::encodeInstruction(const MCInst &MI,
     expandAddTPRel(MI, CB, Fixups, STI);
     MCNumEmitted += 1;
     return;
+  case RISCV::PseudoAddBaseIdx:
+  case RISCV::PseudoAddUWBaseIdx:
+  case RISCV::PseudoSh1AddBaseIdx:
+  case RISCV::PseudoSh2AddBaseIdx:
+  case RISCV::PseudoSh3AddBaseIdx:
+  case RISCV::PseudoSh1AddUWBaseIdx:
+  case RISCV::PseudoSh2AddUWBaseIdx:
+  case RISCV::PseudoSh3AddUWBaseIdx:
+    expandAddBaseIdx(MI, CB, Fixups, STI);
+    MCNumEmitted += 1;
+    return;
   case RISCV::PseudoLongBEQ:
   case RISCV::PseudoLongBNE:
   case RISCV::PseudoLongBEQI:
@@ -807,12 +884,16 @@ uint64_t RISCVMCCodeEmitter::getImmOpValue(const MCInst &MI, unsigned OpNo,
              "invalid specifier");
       break;
     case ELF::R_RISCV_TPREL_ADD:
-      // tprel_add is only used to indicate that a relocation should be emitted
-      // for an add instruction used in TP-relative addressing. It should not be
-      // expanded as if representing an actual instruction operand and so to
-      // encounter it here is an error.
+    case ELF::R_RISCV_BASE_IDX_ADD:
+      // tprel_add / base_idx_add are only used to indicate that a relocation
+      // should be emitted for an add instruction used in TP-relative or
+      // base+index addressing. They should not be expanded as if representing
+      // an actual instruction operand and so to encounter them here is an
+      // error.
+
       llvm_unreachable(
-          "ELF::R_RISCV_TPREL_ADD should not represent an instruction operand");
+          "R_RISCV_TPREL_ADD or R_RISCV_BASE_IDX_ADD should not represent "
+          "an instruction operand");
     case RISCV::S_QC_ACCESS:
       // The same logic for tprel_add applies to S_QC_ACCESS, for similar
       // reasons, but we use a specifier becuase %qc.access() gets expanded
@@ -832,6 +913,16 @@ uint64_t RISCVMCCodeEmitter::getImmOpValue(const MCInst &MI, unsigned OpNo,
       FixupKind = RISCV::fixup_riscv_hi20;
       RelaxCandidate = true;
       break;
+    case RISCV::S_BASE_IDX_LO:
+      if (MIFrm == RISCVII::InstFormatI)
+        FixupKind = ELF::R_RISCV_BASE_IDX_LO12_I;
+      else if (MIFrm == RISCVII::InstFormatS)
+        FixupKind = ELF::R_RISCV_BASE_IDX_LO12_S;
+      else
+        llvm_unreachable(
+            "S_BASE_IDX_LO used with unexpected instruction format");
+      RelaxCandidate = true;
+      break;
     case RISCV::S_PCREL_LO:
       if (MIFrm == RISCVII::InstFormatI)
         FixupKind = RISCV::fixup_riscv_pcrel_lo12_i;
diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.cpp
index cf9db21a47dcb..8781906b46e83 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.cpp
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.cpp
@@ -24,6 +24,8 @@ RISCV::Specifier RISCV::parseSpecifierName(StringRef name) {
   return StringSwitch<RISCV::Specifier>(name)
       .Case("lo", RISCV::S_LO)
       .Case("hi", ELF::R_RISCV_HI20)
+      .Case("base_idx_lo", RISCV::S_BASE_IDX_LO)
+      .Case("base_idx_add", ELF::R_RISCV_BASE_IDX_ADD)
       .Case("pcrel_lo", RISCV::S_PCREL_LO)
       .Case("pcrel_hi", RISCV::S_PCREL_HI)
       .Case("got_pcrel_hi", RISCV::S_GOT_HI)
@@ -52,6 +54,10 @@ StringRef RISCV::getSpecifierName(Specifier S) {
     return "lo";
   case ELF::R_RISCV_HI20:
     return "hi";
+  case RISCV::S_BASE_IDX_LO:
+    return "base_idx_lo";
+  case ELF::R_RISCV_BASE_IDX_ADD:
+    return "base_idx_add";
   case RISCV::S_PCREL_LO:
     return "pcrel_lo";
   case RISCV::S_PCREL_HI:
diff --git a/llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp b/llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp
index e37ea27364e6e..e0c4cb85431d0 100644
--- a/llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp
+++ b/llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp
@@ -1079,6 +1079,12 @@ static MCOperand lowerSymbolOperand(const MachineOperand &MO, MCSymbol *Sym,
   case RISCVII::MO_HI:
     Kind = ELF::R_RISCV_HI20;
     break;
+  case RISCVII::MO_BASE_IDX_LO:
+    Kind = RISCV::S_BASE_IDX_LO;
+    break;
+  case RISCVII::MO_BASE_IDX_ADD:
+    Kind = ELF::R_RISCV_BASE_IDX_ADD;
+    break;
   case RISCVII::MO_PCREL_LO:
     Kind = RISCV::S_PCREL_LO;
     break;
diff --git a/llvm/lib/Target/RISCV/RISCVInstrFormats.td b/llvm/lib/Target/RISCV/RISCVInstrFormats.td
index b945fa957072e..2a6080721f92f 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrFormats.td
+++ b/llvm/lib/Target/RISCV/RISCVInstrFormats.td
@@ -365,6 +365,17 @@ class PseudoStore<string opcodestr, DAGOperand rsty = GPR>
   let isCodeGenOnly = 0;
 }
 
+// Pseudo add or shxadd instructions.
+class PseudoBaseIdx<string opcodestr>
+    : Pseudo<(outs GPR:$rd),
+             (ins GPR:$rs1, GPR:$rs2, base_idx_add_symbol:$src), [], opcodestr,
+             "$rd, $rs1, $rs2, $src"> {
+  let hasSideEffects = 0;
+  let mayLoad = 1;
+  let mayStore = 0;
+  let isCodeGenOnly = 0;
+}
+
 // Instruction formats are listed in the order they appear in the RISC-V
 // instruction set manual (R, R4, I, S, B, U, J).
 
diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp b/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp
index 50f548857a97b..976bec1cace5d 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp
+++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp
@@ -3613,6 +3613,8 @@ RISCVInstrInfo::getSerializableDirectMachineOperandTargetFlags() const {
       {MO_CALL, "riscv-call"},
       {MO_LO, "riscv-lo"},
       {MO_HI, "riscv-hi"},
+      {MO_BASE_IDX_LO, "riscv-base-idx-lo"},
+      {MO_BASE_IDX_ADD, "riscv-base-idx-add"},
       {MO_PCREL_LO, "riscv-pcrel-lo"},
       {MO_PCREL_HI, "riscv-pcrel-hi"},
       {MO_GOT_HI, "riscv-got-hi"},
diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfo.td b/llvm/lib/Target/RISCV/RISCVInstrInfo.td
index ca506b8013212..317caf688e00a 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrInfo.td
+++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.td
@@ -486,6 +486,19 @@ def tprel_add_symbol : Operand<XLenVT> {
   let ParserMatchClass = TPRelAddSymbol;
 }
 
+def BaseIdxAddSymbol : AsmOperandClass {
+  let Name = "BaseIdxAddSymbol";
+  let RenderMethod = "addImmOperands";
+  let DiagnosticType = "InvalidBaseIdxAddSymbol";
+  let DiagnosticString = "operand must be a symbol with %base_idx_add specifier";
+  let ParserMethod = "parseOperandWithSpecifier";
+}
+
+// A symbol with the %base_idx_add variant.
+def base_idx_add_symbol : Operand<XLenVT> {
+  let ParserMatchClass = BaseIdxAddSymbol;
+}
+
 def CSRSystemRegister : AsmOperandClass {
   let Name = "CSRSystemRegister";
   let ParserMethod = "parseCSRSystemRegister";
@@ -1545,11 +1558,18 @@ def : PatGprShiftMaskXLen<sra, SRA>;
 // fourth operand to emit a relocation on a symbol relating to this instruction.
 // The relocation does not affect any bits of the instruction itself but is used
 // as a hint to the linker.
-let hasSideEffects = 0, mayLoad = 0, mayStore = 0, isCodeGenOnly = 0 in
+let hasSideEffects = 0, mayLoad = 0, mayStore = 0, isCodeGenOnly = 0 in {
 def PseudoAddTPRel : Pseudo<(outs GPR:$rd),
                             (ins GPR:$rs1, GPR:$rs2, tprel_add_symbol:$src), [],
                             "add", "$rd, $rs1, $rs2, $src">;
 
+// This is a special case of the ADD instruction used to facilitate the use of a
+// fourth operand to emit a relocation on a symbol relating to this instruction.
+// The relocation does not affect any bits of the instruction itself but is used
+// as a hint to the linker.
+def PseudoAddBaseIdx : PseudoBaseIdx<"add">;
+}
+
 /// FrameIndex calculations
 
 def : Pat<(frameindex:$fi), (ADDI (iPTR (to_tframeindex $fi)), 0)>;
diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfoZb.td b/llvm/lib/Target/RISCV/RISCVInstrInfoZb.td
index 418efefe58318..5491e0cbd193c 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrInfoZb.td
+++ b/llvm/lib/Target/RISCV/RISCVInstrInfoZb.td
@@ -258,6 +258,23 @@ class RVBShift_ri<bits<5> imm11_7, bits<3> funct3, RISCVOpcode opcode,
                    (ins GPR:$rs1, uimmlog2xlen:$shamt), opcodestr,
                    "$rd, $rs1, $shamt">;
 
+// This is a special case of the SH1ADD/SH2ADD/SH3ADD instruction used to
+// facilitate the use of a fourth operand to emit a relocation on a symbol
+// relating to this instruction. The relocation does not affect any bits of the
+// instruction itself but is used as a hint to the linker.
+let Predicates = [HasStdExtZba] in {
+  def PseudoSh1AddBaseIdx : PseudoBaseIdx<"sh1add">;
+  def PseudoSh2AddBaseIdx : PseudoBaseIdx<"sh2add">;
+  def PseudoSh3AddBaseIdx : PseudoBaseIdx<"sh3add">;
+}
+
+let Predicates = [HasStdExtZba, IsRV64] in {
+  def PseudoAddUWBaseIdx : PseudoBaseIdx<"add.uw">;
+  def PseudoSh1AddUWBaseIdx : PseudoBaseIdx<"sh1add.uw">;
+  def PseudoSh2AddUWBaseIdx : PseudoBaseIdx<"sh2add.uw">;
+  def PseudoSh3AddUWBaseIdx : PseudoBaseIdx<"sh3add.uw">;
+}
+
 //===----------------------------------------------------------------------===//
 // Instructions
 //===----------------------------------------------------------------------===//
diff --git a/llvm/test/MC/RISCV/Relocations/mc-dump.s b/llvm/test/MC/RISCV/Relocations/mc-dump.s
index 82b30aabdb781..c6f7a4a231084 100644
--- a/llvm/test/MC/RISCV/Relocations/mc-dump.s
+++ b/llvm/test/MC/RISCV/Relocations/mc-dump.s
@@ -7,7 +7,7 @@
 # CHECK-NEXT:  Align:4 Fill:0 FillLen:1 MaxBytesToEmit:4 Nops
 # CHECK-NEXT:  Symbol @0 .text
 # CHECK-NEXT:0 Data LinkerRelaxable Size:8 [97,00,00,00,e7,80,00,00]
-# CHECK-NEXT:  Fixup @0 Value:specifier(4014,ext) Kind:4023 LinkerRelaxable
+# CHECK-NEXT:  Fixup @0 Value:specifier(4015,ext) Kind:4023 LinkerRelaxable
 # CHECK-NEXT:  Symbol @0 $x
 # CHECK-NEXT:8 Align LinkerRelaxable Size:0+6 []
 # CHECK-NEXT:  Align:8 Fill:0 FillLen:1 MaxBytesToEmit:8 Nops
diff --git a/llvm/test/MC/RISCV/Relocations/relocations.s b/llvm/test/MC/RISCV/Relocations/relocations.s
index 42cdfe338fe54..caf722202d022 100644
--- a/llvm/test/MC/RISCV/Relocations/relocations.s
+++ b/llvm/test/MC/RISCV/Relocations/relocations.s
@@ -1,8 +1,8 @@
-# RUN: llvm-mc -triple riscv32 -M no-aliases %s -show-encoding \
+# RUN: llvm-mc -triple riscv64 -mattr=+zba -M no-aliases %s -show-encoding \
 # RUN:     | FileCheck -check-prefix=INSTR %s
-# RUN: llvm-mc -filetype=obj -triple riscv32 -mattr=+c %s \
+# RUN: llvm-mc -filetype=obj -triple riscv64 -mattr=+c,+zba %s \
 # RUN:     | llvm-readobj -r - | FileCheck -check-prefix=RELOC %s
-# RUN: llvm-mc -filetype=obj -triple riscv32 -mattr=+c,+relax %s \
+# RUN: llvm-mc -filetype=obj -triple riscv64 -mattr=+c,+zba,+relax %s \
 # RUN:     | llvm-readobj -r - | FileCheck -check-prefix=RELOC-RELAX %s
 
 # Check prefixes:
@@ -51,6 +51,14 @@ addi t1, t1, %tprel_lo(foo+4)
 # RELOC: R_RISCV_TPREL_LO12_I foo 0x4
 # INSTR: addi t1, t1, %tprel_lo(foo+4)
 
+addi t1, t1, %base_idx_lo(foo)
+# RELOC: R_RISCV_BASE_IDX_LO12_I foo 0x0
+# INSTR: addi t1, t1, %base_idx_lo(foo)
+
+addi t1, t1, %base_idx_lo(foo+4)
+# RELOC: R_RISCV_BASE_IDX_LO12_I foo 0x4
+# INSTR: addi t1, t1, %base_idx_lo(foo+4)
+
 sb t1, %lo(foo)(a2)
 # RELOC: R_RISCV_LO12_S foo 0x0
 # INSTR: sb t1, %lo(foo)(a2)
@@ -67,6 +75,14 @@ sb t1, %tprel_lo(foo+4)(a2)
 # RELOC: R_RISCV_TPREL_LO12_S foo 0x4
 # INSTR: sb t1, %tprel_lo(foo+4)(a2)
 
+sb t1, %base_idx_lo(foo)(a2)
+# RELOC: R_RISCV_BASE_IDX_LO12_S foo 0x0
+# INSTR: sb t1, %base_idx_lo(foo)(a2)
+
+sb t1, %base_idx_lo(foo+4)(a2)
+# RELOC: R_RISCV_BASE_IDX_LO12_S foo 0x4
+# INSTR: sb t1, %base_idx_lo(foo+4)(a2)
+
 .L0:
 auipc t1, %pcrel_hi(foo)
 # RELOC: R_RISCV_PCREL_HI20 foo 0x0
@@ -142,6 +158,38 @@ add t1, t1, tp, %tprel_add(foo)
 # RELOC: R_RISCV_TPREL_ADD foo 0x0
 # INSTR: add t1, t1, tp, %tprel_add(foo)
 
+add t1, t1, t2, %base_idx_add(foo)
+# RELOC: R_RISCV_BASE_IDX_ADD foo 0x0
+# INSTR: add t1, t1, t2, %base_idx_add(foo)
+
+add.uw t1, t1, t2, %base_idx_add(foo)
+# RELOC: R_RISCV_BASE_IDX_ADD foo 0x0
+# INSTR: add.uw t1, t1, t2, %base_idx_add(foo)
+
+sh1add t1, t1, t2, %base_idx_add(foo)
+# RELOC: R_RISCV_BASE_IDX_ADD foo 0x0
+# INSTR: sh1add t1, t1, t2, %base_idx_add(foo)
+
+sh1add.uw t1, t1, t2, %base_idx_add(foo)
+# RELOC: R_RISCV_BASE_IDX_ADD foo 0x0
+# INSTR: sh1add.uw t1, t1, t2, %base_idx_add(foo)
+
+sh2add t1, t1, t2, %base_idx_add(foo)
+# RELOC: R_RISCV_BASE_IDX_ADD foo 0x0
+# INSTR: sh2add t1, t1, t2, %base_idx_add(foo)
+
+sh2add.uw t1, t1, t2, %base_idx_add(foo)
+# RELOC: R_RISCV_BASE_IDX_ADD foo 0x0
+# INSTR: sh2add.uw t1, t1, t2, %base_idx_add(foo)
+
+sh3add t1, t1, t2, %base_idx_add(foo)
+# RELOC: R_RISCV_BASE_IDX_ADD foo 0x0
+# INSTR: sh3add t1, t1, t2, %base_idx_add(foo)
+
+sh3add.uw t1, t1, t2, %base_idx_add(foo)
+# RELOC: R_RISCV_BASE_IDX_ADD foo 0x0
+# INSTR: sh3add.uw t1, t1, t2, %base_idx_add(foo)
+
 jal zero, foo
 # RELOC: R_RISCV_JAL
 # INSTR: jal zero, foo
diff --git a/llvm/test/MC/RISCV/rv32zba-invalid.s b/llvm/test/MC/RISCV/rv32zba-invalid.s
index 41e8076f49572..a1f9d5cd20368 100644
--- a/llvm/test/MC/RISCV/rv32zba-invalid.s
+++ b/llvm/test/MC/RISCV/rv32zba-invalid.s
@@ -11,3 +11,19 @@ add.uw t0, t1, t2 # CHECK: :[[@LINE]]:1: error: instruction requires the followi
 sh1add.uw t0, t1, t2 # CHECK: :[[@LINE]]:1: error: instruction requires the following: RV64I Base Instruction Set{{$}}
 sh2add.uw t0, t1, t2 # CHECK: :[[@LINE]]:1: error: instruction requires the following: RV64I Base Instruction Set{{$}}
 sh3add.uw t0, t1, t2 # CHECK: :[[@LINE]]:1: error: instruction requires the following: RV64I Base Instruction Set{{$}}
+
+# Base+index symbol names require a %base_idx_add modifier.
+sh1add a0, a0, a1, %hi(foo)
+# CHECK: :[[@LINE-1]]:1: error: invalid instruction, any one of the following would fix this:
+# CHECK: :[[@LINE-2]]:20: note: invalid operand for instruction
+# CHECK: :[[@LINE-3]]:20: note: operand must be a symbol with %base_idx_add specifier
+
+sh2add a0, a0, a1, %hi(foo)
+# CHECK: :[[@LINE-1]]:1: error: invalid instruction, any one of the following would fix this:
+# CHECK: :[[@LINE-2]]:20: note: invalid operand for instruction
+# CHECK: :[[@LINE-3]]:20: note: operand must be a symbol with %base_idx_add specifier
+
+sh3add a0, a0, a1, %hi(foo)
+# CHECK: :[[@LINE-1]]:1: error: invalid instruction, any one of the following would fix this:
+# CHECK: :[[@LINE-2]]:20: note: invalid operand for instruction
+# CHECK: :[[@LINE-3]]:20: note: operand must be a symbol with %base_idx_add specifier
diff --git a/llvm/test/MC/RISCV/rv64zba-invalid.s b/llvm/test/MC/RISCV/rv64zba-invalid.s
index 8dbea991950b4..bece86a76d9ef 100644
--- a/llvm/test/MC/RISCV/rv64zba-invalid.s
+++ b/llvm/test/MC/RISCV/rv64zba-invalid.s
@@ -13,3 +13,39 @@ sh1add.uw t0, t1 # CHECK: :[[@LINE]]:17: error: too few operands for instruction
 sh2add.uw t0, t1 # CHECK: :[[@LINE]]:17: error: too few operands for instruction
 # Too few operands
 sh3add.uw t0, t1 # CHECK: :[[@LINE]]:17: error: too few operands for instruction
+
+# Base+index symbol names require a %base_idx_add modifier.
+sh1add a0, a0, a1, %hi(foo)
+# CHECK: :[[@LINE-1]]:1: error: invalid instruction, any one of the following would fix this:
+# CHECK: :[[@LINE-2]]:20: note: invalid operand for instruction
+# CHECK: :[[@LINE-3]]:20: note: operand must be a symbol with %base_idx_add specifier
+
+sh2add a0, a0, a1, %hi(foo)
+# CHECK: :[[@LINE-1]]:1: error: invalid instruction, any one of the following would fix this:
+# CHECK: :[[@LINE-2]]:20: note: invalid operand for instruction
+# CHECK: :[[@LINE-3]]:20: note: operand must be a symbol with %base_idx_add specifier
+
+sh3add a0, a0, a1, %hi(foo)
+# CHECK: :[[@LINE-1]]:1: error: invalid instruction, any one of the following would fix this:
+# CHECK: :[[@LINE-2]]:20: note: invalid operand for instruction
+# CHECK: :[[@LINE-3]]:20: note: operand must be a symbol with %base_idx_add specifier
+
+add.uw a0, a0, a1, %hi(foo)
+# CHECK: :[[@LINE-1]]:1: error: invalid instruction, any one of the following would fix this:
+# CHECK: :[[@LINE-2]]:20: note: invalid operand for instruction
+# CHECK: :[[@LINE-3]]:20: note: operand must be a symbol with %base_idx_add specifier
+
+sh1add.uw a0, a0, a1, %hi(foo)
+# CHECK: :[[@LINE-1]]:1: error: invalid instruction, any one of the following would fix this:
+# CHECK: :[[@LINE-2]]:23: note: invalid operand for instruction
+# CHECK: :[[@LINE-3]]:23: note: operand must be a symbol with %base_idx_add specifier
+
+sh2add.uw a0, a0, a1, %hi(foo)
+# CHECK: :[[@LINE-1]]:1: error: invalid instruction, any one of the following would fix this:
+# CHECK: :[[@LINE-2]]:23: note: invalid operand for instruction
+# CHECK: :[[@LINE-3]]:23: note: operand must be a symbol with %base_idx_add specifier
+
+sh3add.uw a0, a0, a1, %hi(foo)
+# CHECK: :[[@LINE-1]]:1: error: invalid instruction, any one of the following would fix this:
+# CHECK: :[[@LINE-2]]:23: note: invalid operand for instruction
+# CHECK: :[[@LINE-3]]:23: note: operand must be a symbol with %base_idx_add specifier
\ No newline at end of file

>From ea2d0c067b68f970cae2fafd484b8883ee0dd9fd Mon Sep 17 00:00:00 2001
From: wengliqin <liqin.weng at spacemit.com>
Date: Wed, 8 Jul 2026 15:36:48 +0800
Subject: [PATCH 2/3] fix the comments

---
 .../RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp  | 18 +++++++++---------
 llvm/lib/Target/RISCV/RISCVInstrFormats.td     |  2 +-
 llvm/lib/Target/RISCV/RISCVInstrInfo.td        |  3 +--
 3 files changed, 11 insertions(+), 12 deletions(-)

diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp
index d53fcd9c3d6fc..4ac9cf9dd5006 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp
@@ -328,7 +328,7 @@ void RISCVMCCodeEmitter::expandAddTPRel(const MCInst &MI,
 static unsigned getAddOpAndFixups(unsigned AddOp) {
   switch (AddOp) {
   default:
-    llvm_unreachable("Unexpected ADD or SHXADD Opcode on GP-relative!");
+    llvm_unreachable("Unexpected ADD or SHXADD Opcode on base+idx!");
   case RISCV::PseudoAddBaseIdx:
     return RISCV::ADD;
   case RISCV::PseudoAddUWBaseIdx:
@@ -348,21 +348,21 @@ static unsigned getAddOpAndFixups(unsigned AddOp) {
   }
 }
 
-// Pseudo ADD/SHXADD (base-index variant) to a simple ADD or SHXADD with the
-// correct relocation.
+// Expand pseudo ADD/SHXADD (base-index variant) to a simple ADD or SHXADD with
+// the correct relocation.
 void RISCVMCCodeEmitter::expandAddBaseIdx(const MCInst &MI,
                                           SmallVectorImpl<char> &CB,
                                           SmallVectorImpl<MCFixup> &Fixups,
                                           const MCSubtargetInfo &STI) const {
-  MCOperand DestReg = MI.getOperand(0);
+  const MCOperand &Dest = MI.getOperand(0);
   // At link time rs2 of the add/shXadd may be rewritten to gp (or zero for
   // abs-near) when the symbol can be reached via gp-relative addressing.
-  MCOperand Src1 = MI.getOperand(1);
-  MCOperand Src2 = MI.getOperand(2);
+  const MCOperand &Src1 = MI.getOperand(1);
+  const MCOperand &Src2 = MI.getOperand(2);
 
-  MCOperand SrcSymbol = MI.getOperand(3);
+  const MCOperand &SrcSymbol = MI.getOperand(3);
   assert(SrcSymbol.isExpr() &&
-         "Expected expression as third input to GP-relative add");
+         "Expected expression as third input to base+idx add");
 
   const auto *Expr = dyn_cast<MCSpecifierExpr>(SrcSymbol.getExpr());
   assert(Expr && (Expr->getSpecifier() == ELF::R_RISCV_BASE_IDX_ADD) &&
@@ -380,7 +380,7 @@ void RISCVMCCodeEmitter::expandAddBaseIdx(const MCInst &MI,
 
   // Emit a normal ADD or SHXADD instruction with the given operands.
   MCInst TmpInst = MCInstBuilder(BuildOpcode)
-                       .addOperand(DestReg)
+                       .addOperand(Dest)
                        .addOperand(Src1)
                        .addOperand(Src2);
   uint32_t Binary = getBinaryCodeForInstr(TmpInst, Fixups, STI);
diff --git a/llvm/lib/Target/RISCV/RISCVInstrFormats.td b/llvm/lib/Target/RISCV/RISCVInstrFormats.td
index 2a6080721f92f..e2b3ccc94253c 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrFormats.td
+++ b/llvm/lib/Target/RISCV/RISCVInstrFormats.td
@@ -371,7 +371,7 @@ class PseudoBaseIdx<string opcodestr>
              (ins GPR:$rs1, GPR:$rs2, base_idx_add_symbol:$src), [], opcodestr,
              "$rd, $rs1, $rs2, $src"> {
   let hasSideEffects = 0;
-  let mayLoad = 1;
+  let mayLoad = 0;
   let mayStore = 0;
   let isCodeGenOnly = 0;
 }
diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfo.td b/llvm/lib/Target/RISCV/RISCVInstrInfo.td
index 317caf688e00a..0456b7e3cf5d3 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrInfo.td
+++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.td
@@ -1558,7 +1558,7 @@ def : PatGprShiftMaskXLen<sra, SRA>;
 // fourth operand to emit a relocation on a symbol relating to this instruction.
 // The relocation does not affect any bits of the instruction itself but is used
 // as a hint to the linker.
-let hasSideEffects = 0, mayLoad = 0, mayStore = 0, isCodeGenOnly = 0 in {
+let hasSideEffects = 0, mayLoad = 0, mayStore = 0, isCodeGenOnly = 0 in
 def PseudoAddTPRel : Pseudo<(outs GPR:$rd),
                             (ins GPR:$rs1, GPR:$rs2, tprel_add_symbol:$src), [],
                             "add", "$rd, $rs1, $rs2, $src">;
@@ -1568,7 +1568,6 @@ def PseudoAddTPRel : Pseudo<(outs GPR:$rd),
 // The relocation does not affect any bits of the instruction itself but is used
 // as a hint to the linker.
 def PseudoAddBaseIdx : PseudoBaseIdx<"add">;
-}
 
 /// FrameIndex calculations
 

>From 3f2d76887a4f500dbb0c0f3c3e1dee42d3893aee Mon Sep 17 00:00:00 2001
From: wengliqin <liqin.weng at spacemit.com>
Date: Tue, 28 Jul 2026 17:43:51 +0800
Subject: [PATCH 3/3] 1. Add relaxation for absolute int12  for
 R_RISCV_BASE_IDX* 2. change INTERNAL_R_RISCV_VASE_IDX_* to   
 INTERNAL_R_RISCV_BASE_IDX_GPREL*

---
 lld/ELF/Arch/RISCV.cpp               | 67 ++++++++++++++++++++++------
 lld/test/ELF/riscv-base-idx-add.s    | 25 ++++++++++-
 lld/test/ELF/riscv-base-idx-shxadd.s | 25 ++++++++++-
 llvm/test/MC/RISCV/rv32zba-invalid.s |  6 +--
 llvm/test/MC/RISCV/rv64zba-invalid.s | 14 +++---
 5 files changed, 110 insertions(+), 27 deletions(-)

diff --git a/lld/ELF/Arch/RISCV.cpp b/lld/ELF/Arch/RISCV.cpp
index cc233f357e2dc..c85d4d086fa00 100644
--- a/lld/ELF/Arch/RISCV.cpp
+++ b/lld/ELF/Arch/RISCV.cpp
@@ -74,9 +74,12 @@ class RISCV final : public TargetInfo {
 #define INTERNAL_R_RISCV_GPREL_S 257
 #define INTERNAL_R_RISCV_X0REL_I 258
 #define INTERNAL_R_RISCV_X0REL_S 259
-#define INTERNAL_R_RISCV_BASE_IDX_ADD 260
-#define INTERNAL_R_RISCV_BASE_IDX_LO12_I 261
-#define INTERNAL_R_RISCV_BASE_IDX_LO12_S 262
+#define INTERNAL_R_RISCV_BASE_IDX_X0REL_I 260
+#define INTERNAL_R_RISCV_BASE_IDX_X0REL_S 261
+#define INTERNAL_R_RISCV_BASE_IDX_X0REL_ADD 262
+#define INTERNAL_R_RISCV_BASE_IDX_GPREL_I 263
+#define INTERNAL_R_RISCV_BASE_IDX_GPREL_S 264
+#define INTERNAL_R_RISCV_BASE_IDX_GPREL_ADD 265
 
 const uint64_t dtpOffset = 0x800;
 
@@ -652,19 +655,37 @@ void RISCV::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const {
     return;
   }
 
-  case INTERNAL_R_RISCV_BASE_IDX_ADD: {
+  case INTERNAL_R_RISCV_BASE_IDX_X0REL_ADD: {
+    uint32_t insn = (read32le(loc) & ~(31 << 20)) | (X_X0 << 20);
+    write32le(loc, insn);
+    return;
+  }
+
+  case INTERNAL_R_RISCV_BASE_IDX_X0REL_I:
+  case INTERNAL_R_RISCV_BASE_IDX_X0REL_S: {
+    checkInt(ctx, loc, val, 12, rel);
+    uint32_t insn = read32le(loc);
+    if (rel.type == INTERNAL_R_RISCV_BASE_IDX_X0REL_I)
+      insn = setLO12_I(insn, val);
+    else
+      insn = setLO12_S(insn, val);
+    write32le(loc, insn);
+    return;
+  }
+
+  case INTERNAL_R_RISCV_BASE_IDX_GPREL_ADD: {
     uint32_t insn = (read32le(loc) & ~(31 << 20)) | (X_GP << 20);
     write32le(loc, insn);
     return;
   }
 
-  case INTERNAL_R_RISCV_BASE_IDX_LO12_I:
-  case INTERNAL_R_RISCV_BASE_IDX_LO12_S: {
+  case INTERNAL_R_RISCV_BASE_IDX_GPREL_I:
+  case INTERNAL_R_RISCV_BASE_IDX_GPREL_S: {
     Defined *gp = ctx.sym.riscvGlobalPointer;
     int64_t displace = SignExtend64(val - gp->getVA(ctx), bits);
     checkInt(ctx, loc, displace, 12, rel);
     uint32_t insn = read32le(loc);
-    if (rel.type == INTERNAL_R_RISCV_BASE_IDX_LO12_I)
+    if (rel.type == INTERNAL_R_RISCV_BASE_IDX_GPREL_I)
       insn = setLO12_I(insn, displace);
     else
       insn = setLO12_S(insn, displace);
@@ -1038,6 +1059,23 @@ static void relaxHi20Lo12(Ctx &ctx, const InputSection &sec, size_t i,
     case R_RISCV_LO12_S:
       sec.relaxAux->relocTypes[i] = INTERNAL_R_RISCV_X0REL_S;
       break;
+    case R_RISCV_BASE_IDX_ADD: {
+      uint32_t insn = read32le(sec.content().data() + r.offset) & 0xFE000000;
+      if (insn == 0) {
+        // remove add a0, a0, zero
+        sec.relaxAux->relocTypes[i] = R_RISCV_RELAX;
+        remove = 4;
+      } else {
+        sec.relaxAux->relocTypes[i] = INTERNAL_R_RISCV_BASE_IDX_X0REL_ADD;
+      }
+      break;
+    }
+    case R_RISCV_BASE_IDX_LO12_I:
+      sec.relaxAux->relocTypes[i] = INTERNAL_R_RISCV_BASE_IDX_X0REL_I;
+      break;
+    case R_RISCV_BASE_IDX_LO12_S:
+      sec.relaxAux->relocTypes[i] = INTERNAL_R_RISCV_BASE_IDX_X0REL_S;
+      break;
     }
     return;
   }
@@ -1062,13 +1100,13 @@ static void relaxHi20Lo12(Ctx &ctx, const InputSection &sec, size_t i,
     sec.relaxAux->relocTypes[i] = INTERNAL_R_RISCV_GPREL_S;
     break;
   case R_RISCV_BASE_IDX_ADD:
-    sec.relaxAux->relocTypes[i] = INTERNAL_R_RISCV_BASE_IDX_ADD;
+    sec.relaxAux->relocTypes[i] = INTERNAL_R_RISCV_BASE_IDX_GPREL_ADD;
     break;
   case R_RISCV_BASE_IDX_LO12_I:
-    sec.relaxAux->relocTypes[i] = INTERNAL_R_RISCV_BASE_IDX_LO12_I;
+    sec.relaxAux->relocTypes[i] = INTERNAL_R_RISCV_BASE_IDX_GPREL_I;
     break;
   case R_RISCV_BASE_IDX_LO12_S:
-    sec.relaxAux->relocTypes[i] = INTERNAL_R_RISCV_BASE_IDX_LO12_S;
+    sec.relaxAux->relocTypes[i] = INTERNAL_R_RISCV_BASE_IDX_GPREL_S;
     break;
   }
 }
@@ -1381,9 +1419,12 @@ void RISCV::finalizeRelax(int passes) const {
           case INTERNAL_R_RISCV_GPREL_S:
           case INTERNAL_R_RISCV_X0REL_I:
           case INTERNAL_R_RISCV_X0REL_S:
-          case INTERNAL_R_RISCV_BASE_IDX_ADD:
-          case INTERNAL_R_RISCV_BASE_IDX_LO12_I:
-          case INTERNAL_R_RISCV_BASE_IDX_LO12_S:
+          case INTERNAL_R_RISCV_BASE_IDX_X0REL_I:
+          case INTERNAL_R_RISCV_BASE_IDX_X0REL_S:
+          case INTERNAL_R_RISCV_BASE_IDX_X0REL_ADD:
+          case INTERNAL_R_RISCV_BASE_IDX_GPREL_I:
+          case INTERNAL_R_RISCV_BASE_IDX_GPREL_S:
+          case INTERNAL_R_RISCV_BASE_IDX_GPREL_ADD:
             break;
           case R_RISCV_RELAX:
             // Used by relaxTlsLe to indicate the relocation is ignored.
diff --git a/lld/test/ELF/riscv-base-idx-add.s b/lld/test/ELF/riscv-base-idx-add.s
index 17db2ce3e0a3a..04ede4a4d9197 100644
--- a/lld/test/ELF/riscv-base-idx-add.s
+++ b/lld/test/ELF/riscv-base-idx-add.s
@@ -4,8 +4,8 @@
 # RUN: llvm-mc -filetype=obj -triple=riscv32-unknown-elf -mattr=+relax a.s -o rv32.o
 # RUN: llvm-mc -filetype=obj -triple=riscv64-unknown-elf -mattr=+relax a.s -o rv64.o
 
-# RUN: ld.lld --relax-gp --undefined=__global_pointer$ rv32.o lds -o rv32
-# RUN: ld.lld --relax-gp --undefined=__global_pointer$ rv64.o lds -o rv64
+# RUN: ld.lld --relax-gp --undefined=__global_pointer$ --defsym baz=420 rv32.o lds -o rv32
+# RUN: ld.lld --relax-gp --undefined=__global_pointer$ --defsym baz=420 rv64.o lds -o rv64
 # RUN: llvm-objdump -td -M no-aliases --no-show-raw-insn rv32 | FileCheck %s
 # RUN: llvm-objdump -td -M no-aliases --no-show-raw-insn rv64 | FileCheck %s
 
@@ -26,6 +26,16 @@
 # CHECK-NEXT: add     a0, a0, a1
 # CHECK-NEXT: lw      a0, 0xe(a0)
 # CHECK-NEXT: sw      a0, 0xe(a0)
+# CHECK-NOT:  lui
+# CHECK-NEXT: addi    a1, a1, 0x0
+# CHECK-NOT:  add     a0, a0, zero
+# CHECK-NEXT: lw      a0, 0x0(a0)
+# CHECK-NEXT: sw      a0, 0x0(a0)
+# CHECK-NOT:  lui
+# CHECK-NEXT: addi    a1, a1, 0x1a4
+# CHECK-NOT:  add     a0, a0, zero
+# CHECK-NEXT: lw      a0, 0x1a4(a0)
+# CHECK-NEXT: sw      a0, 0x1a4(a0)
 # CHECK-EMPTY:
 # CHECK-NEXT: <a>:
 # CHECK-NEXT: addi a0, a0, 0x1
@@ -49,6 +59,16 @@ _start:
   add  a0, a0, a1, %base_idx_add(norelax+10)
   lw   a0, %base_idx_lo(norelax+10)(a0)
   sw   a0, %base_idx_lo(norelax+10)(a0)
+  lui  a1, %hi(undefined_weak)
+  addi a1, a1, %base_idx_lo(undefined_weak)
+  add  a0, a0, a1, %base_idx_add(undefined_weak)
+  lw   a0, %base_idx_lo(undefined_weak)(a0)
+  sw   a0, %base_idx_lo(undefined_weak)(a0)
+  lui  a1, %hi(baz)
+  addi a1, a1, %base_idx_lo(baz)
+  add  a0, a0, a1, %base_idx_add(baz)
+  lw   a0, %base_idx_lo(baz)(a0)
+  sw   a0, %base_idx_lo(baz)(a0)
 a:
   addi a0, a0, 1
 
@@ -62,6 +82,7 @@ array1:
 norelax:
   .zero   6
   .size   array, 6
+.weak undefined_weak
 
 #--- lds
 SECTIONS {
diff --git a/lld/test/ELF/riscv-base-idx-shxadd.s b/lld/test/ELF/riscv-base-idx-shxadd.s
index 32f2d97e668df..e9e71871ece3e 100644
--- a/lld/test/ELF/riscv-base-idx-shxadd.s
+++ b/lld/test/ELF/riscv-base-idx-shxadd.s
@@ -4,8 +4,8 @@
 # RUN: llvm-mc -filetype=obj -triple=riscv32-unknown-elf -mattr=+relax,+zba a.s -o rv32.o
 # RUN: llvm-mc -filetype=obj -triple=riscv64-unknown-elf -mattr=+relax,+zba a.s -o rv64.o
 
-# RUN: ld.lld --relax-gp --undefined=__global_pointer$ rv32.o lds -o rv32
-# RUN: ld.lld --relax-gp --undefined=__global_pointer$ rv64.o lds -o rv64
+# RUN: ld.lld --relax-gp --undefined=__global_pointer$ --defsym baz=420 rv32.o lds -o rv32
+# RUN: ld.lld --relax-gp --undefined=__global_pointer$ --defsym baz=420 rv64.o lds -o rv64
 # RUN: llvm-objdump --mattr=+zba -td -M no-aliases --no-show-raw-insn rv32 | FileCheck %s
 # RUN: llvm-objdump --mattr=+zba -td -M no-aliases --no-show-raw-insn rv64 | FileCheck %s
 
@@ -26,6 +26,16 @@
 # CHECK-NEXT: sh1add  a0, a0, a1
 # CHECK-NEXT: lw      a0, 0xe(a0)
 # CHECK-NEXT: sw      a0, 0xe(a0)
+# CHECK-NOT:  lui
+# CHECK-NEXT: addi    a1, a1, 0x0
+# CHECK-NEXT: sh2add  a0, a0, zero
+# CHECK-NEXT: lw      a0, 0x0(a0)
+# CHECK-NEXT: sw      a0, 0x0(a0)
+# CHECK-NOT:  lui
+# CHECK-NEXT: addi    a1, a1, 0x1a4
+# CHECK-NEXT: sh3add  a0, a0, zero
+# CHECK-NEXT: lw      a0, 0x1a4(a0)
+# CHECK-NEXT: sw      a0, 0x1a4(a0)
 # CHECK-EMPTY:
 # CHECK-NEXT: <a>:
 # CHECK-NEXT: addi a0, a0, 0x1
@@ -48,6 +58,16 @@ _start:
   sh1add  a0, a0, a1, %base_idx_add(norelax+10)
   lw   a0, %base_idx_lo(norelax+10)(a0)
   sw   a0, %base_idx_lo(norelax+10)(a0)
+  lui  a1, %hi(undefined_weak)
+  addi a1, a1, %base_idx_lo(undefined_weak)
+  sh2add  a0, a0, a1, %base_idx_add(undefined_weak)
+  lw   a0, %base_idx_lo(undefined_weak)(a0)
+  sw   a0, %base_idx_lo(undefined_weak)(a0)
+  lui  a1, %hi(baz)
+  addi a1, a1, %base_idx_lo(baz)
+  sh3add  a0, a0, a1, %base_idx_add(baz)
+  lw   a0, %base_idx_lo(baz)(a0)
+  sw   a0, %base_idx_lo(baz)(a0)
 a:
   addi a0, a0, 1
 
@@ -61,6 +81,7 @@ array1:
 norelax:
   .zero   6
   .size   array, 6
+.weak undefined_weak
 
 #--- lds
 SECTIONS {
diff --git a/llvm/test/MC/RISCV/rv32zba-invalid.s b/llvm/test/MC/RISCV/rv32zba-invalid.s
index a1f9d5cd20368..bac22926f233d 100644
--- a/llvm/test/MC/RISCV/rv32zba-invalid.s
+++ b/llvm/test/MC/RISCV/rv32zba-invalid.s
@@ -15,15 +15,15 @@ sh3add.uw t0, t1, t2 # CHECK: :[[@LINE]]:1: error: instruction requires the foll
 # Base+index symbol names require a %base_idx_add modifier.
 sh1add a0, a0, a1, %hi(foo)
 # CHECK: :[[@LINE-1]]:1: error: invalid instruction, any one of the following would fix this:
-# CHECK: :[[@LINE-2]]:20: note: invalid operand for instruction
+# CHECK: :[[@LINE-2]]:20: note: unexpected extra operand for instruction
 # CHECK: :[[@LINE-3]]:20: note: operand must be a symbol with %base_idx_add specifier
 
 sh2add a0, a0, a1, %hi(foo)
 # CHECK: :[[@LINE-1]]:1: error: invalid instruction, any one of the following would fix this:
-# CHECK: :[[@LINE-2]]:20: note: invalid operand for instruction
+# CHECK: :[[@LINE-2]]:20: note:  unexpected extra operand for instruction
 # CHECK: :[[@LINE-3]]:20: note: operand must be a symbol with %base_idx_add specifier
 
 sh3add a0, a0, a1, %hi(foo)
 # CHECK: :[[@LINE-1]]:1: error: invalid instruction, any one of the following would fix this:
-# CHECK: :[[@LINE-2]]:20: note: invalid operand for instruction
+# CHECK: :[[@LINE-2]]:20: note: unexpected extra operand for instruction
 # CHECK: :[[@LINE-3]]:20: note: operand must be a symbol with %base_idx_add specifier
diff --git a/llvm/test/MC/RISCV/rv64zba-invalid.s b/llvm/test/MC/RISCV/rv64zba-invalid.s
index bece86a76d9ef..a5bc9863cc1c6 100644
--- a/llvm/test/MC/RISCV/rv64zba-invalid.s
+++ b/llvm/test/MC/RISCV/rv64zba-invalid.s
@@ -17,35 +17,35 @@ sh3add.uw t0, t1 # CHECK: :[[@LINE]]:17: error: too few operands for instruction
 # Base+index symbol names require a %base_idx_add modifier.
 sh1add a0, a0, a1, %hi(foo)
 # CHECK: :[[@LINE-1]]:1: error: invalid instruction, any one of the following would fix this:
-# CHECK: :[[@LINE-2]]:20: note: invalid operand for instruction
+# CHECK: :[[@LINE-2]]:20: note: unexpected extra operand for instruction
 # CHECK: :[[@LINE-3]]:20: note: operand must be a symbol with %base_idx_add specifier
 
 sh2add a0, a0, a1, %hi(foo)
 # CHECK: :[[@LINE-1]]:1: error: invalid instruction, any one of the following would fix this:
-# CHECK: :[[@LINE-2]]:20: note: invalid operand for instruction
+# CHECK: :[[@LINE-2]]:20: note: unexpected extra operand for instruction
 # CHECK: :[[@LINE-3]]:20: note: operand must be a symbol with %base_idx_add specifier
 
 sh3add a0, a0, a1, %hi(foo)
 # CHECK: :[[@LINE-1]]:1: error: invalid instruction, any one of the following would fix this:
-# CHECK: :[[@LINE-2]]:20: note: invalid operand for instruction
+# CHECK: :[[@LINE-2]]:20: note: unexpected extra operand for instruction
 # CHECK: :[[@LINE-3]]:20: note: operand must be a symbol with %base_idx_add specifier
 
 add.uw a0, a0, a1, %hi(foo)
 # CHECK: :[[@LINE-1]]:1: error: invalid instruction, any one of the following would fix this:
-# CHECK: :[[@LINE-2]]:20: note: invalid operand for instruction
+# CHECK: :[[@LINE-2]]:20: note: unexpected extra operand for instruction
 # CHECK: :[[@LINE-3]]:20: note: operand must be a symbol with %base_idx_add specifier
 
 sh1add.uw a0, a0, a1, %hi(foo)
 # CHECK: :[[@LINE-1]]:1: error: invalid instruction, any one of the following would fix this:
-# CHECK: :[[@LINE-2]]:23: note: invalid operand for instruction
+# CHECK: :[[@LINE-2]]:23: note: unexpected extra operand for instruction
 # CHECK: :[[@LINE-3]]:23: note: operand must be a symbol with %base_idx_add specifier
 
 sh2add.uw a0, a0, a1, %hi(foo)
 # CHECK: :[[@LINE-1]]:1: error: invalid instruction, any one of the following would fix this:
-# CHECK: :[[@LINE-2]]:23: note: invalid operand for instruction
+# CHECK: :[[@LINE-2]]:23: note: unexpected extra operand for instruction
 # CHECK: :[[@LINE-3]]:23: note: operand must be a symbol with %base_idx_add specifier
 
 sh3add.uw a0, a0, a1, %hi(foo)
 # CHECK: :[[@LINE-1]]:1: error: invalid instruction, any one of the following would fix this:
-# CHECK: :[[@LINE-2]]:23: note: invalid operand for instruction
+# CHECK: :[[@LINE-2]]:23: note: unexpected extra operand for instruction
 # CHECK: :[[@LINE-3]]:23: note: operand must be a symbol with %base_idx_add specifier
\ No newline at end of file



More information about the llvm-commits mailing list