https://sourceware.org/bugzilla/show_bug.cgi?id=33538
Bug ID: 33538
Summary: [RISC-V] Missed opportunities to relax PC-relative to
GP-relative
Product: binutils
Version: 2.45
Status: UNCONFIRMED
Severity: normal
Priority: P2
Component: binutils
Assignee: unassigned at sourceware dot org
Reporter: davidegrayson at gmail dot com
Target Milestone: ---
The GNU linker is capable of relaxing a pair of instructions using the
R_RISCV_PCREL_HI20 and R_RISCV_PCREL_LO12_[I,S] relocations into a single
`addi _,gp,offset` instruction if the target is near the value of the special
symbol `__global_pointer$`. The allowed values for the offset are
-2048 to 2047.
However, it seems like there are some cases where the linker fails to do it,
for variables that are near the edge of the range and for symbols defined
in the linker script.
Here is an assembly file and linker script that reproduces these issues:
----
.text
.global _start
_start:
.option norelax
la gp, __global_pointer$
.option relax
la a1, _var1 # should get relaxed but doesn't (gp offset -2048)
la a2, _var2 # should get relaxed but doesn't (.balign)
la a3, _var3 # should get relaxed but doesn't (.size)
la a7, __idata_end # gp offset is within range, but doesn't get relaxed
(absolute)
.data
_var1:
.byte 1
_var2:
.byte 2
.byte 0
.byte 0
_var3:
.word 3
.size _var3, . - _var3
.balign 4
----
MEMORY
{
ROM : ORIGIN = 0x10000000, LENGTH = 64K
RAM : ORIGIN = 0x20000000, LENGTH = 64K
}
SECTIONS
{
.text : { *(.text) } >ROM
.data : { *(.data); __idata_end = .; *(.bss); } >RAM
}
__global_pointer$ = 0x20000800;
-----
I ran these commands with binutil 2.45:
riscv64-unknown-elf-as -march=rv32i -mabi=ilp32 test.asm -o test.o
riscv64-unknown-elf-ld --relax --relax-gp --no-pie -m elf32lriscv -T test.ld
test.o -o test.elf
riscv64-unknown-elf-objdump -d test.elf
The output from objdump showed that nothing was relaxed. The output of
objdump also shows the offsets of all the variables involved, so you can
see they were all within range of the __global_pointer$.
10000000 <_start>:
10000000: 10001197 auipc gp,0x10001
10000004: 80018193 addi gp,gp,-2048 # 20000800
<__global_pointer$>
10000008: 10000597 auipc a1,0x10000
1000000c: ff858593 addi a1,a1,-8 # 20000000 <_var1>
10000010: 10000617 auipc a2,0x10000
10000014: ff160613 addi a2,a2,-15 # 20000001 <_var2>
10000018: 10000697 auipc a3,0x10000
1000001c: fec68693 addi a3,a3,-20 # 20000004 <_var3>
10000020: 10000897 auipc a7,0x10000
10000024: fe888893 addi a7,a7,-24 # 20000008
<__idata_end>
If you remove the `.balign 4` directive AND decrease `__global_pointer$` by 1
in the linker script, then the `_var1` relocations are relaxed properly.
If you remove the `.balign 4` directive, then the `_var2` relocations are
relaxed properly. I know that `.balign 4` introduces some uncertainty about
the size of the section, so maybe the linker is misinterpreting that and
thinking that `_var2` might get moved out of range. Once all of the
addresses in RAM have been determined, it is in principle possible to
accurately relax the code in ROM that references them, even if the
addresses were initially uncertain.
If you remove the `.size` directive, then the _var3 relocations are
relaxed properly. I have no idea why that happens; the symbol size is just
metadata for the debuggers as far as I know.
I suspect `__idata_end` doesn't get relaxed simply because it is defined
in the linker script, so the toolchain treats it as an "absolute" symbol.
It doesn't get relaxed even if I move it closer to the GP by adding
1024 bytes of data to the .data section.
I believe the code responsible for this relaxation is
`_bfd_riscv_relax_pc` in `bfd/elfnn-riscv.c`.
--
You are receiving this mail because:
You are on the CC list for the bug.