RISC-V: Optimize lui and auipc relaxations for undefweak symbol.
[deliverable/binutils-gdb.git] / bfd / elfnn-riscv.c
index ccf904af1bcc8c339f02945f8cfbac7f20ad7a88..4ffe6a36e68f6c6c8e2301627dceedfef908f2db 100644 (file)
@@ -3319,6 +3319,7 @@ struct riscv_pcgp_hi_reloc
   bfd_vma hi_addr;
   unsigned hi_sym;
   asection *sym_sec;
+  bfd_boolean undefined_weak;
   riscv_pcgp_hi_reloc *next;
 };
 
@@ -3377,7 +3378,8 @@ riscv_free_pcgp_relocs (riscv_pcgp_relocs *p,
 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)
+                           unsigned hi_sym, asection *sym_sec,
+                           bfd_boolean undefined_weak)
 {
   riscv_pcgp_hi_reloc *new = bfd_malloc (sizeof(*new));
   if (!new)
@@ -3387,6 +3389,7 @@ riscv_record_pcgp_hi_reloc (riscv_pcgp_relocs *p, bfd_vma hi_sec_off,
   new->hi_addr = hi_addr;
   new->hi_sym = hi_sym;
   new->sym_sec = sym_sec;
+  new->undefined_weak = undefined_weak;
   new->next = p->hi;
   p->hi = new;
   return TRUE;
@@ -3439,7 +3442,8 @@ typedef bfd_boolean (*relax_func_t) (bfd *, asection *, asection *,
                                     struct bfd_link_info *,
                                     Elf_Internal_Rela *,
                                     bfd_vma, bfd_vma, bfd_vma, bfd_boolean *,
-                                    riscv_pcgp_relocs *);
+                                    riscv_pcgp_relocs *,
+                                    bfd_boolean undefined_weak);
 
 /* Relax AUIPC + JALR into JAL.  */
 
@@ -3451,7 +3455,8 @@ _bfd_riscv_relax_call (bfd *abfd, asection *sec, asection *sym_sec,
                       bfd_vma max_alignment,
                       bfd_vma reserve_size ATTRIBUTE_UNUSED,
                       bfd_boolean *again,
-                      riscv_pcgp_relocs *pcgp_relocs ATTRIBUTE_UNUSED)
+                      riscv_pcgp_relocs *pcgp_relocs ATTRIBUTE_UNUSED,
+                      bfd_boolean undefined_weak ATTRIBUTE_UNUSED)
 {
   bfd_byte *contents = elf_section_data (sec)->this_hdr.contents;
   bfd_signed_vma foff = symval - (sec_addr (sec) + rel->r_offset);
@@ -3539,7 +3544,8 @@ _bfd_riscv_relax_lui (bfd *abfd,
                      bfd_vma max_alignment,
                      bfd_vma reserve_size,
                      bfd_boolean *again,
-                     riscv_pcgp_relocs *pcgp_relocs ATTRIBUTE_UNUSED)
+                     riscv_pcgp_relocs *pcgp_relocs ATTRIBUTE_UNUSED,
+                     bfd_boolean undefined_weak)
 {
   bfd_byte *contents = elf_section_data (sec)->this_hdr.contents;
   bfd_vma gp = riscv_global_pointer_value (link_info);
@@ -3561,21 +3567,38 @@ _bfd_riscv_relax_lui (bfd *abfd,
 
   /* 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)))
+  if (undefined_weak
+      || (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 = ELFNN_R_SYM (rel->r_info);
       switch (ELFNN_R_TYPE (rel->r_info))
        {
        case R_RISCV_LO12_I:
-         rel->r_info = ELFNN_R_INFO (sym, R_RISCV_GPREL_I);
+         if (undefined_weak)
+           {
+             /* Change the RS1 to zero.  */
+             bfd_vma insn = bfd_get_32 (abfd, contents + rel->r_offset);
+             insn &= ~(OP_MASK_RS1 << OP_SH_RS1);
+             bfd_put_32 (abfd, insn, contents + rel->r_offset);
+           }
+         else
+           rel->r_info = ELFNN_R_INFO (sym, R_RISCV_GPREL_I);
          return TRUE;
 
        case R_RISCV_LO12_S:
-         rel->r_info = ELFNN_R_INFO (sym, R_RISCV_GPREL_S);
+         if (undefined_weak)
+           {
+             /* Change the RS1 to zero.  */
+             bfd_vma insn = bfd_get_32 (abfd, contents + rel->r_offset);
+             insn &= ~(OP_MASK_RS1 << OP_SH_RS1);
+             bfd_put_32 (abfd, insn, contents + rel->r_offset);
+           }
+         else
+           rel->r_info = ELFNN_R_INFO (sym, R_RISCV_GPREL_S);
          return TRUE;
 
        case R_RISCV_HI20:
@@ -3634,7 +3657,8 @@ _bfd_riscv_relax_tls_le (bfd *abfd,
                         bfd_vma max_alignment ATTRIBUTE_UNUSED,
                         bfd_vma reserve_size ATTRIBUTE_UNUSED,
                         bfd_boolean *again,
-                        riscv_pcgp_relocs *prcel_relocs ATTRIBUTE_UNUSED)
+                        riscv_pcgp_relocs *prcel_relocs ATTRIBUTE_UNUSED,
+                        bfd_boolean undefined_weak ATTRIBUTE_UNUSED)
 {
   /* See if this symbol is in range of tp.  */
   if (RISCV_CONST_HIGH_PART (tpoff (link_info, symval)) != 0)
@@ -3674,7 +3698,8 @@ _bfd_riscv_relax_align (bfd *abfd, asection *sec,
                        bfd_vma max_alignment ATTRIBUTE_UNUSED,
                        bfd_vma reserve_size ATTRIBUTE_UNUSED,
                        bfd_boolean *again ATTRIBUTE_UNUSED,
-                       riscv_pcgp_relocs *pcrel_relocs ATTRIBUTE_UNUSED)
+                       riscv_pcgp_relocs *pcrel_relocs ATTRIBUTE_UNUSED,
+                       bfd_boolean undefined_weak ATTRIBUTE_UNUSED)
 {
   bfd_byte *contents = elf_section_data (sec)->this_hdr.contents;
   bfd_vma alignment = 1, pos;
@@ -3732,8 +3757,10 @@ _bfd_riscv_relax_pc  (bfd *abfd ATTRIBUTE_UNUSED,
                      bfd_vma max_alignment,
                      bfd_vma reserve_size,
                      bfd_boolean *again ATTRIBUTE_UNUSED,
-                     riscv_pcgp_relocs *pcgp_relocs)
+                     riscv_pcgp_relocs *pcgp_relocs,
+                     bfd_boolean undefined_weak)
 {
+  bfd_byte *contents = elf_section_data (sec)->this_hdr.contents;
   bfd_vma gp = riscv_global_pointer_value (link_info);
 
   BFD_ASSERT (rel->r_offset + 4 <= sec->size);
@@ -3763,12 +3790,19 @@ _bfd_riscv_relax_pc  (bfd *abfd ATTRIBUTE_UNUSED,
        hi_reloc = *hi;
        symval = hi_reloc.hi_addr;
        sym_sec = hi_reloc.sym_sec;
+
+       /* We can not know whether the undefined weak symbol is referenced
+          according to the information of R_RISCV_PCREL_LO12_I/S.  Therefore,
+          we have to record the 'undefined_weak' flag when handling the
+          corresponding R_RISCV_HI20 reloc in riscv_record_pcgp_hi_reloc.  */
+       undefined_weak = hi_reloc.undefined_weak;
       }
       break;
 
     case R_RISCV_PCREL_HI20:
       /* Mergeable symbols and code might later move out of range.  */
-      if (sym_sec->flags & (SEC_MERGE | SEC_CODE))
+      if (! undefined_weak
+         && sym_sec->flags & (SEC_MERGE | SEC_CODE))
        return TRUE;
 
       /* If the cooresponding lo relocation has already been seen then it's not
@@ -3796,23 +3830,50 @@ _bfd_riscv_relax_pc  (bfd *abfd ATTRIBUTE_UNUSED,
 
   /* 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)))
+  if (undefined_weak
+      || (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;
+         if (undefined_weak)
+           {
+             /* Change the RS1 to zero, and then modify the relocation
+                type to R_RISCV_LO12_I.  */
+             bfd_vma insn = bfd_get_32 (abfd, contents + rel->r_offset);
+             insn &= ~(OP_MASK_RS1 << OP_SH_RS1);
+             bfd_put_32 (abfd, insn, contents + rel->r_offset);
+             rel->r_info = ELFNN_R_INFO (sym, R_RISCV_LO12_I);
+             rel->r_addend = hi_reloc.hi_addend;
+           }
+         else
+           {
+             rel->r_info = ELFNN_R_INFO (sym, R_RISCV_GPREL_I);
+             rel->r_addend += hi_reloc.hi_addend;
+           }
          return TRUE;
 
        case R_RISCV_PCREL_LO12_S:
