diff --git a/lld/ELF/Arch/RISCV.cpp b/lld/ELF/Arch/RISCV.cpp index 6413dcd7dcd79..4134a8281bff5 100644 --- a/lld/ELF/Arch/RISCV.cpp +++ b/lld/ELF/Arch/RISCV.cpp @@ -648,15 +648,49 @@ static void relaxHi20Lo12(const InputSection &sec, size_t i, uint64_t loc, if (!gp) return; - if (!isInt<12>(r.sym->getVA(r.addend) - gp->getVA())) + int64_t theAddr = r.sym->getVA(r.addend); + uint64_t gpVA = gp->getVA(); + if (!isInt<12>(theAddr - gpVA)) return; + // The symbol may be accessed in multiple pieces with different addends. + // If we are relaxing the HI20 relocation, we need to ensure that we only + // relax (and delete the instruction) if all possible LO12 relocations + // that depend on it will be relaxable. Similarly, when relaxing the LO12 + // relocation, we should ensure that the corresponding HI20 is also relaxed. + // Since relocations are not directly related to one another, we must rely on + // what the code generator is allowed to assume about their relationship. + // Namely, any LO12 relocation that relies on a HI20 relocation must not + // relocate an address in a different "accessible block" for an object. + // An "accessible block" is the block of memory with a size equal to the + // lower of the object's size and alignment. + // For example, it is legal to access an element of a naturaly aligned + // array of 8-byte objects with two different 4-byte loads. + // Element number 1 of an array of double can be accessed both as + // (%hi(arr+8), %lo(arr+8), %lo(arr+12)) and + // (%hi(arr+12), %lo(arr+8), %lo(arr+12)). + // In any case, we only relax if the entire range [arr+8, arr+16) is reachable + // using GP-relative addressing. + uint32_t symAlign = + r.sym->getOutputSection() ? r.sym->getOutputSection()->addralign : 0; + int64_t symSize = r.sym->getSize(); + uint32_t reachWindow = std::min(symAlign, symSize); + int64_t loAddr, hiAddr; + if (reachWindow != 0) { + loAddr = (theAddr / reachWindow) * reachWindow; + hiAddr = loAddr + reachWindow - 1; + } else + loAddr = hiAddr = theAddr; + + if (!isInt<12>(loAddr - gpVA) || !isInt<12>(hiAddr - gpVA)) + return; switch (r.type) { - case R_RISCV_HI20: + case R_RISCV_HI20: { // Remove lui rd, %hi20(x). sec.relaxAux->relocTypes[i] = R_RISCV_RELAX; remove = 4; break; + } case R_RISCV_LO12_I: sec.relaxAux->relocTypes[i] = INTERNAL_R_RISCV_GPREL_I; break; diff --git a/lld/test/ELF/riscv-relax-gp-edges.s b/lld/test/ELF/riscv-relax-gp-edges.s new file mode 100644 index 0000000000000..b42d439a982bb --- /dev/null +++ b/lld/test/ELF/riscv-relax-gp-edges.s @@ -0,0 +1,145 @@ +# 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 rv32a.o +# RUN: llvm-mc -filetype=obj -triple=riscv64-unknown-elf -mattr=+relax a.s -o rv64a.o +# RUN: llvm-mc -filetype=obj -triple=riscv32-unknown-elf -mattr=+relax b.s -o rv32b.o +# RUN: llvm-mc -filetype=obj -triple=riscv64-unknown-elf -mattr=+relax b.s -o rv64b.o +# RUN: llvm-mc -filetype=obj -triple=riscv32-unknown-elf -mattr=+relax c.s -o rv32c.o +# RUN: llvm-mc -filetype=obj -triple=riscv64-unknown-elf -mattr=+relax c.s -o rv64c.o + +# RUN: ld.lld --relax-gp --undefined=__global_pointer$ rv32a.o a.lds -o rv32a +# RUN: ld.lld --relax-gp --undefined=__global_pointer$ rv64a.o a.lds -o rv64a +# RUN: ld.lld --relax-gp --undefined=__global_pointer$ rv32b.o b.lds -o rv32b +# RUN: ld.lld --relax-gp --undefined=__global_pointer$ rv64b.o b.lds -o rv64b +# RUN: ld.lld --relax-gp --undefined=__global_pointer$ rv32c.o c.lds -o rv32c +# RUN: ld.lld --relax-gp --undefined=__global_pointer$ rv64c.o c.lds -o rv64c +# RUN: llvm-objdump -td -M no-aliases --no-show-raw-insn --no-print-imm-hex rv32a | FileCheck %s --check-prefix=CHECK-ALIGN +# RUN: llvm-objdump -td -M no-aliases --no-show-raw-insn --no-print-imm-hex rv64a | FileCheck %s --check-prefix=CHECK-ALIGN +# RUN: llvm-objdump -td -M no-aliases --no-show-raw-insn --no-print-imm-hex rv32b | FileCheck %s --check-prefix=CHECK-HI-ADD +# RUN: llvm-objdump -td -M no-aliases --no-show-raw-insn --no-print-imm-hex rv64b | FileCheck %s --check-prefix=CHECK-HI-ADD +# RUN: llvm-objdump -td -M no-aliases --no-show-raw-insn --no-print-imm-hex rv32c | FileCheck %s --check-prefix=CHECK-SIZE +# RUN: llvm-objdump -td -M no-aliases --no-show-raw-insn --no-print-imm-hex rv64c | FileCheck %s --check-prefix=CHECK-SIZE + +# CHECK-ALIGN: 000017e0 l .data {{0+}}80 Var1 +# CHECK-ALIGN: 00000ffc g .sdata {{0+}}00 __global_pointer$ + +# CHECK-ALIGN: <_start>: +# CHECK-ALIGN-NEXT: lui a1, 1 +# CHECK-ALIGN-NEXT: lw a0, 2016(a1) +# CHECK-ALIGN-NEXT: lw a1, 2044(a1) + +#--- a.s +# The relaxation on the lui is rejected because the alignment (which is smaller +# than the size) doesn't allow it. +.global _start +_start: + lui a1, %hi(Var1) + lw a0, %lo(Var1)(a1) # First part is reachable from gp + lw a1, %lo(Var1+28)(a1) # The second part is not reachable + +.section .sdata,"aw" +.section .data,"aw" + .p2align 5 +Var1: + .quad 0 + .zero 120 + .size Var1, 128 + +#--- a.lds +SECTIONS { + .text : { } + .sdata 0x07fc : { } + .data 0x17E0 : { } +} + +# CHECK-HI-ADD: 00001000 l .data {{0+}}08 Var0 +# CHECK-HI-ADD: 00001f80 l .data1 {{0+}}80 Var1 +# CHECK-HI-ADD: 00001800 g .sdata {{0+}}00 __global_pointer$ + +# CHECK-HI-ADD: <_start>: +# CHECK-HI-ADD-NEXT: lw a0, -2048(gp) +# CHECK-HI-ADD-NEXT: lw a1, -2044(gp) +# CHECK-HI-ADD-NEXT: lw a0, 1920(gp) +# CHECK-HI-ADD-NEXT: lw a1, 2044(gp) + +#--- b.s +# The relaxation on the two lui are rejected because the amount of data a LO12 +# reloc is allowed to address below and above the respective HI20 goes past +# the amount reachable from GP. +.global _start +_start: + lui a1, %hi(Var0+4) + lw a0, %lo(Var0)(a1) + lw a1, %lo(Var0+4)(a1) + lui a1, %hi(Var1+124) + lw a0, %lo(Var1)(a1) + lw a1, %lo(Var1+124)(a1) + +.section .sdata,"aw" +.section .data,"aw" + .p2align 3 +Var0: + .quad 0 + .size Var0, 8 + +.section .data1,"aw" + .p2align 7 +Var1: + .quad 0 + .zero 120 + .size Var1, 128 + +#--- b.lds +SECTIONS { + .text : { } + .sdata 0x1000 : { } + .data 0x1000 : { } + .data1 0x1f80 : { } +} + +# CHECK-SIZE: 00000080 l .data {{0+}}08 Var0 +# CHECK-SIZE: 00001000 l .data1 {{0+}}80 Var1 +# CHECK-SIZE: 00000815 g .sdata {{0+}}00 __global_pointer$ + +# CHECK-SIZE: <_start>: +# CHECK-SIZE-NOT: lui +# CHECK-SIZE-NEXT: lw a0, -1941(gp) +# CHECK-SIZE-NEXT: lw a1, -1937(gp) +# CHECK-SIZE-NEXT: lui a1, 1 +# CHECK-SIZE-NEXT: lw a0, 0(a1) +# CHECK-SIZE-NEXT: lw a1, 124(a1) + +#--- c.s +# The relaxation on the second lui is rejected because the size (and alignment) +# allow for a LO12 that cannot reach its target from GP. +.global _start +_start: + lui a1, %hi(Var0) + lw a0, %lo(Var0)(a1) + lw a1, %lo(Var0+4)(a1) + lui a1, %hi(Var1) + lw a0, %lo(Var1)(a1) # First part is reachable from gp + lw a1, %lo(Var1+124)(a1) # The second part is not reachable + +.section .sdata,"aw" +.section .data,"aw" + .p2align 3 +Var0: + .quad 0 + .size Var0, 8 + +.section .data1,"aw" + .p2align 7 +Var1: + .quad 0 + .zero 120 + .size Var1, 128 + +#--- c.lds +SECTIONS { + .text : { } + .sdata 0x0015 : { } + .data 0x0080 : { } + .data1 0x1000 : { } +}