[RFC PATCH 2/5] RISC-V: Add Zcmt table-jump relaxation

Jiawei <[email protected]>
Newsgroups gmane.comp.gnu.binutils
Message-ID <[email protected]>
Collect profitable post-relax call and jump sites, rank JT and JALT candidates
deterministically, emit a JVT only when the net benefit is positive, and
rewrite selected sites to table-jump relocations for non-position-independent
executable links.

bfd/ChangeLog:

	* elfnn-riscv.c (struct riscv_elf_link_hash_table): Add JVT fields.
	(riscv_set_jvt_addr): Add Zcmt support.
	(riscv_is_local_symbol): Ditto.
	(riscv_is_special_symbol_name): Ditto.
	(riscv_jvt_global_hash_entry): Ditto.
	(riscv_jvt_init_key): Ditto.
	(riscv_jvt_key_hash): Ditto.
	(riscv_jvt_htab_hash): Ditto.
	(riscv_jvt_key_eq): Ditto.
	(riscv_jvt_htab_entry_eq): Ditto.
	(riscv_init_jvt_htab): Ditto.
	(riscv_free_jvt_htab): Ditto.
	(riscv_ensure_jvt_htab): Ditto.
	(riscv_update_jvt_entry): Ditto.
	(riscv_elf_link_hash_table_free): Ditto.
	(riscv_elf_link_hash_table_create): Ditto.
	(riscv_use_jvt): Ditto.
	(riscv_elf_create_jvt_section): Ditto.
	(riscv_reject_local_jvt_base_symbol): Ditto.
	(riscv_define_jvt_base_symbol): Ditto.
	(riscv_elf_check_relocs): Ditto.
	(perform_relocation): Ditto.
	(riscv_elf_relocate_section): Ditto.
	(riscv_get_jvt_htab): Ditto.
	(_bfd_riscv_jvt_record): Ditto.
	(riscv_compare_jvt_key): Ditto.
	(riscv_jvt_entry_better): Ditto.
	(riscv_ranking_jvt): Ditto.
	(riscv_record_jvt_index): Ditto.
	(riscv_jvt_profiling): Ditto.
	(_bfd_riscv_jvt_mark): Ditto.
	(_bfd_riscv_relax_call): Ditto.
	(_bfd_riscv_relax_jal): Ditto.
	(_bfd_riscv_get_max_alignment): Ditto.
	(bfd_elfNN_riscv_set_data_segment_info): Ditto.
	(_bfd_riscv_relax_section): Ditto.
	(riscv_final_link): Ditto.
	(bfd_elfNN_bfd_final_link): Ditto.
---
 bfd/elfnn-riscv.c | 1134 +++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 1098 insertions(+), 36 deletions(-)

diff --git a/bfd/elfnn-riscv.c b/bfd/elfnn-riscv.c
index 1c1c3ea36c5..b1b21e996e8 100644
--- a/bfd/elfnn-riscv.c
+++ b/bfd/elfnn-riscv.c
@@ -27,6 +27,7 @@
 #include "libbfd.h"
 #include "bfdlink.h"
 #include "genlink.h"
+#include "hashtab.h"
 #include "elf-bfd.h"
 #include "elfxx-riscv.h"
 #include "elf/riscv.h"
@@ -203,6 +204,65 @@ elfNN_riscv_mkobject (bfd *abfd)
 #include "elf/common.h"
 #include "elf/internal.h"
 
+/* Identity of one JVT entry.  */
+/* JVT candidates are keyed by relocation symbol identity plus addend and PLT
+   selection.  This v2 implementation deliberately does not canonicalize
+   aliases to final section+offset addresses after ordinary relaxation.  */
+typedef struct
+{
+  bool is_local;
+  bool use_plt;
+  bfd *input_bfd;
+  unsigned long symndx;
+  struct elf_link_hash_entry *h;
+  bfd_signed_vma addend;
+} riscv_jvt_key_t;
+
+/* Per-slot data for JVT entries.  */
+typedef struct
+{
+  bfd_size_type benefit;
+  bfd_size_type first_seen;
+  riscv_jvt_key_t key;
+  const char *name;
+} riscv_jvt_slot_t;
+
+/* Hash table for storing table jump candidate entries.  */
+typedef struct
+{
+  /* Hash tables for cm.jt and cm.jalt.  They record all possible
+     entries and will be ranked to put the most beneficial ones into JVT
+     section.  */
+  htab_t jt_htab;
+  htab_t jalt_htab;
+
+  /* Target-endian contents that will be put into JVT section.  */
+  bfd_byte *jvt_addrs;
+
+  asection *jvt_sec;
+  bfd *jvt_sec_owner;
+
+  /* END_IDX is used to calculate size of used slots in the table jump section,
+     and it is set to -1 if the profiling stage is complete.  */
+  int end_idx;
+  bfd_size_type total_benefits;
+  bfd_size_type next_ordinal;
+
+  /* Unified slot array for ranked JVT entries.  */
+  riscv_jvt_slot_t *slots;
+} riscv_jvt_htab_t;
+
+typedef struct
+{
+  unsigned int index;
+
+  riscv_jvt_key_t key;
+
+  bfd_size_type benefit;
+  bfd_size_type first_seen;
+  const char *name;
+} riscv_jvt_htab_entry;
+
 struct riscv_elf_link_hash_table
 {
   struct elf_link_hash_table elf;
@@ -241,6 +301,11 @@ struct riscv_elf_link_hash_table
   bool (*make_plt_header) (bfd *output_bfd, struct riscv_elf_link_hash_table *htab);
   bool (*make_plt_entry) (bfd *output_bfd, asection *got, bfd_vma got_offset,
 			  asection *plt, bfd_vma plt_offset);
+
+  riscv_jvt_htab_t *jvt_htab;
+
+  /* Cached result for riscv_use_jvt, scoped to one link.  */
+  int use_jvt;
 };
 
 /* Instruction access functions. */
@@ -266,6 +331,8 @@ static bool
 riscv_make_plt_header (bfd *, struct riscv_elf_link_hash_table *);
 static bool
 riscv_make_plt_entry (bfd *, asection *, bfd_vma, asection *, bfd_vma);
+static void
+riscv_set_jvt_addr (struct bfd_link_info *, unsigned int, bfd_vma);
 
 void
 riscv_elfNN_set_options (struct bfd_link_info *link_info,
@@ -307,6 +374,13 @@ riscv_is_insn_reloc (const reloc_howto_type *howto)
 	       ? (MINUS_ONE << howto->bitsize) : (bfd_vma)0)) != 0);
 }
 
+/* Return true if the given symbol index is a local symbol.  */
+static inline bool
+riscv_is_local_symbol (Elf_Internal_Shdr *symtab_hdr, unsigned long symndx)
+{
+  return symndx < symtab_hdr->sh_info;
+}
+
 /* PLT/GOT stuff.  */
 #define PLT_HEADER_INSNS 8
 #define PLT_ENTRY_INSNS 4
@@ -533,6 +607,14 @@ riscv_make_plt_zicfilp_unlabeled_entry (bfd *output_bfd, asection *got,
   return true;
 }
 
