Skip to content
This repository has been archived by the owner on Aug 17, 2022. It is now read-only.

Commit

Permalink
RISC-V: Relax RISCV_PCREL_* to RISCV_GPREL_*
Browse files Browse the repository at this point in the history
In the medany code model the compiler generates PCREL_HI20+PCREL_LO12
relocation pairs against local symbols because HI20+LO12 relocations
can't reach high addresses.  We relax HI20+LO12 pairs to GPREL
relocations when possible, which is an important optimization for
Dhrystone.  Without this commit we are unable to relax
PCREL_HI20+PCREL_LO12 pairs to GPREL when possible, causing a XX%
permormance hit on Dhrystone on Rocket.

Note that we'll now relax

  la gp, __global_pointer$

to

  mv gp, gp

which probably isn't what you want in your entry code.  Users who want
gp-relative symbols to continue to resolve should add ".option norelax"
accordingly.  Due to this, the assembler now pairs PCREL relocations
with RELAX relocations when they're expected to be relaxed.
  • Loading branch information
palmer-dabbelt committed May 19, 2017
1 parent 979bcbf commit 7fe9c32
Show file tree
Hide file tree
Showing 2 changed files with 270 additions and 8 deletions.
273 changes: 266 additions & 7 deletions bfd/elfnn-riscv.c
Original file line number Diff line number Diff line change
Expand Up @@ -2704,10 +2704,142 @@ riscv_relax_delete_bytes (bfd *abfd, asection *sec, bfd_vma addr, size_t count)
return TRUE;
}

/* A second format for recording PC-relative hi relocations. This stores the
information required to relax them to GP-relative addresses. */

typedef struct riscv_pcgp_hi_reloc riscv_pcgp_hi_reloc;
struct riscv_pcgp_hi_reloc
{
bfd_vma hi_sec_off;
bfd_vma hi_addend;
bfd_vma hi_addr;
unsigned hi_sym;
asection *sym_sec;
riscv_pcgp_hi_reloc *next;
};

typedef struct riscv_pcgp_lo_reloc riscv_pcgp_lo_reloc;
struct riscv_pcgp_lo_reloc
{
bfd_vma hi_sec_off;
riscv_pcgp_lo_reloc *next;
};

typedef struct
{
riscv_pcgp_hi_reloc *hi;
riscv_pcgp_lo_reloc *lo;
} riscv_pcgp_relocs;

static bfd_boolean
riscv_init_pcgp_relocs (riscv_pcgp_relocs *p)
{
p->hi = NULL;
p->lo = NULL;
return TRUE;
}

static bfd_boolean
riscv_free_pcgp_relocs (riscv_pcgp_relocs *p, bfd *abfd, asection *sec)
{
for (riscv_pcgp_hi_reloc *c = p->hi; c != NULL;)
{
riscv_pcgp_hi_reloc *next = c->next;
free (c);
c = next;
}

for (riscv_pcgp_lo_reloc *c = p->lo; c != NULL;)
{
riscv_pcgp_lo_reloc *next = c->next;
free (c);
c = next;
}
}

static bfd_boolean
riscv_record_pcgp_hi_reloc (riscv_pcgp_relocs *p, bfd_vma hi_sec_off,
bfd_vma hi_addend, bfd_vma hi_addr,
unsigned hi_sym, asection *sym_sec)
{
riscv_pcgp_hi_reloc *new = bfd_malloc (sizeof(*new));
if (!new)
return FALSE;
new->hi_sec_off = hi_sec_off;
new->hi_addend = hi_addend;
new->hi_addr = hi_addr;
new->hi_sym = hi_sym;
new->sym_sec = sym_sec;
new->next = p->hi;
p->hi = new;
return TRUE;
}

static riscv_pcgp_hi_reloc *
riscv_find_pcgp_hi_reloc(riscv_pcgp_relocs *p, bfd_vma hi_sec_off)
{
for (riscv_pcgp_hi_reloc *c = p->hi; c != NULL; c = c->next)
if (c->hi_sec_off == hi_sec_off)
return c;
return NULL;
}

