Add ADR :tlsgd: directive and TLSGD_ADR_PREL21 support.
[deliverable/binutils-gdb.git] / gas / config / tc-aarch64.c
index 7483f30fc1a68d7cc717e65c55c9f3a9017a8c93..bcd6f4fb270ade088e308ff71f379982d476dc4f 100644 (file)
@@ -1,6 +1,6 @@
 /* tc-aarch64.c -- Assemble for the AArch64 ISA
 
-   Copyright (C) 2009-2014 Free Software Foundation, Inc.
+   Copyright (C) 2009-2015 Free Software Foundation, Inc.
    Contributed by ARM Ltd.
 
    This file is part of GAS.
@@ -55,9 +55,6 @@ static const aarch64_feature_set *march_cpu_opt = NULL;
 /* Constants for known architecture features.  */
 static const aarch64_feature_set cpu_default = CPU_DEFAULT;
 
-static const aarch64_feature_set aarch64_arch_any = AARCH64_ANY;
-static const aarch64_feature_set aarch64_arch_none = AARCH64_ARCH_NONE;
-
 #ifdef OBJ_ELF
 /* Pre-defined "_GLOBAL_OFFSET_TABLE_" */
 static symbolS *GOT_symbol;
@@ -1917,6 +1914,7 @@ s_tlsdesccall (int ignored ATTRIBUTE_UNUSED)
 
 static void s_aarch64_arch (int);
 static void s_aarch64_cpu (int);
