#define loongarch_elf_hash_table(p) \
((struct loongarch_elf_link_hash_table *) ((p)->hash)) \
+/* During linker relaxation, indicates whether the section has already
+ undergone alignment processing and no more byte deletion is permitted. */
+#define loongarch_sec_closed_for_deletion(sec) ((sec)->sec_flg0)
+
#define MINUS_ONE ((bfd_vma) 0 - 1)
#define sec_addr(sec) ((sec)->output_section->vma + (sec)->output_offset)
struct pending_delete_op *op;
splay_tree_node node;
- BFD_ASSERT (pdops != NULL);
+ if (!pdops)
+ /* Currently this means we are past the stages where byte deletion could
+ possibly happen. */
+ return offset;
/* Find the op that starts just before the given address. */
node = splay_tree_predecessor (pdops, (splay_tree_key)offset);
static void
loongarch_relax_delete_bytes (bfd *abfd,
- bfd_vma addr,
- size_t count,
- struct bfd_link_info *link_info)
+ bfd_vma addr,
+ size_t count,
+ struct bfd_link_info *link_info)
{
struct loongarch_elf_link_hash_table *htab
= loongarch_elf_hash_table (link_info);
}
}
+static void
+loongarch_relax_delete_or_nop (bfd *abfd,
+ asection *sec,
+ bfd_vma addr,
+ size_t count,
+ struct bfd_link_info *link_info)
+{
+ struct bfd_elf_section_data *data = elf_section_data (sec);
+ bfd_byte *contents = data->this_hdr.contents;
+
+ BFD_ASSERT (count % 4 == 0);
+
+ if (!loongarch_sec_closed_for_deletion (sec))
+ {
+ /* Deletions are still possible within the section. */
+ loongarch_relax_delete_bytes (abfd, addr, count, link_info);
+ return;
+ }
+
+ /* We can no longer delete bytes in the section after enforcing alignment.
+ But as the resulting shrinkage may open up a few more relaxation chances,
+ allowing unnecessary instructions to be replaced with NOPs instead of
+ being removed altogether may still benefit performance to a lesser
+ extent. */
+ for (; count; addr += 4, count -= 4)
+ bfd_put (32, abfd, LARCH_NOP, contents + addr);
+}
+
static void
loongarch_relax_perform_deletes (bfd *abfd, asection *sec,
struct bfd_link_info *link_info)
bfd_put (32, abfd, LARCH_NOP, contents + rel->r_offset);
/* link with -relax option will delete NOP. */
if (!info->disable_target_specific_optimizations)
- loongarch_relax_delete_bytes (abfd, rel->r_offset, 4, info);
+ loongarch_relax_delete_or_nop (abfd, sec, rel->r_offset, 4, info);
return true;
case R_LARCH_TLS_IE_PC_HI20:
if (symval < 0x800)
{
rel->r_info = ELFNN_R_INFO (0, R_LARCH_NONE);
- loongarch_relax_delete_bytes (abfd, rel->r_offset,
+ loongarch_relax_delete_or_nop (abfd, sec, rel->r_offset,
4, link_info);
}
break;
case R_LARCH_TLS_LE64_LO20:
case R_LARCH_TLS_LE64_HI12:
rel->r_info = ELFNN_R_INFO (0, R_LARCH_NONE);
- loongarch_relax_delete_bytes (abfd, rel->r_offset,
- 4, link_info);
+ loongarch_relax_delete_or_nop (abfd, sec, rel->r_offset,
+ 4, link_info);
break;
case R_LARCH_TLS_LE_LO12:
symval = sec_addr (sec)
+ loongarch_calc_relaxed_addr (info, symval - sec_addr (sec));
- /* If pc and symbol not in the same segment, add/sub segment alignment. */
- if (!loongarch_two_sections_in_same_segment (info->output_bfd,
- sec->output_section,
- sym_sec->output_section))
- max_alignment = info->maxpagesize > max_alignment ? info->maxpagesize
- : max_alignment;
-
- if (symval > pc)
- pc -= (max_alignment > 4 ? max_alignment : 0);
- else if (symval < pc)
- pc += (max_alignment > 4 ? max_alignment : 0);
+ /* If pc and symbol not in the same segment, add/sub segment alignment if the
+ section has not undergone alignment processing because distances may grow
+ after alignment. */
+ if (!loongarch_sec_closed_for_deletion (sec))
+ {
+ if (!loongarch_two_sections_in_same_segment (info->output_bfd,
+ sec->output_section,
+ sym_sec->output_section))
+ max_alignment = info->maxpagesize > max_alignment ? info->maxpagesize
+ : max_alignment;
+
+ if (symval > pc)
+ pc -= (max_alignment > 4 ? max_alignment : 0);
+ else if (symval < pc)
+ pc += (max_alignment > 4 ? max_alignment : 0);
+ }
const uint32_t pcaddi = LARCH_OP_PCADDI;
R_LARCH_PCREL20_S2);
rel_lo->r_info = ELFNN_R_INFO (0, R_LARCH_NONE);
- loongarch_relax_delete_bytes (abfd, rel_lo->r_offset, 4, info);
+ loongarch_relax_delete_or_nop (abfd, sec, rel_lo->r_offset, 4, info);
return true;
}
symval = sec_addr (sec)
+ loongarch_calc_relaxed_addr (info, symval - sec_addr (sec));
- /* If pc and symbol not in the same segment, add/sub segment alignment. */
- if (!loongarch_two_sections_in_same_segment (info->output_bfd,
- sec->output_section,
- sym_sec->output_section))
- max_alignment = info->maxpagesize > max_alignment ? info->maxpagesize
- : max_alignment;
-
- if (symval > pc)
- pc -= (max_alignment > 4 ? max_alignment : 0);
- else if (symval < pc)
- pc += (max_alignment > 4 ? max_alignment : 0);
+ /* If pc and symbol not in the same segment, add/sub segment alignment if the
+ section has not undergone alignment processing because distances may grow
+ after alignment. */
+ if (!loongarch_sec_closed_for_deletion (sec))
+ {
+ if (!loongarch_two_sections_in_same_segment (info->output_bfd,
+ sec->output_section,
+ sym_sec->output_section))
+ max_alignment = info->maxpagesize > max_alignment ? info->maxpagesize
+ : max_alignment;
+
+ if (symval > pc)
+ pc -= (max_alignment > 4 ? max_alignment : 0);
+ else if (symval < pc)
+ pc += (max_alignment > 4 ? max_alignment : 0);
+ }
/* Is pcalau12i + addi.d insns? */
if (!LARCH_INSN_JIRL (jirl)
/* Adjust relocations. */
rel->r_info = ELFNN_R_INFO (ELFNN_R_SYM (rel->r_info), R_LARCH_B26);
/* Delete jirl instruction. */
- loongarch_relax_delete_bytes (abfd, rel->r_offset + 4, 4, info);
+ loongarch_relax_delete_or_nop (abfd, sec, rel->r_offset + 4, 4, info);
return true;
}
symval = sec_addr (sec)
+ loongarch_calc_relaxed_addr (info, symval - sec_addr (sec));
- /* If pc and symbol not in the same segment, add/sub segment alignment. */
- if (!loongarch_two_sections_in_same_segment (info->output_bfd,
- sec->output_section,
- sym_sec->output_section))
- max_alignment = info->maxpagesize > max_alignment ? info->maxpagesize
- : max_alignment;
-
- if (symval > pc)
- pc -= (max_alignment > 4 ? max_alignment : 0);
- else if (symval < pc)
- pc += (max_alignment > 4 ? max_alignment : 0);
+ /* If pc and symbol not in the same segment, add/sub segment alignment if the
+ section has not undergone alignment processing because distances may grow
+ after alignment. */
+ if (!loongarch_sec_closed_for_deletion (sec))
+ {
+ if (!loongarch_two_sections_in_same_segment (info->output_bfd,
+ sec->output_section,
+ sym_sec->output_section))
+ max_alignment = info->maxpagesize > max_alignment ? info->maxpagesize
+ : max_alignment;
+
+ if (symval > pc)
+ pc -= (max_alignment > 4 ? max_alignment : 0);
+ else if (symval < pc)
+ pc += (max_alignment > 4 ? max_alignment : 0);
+ }
if ((ELFNN_R_TYPE (rel_lo->r_info) != R_LARCH_GOT_PC_LO12)
|| (LARCH_GET_RD (ld) != rd)
loongarch_elf_hash_table (info)->data_segment_phase = data_segment_phase;
}
-/* Implement R_LARCH_ALIGN by deleting excess alignment NOPs.
- Once we've handled an R_LARCH_ALIGN, we can't relax anything else. */
+/* Honor R_LARCH_ALIGN requests by deleting excess alignment NOPs.
+ Once we've handled an R_LARCH_ALIGN, we can't relax anything else by deleting
+ bytes, or alignment will be disrupted. */
static bool
loongarch_relax_align (bfd *abfd, asection *sec, asection *sym_sec,
Elf_Internal_Rela *rel,
return false;
}
- /* Once we've handled an R_LARCH_ALIGN in a section,
- we can't relax anything else in this section. */
- sec->sec_flg0 = true;
+ /* Once we've handled an R_LARCH_ALIGN in a section, we can't relax anything
+ else by deleting bytes, or alignment will be disrupted. */
+ loongarch_sec_closed_for_deletion (sec) = true;
rel->r_info = ELFNN_R_INFO (0, R_LARCH_NONE);
/* If skipping more bytes than the specified maximum,
symval = sec_addr (sec)
+ loongarch_calc_relaxed_addr (info, symval - sec_addr (sec));
- /* If pc and symbol not in the same segment, add/sub segment alignment. */
- if (!loongarch_two_sections_in_same_segment (info->output_bfd,
- sec->output_section,
- sym_sec->output_section))
- max_alignment = info->maxpagesize > max_alignment ? info->maxpagesize
- : max_alignment;
-
- if (symval > pc)
- pc -= (max_alignment > 4 ? max_alignment : 0);
- else if (symval < pc)
- pc += (max_alignment > 4 ? max_alignment : 0);
+ /* If pc and symbol not in the same segment, add/sub segment alignment if the
+ section has not undergone alignment processing because distances may grow
+ after alignment. */
+ if (!loongarch_sec_closed_for_deletion (sec))
+ {
+ if (!loongarch_two_sections_in_same_segment (info->output_bfd,
+ sec->output_section,
+ sym_sec->output_section))
+ max_alignment = info->maxpagesize > max_alignment ? info->maxpagesize
+ : max_alignment;
+
+ if (symval > pc)
+ pc -= (max_alignment > 4 ? max_alignment : 0);
+ else if (symval < pc)
+ pc += (max_alignment > 4 ? max_alignment : 0);
+ }
const uint32_t pcaddi = LARCH_OP_PCADDI;
}
rel_lo->r_info = ELFNN_R_INFO (0, R_LARCH_NONE);
- loongarch_relax_delete_bytes (abfd, rel_lo->r_offset, 4, info);
+ loongarch_relax_delete_or_nop (abfd, sec, rel_lo->r_offset, 4, info);
return true;
}
if (htab->layout_mutating_for_relr)
return true;
+ /* Definition of LoongArch linker relaxation passes:
+
+ - Pass 0: relaxes everything except R_LARCH_ALIGN, byte deletions are
+ performed; skipped if disable_target_specific_optimizations.
+ - Pass 1: handles alignment, byte deletions are performed. Sections with
+ R_LARCH_ALIGN relocations are marked closed for further byte
+ deletion in order to not disturb alignment. This pass is NOT
+ skipped even if disable_target_specific_optimizations is true.
+ - Pass 2: identical to Pass 0, but replacing relaxed insns with NOP in case
+ the containing section is closed for deletion; skip condition
+ also same as Pass 0. */
+ bool is_alignment_pass = info->relax_pass == 1;
if (bfd_link_relocatable (info)
- || sec->sec_flg0
|| sec->reloc_count == 0
|| (sec->flags & SEC_RELOC) == 0
|| (sec->flags & SEC_HAS_CONTENTS) == 0
/* The exp_seg_relro_adjust is enum phase_enum (0x4). */
|| *(htab->data_segment_phase) == 4
- || (info->disable_target_specific_optimizations
- && info->relax_pass == 0))
+ || (info->disable_target_specific_optimizations && !is_alignment_pass))
return true;
struct bfd_elf_section_data *data = elf_section_data (sec);
htab->max_alignment = max_alignment;
}
- splay_tree pdops = pending_delete_ops_new (abfd);
+ splay_tree pdops = NULL;
+ if (!loongarch_sec_closed_for_deletion (sec))
+ pdops = pending_delete_ops_new (abfd);
+
htab->pending_delete_ops = pdops;
for (unsigned int i = 0; i < sec->reloc_count; i++)
}
relax_func_t relax_func = NULL;
- if (info->relax_pass == 0)
+ if (is_alignment_pass)
+ {
+ if (r_type != R_LARCH_ALIGN)
+ continue;
+ relax_func = loongarch_relax_align;
+ }
+ else
{
switch (r_type)
{
continue;
}
}
- else if (info->relax_pass == 1 && r_type == R_LARCH_ALIGN)
- relax_func = loongarch_relax_align;
- else
- continue;
/* Four kind of relocations:
Normal: symval is the symbol address.
info, again, max_alignment);
}
- loongarch_relax_perform_deletes (abfd, sec, info);
- htab->pending_delete_ops = NULL;
- splay_tree_delete (pdops);
+ if (pdops)
+ {
+ loongarch_relax_perform_deletes (abfd, sec, info);
+ htab->pending_delete_ops = NULL;
+ splay_tree_delete (pdops);
+ }
return true;
}