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,

Updated patch below. The changes are:

* Update structure to add cpr1_size and cpr2_size.
* When merging object's private data take the maximum of register sizes

The spec for O32 FPXX has been updated to account for all recent changes.
https://dmz-portal.mips.com/wiki/MIPS_O32_ABI_-_FR0_and_FR1_Interlinking

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_REG_NONE, AFL_REG_32, AFL_REG_64, AFL_REG_128): 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, get_mips_reg_size): 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, get_mips_reg_size): 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                   |  728 ++++++++++++++++++++++++--
 binutils/readelf.c                 |  204 +++++++-
 elfcpp/elfcpp.h                    |    4 +-
 gas/config/tc-mips.c               | 1012 +++++++++++++++++++++++------------
 gas/config/tc-mips.h               |    5 +
 gas/doc/c-mips.texi                |  128 +++++
 include/elf/mips.h                 |  118 +++++-
 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, 1881 insertions(+), 568 deletions(-)

diff --git a/bfd/elfxx-mips.c b/bfd/elfxx-mips.c
index d939444..0a15d1a 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,46 @@ 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_16 (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->cpr1_size = H_GET_8 (abfd, ex->cpr1_size);
+  in->cpr2_size = H_GET_8 (abfd, ex->cpr2_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_16 (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->cpr1_size, ex->cpr1_size);
+  H_PUT_8 (abfd, in->cpr2_size, ex->cpr2_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 +6962,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 +6997,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 +7177,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 +9101,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 +9109,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 +11859,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 +11925,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 +13689,112 @@ _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_REG_32;
+  else
+    abiflags->gpr_size = AFL_REG_64;
+
+  abiflags->cpr1_size = AFL_REG_NONE;
+
+  in_attr = elf_known_obj_attributes (abfd)[OBJ_ATTR_GNU];
+  abiflags->fp_abi = in_attr[Tag_GNU_MIPS_ABI_FP].i;
+
+  if (abiflags->fp_abi == Val_GNU_MIPS_ABI_FP_SINGLE
+      || abiflags->fp_abi == Val_GNU_MIPS_ABI_FP_XX
+      || (abiflags->fp_abi == Val_GNU_MIPS_ABI_FP_DOUBLE
+	  && abiflags->gpr_size == AFL_REG_32))
+    abiflags->cpr1_size = AFL_REG_32;
+  else if (abiflags->fp_abi == Val_GNU_MIPS_ABI_FP_DOUBLE
+	   || abiflags->fp_abi == Val_GNU_MIPS_ABI_FP_64)
+    abiflags->cpr1_size = AFL_REG_64;
+
+  abiflags->cpr2_size = AFL_REG_NONE;
+
+  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 +13805,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 +13887,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 +14412,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 +14587,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 +14645,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 +14688,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 +14724,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 +14742,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 +14858,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 +14906,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 +15023,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 +15040,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 +15059,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 +15083,86 @@ _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 (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;
+
+#define max(a,b) ((a) > (b) ? (a) : (b))
+  /* Merge abiflags.  */
+  mips_elf_tdata (obfd)->abiflags.gpr_size
+    = max (mips_elf_tdata (obfd)->abiflags.gpr_size,
+	   mips_elf_tdata (ibfd)->abiflags.gpr_size);
+  mips_elf_tdata (obfd)->abiflags.cpr1_size
+    = max (mips_elf_tdata (obfd)->abiflags.cpr1_size,
+	   mips_elf_tdata (ibfd)->abiflags.cpr1_size);
+  mips_elf_tdata (obfd)->abiflags.cpr2_size
+    = max (mips_elf_tdata (obfd)->abiflags.cpr2_size,
+	   mips_elf_tdata (ibfd)->abiflags.cpr2_size);
+#undef max
+  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 +15203,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 +15291,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 +15436,130 @@ _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;
+    }
+}
+
+static int
+get_mips_reg_size (int reg_size)
+{
+  return (reg_size == AFL_REG_NONE) ? 0
+	 : (reg_size == AFL_REG_32) ? 32
+	 : (reg_size == AFL_REG_64) ? 64
+	 : (reg_size == AFL_REG_128) ? 128
+	 : -1;
+}
+
 bfd_boolean
 _bfd_mips_elf_print_private_bfd_data (bfd *abfd, void *ptr)
 {
@@ -15008,7 +15624,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 +15648,30 @@ _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: %d\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",
+	       get_mips_reg_size (abiflags->gpr_size));
+      fprintf (file, "\nCPR1 size: %d",
+	       get_mips_reg_size (abiflags->cpr1_size));
+      fprintf (file, "\nCPR2 size: %d",
+	       get_mips_reg_size (abiflags->cpr2_size));
+      fputs ("\nFP ABI: ", 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..6c3ef70 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,103 @@ 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
+get_mips_reg_size (int reg_size)
+{
+  return (reg_size == AFL_REG_NONE) ? 0
+	 : (reg_size == AFL_REG_32) ? 32
+	 : (reg_size == AFL_REG_64) ? 64
+	 : (reg_size == AFL_REG_128) ? 128
+	 : -1;
+}
+
 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 +12760,57 @@ 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.cpr1_size = BYTE_GET (abiflags_ext->cpr1_size);
+	      abiflags_in.cpr2_size = BYTE_GET (abiflags_ext->cpr2_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: %d\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",
+		      get_mips_reg_size (abiflags_in.gpr_size));
+	      printf ("\nCPR1 size: %d",
+		      get_mips_reg_size (abiflags_in.cpr1_size));
+	      printf ("\nCPR2 size: %d",
+		      get_mips_reg_size (abiflags_in.cpr2_size));
+	      fputs ("\nFP ABI: ", 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 +12950,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..bd896ef 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,67 @@ 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_REG_32 : AFL_REG_64;
+  flags.cpr1_size = file_mips_opts.soft_float ? AFL_REG_NONE
+		    : (file_mips_opts.ase & AFL_ASE_MSA) ? AFL_REG_128
+		    : (file_mips_opts.fp == 64) ? AFL_REG_64
+		    : AFL_REG_32;
+  flags.cpr2_size = AFL_REG_NONE;
+  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 +17591,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 +17601,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 +17618,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 +17735,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 +18124,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 +18147,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 +18313,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 +18560,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..1fb3c70 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,58 @@ typedef struct
   bfd_vma ri_gp_value;
 } Elf64_Internal_RegInfo;
 
+/* ABI Flags structure version 0.  */
+
+typedef struct
+{
+  /* Version of flags structure.  */
+  unsigned char version[2];
+  /* 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 size of co-processor 1 registers.  */
+  unsigned char cpr1_size[1];
+  /* The size of co-processor 2 registers.  */
+  unsigned char cpr2_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 short 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 size of co-processor 1 registers.  */
+  unsigned char cpr1_size;
+  /* The size of co-processor 2 registers.  */
+  unsigned char cpr2_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 +1145,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 +1188,53 @@ 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 xxx_size bytes of an ABI flags structure.  */
+
+#define AFL_REG_NONE	     0x00	/* No registers.  */
+#define AFL_REG_32	     0x01	/* 32-bit registers.  */
+#define AFL_REG_64	     0x02	/* 64-bit registers.  */
+#define AFL_REG_128	     0x03	/* 128-bit registers.  */
+
+/* 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 +1267,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]