+static void s_aarch64_arch_extension (int);
 
 /* This table describes all the machine specific pseudo-ops the assembler
    has to support.  The fields are:
@@ -1934,6 +1932,7 @@ const pseudo_typeS md_pseudo_table[] = {
   {"pool", s_ltorg, 0},
   {"cpu", s_aarch64_cpu, 0},
   {"arch", s_aarch64_arch, 0},
+  {"arch_extension", s_aarch64_arch_extension, 0},
   {"inst", s_aarch64_inst, 0},
 #ifdef OBJ_ELF
   {"tlsdesccall", s_tlsdesccall, 0},
@@ -2311,221 +2310,283 @@ struct reloc_table_entry
 {
   const char *name;
   int pc_rel;
+  bfd_reloc_code_real_type adr_type;
   bfd_reloc_code_real_type adrp_type;
   bfd_reloc_code_real_type movw_type;
   bfd_reloc_code_real_type add_type;
   bfd_reloc_code_real_type ldst_type;
+  bfd_reloc_code_real_type ld_literal_type;
 };
 
 static struct reloc_table_entry reloc_table[] = {
   /* Low 12 bits of absolute address: ADD/i and LDR/STR */
   {"lo12", 0,
+   0,                          /* adr_type */
    0,
    0,
    BFD_RELOC_AARCH64_ADD_LO12,
-   BFD_RELOC_AARCH64_LDST_LO12},
+   BFD_RELOC_AARCH64_LDST_LO12,
+   0},
 
   /* Higher 21 bits of pc-relative page offset: ADRP */
   {"pg_hi21", 1,
+   0,                          /* adr_type */
    BFD_RELOC_AARCH64_ADR_HI21_PCREL,
    0,
    0,
+   0,
    0},
 
   /* Higher 21 bits of pc-relative page offset: ADRP, no check */
   {"pg_hi21_nc", 1,
+   0,                          /* adr_type */
    BFD_RELOC_AARCH64_ADR_HI21_NC_PCREL,
    0,
    0,
+   0,
    0},
 
   /* Most significant bits 0-15 of unsigned address/value: MOVZ */
   {"abs_g0", 0,
+   0,                          /* adr_type */
    0,
    BFD_RELOC_AARCH64_MOVW_G0,
    0,
+   0,
    0},
 
   /* Most significant bits 0-15 of signed address/value: MOVN/Z */
   {"abs_g0_s", 0,
+   0,                          /* adr_type */
    0,
    BFD_RELOC_AARCH64_MOVW_G0_S,
    0,
+   0,
    0},
 
   /* Less significant bits 0-15 of address/value: MOVK, no check */
   {"abs_g0_nc", 0,
+   0,                          /* adr_type */
    0,
    BFD_RELOC_AARCH64_MOVW_G0_NC,
    0,
+   0,
    0},
 
   /* Most significant bits 16-31 of unsigned address/value: MOVZ */
   {"abs_g1", 0,
+   0,                          /* adr_type */
    0,
    BFD_RELOC_AARCH64_MOVW_G1,
    0,
+   0,
    0},
 
   /* Most significant bits 16-31 of signed address/value: MOVN/Z */
   {"abs_g1_s", 0,
+   0,                          /* adr_type */
    0,
    BFD_RELOC_AARCH64_MOVW_G1_S,
    0,
+   0,
    0},
 
   /* Less significant bits 16-31 of address/value: MOVK, no check */
   {"abs_g1_nc", 0,
+   0,                          /* adr_type */
    0,
    BFD_RELOC_AARCH64_MOVW_G1_NC,
    0,
+   0,
    0},
 
   /* Most significant bits 32-47 of unsigned address/value: MOVZ */
   {"abs_g2", 0,
+   0,                          /* adr_type */
    0,
    BFD_RELOC_AARCH64_MOVW_G2,
    0,
+   0,
    0},
 
   /* Most significant bits 32-47 of signed address/value: MOVN/Z */
   {"abs_g2_s", 0,
+   0,                          /* adr_type */
    0,
    BFD_RELOC_AARCH64_MOVW_G2_S,
    0,
+   0,
    0},
 
   /* Less significant bits 32-47 of address/value: MOVK, no check */
   {"abs_g2_nc", 0,
+   0,                          /* adr_type */
    0,
    BFD_RELOC_AARCH64_MOVW_G2_NC,
    0,
+   0,
    0},
 
   /* Most significant bits 48-63 of signed/unsigned address/value: MOVZ */
   {"abs_g3", 0,
+   0,                          /* adr_type */
    0,
    BFD_RELOC_AARCH64_MOVW_G3,
    0,
+   0,
    0},
 
   /* Get to the page containing GOT entry for a symbol.  */
   {"got", 1,
+   0,                          /* adr_type */
    BFD_RELOC_AARCH64_ADR_GOT_PAGE,
    0,
    0,
+   0,
    BFD_RELOC_AARCH64_GOT_LD_PREL19},
 
   /* 12 bit offset into the page containing GOT entry for that symbol.  */
   {"got_lo12", 0,
+   0,                          /* adr_type */
    0,
    0,
    0,
-   BFD_RELOC_AARCH64_LD_GOT_LO12_NC},
+   BFD_RELOC_AARCH64_LD_GOT_LO12_NC,
+   0},
 
   /* Get to the page containing GOT TLS entry for a symbol */
   {"tlsgd", 0,
+   BFD_RELOC_AARCH64_TLSGD_ADR_PREL21, /* adr_type */
    BFD_RELOC_AARCH64_TLSGD_ADR_PAGE21,
    0,
    0,
+   0,
    0},
 
   /* 12 bit offset into the page containing GOT TLS entry for a symbol */
   {"tlsgd_lo12", 0,
+   0,                          /* adr_type */
    0,
    0,
    BFD_RELOC_AARCH64_TLSGD_ADD_LO12_NC,
+   0,
    0},
 
   /* Get to the page containing GOT TLS entry for a symbol */
   {"tlsdesc", 0,
+   0,                          /* adr_type */
    BFD_RELOC_AARCH64_TLSDESC_ADR_PAGE21,
    0,
    0,
+   0,
    0},
 
   /* 12 bit offset into the page containing GOT TLS entry for a symbol */
   {"tlsdesc_lo12", 0,
+   0,                          /* adr_type */
    0,
    0,
    BFD_RELOC_AARCH64_TLSDESC_ADD_LO12_NC,
-   BFD_RELOC_AARCH64_TLSDESC_LD_LO12_NC},
+   BFD_RELOC_AARCH64_TLSDESC_LD_LO12_NC,
+   0},
 
   /* Get to the page containing GOT TLS entry for a symbol */
   {"gottprel", 0,
+   0,                          /* adr_type */
    BFD_RELOC_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21,
    0,
    0,
-   0},
+   0,
+   BFD_RELOC_AARCH64_TLSIE_LD_GOTTPREL_PREL19},
 
   /* 12 bit offset into the page containing GOT TLS entry for a symbol */
   {"gottprel_lo12", 0,
+   0,                          /* adr_type */
    0,
    0,
    0,
-   BFD_RELOC_AARCH64_TLSIE_LD_GOTTPREL_LO12_NC},
+   BFD_RELOC_AARCH64_TLSIE_LD_GOTTPREL_LO12_NC,
+   0},
 
   /* Get tp offset for a symbol.  */
   {"tprel", 0,
+   0,                          /* adr_type */
    0,
    0,
    BFD_RELOC_AARCH64_TLSLE_ADD_TPREL_LO12,
+   0,
    0},
 
   /* Get tp offset for a symbol.  */
   {"tprel_lo12", 0,
+   0,                          /* adr_type */
    0,
    0,
    BFD_RELOC_AARCH64_TLSLE_ADD_TPREL_LO12,
+   0,
    0},
 
   /* Get tp offset for a symbol.  */
   {"tprel_hi12", 0,
+   0,                          /* adr_type */
    0,
    0,
    BFD_RELOC_AARCH64_TLSLE_ADD_TPREL_HI12,
+   0,
    0},
 
   /* Get tp offset for a symbol.  */
   {"tprel_lo12_nc", 0,
+   0,                          /* adr_type */
    0,
    0,
    BFD_RELOC_AARCH64_TLSLE_ADD_TPREL_LO12_NC,
+   0,
    0},
 
   /* Most significant bits 32-47 of address/value: MOVZ.  */
   {"tprel_g2", 0,
+   0,                          /* adr_type */
    0,
    BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G2,
    0,
+   0,
    0},
 
   /* Most significant bits 16-31 of address/value: MOVZ.  */
   {"tprel_g1", 0,
+   0,                          /* adr_type */
    0,
    BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G1,
    0,
+   0,
    0},
 
   /* Most significant bits 16-31 of address/value: MOVZ, no check.  */
   {"tprel_g1_nc", 0,
+   0,                          /* adr_type */
    0,
    BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G1_NC,
    0,
+   0,
    0},
 
   /* Most significant bits 0-15 of address/value: MOVZ.  */
   {"tprel_g0", 0,
+   0,                          /* adr_type */
    0,
    BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G0,
    0,
+   0,
    0},
 
   /* Most significant bits 0-15 of address/value: MOVZ, no check.  */
   {"tprel_g0_nc", 0,
+   0,                          /* adr_type */
    0,
    BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G0_NC,
    0,
+   0,
    0},
 };
 
