]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blobdiff - gas/config/tc-riscv.c
RISC-V: Support riscv bitmanip frozen ZBA/ZBB/ZBC instructions (v0.93).
[thirdparty/binutils-gdb.git] / gas / config / tc-riscv.c
index d011864fd700db841bf70d3441c70f3d5e95e66b..052199ea7404f5a8d88e6a68c2529d501b2df3d9 100644 (file)
@@ -1,5 +1,5 @@
 /* tc-riscv.c -- RISC-V assembler
-   Copyright 2011-2016 Free Software Foundation, Inc.
+   Copyright (C) 2011-2021 Free Software Foundation, Inc.
 
    Contributed by Andrew Waterman (andrew@sifive.com).
    Based on MIPS target.
@@ -28,8 +28,8 @@
 #include "itbl-ops.h"
 #include "dwarf2dbg.h"
 #include "dw2gencfi.h"
-#include "struc-symbol.h"
 
+#include "bfd/elfxx-riscv.h"
 #include "elf/riscv.h"
 #include "opcode/riscv.h"
 
@@ -59,29 +59,143 @@ struct riscv_cl_insn
 #define DEFAULT_ARCH "riscv64"
 #endif
 
-static const char default_arch[] = DEFAULT_ARCH;
+#ifndef DEFAULT_RISCV_ATTR
+#define DEFAULT_RISCV_ATTR 0
+#endif
+
+/* Let riscv_after_parse_args set the default value according to xlen.  */
 
-unsigned xlen = 0; /* width of an x-register */
+#ifndef DEFAULT_RISCV_ARCH_WITH_EXT
+#define DEFAULT_RISCV_ARCH_WITH_EXT NULL
+#endif
+
+/* The default ISA spec is set to 2.2 rather than the lastest version.
+   The reason is that compiler generates the ISA string with fixed 2p0
+   verisons only for the RISCV ELF architecture attributes, but not for
+   the -march option.  Therefore, we should update the compiler or linker
+   to resolve this problem.  */
 
-#define LOAD_ADDRESS_INSN (xlen == 64 ? "ld" : "lw")
+#ifndef DEFAULT_RISCV_ISA_SPEC
+#define DEFAULT_RISCV_ISA_SPEC "2.2"
+#endif
+
+#ifndef DEFAULT_RISCV_PRIV_SPEC
+#define DEFAULT_RISCV_PRIV_SPEC "1.11"
+#endif
+
+static const char default_arch[] = DEFAULT_ARCH;
+static const char *default_arch_with_ext = DEFAULT_RISCV_ARCH_WITH_EXT;
+static enum riscv_isa_spec_class default_isa_spec = ISA_SPEC_CLASS_NONE;
+static enum riscv_priv_spec_class default_priv_spec = PRIV_SPEC_CLASS_NONE;
+
+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;
+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;
 
+/* Set the default_isa_spec.  Return 0 if the input spec string isn't
+   supported.  Otherwise, return 1.  */
+
+static int
+riscv_set_default_isa_spec (const char *s)
+{
+  enum riscv_isa_spec_class class;
+  if (!riscv_get_isa_spec_class (s, &class))
+    {
+      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, assembler will find the suitable CSR address
+   according to default_priv_spec.  We will try to check priv attributes if
+   the input string is NULL.  Return 0 if the input priv spec string isn't
+   supported.  Otherwise, return 1.  */
+
+static int
+riscv_set_default_priv_spec (const char *s)
+{
+  enum riscv_priv_spec_class class;
+  unsigned major, minor, revision;
+  obj_attribute *attr;
+
+  /* Find the corresponding priv spec class.  */
+  if (riscv_get_priv_spec_class (s, &class))
+    {
+      default_priv_spec = class;
+      return 1;
+    }
+
+  if (s != NULL)
+    {
+      as_bad (_("Unknown default privilege spec `%s' set by "
+               "-mpriv-spec or --with-priv-spec"), s);
+      return 0;
+    }
+
+  /* Try to set the default_priv_spec according to the priv 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;
+
+  if (riscv_get_priv_spec_class_from_numbers (major,
+                                             minor,
+                                             revision,
+                                             &class))
+    {
+      /* The priv attributes setting 0.0.0 is meaningless.  We should have set
+        the default_priv_spec by md_parse_option and riscv_after_parse_args,
+        so just skip the following setting.  */
+      if (class == PRIV_SPEC_CLASS_NONE)
+       return 1;
+
+      default_priv_spec = class;
+      return 1;
+    }
+
+  /* Still can not find the priv spec class.  */
+  as_bad (_("Unknown default privilege spec `%d.%d.%d' set by "
+           "privilege 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 csr_check; /* Enable the CSR checking.  */
 };
 
 static struct riscv_set_options riscv_opts =
 {
   0,   /* pic */
   0,   /* rvc */
+  0,   /* rve */
   1,   /* relax */
+  DEFAULT_RISCV_ATTR, /* arch_attr */
+  0.   /* csr_check */
 };
 
 static void
@@ -93,136 +207,183 @@ riscv_set_rvc (bfd_boolean rvc_value)
   riscv_opts.rvc = rvc_value;
 }
 
-struct riscv_subset
+static void
+riscv_set_rve (bfd_boolean rve_value)
 {
-  const char *name;
-
-  struct riscv_subset *next;
-};
+  riscv_opts.rve = rve_value;
+}
 
-static struct riscv_subset *riscv_subsets;
+static riscv_subset_list_t riscv_subsets;
 
 static bfd_boolean
 riscv_subset_supports (const char *feature)
 {
-  struct riscv_subset *s;
-  char *p;
-  unsigned xlen_required = strtoul (feature, &p, 10);
-
-  if (xlen_required && xlen != xlen_required)
-    return FALSE;
+  struct riscv_subset_t *subset;
 
-  for (s = riscv_subsets; s != NULL; s = s->next)
-    if (strcasecmp (s->name, p) == 0)
-      return TRUE;
+  if (riscv_opts.rvc && (strcasecmp (feature, "c") == 0))
+    return TRUE;
 
-  return FALSE;
+  return riscv_lookup_subset (&riscv_subsets, feature, &subset);
 }
 
-static void
-riscv_add_subset (const char *subset)
+static bfd_boolean
+riscv_multi_subset_supports (enum riscv_insn_class insn_class)
 {
-  struct riscv_subset *s = xmalloc (sizeof *s);
+  switch (insn_class)
+    {
+    case INSN_CLASS_I: return riscv_subset_supports ("i");
+    case INSN_CLASS_C: return riscv_subset_supports ("c");
+    case INSN_CLASS_A: return riscv_subset_supports ("a");
+    case INSN_CLASS_M: return riscv_subset_supports ("m");
+    case INSN_CLASS_F: return riscv_subset_supports ("f");
+    case INSN_CLASS_D: return riscv_subset_supports ("d");
+    case INSN_CLASS_Q: return riscv_subset_supports ("q");
+
+    case INSN_CLASS_F_AND_C:
+      return (riscv_subset_supports ("f")
+             && riscv_subset_supports ("c"));
+    case INSN_CLASS_D_AND_C:
+      return (riscv_subset_supports ("d")
+             && riscv_subset_supports ("c"));
+
+    case INSN_CLASS_ZICSR:
+      return riscv_subset_supports ("zicsr");
+    case INSN_CLASS_ZIFENCEI:
+      return riscv_subset_supports ("zifencei");
+
+    case INSN_CLASS_ZBA:
+      return riscv_subset_supports ("zba");
+    case INSN_CLASS_ZBB:
+      return riscv_subset_supports ("zbb");
+    case INSN_CLASS_ZBC:
+      return riscv_subset_supports ("zbc");
+    case INSN_CLASS_ZBA_OR_ZBB:
+      return (riscv_subset_supports ("zba")
+             || riscv_subset_supports ("zbb"));
 
-  s->name = xstrdup (subset);
-  s->next = riscv_subsets;
-  riscv_subsets = s;
+    default:
+      as_fatal ("Unreachable");
+      return FALSE;
+    }
 }
 
-/* Set which ISA and extensions are available.  Formally, ISA strings must
-   begin with RV32 or RV64, but we allow the prefix to be omitted.
+/* Handle of the extension with version hash table.  */
+static htab_t ext_version_hash = NULL;
 
-   FIXME: Version numbers are not supported yet.  */
-static void
-riscv_set_arch (const char *p)
+static htab_t
+init_ext_version_hash (const struct riscv_ext_version *table)
 {
-  const char *all_subsets = "IMAFDC";
-  const char *extension = NULL;
-  int rvc = 0;
-  int i;
+  int i = 0;
+  htab_t hash = str_htab_create ();
 
-  if (strncasecmp (p, "RV32", 4) == 0)
+  while (table[i].name)
     {
-      xlen = 32;
-      p += 4;
+      const char *name = table[i].name;
+      if (str_hash_insert (hash, name, &table[i], 0) != NULL)
+       as_fatal (_("duplicate %s"), name);
+
+      i++;
+      while (table[i].name
+            && strcmp (table[i].name, name) == 0)
+       i++;
     }
-  else if (strncasecmp (p, "RV64", 4) == 0)
-    {
-      xlen = 64;
-      p += 4;
-    }
-  else if (strncasecmp (p, "RV", 2) == 0)
-    p += 2;
-
-  switch (TOUPPER(*p))
-    {
-      case 'I':
-       break;
 
-      case 'G':
-       p++;
-       /* Fall through.  */
+  return hash;
+}
 
-      case '\0':
-       for (i = 0; all_subsets[i] != '\0'; i++)
-         {
-           const char subset[] = {all_subsets[i], '\0'};
-           riscv_add_subset (subset);
-         }
-       break;
+static void
+riscv_get_default_ext_version (const char *name,
+                              int *major_version,
+                              int *minor_version)
+{
+  struct riscv_ext_version *ext;
 
-      default:
-       as_fatal ("`I' must be the first ISA subset name specified (got %c)",
-                 *p);
-    }
+  if (name == NULL || default_isa_spec == ISA_SPEC_CLASS_NONE)
+    return;
 
-  while (*p)
+  ext = (struct riscv_ext_version *) str_hash_find (ext_version_hash, name);
+  while (ext
+        && ext->name
+        && strcmp (ext->name, name) == 0)
     {
-      if (TOUPPER(*p) == 'X')
+      if (ext->isa_spec_class == ISA_SPEC_CLASS_DRAFT
+         || ext->isa_spec_class == default_isa_spec)
        {
-         char *subset = xstrdup (p), *q = subset;
-
-         while (*++q != '\0' && *q != '_')
-           ;
-         *q = '\0';
-
-         if (extension)
-           as_fatal ("only one eXtension is supported (found %s and %s)",
-                     extension, subset);
-         extension = subset;
-         riscv_add_subset (subset);
-         p += strlen (subset);
-         free (subset);
-       }
-      else if (*p == '_')
-       p++;
-      else if ((all_subsets = strchr (all_subsets, *p)) != NULL)
-       {
-         const char subset[] = {*p, 0};
-         riscv_add_subset (subset);
-         if (TOUPPER(*p) == 'C')
-           rvc = 1;
-         all_subsets++;
-         p++;
+         *major_version = ext->major_version;
+         *minor_version = ext->minor_version;
+         return;
        }
-      else
-       as_fatal ("unsupported ISA subset %c", *p);
+      ext++;
     }
+}
 
-  if (rvc)
+/* Set which ISA and extensions are available.  */
+
+static void
+riscv_set_arch (const char *s)
+{
+  riscv_parse_subset_t rps;
+  rps.subset_list = &riscv_subsets;
+  rps.error_handler = as_bad;
+  rps.xlen = &xlen;
+  rps.get_default_version = riscv_get_default_ext_version;
+
+  if (s == NULL)
+    return;
+
+  riscv_release_subset_list (&riscv_subsets);
+  riscv_parse_subset (&rps, s);
+}
+
+/* Indicate -mabi= option is explictly set.  */
+static bfd_boolean explicit_mabi = FALSE;
+
+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;
+}
+
+/* If the -mabi option isn't set, then we set the abi according to the arch
+   string.  Otherwise, check if there are conflicts between architecture
+   and abi setting.  */
+
+static void
+riscv_set_abi_by_arch (void)
+{
+  if (!explicit_mabi)
     {
-      /* Override -m[no-]rvc setting if C was explicitly listed.  */
-      riscv_set_rvc (TRUE);
+      if (riscv_subset_supports ("q"))
+       riscv_set_abi (xlen, FLOAT_ABI_QUAD, FALSE);
+      else if (riscv_subset_supports ("d"))
+       riscv_set_abi (xlen, FLOAT_ABI_DOUBLE, FALSE);
+      else
+       riscv_set_abi (xlen, FLOAT_ABI_SOFT, FALSE);
     }
   else
     {
-      /* Add RVC anyway.  -m[no-]rvc toggles its availability.  */
-      riscv_add_subset ("C");
+      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);
     }
