This is the mail archive of the binutils@sourceware.org mailing list for the binutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

RE: [PATCH 1/2] Add support for O32 FPXX ABI


Hi Richard,

I've reworked the patch to use a loadable section and then segment for passing information to the dynamic linker. This is very similar to the structure that you proposed but I have made some changes:

1) I'm not sure there is much value encoding the gpr_size as log2 versus just having an enum for the two current widths. I'm not that bothered though so if you have a gut feeling it would be better log2 then fine.
2) I changed fpr_size to fp_abi. It gives some synergy with gnu_attributes and I have re-used the same values
3) I'm not sure we need to track FP register width specifically. In the end the MSA ABI is actually a no-change so recording MSA ABI is not necessary but recording the presence of the MSA ASE is useful. I'm suggesting we use this along with the fp_abi to determine the width of the fp registers.
4) isa_ext and ases make direct use of the existing masks used by gas and I have moved all these to be part of the include/elf/mips.h header. This does leave one small annoyance which is that 64-bit versions of ASEs don't really need to be included in this mask but are for reasons internal to gas. I'm not sure what to do with this, I'm thinking some rework of tc-mips to avoid the need for the 64-bit versions of the ASEs in the mask.

In terms of internal consistency checks I have implemented a hybrid check where ELF flags continue to be used for existing information and abiflags are used for new information. This is mainly due to needing this working and not wanting to break the consistency checking code.

As before it would be useful to get comments on the ABI aspects of this first. I will then update the FPXX specification accordingly when the last few details are ironed out.

I have not yet reworked the new tests nor done the work in glibc to ensure the whole solution works but will do this once the ABI changes are OK.

Regards,
Matthew

include/

    * elf/mips.h (PT_MIPS_ABIFLAGS, SHT_MIPS_ABIFLAGS): Define.
    (Val_GNU_MIPS_ABI_FP_OLD_64): Rename from Val_GNU_MIPS_ABI_FP_64.
    (Val_GNU_MIPS_ABI_FP_64): Redefine.
    (Val_GNU_MIPS_ABI_FP_XX): Define.
    (Elf_External_ABIFlags_v0, Elf_Internal_ABIFlags_v0): New structures.
    (AFL_GPR_32, AFL_GPR_64): Define.
    (AFL_ASE_*): Define. Moved and renamed from opcode/mips.h ASE_*.
    (AFL_EXT_*): Define. Moved and renamed from opcode/mips.h INSN_*.
    * opcode/mips.h (elf/mips.h): Include.
    (INSN_*, ASE_*): Remove.
    (cpu_insn_mask): New static inline function.  Derived from cpu_is_member.
    (cpu_is_member): Rework to use cpu_insn_mask.

bfd/

    * elfxx-mips.c (ABI_O32_P, MIPS_ELF_ABIFLAGS_SECTION_NAME_P): New macro.
    (_bfd_mips_elf_additional_program_headers): Account for new
    PT_MIPS_ABIFLAGS program header.
    (_bfd_mips_elf_modify_segment_map): Create PT_MIPS_ABIFLAGS segment and
    associate with .MIPS.abiflags.
    (mips_elf_merge_obj_attributes): Error checking for
    Val_GNU_MIPS_ABI_FP_OLD_64 and Val_GNU_MIPS_ABI_FP_XX.
    Check EF_MIPS_FP64 flag consistency.
    (_bfd_mips_elf_merge_private_bfd_data): Restructure and add abiflags checks.
    (_bfd_mips_elf_print_private_bfd_data): Display abiflags data.
    (mips_elf_obj_tdata): Add abiflags and abiflags_valid fields.
    (bfd_mips_elf_swap_abiflags_v0_in, bfd_mips_elf_swap_abiflags_v0_out):
    New function.
    (_bfd_mips_elf_section_from_shdr, _bfd_mips_elf_fake_sections): Handle
    SHT_MIPS_ABIFLAGS.
    (_bfd_mips_elf_always_size_sections): Handle .MIPS.abiflags.
    (update_mips_abiflags_isa, infer_mips_abiflags, print_mips_ases,
    print_mips_isa_ext, print_mips_fp_abi_value): New static functions.
    (mips_32bit_flags_p): Moved higher.
    (_bfd_mips_elf_final_link): Handle .MIPS.abiflags

binutils/

    * readelf.c (get_mips_segment_type): Display name for PT_MIPS_ABIFLAGS.
    (get_mips_section_type_name): Display name for SHT_MIPS_ABIFLAGS.
    (display_mips_gnu_attribute): Abstracted fp abi printing to...
    (print_mips_fp_abi_value): New static function. Handle
    Val_GNU_MIPS_ABI_FP_OLD_64 and Val_GNU_MIPS_ABI_FP_XX.
    (print_mips_ases, print_mips_isa_ext): New static functions.
    (process_mips_specific): Display abiflags data.

elfcpp/

    * elfcpp.h (PT_MIPS_ABIFLAGS): New program header type.

gas/

    * config/tc-mips.c (struct mips_set_options): Rename fp32 to fp to allow
    it to store three modes 32, 0==FPXX, 64, update all occurences.  Add
    mips_defer_fp_warn.
    (mips_seen_fp_code, file_mips_opts_checked, mips_flags_frag): New static
    global.
    (file_mips_opts): New static global replacing...
    (file_mips_gp32, file_mips_fp32, file_mips_isa, file_mips_arch,
    file_mips_soft_float, file_mips_single_float): Delete global.
    (mips_opts): Update defaults due to structure changes.
    (md_parse_option): Update due to change in globals.
    (s_module): New static function.
    (enum options, md_longopts, md_parse_option): Add -mfpxx option.
    (mips_pseudo_table): Add .module handler.
    (mips_set_ase): Add opts argument and use instead of mips_opts.
    (md_begin): Use file_mips_opts instead of file_mips_*. Create .MIPS.abiflags
    section.
    (md_mips_end): Add consistency checks for command line, .module and
    .gnu_attribute.  Update .gnu_attribute based on command line and .module
    as applicable.
    (md_assemble): Use file_mips_check_options.
    (check_regno): Set mips_seen_fp_code.  Handle mips_defer_fp_warn.
    (match_float_constant): Rewrite check regarding FP register width.  Add
    support for generating constants when MXHC1 is present.  Handle fp==0 to
    comply with FPXX ABI.
    (macro): Update M_LI_DD similarly to match_float_constant.  Generate MTHC1
    when available.  Check that correct code can be generated for FPXX and
    FP64 ABIs.
    (mips_set_architecture): Delete function.  Moved to mips_after_parse_args.
    (mips_after_parse_args): Move error checking to mips_check_options.  All
    logic now applies to file_mips_opts first and then copies the final state
    to mips_opts.
    (mips_check_options): New static function.  Common option checking for
    command line, .module and .set.  Use general terms in error messages
    instead of refering to command line options.
    (file_mips_check_options): New static function.  A wrapper for
    mips_check_options with file_mips_opts.
    (s_mipsset): Split into s_mipsset and s_mipssettings.  Settings supported
    by both .set and .module are moved to s_mipssettings.  Warnings and errors
    are kept in s_mipsset because when s_mipssettings is used with s_module
    the warnings are deferred until code is generated.  Any setting supporting
    'default' value is kept in s_mipsset as it is not applicable to s_module.
    Inferred settings are also kept in s_mipsset as s_module does not infer
    any settings.  Use mips_check_options.  Set mips_defer_fp_warn.
    (s_mipssettings): New static function derived from s_mipsset.
    (s_module): New static function that implements .module.  Allows file
    level settings to be changed until code is generated.  Updates BFD arch
    if it is changed by .module.
    (s_cpload, s_cpsetup, s_cplocal, s_cprestore, s_cpreturn, s_cpadd): Use
    file_mips_check_options.
    (mips_elf_final_processing): Use fpabi == Val_GNU_MIPS_ABI_FP_OLD_64
    to determine when to add the EF_MIPS_FP64 flag.  Populate the .MIPS.abiflags
    section.
    (md_obj_end): Use file_mips_check_options.
    (mips_parse_cpu): Update for globals.
    (mips_convert_symbolic_attribute): Support names for .gnu_attribute.
    (ASE_*): Rename throughout to AFL_ASE_*.
    (INSN_*): Rename throughout to AFL_EXT_*.
    * config/tc-mips.h (CONVERT_SYMBOLIC_ATTRIBUTE): Implement macro.
    * doc/c-mips.texi: Document -mfpxx, gnu_attribute values and FP ABIs.

ld/

    * emulparams/elf32bmip.sh: Add .MIPS.abiflags_valid.
    * emulparams/elf32bmipn32-defs.sh: Add .MIPS.abiflags_valid.

opcodes/

    * micromips-opc.c (ASE_*): Rename throughout to AFL_ASE_*.
    * mips-dis.c (ASE_*): Rename throughout to AFL_ASE_*.
    (INSN_*): Rename throughout to AFL_EXT_*.
    * mips-opc.c (ASE_*): Rename throughout to AFL_ASE_*.
    (INSN_*): Rename throughout to AFL_EXT_*.
---
 bfd/elfxx-mips.c                   |  692 +++++++++++++++++++++++--
 binutils/readelf.c                 |  190 ++++++-
 elfcpp/elfcpp.h                    |    4 +-
 gas/config/tc-mips.c               | 1007 +++++++++++++++++++++++-------------
 gas/config/tc-mips.h               |    5 +
 gas/doc/c-mips.texi                |  128 +++++
 include/elf/mips.h                 |  108 ++++-
 include/opcode/mips.h              |  116 +----
 ld/emulparams/elf32bmip.sh         |    3 +-
 ld/emulparams/elf32bmipn32-defs.sh |    1 +
 opcodes/micromips-opc.c            |   18 +-
 opcodes/mips-dis.c                 |   44 +-
 opcodes/mips-opc.c                 |   68 ++--
 13 files changed, 1816 insertions(+), 568 deletions(-)

diff --git a/bfd/elfxx-mips.c b/bfd/elfxx-mips.c
index d939444..e77df3c 100644
--- a/bfd/elfxx-mips.c
+++ b/bfd/elfxx-mips.c
@@ -547,6 +547,10 @@ struct mips_elf_obj_tdata
   /* Input BFD providing Tag_GNU_MIPS_ABI_MSA attribute for output.  */
   bfd *abi_msa_bfd;
 
+  /* The abiflags for this object.  */
+  Elf_Internal_ABIFlags_v0 abiflags;
+  bfd_boolean abiflags_valid;
+
   /* The GOT requirements of input bfds.  */
   struct mips_got_info *got;
 
@@ -776,6 +780,10 @@ static bfd *reldyn_sorting_bfd;
 #define PIC_OBJECT_P(abfd) \
   ((elf_elfheader (abfd)->e_flags & EF_MIPS_PIC) != 0)
 
+/* Nonzero if ABFD is using the O32 ABI.  */
+#define ABI_O32_P(abfd) \
+  ((elf_elfheader (abfd)->e_flags & EF_MIPS_ABI) == E_MIPS_ABI_O32)
+
 /* Nonzero if ABFD is using the N32 ABI.  */
 #define ABI_N32_P(abfd) \
   ((elf_elfheader (abfd)->e_flags & EF_MIPS_ABI2) != 0)
@@ -808,6 +816,10 @@ static bfd *reldyn_sorting_bfd;
 #define MIPS_ELF_OPTIONS_SECTION_NAME_P(NAME) \
   (strcmp (NAME, ".MIPS.options") == 0 || strcmp (NAME, ".options") == 0)
 
