[lld] 8544479 - [lld][RISCV] Implement GP relaxation for R_RISCV_HI20/R_RISCV_LO12_I/R_RISCV_LO12_S.

Craig Topper via llvm-commits llvm-commits at lists.llvm.org
Thu Apr 13 10:52:42 PDT 2023


Author: Craig Topper
Date: 2023-04-13T10:52:15-07:00
New Revision: 85444794cdde8211b916767cc51a37547dbd17e9

URL: https://github.com/llvm/llvm-project/commit/85444794cdde8211b916767cc51a37547dbd17e9
DIFF: https://github.com/llvm/llvm-project/commit/85444794cdde8211b916767cc51a37547dbd17e9.diff

LOG: [lld][RISCV] Implement GP relaxation for R_RISCV_HI20/R_RISCV_LO12_I/R_RISCV_LO12_S.

This implements support for relaxing these relocations to use the GP
register to compute addresses of globals in the .sdata and .sbss
sections.

This feature is off by default and must be enabled by passing
--relax-gp to the linker.

The GP register might not always be the "global pointer". It can
be used for other purposes. See discussion here
https://github.com/riscv-non-isa/riscv-elf-psabi-doc/pull/371

Reviewed By: MaskRay

Differential Revision: https://reviews.llvm.org/D143673

Added: 
    lld/test/ELF/riscv-relax-hi20-lo12-pie.s
    lld/test/ELF/riscv-relax-hi20-lo12.s

Modified: 
    lld/ELF/Arch/RISCV.cpp
    lld/ELF/Config.h
    lld/ELF/Driver.cpp
    lld/ELF/Options.td
    lld/ELF/Symbols.cpp
    lld/ELF/Symbols.h
    lld/ELF/Writer.cpp
    lld/docs/ld.lld.1

Removed: 
    


################################################################################
diff  --git a/lld/ELF/Arch/RISCV.cpp b/lld/ELF/Arch/RISCV.cpp
index 8e556b905b1c2..26977015dab3a 100644
--- a/lld/ELF/Arch/RISCV.cpp
+++ b/lld/ELF/Arch/RISCV.cpp
@@ -48,6 +48,11 @@ class RISCV final : public TargetInfo {
 
 } // end anonymous namespace
 
