1
1
mirror of https://github.com/rui314/mold.git synced 2024-10-26 13:10:46 +03:00

[LoongArch] Fix symbol value computation for the extreme code model

The LoongArch extreme code model uses the following instructions to
materialize a 64-bit value in a regiter.

  pcalau12i $t1, %pc_hi20(g2)
  addi.d    $t0, $zero, %pc_lo12(g2)
  lu32i.d   $t0, %pc64_lo20(g2)
  lu52i.d   $t0, $t0, %pc64_hi12(g2)

The previous relocation formula expected that `page(pc)` would be
consistent for all the instructions, but this assumption is incorrect
if the instruction sequence crosses a 4 KiB boundary.

Now, the LoongArch psABI requires that the machine instructions must be
consecutive, and the relocations for lu32.d and lu52i.d uses
`page(pc - 8)` and `page(pc - 16)` instead of `page(pc)`.

This psABI change gave me an impression that the LoongArch's extreme code
model was poorly designed and inadequately tested. If these instructions
must be consecutive, only a single relocation referring to the beginning
of the instructions would suffice, which relocates all the following four
instructions at once.

Maybe we do not need to support the relocations for the extreme code model
because the code model was buggy at the spec level, which suggests that no
one is using them. But I'm not sure if it is safe to remove them, so let's
just follow the psABI change.
This commit is contained in:
Rui Ueyama 2024-08-14 13:21:00 +09:00
parent 9e0c1c81dd
commit d6d8d2e494

View File

@ -51,7 +51,7 @@ static u64 hi20(u64 val, u64 pc) {
return bits(page(val + 0x800) - page(pc), 31, 12);
}
static u64 hi64(u64 val, u64 pc) {
static u64 higher20(u64 val, u64 pc) {
// A PC-relative 64-bit address is materialized with the following
// instructions for the large code model:
//
@ -65,21 +65,15 @@ static u64 hi64(u64 val, u64 pc) {
// ADDI.D adds a sign-extended 12 bit value to a register. LU32I.D and
// LU52I.D simply set bits to [51:31] and to [63:53], respectively.
//
// Compensating all the sign-extensions is a bit complicated.
u64 x = page(val) - page(pc);
if (val & 0x800)
x += 0x1000 - 0x1'0000'0000;
if (x & 0x8000'0000)
x += 0x1'0000'0000;
return x;
}
static u64 higher20(u64 val, u64 pc) {
return bits(hi64(val, pc), 51, 32);
// Compensating all the sign-extensions is a bit complicated. The
// psABI gave the following formula.
val = val + 0x8000'0000 + ((val & 0x800) ? (0x1000 - 0x1'0000'0000) : 0);
return bits(page(val) - page(pc - 8), 51, 32);
}
static u64 highest12(u64 val, u64 pc) {
return bits(hi64(val, pc), 63, 52);
val = val + 0x8000'0000 + ((val & 0x800) ? (0x1000 - 0x1'0000'0000) : 0);
return bits(page(val) - page(pc - 12), 63, 52);
}
static void write_k12(u8 *loc, u32 val) {