Skip to content

Commit 9f64b25

Browse files
committed
[LLD][RISCV] Add relaxation for absolute int12 Hi20Lo12
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 0081ec1 commit 9f64b25

File tree

2 files changed

+59
-4
lines changed

2 files changed

+59
-4
lines changed

lld/ELF/Arch/RISCV.cpp

Lines changed: 39 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,
@@ -464,6 +467,20 @@ void RISCV::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const {
464467
return;
465468
}
466469

470+
case INTERNAL_R_RISCV_X0REL_I:
471+
case INTERNAL_R_RISCV_X0REL_S: {
472+
assert(isInt<12>(val));
473+
uint64_t hi = (val + 0x800) >> 12;
474+
uint64_t lo = val - (hi << 12);
475+
uint32_t insn = (read32le(loc) & ~(31 << 15)) | (X_X0 << 15);
476+
if (rel.type == INTERNAL_R_RISCV_X0REL_I)
477+
insn = setLO12_I(insn, lo);
478+
else
479+
insn = setLO12_S(insn, lo);
480+
write32le(loc, insn);
481+
return;
482+
}
483+
467484
case INTERNAL_R_RISCV_GPREL_I:
468485
case INTERNAL_R_RISCV_GPREL_S: {
469486
Defined *gp = ElfSym::riscvGlobalPointer;
@@ -789,6 +806,25 @@ static void relaxTlsLe(const InputSection &sec, size_t i, uint64_t loc,
789806

790807
static void relaxHi20Lo12(const InputSection &sec, size_t i, uint64_t loc,
791808
Relocation &r, uint32_t &remove) {
809+
810+
// Fold into use of x0+offset
811+
if (isInt<12>(r.sym->getVA(r.addend))) {
812+
switch (r.type) {
813+
case R_RISCV_HI20:
814+
// Remove lui rd, %hi20(x).
815+
sec.relaxAux->relocTypes[i] = R_RISCV_RELAX;
816+
remove = 4;
817+
break;
818+
case R_RISCV_LO12_I:
819+
sec.relaxAux->relocTypes[i] = INTERNAL_R_RISCV_X0REL_I;
820+
break;
821+
case R_RISCV_LO12_S:
822+
sec.relaxAux->relocTypes[i] = INTERNAL_R_RISCV_X0REL_S;
823+
break;
824+
}
825+
return;
826+
}
827+
792828
const Defined *gp = ElfSym::riscvGlobalPointer;
793829
if (!gp)
794830
return;
@@ -989,6 +1025,8 @@ void RISCV::finalizeRelax(int passes) const {
9891025
switch (newType) {
9901026
case INTERNAL_R_RISCV_GPREL_I:
9911027
case INTERNAL_R_RISCV_GPREL_S:
1028+
case INTERNAL_R_RISCV_X0REL_I:
1029+
case INTERNAL_R_RISCV_X0REL_S:
9921030
break;
9931031
case R_RISCV_RELAX:
9941032
// 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)