Skip to content

Commit 8544479

Browse files
committed
[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 riscv-non-isa/riscv-elf-psabi-doc#371 Reviewed By: MaskRay Differential Revision: https://reviews.llvm.org/D143673
1 parent b60513e commit 8544479

10 files changed

+177
-4
lines changed

lld/ELF/Arch/RISCV.cpp

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,11 @@ class RISCV final : public TargetInfo {
4848

4949
} // end anonymous namespace
5050

51+
// These are internal relocation numbers for GP relaxation. They aren't part
52+
// of the psABI spec.
53+
#define INTERNAL_R_RISCV_GPREL_I 256
54+
#define INTERNAL_R_RISCV_GPREL_S 257
55+
5156
const uint64_t dtpOffset = 0x800;
5257

5358
enum Op {
@@ -62,6 +67,7 @@ enum Op {
6267

6368
enum Reg {
6469
X_RA = 1,
70+
X_GP = 3,
6571
X_TP = 4,
6672
X_T0 = 5,
6773
X_T1 = 6,
@@ -436,6 +442,20 @@ void RISCV::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const {
436442
return;
437443
}
438444

445+
case INTERNAL_R_RISCV_GPREL_I:
446+
case INTERNAL_R_RISCV_GPREL_S: {
447+
Defined *gp = ElfSym::riscvGlobalPointer;
448+
int64_t displace = SignExtend64(val - gp->getVA(), bits);
449+
checkInt(loc, displace, 12, rel);
450+
uint32_t insn = (read32le(loc) & ~(31 << 15)) | (X_GP << 15);
451+
if (rel.type == INTERNAL_R_RISCV_GPREL_I)
452+
insn = setLO12_I(insn, displace);
453+
else
454+
insn = setLO12_S(insn, displace);
455+
write32le(loc, insn);
456+
return;
457+
}
458+
439459
case R_RISCV_ADD8:
440460
*loc += val;
441461
return;
@@ -614,6 +634,30 @@ static void relaxTlsLe(const InputSection &sec, size_t i, uint64_t loc,
614634
}
615635
}
616636

637+
static void relaxHi20Lo12(const InputSection &sec, size_t i, uint64_t loc,
638+
Relocation &r, uint32_t &remove) {
639+
const Defined *gp = ElfSym::riscvGlobalPointer;
640+
if (!gp)
641+
return;
642+
643+
if (!isInt<12>(r.sym->getVA(r.addend) - gp->getVA()))
644+
return;
645+
646+
switch (r.type) {
647+
case R_RISCV_HI20:
648+
// Remove lui rd, %hi20(x).
649+
sec.relaxAux->relocTypes[i] = R_RISCV_RELAX;
650+
remove = 4;
651+
break;
652+
case R_RISCV_LO12_I:
653+
sec.relaxAux->relocTypes[i] = INTERNAL_R_RISCV_GPREL_I;
654+
break;
655+
case R_RISCV_LO12_S:
656+
sec.relaxAux->relocTypes[i] = INTERNAL_R_RISCV_GPREL_S;
657+
break;
658+
}
659+
}
660+
617661
static bool relax(InputSection &sec) {
618662
const uint64_t secAddr = sec.getVA();
619663
auto &aux = *sec.relaxAux;
@@ -665,6 +709,13 @@ static bool relax(InputSection &sec) {
665709
sec.relocs()[i + 1].type == R_RISCV_RELAX)
666710
relaxTlsLe(sec, i, loc, r, remove);
667711
break;
712+
case R_RISCV_HI20:
713+
case R_RISCV_LO12_I:
714+
case R_RISCV_LO12_S:
715+
if (i + 1 != sec.relocs().size() &&
716+
sec.relocs()[i + 1].type == R_RISCV_RELAX)
717+
relaxHi20Lo12(sec, i, loc, r, remove);
718+
break;
668719
}
669720

670721
// For all anchors whose offsets are <= r.offset, they are preceded by
@@ -778,6 +829,9 @@ void elf::riscvFinalizeRelax(int passes) {
778829
}
779830
} else if (RelType newType = aux.relocTypes[i]) {
780831
switch (newType) {
832+
case INTERNAL_R_RISCV_GPREL_I:
833+
case INTERNAL_R_RISCV_GPREL_S:
834+
break;
781835
case R_RISCV_RELAX:
782836
// Used by relaxTlsLe to indicate the relocation is ignored.
783837
break;

lld/ELF/Config.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,7 @@ struct Config {
253253
bool printGcSections;
254254
bool printIcfSections;
255255
bool relax;
256+
bool relaxGP;
256257
bool relocatable;
257258
bool relrGlibc = false;
258259
bool relrPackDynRelocs = false;

lld/ELF/Driver.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,9 @@ static void checkOptions() {
357357
if (config->pcRelOptimize && config->emachine != EM_PPC64)
358358
error("--pcrel-optimize is only supported on PowerPC64 targets");
359359

360+
if (config->relaxGP && config->emachine != EM_RISCV)
361+
error("--relax-gp is only supported on RISC-V targets");
362+
360363
if (config->pie && config->shared)
361364
error("-shared and -pie may not be used together");
362365

@@ -1217,6 +1220,7 @@ static void readConfigs(opt::InputArgList &args) {
12171220
config->printSymbolOrder =
12181221
args.getLastArgValue(OPT_print_symbol_order);
12191222
config->relax = args.hasFlag(OPT_relax, OPT_no_relax, true);
1223+
config->relaxGP = args.hasFlag(OPT_relax_gp, OPT_no_relax_gp, false);
12201224
config->rpath = getRpath(args);
12211225
config->relocatable = args.hasArg(OPT_relocatable);
12221226

lld/ELF/Options.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,10 @@ defm relax: BB<"relax",
355355
"Enable target-specific relaxations if supported (default)",
356356
"Disable target-specific relaxations">;
357357

358+
defm relax_gp: BB<"relax-gp",
359+
"Enable global pointer relaxation",
360+
"Disable global pointer relaxation (default)">;
361+
358362
defm reproduce:
359363
EEq<"reproduce",
360364
"Write tar file containing inputs and command to reproduce link">;

lld/ELF/Symbols.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ Defined *ElfSym::globalOffsetTable;
7171
Defined *ElfSym::mipsGp;
7272
Defined *ElfSym::mipsGpDisp;
7373
Defined *ElfSym::mipsLocalGp;
74+
Defined *ElfSym::riscvGlobalPointer;
7475
Defined *ElfSym::relaIpltStart;
7576
Defined *ElfSym::relaIpltEnd;
7677
Defined *ElfSym::tlsModuleBase;

lld/ELF/Symbols.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -512,6 +512,9 @@ struct ElfSym {
512512
static Defined *mipsGpDisp;
513513
static Defined *mipsLocalGp;
514514

515+
// __global_pointer$ for RISC-V.
516+
static Defined *riscvGlobalPointer;
517+
515518
// __rel{,a}_iplt_{start,end} symbols.
516519
static Defined *relaIpltStart;
517520
static Defined *relaIpltEnd;

lld/ELF/Writer.cpp

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1872,10 +1872,20 @@ template <class ELFT> void Writer<ELFT>::finalizeSections() {
18721872
// should only be defined in an executable. If .sdata does not exist, its
18731873
// value/section does not matter but it has to be relative, so set its
18741874
// st_shndx arbitrarily to 1 (Out::elfHeader).
1875-
if (config->emachine == EM_RISCV && !config->shared) {
1876-
OutputSection *sec = findSection(".sdata");
1877-
addOptionalRegular("__global_pointer$", sec ? sec : Out::elfHeader, 0x800,
1878-
STV_DEFAULT);
1875+
if (config->emachine == EM_RISCV) {
1876+
ElfSym::riscvGlobalPointer = nullptr;
1877+
if (!config->shared) {
1878+
OutputSection *sec = findSection(".sdata");
1879+
addOptionalRegular(
1880+
"__global_pointer$", sec ? sec : Out::elfHeader, 0x800, STV_DEFAULT);
1881+
// Set riscvGlobalPointer to be used by the optional global pointer
1882+
// relaxation.
1883+
if (config->relaxGP) {
1884+
Symbol *s = symtab.find("__global_pointer$");
1885+
if (s && s->isDefined())
1886+
ElfSym::riscvGlobalPointer = cast<Defined>(s);
1887+
}
1888+
}
18791889
}
18801890

18811891
if (config->emachine == EM_386 || config->emachine == EM_X86_64) {

lld/docs/ld.lld.1

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -493,6 +493,8 @@ and
493493
.It Fl -pop-state
494494
Restore the states saved by
495495
.Fl -push-state.
496+
.It Fl --relax-gp
497+
Enable global pointer relaxation for RISC-V.
496498
.It Fl -relocatable , Fl r
497499
Create relocatable object file.
498500
.It Fl -reproduce Ns = Ns Ar path
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# REQUIRES: riscv
2+
# RUN: rm -rf %t && split-file %s %t && cd %t
3+
4+
# RUN: llvm-mc -filetype=obj -triple=riscv32-unknown-elf -mattr=+relax a.s -o rv32.o
5+
# RUN: llvm-mc -filetype=obj -triple=riscv64-unknown-elf -mattr=+relax a.s -o rv64.o
6+
# RUN: llvm-mc -filetype=obj -triple=riscv64-unknown-elf -mattr=+relax a.s -o rv64-pie.o
7+
8+
# RUN: ld.lld --relax-gp --undefined=__global_pointer$ rv32.o lds -pie -o rv32
9+
# RUN: ld.lld --relax-gp --undefined=__global_pointer$ rv64.o lds -shared -o rv64
10+
# RUN: llvm-objdump -td -M no-aliases --no-show-raw-insn rv32 | FileCheck %s
11+
# RUN: llvm-objdump -td -M no-aliases --no-show-raw-insn rv64 | FileCheck %s
12+
13+
# CHECK: lui a0, 512
14+
# CHECK-NEXT: addi a0, a0, 1
15+
# CHECK-NEXT: lw a0, 1(a0)
16+
# CHECK-NEXT: sw a0, 1(a0)
17+
18+
#--- a.s
19+
.globl abs
20+
abs = 0x200001
21+
22+
.global _start
23+
_start:
24+
lui a0, %hi(abs)
25+
addi a0, a0, %lo(abs)
26+
lw a0, %lo(abs)(a0)
27+
sw a0, %lo(abs)(a0)
28+
29+
#--- lds
30+
SECTIONS {
31+
.text : {*(.text) }
32+
.sdata 0x200000 : {}
33+
}

lld/test/ELF/riscv-relax-hi20-lo12.s

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# REQUIRES: riscv
2+
# RUN: rm -rf %t && split-file %s %t && cd %t
3+
4+
# RUN: llvm-mc -filetype=obj -triple=riscv32-unknown-elf -mattr=+relax a.s -o rv32.o
5+
# RUN: llvm-mc -filetype=obj -triple=riscv64-unknown-elf -mattr=+relax a.s -o rv64.o
6+
7+
# RUN: ld.lld --relax-gp --undefined=__global_pointer$ rv32.o lds -o rv32
8+
# RUN: ld.lld --relax-gp --undefined=__global_pointer$ rv64.o lds -o rv64
9+
# RUN: llvm-objdump -td -M no-aliases --no-show-raw-insn rv32 | FileCheck %s
10+
# RUN: llvm-objdump -td -M no-aliases --no-show-raw-insn rv64 | FileCheck %s
11+
12+
# CHECK: 00000028 l .text {{0*}}0 a
13+
14+
# CHECK-NOT: lui
15+
# CHECK: addi a0, gp, -2048
16+
# CHECK-NEXT: lw a0, -2048(gp)
17+
# CHECK-NEXT: sw a0, -2048(gp)
18+
# CHECK-NOT: lui
19+
# CHECK-NEXT: addi a0, gp, 2047
20+
# CHECK-NEXT: lb a0, 2047(gp)
21+
# CHECK-NEXT: sb a0, 2047(gp)
22+
# CHECK-NEXT: lui a0, 513
23+
# CHECK-NEXT: addi a0, a0, 0
24+
# CHECK-NEXT: lw a0, 0(a0)
25+
# CHECK-NEXT: sw a0, 0(a0)
26+
# CHECK-EMPTY:
27+
# CHECK-NEXT: <a>:
28+
# CHECK-NEXT: addi a0, a0, 1
29+
30+
#--- a.s
31+
.global _start
32+
_start:
33+
lui a0, %hi(foo)
34+
addi a0, a0, %lo(foo)
35+
lw a0, %lo(foo)(a0)
36+
sw a0, %lo(foo)(a0)
37+
lui a0, %hi(bar)
38+
addi a0, a0, %lo(bar)
39+
lb a0, %lo(bar)(a0)
40+
sb a0, %lo(bar)(a0)
41+
lui a0, %hi(norelax)
42+
addi a0, a0, %lo(norelax)
43+
lw a0, %lo(norelax)(a0)
44+
sw a0, %lo(norelax)(a0)
45+
a:
46+
addi a0, a0, 1
47+
48+
.section .sdata,"aw"
49+
foo:
50+
.word 0
51+
.space 4091
52+
bar:
53+
.byte 0
54+
norelax:
55+
.word 0
56+
57+
#--- lds
58+
SECTIONS {
59+
.text : {*(.text) }
60+
.sdata 0x200000 : { }
61+
}

0 commit comments

Comments
 (0)