[PATCH 3/7] RISC-V: Support new GAS options and configure options to set ISA versions.

Nelson Chu
2020-04-18T11:08:19Z

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 options and configure options to make it easiler
for user.

New Assembler Options,

* -misa-spec = [2p0|2p1|2p2|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 don’t set in the ELF arch
attributes or -march option.

* -mriscv-isa-version = <ISA-string>
The syntax of <ISA-string> is same as -march option, but without rv32 or rv64
prefix. There are two reasons that you may need this option,

1. Some versions of extensions defined in the ISA spec are not matched to the
toolchain’s implementation. For example, V-ext is defined to version 0.7 in
ISA spec, but we only have implementations for 0.8 and 0.9. Therefore, you
have to use this option to choose the correct version for V-ext.

2. You want to update the version for the specific standard extensions if the
version are not set in the ELF arch attributes and -march option.

New Default Configure Options,

* --with-arch = <ISA-string>
The syntax of <ISA-string> is same as -march option. Assembler will check this
if -march option and ELF arch attributes aren’t set.

* --with-isa-spec = [2p0|2p1|2p2|20190608|20191213]
The syntax is same as -misa-spec option. Assembler will check this if
-misa-spec option isn’t set.

* --with-riscv-isa-version = <ISA-string>
The syntax of <ISA-string> is same as -mriscv-isa-version option. Assembler
will check this if -mriscv-isa-version option isn’t set.

The Priority of these options,

* ELF arch attributes > Assembler options > Default configure options
* For GAS options, -march > -mriscv-isa-version > -misa-spec
* For configure options, --with-arch > --with-riscv-isa-version > --with-isa-spec

gas/
* config/tc-riscv.c (DEFAULT_ARCH_WITH_EXT, DEFAULT_ISA_SPEC,
DEFAULT_RISCV_ISA_VERSION): Default configure option settings.
You can set them by new configure options --with-arch,
--with-isa-spec and --with-riscv-isa-version.

(default_arch_with_ext, default_isa_spec, default_riscv_isa_version):
const char strings which are used to set the ISA extensions. You can
use gas options -march (or ELF build attributes), and new gas options
-misa-spec and -mriscv-isa-version to set them, respectively. If the
gas options and attributes are not set, then assembler will check the
default configure option settings.

(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): Add new bfd_boolean parameter update_version.
The update_version is TRUE means we just want to update the version
according to default_riscv_isa_version, so don't call the
riscv_release_subset_list to reset the subset list. Pass two new
arguments to riscv_parse_subset, one is update_version mentioned above,
the other is the default_isa_spec. We use the default_isa_spec to
choose the default version for the extensions.

(enum options, struct option md_longopts): Add new gas options,
-misa-spec and -mriscv-isa-version.
(md_parse_option): Handle the above new gas options.
(riscv_after_parse_args): Call init_ext_version_hash to initialize the
ext_version_hash, and then always call riscv_set_arch twice to set and
update the architecture with versions according to default_arch_with_ext
and default_riscv_isa_version.
(s_riscv_attribute): Same as riscv_after_parse_args, we need to call
riscv_set_arch twice.

* testsuite/gas/riscv/attribute-01.d: Add -misa-spec=2p2. Set 0p0
as default version for x extensions.
* testsuite/gas/riscv/attribute-02.d: Likewise.
* testsuite/gas/riscv/attribute-03.d: Likewise.
* testsuite/gas/riscv/attribute-04.d: Likewise.
* testsuite/gas/riscv/attribute-05.d: Likewise.
* testsuite/gas/riscv/attribute-06.d: Likewise.
* testsuite/gas/riscv/attribute-07.d: Likewise.
* testsuite/gas/riscv/attribute-08.d: Likewise.
* testsuite/gas/riscv/attribute-09.d: New testcase. For i-ext, we
already set the version 1p9 in elf attribute, so -misa-spec and
-mriscv-isa-version don't update the version to 2p0 and 2p1.
For f-ext, we don't set the version to it, so use the default version
2p0 according to the ISA spec 2p2. For v-ext, the default version is
0p7 according to ISA spec 2p2, but we use -mriscv-isa-version to update
it to 0p9. For a-ext, it isn't enabled by -march or elf attribute, so
we don't update it's version even if -mriscv-isa-version is set.
As for the zicsr extension, it is defined in the lastest ISA spec
rather than 2p2, so set it's version to 0p0.
* testsuite/gas/riscv/attribute-09.s: Likewise.
* testsuite/gas/riscv/attribute-10.d: New testcase. The version of
zicsr is 2p0 according to ISA spec 20191213.
* testsuite/gas/riscv/attribute-11.d: New testcase. We can also update
the z* extension by -mriscv-isa-version.

* configure.ac: Add new configure options, --with-arch, --with-isa-spec
and --with-riscv-isa-version.
* configure: Regenerated.
* config.in: Regenerated.

bfd/
* elfxx-riscv.h (struct riscv_subset_t): Add new bfd_boolean field
use_default_version, which is used to record whether the default
version is used for the extension.
(struct riscv_parse_subset_t): Add new callback function
get_default_version. It is used to find the default version for
the specific extension.
(riscv_add_subset, riscv_parse_subset): Update function declaration.

* 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_update_subset_version): Only update the version of existin
standard extensions and their Z* sub extensions, if -mriscv-isa-version
or --with-riscv-isa-version are set, and the default version are used.

