X-Git-Url: http://git.ipfire.org/?a=blobdiff_plain;f=gas%2Fconfig%2Ftc-riscv.c;h=052199ea7404f5a8d88e6a68c2529d501b2df3d9;hb=2652cfad8d9d6ab05fe6296802ec499682a00749;hp=d011864fd700db841bf70d3441c70f3d5e95e66b;hpb=45f764234a71431b581340957a3c8338e0593fdb;p=thirdparty%2Fbinutils-gdb.git diff --git a/gas/config/tc-riscv.c b/gas/config/tc-riscv.c index d011864fd70..052199ea740 100644 --- a/gas/config/tc-riscv.c +++ b/gas/config/tc-riscv.c @@ -1,5 +1,5 @@ /* tc-riscv.c -- RISC-V assembler - Copyright 2011-2016 Free Software Foundation, Inc. + Copyright (C) 2011-2021 Free Software Foundation, Inc. Contributed by Andrew Waterman (andrew@sifive.com). Based on MIPS target. @@ -28,8 +28,8 @@ #include "itbl-ops.h" #include "dwarf2dbg.h" #include "dw2gencfi.h" -#include "struc-symbol.h" +#include "bfd/elfxx-riscv.h" #include "elf/riscv.h" #include "opcode/riscv.h" @@ -59,29 +59,143 @@ struct riscv_cl_insn #define DEFAULT_ARCH "riscv64" #endif -static const char default_arch[] = DEFAULT_ARCH; +#ifndef DEFAULT_RISCV_ATTR +#define DEFAULT_RISCV_ATTR 0 +#endif + +/* Let riscv_after_parse_args set the default value according to xlen. */ -unsigned xlen = 0; /* width of an x-register */ +#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. */ -#define LOAD_ADDRESS_INSN (xlen == 64 ? "ld" : "lw") +#ifndef DEFAULT_RISCV_ISA_SPEC +#define DEFAULT_RISCV_ISA_SPEC "2.2" +#endif + +#ifndef DEFAULT_RISCV_PRIV_SPEC +#define DEFAULT_RISCV_PRIV_SPEC "1.11" +#endif + +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 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 { + FLOAT_ABI_DEFAULT = -1, + FLOAT_ABI_SOFT, + FLOAT_ABI_SINGLE, + FLOAT_ABI_DOUBLE, + FLOAT_ABI_QUAD +}; +static enum float_abi float_abi = FLOAT_ABI_DEFAULT; + +#define LOAD_ADDRESS_INSN (abi_xlen == 64 ? "ld" : "lw") #define ADD32_INSN (xlen == 64 ? "addiw" : "addi") static unsigned elf_flags = 0; +/* Set the default_isa_spec. Return 0 if the input spec string 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)) + { + as_bad ("Unknown default ISA spec `%s' set by " + "-misa-spec or --with-isa-spec", s); + return 0; + } + else + default_isa_spec = class; + 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. */ + +static int +riscv_set_default_priv_spec (const char *s) +{ + enum riscv_priv_spec_class class; + unsigned major, minor, revision; + obj_attribute *attr; + + /* Find the corresponding priv spec class. */ + if (riscv_get_priv_spec_class (s, &class)) + { + 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); + return 0; + } + + /* Try to set the default_priv_spec according to the priv 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; + + if (riscv_get_priv_spec_class_from_numbers (major, + minor, + revision, + &class)) + { + /* 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); + 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 csr_check; /* Enable the CSR checking. */ }; static struct riscv_set_options riscv_opts = { 0, /* pic */ 0, /* rvc */ + 0, /* rve */ 1, /* relax */ + DEFAULT_RISCV_ATTR, /* arch_attr */ + 0. /* csr_check */ }; static void @@ -93,136 +207,183 @@ riscv_set_rvc (bfd_boolean rvc_value) riscv_opts.rvc = rvc_value; } -struct riscv_subset +static void +riscv_set_rve (bfd_boolean rve_value) { - const char *name; - - struct riscv_subset *next; -}; + riscv_opts.rve = rve_value; +} -static struct riscv_subset *riscv_subsets; +static riscv_subset_list_t riscv_subsets; static bfd_boolean riscv_subset_supports (const char *feature) { - struct riscv_subset *s; - char *p; - unsigned xlen_required = strtoul (feature, &p, 10); - - if (xlen_required && xlen != xlen_required) - return FALSE; + struct riscv_subset_t *subset; - for (s = riscv_subsets; s != NULL; s = s->next) - if (strcasecmp (s->name, p) == 0) - return TRUE; + if (riscv_opts.rvc && (strcasecmp (feature, "c") == 0)) + return TRUE; - return FALSE; + return riscv_lookup_subset (&riscv_subsets, feature, &subset); } -static void -riscv_add_subset (const char *subset) +static bfd_boolean +riscv_multi_subset_supports (enum riscv_insn_class insn_class) { - struct riscv_subset *s = xmalloc (sizeof *s); + switch (insn_class) + { + case INSN_CLASS_I: return riscv_subset_supports ("i"); + case INSN_CLASS_C: return riscv_subset_supports ("c"); + case INSN_CLASS_A: return riscv_subset_supports ("a"); + case INSN_CLASS_M: return riscv_subset_supports ("m"); + case INSN_CLASS_F: return riscv_subset_supports ("f"); + case INSN_CLASS_D: return riscv_subset_supports ("d"); + case INSN_CLASS_Q: return riscv_subset_supports ("q"); + + case INSN_CLASS_F_AND_C: + return (riscv_subset_supports ("f") + && riscv_subset_supports ("c")); + case INSN_CLASS_D_AND_C: + return (riscv_subset_supports ("d") + && riscv_subset_supports ("c")); + + case INSN_CLASS_ZICSR: + return riscv_subset_supports ("zicsr"); + case INSN_CLASS_ZIFENCEI: + return riscv_subset_supports ("zifencei"); + + case INSN_CLASS_ZBA: + return riscv_subset_supports ("zba"); + case INSN_CLASS_ZBB: + return riscv_subset_supports ("zbb"); + case INSN_CLASS_ZBC: + return riscv_subset_supports ("zbc"); + case INSN_CLASS_ZBA_OR_ZBB: + return (riscv_subset_supports ("zba") + || riscv_subset_supports ("zbb")); - s->name = xstrdup (subset); - s->next = riscv_subsets; - riscv_subsets = s; + default: + as_fatal ("Unreachable"); + return FALSE; + } } -/* Set which ISA and extensions are available. Formally, ISA strings must - begin with RV32 or RV64, but we allow the prefix to be omitted. +/* Handle of the extension with version hash table. */ +static htab_t ext_version_hash = NULL; - FIXME: Version numbers are not supported yet. */ -static void -riscv_set_arch (const char *p) +static htab_t +init_ext_version_hash (const struct riscv_ext_version *table) { - const char *all_subsets = "IMAFDC"; - const char *extension = NULL; - int rvc = 0; - int i; + int i = 0; + htab_t hash = str_htab_create (); - if (strncasecmp (p, "RV32", 4) == 0) + while (table[i].name) { - xlen = 32; - p += 4; + 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++; } - else if (strncasecmp (p, "RV64", 4) == 0) - { - xlen = 64; - p += 4; - } - else if (strncasecmp (p, "RV", 2) == 0) - p += 2; - - switch (TOUPPER(*p)) - { - case 'I': - break; - case 'G': - p++; - /* Fall through. */ + return hash; +} - case '\0': - for (i = 0; all_subsets[i] != '\0'; i++) - { - const char subset[] = {all_subsets[i], '\0'}; - riscv_add_subset (subset); - } - break; +static void +riscv_get_default_ext_version (const char *name, + int *major_version, + int *minor_version) +{ + struct riscv_ext_version *ext; - default: - as_fatal ("`I' must be the first ISA subset name specified (got %c)", - *p); - } + if (name == NULL || default_isa_spec == ISA_SPEC_CLASS_NONE) + return; - while (*p) + ext = (struct riscv_ext_version *) str_hash_find (ext_version_hash, name); + while (ext + && ext->name + && strcmp (ext->name, name) == 0) { - if (TOUPPER(*p) == 'X') + if (ext->isa_spec_class == ISA_SPEC_CLASS_DRAFT + || ext->isa_spec_class == default_isa_spec) { - char *subset = xstrdup (p), *q = subset; - - while (*++q != '\0' && *q != '_') - ; - *q = '\0'; - - if (extension) - as_fatal ("only one eXtension is supported (found %s and %s)", - extension, subset); - extension = subset; - riscv_add_subset (subset); - p += strlen (subset); - free (subset); - } - else if (*p == '_') - p++; - else if ((all_subsets = strchr (all_subsets, *p)) != NULL) - { - const char subset[] = {*p, 0}; - riscv_add_subset (subset); - if (TOUPPER(*p) == 'C') - rvc = 1; - all_subsets++; - p++; + *major_version = ext->major_version; + *minor_version = ext->minor_version; + return; } - else - as_fatal ("unsupported ISA subset %c", *p); + ext++; } +} - if (rvc) +/* Set which ISA and extensions are available. */ + +static void +riscv_set_arch (const char *s) +{ + riscv_parse_subset_t rps; + rps.subset_list = &riscv_subsets; + rps.error_handler = as_bad; + rps.xlen = &xlen; + rps.get_default_version = riscv_get_default_ext_version; + + if (s == NULL) + return; + + riscv_release_subset_list (&riscv_subsets); + riscv_parse_subset (&rps, s); +} + +/* Indicate -mabi= option is explictly set. */ +static bfd_boolean explicit_mabi = FALSE; + +static void +riscv_set_abi (unsigned new_xlen, enum float_abi new_float_abi, bfd_boolean rve) +{ + abi_xlen = new_xlen; + float_abi = new_float_abi; + rve_abi = rve; +} + +/* 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. */ + +static void +riscv_set_abi_by_arch (void) +{ + if (!explicit_mabi) { - /* Override -m[no-]rvc setting if C was explicitly listed. */ - riscv_set_rvc (TRUE); + 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); + else + riscv_set_abi (xlen, FLOAT_ABI_SOFT, FALSE); } else { - /* Add RVC anyway. -m[no-]rvc toggles its availability. */ - riscv_add_subset ("C"); + gas_assert (abi_xlen != 0 && xlen != 0 && float_abi != FLOAT_ABI_DEFAULT); + if (abi_xlen > xlen) + 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); } + + /* Update the EF_RISCV_FLOAT_ABI field of elf_flags. */ + elf_flags &= ~EF_RISCV_FLOAT_ABI; + elf_flags |= float_abi << 1; + + if (rve_abi) + elf_flags |= EF_RISCV_RVE; } /* Handle of the OPCODE hash table. */ -static struct hash_control *op_hash = NULL; +static htab_t op_hash = NULL; + +/* Handle of the type of .insn hash table. */ +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 */ @@ -248,6 +409,15 @@ const char EXP_CHARS[] = "eE"; /* or 0d1.2345e12 */ const char FLT_CHARS[] = "rRsSfFdDxXpP"; +/* Indicate we are already assemble any instructions or not. */ +static bfd_boolean start_assemble = FALSE; + +/* Indicate ELF attributes are explicitly set. */ +static bfd_boolean explicit_attr = FALSE; + +/* Indicate CSR or priv instructions are explicitly used. */ +static bfd_boolean explicit_priv_attr = FALSE; + /* Macros for encoding relaxation state for RVC branches and far jumps. */ #define RELAX_BRANCH_ENCODE(uncond, rvc, length) \ ((relax_substateT) \ @@ -286,7 +456,10 @@ static char *expr_end; 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. */ @@ -315,7 +488,7 @@ static void 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 @@ -373,6 +546,7 @@ relaxed_branch_length (fragS *fragp, asection *sec, int update) if (fragp->fr_symbol != NULL && S_IS_DEFINED (fragp->fr_symbol) + && !S_IS_WEAK (fragp->fr_symbol) && sec == S_GET_SEGMENT (fragp->fr_symbol)) { offsetT val = S_GET_VALUE (fragp->fr_symbol) + fragp->fr_offset; @@ -393,21 +567,116 @@ relaxed_branch_length (fragS *fragp, asection *sec, int update) return length; } -struct regname +/* Information about an opcode name, mnemonics and its value. */ +struct opcode_name_t { const char *name; - unsigned int num; + unsigned int val; +}; + +/* List for all supported opcode name. */ +static const struct opcode_name_t opcode_name_list[] = +{ + {"C0", 0x0}, + {"C1", 0x1}, + {"C2", 0x2}, + + {"LOAD", 0x03}, + {"LOAD_FP", 0x07}, + {"CUSTOM_0", 0x0b}, + {"MISC_MEM", 0x0f}, + {"OP_IMM", 0x13}, + {"AUIPC", 0x17}, + {"OP_IMM_32", 0x1b}, + /* 48b 0x1f. */ + + {"STORE", 0x23}, + {"STORE_FP", 0x27}, + {"CUSTOM_1", 0x2b}, + {"AMO", 0x2f}, + {"OP", 0x33}, + {"LUI", 0x37}, + {"OP_32", 0x3b}, + /* 64b 0x3f. */ + + {"MADD", 0x43}, + {"MSUB", 0x47}, + {"NMADD", 0x4f}, + {"NMSUB", 0x4b}, + {"OP_FP", 0x53}, + /*reserved 0x57. */ + {"CUSTOM_2", 0x5b}, + /* 48b 0x5f. */ + + {"BRANCH", 0x63}, + {"JALR", 0x67}, + /*reserved 0x5b. */ + {"JAL", 0x6f}, + {"SYSTEM", 0x73}, + /*reserved 0x77. */ + {"CUSTOM_3", 0x7b}, + /* >80b 0x7f. */ + + {NULL, 0} }; +/* Hash table for lookup opcode name. */ +static htab_t opcode_names_hash = NULL; + +/* Initialization for hash table of opcode name. */ +static void +init_opcode_names_hash (void) +{ + const struct opcode_name_t *opcode; + + 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); +} + +/* Find `s` is a valid opcode name or not, + return the opcode name info if found. */ +static const struct opcode_name_t * +opcode_name_lookup (char **s) +{ + char *e; + char save_c; + struct opcode_name_t *o; + + /* Find end of name. */ + e = *s; + if (is_name_beginner (*e)) + ++e; + while (is_part_of_name (*e)) + ++e; + + /* Terminate name. */ + save_c = *e; + *e = '\0'; + + o = (struct opcode_name_t *) str_hash_find (opcode_names_hash, *s); + + /* Advance to next token if one was recognized. */ + if (o) + *s = e; + + *e = save_c; + expr_end = e; + + return o; +} + enum reg_class { RCLASS_GPR, RCLASS_FPR, - RCLASS_CSR, - RCLASS_MAX + RCLASS_MAX, + + RCLASS_CSR }; -static struct hash_control *reg_names_hash = NULL; +static htab_t reg_names_hash = NULL; +static htab_t csr_extra_hash = NULL; #define ENCODE_REG_HASH(cls, n) \ ((void *)(uintptr_t)((n) * RCLASS_MAX + (cls) + 1)) @@ -418,10 +687,8 @@ static void hash_reg_name (enum reg_class class, const char *name, unsigned n) { void *hash = ENCODE_REG_HASH (class, n); - const char *retval = hash_insert (reg_names_hash, name, hash); - - if (retval != NULL) - as_fatal (_("internal error: can't hash `%s': %s"), name, retval); + if (str_hash_insert (reg_names_hash, name, hash, 0) != NULL) + as_fatal (_("duplicate %s"), name); } static void @@ -433,13 +700,151 @@ hash_reg_names (enum reg_class class, const char * const names[], unsigned n) hash_reg_name (class, names[i], i); } +/* 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) +{ + struct riscv_csr_extra *entry, *pre_entry; + bfd_boolean 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; + pre_entry = entry; + entry = entry->next; + } + + /* Duplicate setting for the CSR, just return and do nothing. */ + if (!need_enrty) + return; + + entry = XNEW (struct riscv_csr_extra); + 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. */ + +static unsigned int +riscv_csr_address (const char *csr_name, + struct riscv_csr_extra *entry) +{ + 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; + + switch (csr_class) + { + case CSR_CLASS_I: + result = riscv_subset_supports ("i"); + break; + case CSR_CLASS_I_32: + result = (xlen == 32 && riscv_subset_supports ("i")); + break; + case CSR_CLASS_F: + result = riscv_subset_supports ("f"); + need_check_version = FALSE; + 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); + + while (entry != NULL) + { + if (!need_check_version + || (default_priv_spec >= entry->define_version + && default_priv_spec < entry->abort_version)) + { + /* Find the suitable 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. */ + if (riscv_opts.csr_check) + { + const char *priv_name = riscv_get_priv_spec_name (default_priv_spec); + + if (priv_name != NULL) + as_warn (_("Invalid CSR `%s' for the privilege 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. */ + +static unsigned int +reg_csr_lookup_internal (const char *s) +{ + struct riscv_csr_extra *r = + (struct riscv_csr_extra *) str_hash_find (csr_extra_hash, 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); +} + static unsigned int reg_lookup_internal (const char *s, enum reg_class class) { - struct regname *r = (struct regname *) hash_find (reg_names_hash, s); + void *r; + if (class == RCLASS_CSR) + return reg_csr_lookup_internal (s); + + r = str_hash_find (reg_names_hash, s); if (r == NULL || DECODE_REG_CLASS (r) != class) return -1; + + if (riscv_opts.rve && class == RCLASS_GPR && DECODE_REG_NUM (r) > 15) + return -1; + return DECODE_REG_NUM (r); } @@ -477,6 +882,9 @@ 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; + for (i = 0; i < size; i++) if (array[i] != NULL && strncmp (array[i], *s, len) == 0) { @@ -490,15 +898,24 @@ arg_lookup (char **s, const char *const *array, size_t size, unsigned *regnop) /* For consistency checking, verify that all bits are specified either by the match/mask part of the instruction definition, or by the - operand list. */ + operand list. + + `length` could be 0, 4 or 8, 0 for auto detection. */ static bfd_boolean -validate_riscv_insn (const struct riscv_opcode *opc) +validate_riscv_insn (const struct riscv_opcode *opc, int length) { const char *p = opc->args; char c; insn_t used_bits = opc->mask; - int insn_width = 8 * riscv_insn_length (opc->match); - insn_t required_bits = ~0ULL >> (64 - insn_width); + int insn_width; + insn_t required_bits; + + if (length == 0) + insn_width = 8 * riscv_insn_length (opc->match); + else + insn_width = 8 * length; + + required_bits = ~0ULL >> (64 - insn_width); if ((used_bits & opc->match) != (opc->match & required_bits)) { @@ -518,6 +935,7 @@ validate_riscv_insn (const struct riscv_opcode *opc) 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; @@ -529,6 +947,7 @@ validate_riscv_insn (const struct riscv_opcode *opc) 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; @@ -537,8 +956,24 @@ validate_riscv_insn (const struct riscv_opcode *opc) case 'V': USE_BITS (OP_MASK_CRS2, OP_SH_CRS2); break; case '<': used_bits |= ENCODE_RVC_IMM (-1U); break; case '>': used_bits |= ENCODE_RVC_IMM (-1U); break; + case '8': used_bits |= ENCODE_RVC_UIMM8 (-1U); break; + case 'S': USE_BITS (OP_MASK_CRS1S, OP_SH_CRS1S); break; case 'T': USE_BITS (OP_MASK_CRS2, OP_SH_CRS2); break; case 'D': USE_BITS (OP_MASK_CRS2S, OP_SH_CRS2S); break; + case 'F': /* funct */ + switch (c = *p++) + { + case '6': USE_BITS (OP_MASK_CFUNCT6, OP_SH_CFUNCT6); break; + case '4': USE_BITS (OP_MASK_CFUNCT4, OP_SH_CFUNCT4); break; + case '3': USE_BITS (OP_MASK_CFUNCT3, OP_SH_CFUNCT3); break; + case '2': USE_BITS (OP_MASK_CFUNCT2, OP_SH_CFUNCT2); break; + default: + as_bad (_("internal: bad RISC-V opcode" + " (unknown operand type `CF%c'): %s %s"), + c, opc->name, opc->args); + return FALSE; + } + break; default: as_bad (_("internal: bad RISC-V opcode (unknown operand type `C%c'): %s %s"), c, opc->name, opc->args); @@ -563,6 +998,7 @@ validate_riscv_insn (const struct riscv_opcode *opc) 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': @@ -571,9 +1007,36 @@ validate_riscv_insn (const struct riscv_opcode *opc) case 'p': used_bits |= ENCODE_SBTYPE_IMM (-1U); break; case 'q': used_bits |= ENCODE_STYPE_IMM (-1U); break; case 'u': used_bits |= ENCODE_UTYPE_IMM (-1U); break; + case 'z': break; case '[': break; case ']': break; case '0': break; + case '1': break; + case 'F': /* funct */ + switch (c = *p++) + { + case '7': USE_BITS (OP_MASK_FUNCT7, OP_SH_FUNCT7); break; + case '3': USE_BITS (OP_MASK_FUNCT3, OP_SH_FUNCT3); break; + case '2': USE_BITS (OP_MASK_FUNCT2, OP_SH_FUNCT2); break; + default: + as_bad (_("internal: bad RISC-V opcode" + " (unknown operand type `F%c'): %s %s"), + c, opc->name, opc->args); + return FALSE; + } + break; + case 'O': /* opcode */ + switch (c = *p++) + { + case '4': USE_BITS (OP_MASK_OP, OP_SH_OP); break; + case '2': USE_BITS (OP_MASK_OP2, OP_SH_OP2); break; + default: + as_bad (_("internal: bad RISC-V opcode" + " (unknown operand type `F%c'): %s %s"), + c, opc->name, opc->args); + return FALSE; + } + break; default: as_bad (_("internal: bad RISC-V opcode " "(unknown operand type `%c'): %s %s"), @@ -597,55 +1060,76 @@ struct percent_op_match bfd_reloc_code_real_type reloc; }; -/* This function is called once, at assembler startup time. It should set up - all the tables, etc. that the MD part of the assembler will need. */ - -void -md_begin (void) +/* Common hash table initialization function for + instruction and .insn directive. */ +static htab_t +init_opcode_hash (const struct riscv_opcode *opcodes, + bfd_boolean insn_directive_p) { int i = 0; - - if (! bfd_set_arch_mach (stdoutput, bfd_arch_riscv, 0)) - as_warn (_("Could not set architecture and machine")); - - op_hash = hash_new (); - - while (riscv_opcodes[i].name) + int length; + htab_t hash = str_htab_create (); + while (opcodes[i].name) { - const char *name = riscv_opcodes[i].name; - const char *hash_error = - hash_insert (op_hash, name, (void *) &riscv_opcodes[i]); - - if (hash_error) - { - fprintf (stderr, _("internal error: can't hash `%s': %s\n"), - riscv_opcodes[i].name, hash_error); - /* Probably a memory allocation problem? Give up now. */ - as_fatal (_("Broken assembler. No assembly attempted.")); - } + const char *name = opcodes[i].name; + if (str_hash_insert (hash, name, &opcodes[i], 0) != NULL) + as_fatal (_("duplicate %s"), name); do { - if (riscv_opcodes[i].pinfo != INSN_MACRO) + if (opcodes[i].pinfo != INSN_MACRO) { - if (!validate_riscv_insn (&riscv_opcodes[i])) + if (insn_directive_p) + length = ((name[0] == 'c') ? 2 : 4); + else + length = 0; /* Let assembler determine the length. */ + if (!validate_riscv_insn (&opcodes[i], length)) as_fatal (_("Broken assembler. No assembly attempted.")); } + else + gas_assert (!insn_directive_p); ++i; } - while (riscv_opcodes[i].name && !strcmp (riscv_opcodes[i].name, name)); + while (opcodes[i].name && !strcmp (opcodes[i].name, name)); } - reg_names_hash = hash_new (); + return hash; +} + +/* This function is called once, at assembler startup time. It should set up + all the tables, etc. that the MD part of the assembler will need. */ + +void +md_begin (void) +{ + unsigned long mach = xlen == 64 ? bfd_mach_riscv64 : bfd_mach_riscv32; + + if (! bfd_set_arch_mach (stdoutput, bfd_arch_riscv, mach)) + as_warn (_("Could not set architecture and machine")); + + op_hash = init_opcode_hash (riscv_opcodes, FALSE); + insn_type_hash = init_opcode_hash (riscv_insn_types, TRUE); + + reg_names_hash = 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); - -#define DECLARE_CSR(name, num) hash_reg_name (RCLASS_CSR, #name, num); + /* Add "fp" as an alias for "s0". */ + hash_reg_name (RCLASS_GPR, "fp", 8); + + /* Create and insert CSR hash tables. */ + csr_extra_hash = str_htab_create (); +#define DECLARE_CSR(name, num, class, define_version, abort_version) \ + riscv_init_csr_hash (#name, num, class, define_version, abort_version); +#define DECLARE_CSR_ALIAS(name, num, class, define_version, abort_version) \ + DECLARE_CSR(name, num, class, define_version, abort_version); #include "opcode/riscv-opc.h" #undef DECLARE_CSR + opcode_names_hash = str_htab_create (); + init_opcode_names_hash (); + /* Set the default alignment for the text section. */ record_alignment (text_section, riscv_opts.rvc ? 1 : 2); } @@ -693,15 +1177,19 @@ append_insn (struct riscv_cl_insn *ip, expressionS *address_expr, int j = reloc_type == BFD_RELOC_RISCV_JMP; int best_case = riscv_insn_length (ip->insn_opcode); unsigned worst_case = relaxed_branch_length (NULL, NULL, 0); + + if (now_seg == absolute_section) + { + as_bad (_("relaxable branches not supported in absolute section")); + return; + } + add_relaxed_insn (ip, worst_case, best_case, RELAX_BRANCH_ENCODE (j, best_case == 2, worst_case), address_expr->X_add_symbol, address_expr->X_add_number); return; } - else if (address_expr->X_op == O_constant) - ip->insn_opcode |= riscv_apply_const_reloc (reloc_type, - address_expr->X_add_number); else { howto = bfd_reloc_type_lookup (stdoutput, reloc_type); @@ -718,6 +1206,19 @@ append_insn (struct riscv_cl_insn *ip, expressionS *address_expr, add_fixed_insn (ip); install_insn (ip); + + /* We need to start a new frag after any instruction that can be + optimized away or compressed by the linker during relaxation, to prevent + the assembler from computing static offsets across such an instruction. + This is necessary to get correct EH info. */ + if (reloc_type == BFD_RELOC_RISCV_HI20 + || reloc_type == BFD_RELOC_RISCV_PCREL_HI20 + || reloc_type == BFD_RELOC_RISCV_TPREL_HI20 + || reloc_type == BFD_RELOC_RISCV_TPREL_ADD) + { + frag_wane (frag_now); + frag_new (0); + } } /* Build an instruction created by a macro expansion. This is passed @@ -736,7 +1237,7 @@ macro_build (expressionS *ep, const char *name, const char *fmt, ...) va_start (args, fmt); r = BFD_RELOC_UNUSED; - mo = (struct riscv_opcode *) hash_find (op_hash, name); + mo = (struct riscv_opcode *) str_hash_find (op_hash, name); gas_assert (mo); /* Find a non-RVC variant of the instruction. append_insn will compress @@ -788,6 +1289,29 @@ macro_build (expressionS *ep, const char *name, const char *fmt, ...) append_insn (&insn, ep, r); } +/* Build an instruction created by a macro expansion. Like md_assemble but + accept a printf-style format string and arguments. */ + +static void +md_assemblef (const char *format, ...) +{ + char *buf = NULL; + va_list ap; + int r; + + va_start (ap, format); + + r = vasprintf (&buf, format, ap); + + if (r < 0) + as_fatal (_("internal error: vasprintf failed")); + + md_assemble (buf); + free(buf); + + va_end (ap); +} + /* Sign-extend 32-bit mode constants that have bit 31 set and all higher bits unset. */ static void @@ -801,13 +1325,18 @@ normalize_constant_expr (expressionS *ex) - 0x80000000); } -/* Fail if an expression is not a constant. */ +/* Fail if an expression EX is not a constant. IP is the instruction using EX. + MAYBE_CSR is true if the symbol may be an unrecognized CSR name. */ static void -check_absolute_expr (struct riscv_cl_insn *ip, expressionS *ex) +check_absolute_expr (struct riscv_cl_insn *ip, expressionS *ex, + bfd_boolean maybe_csr) { if (ex->X_op == O_big) as_bad (_("unsupported large constant")); + else if (maybe_csr && ex->X_op == O_symbol) + as_bad (_("unknown CSR `%s'"), + S_GET_NAME (ex->X_add_symbol)); else if (ex->X_op != O_constant) as_bad (_("Instruction %s requires absolute expression"), ip->insn_mo->name); @@ -817,8 +1346,8 @@ check_absolute_expr (struct riscv_cl_insn *ip, expressionS *ex) static symbolS * make_internal_label (void) { - return (symbolS *) local_symbol_make (FAKE_LABEL_NAME, now_seg, - (valueT) frag_now_fix (), frag_now); + return (symbolS *) local_symbol_make (FAKE_LABEL_NAME, now_seg, frag_now, + frag_now_fix ()); } /* Load an entry from the GOT. */ @@ -858,8 +1387,13 @@ static void riscv_call (int destreg, int tempreg, expressionS *ep, bfd_reloc_code_real_type reloc) { + /* Ensure the jalr is emitted to the same frag as the auipc. */ + frag_grow (8); macro_build (ep, "auipc", "d,u", tempreg, reloc); macro_build (NULL, "jalr", "d,s", destreg, tempreg); + /* See comment at end of append_insn. */ + frag_wane (frag_now); + frag_new (0); } /* Load an integer constant into a register. */ @@ -868,8 +1402,9 @@ static void load_const (int reg, expressionS *ep) { int shift = RISCV_IMM_BITS; + bfd_vma upper_imm, sign = (bfd_vma) 1 << (RISCV_IMM_BITS - 1); expressionS upper = *ep, lower = *ep; - lower.X_add_number = (int32_t) ep->X_add_number << (32-shift) >> (32-shift); + lower.X_add_number = ((ep->X_add_number & (sign + sign - 1)) ^ sign) - sign; upper.X_add_number -= lower.X_add_number; if (ep->X_op != O_constant) @@ -887,9 +1422,10 @@ load_const (int reg, expressionS *ep) upper.X_add_number = (int64_t) upper.X_add_number >> shift; load_const (reg, &upper); - macro_build (NULL, "slli", "d,s,>", reg, reg, shift); + md_assemblef ("slli x%d, x%d, 0x%x", reg, reg, shift); if (lower.X_add_number != 0) - macro_build (&lower, "addi", "d,s,j", reg, reg, BFD_RELOC_RISCV_LO12_I); + md_assemblef ("addi x%d, x%d, %" BFD_VMA_FMT "d", reg, reg, + lower.X_add_number); } else { @@ -898,17 +1434,38 @@ load_const (int reg, expressionS *ep) if (upper.X_add_number != 0) { - macro_build (ep, "lui", "d,u", reg, BFD_RELOC_RISCV_HI20); + /* Discard low part and zero-extend upper immediate. */ + upper_imm = ((uint32_t)upper.X_add_number >> shift); + + md_assemblef ("lui x%d, 0x%" BFD_VMA_FMT "x", reg, upper_imm); hi_reg = reg; } if (lower.X_add_number != 0 || hi_reg == 0) - macro_build (ep, ADD32_INSN, "d,s,j", reg, hi_reg, - BFD_RELOC_RISCV_LO12_I); + md_assemblef ("%s x%d, x%d, %" BFD_VMA_FMT "d", ADD32_INSN, reg, hi_reg, + lower.X_add_number); + } +} + +/* Zero extend and sign extend byte/half-word/word. */ + +static void +riscv_ext (int destreg, int srcreg, unsigned shift, bfd_boolean sign) +{ + if (sign) + { + md_assemblef ("slli x%d, x%d, 0x%x", destreg, srcreg, shift); + md_assemblef ("srai x%d, x%d, 0x%x", destreg, destreg, shift); + } + else + { + md_assemblef ("slli x%d, x%d, 0x%x", destreg, srcreg, shift); + md_assemblef ("srli x%d, x%d, 0x%x", destreg, destreg, shift); } } /* 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) @@ -1029,6 +1586,22 @@ macro (struct riscv_cl_insn *ip, expressionS *imm_expr, riscv_call (rd, rs1, imm_expr, *imm_reloc); break; + case M_ZEXTH: + riscv_ext (rd, rs1, xlen - 16, FALSE); + break; + + case M_ZEXTW: + 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; + default: as_bad (_("Macro %s not implemented"), ip->insn_mo->name); break; @@ -1039,6 +1612,7 @@ static const struct percent_op_match percent_op_utype[] = { {"%tprel_hi", BFD_RELOC_RISCV_TPREL_HI20}, {"%pcrel_hi", BFD_RELOC_RISCV_PCREL_HI20}, + {"%got_pcrel_hi", BFD_RELOC_RISCV_GOT_HI20}, {"%tls_ie_pcrel_hi", BFD_RELOC_RISCV_TLS_GOT_HI20}, {"%tls_gd_pcrel_hi", BFD_RELOC_RISCV_TLS_GD_HI20}, {"%hi", BFD_RELOC_RISCV_HI20}, @@ -1067,6 +1641,11 @@ static const struct percent_op_match percent_op_rtype[] = {0, 0} }; +static const struct percent_op_match percent_op_null[] = +{ + {0, 0} +}; + /* Return true if *STR points to a relocation operator. When returning true, move *STR over the operator and store its relocation code in *RELOC. Leave both *STR and *RELOC alone when returning false. */ @@ -1126,11 +1705,15 @@ my_getSmallExpression (expressionS *ep, bfd_reloc_code_real_type *reloc, unsigned crux_depth, str_depth, regno; char *crux; - /* First, check for integer registers. */ + /* First, check for integer registers. No callers can accept a reg, but + we need to avoid accidentally creating a useless undefined symbol below, + if this is an instruction pattern that can't match. A glibc build fails + if this is removed. */ if (reg_lookup (&str, RCLASS_GPR, ®no)) { ep->X_op = O_register; ep->X_add_number = regno; + expr_end = str; return 0; } @@ -1172,13 +1755,128 @@ my_getSmallExpression (expressionS *ep, bfd_reloc_code_real_type *reloc, return reloc_index; } +/* Parse opcode name, could be an mnemonics or number. */ +static size_t +my_getOpcodeExpression (expressionS *ep, bfd_reloc_code_real_type *reloc, + char *str, const struct percent_op_match *percent_op) +{ + const struct opcode_name_t *o = opcode_name_lookup (&str); + + if (o != NULL) + { + ep->X_op = O_constant; + ep->X_add_number = o->val; + return 0; + } + + return my_getSmallExpression (ep, reloc, str, percent_op); +} + +/* Detect and handle implicitly zero load-store offsets. For example, + "lw t0, (t1)" is shorthand for "lw t0, 0(t1)". Return TRUE iff such + an implicit offset was detected. */ + +static bfd_boolean +riscv_handle_implicit_zero_offset (expressionS *ep, const char *s) +{ + /* Check whether there is only a single bracketed expression left. + If so, it must be the base register and the constant must be zero. */ + if (*s == '(' && strchr (s + 1, '(') == 0) + { + ep->X_op = O_constant; + ep->X_add_number = 0; + return TRUE; + } + + return FALSE; +} + +/* All RISC-V CSR instructions belong to one of these classes. */ + +enum csr_insn_type +{ + INSN_NOT_CSR, + INSN_CSRRW, + INSN_CSRRS, + INSN_CSRRC +}; + +/* Return which CSR instruction is checking. */ + +static enum csr_insn_type +riscv_csr_insn_type (insn_t insn) +{ + if (((insn ^ MATCH_CSRRW) & MASK_CSRRW) == 0 + || ((insn ^ MATCH_CSRRWI) & MASK_CSRRWI) == 0) + return INSN_CSRRW; + else if (((insn ^ MATCH_CSRRS) & MASK_CSRRS) == 0 + || ((insn ^ MATCH_CSRRSI) & MASK_CSRRSI) == 0) + return INSN_CSRRS; + else if (((insn ^ MATCH_CSRRC) & MASK_CSRRC) == 0 + || ((insn ^ MATCH_CSRRCI) & MASK_CSRRCI) == 0) + return INSN_CSRRC; + else + return INSN_NOT_CSR; +} + +/* CSRRW and CSRRWI always write CSR. CSRRS, CSRRC, CSRRSI and CSRRCI write + CSR when RS1 isn't zero. The CSR is read only if the [11:10] bits of + CSR address is 0x3. */ + +static bfd_boolean +riscv_csr_read_only_check (insn_t insn) +{ + int csr = (insn & (OP_MASK_CSR << OP_SH_CSR)) >> OP_SH_CSR; + int rs1 = (insn & (OP_MASK_RS1 << OP_SH_RS1)) >> OP_SH_RS1; + int readonly = (((csr & (0x3 << 10)) >> 10) == 0x3); + enum csr_insn_type csr_insn = riscv_csr_insn_type (insn); + + if (readonly + && (((csr_insn == INSN_CSRRS + || csr_insn == INSN_CSRRC) + && rs1 != 0) + || csr_insn == INSN_CSRRW)) + return FALSE; + + return TRUE; +} + +/* 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. + + hret is used to return from traps in H-mode. H-mode is removed since + the v1.10 priv spec, but probably be added in the new hypervisor spec. + Therefore, hret should be controlled by the hypervisor spec rather than + priv spec in the future. + + dret is defined in the debug spec, so it should be checked in the future, + too. */ + +static bfd_boolean +riscv_is_priv_insn (insn_t insn) +{ + return (((insn ^ MATCH_SRET) & MASK_SRET) == 0 + || ((insn ^ MATCH_MRET) & MASK_MRET) == 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. */ + || ((insn ^ MATCH_SFENCE_VM) & MASK_SFENCE_VM) == 0); +} + /* This routine assembles an instruction into its binary format. As a side effect, it sets the global variable imm_reloc to the type of relocation to do if one of the operands is an address expression. */ static const char * riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr, - bfd_reloc_code_real_type *imm_reloc) + bfd_reloc_code_real_type *imm_reloc, htab_t hash) { char *s; const char *args; @@ -1190,9 +1888,11 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr, int argnum; const struct percent_op_match *p; const char *error = "unrecognized opcode"; + /* Indicate we are assembling instruction with CSR. */ + bfd_boolean insn_with_csr = FALSE; /* Parse the name of the instruction. Terminate the string if whitespace - is found so that hash_find only sees the name part of the string. */ + is found so that str_hash_find only sees the name part of the string. */ for (s = str; *s != '\0'; ++s) if (ISSPACE (*s)) { @@ -1201,12 +1901,15 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr, break; } - insn = (struct riscv_opcode *) hash_find (op_hash, str); + insn = (struct riscv_opcode *) str_hash_find (hash, str); argsStart = s; for ( ; insn && insn->name && strcmp (insn->name, str) == 0; insn++) { - if (!riscv_subset_supports (insn->subset)) + if ((insn->xlen_requirement != 0) && (xlen != insn->xlen_requirement)) + continue; + + if (!riscv_multi_subset_supports (insn->insn_class)) continue; create_insn (ip, insn); @@ -1226,13 +1929,36 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr, { if (!insn->match_func (insn, ip->insn_opcode)) break; - if (riscv_insn_length (insn->match) == 2 && !riscv_opts.rvc) + + /* For .insn, insn->match and insn->mask are 0. */ + if (riscv_insn_length ((insn->match == 0 && insn->mask == 0) + ? ip->insn_opcode + : insn->match) == 2 + && !riscv_opts.rvc) break; + + if (riscv_is_priv_insn (ip->insn_opcode)) + explicit_priv_attr = TRUE; + + /* Check if we write a read-only CSR by the CSR + instruction. */ + if (insn_with_csr + && riscv_opts.csr_check + && !riscv_csr_read_only_check (ip->insn_opcode)) + { + /* 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; + } } if (*s != '\0') break; /* Successful assembly. */ error = NULL; + insn_with_csr = FALSE; goto out; case 'C': /* RVC */ @@ -1275,6 +2001,11 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr, || regno != X_SP) break; continue; + case 'z': /* RS2, contrained to equal x0. */ + if (!reg_lookup (&s, RCLASS_GPR, ®no) + || regno != 0) + break; + continue; case '>': if (my_getSmallExpression (imm_expr, imm_reloc, s, p) || imm_expr->X_op != O_constant @@ -1282,24 +2013,33 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr, || imm_expr->X_add_number >= 64) break; ip->insn_opcode |= ENCODE_RVC_IMM (imm_expr->X_add_number); -rvc_imm_done: + rvc_imm_done: s = expr_end; imm_expr->X_op = O_absent; continue; case '<': if (my_getSmallExpression (imm_expr, imm_reloc, s, p) || imm_expr->X_op != O_constant - || !VALID_RVC_IMM (imm_expr->X_add_number) || imm_expr->X_add_number <= 0 - || imm_expr->X_add_number >= 32) + || imm_expr->X_add_number >= 32 + || !VALID_RVC_IMM ((valueT) imm_expr->X_add_number)) break; ip->insn_opcode |= ENCODE_RVC_IMM (imm_expr->X_add_number); goto rvc_imm_done; + case '8': + if (my_getSmallExpression (imm_expr, imm_reloc, s, p) + || imm_expr->X_op != O_constant + || imm_expr->X_add_number < 0 + || imm_expr->X_add_number >= 256 + || !VALID_RVC_UIMM8 ((valueT) imm_expr->X_add_number)) + break; + ip->insn_opcode |= ENCODE_RVC_UIMM8 (imm_expr->X_add_number); + goto rvc_imm_done; case 'i': if (my_getSmallExpression (imm_expr, imm_reloc, s, p) || imm_expr->X_op != O_constant || imm_expr->X_add_number == 0 - || !VALID_RVC_SIMM3 (imm_expr->X_add_number)) + || !VALID_RVC_SIMM3 ((valueT) imm_expr->X_add_number)) break; ip->insn_opcode |= ENCODE_RVC_SIMM3 (imm_expr->X_add_number); goto rvc_imm_done; @@ -1307,45 +2047,63 @@ rvc_imm_done: if (my_getSmallExpression (imm_expr, imm_reloc, s, p) || imm_expr->X_op != O_constant || imm_expr->X_add_number == 0 - || !VALID_RVC_IMM (imm_expr->X_add_number)) + || !VALID_RVC_IMM ((valueT) imm_expr->X_add_number)) break; ip->insn_opcode |= ENCODE_RVC_IMM (imm_expr->X_add_number); goto rvc_imm_done; case 'k': + if (riscv_handle_implicit_zero_offset (imm_expr, s)) + continue; if (my_getSmallExpression (imm_expr, imm_reloc, s, p) || imm_expr->X_op != O_constant - || !VALID_RVC_LW_IMM (imm_expr->X_add_number)) + || !VALID_RVC_LW_IMM ((valueT) imm_expr->X_add_number)) break; ip->insn_opcode |= ENCODE_RVC_LW_IMM (imm_expr->X_add_number); goto rvc_imm_done; case 'l': + if (riscv_handle_implicit_zero_offset (imm_expr, s)) + continue; if (my_getSmallExpression (imm_expr, imm_reloc, s, p) || imm_expr->X_op != O_constant - || !VALID_RVC_LD_IMM (imm_expr->X_add_number)) + || !VALID_RVC_LD_IMM ((valueT) imm_expr->X_add_number)) break; ip->insn_opcode |= ENCODE_RVC_LD_IMM (imm_expr->X_add_number); goto rvc_imm_done; case 'm': + if (riscv_handle_implicit_zero_offset (imm_expr, s)) + continue; if (my_getSmallExpression (imm_expr, imm_reloc, s, p) || imm_expr->X_op != O_constant - || !VALID_RVC_LWSP_IMM (imm_expr->X_add_number)) + || !VALID_RVC_LWSP_IMM ((valueT) imm_expr->X_add_number)) break; ip->insn_opcode |= ENCODE_RVC_LWSP_IMM (imm_expr->X_add_number); goto rvc_imm_done; case 'n': + if (riscv_handle_implicit_zero_offset (imm_expr, s)) + continue; if (my_getSmallExpression (imm_expr, imm_reloc, s, p) || imm_expr->X_op != O_constant - || !VALID_RVC_LDSP_IMM (imm_expr->X_add_number)) + || !VALID_RVC_LDSP_IMM ((valueT) imm_expr->X_add_number)) break; ip->insn_opcode |= ENCODE_RVC_LDSP_IMM (imm_expr->X_add_number); goto rvc_imm_done; + case 'o': + if (my_getSmallExpression (imm_expr, imm_reloc, s, p) + || imm_expr->X_op != O_constant + /* C.addiw, c.li, and c.andi allow zero immediate. + C.addi allows zero immediate as hint. Otherwise this + is same as 'j'. */ + || !VALID_RVC_IMM ((valueT) imm_expr->X_add_number)) + break; + ip->insn_opcode |= ENCODE_RVC_IMM (imm_expr->X_add_number); + goto rvc_imm_done; case 'K': if (my_getSmallExpression (imm_expr, imm_reloc, s, p) || imm_expr->X_op != O_constant - || !VALID_RVC_ADDI4SPN_IMM (imm_expr->X_add_number) - || imm_expr->X_add_number == 0) + || imm_expr->X_add_number == 0 + || !VALID_RVC_ADDI4SPN_IMM ((valueT) imm_expr->X_add_number)) break; ip->insn_opcode |= ENCODE_RVC_ADDI4SPN_IMM (imm_expr->X_add_number); @@ -1353,24 +2111,28 @@ rvc_imm_done: case 'L': if (my_getSmallExpression (imm_expr, imm_reloc, s, p) || imm_expr->X_op != O_constant - || !VALID_RVC_ADDI16SP_IMM (imm_expr->X_add_number) - || imm_expr->X_add_number == 0) + || imm_expr->X_add_number == 0 + || !VALID_RVC_ADDI16SP_IMM ((valueT) imm_expr->X_add_number)) break; ip->insn_opcode |= ENCODE_RVC_ADDI16SP_IMM (imm_expr->X_add_number); goto rvc_imm_done; case 'M': + if (riscv_handle_implicit_zero_offset (imm_expr, s)) + continue; if (my_getSmallExpression (imm_expr, imm_reloc, s, p) || imm_expr->X_op != O_constant - || !VALID_RVC_SWSP_IMM (imm_expr->X_add_number)) + || !VALID_RVC_SWSP_IMM ((valueT) imm_expr->X_add_number)) break; ip->insn_opcode |= ENCODE_RVC_SWSP_IMM (imm_expr->X_add_number); goto rvc_imm_done; case 'N': + if (riscv_handle_implicit_zero_offset (imm_expr, s)) + continue; if (my_getSmallExpression (imm_expr, imm_reloc, s, p) || imm_expr->X_op != O_constant - || !VALID_RVC_SDSP_IMM (imm_expr->X_add_number)) + || !VALID_RVC_SDSP_IMM ((valueT) imm_expr->X_add_number)) break; ip->insn_opcode |= ENCODE_RVC_SDSP_IMM (imm_expr->X_add_number); @@ -1379,7 +2141,7 @@ rvc_imm_done: p = percent_op_utype; if (my_getSmallExpression (imm_expr, imm_reloc, s, p)) break; -rvc_lui: + rvc_lui: if (imm_expr->X_op != O_constant || imm_expr->X_add_number <= 0 || imm_expr->X_add_number >= RISCV_BIGIMM_REACH @@ -1402,6 +2164,12 @@ rvc_lui: goto branch; case 'a': goto jump; + case 'S': /* Floating-point RS1 x8-x15. */ + if (!reg_lookup (&s, RCLASS_FPR, ®no) + || !(regno >= 8 && regno <= 15)) + break; + INSERT_OPERAND (CRS1S, *ip, regno % 8); + continue; case 'D': /* Floating-point RS2 x8-x15. */ if (!reg_lookup (&s, RCLASS_FPR, ®no) || !(regno >= 8 && regno <= 15)) @@ -1413,6 +2181,74 @@ rvc_lui: break; INSERT_OPERAND (CRS2, *ip, regno); continue; + case 'F': + switch (*++args) + { + case '6': + if (my_getSmallExpression (imm_expr, imm_reloc, s, p) + || imm_expr->X_op != O_constant + || imm_expr->X_add_number < 0 + || imm_expr->X_add_number >= 64) + { + as_bad (_("bad value for funct6 field, " + "value must be 0...64")); + break; + } + + INSERT_OPERAND (CFUNCT6, *ip, imm_expr->X_add_number); + imm_expr->X_op = O_absent; + s = expr_end; + continue; + case '4': + if (my_getSmallExpression (imm_expr, imm_reloc, s, p) + || imm_expr->X_op != O_constant + || imm_expr->X_add_number < 0 + || imm_expr->X_add_number >= 16) + { + as_bad (_("bad value for funct4 field, " + "value must be 0...15")); + break; + } + + INSERT_OPERAND (CFUNCT4, *ip, imm_expr->X_add_number); + imm_expr->X_op = O_absent; + s = expr_end; + continue; + case '3': + if (my_getSmallExpression (imm_expr, imm_reloc, s, p) + || imm_expr->X_op != O_constant + || imm_expr->X_add_number < 0 + || imm_expr->X_add_number >= 8) + { + as_bad (_("bad value for funct3 field, " + "value must be 0...7")); + break; + } + INSERT_OPERAND (CFUNCT3, *ip, imm_expr->X_add_number); + imm_expr->X_op = O_absent; + s = expr_end; + continue; + case '2': + if (my_getSmallExpression (imm_expr, imm_reloc, s, p) + || imm_expr->X_op != O_constant + || imm_expr->X_add_number < 0 + || imm_expr->X_add_number >= 4) + { + as_bad (_("bad value for funct2 field, " + "value must be 0...3")); + break; + } + INSERT_OPERAND (CFUNCT2, *ip, imm_expr->X_add_number); + imm_expr->X_op = O_absent; + s = expr_end; + continue; + default: + as_bad (_("bad compressed FUNCT field" + " specifier 'CF%c'\n"), + *args); + } + break; + default: as_bad (_("bad RVC field specifier 'C%c'\n"), *args); } @@ -1435,10 +2271,10 @@ rvc_lui: case '<': /* Shift amount, 0 - 31. */ my_getExpression (imm_expr, s); - check_absolute_expr (ip, imm_expr); + check_absolute_expr (ip, imm_expr, FALSE); if ((unsigned long) imm_expr->X_add_number > 31) - as_warn (_("Improper shift amount (%lu)"), - (unsigned long) imm_expr->X_add_number); + 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; @@ -1446,10 +2282,10 @@ rvc_lui: case '>': /* Shift amount, 0 - (XLEN-1). */ my_getExpression (imm_expr, s); - check_absolute_expr (ip, imm_expr); + check_absolute_expr (ip, imm_expr, FALSE); if ((unsigned long) imm_expr->X_add_number >= xlen) - as_warn (_("Improper shift amount (%lu)"), - (unsigned long) imm_expr->X_add_number); + 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; @@ -1457,25 +2293,27 @@ rvc_lui: case 'Z': /* CSRRxI immediate. */ my_getExpression (imm_expr, s); - check_absolute_expr (ip, imm_expr); + check_absolute_expr (ip, imm_expr, FALSE); if ((unsigned long) imm_expr->X_add_number > 31) - as_warn (_("Improper CSRxI immediate (%lu)"), - (unsigned long) imm_expr->X_add_number); + 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; continue; case 'E': /* Control register. */ + insn_with_csr = TRUE; + explicit_priv_attr = TRUE; if (reg_lookup (&s, RCLASS_CSR, ®no)) INSERT_OPERAND (CSR, *ip, regno); else { my_getExpression (imm_expr, s); - check_absolute_expr (ip, imm_expr); + check_absolute_expr (ip, imm_expr, TRUE); if ((unsigned long) imm_expr->X_add_number > 0xfff) - as_warn (_("Improper CSR address (%lu)"), - (unsigned long) imm_expr->X_add_number); + 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; @@ -1506,6 +2344,7 @@ rvc_lui: case 'd': /* Destination register. */ case 's': /* Source register. */ case 't': /* Target register. */ + case 'r': /* rs3. */ if (reg_lookup (&s, RCLASS_GPR, ®no)) { c = *args; @@ -1525,6 +2364,9 @@ rvc_lui: case 't': INSERT_OPERAND (RS2, *ip, regno); break; + case 'r': + INSERT_OPERAND (RS3, *ip, regno); + break; } continue; } @@ -1582,9 +2424,20 @@ rvc_lui: s = expr_end; continue; + case 'B': + my_getExpression (imm_expr, s); + normalize_constant_expr (imm_expr); + /* The 'B' format specifier must be a symbol or a constant. */ + if (imm_expr->X_op != O_symbol && imm_expr->X_op != O_constant) + break; + if (imm_expr->X_op == O_symbol) + *imm_reloc = BFD_RELOC_32; + s = expr_end; + continue; + case 'j': /* Sign-extended immediate. */ - *imm_reloc = BFD_RELOC_RISCV_LO12_I; p = percent_op_itype; + *imm_reloc = BFD_RELOC_RISCV_LO12_I; goto alu_op; case 'q': /* Store displacement. */ p = percent_op_stype; @@ -1594,18 +2447,15 @@ rvc_lui: p = percent_op_itype; *imm_reloc = BFD_RELOC_RISCV_LO12_I; goto load_store; - case '0': /* AMO "displacement," which must be zero. */ + case '1': /* 4-operand add, must be %tprel_add. */ p = percent_op_rtype; - *imm_reloc = BFD_RELOC_UNUSED; -load_store: - /* Check whether there is only a single bracketed expression - left. If so, it must be the base register and the - constant must be zero. */ - imm_expr->X_op = O_constant; - imm_expr->X_add_number = 0; - if (*s == '(' && strchr (s + 1, '(') == 0) + goto alu_op; + case '0': /* AMO "displacement," which must be zero. */ + p = percent_op_null; + load_store: + if (riscv_handle_implicit_zero_offset (imm_expr, s)) continue; -alu_op: + 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. */ @@ -1614,6 +2464,7 @@ alu_op: normalize_constant_expr (imm_expr); if (imm_expr->X_op != O_constant || (*args == '0' && imm_expr->X_add_number != 0) + || (*args == '1') || imm_expr->X_add_number >= (signed)RISCV_IMM_REACH/2 || imm_expr->X_add_number < -(signed)RISCV_IMM_REACH/2) break; @@ -1623,7 +2474,7 @@ alu_op: continue; case 'p': /* PC-relative offset. */ -branch: + branch: *imm_reloc = BFD_RELOC_12_PCREL; my_getExpression (imm_expr, s); s = expr_end; @@ -1631,9 +2482,11 @@ branch: case 'u': /* Upper 20 bits. */ p = percent_op_utype; - if (!my_getSmallExpression (imm_expr, imm_reloc, s, p) - && imm_expr->X_op == O_constant) + if (!my_getSmallExpression (imm_expr, imm_reloc, s, p)) { + if (imm_expr->X_op != O_constant) + break; + if (imm_expr->X_add_number < 0 || imm_expr->X_add_number >= (signed)RISCV_BIGIMM_REACH) as_bad (_("lui expression not in range 0..1048575")); @@ -1645,7 +2498,7 @@ branch: continue; case 'a': /* 20-bit PC-relative offset. */ -jump: + jump: my_getExpression (imm_expr, s); s = expr_end; *imm_reloc = BFD_RELOC_RISCV_JMP; @@ -1662,6 +2515,108 @@ jump: else *imm_reloc = BFD_RELOC_RISCV_CALL; continue; + case 'O': + switch (*++args) + { + case '4': + if (my_getOpcodeExpression (imm_expr, imm_reloc, s, p) + || imm_expr->X_op != O_constant + || imm_expr->X_add_number < 0 + || imm_expr->X_add_number >= 128 + || (imm_expr->X_add_number & 0x3) != 3) + { + as_bad (_("bad value for opcode field, " + "value must be 0...127 and " + "lower 2 bits must be 0x3")); + break; + } + + INSERT_OPERAND (OP, *ip, imm_expr->X_add_number); + imm_expr->X_op = O_absent; + s = expr_end; + continue; + case '2': + if (my_getOpcodeExpression (imm_expr, imm_reloc, s, p) + || imm_expr->X_op != O_constant + || imm_expr->X_add_number < 0 + || imm_expr->X_add_number >= 3) + { + as_bad (_("bad value for opcode field, " + "value must be 0...2")); + break; + } + + INSERT_OPERAND (OP2, *ip, imm_expr->X_add_number); + imm_expr->X_op = O_absent; + s = expr_end; + continue; + default: + as_bad (_("bad Opcode field specifier 'O%c'\n"), *args); + } + break; + + case 'F': + switch (*++args) + { + case '7': + if (my_getSmallExpression (imm_expr, imm_reloc, s, p) + || imm_expr->X_op != O_constant + || imm_expr->X_add_number < 0 + || imm_expr->X_add_number >= 128) + { + as_bad (_("bad value for funct7 field, " + "value must be 0...127")); + break; + } + + INSERT_OPERAND (FUNCT7, *ip, imm_expr->X_add_number); + imm_expr->X_op = O_absent; + s = expr_end; + continue; + case '3': + if (my_getSmallExpression (imm_expr, imm_reloc, s, p) + || imm_expr->X_op != O_constant + || imm_expr->X_add_number < 0 + || imm_expr->X_add_number >= 8) + { + as_bad (_("bad value for funct3 field, " + "value must be 0...7")); + break; + } + + INSERT_OPERAND (FUNCT3, *ip, imm_expr->X_add_number); + imm_expr->X_op = O_absent; + s = expr_end; + continue; + case '2': + if (my_getSmallExpression (imm_expr, imm_reloc, s, p) + || imm_expr->X_op != O_constant + || imm_expr->X_add_number < 0 + || imm_expr->X_add_number >= 4) + { + as_bad (_("bad value for funct2 field, " + "value must be 0...3")); + break; + } + + INSERT_OPERAND (FUNCT2, *ip, imm_expr->X_add_number); + imm_expr->X_op = O_absent; + s = expr_end; + continue; + + default: + as_bad (_("bad FUNCT field specifier 'F%c'\n"), *args); + } + break; + + case 'z': + if (my_getSmallExpression (imm_expr, imm_reloc, s, p) + || imm_expr->X_op != O_constant + || imm_expr->X_add_number != 0) + break; + s = expr_end; + imm_expr->X_op = O_absent; + continue; default: as_fatal (_("internal error: bad argument type %c"), *args); @@ -1670,9 +2625,10 @@ jump: } s = argsStart; error = _("illegal operands"); + insn_with_csr = FALSE; } -out: + out: /* Restore the character we might have clobbered above. */ if (save_c) *(argsStart - 1) = save_c; @@ -1687,7 +2643,18 @@ md_assemble (char *str) expressionS imm_expr; bfd_reloc_code_real_type imm_reloc = BFD_RELOC_UNUSED; - const char *error = riscv_ip (str, &insn, &imm_expr, &imm_reloc); + /* The arch and priv attributes should be set before assembling. */ + if (!start_assemble) + { + start_assemble = TRUE; + riscv_set_abi_by_arch (); + + /* Set the default_priv_spec according to the priv attributes. */ + if (!riscv_set_default_priv_spec (NULL)) + return; + } + + const char *error = riscv_ip (str, &insn, &imm_expr, &imm_reloc, op_hash); if (error) { @@ -1710,89 +2677,134 @@ md_atof (int type, char *litP, int *sizeP) 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:"; enum options { - OPTION_M32 = OPTION_MD_BASE, - OPTION_M64, - OPTION_MARCH, + OPTION_MARCH = OPTION_MD_BASE, OPTION_PIC, OPTION_NO_PIC, - OPTION_MSOFT_FLOAT, - OPTION_MHARD_FLOAT, - OPTION_MRVC, - OPTION_MNO_RVC, + OPTION_MABI, + OPTION_RELAX, + OPTION_NO_RELAX, + OPTION_ARCH_ATTR, + OPTION_NO_ARCH_ATTR, + OPTION_CSR_CHECK, + OPTION_NO_CSR_CHECK, + OPTION_MISA_SPEC, + OPTION_MPRIV_SPEC, + OPTION_BIG_ENDIAN, + OPTION_LITTLE_ENDIAN, OPTION_END_OF_ENUM }; struct option md_longopts[] = { - {"m32", no_argument, NULL, OPTION_M32}, - {"m64", no_argument, NULL, OPTION_M64}, {"march", required_argument, NULL, OPTION_MARCH}, {"fPIC", no_argument, NULL, OPTION_PIC}, {"fpic", no_argument, NULL, OPTION_PIC}, {"fno-pic", no_argument, NULL, OPTION_NO_PIC}, - {"mrvc", no_argument, NULL, OPTION_MRVC}, - {"mno-rvc", no_argument, NULL, OPTION_MNO_RVC}, - {"msoft-float", no_argument, NULL, OPTION_MSOFT_FLOAT}, - {"mhard-float", no_argument, NULL, OPTION_MHARD_FLOAT}, + {"mabi", required_argument, NULL, OPTION_MABI}, + {"mrelax", no_argument, NULL, OPTION_RELAX}, + {"mno-relax", no_argument, NULL, OPTION_NO_RELAX}, + {"march-attr", no_argument, NULL, OPTION_ARCH_ATTR}, + {"mno-arch-attr", no_argument, NULL, OPTION_NO_ARCH_ATTR}, + {"mcsr-check", no_argument, NULL, OPTION_CSR_CHECK}, + {"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} }; size_t md_longopts_size = sizeof (md_longopts); -enum float_mode -{ - FLOAT_MODE_DEFAULT, - FLOAT_MODE_SOFT, - FLOAT_MODE_HARD -}; -static enum float_mode float_mode = FLOAT_MODE_DEFAULT; - int md_parse_option (int c, const char *arg) { switch (c) { - case OPTION_MRVC: - riscv_set_rvc (TRUE); + case OPTION_MARCH: + /* riscv_after_parse_args will call riscv_set_arch to parse + the architecture. */ + default_arch_with_ext = arg; break; - case OPTION_MNO_RVC: - riscv_set_rvc (FALSE); + case OPTION_NO_PIC: + riscv_opts.pic = FALSE; break; - case OPTION_MSOFT_FLOAT: - float_mode = FLOAT_MODE_SOFT; + case OPTION_PIC: + riscv_opts.pic = TRUE; break; - case OPTION_MHARD_FLOAT: - float_mode = FLOAT_MODE_HARD; + case OPTION_MABI: + if (strcmp (arg, "ilp32") == 0) + riscv_set_abi (32, FLOAT_ABI_SOFT, FALSE); + else if (strcmp (arg, "ilp32e") == 0) + riscv_set_abi (32, FLOAT_ABI_SOFT, TRUE); + else if (strcmp (arg, "ilp32f") == 0) + riscv_set_abi (32, FLOAT_ABI_SINGLE, FALSE); + else if (strcmp (arg, "ilp32d") == 0) + riscv_set_abi (32, FLOAT_ABI_DOUBLE, FALSE); + else if (strcmp (arg, "ilp32q") == 0) + riscv_set_abi (32, FLOAT_ABI_QUAD, FALSE); + else if (strcmp (arg, "lp64") == 0) + riscv_set_abi (64, FLOAT_ABI_SOFT, FALSE); + else if (strcmp (arg, "lp64f") == 0) + riscv_set_abi (64, FLOAT_ABI_SINGLE, FALSE); + else if (strcmp (arg, "lp64d") == 0) + riscv_set_abi (64, FLOAT_ABI_DOUBLE, FALSE); + else if (strcmp (arg, "lp64q") == 0) + riscv_set_abi (64, FLOAT_ABI_QUAD, FALSE); + else + return 0; + explicit_mabi = TRUE; break; - case OPTION_M32: - xlen = 32; + case OPTION_RELAX: + riscv_opts.relax = TRUE; break; - case OPTION_M64: - xlen = 64; + case OPTION_NO_RELAX: + riscv_opts.relax = FALSE; break; - case OPTION_MARCH: - riscv_set_arch (arg); + case OPTION_ARCH_ATTR: + riscv_opts.arch_attr = TRUE; break; - case OPTION_NO_PIC: - riscv_opts.pic = FALSE; + case OPTION_NO_ARCH_ATTR: + riscv_opts.arch_attr = FALSE; break; - case OPTION_PIC: - riscv_opts.pic = TRUE; + case OPTION_CSR_CHECK: + riscv_opts.csr_check = TRUE; + break; + + case OPTION_NO_CSR_CHECK: + riscv_opts.csr_check = FALSE; + break; + + case OPTION_MISA_SPEC: + return riscv_set_default_isa_spec (arg); + + 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: @@ -1805,9 +2817,10 @@ md_parse_option (int c, const char *arg) void riscv_after_parse_args (void) { - if (riscv_subsets == NULL) - riscv_set_arch ("RVIMAFD"); - + /* 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. */ if (xlen == 0) { if (strcmp (default_arch, "riscv32") == 0) @@ -1817,6 +2830,40 @@ riscv_after_parse_args (void) else as_bad ("unknown default architecture `%s'", default_arch); } + if (default_arch_with_ext == NULL) + default_arch_with_ext = xlen == 64 ? "rv64g" : "rv32g"; + + /* Initialize the hash table for extensions with default version. */ + ext_version_hash = init_ext_version_hash (riscv_ext_version_table); + + /* If the -misa-spec isn't set, then we set the default ISA spec according + to DEFAULT_RISCV_ISA_SPEC. */ + 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); + + /* 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. */ + if (flag_dwarf_cie_version == -1) + flag_dwarf_cie_version = 3; } long @@ -1833,6 +2880,8 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED) unsigned int subtype; bfd_byte *buf = (bfd_byte *) (fixP->fx_frag->fr_literal + fixP->fx_where); bfd_boolean relaxable = FALSE; + offsetT loc; + segT sub_segment; /* Remember value for tc_gen_reloc. */ fixP->fx_addnumber = *valP; @@ -1844,11 +2893,12 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED) case BFD_RELOC_RISCV_LO12_S: 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; break; case BFD_RELOC_RISCV_GOT_HI20: - case BFD_RELOC_RISCV_PCREL_HI20: case BFD_RELOC_RISCV_ADD8: case BFD_RELOC_RISCV_ADD16: case BFD_RELOC_RISCV_ADD32: @@ -1872,11 +2922,32 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED) case BFD_RELOC_RISCV_TLS_GD_HI20: case BFD_RELOC_RISCV_TLS_DTPREL32: case BFD_RELOC_RISCV_TLS_DTPREL64: - S_SET_THREAD_LOCAL (fixP->fx_addsy); + if (fixP->fx_addsy != NULL) + S_SET_THREAD_LOCAL (fixP->fx_addsy); + else + as_bad_where (fixP->fx_file, fixP->fx_line, + _("TLS relocation against a constant")); break; - case BFD_RELOC_64: case BFD_RELOC_32: + /* Use pc-relative relocation for FDE initial location. + The symbol address in .eh_frame may be adjusted in + _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.*/ + if (fixP->fx_addsy && fixP->fx_subsy + && (sub_segment = S_GET_SEGMENT (fixP->fx_subsy)) + && strcmp (sub_segment->name, ".eh_frame") == 0 + && S_GET_VALUE (fixP->fx_subsy) + == fixP->fx_frag->fr_address + fixP->fx_where) + { + fixP->fx_r_type = BFD_RELOC_RISCV_32_PCREL; + fixP->fx_subsy = NULL; + break; + } + /* Fall through. */ + case BFD_RELOC_64: case BFD_RELOC_16: case BFD_RELOC_8: case BFD_RELOC_RISCV_CFA: @@ -1912,30 +2983,31 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED) case BFD_RELOC_RISCV_CFA: /* Load the byte to get the subtype. */ - subtype = bfd_get_8 (NULL, &fixP->fx_frag->fr_literal[fixP->fx_where]); + subtype = bfd_get_8 (NULL, &((fragS *) (fixP->fx_frag->fr_opcode))->fr_literal[fixP->fx_where]); + loc = fixP->fx_frag->fr_fix - (subtype & 7); switch (subtype) { case DW_CFA_advance_loc1: - fixP->fx_where++; - fixP->fx_next->fx_where++; + fixP->fx_where = loc + 1; + fixP->fx_next->fx_where = loc + 1; fixP->fx_r_type = BFD_RELOC_RISCV_SET8; fixP->fx_next->fx_r_type = BFD_RELOC_RISCV_SUB8; break; case DW_CFA_advance_loc2: fixP->fx_size = 2; - fixP->fx_where++; fixP->fx_next->fx_size = 2; - fixP->fx_next->fx_where++; + fixP->fx_where = loc + 1; + fixP->fx_next->fx_where = loc + 1; fixP->fx_r_type = BFD_RELOC_RISCV_SET16; fixP->fx_next->fx_r_type = BFD_RELOC_RISCV_SUB16; break; case DW_CFA_advance_loc4: fixP->fx_size = 4; - fixP->fx_where++; fixP->fx_next->fx_size = 4; - fixP->fx_next->fx_where++; + fixP->fx_where = loc; + fixP->fx_next->fx_where = loc; fixP->fx_r_type = BFD_RELOC_RISCV_SET32; fixP->fx_next->fx_r_type = BFD_RELOC_RISCV_SUB32; break; @@ -1944,6 +3016,8 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED) if (subtype < 0x80 && (subtype & 0x40)) { /* DW_CFA_advance_loc */ + fixP->fx_frag = (fragS *) fixP->fx_frag->fr_opcode; + fixP->fx_next->fx_frag = fixP->fx_frag; fixP->fx_r_type = BFD_RELOC_RISCV_SET6; fixP->fx_next->fx_r_type = BFD_RELOC_RISCV_SUB6; } @@ -2017,8 +3091,12 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED) relaxable = TRUE; break; + case BFD_RELOC_RISCV_PCREL_HI20: case BFD_RELOC_RISCV_PCREL_LO12_S: case BFD_RELOC_RISCV_PCREL_LO12_I: + relaxable = riscv_opts.relax; + break; + case BFD_RELOC_RISCV_ALIGN: break; @@ -2028,12 +3106,17 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED) as_fatal (_("internal error: bad relocation #%d"), fixP->fx_r_type); } + if (fixP->fx_subsy != NULL) + as_bad_where (fixP->fx_file, fixP->fx_line, + _("unsupported symbol subtraction")); + /* Add an R_RISCV_RELAX reloc if the reloc is relaxable. */ if (relaxable && fixP->fx_tcbit && fixP->fx_addsy != NULL) { fixP->fx_next = xmemdup (fixP, sizeof (*fixP), sizeof (*fixP)); fixP->fx_next->fx_addsy = fixP->fx_next->fx_subsy = NULL; fixP->fx_next->fx_r_type = BFD_RELOC_RISCV_RELAX; + fixP->fx_next->fx_size = 0; } } @@ -2044,34 +3127,42 @@ void riscv_pre_output_hook (void) { const frchainS *frch; - const asection *s; + segT s; + + /* Save the current segment info. */ + segT seg = now_seg; + subsegT subseg = now_subseg; for (s = stdoutput->sections; s; s = s->next) for (frch = seg_info (s)->frchainP; frch; frch = frch->frch_next) { - const fragS *frag; + fragS *frag; for (frag = frch->frch_root; frag; frag = frag->fr_next) { if (frag->fr_type == rs_cfa) { - const fragS *loc4_frag; expressionS exp; + expressionS *symval; - symbolS *add_symbol = frag->fr_symbol->sy_value.X_add_symbol; - symbolS *op_symbol = frag->fr_symbol->sy_value.X_op_symbol; - + symval = symbol_get_value_expression (frag->fr_symbol); exp.X_op = O_subtract; - exp.X_add_symbol = add_symbol; + exp.X_add_symbol = symval->X_add_symbol; exp.X_add_number = 0; - exp.X_op_symbol = op_symbol; + exp.X_op_symbol = symval->X_op_symbol; + + /* We must set the segment before creating a frag after all + frag chains have been chained together. */ + subseg_set (s, frch->frch_subseg); - loc4_frag = (fragS *) frag->fr_opcode; - fix_new_exp (loc4_frag, (int) frag->fr_offset, 1, &exp, 0, + fix_new_exp (frag, (int) frag->fr_offset, 1, &exp, 0, BFD_RELOC_RISCV_CFA); } } } + + /* Restore the original segment info. */ + subseg_set (seg, subseg); } @@ -2109,6 +3200,10 @@ s_riscv_option (int x ATTRIBUTE_UNUSED) riscv_opts.relax = TRUE; else if (strcmp (name, "norelax") == 0) riscv_opts.relax = FALSE; + else if (strcmp (name, "csr-check") == 0) + riscv_opts.csr_check = TRUE; + else if (strcmp (name, "no-csr-check") == 0) + riscv_opts.csr_check = FALSE; else if (strcmp (name, "push") == 0) { struct riscv_option_stack *s; @@ -2179,63 +3274,103 @@ s_bss (int ignore ATTRIBUTE_UNUSED) demand_empty_rest_of_line (); } -/* Align to a given power of two. */ - static void -s_align (int bytes_p) +riscv_make_nops (char *buf, bfd_vma bytes) { - int fill_value = 0, fill_value_specified = 0; - int min_text_alignment = riscv_opts.rvc ? 2 : 4; - int alignment = get_absolute_expression(), bytes; + bfd_vma i = 0; - if (bytes_p) + /* RISC-V instructions cannot begin or end on odd addresses, so this case + means we are not within a valid instruction sequence. It is thus safe + to use a zero byte, even though that is not a valid instruction. */ + if (bytes % 2 == 1) + buf[i++] = 0; + + /* Use at most one 2-byte NOP. */ + if ((bytes - i) % 4 == 2) { - bytes = alignment; - if (bytes < 1 || (bytes & (bytes-1)) != 0) - as_bad (_("alignment not a power of 2: %d"), bytes); - for (alignment = 0; bytes > 1; bytes >>= 1) - alignment++; + number_to_chars_littleendian (buf + i, RVC_NOP, 2); + i += 2; } - bytes = 1 << alignment; + /* Fill the remainder with 4-byte NOPs. */ + for ( ; i < bytes; i += 4) + number_to_chars_littleendian (buf + i, RISCV_NOP, 4); +} + +/* Called from md_do_align. Used to create an alignment frag in a + code section by emitting a worst-case NOP sequence that the linker + will later relax to the correct number of NOPs. We can't compute + the correct alignment now because of other linker relaxations. */ - if (alignment < 0 || alignment > 31) - as_bad (_("unsatisfiable alignment: %d"), alignment); +bfd_boolean +riscv_frag_align_code (int n) +{ + bfd_vma bytes = (bfd_vma) 1 << n; + bfd_vma insn_alignment = riscv_opts.rvc ? 2 : 4; + bfd_vma worst_case_bytes = bytes - insn_alignment; + char *nops; + expressionS ex; - if (*input_line_pointer == ',') - { - ++input_line_pointer; - fill_value = get_absolute_expression (); - fill_value_specified = 1; - } + /* If we are moving to a smaller alignment than the instruction size, then no + alignment is required. */ + if (bytes <= insn_alignment) + return TRUE; + + /* When not relaxing, riscv_handle_align handles code alignment. */ + if (!riscv_opts.relax) + return FALSE; + + nops = frag_more (worst_case_bytes); - if (!fill_value_specified - && subseg_text_p (now_seg) - && bytes > min_text_alignment) + ex.X_op = O_constant; + ex.X_add_number = 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); + + return TRUE; +} + +/* Implement HANDLE_ALIGN. */ + +void +riscv_handle_align (fragS *fragP) +{ + switch (fragP->fr_type) { - /* Emit the worst-case NOP string. The linker will delete any - unnecessary NOPs. This allows us to support code alignment - in spite of linker relaxations. */ - bfd_vma i, worst_case_bytes = bytes - min_text_alignment; - char *nops = frag_more (worst_case_bytes); - for (i = 0; i < worst_case_bytes - 2; i += 4) - md_number_to_chars (nops + i, RISCV_NOP, 4); - if (i < worst_case_bytes) - md_number_to_chars (nops + i, RVC_NOP, 2); - - expressionS ex; - ex.X_op = O_constant; - ex.X_add_number = worst_case_bytes; - - fix_new_exp (frag_now, nops - frag_now->fr_literal, 0, - &ex, FALSE, BFD_RELOC_RISCV_ALIGN); - } - else if (alignment) - frag_align (alignment, fill_value, 0); + case rs_align_code: + /* When relaxing, riscv_frag_align_code handles code alignment. */ + if (!riscv_opts.relax) + { + bfd_signed_vma bytes = (fragP->fr_next->fr_address + - fragP->fr_address - fragP->fr_fix); + /* We have 4 byte uncompressed nops. */ + bfd_signed_vma size = 4; + bfd_signed_vma excess = bytes % size; + char *p = fragP->fr_literal + fragP->fr_fix; + + if (bytes <= 0) + break; - record_alignment (now_seg, alignment); + /* Insert zeros or compressed nops to get 4 byte alignment. */ + if (excess) + { + riscv_make_nops (p, excess); + fragP->fr_fix += excess; + p += excess; + } - demand_empty_rest_of_line (); + /* Insert variable number of 4 byte uncompressed nops. */ + riscv_make_nops (p, size); + fragP->fr_var = size; + } + break; + + default: + break; + } } int @@ -2363,14 +3498,14 @@ md_convert_frag_branch (fragS *fragp) insn = bfd_getl32 (buf); insn ^= MATCH_BEQ ^ MATCH_BNE; insn |= ENCODE_SBTYPE_IMM (8); - md_number_to_chars ((char *) buf, insn, 4); + bfd_putl32 (insn, buf); buf += 4; -jump: + 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); + bfd_putl32 (MATCH_JAL, buf); buf += 4; break; @@ -2386,7 +3521,7 @@ jump: abort (); } -done: + done: fixp->fx_file = fragp->fr_file; fixp->fx_line = fragp->fr_line; @@ -2412,15 +3547,16 @@ md_show_usage (FILE *stream) { fprintf (stream, _("\ RISC-V options:\n\ - -m32 assemble RV32 code\n\ - -m64 assemble RV64 code (default)\n\ - -fpic generate position-independent code\n\ - -fno-pic don't generate position-independent code (default)\n\ - -msoft-float don't use F registers for floating-point values\n\ - -mhard-float use F registers for floating-point values (default)\n\ - -mno-rvc disable the C extension for compressed instructions (default)\n\ - -mrvc enable the C extension for compressed instructions\n\ - -march=ISA set the RISC-V architecture, RV64IMAFD by default\n\ + -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\ + -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\ ")); } @@ -2442,6 +3578,10 @@ tc_riscv_regname_to_dw2regnum (char *regname) if ((reg = reg_lookup_internal (regname, RCLASS_FPR)) >= 0) return reg + 32; + /* CSRs are numbered 4096 -> 8191. */ + if ((reg = reg_lookup_internal (regname, RCLASS_CSR)) >= 0) + return reg + 4096; + as_bad (_("unknown register `%s'"), regname); return -1; } @@ -2449,24 +3589,8 @@ tc_riscv_regname_to_dw2regnum (char *regname) void riscv_elf_final_processing (void) { - enum float_mode elf_float_mode = float_mode; - + riscv_set_abi_by_arch (); elf_elfheader (stdoutput)->e_flags |= elf_flags; - - if (elf_float_mode == FLOAT_MODE_DEFAULT) - { - struct riscv_subset *subset; - - /* Assume soft-float unless D extension is present. */ - elf_float_mode = FLOAT_MODE_SOFT; - - for (subset = riscv_subsets; subset != NULL; subset = subset->next) - if (strcasecmp (subset->name, "D") == 0) - elf_float_mode = FLOAT_MODE_HARD; - } - - if (elf_float_mode == FLOAT_MODE_SOFT) - elf_elfheader (stdoutput)->e_flags |= EF_RISCV_SOFT_FLOAT; } /* Parse the .sleb128 and .uleb128 pseudos. Only allow constant expressions, @@ -2487,6 +3611,199 @@ s_riscv_leb128 (int sign) return s_leb128 (sign); } +/* Parse the .insn directive. */ + +static void +s_riscv_insn (int x ATTRIBUTE_UNUSED) +{ + char *str = input_line_pointer; + struct riscv_cl_insn insn; + expressionS imm_expr; + bfd_reloc_code_real_type imm_reloc = BFD_RELOC_UNUSED; + char save_c; + + while (!is_end_of_line[(unsigned char) *input_line_pointer]) + ++input_line_pointer; + + save_c = *input_line_pointer; + *input_line_pointer = '\0'; + + const char *error = riscv_ip (str, &insn, &imm_expr, + &imm_reloc, insn_type_hash); + + if (error) + { + as_bad ("%s `%s'", error, str); + } + else + { + gas_assert (insn.insn_mo->pinfo != INSN_MACRO); + append_insn (&insn, &imm_expr, imm_reloc); + } + + *input_line_pointer = save_c; + demand_empty_rest_of_line (); +} + +/* Update arch and priv attributes. If we don't set the corresponding ELF + attributes, 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. */ + 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); + bfd_elf_add_proc_attr_string (stdoutput, Tag_RISCV_arch, arch_str); + xfree ((void *)arch_str); + + /* For the file without any instruction, we don't set the default_priv_spec + according to the priv attributes since the md_assemble isn't called. + Call riscv_set_default_priv_spec here for the above case, although + it seems strange. */ + 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 (!explicit_priv_attr) + return; + + /* Re-write priv attributes by default_priv_spec. */ + priv_str = riscv_get_priv_spec_name (default_priv_spec); + p = priv_str; + for (i = 0; *p; ++p) + { + if (*p == '.' && i < 3) + { + versions[i++] = number; + number = 0; + } + else if (ISDIGIT (*p)) + number = (number * 10) + (*p - '0'); + else + { + as_bad (_("internal: bad RISC-V priv spec string (%s)"), priv_str); + return; + } + } + versions[i] = number; + + /* Set the priv 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. */ + +static void +riscv_set_public_attributes (void) +{ + if (riscv_opts.arch_attr || explicit_attr) + riscv_write_out_attrs (); +} + +/* Called after all assembly has been done. */ + +void +riscv_md_end (void) +{ + riscv_set_public_attributes (); +} + +/* Given a symbolic attribute NAME, return the proper integer value. + Returns -1 if the attribute is not known. */ + +int +riscv_convert_symbolic_attribute (const char *name) +{ + static const struct + { + const char * name; + const int tag; + } + attribute_table[] = + { + /* When you modify this table you should + also modify the list in doc/c-riscv.texi. */ +#define T(tag) {#tag, Tag_RISCV_##tag}, {"Tag_RISCV_" #tag, Tag_RISCV_##tag} + T(arch), + T(priv_spec), + T(priv_spec_minor), + T(priv_spec_revision), + T(unaligned_access), + T(stack_align), +#undef T + }; + + unsigned int i; + + if (name == NULL) + return -1; + + for (i = 0; i < ARRAY_SIZE (attribute_table); i++) + if (strcmp (name, attribute_table[i].name) == 0) + return attribute_table[i].tag; + + return -1; +} + +/* Parse a .attribute directive. */ + +static void +s_riscv_attribute (int ignored ATTRIBUTE_UNUSED) +{ + int tag = obj_elf_vendor_attribute (OBJ_ATTR_PROC); + unsigned old_xlen; + obj_attribute *attr; + + explicit_attr = TRUE; + switch (tag) + { + case Tag_RISCV_arch: + old_xlen = xlen; + attr = elf_known_obj_attributes_proc (stdoutput); + if (!start_assemble) + riscv_set_arch (attr[Tag_RISCV_arch].s); + else + as_fatal (_(".attribute arch must set before any instructions")); + + if (old_xlen != xlen) + { + /* We must re-init bfd again if xlen is changed. */ + unsigned long mach = xlen == 64 ? bfd_mach_riscv64 : bfd_mach_riscv32; + bfd_find_target (riscv_target_format (), stdoutput); + + if (! bfd_set_arch_mach (stdoutput, bfd_arch_riscv, mach)) + as_warn (_("Could not set architecture and machine")); + } + break; + + case Tag_RISCV_priv_spec: + 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")); + break; + + default: + break; + } +} + /* Pseudo-op table. */ static const pseudo_typeS riscv_pseudo_table[] = @@ -2499,11 +3816,10 @@ static const pseudo_typeS riscv_pseudo_table[] = {"dtprelword", s_dtprel, 4}, {"dtpreldword", s_dtprel, 8}, {"bss", s_bss, 0}, - {"align", s_align, 0}, - {"p2align", s_align, 0}, - {"balign", s_align, 1}, {"uleb128", s_riscv_leb128, 0}, {"sleb128", s_riscv_leb128, 1}, + {"insn", s_riscv_insn, 0}, + {"attribute", s_riscv_attribute, 0}, { NULL, NULL, 0 }, };