From 54ae5ffe5d0daad9f2979390f8ca7286fed50bcc Mon Sep 17 00:00:00 2001 From: Jiawei Date: Fri, 21 Apr 2023 15:57:22 +0800 Subject: [PATCH] RISC-V: Supports Zcmp push/pop/popret[z] instructions. --- bfd/elfxx-riscv.c | 20 ++++ bfd/elfxx-riscv.h | 3 + gas/config/tc-riscv.c | 192 +++++++++++++++++++++++++++++++++++++ include/opcode/riscv-opc.h | 14 +++ include/opcode/riscv.h | 21 ++++ opcodes/riscv-dis.c | 63 ++++++++++++ opcodes/riscv-opc.c | 6 ++ 7 files changed, 319 insertions(+) diff --git a/bfd/elfxx-riscv.c b/bfd/elfxx-riscv.c index e341de539ac..07caa27febe 100644 --- a/bfd/elfxx-riscv.c +++ b/bfd/elfxx-riscv.c @@ -1361,6 +1361,8 @@ static struct riscv_supported_ext riscv_supported_std_z_ext[] = {"zcb", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 }, {"zcf", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 }, {"zcd", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 }, + {"zcmp", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 }, + {"zcmt", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 }, {NULL, 0, 0, 0, 0} }; @@ -2591,6 +2593,8 @@ riscv_multi_subset_supports (riscv_parse_subset_t *rps, case INSN_CLASS_ZCB_AND_ZMMUL: return (riscv_subset_supports (rps, "zcb") && riscv_subset_supports (rps, "zmmul")); + case INSN_CLASS_ZCMP: + return riscv_subset_supports (rps, "zcmp"); case INSN_CLASS_SVINVAL: return riscv_subset_supports (rps, "svinval"); case INSN_CLASS_H: @@ -2847,6 +2851,8 @@ riscv_multi_subset_supports_ext (riscv_parse_subset_t *rps, return _("zcb' and `zbb"); case INSN_CLASS_ZCB_AND_ZMMUL: return _("zcb' and `zmmul', or `zcb' and `m"); + case INSN_CLASS_ZCMP: + return "zcmp"; case INSN_CLASS_SVINVAL: return "svinval"; case INSN_CLASS_H: @@ -2897,3 +2903,17 @@ riscv_multi_subset_supports_ext (riscv_parse_subset_t *rps, return NULL; } } + +/* get base sp adjustment */ + +int +riscv_get_base_spimm (insn_t opcode, riscv_parse_subset_t *rps) +{ + unsigned sp_alignment = 16; + unsigned reg_size = *(rps->xlen) / 8; + unsigned rlist = EXTRACT_BITS (opcode, OP_MASK_RLIST, OP_SH_RLIST); + + unsigned min_sp_adj = (rlist - 3) * reg_size + (rlist == 15 ? reg_size : 0); + return ((min_sp_adj / sp_alignment) + (min_sp_adj % sp_alignment != 0)) + * sp_alignment; +} diff --git a/bfd/elfxx-riscv.h b/bfd/elfxx-riscv.h index abcb409bd78..37c85217eb4 100644 --- a/bfd/elfxx-riscv.h +++ b/bfd/elfxx-riscv.h @@ -123,3 +123,6 @@ extern void bfd_elf32_riscv_set_data_segment_info (struct bfd_link_info *, int *); extern void bfd_elf64_riscv_set_data_segment_info (struct bfd_link_info *, int *); + +extern int +riscv_get_base_spimm (insn_t, riscv_parse_subset_t *); diff --git a/gas/config/tc-riscv.c b/gas/config/tc-riscv.c index ff22efeb063..195214f2a4d 100644 --- a/gas/config/tc-riscv.c +++ b/gas/config/tc-riscv.c @@ -1238,6 +1238,152 @@ arg_lookup (char **s, const char *const *array, size_t size, unsigned *regnop) return false; } +/* Map ra and s-register to [4,15], so that we can check if the + reg2 in register list reg1-reg2 or single reg2 is valid or not, + and obtain the corresponding rlist value. + + ra - 4 + s0 - 5 + s1 - 6 + .... + s10 - 0 (invalid) + s11 - 15 +*/ + +static int +regno_to_rlist (unsigned regno) +{ + if (regno == X_RA) + return 4; + else if (regno == X_S0 || regno == X_S1) + return 5 + regno - X_S0; + else if (regno >= X_S2 && regno < X_S10) + return 7 + regno - X_S2; + else if (regno == X_S11) + return 15; + + return 0; /* invalid symbol */ +} + +/* Parse register list, and the parsed rlist value is stored in rlist + argument. + + If ABI register names are used (e.g. ra and s0), the register + list could be "{ra}", "{ra, s0}", "{ra, s0-sN}", where 0 < N < 10 or + N == 11. + + If numeric register names are used (e.g. x1 and x8), the register list + could be "{x1}", "{x1,x8}", "{x1,x8-x9}", "{x1,x8-x9, x18}" and + "{x1,x8-x9,x18-xN}", where 19 < N < 25 or N == 27. + + It will fail if numeric register names and ABI register names are used + at the same time. + */ + +static bool +reglist_lookup (char **s, unsigned *rlist) +{ + unsigned regno; + /* Use to check if the register format is xreg. */ + bool use_xreg = **s == 'x'; + + /* The first register in register list should be ra. */ + if (!reg_lookup (s, RCLASS_GPR, ®no) + || !(*rlist = regno_to_rlist (regno)) /* update rlist */ + || regno != X_RA) + return FALSE; + + /* Skip "whitespace, whitespace" pattern. */ + while (ISSPACE (**s)) + ++ *s; + if (**s == '}') + return TRUE; + else if (**s != ',') + return FALSE; + while (ISSPACE (*++*s)) + ++ *s; + + /* Do not use numeric and abi names at the same time. */ + if (use_xreg && **s != 'x') + return FALSE; + + /* Reg1 should be s0 or its numeric names x8. */ + if (!reg_lookup (s, RCLASS_GPR, ®no) + || !(*rlist = regno_to_rlist (regno)) + || regno != X_S0) + return FALSE; + + /* Skip "whitespace - whitespace" pattern. */ + while (ISSPACE (**s)) + ++ *s; + if (**s == '}') + return TRUE; + else if (**s != '-') + return FALSE; + while (ISSPACE (*++*s)) + ++ *s; + + if (use_xreg && **s != 'x') + return FALSE; + + /* Reg2 is x9 if the numeric name is used, otherwise, + it could be any other sN register, where N > 0. */ + if (!reg_lookup (s, RCLASS_GPR, ®no) + || !(*rlist = regno_to_rlist (regno)) + || regno <= X_S0 + || (use_xreg && regno != X_S1)) + return FALSE; + + /* Skip whitespace */ + while (ISSPACE (**s)) + ++ *s; + + /* Check if it is the end of register list. */ + if (**s == '}') + return TRUE; + else if (!use_xreg) + return FALSE; + + /* Here is not reachable if the abi name is used. */ + gas_assert (use_xreg); + + /* If the numeric name is used, we need to parse extra + register list, reg3 or reg3-reg4. */ + + /* Skip ", white space" pattern. */ + if (**s != ',') + return FALSE; + while (ISSPACE (*++*s)) + ++ *s; + + if (use_xreg && **s != 'x') + return FALSE; + + /* Reg3 should be s2. */ + if (!reg_lookup (s, RCLASS_GPR, ®no) + || !(*rlist = regno_to_rlist (regno)) + || regno != X_S2) + return FALSE; + + /* skip "whitespace - whitespace" pattern. */ + while (ISSPACE (**s)) + ++ *s; + if (**s == '}') + return TRUE; + else if (**s != '-') + return FALSE; + while (ISSPACE (*++*s)) + ++ *s; + + /* Reg4 could be any other sN register, where N > 1. */ + if (!reg_lookup (s, RCLASS_GPR, ®no) + || !(*rlist = regno_to_rlist (regno)) + || regno <= X_S2) + return FALSE; + + return TRUE; +} + static bool flt_lookup (float f, const float *array, size_t size, unsigned *regnop) { @@ -1335,6 +1481,17 @@ validate_riscv_insn (const struct riscv_opcode *opc, int length) goto unknown_validate_operand; } break; + case 'Z': /* Zcmp extension operators. */ + switch (*++oparg) + { + /* 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. */ + case 'r': USE_BITS (OP_MASK_RLIST, OP_SH_RLIST); break; + default: + goto unknown_validate_operand; + } + break; default: goto unknown_validate_operand; } @@ -1369,6 +1526,8 @@ validate_riscv_insn (const struct riscv_opcode *opc, int length) case ',': break; case '(': break; case ')': break; + case '{': break; + case '}': break; case '!': break; case '<': USE_BITS (OP_MASK_SHAMTW, OP_SH_SHAMTW); break; case '>': USE_BITS (OP_MASK_SHAMT, OP_SH_SHAMT); break; @@ -2994,6 +3153,37 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr, } break; + case 'Z': /* Zcmp extension. */ + switch (*++oparg) + { + case 'r': + /* we use regno to store reglist value here. */ + if (!reglist_lookup (&asarg, ®no)) + break; + INSERT_OPERAND (RLIST, *ip, regno); + continue; + + case 'p': + if (my_getSmallExpression (imm_expr, imm_reloc, asarg, p) + || imm_expr->X_op != O_constant) + break; + /* convert stack adjust of cm.push to a positive offset. */ + if (ip->insn_mo->match == MATCH_CM_PUSH) + imm_expr->X_add_number *= -1; + /* subtract base stack adjust. */ + imm_expr->X_add_number -= + riscv_get_base_spimm (ip->insn_opcode, &riscv_rps_as); + if (!VALID_ZCMP_SPIMM (imm_expr->X_add_number)) + break; + ip->insn_opcode |= + ENCODE_ZCMP_SPIMM (imm_expr->X_add_number); + goto rvc_imm_done; + + default: + goto unknown_riscv_ip_operand; + } + break; + default: goto unknown_riscv_ip_operand; } @@ -3185,6 +3375,8 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr, case '(': case ')': + case '{': + case '}': case '[': case ']': case '!': diff --git a/include/opcode/riscv-opc.h b/include/opcode/riscv-opc.h index 2f033fadcb8..39031d763c5 100644 --- a/include/opcode/riscv-opc.h +++ b/include/opcode/riscv-opc.h @@ -2327,6 +2327,15 @@ #define MASK_WRS_NTO 0xffffffff #define MATCH_WRS_STO 0x01d00073 #define MASK_WRS_STO 0xffffffff +/* ZCMP instructions. */ +#define MATCH_CM_PUSH 0xb802 +#define MASK_CM_PUSH 0xff03 +#define MATCH_CM_POP 0xba02 +#define MASK_CM_POP 0xff03 +#define MATCH_CM_POPRET 0xbe02 +#define MASK_CM_POPRET 0xff03 +#define MATCH_CM_POPRETZ 0xbc02 +#define MASK_CM_POPRETZ 0xff03 /* Vendor-specific (CORE-V) Xcvmac instructions. */ #define MATCH_CV_MAC 0x9000302b #define MASK_CV_MAC 0xfe00707f @@ -4078,6 +4087,11 @@ DECLARE_INSN(c_lhu, MATCH_C_LHU, MASK_C_LHU) DECLARE_INSN(c_lh, MATCH_C_LH, MASK_C_LH) DECLARE_INSN(c_sb, MATCH_C_SB, MASK_C_SB) DECLARE_INSN(c_sh, MATCH_C_SH, MASK_C_SH) +/* Zcmp instructions. */ +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) /* 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 cfdf576fa3a..52ff2df104c 100644 --- a/include/opcode/riscv.h +++ b/include/opcode/riscv.h @@ -135,6 +135,10 @@ static inline unsigned int riscv_insn_length (insn_t insn) #define EXTRACT_CV_SIMD_UIMM6(x) \ ((RV_X(x, 25, 1)) | (RV_X(x, 20, 5) << 1)) +/* Zcmp extension. */ +#define EXTRACT_ZCMP_SPIMM(x) \ + (RV_X(x, 2, 2) << 4) + #define ENCODE_ITYPE_IMM(x) \ (RV_X(x, 0, 12) << 20) #define ENCODE_STYPE_IMM(x) \ @@ -205,6 +209,10 @@ static inline unsigned int riscv_insn_length (insn_t insn) #define ENCODE_CV_SIMD_UIMM6(x) \ ((RV_X(x, 0, 1) << 25) | (RV_X(x, 1, 5) << 20)) +/* Zcmp extenison. */ +#define ENCODE_ZCMP_SPIMM(x) \ + (RV_X(x, 4, 2) << 2) + #define VALID_ITYPE_IMM(x) (EXTRACT_ITYPE_IMM(ENCODE_ITYPE_IMM(x)) == (x)) #define VALID_STYPE_IMM(x) (EXTRACT_STYPE_IMM(ENCODE_STYPE_IMM(x)) == (x)) #define VALID_BTYPE_IMM(x) (EXTRACT_BTYPE_IMM(ENCODE_BTYPE_IMM(x)) == (x)) @@ -232,6 +240,9 @@ static inline unsigned int riscv_insn_length (insn_t insn) #define VALID_ZCB_BYTE_UIMM(x) (EXTRACT_ZCB_BYTE_UIMM(ENCODE_ZCB_BYTE_UIMM(x)) == (x)) #define VALID_ZCB_HALFWORD_UIMM(x) (EXTRACT_ZCB_HALFWORD_UIMM(ENCODE_ZCB_HALFWORD_UIMM(x)) == (x)) +/* Zcmp extension. */ +#define VALID_ZCMP_SPIMM(x) (EXTRACT_ZCMP_SPIMM(ENCODE_ZCMP_SPIMM(x)) == (x)) + #define RISCV_RTYPE(insn, rd, rs1, rs2) \ ((MATCH_ ## insn) | ((rd) << OP_SH_RD) | ((rs1) << OP_SH_RS1) | ((rs2) << OP_SH_RS2)) #define RISCV_ITYPE(insn, rd, rs1, imm) \ @@ -304,6 +315,10 @@ static inline unsigned int riscv_insn_length (insn_t insn) #define OP_MASK_LN 0x1 #define OP_SH_LN 7 +/* ZC Specific */ +#define OP_MASK_RLIST 0xf +#define OP_SH_RLIST 4 + #define OP_MASK_CSR 0xfffU #define OP_SH_CSR 20 @@ -379,6 +394,11 @@ static inline unsigned int riscv_insn_length (insn_t insn) #define X_T0 5 #define X_T1 6 #define X_T2 7 +#define X_S0 8 +#define X_S1 9 +#define X_S2 18 +#define X_S10 26 +#define X_S11 27 #define X_T3 28 #define NGPR 32 @@ -491,6 +511,7 @@ enum riscv_insn_class INSN_CLASS_ZICBOM, INSN_CLASS_ZICBOP, INSN_CLASS_ZICBOZ, + INSN_CLASS_ZCMP, INSN_CLASS_H, INSN_CLASS_XCVMAC, INSN_CLASS_XCVALU, diff --git a/opcodes/riscv-dis.c b/opcodes/riscv-dis.c index b137549b43c..865c9eed72d 100644 --- a/opcodes/riscv-dis.c +++ b/opcodes/riscv-dis.c @@ -82,6 +82,8 @@ static const char (*riscv_fpr_names)[NRC]; /* If set, disassemble as most general instruction. */ static bool no_aliases = false; +/* If set, disassemble numeric register names instead of ABI names. */ +static int numeric; /* Set default RISC-V disassembler options. */ @@ -91,6 +93,7 @@ set_default_riscv_dis_options (void) riscv_gpr_names = riscv_gpr_names_abi; riscv_fpr_names = riscv_fpr_names_abi; no_aliases = false; + numeric = 0; } /* Parse RISC-V disassembler option (without arguments). */ @@ -104,6 +107,7 @@ parse_riscv_dis_option_without_args (const char *option) { riscv_gpr_names = riscv_gpr_names_numeric; riscv_fpr_names = riscv_fpr_names_numeric; + numeric = 1; } else return false; @@ -222,6 +226,50 @@ maybe_print_address (struct riscv_private_data *pd, int base_reg, int offset, pd->print_addr = (bfd_vma)(uint32_t)pd->print_addr; } +/* Get ZCMP rlist field. */ + +static void +print_rlist (disassemble_info *info, insn_t l) +{ + unsigned rlist = (int)EXTRACT_OPERAND (RLIST, l); + unsigned r_start = numeric ? X_S2 : X_S0; + info->fprintf_func (info->stream, "%s", riscv_gpr_names[X_RA]); + + if (rlist == 5) + info->fprintf_func (info->stream, ",%s", riscv_gpr_names[X_S0]); + else if (rlist == 6 || (numeric && rlist > 6)) + info->fprintf_func (info->stream, ",%s-%s", + riscv_gpr_names[X_S0], + riscv_gpr_names[X_S1]); + + if (rlist == 15) + info->fprintf_func (info->stream, ",%s-%s", + riscv_gpr_names[r_start], + riscv_gpr_names[X_S11]); + else if (rlist == 7 && numeric) + info->fprintf_func (info->stream, ",%s", + riscv_gpr_names[X_S2]); + else if (rlist > 6) + info->fprintf_func (info->stream, ",%s-%s", + riscv_gpr_names[r_start], + riscv_gpr_names[rlist + 11]); +} + +/* Get ZCMP sp adjustment immediate. */ + +static int +riscv_get_spimm (insn_t l) +{ + int spimm = riscv_get_base_spimm(l, &riscv_rps_dis); + + spimm += EXTRACT_ZCMP_SPIMM (l); + + if (((l ^ MATCH_CM_PUSH) & MASK_CM_PUSH) == 0) + spimm *= -1; + + return spimm; +} + /* Print insn arguments for 32/64-bit code. */ static void @@ -336,6 +384,19 @@ print_insn_args (const char *oparg, insn_t l, bfd_vma pc, disassemble_info *info print (info->stream, dis_style_register, "%s", riscv_fpr_names[EXTRACT_OPERAND (CRS2S, l) + 8]); break; + case 'Z': /* Zcmp extension 16 bits length instruction fields. */ + switch (*++oparg) + { + case 'r': + print_rlist (info, l); + break; + case 'p': + print (info->stream, dis_style_immediate, "%d", + riscv_get_spimm (l)); + break; + default: break; + } + break; } break; @@ -428,6 +489,8 @@ print_insn_args (const char *oparg, insn_t l, bfd_vma pc, disassemble_info *info case ')': case '[': case ']': + case '{': + case '}': print (info->stream, dis_style_text, "%c", *oparg); break; diff --git a/opcodes/riscv-opc.c b/opcodes/riscv-opc.c index 83468be0d2a..df554974ade 100644 --- a/opcodes/riscv-opc.c +++ b/opcodes/riscv-opc.c @@ -2526,6 +2526,12 @@ const struct riscv_opcode riscv_opcodes[] = /* END OF CORE-V */ +/* Zcmp instructions. */ +{"cm.push", 0, INSN_CLASS_ZCMP, "{CZr},CZp", MATCH_CM_PUSH, MASK_CM_PUSH, match_opcode, 0 }, +{"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 }, + /* Terminate the list. */ {0, 0, INSN_CLASS_NONE, 0, 0, 0, 0, 0} };