X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=gas%2Fconfig%2Ftc-riscv.c;h=2f5ee18e451e895b990f8f4cb5dee07f54f6a451;hb=d0975d800285f61d60cd7c3f47b185304a09a052;hp=ca7e52a16915a405b9eb55fc887105fd4d976913;hpb=c2137f55ad04e451d834048d4bfec1de2daea20e;p=binutils-gdb.git diff --git a/gas/config/tc-riscv.c b/gas/config/tc-riscv.c index ca7e52a1691..2f5ee18e451 100644 --- a/gas/config/tc-riscv.c +++ b/gas/config/tc-riscv.c @@ -1,5 +1,5 @@ /* tc-riscv.c -- RISC-V assembler - Copyright (C) 2011-2020 Free Software Foundation, Inc. + Copyright (C) 2011-2022 Free Software Foundation, Inc. Contributed by Andrew Waterman (andrew@sifive.com). Based on MIPS target. @@ -55,6 +55,66 @@ struct riscv_cl_insn 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 @@ -64,19 +124,13 @@ struct riscv_cl_insn #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 @@ -85,13 +139,14 @@ struct riscv_cl_insn 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, @@ -105,17 +160,18 @@ static enum float_abi float_abi = FLOAT_ABI_DEFAULT; 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 @@ -123,20 +179,19 @@ riscv_set_default_isa_spec (const char *s) 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; @@ -144,62 +199,57 @@ riscv_set_default_priv_spec (const char *s) 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; @@ -207,149 +257,87 @@ riscv_set_rvc (bfd_boolean rvc_value) riscv_opts.rvc = rvc_value; } -static void -riscv_set_rve (bfd_boolean rve_value) +/* This linked list records all enabled extensions, which are parsed from + the architecture string. The architecture string can be set by the + -march option, the elf architecture attributes, and the --with-arch + configure option. */ +static riscv_subset_list_t *riscv_subsets = NULL; +static riscv_parse_subset_t riscv_rps_as = { - riscv_opts.rve = rve_value; -} - -static riscv_subset_list_t riscv_subsets; + NULL, /* subset_list, we will set it later once + riscv_opts_stack is created or updated. */ + as_bad, /* error_handler. */ + &xlen, /* xlen. */ + &default_isa_spec, /* isa_spec. */ + true, /* check_unknown_prefixed_ext. */ +}; -static bfd_boolean -riscv_subset_supports (const char *feature) +/* This structure is used to hold a stack of .option values. */ +struct riscv_option_stack { - struct riscv_subset_t *subset; + struct riscv_option_stack *next; + struct riscv_set_options options; + riscv_subset_list_t *subset_list; +}; - if (riscv_opts.rvc && (strcasecmp (feature, "c") == 0)) - return TRUE; +static struct riscv_option_stack *riscv_opts_stack = NULL; - return riscv_lookup_subset (&riscv_subsets, feature, &subset); -} +/* Set which ISA and extensions are available. */ -static bfd_boolean -riscv_multi_subset_supports (enum riscv_insn_class insn_class) +static void +riscv_set_arch (const char *s) { - switch (insn_class) + if (s != NULL && strcmp (s, "") == 0) { - case INSN_CLASS_I: return riscv_subset_supports ("i"); - case INSN_CLASS_C: return riscv_subset_supports ("c"); - case INSN_CLASS_A: return riscv_subset_supports ("a"); - case INSN_CLASS_M: return riscv_subset_supports ("m"); - case INSN_CLASS_F: return riscv_subset_supports ("f"); - case INSN_CLASS_D: return riscv_subset_supports ("d"); - case INSN_CLASS_Q: return riscv_subset_supports ("q"); - - case INSN_CLASS_F_AND_C: - return (riscv_subset_supports ("f") - && riscv_subset_supports ("c")); - case INSN_CLASS_D_AND_C: - return (riscv_subset_supports ("d") - && riscv_subset_supports ("c")); - - case INSN_CLASS_ZICSR: - return riscv_subset_supports ("zicsr"); - case INSN_CLASS_ZIFENCEI: - return riscv_subset_supports ("zifencei"); - - default: - as_fatal ("Unreachable"); - return FALSE; + as_bad (_("the architecture string of -march and elf architecture " + "attributes cannot be empty")); + return; } -} -/* Handle of the extension with version hash table. */ -static htab_t ext_version_hash = NULL; - -static htab_t -init_ext_version_hash (const struct riscv_ext_version *table) -{ - int i = 0; - htab_t hash = str_htab_create (); - - while (table[i].name) + if (riscv_subsets == NULL) { - const char *name = table[i].name; - if (str_hash_insert (hash, name, &table[i], 0) != NULL) - as_fatal (_("duplicate %s"), name); - - i++; - while (table[i].name - && strcmp (table[i].name, name) == 0) - i++; + riscv_subsets = XNEW (riscv_subset_list_t); + riscv_subsets->head = NULL; + riscv_subsets->tail = NULL; + riscv_rps_as.subset_list = riscv_subsets; } + riscv_release_subset_list (riscv_subsets); + riscv_parse_subset (&riscv_rps_as, s); - return hash; -} - -static void -riscv_get_default_ext_version (const char *name, - int *major_version, - int *minor_version) -{ - struct riscv_ext_version *ext; - - if (name == NULL || default_isa_spec == ISA_SPEC_CLASS_NONE) - return; - - ext = (struct riscv_ext_version *) str_hash_find (ext_version_hash, name); - while (ext - && ext->name - && strcmp (ext->name, name) == 0) - { - if (ext->isa_spec_class == default_isa_spec) - { - *major_version = ext->major_version; - *minor_version = ext->minor_version; - return; - } - ext++; - } + riscv_set_rvc (false); + if (riscv_subset_supports (&riscv_rps_as, "c")) + riscv_set_rvc (true); } -/* 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 bool explicit_mabi = false; -/* Indicate -mabi= option is explictly set. */ -static bfd_boolean 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 { @@ -358,6 +346,22 @@ riscv_set_abi_by_arch (void) 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. */ @@ -375,37 +379,40 @@ static htab_t op_hash = NULL; 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) \ @@ -438,14 +445,166 @@ static bfd_boolean explicit_priv_attr = FALSE; #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. */ @@ -474,7 +633,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 @@ -590,7 +749,7 @@ static const struct opcode_name_t opcode_name_list[] = {"NMADD", 0x4f}, {"NMSUB", 0x4b}, {"OP_FP", 0x53}, - /*reserved 0x57. */ + {"OP_V", 0x57}, {"CUSTOM_2", 0x5b}, /* 48b 0x5f. */ @@ -610,6 +769,7 @@ static const struct opcode_name_t opcode_name_list[] = static htab_t opcode_names_hash = NULL; /* Initialization for hash table of opcode name. */ + static void init_opcode_names_hash (void) { @@ -617,11 +777,12 @@ 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) { @@ -652,10 +813,13 @@ 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 @@ -674,7 +838,7 @@ hash_reg_name (enum reg_class class, const char *name, unsigned n) { 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 @@ -687,51 +851,56 @@ hash_reg_names (enum reg_class class, const char * const names[], unsigned n) } /* 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, @@ -739,31 +908,79 @@ 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) { @@ -771,32 +988,28 @@ riscv_csr_address (const char *csr_name, || (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) @@ -807,12 +1020,6 @@ 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); } @@ -828,13 +1035,15 @@ reg_lookup_internal (const char *s, enum reg_class class) 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; @@ -862,36 +1071,36 @@ reg_lookup (char **s, enum reg_class class, unsigned int *regnop) 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; @@ -907,150 +1116,177 @@ validate_riscv_insn (const struct riscv_opcode *opc, int length) { 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; @@ -1059,7 +1295,7 @@ init_opcode_hash (const struct riscv_opcode *opcodes, { 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 { @@ -1068,9 +1304,10 @@ init_opcode_hash (const struct riscv_opcode *opcodes, 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); @@ -1091,16 +1328,18 @@ 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")); + 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); @@ -1180,11 +1419,12 @@ append_insn (struct riscv_cl_insn *ip, expressionS *address_expr, { 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; } @@ -1208,9 +1448,9 @@ append_insn (struct riscv_cl_insn *ip, expressionS *address_expr, } /* 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, ...) @@ -1219,6 +1459,7 @@ 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); @@ -1233,26 +1474,54 @@ macro_build (expressionS *ep, const char *name, const char *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': @@ -1265,7 +1534,8 @@ macro_build (expressionS *ep, const char *name, const char *fmt, ...) case ',': continue; default: - as_fatal (_("internal error: invalid macro")); + unknown_macro_argument: + as_fatal (_("internal: invalid macro argument `%s'"), fmtStart); } break; } @@ -1290,7 +1560,7 @@ md_assemblef (const char *format, ...) r = vasprintf (&buf, format, ap); if (r < 0) - as_fatal (_("internal error: vasprintf failed")); + as_fatal (_("internal: vasprintf failed")); md_assemble (buf); free(buf); @@ -1300,6 +1570,7 @@ md_assemblef (const char *format, ...) /* Sign-extend 32-bit mode constants that have bit 31 set and all higher bits unset. */ + static void normalize_constant_expr (expressionS *ex) { @@ -1316,7 +1587,7 @@ 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")); @@ -1324,7 +1595,7 @@ check_absolute_expr (struct riscv_cl_insn *ip, expressionS *ex, 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); } @@ -1337,6 +1608,7 @@ make_internal_label (void) } /* Load an entry from the GOT. */ + static void pcrel_access (int destreg, int tempreg, expressionS *ep, const char *lo_insn, const char *lo_pattern, @@ -1369,6 +1641,7 @@ pcrel_store (int srcreg, int tempreg, expressionS *ep, const char *lo_insn, } /* 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) @@ -1410,8 +1683,8 @@ load_const (int reg, expressionS *ep) 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 { @@ -1423,20 +1696,20 @@ load_const (int reg, expressionS *ep) /* 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) { @@ -1450,7 +1723,97 @@ riscv_ext (int destreg, int srcreg, unsigned shift, bfd_boolean 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) @@ -1474,10 +1837,10 @@ macro (struct riscv_cl_insn *ip, expressionS *imm_expr, 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; @@ -1572,23 +1935,37 @@ macro (struct riscv_cl_insn *ip, expressionS *imm_expr, break; case M_ZEXTH: - riscv_ext (rd, rs1, xlen - 16, FALSE); + riscv_ext (rd, rs1, xlen - 16, false); break; case M_ZEXTW: - riscv_ext (rd, rs1, xlen - 32, FALSE); + riscv_ext (rd, rs1, xlen - 32, false); break; case M_SEXTB: - riscv_ext (rd, rs1, xlen - 8, TRUE); + riscv_ext (rd, rs1, xlen - 8, true); break; case M_SEXTH: - riscv_ext (rd, rs1, xlen - 16, TRUE); + riscv_ext (rd, rs1, xlen - 16, true); + break; + + case M_VMSGE: + case M_VMSGEU: + vector_macro (ip); + break; + + case M_FLH: + pcrel_load (rd, rs1, imm_expr, "flh", + BFD_RELOC_RISCV_PCREL_HI20, BFD_RELOC_RISCV_PCREL_LO12_I); + break; + case M_FSH: + pcrel_store (rs2, rs1, imm_expr, "fsh", + BFD_RELOC_RISCV_PCREL_HI20, BFD_RELOC_RISCV_PCREL_LO12_S); break; default: - as_bad (_("Macro %s not implemented"), ip->insn_mo->name); + as_bad (_("internal: macro %s not implemented"), ip->insn_mo->name); break; } } @@ -1635,7 +2012,7 @@ static const struct percent_op_match percent_op_null[] = 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) { @@ -1655,13 +2032,13 @@ parse_relocation (char **str, bfd_reloc_code_real_type *reloc, 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 @@ -1703,9 +2080,9 @@ my_getSmallExpression (expressionS *ep, bfd_reloc_code_real_type *reloc, } /* 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 @@ -1741,6 +2118,7 @@ my_getSmallExpression (expressionS *ep, bfd_reloc_code_real_type *reloc, } /* 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) @@ -1757,11 +2135,71 @@ my_getOpcodeExpression (expressionS *ep, bfd_reloc_code_real_type *reloc, 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. @@ -1770,14 +2208,13 @@ riscv_handle_implicit_zero_offset (expressionS *ep, const char *s) { 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, @@ -1808,7 +2245,7 @@ riscv_csr_insn_type (insn_t insn) 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; @@ -1821,12 +2258,12 @@ riscv_csr_read_only_check (insn_t insn) || 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. @@ -1839,7 +2276,7 @@ riscv_csr_read_only_check (insn_t insn) 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 @@ -1847,11 +2284,7 @@ riscv_is_priv_insn (insn_t insn) || ((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); } @@ -1859,57 +2292,66 @@ riscv_is_priv_insn (insn_t insn) 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)) @@ -1923,7 +2365,7 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr, 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. */ @@ -1934,197 +2376,206 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr, /* 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 @@ -2134,10 +2585,10 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr, && (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)) @@ -2150,163 +2601,329 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr, 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; @@ -2314,11 +2931,11 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr, 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); @@ -2326,15 +2943,15 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr, } 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. */ @@ -2357,16 +2974,18 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr, } 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': @@ -2377,7 +2996,7 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr, break; case 'U': INSERT_OPERAND (RS1, *ip, regno); - /* fallthru */ + /* Fall through. */ case 'T': INSERT_OPERAND (RS2, *ip, regno); break; @@ -2387,37 +3006,36 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr, } 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. */ @@ -2432,42 +3050,44 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr, 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; @@ -2479,32 +3099,33 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr, *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) + my_getExpression (imm_expr, asarg); + asarg = expr_end; + if (strcmp (asarg, "@plt") == 0) { *imm_reloc = BFD_RELOC_RISCV_CALL_PLT; - s += 4; + asarg += 4; } else *imm_reloc = BFD_RELOC_RISCV_CALL; continue; + case 'O': - switch (*++args) + switch (*++oparg) { case '4': - if (my_getOpcodeExpression (imm_expr, imm_reloc, s, p) + if (my_getOpcodeExpression (imm_expr, imm_reloc, asarg, p) || imm_expr->X_op != O_constant || imm_expr->X_add_number < 0 || imm_expr->X_add_number >= 128 @@ -2515,13 +3136,13 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr, "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) @@ -2530,21 +3151,21 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr, "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) @@ -2553,13 +3174,13 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr, "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) @@ -2568,13 +3189,13 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr, "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) @@ -2583,44 +3204,128 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr, "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 . 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) { @@ -2628,22 +3333,29 @@ 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; } @@ -2662,7 +3374,10 @@ 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:"; @@ -2681,6 +3396,8 @@ enum options OPTION_NO_CSR_CHECK, OPTION_MISA_SPEC, OPTION_MPRIV_SPEC, + OPTION_BIG_ENDIAN, + OPTION_LITTLE_ENDIAN, OPTION_END_OF_ENUM }; @@ -2699,6 +3416,8 @@ struct option md_longopts[] = {"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} }; @@ -2710,65 +3429,63 @@ md_parse_option (int c, const char *arg) 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: @@ -2777,6 +3494,14 @@ md_parse_option (int c, const char *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: return 0; } @@ -2787,10 +3512,8 @@ md_parse_option (int c, const char *arg) 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) @@ -2800,35 +3523,15 @@ 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. */ + /* 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. */ @@ -2849,7 +3552,7 @@ 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; + bool relaxable = false; offsetT loc; segT sub_segment; @@ -2864,8 +3567,8 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED) 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: @@ -2885,7 +3588,7 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED) 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: @@ -2905,7 +3608,7 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED) _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 @@ -2992,7 +3695,7 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED) 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; @@ -3022,7 +3725,7 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED) /* 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; @@ -3032,7 +3735,7 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED) /* 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; @@ -3042,7 +3745,7 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED) /* 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; @@ -3052,13 +3755,13 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED) /* 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: @@ -3073,12 +3776,11 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED) 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) @@ -3135,17 +3837,6 @@ riscv_pre_output_hook (void) 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 @@ -3159,29 +3850,49 @@ s_riscv_option (int x ATTRIBUTE_UNUSED) *input_line_pointer = '\0'; if (strcmp (name, "rvc") == 0) - riscv_set_rvc (TRUE); + { + riscv_update_subset (&riscv_rps_as, "+c"); + riscv_set_rvc (true); + } else if (strcmp (name, "norvc") == 0) - riscv_set_rvc (FALSE); + { + riscv_update_subset (&riscv_rps_as, "-c"); + riscv_set_rvc (false); + } else if (strcmp (name, "pic") == 0) - riscv_opts.pic = TRUE; + riscv_opts.pic = true; else if (strcmp (name, "nopic") == 0) - riscv_opts.pic = FALSE; + riscv_opts.pic = false; else if (strcmp (name, "relax") == 0) - riscv_opts.relax = TRUE; + riscv_opts.relax = true; else if (strcmp (name, "norelax") == 0) - riscv_opts.relax = FALSE; + riscv_opts.relax = false; else if (strcmp (name, "csr-check") == 0) - riscv_opts.csr_check = TRUE; + riscv_opts.csr_check = true; else if (strcmp (name, "no-csr-check") == 0) - riscv_opts.csr_check = FALSE; + riscv_opts.csr_check = false; + else if (strncmp (name, "arch,", 5) == 0) + { + name += 5; + if (ISSPACE (*name) && *name != '\0') + name++; + riscv_update_subset (&riscv_rps_as, name); + + riscv_set_rvc (false); + if (riscv_subset_supports (&riscv_rps_as, "c")) + riscv_set_rvc (true); + } else if (strcmp (name, "push") == 0) { struct riscv_option_stack *s; - s = (struct riscv_option_stack *) xmalloc (sizeof *s); + s = XNEW (struct riscv_option_stack); s->next = riscv_opts_stack; s->options = riscv_opts; + s->subset_list = riscv_subsets; riscv_opts_stack = s; + riscv_subsets = riscv_copy_subset_list (s->subset_list); + riscv_rps_as.subset_list = riscv_subsets; } else if (strcmp (name, "pop") == 0) { @@ -3192,14 +3903,18 @@ s_riscv_option (int x ATTRIBUTE_UNUSED) 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 (); @@ -3219,7 +3934,7 @@ s_dtprel (int bytes) 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 (); @@ -3227,7 +3942,7 @@ s_dtprel (int bytes) 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)); @@ -3258,13 +3973,13 @@ riscv_make_nops (char *buf, bfd_vma bytes) /* 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 @@ -3272,7 +3987,7 @@ riscv_make_nops (char *buf, bfd_vma bytes) 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; @@ -3284,11 +3999,11 @@ riscv_frag_align_code (int 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); @@ -3298,9 +4013,17 @@ riscv_frag_align_code (int n) 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. */ @@ -3319,6 +4042,7 @@ riscv_handle_align (fragS *fragP) /* 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) @@ -3327,12 +4051,20 @@ riscv_handle_align (fragS *fragP) /* 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; } @@ -3343,10 +4075,34 @@ riscv_handle_align (fragS *fragP) } } +/* 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 @@ -3388,7 +4144,7 @@ riscv_relax_frag (asection *sec, fragS *fragp, long stretch ATTRIBUTE_UNUSED) 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; } @@ -3440,7 +4196,7 @@ md_convert_frag_branch (fragS *fragp) /* 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; @@ -3450,7 +4206,7 @@ md_convert_frag_branch (fragS *fragp) 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; @@ -3467,15 +4223,15 @@ md_convert_frag_branch (fragS *fragp) /* 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; @@ -3483,7 +4239,7 @@ md_convert_frag_branch (fragS *fragp) 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; @@ -3517,20 +4273,25 @@ md_show_usage (FILE *stream) { 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) { @@ -3581,7 +4342,10 @@ s_riscv_leb128 (int sign) return s_leb128 (sign); } -/* Parse the .insn directive. */ +/* Parse the .insn directive. There are three formats, + Format 1: .insn , , ... + Format 2: .insn , + Format 3: .insn . */ static void s_riscv_insn (int x ATTRIBUTE_UNUSED) @@ -3598,12 +4362,24 @@ 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 { @@ -3615,42 +4391,38 @@ s_riscv_insn (int x ATTRIBUTE_UNUSED) 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) { @@ -3663,21 +4435,19 @@ riscv_write_out_attrs (void) 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) @@ -3689,11 +4459,20 @@ 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. */ @@ -3702,28 +4481,27 @@ riscv_convert_symbolic_attribute (const char *name) { 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; @@ -3740,7 +4518,7 @@ s_riscv_attribute (int ignored ATTRIBUTE_UNUSED) unsigned old_xlen; obj_attribute *attr; - explicit_attr = TRUE; + explicit_attr = true; switch (tag) { case Tag_RISCV_arch: @@ -3749,7 +4527,8 @@ s_riscv_attribute (int ignored ATTRIBUTE_UNUSED) 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) { @@ -3758,7 +4537,7 @@ s_riscv_attribute (int ignored ATTRIBUTE_UNUSED) 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; @@ -3766,7 +4545,8 @@ s_riscv_attribute (int ignored ATTRIBUTE_UNUSED) 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: @@ -3774,11 +4554,62 @@ s_riscv_attribute (int ignored ATTRIBUTE_UNUSED) } } -/* 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}, @@ -3790,6 +4621,8 @@ static const pseudo_typeS riscv_pseudo_table[] = {"sleb128", s_riscv_leb128, 1}, {"insn", s_riscv_insn, 0}, {"attribute", s_riscv_attribute, 0}, + {"variant_cc", s_variant_cc, 0}, + {"float16", float_cons, 'h'}, { NULL, NULL, 0 }, };