/* tc-riscv.c -- RISC-V assembler
- Copyright (C) 2011-2017 Free Software Foundation, Inc.
+ Copyright (C) 2011-2019 Free Software Foundation, Inc.
Contributed by Andrew Waterman (andrew@sifive.com).
Based on MIPS target.
#include "itbl-ops.h"
#include "dwarf2dbg.h"
#include "dw2gencfi.h"
-#include "struc-symbol.h"
+#include "bfd/elfxx-riscv.h"
#include "elf/riscv.h"
#include "opcode/riscv.h"
#define DEFAULT_ARCH "riscv64"
#endif
+#ifndef DEFAULT_RISCV_ATTR
+#define DEFAULT_RISCV_ATTR 0
+#endif
+
static const char default_arch[] = DEFAULT_ARCH;
static unsigned xlen = 0; /* width of an x-register */
static unsigned abi_xlen = 0; /* width of a pointer in the ABI */
+static bfd_boolean rve_abi = FALSE;
#define LOAD_ADDRESS_INSN (abi_xlen == 64 ? "ld" : "lw")
#define ADD32_INSN (xlen == 64 ? "addiw" : "addi")
{
int pic; /* Generate position-independent code. */
int rvc; /* Generate RVC code. */
+ int rve; /* Generate RVE code. */
int relax; /* Emit relocs the linker is allowed to relax. */
+ int arch_attr; /* Emit arch attribute. */
};
static struct riscv_set_options riscv_opts =
{
0, /* pic */
0, /* rvc */
+ 0, /* rve */
1, /* relax */
+ DEFAULT_RISCV_ATTR, /* arch_attr */
};
static void
riscv_opts.rvc = rvc_value;
}
-struct riscv_subset
+static void
+riscv_set_rve (bfd_boolean rve_value)
{
- const char *name;
-
- struct riscv_subset *next;
-};
+ riscv_opts.rve = rve_value;
+}
-static struct riscv_subset *riscv_subsets;
+static riscv_subset_list_t riscv_subsets;
static bfd_boolean
riscv_subset_supports (const char *feature)
{
- struct riscv_subset *s;
- char *p;
- unsigned xlen_required = strtoul (feature, &p, 10);
-
- if (xlen_required && xlen != xlen_required)
- return FALSE;
-
- for (s = riscv_subsets; s != NULL; s = s->next)
- if (strcasecmp (s->name, p) == 0)
- return TRUE;
+ if (riscv_opts.rvc && (strcasecmp (feature, "c") == 0))
+ return TRUE;
- return FALSE;
+ return riscv_lookup_subset (&riscv_subsets, feature) != NULL;
}
-static void
-riscv_clear_subsets (void)
+static bfd_boolean
+riscv_multi_subset_supports (enum riscv_insn_class insn_class)
{
- while (riscv_subsets != NULL)
+ switch (insn_class)
{
- struct riscv_subset *next = riscv_subsets->next;
- free ((void *) riscv_subsets->name);
- free (riscv_subsets);
- riscv_subsets = next;
- }
-}
+ case INSN_CLASS_I: return riscv_subset_supports ("i");
+ case INSN_CLASS_C: return riscv_subset_supports ("c");
+ case INSN_CLASS_A: return riscv_subset_supports ("a");
+ case INSN_CLASS_M: return riscv_subset_supports ("m");
+ case INSN_CLASS_F: return riscv_subset_supports ("f");
+ case INSN_CLASS_D: return riscv_subset_supports ("d");
+ case INSN_CLASS_D_AND_C:
+ return riscv_subset_supports ("d") && riscv_subset_supports ("c");
-static void
-riscv_add_subset (const char *subset)
-{
- struct riscv_subset *s = xmalloc (sizeof *s);
+ case INSN_CLASS_F_AND_C:
+ return riscv_subset_supports ("f") && riscv_subset_supports ("c");
- s->name = xstrdup (subset);
- s->next = riscv_subsets;
- riscv_subsets = s;
+ case INSN_CLASS_Q: return riscv_subset_supports ("q");
+
+ default:
+ as_fatal ("Unreachable");
+ return FALSE;
+ }
}
/* Set which ISA and extensions are available. */
static void
riscv_set_arch (const char *s)
{
- const char *all_subsets = "imafdqc";
- char *extension = NULL;
- const char *p = s;
-
- riscv_clear_subsets();
-
- if (strncmp (p, "rv32", 4) == 0)
- {
- xlen = 32;
- p += 4;
- }
- else if (strncmp (p, "rv64", 4) == 0)
- {
- xlen = 64;
- p += 4;
- }
- else
- as_fatal ("-march=%s: ISA string must begin with rv32 or rv64", s);
-
- switch (*p)
- {
- case 'i':
- break;
-
- case 'g':
- p++;
- for ( ; *all_subsets != 'q'; all_subsets++)
- {
- const char subset[] = {*all_subsets, '\0'};
- riscv_add_subset (subset);
- }
- break;
-
- default:
- as_fatal ("-march=%s: first ISA subset must be `i' or `g'", s);
- }
-
- while (*p)
- {
- if (*p == 'x')
- {
- char *subset = xstrdup (p);
- char *q = subset;
-
- while (*++q != '\0' && *q != '_')
- ;
- *q = '\0';
-
- if (extension)
- as_fatal ("-march=%s: only one non-standard extension is supported"
- " (found `%s' and `%s')", s, extension, subset);
- extension = subset;
- riscv_add_subset (subset);
- p += strlen (subset);
- }
- else if (*p == '_')
- p++;
- else if ((all_subsets = strchr (all_subsets, *p)) != NULL)
- {
- const char subset[] = {*p, 0};
- riscv_add_subset (subset);
- all_subsets++;
- p++;
- }
- else
- as_fatal ("-march=%s: unsupported ISA subset `%c'", s, *p);
- }
+ riscv_parse_subset_t rps;
+ rps.subset_list = &riscv_subsets;
+ rps.error_handler = as_fatal;
+ rps.xlen = &xlen;
- free (extension);
+ riscv_release_subset_list (&riscv_subsets);
+ riscv_parse_subset (&rps, s);
}
/* Handle of the OPCODE hash table. */
static struct hash_control *op_hash = NULL;
+/* Handle of the type of .insn hash table. */
+static struct hash_control *insn_type_hash = NULL;
+
/* This array holds the chars that always start a comment. If the
pre-processor is disabled, these aren't very useful */
const char comment_chars[] = "#";
/* or 0d1.2345e12 */
const char FLT_CHARS[] = "rRsSfFdDxXpP";
+/* Indicate we are already assemble any instructions or not. */
+static bfd_boolean start_assemble = FALSE;
+
+/* Indicate arch attribute is explictly set. */
+static bfd_boolean explicit_arch_attr = FALSE;
+
/* Macros for encoding relaxation state for RVC branches and far jumps. */
#define RELAX_BRANCH_ENCODE(uncond, rvc, length) \
((relax_substateT) \
return length;
}
-struct regname
+/* Information about an opcode name, mnemonics and its value. */
+struct opcode_name_t
{
const char *name;
- unsigned int num;
+ unsigned int val;
+};
+
+/* List for all supported opcode name. */
+static const struct opcode_name_t opcode_name_list[] =
+{
+ {"C0", 0x0},
+ {"C1", 0x1},
+ {"C2", 0x2},
+
+ {"LOAD", 0x03},
+ {"LOAD_FP", 0x07},
+ {"CUSTOM_0", 0x0b},
+ {"MISC_MEM", 0x0f},
+ {"OP_IMM", 0x13},
+ {"AUIPC", 0x17},
+ {"OP_IMM_32", 0x1b},
+ /* 48b 0x1f. */
+
+ {"STORE", 0x23},
+ {"STORE_FP", 0x27},
+ {"CUSTOM_1", 0x2b},
+ {"AMO", 0x2f},
+ {"OP", 0x33},
+ {"LUI", 0x37},
+ {"OP_32", 0x3b},
+ /* 64b 0x3f. */
+
+ {"MADD", 0x43},
+ {"MSUB", 0x47},
+ {"NMADD", 0x4f},
+ {"NMSUB", 0x4b},
+ {"OP_FP", 0x53},
+ /*reserved 0x57. */
+ {"CUSTOM_2", 0x5b},
+ /* 48b 0x5f. */
+
+ {"BRANCH", 0x63},
+ {"JALR", 0x67},
+ /*reserved 0x5b. */
+ {"JAL", 0x6f},
+ {"SYSTEM", 0x73},
+ /*reserved 0x77. */
+ {"CUSTOM_3", 0x7b},
+ /* >80b 0x7f. */
+
+ {NULL, 0}
};
+/* Hash table for lookup opcode name. */
+static struct hash_control *opcode_names_hash = NULL;
+
+/* Initialization for hash table of opcode name. */
+static void
+init_opcode_names_hash (void)
+{
+ const char *retval;
+ const struct opcode_name_t *opcode;
+
+ for (opcode = &opcode_name_list[0]; opcode->name != NULL; ++opcode)
+ {
+ retval = hash_insert (opcode_names_hash, opcode->name, (void *)opcode);
+
+ if (retval != NULL)
+ as_fatal (_("internal error: can't hash `%s': %s"),
+ opcode->name, retval);
+ }
+}
+
+/* Find `s` is a valid opcode name or not,
+ return the opcode name info if found. */
+static const struct opcode_name_t *
+opcode_name_lookup (char **s)
+{
+ char *e;
+ char save_c;
+ struct opcode_name_t *o;
+
+ /* Find end of name. */
+ e = *s;
+ if (is_name_beginner (*e))
+ ++e;
+ while (is_part_of_name (*e))
+ ++e;
+
+ /* Terminate name. */
+ save_c = *e;
+ *e = '\0';
+
+ o = (struct opcode_name_t *) hash_find (opcode_names_hash, *s);
+
+ /* Advance to next token if one was recognized. */
+ if (o)
+ *s = e;
+
+ *e = save_c;
+ expr_end = e;
+
+ return o;
+}
+
enum reg_class
{
RCLASS_GPR,
static unsigned int
reg_lookup_internal (const char *s, enum reg_class class)
{
- struct regname *r = (struct regname *) hash_find (reg_names_hash, s);
+ void *r = hash_find (reg_names_hash, s);
if (r == NULL || DECODE_REG_CLASS (r) != class)
return -1;
+
+ if (riscv_opts.rve && class == RCLASS_GPR && DECODE_REG_NUM (r) > 15)
+ return -1;
+
return DECODE_REG_NUM (r);
}
const char *p = strchr (*s, ',');
size_t i, len = p ? (size_t)(p - *s) : strlen (*s);
+ if (len == 0)
+ return FALSE;
+
for (i = 0; i < size; i++)
if (array[i] != NULL && strncmp (array[i], *s, len) == 0)
{
/* For consistency checking, verify that all bits are specified either
by the match/mask part of the instruction definition, or by the
- operand list. */
+ operand list.
+
+ `length` could be 0, 4 or 8, 0 for auto detection. */
static bfd_boolean
-validate_riscv_insn (const struct riscv_opcode *opc)
+validate_riscv_insn (const struct riscv_opcode *opc, int length)
{
const char *p = opc->args;
char c;
insn_t used_bits = opc->mask;
- int insn_width = 8 * riscv_insn_length (opc->match);
- insn_t required_bits = ~0ULL >> (64 - insn_width);
+ int insn_width;
+ insn_t required_bits;
+
+ if (length == 0)
+ insn_width = 8 * riscv_insn_length (opc->match);
+ else
+ insn_width = 8 * length;
+
+ required_bits = ~0ULL >> (64 - insn_width);
if ((used_bits & opc->match) != (opc->match & required_bits))
{
case 'v': used_bits |= ENCODE_RVC_IMM (-1U); break;
case 'w': break; /* RS1S, constrained to equal RD */
case 'x': break; /* RS2S, constrained to equal RD */
+ case 'z': break; /* RS2S, contrained to be x0 */
case 'K': used_bits |= ENCODE_RVC_ADDI4SPN_IMM (-1U); break;
case 'L': used_bits |= ENCODE_RVC_ADDI16SP_IMM (-1U); break;
case 'M': used_bits |= ENCODE_RVC_SWSP_IMM (-1U); break;
case 'V': USE_BITS (OP_MASK_CRS2, OP_SH_CRS2); break;
case '<': used_bits |= ENCODE_RVC_IMM (-1U); break;
case '>': used_bits |= ENCODE_RVC_IMM (-1U); break;
+ case '8': used_bits |= ENCODE_RVC_UIMM8 (-1U); break;
+ case 'S': USE_BITS (OP_MASK_CRS1S, OP_SH_CRS1S); break;
case 'T': USE_BITS (OP_MASK_CRS2, OP_SH_CRS2); break;
case 'D': USE_BITS (OP_MASK_CRS2S, OP_SH_CRS2S); break;
+ case 'F': /* funct */
+ switch (c = *p++)
+ {
+ case '6': USE_BITS (OP_MASK_CFUNCT6, OP_SH_CFUNCT6); break;
+ case '4': USE_BITS (OP_MASK_CFUNCT4, OP_SH_CFUNCT4); break;
+ case '3': USE_BITS (OP_MASK_CFUNCT3, OP_SH_CFUNCT3); break;
+ case '2': USE_BITS (OP_MASK_CFUNCT2, OP_SH_CFUNCT2); break;
+ default:
+ as_bad (_("internal: bad RISC-V opcode"
+ " (unknown operand type `CF%c'): %s %s"),
+ c, opc->name, opc->args);
+ return FALSE;
+ }
+ break;
default:
as_bad (_("internal: bad RISC-V opcode (unknown operand type `C%c'): %s %s"),
c, opc->name, opc->args);
case 'm': USE_BITS (OP_MASK_RM, OP_SH_RM); break;
case 's': USE_BITS (OP_MASK_RS1, OP_SH_RS1); break;
case 't': USE_BITS (OP_MASK_RS2, OP_SH_RS2); break;
+ case 'r': USE_BITS (OP_MASK_RS3, OP_SH_RS3); break;
case 'P': USE_BITS (OP_MASK_PRED, OP_SH_PRED); break;
case 'Q': USE_BITS (OP_MASK_SUCC, OP_SH_SUCC); break;
case 'o':
case 'p': used_bits |= ENCODE_SBTYPE_IMM (-1U); break;
case 'q': used_bits |= ENCODE_STYPE_IMM (-1U); break;
case 'u': used_bits |= ENCODE_UTYPE_IMM (-1U); break;
+ case 'z': break;
case '[': break;
case ']': break;
case '0': break;
+ case '1': break;
+ case 'F': /* funct */
+ switch (c = *p++)
+ {
+ case '7': USE_BITS (OP_MASK_FUNCT7, OP_SH_FUNCT7); break;
+ case '3': USE_BITS (OP_MASK_FUNCT3, OP_SH_FUNCT3); break;
+ case '2': USE_BITS (OP_MASK_FUNCT2, OP_SH_FUNCT2); break;
+ default:
+ as_bad (_("internal: bad RISC-V opcode"
+ " (unknown operand type `F%c'): %s %s"),
+ c, opc->name, opc->args);
+ return FALSE;
+ }
+ break;
+ case 'O': /* opcode */
+ switch (c = *p++)
+ {
+ case '4': USE_BITS (OP_MASK_OP, OP_SH_OP); break;
+ case '2': USE_BITS (OP_MASK_OP2, OP_SH_OP2); break;
+ default:
+ as_bad (_("internal: bad RISC-V opcode"
+ " (unknown operand type `F%c'): %s %s"),
+ c, opc->name, opc->args);
+ return FALSE;
+ }
+ break;
default:
as_bad (_("internal: bad RISC-V opcode "
"(unknown operand type `%c'): %s %s"),
bfd_reloc_code_real_type reloc;
};
-/* This function is called once, at assembler startup time. It should set up
- all the tables, etc. that the MD part of the assembler will need. */
-
-void
-md_begin (void)
+/* Common hash table initialization function for
+ instruction and .insn directive. */
+static struct hash_control *
+init_opcode_hash (const struct riscv_opcode *opcodes,
+ bfd_boolean insn_directive_p)
{
int i = 0;
- unsigned long mach = xlen == 64 ? bfd_mach_riscv64 : bfd_mach_riscv32;
-
- if (! bfd_set_arch_mach (stdoutput, bfd_arch_riscv, mach))
- as_warn (_("Could not set architecture and machine"));
-
- op_hash = hash_new ();
-
- while (riscv_opcodes[i].name)
+ int length;
+ struct hash_control *hash = hash_new ();
+ while (opcodes[i].name)
{
- const char *name = riscv_opcodes[i].name;
+ const char *name = opcodes[i].name;
const char *hash_error =
- hash_insert (op_hash, name, (void *) &riscv_opcodes[i]);
+ hash_insert (hash, name, (void *) &opcodes[i]);
if (hash_error)
{
fprintf (stderr, _("internal error: can't hash `%s': %s\n"),
- riscv_opcodes[i].name, hash_error);
+ opcodes[i].name, hash_error);
/* Probably a memory allocation problem? Give up now. */
as_fatal (_("Broken assembler. No assembly attempted."));
}
do
{
- if (riscv_opcodes[i].pinfo != INSN_MACRO)
+ if (opcodes[i].pinfo != INSN_MACRO)
{
- if (!validate_riscv_insn (&riscv_opcodes[i]))
+ if (insn_directive_p)
+ length = ((name[0] == 'c') ? 2 : 4);
+ else
+ length = 0; /* Let assembler determine the length. */
+ if (!validate_riscv_insn (&opcodes[i], length))
as_fatal (_("Broken assembler. No assembly attempted."));
}
+ else
+ gas_assert (!insn_directive_p);
++i;
}
- while (riscv_opcodes[i].name && !strcmp (riscv_opcodes[i].name, name));
+ while (opcodes[i].name && !strcmp (opcodes[i].name, name));
}
+ return hash;
+}
+
+/* This function is called once, at assembler startup time. It should set up
+ all the tables, etc. that the MD part of the assembler will need. */
+
+void
+md_begin (void)
+{
+ unsigned long mach = xlen == 64 ? bfd_mach_riscv64 : bfd_mach_riscv32;
+
+ if (! bfd_set_arch_mach (stdoutput, bfd_arch_riscv, mach))
+ as_warn (_("Could not set architecture and machine"));
+
+ op_hash = init_opcode_hash (riscv_opcodes, FALSE);
+ insn_type_hash = init_opcode_hash (riscv_insn_types, TRUE);
+
reg_names_hash = hash_new ();
hash_reg_names (RCLASS_GPR, riscv_gpr_names_numeric, NGPR);
hash_reg_names (RCLASS_GPR, riscv_gpr_names_abi, NGPR);
hash_reg_names (RCLASS_FPR, riscv_fpr_names_numeric, NFPR);
hash_reg_names (RCLASS_FPR, riscv_fpr_names_abi, NFPR);
+ /* Add "fp" as an alias for "s0". */
+ hash_reg_name (RCLASS_GPR, "fp", 8);
+
+ opcode_names_hash = hash_new ();
+ init_opcode_names_hash ();
+
#define DECLARE_CSR(name, num) hash_reg_name (RCLASS_CSR, #name, num);
#define DECLARE_CSR_ALIAS(name, num) DECLARE_CSR(name, num);
#include "opcode/riscv-opc.h"
append_insn (&insn, ep, r);
}
+/* Build an instruction created by a macro expansion. Like md_assemble but
+ accept a printf-style format string and arguments. */
+
+static void
+md_assemblef (const char *format, ...)
+{
+ char *buf = NULL;
+ va_list ap;
+ int r;
+
+ va_start (ap, format);
+
+ r = vasprintf (&buf, format, ap);
+
+ if (r < 0)
+ as_fatal (_("internal error: vasprintf failed"));
+
+ md_assemble (buf);
+ free(buf);
+
+ va_end (ap);
+}
+
/* Sign-extend 32-bit mode constants that have bit 31 set and all higher bits
unset. */
static void
- 0x80000000);
}
-/* Fail if an expression is not a constant. */
+/* Fail if an expression EX is not a constant. IP is the instruction using EX.
+ MAYBE_CSR is true if the symbol may be an unrecognized CSR name. */
static void
-check_absolute_expr (struct riscv_cl_insn *ip, expressionS *ex)
+check_absolute_expr (struct riscv_cl_insn *ip, expressionS *ex,
+ bfd_boolean maybe_csr)
{
if (ex->X_op == O_big)
as_bad (_("unsupported large constant"));
+ else if (maybe_csr && ex->X_op == O_symbol)
+ as_bad (_("unknown CSR `%s'"),
+ S_GET_NAME (ex->X_add_symbol));
else if (ex->X_op != O_constant)
as_bad (_("Instruction %s requires absolute expression"),
ip->insn_mo->name);
load_const (int reg, expressionS *ep)
{
int shift = RISCV_IMM_BITS;
+ bfd_vma upper_imm;
expressionS upper = *ep, lower = *ep;
lower.X_add_number = (int32_t) ep->X_add_number << (32-shift) >> (32-shift);
upper.X_add_number -= lower.X_add_number;
upper.X_add_number = (int64_t) upper.X_add_number >> shift;
load_const (reg, &upper);
- macro_build (NULL, "slli", "d,s,>", reg, reg, shift);
+ md_assemblef ("slli x%d, x%d, 0x%x", reg, reg, shift);
if (lower.X_add_number != 0)
- macro_build (&lower, "addi", "d,s,j", reg, reg, BFD_RELOC_RISCV_LO12_I);
+ md_assemblef ("addi x%d, x%d, %" BFD_VMA_FMT "d", reg, reg,
+ lower.X_add_number);
}
else
{
if (upper.X_add_number != 0)
{
- macro_build (ep, "lui", "d,u", reg, BFD_RELOC_RISCV_HI20);
+ /* Discard low part and zero-extend upper immediate. */
+ upper_imm = ((uint32_t)upper.X_add_number >> shift);
+
+ md_assemblef ("lui x%d, 0x%" BFD_VMA_FMT "x", reg, upper_imm);
hi_reg = reg;
}
if (lower.X_add_number != 0 || hi_reg == 0)
- macro_build (ep, ADD32_INSN, "d,s,j", reg, hi_reg,
- BFD_RELOC_RISCV_LO12_I);
+ md_assemblef ("%s x%d, x%d, %" BFD_VMA_FMT "d", ADD32_INSN, reg, hi_reg,
+ lower.X_add_number);
}
}
{0, 0}
};
+static const struct percent_op_match percent_op_null[] =
+{
+ {0, 0}
+};
+
/* Return true if *STR points to a relocation operator. When returning true,
move *STR over the operator and store its relocation code in *RELOC.
Leave both *STR and *RELOC alone when returning false. */
unsigned crux_depth, str_depth, regno;
char *crux;
- /* First, check for integer registers. */
+ /* First, check for integer registers. No callers can accept a reg, but
+ we need to avoid accidentally creating a useless undefined symbol below,
+ if this is an instruction pattern that can't match. A glibc build fails
+ if this is removed. */
if (reg_lookup (&str, RCLASS_GPR, ®no))
{
ep->X_op = O_register;
ep->X_add_number = regno;
+ expr_end = str;
return 0;
}
return reloc_index;
}
+/* Parse opcode name, could be an mnemonics or number. */
+static size_t
+my_getOpcodeExpression (expressionS *ep, bfd_reloc_code_real_type *reloc,
+ char *str, const struct percent_op_match *percent_op)
+{
+ const struct opcode_name_t *o = opcode_name_lookup (&str);
+
+ if (o != NULL)
+ {
+ ep->X_op = O_constant;
+ ep->X_add_number = o->val;
+ return 0;
+ }
+
+ return my_getSmallExpression (ep, reloc, str, percent_op);
+}
+
/* Detect and handle implicitly zero load-store offsets. For example,
"lw t0, (t1)" is shorthand for "lw t0, 0(t1)". Return TRUE iff such
an implicit offset was detected. */
static bfd_boolean
-riscv_handle_implicit_zero_offset (expressionS *expr, const char *s)
+riscv_handle_implicit_zero_offset (expressionS *ep, const char *s)
{
/* Check whether there is only a single bracketed expression left.
If so, it must be the base register and the constant must be zero. */
if (*s == '(' && strchr (s + 1, '(') == 0)
{
- expr->X_op = O_constant;
- expr->X_add_number = 0;
+ ep->X_op = O_constant;
+ ep->X_add_number = 0;
return TRUE;
}
static const char *
riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr,
- bfd_reloc_code_real_type *imm_reloc)
+ bfd_reloc_code_real_type *imm_reloc, struct hash_control *hash)
{
char *s;
const char *args;
break;
}
- insn = (struct riscv_opcode *) hash_find (op_hash, str);
+ insn = (struct riscv_opcode *) hash_find (hash, str);
argsStart = s;
for ( ; insn && insn->name && strcmp (insn->name, str) == 0; insn++)
{
- if (!riscv_subset_supports (insn->subset))
+ if ((insn->xlen_requirement != 0) && (xlen != insn->xlen_requirement))
+ continue;
+
+ if (!riscv_multi_subset_supports (insn->insn_class))
continue;
create_insn (ip, insn);
{
if (!insn->match_func (insn, ip->insn_opcode))
break;
- if (riscv_insn_length (insn->match) == 2 && !riscv_opts.rvc)
+
+ /* For .insn, insn->match and insn->mask are 0. */
+ if (riscv_insn_length ((insn->match == 0 && insn->mask == 0)
+ ? ip->insn_opcode
+ : insn->match) == 2
+ && !riscv_opts.rvc)
break;
}
if (*s != '\0')
|| regno != X_SP)
break;
continue;
+ case 'z': /* RS2, contrained to equal x0. */
+ if (!reg_lookup (&s, RCLASS_GPR, ®no)
+ || regno != 0)
+ break;
+ continue;
case '>':
if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
|| imm_expr->X_op != O_constant
break;
ip->insn_opcode |= ENCODE_RVC_IMM (imm_expr->X_add_number);
goto rvc_imm_done;
+ case '8':
+ if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
+ || imm_expr->X_op != O_constant
+ || !VALID_RVC_UIMM8 (imm_expr->X_add_number)
+ || imm_expr->X_add_number < 0
+ || imm_expr->X_add_number >= 256)
+ break;
+ ip->insn_opcode |= ENCODE_RVC_UIMM8 (imm_expr->X_add_number);
+ goto rvc_imm_done;
case 'i':
if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
|| imm_expr->X_op != O_constant
case 'o':
if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
|| imm_expr->X_op != O_constant
+ /* C.addiw, c.li, and c.andi allow zero immediate.
+ C.addi allows zero immediate as hint. Otherwise this
+ is same as 'j'. */
|| !VALID_RVC_IMM (imm_expr->X_add_number))
break;
ip->insn_opcode |= ENCODE_RVC_IMM (imm_expr->X_add_number);
goto branch;
case 'a':
goto jump;
+ case 'S': /* Floating-point RS1 x8-x15. */
+ if (!reg_lookup (&s, RCLASS_FPR, ®no)
+ || !(regno >= 8 && regno <= 15))
+ break;
+ INSERT_OPERAND (CRS1S, *ip, regno % 8);
+ continue;
case 'D': /* Floating-point RS2 x8-x15. */
if (!reg_lookup (&s, RCLASS_FPR, ®no)
|| !(regno >= 8 && regno <= 15))
break;
INSERT_OPERAND (CRS2, *ip, regno);
continue;
+ case 'F':
+ switch (*++args)
+ {
+ case '6':
+ if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
+ || imm_expr->X_op != O_constant
+ || imm_expr->X_add_number < 0
+ || imm_expr->X_add_number >= 64)
+ {
+ as_bad (_("bad value for funct6 field, "
+ "value must be 0...64"));
+ break;
+ }
+
+ INSERT_OPERAND (CFUNCT6, *ip, imm_expr->X_add_number);
+ imm_expr->X_op = O_absent;
+ s = expr_end;
+ continue;
+ case '4':
+ if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
+ || imm_expr->X_op != O_constant
+ || imm_expr->X_add_number < 0
+ || imm_expr->X_add_number >= 16)
+ {
+ as_bad (_("bad value for funct4 field, "
+ "value must be 0...15"));
+ break;
+ }
+
+ INSERT_OPERAND (CFUNCT4, *ip, imm_expr->X_add_number);
+ imm_expr->X_op = O_absent;
+ s = expr_end;
+ continue;
+ case '3':
+ if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
+ || imm_expr->X_op != O_constant
+ || imm_expr->X_add_number < 0
+ || imm_expr->X_add_number >= 8)
+ {
+ as_bad (_("bad value for funct3 field, "
+ "value must be 0...7"));
+ break;
+ }
+ INSERT_OPERAND (CFUNCT3, *ip, imm_expr->X_add_number);
+ imm_expr->X_op = O_absent;
+ s = expr_end;
+ continue;
+ case '2':
+ if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
+ || imm_expr->X_op != O_constant
+ || imm_expr->X_add_number < 0
+ || imm_expr->X_add_number >= 4)
+ {
+ as_bad (_("bad value for funct2 field, "
+ "value must be 0...3"));
+ break;
+ }
+ INSERT_OPERAND (CFUNCT2, *ip, imm_expr->X_add_number);
+ imm_expr->X_op = O_absent;
+ s = expr_end;
+ continue;
+ default:
+ as_bad (_("bad compressed FUNCT field"
+ " specifier 'CF%c'\n"),
+ *args);
+ }
+ break;
+
default:
as_bad (_("bad RVC field specifier 'C%c'\n"), *args);
}
case '<': /* Shift amount, 0 - 31. */
my_getExpression (imm_expr, s);
- check_absolute_expr (ip, imm_expr);
+ check_absolute_expr (ip, imm_expr, FALSE);
if ((unsigned long) imm_expr->X_add_number > 31)
as_bad (_("Improper shift amount (%lu)"),
(unsigned long) imm_expr->X_add_number);
case '>': /* Shift amount, 0 - (XLEN-1). */
my_getExpression (imm_expr, s);
- check_absolute_expr (ip, imm_expr);
+ check_absolute_expr (ip, imm_expr, FALSE);
if ((unsigned long) imm_expr->X_add_number >= xlen)
as_bad (_("Improper shift amount (%lu)"),
(unsigned long) imm_expr->X_add_number);
case 'Z': /* CSRRxI immediate. */
my_getExpression (imm_expr, s);
- check_absolute_expr (ip, imm_expr);
+ check_absolute_expr (ip, imm_expr, FALSE);
if ((unsigned long) imm_expr->X_add_number > 31)
as_bad (_("Improper CSRxI immediate (%lu)"),
(unsigned long) imm_expr->X_add_number);
else
{
my_getExpression (imm_expr, s);
- check_absolute_expr (ip, imm_expr);
+ check_absolute_expr (ip, imm_expr, TRUE);
if ((unsigned long) imm_expr->X_add_number > 0xfff)
as_bad (_("Improper CSR address (%lu)"),
(unsigned long) imm_expr->X_add_number);
case 'd': /* Destination register. */
case 's': /* Source register. */
case 't': /* Target register. */
+ case 'r': /* rs3. */
if (reg_lookup (&s, RCLASS_GPR, ®no))
{
c = *args;
case 't':
INSERT_OPERAND (RS2, *ip, regno);
break;
+ case 'r':
+ INSERT_OPERAND (RS3, *ip, regno);
+ break;
}
continue;
}
s = expr_end;
continue;
+ case 'B':
+ my_getExpression (imm_expr, s);
+ normalize_constant_expr (imm_expr);
+ /* The 'B' format specifier must be a symbol or a constant. */
+ if (imm_expr->X_op != O_symbol && imm_expr->X_op != O_constant)
+ break;
+ if (imm_expr->X_op == O_symbol)
+ *imm_reloc = BFD_RELOC_32;
+ s = expr_end;
+ continue;
+
case 'j': /* Sign-extended immediate. */
- *imm_reloc = BFD_RELOC_RISCV_LO12_I;
p = percent_op_itype;
+ *imm_reloc = BFD_RELOC_RISCV_LO12_I;
goto alu_op;
case 'q': /* Store displacement. */
p = percent_op_stype;
p = percent_op_itype;
*imm_reloc = BFD_RELOC_RISCV_LO12_I;
goto load_store;
- case '0': /* AMO "displacement," which must be zero. */
+ case '1': /* 4-operand add, must be %tprel_add. */
p = percent_op_rtype;
- *imm_reloc = BFD_RELOC_UNUSED;
+ goto alu_op;
+ case '0': /* AMO "displacement," which must be zero. */
+ p = percent_op_null;
load_store:
if (riscv_handle_implicit_zero_offset (imm_expr, s))
continue;
normalize_constant_expr (imm_expr);
if (imm_expr->X_op != O_constant
|| (*args == '0' && imm_expr->X_add_number != 0)
+ || (*args == '1')
|| imm_expr->X_add_number >= (signed)RISCV_IMM_REACH/2
|| imm_expr->X_add_number < -(signed)RISCV_IMM_REACH/2)
break;
case 'u': /* Upper 20 bits. */
p = percent_op_utype;
- if (!my_getSmallExpression (imm_expr, imm_reloc, s, p)
- && imm_expr->X_op == O_constant)
+ if (!my_getSmallExpression (imm_expr, imm_reloc, s, p))
{
+ if (imm_expr->X_op != O_constant)
+ break;
+
if (imm_expr->X_add_number < 0
|| imm_expr->X_add_number >= (signed)RISCV_BIGIMM_REACH)
as_bad (_("lui expression not in range 0..1048575"));
else
*imm_reloc = BFD_RELOC_RISCV_CALL;
continue;
+ case 'O':
+ switch (*++args)
+ {
+ case '4':
+ if (my_getOpcodeExpression (imm_expr, imm_reloc, s, p)
+ || imm_expr->X_op != O_constant
+ || imm_expr->X_add_number < 0
+ || imm_expr->X_add_number >= 128
+ || (imm_expr->X_add_number & 0x3) != 3)
+ {
+ as_bad (_("bad value for opcode field, "
+ "value must be 0...127 and "
+ "lower 2 bits must be 0x3"));
+ break;
+ }
+
+ INSERT_OPERAND (OP, *ip, imm_expr->X_add_number);
+ imm_expr->X_op = O_absent;
+ s = expr_end;
+ continue;
+ case '2':
+ if (my_getOpcodeExpression (imm_expr, imm_reloc, s, p)
+ || imm_expr->X_op != O_constant
+ || imm_expr->X_add_number < 0
+ || imm_expr->X_add_number >= 3)
+ {
+ as_bad (_("bad value for opcode field, "
+ "value must be 0...2"));
+ break;
+ }
+
+ INSERT_OPERAND (OP2, *ip, imm_expr->X_add_number);
+ imm_expr->X_op = O_absent;
+ s = expr_end;
+ continue;
+ default:
+ as_bad (_("bad Opcode field specifier 'O%c'\n"), *args);
+ }
+ break;
+
+ case 'F':
+ switch (*++args)
+ {
+ case '7':
+ if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
+ || imm_expr->X_op != O_constant
+ || imm_expr->X_add_number < 0
+ || imm_expr->X_add_number >= 128)
+ {
+ as_bad (_("bad value for funct7 field, "
+ "value must be 0...127"));
+ break;
+ }
+
+ INSERT_OPERAND (FUNCT7, *ip, imm_expr->X_add_number);
+ imm_expr->X_op = O_absent;
+ s = expr_end;
+ continue;
+ case '3':
+ if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
+ || imm_expr->X_op != O_constant
+ || imm_expr->X_add_number < 0
+ || imm_expr->X_add_number >= 8)
+ {
+ as_bad (_("bad value for funct3 field, "
+ "value must be 0...7"));
+ break;
+ }
+
+ INSERT_OPERAND (FUNCT3, *ip, imm_expr->X_add_number);
+ imm_expr->X_op = O_absent;
+ s = expr_end;
+ continue;
+ case '2':
+ if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
+ || imm_expr->X_op != O_constant
+ || imm_expr->X_add_number < 0
+ || imm_expr->X_add_number >= 4)
+ {
+ as_bad (_("bad value for funct2 field, "
+ "value must be 0...3"));
+ break;
+ }
+
+ INSERT_OPERAND (FUNCT2, *ip, imm_expr->X_add_number);
+ imm_expr->X_op = O_absent;
+ s = expr_end;
+ continue;
+
+ default:
+ as_bad (_("bad FUNCT field specifier 'F%c'\n"), *args);
+ }
+ break;
+
+ case 'z':
+ if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
+ || imm_expr->X_op != O_constant
+ || imm_expr->X_add_number != 0)
+ break;
+ s = expr_end;
+ imm_expr->X_op = O_absent;
+ continue;
default:
as_fatal (_("internal error: bad argument type %c"), *args);
expressionS imm_expr;
bfd_reloc_code_real_type imm_reloc = BFD_RELOC_UNUSED;
- const char *error = riscv_ip (str, &insn, &imm_expr, &imm_reloc);
+ const char *error = riscv_ip (str, &insn, &imm_expr, &imm_reloc, op_hash);
+
+ start_assemble = TRUE;
if (error)
{
OPTION_PIC,
OPTION_NO_PIC,
OPTION_MABI,
+ OPTION_RELAX,
+ OPTION_NO_RELAX,
+ OPTION_ARCH_ATTR,
+ OPTION_NO_ARCH_ATTR,
OPTION_END_OF_ENUM
};
{"fpic", no_argument, NULL, OPTION_PIC},
{"fno-pic", no_argument, NULL, OPTION_NO_PIC},
{"mabi", required_argument, NULL, OPTION_MABI},
+ {"mrelax", no_argument, NULL, OPTION_RELAX},
+ {"mno-relax", no_argument, NULL, OPTION_NO_RELAX},
+ {"march-attr", no_argument, NULL, OPTION_ARCH_ATTR},
+ {"mno-arch-attr", no_argument, NULL, OPTION_NO_ARCH_ATTR},
{NULL, no_argument, NULL, 0}
};
static enum float_abi float_abi = FLOAT_ABI_DEFAULT;
static void
-riscv_set_abi (unsigned new_xlen, enum float_abi new_float_abi)
+riscv_set_abi (unsigned new_xlen, enum float_abi new_float_abi, bfd_boolean rve)
{
abi_xlen = new_xlen;
float_abi = new_float_abi;
+ rve_abi = rve;
}
int
case OPTION_MABI:
if (strcmp (arg, "ilp32") == 0)
- riscv_set_abi (32, FLOAT_ABI_SOFT);
+ riscv_set_abi (32, FLOAT_ABI_SOFT, FALSE);
+ else if (strcmp (arg, "ilp32e") == 0)
+ riscv_set_abi (32, FLOAT_ABI_SOFT, TRUE);
else if (strcmp (arg, "ilp32f") == 0)
- riscv_set_abi (32, FLOAT_ABI_SINGLE);
+ riscv_set_abi (32, FLOAT_ABI_SINGLE, FALSE);
else if (strcmp (arg, "ilp32d") == 0)
- riscv_set_abi (32, FLOAT_ABI_DOUBLE);
+ riscv_set_abi (32, FLOAT_ABI_DOUBLE, FALSE);
else if (strcmp (arg, "ilp32q") == 0)
- riscv_set_abi (32, FLOAT_ABI_QUAD);
+ riscv_set_abi (32, FLOAT_ABI_QUAD, FALSE);
else if (strcmp (arg, "lp64") == 0)
- riscv_set_abi (64, FLOAT_ABI_SOFT);
+ riscv_set_abi (64, FLOAT_ABI_SOFT, FALSE);
else if (strcmp (arg, "lp64f") == 0)
- riscv_set_abi (64, FLOAT_ABI_SINGLE);
+ riscv_set_abi (64, FLOAT_ABI_SINGLE, FALSE);
else if (strcmp (arg, "lp64d") == 0)
- riscv_set_abi (64, FLOAT_ABI_DOUBLE);
+ riscv_set_abi (64, FLOAT_ABI_DOUBLE, FALSE);
else if (strcmp (arg, "lp64q") == 0)
- riscv_set_abi (64, FLOAT_ABI_QUAD);
+ riscv_set_abi (64, FLOAT_ABI_QUAD, FALSE);
else
return 0;
break;
+ case OPTION_RELAX:
+ riscv_opts.relax = TRUE;
+ break;
+
+ case OPTION_NO_RELAX:
+ riscv_opts.relax = FALSE;
+ break;
+
+ case OPTION_ARCH_ATTR:
+ riscv_opts.arch_attr = TRUE;
+ break;
+
+ case OPTION_NO_ARCH_ATTR:
+ riscv_opts.arch_attr = FALSE;
+ break;
+
default:
return 0;
}
as_bad ("unknown default architecture `%s'", default_arch);
}
- if (riscv_subsets == NULL)
+ if (riscv_subsets.head == NULL)
riscv_set_arch (xlen == 64 ? "rv64g" : "rv32g");
/* Add the RVC extension, regardless of -march, to support .option rvc. */
riscv_set_rvc (FALSE);
if (riscv_subset_supports ("c"))
riscv_set_rvc (TRUE);
- else
- riscv_add_subset ("c");
+
+ /* Enable RVE if specified by the -march option. */
+ riscv_set_rve (FALSE);
+ if (riscv_subset_supports ("e"))
+ riscv_set_rve (TRUE);
/* Infer ABI from ISA if not specified on command line. */
if (abi_xlen == 0)
if (float_abi == FLOAT_ABI_DEFAULT)
{
- struct riscv_subset *subset;
+ riscv_subset_t *subset;
/* Assume soft-float unless D extension is present. */
float_abi = FLOAT_ABI_SOFT;
- for (subset = riscv_subsets; subset != NULL; subset = subset->next)
+ for (subset = riscv_subsets.head; subset != NULL; subset = subset->next)
{
if (strcasecmp (subset->name, "D") == 0)
float_abi = FLOAT_ABI_DOUBLE;
}
}
+ if (rve_abi)
+ elf_flags |= EF_RISCV_RVE;
+
/* Insert float_abi into the EF_RISCV_FLOAT_ABI field of elf_flags. */
elf_flags |= float_abi * (EF_RISCV_FLOAT_ABI & ~(EF_RISCV_FLOAT_ABI << 1));
}
if (frag->fr_type == rs_cfa)
{
expressionS exp;
+ expressionS *symval;
- symbolS *add_symbol = frag->fr_symbol->sy_value.X_add_symbol;
- symbolS *op_symbol = frag->fr_symbol->sy_value.X_op_symbol;
-
+ symval = symbol_get_value_expression (frag->fr_symbol);
exp.X_op = O_subtract;
- exp.X_add_symbol = add_symbol;
+ exp.X_add_symbol = symval->X_add_symbol;
exp.X_add_number = 0;
- exp.X_op_symbol = op_symbol;
+ exp.X_op_symbol = symval->X_op_symbol;
fix_new_exp (frag, (int) frag->fr_offset, 1, &exp, 0,
BFD_RELOC_RISCV_CFA);
if (bytes <= insn_alignment)
return TRUE;
- nops = frag_more (worst_case_bytes);
-
/* When not relaxing, riscv_handle_align handles code alignment. */
if (!riscv_opts.relax)
return FALSE;
+ nops = frag_more (worst_case_bytes);
+
ex.X_op = O_constant;
ex.X_add_number = worst_case_bytes;
/* When relaxing, riscv_frag_align_code handles code alignment. */
if (!riscv_opts.relax)
{
- bfd_signed_vma count = fragP->fr_next->fr_address
- - fragP->fr_address - fragP->fr_fix;
-
- if (count <= 0)
+ bfd_signed_vma bytes = (fragP->fr_next->fr_address
+ - fragP->fr_address - fragP->fr_fix);
+ /* We have 4 byte uncompressed nops. */
+ bfd_signed_vma size = 4;
+ bfd_signed_vma excess = bytes % size;
+ char *p = fragP->fr_literal + fragP->fr_fix;
+
+ if (bytes <= 0)
break;
- count &= MAX_MEM_FOR_RS_ALIGN_CODE;
- riscv_make_nops (fragP->fr_literal + fragP->fr_fix, count);
- fragP->fr_var = count;
+ /* Insert zeros or compressed nops to get 4 byte alignment. */
+ if (excess)
+ {
+ riscv_make_nops (p, excess);
+ fragP->fr_fix += excess;
+ p += excess;
+ }
+
+ /* Insert variable number of 4 byte uncompressed nops. */
+ riscv_make_nops (p, size);
+ fragP->fr_var = size;
}
break;
-fno-pic don't generate position-independent code (default)\n\
-march=ISA set the RISC-V architecture\n\
-mabi=ABI set the RISC-V ABI\n\
+ -mrelax enable relax (default)\n\
+ -mno-relax disable relax\n\
+ -march-attr generate RISC-V arch attribute\n\
+ -mno-arch-attr don't generate RISC-V arch attribute\n\
"));
}
if ((reg = reg_lookup_internal (regname, RCLASS_FPR)) >= 0)
return reg + 32;
+ /* CSRs are numbered 4096 -> 8191. */
+ if ((reg = reg_lookup_internal (regname, RCLASS_CSR)) >= 0)
+ return reg + 4096;
+
as_bad (_("unknown register `%s'"), regname);
return -1;
}
return s_leb128 (sign);
}
+/* Parse the .insn directive. */
+
+static void
+s_riscv_insn (int x ATTRIBUTE_UNUSED)
+{
+ char *str = input_line_pointer;
+ struct riscv_cl_insn insn;
+ expressionS imm_expr;
+ bfd_reloc_code_real_type imm_reloc = BFD_RELOC_UNUSED;
+ char save_c;
+
+ while (!is_end_of_line[(unsigned char) *input_line_pointer])
+ ++input_line_pointer;
+
+ save_c = *input_line_pointer;
+ *input_line_pointer = '\0';
+
+ const char *error = riscv_ip (str, &insn, &imm_expr,
+ &imm_reloc, insn_type_hash);
+
+ if (error)
+ {
+ as_bad ("%s `%s'", error, str);
+ }
+ else
+ {
+ gas_assert (insn.insn_mo->pinfo != INSN_MACRO);
+ append_insn (&insn, &imm_expr, imm_reloc);
+ }
+
+ *input_line_pointer = save_c;
+ demand_empty_rest_of_line ();
+}
+
+/* Update arch attributes. */
+
+static void
+riscv_write_out_arch_attr (void)
+{
+ const char *arch_str = riscv_arch_str (xlen, &riscv_subsets);
+
+ bfd_elf_add_proc_attr_string (stdoutput, Tag_RISCV_arch, arch_str);
+
+ xfree ((void *)arch_str);
+}
+
+/* Add the default contents for the .riscv.attributes section. */
+
+static void
+riscv_set_public_attributes (void)
+{
+ if (riscv_opts.arch_attr || explicit_arch_attr)
+ /* Re-write arch attribute to normalize the arch string. */
+ riscv_write_out_arch_attr ();
+}
+
+/* Called after all assembly has been done. */
+
+void
+riscv_md_end (void)
+{
+ riscv_set_public_attributes ();
+}
+
+/* Given a symbolic attribute NAME, return the proper integer value.
+ Returns -1 if the attribute is not known. */
+
+int
+riscv_convert_symbolic_attribute (const char *name)
+{
+ static const struct
+ {
+ const char * name;
+ const int tag;
+ }
+ attribute_table[] =
+ {
+ /* When you modify this table you should
+ also modify the list in doc/c-riscv.texi. */
+#define T(tag) {#tag, Tag_RISCV_##tag}, {"Tag_RISCV_" #tag, Tag_RISCV_##tag}
+ T(arch),
+ T(priv_spec),
+ T(priv_spec_minor),
+ T(priv_spec_revision),
+ T(unaligned_access),
+ T(stack_align),
+#undef T
+ };
+
+ unsigned int i;
+
+ if (name == NULL)
+ return -1;
+
+ for (i = 0; i < ARRAY_SIZE (attribute_table); i++)
+ if (strcmp (name, attribute_table[i].name) == 0)
+ return attribute_table[i].tag;
+
+ return -1;
+}
+
+/* Parse a .attribute directive. */
+
+static void
+s_riscv_attribute (int ignored ATTRIBUTE_UNUSED)
+{
+ int tag = obj_elf_vendor_attribute (OBJ_ATTR_PROC);
+
+ if (tag == Tag_RISCV_arch)
+ {
+ unsigned old_xlen = xlen;
+
+ explicit_arch_attr = TRUE;
+ obj_attribute *attr;
+ attr = elf_known_obj_attributes_proc (stdoutput);
+ if (!start_assemble)
+ riscv_set_arch (attr[Tag_RISCV_arch].s);
+ else
+ as_fatal (_(".attribute arch must set before any instructions"));
+
+ if (old_xlen != xlen)
+ {
+ /* We must re-init bfd again if xlen is changed. */
+ unsigned long mach = xlen == 64 ? bfd_mach_riscv64 : bfd_mach_riscv32;
+ bfd_find_target (riscv_target_format (), stdoutput);
+
+ if (! bfd_set_arch_mach (stdoutput, bfd_arch_riscv, mach))
+ as_warn (_("Could not set architecture and machine"));
+ }
+ }
+}
+
/* Pseudo-op table. */
static const pseudo_typeS riscv_pseudo_table[] =
{"bss", s_bss, 0},
{"uleb128", s_riscv_leb128, 0},
{"sleb128", s_riscv_leb128, 1},
+ {"insn", s_riscv_insn, 0},
+ {"attribute", s_riscv_attribute, 0},
{ NULL, NULL, 0 },
};