RISC-V: Set EF_RISCV_TSO also on .option arch
[binutils-gdb.git] / gas / config / tc-riscv.c
index f0c1f4ca75cd65203778083270d7af6f806d33c7..42d7bf62e4f71b7161cfa0fc465ee2b0302272ed 100644 (file)
@@ -1,5 +1,5 @@
 /* tc-riscv.c -- RISC-V assembler
-   Copyright (C) 2011-2019 Free Software Foundation, Inc.
+   Copyright (C) 2011-2022 Free Software Foundation, Inc.
 
    Contributed by Andrew Waterman (andrew@sifive.com).
    Based on MIPS target.
@@ -55,6 +55,66 @@ struct riscv_cl_insn
   fixS *fixp;
 };
 
+/* All RISC-V CSR belong to one of these classes.  */
+enum riscv_csr_class
+{
+  CSR_CLASS_NONE,
+
+  CSR_CLASS_I,
+  CSR_CLASS_I_32,      /* rv32 only */
+  CSR_CLASS_F,         /* f-ext only */
+  CSR_CLASS_ZKR,       /* zkr only */
+  CSR_CLASS_V,         /* rvv only */
+  CSR_CLASS_DEBUG,     /* debug CSR */
+  CSR_CLASS_H,         /* hypervisor */
+  CSR_CLASS_H_32,      /* hypervisor, rv32 only */
+  CSR_CLASS_SMSTATEEN,         /* Smstateen only */
+  CSR_CLASS_SMSTATEEN_AND_H,   /* Smstateen only (with H) */
+  CSR_CLASS_SMSTATEEN_32,      /* Smstateen RV32 only */
+  CSR_CLASS_SMSTATEEN_AND_H_32,        /* Smstateen RV32 only (with H) */
+  CSR_CLASS_SSCOFPMF,          /* Sscofpmf only */
+  CSR_CLASS_SSCOFPMF_32,       /* Sscofpmf RV32 only */
+  CSR_CLASS_SSTC,              /* Sstc only */
+  CSR_CLASS_SSTC_AND_H,                /* Sstc only (with H) */
+  CSR_CLASS_SSTC_32,           /* Sstc RV32 only */
+  CSR_CLASS_SSTC_AND_H_32,     /* Sstc RV32 only (with H) */
+};
+
+/* This structure holds all restricted conditions for a CSR.  */
+struct riscv_csr_extra
+{
+  /* Class to which this CSR belongs.  Used to decide whether or
+     not this CSR is legal in the current -march context.  */
+  enum riscv_csr_class csr_class;
+
+  /* CSR may have differnet numbers in the previous priv spec.  */
+  unsigned address;
+
+  /* Record the CSR is defined/valid in which versions.  */
+  enum riscv_spec_class define_version;
+
+  /* Record the CSR is aborted/invalid from which versions.  If it isn't
+     aborted in the current version, then it should be PRIV_SPEC_CLASS_DRAFT.  */
+  enum riscv_spec_class abort_version;
+
+  /* The CSR may have more than one setting.  */
+  struct riscv_csr_extra *next;
+};
+
+/* This structure contains information about errors that occur within the
+   riscv_ip function */
+struct riscv_ip_error
+{
+  /* General error message */
+  const char* msg;
+
+  /* Statement that caused the error */
+  char* statement;
+
+  /* Missing extension that needs to be enabled */
+  const char* missing_ext;
+};
+
 #ifndef DEFAULT_ARCH
 #define DEFAULT_ARCH "riscv64"
 #endif
@@ -63,39 +123,133 @@ struct riscv_cl_insn
 #define DEFAULT_RISCV_ATTR 0
 #endif
 
-static const char default_arch[] = DEFAULT_ARCH;
+/* Let riscv_after_parse_args set the default value according to xlen.  */
+#ifndef DEFAULT_RISCV_ARCH_WITH_EXT
+#define DEFAULT_RISCV_ARCH_WITH_EXT NULL
+#endif
+
+/* Need to sync the version with RISC-V compiler.  */
+#ifndef DEFAULT_RISCV_ISA_SPEC
+#define DEFAULT_RISCV_ISA_SPEC "20191213"
+#endif
+
+#ifndef DEFAULT_RISCV_PRIV_SPEC
+#define DEFAULT_RISCV_PRIV_SPEC "1.11"
+#endif
 
-static unsigned xlen = 0; /* width of an x-register */
-static unsigned abi_xlen = 0; /* width of a pointer in the ABI */
-static bfd_boolean rve_abi = FALSE;
+static const char default_arch[] = DEFAULT_ARCH;
+static const char *default_arch_with_ext = DEFAULT_RISCV_ARCH_WITH_EXT;
+static enum riscv_spec_class default_isa_spec = ISA_SPEC_CLASS_NONE;
+static enum riscv_spec_class default_priv_spec = PRIV_SPEC_CLASS_NONE;
+
+static unsigned xlen = 0; /* The width of an x-register.  */
+static unsigned abi_xlen = 0; /* The width of a pointer in the ABI.  */
+static bool rve_abi = false;
+enum float_abi
+{
+  FLOAT_ABI_DEFAULT = -1,
+  FLOAT_ABI_SOFT,
+  FLOAT_ABI_SINGLE,
+  FLOAT_ABI_DOUBLE,
+  FLOAT_ABI_QUAD
+};
+static enum float_abi float_abi = FLOAT_ABI_DEFAULT;
 
 #define LOAD_ADDRESS_INSN (abi_xlen == 64 ? "ld" : "lw")
 #define ADD32_INSN (xlen == 64 ? "addiw" : "addi")
 
 static unsigned elf_flags = 0;
 
-/* This is the set of options which the .option pseudo-op may modify.  */
+/* Set the default_isa_spec.  Return 0 if the spec isn't supported.
+   Otherwise, return 1.  */
+
+static int
+riscv_set_default_isa_spec (const char *s)
+{
+  enum riscv_spec_class class = ISA_SPEC_CLASS_NONE;
+  RISCV_GET_ISA_SPEC_CLASS (s, class);
+  if (class == ISA_SPEC_CLASS_NONE)
+    {
+      as_bad ("unknown default ISA spec `%s' set by "
+             "-misa-spec or --with-isa-spec", s);
+      return 0;
+    }
+  else
+    default_isa_spec = class;
+  return 1;
+}
+
+/* Set the default_priv_spec.  Find the privileged elf attributes when
+   the input string is NULL.  Return 0 if the spec isn't supported.
+   Otherwise, return 1.  */
+
+static int
+riscv_set_default_priv_spec (const char *s)
+{
+  enum riscv_spec_class class = PRIV_SPEC_CLASS_NONE;
+  unsigned major, minor, revision;
+  obj_attribute *attr;
 
+  RISCV_GET_PRIV_SPEC_CLASS (s, class);
+  if (class != PRIV_SPEC_CLASS_NONE)
+    {
+      default_priv_spec = class;
+      return 1;
+    }
+
+  if (s != NULL)
+    {
+      as_bad (_("unknown default privileged spec `%s' set by "
+               "-mpriv-spec or --with-priv-spec"), s);
+      return 0;
+    }
+
+  /* Set the default_priv_spec by the privileged elf attributes.  */
+  attr = elf_known_obj_attributes_proc (stdoutput);
+  major = (unsigned) attr[Tag_RISCV_priv_spec].i;
+  minor = (unsigned) attr[Tag_RISCV_priv_spec_minor].i;
+  revision = (unsigned) attr[Tag_RISCV_priv_spec_revision].i;
+  /* Version 0.0.0 is the default value and meningless.  */
+  if (major == 0 && minor == 0 && revision == 0)
+    return 1;
+
+  riscv_get_priv_spec_class_from_numbers (major, minor, revision, &class);
+  if (class != PRIV_SPEC_CLASS_NONE)
+    {
+      default_priv_spec = class;
+      return 1;
+    }
+
+  /* Still can not find the privileged spec class.  */
+  as_bad (_("unknown default privileged spec `%d.%d.%d' set by "
+           "privileged elf attributes"), major, minor, revision);
+  return 0;
+}
+
+/* This is the set of options which the .option pseudo-op may modify.  */
 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 arch attribute.  */
+  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 */
+  0, /* pic */
+  0, /* rvc */
+  1, /* relax */
   DEFAULT_RISCV_ATTR, /* arch_attr */
+  0, /* csr_check */
 };
 
+/* Enable or disable the rvc flags for riscv_opts.  Turn on the rvc flag
+   for elf_flags once we have enabled c extension.  */
+
 static void
-riscv_set_rvc (bfd_boolean rvc_value)
+riscv_set_rvc (bool rvc_value)
 {
   if (rvc_value)
     elf_flags |= EF_RISCV_RVC;
@@ -103,84 +257,173 @@ riscv_set_rvc (bfd_boolean rvc_value)
   riscv_opts.rvc = rvc_value;
 }
 
+/* Turn on the tso flag for elf_flags once we have enabled ztso extension.  */
+
 static void
-riscv_set_rve (bfd_boolean rve_value)
+riscv_set_tso ()
 {
-  riscv_opts.rve = rve_value;
+  elf_flags |= EF_RISCV_TSO;
 }
 
-static riscv_subset_list_t riscv_subsets;
+/* This linked list records all enabled extensions, which are parsed from
+   the architecture string.  The architecture string can be set by the
+   -march option, the elf architecture attributes, and the --with-arch
+   configure option.  */
+static riscv_subset_list_t *riscv_subsets = NULL;
+static riscv_parse_subset_t riscv_rps_as =
+{
+  NULL,                        /* subset_list, we will set it later once
+                          riscv_opts_stack is created or updated.  */
+  as_bad,              /* error_handler.  */
+  &xlen,               /* xlen.  */
+  &default_isa_spec,   /* isa_spec.  */
+  true,                        /* check_unknown_prefixed_ext.  */
+};
 
-static bfd_boolean
-riscv_subset_supports (const char *feature)
+/* This structure is used to hold a stack of .option values.  */
+struct riscv_option_stack
 {
-  if (riscv_opts.rvc && (strcasecmp (feature, "c") == 0))
-    return TRUE;
+  struct riscv_option_stack *next;
+  struct riscv_set_options options;
+  riscv_subset_list_t *subset_list;
+};
 
-  return riscv_lookup_subset (&riscv_subsets, feature) != NULL;
-}
+static struct riscv_option_stack *riscv_opts_stack = NULL;
 
-static bfd_boolean
-riscv_multi_subset_supports (const char *features[])
+/* Set which ISA and extensions are available.  */
+
+static void
+riscv_set_arch (const char *s)
 {
-  unsigned i = 0;
-  bfd_boolean supported = TRUE;
+  if (s != NULL && strcmp (s, "") == 0)
+    {
+      as_bad (_("the architecture string of -march and elf architecture "
+               "attributes cannot be empty"));
+      return;
+    }
 
-  for (;features[i]; ++i)
-    supported = supported && riscv_subset_supports (features[i]);
+  if (riscv_subsets == NULL)
+    {
+      riscv_subsets = XNEW (riscv_subset_list_t);
+      riscv_subsets->head = NULL;
+      riscv_subsets->tail = NULL;
+      riscv_rps_as.subset_list = riscv_subsets;
+    }
+  riscv_release_subset_list (riscv_subsets);
+  riscv_parse_subset (&riscv_rps_as, s);
+
+  riscv_set_rvc (false);
+  if (riscv_subset_supports (&riscv_rps_as, "c"))
+    riscv_set_rvc (true);
 
-  return supported;
+  if (riscv_subset_supports (&riscv_rps_as, "ztso"))
+    riscv_set_tso ();
 }
 
-/* Set which ISA and extensions are available.  */
+/* Indicate -mabi option is explictly set.  */
+static bool explicit_mabi = false;
+
+/* Set the abi information.  */
 
 static void
-riscv_set_arch (const char *s)
+riscv_set_abi (unsigned new_xlen, enum float_abi new_float_abi, bool rve)
 {
-  riscv_parse_subset_t rps;
-  rps.subset_list = &riscv_subsets;
-  rps.error_handler = as_fatal;
-  rps.xlen = &xlen;
+  abi_xlen = new_xlen;
+  float_abi = new_float_abi;
+  rve_abi = rve;
+}
+
+/* If the -mabi option isn't set, then set the abi according to the
+   ISA string.  Otherwise, check if there is any conflict.  */
 
-  riscv_release_subset_list (&riscv_subsets);
-  riscv_parse_subset (&rps, s);
+static void
+riscv_set_abi_by_arch (void)
+{
+  if (!explicit_mabi)
+    {
+      if (riscv_subset_supports (&riscv_rps_as, "q"))
+       riscv_set_abi (xlen, FLOAT_ABI_QUAD, false);
+      else if (riscv_subset_supports (&riscv_rps_as, "d"))
+       riscv_set_abi (xlen, FLOAT_ABI_DOUBLE, false);
+      else if (riscv_subset_supports (&riscv_rps_as, "e"))
+       riscv_set_abi (xlen, FLOAT_ABI_SOFT, true);
+      else
+       riscv_set_abi (xlen, FLOAT_ABI_SOFT, false);
+    }
+  else
+    {
+      gas_assert (abi_xlen != 0 && xlen != 0 && float_abi != FLOAT_ABI_DEFAULT);
+      if (abi_xlen > xlen)
+       as_bad ("can't have %d-bit ABI on %d-bit ISA", abi_xlen, xlen);
+      else if (abi_xlen < xlen)
+       as_bad ("%d-bit ABI not yet supported on %d-bit ISA", abi_xlen, xlen);
+
+      if (riscv_subset_supports (&riscv_rps_as, "e") && !rve_abi)
+       as_bad ("only the ilp32e ABI is supported for e extension");
+
+      if (float_abi == FLOAT_ABI_SINGLE
+         && !riscv_subset_supports (&riscv_rps_as, "f"))
+       as_bad ("ilp32f/lp64f ABI can't be used when f extension "
+               "isn't supported");
+      else if (float_abi == FLOAT_ABI_DOUBLE
+              && !riscv_subset_supports (&riscv_rps_as, "d"))
+       as_bad ("ilp32d/lp64d ABI can't be used when d extension "
+               "isn't supported");
+      else if (float_abi == FLOAT_ABI_QUAD
+              && !riscv_subset_supports (&riscv_rps_as, "q"))
+       as_bad ("ilp32q/lp64q ABI can't be used when q extension "
+               "isn't supported");
+    }
+
+  /* Update the EF_RISCV_FLOAT_ABI field of elf_flags.  */
+  elf_flags &= ~EF_RISCV_FLOAT_ABI;
+  elf_flags |= float_abi << 1;
+
+  if (rve_abi)
+    elf_flags |= EF_RISCV_RVE;
 }
 
 /* Handle of the OPCODE hash table.  */
-static struct hash_control *op_hash = NULL;
+static htab_t op_hash = NULL;
 
 /* Handle of the type of .insn hash table.  */
-static struct hash_control *insn_type_hash = NULL;
+static htab_t insn_type_hash = NULL;
 
 /* This array holds the chars that always start a comment.  If the
-    pre-processor is disabled, these aren't very useful */
+   pre-processor is disabled, these aren't very useful.  */
 const char comment_chars[] = "#";
 
 /* This array holds the chars that only start a comment at the beginning of
    a line.  If the line seems to have the form '# 123 filename'
-   .line and .file directives will appear in the pre-processed output */
-/* Note that input_file.c hand checks for '#' at the beginning of the
+   .line and .file directives will appear in the pre-processed output
+
+   Note that input_file.c hand checks for '#' at the beginning of the
    first line of the input file.  This is because the compiler outputs
-   #NO_APP at the beginning of its output.  */
-/* Also note that C style comments are always supported.  */
+   #NO_APP at the beginning of its output.
+
+   Also note that C style comments are always supported.  */
 const char line_comment_chars[] = "#";
 
 /* This array holds machine specific line separator characters.  */
 const char line_separator_chars[] = ";";
 
-/* Chars that can be used to separate mant from exp in floating point nums */
+/* Chars that can be used to separate mant from exp in floating point nums */
 const char EXP_CHARS[] = "eE";
 
-/* Chars that mean this number is a floating point constant */
-/* As in 0f12.456 */
-/* or    0d1.2345e12 */
-const char FLT_CHARS[] = "rRsSfFdDxXpP";
+/* Chars that mean this number is a floating point constant.
+   As in 0f12.456 or 0d1.2345e12.  */
+const char FLT_CHARS[] = "rRsSfFdDxXpPhH";
 
 /* Indicate we are already assemble any instructions or not.  */
-static bfd_boolean start_assemble = FALSE;
+static bool start_assemble = false;
 
-/* Indicate arch attribute is explictly set.  */
-static bfd_boolean explicit_arch_attr = FALSE;
+/* Indicate ELF attributes are explicitly set.  */
+static bool explicit_attr = false;
+
+/* Indicate CSR or priv instructions are explicitly used.  */
+static bool explicit_priv_attr = false;
+
+static char *expr_end;
 
 /* Macros for encoding relaxation state for RVC branches and far jumps.  */
 #define RELAX_BRANCH_ENCODE(uncond, rvc, length)       \