(riscv_add_subset): New parameter use_default_version. Record whether
the default version is used. riscv_update_subset_version only update
the version of extensions if the default versions are used.

(riscv_parse_subset): Add two parameters isa_spec_name and
update_version. Pass them to riscv_parse_std_ext and
riscv_parse_prefixed_ext. The string set by -mriscv-isa-version or
--with-riscv-isa-version doesn't have to start with rv32/rv64, so
skip the checking if update_version is TRUE.
(riscv_parse_std_ext): If update_version is TRUE, then we just call
riscv_update_subset_version to update the default version. Otherwise,
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): Same as the riscv_parse_std_ext.
(riscv_std_z_ext_strtab): Support Zicsr extensions.

* elfnn-riscv.c (riscv_merge_std_ext, riscv_merge_multi_letter_ext):
Updated. Pass update_version as FALSE to riscv_add_subset, the
update_version is only needed for assembler, not linker.
(riscv_merge_arch_attr_info): The callback function get_default_version
is only needed for assembler, so set it to NULL in linker. Also, Pass
isa_spec_name as NULL and update_version as FALSE to riscv_parse_subset.

include/
* opcode/riscv.h (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.
---
bfd/elfnn-riscv.c | 24 ++-
bfd/elfxx-riscv.c | 350 ++++++++++++++++++++++++---------
bfd/elfxx-riscv.h | 12 +-
gas/config.in | 9 +
gas/config/tc-riscv.c | 128 +++++++++++-
gas/configure | 53 ++++-
gas/configure.ac | 35 +++-
gas/testsuite/gas/riscv/attribute-01.d | 2 +-
gas/testsuite/gas/riscv/attribute-02.d | 4 +-
gas/testsuite/gas/riscv/attribute-03.d | 4 +-
gas/testsuite/gas/riscv/attribute-04.d | 2 +-
gas/testsuite/gas/riscv/attribute-05.d | 2 +-
gas/testsuite/gas/riscv/attribute-06.d | 2 +-
gas/testsuite/gas/riscv/attribute-07.d | 2 +-
gas/testsuite/gas/riscv/attribute-08.d | 2 +-
gas/testsuite/gas/riscv/attribute-09.d | 6 +
gas/testsuite/gas/riscv/attribute-09.s | 1 +
gas/testsuite/gas/riscv/attribute-10.d | 6 +
gas/testsuite/gas/riscv/attribute-11.d | 6 +
include/opcode/riscv.h | 11 ++
opcodes/riscv-opc.c | 53 +++++
21 files changed, 585 insertions(+), 129 deletions(-)
create mode 100644 gas/testsuite/gas/riscv/attribute-09.d
create mode 100644 gas/testsuite/gas/riscv/attribute-09.s
create mode 100644 gas/testsuite/gas/riscv/attribute-10.d
create mode 100644 gas/testsuite/gas/riscv/attribute-11.d

diff --git a/bfd/elfnn-riscv.c b/bfd/elfnn-riscv.c
index 8fcb106..4ca49e0 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
@@ -2818,8 +2818,8 @@ riscv_merge_std_ext (bfd *ibfd,
return FALSE;
}
else
- riscv_add_subset (&merged_subsets,
- in->name, in->major_version, in->minor_version);
+ riscv_add_subset (&merged_subsets, in->name, in->major_version,
+ in->minor_version, FALSE);

in = in->next;
out = out->next;
@@ -2848,7 +2848,7 @@ riscv_merge_std_ext (bfd *ibfd,

struct riscv_subset_t *merged = find_in ? find_in : find_out;
riscv_add_subset (&merged_subsets, merged->name,
- merged->major_version, merged->minor_version);
+ merged->major_version, merged->minor_version, FALSE);
}

/* Skip all standard extensions. */
@@ -2917,14 +2917,14 @@ riscv_merge_multi_letter_ext (bfd *ibfd,
{
/* `in' comes before `out', append `in' and increment. */
riscv_add_subset (&merged_subsets, in->name, in->major_version,
- in->minor_version);
+ in->minor_version, FALSE);
in = in->next;
}
else if (cmp > 0)
{
/* `out' comes before `in', append `out' and increment. */
riscv_add_subset (&merged_subsets, out->name, out->major_version,
- out->minor_version);
+ out->minor_version, FALSE);
out = out->next;
}
else
@@ -2938,7 +2938,7 @@ riscv_merge_multi_letter_ext (bfd *ibfd,
}

riscv_add_subset (&merged_subsets, out->name, out->major_version,
- out->minor_version);
+ out->minor_version, FALSE);
out = out->next;
in = in->next;
}
@@ -2952,7 +2952,7 @@ riscv_merge_multi_letter_ext (bfd *ibfd,
while (tail)
{
riscv_add_subset (&merged_subsets, tail->name, tail->major_version,
- tail->minor_version);
+ tail->minor_version, FALSE);
tail = tail->next;
}
}
@@ -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;
@@ -2993,10 +2997,10 @@ riscv_merge_arch_attr_info (bfd *ibfd, char *in_arch, char *out_arch)
return in_arch;

/* Parse subset from arch string. */
- if (!riscv_parse_subset (&rpe_in, in_arch))
+ if (!riscv_parse_subset (&rpe_in, in_arch, NULL, FALSE))
return NULL;

- if (!riscv_parse_subset (&rpe_out, out_arch))
+ if (!riscv_parse_subset (&rpe_out, out_arch, NULL, FALSE))
return NULL;

