+2017-06-21  Thomas Preud'homme  <thomas.preudhomme@arm.com>
+
+       * config/tc-arm.c (dyn_mcpu_ext_opt): New static variable.
+       (dyn_march_ext_opt): Likewise.
+       (md_begin): Copy extension feature bits alongside architecture ones.
+       Merge extensions feature bits in selected_cpu and cpu_variant if there
+       is some.
+       (arm_parse_extension): Pass architecture and extension feature bits in
+       separate parameters, with architecture bits being read only.  Update
+       **opt_p directly rather than *ext_set and initialize it if needed.
+       (arm_parse_cpu): Stop merging architecture and extension feature bits
+       and instead use mcpu_cpu_opt and dyn_mcpu_ext_opt to memorize them
+       respectively.  Adapt to change in parameters of arm_parse_extension.
+       (arm_parse_arch): Adapt to change in parameters of arm_parse_extension.
+       (aeabi_set_attribute_string): Make function static.
+       (arm_md_post_relax): New function.
+       (s_arm_cpu): Stop merging architecture and extension feature bits and
+       instead use mcpu_cpu_opt and dyn_mcpu_ext_opt to memorize them
+       respectively.  Merge extension feature bits in cpu_variant
+       if there is any.
+       (s_arm_arch): Reset extension feature bit.  Set selected_cpu from
+       *mcpu_cpu_opt and cpu_variant from selected_cpu and *mfpu_opt for
+       consistency with s_arm_cpu.
+       (s_arm_arch_extension): Update *dyn_mcpu_ext_opt rather than
+       selected_cpu, allocating it before hand if needed.  Set selected_cpu
+       from it and then cpu_variant.
+       (s_arm_fpu): Merge *mcpu_ext_opt feature bits if any in cpu_variant.
+       * config/tc-arm.h (md_post_relax_hook): Set to arm_md_post_relax.
+       (aeabi_set_public_attributes): Delete external declaration.
+       (arm_md_post_relax): Declare externally.
+
 2017-06-21  Thomas Preud'homme  <thomas.preudhomme@arm.com>
 
        * config/tc-arm.c (struct arm_cpu_option_table): New ext field.
 
 static const arm_feature_set *legacy_fpu = NULL;
 
 static const arm_feature_set *mcpu_cpu_opt = NULL;
+static arm_feature_set *dyn_mcpu_ext_opt = NULL;
 static const arm_feature_set *mcpu_fpu_opt = NULL;
 static const arm_feature_set *march_cpu_opt = NULL;
+static arm_feature_set *dyn_march_ext_opt = NULL;
 static const arm_feature_set *march_fpu_opt = NULL;
 static const arm_feature_set *mfpu_opt = NULL;
 static const arm_feature_set *object_arch = NULL;
       mcpu_cpu_opt = legacy_cpu;
     }
   else if (!mcpu_cpu_opt)
-    mcpu_cpu_opt = march_cpu_opt;
+    {
+      mcpu_cpu_opt = march_cpu_opt;
+      dyn_mcpu_ext_opt = dyn_march_ext_opt;
+      /* Avoid double free in arm_md_end.  */
+      dyn_march_ext_opt = NULL;
+    }
 
   if (legacy_fpu)
     {
       mcpu_cpu_opt = &cpu_default;
       selected_cpu = cpu_default;
     }
+  else if (dyn_mcpu_ext_opt)
+    ARM_MERGE_FEATURE_SETS (selected_cpu, *mcpu_cpu_opt, *dyn_mcpu_ext_opt);
   else
     selected_cpu = *mcpu_cpu_opt;
 #else
-  if (mcpu_cpu_opt)
+  if (mcpu_cpu_opt && dyn_mcpu_ext_opt)
+    ARM_MERGE_FEATURE_SETS (selected_cpu, *mcpu_cpu_opt, *dyn_mcpu_ext_opt);
+  else if (mcpu_cpu_opt)
     selected_cpu = *mcpu_cpu_opt;
   else
     mcpu_cpu_opt = &arm_arch_any;
 #endif
 
   ARM_MERGE_FEATURE_SETS (cpu_variant, *mcpu_cpu_opt, *mfpu_opt);
