[PATCH 1/3] RISC-V: Add ZCMT table jump relaxation
Jerry Zhang Jian
jerry.zhangjian@sifive.com
Mon Jan 12 13:29:32 GMT 2026
From: Kito Cheng <kito.cheng@sifive.com>
Add a ZCMT table jump relaxation pass in the linker, including JVT
section handling and related relax flow updates. Define the ZCMT
relocation and table jump constants, and add --relax-zcmt and
--no-relax-zcmt to control the feature.
Signed-off-by: Kito Cheng <kito.cheng@sifive.com>
Co-Authored-by: Jerry Zhang Jian <jerry.zhangjian@sifive.com>
Co-Authored-by: Hau Hsu <hau.hsu@sifive.com>
Co-Authored-by: Charlie Keaney <charlie.keaney@embecosm.com>
Co-Authored-by: Mary Bennett <mary.bennett@embecosm.com>
Co-Authored-by: Nandni Jamnadas <nandni.jamnadas@embecosm.com>
Co-Authored-by: Sinan Lin <sinan.lin@linux.alibaba.com>
Co-Authored-by: Simon Cook <simon.cook@embecosm.com>
Co-Authored-by: Shihua Liao <shihua@iscas.ac.cn>
Co-Authored-by: Yulong Shi <yulong@iscas.ac.cn>
---
bfd/elfnn-riscv.c | 903 +++++++++++++++++++++++++++++++++++++--
bfd/elfxx-riscv.h | 21 +
include/elf/riscv.h | 5 +
include/opcode/riscv.h | 9 +
ld/emultempl/riscvelf.em | 17 +-
ld/ldlex.h | 4 +-
6 files changed, 916 insertions(+), 43 deletions(-)
diff --git a/bfd/elfnn-riscv.c b/bfd/elfnn-riscv.c
index 345b3bb96fd..6f3cfe11bd1 100644
--- a/bfd/elfnn-riscv.c
+++ b/bfd/elfnn-riscv.c
@@ -27,6 +27,7 @@
#include "libbfd.h"
#include "bfdlink.h"
#include "genlink.h"
+#include "hashtab.h"
#include "elf-bfd.h"
#include "elfxx-riscv.h"
#include "elf/riscv.h"
@@ -208,6 +209,52 @@ elfNN_riscv_mkobject (bfd *abfd)
#include "elf/common.h"
#include "elf/internal.h"
+/* Per-slot data for JVT entries. */
+typedef struct
+{
+ unsigned int benefit;
+ unsigned int symndx;
+ bfd *input_bfd;
+ const char *name;
+} riscv_jvt_slot_t;
+
+/* Hash table for storing table jump candidate entries. */
+typedef struct
+{
+ /* Hash tables for cm.jt and cm.jalt. They records all possible
+ entries and will be ranked to put most beneficial ones into jvt
+ section. */
+ htab_t jt_htab;
+ htab_t jalt_htab;
+
+ /* Array of addresses that will be put into jvt section. */
+ uintNN_t *jvt_addrs;
+
+ asection *jvt_sec;
+ bfd *jvt_sec_owner;
+
+ /* end_idx is used to calculate size of used slots at table jump section,
+ and it is set to -1 if the profiling stage completed. */
+ int end_idx;
+ unsigned int total_benefits;
+
+ /* Unified slot array for ranked JVT entries. */
+ riscv_jvt_slot_t *slots;
+} riscv_jvt_htab_t;
+
+typedef struct
+{
+ unsigned int index;
+
+ /* Record input_bfd and symbol index so that we can distinguish local
+ functions in different input bfds. */
+ bfd *input_bfd;
+ unsigned long symndx;
+
+ unsigned int benefit;
+ const char *name;
+} riscv_jvt_htab_entry;
+
struct riscv_elf_link_hash_table
{
struct elf_link_hash_table elf;
@@ -246,6 +293,8 @@ struct riscv_elf_link_hash_table
bool (*make_plt_header) (bfd *output_bfd, struct riscv_elf_link_hash_table *htab);
bool (*make_plt_entry) (bfd *output_bfd, asection *got, bfd_vma got_offset,
asection *plt, bfd_vma plt_offset);
+
+ riscv_jvt_htab_t *jvt_htab;
};
/* Instruction access functions. */
@@ -312,6 +361,53 @@ riscv_is_insn_reloc (const reloc_howto_type *howto)
? (MINUS_ONE << howto->bitsize) : (bfd_vma)0)) != 0);
}
+/* Return true if the given symbol index is a local symbol. */
+static inline bool
+riscv_is_local_symbol (Elf_Internal_Shdr *symtab_hdr, unsigned long symndx)
+{
+ /* Document:
+ https://docs.oracle.com/cd/E19683-01/816-7529/chapter6-79797/index.html
+
+ > a symbol table section's sh_info section header member holds the
+ > symbol table index for the first non-local symbol. */
+ return symndx < symtab_hdr->sh_info;
+}
+
+static const char *
+riscv_get_symbol_name (bfd *abfd, unsigned long symndx)
+{
+ Elf_Internal_Shdr *symtab_hdr = &elf_symtab_hdr (abfd);
+ const char *name;
+
+ if (!symtab_hdr->contents)
+ return NULL;
+
+ if (symndx < symtab_hdr->sh_info)
+ {
+ /* A local symbol. */
+ Elf_Internal_Sym *sym
+ = ((Elf_Internal_Sym *) symtab_hdr->contents + symndx);
+ name = bfd_elf_sym_name (abfd, symtab_hdr, sym, NULL);
+ }
+ else
+ {
+ struct elf_link_hash_entry *h;
+ unsigned indx = symndx - symtab_hdr->sh_info;
+ h = elf_sym_hashes (abfd)[indx];
+ while (h->root.type == bfd_link_hash_indirect
+ || h->root.type == bfd_link_hash_warning)
+ h = (struct elf_link_hash_entry *) h->root.u.i.link;
+
+ if (h != NULL && h->type != STT_GNU_IFUNC)
+ name = h->root.root.string;
+ else
+ /* We do not handle STT_GNU_IFUNC currently. */
+ return NULL;
+ }
+
+ return name;
+}
+
/* PLT/GOT stuff. */
#define PLT_HEADER_INSNS 8
#define PLT_ENTRY_INSNS 4
@@ -538,6 +634,17 @@ riscv_make_plt_zicfilp_unlabeled_entry (bfd *output_bfd, asection *got,
return true;
}
+static inline bool
+riscv_is_special_symbol_name (bfd *abfd, const char *name)
+{
+ /* PR27584, local and empty symbols. Since they are usually
+ generated for pcrel relocations. */
+ return (!strcmp (name, "")
+ || _bfd_elf_is_local_label_name (abfd, name)
+ /* PR27916, mapping symbols. */
+ || riscv_elf_is_mapping_symbols (name));
+}
+
/* Create an entry in an RISC-V ELF linker hash table. */
static struct bfd_hash_entry *
@@ -568,6 +675,147 @@ link_hash_newfunc (struct bfd_hash_entry *entry,
return entry;
}
+static hashval_t
+riscv_jvt_htab_hash (const void *entry)
+{
+ const riscv_jvt_htab_entry *e = entry;
+ unsigned int id = e->input_bfd->id;
+ unsigned int symndx = e->symndx;
+ Elf_Internal_Shdr *symtab_hdr = &elf_symtab_hdr (e->input_bfd);
+
+ if (riscv_is_local_symbol (symtab_hdr, symndx))
+ {
+ /* For local symbols, use the symbol table entry index and input_bfd's id
+ to hash (Cantor pairing function). */
+ return (id + symndx) * (id + symndx + 1) / 2 + id;
+ }
+ else
+ {
+ /* For global symbols, use the symbol name. */
+ const char *name = riscv_get_symbol_name (e->input_bfd, symndx);
+ if (name == NULL)
+ return 0;
+ return htab_hash_string (name);
+ }
+}
+
+static int
+riscv_jvt_htab_entry_eq (const void *entry1, const void *entry2)
+{
+ const riscv_jvt_htab_entry *e1 = entry1, *e2 = entry2;
+ unsigned int symndx1 = e1->symndx, symndx2 = e2->symndx;
+ Elf_Internal_Shdr *symtab_hdr1 = &elf_symtab_hdr (e1->input_bfd),
+ *symtab_hdr2 = &elf_symtab_hdr (e2->input_bfd);
+
+ if (riscv_is_local_symbol (symtab_hdr1, symndx1)
+ && riscv_is_local_symbol (symtab_hdr2, symndx2))
+ {
+ return (e1->input_bfd->id == e2->input_bfd->id)
+ && (e1->symndx == e2->symndx);
+ }
+ else if (!riscv_is_local_symbol (symtab_hdr1, symndx1)
+ && !riscv_is_local_symbol (symtab_hdr2, symndx2))
+ {
+ const char *name1 = riscv_get_symbol_name (e1->input_bfd, symndx1);
+ const char *name2 = riscv_get_symbol_name (e2->input_bfd, symndx2);
+ if (name1 == NULL || name2 == NULL)
+ return name1 == name2;
+ return (strcmp (name1, name2) == 0);
+ }
+ /* One is global and the other one is local, just return false. */
+ return false;
+}
+
+static bool
+riscv_init_jvt_htab (riscv_jvt_htab_t *htab)
+{
+ htab->slots = bfd_zmalloc (sizeof (riscv_jvt_slot_t) * ZCMT_TOTAL_ENTRIES);
+ htab->jvt_addrs = bfd_zmalloc (RISCV_ELF_WORD_BYTES * ZCMT_TOTAL_ENTRIES);
+ if (htab->slots == NULL || htab->jvt_addrs == NULL)
+ return false;
+ htab->end_idx = 0;
+ htab->total_benefits = 0;
+
+ htab->jt_htab = htab_create (ZCMT_JT_NUM_ENTRIES, riscv_jvt_htab_hash,
+ riscv_jvt_htab_entry_eq, free);
+ if (htab->jt_htab == NULL)
+ return false;
+
+ htab->jalt_htab = htab_create (ZCMT_JALT_NUM_ENTRIES, riscv_jvt_htab_hash,
+ riscv_jvt_htab_entry_eq, free);
+ return htab->jalt_htab != NULL;
+}
+
+static void
+riscv_free_jvt_htab (riscv_jvt_htab_t *htab)
+{
+ free (htab->slots);
+ free (htab->jvt_addrs);
+ if (htab->jt_htab)
+ htab_delete (htab->jt_htab);
+ if (htab->jalt_htab)
+ htab_delete (htab->jalt_htab);
+}
+
+static bool
+riscv_ensure_jvt_htab (struct riscv_elf_link_hash_table *htab)
+{
+ if (htab->jvt_htab != NULL)
+ return true;
+
+ htab->jvt_htab = (riscv_jvt_htab_t *) bfd_zmalloc (sizeof (*htab->jvt_htab));
+ if (htab->jvt_htab == NULL)
+ return false;
+
+ if (!riscv_init_jvt_htab (htab->jvt_htab))
+ {
+ riscv_free_jvt_htab (htab->jvt_htab);
+ free (htab->jvt_htab);
+ htab->jvt_htab = NULL;
+ return false;
+ }
+
+ return true;
+}
+
+/* Update table jump hash entry. */
+
+static bool
+riscv_update_jvt_entry (htab_t htab, bfd *abfd, unsigned int symndx,
+ unsigned int benefit, const char *name)
+{
+ riscv_jvt_htab_entry search = {.index = 0,
+ .input_bfd = abfd,
+ .symndx = symndx,
+ .benefit = 0,
+ .name = NULL};
+
+ riscv_jvt_htab_entry *entry = htab_find (htab, &search);
+
+ if (entry == NULL)
+ {
+ riscv_jvt_htab_entry **slot
+ = (riscv_jvt_htab_entry **) htab_find_slot (htab, &search, INSERT);
+
+ BFD_ASSERT (*slot == NULL);
+
+ *slot
+ = (riscv_jvt_htab_entry *) bfd_zmalloc (sizeof (riscv_jvt_htab_entry));
+
+ if (*slot == NULL)
+ return false;
+
+ (*slot)->input_bfd = abfd;
+ (*slot)->symndx = symndx;
+ (*slot)->benefit = benefit;
+ (*slot)->name = name;
+ }
+ else
+ entry->benefit += benefit;
+
+ return true;
+}
+
/* Compute a hash of a local hash entry. We use elf_link_hash_entry
for local symbol so that we can handle local STT_GNU_IFUNC symbols
as global symbol. We reuse indx and dynstr_index for local symbol
@@ -645,6 +893,12 @@ riscv_elf_link_hash_table_free (bfd *obfd)
if (ret->loc_hash_memory)
objalloc_free ((struct objalloc *) ret->loc_hash_memory);
+ if (ret->jvt_htab)
+ {
+ riscv_free_jvt_htab (ret->jvt_htab);
+ free (ret->jvt_htab);
+ }
+
_bfd_elf_link_hash_table_free (obfd);
}
@@ -920,6 +1174,95 @@ bad_static_reloc (bfd *abfd, unsigned r_type, struct elf_link_hash_entry *h)
return false;
}
+static bool
+riscv_use_jvt (struct bfd_link_info *info)
+{
+ unsigned xlen = ARCH_SIZE;
+ riscv_subset_list_t subsets;
+ enum
+ {
+ UNDETERMINED = -1,
+ NOT_USE_ZCMT = 0,
+ USE_ZCMT = 1,
+ };
+ static int cache_result = UNDETERMINED;
+ static struct bfd_link_info *cache_info = NULL;
+ struct riscv_elf_link_hash_table *htab;
+
+ if (cache_result != UNDETERMINED && cache_info == info)
+ return cache_result;
+
+ htab = riscv_elf_hash_table (info);
+ /* If relax is disabled by user or not linking executable, table jump insn
+ will not be generated. */
+ if (info->disable_target_specific_optimizations >= 1
+ || !bfd_link_executable (info) || !htab->params->relax_zcmt)
+ {
+ cache_result = NOT_USE_ZCMT;
+ cache_info = info;
+ return cache_result;
+ }
+
+ bfd *obfd = info->output_bfd;
+ obj_attribute *out_attr = elf_known_obj_attributes_proc (obfd);
+
+ subsets.head = NULL;
+ subsets.tail = NULL;
+ subsets.arch_str = NULL;
+
+ riscv_parse_subset_t riscv_rps_ld_out
+ = {&subsets, _bfd_error_handler, &xlen, NULL, false};
+
+ if (!riscv_parse_subset (&riscv_rps_ld_out, out_attr[Tag_RISCV_arch].s))
+ {
+ cache_result = NOT_USE_ZCMT;
+ cache_info = info;
+ return cache_result;
+ }
+
+ cache_result = riscv_subset_supports (&riscv_rps_ld_out, "zcmt");
+ cache_info = info;
+ riscv_release_subset_list (&subsets);
+
+ return cache_result;
+}
+
+static bool
+riscv_elf_create_jvt_section (bfd *abfd, struct bfd_link_info *info)
+{
+ asection *sec;
+ struct riscv_elf_link_hash_table *htab;
+
+ /* Skip if no Zcmt. */
+ if (!riscv_use_jvt (info))
+ return true;
+
+ htab = riscv_elf_hash_table (info);
+ if (!riscv_ensure_jvt_htab (htab))
+ return false;
+ sec = bfd_get_linker_section (abfd, TABLE_JUMP_SEC_NAME);
+
+ if (sec != NULL)
+ return true;
+
+ if (htab->jvt_htab->jvt_sec == NULL)
+ {
+ sec = bfd_make_section_anyway_with_flags (
+ abfd, TABLE_JUMP_SEC_NAME,
+ (SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_HAS_CONTENTS | SEC_IN_MEMORY
+ | SEC_KEEP | SEC_CODE));
+
+ if (sec == NULL || !bfd_set_section_alignment (sec, 6)
+ || !bfd_set_section_size (sec, 256 * RISCV_ELF_WORD_BYTES))
+ return false;
+
+ htab->jvt_htab->jvt_sec = sec;
+ htab->jvt_htab->jvt_sec_owner = abfd;
+ }
+
+ return true;
+}
+
/* Look through the relocs for a section during the first phase, and
allocate space in the global offset table or procedure linkage
table. */
@@ -1266,6 +1609,9 @@ riscv_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
}
}
+ if (!riscv_elf_create_jvt_section (abfd, info))
+ return false;
+
return true;
}
@@ -1699,9 +2045,11 @@ riscv_elf_late_size_sections (bfd *output_bfd, struct bfd_link_info *info)
bfd *dynobj;
asection *s;
bfd *ibfd;
+ riscv_jvt_htab_t *jvt_htab;
htab = riscv_elf_hash_table (info);
BFD_ASSERT (htab != NULL);
+ jvt_htab = htab->jvt_htab;
dynobj = htab->elf.dynobj;
if (dynobj == NULL)
return true;
@@ -1719,6 +2067,30 @@ riscv_elf_late_size_sections (bfd *output_bfd, struct bfd_link_info *info)
}
}
+ struct bfd_link_hash_entry *bh = NULL;
+
+ if (riscv_use_jvt (info) && jvt_htab != NULL && jvt_htab->jvt_sec)
+ {
+ s = jvt_htab->jvt_sec;
+
+ BFD_ASSERT (s != NULL);
+
+ s->contents = (bfd_byte *) bfd_alloc (s->owner, s->size);
+ s->alloced = 1;
+
+ if (s->contents == NULL)
+ return false;
+
+ if (s->output_section == NULL)
+ return false;
+
+ if (!_bfd_generic_link_add_one_symbol (
+ info, output_bfd, RISCV_TABLE_JUMP_BASE_SYMBOL, BSF_GLOBAL, s,
+ (bfd_vma) 0, (const char *) NULL, true,
+ get_elf_backend_data (output_bfd)->collect, &bh))
+ return false;
+ }
+
/* Set up .got offsets for local syms, and space for local dynamic
relocs. */
for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link.next)
@@ -2042,6 +2414,9 @@ perform_relocation (const reloc_howto_type *howto,
value = ENCODE_CBTYPE_IMM (value);
break;
+ case R_RISCV_TABLE_JUMP:
+ return bfd_reloc_ok;
+
case R_RISCV_RVC_JUMP:
if (!VALID_CJTYPE_IMM (value))
return bfd_reloc_overflow;
@@ -2986,6 +3361,17 @@ riscv_elf_relocate_section (bfd *output_bfd,
}
break;
+ case R_RISCV_TABLE_JUMP:
+ {
+ bfd_vma insn = bfd_getl16 (contents + rel->r_offset);
+ unsigned int tbl_index = EXTRACT_ZCMT_INDEX (insn);
+ if (!riscv_ensure_jvt_htab (htab))
+ return false;
+ htab->jvt_htab->jvt_addrs[tbl_index] = relocation + rel->r_addend;
+ htab->jvt_htab->jvt_addrs[tbl_index] &= ~(bfd_vma) 1;
+ }
+ break;
+
case R_RISCV_CALL:
case R_RISCV_CALL_PLT:
/* Handle a call to an undefined weak function. This won't be
@@ -4811,6 +5197,228 @@ _riscv_relax_delete_immediate (bfd *abfd,
link_info, p, 0, sec->size);
}
+/*
+ Return jt jump table if rd is x0, or jalt jump table if rd is other registers.
+ */
+
+static htab_t
+riscv_get_jvt_htab (struct bfd_link_info *info, unsigned int link_reg)
+{
+ struct riscv_elf_link_hash_table *htab = riscv_elf_hash_table (info);
+ riscv_jvt_htab_t *jvt_htab;
+
+ if (htab == NULL || !riscv_ensure_jvt_htab (htab))
+ return NULL;
+
+ jvt_htab = htab->jvt_htab;
+
+ if (link_reg == 0)
+ return jvt_htab->jt_htab;
+ if (link_reg == X_RA)
+ return jvt_htab->jalt_htab;
+
+ return NULL;
+}
+
+/* Record JAL or CALL in jump table hash. */
+
+static bool
+_bfd_riscv_jvt_record (bfd *abfd, asection *sec ATTRIBUTE_UNUSED,
+ asection *sym_sec ATTRIBUTE_UNUSED,
+ struct bfd_link_info *link_info, Elf_Internal_Rela *rel,
+ bfd_vma symval ATTRIBUTE_UNUSED,
+ bfd_vma max_alignment ATTRIBUTE_UNUSED,
+ bfd_vma reserve_size ATTRIBUTE_UNUSED,
+ bool *again ATTRIBUTE_UNUSED,
+ riscv_pcgp_relocs *pcgp_relocs ATTRIBUTE_UNUSED,
+ bool undefined_weak ATTRIBUTE_UNUSED)
+{
+ bfd_byte *contents = elf_section_data (sec)->this_hdr.contents;
+ bfd_vma jal = bfd_getl32 (contents + rel->r_offset);
+ int type = ELFNN_R_TYPE (rel->r_info);
+ unsigned int rd = (jal >> OP_SH_RD) & OP_MASK_RD;
+ htab_t tbljal_htab = riscv_get_jvt_htab (link_info, rd);
+ const char *name = riscv_get_symbol_name (abfd, ELFNN_R_SYM (rel->r_info));
+ unsigned long r_symndx = ELFNN_R_SYM (rel->r_info);
+
+ if (tbljal_htab == NULL || name == NULL
+ || riscv_is_special_symbol_name (abfd, name))
+ return true;
+
+ unsigned int benefit = 0;
+ if (type == R_RISCV_CALL || type == R_RISCV_CALL_PLT)
+ benefit = (4 + 4) - 2; /* AUIPC + JALR -> CM.[JT,JALT]. */
+ else if (type == R_RISCV_JAL)
+ benefit = 4 - 2; /* JAL -> CM.[JT,JALT]. */
+ else
+ BFD_ASSERT (false);
+
+ return riscv_update_jvt_entry (tbljal_htab, abfd, r_symndx, benefit, name);
+}
+
+/* Relax JAL to CM.[JT,JALT]. */
+
+static bool
+_bfd_riscv_relax_jal (bfd *abfd, asection *sec ATTRIBUTE_UNUSED,
+ asection *sym_sec ATTRIBUTE_UNUSED,
+ struct bfd_link_info *link_info, Elf_Internal_Rela *rel,
+ bfd_vma symval ATTRIBUTE_UNUSED,
+ bfd_vma max_alignment ATTRIBUTE_UNUSED,
+ bfd_vma reserve_size ATTRIBUTE_UNUSED,
+ bool *again ATTRIBUTE_UNUSED,
+ riscv_pcgp_relocs *pcgp_relocs ATTRIBUTE_UNUSED,
+ bool undefined_weak ATTRIBUTE_UNUSED)
+{
+ bfd_byte *contents = elf_section_data (sec)->this_hdr.contents;
+ bfd_vma jal = bfd_getl32 (contents + rel->r_offset);
+
+ if (link_info->relax_pass == RELAX_PASS_SHORTEN_LUI_CALL_TRREL_PCREL
+ && ((jal ^ MATCH_CM_JALT) & MASK_CM_JALT) == 0)
+ {
+ rel->r_info
+ = ELFNN_R_INFO (ELFNN_R_SYM (rel->r_info), R_RISCV_TABLE_JUMP);
+ *again = true;
+ return riscv_relax_delete_bytes (abfd, sec, rel->r_offset + 2, 2,
+ link_info, pcgp_relocs, rel);
+ }
+ return true;
+}
+
+static bfd_vma
+riscv_get_symbol_addr (bfd *input_bfd, unsigned int symndx)
+{
+ Elf_Internal_Shdr *symtab_hdr = &elf_symtab_hdr (input_bfd);
+ if (riscv_is_local_symbol (symtab_hdr, symndx))
+ {
+ if (!symtab_hdr->contents)
+ return 0;
+
+ Elf_Internal_Sym *sym
+ = ((Elf_Internal_Sym *) symtab_hdr->contents + symndx);
+ bfd_vma sec_offset
+ = sec_addr (elf_elfsections (input_bfd)[sym->st_shndx]->bfd_section);
+ return sec_offset + sym->st_value;
+ }
+ else /* global symbol */
+ {
+ struct elf_link_hash_entry *h;
+ unsigned indx = symndx - symtab_hdr->sh_info;
+ h = elf_sym_hashes (input_bfd)[indx];
+ return sec_addr (h->root.u.def.section) + h->root.u.def.value;
+ }
+}
+
+/* Iterate all jump table entris and update addresses after alignment relax
+ pass. */
+static void
+riscv_update_jvt_addrs (struct bfd_link_info *link_info)
+{
+ struct riscv_elf_link_hash_table *htab = riscv_elf_hash_table (link_info);
+ riscv_jvt_htab_t *jvt_htab = htab->jvt_htab;
+ if (jvt_htab == NULL)
+ return;
+ for (uintNN_t i = 0; i < ZCMT_TOTAL_ENTRIES; i++)
+ {
+ riscv_jvt_slot_t *slot = &jvt_htab->slots[i];
+ if (slot->benefit != 0)
+ {
+ bfd_vma addr = riscv_get_symbol_addr (slot->input_bfd, slot->symndx);
+ if (addr != 0)
+ jvt_htab->jvt_addrs[i] = addr;
+ }
+ }
+}
+
+typedef struct
+{
+ riscv_jvt_htab_t *htab;
+ unsigned int start;
+ unsigned int end;
+} riscv_jvt_args;
+
+static int
+riscv_ranking_jvt (void **entry_ptr, void *_arg)
+{
+ const riscv_jvt_htab_entry *entry = *entry_ptr;
+ riscv_jvt_args *arg = _arg;
+ riscv_jvt_slot_t *slots = arg->htab->slots;
+
+ /* Search insert position using binary search (descending order). */
+ unsigned int left = arg->start;
+ unsigned int right = arg->end + 1;
+
+ while (left < right)
+ {
+ unsigned int mid = (left + right) / 2;
+ if (slots[mid].benefit == entry->benefit)
+ {
+ left = mid;
+ break;
+ }
+ else if (slots[mid].benefit == 0 || slots[mid].benefit < entry->benefit)
+ right = mid;
+ else
+ left = mid + 1;
+ }
+
+ /* Shift slots to make room for the new entry. */
+ for (unsigned int idx = arg->end; idx > left; idx--)
+ slots[idx] = slots[idx - 1];
+
+ if (left <= arg->end)
+ {
+ slots[left].benefit = entry->benefit;
+ slots[left].name = entry->name;
+ slots[left].input_bfd = entry->input_bfd;
+ slots[left].symndx = entry->symndx;
+ }
+
+ return true;
+}
+
+static bool
+riscv_record_jvt_index (htab_t htab, riscv_jvt_args *args)
+{
+ unsigned int idx;
+ riscv_jvt_htab_t *jvt_htab = args->htab;
+ riscv_jvt_slot_t *slots = jvt_htab->slots;
+ riscv_jvt_htab_entry *entry = NULL;
+
+ for (idx = args->start; idx <= args->end && slots[idx].benefit != 0; idx++)
+ {
+ riscv_jvt_htab_entry search = {.index = 0,
+ .input_bfd = slots[idx].input_bfd,
+ .symndx = slots[idx].symndx,
+ .name = NULL,
+ .benefit = 0};
+ entry = htab_find (htab, &search);
+
+ BFD_ASSERT (entry != NULL);
+ entry->index = idx + 1;
+ jvt_htab->total_benefits += slots[idx].benefit;
+ }
+
+ /* True if there is at least one entry in table jump section. */
+ if (entry && entry->index)
+ jvt_htab->end_idx = entry->index;
+
+ return true;
+}
+
+static bool
+riscv_jvt_profiling (riscv_jvt_htab_t *jvt_htab, riscv_jvt_args *args)
+{
+ args->start = ZCMT_JT_BEGIN, args->end = ZCMT_JT_END;
+ /* Do a ranking. */
+ htab_traverse (jvt_htab->jt_htab, riscv_ranking_jvt, args);
+ riscv_record_jvt_index (jvt_htab->jt_htab, args);
+
+ args->start = ZCMT_JALT_BEGIN, args->end = ZCMT_JALT_END;
+ htab_traverse (jvt_htab->jalt_htab, riscv_ranking_jvt, args);
+ riscv_record_jvt_index (jvt_htab->jalt_htab, args);
+ return true;
+}
+
/* Delete the bytes for R_RISCV_DELETE relocs. */
static bool
@@ -4870,7 +5478,57 @@ typedef bool (*relax_func_t) (bfd *, asection *, asection *,
riscv_pcgp_relocs *,
bool undefined_weak);
-/* Relax AUIPC + JALR into JAL. */
+/* Mark JAL/CALL/CALL_PLT to use table jump instructions. */
+
+static bool
+_bfd_riscv_jvt_mark (bfd *abfd, asection *sec,
+ asection *sym_sec ATTRIBUTE_UNUSED,
+ struct bfd_link_info *link_info, Elf_Internal_Rela *rel,
+ bfd_vma symval ATTRIBUTE_UNUSED,
+ bfd_vma max_alignment ATTRIBUTE_UNUSED,
+ bfd_vma reserve_size ATTRIBUTE_UNUSED,
+ bool *again ATTRIBUTE_UNUSED,
+ riscv_pcgp_relocs *pcgp_relocs ATTRIBUTE_UNUSED,
+ bool undefined_weak ATTRIBUTE_UNUSED)
+{
+ bfd_byte *contents = elf_section_data (sec)->this_hdr.contents;
+ int type = ELFNN_R_TYPE (rel->r_info);
+ bfd_byte *jump_inst_location = NULL;
+
+ if (type == R_RISCV_JAL)
+ jump_inst_location = contents + rel->r_offset;
+ else if (type == R_RISCV_CALL || type == R_RISCV_CALL_PLT)
+ jump_inst_location = contents + rel->r_offset + 4;
+ else /* We only relax JAL/CALL/CALL_PLT. */
+ BFD_ASSERT (false);
+
+ bfd_vma jump_inst = bfd_getl32 (jump_inst_location);
+ int rd = (jump_inst >> OP_SH_RD) & OP_MASK_RD;
+ htab_t tbljal_htab = riscv_get_jvt_htab (link_info, rd);
+
+ /* Check if it uses a valid link register. */
+ if (tbljal_htab == NULL)
+ return true;
+
+ unsigned long r_symndx = ELFNN_R_SYM (rel->r_info);
+
+ riscv_jvt_htab_entry search = {.index = 0,
+ .input_bfd = abfd,
+ .symndx = r_symndx,
+ .benefit = 0,
+ .name = NULL};
+ riscv_jvt_htab_entry *entry = htab_find (tbljal_htab, &search);
+
+ /* entry->index == 0 when the entry is not used as a table jump entry. */
+ if (entry != NULL && entry->index > 0)
+ {
+ bfd_vma target = MATCH_CM_JALT | ENCODE_ZCMT_INDEX (entry->index - 1);
+ bfd_putl32 (target, contents + rel->r_offset);
+ }
+ return true;
+}
+
+/* Relax AUIPC + JALR into JAL or CM.[JT,JALT]. */
static bool
_bfd_riscv_relax_call (bfd *abfd, asection *sec, asection *sym_sec,
@@ -4889,6 +5547,19 @@ _bfd_riscv_relax_call (bfd *abfd, asection *sec, asection *sym_sec,
bfd_vma auipc, jalr;
int rd, r_type, len = 4, rvc = elf_elfheader (abfd)->e_flags & EF_RISCV_RVC;
+ auipc = bfd_getl32 (contents + rel->r_offset);
+ jalr = bfd_getl32 (contents + rel->r_offset + 4);
+
+ /* Relax a table jump instruction that is marked. */
+ if (((auipc ^ MATCH_CM_JALT) & MASK_CM_JALT) == 0)
+ {
+ rel->r_info
+ = ELFNN_R_INFO (ELFNN_R_SYM (rel->r_info), R_RISCV_TABLE_JUMP);
+ *again = true;
+ return riscv_relax_delete_bytes (abfd, sec, rel->r_offset + 2, 6,
+ link_info, pcgp_relocs, rel);
+ }
+
/* If the call crosses section boundaries, an alignment directive could
cause the PC-relative offset to later increase, so we need to add in the
max alignment of any section inclusive from the call to the target.
@@ -4902,14 +5573,13 @@ _bfd_riscv_relax_call (bfd *abfd, asection *sec, asection *sym_sec,
}
/* See if this function call can be shortened. */
- if (!VALID_JTYPE_IMM (foff) && !(!bfd_link_pic (link_info) && near_zero))
+ if (!VALID_JTYPE_IMM (foff) && !(!bfd_link_pic (link_info) && near_zero)
+ && link_info->relax_pass != RELAX_PASS_TABLE_JUMP_PROFILING)
return true;
/* Shorten the function call. */
BFD_ASSERT (rel->r_offset + 8 <= sec->size);
- auipc = bfd_getl32 (contents + rel->r_offset);
- jalr = bfd_getl32 (contents + rel->r_offset + 4);
rd = (jalr >> OP_SH_RD) & OP_MASK_RD;
rvc = rvc && VALID_CJTYPE_IMM (foff);
@@ -4962,6 +5632,11 @@ _bfd_riscv_get_max_alignment (asection *sec, bfd_vma gp)
for (o = sec->output_section->owner->sections; o != NULL; o = o->next)
{
bool valid = true;
+ /* Exclude table jump section because it is not affecting the
+ alignment. */
+ if (strcmp (o->name, TABLE_JUMP_SEC_NAME) == 0)
+ continue;
+
if (gp
&& !(VALID_ITYPE_IMM (sec_addr (o) - gp)
|| VALID_ITYPE_IMM (sec_addr (o) + o->size - gp)))
@@ -5368,10 +6043,10 @@ bfd_elfNN_riscv_set_data_segment_info (struct bfd_link_info *info,
}
/* Relax a section.
-
- Pass 0: Shortens code sequences for LUI/CALL/TPREL/PCREL relocs and
+ Pass 0: Table jump profiling
+ Pass 1: Shortens code sequences for LUI/CALL/TPREL/PCREL relocs and
deletes the obsolete bytes.
- Pass 1: Which cannot be disabled, handles code alignment directives. */
+ Pass 2: Which cannot be disabled, handles code alignment directives. */
static bool
_bfd_riscv_relax_section (bfd *abfd, asection *sec,
@@ -5384,19 +6059,18 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec,
Elf_Internal_Rela *relocs;
bool ret = false;
unsigned int i;
- bfd_vma max_alignment, reserve_size = 0;
+ bfd_vma max_alignment, reserve_size = 0, used_bytes, trimmed_bytes;
riscv_pcgp_relocs pcgp_relocs;
static asection *first_section = NULL;
+ riscv_jvt_htab_t *jvt_htab = NULL;
+ struct elf_link_hash_entry *jvt_sym;
*again = false;
- if (bfd_link_relocatable (info)
- || sec->sec_flg0
- || sec->reloc_count == 0
- || (sec->flags & SEC_RELOC) == 0
- || (sec->flags & SEC_HAS_CONTENTS) == 0
+ if (bfd_link_relocatable (info) || sec->sec_flg0 || sec->reloc_count == 0
+ || (sec->flags & SEC_RELOC) == 0 || (sec->flags & SEC_HAS_CONTENTS) == 0
|| (info->disable_target_specific_optimizations
- && info->relax_pass == 0)
+ && info->relax_pass <= RELAX_PASS_CAN_BE_DISABLED)
/* The exp_seg_relro_adjust is enum phase_enum (0x4),
and defined in ld/ldexp.h. */
|| *(htab->data_segment_phase) == 4)
@@ -5417,6 +6091,16 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec,
else if (!(relocs = _bfd_elf_link_read_relocs (abfd, sec, NULL, NULL,
info->keep_memory)))
goto fail;
+ /* Read this BFD's contents if we haven't done so already. */
+ if (!data->this_hdr.contents
+ && !bfd_malloc_and_get_section (abfd, sec, &data->this_hdr.contents))
+ goto fail;
+
+ /* Read this BFD's symbols if we haven't done so already. */
+ if (symtab_hdr->sh_info != 0 && !symtab_hdr->contents
+ && !(symtab_hdr->contents = (unsigned char *) bfd_elf_get_elf_syms (
+ abfd, symtab_hdr, symtab_hdr->sh_info, 0, NULL, NULL, NULL)))
+ goto fail;
/* Estimate the maximum alignment for all output sections once time
should be enough. */
@@ -5427,6 +6111,104 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec,
htab->max_alignment = max_alignment;
}
+ /* relax_trip 0 / JVT_PROFILING_RECORD_SYM:
+ Record symbol address and expected size saving of each relocation
+ that can be replaced by table jump instructions.
+
+ relax_trip 1 / JVT_PROFILING_RANK:
+ Rank the best ZCMT_JT_NUM_ENTRIES relocations to replace for cm.jt and
+ the best ZCMT_JALT_NUM_ENTRIES relocations for cm.jalt in terms of the
+ total size saved.
+
+ relax_trip 2 / JVT_PROFILING_DETERMINE:
+ Check if table jump can reduce the size, and delete the whole table
+ jump section if the size will not be reduced.
+ If table jump can save size, and then we replace all targeted
+ instructions/instruction pairs (e.g. auipc+jalr) to table jump
+ instructions with the index encoded. Then add relocations to the jump
+ table.
+
+ relax_trip 3 / JVT_PROFILING_TRIM:
+ Trim unused slots in the table jump section. */
+
+ if (info->relax_pass == RELAX_PASS_TABLE_JUMP_PROFILING
+ && riscv_use_jvt (info))
+ {
+ if (!riscv_ensure_jvt_htab (htab))
+ goto fail;
+ jvt_htab = htab->jvt_htab;
+
+ riscv_relax_delete_bytes = _riscv_relax_delete_immediate;
+ /* Avoid size benefits of relocations to be recorded multiple times. */
+ if (info->relax_trip == JVT_PROFILING_RECORD_SYM
+ && *(htab->data_segment_phase) != 0)
+ return true;
+
+ /* Rank the entries, and calculate the expected total saving. */
+ else if (info->relax_trip == JVT_PROFILING_RANK)
+ {
+ *again = true;
+ /* Profiling stage finished. */
+ if (jvt_htab->end_idx != 0)
+ return true;
+
+ riscv_jvt_args args = {jvt_htab, 0, 0};
+ /* Estimate size benefits if table jump is used. */
+ riscv_jvt_profiling (jvt_htab, &args);
+ return true;
+ }
+
+ /* Skip generating table jump instructions if they do not help reduce
+ * code size. */
+ else if (info->relax_trip == JVT_PROFILING_DETERMINE)
+ {
+ /* Check if table jump can save size. Skip generating table
+ jump instruction if not. */
+ if (jvt_htab->end_idx >= 0
+ && jvt_htab->total_benefits
+ <= (unsigned int) jvt_htab->end_idx * RISCV_ELF_WORD_BYTES
+ && jvt_htab->jvt_sec->size > 0U)
+ {
+ jvt_sym = elf_link_hash_lookup (elf_hash_table (info),
+ RISCV_TABLE_JUMP_BASE_SYMBOL,
+ false, false, true);
+ jvt_sym->root.u.def.section = bfd_abs_section_ptr;
+ return riscv_relax_delete_bytes (jvt_htab->jvt_sec->owner,
+ jvt_htab->jvt_sec, 0,
+ jvt_htab->jvt_sec->size, info,
+ NULL, NULL);
+ }
+
+ else if (jvt_htab->jvt_sec->size == 0)
+ return true;
+ else if (jvt_htab->jvt_sec->size > 0)
+ *again = true;
+ }
+
+ /* Trim the unused slot at the table jump section.
+ TODO: skip generating entries if its saving is less than
+ RISCV_ELF_WORD_BYTES. We should skip those insns at the relax trip 2
+ without deleting bytes. */
+ else if (info->relax_trip == JVT_PROFILING_TRIM)
+ {
+ /* Table jump entry section is trimmed. */
+ if (jvt_htab->end_idx < 0)
+ return true;
+
+ used_bytes = jvt_htab->end_idx * RISCV_ELF_WORD_BYTES;
+ trimmed_bytes
+ = (ZCMT_TOTAL_ENTRIES - jvt_htab->end_idx) * RISCV_ELF_WORD_BYTES;
+ /* Trim unused slots. */
+ if (!riscv_relax_delete_bytes (jvt_htab->jvt_sec->owner,
+ jvt_htab->jvt_sec, used_bytes,
+ trimmed_bytes, info, NULL, NULL))
+ return false;
+ /* Mark table jump profiling stage as completed. */
+ jvt_htab->end_idx = -1;
+ return true;
+ }
+ }
+
/* Examine and consider relaxing each reloc. */
for (i = 0; i < sec->reloc_count; i++)
{
@@ -5440,17 +6222,38 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec,
relax_func = NULL;
riscv_relax_delete_bytes = NULL;
- if (info->relax_pass == 0)
+ if (info->relax_pass == RELAX_PASS_TABLE_JUMP_PROFILING)
+ {
+ if (!riscv_use_jvt (info))
+ return true;
+ if (info->relax_trip == JVT_PROFILING_RECORD_SYM)
+ {
+ if (type == R_RISCV_CALL || type == R_RISCV_CALL_PLT
+ || type == R_RISCV_JAL)
+ relax_func = _bfd_riscv_jvt_record;
+ else
+ continue;
+ *again = true;
+ }
+ else if (info->relax_trip == JVT_PROFILING_DETERMINE)
+ {
+ if (type == R_RISCV_CALL || type == R_RISCV_CALL_PLT
+ || type == R_RISCV_JAL)
+ relax_func = _bfd_riscv_jvt_mark;
+ else
+ continue;
+ }
+ }
+ else if (info->relax_pass == RELAX_PASS_SHORTEN_LUI_CALL_TRREL_PCREL)
{
- if (type == R_RISCV_CALL
- || type == R_RISCV_CALL_PLT)
+ if (type == R_RISCV_CALL || type == R_RISCV_CALL_PLT)
relax_func = _bfd_riscv_relax_call;
- else if (type == R_RISCV_HI20
- || type == R_RISCV_LO12_I
+ else if (type == R_RISCV_JAL)
+ relax_func = _bfd_riscv_relax_jal;
+ else if (type == R_RISCV_HI20 || type == R_RISCV_LO12_I
|| type == R_RISCV_LO12_S)
relax_func = _bfd_riscv_relax_lui;
- else if (type == R_RISCV_TPREL_HI20
- || type == R_RISCV_TPREL_ADD
+ else if (type == R_RISCV_TPREL_HI20 || type == R_RISCV_TPREL_ADD
|| type == R_RISCV_TPREL_LO12_I
|| type == R_RISCV_TPREL_LO12_S)
relax_func = _bfd_riscv_relax_tls_le;
@@ -5464,15 +6267,18 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec,
riscv_relax_delete_bytes = _riscv_relax_delete_piecewise;
/* Only relax this reloc if it is paired with R_RISCV_RELAX. */
- if (i == sec->reloc_count - 1
- || ELFNN_R_TYPE ((rel + 1)->r_info) != R_RISCV_RELAX
- || rel->r_offset != (rel + 1)->r_offset)
+ if (type != R_RISCV_JAL
+ && (i == sec->reloc_count - 1
+ || ELFNN_R_TYPE ((rel + 1)->r_info) != R_RISCV_RELAX
+ || rel->r_offset != (rel + 1)->r_offset))
continue;
/* Skip over the R_RISCV_RELAX. */
- i++;
+ if (type != R_RISCV_JAL)
+ i++;
}
- else if (info->relax_pass == 1 && type == R_RISCV_ALIGN)
+ else if (info->relax_pass == RELAX_PASS_ALIGNMENT
+ && type == R_RISCV_ALIGN)
{
relax_func = _bfd_riscv_relax_align;
riscv_relax_delete_bytes = _riscv_relax_delete_immediate;
@@ -5482,20 +6288,6 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec,
data->relocs = relocs;
- /* Read this BFD's contents if we haven't done so already. */
- if (!data->this_hdr.contents
- && !bfd_malloc_and_get_section (abfd, sec, &data->this_hdr.contents))
- goto fail;
-
- /* Read this BFD's symbols if we haven't done so already. */
- if (symtab_hdr->sh_info != 0
- && !symtab_hdr->contents
- && !(symtab_hdr->contents =
- (unsigned char *) bfd_elf_get_elf_syms (abfd, symtab_hdr,
- symtab_hdr->sh_info,
- 0, NULL, NULL, NULL)))
- goto fail;
-
/* Get the value of the symbol referred to by the reloc. */
if (ELFNN_R_SYM (rel->r_info) < symtab_hdr->sh_info)
{
@@ -5841,6 +6633,36 @@ riscv_elf_obj_attrs_arg_type (obj_attr_tag_t tag)
return (tag & 1) != 0 ? ATTR_TYPE_FLAG_STR_VAL : ATTR_TYPE_FLAG_INT_VAL;
}
+static bool
+riscv_final_link (bfd *abfd, struct bfd_link_info *info)
+{
+ struct riscv_elf_link_hash_table *htab = riscv_elf_hash_table (info);
+ riscv_jvt_htab_t *jvt_htab = htab->jvt_htab;
+
+ /* Update jump table addresses before bfd_elf_final_link because it freed
+ memory. */
+ if (riscv_use_jvt (info)
+ /* jvt_sec is not created if no relocation happened, so we
+ need to check section pointer here. */
+ && jvt_htab != NULL && jvt_htab->jvt_sec)
+ riscv_update_jvt_addrs (info);
+
+ if (!_bfd_elf_final_link (abfd, info))
+ return false;
+
+ if (riscv_use_jvt (info) && jvt_htab != NULL && jvt_htab->jvt_sec)
+ {
+ asection *sec = jvt_htab->jvt_sec;
+ asection *out_sec = sec->output_section;
+
+ if (!bfd_set_section_contents (abfd, out_sec, jvt_htab->jvt_addrs,
+ (file_ptr) sec->output_offset, sec->size))
+ return false;
+ }
+
+ return true;
+}
+
/* Do not choose mapping symbols as a function name. */
static bfd_size_type
@@ -6006,6 +6828,7 @@ elfNN_riscv_merge_gnu_properties (struct bfd_link_info *info, bfd *abfd,
#define bfd_elfNN_mkobject elfNN_riscv_mkobject
#define bfd_elfNN_get_synthetic_symtab \
elfNN_riscv_get_synthetic_symtab
+#define bfd_elfNN_bfd_final_link riscv_final_link
#define elf_backend_reloc_type_class riscv_reloc_type_class
#define elf_backend_copy_indirect_symbol riscv_elf_copy_indirect_symbol
diff --git a/bfd/elfxx-riscv.h b/bfd/elfxx-riscv.h
index ed4e78efc83..d3148a8d6cc 100644
--- a/bfd/elfxx-riscv.h
+++ b/bfd/elfxx-riscv.h
@@ -39,6 +39,8 @@ struct riscv_elf_params
bool relax_gp;
/* Whether to check if SUB_ULEB128 relocation has non-zero addend. */
bool check_uleb128;
+ /* Whether to relax code sequences for zcmt. */
+ bool relax_zcmt;
};
extern void riscv_elf32_set_options (struct bfd_link_info *,
@@ -96,6 +98,25 @@ typedef struct
bool check_unknown_prefixed_ext;
} riscv_parse_subset_t;
+/* List the relxation pass. */
+enum riscv_relax_pass
+{
+ RELAX_PASS_TABLE_JUMP_PROFILING,
+ RELAX_PASS_SHORTEN_LUI_CALL_TRREL_PCREL,
+ RELAX_PASS_CAN_BE_DISABLED = RELAX_PASS_SHORTEN_LUI_CALL_TRREL_PCREL,
+ RELAX_PASS_ALIGNMENT,
+ RELAX_PASS_END,
+};
+
+/* Trips for jvt profiling relaxation pass. */
+enum jvt_profiling_trip
+{
+ JVT_PROFILING_RECORD_SYM,
+ JVT_PROFILING_RANK,
+ JVT_PROFILING_DETERMINE,
+ JVT_PROFILING_TRIM,
+};
+
extern bool
riscv_parse_subset (riscv_parse_subset_t *,
const char *);
diff --git a/include/elf/riscv.h b/include/elf/riscv.h
index 2ea1ae82eb6..ce888ce9244 100644
--- a/include/elf/riscv.h
+++ b/include/elf/riscv.h
@@ -104,6 +104,7 @@ END_RELOC_NUMBERS (R_RISCV_max)
#define R_RISCV_GPREL_S (R_RISCV_max + 3)
#define R_RISCV_TPREL_I (R_RISCV_max + 4)
#define R_RISCV_TPREL_S (R_RISCV_max + 5)
+#define R_RISCV_TABLE_JUMP (R_RISCV_max + 6)
/* Processor specific flags for the ELF header e_flags field. */
@@ -131,6 +132,10 @@ END_RELOC_NUMBERS (R_RISCV_max)
/* The name of the global pointer symbol. */
#define RISCV_GP_SYMBOL "__global_pointer$"
+#define RISCV_TABLE_JUMP_BASE_SYMBOL "__jvt_base$"
+
+#define TABLE_JUMP_SEC_NAME ".riscv.jvt"
+
/* Processor specific dynamic array tags. */
#define DT_RISCV_VARIANT_CC (DT_LOPROC + 1)
diff --git a/include/opcode/riscv.h b/include/opcode/riscv.h
index 858fcce6871..49631ddba95 100644
--- a/include/opcode/riscv.h
+++ b/include/opcode/riscv.h
@@ -680,6 +680,15 @@ struct riscv_opcode
disassembler, and requires special treatment by the assembler. */
#define INSN_MACRO 0xffffffff
+/* Define Zcmt jump table constants. */
+#define ZCMT_TOTAL_ENTRIES 256
+#define ZCMT_JT_BEGIN 0
+#define ZCMT_JT_END 31
+#define ZCMT_JT_NUM_ENTRIES (ZCMT_JT_END - ZCMT_JT_BEGIN + 1)
+#define ZCMT_JALT_BEGIN (ZCMT_JT_END + 1)
+#define ZCMT_JALT_END (ZCMT_TOTAL_ENTRIES - 1)
+#define ZCMT_JALT_NUM_ENTRIES (ZCMT_JALT_END - ZCMT_JALT_BEGIN + 1)
+
/* This is a list of macro expanded instructions. */
enum
{
diff --git a/ld/emultempl/riscvelf.em b/ld/emultempl/riscvelf.em
index 78e1fcde68e..988fd03cd01 100644
--- a/ld/emultempl/riscvelf.em
+++ b/ld/emultempl/riscvelf.em
@@ -26,7 +26,8 @@ fragment <<EOF
#include "elfxx-riscv.h"
static struct riscv_elf_params params = { .relax_gp = 1,
- .check_uleb128 = 0};
+ .check_uleb128 = 0,
+ .relax_zcmt = 0};
EOF
# Define some shell vars to insert bits of code into the standard elf
@@ -36,6 +37,8 @@ PARSE_AND_LIST_LONGOPTS=${PARSE_AND_LIST_LONGOPTS}'
{ "no-relax-gp", no_argument, NULL, OPTION_NO_RELAX_GP },
{ "check-uleb128", no_argument, NULL, OPTION_CHECK_ULEB128 },
{ "no-check-uleb128", no_argument, NULL, OPTION_NO_CHECK_ULEB128 },
+ { "relax-zcmt", no_argument, NULL, OPTION_RELAX_ZCMT },
+ { "no-relax-zcmt", no_argument, NULL, OPTION_NO_RELAX_ZCMT },
'
PARSE_AND_LIST_OPTIONS=${PARSE_AND_LIST_OPTIONS}'
@@ -43,6 +46,8 @@ PARSE_AND_LIST_OPTIONS=${PARSE_AND_LIST_OPTIONS}'
fprintf (file, _(" --no-relax-gp Don'\''t perform GP relaxation\n"));
fprintf (file, _(" --check-uleb128 Check if SUB_ULEB128 has non-zero addend\n"));
fprintf (file, _(" --no-check-uleb128 Don'\''t check if SUB_ULEB128 has non-zero addend\n"));
+ fprintf (file, _(" --relax-zcmt Perform Zcmt relaxation\n"));
+ fprintf (file, _(" --no-relax-zcmt Don'\''t perform Zcmt relaxation (default)\n"));
'
PARSE_AND_LIST_ARGS_CASES=${PARSE_AND_LIST_ARGS_CASES}'
@@ -61,6 +66,14 @@ PARSE_AND_LIST_ARGS_CASES=${PARSE_AND_LIST_ARGS_CASES}'
case OPTION_NO_CHECK_ULEB128:
params.check_uleb128 = 0;
break;
+
+ case OPTION_RELAX_ZCMT:
+ params.relax_zcmt = 1;
+ break;
+
+ case OPTION_NO_RELAX_ZCMT:
+ params.relax_zcmt = 0;
+ break;
'
fragment <<EOF
@@ -81,7 +94,7 @@ riscv_elf_before_allocation (void)
ENABLE_RELAXATION;
}
- link_info.relax_pass = 2;
+ link_info.relax_pass = RELAX_PASS_END;
}
static void
diff --git a/ld/ldlex.h b/ld/ldlex.h
index 24cac1cdfc0..73d30117db7 100644
--- a/ld/ldlex.h
+++ b/ld/ldlex.h
@@ -96,7 +96,7 @@ enum option_values
OPTION_WARN_ONCE,
OPTION_WARN_SECTION_ALIGN,
OPTION_SPLIT_BY_RELOC,
- OPTION_SPLIT_BY_FILE ,
+ OPTION_SPLIT_BY_FILE,
OPTION_WHOLE_ARCHIVE,
OPTION_ADD_DT_NEEDED_FOR_DYNAMIC,
OPTION_NO_ADD_DT_NEEDED_FOR_DYNAMIC,
@@ -425,6 +425,8 @@ enum option_values
OPTION_NO_RELAX_GP,
OPTION_CHECK_ULEB128,
OPTION_NO_CHECK_ULEB128,
+ OPTION_RELAX_ZCMT,
+ OPTION_NO_RELAX_ZCMT,
/* Used by emultempl/rxelf.em. */
OPTION_NO_FLAG_MISMATCH_WARNINGS,
OPTION_IGNORE_LMA,
--
2.51.0
More information about the Binutils
mailing list