@@ -213,14 +456,166 @@ static bfd_boolean explicit_arch_attr = FALSE;
 #define OPCODE_MATCHES(OPCODE, OP) \
   (((OPCODE) & MASK_##OP) == MATCH_##OP)
 
-static char *expr_end;
+/* Create a new mapping symbol for the transition to STATE.  */
+
+static void
+make_mapping_symbol (enum riscv_seg_mstate state,
+                    valueT value,
+                    fragS *frag)
+{
+  const char *name;
+  switch (state)
+    {
+    case MAP_DATA:
+      name = "$d";
+      break;
+    case MAP_INSN:
+      name = "$x";
+      break;
+    default:
+      abort ();
+    }
+
+  symbolS *symbol = symbol_new (name, now_seg, frag, value);
+  symbol_get_bfdsym (symbol)->flags |= (BSF_NO_FLAGS | BSF_LOCAL);
+
+  /* If .fill or other data filling directive generates zero sized data,
+     or we are adding odd alignemnts, then the mapping symbol for the
+     following code will have the same value.  */
+  if (value == 0)
+    {
+       if (frag->tc_frag_data.first_map_symbol != NULL)
+       {
+         know (S_GET_VALUE (frag->tc_frag_data.first_map_symbol)
+               == S_GET_VALUE (symbol));
+         /* Remove the old one.  */
+         symbol_remove (frag->tc_frag_data.first_map_symbol,
+                        &symbol_rootP, &symbol_lastP);
+       }
+      frag->tc_frag_data.first_map_symbol = symbol;
+    }
+  if (frag->tc_frag_data.last_map_symbol != NULL)
+    {
+      /* The mapping symbols should be added in offset order.  */
+      know (S_GET_VALUE (frag->tc_frag_data.last_map_symbol)
+                        <= S_GET_VALUE (symbol));
+      /* Remove the old one.  */
+      if (S_GET_VALUE (frag->tc_frag_data.last_map_symbol)
+         == S_GET_VALUE (symbol))
+       symbol_remove (frag->tc_frag_data.last_map_symbol,
+                      &symbol_rootP, &symbol_lastP);
+    }
+  frag->tc_frag_data.last_map_symbol = symbol;
+}
+
+/* Set the mapping state for frag_now.  */
+
+void
+riscv_mapping_state (enum riscv_seg_mstate to_state,
+                    int max_chars)
+{
+  enum riscv_seg_mstate from_state =
+       seg_info (now_seg)->tc_segment_info_data.map_state;
+
+  if (!SEG_NORMAL (now_seg)
+      /* For now I only add the mapping symbols to text sections.
+        Therefore, the dis-assembler only show the actual contents
+        distribution for text.  Other sections will be shown as
+        data without the details.  */
+      || !subseg_text_p (now_seg))
+    return;
+
+  /* The mapping symbol should be emitted if not in the right
+     mapping state  */
+  if (from_state == to_state)
+    return;
+
+  valueT value = (valueT) (frag_now_fix () - max_chars);
+  seg_info (now_seg)->tc_segment_info_data.map_state = to_state;
+  make_mapping_symbol (to_state, value, frag_now);
+}
+
+/* Add the odd bytes of paddings for riscv_handle_align.  */
+
+static void
+riscv_add_odd_padding_symbol (fragS *frag)
+{
+  /* If there was already a mapping symbol, it should be
+     removed in the make_mapping_symbol.  */
+  make_mapping_symbol (MAP_DATA, frag->fr_fix, frag);
+  make_mapping_symbol (MAP_INSN, frag->fr_fix + 1, frag);
+}
+
+/* Remove any excess mapping symbols generated for alignment frags in
+   SEC.  We may have created a mapping symbol before a zero byte
+   alignment; remove it if there's a mapping symbol after the
+   alignment.  */
+
+static void
+riscv_check_mapping_symbols (bfd *abfd ATTRIBUTE_UNUSED,
+                            asection *sec,
+                            void *dummy ATTRIBUTE_UNUSED)
+{
+  segment_info_type *seginfo = seg_info (sec);
+  fragS *fragp;
+
+  if (seginfo == NULL || seginfo->frchainP == NULL)
+    return;
+
+  for (fragp = seginfo->frchainP->frch_root;
+       fragp != NULL;
+       fragp = fragp->fr_next)
+    {
+      symbolS *last = fragp->tc_frag_data.last_map_symbol;
+      fragS *next = fragp->fr_next;
+
+      if (last == NULL || next == NULL)
+       continue;
+
+      /* Check the last mapping symbol if it is at the boundary of
+        fragment.  */
+      if (S_GET_VALUE (last) < next->fr_address)
+       continue;
+      know (S_GET_VALUE (last) == next->fr_address);
+
+      do
+       {
+         if (next->tc_frag_data.first_map_symbol != NULL)
+           {
+             /* The last mapping symbol overlaps with another one
+                which at the start of the next frag.  */
+             symbol_remove (last, &symbol_rootP, &symbol_lastP);
+             break;
+           }
+
+         if (next->fr_next == NULL)
+           {
+             /* The last mapping symbol is at the end of the section.  */
+             know (next->fr_fix == 0 && next->fr_var == 0);
+             symbol_remove (last, &symbol_rootP, &symbol_lastP);
+             break;
+           }
+
+         /* Since we may have empty frags without any mapping symbols,
+            keep looking until the non-empty frag.  */
+         if (next->fr_address != next->fr_next->fr_address)
+           break;
+
+         next = next->fr_next;
+       }
+      while (next != NULL);
+    }
+}
 
 /* The default target format to use.  */
 
 const char *
 riscv_target_format (void)
 {
-  return xlen == 64 ? "elf64-littleriscv" : "elf32-littleriscv";
+  if (target_big_endian)
+    return xlen == 64 ? "elf64-bigriscv" : "elf32-bigriscv";
+  else
+    return xlen == 64 ? "elf64-littleriscv" : "elf32-littleriscv";
 }
 
 /* Return the length of instruction INSN.  */
@@ -249,7 +644,7 @@ static void
 install_insn (const struct riscv_cl_insn *insn)
 {
   char *f = insn->frag->fr_literal + insn->where;
-  md_number_to_chars (f, insn->insn_opcode, insn_length (insn));
+  number_to_chars_littleendian (f, insn->insn_opcode, insn_length (insn));
 }
 
 /* Move INSN to offset WHERE in FRAG.  Adjust the fixups accordingly
@@ -365,7 +760,7 @@ static const struct opcode_name_t opcode_name_list[] =
   {"NMADD",     0x4f},
   {"NMSUB",     0x4b},
   {"OP_FP",     0x53},
-  /*reserved    0x57.  */
+  {"OP_V",      0x57},
   {"CUSTOM_2",  0x5b},
   /* 48b        0x5f.  */
 
@@ -382,27 +777,23 @@ static const struct opcode_name_t opcode_name_list[] =
 };
 
 /* Hash table for lookup opcode name.  */
-static struct hash_control *opcode_names_hash = NULL;
+static htab_t opcode_names_hash = NULL;
 
 /* Initialization for hash table of opcode name.  */
+
 static void
 init_opcode_names_hash (void)
 {
-  const char *retval;
   const struct opcode_name_t *opcode;
 
   for (opcode = &opcode_name_list[0]; opcode->name != NULL; ++opcode)
-    {
-      retval = hash_insert (opcode_names_hash, opcode->name, (void *)opcode);
-
-      if (retval != NULL)
-       as_fatal (_("internal error: can't hash `%s': %s"),
-                 opcode->name, retval);
-    }
+    if (str_hash_insert (opcode_names_hash, opcode->name, opcode, 0) != NULL)
+      as_fatal (_("internal: duplicate %s"), opcode->name);
 }
 
-/* Find `s` is a valid opcode name or not,
-   return the opcode name info if found.  */
+/* Find `s` is a valid opcode name or not, return the opcode name info
+   if found.  */
+
 static const struct opcode_name_t *
 opcode_name_lookup (char **s)
 {
@@ -421,7 +812,7 @@ opcode_name_lookup (char **s)
   save_c = *e;
   *e = '\0';
 
-  o = (struct opcode_name_t *) hash_find (opcode_names_hash, *s);
+  o = (struct opcode_name_t *) str_hash_find (opcode_names_hash, *s);
 
   /* Advance to next token if one was recognized.  */
   if (o)
@@ -433,21 +824,20 @@ opcode_name_lookup (char **s)
   return o;
 }
 
-struct regname
-{
-  const char *name;
-  unsigned int num;
-};
-
+/* All RISC-V registers belong to one of these classes.  */
 enum reg_class
 {
   RCLASS_GPR,
   RCLASS_FPR,
-  RCLASS_CSR,
-  RCLASS_MAX
+  RCLASS_VECR,
+  RCLASS_VECM,
+  RCLASS_MAX,
+
+  RCLASS_CSR
 };
 
-static struct hash_control *reg_names_hash = NULL;
+static htab_t reg_names_hash = NULL;
+static htab_t csr_extra_hash = NULL;
 
 #define ENCODE_REG_HASH(cls, n) \
   ((void *)(uintptr_t)((n) * RCLASS_MAX + (cls) + 1))
@@ -458,10 +848,8 @@ static void
 hash_reg_name (enum reg_class class, const char *name, unsigned n)
 {
   void *hash = ENCODE_REG_HASH (class, n);
-  const char *retval = hash_insert (reg_names_hash, name, hash);
-
-  if (retval != NULL)
-    as_fatal (_("internal error: can't hash `%s': %s"), name, retval);
+  if (str_hash_insert (reg_names_hash, name, hash, 0) != NULL)
+    as_fatal (_("internal: duplicate %s"), name);
 }
 
 static void
@@ -473,21 +861,200 @@ hash_reg_names (enum reg_class class, const char * const names[], unsigned n)
     hash_reg_name (class, names[i], i);
 }
 
+/* Init hash table csr_extra_hash to handle CSR.  */
+
+static void
+riscv_init_csr_hash (const char *name,
+                    unsigned address,
+                    enum riscv_csr_class class,
+                    enum riscv_spec_class define_version,
+                    enum riscv_spec_class abort_version)
+{
+  struct riscv_csr_extra *entry, *pre_entry;
+  bool need_enrty = true;
+
+  pre_entry = NULL;
+  entry = (struct riscv_csr_extra *) str_hash_find (csr_extra_hash, name);
+  while (need_enrty && entry != NULL)
+    {
+      if (entry->csr_class == class
+         && entry->address == address
+         && entry->define_version == define_version
+         && entry->abort_version == abort_version)
+       need_enrty = false;
+      pre_entry = entry;
+      entry = entry->next;
+    }
+
+  /* Duplicate CSR.  */
+  if (!need_enrty)
+    return;
+
+  entry = notes_alloc (sizeof (*entry));
+  entry->csr_class = class;
+  entry->address = address;
+  entry->define_version = define_version;
+  entry->abort_version = abort_version;
+  entry->next = NULL;
+
+  if (pre_entry == NULL)
+    str_hash_insert (csr_extra_hash, name, entry, 0);
+  else
+    pre_entry->next = entry;
+}
+
+/* Return the CSR address after checking the ISA dependency and
+   the privileged spec version.
+
+   There are one warning and two errors for CSR,
+
+   Invalid CSR: the CSR was defined, but isn't allowed for the current ISA
+   or the privileged spec, report warning only if -mcsr-check is set.
+   Unknown CSR: the CSR has never been defined, report error.
+   Improper CSR: the CSR number over the range (> 0xfff), report error.  */
+
+static unsigned int
+riscv_csr_address (const char *csr_name,
+                  struct riscv_csr_extra *entry)
+{
+  struct riscv_csr_extra *saved_entry = entry;
+  enum riscv_csr_class csr_class = entry->csr_class;
+  bool need_check_version = false;
+  bool is_rv32_only = false;
+  bool is_h_required = false;
+  const char* extension = NULL;
+
+  switch (csr_class)
+    {
+    case CSR_CLASS_I_32:
+      is_rv32_only = true;
+      /* Fall through.  */
+    case CSR_CLASS_I:
+      need_check_version = true;
+      extension = "i";
+      break;
+    case CSR_CLASS_H_32:
+      is_rv32_only = true;
+      /* Fall through.  */
+    case CSR_CLASS_H:
+      extension = "h";
+      break;
+    case CSR_CLASS_F:
+      extension = "f";
+      break;
+    case CSR_CLASS_ZKR:
+      extension = "zkr";
+      break;
+    case CSR_CLASS_V:
+      extension = "zve32x";
+      break;
+    case CSR_CLASS_SMSTATEEN:
+    case CSR_CLASS_SMSTATEEN_AND_H:
+    case CSR_CLASS_SMSTATEEN_32:
+    case CSR_CLASS_SMSTATEEN_AND_H_32:
+      is_rv32_only = (csr_class == CSR_CLASS_SMSTATEEN_32
+                     || csr_class == CSR_CLASS_SMSTATEEN_AND_H_32);
+      is_h_required = (csr_class == CSR_CLASS_SMSTATEEN_AND_H
+                     || csr_class == CSR_CLASS_SMSTATEEN_AND_H_32);
+      extension = "smstateen";
+      break;
+    case CSR_CLASS_SSCOFPMF_32:
+      is_rv32_only = true;
+      /* Fall through.  */
+    case CSR_CLASS_SSCOFPMF:
+      extension = "sscofpmf";
+      break;
+    case CSR_CLASS_SSTC:
+    case CSR_CLASS_SSTC_AND_H:
+    case CSR_CLASS_SSTC_32:
+    case CSR_CLASS_SSTC_AND_H_32:
+      is_rv32_only = (csr_class == CSR_CLASS_SSTC_32
+                     || csr_class == CSR_CLASS_SSTC_AND_H_32);
+      is_h_required = (csr_class == CSR_CLASS_SSTC_AND_H
+                     || csr_class == CSR_CLASS_SSTC_AND_H_32);
+      extension = "sstc";
+      break;
+    case CSR_CLASS_DEBUG:
+      break;
+    default:
+      as_bad (_("internal: bad RISC-V CSR class (0x%x)"), csr_class);
+    }
+
+  if (riscv_opts.csr_check)
+    {
+      if (is_rv32_only && xlen != 32)
+       as_warn (_("invalid CSR `%s', needs rv32i extension"), csr_name);
+      if (is_h_required && !riscv_subset_supports (&riscv_rps_as, "h"))
+       as_warn (_("invalid CSR `%s', needs `h' extension"), csr_name);
+
+      if (extension != NULL
+         && !riscv_subset_supports (&riscv_rps_as, extension))
+       as_warn (_("invalid CSR `%s', needs `%s' extension"),
+                csr_name, extension);
+    }
+
+  while (entry != NULL)
+    {
+      if (!need_check_version
+         || (default_priv_spec >= entry->define_version
+             && default_priv_spec < entry->abort_version))
+       {
+         /* Find the CSR according to the specific version.  */
+         return entry->address;
+       }
+      entry = entry->next;
+    }
+
+  /* Can not find the CSR address from the chosen privileged version,
+     so use the newly defined value.  */
+  if (riscv_opts.csr_check)
+    {
+      const char *priv_name = NULL;
+      RISCV_GET_PRIV_SPEC_NAME (priv_name, default_priv_spec);
+      if (priv_name != NULL)
+       as_warn (_("invalid CSR `%s' for the privileged spec `%s'"),
+                csr_name, priv_name);
+    }
+
+  return saved_entry->address;
+}
+
+/* Return -1 if the CSR has never been defined.  Otherwise, return
+   the address.  */
+
+static unsigned int
+reg_csr_lookup_internal (const char *s)
+{
+  struct riscv_csr_extra *r =
+    (struct riscv_csr_extra *) str_hash_find (csr_extra_hash, s);
+
+  if (r == NULL)
+    return -1U;
+
+  return riscv_csr_address (s, r);
+}
+
 static unsigned int
 reg_lookup_internal (const char *s, enum reg_class class)
 {
-  struct regname *r = (struct regname *) hash_find (reg_names_hash, s);
+  void *r;
+
+  if (class == RCLASS_CSR)
+    return reg_csr_lookup_internal (s);
 
+  r = str_hash_find (reg_names_hash, s);
   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 (&riscv_rps_as, "e")
+      && class == RCLASS_GPR
+      && DECODE_REG_NUM (r) > 15)
     return -1;
 
   return DECODE_REG_NUM (r);
 }
 
-static bfd_boolean
+static bool
 reg_lookup (char **s, enum reg_class class, unsigned int *regnop)
 {
   char *e;
@@ -515,36 +1082,36 @@ reg_lookup (char **s, enum reg_class class, unsigned int *regnop)
   return reg >= 0;
 }
 
-static bfd_boolean
+static bool
 arg_lookup (char **s, const char *const *array, size_t size, unsigned *regnop)
 {
   const char *p = strchr (*s, ',');
   size_t i, len = p ? (size_t)(p - *s) : strlen (*s);
 
   if (len == 0)
-    return FALSE;
+    return false;
 
   for (i = 0; i < size; i++)
     if (array[i] != NULL && strncmp (array[i], *s, len) == 0)
       {
        *regnop = i;
        *s += len;
-       return TRUE;
+       return true;
       }
 
-  return FALSE;
+  return false;
 }
 
+#define USE_BITS(mask,shift) (used_bits |= ((insn_t)(mask) << (shift)))
+
 /* For consistency checking, verify that all bits are specified either
    by the match/mask part of the instruction definition, or by the
-   operand list.
+   operand list. The `length` could be 0, 4 or 8, 0 for auto detection.  */
 