static bfd_boolean
riscv_delete_pcgp_hi_reloc(riscv_pcgp_relocs *p, bfd_vma hi_sec_off)
{
bfd_boolean out = FALSE;

for (riscv_pcgp_hi_reloc *c = p->hi; c != NULL; c = c->next)
if (c->hi_sec_off == hi_sec_off)
out = TRUE;

return out;
}

static bfd_boolean
riscv_use_pcgp_hi_reloc(riscv_pcgp_relocs *p, bfd_vma hi_sec_off)
{
bfd_boolean out = FALSE;

for (riscv_pcgp_hi_reloc *c = p->hi; c != NULL; c = c->next)
if (c->hi_sec_off == hi_sec_off)
out = TRUE;

return out;
}

static bfd_boolean
riscv_record_pcgp_lo_reloc (riscv_pcgp_relocs *p, bfd_vma hi_sec_off)
{
riscv_pcgp_lo_reloc *new = bfd_malloc (sizeof(*new));
if (!new)
return FALSE;
new->hi_sec_off = hi_sec_off;
new->next = p->lo;
p->lo = new;
return TRUE;
}

static bfd_boolean
riscv_find_pcgp_lo_reloc (riscv_pcgp_relocs *p, bfd_vma hi_sec_off)
{
for (riscv_pcgp_lo_reloc *c = p->lo; c != NULL; c = c->next)
if (c->hi_sec_off == hi_sec_off)
return TRUE;
return FALSE;
}

static bfd_boolean
riscv_delete_pcgp_lo_reloc(riscv_pcgp_relocs *p, bfd_vma lo_sec_off, size_t bytes)
{
return TRUE;
}

typedef bfd_boolean (*relax_func_t) (bfd *, asection *, asection *,
struct bfd_link_info *,
Elf_Internal_Rela *,
bfd_vma, bfd_vma, bfd_vma, bfd_boolean *);
bfd_vma, bfd_vma, bfd_vma, bfd_boolean *,
riscv_pcgp_relocs *);

/* Relax AUIPC + JALR into JAL. */

