[v2] RISC-V: Support new .option arch directive.

Message ID 1630409654-21459-1-git-send-email-nelson.chu@sifive.com
State New
Headers show
Series
  • [v2] RISC-V: Support new .option arch directive.
Related show

Commit Message

Nelson Chu Aug. 31, 2021, 11:34 a.m.
* Compared to v1 patch,

1. Added new operand '=' to reset the whole architecture configuration.
2. Updated the syntax, need a comma after .option arch.
3. Enhanced the parser, to report more errors in details.

-- 
2.7.4

Patch

======================================================================

https://github.com/riscv/riscv-asm-manual/pull/67

Format:
.option arch, +<extension><version>, ...
.option arch, -<extension>
.option arch, =<ISA string>

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 arch, =rv32gc       # arch = rv32i2p0_m2p0_a2p0_f2p0_d2p0_c2p0
.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.
4. If arch=rv64i3p0, then ".option arch, +i" will update the i's version
   from 2.0 to the default one according to the chosen isa spec.

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                          | 150 ++++++++++++++++++++++++++++-
 bfd/elfxx-riscv.h                          |   7 ++
 gas/config/tc-riscv.c                      |  93 +++++++++++-------
 gas/doc/c-riscv.texi                       |  15 ++-
 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-03.d   |   8 ++
 gas/testsuite/gas/riscv/option-arch-03.s   |   3 +
 gas/testsuite/gas/riscv/option-arch-fail.d |   3 +
 gas/testsuite/gas/riscv/option-arch-fail.l |   7 ++
 gas/testsuite/gas/riscv/option-arch-fail.s |   9 ++
 14 files changed, 304 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-03.d
 create mode 100644 gas/testsuite/gas/riscv/option-arch-03.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 2b8f60c..f685171 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, &current))
-    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,146 @@  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;
+	case '=':
+	  riscv_release_subset_list (rps->subset_list);
+	  return riscv_parse_subset (rps, p);
+	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)
+	{
+	  free (subset);
+	  return false;
+	}
+
+      if (strlen (subset) == 0
+	  || (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++ == ',');
+
+  if (*(--p) != '\0')
+    rps->error_handler
+      (_("unexpected value in .option arch `%s'"), str);
+
+  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 6a2501b..cf5f61b 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 1dbc4d8..4d52b55 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.  */
@@ -1099,7 +1100,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);
@@ -3456,15 +3459,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
@@ -3477,10 +3471,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)
@@ -3493,14 +3500,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,", 5) == 0)
+    {
+      name += 5;
+      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)
     {
@@ -3511,8 +3527,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);
 	}
     }
@@ -3993,7 +4012,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 bfbf61d..697be3a 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,19 @@  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}]
+@itemx arch, @var{=ISA}
+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.  If you want to reset the whole ISA
+string, you can also use @samp{.option arch, =rv32imac} to overwrite the
+previous settings.
+
 @cindex INSN directives
 @item .insn @var{type}, @var{operand} [,...,@var{operand_n}]
 @itemx .insn @var{insn_length}, @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 0000000..9e82616
--- /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 0000000..86a44d4
--- /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 0000000..9a6c2c5
--- /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 0000000..c79a5c3
--- /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 0000000..0aa0fc9
--- /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-03.d b/gas/testsuite/gas/riscv/option-arch-03.d
new file mode 100644
index 0000000..b621d03
--- /dev/null
+++ b/gas/testsuite/gas/riscv/option-arch-03.d
@@ -0,0 +1,8 @@ 
+#as:
+#readelf: -A
+#source: option-arch-03.s
+
+Attribute Section: riscv
+File Attributes
+  Tag_RISCV_arch: "rv32i2p0_c2p0"
+#...
diff --git a/gas/testsuite/gas/riscv/option-arch-03.s b/gas/testsuite/gas/riscv/option-arch-03.s
new file mode 100644
index 0000000..7183140
--- /dev/null
+++ b/gas/testsuite/gas/riscv/option-arch-03.s
@@ -0,0 +1,3 @@ 
+.attribute arch, "rv64ic"
+.option arch, +d2p0, -c
+.option arch, =rv32ic
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 0000000..bce5d32
--- /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 0000000..499dc80
--- /dev/null
+++ b/gas/testsuite/gas/riscv/option-arch-fail.l
@@ -0,0 +1,7 @@ 
+.*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: unexpected value in .option arch `\+f2p0_d2p0'
+.*Error: unknown ISA extension in .option arch `'
+.*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 0000000..a0ff1d3
--- /dev/null
+++ b/gas/testsuite/gas/riscv/option-arch-fail.s
@@ -0,0 +1,9 @@ 
+.attribute arch, "rv64ic"
+.option push
+.option arch, m2p0
+.option arch, -i
+.option arch, +zsubset2p0
+.option arch, +f2p0_d2p0
+.option arch, +
+.option pop
+.option pop