-   `length` could be 0, 4 or 8, 0 for auto detection.  */
-static bfd_boolean
+static bool
 validate_riscv_insn (const struct riscv_opcode *opc, int length)
 {
-  const char *p = opc->args;
-  char c;
+  const char *oparg, *opargStart;
   insn_t used_bits = opc->mask;
   int insn_width;
   insn_t required_bits;
@@ -560,167 +1127,186 @@ validate_riscv_insn (const struct riscv_opcode *opc, int length)
     {
       as_bad (_("internal: bad RISC-V opcode (mask error): %s %s"),
              opc->name, opc->args);
-      return FALSE;
+      return false;
     }
 
-#define USE_BITS(mask,shift)   (used_bits |= ((insn_t)(mask) << (shift)))
-  while (*p)
-    switch (c = *p++)
-      {
-      case 'C': /* RVC */
-       switch (c = *p++)
-         {
-         case 'a': used_bits |= ENCODE_RVC_J_IMM (-1U); break;
-         case 'c': break; /* RS1, constrained to equal sp */
-         case 'i': used_bits |= ENCODE_RVC_SIMM3(-1U); break;
-         case 'j': used_bits |= ENCODE_RVC_IMM (-1U); break;
-         case 'o': used_bits |= ENCODE_RVC_IMM (-1U); break;
-         case 'k': used_bits |= ENCODE_RVC_LW_IMM (-1U); break;
-         case 'l': used_bits |= ENCODE_RVC_LD_IMM (-1U); break;
-         case 'm': used_bits |= ENCODE_RVC_LWSP_IMM (-1U); break;
-         case 'n': used_bits |= ENCODE_RVC_LDSP_IMM (-1U); break;
-         case 'p': used_bits |= ENCODE_RVC_B_IMM (-1U); break;
-         case 's': USE_BITS (OP_MASK_CRS1S, OP_SH_CRS1S); break;
-         case 't': USE_BITS (OP_MASK_CRS2S, OP_SH_CRS2S); break;
-         case 'u': used_bits |= ENCODE_RVC_IMM (-1U); break;
-         case 'v': used_bits |= ENCODE_RVC_IMM (-1U); break;
-         case 'w': break; /* RS1S, constrained to equal RD */
-         case 'x': break; /* RS2S, constrained to equal RD */
-         case 'z': break; /* RS2S, contrained to be x0 */
-         case 'K': used_bits |= ENCODE_RVC_ADDI4SPN_IMM (-1U); break;
-         case 'L': used_bits |= ENCODE_RVC_ADDI16SP_IMM (-1U); break;
-         case 'M': used_bits |= ENCODE_RVC_SWSP_IMM (-1U); break;
-         case 'N': used_bits |= ENCODE_RVC_SDSP_IMM (-1U); break;
-         case 'U': break; /* RS1, constrained to equal RD */
-         case 'V': USE_BITS (OP_MASK_CRS2, OP_SH_CRS2); break;
-         case '<': used_bits |= ENCODE_RVC_IMM (-1U); break;
-         case '>': used_bits |= ENCODE_RVC_IMM (-1U); break;
-         case '8': used_bits |= ENCODE_RVC_UIMM8 (-1U); break;
-         case 'S': USE_BITS (OP_MASK_CRS1S, OP_SH_CRS1S); break;
-         case 'T': USE_BITS (OP_MASK_CRS2, OP_SH_CRS2); break;
-         case 'D': USE_BITS (OP_MASK_CRS2S, OP_SH_CRS2S); break;
-         case 'F': /* funct */
-           switch (c = *p++)
-             {
+  for (oparg = opc->args; *oparg; ++oparg)
+    {
+      opargStart = oparg;
+      switch (*oparg)
+       {
+       case 'C': /* RVC */
+         switch (*++oparg)
+           {
+           case 'U': break; /* CRS1, constrained to equal RD.  */
+           case 'c': break; /* CRS1, constrained to equal sp.  */
+           case 'T': /* CRS2, floating point.  */
+           case 'V': USE_BITS (OP_MASK_CRS2, OP_SH_CRS2); break;
+           case 'S': /* CRS1S, floating point.  */
+           case 's': USE_BITS (OP_MASK_CRS1S, OP_SH_CRS1S); break;
+           case 'w': break; /* CRS1S, constrained to equal RD.  */
+           case 'D': /* CRS2S, floating point.  */
+           case 't': USE_BITS (OP_MASK_CRS2S, OP_SH_CRS2S); break;
+           case 'x': break; /* CRS2S, constrained to equal RD.  */
+           case 'z': break; /* CRS2S, constrained to be x0.  */
+           case '>': /* CITYPE immediate, compressed shift.  */
+           case 'u': /* CITYPE immediate, compressed lui.  */
+           case 'v': /* CITYPE immediate, li to compressed lui.  */
+           case 'o': /* CITYPE immediate, allow zero.  */
+           case 'j': used_bits |= ENCODE_CITYPE_IMM (-1U); break;
+           case 'L': used_bits |= ENCODE_CITYPE_ADDI16SP_IMM (-1U); break;
+           case 'm': used_bits |= ENCODE_CITYPE_LWSP_IMM (-1U); break;
+           case 'n': used_bits |= ENCODE_CITYPE_LDSP_IMM (-1U); break;
+           case '6': used_bits |= ENCODE_CSSTYPE_IMM (-1U); break;
+           case 'M': used_bits |= ENCODE_CSSTYPE_SWSP_IMM (-1U); break;
+           case 'N': used_bits |= ENCODE_CSSTYPE_SDSP_IMM (-1U); break;
+           case '8': used_bits |= ENCODE_CIWTYPE_IMM (-1U); break;
+           case 'K': used_bits |= ENCODE_CIWTYPE_ADDI4SPN_IMM (-1U); break;
+           /* CLTYPE and CSTYPE have the same immediate encoding.  */
+           case '5': used_bits |= ENCODE_CLTYPE_IMM (-1U); break;
+           case 'k': used_bits |= ENCODE_CLTYPE_LW_IMM (-1U); break;
+           case 'l': used_bits |= ENCODE_CLTYPE_LD_IMM (-1U); break;
+           case 'p': used_bits |= ENCODE_CBTYPE_IMM (-1U); break;
+           case 'a': used_bits |= ENCODE_CJTYPE_IMM (-1U); break;
+           case 'F': /* Compressed funct for .insn directive.  */
+             switch (*++oparg)
+               {
                case '6': USE_BITS (OP_MASK_CFUNCT6, OP_SH_CFUNCT6); break;
                case '4': USE_BITS (OP_MASK_CFUNCT4, OP_SH_CFUNCT4); break;
                case '3': USE_BITS (OP_MASK_CFUNCT3, OP_SH_CFUNCT3); break;
                case '2': USE_BITS (OP_MASK_CFUNCT2, OP_SH_CFUNCT2); break;
                default:
-                 as_bad (_("internal: bad RISC-V opcode"
-                           " (unknown operand type `CF%c'): %s %s"),
-                         c, opc->name, opc->args);
-                 return FALSE;
-             }
-           break;
-         default:
-           as_bad (_("internal: bad RISC-V opcode (unknown operand type `C%c'): %s %s"),
-                   c, opc->name, opc->args);
-           return FALSE;
-         }
-       break;
-      case ',': break;
-      case '(': break;
-      case ')': break;
-      case '<': USE_BITS (OP_MASK_SHAMTW,      OP_SH_SHAMTW);  break;
-      case '>':        USE_BITS (OP_MASK_SHAMT,        OP_SH_SHAMT);   break;
-      case 'A': break;
-      case 'D':        USE_BITS (OP_MASK_RD,           OP_SH_RD);      break;
-      case 'Z':        USE_BITS (OP_MASK_RS1,          OP_SH_RS1);     break;
-      case 'E':        USE_BITS (OP_MASK_CSR,          OP_SH_CSR);     break;
-      case 'I': break;
-      case 'R':        USE_BITS (OP_MASK_RS3,          OP_SH_RS3);     break;
-      case 'S':        USE_BITS (OP_MASK_RS1,          OP_SH_RS1);     break;
-      case 'U':        USE_BITS (OP_MASK_RS1,          OP_SH_RS1);     /* fallthru */
-      case 'T':        USE_BITS (OP_MASK_RS2,          OP_SH_RS2);     break;
-      case 'd':        USE_BITS (OP_MASK_RD,           OP_SH_RD);      break;
-      case 'm':        USE_BITS (OP_MASK_RM,           OP_SH_RM);      break;
-      case 's':        USE_BITS (OP_MASK_RS1,          OP_SH_RS1);     break;
-      case 't':        USE_BITS (OP_MASK_RS2,          OP_SH_RS2);     break;
-      case 'r':        USE_BITS (OP_MASK_RS3,          OP_SH_RS3);     break;
-      case 'P':        USE_BITS (OP_MASK_PRED,         OP_SH_PRED); break;
-      case 'Q':        USE_BITS (OP_MASK_SUCC,         OP_SH_SUCC); break;
-      case 'o':
-      case 'j': used_bits |= ENCODE_ITYPE_IMM (-1U); break;
-      case 'a':        used_bits |= ENCODE_UJTYPE_IMM (-1U); break;
-      case 'p':        used_bits |= ENCODE_SBTYPE_IMM (-1U); break;
-      case 'q':        used_bits |= ENCODE_STYPE_IMM (-1U); break;
-      case 'u':        used_bits |= ENCODE_UTYPE_IMM (-1U); break;
-      case 'z': break;
-      case '[': break;
-      case ']': break;
-      case '0': break;
-      case '1': break;
-      case 'F': /* funct */
-       switch (c = *p++)
-         {
-           case '7': USE_BITS (OP_MASK_FUNCT7, OP_SH_FUNCT7); break;
-           case '3': USE_BITS (OP_MASK_FUNCT3, OP_SH_FUNCT3); break;
-           case '2': USE_BITS (OP_MASK_FUNCT2, OP_SH_FUNCT2); break;
+                 goto unknown_validate_operand;
+               }
+             break;
            default:
-             as_bad (_("internal: bad RISC-V opcode"
-                       " (unknown operand type `F%c'): %s %s"),
-                     c, opc->name, opc->args);
-           return FALSE;
-         }
-       break;
-      case 'O': /* opcode */
-       switch (c = *p++)
-         {
-           case '4': USE_BITS (OP_MASK_OP, OP_SH_OP); break;
-           case '2': USE_BITS (OP_MASK_OP2, OP_SH_OP2); break;
+             goto unknown_validate_operand;
+           }
+         break;  /* end RVC */
+       case 'V': /* RVV */
+         switch (*++oparg)
+           {
+           case 'd':
+           case 'f': USE_BITS (OP_MASK_VD, OP_SH_VD); break;
+           case 'e': USE_BITS (OP_MASK_VWD, OP_SH_VWD); break;
+           case 's': USE_BITS (OP_MASK_VS1, OP_SH_VS1); break;
+           case 't': USE_BITS (OP_MASK_VS2, OP_SH_VS2); break;
+           case 'u': USE_BITS (OP_MASK_VS1, OP_SH_VS1);
+                     USE_BITS (OP_MASK_VS2, OP_SH_VS2); break;
+           case 'v': USE_BITS (OP_MASK_VD, OP_SH_VD);
+                     USE_BITS (OP_MASK_VS1, OP_SH_VS1);
+                     USE_BITS (OP_MASK_VS2, OP_SH_VS2); break;
+           case '0': break;
+           case 'b': used_bits |= ENCODE_RVV_VB_IMM (-1U); break;
+           case 'c': used_bits |= ENCODE_RVV_VC_IMM (-1U); break;
+           case 'i':
+           case 'j':
+           case 'k': USE_BITS (OP_MASK_VIMM, OP_SH_VIMM); break;
+           case 'm': USE_BITS (OP_MASK_VMASK, OP_SH_VMASK); break;
            default:
-             as_bad (_("internal: bad RISC-V opcode"
-                       " (unknown operand type `F%c'): %s %s"),
-                     c, opc->name, opc->args);
-            return FALSE;
-         }
-       break;
-      default:
-       as_bad (_("internal: bad RISC-V opcode "
-                 "(unknown operand type `%c'): %s %s"),
-               c, opc->name, opc->args);
-       return FALSE;
-      }
-#undef USE_BITS
+             goto unknown_validate_operand;
+           }
+         break; /* end RVV */
+       case ',': break;
+       case '(': break;
+       case ')': break;
+       case '<': USE_BITS (OP_MASK_SHAMTW, OP_SH_SHAMTW); break;
+       case '>': USE_BITS (OP_MASK_SHAMT, OP_SH_SHAMT); break;
+       case 'A': break; /* Macro operand, must be symbol.  */
+       case 'B': break; /* Macro operand, must be symbol or constant.  */
+       case 'I': break; /* Macro operand, must be constant.  */
+       case 'D': /* RD, floating point.  */
+       case 'd': USE_BITS (OP_MASK_RD, OP_SH_RD); break;
+       case 'y': USE_BITS (OP_MASK_BS, OP_SH_BS); break;
+       case 'Y': USE_BITS (OP_MASK_RNUM, OP_SH_RNUM); break;
+       case 'Z': /* RS1, CSR number.  */
+       case 'S': /* RS1, floating point.  */
+       case 's': USE_BITS (OP_MASK_RS1, OP_SH_RS1); break;
+       case 'U': /* RS1 and RS2 are the same, floating point.  */
+         USE_BITS (OP_MASK_RS1, OP_SH_RS1);
+         /* Fall through.  */
+       case 'T': /* RS2, floating point.  */
+       case 't': USE_BITS (OP_MASK_RS2, OP_SH_RS2); break;
+       case 'R': /* RS3, floating point.  */
+       case 'r': USE_BITS (OP_MASK_RS3, OP_SH_RS3); break;
+       case 'm': USE_BITS (OP_MASK_RM, OP_SH_RM); break;
+       case 'E': USE_BITS (OP_MASK_CSR, OP_SH_CSR); break;
+       case 'P': USE_BITS (OP_MASK_PRED, OP_SH_PRED); break;
+       case 'Q': USE_BITS (OP_MASK_SUCC, OP_SH_SUCC); break;
+       case 'o': /* ITYPE immediate, load displacement.  */
+       case 'j': used_bits |= ENCODE_ITYPE_IMM (-1U); break;
+       case 'a': used_bits |= ENCODE_JTYPE_IMM (-1U); break;
+       case 'p': used_bits |= ENCODE_BTYPE_IMM (-1U); break;
+       case 'f': /* Fall through.  */
+       case 'q': used_bits |= ENCODE_STYPE_IMM (-1U); break;
+       case 'u': used_bits |= ENCODE_UTYPE_IMM (-1U); break;
+       case 'z': break; /* Zero immediate.  */
+       case '[': break; /* Unused operand.  */
+       case ']': break; /* Unused operand.  */
+       case '0': break; /* AMO displacement, must to zero.  */
+       case '1': break; /* Relaxation operand.  */
+       case 'F': /* Funct for .insn directive.  */
+         switch (*++oparg)
+           {
+             case '7': USE_BITS (OP_MASK_FUNCT7, OP_SH_FUNCT7); break;
+             case '3': USE_BITS (OP_MASK_FUNCT3, OP_SH_FUNCT3); break;
+             case '2': USE_BITS (OP_MASK_FUNCT2, OP_SH_FUNCT2); break;
+             default:
+               goto unknown_validate_operand;
+           }
+         break;
+       case 'O': /* Opcode for .insn directive.  */
+         switch (*++oparg)
+           {
+             case '4': USE_BITS (OP_MASK_OP, OP_SH_OP); break;
+             case '2': USE_BITS (OP_MASK_OP2, OP_SH_OP2); break;
+             default:
+               goto unknown_validate_operand;
+           }
+         break;
+       default:
+       unknown_validate_operand:
+         as_bad (_("internal: bad RISC-V opcode "
+                   "(unknown operand type `%s'): %s %s"),
+                 opargStart, opc->name, opc->args);
+         return false;
+       }
+    }
+
   if (used_bits != required_bits)
     {
-      as_bad (_("internal: bad RISC-V opcode (bits 0x%lx undefined): %s %s"),
+      as_bad (_("internal: bad RISC-V opcode "
+               "(bits 0x%lx undefined): %s %s"),
              ~(unsigned long)(used_bits & required_bits),
              opc->name, opc->args);
-      return FALSE;
+      return false;
     }
-  return TRUE;
+  return true;
 }
 
+#undef USE_BITS
+
 struct percent_op_match
 {
   const char *str;
   bfd_reloc_code_real_type reloc;
 };
 
-/* Common hash table initialization function for
-   instruction and .insn directive.  */
-static struct hash_control *
+/* Common hash table initialization function for instruction and .insn
+   directive.  */
+
+static htab_t
 init_opcode_hash (const struct riscv_opcode *opcodes,
-                 bfd_boolean insn_directive_p)
+                 bool insn_directive_p)
 {
   int i = 0;
   int length;
-  struct hash_control *hash = hash_new ();
+  htab_t hash = str_htab_create ();
   while (opcodes[i].name)
     {
       const char *name = opcodes[i].name;
-      const char *hash_error =
-       hash_insert (hash, name, (void *) &opcodes[i]);
-
-      if (hash_error)
-       {
-         fprintf (stderr, _("internal error: can't hash `%s': %s\n"),
-                  opcodes[i].name, hash_error);
-         /* Probably a memory allocation problem?  Give up now.  */
-         as_fatal (_("Broken assembler.  No assembly attempted."));
-       }
+      if (str_hash_insert (hash, name, &opcodes[i], 0) != NULL)
+       as_fatal (_("internal: duplicate %s"), name);
 
       do
        {
@@ -729,9 +1315,10 @@ init_opcode_hash (const struct riscv_opcode *opcodes,
              if (insn_directive_p)
                length = ((name[0] == 'c') ? 2 : 4);
              else
-               length = 0; /* Let assembler determine the length. */
+               length = 0; /* Let assembler determine the length.  */
              if (!validate_riscv_insn (&opcodes[i], length))
-               as_fatal (_("Broken assembler.  No assembly attempted."));
+               as_fatal (_("internal: broken assembler.  "
+                           "No assembly attempted"));
            }
          else
            gas_assert (!insn_directive_p);
@@ -752,28 +1339,33 @@ md_begin (void)
   unsigned long mach = xlen == 64 ? bfd_mach_riscv64 : bfd_mach_riscv32;
 
   if (! bfd_set_arch_mach (stdoutput, bfd_arch_riscv, mach))
-    as_warn (_("Could not set architecture and machine"));
+    as_warn (_("could not set architecture and machine"));
 
-  op_hash = init_opcode_hash (riscv_opcodes, FALSE);
-  insn_type_hash = init_opcode_hash (riscv_insn_types, TRUE);
+  op_hash = init_opcode_hash (riscv_opcodes, false);
+  insn_type_hash = init_opcode_hash (riscv_insn_types, true);
 
-  reg_names_hash = hash_new ();
+  reg_names_hash = str_htab_create ();
   hash_reg_names (RCLASS_GPR, riscv_gpr_names_numeric, NGPR);
   hash_reg_names (RCLASS_GPR, riscv_gpr_names_abi, NGPR);
   hash_reg_names (RCLASS_FPR, riscv_fpr_names_numeric, NFPR);
   hash_reg_names (RCLASS_FPR, riscv_fpr_names_abi, NFPR);
-
+  hash_reg_names (RCLASS_VECR, riscv_vecr_names_numeric, NVECR);
+  hash_reg_names (RCLASS_VECM, riscv_vecm_names_numeric, NVECM);
   /* Add "fp" as an alias for "s0".  */
   hash_reg_name (RCLASS_GPR, "fp", 8);
 
-  opcode_names_hash = hash_new ();
-  init_opcode_names_hash ();
-
-#define DECLARE_CSR(name, num) hash_reg_name (RCLASS_CSR, #name, num);
-#define DECLARE_CSR_ALIAS(name, num) DECLARE_CSR(name, num);
+  /* Create and insert CSR hash tables.  */
+  csr_extra_hash = str_htab_create ();
+#define DECLARE_CSR(name, num, class, define_version, abort_version) \
+  riscv_init_csr_hash (#name, num, class, define_version, abort_version);
+#define DECLARE_CSR_ALIAS(name, num, class, define_version, abort_version) \
+  DECLARE_CSR(name, num, class, define_version, abort_version);
 #include "opcode/riscv-opc.h"
 #undef DECLARE_CSR
 
+  opcode_names_hash = str_htab_create ();
+  init_opcode_names_hash ();
+
   /* Set the default alignment for the text section.  */
   record_alignment (text_section, riscv_opts.rvc ? 1 : 2);
 }
@@ -821,6 +1413,13 @@ append_insn (struct riscv_cl_insn *ip, expressionS *address_expr,
          int j = reloc_type == BFD_RELOC_RISCV_JMP;
          int best_case = riscv_insn_length (ip->insn_opcode);
          unsigned worst_case = relaxed_branch_length (NULL, NULL, 0);
+
+         if (now_seg == absolute_section)
+           {
+             as_bad (_("relaxable branches not supported in absolute section"));
+             return;
+           }
+
          add_relaxed_insn (ip, worst_case, best_case,
                            RELAX_BRANCH_ENCODE (j, best_case == 2, worst_case),
                            address_expr->X_add_symbol,
@@ -831,11 +1430,12 @@ append_insn (struct riscv_cl_insn *ip, expressionS *address_expr,
        {
          howto = bfd_reloc_type_lookup (stdoutput, reloc_type);
          if (howto == NULL)
-           as_bad (_("Unsupported RISC-V relocation number %d"), reloc_type);
+           as_bad (_("internal: unsupported RISC-V relocation number %d"),
+                   reloc_type);
 
          ip->fixp = fix_new_exp (ip->frag, ip->where,
                                  bfd_get_reloc_size (howto),
-                                 address_expr, FALSE, reloc_type);
+                                 address_expr, false, reloc_type);
 
          ip->fixp->fx_tcbit = riscv_opts.relax;
        }
@@ -848,9 +1448,7 @@ append_insn (struct riscv_cl_insn *ip, expressionS *address_expr,
      optimized away or compressed by the linker during relaxation, to prevent
      the assembler from computing static offsets across such an instruction.
      This is necessary to get correct EH info.  */
-  if (reloc_type == BFD_RELOC_RISCV_CALL
-      || reloc_type == BFD_RELOC_RISCV_CALL_PLT
-      || reloc_type == BFD_RELOC_RISCV_HI20
+  if (reloc_type == BFD_RELOC_RISCV_HI20
       || reloc_type == BFD_RELOC_RISCV_PCREL_HI20
       || reloc_type == BFD_RELOC_RISCV_TPREL_HI20
       || reloc_type == BFD_RELOC_RISCV_TPREL_ADD)
@@ -861,9 +1459,9 @@ append_insn (struct riscv_cl_insn *ip, expressionS *address_expr,
 }
 
 /* Build an instruction created by a macro expansion.  This is passed
-   a pointer to the count of instructions created so far, an
-   expression, the name of the instruction to build, an operand format
-   string, and corresponding arguments.  */
+   a pointer to the count of instructions created so far, an expression,
+   the name of the instruction to build, an operand format string, and
+   corresponding arguments.  */
 
 static void
 macro_build (expressionS *ep, const char *name, const char *fmt, ...)
@@ -872,11 +1470,12 @@ macro_build (expressionS *ep, const char *name, const char *fmt, ...)
   struct riscv_cl_insn insn;
   bfd_reloc_code_real_type r;
   va_list args;
+  const char *fmtStart;
 
   va_start (args, fmt);
 
   r = BFD_RELOC_UNUSED;
-  mo = (struct riscv_opcode *) hash_find (op_hash, name);
+  mo = (struct riscv_opcode *) str_hash_find (op_hash, name);
   gas_assert (mo);
 
   /* Find a non-RVC variant of the instruction.  append_insn will compress
@@ -886,26 +1485,54 @@ macro_build (expressionS *ep, const char *name, const char *fmt, ...)
   gas_assert (strcmp (name, mo->name) == 0);
 
   create_insn (&insn, mo);
-  for (;;)
+  for (;; ++fmt)
     {
-      switch (*fmt++)
+      fmtStart = fmt;
+      switch (*fmt)
        {
+       case 'V': /* RVV */
+         switch (*++fmt)
+           {
+           case 'd':
+             INSERT_OPERAND (VD, insn, va_arg (args, int));
+             continue;
+           case 's':
+             INSERT_OPERAND (VS1, insn, va_arg (args, int));
+             continue;
+           case 't':
+             INSERT_OPERAND (VS2, insn, va_arg (args, int));
+             continue;
+           case 'm':
+             {
+               int reg = va_arg (args, int);
+               if (reg == -1)
+                 {
+                   INSERT_OPERAND (VMASK, insn, 1);
+                   continue;
+                 }
+               else if (reg == 0)
+                 {
+                   INSERT_OPERAND (VMASK, insn, 0);
+                   continue;
+                 }
+               else
+                 goto unknown_macro_argument;
+             }
+           default:
+             goto unknown_macro_argument;
+           }
+         break;
+
        case 'd':
          INSERT_OPERAND (RD, insn, va_arg (args, int));
          continue;
-
        case 's':
          INSERT_OPERAND (RS1, insn, va_arg (args, int));
          continue;
-
        case 't':
          INSERT_OPERAND (RS2, insn, va_arg (args, int));
          continue;
 
-       case '>':
-         INSERT_OPERAND (SHAMT, insn, va_arg (args, int));
-         continue;
-
        case 'j':
        case 'u':
        case 'q':
@@ -918,7 +1545,8 @@ macro_build (expressionS *ep, const char *name, const char *fmt, ...)
        case ',':
          continue;
        default:
-         as_fatal (_("internal error: invalid macro"));
+       unknown_macro_argument:
+         as_fatal (_("internal: invalid macro argument `%s'"), fmtStart);
        }
       break;
     }
@@ -928,8 +1556,32 @@ macro_build (expressionS *ep, const char *name, const char *fmt, ...)
   append_insn (&insn, ep, r);
 }
 
+/* Build an instruction created by a macro expansion.  Like md_assemble but
+   accept a printf-style format string and arguments.  */
+
+static void
+md_assemblef (const char *format, ...)
+{
+  char *buf = NULL;
+  va_list ap;
+  int r;
+
+  va_start (ap, format);
+
+  r = vasprintf (&buf, format, ap);
+
+  if (r < 0)
+    as_fatal (_("internal: vasprintf failed"));
+
+  md_assemble (buf);
+  free(buf);
+
+  va_end (ap);
+}
+
 /* Sign-extend 32-bit mode constants that have bit 31 set and all higher bits
    unset.  */
+
 static void
 normalize_constant_expr (expressionS *ex)
 {
@@ -946,7 +1598,7 @@ normalize_constant_expr (expressionS *ex)
 
 static void
 check_absolute_expr (struct riscv_cl_insn *ip, expressionS *ex,
-                    bfd_boolean maybe_csr)
+                    bool maybe_csr)
 {
   if (ex->X_op == O_big)
     as_bad (_("unsupported large constant"));
@@ -954,7 +1606,7 @@ check_absolute_expr (struct riscv_cl_insn *ip, expressionS *ex,
     as_bad (_("unknown CSR `%s'"),
            S_GET_NAME (ex->X_add_symbol));
   else if (ex->X_op != O_constant)
-    as_bad (_("Instruction %s requires absolute expression"),
+    as_bad (_("instruction %s requires absolute expression"),
            ip->insn_mo->name);
   normalize_constant_expr (ex);
 }
@@ -962,11 +1614,12 @@ check_absolute_expr (struct riscv_cl_insn *ip, expressionS *ex,
 static symbolS *
 make_internal_label (void)
 {
-  return (symbolS *) local_symbol_make (FAKE_LABEL_NAME, now_seg,
-                                       (valueT) frag_now_fix (), frag_now);
+  return (symbolS *) local_symbol_make (FAKE_LABEL_NAME, now_seg, frag_now,
+                                       frag_now_fix ());
 }
 
 /* Load an entry from the GOT.  */
+
 static void
 pcrel_access (int destreg, int tempreg, expressionS *ep,
              const char *lo_insn, const char *lo_pattern,
@@ -999,12 +1652,18 @@ pcrel_store (int srcreg, int tempreg, expressionS *ep, const char *lo_insn,
 }
 
 /* PC-relative function call using AUIPC/JALR, relaxed to JAL.  */
+
 static void
 riscv_call (int destreg, int tempreg, expressionS *ep,
            bfd_reloc_code_real_type reloc)
 {
+  /* Ensure the jalr is emitted to the same frag as the auipc.  */
+  frag_grow (8);
   macro_build (ep, "auipc", "d,u", tempreg, reloc);
   macro_build (NULL, "jalr", "d,s", destreg, tempreg);
+  /* See comment at end of append_insn.  */
+  frag_wane (frag_now);
+  frag_new (0);
 }
 
 /* Load an integer constant into a register.  */
@@ -1013,8 +1672,9 @@ static void
 load_const (int reg, expressionS *ep)
 {
   int shift = RISCV_IMM_BITS;
+  bfd_vma upper_imm, sign = (bfd_vma) 1 << (RISCV_IMM_BITS - 1);
   expressionS upper = *ep, lower = *ep;
-  lower.X_add_number = (int32_t) ep->X_add_number << (32-shift) >> (32-shift);
+  lower.X_add_number = ((ep->X_add_number & (sign + sign - 1)) ^ sign) - sign;
   upper.X_add_number -= lower.X_add_number;
 
   if (ep->X_op != O_constant)
@@ -1032,9 +1692,10 @@ load_const (int reg, expressionS *ep)
       upper.X_add_number = (int64_t) upper.X_add_number >> shift;
       load_const (reg, &upper);
 
-      macro_build (NULL, "slli", "d,s,>", reg, reg, shift);
+      md_assemblef ("slli x%d, x%d, 0x%x", reg, reg, shift);
       if (lower.X_add_number != 0)
-       macro_build (&lower, "addi", "d,s,j", reg, reg, BFD_RELOC_RISCV_LO12_I);
+       md_assemblef ("addi x%d, x%d, %" PRId64, reg, reg,
+                     (int64_t) lower.X_add_number);
     }
   else
     {
@@ -1043,17 +1704,127 @@ load_const (int reg, expressionS *ep)
 
       if (upper.X_add_number != 0)
        {
-         macro_build (ep, "lui", "d,u", reg, BFD_RELOC_RISCV_HI20);
+         /* Discard low part and zero-extend upper immediate.  */
+         upper_imm = ((uint32_t)upper.X_add_number >> shift);
+
+         md_assemblef ("lui x%d, 0x%" PRIx64, reg, (uint64_t) upper_imm);
          hi_reg = reg;
        }
 
       if (lower.X_add_number != 0 || hi_reg == 0)
-       macro_build (ep, ADD32_INSN, "d,s,j", reg, hi_reg,
-                    BFD_RELOC_RISCV_LO12_I);
+       md_assemblef ("%s x%d, x%d, %" PRId64, ADD32_INSN, reg, hi_reg,
+                     (int64_t) lower.X_add_number);
+    }
+}
+
+/* Zero extend and sign extend byte/half-word/word.  */
+
+static void
+riscv_ext (int destreg, int srcreg, unsigned shift, bool sign)
+{
+  if (sign)
+    {
+      md_assemblef ("slli x%d, x%d, 0x%x", destreg, srcreg, shift);
+      md_assemblef ("srai x%d, x%d, 0x%x", destreg, destreg, shift);
+    }
+  else
+    {
+      md_assemblef ("slli x%d, x%d, 0x%x", destreg, srcreg, shift);
+      md_assemblef ("srli x%d, x%d, 0x%x", destreg, destreg, shift);
+    }
+}
+
+/* Expand RISC-V Vector macros into one or more instructions.  */
+
+static void
+vector_macro (struct riscv_cl_insn *ip)
+{
+  int vd = (ip->insn_opcode >> OP_SH_VD) & OP_MASK_VD;
+  int vs1 = (ip->insn_opcode >> OP_SH_VS1) & OP_MASK_VS1;
+  int vs2 = (ip->insn_opcode >> OP_SH_VS2) & OP_MASK_VS2;
+  int vm = (ip->insn_opcode >> OP_SH_VMASK) & OP_MASK_VMASK;
+  int vtemp = (ip->insn_opcode >> OP_SH_VFUNCT6) & OP_MASK_VFUNCT6;
+  int mask = ip->insn_mo->mask;
+
+  switch (mask)
+    {
+    case M_VMSGE:
+      if (vm)
+       {
+         /* Unmasked.  */
+         macro_build (NULL, "vmslt.vx", "Vd,Vt,sVm", vd, vs2, vs1, -1);
+         macro_build (NULL, "vmnand.mm", "Vd,Vt,Vs", vd, vd, vd);
+         break;
+       }
+      if (vtemp != 0)
+       {
+         /* Masked.  Have vtemp to avoid overlap constraints.  */
+         if (vd == vm)
+           {
+             macro_build (NULL, "vmslt.vx", "Vd,Vt,s", vtemp, vs2, vs1);
+             macro_build (NULL, "vmandnot.mm", "Vd,Vt,Vs", vd, vm, vtemp);
+           }
+         else
+           {
+             /* Preserve the value of vd if not updating by vm.  */
+             macro_build (NULL, "vmslt.vx", "Vd,Vt,s", vtemp, vs2, vs1);
+             macro_build (NULL, "vmandnot.mm", "Vd,Vt,Vs", vtemp, vm, vtemp);
+             macro_build (NULL, "vmandnot.mm", "Vd,Vt,Vs", vd, vd, vm);
+             macro_build (NULL, "vmor.mm", "Vd,Vt,Vs", vd, vtemp, vd);
+           }
+       }
+      else if (vd != vm)
+       {
+         /* Masked.  This may cause the vd overlaps vs2, when LMUL > 1.  */
+         macro_build (NULL, "vmslt.vx", "Vd,Vt,sVm", vd, vs2, vs1, vm);
+         macro_build (NULL, "vmxor.mm", "Vd,Vt,Vs", vd, vd, vm);
+       }
+      else
+       as_bad (_("must provide temp if destination overlaps mask"));
+      break;
+
+    case M_VMSGEU:
+      if (vm)
+       {
+         /* Unmasked.  */
+         macro_build (NULL, "vmsltu.vx", "Vd,Vt,sVm", vd, vs2, vs1, -1);
+         macro_build (NULL, "vmnand.mm", "Vd,Vt,Vs", vd, vd, vd);
+         break;
+       }
+      if (vtemp != 0)
+       {
+         /* Masked.  Have vtemp to avoid overlap constraints.  */
+         if (vd == vm)
+           {
+             macro_build (NULL, "vmsltu.vx", "Vd,Vt,s", vtemp, vs2, vs1);
+             macro_build (NULL, "vmandnot.mm", "Vd,Vt,Vs", vd, vm, vtemp);
+           }
+         else
+           {
+             /* Preserve the value of vd if not updating by vm.  */
+             macro_build (NULL, "vmsltu.vx", "Vd,Vt,s", vtemp, vs2, vs1);
+             macro_build (NULL, "vmandnot.mm", "Vd,Vt,Vs", vtemp, vm, vtemp);
+             macro_build (NULL, "vmandnot.mm", "Vd,Vt,Vs", vd, vd, vm);
+             macro_build (NULL, "vmor.mm", "Vd,Vt,Vs", vd, vtemp, vd);
+           }
+       }
+      else if (vd != vm)
+       {
+         /* Masked.  This may cause the vd overlaps vs2, when LMUL > 1.  */
+         macro_build (NULL, "vmsltu.vx", "Vd,Vt,sVm", vd, vs2, vs1, vm);
+         macro_build (NULL, "vmxor.mm", "Vd,Vt,Vs", vd, vd, vm);
+       }
+      else
+       as_bad (_("must provide temp if destination overlaps mask"));
+      break;
+
+    default:
+      break;
     }
 }
 
 /* Expand RISC-V assembly macros into one or more instructions.  */
+
 static void
 macro (struct riscv_cl_insn *ip, expressionS *imm_expr,
        bfd_reloc_code_real_type *imm_reloc)
@@ -1077,10 +1848,10 @@ macro (struct riscv_cl_insn *ip, expressionS *imm_expr,
 
       if (imm_expr->X_op == O_constant)
        load_const (rd, imm_expr);
-      else if (riscv_opts.pic && mask == M_LA) /* Global PIC symbol */
+      else if (riscv_opts.pic && mask == M_LA) /* Global PIC symbol */
        pcrel_load (rd, rd, imm_expr, LOAD_ADDRESS_INSN,
                    BFD_RELOC_RISCV_GOT_HI20, BFD_RELOC_RISCV_PCREL_LO12_I);
-      else /* Local PIC symbol, or any non-PIC symbol */
+      else /* Local PIC symbol, or any non-PIC symbol */
        pcrel_load (rd, rd, imm_expr, "addi",
                    BFD_RELOC_RISCV_PCREL_HI20, BFD_RELOC_RISCV_PCREL_LO12_I);
       break;
@@ -1174,8 +1945,38 @@ macro (struct riscv_cl_insn *ip, expressionS *imm_expr,
       riscv_call (rd, rs1, imm_expr, *imm_reloc);
       break;
 
+    case M_ZEXTH:
+      riscv_ext (rd, rs1, xlen - 16, false);
+      break;
+
+    case M_ZEXTW:
+      riscv_ext (rd, rs1, xlen - 32, false);
+      break;
+
+    case M_SEXTB:
+      riscv_ext (rd, rs1, xlen - 8, true);
+      break;
+
+    case M_SEXTH:
+      riscv_ext (rd, rs1, xlen - 16, true);
+      break;
+
+    case M_VMSGE:
+    case M_VMSGEU:
+      vector_macro (ip);
+      break;
+
+    case M_FLH:
+      pcrel_load (rd, rs1, imm_expr, "flh",
+                 BFD_RELOC_RISCV_PCREL_HI20, BFD_RELOC_RISCV_PCREL_LO12_I);
+      break;
+    case M_FSH:
+      pcrel_store (rs2, rs1, imm_expr, "fsh",
+                  BFD_RELOC_RISCV_PCREL_HI20, BFD_RELOC_RISCV_PCREL_LO12_S);
+      break;
+
     default:
-      as_bad (_("Macro %s not implemented"), ip->insn_mo->name);
+      as_bad (_("internal: macro %s not implemented"), ip->insn_mo->name);
       break;
     }
 }
@@ -1184,6 +1985,7 @@ static const struct percent_op_match percent_op_utype[] =
 {
   {"%tprel_hi", BFD_RELOC_RISCV_TPREL_HI20},
   {"%pcrel_hi", BFD_RELOC_RISCV_PCREL_HI20},
+  {"%got_pcrel_hi", BFD_RELOC_RISCV_GOT_HI20},
   {"%tls_ie_pcrel_hi", BFD_RELOC_RISCV_TLS_GOT_HI20},
   {"%tls_gd_pcrel_hi", BFD_RELOC_RISCV_TLS_GD_HI20},
   {"%hi", BFD_RELOC_RISCV_HI20},
@@ -1221,7 +2023,7 @@ static const struct percent_op_match percent_op_null[] =
    move *STR over the operator and store its relocation code in *RELOC.
    Leave both *STR and *RELOC alone when returning false.  */
 
-static bfd_boolean
+static bool
 parse_relocation (char **str, bfd_reloc_code_real_type *reloc,
                  const struct percent_op_match *percent_op)
 {
@@ -1241,13 +2043,13 @@ parse_relocation (char **str, bfd_reloc_code_real_type *reloc,
        if (*reloc != BFD_RELOC_UNUSED
            && !bfd_reloc_type_lookup (stdoutput, *reloc))
          {
-           as_bad ("relocation %s isn't supported by the current ABI",
-                   percent_op->str);
+           as_bad ("internal: relocation %s isn't supported by the "
+                   "current ABI", percent_op->str);
            *reloc = BFD_RELOC_UNUSED;
          }
-       return TRUE;
+       return true;
       }
-  return FALSE;
+  return false;
 }
 
 static void
@@ -1289,9 +2091,9 @@ my_getSmallExpression (expressionS *ep, bfd_reloc_code_real_type *reloc,
     }
 
   /* Search for the start of the main expression.
-     End the loop with CRUX pointing to the start
-     of the main expression and with CRUX_DEPTH containing the number
-     of open brackets at that point.  */
+
+     End the loop with CRUX pointing to the start of the main expression and
+     with CRUX_DEPTH containing the number of open brackets at that point.  */
   reloc_index = -1;
   str_depth = 0;
   do
@@ -1327,6 +2129,7 @@ my_getSmallExpression (expressionS *ep, bfd_reloc_code_real_type *reloc,
 }
 
 /* Parse opcode name, could be an mnemonics or number.  */
+
 static size_t
 my_getOpcodeExpression (expressionS *ep, bfd_reloc_code_real_type *reloc,
                        char *str, const struct percent_op_match *percent_op)
@@ -1343,11 +2146,71 @@ my_getOpcodeExpression (expressionS *ep, bfd_reloc_code_real_type *reloc,
   return my_getSmallExpression (ep, reloc, str, percent_op);
 }
 
+/* Parse string STR as a vsetvli operand.  Store the expression in *EP.
+   On exit, EXPR_END points to the first character after the expression.  */
+
+static void
+my_getVsetvliExpression (expressionS *ep, char *str)
+{
+  unsigned int vsew_value = 0, vlmul_value = 0;
+  unsigned int vta_value = 0, vma_value = 0;
+  bfd_boolean vsew_found = FALSE, vlmul_found = FALSE;
+  bfd_boolean vta_found = FALSE, vma_found = FALSE;
+
+  if (arg_lookup (&str, riscv_vsew, ARRAY_SIZE (riscv_vsew), &vsew_value))
+    {
+      if (*str == ',')
+       ++str;
+      if (vsew_found)
+       as_bad (_("multiple vsew constants"));
+      vsew_found = TRUE;
+    }
+  if (arg_lookup (&str, riscv_vlmul, ARRAY_SIZE (riscv_vlmul), &vlmul_value))
+    {
+      if (*str == ',')
+       ++str;
+      if (vlmul_found)
+       as_bad (_("multiple vlmul constants"));
+      vlmul_found = TRUE;
+    }
+  if (arg_lookup (&str, riscv_vta, ARRAY_SIZE (riscv_vta), &vta_value))
+    {
+      if (*str == ',')
+       ++str;
+      if (vta_found)
+       as_bad (_("multiple vta constants"));
+      vta_found = TRUE;
+    }
+  if (arg_lookup (&str, riscv_vma, ARRAY_SIZE (riscv_vma), &vma_value))
+    {
+      if (*str == ',')
+       ++str;
+      if (vma_found)
+       as_bad (_("multiple vma constants"));
+      vma_found = TRUE;
+    }
+
+  if (vsew_found || vlmul_found || vta_found || vma_found)
+    {
+      ep->X_op = O_constant;
+      ep->X_add_number = (vlmul_value << OP_SH_VLMUL)
+                        | (vsew_value << OP_SH_VSEW)
+                        | (vta_value << OP_SH_VTA)
+                        | (vma_value << OP_SH_VMA);
+      expr_end = str;
+    }
+  else
+    {
+      my_getExpression (ep, str);
+      str = expr_end;
+    }
+}
+
 /* Detect and handle implicitly zero load-store offsets.  For example,
-   "lw t0, (t1)" is shorthand for "lw t0, 0(t1)".  Return TRUE iff such
+   "lw t0, (t1)" is shorthand for "lw t0, 0(t1)".  Return true if such
    an implicit offset was detected.  */
 
-static bfd_boolean
+static bool
 riscv_handle_implicit_zero_offset (expressionS *ep, const char *s)
 {
   /* Check whether there is only a single bracketed expression left.
@@ -1356,65 +2219,150 @@ riscv_handle_implicit_zero_offset (expressionS *ep, const char *s)
     {
       ep->X_op = O_constant;
       ep->X_add_number = 0;
-      return TRUE;
+      return true;
     }
 
-  return FALSE;
+  return false;
+}
+
+/* All RISC-V CSR instructions belong to one of these classes.  */
+enum csr_insn_type
+{
+  INSN_NOT_CSR,
+  INSN_CSRRW,
+  INSN_CSRRS,
+  INSN_CSRRC
+};
+
+/* Return which CSR instruction is checking.  */
+
+static enum csr_insn_type
+riscv_csr_insn_type (insn_t insn)
+{
+  if (((insn ^ MATCH_CSRRW) & MASK_CSRRW) == 0
+      || ((insn ^ MATCH_CSRRWI) & MASK_CSRRWI) == 0)
+    return INSN_CSRRW;
+  else if (((insn ^ MATCH_CSRRS) & MASK_CSRRS) == 0
+          || ((insn ^ MATCH_CSRRSI) & MASK_CSRRSI) == 0)
+    return INSN_CSRRS;
+  else if (((insn ^ MATCH_CSRRC) & MASK_CSRRC) == 0
+          || ((insn ^ MATCH_CSRRCI) & MASK_CSRRCI) == 0)
+    return INSN_CSRRC;
+  else
+    return INSN_NOT_CSR;
+}
+
+/* CSRRW and CSRRWI always write CSR.  CSRRS, CSRRC, CSRRSI and CSRRCI write
+   CSR when RS1 isn't zero.  The CSR is read only if the [11:10] bits of
+   CSR address is 0x3.  */
+
+static bool
+riscv_csr_read_only_check (insn_t insn)
+{
+  int csr = (insn & (OP_MASK_CSR << OP_SH_CSR)) >> OP_SH_CSR;
+  int rs1 = (insn & (OP_MASK_RS1 << OP_SH_RS1)) >> OP_SH_RS1;
+  int readonly = (((csr & (0x3 << 10)) >> 10) == 0x3);
+  enum csr_insn_type csr_insn = riscv_csr_insn_type (insn);
+
+  if (readonly
+      && (((csr_insn == INSN_CSRRS
+           || csr_insn == INSN_CSRRC)
+          && rs1 != 0)
+         || csr_insn == INSN_CSRRW))
+    return false;
+
+  return true;
+}
+
+/* Return true if it is a privileged instruction.  Otherwise, return false.
+
+   uret is actually a N-ext instruction.  So it is better to regard it as
+   an user instruction rather than the priv instruction.
+
+   hret is used to return from traps in H-mode.  H-mode is removed since
+   the v1.10 priv spec, but probably be added in the new hypervisor spec.
+   Therefore, hret should be controlled by the hypervisor spec rather than
+   priv spec in the future.
+
+   dret is defined in the debug spec, so it should be checked in the future,
+   too.  */
+
+static bool
+riscv_is_priv_insn (insn_t insn)
+{
+  return (((insn ^ MATCH_SRET) & MASK_SRET) == 0
+         || ((insn ^ MATCH_MRET) & MASK_MRET) == 0
+         || ((insn ^ MATCH_SFENCE_VMA) & MASK_SFENCE_VMA) == 0
+         || ((insn ^ MATCH_WFI) & MASK_WFI) == 0
+  /* The sfence.vm is dropped in the v1.10 priv specs, but we still need to
+     check it here to keep the compatible.  */
+         || ((insn ^ MATCH_SFENCE_VM) & MASK_SFENCE_VM) == 0);
 }
 
 /* This routine assembles an instruction into its binary format.  As a
    side effect, it sets the global variable imm_reloc to the type of
    relocation to do if one of the operands is an address expression.  */
 
-static const char *
+static struct riscv_ip_error
 riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr,
-         bfd_reloc_code_real_type *imm_reloc, struct hash_control *hash)
+         bfd_reloc_code_real_type *imm_reloc, htab_t hash)
 {
-  char *s;
-  const char *args;
-  char c = 0;
+  /* The operand string defined in the riscv_opcodes.  */
+  const char *oparg, *opargStart;
+  /* The parsed operands from assembly.  */
+  char *asarg, *asargStart;
+  char save_c = 0;
   struct riscv_opcode *insn;
-  char *argsStart;
   unsigned int regno;
-  char save_c = 0;
-  int argnum;
   const struct percent_op_match *p;
-  const char *error = "unrecognized opcode";
+  struct riscv_ip_error error;
+  error.msg = "unrecognized opcode";
+  error.statement = str;
+  error.missing_ext = NULL;
+  /* Indicate we are assembling instruction with CSR.  */
+  bool insn_with_csr = false;
 
   /* Parse the name of the instruction.  Terminate the string if whitespace
-     is found so that hash_find only sees the name part of the string.  */
-  for (s = str; *s != '\0'; ++s)
-    if (ISSPACE (*s))
+     is found so that str_hash_find only sees the name part of the string.  */
+  for (asarg = str; *asarg!= '\0'; ++asarg)
+    if (ISSPACE (*asarg))
       {
-       save_c = *s;
-       *s++ = '\0';
+       save_c = *asarg;
+       *asarg++ = '\0';
        break;
       }
 
-  insn = (struct riscv_opcode *) hash_find (hash, str);
+  insn = (struct riscv_opcode *) str_hash_find (hash, str);
 
-  argsStart = s;
+  asargStart = asarg;
   for ( ; insn && insn->name && strcmp (insn->name, str) == 0; insn++)
     {
       if ((insn->xlen_requirement != 0) && (xlen != insn->xlen_requirement))
        continue;
 
-      if (!riscv_multi_subset_supports (insn->subset))
-       continue;
+      if (!riscv_multi_subset_supports (&riscv_rps_as, insn->insn_class))
+       {
+         error.missing_ext = riscv_multi_subset_supports_ext (&riscv_rps_as,
+                                                              insn->insn_class);
+         continue;
+       }
 
+      /* Reset error message of the previous round.  */
+      error.msg = _("illegal operands");
+      error.missing_ext = NULL;
       create_insn (ip, insn);
-      argnum = 1;
 
       imm_expr->X_op = O_absent;
       *imm_reloc = BFD_RELOC_UNUSED;
       p = percent_op_itype;
 
-      for (args = insn->args;; ++args)
+      for (oparg = insn->args;; ++oparg)
        {
-         s += strspn (s, " \t");
-         switch (*args)
+         opargStart = oparg;
+         asarg += strspn (asarg, " \t");
+         switch (*oparg)
            {
-           case '\0':  /* End of args.  */
+           case '\0': /* End of args.  */
              if (insn->pinfo != INSN_MACRO)
                {
                  if (!insn->match_func (insn, ip->insn_opcode))
@@ -1426,194 +2374,221 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr,
                                         : insn->match) == 2
                      && !riscv_opts.rvc)
                    break;
+
+                 if (riscv_is_priv_insn (ip->insn_opcode))
+                   explicit_priv_attr = true;
+
+                 /* Check if we write a read-only CSR by the CSR
+                    instruction.  */
+                 if (insn_with_csr
+                     && riscv_opts.csr_check
+                     && !riscv_csr_read_only_check (ip->insn_opcode))
+                   {
+                     /* Restore the character in advance, since we want to
+                        report the detailed warning message here.  */
+                     if (save_c)
+                       *(asargStart - 1) = save_c;
+                     as_warn (_("read-only CSR is written `%s'"), str);
+                     insn_with_csr = false;
+                   }
+
+                 /* The (segmant) load and store with EEW 64 cannot be used
+                    when zve32x is enabled.  */
+                 if (ip->insn_mo->pinfo & INSN_V_EEW64
+                     && riscv_subset_supports (&riscv_rps_as, "zve32x")
+                     && !riscv_subset_supports (&riscv_rps_as, "zve64x"))
+                   {
+                     error.msg = _("illegal opcode for zve32x");
+                     break;
+                   }
                }
-             if (*s != '\0')
+             if (*asarg != '\0')
                break;
              /* Successful assembly.  */
-             error = NULL;
+             error.msg = NULL;
+             insn_with_csr = false;
              goto out;
 
            case 'C': /* RVC */
-             switch (*++args)
+             switch (*++oparg)
                {
-               case 's': /* RS1 x8-x15 */
-                 if (!reg_lookup (&s, RCLASS_GPR, &regno)
+               case 's': /* RS1 x8-x15 */
+                 if (!reg_lookup (&asarg, RCLASS_GPR, &regno)
                      || !(regno >= 8 && regno <= 15))
                    break;
                  INSERT_OPERAND (CRS1S, *ip, regno % 8);
                  continue;
                case 'w': /* RS1 x8-x15, constrained to equal RD x8-x15.  */
-                 if (!reg_lookup (&s, RCLASS_GPR, &regno)
+                 if (!reg_lookup (&asarg, RCLASS_GPR, &regno)
                      || EXTRACT_OPERAND (CRS1S, ip->insn_opcode) + 8 != regno)
                    break;
                  continue;
-               case 't': /* RS2 x8-x15 */
-                 if (!reg_lookup (&s, RCLASS_GPR, &regno)
+               case 't': /* RS2 x8-x15 */
+                 if (!reg_lookup (&asarg, RCLASS_GPR, &regno)
                      || !(regno >= 8 && regno <= 15))
                    break;
                  INSERT_OPERAND (CRS2S, *ip, regno % 8);
                  continue;
                case 'x': /* RS2 x8-x15, constrained to equal RD x8-x15.  */
-                 if (!reg_lookup (&s, RCLASS_GPR, &regno)
+                 if (!reg_lookup (&asarg, RCLASS_GPR, &regno)
                      || EXTRACT_OPERAND (CRS2S, ip->insn_opcode) + 8 != regno)
                    break;
                  continue;
                case 'U': /* RS1, constrained to equal RD.  */
-                 if (!reg_lookup (&s, RCLASS_GPR, &regno)
+                 if (!reg_lookup (&asarg, RCLASS_GPR, &regno)
                      || EXTRACT_OPERAND (RD, ip->insn_opcode) != regno)
                    break;
                  continue;
                case 'V': /* RS2 */
-                 if (!reg_lookup (&s, RCLASS_GPR, &regno))
+                 if (!reg_lookup (&asarg, RCLASS_GPR, &regno))
                    break;
                  INSERT_OPERAND (CRS2, *ip, regno);
                  continue;
                case 'c': /* RS1, constrained to equal sp.  */
-                 if (!reg_lookup (&s, RCLASS_GPR, &regno)
+                 if (!reg_lookup (&asarg, RCLASS_GPR, &regno)
                      || regno != X_SP)
                    break;
                  continue;
-               case 'z': /* RS2, contrained to equal x0.  */
-                 if (!reg_lookup (&s, RCLASS_GPR, &regno)
+               case 'z': /* RS2, constrained to equal x0.  */
+                 if (!reg_lookup (&asarg, RCLASS_GPR, &regno)
                      || regno != 0)
                    break;
                  continue;
-               case '>':
-                 if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
+               case '>': /* Shift amount, 0 - (XLEN-1).  */
+                 if (my_getSmallExpression (imm_expr, imm_reloc, asarg, p)
                      || imm_expr->X_op != O_constant
-                     || imm_expr->X_add_number <= 0
-                     || imm_expr->X_add_number >= 64)
+                     || (unsigned long) imm_expr->X_add_number >= xlen)
                    break;
-                 ip->insn_opcode |= ENCODE_RVC_IMM (imm_expr->X_add_number);
-rvc_imm_done:
-                 s = expr_end;
+                 ip->insn_opcode |= ENCODE_CITYPE_IMM (imm_expr->X_add_number);
+               rvc_imm_done:
+                 asarg = expr_end;
                  imm_expr->X_op = O_absent;
                  continue;
-               case '<':
-                 if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
-                     || imm_expr->X_op != O_constant
-                     || !VALID_RVC_IMM (imm_expr->X_add_number)
-                     || imm_expr->X_add_number <= 0
-                     || imm_expr->X_add_number >= 32)
+               case '5':
+                 if (my_getSmallExpression (imm_expr, imm_reloc, asarg, p)
+                     || imm_expr->X_op != O_constant
+                     || imm_expr->X_add_number < 0
+                     || imm_expr->X_add_number >= 32
+                     || !VALID_CLTYPE_IMM ((valueT) imm_expr->X_add_number))
                    break;
-                 ip->insn_opcode |= ENCODE_RVC_IMM (imm_expr->X_add_number);
+                 ip->insn_opcode |= ENCODE_CLTYPE_IMM (imm_expr->X_add_number);
                  goto rvc_imm_done;
-               case '8':
-                 if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
+               case '6':
+                 if (my_getSmallExpression (imm_expr, imm_reloc, asarg, p)
                      || imm_expr->X_op != O_constant
-                     || !VALID_RVC_UIMM8 (imm_expr->X_add_number)
                      || imm_expr->X_add_number < 0
-                     || imm_expr->X_add_number >= 256)
+                     || imm_expr->X_add_number >= 64
+                     || !VALID_CSSTYPE_IMM ((valueT) imm_expr->X_add_number))
                    break;
-                 ip->insn_opcode |= ENCODE_RVC_UIMM8 (imm_expr->X_add_number);
+                 ip->insn_opcode |= ENCODE_CSSTYPE_IMM (imm_expr->X_add_number);
                  goto rvc_imm_done;
-               case 'i':
-                 if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
+               case '8':
+                 if (my_getSmallExpression (imm_expr, imm_reloc, asarg, p)
                      || imm_expr->X_op != O_constant
-                     || imm_expr->X_add_number == 0
-                     || !VALID_RVC_SIMM3 (imm_expr->X_add_number))
+                     || imm_expr->X_add_number < 0
+                     || imm_expr->X_add_number >= 256
+                     || !VALID_CIWTYPE_IMM ((valueT) imm_expr->X_add_number))
                    break;
-                 ip->insn_opcode |= ENCODE_RVC_SIMM3 (imm_expr->X_add_number);
+                 ip->insn_opcode |= ENCODE_CIWTYPE_IMM (imm_expr->X_add_number);
                  goto rvc_imm_done;
                case 'j':
-                 if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
+                 if (my_getSmallExpression (imm_expr, imm_reloc, asarg, p)
                      || imm_expr->X_op != O_constant
                      || imm_expr->X_add_number == 0
-                     || !VALID_RVC_IMM (imm_expr->X_add_number))
+                     || !VALID_CITYPE_IMM ((valueT) imm_expr->X_add_number))
                    break;
-                 ip->insn_opcode |= ENCODE_RVC_IMM (imm_expr->X_add_number);
+                 ip->insn_opcode |= ENCODE_CITYPE_IMM (imm_expr->X_add_number);
                  goto rvc_imm_done;
                case 'k':
-                 if (riscv_handle_implicit_zero_offset (imm_expr, s))
+                 if (riscv_handle_implicit_zero_offset (imm_expr, asarg))
                    continue;
-                 if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
+                 if (my_getSmallExpression (imm_expr, imm_reloc, asarg, p)
                      || imm_expr->X_op != O_constant
-                     || !VALID_RVC_LW_IMM (imm_expr->X_add_number))
+                     || !VALID_CLTYPE_LW_IMM ((valueT) imm_expr->X_add_number))
                    break;
-                 ip->insn_opcode |= ENCODE_RVC_LW_IMM (imm_expr->X_add_number);
+                 ip->insn_opcode |= ENCODE_CLTYPE_LW_IMM (imm_expr->X_add_number);
                  goto rvc_imm_done;
                case 'l':
-                 if (riscv_handle_implicit_zero_offset (imm_expr, s))
+                 if (riscv_handle_implicit_zero_offset (imm_expr, asarg))
                    continue;
-                 if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
+                 if (my_getSmallExpression (imm_expr, imm_reloc, asarg, p)
                      || imm_expr->X_op != O_constant
-                     || !VALID_RVC_LD_IMM (imm_expr->X_add_number))
+                     || !VALID_CLTYPE_LD_IMM ((valueT) imm_expr->X_add_number))
                    break;
-                 ip->insn_opcode |= ENCODE_RVC_LD_IMM (imm_expr->X_add_number);
+                 ip->insn_opcode |= ENCODE_CLTYPE_LD_IMM (imm_expr->X_add_number);
                  goto rvc_imm_done;
                case 'm':
-                 if (riscv_handle_implicit_zero_offset (imm_expr, s))
+                 if (riscv_handle_implicit_zero_offset (imm_expr, asarg))
                    continue;
-                 if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
+                 if (my_getSmallExpression (imm_expr, imm_reloc, asarg, p)
                      || imm_expr->X_op != O_constant
-                     || !VALID_RVC_LWSP_IMM (imm_expr->X_add_number))
+                     || !VALID_CITYPE_LWSP_IMM ((valueT) imm_expr->X_add_number))
                    break;
                  ip->insn_opcode |=
-                   ENCODE_RVC_LWSP_IMM (imm_expr->X_add_number);
+                   ENCODE_CITYPE_LWSP_IMM (imm_expr->X_add_number);
                  goto rvc_imm_done;
                case 'n':
-                 if (riscv_handle_implicit_zero_offset (imm_expr, s))
+                 if (riscv_handle_implicit_zero_offset (imm_expr, asarg))
                    continue;
-                 if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
+                 if (my_getSmallExpression (imm_expr, imm_reloc, asarg, p)
                      || imm_expr->X_op != O_constant
-                     || !VALID_RVC_LDSP_IMM (imm_expr->X_add_number))
+                     || !VALID_CITYPE_LDSP_IMM ((valueT) imm_expr->X_add_number))
                    break;
                  ip->insn_opcode |=
-                   ENCODE_RVC_LDSP_IMM (imm_expr->X_add_number);
+                   ENCODE_CITYPE_LDSP_IMM (imm_expr->X_add_number);
                  goto rvc_imm_done;
                case 'o':
-                 if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
+                 if (my_getSmallExpression (imm_expr, imm_reloc, asarg, p)
                      || imm_expr->X_op != O_constant
                      /* C.addiw, c.li, and c.andi allow zero immediate.
                         C.addi allows zero immediate as hint.  Otherwise this
                         is same as 'j'.  */
-                     || !VALID_RVC_IMM (imm_expr->X_add_number))
+                     || !VALID_CITYPE_IMM ((valueT) imm_expr->X_add_number))
                    break;
-                 ip->insn_opcode |= ENCODE_RVC_IMM (imm_expr->X_add_number);
+                 ip->insn_opcode |= ENCODE_CITYPE_IMM (imm_expr->X_add_number);
                  goto rvc_imm_done;
                case 'K':
-                 if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
+                 if (my_getSmallExpression (imm_expr, imm_reloc, asarg, p)
                      || imm_expr->X_op != O_constant
-                     || !VALID_RVC_ADDI4SPN_IMM (imm_expr->X_add_number)
-                     || imm_expr->X_add_number == 0)
+                     || imm_expr->X_add_number == 0
+                     || !VALID_CIWTYPE_ADDI4SPN_IMM ((valueT) imm_expr->X_add_number))
                    break;
                  ip->insn_opcode |=
-                   ENCODE_RVC_ADDI4SPN_IMM (imm_expr->X_add_number);
+                   ENCODE_CIWTYPE_ADDI4SPN_IMM (imm_expr->X_add_number);
                  goto rvc_imm_done;
                case 'L':
-                 if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
+                 if (my_getSmallExpression (imm_expr, imm_reloc, asarg, p)
                      || imm_expr->X_op != O_constant
-                     || !VALID_RVC_ADDI16SP_IMM (imm_expr->X_add_number)
-                     || imm_expr->X_add_number == 0)
+                     || !VALID_CITYPE_ADDI16SP_IMM ((valueT) imm_expr->X_add_number))
                    break;
                  ip->insn_opcode |=
-                   ENCODE_RVC_ADDI16SP_IMM (imm_expr->X_add_number);
+                   ENCODE_CITYPE_ADDI16SP_IMM (imm_expr->X_add_number);
                  goto rvc_imm_done;
                case 'M':
-                 if (riscv_handle_implicit_zero_offset (imm_expr, s))
+                 if (riscv_handle_implicit_zero_offset (imm_expr, asarg))
                    continue;
-                 if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
+                 if (my_getSmallExpression (imm_expr, imm_reloc, asarg, p)
                      || imm_expr->X_op != O_constant
-                     || !VALID_RVC_SWSP_IMM (imm_expr->X_add_number))
+                     || !VALID_CSSTYPE_SWSP_IMM ((valueT) imm_expr->X_add_number))
                    break;
                  ip->insn_opcode |=
-                   ENCODE_RVC_SWSP_IMM (imm_expr->X_add_number);
+                   ENCODE_CSSTYPE_SWSP_IMM (imm_expr->X_add_number);
                  goto rvc_imm_done;
                case 'N':
-                 if (riscv_handle_implicit_zero_offset (imm_expr, s))
+                 if (riscv_handle_implicit_zero_offset (imm_expr, asarg))
                    continue;
-                 if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
+                 if (my_getSmallExpression (imm_expr, imm_reloc, asarg, p)
                      || imm_expr->X_op != O_constant
-                     || !VALID_RVC_SDSP_IMM (imm_expr->X_add_number))
+                     || !VALID_CSSTYPE_SDSP_IMM ((valueT) imm_expr->X_add_number))
                    break;
                  ip->insn_opcode |=
-                   ENCODE_RVC_SDSP_IMM (imm_expr->X_add_number);
+                   ENCODE_CSSTYPE_SDSP_IMM (imm_expr->X_add_number);
                  goto rvc_imm_done;
                case 'u':
                  p = percent_op_utype;
-                 if (my_getSmallExpression (imm_expr, imm_reloc, s, p))
+                 if (my_getSmallExpression (imm_expr, imm_reloc, asarg, p))
                    break;
-rvc_lui:
+               rvc_lui:
                  if (imm_expr->X_op != O_constant
                      || imm_expr->X_add_number <= 0
                      || imm_expr->X_add_number >= RISCV_BIGIMM_REACH
@@ -1621,10 +2596,10 @@ rvc_lui:
                          && (imm_expr->X_add_number <
                              RISCV_BIGIMM_REACH - RISCV_RVC_IMM_REACH / 2)))
                    break;
-                 ip->insn_opcode |= ENCODE_RVC_IMM (imm_expr->X_add_number);
+                 ip->insn_opcode |= ENCODE_CITYPE_IMM (imm_expr->X_add_number);
                  goto rvc_imm_done;
                case 'v':
-                 if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
+                 if (my_getSmallExpression (imm_expr, imm_reloc, asarg, p)
                      || (imm_expr->X_add_number & (RISCV_IMM_REACH - 1))
                      || ((int32_t)imm_expr->X_add_number
                          != imm_expr->X_add_number))
@@ -1637,161 +2612,329 @@ rvc_lui:
                case 'a':
                  goto jump;
                case 'S': /* Floating-point RS1 x8-x15.  */
-                 if (!reg_lookup (&s, RCLASS_FPR, &regno)
+                 if (!reg_lookup (&asarg, RCLASS_FPR, &regno)
                      || !(regno >= 8 && regno <= 15))
                    break;
                  INSERT_OPERAND (CRS1S, *ip, regno % 8);
                  continue;
                case 'D': /* Floating-point RS2 x8-x15.  */
-                 if (!reg_lookup (&s, RCLASS_FPR, &regno)
+                 if (!reg_lookup (&asarg, RCLASS_FPR, &regno)
                      || !(regno >= 8 && regno <= 15))
                    break;
                  INSERT_OPERAND (CRS2S, *ip, regno % 8);
                  continue;
                case 'T': /* Floating-point RS2.  */
-                 if (!reg_lookup (&s, RCLASS_FPR, &regno))
+                 if (!reg_lookup (&asarg, RCLASS_FPR, &regno))
                    break;
                  INSERT_OPERAND (CRS2, *ip, regno);
                  continue;
                case 'F':
-                 switch (*++args)
+                 switch (*++oparg)
                    {
                      case '6':
-                       if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
+                       if (my_getSmallExpression (imm_expr, imm_reloc, asarg, p)
                            || imm_expr->X_op != O_constant
                            || imm_expr->X_add_number < 0
                            || imm_expr->X_add_number >= 64)
                          {
-                           as_bad (_("bad value for funct6 field, "
-                                     "value must be 0...64"));
+                           as_bad (_("bad value for compressed funct6 "
+                                     "field, value must be 0...63"));
                            break;
                          }
-
                        INSERT_OPERAND (CFUNCT6, *ip, imm_expr->X_add_number);
                        imm_expr->X_op = O_absent;
-                       s = expr_end;
+                       asarg = expr_end;
                        continue;
+
                      case '4':
-                       if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
+                       if (my_getSmallExpression (imm_expr, imm_reloc, asarg, p)
                            || imm_expr->X_op != O_constant
                            || imm_expr->X_add_number < 0
                            || imm_expr->X_add_number >= 16)
                          {
-                           as_bad (_("bad value for funct4 field, "
-                                     "value must be 0...15"));
+                           as_bad (_("bad value for compressed funct4 "
+                                     "field, value must be 0...15"));
                            break;
                          }
-
                        INSERT_OPERAND (CFUNCT4, *ip, imm_expr->X_add_number);
                        imm_expr->X_op = O_absent;
-                       s = expr_end;
+                       asarg = expr_end;
                        continue;
+
                      case '3':
-                       if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
+                       if (my_getSmallExpression (imm_expr, imm_reloc, asarg, p)
                            || imm_expr->X_op != O_constant
                            || imm_expr->X_add_number < 0
                            || imm_expr->X_add_number >= 8)
                          {
-                           as_bad (_("bad value for funct3 field, "
-                                     "value must be 0...7"));
+                           as_bad (_("bad value for compressed funct3 "
+                                     "field, value must be 0...7"));
                            break;
                          }
                        INSERT_OPERAND (CFUNCT3, *ip, imm_expr->X_add_number);
                        imm_expr->X_op = O_absent;
-                       s = expr_end;
+                       asarg = expr_end;
                        continue;
+
                      case '2':
-                       if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
+                       if (my_getSmallExpression (imm_expr, imm_reloc, asarg, p)
                            || imm_expr->X_op != O_constant
                            || imm_expr->X_add_number < 0
                            || imm_expr->X_add_number >= 4)
                          {
-                           as_bad (_("bad value for funct2 field, "
-                                     "value must be 0...3"));
+                           as_bad (_("bad value for compressed funct2 "
+                                     "field, value must be 0...3"));
                            break;
                          }
                        INSERT_OPERAND (CFUNCT2, *ip, imm_expr->X_add_number);
                        imm_expr->X_op = O_absent;
-                       s = expr_end;
+                       asarg = expr_end;
                        continue;
+
                      default:
-                       as_bad (_("bad compressed FUNCT field"
-                                 " specifier 'CF%c'\n"),
-                               *args);
+                       goto unknown_riscv_ip_operand;
                    }
                  break;
 
                default:
-                 as_bad (_("bad RVC field specifier 'C%c'\n"), *args);
+                 goto unknown_riscv_ip_operand;
                }
-             break;
+             break; /* end RVC */
+
+           case 'V': /* RVV */
+             switch (*++oparg)
+               {
+               case 'd': /* VD */
+                 if (!reg_lookup (&asarg, RCLASS_VECR, &regno))
+                   break;
+                 INSERT_OPERAND (VD, *ip, regno);
+                 continue;
+
+               case 'e': /* AMO VD */
+                 if (reg_lookup (&asarg, RCLASS_GPR, &regno) && regno == 0)
+                   INSERT_OPERAND (VWD, *ip, 0);
+                 else if (reg_lookup (&asarg, RCLASS_VECR, &regno))
+                   {
+                     INSERT_OPERAND (VWD, *ip, 1);
+                     INSERT_OPERAND (VD, *ip, regno);
+                   }
+                 else
+                   break;
+                 continue;
+
+               case 'f': /* AMO VS3 */
+                 if (!reg_lookup (&asarg, RCLASS_VECR, &regno))
+                   break;
+                 if (!EXTRACT_OPERAND (VWD, ip->insn_opcode))
+                   INSERT_OPERAND (VD, *ip, regno);
+                 else
+                   {
+                     /* VS3 must match VD.  */
+                     if (EXTRACT_OPERAND (VD, ip->insn_opcode) != regno)
+                       break;
+                   }
+                 continue;
+
+               case 's': /* VS1 */
+                 if (!reg_lookup (&asarg, RCLASS_VECR, &regno))
+                   break;
+                 INSERT_OPERAND (VS1, *ip, regno);
+                 continue;
+
+               case 't': /* VS2 */
+                 if (!reg_lookup (&asarg, RCLASS_VECR, &regno))
+                   break;
+                 INSERT_OPERAND (VS2, *ip, regno);
+                 continue;
+
+               case 'u': /* VS1 == VS2 */
+                 if (!reg_lookup (&asarg, RCLASS_VECR, &regno))
+                   break;
+                 INSERT_OPERAND (VS1, *ip, regno);
+                 INSERT_OPERAND (VS2, *ip, regno);
+                 continue;
+
+               case 'v': /* VD == VS1 == VS2 */
+                 if (!reg_lookup (&asarg, RCLASS_VECR, &regno))
+                   break;
+                 INSERT_OPERAND (VD, *ip, regno);
+                 INSERT_OPERAND (VS1, *ip, regno);
+                 INSERT_OPERAND (VS2, *ip, regno);
+                 continue;
+
+               /* The `V0` is carry-in register for v[m]adc and v[m]sbc,
+                  and is used to choose vs1/rs1/frs1/imm or vs2 for
+                  v[f]merge.  It use the same encoding as the vector mask
+                  register.  */
+               case '0':
+                 if (reg_lookup (&asarg, RCLASS_VECR, &regno) && regno == 0)
+                   continue;
+                 break;
+
+               case 'b': /* vtypei for vsetivli */
+                 my_getVsetvliExpression (imm_expr, asarg);
+                 check_absolute_expr (ip, imm_expr, FALSE);
+                 if (!VALID_RVV_VB_IMM (imm_expr->X_add_number))
+                   as_bad (_("bad value for vsetivli immediate field, "
+                             "value must be 0..1023"));
+                 ip->insn_opcode
+                   |= ENCODE_RVV_VB_IMM (imm_expr->X_add_number);
+                 imm_expr->X_op = O_absent;
+                 asarg = expr_end;
+                 continue;
+
+               case 'c': /* vtypei for vsetvli */
+                 my_getVsetvliExpression (imm_expr, asarg);
+                 check_absolute_expr (ip, imm_expr, FALSE);
+                 if (!VALID_RVV_VC_IMM (imm_expr->X_add_number))
+                   as_bad (_("bad value for vsetvli immediate field, "
+                             "value must be 0..2047"));
+                 ip->insn_opcode
+                   |= ENCODE_RVV_VC_IMM (imm_expr->X_add_number);
+                 imm_expr->X_op = O_absent;
+                 asarg = expr_end;
+                 continue;
+
+               case 'i': /* vector arith signed immediate */
+                 my_getExpression (imm_expr, asarg);
+                 check_absolute_expr (ip, imm_expr, FALSE);
+                 if (imm_expr->X_add_number > 15
+                     || imm_expr->X_add_number < -16)
+                   as_bad (_("bad value for vector immediate field, "
+                             "value must be -16...15"));
+                 INSERT_OPERAND (VIMM, *ip, imm_expr->X_add_number);
+                 imm_expr->X_op = O_absent;
+                 asarg = expr_end;
+                 continue;
+
+               case 'j': /* vector arith unsigned immediate */
+                 my_getExpression (imm_expr, asarg);
+                 check_absolute_expr (ip, imm_expr, FALSE);
+                 if (imm_expr->X_add_number < 0
+                     || imm_expr->X_add_number >= 32)
+                   as_bad (_("bad value for vector immediate field, "
+                             "value must be 0...31"));
+                 INSERT_OPERAND (VIMM, *ip, imm_expr->X_add_number);
+                 imm_expr->X_op = O_absent;
+                 asarg = expr_end;
+                 continue;
+
+               case 'k': /* vector arith signed immediate, minus 1 */
+                 my_getExpression (imm_expr, asarg);
+                 check_absolute_expr (ip, imm_expr, FALSE);
+                 if (imm_expr->X_add_number > 16
+                     || imm_expr->X_add_number < -15)
+                   as_bad (_("bad value for vector immediate field, "
+                             "value must be -15...16"));
+                 INSERT_OPERAND (VIMM, *ip, imm_expr->X_add_number - 1);
+                 imm_expr->X_op = O_absent;
+                 asarg = expr_end;
+                 continue;
+
+               case 'm': /* optional vector mask */
+                 if (*asarg == '\0')
+                   {
+                     INSERT_OPERAND (VMASK, *ip, 1);
+                     continue;
+                   }
+                 else if (*asarg == ',' && asarg++
+                          && reg_lookup (&asarg, RCLASS_VECM, &regno)
+                          && regno == 0)
+                   {
+                     INSERT_OPERAND (VMASK, *ip, 0);
+                     continue;
+                   }
+                 break;
+
+               case 'M': /* required vector mask */
+                 if (reg_lookup (&asarg, RCLASS_VECM, &regno) && regno == 0)
+                   {
+                     INSERT_OPERAND (VMASK, *ip, 0);
+                     continue;
+                   }
+                 break;
+
+               case 'T': /* vector macro temporary register */
+                 if (!reg_lookup (&asarg, RCLASS_VECR, &regno) || regno == 0)
+                   break;
+                 /* Store it in the FUNCT6 field as we don't have anyplace
+                    else to store it.  */
+                 INSERT_OPERAND (VFUNCT6, *ip, regno);
+                 continue;
+
+               default:
+                 goto unknown_riscv_ip_operand;
+               }
+             break; /* end RVV */
 
            case ',':
-             ++argnum;
-             if (*s++ == *args)
+             if (*asarg++ == *oparg)
                continue;
-             s--;
+             asarg--;
              break;
 
            case '(':
            case ')':
            case '[':
            case ']':
-             if (*s++ == *args)
+             if (*asarg++ == *oparg)
                continue;
              break;
 
-           case '<':           /* Shift amount, 0 - 31.  */
-             my_getExpression (imm_expr, s);
-             check_absolute_expr (ip, imm_expr, FALSE);
+           case '<': /* Shift amount, 0 - 31.  */
+             my_getExpression (imm_expr, asarg);
+             check_absolute_expr (ip, imm_expr, false);
              if ((unsigned long) imm_expr->X_add_number > 31)
-               as_bad (_("Improper shift amount (%lu)"),
+               as_bad (_("improper shift amount (%lu)"),
                        (unsigned long) imm_expr->X_add_number);
              INSERT_OPERAND (SHAMTW, *ip, imm_expr->X_add_number);
              imm_expr->X_op = O_absent;
-             s = expr_end;
+             asarg = expr_end;
              continue;
 
-           case '>':           /* Shift amount, 0 - (XLEN-1).  */
-             my_getExpression (imm_expr, s);
-             check_absolute_expr (ip, imm_expr, FALSE);
+           case '>': /* Shift amount, 0 - (XLEN-1).  */
+             my_getExpression (imm_expr, asarg);
+             check_absolute_expr (ip, imm_expr, false);
              if ((unsigned long) imm_expr->X_add_number >= xlen)
-               as_bad (_("Improper shift amount (%lu)"),
+               as_bad (_("improper shift amount (%lu)"),
                        (unsigned long) imm_expr->X_add_number);
              INSERT_OPERAND (SHAMT, *ip, imm_expr->X_add_number);
              imm_expr->X_op = O_absent;
-             s = expr_end;
+             asarg = expr_end;
              continue;
 
-           case 'Z':           /* CSRRxI immediate.  */
-             my_getExpression (imm_expr, s);
-             check_absolute_expr (ip, imm_expr, FALSE);
+           case 'Z': /* CSRRxI immediate.  */
+             my_getExpression (imm_expr, asarg);
+             check_absolute_expr (ip, imm_expr, false);
              if ((unsigned long) imm_expr->X_add_number > 31)
-               as_bad (_("Improper CSRxI immediate (%lu)"),
+               as_bad (_("improper CSRxI immediate (%lu)"),
                        (unsigned long) imm_expr->X_add_number);
              INSERT_OPERAND (RS1, *ip, imm_expr->X_add_number);
              imm_expr->X_op = O_absent;
-             s = expr_end;
+             asarg = expr_end;
              continue;
 
-           case 'E':           /* Control register.  */
-             if (reg_lookup (&s, RCLASS_CSR, &regno))
+           case 'E': /* Control register.  */
+             insn_with_csr = true;
+             explicit_priv_attr = true;
+             if (reg_lookup (&asarg, RCLASS_CSR, &regno))
                INSERT_OPERAND (CSR, *ip, regno);
              else
                {
-                 my_getExpression (imm_expr, s);
-                 check_absolute_expr (ip, imm_expr, TRUE);
+                 my_getExpression (imm_expr, asarg);
+                 check_absolute_expr (ip, imm_expr, true);
                  if ((unsigned long) imm_expr->X_add_number > 0xfff)
-                   as_bad (_("Improper CSR address (%lu)"),
+                   as_bad (_("improper CSR address (%lu)"),
                            (unsigned long) imm_expr->X_add_number);
                  INSERT_OPERAND (CSR, *ip, imm_expr->X_add_number);
                  imm_expr->X_op = O_absent;
-                 s = expr_end;
+                 asarg = expr_end;
                }
              continue;
 
-           case 'm':           /* Rounding mode.  */
-             if (arg_lookup (&s, riscv_rm, ARRAY_SIZE (riscv_rm), &regno))
+           case 'm': /* Rounding mode.  */
+             if (arg_lookup (&asarg, riscv_rm,
+                             ARRAY_SIZE (riscv_rm), &regno))
                {
                  INSERT_OPERAND (RM, *ip, regno);
                  continue;
@@ -1799,11 +2942,11 @@ rvc_lui:
              break;
 
            case 'P':
-           case 'Q':           /* Fence predecessor/successor.  */
-             if (arg_lookup (&s, riscv_pred_succ, ARRAY_SIZE (riscv_pred_succ),
-                             &regno))
+           case 'Q': /* Fence predecessor/successor.  */
+             if (arg_lookup (&asarg, riscv_pred_succ,
+                             ARRAY_SIZE (riscv_pred_succ), &regno))
                {
-                 if (*args == 'P')
+                 if (*oparg == 'P')
                    INSERT_OPERAND (PRED, *ip, regno);
                  else
                    INSERT_OPERAND (SUCC, *ip, regno);
@@ -1811,15 +2954,15 @@ rvc_lui:
                }
              break;
 
-           case 'd':           /* Destination register.  */
-           case 's':           /* Source register.  */
-           case 't':           /* Target register.  */
-           case 'r':           /* rs3.  */
-             if (reg_lookup (&s, RCLASS_GPR, &regno))
+           case 'd': /* Destination register.  */
+           case 's': /* Source register.  */
+           case 't': /* Target register.  */
+           case 'r': /* RS3 */
+             if (reg_lookup (&asarg, RCLASS_GPR, &regno))
                {
-                 c = *args;
-                 if (*s == ' ')
-                   ++s;
+                 char c = *oparg;
+                 if (*asarg == ' ')
+                   ++asarg;
 
                  /* Now that we have assembled one operand, we use the args
                     string to figure out where it goes in the instruction.  */
@@ -1842,16 +2985,18 @@ rvc_lui:
                }
              break;
 
-           case 'D':           /* Floating point rd.  */
-           case 'S':           /* Floating point rs1.  */
-           case 'T':           /* Floating point rs2.  */
-           case 'U':           /* Floating point rs1 and rs2.  */
-           case 'R':           /* Floating point rs3.  */
-             if (reg_lookup (&s, RCLASS_FPR, &regno))
+           case 'D': /* Floating point RD.  */
+           case 'S': /* Floating point RS1.  */
+           case 'T': /* Floating point RS2.  */
+           case 'U': /* Floating point RS1 and RS2.  */
+           case 'R': /* Floating point RS3.  */
+             if (reg_lookup (&asarg,
+                             (riscv_subset_supports (&riscv_rps_as, "zfinx")
+                             ? RCLASS_GPR : RCLASS_FPR), &regno))
                {
-                 c = *args;
-                 if (*s == ' ')
-                   ++s;
+                 char c = *oparg;
+                 if (*asarg == ' ')
+                   ++asarg;
                  switch (c)
                    {
                    case 'D':
@@ -1862,7 +3007,7 @@ rvc_lui:
                      break;
                    case 'U':
                      INSERT_OPERAND (RS1, *ip, regno);
-                     /* fallthru */
+                     /* Fall through.  */
                    case 'T':
                      INSERT_OPERAND (RS2, *ip, regno);
                      break;
@@ -1872,37 +3017,36 @@ rvc_lui:
                    }
                  continue;
                }
-
              break;
 
            case 'I':
-             my_getExpression (imm_expr, s);
+             my_getExpression (imm_expr, asarg);
              if (imm_expr->X_op != O_big
                  && imm_expr->X_op != O_constant)
                break;
              normalize_constant_expr (imm_expr);
-             s = expr_end;
+             asarg = expr_end;
              continue;
 
            case 'A':
-             my_getExpression (imm_expr, s);
+             my_getExpression (imm_expr, asarg);
              normalize_constant_expr (imm_expr);
              /* The 'A' format specifier must be a symbol.  */
              if (imm_expr->X_op != O_symbol)
                break;
              *imm_reloc = BFD_RELOC_32;
-             s = expr_end;
+             asarg = expr_end;
              continue;
 
            case 'B':
-             my_getExpression (imm_expr, s);
+             my_getExpression (imm_expr, asarg);
              normalize_constant_expr (imm_expr);
              /* The 'B' format specifier must be a symbol or a constant.  */
              if (imm_expr->X_op != O_symbol && imm_expr->X_op != O_constant)
                break;
              if (imm_expr->X_op == O_symbol)
                *imm_reloc = BFD_RELOC_32;
-             s = expr_end;
+             asarg = expr_end;
              continue;
 
            case 'j': /* Sign-extended immediate.  */
@@ -1917,42 +3061,44 @@ rvc_lui:
              p = percent_op_itype;
              *imm_reloc = BFD_RELOC_RISCV_LO12_I;
              goto load_store;
-           case '1': /* 4-operand add, must be %tprel_add.  */
+           case '1':
+             /* This is used for TLS, where the fourth operand is
+                %tprel_add, to get a relocation applied to an add
+                instruction, for relaxation to use.  */
              p = percent_op_rtype;
              goto alu_op;
-           case '0': /* AMO "displacement," which must be zero.  */
+           case '0': /* AMO displacement, which must be zero.  */
              p = percent_op_null;
-load_store:
-             if (riscv_handle_implicit_zero_offset (imm_expr, s))
+           load_store:
+             if (riscv_handle_implicit_zero_offset (imm_expr, asarg))
                continue;
-alu_op:
+           alu_op:
              /* If this value won't fit into a 16 bit offset, then go
                 find a macro that will generate the 32 bit offset
                 code pattern.  */
-             if (!my_getSmallExpression (imm_expr, imm_reloc, s, p))
+             if (!my_getSmallExpression (imm_expr, imm_reloc, asarg, p))
                {
                  normalize_constant_expr (imm_expr);
                  if (imm_expr->X_op != O_constant
-                     || (*args == '0' && imm_expr->X_add_number != 0)
-                     || (*args == '1')
+                     || (*oparg == '0' && imm_expr->X_add_number != 0)
+                     || (*oparg == '1')
                      || imm_expr->X_add_number >= (signed)RISCV_IMM_REACH/2
                      || imm_expr->X_add_number < -(signed)RISCV_IMM_REACH/2)
                    break;
                }
-
-             s = expr_end;
+             asarg = expr_end;
              continue;
 
-           case 'p':           /* PC-relative offset.  */
-branch:
+           case 'p': /* PC-relative offset.  */
+           branch:
              *imm_reloc = BFD_RELOC_12_PCREL;
-             my_getExpression (imm_expr, s);
-             s = expr_end;
+             my_getExpression (imm_expr, asarg);
+             asarg = expr_end;
              continue;
 
-           case 'u':           /* Upper 20 bits.  */
+           case 'u': /* Upper 20 bits.  */
              p = percent_op_utype;
-             if (!my_getSmallExpression (imm_expr, imm_reloc, s, p))
+             if (!my_getSmallExpression (imm_expr, imm_reloc, asarg, p))
                {
                  if (imm_expr->X_op != O_constant)
                    break;
@@ -1964,32 +3110,29 @@ branch:
                  *imm_reloc = BFD_RELOC_RISCV_HI20;
                  imm_expr->X_add_number <<= RISCV_IMM_BITS;
                }
-             s = expr_end;
+             asarg = expr_end;
              continue;
 
-           case 'a':           /* 20-bit PC-relative offset.  */
-jump:
-             my_getExpression (imm_expr, s);
-             s = expr_end;
+           case 'a': /* 20-bit PC-relative offset.  */
+           jump:
+             my_getExpression (imm_expr, asarg);
+             asarg = expr_end;
              *imm_reloc = BFD_RELOC_RISCV_JMP;
              continue;
 
            case 'c':
-             my_getExpression (imm_expr, s);
-             s = expr_end;
-             if (strcmp (s, "@plt") == 0)
-               {
-                 *imm_reloc = BFD_RELOC_RISCV_CALL_PLT;
-                 s += 4;
-               }
-             else
-               *imm_reloc = BFD_RELOC_RISCV_CALL;
+             my_getExpression (imm_expr, asarg);
+             asarg = expr_end;
+             if (strcmp (asarg, "@plt") == 0)
+               asarg += 4;
+             *imm_reloc = BFD_RELOC_RISCV_CALL_PLT;
              continue;
+
            case 'O':
-             switch (*++args)
+             switch (*++oparg)
                {
                case '4':
-                 if (my_getOpcodeExpression (imm_expr, imm_reloc, s, p)
+                 if (my_getOpcodeExpression (imm_expr, imm_reloc, asarg, p)
                      || imm_expr->X_op != O_constant
                      || imm_expr->X_add_number < 0
                      || imm_expr->X_add_number >= 128
@@ -2000,13 +3143,13 @@ jump:
                                "lower 2 bits must be 0x3"));
                      break;
                    }
-
                  INSERT_OPERAND (OP, *ip, imm_expr->X_add_number);
                  imm_expr->X_op = O_absent;
-                 s = expr_end;
+                 asarg = expr_end;
                  continue;
+
                case '2':
-                 if (my_getOpcodeExpression (imm_expr, imm_reloc, s, p)
+                 if (my_getOpcodeExpression (imm_expr, imm_reloc, asarg, p)
                      || imm_expr->X_op != O_constant
                      || imm_expr->X_add_number < 0
                      || imm_expr->X_add_number >= 3)
@@ -2015,21 +3158,21 @@ jump:
                                "value must be 0...2"));
                      break;
                    }
-
                  INSERT_OPERAND (OP2, *ip, imm_expr->X_add_number);
                  imm_expr->X_op = O_absent;
-                 s = expr_end;
+                 asarg = expr_end;
                  continue;
+
                default:
-                 as_bad (_("bad Opcode field specifier 'O%c'\n"), *args);
+                 goto unknown_riscv_ip_operand;
                }
              break;
 
            case 'F':
-             switch (*++args)
+             switch (*++oparg)
                {
                case '7':
-                 if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
+                 if (my_getSmallExpression (imm_expr, imm_reloc, asarg, p)
                      || imm_expr->X_op != O_constant
                      || imm_expr->X_add_number < 0
                      || imm_expr->X_add_number >= 128)
@@ -2038,13 +3181,13 @@ jump:
                                "value must be 0...127"));
                      break;
                    }
-
                  INSERT_OPERAND (FUNCT7, *ip, imm_expr->X_add_number);
                  imm_expr->X_op = O_absent;
-                 s = expr_end;
+                 asarg = expr_end;
                  continue;
+
                case '3':
-                 if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
+                 if (my_getSmallExpression (imm_expr, imm_reloc, asarg, p)
                      || imm_expr->X_op != O_constant
                      || imm_expr->X_add_number < 0
                      || imm_expr->X_add_number >= 8)
@@ -2053,13 +3196,13 @@ jump:
                                "value must be 0...7"));
                      break;
                    }