/* Checking XLEN. */
diff --git a/bfd/elfxx-riscv.c b/bfd/elfxx-riscv.c
index b15fdee..3a6aaf3 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;
}

@@ -1106,6 +1097,41 @@ riscv_supported_std_ext (void)
return "mafdqlcbjtpvn";
}

+/* Update the version of standard extensions and their Z* sub extensions
+ if -mriscv-isa-version is set. */
+
+static bfd_boolean
+riscv_update_subset_version (riscv_parse_subset_t *rps,
+ const char *ext_with_version,
+ char *ext,
+ unsigned major_version,
+ unsigned minor_version,
+ bfd_boolean use_default_version)
+{
+ riscv_subset_t *subset = riscv_lookup_subset (rps->subset_list, ext);;
+
+ /* The extension doesn't exist, so just return. */
+ if (subset == NULL)
+ return TRUE;
+
+ /* This means we can not find the version in the string. */
+ if (use_default_version)
+ {
+ rps->error_handler ("-mriscv-isa-version=%s: unexpected version "
+ "setting for subset `%s'", ext_with_version, ext);
+ return FALSE;
+ }
+
+ /* We only update the extension which use the default version. */
+ if (subset->use_default_version)
+ {
+ subset->major_version = major_version;
+ subset->minor_version = minor_version;
+ }
+
+ return TRUE;
+}
+
/* Parsing function for standard extensions.

Return Value:
@@ -1114,46 +1140,95 @@ riscv_supported_std_ext (void)
Arguments:
`rps`: Hooks and status for parsing subset.
`march`: Full arch string.
- `p`: Curent parsing position. */
+ `p`: Curent parsing position.
+ `isa_spec_name`: The ISA spec name. We set the default ISA versions
+ according to it.
+ `update_version`: True if the -mriscv-isa-version is set, and we need
+ to update the version for the existing extensions. */

static const char *
riscv_parse_std_ext (riscv_parse_subset_t *rps,
- const char *march, const char *p)
+ const char *march,
+ const char *p,
+ const char *isa_spec_name,
+ bfd_boolean update_version)
{
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);
+
+ /* Update the version for exsiting extension. */
+ if (update_version)
+ {
+ if (!riscv_update_subset_version (rps, march, "i",
+ major_version,
+ minor_version,
+ use_default_version))
+ return NULL;
+ break;
+ }
+
+ /* Find the default version if needed. */
+ if (use_default_version)
+ rps->get_default_version ("i",
+ isa_spec_name,
+ &major_version,
+ &minor_version);
+ riscv_add_subset (rps->subset_list, "i", major_version, minor_version,
+ use_default_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);
+
+ /* Update the version for exsiting extension. */
+ if (update_version)
+ {
+ if (!riscv_update_subset_version (rps, march, "e",
+ major_version,
+ minor_version,
+ use_default_version))
+ return NULL;
+ break;
+ }
+
+ /* Find the default version if needed. */
+ if (use_default_version)
+ rps->get_default_version ("e",
+ isa_spec_name,
+ &major_version,
+ &minor_version);
+ riscv_add_subset (rps->subset_list, "e", major_version, minor_version,
+ use_default_version);
+
+ /* i-ext must be enabled. */
+ rps->get_default_version ("i",
+ isa_spec_name,
+ &major_version,
+ &minor_version);
+ riscv_add_subset (rps->subset_list, "i", major_version, minor_version,
+ TRUE);

if (*rps->xlen > 32)
{
@@ -1161,35 +1236,58 @@ 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);
+
+ /* Set the version of g in -mriscv-isa-version is meaningless,
+ so just skip it. */
+ if (update_version)
+ break;
+
+ /* i-ext must be enabled. */
+ rps->get_default_version ("i",
+ isa_spec_name,
+ &major_version,
+ &minor_version);
+ riscv_add_subset (rps->subset_list, "i", major_version, minor_version,
+ TRUE);

for ( ; *std_exts != 'q'; std_exts++)
{
const char subset[] = {*std_exts, '\0'};
- riscv_add_subset (
- rps->subset_list, subset, major_version, minor_version);
+ rps->get_default_version (subset,
+ isa_spec_name,
+ &major_version,
+ &minor_version);
+ riscv_add_subset (rps->subset_list, subset, major_version,
+ minor_version, TRUE);
}
break;

