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

Nelson Chu
2020-05-06T02:55:56Z

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