+/* True if NAME is the recognized name of any SHT_MIPS_ABIFLAGS section.  */
+#define MIPS_ELF_ABIFLAGS_SECTION_NAME_P(NAME) \
+  (strcmp (NAME, ".MIPS.abiflags") == 0)
+
 /* Whether the section is readonly.  */
 #define MIPS_ELF_READONLY_SECTION(sec) \
   ((sec->flags & (SEC_ALLOC | SEC_LOAD | SEC_READONLY))		\
@@ -2664,6 +2676,42 @@ bfd_mips_elf_swap_options_out (bfd *abfd, const Elf_Internal_Options *in,
   H_PUT_16 (abfd, in->section, ex->section);
   H_PUT_32 (abfd, in->info, ex->info);
 }
+
+/* Swap in an abiflags structure.  */
+
+void
+bfd_mips_elf_swap_abiflags_v0_in (bfd *abfd,
+				  const Elf_External_ABIFlags_v0 *ex,
+				  Elf_Internal_ABIFlags_v0 *in)
+{
+  in->version = H_GET_32 (abfd, ex->version);
+  in->isa_level = H_GET_8 (abfd, ex->isa_level);
+  in->isa_rev = H_GET_8 (abfd, ex->isa_rev);
+  in->gpr_size = H_GET_8 (abfd, ex->gpr_size);
+  in->fp_abi = H_GET_8 (abfd, ex->fp_abi);
+  in->isa_ext = H_GET_32 (abfd, ex->isa_ext);
+  in->ases = H_GET_32 (abfd, ex->ases);
+  in->flags1 = H_GET_32 (abfd, ex->flags1);
+  in->flags2 = H_GET_32 (abfd, ex->flags2);
+}
+
+/* Swap out an abiflags structure.  */
+
+void
+bfd_mips_elf_swap_abiflags_v0_out (bfd *abfd,
+				   const Elf_Internal_ABIFlags_v0 *in,
+				   Elf_External_ABIFlags_v0 *ex)
+{
+  H_PUT_32 (abfd, in->version, ex->version);
+  H_PUT_8 (abfd, in->isa_level, ex->isa_level);
+  H_PUT_8 (abfd, in->isa_rev, ex->isa_rev);
+  H_PUT_8 (abfd, in->gpr_size, ex->gpr_size);
+  H_PUT_8 (abfd, in->fp_abi, ex->fp_abi);
+  H_PUT_32 (abfd, in->isa_ext, ex->isa_ext);
+  H_PUT_32 (abfd, in->ases, ex->ases);
+  H_PUT_32 (abfd, in->flags1, ex->flags1);
+  H_PUT_32 (abfd, in->flags2, ex->flags2);
+}
 

 /* This function is called via qsort() to sort the dynamic relocation
    entries by increasing r_symndx value.  */
@@ -6910,6 +6958,11 @@ _bfd_mips_elf_section_from_shdr (bfd *abfd,
       if (!MIPS_ELF_OPTIONS_SECTION_NAME_P (name))
 	return FALSE;
       break;
+    case SHT_MIPS_ABIFLAGS:
+      if (!MIPS_ELF_ABIFLAGS_SECTION_NAME_P (name))
+	return FALSE;
+      flags = (SEC_LINK_ONCE | SEC_LINK_DUPLICATES_SAME_SIZE);
+      break;
     case SHT_MIPS_DWARF:
       if (! CONST_STRNEQ (name, ".debug_")
           && ! CONST_STRNEQ (name, ".zdebug_"))
@@ -6940,6 +6993,20 @@ _bfd_mips_elf_section_from_shdr (bfd *abfd,
 	return FALSE;
     }
 
+  if (hdr->sh_type == SHT_MIPS_ABIFLAGS)
+    {
+      Elf_External_ABIFlags_v0 ext;
+
+      if (! bfd_get_section_contents (abfd, hdr->bfd_section,
+				      &ext, 0, sizeof ext))
+	return FALSE;
+      bfd_mips_elf_swap_abiflags_v0_in (abfd, &ext,
+					&mips_elf_tdata (abfd)->abiflags);
+      if (mips_elf_tdata (abfd)->abiflags.version != 0)
+	return FALSE;
+      mips_elf_tdata (abfd)->abiflags_valid = TRUE;
+    }
+
   /* FIXME: We should record sh_info for a .gptab section.  */
 
   /* For a .reginfo section, set the gp value in the tdata information
@@ -7106,6 +7173,11 @@ _bfd_mips_elf_fake_sections (bfd *abfd, Elf_Internal_Shdr *hdr, asection *sec)
       hdr->sh_entsize = 1;
       hdr->sh_flags |= SHF_MIPS_NOSTRIP;
     }
+  else if (CONST_STRNEQ (name, ".MIPS.abiflags"))
+    {
+      hdr->sh_type = SHT_MIPS_ABIFLAGS;
+      hdr->sh_entsize = sizeof (Elf_External_ABIFlags_v0);
+    }
   else if (CONST_STRNEQ (name, ".debug_")
            || CONST_STRNEQ (name, ".zdebug_"))
     {
@@ -9025,7 +9097,7 @@ bfd_boolean
 _bfd_mips_elf_always_size_sections (bfd *output_bfd,
 				    struct bfd_link_info *info)
 {
-  asection *ri;
+  asection *sect;
   struct mips_elf_link_hash_table *htab;
   struct mips_htab_traverse_info hti;
 
@@ -9033,9 +9105,14 @@ _bfd_mips_elf_always_size_sections (bfd *output_bfd,
   BFD_ASSERT (htab != NULL);
 
   /* The .reginfo section has a fixed size.  */
-  ri = bfd_get_section_by_name (output_bfd, ".reginfo");
-  if (ri != NULL)
-    bfd_set_section_size (output_bfd, ri, sizeof (Elf32_External_RegInfo));
+  sect = bfd_get_section_by_name (output_bfd, ".reginfo");
+  if (sect != NULL)
+    bfd_set_section_size (output_bfd, sect, sizeof (Elf32_External_RegInfo));
+
+  /* The .MIPS.abiflags section has a fixed size.  */
+  sect = bfd_get_section_by_name (output_bfd, ".MIPS.abiflags");
+  if (sect != NULL)
+    bfd_set_section_size (output_bfd, sect, sizeof (Elf_External_ABIFlags_v0));
 
   hti.info = info;
   hti.output_bfd = output_bfd;
@@ -11778,6 +11855,10 @@ _bfd_mips_elf_additional_program_headers (bfd *abfd,
   if (s && (s->flags & SEC_LOAD))
     ++ret;
 
+  /* See if we need a PT_MIPS_ABIFLAGS segment.  */
+  if (bfd_get_section_by_name (abfd, ".MIPS.abiflags"))
+    ++ret;
+
   /* See if we need a PT_MIPS_OPTIONS segment.  */
   if (IRIX_COMPAT (abfd) == ict_irix6
       && bfd_get_section_by_name (abfd,
@@ -11840,6 +11921,37 @@ _bfd_mips_elf_modify_segment_map (bfd *abfd,
 	}
     }
 
+  /* If there is a .MIPS.abiflags section, we need a PT_MIPS_ABIFLAGS
+     segment.  */
+  s = bfd_get_section_by_name (abfd, ".MIPS.abiflags");
+  if (s != NULL && (s->flags & SEC_LOAD) != 0)
+    {
+      for (m = elf_seg_map (abfd); m != NULL; m = m->next)
+	if (m->p_type == PT_MIPS_ABIFLAGS)
+	  break;
+      if (m == NULL)
+	{
+	  amt = sizeof *m;
+	  m = bfd_zalloc (abfd, amt);
+	  if (m == NULL)
+	    return FALSE;
+
+	  m->p_type = PT_MIPS_ABIFLAGS;
+	  m->count = 1;
+	  m->sections[0] = s;
+
+	  /* We want to put it after the PHDR and INTERP segments.  */
+	  pm = &elf_seg_map (abfd);
+	  while (*pm != NULL
+		 && ((*pm)->p_type == PT_PHDR
+		     || (*pm)->p_type == PT_INTERP))
+	    pm = &(*pm)->next;
+
+	  m->next = *pm;
+	  *pm = m;
+	}
+    }
+
   /* For IRIX 6, we don't have .mdebug sections, nor does anything but
      .dynamic end up in PT_DYNAMIC.  However, we do have to insert a
      PT_MIPS_OPTIONS segment immediately following the program header
@@ -13573,6 +13685,99 @@ _bfd_mips_elf_insn32 (struct bfd_link_info *info, bfd_boolean on)
   mips_elf_hash_table (info)->insn32 = on;
 }
 

+/* Update the isa_level and isa_rev fields of abiflags.  */
+
+static void
+update_mips_abiflags_isa (bfd *abfd, Elf_Internal_ABIFlags_v0 *abiflags)
+{
+  abiflags->isa_rev = 0;
+  switch (elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH)
+    {
+    case E_MIPS_ARCH_1:
+      abiflags->isa_level = 1;
+      break;
+    case E_MIPS_ARCH_2:
+      abiflags->isa_level = 2;
+      break;
+    case E_MIPS_ARCH_3:
+      abiflags->isa_level = 3;
+      break;
+    case E_MIPS_ARCH_4:
+      abiflags->isa_level = 4;
+      break;
+    case E_MIPS_ARCH_5:
+      abiflags->isa_level = 5;
+      break;
+    case E_MIPS_ARCH_32:
+      abiflags->isa_level = 32;
+      abiflags->isa_rev = 1;
+      break;
+    case E_MIPS_ARCH_32R2:
+      abiflags->isa_level = 32;
+      abiflags->isa_rev = 2;
+      break;
+    case E_MIPS_ARCH_64:
+      abiflags->isa_level = 64;
+      abiflags->isa_rev = 1;
+      break;
+    case E_MIPS_ARCH_64R2:
+      abiflags->isa_level = 64;
+      abiflags->isa_rev = 2;
+      break;
+    default:
+      (*_bfd_error_handler)
+	(_("%B: Unknown architecture %s"),
+	 abfd, bfd_printable_name (abfd));
+    }
+}
+
+/* Return true if the given ELF header flags describe a 32-bit binary.  */
+
+static bfd_boolean
+mips_32bit_flags_p (flagword flags)
+{
+  return ((flags & EF_MIPS_32BITMODE) != 0
+	  || (flags & EF_MIPS_ABI) == E_MIPS_ABI_O32
+	  || (flags & EF_MIPS_ABI) == E_MIPS_ABI_EABI32
+	  || (flags & EF_MIPS_ARCH) == E_MIPS_ARCH_1
+	  || (flags & EF_MIPS_ARCH) == E_MIPS_ARCH_2
+	  || (flags & EF_MIPS_ARCH) == E_MIPS_ARCH_32
+	  || (flags & EF_MIPS_ARCH) == E_MIPS_ARCH_32R2);
+}
+
+/* Infer the content of the ABI flags based on the elf header.  */
+
+static void
+infer_mips_abiflags (bfd *abfd, Elf_Internal_ABIFlags_v0* abiflags)
+{
+  obj_attribute *in_attr;
+
+  abiflags->version = 0;
+
+  update_mips_abiflags_isa (abfd, abiflags);
+
+  if (mips_32bit_flags_p (elf_elfheader (abfd)->e_flags))
+    abiflags->gpr_size = AFL_GPR_32;
+  else
+    abiflags->gpr_size = AFL_GPR_64;
+
+  in_attr = elf_known_obj_attributes (abfd)[OBJ_ATTR_GNU];
+  abiflags->fp_abi = in_attr[Tag_GNU_MIPS_ABI_FP].i;
+
+  abiflags->isa_ext = 0;
+  abiflags->ases = 0;
+
+  if (elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH_ASE_MDMX)
+    abiflags->ases |= AFL_ASE_MDMX;
+  if (elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH_ASE_M16)
+    abiflags->ases |= AFL_ASE_MIPS16;
+  if (elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH_ASE_MICROMIPS)
+    abiflags->ases |= AFL_ASE_MICROMIPS;
+
+  abiflags->flags1 = 0;
+  abiflags->flags2 = 0;
+}
+
 /* We need to use a special link routine to handle the .reginfo and
    the .mdebug sections.  We need to merge all instances of these
    sections together, not write them all out sequentially.  */
@@ -13583,7 +13788,7 @@ _bfd_mips_elf_final_link (bfd *abfd, struct bfd_link_info *info)
   asection *o;
   struct bfd_link_order *p;
   asection *reginfo_sec, *mdebug_sec, *gptab_data_sec, *gptab_bss_sec;
-  asection *rtproc_sec;
+  asection *rtproc_sec, *abiflags_sec;
   Elf32_RegInfo reginfo;
   struct ecoff_debug_info debug;
   struct mips_htab_traverse_info hti;
@@ -13665,12 +13870,47 @@ _bfd_mips_elf_final_link (bfd *abfd, struct bfd_link_info *info)
 
   /* Go through the sections and collect the .reginfo and .mdebug
      information.  */
+  abiflags_sec = NULL;
   reginfo_sec = NULL;
   mdebug_sec = NULL;
   gptab_data_sec = NULL;
   gptab_bss_sec = NULL;
   for (o = abfd->sections; o != NULL; o = o->next)
     {
+      if (strcmp (o->name, ".MIPS.abiflags") == 0)
+	{
+	  /* We have found the .MIPS.abiflags section in the output file.
+	     Look through all the link_orders comprising it and remove them.
+	     The data is merged in _bfd_mips_elf_merge_private_bfd_data.  */
+	  for (p = o->map_head.link_order; p != NULL; p = p->next)
+	    {
+	      asection *input_section;
+
+	      if (p->type != bfd_indirect_link_order)
+		{
+		  if (p->type == bfd_data_link_order)
+		    continue;
+		  abort ();
+		}
+
+	      input_section = p->u.indirect.section;
+
+	      /* Hack: reset the SEC_HAS_CONTENTS flag so that
+		 elf_link_input_bfd ignores this section.  */
+	      input_section->flags &= ~SEC_HAS_CONTENTS;
+	    }
+
+	  /* Size has been set in _bfd_mips_elf_always_size_sections.  */
+	  BFD_ASSERT(o->size == sizeof (Elf_External_ABIFlags_v0));
+
+	  /* Skip this section later on (I don't think this currently
+	     matters, but someday it might).  */
+	  o->map_head.link_order = NULL;
+
+	  abiflags_sec = o;
+
+	}
+
       if (strcmp (o->name, ".reginfo") == 0)
 	{
 	  memset (&reginfo, 0, sizeof reginfo);
@@ -14155,6 +14395,24 @@ _bfd_mips_elf_final_link (bfd *abfd, struct bfd_link_info *info)
 
   /* Now write out the computed sections.  */
 
+  if (abiflags_sec != NULL)
+    {
+      Elf_External_ABIFlags_v0 ext;
+      Elf_Internal_ABIFlags_v0 *abiflags;
+
+      abiflags = &mips_elf_tdata (abfd)->abiflags;
+
+      /* Set up the abiflags if no valid input sections were found.  */
+      if (!mips_elf_tdata (abfd)->abiflags_valid)
+	{
+	  infer_mips_abiflags (abfd, abiflags);
+	  mips_elf_tdata (abfd)->abiflags_valid = TRUE;
+	}
+      bfd_mips_elf_swap_abiflags_v0_out (abfd, abiflags, &ext);
+      if (! bfd_set_section_contents (abfd, abiflags_sec, &ext, 0, sizeof ext))
+	return FALSE;
+    }
+
   if (reginfo_sec != NULL)
     {
       Elf32_External_RegInfo ext;
@@ -14312,21 +14570,6 @@ mips_mach_extends_p (unsigned long base, unsigned long extension)
 }
 
 
-/* Return true if the given ELF header flags describe a 32-bit binary.  */
-
-static bfd_boolean
-mips_32bit_flags_p (flagword flags)
-{
-  return ((flags & EF_MIPS_32BITMODE) != 0
-	  || (flags & EF_MIPS_ABI) == E_MIPS_ABI_O32
-	  || (flags & EF_MIPS_ABI) == E_MIPS_ABI_EABI32
-	  || (flags & EF_MIPS_ARCH) == E_MIPS_ARCH_1
-	  || (flags & EF_MIPS_ARCH) == E_MIPS_ARCH_2
-	  || (flags & EF_MIPS_ARCH) == E_MIPS_ARCH_32
-	  || (flags & EF_MIPS_ARCH) == E_MIPS_ARCH_32R2);
-}
-
-
 /* Merge object attributes from IBFD into OBFD.  Raise an error if
    there are conflicting attributes.  */
 static bfd_boolean
@@ -14385,6 +14628,17 @@ mips_elf_merge_obj_attributes (bfd *ibfd, bfd *obfd)
 		   obfd, abi_fp_bfd, ibfd, "-mhard-float", "-msoft-float");
 		break;
 
+	      case Val_GNU_MIPS_ABI_FP_OLD_64:
+		_bfd_error_handler
+		  (_("Warning: %B uses %s (set by %B), %B uses %s"),
+		   obfd, abi_fp_bfd, ibfd,
+		   "-mdouble-float", "-mips32r2 -mfp64 (12 callee-saved)");
+		break;
+
+	      case Val_GNU_MIPS_ABI_FP_XX:
+		/* No change -mfp32 + -mfpxx == -mfp32 */
+		break;
+
 	      case Val_GNU_MIPS_ABI_FP_64:
 		_bfd_error_handler
 		  (_("Warning: %B uses %s (set by %B), %B uses %s"),
@@ -14417,6 +14671,20 @@ mips_elf_merge_obj_attributes (bfd *ibfd, bfd *obfd)
 		   obfd, abi_fp_bfd, ibfd, "-mhard-float", "-msoft-float");
 		break;
 
+	      case Val_GNU_MIPS_ABI_FP_OLD_64:
+		_bfd_error_handler
+		  (_("Warning: %B uses %s (set by %B), %B uses %s"),
+		   obfd, abi_fp_bfd, ibfd,
+		   "-msingle-float", "-mips32r2 -mfp64 (12 callee-saved)");
+		break;
+
+	      case Val_GNU_MIPS_ABI_FP_XX:
+		_bfd_error_handler
+		  (_("Warning: %B uses %s (set by %B), %B uses %s"),
+		   obfd, abi_fp_bfd, ibfd,
+		   "-msingle-float", "-mfpxx");
+		break;
+
 	      case Val_GNU_MIPS_ABI_FP_64:
 		_bfd_error_handler
 		  (_("Warning: %B uses %s (set by %B), %B uses %s"),
@@ -14439,6 +14707,8 @@ mips_elf_merge_obj_attributes (bfd *ibfd, bfd *obfd)
 	      {
 	      case Val_GNU_MIPS_ABI_FP_DOUBLE:
 	      case Val_GNU_MIPS_ABI_FP_SINGLE:
+	      case Val_GNU_MIPS_ABI_FP_OLD_64:
+	      case Val_GNU_MIPS_ABI_FP_XX:
 	      case Val_GNU_MIPS_ABI_FP_64:
 		_bfd_error_handler
 		  (_("Warning: %B uses %s (set by %B), %B uses %s"),
@@ -14455,6 +14725,99 @@ mips_elf_merge_obj_attributes (bfd *ibfd, bfd *obfd)
 	      }
 	    break;
 
+	  case Val_GNU_MIPS_ABI_FP_OLD_64:
+	    switch (in_attr[Tag_GNU_MIPS_ABI_FP].i)
+	      {
+	      case Val_GNU_MIPS_ABI_FP_DOUBLE:
+		_bfd_error_handler
+		  (_("Warning: %B uses %s (set by %B), %B uses %s"),
+		   obfd, abi_fp_bfd, ibfd,
+		   "-mips32r2 -mfp64 (12 callee-saved)", "-mdouble-float");
+		break;
+
+	      case Val_GNU_MIPS_ABI_FP_SINGLE:
+		_bfd_error_handler
+		  (_("Warning: %B uses %s (set by %B), %B uses %s"),
+		   obfd, abi_fp_bfd, ibfd,
+		   "-mips32r2 -mfp64 (12 callee-saved)", "-msingle-float");
+		break;
+
+	      case Val_GNU_MIPS_ABI_FP_SOFT:
+		_bfd_error_handler
+		  (_("Warning: %B uses %s (set by %B), %B uses %s"),
+		   obfd, abi_fp_bfd, ibfd, "-mhard-float", "-msoft-float");
+		break;
+
+	      case Val_GNU_MIPS_ABI_FP_XX:
+		_bfd_error_handler
+		  (_("Warning: %B uses %s (set by %B), %B uses %s"),
+		   obfd, abi_fp_bfd, ibfd, "-mips32r2 -mfp64 (12 callee-saved)",
+		   "-mfpxx");
+		break;
+
+	      case Val_GNU_MIPS_ABI_FP_64:
+		_bfd_error_handler
+		  (_("Warning: %B uses %s (set by %B), %B uses %s"),
+		   obfd, abi_fp_bfd, ibfd, "-mips32r2 -mfp64 (12 callee-saved)",
+		   "-mips32r2 -mfp64");
+		break;
+
+	      default:
+		_bfd_error_handler
+		  (_("Warning: %B uses %s (set by %B), "
+		     "%B uses unknown floating point ABI %d"),
+		   obfd, abi_fp_bfd, ibfd,
+		   "-mips32r2 -mfp64 (12 callee-saved)",
+		   in_attr[Tag_GNU_MIPS_ABI_FP].i);
+		break;
+	      }
+	    break;
+
+	  case Val_GNU_MIPS_ABI_FP_XX:
+	    switch (in_attr[Tag_GNU_MIPS_ABI_FP].i)
+	      {
+	      case Val_GNU_MIPS_ABI_FP_DOUBLE:
+		/* Update: -mfpxx + -mfp32 == -mfp32.  */
+		mips_elf_tdata (obfd)->abi_fp_bfd = ibfd;
+		out_attr[Tag_GNU_MIPS_ABI_FP].i = in_attr[Tag_GNU_MIPS_ABI_FP].i;
+		break;
+
+	      case Val_GNU_MIPS_ABI_FP_SINGLE:
+		_bfd_error_handler
+		  (_("Warning: %B uses %s (set by %B), %B uses %s"),
+		   obfd, abi_fp_bfd, ibfd,
+		   "-mfpxx", "-msingle-float");
+		break;
+
+	      case Val_GNU_MIPS_ABI_FP_SOFT:
+		_bfd_error_handler
+		  (_("Warning: %B uses %s (set by %B), %B uses %s"),
+		   obfd, abi_fp_bfd, ibfd, "-mhard-float", "-msoft-float");
+		break;
+
+	      case Val_GNU_MIPS_ABI_FP_OLD_64:
+		_bfd_error_handler
+		  (_("Warning: %B uses %s (set by %B), %B uses %s"),
+		   obfd, abi_fp_bfd, ibfd, "-mfpxx",
+		   "-mips32r2 -mfp64 (12 callee-saved)");
+		break;
+
+	      case Val_GNU_MIPS_ABI_FP_64:
+		/* Update: -mfpxx + -mfp64 == -mfp64.  */
+		mips_elf_tdata (obfd)->abi_fp_bfd = ibfd;
+		out_attr[Tag_GNU_MIPS_ABI_FP].i = in_attr[Tag_GNU_MIPS_ABI_FP].i;
+		break;
+
+	      default:
+		_bfd_error_handler
+		  (_("Warning: %B uses %s (set by %B), "
+		     "%B uses unknown floating point ABI %d"),
+		   obfd, abi_fp_bfd, ibfd,
+		   "-mfpxx", in_attr[Tag_GNU_MIPS_ABI_FP].i);
+		break;
+	      }
+	    break;
+
 	  case Val_GNU_MIPS_ABI_FP_64:
 	    switch (in_attr[Tag_GNU_MIPS_ABI_FP].i)
 	      {
@@ -14478,6 +14841,17 @@ mips_elf_merge_obj_attributes (bfd *ibfd, bfd *obfd)
 		   obfd, abi_fp_bfd, ibfd, "-mhard-float", "-msoft-float");
 		break;
 
+	      case Val_GNU_MIPS_ABI_FP_OLD_64:
+		_bfd_error_handler
+		  (_("Warning: %B uses %s (set by %B), %B uses %s"),
+		   obfd, abi_fp_bfd, ibfd, "-mips32r2 -mfp64",
+		   "-mips32r2 -mfp64 (12 callee-saved)");
+		break;
+
+	      case Val_GNU_MIPS_ABI_FP_XX:
+		/* No change -mfp64 + -mfpxx == -mfp64 */
+		break;
+
 	      default:
 		_bfd_error_handler
 		  (_("Warning: %B uses %s (set by %B), "
@@ -14515,6 +14889,23 @@ mips_elf_merge_obj_attributes (bfd *ibfd, bfd *obfd)
 		   out_attr[Tag_GNU_MIPS_ABI_FP].i, "-msoft-float");
 		break;
 
+	      case Val_GNU_MIPS_ABI_FP_OLD_64:
+		_bfd_error_handler
+		  (_("Warning: %B uses unknown floating point ABI %d "
+		     "(set by %B), %B uses %s"),
+		   obfd, abi_fp_bfd, ibfd,
+		   out_attr[Tag_GNU_MIPS_ABI_FP].i,
+		   "-mips32r2 -mfp64 (12 callee-saved)");
+		break;
+
+	      case Val_GNU_MIPS_ABI_FP_XX:
+		_bfd_error_handler
+		  (_("Warning: %B uses unknown floating point ABI %d "
+		     "(set by %B), %B uses %s"),
+		   obfd, abi_fp_bfd, ibfd,
+		   out_attr[Tag_GNU_MIPS_ABI_FP].i, "-mfpxx");
+		break;
+
 	      case Val_GNU_MIPS_ABI_FP_64:
 		_bfd_error_handler
 		  (_("Warning: %B uses unknown floating point ABI %d "
@@ -14615,6 +15006,16 @@ _bfd_mips_elf_merge_private_bfd_data (bfd *ibfd, bfd *obfd)
       return FALSE;
     }
 
+  /* Set up the FP ABI attribute from the abiflags if it is not already
+     set.  */
+  if (mips_elf_tdata (ibfd)->abiflags_valid)
+    {
+      obj_attribute *in_attr = elf_known_obj_attributes (ibfd)[OBJ_ATTR_GNU];
+      if (in_attr[Tag_GNU_MIPS_ABI_FP].i == Val_GNU_MIPS_ABI_FP_ANY)
+        in_attr[Tag_GNU_MIPS_ABI_FP].i =
+	  mips_elf_tdata (ibfd)->abiflags.fp_abi;
+    }
+
   if (!mips_elf_merge_obj_attributes (ibfd, obfd))
     return FALSE;
 
@@ -14622,26 +15023,6 @@ _bfd_mips_elf_merge_private_bfd_data (bfd *ibfd, bfd *obfd)
   elf_elfheader (obfd)->e_flags |= new_flags & EF_MIPS_NOREORDER;
   old_flags = elf_elfheader (obfd)->e_flags;
 
-  if (! elf_flags_init (obfd))
-    {
-      elf_flags_init (obfd) = TRUE;
-      elf_elfheader (obfd)->e_flags = new_flags;
-      elf_elfheader (obfd)->e_ident[EI_CLASS]
-	= elf_elfheader (ibfd)->e_ident[EI_CLASS];
-
-      if (bfd_get_arch (obfd) == bfd_get_arch (ibfd)
-	  && (bfd_get_arch_info (obfd)->the_default
-	      || mips_mach_extends_p (bfd_get_mach (obfd),
-				      bfd_get_mach (ibfd))))
-	{
-	  if (! bfd_set_arch_mach (obfd, bfd_get_arch (ibfd),
-				   bfd_get_mach (ibfd)))
-	    return FALSE;
-	}
-
-      return TRUE;
-    }
-
   /* Check flag compatibility.  */
 
   new_flags &= ~EF_MIPS_NOREORDER;
@@ -14661,9 +15042,6 @@ _bfd_mips_elf_merge_private_bfd_data (bfd *ibfd, bfd *obfd)
   if ((ibfd->flags & DYNAMIC) != 0)
     new_flags |= EF_MIPS_PIC | EF_MIPS_CPIC;
 
-  if (new_flags == old_flags)
-    return TRUE;
-
   /* Check to see if the input BFD actually contains any sections.
      If not, its flags may not have been initialised either, but it cannot
      actually cause any incompatibility.  */
@@ -14688,6 +15066,79 @@ _bfd_mips_elf_merge_private_bfd_data (bfd *ibfd, bfd *obfd)
   if (null_input_bfd)
     return TRUE;
 
+  /* Populate abiflags using existing information.  */
+  if (!mips_elf_tdata (ibfd)->abiflags_valid)
+    {
+      infer_mips_abiflags (ibfd, &mips_elf_tdata (ibfd)->abiflags);
+      mips_elf_tdata (ibfd)->abiflags_valid = TRUE;
+    }
+  else
+    {
+      Elf_Internal_ABIFlags_v0 abiflags;
+      Elf_Internal_ABIFlags_v0 *iabiflags;
+      infer_mips_abiflags (ibfd, &abiflags);
+      iabiflags = &mips_elf_tdata (ibfd)->abiflags;
+
+      if (iabiflags->isa_level != abiflags.isa_level
+	  || iabiflags->isa_rev != abiflags.isa_rev)
+	(*_bfd_error_handler)
+	  (_("%B: warning: Inconsistent ISA between e_flags and "
+	     ".MIPS.abiflags"), ibfd);
+      if (iabiflags->gpr_size != abiflags.gpr_size)
+	(*_bfd_error_handler)
+	  (_("%B: warning: Inconsistent GPR size between e_flags and "
+	     ".MIPS.abiflags"), ibfd);
+      if (abiflags.fp_abi != Val_GNU_MIPS_ABI_FP_ANY
+	  && iabiflags->fp_abi != abiflags.fp_abi)
+	(*_bfd_error_handler)
+	  (_("%B: warning: Inconsistent FP ABI between e_flags and "
+	     ".MIPS.abiflags"), ibfd);
+      if ((iabiflags->ases & abiflags.ases) != abiflags.ases)
+	(*_bfd_error_handler)
+	  (_("%B: warning: Inconsistent ASEs between e_flags and "
+	     ".MIPS.abiflags"), ibfd);
+    }
+
+  if (!mips_elf_tdata (obfd)->abiflags_valid)
+    {
+      /* Copy input abiflags if output abiflags are not already valid.  */
+      mips_elf_tdata (obfd)->abiflags = mips_elf_tdata (ibfd)->abiflags;
+      mips_elf_tdata (obfd)->abiflags_valid = TRUE;
+    }
+
+  if (! elf_flags_init (obfd))
+    {
+      elf_flags_init (obfd) = TRUE;
+      elf_elfheader (obfd)->e_flags = new_flags;
+      elf_elfheader (obfd)->e_ident[EI_CLASS]
+	= elf_elfheader (ibfd)->e_ident[EI_CLASS];
+
+      if (bfd_get_arch (obfd) == bfd_get_arch (ibfd)
+	  && (bfd_get_arch_info (obfd)->the_default
+	      || mips_mach_extends_p (bfd_get_mach (obfd),
+				      bfd_get_mach (ibfd))))
+	{
+	  if (! bfd_set_arch_mach (obfd, bfd_get_arch (ibfd),
+				   bfd_get_mach (ibfd)))
+	    return FALSE;
+	}
+
+      return TRUE;
+    }
+
+  /* Update the output abiflags fp_abi using the computed fp_abi.  */
+  obj_attribute *out_attr = elf_known_obj_attributes (obfd)[OBJ_ATTR_GNU];
+  mips_elf_tdata (obfd)->abiflags.fp_abi = out_attr[Tag_GNU_MIPS_ABI_FP].i;
+
+  /* Merge abiflags.  */
+  mips_elf_tdata (obfd)->abiflags.ases
+    |= mips_elf_tdata (ibfd)->abiflags.ases;
+  mips_elf_tdata (obfd)->abiflags.isa_ext
+    |= mips_elf_tdata (ibfd)->abiflags.isa_ext;
+
+  if (new_flags == old_flags)
+    return TRUE;
+
   ok = TRUE;
 
   if (((new_flags & (EF_MIPS_PIC | EF_MIPS_CPIC)) != 0)
@@ -14728,6 +15179,9 @@ _bfd_mips_elf_merge_private_bfd_data (bfd *ibfd, bfd *obfd)
 	  elf_elfheader (obfd)->e_flags
 	    |= new_flags & (EF_MIPS_ARCH | EF_MIPS_MACH | EF_MIPS_32BITMODE);
 
+	  /* Update the ABI flags isa_level and isa_rev fields.  */
+	  update_mips_abiflags_isa (obfd, &mips_elf_tdata (obfd)->abiflags);
+
 	  /* Copy across the ABI flags if OBFD doesn't use them
 	     and if that was what caused us to treat IBFD as 32-bit.  */
 	  if ((old_flags & EF_MIPS_ABI) == 0
@@ -14813,6 +15267,20 @@ _bfd_mips_elf_merge_private_bfd_data (bfd *ibfd, bfd *obfd)
       old_flags &= ~EF_MIPS_NAN2008;
     }
 
+  /* Compare FP64 state.  */
+  if ((new_flags & EF_MIPS_FP64) != (old_flags & EF_MIPS_FP64))
+    {
+      _bfd_error_handler (_("%B: linking %s module with previous %s modules"),
+			  ibfd,
+			  (new_flags & EF_MIPS_FP64
+			   ? "-mfp64" : "-mfp32"),
+			  (old_flags & EF_MIPS_FP64
+			   ? "-mfp64" : "-mfp32"));
+      ok = FALSE;
+      new_flags &= ~EF_MIPS_FP64;
+      old_flags &= ~EF_MIPS_FP64;
+    }
+
   /* Warn about any other mismatches */
   if (new_flags != old_flags)
     {
@@ -14944,6 +15412,120 @@ _bfd_mips_elf_get_target_dtag (bfd_vma dtag)
     }
 }
 
+static void
+print_mips_ases (FILE *file, unsigned int mask)
+{
+  if (mask & AFL_ASE_DSP)
+    fputs ("\n\tDSP ASE", file);
+  if (mask & AFL_ASE_DSP64)
+    fputs ("\n\tDSP ASE (64-bit)", file);
+  if (mask & AFL_ASE_DSPR2)
+    fputs ("\n\tDSP R2 ASE", file);
+  if (mask & AFL_ASE_EVA)
+    fputs ("\n\tEnhanced VA Scheme", file);
+  if (mask & AFL_ASE_MCU)
+    fputs ("\n\tMCU (MicroController) ASE", file);
+  if (mask & AFL_ASE_MDMX)
+    fputs ("\n\tMDMX ASE", file);
+  if (mask & AFL_ASE_MIPS3D)
+    fputs ("\n\tMIPS-3D ASE", file);
+  if (mask & AFL_ASE_MT)
+    fputs ("\n\tMT ASE", file);
+  if (mask & AFL_ASE_SMARTMIPS)
+    fputs ("\n\tSmartMIPS ASE", file);
+  if (mask & AFL_ASE_VIRT)
+    fputs ("\n\tVZ ASE", file);
+  if (mask & AFL_ASE_VIRT64)
+    fputs ("\n\tVZ ASE (64-bit)", file);
+  if (mask & AFL_ASE_MSA)
+    fputs ("\n\tMSA ASE", file);
+  if (mask & AFL_ASE_MSA64)
+    fputs ("\n\tMSA ASE (64-bit)", file);
+  if (mask & AFL_ASE_MIPS16)
+    fputs ("\n\tMIPS16 ASE", file);
+  if (mask & AFL_ASE_MICROMIPS)
+    fputs ("\n\tMICROMIPS ASE", file);
+  if (mask & AFL_ASE_XPA)
+    fputs ("\n\tXPA ASE", file);
+  if (mask == 0)
+    fputs ("\n\tNone", file);
+}
+
+static void
+print_mips_isa_ext (FILE *file, unsigned int mask)
+{
+  if (mask & AFL_EXT_XLR)
+    fputs ("\n\tRMI Xlr instruction", file);
+  if (mask & AFL_EXT_OCTEON2)
+    fputs ("\n\tCavium Networks Octeon2", file);
+  if (mask & AFL_EXT_OCTEONP)
+    fputs ("\n\tCavium Networks OcteonP", file);
+  if (mask & AFL_EXT_LOONGSON_3A)
+    fputs ("\n\tLoongson 3A", file);
+  if (mask & AFL_EXT_OCTEON)
+    fputs ("\n\tCavium Networks Octeon", file);
+  if (mask & AFL_EXT_5900)
+    fputs ("\n\tMIPS R5900", file);
+  if (mask & AFL_EXT_4650)
+    fputs ("\n\tMIPS R4650", file);
+  if (mask & AFL_EXT_4010)
+    fputs ("\n\tLSI R4010", file);
+  if (mask & AFL_EXT_4100)
+    fputs ("\n\tNEC VR4100", file);
+  if (mask & AFL_EXT_3900)
+    fputs ("\n\tToshiba R3900", file);
+  if (mask & AFL_EXT_10000)
+    fputs ("\n\tMIPS R10000", file);
+  if (mask & AFL_EXT_SB1)
+    fputs ("\n\tBroadcom SB-1", file);
+  if (mask & AFL_EXT_4111)
+    fputs ("\n\tNEC VR4111/VR4181", file);
+  if (mask & AFL_EXT_4120)
+    fputs ("\n\tNEC VR4120", file);
+  if (mask & AFL_EXT_5400)
+    fputs ("\n\tNEC VR5400", file);
+  if (mask & AFL_EXT_5500)
+    fputs ("\n\tNEC VR5500", file);
+  if (mask & AFL_EXT_LOONGSON_2E)
+    fputs ("\n\tST Microelectronics Loongson 2E", file);
+  if (mask & AFL_EXT_LOONGSON_2F)
+    fputs ("\n\tST Microelectronics Loongson 2F", file);
+  if (mask == 0)
+    fputs ("\n\tNone", file);
+}
+
+static void
+print_mips_fp_abi_value (FILE *file, int val)
+{
+  switch (val)
+    {
+    case Val_GNU_MIPS_ABI_FP_ANY:
+      fprintf (file, _("Hard or soft float\n"));
+      break;
+    case Val_GNU_MIPS_ABI_FP_DOUBLE:
+      fprintf (file, _("Hard float (double precision)\n"));
+      break;
+    case Val_GNU_MIPS_ABI_FP_SINGLE:
+      fprintf (file, _("Hard float (single precision)\n"));
+      break;
+    case Val_GNU_MIPS_ABI_FP_SOFT:
+      fprintf (file, _("Soft float\n"));
+      break;
+    case Val_GNU_MIPS_ABI_FP_OLD_64:
+      fprintf (file, _("Hard float (MIPS32r2 64-bit FPU 12 callee-saved)\n"));
+      break;
+    case Val_GNU_MIPS_ABI_FP_XX:
+      fprintf (file, _("Hard float (32-bit CPU, Any FPU)\n"));
+      break;
+    case Val_GNU_MIPS_ABI_FP_64:
+      fprintf (file, _("Hard float (32-bit CPU, 64-bit FPU)\n"));
+      break;
+    default:
+      fprintf (file, "??? (%d)\n", val);
+      break;
+    }
+}
+
 bfd_boolean
 _bfd_mips_elf_print_private_bfd_data (bfd *abfd, void *ptr)
 {
@@ -15008,7 +15590,7 @@ _bfd_mips_elf_print_private_bfd_data (bfd *abfd, void *ptr)
     fprintf (file, " [nan2008]");
 
   if (elf_elfheader (abfd)->e_flags & EF_MIPS_FP64)
-    fprintf (file, " [fp64]");
+    fprintf (file, " [old fp64]");
 
   if (elf_elfheader (abfd)->e_flags & EF_MIPS_32BITMODE)
     fprintf (file, " [32bitmode]");
@@ -15032,6 +15614,28 @@ _bfd_mips_elf_print_private_bfd_data (bfd *abfd, void *ptr)
 
   fputc ('\n', file);
 
+  if (mips_elf_tdata (abfd)->abiflags_valid)
+    {
+      Elf_Internal_ABIFlags_v0 *abiflags = &mips_elf_tdata (abfd)->abiflags;
+      fprintf (file, "\nMIPS ABI Flags Version: %ld\n", abiflags->version);
+      fprintf (file, "\nISA: MIPS%d", abiflags->isa_level);
+      if (abiflags->isa_rev != 0)
+	fprintf (file, "r%d", abiflags->isa_rev);
+      fprintf (file, "\nGPR size: %d",
+	       (abiflags->gpr_size == AFL_GPR_32) ? 32
+	       : ((abiflags->gpr_size == AFL_GPR_64) ? 64
+	       : -1));
+      fputs ("\nFPR mode: ", file);
+      print_mips_fp_abi_value (file, abiflags->fp_abi);
+      fputs ("ISA Extensions:", file);
+      print_mips_isa_ext (file, abiflags->isa_ext);
+      fputs ("\nASEs:", file);
+      print_mips_ases (file, abiflags->ases);
+      fprintf (file, "\nFLAGS 1: %8.8lx", abiflags->flags1);
+      fprintf (file, "\nFLAGS 2: %8.8lx", abiflags->flags2);
+      fputc ('\n', file);
+    }
+
   return TRUE;
 }
 
diff --git a/binutils/readelf.c b/binutils/readelf.c
index 9cafd7c..b25e2ca 100644
--- a/binutils/readelf.c
+++ b/binutils/readelf.c
@@ -3165,6 +3165,8 @@ get_mips_segment_type (unsigned long type)
       return "RTPROC";
     case PT_MIPS_OPTIONS:
       return "OPTIONS";
+    case PT_MIPS_ABIFLAGS:
+      return "ABIFLAGS";
     default:
       break;
     }
@@ -3364,6 +3366,7 @@ get_mips_section_type_name (unsigned int sh_type)
     case SHT_MIPS_EH_REGION:	 return "MIPS_EH_REGION";
     case SHT_MIPS_XLATE_OLD:	 return "MIPS_XLATE_OLD";
     case SHT_MIPS_PDR_EXCEPTION: return "MIPS_PDR_EXCEPTION";
+    case SHT_MIPS_ABIFLAGS:	 return "MIPS_ABIFLAGS";
     default:
       break;
     }
@@ -11993,6 +11996,38 @@ display_sparc_gnu_attribute (unsigned char * p,
   return display_tag_value (tag, p, end);
 }
 
+static void
+print_mips_fp_abi_value (int val)
+{
+  switch (val)
+    {
+    case Val_GNU_MIPS_ABI_FP_ANY:
+      printf (_("Hard or soft float\n"));
+      break;
+    case Val_GNU_MIPS_ABI_FP_DOUBLE:
+      printf (_("Hard float (double precision)\n"));
+      break;
+    case Val_GNU_MIPS_ABI_FP_SINGLE:
+      printf (_("Hard float (single precision)\n"));
+      break;
+    case Val_GNU_MIPS_ABI_FP_SOFT:
+      printf (_("Soft float\n"));
+      break;
+    case Val_GNU_MIPS_ABI_FP_OLD_64:
+      printf (_("Hard float (MIPS32r2 64-bit FPU 12 callee-saved)\n"));
+      break;
+    case Val_GNU_MIPS_ABI_FP_XX:
+      printf (_("Hard float (32-bit CPU, Any FPU)\n"));
+      break;
+    case Val_GNU_MIPS_ABI_FP_64:
+      printf (_("Hard float (32-bit CPU, 64-bit FPU)\n"));
+      break;
+    default:
+      printf ("??? (%d)\n", val);
+      break;
+    }
+}
+
 static unsigned char *
 display_mips_gnu_attribute (unsigned char * p,
 			    int tag,
@@ -12007,27 +12042,8 @@ display_mips_gnu_attribute (unsigned char * p,
       p += len;
       printf ("  Tag_GNU_MIPS_ABI_FP: ");
 
-      switch (val)
-	{
-	case Val_GNU_MIPS_ABI_FP_ANY:
-	  printf (_("Hard or soft float\n"));
-	  break;
-	case Val_GNU_MIPS_ABI_FP_DOUBLE:
-	  printf (_("Hard float (double precision)\n"));
-	  break;
-	case Val_GNU_MIPS_ABI_FP_SINGLE:
-	  printf (_("Hard float (single precision)\n"));
-	  break;
-	case Val_GNU_MIPS_ABI_FP_SOFT:
-	  printf (_("Soft float\n"));
-	  break;
-	case Val_GNU_MIPS_ABI_FP_64:
-	  printf (_("Hard float (MIPS32r2 64-bit FPU)\n"));
-	  break;
-	default:
-	  printf ("??? (%d)\n", val);
-	  break;
-	}
+      print_mips_fp_abi_value (val);
+
       return p;
    }
 
@@ -12630,10 +12646,93 @@ print_mips_pltgot_entry (unsigned char * data, bfd_vma pltgot, bfd_vma addr)
   return addr + (is_32bit_elf ? 4 : 8);
 }
 
+static void
+print_mips_ases (unsigned int mask)
+{
+  if (mask & AFL_ASE_DSP)
+    fputs ("\n\tDSP ASE", stdout);
+  if (mask & AFL_ASE_DSP64)
+    fputs ("\n\tDSP ASE (64-bit)", stdout);
+  if (mask & AFL_ASE_DSPR2)
+    fputs ("\n\tDSP R2 ASE", stdout);
+  if (mask & AFL_ASE_EVA)
+    fputs ("\n\tEnhanced VA Scheme", stdout);
+  if (mask & AFL_ASE_MCU)
+    fputs ("\n\tMCU (MicroController) ASE", stdout);
+  if (mask & AFL_ASE_MDMX)
+    fputs ("\n\tMDMX ASE", stdout);
+  if (mask & AFL_ASE_MIPS3D)
+    fputs ("\n\tMIPS-3D ASE", stdout);
+  if (mask & AFL_ASE_MT)
+    fputs ("\n\tMT ASE", stdout);
+  if (mask & AFL_ASE_SMARTMIPS)
+    fputs ("\n\tSmartMIPS ASE", stdout);
+  if (mask & AFL_ASE_VIRT)
+    fputs ("\n\tVZ ASE", stdout);
+  if (mask & AFL_ASE_VIRT64)
+    fputs ("\n\tVZ ASE (64-bit)", stdout);
+  if (mask & AFL_ASE_MSA)
+    fputs ("\n\tMSA ASE", stdout);
+  if (mask & AFL_ASE_MSA64)
+    fputs ("\n\tMSA ASE (64-bit)", stdout);
+  if (mask & AFL_ASE_MIPS16)
+    fputs ("\n\tMIPS16 ASE", stdout);
+  if (mask & AFL_ASE_MICROMIPS)
+    fputs ("\n\tMICROMIPS ASE", stdout);
+  if (mask & AFL_ASE_XPA)
+    fputs ("\n\tXPA ASE", stdout);
+  if (mask == 0)
+    fputs ("\n\tNone", stdout);
+}
+
+static void
+print_mips_isa_ext (unsigned int mask)
+{
+  if (mask & AFL_EXT_XLR)
+    fputs ("\n\tRMI Xlr instruction", stdout);
+  if (mask & AFL_EXT_OCTEON2)
+    fputs ("\n\tCavium Networks Octeon2", stdout);
+  if (mask & AFL_EXT_OCTEONP)
+    fputs ("\n\tCavium Networks OcteonP", stdout);
+  if (mask & AFL_EXT_LOONGSON_3A)
+    fputs ("\n\tLoongson 3A", stdout);
+  if (mask & AFL_EXT_OCTEON)
+    fputs ("\n\tCavium Networks Octeon", stdout);
+  if (mask & AFL_EXT_5900)
+    fputs ("\n\tMIPS R5900", stdout);
+  if (mask & AFL_EXT_4650)
+    fputs ("\n\tMIPS R4650", stdout);
+  if (mask & AFL_EXT_4010)
+    fputs ("\n\tLSI R4010", stdout);
+  if (mask & AFL_EXT_4100)
+    fputs ("\n\tNEC VR4100", stdout);
+  if (mask & AFL_EXT_3900)
+    fputs ("\n\tToshiba R3900", stdout);
+  if (mask & AFL_EXT_10000)
+    fputs ("\n\tMIPS R10000", stdout);
+  if (mask & AFL_EXT_SB1)
+    fputs ("\n\tBroadcom SB-1", stdout);
+  if (mask & AFL_EXT_4111)
+    fputs ("\n\tNEC VR4111/VR4181", stdout);
+  if (mask & AFL_EXT_4120)
+    fputs ("\n\tNEC VR4120", stdout);
+  if (mask & AFL_EXT_5400)
+    fputs ("\n\tNEC VR5400", stdout);
+  if (mask & AFL_EXT_5500)
+    fputs ("\n\tNEC VR5500", stdout);
+  if (mask & AFL_EXT_LOONGSON_2E)
+    fputs ("\n\tST Microelectronics Loongson 2E", stdout);
+  if (mask & AFL_EXT_LOONGSON_2F)
+    fputs ("\n\tST Microelectronics Loongson 2F", stdout);
+  if (mask == 0)
+    fputs ("\n\tNone", stdout);
+}
+
 static int
 process_mips_specific (FILE * file)
 {
   Elf_Internal_Dyn * entry;
+  Elf_Internal_Shdr *sect = NULL;
   size_t liblist_offset = 0;
   size_t liblistno = 0;
   size_t conflictsno = 0;
@@ -12651,6 +12750,53 @@ process_mips_specific (FILE * file)
   process_attributes (file, NULL, SHT_GNU_ATTRIBUTES, NULL,
 		      display_mips_gnu_attribute);
 
+  sect = find_section (".MIPS.abiflags");
+
+  if (sect != NULL)
+    {
+      Elf_External_ABIFlags_v0 *abiflags_ext;
+      Elf_Internal_ABIFlags_v0 abiflags_in;
+
+      if (sizeof (Elf_External_ABIFlags_v0) != sect->sh_size)
+	fputs ("\nCorrupt ABI Flags section.\n", stdout);
+      else
+	{
+	  abiflags_ext = get_data (NULL, file, sect->sh_offset, 1,
+				   sect->sh_size, _("MIPS ABI Flags section"));
+	  if (abiflags_ext)
+	    {
+	      abiflags_in.version = BYTE_GET (abiflags_ext->version);
+	      abiflags_in.isa_level = BYTE_GET (abiflags_ext->isa_level);
+	      abiflags_in.isa_rev = BYTE_GET (abiflags_ext->isa_rev);
+	      abiflags_in.gpr_size = BYTE_GET (abiflags_ext->gpr_size);
+	      abiflags_in.fp_abi = BYTE_GET (abiflags_ext->fp_abi);
+	      abiflags_in.isa_ext = BYTE_GET (abiflags_ext->isa_ext);
+	      abiflags_in.ases = BYTE_GET (abiflags_ext->ases);
+	      abiflags_in.flags1 = BYTE_GET (abiflags_ext->flags1);
+	      abiflags_in.flags2 = BYTE_GET (abiflags_ext->flags2);
+
+	      printf ("\nMIPS ABI Flags Version: %ld\n", abiflags_in.version);
+	      printf ("\nISA: MIPS%d", abiflags_in.isa_level);
+	      if (abiflags_in.isa_rev != 0)
+		printf ("r%d", abiflags_in.isa_rev);
+	      printf ("\nGPR size: %d",
+		      (abiflags_in.gpr_size == AFL_GPR_32) ? 32
+		       : ((abiflags_in.gpr_size == AFL_GPR_64) ? 64
+		       : -1));
+	      fputs ("\nFPR mode: ", stdout);
+	      print_mips_fp_abi_value (abiflags_in.fp_abi);
+	      fputs ("ISA Extensions:", stdout);
+	      print_mips_isa_ext (abiflags_in.isa_ext);
+	      fputs ("\nASEs:", stdout);
+	      print_mips_ases (abiflags_in.ases);
+	      printf ("\nFLAGS 1: %8.8lx", abiflags_in.flags1);
+	      printf ("\nFLAGS 2: %8.8lx", abiflags_in.flags2);
+	      fputc ('\n', stdout);
+	      free (abiflags_ext);
+	    }
+	}
+    }
+
   /* We have a lot of special sections.  Thanks SGI!  */
   if (dynamic_section == NULL)
     /* No information available.  */
@@ -12790,11 +12936,11 @@ process_mips_specific (FILE * file)
   if (options_offset != 0)
     {
       Elf_External_Options * eopt;
-      Elf_Internal_Shdr * sect = section_headers;
       Elf_Internal_Options * iopt;
       Elf_Internal_Options * option;
       size_t offset;
       int cnt;
+      sect = section_headers;
 
       /* Find the section header so that we get the size.  */
       while (sect->sh_type != SHT_MIPS_OPTIONS)
diff --git a/elfcpp/elfcpp.h b/elfcpp/elfcpp.h
index 561b54a..1c48bd5 100644
--- a/elfcpp/elfcpp.h
+++ b/elfcpp/elfcpp.h
@@ -490,7 +490,9 @@ enum PT
   // Runtime procedure table.
   PT_MIPS_RTPROC = 0x70000001,
   // .MIPS.options section.
-  PT_MIPS_OPTIONS = 0x70000002
+  PT_MIPS_OPTIONS = 0x70000002,
+  // .MIPS.abiflags section.
+  PT_MIPS_ABIFLAGS = 0x70000003
 };
 
 // The valid bit flags found in the Phdr p_flags field.
diff --git a/gas/config/tc-mips.c b/gas/config/tc-mips.c
index 960169e..350cc08 100644
--- a/gas/config/tc-mips.c
+++ b/gas/config/tc-mips.c
@@ -42,6 +42,8 @@ typedef char static_assert2[sizeof (valueT) < 8 ? -1 : 1];
 #define DBG(x)
 #endif
 
+#define streq(a, b)           (strcmp (a, b) == 0)
+
 #define SKIP_SPACE_TABS(S) \
   do { while (*(S) == ' ' || *(S) == '\t') ++(S); } while (0)
 
@@ -87,6 +89,7 @@ int mips_flag_pdr = TRUE;
 #include "ecoff.h"
 
 static char *mips_regmask_frag;
+static char *mips_flags_frag;
 
 #define ZERO 0
 #define ATREG 1
@@ -240,7 +243,7 @@ struct mips_set_options
      to 32 bit.  This is initially determined when -mgp32 or -mfp32
      is passed but can changed if the assembler code uses .set mipsN.  */
   int gp32;
-  int fp32;
+  int fp;
   /* MIPS architecture (CPU) type.  Changed by .set arch=FOO, the -march
      command line option, and the default CPU.  */
   int arch;
@@ -255,34 +258,50 @@ struct mips_set_options
      Changed by .set singlefloat or .set doublefloat, command-line options
      -msingle-float or -mdouble-float.  The default is false.  */
   bfd_boolean single_float;
+
+  /* Set when an FP mode is entered that is incompatible with the overall
+     module's mode.  This is a potential error if floating point code goes
+     on to be emitted that uses odd numbered single precision registers. A
+     warning is generated if this then happens prior to re-entering a region
+     with a compatible FP mode.  */
+  bfd_boolean mips_defer_fp_warn;
 };
 
-/* This is the struct we use to hold the current set of options.  Note
-   that we must set the isa field to ISA_UNKNOWN and the ASE fields to
-   -1 to indicate that they have not been initialized.  */
+/* Specifies whether FP code has been seen.  */
+static bfd_boolean mips_seen_fp_code = FALSE;
 
-/* True if -mgp32 was passed.  */
-static int file_mips_gp32 = -1;
+/* Specifies whether module level options have been checked yet.  */
+static bfd_boolean file_mips_opts_checked = FALSE;
 
-/* True if -mfp32 was passed.  */
-static int file_mips_fp32 = -1;
+/* True if -mnan=2008, false if -mnan=legacy.  */
+static bfd_boolean mips_flag_nan2008 = FALSE;
 
-/* 1 if -msoft-float, 0 if -mhard-float.  The default is 0.  */
-static int file_mips_soft_float = 0;
+/* This is the struct we use to hold the module level set of options.  Note
+   that we must set the isa field to ISA_UNKNOWN and the ASE fields to
+   -1 to indicate that they have not been initialized.  */
 
-/* 1 if -msingle-float, 0 if -mdouble-float.  The default is 0.   */
-static int file_mips_single_float = 0;
+static struct mips_set_options file_mips_opts =
+{
+  /* isa */ ISA_UNKNOWN, /* ase */ 0, /* mips16 */ -1, /* micromips */ -1,
+  /* noreorder */ 0,  /* at */ ATREG, /* warn_about_macros */ 0,
+  /* nomove */ 0, /* nobopt */ 0, /* noautoextend */ 0, /* insn32 */ FALSE,
+  /* gp32 */ -1, /* fp */ -1, /* arch */ CPU_UNKNOWN, /* sym32 */ FALSE,
+  /* soft_float */ FALSE, /* single_float */ FALSE,
+  /* mips_defer_fp_warn */ FALSE
+};
 
-/* True if -mnan=2008, false if -mnan=legacy.  */
-static bfd_boolean mips_flag_nan2008 = FALSE;
+/* This is the struct we use to hold the current set of options.  Note
+   that we must set the isa field to ISA_UNKNOWN and the ASE fields to
+   -1 to indicate that they have not been initialized.  */
 
 static struct mips_set_options mips_opts =
 {
   /* isa */ ISA_UNKNOWN, /* ase */ 0, /* mips16 */ -1, /* micromips */ -1,
   /* noreorder */ 0,  /* at */ ATREG, /* warn_about_macros */ 0,
   /* nomove */ 0, /* nobopt */ 0, /* noautoextend */ 0, /* insn32 */ FALSE,
-  /* gp32 */ 0, /* fp32 */ 0, /* arch */ CPU_UNKNOWN, /* sym32 */ FALSE,
-  /* soft_float */ FALSE, /* single_float */ FALSE
+  /* gp32 */ -1, /* fp */ -1, /* arch */ CPU_UNKNOWN, /* sym32 */ FALSE,
+  /* soft_float */ FALSE, /* single_float */ FALSE,
+  /* mips_defer_fp_warn */ FALSE
 };
 
 /* The set of ASEs that were selected on the command line, either
@@ -298,9 +317,6 @@ static unsigned int file_ase_explicit;
 unsigned long mips_gprmask;
 unsigned long mips_cprmask[4];
 
-/* MIPS ISA we are using for this output file.  */
-static int file_mips_isa = ISA_UNKNOWN;
-
 /* True if any MIPS16 code was produced.  */
 static int file_ase_mips16;
 
@@ -325,7 +341,6 @@ static int file_ase_micromips;
 #endif
 
 /* The argument of the -march= flag.  The architecture we are assembling.  */
-static int file_mips_arch = CPU_UNKNOWN;
 static const char *mips_arch_string;
 
 /* The argument of the -mtune= flag.  The architecture for which we
@@ -375,7 +390,7 @@ static int mips_32bitmode = 0;
 #define ISA_HAS_ROR(ISA)		\
   ((ISA) == ISA_MIPS32R2		\
    || (ISA) == ISA_MIPS64R2		\
-   || (mips_opts.ase & ASE_SMARTMIPS)	\
+   || (mips_opts.ase & AFL_ASE_SMARTMIPS)	\
    || mips_opts.micromips		\
    )
 
@@ -396,7 +411,7 @@ static int mips_32bitmode = 0;
     (mips_opts.gp32 || !ISA_HAS_64BIT_REGS (mips_opts.isa))
 
 #define HAVE_32BIT_FPRS                            \
-    (mips_opts.fp32 || !ISA_HAS_64BIT_FPRS (mips_opts.isa))
+    (mips_opts.fp != 64 || !ISA_HAS_64BIT_FPRS (mips_opts.isa))
 
 #define HAVE_64BIT_GPRS (!HAVE_32BIT_GPRS)
 #define HAVE_64BIT_FPRS (!HAVE_32BIT_FPRS)
@@ -1269,6 +1284,7 @@ static void s_ehword (int);
 static void s_cpadd (int);
 static void s_insn (int);
 static void s_nan (int);
+static void s_module (int);
 static void md_obj_begin (void);
 static void md_obj_end (void);
 static void s_mips_ent (int);
@@ -1378,6 +1394,7 @@ enum options
     OPTION_CONSTRUCT_FLOATS,
     OPTION_NO_CONSTRUCT_FLOATS,
     OPTION_FP64,
+    OPTION_FPXX,
     OPTION_GP64,
     OPTION_RELAX_BRANCH,
     OPTION_NO_RELAX_BRANCH,
@@ -1493,6 +1510,7 @@ struct option md_longopts[] =
   {"construct-floats", no_argument, NULL, OPTION_CONSTRUCT_FLOATS},
   {"no-construct-floats", no_argument, NULL, OPTION_NO_CONSTRUCT_FLOATS},
   {"mfp64", no_argument, NULL, OPTION_FP64},
+  {"mfpxx", no_argument, NULL, OPTION_FPXX},
   {"mgp64", no_argument, NULL, OPTION_GP64},
   {"relax-branch", no_argument, NULL, OPTION_RELAX_BRANCH},
   {"no-relax-branch", no_argument, NULL, OPTION_NO_RELAX_BRANCH},
@@ -1563,59 +1581,59 @@ struct mips_ase
 
 /* A table of all supported ASEs.  */
 static const struct mips_ase mips_ases[] = {
-  { "dsp", ASE_DSP, ASE_DSP64,
+  { "dsp", AFL_ASE_DSP, AFL_ASE_DSP64,
     OPTION_DSP, OPTION_NO_DSP,
     2, 2, 2, 2 },
 
-  { "dspr2", ASE_DSP | ASE_DSPR2, 0,
+  { "dspr2", AFL_ASE_DSP | AFL_ASE_DSPR2, 0,
     OPTION_DSPR2, OPTION_NO_DSPR2,
     2, 2, 2, 2 },
 
-  { "eva", ASE_EVA, 0,
+  { "eva", AFL_ASE_EVA, 0,
     OPTION_EVA, OPTION_NO_EVA,
     2, 2, 2, 2 },
 
-  { "mcu", ASE_MCU, 0,
+  { "mcu", AFL_ASE_MCU, 0,
     OPTION_MCU, OPTION_NO_MCU,
     2, 2, 2, 2 },
 
   /* Deprecated in MIPS64r5, but we don't implement that yet.  */
-  { "mdmx", ASE_MDMX, 0,
+  { "mdmx", AFL_ASE_MDMX, 0,
     OPTION_MDMX, OPTION_NO_MDMX,
     -1, 1, -1, -1 },
 
   /* Requires 64-bit FPRs, so the minimum MIPS32 revision is 2.  */
-  { "mips3d", ASE_MIPS3D, 0,
+  { "mips3d", AFL_ASE_MIPS3D, 0,
     OPTION_MIPS3D, OPTION_NO_MIPS3D,
     2, 1, -1, -1 },
 
-  { "mt", ASE_MT, 0,
+  { "mt", AFL_ASE_MT, 0,
     OPTION_MT, OPTION_NO_MT,
     2, 2, -1, -1 },
 
-  { "smartmips", ASE_SMARTMIPS, 0,
+  { "smartmips", AFL_ASE_SMARTMIPS, 0,
     OPTION_SMARTMIPS, OPTION_NO_SMARTMIPS,
     1, -1, -1, -1 },
 
-  { "virt", ASE_VIRT, ASE_VIRT64,
+  { "virt", AFL_ASE_VIRT, AFL_ASE_VIRT64,
     OPTION_VIRT, OPTION_NO_VIRT,
     2, 2, 2, 2 },
 
-  { "msa", ASE_MSA, ASE_MSA64,
+  { "msa", AFL_ASE_MSA, AFL_ASE_MSA64,
     OPTION_MSA, OPTION_NO_MSA,
     2, 2, 2, 2 },
 
-  { "xpa", ASE_XPA, 0,
+  { "xpa", AFL_ASE_XPA, 0,
     OPTION_XPA, OPTION_NO_XPA,
     2, 2, -1, -1 }
 };
 
 /* The set of ASEs that require -mfp64.  */
-#define FP64_ASES (ASE_MIPS3D | ASE_MDMX)
+#define FP64_ASES (AFL_ASE_MIPS3D | AFL_ASE_MDMX)
 
 /* Groups of ASE_* flags that represent different revisions of an ASE.  */
 static const unsigned int mips_ase_groups[] = {
-  ASE_DSP | ASE_DSPR2
+  AFL_ASE_DSP | AFL_ASE_DSPR2
 };
 

 /* Pseudo-op table.
@@ -1660,6 +1678,7 @@ static const pseudo_typeS mips_pseudo_table[] =
   {"cpadd", s_cpadd, 0},
   {"insn", s_insn, 0},
   {"nan", s_nan, 0},
+  {"module", s_module, 0},
 
   /* Relatively generic pseudo-ops that happen to be used on MIPS
      chips.  */
@@ -1913,7 +1932,7 @@ mips_check_isa_supports_ase (const struct mips_ase *ase)
 		 ase->name, base, size, min_rev);
     }
   if ((ase->flags & FP64_ASES)
-      && mips_opts.fp32
+      && mips_opts.fp != 64
       && (warned_fp32 & ase->flags) != ase->flags)
     {
       warned_fp32 |= ase->flags;
@@ -1941,14 +1960,15 @@ mips_check_isa_supports_ases (void)
    that were affected.  */
 
 static unsigned int
-mips_set_ase (const struct mips_ase *ase, bfd_boolean enabled_p)
+mips_set_ase (const struct mips_ase *ase, struct mips_set_options *opts,
+	      bfd_boolean enabled_p)
 {
   unsigned int mask;
 
   mask = mips_ase_mask (ase->flags);
-  mips_opts.ase &= ~mask;
+  opts->ase &= ~mask;
   if (enabled_p)
-    mips_opts.ase |= ase->flags;
+    opts->ase |= ase->flags;
   return mask;
 }
 
@@ -3344,7 +3364,7 @@ md_begin (void)
       g_switch_value = 0;
     }
 
-  if (! bfd_set_arch_mach (stdoutput, bfd_arch_mips, file_mips_arch))
+  if (! bfd_set_arch_mach (stdoutput, bfd_arch_mips, file_mips_opts.arch))
     as_warn (_("could not set architecture and machine"));
 
   op_hash = hash_new ();
@@ -3570,6 +3590,11 @@ md_begin (void)
 	}
       }
 
+    sec = subseg_new (".MIPS.abiflags", (subsegT) 0);
+    bfd_set_section_flags (stdoutput, sec, flags);
+    bfd_set_section_alignment (stdoutput, sec, 3);
+    mips_flags_frag = frag_more (sizeof (Elf_External_ABIFlags_v0));
+
     if (ECOFF_DEBUGGING)
       {
 	sec = subseg_new (".mdebug", (subsegT) 0);
@@ -3602,6 +3627,173 @@ md_mips_end (void)
   mips_emit_delays ();
   if (! ECOFF_DEBUGGING)
     md_obj_end ();
+
+  int fpabi = bfd_elf_get_obj_attr_int (stdoutput, OBJ_ATTR_GNU,
+					    Tag_GNU_MIPS_ABI_FP);
+  switch (fpabi)
+    {
+    case Val_GNU_MIPS_ABI_FP_DOUBLE:
+      if ((file_mips_opts.gp32 ? 32 : 64) != file_mips_opts.fp
+	  || file_mips_opts.single_float == 1
+	  || file_mips_opts.soft_float == 1)
+	as_warn (_("Incorrect .gnu_attribute %d,%d found when module is "
+		   "`%s'"),
+		 Tag_GNU_MIPS_ABI_FP, fpabi,
+		 (file_mips_opts.single_float == 1) ? "single-float"
+		 : (file_mips_opts.soft_float == 1) ? "soft-float"
+		 : (file_mips_opts.fp == 32) ? "fp32"
+		 : (file_mips_opts.fp == 64) ? "fp64" : "fpxx");
+      break;
+    case Val_GNU_MIPS_ABI_FP_XX:
+      if (mips_abi != O32_ABI
+	  || file_mips_opts.fp != 0
+	  || file_mips_opts.single_float == 1
+	  || file_mips_opts.soft_float == 1)
+	as_warn (_("Incorrect .gnu_attribute %d,%d found when module is "
+		   "`%s'"),
+		 Tag_GNU_MIPS_ABI_FP, fpabi,
+		 (mips_abi != O32_ABI) ? "!-mabi=32"
+		 : (file_mips_opts.single_float == 1) ? "single-float"
+		 : (file_mips_opts.soft_float == 1) ? "soft-float"
+		 : (file_mips_opts.fp == 32) ? "fp32"
+		 : (file_mips_opts.fp == 64) ? "fp64" : "fpxx");
+      break;
+    case Val_GNU_MIPS_ABI_FP_64:
+      if (mips_abi != O32_ABI
+	  || file_mips_opts.fp != 64
+	  || file_mips_opts.single_float == 1
+	  || file_mips_opts.soft_float == 1)
+	as_warn (_("Incorrect .gnu_attribute %d,%d found when module is "
+		   "`%s'"),
+		 Tag_GNU_MIPS_ABI_FP, fpabi,
+		 (mips_abi != O32_ABI) ? "!-mabi=32"
+		 : (file_mips_opts.single_float == 1) ? "single-float"
+		 : (file_mips_opts.soft_float == 1) ? "soft-float"
+		 : (file_mips_opts.fp == 32) ? "fp32"
+		 : (file_mips_opts.fp == 64) ? "fp64" : "fpxx");
+      break;
+    case Val_GNU_MIPS_ABI_FP_SINGLE:
+      if (file_mips_opts.single_float != 1)
+	as_warn (_("Incorrect .gnu_attribute %d,%d found when module is "
+		   "not `single-float'"),
+		 Tag_GNU_MIPS_ABI_FP, fpabi);
+      break;
+    case Val_GNU_MIPS_ABI_FP_SOFT:
+      if (file_mips_opts.soft_float != 1)
+	as_warn (_("Incorrect .gnu_attribute %d,%d found when module is "
+		   "not `soft-float'"),
+		 Tag_GNU_MIPS_ABI_FP, fpabi);
+      break;
+    case Val_GNU_MIPS_ABI_FP_OLD_64:
+      as_warn (_("Incorrect .gnu_attribute %d,%d found. ABI not "
+		 "supported"),
+	       Tag_GNU_MIPS_ABI_FP, fpabi);
+      break;
+    default:
+      if (fpabi != Val_GNU_MIPS_ABI_FP_ANY)
+	as_warn (_("Incompatible module option and .gnu_attribute seen, "
+		   "unexpected Tag_GNU_MIPS_ABI_FP: %d"),
+		 fpabi);
+      break;
+    }
+
+  /* Only update the ABI if it is not already specified.  A .gnu_attribute
+     always wins.  */
+  if (fpabi == Val_GNU_MIPS_ABI_FP_ANY)
+    {
+      /* Soft-float gets precedence over single-float, the two options should
+	 not be used together so this should not matter.  */
+      if (file_mips_opts.soft_float == 1)
+	fpabi = Val_GNU_MIPS_ABI_FP_SOFT;
+      /* If floating-point code has been seen and the module is single-float
+	 then give this precedence over the double-precision cases below.
+	 Single-float can theoretically be used with any width register.  */
+      else if (mips_seen_fp_code == TRUE
+	       && file_mips_opts.single_float == 1)
+	fpabi = Val_GNU_MIPS_ABI_FP_SINGLE;
+      else if (mips_seen_fp_code == TRUE)
+	{
+	  switch (file_mips_opts.fp)
+	    {
+	    case 32:
+	      fpabi = Val_GNU_MIPS_ABI_FP_DOUBLE;
+	      break;
+	    case 0:
+	      fpabi = Val_GNU_MIPS_ABI_FP_XX;
+	      break;
+	    case 64:
+	      if (file_mips_opts.gp32)
+		fpabi = Val_GNU_MIPS_ABI_FP_64;
+	      else
+		fpabi = Val_GNU_MIPS_ABI_FP_DOUBLE;
+	      break;
+	    }
+	}
+
+      bfd_elf_add_obj_attr_int (stdoutput, OBJ_ATTR_GNU, 
+				Tag_GNU_MIPS_ABI_FP, fpabi);
+    }
+}
+
+/* Perform consistency checks on the current options.  */
+
+static void
+mips_check_options (struct mips_set_options *opts, bfd_boolean abi_checks)
+{
+  /* Check the size of integer registers agrees with the ABI and ISA.  */
+  if (opts->gp32 == 0 && !ISA_HAS_64BIT_REGS (opts->isa))
+    as_bad (_("`gp64' used with a 32-bit processor"));
+  else if (abi_checks == TRUE
+	   && opts->gp32 == 1 && ABI_NEEDS_64BIT_REGS (mips_abi))
+    as_bad (_("`gp32' used with a 64-bit ABI"));
+  else if (abi_checks == TRUE
+	   && opts->gp32 == 0 && ABI_NEEDS_32BIT_REGS (mips_abi))
+    as_bad (_("`gp64' used with a 32-bit ABI"));
+
+  /* Check the size of the float registers agrees with the ABI and ISA.  */
+  switch (opts->fp)
+    {
+    case 0:
+      if (!CPU_HAS_LDC1_SDC1 (opts->arch))
+	as_bad (_("`fpxx' used with a cpu lacking ldc1/sdc1 instructions"));
+      else if (opts->single_float == 1
+	       || opts->soft_float == 1)
+	as_bad (_("`fpxx' cannot be used with `single-float' or `soft-float'"));
+      break;
+    case 64:
+      if (!ISA_HAS_64BIT_FPRS (opts->isa))
+	as_bad (_("`fp64' used with a 32-bit fpu"));
+      else if (abi_checks == TRUE
+	       && ABI_NEEDS_32BIT_REGS (mips_abi)
+	       && !ISA_HAS_MXHC1 (opts->isa))
+	as_warn (_("`fp64' used with a 32-bit ABI"));
+      break;
+    case 32:
+      if (abi_checks == TRUE
+	  && ABI_NEEDS_64BIT_REGS (mips_abi))
+	as_warn (_("`fp32' used with a 64-bit ABI"));
+      break;
+    default:
+      as_bad (_("Unknown size of floating point registers"));
+    }
+
+  if (opts->micromips == 1 && opts->mips16 == 1)
+    as_bad (_("`mips16' cannot be used with `micromips'"));
+}
+
+/* Perform consistency checks on the module level options exactly once.
+   This is a deferred check that happens:
+     at the first .set directive
+     or, at the first pseudo op that generates code
+     or, at the first instruction
+     or, at the end.  */
+
+static void
+file_mips_check_options (void)
+{
+  if (file_mips_opts_checked == FALSE)
+    mips_check_options (&file_mips_opts, TRUE);
+  file_mips_opts_checked = TRUE;
 }
 
 void
@@ -3611,6 +3803,8 @@ md_assemble (char *str)
   bfd_reloc_code_real_type unused_reloc[3]
     = {BFD_RELOC_UNUSED, BFD_RELOC_UNUSED, BFD_RELOC_UNUSED};
 
+  file_mips_check_options ();
+
   imm_expr.X_op = O_absent;
   offset_expr.X_op = O_absent;
   offset_reloc[0] = BFD_RELOC_UNUSED;
@@ -4397,7 +4591,7 @@ convert_reg_type (const struct mips_opcode *opcode,
     case OP_REG_FP:
       /* Allow vector register names for MDMX if the instruction is a 64-bit
 	 FPR load, store or move (including moves to and from GPRs).  */
-      if ((mips_opts.ase & ASE_MDMX)
+      if ((mips_opts.ase & AFL_ASE_MDMX)
 	  && (opcode->pinfo & FP_D)
 	  && (opcode->pinfo & (INSN_COPROC_MOVE_DELAY
 			       | INSN_COPROC_MEMORY_DELAY
@@ -4413,7 +4607,7 @@ convert_reg_type (const struct mips_opcode *opcode,
       return RTYPE_CCC;
 
     case OP_REG_VEC:
-      if (opcode->membership & INSN_5400)
+      if (opcode->membership & AFL_EXT_5400)
 	return RTYPE_FPU;
       return RTYPE_FPU | RTYPE_VEC;
 
@@ -4464,11 +4658,22 @@ check_regno (struct mips_arg_info *arg,
   if (AT && type == OP_REG_GP && regno == AT)
     arg->seen_at = TRUE;
 
-  if (type == OP_REG_FP
-      && (regno & 1) != 0
-      && HAVE_32BIT_FPRS
-      && !mips_oddfpreg_ok (arg->insn->insn_mo, arg->opnum))
-    as_warn (_("float register should be even, was %d"), regno);
+  if (type == OP_REG_FP)
+    {
+      mips_seen_fp_code = TRUE;
+      if ((regno & 1) != 0)
+	{
+	  if (mips_opts.mips_defer_fp_warn == TRUE)
+	    as_warn (_("Dangerous use of FP registers in fp%s when module is fp%s"),
+		     (mips_opts.fp == 32) ? "32"
+		     : (mips_opts.fp == 64) ? "64" : "xx",
+		     (file_mips_opts.fp == 32) ? "32"
+		     : (file_mips_opts.fp == 64) ? "64" : "xx");
+	  if (HAVE_32BIT_FPRS
+	      && !mips_oddfpreg_ok (arg->insn->insn_mo, arg->opnum))
+	    as_warn (_("float register should be even, was %d"), regno);
+	}
+    }
 
   if (type == OP_REG_CCC)
     {
@@ -5123,7 +5328,7 @@ match_mdmx_imm_reg_operand (struct mips_arg_info *arg,
 
   if (arg->token->type == OT_REG)
     {
-      if ((opcode->membership & INSN_5400)
+      if ((opcode->membership & AFL_EXT_5400)
 	  && strcmp (opcode->name, "rzu.ob") == 0)
 	{
 	  set_insn_error_i (arg->argnum, _("operand %d must be an immediate"),
@@ -5150,7 +5355,7 @@ match_mdmx_imm_reg_operand (struct mips_arg_info *arg,
       else
 	{
 	  /* A full vector.  */
-	  if ((opcode->membership & INSN_5400)
+	  if ((opcode->membership & AFL_EXT_5400)
 	      && (strcmp (opcode->name, "sll.ob") == 0
 		  || strcmp (opcode->name, "srl.ob") == 0))
 	    {
@@ -5322,13 +5527,12 @@ match_float_constant (struct mips_arg_info *arg, expressionS *imm,
   /* Handle 64-bit constants for which an immediate value is best.  */
   if (length == 8
       && !mips_disable_float_construction
-      /* Constants can only be constructed in GPRs and copied
-	 to FPRs if the GPRs are at least as wide as the FPRs.
-	 Force the constant into memory if we are using 64-bit FPRs
-	 but the GPRs are only 32 bits wide.  */
-      /* ??? No longer true with the addition of MTHC1, but this
-	 is legacy code...  */
-      && (using_gprs || !(HAVE_64BIT_FPRS && HAVE_32BIT_GPRS))
+      /* Constants can only be constructed in GPRs and copied to FPRs if the
+	 GPRs are at least as wide as the FPRs or MTHC1 is available.  */
+      && (using_gprs
+	  || HAVE_64BIT_GPRS
+	  || ISA_HAS_MXHC1 (mips_opts.isa)
+	  || (HAVE_32BIT_FPRS && mips_opts.fp != 0))
       && ((data[0] == 0 && data[1] == 0)
 	  || (data[2] == 0 && data[3] == 0))
       && ((data[4] == 0 && data[5] == 0)
@@ -6690,7 +6894,8 @@ append_insn (struct mips_cl_insn *ip, expressionS *address_expr,
 	     && (mips_opts.at || mips_pic == NO_PIC)
 	     /* Don't relax BPOSGE32/64 or BC1ANY2T/F and BC1ANY4T/F
 	        as they have no complementing branches.  */
-	     && !(ip->insn_mo->ase & (ASE_MIPS3D | ASE_DSP64 | ASE_DSP)));
+	     && !(ip->insn_mo->ase & (AFL_ASE_MIPS3D | AFL_ASE_DSP64
+				      | AFL_ASE_DSP)));
 
   if (!HAVE_CODE_COMPRESSION
       && address_expr
@@ -11520,14 +11725,18 @@ macro (struct mips_cl_insn *ip, char *str)
 	{
 	  used_at = 1;
 	  load_register (AT, &imm_expr, HAVE_64BIT_FPRS);
-	  if (HAVE_64BIT_FPRS)
-	    {
-	      gas_assert (HAVE_64BIT_GPRS);
-	      macro_build (NULL, "dmtc1", "t,S", AT, op[0]);
-	    }
+	  if (HAVE_64BIT_FPRS && HAVE_64BIT_GPRS)
+	    macro_build (NULL, "dmtc1", "t,S", AT, op[0]);
 	  else
 	    {
-	      macro_build (NULL, "mtc1", "t,G", AT, op[0] + 1);
+	      if (ISA_HAS_MXHC1 (mips_opts.isa))
+	        macro_build (NULL, "mthc1", "t,G", AT, op[0]);
+	      else if (mips_opts.fp != 32)
+		as_bad (_("Unable to generate `%s' compliant code "
+			  "without mthc1"),
+			(mips_opts.fp == 64) ? "fp64" : "fpxx");
+	      else
+		macro_build (NULL, "mtc1", "t,G", AT, op[0] + 1);
 	      if (offset_expr.X_op == O_absent)
 		macro_build (NULL, "mtc1", "t,G", 0, op[0]);
 	      else
@@ -13456,7 +13665,7 @@ md_parse_option (int c, char *arg)
   for (i = 0; i < ARRAY_SIZE (mips_ases); i++)
     if (c == mips_ases[i].option_on || c == mips_ases[i].option_off)
       {
-	file_ase_explicit |= mips_set_ase (&mips_ases[i],
+	file_ase_explicit |= mips_set_ase (&mips_ases[i], &file_mips_opts,
 					   c == mips_ases[i].option_on);
 	return 1;
       }
@@ -13506,39 +13715,39 @@ md_parse_option (int c, char *arg)
       break;
 
     case OPTION_MIPS1:
-      file_mips_isa = ISA_MIPS1;
+      file_mips_opts.isa = ISA_MIPS1;
       break;
 
     case OPTION_MIPS2:
-      file_mips_isa = ISA_MIPS2;
+      file_mips_opts.isa = ISA_MIPS2;
       break;
 
     case OPTION_MIPS3:
-      file_mips_isa = ISA_MIPS3;
+      file_mips_opts.isa = ISA_MIPS3;
       break;
 
     case OPTION_MIPS4:
-      file_mips_isa = ISA_MIPS4;
+      file_mips_opts.isa = ISA_MIPS4;
       break;
 
     case OPTION_MIPS5:
-      file_mips_isa = ISA_MIPS5;
+      file_mips_opts.isa = ISA_MIPS5;
       break;
 
     case OPTION_MIPS32:
-      file_mips_isa = ISA_MIPS32;
+      file_mips_opts.isa = ISA_MIPS32;
       break;
 
     case OPTION_MIPS32R2:
-      file_mips_isa = ISA_MIPS32R2;
+      file_mips_opts.isa = ISA_MIPS32R2;
       break;
 
     case OPTION_MIPS64R2:
-      file_mips_isa = ISA_MIPS64R2;
+      file_mips_opts.isa = ISA_MIPS64R2;
       break;
 
     case OPTION_MIPS64:
-      file_mips_isa = ISA_MIPS64;
+      file_mips_opts.isa = ISA_MIPS64;
       break;
 
     case OPTION_MTUNE:
@@ -13582,32 +13791,32 @@ md_parse_option (int c, char *arg)
       break;
 
     case OPTION_MICROMIPS:
-      if (mips_opts.mips16 == 1)
+      if (file_mips_opts.mips16 == 1)
 	{
 	  as_bad (_("-mmicromips cannot be used with -mips16"));
 	  return 0;
 	}
-      mips_opts.micromips = 1;
+      file_mips_opts.micromips = 1;
       mips_no_prev_insn ();
       break;
 
     case OPTION_NO_MICROMIPS:
-      mips_opts.micromips = 0;
+      file_mips_opts.micromips = 0;
       mips_no_prev_insn ();
       break;
 
     case OPTION_MIPS16:
-      if (mips_opts.micromips == 1)
+      if (file_mips_opts.micromips == 1)
 	{
 	  as_bad (_("-mips16 cannot be used with -micromips"));
 	  return 0;
 	}
-      mips_opts.mips16 = 1;
+      file_mips_opts.mips16 = 1;
       mips_no_prev_insn ();
       break;
 
     case OPTION_NO_MIPS16:
-      mips_opts.mips16 = 0;
+      file_mips_opts.mips16 = 0;
       mips_no_prev_insn ();
       break;
 
@@ -13676,11 +13885,11 @@ md_parse_option (int c, char *arg)
       break;
 
     case OPTION_INSN32:
-      mips_opts.insn32 = TRUE;
+      file_mips_opts.insn32 = TRUE;
       break;
 
     case OPTION_NO_INSN32:
-      mips_opts.insn32 = FALSE;
+      file_mips_opts.insn32 = FALSE;
       break;
 
     case OPTION_MSHARED:
@@ -13692,11 +13901,11 @@ md_parse_option (int c, char *arg)
       break;
 
     case OPTION_MSYM32:
-      mips_opts.sym32 = TRUE;
+      file_mips_opts.sym32 = TRUE;
       break;
 
     case OPTION_MNO_SYM32:
-      mips_opts.sym32 = FALSE;
+      file_mips_opts.sym32 = FALSE;
       break;
 
       /* When generating ELF code, we permit -KPIC and -call_shared to
@@ -13746,35 +13955,39 @@ md_parse_option (int c, char *arg)
       break;
 
     case OPTION_GP32:
-      file_mips_gp32 = 1;
+      file_mips_opts.gp32 = 1;
       break;
 
     case OPTION_GP64:
-      file_mips_gp32 = 0;
+      file_mips_opts.gp32 = 0;
       break;
 
     case OPTION_FP32:
-      file_mips_fp32 = 1;
+      file_mips_opts.fp = 32;
+      break;
+
+    case OPTION_FPXX:
+      file_mips_opts.fp = 0;
       break;
 
     case OPTION_FP64:
-      file_mips_fp32 = 0;
+      file_mips_opts.fp = 64;
       break;
 
     case OPTION_SINGLE_FLOAT:
-      file_mips_single_float = 1;
+      file_mips_opts.single_float = 1;
       break;
 
     case OPTION_DOUBLE_FLOAT:
-      file_mips_single_float = 0;
+      file_mips_opts.single_float = 0;
       break;
 
     case OPTION_SOFT_FLOAT:
-      file_mips_soft_float = 1;
+      file_mips_opts.soft_float = 1;
       break;
 
     case OPTION_HARD_FLOAT:
-      file_mips_soft_float = 0;
+      file_mips_opts.soft_float = 0;
       break;
 
     case OPTION_MABI:
@@ -13849,22 +14062,7 @@ md_parse_option (int c, char *arg)
   return 1;
 }
 

-/* Set up globals to generate code for the ISA or processor
-   described by INFO.  */
-
-static void
-mips_set_architecture (const struct mips_cpu_info *info)
-{
-  if (info != 0)
-    {
-      file_mips_arch = info->cpu;
-      mips_opts.arch = info->cpu;
-      mips_opts.isa = info->isa;
-    }
-}
-
-
-/* Likewise for tuning.  */
+/* Set up globals to tune for the ISA or processor described by INFO.  */
 
 static void
 mips_set_tune (const struct mips_cpu_info *info)
@@ -13899,9 +14097,9 @@ mips_after_parse_args (void)
   if (mips_arch_string != 0)
     arch_info = mips_parse_cpu ("-march", mips_arch_string);
 
-  if (file_mips_isa != ISA_UNKNOWN)
+  if (file_mips_opts.isa != ISA_UNKNOWN)
     {
-      /* Handle -mipsN.  At this point, file_mips_isa contains the
+      /* Handle -mipsN.  At this point, file_mips_opts.isa contains the
 	 ISA level specified by -mipsN, while arch_info->isa contains
 	 the -march selection (if any).  */
       if (arch_info != 0)
@@ -13909,14 +14107,14 @@ mips_after_parse_args (void)
 	  /* -march takes precedence over -mipsN, since it is more descriptive.
 	     There's no harm in specifying both as long as the ISA levels
 	     are the same.  */
-	  if (file_mips_isa != arch_info->isa)
+	  if (file_mips_opts.isa != arch_info->isa)
 	    as_bad (_("-%s conflicts with the other architecture options,"
 		      " which imply -%s"),
-		    mips_cpu_info_from_isa (file_mips_isa)->name,
+		    mips_cpu_info_from_isa (file_mips_opts.isa)->name,
 		    mips_cpu_info_from_isa (arch_info->isa)->name);
 	}
       else
-	arch_info = mips_cpu_info_from_isa (file_mips_isa);
+	arch_info = mips_cpu_info_from_isa (file_mips_opts.isa);
     }
 
   if (arch_info == 0)
@@ -13929,9 +14127,11 @@ mips_after_parse_args (void)
     as_bad (_("-march=%s is not compatible with the selected ABI"),
 	    arch_info->name);
 
-  mips_set_architecture (arch_info);
+  file_mips_opts.arch = arch_info->cpu;
+  file_mips_opts.isa = arch_info->isa;
 
-  /* Optimize for file_mips_arch, unless -mtune selects a different processor.  */
+  /* Optimize for file_mips_opts.arch, unless -mtune selects a different
+     processor.  */
   if (mips_tune_string != 0)
     tune_info = mips_parse_cpu ("-mtune", mips_tune_string);
 
@@ -13940,30 +14140,17 @@ mips_after_parse_args (void)
   else
     mips_set_tune (tune_info);
 
-  if (file_mips_gp32 >= 0)
-    {
-      /* The user specified the size of the integer registers.  Make sure
-	 it agrees with the ABI and ISA.  */
-      if (file_mips_gp32 == 0 && !ISA_HAS_64BIT_REGS (mips_opts.isa))
-	as_bad (_("-mgp64 used with a 32-bit processor"));
-      else if (file_mips_gp32 == 1 && ABI_NEEDS_64BIT_REGS (mips_abi))
-	as_bad (_("-mgp32 used with a 64-bit ABI"));
-      else if (file_mips_gp32 == 0 && ABI_NEEDS_32BIT_REGS (mips_abi))
-	as_bad (_("-mgp64 used with a 32-bit ABI"));
-    }
-  else
+  if (file_mips_opts.gp32 < 0)
     {
       /* Infer the integer register size from the ABI and processor.
 	 Restrict ourselves to 32-bit registers if that's all the
 	 processor has, or if the ABI cannot handle 64-bit registers.  */
-      file_mips_gp32 = (ABI_NEEDS_32BIT_REGS (mips_abi)
-			|| !ISA_HAS_64BIT_REGS (mips_opts.isa));
+      file_mips_opts.gp32 = (ABI_NEEDS_32BIT_REGS (mips_abi)
+			     || !ISA_HAS_64BIT_REGS (file_mips_opts.isa));
     }
 
-  switch (file_mips_fp32)
+  if (file_mips_opts.fp < 0)
     {
-    default:
-    case -1:
       /* No user specified float register size.
 	 ??? GAS treats single-float processors as though they had 64-bit
 	 float registers (although it complains when double-precision
@@ -13971,67 +14158,49 @@ mips_after_parse_args (void)
 	 registers would lead to spurious "register must be even" messages.
 	 So here we assume float registers are never smaller than the
 	 integer ones.  */
-      if (file_mips_gp32 == 0)
+      if (file_mips_opts.gp32 == 0)
 	/* 64-bit integer registers implies 64-bit float registers.  */
-	file_mips_fp32 = 0;
-      else if ((mips_opts.ase & FP64_ASES)
-	       && ISA_HAS_64BIT_FPRS (mips_opts.isa))
+	file_mips_opts.fp = 64;
+      else if ((file_mips_opts.ase & FP64_ASES)
+	       && ISA_HAS_64BIT_FPRS (file_mips_opts.isa))
 	/* -mips3d and -mdmx imply 64-bit float registers, if possible.  */
-	file_mips_fp32 = 0;
+	file_mips_opts.fp = 64;
       else
 	/* 32-bit float registers.  */
-	file_mips_fp32 = 1;
-      break;
-
-    /* The user specified the size of the float registers.  Check if it
-       agrees with the ABI and ISA.  */
-    case 0:
-      if (!ISA_HAS_64BIT_FPRS (mips_opts.isa))
-	as_bad (_("-mfp64 used with a 32-bit fpu"));
-      else if (ABI_NEEDS_32BIT_REGS (mips_abi)
-	       && !ISA_HAS_MXHC1 (mips_opts.isa))
-	as_warn (_("-mfp64 used with a 32-bit ABI"));
-      break;
-    case 1:
-      if (ABI_NEEDS_64BIT_REGS (mips_abi))
-	as_warn (_("-mfp32 used with a 64-bit ABI"));
-      break;
+	file_mips_opts.fp = 32;
     }
 
   /* End of GCC-shared inference code.  */
 
   /* This flag is set when we have a 64-bit capable CPU but use only
      32-bit wide registers.  Note that EABI does not use it.  */
-  if (ISA_HAS_64BIT_REGS (mips_opts.isa)
-      && ((mips_abi == NO_ABI && file_mips_gp32 == 1)
+  if (ISA_HAS_64BIT_REGS (file_mips_opts.isa)
+      && ((mips_abi == NO_ABI && file_mips_opts.gp32 == 1)
 	  || mips_abi == O32_ABI))
     mips_32bitmode = 1;
 
-  if (mips_opts.isa == ISA_MIPS1 && mips_trap)
+  if (file_mips_opts.isa == ISA_MIPS1 && mips_trap)
     as_bad (_("trap exception not supported at ISA 1"));
 
   /* If the selected architecture includes support for ASEs, enable
      generation of code for them.  */
-  if (mips_opts.mips16 == -1)
-    mips_opts.mips16 = (CPU_HAS_MIPS16 (file_mips_arch)) ? 1 : 0;
-  if (mips_opts.micromips == -1)
-    mips_opts.micromips = (CPU_HAS_MICROMIPS (file_mips_arch)) ? 1 : 0;
+  if (file_mips_opts.mips16 == -1)
+    file_mips_opts.mips16 = (CPU_HAS_MIPS16 (file_mips_opts.arch)) ? 1 : 0;
+  if (file_mips_opts.micromips == -1)
+    file_mips_opts.micromips = (CPU_HAS_MICROMIPS (file_mips_opts.arch))
+				? 1 : 0;
 
   /* MIPS3D and MDMX require 64-bit FPRs, so -mfp32 should stop those
      ASEs from being selected implicitly.  */
-  if (file_mips_fp32 == 1)
-    file_ase_explicit |= ASE_MIPS3D | ASE_MDMX;
+  if (file_mips_opts.fp == 32)
+    file_ase_explicit |= AFL_ASE_MIPS3D | AFL_ASE_MDMX;
 
   /* If the user didn't explicitly select or deselect a particular ASE,
      use the default setting for the CPU.  */
-  mips_opts.ase |= (arch_info->ase & ~file_ase_explicit);
+  file_mips_opts.ase |= (arch_info->ase & ~file_ase_explicit);
 
-  file_mips_isa = mips_opts.isa;
-  file_ase = mips_opts.ase;
-  mips_opts.gp32 = file_mips_gp32;
-  mips_opts.fp32 = file_mips_fp32;
-  mips_opts.soft_float = file_mips_soft_float;
-  mips_opts.single_float = file_mips_single_float;
+  /* Set up the current options.  These may change throughout assembly.  */
+  mips_opts = file_mips_opts;
 
   mips_check_isa_supports_ases ();
 
@@ -14922,30 +15091,11 @@ struct mips_option_stack
 
 static struct mips_option_stack *mips_opts_stack;
 
-/* Handle the .set pseudo-op.  */
-
-static void
-s_mipsset (int x ATTRIBUTE_UNUSED)
+static bfd_boolean
+s_mipssettings (char * name)
 {
-  char *name = input_line_pointer, ch;
   const struct mips_ase *ase;
-
-  while (!is_end_of_line[(unsigned char) *input_line_pointer])
-    ++input_line_pointer;
-  ch = *input_line_pointer;
-  *input_line_pointer = '\0';
-
-  if (strcmp (name, "reorder") == 0)
-    {
-      if (mips_opts.noreorder)
-	end_noreorder ();
-    }
-  else if (strcmp (name, "noreorder") == 0)
-    {
-      if (!mips_opts.noreorder)
-	start_noreorder ();
-    }
-  else if (strncmp (name, "at=", 3) == 0)
+  if (strncmp (name, "at=", 3) == 0)
     {
       char *s = name + 3;
 
@@ -14953,61 +15103,27 @@ s_mipsset (int x ATTRIBUTE_UNUSED)
 	as_bad (_("unrecognized register name `%s'"), s);
     }
   else if (strcmp (name, "at") == 0)
-    {
-      mips_opts.at = ATREG;
-    }
+    mips_opts.at = ATREG;
   else if (strcmp (name, "noat") == 0)
-    {
-      mips_opts.at = ZERO;
-    }
-  else if (strcmp (name, "macro") == 0)
-    {
-      mips_opts.warn_about_macros = 0;
-    }
-  else if (strcmp (name, "nomacro") == 0)
-    {
-      if (mips_opts.noreorder == 0)
-	as_bad (_("`noreorder' must be set before `nomacro'"));
-      mips_opts.warn_about_macros = 1;
-    }
+    mips_opts.at = ZERO;
   else if (strcmp (name, "move") == 0 || strcmp (name, "novolatile") == 0)
-    {
-      mips_opts.nomove = 0;
-    }
+    mips_opts.nomove = 0;
   else if (strcmp (name, "nomove") == 0 || strcmp (name, "volatile") == 0)
-    {
-      mips_opts.nomove = 1;
-    }
+    mips_opts.nomove = 1;
   else if (strcmp (name, "bopt") == 0)
-    {
-      mips_opts.nobopt = 0;
-    }
+    mips_opts.nobopt = 0;
   else if (strcmp (name, "nobopt") == 0)
-    {
-      mips_opts.nobopt = 1;
-    }
-  else if (strcmp (name, "gp=default") == 0)
-    mips_opts.gp32 = file_mips_gp32;
+    mips_opts.nobopt = 1;
   else if (strcmp (name, "gp=32") == 0)
     mips_opts.gp32 = 1;
   else if (strcmp (name, "gp=64") == 0)
-    {
-      if (!ISA_HAS_64BIT_REGS (mips_opts.isa))
-	as_warn (_("%s isa does not support 64-bit registers"),
-		 mips_cpu_info_from_isa (mips_opts.isa)->name);
-      mips_opts.gp32 = 0;
-    }
-  else if (strcmp (name, "fp=default") == 0)
-    mips_opts.fp32 = file_mips_fp32;
+    mips_opts.gp32 = 0;
   else if (strcmp (name, "fp=32") == 0)
-    mips_opts.fp32 = 1;
+    mips_opts.fp = 32;
+  else if (strcmp (name, "fp=xx") == 0)
+    mips_opts.fp = 0;
   else if (strcmp (name, "fp=64") == 0)
-    {
-      if (!ISA_HAS_64BIT_FPRS (mips_opts.isa))
-	as_warn (_("%s isa does not support 64-bit floating point registers"),
-		 mips_cpu_info_from_isa (mips_opts.isa)->name);
-      mips_opts.fp32 = 0;
-    }
+    mips_opts.fp = 64;
   else if (strcmp (name, "softfloat") == 0)
     mips_opts.soft_float = 1;
   else if (strcmp (name, "hardfloat") == 0)
@@ -15018,45 +15134,29 @@ s_mipsset (int x ATTRIBUTE_UNUSED)
     mips_opts.single_float = 0;
   else if (strcmp (name, "mips16") == 0
 	   || strcmp (name, "MIPS-16") == 0)
-    {
-      if (mips_opts.micromips == 1)
-	as_fatal (_("`mips16' cannot be used with `micromips'"));
-      mips_opts.mips16 = 1;
-    }
+    mips_opts.mips16 = 1;
   else if (strcmp (name, "nomips16") == 0
 	   || strcmp (name, "noMIPS-16") == 0)
     mips_opts.mips16 = 0;
   else if (strcmp (name, "micromips") == 0)
-    {
-      if (mips_opts.mips16 == 1)
-	as_fatal (_("`micromips' cannot be used with `mips16'"));
-      mips_opts.micromips = 1;
-    }
+    mips_opts.micromips = 1;
   else if (strcmp (name, "nomicromips") == 0)
     mips_opts.micromips = 0;
   else if (name[0] == 'n'
 	   && name[1] == 'o'
 	   && (ase = mips_lookup_ase (name + 2)))
-    mips_set_ase (ase, FALSE);
+    mips_set_ase (ase, &mips_opts, FALSE);
   else if ((ase = mips_lookup_ase (name)))
-    mips_set_ase (ase, TRUE);
+    mips_set_ase (ase, &mips_opts, TRUE);
   else if (strncmp (name, "mips", 4) == 0 || strncmp (name, "arch=", 5) == 0)
     {
-      int reset = 0;
-
       /* Permit the user to change the ISA and architecture on the fly.
 	 Needless to say, misuse can cause serious problems.  */
-      if (strcmp (name, "mips0") == 0 || strcmp (name, "arch=default") == 0)
-	{
-	  reset = 1;
-	  mips_opts.isa = file_mips_isa;
-	  mips_opts.arch = file_mips_arch;
-	}
-      else if (strncmp (name, "arch=", 5) == 0)
+      if (strncmp (name, "arch=", 5) == 0)
 	{
 	  const struct mips_cpu_info *p;
 
-	  p = mips_parse_cpu("internal use", name + 5);
+	  p = mips_parse_cpu ("internal use", name + 5);
 	  if (!p)
 	    as_bad (_("unknown architecture %s"), name + 5);
 	  else
@@ -15069,7 +15169,7 @@ s_mipsset (int x ATTRIBUTE_UNUSED)
 	{
 	  const struct mips_cpu_info *p;
 
-	  p = mips_parse_cpu("internal use", name);
+	  p = mips_parse_cpu ("internal use", name);
 	  if (!p)
 	    as_bad (_("unknown ISA level %s"), name + 4);
 	  else
@@ -15080,42 +15180,6 @@ s_mipsset (int x ATTRIBUTE_UNUSED)
 	}
       else
 	as_bad (_("unknown ISA or architecture %s"), name);
-
-      switch (mips_opts.isa)
-	{
-	case  0:
-	  break;
-	case ISA_MIPS1:
-	case ISA_MIPS2:
-	case ISA_MIPS32:
-	case ISA_MIPS32R2:
-	  mips_opts.gp32 = 1;
-	  mips_opts.fp32 = 1;
-	  break;
-	case ISA_MIPS3:
-	case ISA_MIPS4:
-	case ISA_MIPS5:
-	case ISA_MIPS64:
-	case ISA_MIPS64R2:
-	  mips_opts.gp32 = 0;
-	  if (mips_opts.arch == CPU_R5900)
-	    {
-		mips_opts.fp32 = 1;
-	    }
-	  else
-	    {
-	  mips_opts.fp32 = 0;
-	    }
-	  break;
-	default:
-	  as_bad (_("unknown ISA level %s"), name + 4);
-	  break;
-	}
-      if (reset)
-	{
-	  mips_opts.gp32 = file_mips_gp32;
-	  mips_opts.fp32 = file_mips_fp32;
-	}
     }
   else if (strcmp (name, "autoextend") == 0)
     mips_opts.noautoextend = 0;
@@ -15125,6 +15189,68 @@ s_mipsset (int x ATTRIBUTE_UNUSED)
     mips_opts.insn32 = TRUE;
   else if (strcmp (name, "noinsn32") == 0)
     mips_opts.insn32 = FALSE;
+  else if (strcmp (name, "sym32") == 0)
+    mips_opts.sym32 = TRUE;
+  else if (strcmp (name, "nosym32") == 0)
+    mips_opts.sym32 = FALSE;
+  else
+    return FALSE;
+  return TRUE;
+}
+
+/* Handle the .set pseudo-op.  */
+
+static void
+s_mipsset (int x ATTRIBUTE_UNUSED)
+{
+  char *name = input_line_pointer, ch;
+  int prev_isa = mips_opts.isa;
+
+  file_mips_check_options ();
+
+  while (!is_end_of_line[(unsigned char) *input_line_pointer])
+    ++input_line_pointer;
+  ch = *input_line_pointer;
+  *input_line_pointer = '\0';
+
+  if (strchr (name, ','))
+    {
+      /* Generic ".set" directive; use the generic handler.  */
+      *input_line_pointer = ch;
+      input_line_pointer = name;
+      s_set (0);
+      return;
+    }
+
+  if (strcmp (name, "reorder") == 0)
+    {
+      if (mips_opts.noreorder)
+	end_noreorder ();
+    }
+  else if (strcmp (name, "noreorder") == 0)
+    {
+      if (!mips_opts.noreorder)
+	start_noreorder ();
+    }
+  else if (strcmp (name, "macro") == 0)
+    mips_opts.warn_about_macros = 0;
+  else if (strcmp (name, "nomacro") == 0)
+    {
+      if (mips_opts.noreorder == 0)
+	as_bad (_("`noreorder' must be set before `nomacro'"));
+      mips_opts.warn_about_macros = 1;
+    }
+  else if (strcmp (name, "gp=default") == 0)
+    mips_opts.gp32 = file_mips_opts.gp32;
+  else if (strcmp (name, "fp=default") == 0)
+    mips_opts.fp = file_mips_opts.fp;
+  else if (strcmp (name, "mips0") == 0 || strcmp (name, "arch=default") == 0)
+    {
+      mips_opts.isa = file_mips_opts.isa;
+      mips_opts.arch = file_mips_opts.arch;
+      mips_opts.gp32 = file_mips_opts.gp32;
+      mips_opts.fp = file_mips_opts.fp;
+    }
   else if (strcmp (name, "push") == 0)
     {
       struct mips_option_stack *s;
@@ -15155,23 +15281,87 @@ s_mipsset (int x ATTRIBUTE_UNUSED)
 	  free (s);
 	}
     }
-  else if (strcmp (name, "sym32") == 0)
-    mips_opts.sym32 = TRUE;
-  else if (strcmp (name, "nosym32") == 0)
-    mips_opts.sym32 = FALSE;
-  else if (strchr (name, ','))
+  else if (s_mipssettings (name) == FALSE)
+    as_warn (_("tried to set unrecognized symbol: %s\n"), name);
+
+  /* The use of .set [arch|cpu]= historically 'fixes' the width of gp and fp
+     registers based on what is supported by the arch/cpu.  */
+  if (mips_opts.isa != prev_isa)
     {
-      /* Generic ".set" directive; use the generic handler.  */
-      *input_line_pointer = ch;
-      input_line_pointer = name;
-      s_set (0);
-      return;
+      switch (mips_opts.isa)
+	{
+	case 0:
+	  break;
+	case ISA_MIPS1:
+	case ISA_MIPS2:
+	case ISA_MIPS32:
+	case ISA_MIPS32R2:
+	  mips_opts.gp32 = 1;
+	  if (mips_opts.fp != 0)
+	    mips_opts.fp = 32;
+	  break;
+	case ISA_MIPS3:
+	case ISA_MIPS4:
+	case ISA_MIPS5:
+	case ISA_MIPS64:
+	case ISA_MIPS64R2:
+	  mips_opts.gp32 = 0;
+	  if (mips_opts.arch == CPU_R5900)
+	    {
+	      if (mips_opts.fp != 0)
+		mips_opts.fp = 32;
+	    }
+	  else if (mips_opts.fp != 0)
+	    mips_opts.fp = 64;
+	  break;
+	default:
+	  as_bad (_("unknown ISA level %s"), name + 4);
+	  break;
+	}
     }
-  else
+
+  mips_check_options (&mips_opts, FALSE);
+
+  /* An error may occur if entering fp32 with the overall module as fp64
+     and vice-versa.  Record incompatibilities to report dangerous code
+     generation if it occurs.  */
+  mips_opts.mips_defer_fp_warn = (mips_abi == O32_ABI 
+				  && file_mips_opts.fp != 0 && mips_opts.fp != 0
+				  && mips_opts.fp != file_mips_opts.fp);
+
+  mips_check_isa_supports_ases ();
+  *input_line_pointer = ch;
+  demand_empty_rest_of_line ();
+}
+
+/* Handle the .module pseudo-op.  */
+
+static void
+s_module (int ignore ATTRIBUTE_UNUSED)
+{
+  char *name = input_line_pointer, ch;
+  int prev_arch = file_mips_opts.arch;
+
+  while (!is_end_of_line[(unsigned char) *input_line_pointer])
+    ++input_line_pointer;
+  ch = *input_line_pointer;
+  *input_line_pointer = '\0';
+
+  if (file_mips_opts_checked == FALSE)
     {
-      as_warn (_("tried to set unrecognized symbol: %s\n"), name);
+      if (s_mipssettings (name) == FALSE)
+	as_warn (_(".module used with unrecognized symbol: %s\n"), name);
+
+      /* Update module level settings from mips_opts.  */
+      file_mips_opts = mips_opts;
     }
-  mips_check_isa_supports_ases ();
+  else
+    as_warn (_("ignoring .module after generating code"));
+
+  if (prev_arch != file_mips_opts.arch
+      && ! bfd_set_arch_mach (stdoutput, bfd_arch_mips, file_mips_opts.arch))
+    as_warn (_("could not set architecture and machine"));
+
   *input_line_pointer = ch;
   demand_empty_rest_of_line ();
 }
@@ -15218,6 +15408,8 @@ s_cpload (int ignore ATTRIBUTE_UNUSED)
   int reg;
   int in_shared;
 
+  file_mips_check_options ();
+
   /* If we are not generating SVR4 PIC code, or if this is NewABI code,
      .cpload is ignored.  */
   if (mips_pic != SVR4_PIC || HAVE_NEWABI)
@@ -15295,6 +15487,8 @@ s_cpsetup (int ignore ATTRIBUTE_UNUSED)
   expressionS ex_sym;
   int reg1;
 
+  file_mips_check_options ();
+
   /* If we are not generating SVR4 PIC code, .cpsetup is ignored.
      We also need NewABI support.  */
   if (mips_pic != SVR4_PIC || ! HAVE_NEWABI)
@@ -15398,6 +15592,8 @@ s_cpsetup (int ignore ATTRIBUTE_UNUSED)
 static void
 s_cplocal (int ignore ATTRIBUTE_UNUSED)
 {
+  file_mips_check_options ();
+
   /* If we are not generating SVR4 PIC code, or if this is not NewABI code,
      .cplocal is ignored.  */
   if (mips_pic != SVR4_PIC || ! HAVE_NEWABI)
@@ -15426,6 +15622,8 @@ s_cprestore (int ignore ATTRIBUTE_UNUSED)
 {
   expressionS ex;
 
+  file_mips_check_options ();
+
   /* If we are not generating SVR4 PIC code, or if this is NewABI code,
      .cprestore is ignored.  */
   if (mips_pic != SVR4_PIC || HAVE_NEWABI)
@@ -15473,6 +15671,8 @@ s_cpreturn (int ignore ATTRIBUTE_UNUSED)
 {
   expressionS ex;
 
+  file_mips_check_options ();
+
   /* If we are not generating SVR4 PIC code, .cpreturn is ignored.
      We also need NewABI support.  */
   if (mips_pic != SVR4_PIC || ! HAVE_NEWABI)
@@ -15707,6 +15907,8 @@ s_cpadd (int ignore ATTRIBUTE_UNUSED)
 {
   int reg;
 
+  file_mips_check_options ();
+
   /* This is ignored when not generating SVR4 PIC code.  */
   if (mips_pic != SVR4_PIC)
     {
@@ -17277,6 +17479,62 @@ mips_add_dot_label (symbolS *sym)
 void
 mips_elf_final_processing (void)
 {
+  int fpabi;
+  Elf_Internal_ABIFlags_v0 flags;
+
+  flags.version = 0;
+  flags.isa_rev = 0;
+  switch (file_mips_opts.isa)
+    {
+    case INSN_ISA1:
+      flags.isa_level = 1;
+      break;
+    case INSN_ISA2:
+      flags.isa_level = 2;
+      break;
+    case INSN_ISA3:
+      flags.isa_level = 3;
+      break;
+    case INSN_ISA4:
+      flags.isa_level = 4;
+      break;
+    case INSN_ISA5:
+      flags.isa_level = 5;
+      break;
+    case INSN_ISA32:
+      flags.isa_level = 32;
+      flags.isa_rev = 1;
+      break;
+    case INSN_ISA32R2:
+      flags.isa_level = 32;
+      flags.isa_rev = 2;
+      break;
+    case INSN_ISA64:
+      flags.isa_level = 64;
+      flags.isa_rev = 1;
+      break;
+    case INSN_ISA64R2:
+      flags.isa_level = 64;
+      flags.isa_rev = 2;
+      break;
+    }
+
+  flags.gpr_size = (file_mips_opts.gp32 ? AFL_GPR_32 : AFL_GPR_64);
+  flags.fp_abi = bfd_elf_get_obj_attr_int (stdoutput, OBJ_ATTR_GNU,
+                                           Tag_GNU_MIPS_ABI_FP);
+  flags.isa_ext = cpu_insn_mask (file_mips_opts.arch);
+  flags.ases = file_mips_opts.ase;
+  if (file_ase_mips16)
+    flags.ases |= AFL_ASE_MIPS16;
+  if (file_ase_micromips)
+    flags.ases |= AFL_ASE_MICROMIPS;
+  flags.flags1 = 0;
+  flags.flags2 = 0;
+
+  bfd_mips_elf_swap_abiflags_v0_out (stdoutput, &flags,
+				     ((Elf_External_ABIFlags_v0 *)
+				     mips_flags_frag));
+
   /* Write out the register information.  */
   if (mips_abi != N64_ABI)
     {
@@ -17328,7 +17586,7 @@ mips_elf_final_processing (void)
     elf_elfheader (stdoutput)->e_flags |= EF_MIPS_ARCH_ASE_M16;
   if (file_ase_micromips)
     elf_elfheader (stdoutput)->e_flags |= EF_MIPS_ARCH_ASE_MICROMIPS;
-  if (file_ase & ASE_MDMX)
+  if (file_ase & AFL_ASE_MDMX)
     elf_elfheader (stdoutput)->e_flags |= EF_MIPS_ARCH_ASE_MDMX;
 
   /* Set the MIPS ELF ABI flags.  */
@@ -17338,7 +17596,7 @@ mips_elf_final_processing (void)
     elf_elfheader (stdoutput)->e_flags |= E_MIPS_ABI_O64;
   else if (mips_abi == EABI_ABI)
     {
-      if (!file_mips_gp32)
+      if (!file_mips_opts.gp32)
 	elf_elfheader (stdoutput)->e_flags |= E_MIPS_ABI_EABI64;
       else
 	elf_elfheader (stdoutput)->e_flags |= E_MIPS_ABI_EABI32;
@@ -17355,7 +17613,9 @@ mips_elf_final_processing (void)
     elf_elfheader (stdoutput)->e_flags |= EF_MIPS_NAN2008;
 
   /* 32 bit code with 64 bit FP registers.  */
-  if (!file_mips_fp32 && ABI_NEEDS_32BIT_REGS (mips_abi))
+  fpabi = bfd_elf_get_obj_attr_int (stdoutput, OBJ_ATTR_GNU,
+				    Tag_GNU_MIPS_ABI_FP);
+  if (fpabi == Val_GNU_MIPS_ABI_FP_OLD_64)
     elf_elfheader (stdoutput)->e_flags |= EF_MIPS_FP64;
 }
 

@@ -17470,6 +17730,9 @@ md_obj_end (void)
   /* Check for premature end, nesting errors, etc.  */
   if (cur_proc_ptr)
     as_warn (_("missing .end at end of assembly"));
+
+  /* Just in case no code was emitted, do the consistency check.  */
+  file_mips_check_options ();
 }
 
 static long
@@ -17856,20 +18119,20 @@ static const struct mips_cpu_info mips_cpu_info_table[] =
   { "4kc",            0, 0,			ISA_MIPS32,   CPU_MIPS32 },
   { "4km",            0, 0,			ISA_MIPS32,   CPU_MIPS32 },
   { "4kp",            0, 0,			ISA_MIPS32,   CPU_MIPS32 },
-  { "4ksc",           0, ASE_SMARTMIPS,		ISA_MIPS32,   CPU_MIPS32 },
+  { "4ksc",           0, AFL_ASE_SMARTMIPS,	ISA_MIPS32,   CPU_MIPS32 },
 
   /* MIPS 32 Release 2 */
   { "4kec",           0, 0,			ISA_MIPS32R2, CPU_MIPS32R2 },
   { "4kem",           0, 0,			ISA_MIPS32R2, CPU_MIPS32R2 },
   { "4kep",           0, 0,			ISA_MIPS32R2, CPU_MIPS32R2 },
-  { "4ksd",           0, ASE_SMARTMIPS,		ISA_MIPS32R2, CPU_MIPS32R2 },
+  { "4ksd",           0, AFL_ASE_SMARTMIPS,	ISA_MIPS32R2, CPU_MIPS32R2 },
   { "m4k",            0, 0,			ISA_MIPS32R2, CPU_MIPS32R2 },
   { "m4kp",           0, 0,			ISA_MIPS32R2, CPU_MIPS32R2 },
-  { "m14k",           0, ASE_MCU,		ISA_MIPS32R2, CPU_MIPS32R2 },
-  { "m14kc",          0, ASE_MCU,		ISA_MIPS32R2, CPU_MIPS32R2 },
-  { "m14ke",          0, ASE_DSP | ASE_DSPR2 | ASE_MCU,
+  { "m14k",           0, AFL_ASE_MCU,		ISA_MIPS32R2, CPU_MIPS32R2 },
+  { "m14kc",          0, AFL_ASE_MCU,		ISA_MIPS32R2, CPU_MIPS32R2 },
+  { "m14ke",          0, AFL_ASE_DSP | AFL_ASE_DSPR2 | AFL_ASE_MCU,
 						ISA_MIPS32R2, CPU_MIPS32R2 },
-  { "m14kec",         0, ASE_DSP | ASE_DSPR2 | ASE_MCU,
+  { "m14kec",         0, AFL_ASE_DSP | AFL_ASE_DSPR2 | AFL_ASE_MCU,
 						ISA_MIPS32R2, CPU_MIPS32R2 },
   { "24kc",           0, 0,			ISA_MIPS32R2, CPU_MIPS32R2 },
   { "24kf2_1",        0, 0,			ISA_MIPS32R2, CPU_MIPS32R2 },
@@ -17879,50 +18142,70 @@ static const struct mips_cpu_info mips_cpu_info_table[] =
   { "24kfx",          0, 0,			ISA_MIPS32R2, CPU_MIPS32R2 },
   { "24kx",           0, 0,			ISA_MIPS32R2, CPU_MIPS32R2 },
   /* 24KE is a 24K with DSP ASE, other ASEs are optional.  */
-  { "24kec",          0, ASE_DSP,		ISA_MIPS32R2, CPU_MIPS32R2 },
-  { "24kef2_1",       0, ASE_DSP,		ISA_MIPS32R2, CPU_MIPS32R2 },
-  { "24kef",          0, ASE_DSP,		ISA_MIPS32R2, CPU_MIPS32R2 },
-  { "24kef1_1",       0, ASE_DSP,		ISA_MIPS32R2, CPU_MIPS32R2 },
+  { "24kec",          0, AFL_ASE_DSP,		ISA_MIPS32R2, CPU_MIPS32R2 },
+  { "24kef2_1",       0, AFL_ASE_DSP,		ISA_MIPS32R2, CPU_MIPS32R2 },
+  { "24kef",          0, AFL_ASE_DSP,		ISA_MIPS32R2, CPU_MIPS32R2 },
+  { "24kef1_1",       0, AFL_ASE_DSP,		ISA_MIPS32R2, CPU_MIPS32R2 },
   /* Deprecated forms of the above.  */
-  { "24kefx",         0, ASE_DSP,		ISA_MIPS32R2, CPU_MIPS32R2 },
-  { "24kex",          0, ASE_DSP,		ISA_MIPS32R2, CPU_MIPS32R2 },
+  { "24kefx",         0, AFL_ASE_DSP,		ISA_MIPS32R2, CPU_MIPS32R2 },
+  { "24kex",          0, AFL_ASE_DSP,		ISA_MIPS32R2, CPU_MIPS32R2 },
   /* 34K is a 24K with DSP and MT ASE, other ASEs are optional.  */
-  { "34kc",           0, ASE_DSP | ASE_MT,	ISA_MIPS32R2, CPU_MIPS32R2 },
-  { "34kf2_1",        0, ASE_DSP | ASE_MT,	ISA_MIPS32R2, CPU_MIPS32R2 },
-  { "34kf",           0, ASE_DSP | ASE_MT,	ISA_MIPS32R2, CPU_MIPS32R2 },
-  { "34kf1_1",        0, ASE_DSP | ASE_MT,	ISA_MIPS32R2, CPU_MIPS32R2 },
+  { "34kc",           0, AFL_ASE_DSP | AFL_ASE_MT,
+						ISA_MIPS32R2, CPU_MIPS32R2 },
+  { "34kf2_1",        0, AFL_ASE_DSP | AFL_ASE_MT,
+						ISA_MIPS32R2, CPU_MIPS32R2 },
+  { "34kf",           0, AFL_ASE_DSP | AFL_ASE_MT,
+						ISA_MIPS32R2, CPU_MIPS32R2 },
+  { "34kf1_1",        0, AFL_ASE_DSP | AFL_ASE_MT,
+						ISA_MIPS32R2, CPU_MIPS32R2 },
   /* Deprecated forms of the above.  */
-  { "34kfx",          0, ASE_DSP | ASE_MT,	ISA_MIPS32R2, CPU_MIPS32R2 },
-  { "34kx",           0, ASE_DSP | ASE_MT,	ISA_MIPS32R2, CPU_MIPS32R2 },
+  { "34kfx",          0, AFL_ASE_DSP | AFL_ASE_MT,
+						ISA_MIPS32R2, CPU_MIPS32R2 },
+  { "34kx",           0, AFL_ASE_DSP | AFL_ASE_MT,
+						ISA_MIPS32R2, CPU_MIPS32R2 },
   /* 34Kn is a 34kc without DSP.  */
-  { "34kn",           0, ASE_MT,		ISA_MIPS32R2, CPU_MIPS32R2 },
+  { "34kn",           0, AFL_ASE_MT,		ISA_MIPS32R2, CPU_MIPS32R2 },
   /* 74K with DSP and DSPR2 ASE, other ASEs are optional.  */
-  { "74kc",           0, ASE_DSP | ASE_DSPR2,	ISA_MIPS32R2, CPU_MIPS32R2 },
-  { "74kf2_1",        0, ASE_DSP | ASE_DSPR2,	ISA_MIPS32R2, CPU_MIPS32R2 },
-  { "74kf",           0, ASE_DSP | ASE_DSPR2,	ISA_MIPS32R2, CPU_MIPS32R2 },
-  { "74kf1_1",        0, ASE_DSP | ASE_DSPR2,	ISA_MIPS32R2, CPU_MIPS32R2 },
-  { "74kf3_2",        0, ASE_DSP | ASE_DSPR2,	ISA_MIPS32R2, CPU_MIPS32R2 },
+  { "74kc",           0, AFL_ASE_DSP | AFL_ASE_DSPR2,
+						ISA_MIPS32R2, CPU_MIPS32R2 },
+  { "74kf2_1",        0, AFL_ASE_DSP | AFL_ASE_DSPR2,
+						ISA_MIPS32R2, CPU_MIPS32R2 },
+  { "74kf",           0, AFL_ASE_DSP | AFL_ASE_DSPR2,
+						ISA_MIPS32R2, CPU_MIPS32R2 },
+  { "74kf1_1",        0, AFL_ASE_DSP | AFL_ASE_DSPR2,
+						ISA_MIPS32R2, CPU_MIPS32R2 },
+  { "74kf3_2",        0, AFL_ASE_DSP | AFL_ASE_DSPR2,
+						ISA_MIPS32R2, CPU_MIPS32R2 },
   /* Deprecated forms of the above.  */
-  { "74kfx",          0, ASE_DSP | ASE_DSPR2,	ISA_MIPS32R2, CPU_MIPS32R2 },
-  { "74kx",           0, ASE_DSP | ASE_DSPR2,	ISA_MIPS32R2, CPU_MIPS32R2 },
+  { "74kfx",          0, AFL_ASE_DSP | AFL_ASE_DSPR2,
+						ISA_MIPS32R2, CPU_MIPS32R2 },
+  { "74kx",           0, AFL_ASE_DSP | AFL_ASE_DSPR2,
+						ISA_MIPS32R2, CPU_MIPS32R2 },
   /* 1004K cores are multiprocessor versions of the 34K.  */
-  { "1004kc",         0, ASE_DSP | ASE_MT,	ISA_MIPS32R2, CPU_MIPS32R2 },
-  { "1004kf2_1",      0, ASE_DSP | ASE_MT,	ISA_MIPS32R2, CPU_MIPS32R2 },
-  { "1004kf",         0, ASE_DSP | ASE_MT,	ISA_MIPS32R2, CPU_MIPS32R2 },
-  { "1004kf1_1",      0, ASE_DSP | ASE_MT,	ISA_MIPS32R2, CPU_MIPS32R2 },
+  { "1004kc",         0, AFL_ASE_DSP | AFL_ASE_MT,
+						ISA_MIPS32R2, CPU_MIPS32R2 },
+  { "1004kf2_1",      0, AFL_ASE_DSP | AFL_ASE_MT,
+						ISA_MIPS32R2, CPU_MIPS32R2 },
+  { "1004kf",         0, AFL_ASE_DSP | AFL_ASE_MT,
+						ISA_MIPS32R2, CPU_MIPS32R2 },
+  { "1004kf1_1",      0, AFL_ASE_DSP | AFL_ASE_MT,
+						ISA_MIPS32R2, CPU_MIPS32R2 },
   /* P5600 with EVA and Virtualization ASEs, other ASEs are optional.  */
-  { "p5600",          0, ASE_VIRT | ASE_EVA | ASE_XPA, 	ISA_MIPS32R2, CPU_MIPS32R2 },
+  { "p5600",          0, AFL_ASE_VIRT | AFL_ASE_EVA | AFL_ASE_XPA,
+						ISA_MIPS32R2, CPU_MIPS32R2 },
 
   /* MIPS 64 */
   { "5kc",            0, 0,			ISA_MIPS64,   CPU_MIPS64 },
   { "5kf",            0, 0,			ISA_MIPS64,   CPU_MIPS64 },
-  { "20kc",           0, ASE_MIPS3D,		ISA_MIPS64,   CPU_MIPS64 },
-  { "25kf",           0, ASE_MIPS3D,		ISA_MIPS64,   CPU_MIPS64 },
+  { "20kc",           0, AFL_ASE_MIPS3D,	ISA_MIPS64,   CPU_MIPS64 },
+  { "25kf",           0, AFL_ASE_MIPS3D,	ISA_MIPS64,   CPU_MIPS64 },
 
   /* Broadcom SB-1 CPU core */
-  { "sb1",            0, ASE_MIPS3D | ASE_MDMX,	ISA_MIPS64,   CPU_SB1 },
+  { "sb1",            0, AFL_ASE_MIPS3D | AFL_ASE_MDMX,
+						ISA_MIPS64,   CPU_SB1 },
   /* Broadcom SB-1A CPU core */
-  { "sb1a",           0, ASE_MIPS3D | ASE_MDMX,	ISA_MIPS64,   CPU_SB1 },
+  { "sb1a",           0, AFL_ASE_MIPS3D | AFL_ASE_MDMX,
+						ISA_MIPS64,   CPU_SB1 },
   
   { "loongson3a",     0, 0,			ISA_MIPS64R2, CPU_LOONGSON_3A },
 
@@ -18025,8 +18308,8 @@ mips_parse_cpu (const char *option, const char *cpu_string)
       if (ABI_NEEDS_64BIT_REGS (mips_abi))
 	return mips_cpu_info_from_isa (ISA_MIPS3);
 
-      if (file_mips_gp32 >= 0)
-	return mips_cpu_info_from_isa (file_mips_gp32 ? ISA_MIPS1 : ISA_MIPS3);
+      if (file_mips_opts.gp32 >= 0)
+	return mips_cpu_info_from_isa (file_mips_opts.gp32 ? ISA_MIPS1 : ISA_MIPS3);
 
       return mips_cpu_info_from_isa (MIPS_DEFAULT_64BIT
 				     ? ISA_MIPS3
@@ -18272,3 +18555,33 @@ tc_mips_regname_to_dw2regnum (char *regname)
 
   return regnum;
 }
+
+/* Given a symbolic attribute NAME, return the proper integer value.
+   Returns -1 if the attribute is not known.  */
+
+int
+mips_convert_symbolic_attribute (const char *name)
+{
+  static const struct
+  {
+    const char * name;
+    const int    tag;
+  }
+  attribute_table[] =
+    {
+#define T(tag) {#tag, tag}
+      T (Tag_GNU_MIPS_ABI_FP),
+      T (Tag_GNU_MIPS_ABI_MSA),
+#undef T
+    };
+  unsigned int i;
+
+  if (name == NULL)
+    return -1;
+
+  for (i = 0; i < ARRAY_SIZE (attribute_table); i++)
+    if (streq (name, attribute_table[i].name))
+      return attribute_table[i].tag;
+
+  return -1;
+}
diff --git a/gas/config/tc-mips.h b/gas/config/tc-mips.h
index 510e811..0a07f3a 100644
--- a/gas/config/tc-mips.h
+++ b/gas/config/tc-mips.h
@@ -194,4 +194,9 @@ extern int tc_mips_regname_to_dw2regnum (char *regname);
    64-bit form for n64 CFIs.  */
 #define CFI_DIFF_EXPR_OK 0
 
+#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)
+#define CONVERT_SYMBOLIC_ATTRIBUTE(name) mips_convert_symbolic_attribute (name)
+extern int mips_convert_symbolic_attribute (const char *);
+#endif
+
 #endif /* TC_MIPS */
diff --git a/gas/doc/c-mips.texi b/gas/doc/c-mips.texi
index 0c5e82d..37926d4 100644
--- a/gas/doc/c-mips.texi
+++ b/gas/doc/c-mips.texi
@@ -28,6 +28,7 @@ Assembly Language Programming'' in the same work.
 * MIPS assembly options:: Directives to control code generation
 * MIPS autoextend::	Directives for extending MIPS 16 bit instructions
 * MIPS insn::		Directive to mark data as an instruction
+* MIPS FP ABIs::	Marking which FP ABI is in use
 * MIPS NaN Encodings::	Directives to record which NaN encoding is being used
 * MIPS Option Stack::	Directives to save and restore options
 * MIPS ASE Instruction Generation Overrides:: Directives to control
@@ -119,6 +120,15 @@ The @code{.set gp=64} and @code{.set fp=64} directives allow the size
 of registers to be changed for parts of an object. The default value is
 restored by @code{.set gp=default} and @code{.set fp=default}.
 
+@item -mfpxx
+Make no assumptions about whether 32-bit or 64-bit registers are available.
+This is provided to support having modules compatible with either
+@samp{-mfp32} or @samp{-mfp64}. This option can only be used with MIPS II
+and above.
+
+The @code{.set fp=xx} directive allows a part of an object to be marked
+as not making assumptions about 32-bit or 64-bita FP registers.  The
+default value is restored by @code{.set fp=default}.
 @item -mips16
 @itemx -no-mips16
 Generate code for the MIPS 16 processor.  This is equivalent to putting
@@ -687,6 +697,22 @@ Traditional MIPS assemblers do not support this directive.
 @node MIPS assembly options
 @section Directives to control code generation
 
+@cindex MIPS directives to override command line options
+@kindex @code{.module}
+The @code{.module} directive allows command line options to be set directly
+from assembly.  The format of the directive matches the @code{.set}
+directive but only those options which are relevant to a whole module are
+supported.  The effect of a @code{.module} directive is the same as the
+corresponding command line option.  Where @code{.set} directives support
+returning to a default then the @code{.module} directives do not as they
+define the defaults.
+
+These module level directives must appear first in assembly and will raise
+a warning if found after the first instruction, @code{.set} directive or
+any code generating directive.
+
+Traditional MIPS assemblers do not support this directive.
+
 @cindex MIPS 32-bit microMIPS instruction generation override
 @kindex @code{.set insn32}
 @kindex @code{.set noinsn32}
@@ -749,6 +775,108 @@ baz:
 
 @end example
 
+@node MIPS FP ABIs
+@section Directives to control the FP ABI
+@menu
+* MIPS FP ABI History::                History of FP ABIs
+* MIPS FP ABI Variants::               Supported FP ABIs
+* MIPS FP ABI Selection::              Automatic selection of FP ABI
+* MIPS FP ABI Compatibility::          Linking different FP ABI variants
+@end menu
+
+@node MIPS FP ABI History
+@subsection History of FP ABIs
+@cindex @code{.gnu_attribute 4, @var{n}} directive, MIPS
+@cindex @code{.gnu_attribute Tag_GNU_MIPS_ABI_FP, @var{n}} directive, MIPS
+The MIPS ABIs support a variety of different floating-point extensions
+where calling-convention and register sizes vary for floating-point data.
+The extensions exist to support a wide variety of optional architecture
+features.  The resulting ABI variants are generally incompatible with each
+other and must be tracked carefully.
+
+Traditionally the use of an explicit @code{.gnu_attribute 4, @var{n}}
+directive is used to indicate which ABI is in use by a specific module.
+It was then left to the user to ensure that command line options and the
+selected ABI were compatible with some potential for inconsistencies.
+
+@node MIPS FP ABI Variants
+@subsection Supported FP ABIs
+The supported floating-point ABI variants are:
+
+@table @code
+@item 0 - No floating-point
+This variant is used to indicate that floating-point is not used within
+the module at all and therefore has no impact on the ABI.  This is the
+default.
+
+@item 1 - Double-precision
+This variant indicates that double-precision support is used.  For 64-bit
+ABIs this means that 64-bit wide floating-point registers are required.
+For 32-bit ABIs this means that 32-bit wide floating-point registers are
+required and double precision operations across pairs of registers.
+
+@item 2 - Single-precision
+This variant indicates that single-precision support is used.  This is
+generally taken to mean that the ABI is also modified such that
+sizeof (double) == sizeof (float).  This has an impact on calling
+convention and callee-save behaviour.
+
+@item 3 - Soft-float
+This variant indicates that although floating-point support is used all
+operations are emulated in software.  This means the ABI is modified to
+pass all floating-point data in general-purpose registers.
+
+@item 4 - Deprecated
+This variant existed as an initial attempt at supporting 64-bit wide
+floating-point registers for O32 ABI on a MIPS32r2 cpu.  This has been
+superceded by @value{5} and @value{6}.
+
+@item 5 - Double-precision 32-bit CPU, 32-bit or 64-bit FPU
+This variant is used by 32-bit ABIs to indicate that the floating-point
+code in the module has been designed to operate correctly with either
+32-bit wide or 64-bit wide floating-point registers.  Double precision
+support is used.  Only O32 currently supports this variant and requires
+a minimum architecture of MIPS II.
+
+@item 6 - Double-precision 32-bit FPU, 64-bit FPU
+This variant is used by 32-bit ABIs to indicate that the floating-point
+code in the module requires 64-bit wide floating-point registers.
+Double precision support is used.  Only O32 currently supports this
+variant and requires a minimum architecture of MIPS32r2.
+@end table
+
+@node MIPS FP ABI Selection
+@subsection Automatic selection of FP ABI
+@cindex @code{.module fp=@var{nn}} directive, MIPS
+In order to simplify and add safety to the process of selecting the
+correct floating-point ABI, the assembler will automatically infer the
+correct @code{.gnu_attribute 4, @var{n}} directive based on command line
+options @code{.module} overrides and instruction usage.  Where an explicit
+@code{.gnu_attribute 4, @var{n}} directive has been seen then a warning
+will be raised if it does not match an inferred setting.
+
+The floating-point ABI is inferred as follows.  If @samp{-msoft-float}
+has been used the module will be marked as soft-float.  The hard-float
+ABIs are then only inferred if a floating point instruction is seen.
+Firstly, if @samp{-msingle-float} has been used then the module will
+be marked as single-precision.  The remaining ABIs are selected based
+on the FP register width.  Double-precision is selected if the width
+of GP and FP registers match and the special double precision variants
+for 32-bit ABIs are then selected depending on @samp{-mfpxx} and
+@samp{-mfp64}.
+
+@node MIPS FP ABI Compatibility
+@subsection Linking different FP ABI variants
+Modules using the default FP ABI (no floating-point) can be linked with
+any other (singular) FP ABI variant.
+
+Special compatibility support exists for O32 with the three
+double-precision FP ABI variants.  The @samp{-mfpxx} FP ABI is explicitly
+designed to be compatible with both the standard double-precision ABI and
+the @samp{-mfp64} FP ABI.  This makes it desirable for O32 modules to be
+built as @samp{-mfpxx} to ensure the maximum compatibility with other
+modules produced for more specific needs.
+
 @node MIPS NaN Encodings
 @section Directives to record which NaN encoding is being used
 
diff --git a/include/elf/mips.h b/include/elf/mips.h
index 2949629..85ed0e9 100644
--- a/include/elf/mips.h
+++ b/include/elf/mips.h
@@ -428,6 +428,8 @@ END_RELOC_NUMBERS (R_MIPS_maxext)
 /* Runtime procedure descriptor table exception information (ucode) ??? */
 #define SHT_MIPS_PDR_EXCEPTION	0x70000029
 
+/* ABI related flags section.  */
+#define SHT_MIPS_ABIFLAGS	0x7000002a
 
 /* A section of type SHT_MIPS_LIBLIST contains an array of the
    following structure.  The sh_link field is the section index of the
@@ -593,6 +595,9 @@ extern void bfd_mips_elf32_swap_reginfo_out
 
 /* .MIPS.options section.  */
 #define PT_MIPS_OPTIONS		0x70000002
+
+/* Records ABI related flags.  */
+#define PT_MIPS_ABIFLAGS	0x70000003
 

 /* Processor specific dynamic array tags.  */
 
@@ -1048,6 +1053,50 @@ typedef struct
   bfd_vma ri_gp_value;
 } Elf64_Internal_RegInfo;
 
+/* ABI Flags structure version 0.  */
+
+typedef struct
+{
+  /* Version of flags structure.  */
+  unsigned char version[4];
+  /* The level of the ISA: 1-5, 32, 64.  */
+  unsigned char isa_level[1];
+  /* The revision of ISA: 0 for MIPS V and below, 1-n otherwise.  */
+  unsigned char isa_rev[1];
+  /* The size of general purpose registers.  */
+  unsigned char gpr_size[1];
+  /* The floating-point ABI.  */
+  unsigned char fp_abi[1];
+  /* Mask of processor-specific extensions.  */
+  unsigned char isa_ext[4];
+  /* Mask of ASEs used.  */
+  unsigned char ases[4];
+  /* Mask of general flags.  */
+  unsigned char flags1[4];
+  unsigned char flags2[4];
+} Elf_External_ABIFlags_v0;
+
+typedef struct
+{
+  /* Version of flags structure.  */
+  unsigned long version;
+  /* The level of the ISA: 1-5, 32, 64.  */
+  unsigned char isa_level;
+  /* The revision of ISA: 0 for MIPS V and below, 1-n otherwise.  */
+  unsigned char isa_rev;
+  /* The size of general purpose registers.  */
+  unsigned char gpr_size;
+  /* The floating-point ABI.  */
+  unsigned char fp_abi;
+  /* Mask of processor-specific extensions.  */
+  unsigned long isa_ext;
+  /* Mask of ASEs used.  */
+  unsigned long ases;
+  /* Mask of general flags.  */
+  unsigned long flags1;
+  unsigned long flags2;
+} Elf_Internal_ABIFlags_v0;
+
 typedef struct
 {
   /* The hash value computed from the name of the corresponding
@@ -1088,6 +1137,12 @@ extern void bfd_mips_elf64_swap_reginfo_in
 extern void bfd_mips_elf64_swap_reginfo_out
   (bfd *, const Elf64_Internal_RegInfo *, Elf64_External_RegInfo *);
 
+/* MIPS ELF flags swapping routines.  */
+extern void bfd_mips_elf_swap_abiflags_v0_in
+  (bfd *, const Elf_External_ABIFlags_v0 *, Elf_Internal_ABIFlags_v0 *);
+extern void bfd_mips_elf_swap_abiflags_v0_out
+  (bfd *, const Elf_Internal_ABIFlags_v0 *, Elf_External_ABIFlags_v0 *);
+
 /* Masks for the info work of an ODK_EXCEPTIONS descriptor.  */
 #define OEX_FPU_MIN	0x1f	/* FPEs which must be enabled.  */
 #define OEX_FPU_MAX	0x1f00	/* FPEs which may be enabled.  */
@@ -1125,6 +1180,51 @@ extern void bfd_mips_elf64_swap_reginfo_out
 /* Masks for the info word of an ODK_HWAND/ODK_HWOR descriptor.  */
 #define OHWA0_R4KEOP_CHECKED	0x00000001
 #define OHWA0_R4KEOP_CLEAN	0x00000002
+
+/* Values for the gpr_size byte of an abi flags structure.  */
+
+#define AFL_GPR_32	     0x01	/* 32-bit GPRs.  */
+#define AFL_GPR_64	     0x02	/* 64-bit GPRs.  */
+
+/* Masks for the ases word of an ABI flags structure.  */
+
+#define AFL_ASE_DSP          0x00000001 /* DSP ASE.  */
+#define AFL_ASE_DSP64        0x00000002 /* DSP ASE (64-bit).  */
+#define AFL_ASE_DSPR2        0x00000004 /* DSP R2 ASE.  */
+#define AFL_ASE_EVA          0x00000008 /* Enhanced VA Scheme.  */
+#define AFL_ASE_MCU          0x00000010 /* MCU (MicroController) ASE.  */
+#define AFL_ASE_MDMX         0x00000020 /* MDMX ASE.  */
+#define AFL_ASE_MIPS3D       0x00000040 /* MIPS-3D ASE.  */
+#define AFL_ASE_MT           0x00000080 /* MT ASE.  */
+#define AFL_ASE_SMARTMIPS    0x00000100 /* SmartMIPS ASE.  */
+#define AFL_ASE_VIRT         0x00000200 /* VZ ASE.  */
+#define AFL_ASE_VIRT64       0x00000400 /* VZ ASE (64-bit).  */
+#define AFL_ASE_MSA          0x00000800 /* MSA ASE.  */
+#define AFL_ASE_MSA64        0x00001000 /* MSA ASE (64-bit).  */
+#define AFL_ASE_MIPS16       0x00002000 /* MIPS16 ASE.  */
+#define AFL_ASE_MICROMIPS    0x00004000 /* MICROMIPS ASE.  */
+#define AFL_ASE_XPA	     0x00002000 /* XPA ASE.  */
+
+/* Masks for the isa_ext word of an ABI flags structure.  */
+
+#define AFL_EXT_XLR          0x00000020 /* RMI Xlr instruction.  */
+#define AFL_EXT_OCTEON2      0x00000100 /* Cavium Networks Octeon2.  */
+#define AFL_EXT_OCTEONP      0x00000200 /* Cavium Networks OcteonP.  */
+#define AFL_EXT_LOONGSON_3A  0x00000400 /* Loongson 3A.  */
+#define AFL_EXT_OCTEON       0x00000800 /* Cavium Networks Octeon.  */
+#define AFL_EXT_5900         0x00004000 /* MIPS R5900 instruction.  */
+#define AFL_EXT_4650         0x00010000 /* MIPS R4650 instruction.  */
+#define AFL_EXT_4010         0x00020000 /* LSI R4010 instruction.  */
+#define AFL_EXT_4100         0x00040000 /* NEC VR4100 instruction.  */
+#define AFL_EXT_3900         0x00080000 /* Toshiba R3900 instruction.  */
+#define AFL_EXT_10000        0x00100000 /* MIPS R10000 instruction.  */
+#define AFL_EXT_SB1          0x00200000 /* Broadcom SB-1 instruction.  */
+#define AFL_EXT_4111         0x00400000 /* NEC VR4111/VR4181 instruction.  */
+#define AFL_EXT_4120         0x00800000 /* NEC VR4120 instruction.  */
+#define AFL_EXT_5400         0x01000000 /* NEC VR5400 instruction.  */
+#define AFL_EXT_5500         0x02000000 /* NEC VR5500 instruction.  */
+#define AFL_EXT_LOONGSON_2E  0x40000000 /* ST Microelectronics Loongson 2E.  */
+#define AFL_EXT_LOONGSON_2F  0x80000000 /* ST Microelectronics Loongson 2F.  */
 

 
 /* Object attribute tags.  */
@@ -1157,7 +1257,13 @@ enum
   Val_GNU_MIPS_ABI_FP_SOFT = 3,
 
   /* Using -mips32r2 -mfp64.  */
-  Val_GNU_MIPS_ABI_FP_64 = 4,
+  Val_GNU_MIPS_ABI_FP_OLD_64 = 4,
+
+  /* Using -mfpxx */
+  Val_GNU_MIPS_ABI_FP_XX = 5,
+
+  /* Using -mips32r2 -mfp64.  */
+  Val_GNU_MIPS_ABI_FP_64 = 6,
 
   /* Values defined for Tag_GNU_MIPS_ABI_MSA.  */
 
diff --git a/include/opcode/mips.h b/include/opcode/mips.h
index a5d2935..ccd7e06 100644
--- a/include/opcode/mips.h
+++ b/include/opcode/mips.h
@@ -24,6 +24,7 @@
 #define _MIPS_H_
 
 #include "bfd.h"
+#include "elf/mips.h"
 
 /* These are bit masks and shift counts to use to access the various
    fields of an instruction.  To retrieve the X field of an
@@ -1091,73 +1092,6 @@ struct mips_opcode
 static const unsigned int mips_isa_table[] =
   { 0x0001, 0x0003, 0x0607, 0x1e0f, 0x3e1f, 0x0a23, 0x3e63, 0x3ebf, 0x3fff };
 
-/* Masks used for Chip specific instructions.  */
-#define INSN_CHIP_MASK		  0xc3ff0f20
-
-/* Cavium Networks Octeon instructions.  */
-#define INSN_OCTEON		  0x00000800
-#define INSN_OCTEONP		  0x00000200
-#define INSN_OCTEON2		  0x00000100
-
-/* MIPS R5900 instruction */
-#define INSN_5900                 0x00004000
-
-/* MIPS R4650 instruction.  */
-#define INSN_4650                 0x00010000
-/* LSI R4010 instruction.  */
-#define INSN_4010                 0x00020000
-/* NEC VR4100 instruction.  */
-#define INSN_4100                 0x00040000
-/* Toshiba R3900 instruction.  */
-#define INSN_3900                 0x00080000
-/* MIPS R10000 instruction.  */
-#define INSN_10000                0x00100000
-/* Broadcom SB-1 instruction.  */
-#define INSN_SB1                  0x00200000
-/* NEC VR4111/VR4181 instruction.  */
-#define INSN_4111                 0x00400000
-/* NEC VR4120 instruction.  */
-#define INSN_4120                 0x00800000
-/* NEC VR5400 instruction.  */
-#define INSN_5400		  0x01000000
-/* NEC VR5500 instruction.  */
-#define INSN_5500		  0x02000000
-
-/* ST Microelectronics Loongson 2E.  */
-#define INSN_LOONGSON_2E          0x40000000
-/* ST Microelectronics Loongson 2F.  */
-#define INSN_LOONGSON_2F          0x80000000
-/* Loongson 3A.  */
-#define INSN_LOONGSON_3A          0x00000400
-/* RMI Xlr instruction */
-#define INSN_XLR                 0x00000020
-
-/* DSP ASE */
-#define ASE_DSP			0x00000001
-#define ASE_DSP64		0x00000002
-/* DSP R2 ASE  */
-#define ASE_DSPR2		0x00000004
-/* Enhanced VA Scheme */
-#define ASE_EVA			0x00000008
-/* MCU (MicroController) ASE */
-#define ASE_MCU			0x00000010
-/* MDMX ASE */
-#define ASE_MDMX		0x00000020
-/* MIPS-3D ASE */
-#define ASE_MIPS3D		0x00000040
-/* MT ASE */
-#define ASE_MT			0x00000080
-/* SmartMIPS ASE  */
-#define ASE_SMARTMIPS		0x00000100
-/* Virtualization ASE */
-#define ASE_VIRT		0x00000200
-#define ASE_VIRT64		0x00000400
-/* MSA Extension  */
-#define ASE_MSA			0x00000800
-#define ASE_MSA64		0x00001000
-/* eXtended Physical Address (XPA) Extension.  */
-#define ASE_XPA			0x00002000
-
 /* MIPS ISA defines, use instead of hardcoding ISA level.  */
 
 #define       ISA_UNKNOWN     0               /* Gas internal use.  */
@@ -1217,75 +1151,81 @@ static const unsigned int mips_isa_table[] =
 
 /* Return true if the given CPU is included in INSN_* mask MASK.  */
 
-static inline bfd_boolean
-cpu_is_member (int cpu, unsigned int mask)
+static inline unsigned long
+cpu_insn_mask (int cpu)
 {
   switch (cpu)
     {
     case CPU_R4650:
     case CPU_RM7000:
     case CPU_RM9000:
-      return (mask & INSN_4650) != 0;
+      return AFL_EXT_4650;
 
     case CPU_R4010:
-      return (mask & INSN_4010) != 0;
+      return AFL_EXT_4010;
 
     case CPU_VR4100:
-      return (mask & INSN_4100) != 0;
+      return AFL_EXT_4100;
 
     case CPU_R3900:
-      return (mask & INSN_3900) != 0;
+      return AFL_EXT_3900;
 
     case CPU_R10000:
     case CPU_R12000:
     case CPU_R14000:
     case CPU_R16000:
-      return (mask & INSN_10000) != 0;
+      return AFL_EXT_10000;
 
     case CPU_SB1:
-      return (mask & INSN_SB1) != 0;
+      return AFL_EXT_SB1;
 
     case CPU_R4111:
-      return (mask & INSN_4111) != 0;
+      return AFL_EXT_4111;
 
     case CPU_VR4120:
-      return (mask & INSN_4120) != 0;
+      return AFL_EXT_4120;
 
     case CPU_VR5400:
-      return (mask & INSN_5400) != 0;
+      return AFL_EXT_5400;
 
     case CPU_VR5500:
-      return (mask & INSN_5500) != 0;
+      return AFL_EXT_5500;
 
     case CPU_R5900:
-      return (mask & INSN_5900) != 0;
+      return AFL_EXT_5900;
 
     case CPU_LOONGSON_2E:
-      return (mask & INSN_LOONGSON_2E) != 0;
+      return AFL_EXT_LOONGSON_2E;
 
     case CPU_LOONGSON_2F:
-      return (mask & INSN_LOONGSON_2F) != 0;
+      return AFL_EXT_LOONGSON_2F;
 
     case CPU_LOONGSON_3A:
-      return (mask & INSN_LOONGSON_3A) != 0;
+      return AFL_EXT_LOONGSON_3A;
 
     case CPU_OCTEON:
-      return (mask & INSN_OCTEON) != 0;
+      return AFL_EXT_OCTEON;
 
     case CPU_OCTEONP:
-      return (mask & INSN_OCTEONP) != 0;
+      return AFL_EXT_OCTEONP;
 
     case CPU_OCTEON2:
-      return (mask & INSN_OCTEON2) != 0;
+      return AFL_EXT_OCTEON2;
 
     case CPU_XLR:
-      return (mask & INSN_XLR) != 0;
+      return AFL_EXT_XLR;
 
     default:
-      return FALSE;
+      return 0;
     }
 }
 
+static inline bfd_boolean
+cpu_is_member (int cpu, unsigned int mask)
+{
+  return (cpu_insn_mask (cpu) & mask) != 0;
+}
+
 /* Test for membership in an ISA including chip specific ISAs.  INSN
    is pointer to an element of the opcode table; ISA is the specified
    ISA/ASE bitmask to test against; and CPU is the CPU specific ISA to
diff --git a/ld/emulparams/elf32bmip.sh b/ld/emulparams/elf32bmip.sh
index 118d57a..8da0f8f 100644
--- a/ld/emulparams/elf32bmip.sh
+++ b/ld/emulparams/elf32bmip.sh
@@ -17,7 +17,8 @@ if test -z "${CREATE_SHLIB}"; then
   INITIAL_READONLY_SECTIONS=".interp       ${RELOCATING-0} : { *(.interp) }"
 fi
 INITIAL_READONLY_SECTIONS="${INITIAL_READONLY_SECTIONS}
-  .reginfo      ${RELOCATING-0} : { *(.reginfo) }
+  .MIPS.abiflags ${RELOCATING-0} : { *(.MIPS.abiflags) }
+  .reginfo       ${RELOCATING-0} : { *(.reginfo) }
 "
 OTHER_TEXT_SECTIONS='*(.mips16.fn.*) *(.mips16.call.*)'
 # Unlike most targets, the MIPS backend puts all dynamic relocations
diff --git a/ld/emulparams/elf32bmipn32-defs.sh b/ld/emulparams/elf32bmipn32-defs.sh
index 514990b..723eac8 100644
--- a/ld/emulparams/elf32bmipn32-defs.sh
+++ b/ld/emulparams/elf32bmipn32-defs.sh
@@ -88,6 +88,7 @@ if test -z "${CREATE_SHLIB}"; then
   INITIAL_READONLY_SECTIONS=".interp       ${RELOCATING-0} : { *(.interp) }"
 fi
 INITIAL_READONLY_SECTIONS="${INITIAL_READONLY_SECTIONS}
+  .MIPS.abiflags      ${RELOCATING-0} : { *(.MIPS.abiflags) }
   .reginfo      ${RELOCATING-0} : { *(.reginfo) }"
 # Discard any .MIPS.content* or .MIPS.events* sections.  The linker
 # doesn't know how to adjust them.
diff --git a/opcodes/micromips-opc.c b/opcodes/micromips-opc.c
index af7cbf6..c688065 100644
--- a/opcodes/micromips-opc.c
+++ b/opcodes/micromips-opc.c
@@ -257,25 +257,25 @@ decode_micromips_operand (const char *p)
 #define RD_a	RD_HILO		/* Read DSP accumulators (reuse RD_HILO).  */
 #define MOD_a	WR_a|RD_a
 #define DSP_VOLA INSN_NO_DELAY_SLOT
-#define D32	ASE_DSP
-#define D33	ASE_DSPR2
+#define D32	AFL_ASE_DSP
+#define D33	AFL_ASE_DSPR2
 
 /* MIPS MCU (MicroController) ASE support.  */
-#define MC	ASE_MCU
+#define MC	AFL_ASE_MCU
 
 /* MIPS Enhanced VA Scheme.  */
-#define EVA	ASE_EVA
+#define EVA	AFL_ASE_EVA
 
 /* TLB invalidate instruction support.  */
-#define TLBINV	ASE_EVA
+#define TLBINV	AFL_ASE_EVA
 
 /* MIPS Virtualization ASE.  */
-#define IVIRT	ASE_VIRT
-#define IVIRT64	ASE_VIRT64
+#define IVIRT	AFL_ASE_VIRT
+#define IVIRT64	AFL_ASE_VIRT64
 
 /* MSA support.  */
-#define MSA     ASE_MSA
-#define MSA64   ASE_MSA64
+#define MSA     AFL_ASE_MSA
+#define MSA64   AFL_ASE_MSA64
 
 const struct mips_opcode micromips_opcodes[] =
 {
diff --git a/opcodes/mips-dis.c b/opcodes/mips-dis.c
index 0f8624e..658916e 100644
--- a/opcodes/mips-dis.c
+++ b/opcodes/mips-dis.c
@@ -543,66 +543,68 @@ const struct mips_arch_choice mips_arch_choices[] =
      MIPS32 Architecture_ (MIPS Document Number MD00082, Revision 0.95),
      page 1.  */
   { "mips32",	1, bfd_mach_mipsisa32, CPU_MIPS32,
-    ISA_MIPS32,  ASE_SMARTMIPS,
+    ISA_MIPS32,  AFL_ASE_SMARTMIPS,
     mips_cp0_names_mips3264,
     mips_cp0sel_names_mips3264, ARRAY_SIZE (mips_cp0sel_names_mips3264),
     mips_cp1_names_mips3264, mips_hwr_names_numeric },
 
   { "mips32r2",	1, bfd_mach_mipsisa32r2, CPU_MIPS32R2,
     ISA_MIPS32R2,
-    (ASE_SMARTMIPS | ASE_DSP | ASE_DSPR2 | ASE_EVA | ASE_MIPS3D
-     | ASE_MT | ASE_MCU | ASE_VIRT | ASE_MSA | ASE_XPA),
+    (AFL_ASE_SMARTMIPS | AFL_ASE_DSP | AFL_ASE_DSPR2 | AFL_ASE_EVA
+     | AFL_ASE_MIPS3D | AFL_ASE_MT | AFL_ASE_MCU | AFL_ASE_VIRT | AFL_ASE_MSA
+     | AFL_ASE_XPA),
     mips_cp0_names_mips3264r2,
     mips_cp0sel_names_mips3264r2, ARRAY_SIZE (mips_cp0sel_names_mips3264r2),
     mips_cp1_names_mips3264, mips_hwr_names_mips3264r2 },
 
   /* For stock MIPS64, disassemble all applicable MIPS-specified ASEs.  */
   { "mips64",	1, bfd_mach_mipsisa64, CPU_MIPS64,
-    ISA_MIPS64,  ASE_MIPS3D | ASE_MDMX,
+    ISA_MIPS64,  AFL_ASE_MIPS3D | AFL_ASE_MDMX,
     mips_cp0_names_mips3264,
     mips_cp0sel_names_mips3264, ARRAY_SIZE (mips_cp0sel_names_mips3264),
     mips_cp1_names_mips3264, mips_hwr_names_numeric },
 
   { "mips64r2",	1, bfd_mach_mipsisa64r2, CPU_MIPS64R2,
     ISA_MIPS64R2,
-    (ASE_MIPS3D | ASE_DSP | ASE_DSPR2 | ASE_DSP64 | ASE_EVA | ASE_MT
-     | ASE_MCU | ASE_VIRT | ASE_VIRT64 | ASE_MSA | ASE_MSA64 | ASE_XPA),
+    (AFL_ASE_MIPS3D | AFL_ASE_DSP | AFL_ASE_DSPR2 | AFL_ASE_DSP64 | AFL_ASE_EVA
+     | AFL_ASE_MT | AFL_ASE_MCU | AFL_ASE_VIRT | AFL_ASE_VIRT64 | AFL_ASE_MSA
+     | AFL_ASE_MSA64 | AFL_ASE_XPA),
     mips_cp0_names_mips3264r2,
     mips_cp0sel_names_mips3264r2, ARRAY_SIZE (mips_cp0sel_names_mips3264r2),
     mips_cp1_names_mips3264, mips_hwr_names_mips3264r2 },
 
   { "sb1",	1, bfd_mach_mips_sb1, CPU_SB1,
-    ISA_MIPS64 | INSN_SB1,  ASE_MIPS3D,
+    ISA_MIPS64 | AFL_EXT_SB1,  AFL_ASE_MIPS3D,
     mips_cp0_names_sb1,
     mips_cp0sel_names_sb1, ARRAY_SIZE (mips_cp0sel_names_sb1),
     mips_cp1_names_mips3264, mips_hwr_names_numeric },
 
   { "loongson2e",   1, bfd_mach_mips_loongson_2e, CPU_LOONGSON_2E,
-    ISA_MIPS3 | INSN_LOONGSON_2E, 0, mips_cp0_names_numeric,
+    ISA_MIPS3 | AFL_EXT_LOONGSON_2E, 0, mips_cp0_names_numeric,
     NULL, 0, mips_cp1_names_numeric, mips_hwr_names_numeric },
 
   { "loongson2f",   1, bfd_mach_mips_loongson_2f, CPU_LOONGSON_2F,
-    ISA_MIPS3 | INSN_LOONGSON_2F, 0, mips_cp0_names_numeric,
+    ISA_MIPS3 | AFL_EXT_LOONGSON_2F, 0, mips_cp0_names_numeric,
     NULL, 0, mips_cp1_names_numeric, mips_hwr_names_numeric },
 
   { "loongson3a",   1, bfd_mach_mips_loongson_3a, CPU_LOONGSON_3A,
-    ISA_MIPS64R2 | INSN_LOONGSON_3A, 0, mips_cp0_names_numeric,
+    ISA_MIPS64R2 | AFL_EXT_LOONGSON_3A, 0, mips_cp0_names_numeric,
     NULL, 0, mips_cp1_names_mips3264, mips_hwr_names_numeric },
 
   { "octeon",   1, bfd_mach_mips_octeon, CPU_OCTEON,
-    ISA_MIPS64R2 | INSN_OCTEON, 0, mips_cp0_names_numeric, NULL, 0,
+    ISA_MIPS64R2 | AFL_EXT_OCTEON, 0, mips_cp0_names_numeric, NULL, 0,
     mips_cp1_names_mips3264, mips_hwr_names_numeric },
 
   { "octeon+",   1, bfd_mach_mips_octeonp, CPU_OCTEONP,
-    ISA_MIPS64R2 | INSN_OCTEONP, 0, mips_cp0_names_numeric,
+    ISA_MIPS64R2 | AFL_EXT_OCTEONP, 0, mips_cp0_names_numeric,
     NULL, 0, mips_cp1_names_mips3264, mips_hwr_names_numeric },
 
   { "octeon2",   1, bfd_mach_mips_octeon2, CPU_OCTEON2,
-    ISA_MIPS64R2 | INSN_OCTEON2, 0, mips_cp0_names_numeric,
+    ISA_MIPS64R2 | AFL_EXT_OCTEON2, 0, mips_cp0_names_numeric,
     NULL, 0, mips_cp1_names_mips3264, mips_hwr_names_numeric },
 
   { "xlr", 1, bfd_mach_mips_xlr, CPU_XLR,
-    ISA_MIPS64 | INSN_XLR, 0,
+    ISA_MIPS64 | AFL_EXT_XLR, 0,
     mips_cp0_names_xlr,
     mips_cp0sel_names_xlr, ARRAY_SIZE (mips_cp0sel_names_xlr),
     mips_cp1_names_mips3264, mips_hwr_names_numeric },
@@ -610,7 +612,7 @@ const struct mips_arch_choice mips_arch_choices[] =
   /* XLP is mostly like XLR, with the prominent exception it is being
      MIPS64R2.  */
   { "xlp", 1, bfd_mach_mips_xlr, CPU_XLR,
-    ISA_MIPS64R2 | INSN_XLR, 0,
+    ISA_MIPS64R2 | AFL_EXT_XLR, 0,
     mips_cp0_names_xlr,
     mips_cp0sel_names_xlr, ARRAY_SIZE (mips_cp0sel_names_xlr),
     mips_cp1_names_mips3264, mips_hwr_names_numeric },
@@ -797,23 +799,23 @@ parse_mips_dis_option (const char *option, unsigned int len)
 
   if (CONST_STRNEQ (option, "msa"))
     {
-      mips_ase |= ASE_MSA;
+      mips_ase |= AFL_ASE_MSA;
       if ((mips_isa & INSN_ISA_MASK) == ISA_MIPS64R2)
-	  mips_ase |= ASE_MSA64;
+	  mips_ase |= AFL_ASE_MSA64;
       return;
     }
 
   if (CONST_STRNEQ (option, "virt"))
     {
-      mips_ase |= ASE_VIRT;
+      mips_ase |= AFL_ASE_VIRT;
       if (mips_isa & ISA_MIPS64R2)
-	mips_ase |= ASE_VIRT64;
+	mips_ase |= AFL_ASE_VIRT64;
       return;
     }
 
   if (CONST_STRNEQ (option, "xpa"))
     {
-      mips_ase |= ASE_XPA;
+      mips_ase |= AFL_ASE_XPA;
       return;
     }
   
@@ -979,7 +981,7 @@ print_reg (struct disassemble_info *info, const struct mips_opcode *opcode,
       break;
 
     case OP_REG_VEC:
-      if (opcode->membership & INSN_5400)
+      if (opcode->membership & AFL_EXT_5400)
 	info->fprintf_func (info->stream, "$f%d", regno);
       else
 	info->fprintf_func (info->stream, "$v%d", regno);
diff --git a/opcodes/mips-opc.c b/opcodes/mips-opc.c
index 9181c3f..bb7d035 100644
--- a/opcodes/mips-opc.c
+++ b/opcodes/mips-opc.c
@@ -257,37 +257,37 @@ decode_mips_operand (const char *p)
 #define I5_33   INSN_ISA5_32R2
 
 /* MIPS64 MIPS-3D ASE support.  */
-#define M3D     ASE_MIPS3D
+#define M3D     AFL_ASE_MIPS3D
 
 /* MIPS32 SmartMIPS ASE support.  */
-#define SMT	ASE_SMARTMIPS
+#define SMT	AFL_ASE_SMARTMIPS
 
 /* MIPS64 MDMX ASE support.  */
-#define MX      ASE_MDMX
+#define MX      AFL_ASE_MDMX
 
-#define IL2E    (INSN_LOONGSON_2E)
-#define IL2F    (INSN_LOONGSON_2F)
-#define IL3A    (INSN_LOONGSON_3A)
+#define IL2E    (AFL_EXT_LOONGSON_2E)
+#define IL2F    (AFL_EXT_LOONGSON_2F)
+#define IL3A    (AFL_EXT_LOONGSON_3A)
 
-#define P3	INSN_4650
-#define L1	INSN_4010
-#define V1	(INSN_4100 | INSN_4111 | INSN_4120)
-#define T3      INSN_3900
+#define P3	AFL_EXT_4650
+#define L1	AFL_EXT_4010
+#define V1	(AFL_EXT_4100 | AFL_EXT_4111 | AFL_EXT_4120)
+#define T3      AFL_EXT_3900
 /* Emotion Engine MIPS r5900. */
-#define EE      INSN_5900
-#define M1	INSN_10000
-#define SB1     INSN_SB1
-#define N411	INSN_4111
-#define N412	INSN_4120
-#define N5	(INSN_5400 | INSN_5500)
-#define N54	INSN_5400
-#define N55	INSN_5500
-#define IOCT	(INSN_OCTEON | INSN_OCTEONP | INSN_OCTEON2)
-#define IOCTP	(INSN_OCTEONP | INSN_OCTEON2)
-#define IOCT2	INSN_OCTEON2
-#define XLR     INSN_XLR
-#define IVIRT	ASE_VIRT
-#define IVIRT64	ASE_VIRT64
+#define EE      AFL_EXT_5900
+#define M1	AFL_EXT_10000
+#define SB1     AFL_EXT_SB1
+#define N411	AFL_EXT_4111
+#define N412	AFL_EXT_4120
+#define N5	(AFL_EXT_5400 | AFL_EXT_5500)
+#define N54	AFL_EXT_5400
+#define N55	AFL_EXT_5500
+#define IOCT	(AFL_EXT_OCTEON | AFL_EXT_OCTEONP | AFL_EXT_OCTEON2)
+#define IOCTP	(AFL_EXT_OCTEONP | AFL_EXT_OCTEON2)
+#define IOCT2	AFL_EXT_OCTEON2
+#define XLR     AFL_EXT_XLR
+#define IVIRT	AFL_ASE_VIRT
+#define IVIRT64	AFL_ASE_VIRT64
 
 #define G1      (T3             \
                  |EE            \
@@ -339,28 +339,28 @@ decode_mips_operand (const char *p)
 #define RD_a	RD_HILO	/* Read dsp accumulators (reuse RD_HILO)  */
 #define MOD_a	WR_a|RD_a
 #define DSP_VOLA INSN_NO_DELAY_SLOT
-#define D32	ASE_DSP
-#define D33	ASE_DSPR2
-#define D64	ASE_DSP64
+#define D32	AFL_ASE_DSP
+#define D33	AFL_ASE_DSPR2
+#define D64	AFL_ASE_DSP64
 
 /* MIPS MT ASE support.  */
-#define MT32	ASE_MT
+#define MT32	AFL_ASE_MT
 
 /* MIPS MCU (MicroController) ASE support.  */
-#define MC	ASE_MCU
+#define MC	AFL_ASE_MCU
 
 /* MIPS Enhanced VA Scheme.  */
-#define EVA	ASE_EVA
+#define EVA	AFL_ASE_EVA
 
 /* TLB invalidate instruction support.  */
-#define TLBINV	ASE_EVA
+#define TLBINV	AFL_ASE_EVA
 
 /* MSA support.  */
-#define MSA	ASE_MSA
-#define MSA64	ASE_MSA64
+#define MSA	AFL_ASE_MSA
+#define MSA64	AFL_ASE_MSA64
 
 /* eXtended Physical Address (XPA) support.  */
-#define XPA     ASE_XPA
+#define XPA     AFL_ASE_XPA
 
 /* The order of overloaded instructions matters.  Label arguments and
    register arguments look the same. Instructions that can have either
-- 
1.7.1


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]