+
+  /* 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 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 */
@@ -248,6 +409,15 @@ const char EXP_CHARS[] = "eE";
 /* or    0d1.2345e12 */
 const char FLT_CHARS[] = "rRsSfFdDxXpP";
 
+/* Indicate we are already assemble any instructions or not.  */
+static bfd_boolean start_assemble = FALSE;
+
+/* Indicate ELF attributes are explicitly set.  */
+static bfd_boolean explicit_attr = FALSE;
+
+/* Indicate CSR or priv instructions are explicitly used.  */
+static bfd_boolean explicit_priv_attr = FALSE;
+
 /* Macros for encoding relaxation state for RVC branches and far jumps.  */
 #define RELAX_BRANCH_ENCODE(uncond, rvc, length)       \
   ((relax_substateT)                                   \
@@ -286,7 +456,10 @@ static char *expr_end;
 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.  */
@@ -315,7 +488,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
@@ -373,6 +546,7 @@ relaxed_branch_length (fragS *fragp, asection *sec, int update)
 
   if (fragp->fr_symbol != NULL
       && S_IS_DEFINED (fragp->fr_symbol)
+      && !S_IS_WEAK (fragp->fr_symbol)
       && sec == S_GET_SEGMENT (fragp->fr_symbol))
     {
       offsetT val = S_GET_VALUE (fragp->fr_symbol) + fragp->fr_offset;
@@ -393,21 +567,116 @@ relaxed_branch_length (fragS *fragp, asection *sec, int update)
   return length;
 }
 
-struct regname
+/* Information about an opcode name, mnemonics and its value.  */
+struct opcode_name_t
 {
   const char *name;
-  unsigned int num;
+  unsigned int val;
+};
+
+/* List for all supported opcode name.  */
+static const struct opcode_name_t opcode_name_list[] =
+{
+  {"C0",        0x0},
+  {"C1",        0x1},
+  {"C2",        0x2},
+
+  {"LOAD",      0x03},
+  {"LOAD_FP",   0x07},
+  {"CUSTOM_0",  0x0b},
+  {"MISC_MEM",  0x0f},
+  {"OP_IMM",    0x13},
+  {"AUIPC",     0x17},
+  {"OP_IMM_32", 0x1b},
+  /* 48b        0x1f.  */
+
+  {"STORE",     0x23},
+  {"STORE_FP",  0x27},
+  {"CUSTOM_1",  0x2b},
+  {"AMO",       0x2f},
+  {"OP",        0x33},
+  {"LUI",       0x37},
+  {"OP_32",     0x3b},
+  /* 64b        0x3f.  */
+
+  {"MADD",      0x43},
+  {"MSUB",      0x47},
+  {"NMADD",     0x4f},
+  {"NMSUB",     0x4b},
+  {"OP_FP",     0x53},
+  /*reserved    0x57.  */
+  {"CUSTOM_2",  0x5b},
+  /* 48b        0x5f.  */
+
+  {"BRANCH",    0x63},
+  {"JALR",      0x67},
+  /*reserved    0x5b.  */
+  {"JAL",       0x6f},
+  {"SYSTEM",    0x73},
+  /*reserved    0x77.  */
+  {"CUSTOM_3",  0x7b},
+  /* >80b       0x7f.  */
+
+  {NULL, 0}
 };
 
+/* Hash table for lookup opcode name.  */
+static htab_t opcode_names_hash = NULL;
+
+/* Initialization for hash table of opcode name.  */
+static void
+init_opcode_names_hash (void)
+{
+  const struct opcode_name_t *opcode;
+
+  for (opcode = &opcode_name_list[0]; opcode->name != NULL; ++opcode)
+    if (str_hash_insert (opcode_names_hash, opcode->name, opcode, 0) != NULL)
+      as_fatal (_("duplicate %s"), opcode->name);
+}
+
+/* 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)
+{
+  char *e;
+  char save_c;
+  struct opcode_name_t *o;
+
+  /* Find end of name.  */
+  e = *s;
+  if (is_name_beginner (*e))
+    ++e;
+  while (is_part_of_name (*e))
+    ++e;
+
+  /* Terminate name.  */
+  save_c = *e;
+  *e = '\0';
+
+  o = (struct opcode_name_t *) str_hash_find (opcode_names_hash, *s);
+
+  /* Advance to next token if one was recognized.  */
+  if (o)
+    *s = e;
+
+  *e = save_c;
+  expr_end = e;
+
+  return o;
+}
+
 enum reg_class
 {
   RCLASS_GPR,
   RCLASS_FPR,
-  RCLASS_CSR,
-  RCLASS_MAX
+  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))
@@ -418,10 +687,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 (_("duplicate %s"), name);
 }
 
 static void
@@ -433,13 +700,151 @@ 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_priv_spec_class define_version,
+                    enum riscv_priv_spec_class abort_version)
+{
+  struct riscv_csr_extra *entry, *pre_entry;
+  bfd_boolean 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 setting for the CSR, just return and do nothing.  */
+  if (!need_enrty)
+    return;
+
+  entry = XNEW (struct riscv_csr_extra);
+  entry->csr_class = class;
+  entry->address = address;
+  entry->define_version = define_version;
+  entry->abort_version = abort_version;
+  entry->next = NULL;
+
+  /* If the CSR hasn't been inserted in the hash table, then insert it.
+     Otherwise, attach the extra information to the entry which is already
+     in the hash table.  */
+  if (pre_entry == NULL)
+    str_hash_insert (csr_extra_hash, name, entry, 0);
+  else
+    pre_entry->next = entry;
+}
+
+/* Return the suitable CSR address after checking the ISA dependency and
+   priv spec versions.  */
+
+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;
+  bfd_boolean need_check_version = TRUE;
+  bfd_boolean result = TRUE;
+
+  switch (csr_class)
+    {
+    case CSR_CLASS_I:
+      result = riscv_subset_supports ("i");
+      break;
+    case CSR_CLASS_I_32:
+      result = (xlen == 32 && riscv_subset_supports ("i"));
+      break;
+    case CSR_CLASS_F:
+      result = riscv_subset_supports ("f");
+      need_check_version = FALSE;
+      break;
+    case CSR_CLASS_DEBUG:
+      need_check_version = FALSE;
+      break;
+    default:
+      as_bad (_("internal: bad RISC-V CSR class (0x%x)"), csr_class);
+    }
+
+  /* Don't report the ISA conflict when -mcsr-check isn't set.  */
+  if (riscv_opts.csr_check && !result)
+    as_warn (_("Invalid CSR `%s' for the current ISA"), csr_name);
+
+  while (entry != NULL)
+    {
+      if (!need_check_version
+         || (default_priv_spec >= entry->define_version
+             && default_priv_spec < entry->abort_version))
+       {
+         /* Find the suitable CSR according to the specific version.  */
+         return entry->address;
+       }
+      entry = entry->next;
+    }
+
+  /* We can not find the suitable CSR address according to the privilege
+     version.  Therefore, we use the last defined value.  Report the warning
+     only when the -mcsr-check is set.  Enable the -mcsr-check is recommended,
+     otherwise, you may get the unexpected CSR address.  */
+  if (riscv_opts.csr_check)
+    {
+      const char *priv_name = riscv_get_priv_spec_name (default_priv_spec);
+
+      if (priv_name != NULL)
+       as_warn (_("Invalid CSR `%s' for the privilege spec `%s'"),
+                csr_name, priv_name);
+    }
+
+  return saved_entry->address;
+}
+
+/* Once the CSR is defined, including the old privilege spec, then we call
+   riscv_csr_class_check and riscv_csr_version_check to do the further checking
+   and get the corresponding address.  Return -1 if the CSR is 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;
+
+  /* We just report the warning when the CSR is invalid.  "Invalid CSR" means
+     the CSR was defined, but isn't allowed for the current ISA setting or
+     the privilege spec.  If the CSR is never been defined, then assembler
+     will regard it as a "Unknown CSR" and report error.  If user use number
+     to set the CSR, but over the range (> 0xfff), then assembler will report
+     "Improper CSR" error for it.  */
+  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)
+    return -1;
+
   return DECODE_REG_NUM (r);
 }
 