-         rel->r_info = ELFNN_R_INFO (sym, R_RISCV_GPREL_S);
-         rel->r_addend += hi_reloc.hi_addend;
+         if (undefined_weak)
+           {
+             /* Change the RS1 to zero, and then modify the relocation
+                type to R_RISCV_LO12_S.  */
+             bfd_vma insn = bfd_get_32 (abfd, contents + rel->r_offset);
+             insn &= ~(OP_MASK_RS1 << OP_SH_RS1);
+             bfd_put_32 (abfd, insn, contents + rel->r_offset);
+             rel->r_info = ELFNN_R_INFO (sym, R_RISCV_LO12_S);
+             rel->r_addend = hi_reloc.hi_addend;
+           }
+         else
+           {
+             rel->r_info = ELFNN_R_INFO (sym, R_RISCV_GPREL_S);
+             rel->r_addend += hi_reloc.hi_addend;
+           }
          return TRUE;
 
        case R_RISCV_PCREL_HI20:
@@ -3821,7 +3882,8 @@ _bfd_riscv_relax_pc  (bfd *abfd ATTRIBUTE_UNUSED,
                                      rel->r_addend,
                                      symval,
                                      ELFNN_R_SYM(rel->r_info),
-                                     sym_sec);
+                                     sym_sec,
+                                     undefined_weak);
          /* We can delete the unnecessary AUIPC and reloc.  */
          rel->r_info = ELFNN_R_INFO (0, R_RISCV_DELETE);
          rel->r_addend = 4;
