]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blobdiff - gas/config/tc-aarch64.c
Update year range in copyright notice of binutils files
[thirdparty/binutils-gdb.git] / gas / config / tc-aarch64.c
index 3bccfa24ba7412214a176e0ef47c1d3bb12ce9b7..b04605cb1aadce009d6454df3392936ee433f495 100644 (file)
@@ -1,6 +1,6 @@
 /* tc-aarch64.c -- Assemble for the AArch64 ISA
 
-   Copyright (C) 2009-2019 Free Software Foundation, Inc.
+   Copyright (C) 2009-2021 Free Software Foundation, Inc.
    Contributed by ARM Ltd.
 
    This file is part of GAS.
@@ -247,12 +247,6 @@ set_fatal_syntax_error (const char *error)
    present. */
 #define COND_ALWAYS 0x10
 
-typedef struct
-{
-  const char *template;
-  unsigned long value;
-} asm_barrier_opt;
-
 typedef struct
 {
   const char *template;
@@ -451,21 +445,21 @@ get_reg_expected_msg (aarch64_reg_type reg_type)
 /* Instructions take 4 bytes in the object file.  */
 #define INSN_SIZE      4
 
-static struct hash_control *aarch64_ops_hsh;
-static struct hash_control *aarch64_cond_hsh;
-static struct hash_control *aarch64_shift_hsh;
-static struct hash_control *aarch64_sys_regs_hsh;
-static struct hash_control *aarch64_pstatefield_hsh;
-static struct hash_control *aarch64_sys_regs_ic_hsh;
-static struct hash_control *aarch64_sys_regs_dc_hsh;
-static struct hash_control *aarch64_sys_regs_at_hsh;
-static struct hash_control *aarch64_sys_regs_tlbi_hsh;
-static struct hash_control *aarch64_sys_regs_sr_hsh;
-static struct hash_control *aarch64_reg_hsh;
-static struct hash_control *aarch64_barrier_opt_hsh;
-static struct hash_control *aarch64_nzcv_hsh;
-static struct hash_control *aarch64_pldop_hsh;
-static struct hash_control *aarch64_hint_opt_hsh;
+static htab_t aarch64_ops_hsh;
+static htab_t aarch64_cond_hsh;
+static htab_t aarch64_shift_hsh;
+static htab_t aarch64_sys_regs_hsh;
+static htab_t aarch64_pstatefield_hsh;
+static htab_t aarch64_sys_regs_ic_hsh;
+static htab_t aarch64_sys_regs_dc_hsh;
+static htab_t aarch64_sys_regs_at_hsh;
+static htab_t aarch64_sys_regs_tlbi_hsh;
+static htab_t aarch64_sys_regs_sr_hsh;
+static htab_t aarch64_reg_hsh;
+static htab_t aarch64_barrier_opt_hsh;
+static htab_t aarch64_nzcv_hsh;
+static htab_t aarch64_pldop_hsh;
+static htab_t aarch64_hint_opt_hsh;
 
 /* Stuff needed to resolve the label ambiguity
    As:
@@ -529,7 +523,7 @@ const char EXP_CHARS[] = "eE";
 /* As in 0f12.456  */
 /* or   0d1.2345e12  */
 
-const char FLT_CHARS[] = "rRsSfFdDxXeEpP";
+const char FLT_CHARS[] = "rRsSfFdDxXeEpPhH";
 
 /* Prefix character that indicates the start of an immediate value.  */
 #define is_immediate_prefix(C) ((C) == '#')
@@ -635,6 +629,54 @@ my_get_expression (expressionS * ep, char **str, int prefix_mode,
 const char *
 md_atof (int type, char *litP, int *sizeP)
 {
+  /* If this is a bfloat16 type, then parse it slightly differently -
+     as it does not follow the IEEE standard exactly.  */
+  if (type == 'b')
+    {
+      char * t;
+      LITTLENUM_TYPE words[MAX_LITTLENUMS];
+      FLONUM_TYPE generic_float;
+
+      t = atof_ieee_detail (input_line_pointer, 1, 8, words, &generic_float);
+
+      if (t)
+       input_line_pointer = t;
+      else
+       return _("invalid floating point number");
+
+      switch (generic_float.sign)
+       {
+       /* Is +Inf.  */
+       case 'P':
+         words[0] = 0x7f80;
+         break;
+
+       /* Is -Inf.  */
+       case 'N':
+         words[0] = 0xff80;
+         break;
+
+       /* Is NaN.  */
+       /* bfloat16 has two types of NaN - quiet and signalling.
+          Quiet NaN has bit[6] == 1 && faction != 0, whereas
+          signalling Nan's have bit[0] == 0 && fraction != 0.
+          Chose this specific encoding as it is the same form
+          as used by other IEEE 754 encodings in GAS.  */
+       case 0:
+         words[0] = 0x7fff;
+         break;
+
+       default:
+         break;
+       }
+
+      *sizeP = 2;
+
+      md_number_to_chars (litP, (valueT) words[0], sizeof (LITTLENUM_TYPE));
+
+      return NULL;
+    }
+
   return ieee_md_atof (type, litP, sizeP, target_big_endian);
 }
 
@@ -716,7 +758,7 @@ parse_reg (char **ccp)
     p++;
   while (ISALPHA (*p) || ISDIGIT (*p) || *p == '_');
 
-  reg = (reg_entry *) hash_find_n (aarch64_reg_hsh, start, p - start);
+  reg = (reg_entry *) str_hash_find_n (aarch64_reg_hsh, start, p - start);
 
   if (!reg)
     return NULL;
@@ -835,7 +877,7 @@ parse_vector_type_for_operand (aarch64_reg_type reg_type,
       return FALSE;
     }
 
-elt_size:
+ elt_size:
   switch (TOLOWER (*ptr))
     {
     case 'b':
@@ -1267,7 +1309,7 @@ insert_reg_alias (char *str, int number, aarch64_reg_type type)
   reg_entry *new;
   const char *name;
 
-  if ((new = hash_find (aarch64_reg_hsh, str)) != 0)
+  if ((new = str_hash_find (aarch64_reg_hsh, str)) != 0)
     {
       if (new->builtin)
        as_warn (_("ignoring attempt to redefine built-in register '%s'"),
@@ -1289,8 +1331,7 @@ insert_reg_alias (char *str, int number, aarch64_reg_type type)
   new->type = type;
   new->builtin = FALSE;
 
-  if (hash_insert (aarch64_reg_hsh, name, (void *) new))
-    abort ();
+  str_hash_insert (aarch64_reg_hsh, name, new, 0);
 
   return new;
 }
@@ -1319,7 +1360,7 @@ create_register_alias (char *newname, char *p)
   if (*oldname == '\0')
     return FALSE;
 
-  old = hash_find (aarch64_reg_hsh, oldname);
+  old = str_hash_find (aarch64_reg_hsh, oldname);
   if (!old)
     {
       as_warn (_("unknown register '%s' -- .req ignored"), oldname);
@@ -1408,7 +1449,7 @@ s_unreq (int a ATTRIBUTE_UNUSED)
     as_bad (_("invalid syntax for .unreq directive"));
   else
     {
-      reg_entry *reg = hash_find (aarch64_reg_hsh, name);
+      reg_entry *reg = str_hash_find (aarch64_reg_hsh, name);
 
       if (!reg)
        as_bad (_("unknown register alias '%s'"), name);
@@ -1420,7 +1461,7 @@ s_unreq (int a ATTRIBUTE_UNUSED)
          char *p;
          char *nbuf;
 
-         hash_delete (aarch64_reg_hsh, name, FALSE);
+         str_hash_delete (aarch64_reg_hsh, name);
          free ((char *) reg->name);
          free (reg);
 
@@ -1431,20 +1472,20 @@ s_unreq (int a ATTRIBUTE_UNUSED)
          nbuf = strdup (name);
          for (p = nbuf; *p; p++)
            *p = TOUPPER (*p);
-         reg = hash_find (aarch64_reg_hsh, nbuf);
+         reg = str_hash_find (aarch64_reg_hsh, nbuf);
          if (reg)
            {
-             hash_delete (aarch64_reg_hsh, nbuf, FALSE);
+             str_hash_delete (aarch64_reg_hsh, nbuf);
              free ((char *) reg->name);
              free (reg);
            }
 
          for (p = nbuf; *p; p++)
            *p = TOLOWER (*p);
-         reg = hash_find (aarch64_reg_hsh, nbuf);
+         reg = str_hash_find (aarch64_reg_hsh, nbuf);
          if (reg)
            {
-             hash_delete (aarch64_reg_hsh, nbuf, FALSE);
+             str_hash_delete (aarch64_reg_hsh, nbuf);
              free ((char *) reg->name);
              free (reg);
            }
@@ -1488,7 +1529,7 @@ make_mapping_symbol (enum mstate state, valueT value, fragS * frag)
       abort ();
     }
 
-  symbolP = symbol_new (symname, now_seg, value, frag);
+  symbolP = symbol_new (symname, now_seg, frag, value);
   symbol_get_bfdsym (symbolP)->flags |= type | BSF_LOCAL;
 
   /* Save the mapping symbols for future reference.  Also check that
@@ -1695,7 +1736,7 @@ find_or_make_literal_pool (int size)
   if (pool->symbol == NULL)
     {
       pool->symbol = symbol_create (FAKE_LABEL_NAME, undefined_section,
-                                   (valueT) 0, &zero_address_frag);
+                                   &zero_address_frag, 0);
       pool->id = latest_pool_num++;
     }
 
@@ -1954,7 +1995,7 @@ s_variant_pcs (int ignored ATTRIBUTE_UNUSED)
   restore_line_pointer (c);
   demand_empty_rest_of_line ();
   bfdsym = symbol_get_bfdsym (sym);
-  elfsym = elf_symbol_from (bfd_asymbol_bfd (bfdsym), bfdsym);
+  elfsym = elf_symbol_from (bfdsym);
   gas_assert (elfsym);
   elfsym->internal_elf_sym.st_other |= STO_AARCH64_VARIANT_PCS;
 }
@@ -2106,6 +2147,8 @@ const pseudo_typeS md_pseudo_table[] = {
   {"dword", s_aarch64_elf_cons, 8},
   {"variant_pcs", s_variant_pcs, 0},
 #endif
+  {"float16", float_cons, 'h'},
+  {"bfloat16", float_cons, 'b'},
   {0, 0, 0}
 };
 \f
@@ -2140,7 +2183,7 @@ reg_name_p (char *str, aarch64_reg_type reg_type)
     return FALSE;
 
   skip_whitespace (str);
-  if (*str == ',' || is_end_of_line[(unsigned int) *str])
+  if (*str == ',' || is_end_of_line[(unsigned char) *str])
     return TRUE;
 
   return FALSE;
@@ -2374,7 +2417,7 @@ parse_aarch64_imm_float (char **ccp, int *immed, bfd_boolean dp_p,
   *ccp = str;
   return TRUE;
 
-invalid_fp:
+ invalid_fp:
   set_fatal_syntax_error (_("invalid floating-point constant"));
   return FALSE;
 }
@@ -3072,7 +3115,7 @@ parse_shift (char **str, aarch64_opnd_info *operand, enum parse_shift_mode mode)
       return FALSE;
     }
 
-  shift_op = hash_find_n (aarch64_shift_hsh, *str, p - *str);
+  shift_op = str_hash_find_n (aarch64_shift_hsh, *str, p - *str);
 
   if (shift_op == NULL)
     {
@@ -3401,6 +3444,7 @@ parse_shifter_operand_reloc (char **str, aarch64_opnd_info *operand,
      [base,Xm,SXTX {#imm}]
      [base,Wm,(S|U)XTW {#imm}]
    Pre-indexed
+     [base]!                    // in ldraa/ldrab exclusive
      [base,#imm]!
    Post-indexed
      [base],#imm
@@ -3715,29 +3759,42 @@ parse_address_main (char **str, aarch64_opnd_info *operand,
     }
 
   /* If at this point neither .preind nor .postind is set, we have a
-     bare [Rn]{!}; reject [Rn]! accept [Rn] as a shorthand for [Rn,#0].
+     bare [Rn]{!}; only accept [Rn]! as a shorthand for [Rn,#0]! for ldraa and
+     ldrab, accept [Rn] as a shorthand for [Rn,#0].
      For SVE2 vector plus scalar offsets, allow [Zn.<T>] as shorthand for
      [Zn.<T>, xzr].  */
   if (operand->addr.preind == 0 && operand->addr.postind == 0)
     {
       if (operand->addr.writeback)
        {
-         /* Reject [Rn]!   */
-         set_syntax_error (_("missing offset in the pre-indexed address"));
-         return FALSE;
-       }
-
-      operand->addr.preind = 1;
-      if (operand->type == AARCH64_OPND_SVE_ADDR_ZX)
-       {
-         operand->addr.offset.is_reg = 1;
-         operand->addr.offset.regno = REG_ZR;
-         *offset_qualifier = AARCH64_OPND_QLF_X;
+         if (operand->type == AARCH64_OPND_ADDR_SIMM10)
+            {
+              /* Accept [Rn]! as a shorthand for [Rn,#0]!   */
+              operand->addr.offset.is_reg = 0;
+              operand->addr.offset.imm = 0;
+              operand->addr.preind = 1;
+            }
+          else
+           {
+            /* Reject [Rn]!   */
+            set_syntax_error (_("missing offset in the pre-indexed address"));
+            return FALSE;
+          }
        }
-      else
+       else
        {
-         inst.reloc.exp.X_op = O_constant;
-         inst.reloc.exp.X_add_number = 0;
+          operand->addr.preind = 1;
+          if (operand->type == AARCH64_OPND_SVE_ADDR_ZX)
+          {
+            operand->addr.offset.is_reg = 1;
+            operand->addr.offset.regno = REG_ZR;
+            *offset_qualifier = AARCH64_OPND_QLF_X;
+          }
+          else
+          {
+            inst.reloc.exp.X_op = O_constant;
+            inst.reloc.exp.X_add_number = 0;
+          }
        }
     }
 
@@ -3915,7 +3972,7 @@ parse_pldop (char **str)
   while (ISALNUM (*q))
     q++;
 
-  o = hash_find_n (aarch64_pldop_hsh, p, q - p);
+  o = str_hash_find_n (aarch64_pldop_hsh, p, q - p);
   if (!o)
     return PARSE_FAIL;
 
@@ -3930,13 +3987,13 @@ static int
 parse_barrier (char **str)
 {
   char *p, *q;
-  const asm_barrier_opt *o;
+  const struct aarch64_name_value_pair *o;
 
   p = q = *str;
   while (ISALPHA (*q))
     q++;
 
-  o = hash_find_n (aarch64_barrier_opt_hsh, p, q - p);
+  o = str_hash_find_n (aarch64_barrier_opt_hsh, p, q - p);
   if (!o)
     return PARSE_FAIL;
 
@@ -3958,11 +4015,11 @@ parse_barrier_psb (char **str,
   while (ISALPHA (*q))
     q++;
 
-  o = hash_find_n (aarch64_hint_opt_hsh, p, q - p);
+  o = str_hash_find_n (aarch64_hint_opt_hsh, p, q - p);
   if (!o)
     {
       set_fatal_syntax_error
-       ( _("unknown or missing option to PSB"));
+       ( _("unknown or missing option to PSB/TSB"));
       return PARSE_FAIL;
     }
 
@@ -3970,7 +4027,7 @@ parse_barrier_psb (char **str,
     {
       /* PSB only accepts option name 'CSYNC'.  */
       set_syntax_error
-       (_("the specified option is not accepted for PSB"));
+       (_("the specified option is not accepted for PSB/TSB"));
       return PARSE_FAIL;
     }
 
@@ -3979,6 +4036,29 @@ parse_barrier_psb (char **str,
   return 0;
 }
 
+/* Parse an operand for CSR (CSRE instruction).  */
+
+static int
+parse_csr_operand (char **str)
+{
+  char *p, *q;
+
+  p = q = *str;
+  while (ISALPHA (*q))
+    q++;
+
+  /* Instruction has only one operand PDEC which encodes Rt field of the
+     operation to 0b11111.  */
+  if (strcasecmp(p, "pdec"))
+  {
+    set_syntax_error (_("CSR instruction accepts only PDEC"));
+    return PARSE_FAIL;
+  }
+
+  *str = q;
+  return 0;
+}
+
 /* Parse an operand for BTI.  Set *HINT_OPT to the hint-option record
    return 0 if successful.  Otherwise return PARSE_FAIL.  */
 
@@ -3993,7 +4073,7 @@ parse_bti_operand (char **str,
   while (ISALPHA (*q))
     q++;
 
-  o = hash_find_n (aarch64_hint_opt_hsh, p, q - p);
+  o = str_hash_find_n (aarch64_hint_opt_hsh, p, q - p);
   if (!o)
     {
       set_fatal_syntax_error
@@ -4031,24 +4111,28 @@ parse_bti_operand (char **str,
 */
 
 static int
-parse_sys_reg (char **str, struct hash_control *sys_regs,
+parse_sys_reg (char **str, htab_t sys_regs,
               int imple_defined_p, int pstatefield_p,
               uint32_t* flags)
 {
   char *p, *q;
-  char buf[32];
+  char buf[AARCH64_MAX_SYSREG_NAME_LEN];
   const aarch64_sys_reg *o;
   int value;
 
   p = buf;
   for (q = *str; ISALNUM (*q) || *q == '_'; q++)
-    if (p < buf + 31)
+    if (p < buf + (sizeof (buf) - 1))
       *p++ = TOLOWER (*q);
   *p = '\0';
-  /* Assert that BUF be large enough.  */
-  gas_assert (p - buf == q - *str);
 
-  o = hash_find (sys_regs, buf);
+  /* If the name is longer than AARCH64_MAX_SYSREG_NAME_LEN then it cannot be a
+     valid system register.  This is enforced by construction of the hash
+     table.  */
+  if (p - buf != q - *str)
+    return PARSE_FAIL;
+
+  o = str_hash_find (sys_regs, buf);
   if (!o)
     {
       if (!imple_defined_p)
@@ -4073,10 +4157,12 @@ parse_sys_reg (char **str, struct hash_control *sys_regs,
       if (pstatefield_p && !aarch64_pstatefield_supported_p (cpu_variant, o))
        as_bad (_("selected processor does not support PSTATE field "
                  "name '%s'"), buf);
-      if (!pstatefield_p && !aarch64_sys_reg_supported_p (cpu_variant, o))
+      if (!pstatefield_p
+         && !aarch64_sys_ins_reg_supported_p (cpu_variant, o->name,
+                                              o->value, o->flags, o->features))
        as_bad (_("selected processor does not support system register "
                  "name '%s'"), buf);
-      if (aarch64_sys_reg_deprecated_p (o))
+      if (aarch64_sys_reg_deprecated_p (o->flags))
        as_warn (_("system register name '%s' is deprecated and may be "
                   "removed in a future release"), buf);
       value = o->value;
@@ -4092,25 +4178,35 @@ parse_sys_reg (char **str, struct hash_control *sys_regs,
    for the option, or NULL.  */
 
 static const aarch64_sys_ins_reg *
-parse_sys_ins_reg (char **str, struct hash_control *sys_ins_regs)
+parse_sys_ins_reg (char **str, htab_t sys_ins_regs)
 {
   char *p, *q;
-  char buf[32];
+  char buf[AARCH64_MAX_SYSREG_NAME_LEN];
   const aarch64_sys_ins_reg *o;
 
   p = buf;
   for (q = *str; ISALNUM (*q) || *q == '_'; q++)
-    if (p < buf + 31)
+    if (p < buf + (sizeof (buf) - 1))
       *p++ = TOLOWER (*q);
   *p = '\0';
 
-  o = hash_find (sys_ins_regs, buf);
+  /* If the name is longer than AARCH64_MAX_SYSREG_NAME_LEN then it cannot be a
+     valid system register.  This is enforced by construction of the hash
+     table.  */
+  if (p - buf != q - *str)
+    return NULL;
+
+  o = str_hash_find (sys_ins_regs, buf);
   if (!o)
     return NULL;
 
-  if (!aarch64_sys_ins_reg_supported_p (cpu_variant, o))
+  if (!aarch64_sys_ins_reg_supported_p (cpu_variant,
+                                       o->name, o->value, o->flags, 0))
     as_bad (_("selected processor does not support system register "
              "name '%s'"), buf);
+  if (aarch64_sys_reg_deprecated_p (o->flags))
+    as_warn (_("system register name '%s' is deprecated and may be "
+          "removed in a future release"), buf);
 
   *str = q;
   return o;
@@ -4264,7 +4360,10 @@ reencode_movzn_to_movn (uint32_t opcode)
 static fixS *
 fix_new_aarch64 (fragS * frag,
                 int where,
-                short int size, expressionS * exp, int pc_rel, int reloc)
+                short int size,
+                expressionS * exp,
+                int pc_rel,
+                int reloc)
 {
   fixS *new_fix;
 
@@ -4634,7 +4733,7 @@ print_operands (char *buf, const aarch64_opcode *opcode,
 
       /* Generate the operand string in STR.  */
       aarch64_print_operand (str, sizeof (str), 0, opcode, opnds, i, NULL, NULL,
-                            NULL);
+                            NULL, cpu_variant);
 
       /* Delimiter.  */
       if (str[0] != '\0')
@@ -4960,7 +5059,8 @@ get_aarch64_insn (char *buf)
 {
   unsigned char *where = (unsigned char *) buf;
   uint32_t result;
-  result = (where[0] | (where[1] << 8) | (where[2] << 16) | (where[3] << 24));
+  result = ((where[0] | (where[1] << 8) | (where[2] << 16)
+            | ((uint32_t) where[3] << 24)));
   return result;
 }
 
@@ -5013,7 +5113,7 @@ lookup_mnemonic (const char *start, int len)
 {
   templates *templ = NULL;
 
-  templ = hash_find_n (aarch64_ops_hsh, start, len);
+  templ = str_hash_find_n (aarch64_ops_hsh, start, len);
   return templ;
 }
 
@@ -5044,7 +5144,7 @@ opcode_lookup (char **str)
   /* Handle a possible condition.  */
   if (dot)
     {
-      cond = hash_find_n (aarch64_cond_hsh, dot + 1, end - dot - 1);
+      cond = str_hash_find_n (aarch64_cond_hsh, dot + 1, end - dot - 1);
       if (cond)
        {
          inst.cond = cond->value;
@@ -5115,6 +5215,10 @@ vectype_to_qualifier (const struct vector_type_el *vectype)
       if (vectype->type == NT_b && vectype->width == 4)
        return AARCH64_OPND_QLF_S_4B;
 
+      /* Special case S_2H.  */
+      if (vectype->type == NT_h && vectype->width == 2)
+       return AARCH64_OPND_QLF_S_2H;
+
       /* Vector element register.  */
       return AARCH64_OPND_QLF_S_B + vectype->type;
     }
@@ -5147,7 +5251,7 @@ vectype_to_qualifier (const struct vector_type_el *vectype)
       return offset;
     }
 
-vectype_conversion_fail:
+ vectype_conversion_fail:
   first_error (_("bad vector arrangement type"));
   return AARCH64_OPND_QLF_NIL;
 }
@@ -5172,6 +5276,7 @@ process_omitted_operand (enum aarch64_opnd type, const aarch64_opcode *opcode,
     case AARCH64_OPND_Rm:
     case AARCH64_OPND_Rt:
     case AARCH64_OPND_Rt2:
+    case AARCH64_OPND_Rt_LS64:
     case AARCH64_OPND_Rt_SP:
     case AARCH64_OPND_Rs:
     case AARCH64_OPND_Ra:
@@ -5541,10 +5646,26 @@ parse_operands (char *str, const aarch64_opcode *opcode)
        case AARCH64_OPND_Rt2:
        case AARCH64_OPND_Rs:
        case AARCH64_OPND_Ra:
+       case AARCH64_OPND_Rt_LS64:
        case AARCH64_OPND_Rt_SYS:
        case AARCH64_OPND_PAIRREG:
        case AARCH64_OPND_SVE_Rm:
          po_int_reg_or_fail (REG_TYPE_R_Z);
+
+         /* In LS64 load/store instructions Rt register number must be even
+            and <=22.  */
+         if (operands[i] == AARCH64_OPND_Rt_LS64)
+         {
+           /* We've already checked if this is valid register.
+              This will check if register number (Rt) is not undefined for LS64
+              instructions:
+              if Rt<4:3> == '11' || Rt<0> == '1' then UNDEFINED.  */
+           if ((info->reg.regno & 0x18) == 0x18 || (info->reg.regno & 0x01) == 0x01)
+           {
+             set_syntax_error (_("invalid Rt register number in 64-byte load/store"));
+             goto failure;
+           }
+         }
          break;
 
        case AARCH64_OPND_Rd_SP:
@@ -5733,11 +5854,20 @@ parse_operands (char *str, const aarch64_opcode *opcode)
              val = parse_vector_reg_list (&str, reg_type, &vectype);
              if (val == PARSE_FAIL)
                goto failure;
+
              if (! reg_list_valid_p (val, /* accept_alternate */ 0))
                {
                  set_fatal_syntax_error (_("invalid register list"));
                  goto failure;
                }
+
+             if (vectype.width != 0 && *str != ',')
+               {
+                 set_fatal_syntax_error
+                   (_("expected element type rather than vector type"));
+                 goto failure;
+               }
+
              info->reglist.first_regno = (val >> 2) & 0x1f;
              info->reglist.num_regs = (val & 0x3) + 1;
            }
@@ -6071,6 +6201,7 @@ parse_operands (char *str, const aarch64_opcode *opcode)
          break;
 
        case AARCH64_OPND_EXCEPTION:
+       case AARCH64_OPND_UNDEFINED:
          po_misc_or_fail (parse_immediate_expression (&str, &inst.reloc.exp,
                                                       imm_reg_type));
          assign_imm_if_const_or_fixup_later (&inst.reloc, info,
@@ -6081,7 +6212,7 @@ parse_operands (char *str, const aarch64_opcode *opcode)
 
        case AARCH64_OPND_NZCV:
          {
-           const asm_nzcv *nzcv = hash_find_n (aarch64_nzcv_hsh, str, 4);
+           const asm_nzcv *nzcv = str_hash_find_n (aarch64_nzcv_hsh, str, 4);
            if (nzcv != NULL)
              {
                str += 4;
@@ -6100,7 +6231,7 @@ parse_operands (char *str, const aarch64_opcode *opcode)
            do
              str++;
            while (ISALPHA (*str));
-           info->cond = hash_find_n (aarch64_cond_hsh, start, str - start);
+           info->cond = str_hash_find_n (aarch64_cond_hsh, start, str - start);
            if (info->cond == NULL)
              {
                set_syntax_error (_("invalid condition"));
@@ -6356,6 +6487,7 @@ parse_operands (char *str, const aarch64_opcode *opcode)
          break;
 
        case AARCH64_OPND_SVE_ADDR_RI_S4x16:
+       case AARCH64_OPND_SVE_ADDR_RI_S4x32:
        case AARCH64_OPND_SVE_ADDR_RI_S4xVL:
        case AARCH64_OPND_SVE_ADDR_RI_S4x2xVL:
        case AARCH64_OPND_SVE_ADDR_RI_S4x3xVL:
@@ -6573,7 +6705,7 @@ parse_operands (char *str, const aarch64_opcode *opcode)
        case AARCH64_OPND_SYSREG_TLBI:
          inst.base.operands[i].sysins_op =
            parse_sys_ins_reg (&str, aarch64_sys_regs_tlbi_hsh);
-sys_reg_ins:
+       sys_reg_ins:
          if (inst.base.operands[i].sysins_op == NULL)
            {
              set_fatal_syntax_error ( _("unknown or missing operation name"));
@@ -6594,12 +6726,53 @@ sys_reg_ins:
              backtrack_pos = 0;
              goto failure;
            }
+         if (val != PARSE_FAIL
+             && operands[i] == AARCH64_OPND_BARRIER)
+           {
+             /* Regular barriers accept options CRm (C0-C15).
+                DSB nXS barrier variant accepts values > 15.  */
+             if (val < 0 || val > 15)
+             {
+               set_syntax_error (_("the specified option is not accepted in DSB"));
+               goto failure;
+             }
+           }
          /* This is an extension to accept a 0..15 immediate.  */
          if (val == PARSE_FAIL)
            po_imm_or_fail (0, 15);
          info->barrier = aarch64_barrier_options + val;
          break;
 
+       case AARCH64_OPND_BARRIER_DSB_NXS:
+         val = parse_barrier (&str);
+         if (val != PARSE_FAIL)
+           {
+             /* DSB nXS barrier variant accept only <option>nXS qualifiers.  */
+             if (!(val == 16 || val == 20 || val == 24 || val == 28))
+               {
+                 set_syntax_error (_("the specified option is not accepted in DSB"));
+                 /* Turn off backtrack as this optional operand is present.  */
+                 backtrack_pos = 0;
+                 goto failure;
+               }
+           }
+         else
+           {
+             /* DSB nXS barrier variant accept 5-bit unsigned immediate, with
+                possible values 16, 20, 24 or 28 , encoded as val<3:2>.  */
+             if (! parse_constant_immediate (&str, &val, imm_reg_type))
+               goto failure;
+             if (!(val == 16 || val == 20 || val == 24 || val == 28))
+               {
+                 set_syntax_error (_("immediate value must be 16, 20, 24, 28"));
+                 goto failure;
+               }
+           }
+         /* Option index is encoded as 2-bit value in val<3:2>.  */
+         val = (val >> 2) - 4;
+         info->barrier = aarch64_barrier_dsb_nxs_options + val;
+         break;
+
        case AARCH64_OPND_PRFOP:
          val = parse_pldop (&str);
          /* This is an extension to accept a 0..31 immediate.  */
@@ -6620,6 +6793,12 @@ sys_reg_ins:
            goto failure;
          break;
 
+       case AARCH64_OPND_CSRE_CSR:
+         val = parse_csr_operand (&str);
+         if (val == PARSE_FAIL)
+           goto failure;
+         break;
+
        default:
          as_fatal (_("unhandled operand code %d"), operands[i]);
        }
@@ -6628,7 +6807,7 @@ sys_reg_ins:
       inst.base.operands[i].present = 1;
       continue;
 
-failure:
+    failure:
       /* The parse routine should already have set the error, but in case
         not, set a default one here.  */
       if (! error_p ())
@@ -6694,7 +6873,7 @@ failure:
        (_("unexpected characters following instruction"));
     }
 
-parse_operands_return:
+ parse_operands_return:
 
   if (error_p ())
     {
@@ -7353,7 +7532,7 @@ aarch64_init_frag (fragS * fragP, int max_chars)
 
   /* PR 21809: Do not set a mapping state for debug sections
      - it just confuses other tools.  */
-  if (bfd_get_section_flags (NULL, now_seg) & SEC_DEBUGGING)
+  if (bfd_section_flags (now_seg) & SEC_DEBUGGING)
     return;
 
   switch (fragP->fr_type)
@@ -7465,7 +7644,7 @@ md_undefined_symbol (char *name ATTRIBUTE_UNUSED)
            as_bad (_("GOT already in the symbol table"));
 
          GOT_symbol = symbol_new (name, undefined_section,
-                                  (valueT) 0, &zero_address_frag);
+                                  &zero_address_frag, 0);
        }
 
       return GOT_symbol;
@@ -7666,11 +7845,12 @@ fix_insn (fixS *fixP, uint32_t flags, offsetT value)
   switch (opnd)
     {
     case AARCH64_OPND_EXCEPTION:
+    case AARCH64_OPND_UNDEFINED:
       if (unsigned_overflow (value, 16))
        as_bad_where (fixP->fx_file, fixP->fx_line,
                      _("immediate out of range"));
       insn = get_aarch64_insn (buf);
-      insn |= encode_svc_imm (value);
+      insn |= (opnd == AARCH64_OPND_EXCEPTION) ? encode_svc_imm (value) : value;
       put_aarch64_insn (buf, insn);
       break;
 
@@ -8184,12 +8364,11 @@ md_apply_fix (fixS * fixP, valueT * valP, segT seg)
       break;
     }
 
-apply_fix_return:
+ apply_fix_return:
   /* Free the allocated the struct aarch64_inst.
      N.B. currently there are very limited number of fix-up types actually use
      this field, so the impact on the performance should be minimal .  */
-  if (fixP->tc_fix_data.inst != NULL)
-    free (fixP->tc_fix_data.inst);
+  free (fixP->tc_fix_data.inst);
 
   return;
 }
@@ -8523,13 +8702,16 @@ aarch64_adjust_symtab (void)
 }
 
 static void
-checked_hash_insert (struct hash_control *table, const char *key, void *value)
+checked_hash_insert (htab_t table, const char *key, void *value)
 {
-  const char *hash_err;
+  str_hash_insert (table, key, value, 0);
+}
 
-  hash_err = hash_insert (table, key, value);
-  if (hash_err)
-    printf ("Internal Error:  Can't hash %s\n", key);
+static void
+sysreg_hash_insert (htab_t table, const char *key, void *value)
+{
+  gas_assert (strlen (key) < AARCH64_MAX_SYSREG_NAME_LEN);
+  checked_hash_insert (table, key, value);
 }
 
 static void
@@ -8540,7 +8722,7 @@ fill_instruction_hash_table (void)
   while (opcode->name != NULL)
     {
       templates *templ, *new_templ;
-      templ = hash_find (aarch64_ops_hsh, opcode->name);
+      templ = str_hash_find (aarch64_ops_hsh, opcode->name);
 
       new_templ = XNEW (templates);
       new_templ->opcode = opcode;
@@ -8586,56 +8768,55 @@ md_begin (void)
   unsigned mach;
   unsigned int i;
 
-  if ((aarch64_ops_hsh = hash_new ()) == NULL
-      || (aarch64_cond_hsh = hash_new ()) == NULL
-      || (aarch64_shift_hsh = hash_new ()) == NULL
-      || (aarch64_sys_regs_hsh = hash_new ()) == NULL
-      || (aarch64_pstatefield_hsh = hash_new ()) == NULL
-      || (aarch64_sys_regs_ic_hsh = hash_new ()) == NULL
-      || (aarch64_sys_regs_dc_hsh = hash_new ()) == NULL
-      || (aarch64_sys_regs_at_hsh = hash_new ()) == NULL
-      || (aarch64_sys_regs_tlbi_hsh = hash_new ()) == NULL
-      || (aarch64_sys_regs_sr_hsh = hash_new ()) == NULL
-      || (aarch64_reg_hsh = hash_new ()) == NULL
-      || (aarch64_barrier_opt_hsh = hash_new ()) == NULL
-      || (aarch64_nzcv_hsh = hash_new ()) == NULL
-      || (aarch64_pldop_hsh = hash_new ()) == NULL
-      || (aarch64_hint_opt_hsh = hash_new ()) == NULL)
-    as_fatal (_("virtual memory exhausted"));
+  aarch64_ops_hsh = str_htab_create ();
+  aarch64_cond_hsh = str_htab_create ();
+  aarch64_shift_hsh = str_htab_create ();
+  aarch64_sys_regs_hsh = str_htab_create ();
+  aarch64_pstatefield_hsh = str_htab_create ();
+  aarch64_sys_regs_ic_hsh = str_htab_create ();
+  aarch64_sys_regs_dc_hsh = str_htab_create ();
+  aarch64_sys_regs_at_hsh = str_htab_create ();
+  aarch64_sys_regs_tlbi_hsh = str_htab_create ();
+  aarch64_sys_regs_sr_hsh = str_htab_create ();
+  aarch64_reg_hsh = str_htab_create ();
+  aarch64_barrier_opt_hsh = str_htab_create ();
+  aarch64_nzcv_hsh = str_htab_create ();
+  aarch64_pldop_hsh = str_htab_create ();
+  aarch64_hint_opt_hsh = str_htab_create ();
 
   fill_instruction_hash_table ();
 
   for (i = 0; aarch64_sys_regs[i].name != NULL; ++i)
-    checked_hash_insert (aarch64_sys_regs_hsh, aarch64_sys_regs[i].name,
+    sysreg_hash_insert (aarch64_sys_regs_hsh, aarch64_sys_regs[i].name,
                         (void *) (aarch64_sys_regs + i));
 
   for (i = 0; aarch64_pstatefields[i].name != NULL; ++i)
-    checked_hash_insert (aarch64_pstatefield_hsh,
+    sysreg_hash_insert (aarch64_pstatefield_hsh,
                         aarch64_pstatefields[i].name,
                         (void *) (aarch64_pstatefields + i));
 
   for (i = 0; aarch64_sys_regs_ic[i].name != NULL; i++)
-    checked_hash_insert (aarch64_sys_regs_ic_hsh,
+    sysreg_hash_insert (aarch64_sys_regs_ic_hsh,
                         aarch64_sys_regs_ic[i].name,
                         (void *) (aarch64_sys_regs_ic + i));
 
   for (i = 0; aarch64_sys_regs_dc[i].name != NULL; i++)
-    checked_hash_insert (aarch64_sys_regs_dc_hsh,
+    sysreg_hash_insert (aarch64_sys_regs_dc_hsh,
                         aarch64_sys_regs_dc[i].name,
                         (void *) (aarch64_sys_regs_dc + i));
 
   for (i = 0; aarch64_sys_regs_at[i].name != NULL; i++)
-    checked_hash_insert (aarch64_sys_regs_at_hsh,
+    sysreg_hash_insert (aarch64_sys_regs_at_hsh,
                         aarch64_sys_regs_at[i].name,
                         (void *) (aarch64_sys_regs_at + i));
 
   for (i = 0; aarch64_sys_regs_tlbi[i].name != NULL; i++)
-    checked_hash_insert (aarch64_sys_regs_tlbi_hsh,
+    sysreg_hash_insert (aarch64_sys_regs_tlbi_hsh,
                         aarch64_sys_regs_tlbi[i].name,
                         (void *) (aarch64_sys_regs_tlbi + i));
 
   for (i = 0; aarch64_sys_regs_sr[i].name != NULL; i++)
-    checked_hash_insert (aarch64_sys_regs_sr_hsh,
+    sysreg_hash_insert (aarch64_sys_regs_sr_hsh,
                         aarch64_sys_regs_sr[i].name,
                         (void *) (aarch64_sys_regs_sr + i));
 
@@ -8688,6 +8869,16 @@ md_begin (void)
                           (void *) (aarch64_barrier_options + i));
     }
 
+  for (i = 0; i < ARRAY_SIZE (aarch64_barrier_dsb_nxs_options); i++)
+    {
+      const char *name = aarch64_barrier_dsb_nxs_options[i].name;
+      checked_hash_insert (aarch64_barrier_opt_hsh, name,
+                          (void *) (aarch64_barrier_dsb_nxs_options + i));
+      /* Also hash the name in the upper case.  */
+      checked_hash_insert (aarch64_barrier_opt_hsh, get_upper_str (name),
+                          (void *) (aarch64_barrier_dsb_nxs_options + i));
+    }
+
   for (i = 0; i < ARRAY_SIZE (aarch64_prfops); i++)
     {
       const char* name = aarch64_prfops[i].name;
@@ -8704,12 +8895,15 @@ md_begin (void)
   for (i = 0; aarch64_hint_options[i].name != NULL; i++)
     {
       const char* name = aarch64_hint_options[i].name;
+      const char* upper_name = get_upper_str(name);
 
       checked_hash_insert (aarch64_hint_opt_hsh, name,
                           (void *) (aarch64_hint_options + i));
-      /* Also hash the name in the upper case.  */
-      checked_hash_insert (aarch64_pldop_hsh, get_upper_str (name),
-                          (void *) (aarch64_hint_options + i));
+
+      /* Also hash the name in the upper case if not the same.  */
+      if (strcmp (name, upper_name) != 0)
+       checked_hash_insert (aarch64_hint_opt_hsh, upper_name,
+                            (void *) (aarch64_hint_options + i));
     }
 
   /* Set the cpu variant based on the command-line options.  */
@@ -8790,6 +8984,8 @@ struct aarch64_cpu_option_table
    recognized by GCC.  */
 static const struct aarch64_cpu_option_table aarch64_cpus[] = {
   {"all", AARCH64_ANY, NULL},
+  {"cortex-a34", AARCH64_FEATURE (AARCH64_ARCH_V8,
+                                 AARCH64_FEATURE_CRC), "Cortex-A34"},
   {"cortex-a35", AARCH64_FEATURE (AARCH64_ARCH_V8,
                                  AARCH64_FEATURE_CRC), "Cortex-A35"},
   {"cortex-a53", AARCH64_FEATURE (AARCH64_ARCH_V8,
@@ -8809,6 +9005,49 @@ static const struct aarch64_cpu_option_table aarch64_cpus[] = {
   {"cortex-a76", AARCH64_FEATURE (AARCH64_ARCH_V8_2,
                                  AARCH64_FEATURE_RCPC | AARCH64_FEATURE_F16 | AARCH64_FEATURE_DOTPROD),
                                  "Cortex-A76"},
+  {"cortex-a76ae", AARCH64_FEATURE (AARCH64_ARCH_V8_2,
+                                   AARCH64_FEATURE_F16 | AARCH64_FEATURE_RCPC
+                                   | AARCH64_FEATURE_DOTPROD
+                                   | AARCH64_FEATURE_SSBS),
+                                   "Cortex-A76AE"},
+  {"cortex-a77", AARCH64_FEATURE (AARCH64_ARCH_V8_2,
+                                 AARCH64_FEATURE_F16 | AARCH64_FEATURE_RCPC
+                                 | AARCH64_FEATURE_DOTPROD
+                                 | AARCH64_FEATURE_SSBS),
+                                 "Cortex-A77"},
+  {"cortex-a65", AARCH64_FEATURE (AARCH64_ARCH_V8_2,
+                                 AARCH64_FEATURE_F16 | AARCH64_FEATURE_RCPC
+                                 | AARCH64_FEATURE_DOTPROD
+                                 | AARCH64_FEATURE_SSBS),
+                                 "Cortex-A65"},
+  {"cortex-a65ae", AARCH64_FEATURE (AARCH64_ARCH_V8_2,
+                                   AARCH64_FEATURE_F16 | AARCH64_FEATURE_RCPC
+                                   | AARCH64_FEATURE_DOTPROD
+                                   | AARCH64_FEATURE_SSBS),
+                                   "Cortex-A65AE"},
+  {"cortex-a78", AARCH64_FEATURE (AARCH64_ARCH_V8_2,
+                 AARCH64_FEATURE_F16
+                 | AARCH64_FEATURE_RCPC
+                 | AARCH64_FEATURE_DOTPROD
+                 | AARCH64_FEATURE_SSBS
+                 | AARCH64_FEATURE_PROFILE),
+   "Cortex-A78"},
+  {"cortex-a78ae", AARCH64_FEATURE (AARCH64_ARCH_V8_2,
+                   AARCH64_FEATURE_F16
+                   | AARCH64_FEATURE_RCPC
+                   | AARCH64_FEATURE_DOTPROD
+                   | AARCH64_FEATURE_SSBS
+                   | AARCH64_FEATURE_PROFILE),
+   "Cortex-A78AE"},
+  {"cortex-a78c", AARCH64_FEATURE (AARCH64_ARCH_V8_2,
+                   AARCH64_FEATURE_DOTPROD
+                   | AARCH64_FEATURE_F16
+                   | AARCH64_FEATURE_FLAGM
+                   | AARCH64_FEATURE_PAC
+                   | AARCH64_FEATURE_PROFILE
+                   | AARCH64_FEATURE_RCPC
+                   | AARCH64_FEATURE_SSBS),
+   "Cortex-A78C"},
   {"ares", AARCH64_FEATURE (AARCH64_ARCH_V8_2,
                                  AARCH64_FEATURE_RCPC | AARCH64_FEATURE_F16
                                  | AARCH64_FEATURE_DOTPROD
@@ -8831,6 +9070,25 @@ static const struct aarch64_cpu_option_table aarch64_cpus[] = {
                                  | AARCH64_FEATURE_DOTPROD
                                  | AARCH64_FEATURE_PROFILE),
                                  "Neoverse N1"},
+  {"neoverse-n2", AARCH64_FEATURE (AARCH64_ARCH_V8_5,
+                                  AARCH64_FEATURE_BFLOAT16
+                                | AARCH64_FEATURE_I8MM
+                                | AARCH64_FEATURE_F16
+                                | AARCH64_FEATURE_SVE
+                                | AARCH64_FEATURE_SVE2
+                                | AARCH64_FEATURE_SVE2_BITPERM
+                                | AARCH64_FEATURE_MEMTAG
+                                | AARCH64_FEATURE_RNG),
+                                "Neoverse N2"},
+  {"neoverse-v1", AARCH64_FEATURE (AARCH64_ARCH_V8_4,
+                           AARCH64_FEATURE_PROFILE
+                         | AARCH64_FEATURE_CVADP
+                         | AARCH64_FEATURE_SVE
+                         | AARCH64_FEATURE_SSBS
+                         | AARCH64_FEATURE_RNG
+                         | AARCH64_FEATURE_F16
+                         | AARCH64_FEATURE_BFLOAT16
+                         | AARCH64_FEATURE_I8MM), "Neoverse V1"},
   {"qdf24xx", AARCH64_FEATURE (AARCH64_ARCH_V8,
                               AARCH64_FEATURE_CRC | AARCH64_FEATURE_CRYPTO
                               | AARCH64_FEATURE_RDMA),
@@ -8851,6 +9109,14 @@ static const struct aarch64_cpu_option_table aarch64_cpus[] = {
   {"xgene1", AARCH64_ARCH_V8, "APM X-Gene 1"},
   {"xgene2", AARCH64_FEATURE (AARCH64_ARCH_V8,
                              AARCH64_FEATURE_CRC), "APM X-Gene 2"},
+  {"cortex-r82", AARCH64_ARCH_V8_R, "Cortex-R82"},
+  {"cortex-x1", AARCH64_FEATURE (AARCH64_ARCH_V8_2,
+                AARCH64_FEATURE_F16
+                | AARCH64_FEATURE_RCPC
+                | AARCH64_FEATURE_DOTPROD
+                | AARCH64_FEATURE_SSBS
+                | AARCH64_FEATURE_PROFILE),
+                "Cortex-X1"},
   {"generic", AARCH64_ARCH_V8, NULL},
 
   {NULL, AARCH64_ARCH_NONE, NULL}
@@ -8872,6 +9138,9 @@ static const struct aarch64_arch_option_table aarch64_archs[] = {
   {"armv8.3-a", AARCH64_ARCH_V8_3},
   {"armv8.4-a", AARCH64_ARCH_V8_4},
   {"armv8.5-a", AARCH64_ARCH_V8_5},
+  {"armv8.6-a", AARCH64_ARCH_V8_6},
+  {"armv8.7-a", AARCH64_ARCH_V8_7},
+  {"armv8-r",  AARCH64_ARCH_V8_R},
   {NULL, AARCH64_ARCH_NONE}
 };
 
@@ -8886,9 +9155,7 @@ struct aarch64_option_cpu_value_table
 static const struct aarch64_option_cpu_value_table aarch64_features[] = {
   {"crc",              AARCH64_FEATURE (AARCH64_FEATURE_CRC, 0),
                        AARCH64_ARCH_NONE},
-  {"crypto",           AARCH64_FEATURE (AARCH64_FEATURE_CRYPTO
-                                        | AARCH64_FEATURE_AES
-                                        | AARCH64_FEATURE_SHA2, 0),
+  {"crypto",           AARCH64_FEATURE (AARCH64_FEATURE_CRYPTO, 0),
                        AARCH64_FEATURE (AARCH64_FEATURE_SIMD, 0)},
   {"fp",               AARCH64_FEATURE (AARCH64_FEATURE_FP, 0),
                        AARCH64_ARCH_NONE},
@@ -8934,9 +9201,8 @@ static const struct aarch64_option_cpu_value_table aarch64_features[] = {
                        AARCH64_ARCH_NONE},
   {"sm4",              AARCH64_FEATURE (AARCH64_FEATURE_SM4, 0),
                        AARCH64_ARCH_NONE},
-  {"sha3",             AARCH64_FEATURE (AARCH64_FEATURE_SHA2
-                                        | AARCH64_FEATURE_SHA3, 0),
-                       AARCH64_ARCH_NONE},
+  {"sha3",             AARCH64_FEATURE (AARCH64_FEATURE_SHA3, 0),
+                       AARCH64_FEATURE (AARCH64_FEATURE_SHA2, 0)},
   {"rng",              AARCH64_FEATURE (AARCH64_FEATURE_RNG, 0),
                        AARCH64_ARCH_NONE},
   {"ssbs",             AARCH64_FEATURE (AARCH64_FEATURE_SSBS, 0),
@@ -8954,8 +9220,24 @@ static const struct aarch64_option_cpu_value_table aarch64_features[] = {
   {"sve2-sha3",                AARCH64_FEATURE (AARCH64_FEATURE_SVE2_SHA3, 0),
                        AARCH64_FEATURE (AARCH64_FEATURE_SVE2
                                         | AARCH64_FEATURE_SHA3, 0)},
-  {"bitperm",          AARCH64_FEATURE (AARCH64_FEATURE_SVE2_BITPERM, 0),
+  {"sve2-bitperm",     AARCH64_FEATURE (AARCH64_FEATURE_SVE2_BITPERM, 0),
                        AARCH64_FEATURE (AARCH64_FEATURE_SVE2, 0)},
+  {"bf16",             AARCH64_FEATURE (AARCH64_FEATURE_BFLOAT16, 0),
+                       AARCH64_ARCH_NONE},
+  {"i8mm",             AARCH64_FEATURE (AARCH64_FEATURE_I8MM, 0),
+                       AARCH64_ARCH_NONE},
+  {"f32mm",            AARCH64_FEATURE (AARCH64_FEATURE_F32MM, 0),
+                       AARCH64_FEATURE (AARCH64_FEATURE_SVE, 0)},
+  {"f64mm",            AARCH64_FEATURE (AARCH64_FEATURE_F64MM, 0),
+                       AARCH64_FEATURE (AARCH64_FEATURE_SVE, 0)},
+  {"csre",             AARCH64_FEATURE (AARCH64_FEATURE_CSRE, 0),
+                       AARCH64_ARCH_NONE},
+  {"ls64",             AARCH64_FEATURE (AARCH64_FEATURE_LS64, 0),
+                       AARCH64_ARCH_NONE},
+  {"flagm",            AARCH64_FEATURE (AARCH64_FEATURE_FLAGM, 0),
+                       AARCH64_ARCH_NONE},
+  {"pauth",            AARCH64_FEATURE (AARCH64_FEATURE_PAC, 0),
+                       AARCH64_ARCH_NONE},
   {NULL,               AARCH64_ARCH_NONE, AARCH64_ARCH_NONE},
 };
 
@@ -9420,3 +9702,34 @@ aarch64_copy_symbol_attributes (symbolS * dest, symbolS * src)
 {
   AARCH64_GET_FLAG (dest) = AARCH64_GET_FLAG (src);
 }
+
+#ifdef OBJ_ELF
+/* Same as elf_copy_symbol_attributes, but without copying st_other.
+   This is needed so AArch64 specific st_other values can be independently
+   specified for an IFUNC resolver (that is called by the dynamic linker)
+   and the symbol it resolves (aliased to the resolver).  In particular,
+   if a function symbol has special st_other value set via directives,
+   then attaching an IFUNC resolver to that symbol should not override
+   the st_other setting.  Requiring the directive on the IFUNC resolver
+   symbol would be unexpected and problematic in C code, where the two
+   symbols appear as two independent function declarations.  */
+
+void
+aarch64_elf_copy_symbol_attributes (symbolS *dest, symbolS *src)
+{
+  struct elf_obj_sy *srcelf = symbol_get_obj (src);
+  struct elf_obj_sy *destelf = symbol_get_obj (dest);
+  if (srcelf->size)
+    {
+      if (destelf->size == NULL)
+       destelf->size = XNEW (expressionS);
+      *destelf->size = *srcelf->size;
+    }
+  else
+    {
+      free (destelf->size);
+      destelf->size = NULL;
+    }
+  S_SET_SIZE (dest, S_GET_SIZE (src));
+}
+#endif