[PATCH v3] LoongArch: Allow to relax instructions into NOPs after handling alignment

Lulu Cai cailulu@loongson.cn
Mon Jul 7 10:12:13 GMT 2025


Thanks for your contribution, the patch has been applied.

On 7/6/25 9:06 AM, WANG Xuerui wrote:
> From: WANG Xuerui <git@xen0n.name>
>
> Right now, LoongArch linker relaxation is 2-pass, since after alignment
> is done, byte deletion can no longer happen. However, as the alignment
> pass also shrinks text sections, new relaxation chances may well be
> created after alignment is done. Although at this point we can no longer
> delete unused instructions without disturbing alignment, we can still
> replace them with NOPs; popular LoongArch micro-architectures can
> eliminate NOPs during execution, so we can expect a (very) slight
> performance improvement from those late-created relaxation chances.
>
> To achieve this, the number of relax passes is raised to 3 for
> LoongArch, and every relaxation handler except loongarch_relax_align is
> migrated to a new helper loongarch_relax_delete_or_nop, that either
> deletes bytes or fills the bytes to be "deleted" with NOPs, depending on
> whether the containing section already has undergone alignment. Also,
> since no byte can be deleted during this relax pass, in the pass the
> pending_delete_ops structure is no longer allocated, and
> loongarch_calc_relaxed_addr(x) degrades to the trivial "return x" in
> this case.
>
> In addition, previously when calculating distances to symbols, an
> extra segment alignment must be considered, because alignment may
> increase distance between sites. However in the newly added 3rd pass
> code size can no longer increase for "closed" sections, so we can skip
> the adjustment for them to allow for a few more relaxation chances.
>
> A simple way to roughly measure this change's effectiveness is to check
> how many pcalau12i + addi.d pairs are relaxed into pcaddi's. Taking a
> Firefox 140.0.2 test build of mine as an example:
>
> Before: 47842 pcaddi's in libxul.so
> After: 48089
>
> This is a 0.5% increase, which is kind of acceptable for a peephole
> optimization like this; of which 9 are due to the "relax"ed symbol
> distance treatment.
>
> Signed-off-by: WANG Xuerui <git@xen0n.name>
> ---
>
> Changes from v2:
>
> - Style and wording fixes; suggested by Qinggang.
>
> Changes from v1:
>
> - No longer adjust max_alignment when calculating distances between
>    symbols in different segments during relax pass 2 (3rd pass);
>    suggested by Ruoyao.
> - Improved comments and the test case.
>
>   bfd/elfnn-loongarch.c                         | 218 ++++++++++++------
>   ld/emultempl/loongarchelf.em                  |   2 +-
>   .../ld-loongarch-elf/ld-loongarch-elf.exp     |   1 +
>   .../ld-loongarch-elf/relax-after-alignment.d  |  30 +++
>   .../ld-loongarch-elf/relax-after-alignment.s  |  49 ++++
>   5 files changed, 227 insertions(+), 73 deletions(-)
>   create mode 100644 ld/testsuite/ld-loongarch-elf/relax-after-alignment.d
>   create mode 100644 ld/testsuite/ld-loongarch-elf/relax-after-alignment.s
>
> diff --git a/bfd/elfnn-loongarch.c b/bfd/elfnn-loongarch.c
> index a480a709897..c75f5206f2e 100644
> --- a/bfd/elfnn-loongarch.c
> +++ b/bfd/elfnn-loongarch.c
> @@ -173,6 +173,10 @@ loongarch_elf_new_section_hook (bfd *abfd, asection *sec)
>   #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)
> @@ -4789,7 +4793,10 @@ loongarch_calc_relaxed_addr (struct bfd_link_info *info, bfd_vma 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);
> @@ -4814,9 +4821,9 @@ loongarch_calc_relaxed_addr (struct bfd_link_info *info, bfd_vma 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);
> @@ -4867,6 +4874,34 @@ loongarch_relax_delete_bytes (bfd *abfd,
>       }
>   }
>   
> +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)
> @@ -5135,7 +5170,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, 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:
> @@ -5250,7 +5285,7 @@ loongarch_relax_tls_le (bfd *abfd, asection *sec, asection *sym_sec,
>   	    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;
> @@ -5275,8 +5310,8 @@ loongarch_relax_tls_le (bfd *abfd, asection *sec, asection *sym_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, 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:
> @@ -5339,17 +5374,22 @@ loongarch_relax_pcala_addi (bfd *abfd, asection *sec, asection *sym_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,
> -					       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;
>   
> @@ -5376,7 +5416,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, rel_lo->r_offset, 4, info);
> +  loongarch_relax_delete_or_nop (abfd, sec, rel_lo->r_offset, 4, info);
>   
>     return true;
>   }
> @@ -5404,17 +5444,22 @@ loongarch_relax_call36 (bfd *abfd, asection *sec, asection *sym_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,
> -					       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)
> @@ -5436,7 +5481,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, rel->r_offset + 4, 4, info);
> +  loongarch_relax_delete_or_nop (abfd, sec, rel->r_offset + 4, 4, info);
>     return true;
>   }
>   
> @@ -5468,17 +5513,22 @@ loongarch_relax_pcala_ld (bfd *abfd, asection *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,
> -					       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)
> @@ -5511,8 +5561,9 @@ bfd_elfNN_loongarch_set_data_segment_info (struct bfd_link_info *info,
>       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,
> @@ -5553,9 +5604,9 @@ loongarch_relax_align (bfd *abfd, asection *sec, asection *sym_sec,
>         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,
> @@ -5600,17 +5651,22 @@ loongarch_relax_tls_ld_gd_desc (bfd *abfd, asection *sec, asection *sym_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,
> -					       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;
>   
> @@ -5653,7 +5709,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, rel_lo->r_offset, 4, info);
> +  loongarch_relax_delete_or_nop (abfd, sec, rel_lo->r_offset, 4, info);
>   
>     return true;
>   }
> @@ -5697,15 +5753,25 @@ loongarch_elf_relax_section (bfd *abfd, asection *sec,
>     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);
> @@ -5741,7 +5807,10 @@ loongarch_elf_relax_section (bfd *abfd, asection *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++)
> @@ -5783,7 +5852,13 @@ loongarch_elf_relax_section (bfd *abfd, asection *sec,
>   	}
>   
>         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)
>   	    {
> @@ -5837,10 +5912,6 @@ loongarch_elf_relax_section (bfd *abfd, asection *sec,
>   		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.
> @@ -5979,9 +6050,12 @@ 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 (pdops);
> +  if (pdops)
> +    {
> +      loongarch_relax_perform_deletes (abfd, sec, info);
> +      htab->pending_delete_ops = NULL;
> +      splay_tree_delete (pdops);
> +    }
>   
>     return true;
>   }
> diff --git a/ld/emultempl/loongarchelf.em b/ld/emultempl/loongarchelf.em
> index 928fd83ab15..517ece16105 100644
> --- a/ld/emultempl/loongarchelf.em
> +++ b/ld/emultempl/loongarchelf.em
> @@ -58,7 +58,7 @@ larch_elf_before_allocation (void)
>   	ENABLE_RELAXATION;
>       }
>   
> -  link_info.relax_pass = 2;
> +  link_info.relax_pass = 3;
>   }
>   
>   static void
> diff --git a/ld/testsuite/ld-loongarch-elf/ld-loongarch-elf.exp b/ld/testsuite/ld-loongarch-elf/ld-loongarch-elf.exp
> index 2f09a69befb..e23cdc87161 100644
> --- a/ld/testsuite/ld-loongarch-elf/ld-loongarch-elf.exp
> +++ b/ld/testsuite/ld-loongarch-elf/ld-loongarch-elf.exp
> @@ -45,6 +45,7 @@ if [istarget "loongarch64-*-*"] {
>       run_dump_test "underflow_s_5_20"
>       run_dump_test "tls-le-norelax"
>       run_dump_test "tls-le-relax"
> +    run_dump_test "relax-after-alignment"
>       run_dump_test "relax-medium-call"
>       run_dump_test "relax-medium-call-1"
>       run_dump_test "check_got_relax"
> diff --git a/ld/testsuite/ld-loongarch-elf/relax-after-alignment.d b/ld/testsuite/ld-loongarch-elf/relax-after-alignment.d
> new file mode 100644
> index 00000000000..844c518e9c7
> --- /dev/null
> +++ b/ld/testsuite/ld-loongarch-elf/relax-after-alignment.d
> @@ -0,0 +1,30 @@
> +#name: additional relaxation chances after alignment processing
> +#as:
> +#ld: --defsym _start=0
> +#objdump: -d --no-show-raw-insn
> +
> +.*:\s+file format .*
> +
> +
> +Disassembly of section \.text:
> +
> +0000000120000400 <before>:
> +\s*120000400:\s+pcalau12i\s+\$t0, 512
> +\s*[0-9a-f]+:\s+addi\.d\s+\$t0, \$t0, 1024
> +\s*[0-9a-f]+:\s+pcaddi\s+\$t0, 524286
> +\s*[0-9a-f]+:\s+nop\s*
> +\s*\.\.\.
> +\s*120000c00:\s+pcaddi\s+\$t0, 523776
> +\s*\.\.\.
> +
> +0000000120200400 <target>:
> +\s*120200400:\s+break\s+0x0
> +
> +0000000120200404 <after>:
> +\s*\.\.\.
> +\s*1203ffbfc:\s+pcaddi\s+\$t0, -523775
> +\s*\.\.\.
> +\s*120400400:\s+pcaddi\s+\$t0, -524288
> +\s*[0-9a-f]+:\s+nop\s*
> +\s*[0-9a-f]+:\s+pcalau12i\s+\$t0, -512
> +\s*[0-9a-f]+:\s+addi\.d\s+\$t0, \$t0, 1024
> diff --git a/ld/testsuite/ld-loongarch-elf/relax-after-alignment.s b/ld/testsuite/ld-loongarch-elf/relax-after-alignment.s
> new file mode 100644
> index 00000000000..24d29ecb257
> --- /dev/null
> +++ b/ld/testsuite/ld-loongarch-elf/relax-after-alignment.s
> @@ -0,0 +1,49 @@
> +# 0x0 pre-relax, 0x400 post-relax
> +# all addresses are additionally offset by 0x120000000 without `ld -shared`
> +before:
> +    la.pcrel $t0, target  # too far; should stay as pcalau12i + addi.d
> +    la.pcrel $t0, target  # furthest reach of relax pass 2
> +
> +# 0x10 pre-relax, 0x410 post-relax
> +.rept 508
> +.word 0
> +.endr
> +
> +# 0x800 {pre,post}-relax
> +# 255 nops + R_LARCH_ALIGN before relaxation
> +# none of the aligning nops should remain after relax pass 1
> +.p2align 10
> +
> +# 0xbfc pre-relax, 0xc00 post-relax
> +    la.pcrel $t0, target  # should become single pcaddi in relax pass 0
> +
> +# 0xc04 {pre,post}-relax
> +.rept 523775
> +.word 0
> +.endr
> +
> +# 0x200400 {pre,post}-relax
> +target:
> +    break 0
> +
> +# 0x200404 {pre,post}-relax
> +after:
> +.rept 523774
> +.word 0
> +.endr
> +
> +# 0x3ffbfc {pre,post}-relax
> +    la.pcrel $t0, target  # should become single pcaddi in relax pass 0
> +
> +# 255 nops + R_LARCH_ALIGN before relaxation
> +# none of the aligning nops should remain after relax pass 1
> +.p2align 10
> +
> +# 0x400000 pre-relax, 0x3ffc00 post-relax
> +.rept 512
> +.word 0
> +.endr
> +
> +# 0x400800 pre-relax, 0x400400 post-relax
> +    la.pcrel $t0, target  # furthest reach of relax pass 2
> +    la.pcrel $t0, target  # too far; should stay as pcalau12i + addi.d




More information about the Binutils mailing list