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,22 @@ return; } + case R_RISCV_GPREL_I: + case R_RISCV_GPREL_S: { + Defined *gp = ElfSym::riscvGlobalPointer; + assert(gp && "Can't find GP pointer?"); + 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 +629,31 @@ } } +static void relaxHi20Lo12(const InputSection &sec, size_t i, uint64_t loc, + Relocation &r, uint32_t &remove) { + uint64_t target = r.sym->getVA(r.addend); + Defined *gp = ElfSym::riscvGlobalPointer; + if (gp) { + int64_t displace = target - 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 +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 (config->gpRelax && 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 +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 @@ -217,6 +217,7 @@ bool gdbIndex; bool gnuHash = false; bool gnuUnique; + bool gpRelax; bool hasDynSymTab; bool ignoreDataAddressEquality; bool ignoreFunctionAddressEquality; 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->gpRelax && config->emachine != EM_RISCV) + error("--gp-relax is only support on RISC-V targets"); + if (config->pie && config->shared) error("-shared and -pie may not be used together"); @@ -1121,6 +1124,7 @@ config->gcSections = args.hasFlag(OPT_gc_sections, OPT_no_gc_sections, false); config->gnuUnique = args.hasFlag(OPT_gnu_unique, OPT_no_gnu_unique, true); config->gdbIndex = args.hasFlag(OPT_gdb_index, OPT_no_gdb_index, false); + config->gpRelax = args.hasArg(OPT_gp_relax); config->icf = getICF(args); config->ignoreDataAddressEquality = args.hasArg(OPT_ignore_data_address_equality); diff --git a/lld/ELF/Options.td b/lld/ELF/Options.td --- a/lld/ELF/Options.td +++ b/lld/ELF/Options.td @@ -234,6 +234,8 @@ "Enable STB_GNU_UNIQUE symbol binding (default)", "Disable STB_GNU_UNIQUE symbol binding">; +def gp_relax: F<"gp-relax">, HelpText<"Enable GP relaxation">; + defm hash_style: Eq<"hash-style", "Specify hash style (sysv, gnu or both)">; def help: F<"help">, HelpText<"Print option help">; 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 @@ -971,6 +971,13 @@ rank |= RF_MIPS_NOT_GOT; } + if (config->emachine == EM_RISCV && config->gpRelax) { + // Bring .sdata and .sbss sections closer together. + if (osec.name != ".sbss") + rank |= 2; // TODO: Proper constant + if (osec.name == ".sdata") + rank |= 1; // TODO: Proper constant + } return rank; } @@ -1858,8 +1865,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/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 --gp-relax --undefined=__global_pointer$ %t.rv32.o %t.lds -o %t.rv32 +# RUN: ld.lld --gp-relax --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 --gp-relax --undefined=__global_pointer$ %t.rv32.o %t-out-of-range.lds -o %t.rv32-out-of-range +# RUN: ld.lld --gp-relax --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)