Skip to content

Commit

Permalink
[lld][RISCV] Implement GP relaxation for R_RISCV_HI20/R_RISCV_LO12_I/…
Browse files Browse the repository at this point in the history
…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
riscv-non-isa/riscv-elf-psabi-doc#371

Reviewed By: MaskRay

Differential Revision: https://reviews.llvm.org/D143673
  • Loading branch information
topperc committed Apr 13, 2023
1 parent b60513e commit 8544479
Show file tree
Hide file tree
Showing 10 changed files with 177 additions and 4 deletions.
54 changes: 54 additions & 0 deletions lld/ELF/Arch/RISCV.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -62,6 +67,7 @@ enum Op {

enum Reg {
X_RA = 1,
X_GP = 3,
X_TP = 4,
X_T0 = 5,
X_T1 = 6,
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions lld/ELF/Config.h
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,7 @@ struct Config {
bool printGcSections;
bool printIcfSections;
bool relax;
bool relaxGP;
bool relocatable;
bool relrGlibc = false;
bool relrPackDynRelocs = false;
Expand Down
4 changes: 4 additions & 0 deletions lld/ELF/Driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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");

Expand Down Expand Up @@ -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);

Expand Down
4 changes: 4 additions & 0 deletions lld/ELF/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -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">;
Expand Down
1 change: 1 addition & 0 deletions lld/ELF/Symbols.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
3 changes: 3 additions & 0 deletions lld/ELF/Symbols.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
18 changes: 14 additions & 4 deletions lld/ELF/Writer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
2 changes: 2 additions & 0 deletions lld/docs/ld.lld.1
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
33 changes: 33 additions & 0 deletions lld/test/ELF/riscv-relax-hi20-lo12-pie.s
Original file line number Diff line number Diff line change
@@ -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 : {}
}
61 changes: 61 additions & 0 deletions lld/test/ELF/riscv-relax-hi20-lo12.s
Original file line number Diff line number Diff line change
@@ -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 : { }
}

0 comments on commit 8544479

Please sign in to comment.