@@ -3847,7 +3909,8 @@ _bfd_riscv_relax_delete (bfd *abfd,
                         bfd_vma max_alignment ATTRIBUTE_UNUSED,
                         bfd_vma reserve_size ATTRIBUTE_UNUSED,
                         bfd_boolean *again ATTRIBUTE_UNUSED,
-                        riscv_pcgp_relocs *pcgp_relocs ATTRIBUTE_UNUSED)
+                        riscv_pcgp_relocs *pcgp_relocs ATTRIBUTE_UNUSED,
+                        bfd_boolean undefined_weak ATTRIBUTE_UNUSED)
 {
   if (!riscv_relax_delete_bytes(abfd, sec, rel->r_offset, rel->r_addend,
                                link_info))
@@ -3914,6 +3977,7 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec,
       int type = ELFNN_R_TYPE (rel->r_info);
       bfd_vma symval;
       char symtype;
+      bfd_boolean undefined_weak = FALSE;
 
       relax_func = NULL;
       if (info->relax_pass == 0)
@@ -4008,11 +4072,36 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec,
                 || h->root.type == bfd_link_hash_warning)
            h = (struct elf_link_hash_entry *) h->root.u.i.link;
 
+         if (h->root.type == bfd_link_hash_undefweak
+             && (relax_func == _bfd_riscv_relax_lui
+                 || relax_func == _bfd_riscv_relax_pc))
+           {
+             /* For the lui and auipc relaxations, since the symbol
+                value of an undefined weak symbol is always be zero,
+                we can optimize the patterns into a single LI/MV/ADDI
+                instruction.
+
+                Note that, creating shared libraries and pie output may
+                break the rule above.  Fortunately, since we do not relax
+                pc relocs when creating shared libraries and pie output,
+                and the absolute address access for R_RISCV_HI20 isn't
+                allowed when "-fPIC" is set, the problem of creating shared
+                libraries can not happen currently.  Once we support the
+                auipc relaxations when creating shared libraries, then we will
+                need the more rigorous checking for this optimization.  */
+             undefined_weak = TRUE;
+           }
+
          if (h->plt.offset != MINUS_ONE)
            {
              sym_sec = htab->elf.splt;
              symval = h->plt.offset;
            }
+         else if (undefined_weak)
+           {
+             symval = 0;
+             sym_sec = bfd_und_section_ptr;
+           }
          else if (h->root.u.def.section->output_section == NULL
                   || (h->root.type != bfd_link_hash_defined
                       && h->root.type != bfd_link_hash_defweak))
@@ -4065,7 +4154,7 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec,
 
       if (!relax_func (abfd, sec, sym_sec, info, rel, symval,
                       max_alignment, reserve_size, again,
-                      &pcgp_relocs))
+                      &pcgp_relocs, undefined_weak))
        goto fail;
     }
 
This page took 0.029533 seconds and 4 git commands to generate.