@@ -477,6 +882,9 @@ 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;
+
   for (i = 0; i < size; i++)
     if (array[i] != NULL && strncmp (array[i], *s, len) == 0)
       {
@@ -490,15 +898,24 @@ arg_lookup (char **s, const char *const *array, size_t size, unsigned *regnop)
 
 /* 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.
+
+   `length` could be 0, 4 or 8, 0 for auto detection.  */
 static bfd_boolean
-validate_riscv_insn (const struct riscv_opcode *opc)
+validate_riscv_insn (const struct riscv_opcode *opc, int length)
 {
   const char *p = opc->args;
   char c;
   insn_t used_bits = opc->mask;
-  int insn_width = 8 * riscv_insn_length (opc->match);
-  insn_t required_bits = ~0ULL >> (64 - insn_width);
+  int insn_width;
+  insn_t required_bits;
+
+  if (length == 0)
+    insn_width = 8 * riscv_insn_length (opc->match);
+  else
+    insn_width = 8 * length;
+
+  required_bits = ~0ULL >> (64 - insn_width);
 
   if ((used_bits & opc->match) != (opc->match & required_bits))
     {
@@ -518,6 +935,7 @@ validate_riscv_insn (const struct riscv_opcode *opc)
          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;
@@ -529,6 +947,7 @@ validate_riscv_insn (const struct riscv_opcode *opc)
          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;
@@ -537,8 +956,24 @@ validate_riscv_insn (const struct riscv_opcode *opc)
          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++)
+             {
+               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);
@@ -563,6 +998,7 @@ validate_riscv_insn (const struct riscv_opcode *opc)
       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':
@@ -571,9 +1007,36 @@ validate_riscv_insn (const struct riscv_opcode *opc)
       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;
+           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;
+           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"),
@@ -597,55 +1060,76 @@ struct percent_op_match
   bfd_reloc_code_real_type reloc;
 };
 
-/* This function is called once, at assembler startup time.  It should set up
-   all the tables, etc. that the MD part of the assembler will need.  */
-
-void
-md_begin (void)
+/* 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)
 {
   int i = 0;
-
-  if (! bfd_set_arch_mach (stdoutput, bfd_arch_riscv, 0))
-    as_warn (_("Could not set architecture and machine"));
-
-  op_hash = hash_new ();
-
-  while (riscv_opcodes[i].name)
+  int length;
+  htab_t hash = str_htab_create ();
+  while (opcodes[i].name)
     {
-      const char *name = riscv_opcodes[i].name;
-      const char *hash_error =
-       hash_insert (op_hash, name, (void *) &riscv_opcodes[i]);
-
-      if (hash_error)
-       {
-         fprintf (stderr, _("internal error: can't hash `%s': %s\n"),
-                  riscv_opcodes[i].name, hash_error);
-         /* Probably a memory allocation problem?  Give up now.  */
-         as_fatal (_("Broken assembler.  No assembly attempted."));
-       }
+      const char *name = opcodes[i].name;
+      if (str_hash_insert (hash, name, &opcodes[i], 0) != NULL)
+       as_fatal (_("duplicate %s"), name);
 
       do
        {
-         if (riscv_opcodes[i].pinfo != INSN_MACRO)
+         if (opcodes[i].pinfo != INSN_MACRO)
            {
-             if (!validate_riscv_insn (&riscv_opcodes[i]))
+             if (insn_directive_p)
+               length = ((name[0] == 'c') ? 2 : 4);
+             else
+               length = 0; /* Let assembler determine the length. */
+             if (!validate_riscv_insn (&opcodes[i], length))
                as_fatal (_("Broken assembler.  No assembly attempted."));
            }
+         else
+           gas_assert (!insn_directive_p);
          ++i;
        }
-      while (riscv_opcodes[i].name && !strcmp (riscv_opcodes[i].name, name));
+      while (opcodes[i].name && !strcmp (opcodes[i].name, name));
     }
 
-  reg_names_hash = hash_new ();
+  return hash;
+}
+
+/* This function is called once, at assembler startup time.  It should set up
+   all the tables, etc. that the MD part of the assembler will need.  */
+
+void
+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"));
+
+  op_hash = init_opcode_hash (riscv_opcodes, FALSE);
+  insn_type_hash = init_opcode_hash (riscv_insn_types, TRUE);
+
+  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);
-
-#define DECLARE_CSR(name, num) hash_reg_name (RCLASS_CSR, #name, num);
+  /* Add "fp" as an alias for "s0".  */
+  hash_reg_name (RCLASS_GPR, "fp", 8);
+
+  /* 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);
 }
@@ -693,15 +1177,19 @@ 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,
                            address_expr->X_add_number);
          return;
        }
-      else if (address_expr->X_op == O_constant)
-       ip->insn_opcode |= riscv_apply_const_reloc (reloc_type,
-                                                   address_expr->X_add_number);
       else
        {
          howto = bfd_reloc_type_lookup (stdoutput, reloc_type);
@@ -718,6 +1206,19 @@ append_insn (struct riscv_cl_insn *ip, expressionS *address_expr,
 
   add_fixed_insn (ip);
   install_insn (ip);
+
+  /* We need to start a new frag after any instruction that can be
+     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_HI20
+      || reloc_type == BFD_RELOC_RISCV_PCREL_HI20
+      || reloc_type == BFD_RELOC_RISCV_TPREL_HI20
+      || reloc_type == BFD_RELOC_RISCV_TPREL_ADD)
+    {
+      frag_wane (frag_now);
+      frag_new (0);
+    }
 }
 
 /* Build an instruction created by a macro expansion.  This is passed
@@ -736,7 +1237,7 @@ macro_build (expressionS *ep, const char *name, const char *fmt, ...)
   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
@@ -788,6 +1289,29 @@ 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 error: 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
@@ -801,13 +1325,18 @@ normalize_constant_expr (expressionS *ex)
                        - 0x80000000);
 }
 
-/* Fail if an expression is not a constant.  */
+/* Fail if an expression EX is not a constant.  IP is the instruction using EX.
+   MAYBE_CSR is true if the symbol may be an unrecognized CSR name.  */
 
 static void
-check_absolute_expr (struct riscv_cl_insn *ip, expressionS *ex)
+check_absolute_expr (struct riscv_cl_insn *ip, expressionS *ex,
+                    bfd_boolean maybe_csr)
 {
   if (ex->X_op == O_big)
     as_bad (_("unsupported large constant"));
+  else if (maybe_csr && ex->X_op == O_symbol)
+    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"),
            ip->insn_mo->name);
@@ -817,8 +1346,8 @@ 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.  */
@@ -858,8 +1387,13 @@ 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.  */
@@ -868,8 +1402,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)
@@ -887,9 +1422,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, %" BFD_VMA_FMT "d", reg, reg,
+                     lower.X_add_number);
     }
   else
     {
@@ -898,17 +1434,38 @@ 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%" BFD_VMA_FMT "x", reg, 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, %" BFD_VMA_FMT "d", ADD32_INSN, reg, hi_reg,
+                     lower.X_add_number);
+    }
+}
+
+/* Zero extend and sign extend byte/half-word/word.  */
+
+static void
+riscv_ext (int destreg, int srcreg, unsigned shift, bfd_boolean 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 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)
@@ -1029,6 +1586,22 @@ 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;
+
     default:
       as_bad (_("Macro %s not implemented"), ip->insn_mo->name);
       break;
@@ -1039,6 +1612,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},
@@ -1067,6 +1641,11 @@ static const struct percent_op_match percent_op_rtype[] =
   {0, 0}
 };
 
+static const struct percent_op_match percent_op_null[] =
+{
+  {0, 0}
+};
+
 /* Return true if *STR points to a relocation operator.  When returning true,
    move *STR over the operator and store its relocation code in *RELOC.
    Leave both *STR and *RELOC alone when returning false.  */
