[PATCH 1/3] RISC-V: Add ZCMT table jump relaxation

Jerry Zhang Jian jerry.zhangjian@sifive.com
Mon Jan 12 13:29:32 GMT 2026


From: Kito Cheng <kito.cheng@sifive.com>

Add a ZCMT table jump relaxation pass in the linker, including JVT
section handling and related relax flow updates. Define the ZCMT
relocation and table jump constants, and add --relax-zcmt and
--no-relax-zcmt to control the feature.

Signed-off-by: Kito Cheng <kito.cheng@sifive.com>
Co-Authored-by: Jerry Zhang Jian <jerry.zhangjian@sifive.com>
Co-Authored-by: Hau Hsu <hau.hsu@sifive.com>
Co-Authored-by: Charlie Keaney <charlie.keaney@embecosm.com>
Co-Authored-by: Mary Bennett <mary.bennett@embecosm.com>
Co-Authored-by: Nandni Jamnadas <nandni.jamnadas@embecosm.com>
Co-Authored-by: Sinan Lin <sinan.lin@linux.alibaba.com>
Co-Authored-by: Simon Cook <simon.cook@embecosm.com>
Co-Authored-by: Shihua Liao <shihua@iscas.ac.cn>
Co-Authored-by: Yulong Shi <yulong@iscas.ac.cn>
---
 bfd/elfnn-riscv.c        | 903 +++++++++++++++++++++++++++++++++++++--
 bfd/elfxx-riscv.h        |  21 +
 include/elf/riscv.h      |   5 +
 include/opcode/riscv.h   |   9 +
 ld/emultempl/riscvelf.em |  17 +-
 ld/ldlex.h               |   4 +-
 6 files changed, 916 insertions(+), 43 deletions(-)

diff --git a/bfd/elfnn-riscv.c b/bfd/elfnn-riscv.c
index 345b3bb96fd..6f3cfe11bd1 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"
@@ -208,6 +209,52 @@ elfNN_riscv_mkobject (bfd *abfd)
 #include "elf/common.h"
 #include "elf/internal.h"
 
+/* Per-slot data for JVT entries.  */
+typedef struct
+{
+  unsigned int benefit;
+  unsigned int symndx;
+  bfd *input_bfd;
+  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 records all possible
+     entries and will be ranked to put most beneficial ones into jvt
+     section.  */
+  htab_t jt_htab;
+  htab_t jalt_htab;
+
+  /* Array of addresses that will be put into jvt section.  */
+  uintNN_t *jvt_addrs;
+
+  asection *jvt_sec;
+  bfd *jvt_sec_owner;
+
+  /* end_idx is used to calculate size of used slots at table jump section,
+     and it is set to -1 if the profiling stage completed.  */
+  int end_idx;
+  unsigned int total_benefits;
+
+  /* Unified slot array for ranked JVT entries.  */
+  riscv_jvt_slot_t *slots;
+} riscv_jvt_htab_t;
+
+typedef struct
+{
+  unsigned int index;
+
+  /* Record input_bfd and symbol index so that we can distinguish local
+   functions in different input bfds.  */
+  bfd *input_bfd;
+  unsigned long symndx;
+
+  unsigned int benefit;
+  const char *name;
+} riscv_jvt_htab_entry;
+
 struct riscv_elf_link_hash_table
 {
   struct elf_link_hash_table elf;
@@ -246,6 +293,8 @@ 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;
 };
 
 /* Instruction access functions. */
@@ -312,6 +361,53 @@ 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)
+{
+  /* Document:
+     https://docs.oracle.com/cd/E19683-01/816-7529/chapter6-79797/index.html
+
+     > a symbol table section's sh_info section header member holds the
+     > symbol table index for the first non-local symbol.  */
+  return symndx < symtab_hdr->sh_info;
+}
+
+static const char *
+riscv_get_symbol_name (bfd *abfd, unsigned long symndx)
+{
+  Elf_Internal_Shdr *symtab_hdr = &elf_symtab_hdr (abfd);
+  const char *name;
+
+  if (!symtab_hdr->contents)
+    return NULL;
+
+  if (symndx < symtab_hdr->sh_info)
+    {
+      /* A local symbol.  */
+      Elf_Internal_Sym *sym
+	= ((Elf_Internal_Sym *) symtab_hdr->contents + symndx);
+      name = bfd_elf_sym_name (abfd, symtab_hdr, sym, NULL);
+    }
+  else
+    {
+      struct elf_link_hash_entry *h;
+      unsigned indx = symndx - symtab_hdr->sh_info;
+      h = elf_sym_hashes (abfd)[indx];
+      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;
+
+      if (h != NULL && h->type != STT_GNU_IFUNC)
+	name = h->root.root.string;
+      else
+	/* We do not handle STT_GNU_IFUNC currently.  */
+	return NULL;
+    }
+
+  return name;
+}
+
 /* PLT/GOT stuff.  */
 #define PLT_HEADER_INSNS 8
 #define PLT_ENTRY_INSNS 4
@@ -538,6 +634,17 @@ 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)
+{
+  /* PR27584, local and empty symbols.  Since they are usually
+     generated for pcrel relocations.  */
+  return (!strcmp (name, "")
+	  || _bfd_elf_is_local_label_name (abfd, name)
+	  /* PR27916, mapping symbols.  */
+	  || riscv_elf_is_mapping_symbols (name));
+}
+
 /* Create an entry in an RISC-V ELF linker hash table.  */
 
 static struct bfd_hash_entry *
@@ -568,6 +675,147 @@ link_hash_newfunc (struct bfd_hash_entry *entry,
   return entry;
 }
 
+static hashval_t
+riscv_jvt_htab_hash (const void *entry)
+{
+  const riscv_jvt_htab_entry *e = entry;
+  unsigned int id = e->input_bfd->id;
+  unsigned int symndx = e->symndx;
+  Elf_Internal_Shdr *symtab_hdr = &elf_symtab_hdr (e->input_bfd);
+
+  if (riscv_is_local_symbol (symtab_hdr, symndx))
+    {
+      /* For local symbols, use the symbol table entry index and input_bfd's id
+	 to hash (Cantor pairing function). */
+      return (id + symndx) * (id + symndx + 1) / 2 + id;
+    }
+  else
+    {
+      /* For global symbols, use the symbol name.  */
+      const char *name = riscv_get_symbol_name (e->input_bfd, symndx);
+      if (name == NULL)
+	return 0;
+      return htab_hash_string (name);
+    }
+}
+
+static int
+riscv_jvt_htab_entry_eq (const void *entry1, const void *entry2)
+{
+  const riscv_jvt_htab_entry *e1 = entry1, *e2 = entry2;
+  unsigned int symndx1 = e1->symndx, symndx2 = e2->symndx;
+  Elf_Internal_Shdr *symtab_hdr1 = &elf_symtab_hdr (e1->input_bfd),
+		    *symtab_hdr2 = &elf_symtab_hdr (e2->input_bfd);
+
+  if (riscv_is_local_symbol (symtab_hdr1, symndx1)
+      && riscv_is_local_symbol (symtab_hdr2, symndx2))
+    {
+      return (e1->input_bfd->id == e2->input_bfd->id)
+	     && (e1->symndx == e2->symndx);
+    }
+  else if (!riscv_is_local_symbol (symtab_hdr1, symndx1)
+	   && !riscv_is_local_symbol (symtab_hdr2, symndx2))
+    {
+      const char *name1 = riscv_get_symbol_name (e1->input_bfd, symndx1);
+      const char *name2 = riscv_get_symbol_name (e2->input_bfd, symndx2);
+      if (name1 == NULL || name2 == NULL)
+	return name1 == name2;
+      return (strcmp (name1, name2) == 0);
+    }
+  /* One is global and the other one is local, just return false.  */
+  return false;
+}
+
+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->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 (htab_t htab, bfd *abfd, unsigned int symndx,
+			unsigned int benefit, const char *name)
+{
+  riscv_jvt_htab_entry search = {.index = 0,
+				 .input_bfd = abfd,
+				 .symndx = symndx,
+				 .benefit = 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)->input_bfd = abfd;
+      (*slot)->symndx = symndx;
+      (*slot)->benefit = benefit;
+      (*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
@@ -645,6 +893,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);
 }
 
@@ -920,6 +1174,95 @@ 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;
+  enum
+  {
+    UNDETERMINED = -1,
+    NOT_USE_ZCMT = 0,
+    USE_ZCMT = 1,
+  };
+  static int cache_result = UNDETERMINED;
+  static struct bfd_link_info *cache_info = NULL;
+  struct riscv_elf_link_hash_table *htab;
+
+  if (cache_result != UNDETERMINED && cache_info == info)
+    return cache_result;
+
+  htab = riscv_elf_hash_table (info);
+  /* If relax is disabled by user or not linking executable, table jump insn
+     will not be generated.  */
+  if (info->disable_target_specific_optimizations >= 1
+      || !bfd_link_executable (info) || !htab->params->relax_zcmt)
+    {
+      cache_result = NOT_USE_ZCMT;
+      cache_info = info;
+      return cache_result;
+    }
+
+  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))
+    {
+      cache_result = NOT_USE_ZCMT;
+      cache_info = info;
+      return cache_result;
+    }
+
+  cache_result = riscv_subset_supports (&riscv_rps_ld_out, "zcmt");
+  cache_info = info;
+  riscv_release_subset_list (&subsets);
+
+  return cache_result;
+}
+
+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));
+
+      if (sec == NULL || !bfd_set_section_alignment (sec, 6)
+	  || !bfd_set_section_size (sec, 256 * RISCV_ELF_WORD_BYTES))
+	return false;
+
+      htab->jvt_htab->jvt_sec = sec;
+      htab->jvt_htab->jvt_sec_owner = abfd;
+    }
+
+  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.  */
@@ -1266,6 +1609,9 @@ riscv_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 	}
     }
 
+  if (!riscv_elf_create_jvt_section (abfd, info))
+    return false;
+
   return true;
 }
 
@@ -1699,9 +2045,11 @@ riscv_elf_late_size_sections (bfd *output_bfd, struct bfd_link_info *info)
   bfd *dynobj;
   asection *s;
   bfd *ibfd;
+  riscv_jvt_htab_t *jvt_htab;
 
   htab = riscv_elf_hash_table (info);
   BFD_ASSERT (htab != NULL);
+  jvt_htab = htab->jvt_htab;
   dynobj = htab->elf.dynobj;
   if (dynobj == NULL)
     return true;
@@ -1719,6 +2067,30 @@ riscv_elf_late_size_sections (bfd *output_bfd, struct bfd_link_info *info)
 	}
     }
 
+  struct bfd_link_hash_entry *bh = NULL;
+
+  if (riscv_use_jvt (info) && jvt_htab != NULL && jvt_htab->jvt_sec)
+    {
+      s = jvt_htab->jvt_sec;
+
+      BFD_ASSERT (s != NULL);
+
+      s->contents = (bfd_byte *) bfd_alloc (s->owner, s->size);
+      s->alloced = 1;
+
+      if (s->contents == NULL)
+	return false;
+
+      if (s->output_section == NULL)
+	return false;
+
+      if (!_bfd_generic_link_add_one_symbol (
+	    info, output_bfd, RISCV_TABLE_JUMP_BASE_SYMBOL, BSF_GLOBAL, s,
+	    (bfd_vma) 0, (const char *) NULL, true,
+	    get_elf_backend_data (output_bfd)->collect, &bh))
+	return false;
+    }
+
   /* Set up .got offsets for local syms, and space for local dynamic
      relocs.  */
   for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link.next)
@@ -2042,6 +2414,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;
@@ -2986,6 +3361,17 @@ riscv_elf_relocate_section (bfd *output_bfd,
 	  }
 	  break;
 
+	case R_RISCV_TABLE_JUMP:
+	  {
+	    bfd_vma insn = bfd_getl16 (contents + rel->r_offset);
+	    unsigned int tbl_index = EXTRACT_ZCMT_INDEX (insn);
+	    if (!riscv_ensure_jvt_htab (htab))
+	      return false;
+	    htab->jvt_htab->jvt_addrs[tbl_index] = relocation + rel->r_addend;
+	    htab->jvt_htab->jvt_addrs[tbl_index] &= ~(bfd_vma) 1;
+	  }
+	  break;
+
 	case R_RISCV_CALL:
 	case R_RISCV_CALL_PLT:
 	  /* Handle a call to an undefined weak function.  This won't be
@@ -4811,6 +5197,228 @@ _riscv_relax_delete_immediate (bfd *abfd,
 				    link_info, p, 0, sec->size);
 }
 
+/*
+ Return jt jump table if rd is x0, or jalt jump table if rd is other registers.
+ */
+
+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);
+  riscv_jvt_htab_t *jvt_htab;
+
+  if (htab == NULL || !riscv_ensure_jvt_htab (htab))
+    return NULL;
+
+  jvt_htab = htab->jvt_htab;
+
+  if (link_reg == 0)
+    return jvt_htab->jt_htab;
+  if (link_reg == X_RA)
+    return jvt_htab->jalt_htab;
+
+  return NULL;
+}
+
+/* Record JAL or CALL in jump table hash.  */
+
+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;
+  bfd_vma jal = bfd_getl32 (contents + rel->r_offset);
+  int type = ELFNN_R_TYPE (rel->r_info);
+  unsigned int rd = (jal >> OP_SH_RD) & OP_MASK_RD;
+  htab_t tbljal_htab = riscv_get_jvt_htab (link_info, rd);
+  const char *name = riscv_get_symbol_name (abfd, ELFNN_R_SYM (rel->r_info));
+  unsigned long r_symndx = ELFNN_R_SYM (rel->r_info);
+
+  if (tbljal_htab == NULL || name == NULL
+      || riscv_is_special_symbol_name (abfd, name))
+    return true;
+
+  unsigned int benefit = 0;
+  if (type == R_RISCV_CALL || type == R_RISCV_CALL_PLT)
+    benefit = (4 + 4) - 2; /* AUIPC + JALR -> CM.[JT,JALT].  */
+  else if (type == R_RISCV_JAL)
+    benefit = 4 - 2; /* JAL -> CM.[JT,JALT].  */
+  else
+    BFD_ASSERT (false);
+
+  return riscv_update_jvt_entry (tbljal_htab, abfd, r_symndx, benefit, name);
+}
+
+/* Relax JAL to CM.[JT,JALT].  */
+
+static bool
+_bfd_riscv_relax_jal (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;
+  bfd_vma jal = bfd_getl32 (contents + rel->r_offset);
+
+  if (link_info->relax_pass == RELAX_PASS_SHORTEN_LUI_CALL_TRREL_PCREL
+      && ((jal ^ MATCH_CM_JALT) & MASK_CM_JALT) == 0)
+    {
+      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, 2,
+				       link_info, pcgp_relocs, rel);
+    }
+  return true;
+}
+
+static bfd_vma
+riscv_get_symbol_addr (bfd *input_bfd, unsigned int symndx)
+{
+  Elf_Internal_Shdr *symtab_hdr = &elf_symtab_hdr (input_bfd);
+  if (riscv_is_local_symbol (symtab_hdr, symndx))
+    {
+      if (!symtab_hdr->contents)
+	return 0;
+
+      Elf_Internal_Sym *sym
+	= ((Elf_Internal_Sym *) symtab_hdr->contents + symndx);
+      bfd_vma sec_offset
+	= sec_addr (elf_elfsections (input_bfd)[sym->st_shndx]->bfd_section);
+      return sec_offset + sym->st_value;
+    }
+  else /* global symbol */
+    {
+      struct elf_link_hash_entry *h;
+      unsigned indx = symndx - symtab_hdr->sh_info;
+      h = elf_sym_hashes (input_bfd)[indx];
+      return sec_addr (h->root.u.def.section) + h->root.u.def.value;
+    }
+}
+
+/* Iterate all jump table entris and update addresses after alignment relax
+   pass.  */
+static void
+riscv_update_jvt_addrs (struct bfd_link_info *link_info)
+{
+  struct riscv_elf_link_hash_table *htab = riscv_elf_hash_table (link_info);
+  riscv_jvt_htab_t *jvt_htab = htab->jvt_htab;
+  if (jvt_htab == NULL)
+    return;
+  for (uintNN_t i = 0; i < ZCMT_TOTAL_ENTRIES; i++)
+    {
+      riscv_jvt_slot_t *slot = &jvt_htab->slots[i];
+      if (slot->benefit != 0)
+	{
+	  bfd_vma addr = riscv_get_symbol_addr (slot->input_bfd, slot->symndx);
+	  if (addr != 0)
+	    jvt_htab->jvt_addrs[i] = addr;
+	}
+    }
+}
+
+typedef struct
+{
+  riscv_jvt_htab_t *htab;
+  unsigned int start;
+  unsigned int end;
+} riscv_jvt_args;
+
+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;
+
+  /* Search insert position using binary search (descending order).  */
+  unsigned int left = arg->start;
+  unsigned int right = arg->end + 1;
+
+  while (left < right)
+    {
+      unsigned int mid = (left + right) / 2;
+      if (slots[mid].benefit == entry->benefit)
+	{
+	  left = mid;
+	  break;
+	}
+      else if (slots[mid].benefit == 0 || slots[mid].benefit < entry->benefit)
+	right = mid;
+      else
+	left = mid + 1;
+    }
+
+  /* Shift slots to make room for the new entry.  */
+  for (unsigned int idx = arg->end; idx > left; idx--)
+    slots[idx] = slots[idx - 1];
+
+  if (left <= arg->end)
+    {
+      slots[left].benefit = entry->benefit;
+      slots[left].name = entry->name;
+      slots[left].input_bfd = entry->input_bfd;
+      slots[left].symndx = entry->symndx;
+    }
+
+  return true;
+}
+
+static bool
+riscv_record_jvt_index (htab_t htab, riscv_jvt_args *args)
+{
+  unsigned int idx;
+  riscv_jvt_htab_t *jvt_htab = args->htab;
+  riscv_jvt_slot_t *slots = jvt_htab->slots;
+  riscv_jvt_htab_entry *entry = NULL;
+
+  for (idx = args->start; idx <= args->end && slots[idx].benefit != 0; idx++)
+    {
+      riscv_jvt_htab_entry search = {.index = 0,
+				     .input_bfd = slots[idx].input_bfd,
+				     .symndx = slots[idx].symndx,
+				     .name = NULL,
+				     .benefit = 0};
+      entry = htab_find (htab, &search);
+
+      BFD_ASSERT (entry != NULL);
+      entry->index = idx + 1;
+      jvt_htab->total_benefits += slots[idx].benefit;
+    }
+
+  /* True if there is at least one entry in table jump section.  */
+  if (entry && entry->index)
+    jvt_htab->end_idx = entry->index;
+
+  return true;
+}
+
+static bool
+riscv_jvt_profiling (riscv_jvt_htab_t *jvt_htab, riscv_jvt_args *args)
+{
+  args->start = ZCMT_JT_BEGIN, args->end = ZCMT_JT_END;
+  /* Do a ranking.  */
+  htab_traverse (jvt_htab->jt_htab, riscv_ranking_jvt, args);
+  riscv_record_jvt_index (jvt_htab->jt_htab, args);
+
+  args->start = ZCMT_JALT_BEGIN, args->end = ZCMT_JALT_END;
+  htab_traverse (jvt_htab->jalt_htab, riscv_ranking_jvt, args);
+  riscv_record_jvt_index (jvt_htab->jalt_htab, args);
+  return true;
+}
+
 /* Delete the bytes for R_RISCV_DELETE relocs.  */
 
 static bool
@@ -4870,7 +5478,57 @@ 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 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_byte *jump_inst_location = NULL;
+
+  if (type == R_RISCV_JAL)
+    jump_inst_location = contents + rel->r_offset;
+  else if (type == R_RISCV_CALL || type == R_RISCV_CALL_PLT)
+    jump_inst_location = contents + rel->r_offset + 4;
+  else /* We only relax JAL/CALL/CALL_PLT.  */
+    BFD_ASSERT (false);
+
+  bfd_vma jump_inst = bfd_getl32 (jump_inst_location);
+  int rd = (jump_inst >> OP_SH_RD) & OP_MASK_RD;
+  htab_t tbljal_htab = riscv_get_jvt_htab (link_info, rd);
+
+  /* Check if it uses a valid link register.  */
+  if (tbljal_htab == NULL)
+    return true;
+
+  unsigned long r_symndx = ELFNN_R_SYM (rel->r_info);
+
+  riscv_jvt_htab_entry search = {.index = 0,
+				 .input_bfd = abfd,
+				 .symndx = r_symndx,
+				 .benefit = 0,
+				 .name = NULL};
+  riscv_jvt_htab_entry *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)
+    {
+      bfd_vma target = MATCH_CM_JALT | ENCODE_ZCMT_INDEX (entry->index - 1);
+      bfd_putl32 (target, contents + rel->r_offset);
+    }
+  return true;
+}
+
+/* Relax AUIPC + JALR into JAL or CM.[JT,JALT].  */
 
 static bool
 _bfd_riscv_relax_call (bfd *abfd, asection *sec, asection *sym_sec,
@@ -4889,6 +5547,19 @@ _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);
+
+  /* Relax a table jump instruction that is marked.  */
+  if (((auipc ^ MATCH_CM_JALT) & MASK_CM_JALT) == 0)
+    {
+      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, 6,
+				       link_info, pcgp_relocs, rel);
+    }
+
   /* 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.
@@ -4902,14 +5573,13 @@ _bfd_riscv_relax_call (bfd *abfd, asection *sec, asection *sym_sec,
     }
 
   /* See if this function call can be shortened.  */
-  if (!VALID_JTYPE_IMM (foff) && !(!bfd_link_pic (link_info) && near_zero))
+  if (!VALID_JTYPE_IMM (foff) && !(!bfd_link_pic (link_info) && near_zero)
+      && link_info->relax_pass != RELAX_PASS_TABLE_JUMP_PROFILING)
     return true;
 
   /* 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);
 
@@ -4962,6 +5632,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 table jump section because it is not affecting the
+	 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)))
@@ -5368,10 +6043,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
+   Pass 0: Table jump profiling
+   Pass 1: 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 2: Which cannot be disabled, handles code alignment directives.  */
 
 static bool
 _bfd_riscv_relax_section (bfd *abfd, asection *sec,
@@ -5384,19 +6059,18 @@ _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;
+  struct elf_link_hash_entry *jvt_sym;
 
   *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)
@@ -5417,6 +6091,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.  */
@@ -5427,6 +6111,104 @@ _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);
+	  return true;
+	}
+
+      /* Skip generating table jump instructions if they do not help reduce
+       * code size.   */
+      else if (info->relax_trip == JVT_PROFILING_DETERMINE)
+	{
+	  /* Check if table jump can save size. Skip generating table
+	    jump instruction if not.  */
+	  if (jvt_htab->end_idx >= 0
+	      && jvt_htab->total_benefits
+		   <= (unsigned int) jvt_htab->end_idx * RISCV_ELF_WORD_BYTES
+	      && jvt_htab->jvt_sec->size > 0U)
+	    {
+	      jvt_sym = elf_link_hash_lookup (elf_hash_table (info),
+					      RISCV_TABLE_JUMP_BASE_SYMBOL,
+					      false, false, true);
+	      jvt_sym->root.u.def.section = bfd_abs_section_ptr;
+	      return riscv_relax_delete_bytes (jvt_htab->jvt_sec->owner,
+					       jvt_htab->jvt_sec, 0,
+					       jvt_htab->jvt_sec->size, info,
+					       NULL, NULL);
+	    }
+
+	  else if (jvt_htab->jvt_sec->size == 0)
+	    return true;
+	  else if (jvt_htab->jvt_sec->size > 0)
+	    *again = true;
+	}
+
+      /* Trim the unused slot at the table jump section.
+	  TODO: skip generating entries if its saving is less than
+	 RISCV_ELF_WORD_BYTES. We should skip those insns at the relax trip 2
+	 without deleting bytes.  */
+      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;
+	  trimmed_bytes
+	    = (ZCMT_TOTAL_ENTRIES - jvt_htab->end_idx) * RISCV_ELF_WORD_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))
+	    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++)
     {
@@ -5440,17 +6222,38 @@ _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;
+	  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;
+	    }
+	  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;
+	    }
+	}
+      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;
@@ -5464,15 +6267,18 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec,
 	  riscv_relax_delete_bytes = _riscv_relax_delete_piecewise;
 
 	  /* Only relax this reloc if it is paired with R_RISCV_RELAX.  */
-	  if (i == sec->reloc_count - 1
-	      || ELFNN_R_TYPE ((rel + 1)->r_info) != R_RISCV_RELAX
-	      || rel->r_offset != (rel + 1)->r_offset)
+	  if (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;
 
 	  /* Skip over the R_RISCV_RELAX.  */
-	  i++;
+	  if (type != R_RISCV_JAL)
+	    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;
@@ -5482,20 +6288,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)
 	{
@@ -5841,6 +6633,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;
+
+  /* Update jump table addresses before bfd_elf_final_link because it freed
+     memory. */
+  if (riscv_use_jvt (info)
+      /* jvt_sec is not created if no relocation happened, so we
+	 need to check section pointer here.  */
+      && jvt_htab != NULL && jvt_htab->jvt_sec)
+    riscv_update_jvt_addrs (info);
+
+  if (!_bfd_elf_final_link (abfd, info))
+    return false;
+
+  if (riscv_use_jvt (info) && jvt_htab != NULL && jvt_htab->jvt_sec)
+    {
+      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
@@ -6006,6 +6828,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
diff --git a/bfd/elfxx-riscv.h b/bfd/elfxx-riscv.h
index ed4e78efc83..d3148a8d6cc 100644
--- a/bfd/elfxx-riscv.h
+++ b/bfd/elfxx-riscv.h
@@ -39,6 +39,8 @@ struct riscv_elf_params
   bool relax_gp;
   /* Whether to check if SUB_ULEB128 relocation has non-zero addend.  */
   bool check_uleb128;
+  /* Whether to relax code sequences for zcmt.  */
+  bool relax_zcmt;
 };
 
 extern void riscv_elf32_set_options (struct bfd_link_info *,
@@ -96,6 +98,25 @@ typedef struct
   bool check_unknown_prefixed_ext;
 } riscv_parse_subset_t;
 
+/* List the relxation pass.  */
+enum riscv_relax_pass
+{
+  RELAX_PASS_TABLE_JUMP_PROFILING,
+  RELAX_PASS_SHORTEN_LUI_CALL_TRREL_PCREL,
+  RELAX_PASS_CAN_BE_DISABLED = RELAX_PASS_SHORTEN_LUI_CALL_TRREL_PCREL,
+  RELAX_PASS_ALIGNMENT,
+  RELAX_PASS_END,
+};
+
+/* Trips for jvt profiling relaxation pass.  */
+enum jvt_profiling_trip
+{
+  JVT_PROFILING_RECORD_SYM,
+  JVT_PROFILING_RANK,
+  JVT_PROFILING_DETERMINE,
+  JVT_PROFILING_TRIM,
+};
+
 extern bool
 riscv_parse_subset (riscv_parse_subset_t *,
 		    const char *);
diff --git a/include/elf/riscv.h b/include/elf/riscv.h
index 2ea1ae82eb6..ce888ce9244 100644
--- a/include/elf/riscv.h
+++ b/include/elf/riscv.h
@@ -104,6 +104,7 @@ END_RELOC_NUMBERS (R_RISCV_max)
 #define R_RISCV_GPREL_S (R_RISCV_max + 3)
 #define R_RISCV_TPREL_I (R_RISCV_max + 4)
 #define R_RISCV_TPREL_S (R_RISCV_max + 5)
+#define R_RISCV_TABLE_JUMP (R_RISCV_max + 6)
 
 /* Processor specific flags for the ELF header e_flags field.  */
 
@@ -131,6 +132,10 @@ END_RELOC_NUMBERS (R_RISCV_max)
 /* The name of the global pointer symbol.  */
 #define RISCV_GP_SYMBOL "__global_pointer$"
 
+#define RISCV_TABLE_JUMP_BASE_SYMBOL "__jvt_base$"
+
+#define TABLE_JUMP_SEC_NAME ".riscv.jvt"
+
 /* Processor specific dynamic array tags.  */
 #define DT_RISCV_VARIANT_CC (DT_LOPROC + 1)
 
diff --git a/include/opcode/riscv.h b/include/opcode/riscv.h
index 858fcce6871..49631ddba95 100644
--- a/include/opcode/riscv.h
+++ b/include/opcode/riscv.h
@@ -680,6 +680,15 @@ struct riscv_opcode
    disassembler, and requires special treatment by the assembler.  */
 #define INSN_MACRO		0xffffffff
 
+/* Define Zcmt jump table constants.  */
+#define ZCMT_TOTAL_ENTRIES 256
+#define ZCMT_JT_BEGIN 0
+#define ZCMT_JT_END 31
+#define ZCMT_JT_NUM_ENTRIES (ZCMT_JT_END - ZCMT_JT_BEGIN + 1)
+#define ZCMT_JALT_BEGIN (ZCMT_JT_END + 1)
+#define ZCMT_JALT_END (ZCMT_TOTAL_ENTRIES - 1)
+#define ZCMT_JALT_NUM_ENTRIES (ZCMT_JALT_END - ZCMT_JALT_BEGIN + 1)
+
 /* This is a list of macro expanded instructions.  */
 enum
 {
diff --git a/ld/emultempl/riscvelf.em b/ld/emultempl/riscvelf.em
index 78e1fcde68e..988fd03cd01 100644
--- a/ld/emultempl/riscvelf.em
+++ b/ld/emultempl/riscvelf.em
@@ -26,7 +26,8 @@ fragment <<EOF
 #include "elfxx-riscv.h"
 
 static struct riscv_elf_params params = { .relax_gp = 1,
-					  .check_uleb128 = 0};
+					  .check_uleb128 = 0,
+					  .relax_zcmt = 0};
 EOF
 
 # Define some shell vars to insert bits of code into the standard elf
@@ -36,6 +37,8 @@ PARSE_AND_LIST_LONGOPTS=${PARSE_AND_LIST_LONGOPTS}'
     { "no-relax-gp", no_argument, NULL, OPTION_NO_RELAX_GP },
     { "check-uleb128", no_argument, NULL, OPTION_CHECK_ULEB128 },
     { "no-check-uleb128", no_argument, NULL, OPTION_NO_CHECK_ULEB128 },
+    { "relax-zcmt", no_argument, NULL, OPTION_RELAX_ZCMT },
+    { "no-relax-zcmt", no_argument, NULL, OPTION_NO_RELAX_ZCMT },
 '
 
 PARSE_AND_LIST_OPTIONS=${PARSE_AND_LIST_OPTIONS}'
@@ -43,6 +46,8 @@ PARSE_AND_LIST_OPTIONS=${PARSE_AND_LIST_OPTIONS}'
   fprintf (file, _("  --no-relax-gp               Don'\''t perform GP relaxation\n"));
   fprintf (file, _("  --check-uleb128             Check if SUB_ULEB128 has non-zero addend\n"));
   fprintf (file, _("  --no-check-uleb128          Don'\''t check if SUB_ULEB128 has non-zero addend\n"));
+  fprintf (file, _("  --relax-zcmt                Perform Zcmt relaxation\n"));
+  fprintf (file, _("  --no-relax-zcmt             Don'\''t perform Zcmt relaxation (default)\n"));
 '
 
 PARSE_AND_LIST_ARGS_CASES=${PARSE_AND_LIST_ARGS_CASES}'
@@ -61,6 +66,14 @@ PARSE_AND_LIST_ARGS_CASES=${PARSE_AND_LIST_ARGS_CASES}'
     case OPTION_NO_CHECK_ULEB128:
       params.check_uleb128 = 0;
       break;
+
+    case OPTION_RELAX_ZCMT:
+      params.relax_zcmt = 1;
+      break;
+
+    case OPTION_NO_RELAX_ZCMT:
+      params.relax_zcmt = 0;
+      break;
 '
 
 fragment <<EOF
@@ -81,7 +94,7 @@ riscv_elf_before_allocation (void)
 	ENABLE_RELAXATION;
     }
 
-  link_info.relax_pass = 2;
+  link_info.relax_pass = RELAX_PASS_END;
 }
 
 static void
diff --git a/ld/ldlex.h b/ld/ldlex.h
index 24cac1cdfc0..73d30117db7 100644
--- a/ld/ldlex.h
+++ b/ld/ldlex.h
@@ -96,7 +96,7 @@ enum option_values
   OPTION_WARN_ONCE,
   OPTION_WARN_SECTION_ALIGN,
   OPTION_SPLIT_BY_RELOC,
-  OPTION_SPLIT_BY_FILE ,
+  OPTION_SPLIT_BY_FILE,
   OPTION_WHOLE_ARCHIVE,
   OPTION_ADD_DT_NEEDED_FOR_DYNAMIC,
   OPTION_NO_ADD_DT_NEEDED_FOR_DYNAMIC,
@@ -425,6 +425,8 @@ enum option_values
   OPTION_NO_RELAX_GP,
   OPTION_CHECK_ULEB128,
   OPTION_NO_CHECK_ULEB128,
+  OPTION_RELAX_ZCMT,
+  OPTION_NO_RELAX_ZCMT,
   /* Used by emultempl/rxelf.em.  */
   OPTION_NO_FLAG_MISMATCH_WARNINGS,
   OPTION_IGNORE_LMA,
-- 
2.51.0



More information about the Binutils mailing list