+  if (dyn_mcpu_ext_opt)
+    ARM_MERGE_FEATURE_SETS (cpu_variant, cpu_variant, *dyn_mcpu_ext_opt);
 
   autoselect_thumb_from_cpu_variant ();
 
 };
 
 static bfd_boolean
-arm_parse_extension (const char *str, const arm_feature_set **opt_p)
+arm_parse_extension (const char *str, const arm_feature_set *opt_set,
+                    arm_feature_set **ext_set_p)
 {
-  arm_feature_set *ext_set = XNEW (arm_feature_set);
-
   /* We insist on extensions being specified in alphabetical order, and with
      extensions being added before being removed.  We achieve this by having
      the global ARM_EXTENSIONS table in alphabetical order, and using the
   const arm_feature_set arm_any = ARM_ANY;
   int adding_value = -1;
 
-  /* Copy the feature set, so that we can modify it.  */
-  *ext_set = **opt_p;
-  *opt_p = ext_set;
+  if (!*ext_set_p)
+    {
+      *ext_set_p = XNEW (arm_feature_set);
+      **ext_set_p = arm_arch_none;
+    }
 
   while (str != NULL && *str != 0)
     {
                /* Empty entry.  */
                if (ARM_FEATURE_EQUAL (opt->allowed_archs[i], arm_any))
                  continue;
-               if (ARM_FSET_CPU_SUBSET (opt->allowed_archs[i], *ext_set))
+               if (ARM_FSET_CPU_SUBSET (opt->allowed_archs[i], *opt_set))
                  break;
              }
            if (i == nb_allowed_archs)
 
            /* Add or remove the extension.  */
            if (adding_value)
-             ARM_MERGE_FEATURE_SETS (*ext_set, *ext_set, opt->merge_value);
+             ARM_MERGE_FEATURE_SETS (**ext_set_p, **ext_set_p,
+                                     opt->merge_value);
            else
-             ARM_CLEAR_FEATURE (*ext_set, *ext_set, opt->clear_value);
+             ARM_CLEAR_FEATURE (**ext_set_p, **ext_set_p, opt->clear_value);
 
            break;
          }
   for (opt = arm_cpus; opt->name != NULL; opt++)
     if (opt->name_len == len && strncmp (opt->name, str, len) == 0)
       {
-       arm_feature_set *cpu_set = XNEW (arm_feature_set);
-       ARM_MERGE_FEATURE_SETS (*cpu_set, opt->value, opt->ext);
-       mcpu_cpu_opt = cpu_set;
+       mcpu_cpu_opt = &opt->value;
+       if (!dyn_mcpu_ext_opt)
+         dyn_mcpu_ext_opt = XNEW (arm_feature_set);
+       *dyn_mcpu_ext_opt = opt->ext;
        mcpu_fpu_opt = &opt->default_fpu;
        if (opt->canonical_name)
          {
          }
 
        if (ext != NULL)
-         return arm_parse_extension (ext, &mcpu_cpu_opt);
+         return arm_parse_extension (ext, mcpu_cpu_opt, &dyn_mcpu_ext_opt);
 
        return TRUE;
       }
        strcpy (selected_cpu_name, opt->name);
 
        if (ext != NULL)
-         return arm_parse_extension (ext, &march_cpu_opt);
+         return arm_parse_extension (ext, march_cpu_opt, &dyn_march_ext_opt);
 
        return TRUE;
       }
 }
 
 /* Set the public EABI object attributes.  */
-void
+static void
 aeabi_set_public_attributes (void)
 {
   int arch;
     aeabi_set_attribute_int (Tag_Virtualization_use, virt_sec);
 }
 
