Re: [PATCH v2 3/9] RISC-V: Support GAS option -misa-spec to set ISA versions.

Nelson Chu
2020-05-19T09:08:00Z

PING :)

On Wed, May 6, 2020 at 10:55 AM Nelson Chu <nelson.chu@sifive.com> wrote:
>
> For now, we can only use the GAS option -march and ELF arch attribute
> to set the versions for ISA extensions. It seems not so friendly for
> user. Therefore, we support new GAS option to make it easiler.
>
> * -misa-spec = [2.2|20190608|20191213]
> You can simply choose the ISA spec by this option, and then assembler
> will set the version for the standard extensions if you do not set in
> the ELF arch attributes or -march option.
>
> The default ISA spec is set to 2.2 rather than the lastest version, if
> the -misa-spec is not set. 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. We should resolve this in the future patches.
>
> gas/
> * config/tc-riscv.c (default_arch_with_ext, default_isa_spec):
> Static variables which are used to set the ISA extensions. You can
> use -march (or ELF build attributes) and -misa-spec to set them,
> respectively.
>
> (ext_version_hash): The hash table used to handle the extensions
> with versions.
> (init_ext_version_hash): Initialize the ext_version_hash according
> to riscv_ext_version_table.
>
> (riscv_get_default_ext_version): The callback function of
> riscv_parse_subset_t. According to the choosed ISA spec,
> get the default version for the specific extension.
> (riscv_set_arch): Set the callback function.
>
> (enum options, struct option md_longopts): Add new option -misa-spec.
> (md_parse_option): Do not call riscv_set_arch for -march. We will
> call it later in riscv_after_parse_args. Call riscv_get_isa_spec_class
> to set default_isa_spec class.
> (riscv_after_parse_args): Call init_ext_version_hash to initialize the
> ext_version_hash, and then call riscv_set_arch to set the architecture
> with versions according to default_arch_with_ext.
>
> * testsuite/gas/riscv/attribute-02.d: Set 0p0 as default version for
> x extensions.
> * testsuite/gas/riscv/attribute-03.d: Likewise.
> * testsuite/gas/riscv/attribute-09.d: New testcase. For i-ext, we
> already set it's version to 2p1 by march, so no need to use the default
> 2p2 version. For m-ext, we do not set the version by -march and ELF arch
> attribute, so set the default 2p0 to it. For zicsr, it is not defined in
> ISA spec 2p2, so set 0p0 to it.
> * testsuite/gas/riscv/attribute-10.d: New testcase. The version of
> zicsr is 2p0 according to ISA spec 20191213.
>
> bfd/
> * elfxx-riscv.h (riscv_parse_subset_t): Add new callback function
> get_default_version. It is used to find the default version for
> the specific extension.
>
> * elfxx-riscv.c (riscv_parsing_subset_version): Remove the parameters
> default_major_version and default_minor_version. Add new bfd_boolean
> parameter *use_default_version. Set it to TRUE if we need to call
> the callback rps->get_default_version to find the default version.
> (riscv_parse_std_ext): Call rps->get_default_version if we fail to find
> the default version in riscv_parsing_subset_version, and then call
> riscv_add_subset to add the subset into subset list.
> (riscv_parse_prefixed_ext): Likewise.
> (riscv_std_z_ext_strtab): Support Zicsr extensions.
>
> * elfnn-riscv.c (riscv_merge_std_ext): Use strcasecmp to compare the
> strings rather than characters.
> riscv_merge_arch_attr_info): The callback function get_default_version
> is only needed for assembler, so set it to NULL int the linker.
>
> include/
> * opcode/riscv.h: Include "bfd.h" to support bfd_boolean.
> (enum riscv_isa_spec_class): New enum class. All supported ISA spec
> belong to one of the class
> (struct riscv_ext_version): New structure holds version information
> for the specific ISA.
>
> opcodes/
> * riscv-opc.c (riscv_ext_version_table): The table used to store
> all information about the supported spec and the corresponding ISA
> versions. Currently, only Zicsr is supported to verify the
> correctness of Z sub extension settings. Others will be supported
> in the future patches.
> (struct isa_spec_t, isa_specs): List for all supported ISA spec
> classes and the corresponding strings.
> (riscv_get_isa_spec_class): New function. Get the corresponding ISA
> spec class by giving a ISA spec string.
> ---
> bfd/elfnn-riscv.c | 6 +-
> bfd/elfxx-riscv.c | 203 +++++++++++++++++++--------------
> bfd/elfxx-riscv.h | 3 +
> gas/config/tc-riscv.c | 100 +++++++++++++++-
> gas/testsuite/gas/riscv/attribute-02.d | 2 +-
> gas/testsuite/gas/riscv/attribute-03.d | 2 +-
> gas/testsuite/gas/riscv/attribute-09.d | 6 +
> gas/testsuite/gas/riscv/attribute-10.d | 6 +
> include/opcode/riscv.h | 26 +++++
> opcodes/riscv-opc.c | 93 +++++++++++++++
> 10 files changed, 355 insertions(+), 92 deletions(-)
> create mode 100644 gas/testsuite/gas/riscv/attribute-09.d
> create mode 100644 gas/testsuite/gas/riscv/attribute-10.d
>
> diff --git a/bfd/elfnn-riscv.c b/bfd/elfnn-riscv.c
> index 473bf50..2b5e713 100644
> --- a/bfd/elfnn-riscv.c
> +++ b/bfd/elfnn-riscv.c
> @@ -2802,7 +2802,7 @@ riscv_merge_std_ext (bfd *ibfd,
> if (!riscv_i_or_e_p (ibfd, out_arch, out))
> return FALSE;
>
> - if (in->name[0] != out->name[0])
> + if (strcasecmp (in->name, out->name) != 0)
> {
> /* TODO: We might allow merge 'i' with 'e'. */
> _bfd_error_handler
> @@ -2975,13 +2975,17 @@ riscv_merge_arch_attr_info (bfd *ibfd, char *in_arch, char *out_arch)
> riscv_parse_subset_t rpe_in;
> riscv_parse_subset_t rpe_out;
>
> + /* Only assembler needs to check the default version of ISA, so just set
> + the rpe_in.get_default_version and rpe_out.get_default_version to NULL. */
> rpe_in.subset_list = &in_subsets;
> rpe_in.error_handler = _bfd_error_handler;
> rpe_in.xlen = &xlen_in;
> + rpe_in.get_default_version = NULL;
>
> rpe_out.subset_list = &out_subsets;
> rpe_out.error_handler = _bfd_error_handler;
> rpe_out.xlen = &xlen_out;
> + rpe_out.get_default_version = NULL;
>
> if (in_arch == NULL && out_arch == NULL)
> return NULL;
> diff --git a/bfd/elfxx-riscv.c b/bfd/elfxx-riscv.c
> index b15fdee..e025689 100644
> --- a/bfd/elfxx-riscv.c
> +++ b/bfd/elfxx-riscv.c
> @@ -1025,9 +1025,8 @@ riscv_elf_add_sub_reloc (bfd *abfd,
> `minor_version`: Parsing result of minor version, set to 0 if version is
> not present in arch string, but set to `default_minor_version` if
> `major_version` using default_major_version.
> - `default_major_version`: Default major version.
> - `default_minor_version`: Default minor version.
> - `std_ext_p`: True if parsing std extension. */
> + `std_ext_p`: True if parsing std extension.
> + `use_default_version`: Set it to True if we need the default version. */
>
> static const char *
> riscv_parsing_subset_version (riscv_parse_subset_t *rps,
> @@ -1035,17 +1034,16 @@ riscv_parsing_subset_version (riscv_parse_subset_t *rps,
> const char *p,
> unsigned *major_version,
> unsigned *minor_version,
> - unsigned default_major_version,
> - unsigned default_minor_version,
> - bfd_boolean std_ext_p)
> + bfd_boolean std_ext_p,
> + bfd_boolean *use_default_version)
> {
> bfd_boolean major_p = TRUE;
> unsigned version = 0;
> - unsigned major = 0;
> - unsigned minor = 0;
> char np;
>
> - for (;*p; ++p)
> + *major_version = 0;
> + *minor_version = 0;
> + for (; *p; ++p)
> {
> if (*p == 'p')
> {
> @@ -1057,7 +1055,6 @@ riscv_parsing_subset_version (riscv_parse_subset_t *rps,
> if (std_ext_p)
> {
> *major_version = version;
> - *minor_version = 0;
> return p;
> }
> else
> @@ -1068,7 +1065,7 @@ riscv_parsing_subset_version (riscv_parse_subset_t *rps,
> }
> }
>
> - major = version;
> + *major_version = version;
> major_p = FALSE;
> version = 0;
> }
> @@ -1079,21 +1076,15 @@ riscv_parsing_subset_version (riscv_parse_subset_t *rps,
> }
>
> if (major_p)
> - major = version;
> + *major_version = version;
> else
> - minor = version;
> + *minor_version = version;
>
> - if (major == 0 && minor == 0)
> - {
> - /* We don't found any version string, use default version. */
> - *major_version = default_major_version;
> - *minor_version = default_minor_version;
> - }
> - else
> - {
> - *major_version = major;
> - *minor_version = minor;
> - }
> + /* We can not find any version in string, need to parse default version. */
> + if (use_default_version != NULL
> + && *major_version == 0
> + && *minor_version == 0)
> + *use_default_version = TRUE;
> return p;
> }
>
> @@ -1122,38 +1113,58 @@ riscv_parse_std_ext (riscv_parse_subset_t *rps,
> {
> const char *all_std_exts = riscv_supported_std_ext ();
> const char *std_exts = all_std_exts;
> -
> unsigned major_version = 0;
> unsigned minor_version = 0;
> char std_ext = '\0';
> + bfd_boolean use_default_version = FALSE;
>
> /* First letter must start with i, e or g. */
> switch (*p)
> {
> case 'i':
> - p++;
> - p = riscv_parsing_subset_version (
> - rps,
> - march,
> - p, &major_version, &minor_version,
> - /* default_major_version= */ 2,
> - /* default_minor_version= */ 0,
> - /* std_ext_p= */TRUE);
> - riscv_add_subset (rps->subset_list, "i", major_version, minor_version);
> + p = riscv_parsing_subset_version (rps,
> + march,
> + ++p,
> + &major_version,
> + &minor_version,
> + /* std_ext_p= */TRUE,
> + &use_default_version);
> +
> + /* Find the default version if needed. */
> + if (use_default_version
> + && rps->get_default_version != NULL)
> + rps->get_default_version ("i",
> + &major_version,
> + &minor_version);
> + riscv_add_subset (rps->subset_list, "i",
> + major_version, minor_version);
> break;
>
> case 'e':
> - p++;
> - p = riscv_parsing_subset_version (
> - rps,
> - march,
> - p, &major_version, &minor_version,
> - /* default_major_version= */ 1,
> - /* default_minor_version= */ 9,
> - /* std_ext_p= */TRUE);
> -
> - riscv_add_subset (rps->subset_list, "e", major_version, minor_version);
> - riscv_add_subset (rps->subset_list, "i", 2, 0);
> + p = riscv_parsing_subset_version (rps,
> + march,
> + ++p,
> + &major_version,
> + &minor_version,
> + /* std_ext_p= */TRUE,
> + &use_default_version);
> +
> + /* Find the default version if needed. */
> + if (use_default_version
> + && rps->get_default_version != NULL)
> + rps->get_default_version ("e",
> + &major_version,
> + &minor_version);
> + riscv_add_subset (rps->subset_list, "e",
> + major_version, minor_version);
> +
> + /* i-ext must be enabled. */
> + if (rps->get_default_version != NULL)
> + rps->get_default_version ("i",
> + &major_version,
> + &minor_version);
> + riscv_add_subset (rps->subset_list, "i",
> + major_version, minor_version);
>
> if (*rps->xlen > 32)
> {
> @@ -1161,25 +1172,36 @@ riscv_parse_std_ext (riscv_parse_subset_t *rps,
> march, *rps->xlen);
> return NULL;
> }
> -
> break;
>
> case 'g':
> - p++;
> - p = riscv_parsing_subset_version (
> - rps,
> - march,
> - p, &major_version, &minor_version,
> - /* default_major_version= */ 2,
> - /* default_minor_version= */ 0,
> - /* std_ext_p= */TRUE);
> - riscv_add_subset (rps->subset_list, "i", major_version, minor_version);
> + /* The g-ext shouldn't has the version, so we just skip the setting if
> + user set a version to it. */
> + p = riscv_parsing_subset_version (rps,
> + march,
> + ++p,
> + &major_version,
> + &minor_version,
> + TRUE,
> + &use_default_version);
> +
> + /* i-ext must be enabled. */
> + if (rps->get_default_version != NULL)
> + rps->get_default_version ("i",
> + &major_version,
> + &minor_version);
> + riscv_add_subset (rps->subset_list, "i",
> + major_version, minor_version);
>
> for ( ; *std_exts != 'q'; std_exts++)
> {
> const char subset[] = {*std_exts, '\0'};
> - riscv_add_subset (
> - rps->subset_list, subset, major_version, minor_version);
> + if (rps->get_default_version != NULL)
> + rps->get_default_version (subset,
> + &major_version,
> + &minor_version);
> + riscv_add_subset (rps->subset_list, subset,
> + major_version, minor_version);
> }
> break;
>
> @@ -1189,7 +1211,9 @@ riscv_parse_std_ext (riscv_parse_subset_t *rps,
> return NULL;
> }
>
> - while (*p)
> + /* The riscv_parsing_subset_version may set `p` to NULL, so I think we should
> + skip parsing the string if `p` is NULL or value of `p` is `\0`. */
> + while (p != NULL && *p != '\0')
> {
> char subset[2] = {0, 0};
>
> @@ -1218,21 +1242,26 @@ riscv_parse_std_ext (riscv_parse_subset_t *rps,
> march, *p);
> return NULL;
> }
> -
> std_exts++;
>
> - p++;
> - p = riscv_parsing_subset_version (
> - rps,
> - march,
> - p, &major_version, &minor_version,
> - /* default_major_version= */ 2,
> - /* default_minor_version= */ 0,
> - /* std_ext_p= */TRUE);
> -
> + use_default_version = FALSE;
> subset[0] = std_ext;
> -
> - riscv_add_subset (rps->subset_list, subset, major_version, minor_version);
> + p = riscv_parsing_subset_version (rps,
> + march,
> + ++p,
> + &major_version,
> + &minor_version,
> + TRUE,
> + &use_default_version);
> +
> + /* Find the default version if needed. */
> + if (use_default_version
> + && rps->get_default_version != NULL)
> + rps->get_default_version (subset,
> + &major_version,
> + &minor_version);
> + riscv_add_subset (rps->subset_list, subset,
> + major_version, minor_version);
> }
> return p;
> }
> @@ -1272,9 +1301,10 @@ typedef struct riscv_parse_config
> } riscv_parse_config_t;
>
> /* Parse a generic prefixed extension.
> - march: The full architecture string as passed in by "-march=...".
> - p: Point from which to start parsing the -march string.
> - config: What class of extensions to parse, predicate funcs,
> + `rps`: Hooks and status for parsing subset.
> + `march`: The full architecture string as passed in by "-march=...".
> + `p`: Point from which to start parsing the -march string.
> + `config`: What class of extensions to parse, predicate funcs,
> and strings to use in error reporting. */
>
> static const char *
> @@ -1287,6 +1317,7 @@ riscv_parse_prefixed_ext (riscv_parse_subset_t *rps,
> unsigned minor_version = 0;
> const char *last_name;
> riscv_isa_ext_class_t class;
> + bfd_boolean use_default_version;
>
> while (*p)
> {
> @@ -1309,15 +1340,11 @@ riscv_parse_prefixed_ext (riscv_parse_subset_t *rps,
> while (*++q != '\0' && *q != '_' && !ISDIGIT (*q))
> ;
>
> + use_default_version = FALSE;
> end_of_version =
> - riscv_parsing_subset_version (
> - rps,
> - march,
> - q, &major_version, &minor_version,
> - /* default_major_version= */ 2,
> - /* default_minor_version= */ 0,
> - /* std_ext_p= */FALSE);
> -
> + riscv_parsing_subset_version (rps, march, q, &major_version,
> + &minor_version, FALSE,
> + &use_default_version);
> *q = '\0';
>
> /* Check that the name is valid.
> @@ -1337,7 +1364,6 @@ riscv_parse_prefixed_ext (riscv_parse_subset_t *rps,
>
> /* Check that the last item is not the same as this. */
> last_name = rps->subset_list->tail->name;
> -
> if (!strcasecmp (last_name, subset))
> {
> rps->error_handler ("-march=%s: Duplicate %s ISA extension: \'%s\'",
> @@ -1357,7 +1383,15 @@ riscv_parse_prefixed_ext (riscv_parse_subset_t *rps,
> return NULL;
> }
>
> - riscv_add_subset (rps->subset_list, subset, major_version, minor_version);
> + /* Find the default version if needed. */
> + if (use_default_version
> + && rps->get_default_version != NULL)
> + rps->get_default_version (subset,
> + &major_version,
> + &minor_version);
> + riscv_add_subset (rps->subset_list, subset,
> + major_version, minor_version);
> +
> free (subset);
> p += end_of_version - subset;
>
> @@ -1384,7 +1418,7 @@ riscv_parse_prefixed_ext (riscv_parse_subset_t *rps,
>
> static const char * const riscv_std_z_ext_strtab[] =
> {
> - NULL
> + "zicsr", NULL
> };
>
> /* Same as `riscv_std_z_ext_strtab', but for S-class extensions. */
> @@ -1490,7 +1524,6 @@ riscv_parse_subset (riscv_parse_subset_t *rps,
> return FALSE;
>
> /* Parse the different classes of extensions in the specified order. */
> -
> for (i = 0; i < ARRAY_SIZE (parse_config); ++i) {
> p = riscv_parse_prefixed_ext (rps, arch, p, &parse_config[i]);
>
> diff --git a/bfd/elfxx-riscv.h b/bfd/elfxx-riscv.h
> index 76ee274..cbafd28 100644
> --- a/bfd/elfxx-riscv.h
> +++ b/bfd/elfxx-riscv.h
> @@ -72,6 +72,9 @@ typedef struct {
> void (*error_handler) (const char *,
> ...) ATTRIBUTE_PRINTF_1;
> unsigned *xlen;
> + void (*get_default_version) (const char *,
> + unsigned int *,
> + unsigned int *);
> } riscv_parse_subset_t;
>
> extern bfd_boolean
> diff --git a/gas/config/tc-riscv.c b/gas/config/tc-riscv.c
> index 168561e..5ef257e 100644
> --- a/gas/config/tc-riscv.c
> +++ b/gas/config/tc-riscv.c
> @@ -64,6 +64,8 @@ struct riscv_cl_insn
> #endif
>
> static const char default_arch[] = DEFAULT_ARCH;
> +static const char *default_arch_with_ext = NULL;
> +static enum riscv_isa_spec_class default_isa_spec = ISA_SPEC_CLASS_NONE;
>
> static unsigned xlen = 0; /* width of an x-register */
> static unsigned abi_xlen = 0; /* width of a pointer in the ABI */
> @@ -147,6 +149,67 @@ riscv_multi_subset_supports (enum riscv_insn_class insn_class)
> }
> }
>
> +/* Handle of the extension with version hash table. */
> +static struct hash_control *ext_version_hash = NULL;
> +
> +static struct hash_control *
> +init_ext_version_hash (const struct riscv_ext_version *table)
> +{
> + int i = 0;
> + struct hash_control *hash = hash_new ();
> +
> + while (table[i].name)
> + {
> + const char *name = table[i].name;
> + const char *hash_error =
> + hash_insert (hash, name, (void *) &table[i]);
> +
> + if (hash_error != NULL)
> + {
> + fprintf (stderr, _("internal error: can't hash `%s': %s\n"),
> + table[i].name, hash_error);
> + /* Probably a memory allocation problem? Give up now. */
> + as_fatal (_("Broken assembler. No assembly attempted."));
> + return NULL;
> + }
> +
> + i++;
> + while (table[i].name
> + && strcmp (table[i].name, name) == 0)
> + i++;
> + }
> +
> + return hash;
> +}
> +
> +static void
> +riscv_get_default_ext_version (const char *name,
> + unsigned int *major_version,
> + unsigned int *minor_version)
> +{
> + struct riscv_ext_version *ext;
> +
> + *major_version = 0;
> + *minor_version = 0;
> +
> + if (name == NULL || default_isa_spec == ISA_SPEC_CLASS_NONE)
> + return;
> +
> + ext = (struct riscv_ext_version *) 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++;
> + }
> +}
> +
> /* Set which ISA and extensions are available. */
>
> static void
> @@ -156,6 +219,10 @@ riscv_set_arch (const char *s)
> rps.subset_list = &riscv_subsets;
> rps.error_handler = as_fatal;
> 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);
> @@ -2348,6 +2415,7 @@ enum options
> OPTION_NO_ARCH_ATTR,
> OPTION_CSR_CHECK,
> OPTION_NO_CSR_CHECK,
> + OPTION_MISA_SPEC,
> OPTION_END_OF_ENUM
> };
>
> @@ -2364,6 +2432,7 @@ struct option md_longopts[] =
> {"mno-arch-attr", no_argument, NULL, OPTION_NO_ARCH_ATTR},
> {"mcsr-check", no_argument, NULL, OPTION_CSR_CHECK},
> {"mno-csr-check", no_argument, NULL, OPTION_NO_CSR_CHECK},
> + {"misa-spec", required_argument, NULL, OPTION_MISA_SPEC},
>
> {NULL, no_argument, NULL, 0}
> };
> @@ -2392,7 +2461,9 @@ md_parse_option (int c, const char *arg)
> switch (c)
> {
> case OPTION_MARCH:
> - riscv_set_arch (arg);
> + /* riscv_after_parse_args will call riscv_set_arch to parse
> + the architecture. */
> + default_arch_with_ext = arg;
> break;
>
> case OPTION_NO_PIC:
> @@ -2450,6 +2521,14 @@ md_parse_option (int c, const char *arg)
> riscv_opts.csr_check = FALSE;
> break;
>
> + case OPTION_MISA_SPEC:
> + if (!riscv_get_isa_spec_class (arg, &default_isa_spec))
> + {
> + as_bad ("Unknown default ISA spec `%s' set by -misa-spec", arg);
> + return 0;
> + }
> + break;
> +
> default:
> return 0;
> }
> @@ -2469,9 +2548,22 @@ riscv_after_parse_args (void)
> else
> as_bad ("unknown default architecture `%s'", default_arch);
> }
> -
> - if (riscv_subsets.head == NULL)
> - riscv_set_arch (xlen == 64 ? "rv64g" : "rv32g");
> + 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);
> +
> + /* 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. */
> + if (default_isa_spec == ISA_SPEC_CLASS_NONE)
> + default_isa_spec = ISA_SPEC_CLASS_2P2;
> +
> + /* Set the architecture according to -march. */
> + riscv_set_arch (default_arch_with_ext);
>
> /* Add the RVC extension, regardless of -march, to support .option rvc. */
> riscv_set_rvc (FALSE);
> diff --git a/gas/testsuite/gas/riscv/attribute-02.d b/gas/testsuite/gas/riscv/attribute-02.d
> index bc3295b..e1e8ce3 100644
> --- a/gas/testsuite/gas/riscv/attribute-02.d
> +++ b/gas/testsuite/gas/riscv/attribute-02.d
> @@ -3,4 +3,4 @@
> #source: empty.s
> Attribute Section: riscv
> File Attributes
> - Tag_RISCV_arch: "rv32i2p0_m2p0_a2p0_f2p0_d2p0_xargle2p0"
> + Tag_RISCV_arch: "rv32i2p0_m2p0_a2p0_f2p0_d2p0_xargle0p0"
> diff --git a/gas/testsuite/gas/riscv/attribute-03.d b/gas/testsuite/gas/riscv/attribute-03.d
> index 78b706a..fa38bf3 100644
> --- a/gas/testsuite/gas/riscv/attribute-03.d
> +++ b/gas/testsuite/gas/riscv/attribute-03.d
> @@ -3,4 +3,4 @@
> #source: empty.s
> Attribute Section: riscv
> File Attributes
> - Tag_RISCV_arch: "rv32i2p0_m2p0_a2p0_f2p0_d2p0_xargle2p0_xfoo2p0"
> + Tag_RISCV_arch: "rv32i2p0_m2p0_a2p0_f2p0_d2p0_xargle0p0_xfoo0p0"
> diff --git a/gas/testsuite/gas/riscv/attribute-09.d b/gas/testsuite/gas/riscv/attribute-09.d
> new file mode 100644
> index 0000000..cad1713
> --- /dev/null
> +++ b/gas/testsuite/gas/riscv/attribute-09.d
> @@ -0,0 +1,6 @@
> +#as: -march-attr -march=rv32i2p1m_zicsr -misa-spec=2.2
> +#readelf: -A
> +#source: empty.s
> +Attribute Section: riscv
> +File Attributes
> + Tag_RISCV_arch: "rv32i2p1_m2p0_zicsr0p0"
> diff --git a/gas/testsuite/gas/riscv/attribute-10.d b/gas/testsuite/gas/riscv/attribute-10.d
> new file mode 100644
> index 0000000..ba903d1
> --- /dev/null
> +++ b/gas/testsuite/gas/riscv/attribute-10.d
> @@ -0,0 +1,6 @@
> +#as: -march-attr -march=rv32gc_zicsr -misa-spec=20191213
> +#readelf: -A
> +#source: empty.s
> +Attribute Section: riscv
> +File Attributes
> + Tag_RISCV_arch: "rv32i2p1_m2p0_a2p1_f2p2_d2p2_c2p0_zicsr2p0"
> diff --git a/include/opcode/riscv.h b/include/opcode/riscv.h
> index ac6e861..d83e9ca 100644
> --- a/include/opcode/riscv.h
> +++ b/include/opcode/riscv.h
> @@ -24,6 +24,7 @@
> #include "riscv-opc.h"
> #include <stdlib.h>
> #include <stdint.h>
> +#include "bfd.h"
>
> typedef uint64_t insn_t;
>
> @@ -343,6 +344,27 @@ struct riscv_opcode
> unsigned long pinfo;
> };
>
> +/* The current supported ISA spec versions. */
> +
> +enum riscv_isa_spec_class
> +{
> + ISA_SPEC_CLASS_NONE,
> +
> + ISA_SPEC_CLASS_2P2,
> + ISA_SPEC_CLASS_20190608,
> + ISA_SPEC_CLASS_20191213
> +};
> +
> +/* This structure holds version information for specific ISA. */
> +
> +struct riscv_ext_version
> +{
> + const char *name;
> + enum riscv_isa_spec_class isa_spec_class;
> + unsigned int major_version;
> + unsigned int minor_version;
> +};
> +
> /* Instruction is a simple alias (e.g. "mv" for "addi"). */
> #define INSN_ALIAS 0x00000001
>
> @@ -420,5 +442,9 @@ extern const char * const riscv_fpr_names_abi[NFPR];
>
> extern const struct riscv_opcode riscv_opcodes[];
> extern const struct riscv_opcode riscv_insn_types[];
> +extern const struct riscv_ext_version riscv_ext_version_table[];
> +
> +extern bfd_boolean
> +riscv_get_isa_spec_class (const char *, enum riscv_isa_spec_class *);
>
> #endif /* _RISCV_H_ */
> diff --git a/opcodes/riscv-opc.c b/opcodes/riscv-opc.c
> index ceedcaf..f08b15e 100644
> --- a/opcodes/riscv-opc.c
> +++ b/opcodes/riscv-opc.c
> @@ -884,3 +884,96 @@ const struct riscv_opcode riscv_insn_types[] =
> /* Terminate the list. */
> {0, 0, INSN_CLASS_NONE, 0, 0, 0, 0, 0}
> };
> +
> +/* All standard extensions defined in all supported ISA spec. */
> +const struct riscv_ext_version riscv_ext_version_table[] =
> +{
> +/* name, ISA spec, major version, minor_version. */
> +{"e", ISA_SPEC_CLASS_20191213, 1, 9},
> +{"e", ISA_SPEC_CLASS_20190608, 1, 9},
> +{"e", ISA_SPEC_CLASS_2P2, 1, 9},
> +
> +{"i", ISA_SPEC_CLASS_20191213, 2, 1},
> +{"i", ISA_SPEC_CLASS_20190608, 2, 1},
> +{"i", ISA_SPEC_CLASS_2P2, 2, 0},
> +
> +{"m", ISA_SPEC_CLASS_20191213, 2, 0},
> +{"m", ISA_SPEC_CLASS_20190608, 2, 0},
> +{"m", ISA_SPEC_CLASS_2P2, 2, 0},
> +
> +{"a", ISA_SPEC_CLASS_20191213, 2, 1},
> +{"a", ISA_SPEC_CLASS_20190608, 2, 0},
> +{"a", ISA_SPEC_CLASS_2P2, 2, 0},
> +
> +{"f", ISA_SPEC_CLASS_20191213, 2, 2},
> +{"f", ISA_SPEC_CLASS_20190608, 2, 2},
> +{"f", ISA_SPEC_CLASS_2P2, 2, 0},
> +
> +{"d", ISA_SPEC_CLASS_20191213, 2, 2},
> +{"d", ISA_SPEC_CLASS_20190608, 2, 2},
> +{"d", ISA_SPEC_CLASS_2P2, 2, 0},
> +
> +{"q", ISA_SPEC_CLASS_20191213, 2, 2},
> +{"q", ISA_SPEC_CLASS_20190608, 2, 2},
> +{"q", ISA_SPEC_CLASS_2P2, 2, 0},
> +
> +{"c", ISA_SPEC_CLASS_20191213, 2, 0},
> +{"c", ISA_SPEC_CLASS_20190608, 2, 0},
> +{"c", ISA_SPEC_CLASS_2P2, 2, 0},
> +
> +{"p", ISA_SPEC_CLASS_20191213, 0, 2},
> +{"p", ISA_SPEC_CLASS_20190608, 0, 2},
> +{"p", ISA_SPEC_CLASS_2P2, 0, 1},
> +
> +{"v", ISA_SPEC_CLASS_20191213, 0, 7},
> +{"v", ISA_SPEC_CLASS_20190608, 0, 7},
> +{"v", ISA_SPEC_CLASS_2P2, 0, 7},
> +
> +{"n", ISA_SPEC_CLASS_20190608, 1, 1},
> +{"n", ISA_SPEC_CLASS_2P2, 1, 1},
> +
> +{"zicsr", ISA_SPEC_CLASS_20191213, 2, 0},
> +{"zicsr", ISA_SPEC_CLASS_20190608, 2, 0},
> +
> +/* Terminate the list. */
> +{NULL, 0, 0, 0}
> +};
> +
> +struct isa_spec_t
> +{
> + const char *name;
> + enum riscv_isa_spec_class class;
> +};
> +
> +/* List for all supported ISA spec versions. */
> +static const struct isa_spec_t isa_specs[] =
> +{
> + {"2.2", ISA_SPEC_CLASS_2P2},
> + {"20190608", ISA_SPEC_CLASS_20190608},
> + {"20191213", ISA_SPEC_CLASS_20191213},
> +
> +/* Terminate the list. */
> + {NULL, 0}
> +};
> +
> +/* Get the corresponding ISA spec class by giving a ISA spec string. */
> +
> +bfd_boolean
> +riscv_get_isa_spec_class (const char *s,
> + enum riscv_isa_spec_class *class)
> +{
> + const struct isa_spec_t *version;
> +
> + if (s == NULL)
> + return FALSE;
> +
> + for (version = &isa_specs[0]; version->name != NULL; ++version)
> + if (strcmp (version->name, s) == 0)
> + {
> + *class = version->class;
> + return TRUE;
> + }
> +
> + /* Can not find the supported ISA spec. */
> + return FALSE;
> +}
> --
> 2.7.4
>