diff --git a/lld/ELF/Arch/RISCV.cpp b/lld/ELF/Arch/RISCV.cpp --- a/lld/ELF/Arch/RISCV.cpp +++ b/lld/ELF/Arch/RISCV.cpp @@ -62,6 +62,7 @@ enum Reg { X_RA = 1, + X_GP = 3, X_TP = 4, X_T0 = 5, X_T1 = 6, @@ -436,6 +437,21 @@ return; } + case R_RISCV_GPREL_I: + case R_RISCV_GPREL_S: { + Defined *gp = ElfSym::riscvGlobalPointer; + int64_t displace = val - gp->getVA(); + checkInt(loc, displace, 12, rel); + uint32_t insn = read32le(loc); + insn = (insn & ~(31 << 15)) | (X_GP << 15); + if (rel.type == 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 +630,30 @@ } } +static void relaxHi20Lo12(const InputSection &sec, size_t i, uint64_t loc, + Relocation &r, uint32_t &remove) { + 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: + // delete unnecessary instruction + sec.relaxAux->relocTypes[i] = R_RISCV_RELAX; + remove = 4; + break; + case R_RISCV_LO12_I: + sec.relaxAux->relocTypes[i] = R_RISCV_GPREL_I; + break; + case R_RISCV_LO12_S: + sec.relaxAux->relocTypes[i] = R_RISCV_GPREL_S; + break; + } +} + static bool relax(InputSection &sec) { const uint64_t secAddr = sec.getVA(); auto &aux = *sec.relaxAux; @@ -665,6 +705,13 @@ 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 +825,9 @@ } } else if (RelType newType = aux.relocTypes[i]) { switch (newType) { + case R_RISCV_GPREL_I: + case 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 --- a/lld/ELF/Config.h +++ b/lld/ELF/Config.h @@ -244,6 +244,7 @@ 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 --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -352,6 +352,9 @@ 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"); @@ -1204,6 +1207,7 @@ 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 --- a/lld/ELF/Options.td +++ b/lld/ELF/Options.td @@ -355,6 +355,10 @@ "Enable target-specific relaxations if supported (default)", "Disable target-specific relaxations">; +defm relax_gp: BB<"relax-gp", + "Enable GP relaxation", + "Disable GP relaxation (default)">; + defm reproduce: EEq<"reproduce", "Write tar file containing inputs and command to reproduce link">; diff --git a/lld/ELF/Symbols.h b/lld/ELF/Symbols.h --- a/lld/ELF/Symbols.h +++ b/lld/ELF/Symbols.h @@ -512,6 +512,9 @@ 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/Symbols.cpp b/lld/ELF/Symbols.cpp --- a/lld/ELF/Symbols.cpp +++ b/lld/ELF/Symbols.cpp @@ -71,6 +71,7 @@ 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/Writer.cpp b/lld/ELF/Writer.cpp --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -1858,8 +1858,11 @@ // 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); + Defined *globalPointer = addOptionalRegular( + "__global_pointer$", sec ? sec : Out::elfHeader, 0x800, STV_DEFAULT); + // Save the globalPointer for use by GP relaxation if enabled. + if (config->relaxGP) + ElfSym::riscvGlobalPointer = globalPointer; } if (config->emachine == EM_386 || config->emachine == EM_X86_64) { diff --git a/lld/docs/ld.lld.1 b/lld/docs/ld.lld.1 --- a/lld/docs/ld.lld.1 +++ b/lld/docs/ld.lld.1 @@ -493,6 +493,8 @@ .It Fl -pop-state Restore the states saved by .Fl -push-state. +.It Fl --relax-gp +Enable GP 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.s b/lld/test/ELF/riscv-relax-hi20-lo12.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/riscv-relax-hi20-lo12.s @@ -0,0 +1,53 @@ +# 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 -d -M no-aliases --no-show-raw-insn rv32 | FileCheck %s +# RUN: llvm-objdump -d -M no-aliases --no-show-raw-insn rv64 | FileCheck %s +# 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) + +#--- 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) + +.section .sdata,"aw" +foo: + .word 0 + .space 4091 +bar: + .byte 0 +norelax: + .word 0 + +#--- lds +SECTIONS { + .text : {*(.text) } + .sdata 0x200000 : { foo = .; bar = . + 4096; } +} diff --git a/llvm/include/llvm/BinaryFormat/ELFRelocs/RISCV.def b/llvm/include/llvm/BinaryFormat/ELFRelocs/RISCV.def --- a/llvm/include/llvm/BinaryFormat/ELFRelocs/RISCV.def +++ b/llvm/include/llvm/BinaryFormat/ELFRelocs/RISCV.def @@ -46,6 +46,8 @@ ELF_RELOC(R_RISCV_RVC_BRANCH, 44) ELF_RELOC(R_RISCV_RVC_JUMP, 45) ELF_RELOC(R_RISCV_RVC_LUI, 46) +ELF_RELOC(R_RISCV_GPREL_I, 47) +ELF_RELOC(R_RISCV_GPREL_S, 48) ELF_RELOC(R_RISCV_RELAX, 51) ELF_RELOC(R_RISCV_SUB6, 52) ELF_RELOC(R_RISCV_SET6, 53)