[PATCH] RISC-V: Supports Zcmt extension.

Kito Cheng kito.cheng@gmail.com
Fri Jan 5 06:51:15 GMT 2024


Could you split this patch into two parts, one for adding opcode and
test only and another one for linker relaxation part?

On Tue, Nov 28, 2023 at 2:05 PM Jiawei <jiawei@iscas.ac.cn> wrote:
>
> This patch supports Zcmt instruction 'cm.jt' and 'cm.jalt'.
> Add new CSR jvt for tablejump using.
>
> 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/ChangeLog:
>
>         * elfnn-riscv.c (ZCMT_PRINT_TABLE_JUMP_ENTRIES): New macro.
>         (struct riscv_elf_link_hash_table): New struct.
>         (riscv_table_jump_htab_hash): New function.
>         (riscv_table_jump_htab_entry_eq): Ditto.
>         (riscv_init_table_jump_htab): Ditto.
>         (riscv_free_table_jump_htab): Ditto.
>         (riscv_update_table_jump_entry): Ditto.
>         (print_tablejump_entries): Ditto.
>         (riscv_elf_link_hash_table_free): Ditto.
>         (riscv_elf_link_hash_table_create): Ditto.
>         (riscv_use_table_jump): Ditto.
>         (bfd_elf_riscv_make_tablejump_section): Ditto.
>         (riscv_elf_check_relocs): Ditto.
>         (riscv_get_table_jump_htab): Ditto.
>         (riscv_get_symbol_name): Ditto.
>         (_bfd_riscv_table_jump_mark): Ditto.
>         (_bfd_riscv_relax_call): Ditto.
>         (_bfd_riscv_record_jal): Ditto.
>         (riscv_ranking_table_jump): Ditto.
>         (riscv_record_table_jump_index): Ditto.
>         (riscv_table_jump_profiling): Ditto.
>         (_bfd_riscv_relax_section): Ditto.
>         * elfxx-riscv.c (riscv_multi_subset_supports): New extension.
>         (riscv_multi_subset_supports_ext): Ditto.
>
> gas/ChangeLog:
>
>         * config/tc-riscv.c (enum riscv_csr_class): New CSR.
>         (riscv_csr_address): Ditto.
>         (validate_riscv_insn): New operand.
>         (riscv_ip): Ditto.
>         (md_apply_fix): New case.
>         (md_convert_frag_branch): Ditto.
>         * testsuite/gas/riscv/csr-version-1p10.d: New CSR.
>         * testsuite/gas/riscv/csr-version-1p10.l: Ditto.
>         * testsuite/gas/riscv/csr-version-1p11.d: Ditto.
>         * testsuite/gas/riscv/csr-version-1p11.l: Ditto.
>         * testsuite/gas/riscv/csr-version-1p12.d: Ditto.
>         * testsuite/gas/riscv/csr-version-1p12.l: Ditto.
>         * testsuite/gas/riscv/csr-version-1p9p1.d: Ditto.
>         * testsuite/gas/riscv/csr-version-1p9p1.l: Ditto.
>         * testsuite/gas/riscv/csr.s: Ditto.
>         * testsuite/gas/riscv/zcmt-emit-jal-relax.d: New test.
>         * testsuite/gas/riscv/zcmt-emit-jal-relax.s: New test.
>         * testsuite/gas/riscv/zcmt-relax-branch.d: New test.
>         * testsuite/gas/riscv/zcmt-relax-branch.s: New test.
>         * testsuite/gas/riscv/zcmt.d: New test.
>         * testsuite/gas/riscv/zcmt.s: New test.
>
> include/ChangeLog:
>
>         * elf/riscv.h (START_RELOC_NUMBERS): New macro.
>         (RISCV_TABLE_JUMP_BASE_SYMBOL): Ditto.
>         (TABLE_JUMP_SEC_NAME): Ditto.
>         * opcode/riscv-opc.h (MATCH_TABLE_JUMP): New opcode.
>         (MASK_CM_JT): Ditto.
>         (MASK_CM_JALT): Ditto.
>         (CSR_JVT): Ditto.
>         (DECLARE_CSR): Ditto.
>         * opcode/riscv.h (EXTRACT_ZCMT_TABLE_JUMP_INDEX): Ditto.
>         (ENCODE_ZCMT_TABLE_JUMP_INDEX): Ditto.
>         (enum riscv_insn_class): New class.
>
> opcodes/ChangeLog:
>
>         * riscv-dis.c (struct riscv_private_data): New data.
>         (print_jvt_index): New function.
>         (print_jvt_entry_value): Ditto.
>         (print_insn_args): New operand.
>         (riscv_disassemble_insn): New print.
>         * riscv-opc.c (match_cm_jt): New function.
>         (match_cm_jalt): Ditto.
>
> ---
>  bfd/elfnn-riscv.c                             | 563 +++++++++++++++++-
>  bfd/elfxx-riscv.c                             |  22 +
>  gas/config/tc-riscv.c                         |  69 ++-
>  gas/testsuite/gas/riscv/csr-version-1p10.d    |   2 +
>  gas/testsuite/gas/riscv/csr-version-1p10.l    |   4 +
>  gas/testsuite/gas/riscv/csr-version-1p11.d    |   2 +
>  gas/testsuite/gas/riscv/csr-version-1p11.l    |   4 +
>  gas/testsuite/gas/riscv/csr-version-1p12.d    |   2 +
>  gas/testsuite/gas/riscv/csr-version-1p12.l    |   4 +
>  gas/testsuite/gas/riscv/csr-version-1p9p1.d   |   2 +
>  gas/testsuite/gas/riscv/csr-version-1p9p1.l   |   4 +
>  gas/testsuite/gas/riscv/csr.s                 |   3 +
>  gas/testsuite/gas/riscv/zcmt-emit-jal-relax.d |  21 +
>  gas/testsuite/gas/riscv/zcmt-emit-jal-relax.s |  46 ++
>  gas/testsuite/gas/riscv/zcmt-relax-branch.d   |  19 +
>  gas/testsuite/gas/riscv/zcmt-relax-branch.s   |   6 +
>  gas/testsuite/gas/riscv/zcmt.d                |  14 +
>  gas/testsuite/gas/riscv/zcmt.s                |   5 +
>  include/elf/riscv.h                           |   7 +
>  include/opcode/riscv-opc.h                    |   6 +
>  include/opcode/riscv.h                        |   5 +
>  opcodes/riscv-dis.c                           | 110 ++++
>  opcodes/riscv-opc.c                           |  23 +
>  23 files changed, 940 insertions(+), 3 deletions(-)
>  create mode 100644 gas/testsuite/gas/riscv/zcmt-emit-jal-relax.d
>  create mode 100644 gas/testsuite/gas/riscv/zcmt-emit-jal-relax.s
>  create mode 100644 gas/testsuite/gas/riscv/zcmt-relax-branch.d
>  create mode 100644 gas/testsuite/gas/riscv/zcmt-relax-branch.s
>  create mode 100644 gas/testsuite/gas/riscv/zcmt.d
>  create mode 100644 gas/testsuite/gas/riscv/zcmt.s
>
> 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 },
> --
> 2.25.1
>


More information about the Binutils mailing list