aboutsummaryrefslogtreecommitdiff
path: root/gas/config/tc-riscv.c
diff options
context:
space:
mode:
Diffstat (limited to 'gas/config/tc-riscv.c')
-rw-r--r--gas/config/tc-riscv.c181
1 files changed, 181 insertions, 0 deletions
diff --git a/gas/config/tc-riscv.c b/gas/config/tc-riscv.c
index 94f0c67..1757ff6 100644
--- a/gas/config/tc-riscv.c
+++ b/gas/config/tc-riscv.c
@@ -1254,6 +1254,158 @@ flt_lookup (float f, const float *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 reg_list value.
+
+ ra - 4
+ s0 - 5
+ s1 - 6
+ ....
+ s10 - 0 (invalid)
+ s11 - 15. */
+
+static int
+regno_to_reg_list (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;
+
+ /* Invalid symbol. */
+ return 0;
+}
+
+/* Parse register list, and return the last register by regno_to_reg_list.
+
+ 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.
+
+ The numeric and ABI register names cannot be used at the same time.
+
+ TODO: Report errors for the following cases,
+ 1. Too many registers in the list.
+ 2. Cases which return 0.
+ 3. Illegal formats, for example, {x1,x8-NULL,x18-x24/x18}, {x1-x2,x8}. */
+
+static unsigned
+reglist_lookup_internal (char *reglist)
+{
+ unsigned regno = 0;
+ unsigned reg_list = 0;
+ char *regname[3][2] = {{NULL}};
+ char *save_tok, *save_subtok;
+ unsigned i, j;
+
+ char *token = strtok_r (reglist, ",", &save_tok);
+ for (i = 0; i < 3 && token != NULL;
+ token = strtok_r (NULL, ",", &save_tok), i++)
+ {
+ char *subtoken = strtok_r (token, "-", &save_subtok);
+ for (j = 0; j < 2 && subtoken != NULL;
+ subtoken = strtok_r (NULL, "-", &save_subtok), j++)
+ regname[i][j] = subtoken;
+ }
+
+ bool reg1_numeric = false;
+ for (i = 0; i < 3; i++)
+ {
+ if (regname[i][0] == NULL)
+ continue;
+#define REG_TO_REG_LIST(NAME, NUM, LIST) \
+ (reg_lookup (&NAME, RCLASS_GPR, &NUM) && (LIST = regno_to_reg_list (NUM)))
+#define REG_NUMERIC(NAME) (NAME[0] == 'x')
+#define REG_CONFLICT(NAME, REG_NUMERIC) \
+ ((NAME[0] == 'x' && !REG_NUMERIC) || (NAME[0] != 'x' && REG_NUMERIC))
+ switch (i)
+ {
+ case 0:
+ reg1_numeric = REG_NUMERIC (regname[i][0]);
+ if (!REG_TO_REG_LIST (regname[i][0], regno, reg_list)
+ || regno != X_RA)
+ return 0;
+ break;
+ case 1:
+ if (REG_CONFLICT (regname[i][0], reg1_numeric)
+ /* The second register should be s0 or its numeric names x8. */
+ || !REG_TO_REG_LIST (regname[i][0], regno, reg_list)
+ || regno != X_S0)
+ return 0;
+ else if (regname[i][1] == NULL)
+ return reg_list;
+
+ if (REG_CONFLICT (regname[i][1], reg1_numeric)
+ /* The third register is x9 if the numeric name is used.
+ Otherwise, it could be any other sN register, where N > 0. */
+ || !REG_TO_REG_LIST (regname[i][1], regno, reg_list)
+ || regno <= X_S0
+ || (reg1_numeric && regno != X_S1))
+ return 0;
+ break;
+ case 2:
+ /* Must use register numeric names. */
+ if (!reg1_numeric
+ || !REG_NUMERIC (regname[i][0])
+ /* The fourth register should be s2. */
+ || !REG_TO_REG_LIST (regname[i][0], regno, reg_list)
+ || regno != X_S2)
+ return 0;
+ else if (regname[i][1] == NULL)
+ return reg_list;
+
+ if (!reg1_numeric
+ || !REG_NUMERIC (regname[i][1])
+ /* The fifth register could be any other sN register, where N > 1. */
+ || !REG_TO_REG_LIST (regname[i][1], regno, reg_list)
+ || regno <= X_S2)
+ return 0;
+ break;
+ default:
+ return 0;
+ }
+#undef REG_TO_REG_LIST
+#undef REG_NUMERIC
+#undef REG_CONFLICT
+ }
+ return reg_list;
+}
+
+/* Parse register list. Return false if REG_LIST is zero, which is an
+ invalid value. */
+
+static bool
+reglist_lookup (char **s, unsigned *reg_list)
+{
+ *reg_list = 0;
+ char *reglist = strdup (*s);
+ if (reglist != NULL)
+ {
+ char *token = strtok (reglist, "}");
+ if (token != NULL)
+ {
+ *s += strlen (token);
+ *reg_list = reglist_lookup_internal (reglist);
+ }
+ else
+ {
+ as_bad (_("cannot find `}' for cm.push/cm.pop"));
+ *reg_list = 0;
+ }
+ }
+ free (reglist);
+ return *reg_list == 0 ? false : true;
+}
+
#define USE_BITS(mask,shift) (used_bits |= ((insn_t)(mask) << (shift)))
#define USE_IMM(n, s) \
(used_bits |= ((insn_t)((1ull<<n)-1) << (s)))
@@ -1370,6 +1522,8 @@ validate_riscv_insn (const struct riscv_opcode *opc, int length)
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;
case 'A': break; /* Macro operand, must be symbol. */
@@ -1450,6 +1604,10 @@ validate_riscv_insn (const struct riscv_opcode *opc, int length)
case 'h': used_bits |= ENCODE_ZCB_HALFWORD_UIMM (-1U); break;
/* halfword immediate operators, load/store halfword insns. */
case 'b': used_bits |= ENCODE_ZCB_BYTE_UIMM (-1U); 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. */
+ case 'r': USE_BITS (OP_MASK_REG_LIST, OP_SH_REG_LIST); break;
case 'f': break;
default:
goto unknown_validate_operand;
@@ -3206,6 +3364,8 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr,
case ')':
case '[':
case ']':
+ case '{':
+ case '}':
if (*asarg++ == *oparg)
continue;
break;
@@ -3661,6 +3821,27 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr,
break;
ip->insn_opcode |= ENCODE_ZCB_BYTE_UIMM (imm_expr->X_add_number);
goto rvc_imm_done;
+ case 'r':
+ if (!reglist_lookup (&asarg, &regno))
+ break;
+ INSERT_OPERAND (REG_LIST, *ip, regno);
+ continue;
+ case 'p':
+ if (my_getSmallExpression (imm_expr, imm_reloc, asarg, p)
+ || imm_expr->X_op != O_constant)
+ break;
+ /* Convert stack adjustment of cm.push to a positive
+ offset. */
+ if (ip->insn_mo->match == MATCH_CM_PUSH)
+ imm_expr->X_add_number *= -1;
+ /* Subtract base stack adjustment and get spimm. */
+ imm_expr->X_add_number -=
+ riscv_get_sp_base (ip->insn_opcode, *riscv_rps_as.xlen);
+ 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;
case 'f': /* Operand for matching immediate 255. */
if (my_getSmallExpression (imm_expr, imm_reloc, asarg, p)
|| imm_expr->X_op != O_constant