@@ -1126,11 +1705,15 @@ my_getSmallExpression (expressionS *ep, bfd_reloc_code_real_type *reloc,
   unsigned crux_depth, str_depth, regno;
   char *crux;
 
-  /* First, check for integer registers.  */
+  /* First, check for integer registers.  No callers can accept a reg, but
+     we need to avoid accidentally creating a useless undefined symbol below,
+     if this is an instruction pattern that can't match.  A glibc build fails
+     if this is removed.  */
   if (reg_lookup (&str, RCLASS_GPR, &regno))
     {
       ep->X_op = O_register;
       ep->X_add_number = regno;
+      expr_end = str;
       return 0;
     }
 
@@ -1172,13 +1755,128 @@ my_getSmallExpression (expressionS *ep, bfd_reloc_code_real_type *reloc,
   return reloc_index;
 }
 
+/* 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)
+{
+  const struct opcode_name_t *o = opcode_name_lookup (&str);
+
+  if (o != NULL)
+    {
+      ep->X_op = O_constant;
+      ep->X_add_number = o->val;
+      return 0;
+    }
+
+  return my_getSmallExpression (ep, reloc, str, percent_op);
+}
+
+/* Detect and handle implicitly zero load-store offsets.  For example,
+   "lw t0, (t1)" is shorthand for "lw t0, 0(t1)".  Return TRUE iff such
+   an implicit offset was detected.  */
+
+static bfd_boolean
+riscv_handle_implicit_zero_offset (expressionS *ep, const char *s)
+{
+  /* Check whether there is only a single bracketed expression left.
+     If so, it must be the base register and the constant must be zero.  */
+  if (*s == '(' && strchr (s + 1, '(') == 0)
+    {
+      ep->X_op = O_constant;
+      ep->X_add_number = 0;
+      return TRUE;
+    }
+
+  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 bfd_boolean
+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 bfd_boolean
+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.  Maybe we should issue warning
+     if sfence.vm is used, but the priv spec newer than v1.10 is chosen.
+     We already have a similar check for CSR, but not yet for instructions.
+     It would be good if we could check the spec versions both for CSR and
+     instructions, but not here.  */
+         || ((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 *
 riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr,
-         bfd_reloc_code_real_type *imm_reloc)
+         bfd_reloc_code_real_type *imm_reloc, htab_t hash)
 {
   char *s;
   const char *args;
@@ -1190,9 +1888,11 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr,
   int argnum;
   const struct percent_op_match *p;
   const char *error = "unrecognized opcode";
+  /* Indicate we are assembling instruction with CSR.  */
+  bfd_boolean 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.  */
+     is found so that str_hash_find only sees the name part of the string.  */
   for (s = str; *s != '\0'; ++s)
     if (ISSPACE (*s))
       {
@@ -1201,12 +1901,15 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr,
        break;
       }
 
-  insn = (struct riscv_opcode *) hash_find (op_hash, str);
+  insn = (struct riscv_opcode *) str_hash_find (hash, str);
 
   argsStart = s;
   for ( ; insn && insn->name && strcmp (insn->name, str) == 0; insn++)
     {
-      if (!riscv_subset_supports (insn->subset))
+      if ((insn->xlen_requirement != 0) && (xlen != insn->xlen_requirement))
+       continue;
+
+      if (!riscv_multi_subset_supports (insn->insn_class))
        continue;
 
       create_insn (ip, insn);
@@ -1226,13 +1929,36 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr,
                {
                  if (!insn->match_func (insn, ip->insn_opcode))
                    break;
-                 if (riscv_insn_length (insn->match) == 2 && !riscv_opts.rvc)
+
+                 /* For .insn, insn->match and insn->mask are 0.  */
+                 if (riscv_insn_length ((insn->match == 0 && insn->mask == 0)
+                                        ? ip->insn_opcode
+                                        : 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)
+                       *(argsStart - 1) = save_c;
+                     as_warn (_("Read-only CSR is written `%s'"), str);
+                     insn_with_csr = FALSE;
+                   }
                }
              if (*s != '\0')
                break;
              /* Successful assembly.  */
              error = NULL;
+             insn_with_csr = FALSE;
              goto out;
 
            case 'C': /* RVC */
@@ -1275,6 +2001,11 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr,
                      || regno != X_SP)
                    break;
                  continue;
+               case 'z': /* RS2, contrained to equal x0.  */
+                 if (!reg_lookup (&s, RCLASS_GPR, &regno)
+                     || regno != 0)
+                   break;
+                 continue;
                case '>':
                  if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
                      || imm_expr->X_op != O_constant
@@ -1282,24 +2013,33 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr,
                      || imm_expr->X_add_number >= 64)
                    break;
                  ip->insn_opcode |= ENCODE_RVC_IMM (imm_expr->X_add_number);
-rvc_imm_done:
+               rvc_imm_done:
                  s = 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)
+                     || imm_expr->X_add_number >= 32
+                     || !VALID_RVC_IMM ((valueT) imm_expr->X_add_number))
                    break;
                  ip->insn_opcode |= ENCODE_RVC_IMM (imm_expr->X_add_number);
                  goto rvc_imm_done;
+               case '8':
+                 if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
+                     || imm_expr->X_op != O_constant
+                     || imm_expr->X_add_number < 0
+                     || imm_expr->X_add_number >= 256
+                     || !VALID_RVC_UIMM8 ((valueT) imm_expr->X_add_number))
+                   break;
+                 ip->insn_opcode |= ENCODE_RVC_UIMM8 (imm_expr->X_add_number);
+                 goto rvc_imm_done;
                case 'i':
                  if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
                      || imm_expr->X_op != O_constant
                      || imm_expr->X_add_number == 0
-                     || !VALID_RVC_SIMM3 (imm_expr->X_add_number))
+                     || !VALID_RVC_SIMM3 ((valueT) imm_expr->X_add_number))
                    break;
                  ip->insn_opcode |= ENCODE_RVC_SIMM3 (imm_expr->X_add_number);
                  goto rvc_imm_done;
@@ -1307,45 +2047,63 @@ rvc_imm_done:
                  if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
                      || imm_expr->X_op != O_constant
                      || imm_expr->X_add_number == 0
-                     || !VALID_RVC_IMM (imm_expr->X_add_number))
+                     || !VALID_RVC_IMM ((valueT) imm_expr->X_add_number))
                    break;
                  ip->insn_opcode |= ENCODE_RVC_IMM (imm_expr->X_add_number);
                  goto rvc_imm_done;
                case 'k':
+                 if (riscv_handle_implicit_zero_offset (imm_expr, s))
+                   continue;
                  if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
                      || imm_expr->X_op != O_constant
-                     || !VALID_RVC_LW_IMM (imm_expr->X_add_number))
+                     || !VALID_RVC_LW_IMM ((valueT) imm_expr->X_add_number))
                    break;
                  ip->insn_opcode |= ENCODE_RVC_LW_IMM (imm_expr->X_add_number);
                  goto rvc_imm_done;
                case 'l':
+                 if (riscv_handle_implicit_zero_offset (imm_expr, s))
+                   continue;
                  if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
                      || imm_expr->X_op != O_constant
-                     || !VALID_RVC_LD_IMM (imm_expr->X_add_number))
+                     || !VALID_RVC_LD_IMM ((valueT) imm_expr->X_add_number))
                    break;
                  ip->insn_opcode |= ENCODE_RVC_LD_IMM (imm_expr->X_add_number);
                  goto rvc_imm_done;
                case 'm':
+                 if (riscv_handle_implicit_zero_offset (imm_expr, s))
+                   continue;
                  if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
                      || imm_expr->X_op != O_constant
-                     || !VALID_RVC_LWSP_IMM (imm_expr->X_add_number))
+                     || !VALID_RVC_LWSP_IMM ((valueT) imm_expr->X_add_number))
                    break;
                  ip->insn_opcode |=
                    ENCODE_RVC_LWSP_IMM (imm_expr->X_add_number);
                  goto rvc_imm_done;
                case 'n':
+                 if (riscv_handle_implicit_zero_offset (imm_expr, s))
+                   continue;
                  if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
                      || imm_expr->X_op != O_constant
-                     || !VALID_RVC_LDSP_IMM (imm_expr->X_add_number))
+                     || !VALID_RVC_LDSP_IMM ((valueT) imm_expr->X_add_number))
                    break;
                  ip->insn_opcode |=
                    ENCODE_RVC_LDSP_IMM (imm_expr->X_add_number);
                  goto rvc_imm_done;
+               case 'o':
+                 if (my_getSmallExpression (imm_expr, imm_reloc, s, 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 ((valueT) imm_expr->X_add_number))
+                   break;
+                 ip->insn_opcode |= ENCODE_RVC_IMM (imm_expr->X_add_number);
+                 goto rvc_imm_done;
                case 'K':
                  if (my_getSmallExpression (imm_expr, imm_reloc, s, 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_RVC_ADDI4SPN_IMM ((valueT) imm_expr->X_add_number))
                    break;
                  ip->insn_opcode |=
                    ENCODE_RVC_ADDI4SPN_IMM (imm_expr->X_add_number);
@@ -1353,24 +2111,28 @@ rvc_imm_done:
                case 'L':
                  if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
                      || imm_expr->X_op != O_constant
-                     || !VALID_RVC_ADDI16SP_IMM (imm_expr->X_add_number)
-                     || imm_expr->X_add_number == 0)
+                     || imm_expr->X_add_number == 0
+                     || !VALID_RVC_ADDI16SP_IMM ((valueT) imm_expr->X_add_number))
                    break;
                  ip->insn_opcode |=
                    ENCODE_RVC_ADDI16SP_IMM (imm_expr->X_add_number);
                  goto rvc_imm_done;
                case 'M':
+                 if (riscv_handle_implicit_zero_offset (imm_expr, s))
+                   continue;
                  if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
                      || imm_expr->X_op != O_constant
-                     || !VALID_RVC_SWSP_IMM (imm_expr->X_add_number))
+                     || !VALID_RVC_SWSP_IMM ((valueT) imm_expr->X_add_number))
                    break;
                  ip->insn_opcode |=
                    ENCODE_RVC_SWSP_IMM (imm_expr->X_add_number);
                  goto rvc_imm_done;
                case 'N':
