/* tc-riscv.c -- RISC-V assembler
- Copyright (C) 2011-2021 Free Software Foundation, Inc.
+ Copyright (C) 2011-2023 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"
/* The opcode's entry in riscv_opcodes. */
const struct riscv_opcode *insn_mo;
- /* The encoded instruction bits. */
+ /* The encoded instruction bits
+ (first bits enough to extract instruction length on a long opcode). */
insn_t insn_opcode;
+ /* The long encoded instruction bits ([0] is non-zero on a long opcode). */
+ char insn_long_opcode[RISCV_MAX_INSN_LEN];
+
/* The frag that contains the instruction. */
struct frag *frag;
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_SMAIA, /* Smaia */
+ CSR_CLASS_SMAIA_32, /* Smaia, rv32 only */
+ CSR_CLASS_SMSTATEEN, /* Smstateen only */
+ CSR_CLASS_SMSTATEEN_32, /* Smstateen RV32 only */
+ CSR_CLASS_SSAIA, /* Ssaia */
+ CSR_CLASS_SSAIA_AND_H, /* Ssaia with H */
+ CSR_CLASS_SSAIA_32, /* Ssaia, rv32 only */
+ CSR_CLASS_SSAIA_AND_H_32, /* Ssaia with H, rv32 only */
+ CSR_CLASS_SSSTATEEN, /* S[ms]stateen only */
+ CSR_CLASS_SSSTATEEN_AND_H, /* S[ms]stateen only (with H) */
+ CSR_CLASS_SSSTATEEN_AND_H_32, /* S[ms]stateen 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
-{
- 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[] =
+/* This structure contains information about errors that occur within the
+ riscv_ip function */
+struct riscv_ip_error
{
- {"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},
-
- {"zbb", ISA_SPEC_CLASS_DRAFT, 0, 93},
- {"zba", ISA_SPEC_CLASS_DRAFT, 0, 93},
- {"zbc", ISA_SPEC_CLASS_DRAFT, 0, 93},
-
- /* 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 elf_flags = 0;
+static bool probing_insn_operands;
+
/* Set the default_isa_spec. Return 0 if the spec isn't supported.
Otherwise, return 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 (bool rvc_value)
{
riscv_opts.rvc = rvc_value;
}
+/* Turn on the tso flag for elf_flags once we have enabled ztso extension. */
+
static void
-riscv_set_rve (bool rve_value)
+riscv_set_tso (void)
{
- riscv_opts.rve = rve_value;
+ elf_flags |= EF_RISCV_TSO;
}
-static riscv_subset_list_t riscv_subsets;
-
-static bool
-riscv_subset_supports (const char *feature)
+/* The linked list hanging off of .subsets_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_parse_subset_t riscv_rps_as =
{
- struct riscv_subset_t *subset;
-
- if (riscv_opts.rvc && (strcasecmp (feature, "c") == 0))
- return true;
-
- return riscv_lookup_subset (&riscv_subsets, feature, &subset);
-}
-
-static bool
-riscv_multi_subset_supports (enum riscv_insn_class insn_class)
-{
- switch (insn_class)
- {
- 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");
-
- case INSN_CLASS_ZBB:
- return riscv_subset_supports ("zbb");
-
- case INSN_CLASS_ZBA:
- return riscv_subset_supports ("zba");
-
- case INSN_CLASS_ZBC:
- return riscv_subset_supports ("zbc");
-
- default:
- as_fatal ("internal: unreachable");
- return false;
- }
-}
+ 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. */
+};
-/* Handle of the extension with version hash table. */
-static htab_t ext_version_hash = NULL;
+/* Update the architecture string in the subset_list. */
-static htab_t
-init_ext_version_hash (void)
+static void
+riscv_reset_subsets_list_arch_str (void)
{
- const struct riscv_ext_version *table = ext_version_table;
- htab_t hash = str_htab_create ();
- int i = 0;
-
- while (table[i].name)
- {
- 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++;
- }
-
- return hash;
+ riscv_subset_list_t *subsets = riscv_rps_as.subset_list;
+ if (subsets->arch_str != NULL)
+ free ((void *) subsets->arch_str);
+ subsets->arch_str = riscv_arch_str (xlen, subsets);
}
-static void
-riscv_get_default_ext_version (const char *name,
- int *major_version,
- int *minor_version)
+/* This structure is used to hold a stack of .option values. */
+struct riscv_option_stack
{
- struct riscv_ext_version *ext;
+ struct riscv_option_stack *next;
+ struct riscv_set_options options;
+ riscv_subset_list_t *subset_list;
+};
- 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++;
- }
-}
+static struct riscv_option_stack *riscv_opts_stack = NULL;
/* 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;
- rps.check_unknown_prefixed_ext = true;
-
- if (s == NULL)
- return;
+ if (s != NULL && strcmp (s, "") == 0)
+ {
+ as_bad (_("the architecture string of -march and elf architecture "
+ "attributes cannot be empty"));
+ return;
+ }
- riscv_release_subset_list (&riscv_subsets);
- riscv_parse_subset (&rps, s);
+ if (riscv_rps_as.subset_list == NULL)
+ {
+ riscv_rps_as.subset_list = XNEW (riscv_subset_list_t);
+ riscv_rps_as.subset_list->head = NULL;
+ riscv_rps_as.subset_list->tail = NULL;
+ riscv_rps_as.subset_list->arch_str = NULL;
+ }
+ riscv_release_subset_list (riscv_rps_as.subset_list);
+ riscv_parse_subset (&riscv_rps_as, s);
+ riscv_reset_subsets_list_arch_str ();
- /* To support .option rvc and rve. */
riscv_set_rvc (false);
- if (riscv_subset_supports ("c"))
+ if (riscv_subset_supports (&riscv_rps_as, "c"))
riscv_set_rvc (true);
- riscv_set_rve (false);
- if (riscv_subset_supports ("e"))
- riscv_set_rve (true);
+
+ if (riscv_subset_supports (&riscv_rps_as, "ztso"))
+ riscv_set_tso ();
}
/* Indicate -mabi option is explictly set. */
static bool explicit_mabi = false;
+/* Set the abi information. */
+
static void
riscv_set_abi (unsigned new_xlen, enum float_abi new_float_abi, bool rve)
{
{
if (!explicit_mabi)
{
- if (riscv_subset_supports ("q"))
+ if (riscv_subset_supports (&riscv_rps_as, "q"))
riscv_set_abi (xlen, FLOAT_ABI_QUAD, false);
- else if (riscv_subset_supports ("d"))
+ else if (riscv_subset_supports (&riscv_rps_as, "d"))
riscv_set_abi (xlen, FLOAT_ABI_DOUBLE, false);
- else if (riscv_subset_supports ("e"))
+ 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);
else if (abi_xlen < xlen)
as_bad ("%d-bit ABI not yet supported on %d-bit ISA", abi_xlen, xlen);
- if (riscv_subset_supports ("e") && !rve_abi)
+ 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 ("f"))
+ && !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 ("d"))
+ && !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 ("q"))
+ && !riscv_subset_supports (&riscv_rps_as, "q"))
as_bad ("ilp32q/lp64q ABI can't be used when q extension "
"isn't supported");
}
/* 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 bool start_assemble = false;
/* Indicate CSR or priv instructions are explicitly used. */
static bool explicit_priv_attr = false;
+static char *expr_parse_end;
+
/* Macros for encoding relaxation state for RVC branches and far jumps. */
#define RELAX_BRANCH_ENCODE(uncond, rvc, length) \
((relax_substateT) \
#define INSERT_OPERAND(FIELD, INSN, VALUE) \
INSERT_BITS ((INSN).insn_opcode, VALUE, OP_MASK_##FIELD, OP_SH_##FIELD)
+#define INSERT_IMM(n, s, INSN, VALUE) \
+ INSERT_BITS ((INSN).insn_opcode, VALUE, (1ULL<<n) - 1, s)
+
/* Determine if an instruction matches an opcode. */
#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)
+ fragS *frag,
+ const char *arch_str,
+ bool odd_data_padding)
{
const char *name;
+ char *buff = NULL;
switch (state)
{
case MAP_DATA:
name = "$d";
break;
case MAP_INSN:
- name = "$x";
+ if (arch_str != NULL)
+ {
+ size_t size = strlen (arch_str) + 3; /* "$x" + '\0' */
+ buff = xmalloc (size);
+ snprintf (buff, size, "$x%s", arch_str);
+ name = buff;
+ }
+ else
+ 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 (arch_str != NULL)
+ {
+ /* Store current $x+arch into tc_segment_info. */
+ seg_info (now_seg)->tc_segment_info_data.arch_map_symbol = symbol;
+ xfree ((void *) buff);
+ }
/* 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. */
+ then mapping symbol for the following code will have the same value.
+
+ Please see gas/testsuite/gas/riscv/mapping.s: .text.zero.fill.first
+ and .text.zero.fill.last. */
+ symbolS *first = frag->tc_frag_data.first_map_symbol;
+ symbolS *last = frag->tc_frag_data.last_map_symbol;
+ symbolS *removed = NULL;
if (value == 0)
{
- if (frag->tc_frag_data.first_map_symbol != NULL)
+ if (first != NULL)
{
- know (S_GET_VALUE (frag->tc_frag_data.first_map_symbol)
- == S_GET_VALUE (symbol));
+ know (S_GET_VALUE (first) == S_GET_VALUE (symbol)
+ && first == last);
/* Remove the old one. */
- symbol_remove (frag->tc_frag_data.first_map_symbol,
- &symbol_rootP, &symbol_lastP);
+ removed = first;
}
frag->tc_frag_data.first_map_symbol = symbol;
}
- if (frag->tc_frag_data.last_map_symbol != NULL)
+ else if (last != 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));
+ know (S_GET_VALUE (last) <= 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);
+ if (S_GET_VALUE (last) == S_GET_VALUE (symbol))
+ removed = last;
}
frag->tc_frag_data.last_map_symbol = symbol;
+
+ if (removed == NULL)
+ return;
+
+ if (odd_data_padding)
+ {
+ /* If the removed mapping symbol is $x+arch, then add it back to
+ the next $x. */
+ const char *str = strncmp (S_GET_NAME (removed), "$xrv", 4) == 0
+ ? S_GET_NAME (removed) + 2 : NULL;
+ make_mapping_symbol (MAP_INSN, frag->fr_fix + 1, frag, str,
+ false/* odd_data_padding */);
+ }
+ symbol_remove (removed, &symbol_rootP, &symbol_lastP);
}
/* Set the mapping state for frag_now. */
void
riscv_mapping_state (enum riscv_seg_mstate to_state,
- int max_chars)
+ int max_chars,
+ bool fr_align_code)
{
enum riscv_seg_mstate from_state =
seg_info (now_seg)->tc_segment_info_data.map_state;
+ bool reset_seg_arch_str = false;
if (!SEG_NORMAL (now_seg)
- /* For now I only add the mapping symbols to text sections.
+ /* For now we 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. */
return;
/* The mapping symbol should be emitted if not in the right
- mapping state */
- if (from_state == to_state)
+ mapping state. */
+ symbolS *seg_arch_symbol =
+ seg_info (now_seg)->tc_segment_info_data.arch_map_symbol;
+ if (to_state == MAP_INSN && seg_arch_symbol == 0)
+ {
+ /* Always add $x+arch at the first instruction of section. */
+ reset_seg_arch_str = true;
+ }
+ else if (seg_arch_symbol != 0
+ && to_state == MAP_INSN
+ && !fr_align_code
+ && strcmp (riscv_rps_as.subset_list->arch_str,
+ S_GET_NAME (seg_arch_symbol) + 2) != 0)
+ {
+ reset_seg_arch_str = true;
+ }
+ else 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);
+ const char *arch_str = reset_seg_arch_str
+ ? riscv_rps_as.subset_list->arch_str : NULL;
+ make_mapping_symbol (to_state, value, frag_now, arch_str,
+ false/* odd_data_padding */);
}
/* Add the odd bytes of paddings for riscv_handle_align. */
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);
+ removed in the make_mapping_symbol.
+
+ Please see gas/testsuite/gas/riscv/mapping.s: .text.odd.align.*. */
+ make_mapping_symbol (MAP_DATA, frag->fr_fix, frag,
+ NULL/* arch_str */, true/* odd_data_padding */);
}
/* Remove any excess mapping symbols generated for alignment frags in
do
{
- if (next->tc_frag_data.first_map_symbol != NULL)
+ symbolS *next_first = next->tc_frag_data.first_map_symbol;
+ if (next_first != NULL)
{
/* The last mapping symbol overlaps with another one
- which at the start of the next frag. */
- symbol_remove (last, &symbol_rootP, &symbol_lastP);
+ which at the start of the next frag.
+
+ Please see the gas/testsuite/gas/riscv/mapping.s:
+ .text.zero.fill.align.A and .text.zero.fill.align.B. */
+ know (S_GET_VALUE (last) == S_GET_VALUE (next_first));
+ symbolS *removed = last;
+ if (strncmp (S_GET_NAME (last), "$xrv", 4) == 0
+ && strcmp (S_GET_NAME (next_first), "$x") == 0)
+ removed = next_first;
+ symbol_remove (removed, &symbol_rootP, &symbol_lastP);
break;
}
if (next->fr_next == NULL)
{
- /* The last mapping symbol is at the end of the section. */
+ /* The last mapping symbol is at the end of the section.
+
+ Please see the gas/testsuite/gas/riscv/mapping.s:
+ .text.last.section. */
know (next->fr_fix == 0 && next->fr_var == 0);
symbol_remove (last, &symbol_rootP, &symbol_lastP);
break;
{
insn->insn_mo = mo;
insn->insn_opcode = mo->match;
+ insn->insn_long_opcode[0] = 0;
insn->frag = NULL;
insn->where = 0;
insn->fixp = NULL;
install_insn (const struct riscv_cl_insn *insn)
{
char *f = insn->frag->fr_literal + insn->where;
- number_to_chars_littleendian (f, insn->insn_opcode, insn_length (insn));
+ if (insn->insn_long_opcode[0] != 0)
+ memcpy (f, insn->insn_long_opcode, insn_length (insn));
+ else
+ number_to_chars_littleendian (f, insn->insn_opcode, insn_length (insn));
}
/* Move INSN to offset WHERE in FRAG. Adjust the fixups accordingly
{"NMADD", 0x4f},
{"NMSUB", 0x4b},
{"OP_FP", 0x53},
- /*reserved 0x57. */
+ {"OP_V", 0x57},
{"CUSTOM_2", 0x5b},
/* 48b 0x5f. */
*s = e;
*e = save_c;
- expr_end = e;
+ expr_parse_end = e;
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
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;
- bool need_check_version = true;
- bool 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_SMAIA_32:
+ is_rv32_only = true;
+ /* Fall through. */
+ case CSR_CLASS_SMAIA:
+ extension = "smaia";
+ break;
+ case CSR_CLASS_SMSTATEEN_32:
+ is_rv32_only = true;
+ /* Fall through. */
+ case CSR_CLASS_SMSTATEEN:
+ extension = "smstateen";
+ break;
+ case CSR_CLASS_SSAIA:
+ case CSR_CLASS_SSAIA_AND_H:
+ case CSR_CLASS_SSAIA_32:
+ case CSR_CLASS_SSAIA_AND_H_32:
+ is_rv32_only = (csr_class == CSR_CLASS_SSAIA_32
+ || csr_class == CSR_CLASS_SSAIA_AND_H_32);
+ is_h_required = (csr_class == CSR_CLASS_SSAIA_AND_H
+ || csr_class == CSR_CLASS_SSAIA_AND_H_32);
+ extension = "ssaia";
+ break;
+ case CSR_CLASS_SSSTATEEN_AND_H_32:
+ is_rv32_only = true;
+ /* Fall through. */
+ case CSR_CLASS_SSSTATEEN_AND_H:
+ is_h_required = true;
+ /* Fall through. */
+ case CSR_CLASS_SSSTATEEN:
+ extension = "ssstateen";
+ 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);
return false;
for (i = 0; i < size; i++)
- if (array[i] != NULL && strncmp (array[i], *s, len) == 0)
+ if (array[i] != NULL && strncmp (array[i], *s, len) == 0
+ && array[i][len] == '\0')
{
*regnop = i;
*s += len;
return false;
}
+static bool
+flt_lookup (float f, const float *array, size_t size, unsigned *regnop)
+{
+ size_t i;
+
+ for (i = 0; i < size; i++)
+ if (array[i] == f)
+ {
+ *regnop = i;
+ return true;
+ }
+
+ return false;
+}
+
+#define USE_BITS(mask,shift) (used_bits |= ((insn_t)(mask) << (shift)))
+#define USE_IMM(n, s) \
+ (used_bits |= ((insn_t)((1ull<<n)-1) << (s)))
+
/* 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. */
+ operand list. The `length` could be the actual instruction length or
+ 0 for auto-detection. */
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;
if (length == 0)
- insn_width = 8 * riscv_insn_length (opc->match);
- else
- insn_width = 8 * length;
+ length = riscv_insn_length (opc->match);
+ /* We don't support instructions longer than 64-bits yet. */
+ if (length > 8)
+ length = 8;
+ insn_width = 8 * length;
- required_bits = ~0ULL >> (64 - insn_width);
+ required_bits = ((insn_t)~0ULL) >> (64 - insn_width);
if ((used_bits & opc->match) != (opc->match & required_bits))
{
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 'l': used_bits |= ENCODE_RVV_VI_UIMM6 (-1U); break;
+ case 'm': USE_BITS (OP_MASK_VMASK, OP_SH_VMASK); break;
+ case 'M': break; /* Macro operand, must be a mask register. */
+ case 'T': break; /* Macro operand, must be a vector register. */
default:
- as_bad (_("internal: bad RISC-V opcode "
- "(unknown operand type `F%c'): %s %s"),
- c, opc->name, opc->args);
- return false;
+ 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 'c': 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 '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;
+ case 'W': /* Various operands. */
+ switch (*++oparg)
+ {
+ case 'i':
+ switch (*++oparg)
+ {
+ case 'f': used_bits |= ENCODE_STYPE_IMM (-1U); break;
+ default:
+ goto unknown_validate_operand;
+ }
+ break;
+ case 'f':
+ switch (*++oparg)
+ {
+ case 'v': USE_BITS (OP_MASK_RS1, OP_SH_RS1); break;
+ default:
+ goto unknown_validate_operand;
+ }
+ break;
+ default:
+ goto unknown_validate_operand;
+ }
+ break;
+ case 'X': /* Integer immediate. */
+ {
+ size_t n;
+ size_t s;
+
+ switch (*++oparg)
+ {
+ case 'l': /* Literal. */
+ oparg += strcspn(oparg, ",") - 1;
+ break;
+ case 's': /* 'XsN@S' ... N-bit signed immediate at bit S. */
+ goto use_imm;
+ case 'u': /* 'XuN@S' ... N-bit unsigned immediate at bit S. */
+ goto use_imm;
+ use_imm:
+ n = strtol (oparg + 1, (char **)&oparg, 10);
+ if (*oparg != '@')
+ goto unknown_validate_operand;
+ s = strtol (oparg + 1, (char **)&oparg, 10);
+ oparg--;
+
+ USE_IMM (n, s);
+ break;
+ default:
+ goto unknown_validate_operand;
+ }
}
- 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
+ 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),
+ "(bits %#llx undefined or invalid): %s %s"),
+ (unsigned long long)(used_bits ^ required_bits),
opc->name, opc->args);
return false;
}
return true;
}
+#undef USE_BITS
+
struct percent_op_match
{
const char *str;
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);
|| reloc_type == BFD_RELOC_RISCV_JMP)
{
int j = reloc_type == BFD_RELOC_RISCV_JMP;
- int best_case = riscv_insn_length (ip->insn_opcode);
+ int best_case = insn_length (ip);
unsigned worst_case = relaxed_branch_length (NULL, NULL, 0);
if (now_seg == absolute_section)
}
add_fixed_insn (ip);
- install_insn (ip);
/* We need to start a new frag after any instruction that can be
optimized away or compressed by the linker during relaxation, to prevent
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;
}
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);
}
}
}
}
+/* 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
case M_LA:
case M_LLA:
+ case M_LGA:
/* Load the address of a symbol into a register. */
if (!IS_SEXT_32BIT_NUM (imm_expr->X_add_number))
as_bad (_("offset too large"));
if (imm_expr->X_op == O_constant)
load_const (rd, imm_expr);
- else if (riscv_opts.pic && mask == M_LA) /* Global PIC symbol. */
+ /* Global PIC symbol. */
+ else if ((riscv_opts.pic && mask == M_LA)
+ || mask == M_LGA)
pcrel_load (rd, rd, imm_expr, LOAD_ADDRESS_INSN,
BFD_RELOC_RISCV_GOT_HI20, BFD_RELOC_RISCV_PCREL_LO12_I);
- else /* Local PIC symbol, or any non-PIC symbol. */
+ /* Local PIC symbol, or any non-PIC symbol. */
+ else
pcrel_load (rd, rd, imm_expr, "addi",
BFD_RELOC_RISCV_PCREL_HI20, BFD_RELOC_RISCV_PCREL_LO12_I);
break;
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:
as_bad (_("internal: macro %s not implemented"), ip->insn_mo->name);
break;
static const struct percent_op_match percent_op_utype[] =
{
- {"%tprel_hi", BFD_RELOC_RISCV_TPREL_HI20},
- {"%pcrel_hi", BFD_RELOC_RISCV_PCREL_HI20},
- {"%got_pcrel_hi", BFD_RELOC_RISCV_GOT_HI20},
- {"%tls_ie_pcrel_hi", BFD_RELOC_RISCV_TLS_GOT_HI20},
- {"%tls_gd_pcrel_hi", BFD_RELOC_RISCV_TLS_GD_HI20},
- {"%hi", BFD_RELOC_RISCV_HI20},
+ {"tprel_hi", BFD_RELOC_RISCV_TPREL_HI20},
+ {"pcrel_hi", BFD_RELOC_RISCV_PCREL_HI20},
+ {"got_pcrel_hi", BFD_RELOC_RISCV_GOT_HI20},
+ {"tls_ie_pcrel_hi", BFD_RELOC_RISCV_TLS_GOT_HI20},
+ {"tls_gd_pcrel_hi", BFD_RELOC_RISCV_TLS_GD_HI20},
+ {"hi", BFD_RELOC_RISCV_HI20},
{0, 0}
};
static const struct percent_op_match percent_op_itype[] =
{
- {"%lo", BFD_RELOC_RISCV_LO12_I},
- {"%tprel_lo", BFD_RELOC_RISCV_TPREL_LO12_I},
- {"%pcrel_lo", BFD_RELOC_RISCV_PCREL_LO12_I},
+ {"lo", BFD_RELOC_RISCV_LO12_I},
+ {"tprel_lo", BFD_RELOC_RISCV_TPREL_LO12_I},
+ {"pcrel_lo", BFD_RELOC_RISCV_PCREL_LO12_I},
{0, 0}
};
static const struct percent_op_match percent_op_stype[] =
{
- {"%lo", BFD_RELOC_RISCV_LO12_S},
- {"%tprel_lo", BFD_RELOC_RISCV_TPREL_LO12_S},
- {"%pcrel_lo", BFD_RELOC_RISCV_PCREL_LO12_S},
+ {"lo", BFD_RELOC_RISCV_LO12_S},
+ {"tprel_lo", BFD_RELOC_RISCV_TPREL_LO12_S},
+ {"pcrel_lo", BFD_RELOC_RISCV_PCREL_LO12_S},
{0, 0}
};
static const struct percent_op_match percent_op_rtype[] =
{
- {"%tprel_add", BFD_RELOC_RISCV_TPREL_ADD},
+ {"tprel_add", BFD_RELOC_RISCV_TPREL_ADD},
{0, 0}
};
const struct percent_op_match *percent_op)
{
for ( ; percent_op->str; percent_op++)
- if (strncasecmp (*str, percent_op->str, strlen (percent_op->str)) == 0)
+ if (strncasecmp (*str + 1, percent_op->str, strlen (percent_op->str)) == 0)
{
- int len = strlen (percent_op->str);
+ size_t len = 1 + strlen (percent_op->str);
- if (!ISSPACE ((*str)[len]) && (*str)[len] != '(')
+ while (ISSPACE ((*str)[len]))
+ ++len;
+ if ((*str)[len] != '(')
continue;
- *str += strlen (percent_op->str);
+ *str += len;
*reloc = percent_op->reloc;
/* Check whether the output BFD supports this relocation.
save_in = input_line_pointer;
input_line_pointer = str;
expression (ep);
- expr_end = input_line_pointer;
+ expr_parse_end = input_line_pointer;
input_line_pointer = save_in;
}
expression in *EP and the relocation, if any, in RELOC.
Return the number of relocation operators used (0 or 1).
- On exit, EXPR_END points to the first character after the expression. */
+ On exit, EXPR_PARSE_END points to the first character after the
+ expression. */
static size_t
my_getSmallExpression (expressionS *ep, bfd_reloc_code_real_type *reloc,
char *str, const struct percent_op_match *percent_op)
{
size_t reloc_index;
- unsigned crux_depth, str_depth, regno;
+ unsigned crux_depth, str_depth;
+ bool orig_probing = probing_insn_operands;
char *crux;
- /* 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;
- }
-
/* Search for the start of the main expression.
End the loop with CRUX pointing to the start of the main expression and
&& reloc_index < 1
&& parse_relocation (&str, reloc, percent_op));
+ if (*str == '%')
+ {
+ /* expression() will choke on anything looking like an (unrecognized)
+ relocation specifier. Don't even call it, avoiding multiple (and
+ perhaps redundant) error messages; our caller will issue one. */
+ ep->X_op = O_illegal;
+ return 0;
+ }
+
+ /* Anything inside parentheses or subject to a relocation operator cannot
+ be a register and hence can be treated the same as operands to
+ directives (other than .insn). */
+ if (str_depth || reloc_index)
+ probing_insn_operands = false;
+
my_getExpression (ep, crux);
- str = expr_end;
+ str = expr_parse_end;
+
+ probing_insn_operands = orig_probing;
/* Match every open bracket. */
while (crux_depth > 0 && (*str == ')' || *str == ' ' || *str == '\t'))
if (crux_depth > 0)
as_bad ("unclosed '('");
- expr_end = str;
+ expr_parse_end = str;
return reloc_index;
}
static size_t
my_getOpcodeExpression (expressionS *ep, bfd_reloc_code_real_type *reloc,
- char *str, const struct percent_op_match *percent_op)
+ char *str)
{
const struct opcode_name_t *o = opcode_name_lookup (&str);
return 0;
}
- return my_getSmallExpression (ep, reloc, str, percent_op);
+ return my_getSmallExpression (ep, reloc, str, percent_op_null);
+}
+
+/* Parse string STR as a vsetvli operand. Store the expression in *EP.
+ On exit, EXPR_PARSE_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_parse_end = str;
+ }
+ else
+ {
+ my_getExpression (ep, str);
+ str = expr_parse_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 bool
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.
|| ((insn ^ MATCH_SFENCE_VM) & MASK_SFENCE_VM) == 0);
}
+static symbolS *deferred_sym_rootP;
+static symbolS *deferred_sym_lastP;
+/* Since symbols can't easily be freed, try to recycle ones which weren't
+ committed. */
+static symbolS *orphan_sym_rootP;
+static symbolS *orphan_sym_lastP;
+
/* This routine assembles an instruction into its binary format. As a
side effect, it sets the global variable imm_reloc to the type of
relocation to do if one of the operands is an address expression. */
-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. */
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;
+ probing_insn_operands = true;
+
+ 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;
+
+ /* Purge deferred symbols from the previous round, if any. */
+ while (deferred_sym_rootP)
+ {
+ symbolS *sym = deferred_sym_rootP;
+
+ symbol_remove (sym, &deferred_sym_rootP, &deferred_sym_lastP);
+ symbol_append (sym, orphan_sym_lastP, &orphan_sym_rootP,
+ &orphan_sym_lastP);
+ }
create_insn (ip, insn);
- argnum = 1;
imm_expr->X_op = O_absent;
*imm_reloc = BFD_RELOC_UNUSED;
- p = percent_op_itype;
+ p = percent_op_null;
- 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)
/* 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;
}
+
+ /* 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;
+ error.msg = NULL;
insn_with_csr = false;
+
+ /* Commit deferred symbols, if any. */
+ while (deferred_sym_rootP)
+ {
+ symbolS *sym = deferred_sym_rootP;
+
+ symbol_remove (sym, &deferred_sym_rootP,
+ &deferred_sym_lastP);
+ symbol_append (sym, symbol_lastP, &symbol_rootP,
+ &symbol_lastP);
+ symbol_table_insert (sym);
+ }
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 '>': /* Shift amount, 0 - (XLEN-1). */
- if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
+ if (my_getSmallExpression (imm_expr, imm_reloc, asarg, p)
|| imm_expr->X_op != O_constant
|| (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_parse_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
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
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
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_parse_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_parse_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_parse_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_parse_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_parse_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_parse_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_parse_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_parse_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_parse_end;
+ continue;
+
+ case 'l': /* 6-bit 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 >= 64)
+ as_bad (_("bad value for vector immediate field, "
+ "value must be 0...63"));
+ ip->insn_opcode |= ENCODE_RVV_VI_UIMM6 (imm_expr->X_add_number);
+ imm_expr->X_op = O_absent;
+ asarg = expr_parse_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);
+ 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);
+ as_bad (_("improper shift amount (%"PRIu64")"),
+ 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_parse_end;
continue;
case '>': /* Shift amount, 0 - (XLEN-1). */
- my_getExpression (imm_expr, s);
+ 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);
+ as_bad (_("improper shift amount (%"PRIu64")"),
+ 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_parse_end;
continue;
case 'Z': /* CSRRxI immediate. */
- my_getExpression (imm_expr, s);
+ 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);
+ as_bad (_("improper CSRxI immediate (%"PRIu64")"),
+ 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_parse_end;
continue;
case 'E': /* Control register. */
insn_with_csr = true;
explicit_priv_attr = true;
- if (reg_lookup (&s, RCLASS_CSR, ®no))
+ if (reg_lookup (&asarg, RCLASS_CSR, ®no))
INSERT_OPERAND (CSR, *ip, regno);
else
{
- my_getExpression (imm_expr, s);
+ 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);
+ as_bad (_("improper CSR address (%"PRIu64")"),
+ 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_parse_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_parse_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_parse_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_parse_end;
continue;
case 'j': /* Sign-extended immediate. */
p = percent_op_rtype;
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))
+ 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_parse_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_parse_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_parse_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_parse_end;
*imm_reloc = BFD_RELOC_RISCV_JMP;
continue;
case 'c':
- my_getExpression (imm_expr, s);
- s = expr_end;
- if (strcmp (s, "@plt") == 0)
- {
- *imm_reloc = BFD_RELOC_RISCV_CALL_PLT;
- s += 4;
- }
- else
- *imm_reloc = BFD_RELOC_RISCV_CALL;
+ my_getExpression (imm_expr, asarg);
+ asarg = expr_parse_end;
+ if (strcmp (asarg, "@plt") == 0)
+ asarg += 4;
+ *imm_reloc = BFD_RELOC_RISCV_CALL_PLT;
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)
|| 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_parse_end;
continue;
case '2':
- if (my_getOpcodeExpression (imm_expr, imm_reloc, s, p)
+ if (my_getOpcodeExpression (imm_expr, imm_reloc, asarg)
|| 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_parse_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_parse_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_parse_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_parse_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_parse_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_parse_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_parse_end;
imm_expr->X_op = O_absent;
continue;
+ case 'W': /* Various operands. */
+ switch (*++oparg)
+ {
+ case 'i':
+ switch (*++oparg)
+ {
+ case 'f':
+ /* Prefetch offset for 'Zicbop' extension.
+ 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 >= RISCV_IMM_REACH / 2
+ || imm_expr->X_add_number < -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_parse_end;
+ continue;
+ default:
+ goto unknown_riscv_ip_operand;
+ }
+ break;
+ case 'f':
+ switch (*++oparg)
+ {
+ case 'v':
+ /* FLI.[HSDQ] value field for 'Zfa' extension. */
+ if (!arg_lookup (&asarg, riscv_fli_symval,
+ ARRAY_SIZE (riscv_fli_symval), ®no))
+ {
+ /* 0.0 is not a valid entry in riscv_fli_numval. */
+ errno = 0;
+ float f = strtof (asarg, &asarg);
+ if (errno != 0 || f == 0.0
+ || !flt_lookup (f, riscv_fli_numval,
+ ARRAY_SIZE(riscv_fli_numval),
+ ®no))
+ {
+ as_bad (_("bad fli constant operand, "
+ "supported constants must be in "
+ "decimal or hexadecimal floating-point "
+ "literal form"));
+ break;
+ }
+ }
+ INSERT_OPERAND (RS1, *ip, regno);
+ continue;
+ default:
+ goto unknown_riscv_ip_operand;
+ }
+ break;
+ default:
+ goto unknown_riscv_ip_operand;
+ }
+ break;
+
+ case 'X': /* Integer immediate. */
+ {
+ size_t n;
+ size_t s;
+ bool sign;
+
+ switch (*++oparg)
+ {
+ case 'l': /* Literal. */
+ n = strcspn (++oparg, ",");
+ if (strncmp (oparg, asarg, n))
+ as_bad (_("unexpected literal (%s)"), asarg);
+ oparg += n - 1;
+ asarg += n;
+ continue;
+ case 's': /* 'XsN@S' ... N-bit signed immediate at bit S. */
+ sign = true;
+ goto parse_imm;
+ case 'u': /* 'XuN@S' ... N-bit unsigned immediate at bit S. */
+ sign = false;
+ goto parse_imm;
+ parse_imm:
+ n = strtol (oparg + 1, (char **)&oparg, 10);
+ if (*oparg != '@')
+ goto unknown_riscv_ip_operand;
+ s = strtol (oparg + 1, (char **)&oparg, 10);
+ oparg--;
+
+ my_getExpression (imm_expr, asarg);
+ check_absolute_expr (ip, imm_expr, false);
+ if (!sign)
+ {
+ if (!VALIDATE_U_IMM (imm_expr->X_add_number, n))
+ as_bad (_("improper immediate value (%"PRIu64")"),
+ imm_expr->X_add_number);
+ }
+ else
+ {
+ if (!VALIDATE_S_IMM (imm_expr->X_add_number, n))
+ as_bad (_("improper immediate value (%"PRIi64")"),
+ imm_expr->X_add_number);
+ }
+ INSERT_IMM (n, s, *ip, imm_expr->X_add_number);
+ imm_expr->X_op = O_absent;
+ asarg = expr_parse_end;
+ continue;
+ default:
+ goto unknown_riscv_ip_operand;
+ }
+ }
+ break;
+
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");
+ 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;
+
+ probing_insn_operands = false;
return error;
}
do
{
expression (imm_expr);
- if (imm_expr->X_op != O_constant)
+ switch (imm_expr->X_op)
{
+ case O_constant:
+ values[num++] = (insn_t) imm_expr->X_add_number;
+ break;
+ case O_big:
+ /* Extract lower 32-bits of a big number.
+ Assume that generic_bignum_to_int32 work on such number. */
+ values[num++] = (insn_t) generic_bignum_to_int32 ();
+ break;
+ default:
/* The first value isn't constant, so it should be
.insn <type> <operands>. We have been parsed it
in the riscv_ip. */
return error;
return _("values must be constant");
}
- values[num++] = (insn_t) imm_expr->X_add_number;
}
- while (*input_line_pointer++ == ',' && num < 2);
+ while (*input_line_pointer++ == ',' && num < 2 && imm_expr->X_op != O_big);
input_line_pointer--;
if (*input_line_pointer != '\0')
insn->match = values[num - 1];
create_insn (ip, insn);
unsigned int bytes = riscv_insn_length (insn->match);
- if (values[num - 1] >> (8 * bytes) != 0
- || (num == 2 && values[0] != bytes))
+
+ if (num == 2 && values[0] != bytes)
+ return _("value conflicts with instruction length");
+
+ if (imm_expr->X_op == O_big)
+ {
+ unsigned int llen = 0;
+ for (LITTLENUM_TYPE lval = generic_bignum[imm_expr->X_add_number - 1];
+ lval != 0; llen++)
+ lval >>= BITS_PER_CHAR;
+ unsigned int repr_bytes
+ = (imm_expr->X_add_number - 1) * CHARS_PER_LITTLENUM + llen;
+ if (bytes < repr_bytes)
+ return _("value conflicts with instruction length");
+ for (num = 0; num < imm_expr->X_add_number - 1; ++num)
+ number_to_chars_littleendian (
+ ip->insn_long_opcode + num * CHARS_PER_LITTLENUM,
+ generic_bignum[num],
+ CHARS_PER_LITTLENUM);
+ if (llen != 0)
+ number_to_chars_littleendian (
+ ip->insn_long_opcode + num * CHARS_PER_LITTLENUM,
+ generic_bignum[num],
+ llen);
+ memset(ip->insn_long_opcode + repr_bytes, 0, bytes - repr_bytes);
+ return NULL;
+ }
+
+ if (bytes < sizeof(values[0]) && values[num - 1] >> (8 * bytes) != 0)
return _("value conflicts with instruction length");
return NULL;
return;
}
- riscv_mapping_state (MAP_INSN, 0);
+ riscv_mapping_state (MAP_INSN, 0, false/* fr_align_code */);
- const char *error = riscv_ip (str, &insn, &imm_expr, &imm_reloc, op_hash);
+ 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;
}
const char *
md_atof (int type, char *litP, int *sizeP)
{
- return ieee_md_atof (type, litP, sizeP, TARGET_BYTES_BIG_ENDIAN);
+ return ieee_md_atof (type, litP, sizeP, target_big_endian);
}
void
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)
flag_dwarf_cie_version = 3;
}
+bool riscv_parse_name (const char *name, struct expressionS *ep,
+ enum expr_mode mode)
+{
+ unsigned int regno;
+ symbolS *sym;
+
+ if (!probing_insn_operands)
+ return false;
+
+ gas_assert (mode == expr_normal);
+
+ regno = reg_lookup_internal (name, RCLASS_GPR);
+ if (regno == (unsigned int)-1)
+ return false;
+
+ if (symbol_find (name) != NULL)
+ return false;
+
+ /* Create a symbol without adding it to the symbol table yet.
+ Insertion will happen only once we commit to using the insn
+ we're probing operands for. */
+ for (sym = deferred_sym_rootP; sym; sym = symbol_next (sym))
+ if (strcmp (name, S_GET_NAME (sym)) == 0)
+ break;
+ if (!sym)
+ {
+ for (sym = orphan_sym_rootP; sym; sym = symbol_next (sym))
+ if (strcmp (name, S_GET_NAME (sym)) == 0)
+ {
+ symbol_remove (sym, &orphan_sym_rootP, &orphan_sym_lastP);
+ break;
+ }
+ if (!sym)
+ sym = symbol_create (name, undefined_section,
+ &zero_address_frag, 0);
+
+ symbol_append (sym, deferred_sym_lastP, &deferred_sym_rootP,
+ &deferred_sym_lastP);
+ }
+
+ ep->X_op = O_symbol;
+ ep->X_add_symbol = sym;
+ ep->X_add_number = 0;
+
+ return true;
+}
+
long
md_pcrel_from (fixS *fixP)
{
case BFD_RELOC_RISCV_SUB32:
case BFD_RELOC_RISCV_SUB64:
case BFD_RELOC_RISCV_RELAX:
+ /* cvt_frag_to_fill () has called output_leb128 (). */
+ case BFD_RELOC_RISCV_SET_ULEB128:
+ case BFD_RELOC_RISCV_SUB_ULEB128:
break;
case BFD_RELOC_RISCV_TPREL_HI20:
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_reset_subsets_list_arch_str ();
+ riscv_set_rvc (true);
+ }
else if (strcmp (name, "norvc") == 0)
- riscv_set_rvc (false);
+ {
+ riscv_update_subset (&riscv_rps_as, "-c");
+ riscv_reset_subsets_list_arch_str ();
+ riscv_set_rvc (false);
+ }
else if (strcmp (name, "pic") == 0)
riscv_opts.pic = true;
else if (strcmp (name, "nopic") == 0)
riscv_opts.csr_check = true;
else if (strcmp (name, "no-csr-check") == 0)
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_reset_subsets_list_arch_str ();
+
+ riscv_set_rvc (false);
+ if (riscv_subset_supports (&riscv_rps_as, "c"))
+ riscv_set_rvc (true);
+
+ if (riscv_subset_supports (&riscv_rps_as, "ztso"))
+ riscv_set_tso ();
+ }
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_rps_as.subset_list;
riscv_opts_stack = s;
+ riscv_rps_as.subset_list = riscv_copy_subset_list (s->subset_list);
}
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_rps_as.subset_list;
riscv_opts_stack = s->next;
+ riscv_opts = s->options;
+ riscv_rps_as.subset_list = s->subset_list;
+ riscv_release_subset_list (release_subsets);
free (s);
}
}
else
{
- as_warn (_("unrecognized .option directive: %s\n"), name);
+ as_warn (_("unrecognized .option directive: %s"), name);
}
*input_line_pointer = ch;
demand_empty_rest_of_line ();
if (!riscv_opts.relax)
return false;
+ /* Maybe we should use frag_var to create a new rs_align_code fragment,
+ rather than just use frag_more to handle an alignment here? So that we
+ don't need to call riscv_mapping_state again later, and then only need
+ to check frag->fr_type to see if it is frag_align_code. */
nops = frag_more (worst_case_bytes);
ex.X_op = O_constant;
fix_new_exp (frag_now, nops - frag_now->fr_literal, 0,
&ex, false, BFD_RELOC_RISCV_ALIGN);
- riscv_mapping_state (MAP_INSN, worst_case_bytes);
+ riscv_mapping_state (MAP_INSN, worst_case_bytes, true/* fr_align_code */);
+
+ /* 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;
}
case rs_fill:
case rs_align:
case rs_align_test:
- riscv_mapping_state (MAP_DATA, max_chars);
+ riscv_mapping_state (MAP_DATA, max_chars, false/* fr_align_code */);
break;
case rs_align_code:
- riscv_mapping_state (MAP_INSN, max_chars);
+ riscv_mapping_state (MAP_INSN, max_chars, true/* fr_align_code */);
break;
default:
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\
"));
}
if ((reg = reg_lookup_internal (regname, RCLASS_FPR)) >= 0)
return reg + 32;
+ if ((reg = reg_lookup_internal (regname, RCLASS_VECR)) >= 0)
+ return reg + 96;
+
/* CSRs are numbered 4096 -> 8191. */
if ((reg = reg_lookup_internal (regname, RCLASS_CSR)) >= 0)
return reg + 4096;
riscv_elf_final_processing (void)
{
riscv_set_abi_by_arch ();
+ riscv_release_subset_list (riscv_rps_as.subset_list);
elf_elfheader (stdoutput)->e_flags |= elf_flags;
}
char *save_in = input_line_pointer;
expression (&exp);
- if (exp.X_op != O_constant)
- as_bad (_("non-constant .%cleb128 is not supported"), sign ? 's' : 'u');
+ if (sign && exp.X_op != O_constant)
+ as_bad (_("non-constant .sleb128 is not supported"));
+ else if (!sign && exp.X_op != O_constant && exp.X_op != O_subtract)
+ as_bad (_(".uleb128 only supports constant or subtract expressions"));
+
demand_empty_rest_of_line ();
input_line_pointer = save_in;
save_c = *input_line_pointer;
*input_line_pointer = '\0';
- riscv_mapping_state (MAP_INSN, 0);
+ riscv_mapping_state (MAP_INSN, 0, false/* fr_align_code */);
- const char *error = riscv_ip (str, &insn, &imm_expr,
+ struct riscv_ip_error error = riscv_ip (str, &insn, &imm_expr,
&imm_reloc, insn_type_hash);
- if (error)
+ if (error.msg)
{
char *save_in = input_line_pointer;
- error = riscv_ip_hardcode (str, &insn, &imm_expr, error);
+ error.msg = riscv_ip_hardcode (str, &insn, &imm_expr, error.msg);
input_line_pointer = save_in;
}
- if (error)
- as_bad ("%s `%s'", error, str);
+ if (error.msg)
+ {
+ 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
{
gas_assert (insn.insn_mo->pinfo != INSN_MACRO);
unsigned int i;
/* Re-write architecture elf attribute. */
- arch_str = riscv_arch_str (xlen, &riscv_subsets);
+ arch_str = riscv_rps_as.subset_list->arch_str;
bfd_elf_add_proc_attr_string (stdoutput, Tag_RISCV_arch, arch_str);
- xfree ((void *) arch_str);
/* For the file without any instruction, we don't set the default_priv_spec
according to the privileged elf attributes since the md_assemble isn't
riscv_write_out_attrs ();
}
+/* Scan uleb128 subtraction expressions and insert fixups for them.
+ e.g., .uleb128 .L1 - .L0
+ Because relaxation may change the value of the subtraction, we
+ must resolve them at link-time. */
+
+static void
+riscv_insert_uleb128_fixes (bfd *abfd ATTRIBUTE_UNUSED,
+ asection *sec, void *xxx ATTRIBUTE_UNUSED)
+{
+ segment_info_type *seginfo = seg_info (sec);
+ struct frag *fragP;
+
+ subseg_set (sec, 0);
+
+ for (fragP = seginfo->frchainP->frch_root;
+ fragP; fragP = fragP->fr_next)
+ {
+ expressionS *exp, *exp_dup;
+
+ if (fragP->fr_type != rs_leb128 || fragP->fr_symbol == NULL)
+ continue;
+
+ exp = symbol_get_value_expression (fragP->fr_symbol);
+
+ if (exp->X_op != O_subtract)
+ continue;
+
+ /* Only unsigned leb128 can be handled. */
+ gas_assert (fragP->fr_subtype == 0);
+ exp_dup = xmemdup (exp, sizeof (*exp), sizeof (*exp));
+ exp_dup->X_op = O_symbol;
+ exp_dup->X_op_symbol = NULL;
+
+ /* Insert relocations to resolve the subtraction at link-time.
+ Emit the SET relocation first in riscv. */
+ exp_dup->X_add_symbol = exp->X_add_symbol;
+ fix_new_exp (fragP, fragP->fr_fix, 0,
+ exp_dup, 0, BFD_RELOC_RISCV_SET_ULEB128);
+ exp_dup->X_add_symbol = exp->X_op_symbol;
+ fix_new_exp (fragP, fragP->fr_fix, 0,
+ exp_dup, 0, BFD_RELOC_RISCV_SUB_ULEB128);
+ }
+}
+
/* Called after all assembly has been done. */
void
-riscv_md_end (void)
+riscv_md_finish (void)
{
riscv_set_public_attributes ();
+ if (riscv_opts.relax)
+ bfd_map_over_sections (stdoutput, riscv_insert_uleb128_fixes, NULL);
}
/* Adjust the symbol table. */
}
}
+/* 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 },
};