Skip to content

Commit 631769f

Browse files
authored
[LLD][RISCV] Add relaxation for absolute int12 Hi20Lo12 (#86124)
If we have an absolute address whose high bits are known to be a sign extend of the low 12 bits, we can avoid emitting the LUI entirely. This is implemented in an analogous manner to the gp relative relocations - defining an internal usage relocation type. Since 12 bits (really 11 since the high bit must be zero in user code) is less than one page, all of these offsets fit in the null page. As such, the only application of these is likely to be undefined weak symbols except for embedded use cases. I'm mostly posting this for completeness sake.
1 parent 9020783 commit 631769f

File tree

2 files changed

+57
-4
lines changed

2 files changed

+57
-4
lines changed

lld/ELF/Arch/RISCV.cpp

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,12 @@ class RISCV final : public TargetInfo {
5050

5151
} // end anonymous namespace
5252

53-
// These are internal relocation numbers for GP relaxation. They aren't part
53+
// These are internal relocation numbers for GP/X0 relaxation. They aren't part
5454
// of the psABI spec.
5555
#define INTERNAL_R_RISCV_GPREL_I 256
5656
#define INTERNAL_R_RISCV_GPREL_S 257
57+
#define INTERNAL_R_RISCV_X0REL_I 258
58+
#define INTERNAL_R_RISCV_X0REL_S 259
5759

5860
const uint64_t dtpOffset = 0x800;
5961

@@ -70,6 +72,7 @@ enum Op {
7072
};
7173

7274
enum Reg {
75+
X_X0 = 0,
7376
X_RA = 1,
7477
X_GP = 3,
7578
X_TP = 4,
@@ -447,6 +450,18 @@ void RISCV::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const {
447450
return;
448451
}
449452

453+
case INTERNAL_R_RISCV_X0REL_I:
454+
case INTERNAL_R_RISCV_X0REL_S: {
455+
checkInt(ctx, loc, val, 12, rel);
456+
uint32_t insn = (read32le(loc) & ~(31 << 15)) | (X_X0 << 15);
457+
if (rel.type == INTERNAL_R_RISCV_X0REL_I)
458+
insn = setLO12_I(insn, val);
459+
else
460+
insn = setLO12_S(insn, val);
461+
write32le(loc, insn);
462+
return;
463+
}
464+
450465
case INTERNAL_R_RISCV_GPREL_I:
451466
case INTERNAL_R_RISCV_GPREL_S: {
452467
Defined *gp = ctx.sym.riscvGlobalPointer;
@@ -772,6 +787,25 @@ static void relaxTlsLe(Ctx &ctx, const InputSection &sec, size_t i,
772787

773788
static void relaxHi20Lo12(Ctx &ctx, const InputSection &sec, size_t i,
774789
uint64_t loc, Relocation &r, uint32_t &remove) {
790+
791+
// Fold into use of x0+offset
792+
if (isInt<12>(r.sym->getVA(ctx, r.addend))) {
793+
switch (r.type) {
794+
case R_RISCV_HI20:
795+
// Remove lui rd, %hi20(x).
796+
sec.relaxAux->relocTypes[i] = R_RISCV_RELAX;
797+
remove = 4;
798+
break;
799+
case R_RISCV_LO12_I:
800+
sec.relaxAux->relocTypes[i] = INTERNAL_R_RISCV_X0REL_I;
801+
break;
802+
case R_RISCV_LO12_S:
803+
sec.relaxAux->relocTypes[i] = INTERNAL_R_RISCV_X0REL_S;
804+
break;
805+
}
806+
return;
807+
}
808+
775809
const Defined *gp = ctx.sym.riscvGlobalPointer;
776810
if (!gp)
777811
return;
@@ -974,6 +1008,8 @@ void RISCV::finalizeRelax(int passes) const {
9741008
switch (newType) {
9751009
case INTERNAL_R_RISCV_GPREL_I:
9761010
case INTERNAL_R_RISCV_GPREL_S:
1011+
case INTERNAL_R_RISCV_X0REL_I:
1012+
case INTERNAL_R_RISCV_X0REL_S:
9771013
break;
9781014
case R_RISCV_RELAX:
9791015
// Used by relaxTlsLe to indicate the relocation is ignored.

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

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@
44
# RUN: llvm-mc -filetype=obj -triple=riscv32-unknown-elf -mattr=+relax a.s -o rv32.o
55
# RUN: llvm-mc -filetype=obj -triple=riscv64-unknown-elf -mattr=+relax a.s -o rv64.o
66

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
7+
# RUN: ld.lld --relax-gp --undefined=__global_pointer$ --defsym baz=420 rv32.o lds -o rv32
8+
# RUN: ld.lld --relax-gp --undefined=__global_pointer$ --defsym baz=420 rv64.o lds -o rv64
99
# RUN: llvm-objdump -td -M no-aliases --no-show-raw-insn rv32 | FileCheck %s
1010
# RUN: llvm-objdump -td -M no-aliases --no-show-raw-insn rv64 | FileCheck %s
1111

12-
# CHECK: 00000028 l .text {{0*}}0 a
12+
# CHECK: 00000040 l .text {{0*}}0 a
1313

1414
# CHECK-NOT: lui
1515
# CHECK: addi a0, gp, -0x800
@@ -23,6 +23,14 @@
2323
# CHECK-NEXT: addi a0, a0, 0x0
2424
# CHECK-NEXT: lw a0, 0x0(a0)
2525
# CHECK-NEXT: sw a0, 0x0(a0)
26+
# CHECK-NOT: lui
27+
# CHECK: addi a0, zero, 0x0
28+
# CHECK-NEXT: lw a0, 0x0(zero)
29+
# CHECK-NEXT: sw a0, 0x0(zero)
30+
# CHECK-NOT: lui
31+
# CHECK: addi a0, zero, 0x1a4
32+
# CHECK-NEXT: lw a0, 0x1a4(zero)
33+
# CHECK-NEXT: sw a0, 0x1a4(zero)
2634
# CHECK-EMPTY:
2735
# CHECK-NEXT: <a>:
2836
# CHECK-NEXT: addi a0, a0, 0x1
@@ -42,6 +50,14 @@ _start:
4250
addi a0, a0, %lo(norelax)
4351
lw a0, %lo(norelax)(a0)
4452
sw a0, %lo(norelax)(a0)
53+
lui a0, %hi(undefined_weak)
54+
addi a0, a0, %lo(undefined_weak)
55+
lw a0, %lo(undefined_weak)(a0)
56+
sw a0, %lo(undefined_weak)(a0)
57+
lui a0, %hi(baz)
58+
addi a0, a0, %lo(baz)
59+
lw a0, %lo(baz)(a0)
60+
sw a0, %lo(baz)(a0)
4561
a:
4662
addi a0, a0, 1
4763

@@ -53,6 +69,7 @@ bar:
5369
.byte 0
5470
norelax:
5571
.word 0
72+
.weak undefined_weak
5673

5774
#--- lds
5875
SECTIONS {

0 commit comments

Comments
 (0)