-
                  INSERT_OPERAND (FUNCT3, *ip, imm_expr->X_add_number);
                  imm_expr->X_op = O_absent;
-                 s = expr_end;
+                 asarg = expr_end;
                  continue;
+
                case '2':
-                 if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
+                 if (my_getSmallExpression (imm_expr, imm_reloc, asarg, p)
                      || imm_expr->X_op != O_constant
                      || imm_expr->X_add_number < 0
                      || imm_expr->X_add_number >= 4)
@@ -2068,43 +3211,128 @@ jump:
                                "value must be 0...3"));
                      break;
                    }
-
                  INSERT_OPERAND (FUNCT2, *ip, imm_expr->X_add_number);
                  imm_expr->X_op = O_absent;
-                 s = expr_end;
+                 asarg = expr_end;
                  continue;
 
                default:
-                 as_bad (_("bad FUNCT field specifier 'F%c'\n"), *args);
+                 goto unknown_riscv_ip_operand;
                }
              break;
 
+           case 'y': /* bs immediate */
+             my_getExpression (imm_expr, asarg);
+             check_absolute_expr (ip, imm_expr, FALSE);
+             if ((unsigned long)imm_expr->X_add_number > 3)
+               as_bad(_("Improper bs immediate (%lu)"),
+                      (unsigned long)imm_expr->X_add_number);
+             INSERT_OPERAND(BS, *ip, imm_expr->X_add_number);
+             imm_expr->X_op = O_absent;
+             asarg = expr_end;
+             continue;
+
+           case 'Y': /* rnum immediate */
+             my_getExpression (imm_expr, asarg);
+             check_absolute_expr (ip, imm_expr, FALSE);
+             if ((unsigned long)imm_expr->X_add_number > 10)
+               as_bad(_("Improper rnum immediate (%lu)"),
+                      (unsigned long)imm_expr->X_add_number);
+             INSERT_OPERAND(RNUM, *ip, imm_expr->X_add_number);
+             imm_expr->X_op = O_absent;
+             asarg = expr_end;
+             continue;
+
            case 'z':