+                 if (riscv_handle_implicit_zero_offset (imm_expr, s))
+                   continue;
                  if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
                      || imm_expr->X_op != O_constant
-                     || !VALID_RVC_SDSP_IMM (imm_expr->X_add_number))
+                     || !VALID_RVC_SDSP_IMM ((valueT) imm_expr->X_add_number))
                    break;
                  ip->insn_opcode |=
                    ENCODE_RVC_SDSP_IMM (imm_expr->X_add_number);
@@ -1379,7 +2141,7 @@ rvc_imm_done:
                  p = percent_op_utype;
                  if (my_getSmallExpression (imm_expr, imm_reloc, s, 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
@@ -1402,6 +2164,12 @@ rvc_lui:
                  goto branch;
                case 'a':
                  goto jump;
+               case 'S': /* Floating-point RS1 x8-x15.  */
+                 if (!reg_lookup (&s, 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)
                      || !(regno >= 8 && regno <= 15))
@@ -1413,6 +2181,74 @@ rvc_lui:
                    break;
                  INSERT_OPERAND (CRS2, *ip, regno);
                  continue;
+               case 'F':
+                 switch (*++args)
+                   {
+                     case '6':
+                       if (my_getSmallExpression (imm_expr, imm_reloc, s, 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"));
+                           break;
+                         }
+
+                       INSERT_OPERAND (CFUNCT6, *ip, imm_expr->X_add_number);
+                       imm_expr->X_op = O_absent;
+                       s = expr_end;
+                       continue;
+                     case '4':
+                       if (my_getSmallExpression (imm_expr, imm_reloc, s, 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"));
+                           break;
+                         }
+
+                       INSERT_OPERAND (CFUNCT4, *ip, imm_expr->X_add_number);
+                       imm_expr->X_op = O_absent;
+                       s = expr_end;
+                       continue;
+                     case '3':
+                       if (my_getSmallExpression (imm_expr, imm_reloc, s, 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"));
+                           break;
+                         }
+                       INSERT_OPERAND (CFUNCT3, *ip, imm_expr->X_add_number);
+                       imm_expr->X_op = O_absent;
+                       s = expr_end;
+                       continue;
+                     case '2':
+                       if (my_getSmallExpression (imm_expr, imm_reloc, s, 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"));
+                           break;
+                         }
+                       INSERT_OPERAND (CFUNCT2, *ip, imm_expr->X_add_number);
+                       imm_expr->X_op = O_absent;
+                       s = expr_end;
+                       continue;
+                     default:
+                       as_bad (_("bad compressed FUNCT field"
+                                 " specifier 'CF%c'\n"),
+                               *args);
+                   }
+                 break;
+
                default:
                  as_bad (_("bad RVC field specifier 'C%c'\n"), *args);
                }
@@ -1435,10 +2271,10 @@ rvc_lui:
 
            case '<':           /* Shift amount, 0 - 31.  */
              my_getExpression (imm_expr, s);
-             check_absolute_expr (ip, imm_expr);
+             check_absolute_expr (ip, imm_expr, FALSE);
              if ((unsigned long) imm_expr->X_add_number > 31)
-               as_warn (_("Improper shift amount (%lu)"),
-                        (unsigned long) imm_expr->X_add_number);
+               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;
@@ -1446,10 +2282,10 @@ rvc_lui:
 
            case '>':           /* Shift amount, 0 - (XLEN-1).  */
              my_getExpression (imm_expr, s);
-             check_absolute_expr (ip, imm_expr);
+             check_absolute_expr (ip, imm_expr, FALSE);
              if ((unsigned long) imm_expr->X_add_number >= xlen)
-               as_warn (_("Improper shift amount (%lu)"),
-                        (unsigned long) imm_expr->X_add_number);
+               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;
@@ -1457,25 +2293,27 @@ rvc_lui:
 
            case 'Z':           /* CSRRxI immediate.  */
              my_getExpression (imm_expr, s);
-             check_absolute_expr (ip, imm_expr);
+             check_absolute_expr (ip, imm_expr, FALSE);
              if ((unsigned long) imm_expr->X_add_number > 31)
-               as_warn (_("Improper CSRxI immediate (%lu)"),
-                        (unsigned long) imm_expr->X_add_number);
+               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;
              continue;
 
            case 'E':           /* Control register.  */
+             insn_with_csr = TRUE;
+             explicit_priv_attr = TRUE;
              if (reg_lookup (&s, RCLASS_CSR, &regno))
                INSERT_OPERAND (CSR, *ip, regno);
              else
                {
                  my_getExpression (imm_expr, s);
-                 check_absolute_expr (ip, imm_expr);
+                 check_absolute_expr (ip, imm_expr, TRUE);
                  if ((unsigned long) imm_expr->X_add_number > 0xfff)
-                   as_warn (_("Improper CSR address (%lu)"),
-                            (unsigned long) imm_expr->X_add_number);
+                   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;
@@ -1506,6 +2344,7 @@ rvc_lui:
            case 'd':           /* Destination register.  */
            case 's':           /* Source register.  */
            case 't':           /* Target register.  */
+           case 'r':           /* rs3.  */
              if (reg_lookup (&s, RCLASS_GPR, &regno))
                {
                  c = *args;
@@ -1525,6 +2364,9 @@ rvc_lui:
                    case 't':
                      INSERT_OPERAND (RS2, *ip, regno);
                      break;
+                   case 'r':
+                     INSERT_OPERAND (RS3, *ip, regno);
+                     break;
                    }
                  continue;
                }
@@ -1582,9 +2424,20 @@ rvc_lui:
              s = expr_end;
              continue;
 
+           case 'B':
+             my_getExpression (imm_expr, s);
+             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;
+             continue;
+
            case 'j': /* Sign-extended immediate.  */
-             *imm_reloc = BFD_RELOC_RISCV_LO12_I;
              p = percent_op_itype;
+             *imm_reloc = BFD_RELOC_RISCV_LO12_I;
              goto alu_op;
            case 'q': /* Store displacement.  */
              p = percent_op_stype;
@@ -1594,18 +2447,15 @@ rvc_lui:
              p = percent_op_itype;
              *imm_reloc = BFD_RELOC_RISCV_LO12_I;
              goto load_store;
-           case '0': /* AMO "displacement," which must be zero.  */
+           case '1': /* 4-operand add, must be %tprel_add.  */
              p = percent_op_rtype;
-             *imm_reloc = BFD_RELOC_UNUSED;
-load_store:
-             /* Check whether there is only a single bracketed expression
-                left.  If so, it must be the base register and the
-                constant must be zero.  */
-             imm_expr->X_op = O_constant;
-             imm_expr->X_add_number = 0;
-             if (*s == '(' && strchr (s + 1, '(') == 0)
+             goto alu_op;
+           case '0': /* AMO "displacement," which must be zero.  */
+             p = percent_op_null;
+           load_store:
+             if (riscv_handle_implicit_zero_offset (imm_expr, s))
                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.  */
@@ -1614,6 +2464,7 @@ alu_op:
                  normalize_constant_expr (imm_expr);
                  if (imm_expr->X_op != O_constant
                      || (*args == '0' && imm_expr->X_add_number != 0)
+                     || (*args == '1')
                      || imm_expr->X_add_number >= (signed)RISCV_IMM_REACH/2
                      || imm_expr->X_add_number < -(signed)RISCV_IMM_REACH/2)
                    break;
@@ -1623,7 +2474,7 @@ alu_op:
              continue;
 
            case 'p':           /* PC-relative offset.  */
-branch:
+           branch:
              *imm_reloc = BFD_RELOC_12_PCREL;
              my_getExpression (imm_expr, s);
              s = expr_end;
@@ -1631,9 +2482,11 @@ branch:
 
            case 'u':           /* Upper 20 bits.  */
              p = percent_op_utype;
-             if (!my_getSmallExpression (imm_expr, imm_reloc, s, p)
-                 && imm_expr->X_op == O_constant)
+             if (!my_getSmallExpression (imm_expr, imm_reloc, s, p))
                {
+                 if (imm_expr->X_op != O_constant)
+                   break;
+
                  if (imm_expr->X_add_number < 0
                      || imm_expr->X_add_number >= (signed)RISCV_BIGIMM_REACH)
                    as_bad (_("lui expression not in range 0..1048575"));
@@ -1645,7 +2498,7 @@ branch:
              continue;
 
            case 'a':           /* 20-bit PC-relative offset.  */
-jump:
+           jump:
              my_getExpression (imm_expr, s);
              s = expr_end;
              *imm_reloc = BFD_RELOC_RISCV_JMP;
@@ -1662,6 +2515,108 @@ jump:
              else
                *imm_reloc = BFD_RELOC_RISCV_CALL;
              continue;
+           case 'O':
+             switch (*++args)
+               {
+               case '4':
+                 if (my_getOpcodeExpression (imm_expr, imm_reloc, s, p)
+                     || imm_expr->X_op != O_constant
+                     || imm_expr->X_add_number < 0
+                     || imm_expr->X_add_number >= 128
+                     || (imm_expr->X_add_number & 0x3) != 3)
+                   {
+                     as_bad (_("bad value for opcode field, "
+                               "value must be 0...127 and "
+                               "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;
+                 continue;
+               case '2':
+                 if (my_getOpcodeExpression (imm_expr, imm_reloc, s, p)
+                     || imm_expr->X_op != O_constant
+                     || imm_expr->X_add_number < 0
+                     || imm_expr->X_add_number >= 3)
+                   {
+                     as_bad (_("bad value for opcode field, "
+                               "value must be 0...2"));
+                     break;
+                   }
+
+                 INSERT_OPERAND (OP2, *ip, imm_expr->X_add_number);
+                 imm_expr->X_op = O_absent;
+                 s = expr_end;
+                 continue;
+               default:
+                 as_bad (_("bad Opcode field specifier 'O%c'\n"), *args);
+               }
+             break;
+
+           case 'F':
+             switch (*++args)
+               {
+               case '7':
+                 if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
+                     || imm_expr->X_op != O_constant
+                     || imm_expr->X_add_number < 0
+                     || imm_expr->X_add_number >= 128)
+                   {
+                     as_bad (_("bad value for funct7 field, "
+                               "value must be 0...127"));
+                     break;
+                   }
+
+                 INSERT_OPERAND (FUNCT7, *ip, imm_expr->X_add_number);
+                 imm_expr->X_op = O_absent;
+                 s = expr_end;
+                 continue;
+               case '3':
+                 if (my_getSmallExpression (imm_expr, imm_reloc, s, 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"));
+                     break;
+                   }
+
+                 INSERT_OPERAND (FUNCT3, *ip, imm_expr->X_add_number);
+                 imm_expr->X_op = O_absent;
+                 s = expr_end;
+                 continue;
+               case '2':
+                 if (my_getSmallExpression (imm_expr, imm_reloc, s, 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"));
+                     break;
+                   }
+
+                 INSERT_OPERAND (FUNCT2, *ip, imm_expr->X_add_number);
+                 imm_expr->X_op = O_absent;
+                 s = expr_end;
+                 continue;
+
+               default:
+                 as_bad (_("bad FUNCT field specifier 'F%c'\n"), *args);
+               }
+             break;
+
+           case 'z':
+             if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
+                 || imm_expr->X_op != O_constant
+                 || imm_expr->X_add_number != 0)
+               break;
+             s = expr_end;
+             imm_expr->X_op = O_absent;
+             continue;
 
            default:
              as_fatal (_("internal error: bad argument type %c"), *args);
@@ -1670,9 +2625,10 @@ jump:
        }
       s = argsStart;
       error = _("illegal operands");
+      insn_with_csr = FALSE;
     }
 
-out:
+ out:
   /* Restore the character we might have clobbered above.  */
   if (save_c)
     *(argsStart - 1) = save_c;
@@ -1687,7 +2643,18 @@ 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);
+  /* The arch and priv attributes should be set before assembling.  */
+  if (!start_assemble)
+    {
+      start_assemble = TRUE;
+      riscv_set_abi_by_arch ();
+
+      /* Set the default_priv_spec according to the priv attributes.  */
+      if (!riscv_set_default_priv_spec (NULL))
+       return;
+    }
+
+  const char *error = riscv_ip (str, &insn, &imm_expr, &imm_reloc, op_hash);
 
   if (error)
     {
@@ -1710,89 +2677,134 @@ 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:";
 
 enum options
 {
-  OPTION_M32 = OPTION_MD_BASE,
-  OPTION_M64,
-  OPTION_MARCH,
+  OPTION_MARCH = OPTION_MD_BASE,
   OPTION_PIC,
   OPTION_NO_PIC,
-  OPTION_MSOFT_FLOAT,
-  OPTION_MHARD_FLOAT,
-  OPTION_MRVC,
-  OPTION_MNO_RVC,
+  OPTION_MABI,
+  OPTION_RELAX,
+  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
 };
 
 struct option md_longopts[] =
 {
-  {"m32", no_argument, NULL, OPTION_M32},
-  {"m64", no_argument, NULL, OPTION_M64},
   {"march", required_argument, NULL, OPTION_MARCH},
   {"fPIC", no_argument, NULL, OPTION_PIC},
   {"fpic", no_argument, NULL, OPTION_PIC},
   {"fno-pic", no_argument, NULL, OPTION_NO_PIC},
-  {"mrvc", no_argument, NULL, OPTION_MRVC},
-  {"mno-rvc", no_argument, NULL, OPTION_MNO_RVC},
-  {"msoft-float", no_argument, NULL, OPTION_MSOFT_FLOAT},
-  {"mhard-float", no_argument, NULL, OPTION_MHARD_FLOAT},
+  {"mabi", required_argument, NULL, OPTION_MABI},
+  {"mrelax", no_argument, NULL, OPTION_RELAX},
+  {"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_mode
-{
-  FLOAT_MODE_DEFAULT,
-  FLOAT_MODE_SOFT,
-  FLOAT_MODE_HARD
-};
-static enum float_mode float_mode = FLOAT_MODE_DEFAULT;
-
 int
 md_parse_option (int c, const char *arg)
 {
   switch (c)
     {
-    case OPTION_MRVC:
-      riscv_set_rvc (TRUE);
+    case OPTION_MARCH:
+      /* riscv_after_parse_args will call riscv_set_arch to parse
+        the architecture.  */
+      default_arch_with_ext = arg;
       break;
 
-    case OPTION_MNO_RVC:
-      riscv_set_rvc (FALSE);
+    case OPTION_NO_PIC:
+      riscv_opts.pic = FALSE;
       break;
 
-    case OPTION_MSOFT_FLOAT:
-      float_mode = FLOAT_MODE_SOFT;
+    case OPTION_PIC:
+      riscv_opts.pic = TRUE;
       break;
 
-    case OPTION_MHARD_FLOAT:
-      float_mode = FLOAT_MODE_HARD;
+    case OPTION_MABI:
+      if (strcmp (arg, "ilp32") == 0)
+       riscv_set_abi (32, FLOAT_ABI_SOFT, FALSE);
+      else if (strcmp (arg, "ilp32e") == 0)
+       riscv_set_abi (32, FLOAT_ABI_SOFT, TRUE);
+      else if (strcmp (arg, "ilp32f") == 0)
+       riscv_set_abi (32, FLOAT_ABI_SINGLE, FALSE);
+      else if (strcmp (arg, "ilp32d") == 0)
+       riscv_set_abi (32, FLOAT_ABI_DOUBLE, FALSE);
+      else if (strcmp (arg, "ilp32q") == 0)
+       riscv_set_abi (32, FLOAT_ABI_QUAD, FALSE);
+      else if (strcmp (arg, "lp64") == 0)
+       riscv_set_abi (64, FLOAT_ABI_SOFT, FALSE);
+      else if (strcmp (arg, "lp64f") == 0)
+       riscv_set_abi (64, FLOAT_ABI_SINGLE, FALSE);
+      else if (strcmp (arg, "lp64d") == 0)
+       riscv_set_abi (64, FLOAT_ABI_DOUBLE, FALSE);
+      else if (strcmp (arg, "lp64q") == 0)
+       riscv_set_abi (64, FLOAT_ABI_QUAD, FALSE);
+      else
+       return 0;
+      explicit_mabi = TRUE;
       break;
 
-    case OPTION_M32:
-      xlen = 32;
+    case OPTION_RELAX:
+      riscv_opts.relax = TRUE;
       break;
 
-    case OPTION_M64:
-      xlen = 64;
+    case OPTION_NO_RELAX:
+      riscv_opts.relax = FALSE;
       break;
 
-    case OPTION_MARCH:
-      riscv_set_arch (arg);
+    case OPTION_ARCH_ATTR:
+      riscv_opts.arch_attr = TRUE;
       break;
 
-    case OPTION_NO_PIC:
-      riscv_opts.pic = FALSE;
+    case OPTION_NO_ARCH_ATTR:
+      riscv_opts.arch_attr = FALSE;
       break;
 
-    case OPTION_PIC:
-      riscv_opts.pic = TRUE;
+    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:
@@ -1805,9 +2817,10 @@ md_parse_option (int c, const char *arg)
 void
 riscv_after_parse_args (void)
 {
-  if (riscv_subsets == NULL)
-    riscv_set_arch ("RVIMAFD");
-
+  /* The --with-arch is optional for now, so we have to set the xlen
+     according to the default_arch, which is set by the --targte, first.
+     Then, we use the xlen to set the default_arch_with_ext if the
+     -march and --with-arch are not set.  */
   if (xlen == 0)
     {
       if (strcmp (default_arch, "riscv32") == 0)
@@ -1817,6 +2830,40 @@ riscv_after_parse_args (void)
       else
        as_bad ("unknown default architecture `%s'", default_arch);
     }
+  if (default_arch_with_ext == NULL)
+    default_arch_with_ext = xlen == 64 ? "rv64g" : "rv32g";
+
+  /* Initialize the hash table for extensions with default version.  */
+  ext_version_hash = init_ext_version_hash (riscv_ext_version_table);
+
+  /* If the -misa-spec isn't set, then we set the default ISA spec according
+     to DEFAULT_RISCV_ISA_SPEC.  */
+  if (default_isa_spec == ISA_SPEC_CLASS_NONE)
+    riscv_set_default_isa_spec (DEFAULT_RISCV_ISA_SPEC);
+
+  /* Set the architecture according to -march or or --with-arch.  */
+  riscv_set_arch (default_arch_with_ext);
+
+  /* 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);
+
+  /* If the -mpriv-spec isn't set, then we set the default privilege spec
+     according to DEFAULT_PRIV_SPEC.  */
+  if (default_priv_spec == PRIV_SPEC_CLASS_NONE)
+    riscv_set_default_priv_spec (DEFAULT_RISCV_PRIV_SPEC);
+
+  /* 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
@@ -1833,6 +2880,8 @@ 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;
+  offsetT loc;
+  segT sub_segment;
 
   /* Remember value for tc_gen_reloc.  */
   fixP->fx_addnumber = *valP;
@@ -1844,11 +2893,12 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
     case BFD_RELOC_RISCV_LO12_S:
       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;
       break;
 
     case BFD_RELOC_RISCV_GOT_HI20:
-    case BFD_RELOC_RISCV_PCREL_HI20:
     case BFD_RELOC_RISCV_ADD8:
     case BFD_RELOC_RISCV_ADD16:
     case BFD_RELOC_RISCV_ADD32:
@@ -1872,11 +2922,32 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
     case BFD_RELOC_RISCV_TLS_GD_HI20:
     case BFD_RELOC_RISCV_TLS_DTPREL32:
     case BFD_RELOC_RISCV_TLS_DTPREL64:
-      S_SET_THREAD_LOCAL (fixP->fx_addsy);
+      if (fixP->fx_addsy != NULL)
+       S_SET_THREAD_LOCAL (fixP->fx_addsy);
+      else
+       as_bad_where (fixP->fx_file, fixP->fx_line,
+                     _("TLS relocation against a constant"));
       break;
 
-    case BFD_RELOC_64:
     case BFD_RELOC_32:
+      /* Use pc-relative relocation for FDE initial location.
+        The symbol address in .eh_frame may be adjusted in
+        _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.*/
+      if (fixP->fx_addsy && fixP->fx_subsy
+         && (sub_segment = S_GET_SEGMENT (fixP->fx_subsy))
+         && strcmp (sub_segment->name, ".eh_frame") == 0
+         && S_GET_VALUE (fixP->fx_subsy)
+            == fixP->fx_frag->fr_address + fixP->fx_where)
+       {
+         fixP->fx_r_type = BFD_RELOC_RISCV_32_PCREL;
+         fixP->fx_subsy = NULL;
+         break;
+       }
+      /* Fall through.  */
+    case BFD_RELOC_64:
     case BFD_RELOC_16:
     case BFD_RELOC_8:
     case BFD_RELOC_RISCV_CFA:
@@ -1912,30 +2983,31 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
 
            case BFD_RELOC_RISCV_CFA:
              /* Load the byte to get the subtype.  */
-             subtype = bfd_get_8 (NULL, &fixP->fx_frag->fr_literal[fixP->fx_where]);
+             subtype = bfd_get_8 (NULL, &((fragS *) (fixP->fx_frag->fr_opcode))->fr_literal[fixP->fx_where]);
+             loc = fixP->fx_frag->fr_fix - (subtype & 7);
              switch (subtype)
                {
                case DW_CFA_advance_loc1:
-                 fixP->fx_where++;
-                 fixP->fx_next->fx_where++;
+                 fixP->fx_where = loc + 1;
+                 fixP->fx_next->fx_where = loc + 1;
                  fixP->fx_r_type = BFD_RELOC_RISCV_SET8;
                  fixP->fx_next->fx_r_type = BFD_RELOC_RISCV_SUB8;
                  break;
 
                case DW_CFA_advance_loc2:
                  fixP->fx_size = 2;
-                 fixP->fx_where++;
                  fixP->fx_next->fx_size = 2;
-                 fixP->fx_next->fx_where++;
+                 fixP->fx_where = loc + 1;
+                 fixP->fx_next->fx_where = loc + 1;
                  fixP->fx_r_type = BFD_RELOC_RISCV_SET16;
                  fixP->fx_next->fx_r_type = BFD_RELOC_RISCV_SUB16;
                  break;
 
                case DW_CFA_advance_loc4:
                  fixP->fx_size = 4;
-                 fixP->fx_where++;
                  fixP->fx_next->fx_size = 4;
-                 fixP->fx_next->fx_where++;
+                 fixP->fx_where = loc;
+                 fixP->fx_next->fx_where = loc;
                  fixP->fx_r_type = BFD_RELOC_RISCV_SET32;
                  fixP->fx_next->fx_r_type = BFD_RELOC_RISCV_SUB32;
                  break;
@@ -1944,6 +3016,8 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
                  if (subtype < 0x80 && (subtype & 0x40))
                    {
                      /* DW_CFA_advance_loc */
+                     fixP->fx_frag = (fragS *) fixP->fx_frag->fr_opcode;
+                     fixP->fx_next->fx_frag = fixP->fx_frag;
                      fixP->fx_r_type = BFD_RELOC_RISCV_SET6;
                      fixP->fx_next->fx_r_type = BFD_RELOC_RISCV_SUB6;
                    }
@@ -2017,8 +3091,12 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
       relaxable = TRUE;
       break;
 
+    case BFD_RELOC_RISCV_PCREL_HI20:
     case BFD_RELOC_RISCV_PCREL_LO12_S:
     case BFD_RELOC_RISCV_PCREL_LO12_I:
+      relaxable = riscv_opts.relax;
+      break;
+
     case BFD_RELOC_RISCV_ALIGN:
       break;
 
@@ -2028,12 +3106,17 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
        as_fatal (_("internal error: bad relocation #%d"), fixP->fx_r_type);
     }
 
+  if (fixP->fx_subsy != NULL)
+    as_bad_where (fixP->fx_file, fixP->fx_line,
+                 _("unsupported symbol subtraction"));
+
   /* Add an R_RISCV_RELAX reloc if the reloc is relaxable.  */
   if (relaxable && fixP->fx_tcbit && fixP->fx_addsy != NULL)
     {
       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;
     }
 }
 
@@ -2044,34 +3127,42 @@ 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)
       {
-       const fragS *frag;
+       fragS *frag;
 
        for (frag = frch->frch_root; frag; frag = frag->fr_next)
          {
            if (frag->fr_type == rs_cfa)
              {
-               const fragS *loc4_frag;
                expressionS exp;
+               expressionS *symval;
 
-               symbolS *add_symbol = frag->fr_symbol->sy_value.X_add_symbol;
-               symbolS *op_symbol = frag->fr_symbol->sy_value.X_op_symbol;
-
+               symval = symbol_get_value_expression (frag->fr_symbol);
                exp.X_op = O_subtract;
-               exp.X_add_symbol = add_symbol;
+               exp.X_add_symbol = symval->X_add_symbol;
                exp.X_add_number = 0;
-               exp.X_op_symbol = op_symbol;
+               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);
 
-               loc4_frag = (fragS *) frag->fr_opcode;
-               fix_new_exp (loc4_frag, (int) frag->fr_offset, 1, &exp, 0,
+               fix_new_exp (frag, (int) frag->fr_offset, 1, &exp, 0,
                             BFD_RELOC_RISCV_CFA);
              }
          }
       }
+
+  /* Restore the original segment info.  */
+  subseg_set (seg, subseg);
 }
 
 
@@ -2109,6 +3200,10 @@ s_riscv_option (int x ATTRIBUTE_UNUSED)
     riscv_opts.relax = TRUE;
   else if (strcmp (name, "norelax") == 0)
     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 (strcmp (name, "push") == 0)
     {
       struct riscv_option_stack *s;
@@ -2179,63 +3274,103 @@ s_bss (int ignore ATTRIBUTE_UNUSED)
   demand_empty_rest_of_line ();
 }
 
-/* Align to a given power of two.  */
-
 static void
-s_align (int bytes_p)
+riscv_make_nops (char *buf, bfd_vma bytes)
 {
-  int fill_value = 0, fill_value_specified = 0;
-  int min_text_alignment = riscv_opts.rvc ? 2 : 4;
-  int alignment = get_absolute_expression(), bytes;
+  bfd_vma i = 0;
 
-  if (bytes_p)
+  /* RISC-V instructions cannot begin or end on odd addresses, so this case
+     means we are not within a valid instruction sequence.  It is thus safe
+     to use a zero byte, even though that is not a valid instruction.  */
+  if (bytes % 2 == 1)
+    buf[i++] = 0;
+
+  /* Use at most one 2-byte NOP.  */
+  if ((bytes - i) % 4 == 2)
     {
-      bytes = alignment;
-      if (bytes < 1 || (bytes & (bytes-1)) != 0)
-       as_bad (_("alignment not a power of 2: %d"), bytes);
-      for (alignment = 0; bytes > 1; bytes >>= 1)
-       alignment++;
+      number_to_chars_littleendian (buf + i, RVC_NOP, 2);
+      i += 2;
     }
 
-  bytes = 1 << alignment;
+  /* Fill the remainder with 4-byte NOPs.  */
+  for ( ; i < bytes; i += 4)
+    number_to_chars_littleendian (buf + i, RISCV_NOP, 4);
+}
+
+/* Called from md_do_align.  Used to create an alignment frag in a
+   code section by emitting a worst-case NOP sequence that the linker
+   will later relax to the correct number of NOPs.  We can't compute
+   the correct alignment now because of other linker relaxations.  */
 
-  if (alignment < 0 || alignment > 31)
-    as_bad (_("unsatisfiable alignment: %d"), alignment);
+bfd_boolean
+riscv_frag_align_code (int n)
+{
+  bfd_vma bytes = (bfd_vma) 1 << n;
+  bfd_vma insn_alignment = riscv_opts.rvc ? 2 : 4;
+  bfd_vma worst_case_bytes = bytes - insn_alignment;
+  char *nops;
+  expressionS ex;
 
-  if (*input_line_pointer == ',')
-    {
-      ++input_line_pointer;
-      fill_value = get_absolute_expression ();
-      fill_value_specified = 1;
-    }
+  /* If we are moving to a smaller alignment than the instruction size, then no
+     alignment is required. */
+  if (bytes <= insn_alignment)
+    return TRUE;
+
+  /* When not relaxing, riscv_handle_align handles code alignment.  */
+  if (!riscv_opts.relax)
+    return FALSE;
+
+  nops = frag_more (worst_case_bytes);
 
-  if (!fill_value_specified
-      && subseg_text_p (now_seg)
-      && bytes > min_text_alignment)
+  ex.X_op = O_constant;
+  ex.X_add_number = worst_case_bytes;
+
+  riscv_make_nops (nops, worst_case_bytes);
+
+  fix_new_exp (frag_now, nops - frag_now->fr_literal, 0,
+              &ex, FALSE, BFD_RELOC_RISCV_ALIGN);
+
+  return TRUE;
+}
+
+/* Implement HANDLE_ALIGN.  */
+
+void
+riscv_handle_align (fragS *fragP)
+{
+  switch (fragP->fr_type)
     {
-      /* Emit the worst-case NOP string.  The linker will delete any
-        unnecessary NOPs.  This allows us to support code alignment
-        in spite of linker relaxations.  */
-      bfd_vma i, worst_case_bytes = bytes - min_text_alignment;
-      char *nops = frag_more (worst_case_bytes);
-      for (i = 0; i < worst_case_bytes - 2; i += 4)
-       md_number_to_chars (nops + i, RISCV_NOP, 4);
-      if (i < worst_case_bytes)
-       md_number_to_chars (nops + i, RVC_NOP, 2);
-
-      expressionS ex;
-      ex.X_op = O_constant;
-      ex.X_add_number = worst_case_bytes;
-
-      fix_new_exp (frag_now, nops - frag_now->fr_literal, 0,
-                  &ex, FALSE, BFD_RELOC_RISCV_ALIGN);
-    }
-  else if (alignment)
-    frag_align (alignment, fill_value, 0);
+    case rs_align_code:
+      /* When relaxing, riscv_frag_align_code handles code alignment.  */
+      if (!riscv_opts.relax)
+       {
+         bfd_signed_vma bytes = (fragP->fr_next->fr_address
+                                 - fragP->fr_address - fragP->fr_fix);
+         /* We have 4 byte uncompressed nops.  */
+         bfd_signed_vma size = 4;
+         bfd_signed_vma excess = bytes % size;
+         char *p = fragP->fr_literal + fragP->fr_fix;
+
+         if (bytes <= 0)
+           break;
 
-  record_alignment (now_seg, alignment);
+         /* Insert zeros or compressed nops to get 4 byte alignment.  */
+         if (excess)
+           {
+             riscv_make_nops (p, excess);
+             fragP->fr_fix += excess;
+             p += excess;
+           }
 
-  demand_empty_rest_of_line ();
+         /* Insert variable number of 4 byte uncompressed nops.  */
+         riscv_make_nops (p, size);
+         fragP->fr_var = size;
+       }
+      break;
+
+    default:
+      break;
+    }
 }
 
 int
@@ -2363,14 +3498,14 @@ md_convert_frag_branch (fragS *fragp)
       insn = bfd_getl32 (buf);
       insn ^= MATCH_BEQ ^ MATCH_BNE;
       insn |= ENCODE_SBTYPE_IMM (8);
-      md_number_to_chars ((char *) buf, insn, 4);
+      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);
+      bfd_putl32 (MATCH_JAL, buf);
       buf += 4;
       break;
 
@@ -2386,7 +3521,7 @@ jump:
       abort ();
     }
 
-done:
+ done:
   fixp->fx_file = fragp->fr_file;
   fixp->fx_line = fragp->fr_line;
 
@@ -2412,15 +3547,16 @@ md_show_usage (FILE *stream)
 {
   fprintf (stream, _("\
 RISC-V options:\n\
-  -m32           assemble RV32 code\n\
-  -m64           assemble RV64 code (default)\n\
-  -fpic          generate position-independent code\n\
-  -fno-pic       don't generate position-independent code (default)\n\
-  -msoft-float   don't use F registers for floating-point values\n\
-  -mhard-float   use F registers for floating-point values (default)\n\
-  -mno-rvc       disable the C extension for compressed instructions (default)\n\
-  -mrvc          enable the C extension for compressed instructions\n\
-  -march=ISA     set the RISC-V architecture, RV64IMAFD by default\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\
+  -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.9.1, 1.10, 1.11)\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\
 "));
 }
 
@@ -2442,6 +3578,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;
 }
@@ -2449,24 +3589,8 @@ tc_riscv_regname_to_dw2regnum (char *regname)
 void
 riscv_elf_final_processing (void)
 {
-  enum float_mode elf_float_mode = float_mode;
-
+  riscv_set_abi_by_arch ();
   elf_elfheader (stdoutput)->e_flags |= elf_flags;
-
-  if (elf_float_mode == FLOAT_MODE_DEFAULT)
-    {
-      struct riscv_subset *subset;
-
-      /* Assume soft-float unless D extension is present.  */
-      elf_float_mode = FLOAT_MODE_SOFT;
-
-      for (subset = riscv_subsets; subset != NULL; subset = subset->next)
-       if (strcasecmp (subset->name, "D") == 0)
-         elf_float_mode = FLOAT_MODE_HARD;
-    }
-
-  if (elf_float_mode == FLOAT_MODE_SOFT)
-    elf_elfheader (stdoutput)->e_flags |= EF_RISCV_SOFT_FLOAT;
 }
 
 /* Parse the .sleb128 and .uleb128 pseudos.  Only allow constant expressions,
@@ -2487,6 +3611,199 @@ s_riscv_leb128 (int sign)
   return s_leb128 (sign);
 }
 
+/* Parse the .insn directive.  */
+
+static void
+s_riscv_insn (int x ATTRIBUTE_UNUSED)
+{
+  char *str = input_line_pointer;
+  struct riscv_cl_insn insn;
+  expressionS imm_expr;
+  bfd_reloc_code_real_type imm_reloc = BFD_RELOC_UNUSED;
+  char save_c;
+
+  while (!is_end_of_line[(unsigned char) *input_line_pointer])
+    ++input_line_pointer;
+
+  save_c = *input_line_pointer;
+  *input_line_pointer = '\0';
+
+  const char *error = riscv_ip (str, &insn, &imm_expr,
+                               &imm_reloc, insn_type_hash);
+
+  if (error)
+    {
+      as_bad ("%s `%s'", error, str);
+    }
+  else
+    {
+      gas_assert (insn.insn_mo->pinfo != INSN_MACRO);
+      append_insn (&insn, &imm_expr, imm_reloc);
+    }
+
+  *input_line_pointer = save_c;
+  demand_empty_rest_of_line ();
+}
+
+/* Update arch and priv attributes.  If we don't set the corresponding ELF
+   attributes, then try to output the default ones.  */
+
+static void
+riscv_write_out_attrs (void)
+{
+  const char *arch_str, *priv_str, *p;
+  /* versions[0] is major, versions[1] is minor,
+     and versions[3] is revision.  */
+  unsigned versions[3] = {0}, number = 0;
+  unsigned int i;
+
+  /* Re-write arch attribute to normalize the arch string.  */
+  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 priv attributes since the md_assemble isn't called.
+     Call riscv_set_default_priv_spec here for the above case, although
+     it seems strange.  */
+  if (!start_assemble
+      && !riscv_set_default_priv_spec (NULL))
+    return;
+
+  /* If we already have set elf priv attributes, then no need to do anything,
+     assembler will generate them according to what you set.  Otherwise, don't
+     generate or update them when no CSR and priv instructions are used.
+     Generate the priv attributes according to default_priv_spec, which can be
+     set by -mpriv-spec and --with-priv-spec, and be updated by the original
+     priv attribute sets.  */
+  if (!explicit_priv_attr)
+    return;
+
+  /* Re-write priv attributes by default_priv_spec.  */
+  priv_str = riscv_get_priv_spec_name (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 priv spec string (%s)"), priv_str);
+         return;
+       }
+    }
+  versions[i] = number;
+
+  /* Set the priv 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.  If any
+   ELF attribute or -march-attr options is set, call riscv_write_out_attrs
+   to update the arch and priv attributes.  */
+
+static void
+riscv_set_public_attributes (void)
+{
+  if (riscv_opts.arch_attr || explicit_attr)
+    riscv_write_out_attrs ();
+}
+
+/* Called after all assembly has been done.  */
+
+void
+riscv_md_end (void)
+{
+  riscv_set_public_attributes ();
+}
+
+/* Given a symbolic attribute NAME, return the proper integer value.
+   Returns -1 if the attribute is not known.  */
+
+int
+riscv_convert_symbolic_attribute (const char *name)
+{
+  static const struct
+  {
+    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),
+#undef T
+    };
+
+  unsigned int i;
+
+  if (name == NULL)
+    return -1;
+
+  for (i = 0; i < ARRAY_SIZE (attribute_table); i++)
+    if (strcmp (name, attribute_table[i].name) == 0)
+      return attribute_table[i].tag;
+
+  return -1;
+}
+
+/* Parse a .attribute directive.  */
+
+static void
+s_riscv_attribute (int ignored ATTRIBUTE_UNUSED)
+{
+  int tag = obj_elf_vendor_attribute (OBJ_ATTR_PROC);
+  unsigned old_xlen;
+  obj_attribute *attr;
+
+  explicit_attr = TRUE;
+  switch (tag)
+    {
+    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"));
+
+      if (old_xlen != xlen)
+       {
+         /* We must re-init bfd again if xlen is changed.  */
+         unsigned long mach = xlen == 64 ? bfd_mach_riscv64 : bfd_mach_riscv32;
+         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"));
+       }
+      break;
+
+    case Tag_RISCV_priv_spec:
+    case Tag_RISCV_priv_spec_minor:
+    case Tag_RISCV_priv_spec_revision:
+      if (start_assemble)
+       as_fatal (_(".attribute priv spec must set before any instructions"));
+      break;
+
+    default:
+      break;
+    }
+}
+
 /* Pseudo-op table.  */
 
 static const pseudo_typeS riscv_pseudo_table[] =
@@ -2499,11 +3816,10 @@ static const pseudo_typeS riscv_pseudo_table[] =
   {"dtprelword", s_dtprel, 4},
   {"dtpreldword", s_dtprel, 8},
   {"bss", s_bss, 0},
-  {"align", s_align, 0},
-  {"p2align", s_align, 0},
-  {"balign", s_align, 1},
   {"uleb128", s_riscv_leb128, 0},
   {"sleb128", s_riscv_leb128, 1},
+  {"insn", s_riscv_insn, 0},
+  {"attribute", s_riscv_attribute, 0},
 
   { NULL, NULL, 0 },
 };