+static inline bool
+riscv_is_special_symbol_name (bfd *abfd, const char *name)
+{
+  return (!strcmp (name, "")
+	  || _bfd_elf_is_local_label_name (abfd, name)
+	  || riscv_elf_is_mapping_symbols (name));
+}
+
 /* Create an entry in an RISC-V ELF linker hash table.  */
 
 static struct bfd_hash_entry *
@@ -563,6 +645,211 @@ link_hash_newfunc (struct bfd_hash_entry *entry,
   return entry;
 }
 
+static struct elf_link_hash_entry *
+riscv_jvt_global_hash_entry (bfd *abfd, unsigned long symndx)
+{
+  Elf_Internal_Shdr *symtab_hdr = &elf_symtab_hdr (abfd);
+  struct elf_link_hash_entry *h;
+
+  if (symndx < symtab_hdr->sh_info)
+    return NULL;
+
+  h = elf_sym_hashes (abfd)[symndx - symtab_hdr->sh_info];
+  if (h == NULL)
+    return NULL;
+
+  while (h->root.type == bfd_link_hash_indirect
+	 || h->root.type == bfd_link_hash_warning)
+    h = (struct elf_link_hash_entry *) h->root.u.i.link;
+
+  return h;
+}
+
+static bool
+riscv_jvt_init_key (struct bfd_link_info *info, bfd *abfd,
+		    const Elf_Internal_Rela *rel, riscv_jvt_key_t *key,
+		    const char **name)
+{
+  Elf_Internal_Shdr *symtab_hdr = &elf_symtab_hdr (abfd);
+  unsigned long symndx = ELFNN_R_SYM (rel->r_info);
+
+  memset (key, 0, sizeof (*key));
+  key->input_bfd = abfd;
+  key->symndx = symndx;
+  key->addend = rel->r_addend;
+
+  if (riscv_is_local_symbol (symtab_hdr, symndx))
+    {
+      Elf_Internal_Sym *sym;
+
+      if (symtab_hdr->contents == NULL)
+	return false;
+
+      sym = (Elf_Internal_Sym *) symtab_hdr->contents + symndx;
+      *name = bfd_elf_sym_name (abfd, symtab_hdr, sym, NULL);
+      key->is_local = true;
+    }
+  else
+    {
+      struct riscv_elf_link_hash_table *htab = riscv_elf_hash_table (info);
+      struct elf_link_hash_entry *h = riscv_jvt_global_hash_entry (abfd,
+								   symndx);
+
+      if (h == NULL || h->type == STT_GNU_IFUNC)
+	return false;
+
+      key->h = h;
+      key->use_plt = htab->elf.splt != NULL && h->plt.offset != MINUS_ONE;
+      *name = h->root.root.string;
+    }
+
+  return *name != NULL && !riscv_is_special_symbol_name (abfd, *name);
+}
+
+static hashval_t
+riscv_jvt_key_hash (const riscv_jvt_key_t *key)
+{
+  hashval_t h;
+  bfd_vma addend = (bfd_vma) key->addend;
+
+  if (key->is_local)
+    {
+      unsigned int id = key->input_bfd->id;
+      unsigned int symndx = key->symndx;
+
+      h = (id + symndx) * (id + symndx + 1) / 2 + id;
+    }
+  else
+    h = htab_hash_pointer (key->h);
+
+  h ^= (hashval_t) addend;
+  h ^= (hashval_t) (addend >> 32);
+  if (key->use_plt)
+    h ^= 0x9e3779b9;
+  return h;
+}
+
+static hashval_t
+riscv_jvt_htab_hash (const void *entry)
+{
+  const riscv_jvt_htab_entry *e = entry;
+  return riscv_jvt_key_hash (&e->key);
+}
+
+static bool
+riscv_jvt_key_eq (const riscv_jvt_key_t *key1, const riscv_jvt_key_t *key2)
+{
+  if (key1->is_local != key2->is_local
+      || key1->use_plt != key2->use_plt
+      || key1->addend != key2->addend)
+    return false;
+
+  if (key1->is_local)
+    return (key1->input_bfd->id == key2->input_bfd->id
+	    && key1->symndx == key2->symndx);
+
+  return key1->h == key2->h;
+}
+
+static int
+riscv_jvt_htab_entry_eq (const void *entry1, const void *entry2)
+{
+  const riscv_jvt_htab_entry *e1 = entry1, *e2 = entry2;
+  return riscv_jvt_key_eq (&e1->key, &e2->key);
+}
+
+static bool
+riscv_init_jvt_htab (riscv_jvt_htab_t *htab)
+{
+  htab->slots = bfd_zmalloc (sizeof (riscv_jvt_slot_t) * ZCMT_TOTAL_ENTRIES);
+  htab->jvt_addrs = bfd_zmalloc (RISCV_ELF_WORD_BYTES * ZCMT_TOTAL_ENTRIES);
+  if (htab->slots == NULL || htab->jvt_addrs == NULL)
+    return false;
+  htab->end_idx = 0;
+  htab->total_benefits = 0;
+  htab->next_ordinal = 0;
+
+  htab->jt_htab = htab_create (ZCMT_JT_NUM_ENTRIES, riscv_jvt_htab_hash,
+			       riscv_jvt_htab_entry_eq, free);
+  if (htab->jt_htab == NULL)
+    return false;
+
+  htab->jalt_htab = htab_create (ZCMT_JALT_NUM_ENTRIES, riscv_jvt_htab_hash,
+				 riscv_jvt_htab_entry_eq, free);
+  return htab->jalt_htab != NULL;
+}
+
+static void
+riscv_free_jvt_htab (riscv_jvt_htab_t *htab)
+{
+  free (htab->slots);
+  free (htab->jvt_addrs);
+  if (htab->jt_htab)
+    htab_delete (htab->jt_htab);
+  if (htab->jalt_htab)
+    htab_delete (htab->jalt_htab);
+}
+
+static bool
+riscv_ensure_jvt_htab (struct riscv_elf_link_hash_table *htab)
+{
+  if (htab->jvt_htab != NULL)
+    return true;
+
+  htab->jvt_htab = (riscv_jvt_htab_t *) bfd_zmalloc (sizeof (*htab->jvt_htab));
+  if (htab->jvt_htab == NULL)
+    return false;
+
+  if (!riscv_init_jvt_htab (htab->jvt_htab))
+    {
+      riscv_free_jvt_htab (htab->jvt_htab);
+      free (htab->jvt_htab);
+      htab->jvt_htab = NULL;
+      return false;
+    }
+
+  return true;
+}
+
+/* Update table jump hash entry.  */
+
+static bool
+riscv_update_jvt_entry (riscv_jvt_htab_t *jvt_htab, htab_t htab,
+			const riscv_jvt_key_t *key,
+			bfd_size_type benefit, const char *name)
+{
+  riscv_jvt_htab_entry search = {.index = 0,
+				 .key = *key,
+				 .benefit = 0,
+				 .first_seen = 0,
+				 .name = NULL};
+
+  riscv_jvt_htab_entry *entry = htab_find (htab, &search);
+
+  if (entry == NULL)
+    {
+      riscv_jvt_htab_entry **slot
+	= (riscv_jvt_htab_entry **) htab_find_slot (htab, &search, INSERT);
+
+      BFD_ASSERT (*slot == NULL);
+
+      *slot
+	= (riscv_jvt_htab_entry *) bfd_zmalloc (sizeof (riscv_jvt_htab_entry));
+
+      if (*slot == NULL)
+	return false;
+
+      (*slot)->key = *key;
+      (*slot)->benefit = benefit;
+      (*slot)->first_seen = jvt_htab->next_ordinal++;
+      (*slot)->name = name;
+    }
+  else
+    entry->benefit += benefit;
+
+  return true;
+}
+
 /* Compute a hash of a local hash entry.  We use elf_link_hash_entry
    for local symbol so that we can handle local STT_GNU_IFUNC symbols
    as global symbol.  We reuse indx and dynstr_index for local symbol
@@ -640,6 +927,12 @@ riscv_elf_link_hash_table_free (bfd *obfd)
   if (ret->loc_hash_memory)
     objalloc_free ((struct objalloc *) ret->loc_hash_memory);
 
+  if (ret->jvt_htab)
+    {
+      riscv_free_jvt_htab (ret->jvt_htab);
+      free (ret->jvt_htab);
+    }
+
   _bfd_elf_link_hash_table_free (obfd);
 }
 
@@ -696,6 +989,7 @@ riscv_elf_link_hash_table_create (bfd *abfd)
 
   ret->max_alignment = (bfd_vma) -1;
   ret->max_alignment_for_gp = (bfd_vma) -1;
+  ret->use_jvt = -1;
 
   setup_plt_values (abfd, ret, PLT_NORMAL);
 
@@ -915,6 +1209,193 @@ bad_static_reloc (bfd *abfd, unsigned r_type, struct elf_link_hash_entry *h)
   return false;
 }
 
+static bool
+riscv_use_jvt (struct bfd_link_info *info)
+{
+  unsigned xlen = ARCH_SIZE;
+  riscv_subset_list_t subsets;
+  struct riscv_elf_link_hash_table *htab;
+
+  htab = riscv_elf_hash_table (info);
+  if (htab->use_jvt >= 0)
+    return htab->use_jvt;
+
+  /* If relax is disabled by user, generating position-independent output,
+     or not linking an executable, table jump insns will not be generated.  */
+  if (info->disable_target_specific_optimizations >= 1
+      || bfd_link_pic (info)
+      || !bfd_link_executable (info)
+      || !htab->params->relax_zcmt)
+    {
+      htab->use_jvt = 0;
+      return htab->use_jvt;
+    }
+
+  bfd *obfd = info->output_bfd;
+  obj_attribute *out_attr = elf_known_obj_attributes_proc (obfd);
+
+  subsets.head = NULL;
+  subsets.tail = NULL;
+  subsets.arch_str = NULL;
+
+  riscv_parse_subset_t riscv_rps_ld_out
+    = {&subsets, _bfd_error_handler, &xlen, NULL, false};
+
+  if (!riscv_parse_subset (&riscv_rps_ld_out, out_attr[Tag_RISCV_arch].s))
+    {
+      htab->use_jvt = 0;
+      riscv_release_subset_list (&subsets);
+      return htab->use_jvt;
+    }
+
+  /* Mirror the ISA and psABI constraints: Zcmt depends on Zca and Zicsr,
+     and its encodings overlap with Zcd compressed floating-point encodings.  */
+  htab->use_jvt = (riscv_subset_supports (&riscv_rps_ld_out, "zcmt")
+		   && riscv_subset_supports (&riscv_rps_ld_out, "zca")
+		   && riscv_subset_supports (&riscv_rps_ld_out, "zicsr")
+		   && !riscv_subset_supports (&riscv_rps_ld_out, "zcd"));
+  riscv_release_subset_list (&subsets);
+
+  return htab->use_jvt;
+}
+
+static bool
+riscv_elf_create_jvt_section (bfd *abfd, struct bfd_link_info *info)
+{
+  asection *sec;
+  struct riscv_elf_link_hash_table *htab;
+
+  /* Skip if no Zcmt.  */
+  if (!riscv_use_jvt (info))
+    return true;
+
+  htab = riscv_elf_hash_table (info);
+  if (!riscv_ensure_jvt_htab (htab))
+    return false;
+  sec = bfd_get_linker_section (abfd, TABLE_JUMP_SEC_NAME);
+
+  if (sec != NULL)
+    return true;
+
+  if (htab->jvt_htab->jvt_sec == NULL)
+    {
+      sec = bfd_make_section_anyway_with_flags (
+	abfd, TABLE_JUMP_SEC_NAME,
+	(SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_HAS_CONTENTS | SEC_IN_MEMORY
+	 | SEC_KEEP | SEC_CODE));
+
+      /* Create only a zero-sized placeholder before relaxation.  The Zcmt
+	 profiling pass sets the final alignment and size if a profitable
+	 table is chosen, so links that opt in but select no JVT do not reserve
+	 a full table or perturb layout.  */
+      if (sec == NULL || !bfd_set_section_alignment (sec, 0)
+	  || !bfd_set_section_size (sec, 0))
+	return false;
+
+      htab->jvt_htab->jvt_sec = sec;
+      htab->jvt_htab->jvt_sec_owner = abfd;
+    }
+
+  return true;
+}
+
+static bool
+riscv_reject_local_jvt_base_symbol (bfd *abfd)
+{
+  Elf_Internal_Shdr *symtab_hdr = &elf_symtab_hdr (abfd);
+  Elf_Internal_Sym *isymbuf;
+  unsigned long symndx;
+
+  if (symtab_hdr->sh_info == 0)
+    return true;
+
+  if (symtab_hdr->contents == NULL
+      && !(symtab_hdr->contents = (unsigned char *) bfd_elf_get_elf_syms
+	   (abfd, symtab_hdr, symtab_hdr->sh_info, 0, NULL, NULL, NULL)))
+    return false;
+
+  isymbuf = (Elf_Internal_Sym *) symtab_hdr->contents;
+  for (symndx = 0; symndx < symtab_hdr->sh_info; symndx++)
+    {
+      const char *name = bfd_elf_sym_name (abfd, symtab_hdr,
+					   isymbuf + symndx, NULL);
+
+      if (name != NULL && strcmp (name, RISCV_TABLE_JUMP_BASE_SYMBOL) == 0)
+	{
+	  _bfd_error_handler
+	    (_("%pB: %s is reserved for Zcmt table-jump relaxation"),
+	     abfd, RISCV_TABLE_JUMP_BASE_SYMBOL);
+	  bfd_set_error (bfd_error_bad_value);
+	  return false;
+	}
+    }
+
+  return true;
+}
+
+static bool
+riscv_define_jvt_base_symbol (bfd *abfd, struct bfd_link_info *info)
+{
+  struct riscv_elf_link_hash_table *htab = riscv_elf_hash_table (info);
+  riscv_jvt_htab_t *jvt_htab = htab->jvt_htab;
+  struct elf_link_hash_entry *h;
+  struct bfd_link_hash_entry *bh = NULL;
+  const struct elf_backend_data *bed = get_elf_backend_data (abfd);
+  bfd *ibfd;
+
+  if (jvt_htab == NULL || jvt_htab->jvt_sec == NULL
+      || jvt_htab->jvt_sec->size == 0)
+    return true;
+
+  if (jvt_htab->jvt_sec->contents == NULL)
+    {
+      jvt_htab->jvt_sec->contents
+	= (bfd_byte *) bfd_zalloc (jvt_htab->jvt_sec->owner,
+				   jvt_htab->jvt_sec->size);
+      jvt_htab->jvt_sec->alloced = 1;
+      if (jvt_htab->jvt_sec->contents == NULL)
+	return false;
+    }
+
+  h = elf_link_hash_lookup (elf_hash_table (info),
+			    RISCV_TABLE_JUMP_BASE_SYMBOL,
+			    false, false, true);
+  /* Code may reference __jvt_base$ to initialize the JVT CSR before
+     executing cm.jt/cm.jalt.  That is not a user definition; resolve
+     undefined references by defining the linker-owned symbol below, but
+     still reject any user-provided definition of the reserved name.  */
+  if (h != NULL
+      && h->root.type != bfd_link_hash_new
+      && h->root.type != bfd_link_hash_undefined
+      && h->root.type != bfd_link_hash_undefweak)
+    {
+      _bfd_error_handler
+	(_("%pB: %s is reserved for Zcmt table-jump relaxation"),
+	 abfd, RISCV_TABLE_JUMP_BASE_SYMBOL);
+      bfd_set_error (bfd_error_bad_value);
+      return false;
+    }
+
+  for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link.next)
+    if (!riscv_reject_local_jvt_base_symbol (ibfd))
+      return false;
+
+  if (!_bfd_generic_link_add_one_symbol (info, abfd,
+					 RISCV_TABLE_JUMP_BASE_SYMBOL,
+					 BSF_GLOBAL, jvt_htab->jvt_sec,
+					 0, NULL, true, bed->collect, &bh))
+    return false;
+
+  h = (struct elf_link_hash_entry *) bh;
+  h->root.linker_def = 1;
+  h->def_regular = 1;
+  h->type = STT_NOTYPE;
+  if (ELF_ST_VISIBILITY (h->other) != STV_INTERNAL)
+    h->other = ((h->other & ~ELF_ST_VISIBILITY (-1)) | STV_HIDDEN);
+  bed->elf_backend_hide_symbol (info, h, true);
+  return true;
+}
+
 /* Look through the relocs for a section during the first phase, and
    allocate space in the global offset table or procedure linkage
    table.  */
