[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