[PATCH v1]RISC-V: Zcmt relaxationi support.
Jiawei
jiawei@iscas.ac.cn
Thu Nov 27 02:53:32 GMT 2025
More information about the Binutils mailing list
Thu Nov 27 02:53:32 GMT 2025
- Previous message (by thread): [PATCH v1]RISC-V: Zcmt relaxationi support.
- Next message (by thread): [PATCH v1]RISC-V: Zcmt relaxationi support.
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
LGTM, thanks for you pick up this. BR, Jiawei > This patch is refactored the > https://sourceware.org/pipermail/binutils/2024-January/131573.html. > > and below changes are made like > a)Enabled zcmt relaxation from linker along --relax option. > b)Added min-zcmt-size option to zcmt size heuristic. > c)Disassembler support for zcmt insn. > --- > bfd/elfnn-riscv.c | 648 ++++++++++++++++++++- > bfd/elfxx-riscv.c | 17 + > gas/config/tc-riscv.c | 41 +- > include/bfdlink.h | 3 + > include/elf/riscv.h | 10 + > ld/ldlex.h | 1 + > ld/ldmain.c | 2 + > ld/lexsup.c | 13 + > ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp | 1 + > ld/testsuite/ld-riscv-elf/zcmt_relax.d | 18 + > ld/testsuite/ld-riscv-elf/zcmt_relax.ld | 38 ++ > ld/testsuite/ld-riscv-elf/zcmt_relax.s | 9 + > opcodes/riscv-dis.c | 40 ++ > 13 files changed, 831 insertions(+), 10 deletions(-) > create mode 100644 ld/testsuite/ld-riscv-elf/zcmt_relax.d > create mode 100644 ld/testsuite/ld-riscv-elf/zcmt_relax.ld > create mode 100644 ld/testsuite/ld-riscv-elf/zcmt_relax.s > > diff --git a/bfd/elfnn-riscv.c b/bfd/elfnn-riscv.c > index dd5268b7d16..d5642842527 100644 > --- a/bfd/elfnn-riscv.c > +++ b/bfd/elfnn-riscv.c > @@ -208,6 +208,40 @@ 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; > + /* cmjt table active index */ > + short tbljt_index; > + /* cmjalt table active index */ > + short tbljalt_index; > + /* used for profiling */ > + 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; > @@ -246,6 +280,7 @@ struct riscv_elf_link_hash_table > bool (*make_plt_header) (bfd *output_bfd, struct > riscv_elf_link_hash_table *htab); > bool (*make_plt_entry) (bfd *output_bfd, asection *got, bfd_vma > got_offset, > asection *plt, bfd_vma plt_offset); > + riscv_table_jump_htab_t *table_jump_htab; > }; > > /* Instruction access functions. */ > @@ -568,6 +603,83 @@ 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 { > + /* account for jump entry size */ > + entry->benefit += benefit + RISCV_ELF_WORD_BYTES; > + } > + 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 > @@ -632,6 +744,25 @@ 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 > @@ -645,6 +776,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); > } > > @@ -699,6 +839,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; > > @@ -920,6 +1070,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 --no-relax */ > + if (info->disable_target_specific_optimizations > 0) > + 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; > + subsets.arch_str = NULL; > + > + riscv_parse_subset_t riscv_rps_ld_out = > + {&subsets, _bfd_error_handler, &xlen, NULL, false}; > + > + if (!riscv_parse_subset (&riscv_rps_ld_out, > out_attr[Tag_RISCV_arch].s)) > + 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 or --no-relax. */ > + 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. */ > @@ -1266,6 +1485,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; > } > > @@ -1977,6 +2199,10 @@ perform_relocation (const reloc_howto_type *howto, > bfd *input_bfd, > bfd_byte *contents) > { > + /* R_RISCV_TABLE_JUMP expected for zcmt. */ > + if (ELFNN_R_TYPE (rel->r_info) == R_RISCV_TABLE_JUMP) > + return bfd_reloc_ok; > + > if (howto->pc_relative) > value -= sec_addr (input_section) + rel->r_offset; > > @@ -3353,6 +3579,9 @@ riscv_elf_relocate_section (bfd *output_bfd, > unresolved_reloc = false; > break; > > + case R_RISCV_TABLE_JUMP: > + break; > + > default: > r = bfd_reloc_notsupported; > } > @@ -4869,6 +5098,94 @@ 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 > + && tbj_htab->tbljalt_index >= CMJALT_INDEX) > + 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_CM_JT | ENCODE_ZCMT_INDEX (entry->index-1); > + bfd_putl32 (target, contents + rel->r_offset); > + } > + return true; > +} > + > /* Relax AUIPC + JALR into JAL. */ > > static bool > @@ -4900,9 +5217,12 @@ _bfd_riscv_relax_call (bfd *abfd, asection > *sec, asection *sym_sec, > foff += ((bfd_signed_vma) foff < 0 ? -max_alignment : > max_alignment); > } > > - /* See if this function call can be shortened. */ > - if (!VALID_JTYPE_IMM (foff) && !(!bfd_link_pic (link_info) && > near_zero)) > - return true; > + /* See if this function call can be shortened or fall back to zcmt > + opporunity. */ > + if (!VALID_JTYPE_IMM (foff) && !(!bfd_link_pic (link_info) && > near_zero ) > + && !riscv_use_table_jump (link_info)) { > + return true; > + } > > /* Shorten the function call. */ > BFD_ASSERT (rel->r_offset + 8 <= sec->size); > @@ -4935,6 +5255,26 @@ _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; > + > + *again = 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. */ > @@ -5355,6 +5695,159 @@ _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_CM_JT) & MASK_CM_JALT) == 0) > + { > + rel->r_info = ELFNN_R_INFO (ELFNN_R_SYM (rel->r_info), > R_RISCV_TABLE_JUMP); > + *again = true; > + return riscv_relax_delete_bytes (abfd, sec, > + rel->r_offset + 2, 6, link_info, pcgp_relocs, NULL); > + } > + > + if (tbljal_htab == NULL > + || name == NULL > + || (link_info->relax_pass == 0 && link_info->relax_trip > 0) > + || link_info->relax_pass > 0) > + return true; > + > + *again = 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 index with a zero > benefit. */ > + 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, bool iscmjl) > +{ > + 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 = tbj_htab->tbljt_index = entry->index; > + if (iscmjl) > + tbj_htab->tbljt_index = 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); > + /* cmjl enteries and update tbljt_index. */ > + riscv_record_table_jump_index ( > + table_jump_htab->tbljt_htab, > + args, > + true); > + > + 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, > + false); > + return true; > +} > + > /* Called by after_allocation to set the information of data segment > before relaxing. */ > > @@ -5366,6 +5859,41 @@ bfd_elfNN_riscv_set_data_segment_info (struct > bfd_link_info *info, > htab->data_segment_phase = data_segment_phase; > } > > + > +static void cleanup_pad_CMJALT(riscv_table_jump_htab_t *htab) > +{ > + unsigned int end_idx = htab->end_idx; > + unsigned int jt_index = htab->tbljt_index; > + unsigned int alt_index = htab->tbljalt_index; > + > + /* Nothing to clean up if no entries,set up cmjalt index. */ > + if (htab->tbljalt_index == 0) > + { > + htab->tbljalt_index = jt_index; > + return ; > + } > + > + /* Check if there's enough "room" to do cleanup. */ > + unsigned int entries_after_cmj = end_idx - (CMJALT_INDEX - 1); > + unsigned int alt_space = CMJALT_INDEX - jt_index; > + > + if (alt_space > entries_after_cmj) > + { > + /* Clean up from CMJALT_INDEX up to current alt_index. */ > + for (unsigned int i = CMJALT_INDEX; i < alt_index; i++) > + { > + htab->total_saving -= htab->savings[i]; > + htab->tbj_indexes[i] = 0; > + htab->savings[i] = 0; > + htab->names[i] = NULL; > + } > + > + /* Reset alt index to jump-table index. */ > + htab->tbljalt_index = jt_index; > + } > + return ; > +} > + > /* Relax a section. > > Pass 0: Shortens code sequences for LUI/CALL/TPREL/PCREL relocs and > @@ -5386,6 +5914,9 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec, > bfd_vma max_alignment, reserve_size = 0; > 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; > + bool is_zcmt = riscv_use_table_jump (info); > > *again = false; > > @@ -5426,6 +5957,80 @@ _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. > + > + relax_trip 3: Trim unused slots in the table jump section. > + 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. */ > + > + if (info->relax_pass == 0 > + && is_zcmt) > + { > + /* 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) > + { > + /*come back for trip 2. */ > + *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. > + Or install the internal zcmt symbol definition. */ > + else if (info->relax_trip == 2) > + { > + /* check the min size to be saved */ > + if (table_jump_htab->total_saving <= info->min_zcmt_size) > + return true; > + > + /* Table jump entry symbol definition. */ > + jvt_sym = elf_link_hash_lookup (elf_hash_table (info), > + RISCV_TABLE_JUMP_BASE_SYMBOL, > + true, false, true); > + jvt_sym->root.u.def.section = bfd_abs_section_ptr; > + /* come back for trip 3. */ > + *again = true; > + } > + /* cleanup the tables entries and update the section content. */ > + else if (info->relax_trip == 3) > + { > + /* Table jump entry section is trimmed. */ > + if (table_jump_htab->end_idx < 0) > + return true; > + > + cleanup_pad_CMJALT(table_jump_htab); > + /* buffer to hold the table enteries. */ > + int cmt_jbl_section_content_size = (table_jump_htab->tbljalt_index > + * RISCV_ELF_WORD_BYTES); > + bfd_byte *buf = (bfd_byte*) > bfd_malloc(cmt_jbl_section_content_size); > + /* copy the table entries. */ > + for (short int index = 0; index < > table_jump_htab->tbljalt_index ; index++) > + bfd_putl32(table_jump_htab->tbj_indexes[index],buf+(index *4)); > + > + elf_section_data(table_jump_htab->tablejump_sec)->this_hdr.contents=buf; > + /* we are done ,tbljalt_index used to patch the relocation. */ > + table_jump_htab->end_idx = -1; > + } > + } > /* Examine and consider relaxing each reloc. */ > for (i = 0; i < sec->reloc_count; i++) > { > @@ -5439,11 +6044,16 @@ _bfd_riscv_relax_section (bfd *abfd, asection > *sec, > > relax_func = NULL; > riscv_relax_delete_bytes = NULL; > + > if (info->relax_pass == 0) > { > + if (!is_zcmt) > + 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) > @@ -5459,25 +6069,41 @@ _bfd_riscv_relax_section (bfd *abfd, asection > *sec, > || type == R_RISCV_PCREL_LO12_S)) > relax_func = _bfd_riscv_relax_pc; > else > - continue; > + continue; > + > riscv_relax_delete_bytes = _riscv_relax_delete_piecewise; > > /* Only relax this reloc if it is paired with R_RISCV_RELAX. */ > - if (i == sec->reloc_count - 1 > + if ((i == sec->reloc_count - 1 > || ELFNN_R_TYPE ((rel + 1)->r_info) != R_RISCV_RELAX > || rel->r_offset != (rel + 1)->r_offset) > + && !is_zcmt) > continue; > > + /* Replace JAL/JALR with cmjlt/ cmjalt. */ > + if (info->relax_trip == 3) > + relax_func = _bfd_riscv_table_jump_mark; > + > /* Skip over the R_RISCV_RELAX. */ > i++; > - } > - else if (info->relax_pass == 1 && type == R_RISCV_ALIGN) > +} > + else if (info->relax_pass == 1 > + && type == R_RISCV_ALIGN) > { > relax_func = _bfd_riscv_relax_align; > riscv_relax_delete_bytes = _riscv_relax_delete_immediate; > } > + else if (info->relax_pass == 1 > + && (type == R_RISCV_JAL > + || type == R_RISCV_CALL > + || type == R_RISCV_CALL_PLT)) > + { > + relax_func = _bfd_riscv_record_jal; > + riscv_relax_delete_bytes = _riscv_relax_delete_immediate; > + } > + > else > - continue; > + continue; > > data->relocs = relocs; > > @@ -5648,6 +6274,12 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec, > free (relocs); > riscv_free_pcgp_relocs (&pcgp_relocs, abfd, sec); > > + /* If no size is saved, exclude the table jump section. */ > + if ((info->relax_pass == 1) > + && (table_jump_htab->total_saving == 0) > + && is_zcmt) > + table_jump_htab->tablejump_sec->flags |= SEC_EXCLUDE; > + > return ret; > } > > diff --git a/bfd/elfxx-riscv.c b/bfd/elfxx-riscv.c > index 085d77923a0..989689969ed 100644 > --- a/bfd/elfxx-riscv.c > +++ b/bfd/elfxx-riscv.c > @@ -958,6 +958,23 @@ static const 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_INDEX (-1U), /* dst_mask */ > + true), /* pcrel_offset */ > + > + EMPTY_HOWTO (226), > }; > > /* A mapping from BFD reloc types to RISC-V ELF reloc types. */ > diff --git a/gas/config/tc-riscv.c b/gas/config/tc-riscv.c > index df60c206b82..25404ddd773 100644 > --- a/gas/config/tc-riscv.c > +++ b/gas/config/tc-riscv.c > @@ -4854,6 +4854,12 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg) > 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. */ > @@ -5433,7 +5439,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; > @@ -5460,7 +5481,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/include/bfdlink.h b/include/bfdlink.h > index 00fe0f8c7c8..b40d23b516d 100644 > --- a/include/bfdlink.h > +++ b/include/bfdlink.h > @@ -769,6 +769,9 @@ struct bfd_link_info > /* The maximum cache size. Backend can use cache_size and and > max_cache_size to decide if keep_memory should be honored. */ > bfd_size_type max_cache_size; > + > + /* Minimum size to be saved with ZCMT sections. */ > + bfd_size_type min_zcmt_size; > }; > > /* Some forward-definitions used by some callbacks. */ > diff --git a/include/elf/riscv.h b/include/elf/riscv.h > index 2ea1ae82eb6..f2f5f8618e3 100644 > --- a/include/elf/riscv.h > +++ b/include/elf/riscv.h > @@ -95,6 +95,8 @@ START_RELOC_NUMBERS (elf_riscv_reloc_type) > RELOC_NUMBER (R_RISCV_TLSDESC_LOAD_LO12, 63) > RELOC_NUMBER (R_RISCV_TLSDESC_ADD_LO12, 64) > RELOC_NUMBER (R_RISCV_TLSDESC_CALL, 65) > + /* Zcmt Specific Relocation. */ > + RELOC_NUMBER (R_RISCV_TABLE_JUMP, 226) > END_RELOC_NUMBERS (R_RISCV_max) > > /* Internal relocations used exclusively by the relaxation pass. */ > @@ -131,6 +133,14 @@ 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 ".jvt" > + > +/* CMJALT Start index. */ > +#define CMJALT_INDEX 32 > + > /* Processor specific dynamic array tags. */ > #define DT_RISCV_VARIANT_CC (DT_LOPROC + 1) > > diff --git a/ld/ldlex.h b/ld/ldlex.h > index 24cac1cdfc0..8158aa223a8 100644 > --- a/ld/ldlex.h > +++ b/ld/ldlex.h > @@ -475,6 +475,7 @@ enum option_values > /* Used by emultempl/elf-i386-glibc.em. */ > OPTION_GNU_TLS_VERSION_TAG, > OPTION_NO_GNU_TLS_VERSION_TAG, > + OPTION_MIN_ZCMT_SIZE, > }; > > /* The initial parser states. */ > diff --git a/ld/ldmain.c b/ld/ldmain.c > index 157f205671e..45b0cf2649d 100644 > --- a/ld/ldmain.c > +++ b/ld/ldmain.c > @@ -677,6 +677,8 @@ main (int argc, char **argv) > link_info.allow_undefined_version = true; > link_info.keep_memory = true; > link_info.max_cache_size = (bfd_size_type) -1; > + /* default min 4 bytes to be saved */ > + link_info.min_zcmt_size = 4; > link_info.combreloc = true; > link_info.strip_discarded = true; > link_info.prohibit_multiple_definition_absolute = false; > diff --git a/ld/lexsup.c b/ld/lexsup.c > index 5cb77992733..1ee90e3a47c 100644 > --- a/ld/lexsup.c > +++ b/ld/lexsup.c > @@ -447,6 +447,10 @@ static const struct ld_option ld_options[] = > OPTION_MAX_CACHE_SIZE}, > '\0', NULL, N_("Set the maximum cache size to SIZE bytes"), > TWO_DASHES }, > + { {"min-zcmt-size=SIZE", required_argument, NULL, > + OPTION_MIN_ZCMT_SIZE}, > + '\0', NULL, N_("Set the minimum size to be saved with zcmt and > SIZE in bytes"), > + TWO_DASHES }, > { {"relax", no_argument, NULL, OPTION_RELAX}, > '\0', NULL, N_("Reduce code size by using target specific > optimizations"), TWO_DASHES }, > { {"no-relax", no_argument, NULL, OPTION_NO_RELAX}, > @@ -1777,6 +1781,15 @@ parse_args (unsigned argc, char **argv) > } > break; > > + case OPTION_MIN_ZCMT_SIZE: > + { > + char *end; > + bfd_size_type zcmt_size = strtoul (optarg, &end, 0); > + if (*end != '\0') > + fatal (_("%P: invalid cache memory size: %s\n"), optarg); > + link_info.min_zcmt_size = zcmt_size; > + } > + break; > case OPTION_HASH_SIZE: > { > bfd_size_type new_size; > diff --git a/ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp > b/ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp > index b0d510ac8da..1f37a9b0fac 100644 > --- a/ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp > +++ b/ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp > @@ -127,6 +127,7 @@ if [istarget "riscv*-*-*"] { > run_dump_test "c-lui" > run_dump_test "c-lui-2" > run_dump_test "disas-jalr" > + run_dump_test "zcmt_relax" > run_dump_test "pcrel-lo-addend" > run_dump_test "pcrel-lo-addend-2a" > run_dump_test "pcrel-lo-addend-2b" > diff --git a/ld/testsuite/ld-riscv-elf/zcmt_relax.d > b/ld/testsuite/ld-riscv-elf/zcmt_relax.d > new file mode 100644 > index 00000000000..4ea4e53fb49 > --- /dev/null > +++ b/ld/testsuite/ld-riscv-elf/zcmt_relax.d > @@ -0,0 +1,18 @@ > +#source: zcmt_relax.s > +#as: > -march=rv32imafdb_zicbop_zicond_zicsr_zihintpause_zimop_zaamo_zalrsc_zfa_zca_zcb_zcf_zcmop_zcmp_zcmt_zba_zbb_zbs > +#ld: -melf32lriscv -Tzcmt_relax.ld --min-zcmt-size=6 --defsym > foo=0x150000 --defsym foo_1=0x150010 > +#objdump: -d > + > +.*: file format .* > + > +Disassembly of section \.text: > + > +[0-9a-f]+ <main>: > +^\s+[0-9A-Fa-f]+:\s+[0-9A-Fa-f]+\s+auipc\s+ra,0x[0-9A-Fa-f]+$ > +^\s*[0-9A-Fa-f]+:\s+[0-9A-Fa-f]+\s+jalr\s+ra(?:\s+#\s*[0-9A-Fa-f]+(?:\s+<[^>]+>)?)?$ > +^\s*[0-9A-Fa-f]+:\s+[0-9A-Fa-f]+\s+cm\.jt\s+[0-9A-Fa-f]+$ > +^\s+[0-9A-Fa-f]+:\s+[0-9A-Fa-f]+\s+auipc\s+ra,0x[0-9A-Fa-f]+$ > +^\s*[0-9A-Fa-f]+:\s+[0-9A-Fa-f]+\s+jalr\s+[-+]?[0-9A-Fa-f]+\(ra\)\s*#\s*[0-9A-Fa-f]+\s+<[^>]+>$ > +^\s*[0-9A-Fa-f]+:\s+[0-9A-Fa-f]+\s+cm\.jt\s+[0-9A-Fa-f]+$ > +^\s*[0-9A-Fa-f]+:\s+[0-9A-Fa-f]+\s+cm\.jt\s+[0-9A-Fa-f]+$ > +#pass > diff --git a/ld/testsuite/ld-riscv-elf/zcmt_relax.ld > b/ld/testsuite/ld-riscv-elf/zcmt_relax.ld > new file mode 100644 > index 00000000000..7ec7c98a45b > --- /dev/null > +++ b/ld/testsuite/ld-riscv-elf/zcmt_relax.ld > @@ -0,0 +1,38 @@ > +/* linker.ld - minimal RISC-V linker script with __jvt_base$ */ > + > +ENTRY(main) /* program entry point */ > + > +SECTIONS > +{ > + /* Code section */ > + .text : ALIGN(4) > + { > + *(.text*) > + } > + > + /* Jump Vector Table (JVT) section */ > + .jvt : ALIGN(8) > + { > + __jvt_base$ = .; /* define the base symbol for the JVT */ > + KEEP(*(.jvt*)) /* keep all JVT entries */ > + } > + > + /* Read-only data */ > + .rodata : ALIGN(4) > + { > + *(.rodata*) > + } > + > + /* Data section */ > + .data : ALIGN(4) > + { > + *(.data*) > + } > + > + /* BSS section */ > + .bss : ALIGN(4) > + { > + *(.bss*) > + *(COMMON) > + } > +} > diff --git a/ld/testsuite/ld-riscv-elf/zcmt_relax.s > b/ld/testsuite/ld-riscv-elf/zcmt_relax.s > new file mode 100644 > index 00000000000..fec4020cd2c > --- /dev/null > +++ b/ld/testsuite/ld-riscv-elf/zcmt_relax.s > @@ -0,0 +1,9 @@ > +.text > +.global main > +.p2align 3 > +main: > + call foo > + tail foo_1 > + call foo > + tail foo_1 > + tail foo_1 > diff --git a/opcodes/riscv-dis.c b/opcodes/riscv-dis.c > index fa2d44af28f..a2665714680 100644 > --- a/opcodes/riscv-dis.c > +++ b/opcodes/riscv-dis.c > @@ -44,6 +44,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; > @@ -963,6 +965,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. */ >
- Previous message (by thread): [PATCH v1]RISC-V: Zcmt relaxationi support.
- Next message (by thread): [PATCH v1]RISC-V: Zcmt relaxationi support.
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
More information about the Binutils mailing list