-             if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
+             if (my_getSmallExpression (imm_expr, imm_reloc, asarg, p)
                  || imm_expr->X_op != O_constant
                  || imm_expr->X_add_number != 0)
                break;
-             s = expr_end;
+             asarg = expr_end;
              imm_expr->X_op = O_absent;
              continue;
 
+           case 'f': /* Prefetch offset, pseudo S-type but lower 5-bits zero.  */
+             if (riscv_handle_implicit_zero_offset (imm_expr, asarg))
+               continue;
+             my_getExpression (imm_expr, asarg);
+             check_absolute_expr (ip, imm_expr, false);
+             if (((unsigned) (imm_expr->X_add_number) & 0x1fU)
+                 || imm_expr->X_add_number >= (signed) RISCV_IMM_REACH / 2
+                 || imm_expr->X_add_number < -(signed) RISCV_IMM_REACH / 2)
+               as_bad (_("improper prefetch offset (%ld)"),
+                       (long) imm_expr->X_add_number);
+             ip->insn_opcode |=
+               ENCODE_STYPE_IMM ((unsigned) (imm_expr->X_add_number) &
+                                 ~ 0x1fU);
+             imm_expr->X_op = O_absent;
+             asarg = expr_end;
+             continue;
+
            default:
-             as_fatal (_("internal error: bad argument type %c"), *args);
+           unknown_riscv_ip_operand:
+             as_fatal (_("internal: unknown argument type `%s'"),
+                       opargStart);
            }
          break;
        }
