@@ -195,6 +195,10 @@ static bool start_assemble = false;
static bool probing_insn_operands;
+/* Global instruction table for APEX extensions.
+ Indexed by instruction format offset (XD, XS, XI, XC) plus sub-opcode. */
+struct riscv_opcode *arcv_apex_insns[ARCV_APEX_INSN_LIMIT];
+
/* Set the default_isa_spec. Return 0 if the spec isn't supported.
Otherwise, return 1. */
@@ -1770,6 +1774,18 @@ validate_riscv_insn (const struct riscv_opcode *opc, int length)
goto unknown_validate_operand;
}
break;
+ case 'a': /* Vendor-specific (ARC-V) operands. */
+ switch (*++oparg)
+ {
+ case 'd': USE_BITS (OP_MASK_RD, OP_SH_RD); break;
+ case 's': USE_BITS (OP_MASK_RS1, OP_SH_RS1); break;
+ case 't': USE_BITS (OP_MASK_RS2, OP_SH_RS2); break;
+ case 'k': used_bits |= ENCODE_ARCV_APEX_8BIT_IMM (-1U); break;
+ case 'j': used_bits |= ENCODE_ARCV_APEX_12BIT_IMM (-1U); break;
+ default:
+ goto unknown_validate_operand;
+ }
+ break;
default:
goto unknown_validate_operand;
}
@@ -2835,6 +2851,164 @@ static symbolS *deferred_sym_lastP;
static symbolS *orphan_sym_rootP;
static symbolS *orphan_sym_lastP;
+/* Return true if INSN is the first entry of an APEX XS|XC pair
+ (i.e. the next contiguous entry has the same name and an XC mask). */
+
+static bool
+arcv_apex_xs_xc_pair_p (const struct riscv_opcode *insn)
+{
+ return (insn->mask == ARCV_APEX_MASK_XS
+ && insn[1].name != NULL
+ && strcmp (insn->name, insn[1].name) == 0
+ && insn[1].mask == ARCV_APEX_MASK_XC);
+}
+
+/* Validate operands of an APEX instruction.
+
+ Verifies that operands match expected argument types for XD, XS, XI,
+ or XC APEX instruction formats. Ensures operand count matches, register
+ operands are valid GPRs, and immediate operands conform to constraints.
+ For XS|XC pairs, an out-of-range immediate requires rd == rs1 (the XC
+ encoding only has a single register field). Returns true if valid. */
+
+static bool
+arcv_apex_validate_insn (const char *operands, const struct riscv_opcode *insn)
+{
+ /* Skip non-APEX instructions. */
+ if (insn == NULL
+ || (insn->mask != ARCV_APEX_MASK_XD
+ && insn->mask != ARCV_APEX_MASK_XS
+ && insn->mask != ARCV_APEX_MASK_XI
+ && insn->mask != ARCV_APEX_MASK_XC))
+ return true;
+
+ /* Make local copies for tokenization. */
+ char *operands_copy = xstrdup (operands);
+ char *args_copy = xstrdup (insn->args);
+ bool ok = true;
+
+ char *operand_tokens[3] = { NULL };
+ char *arg_types[3] = { NULL };
+ expressionS operand_exprs[3];
+
+ int operand_count = 0;
+
+ /* Split operands into tokens (at most 3). */
+ char *token = strtok (operands_copy, ",");
+ while (token && operand_count < 3)
+ {
+ operand_tokens[operand_count++] = token;
+ token = strtok (NULL, ",");
+ }
+ if (token)
+ operand_count++;
+
+ /* Split expected argument types into tokens (at most 3). */
+ int arg_count = 0;
+ token = strtok (args_copy, ",");
+ while (token && arg_count < 3)
+ {
+ arg_types[arg_count++] = token;
+ token = strtok (NULL, ",");
+ }
+
+ /* Check if operand count matches the expected argument count. */
+ if (arg_count != operand_count)
+ {
+ as_bad (_("expected %d operands but %d were specified"),
+ arg_count, operand_count);
+ ok = false;
+ }
+ else
+ {
+ for (int i = 0; i < arg_count && ok; i++)
+ {
+ const char *arg_type = arg_types[i];
+ char *operand = operand_tokens[i];
+ while (ISSPACE (*operand))
+ operand++;
+ /* Skip the ARC-V APEX operand prefix "Xa". */
+ arg_type += 2;
+
+ my_getExpression (&operand_exprs[i], operand, false);
+ /* Check that immediate operands are constants. */
+ if ((*arg_type == 'k' || *arg_type == 'j')
+ && operand_exprs[i].X_op != O_constant)
+ {
+ as_bad (_("operands do not conform to the "
+ "specified APEX format"));
+ ok = false;
+ }
+ else if (*arg_type != 'k' && *arg_type != 'j'
+ && !str_hash_find (reg_names_hash, operand))
+ {
+ as_bad (_("operand must be a general-purpose "
+ "register"));
+ ok = false;
+ }
+ }
+
+ /* For plain XS instructions (no XC fallback), check the 8-bit
+ immediate range here so the user gets a specific message
+ rather than "illegal operands" from the assembly phase. */
+ if (ok && insn->mask == ARCV_APEX_MASK_XS
+ && !arcv_apex_xs_xc_pair_p (insn))
+ {
+ for (int i = 0; i < arg_count && ok; i++)
+ {
+ const char *at = arg_types[i];
+ if (at[0] == 'X' && at[1] == 'a' && at[2] == 'k')
+ {
+ if (operand_exprs[i].X_op == O_constant
+ && (operand_exprs[i].X_add_number < -128
+ || operand_exprs[i].X_add_number > 127))
+ {
+ as_bad (_("integer operand out of range; "
+ "should be from -128 to 127, "
+ "inclusive"));
+ ok = false;
+ }
+ }
+ }
+ }
+
+ /* For XS|XC pairs: if the immediate exceeds the XS 8-bit range,
+ the assembler will fall through to the XC encoding which only
+ has a single register field (dest == src). Reject early if
+ the registers differ. */
+ if (ok && arcv_apex_xs_xc_pair_p (insn) && arg_count == 3)
+ {
+ const char *arg2 = arg_types[2];
+ if (arg2[0] == 'X' && arg2[1] == 'a' && arg2[2] == 'k')
+ {
+ if (operand_exprs[2].X_op == O_constant
+ && (operand_exprs[2].X_add_number < -128
+ || operand_exprs[2].X_add_number > 127))
+ {
+ const char *op0 = operand_tokens[0];
+ const char *op1 = operand_tokens[1];
+ while (ISSPACE (*op0))
+ op0++;
+ while (ISSPACE (*op1))
+ op1++;
+ if (strcmp (op0, op1) != 0)
+ {
+ as_bad (_("APEX XS|XC instruction needs identical "
+ "destination and source when the "
+ "immediate is outside the range "
+ "-128..127"));
+ ok = false;
+ }
+ }
+ }
+ }
+ }
+
+ xfree (operands_copy);
+ xfree (args_copy);
+ return ok;
+}
+
/* This routine assembles an instruction into its binary format. As a
side effect, it sets the global variable imm_reloc to the type of
relocation to do if one of the operands is an address expression. */
@@ -2871,6 +3045,10 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr,
insn = str_hash_find (hash, str);
+ /* Early validation for APEX instructions. */
+ if (!arcv_apex_validate_insn (asarg, insn))
+ return error;
+
probing_insn_operands = true;
asargStart = asarg;
@@ -4286,6 +4464,64 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr,
}
break;
+ case 'a': /* Vendor-specific (ARC-V) operands. */
+ switch (*++oparg)
+ {
+ case 'k': /* ARC-V APEX 8-bit immediate. */
+ my_getExpression (imm_expr, asarg, false);
+ check_absolute_expr (ip, imm_expr, false);
+ if (imm_expr->X_add_number < -128
+ || imm_expr->X_add_number > 127)
+ /* Out of 8-bit range. Break to try next variant
+ (e.g., XC encoding in XS|XC pairs). Validation
+ already checked this and provided user feedback. */
+ break;
+ ip->insn_opcode
+ |= ENCODE_ARCV_APEX_8BIT_IMM (imm_expr->X_add_number);
+ asarg = expr_parse_end;
+ continue;
+ case 'j': /* ARC-V APEX 12-bit immediate. */
+ my_getExpression (imm_expr, asarg, false);
+ check_absolute_expr (ip, imm_expr, false);
+ if (imm_expr->X_add_number < -2048
+ || imm_expr->X_add_number > 2047)
+ as_bad (_("integer operand out of range; "
+ "should be from -2048 to 2047, "
+ "inclusive"));
+ ip->insn_opcode
+ |= ENCODE_ARCV_APEX_12BIT_IMM
+ (imm_expr->X_add_number);
+ asarg = expr_parse_end;
+ continue;
+ case 'd': /* ARC-V APEX destination register. */
+ case 's': /* ARC-V APEX source register. */
+ case 't': /* ARC-V APEX target register. */
+ if (reg_lookup (&asarg, RCLASS_GPR, ®no))
+ {
+ char c = *oparg;
+ if (*asarg == ' ')
+ ++asarg;
+
+ switch (c)
+ {
+ case 's':
+ INSERT_OPERAND (RS1, *ip, regno);
+ break;
+ case 'd':
+ INSERT_OPERAND (RD, *ip, regno);
+ break;
+ case 't':
+ INSERT_OPERAND (RS2, *ip, regno);
+ break;
+ }
+ continue;
+ }
+ break;
+ default:
+ goto unknown_riscv_ip_operand;
+ }
+ break;
+
default:
goto unknown_riscv_ip_operand;
}
@@ -5899,6 +6135,400 @@ s_variant_cc (int ignored ATTRIBUTE_UNUSED)
elfsym->internal_elf_sym.st_other |= STO_RISCV_VARIANT_CC;
}
+/* Record INSN in the arcv_apex_insns table for the given format OFFSET
+ and function OPCODE. Returns false if that slot is already taken
+ (duplicate .extInstruction for the same encoding). */
+
+static bool
+arcv_apex_check_and_set_insn (struct riscv_opcode *insn,
+ unsigned int opcode,
+ unsigned int offset)
+{
+ if (arcv_apex_insns[offset + opcode])
+ {
+ as_bad (_(".extInstruction '%s' duplicates opcode used by '%s'"),
+ insn->name, arcv_apex_insns[offset + opcode]->name);
+ return false;
+ }
+ arcv_apex_insns[offset + opcode] = insn;
+ return true;
+}
+
+/* Create and get a read-only COMDAT section for an APEX instruction.
+
+ Section names encode the instruction format flags (XD=1<<0, XS=1<<1,
+ XI=1<<2, XC=1<<3), followed by the fixed Custom-0 value (11) and the
+ instruction opcode.
+
+ For example, an instruction declared as:
+ .extInstruction foo,7,XS,XC
+ maps to:
+ .riscvapex.<XS|XC>.11.<function_code>
+ and concretely:
+ .riscvapex.10.11.7
+
+ The section is placed in a COMDAT group so that the linker keeps only
+ one copy when multiple compilation units define the same section.
+ The COMDAT group name mirrors the section name but uses a ".apex."
+ prefix instead of ".riscvapex.". */
+
+static segT
+arcv_apex_get_metadata_section (unsigned int insn_format,
+ unsigned int sub_opcode)
+{
+ /* Allocate buffer for full section name. */
+ const char *section_name
+ = xasprintf (".riscvapex.%u.%u.%u",
+ insn_format, ARCV_APEX_CUSTOM0_OPCODE, sub_opcode);
+
+ /* Create a new section with appropriate flags. */
+ segT apex_section = subseg_new (section_name, 0);
+ bfd_set_section_flags (apex_section,
+ SEC_READONLY | SEC_HAS_CONTENTS | SEC_LINK_ONCE);
+
+ /* Build the COMDAT group name for the section, prefixed with ".apex.". */
+ const char *group_name
+ = xasprintf (".apex.%u.%u.%u",
+ insn_format, ARCV_APEX_CUSTOM0_OPCODE, sub_opcode);
+
+ /* Attach the group name to the section. */
+ elf_set_group_name (apex_section, group_name);
+
+ return apex_section;
+}
+
+/* Write APEX instruction metadata into the current section.
+
+ Builds an apex_insn record with fixed fields, function code,
+ flags, and the instruction mnemonic. The record is padded to
+ maintain 16-bit alignment, ensuring correct layout in the
+ section. This section is later used by the disassembler to
+ decode the instruction. */
+
+static void
+arcv_apex_write_metadata (const char *insn_name,
+ unsigned int flags,
+ unsigned int sub_opcode)
+{
+ struct apex_insn apex;
+ size_t name_len = strlen (insn_name);
+
+ /* NUL terminator, plus one extra zero byte if needed to
+ keep the total record length even. */
+ size_t nul_pad = (name_len & 1) ? 1 : 2;
+
+ size_t total_size = offsetof (struct apex_insn, name)
+ + name_len + nul_pad;
+
+ apex.len = total_size;
+ apex.type = ARCV_APEX_METADATA_TYPE;
+ apex.opcode = ARCV_APEX_CUSTOM0_OPCODE;
+ apex.func_t = sub_opcode;
+ apex.flags = flags;
+
+ /* Emit fixed fields. */
+ char *where = frag_more (offsetof (struct apex_insn, name));
+ memcpy (where, &apex, offsetof (struct apex_insn, name));
+
+ /* Emit instruction name. */
+ where = frag_more (name_len);
+ memcpy (where, insn_name, name_len);
+
+ /* Emit NUL terminator (+ alignment padding). */
+ where = frag_more (nul_pad);
+ md_number_to_chars (where, 0x0, nul_pad);
+}
+
+/* Allocate and register an APEX instruction.
+
+ Allocates riscv_opcode structure(s), initializes them according to the
+ instruction format using the appropriate setup function, and inserts
+ them into the opcode hash table for later lookup during assembly.
+
+ For combined XS|XC instructions, two contiguous entries are allocated
+ (XS first, then XC, followed by a NULL sentinel) so that riscv_ip's
+ opcode iteration loop naturally tries the XS encoding first and falls
+ through to the wider XC encoding when the immediate does not fit. */
+
+static void
+arcv_apex_register_insn (const char *insn_name,
+ unsigned int flags,
+ unsigned int sub_opcode)
+{
+ struct riscv_opcode *insn;
+ unsigned int offset = 0;
+ unsigned int fmt = flags & (APEX_FLAG_XD | APEX_FLAG_XS
+ | APEX_FLAG_XI | APEX_FLAG_XC);
+ unsigned int max_code;
+ void **hash_slot;
+
+ if (str_hash_find (op_hash, insn_name) != NULL)
+ {
+ as_bad (_("instruction `%s' is already defined; "
+ "`.extInstruction' cannot reuse that name"),
+ insn_name);
+ xfree ((void *) insn_name);
+ return;
+ }
+
+ if (fmt == APEX_FLAG_XD)
+ max_code = 255;
+ else if (fmt == APEX_FLAG_XS)
+ max_code = 63;
+ else if (fmt == (APEX_FLAG_XS | APEX_FLAG_XC)
+ || fmt == APEX_FLAG_XI || fmt == APEX_FLAG_XC)
+ max_code = 31;
+ else
+ {
+ as_bad (_("`.extInstruction' requires at least one of XD, XS, XI, XC"));
+ xfree ((void *) insn_name);
+ return;
+ }
+
+ if (sub_opcode > max_code)
+ {
+ as_bad (_("APEX instruction function code out of range"));
+ xfree ((void *) insn_name);
+ return;
+ }
+
+ if (fmt == (APEX_FLAG_XS | APEX_FLAG_XC))
+ {
+ /* Allocate a contiguous pair of entries plus a NULL sentinel so
+ that riscv_ip's insn++ iteration finds both encodings. The
+ XS entry is tried first; its match_func rejects same-register
+ operands so that they always use the XC encoding. If the 8-bit
+ immediate does not fit, riscv_ip also falls through to the XC
+ (12-bit) entry. */
+ struct riscv_opcode *entries = XCNEWVEC (struct riscv_opcode, 3);
+ struct riscv_opcode *xs_insn = &entries[0];
+ struct riscv_opcode *xc_insn = &entries[1];
+
+ arcv_apex_init_dynamic_insn (xs_insn);
+ xs_insn->name = insn_name;
+ arcv_apex_setup_xs_insn (xs_insn, flags, sub_opcode);
+ xs_insn->match_func = arcv_apex_match_rd_ne_rs1;
+
+ arcv_apex_init_dynamic_insn (xc_insn);
+ xc_insn->name = insn_name;
+ arcv_apex_setup_xc_insn (xc_insn, sub_opcode);
+
+ /* entries[2] is zero-initialized (name == NULL) by XCNEWVEC,
+ which acts as the sentinel for the riscv_ip iteration. */
+
+ if (!arcv_apex_check_and_set_insn (xs_insn, sub_opcode,
+ ARCV_APEX_OFFSET_XS))
+ {
+ xfree (entries);
+ xfree ((void *) insn_name);
+ return;
+ }
+ if (!arcv_apex_check_and_set_insn (xc_insn, sub_opcode,
+ ARCV_APEX_OFFSET_XC))
+ {
+ arcv_apex_insns[sub_opcode + ARCV_APEX_OFFSET_XS] = NULL;
+ xfree (entries);
+ xfree ((void *) insn_name);
+ return;
+ }
+
+ insn = xs_insn;
+ }
+ else
+ {
+ /* Allocate a 2-entry array: the instruction plus a zero sentinel
+ (name == NULL) so that riscv_ip's insn++ loop and helpers like
+ arcv_apex_xs_xc_pair_p can safely read insn[1]. */
+ insn = XCNEWVEC (struct riscv_opcode, 2);
+ arcv_apex_init_dynamic_insn (insn);
+ insn->name = insn_name;
+
+ switch (fmt)
+ {
+ case APEX_FLAG_XD:
+ arcv_apex_setup_xd_insn (insn, flags, sub_opcode);
+ offset = ARCV_APEX_OFFSET_XD;
+ break;
+ case APEX_FLAG_XS:
+ arcv_apex_setup_xs_insn (insn, flags, sub_opcode);
+ offset = ARCV_APEX_OFFSET_XS;
+ break;
+ case APEX_FLAG_XI:
+ arcv_apex_setup_xi_insn (insn, flags, sub_opcode);
+ offset = ARCV_APEX_OFFSET_XI;
+ break;
+ case APEX_FLAG_XC:
+ arcv_apex_setup_xc_insn (insn, sub_opcode);
+ offset = ARCV_APEX_OFFSET_XC;
+ break;
+ default:
+ as_bad (_("internal: bad APEX instruction format in registration"));
+ xfree (insn);
+ xfree ((void *) insn_name);
+ return;
+ }
+
+ if (!arcv_apex_check_and_set_insn (insn, sub_opcode, offset))
+ {
+ xfree (insn);
+ xfree ((void *) insn_name);
+ return;
+ }
+ }
+
+ hash_slot = str_hash_insert (op_hash, insn->name, insn, 0);
+ if (hash_slot != NULL)
+ {
+ as_bad (_("internal: cannot register APEX instruction `%s'"),
+ insn_name);
+ if (fmt == (APEX_FLAG_XS | APEX_FLAG_XC))
+ {
+ arcv_apex_insns[sub_opcode + ARCV_APEX_OFFSET_XS] = NULL;
+ arcv_apex_insns[sub_opcode + ARCV_APEX_OFFSET_XC] = NULL;
+ }
+ else
+ arcv_apex_insns[sub_opcode + offset] = NULL;
+ xfree (insn);
+ xfree ((void *) insn_name);
+ return;
+ }
+
+ segT saved_seg = now_seg;
+ subsegT saved_subseg = now_subseg;
+
+ segT apex_section = arcv_apex_get_metadata_section (flags & 0xF, sub_opcode);
+ subseg_set (apex_section, 0);
+
+ arcv_apex_write_metadata (insn_name, flags, sub_opcode);
+
+ subseg_set (saved_seg, saved_subseg);
+}
+
+/* Parse an APEX .extInstruction directive.
+
+ Syntax: .extInstruction <name>, <sub_opcode>, <attributes>
+
+ The <attributes> may include XD, XS, XI, XC, void, no_src0, and no_src1.
+ Validates attribute combinations and registers the instruction once
+ successfully parsed. */
+
+static void
+arcv_apex_section_parser (int ignore ATTRIBUTE_UNUSED)
+{
+ char *p, c, *insn_name;
+ char *insn_format;
+ unsigned int sub_opcode;
+ /* Skip leading whitespace. */
+ SKIP_WHITESPACE ();
+
+ p = input_line_pointer;
+ c = get_symbol_name (&p);
+
+ insn_name = xstrdup (p);
+ restore_line_pointer (c);
+
+ /* Convert mnemonic to lowercase for canonical form. */
+ for (p = insn_name; *p; ++p)
+ *p = TOLOWER (*p);
+
+ /* Expect a comma after mnemonic. */
+ if (*input_line_pointer != ',')
+ {
+ as_bad (_("expected comma after instruction name"));
+ ignore_rest_of_line ();
+ xfree (insn_name);
+ return;
+ }
+
+ input_line_pointer++;
+ /* Parse sub-opcode value. */
+ sub_opcode = get_absolute_expression ();
+
+ /* Expect a comma after function code. */
+ if (*input_line_pointer != ',')
+ {
+ as_bad (_("expected comma after instruction opcode"));
+ ignore_rest_of_line ();
+ xfree (insn_name);
+ return;
+ }
+
+ uint8_t flags = 0;
+ while (*input_line_pointer == ',')
+ {
+ input_line_pointer++;
+
+ p = input_line_pointer;
+ c = get_symbol_name (&p);
+ insn_format = xstrdup (p);
+
+ if (strcmp (insn_format, "XD") == 0)
+ flags |= APEX_FLAG_XD;
+ else if (strcmp (insn_format, "XS") == 0)
+ flags |= APEX_FLAG_XS;
+ else if (strcmp (insn_format, "XI") == 0)
+ flags |= (APEX_FLAG_XI | APEX_FLAG_NO_SRC1);
+ else if (strcmp (insn_format, "XC") == 0)
+ flags |= APEX_FLAG_XC;
+ else if (strcmp (insn_format, "void") == 0)
+ flags |= APEX_FLAG_VOID;
+ else if (strcmp (insn_format, "no_src0") == 0)
+ flags |= APEX_FLAG_NO_SRC0;
+ else if (strcmp (insn_format, "no_src1") == 0)
+ flags |= APEX_FLAG_NO_SRC1;
+ else
+ {
+ as_bad (_("unrecognized APEX attribute; must be one of "
+ "XD, XS, XI, XC, void, no_src0, no_src1"));
+ restore_line_pointer (c);
+ xfree (insn_format);
+ xfree (insn_name);
+ ignore_rest_of_line ();
+ return;
+ }
+
+ restore_line_pointer (c);
+ xfree (insn_format);
+ }
+
+ if (flags & APEX_FLAG_XD
+ && (flags & (APEX_FLAG_XS | APEX_FLAG_XI | APEX_FLAG_XC)))
+ {
+ as_bad (_("XD and non-XD APEX formats cannot be combined"));
+ xfree (insn_name);
+ ignore_rest_of_line ();
+ return;
+ }
+
+ if ((flags & APEX_FLAG_VOID) && (flags & APEX_FLAG_XC))
+ {
+ as_bad (_("XC and VOID APEX formats cannot be combined"));
+ xfree (insn_name);
+ ignore_rest_of_line ();
+ return;
+ }
+
+ if (flags & APEX_FLAG_NO_SRC0
+ && (flags & (APEX_FLAG_XS | APEX_FLAG_XI | APEX_FLAG_XC)))
+ {
+ as_bad (_("NO_SRC0 and non-XD APEX formats cannot be combined"));
+ xfree (insn_name);
+ ignore_rest_of_line ();
+ return;
+ }
+
+ if (flags & APEX_FLAG_NO_SRC1
+ && (flags & (APEX_FLAG_XS | APEX_FLAG_XC)))
+ {
+ as_bad (_("NO_SRC1 and non-XD/XI APEX formats cannot be combined"));
+ xfree (insn_name);
+ ignore_rest_of_line ();
+ return;
+ }
+
+ arcv_apex_register_insn (insn_name, flags, sub_opcode);
+}
+
/* RISC-V pseudo-ops table. */
static const pseudo_typeS riscv_pseudo_table[] =
{
@@ -5915,6 +6545,7 @@ static const pseudo_typeS riscv_pseudo_table[] =
{"variant_cc", s_variant_cc, 0},
{"float16", float_cons, 'h'},
{"bfloat16", float_cons, 'b'},
+ {"extinstruction", arcv_apex_section_parser, 0},
{ NULL, NULL, 0 },
};
@@ -265,6 +265,59 @@ Floating point constructors for the bfloat16 type, example usage:
.bfloat16 0b:ffc1
@end smallexample
+@cindex @code{.extInstruction} directive, RISC-V
+@cindex ARC-V APEX instructions
+@item .extInstruction @var{name}, @var{opcode}, @var{attrs}@dots{}
+Define a dynamic APEX instruction that uses the Custom-0 encoding space.
+The @var{name} is the mnemonic used in subsequent assembly. The
+@var{opcode} is the function code (sub-opcode) whose valid range depends
+on the format. The @var{attrs} are a comma-separated list drawn from:
+
+@table @code
+@item XD
+XD format (3 register operands, 8-bit sub-opcode, range 0--255).
+@item XS
+XS format (2 registers + 8-bit signed immediate, 6-bit sub-opcode,
+range 0--63).
+@item XI
+XI format (1 register + 12-bit signed immediate, 5-bit sub-opcode,
+range 0--31).
+@item XC
+XC format (1 register acting as both destination and source +
+12-bit signed immediate, 5-bit sub-opcode, range 0--31).
+@item void
+Omit the destination register from the operand list.
+@item no_src0
+Omit the first source register from the operand list.
+@item no_src1
+Omit the second source register (or immediate) from the operand list.
+@end table
+
+Exactly one of @code{XD}, @code{XS}, @code{XI}, or @code{XC} must be
+specified. @code{XS} and @code{XC} may be combined; in that case the
+assembler uses the XS encoding when the destination and source registers
+differ (and the immediate fits in 8 bits), and the XC encoding when the
+registers are the same or the immediate requires 12 bits.
+
+Examples:
+
+@smallexample
+ .extInstruction myalu, 1, XD
+ myalu a0, a1, a2
+
+ .extInstruction myimm, 5, XS
+ myimm a0, a1, 42
+
+ .extInstruction myflex, 3, XS, XC
+ myflex a0, a1, 10
+ myflex a0, a0, 1000
+@end smallexample
+
+The assembler emits metadata about each @code{.extInstruction} into
+ELF sections named @code{.riscvapex.@var{fmt}.@var{custom0}.@var{opcode}},
+which the disassembler reads to reconstruct instruction names and
+operand formats.
+
@end table
@node RISC-V-Modifiers
new file mode 100644
@@ -0,0 +1,18 @@
+#source: x-arcv-apex-01.s
+#objdump: -d
+
+.*:[ ]+file format .*
+
+
+Disassembly of section .text:
+
+0+[0-9a-f]+ <.text>:
+[ ]+[0-9a-f]+:[ ]+00c5c50b[ ]+.*
+[ ]+[0-9a-f]+:[ ]+02c5800b[ ]+.*
+[ ]+[0-9a-f]+:[ ]+0205c00b[ ]+.*
+[ ]+[0-9a-f]+:[ ]+0400000b[ ]+.*
+[ ]+[0-9a-f]+:[ ]+0b15b50b[ ]+.*
+[ ]+[0-9a-f]+:[ ]+0b15d00b[ ]+.*
+[ ]+[0-9a-f]+:[ ]+0163a50b[ ]+.*
+[ ]+[0-9a-f]+:[ ]+0164200b[ ]+.*
+[ ]+[0-9a-f]+:[ ]+0214e50b[ ]+.*
new file mode 100644
@@ -0,0 +1,19 @@
+ .extInstruction foo0,1,XD
+ .extInstruction foo1,2,XD,void
+ .extInstruction foo2,3,XD,void,no_src1
+ .extInstruction foo3,4,XD,void,no_src0,no_src1
+ .extInstruction foo4,5,XS
+ .extInstruction foo5,6,XS,void
+ .extInstruction foo6,7,XI
+ .extInstruction foo7,8,XI,void
+ .extInstruction foo8,9,XC
+
+ foo0 a0,a1,a2
+ foo1 a1,a2
+ foo2 a1
+ foo3
+ foo4 a0,a1,11
+ foo5 a1,11
+ foo6 a0,22
+ foo7 22
+ foo8 a0,a0,33
new file mode 100644
@@ -0,0 +1,13 @@
+#source: x-arcv-apex-02.s
+#objdump: -d
+
+.*:[ ]+file format .*
+
+
+Disassembly of section .text:
+
+0+[0-9a-f]+ <.text>:
+[ ]+[0-9a-f]+:[ ]+0020e50b[ ]+.*
+[ ]+[0-9a-f]+:[ ]+0205b50b[ ]+.*
+[ ]+[0-9a-f]+:[ ]+1000e50b[ ]+.*
+[ ]+[0-9a-f]+:[ ]+0ff0e50b[ ]+.*
new file mode 100644
@@ -0,0 +1,6 @@
+ .extInstruction foo,1,XS,XC
+
+ foo a0,a0,2
+ foo a0,a1,2
+ foo a0,a0,256
+ foo a0,a0,255
new file mode 100644
@@ -0,0 +1,46 @@
+#source: x-arcv-apex-01.s
+#objdump: -s
+
+.*:[ ]+file format .*
+
+Contents of section .group:
+ 0000 01000000 0d000000.*
+Contents of section .group:
+ 0000 01000000 0e000000.*
+Contents of section .group:
+ 0000 01000000 0f000000.*
+Contents of section .group:
+ 0000 01000000 10000000.*
+Contents of section .group:
+ 0000 01000000 11000000.*
+Contents of section .group:
+ 0000 01000000 12000000.*
+Contents of section .group:
+ 0000 01000000 13000000.*
+Contents of section .group:
+ 0000 01000000 14000000.*
+Contents of section .group:
+ 0000 01000000 15000000.*
+Contents of section \.text:
+ 0000 0bc5c500 0b80c502 0bc00502 0b000004.*
+ 0010 0bb5150b 0bd0150b 0ba56301 0b206401.*
+ 0020 0be51402.*
+Contents of section \.riscvapex\.1\.11\.1:
+ 0000 0c010b01 0100666f 6f300000.*
+Contents of section \.riscvapex\.1\.11\.2:
+ 0000 0c010b02 1100666f 6f310000.*
+Contents of section \.riscvapex\.1\.11\.3:
+ 0000 0c010b03 5100666f 6f320000.*
+Contents of section \.riscvapex\.1\.11\.4:
+ 0000 0c010b04 7100666f 6f330000.*
+Contents of section \.riscvapex\.2\.11\.5:
+ 0000 0c010b05 0200666f 6f340000.*
+Contents of section \.riscvapex\.2\.11\.6:
+ 0000 0c010b06 1200666f 6f350000.*
+Contents of section \.riscvapex\.4\.11\.7:
+ 0000 0c010b07 4400666f 6f360000.*
+Contents of section \.riscvapex\.4\.11\.8:
+ 0000 0c010b08 5400666f 6f370000.*
+Contents of section \.riscvapex\.8\.11\.9:
+ 0000 0c010b09 0800666f 6f380000.*
+#pass
new file mode 100644
@@ -0,0 +1,15 @@
+#source: x-arcv-apex-04.s
+#objdump: -d
+
+.*:[ ]+file format .*
+
+
+Disassembly of section .text:
+
+0+[0-9a-f]+ <.text>:
+[ ]+[0-9a-f]+:[ ]+8005b50b[ ]+.*
+[ ]+[0-9a-f]+:[ ]+7f05b50b[ ]+.*
+[ ]+[0-9a-f]+:[ ]+8000a50b[ ]+.*
+[ ]+[0-9a-f]+:[ ]+7ff0a50b[ ]+.*
+[ ]+[0-9a-f]+:[ ]+8000e50b[ ]+.*
+[ ]+[0-9a-f]+:[ ]+7ff0e50b[ ]+.*
new file mode 100644
@@ -0,0 +1,10 @@
+ .extInstruction foo0,1,XS
+ .extInstruction foo1,1,XI
+ .extInstruction foo2,1,XC
+
+ foo0 a0,a1,-128
+ foo0 a0,a1,127
+ foo1 a0,-2048
+ foo1 a0,2047
+ foo2 a0,a0,-2048
+ foo2 a0,a0,2047
new file mode 100644
@@ -0,0 +1 @@
+#error_output: x-arcv-apex-fail-01.l
new file mode 100644
@@ -0,0 +1,2 @@
+.*: Assembler messages:
+.*: Error: unrecognized APEX attribute; must be one of XD, XS, XI, XC, void, no_src0, no_src1
new file mode 100644
@@ -0,0 +1 @@
+ .extInstruction foo,1,XY
new file mode 100644
@@ -0,0 +1 @@
+#error_output: x-arcv-apex-fail-02.l
new file mode 100644
@@ -0,0 +1,5 @@
+.*: Assembler messages:
+.*: Error: NO_SRC0 and non-XD APEX formats cannot be combined
+.*: Error: NO_SRC0 and non-XD APEX formats cannot be combined
+.*: Error: XC and VOID APEX formats cannot be combined
+
new file mode 100644
@@ -0,0 +1,3 @@
+ .extInstruction foo0,1,XS,no_src0,no_src1
+ .extInstruction foo1,1,XI,no_src0,no_src1
+ .extInstruction foo2,1,XC,void,no_src0,no_src1
new file mode 100644
@@ -0,0 +1 @@
+#error_output: x-arcv-apex-fail-03.l
new file mode 100644
@@ -0,0 +1,9 @@
+.*: Assembler messages:
+.*: Error: integer operand out of range; should be from -128 to 127, inclusive
+.*: Error: unrecognized opcode `foo0'
+.*: Error: integer operand out of range; should be from -128 to 127, inclusive
+.*: Error: unrecognized opcode `foo0'
+.*: Error: integer operand out of range; should be from -2048 to 2047, inclusive
+.*: Error: integer operand out of range; should be from -2048 to 2047, inclusive
+.*: Error: integer operand out of range; should be from -2048 to 2047, inclusive
+.*: Error: integer operand out of range; should be from -2048 to 2047, inclusive
new file mode 100644
@@ -0,0 +1,12 @@
+ .extInstruction foo0,1,XS
+ .extInstruction foo1,2,XI
+ .extInstruction foo2,3,XC
+
+ foo0 a0,a1,-129
+ foo0 a0,a1,128
+
+ foo1 a0,-2049
+ foo1 a0,2048
+
+ foo2 a0,a0,-2049
+ foo2 a0,a0,2048
new file mode 100644
@@ -0,0 +1 @@
+#error_output: x-arcv-apex-fail-04.l
new file mode 100644
@@ -0,0 +1,3 @@
+.*: Assembler messages:
+.*: Error: expected comma after instruction name
+.*: Error: expected comma after instruction opcode
new file mode 100644
@@ -0,0 +1,2 @@
+ .extInstruction foo
+ .extInstruction bar,1
new file mode 100644
@@ -0,0 +1 @@
+#error_output: x-arcv-apex-fail-05.l
new file mode 100644
@@ -0,0 +1,3 @@
+.*: Assembler messages:
+.*: Error: expected 3 operands but 2 were specified
+#...
\ No newline at end of file
new file mode 100644
@@ -0,0 +1,3 @@
+ .extInstruction foo,1,XD
+
+ foo a0,a1
new file mode 100644
@@ -0,0 +1 @@
+#error_output: x-arcv-apex-fail-06.l
new file mode 100644
@@ -0,0 +1,7 @@
+.*: Assembler messages:
+.*: Error: operands do not conform to the specified APEX format
+#...
+.*: Error: operands do not conform to the specified APEX format
+#...
+.*: Error: operands do not conform to the specified APEX format
+#...
new file mode 100644
@@ -0,0 +1,7 @@
+ .extInstruction foo,1,XS
+ .extInstruction bar,1,XI
+ .extInstruction baz,1,XC
+
+ foo a0,a1,a2
+ bar a3,a4
+ baz a5,a6,a7
new file mode 100644
@@ -0,0 +1 @@
+#error_output: x-arcv-apex-fail-07.l
new file mode 100644
@@ -0,0 +1,9 @@
+.*: Assembler messages:
+.*: Error: operand must be a general-purpose register
+#...
+.*: Error: operand must be a general-purpose register
+#...
+.*: Error: operand must be a general-purpose register
+#...
+.*: Error: operand must be a general-purpose register
+#...
new file mode 100644
@@ -0,0 +1,9 @@
+ .extInstruction foo0,1,XD
+ .extInstruction foo1,1,XS
+ .extInstruction foo2,1,XI
+ .extInstruction foo3,1,XC
+
+ foo0 a0,a1,r2
+ foo1 a0,r1,1
+ foo2 r0,1
+ foo3 a0,r0,1
new file mode 100644
@@ -0,0 +1 @@
+#error_output: x-arcv-apex-fail-08.l
new file mode 100644
@@ -0,0 +1,5 @@
+.*: Assembler messages:
+.*: Error: .extInstruction 'bar1' duplicates opcode used by 'foo1'
+.*: Error: .extInstruction 'baz1' duplicates opcode used by 'foo1'
+.*: Error: .extInstruction 'bar2' duplicates opcode used by 'foo2'
+.*: Error: .extInstruction 'baz2' duplicates opcode used by 'foo2'
new file mode 100644
@@ -0,0 +1,7 @@
+ .extInstruction foo1,1,XS,XC
+ .extInstruction bar1,1,XC
+ .extInstruction baz1,1,XS
+
+ .extInstruction foo2,2,XC,XS
+ .extInstruction bar2,2,XC
+ .extInstruction baz2,2,XS
new file mode 100644
@@ -0,0 +1 @@
+#error_output: x-arcv-apex-fail-09.l
new file mode 100644
@@ -0,0 +1,3 @@
+.*: Assembler messages:
+.*: Error: instruction `add' is already defined; `\.extInstruction' cannot reuse that name
+#...
new file mode 100644
@@ -0,0 +1 @@
+ .extInstruction add,1,XS
new file mode 100644
@@ -0,0 +1 @@
+#error_output: x-arcv-apex-fail-10.l
new file mode 100644
@@ -0,0 +1,3 @@
+.*: Assembler messages:
+.*: Error: instruction `fooapex' is already defined; `\.extInstruction' cannot reuse that name
+#...
new file mode 100644
@@ -0,0 +1,2 @@
+ .extInstruction fooapex,1,XS
+ .extInstruction fooapex,2,XS
@@ -732,4 +732,72 @@ extern const struct riscv_opcode riscv_insn_types[];
extern unsigned int riscv_get_sp_base (insn_t, unsigned int);
+extern int arcv_apex_match_rd_ne_rs1 (const struct riscv_opcode *, insn_t);
+extern void arcv_apex_init_dynamic_insn (struct riscv_opcode *);
+extern void arcv_apex_setup_xd_insn (struct riscv_opcode *, unsigned int,
+ unsigned int);
+extern void arcv_apex_setup_xs_insn (struct riscv_opcode *, unsigned int,
+ unsigned int);
+extern void arcv_apex_setup_xi_insn (struct riscv_opcode *, unsigned int,
+ unsigned int);
+extern void arcv_apex_setup_xc_insn (struct riscv_opcode *, unsigned int);
+
+/* ARC-V APEX instruction format flags. Used by .extInstruction directive
+ and metadata serialization/deserialization. */
+enum apex_flags {
+ APEX_FLAG_NONE = 0,
+ APEX_FLAG_XD = 1 << 0,
+ APEX_FLAG_XS = 1 << 1,
+ APEX_FLAG_XI = 1 << 2,
+ APEX_FLAG_XC = 1 << 3,
+ APEX_FLAG_VOID = 1 << 4,
+ APEX_FLAG_NO_SRC0 = 1 << 5,
+ APEX_FLAG_NO_SRC1 = 1 << 6,
+};
+
+/* ARC-V APEX instruction metadata record. Serialized in ELF .riscvapex.*
+ sections and read by the disassembler. */
+struct apex_insn
+{
+ uint8_t len;
+ uint8_t type;
+ uint8_t opcode;
+ uint8_t func_t;
+ uint16_t flags;
+ char name[1];
+};
+
+/* The XD-type has 8 function bits encoding up to 256 instructions.
+ The XS-type has 6 function bits encoding up to 64 instructions.
+ Both the XI-type and the XC-type have 5 function bits each encoding up
+ to 32 instructions respectively. Thus giving a total of 384 possible
+ different instructions. */
+#define ARCV_APEX_INSN_LIMIT 384
+#define ARCV_APEX_OFFSET_XD 0 /* 256 entries. */
+#define ARCV_APEX_OFFSET_XS (ARCV_APEX_OFFSET_XD + 256) /* 0 + 256 = 256 */
+#define ARCV_APEX_OFFSET_XI (ARCV_APEX_OFFSET_XS + 64) /* 256 + 64 = 320 */
+#define ARCV_APEX_OFFSET_XC (ARCV_APEX_OFFSET_XI + 32) /* 320 + 32 = 352 */
+
+/* Fixed encoding bits that distinguish XS, XI, and XC formats. */
+#define ARCV_APEX_XS_FIXED_BITS 0x1000
+#define ARCV_APEX_XI_FIXED_BITS 0x2000
+#define ARCV_APEX_XC_FIXED_BITS 0x6000
+
+#define ARCV_APEX_MASK_XD 0xFE00407F
+#define ARCV_APEX_MASK_XS 0xF0707F
+#define ARCV_APEX_MASK_XI 0xFA07F
+#define ARCV_APEX_MASK_XC 0xFE07F
+
+/* RISC-V Custom-0 major opcode used by ARC-V APEX instructions. */
+#define ARCV_APEX_CUSTOM0_OPCODE 0xb
+
+/* Metadata record type for APEX instructions. */
+#define ARCV_APEX_METADATA_TYPE 1
+
+#define ENCODE_ARCV_APEX_8BIT_IMM(x) \
+ (RV_X(x, 0, 8) << 24)
+
+#define ENCODE_ARCV_APEX_12BIT_IMM(x) \
+ (RV_X(x, 0, 12) << 20)
+
#endif /* _RISCV_H_ */
@@ -37,6 +37,14 @@ proc riscv_choose_lp64_emul {} {
return "elf64lriscv"
}
+proc riscv_choose_64_or_32_emul {} {
+ if { [istarget "riscv64*-*"] } {
+ return [riscv_choose_lp64_emul]
+ } else {
+ return [riscv_choose_ilp32_emul]
+ }
+}
+
# target: rv32 or rv64.
# output: Which output you want? (exe, pie, .so)
proc run_dump_test_ifunc { name target output} {
@@ -174,6 +182,8 @@ if [istarget "riscv*-*-*"] {
run_dump_test "uleb128"
run_dump_test "pr31179"
run_dump_test "pr31179-r"
+ run_dump_test "x-arcv-apex-01"
+ run_dump_test "x-arcv-apex-02"
run_ld_link_tests [list \
[list "Weak reference 32" "-T weakref.ld -m[riscv_choose_ilp32_emul]" "" \
"-march=rv32i -mabi=ilp32" {weakref32.s} \
new file mode 100644
@@ -0,0 +1,8 @@
+ .extern func_1
+ .extern func_2
+ .globl _start
+
+ _start:
+ call func_1
+ call func_2
+ ret
new file mode 100644
@@ -0,0 +1,21 @@
+ .extInstruction foo0,1,XD
+ .extInstruction foo1,2,XD,void
+ .extInstruction foo2,3,XD,void,no_src1
+ .extInstruction foo3,4,XD,void,no_src0,no_src1
+ .extInstruction foo4,5,XS
+ .extInstruction foo5,6,XS,void
+ .extInstruction foo6,7,XI
+ .extInstruction foo7,8,XI,void
+ .extInstruction foo8,9,XC
+
+ .globl func_1
+ func_1:
+ foo0 a0,a1,a2
+ foo1 a1,a2
+ foo2 a1
+ foo3
+ foo4 a0,a1,11
+ foo5 a1,11
+ foo6 a0,22
+ foo7 22
+ foo8 a0,a0,33
new file mode 100644
@@ -0,0 +1,21 @@
+ .extInstruction foo0,1,XD
+ .extInstruction foo1,2,XD,void
+ .extInstruction foo2,3,XD,void,no_src1
+ .extInstruction foo3,4,XD,void,no_src0,no_src1
+ .extInstruction foo4,5,XS
+ .extInstruction foo5,6,XS,void
+ .extInstruction foo6,7,XI
+ .extInstruction foo7,8,XI,void
+ .extInstruction foo8,9,XC
+
+ .globl func_2
+ func_2:
+ foo0 a0,a1,a2
+ foo1 a1,a2
+ foo2 a1
+ foo3
+ foo4 a0,a1,11
+ foo5 a1,11
+ foo6 a0,22
+ foo7 22
+ foo8 a0,a0,33
new file mode 100644
@@ -0,0 +1,32 @@
+#source: x-arcv-apex-01-a.s
+#source: x-arcv-apex-01-b.s
+#source: x-arcv-apex-01-c.s
+#ld: -m[riscv_choose_64_or_32_emul]
+#objdump: -s
+
+.*:[ ]+file format .*
+
+#...
+Contents of section \.text:
+#...
+Contents of section \.riscv\.attributes:
+#...
+Contents of section .riscvapex.1.11.1:
+ 0000 0c010b01 0100666f 6f300000.*
+Contents of section .riscvapex.1.11.2:
+ 0000 0c010b02 1100666f 6f310000.*
+Contents of section .riscvapex.1.11.3:
+ 0000 0c010b03 5100666f 6f320000.*
+Contents of section .riscvapex.1.11.4:
+ 0000 0c010b04 7100666f 6f330000.*
+Contents of section .riscvapex.2.11.5:
+ 0000 0c010b05 0200666f 6f340000.*
+Contents of section .riscvapex.2.11.6:
+ 0000 0c010b06 1200666f 6f350000.*
+Contents of section .riscvapex.4.11.7:
+ 0000 0c010b07 4400666f 6f360000.*
+Contents of section .riscvapex.4.11.8:
+ 0000 0c010b08 5400666f 6f370000.*
+Contents of section .riscvapex.8.11.9:
+ 0000 0c010b09 0800666f 6f380000.*
+#pass
new file mode 100644
@@ -0,0 +1,36 @@
+#source: x-arcv-apex-01-a.s
+#source: x-arcv-apex-01-b.s
+#source: x-arcv-apex-01-c.s
+#ld: -m[riscv_choose_64_or_32_emul]
+#objdump: -D -j .text
+
+.*:[ ]+file format .*
+
+
+Disassembly of section .text:
+
+0+[0-9a-f]+ <_start>:
+#...
+
+0+[0-9a-f]+ <func_1>:
+[ ]+[0-9a-f]+:[ ]+00c5c50b[ ]+.*
+[ ]+[0-9a-f]+:[ ]+02c5800b[ ]+.*
+[ ]+[0-9a-f]+:[ ]+0205c00b[ ]+.*
+[ ]+[0-9a-f]+:[ ]+0400000b[ ]+.*
+[ ]+[0-9a-f]+:[ ]+0b15b50b[ ]+.*
+[ ]+[0-9a-f]+:[ ]+0b15d00b[ ]+.*
+[ ]+[0-9a-f]+:[ ]+0163a50b[ ]+.*
+[ ]+[0-9a-f]+:[ ]+0164200b[ ]+.*
+[ ]+[0-9a-f]+:[ ]+0214e50b[ ]+.*
+
+0+[0-9a-f]+ <func_2>:
+[ ]+[0-9a-f]+:[ ]+00c5c50b[ ]+.*
+[ ]+[0-9a-f]+:[ ]+02c5800b[ ]+.*
+[ ]+[0-9a-f]+:[ ]+0205c00b[ ]+.*
+[ ]+[0-9a-f]+:[ ]+0400000b[ ]+.*
+[ ]+[0-9a-f]+:[ ]+0b15b50b[ ]+.*
+[ ]+[0-9a-f]+:[ ]+0b15d00b[ ]+.*
+[ ]+[0-9a-f]+:[ ]+0163a50b[ ]+.*
+[ ]+[0-9a-f]+:[ ]+0164200b[ ]+.*
+[ ]+[0-9a-f]+:[ ]+0214e50b[ ]+.*
+#pass
@@ -448,6 +448,146 @@ match_rd_x1x5_opcode (const struct riscv_opcode *op,
return match_opcode (op, insn) && (rd == 1 || rd == 5);
}
+/* Invariant fields for dynamically allocated APEX opcodes (GAS / metadata).
+ Per-insn name, match, mask, and args are filled by arcv_apex_setup_* or
+ the XS|XC conversion path. */
+
+static const struct riscv_opcode arcv_apex_dynamic_insn_proto =
+{
+ NULL, /* name. */
+ 0, /* xlen_requirement. */
+ INSN_CLASS_I, /* insn_class. */
+ NULL, /* args. */
+ 0, /* match. */
+ 0, /* mask. */
+ match_opcode, /* match_func. */
+ 0 /* pinfo. */
+};
+
+void
+arcv_apex_init_dynamic_insn (struct riscv_opcode *insn)
+{
+ memcpy (insn, &arcv_apex_dynamic_insn_proto, sizeof (*insn));
+}
+
+/* Match only when rd != rs1. Used by the XS entry in an XS|XC pair so
+ that same-register operands always fall through to the XC encoding. */
+
+int
+arcv_apex_match_rd_ne_rs1 (const struct riscv_opcode *op, insn_t insn)
+{
+ int rd = (insn & MASK_RD) >> OP_SH_RD;
+ int rs1 = (insn & MASK_RS1) >> OP_SH_RS1;
+ return match_opcode (op, insn) && rd != rs1;
+}
+
+/* Initialize an APEX instruction in XD format.
+ Sets mask, match, and operand argument string based on flags.
+ The flags APEX_FLAG_VOID, APEX_FLAG_NO_SRC0, and APEX_FLAG_NO_SRC1
+ control which operands are present in the instruction definition. */
+
+void
+arcv_apex_setup_xd_insn (struct riscv_opcode *insn,
+ unsigned int flags,
+ unsigned int sub_opcode)
+{
+ insn->mask = ARCV_APEX_MASK_XD;
+ insn->match = ((uint32_t)(sub_opcode & 0xFE) << 24)
+ | ((uint32_t)(sub_opcode & 0x1) << 14)
+ | ARCV_APEX_CUSTOM0_OPCODE;
+
+ /* Select operand pattern based on flags.
+ Operands use vendor prefix Xa (ARC-V APEX):
+ d = dest, s = src1, t = src2. */
+ switch (flags & (APEX_FLAG_VOID | APEX_FLAG_NO_SRC0 | APEX_FLAG_NO_SRC1))
+ {
+ case APEX_FLAG_NO_SRC1:
+ insn->args = "Xad,Xas";
+ break;
+ case APEX_FLAG_NO_SRC0 | APEX_FLAG_NO_SRC1:
+ insn->args = "Xad";
+ break;
+ case APEX_FLAG_VOID | APEX_FLAG_NO_SRC0 | APEX_FLAG_NO_SRC1:
+ insn->args = "";
+ break;
+ case APEX_FLAG_VOID | APEX_FLAG_NO_SRC1:
+ insn->args = "Xas";
+ break;
+ case APEX_FLAG_VOID:
+ insn->args = "Xas,Xat";
+ break;
+ case 0:
+ insn->args = "Xad,Xas,Xat";
+ break;
+ default:
+ insn->args = "Xad,Xas,Xat";
+ break;
+ }
+}
+
+/* Initialize an APEX instruction in XS format.
+ Sets mask, match, and operand argument string based on flags.
+ The APEX_FLAG_VOID flag controls whether the destination operand
+ is included. */
+
+void
+arcv_apex_setup_xs_insn (struct riscv_opcode *insn,
+ unsigned int flags,
+ unsigned int sub_opcode)
+{
+ insn->mask = ARCV_APEX_MASK_XS;
+ insn->match = ((uint32_t)(sub_opcode & 0x3C) << 18)
+ | ((uint32_t)(sub_opcode & 0x3) << 13)
+ | ARCV_APEX_CUSTOM0_OPCODE
+ | ARCV_APEX_XS_FIXED_BITS;
+
+ /* Select operand pattern based on APEX_FLAG_VOID.
+ Operands: d = dest, s = src1, k = 8-bit immediate. */
+ if (flags & APEX_FLAG_VOID)
+ insn->args = "Xas,Xak";
+ else
+ insn->args = "Xad,Xas,Xak";
+}
+
+/* Initialize an APEX instruction in XI format.
+ Sets mask, match, and operand argument string based on flags.
+ The APEX_FLAG_VOID flag controls whether the destination operand
+ is included. */
+
+void
+arcv_apex_setup_xi_insn (struct riscv_opcode *insn,
+ unsigned int flags,
+ unsigned int sub_opcode)
+{
+ insn->mask = ARCV_APEX_MASK_XI;
+ insn->match = ((uint32_t)(sub_opcode & 0x1F) << 15)
+ | ARCV_APEX_CUSTOM0_OPCODE
+ | ARCV_APEX_XI_FIXED_BITS;
+
+ if (flags & APEX_FLAG_VOID)
+ insn->args = "Xaj";
+ else
+ insn->args = "Xad,Xaj";
+}
+
+/* Initialize an APEX instruction in XC format.
+ Sets mask and match. Set fixed operand argument string.
+ The XC form represents instructions where dest == src. */
+
+void
+arcv_apex_setup_xc_insn (struct riscv_opcode *insn,
+ unsigned int sub_opcode)
+{
+ insn->mask = ARCV_APEX_MASK_XC;
+ insn->match = ((uint32_t)(sub_opcode & 0x1F) << 15)
+ | ARCV_APEX_CUSTOM0_OPCODE
+ | ARCV_APEX_XC_FIXED_BITS;
+
+ /* Fixed operand pattern for XC instructions:
+ dest, dest, 12-bit immediate (same encoding field as XI). */
+ insn->args = "Xad,Xad,Xaj";
+}
+
const struct riscv_opcode riscv_opcodes[] =
{
/* name, xlen, isa, operands, match, mask, match_func, pinfo. */