+// These are internal relocation numbers for GP relaxation. They aren't part
+// of the psABI spec.
+#define INTERNAL_R_RISCV_GPREL_I 256
+#define INTERNAL_R_RISCV_GPREL_S 257
+
 const uint64_t dtpOffset = 0x800;
 
 enum Op {
@@ -62,6 +67,7 @@ enum Op {
 
 enum Reg {
   X_RA = 1,
+  X_GP = 3,
   X_TP = 4,
   X_T0 = 5,
   X_T1 = 6,
@@ -436,6 +442,20 @@ void RISCV::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const {
     return;
   }
 
+  case INTERNAL_R_RISCV_GPREL_I:
+  case INTERNAL_R_RISCV_GPREL_S: {
+    Defined *gp = ElfSym::riscvGlobalPointer;
+    int64_t displace = SignExtend64(val - gp->getVA(), bits);
+    checkInt(loc, displace, 12, rel);
+    uint32_t insn = (read32le(loc) & ~(31 << 15)) | (X_GP << 15);
+    if (rel.type == INTERNAL_R_RISCV_GPREL_I)
+      insn = setLO12_I(insn, displace);
+    else
+      insn = setLO12_S(insn, displace);
+    write32le(loc, insn);
+    return;
+  }
+
   case R_RISCV_ADD8:
     *loc += val;
     return;
@@ -614,6 +634,30 @@ static void relaxTlsLe(const InputSection &sec, size_t i, uint64_t loc,
   }
 }
 
+static void relaxHi20Lo12(const InputSection &sec, size_t i, uint64_t loc,
+                          Relocation &r, uint32_t &remove) {
+  const Defined *gp = ElfSym::riscvGlobalPointer;
+  if (!gp)
+    return;
+
+  if (!isInt<12>(r.sym->getVA(r.addend) - gp->getVA()))
+    return;
+
+  switch (r.type) {
+  case R_RISCV_HI20:
+    // Remove lui rd, %hi20(x).
+    sec.relaxAux->relocTypes[i] = R_RISCV_RELAX;
+    remove = 4;
+    break;
+  case R_RISCV_LO12_I:
+    sec.relaxAux->relocTypes[i] = INTERNAL_R_RISCV_GPREL_I;
+    break;
+  case R_RISCV_LO12_S:
+    sec.relaxAux->relocTypes[i] = INTERNAL_R_RISCV_GPREL_S;
+    break;
+  }
+}
+
 static bool relax(InputSection &sec) {
   const uint64_t secAddr = sec.getVA();
   auto &aux = *sec.relaxAux;
@@ -665,6 +709,13 @@ static bool relax(InputSection &sec) {
           sec.relocs()[i + 1].type == R_RISCV_RELAX)
         relaxTlsLe(sec, i, loc, r, remove);
       break;
+    case R_RISCV_HI20:
+    case R_RISCV_LO12_I:
+    case R_RISCV_LO12_S:
+      if (i + 1 != sec.relocs().size() &&
+          sec.relocs()[i + 1].type == R_RISCV_RELAX)
+        relaxHi20Lo12(sec, i, loc, r, remove);
+      break;
     }
 
     // For all anchors whose offsets are <= r.offset, they are preceded by
@@ -778,6 +829,9 @@ void elf::riscvFinalizeRelax(int passes) {
           }
         } else if (RelType newType = aux.relocTypes[i]) {
           switch (newType) {
+          case INTERNAL_R_RISCV_GPREL_I:
+          case INTERNAL_R_RISCV_GPREL_S:
+            break;
           case R_RISCV_RELAX:
             // Used by relaxTlsLe to indicate the relocation is ignored.
             break;

diff  --git a/lld/ELF/Config.h b/lld/ELF/Config.h
index a6e87511bcfb6..c446508dfa2f0 100644
--- a/lld/ELF/Config.h
+++ b/lld/ELF/Config.h
@@ -253,6 +253,7 @@ struct Config {
   bool printGcSections;
   bool printIcfSections;
   bool relax;
+  bool relaxGP;
   bool relocatable;
   bool relrGlibc = false;
   bool relrPackDynRelocs = false;

diff  --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp
index 5099f1cbe96a6..5ba5eab233550 100644
--- a/lld/ELF/Driver.cpp
+++ b/lld/ELF/Driver.cpp
@@ -357,6 +357,9 @@ static void checkOptions() {
   if (config->pcRelOptimize && config->emachine != EM_PPC64)
     error("--pcrel-optimize is only supported on PowerPC64 targets");
 
+  if (config->relaxGP && config->emachine != EM_RISCV)
+    error("--relax-gp is only supported on RISC-V targets");
+
   if (config->pie && config->shared)
     error("-shared and -pie may not be used together");
 
@@ -1217,6 +1220,7 @@ static void readConfigs(opt::InputArgList &args) {
   config->printSymbolOrder =
       args.getLastArgValue(OPT_print_symbol_order);
   config->relax = args.hasFlag(OPT_relax, OPT_no_relax, true);
+  config->relaxGP = args.hasFlag(OPT_relax_gp, OPT_no_relax_gp, false);
   config->rpath = getRpath(args);
   config->relocatable = args.hasArg(OPT_relocatable);
 

diff  --git a/lld/ELF/Options.td b/lld/ELF/Options.td
index 87d84246ac45d..5b3d9f6c40dee 100644
--- a/lld/ELF/Options.td
+++ b/lld/ELF/Options.td
@@ -355,6 +355,10 @@ defm relax: BB<"relax",
   "Enable target-specific relaxations if supported (default)",
   "Disable target-specific relaxations">;
 
+defm relax_gp: BB<"relax-gp",
+  "Enable global pointer relaxation",
+  "Disable global pointer relaxation (default)">;
+
 defm reproduce:
   EEq<"reproduce",
      "Write tar file containing inputs and command to reproduce link">;

diff  --git a/lld/ELF/Symbols.cpp b/lld/ELF/Symbols.cpp
index c2375e07f248c..62a8a3c30664e 100644
--- a/lld/ELF/Symbols.cpp
+++ b/lld/ELF/Symbols.cpp
@@ -71,6 +71,7 @@ Defined *ElfSym::globalOffsetTable;
 Defined *ElfSym::mipsGp;
 Defined *ElfSym::mipsGpDisp;
 Defined *ElfSym::mipsLocalGp;
+Defined *ElfSym::riscvGlobalPointer;
 Defined *ElfSym::relaIpltStart;
 Defined *ElfSym::relaIpltEnd;
 Defined *ElfSym::tlsModuleBase;

diff  --git a/lld/ELF/Symbols.h b/lld/ELF/Symbols.h
index 5e3d52d4e5530..e0b74faafeca6 100644
--- a/lld/ELF/Symbols.h
+++ b/lld/ELF/Symbols.h
@@ -512,6 +512,9 @@ struct ElfSym {
   static Defined *mipsGpDisp;
   static Defined *mipsLocalGp;
 
+  // __global_pointer$ for RISC-V.
+  static Defined *riscvGlobalPointer;
+
   // __rel{,a}_iplt_{start,end} symbols.
   static Defined *relaIpltStart;
   static Defined *relaIpltEnd;

diff  --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp
index 534794bd835a3..f6b62b778b362 100644
--- a/lld/ELF/Writer.cpp
+++ b/lld/ELF/Writer.cpp
@@ -1872,10 +1872,20 @@ template <class ELFT> void Writer<ELFT>::finalizeSections() {
     // should only be defined in an executable. If .sdata does not exist, its
     // value/section does not matter but it has to be relative, so set its
     // st_shndx arbitrarily to 1 (Out::elfHeader).
-    if (config->emachine == EM_RISCV && !config->shared) {
-      OutputSection *sec = findSection(".sdata");
-      addOptionalRegular("__global_pointer$", sec ? sec : Out::elfHeader, 0x800,
-                         STV_DEFAULT);
+    if (config->emachine == EM_RISCV) {
+      ElfSym::riscvGlobalPointer = nullptr;
+      if (!config->shared) {
+        OutputSection *sec = findSection(".sdata");
+        addOptionalRegular(
+            "__global_pointer$", sec ? sec : Out::elfHeader, 0x800, STV_DEFAULT);
+        // Set riscvGlobalPointer to be used by the optional global pointer
+        // relaxation.
+        if (config->relaxGP) {
+          Symbol *s = symtab.find("__global_pointer$");
+          if (s && s->isDefined())
+            ElfSym::riscvGlobalPointer = cast<Defined>(s);
+        }
+      }
     }
 
     if (config->emachine == EM_386 || config->emachine == EM_X86_64) {

diff  --git a/lld/docs/ld.lld.1 b/lld/docs/ld.lld.1
index 09b82995ead41..d97cd26a6378b 100644
--- a/lld/docs/ld.lld.1
+++ b/lld/docs/ld.lld.1
@@ -493,6 +493,8 @@ and
 .It Fl -pop-state
 Restore the states saved by
 .Fl -push-state.
+.It Fl --relax-gp
+Enable global pointer relaxation for RISC-V.
 .It Fl -relocatable , Fl r
 Create relocatable object file.
 .It Fl -reproduce Ns = Ns Ar path

diff  --git a/lld/test/ELF/riscv-relax-hi20-lo12-pie.s b/lld/test/ELF/riscv-relax-hi20-lo12-pie.s
new file mode 100644
index 0000000000000..87e4f8f000e59
--- /dev/null
+++ b/lld/test/ELF/riscv-relax-hi20-lo12-pie.s
@@ -0,0 +1,33 @@
+# 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: llvm-mc -filetype=obj -triple=riscv64-unknown-elf -mattr=+relax a.s -o rv64-pie.o
+
+# RUN: ld.lld --relax-gp --undefined=__global_pointer$ rv32.o lds -pie -o rv32
+# RUN: ld.lld --relax-gp --undefined=__global_pointer$ rv64.o lds -shared -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:      lui     a0, 512
+# CHECK-NEXT: addi    a0, a0, 1
+# CHECK-NEXT: lw      a0, 1(a0)
+# CHECK-NEXT: sw      a0, 1(a0)
+
+#--- a.s
+.globl abs
+abs = 0x200001
+
+.global _start
+_start:
+  lui a0, %hi(abs)
+  addi a0, a0, %lo(abs)
+  lw a0, %lo(abs)(a0)
+  sw a0, %lo(abs)(a0)
+
+#--- lds
+SECTIONS {
+  .text : {*(.text) }
+  .sdata 0x200000 : {}
+}

diff  --git a/lld/test/ELF/riscv-relax-hi20-lo12.s b/lld/test/ELF/riscv-relax-hi20-lo12.s
new file mode 100644
index 0000000000000..feef08767c19b
--- /dev/null
+++ b/lld/test/ELF/riscv-relax-hi20-lo12.s
@@ -0,0 +1,61 @@
+# 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: 00000028 l       .text {{0*}}0 a
+
+# CHECK-NOT:  lui
+# CHECK:      addi    a0, gp, -2048
+# CHECK-NEXT: lw      a0, -2048(gp)
+# CHECK-NEXT: sw      a0, -2048(gp)
+# CHECK-NOT:  lui
+# CHECK-NEXT: addi    a0, gp, 2047
+# CHECK-NEXT: lb      a0, 2047(gp)
+# CHECK-NEXT: sb      a0, 2047(gp)
+# CHECK-NEXT: lui     a0, 513
+# CHECK-NEXT: addi    a0, a0, 0
+# CHECK-NEXT: lw      a0, 0(a0)
+# CHECK-NEXT: sw      a0, 0(a0)
+# CHECK-EMPTY:
+# CHECK-NEXT: <a>:
+# CHECK-NEXT: addi a0, a0, 1
+
+#--- a.s
+.global _start
+_start:
+  lui a0, %hi(foo)
+  addi a0, a0, %lo(foo)
+  lw a0, %lo(foo)(a0)
+  sw a0, %lo(foo)(a0)
+  lui a0, %hi(bar)
+  addi a0, a0, %lo(bar)
+  lb a0, %lo(bar)(a0)
+  sb a0, %lo(bar)(a0)
+  lui a0, %hi(norelax)
+  addi a0, a0, %lo(norelax)
+  lw a0, %lo(norelax)(a0)
+  sw a0, %lo(norelax)(a0)
+a:
+  addi a0, a0, 1
+
+.section .sdata,"aw"
+foo:
+  .word 0
+  .space 4091
+bar:
+  .byte 0
+norelax:
+  .word 0
+
+#--- lds
+SECTIONS {
+  .text : {*(.text) }
+  .sdata 0x200000 : { }
+}


        


More information about the llvm-commits mailing list