default:
- rps->error_handler (
- "-march=%s: first ISA subset must be `e', `i' or `g'", march);
- return NULL;
+ /* The first ISA subset of -mriscv-isa-version will not necessarily
+ be e/i/g. */
+ if (!update_version)
+ {
+ rps->error_handler (
+ "-march=%s: first ISA subset must be `e', `i' or `g'", march);
+ 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 +1316,37 @@ 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;
+ p = riscv_parsing_subset_version (rps,
+ march,
+ ++p,
+ &major_version,
+ &minor_version,
+ TRUE,
+ &use_default_version);
+
+ /* Update the version for exsiting extension. */
+ if (update_version)
+ {
+ if (!riscv_update_subset_version (rps, march, subset,
+ major_version,
+ minor_version,
+ use_default_version))
+ return NULL;
+ continue;
+ }

- 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 (subset,
+ isa_spec_name,
+ &major_version,
+ &minor_version);
+ riscv_add_subset (rps->subset_list, subset, major_version, minor_version,
+ use_default_version);
}
return p;
}
@@ -1272,21 +1386,28 @@ 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,
- and strings to use in error reporting. */
+ `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.
+ `isa_spec_name`: ISA spec name. We set the default ISA versions
+ according to it.
+ `update_version`: True if the -mriscv-isa-version is set, and we need
+ update the version for the existing extensions. */

static const char *
riscv_parse_prefixed_ext (riscv_parse_subset_t *rps,
const char *march,
const char *p,
- const riscv_parse_config_t *config)
+ const riscv_parse_config_t *config,
+ const char *isa_spec_name,
+ bfd_boolean update_version)
{
unsigned major_version = 0;
unsigned minor_version = 0;
const char *last_name;
riscv_isa_ext_class_t class;
+ bfd_boolean use_default_version;

while (*p)
{
@@ -1309,15 +1430,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.
@@ -1335,10 +1452,11 @@ riscv_parse_prefixed_ext (riscv_parse_subset_t *rps,
return NULL;
}

- /* Check that the last item is not the same as this. */
+ /* Check that the last item is not the same as this. Just skip this
+ check when updating the version. */
last_name = rps->subset_list->tail->name;
-
- if (!strcasecmp (last_name, subset))
+ if (!update_version
+ && !strcasecmp (last_name, subset))
{
rps->error_handler ("-march=%s: Duplicate %s ISA extension: \'%s\'",
march, config->prefix, subset);
@@ -1357,7 +1475,30 @@ riscv_parse_prefixed_ext (riscv_parse_subset_t *rps,
return NULL;
}

- riscv_add_subset (rps->subset_list, subset, major_version, minor_version);
+ if (update_version)
+ {
+ /* Update the version for exsiting extension. */
+ if (!riscv_update_subset_version (rps, march, subset,
+ major_version,
+ minor_version,
+ use_default_version))
+ {
+ free (subset);
+ return NULL;
+ }
+ }
+ else
+ {
+ /* Find the default version if needed. */
+ if (use_default_version)
+ rps->get_default_version (subset,
+ isa_spec_name,
+ &major_version,
+ &minor_version);
+ riscv_add_subset (rps->subset_list, subset, major_version,
+ minor_version, use_default_version);
+ }
+
free (subset);
p += end_of_version - subset;

@@ -1384,7 +1525,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. */
@@ -1457,11 +1598,17 @@ static const riscv_parse_config_t parse_config[] =

Arguments:
`rps`: Hooks and status for parsing subset.
- `arch`: Arch string. */
+ `arch`: Arch string.
+ `isa_spec_name`: The ISA spec name. We set the default ISA versions
+ according to it.
+ `update_version`: True if the -mriscv-isa-version is set, and we need
+ update the version for the existing extensions. */

bfd_boolean
riscv_parse_subset (riscv_parse_subset_t *rps,
- const char *arch)
+ const char *arch,
+ const char *isa_spec_name,
+ bfd_boolean update_version)
{
const char *p = arch;
size_t i;
@@ -1476,15 +1623,17 @@ riscv_parse_subset (riscv_parse_subset_t *rps,
*rps->xlen = 64;
p += 4;
}
- else
+ else if (!update_version)
{
+ /* The string set by -mriscv-isa-version doesn't have to start
+ with rv32/rv64. */
rps->error_handler ("-march=%s: ISA string must begin with rv32 or rv64",
arch);
return FALSE;
}

/* Parsing standard extension. */
- p = riscv_parse_std_ext (rps, arch, p);
+ p = riscv_parse_std_ext (rps, arch, p, isa_spec_name, update_version);

if (p == NULL)
return FALSE;
@@ -1492,7 +1641,8 @@ riscv_parse_subset (riscv_parse_subset_t *rps,
/* 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]);
+ p = riscv_parse_prefixed_ext (rps, arch, p, &parse_config[i],
+ isa_spec_name, update_version);

if (p == NULL)
return FALSE;
@@ -1543,7 +1693,8 @@ riscv_parse_subset (riscv_parse_subset_t *rps,
void
riscv_add_subset (riscv_subset_list_t *subset_list,
const char *subset,
- int major, int minor)
+ int major, int minor,
+ bfd_boolean use_default_version)
{
riscv_subset_t *s = xmalloc (sizeof *s);

@@ -1553,6 +1704,9 @@ riscv_add_subset (riscv_subset_list_t *subset_list,
s->name = xstrdup (subset);
s->major_version = major;
s->minor_version = minor;
+ /* Record whether the default version is used. The -mriscv-isa-version
+ only update the ISA which use the default version. */
+ s->use_default_version = use_default_version;
s->next = NULL;

if (subset_list->tail != NULL)
diff --git a/bfd/elfxx-riscv.h b/bfd/elfxx-riscv.h
index 76ee274..e3d5177 100644
--- a/bfd/elfxx-riscv.h
+++ b/bfd/elfxx-riscv.h
@@ -40,6 +40,7 @@ struct riscv_subset_t
const char *name;
int major_version;
int minor_version;
+ bfd_boolean use_default_version;
struct riscv_subset_t *next;
};

@@ -56,7 +57,8 @@ riscv_release_subset_list (riscv_subset_list_t *);
extern void
riscv_add_subset (riscv_subset_list_t *,
const char *,
- int, int);
+ int, int,
+ bfd_boolean);

extern riscv_subset_t *
riscv_lookup_subset (const riscv_subset_list_t *,
@@ -72,11 +74,17 @@ typedef struct {
void (*error_handler) (const char *,
...) ATTRIBUTE_PRINTF_1;
unsigned *xlen;
+ void (*get_default_version) (const char *,
+ const char *,
+ unsigned int *,
+ unsigned int *);
} riscv_parse_subset_t;

extern bfd_boolean
riscv_parse_subset (riscv_parse_subset_t *,
- const char *);
+ const char *,
+ const char *,
+ bfd_boolean);

extern const char *
riscv_supported_std_ext (void);
diff --git a/gas/config.in b/gas/config.in
index 8724eb1..935bd9b 100644
--- a/gas/config.in
+++ b/gas/config.in
@@ -30,6 +30,9 @@
/* Default architecture. */
#undef DEFAULT_ARCH

+/* Define default value for -march */
+#undef DEFAULT_ARCH_WITH_EXT
+
/* Default CRIS architecture. */
#undef DEFAULT_CRIS_ARCH

@@ -50,12 +53,18 @@
/* Define to 1 if you want to generate x86 relax relocations by default. */
#undef DEFAULT_GENERATE_X86_RELAX_RELOCATIONS

+/* Define default value for -misa-spec */
+#undef DEFAULT_ISA_SPEC
+
/* Define to 1 if you want to fix Loongson3 LLSC Errata by default. */
#undef DEFAULT_MIPS_FIX_LOONGSON3_LLSC

/* Define to 1 if you want to generate RISC-V arch attribute by default. */
#undef DEFAULT_RISCV_ATTR

+/* Define default isa version which are not covered by --with-isa-spec */
+#undef DEFAULT_RISCV_ISA_VERSION
+
/* Define to 1 if you want to generate GNU x86 used ISA and feature properties
by default. */
#undef DEFAULT_X86_USED_NOTE
diff --git a/gas/config/tc-riscv.c b/gas/config/tc-riscv.c
index 168561e..bfcf2f0 100644
--- a/gas/config/tc-riscv.c
+++ b/gas/config/tc-riscv.c
@@ -63,7 +63,22 @@ struct riscv_cl_insn
#define DEFAULT_RISCV_ATTR 0
#endif

+#ifndef DEFAULT_ARCH_WITH_EXT
+#define DEFAULT_ARCH_WITH_EXT NULL
+#endif
+
+#ifndef DEFAULT_ISA_SPEC
+#define DEFAULT_ISA_SPEC "2p2"
+#endif
+
+#ifndef DEFAULT_RISCV_ISA_VERSION
+#define DEFAULT_RISCV_ISA_VERSION NULL
+#endif
+
static const char default_arch[] = DEFAULT_ARCH;
+static const char *default_arch_with_ext = DEFAULT_ARCH_WITH_EXT;
+static const char *default_isa_spec = DEFAULT_ISA_SPEC;
+static const char *default_riscv_isa_version = DEFAULT_RISCV_ISA_VERSION;

static unsigned xlen = 0; /* width of an x-register */
static unsigned abi_xlen = 0; /* width of a pointer in the ABI */
@@ -147,18 +162,87 @@ 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,
+ const char *spec_name,
+ unsigned int *major_version,
+ unsigned int *minor_version)
+{
+ struct riscv_ext_version *ext;
+
+ *major_version = 0;
+ *minor_version = 0;
+
+ if (name == NULL
+ || spec_name == NULL)
+ return;
+
+ ext = (struct riscv_ext_version *) hash_find (ext_version_hash, name);
+ while (ext
+ && ext->name
+ && strcmp (ext->name, name) == 0)
+ {
+ if (ext->spec_name
+ && strcmp (ext->spec_name, spec_name) == 0)
+ {
+ *major_version = ext->major_version;
+ *minor_version = ext->minor_version;
+ return;
+ }
+ ext++;
+ }
+}
+
/* Set which ISA and extensions are available. */

static void
-riscv_set_arch (const char *s)
+riscv_set_arch (const char *s, bfd_boolean update_version)
{
riscv_parse_subset_t rps;
rps.subset_list = &riscv_subsets;
rps.error_handler = as_fatal;
rps.xlen = &xlen;
+ rps.get_default_version = riscv_get_default_ext_version;

- riscv_release_subset_list (&riscv_subsets);
- riscv_parse_subset (&rps, s);
+ if (s == NULL)
+ return;
+
+ if (!update_version)
+ riscv_release_subset_list (&riscv_subsets);
+ riscv_parse_subset (&rps, s, default_isa_spec, update_version);
}

/* Handle of the OPCODE hash table. */
@@ -2348,6 +2432,8 @@ enum options
OPTION_NO_ARCH_ATTR,
OPTION_CSR_CHECK,
OPTION_NO_CSR_CHECK,
+ OPTION_MISA_SPEC,
+ OPTION_MRISCV_ISA_VERSION,
OPTION_END_OF_ENUM
};

@@ -2364,6 +2450,8 @@ 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},
+ {"mriscv-isa-version", required_argument, NULL, OPTION_MRISCV_ISA_VERSION},

{NULL, no_argument, NULL, 0}
};
@@ -2392,7 +2480,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 +2540,14 @@ md_parse_option (int c, const char *arg)
riscv_opts.csr_check = FALSE;
break;

+ case OPTION_MISA_SPEC:
+ default_isa_spec = arg;
+ break;
+
+ case OPTION_MRISCV_ISA_VERSION:
+ default_riscv_isa_version = arg;
+ break;
+
default:
return 0;
}
@@ -2460,6 +2558,13 @@ md_parse_option (int c, const char *arg)
void
riscv_after_parse_args (void)
{
+ /* Initialize the hash table for extensions with default version. */
+ ext_version_hash = init_ext_version_hash (riscv_ext_version_table);
+
+ /* The --with-arch is optional for now, so we have to set the xlen
+ according to the default_arch, which is set by the --targte, first.
+ Then, we use the xlen to set the default_arch_with_ext if the
+ -march and --with-arch are not set. */
if (xlen == 0)
{
if (strcmp (default_arch, "riscv32") == 0)
@@ -2469,9 +2574,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";
+
+ /* Set the architecture according to -march or --with-arch. */
+ riscv_set_arch (default_arch_with_ext, FALSE);

- if (riscv_subsets.head == NULL)
- riscv_set_arch (xlen == 64 ? "rv64g" : "rv32g");
+ /* Update the version info according to -mriscv-isa-spec or
+ --with-riscv-isa-spec. */
+ riscv_set_arch (default_riscv_isa_version, TRUE);

/* Add the RVC extension, regardless of -march, to support .option rvc. */
riscv_set_rvc (FALSE);
@@ -3366,7 +3477,7 @@ s_riscv_attribute (int ignored ATTRIBUTE_UNUSED)
obj_attribute *attr;
attr = elf_known_obj_attributes_proc (stdoutput);
if (!start_assemble)
- riscv_set_arch (attr[Tag_RISCV_arch].s);
+ riscv_set_arch (attr[Tag_RISCV_arch].s, FALSE);
else
as_fatal (_(".attribute arch must set before any instructions"));

@@ -3379,6 +3490,9 @@ s_riscv_attribute (int ignored ATTRIBUTE_UNUSED)
if (! bfd_set_arch_mach (stdoutput, bfd_arch_riscv, mach))
as_warn (_("Could not set architecture and machine"));
}
+
+ /* We also need to update the version of ISA here. */
+ riscv_set_arch (default_riscv_isa_version, TRUE);
}
}

diff --git a/gas/configure b/gas/configure
index 1515787..bce847a 100755
--- a/gas/configure
+++ b/gas/configure
@@ -13009,7 +13009,7 @@ $as_echo "#define NDS32_DEFAULT_ZOL_EXT 1" >>confdefs.h
$as_echo "$enable_zol_ext" >&6; }
;;

- aarch64 | i386 | riscv | s390 | sparc)
+ aarch64 | i386 | s390 | sparc)
if test $this_target = $target ; then

cat >>confdefs.h <<_ACEOF
@@ -13019,6 +13019,57 @@ _ACEOF
fi
;;

+ riscv)
+ # --target=riscv[32|64]-*-*. */
+ if test $this_target = $target ; then
+
+cat >>confdefs.h <<_ACEOF
+#define DEFAULT_ARCH "${arch}"
+_ACEOF
+
+ fi
+
+ # --with-arch=<value>. The syntax of <value> is same as Gas option -march.
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for default configuration of --with-arch" >&5
+$as_echo_n "checking for default configuration of --with-arch... " >&6; }
+ if test "x${with_arch}" != x; then
+
+cat >>confdefs.h <<_ACEOF
+#define DEFAULT_ARCH_WITH_EXT "$with_arch"
+_ACEOF
+
+ fi
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_arch" >&5
+$as_echo "$with_arch" >&6; }
+
+ # --with-isa-spec=[2p0|2p1|2p2|20190608|20191213].
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for default configuration of --with-isa-spec" >&5
+$as_echo_n "checking for default configuration of --with-isa-spec... " >&6; }
+ if test "x${with_isa_spec}" != x; then
+
+cat >>confdefs.h <<_ACEOF
+#define DEFAULT_ISA_SPEC "$with_isa_spec"
+_ACEOF
+
+ fi
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_isa_spec" >&5
+$as_echo "$with_isa_spec" >&6; }
+
+ # --with-riscv-isa-version=<value>. The syntax of <value> is same as Gas
+ # -march, but without the rv[32|64] prefix.
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for default configuration of --with-riscv-isa-version" >&5
+$as_echo_n "checking for default configuration of --with-riscv-isa-version... " >&6; }
+ if test "x${with_riscv_isa_version}" != x; then
+
+cat >>confdefs.h <<_ACEOF
+#define DEFAULT_RISCV_ISA_VERSION "$with_riscv_isa_version"
+_ACEOF
+
+ fi
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_riscv_isa_version" >&5
+$as_echo "$with_riscv_isa_version" >&6; }
+ ;;
+
rl78)
f=rl78-parse.o
case " $extra_objects " in
diff --git a/gas/configure.ac b/gas/configure.ac
index 6f32e55..be4ba20 100644
--- a/gas/configure.ac
+++ b/gas/configure.ac
@@ -569,12 +569,45 @@ changequote([,])dnl
AC_MSG_RESULT($enable_zol_ext)
;;

- aarch64 | i386 | riscv | s390 | sparc)
+ aarch64 | i386 | s390 | sparc)
if test $this_target = $target ; then
AC_DEFINE_UNQUOTED(DEFAULT_ARCH, "${arch}", [Default architecture.])
fi
;;

+ riscv)
+ # --target=riscv[32|64]-*-*. */
+ if test $this_target = $target ; then
+ AC_DEFINE_UNQUOTED(DEFAULT_ARCH, "${arch}", [Default architecture.])
+ fi
+
+ # --with-arch=<value>. The syntax of <value> is same as Gas option -march.
+ AC_MSG_CHECKING(for default configuration of --with-arch)
+ if test "x${with_arch}" != x; then
+ AC_DEFINE_UNQUOTED(DEFAULT_ARCH_WITH_EXT, "$with_arch",
+ [Define default value for -march])
+ fi
+ AC_MSG_RESULT($with_arch)
+
+ # --with-isa-spec=[2p0|2p1|2p2|20190608|20191213].
+ AC_MSG_CHECKING(for default configuration of --with-isa-spec)
+ if test "x${with_isa_spec}" != x; then
+ AC_DEFINE_UNQUOTED(DEFAULT_ISA_SPEC, "$with_isa_spec",
+ [Define default value for -misa-spec])
+ fi
+ AC_MSG_RESULT($with_isa_spec)
+
+ # --with-riscv-isa-version=<value>. The syntax of <value> is same as Gas
+ # -march, but without the rv[32|64] prefix.
+ AC_MSG_CHECKING(for default configuration of --with-riscv-isa-version)
+ if test "x${with_riscv_isa_version}" != x; then
+ AC_DEFINE_UNQUOTED(DEFAULT_RISCV_ISA_VERSION, "$with_riscv_isa_version",
+ [Define default isa version which are not
+ covered by --with-isa-spec])
+ fi
+ AC_MSG_RESULT($with_riscv_isa_version)
+ ;;
+
rl78)
f=rl78-parse.o
case " $extra_objects " in
diff --git a/gas/testsuite/gas/riscv/attribute-01.d b/gas/testsuite/gas/riscv/attribute-01.d
index e22773e..3f9e841 100644
--- a/gas/testsuite/gas/riscv/attribute-01.d
+++ b/gas/testsuite/gas/riscv/attribute-01.d
@@ -1,4 +1,4 @@
-#as: -march=rv32g -march-attr
+#as: -march=rv32g -march-attr -misa-spec=2p2
#readelf: -A
#source: empty.s
Attribute Section: riscv
diff --git a/gas/testsuite/gas/riscv/attribute-02.d b/gas/testsuite/gas/riscv/attribute-02.d
index bc3295b..a58248e 100644
--- a/gas/testsuite/gas/riscv/attribute-02.d
+++ b/gas/testsuite/gas/riscv/attribute-02.d
@@ -1,6 +1,6 @@
-#as: -march=rv32gxargle -march-attr
+#as: -march=rv32gxargle -march-attr -misa-spec=2p2
#readelf: -A
#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..04b9ab5 100644
--- a/gas/testsuite/gas/riscv/attribute-03.d
+++ b/gas/testsuite/gas/riscv/attribute-03.d
@@ -1,6 +1,6 @@
-#as: -march=rv32gxargle_xfoo -march-attr
+#as: -march=rv32gxargle_xfoo -march-attr -misa-spec=2p2
#readelf: -A
#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-04.d b/gas/testsuite/gas/riscv/attribute-04.d
index c97bf03..8a3f51b 100644
--- a/gas/testsuite/gas/riscv/attribute-04.d
+++ b/gas/testsuite/gas/riscv/attribute-04.d
@@ -1,4 +1,4 @@
-#as: -march-attr
+#as: -march-attr -misa-spec=2p2
#readelf: -A
#source: attribute-04.s
Attribute Section: riscv
diff --git a/gas/testsuite/gas/riscv/attribute-05.d b/gas/testsuite/gas/riscv/attribute-05.d
index f9b65f2..3309ff3 100644
--- a/gas/testsuite/gas/riscv/attribute-05.d
+++ b/gas/testsuite/gas/riscv/attribute-05.d
@@ -1,4 +1,4 @@
-#as: -march-attr
+#as: -march-attr -misa-spec=2p2
#readelf: -A
#source: attribute-05.s
Attribute Section: riscv
diff --git a/gas/testsuite/gas/riscv/attribute-06.d b/gas/testsuite/gas/riscv/attribute-06.d
index 1abeb47..5c8a5c6 100644
--- a/gas/testsuite/gas/riscv/attribute-06.d
+++ b/gas/testsuite/gas/riscv/attribute-06.d
@@ -1,4 +1,4 @@
-#as: -march=rv32g2p0 -march-attr
+#as: -march=rv32g2p1 -march-attr -misa-spec=2p2
#readelf: -A
#source: attribute-06.s
Attribute Section: riscv
diff --git a/gas/testsuite/gas/riscv/attribute-07.d b/gas/testsuite/gas/riscv/attribute-07.d
index dfd7e6b..20ac9be 100644
--- a/gas/testsuite/gas/riscv/attribute-07.d
+++ b/gas/testsuite/gas/riscv/attribute-07.d
@@ -1,4 +1,4 @@
-#as: -march=rv64g2p0 -march-attr
+#as: -march=rv64g2p1 -march-attr -misa-spec=2p2
#readelf: -A
#source: attribute-07.s
Attribute Section: riscv
diff --git a/gas/testsuite/gas/riscv/attribute-08.d b/gas/testsuite/gas/riscv/attribute-08.d
index c10ac0c..7f580d3 100644
--- a/gas/testsuite/gas/riscv/attribute-08.d
+++ b/gas/testsuite/gas/riscv/attribute-08.d
@@ -1,4 +1,4 @@
-#as: -march-attr
+#as: -march-attr -misa-spec=2p2
#readelf: -A
#source: attribute-08.s
Attribute Section: riscv
diff --git a/gas/testsuite/gas/riscv/attribute-09.d b/gas/testsuite/gas/riscv/attribute-09.d
new file mode 100644
index 0000000..77c7ef3
--- /dev/null
+++ b/gas/testsuite/gas/riscv/attribute-09.d
@@ -0,0 +1,6 @@
+#as: -march-attr -misa-spec=2p2 -mriscv-isa-version=i2p1a2p1v0p9
+#readelf: -A
+#source: attribute-09.s
+Attribute Section: riscv
+File Attributes
+ Tag_RISCV_arch: "rv32i1p9_f2p0_v0p9_zicsr0p0"
diff --git a/gas/testsuite/gas/riscv/attribute-09.s b/gas/testsuite/gas/riscv/attribute-09.s
new file mode 100644
index 0000000..0b1b16c
--- /dev/null
+++ b/gas/testsuite/gas/riscv/attribute-09.s
@@ -0,0 +1 @@
+ .attribute arch, "rv32i1p9fv_zicsr"
diff --git a/gas/testsuite/gas/riscv/attribute-10.d b/gas/testsuite/gas/riscv/attribute-10.d
new file mode 100644
index 0000000..9c72035
--- /dev/null
+++ b/gas/testsuite/gas/riscv/attribute-10.d
@@ -0,0 +1,6 @@
+#as: -march-attr -march=rv32i_zicsr -misa-spec=20191213
+#readelf: -A
+#source: empty.s
+Attribute Section: riscv
+File Attributes
+ Tag_RISCV_arch: "rv32i2p1_zicsr2p0"
diff --git a/gas/testsuite/gas/riscv/attribute-11.d b/gas/testsuite/gas/riscv/attribute-11.d
new file mode 100644
index 0000000..bc7f986
--- /dev/null
+++ b/gas/testsuite/gas/riscv/attribute-11.d
@@ -0,0 +1,6 @@
+#as: -march-attr -march=rv32i_zicsr -misa-spec=20191213 -mriscv-isa-version=zicsr2p1
+#readelf: -A
+#source: empty.s
+Attribute Section: riscv
+File Attributes
+ Tag_RISCV_arch: "rv32i2p1_zicsr2p1"
diff --git a/include/opcode/riscv.h b/include/opcode/riscv.h
index ac6e861..f8b0de3 100644
--- a/include/opcode/riscv.h
+++ b/include/opcode/riscv.h
@@ -343,6 +343,16 @@ struct riscv_opcode
unsigned long pinfo;
};

+/* This structure holds version information for specific ISA. */
+
+struct riscv_ext_version
+{
+ const char *name;
+ const char *spec_name;
+ unsigned int major_version;
+ unsigned int minor_version;
+};
+
/* Instruction is a simple alias (e.g. "mv" for "addi"). */
#define INSN_ALIAS 0x00000001

@@ -420,5 +430,6 @@ 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[];

#endif /* _RISCV_H_ */
diff --git a/opcodes/riscv-opc.c b/opcodes/riscv-opc.c
index ceedcaf..7b885bf 100644
--- a/opcodes/riscv-opc.c
+++ b/opcodes/riscv-opc.c
@@ -884,3 +884,56 @@ const struct riscv_opcode riscv_insn_types[] =
/* Terminate the list. */
{0, 0, INSN_CLASS_NONE, 0, 0, 0, 0, 0}
};
+
+const struct riscv_ext_version riscv_ext_version_table[] =
+{
+/* name, spec name, major, minor. */
+{"e", "20191213", 1, 9},
+{"e", "20190608", 1, 9},
+{"e", "2p2", 1, 9},
+
+{"i", "20191213", 2, 1},
+{"i", "20190608", 2, 1},
+{"i", "2p2", 2, 0},
+
+{"m", "20191213", 2, 0},
+{"m", "20190608", 2, 0},
+{"m", "2p2", 2, 0},
+
+{"a", "20191213", 2, 1},
+{"a", "20190608", 2, 0},
+{"a", "2p2", 2, 0},
+
+{"f", "20191213", 2, 2},
+{"f", "20190608", 2, 2},
+{"f", "2p2", 2, 0},
+
+{"d", "20191213", 2, 2},
+{"d", "20190608", 2, 2},
+{"d", "2p2", 2, 0},
+
+{"q", "20191213", 2, 2},
+{"q", "20190608", 2, 2},
+{"q", "2p2", 2, 0},
+
+{"c", "20191213", 2, 0},
+{"c", "20190608", 2, 0},
+{"c", "2p2", 2, 0},
+
+{"p", "20191213", 0, 2},
+{"p", "20190608", 0, 2},
+{"p", "2p2", 0, 1},
+
+{"v", "20191213", 0, 7},
+{"v", "20190608", 0, 7},
+{"v", "2p2", 0, 7},
+
+{"n", "20190608", 1, 1},
+{"n", "2p2", 1, 1},
+
+{"zicsr","20191213", 2, 0},
+{"zicsr","20190608", 2, 0},
+
+/* Terminate the list. */
+{NULL, NULL, 0, 0}
+};
--
2.7.4