-      s = argsStart;
-      error = _("illegal operands");
+      asarg = asargStart;
+      insn_with_csr = false;
     }
 
-out:
+ out:
   /* Restore the character we might have clobbered above.  */
   if (save_c)
-    *(argsStart - 1) = save_c;
+    *(asargStart  - 1) = save_c;
 
   return error;
 }
 
+/* Similar to riscv_ip, but assembles an instruction according to the
+   hardcode values of .insn directive.  */
+
+static const char *
+riscv_ip_hardcode (char *str,
+                  struct riscv_cl_insn *ip,
+                  expressionS *imm_expr,
+                  const char *error)
+{
+  struct riscv_opcode *insn;
+  insn_t values[2] = {0, 0};
+  unsigned int num = 0;
+
+  input_line_pointer = str;
+  do
+    {
+      expression (imm_expr);
+      if (imm_expr->X_op != O_constant)
+       {
+         /* The first value isn't constant, so it should be
+            .insn <type> <operands>.  We have been parsed it
+            in the riscv_ip.  */
+         if (num == 0)
+           return error;
+         return _("values must be constant");
+       }
+      values[num++] = (insn_t) imm_expr->X_add_number;
+    }
+  while (*input_line_pointer++ == ',' && num < 2);
+
+  input_line_pointer--;
+  if (*input_line_pointer != '\0')
+    return _("unrecognized values");
+
+  insn = XNEW (struct riscv_opcode);
+  insn->match = values[num - 1];
+  create_insn (ip, insn);
+  unsigned int bytes = riscv_insn_length (insn->match);
+  if ((bytes < sizeof(values[0]) && values[num - 1] >> (8 * bytes) != 0)
+      || (num == 2 && values[0] != bytes))
+    return _("value conflicts with instruction length");
+
+  return NULL;
+}
+
 void
 md_assemble (char *str)
 {
@@ -2112,13 +3340,29 @@ md_assemble (char *str)
   expressionS imm_expr;
   bfd_reloc_code_real_type imm_reloc = BFD_RELOC_UNUSED;
 
-  const char *error = riscv_ip (str, &insn, &imm_expr, &imm_reloc, op_hash);
+  /* The architecture and privileged elf attributes should be set
+     before assembling.  */
+  if (!start_assemble)
+    {
+      start_assemble = true;
+
+      riscv_set_abi_by_arch ();
+      if (!riscv_set_default_priv_spec (NULL))
+       return;
+    }
+
+  riscv_mapping_state (MAP_INSN, 0);
 
-  start_assemble = TRUE;
+  const struct riscv_ip_error error = riscv_ip (str, &insn, &imm_expr,
+                                               &imm_reloc, op_hash);
 
-  if (error)
+  if (error.msg)
     {
-      as_bad ("%s `%s'", error, str);
+      if (error.missing_ext)
+       as_bad ("%s `%s', extension `%s' required", error.msg,
+               error.statement, error.missing_ext);
+      else
+       as_bad ("%s `%s'", error.msg, error.statement);
       return;
     }
 
@@ -2137,7 +3381,10 @@ md_atof (int type, char *litP, int *sizeP)
 void
 md_number_to_chars (char *buf, valueT val, int n)
 {
-  number_to_chars_littleendian (buf, val, n);
+  if (target_big_endian)
+    number_to_chars_bigendian (buf, val, n);
+  else
+    number_to_chars_littleendian (buf, val, n);
 }
 
 const char *md_shortopts = "O::g::G:";
@@ -2152,6 +3399,12 @@ enum options
   OPTION_NO_RELAX,
   OPTION_ARCH_ATTR,
   OPTION_NO_ARCH_ATTR,
+  OPTION_CSR_CHECK,
+  OPTION_NO_CSR_CHECK,
+  OPTION_MISA_SPEC,
+  OPTION_MPRIV_SPEC,
+  OPTION_BIG_ENDIAN,
+  OPTION_LITTLE_ENDIAN,
   OPTION_END_OF_ENUM
 };
 
@@ -2166,82 +3419,94 @@ struct option md_longopts[] =
   {"mno-relax", no_argument, NULL, OPTION_NO_RELAX},
   {"march-attr", no_argument, NULL, OPTION_ARCH_ATTR},
   {"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},
+  {"mpriv-spec", required_argument, NULL, OPTION_MPRIV_SPEC},
+  {"mbig-endian", no_argument, NULL, OPTION_BIG_ENDIAN},
+  {"mlittle-endian", no_argument, NULL, OPTION_LITTLE_ENDIAN},
 
   {NULL, no_argument, NULL, 0}
 };
 size_t md_longopts_size = sizeof (md_longopts);
 
-enum float_abi {
-  FLOAT_ABI_DEFAULT = -1,
-  FLOAT_ABI_SOFT,
-  FLOAT_ABI_SINGLE,
-  FLOAT_ABI_DOUBLE,
-  FLOAT_ABI_QUAD
-};
-static enum float_abi float_abi = FLOAT_ABI_DEFAULT;
-
-static void
-riscv_set_abi (unsigned new_xlen, enum float_abi new_float_abi, bfd_boolean rve)
-{
-  abi_xlen = new_xlen;
-  float_abi = new_float_abi;
-  rve_abi = rve;
-}
-
 int
 md_parse_option (int c, const char *arg)
 {
   switch (c)
     {
     case OPTION_MARCH:
-      riscv_set_arch (arg);
+      default_arch_with_ext = arg;
       break;
 
     case OPTION_NO_PIC:
-      riscv_opts.pic = FALSE;
+      riscv_opts.pic = false;
       break;
 
     case OPTION_PIC:
-      riscv_opts.pic = TRUE;
+      riscv_opts.pic = true;
       break;
 
     case OPTION_MABI:
       if (strcmp (arg, "ilp32") == 0)
-       riscv_set_abi (32, FLOAT_ABI_SOFT, FALSE);
+       riscv_set_abi (32, FLOAT_ABI_SOFT, false);
       else if (strcmp (arg, "ilp32e") == 0)
-       riscv_set_abi (32, FLOAT_ABI_SOFT, TRUE);
+       riscv_set_abi (32, FLOAT_ABI_SOFT, true);
       else if (strcmp (arg, "ilp32f") == 0)
-       riscv_set_abi (32, FLOAT_ABI_SINGLE, FALSE);
+       riscv_set_abi (32, FLOAT_ABI_SINGLE, false);
       else if (strcmp (arg, "ilp32d") == 0)
-       riscv_set_abi (32, FLOAT_ABI_DOUBLE, FALSE);
+       riscv_set_abi (32, FLOAT_ABI_DOUBLE, false);
       else if (strcmp (arg, "ilp32q") == 0)
-       riscv_set_abi (32, FLOAT_ABI_QUAD, FALSE);
+       riscv_set_abi (32, FLOAT_ABI_QUAD, false);
       else if (strcmp (arg, "lp64") == 0)
-       riscv_set_abi (64, FLOAT_ABI_SOFT, FALSE);
+       riscv_set_abi (64, FLOAT_ABI_SOFT, false);
       else if (strcmp (arg, "lp64f") == 0)
-       riscv_set_abi (64, FLOAT_ABI_SINGLE, FALSE);
+       riscv_set_abi (64, FLOAT_ABI_SINGLE, false);
       else if (strcmp (arg, "lp64d") == 0)
-       riscv_set_abi (64, FLOAT_ABI_DOUBLE, FALSE);
+       riscv_set_abi (64, FLOAT_ABI_DOUBLE, false);
       else if (strcmp (arg, "lp64q") == 0)
-       riscv_set_abi (64, FLOAT_ABI_QUAD, FALSE);
+       riscv_set_abi (64, FLOAT_ABI_QUAD, false);
       else
        return 0;
+      explicit_mabi = true;
       break;
 
     case OPTION_RELAX:
-      riscv_opts.relax = TRUE;
+      riscv_opts.relax = true;
       break;
 
     case OPTION_NO_RELAX:
-      riscv_opts.relax = FALSE;
+      riscv_opts.relax = false;
       break;
 
     case OPTION_ARCH_ATTR:
-      riscv_opts.arch_attr = TRUE;
+      riscv_opts.arch_attr = true;
       break;
 
     case OPTION_NO_ARCH_ATTR:
-      riscv_opts.arch_attr = FALSE;
+      riscv_opts.arch_attr = false;
+      break;
+
+    case OPTION_CSR_CHECK:
+      riscv_opts.csr_check = true;
+      break;
+
+    case OPTION_NO_CSR_CHECK:
+      riscv_opts.csr_check = false;
+      break;
+
+    case OPTION_MISA_SPEC:
+      return riscv_set_default_isa_spec (arg);
+
+    case OPTION_MPRIV_SPEC:
+      return riscv_set_default_priv_spec (arg);
+
+    case OPTION_BIG_ENDIAN:
+      target_big_endian = 1;
+      break;
+
+    case OPTION_LITTLE_ENDIAN:
+      target_big_endian = 0;
       break;
 
     default:
@@ -2254,6 +3519,8 @@ md_parse_option (int c, const char *arg)
 void
 riscv_after_parse_args (void)
 {
+  /* The --with-arch is optional for now, so we still need to set the xlen
+     according to the default_arch, which is set by the --target.  */
   if (xlen == 0)
     {
       if (strcmp (default_arch, "riscv32") == 0)
@@ -2264,48 +3531,19 @@ riscv_after_parse_args (void)
        as_bad ("unknown default architecture `%s'", default_arch);
     }
 
-  if (riscv_subsets.head == NULL)
-    riscv_set_arch (xlen == 64 ? "rv64g" : "rv32g");
-
-  /* Add the RVC extension, regardless of -march, to support .option rvc.  */
-  riscv_set_rvc (FALSE);
-  if (riscv_subset_supports ("c"))
-    riscv_set_rvc (TRUE);
-
-  /* Enable RVE if specified by the -march option.  */
-  riscv_set_rve (FALSE);
-  if (riscv_subset_supports ("e"))
-    riscv_set_rve (TRUE);
-
-  /* Infer ABI from ISA if not specified on command line.  */
-  if (abi_xlen == 0)
-    abi_xlen = xlen;
-  else if (abi_xlen > xlen)
-    as_bad ("can't have %d-bit ABI on %d-bit ISA", abi_xlen, xlen);
-  else if (abi_xlen < xlen)
-    as_bad ("%d-bit ABI not yet supported on %d-bit ISA", abi_xlen, xlen);
-
-  if (float_abi == FLOAT_ABI_DEFAULT)
-    {
-      riscv_subset_t *subset;
-
-      /* Assume soft-float unless D extension is present.  */
-      float_abi = FLOAT_ABI_SOFT;
-
-      for (subset = riscv_subsets.head; subset != NULL; subset = subset->next)
-       {
-         if (strcasecmp (subset->name, "D") == 0)
-           float_abi = FLOAT_ABI_DOUBLE;
-         if (strcasecmp (subset->name, "Q") == 0)
-           float_abi = FLOAT_ABI_QUAD;
-       }
-    }
+  /* Set default specs.  */
+  if (default_isa_spec == ISA_SPEC_CLASS_NONE)
+    riscv_set_default_isa_spec (DEFAULT_RISCV_ISA_SPEC);
+  if (default_priv_spec == PRIV_SPEC_CLASS_NONE)
+    riscv_set_default_priv_spec (DEFAULT_RISCV_PRIV_SPEC);
 
-  if (rve_abi)
-    elf_flags |= EF_RISCV_RVE;
+  riscv_set_arch (default_arch_with_ext);
 
-  /* Insert float_abi into the EF_RISCV_FLOAT_ABI field of elf_flags.  */
-  elf_flags |= float_abi * (EF_RISCV_FLOAT_ABI & ~(EF_RISCV_FLOAT_ABI << 1));
+  /* If the CIE to be produced has not been overridden on the command line,
+     then produce version 3 by default.  This allows us to use the full
+     range of registers in a .cfi_return_column directive.  */
+  if (flag_dwarf_cie_version == -1)
+    flag_dwarf_cie_version = 3;
 }
 
 long
@@ -2321,7 +3559,7 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
 {
   unsigned int subtype;
   bfd_byte *buf = (bfd_byte *) (fixP->fx_frag->fr_literal + fixP->fx_where);
-  bfd_boolean relaxable = FALSE;
+  bool relaxable = false;
   offsetT loc;
   segT sub_segment;
 
@@ -2336,8 +3574,8 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
       bfd_putl32 (riscv_apply_const_reloc (fixP->fx_r_type, *valP)
                  | bfd_getl32 (buf), buf);
       if (fixP->fx_addsy == NULL)
-       fixP->fx_done = TRUE;
-      relaxable = TRUE;
+       fixP->fx_done = true;
+      relaxable = true;
       break;
 
     case BFD_RELOC_RISCV_GOT_HI20:
@@ -2357,7 +3595,7 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
     case BFD_RELOC_RISCV_TPREL_LO12_I:
     case BFD_RELOC_RISCV_TPREL_LO12_S:
     case BFD_RELOC_RISCV_TPREL_ADD:
-      relaxable = TRUE;
+      relaxable = true;
       /* Fall through.  */
 
     case BFD_RELOC_RISCV_TLS_GOT_HI20:
@@ -2377,7 +3615,7 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
         _bfd_elf_discard_section_eh_frame, and the content of
         .eh_frame will be adjusted in _bfd_elf_write_section_eh_frame.
         Therefore, we cannot insert a relocation whose addend symbol is
-        in .eh_frame. Othrewise, the value may be adjusted twice.*/
+        in .eh_frame.  Othrewise, the value may be adjusted twice.  */
       if (fixP->fx_addsy && fixP->fx_subsy
          && (sub_segment = S_GET_SEGMENT (fixP->fx_subsy))
          && strcmp (sub_segment->name, ".eh_frame") == 0
@@ -2464,7 +3702,7 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
                      fixP->fx_next->fx_r_type = BFD_RELOC_RISCV_SUB6;
                    }
                  else
-                   as_fatal (_("internal error: bad CFA value #%d"), subtype);
+                   as_fatal (_("internal: bad CFA value #%d"), subtype);
                  break;
                }
              break;
@@ -2494,7 +3732,7 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
          /* Fill in a tentative value to improve objdump readability.  */
          bfd_vma target = S_GET_VALUE (fixP->fx_addsy) + *valP;
          bfd_vma delta = target - md_pcrel_from (fixP);
-         bfd_putl32 (bfd_getl32 (buf) | ENCODE_UJTYPE_IMM (delta), buf);
+         bfd_putl32 (bfd_getl32 (buf) | ENCODE_JTYPE_IMM (delta), buf);
        }
       break;
 
@@ -2504,7 +3742,7 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
          /* Fill in a tentative value to improve objdump readability.  */
          bfd_vma target = S_GET_VALUE (fixP->fx_addsy) + *valP;
          bfd_vma delta = target - md_pcrel_from (fixP);
-         bfd_putl32 (bfd_getl32 (buf) | ENCODE_SBTYPE_IMM (delta), buf);
+         bfd_putl32 (bfd_getl32 (buf) | ENCODE_BTYPE_IMM (delta), buf);
        }
       break;
 
@@ -2514,7 +3752,7 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
          /* Fill in a tentative value to improve objdump readability.  */
          bfd_vma target = S_GET_VALUE (fixP->fx_addsy) + *valP;
          bfd_vma delta = target - md_pcrel_from (fixP);
-         bfd_putl16 (bfd_getl16 (buf) | ENCODE_RVC_B_IMM (delta), buf);
+         bfd_putl16 (bfd_getl16 (buf) | ENCODE_CBTYPE_IMM (delta), buf);
        }
       break;
 
@@ -2524,13 +3762,13 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
          /* Fill in a tentative value to improve objdump readability.  */
          bfd_vma target = S_GET_VALUE (fixP->fx_addsy) + *valP;
          bfd_vma delta = target - md_pcrel_from (fixP);
-         bfd_putl16 (bfd_getl16 (buf) | ENCODE_RVC_J_IMM (delta), buf);
+         bfd_putl16 (bfd_getl16 (buf) | ENCODE_CJTYPE_IMM (delta), buf);
        }
       break;
 
     case BFD_RELOC_RISCV_CALL:
     case BFD_RELOC_RISCV_CALL_PLT:
-      relaxable = TRUE;
+      relaxable = true;
       break;
 
     case BFD_RELOC_RISCV_PCREL_HI20:
@@ -2545,12 +3783,11 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
     default:
       /* We ignore generic BFD relocations we don't know about.  */
       if (bfd_reloc_type_lookup (stdoutput, fixP->fx_r_type) != NULL)
-       as_fatal (_("internal error: bad relocation #%d"), fixP->fx_r_type);
+       as_fatal (_("internal: bad relocation #%d"), fixP->fx_r_type);
     }
 
   if (fixP->fx_subsy != NULL)
-    as_bad_where (fixP->fx_file, fixP->fx_line,
-                 _("unsupported symbol subtraction"));
+    as_bad_subtract (fixP);
 
   /* Add an R_RISCV_RELAX reloc if the reloc is relaxable.  */
   if (relaxable && fixP->fx_tcbit && fixP->fx_addsy != NULL)
@@ -2558,6 +3795,7 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
       fixP->fx_next = xmemdup (fixP, sizeof (*fixP), sizeof (*fixP));
       fixP->fx_next->fx_addsy = fixP->fx_next->fx_subsy = NULL;
       fixP->fx_next->fx_r_type = BFD_RELOC_RISCV_RELAX;
+      fixP->fx_next->fx_size = 0;
     }
 }
 
@@ -2568,7 +3806,11 @@ void
 riscv_pre_output_hook (void)
 {
   const frchainS *frch;
-  const asection *s;
+  segT s;
+
+  /* Save the current segment info.  */
+  segT seg = now_seg;
+  subsegT subseg = now_subseg;
 
   for (s = stdoutput->sections; s; s = s->next)
     for (frch = seg_info (s)->frchainP; frch; frch = frch->frch_next)
@@ -2588,23 +3830,19 @@ riscv_pre_output_hook (void)
                exp.X_add_number = 0;
                exp.X_op_symbol = symval->X_op_symbol;
 
+               /* We must set the segment before creating a frag after all
+                  frag chains have been chained together.  */
+               subseg_set (s, frch->frch_subseg);
+
                fix_new_exp (frag, (int) frag->fr_offset, 1, &exp, 0,
                             BFD_RELOC_RISCV_CFA);
              }
          }
       }
-}
-
-
-/* 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;
+  /* Restore the original segment info.  */
+  subseg_set (seg, subseg);
+}
 
 /* Handle the .option pseudo-op.  */
 
@@ -2619,25 +3857,52 @@ s_riscv_option (int x ATTRIBUTE_UNUSED)
   *input_line_pointer = '\0';
 
   if (strcmp (name, "rvc") == 0)
-    riscv_set_rvc (TRUE);
+    {
+      riscv_update_subset (&riscv_rps_as, "+c");
+      riscv_set_rvc (true);
+    }
   else if (strcmp (name, "norvc") == 0)
-    riscv_set_rvc (FALSE);
+    {
+      riscv_update_subset (&riscv_rps_as, "-c");
+      riscv_set_rvc (false);
+    }
   else if (strcmp (name, "pic") == 0)
-    riscv_opts.pic = TRUE;
+    riscv_opts.pic = true;
   else if (strcmp (name, "nopic") == 0)
-    riscv_opts.pic = FALSE;
+    riscv_opts.pic = false;
   else if (strcmp (name, "relax") == 0)
-    riscv_opts.relax = TRUE;
+    riscv_opts.relax = true;
   else if (strcmp (name, "norelax") == 0)
-    riscv_opts.relax = FALSE;
+    riscv_opts.relax = false;
+  else if (strcmp (name, "csr-check") == 0)
+    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 (&riscv_rps_as, name);
+
+      riscv_set_rvc (false);
+      if (riscv_subset_supports (&riscv_rps_as, "c"))
+       riscv_set_rvc (true);
+
+      if (riscv_subset_supports (&riscv_rps_as, "ztso"))
+       riscv_set_tso ();
+    }
   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);
+      riscv_rps_as.subset_list = riscv_subsets;
     }
   else if (strcmp (name, "pop") == 0)
     {
@@ -2648,14 +3913,18 @@ 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_rps_as.subset_list = riscv_subsets;
+         riscv_release_subset_list (release_subsets);
          free (s);
        }
     }
   else
     {
-      as_warn (_("Unrecognized .option directive: %s\n"), name);
+      as_warn (_("unrecognized .option directive: %s\n"), name);
     }
   *input_line_pointer = ch;
   demand_empty_rest_of_line ();