@@ -2932,6 +2993,7 @@ parse_address_main (char **str, aarch64_opnd_info *operand, int reloc,
       skip_past_char (&p, '#');
       if (reloc && skip_past_char (&p, ':'))
        {
+         bfd_reloc_code_real_type ty;
          struct reloc_table_entry *entry;
 
          /* Try to parse a relocation modifier.  Anything else is
@@ -2943,7 +3005,19 @@ parse_address_main (char **str, aarch64_opnd_info *operand, int reloc,
              return FALSE;
            }
 
-         if (entry->ldst_type == 0)
+         switch (operand->type)
+           {
+           case AARCH64_OPND_ADDR_PCREL21:
+             /* adr */
+             ty = entry->adr_type;
+             break;
+
+           default:
+             ty = entry->ld_literal_type;
+             break;
+           }
+
+         if (ty == 0)
            {
              set_syntax_error
                (_("this relocation modifier is not allowed on this "
@@ -2959,8 +3033,8 @@ parse_address_main (char **str, aarch64_opnd_info *operand, int reloc,
            }
 
          /* #:<reloc_op>:<expr>  */
-         /* Record the load/store relocation type.  */
-         inst.reloc.type = entry->ldst_type;
+         /* Record the relocation type.  */
+         inst.reloc.type = ty;
          inst.reloc.pc_rel = entry->pc_rel;
        }
       else
@@ -5488,6 +5562,47 @@ programmer_friendly_fixup (aarch64_instruction *instr)
   return TRUE;
 }
 
+/* Check for loads and stores that will cause unpredictable behavior.  */
+
+static void
+warn_unpredictable_ldst (aarch64_instruction *instr, char *str)
+{
+  aarch64_inst *base = &instr->base;
+  const aarch64_opcode *opcode = base->opcode;
+  const aarch64_opnd_info *opnds = base->operands;
+  switch (opcode->iclass)
+    {
+    case ldst_pos:
+    case ldst_imm9:
+    case ldst_unscaled:
+    case ldst_unpriv:
+      /* Loading/storing the base register is unpredictable if writeback.  */
+      if ((aarch64_get_operand_class (opnds[0].type)
+          == AARCH64_OPND_CLASS_INT_REG)
+         && opnds[0].reg.regno == opnds[1].addr.base_regno
+         && opnds[1].addr.writeback)
+       as_warn (_("unpredictable transfer with writeback -- `%s'"), str);
+      break;
+    case ldstpair_off:
+    case ldstnapair_offs:
+    case ldstpair_indexed:
+      /* Loading/storing the base register is unpredictable if writeback.  */
+      if ((aarch64_get_operand_class (opnds[0].type)
+          == AARCH64_OPND_CLASS_INT_REG)
+         && (opnds[0].reg.regno == opnds[2].addr.base_regno
+           || opnds[1].reg.regno == opnds[2].addr.base_regno)
+         && opnds[2].addr.writeback)
+           as_warn (_("unpredictable transfer with writeback -- `%s'"), str);
+      /* Load operations must load different registers.  */
+      if ((opcode->opcode & (1 << 22))
+         && opnds[0].reg.regno == opnds[1].reg.regno)
+           as_warn (_("unpredictable load of register pair -- `%s'"), str);
+      break;
+    default:
+      break;
+    }
+}
+
 /* A wrapper function to interface with libopcodes on encoding and
    record the error message if there is any.
 
@@ -5620,6 +5735,8 @@ md_assemble (char *str)
              return;
            }
 
+         warn_unpredictable_ldst (&inst, str);
+
          if (inst.reloc.type == BFD_RELOC_UNUSED
              || !inst.reloc.need_libopcodes_p)
            output_inst (NULL);
@@ -5825,7 +5942,24 @@ md_section_align (segT segment ATTRIBUTE_UNUSED, valueT size)
 }
 
 /* This is called from HANDLE_ALIGN in write.c.         Fill in the contents
-   of an rs_align_code fragment.  */
+   of an rs_align_code fragment.
+
+   Here we fill the frag with the appropriate info for padding the
+   output stream.  The resulting frag will consist of a fixed (fr_fix)
+   and of a repeating (fr_var) part.
+
+   The fixed content is always emitted before the repeating content and
+   these two parts are used as follows in constructing the output:
+   - the fixed part will be used to align to a valid instruction word
+     boundary, in case that we start at a misaligned address; as no
+     executable instruction can live at the misaligned location, we
+     simply fill with zeros;
+   - the variable part will be used to cover the remaining padding and
+     we fill using the AArch64 NOP instruction.
+
+   Note that the size of a RS_ALIGN_CODE fragment is always 7 to provide
+   enough storage space for up to 3 bytes for padding the back to a valid
+   instruction alignment and exactly 4 bytes to store the NOP pattern.  */
 
 void
 aarch64_handle_align (fragS * fragP)
@@ -5836,69 +5970,33 @@ aarch64_handle_align (fragS * fragP)
 
   int bytes, fix, noop_size;
   char *p;
-  const char *noop;
 
   if (fragP->fr_type != rs_align_code)
     return;
 
   bytes = fragP->fr_next->fr_address - fragP->fr_address - fragP->fr_fix;
   p = fragP->fr_literal + fragP->fr_fix;
-  fix = 0;
-
-  if (bytes > MAX_MEM_FOR_RS_ALIGN_CODE)
-    bytes &= MAX_MEM_FOR_RS_ALIGN_CODE;
 
 #ifdef OBJ_ELF
   gas_assert (fragP->tc_frag_data.recorded);
 #endif
 
-  noop = aarch64_noop;
   noop_size = sizeof (aarch64_noop);
-  fragP->fr_var = noop_size;
 
-  if (bytes & (noop_size - 1))
+  fix = bytes & (noop_size - 1);
+  if (fix)
     {
-      fix = bytes & (noop_size - 1);
 #ifdef OBJ_ELF
       insert_data_mapping_symbol (MAP_INSN, fragP->fr_fix, fragP, fix);
 #endif
       memset (p, 0, fix);
       p += fix;
-      bytes -= fix;
-    }
-
-  while (bytes >= noop_size)
-    {
-      memcpy (p, noop, noop_size);
-      p += noop_size;
-      bytes -= noop_size;
-      fix += noop_size;
+      fragP->fr_fix += fix;
     }
 
-  fragP->fr_fix += fix;
-}
-
-/* Called from md_do_align.  Used to create an alignment
-   frag in a code section.  */
-
-void
-aarch64_frag_align_code (int n, int max)
-{
-  char *p;
-
-  /* We assume that there will never be a requirement
-     to support alignments greater than x bytes.  */
-  if (max > MAX_MEM_FOR_RS_ALIGN_CODE)
-    as_fatal (_
-             ("alignments greater than %d bytes not supported in .text sections"),
-             MAX_MEM_FOR_RS_ALIGN_CODE + 1);
-
-  p = frag_var (rs_align_code,
-               MAX_MEM_FOR_RS_ALIGN_CODE,
-               1,
-               (relax_substateT) max,
-               (symbolS *) NULL, (offsetT) n, (char *) NULL);
-  *p = 0;
+  if (noop_size)
+    memcpy (p, aarch64_noop, noop_size);
+  fragP->fr_var = noop_size;
 }
 
 /* Perform target specific initialisation of a frag.
@@ -6614,9 +6712,11 @@ md_apply_fix (fixS * fixP, valueT * valP, segT seg)
     case BFD_RELOC_AARCH64_TLSDESC_LD64_LO12_NC:
     case BFD_RELOC_AARCH64_TLSGD_ADD_LO12_NC:
     case BFD_RELOC_AARCH64_TLSGD_ADR_PAGE21:
+    case BFD_RELOC_AARCH64_TLSGD_ADR_PREL21:
     case BFD_RELOC_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21:
     case BFD_RELOC_AARCH64_TLSIE_LD32_GOTTPREL_LO12_NC:
     case BFD_RELOC_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC:
+    case BFD_RELOC_AARCH64_TLSIE_LD_GOTTPREL_PREL19:
     case BFD_RELOC_AARCH64_TLSLE_ADD_TPREL_HI12:
     case BFD_RELOC_AARCH64_TLSLE_ADD_TPREL_LO12:
     case BFD_RELOC_AARCH64_TLSLE_ADD_TPREL_LO12_NC:
@@ -6813,9 +6913,11 @@ aarch64_force_relocation (struct fix *fixp)
     case BFD_RELOC_AARCH64_TLSDESC_LD64_LO12_NC:
     case BFD_RELOC_AARCH64_TLSGD_ADD_LO12_NC:
     case BFD_RELOC_AARCH64_TLSGD_ADR_PAGE21:
+    case BFD_RELOC_AARCH64_TLSGD_ADR_PREL21:
     case BFD_RELOC_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21:
     case BFD_RELOC_AARCH64_TLSIE_LD32_GOTTPREL_LO12_NC:
     case BFD_RELOC_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC:
+    case BFD_RELOC_AARCH64_TLSIE_LD_GOTTPREL_PREL19:
     case BFD_RELOC_AARCH64_TLSLE_ADD_TPREL_HI12:
     case BFD_RELOC_AARCH64_TLSLE_ADD_TPREL_LO12:
     case BFD_RELOC_AARCH64_TLSLE_ADD_TPREL_LO12_NC:
@@ -7201,17 +7303,22 @@ struct aarch64_cpu_option_table
    recognized by GCC.  */
 static const struct aarch64_cpu_option_table aarch64_cpus[] = {
   {"all", AARCH64_ANY, NULL},
-  {"cortex-a53",       AARCH64_ARCH_V8, "Cortex-A53"},
-  {"cortex-a57",       AARCH64_ARCH_V8, "Cortex-A57"},
-  {"thunderx",         AARCH64_ARCH_V8, "Cavium ThunderX"},
+  {"cortex-a53", AARCH64_FEATURE (AARCH64_ARCH_V8,
+                                 AARCH64_FEATURE_CRC), "Cortex-A53"},
+  {"cortex-a57", AARCH64_FEATURE (AARCH64_ARCH_V8,
+                                 AARCH64_FEATURE_CRC), "Cortex-A57"},
+  {"cortex-a72", AARCH64_FEATURE (AARCH64_ARCH_V8,
+                                 AARCH64_FEATURE_CRC), "Cortex-A72"},
+  {"thunderx", AARCH64_ARCH_V8, "Cavium ThunderX"},
+  /* The 'xgene-1' name is an older name for 'xgene1', which was used
+     in earlier releases and is superseded by 'xgene1' in all
+     tools.  */
   {"xgene-1", AARCH64_ARCH_V8, "APM X-Gene 1"},
+  {"xgene1", AARCH64_ARCH_V8, "APM X-Gene 1"},
+  {"xgene2", AARCH64_FEATURE (AARCH64_ARCH_V8,
+                             AARCH64_FEATURE_CRC), "APM X-Gene 2"},
   {"generic", AARCH64_ARCH_V8, NULL},
 
-  /* These two are example CPUs supported in GCC, once we have real
-     CPUs they will be removed.  */
-  {"example-1",        AARCH64_ARCH_V8, NULL},
-  {"example-2",        AARCH64_ARCH_V8, NULL},
-
   {NULL, AARCH64_ARCH_NONE, NULL}
 };
 
@@ -7254,7 +7361,8 @@ struct aarch64_long_option_table
 };
 
 static int
