[PATCH] RISC-V: Support new .option arch directive.
Nelson Chu
nelson.chu@sifive.com
Thu Jul 29 11:31:15 GMT 2021
https://github.com/riscv/riscv-asm-manual/pull/67
Format:
.option arch +<extension><version>, ...
.option arch -<extension>
The new direcitve is used to enable/disable extensions for the specific
code region. For example,
.attribute arch, "rv64ic" # arch = rv64i2p0_c2p0
.option push
.option arch +d2p0, -c # arch = rv64i2p0_f2p0_d2p0, f is added implied
.option pop # arch = rv64i2p0_c2p0
Note that,
1. ".option rvc/norvc" have the same behavior as ".option arch +c/-c".
2. ".option arch -i" is illegal, since we cannot remove base i extension.
3. If arch=rv64i2p0, then ".option arch +i3p0" will update the i's version
from 2.0 to 3.0.
bfd/
* elfxx-riscv.c (riscv_add_subset): If the subset is already added,
and the new versions are not RISCV_UNKNOWN_VERSION, then update the
versions to the subset list.
(riscv_copy_subset): New function. Copy the subset from list.
(riscv_copy_subset_list): New function. Return the new copyed list.
(riscv_remove_subset): New function. Remove the subset from list.
(riscv_update_subset): New function. Add subsets (or update versions)
if the extensions begin with `+`. Otherwise, call riscv_remove_subset
if the extensions begin with `-`.
* elfxx-riscv.h: Updated.
gas/
* config/tc-riscv.c (riscv_set_options): Removed rve field. But still
keep the rvc field since it will be used many times.
(riscv_opts): Likewise.
(reg_lookup_internal): Use riscv_subset_supports ("e") to replace
riscv_opts.rve.
(riscv_set_rve): Removed.
(struct riscv_option_stack, riscv_opts_stack): Moved forward.
(riscv_subset_supports): No need to check the riscv_opts.rvc, since
".option rvc/norvc" and ".option arch +c, -c" also update the subset
list for now.
(s_riscv_option): Support new .option arch directive, to add, remove or
update subsets for the specific code region.
(riscv_set_arch): Updated.
(riscv_write_out_attrs): Likewise.
* doc/c-riscv.texi: Added document for new .option arch directive.
* testsuite/gas/riscv/option-arch-01a.d: New testcase.
* testsuite/gas/riscv/option-arch-01b.d: Likewise.
* testsuite/gas/riscv/option-arch-01.s: Likewise..
* testsuite/gas/riscv/option-arch-02.d: Likewise.
* testsuite/gas/riscv/option-arch-02.s: Likewise.
* testsuite/gas/riscv/option-arch-fail.d: Likewise.
* testsuite/gas/riscv/option-arch-fail.l: Likewise.
* testsuite/gas/riscv/option-arch-fail.s: Likewise.
---
bfd/elfxx-riscv.c | 145 ++++++++++++++++++++-
bfd/elfxx-riscv.h | 7 +
gas/config/tc-riscv.c | 93 +++++++------
gas/doc/c-riscv.texi | 12 +-
gas/testsuite/gas/riscv/option-arch-01.s | 10 ++
gas/testsuite/gas/riscv/option-arch-01a.d | 14 ++
gas/testsuite/gas/riscv/option-arch-01b.d | 8 ++
gas/testsuite/gas/riscv/option-arch-02.d | 8 ++
gas/testsuite/gas/riscv/option-arch-02.s | 8 ++
gas/testsuite/gas/riscv/option-arch-fail.d | 3 +
gas/testsuite/gas/riscv/option-arch-fail.l | 5 +
gas/testsuite/gas/riscv/option-arch-fail.s | 8 ++
12 files changed, 282 insertions(+), 39 deletions(-)
create mode 100644 gas/testsuite/gas/riscv/option-arch-01.s
create mode 100644 gas/testsuite/gas/riscv/option-arch-01a.d
create mode 100644 gas/testsuite/gas/riscv/option-arch-01b.d
create mode 100644 gas/testsuite/gas/riscv/option-arch-02.d
create mode 100644 gas/testsuite/gas/riscv/option-arch-02.s
create mode 100644 gas/testsuite/gas/riscv/option-arch-fail.d
create mode 100644 gas/testsuite/gas/riscv/option-arch-fail.l
create mode 100644 gas/testsuite/gas/riscv/option-arch-fail.s
diff --git a/bfd/elfxx-riscv.c b/bfd/elfxx-riscv.c
index 2b8f60caf32..c690cd572eb 100644
--- a/bfd/elfxx-riscv.c
+++ b/bfd/elfxx-riscv.c
@@ -1315,7 +1315,15 @@ riscv_add_subset (riscv_subset_list_t *subset_list,
riscv_subset_t *current, *new;
if (riscv_lookup_subset (subset_list, subset, ¤t))
- return;
+ {
+ if (major != RISCV_UNKNOWN_VERSION
+ && minor != RISCV_UNKNOWN_VERSION)
+ {
+ current->major_version = major;
+ current->minor_version = minor;
+ }
+ return;
+ }
new = xmalloc (sizeof *new);
new->name = xstrdup (subset);
@@ -1832,6 +1840,141 @@ riscv_parse_subset (riscv_parse_subset_t *rps,
return riscv_parse_check_conflicts (rps);
}
+/* Copy the subset in the subset list. */
+
+static struct riscv_subset_t *
+riscv_copy_subset (riscv_subset_list_t *subset_list,
+ riscv_subset_t *subset)
+{
+ if (subset == NULL)
+ return NULL;
+
+ riscv_subset_t *new = xmalloc (sizeof *new);
+ new->name = xstrdup (subset->name);
+ new->major_version = subset->major_version;
+ new->minor_version = subset->minor_version;
+ new->next = riscv_copy_subset (subset_list, subset->next);
+
+ if (subset->next == NULL)
+ subset_list->tail = new;
+
+ return new;
+}
+
+/* Copy the subset list. */
+
+riscv_subset_list_t *
+riscv_copy_subset_list (riscv_subset_list_t *subset_list)
+{
+ riscv_subset_list_t *new = xmalloc (sizeof *new);
+ new->head = riscv_copy_subset (new, subset_list->head);
+ return new;
+}
+
+/* Remove the subset from the list. */
+
+static void
+riscv_remove_subset (riscv_subset_list_t *subset_list,
+ const char *subset)
+{
+ riscv_subset_t *current = subset_list->head;
+ riscv_subset_t *pre = NULL;
+ for (; current != NULL; pre = current, current = current->next)
+ {
+ if (strcmp (current->name, subset) == 0)
+ {
+ if (pre == NULL)
+ subset_list->head = current->next;
+ else
+ pre->next = current->next;
+ if (current->next == NULL)
+ subset_list->tail = pre;
+ free ((void *) current->name);
+ free (current);
+ break;
+ }
+ }
+}
+
+/* Update subset list for .option arch. */
+
+bool
+riscv_update_subset (riscv_parse_subset_t *rps,
+ const char *str)
+{
+ const char *p = str;
+
+ do
+ {
+ int major_version = RISCV_UNKNOWN_VERSION;
+ int minor_version = RISCV_UNKNOWN_VERSION;
+
+ bool removed = false;
+ switch (*p++)
+ {
+ case '+': removed = false; break;
+ case '-': removed = true; break;
+ default:
+ rps->error_handler
+ (_("extensions must begin with +/- in .option arch `%s'"), str);
+ return false;
+ }
+
+ char *subset = xstrdup (p);
+ char *q = subset;
+ while (*q != '\0' && !ISDIGIT (*q) && *q != ',')
+ q++;
+
+ const char *end_of_version =
+ riscv_parsing_subset_version (rps, p, q,
+ &major_version,
+ &minor_version, false);
+ *q = '\0';
+
+ if (end_of_version == NULL
+ || *subset == '\0')
+ {
+ rps->error_handler
+ (_("unexpected value in .option arch `%s'"), str);
+ free (subset);
+ return false;
+ }
+
+ if ((strlen (subset) == 1
+ && riscv_ext_order[(*subset - 'a')] == 0)
+ || (strlen (subset) > 1
+ && rps->check_unknown_prefixed_ext
+ && !riscv_valid_prefixed_ext (subset)))
+ {
+ rps->error_handler
+ (_("unknown ISA extension in .option arch `%s'"), subset);
+ free (subset);
+ return false;
+ }
+
+ if (removed)
+ {
+ if (strcmp (subset, "i") == 0)
+ {
+ rps->error_handler
+ (_("cannot remove extension i in .option arch `%s'"), str);
+ free (subset);
+ return false;
+ }
+ riscv_remove_subset (rps->subset_list, subset);
+ }
+ else
+ riscv_parse_add_subset (rps, subset, major_version, minor_version, true);
+ p += end_of_version - subset;
+ free (subset);
+ }
+ while (*p++ == ',');
+
+ riscv_parse_add_implicit_subsets (rps);
+
+ return riscv_parse_check_conflicts (rps);
+}
+
/* Return the number of digits for the input. */
size_t
diff --git a/bfd/elfxx-riscv.h b/bfd/elfxx-riscv.h
index 6a2501b7be8..cf5f61b0965 100644
--- a/bfd/elfxx-riscv.h
+++ b/bfd/elfxx-riscv.h
@@ -81,6 +81,13 @@ extern bool
riscv_parse_subset (riscv_parse_subset_t *,
const char *);
+extern riscv_subset_list_t *
+riscv_copy_subset_list (riscv_subset_list_t *);
+
+extern bool
+riscv_update_subset (riscv_parse_subset_t *,
+ const char *);
+
extern const char *
riscv_supported_std_ext (void);
diff --git a/gas/config/tc-riscv.c b/gas/config/tc-riscv.c
index 460667e4349..b4ee2d677bd 100644
--- a/gas/config/tc-riscv.c
+++ b/gas/config/tc-riscv.c
@@ -263,22 +263,31 @@ struct riscv_set_options
{
int pic; /* Generate position-independent code. */
int rvc; /* Generate RVC code. */
- int rve; /* Generate RVE code. */
int relax; /* Emit relocs the linker is allowed to relax. */
int arch_attr; /* Emit architecture and privileged elf attributes. */
int csr_check; /* Enable the CSR checking. */
};
-
static struct riscv_set_options riscv_opts =
{
0, /* pic */
0, /* rvc */
- 0, /* rve */
1, /* relax */
DEFAULT_RISCV_ATTR, /* arch_attr */
0, /* csr_check */
};
+static riscv_subset_list_t *riscv_subsets = NULL;
+
+/* This structure is used to hold a stack of .option values. */
+struct riscv_option_stack
+{
+ struct riscv_option_stack *next;
+ struct riscv_set_options options;
+ riscv_subset_list_t *subset_list;
+};
+
+static struct riscv_option_stack *riscv_opts_stack = NULL;
+
static void
riscv_set_rvc (bool rvc_value)
{
@@ -288,23 +297,12 @@ riscv_set_rvc (bool rvc_value)
riscv_opts.rvc = rvc_value;
}
-static void
-riscv_set_rve (bool rve_value)
-{
- riscv_opts.rve = rve_value;
-}
-
-static riscv_subset_list_t riscv_subsets;
-
static bool
riscv_subset_supports (const char *feature)
{
struct riscv_subset_t *subset;
-
- if (riscv_opts.rvc && (strcasecmp (feature, "c") == 0))
- return true;
-
- return riscv_lookup_subset (&riscv_subsets, feature, &subset);
+ return riscv_lookup_subset (riscv_subsets,
+ feature, &subset);
}
static bool
@@ -405,8 +403,15 @@ riscv_get_default_ext_version (const char *name,
static void
riscv_set_arch (const char *s)
{
+ if (riscv_subsets == NULL)
+ {
+ riscv_subsets = XNEW (riscv_subset_list_t);
+ riscv_subsets->head = NULL;
+ riscv_subsets->tail = NULL;
+ }
+
riscv_parse_subset_t rps;
- rps.subset_list = &riscv_subsets;
+ rps.subset_list = riscv_subsets;
rps.error_handler = as_bad;
rps.xlen = &xlen;
rps.get_default_version = riscv_get_default_ext_version;
@@ -415,16 +420,12 @@ riscv_set_arch (const char *s)
if (s == NULL)
return;
- riscv_release_subset_list (&riscv_subsets);
+ riscv_release_subset_list (riscv_subsets);
riscv_parse_subset (&rps, s);
- /* To support .option rvc and rve. */
riscv_set_rvc (false);
if (riscv_subset_supports ("c"))
riscv_set_rvc (true);
- riscv_set_rve (false);
- if (riscv_subset_supports ("e"))
- riscv_set_rve (true);
}
/* Indicate -mabi option is explictly set. */
@@ -948,7 +949,9 @@ reg_lookup_internal (const char *s, enum reg_class class)
if (r == NULL || DECODE_REG_CLASS (r) != class)
return -1;
- if (riscv_opts.rve && class == RCLASS_GPR && DECODE_REG_NUM (r) > 15)
+ if (riscv_subset_supports ("e")
+ && class == RCLASS_GPR
+ && DECODE_REG_NUM (r) > 15)
return -1;
return DECODE_REG_NUM (r);
@@ -3258,15 +3261,6 @@ riscv_pre_output_hook (void)
subseg_set (seg, subseg);
}
-/* This structure is used to hold a stack of .option values. */
-struct riscv_option_stack
-{
- struct riscv_option_stack *next;
- struct riscv_set_options options;
-};
-
-static struct riscv_option_stack *riscv_opts_stack;
-
/* Handle the .option pseudo-op. */
static void
@@ -3279,10 +3273,23 @@ s_riscv_option (int x ATTRIBUTE_UNUSED)
ch = *input_line_pointer;
*input_line_pointer = '\0';
+ riscv_parse_subset_t rps;
+ rps.subset_list = riscv_subsets;
+ rps.error_handler = as_bad;
+ rps.xlen = &xlen;
+ rps.get_default_version = riscv_get_default_ext_version;
+ rps.check_unknown_prefixed_ext = true;
+
if (strcmp (name, "rvc") == 0)
- riscv_set_rvc (true);
+ {
+ riscv_update_subset (&rps, "+c");
+ riscv_set_rvc (true);
+ }
else if (strcmp (name, "norvc") == 0)
- riscv_set_rvc (false);
+ {
+ riscv_update_subset (&rps, "-c");
+ riscv_set_rvc (false);
+ }
else if (strcmp (name, "pic") == 0)
riscv_opts.pic = true;
else if (strcmp (name, "nopic") == 0)
@@ -3295,14 +3302,23 @@ s_riscv_option (int x ATTRIBUTE_UNUSED)
riscv_opts.csr_check = true;
else if (strcmp (name, "no-csr-check") == 0)
riscv_opts.csr_check = false;
+ else if (strncmp (name, "arch", 4) == 0)
+ {
+ name += 4;
+ if (ISSPACE (*name) && *name != '\0')
+ name++;
+ riscv_update_subset (&rps, name);
+ }
else if (strcmp (name, "push") == 0)
{
struct riscv_option_stack *s;
- s = (struct riscv_option_stack *) xmalloc (sizeof *s);
+ s = XNEW (struct riscv_option_stack);
s->next = riscv_opts_stack;
s->options = riscv_opts;
+ s->subset_list = riscv_subsets;
riscv_opts_stack = s;
+ riscv_subsets = riscv_copy_subset_list (s->subset_list);
}
else if (strcmp (name, "pop") == 0)
{
@@ -3313,8 +3329,11 @@ s_riscv_option (int x ATTRIBUTE_UNUSED)
as_bad (_(".option pop with no .option push"));
else
{
- riscv_opts = s->options;
+ riscv_subset_list_t *release_subsets = riscv_subsets;
riscv_opts_stack = s->next;
+ riscv_opts = s->options;
+ riscv_subsets = s->subset_list;
+ riscv_release_subset_list (release_subsets);
free (s);
}
}
@@ -3751,7 +3770,7 @@ riscv_write_out_attrs (void)
unsigned int i;
/* Re-write architecture elf attribute. */
- arch_str = riscv_arch_str (xlen, &riscv_subsets);
+ arch_str = riscv_arch_str (xlen, riscv_subsets);
bfd_elf_add_proc_attr_string (stdoutput, Tag_RISCV_arch, arch_str);
xfree ((void *) arch_str);
diff --git a/gas/doc/c-riscv.texi b/gas/doc/c-riscv.texi
index c15297e1bea..5c227d5d4c7 100644
--- a/gas/doc/c-riscv.texi
+++ b/gas/doc/c-riscv.texi
@@ -194,7 +194,7 @@ command-line options are respected for the bulk of the file being assembled.
@itemx norvc
Enables or disables the generation of compressed instructions. Instructions
are opportunistically compressed by the RISC-V assembler when possible, but
-sometimes this behavior is not desirable.
+sometimes this behavior is not desirable, especially when handling alignments.
@item pic
@itemx nopic
@@ -212,6 +212,16 @@ desirable.
@itemx no-csr-check
Enables or disables the CSR checking.
+@item arch @var{+extension[version]} [,...,@var{+extension_n[version_n]}]
+@itemx arch @var{-extension} [,...,@var{-extension_n}]
+Enables or disables the extensions for specific code region. For example,
+@samp{.option arch +m2p0} means add m extension with version 2.0, and
+@samp{.option arch -f, -d} means remove extensions, f and d, from the
+architecture string. Note that, @samp{.option arch +c, -c} have the same
+behavior as @samp{.option rvc, norvc}. However, they are also undesirable
+sometimes. Besides, @samp{.option arch -i} is illegal, since we cannot
+remove the base i extension anytime.
+
@cindex INSN directives
@item .insn @var{value}
@itemx .insn @var{value}
diff --git a/gas/testsuite/gas/riscv/option-arch-01.s b/gas/testsuite/gas/riscv/option-arch-01.s
new file mode 100644
index 00000000000..90fddf13f89
--- /dev/null
+++ b/gas/testsuite/gas/riscv/option-arch-01.s
@@ -0,0 +1,10 @@
+.attribute arch, "rv64ic"
+add a0, a0, a1
+.option push
+.option arch +d2p0, -c, +xvendor1p0
+add a0, a0, a1
+frcsr a0
+.option push
+.option arch +i3p0, +m3p0, +d3p0
+.option pop
+.option pop
diff --git a/gas/testsuite/gas/riscv/option-arch-01a.d b/gas/testsuite/gas/riscv/option-arch-01a.d
new file mode 100644
index 00000000000..86a44d415e5
--- /dev/null
+++ b/gas/testsuite/gas/riscv/option-arch-01a.d
@@ -0,0 +1,14 @@
+#as:
+#source: option-arch-01.s
+#objdump: -d
+
+.*:[ ]+file format .*
+
+
+Disassembly of section .text:
+
+0+000 <.text>:
+[ ]+[0-9a-f]+:[ ]+952e[ ]+add[ ]+a0,a0,a1
+[ ]+[0-9a-f]+:[ ]+00b50533[ ]+add[ ]+a0,a0,a1
+[ ]+[0-9a-f]+:[ ]+00302573[ ]+frcsr[ ]+a0
+#...
diff --git a/gas/testsuite/gas/riscv/option-arch-01b.d b/gas/testsuite/gas/riscv/option-arch-01b.d
new file mode 100644
index 00000000000..9a6c2c528ca
--- /dev/null
+++ b/gas/testsuite/gas/riscv/option-arch-01b.d
@@ -0,0 +1,8 @@
+#as:
+#readelf: -A
+#source: option-arch-01.s
+
+Attribute Section: riscv
+File Attributes
+ Tag_RISCV_arch: "rv64i2p0_c2p0"
+#...
diff --git a/gas/testsuite/gas/riscv/option-arch-02.d b/gas/testsuite/gas/riscv/option-arch-02.d
new file mode 100644
index 00000000000..c79a5c356dc
--- /dev/null
+++ b/gas/testsuite/gas/riscv/option-arch-02.d
@@ -0,0 +1,8 @@
+#as:
+#readelf: -A
+#source: option-arch-02.s
+
+Attribute Section: riscv
+File Attributes
+ Tag_RISCV_arch: "rv64i3p0_m3p0_f2p0_d3p0_c2p0"
+#...
diff --git a/gas/testsuite/gas/riscv/option-arch-02.s b/gas/testsuite/gas/riscv/option-arch-02.s
new file mode 100644
index 00000000000..207f51d2828
--- /dev/null
+++ b/gas/testsuite/gas/riscv/option-arch-02.s
@@ -0,0 +1,8 @@
+.attribute arch, "rv64ic"
+add a0, a0, a1
+.option push
+.option arch +d2p0, -c, +xvendor1p0
+add a0, a0, a1
+frcsr a0
+.option pop
+.option arch +i3p0, +m3p0, +d3p0
diff --git a/gas/testsuite/gas/riscv/option-arch-fail.d b/gas/testsuite/gas/riscv/option-arch-fail.d
new file mode 100644
index 00000000000..bce5d324f37
--- /dev/null
+++ b/gas/testsuite/gas/riscv/option-arch-fail.d
@@ -0,0 +1,3 @@
+#as:
+#source: option-arch-fail.s
+#error_output: option-arch-fail.l
diff --git a/gas/testsuite/gas/riscv/option-arch-fail.l b/gas/testsuite/gas/riscv/option-arch-fail.l
new file mode 100644
index 00000000000..3fd258efc86
--- /dev/null
+++ b/gas/testsuite/gas/riscv/option-arch-fail.l
@@ -0,0 +1,5 @@
+.*Assembler messages:
+.*Error: extensions must begin with \+/\- in .option arch `m2p0'
+.*Error: cannot remove extension i in .option arch `-i'
+.*Error: unknown ISA extension in .option arch `zsubset'
+.*Error: .option pop with no .option push
diff --git a/gas/testsuite/gas/riscv/option-arch-fail.s b/gas/testsuite/gas/riscv/option-arch-fail.s
new file mode 100644
index 00000000000..dd4ff644f83
--- /dev/null
+++ b/gas/testsuite/gas/riscv/option-arch-fail.s
@@ -0,0 +1,8 @@
+.attribute arch, "rv64ic"
+.option push
+.option arch m2p0
+.option arch -i
+.option arch +zsubset2p0
+.option arch +f2p0_d2p0
+.option pop
+.option pop
--
2.30.2
More information about the Binutils
mailing list