+/* Post relaxation hook.  Recompute ARM attributes now that relaxation is
+   finished and free extension feature bits which will not be used anymore.  */
+void
+arm_md_post_relax (void)
+{
+  aeabi_set_public_attributes ();
+  XDELETE (dyn_mcpu_ext_opt);
+  dyn_mcpu_ext_opt = NULL;
+  XDELETE (dyn_march_ext_opt);
+  dyn_march_ext_opt = NULL;
+}
+
 /* Add the default contents for the .ARM.attributes section.  */
 void
 arm_md_end (void)
   for (opt = arm_cpus + 1; opt->name != NULL; opt++)
     if (streq (opt->name, name))
       {
-       arm_feature_set *cpu_set = XNEW (arm_feature_set);
-       ARM_MERGE_FEATURE_SETS (*cpu_set, opt->value, opt->ext);
-       mcpu_cpu_opt = cpu_set;
-       selected_cpu = *mcpu_cpu_opt;
+       mcpu_cpu_opt = &opt->value;
+       if (!dyn_mcpu_ext_opt)
+         dyn_mcpu_ext_opt = XNEW (arm_feature_set);
+       *dyn_mcpu_ext_opt = opt->ext;
+       ARM_MERGE_FEATURE_SETS (selected_cpu, *mcpu_cpu_opt, *dyn_mcpu_ext_opt);
        if (opt->canonical_name)
          strcpy (selected_cpu_name, opt->canonical_name);
        else
            selected_cpu_name[i] = 0;
          }
        ARM_MERGE_FEATURE_SETS (cpu_variant, *mcpu_cpu_opt, *mfpu_opt);
+       if (dyn_mcpu_ext_opt)
+         ARM_MERGE_FEATURE_SETS (cpu_variant, cpu_variant, *dyn_mcpu_ext_opt);
        *input_line_pointer = saved_char;
        demand_empty_rest_of_line ();
        return;
     if (streq (opt->name, name))
       {
        mcpu_cpu_opt = &opt->value;
-       selected_cpu = opt->value;
+       XDELETE (dyn_mcpu_ext_opt);
+       dyn_mcpu_ext_opt = NULL;
+       selected_cpu = *mcpu_cpu_opt;
        strcpy (selected_cpu_name, opt->name);
-       ARM_MERGE_FEATURE_SETS (cpu_variant, *mcpu_cpu_opt, *mfpu_opt);
+       ARM_MERGE_FEATURE_SETS (cpu_variant, selected_cpu, *mfpu_opt);
        *input_line_pointer = saved_char;
        demand_empty_rest_of_line ();
        return;
            break;
          }
 
+       if (!dyn_mcpu_ext_opt)
+         {
+           dyn_mcpu_ext_opt = XNEW (arm_feature_set);
+           *dyn_mcpu_ext_opt = arm_arch_none;
+         }
        if (adding_value)
-         ARM_MERGE_FEATURE_SETS (selected_cpu, selected_cpu,
+         ARM_MERGE_FEATURE_SETS (*dyn_mcpu_ext_opt, *dyn_mcpu_ext_opt,
                                  opt->merge_value);
        else
-         ARM_CLEAR_FEATURE (selected_cpu, selected_cpu, opt->clear_value);
+         ARM_CLEAR_FEATURE (*dyn_mcpu_ext_opt, *dyn_mcpu_ext_opt,
+                            opt->clear_value);
 
-       mcpu_cpu_opt = &selected_cpu;
-       ARM_MERGE_FEATURE_SETS (cpu_variant, *mcpu_cpu_opt, *mfpu_opt);
+       ARM_MERGE_FEATURE_SETS (selected_cpu, *mcpu_cpu_opt, *dyn_mcpu_ext_opt);
+       ARM_MERGE_FEATURE_SETS (cpu_variant, selected_cpu, *mfpu_opt);
        *input_line_pointer = saved_char;
        demand_empty_rest_of_line ();
        return;
       {
        mfpu_opt = &opt->value;
        ARM_MERGE_FEATURE_SETS (cpu_variant, *mcpu_cpu_opt, *mfpu_opt);
+       if (dyn_mcpu_ext_opt)
+         ARM_MERGE_FEATURE_SETS (cpu_variant, cpu_variant, *dyn_mcpu_ext_opt);
        *input_line_pointer = saved_char;
        demand_empty_rest_of_line ();
        return;