[lld] [llvm] [RISCV] Teach RISCVMergeBaseOffset to merge %lo into load/store folding arithmetic (PR #185353)

via llvm-commits llvm-commits at lists.llvm.org
Sun Mar 8 23:22:47 PDT 2026


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-lld-elf

Author: LiqinWeng (LiqinWeng)

<details>
<summary>Changes</summary>

It's possible we have:
1. first scenarios, the add opcode can be add or add.uw
```
lui     vr1, %hi(sym)
addi    vr1, vr1, %lo(sym)
add     vr2, vrx, vr1
lbu     vr3, off(vr2)

-----Transform-----

lui     vr1, %hi(sym+off)
add     vr2, vrx, vr1, %gprel_add(sym+off)
lbu     vr3, %gprel_lo(sym+off)(vr2)
```
2. second scenarios, the add opcode can be sh1add/sh2add/sh3add/sh1add.uw/sh2add.uw/sh3add.uw
```
lui     vr1, %hi(sym)
addi    vr1, vr1, %lo(sym)
sh1add  vr2, vrx, vr1
lbu     vr3, off(vr2)

-----Transform-----

lui     vr1, %hi(sym+off)
sh1add  vr2, vrx, vr1, %gprel_shxadd(sym+off)
lbu     vr3, %gprel_lo(sym+off)(vr2)
```
3. third scenarios, same as shxadd
```
addi    vrx, vr0, offAddi
lui     vr1, %hi(sym)
addi    vr1, vr1, %lo(sym)
add     vr2, vrx, vr1
lbu     vr3, off(vr2)

-----Transform-----

lui     vr1, %hi(sym+off+offAddi)
add     vr2, vr0, vr1, %gprel_add(sym+off+offAddi)
lbu     vr3, %gprel_lo(sym+off+offAddi)(vr2)
```

---

Patch is 87.80 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/185353.diff


25 Files Affected:

- (modified) lld/ELF/Arch/RISCV.cpp (+60-2) 
- (added) lld/test/ELF/riscv-relax-gprel-add.s (+71) 
- (added) lld/test/ELF/riscv-relax-gprel-shxadd.s (+70) 
- (modified) llvm/include/llvm/BinaryFormat/ELFRelocs/RISCV.def (+4) 
- (modified) llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp (+12-1) 
- (modified) llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp (+6) 
- (modified) llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h (+4) 
- (modified) llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFObjectWriter.cpp (+8) 
- (modified) llvm/lib/Target/RISCV/MCTargetDesc/RISCVFixupKinds.h (+9) 
- (modified) llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCAsmInfo.h (+1) 
- (modified) llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp (+91-2) 
- (modified) llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.cpp (+9) 
- (modified) llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp (+9) 
- (modified) llvm/lib/Target/RISCV/RISCVInstrFormats.td (+11) 
- (modified) llvm/lib/Target/RISCV/RISCVInstrInfo.cpp (+3) 
- (modified) llvm/lib/Target/RISCV/RISCVInstrInfo.td (+19) 
- (modified) llvm/lib/Target/RISCV/RISCVInstrInfoZb.td (+17) 
- (modified) llvm/lib/Target/RISCV/RISCVMergeBaseOffset.cpp (+260-1) 
- (added) llvm/test/CodeGen/RISCV/fold-addi-with-add-into-memops.ll (+1232) 
- (modified) llvm/test/CodeGen/RISCV/loop-strength-reduce-add-cheaper-than-mul.ll (+4-6) 
- (modified) llvm/test/CodeGen/RISCV/lpad.ll (+8-12) 
- (modified) llvm/test/MC/RISCV/Relocations/relocations.s (+48) 
- (modified) llvm/test/MC/RISCV/rv32zba-invalid.s (+5) 
- (modified) llvm/test/MC/RISCV/rv64zba-invalid.s (+9) 
- (modified) llvm/test/MC/RISCV/rv64zbb-invalid.s (+1-1) 


``````````diff
diff --git a/lld/ELF/Arch/RISCV.cpp b/lld/ELF/Arch/RISCV.cpp
index 85f49c9260565..ccaed6b675d7b 100644
--- a/lld/ELF/Arch/RISCV.cpp
+++ b/lld/ELF/Arch/RISCV.cpp
@@ -74,6 +74,10 @@ 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_GPREL_ADD 260
+#define INTERNAL_R_RISCV_GPREL_SHXADD 261
+#define INTERNAL_R_RISCV_GPREL_ADD_I 262
+#define INTERNAL_R_RISCV_GPREL_ADD_S 263
 
 const uint64_t dtpOffset = 0x800;
 
@@ -289,6 +293,10 @@ RelExpr RISCV::getRelExpr(const RelType type, const Symbol &s,
   case R_RISCV_HI20:
   case R_RISCV_LO12_I:
   case R_RISCV_LO12_S:
+  case R_RISCV_GPREL_ADD:
+  case R_RISCV_GPREL_SHXADD:
+  case R_RISCV_GPREL_LO12_I:
+  case R_RISCV_GPREL_LO12_S:
     return R_ABS;
   case R_RISCV_ADD8:
   case R_RISCV_ADD16:
@@ -499,7 +507,8 @@ 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_LO12_I: {
+  case R_RISCV_LO12_I:
+  case R_RISCV_GPREL_LO12_I: {
     uint64_t hi = (val + 0x800) >> 12;
     uint64_t lo = val - (hi << 12);
     write32le(loc, setLO12_I(read32le(loc), lo & 0xfff));
@@ -508,12 +517,40 @@ 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_LO12_S: {
+  case R_RISCV_LO12_S:
+  case R_RISCV_GPREL_LO12_S: {
     uint64_t hi = (val + 0x800) >> 12;
     uint64_t lo = val - (hi << 12);
     write32le(loc, setLO12_S(read32le(loc), lo));
     return;
   }
+  case R_RISCV_GPREL_ADD:
+  case R_RISCV_GPREL_SHXADD: {
+    write32le(loc, read32le(loc));
+    return;
+  }
+
+  case INTERNAL_R_RISCV_GPREL_ADD:
+  case INTERNAL_R_RISCV_GPREL_SHXADD: {
+    uint32_t insn = (read32le(loc) & ~(31 << 20)) | (X_GP << 20);
+    write32le(loc, insn);
+    return;
+  }
+
+  case INTERNAL_R_RISCV_GPREL_ADD_I:
+  case INTERNAL_R_RISCV_GPREL_ADD_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_GPREL_ADD_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: {
@@ -895,6 +932,19 @@ 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_GPREL_ADD:
+    sec.relaxAux->relocTypes[i] = INTERNAL_R_RISCV_GPREL_ADD;
+    break;
+  case R_RISCV_GPREL_SHXADD:
+    sec.relaxAux->relocTypes[i] = INTERNAL_R_RISCV_GPREL_SHXADD;
+    break;
+  case R_RISCV_GPREL_LO12_I:
+    sec.relaxAux->relocTypes[i] = INTERNAL_R_RISCV_GPREL_ADD_I;
+    break;
+  case R_RISCV_GPREL_LO12_S:
+    sec.relaxAux->relocTypes[i] = INTERNAL_R_RISCV_GPREL_ADD_S;
+    break;
   }
 }
 
@@ -950,6 +1000,10 @@ 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_GPREL_ADD:
+    case R_RISCV_GPREL_SHXADD:
+    case R_RISCV_GPREL_LO12_I:
+    case R_RISCV_GPREL_LO12_S:
       if (relaxable(relocs, i))
         relaxHi20Lo12(ctx, sec, i, loc, r, remove);
       break;
@@ -1193,6 +1247,10 @@ 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_GPREL_ADD:
+          case INTERNAL_R_RISCV_GPREL_SHXADD:
+          case INTERNAL_R_RISCV_GPREL_ADD_I:
+          case INTERNAL_R_RISCV_GPREL_ADD_S:
             break;
           case R_RISCV_RELAX:
             // Used by relaxTlsLe to indicate the relocation is ignored.
diff --git a/lld/test/ELF/riscv-relax-gprel-add.s b/lld/test/ELF/riscv-relax-gprel-add.s
new file mode 100644
index 0000000000000..67c74db3c2f9d
--- /dev/null
+++ b/lld/test/ELF/riscv-relax-gprel-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, %gprel_lo(array)
+  add  a0, a0, a1, %gprel_add(array)
+  lw   a0, %gprel_lo(array)(a0)
+  sw   a0, %gprel_lo(array)(a0)
+  lui  a1, %hi(array1+10)
+  addi a1, a1, %gprel_lo(array1+10)
+  add  a0, a0, a1, %gprel_add(array1+10)
+  lw   a0, %gprel_lo(array1+10)(a0)
+  sw   a0, %gprel_lo(array1+10)(a0)
+  lui  a1, %hi(norelax+10)
+  addi a1, a1, %gprel_lo(norelax+10)
+  add  a0, a0, a1, %gprel_add(norelax+10)
+  lw   a0, %gprel_lo(norelax+10)(a0)
+  sw   a0, %gprel_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-relax-gprel-shxadd.s b/lld/test/ELF/riscv-relax-gprel-shxadd.s
new file mode 100644
index 0000000000000..9dab44b6baa26
--- /dev/null
+++ b/lld/test/ELF/riscv-relax-gprel-shxadd.s
@@ -0,0 +1,70 @@
+# 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, %gprel_lo(array)
+  sh1add  a0, a0, a1, %gprel_shxadd(array)
+  lw   a0, %gprel_lo(array)(a0)
+  sw   a0, %gprel_lo(array)(a0)
+  lui  a1, %hi(array1+10)
+  addi a1, a1, %gprel_lo(array1+10)
+  sh1add  a0, a0, a1, %gprel_shxadd(array1+10)
+  lw   a0, %gprel_lo(array1+10)(a0)
+  sw   a0, %gprel_lo(array1+10)(a0)
+  lui  a1, %hi(norelax+10)
+  addi a1, a1, %gprel_lo(norelax+10)
+  sh1add  a0, a0, a1, %gprel_shxadd(norelax+10)
+  lw   a0, %gprel_lo(norelax+10)(a0)
+  sw   a0, %gprel_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..a3d8743523ca9 100644
--- a/llvm/include/llvm/BinaryFormat/ELFRelocs/RISCV.def
+++ b/llvm/include/llvm/BinaryFormat/ELFRelocs/RISCV.def
@@ -60,6 +60,10 @@ 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_GPREL_LO12_I,      66)
+ELF_RELOC(R_RISCV_GPREL_LO12_S,      67)
+ELF_RELOC(R_RISCV_GPREL_ADD,         68)
+ELF_RELOC(R_RISCV_GPREL_SHXADD,      69)
 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 443b0b5f3c04b..1468ae35cb96e 100644
--- a/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp
+++ b/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp
@@ -608,6 +608,17 @@ struct RISCVOperand final : public MCParsedAsmOperand {
            VK == ELF::R_RISCV_TPREL_ADD;
   }
 
+  bool isGPRelAddSymbol() 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_GPREL_ADD || VK == ELF::R_RISCV_GPREL_SHXADD);
+  }
+
   bool isTLSDESCCallSymbol() const {
     int64_t Imm;
     // Must be of 'immediate' type but not a constant.
@@ -869,7 +880,7 @@ 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_LO || VK == RISCV::S_PCREL_LO || VK == RISCV::S_GPREL_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/RISCVAsmBackend.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp
index 0f63b02b54c74..04852901d57b3 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp
@@ -77,6 +77,10 @@ MCFixupKindInfo RISCVAsmBackend::getFixupKindInfo(MCFixupKind Kind) const {
       {"fixup_riscv_lo12_i", 20, 12, 0},
       {"fixup_riscv_12_i", 20, 12, 0},
       {"fixup_riscv_lo12_s", 0, 32, 0},
+      {"fixup_riscv_gprel_lo12_i", 20, 12, 0},
+      {"fixup_riscv_gprel_lo12_s", 0, 32, 0},
+      {"fixup_riscv_gprel_add", 0, 0, 0},
+      {"fixup_riscv_gprel_shxadd", 0, 0, 0},
       {"fixup_riscv_pcrel_hi20", 12, 20, 0},
       {"fixup_riscv_pcrel_lo12_i", 20, 12, 0},
       {"fixup_riscv_pcrel_lo12_s", 0, 32, 0},
@@ -506,6 +510,7 @@ static uint64_t adjustFixupValue(const MCFixup &Fixup, uint64_t Value,
   case FK_Data_leb128:
     return Value;
   case RISCV::fixup_riscv_lo12_i:
+  case RISCV::fixup_riscv_gprel_lo12_i:
   case RISCV::fixup_riscv_pcrel_lo12_i:
     return Value & 0xfff;
   case RISCV::fixup_riscv_12_i:
@@ -515,6 +520,7 @@ static uint64_t adjustFixupValue(const MCFixup &Fixup, uint64_t Value,
     }
     return Value & 0xfff;
   case RISCV::fixup_riscv_lo12_s:
+  case RISCV::fixup_riscv_gprel_lo12_s:
   case RISCV::fixup_riscv_pcrel_lo12_s:
     return (((Value >> 5) & 0x7f) << 25) | ((Value & 0x1f) << 7);
   case RISCV::fixup_riscv_hi20:
diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h
index c4d54bdd60737..01b16cee5c599 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h
@@ -470,6 +470,10 @@ enum {
   MO_TLSDESC_ADD_LO = 15,
   MO_TLSDESC_CALL = 16,
 
+  MO_GPREL_LO = 17,
+  MO_GPREL_ADD = 18,
+  MO_GPREL_SHXADD = 19,
+
   // Used to differentiate between target-specific "direct" flags and "bitmask"
   // flags. A machine operand can only have one "direct" flag, but can have
   // multiple "bitmask" flags.
diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFObjectWriter.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFObjectWriter.cpp
index 2885e3cca8722..613c8b1c8ac3c 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFObjectWriter.cpp
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFObjectWriter.cpp
@@ -135,6 +135,14 @@ unsigned RISCVELFObjectWriter::getRelocType(const MCFixup &Fixup,
     return ELF::R_RISCV_LO12_I;
   case RISCV::fixup_riscv_lo12_s:
     return ELF::R_RISCV_LO12_S;
+  case RISCV::fixup_riscv_gprel_lo12_i:
+    return ELF::R_RISCV_GPREL_LO12_I;
+  case RISCV::fixup_riscv_gprel_lo12_s:
+    return ELF::R_RISCV_GPREL_LO12_S;
+  case RISCV::fixup_riscv_gprel_add:
+    return ELF::R_RISCV_GPREL_ADD;
+  case RISCV::fixup_riscv_gprel_shxadd:
+    return ELF::R_RISCV_GPREL_SHXADD;
   case RISCV::fixup_riscv_rvc_imm:
     reportError(Fixup.getLoc(), "No relocation for CI-type instructions");
     return ELF::R_RISCV_NONE;
diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVFixupKinds.h b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVFixupKinds.h
index a2b75e4a42e76..f56d54d8fb3f4 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVFixupKinds.h
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVFixupKinds.h
@@ -24,6 +24,15 @@ enum Fixups {
   fixup_riscv_12_i,
   // 12-bit fixup corresponding to %lo(foo) for the S-type store instructions
   fixup_riscv_lo12_s,
+  // 12-bit fixup corresponding to %gprel_lo(foo) for instructions like addi
+  fixup_riscv_gprel_lo12_i,
+  // 12-bit fixup corresponding to %gprel_lo(foo) for the S-type store
+  // instructions
+  fixup_riscv_gprel_lo12_s,
+  // Fixup corresponding to %gprel_add(foo) for PseudoAddGPRel/PseudoAddUWGPRel, used as a linker hint
+  fixup_riscv_gprel_add,
+  // Fixup corresponding to %gprel_shxadd(foo) for PseudoSh1AddGPRel/PseudoSh2AddGPRel/PseudoSh3AddGPRel/PseudoSh1AddUWGPRel/PseudoSh2AddUWGPRel/PseudoSh3AddUWGPRel, used as a linker hint
+  fixup_riscv_gprel_shxadd,
   // 20-bit fixup corresponding to %pcrel_hi(foo) for instructions like auipc
   fixup_riscv_pcrel_hi20,
   // 12-bit fixup corresponding to %pcrel_lo(foo) for instructions like addi
diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCAsmInfo.h b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCAsmInfo.h
index d030c3b5cf867..69823c5adc1a2 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCAsmInfo.h
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCAsmInfo.h
@@ -45,6 +45,7 @@ enum {
   S_TPREL_LO,
   S_CALL_PLT,
   S_GOT_HI,
+  S_GPREL_LO,
   // Vendor-specific relocation types might conflict across vendors.
   // Refer to them using Specifier constants.
   S_QC_ABS20,
diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp
index 4304a1e651ca5..4cead8bf751cf 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp
@@ -64,6 +64,9 @@ class RISCVMCCodeEmitter : public MCCodeEmitter {
                       SmallVectorImpl<MCFixup> &Fixups,
                       const MCSubtargetInfo &STI) const;
 
+  void expandAddGPRel(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;
@@ -260,6 +263,68 @@ void RISCVMCCodeEmitter::expandAddTPRel(const MCInst &MI,
   support::endian::write(CB, Binary, llvm::endianness::little);
 }
 
+static std::pair<unsigned, uint16_t> getAddOpAndFixups(unsigned AddOp) {
+  switch (AddOp) {
+  default:
+    llvm_unreachable("Unexpected ADD or SHXADD Opcode on GP-relative!");
+  case RISCV::PseudoAddGPRel:
+    return std::make_pair(RISCV::ADD, ELF::R_RISCV_GPREL_ADD);
+  case RISCV::PseudoAddUWGPRel:
+    return std::make_pair(RISCV::ADD_UW, ELF::R_RISCV_GPREL_ADD);
+  case RISCV::PseudoSh1AddGPRel:
+    return std::make_pair(RISCV::SH1ADD, ELF::R_RISCV_GPREL_SHXADD);
+  case RISCV::PseudoSh2AddGPRel:
+    return std::make_pair(RISCV::SH2ADD, ELF::R_RISCV_GPREL_SHXADD);
+  case RISCV::PseudoSh3AddGPRel:
+    return std::make_pair(RISCV::SH3ADD, ELF::R_RISCV_GPREL_SHXADD);
+  case RISCV::PseudoSh1AddUWGPRel:
+    return std::make_pair(RISCV::SH1ADD_UW, ELF::R_RISCV_GPREL_SHXADD);
+  case RISCV::PseudoSh2AddUWGPRel:
+    return std::make_pair(RISCV::SH2ADD_UW, ELF::R_RISCV_GPREL_SHXADD);
+  case RISCV::PseudoSh3AddUWGPRel:
+    return std::make_pair(RISCV::SH3ADD_UW, ELF::R_RISCV_GPREL_SHXADD);
+  }
+}
+
+// PseudoAddGPRel/PseudoAddUWGPRel/PseudoSh1AddGPRel/PseudoSh2AddGPRel/PseudoSh3AddGPRel/PseudoSh1AddUWGPRel/PseudoSh2AddUWGPRel/PseudoSh3AddUWGPRel to a simple ADD or SHXADD with the correct relocation.
+void RISCVMCCodeEmitter::expandAddGPRel(const MCInst &MI,
+                                        SmallVectorImpl<char> &CB,
+                                        SmallVectorImpl<MCFixup> &Fixups,
+                                        const MCSubtargetInfo &STI) const {
+  MCOperand DestReg = MI.getOperand(0);
+  // If the global array can be accessed by GP, src1 or src2 needs to be
+  // replaced with X3 reg in link time.
+  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_GPREL_ADD ||
+          Expr->getSpecifier() == ELF::R_RISCV_GPREL_SHXADD) &&
+         "Expected gprel_add or gprel_shxadd relocation on GP-relative symbol");
+
+  std::pair<unsigned, uint16_t> Res = getAddOpAndFixups(MI.getOpcode());
+
+  // Emit the correct gprel_add or gprel_shxadd relocation for the symbol.
+  addFixup(Fixups, 0, Expr, Res.second);
+
+  // Emit fixup_riscv_relax for gprel_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(Res.first).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:
@@ -440,6 +505,17 @@ void RISCVMCCodeEmitter::encodeInstruction(const MCInst &MI,
     expandAddTPRel(MI, CB, Fixups, STI);
     MCNumEmitted += 1;
     return;
+  case RISCV::PseudoAddGPRel:
+  case RISCV::PseudoAddUWGPRel:
+  case RISCV::PseudoSh1AddGPRel:
+  case RISCV::PseudoSh2AddGPRel:
+  case RISCV::PseudoSh3AddGPRel:
+  case RISCV::PseudoSh1AddUWGPRel:
+  case RISCV::PseudoSh2AddUWGPRel:
+  case RISCV::PseudoSh3AddUWGPRel:
+    expandAddGPRel(MI, CB, Fixups, STI);
+    MCNumEmitted += 1;
+    return;
   case RISCV::PseudoLongBEQ:
   case RISCV::PseudoLongBNE:
   case RISCV::PseudoLongBLT:
@@ -636,12 +712,16 @@ uint64_t RISCVMCCodeEmitter::getImmOpValue(const MCInst &MI, unsigned OpNo,
              "invalid specifier");
       break;
     case ELF::R_RISCV_TPREL_ADD:
+    case ELF::R_RISCV_GPREL_ADD:
+    case ELF::R_RISCV_GPREL_SHXADD:
       // 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 he...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/185353


More information about the llvm-commits mailing list