-aarch64_parse_features (char *str, const aarch64_feature_set **opt_p)
+aarch64_parse_features (char *str, const aarch64_feature_set **opt_p,
+                       bfd_boolean ext_only)
 {
   /* We insist on extensions being added before being removed.  We achieve
      this by using the ADDING_VALUE variable to indicate whether we are
@@ -7270,17 +7378,19 @@ aarch64_parse_features (char *str, const aarch64_feature_set **opt_p)
   while (str != NULL && *str != 0)
     {
       const struct aarch64_option_cpu_value_table *opt;
-      char *ext;
+      char *ext = NULL;
       int optlen;
 
-      if (*str != '+')
+      if (!ext_only)
        {
-         as_bad (_("invalid architectural extension"));
-         return 0;
-       }
+         if (*str != '+')
+           {
+             as_bad (_("invalid architectural extension"));
+             return 0;
+           }
 
-      str++;
-      ext = strchr (str, '+');
+         ext = strchr (++str, '+');
+       }
 
       if (ext != NULL)
        optlen = ext - str;
@@ -7360,7 +7470,7 @@ aarch64_parse_cpu (char *str)
       {
        mcpu_cpu_opt = &opt->value;
        if (ext != NULL)
-         return aarch64_parse_features (ext, &mcpu_cpu_opt);
+         return aarch64_parse_features (ext, &mcpu_cpu_opt, FALSE);
 
        return 1;
       }
@@ -7392,7 +7502,7 @@ aarch64_parse_arch (char *str)
       {
        march_cpu_opt = &opt->value;
        if (ext != NULL)
-         return aarch64_parse_features (ext, &march_cpu_opt);
+         return aarch64_parse_features (ext, &march_cpu_opt, FALSE);
 
        return 1;
       }
@@ -7575,7 +7685,7 @@ s_aarch64_cpu (int ignored ATTRIBUTE_UNUSED)
       {
        mcpu_cpu_opt = &opt->value;
        if (ext != NULL)
-         if (!aarch64_parse_features (ext, &mcpu_cpu_opt))
+         if (!aarch64_parse_features (ext, &mcpu_cpu_opt, FALSE))
            return;
 
        cpu_variant = *mcpu_cpu_opt;
@@ -7621,7 +7731,7 @@ s_aarch64_arch (int ignored ATTRIBUTE_UNUSED)
       {
        mcpu_cpu_opt = &opt->value;
        if (ext != NULL)
-         if (!aarch64_parse_features (ext, &mcpu_cpu_opt))
+         if (!aarch64_parse_features (ext, &mcpu_cpu_opt, FALSE))
            return;
 
        cpu_variant = *mcpu_cpu_opt;
@@ -7636,6 +7746,28 @@ s_aarch64_arch (int ignored ATTRIBUTE_UNUSED)
   ignore_rest_of_line ();
 }
 
+/* Parse a .arch_extension directive.  */
+
+static void
+s_aarch64_arch_extension (int ignored ATTRIBUTE_UNUSED)
+{
+  char saved_char;
+  char *ext = input_line_pointer;;
+
+  while (*input_line_pointer && !ISSPACE (*input_line_pointer))
+    input_line_pointer++;
+  saved_char = *input_line_pointer;
+  *input_line_pointer = 0;
+
+  if (!aarch64_parse_features (ext, &mcpu_cpu_opt, TRUE))
+    return;
+
+  cpu_variant = *mcpu_cpu_opt;
+
+  *input_line_pointer = saved_char;
+  demand_empty_rest_of_line ();
+}
+
 /* Copy symbol information.  */
 
 void
This page took 0.031804 seconds and 4 git commands to generate.