diff --git a/elf/arch-riscv.cc b/elf/arch-riscv.cc index 10250e3036..ab8e8e8d76 100644 --- a/elf/arch-riscv.cc +++ b/elf/arch-riscv.cc @@ -364,6 +364,32 @@ void InputSection::apply_reloc_alloc(Context &ctx, u8 *base) { break; } case R_RISCV_GOT_HI20: + if (ctx.arg.relax && + sym.is_pcrel_linktime_const(ctx) && + i + 2 < rels.size() && + rels[i + 1].r_type == R_RISCV_PCREL_LO12_I && + rels[i + 1].r_offset == rels[i].r_offset + 4 && + file.symbols[rels[i + 1].r_sym]->value == r_offset && + rels[i + 2].r_type == R_RISCV_RELAX) { + // AUIPC + LD is used to load a value from a GOT slot. If the + // value is actually a PC-relative link-time constant, we can + // rewrite AUIPC + LD with AUIPC + ADDI to eliminate the memory + // load. + i64 val = S + A - P; + i64 rd1 = get_rd(rel.r_offset); + i64 rd2 = get_rd(rel.r_offset + 4); + + if ((i32)val == val && rd1 == rd2) { + // auipc , %hi20(val) + write_utype(loc, val); + + // addi , , %lo12(val) + *(ul32 *)(loc + 4) = 0b0010011 | (rd1 << 15) | (rd1 << 7); + write_itype(loc + 4, val); + i += 2; + break; + } + } write_utype(loc, G + GOT + A - P); break; case R_RISCV_TLS_GOT_HI20: