diff --git a/bfd/elfnn-riscv.c b/bfd/elfnn-riscv.c
index 5c4bf4bc3cb..46211382071 100644
--- a/bfd/elfnn-riscv.c
+++ b/bfd/elfnn-riscv.c
@@ -203,6 +203,37 @@ elfNN_riscv_mkobject (bfd *abfd)
 #include "elf/common.h"
 #include "elf/internal.h"
 
+/* debug use */
+#define ZCMT_PRINT_TABLE_JUMP_ENTRIES 0
+
+/* Hash table for storing table jump candidate entries.  */
+typedef struct
+{
+  htab_t tbljt_htab;
+  htab_t tbljalt_htab;
+  uintNN_t *tbj_indexes;
+  asection *tablejump_sec;
+  bfd *tablejump_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;
+  int total_saving;
+
+  /* debug use.  */
+  int *savings;
+  const char **names;
+} riscv_table_jump_htab_t;
+
+typedef struct
+{
+  bfd_vma address;
+  unsigned int index;
+
+  /* debug use.  */
+  const char *name;
+  int benefit;
+} riscv_table_jump_htab_entry;
+
 struct riscv_elf_link_hash_table
 {
   struct elf_link_hash_table elf;
@@ -232,6 +263,8 @@ struct riscv_elf_link_hash_table
 
   /* Relocations for variant CC symbols may be present.  */
   int variant_cc;
+
+  riscv_table_jump_htab_t *table_jump_htab;
 };
 
 /* Instruction access functions.  */
@@ -406,6 +439,82 @@ link_hash_newfunc (struct bfd_hash_entry *entry,
   return entry;
 }
 
+static hashval_t
+riscv_table_jump_htab_hash (const void *entry)
+{
+  const riscv_table_jump_htab_entry *e = entry;
+  return (hashval_t)(e->address >> 2);
+}
+
+static int
+riscv_table_jump_htab_entry_eq (const void *entry1, const void *entry2)
+{
+  const riscv_table_jump_htab_entry *e1 = entry1, *e2 = entry2;
+  return e1->address == e2->address;
+}
+
+static bool
+riscv_init_table_jump_htab (riscv_table_jump_htab_t *htab)
+{
+  htab->names = bfd_zmalloc (sizeof (const char *) * 256);
+  htab->savings = bfd_zmalloc (sizeof (unsigned int) * 256);
+  htab->tbj_indexes = bfd_zmalloc (RISCV_ELF_WORD_BYTES * 256);
+  htab->end_idx = 0;
+  htab->total_saving = 0;
+
+  htab->tbljt_htab = htab_create (50, riscv_table_jump_htab_hash,
+			      riscv_table_jump_htab_entry_eq, free);
+  if (htab->tbljt_htab == NULL)
+    return false;
+
+  htab->tbljalt_htab = htab_create (50, riscv_table_jump_htab_hash,
+			      riscv_table_jump_htab_entry_eq, free);
+  return htab->tbljalt_htab != NULL;
+}
+
+static void
+riscv_free_table_jump_htab (riscv_table_jump_htab_t *htab)
+{
+  free (htab->names);
+  free (htab->savings);
+  free (htab->tbj_indexes);
+  htab_delete (htab->tbljt_htab);
+  htab_delete (htab->tbljalt_htab);
+}
+
+static bool
+riscv_update_table_jump_entry (htab_t htab,
+			       bfd_vma addr,
+			       unsigned int benefit,
+			       const char *name)
+{
+  riscv_table_jump_htab_entry search = {addr, 0, NULL, 0};
+  riscv_table_jump_htab_entry *entry = htab_find (htab, &search);
+
+  if (entry == NULL)
+    {
+      riscv_table_jump_htab_entry **slot =
+	(riscv_table_jump_htab_entry **) htab_find_slot (
+	  htab, &search, INSERT);
+
+      BFD_ASSERT (*slot == NULL);
+
+      *slot = (riscv_table_jump_htab_entry *) bfd_zmalloc (
+	    sizeof (riscv_table_jump_htab_entry));
+
+      if (*slot == NULL)
+	return false;
+
+      (*slot)->address = addr;
+      (*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
@@ -470,6 +579,24 @@ riscv_elf_get_local_sym_hash (struct riscv_elf_link_hash_table *htab,
   return &ret->elf;
 }
 
+#if ZCMT_PRINT_TABLE_JUMP_ENTRIES
+static void
+print_tablejump_entries(riscv_table_jump_htab_t *table_jump_htab)
+{
+  if (table_jump_htab->tbj_indexes[0])
+    printf("cm.jt:\n");
+  for (unsigned int z = 0; z < 32 && table_jump_htab->tbj_indexes[z] != 0; z ++)
+    printf ("\tindex=%d, sym name=%s, address=0x%08lx, savings=%u\n",
+	z, table_jump_htab->names[z], table_jump_htab->tbj_indexes[z], table_jump_htab->savings[z]);
+
+  if (table_jump_htab->tbj_indexes[32])
+    printf("cm.jalt:\n");
+  for (unsigned int z = 32; z < 256 && table_jump_htab->tbj_indexes[z] != 0; z ++)
+    printf ("\tindex=%d, sym name=%s, address=0x%08lx, savings=%u\n",
+	z, table_jump_htab->names[z], table_jump_htab->tbj_indexes[z], table_jump_htab->savings[z]);
+}
+#endif
+
 /* Destroy a RISC-V elf linker hash table.  */
 
 static void
@@ -483,6 +610,15 @@ riscv_elf_link_hash_table_free (bfd *obfd)
   if (ret->loc_hash_memory)
     objalloc_free ((struct objalloc *) ret->loc_hash_memory);
 
+  if (ret->table_jump_htab)
+    {
+      #if ZCMT_PRINT_TABLE_JUMP_ENTRIES
+	print_tablejump_entries(ret->table_jump_htab);
+      #endif
+      riscv_free_table_jump_htab (ret->table_jump_htab);
+      free (ret->table_jump_htab);
+    }
+
   _bfd_elf_link_hash_table_free (obfd);
 }
 
@@ -506,6 +642,16 @@ riscv_elf_link_hash_table_create (bfd *abfd)
       return NULL;
     }
 
+  ret->table_jump_htab = (riscv_table_jump_htab_t *) bfd_zmalloc (
+	  sizeof (riscv_table_jump_htab_t));
+
+  if (ret->table_jump_htab == NULL
+	|| !riscv_init_table_jump_htab(ret->table_jump_htab))
+    {
+      riscv_elf_link_hash_table_free (abfd);
+      return NULL;
+    }
+
   ret->max_alignment = (bfd_vma) -1;
   ret->max_alignment_for_gp = (bfd_vma) -1;
 
@@ -725,6 +871,75 @@ bad_static_reloc (bfd *abfd, unsigned r_type, struct elf_link_hash_entry *h)
   return false;
 }
 
+static bool
+riscv_use_table_jump (struct bfd_link_info *info)
+{
+  unsigned xlen = ARCH_SIZE;
+  riscv_subset_list_t subsets;
+  bool ret;
+
+  /* If relax is disabled by user, table jump insn
+     will not be generated.  */
+  if (info->disable_target_specific_optimizations >= 1)
+    return false;
+
+  if (!bfd_link_executable (info))
+    return false;
+
+  bfd *obfd = info->output_bfd;
+  obj_attribute *out_attr = elf_known_obj_attributes_proc (obfd);
+
+  subsets.head = NULL;
+  subsets.tail = 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))
+    return false;
+
+  ret = riscv_subset_supports (&riscv_rps_ld_out, "zcmt");
+  riscv_release_subset_list (&subsets);
+
+  return ret;
+}
+
+static bool
+bfd_elf_riscv_make_tablejump_section (bfd *abfd, struct bfd_link_info *info)
+{
+  asection *sec;
+  struct riscv_elf_link_hash_table *htab;
+  const struct elf_backend_data *bed;
+
+  /* Skip if no Zcmt.  */
+  if (!riscv_use_table_jump (info))
+    return true;
+
+  bed = get_elf_backend_data (abfd);
+  htab = riscv_elf_hash_table (info);
+  sec = bfd_get_linker_section (abfd, TABLE_JUMP_SEC_NAME);
+
+  if (sec != NULL)
+    return true;
+
+  if (htab->table_jump_htab->tablejump_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));
+
+      if (sec == NULL
+	  || !bfd_set_section_alignment (sec, bed->s->log_file_align)
+	  || !bfd_set_section_size (sec, 256 * RISCV_ELF_WORD_BYTES))
+	return false;
+
+      htab->table_jump_htab->tablejump_sec = sec;
+      htab->table_jump_htab->tablejump_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.  */
@@ -1065,6 +1280,9 @@ riscv_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 	}
     }
 
+  if (!bfd_elf_riscv_make_tablejump_section (abfd, info))
+    return false;
+
   return true;
 }
 
@@ -4480,6 +4698,92 @@ typedef bool (*relax_func_t) (bfd *, asection *, asection *,
 			      riscv_pcgp_relocs *,
 			      bool undefined_weak);
 
+static htab_t
+riscv_get_table_jump_htab (struct bfd_link_info *info, unsigned int link_reg)
+{
+  struct riscv_elf_link_hash_table *htab = riscv_elf_hash_table (info);
+  riscv_table_jump_htab_t *tbj_htab = htab->table_jump_htab;
+
+  BFD_ASSERT (tbj_htab != NULL);
+
+  if (link_reg == 0)
+    return tbj_htab->tbljt_htab;
+  if (link_reg == X_RA)
+    return tbj_htab->tbljalt_htab;
+
+  return NULL;
+}
+
+static const char*
+riscv_get_symbol_name (bfd *abfd, Elf_Internal_Rela *rel)
+{
+  unsigned long r_symndx = ELFNN_R_SYM (rel->r_info);
+  Elf_Internal_Shdr *symtab_hdr = &elf_symtab_hdr (abfd);
+  const char *name;
+
+  if (!symtab_hdr->contents)
+    return NULL;
+
+  if (ELFNN_R_SYM (rel->r_info) < symtab_hdr->sh_info)
+    {
+      /* A local symbol.  */
+      Elf_Internal_Sym *sym = ((Elf_Internal_Sym *) symtab_hdr->contents
+		+ r_symndx);
+      name = bfd_elf_sym_name (abfd, symtab_hdr, sym, NULL);
+    }
+  else
+    {
+      struct elf_link_hash_entry *h;
+      unsigned indx = r_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;
+}
+
+static bool
+_bfd_riscv_table_jump_mark (bfd *abfd ATTRIBUTE_UNUSED, asection *sec,
+		       asection *sym_sec ATTRIBUTE_UNUSED,
+		       struct bfd_link_info *link_info,
+		       Elf_Internal_Rela *rel,
+		       bfd_vma symval,
+		       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 target = bfd_getl32 (contents + rel->r_offset);
+  if(ELFNN_R_TYPE (rel->r_info) != R_RISCV_JAL)
+    target = bfd_getl32 (contents + rel->r_offset + 4);
+  int rd = (target >> OP_SH_RD) & OP_MASK_RD;
+  htab_t tbljal_htab = riscv_get_table_jump_htab (link_info, rd);
+
+  /* Check if it uses a valid link register.  */
+  if (tbljal_htab == NULL)
+    return true;
+
+  riscv_table_jump_htab_entry search = {symval, 0, NULL, 0};
+  riscv_table_jump_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)
+    {
+      target = MATCH_TABLE_JUMP | ENCODE_ZCMT_TABLE_JUMP_INDEX (entry->index-1);
+      bfd_putl32 (target, contents + rel->r_offset);
+    }
+  return true;
+}
+
 /* Relax AUIPC + JALR into JAL.  */
 
 static bool
@@ -4546,6 +4850,25 @@ _bfd_riscv_relax_call (bfd *abfd, asection *sec, asection *sym_sec,
       auipc = MATCH_JALR | (rd << OP_SH_RD);
     }
 
+  /* Table jump profiling stage. It will be moved out of the relax_call function.  */
+  if (link_info->relax_pass == 0)
+    {
+      /* Early stop to prevent _bfd_riscv_relax_call to delete bytes in pass 0.  */
+      if (link_info->relax_trip != 0)
+	return true;
+
+      htab_t tbljal_htab = riscv_get_table_jump_htab (link_info, rd);
+      const char *name = riscv_get_symbol_name (abfd, rel);
+      unsigned int benefit = len - 2;
+
+      if (tbljal_htab == NULL
+	  || name == NULL
+	  || benefit == 0)
+	return true;
+
+      return riscv_update_table_jump_entry (tbljal_htab, symval, benefit, name);
+    }
+
   /* Replace the R_RISCV_CALL reloc.  */
   rel->r_info = ELFNN_R_INFO (ELFNN_R_SYM (rel->r_info), r_type);
   /* Replace the AUIPC.  */
@@ -4941,6 +5264,155 @@ _bfd_riscv_relax_pc (bfd *abfd ATTRIBUTE_UNUSED,
   return true;
 }
 
+static bool
+_bfd_riscv_record_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,
+			 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);
+  unsigned int rd = (jal >> OP_SH_RD) & OP_MASK_RD;
+  htab_t tbljal_htab = riscv_get_table_jump_htab (link_info, rd);
+  const char *name = riscv_get_symbol_name (abfd, rel);
+
+  if (link_info->relax_pass == 1
+      && ((jal ^ MATCH_TABLE_JUMP) & 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, NULL);
+    }
+
+  if (tbljal_htab == NULL
+      || name == NULL
+      || (link_info->relax_pass == 0 && link_info->relax_trip > 0)
+      || link_info->relax_pass > 0)
+    return true;
+
+  return riscv_update_table_jump_entry (tbljal_htab, symval, 2, name);
+}
+
+typedef struct
+{
+  riscv_table_jump_htab_t *htab;
+  unsigned int start;
+  unsigned int end;
+} riscv_table_jump_args;
+
+static int
+riscv_ranking_table_jump (void **entry_ptr, void *_arg)
+{
+  const riscv_table_jump_htab_entry *entry;
+  riscv_table_jump_args *arg;
+  riscv_table_jump_htab_t *htab;
+  int *savings;
+  const char **names;
+  uintNN_t *tbj_indexes;
+
+  entry = (const riscv_table_jump_htab_entry *) *entry_ptr;
+  arg = (riscv_table_jump_args*) _arg;
+  htab = (riscv_table_jump_htab_t *) arg->htab;
+
+  savings = htab->savings;
+  names = htab->names;
+  tbj_indexes = htab->tbj_indexes;
+
+  /* search insert position and rank.  */
+  unsigned int left = arg->start;
+  unsigned int right = arg->end + 1;
+
+  while (left < right)
+    {
+      unsigned int mid = (left + right) / 2;
+      /* `entry->benefit != 0` helps prioritize entries with a zero benefit.
+	 This is useful when the option --zcmt-force-table-jump is used.  */
+      if (savings[mid] == entry->benefit && entry->benefit != 0)
+	{
+	  left = mid;
+	  break;
+	}
+      else if (savings[mid] == 0
+	  || savings[mid] < entry->benefit)
+	right = mid;
+      else
+	left = mid + 1;
+    }
+
+  for (unsigned int idx = arg->end; idx > left; idx--)
+    {
+      tbj_indexes[idx] = tbj_indexes[idx-1];
+      savings[idx] = savings[idx-1];
+      names[idx] = names[idx-1];
+    }
+
+  if (left <= arg->end)
+    {
+      tbj_indexes[left] = (uintNN_t) entry->address;
+      savings[left] = entry->benefit;
+      names[left] = entry->name;
+    }
+
+  return true;
+}
+
+static bool
+riscv_record_table_jump_index (htab_t htab, riscv_table_jump_args *args)
+{
+  unsigned int idx;
+  riscv_table_jump_htab_t *tbj_htab = args->htab;
+  riscv_table_jump_htab_entry search;
+  riscv_table_jump_htab_entry *entry = NULL;
+
+  for (idx = args->start; idx <= args->end && tbj_htab->tbj_indexes[idx]; idx++)
+    {
+      search = (riscv_table_jump_htab_entry)
+	  {tbj_htab->tbj_indexes[idx], 0, NULL, 0};
+      entry = htab_find (htab, &search);
+
+      BFD_ASSERT (entry != NULL);
+      entry->index = idx + 1;
+      tbj_htab->total_saving += tbj_htab->savings[idx];
+    }
+
+  /* True if there is at least one entry in table jump section.  */
+  if (entry && entry->index)
+    tbj_htab->end_idx = entry->index;
+
+  return true;
+}
+
+static bool
+riscv_table_jump_profiling (riscv_table_jump_htab_t *table_jump_htab,
+    riscv_table_jump_args *args)
+{
+  args->start = 0, args->end = 31;
+  /* Do a ranking.  */
+  htab_traverse (table_jump_htab->tbljt_htab,
+	riscv_ranking_table_jump,
+	args);
+  riscv_record_table_jump_index (
+	  table_jump_htab->tbljt_htab,
+	  args);
+
+  args->start = 32, args->end = 255;
+  htab_traverse (table_jump_htab->tbljalt_htab,
+	riscv_ranking_table_jump,
+	args);
+  riscv_record_table_jump_index (
+	  table_jump_htab->tbljalt_htab,
+	  args);
+  return true;
+}
+
 /* Called by after_allocation to set the information of data segment
    before relaxing.  */
 
@@ -4969,9 +5441,11 @@ _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_table_jump_htab_t *table_jump_htab = htab->table_jump_htab;
+  struct elf_link_hash_entry *jvt_sym;
 
   *again = false;
 
@@ -5012,6 +5486,87 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec,
       htab->max_alignment = max_alignment;
     }
 
+  /* relax_trip 0:
+     Record symbol address and expected size saving of each relocation
+     that can be replaced by table jump instructions.
+
+     relax_trip 1:
+     Rank the best 32 relocations to replace for cm.jt and the best 224
+     relocations for cm.jalt in terms of the total size saved.
+
+     relax_trip 2:
+     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.
+
+     relax_trip 3: Trim unused slots in the table jump section.  */
+
+  if (info->relax_pass == 0
+      && riscv_use_table_jump (info))
+    {
+      /* Avoid size savings of relocations to be recoreded multiple times.  */
+      if (info->relax_trip == 0 && *(htab->data_segment_phase) != 0)
+	return true;
+      /* Rank the entries, and calculate the expected total saving.  */
+      else if (info->relax_trip == 1)
+	{
+	  *again = true;
+	  /* Profiling stage finished.  */
+	  if (table_jump_htab->end_idx != 0)
+	    return true;
+
+	  riscv_table_jump_args args = {table_jump_htab, 0, 0};
+	  /* Estimate size savings if table jump is used.  */
+	  riscv_table_jump_profiling (table_jump_htab, &args);
+	  return true;
+	}
+      /* Skip generating table jump instructions if they do not help reduce code size.   */
+      else if (info->relax_trip == 2)
+	{
+	  /* Check if table jump can save size. Skip generating table
+	     jump instruction if not.  */
+	  if (table_jump_htab->total_saving <=
+		  table_jump_htab->end_idx * RISCV_ELF_WORD_BYTES
+		  && table_jump_htab->tablejump_sec->size > 0)
+	    {
+	      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 (table_jump_htab->tablejump_sec_owner,
+		table_jump_htab->tablejump_sec,
+		0, table_jump_htab->tablejump_sec->size, info, NULL, NULL);
+	    }
+	  else if (table_jump_htab->tablejump_sec->size == 0)
+	    return true;
+	  else if (table_jump_htab->tablejump_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 == 3)
+	{
+	  /* Table jump entry section is trimmed.  */
+	  if (table_jump_htab->end_idx < 0)
+	    return true;
+
+	  used_bytes = table_jump_htab->end_idx * RISCV_ELF_WORD_BYTES;
+	  trimmed_bytes = (256 - table_jump_htab->end_idx) * RISCV_ELF_WORD_BYTES;
+	  /* Trim unused slots.  */
+	  if (!riscv_relax_delete_bytes (table_jump_htab->tablejump_sec_owner,
+		table_jump_htab->tablejump_sec,
+		used_bytes, trimmed_bytes, info, NULL, NULL))
+	    return false;
+	  /* Mark table jump profiling stage as completed.  */
+	  table_jump_htab->end_idx = -1;
+	  return true;
+	}
+    }
+
   /* Examine and consider relaxing each reloc.  */
   for (i = 0; i < sec->reloc_count; i++)
     {
@@ -5027,9 +5582,13 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec,
       riscv_relax_delete_bytes = NULL;
       if (info->relax_pass == 0)
 	{
+	  if (!riscv_use_table_jump (info))
+	    return true;
 	  if (type == R_RISCV_CALL
 	      || type == R_RISCV_CALL_PLT)
 	    relax_func = _bfd_riscv_relax_call;
+	  else if (type == R_RISCV_JAL)
+		relax_func = _bfd_riscv_record_jal;
 	  else if (type == R_RISCV_HI20
 		   || type == R_RISCV_LO12_I
 		   || type == R_RISCV_LO12_S)
@@ -5062,6 +5621,8 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec,
 	  relax_func = _bfd_riscv_relax_align;
 	  riscv_relax_delete_bytes = _riscv_relax_delete_immediate;
 	}
+      else if (info->relax_trip == 2)
+	    relax_func = _bfd_riscv_table_jump_mark;
       else
 	continue;
 
diff --git a/bfd/elfxx-riscv.c b/bfd/elfxx-riscv.c
index 65fb29d030a..d7ba903ab8e 100644
--- a/bfd/elfxx-riscv.c
+++ b/bfd/elfxx-riscv.c
@@ -889,6 +889,21 @@ static reloc_howto_type howto_table_internal[] =
 	 0,				/* src_mask */
 	 ENCODE_STYPE_IMM (-1U),	/* dst_mask */
 	 false),			/* pcrel_offset */
+
+  /* Table jump entries.  */
+  HOWTO (R_RISCV_TABLE_JUMP,		/* type */
+	 0,				/* rightshift */
+	 1,				/* size */
+	 16,				/* bitsize */
+	 true,				/* pc_relative */
+	 0,				/* bitpos */
+	 complain_overflow_dont,	/* complain_on_overflow */
+	 bfd_elf_generic_reloc,		/* special_function */
+	 "R_RISCV_TABLE_JUMP",	/* name */
+	 false,				/* partial_inplace */
+	 0,				/* src_mask */
+	 ENCODE_ZCMT_TABLE_JUMP_INDEX (-1U),	/* dst_mask */
+	 true),				/* pcrel_offset */
 };
 
 /* A mapping from BFD reloc types to RISC-V ELF reloc types.  */
@@ -1191,6 +1206,8 @@ static struct riscv_implicit_subset riscv_implicit_subsets[] =
   {"zcd", "zca",	check_implicit_always},
   {"zcb", "zca",	check_implicit_always},
   {"zcmp", "zca",	check_implicit_always},
+  {"zcmt", "zca",	check_implicit_always},
+  {"zcmt", "zicsr",	check_implicit_always},
   {"smaia", "ssaia",		check_implicit_always},
   {"smcntrpmf", "zicsr",	check_implicit_always},
   {"smstateen", "ssstateen",	check_implicit_always},
@@ -1334,6 +1351,7 @@ static struct riscv_supported_ext riscv_supported_std_z_ext[] =
   {"zcf",		ISA_SPEC_CLASS_DRAFT,		1, 0,  0 },
   {"zcd",		ISA_SPEC_CLASS_DRAFT,		1, 0,  0 },
   {"zcmp",		ISA_SPEC_CLASS_DRAFT,		1, 0,  0 },
+  {"zcmt",		ISA_SPEC_CLASS_DRAFT,		1, 0,  0 },
   {NULL, 0, 0, 0, 0}
 };
 
@@ -2568,6 +2586,8 @@ riscv_multi_subset_supports (riscv_parse_subset_t *rps,
 	      && riscv_subset_supports (rps, "zmmul"));
     case INSN_CLASS_ZCMP:
       return riscv_subset_supports (rps, "zcmp");
+    case INSN_CLASS_ZCMT:
+      return riscv_subset_supports (rps, "zcmt");
     case INSN_CLASS_SVINVAL:
       return riscv_subset_supports (rps, "svinval");
     case INSN_CLASS_H:
@@ -2818,6 +2838,8 @@ riscv_multi_subset_supports_ext (riscv_parse_subset_t *rps,
       return _("zcb' and `zmmul', or `zcb' and `m");
     case INSN_CLASS_ZCMP:
       return "zcmp";
+    case INSN_CLASS_ZCMT:
+      return "zcmt";
     case INSN_CLASS_SVINVAL:
       return "svinval";
     case INSN_CLASS_H:
diff --git a/gas/config/tc-riscv.c b/gas/config/tc-riscv.c
index 4adb3d44b8b..823cea65a65 100644
--- a/gas/config/tc-riscv.c
+++ b/gas/config/tc-riscv.c
@@ -69,6 +69,7 @@ enum riscv_csr_class
   CSR_CLASS_F,		/* f-ext only */
   CSR_CLASS_ZKR,	/* zkr only */
   CSR_CLASS_V,		/* rvv only */
+  CSR_CLASS_ZCMT,	/* zcmt only */
   CSR_CLASS_DEBUG,	/* debug CSR */
   CSR_CLASS_H,		/* hypervisor */
   CSR_CLASS_H_32,	/* hypervisor, rv32 only */
@@ -1049,6 +1050,9 @@ riscv_csr_address (const char *csr_name,
     case CSR_CLASS_V:
       extension = "zve32x";
       break;
+    case CSR_CLASS_ZCMT:
+      extension = "zcmt";
+      break;
     case CSR_CLASS_SMAIA_32:
       is_rv32_only = true;
       /* Fall through.  */
@@ -1574,6 +1578,8 @@ validate_riscv_insn (const struct riscv_opcode *opc, int length)
 		case 'p': used_bits |= ENCODE_ZCMP_SPIMM (-1U); break;
 		/* register list operand for cm.push and cm.pop.  */
 		case 'r': USE_BITS (OP_MASK_RLIST, OP_SH_RLIST); break;
+		case 'i':
+		case 'I': used_bits |= ENCODE_ZCMT_TABLE_JUMP_INDEX (-1U); break;
 		case 'f': break;
 		default:
 		  goto unknown_validate_operand;
@@ -3687,6 +3693,28 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr,
 			break;
 		      INSERT_OPERAND (SREG2, *ip, regno % 8);
 		      continue;
+		    case 'I': /* index operand of cm.jt. The range is from 0 to 31.  */
+		      my_getExpression (imm_expr, asarg);
+		      if (imm_expr->X_op != O_constant
+			  || imm_expr->X_add_number < 0
+			  || imm_expr->X_add_number > 31)
+			{
+			  as_bad ("bad index value for cm.jt, range: [0, 31]");
+			  break;
+			}
+		      ip->insn_opcode |= ENCODE_ZCMT_TABLE_JUMP_INDEX (imm_expr->X_add_number);
+		      goto rvc_imm_done;
+		    case 'i': /* index operand of cm.jalt. The range is from 32 to 255.  */
+		      my_getExpression (imm_expr, asarg);
+		      if (imm_expr->X_op != O_constant
+			  || imm_expr->X_add_number < 32
+			  || imm_expr->X_add_number > 255)
+			{
+			  as_bad ("bad index value for cm.jalt, range: [32, 255]");
+			  break;
+			}
+		      ip->insn_opcode |= ENCODE_ZCMT_TABLE_JUMP_INDEX (imm_expr->X_add_number);
+		      goto rvc_imm_done;
 		    default:
 		      goto unknown_riscv_ip_operand;
 		    }
@@ -4331,6 +4359,12 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
       break;
 
     case BFD_RELOC_RISCV_JMP:
+      /* j and jal can be relaxed into cm.jalt or cm.jt if zcmt is used.  */
+      if (riscv_subset_supports (&riscv_rps_as, "zcmt"))
+	{
+	  relaxable = true;
+	  fixP->fx_tcbit = riscv_opts.relax;
+	}
       if (fixP->fx_addsy)
 	{
 	  /* Fill in a tentative value to improve objdump readability.  */
@@ -4816,7 +4850,22 @@ md_convert_frag_branch (fragS *fragp)
 	    /* Invert the branch condition.  Branch over the jump.  */
 	    insn = bfd_getl16 (buf);
 	    insn ^= MATCH_C_BEQZ ^ MATCH_C_BNEZ;
-	    insn |= ENCODE_CBTYPE_IMM (6);
+      if (riscv_subset_supports (&riscv_rps_as, "zcmt"))
+	      {
+		symbolS *sym = symbol_new (FAKE_LABEL_NAME , now_seg, fragp,
+				     fragp->fr_fix + fragp->fr_var);
+		fixp = fix_new (fragp,
+				buf - (bfd_byte *)fragp->fr_literal,
+				2,
+				sym,
+				0,
+				false,
+				BFD_RELOC_RISCV_RVC_BRANCH);
+		fixp->fx_file = fragp->fr_file;
+		fixp->fx_line = fragp->fr_line;
+	      }
+	    else
+	      insn |= ENCODE_CBTYPE_IMM (6);
 	    bfd_putl16 (insn, buf);
 	    buf += 2;
 	    goto jump;
@@ -4843,7 +4892,23 @@ md_convert_frag_branch (fragS *fragp)
       /* Invert the branch condition.  Branch over the jump.  */
       insn = bfd_getl32 (buf);
       insn ^= MATCH_BEQ ^ MATCH_BNE;
-      insn |= ENCODE_BTYPE_IMM (8);
+      if (riscv_subset_supports (&riscv_rps_as, "zcmt"))
+	{
+	  symbolS *sym = (symbolS *) local_symbol_make (
+		    FAKE_LABEL_NAME, now_seg, fragp,
+		    fragp->fr_fix + fragp->fr_var);
+	  fixp = fix_new (fragp,
+			  buf - (bfd_byte *)fragp->fr_literal,
+			  4,
+			  sym,
+			  0,
+			  false,
+			  BFD_RELOC_12_PCREL);
+	  fixp->fx_file = fragp->fr_file;
+	  fixp->fx_line = fragp->fr_line;
+	}
+      else
+	insn |= ENCODE_BTYPE_IMM (8);
       bfd_putl32 (insn, buf);
       buf += 4;
 
diff --git a/gas/testsuite/gas/riscv/csr-version-1p10.d b/gas/testsuite/gas/riscv/csr-version-1p10.d
index dbdc077adac..b42331338fa 100644
--- a/gas/testsuite/gas/riscv/csr-version-1p10.d
+++ b/gas/testsuite/gas/riscv/csr-version-1p10.d
@@ -895,3 +895,5 @@ Disassembly of section .text:
 [ 	]+[0-9a-f]+:[ 	]+c2159073[ 	]+csrw[ 	]+vtype,a1
 [ 	]+[0-9a-f]+:[ 	]+c2202573[ 	]+csrr[ 	]+a0,vlenb
 [ 	]+[0-9a-f]+:[ 	]+c2259073[ 	]+csrw[ 	]+vlenb,a1
+[ 	]+[0-9a-f]+:[ 	]+01702573[ 	]+csrr[ 	]+a0,jvt
+[ 	]+[0-9a-f]+:[ 	]+01759073[ 	]+csrw[ 	]+jvt,a1
diff --git a/gas/testsuite/gas/riscv/csr-version-1p10.l b/gas/testsuite/gas/riscv/csr-version-1p10.l
index 054179a416d..44ef6ff53ce 100644
--- a/gas/testsuite/gas/riscv/csr-version-1p10.l
+++ b/gas/testsuite/gas/riscv/csr-version-1p10.l
@@ -1613,3 +1613,7 @@
 .*Info: macro .*
 .*Warning: read-only CSR is written `csrw vlenb,a1'
 .*Info: macro .*
+.*Warning: invalid CSR `jvt', needs `zcmt' extension
+.*Info: macro .*
+.*Warning: invalid CSR `jvt', needs `zcmt' extension
+.*Info: macro .*
diff --git a/gas/testsuite/gas/riscv/csr-version-1p11.d b/gas/testsuite/gas/riscv/csr-version-1p11.d
index 7ba88b6d1d5..b9eb5235fa2 100644
--- a/gas/testsuite/gas/riscv/csr-version-1p11.d
+++ b/gas/testsuite/gas/riscv/csr-version-1p11.d
@@ -895,3 +895,5 @@ Disassembly of section .text:
 [ 	]+[0-9a-f]+:[ 	]+c2159073[ 	]+csrw[ 	]+vtype,a1
 [ 	]+[0-9a-f]+:[ 	]+c2202573[ 	]+csrr[ 	]+a0,vlenb
 [ 	]+[0-9a-f]+:[ 	]+c2259073[ 	]+csrw[ 	]+vlenb,a1
+[ 	]+[0-9a-f]+:[ 	]+01702573[ 	]+csrr[ 	]+a0,jvt
+[ 	]+[0-9a-f]+:[ 	]+01759073[ 	]+csrw[ 	]+jvt,a1
diff --git a/gas/testsuite/gas/riscv/csr-version-1p11.l b/gas/testsuite/gas/riscv/csr-version-1p11.l
index cc365f1df41..a2dcc24ffb7 100644
--- a/gas/testsuite/gas/riscv/csr-version-1p11.l
+++ b/gas/testsuite/gas/riscv/csr-version-1p11.l
@@ -1609,3 +1609,7 @@
 .*Info: macro .*
 .*Warning: read-only CSR is written `csrw vlenb,a1'
 .*Info: macro .*
+.*Warning: invalid CSR `jvt', needs `zcmt' extension
+.*Info: macro .*
+.*Warning: invalid CSR `jvt', needs `zcmt' extension
+.*Info: macro .*
diff --git a/gas/testsuite/gas/riscv/csr-version-1p12.d b/gas/testsuite/gas/riscv/csr-version-1p12.d
index 677820b9526..fcd48003ef9 100644
--- a/gas/testsuite/gas/riscv/csr-version-1p12.d
+++ b/gas/testsuite/gas/riscv/csr-version-1p12.d
@@ -895,3 +895,5 @@ Disassembly of section .text:
 [ 	]+[0-9a-f]+:[ 	]+c2159073[ 	]+csrw[ 	]+vtype,a1
 [ 	]+[0-9a-f]+:[ 	]+c2202573[ 	]+csrr[ 	]+a0,vlenb
 [ 	]+[0-9a-f]+:[ 	]+c2259073[ 	]+csrw[ 	]+vlenb,a1
+[ 	]+[0-9a-f]+:[ 	]+01702573[ 	]+csrr[ 	]+a0,jvt
+[ 	]+[0-9a-f]+:[ 	]+01759073[ 	]+csrw[ 	]+jvt,a1
diff --git a/gas/testsuite/gas/riscv/csr-version-1p12.l b/gas/testsuite/gas/riscv/csr-version-1p12.l
index 7a7f5f717c5..f6533672805 100644
--- a/gas/testsuite/gas/riscv/csr-version-1p12.l
+++ b/gas/testsuite/gas/riscv/csr-version-1p12.l
@@ -1373,3 +1373,7 @@
 .*Info: macro .*
 .*Warning: read-only CSR is written `csrw vlenb,a1'
 .*Info: macro .*
+.*Warning: invalid CSR `jvt', needs `zcmt' extension
+.*Info: macro .*
+.*Warning: invalid CSR `jvt', needs `zcmt' extension
+.*Info: macro .*
diff --git a/gas/testsuite/gas/riscv/csr-version-1p9p1.d b/gas/testsuite/gas/riscv/csr-version-1p9p1.d
index f4d2b04ca6a..d3540c1690c 100644
--- a/gas/testsuite/gas/riscv/csr-version-1p9p1.d
+++ b/gas/testsuite/gas/riscv/csr-version-1p9p1.d
@@ -895,3 +895,5 @@ Disassembly of section .text:
 [ 	]+[0-9a-f]+:[ 	]+c2159073[ 	]+csrw[ 	]+vtype,a1
 [ 	]+[0-9a-f]+:[ 	]+c2202573[ 	]+csrr[ 	]+a0,vlenb
 [ 	]+[0-9a-f]+:[ 	]+c2259073[ 	]+csrw[ 	]+vlenb,a1
+[ 	]+[0-9a-f]+:[ 	]+01702573[ 	]+csrr[ 	]+a0,jvt
+[ 	]+[0-9a-f]+:[ 	]+01759073[ 	]+csrw[ 	]+jvt,a1
diff --git a/gas/testsuite/gas/riscv/csr-version-1p9p1.l b/gas/testsuite/gas/riscv/csr-version-1p9p1.l
index 7fcd73ab7dd..bfd8111508f 100644
--- a/gas/testsuite/gas/riscv/csr-version-1p9p1.l
+++ b/gas/testsuite/gas/riscv/csr-version-1p9p1.l
@@ -1681,3 +1681,7 @@
 .*Info: macro .*
 .*Warning: read-only CSR is written `csrw vlenb,a1'
 .*Info: macro .*
+.*Warning: invalid CSR `jvt', needs `zcmt' extension
+.*Info: macro .*
+.*Warning: invalid CSR `jvt', needs `zcmt' extension
+.*Info: macro .*
diff --git a/gas/testsuite/gas/riscv/csr.s b/gas/testsuite/gas/riscv/csr.s
index 3d8da5488a0..c542cd9243c 100644
--- a/gas/testsuite/gas/riscv/csr.s
+++ b/gas/testsuite/gas/riscv/csr.s
@@ -510,3 +510,6 @@
 	csr vl
 	csr vtype
 	csr vlenb
+
+	# Zcmt
+	csr jvt
diff --git a/gas/testsuite/gas/riscv/zcmt-emit-jal-relax.d b/gas/testsuite/gas/riscv/zcmt-emit-jal-relax.d
new file mode 100644
index 00000000000..deaa99692f4
--- /dev/null
+++ b/gas/testsuite/gas/riscv/zcmt-emit-jal-relax.d
@@ -0,0 +1,21 @@
+#as: -march=rv32i_zcmt
+#source: zcmt-emit-jal-relax.s
+#objdump: -dr -Mno-aliases
+
+.*:[	 ]+file format .*
+
+
+Disassembly of section .text:
+
+0+000 <target>:
+[	 ]*[0-9a-f]+:[	 ]+e001[	 ]+c.bnez[	 ]+s0,0.+
+[^:]+: R_RISCV_RVC_BRANCH[	]+target
+[	 ]*[0-9a-f]+:[	 ]+00a010ef[	 ]+jal[	 ]+ra,100c.+
+[^:]+: R_RISCV_JAL[	]+Far
+[^:]+: R_RISCV_RELAX[	 ]+\*ABS\*
+[	 ]*[0-9a-f]+:[	 ]+c019[	 ]+c.beqz[	 ]+s0,c.+
+[^:]+: R_RISCV_RVC_BRANCH[	]+\.L0
+[	 ]*[0-9a-f]+:[	 ]+0040106f[	 ]+jal[	 ]+zero,100c.+
+[^:]+: R_RISCV_JAL[	 ]+Far
+[^:]+: R_RISCV_RELAX[	 ]+\*ABS\*
+#pass
diff --git a/gas/testsuite/gas/riscv/zcmt-emit-jal-relax.s b/gas/testsuite/gas/riscv/zcmt-emit-jal-relax.s
new file mode 100644
index 00000000000..2a91f4031ce
--- /dev/null
+++ b/gas/testsuite/gas/riscv/zcmt-emit-jal-relax.s
@@ -0,0 +1,46 @@
+.macro PADDIING_32_BYTES
+	.option push
+	.option arch, -zcmt
+	.option arch, -zca
+	nop
+	nop
+	nop
+	nop
+	nop
+	nop
+	nop
+	nop
+	.option pop
+.endm
+
+.macro PADDIING_256_BYTES
+	PADDIING_32_BYTES
+	PADDIING_32_BYTES
+	PADDIING_32_BYTES
+	PADDIING_32_BYTES
+	PADDIING_32_BYTES
+	PADDIING_32_BYTES
+	PADDIING_32_BYTES
+	PADDIING_32_BYTES
+.endm
+
+.macro PADDIING_2048_BYTES
+	PADDIING_256_BYTES
+	PADDIING_256_BYTES
+	PADDIING_256_BYTES
+	PADDIING_256_BYTES
+	PADDIING_256_BYTES
+	PADDIING_256_BYTES
+	PADDIING_256_BYTES
+	PADDIING_256_BYTES
+.endm
+
+.option relax
+target:
+	c.bnez s0, target
+	jal Far
+	c.bnez s0, Far
+	PADDIING_2048_BYTES
+	PADDIING_2048_BYTES
+Far:
+	ret
diff --git a/gas/testsuite/gas/riscv/zcmt-relax-branch.d b/gas/testsuite/gas/riscv/zcmt-relax-branch.d
new file mode 100644
index 00000000000..ea4eefe71f8
--- /dev/null
+++ b/gas/testsuite/gas/riscv/zcmt-relax-branch.d
@@ -0,0 +1,19 @@
+#as: -march=rv32i_zcmt
+#source: zcmt-relax-branch.s
+#objdump: -dr -Mno-aliases
+
+.*:[	 ]+file format .*
+
+
+Disassembly of section .text:
+
+0+000 <target>:
+[	 ]*[0-9a-f]+:[	 ]+00940263[	 ]+beq[	 ]+s0,s1,4.+
+[^:]+: R_RISCV_BRANCH[	]+NORMAL
+
+0+4 <NORMAL>:
+[	 ]*[0-9a-f]+:[	 ]+00941463[	 ]+bne[	 ]+s0,s1,c.+
+[^:]+: R_RISCV_BRANCH[	]+\.L0
+[	 ]*[0-9a-f]+:[	 ]+0000006f[	 ]+jal[	 ]+zero,8.+
+[^:]+: R_RISCV_JAL[	]+\*ABS\*\-0x4
+[	 ]*[0-9a-f]+:[	 ]+8082[	 ]+c.jr[	 ]+ra
diff --git a/gas/testsuite/gas/riscv/zcmt-relax-branch.s b/gas/testsuite/gas/riscv/zcmt-relax-branch.s
new file mode 100644
index 00000000000..fe4e4f7c363
--- /dev/null
+++ b/gas/testsuite/gas/riscv/zcmt-relax-branch.s
@@ -0,0 +1,6 @@
+.option norelax
+target:
+	beq s0,s1,NORMAL
+NORMAL:
+	beq s0,s1,-4
+	ret
diff --git a/gas/testsuite/gas/riscv/zcmt.d b/gas/testsuite/gas/riscv/zcmt.d
new file mode 100644
index 00000000000..d37e4644869
--- /dev/null
+++ b/gas/testsuite/gas/riscv/zcmt.d
@@ -0,0 +1,14 @@
+#as: -march=rv32i_zcmt
+#source: zcmt.s
+#objdump: -dr -Mno-aliases
+
+.*:[	 ]+file format .*
+
+
+Disassembly of section .text:
+
+0+000 <target>:
+[	 ]*[0-9a-f]+:[	 ]+a002[	 ]+cm.jt[	 ]+0 # a07ea002 <target\+0xa07ea002>
+[	 ]*[0-9a-f]+:[	 ]+a07e[	 ]+cm.jt[	 ]+31
+[	 ]*[0-9a-f]+:[	 ]+a102[	 ]+cm.jalt[	 ]+64
+[	 ]*[0-9a-f]+:[	 ]+a3fe[	 ]+cm.jalt[	 ]+255
diff --git a/gas/testsuite/gas/riscv/zcmt.s b/gas/testsuite/gas/riscv/zcmt.s
new file mode 100644
index 00000000000..0392eea9846
--- /dev/null
+++ b/gas/testsuite/gas/riscv/zcmt.s
@@ -0,0 +1,5 @@
+target:
+	cm.jt 0
+	cm.jt 31
+	cm.jalt 64
+	cm.jalt 255
diff --git a/include/elf/riscv.h b/include/elf/riscv.h
index 56d419c665b..806caad4719 100644
--- a/include/elf/riscv.h
+++ b/include/elf/riscv.h
@@ -90,6 +90,8 @@ START_RELOC_NUMBERS (elf_riscv_reloc_type)
   /* Reserved 59 for R_RISCV_PLT32.  */
   RELOC_NUMBER (R_RISCV_SET_ULEB128, 60)
   RELOC_NUMBER (R_RISCV_SUB_ULEB128, 61)
+  /* Zcmt Specific Relocation.  */
+  RELOC_NUMBER (R_RISCV_TABLE_JUMP, 226)
 END_RELOC_NUMBERS (R_RISCV_max)
 
 /* Internal relocations used exclusively by the relaxation pass.  */
@@ -126,6 +128,11 @@ END_RELOC_NUMBERS (R_RISCV_max)
 /* The name of the global pointer symbol.  */
 #define RISCV_GP_SYMBOL "__global_pointer$"
 
+/* Zcmt table jump symbol.  */
+#define RISCV_TABLE_JUMP_BASE_SYMBOL "__jvt_base$"
+
+#define TABLE_JUMP_SEC_NAME ".text.tbljal"
+
 /* Processor specific dynamic array tags.  */
 #define DT_RISCV_VARIANT_CC (DT_LOPROC + 1)
 
diff --git a/include/opcode/riscv-opc.h b/include/opcode/riscv-opc.h
index b617ae8d178..80ce9606d68 100644
--- a/include/opcode/riscv-opc.h
+++ b/include/opcode/riscv-opc.h
@@ -2248,6 +2248,10 @@
 #define MASK_CM_MVA01S 0xfc63
 #define MATCH_CM_MVSA01 0xac22
 #define MASK_CM_MVSA01 0xfc63
+/* Zcmt instructions.  */
+#define MATCH_TABLE_JUMP 0xa002
+#define MASK_CM_JT 0xff03
+#define MASK_CM_JALT 0xfc03
 /* Svinval instruction.  */
 #define MATCH_SINVAL_VMA 0x16000073
 #define MASK_SINVAL_VMA 0xfe007fff
@@ -3429,6 +3433,7 @@
 #define CSR_VL 0xc20
 #define CSR_VTYPE 0xc21
 #define CSR_VLENB 0xc22
+#define CSR_JVT 0x0017
 #endif /* RISCV_ENCODING_H */
 #ifdef DECLARE_INSN
 DECLARE_INSN(slli_rv32, MATCH_SLLI_RV32, MASK_SLLI_RV32)
@@ -4444,6 +4449,7 @@ DECLARE_CSR(vcsr, CSR_VCSR, CSR_CLASS_V, PRIV_SPEC_CLASS_NONE, PRIV_SPEC_CLASS_N
 DECLARE_CSR(vl, CSR_VL, CSR_CLASS_V, PRIV_SPEC_CLASS_NONE, PRIV_SPEC_CLASS_NONE)
 DECLARE_CSR(vtype, CSR_VTYPE, CSR_CLASS_V, PRIV_SPEC_CLASS_NONE, PRIV_SPEC_CLASS_NONE)
 DECLARE_CSR(vlenb, CSR_VLENB, CSR_CLASS_V, PRIV_SPEC_CLASS_NONE, PRIV_SPEC_CLASS_NONE)
+DECLARE_CSR(jvt, CSR_JVT, CSR_CLASS_ZCMT, PRIV_SPEC_CLASS_NONE, PRIV_SPEC_CLASS_NONE)
 #endif /* DECLARE_CSR */
 #ifdef DECLARE_CSR_ALIAS
 DECLARE_CSR_ALIAS(ubadaddr, CSR_UTVAL, CSR_CLASS_I, PRIV_SPEC_CLASS_1P9P1, PRIV_SPEC_CLASS_1P10)
diff --git a/include/opcode/riscv.h b/include/opcode/riscv.h
index 7a21166eb7b..2c9cb667887 100644
--- a/include/opcode/riscv.h
+++ b/include/opcode/riscv.h
@@ -115,6 +115,8 @@ static inline unsigned int riscv_insn_length (insn_t insn)
   (RV_X(x, 5, 1) << 1)
 #define EXTRACT_ZCMP_SPIMM(x) \
   (RV_X(x, 2, 2) << 4)
+#define EXTRACT_ZCMT_TABLE_JUMP_INDEX(x) \
+  (RV_X(x, 2, 8))
 /* Vendor-specific (CORE-V) extract macros.  */
 #define EXTRACT_CV_IS2_UIMM5(x) \
   (RV_X(x, 20, 5))
@@ -173,6 +175,8 @@ static inline unsigned int riscv_insn_length (insn_t insn)
   (RV_X(x, 1, 1) << 5)
 #define ENCODE_ZCMP_SPIMM(x) \
   (RV_X(x, 4, 2) << 2)
+#define ENCODE_ZCMT_TABLE_JUMP_INDEX(x) \
+  (RV_X(x, 0, 8) << 2)
 /* Vendor-specific (CORE-V) encode macros.  */
 #define ENCODE_CV_IS2_UIMM5(x) \
   (RV_X(x, 0, 5) << 20)
@@ -477,6 +481,7 @@ enum riscv_insn_class
   INSN_CLASS_ZCB_AND_ZBB,
   INSN_CLASS_ZCB_AND_ZMMUL,
   INSN_CLASS_ZCMP,
+  INSN_CLASS_ZCMT,
   INSN_CLASS_SVINVAL,
   INSN_CLASS_ZICBOM,
   INSN_CLASS_ZICBOP,
diff --git a/opcodes/riscv-dis.c b/opcodes/riscv-dis.c
index 23bbf428d19..bde92ef05c1 100644
--- a/opcodes/riscv-dis.c
+++ b/opcodes/riscv-dis.c
@@ -56,6 +56,8 @@ struct riscv_private_data
 {
   bfd_vma gp;
   bfd_vma print_addr;
+  bfd_vma jvt_base;
+  bfd_vma jvt_end;
   bfd_vma hi_addr[OP_MASK_RD + 1];
   bool to_print_addr;
   bool has_gp;
@@ -219,6 +221,54 @@ maybe_print_address (struct riscv_private_data *pd, int base_reg, int offset,
     pd->print_addr = (bfd_vma)(uint32_t)pd->print_addr;
 }
 
+/* Print table jump index.  */
+
+static bool
+print_jvt_index (disassemble_info *info, unsigned int index)
+{
+  bfd_vma entry_value;
+  bfd_vma memaddr;
+  int status;
+
+  bfd_byte packet[8] = {0};
+  struct riscv_private_data *pd = info->private_data;
+
+  memaddr = pd->jvt_base + index * (xlen/8);
+  status = (*info->read_memory_func) (memaddr, packet, xlen / 8, info);
+  if (status != 0)
+    return false;
+
+  entry_value = xlen == 32 ? bfd_getl32 (packet)
+			    : bfd_getl64 (packet);
+
+  maybe_print_address (pd, 0, entry_value, 0);
+  return true;
+}
+
+/* Print table jump entry value.  */
+
+static bool
+print_jvt_entry_value (disassemble_info *info, bfd_vma memaddr)
+{
+  bfd_vma entry_value;
+  int status;
+  struct riscv_private_data *pd = info->private_data;
+  bfd_byte packet[8] = {0};
+  unsigned index = (memaddr - pd->jvt_base) / (xlen / 8);
+
+  status = (*info->read_memory_func) (memaddr, packet, xlen / 8, info);
+  if (status != 0)
+    return false;
+
+  entry_value = xlen == 32 ? bfd_getl32 (packet)
+			    : bfd_getl64 (packet);
+
+  info->target = entry_value;
+  (*info->fprintf_func) (info->stream, "index %u # ", index);
+  (*info->print_address_func) (info->target, info);
+  return true;
+}
+
 /* Get Zcmp rlist field.  */
 
 static void
@@ -710,6 +760,12 @@ print_insn_args (const char *oparg, insn_t l, bfd_vma pc, disassemble_info *info
 		  print (info->stream, dis_style_immediate, "%d",
 			 riscv_get_spimm (l));
 		  break;
+		case 'i':
+		case 'I':
+		  print (info->stream, dis_style_address_offset,
+			 "%lu", EXTRACT_ZCMT_TABLE_JUMP_INDEX (l));
+		  print_jvt_index (info, EXTRACT_ZCMT_TABLE_JUMP_INDEX (l));
+		  break;
 		default:
 		  goto undefined_modifier;
 		}
@@ -824,6 +880,44 @@ riscv_disassemble_insn (bfd_vma memaddr,
       init = true;
     }
 
+  if (info->private_data == NULL)
+    {
+      bfd_vma sym_val;
+
+      pd = info->private_data = xcalloc (1, sizeof (struct riscv_private_data));
+      pd->gp = -1;
+      pd->print_addr = -1;
+      pd->jvt_base = -1;
+      pd->jvt_end = -1;
+
+      for (i = 0; i < (int)ARRAY_SIZE (pd->hi_addr); i++)
+	pd->hi_addr[i] = -1;
+
+      for (i = 0; i < info->symtab_size; i++)
+	{
+	  if (strcmp (bfd_asymbol_name (info->symtab[i]), RISCV_GP_SYMBOL) == 0)
+	    pd->gp = bfd_asymbol_value (info->symtab[i]);
+	  /* Read the address of table jump entries.  */
+	  else if (strcmp (bfd_asymbol_name (info->symtab[i]),
+				  RISCV_TABLE_JUMP_BASE_SYMBOL) == 0)
+	    pd->jvt_base = bfd_asymbol_value (info->symtab[i]);
+	}
+
+      /* Calculate the closest symbol from jvt base to determine the size of table jump
+	 entry section.  */
+      if (pd->jvt_base != 0)
+	{
+	  for (i = 0; i < info->symtab_size; i++)
+	    {
+	      sym_val = bfd_asymbol_value (info->symtab[i]);
+	      if (sym_val > pd->jvt_base && sym_val < pd->jvt_end)
+		pd->jvt_end = sym_val;
+	    }
+	}
+    }
+  else
+    pd = info->private_data;
+
   insnlen = riscv_insn_length (word);
 
   /* RISC-V instructions are always little-endian.  */
@@ -854,6 +948,22 @@ riscv_disassemble_insn (bfd_vma memaddr,
 	  xlen = ehdr->e_ident[EI_CLASS] == ELFCLASS64 ? 64 : 32;
 	}
 
+	  if (pd->jvt_base
+		&& (pd->jvt_end > pd->jvt_base + 255 * (xlen / 8)))
+	    pd->jvt_end = pd->jvt_base + 255 * (xlen / 8);
+
+      /* Dump jump table entries.  */
+      if (riscv_subset_supports (&riscv_rps_dis, "zcmt")
+	  && pd->jvt_base != 0
+	  && pd->jvt_base != (bfd_vma)-1
+	  && memaddr >= pd->jvt_base
+	  && memaddr < pd->jvt_end
+	  && print_jvt_entry_value (info, memaddr))
+	{
+	  info->bytes_per_chunk = xlen / 8;
+	  return xlen / 8;
+	}
+
       /* If arch has the Zfinx extension, replace FPR with GPR.  */
       if (riscv_subset_supports (&riscv_rps_dis, "zfinx"))
 	riscv_fpr_names = riscv_gpr_names;
diff --git a/opcodes/riscv-opc.c b/opcodes/riscv-opc.c
index f3839188255..1528b7ff226 100644
--- a/opcodes/riscv-opc.c
+++ b/opcodes/riscv-opc.c
@@ -328,6 +328,25 @@ match_sreg1_not_eq_sreg2 (const struct riscv_opcode *op, insn_t insn)
       && (EXTRACT_OPERAND (SREG1, insn) != EXTRACT_OPERAND (SREG2, insn));
 }
 
+/* This is used for cm.jt. This requires index operand to be less than 32.  */
+
+static int
+match_cm_jt (const struct riscv_opcode *op, insn_t insn)
+{
+  return match_opcode (op, insn)
+    && EXTRACT_ZCMT_TABLE_JUMP_INDEX (insn) < 32;
+}
+
+/* This is used for cm.jalt. This requires index operand to be in 32 to 255.  */
+
+static int
+match_cm_jalt (const struct riscv_opcode *op, insn_t insn)
+{
+  return match_opcode (op, insn)
+    && EXTRACT_ZCMT_TABLE_JUMP_INDEX (insn) >= 32
+    && EXTRACT_ZCMT_TABLE_JUMP_INDEX (insn) < 256;
+}
+
 /* The order of overloaded instructions matters.  Label arguments and
    register arguments look the same. Instructions that can have either
    for arguments must apear in the correct order in this table for the
@@ -1996,6 +2015,10 @@ const struct riscv_opcode riscv_opcodes[] =
 {"cm.mva01s",  0,  INSN_CLASS_ZCMP, "Wc1,Wc2",    MATCH_CM_MVA01S, MASK_CM_MVA01S, match_opcode, 0 },
 {"cm.mvsa01",  0,  INSN_CLASS_ZCMP, "Wc1,Wc2",    MATCH_CM_MVSA01, MASK_CM_MVSA01, match_sreg1_not_eq_sreg2, 0 },
 
+/* Zcmt instructions */
+{"cm.jt",      0,  INSN_CLASS_ZCMT, "WcI",        MATCH_TABLE_JUMP, MASK_CM_JT, match_cm_jt, 0 },
+{"cm.jalt",    0,  INSN_CLASS_ZCMT, "Wci",        MATCH_TABLE_JUMP, MASK_CM_JALT, match_cm_jalt, 0 },
+
 /* Supervisor instructions.  */
 {"csrr",       0, INSN_CLASS_ZICSR, "d,E",   MATCH_CSRRS, MASK_CSRRS|MASK_RS1, match_opcode, INSN_ALIAS },
 {"csrwi",      0, INSN_CLASS_ZICSR, "E,Z",   MATCH_CSRRWI, MASK_CSRRWI|MASK_RD, match_opcode, INSN_ALIAS },
