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, @@ -435,6 +436,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; @@ -612,6 +628,31 @@ } } +static void relaxHi20Lo12(const InputSection &sec, size_t i, uint64_t loc, + Relocation &r, uint32_t &remove) { + Defined *gp = ElfSym::riscvGlobalPointer; + if (!gp) + return; + + int64_t displace = r.sym->getVA(r.addend) - gp->getVA(); + + if (isInt<12>(displace)) { + 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; @@ -663,6 +704,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 (config->relaxGP && 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 @@ -776,6 +824,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"); @@ -1187,6 +1190,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 @@ -825,20 +825,22 @@ // * It is easy to check if a give branch was taken. // * It is easy two see how similar two ranks are (see getRankProximity). enum RankFlags { - RF_NOT_ADDR_SET = 1 << 27, - RF_NOT_ALLOC = 1 << 26, - RF_PARTITION = 1 << 18, // Partition number (8 bits) - RF_NOT_PART_EHDR = 1 << 17, - RF_NOT_PART_PHDR = 1 << 16, - RF_NOT_INTERP = 1 << 15, - RF_NOT_NOTE = 1 << 14, - RF_WRITE = 1 << 13, - RF_EXEC_WRITE = 1 << 12, - RF_EXEC = 1 << 11, - RF_RODATA = 1 << 10, - RF_NOT_RELRO = 1 << 9, - RF_NOT_TLS = 1 << 8, - RF_BSS = 1 << 7, + RF_NOT_ADDR_SET = 1 << 29, + RF_NOT_ALLOC = 1 << 28, + RF_PARTITION = 1 << 20, // Partition number (8 bits) + RF_NOT_PART_EHDR = 1 << 19, + RF_NOT_PART_PHDR = 1 << 18, + RF_NOT_INTERP = 1 << 17, + RF_NOT_NOTE = 1 << 16, + RF_WRITE = 1 << 15, + RF_EXEC_WRITE = 1 << 14, + RF_EXEC = 1 << 13, + RF_RODATA = 1 << 12, + RF_NOT_RELRO = 1 << 11, + RF_NOT_TLS = 1 << 10, + RF_BSS = 1 << 9, + RF_RISCV_NOT_SBSS = 1 << 8, + RF_RISCV_SDATA = 1 << 7, RF_PPC_NOT_TOCBSS = 1 << 6, RF_PPC_TOCL = 1 << 5, RF_PPC_TOC = 1 << 4, @@ -971,6 +973,13 @@ rank |= RF_MIPS_NOT_GOT; } + if (config->emachine == EM_RISCV && config->relaxGP) { + // Bring .sdata and .sbss sections closer together. + if (osec.name != ".sbss") + rank |= RF_RISCV_NOT_SBSS; + if (osec.name == ".sdata") + rank |= RF_RISCV_SDATA; + } return rank; } @@ -1858,8 +1867,8 @@ // 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); + ElfSym::riscvGlobalPointer = addOptionalRegular( + "__global_pointer$", sec ? sec : Out::elfHeader, 0x800, STV_DEFAULT); } 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 @@ -350,6 +350,8 @@ Do not set the text data sections to be writable, page align sections. .It Fl -no-relax Disable target-specific relaxations. For x86-64 this disables R_X86_64_GOTPCRELX and R_X86_64_REX_GOTPCRELX GOT optimization. +.It Fl --no-relax-gp +Disable GP relaxation for RISC-V. .It Fl -no-rosegment Do not put read-only non-executable sections in their own segment. .It Fl -undefined-version @@ -493,6 +495,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,33 @@ +# REQUIRES: riscv + +# RUN: llvm-mc -filetype=obj -triple=riscv32-unknown-elf -mattr=+relax %s -o %t.rv32.o +# RUN: llvm-mc -filetype=obj -triple=riscv64-unknown-elf -mattr=+relax %s -o %t.rv64.o +# RUN: llvm-mc -filetype=obj -triple=riscv32-unknown-elf -mattr=+c,+relax %s -o %t.rv32c.o +# RUN: llvm-mc -filetype=obj -triple=riscv64-unknown-elf -mattr=+c,+relax %s -o %t.rv64c.o + +# RUN: echo 'SECTIONS { .text : { *(.text) } .sdata 0x200000 : { foo = .; } }' > %t.lds +# RUN: ld.lld --relax-gp --undefined=__global_pointer$ %t.rv32.o %t.lds -o %t.rv32 +# RUN: ld.lld --relax-gp --undefined=__global_pointer$ %t.rv64.o %t.lds -o %t.rv64 +# RUN: llvm-objdump -d -M no-aliases --no-show-raw-insn %t.rv32 | FileCheck --check-prefix=GP %s +# RUN: llvm-objdump -d -M no-aliases --no-show-raw-insn %t.rv64 | FileCheck --check-prefix=GP %s +# GP-NOT: lui +# GP: addi a0, gp, -2048 +# GP-NEXT: lw a0, -2048(gp) +# GP-NEXT: sw a0, -2048(gp) + +# RUN: echo 'SECTIONS { .text : { *(.text) } .sdata 0x200000 : { foo = . + 4096; } }' > %t-out-of-range.lds +# RUN: ld.lld --relax-gp --undefined=__global_pointer$ %t.rv32.o %t-out-of-range.lds -o %t.rv32-out-of-range +# RUN: ld.lld --relax-gp --undefined=__global_pointer$ %t.rv64.o %t-out-of-range.lds -o %t.rv64-out-of-range +# RUN: llvm-objdump -d -M no-aliases --no-show-raw-insn %t.rv32-out-of-range | FileCheck --check-prefix=NORELAX %s +# RUN: llvm-objdump -d -M no-aliases --no-show-raw-insn %t.rv64-out-of-range | FileCheck --check-prefix=NORELAX %s +# NORELAX: lui a0, 513 +# NORELAX-NEXT: addi a0, a0, 0 +# NORELAX-NEXT: lw a0, 0(a0) +# NORELAX-NEXT: sw a0, 0(a0) + +.global _start +_start: + lui a0, %hi(foo) + addi a0, a0, %lo(foo) + lw a0, %lo(foo)(a0) + sw a0, %lo(foo)(a0) 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)