From d0680f912bf42802e62201ca665c883fbdacc2d9 Mon Sep 17 00:00:00 2001 From: Jiawei Date: Fri, 21 Apr 2023 15:57:24 +0800 Subject: [PATCH] RISC-V: Supports Zcmp 'cm.mv' instructions. --- gas/config/tc-riscv.c | 334 ++++++++++++++++++++++++++++++++++++- gas/config/tc-riscv.h | 6 + include/opcode/riscv-opc.h | 6 + include/opcode/riscv.h | 11 ++ opcodes/riscv-dis.c | 19 +++ opcodes/riscv-opc.c | 9 +- 6 files changed, 380 insertions(+), 5 deletions(-) diff --git a/gas/config/tc-riscv.c b/gas/config/tc-riscv.c index 195214f2a4d..9f65048d778 100644 --- a/gas/config/tc-riscv.c +++ b/gas/config/tc-riscv.c @@ -157,6 +157,45 @@ struct riscv_ip_error #define DEFAULT_RISCV_PRIV_SPEC "1.11" #endif +/* Instruction pair combiner check function. */ +typedef bfd_boolean (*riscv_combine_check) (const struct riscv_cl_insn*, + const expressionS*, const bfd_reloc_code_real_type); + +typedef bfd_boolean (*riscv_combine_avail) (void); + +/* A matcher defines a rule to combine an instruction pair. */ +struct riscv_combiner_matcher +{ + /* Func to check the first/second insn in insn pair. */ + riscv_combine_check check_1; + riscv_combine_check check_2; + + /* Func to write the combined insn after check_2 passes. + The combined insn is written to the cached field in + riscv_combiner. */ + riscv_combine_check update; + + /* Rturn TRUE if this matcher is available. */ + riscv_combine_avail avail; +}; + +struct riscv_combiner +{ + /* Matcher list */ + struct riscv_combiner_matcher *matcher; + + /* idx is 0 if no insn is cached. If idx is not 0, + then an insn is cached through the matcher(check_1) + at the index idx-1 in matcher list. */ + int idx; + + /* Holding a cached insn information, + insn symbol, info and reloc type */ + expressionS imm_expr; + struct riscv_cl_insn insn; + bfd_reloc_code_real_type imm_reloc; +}; + static const char default_arch[] = DEFAULT_ARCH; static const char *default_arch_with_ext = DEFAULT_RISCV_ARCH_WITH_EXT; static enum riscv_spec_class default_isa_spec = ISA_SPEC_CLASS_NONE; @@ -459,6 +498,9 @@ static bool explicit_priv_attr = false; static char *expr_parse_end; +/* Instruction pair combiner */ +static struct riscv_combiner *insn_combiner; + /* Macros for encoding relaxation state for RVC branches and far jumps. */ #define RELAX_BRANCH_ENCODE(uncond, rvc, length) \ ((relax_substateT) \ @@ -1484,6 +1526,9 @@ validate_riscv_insn (const struct riscv_opcode *opc, int length) case 'Z': /* Zcmp extension operators. */ switch (*++oparg) { + /* sreg operators in cm.mvsa01 and cm.mva01s. */ + case '1': USE_BITS (OP_MASK_SREG1, OP_SH_SREG1); break; + case '2': USE_BITS (OP_MASK_SREG2, OP_SH_SREG2); break; /* immediate offset operand for cm.push and cm.pop. */ case 'p': used_bits |= ENCODE_ZCMP_SPIMM (-1U); break; /* register list operand for cm.push and cm.pop. */ @@ -1903,6 +1948,205 @@ append_insn (struct riscv_cl_insn *ip, expressionS *address_expr, } } +/* Return TRUE if instruction combiner is available. */ + +static bfd_boolean +use_insn_combiner (void) +{ + return riscv_subset_supports (&riscv_rps_as, "zcmp"); +} + +/* Return TRUE if the insn is valid for the first insn in + instruction pair. */ + +static bfd_boolean +zcmp_mva01s_1 (const struct riscv_cl_insn *insn, + const expressionS *imm_expr ATTRIBUTE_UNUSED, + const bfd_reloc_code_real_type reloc_type ATTRIBUTE_UNUSED) +{ + int rd, rs2; + + /* mv is replaced by c.mv in C ext */ + if (insn->insn_mo->match != MATCH_C_MV) + return FALSE; + + rd = EXTRACT_OPERAND (RD, insn->insn_opcode); + rs2 = EXTRACT_OPERAND (CRS2, insn->insn_opcode); + + return (rd == X_A0 || rd == X_A1) + && RISCV_SREG_0_7 (rs2); +} + +static bfd_boolean +zcmp_mvsa01_1 (const struct riscv_cl_insn *insn, + const expressionS *imm_expr ATTRIBUTE_UNUSED, + const bfd_reloc_code_real_type reloc_type ATTRIBUTE_UNUSED) +{ + int rd, rs2; + + /* mv is replaced by c.mv in C ext */ + if (insn->insn_mo->match != MATCH_C_MV) + return FALSE; + + rd = EXTRACT_OPERAND (RD, insn->insn_opcode); + rs2 = EXTRACT_OPERAND (CRS2, insn->insn_opcode); + + return (rs2 == X_A0 || rs2 == X_A1) + && RISCV_SREG_0_7 (rd); +} + +/* Return TRUE if the insn is valid for the second insn in + instruction pair. */ + +static bfd_boolean +zcmp_mva01s_2 (const struct riscv_cl_insn *insn, + const expressionS *imm_expr ATTRIBUTE_UNUSED, + const bfd_reloc_code_real_type reloc_type ATTRIBUTE_UNUSED) +{ + if (insn->insn_mo->match != MATCH_C_MV) + return FALSE; + + int rd = EXTRACT_OPERAND (RD, insn->insn_opcode); + int rs2 = EXTRACT_OPERAND (CRS2, insn->insn_opcode); + int rd_cache = EXTRACT_OPERAND (RD, insn_combiner->insn.insn_opcode); + + /* First check if rd does not equal the rd of cached c.mv insn, + and if rd is a0 or a1. */ + if ((rd == rd_cache) + || (rd != X_A0 && rd != X_A1)) + return FALSE; + + /* Then we check if rs is s0-s7. */ + return RISCV_SREG_0_7 (rs2); +} + +static bfd_boolean +zcmp_mvsa01_2 (const struct riscv_cl_insn *insn, + const expressionS *imm_expr ATTRIBUTE_UNUSED, + const bfd_reloc_code_real_type reloc_type ATTRIBUTE_UNUSED) +{ + if (insn->insn_mo->match != MATCH_C_MV) + return FALSE; + + int rd = EXTRACT_OPERAND (RD, insn->insn_opcode); + int rs2 = EXTRACT_OPERAND (CRS2, insn->insn_opcode); + int rd_cache = EXTRACT_OPERAND (RD, insn_combiner->insn.insn_opcode); + int rs2_cache = EXTRACT_OPERAND (CRS2, insn_combiner->insn.insn_opcode); + + /* First check if rs does not equal the rd of cached c.mv insn, + and if rs is a0 or a1. */ + if ((rs2 == rs2_cache) + || (rs2 != X_A0 && rs2 != X_A1)) + return FALSE; + + /* Then we check if rd is s0-s7 and does not equal the + rd of cached c.mv insn. */ + return (rd != rd_cache) + && RISCV_SREG_0_7 (rd); +} + +/* Write combined insn to the cached field in insn_combiner to append + later. */ + +static bfd_boolean +zcmp_mva01s_update (const struct riscv_cl_insn *insn, + const expressionS *imm_expr ATTRIBUTE_UNUSED, + const bfd_reloc_code_real_type reloc_type ATTRIBUTE_UNUSED) +{ + unsigned sreg1, sreg2; + struct riscv_cl_insn *cached_insn = &insn_combiner->insn; + + unsigned rd = EXTRACT_OPERAND (RD, insn->insn_opcode); + unsigned rs2 = EXTRACT_OPERAND (CRS2, insn->insn_opcode); + unsigned rs2_cache = EXTRACT_OPERAND (CRS2, cached_insn->insn_opcode); + + cached_insn->insn_opcode = MATCH_CM_MVA01S; + + sreg1 = (rd == X_A0 ? rs2 : rs2_cache) % 8; + sreg2 = (rd == X_A0 ? rs2_cache : rs2) % 8; + + INSERT_OPERAND (SREG1, *cached_insn, sreg1); + INSERT_OPERAND (SREG2, *cached_insn, sreg2); + + return TRUE; +} + +static bfd_boolean +zcmp_mvsa01_update (const struct riscv_cl_insn *insn, + const expressionS *imm_expr ATTRIBUTE_UNUSED, + const bfd_reloc_code_real_type reloc_type ATTRIBUTE_UNUSED) +{ + unsigned sreg1, sreg2; + struct riscv_cl_insn *cached_insn = &insn_combiner->insn; + + unsigned rd = EXTRACT_OPERAND (RD, insn->insn_opcode); + unsigned rd_cache = EXTRACT_OPERAND (RD, cached_insn->insn_opcode); + unsigned rs2 = EXTRACT_OPERAND (CRS2, insn->insn_opcode); + + cached_insn->insn_opcode = MATCH_CM_MVSA01; + + sreg1 = (rs2 == X_A0 ? rd : rd_cache) % 8; + sreg2 = (rs2 == X_A0 ? rd_cache : rd) % 8; + + INSERT_OPERAND (SREG1, *cached_insn, sreg1); + INSERT_OPERAND (SREG2, *cached_insn, sreg2); + return TRUE; +} + +/* Instruction pair matching table. */ + +static struct riscv_combiner_matcher riscv_comb_matchers [] = { + { zcmp_mva01s_1, zcmp_mva01s_2, zcmp_mva01s_update, use_insn_combiner }, + { zcmp_mvsa01_1, zcmp_mvsa01_2, zcmp_mvsa01_update, use_insn_combiner }, + { NULL, NULL, NULL, NULL }, +}; + +/* Cache an instruction when it passes check function */ + +static void +cache_an_insn (struct riscv_cl_insn *insn, + expressionS *imm_expr, + bfd_reloc_code_real_type reloc_type) +{ + memcpy((void*)&(insn_combiner->imm_expr), + (void*)imm_expr, sizeof(expressionS)); + memcpy((void*)&(insn_combiner->insn), (void*)insn, + sizeof(struct riscv_cl_insn)); + insn_combiner->imm_reloc = reloc_type; +} + +/* Initialize instruction pair combiner */ + +static void +init_insn_combiner (void) +{ + insn_combiner = (struct riscv_combiner *) + xmalloc (sizeof (struct riscv_combiner)); + insn_combiner->idx = 0; + insn_combiner->matcher = riscv_comb_matchers; +} + +/* Return TRUE if combiner has cached one instruction. */ + +static bfd_boolean +has_cached_insn (void) +{ + return use_insn_combiner () + && insn_combiner + && insn_combiner->idx > 0; +} + +/* Append a cached instruction. */ + +static void +release_cached_insn (void) +{ + append_insn (&insn_combiner->insn, + &insn_combiner->imm_expr, + insn_combiner->imm_reloc); + insn_combiner->idx = 0; +} + /* Build an instruction created by a macro expansion. This is passed a pointer to the count of instructions created so far, an expression, the name of the instruction to build, an operand format string, and @@ -3179,6 +3423,19 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr, ENCODE_ZCMP_SPIMM (imm_expr->X_add_number); goto rvc_imm_done; + case '1': + if (!reg_lookup (&asarg, RCLASS_GPR, ®no) + || !RISCV_SREG_0_7 (regno)) + break; + INSERT_OPERAND (SREG1, *ip, regno % 8); + continue; + + case '2': + if (!reg_lookup (&asarg, RCLASS_GPR, ®no) + || !RISCV_SREG_0_7 (regno)) + break; + INSERT_OPERAND (SREG2, *ip, regno % 8); + continue; default: goto unknown_riscv_ip_operand; } @@ -4206,6 +4463,57 @@ riscv_ip_hardcode (char *str, return NULL; } +static +void riscv_append_insn (struct riscv_cl_insn *insn, expressionS *imm_expr, + bfd_reloc_code_real_type imm_reloc) +{ + if (insn->insn_mo->pinfo == INSN_MACRO) + { + if (has_cached_insn ()) + release_cached_insn (); + macro (insn, imm_expr, &imm_reloc); + return; + } + + if (use_insn_combiner ()) + { + struct riscv_combiner_matcher *matchers = insn_combiner->matcher; + unsigned idx; + + /* if one insn is cached, we now check the second insn */ + if (insn_combiner->idx) + { + idx = insn_combiner->idx - 1; + + /* if successfully match a insn pair, we output the merged result */ + if (matchers[idx].check_2 (insn, imm_expr, imm_reloc)) + { + matchers[idx].update (insn, imm_expr, imm_reloc); + release_cached_insn (); + return; + } + + release_cached_insn (); + } + + gas_assert (insn_combiner->idx == 0); + + for (idx = 0; matchers[idx].check_1 != NULL; idx++) + { + if (!matchers[idx].avail()) + continue; + if (matchers[idx].check_1 (insn, imm_expr, imm_reloc)) + { + cache_an_insn (insn, imm_expr, imm_reloc); + insn_combiner->idx = idx + 1; + return; + } + } + } + + append_insn (insn, imm_expr, imm_reloc); +} + void md_assemble (char *str) { @@ -4218,6 +4526,10 @@ md_assemble (char *str) if (!start_assemble) { start_assemble = true; + /* Initialize instruction pair combiner for cm.mva01s + and cm.mvsa01*/ + if (use_insn_combiner ()) + init_insn_combiner (); riscv_set_abi_by_arch (); if (!riscv_set_default_priv_spec (NULL)) @@ -4239,10 +4551,7 @@ md_assemble (char *str) return; } - if (insn.insn_mo->pinfo == INSN_MACRO) - macro (&insn, &imm_expr, &imm_reloc); - else - append_insn (&insn, &imm_expr, imm_reloc); + riscv_append_insn (&insn, &imm_expr, imm_reloc); } const char * @@ -5479,11 +5788,28 @@ riscv_insert_uleb128_fixes (bfd *abfd ATTRIBUTE_UNUSED, } } +/* Implement TC_START_LABEL and md_cleanup. Release cache instruction + when assemble finished parsing input file or defining a label */ + +bfd_boolean +riscv_md_cleanup (void) +{ + if (has_cached_insn ()) + release_cached_insn (); + + return TRUE; +} + /* Called after all assembly has been done. */ void riscv_md_finish (void) { + if (use_insn_combiner () + && insn_combiner) + { + free (insn_combiner); + } riscv_set_public_attributes (); if (riscv_opts.relax) bfd_map_over_sections (stdoutput, riscv_insert_uleb128_fixes, NULL); diff --git a/gas/config/tc-riscv.h b/gas/config/tc-riscv.h index 0c70c7d4739..9a02097dbcd 100644 --- a/gas/config/tc-riscv.h +++ b/gas/config/tc-riscv.h @@ -133,6 +133,12 @@ bool riscv_parse_name (const char *, struct expressionS *, enum expr_mode); extern void riscv_md_finish (void); extern int riscv_convert_symbolic_attribute (const char *); +#define md_cleanup riscv_md_cleanup +#define TC_START_LABEL(STR, NUL_CHAR, NEXT_CHAR) \ + (NEXT_CHAR == ':' && riscv_md_cleanup ()) + +extern bfd_boolean riscv_md_cleanup (void); + /* Set mapping symbol states. */ #define md_cons_align(nbytes) riscv_mapping_state (MAP_DATA, 0, 0) void riscv_mapping_state (enum riscv_seg_mstate, int, bool); diff --git a/include/opcode/riscv-opc.h b/include/opcode/riscv-opc.h index 39031d763c5..f170ec3afbd 100644 --- a/include/opcode/riscv-opc.h +++ b/include/opcode/riscv-opc.h @@ -2336,6 +2336,10 @@ #define MASK_CM_POPRET 0xff03 #define MATCH_CM_POPRETZ 0xbc02 #define MASK_CM_POPRETZ 0xff03 +#define MATCH_CM_MVA01S 0xac62 +#define MASK_CM_MVA01S 0xfc63 +#define MATCH_CM_MVSA01 0xac22 +#define MASK_CM_MVSA01 0xfc63 /* Vendor-specific (CORE-V) Xcvmac instructions. */ #define MATCH_CV_MAC 0x9000302b #define MASK_CV_MAC 0xfe00707f @@ -4092,6 +4096,8 @@ DECLARE_INSN(cm_push, MATCH_CM_PUSH, MASK_CM_PUSH) DECLARE_INSN(cm_pop, MATCH_CM_POP, MASK_CM_POP) DECLARE_INSN(cm_popret, MATCH_CM_POPRET, MASK_CM_POPRET) DECLARE_INSN(cm_popretz, MATCH_CM_POPRETZ, MASK_CM_POPRETZ) +DECLARE_INSN(cm_mvsa01, MATCH_CM_MVSA01, MASK_CM_MVSA01) +DECLARE_INSN(cm_mva01s, MATCH_CM_MVA01S, MASK_CM_MVA01S) /* Vendor-specific (T-Head) XTheadBa instructions. */ DECLARE_INSN(th_addsl, MATCH_TH_ADDSL, MASK_TH_ADDSL) /* Vendor-specific (T-Head) XTheadBb instructions. */ diff --git a/include/opcode/riscv.h b/include/opcode/riscv.h index 52ff2df104c..d17ea364f12 100644 --- a/include/opcode/riscv.h +++ b/include/opcode/riscv.h @@ -318,6 +318,10 @@ static inline unsigned int riscv_insn_length (insn_t insn) /* ZC Specific */ #define OP_MASK_RLIST 0xf #define OP_SH_RLIST 4 +#define OP_MASK_SREG1 0x7 +#define OP_SH_SREG1 7 +#define OP_MASK_SREG2 0x7 +#define OP_SH_SREG2 2 #define OP_MASK_CSR 0xfffU #define OP_SH_CSR 20 @@ -396,7 +400,10 @@ static inline unsigned int riscv_insn_length (insn_t insn) #define X_T2 7 #define X_S0 8 #define X_S1 9 +#define X_A0 10 +#define X_A1 11 #define X_S2 18 +#define X_S7 23 #define X_S10 26 #define X_S11 27 #define X_T3 28 @@ -444,6 +451,10 @@ static inline unsigned int riscv_insn_length (insn_t insn) /* The maximal number of subset can be required. */ #define MAX_SUBSET_NUM 4 +#define RISCV_SREG_0_7(REGNO) \ + ((REGNO == X_S0 || REGNO == X_S1) \ + || (REGNO >= X_S2 && REGNO <= X_S7)) + /* All RISC-V instructions belong to at least one of these classes. */ enum riscv_insn_class { diff --git a/opcodes/riscv-dis.c b/opcodes/riscv-dis.c index 865c9eed72d..705aa8b6793 100644 --- a/opcodes/riscv-dis.c +++ b/opcodes/riscv-dis.c @@ -270,6 +270,17 @@ riscv_get_spimm (insn_t l) return spimm; } +/* Get s-register regno by using sreg number. + e.g. the regno of s0 is 8, so + riscv_get_sregno (0) equals 8. */ + +static unsigned +riscv_get_sregno (unsigned sreg_idx) +{ + return sreg_idx > 1 ? + sreg_idx + 16 : sreg_idx + 8; +} + /* Print insn arguments for 32/64-bit code. */ static void @@ -387,6 +398,14 @@ print_insn_args (const char *oparg, insn_t l, bfd_vma pc, disassemble_info *info case 'Z': /* Zcmp extension 16 bits length instruction fields. */ switch (*++oparg) { + case '1': + print (info->stream, dis_style_register, "%s", + riscv_gpr_names[riscv_get_sregno (EXTRACT_OPERAND (SREG1, l))]); + break; + case '2': + print (info->stream, dis_style_register, "%s", + riscv_gpr_names[riscv_get_sregno (EXTRACT_OPERAND (SREG2, l))]); + break; case 'r': print_rlist (info, l); break; diff --git a/opcodes/riscv-opc.c b/opcodes/riscv-opc.c index df554974ade..35414febe03 100644 --- a/opcodes/riscv-opc.c +++ b/opcodes/riscv-opc.c @@ -321,6 +321,12 @@ match_th_load_pair(const struct riscv_opcode *op, return rd1 != rd2 && rd1 != rs && rd2 != rs && match_opcode (op, insn); } +match_sreg1_not_eq_sreg2 (const struct riscv_opcode *op, insn_t insn) +{ + return match_opcode (op, insn) + && (EXTRACT_OPERAND (SREG1, insn) != EXTRACT_OPERAND (SREG2, insn)); +} + /* 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 @@ -2531,7 +2537,8 @@ const struct riscv_opcode riscv_opcodes[] = {"cm.pop", 0, INSN_CLASS_ZCMP, "{CZr},CZp", MATCH_CM_POP, MASK_CM_POP, match_opcode, 0 }, {"cm.popret", 0, INSN_CLASS_ZCMP, "{CZr},CZp", MATCH_CM_POPRET, MASK_CM_POPRET, match_opcode, 0 }, {"cm.popretz", 0, INSN_CLASS_ZCMP, "{CZr},CZp", MATCH_CM_POPRETZ, MASK_CM_POPRETZ, match_opcode, 0 }, - +{"cm.mva01s", 0, INSN_CLASS_ZCMP, "CZ1,CZ2", MATCH_CM_MVA01S, MASK_CM_MVA01S, match_opcode, 0 }, +{"cm.mvsa01", 0, INSN_CLASS_ZCMP, "CZ1,CZ2", MATCH_CM_MVSA01, MASK_CM_MVSA01, match_sreg1_not_eq_sreg2, 0 }, /* Terminate the list. */ {0, 0, INSN_CLASS_NONE, 0, 0, 0, 0, 0} };