/* tc-riscv.c -- RISC-V assembler
- Copyright (C) 2011-2021 Free Software Foundation, Inc.
+ Copyright (C) 2011-2022 Free Software Foundation, Inc.
Contributed by Andrew Waterman (andrew@sifive.com).
Based on MIPS target.
#include "dwarf2dbg.h"
#include "dw2gencfi.h"
-#include "bfd/cpu-riscv.h"
#include "bfd/elfxx-riscv.h"
#include "elf/riscv.h"
#include "opcode/riscv.h"
CSR_CLASS_NONE,
CSR_CLASS_I,
- CSR_CLASS_I_32, /* rv32 only */
- CSR_CLASS_F, /* f-ext only */
- CSR_CLASS_DEBUG /* debug CSR */
+ CSR_CLASS_I_32, /* rv32 only */
+ CSR_CLASS_F, /* f-ext only */
+ CSR_CLASS_ZKR, /* zkr only */
+ CSR_CLASS_V, /* rvv only */
+ CSR_CLASS_DEBUG, /* debug CSR */
+ CSR_CLASS_H, /* hypervisor */
+ CSR_CLASS_H_32, /* hypervisor, rv32 only */
+ CSR_CLASS_SMSTATEEN, /* Smstateen only */
+ CSR_CLASS_SMSTATEEN_AND_H, /* Smstateen only (with H) */
+ CSR_CLASS_SMSTATEEN_32, /* Smstateen RV32 only */
+ CSR_CLASS_SMSTATEEN_AND_H_32, /* Smstateen RV32 only (with H) */
+ CSR_CLASS_SSCOFPMF, /* Sscofpmf only */
+ CSR_CLASS_SSCOFPMF_32, /* Sscofpmf RV32 only */
+ CSR_CLASS_SSTC, /* Sstc only */
+ CSR_CLASS_SSTC_AND_H, /* Sstc only (with H) */
+ CSR_CLASS_SSTC_32, /* Sstc RV32 only */
+ CSR_CLASS_SSTC_AND_H_32, /* Sstc RV32 only (with H) */
};
/* This structure holds all restricted conditions for a CSR. */
enum riscv_spec_class define_version;
/* Record the CSR is aborted/invalid from which versions. If it isn't
- aborted in the current version, then it should be CSR_CLASS_VDRAFT. */
+ aborted in the current version, then it should be PRIV_SPEC_CLASS_DRAFT. */
enum riscv_spec_class abort_version;
/* The CSR may have more than one setting. */
struct riscv_csr_extra *next;
};
-/* All standard/Z* extensions defined in all supported ISA spec. */
-struct riscv_ext_version
+/* This structure contains information about errors that occur within the
+ riscv_ip function */
+struct riscv_ip_error
{
- const char *name;
- enum riscv_spec_class isa_spec_class;
- int major_version;
- int minor_version;
-};
-
-static const struct riscv_ext_version ext_version_table[] =
-{
- {"e", ISA_SPEC_CLASS_20191213, 1, 9},
- {"e", ISA_SPEC_CLASS_20190608, 1, 9},
- {"e", ISA_SPEC_CLASS_2P2, 1, 9},
-
- {"i", ISA_SPEC_CLASS_20191213, 2, 1},
- {"i", ISA_SPEC_CLASS_20190608, 2, 1},
- {"i", ISA_SPEC_CLASS_2P2, 2, 0},
-
- {"m", ISA_SPEC_CLASS_20191213, 2, 0},
- {"m", ISA_SPEC_CLASS_20190608, 2, 0},
- {"m", ISA_SPEC_CLASS_2P2, 2, 0},
-
- {"a", ISA_SPEC_CLASS_20191213, 2, 1},
- {"a", ISA_SPEC_CLASS_20190608, 2, 0},
- {"a", ISA_SPEC_CLASS_2P2, 2, 0},
+ /* General error message */
+ const char* msg;
- {"f", ISA_SPEC_CLASS_20191213, 2, 2},
- {"f", ISA_SPEC_CLASS_20190608, 2, 2},
- {"f", ISA_SPEC_CLASS_2P2, 2, 0},
+ /* Statement that caused the error */
+ char* statement;
- {"d", ISA_SPEC_CLASS_20191213, 2, 2},
- {"d", ISA_SPEC_CLASS_20190608, 2, 2},
- {"d", ISA_SPEC_CLASS_2P2, 2, 0},
-
- {"q", ISA_SPEC_CLASS_20191213, 2, 2},
- {"q", ISA_SPEC_CLASS_20190608, 2, 2},
- {"q", ISA_SPEC_CLASS_2P2, 2, 0},
-
- {"c", ISA_SPEC_CLASS_20191213, 2, 0},
- {"c", ISA_SPEC_CLASS_20190608, 2, 0},
- {"c", ISA_SPEC_CLASS_2P2, 2, 0},
-
- {"zicsr", ISA_SPEC_CLASS_20191213, 2, 0},
- {"zicsr", ISA_SPEC_CLASS_20190608, 2, 0},
-
- {"zifencei", ISA_SPEC_CLASS_20191213, 2, 0},
- {"zifencei", ISA_SPEC_CLASS_20190608, 2, 0},
-
- {"zihintpause", ISA_SPEC_CLASS_DRAFT, 1, 0},
-
- /* Terminate the list. */
- {NULL, 0, 0, 0}
+ /* Missing extension that needs to be enabled */
+ const char* missing_ext;
};
#ifndef DEFAULT_ARCH
/* Need to sync the version with RISC-V compiler. */
#ifndef DEFAULT_RISCV_ISA_SPEC
-#define DEFAULT_RISCV_ISA_SPEC "2.2"
+#define DEFAULT_RISCV_ISA_SPEC "20191213"
#endif
#ifndef DEFAULT_RISCV_PRIV_SPEC
static unsigned xlen = 0; /* The width of an x-register. */
static unsigned abi_xlen = 0; /* The width of a pointer in the ABI. */
-static bfd_boolean rve_abi = FALSE;
+static bool rve_abi = false;
enum float_abi
{
FLOAT_ABI_DEFAULT = -1,
{
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 architecture and privileged elf attributes. */
int csr_check; /* Enable the CSR checking. */
{
0, /* pic */
0, /* rvc */
- 0, /* rve */
1, /* relax */
DEFAULT_RISCV_ATTR, /* arch_attr */
0, /* csr_check */
};
+/* Enable or disable the rvc flags for riscv_opts. Turn on the rvc flag
+ for elf_flags once we have enabled c extension. */
+
static void
-riscv_set_rvc (bfd_boolean rvc_value)
+riscv_set_rvc (bool rvc_value)
{
if (rvc_value)
elf_flags |= EF_RISCV_RVC;
riscv_opts.rvc = rvc_value;
}
-static void
-riscv_set_rve (bfd_boolean rve_value)
+/* This linked list records all enabled extensions, which are parsed from
+ the architecture string. The architecture string can be set by the
+ -march option, the elf architecture attributes, and the --with-arch
+ configure option. */
+static riscv_subset_list_t *riscv_subsets = NULL;
+static riscv_parse_subset_t riscv_rps_as =
{
- riscv_opts.rve = rve_value;
-}
-
-static riscv_subset_list_t riscv_subsets;
+ NULL, /* subset_list, we will set it later once
+ riscv_opts_stack is created or updated. */
+ as_bad, /* error_handler. */
+ &xlen, /* xlen. */
+ &default_isa_spec, /* isa_spec. */
+ true, /* check_unknown_prefixed_ext. */
+};
-static bfd_boolean
-riscv_subset_supports (const char *feature)
+/* This structure is used to hold a stack of .option values. */
+struct riscv_option_stack
{
- struct riscv_subset_t *subset;
+ struct riscv_option_stack *next;
+ struct riscv_set_options options;
+ riscv_subset_list_t *subset_list;
+};
- if (riscv_opts.rvc && (strcasecmp (feature, "c") == 0))
- return TRUE;
+static struct riscv_option_stack *riscv_opts_stack = NULL;
- return riscv_lookup_subset (&riscv_subsets, feature, &subset);
-}
+/* Set which ISA and extensions are available. */
-static bfd_boolean
-riscv_multi_subset_supports (enum riscv_insn_class insn_class)
+static void
+riscv_set_arch (const char *s)
{
- switch (insn_class)
+ if (s != NULL && strcmp (s, "") == 0)
{
- 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_Q: return riscv_subset_supports ("q");
-
- case INSN_CLASS_F_AND_C:
- return (riscv_subset_supports ("f")
- && riscv_subset_supports ("c"));
- case INSN_CLASS_D_AND_C:
- return (riscv_subset_supports ("d")
- && riscv_subset_supports ("c"));
-
- case INSN_CLASS_ZICSR:
- return riscv_subset_supports ("zicsr");
- case INSN_CLASS_ZIFENCEI:
- return riscv_subset_supports ("zifencei");
- case INSN_CLASS_ZIHINTPAUSE:
- return riscv_subset_supports ("zihintpause");
-
- default:
- as_fatal ("internal: unreachable");
- return FALSE;
+ as_bad (_("the architecture string of -march and elf architecture "
+ "attributes cannot be empty"));
+ return;
}
-}
-
-/* Handle of the extension with version hash table. */
-static htab_t ext_version_hash = NULL;
-
-static htab_t
-init_ext_version_hash (void)
-{
- const struct riscv_ext_version *table = ext_version_table;
- htab_t hash = str_htab_create ();
- int i = 0;
- while (table[i].name)
+ if (riscv_subsets == NULL)
{
- const char *name = table[i].name;
- if (str_hash_insert (hash, name, &table[i], 0) != NULL)
- as_fatal (_("internal: duplicate %s"), name);
-
- i++;
- while (table[i].name
- && strcmp (table[i].name, name) == 0)
- i++;
+ riscv_subsets = XNEW (riscv_subset_list_t);
+ riscv_subsets->head = NULL;
+ riscv_subsets->tail = NULL;
+ riscv_rps_as.subset_list = riscv_subsets;
}
+ riscv_release_subset_list (riscv_subsets);
+ riscv_parse_subset (&riscv_rps_as, s);
- return hash;
-}
-
-static void
-riscv_get_default_ext_version (const char *name,
- int *major_version,
- int *minor_version)
-{
- struct riscv_ext_version *ext;
-
- if (name == NULL || default_isa_spec == ISA_SPEC_CLASS_NONE)
- return;
-
- ext = (struct riscv_ext_version *) str_hash_find (ext_version_hash, name);
- while (ext
- && ext->name
- && strcmp (ext->name, name) == 0)
- {
- if (ext->isa_spec_class == ISA_SPEC_CLASS_DRAFT
- || ext->isa_spec_class == default_isa_spec)
- {
- *major_version = ext->major_version;
- *minor_version = ext->minor_version;
- return;
- }
- ext++;
- }
-}
-
-/* Set which ISA and extensions are available. */
-
-static void
-riscv_set_arch (const char *s)
-{
- riscv_parse_subset_t rps;
- rps.subset_list = &riscv_subsets;
- rps.error_handler = as_bad;
- rps.xlen = &xlen;
- rps.get_default_version = riscv_get_default_ext_version;
-
- if (s == NULL)
- return;
-
- riscv_release_subset_list (&riscv_subsets);
- riscv_parse_subset (&rps, s);
+ riscv_set_rvc (false);
+ if (riscv_subset_supports (&riscv_rps_as, "c"))
+ riscv_set_rvc (true);
}
/* Indicate -mabi option is explictly set. */
-static bfd_boolean explicit_mabi = FALSE;
+static bool explicit_mabi = false;
+
+/* Set the abi information. */
static void
-riscv_set_abi (unsigned new_xlen, enum float_abi new_float_abi, bfd_boolean rve)
+riscv_set_abi (unsigned new_xlen, enum float_abi new_float_abi, bool rve)
{
abi_xlen = new_xlen;
float_abi = new_float_abi;
{
if (!explicit_mabi)
{
- if (riscv_subset_supports ("q"))
- riscv_set_abi (xlen, FLOAT_ABI_QUAD, FALSE);
- else if (riscv_subset_supports ("d"))
- riscv_set_abi (xlen, FLOAT_ABI_DOUBLE, FALSE);
+ if (riscv_subset_supports (&riscv_rps_as, "q"))
+ riscv_set_abi (xlen, FLOAT_ABI_QUAD, false);
+ else if (riscv_subset_supports (&riscv_rps_as, "d"))
+ riscv_set_abi (xlen, FLOAT_ABI_DOUBLE, false);
+ else if (riscv_subset_supports (&riscv_rps_as, "e"))
+ riscv_set_abi (xlen, FLOAT_ABI_SOFT, true);
else
- riscv_set_abi (xlen, FLOAT_ABI_SOFT, FALSE);
+ riscv_set_abi (xlen, FLOAT_ABI_SOFT, false);
}
else
{
as_bad ("can't have %d-bit ABI on %d-bit ISA", abi_xlen, xlen);
else if (abi_xlen < xlen)
as_bad ("%d-bit ABI not yet supported on %d-bit ISA", abi_xlen, xlen);
+
+ if (riscv_subset_supports (&riscv_rps_as, "e") && !rve_abi)
+ as_bad ("only the ilp32e ABI is supported for e extension");
+
+ if (float_abi == FLOAT_ABI_SINGLE
+ && !riscv_subset_supports (&riscv_rps_as, "f"))
+ as_bad ("ilp32f/lp64f ABI can't be used when f extension "
+ "isn't supported");
+ else if (float_abi == FLOAT_ABI_DOUBLE
+ && !riscv_subset_supports (&riscv_rps_as, "d"))
+ as_bad ("ilp32d/lp64d ABI can't be used when d extension "
+ "isn't supported");
+ else if (float_abi == FLOAT_ABI_QUAD
+ && !riscv_subset_supports (&riscv_rps_as, "q"))
+ as_bad ("ilp32q/lp64q ABI can't be used when q extension "
+ "isn't supported");
}
/* Update the EF_RISCV_FLOAT_ABI field of elf_flags. */
/* Chars that mean this number is a floating point constant.
As in 0f12.456 or 0d1.2345e12. */
-const char FLT_CHARS[] = "rRsSfFdDxXpP";
+const char FLT_CHARS[] = "rRsSfFdDxXpPhH";
/* Indicate we are already assemble any instructions or not. */
-static bfd_boolean start_assemble = FALSE;
+static bool start_assemble = false;
/* Indicate ELF attributes are explicitly set. */
-static bfd_boolean explicit_attr = FALSE;
+static bool explicit_attr = false;
/* Indicate CSR or priv instructions are explicitly used. */
-static bfd_boolean explicit_priv_attr = FALSE;
+static bool explicit_priv_attr = false;
+
+static char *expr_end;
/* Macros for encoding relaxation state for RVC branches and far jumps. */
#define RELAX_BRANCH_ENCODE(uncond, rvc, length) \
#define OPCODE_MATCHES(OPCODE, OP) \
(((OPCODE) & MASK_##OP) == MATCH_##OP)
-static char *expr_end;
+/* Create a new mapping symbol for the transition to STATE. */
+
+static void
+make_mapping_symbol (enum riscv_seg_mstate state,
+ valueT value,
+ fragS *frag)
+{
+ const char *name;
+ switch (state)
+ {
+ case MAP_DATA:
+ name = "$d";
+ break;
+ case MAP_INSN:
+ name = "$x";
+ break;
+ default:
+ abort ();
+ }
+
+ symbolS *symbol = symbol_new (name, now_seg, frag, value);
+ symbol_get_bfdsym (symbol)->flags |= (BSF_NO_FLAGS | BSF_LOCAL);
+
+ /* If .fill or other data filling directive generates zero sized data,
+ or we are adding odd alignemnts, then the mapping symbol for the
+ following code will have the same value. */
+ if (value == 0)
+ {
+ if (frag->tc_frag_data.first_map_symbol != NULL)
+ {
+ know (S_GET_VALUE (frag->tc_frag_data.first_map_symbol)
+ == S_GET_VALUE (symbol));
+ /* Remove the old one. */
+ symbol_remove (frag->tc_frag_data.first_map_symbol,
+ &symbol_rootP, &symbol_lastP);
+ }
+ frag->tc_frag_data.first_map_symbol = symbol;
+ }
+ if (frag->tc_frag_data.last_map_symbol != NULL)
+ {
+ /* The mapping symbols should be added in offset order. */
+ know (S_GET_VALUE (frag->tc_frag_data.last_map_symbol)
+ <= S_GET_VALUE (symbol));
+ /* Remove the old one. */
+ if (S_GET_VALUE (frag->tc_frag_data.last_map_symbol)
+ == S_GET_VALUE (symbol))
+ symbol_remove (frag->tc_frag_data.last_map_symbol,
+ &symbol_rootP, &symbol_lastP);
+ }
+ frag->tc_frag_data.last_map_symbol = symbol;
+}
+
+/* Set the mapping state for frag_now. */
+
+void
+riscv_mapping_state (enum riscv_seg_mstate to_state,
+ int max_chars)
+{
+ enum riscv_seg_mstate from_state =
+ seg_info (now_seg)->tc_segment_info_data.map_state;
+
+ if (!SEG_NORMAL (now_seg)
+ /* For now I only add the mapping symbols to text sections.
+ Therefore, the dis-assembler only show the actual contents
+ distribution for text. Other sections will be shown as
+ data without the details. */
+ || !subseg_text_p (now_seg))
+ return;
+
+ /* The mapping symbol should be emitted if not in the right
+ mapping state */
+ if (from_state == to_state)
+ return;
+
+ valueT value = (valueT) (frag_now_fix () - max_chars);
+ seg_info (now_seg)->tc_segment_info_data.map_state = to_state;
+ make_mapping_symbol (to_state, value, frag_now);
+}
+
+/* Add the odd bytes of paddings for riscv_handle_align. */
+
+static void
+riscv_add_odd_padding_symbol (fragS *frag)
+{
+ /* If there was already a mapping symbol, it should be
+ removed in the make_mapping_symbol. */
+ make_mapping_symbol (MAP_DATA, frag->fr_fix, frag);
+ make_mapping_symbol (MAP_INSN, frag->fr_fix + 1, frag);
+}
+
+/* Remove any excess mapping symbols generated for alignment frags in
+ SEC. We may have created a mapping symbol before a zero byte
+ alignment; remove it if there's a mapping symbol after the
+ alignment. */
+
+static void
+riscv_check_mapping_symbols (bfd *abfd ATTRIBUTE_UNUSED,
+ asection *sec,
+ void *dummy ATTRIBUTE_UNUSED)
+{
+ segment_info_type *seginfo = seg_info (sec);
+ fragS *fragp;
+
+ if (seginfo == NULL || seginfo->frchainP == NULL)
+ return;
+
+ for (fragp = seginfo->frchainP->frch_root;
+ fragp != NULL;
+ fragp = fragp->fr_next)
+ {
+ symbolS *last = fragp->tc_frag_data.last_map_symbol;
+ fragS *next = fragp->fr_next;
+
+ if (last == NULL || next == NULL)
+ continue;
+
+ /* Check the last mapping symbol if it is at the boundary of
+ fragment. */
+ if (S_GET_VALUE (last) < next->fr_address)
+ continue;
+ know (S_GET_VALUE (last) == next->fr_address);
+
+ do
+ {
+ if (next->tc_frag_data.first_map_symbol != NULL)
+ {
+ /* The last mapping symbol overlaps with another one
+ which at the start of the next frag. */
+ symbol_remove (last, &symbol_rootP, &symbol_lastP);
+ break;
+ }
+
+ if (next->fr_next == NULL)
+ {
+ /* The last mapping symbol is at the end of the section. */
+ know (next->fr_fix == 0 && next->fr_var == 0);
+ symbol_remove (last, &symbol_rootP, &symbol_lastP);
+ break;
+ }
+
+ /* Since we may have empty frags without any mapping symbols,
+ keep looking until the non-empty frag. */
+ if (next->fr_address != next->fr_next->fr_address)
+ break;
+
+ next = next->fr_next;
+ }
+ while (next != NULL);
+ }
+}
/* The default target format to use. */
{"NMADD", 0x4f},
{"NMSUB", 0x4b},
{"OP_FP", 0x53},
- /*reserved 0x57. */
+ {"OP_V", 0x57},
{"CUSTOM_2", 0x5b},
/* 48b 0x5f. */
return o;
}
+/* All RISC-V registers belong to one of these classes. */
enum reg_class
{
RCLASS_GPR,
RCLASS_FPR,
+ RCLASS_VECR,
+ RCLASS_VECM,
RCLASS_MAX,
RCLASS_CSR
enum riscv_spec_class abort_version)
{
struct riscv_csr_extra *entry, *pre_entry;
- bfd_boolean need_enrty = TRUE;
+ bool need_enrty = true;
pre_entry = NULL;
entry = (struct riscv_csr_extra *) str_hash_find (csr_extra_hash, name);
&& entry->address == address
&& entry->define_version == define_version
&& entry->abort_version == abort_version)
- need_enrty = FALSE;
+ need_enrty = false;
pre_entry = entry;
entry = entry->next;
}
if (!need_enrty)
return;
- entry = XNEW (struct riscv_csr_extra);
+ entry = notes_alloc (sizeof (*entry));
entry->csr_class = class;
entry->address = address;
entry->define_version = define_version;
{
struct riscv_csr_extra *saved_entry = entry;
enum riscv_csr_class csr_class = entry->csr_class;
- bfd_boolean need_check_version = TRUE;
- bfd_boolean result = TRUE;
+ bool need_check_version = false;
+ bool is_rv32_only = false;
+ bool is_h_required = false;
+ const char* extension = NULL;
switch (csr_class)
{
+ case CSR_CLASS_I_32:
+ is_rv32_only = true;
+ /* Fall through. */
case CSR_CLASS_I:
- result = riscv_subset_supports ("i");
+ need_check_version = true;
+ extension = "i";
break;
- case CSR_CLASS_I_32:
- result = (xlen == 32 && riscv_subset_supports ("i"));
+ case CSR_CLASS_H_32:
+ is_rv32_only = true;
+ /* Fall through. */
+ case CSR_CLASS_H:
+ extension = "h";
break;
case CSR_CLASS_F:
- result = riscv_subset_supports ("f");
- need_check_version = FALSE;
+ extension = "f";
+ break;
+ case CSR_CLASS_ZKR:
+ extension = "zkr";
+ break;
+ case CSR_CLASS_V:
+ extension = "zve32x";
+ break;
+ case CSR_CLASS_SMSTATEEN:
+ case CSR_CLASS_SMSTATEEN_AND_H:
+ case CSR_CLASS_SMSTATEEN_32:
+ case CSR_CLASS_SMSTATEEN_AND_H_32:
+ is_rv32_only = (csr_class == CSR_CLASS_SMSTATEEN_32
+ || csr_class == CSR_CLASS_SMSTATEEN_AND_H_32);
+ is_h_required = (csr_class == CSR_CLASS_SMSTATEEN_AND_H
+ || csr_class == CSR_CLASS_SMSTATEEN_AND_H_32);
+ extension = "smstateen";
+ break;
+ case CSR_CLASS_SSCOFPMF_32:
+ is_rv32_only = true;
+ /* Fall through. */
+ case CSR_CLASS_SSCOFPMF:
+ extension = "sscofpmf";
+ break;
+ case CSR_CLASS_SSTC:
+ case CSR_CLASS_SSTC_AND_H:
+ case CSR_CLASS_SSTC_32:
+ case CSR_CLASS_SSTC_AND_H_32:
+ is_rv32_only = (csr_class == CSR_CLASS_SSTC_32
+ || csr_class == CSR_CLASS_SSTC_AND_H_32);
+ is_h_required = (csr_class == CSR_CLASS_SSTC_AND_H
+ || csr_class == CSR_CLASS_SSTC_AND_H_32);
+ extension = "sstc";
break;
case CSR_CLASS_DEBUG:
- need_check_version = FALSE;
break;
default:
as_bad (_("internal: bad RISC-V CSR class (0x%x)"), csr_class);
}
- if (riscv_opts.csr_check && !result)
- as_warn (_("invalid CSR `%s' for the current ISA"), csr_name);
+ if (riscv_opts.csr_check)
+ {
+ if (is_rv32_only && xlen != 32)
+ as_warn (_("invalid CSR `%s', needs rv32i extension"), csr_name);
+ if (is_h_required && !riscv_subset_supports (&riscv_rps_as, "h"))
+ as_warn (_("invalid CSR `%s', needs `h' extension"), csr_name);
+
+ if (extension != NULL
+ && !riscv_subset_supports (&riscv_rps_as, extension))
+ as_warn (_("invalid CSR `%s', needs `%s' extension"),
+ csr_name, extension);
+ }
while (entry != NULL)
{
if (r == NULL || DECODE_REG_CLASS (r) != class)
return -1;
- if (riscv_opts.rve && class == RCLASS_GPR && DECODE_REG_NUM (r) > 15)
+ if (riscv_subset_supports (&riscv_rps_as, "e")
+ && class == RCLASS_GPR
+ && DECODE_REG_NUM (r) > 15)
return -1;
return DECODE_REG_NUM (r);
}
-static bfd_boolean
+static bool
reg_lookup (char **s, enum reg_class class, unsigned int *regnop)
{
char *e;
return reg >= 0;
}
-static bfd_boolean
+static bool
arg_lookup (char **s, const char *const *array, size_t size, unsigned *regnop)
{
const char *p = strchr (*s, ',');
size_t i, len = p ? (size_t)(p - *s) : strlen (*s);
if (len == 0)
- return FALSE;
+ return false;
for (i = 0; i < size; i++)
if (array[i] != NULL && strncmp (array[i], *s, len) == 0)
{
*regnop = i;
*s += len;
- return TRUE;
+ return true;
}
- return FALSE;
+ return false;
}
+#define USE_BITS(mask,shift) (used_bits |= ((insn_t)(mask) << (shift)))
+
/* For consistency checking, verify that all bits are specified either
by the match/mask part of the instruction definition, or by the
operand list. The `length` could be 0, 4 or 8, 0 for auto detection. */
-static bfd_boolean
+static bool
validate_riscv_insn (const struct riscv_opcode *opc, int length)
{
- const char *p = opc->args;
- char c;
+ const char *oparg, *opargStart;
insn_t used_bits = opc->mask;
int insn_width;
insn_t required_bits;
{
as_bad (_("internal: bad RISC-V opcode (mask error): %s %s"),
opc->name, opc->args);
- return FALSE;
+ return false;
}
-#define USE_BITS(mask,shift) (used_bits |= ((insn_t)(mask) << (shift)))
- while (*p)
- switch (c = *p++)
- {
- case 'C': /* RVC */
- switch (c = *p++)
- {
- case 'U': break; /* CRS1, constrained to equal RD. */
- case 'c': break; /* CRS1, constrained to equal sp. */
- case 'T': /* CRS2, floating point. */
- case 'V': USE_BITS (OP_MASK_CRS2, OP_SH_CRS2); break;
- case 'S': /* CRS1S, floating point. */
- case 's': USE_BITS (OP_MASK_CRS1S, OP_SH_CRS1S); break;
- case 'w': break; /* CRS1S, constrained to equal RD. */
- case 'D': /* CRS2S, floating point. */
- case 't': USE_BITS (OP_MASK_CRS2S, OP_SH_CRS2S); break;
- case 'x': break; /* CRS2S, constrained to equal RD. */
- case 'z': break; /* CRS2S, constrained to be x0. */
- case '>': /* CITYPE immediate, compressed shift. */
- case 'u': /* CITYPE immediate, compressed lui. */
- case 'v': /* CITYPE immediate, li to compressed lui. */
- case 'o': /* CITYPE immediate, allow zero. */
- case 'j': used_bits |= ENCODE_CITYPE_IMM (-1U); break;
- case 'L': used_bits |= ENCODE_CITYPE_ADDI16SP_IMM (-1U); break;
- case 'm': used_bits |= ENCODE_CITYPE_LWSP_IMM (-1U); break;
- case 'n': used_bits |= ENCODE_CITYPE_LDSP_IMM (-1U); break;
- case '6': used_bits |= ENCODE_CSSTYPE_IMM (-1U); break;
- case 'M': used_bits |= ENCODE_CSSTYPE_SWSP_IMM (-1U); break;
- case 'N': used_bits |= ENCODE_CSSTYPE_SDSP_IMM (-1U); break;
- case '8': used_bits |= ENCODE_CIWTYPE_IMM (-1U); break;
- case 'K': used_bits |= ENCODE_CIWTYPE_ADDI4SPN_IMM (-1U); break;
- /* CLTYPE and CSTYPE have the same immediate encoding. */
- case '5': used_bits |= ENCODE_CLTYPE_IMM (-1U); break;
- case 'k': used_bits |= ENCODE_CLTYPE_LW_IMM (-1U); break;
- case 'l': used_bits |= ENCODE_CLTYPE_LD_IMM (-1U); break;
- case 'p': used_bits |= ENCODE_CBTYPE_IMM (-1U); break;
- case 'a': used_bits |= ENCODE_CJTYPE_IMM (-1U); break;
- case 'F': /* Compressed funct for .insn directive. */
- switch (c = *p++)
- {
+ for (oparg = opc->args; *oparg; ++oparg)
+ {
+ opargStart = oparg;
+ switch (*oparg)
+ {
+ case 'C': /* RVC */
+ switch (*++oparg)
+ {
+ case 'U': break; /* CRS1, constrained to equal RD. */
+ case 'c': break; /* CRS1, constrained to equal sp. */
+ case 'T': /* CRS2, floating point. */
+ case 'V': USE_BITS (OP_MASK_CRS2, OP_SH_CRS2); break;
+ case 'S': /* CRS1S, floating point. */
+ case 's': USE_BITS (OP_MASK_CRS1S, OP_SH_CRS1S); break;
+ case 'w': break; /* CRS1S, constrained to equal RD. */
+ case 'D': /* CRS2S, floating point. */
+ case 't': USE_BITS (OP_MASK_CRS2S, OP_SH_CRS2S); break;
+ case 'x': break; /* CRS2S, constrained to equal RD. */
+ case 'z': break; /* CRS2S, constrained to be x0. */
+ case '>': /* CITYPE immediate, compressed shift. */
+ case 'u': /* CITYPE immediate, compressed lui. */
+ case 'v': /* CITYPE immediate, li to compressed lui. */
+ case 'o': /* CITYPE immediate, allow zero. */
+ case 'j': used_bits |= ENCODE_CITYPE_IMM (-1U); break;
+ case 'L': used_bits |= ENCODE_CITYPE_ADDI16SP_IMM (-1U); break;
+ case 'm': used_bits |= ENCODE_CITYPE_LWSP_IMM (-1U); break;
+ case 'n': used_bits |= ENCODE_CITYPE_LDSP_IMM (-1U); break;
+ case '6': used_bits |= ENCODE_CSSTYPE_IMM (-1U); break;
+ case 'M': used_bits |= ENCODE_CSSTYPE_SWSP_IMM (-1U); break;
+ case 'N': used_bits |= ENCODE_CSSTYPE_SDSP_IMM (-1U); break;
+ case '8': used_bits |= ENCODE_CIWTYPE_IMM (-1U); break;
+ case 'K': used_bits |= ENCODE_CIWTYPE_ADDI4SPN_IMM (-1U); break;
+ /* CLTYPE and CSTYPE have the same immediate encoding. */
+ case '5': used_bits |= ENCODE_CLTYPE_IMM (-1U); break;
+ case 'k': used_bits |= ENCODE_CLTYPE_LW_IMM (-1U); break;
+ case 'l': used_bits |= ENCODE_CLTYPE_LD_IMM (-1U); break;
+ case 'p': used_bits |= ENCODE_CBTYPE_IMM (-1U); break;
+ case 'a': used_bits |= ENCODE_CJTYPE_IMM (-1U); break;
+ case 'F': /* Compressed funct for .insn directive. */
+ switch (*++oparg)
+ {
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);
- return FALSE;
- }
- 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. */
- case 'B': break; /* Macro operand, must be symbol or constant. */
- case 'I': break; /* Macro operand, must be constant. */
- case 'D': /* RD, floating point. */
- case 'd': USE_BITS (OP_MASK_RD, OP_SH_RD); break;
- case 'Z': /* RS1, CSR number. */
- case 'S': /* RS1, floating point. */
- case 's': USE_BITS (OP_MASK_RS1, OP_SH_RS1); break;
- case 'U': /* RS1 and RS2 are the same, floating point. */
- USE_BITS (OP_MASK_RS1, OP_SH_RS1);
- /* Fall through. */
- case 'T': /* RS2, floating point. */
- case 't': USE_BITS (OP_MASK_RS2, OP_SH_RS2); break;
- case 'R': /* RS3, floating point. */
- case 'r': USE_BITS (OP_MASK_RS3, OP_SH_RS3); break;
- case 'm': USE_BITS (OP_MASK_RM, OP_SH_RM); break;
- case 'E': USE_BITS (OP_MASK_CSR, OP_SH_CSR); 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': /* ITYPE immediate, load displacement. */
- case 'j': used_bits |= ENCODE_ITYPE_IMM (-1U); break;
- case 'a': used_bits |= ENCODE_JTYPE_IMM (-1U); break;
- case 'p': used_bits |= ENCODE_BTYPE_IMM (-1U); break;
- case 'q': used_bits |= ENCODE_STYPE_IMM (-1U); break;
- case 'u': used_bits |= ENCODE_UTYPE_IMM (-1U); break;
- case 'z': break; /* Zero immediate. */
- case '[': break; /* Unused operand. */
- case ']': break; /* Unused operand. */
- case '0': break; /* AMO displacement, must to zero. */
- case '1': break; /* Relaxation operand. */
- case 'F': /* Funct for .insn directive. */
- 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;
+ goto unknown_validate_operand;
+ }
+ 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 for .insn directive. */
- 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;
+ goto unknown_validate_operand;
+ }
+ break; /* end RVC */
+ case 'V': /* RVV */
+ switch (*++oparg)
+ {
+ case 'd':
+ case 'f': USE_BITS (OP_MASK_VD, OP_SH_VD); break;
+ case 'e': USE_BITS (OP_MASK_VWD, OP_SH_VWD); break;
+ case 's': USE_BITS (OP_MASK_VS1, OP_SH_VS1); break;
+ case 't': USE_BITS (OP_MASK_VS2, OP_SH_VS2); break;
+ case 'u': USE_BITS (OP_MASK_VS1, OP_SH_VS1);
+ USE_BITS (OP_MASK_VS2, OP_SH_VS2); break;
+ case 'v': USE_BITS (OP_MASK_VD, OP_SH_VD);
+ USE_BITS (OP_MASK_VS1, OP_SH_VS1);
+ USE_BITS (OP_MASK_VS2, OP_SH_VS2); break;
+ case '0': break;
+ case 'b': used_bits |= ENCODE_RVV_VB_IMM (-1U); break;
+ case 'c': used_bits |= ENCODE_RVV_VC_IMM (-1U); break;
+ case 'i':
+ case 'j':
+ case 'k': USE_BITS (OP_MASK_VIMM, OP_SH_VIMM); break;
+ case 'm': USE_BITS (OP_MASK_VMASK, OP_SH_VMASK); 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"),
- c, opc->name, opc->args);
- return FALSE;
- }
-#undef USE_BITS
+ goto unknown_validate_operand;
+ }
+ break; /* end RVV */
+ 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. */
+ case 'B': break; /* Macro operand, must be symbol or constant. */
+ case 'I': break; /* Macro operand, must be constant. */
+ case 'D': /* RD, floating point. */
+ case 'd': USE_BITS (OP_MASK_RD, OP_SH_RD); break;
+ case 'y': USE_BITS (OP_MASK_BS, OP_SH_BS); break;
+ case 'Y': USE_BITS (OP_MASK_RNUM, OP_SH_RNUM); break;
+ case 'Z': /* RS1, CSR number. */
+ case 'S': /* RS1, floating point. */
+ case 's': USE_BITS (OP_MASK_RS1, OP_SH_RS1); break;
+ case 'U': /* RS1 and RS2 are the same, floating point. */
+ USE_BITS (OP_MASK_RS1, OP_SH_RS1);
+ /* Fall through. */
+ case 'T': /* RS2, floating point. */
+ case 't': USE_BITS (OP_MASK_RS2, OP_SH_RS2); break;
+ case 'R': /* RS3, floating point. */
+ case 'r': USE_BITS (OP_MASK_RS3, OP_SH_RS3); break;
+ case 'm': USE_BITS (OP_MASK_RM, OP_SH_RM); break;
+ case 'E': USE_BITS (OP_MASK_CSR, OP_SH_CSR); 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': /* ITYPE immediate, load displacement. */
+ case 'j': used_bits |= ENCODE_ITYPE_IMM (-1U); break;
+ case 'a': used_bits |= ENCODE_JTYPE_IMM (-1U); break;
+ case 'p': used_bits |= ENCODE_BTYPE_IMM (-1U); break;
+ case 'f': /* Fall through. */
+ case 'q': used_bits |= ENCODE_STYPE_IMM (-1U); break;
+ case 'u': used_bits |= ENCODE_UTYPE_IMM (-1U); break;
+ case 'z': break; /* Zero immediate. */
+ case '[': break; /* Unused operand. */
+ case ']': break; /* Unused operand. */
+ case '0': break; /* AMO displacement, must to zero. */
+ case '1': break; /* Relaxation operand. */
+ case 'F': /* Funct for .insn directive. */
+ switch (*++oparg)
+ {
+ 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:
+ goto unknown_validate_operand;
+ }
+ break;
+ case 'O': /* Opcode for .insn directive. */
+ switch (*++oparg)
+ {
+ case '4': USE_BITS (OP_MASK_OP, OP_SH_OP); break;
+ case '2': USE_BITS (OP_MASK_OP2, OP_SH_OP2); break;
+ default:
+ goto unknown_validate_operand;
+ }
+ break;
+ default:
+ unknown_validate_operand:
+ as_bad (_("internal: bad RISC-V opcode "
+ "(unknown operand type `%s'): %s %s"),
+ opargStart, opc->name, opc->args);
+ return false;
+ }
+ }
+
if (used_bits != required_bits)
{
as_bad (_("internal: bad RISC-V opcode "
"(bits 0x%lx undefined): %s %s"),
~(unsigned long)(used_bits & required_bits),
opc->name, opc->args);
- return FALSE;
+ return false;
}
- return TRUE;
+ return true;
}
+#undef USE_BITS
+
struct percent_op_match
{
const char *str;
static htab_t
init_opcode_hash (const struct riscv_opcode *opcodes,
- bfd_boolean insn_directive_p)
+ bool insn_directive_p)
{
int i = 0;
int length;
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);
+ op_hash = init_opcode_hash (riscv_opcodes, false);
+ insn_type_hash = init_opcode_hash (riscv_insn_types, true);
reg_names_hash = str_htab_create ();
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);
+ hash_reg_names (RCLASS_VECR, riscv_vecr_names_numeric, NVECR);
+ hash_reg_names (RCLASS_VECM, riscv_vecm_names_numeric, NVECM);
/* Add "fp" as an alias for "s0". */
hash_reg_name (RCLASS_GPR, "fp", 8);
{
howto = bfd_reloc_type_lookup (stdoutput, reloc_type);
if (howto == NULL)
- as_bad (_("internal: usupported RISC-V relocation number %d"),
+ as_bad (_("internal: unsupported RISC-V relocation number %d"),
reloc_type);
ip->fixp = fix_new_exp (ip->frag, ip->where,
bfd_get_reloc_size (howto),
- address_expr, FALSE, reloc_type);
+ address_expr, false, reloc_type);
ip->fixp->fx_tcbit = riscv_opts.relax;
}
struct riscv_cl_insn insn;
bfd_reloc_code_real_type r;
va_list args;
+ const char *fmtStart;
va_start (args, fmt);
gas_assert (strcmp (name, mo->name) == 0);
create_insn (&insn, mo);
- for (;;)
+ for (;; ++fmt)
{
- switch (*fmt++)
+ fmtStart = fmt;
+ switch (*fmt)
{
+ case 'V': /* RVV */
+ switch (*++fmt)
+ {
+ case 'd':
+ INSERT_OPERAND (VD, insn, va_arg (args, int));
+ continue;
+ case 's':
+ INSERT_OPERAND (VS1, insn, va_arg (args, int));
+ continue;
+ case 't':
+ INSERT_OPERAND (VS2, insn, va_arg (args, int));
+ continue;
+ case 'm':
+ {
+ int reg = va_arg (args, int);
+ if (reg == -1)
+ {
+ INSERT_OPERAND (VMASK, insn, 1);
+ continue;
+ }
+ else if (reg == 0)
+ {
+ INSERT_OPERAND (VMASK, insn, 0);
+ continue;
+ }
+ else
+ goto unknown_macro_argument;
+ }
+ default:
+ goto unknown_macro_argument;
+ }
+ break;
+
case 'd':
INSERT_OPERAND (RD, insn, va_arg (args, int));
continue;
-
case 's':
INSERT_OPERAND (RS1, insn, va_arg (args, int));
continue;
-
case 't':
INSERT_OPERAND (RS2, insn, va_arg (args, int));
continue;
case ',':
continue;
default:
- as_fatal (_("internal: invalid macro"));
+ unknown_macro_argument:
+ as_fatal (_("internal: invalid macro argument `%s'"), fmtStart);
}
break;
}
static void
check_absolute_expr (struct riscv_cl_insn *ip, expressionS *ex,
- bfd_boolean maybe_csr)
+ bool maybe_csr)
{
if (ex->X_op == O_big)
as_bad (_("unsupported large constant"));
md_assemblef ("slli x%d, x%d, 0x%x", reg, reg, shift);
if (lower.X_add_number != 0)
- md_assemblef ("addi x%d, x%d, %" BFD_VMA_FMT "d", reg, reg,
- lower.X_add_number);
+ md_assemblef ("addi x%d, x%d, %" PRId64, reg, reg,
+ (int64_t) lower.X_add_number);
}
else
{
/* 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);
+ md_assemblef ("lui x%d, 0x%" PRIx64, reg, (uint64_t) upper_imm);
hi_reg = reg;
}
if (lower.X_add_number != 0 || hi_reg == 0)
- md_assemblef ("%s x%d, x%d, %" BFD_VMA_FMT "d", ADD32_INSN, reg, hi_reg,
- lower.X_add_number);
+ md_assemblef ("%s x%d, x%d, %" PRId64, ADD32_INSN, reg, hi_reg,
+ (int64_t) lower.X_add_number);
}
}
/* Zero extend and sign extend byte/half-word/word. */
static void
-riscv_ext (int destreg, int srcreg, unsigned shift, bfd_boolean sign)
+riscv_ext (int destreg, int srcreg, unsigned shift, bool sign)
{
if (sign)
{
}
}
+/* Expand RISC-V Vector macros into one or more instructions. */
+
+static void
+vector_macro (struct riscv_cl_insn *ip)
+{
+ int vd = (ip->insn_opcode >> OP_SH_VD) & OP_MASK_VD;
+ int vs1 = (ip->insn_opcode >> OP_SH_VS1) & OP_MASK_VS1;
+ int vs2 = (ip->insn_opcode >> OP_SH_VS2) & OP_MASK_VS2;
+ int vm = (ip->insn_opcode >> OP_SH_VMASK) & OP_MASK_VMASK;
+ int vtemp = (ip->insn_opcode >> OP_SH_VFUNCT6) & OP_MASK_VFUNCT6;
+ int mask = ip->insn_mo->mask;
+
+ switch (mask)
+ {
+ case M_VMSGE:
+ if (vm)
+ {
+ /* Unmasked. */
+ macro_build (NULL, "vmslt.vx", "Vd,Vt,sVm", vd, vs2, vs1, -1);
+ macro_build (NULL, "vmnand.mm", "Vd,Vt,Vs", vd, vd, vd);
+ break;
+ }
+ if (vtemp != 0)
+ {
+ /* Masked. Have vtemp to avoid overlap constraints. */
+ if (vd == vm)
+ {
+ macro_build (NULL, "vmslt.vx", "Vd,Vt,s", vtemp, vs2, vs1);
+ macro_build (NULL, "vmandnot.mm", "Vd,Vt,Vs", vd, vm, vtemp);
+ }
+ else
+ {
+ /* Preserve the value of vd if not updating by vm. */
+ macro_build (NULL, "vmslt.vx", "Vd,Vt,s", vtemp, vs2, vs1);
+ macro_build (NULL, "vmandnot.mm", "Vd,Vt,Vs", vtemp, vm, vtemp);
+ macro_build (NULL, "vmandnot.mm", "Vd,Vt,Vs", vd, vd, vm);
+ macro_build (NULL, "vmor.mm", "Vd,Vt,Vs", vd, vtemp, vd);
+ }
+ }
+ else if (vd != vm)
+ {
+ /* Masked. This may cause the vd overlaps vs2, when LMUL > 1. */
+ macro_build (NULL, "vmslt.vx", "Vd,Vt,sVm", vd, vs2, vs1, vm);
+ macro_build (NULL, "vmxor.mm", "Vd,Vt,Vs", vd, vd, vm);
+ }
+ else
+ as_bad (_("must provide temp if destination overlaps mask"));
+ break;
+
+ case M_VMSGEU:
+ if (vm)
+ {
+ /* Unmasked. */
+ macro_build (NULL, "vmsltu.vx", "Vd,Vt,sVm", vd, vs2, vs1, -1);
+ macro_build (NULL, "vmnand.mm", "Vd,Vt,Vs", vd, vd, vd);
+ break;
+ }
+ if (vtemp != 0)
+ {
+ /* Masked. Have vtemp to avoid overlap constraints. */
+ if (vd == vm)
+ {
+ macro_build (NULL, "vmsltu.vx", "Vd,Vt,s", vtemp, vs2, vs1);
+ macro_build (NULL, "vmandnot.mm", "Vd,Vt,Vs", vd, vm, vtemp);
+ }
+ else
+ {
+ /* Preserve the value of vd if not updating by vm. */
+ macro_build (NULL, "vmsltu.vx", "Vd,Vt,s", vtemp, vs2, vs1);
+ macro_build (NULL, "vmandnot.mm", "Vd,Vt,Vs", vtemp, vm, vtemp);
+ macro_build (NULL, "vmandnot.mm", "Vd,Vt,Vs", vd, vd, vm);
+ macro_build (NULL, "vmor.mm", "Vd,Vt,Vs", vd, vtemp, vd);
+ }
+ }
+ else if (vd != vm)
+ {
+ /* Masked. This may cause the vd overlaps vs2, when LMUL > 1. */
+ macro_build (NULL, "vmsltu.vx", "Vd,Vt,sVm", vd, vs2, vs1, vm);
+ macro_build (NULL, "vmxor.mm", "Vd,Vt,Vs", vd, vd, vm);
+ }
+ else
+ as_bad (_("must provide temp if destination overlaps mask"));
+ break;
+
+ default:
+ break;
+ }
+}
+
/* Expand RISC-V assembly macros into one or more instructions. */
static void
break;
case M_ZEXTH:
- riscv_ext (rd, rs1, xlen - 16, FALSE);
+ riscv_ext (rd, rs1, xlen - 16, false);
break;
case M_ZEXTW:
- riscv_ext (rd, rs1, xlen - 32, FALSE);
+ riscv_ext (rd, rs1, xlen - 32, false);
break;
case M_SEXTB:
- riscv_ext (rd, rs1, xlen - 8, TRUE);
+ riscv_ext (rd, rs1, xlen - 8, true);
break;
case M_SEXTH:
- riscv_ext (rd, rs1, xlen - 16, TRUE);
+ riscv_ext (rd, rs1, xlen - 16, true);
+ break;
+
+ case M_VMSGE:
+ case M_VMSGEU:
+ vector_macro (ip);
+ break;
+
+ case M_FLH:
+ pcrel_load (rd, rs1, imm_expr, "flh",
+ BFD_RELOC_RISCV_PCREL_HI20, BFD_RELOC_RISCV_PCREL_LO12_I);
+ break;
+ case M_FSH:
+ pcrel_store (rs2, rs1, imm_expr, "fsh",
+ BFD_RELOC_RISCV_PCREL_HI20, BFD_RELOC_RISCV_PCREL_LO12_S);
break;
default:
move *STR over the operator and store its relocation code in *RELOC.
Leave both *STR and *RELOC alone when returning false. */
-static bfd_boolean
+static bool
parse_relocation (char **str, bfd_reloc_code_real_type *reloc,
const struct percent_op_match *percent_op)
{
"current ABI", percent_op->str);
*reloc = BFD_RELOC_UNUSED;
}
- return TRUE;
+ return true;
}
- return FALSE;
+ return false;
}
static void
return my_getSmallExpression (ep, reloc, str, percent_op);
}
+/* Parse string STR as a vsetvli operand. Store the expression in *EP.
+ On exit, EXPR_END points to the first character after the expression. */
+
+static void
+my_getVsetvliExpression (expressionS *ep, char *str)
+{
+ unsigned int vsew_value = 0, vlmul_value = 0;
+ unsigned int vta_value = 0, vma_value = 0;
+ bfd_boolean vsew_found = FALSE, vlmul_found = FALSE;
+ bfd_boolean vta_found = FALSE, vma_found = FALSE;
+
+ if (arg_lookup (&str, riscv_vsew, ARRAY_SIZE (riscv_vsew), &vsew_value))
+ {
+ if (*str == ',')
+ ++str;
+ if (vsew_found)
+ as_bad (_("multiple vsew constants"));
+ vsew_found = TRUE;
+ }
+ if (arg_lookup (&str, riscv_vlmul, ARRAY_SIZE (riscv_vlmul), &vlmul_value))
+ {
+ if (*str == ',')
+ ++str;
+ if (vlmul_found)
+ as_bad (_("multiple vlmul constants"));
+ vlmul_found = TRUE;
+ }
+ if (arg_lookup (&str, riscv_vta, ARRAY_SIZE (riscv_vta), &vta_value))
+ {
+ if (*str == ',')
+ ++str;
+ if (vta_found)
+ as_bad (_("multiple vta constants"));
+ vta_found = TRUE;
+ }
+ if (arg_lookup (&str, riscv_vma, ARRAY_SIZE (riscv_vma), &vma_value))
+ {
+ if (*str == ',')
+ ++str;
+ if (vma_found)
+ as_bad (_("multiple vma constants"));
+ vma_found = TRUE;
+ }
+
+ if (vsew_found || vlmul_found || vta_found || vma_found)
+ {
+ ep->X_op = O_constant;
+ ep->X_add_number = (vlmul_value << OP_SH_VLMUL)
+ | (vsew_value << OP_SH_VSEW)
+ | (vta_value << OP_SH_VTA)
+ | (vma_value << OP_SH_VMA);
+ expr_end = str;
+ }
+ else
+ {
+ my_getExpression (ep, str);
+ str = expr_end;
+ }
+}
+
/* Detect and handle implicitly zero load-store offsets. For example,
- "lw t0, (t1)" is shorthand for "lw t0, 0(t1)". Return TRUE iff such
+ "lw t0, (t1)" is shorthand for "lw t0, 0(t1)". Return true if such
an implicit offset was detected. */
-static bfd_boolean
+static bool
riscv_handle_implicit_zero_offset (expressionS *ep, const char *s)
{
/* Check whether there is only a single bracketed expression left.
{
ep->X_op = O_constant;
ep->X_add_number = 0;
- return TRUE;
+ return true;
}
- return FALSE;
+ return false;
}
/* All RISC-V CSR instructions belong to one of these classes. */
CSR when RS1 isn't zero. The CSR is read only if the [11:10] bits of
CSR address is 0x3. */
-static bfd_boolean
+static bool
riscv_csr_read_only_check (insn_t insn)
{
int csr = (insn & (OP_MASK_CSR << OP_SH_CSR)) >> OP_SH_CSR;
|| csr_insn == INSN_CSRRC)
&& rs1 != 0)
|| csr_insn == INSN_CSRRW))
- return FALSE;
+ return false;
- return TRUE;
+ return true;
}
-/* Return True if it is a privileged instruction. Otherwise, return FALSE.
+/* Return true if it is a privileged instruction. Otherwise, return false.
uret is actually a N-ext instruction. So it is better to regard it as
an user instruction rather than the priv instruction.
dret is defined in the debug spec, so it should be checked in the future,
too. */
-static bfd_boolean
+static bool
riscv_is_priv_insn (insn_t insn)
{
return (((insn ^ MATCH_SRET) & MASK_SRET) == 0
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. */
-static const char *
+static struct riscv_ip_error
riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr,
bfd_reloc_code_real_type *imm_reloc, htab_t hash)
{
- char *s;
- const char *args;
- char c = 0;
+ /* The operand string defined in the riscv_opcodes. */
+ const char *oparg, *opargStart;
+ /* The parsed operands from assembly. */
+ char *asarg, *asargStart;
+ char save_c = 0;
struct riscv_opcode *insn;
- char *argsStart;
unsigned int regno;
- char save_c = 0;
- int argnum;
const struct percent_op_match *p;
- const char *error = "unrecognized opcode";
+ struct riscv_ip_error error;
+ error.msg = "unrecognized opcode";
+ error.statement = str;
+ error.missing_ext = NULL;
/* Indicate we are assembling instruction with CSR. */
- bfd_boolean insn_with_csr = FALSE;
+ bool insn_with_csr = false;
/* Parse the name of the instruction. Terminate the string if whitespace
is found so that str_hash_find only sees the name part of the string. */
- for (s = str; *s != '\0'; ++s)
- if (ISSPACE (*s))
+ for (asarg = str; *asarg!= '\0'; ++asarg)
+ if (ISSPACE (*asarg))
{
- save_c = *s;
- *s++ = '\0';
+ save_c = *asarg;
+ *asarg++ = '\0';
break;
}
insn = (struct riscv_opcode *) str_hash_find (hash, str);
- argsStart = s;
+ asargStart = asarg;
for ( ; insn && insn->name && strcmp (insn->name, str) == 0; insn++)
{
if ((insn->xlen_requirement != 0) && (xlen != insn->xlen_requirement))
continue;
- if (!riscv_multi_subset_supports (insn->insn_class))
- continue;
+ if (!riscv_multi_subset_supports (&riscv_rps_as, insn->insn_class))
+ {
+ error.missing_ext = riscv_multi_subset_supports_ext (&riscv_rps_as,
+ insn->insn_class);
+ continue;
+ }
+ /* Reset error message of the previous round. */
+ error.msg = _("illegal operands");
+ error.missing_ext = NULL;
create_insn (ip, insn);
- argnum = 1;
imm_expr->X_op = O_absent;
*imm_reloc = BFD_RELOC_UNUSED;
p = percent_op_itype;
- for (args = insn->args;; ++args)
+ for (oparg = insn->args;; ++oparg)
{
- s += strspn (s, " \t");
- switch (*args)
+ opargStart = oparg;
+ asarg += strspn (asarg, " \t");
+ switch (*oparg)
{
case '\0': /* End of args. */
if (insn->pinfo != INSN_MACRO)
break;
if (riscv_is_priv_insn (ip->insn_opcode))
- explicit_priv_attr = TRUE;
+ explicit_priv_attr = true;
/* Check if we write a read-only CSR by the CSR
instruction. */
/* Restore the character in advance, since we want to
report the detailed warning message here. */
if (save_c)
- *(argsStart - 1) = save_c;
+ *(asargStart - 1) = save_c;
as_warn (_("read-only CSR is written `%s'"), str);
- insn_with_csr = FALSE;
+ insn_with_csr = false;
+ }
+
+ /* The (segmant) load and store with EEW 64 cannot be used
+ when zve32x is enabled. */
+ if (ip->insn_mo->pinfo & INSN_V_EEW64
+ && riscv_subset_supports (&riscv_rps_as, "zve32x")
+ && !riscv_subset_supports (&riscv_rps_as, "zve64x"))
+ {
+ error.msg = _("illegal opcode for zve32x");
+ break;
}
}
- if (*s != '\0')
+ if (*asarg != '\0')
break;
/* Successful assembly. */
- error = NULL;
- insn_with_csr = FALSE;
+ error.msg = NULL;
+ insn_with_csr = false;
goto out;
case 'C': /* RVC */
- switch (*++args)
+ switch (*++oparg)
{
case 's': /* RS1 x8-x15. */
- if (!reg_lookup (&s, RCLASS_GPR, ®no)
+ if (!reg_lookup (&asarg, RCLASS_GPR, ®no)
|| !(regno >= 8 && regno <= 15))
break;
INSERT_OPERAND (CRS1S, *ip, regno % 8);
continue;
case 'w': /* RS1 x8-x15, constrained to equal RD x8-x15. */
- if (!reg_lookup (&s, RCLASS_GPR, ®no)
+ if (!reg_lookup (&asarg, RCLASS_GPR, ®no)
|| EXTRACT_OPERAND (CRS1S, ip->insn_opcode) + 8 != regno)
break;
continue;
case 't': /* RS2 x8-x15. */
- if (!reg_lookup (&s, RCLASS_GPR, ®no)
+ if (!reg_lookup (&asarg, RCLASS_GPR, ®no)
|| !(regno >= 8 && regno <= 15))
break;
INSERT_OPERAND (CRS2S, *ip, regno % 8);
continue;
case 'x': /* RS2 x8-x15, constrained to equal RD x8-x15. */
- if (!reg_lookup (&s, RCLASS_GPR, ®no)
+ if (!reg_lookup (&asarg, RCLASS_GPR, ®no)
|| EXTRACT_OPERAND (CRS2S, ip->insn_opcode) + 8 != regno)
break;
continue;
case 'U': /* RS1, constrained to equal RD. */
- if (!reg_lookup (&s, RCLASS_GPR, ®no)
+ if (!reg_lookup (&asarg, RCLASS_GPR, ®no)
|| EXTRACT_OPERAND (RD, ip->insn_opcode) != regno)
break;
continue;
case 'V': /* RS2 */
- if (!reg_lookup (&s, RCLASS_GPR, ®no))
+ if (!reg_lookup (&asarg, RCLASS_GPR, ®no))
break;
INSERT_OPERAND (CRS2, *ip, regno);
continue;
case 'c': /* RS1, constrained to equal sp. */
- if (!reg_lookup (&s, RCLASS_GPR, ®no)
+ if (!reg_lookup (&asarg, RCLASS_GPR, ®no)
|| regno != X_SP)
break;
continue;
case 'z': /* RS2, constrained to equal x0. */
- if (!reg_lookup (&s, RCLASS_GPR, ®no)
+ if (!reg_lookup (&asarg, RCLASS_GPR, ®no)
|| regno != 0)
break;
continue;
- case '>':
- if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
+ case '>': /* Shift amount, 0 - (XLEN-1). */
+ if (my_getSmallExpression (imm_expr, imm_reloc, asarg, p)
|| imm_expr->X_op != O_constant
- || imm_expr->X_add_number <= 0
- || imm_expr->X_add_number >= 64)
+ || (unsigned long) imm_expr->X_add_number >= xlen)
break;
ip->insn_opcode |= ENCODE_CITYPE_IMM (imm_expr->X_add_number);
rvc_imm_done:
- s = expr_end;
+ asarg = expr_end;
imm_expr->X_op = O_absent;
continue;
case '5':
- if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
+ if (my_getSmallExpression (imm_expr, imm_reloc, asarg, p)
|| imm_expr->X_op != O_constant
|| imm_expr->X_add_number < 0
|| imm_expr->X_add_number >= 32
|| !VALID_CLTYPE_IMM ((valueT) imm_expr->X_add_number))
break;
- ip->insn_opcode |= ENCODE_CLTYPE_IMM (imm_expr->X_add_number);
+ ip->insn_opcode |= ENCODE_CLTYPE_IMM (imm_expr->X_add_number);
goto rvc_imm_done;
case '6':
- if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
+ if (my_getSmallExpression (imm_expr, imm_reloc, asarg, p)
|| imm_expr->X_op != O_constant
|| imm_expr->X_add_number < 0
|| imm_expr->X_add_number >= 64
|| !VALID_CSSTYPE_IMM ((valueT) imm_expr->X_add_number))
break;
- ip->insn_opcode |= ENCODE_CSSTYPE_IMM (imm_expr->X_add_number);
+ ip->insn_opcode |= ENCODE_CSSTYPE_IMM (imm_expr->X_add_number);
goto rvc_imm_done;
case '8':
- if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
+ if (my_getSmallExpression (imm_expr, imm_reloc, asarg, p)
|| imm_expr->X_op != O_constant
|| imm_expr->X_add_number < 0
|| imm_expr->X_add_number >= 256
|| !VALID_CIWTYPE_IMM ((valueT) imm_expr->X_add_number))
break;
- ip->insn_opcode |= ENCODE_CIWTYPE_IMM (imm_expr->X_add_number);
+ ip->insn_opcode |= ENCODE_CIWTYPE_IMM (imm_expr->X_add_number);
goto rvc_imm_done;
case 'j':
- if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
+ if (my_getSmallExpression (imm_expr, imm_reloc, asarg, p)
|| imm_expr->X_op != O_constant
|| imm_expr->X_add_number == 0
|| !VALID_CITYPE_IMM ((valueT) imm_expr->X_add_number))
ip->insn_opcode |= ENCODE_CITYPE_IMM (imm_expr->X_add_number);
goto rvc_imm_done;
case 'k':
- if (riscv_handle_implicit_zero_offset (imm_expr, s))
+ if (riscv_handle_implicit_zero_offset (imm_expr, asarg))
continue;
- if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
+ if (my_getSmallExpression (imm_expr, imm_reloc, asarg, p)
|| imm_expr->X_op != O_constant
|| !VALID_CLTYPE_LW_IMM ((valueT) imm_expr->X_add_number))
break;
ip->insn_opcode |= ENCODE_CLTYPE_LW_IMM (imm_expr->X_add_number);
goto rvc_imm_done;
case 'l':
- if (riscv_handle_implicit_zero_offset (imm_expr, s))
+ if (riscv_handle_implicit_zero_offset (imm_expr, asarg))
continue;
- if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
+ if (my_getSmallExpression (imm_expr, imm_reloc, asarg, p)
|| imm_expr->X_op != O_constant
|| !VALID_CLTYPE_LD_IMM ((valueT) imm_expr->X_add_number))
break;
ip->insn_opcode |= ENCODE_CLTYPE_LD_IMM (imm_expr->X_add_number);
goto rvc_imm_done;
case 'm':
- if (riscv_handle_implicit_zero_offset (imm_expr, s))
+ if (riscv_handle_implicit_zero_offset (imm_expr, asarg))
continue;
- if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
+ if (my_getSmallExpression (imm_expr, imm_reloc, asarg, p)
|| imm_expr->X_op != O_constant
|| !VALID_CITYPE_LWSP_IMM ((valueT) imm_expr->X_add_number))
break;
ENCODE_CITYPE_LWSP_IMM (imm_expr->X_add_number);
goto rvc_imm_done;
case 'n':
- if (riscv_handle_implicit_zero_offset (imm_expr, s))
+ if (riscv_handle_implicit_zero_offset (imm_expr, asarg))
continue;
- if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
+ if (my_getSmallExpression (imm_expr, imm_reloc, asarg, p)
|| imm_expr->X_op != O_constant
|| !VALID_CITYPE_LDSP_IMM ((valueT) imm_expr->X_add_number))
break;
ENCODE_CITYPE_LDSP_IMM (imm_expr->X_add_number);
goto rvc_imm_done;
case 'o':
- if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
+ if (my_getSmallExpression (imm_expr, imm_reloc, asarg, 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
ip->insn_opcode |= ENCODE_CITYPE_IMM (imm_expr->X_add_number);
goto rvc_imm_done;
case 'K':
- if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
+ if (my_getSmallExpression (imm_expr, imm_reloc, asarg, p)
|| imm_expr->X_op != O_constant
|| imm_expr->X_add_number == 0
|| !VALID_CIWTYPE_ADDI4SPN_IMM ((valueT) imm_expr->X_add_number))
ENCODE_CIWTYPE_ADDI4SPN_IMM (imm_expr->X_add_number);
goto rvc_imm_done;
case 'L':
- if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
+ if (my_getSmallExpression (imm_expr, imm_reloc, asarg, p)
|| imm_expr->X_op != O_constant
|| !VALID_CITYPE_ADDI16SP_IMM ((valueT) imm_expr->X_add_number))
break;
ENCODE_CITYPE_ADDI16SP_IMM (imm_expr->X_add_number);
goto rvc_imm_done;
case 'M':
- if (riscv_handle_implicit_zero_offset (imm_expr, s))
+ if (riscv_handle_implicit_zero_offset (imm_expr, asarg))
continue;
- if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
+ if (my_getSmallExpression (imm_expr, imm_reloc, asarg, p)
|| imm_expr->X_op != O_constant
|| !VALID_CSSTYPE_SWSP_IMM ((valueT) imm_expr->X_add_number))
break;
ENCODE_CSSTYPE_SWSP_IMM (imm_expr->X_add_number);
goto rvc_imm_done;
case 'N':
- if (riscv_handle_implicit_zero_offset (imm_expr, s))
+ if (riscv_handle_implicit_zero_offset (imm_expr, asarg))
continue;
- if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
+ if (my_getSmallExpression (imm_expr, imm_reloc, asarg, p)
|| imm_expr->X_op != O_constant
|| !VALID_CSSTYPE_SDSP_IMM ((valueT) imm_expr->X_add_number))
break;
goto rvc_imm_done;
case 'u':
p = percent_op_utype;
- if (my_getSmallExpression (imm_expr, imm_reloc, s, p))
+ if (my_getSmallExpression (imm_expr, imm_reloc, asarg, p))
break;
rvc_lui:
if (imm_expr->X_op != O_constant
ip->insn_opcode |= ENCODE_CITYPE_IMM (imm_expr->X_add_number);
goto rvc_imm_done;
case 'v':
- if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
+ if (my_getSmallExpression (imm_expr, imm_reloc, asarg, p)
|| (imm_expr->X_add_number & (RISCV_IMM_REACH - 1))
|| ((int32_t)imm_expr->X_add_number
!= imm_expr->X_add_number))
case 'a':
goto jump;
case 'S': /* Floating-point RS1 x8-x15. */
- if (!reg_lookup (&s, RCLASS_FPR, ®no)
+ if (!reg_lookup (&asarg, 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)
+ if (!reg_lookup (&asarg, RCLASS_FPR, ®no)
|| !(regno >= 8 && regno <= 15))
break;
INSERT_OPERAND (CRS2S, *ip, regno % 8);
continue;
case 'T': /* Floating-point RS2. */
- if (!reg_lookup (&s, RCLASS_FPR, ®no))
+ if (!reg_lookup (&asarg, RCLASS_FPR, ®no))
break;
INSERT_OPERAND (CRS2, *ip, regno);
continue;
case 'F':
- switch (*++args)
+ switch (*++oparg)
{
case '6':
- if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
+ if (my_getSmallExpression (imm_expr, imm_reloc, asarg, p)
|| imm_expr->X_op != O_constant
|| imm_expr->X_add_number < 0
|| imm_expr->X_add_number >= 64)
{
as_bad (_("bad value for compressed funct6 "
- "field, value must be 0...64"));
+ "field, value must be 0...63"));
break;
}
INSERT_OPERAND (CFUNCT6, *ip, imm_expr->X_add_number);
imm_expr->X_op = O_absent;
- s = expr_end;
+ asarg = expr_end;
continue;
case '4':
- if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
+ if (my_getSmallExpression (imm_expr, imm_reloc, asarg, p)
|| imm_expr->X_op != O_constant
|| imm_expr->X_add_number < 0
|| imm_expr->X_add_number >= 16)
}
INSERT_OPERAND (CFUNCT4, *ip, imm_expr->X_add_number);
imm_expr->X_op = O_absent;
- s = expr_end;
+ asarg = expr_end;
continue;
case '3':
- if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
+ if (my_getSmallExpression (imm_expr, imm_reloc, asarg, p)
|| imm_expr->X_op != O_constant
|| imm_expr->X_add_number < 0
|| imm_expr->X_add_number >= 8)
}
INSERT_OPERAND (CFUNCT3, *ip, imm_expr->X_add_number);
imm_expr->X_op = O_absent;
- s = expr_end;
+ asarg = expr_end;
continue;
case '2':
- if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
+ if (my_getSmallExpression (imm_expr, imm_reloc, asarg, p)
|| imm_expr->X_op != O_constant
|| imm_expr->X_add_number < 0
|| imm_expr->X_add_number >= 4)
}
INSERT_OPERAND (CFUNCT2, *ip, imm_expr->X_add_number);
imm_expr->X_op = O_absent;
- s = expr_end;
+ asarg = expr_end;
continue;
default:
- as_bad (_("internal: unknown compressed funct "
- "field specifier `CF%c'"), *args);
+ goto unknown_riscv_ip_operand;
}
break;
default:
- as_bad (_("internal: unknown compressed field "
- "specifier `C%c'"), *args);
+ goto unknown_riscv_ip_operand;
}
- break;
+ break; /* end RVC */
+
+ case 'V': /* RVV */
+ switch (*++oparg)
+ {
+ case 'd': /* VD */
+ if (!reg_lookup (&asarg, RCLASS_VECR, ®no))
+ break;
+ INSERT_OPERAND (VD, *ip, regno);
+ continue;
+
+ case 'e': /* AMO VD */
+ if (reg_lookup (&asarg, RCLASS_GPR, ®no) && regno == 0)
+ INSERT_OPERAND (VWD, *ip, 0);
+ else if (reg_lookup (&asarg, RCLASS_VECR, ®no))
+ {
+ INSERT_OPERAND (VWD, *ip, 1);
+ INSERT_OPERAND (VD, *ip, regno);
+ }
+ else
+ break;
+ continue;
+
+ case 'f': /* AMO VS3 */
+ if (!reg_lookup (&asarg, RCLASS_VECR, ®no))
+ break;
+ if (!EXTRACT_OPERAND (VWD, ip->insn_opcode))
+ INSERT_OPERAND (VD, *ip, regno);
+ else
+ {
+ /* VS3 must match VD. */
+ if (EXTRACT_OPERAND (VD, ip->insn_opcode) != regno)
+ break;
+ }
+ continue;
+
+ case 's': /* VS1 */
+ if (!reg_lookup (&asarg, RCLASS_VECR, ®no))
+ break;
+ INSERT_OPERAND (VS1, *ip, regno);
+ continue;
+
+ case 't': /* VS2 */
+ if (!reg_lookup (&asarg, RCLASS_VECR, ®no))
+ break;
+ INSERT_OPERAND (VS2, *ip, regno);
+ continue;
+
+ case 'u': /* VS1 == VS2 */
+ if (!reg_lookup (&asarg, RCLASS_VECR, ®no))
+ break;
+ INSERT_OPERAND (VS1, *ip, regno);
+ INSERT_OPERAND (VS2, *ip, regno);
+ continue;
+
+ case 'v': /* VD == VS1 == VS2 */
+ if (!reg_lookup (&asarg, RCLASS_VECR, ®no))
+ break;
+ INSERT_OPERAND (VD, *ip, regno);
+ INSERT_OPERAND (VS1, *ip, regno);
+ INSERT_OPERAND (VS2, *ip, regno);
+ continue;
+
+ /* The `V0` is carry-in register for v[m]adc and v[m]sbc,
+ and is used to choose vs1/rs1/frs1/imm or vs2 for
+ v[f]merge. It use the same encoding as the vector mask
+ register. */
+ case '0':
+ if (reg_lookup (&asarg, RCLASS_VECR, ®no) && regno == 0)
+ continue;
+ break;
+
+ case 'b': /* vtypei for vsetivli */
+ my_getVsetvliExpression (imm_expr, asarg);
+ check_absolute_expr (ip, imm_expr, FALSE);
+ if (!VALID_RVV_VB_IMM (imm_expr->X_add_number))
+ as_bad (_("bad value for vsetivli immediate field, "
+ "value must be 0..1023"));
+ ip->insn_opcode
+ |= ENCODE_RVV_VB_IMM (imm_expr->X_add_number);
+ imm_expr->X_op = O_absent;
+ asarg = expr_end;
+ continue;
+
+ case 'c': /* vtypei for vsetvli */
+ my_getVsetvliExpression (imm_expr, asarg);
+ check_absolute_expr (ip, imm_expr, FALSE);
+ if (!VALID_RVV_VC_IMM (imm_expr->X_add_number))
+ as_bad (_("bad value for vsetvli immediate field, "
+ "value must be 0..2047"));
+ ip->insn_opcode
+ |= ENCODE_RVV_VC_IMM (imm_expr->X_add_number);
+ imm_expr->X_op = O_absent;
+ asarg = expr_end;
+ continue;
+
+ case 'i': /* vector arith signed immediate */
+ my_getExpression (imm_expr, asarg);
+ check_absolute_expr (ip, imm_expr, FALSE);
+ if (imm_expr->X_add_number > 15
+ || imm_expr->X_add_number < -16)
+ as_bad (_("bad value for vector immediate field, "
+ "value must be -16...15"));
+ INSERT_OPERAND (VIMM, *ip, imm_expr->X_add_number);
+ imm_expr->X_op = O_absent;
+ asarg = expr_end;
+ continue;
+
+ case 'j': /* vector arith unsigned immediate */
+ my_getExpression (imm_expr, asarg);
+ check_absolute_expr (ip, imm_expr, FALSE);
+ if (imm_expr->X_add_number < 0
+ || imm_expr->X_add_number >= 32)
+ as_bad (_("bad value for vector immediate field, "
+ "value must be 0...31"));
+ INSERT_OPERAND (VIMM, *ip, imm_expr->X_add_number);
+ imm_expr->X_op = O_absent;
+ asarg = expr_end;
+ continue;
+
+ case 'k': /* vector arith signed immediate, minus 1 */
+ my_getExpression (imm_expr, asarg);
+ check_absolute_expr (ip, imm_expr, FALSE);
+ if (imm_expr->X_add_number > 16
+ || imm_expr->X_add_number < -15)
+ as_bad (_("bad value for vector immediate field, "
+ "value must be -15...16"));
+ INSERT_OPERAND (VIMM, *ip, imm_expr->X_add_number - 1);
+ imm_expr->X_op = O_absent;
+ asarg = expr_end;
+ continue;
+
+ case 'm': /* optional vector mask */
+ if (*asarg == '\0')
+ {
+ INSERT_OPERAND (VMASK, *ip, 1);
+ continue;
+ }
+ else if (*asarg == ',' && asarg++
+ && reg_lookup (&asarg, RCLASS_VECM, ®no)
+ && regno == 0)
+ {
+ INSERT_OPERAND (VMASK, *ip, 0);
+ continue;
+ }
+ break;
+
+ case 'M': /* required vector mask */
+ if (reg_lookup (&asarg, RCLASS_VECM, ®no) && regno == 0)
+ {
+ INSERT_OPERAND (VMASK, *ip, 0);
+ continue;
+ }
+ break;
+
+ case 'T': /* vector macro temporary register */
+ if (!reg_lookup (&asarg, RCLASS_VECR, ®no) || regno == 0)
+ break;
+ /* Store it in the FUNCT6 field as we don't have anyplace
+ else to store it. */
+ INSERT_OPERAND (VFUNCT6, *ip, regno);
+ continue;
+
+ default:
+ goto unknown_riscv_ip_operand;
+ }
+ break; /* end RVV */
case ',':
- ++argnum;
- if (*s++ == *args)
+ if (*asarg++ == *oparg)
continue;
- s--;
+ asarg--;
break;
case '(':
case ')':
case '[':
case ']':
- if (*s++ == *args)
+ if (*asarg++ == *oparg)
continue;
break;
case '<': /* Shift amount, 0 - 31. */
- my_getExpression (imm_expr, s);
- check_absolute_expr (ip, imm_expr, FALSE);
+ my_getExpression (imm_expr, asarg);
+ 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);
INSERT_OPERAND (SHAMTW, *ip, imm_expr->X_add_number);
imm_expr->X_op = O_absent;
- s = expr_end;
+ asarg = expr_end;
continue;
case '>': /* Shift amount, 0 - (XLEN-1). */
- my_getExpression (imm_expr, s);
- check_absolute_expr (ip, imm_expr, FALSE);
+ my_getExpression (imm_expr, asarg);
+ 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);
INSERT_OPERAND (SHAMT, *ip, imm_expr->X_add_number);
imm_expr->X_op = O_absent;
- s = expr_end;
+ asarg = expr_end;
continue;
case 'Z': /* CSRRxI immediate. */
- my_getExpression (imm_expr, s);
- check_absolute_expr (ip, imm_expr, FALSE);
+ my_getExpression (imm_expr, asarg);
+ 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);
INSERT_OPERAND (RS1, *ip, imm_expr->X_add_number);
imm_expr->X_op = O_absent;
- s = expr_end;
+ asarg = expr_end;
continue;
case 'E': /* Control register. */
- insn_with_csr = TRUE;
- explicit_priv_attr = TRUE;
- if (reg_lookup (&s, RCLASS_CSR, ®no))
+ insn_with_csr = true;
+ explicit_priv_attr = true;
+ if (reg_lookup (&asarg, RCLASS_CSR, ®no))
INSERT_OPERAND (CSR, *ip, regno);
else
{
- my_getExpression (imm_expr, s);
- check_absolute_expr (ip, imm_expr, TRUE);
+ my_getExpression (imm_expr, asarg);
+ 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);
INSERT_OPERAND (CSR, *ip, imm_expr->X_add_number);
imm_expr->X_op = O_absent;
- s = expr_end;
+ asarg = expr_end;
}
continue;
case 'm': /* Rounding mode. */
- if (arg_lookup (&s, riscv_rm, ARRAY_SIZE (riscv_rm), ®no))
+ if (arg_lookup (&asarg, riscv_rm,
+ ARRAY_SIZE (riscv_rm), ®no))
{
INSERT_OPERAND (RM, *ip, regno);
continue;
case 'P':
case 'Q': /* Fence predecessor/successor. */
- if (arg_lookup (&s, riscv_pred_succ, ARRAY_SIZE (riscv_pred_succ),
- ®no))
+ if (arg_lookup (&asarg, riscv_pred_succ,
+ ARRAY_SIZE (riscv_pred_succ), ®no))
{
- if (*args == 'P')
+ if (*oparg == 'P')
INSERT_OPERAND (PRED, *ip, regno);
else
INSERT_OPERAND (SUCC, *ip, regno);
case 's': /* Source register. */
case 't': /* Target register. */
case 'r': /* RS3 */
- if (reg_lookup (&s, RCLASS_GPR, ®no))
+ if (reg_lookup (&asarg, RCLASS_GPR, ®no))
{
- c = *args;
- if (*s == ' ')
- ++s;
+ char c = *oparg;
+ if (*asarg == ' ')
+ ++asarg;
/* Now that we have assembled one operand, we use the args
string to figure out where it goes in the instruction. */
case 'T': /* Floating point RS2. */
case 'U': /* Floating point RS1 and RS2. */
case 'R': /* Floating point RS3. */
- if (reg_lookup (&s, RCLASS_FPR, ®no))
+ if (reg_lookup (&asarg,
+ (riscv_subset_supports (&riscv_rps_as, "zfinx")
+ ? RCLASS_GPR : RCLASS_FPR), ®no))
{
- c = *args;
- if (*s == ' ')
- ++s;
+ char c = *oparg;
+ if (*asarg == ' ')
+ ++asarg;
switch (c)
{
case 'D':
break;
case 'I':
- my_getExpression (imm_expr, s);
+ my_getExpression (imm_expr, asarg);
if (imm_expr->X_op != O_big
&& imm_expr->X_op != O_constant)
break;
normalize_constant_expr (imm_expr);
- s = expr_end;
+ asarg = expr_end;
continue;
case 'A':
- my_getExpression (imm_expr, s);
+ my_getExpression (imm_expr, asarg);
normalize_constant_expr (imm_expr);
/* The 'A' format specifier must be a symbol. */
if (imm_expr->X_op != O_symbol)
break;
*imm_reloc = BFD_RELOC_32;
- s = expr_end;
+ asarg = expr_end;
continue;
case 'B':
- my_getExpression (imm_expr, s);
+ my_getExpression (imm_expr, asarg);
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;
+ asarg = expr_end;
continue;
case 'j': /* Sign-extended immediate. */
case '0': /* AMO displacement, which must be zero. */
p = percent_op_null;
load_store:
- if (riscv_handle_implicit_zero_offset (imm_expr, s))
+ if (riscv_handle_implicit_zero_offset (imm_expr, asarg))
continue;
alu_op:
/* If this value won't fit into a 16 bit offset, then go
find a macro that will generate the 32 bit offset
code pattern. */
- if (!my_getSmallExpression (imm_expr, imm_reloc, s, p))
+ if (!my_getSmallExpression (imm_expr, imm_reloc, asarg, p))
{
normalize_constant_expr (imm_expr);
if (imm_expr->X_op != O_constant
- || (*args == '0' && imm_expr->X_add_number != 0)
- || (*args == '1')
+ || (*oparg == '0' && imm_expr->X_add_number != 0)
+ || (*oparg == '1')
|| imm_expr->X_add_number >= (signed)RISCV_IMM_REACH/2
|| imm_expr->X_add_number < -(signed)RISCV_IMM_REACH/2)
break;
}
- s = expr_end;
+ asarg = expr_end;
continue;
case 'p': /* PC-relative offset. */
branch:
*imm_reloc = BFD_RELOC_12_PCREL;
- my_getExpression (imm_expr, s);
- s = expr_end;
+ my_getExpression (imm_expr, asarg);
+ asarg = expr_end;
continue;
case 'u': /* Upper 20 bits. */
p = percent_op_utype;
- if (!my_getSmallExpression (imm_expr, imm_reloc, s, p))
+ if (!my_getSmallExpression (imm_expr, imm_reloc, asarg, p))
{
if (imm_expr->X_op != O_constant)
break;
*imm_reloc = BFD_RELOC_RISCV_HI20;
imm_expr->X_add_number <<= RISCV_IMM_BITS;
}
- s = expr_end;
+ asarg = expr_end;
continue;
case 'a': /* 20-bit PC-relative offset. */
jump:
- my_getExpression (imm_expr, s);
- s = expr_end;
+ my_getExpression (imm_expr, asarg);
+ asarg = expr_end;
*imm_reloc = BFD_RELOC_RISCV_JMP;
continue;
case 'c':
- my_getExpression (imm_expr, s);
- s = expr_end;
- if (strcmp (s, "@plt") == 0)
+ my_getExpression (imm_expr, asarg);
+ asarg = expr_end;
+ if (strcmp (asarg, "@plt") == 0)
{
*imm_reloc = BFD_RELOC_RISCV_CALL_PLT;
- s += 4;
+ asarg += 4;
}
else
*imm_reloc = BFD_RELOC_RISCV_CALL;
continue;
case 'O':
- switch (*++args)
+ switch (*++oparg)
{
case '4':
- if (my_getOpcodeExpression (imm_expr, imm_reloc, s, p)
+ if (my_getOpcodeExpression (imm_expr, imm_reloc, asarg, p)
|| imm_expr->X_op != O_constant
|| imm_expr->X_add_number < 0
|| imm_expr->X_add_number >= 128
}
INSERT_OPERAND (OP, *ip, imm_expr->X_add_number);
imm_expr->X_op = O_absent;
- s = expr_end;
+ asarg = expr_end;
continue;
case '2':
- if (my_getOpcodeExpression (imm_expr, imm_reloc, s, p)
+ if (my_getOpcodeExpression (imm_expr, imm_reloc, asarg, p)
|| imm_expr->X_op != O_constant
|| imm_expr->X_add_number < 0
|| imm_expr->X_add_number >= 3)
}
INSERT_OPERAND (OP2, *ip, imm_expr->X_add_number);
imm_expr->X_op = O_absent;
- s = expr_end;
+ asarg = expr_end;
continue;
default:
- as_bad (_("internal: unknown opcode field "
- "specifier `O%c'"), *args);
+ goto unknown_riscv_ip_operand;
}
break;
case 'F':
- switch (*++args)
+ switch (*++oparg)
{
case '7':
- if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
+ if (my_getSmallExpression (imm_expr, imm_reloc, asarg, p)
|| imm_expr->X_op != O_constant
|| imm_expr->X_add_number < 0
|| imm_expr->X_add_number >= 128)
}
INSERT_OPERAND (FUNCT7, *ip, imm_expr->X_add_number);
imm_expr->X_op = O_absent;
- s = expr_end;
+ asarg = expr_end;
continue;
case '3':
- if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
+ if (my_getSmallExpression (imm_expr, imm_reloc, asarg, p)
|| imm_expr->X_op != O_constant
|| imm_expr->X_add_number < 0
|| imm_expr->X_add_number >= 8)
}
INSERT_OPERAND (FUNCT3, *ip, imm_expr->X_add_number);
imm_expr->X_op = O_absent;
- s = expr_end;
+ asarg = expr_end;
continue;
case '2':
- if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
+ if (my_getSmallExpression (imm_expr, imm_reloc, asarg, p)
|| imm_expr->X_op != O_constant
|| imm_expr->X_add_number < 0
|| imm_expr->X_add_number >= 4)
}
INSERT_OPERAND (FUNCT2, *ip, imm_expr->X_add_number);
imm_expr->X_op = O_absent;
- s = expr_end;
+ asarg = expr_end;
continue;
default:
- as_bad (_("internal: unknown funct field "
- "specifier `F%c'\n"), *args);
+ goto unknown_riscv_ip_operand;
}
break;
+ case 'y': /* bs immediate */
+ my_getExpression (imm_expr, asarg);
+ check_absolute_expr (ip, imm_expr, FALSE);
+ if ((unsigned long)imm_expr->X_add_number > 3)
+ as_bad(_("Improper bs immediate (%lu)"),
+ (unsigned long)imm_expr->X_add_number);
+ INSERT_OPERAND(BS, *ip, imm_expr->X_add_number);
+ imm_expr->X_op = O_absent;
+ asarg = expr_end;
+ continue;
+
+ case 'Y': /* rnum immediate */
+ my_getExpression (imm_expr, asarg);
+ check_absolute_expr (ip, imm_expr, FALSE);
+ if ((unsigned long)imm_expr->X_add_number > 10)
+ as_bad(_("Improper rnum immediate (%lu)"),
+ (unsigned long)imm_expr->X_add_number);
+ INSERT_OPERAND(RNUM, *ip, imm_expr->X_add_number);
+ imm_expr->X_op = O_absent;
+ asarg = expr_end;
+ continue;
+
case 'z':
- if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
+ if (my_getSmallExpression (imm_expr, imm_reloc, asarg, p)
|| imm_expr->X_op != O_constant
|| imm_expr->X_add_number != 0)
break;
- s = expr_end;
+ asarg = expr_end;
imm_expr->X_op = O_absent;
continue;
+ case 'f': /* Prefetch offset, pseudo S-type but lower 5-bits zero. */
+ if (riscv_handle_implicit_zero_offset (imm_expr, asarg))
+ continue;
+ my_getExpression (imm_expr, asarg);
+ check_absolute_expr (ip, imm_expr, false);
+ if (((unsigned) (imm_expr->X_add_number) & 0x1fU)
+ || imm_expr->X_add_number >= (signed) RISCV_IMM_REACH / 2
+ || imm_expr->X_add_number < -(signed) RISCV_IMM_REACH / 2)
+ as_bad (_("improper prefetch offset (%ld)"),
+ (long) imm_expr->X_add_number);
+ ip->insn_opcode |=
+ ENCODE_STYPE_IMM ((unsigned) (imm_expr->X_add_number) &
+ ~ 0x1fU);
+ imm_expr->X_op = O_absent;
+ asarg = expr_end;
+ continue;
+
default:
- as_fatal (_("internal: unknown argument type `%c'"), *args);
+ unknown_riscv_ip_operand:
+ as_fatal (_("internal: unknown argument type `%s'"),
+ opargStart);
}
break;
}
- s = argsStart;
- error = _("illegal operands");
- insn_with_csr = FALSE;
+ asarg = asargStart;
+ insn_with_csr = false;
}
out:
/* Restore the character we might have clobbered above. */
if (save_c)
- *(argsStart - 1) = save_c;
+ *(asargStart - 1) = save_c;
return error;
}
+/* Similar to riscv_ip, but assembles an instruction according to the
+ hardcode values of .insn directive. */
+
+static const char *
+riscv_ip_hardcode (char *str,
+ struct riscv_cl_insn *ip,
+ expressionS *imm_expr,
+ const char *error)
+{
+ struct riscv_opcode *insn;
+ insn_t values[2] = {0, 0};
+ unsigned int num = 0;
+
+ input_line_pointer = str;
+ do
+ {
+ expression (imm_expr);
+ if (imm_expr->X_op != O_constant)
+ {
+ /* The first value isn't constant, so it should be
+ .insn <type> <operands>. We have been parsed it
+ in the riscv_ip. */
+ if (num == 0)
+ return error;
+ return _("values must be constant");
+ }
+ values[num++] = (insn_t) imm_expr->X_add_number;
+ }
+ while (*input_line_pointer++ == ',' && num < 2);
+
+ input_line_pointer--;
+ if (*input_line_pointer != '\0')
+ return _("unrecognized values");
+
+ insn = XNEW (struct riscv_opcode);
+ insn->match = values[num - 1];
+ create_insn (ip, insn);
+ unsigned int bytes = riscv_insn_length (insn->match);
+ if ((bytes < sizeof(values[0]) && values[num - 1] >> (8 * bytes) != 0)
+ || (num == 2 && values[0] != bytes))
+ return _("value conflicts with instruction length");
+
+ return NULL;
+}
+
void
md_assemble (char *str)
{
before assembling. */
if (!start_assemble)
{
- start_assemble = TRUE;
+ start_assemble = true;
riscv_set_abi_by_arch ();
if (!riscv_set_default_priv_spec (NULL))
return;
}
- const char *error = riscv_ip (str, &insn, &imm_expr, &imm_reloc, op_hash);
+ riscv_mapping_state (MAP_INSN, 0);
+
+ const struct riscv_ip_error error = riscv_ip (str, &insn, &imm_expr,
+ &imm_reloc, op_hash);
- if (error)
+ if (error.msg)
{
- as_bad ("%s `%s'", error, str);
+ if (error.missing_ext)
+ as_bad ("%s `%s', extension `%s' required", error.msg,
+ error.statement, error.missing_ext);
+ else
+ as_bad ("%s `%s'", error.msg, error.statement);
return;
}
break;
case OPTION_NO_PIC:
- riscv_opts.pic = FALSE;
+ riscv_opts.pic = false;
break;
case OPTION_PIC:
- riscv_opts.pic = TRUE;
+ riscv_opts.pic = true;
break;
case OPTION_MABI:
if (strcmp (arg, "ilp32") == 0)
- riscv_set_abi (32, FLOAT_ABI_SOFT, FALSE);
+ riscv_set_abi (32, FLOAT_ABI_SOFT, false);
else if (strcmp (arg, "ilp32e") == 0)
- riscv_set_abi (32, FLOAT_ABI_SOFT, TRUE);
+ riscv_set_abi (32, FLOAT_ABI_SOFT, true);
else if (strcmp (arg, "ilp32f") == 0)
- riscv_set_abi (32, FLOAT_ABI_SINGLE, FALSE);
+ riscv_set_abi (32, FLOAT_ABI_SINGLE, false);
else if (strcmp (arg, "ilp32d") == 0)
- riscv_set_abi (32, FLOAT_ABI_DOUBLE, FALSE);
+ riscv_set_abi (32, FLOAT_ABI_DOUBLE, false);
else if (strcmp (arg, "ilp32q") == 0)
- riscv_set_abi (32, FLOAT_ABI_QUAD, FALSE);
+ riscv_set_abi (32, FLOAT_ABI_QUAD, false);
else if (strcmp (arg, "lp64") == 0)
- riscv_set_abi (64, FLOAT_ABI_SOFT, FALSE);
+ riscv_set_abi (64, FLOAT_ABI_SOFT, false);
else if (strcmp (arg, "lp64f") == 0)
- riscv_set_abi (64, FLOAT_ABI_SINGLE, FALSE);
+ riscv_set_abi (64, FLOAT_ABI_SINGLE, false);
else if (strcmp (arg, "lp64d") == 0)
- riscv_set_abi (64, FLOAT_ABI_DOUBLE, FALSE);
+ riscv_set_abi (64, FLOAT_ABI_DOUBLE, false);
else if (strcmp (arg, "lp64q") == 0)
- riscv_set_abi (64, FLOAT_ABI_QUAD, FALSE);
+ riscv_set_abi (64, FLOAT_ABI_QUAD, false);
else
return 0;
- explicit_mabi = TRUE;
+ explicit_mabi = true;
break;
case OPTION_RELAX:
- riscv_opts.relax = TRUE;
+ riscv_opts.relax = true;
break;
case OPTION_NO_RELAX:
- riscv_opts.relax = FALSE;
+ riscv_opts.relax = false;
break;
case OPTION_ARCH_ATTR:
- riscv_opts.arch_attr = TRUE;
+ riscv_opts.arch_attr = true;
break;
case OPTION_NO_ARCH_ATTR:
- riscv_opts.arch_attr = FALSE;
+ riscv_opts.arch_attr = false;
break;
case OPTION_CSR_CHECK:
- riscv_opts.csr_check = TRUE;
+ riscv_opts.csr_check = true;
break;
case OPTION_NO_CSR_CHECK:
- riscv_opts.csr_check = FALSE;
+ riscv_opts.csr_check = false;
break;
case OPTION_MISA_SPEC:
else
as_bad ("unknown default architecture `%s'", default_arch);
}
- if (default_arch_with_ext == NULL)
- default_arch_with_ext = xlen == 64 ? "rv64g" : "rv32g";
-
- /* Initialize the hash table for extensions with default version. */
- ext_version_hash = init_ext_version_hash ();
/* Set default specs. */
if (default_isa_spec == ISA_SPEC_CLASS_NONE)
riscv_set_arch (default_arch_with_ext);
- /* Add the RVC extension, regardless of -march, to support .option rvc. */
- riscv_set_rvc (FALSE);
- if (riscv_subset_supports ("c"))
- riscv_set_rvc (TRUE);
-
- /* Enable RVE if specified by the -march option. */
- riscv_set_rve (FALSE);
- if (riscv_subset_supports ("e"))
- riscv_set_rve (TRUE);
-
/* If the CIE to be produced has not been overridden on the command line,
then produce version 3 by default. This allows us to use the full
range of registers in a .cfi_return_column directive. */
{
unsigned int subtype;
bfd_byte *buf = (bfd_byte *) (fixP->fx_frag->fr_literal + fixP->fx_where);
- bfd_boolean relaxable = FALSE;
+ bool relaxable = false;
offsetT loc;
segT sub_segment;
bfd_putl32 (riscv_apply_const_reloc (fixP->fx_r_type, *valP)
| bfd_getl32 (buf), buf);
if (fixP->fx_addsy == NULL)
- fixP->fx_done = TRUE;
- relaxable = TRUE;
+ fixP->fx_done = true;
+ relaxable = true;
break;
case BFD_RELOC_RISCV_GOT_HI20:
case BFD_RELOC_RISCV_TPREL_LO12_I:
case BFD_RELOC_RISCV_TPREL_LO12_S:
case BFD_RELOC_RISCV_TPREL_ADD:
- relaxable = TRUE;
+ relaxable = true;
/* Fall through. */
case BFD_RELOC_RISCV_TLS_GOT_HI20:
case BFD_RELOC_RISCV_CALL:
case BFD_RELOC_RISCV_CALL_PLT:
- relaxable = TRUE;
+ relaxable = true;
break;
case BFD_RELOC_RISCV_PCREL_HI20:
}
if (fixP->fx_subsy != NULL)
- as_bad_where (fixP->fx_file, fixP->fx_line,
- _("unsupported symbol subtraction"));
+ as_bad_subtract (fixP);
/* Add an R_RISCV_RELAX reloc if the reloc is relaxable. */
if (relaxable && fixP->fx_tcbit && fixP->fx_addsy != NULL)
subseg_set (seg, subseg);
}
-/* This structure is used to hold a stack of .option values. */
-struct riscv_option_stack
-{
- struct riscv_option_stack *next;
- struct riscv_set_options options;
-};
-
-static struct riscv_option_stack *riscv_opts_stack;
-
/* Handle the .option pseudo-op. */
static void
*input_line_pointer = '\0';
if (strcmp (name, "rvc") == 0)
- riscv_set_rvc (TRUE);
+ {
+ riscv_update_subset (&riscv_rps_as, "+c");
+ riscv_set_rvc (true);
+ }
else if (strcmp (name, "norvc") == 0)
- riscv_set_rvc (FALSE);
+ {
+ riscv_update_subset (&riscv_rps_as, "-c");
+ riscv_set_rvc (false);
+ }
else if (strcmp (name, "pic") == 0)
- riscv_opts.pic = TRUE;
+ riscv_opts.pic = true;
else if (strcmp (name, "nopic") == 0)
- riscv_opts.pic = FALSE;
+ riscv_opts.pic = false;
else if (strcmp (name, "relax") == 0)
- riscv_opts.relax = TRUE;
+ riscv_opts.relax = true;
else if (strcmp (name, "norelax") == 0)
- riscv_opts.relax = FALSE;
+ riscv_opts.relax = false;
else if (strcmp (name, "csr-check") == 0)
- riscv_opts.csr_check = TRUE;
+ riscv_opts.csr_check = true;
else if (strcmp (name, "no-csr-check") == 0)
- riscv_opts.csr_check = FALSE;
+ riscv_opts.csr_check = false;
+ else if (strncmp (name, "arch,", 5) == 0)
+ {
+ name += 5;
+ if (ISSPACE (*name) && *name != '\0')
+ name++;
+ riscv_update_subset (&riscv_rps_as, name);
+
+ riscv_set_rvc (false);
+ if (riscv_subset_supports (&riscv_rps_as, "c"))
+ riscv_set_rvc (true);
+ }
else if (strcmp (name, "push") == 0)
{
struct riscv_option_stack *s;
- s = (struct riscv_option_stack *) xmalloc (sizeof *s);
+ s = XNEW (struct riscv_option_stack);
s->next = riscv_opts_stack;
s->options = riscv_opts;
+ s->subset_list = riscv_subsets;
riscv_opts_stack = s;
+ riscv_subsets = riscv_copy_subset_list (s->subset_list);
+ riscv_rps_as.subset_list = riscv_subsets;
}
else if (strcmp (name, "pop") == 0)
{
as_bad (_(".option pop with no .option push"));
else
{
- riscv_opts = s->options;
+ riscv_subset_list_t *release_subsets = riscv_subsets;
riscv_opts_stack = s->next;
+ riscv_opts = s->options;
+ riscv_subsets = s->subset_list;
+ riscv_rps_as.subset_list = riscv_subsets;
+ riscv_release_subset_list (release_subsets);
free (s);
}
}
p = frag_more (bytes);
md_number_to_chars (p, 0, bytes);
- fix_new_exp (frag_now, p - frag_now->fr_literal, bytes, &ex, FALSE,
+ fix_new_exp (frag_now, p - frag_now->fr_literal, bytes, &ex, false,
(bytes == 8
? BFD_RELOC_RISCV_TLS_DTPREL64
: BFD_RELOC_RISCV_TLS_DTPREL32));
will later relax to the correct number of NOPs. We can't compute
the correct alignment now because of other linker relaxations. */
-bfd_boolean
+bool
riscv_frag_align_code (int n)
{
bfd_vma bytes = (bfd_vma) 1 << n;
/* If we are moving to a smaller alignment than the instruction size, then no
alignment is required. */
if (bytes <= insn_alignment)
- return TRUE;
+ return true;
/* When not relaxing, riscv_handle_align handles code alignment. */
if (!riscv_opts.relax)
- return FALSE;
+ return false;
nops = frag_more (worst_case_bytes);
riscv_make_nops (nops, worst_case_bytes);
fix_new_exp (frag_now, nops - frag_now->fr_literal, 0,
- &ex, FALSE, BFD_RELOC_RISCV_ALIGN);
+ &ex, false, BFD_RELOC_RISCV_ALIGN);
+
+ riscv_mapping_state (MAP_INSN, worst_case_bytes);
+
+ /* We need to start a new frag after the alignment which may be removed by
+ the linker, to prevent the assembler from computing static offsets.
+ This is necessary to get correct EH info. */
+ frag_wane (frag_now);
+ frag_new (0);
- return TRUE;
+ return true;
}
/* Implement HANDLE_ALIGN. */
/* We have 4 byte uncompressed nops. */
bfd_signed_vma size = 4;
bfd_signed_vma excess = bytes % size;
+ bfd_boolean odd_padding = (excess % 2 == 1);
char *p = fragP->fr_literal + fragP->fr_fix;
if (bytes <= 0)
/* Insert zeros or compressed nops to get 4 byte alignment. */
if (excess)
{
+ if (odd_padding)
+ riscv_add_odd_padding_symbol (fragP);
riscv_make_nops (p, excess);
fragP->fr_fix += excess;
p += excess;
}
- /* Insert variable number of 4 byte uncompressed nops. */
+ /* The frag will be changed to `rs_fill` later. The function
+ `write_contents` will try to fill the remaining spaces
+ according to the patterns we give. In this case, we give
+ a 4 byte uncompressed nop as the pattern, and set the size
+ of the pattern into `fr_var`. The nop will be output to the
+ file `fr_offset` times. However, `fr_offset` could be zero
+ if we don't need to pad the boundary finally. */
riscv_make_nops (p, size);
fragP->fr_var = size;
}
}
}
+/* This usually called from frag_var. */
+
+void
+riscv_init_frag (fragS * fragP, int max_chars)
+{
+ /* Do not add mapping symbol to debug sections. */
+ if (bfd_section_flags (now_seg) & SEC_DEBUGGING)
+ return;
+
+ switch (fragP->fr_type)
+ {
+ case rs_fill:
+ case rs_align:
+ case rs_align_test:
+ riscv_mapping_state (MAP_DATA, max_chars);
+ break;
+ case rs_align_code:
+ riscv_mapping_state (MAP_INSN, max_chars);
+ break;
+ default:
+ break;
+ }
+}
+
int
md_estimate_size_before_relax (fragS *fragp, asection *segtype)
{
- return (fragp->fr_var = relaxed_branch_length (fragp, segtype, FALSE));
+ return (fragp->fr_var = relaxed_branch_length (fragp, segtype, false));
}
/* Translate internal representation of relocation info to BFD target
if (RELAX_BRANCH_P (fragp->fr_subtype))
{
offsetT old_var = fragp->fr_var;
- fragp->fr_var = relaxed_branch_length (fragp, sec, TRUE);
+ fragp->fr_var = relaxed_branch_length (fragp, sec, true);
return fragp->fr_var - old_var;
}
reloc = RELAX_BRANCH_UNCOND (fragp->fr_subtype)
? BFD_RELOC_RISCV_RVC_JUMP : BFD_RELOC_RISCV_RVC_BRANCH;
fixp = fix_new_exp (fragp, buf - (bfd_byte *)fragp->fr_literal,
- 2, &exp, FALSE, reloc);
+ 2, &exp, false, reloc);
buf += 2;
goto done;
jump:
/* Jump to the target. */
fixp = fix_new_exp (fragp, buf - (bfd_byte *)fragp->fr_literal,
- 4, &exp, FALSE, BFD_RELOC_RISCV_JMP);
+ 4, &exp, false, BFD_RELOC_RISCV_JMP);
bfd_putl32 (MATCH_JAL, buf);
buf += 4;
break;
reloc = RELAX_BRANCH_UNCOND (fragp->fr_subtype)
? BFD_RELOC_RISCV_JMP : BFD_RELOC_12_PCREL;
fixp = fix_new_exp (fragp, buf - (bfd_byte *)fragp->fr_literal,
- 4, &exp, FALSE, reloc);
+ 4, &exp, false, reloc);
buf += 4;
break;
{
fprintf (stream, _("\
RISC-V options:\n\
- -fpic generate position-independent code\n\
+ -fpic or -fPIC generate position-independent code\n\
-fno-pic don't generate position-independent code (default)\n\
-march=ISA set the RISC-V architecture\n\
-misa-spec=ISAspec set the RISC-V ISA spec (2.2, 20190608, 20191213)\n\
- -mpriv-spec=PRIVspec set the RISC-V privilege spec (1.9, 1.9.1, 1.10, 1.11)\n\
+ -mpriv-spec=PRIVspec set the RISC-V privilege spec (1.9.1, 1.10, 1.11, 1.12)\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\
+ -mcsr-check enable the csr ISA and privilege spec version checks\n\
+ -mno-csr-check disable the csr ISA and privilege spec version checks (default)\n\
+ -mbig-endian assemble for big-endian\n\
+ -mlittle-endian assemble for little-endian\n\
"));
}
return s_leb128 (sign);
}
-/* Parse the .insn directive. */
+/* Parse the .insn directive. There are three formats,
+ Format 1: .insn <type> <operand1>, <operand2>, ...
+ Format 2: .insn <length>, <value>
+ Format 3: .insn <value>. */
static void
s_riscv_insn (int x ATTRIBUTE_UNUSED)
save_c = *input_line_pointer;
*input_line_pointer = '\0';
- const char *error = riscv_ip (str, &insn, &imm_expr,
+ riscv_mapping_state (MAP_INSN, 0);
+
+ struct riscv_ip_error error = riscv_ip (str, &insn, &imm_expr,
&imm_reloc, insn_type_hash);
+ if (error.msg)
+ {
+ char *save_in = input_line_pointer;
+ error.msg = riscv_ip_hardcode (str, &insn, &imm_expr, error.msg);
+ input_line_pointer = save_in;
+ }
- if (error)
+ if (error.msg)
{
- as_bad ("%s `%s'", error, str);
+ if (error.missing_ext)
+ as_bad ("%s `%s', extension `%s' required", error.msg, error.statement,
+ error.missing_ext);
+ else
+ as_bad ("%s `%s'", error.msg, error.statement);
}
else
{
unsigned int i;
/* Re-write architecture elf attribute. */
- arch_str = riscv_arch_str (xlen, &riscv_subsets);
+ arch_str = riscv_arch_str (xlen, riscv_subsets);
bfd_elf_add_proc_attr_string (stdoutput, Tag_RISCV_arch, arch_str);
xfree ((void *) arch_str);
/* Called after all assembly has been done. */
void
-riscv_md_end (void)
+riscv_md_finish (void)
{
riscv_set_public_attributes ();
}
+/* Adjust the symbol table. */
+
+void
+riscv_adjust_symtab (void)
+{
+ bfd_map_over_sections (stdoutput, riscv_check_mapping_symbols, (char *) 0);
+ elf_adjust_symtab ();
+}
+
/* Given a symbolic attribute NAME, return the proper integer value.
Returns -1 if the attribute is not known. */
unsigned old_xlen;
obj_attribute *attr;
- explicit_attr = TRUE;
+ explicit_attr = true;
switch (tag)
{
case Tag_RISCV_arch:
}
}
+/* Mark symbol that it follows a variant CC convention. */
+
+static void
+s_variant_cc (int ignored ATTRIBUTE_UNUSED)
+{
+ char *name;
+ char c;
+ symbolS *sym;
+ asymbol *bfdsym;
+ elf_symbol_type *elfsym;
+
+ c = get_symbol_name (&name);
+ if (!*name)
+ as_bad (_("missing symbol name for .variant_cc directive"));
+ sym = symbol_find_or_make (name);
+ restore_line_pointer (c);
+ demand_empty_rest_of_line ();
+
+ bfdsym = symbol_get_bfdsym (sym);
+ elfsym = elf_symbol_from (bfdsym);
+ gas_assert (elfsym);
+ elfsym->internal_elf_sym.st_other |= STO_RISCV_VARIANT_CC;
+}
+
+/* Same as elf_copy_symbol_attributes, but without copying st_other.
+ This is needed so RISC-V specific st_other values can be independently
+ specified for an IFUNC resolver (that is called by the dynamic linker)
+ and the symbol it resolves (aliased to the resolver). In particular,
+ if a function symbol has special st_other value set via directives,
+ then attaching an IFUNC resolver to that symbol should not override
+ the st_other setting. Requiring the directive on the IFUNC resolver
+ symbol would be unexpected and problematic in C code, where the two
+ symbols appear as two independent function declarations. */
+
+void
+riscv_elf_copy_symbol_attributes (symbolS *dest, symbolS *src)
+{
+ struct elf_obj_sy *srcelf = symbol_get_obj (src);
+ struct elf_obj_sy *destelf = symbol_get_obj (dest);
+ /* If size is unset, copy size from src. Because we don't track whether
+ .size has been used, we can't differentiate .size dest, 0 from the case
+ where dest's size is unset. */
+ if (!destelf->size && S_GET_SIZE (dest) == 0)
+ {
+ if (srcelf->size)
+ {
+ destelf->size = XNEW (expressionS);
+ *destelf->size = *srcelf->size;
+ }
+ S_SET_SIZE (dest, S_GET_SIZE (src));
+ }
+}
+
/* RISC-V pseudo-ops table. */
static const pseudo_typeS riscv_pseudo_table[] =
{
{"sleb128", s_riscv_leb128, 1},
{"insn", s_riscv_insn, 0},
{"attribute", s_riscv_attribute, 0},
+ {"variant_cc", s_variant_cc, 0},
+ {"float16", float_cons, 'h'},
{ NULL, NULL, 0 },
};