@@ -1261,6 +1742,9 @@ riscv_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 	}
     }
 
+  if (!riscv_elf_create_jvt_section (abfd, info))
+    return false;
+
   return true;
 }
 
@@ -2037,6 +2521,9 @@ perform_relocation (const reloc_howto_type *howto,
       value = ENCODE_CBTYPE_IMM (value);
       break;
 
+    case R_RISCV_TABLE_JUMP:
+      return bfd_reloc_ok;
+
     case R_RISCV_RVC_JUMP:
       if (!VALID_CJTYPE_IMM (value))
 	return bfd_reloc_overflow;
@@ -2985,6 +3472,24 @@ riscv_elf_relocate_section (struct bfd_link_info *info,
 	  }
 	  break;
 
+	case R_RISCV_TABLE_JUMP:
+	  {
+	    bfd_vma insn = bfd_getl16 (contents + rel->r_offset);
+	    unsigned int tbl_index = EXTRACT_ZCMT_INDEX (insn);
+
+	    if (via_plt)
+	      {
+		relocation = sec_addr (htab->elf.splt) + h->plt.offset;
+		unresolved_reloc = false;
+	      }
+
+	    if (!riscv_ensure_jvt_htab (htab))
+	      return false;
+	    riscv_set_jvt_addr (info, tbl_index, relocation + rel->r_addend);
+	    rel->r_info = ELFNN_R_INFO (0, R_RISCV_NONE);
+	    continue;
+	  }
+
 	case R_RISCV_CALL:
 	case R_RISCV_CALL_PLT:
 	  /* Handle a call to an undefined weak function.  This won't be
@@ -4267,6 +4772,331 @@ _riscv_relax_delete_immediate (bfd *abfd,
 				    link_info, p, 0, sec->size);
 }
 
+/* Return the cm.jt hash table for x0, or the cm.jalt hash table for ra.  */
+
+static htab_t
+riscv_get_jvt_htab (struct bfd_link_info *info, unsigned int link_reg)
+{
+  struct riscv_elf_link_hash_table *htab = riscv_elf_hash_table (info);
+
+  if (htab == NULL || !riscv_ensure_jvt_htab (htab))
+    return NULL;
+
+  if (link_reg == 0)
+    return htab->jvt_htab->jt_htab;
+  if (link_reg == X_RA)
+    return htab->jvt_htab->jalt_htab;
+
+  return NULL;
+}
+
+/* Record JAL, CALL, or CALL_PLT as a jump table candidate.  */
+
+static bool
+_bfd_riscv_jvt_record (bfd *abfd, asection *sec ATTRIBUTE_UNUSED,
+		       asection *sym_sec ATTRIBUTE_UNUSED,
+		       struct bfd_link_info *link_info, Elf_Internal_Rela *rel,
+		       bfd_vma symval ATTRIBUTE_UNUSED,
+		       bfd_vma max_alignment ATTRIBUTE_UNUSED,
+		       bfd_vma reserve_size ATTRIBUTE_UNUSED,
+		       bool *again ATTRIBUTE_UNUSED,
+		       riscv_pcgp_relocs *pcgp_relocs ATTRIBUTE_UNUSED,
+		       bool undefined_weak ATTRIBUTE_UNUSED)
+{
+  bfd_byte *contents = elf_section_data (sec)->this_hdr.contents;
+  int type = ELFNN_R_TYPE (rel->r_info);
+  bfd_vma insn;
+  bfd_size_type benefit;
+  unsigned int rd;
+  htab_t tbljal_htab;
+  const char *name;
+  riscv_jvt_key_t key;
+
+  if (type == R_RISCV_CALL || type == R_RISCV_CALL_PLT)
+    {
+      insn = bfd_getl32 (contents + rel->r_offset + 4);
+      benefit = (4 + 4) - 2; /* AUIPC + JALR -> cm.jt/cm.jalt.  */
+    }
+  else if (type == R_RISCV_JAL)
+    {
+      insn = bfd_getl32 (contents + rel->r_offset);
+      benefit = 4 - 2; /* JAL -> cm.jt/cm.jalt.  */
+    }
+  else
+    {
+      BFD_ASSERT (false);
+      return true;
+    }
+
+  rd = (insn >> OP_SH_RD) & OP_MASK_RD;
+  tbljal_htab = riscv_get_jvt_htab (link_info, rd);
+
+  if (tbljal_htab == NULL)
+    return true;
+
+  if (!riscv_jvt_init_key (link_info, abfd, rel, &key, &name))
+    return true;
+
+  return riscv_update_jvt_entry (riscv_elf_hash_table (link_info)->jvt_htab,
+				 tbljal_htab, &key, benefit, name);
+}
+
+static void
+riscv_set_jvt_addr (struct bfd_link_info *info, unsigned int index,
+		    bfd_vma addr)
+{
+  struct riscv_elf_link_hash_table *htab = riscv_elf_hash_table (info);
+  bfd_byte *loc;
+
+  BFD_ASSERT (index < ZCMT_TOTAL_ENTRIES);
+  BFD_ASSERT (htab->jvt_htab != NULL);
+
+  addr &= ~(bfd_vma) 1;
+  loc = htab->jvt_htab->jvt_addrs + index * RISCV_ELF_WORD_BYTES;
+  bfd_put_NN (info->output_bfd, addr, loc);
+}
+
+typedef struct
+{
+  riscv_jvt_htab_t *htab;
+  unsigned int start;
+  unsigned int end;
+} riscv_jvt_args;
+
+static int
+riscv_compare_jvt_key (const riscv_jvt_key_t *key1, const char *name1,
+		       const riscv_jvt_key_t *key2, const char *name2)
+{
+  int cmp;
+
+  cmp = strcmp (name1 == NULL ? "" : name1, name2 == NULL ? "" : name2);
+  if (cmp != 0)
+    return cmp;
+
+  cmp = strcmp (bfd_get_filename (key1->input_bfd),
+		bfd_get_filename (key2->input_bfd));
+  if (cmp != 0)
+    return cmp;
+
+  if (key1->symndx != key2->symndx)
+    return key1->symndx < key2->symndx ? -1 : 1;
+  if (key1->addend != key2->addend)
+    return key1->addend < key2->addend ? -1 : 1;
+  if (key1->use_plt != key2->use_plt)
+    return key1->use_plt ? 1 : -1;
+  return 0;
+}
+
+static bool
+riscv_jvt_entry_better (const riscv_jvt_htab_entry *entry,
+			const riscv_jvt_slot_t *slot)
+{
+  if (slot->benefit == 0)
+    return true;
+  if (entry->benefit != slot->benefit)
+    return entry->benefit > slot->benefit;
+  if (entry->first_seen != slot->first_seen)
+    return entry->first_seen < slot->first_seen;
+  return riscv_compare_jvt_key (&entry->key, entry->name, &slot->key,
+				slot->name) < 0;
+}
+
+static int
+riscv_ranking_jvt (void **entry_ptr, void *arg_)
+{
+  const riscv_jvt_htab_entry *entry = *entry_ptr;
+  riscv_jvt_args *arg = arg_;
+  riscv_jvt_slot_t *slots = arg->htab->slots;
+  unsigned int left;
+
+  for (left = arg->start; left <= arg->end; left++)
+    if (riscv_jvt_entry_better (entry, &slots[left]))
+      break;
+
+  if (left > arg->end)
+    return true;
+
+  for (unsigned int idx = arg->end; idx > left; idx--)
+    slots[idx] = slots[idx - 1];
+
+  slots[left].benefit = entry->benefit;
+  slots[left].first_seen = entry->first_seen;
+  slots[left].name = entry->name;
+  slots[left].key = entry->key;
+
+  return true;
+}
+
+static bool
+riscv_record_jvt_index (htab_t htab, riscv_jvt_args *args,
+			unsigned int count)
+{
+  riscv_jvt_htab_t *jvt_htab = args->htab;
+  riscv_jvt_slot_t *slots = jvt_htab->slots;
+  riscv_jvt_htab_entry *entry = NULL;
+  unsigned int idx, end;
+
+  end = args->start + count;
+  for (idx = args->start; idx < end; idx++)
+    {
+      riscv_jvt_htab_entry search =
+	{
+	  .index = 0,
+	  .key = slots[idx].key,
+	  .benefit = 0,
+	  .first_seen = 0,
+	  .name = NULL,
+	};
+
+      entry = htab_find (htab, &search);
+
+      BFD_ASSERT (entry != NULL);
+      entry->index = idx + 1;
+      jvt_htab->total_benefits += slots[idx].benefit;
+    }
+
+  if (entry != NULL && entry->index != 0)
+    jvt_htab->end_idx = entry->index;
+
+  return true;
+}
+
+static bool
+riscv_jvt_profiling (riscv_jvt_htab_t *jvt_htab, riscv_jvt_args *args)
+{
+  bfd_size_type jt_prefix[ZCMT_JT_NUM_ENTRIES + 1];
+  bfd_size_type jalt_prefix[ZCMT_JALT_NUM_ENTRIES + 1];
+  unsigned int jt_count = 0;
+  unsigned int jalt_count = 0;
+  unsigned int best_jt = 0;
+  unsigned int best_jalt = 0;
+  unsigned int best_entries = 0;
+  bfd_size_type best_benefit = 0;
+  bfd_size_type best_net = 0;
+
+  memset (jvt_htab->slots, 0,
+	  sizeof (riscv_jvt_slot_t) * ZCMT_TOTAL_ENTRIES);
+  jvt_htab->total_benefits = 0;
+  jvt_htab->end_idx = 0;
+
+  args->start = ZCMT_JT_BEGIN;
+  args->end = ZCMT_JT_END;
+  htab_traverse (jvt_htab->jt_htab, riscv_ranking_jvt, args);
+
+  args->start = ZCMT_JALT_BEGIN;
+  args->end = ZCMT_JALT_END;
+  htab_traverse (jvt_htab->jalt_htab, riscv_ranking_jvt, args);
+
+  jt_prefix[0] = 0;
+  while (jt_count < ZCMT_JT_NUM_ENTRIES
+	 && jvt_htab->slots[ZCMT_JT_BEGIN + jt_count].benefit != 0)
+    {
+      jt_prefix[jt_count + 1]
+	= jt_prefix[jt_count]
+	  + jvt_htab->slots[ZCMT_JT_BEGIN + jt_count].benefit;
+      jt_count++;
+    }
+
+  jalt_prefix[0] = 0;
+  while (jalt_count < ZCMT_JALT_NUM_ENTRIES
+	 && jvt_htab->slots[ZCMT_JALT_BEGIN + jalt_count].benefit != 0)
+    {
+      jalt_prefix[jalt_count + 1]
+	= jalt_prefix[jalt_count]
+	  + jvt_htab->slots[ZCMT_JALT_BEGIN + jalt_count].benefit;
+      jalt_count++;
+    }
+
+  for (unsigned int jt = 0; jt <= jt_count; jt++)
+    for (unsigned int jalt = 0; jalt <= jalt_count; jalt++)
+      {
+	unsigned int entries;
+	bfd_size_type benefit;
+	bfd_size_type table_cost, net;
+
+	if (jt == 0 && jalt == 0)
+	  entries = 0;
+	else if (jalt == 0)
+	  entries = jt;
+	else
+	  entries = ZCMT_JALT_BEGIN + jalt;
+
+	benefit = jt_prefix[jt] + jalt_prefix[jalt];
+	table_cost = (bfd_size_type) entries * RISCV_ELF_WORD_BYTES;
+	net = benefit > table_cost ? benefit - table_cost : 0;
+
+	if (net > best_net
+	    || (net == best_net
+		&& (entries < best_entries
+		    || (entries == best_entries
+			&& benefit > best_benefit))))
+	  {
+	    best_net = net;
+	    best_jt = jt;
+	    best_jalt = jalt;
+	    best_entries = entries;
+	    best_benefit = benefit;
+	  }
+      }
+
+  if (best_net == 0)
+    {
+      if (jvt_htab->jvt_sec != NULL)
+	{
+	  asection *sec = jvt_htab->jvt_sec;
+	  asection *out_sec = sec->output_section;
+
+	  /* The JVT placeholder is created before profitability is known so
+	     that orphan placement and relaxation sizing can see a normal
+	     linker-created section.  When no positive-benefit table is chosen,
+	     SEC_EXCLUDE alone is not enough: the orphan output section may
+	     already have been inserted into the output BFD section list and a
+	     zero-sized .riscv.jvt would remain visible to later layout and
+	     inspection.  It is safe to remove these sections here because this
+	     path runs before __jvt_base$ is defined, the placeholder has no
+	     user input section owner, and no table-jump relocations have been
+	     marked.  Tests cover both custom-script and default-script no-JVT
+	     outputs.  */
+	  sec->flags &= ~SEC_KEEP;
+	  sec->flags |= SEC_EXCLUDE;
+	  sec->output_section = bfd_abs_section_ptr;
+
+	  if (out_sec != NULL && out_sec != bfd_abs_section_ptr)
+	    {
+	      out_sec->flags &= ~SEC_KEEP;
+	      out_sec->flags |= SEC_EXCLUDE;
+	      if (!bfd_section_removed_from_list (out_sec->owner, out_sec))
+		{
+		  bfd_section_list_remove (out_sec->owner, out_sec);
+		  out_sec->owner->section_count--;
+		}
+	    }
+
+	  if (sec != out_sec
+	      && !bfd_section_removed_from_list (sec->owner, sec))
+	    {
+	      bfd_section_list_remove (sec->owner, sec);
+	      sec->owner->section_count--;
+	    }
+	}
+      jvt_htab->end_idx = -1;
+      return true;
+    }
+
+  args->start = ZCMT_JT_BEGIN;
+  args->end = ZCMT_JT_END;
+  riscv_record_jvt_index (jvt_htab->jt_htab, args, best_jt);
+
+  args->start = ZCMT_JALT_BEGIN;
+  args->end = ZCMT_JALT_END;
+  riscv_record_jvt_index (jvt_htab->jalt_htab, args, best_jalt);
+
+  jvt_htab->end_idx = best_entries;
+  jvt_htab->total_benefits = best_benefit;
+
+  return true;
+}
+
 /* Return true if TYPE is a delete relocation.  */
 
 static bool
@@ -4346,7 +5176,84 @@ typedef bool (*relax_func_t) (bfd *, asection *, asection *,
 			      riscv_pcgp_relocs *,
 			      bool undefined_weak);
 
-/* Relax AUIPC + JALR into JAL.  */
+/* Mark JAL/CALL/CALL_PLT to use table jump instructions.  */
+
+static bool
+_bfd_riscv_jvt_mark (bfd *abfd, asection *sec,
+		     asection *sym_sec ATTRIBUTE_UNUSED,
+		     struct bfd_link_info *link_info, Elf_Internal_Rela *rel,
+		     bfd_vma symval ATTRIBUTE_UNUSED,
+		     bfd_vma max_alignment ATTRIBUTE_UNUSED,
+		     bfd_vma reserve_size ATTRIBUTE_UNUSED,
+		     bool *again,
+		     riscv_pcgp_relocs *pcgp_relocs,
+		     bool undefined_weak ATTRIBUTE_UNUSED)
+{
+  bfd_byte *contents = elf_section_data (sec)->this_hdr.contents;
+  int type = ELFNN_R_TYPE (rel->r_info);
+  bfd_byte *jump_inst_location = NULL;
+  bfd_vma target, jump_inst;
+  unsigned int rd;
+  htab_t tbljal_htab;
+  riscv_jvt_key_t key;
+  const char *name;
+  riscv_jvt_htab_entry search;
+  riscv_jvt_htab_entry *entry;
+  size_t delete_count = 0;
+
+  if (type == R_RISCV_JAL)
+    {
+      jump_inst_location = contents + rel->r_offset;
+      delete_count = 2;
+    }
+  else if (type == R_RISCV_CALL || type == R_RISCV_CALL_PLT)
+    {
+      jump_inst_location = contents + rel->r_offset + 4;
+      delete_count = 6;
+    }
+  else
+    {
+      /* Dispatch should only pass JAL/CALL/CALL_PLT here.  Keep malformed
+	 or unexpected input from turning an internal invariant into a crash.  */
+      BFD_ASSERT (false);
+      return true;
+    }
+
+  jump_inst = bfd_getl32 (jump_inst_location);
+  rd = (jump_inst >> OP_SH_RD) & OP_MASK_RD;
+  tbljal_htab = riscv_get_jvt_htab (link_info, rd);
+
+  /* Check if it uses a valid link register.  */
+  if (tbljal_htab == NULL)
+    return true;
+
+  if (!riscv_jvt_init_key (link_info, abfd, rel, &key, &name))
+    return true;
+
+  search.index = 0;
+  search.key = key;
+  search.benefit = 0;
+  search.first_seen = 0;
+  search.name = NULL;
+  entry = htab_find (tbljal_htab, &search);
+
+  /* entry->index == 0 when the entry is not used as a table jump entry.  */
+  if (entry != NULL && entry->index > 0)
+    {
+      target = MATCH_CM_JT | ENCODE_ZCMT_INDEX (entry->index - 1);
+      bfd_putl16 (target, contents + rel->r_offset);
+      rel->r_info
+	= ELFNN_R_INFO (ELFNN_R_SYM (rel->r_info), R_RISCV_TABLE_JUMP);
+      *again = true;
+      return riscv_relax_delete_bytes (abfd, sec, rel->r_offset + 2,
+				       delete_count, link_info, pcgp_relocs,
+				       rel + 1, false);
+    }
+
+  return true;
+}
+
+/* Relax AUIPC + JALR into JAL or CM.[JT,JALT].  */
 
 static bool
 _bfd_riscv_relax_call (bfd *abfd, asection *sec, asection *sym_sec,
@@ -4365,6 +5272,9 @@ _bfd_riscv_relax_call (bfd *abfd, asection *sec, asection *sym_sec,
   bfd_vma auipc, jalr;
   int rd, r_type, len = 4, rvc = elf_elfheader (abfd)->e_flags & EF_RISCV_RVC;
 
+  auipc = bfd_getl32 (contents + rel->r_offset);
+  jalr = bfd_getl32 (contents + rel->r_offset + 4);
+
   /* If the call crosses section boundaries, an alignment directive could
      cause the PC-relative offset to later increase, so we need to add in the
      max alignment of any section inclusive from the call to the target.
@@ -4384,8 +5294,6 @@ _bfd_riscv_relax_call (bfd *abfd, asection *sec, asection *sym_sec,
   /* Shorten the function call.  */
   BFD_ASSERT (rel->r_offset + 8 <= sec->size);
 
-  auipc = bfd_getl32 (contents + rel->r_offset);
-  jalr = bfd_getl32 (contents + rel->r_offset + 4);
   rd = (jalr >> OP_SH_RD) & OP_MASK_RD;
   rvc = rvc && VALID_CJTYPE_IMM (foff);
 
@@ -4442,13 +5350,14 @@ _bfd_riscv_relax_jal (bfd *abfd, asection *sec, asection *sym_sec,
   bfd_byte *contents = elf_section_data (sec)->this_hdr.contents;
   bfd_vma foff = symval - (sec_addr (sec) + rel->r_offset);
   bool rvc = elf_elfheader (abfd)->e_flags & EF_RISCV_RVC;
+  bfd_vma jal = bfd_getl32 (contents + rel->r_offset);
+  int rd;
 
   /* Can't relax to compressed instruction without RVC.  */
   if (!rvc)
     return true;
 
-  bfd_vma jal = bfd_getl32 (contents + rel->r_offset);
-  int rd = (jal >> OP_SH_RD) & OP_MASK_RD;
+  rd = (jal >> OP_SH_RD) & OP_MASK_RD;
 
   /* C.J exists on RV32 and RV64, but C.JAL is RV32-only.  */
   if (!(rd == 0 || (rd == X_RA && ARCH_SIZE == 32)))
@@ -4503,6 +5412,11 @@ _bfd_riscv_get_max_alignment (asection *sec, bfd_vma gp)
   for (o = sec->output_section->owner->sections; o != NULL; o = o->next)
     {
       bool valid = true;
+      /* Exclude the table jump section because it does not affect
+	 alignment.  */
+      if (strcmp (o->name, TABLE_JUMP_SEC_NAME) == 0)
+	continue;
+
       if (gp
 	  && !(VALID_ITYPE_IMM (sec_addr (o) - gp)
 	       || VALID_ITYPE_IMM (sec_addr (o) + o->size - gp)))
@@ -4909,10 +5823,10 @@ bfd_elfNN_riscv_set_data_segment_info (struct bfd_link_info *info,
 }
 
 /* Relax a section.
-
    Pass 0: Shortens code sequences for LUI/CALL/TPREL/PCREL relocs and
 	   deletes the obsolete bytes.
-   Pass 1: Which cannot be disabled, handles code alignment directives.  */
+   Pass 1: Table jump profiling.
+   Pass 2: Which cannot be disabled, handles code alignment directives.  */
 
 static bool
 _bfd_riscv_relax_section (bfd *abfd, asection *sec,
@@ -4925,19 +5839,17 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec,
   Elf_Internal_Rela *relocs;
   bool ret = false;
   unsigned int i;
-  bfd_vma max_alignment, reserve_size = 0;
+  bfd_vma max_alignment, reserve_size = 0, used_bytes, trimmed_bytes;
   riscv_pcgp_relocs pcgp_relocs;
   static asection *first_section = NULL;
+  riscv_jvt_htab_t *jvt_htab = NULL;
 
   *again = false;
 
-  if (bfd_link_relocatable (info)
-      || sec->sec_flg0
-      || sec->reloc_count == 0
-      || (sec->flags & SEC_RELOC) == 0
-      || (sec->flags & SEC_HAS_CONTENTS) == 0
+  if (bfd_link_relocatable (info) || sec->sec_flg0 || sec->reloc_count == 0
+      || (sec->flags & SEC_RELOC) == 0 || (sec->flags & SEC_HAS_CONTENTS) == 0
       || (info->disable_target_specific_optimizations
-	  && info->relax_pass == 0)
+	  && info->relax_pass <= RELAX_PASS_CAN_BE_DISABLED)
       /* The exp_seg_relro_adjust is enum phase_enum (0x4),
 	 and defined in ld/ldexp.h.  */
       || *(htab->data_segment_phase) == 4)
@@ -4958,6 +5870,16 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec,
   else if (!(relocs = _bfd_elf_link_read_relocs (abfd, sec, NULL, NULL,
 						 info->keep_memory)))
     goto fail;
+  /* Read this BFD's contents if we haven't done so already.  */
+  if (!data->this_hdr.contents
+      && !bfd_malloc_and_get_section (abfd, sec, &data->this_hdr.contents))
+    goto fail;
+
+  /* Read this BFD's symbols if we haven't done so already.  */
+  if (symtab_hdr->sh_info != 0 && !symtab_hdr->contents
+      && !(symtab_hdr->contents = (unsigned char *) bfd_elf_get_elf_syms (
+	     abfd, symtab_hdr, symtab_hdr->sh_info, 0, NULL, NULL, NULL)))
+    goto fail;
 
   /* Estimate the maximum alignment for all output sections once time
      should be enough.  */
@@ -4968,6 +5890,96 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec,
       htab->max_alignment = max_alignment;
     }
 
+  /* relax_trip 0 / JVT_PROFILING_RECORD_SYM:
+	Record symbol address and expected size saving of each relocation
+	that can be replaced by table jump instructions.
+
+     relax_trip 1 / JVT_PROFILING_RANK:
+	Rank the best ZCMT_JT_NUM_ENTRIES relocations to replace for cm.jt and
+	the best ZCMT_JALT_NUM_ENTRIES relocations for cm.jalt in terms of the
+	total size saved.
+
+     relax_trip 2 / JVT_PROFILING_DETERMINE:
+	Check if table jump can reduce the size, and delete the whole table
+	jump section if the size will not be reduced.
+	If table jump can save size, and then we replace all targeted
+	instructions/instruction pairs (e.g. auipc+jalr) to table jump
+	instructions with the index encoded. Then add relocations to the jump
+	table.
+
+     relax_trip 3 / JVT_PROFILING_TRIM:
+	Trim unused slots in the table jump section.  */
+
+  if (info->relax_pass == RELAX_PASS_TABLE_JUMP_PROFILING
+      && riscv_use_jvt (info))
+    {
+      if (!riscv_ensure_jvt_htab (htab))
+	goto fail;
+      jvt_htab = htab->jvt_htab;
+
+      riscv_relax_delete_bytes = _riscv_relax_delete_immediate;
+      /* Avoid size benefits of relocations to be recorded multiple times.  */
+      if (info->relax_trip == JVT_PROFILING_RECORD_SYM
+	  && *(htab->data_segment_phase) != 0)
+	return true;
+
+      /* Rank the entries, and calculate the expected total saving.  */
+      else if (info->relax_trip == JVT_PROFILING_RANK)
+	{
+	  *again = true;
+	  /* Profiling stage finished.  */
+	  if (jvt_htab->end_idx != 0)
+	    return true;
+
+	  riscv_jvt_args args = {jvt_htab, 0, 0};
+	  /* Estimate size benefits if table jump is used.  */
+	  riscv_jvt_profiling (jvt_htab, &args);
+	  if (jvt_htab->end_idx > 0
+	      && (!bfd_set_section_alignment (jvt_htab->jvt_sec, 6)
+		  || !bfd_set_section_size
+		       (jvt_htab->jvt_sec,
+			(bfd_size_type) jvt_htab->end_idx
+			* RISCV_ELF_WORD_BYTES)))
+	    goto fail;
+	  return true;
+	}
+
+      /* Generate table-jump instructions only if profiling chose a
+	 positive-benefit table and sized .riscv.jvt.  */
+      else if (info->relax_trip == JVT_PROFILING_DETERMINE)
+	{
+	  if (jvt_htab->end_idx <= 0 || jvt_htab->jvt_sec->size == 0)
+	    return true;
+
+	  *again = true;
+	}
+
+      /* Trim unused slots in the table jump section.  */
+      else if (info->relax_trip == JVT_PROFILING_TRIM)
+	{
+	  /* Table jump entry section is trimmed.  */
+	  if (jvt_htab->end_idx < 0)
+	    return true;
+
+	  used_bytes = jvt_htab->end_idx * RISCV_ELF_WORD_BYTES;
+	  if (jvt_htab->jvt_sec->size <= used_bytes)
+	    {
+	      jvt_htab->end_idx = -1;
+	      return true;
+	    }
+
+	  trimmed_bytes = jvt_htab->jvt_sec->size - used_bytes;
+	  /* Trim unused slots.  */
+	  if (!riscv_relax_delete_bytes (jvt_htab->jvt_sec->owner,
+					 jvt_htab->jvt_sec, used_bytes,
+					 trimmed_bytes, info, NULL, NULL, false))
+	    return false;
+	  /* Mark table jump profiling stage as completed.  */
+	  jvt_htab->end_idx = -1;
+	  return true;
+	}
+    }
+
   /* Examine and consider relaxing each reloc.  */
   for (i = 0; i < sec->reloc_count; i++)
     {
@@ -4981,17 +5993,49 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec,
 
       relax_func = NULL;
       riscv_relax_delete_bytes = NULL;
-      if (info->relax_pass == 0)
+      if (info->relax_pass == RELAX_PASS_TABLE_JUMP_PROFILING)
+	{
+	  if (!riscv_use_jvt (info))
+	    return true;
+	  riscv_relax_delete_bytes = _riscv_relax_delete_immediate;
+
+	  if ((type == R_RISCV_CALL || type == R_RISCV_CALL_PLT
+	       || type == R_RISCV_JAL)
+	      && (i == sec->reloc_count - 1
+		  || ELFNN_R_TYPE ((rel + 1)->r_info) != R_RISCV_RELAX
+		  || rel->r_offset != (rel + 1)->r_offset))
+	    continue;
+
+	  if (info->relax_trip == JVT_PROFILING_RECORD_SYM)
+	    {
+	      if (type == R_RISCV_CALL || type == R_RISCV_CALL_PLT
+		  || type == R_RISCV_JAL)
+		relax_func = _bfd_riscv_jvt_record;
+	      else
+		continue;
+	      *again = true;
+	      i++;
+	    }
+	  else if (info->relax_trip == JVT_PROFILING_DETERMINE)
+	    {
+	      if (type == R_RISCV_CALL || type == R_RISCV_CALL_PLT
+		  || type == R_RISCV_JAL)
+		relax_func = _bfd_riscv_jvt_mark;
+	      else
+		continue;
+	      i++;
+	    }
+	}
+      else if (info->relax_pass == RELAX_PASS_SHORTEN_LUI_CALL_TRREL_PCREL)
 	{
-	  if (type == R_RISCV_CALL
-	      || type == R_RISCV_CALL_PLT)
+	  if (type == R_RISCV_CALL || type == R_RISCV_CALL_PLT)
 	    relax_func = _bfd_riscv_relax_call;
-	  else if (type == R_RISCV_HI20
-		   || type == R_RISCV_LO12_I
+	  else if (type == R_RISCV_JAL)
+	    relax_func = _bfd_riscv_relax_jal;
+	  else if (type == R_RISCV_HI20 || type == R_RISCV_LO12_I
 		   || type == R_RISCV_LO12_S)
 	    relax_func = _bfd_riscv_relax_lui;
-	  else if (type == R_RISCV_TPREL_HI20
-		   || type == R_RISCV_TPREL_ADD
+	  else if (type == R_RISCV_TPREL_HI20 || type == R_RISCV_TPREL_ADD
 		   || type == R_RISCV_TPREL_LO12_I
 		   || type == R_RISCV_TPREL_LO12_S)
 	    relax_func = _bfd_riscv_relax_tls_le;
@@ -5015,7 +6059,8 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec,
 	  /* Skip over the R_RISCV_RELAX.  */
 	  i++;
 	}
-      else if (info->relax_pass == 1 && type == R_RISCV_ALIGN)
+      else if (info->relax_pass == RELAX_PASS_ALIGNMENT
+	       && type == R_RISCV_ALIGN)
 	{
 	  relax_func = _bfd_riscv_relax_align;
 	  riscv_relax_delete_bytes = _riscv_relax_delete_immediate;
@@ -5025,20 +6070,6 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec,
 
       data->relocs = relocs;
 
-      /* Read this BFD's contents if we haven't done so already.  */
-      if (!data->this_hdr.contents
-	  && !bfd_malloc_and_get_section (abfd, sec, &data->this_hdr.contents))
-	goto fail;
-
-      /* Read this BFD's symbols if we haven't done so already.  */
-      if (symtab_hdr->sh_info != 0
-	  && !symtab_hdr->contents
-	  && !(symtab_hdr->contents =
-	       (unsigned char *) bfd_elf_get_elf_syms (abfd, symtab_hdr,
-						       symtab_hdr->sh_info,
-						       0, NULL, NULL, NULL)))
-	goto fail;
-
       /* Get the value of the symbol referred to by the reloc.  */
       if (ELFNN_R_SYM (rel->r_info) < symtab_hdr->sh_info)
 	{
@@ -5384,6 +6415,36 @@ riscv_elf_obj_attrs_arg_type (obj_attr_tag_t tag)
   return (tag & 1) != 0 ? ATTR_TYPE_FLAG_STR_VAL : ATTR_TYPE_FLAG_INT_VAL;
 }
 
+static bool
+riscv_final_link (bfd *abfd, struct bfd_link_info *info)
+{
+  struct riscv_elf_link_hash_table *htab = riscv_elf_hash_table (info);
+  riscv_jvt_htab_t *jvt_htab = htab->jvt_htab;
+
+  if (riscv_use_jvt (info)
+      && !riscv_define_jvt_base_symbol (abfd, info))
+    return false;
+
+  if (!_bfd_elf_final_link (abfd, info))
+    return false;
+
+  if (riscv_use_jvt (info)
+      && jvt_htab != NULL
+      && jvt_htab->jvt_sec != NULL
+      && jvt_htab->jvt_sec->size != 0
+      && (jvt_htab->jvt_sec->flags & SEC_EXCLUDE) == 0)
+    {
+      asection *sec = jvt_htab->jvt_sec;
+      asection *out_sec = sec->output_section;
+
+      if (!bfd_set_section_contents (abfd, out_sec, jvt_htab->jvt_addrs,
+				     (file_ptr) sec->output_offset, sec->size))
+	return false;
+    }
+
+  return true;
+}
+
 /* Do not choose mapping symbols as a function name.  */
 
 static bfd_size_type
@@ -5549,6 +6610,7 @@ elfNN_riscv_merge_gnu_properties (struct bfd_link_info *info, bfd *abfd,
 #define bfd_elfNN_mkobject			elfNN_riscv_mkobject
 #define bfd_elfNN_get_synthetic_symtab		\
   elfNN_riscv_get_synthetic_symtab
+#define bfd_elfNN_bfd_final_link riscv_final_link
 
 #define elf_backend_reloc_type_class		riscv_reloc_type_class
 #define elf_backend_copy_indirect_symbol	riscv_elf_copy_indirect_symbol
-- 
2.43.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.