Expand All @@ -2718,7 +2850,8 @@ _bfd_riscv_relax_call (bfd *abfd, asection *sec, asection *sym_sec,
bfd_vma symval,
bfd_vma max_alignment,
bfd_vma reserve_size ATTRIBUTE_UNUSED,
bfd_boolean *again)
bfd_boolean *again,
riscv_pcgp_relocs *pcgp_relocs ATTRIBUTE_UNUSED)
{
bfd_byte *contents = elf_section_data (sec)->this_hdr.contents;
bfd_signed_vma foff = symval - (sec_addr (sec) + rel->r_offset);
Expand Down Expand Up @@ -2801,7 +2934,8 @@ _bfd_riscv_relax_lui (bfd *abfd,
bfd_vma symval,
bfd_vma max_alignment,
bfd_vma reserve_size,
bfd_boolean *again)
bfd_boolean *again,
riscv_pcgp_relocs *pcgp_relocs ATTRIBUTE_UNUSED)
{
bfd_byte *contents = elf_section_data (sec)->this_hdr.contents;
bfd_vma gp = riscv_global_pointer_value (link_info);
Expand Down Expand Up @@ -2890,7 +3024,8 @@ _bfd_riscv_relax_tls_le (bfd *abfd,
bfd_vma symval,
bfd_vma max_alignment ATTRIBUTE_UNUSED,
bfd_vma reserve_size ATTRIBUTE_UNUSED,
bfd_boolean *again)
bfd_boolean *again,
riscv_pcgp_relocs *prcel_relocs ATTRIBUTE_UNUSED)
{
/* See if this symbol is in range of tp. */
if (RISCV_CONST_HIGH_PART (tpoff (link_info, symval)) != 0)
Expand Down Expand Up @@ -2929,7 +3064,8 @@ _bfd_riscv_relax_align (bfd *abfd, asection *sec,
bfd_vma symval,
bfd_vma max_alignment ATTRIBUTE_UNUSED,
bfd_vma reserve_size ATTRIBUTE_UNUSED,
bfd_boolean *again ATTRIBUTE_UNUSED)
bfd_boolean *again ATTRIBUTE_UNUSED,
riscv_pcgp_relocs *pcrel_relocs ATTRIBUTE_UNUSED)
{
bfd_byte *contents = elf_section_data (sec)->this_hdr.contents;
bfd_vma alignment = 1, pos;
Expand Down Expand Up @@ -2969,6 +3105,118 @@ _bfd_riscv_relax_align (bfd *abfd, asection *sec,

/* Relax PC-relative references to GP-relative references. */

static bfd_boolean
_bfd_riscv_relax_pc (bfd *abfd,
asection *sec,
asection *sym_sec,
struct bfd_link_info *link_info,
Elf_Internal_Rela *rel,
bfd_vma symval,
bfd_vma max_alignment,
bfd_vma reserve_size,
bfd_boolean *again ATTRIBUTE_UNUSED,
riscv_pcgp_relocs *pcgp_relocs)
{
bfd_vma gp = riscv_global_pointer_value (link_info);

BFD_ASSERT (rel->r_offset + 4 <= sec->size);

/* Chain the _LO relocs to their cooresponding _HI reloc to compute the
* actual target address. */
riscv_pcgp_hi_reloc hi_reloc = {0};
switch (ELFNN_R_TYPE (rel->r_info))
{
case R_RISCV_PCREL_LO12_I:
case R_RISCV_PCREL_LO12_S:
{
riscv_pcgp_hi_reloc *hi = riscv_find_pcgp_hi_reloc (pcgp_relocs,
symval - sec_addr(sym_sec));
if (hi == NULL)
{
riscv_record_pcgp_lo_reloc (pcgp_relocs, symval - sec_addr(sym_sec));
return TRUE;
}

hi_reloc = *hi;
symval = hi_reloc.hi_addr;
sym_sec = hi_reloc.sym_sec;
if (!riscv_use_pcgp_hi_reloc(pcgp_relocs, hi->hi_sec_off))
(*_bfd_error_handler)
(_("%B(%A+0x%lx): Unable to clear RISCV_PCREL_HI20 reloc"
"for cooresponding RISCV_PCREL_LO12 reloc"),
abfd, sec, rel->r_offset);
}
break;

case R_RISCV_PCREL_HI20:
/* Mergeable symbols and code might later move out of range. */
if (sym_sec->flags & (SEC_MERGE | SEC_CODE))
return TRUE;

/* If the cooresponding lo relocation has already been seen then it's not
* safe to relax this relocation. */
if (riscv_find_pcgp_lo_reloc (pcgp_relocs, rel->r_offset))
return TRUE;

break;

default:
abort ();
}

if (gp)
{
/* If gp and the symbol are in the same output section, then
consider only that section's alignment. */
struct bfd_link_hash_entry *h =
bfd_link_hash_lookup (link_info->hash, RISCV_GP_SYMBOL, FALSE, FALSE, TRUE);
if (h->u.def.section->output_section == sym_sec->output_section)
max_alignment = (bfd_vma) 1 << sym_sec->output_section->alignment_power;
}

/* Is the reference in range of x0 or gp?
Valid gp range conservatively because of alignment issue. */
if (VALID_ITYPE_IMM (symval)
|| (symval >= gp
&& VALID_ITYPE_IMM (symval - gp + max_alignment + reserve_size))
|| (symval < gp
&& VALID_ITYPE_IMM (symval - gp - max_alignment - reserve_size)))
{
unsigned sym = hi_reloc.hi_sym;
switch (ELFNN_R_TYPE (rel->r_info))
{
case R_RISCV_PCREL_LO12_I:
rel->r_info = ELFNN_R_INFO (sym, R_RISCV_GPREL_I);
rel->r_addend += hi_reloc.hi_addend;
return riscv_delete_pcgp_lo_reloc (pcgp_relocs, rel->r_offset, 4);

case R_RISCV_PCREL_LO12_S:
rel->r_info = ELFNN_R_INFO (sym, R_RISCV_GPREL_S);
rel->r_addend += hi_reloc.hi_addend;
return riscv_delete_pcgp_lo_reloc (pcgp_relocs, rel->r_offset, 4);

case R_RISCV_PCREL_HI20:
riscv_record_pcgp_hi_reloc (pcgp_relocs,
rel->r_offset,
rel->r_addend,
symval,
ELFNN_R_SYM(rel->r_info),
sym_sec);
/* We can delete the unnecessary AUIPC and reloc. */
rel->r_info = ELFNN_R_INFO (0, R_RISCV_DELETE);
rel->r_addend = 4;
return riscv_delete_pcgp_hi_reloc (pcgp_relocs, rel->r_offset);

default:
abort ();
}
}

return TRUE;
}

/* Relax PC-relative references to GP-relative references. */

static bfd_boolean
_bfd_riscv_relax_delete (bfd *abfd,
asection *sec,
Expand All @@ -2978,7 +3226,8 @@ _bfd_riscv_relax_delete (bfd *abfd,
bfd_vma symval ATTRIBUTE_UNUSED,
bfd_vma max_alignment ATTRIBUTE_UNUSED,
bfd_vma reserve_size ATTRIBUTE_UNUSED,
bfd_boolean *again ATTRIBUTE_UNUSED)
bfd_boolean *again ATTRIBUTE_UNUSED,
riscv_pcgp_relocs *pcgp_relocs ATTRIBUTE_UNUSED)
{
if (!riscv_relax_delete_bytes(abfd, sec, rel->r_offset, rel->r_addend))
return FALSE;
Expand All @@ -3002,6 +3251,7 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec,
bfd_boolean ret = FALSE;
unsigned int i;
bfd_vma max_alignment, reserve_size = 0;
riscv_pcgp_relocs pcgp_relocs;

*again = FALSE;

Expand All @@ -3013,6 +3263,8 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec,
&& info->relax_pass == 0))
return TRUE;

riscv_init_pcgp_relocs (&pcgp_relocs);

/* Read this BFD's relocs if we haven't done so already. */
if (data->relocs)
relocs = data->relocs;
Expand Down Expand Up @@ -3040,6 +3292,11 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec,
|| type == R_RISCV_LO12_I
|| type == R_RISCV_LO12_S)
relax_func = _bfd_riscv_relax_lui;
else if (!bfd_link_pic(info)
&& (type == R_RISCV_PCREL_HI20
|| type == R_RISCV_PCREL_LO12_I
|| type == R_RISCV_PCREL_LO12_S))
relax_func = _bfd_riscv_relax_pc;
else if (type == R_RISCV_TPREL_HI20
|| type == R_RISCV_TPREL_ADD
|| type == R_RISCV_TPREL_LO12_I
Expand Down Expand Up @@ -3130,7 +3387,8 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec,
symval += rel->r_addend;

if (!relax_func (abfd, sec, sym_sec, info, rel, symval,
max_alignment, reserve_size, again))
max_alignment, reserve_size, again,
&pcgp_relocs))
goto fail;
}

Expand All @@ -3139,6 +3397,7 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec,
fail:
if (relocs != data->relocs)
free (relocs);
riscv_free_pcgp_relocs(&pcgp_relocs, abfd, sec);

return ret;
}
Expand Down
5 changes: 4 additions & 1 deletion gas/config/tc-riscv.c
Original file line number Diff line number Diff line change
Expand Up @@ -1878,7 +1878,6 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
break;

case BFD_RELOC_RISCV_GOT_HI20:
case BFD_RELOC_RISCV_PCREL_HI20:
case BFD_RELOC_RISCV_ADD8:
case BFD_RELOC_RISCV_ADD16:
case BFD_RELOC_RISCV_ADD32:
Expand Down Expand Up @@ -2054,8 +2053,12 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
relaxable = TRUE;
break;

case BFD_RELOC_RISCV_PCREL_HI20:
case BFD_RELOC_RISCV_PCREL_LO12_S:
case BFD_RELOC_RISCV_PCREL_LO12_I:
relaxable = riscv_opts.relax;
break;

case BFD_RELOC_RISCV_ALIGN:
break;

Expand Down

0 comments on commit 7fe9c32

Please sign in to comment.