[PATCH] LoongArch: Batch-delete bytes at the end of each relax trip
mengqinggang
mengqinggang@loongson.cn
Sat Jun 14 07:40:08 GMT 2025
LGTM.
It's a great improvement for LoongArch linker relaxation. Thank you very
much!
Some output of gcc check_GNU_style.sh see blow.
Could you please send a new version to fix these code style. Thanks.
Blocks of 8 spaces should be replaced with tabs.
218:+ delete_op_node->key -+ +- next->key
219:+ |<-- op1->size -->| |
220:+ v v v
282:+ = loongarch_calc_relaxed_addr (link_info, orig_value);
332:+ = loongarch_calc_relaxed_addr (link_info, orig_value);
Dot, space, space, new sentence.
73:+ ops had been executed. Used to calculate expected addresses after
215:+ /* Actually delete the bytes. For each delete op the pointer
arithmetics look
There should be exactly one space between function name and parenthesis.
381:+ symval = loongarch_calc_relaxed_addr(link_info, symval);
412:+ + loongarch_calc_relaxed_addr(info, symval - sec_addr (sec));
434:+ + loongarch_calc_relaxed_addr(info, symval - sec_addr (sec));
453:+ + loongarch_calc_relaxed_addr(info, rel_hi->r_offset);
456:+ + loongarch_calc_relaxed_addr(info, symval - sec_addr (sec));
Trailing operator.
138:+ struct loongarch_elf_link_hash_table *htab =
219:+ |<-- op1->size -->| |
222:+ ^
223:+ delete_op_node->key + op1->size -+
在 2025/6/13 下午11:15, WANG Xuerui 写道:
> From: WANG Xuerui <git@xen0n.name>
>
> Previously, memmove and reloc/symbol adjustments happened at each
> loongarch_relax_delete_bytes() call, which is O(n^2) time complexity and
> leads to unacceptable (multiple hours) linking times for certain inputs
> with huge number of relaxable sites -- see the linked issue for details.
>
> To get rid of the quadratic behavior, defer all delete ops to the end of
> each relax trip, with the buffer implemented with the splay tree from
> libiberty. The individual relaxation handlers are converted to handle
> symbol values and relocation offsets as if all preceding deletions
> actually happened; because accesses during a relaxation trip are mostly
> sequential accesses, the splay tree should be efficient. The relaxation
> behavior should remain unchanged and has been confirmed with manually
> inserted debug logs.
>
> Example running times before and after the change with the test case in
> the linked issue (mypy transpiled C), cross-linking on Threadripper
> 3990X:
> Before: 4192.80s user 1.09s system 98% cpu 1:10:53.52 total
> After: 1.76s user 0.74s system 98% cpu 2.539 total - ~1/2382 the time!
>
> Also tested with binutils (bootstrapping self), CPython 3.14 and LLVM
> 20.1.6; all passed the respective test suites.
>
> Link: https://github.com/loongson-community/discussions/issues/56
> Signed-off-by: WANG Xuerui <git@xen0n.name>
> ---
> bfd/elfnn-loongarch.c | 316 +++++++++++++++++++++++++++++++++++-------
> 1 file changed, 266 insertions(+), 50 deletions(-)
>
> diff --git a/bfd/elfnn-loongarch.c b/bfd/elfnn-loongarch.c
> index 5c504f2cad8..15f10d678ed 100644
> --- a/bfd/elfnn-loongarch.c
> +++ b/bfd/elfnn-loongarch.c
> @@ -25,6 +25,7 @@
> #define ARCH_SIZE NN
> #include "elf-bfd.h"
> #include "objalloc.h"
> +#include "splay-tree.h"
> #include "elf/loongarch.h"
> #include "elfxx-loongarch.h"
> #include "opcode/loongarch.h"
> @@ -134,6 +135,10 @@ struct loongarch_elf_link_hash_table
> a partially updated state (some sections have vma updated but the
> others do not), and it's unsafe to do the normal relaxation. */
> bool layout_mutating_for_relr;
> +
> + /* Pending relaxation (byte deletion) operations meant for roughly
> + sequential access. */
> + splay_tree pending_delete_ops;
> };
>
> struct loongarch_elf_section_data
> @@ -4724,12 +4729,128 @@ loongarch_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
> return !fatal;
> }
>
> -static bool
> +/* A pending delete op during a linker relaxation trip, to be stored in a
> + splay tree.
> + The key is the starting offset of this op's deletion range, interpreted
> + as if no delete op is executed for this trip. */
> +struct pending_delete_op
> +{
> + /* Number of bytes to delete at the address. */
> + bfd_size_type size;
> +
> + /* The total offset adjustment at the address as if all preceding delete
> + ops had been executed. Used to calculate expected addresses after
> + relaxation without actually adjusting anything. */
> + bfd_size_type cumulative_offset;
> +};
> +
> +static int
> +pending_delete_op_compare (splay_tree_key a, splay_tree_key b)
> +{
> + bfd_vma off_a = (bfd_vma)a;
> + bfd_vma off_b = (bfd_vma)b;
> +
> + if (off_a < off_b)
> + return -1;
> + else if (off_a > off_b)
> + return 1;
> + else
> + return 0;
> +}
> +
> +static splay_tree
> +pending_delete_ops_new (void)
> +{
> + /* The node values are allocated with bfd_zalloc, so they are automatically
> + taken care of at BFD release time. */
> + return splay_tree_new (pending_delete_op_compare, NULL, NULL);
> +}
> +
> +static bfd_size_type
> +loongarch_calc_relaxed_addr (struct bfd_link_info *info, bfd_vma offset)
> +{
> + struct loongarch_elf_link_hash_table *htab = loongarch_elf_hash_table (info);
> + splay_tree pending_delete_ops = htab->pending_delete_ops;
> + struct pending_delete_op *op;
> + splay_tree_node node;
> +
> + BFD_ASSERT (pending_delete_ops != NULL);
> +
> + /* Find the op that starts just before the given address. */
> + node = splay_tree_predecessor (pending_delete_ops, (splay_tree_key)offset);
> + if (node == NULL)
> + /* Nothing has been deleted yet. */
> + return offset;
> + BFD_ASSERT (((bfd_vma)node->key) < offset);
> + op = (struct pending_delete_op *)node->value;
> +
> + /* If offset is inside this op's range, it is actually one of the deleted
> + bytes, so the adjusted node->key should be returned in this case. */
> + bfd_vma op_end_off = (bfd_vma)node->key + op->size;
> + if (offset < op_end_off)
> + {
> + offset = (bfd_vma)node->key;
> + node = splay_tree_predecessor (pending_delete_ops, node->key);
> + op = node ? (struct pending_delete_op *)node->value : NULL;
> + }
> +
> + return offset - (op ? op->cumulative_offset : 0);
> +}
> +
> +static void
> loongarch_relax_delete_bytes (bfd *abfd,
> - asection *sec,
> 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);
> + splay_tree pending_delete_ops = htab->pending_delete_ops;
> + splay_tree_node node;
> + struct pending_delete_op *op = NULL, *new_op = NULL;
> + bool need_new_node = true;
> +
> + if (count == 0)
> + return;
> +
> + BFD_ASSERT (pending_delete_ops != NULL);
> +
> + node = splay_tree_predecessor (pending_delete_ops, addr);
> + if (node)
> + {
> + op = (struct pending_delete_op *)node->value;
> + if ((bfd_vma)node->key + op->size >= addr)
> + {
> + /* The previous op already covers this offset, coalesce the new op
> + into it. */
> + op->size += count;
> + op->cumulative_offset += count;
> + need_new_node = false;
> + }
> + }
> +
> + if (need_new_node)
> + {
> + new_op = bfd_zalloc (abfd, sizeof (struct pending_delete_op));
> + new_op->size = count;
> + new_op->cumulative_offset = (op ? op->cumulative_offset : 0) + count;
> + node = splay_tree_insert (pending_delete_ops, (splay_tree_key)addr,
> + (splay_tree_value)new_op);
> + }
> +
> + /* Adjust all cumulative offsets after this op. */
> + for (node = splay_tree_successor (pending_delete_ops, node->key);
> + node != NULL;
> + node = splay_tree_successor (pending_delete_ops, node->key))
> + {
> + op = (struct pending_delete_op *)node->value;
> + op->cumulative_offset += count;
> + }
> +}
> +
> +static bool
> +loongarch_relax_perform_deletes (bfd *abfd, asection *sec,
> + struct bfd_link_info *link_info)
> {
> unsigned int i, symcount;
> bfd_vma toaddr = sec->size;
> @@ -4737,30 +4858,73 @@ loongarch_relax_delete_bytes (bfd *abfd,
> Elf_Internal_Shdr *symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
> unsigned int sec_shndx = _bfd_elf_section_from_bfd_section (abfd, sec);
> struct bfd_elf_section_data *data = elf_section_data (sec);
> - bfd_byte *contents = data->this_hdr.contents;
> + bfd_byte *contents = data->this_hdr.contents, *contents_end = NULL;
> struct relr_entry *relr = loongarch_elf_section_data (sec)->relr;
> struct loongarch_elf_link_hash_table *htab =
> loongarch_elf_hash_table (link_info);
> struct relr_entry *relr_end = NULL;
> + splay_tree pending_delete_ops = htab->pending_delete_ops;
> + splay_tree_node delete_op_node = NULL, next = NULL;
>
> if (htab->relr_count)
> relr_end = htab->relr + htab->relr_count;
>
> - /* Actually delete the bytes. */
> - sec->size -= count;
> - memmove (contents + addr, contents + addr + count, toaddr - addr - count);
> + BFD_ASSERT (pending_delete_ops != NULL);
> + delete_op_node = splay_tree_min (pending_delete_ops);
> +
> + if (delete_op_node == NULL)
> + /* No pending delete ops, nothing to do. */
> + return true;
> +
> + /* Actually delete the bytes. For each delete op the pointer arithmetics look
> + like this:
> +
> + delete_op_node->key -+ +- next->key
> + |<-- op1->size -->| |
> + v v v
> + ...------------------------xxxxxxxxxxxxxxxxxxx---------xxxxxxxxxxx-----...
> + ^
> + delete_op_node->key + op1->size -+
> +
> + For memmove, we need to translate offsets to pointers by adding them to
> + `contents`. */
> + for (; delete_op_node; delete_op_node = next)
> + {
> + struct pending_delete_op *op1
> + = (struct pending_delete_op *)delete_op_node->value;
> + bfd_vma op1_start_off = (bfd_vma)delete_op_node->key;
> + bfd_vma op1_end_off = op1_start_off + op1->size;
> + next = splay_tree_successor (pending_delete_ops, delete_op_node->key);
> + bfd_vma op2_start_off = next ? (bfd_vma)next->key : toaddr;
> + bfd_size_type count = op2_start_off - op1_end_off;
> +
> + if (count)
> + {
> + if (contents_end == NULL)
> + /* Start from the end of the first unmodified content chunk. */
> + contents_end = contents + op1_start_off;
> +
> + memmove (contents_end, contents + op1_end_off, count);
> + contents_end += count;
> + }
> +
> + /* Adjust the section size once, when we have reached the end. */
> + if (next == NULL)
> + sec->size -= op1->cumulative_offset;
> + }
>
> /* Adjust the location of all of the relocs. Note that we need not
> adjust the addends, since all PC-relative references must be against
> symbols, which we will adjust below. */
> for (i = 0; i < sec->reloc_count; i++)
> - if (data->relocs[i].r_offset > addr && data->relocs[i].r_offset < toaddr)
> - data->relocs[i].r_offset -= count;
> + if (data->relocs[i].r_offset < toaddr)
> + data->relocs[i].r_offset = loongarch_calc_relaxed_addr (
> + link_info, data->relocs[i].r_offset);
>
> /* Likewise for relative relocs to be packed into .relr. */
> for (; relr && relr < relr_end && relr->sec == sec; relr++)
> - if (relr->off > addr && relr->off < toaddr)
> - relr->off -= count;
> + if (relr->off < toaddr)
> + relr->off = loongarch_calc_relaxed_addr (link_info, relr->off);
>
> /* Adjust the local symbols defined in this section. */
> for (i = 0; i < symtab_hdr->sh_info; i++)
> @@ -4768,24 +4932,33 @@ loongarch_relax_delete_bytes (bfd *abfd,
> Elf_Internal_Sym *sym = (Elf_Internal_Sym *) symtab_hdr->contents + i;
> if (sym->st_shndx == sec_shndx)
> {
> - /* If the symbol is in the range of memory we just moved, we
> - have to adjust its value. */
> - if (sym->st_value > addr && sym->st_value <= toaddr)
> - sym->st_value -= count;
> + bfd_vma orig_value = sym->st_value;
> + if (orig_value <= toaddr)
> + sym->st_value
> + = loongarch_calc_relaxed_addr (link_info, orig_value);
>
> - /* If the symbol *spans* the bytes we just deleted (i.e. its
> - *end* is in the moved bytes but its *start* isn't), then we
> - must adjust its size.
> + /* If the symbol *spans* some deleted bytes (i.e. its *end* is in
> + the moved bytes but its *start* isn't), then we must adjust its
> + size.
>
> This test needs to use the original value of st_value, otherwise
> we might accidentally decrease size when deleting bytes right
> - before the symbol. But since deleted relocs can't span across
> - symbols, we can't have both a st_value and a st_size decrease,
> - so it is simpler to just use an else. */
> - else if (sym->st_value <= addr
> - && sym->st_value + sym->st_size > addr
> - && sym->st_value + sym->st_size <= toaddr)
> - sym->st_size -= count;
> + before the symbol. */
> + bfd_vma sym_end = orig_value + sym->st_size;
> + if (sym_end <= toaddr)
> + {
> + splay_tree_node node = splay_tree_predecessor (
> + pending_delete_ops, (splay_tree_key)sym_end);
> + if (node)
> + {
> + bfd_vma addr = (bfd_vma)node->key;
> + struct pending_delete_op *op
> + = (struct pending_delete_op *)node->value;
> +
> + if (orig_value <= addr && sym_end > addr)
> + sym->st_size -= op->size;
> + }
> + }
> }
> }
>
> @@ -4830,16 +5003,29 @@ loongarch_relax_delete_bytes (bfd *abfd,
> || sym_hash->root.type == bfd_link_hash_defweak)
> && sym_hash->root.u.def.section == sec)
> {
> - /* As above, adjust the value if needed. */
> - if (sym_hash->root.u.def.value > addr
> - && sym_hash->root.u.def.value <= toaddr)
> - sym_hash->root.u.def.value -= count;
> + bfd_vma orig_value = sym_hash->root.u.def.value;
> +
> + /* As above, adjust the value. */
> + if (orig_value <= toaddr)
> + sym_hash->root.u.def.value
> + = loongarch_calc_relaxed_addr (link_info, orig_value);
>
> /* As above, adjust the size if needed. */
> - else if (sym_hash->root.u.def.value <= addr
> - && sym_hash->root.u.def.value + sym_hash->size > addr
> - && sym_hash->root.u.def.value + sym_hash->size <= toaddr)
> - sym_hash->size -= count;
> + bfd_vma sym_end = orig_value + sym_hash->size;
> + if (sym_end <= toaddr)
> + {
> + splay_tree_node node = splay_tree_predecessor (
> + pending_delete_ops, (splay_tree_key)sym_end);
> + if (node)
> + {
> + bfd_vma addr = (bfd_vma)node->key;
> + struct pending_delete_op *op
> + = (struct pending_delete_op *)node->value;
> +
> + if (orig_value <= addr && sym_end > addr)
> + sym_hash->size -= op->size;
> + }
> + }
> }
> }
>
> @@ -4919,7 +5105,7 @@ loongarch_tls_perform_trans (bfd *abfd, asection *sec,
> 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, sec, rel->r_offset, 4, info);
> + loongarch_relax_delete_bytes (abfd, rel->r_offset, 4, info);
> return true;
>
> case R_LARCH_TLS_IE_PC_HI20:
> @@ -5008,8 +5194,7 @@ loongarch_tls_perform_trans (bfd *abfd, asection *sec,
> lu52i.d $rd,$rd,%le64_hi12(sym) => (deleted)
> */
> static bool
> -loongarch_relax_tls_le (bfd *abfd, asection *sec,
> - asection *sym_sec ATTRIBUTE_UNUSED,
> +loongarch_relax_tls_le (bfd *abfd, asection *sec, asection *sym_sec,
> Elf_Internal_Rela *rel, bfd_vma symval,
> struct bfd_link_info *link_info,
> bool *agin ATTRIBUTE_UNUSED,
> @@ -5019,6 +5204,8 @@ loongarch_relax_tls_le (bfd *abfd, asection *sec,
> uint32_t insn = bfd_get (32, abfd, contents + rel->r_offset);
> static uint32_t insn_rj,insn_rd;
> symval = symval - elf_hash_table (link_info)->tls_sec->vma;
> + if (sym_sec == sec)
> + symval = loongarch_calc_relaxed_addr(link_info, symval);
> /* The old LE instruction sequence can be relaxed when the symbol offset
> is smaller than the 12-bit range. */
> if (symval <= 0xfff)
> @@ -5033,7 +5220,7 @@ loongarch_relax_tls_le (bfd *abfd, asection *sec,
> if (symval < 0x800)
> {
> rel->r_info = ELFNN_R_INFO (0, R_LARCH_NONE);
> - loongarch_relax_delete_bytes (abfd, sec, rel->r_offset,
> + loongarch_relax_delete_bytes (abfd, rel->r_offset,
> 4, link_info);
> }
> break;
> @@ -5058,7 +5245,7 @@ loongarch_relax_tls_le (bfd *abfd, asection *sec,
> 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, sec, rel->r_offset,
> + loongarch_relax_delete_bytes (abfd, rel->r_offset,
> 4, link_info);
> break;
>
> @@ -5116,7 +5303,11 @@ loongarch_relax_pcala_addi (bfd *abfd, asection *sec, asection *sym_sec,
> size_input_section already took care of updating it after relaxation,
> so we additionally update once here. */
> sec->output_offset = sec->output_section->size;
> - bfd_vma pc = sec_addr (sec) + rel_hi->r_offset;
> + bfd_vma pc = sec_addr (sec)
> + + loongarch_calc_relaxed_addr (info, rel_hi->r_offset);
> + if (sym_sec == sec)
> + 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,
> @@ -5155,7 +5346,7 @@ loongarch_relax_pcala_addi (bfd *abfd, asection *sec, asection *sym_sec,
> R_LARCH_PCREL20_S2);
> rel_lo->r_info = ELFNN_R_INFO (0, R_LARCH_NONE);
>
> - loongarch_relax_delete_bytes (abfd, sec, rel_lo->r_offset, 4, info);
> + loongarch_relax_delete_bytes (abfd, rel_lo->r_offset, 4, info);
>
> return true;
> }
> @@ -5177,7 +5368,11 @@ loongarch_relax_call36 (bfd *abfd, asection *sec, asection *sym_sec,
> size_input_section already took care of updating it after relaxation,
> so we additionally update once here. */
> sec->output_offset = sec->output_section->size;
> - bfd_vma pc = sec_addr (sec) + rel->r_offset;
> + bfd_vma pc = sec_addr (sec)
> + + loongarch_calc_relaxed_addr (info, rel->r_offset);
> + if (sym_sec == sec)
> + 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,
> @@ -5211,7 +5406,7 @@ loongarch_relax_call36 (bfd *abfd, asection *sec, asection *sym_sec,
> /* 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, sec, rel->r_offset + 4, 4, info);
> + loongarch_relax_delete_bytes (abfd, rel->r_offset + 4, 4, info);
> return true;
> }
>
> @@ -5237,7 +5432,11 @@ loongarch_relax_pcala_ld (bfd *abfd, asection *sec,
> size_input_section already took care of updating it after relaxation,
> so we additionally update once here. */
> sec->output_offset = sec->output_section->size;
> - bfd_vma pc = sec_addr (sec) + rel_hi->r_offset;
> + bfd_vma pc = sec_addr (sec)
> + + loongarch_calc_relaxed_addr(info, rel_hi->r_offset);
> + if (sym_sec == sec)
> + 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,
> @@ -5287,7 +5486,7 @@ bfd_elfNN_loongarch_set_data_segment_info (struct bfd_link_info *info,
> static bool
> loongarch_relax_align (bfd *abfd, asection *sec, asection *sym_sec,
> Elf_Internal_Rela *rel,
> - bfd_vma symval ATTRIBUTE_UNUSED,
> + bfd_vma symval,
> struct bfd_link_info *link_info,
> bool *again ATTRIBUTE_UNUSED,
> bfd_vma max_alignment ATTRIBUTE_UNUSED)
> @@ -5303,6 +5502,10 @@ loongarch_relax_align (bfd *abfd, asection *sec, asection *sym_sec,
> else
> alignment = rel->r_addend + 4;
>
> + if (sym_sec == sec)
> + symval = sec_addr (sec)
> + + loongarch_calc_relaxed_addr (link_info, symval - sec_addr (sec));
> +
> addend = alignment - 4; /* The bytes of NOPs added by R_LARCH_ALIGN. */
> symval -= addend; /* The address of first NOP added by R_LARCH_ALIGN. */
> bfd_vma aligned_addr = ((symval - 1) & ~(alignment - 1)) + alignment;
> @@ -5328,17 +5531,19 @@ loongarch_relax_align (bfd *abfd, asection *sec, asection *sym_sec,
> /* If skipping more bytes than the specified maximum,
> then the alignment is not done at all and delete all NOPs. */
> if (max > 0 && need_nop_bytes > max)
> - return loongarch_relax_delete_bytes (abfd, sec, rel->r_offset,
> - addend, link_info);
> + {
> + loongarch_relax_delete_bytes (abfd, rel->r_offset, addend, link_info);
> + return true;
> + }
>
> /* If the number of NOPs is already correct, there's nothing to do. */
> if (need_nop_bytes == addend)
> return true;
>
> /* Delete the excess NOPs. */
> - return loongarch_relax_delete_bytes (abfd, sec,
> - rel->r_offset + need_nop_bytes,
> - addend - need_nop_bytes, link_info);
> + loongarch_relax_delete_bytes (abfd, rel->r_offset + need_nop_bytes,
> + addend - need_nop_bytes, link_info);
> + return true;
> }
>
> /* Relax pcalau12i + addi.d of TLS LD/GD/DESC to pcaddi. */
> @@ -5359,7 +5564,11 @@ loongarch_relax_tls_ld_gd_desc (bfd *abfd, asection *sec, asection *sym_sec,
> size_input_section already took care of updating it after relaxation,
> so we additionally update once here. */
> sec->output_offset = sec->output_section->size;
> - bfd_vma pc = sec_addr (sec) + rel_hi->r_offset;
> + bfd_vma pc = sec_addr (sec)
> + + loongarch_calc_relaxed_addr (info, rel_hi->r_offset);
> + if (sym_sec == sec)
> + 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,
> @@ -5414,7 +5623,7 @@ loongarch_relax_tls_ld_gd_desc (bfd *abfd, asection *sec, asection *sym_sec,
> }
> rel_lo->r_info = ELFNN_R_INFO (0, R_LARCH_NONE);
>
> - loongarch_relax_delete_bytes (abfd, sec, rel_lo->r_offset, 4, info);
> + loongarch_relax_delete_bytes (abfd, rel_lo->r_offset, 4, info);
>
> return true;
> }
> @@ -5502,6 +5711,9 @@ loongarch_elf_relax_section (bfd *abfd, asection *sec,
> htab->max_alignment = max_alignment;
> }
>
> + splay_tree pending_delete_ops = pending_delete_ops_new ();
> + htab->pending_delete_ops = pending_delete_ops;
> +
> for (unsigned int i = 0; i < sec->reloc_count; i++)
> {
> char symtype;
> @@ -5737,6 +5949,10 @@ loongarch_elf_relax_section (bfd *abfd, asection *sec,
> info, again, max_alignment);
> }
>
> + loongarch_relax_perform_deletes (abfd, sec, info);
> + htab->pending_delete_ops = NULL;
> + splay_tree_delete (pending_delete_ops);
> +
> return true;
> }
>
More information about the Binutils
mailing list