Jim Wilson
unread,Aug 27, 2018, 2:17:21 PM8/27/18Sign in to reply to author
Sign in to forward
You do not have permission to delete messages in this group
Either email addresses are anonymous for this group or you need the view member email addresses permission to view the original message
to RISC-V SW Dev
Consider this testcase.
int array[995] = { [10] 10, [99] 99 };
long long ll = 100;
long long
sub (void)
{
return ll;
}
int
main (void)
{
return sub ();
}
If I use riscv-gnu-toolchain, configured for rv32i newlib, and compile
it with -O -mcmodel=medany -mexplicit-relocs, in the assembly output I
see
sub:
.LA0: auipc a5,%pcrel_hi(ll)
lw a0,%pcrel_lo(.LA0)(a5)
lw a1,%pcrel_lo(.LA0+4)(a5)
ret
which looks reasonable. Though maybe that should be %pcrel_lo(.LA0)+4
instead, because the +4 is added to the address of ll not the address
of .LA0. However, when I disassemble the a.out file, I see
000101ac <sub>:
101ac: 00002797 auipc a5,0x2
101b0: 7fc7a503 lw a0,2044(a5) # 129a8 <ll>
101b4: 8007a583 lw a1,-2048(a5)
101b8: 00008067 ret
and note that the +4 offset overflowed giving silent bad code.
I carefully choose the array size to force the error. if you have a
slightly different version or configuration of the tools, you might
need a different array size to see the error.
The problem here is that while the variable ll is 8-byte aligned, the
auipc is not aligned, and medany is using the offset between the auipc
and ll, so this offset is not a multiple of 8. The auipc is only
guaranteed to have 4-byte alignment without the C extension, and 2
byte alignment with the C extension. GCC is assuming that any offset
smaller than the alignment of the variable is safe, which is not true
in this case.
The same problem can happen for both rv32 and rv64 when using long
doubles and int128_t, which requires 16-byte alignment. We don't have
anything that requires more than 16-byte alignment though, so the
problem ends here.
Unfortunately, I don't see an obvious, easy, and good solution for this.
We could disallow offsets with pcrel_lo, but that means medany code
won't be as efficient as medlow because it will need extra address
generation instructions.
We could force alignment of auipc, but that means potentially emitting
multiple nops before auipc, which again hurts medany code size and
performance.
We could maybe change the code sequence to something like
aupic %pcrel_hi
addi %pcrel_addi
lw %pcrel_lo_with_addi
lw %pcrel_lo_with_addi+4
and then the new pcrel_addi reloc adds a value if necessary to avoid
overflow, and the pcrel_lo_with_addi subtracts the same value. The
addi can then be deleted via relaxation if it is unnecessary.
However, cleanly specifying and implementing these relocs could be a
problem because of the complex interactions between them.
Other solutions might involve defining a new code model, a new ABI, or
adding new instructions to the ISA, all of which I'm hoping to avoid.
While testing this support, I've also managed to find two binutils
bugs that can result in link time errors when pcrel_lo is used with an
offset. Though exactly how those should be fixed depends on how
exactly we decide to fix the gcc problem. There is also the (third)
linker problem of silently creating bad code when pcrel_lo+offset
overflows. I can add an error for that, but if someone hits it, there
isn't anything they can do to fix it, other than to recompile without
-mexplicit-relocs.
Meanwhile, with the gnu toolchain, use of -mcmodel=medany is safe, but
use of both -mcmodel=medany and -mexplicit-relocs together is not
safe.
Jim