/* tc-riscv.c -- RISC-V assembler
- Copyright (C) 2011-2021 Free Software Foundation, Inc.
+ Copyright (C) 2011-2022 Free Software Foundation, Inc.
Contributed by Andrew Waterman (andrew@sifive.com).
Based on MIPS target.
fixS *fixp;
};
+/* All RISC-V CSR belong to one of these classes. */
+enum riscv_csr_class
+{
+ CSR_CLASS_NONE,
+
+ CSR_CLASS_I,
+ CSR_CLASS_I_32, /* rv32 only */
+ CSR_CLASS_F, /* f-ext only */
+ CSR_CLASS_ZKR, /* zkr only */
+ CSR_CLASS_V, /* rvv only */
+ CSR_CLASS_DEBUG, /* debug CSR */
+ CSR_CLASS_H, /* hypervisor */
+ CSR_CLASS_H_32, /* hypervisor, rv32 only */
+ CSR_CLASS_SMSTATEEN, /* Smstateen only */
+ CSR_CLASS_SMSTATEEN_AND_H, /* Smstateen only (with H) */
+ CSR_CLASS_SMSTATEEN_32, /* Smstateen RV32 only */
+ CSR_CLASS_SMSTATEEN_AND_H_32, /* Smstateen RV32 only (with H) */
+ CSR_CLASS_SSCOFPMF, /* Sscofpmf only */
+ CSR_CLASS_SSCOFPMF_32, /* Sscofpmf RV32 only */
+ CSR_CLASS_SSTC, /* Sstc only */
+ CSR_CLASS_SSTC_AND_H, /* Sstc only (with H) */
+ CSR_CLASS_SSTC_32, /* Sstc RV32 only */
+ CSR_CLASS_SSTC_AND_H_32, /* Sstc RV32 only (with H) */
+};
+
+/* This structure holds all restricted conditions for a CSR. */
+struct riscv_csr_extra
+{
+ /* Class to which this CSR belongs. Used to decide whether or
+ not this CSR is legal in the current -march context. */
+ enum riscv_csr_class csr_class;
+
+ /* CSR may have differnet numbers in the previous priv spec. */
+ unsigned address;
+
+ /* Record the CSR is defined/valid in which versions. */
+ 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 PRIV_SPEC_CLASS_DRAFT. */
+ enum riscv_spec_class abort_version;
+
+ /* The CSR may have more than one setting. */
+ struct riscv_csr_extra *next;
+};
+
+/* This structure contains information about errors that occur within the
+ riscv_ip function */
+struct riscv_ip_error
+{
+ /* General error message */
+ const char* msg;
+
+ /* Statement that caused the error */
+ char* statement;
+
+ /* Missing extension that needs to be enabled */
+ const char* missing_ext;
+};
+
#ifndef DEFAULT_ARCH
#define DEFAULT_ARCH "riscv64"
#endif
#endif
/* Let riscv_after_parse_args set the default value according to xlen. */
-
#ifndef DEFAULT_RISCV_ARCH_WITH_EXT
#define DEFAULT_RISCV_ARCH_WITH_EXT NULL
#endif
-/* The default ISA spec is set to 2.2 rather than the lastest version.
- The reason is that compiler generates the ISA string with fixed 2p0
- verisons only for the RISCV ELF architecture attributes, but not for
- the -march option. Therefore, we should update the compiler or linker
- to resolve this problem. */
-
+/* 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 const char default_arch[] = DEFAULT_ARCH;
static const char *default_arch_with_ext = DEFAULT_RISCV_ARCH_WITH_EXT;
-static enum riscv_isa_spec_class default_isa_spec = ISA_SPEC_CLASS_NONE;
-static enum riscv_priv_spec_class default_priv_spec = PRIV_SPEC_CLASS_NONE;
+static enum riscv_spec_class default_isa_spec = ISA_SPEC_CLASS_NONE;
+static enum riscv_spec_class default_priv_spec = PRIV_SPEC_CLASS_NONE;
-static unsigned xlen = 0; /* width of an x-register */
-static unsigned abi_xlen = 0; /* width of a pointer in the ABI */
-static bfd_boolean rve_abi = FALSE;
-enum float_abi {
+static unsigned xlen = 0; /* The width of an x-register. */
+static unsigned abi_xlen = 0; /* The width of a pointer in the ABI. */
+static bool rve_abi = false;
+enum float_abi
+{
FLOAT_ABI_DEFAULT = -1,
FLOAT_ABI_SOFT,
FLOAT_ABI_SINGLE,
static unsigned elf_flags = 0;
-/* Set the default_isa_spec. Return 0 if the input spec string isn't
- supported. Otherwise, return 1. */
+/* Set the default_isa_spec. Return 0 if the spec isn't supported.
+ Otherwise, return 1. */
static int
riscv_set_default_isa_spec (const char *s)
{
- enum riscv_isa_spec_class class;
- if (!riscv_get_isa_spec_class (s, &class))
+ enum riscv_spec_class class = ISA_SPEC_CLASS_NONE;
+ RISCV_GET_ISA_SPEC_CLASS (s, class);
+ if (class == ISA_SPEC_CLASS_NONE)
{
- as_bad ("Unknown default ISA spec `%s' set by "
- "-misa-spec or --with-isa-spec", s);
+ as_bad ("unknown default ISA spec `%s' set by "
+ "-misa-spec or --with-isa-spec", s);
return 0;
}
else
return 1;
}
-/* Set the default_priv_spec, assembler will find the suitable CSR address
- according to default_priv_spec. We will try to check priv attributes if
- the input string is NULL. Return 0 if the input priv spec string isn't
- supported. Otherwise, return 1. */
+/* Set the default_priv_spec. Find the privileged elf attributes when
+ the input string is NULL. Return 0 if the spec isn't supported.
+ Otherwise, return 1. */
static int
riscv_set_default_priv_spec (const char *s)
{
- enum riscv_priv_spec_class class;
+ enum riscv_spec_class class = PRIV_SPEC_CLASS_NONE;
unsigned major, minor, revision;
obj_attribute *attr;
- /* Find the corresponding priv spec class. */
- if (riscv_get_priv_spec_class (s, &class))
+ RISCV_GET_PRIV_SPEC_CLASS (s, class);
+ if (class != PRIV_SPEC_CLASS_NONE)
{
default_priv_spec = class;
return 1;
if (s != NULL)
{
- as_bad (_("Unknown default privilege spec `%s' set by "
- "-mpriv-spec or --with-priv-spec"), s);
+ as_bad (_("unknown default privileged spec `%s' set by "
+ "-mpriv-spec or --with-priv-spec"), s);
return 0;
}
- /* Try to set the default_priv_spec according to the priv attributes. */
+ /* Set the default_priv_spec by the privileged elf attributes. */
attr = elf_known_obj_attributes_proc (stdoutput);
major = (unsigned) attr[Tag_RISCV_priv_spec].i;
minor = (unsigned) attr[Tag_RISCV_priv_spec_minor].i;
revision = (unsigned) attr[Tag_RISCV_priv_spec_revision].i;
+ /* Version 0.0.0 is the default value and meningless. */
+ if (major == 0 && minor == 0 && revision == 0)
+ return 1;
- if (riscv_get_priv_spec_class_from_numbers (major,
- minor,
- revision,
- &class))
+ riscv_get_priv_spec_class_from_numbers (major, minor, revision, &class);
+ if (class != PRIV_SPEC_CLASS_NONE)
{
- /* The priv attributes setting 0.0.0 is meaningless. We should have set
- the default_priv_spec by md_parse_option and riscv_after_parse_args,
- so just skip the following setting. */
- if (class == PRIV_SPEC_CLASS_NONE)
- return 1;
-
default_priv_spec = class;
return 1;
}
- /* Still can not find the priv spec class. */
- as_bad (_("Unknown default privilege spec `%d.%d.%d' set by "
- "privilege attributes"), major, minor, revision);
+ /* Still can not find the privileged spec class. */
+ as_bad (_("unknown default privileged spec `%d.%d.%d' set by "
+ "privileged elf attributes"), major, minor, revision);
return 0;
}
/* This is the set of options which the .option pseudo-op may modify. */
-
struct riscv_set_options
{
int pic; /* Generate position-independent code. */
int rvc; /* Generate RVC code. */
- int rve; /* Generate RVE code. */
int relax; /* Emit relocs the linker is allowed to relax. */
- int arch_attr; /* Emit arch attribute. */
+ int arch_attr; /* Emit architecture and privileged elf attributes. */
int csr_check; /* Enable the CSR checking. */
};
static struct riscv_set_options riscv_opts =
{
- 0, /* pic */
- 0, /* rvc */
- 0, /* rve */
- 1, /* relax */
+ 0, /* pic */
+ 0, /* rvc */
+ 1, /* relax */
DEFAULT_RISCV_ATTR, /* arch_attr */
- 0. /* csr_check */
+ 0, /* csr_check */
};
+/* Enable or disable the rvc flags for riscv_opts. Turn on the rvc flag
+ for elf_flags once we have enabled c extension. */
+
static void
-riscv_set_rvc (bfd_boolean rvc_value)
+riscv_set_rvc (bool rvc_value)
{
if (rvc_value)
elf_flags |= EF_RISCV_RVC;
riscv_opts.rvc = rvc_value;
}
+/* Turn on the tso flag for elf_flags once we have enabled ztso extension. */
+
static void
-riscv_set_rve (bfd_boolean rve_value)
+riscv_set_tso ()
{
- riscv_opts.rve = rve_value;
+ elf_flags |= EF_RISCV_TSO;
}
-static riscv_subset_list_t riscv_subsets;
-
-static bfd_boolean
-riscv_subset_supports (const char *feature)
+/* This linked list records all enabled extensions, which are parsed from
+ the architecture string. The architecture string can be set by the
+ -march option, the elf architecture attributes, and the --with-arch
+ configure option. */
+static riscv_subset_list_t *riscv_subsets = NULL;
+static riscv_parse_subset_t riscv_rps_as =
{
- struct riscv_subset_t *subset;
-
- if (riscv_opts.rvc && (strcasecmp (feature, "c") == 0))
- return TRUE;
-
- return riscv_lookup_subset (&riscv_subsets, feature, &subset);
-}
+ NULL, /* subset_list, we will set it later once
+ riscv_opts_stack is created or updated. */
+ as_bad, /* error_handler. */
+ &xlen, /* xlen. */
+ &default_isa_spec, /* isa_spec. */
+ true, /* check_unknown_prefixed_ext. */
+};
-static bfd_boolean
-riscv_multi_subset_supports (enum riscv_insn_class insn_class)
+/* This structure is used to hold a stack of .option values. */
+struct riscv_option_stack
{
- 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");
+ struct riscv_option_stack *next;
+ struct riscv_set_options options;
+ riscv_subset_list_t *subset_list;
+};
- default:
- as_fatal ("Unreachable");
- return FALSE;
- }
-}
+static struct riscv_option_stack *riscv_opts_stack = NULL;
-/* Handle of the extension with version hash table. */
-static htab_t ext_version_hash = NULL;
+/* Set which ISA and extensions are available. */
-static htab_t
-init_ext_version_hash (const struct riscv_ext_version *table)
+static void
+riscv_set_arch (const char *s)
{
- int i = 0;
- htab_t hash = str_htab_create ();
-
- while (table[i].name)
+ if (s != NULL && strcmp (s, "") == 0)
{
- const char *name = table[i].name;
- if (str_hash_insert (hash, name, &table[i], 0) != NULL)
- as_fatal (_("duplicate %s"), name);
-
- i++;
- while (table[i].name
- && strcmp (table[i].name, name) == 0)
- i++;
+ as_bad (_("the architecture string of -march and elf architecture "
+ "attributes cannot be empty"));
+ return;
}
- return hash;
-}
-
-static void
-riscv_get_default_ext_version (const char *name,
- int *major_version,
- int *minor_version)
-{
- struct riscv_ext_version *ext;
-
- if (name == NULL || default_isa_spec == ISA_SPEC_CLASS_NONE)
- return;
-
- ext = (struct riscv_ext_version *) str_hash_find (ext_version_hash, name);
- while (ext
- && ext->name
- && strcmp (ext->name, name) == 0)
+ if (riscv_subsets == NULL)
{
- if (ext->isa_spec_class == default_isa_spec)
- {
- *major_version = ext->major_version;
- *minor_version = ext->minor_version;
- return;
- }
- ext++;
+ riscv_subsets = XNEW (riscv_subset_list_t);
+ riscv_subsets->head = NULL;
+ riscv_subsets->tail = NULL;
+ riscv_rps_as.subset_list = riscv_subsets;
}
-}
-
-/* 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;
+ riscv_release_subset_list (riscv_subsets);
+ riscv_parse_subset (&riscv_rps_as, s);
- if (s == NULL)
- return;
+ riscv_set_rvc (false);
+ if (riscv_subset_supports (&riscv_rps_as, "c"))
+ riscv_set_rvc (true);
- riscv_release_subset_list (&riscv_subsets);
- riscv_parse_subset (&rps, s);
+ if (riscv_subset_supports (&riscv_rps_as, "ztso"))
+ riscv_set_tso ();
}
-/* Indicate -mabi= option is explictly set. */
-static bfd_boolean explicit_mabi = FALSE;
+/* 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, bfd_boolean rve)
+riscv_set_abi (unsigned new_xlen, enum float_abi new_float_abi, bool rve)
{
abi_xlen = new_xlen;
float_abi = new_float_abi;
rve_abi = rve;
}
-/* If the -mabi option isn't set, then we set the abi according to the arch
- string. Otherwise, check if there are conflicts between architecture
- and abi setting. */
+/* If the -mabi option isn't set, then set the abi according to the
+ ISA string. Otherwise, check if there is any conflict. */
static void
riscv_set_abi_by_arch (void)
{
if (!explicit_mabi)
{
- if (riscv_subset_supports ("q"))
- riscv_set_abi (xlen, FLOAT_ABI_QUAD, FALSE);
- else if (riscv_subset_supports ("d"))
- riscv_set_abi (xlen, FLOAT_ABI_DOUBLE, FALSE);
+ if (riscv_subset_supports (&riscv_rps_as, "q"))
+ riscv_set_abi (xlen, FLOAT_ABI_QUAD, false);
+ else if (riscv_subset_supports (&riscv_rps_as, "d"))
+ riscv_set_abi (xlen, FLOAT_ABI_DOUBLE, false);
+ else if (riscv_subset_supports (&riscv_rps_as, "e"))
+ riscv_set_abi (xlen, FLOAT_ABI_SOFT, true);
else
- riscv_set_abi (xlen, FLOAT_ABI_SOFT, FALSE);
+ riscv_set_abi (xlen, FLOAT_ABI_SOFT, false);
}
else
{
as_bad ("can't have %d-bit ABI on %d-bit ISA", abi_xlen, xlen);
else if (abi_xlen < xlen)
as_bad ("%d-bit ABI not yet supported on %d-bit ISA", abi_xlen, xlen);
+
+ if (riscv_subset_supports (&riscv_rps_as, "e") && !rve_abi)
+ as_bad ("only the ilp32e ABI is supported for e extension");
+
+ if (float_abi == FLOAT_ABI_SINGLE
+ && !riscv_subset_supports (&riscv_rps_as, "f"))
+ as_bad ("ilp32f/lp64f ABI can't be used when f extension "
+ "isn't supported");
+ else if (float_abi == FLOAT_ABI_DOUBLE
+ && !riscv_subset_supports (&riscv_rps_as, "d"))
+ as_bad ("ilp32d/lp64d ABI can't be used when d extension "
+ "isn't supported");
+ else if (float_abi == FLOAT_ABI_QUAD
+ && !riscv_subset_supports (&riscv_rps_as, "q"))
+ as_bad ("ilp32q/lp64q ABI can't be used when q extension "
+ "isn't supported");
}
/* Update the EF_RISCV_FLOAT_ABI field of elf_flags. */
static htab_t insn_type_hash = NULL;
/* This array holds the chars that always start a comment. If the
- pre-processor is disabled, these aren't very useful */
+ pre-processor is disabled, these aren't very useful. */
const char comment_chars[] = "#";
/* This array holds the chars that only start a comment at the beginning of
a line. If the line seems to have the form '# 123 filename'
- .line and .file directives will appear in the pre-processed output */
-/* Note that input_file.c hand checks for '#' at the beginning of the
+ .line and .file directives will appear in the pre-processed output
+
+ Note that input_file.c hand checks for '#' at the beginning of the
first line of the input file. This is because the compiler outputs
- #NO_APP at the beginning of its output. */
-/* Also note that C style comments are always supported. */
+ #NO_APP at the beginning of its output.
+
+ Also note that C style comments are always supported. */
const char line_comment_chars[] = "#";
/* This array holds machine specific line separator characters. */
const char line_separator_chars[] = ";";
-/* Chars that can be used to separate mant from exp in floating point nums */
+/* Chars that can be used to separate mant from exp in floating point nums. */
const char EXP_CHARS[] = "eE";
-/* Chars that mean this number is a floating point constant */
-/* As in 0f12.456 */
-/* or 0d1.2345e12 */
-const char FLT_CHARS[] = "rRsSfFdDxXpP";
+/* Chars that mean this number is a floating point constant.
+ As in 0f12.456 or 0d1.2345e12. */
+const char FLT_CHARS[] = "rRsSfFdDxXpPhH";
/* Indicate we are already assemble any instructions or not. */
-static bfd_boolean start_assemble = FALSE;
+static bool start_assemble = false;
-/* Indicate ELF attributes are explictly set. */
-static bfd_boolean explicit_attr = FALSE;
+/* Indicate ELF attributes are explicitly set. */
+static bool explicit_attr = false;
-/* Indicate CSR or priv instructions are explictly used. */
-static bfd_boolean explicit_priv_attr = FALSE;
+/* Indicate CSR or priv instructions are explicitly used. */
+static bool explicit_priv_attr = false;
+
+static char *expr_end;
/* Macros for encoding relaxation state for RVC branches and far jumps. */
#define RELAX_BRANCH_ENCODE(uncond, rvc, length) \
#define OPCODE_MATCHES(OPCODE, OP) \
(((OPCODE) & MASK_##OP) == MATCH_##OP)
-static char *expr_end;
+/* Create a new mapping symbol for the transition to STATE. */
+
+static void
+make_mapping_symbol (enum riscv_seg_mstate state,
+ valueT value,
+ fragS *frag)
+{
+ const char *name;
+ switch (state)
+ {
+ case MAP_DATA:
+ name = "$d";
+ break;
+ case MAP_INSN:
+ name = "$x";
+ break;
+ default:
+ abort ();
+ }
+
+ symbolS *symbol = symbol_new (name, now_seg, frag, value);
+ symbol_get_bfdsym (symbol)->flags |= (BSF_NO_FLAGS | BSF_LOCAL);
+
+ /* If .fill or other data filling directive generates zero sized data,
+ or we are adding odd alignemnts, then the mapping symbol for the
+ following code will have the same value. */
+ if (value == 0)
+ {
+ if (frag->tc_frag_data.first_map_symbol != NULL)
+ {
+ know (S_GET_VALUE (frag->tc_frag_data.first_map_symbol)
+ == S_GET_VALUE (symbol));
+ /* Remove the old one. */
+ symbol_remove (frag->tc_frag_data.first_map_symbol,
+ &symbol_rootP, &symbol_lastP);
+ }
+ frag->tc_frag_data.first_map_symbol = symbol;
+ }
+ if (frag->tc_frag_data.last_map_symbol != NULL)
+ {
+ /* The mapping symbols should be added in offset order. */
+ know (S_GET_VALUE (frag->tc_frag_data.last_map_symbol)
+ <= S_GET_VALUE (symbol));
+ /* Remove the old one. */
+ if (S_GET_VALUE (frag->tc_frag_data.last_map_symbol)
+ == S_GET_VALUE (symbol))
+ symbol_remove (frag->tc_frag_data.last_map_symbol,
+ &symbol_rootP, &symbol_lastP);
+ }
+ frag->tc_frag_data.last_map_symbol = symbol;
+}
+
+/* Set the mapping state for frag_now. */
+
+void
+riscv_mapping_state (enum riscv_seg_mstate to_state,
+ int max_chars)
+{
+ enum riscv_seg_mstate from_state =
+ seg_info (now_seg)->tc_segment_info_data.map_state;
+
+ if (!SEG_NORMAL (now_seg)
+ /* For now I only add the mapping symbols to text sections.
+ Therefore, the dis-assembler only show the actual contents
+ distribution for text. Other sections will be shown as
+ data without the details. */
+ || !subseg_text_p (now_seg))
+ return;
+
+ /* The mapping symbol should be emitted if not in the right
+ mapping state */
+ if (from_state == to_state)
+ return;
+
+ valueT value = (valueT) (frag_now_fix () - max_chars);
+ seg_info (now_seg)->tc_segment_info_data.map_state = to_state;
+ make_mapping_symbol (to_state, value, frag_now);
+}
+
+/* Add the odd bytes of paddings for riscv_handle_align. */
+
+static void
+riscv_add_odd_padding_symbol (fragS *frag)
+{
+ /* If there was already a mapping symbol, it should be
+ removed in the make_mapping_symbol. */
+ make_mapping_symbol (MAP_DATA, frag->fr_fix, frag);
+ make_mapping_symbol (MAP_INSN, frag->fr_fix + 1, frag);
+}
+
+/* Remove any excess mapping symbols generated for alignment frags in
+ SEC. We may have created a mapping symbol before a zero byte
+ alignment; remove it if there's a mapping symbol after the
+ alignment. */
+
+static void
+riscv_check_mapping_symbols (bfd *abfd ATTRIBUTE_UNUSED,
+ asection *sec,
+ void *dummy ATTRIBUTE_UNUSED)
+{
+ segment_info_type *seginfo = seg_info (sec);
+ fragS *fragp;
+
+ if (seginfo == NULL || seginfo->frchainP == NULL)
+ return;
+
+ for (fragp = seginfo->frchainP->frch_root;
+ fragp != NULL;
+ fragp = fragp->fr_next)
+ {
+ symbolS *last = fragp->tc_frag_data.last_map_symbol;
+ fragS *next = fragp->fr_next;
+
+ if (last == NULL || next == NULL)
+ continue;
+
+ /* Check the last mapping symbol if it is at the boundary of
+ fragment. */
+ if (S_GET_VALUE (last) < next->fr_address)
+ continue;
+ know (S_GET_VALUE (last) == next->fr_address);
+
+ do
+ {
+ if (next->tc_frag_data.first_map_symbol != NULL)
+ {
+ /* The last mapping symbol overlaps with another one
+ which at the start of the next frag. */
+ symbol_remove (last, &symbol_rootP, &symbol_lastP);
+ break;
+ }
+
+ if (next->fr_next == NULL)
+ {
+ /* The last mapping symbol is at the end of the section. */
+ know (next->fr_fix == 0 && next->fr_var == 0);
+ symbol_remove (last, &symbol_rootP, &symbol_lastP);
+ break;
+ }
+
+ /* Since we may have empty frags without any mapping symbols,
+ keep looking until the non-empty frag. */
+ if (next->fr_address != next->fr_next->fr_address)
+ break;
+
+ next = next->fr_next;
+ }
+ while (next != NULL);
+ }
+}
/* The default target format to use. */
const char *
riscv_target_format (void)
{
- return xlen == 64 ? "elf64-littleriscv" : "elf32-littleriscv";
+ if (target_big_endian)
+ return xlen == 64 ? "elf64-bigriscv" : "elf32-bigriscv";
+ else
+ return xlen == 64 ? "elf64-littleriscv" : "elf32-littleriscv";
}
/* Return the length of instruction INSN. */
install_insn (const struct riscv_cl_insn *insn)
{
char *f = insn->frag->fr_literal + insn->where;
- md_number_to_chars (f, insn->insn_opcode, insn_length (insn));
+ 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. */
static htab_t opcode_names_hash = NULL;
/* Initialization for hash table of opcode name. */
+
static void
init_opcode_names_hash (void)
{
for (opcode = &opcode_name_list[0]; opcode->name != NULL; ++opcode)
if (str_hash_insert (opcode_names_hash, opcode->name, opcode, 0) != NULL)
- as_fatal (_("duplicate %s"), opcode->name);
+ as_fatal (_("internal: duplicate %s"), opcode->name);
}
-/* Find `s` is a valid opcode name or not,
- return the opcode name info if found. */
+/* Find `s` is a valid opcode name or not, return the opcode name info
+ if found. */
+
static const struct opcode_name_t *
opcode_name_lookup (char **s)
{
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
{
void *hash = ENCODE_REG_HASH (class, n);
if (str_hash_insert (reg_names_hash, name, hash, 0) != NULL)
- as_fatal (_("duplicate %s"), name);
+ as_fatal (_("internal: duplicate %s"), name);
}
static void
}
/* Init hash table csr_extra_hash to handle CSR. */
+
static void
riscv_init_csr_hash (const char *name,
- unsigned address,
- enum riscv_csr_class class,
- enum riscv_priv_spec_class define_version,
- enum riscv_priv_spec_class abort_version)
+ unsigned address,
+ enum riscv_csr_class class,
+ enum riscv_spec_class define_version,
+ enum riscv_spec_class abort_version)
{
struct riscv_csr_extra *entry, *pre_entry;
- bfd_boolean need_enrty = TRUE;
+ bool need_enrty = true;
pre_entry = NULL;
entry = (struct riscv_csr_extra *) str_hash_find (csr_extra_hash, name);
while (need_enrty && entry != NULL)
{
if (entry->csr_class == class
- && entry->address == address
- && entry->define_version == define_version
- && entry->abort_version == abort_version)
- need_enrty = FALSE;
+ && entry->address == address
+ && entry->define_version == define_version
+ && entry->abort_version == abort_version)
+ need_enrty = false;
pre_entry = entry;
entry = entry->next;
}
-
- /* Duplicate setting for the CSR, just return and do nothing. */
+
+ /* Duplicate 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;
entry->abort_version = abort_version;
entry->next = NULL;
- /* If the CSR hasn't been inserted in the hash table, then insert it.
- Otherwise, attach the extra information to the entry which is already
- in the hash table. */
if (pre_entry == NULL)
str_hash_insert (csr_extra_hash, name, entry, 0);
else
pre_entry->next = entry;
}
-/* Return the suitable CSR address after checking the ISA dependency and
- priv spec versions. */
+/* Return the CSR address after checking the ISA dependency and
+ the privileged spec version.
+
+ There are one warning and two errors for CSR,
+
+ Invalid CSR: the CSR was defined, but isn't allowed for the current ISA
+ or the privileged spec, report warning only if -mcsr-check is set.
+ Unknown CSR: the CSR has never been defined, report error.
+ Improper CSR: the CSR number over the range (> 0xfff), report error. */
static unsigned int
riscv_csr_address (const char *csr_name,
{
struct riscv_csr_extra *saved_entry = entry;
enum riscv_csr_class csr_class = entry->csr_class;
- bfd_boolean need_check_version = TRUE;
- bfd_boolean result = TRUE;
+ bool need_check_version = false;
+ bool is_rv32_only = false;
+ bool is_h_required = false;
+ const char* extension = NULL;
switch (csr_class)
{
+ case CSR_CLASS_I_32:
+ is_rv32_only = true;
+ /* Fall through. */
case CSR_CLASS_I:
- result = riscv_subset_supports ("i");
+ need_check_version = true;
+ extension = "i";
break;
- case CSR_CLASS_I_32:
- result = (xlen == 32 && riscv_subset_supports ("i"));
+ case CSR_CLASS_H_32:
+ is_rv32_only = true;
+ /* Fall through. */
+ case CSR_CLASS_H:
+ extension = "h";
break;
case CSR_CLASS_F:
- result = riscv_subset_supports ("f");
- need_check_version = FALSE;
+ extension = "f";
+ break;
+ case CSR_CLASS_ZKR:
+ extension = "zkr";
+ break;
+ case CSR_CLASS_V:
+ extension = "zve32x";
+ break;
+ case CSR_CLASS_SMSTATEEN:
+ case CSR_CLASS_SMSTATEEN_AND_H:
+ case CSR_CLASS_SMSTATEEN_32:
+ case CSR_CLASS_SMSTATEEN_AND_H_32:
+ is_rv32_only = (csr_class == CSR_CLASS_SMSTATEEN_32
+ || csr_class == CSR_CLASS_SMSTATEEN_AND_H_32);
+ is_h_required = (csr_class == CSR_CLASS_SMSTATEEN_AND_H
+ || csr_class == CSR_CLASS_SMSTATEEN_AND_H_32);
+ extension = "smstateen";
+ break;
+ case CSR_CLASS_SSCOFPMF_32:
+ is_rv32_only = true;
+ /* Fall through. */
+ case CSR_CLASS_SSCOFPMF:
+ extension = "sscofpmf";
+ break;
+ case CSR_CLASS_SSTC:
+ case CSR_CLASS_SSTC_AND_H:
+ case CSR_CLASS_SSTC_32:
+ case CSR_CLASS_SSTC_AND_H_32:
+ is_rv32_only = (csr_class == CSR_CLASS_SSTC_32
+ || csr_class == CSR_CLASS_SSTC_AND_H_32);
+ is_h_required = (csr_class == CSR_CLASS_SSTC_AND_H
+ || csr_class == CSR_CLASS_SSTC_AND_H_32);
+ extension = "sstc";
break;
case CSR_CLASS_DEBUG:
- need_check_version = FALSE;
break;
default:
as_bad (_("internal: bad RISC-V CSR class (0x%x)"), csr_class);
}
- /* Don't report the ISA conflict when -mcsr-check isn't set. */
- 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)
{
|| (default_priv_spec >= entry->define_version
&& default_priv_spec < entry->abort_version))
{
- /* Find the suitable CSR according to the specific version. */
+ /* Find the CSR according to the specific version. */
return entry->address;
}
entry = entry->next;
}
- /* We can not find the suitable CSR address according to the privilege
- version. Therefore, we use the last defined value. Report the warning
- only when the -mcsr-check is set. Enable the -mcsr-check is recommended,
- otherwise, you may get the unexpected CSR address. */
+ /* Can not find the CSR address from the chosen privileged version,
+ so use the newly defined value. */
if (riscv_opts.csr_check)
{
- const char *priv_name = riscv_get_priv_spec_name (default_priv_spec);
-
+ const char *priv_name = NULL;
+ RISCV_GET_PRIV_SPEC_NAME (priv_name, default_priv_spec);
if (priv_name != NULL)
- as_warn (_("Invalid CSR `%s' for the privilege spec `%s'"),
+ as_warn (_("invalid CSR `%s' for the privileged spec `%s'"),
csr_name, priv_name);
}
return saved_entry->address;
}
-/* Once the CSR is defined, including the old privilege spec, then we call
- riscv_csr_class_check and riscv_csr_version_check to do the further checking
- and get the corresponding address. Return -1 if the CSR is never been
- defined. Otherwise, return the address. */
+/* Return -1 if the CSR has never been defined. Otherwise, return
+ the address. */
static unsigned int
reg_csr_lookup_internal (const char *s)
if (r == NULL)
return -1U;
- /* We just report the warning when the CSR is invalid. "Invalid CSR" means
- the CSR was defined, but isn't allowed for the current ISA setting or
- the privilege spec. If the CSR is never been defined, then assembler
- will regard it as a "Unknown CSR" and report error. If user use number
- to set the CSR, but over the range (> 0xfff), then assembler will report
- "Improper CSR" error for it. */
return riscv_csr_address (s, r);
}
if (r == NULL || DECODE_REG_CLASS (r) != class)
return -1;
- if (riscv_opts.rve && class == RCLASS_GPR && DECODE_REG_NUM (r) > 15)
+ if (riscv_subset_supports (&riscv_rps_as, "e")
+ && class == RCLASS_GPR
+ && DECODE_REG_NUM (r) > 15)
return -1;
return DECODE_REG_NUM (r);
}
-static bfd_boolean
+static bool
reg_lookup (char **s, enum reg_class class, unsigned int *regnop)
{
char *e;
return reg >= 0;
}
-static bfd_boolean
+static bool
arg_lookup (char **s, const char *const *array, size_t size, unsigned *regnop)
{
const char *p = strchr (*s, ',');
size_t i, len = p ? (size_t)(p - *s) : strlen (*s);
if (len == 0)
- return FALSE;
+ return false;
for (i = 0; i < size; i++)
if (array[i] != NULL && strncmp (array[i], *s, len) == 0)
{
*regnop = i;
*s += len;
- return TRUE;
+ return true;
}
- return FALSE;
+ return false;
}
+#define USE_BITS(mask,shift) (used_bits |= ((insn_t)(mask) << (shift)))
+
/* For consistency checking, verify that all bits are specified either
by the match/mask part of the instruction definition, or by the
- operand list.
+ operand list. The `length` could be 0, 4 or 8, 0 for auto detection. */
- `length` could be 0, 4 or 8, 0 for auto detection. */
-static bfd_boolean
+static bool
validate_riscv_insn (const struct riscv_opcode *opc, int length)
{
- const char *p = opc->args;
- char c;
+ const char *oparg, *opargStart;
insn_t used_bits = opc->mask;
int insn_width;
insn_t required_bits;
{
as_bad (_("internal: bad RISC-V opcode (mask error): %s %s"),
opc->name, opc->args);
- return FALSE;
+ return false;
}
-#define USE_BITS(mask,shift) (used_bits |= ((insn_t)(mask) << (shift)))
- while (*p)
- switch (c = *p++)
- {
- case 'C': /* RVC */
- switch (c = *p++)
- {
- case 'a': used_bits |= ENCODE_RVC_J_IMM (-1U); break;
- case 'c': break; /* RS1, constrained to equal sp */
- case 'i': used_bits |= ENCODE_RVC_SIMM3(-1U); break;
- case 'j': used_bits |= ENCODE_RVC_IMM (-1U); break;
- case 'o': used_bits |= ENCODE_RVC_IMM (-1U); break;
- case 'k': used_bits |= ENCODE_RVC_LW_IMM (-1U); break;
- case 'l': used_bits |= ENCODE_RVC_LD_IMM (-1U); break;
- case 'm': used_bits |= ENCODE_RVC_LWSP_IMM (-1U); break;
- case 'n': used_bits |= ENCODE_RVC_LDSP_IMM (-1U); break;
- case 'p': used_bits |= ENCODE_RVC_B_IMM (-1U); break;
- case 's': USE_BITS (OP_MASK_CRS1S, OP_SH_CRS1S); break;
- case 't': USE_BITS (OP_MASK_CRS2S, OP_SH_CRS2S); break;
- case 'u': used_bits |= ENCODE_RVC_IMM (-1U); break;
- case 'v': used_bits |= ENCODE_RVC_IMM (-1U); break;
- case 'w': break; /* RS1S, constrained to equal RD */
- case 'x': break; /* RS2S, constrained to equal RD */
- case 'z': break; /* RS2S, contrained to be x0 */
- case 'K': used_bits |= ENCODE_RVC_ADDI4SPN_IMM (-1U); break;
- case 'L': used_bits |= ENCODE_RVC_ADDI16SP_IMM (-1U); break;
- case 'M': used_bits |= ENCODE_RVC_SWSP_IMM (-1U); break;
- case 'N': used_bits |= ENCODE_RVC_SDSP_IMM (-1U); break;
- case 'U': break; /* RS1, constrained to equal RD */
- case 'V': USE_BITS (OP_MASK_CRS2, OP_SH_CRS2); break;
- case '<': used_bits |= ENCODE_RVC_IMM (-1U); break;
- case '>': used_bits |= ENCODE_RVC_IMM (-1U); break;
- case '8': used_bits |= ENCODE_RVC_UIMM8 (-1U); break;
- case 'S': USE_BITS (OP_MASK_CRS1S, OP_SH_CRS1S); break;
- case 'T': USE_BITS (OP_MASK_CRS2, OP_SH_CRS2); break;
- case 'D': USE_BITS (OP_MASK_CRS2S, OP_SH_CRS2S); break;
- case 'F': /* funct */
- switch (c = *p++)
- {
+ 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;
- case 'D': USE_BITS (OP_MASK_RD, OP_SH_RD); break;
- case 'Z': USE_BITS (OP_MASK_RS1, OP_SH_RS1); break;
- case 'E': USE_BITS (OP_MASK_CSR, OP_SH_CSR); break;
- case 'I': break;
- case 'R': USE_BITS (OP_MASK_RS3, OP_SH_RS3); break;
- case 'S': USE_BITS (OP_MASK_RS1, OP_SH_RS1); break;
- case 'U': USE_BITS (OP_MASK_RS1, OP_SH_RS1); /* fallthru */
- case 'T': USE_BITS (OP_MASK_RS2, OP_SH_RS2); break;
- case 'd': USE_BITS (OP_MASK_RD, OP_SH_RD); break;
- case 'm': USE_BITS (OP_MASK_RM, OP_SH_RM); break;
- case 's': USE_BITS (OP_MASK_RS1, OP_SH_RS1); break;
- case 't': USE_BITS (OP_MASK_RS2, OP_SH_RS2); break;
- case 'r': USE_BITS (OP_MASK_RS3, OP_SH_RS3); break;
- case 'P': USE_BITS (OP_MASK_PRED, OP_SH_PRED); break;
- case 'Q': USE_BITS (OP_MASK_SUCC, OP_SH_SUCC); break;
- case 'o':
- case 'j': used_bits |= ENCODE_ITYPE_IMM (-1U); break;
- case 'a': used_bits |= ENCODE_UJTYPE_IMM (-1U); break;
- case 'p': used_bits |= ENCODE_SBTYPE_IMM (-1U); break;
- case 'q': used_bits |= ENCODE_STYPE_IMM (-1U); break;
- case 'u': used_bits |= ENCODE_UTYPE_IMM (-1U); break;
- case 'z': break;
- case '[': break;
- case ']': break;
- case '0': break;
- case '1': break;
- case 'F': /* funct */
- switch (c = *p++)
- {
- case '7': USE_BITS (OP_MASK_FUNCT7, OP_SH_FUNCT7); break;
- case '3': USE_BITS (OP_MASK_FUNCT3, OP_SH_FUNCT3); break;
- case '2': USE_BITS (OP_MASK_FUNCT2, OP_SH_FUNCT2); break;
+ 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 */
- switch (c = *p++)
- {
- case '4': USE_BITS (OP_MASK_OP, OP_SH_OP); break;
- case '2': USE_BITS (OP_MASK_OP2, OP_SH_OP2); break;
+ goto unknown_validate_operand;
+ }
+ break; /* end RVC */
+ case 'V': /* RVV */
+ switch (*++oparg)
+ {
+ case 'd':
+ case 'f': USE_BITS (OP_MASK_VD, OP_SH_VD); break;
+ case 'e': USE_BITS (OP_MASK_VWD, OP_SH_VWD); break;
+ case 's': USE_BITS (OP_MASK_VS1, OP_SH_VS1); break;
+ case 't': USE_BITS (OP_MASK_VS2, OP_SH_VS2); break;
+ case 'u': USE_BITS (OP_MASK_VS1, OP_SH_VS1);
+ USE_BITS (OP_MASK_VS2, OP_SH_VS2); break;
+ case 'v': USE_BITS (OP_MASK_VD, OP_SH_VD);
+ USE_BITS (OP_MASK_VS1, OP_SH_VS1);
+ USE_BITS (OP_MASK_VS2, OP_SH_VS2); break;
+ case '0': break;
+ case 'b': used_bits |= ENCODE_RVV_VB_IMM (-1U); break;
+ case 'c': used_bits |= ENCODE_RVV_VC_IMM (-1U); break;
+ case 'i':
+ case 'j':
+ case 'k': USE_BITS (OP_MASK_VIMM, OP_SH_VIMM); break;
+ case 'm': USE_BITS (OP_MASK_VMASK, OP_SH_VMASK); break;
default:
- as_bad (_("internal: bad RISC-V opcode"
- " (unknown operand type `F%c'): %s %s"),
- c, opc->name, opc->args);
- return FALSE;
- }
- break;
- default:
- as_bad (_("internal: bad RISC-V opcode "
- "(unknown operand type `%c'): %s %s"),
- c, opc->name, opc->args);
- return FALSE;
- }
-#undef USE_BITS
+ goto unknown_validate_operand;
+ }
+ break; /* end RVV */
+ case ',': break;
+ case '(': break;
+ case ')': break;
+ case '<': USE_BITS (OP_MASK_SHAMTW, OP_SH_SHAMTW); break;
+ case '>': USE_BITS (OP_MASK_SHAMT, OP_SH_SHAMT); break;
+ case 'A': break; /* Macro operand, must be symbol. */
+ case 'B': break; /* Macro operand, must be symbol or constant. */
+ case 'I': break; /* Macro operand, must be constant. */
+ case 'D': /* RD, floating point. */
+ case 'd': USE_BITS (OP_MASK_RD, OP_SH_RD); break;
+ case 'y': USE_BITS (OP_MASK_BS, OP_SH_BS); break;
+ case 'Y': USE_BITS (OP_MASK_RNUM, OP_SH_RNUM); break;
+ case 'Z': /* RS1, CSR number. */
+ case 'S': /* RS1, floating point. */
+ case 's': USE_BITS (OP_MASK_RS1, OP_SH_RS1); break;
+ case 'U': /* RS1 and RS2 are the same, floating point. */
+ USE_BITS (OP_MASK_RS1, OP_SH_RS1);
+ /* Fall through. */
+ case 'T': /* RS2, floating point. */
+ case 't': USE_BITS (OP_MASK_RS2, OP_SH_RS2); break;
+ case 'R': /* RS3, floating point. */
+ case 'r': USE_BITS (OP_MASK_RS3, OP_SH_RS3); break;
+ case 'm': USE_BITS (OP_MASK_RM, OP_SH_RM); break;
+ case 'E': USE_BITS (OP_MASK_CSR, OP_SH_CSR); break;
+ case 'P': USE_BITS (OP_MASK_PRED, OP_SH_PRED); break;
+ case 'Q': USE_BITS (OP_MASK_SUCC, OP_SH_SUCC); break;
+ case 'o': /* ITYPE immediate, load displacement. */
+ case 'j': used_bits |= ENCODE_ITYPE_IMM (-1U); break;
+ case 'a': used_bits |= ENCODE_JTYPE_IMM (-1U); break;
+ case 'p': used_bits |= ENCODE_BTYPE_IMM (-1U); break;
+ case 'f': /* Fall through. */
+ case 'q': used_bits |= ENCODE_STYPE_IMM (-1U); break;
+ case 'u': used_bits |= ENCODE_UTYPE_IMM (-1U); break;
+ case 'z': break; /* Zero immediate. */
+ case '[': break; /* Unused operand. */
+ case ']': break; /* Unused operand. */
+ case '0': break; /* AMO displacement, must to zero. */
+ case '1': break; /* Relaxation operand. */
+ case 'F': /* Funct for .insn directive. */
+ switch (*++oparg)
+ {
+ case '7': USE_BITS (OP_MASK_FUNCT7, OP_SH_FUNCT7); break;
+ case '3': USE_BITS (OP_MASK_FUNCT3, OP_SH_FUNCT3); break;
+ case '2': USE_BITS (OP_MASK_FUNCT2, OP_SH_FUNCT2); break;
+ default:
+ goto unknown_validate_operand;
+ }
+ break;
+ case 'O': /* Opcode for .insn directive. */
+ switch (*++oparg)
+ {
+ case '4': USE_BITS (OP_MASK_OP, OP_SH_OP); break;
+ case '2': USE_BITS (OP_MASK_OP2, OP_SH_OP2); break;
+ default:
+ goto unknown_validate_operand;
+ }
+ break;
+ default:
+ unknown_validate_operand:
+ as_bad (_("internal: bad RISC-V opcode "
+ "(unknown operand type `%s'): %s %s"),
+ opargStart, opc->name, opc->args);
+ return false;
+ }
+ }
+
if (used_bits != required_bits)
{
- as_bad (_("internal: bad RISC-V opcode (bits 0x%lx undefined): %s %s"),
+ as_bad (_("internal: bad RISC-V opcode "
+ "(bits 0x%lx undefined): %s %s"),
~(unsigned long)(used_bits & required_bits),
opc->name, opc->args);
- return FALSE;
+ return false;
}
- return TRUE;
+ return true;
}
+#undef USE_BITS
+
struct percent_op_match
{
const char *str;
bfd_reloc_code_real_type reloc;
};
-/* Common hash table initialization function for
- instruction and .insn directive. */
+/* Common hash table initialization function for instruction and .insn
+ directive. */
+
static htab_t
init_opcode_hash (const struct riscv_opcode *opcodes,
- bfd_boolean insn_directive_p)
+ bool insn_directive_p)
{
int i = 0;
int length;
{
const char *name = opcodes[i].name;
if (str_hash_insert (hash, name, &opcodes[i], 0) != NULL)
- as_fatal (_("duplicate %s"), name);
+ as_fatal (_("internal: duplicate %s"), name);
do
{
if (insn_directive_p)
length = ((name[0] == 'c') ? 2 : 4);
else
- length = 0; /* Let assembler determine the length. */
+ length = 0; /* Let assembler determine the length. */
if (!validate_riscv_insn (&opcodes[i], length))
- as_fatal (_("Broken assembler. No assembly attempted."));
+ as_fatal (_("internal: broken assembler. "
+ "No assembly attempted"));
}
else
gas_assert (!insn_directive_p);
unsigned long mach = xlen == 64 ? bfd_mach_riscv64 : bfd_mach_riscv32;
if (! bfd_set_arch_mach (stdoutput, bfd_arch_riscv, mach))
- as_warn (_("Could not set architecture and machine"));
+ as_warn (_("could not set architecture and machine"));
- op_hash = init_opcode_hash (riscv_opcodes, FALSE);
- insn_type_hash = init_opcode_hash (riscv_insn_types, TRUE);
+ op_hash = init_opcode_hash (riscv_opcodes, false);
+ insn_type_hash = init_opcode_hash (riscv_insn_types, true);
reg_names_hash = str_htab_create ();
hash_reg_names (RCLASS_GPR, riscv_gpr_names_numeric, NGPR);
hash_reg_names (RCLASS_GPR, riscv_gpr_names_abi, NGPR);
hash_reg_names (RCLASS_FPR, riscv_fpr_names_numeric, NFPR);
hash_reg_names (RCLASS_FPR, riscv_fpr_names_abi, NFPR);
+ hash_reg_names (RCLASS_VECR, riscv_vecr_names_numeric, NVECR);
+ hash_reg_names (RCLASS_VECM, riscv_vecm_names_numeric, NVECM);
/* Add "fp" as an alias for "s0". */
hash_reg_name (RCLASS_GPR, "fp", 8);
{
howto = bfd_reloc_type_lookup (stdoutput, reloc_type);
if (howto == NULL)
- as_bad (_("Unsupported RISC-V relocation number %d"), reloc_type);
+ as_bad (_("internal: unsupported RISC-V relocation number %d"),
+ reloc_type);
ip->fixp = fix_new_exp (ip->frag, ip->where,
bfd_get_reloc_size (howto),
- address_expr, FALSE, reloc_type);
+ address_expr, false, reloc_type);
ip->fixp->fx_tcbit = riscv_opts.relax;
}
}
/* Build an instruction created by a macro expansion. This is passed
- a pointer to the count of instructions created so far, an
- expression, the name of the instruction to build, an operand format
- string, and corresponding arguments. */
+ a pointer to the count of instructions created so far, an expression,
+ the name of the instruction to build, an operand format string, and
+ corresponding arguments. */
static void
macro_build (expressionS *ep, const char *name, const char *fmt, ...)
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 '>':
- INSERT_OPERAND (SHAMT, insn, va_arg (args, int));
- continue;
-
case 'j':
case 'u':
case 'q':
case ',':
continue;
default:
- as_fatal (_("internal error: invalid macro"));
+ unknown_macro_argument:
+ as_fatal (_("internal: invalid macro argument `%s'"), fmtStart);
}
break;
}
r = vasprintf (&buf, format, ap);
if (r < 0)
- as_fatal (_("internal error: vasprintf failed"));
+ as_fatal (_("internal: vasprintf failed"));
md_assemble (buf);
free(buf);
/* Sign-extend 32-bit mode constants that have bit 31 set and all higher bits
unset. */
+
static void
normalize_constant_expr (expressionS *ex)
{
static void
check_absolute_expr (struct riscv_cl_insn *ip, expressionS *ex,
- bfd_boolean maybe_csr)
+ bool maybe_csr)
{
if (ex->X_op == O_big)
as_bad (_("unsupported large constant"));
as_bad (_("unknown CSR `%s'"),
S_GET_NAME (ex->X_add_symbol));
else if (ex->X_op != O_constant)
- as_bad (_("Instruction %s requires absolute expression"),
+ as_bad (_("instruction %s requires absolute expression"),
ip->insn_mo->name);
normalize_constant_expr (ex);
}
}
/* Load an entry from the GOT. */
+
static void
pcrel_access (int destreg, int tempreg, expressionS *ep,
const char *lo_insn, const char *lo_pattern,
}
/* PC-relative function call using AUIPC/JALR, relaxed to JAL. */
+
static void
riscv_call (int destreg, int tempreg, expressionS *ep,
bfd_reloc_code_real_type reloc)
md_assemblef ("slli x%d, x%d, 0x%x", reg, reg, shift);
if (lower.X_add_number != 0)
- md_assemblef ("addi x%d, x%d, %" BFD_VMA_FMT "d", reg, reg,
- lower.X_add_number);
+ md_assemblef ("addi x%d, x%d, %" PRId64, reg, reg,
+ (int64_t) lower.X_add_number);
}
else
{
/* Discard low part and zero-extend upper immediate. */
upper_imm = ((uint32_t)upper.X_add_number >> shift);
- md_assemblef ("lui x%d, 0x%" BFD_VMA_FMT "x", reg, upper_imm);
+ md_assemblef ("lui x%d, 0x%" PRIx64, reg, (uint64_t) upper_imm);
hi_reg = reg;
}
if (lower.X_add_number != 0 || hi_reg == 0)
- md_assemblef ("%s x%d, x%d, %" BFD_VMA_FMT "d", ADD32_INSN, reg, hi_reg,
- lower.X_add_number);
+ md_assemblef ("%s x%d, x%d, %" PRId64, ADD32_INSN, reg, hi_reg,
+ (int64_t) lower.X_add_number);
}
}
/* Zero extend and sign extend byte/half-word/word. */
static void
-riscv_ext (int destreg, int srcreg, unsigned shift, bfd_boolean sign)
+riscv_ext (int destreg, int srcreg, unsigned shift, bool sign)
{
if (sign)
{
}
}
+/* Expand RISC-V Vector macros into one or more instructions. */
+
+static void
+vector_macro (struct riscv_cl_insn *ip)
+{
+ int vd = (ip->insn_opcode >> OP_SH_VD) & OP_MASK_VD;
+ int vs1 = (ip->insn_opcode >> OP_SH_VS1) & OP_MASK_VS1;
+ int vs2 = (ip->insn_opcode >> OP_SH_VS2) & OP_MASK_VS2;
+ int vm = (ip->insn_opcode >> OP_SH_VMASK) & OP_MASK_VMASK;
+ int vtemp = (ip->insn_opcode >> OP_SH_VFUNCT6) & OP_MASK_VFUNCT6;
+ int mask = ip->insn_mo->mask;
+
+ switch (mask)
+ {
+ case M_VMSGE:
+ if (vm)
+ {
+ /* Unmasked. */
+ macro_build (NULL, "vmslt.vx", "Vd,Vt,sVm", vd, vs2, vs1, -1);
+ macro_build (NULL, "vmnand.mm", "Vd,Vt,Vs", vd, vd, vd);
+ break;
+ }
+ if (vtemp != 0)
+ {
+ /* Masked. Have vtemp to avoid overlap constraints. */
+ if (vd == vm)
+ {
+ macro_build (NULL, "vmslt.vx", "Vd,Vt,s", vtemp, vs2, vs1);
+ macro_build (NULL, "vmandnot.mm", "Vd,Vt,Vs", vd, vm, vtemp);
+ }
+ else
+ {
+ /* Preserve the value of vd if not updating by vm. */
+ macro_build (NULL, "vmslt.vx", "Vd,Vt,s", vtemp, vs2, vs1);
+ macro_build (NULL, "vmandnot.mm", "Vd,Vt,Vs", vtemp, vm, vtemp);
+ macro_build (NULL, "vmandnot.mm", "Vd,Vt,Vs", vd, vd, vm);
+ macro_build (NULL, "vmor.mm", "Vd,Vt,Vs", vd, vtemp, vd);
+ }
+ }
+ else if (vd != vm)
+ {
+ /* Masked. This may cause the vd overlaps vs2, when LMUL > 1. */
+ macro_build (NULL, "vmslt.vx", "Vd,Vt,sVm", vd, vs2, vs1, vm);
+ macro_build (NULL, "vmxor.mm", "Vd,Vt,Vs", vd, vd, vm);
+ }
+ else
+ as_bad (_("must provide temp if destination overlaps mask"));
+ break;
+
+ case M_VMSGEU:
+ if (vm)
+ {
+ /* Unmasked. */
+ macro_build (NULL, "vmsltu.vx", "Vd,Vt,sVm", vd, vs2, vs1, -1);
+ macro_build (NULL, "vmnand.mm", "Vd,Vt,Vs", vd, vd, vd);
+ break;
+ }
+ if (vtemp != 0)
+ {
+ /* Masked. Have vtemp to avoid overlap constraints. */
+ if (vd == vm)
+ {
+ macro_build (NULL, "vmsltu.vx", "Vd,Vt,s", vtemp, vs2, vs1);
+ macro_build (NULL, "vmandnot.mm", "Vd,Vt,Vs", vd, vm, vtemp);
+ }
+ else
+ {
+ /* Preserve the value of vd if not updating by vm. */
+ macro_build (NULL, "vmsltu.vx", "Vd,Vt,s", vtemp, vs2, vs1);
+ macro_build (NULL, "vmandnot.mm", "Vd,Vt,Vs", vtemp, vm, vtemp);
+ macro_build (NULL, "vmandnot.mm", "Vd,Vt,Vs", vd, vd, vm);
+ macro_build (NULL, "vmor.mm", "Vd,Vt,Vs", vd, vtemp, vd);
+ }
+ }
+ else if (vd != vm)
+ {
+ /* Masked. This may cause the vd overlaps vs2, when LMUL > 1. */
+ macro_build (NULL, "vmsltu.vx", "Vd,Vt,sVm", vd, vs2, vs1, vm);
+ macro_build (NULL, "vmxor.mm", "Vd,Vt,Vs", vd, vd, vm);
+ }
+ else
+ as_bad (_("must provide temp if destination overlaps mask"));
+ break;
+
+ default:
+ break;
+ }
+}
+
/* Expand RISC-V assembly macros into one or more instructions. */
+
static void
macro (struct riscv_cl_insn *ip, expressionS *imm_expr,
bfd_reloc_code_real_type *imm_reloc)
if (imm_expr->X_op == O_constant)
load_const (rd, imm_expr);
- else if (riscv_opts.pic && mask == M_LA) /* Global PIC symbol */
+ else if (riscv_opts.pic && mask == M_LA) /* Global PIC symbol. */
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 */
+ else /* Local PIC symbol, or any non-PIC symbol. */
pcrel_load (rd, rd, imm_expr, "addi",
BFD_RELOC_RISCV_PCREL_HI20, BFD_RELOC_RISCV_PCREL_LO12_I);
break;
break;
case M_ZEXTH:
- riscv_ext (rd, rs1, xlen - 16, FALSE);
+ riscv_ext (rd, rs1, xlen - 16, false);
break;
case M_ZEXTW:
- riscv_ext (rd, rs1, xlen - 32, FALSE);
+ riscv_ext (rd, rs1, xlen - 32, false);
+ break;
+
+ case M_SEXTB:
+ riscv_ext (rd, rs1, xlen - 8, true);
+ break;
+
+ case M_SEXTH:
+ riscv_ext (rd, rs1, xlen - 16, true);
break;
- case M_SEXTB:
- riscv_ext (rd, rs1, xlen - 8, TRUE);
+ case M_VMSGE:
+ case M_VMSGEU:
+ vector_macro (ip);
break;
- case M_SEXTH:
- riscv_ext (rd, rs1, xlen - 16, TRUE);
+ 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 (_("Macro %s not implemented"), ip->insn_mo->name);
+ as_bad (_("internal: macro %s not implemented"), ip->insn_mo->name);
break;
}
}
move *STR over the operator and store its relocation code in *RELOC.
Leave both *STR and *RELOC alone when returning false. */
-static bfd_boolean
+static bool
parse_relocation (char **str, bfd_reloc_code_real_type *reloc,
const struct percent_op_match *percent_op)
{
if (*reloc != BFD_RELOC_UNUSED
&& !bfd_reloc_type_lookup (stdoutput, *reloc))
{
- as_bad ("relocation %s isn't supported by the current ABI",
- percent_op->str);
+ as_bad ("internal: relocation %s isn't supported by the "
+ "current ABI", percent_op->str);
*reloc = BFD_RELOC_UNUSED;
}
- return TRUE;
+ return true;
}
- return FALSE;
+ return false;
}
static void
}
/* Search for the start of the main expression.
- End the loop with CRUX pointing to the start
- of the main expression and with CRUX_DEPTH containing the number
- of open brackets at that point. */
+
+ End the loop with CRUX pointing to the start of the main expression and
+ with CRUX_DEPTH containing the number of open brackets at that point. */
reloc_index = -1;
str_depth = 0;
do
}
/* Parse opcode name, could be an mnemonics or number. */
+
static size_t
my_getOpcodeExpression (expressionS *ep, bfd_reloc_code_real_type *reloc,
char *str, const struct percent_op_match *percent_op)
return my_getSmallExpression (ep, reloc, str, percent_op);
}
+/* Parse string STR as a vsetvli operand. Store the expression in *EP.
+ On exit, EXPR_END points to the first character after the expression. */
+
+static void
+my_getVsetvliExpression (expressionS *ep, char *str)
+{
+ unsigned int vsew_value = 0, vlmul_value = 0;
+ unsigned int vta_value = 0, vma_value = 0;
+ bfd_boolean vsew_found = FALSE, vlmul_found = FALSE;
+ bfd_boolean vta_found = FALSE, vma_found = FALSE;
+
+ if (arg_lookup (&str, riscv_vsew, ARRAY_SIZE (riscv_vsew), &vsew_value))
+ {
+ if (*str == ',')
+ ++str;
+ if (vsew_found)
+ as_bad (_("multiple vsew constants"));
+ vsew_found = TRUE;
+ }
+ if (arg_lookup (&str, riscv_vlmul, ARRAY_SIZE (riscv_vlmul), &vlmul_value))
+ {
+ if (*str == ',')
+ ++str;
+ if (vlmul_found)
+ as_bad (_("multiple vlmul constants"));
+ vlmul_found = TRUE;
+ }
+ if (arg_lookup (&str, riscv_vta, ARRAY_SIZE (riscv_vta), &vta_value))
+ {
+ if (*str == ',')
+ ++str;
+ if (vta_found)
+ as_bad (_("multiple vta constants"));
+ vta_found = TRUE;
+ }
+ if (arg_lookup (&str, riscv_vma, ARRAY_SIZE (riscv_vma), &vma_value))
+ {
+ if (*str == ',')
+ ++str;
+ if (vma_found)
+ as_bad (_("multiple vma constants"));
+ vma_found = TRUE;
+ }
+
+ if (vsew_found || vlmul_found || vta_found || vma_found)
+ {
+ ep->X_op = O_constant;
+ ep->X_add_number = (vlmul_value << OP_SH_VLMUL)
+ | (vsew_value << OP_SH_VSEW)
+ | (vta_value << OP_SH_VTA)
+ | (vma_value << OP_SH_VMA);
+ expr_end = str;
+ }
+ else
+ {
+ my_getExpression (ep, str);
+ str = expr_end;
+ }
+}
+
/* Detect and handle implicitly zero load-store offsets. For example,
- "lw t0, (t1)" is shorthand for "lw t0, 0(t1)". Return TRUE iff such
+ "lw t0, (t1)" is shorthand for "lw t0, 0(t1)". Return true if such
an implicit offset was detected. */
-static bfd_boolean
+static bool
riscv_handle_implicit_zero_offset (expressionS *ep, const char *s)
{
/* Check whether there is only a single bracketed expression left.
{
ep->X_op = O_constant;
ep->X_add_number = 0;
- return TRUE;
+ return true;
}
- return FALSE;
+ return false;
}
/* All RISC-V CSR instructions belong to one of these classes. */
-
enum csr_insn_type
{
INSN_NOT_CSR,
CSR when RS1 isn't zero. The CSR is read only if the [11:10] bits of
CSR address is 0x3. */
-static bfd_boolean
+static bool
riscv_csr_read_only_check (insn_t insn)
{
int csr = (insn & (OP_MASK_CSR << OP_SH_CSR)) >> OP_SH_CSR;
|| csr_insn == INSN_CSRRC)
&& rs1 != 0)
|| csr_insn == INSN_CSRRW))
- return FALSE;
+ return false;
- return TRUE;
+ return true;
}
-/* Return True if it is a privileged instruction. Otherwise, return FALSE.
+/* Return true if it is a privileged instruction. Otherwise, return false.
uret is actually a N-ext instruction. So it is better to regard it as
an user instruction rather than the priv instruction.
dret is defined in the debug spec, so it should be checked in the future,
too. */
-static bfd_boolean
+static bool
riscv_is_priv_insn (insn_t insn)
{
return (((insn ^ MATCH_SRET) & MASK_SRET) == 0
|| ((insn ^ MATCH_SFENCE_VMA) & MASK_SFENCE_VMA) == 0
|| ((insn ^ MATCH_WFI) & MASK_WFI) == 0
/* The sfence.vm is dropped in the v1.10 priv specs, but we still need to
- check it here to keep the compatible. Maybe we should issue warning
- if sfence.vm is used, but the priv spec newer than v1.10 is chosen.
- We already have a similar check for CSR, but not yet for instructions.
- It would be good if we could check the spec versions both for CSR and
- instructions, but not here. */
+ check it here to keep the compatible. */
|| ((insn ^ MATCH_SFENCE_VM) & MASK_SFENCE_VM) == 0);
}
side effect, it sets the global variable imm_reloc to the type of
relocation to do if one of the operands is an address expression. */
-static const char *
+static struct riscv_ip_error
riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr,
bfd_reloc_code_real_type *imm_reloc, htab_t hash)
{
- char *s;
- const char *args;
- char c = 0;
+ /* The operand string defined in the riscv_opcodes. */
+ const char *oparg, *opargStart;
+ /* The parsed operands from assembly. */
+ char *asarg, *asargStart;
+ char save_c = 0;
struct riscv_opcode *insn;
- char *argsStart;
unsigned int regno;
- char save_c = 0;
- int argnum;
const struct percent_op_match *p;
- const char *error = "unrecognized opcode";
+ struct riscv_ip_error error;
+ error.msg = "unrecognized opcode";
+ error.statement = str;
+ error.missing_ext = NULL;
/* Indicate we are assembling instruction with CSR. */
- bfd_boolean insn_with_csr = FALSE;
+ bool insn_with_csr = false;
/* Parse the name of the instruction. Terminate the string if whitespace
is found so that str_hash_find only sees the name part of the string. */
- for (s = str; *s != '\0'; ++s)
- if (ISSPACE (*s))
+ for (asarg = str; *asarg!= '\0'; ++asarg)
+ if (ISSPACE (*asarg))
{
- save_c = *s;
- *s++ = '\0';
+ save_c = *asarg;
+ *asarg++ = '\0';
break;
}
insn = (struct riscv_opcode *) str_hash_find (hash, str);
- argsStart = s;
+ asargStart = asarg;
for ( ; insn && insn->name && strcmp (insn->name, str) == 0; insn++)
{
if ((insn->xlen_requirement != 0) && (xlen != insn->xlen_requirement))
continue;
- if (!riscv_multi_subset_supports (insn->insn_class))
- continue;
+ if (!riscv_multi_subset_supports (&riscv_rps_as, insn->insn_class))
+ {
+ error.missing_ext = riscv_multi_subset_supports_ext (&riscv_rps_as,
+ insn->insn_class);
+ continue;
+ }
+ /* Reset error message of the previous round. */
+ error.msg = _("illegal operands");
+ error.missing_ext = NULL;
create_insn (ip, insn);
- argnum = 1;
imm_expr->X_op = O_absent;
*imm_reloc = BFD_RELOC_UNUSED;
p = percent_op_itype;
- for (args = insn->args;; ++args)
+ for (oparg = insn->args;; ++oparg)
{
- s += strspn (s, " \t");
- switch (*args)
+ opargStart = oparg;
+ asarg += strspn (asarg, " \t");
+ switch (*oparg)
{
- case '\0': /* End of args. */
+ case '\0': /* End of args. */
if (insn->pinfo != INSN_MACRO)
{
if (!insn->match_func (insn, ip->insn_opcode))
break;
if (riscv_is_priv_insn (ip->insn_opcode))
- explicit_priv_attr = TRUE;
+ explicit_priv_attr = true;
/* Check if we write a read-only CSR by the CSR
instruction. */
/* Restore the character in advance, since we want to
report the detailed warning message here. */
if (save_c)
- *(argsStart - 1) = save_c;
- as_warn (_("Read-only CSR is written `%s'"), str);
- insn_with_csr = FALSE;
+ *(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;
- insn_with_csr = FALSE;
+ error.msg = NULL;
+ insn_with_csr = false;
goto out;
case 'C': /* RVC */
- switch (*++args)
+ switch (*++oparg)
{
- case 's': /* RS1 x8-x15 */
- if (!reg_lookup (&s, RCLASS_GPR, ®no)
+ case 's': /* RS1 x8-x15. */
+ 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)
+ case 't': /* RS2 x8-x15. */
+ 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, contrained to equal x0. */
- if (!reg_lookup (&s, RCLASS_GPR, ®no)
+ case 'z': /* RS2, constrained to equal x0. */
+ if (!reg_lookup (&asarg, RCLASS_GPR, ®no)
|| regno != 0)
break;
continue;
- case '>':
- if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
+ case '>': /* Shift amount, 0 - (XLEN-1). */
+ if (my_getSmallExpression (imm_expr, imm_reloc, asarg, p)
|| imm_expr->X_op != O_constant
- || imm_expr->X_add_number <= 0
- || imm_expr->X_add_number >= 64)
+ || (unsigned long) imm_expr->X_add_number >= xlen)
break;
- ip->insn_opcode |= ENCODE_RVC_IMM (imm_expr->X_add_number);
+ ip->insn_opcode |= ENCODE_CITYPE_IMM (imm_expr->X_add_number);
rvc_imm_done:
- s = expr_end;
+ asarg = expr_end;
imm_expr->X_op = O_absent;
continue;
- case '<':
- if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
+ case '5':
+ 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 < 0
|| imm_expr->X_add_number >= 32
- || !VALID_RVC_IMM ((valueT) imm_expr->X_add_number))
+ || !VALID_CLTYPE_IMM ((valueT) imm_expr->X_add_number))
break;
- ip->insn_opcode |= ENCODE_RVC_IMM (imm_expr->X_add_number);
+ ip->insn_opcode |= ENCODE_CLTYPE_IMM (imm_expr->X_add_number);
goto rvc_imm_done;
- case '8':
- if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
+ case '6':
+ if (my_getSmallExpression (imm_expr, imm_reloc, asarg, p)
|| imm_expr->X_op != O_constant
|| imm_expr->X_add_number < 0
- || imm_expr->X_add_number >= 256
- || !VALID_RVC_UIMM8 ((valueT) imm_expr->X_add_number))
+ || imm_expr->X_add_number >= 64
+ || !VALID_CSSTYPE_IMM ((valueT) imm_expr->X_add_number))
break;
- ip->insn_opcode |= ENCODE_RVC_UIMM8 (imm_expr->X_add_number);
+ ip->insn_opcode |= ENCODE_CSSTYPE_IMM (imm_expr->X_add_number);
goto rvc_imm_done;
- case 'i':
- if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
+ case '8':
+ if (my_getSmallExpression (imm_expr, imm_reloc, asarg, p)
|| imm_expr->X_op != O_constant
- || imm_expr->X_add_number == 0
- || !VALID_RVC_SIMM3 ((valueT) imm_expr->X_add_number))
+ || imm_expr->X_add_number < 0
+ || imm_expr->X_add_number >= 256
+ || !VALID_CIWTYPE_IMM ((valueT) imm_expr->X_add_number))
break;
- ip->insn_opcode |= ENCODE_RVC_SIMM3 (imm_expr->X_add_number);
+ ip->insn_opcode |= ENCODE_CIWTYPE_IMM (imm_expr->X_add_number);
goto rvc_imm_done;
case 'j':
- if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
+ if (my_getSmallExpression (imm_expr, imm_reloc, asarg, p)
|| imm_expr->X_op != O_constant
|| imm_expr->X_add_number == 0
- || !VALID_RVC_IMM ((valueT) imm_expr->X_add_number))
+ || !VALID_CITYPE_IMM ((valueT) imm_expr->X_add_number))
break;
- ip->insn_opcode |= ENCODE_RVC_IMM (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_RVC_LW_IMM ((valueT) imm_expr->X_add_number))
+ || !VALID_CLTYPE_LW_IMM ((valueT) imm_expr->X_add_number))
break;
- ip->insn_opcode |= ENCODE_RVC_LW_IMM (imm_expr->X_add_number);
+ 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_RVC_LD_IMM ((valueT) imm_expr->X_add_number))
+ || !VALID_CLTYPE_LD_IMM ((valueT) imm_expr->X_add_number))
break;
- ip->insn_opcode |= ENCODE_RVC_LD_IMM (imm_expr->X_add_number);
+ 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_RVC_LWSP_IMM ((valueT) imm_expr->X_add_number))
+ || !VALID_CITYPE_LWSP_IMM ((valueT) imm_expr->X_add_number))
break;
ip->insn_opcode |=
- ENCODE_RVC_LWSP_IMM (imm_expr->X_add_number);
+ 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_RVC_LDSP_IMM ((valueT) imm_expr->X_add_number))
+ || !VALID_CITYPE_LDSP_IMM ((valueT) imm_expr->X_add_number))
break;
ip->insn_opcode |=
- ENCODE_RVC_LDSP_IMM (imm_expr->X_add_number);
+ 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
is same as 'j'. */
- || !VALID_RVC_IMM ((valueT) imm_expr->X_add_number))
+ || !VALID_CITYPE_IMM ((valueT) imm_expr->X_add_number))
break;
- ip->insn_opcode |= ENCODE_RVC_IMM (imm_expr->X_add_number);
+ 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_RVC_ADDI4SPN_IMM ((valueT) imm_expr->X_add_number))
+ || !VALID_CIWTYPE_ADDI4SPN_IMM ((valueT) imm_expr->X_add_number))
break;
ip->insn_opcode |=
- ENCODE_RVC_ADDI4SPN_IMM (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
- || imm_expr->X_add_number == 0
- || !VALID_RVC_ADDI16SP_IMM ((valueT) imm_expr->X_add_number))
+ || !VALID_CITYPE_ADDI16SP_IMM ((valueT) imm_expr->X_add_number))
break;
ip->insn_opcode |=
- ENCODE_RVC_ADDI16SP_IMM (imm_expr->X_add_number);
+ 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_RVC_SWSP_IMM ((valueT) imm_expr->X_add_number))
+ || !VALID_CSSTYPE_SWSP_IMM ((valueT) imm_expr->X_add_number))
break;
ip->insn_opcode |=
- ENCODE_RVC_SWSP_IMM (imm_expr->X_add_number);
+ 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_RVC_SDSP_IMM ((valueT) imm_expr->X_add_number))
+ || !VALID_CSSTYPE_SDSP_IMM ((valueT) imm_expr->X_add_number))
break;
ip->insn_opcode |=
- ENCODE_RVC_SDSP_IMM (imm_expr->X_add_number);
+ ENCODE_CSSTYPE_SDSP_IMM (imm_expr->X_add_number);
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
&& (imm_expr->X_add_number <
RISCV_BIGIMM_REACH - RISCV_RVC_IMM_REACH / 2)))
break;
- ip->insn_opcode |= ENCODE_RVC_IMM (imm_expr->X_add_number);
+ 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 funct6 field, "
- "value must be 0...64"));
+ as_bad (_("bad value for compressed funct6 "
+ "field, value must be 0...63"));
break;
}
-
INSERT_OPERAND (CFUNCT6, *ip, imm_expr->X_add_number);
imm_expr->X_op = O_absent;
- s = expr_end;
+ asarg = expr_end;
continue;
+
case '4':
- if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
+ if (my_getSmallExpression (imm_expr, imm_reloc, asarg, p)
|| imm_expr->X_op != O_constant
|| imm_expr->X_add_number < 0
|| imm_expr->X_add_number >= 16)
{
- as_bad (_("bad value for funct4 field, "
- "value must be 0...15"));
+ as_bad (_("bad value for compressed funct4 "
+ "field, value must be 0...15"));
break;
}
-
INSERT_OPERAND (CFUNCT4, *ip, imm_expr->X_add_number);
imm_expr->X_op = O_absent;
- s = expr_end;
+ asarg = expr_end;
continue;
+
case '3':
- if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
+ if (my_getSmallExpression (imm_expr, imm_reloc, asarg, p)
|| imm_expr->X_op != O_constant
|| imm_expr->X_add_number < 0
|| imm_expr->X_add_number >= 8)
{
- as_bad (_("bad value for funct3 field, "
- "value must be 0...7"));
+ as_bad (_("bad value for compressed funct3 "
+ "field, value must be 0...7"));
break;
}
INSERT_OPERAND (CFUNCT3, *ip, imm_expr->X_add_number);
imm_expr->X_op = O_absent;
- s = expr_end;
+ asarg = expr_end;
continue;
+
case '2':
- if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
+ if (my_getSmallExpression (imm_expr, imm_reloc, asarg, p)
|| imm_expr->X_op != O_constant
|| imm_expr->X_add_number < 0
|| imm_expr->X_add_number >= 4)
{
- as_bad (_("bad value for funct2 field, "
- "value must be 0...3"));
+ as_bad (_("bad value for compressed funct2 "
+ "field, value must be 0...3"));
break;
}
INSERT_OPERAND (CFUNCT2, *ip, imm_expr->X_add_number);
imm_expr->X_op = O_absent;
- s = expr_end;
+ asarg = expr_end;
continue;
+
default:
- as_bad (_("bad compressed FUNCT field"
- " specifier 'CF%c'\n"),
- *args);
+ goto unknown_riscv_ip_operand;
}
break;
default:
- as_bad (_("bad RVC field specifier 'C%c'\n"), *args);
+ goto unknown_riscv_ip_operand;
}
- break;
+ break; /* end RVC */
+
+ case 'V': /* RVV */
+ switch (*++oparg)
+ {
+ case 'd': /* VD */
+ if (!reg_lookup (&asarg, RCLASS_VECR, ®no))
+ break;
+ INSERT_OPERAND (VD, *ip, regno);
+ continue;
+
+ case 'e': /* AMO VD */
+ if (reg_lookup (&asarg, RCLASS_GPR, ®no) && regno == 0)
+ INSERT_OPERAND (VWD, *ip, 0);
+ else if (reg_lookup (&asarg, RCLASS_VECR, ®no))
+ {
+ INSERT_OPERAND (VWD, *ip, 1);
+ INSERT_OPERAND (VD, *ip, regno);
+ }
+ else
+ break;
+ continue;
+
+ case 'f': /* AMO VS3 */
+ if (!reg_lookup (&asarg, RCLASS_VECR, ®no))
+ break;
+ if (!EXTRACT_OPERAND (VWD, ip->insn_opcode))
+ INSERT_OPERAND (VD, *ip, regno);
+ else
+ {
+ /* VS3 must match VD. */
+ if (EXTRACT_OPERAND (VD, ip->insn_opcode) != regno)
+ break;
+ }
+ continue;
+
+ case 's': /* VS1 */
+ if (!reg_lookup (&asarg, RCLASS_VECR, ®no))
+ break;
+ INSERT_OPERAND (VS1, *ip, regno);
+ continue;
+
+ case 't': /* VS2 */
+ if (!reg_lookup (&asarg, RCLASS_VECR, ®no))
+ break;
+ INSERT_OPERAND (VS2, *ip, regno);
+ continue;
+
+ case 'u': /* VS1 == VS2 */
+ if (!reg_lookup (&asarg, RCLASS_VECR, ®no))
+ break;
+ INSERT_OPERAND (VS1, *ip, regno);
+ INSERT_OPERAND (VS2, *ip, regno);
+ continue;
+
+ case 'v': /* VD == VS1 == VS2 */
+ if (!reg_lookup (&asarg, RCLASS_VECR, ®no))
+ break;
+ INSERT_OPERAND (VD, *ip, regno);
+ INSERT_OPERAND (VS1, *ip, regno);
+ INSERT_OPERAND (VS2, *ip, regno);
+ continue;
+
+ /* The `V0` is carry-in register for v[m]adc and v[m]sbc,
+ and is used to choose vs1/rs1/frs1/imm or vs2 for
+ v[f]merge. It use the same encoding as the vector mask
+ register. */
+ case '0':
+ if (reg_lookup (&asarg, RCLASS_VECR, ®no) && regno == 0)
+ continue;
+ break;
+
+ case 'b': /* vtypei for vsetivli */
+ my_getVsetvliExpression (imm_expr, asarg);
+ check_absolute_expr (ip, imm_expr, FALSE);
+ if (!VALID_RVV_VB_IMM (imm_expr->X_add_number))
+ as_bad (_("bad value for vsetivli immediate field, "
+ "value must be 0..1023"));
+ ip->insn_opcode
+ |= ENCODE_RVV_VB_IMM (imm_expr->X_add_number);
+ imm_expr->X_op = O_absent;
+ asarg = expr_end;
+ continue;
+
+ case 'c': /* vtypei for vsetvli */
+ my_getVsetvliExpression (imm_expr, asarg);
+ check_absolute_expr (ip, imm_expr, FALSE);
+ if (!VALID_RVV_VC_IMM (imm_expr->X_add_number))
+ as_bad (_("bad value for vsetvli immediate field, "
+ "value must be 0..2047"));
+ ip->insn_opcode
+ |= ENCODE_RVV_VC_IMM (imm_expr->X_add_number);
+ imm_expr->X_op = O_absent;
+ asarg = expr_end;
+ continue;
+
+ case 'i': /* vector arith signed immediate */
+ my_getExpression (imm_expr, asarg);
+ check_absolute_expr (ip, imm_expr, FALSE);
+ if (imm_expr->X_add_number > 15
+ || imm_expr->X_add_number < -16)
+ as_bad (_("bad value for vector immediate field, "
+ "value must be -16...15"));
+ INSERT_OPERAND (VIMM, *ip, imm_expr->X_add_number);
+ imm_expr->X_op = O_absent;
+ asarg = expr_end;
+ continue;
+
+ case 'j': /* vector arith unsigned immediate */
+ my_getExpression (imm_expr, asarg);
+ check_absolute_expr (ip, imm_expr, FALSE);
+ if (imm_expr->X_add_number < 0
+ || imm_expr->X_add_number >= 32)
+ as_bad (_("bad value for vector immediate field, "
+ "value must be 0...31"));
+ INSERT_OPERAND (VIMM, *ip, imm_expr->X_add_number);
+ imm_expr->X_op = O_absent;
+ asarg = expr_end;
+ continue;
+
+ case 'k': /* vector arith signed immediate, minus 1 */
+ my_getExpression (imm_expr, asarg);
+ check_absolute_expr (ip, imm_expr, FALSE);
+ if (imm_expr->X_add_number > 16
+ || imm_expr->X_add_number < -15)
+ as_bad (_("bad value for vector immediate field, "
+ "value must be -15...16"));
+ INSERT_OPERAND (VIMM, *ip, imm_expr->X_add_number - 1);
+ imm_expr->X_op = O_absent;
+ asarg = expr_end;
+ continue;
+
+ case 'm': /* optional vector mask */
+ if (*asarg == '\0')
+ {
+ INSERT_OPERAND (VMASK, *ip, 1);
+ continue;
+ }
+ else if (*asarg == ',' && asarg++
+ && reg_lookup (&asarg, RCLASS_VECM, ®no)
+ && regno == 0)
+ {
+ INSERT_OPERAND (VMASK, *ip, 0);
+ continue;
+ }
+ break;
+
+ case 'M': /* required vector mask */
+ if (reg_lookup (&asarg, RCLASS_VECM, ®no) && regno == 0)
+ {
+ INSERT_OPERAND (VMASK, *ip, 0);
+ continue;
+ }
+ break;
+
+ case 'T': /* vector macro temporary register */
+ if (!reg_lookup (&asarg, RCLASS_VECR, ®no) || regno == 0)
+ break;
+ /* Store it in the FUNCT6 field as we don't have anyplace
+ else to store it. */
+ INSERT_OPERAND (VFUNCT6, *ip, regno);
+ continue;
+
+ default:
+ goto unknown_riscv_ip_operand;
+ }
+ break; /* end RVV */
case ',':
- ++argnum;
- if (*s++ == *args)
+ if (*asarg++ == *oparg)
continue;
- s--;
+ asarg--;
break;
case '(':
case ')':
case '[':
case ']':
- if (*s++ == *args)
+ if (*asarg++ == *oparg)
continue;
break;
- case '<': /* Shift amount, 0 - 31. */
- my_getExpression (imm_expr, s);
- check_absolute_expr (ip, imm_expr, FALSE);
+ case '<': /* Shift amount, 0 - 31. */
+ 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)"),
+ as_bad (_("improper shift amount (%lu)"),
(unsigned long) imm_expr->X_add_number);
INSERT_OPERAND (SHAMTW, *ip, imm_expr->X_add_number);
imm_expr->X_op = O_absent;
- s = expr_end;
+ asarg = expr_end;
continue;
- case '>': /* Shift amount, 0 - (XLEN-1). */
- my_getExpression (imm_expr, s);
- check_absolute_expr (ip, imm_expr, FALSE);
+ case '>': /* Shift amount, 0 - (XLEN-1). */
+ 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)"),
+ as_bad (_("improper shift amount (%lu)"),
(unsigned long) imm_expr->X_add_number);
INSERT_OPERAND (SHAMT, *ip, imm_expr->X_add_number);
imm_expr->X_op = O_absent;
- s = expr_end;
+ asarg = expr_end;
continue;
- case 'Z': /* CSRRxI immediate. */
- my_getExpression (imm_expr, s);
- check_absolute_expr (ip, imm_expr, FALSE);
+ case 'Z': /* CSRRxI immediate. */
+ 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)"),
+ as_bad (_("improper CSRxI immediate (%lu)"),
(unsigned long) imm_expr->X_add_number);
INSERT_OPERAND (RS1, *ip, imm_expr->X_add_number);
imm_expr->X_op = O_absent;
- s = expr_end;
+ asarg = expr_end;
continue;
- case 'E': /* Control register. */
- insn_with_csr = TRUE;
- explicit_priv_attr = TRUE;
- if (reg_lookup (&s, RCLASS_CSR, ®no))
+ case 'E': /* Control register. */
+ insn_with_csr = true;
+ explicit_priv_attr = true;
+ if (reg_lookup (&asarg, RCLASS_CSR, ®no))
INSERT_OPERAND (CSR, *ip, regno);
else
{
- my_getExpression (imm_expr, s);
- check_absolute_expr (ip, imm_expr, TRUE);
+ my_getExpression (imm_expr, asarg);
+ check_absolute_expr (ip, imm_expr, true);
if ((unsigned long) imm_expr->X_add_number > 0xfff)
- as_bad (_("Improper CSR address (%lu)"),
+ as_bad (_("improper CSR address (%lu)"),
(unsigned long) imm_expr->X_add_number);
INSERT_OPERAND (CSR, *ip, imm_expr->X_add_number);
imm_expr->X_op = O_absent;
- s = expr_end;
+ asarg = expr_end;
}
continue;
- case 'm': /* Rounding mode. */
- if (arg_lookup (&s, riscv_rm, ARRAY_SIZE (riscv_rm), ®no))
+ case 'm': /* Rounding mode. */
+ if (arg_lookup (&asarg, riscv_rm,
+ ARRAY_SIZE (riscv_rm), ®no))
{
INSERT_OPERAND (RM, *ip, regno);
continue;
break;
case 'P':
- case 'Q': /* Fence predecessor/successor. */
- if (arg_lookup (&s, riscv_pred_succ, ARRAY_SIZE (riscv_pred_succ),
- ®no))
+ case 'Q': /* Fence predecessor/successor. */
+ 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);
}
break;
- case 'd': /* Destination register. */
- case 's': /* Source register. */
- case 't': /* Target register. */
- case 'r': /* rs3. */
- if (reg_lookup (&s, RCLASS_GPR, ®no))
+ case 'd': /* Destination register. */
+ case 's': /* Source register. */
+ case 't': /* Target register. */
+ case 'r': /* RS3 */
+ 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. */
}
break;
- case 'D': /* Floating point rd. */
- case 'S': /* Floating point rs1. */
- case 'T': /* Floating point rs2. */
- case 'U': /* Floating point rs1 and rs2. */
- case 'R': /* Floating point rs3. */
- if (reg_lookup (&s, RCLASS_FPR, ®no))
+ case 'D': /* Floating point RD. */
+ case 'S': /* Floating point RS1. */
+ case 'T': /* Floating point RS2. */
+ case 'U': /* Floating point RS1 and RS2. */
+ case 'R': /* Floating point RS3. */
+ 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 'U':
INSERT_OPERAND (RS1, *ip, regno);
- /* fallthru */
+ /* Fall through. */
case 'T':
INSERT_OPERAND (RS2, *ip, regno);
break;
}
continue;
}
-
break;
case 'I':
- my_getExpression (imm_expr, s);
+ my_getExpression (imm_expr, asarg);
if (imm_expr->X_op != O_big
&& imm_expr->X_op != O_constant)
break;
normalize_constant_expr (imm_expr);
- s = expr_end;
+ asarg = expr_end;
continue;
case 'A':
- my_getExpression (imm_expr, s);
+ my_getExpression (imm_expr, asarg);
normalize_constant_expr (imm_expr);
/* The 'A' format specifier must be a symbol. */
if (imm_expr->X_op != O_symbol)
break;
*imm_reloc = BFD_RELOC_32;
- s = expr_end;
+ asarg = expr_end;
continue;
case 'B':
- my_getExpression (imm_expr, s);
+ my_getExpression (imm_expr, asarg);
normalize_constant_expr (imm_expr);
/* The 'B' format specifier must be a symbol or a constant. */
if (imm_expr->X_op != O_symbol && imm_expr->X_op != O_constant)
break;
if (imm_expr->X_op == O_symbol)
*imm_reloc = BFD_RELOC_32;
- s = expr_end;
+ asarg = expr_end;
continue;
case 'j': /* Sign-extended immediate. */
p = percent_op_itype;
*imm_reloc = BFD_RELOC_RISCV_LO12_I;
goto load_store;
- case '1': /* 4-operand add, must be %tprel_add. */
+ case '1':
+ /* This is used for TLS, where the fourth operand is
+ %tprel_add, to get a relocation applied to an add
+ instruction, for relaxation to use. */
p = percent_op_rtype;
goto alu_op;
- case '0': /* AMO "displacement," which must be zero. */
+ case '0': /* AMO displacement, which must be zero. */
p = percent_op_null;
load_store:
- if (riscv_handle_implicit_zero_offset (imm_expr, s))
+ if (riscv_handle_implicit_zero_offset (imm_expr, asarg))
continue;
alu_op:
/* If this value won't fit into a 16 bit offset, then go
find a macro that will generate the 32 bit offset
code pattern. */
- if (!my_getSmallExpression (imm_expr, imm_reloc, s, p))
+ if (!my_getSmallExpression (imm_expr, imm_reloc, asarg, p))
{
normalize_constant_expr (imm_expr);
if (imm_expr->X_op != O_constant
- || (*args == '0' && imm_expr->X_add_number != 0)
- || (*args == '1')
+ || (*oparg == '0' && imm_expr->X_add_number != 0)
+ || (*oparg == '1')
|| imm_expr->X_add_number >= (signed)RISCV_IMM_REACH/2
|| imm_expr->X_add_number < -(signed)RISCV_IMM_REACH/2)
break;
}
-
- s = expr_end;
+ asarg = expr_end;
continue;
- case 'p': /* PC-relative offset. */
+ case 'p': /* PC-relative offset. */
branch:
*imm_reloc = BFD_RELOC_12_PCREL;
- my_getExpression (imm_expr, s);
- s = expr_end;
+ my_getExpression (imm_expr, asarg);
+ asarg = expr_end;
continue;
- case 'u': /* Upper 20 bits. */
+ case 'u': /* Upper 20 bits. */
p = percent_op_utype;
- if (!my_getSmallExpression (imm_expr, imm_reloc, s, p))
+ if (!my_getSmallExpression (imm_expr, imm_reloc, asarg, p))
{
if (imm_expr->X_op != O_constant)
break;
*imm_reloc = BFD_RELOC_RISCV_HI20;
imm_expr->X_add_number <<= RISCV_IMM_BITS;
}
- s = expr_end;
+ asarg = expr_end;
continue;
- case 'a': /* 20-bit PC-relative offset. */
+ case 'a': /* 20-bit PC-relative offset. */
jump:
- my_getExpression (imm_expr, s);
- s = expr_end;
+ my_getExpression (imm_expr, asarg);
+ asarg = expr_end;
*imm_reloc = BFD_RELOC_RISCV_JMP;
continue;
case 'c':
- my_getExpression (imm_expr, s);
- s = expr_end;
- if (strcmp (s, "@plt") == 0)
- {
- *imm_reloc = BFD_RELOC_RISCV_CALL_PLT;
- s += 4;
- }
- else
- *imm_reloc = BFD_RELOC_RISCV_CALL;
+ my_getExpression (imm_expr, asarg);
+ asarg = expr_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, p)
|| imm_expr->X_op != O_constant
|| imm_expr->X_add_number < 0
|| imm_expr->X_add_number >= 128
"lower 2 bits must be 0x3"));
break;
}
-
INSERT_OPERAND (OP, *ip, imm_expr->X_add_number);
imm_expr->X_op = O_absent;
- s = expr_end;
+ asarg = expr_end;
continue;
+
case '2':
- if (my_getOpcodeExpression (imm_expr, imm_reloc, s, p)
+ if (my_getOpcodeExpression (imm_expr, imm_reloc, asarg, p)
|| imm_expr->X_op != O_constant
|| imm_expr->X_add_number < 0
|| imm_expr->X_add_number >= 3)
"value must be 0...2"));
break;
}
-
INSERT_OPERAND (OP2, *ip, imm_expr->X_add_number);
imm_expr->X_op = O_absent;
- s = expr_end;
+ asarg = expr_end;
continue;
+
default:
- as_bad (_("bad Opcode field specifier 'O%c'\n"), *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)
"value must be 0...127"));
break;
}
-
INSERT_OPERAND (FUNCT7, *ip, imm_expr->X_add_number);
imm_expr->X_op = O_absent;
- s = expr_end;
+ asarg = expr_end;
continue;
+
case '3':
- if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
+ if (my_getSmallExpression (imm_expr, imm_reloc, asarg, p)
|| imm_expr->X_op != O_constant
|| imm_expr->X_add_number < 0
|| imm_expr->X_add_number >= 8)
"value must be 0...7"));
break;
}
-
INSERT_OPERAND (FUNCT3, *ip, imm_expr->X_add_number);
imm_expr->X_op = O_absent;
- s = expr_end;
+ asarg = expr_end;
continue;
+
case '2':
- if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
+ if (my_getSmallExpression (imm_expr, imm_reloc, asarg, p)
|| imm_expr->X_op != O_constant
|| imm_expr->X_add_number < 0
|| imm_expr->X_add_number >= 4)
"value must be 0...3"));
break;
}
-
INSERT_OPERAND (FUNCT2, *ip, imm_expr->X_add_number);
imm_expr->X_op = O_absent;
- s = expr_end;
+ asarg = expr_end;
continue;
default:
- as_bad (_("bad FUNCT field specifier 'F%c'\n"), *args);
+ goto unknown_riscv_ip_operand;
}
break;
+ case 'y': /* bs immediate */
+ my_getExpression (imm_expr, asarg);
+ check_absolute_expr (ip, imm_expr, FALSE);
+ if ((unsigned long)imm_expr->X_add_number > 3)
+ as_bad(_("Improper bs immediate (%lu)"),
+ (unsigned long)imm_expr->X_add_number);
+ INSERT_OPERAND(BS, *ip, imm_expr->X_add_number);
+ imm_expr->X_op = O_absent;
+ asarg = expr_end;
+ continue;
+
+ case 'Y': /* rnum immediate */
+ my_getExpression (imm_expr, asarg);
+ check_absolute_expr (ip, imm_expr, FALSE);
+ if ((unsigned long)imm_expr->X_add_number > 10)
+ as_bad(_("Improper rnum immediate (%lu)"),
+ (unsigned long)imm_expr->X_add_number);
+ INSERT_OPERAND(RNUM, *ip, imm_expr->X_add_number);
+ imm_expr->X_op = O_absent;
+ asarg = expr_end;
+ continue;
+
case 'z':
- if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
+ if (my_getSmallExpression (imm_expr, imm_reloc, asarg, p)
|| imm_expr->X_op != O_constant
|| imm_expr->X_add_number != 0)
break;
- s = expr_end;
+ asarg = expr_end;
+ imm_expr->X_op = O_absent;
+ continue;
+
+ case 'f': /* Prefetch offset, pseudo S-type but lower 5-bits zero. */
+ if (riscv_handle_implicit_zero_offset (imm_expr, asarg))
+ continue;
+ my_getExpression (imm_expr, asarg);
+ check_absolute_expr (ip, imm_expr, false);
+ if (((unsigned) (imm_expr->X_add_number) & 0x1fU)
+ || imm_expr->X_add_number >= (signed) RISCV_IMM_REACH / 2
+ || imm_expr->X_add_number < -(signed) RISCV_IMM_REACH / 2)
+ as_bad (_("improper prefetch offset (%ld)"),
+ (long) imm_expr->X_add_number);
+ ip->insn_opcode |=
+ ENCODE_STYPE_IMM ((unsigned) (imm_expr->X_add_number) &
+ ~ 0x1fU);
imm_expr->X_op = O_absent;
+ asarg = expr_end;
continue;
default:
- as_fatal (_("internal error: bad argument type %c"), *args);
+ unknown_riscv_ip_operand:
+ as_fatal (_("internal: unknown argument type `%s'"),
+ opargStart);
}
break;
}
- s = argsStart;
- error = _("illegal operands");
- insn_with_csr = FALSE;
+ asarg = asargStart;
+ insn_with_csr = false;
}
out:
/* Restore the character we might have clobbered above. */
if (save_c)
- *(argsStart - 1) = save_c;
+ *(asargStart - 1) = save_c;
return error;
}
+/* Similar to riscv_ip, but assembles an instruction according to the
+ hardcode values of .insn directive. */
+
+static const char *
+riscv_ip_hardcode (char *str,
+ struct riscv_cl_insn *ip,
+ expressionS *imm_expr,
+ const char *error)
+{
+ struct riscv_opcode *insn;
+ insn_t values[2] = {0, 0};
+ unsigned int num = 0;
+
+ input_line_pointer = str;
+ do
+ {
+ expression (imm_expr);
+ if (imm_expr->X_op != O_constant)
+ {
+ /* The first value isn't constant, so it should be
+ .insn <type> <operands>. We have been parsed it
+ in the riscv_ip. */
+ if (num == 0)
+ return error;
+ return _("values must be constant");
+ }
+ values[num++] = (insn_t) imm_expr->X_add_number;
+ }
+ while (*input_line_pointer++ == ',' && num < 2);
+
+ input_line_pointer--;
+ if (*input_line_pointer != '\0')
+ return _("unrecognized values");
+
+ insn = XNEW (struct riscv_opcode);
+ insn->match = values[num - 1];
+ create_insn (ip, insn);
+ unsigned int bytes = riscv_insn_length (insn->match);
+ if ((bytes < sizeof(values[0]) && values[num - 1] >> (8 * bytes) != 0)
+ || (num == 2 && values[0] != bytes))
+ return _("value conflicts with instruction length");
+
+ return NULL;
+}
+
void
md_assemble (char *str)
{
expressionS imm_expr;
bfd_reloc_code_real_type imm_reloc = BFD_RELOC_UNUSED;
- /* The arch and priv attributes should be set before assembling. */
+ /* The architecture and privileged elf attributes should be set
+ before assembling. */
if (!start_assemble)
{
- start_assemble = TRUE;
- riscv_set_abi_by_arch ();
+ start_assemble = true;
- /* Set the default_priv_spec according to the priv attributes. */
+ riscv_set_abi_by_arch ();
if (!riscv_set_default_priv_spec (NULL))
return;
}
- const char *error = riscv_ip (str, &insn, &imm_expr, &imm_reloc, op_hash);
+ riscv_mapping_state (MAP_INSN, 0);
- if (error)
+ const struct riscv_ip_error error = riscv_ip (str, &insn, &imm_expr,
+ &imm_reloc, op_hash);
+
+ 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;
}
void
md_number_to_chars (char *buf, valueT val, int n)
{
- number_to_chars_littleendian (buf, val, n);
+ if (target_big_endian)
+ number_to_chars_bigendian (buf, val, n);
+ else
+ number_to_chars_littleendian (buf, val, n);
}
const char *md_shortopts = "O::g::G:";
OPTION_NO_CSR_CHECK,
OPTION_MISA_SPEC,
OPTION_MPRIV_SPEC,
+ OPTION_BIG_ENDIAN,
+ OPTION_LITTLE_ENDIAN,
OPTION_END_OF_ENUM
};
{"mno-csr-check", no_argument, NULL, OPTION_NO_CSR_CHECK},
{"misa-spec", required_argument, NULL, OPTION_MISA_SPEC},
{"mpriv-spec", required_argument, NULL, OPTION_MPRIV_SPEC},
+ {"mbig-endian", no_argument, NULL, OPTION_BIG_ENDIAN},
+ {"mlittle-endian", no_argument, NULL, OPTION_LITTLE_ENDIAN},
{NULL, no_argument, NULL, 0}
};
switch (c)
{
case OPTION_MARCH:
- /* riscv_after_parse_args will call riscv_set_arch to parse
- the architecture. */
default_arch_with_ext = arg;
break;
case OPTION_NO_PIC:
- riscv_opts.pic = FALSE;
+ riscv_opts.pic = false;
break;
case OPTION_PIC:
- riscv_opts.pic = TRUE;
+ riscv_opts.pic = true;
break;
case OPTION_MABI:
if (strcmp (arg, "ilp32") == 0)
- riscv_set_abi (32, FLOAT_ABI_SOFT, FALSE);
+ riscv_set_abi (32, FLOAT_ABI_SOFT, false);
else if (strcmp (arg, "ilp32e") == 0)
- riscv_set_abi (32, FLOAT_ABI_SOFT, TRUE);
+ riscv_set_abi (32, FLOAT_ABI_SOFT, true);
else if (strcmp (arg, "ilp32f") == 0)
- riscv_set_abi (32, FLOAT_ABI_SINGLE, FALSE);
+ riscv_set_abi (32, FLOAT_ABI_SINGLE, false);
else if (strcmp (arg, "ilp32d") == 0)
- riscv_set_abi (32, FLOAT_ABI_DOUBLE, FALSE);
+ riscv_set_abi (32, FLOAT_ABI_DOUBLE, false);
else if (strcmp (arg, "ilp32q") == 0)
- riscv_set_abi (32, FLOAT_ABI_QUAD, FALSE);
+ riscv_set_abi (32, FLOAT_ABI_QUAD, false);
else if (strcmp (arg, "lp64") == 0)
- riscv_set_abi (64, FLOAT_ABI_SOFT, FALSE);
+ riscv_set_abi (64, FLOAT_ABI_SOFT, false);
else if (strcmp (arg, "lp64f") == 0)
- riscv_set_abi (64, FLOAT_ABI_SINGLE, FALSE);
+ riscv_set_abi (64, FLOAT_ABI_SINGLE, false);
else if (strcmp (arg, "lp64d") == 0)
- riscv_set_abi (64, FLOAT_ABI_DOUBLE, FALSE);
+ riscv_set_abi (64, FLOAT_ABI_DOUBLE, false);
else if (strcmp (arg, "lp64q") == 0)
- riscv_set_abi (64, FLOAT_ABI_QUAD, FALSE);
+ riscv_set_abi (64, FLOAT_ABI_QUAD, false);
else
return 0;
- explicit_mabi = TRUE;
+ explicit_mabi = true;
break;
case OPTION_RELAX:
- riscv_opts.relax = TRUE;
+ riscv_opts.relax = true;
break;
case OPTION_NO_RELAX:
- riscv_opts.relax = FALSE;
+ riscv_opts.relax = false;
break;
case OPTION_ARCH_ATTR:
- riscv_opts.arch_attr = TRUE;
+ riscv_opts.arch_attr = true;
break;
case OPTION_NO_ARCH_ATTR:
- riscv_opts.arch_attr = FALSE;
+ riscv_opts.arch_attr = false;
break;
case OPTION_CSR_CHECK:
- riscv_opts.csr_check = TRUE;
+ riscv_opts.csr_check = true;
break;
case OPTION_NO_CSR_CHECK:
- riscv_opts.csr_check = FALSE;
+ riscv_opts.csr_check = false;
break;
case OPTION_MISA_SPEC:
case OPTION_MPRIV_SPEC:
return riscv_set_default_priv_spec (arg);
+ case OPTION_BIG_ENDIAN:
+ target_big_endian = 1;
+ break;
+
+ case OPTION_LITTLE_ENDIAN:
+ target_big_endian = 0;
+ break;
+
default:
return 0;
}
void
riscv_after_parse_args (void)
{
- /* The --with-arch is optional for now, so we have to set the xlen
- according to the default_arch, which is set by the --targte, first.
- Then, we use the xlen to set the default_arch_with_ext if the
- -march and --with-arch are not set. */
+ /* The --with-arch is optional for now, so we still need to set the xlen
+ according to the default_arch, which is set by the --target. */
if (xlen == 0)
{
if (strcmp (default_arch, "riscv32") == 0)
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 (riscv_ext_version_table);
- /* If the -misa-spec isn't set, then we set the default ISA spec according
- to DEFAULT_RISCV_ISA_SPEC. */
+ /* Set default specs. */
if (default_isa_spec == ISA_SPEC_CLASS_NONE)
riscv_set_default_isa_spec (DEFAULT_RISCV_ISA_SPEC);
-
- /* Set the architecture according to -march or or --with-arch. */
- riscv_set_arch (default_arch_with_ext);
-
- /* Add the RVC extension, regardless of -march, to support .option rvc. */
- riscv_set_rvc (FALSE);
- if (riscv_subset_supports ("c"))
- riscv_set_rvc (TRUE);
-
- /* Enable RVE if specified by the -march option. */
- riscv_set_rve (FALSE);
- if (riscv_subset_supports ("e"))
- riscv_set_rve (TRUE);
-
- /* If the -mpriv-spec isn't set, then we set the default privilege spec
- according to DEFAULT_PRIV_SPEC. */
if (default_priv_spec == PRIV_SPEC_CLASS_NONE)
riscv_set_default_priv_spec (DEFAULT_RISCV_PRIV_SPEC);
+ riscv_set_arch (default_arch_with_ext);
+
/* If the CIE to be produced has not been overridden on the command line,
then produce version 3 by default. This allows us to use the full
range of registers in a .cfi_return_column directive. */
{
unsigned int subtype;
bfd_byte *buf = (bfd_byte *) (fixP->fx_frag->fr_literal + fixP->fx_where);
- bfd_boolean relaxable = FALSE;
+ bool relaxable = false;
offsetT loc;
segT sub_segment;
bfd_putl32 (riscv_apply_const_reloc (fixP->fx_r_type, *valP)
| bfd_getl32 (buf), buf);
if (fixP->fx_addsy == NULL)
- fixP->fx_done = TRUE;
- relaxable = TRUE;
+ fixP->fx_done = true;
+ relaxable = true;
break;
case BFD_RELOC_RISCV_GOT_HI20:
case BFD_RELOC_RISCV_TPREL_LO12_I:
case BFD_RELOC_RISCV_TPREL_LO12_S:
case BFD_RELOC_RISCV_TPREL_ADD:
- relaxable = TRUE;
+ relaxable = true;
/* Fall through. */
case BFD_RELOC_RISCV_TLS_GOT_HI20:
_bfd_elf_discard_section_eh_frame, and the content of
.eh_frame will be adjusted in _bfd_elf_write_section_eh_frame.
Therefore, we cannot insert a relocation whose addend symbol is
- in .eh_frame. Othrewise, the value may be adjusted twice.*/
+ in .eh_frame. Othrewise, the value may be adjusted twice. */
if (fixP->fx_addsy && fixP->fx_subsy
&& (sub_segment = S_GET_SEGMENT (fixP->fx_subsy))
&& strcmp (sub_segment->name, ".eh_frame") == 0
fixP->fx_next->fx_r_type = BFD_RELOC_RISCV_SUB6;
}
else
- as_fatal (_("internal error: bad CFA value #%d"), subtype);
+ as_fatal (_("internal: bad CFA value #%d"), subtype);
break;
}
break;
/* Fill in a tentative value to improve objdump readability. */
bfd_vma target = S_GET_VALUE (fixP->fx_addsy) + *valP;
bfd_vma delta = target - md_pcrel_from (fixP);
- bfd_putl32 (bfd_getl32 (buf) | ENCODE_UJTYPE_IMM (delta), buf);
+ bfd_putl32 (bfd_getl32 (buf) | ENCODE_JTYPE_IMM (delta), buf);
}
break;
/* Fill in a tentative value to improve objdump readability. */
bfd_vma target = S_GET_VALUE (fixP->fx_addsy) + *valP;
bfd_vma delta = target - md_pcrel_from (fixP);
- bfd_putl32 (bfd_getl32 (buf) | ENCODE_SBTYPE_IMM (delta), buf);
+ bfd_putl32 (bfd_getl32 (buf) | ENCODE_BTYPE_IMM (delta), buf);
}
break;
/* Fill in a tentative value to improve objdump readability. */
bfd_vma target = S_GET_VALUE (fixP->fx_addsy) + *valP;
bfd_vma delta = target - md_pcrel_from (fixP);
- bfd_putl16 (bfd_getl16 (buf) | ENCODE_RVC_B_IMM (delta), buf);
+ bfd_putl16 (bfd_getl16 (buf) | ENCODE_CBTYPE_IMM (delta), buf);
}
break;
/* Fill in a tentative value to improve objdump readability. */
bfd_vma target = S_GET_VALUE (fixP->fx_addsy) + *valP;
bfd_vma delta = target - md_pcrel_from (fixP);
- bfd_putl16 (bfd_getl16 (buf) | ENCODE_RVC_J_IMM (delta), buf);
+ bfd_putl16 (bfd_getl16 (buf) | ENCODE_CJTYPE_IMM (delta), buf);
}
break;
case BFD_RELOC_RISCV_CALL:
case BFD_RELOC_RISCV_CALL_PLT:
- relaxable = TRUE;
+ relaxable = true;
break;
case BFD_RELOC_RISCV_PCREL_HI20:
default:
/* We ignore generic BFD relocations we don't know about. */
if (bfd_reloc_type_lookup (stdoutput, fixP->fx_r_type) != NULL)
- as_fatal (_("internal error: bad relocation #%d"), fixP->fx_r_type);
+ as_fatal (_("internal: bad relocation #%d"), fixP->fx_r_type);
}
if (fixP->fx_subsy != NULL)
- as_bad_where (fixP->fx_file, fixP->fx_line,
- _("unsupported symbol subtraction"));
+ as_bad_subtract (fixP);
/* Add an R_RISCV_RELAX reloc if the reloc is relaxable. */
if (relaxable && fixP->fx_tcbit && fixP->fx_addsy != NULL)
subseg_set (seg, subseg);
}
-
-/* This structure is used to hold a stack of .option values. */
-
-struct riscv_option_stack
-{
- struct riscv_option_stack *next;
- struct riscv_set_options options;
-};
-
-static struct riscv_option_stack *riscv_opts_stack;
-
/* Handle the .option pseudo-op. */
static void
*input_line_pointer = '\0';
if (strcmp (name, "rvc") == 0)
- riscv_set_rvc (TRUE);
+ {
+ riscv_update_subset (&riscv_rps_as, "+c");
+ riscv_set_rvc (true);
+ }
else if (strcmp (name, "norvc") == 0)
- riscv_set_rvc (FALSE);
+ {
+ riscv_update_subset (&riscv_rps_as, "-c");
+ riscv_set_rvc (false);
+ }
else if (strcmp (name, "pic") == 0)
- riscv_opts.pic = TRUE;
+ riscv_opts.pic = true;
else if (strcmp (name, "nopic") == 0)
- riscv_opts.pic = FALSE;
+ riscv_opts.pic = false;
else if (strcmp (name, "relax") == 0)
- riscv_opts.relax = TRUE;
+ riscv_opts.relax = true;
else if (strcmp (name, "norelax") == 0)
- riscv_opts.relax = FALSE;
+ riscv_opts.relax = false;
else if (strcmp (name, "csr-check") == 0)
- riscv_opts.csr_check = TRUE;
+ riscv_opts.csr_check = true;
else if (strcmp (name, "no-csr-check") == 0)
- riscv_opts.csr_check = FALSE;
+ riscv_opts.csr_check = false;
+ else if (strncmp (name, "arch,", 5) == 0)
+ {
+ name += 5;
+ if (ISSPACE (*name) && *name != '\0')
+ name++;
+ riscv_update_subset (&riscv_rps_as, name);
+
+ riscv_set_rvc (false);
+ if (riscv_subset_supports (&riscv_rps_as, "c"))
+ riscv_set_rvc (true);
+
+ 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_subsets;
riscv_opts_stack = s;
+ riscv_subsets = riscv_copy_subset_list (s->subset_list);
+ riscv_rps_as.subset_list = riscv_subsets;
}
else if (strcmp (name, "pop") == 0)
{
as_bad (_(".option pop with no .option push"));
else
{
- riscv_opts = s->options;
+ riscv_subset_list_t *release_subsets = riscv_subsets;
riscv_opts_stack = s->next;
+ riscv_opts = s->options;
+ riscv_subsets = s->subset_list;
+ riscv_rps_as.subset_list = riscv_subsets;
+ riscv_release_subset_list (release_subsets);
free (s);
}
}
else
{
- as_warn (_("Unrecognized .option directive: %s\n"), name);
+ as_warn (_("unrecognized .option directive: %s\n"), name);
}
*input_line_pointer = ch;
demand_empty_rest_of_line ();
if (ex.X_op != O_symbol)
{
- as_bad (_("Unsupported use of %s"), (bytes == 8
+ as_bad (_("unsupported use of %s"), (bytes == 8
? ".dtpreldword"
: ".dtprelword"));
ignore_rest_of_line ();
p = frag_more (bytes);
md_number_to_chars (p, 0, bytes);
- fix_new_exp (frag_now, p - frag_now->fr_literal, bytes, &ex, FALSE,
+ fix_new_exp (frag_now, p - frag_now->fr_literal, bytes, &ex, false,
(bytes == 8
? BFD_RELOC_RISCV_TLS_DTPREL64
: BFD_RELOC_RISCV_TLS_DTPREL32));
/* Use at most one 2-byte NOP. */
if ((bytes - i) % 4 == 2)
{
- md_number_to_chars (buf + i, RVC_NOP, 2);
+ number_to_chars_littleendian (buf + i, RVC_NOP, 2);
i += 2;
}
/* Fill the remainder with 4-byte NOPs. */
for ( ; i < bytes; i += 4)
- md_number_to_chars (buf + i, RISCV_NOP, 4);
+ number_to_chars_littleendian (buf + i, RISCV_NOP, 4);
}
/* Called from md_do_align. Used to create an alignment frag in a
will later relax to the correct number of NOPs. We can't compute
the correct alignment now because of other linker relaxations. */
-bfd_boolean
+bool
riscv_frag_align_code (int n)
{
bfd_vma bytes = (bfd_vma) 1 << n;
/* If we are moving to a smaller alignment than the instruction size, then no
alignment is required. */
if (bytes <= insn_alignment)
- return TRUE;
+ return true;
/* When not relaxing, riscv_handle_align handles code alignment. */
if (!riscv_opts.relax)
- return FALSE;
+ return false;
nops = frag_more (worst_case_bytes);
riscv_make_nops (nops, worst_case_bytes);
fix_new_exp (frag_now, nops - frag_now->fr_literal, 0,
- &ex, FALSE, BFD_RELOC_RISCV_ALIGN);
+ &ex, false, BFD_RELOC_RISCV_ALIGN);
+
+ riscv_mapping_state (MAP_INSN, worst_case_bytes);
+
+ /* We need to start a new frag after the alignment which may be removed by
+ the linker, to prevent the assembler from computing static offsets.
+ This is necessary to get correct EH info. */
+ frag_wane (frag_now);
+ frag_new (0);
- return TRUE;
+ return true;
}
/* Implement HANDLE_ALIGN. */
/* We have 4 byte uncompressed nops. */
bfd_signed_vma size = 4;
bfd_signed_vma excess = bytes % size;
+ bfd_boolean odd_padding = (excess % 2 == 1);
char *p = fragP->fr_literal + fragP->fr_fix;
if (bytes <= 0)
/* Insert zeros or compressed nops to get 4 byte alignment. */
if (excess)
{
+ if (odd_padding)
+ riscv_add_odd_padding_symbol (fragP);
riscv_make_nops (p, excess);
fragP->fr_fix += excess;
p += excess;
}
- /* Insert variable number of 4 byte uncompressed nops. */
+ /* The frag will be changed to `rs_fill` later. The function
+ `write_contents` will try to fill the remaining spaces
+ according to the patterns we give. In this case, we give
+ a 4 byte uncompressed nop as the pattern, and set the size
+ of the pattern into `fr_var`. The nop will be output to the
+ file `fr_offset` times. However, `fr_offset` could be zero
+ if we don't need to pad the boundary finally. */
riscv_make_nops (p, size);
fragP->fr_var = size;
}
}
}
+/* This usually called from frag_var. */
+
+void
+riscv_init_frag (fragS * fragP, int max_chars)
+{
+ /* Do not add mapping symbol to debug sections. */
+ if (bfd_section_flags (now_seg) & SEC_DEBUGGING)
+ return;
+
+ switch (fragP->fr_type)
+ {
+ case rs_fill:
+ case rs_align:
+ case rs_align_test:
+ riscv_mapping_state (MAP_DATA, max_chars);
+ break;
+ case rs_align_code:
+ riscv_mapping_state (MAP_INSN, max_chars);
+ break;
+ default:
+ break;
+ }
+}
+
int
md_estimate_size_before_relax (fragS *fragp, asection *segtype)
{
- return (fragp->fr_var = relaxed_branch_length (fragp, segtype, FALSE));
+ return (fragp->fr_var = relaxed_branch_length (fragp, segtype, false));
}
/* Translate internal representation of relocation info to BFD target
if (RELAX_BRANCH_P (fragp->fr_subtype))
{
offsetT old_var = fragp->fr_var;
- fragp->fr_var = relaxed_branch_length (fragp, sec, TRUE);
+ fragp->fr_var = relaxed_branch_length (fragp, sec, true);
return fragp->fr_var - old_var;
}
/* Invert the branch condition. Branch over the jump. */
insn = bfd_getl16 (buf);
insn ^= MATCH_C_BEQZ ^ MATCH_C_BNEZ;
- insn |= ENCODE_RVC_B_IMM (6);
+ insn |= ENCODE_CBTYPE_IMM (6);
bfd_putl16 (insn, buf);
buf += 2;
goto jump;
reloc = RELAX_BRANCH_UNCOND (fragp->fr_subtype)
? BFD_RELOC_RISCV_RVC_JUMP : BFD_RELOC_RISCV_RVC_BRANCH;
fixp = fix_new_exp (fragp, buf - (bfd_byte *)fragp->fr_literal,
- 2, &exp, FALSE, reloc);
+ 2, &exp, false, reloc);
buf += 2;
goto done;
/* Invert the branch condition. Branch over the jump. */
insn = bfd_getl32 (buf);
insn ^= MATCH_BEQ ^ MATCH_BNE;
- insn |= ENCODE_SBTYPE_IMM (8);
- md_number_to_chars ((char *) buf, insn, 4);
+ insn |= ENCODE_BTYPE_IMM (8);
+ bfd_putl32 (insn, buf);
buf += 4;
jump:
/* Jump to the target. */
fixp = fix_new_exp (fragp, buf - (bfd_byte *)fragp->fr_literal,
- 4, &exp, FALSE, BFD_RELOC_RISCV_JMP);
- md_number_to_chars ((char *) buf, MATCH_JAL, 4);
+ 4, &exp, false, BFD_RELOC_RISCV_JMP);
+ bfd_putl32 (MATCH_JAL, buf);
buf += 4;
break;
reloc = RELAX_BRANCH_UNCOND (fragp->fr_subtype)
? BFD_RELOC_RISCV_JMP : BFD_RELOC_12_PCREL;
fixp = fix_new_exp (fragp, buf - (bfd_byte *)fragp->fr_literal,
- 4, &exp, FALSE, reloc);
+ 4, &exp, false, reloc);
buf += 4;
break;
{
fprintf (stream, _("\
RISC-V options:\n\
- -fpic generate position-independent code\n\
+ -fpic or -fPIC generate position-independent code\n\
-fno-pic don't generate position-independent code (default)\n\
-march=ISA set the RISC-V architecture\n\
-misa-spec=ISAspec set the RISC-V ISA spec (2.2, 20190608, 20191213)\n\
- -mpriv-spec=PRIVspec set the RISC-V privilege spec (1.9, 1.9.1, 1.10, 1.11)\n\
+ -mpriv-spec=PRIVspec set the RISC-V privilege spec (1.9.1, 1.10, 1.11, 1.12)\n\
-mabi=ABI set the RISC-V ABI\n\
-mrelax enable relax (default)\n\
-mno-relax disable relax\n\
-march-attr generate RISC-V arch attribute\n\
-mno-arch-attr don't generate RISC-V arch attribute\n\
+ -mcsr-check enable the csr ISA and privilege spec version checks\n\
+ -mno-csr-check disable the csr ISA and privilege spec version checks (default)\n\
+ -mbig-endian assemble for big-endian\n\
+ -mlittle-endian assemble for little-endian\n\
"));
}
/* Standard calling conventions leave the CFA at SP on entry. */
+
void
riscv_cfi_frame_initial_instructions (void)
{
return s_leb128 (sign);
}
-/* Parse the .insn directive. */
+/* Parse the .insn directive. There are three formats,
+ Format 1: .insn <type> <operand1>, <operand2>, ...
+ Format 2: .insn <length>, <value>
+ Format 3: .insn <value>. */
static void
s_riscv_insn (int x ATTRIBUTE_UNUSED)
save_c = *input_line_pointer;
*input_line_pointer = '\0';
- const char *error = riscv_ip (str, &insn, &imm_expr,
+ riscv_mapping_state (MAP_INSN, 0);
+
+ struct riscv_ip_error error = riscv_ip (str, &insn, &imm_expr,
&imm_reloc, insn_type_hash);
+ if (error.msg)
+ {
+ char *save_in = input_line_pointer;
+ error.msg = riscv_ip_hardcode (str, &insn, &imm_expr, error.msg);
+ input_line_pointer = save_in;
+ }
- if (error)
+ if (error.msg)
{
- as_bad ("%s `%s'", error, str);
+ if (error.missing_ext)
+ as_bad ("%s `%s', extension `%s' required", error.msg, error.statement,
+ error.missing_ext);
+ else
+ as_bad ("%s `%s'", error.msg, error.statement);
}
else
{
demand_empty_rest_of_line ();
}
-/* Update arch and priv attributes. If we don't set the corresponding ELF
- attributes, then try to output the default ones. */
+/* Update architecture and privileged elf attributes. If we don't set
+ them, then try to output the default ones. */
static void
riscv_write_out_attrs (void)
{
const char *arch_str, *priv_str, *p;
- /* versions[0] is major, versions[1] is minor,
- and versions[3] is revision. */
+ /* versions[0]: major version.
+ versions[1]: minor version.
+ versions[2]: revision version. */
unsigned versions[3] = {0}, number = 0;
unsigned int i;
- /* Re-write arch attribute to normalize the arch string. */
- arch_str = riscv_arch_str (xlen, &riscv_subsets);
+ /* Re-write architecture elf attribute. */
+ arch_str = riscv_arch_str (xlen, riscv_subsets);
bfd_elf_add_proc_attr_string (stdoutput, Tag_RISCV_arch, arch_str);
- xfree ((void *)arch_str);
+ xfree ((void *) arch_str);
/* For the file without any instruction, we don't set the default_priv_spec
- according to the priv attributes since the md_assemble isn't called.
- Call riscv_set_default_priv_spec here for the above case, although
- it seems strange. */
+ according to the privileged elf attributes since the md_assemble isn't
+ called. */
if (!start_assemble
&& !riscv_set_default_priv_spec (NULL))
return;
- /* If we already have set elf priv attributes, then no need to do anything,
- assembler will generate them according to what you set. Otherwise, don't
- generate or update them when no CSR and priv instructions are used.
- Generate the priv attributes according to default_priv_spec, which can be
- set by -mpriv-spec and --with-priv-spec, and be updated by the original
- priv attribute sets. */
+ /* If we already have set privileged elf attributes, then no need to do
+ anything. Otherwise, don't generate or update them when no CSR and
+ privileged instructions are used. */
if (!explicit_priv_attr)
return;
- /* Re-write priv attributes by default_priv_spec. */
- priv_str = riscv_get_priv_spec_name (default_priv_spec);
+ RISCV_GET_PRIV_SPEC_NAME (priv_str, default_priv_spec);
p = priv_str;
for (i = 0; *p; ++p)
{
number = (number * 10) + (*p - '0');
else
{
- as_bad (_("internal: bad RISC-V priv spec string (%s)"), priv_str);
+ as_bad (_("internal: bad RISC-V privileged spec (%s)"), priv_str);
return;
}
}
versions[i] = number;
- /* Set the priv attributes. */
+ /* Re-write privileged elf attributes. */
bfd_elf_add_proc_attr_int (stdoutput, Tag_RISCV_priv_spec, versions[0]);
bfd_elf_add_proc_attr_int (stdoutput, Tag_RISCV_priv_spec_minor, versions[1]);
bfd_elf_add_proc_attr_int (stdoutput, Tag_RISCV_priv_spec_revision, versions[2]);
}
-/* Add the default contents for the .riscv.attributes section. If any
- ELF attribute or -march-attr options is set, call riscv_write_out_attrs
- to update the arch and priv attributes. */
+/* Add the default contents for the .riscv.attributes section. */
static void
riscv_set_public_attributes (void)
/* Called after all assembly has been done. */
void
-riscv_md_end (void)
+riscv_md_finish (void)
{
riscv_set_public_attributes ();
}
+/* Adjust the symbol table. */
+
+void
+riscv_adjust_symtab (void)
+{
+ bfd_map_over_sections (stdoutput, riscv_check_mapping_symbols, (char *) 0);
+ elf_adjust_symtab ();
+}
+
/* Given a symbolic attribute NAME, return the proper integer value.
Returns -1 if the attribute is not known. */
{
static const struct
{
- const char * name;
- const int tag;
+ const char *name;
+ const int tag;
}
attribute_table[] =
- {
- /* When you modify this table you should
- also modify the list in doc/c-riscv.texi. */
-#define T(tag) {#tag, Tag_RISCV_##tag}, {"Tag_RISCV_" #tag, Tag_RISCV_##tag}
- T(arch),
- T(priv_spec),
- T(priv_spec_minor),
- T(priv_spec_revision),
- T(unaligned_access),
- T(stack_align),
+ {
+ /* When you modify this table you should
+ also modify the list in doc/c-riscv.texi. */
+#define T(tag) {#tag, Tag_RISCV_##tag}, {"Tag_RISCV_" #tag, Tag_RISCV_##tag}
+ T(arch),
+ T(priv_spec),
+ T(priv_spec_minor),
+ T(priv_spec_revision),
+ T(unaligned_access),
+ T(stack_align),
#undef T
- };
-
- unsigned int i;
+ };
if (name == NULL)
return -1;
+ unsigned int i;
for (i = 0; i < ARRAY_SIZE (attribute_table); i++)
if (strcmp (name, attribute_table[i].name) == 0)
return attribute_table[i].tag;
unsigned old_xlen;
obj_attribute *attr;
- explicit_attr = TRUE;
+ explicit_attr = true;
switch (tag)
{
case Tag_RISCV_arch:
if (!start_assemble)
riscv_set_arch (attr[Tag_RISCV_arch].s);
else
- as_fatal (_(".attribute arch must set before any instructions"));
+ as_fatal (_("architecture elf attributes must set before "
+ "any instructions"));
if (old_xlen != xlen)
{
bfd_find_target (riscv_target_format (), stdoutput);
if (! bfd_set_arch_mach (stdoutput, bfd_arch_riscv, mach))
- as_warn (_("Could not set architecture and machine"));
+ as_warn (_("could not set architecture and machine"));
}
break;
case Tag_RISCV_priv_spec_minor:
case Tag_RISCV_priv_spec_revision:
if (start_assemble)
- as_fatal (_(".attribute priv spec must set before any instructions"));
+ as_fatal (_("privileged elf attributes must set before "
+ "any instructions"));
break;
default:
}
}
-/* Pseudo-op 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[] =
{
- /* RISC-V-specific pseudo-ops. */
{"option", s_riscv_option, 0},
{"half", cons, 2},
{"word", cons, 4},
{"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 },
};