@@ -2675,7 +3944,7 @@ s_dtprel (int bytes)
 
   if (ex.X_op != O_symbol)
     {
-      as_bad (_("Unsupported use of %s"), (bytes == 8
+      as_bad (_("unsupported use of %s"), (bytes == 8
                                           ? ".dtpreldword"
                                           : ".dtprelword"));
       ignore_rest_of_line ();
@@ -2683,7 +3952,7 @@ s_dtprel (int bytes)
 
   p = frag_more (bytes);
   md_number_to_chars (p, 0, bytes);
-  fix_new_exp (frag_now, p - frag_now->fr_literal, bytes, &ex, FALSE,
+  fix_new_exp (frag_now, p - frag_now->fr_literal, bytes, &ex, false,
               (bytes == 8
                ? BFD_RELOC_RISCV_TLS_DTPREL64
                : BFD_RELOC_RISCV_TLS_DTPREL32));
@@ -2714,13 +3983,13 @@ riscv_make_nops (char *buf, bfd_vma bytes)
   /* Use at most one 2-byte NOP.  */
   if ((bytes - i) % 4 == 2)
     {
-      md_number_to_chars (buf + i, RVC_NOP, 2);
+      number_to_chars_littleendian (buf + i, RVC_NOP, 2);
       i += 2;
     }
 
   /* Fill the remainder with 4-byte NOPs.  */
   for ( ; i < bytes; i += 4)
-    md_number_to_chars (buf + i, RISCV_NOP, 4);
+    number_to_chars_littleendian (buf + i, RISCV_NOP, 4);
 }
 
 /* Called from md_do_align.  Used to create an alignment frag in a
@@ -2728,7 +3997,7 @@ riscv_make_nops (char *buf, bfd_vma bytes)
    will later relax to the correct number of NOPs.  We can't compute
    the correct alignment now because of other linker relaxations.  */
 
-bfd_boolean
+bool
 riscv_frag_align_code (int n)
 {
   bfd_vma bytes = (bfd_vma) 1 << n;
@@ -2740,11 +4009,11 @@ riscv_frag_align_code (int n)
   /* If we are moving to a smaller alignment than the instruction size, then no
      alignment is required. */
   if (bytes <= insn_alignment)
-    return TRUE;
+    return true;
 
   /* When not relaxing, riscv_handle_align handles code alignment.  */
   if (!riscv_opts.relax)
-    return FALSE;
+    return false;
 
   nops = frag_more (worst_case_bytes);
 
@@ -2754,9 +4023,17 @@ riscv_frag_align_code (int n)
   riscv_make_nops (nops, worst_case_bytes);
 
   fix_new_exp (frag_now, nops - frag_now->fr_literal, 0,
-              &ex, FALSE, BFD_RELOC_RISCV_ALIGN);
+              &ex, false, BFD_RELOC_RISCV_ALIGN);
+
+  riscv_mapping_state (MAP_INSN, worst_case_bytes);
+
+  /* We need to start a new frag after the alignment which may be removed by
+     the linker, to prevent the assembler from computing static offsets.
+     This is necessary to get correct EH info.  */
+  frag_wane (frag_now);
+  frag_new (0);
 
-  return TRUE;
+  return true;
 }
 
 /* Implement HANDLE_ALIGN.  */
@@ -2775,6 +4052,7 @@ riscv_handle_align (fragS *fragP)
          /* We have 4 byte uncompressed nops.  */
          bfd_signed_vma size = 4;
          bfd_signed_vma excess = bytes % size;
+         bfd_boolean odd_padding = (excess % 2 == 1);
          char *p = fragP->fr_literal + fragP->fr_fix;
 
          if (bytes <= 0)
@@ -2783,12 +4061,20 @@ riscv_handle_align (fragS *fragP)
          /* Insert zeros or compressed nops to get 4 byte alignment.  */
          if (excess)
            {
+             if (odd_padding)
+               riscv_add_odd_padding_symbol (fragP);
              riscv_make_nops (p, excess);
              fragP->fr_fix += excess;
              p += excess;
            }
 
-         /* Insert variable number of 4 byte uncompressed nops.  */
+         /* The frag will be changed to `rs_fill` later.  The function
+            `write_contents` will try to fill the remaining spaces
+            according to the patterns we give.  In this case, we give
+            a 4 byte uncompressed nop as the pattern, and set the size
+            of the pattern into `fr_var`.  The nop will be output to the
+            file `fr_offset` times.  However, `fr_offset` could be zero
+            if we don't need to pad the boundary finally.  */
          riscv_make_nops (p, size);
          fragP->fr_var = size;
        }
@@ -2799,10 +4085,34 @@ riscv_handle_align (fragS *fragP)
     }
 }
 
+/* This usually called from frag_var.  */
+
+void
+riscv_init_frag (fragS * fragP, int max_chars)
+{
+  /* Do not add mapping symbol to debug sections.  */
+  if (bfd_section_flags (now_seg) & SEC_DEBUGGING)
+    return;
+
+  switch (fragP->fr_type)
+    {
+    case rs_fill:
+    case rs_align:
+    case rs_align_test:
+      riscv_mapping_state (MAP_DATA, max_chars);
+      break;
+    case rs_align_code:
+      riscv_mapping_state (MAP_INSN, max_chars);
+      break;
+    default:
+      break;
+    }
+}
+
 int
 md_estimate_size_before_relax (fragS *fragp, asection *segtype)
 {
-  return (fragp->fr_var = relaxed_branch_length (fragp, segtype, FALSE));
+  return (fragp->fr_var = relaxed_branch_length (fragp, segtype, false));
 }
 
 /* Translate internal representation of relocation info to BFD target
@@ -2844,7 +4154,7 @@ riscv_relax_frag (asection *sec, fragS *fragp, long stretch ATTRIBUTE_UNUSED)
   if (RELAX_BRANCH_P (fragp->fr_subtype))
     {
       offsetT old_var = fragp->fr_var;
-      fragp->fr_var = relaxed_branch_length (fragp, sec, TRUE);
+      fragp->fr_var = relaxed_branch_length (fragp, sec, true);
       return fragp->fr_var - old_var;
     }
 
@@ -2896,7 +4206,7 @@ md_convert_frag_branch (fragS *fragp)
            /* Invert the branch condition.  Branch over the jump.  */
            insn = bfd_getl16 (buf);
            insn ^= MATCH_C_BEQZ ^ MATCH_C_BNEZ;
-           insn |= ENCODE_RVC_B_IMM (6);
+           insn |= ENCODE_CBTYPE_IMM (6);
            bfd_putl16 (insn, buf);
            buf += 2;
            goto jump;
@@ -2906,7 +4216,7 @@ md_convert_frag_branch (fragS *fragp)
            reloc = RELAX_BRANCH_UNCOND (fragp->fr_subtype)
                    ? BFD_RELOC_RISCV_RVC_JUMP : BFD_RELOC_RISCV_RVC_BRANCH;
            fixp = fix_new_exp (fragp, buf - (bfd_byte *)fragp->fr_literal,
-                               2, &exp, FALSE, reloc);
+                               2, &exp, false, reloc);
            buf += 2;
            goto done;
 
@@ -2923,15 +4233,15 @@ md_convert_frag_branch (fragS *fragp)
       /* Invert the branch condition.  Branch over the jump.  */
       insn = bfd_getl32 (buf);
       insn ^= MATCH_BEQ ^ MATCH_BNE;
-      insn |= ENCODE_SBTYPE_IMM (8);
-      md_number_to_chars ((char *) buf, insn, 4);
+      insn |= ENCODE_BTYPE_IMM (8);
+      bfd_putl32 (insn, buf);
       buf += 4;
 
-jump:
+    jump:
       /* Jump to the target.  */
       fixp = fix_new_exp (fragp, buf - (bfd_byte *)fragp->fr_literal,
-                         4, &exp, FALSE, BFD_RELOC_RISCV_JMP);
-      md_number_to_chars ((char *) buf, MATCH_JAL, 4);
+                         4, &exp, false, BFD_RELOC_RISCV_JMP);
+      bfd_putl32 (MATCH_JAL, buf);
       buf += 4;
       break;
 
@@ -2939,7 +4249,7 @@ jump:
       reloc = RELAX_BRANCH_UNCOND (fragp->fr_subtype)
              ? BFD_RELOC_RISCV_JMP : BFD_RELOC_12_PCREL;
       fixp = fix_new_exp (fragp, buf - (bfd_byte *)fragp->fr_literal,
-                         4, &exp, FALSE, reloc);
+                         4, &exp, false, reloc);
       buf += 4;
       break;
 
@@ -2947,7 +4257,7 @@ jump:
       abort ();
     }
 
-done:
+ done:
   fixp->fx_file = fragp->fr_file;
   fixp->fx_line = fragp->fr_line;
 
@@ -2973,18 +4283,25 @@ md_show_usage (FILE *stream)
 {
   fprintf (stream, _("\
 RISC-V options:\n\
-  -fpic          generate position-independent code\n\
-  -fno-pic       don't generate position-independent code (default)\n\
-  -march=ISA     set the RISC-V architecture\n\
-  -mabi=ABI      set the RISC-V ABI\n\
-  -mrelax        enable relax (default)\n\
-  -mno-relax     disable relax\n\
-  -march-attr    generate RISC-V arch attribute\n\
-  -mno-arch-attr don't generate RISC-V arch attribute\n\
+  -fpic or -fPIC              generate position-independent code\n\
+  -fno-pic                    don't generate position-independent code (default)\n\
+  -march=ISA                  set the RISC-V architecture\n\
+  -misa-spec=ISAspec          set the RISC-V ISA spec (2.2, 20190608, 20191213)\n\
+  -mpriv-spec=PRIVspec        set the RISC-V privilege spec (1.9.1, 1.10, 1.11, 1.12)\n\
+  -mabi=ABI                   set the RISC-V ABI\n\
+  -mrelax                     enable relax (default)\n\
+  -mno-relax                  disable relax\n\
+  -march-attr                 generate RISC-V arch attribute\n\
+  -mno-arch-attr              don't generate RISC-V arch attribute\n\
+  -mcsr-check                 enable the csr ISA and privilege spec version checks\n\
+  -mno-csr-check              disable the csr ISA and privilege spec version checks (default)\n\
+  -mbig-endian                assemble for big-endian\n\
+  -mlittle-endian             assemble for little-endian\n\
 "));
 }
 
 /* Standard calling conventions leave the CFA at SP on entry.  */
+
 void
 riscv_cfi_frame_initial_instructions (void)
 {
@@ -3002,6 +4319,10 @@ tc_riscv_regname_to_dw2regnum (char *regname)
   if ((reg = reg_lookup_internal (regname, RCLASS_FPR)) >= 0)
     return reg + 32;
 
+  /* CSRs are numbered 4096 -> 8191.  */
+  if ((reg = reg_lookup_internal (regname, RCLASS_CSR)) >= 0)
+    return reg + 4096;
+
   as_bad (_("unknown register `%s'"), regname);
   return -1;
 }
@@ -3009,6 +4330,7 @@ tc_riscv_regname_to_dw2regnum (char *regname)
 void
 riscv_elf_final_processing (void)
 {
+  riscv_set_abi_by_arch ();
   elf_elfheader (stdoutput)->e_flags |= elf_flags;
 }
 
@@ -3030,7 +4352,10 @@ s_riscv_leb128 (int sign)
   return s_leb128 (sign);
 }
 
-/* Parse the .insn directive.  */
+/* Parse the .insn directive.  There are three formats,
+   Format 1: .insn <type> <operand1>, <operand2>, ...
+   Format 2: .insn <length>, <value>
+   Format 3: .insn <value>.  */
 
 static void
 s_riscv_insn (int x ATTRIBUTE_UNUSED)
@@ -3047,12 +4372,24 @@ s_riscv_insn (int x ATTRIBUTE_UNUSED)
   save_c = *input_line_pointer;
   *input_line_pointer = '\0';
 
-  const char *error = riscv_ip (str, &insn, &imm_expr,
+  riscv_mapping_state (MAP_INSN, 0);
+
+  struct riscv_ip_error error = riscv_ip (str, &insn, &imm_expr,
                                &imm_reloc, insn_type_hash);
+  if (error.msg)
+    {
+      char *save_in = input_line_pointer;
+      error.msg = riscv_ip_hardcode (str, &insn, &imm_expr, error.msg);
+      input_line_pointer = save_in;
+    }
 
-  if (error)
+  if (error.msg)
     {
-      as_bad ("%s `%s'", error, str);
+      if (error.missing_ext)
+       as_bad ("%s `%s', extension `%s' required", error.msg, error.statement,
+               error.missing_ext);
+      else
+       as_bad ("%s `%s'", error.msg, error.statement);
     }
   else
     {
@@ -3064,16 +4401,60 @@ s_riscv_insn (int x ATTRIBUTE_UNUSED)
   demand_empty_rest_of_line ();
 }
 
-/* Update arch attributes.  */
+/* Update architecture and privileged elf attributes.  If we don't set
+   them, then try to output the default ones.  */
 
 static void
-riscv_write_out_arch_attr (void)
+riscv_write_out_attrs (void)
 {
-  const char *arch_str = riscv_arch_str (xlen, &riscv_subsets);
+  const char *arch_str, *priv_str, *p;
+  /* versions[0]: major version.
+     versions[1]: minor version.
+     versions[2]: revision version.  */
+  unsigned versions[3] = {0}, number = 0;
+  unsigned int i;
 
+  /* Re-write architecture elf attribute.  */
+  arch_str = riscv_arch_str (xlen, riscv_subsets);
   bfd_elf_add_proc_attr_string (stdoutput, Tag_RISCV_arch, arch_str);
+  xfree ((void *) arch_str);
+
+  /* For the file without any instruction, we don't set the default_priv_spec
+     according to the privileged elf attributes since the md_assemble isn't
+     called.  */
+  if (!start_assemble
+      && !riscv_set_default_priv_spec (NULL))
+    return;
+
+  /* If we already have set privileged elf attributes, then no need to do
+     anything.  Otherwise, don't generate or update them when no CSR and
+     privileged instructions are used.  */
+  if (!explicit_priv_attr)
+    return;
+
+  RISCV_GET_PRIV_SPEC_NAME (priv_str, default_priv_spec);
+  p = priv_str;
+  for (i = 0; *p; ++p)
+    {
+      if (*p == '.' && i < 3)
+       {
+         versions[i++] = number;
+         number = 0;
+       }
+      else if (ISDIGIT (*p))
+       number = (number * 10) + (*p - '0');
+      else
+       {
+         as_bad (_("internal: bad RISC-V privileged spec (%s)"), priv_str);
+         return;
+       }
+    }
+  versions[i] = number;
 
-  xfree ((void *)arch_str);
+  /* Re-write privileged elf attributes.  */
+  bfd_elf_add_proc_attr_int (stdoutput, Tag_RISCV_priv_spec, versions[0]);
+  bfd_elf_add_proc_attr_int (stdoutput, Tag_RISCV_priv_spec_minor, versions[1]);
+  bfd_elf_add_proc_attr_int (stdoutput, Tag_RISCV_priv_spec_revision, versions[2]);
 }
 
 /* Add the default contents for the .riscv.attributes section.  */
@@ -3081,19 +4462,27 @@ riscv_write_out_arch_attr (void)
 static void
 riscv_set_public_attributes (void)
 {
-  if (riscv_opts.arch_attr || explicit_arch_attr)
-    /* Re-write arch attribute to normalize the arch string.  */
-    riscv_write_out_arch_attr ();
+  if (riscv_opts.arch_attr || explicit_attr)
+    riscv_write_out_attrs ();
 }
 
 /* Called after all assembly has been done.  */
 
 void
-riscv_md_end (void)
+riscv_md_finish (void)
 {
   riscv_set_public_attributes ();
 }
 
+/* Adjust the symbol table.  */
+
+void
+riscv_adjust_symtab (void)
+{
+  bfd_map_over_sections (stdoutput, riscv_check_mapping_symbols, (char *) 0);
+  elf_adjust_symtab ();
+}
+
 /* Given a symbolic attribute NAME, return the proper integer value.
    Returns -1 if the attribute is not known.  */
 
@@ -3102,28 +4491,27 @@ riscv_convert_symbolic_attribute (const char *name)
 {
   static const struct
   {
-    const char * name;
-    const int    tag;
+    const char *name;
+    const int tag;
   }
   attribute_table[] =
-    {
-      /* When you modify this table you should
-        also modify the list in doc/c-riscv.texi.  */
-#define T(tag) {#tag, Tag_RISCV_##tag},  {"Tag_RISCV_" #tag, Tag_RISCV_##tag}
-      T(arch),
-      T(priv_spec),
-      T(priv_spec_minor),
-      T(priv_spec_revision),
-      T(unaligned_access),
-      T(stack_align),
+  {
+    /* When you modify this table you should
+       also modify the list in doc/c-riscv.texi.  */
+#define T(tag) {#tag, Tag_RISCV_##tag}, {"Tag_RISCV_" #tag, Tag_RISCV_##tag}
+    T(arch),
+    T(priv_spec),
+    T(priv_spec_minor),
+    T(priv_spec_revision),
+    T(unaligned_access),
+    T(stack_align),
 #undef T
-    };
-
-  unsigned int i;
+  };
 
   if (name == NULL)
     return -1;
 
+  unsigned int i;
   for (i = 0; i < ARRAY_SIZE (attribute_table); i++)
     if (strcmp (name, attribute_table[i].name) == 0)
       return attribute_table[i].tag;
@@ -3137,18 +4525,20 @@ static void
 s_riscv_attribute (int ignored ATTRIBUTE_UNUSED)
 {
   int tag = obj_elf_vendor_attribute (OBJ_ATTR_PROC);
+  unsigned old_xlen;
+  obj_attribute *attr;
 
-  if (tag == Tag_RISCV_arch)
+  explicit_attr = true;
+  switch (tag)
     {
-      unsigned old_xlen = xlen;
-
-      explicit_arch_attr = TRUE;
-      obj_attribute *attr;
+    case Tag_RISCV_arch:
+      old_xlen = xlen;
       attr = elf_known_obj_attributes_proc (stdoutput);
       if (!start_assemble)
        riscv_set_arch (attr[Tag_RISCV_arch].s);
       else
-       as_fatal (_(".attribute arch must set before any instructions"));
+       as_fatal (_("architecture elf attributes must set before "
+                   "any instructions"));
 
       if (old_xlen != xlen)
        {
@@ -3157,16 +4547,79 @@ s_riscv_attribute (int ignored ATTRIBUTE_UNUSED)
          bfd_find_target (riscv_target_format (), stdoutput);
 
          if (! bfd_set_arch_mach (stdoutput, bfd_arch_riscv, mach))
-           as_warn (_("Could not set architecture and machine"));
+           as_warn (_("could not set architecture and machine"));
        }
+      break;
+
+    case Tag_RISCV_priv_spec:
+    case Tag_RISCV_priv_spec_minor:
+    case Tag_RISCV_priv_spec_revision:
+      if (start_assemble)
+       as_fatal (_("privileged elf attributes must set before "
+                  "any instructions"));
+      break;
+
+    default:
+      break;
     }
 }
 
-/* Pseudo-op table.  */
+/* Mark symbol that it follows a variant CC convention.  */
+
+static void
+s_variant_cc (int ignored ATTRIBUTE_UNUSED)
+{
+  char *name;
+  char c;
+  symbolS *sym;
+  asymbol *bfdsym;
+  elf_symbol_type *elfsym;
+
+  c = get_symbol_name (&name);
+  if (!*name)
+    as_bad (_("missing symbol name for .variant_cc directive"));
+  sym = symbol_find_or_make (name);
+  restore_line_pointer (c);
+  demand_empty_rest_of_line ();
+
+  bfdsym = symbol_get_bfdsym (sym);
+  elfsym = elf_symbol_from (bfdsym);
+  gas_assert (elfsym);
+  elfsym->internal_elf_sym.st_other |= STO_RISCV_VARIANT_CC;
+}
+
+/* Same as elf_copy_symbol_attributes, but without copying st_other.
+   This is needed so RISC-V specific st_other values can be independently
+   specified for an IFUNC resolver (that is called by the dynamic linker)
+   and the symbol it resolves (aliased to the resolver).  In particular,
+   if a function symbol has special st_other value set via directives,
+   then attaching an IFUNC resolver to that symbol should not override
+   the st_other setting.  Requiring the directive on the IFUNC resolver
+   symbol would be unexpected and problematic in C code, where the two
+   symbols appear as two independent function declarations.  */
+
+void
+riscv_elf_copy_symbol_attributes (symbolS *dest, symbolS *src)
+{
+  struct elf_obj_sy *srcelf = symbol_get_obj (src);
+  struct elf_obj_sy *destelf = symbol_get_obj (dest);
+  /* If size is unset, copy size from src.  Because we don't track whether
+     .size has been used, we can't differentiate .size dest, 0 from the case
+     where dest's size is unset.  */
+  if (!destelf->size && S_GET_SIZE (dest) == 0)
+    {
+      if (srcelf->size)
+       {
+         destelf->size = XNEW (expressionS);
+         *destelf->size = *srcelf->size;
+       }
+      S_SET_SIZE (dest, S_GET_SIZE (src));
+    }
+}
 
+/* RISC-V pseudo-ops table.  */
 static const pseudo_typeS riscv_pseudo_table[] =
 {
-  /* RISC-V-specific pseudo-ops.  */
   {"option", s_riscv_option, 0},
   {"half", cons, 2},
   {"word", cons, 4},
@@ -3178,6 +4631,8 @@ static const pseudo_typeS riscv_pseudo_table[] =
   {"sleb128", s_riscv_leb128, 1},
   {"insn", s_riscv_insn, 0},
   {"attribute", s_riscv_attribute, 0},
+  {"variant_cc", s_variant_cc, 0},
+  {"float16", float_cons, 'h'},
 
   { NULL, NULL, 0 },
 };